siteplane 0.1.41 → 0.1.43
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/README.md +30 -1
- package/dist/bin/siteplane.js +2033 -417
- package/dist/index.js +1280 -363
- package/dist/next.js +1464 -382
- package/package.json +4 -1
package/dist/bin/siteplane.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/bin/run-siteplane.ts
|
|
4
|
-
import { randomUUID as
|
|
5
|
-
import { access as
|
|
6
|
-
import { join as
|
|
4
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
5
|
+
import { access as access6, readFile as readFile13 } from "fs/promises";
|
|
6
|
+
import { join as join12, resolve as resolve4 } from "path";
|
|
7
7
|
|
|
8
8
|
// ../cli/dist/analytics/manifest.js
|
|
9
9
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
@@ -892,7 +892,7 @@ function redactBookingManifest(value) {
|
|
|
892
892
|
var BOOKING_CREATE_PAYLOAD_LIMIT_BYTES = 16 * 1024;
|
|
893
893
|
|
|
894
894
|
// ../shared/dist/bridge-protocol.js
|
|
895
|
-
import { z as
|
|
895
|
+
import { z as z16 } from "zod";
|
|
896
896
|
|
|
897
897
|
// ../shared/dist/field-id.js
|
|
898
898
|
import { z as z9 } from "zod";
|
|
@@ -939,21 +939,70 @@ var imageValueSchema = z11.object({
|
|
|
939
939
|
]).optional()
|
|
940
940
|
});
|
|
941
941
|
|
|
942
|
+
// ../shared/dist/setup-deployment.js
|
|
943
|
+
import { z as z12 } from "zod";
|
|
944
|
+
var version = z12.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u);
|
|
945
|
+
var hash = z12.string().regex(/^sha256:[0-9a-f]{64}$/u);
|
|
946
|
+
var setupDeploymentRecordSchema = z12.object({
|
|
947
|
+
version: z12.literal(1),
|
|
948
|
+
operationId: z12.string().uuid(),
|
|
949
|
+
provider: z12.literal("vercel"),
|
|
950
|
+
projectId: z12.string().regex(/^prj_[A-Za-z0-9]+$/u),
|
|
951
|
+
teamId: z12.string().regex(/^(?:team|user)_[A-Za-z0-9]+$/u),
|
|
952
|
+
origin: z12.string().url().refine((value) => {
|
|
953
|
+
const url = new URL(value);
|
|
954
|
+
return url.protocol === "https:" && url.origin === value && !url.username && !url.password;
|
|
955
|
+
}),
|
|
956
|
+
deploymentId: z12.string().regex(/^dpl_[A-Za-z0-9]+$/u).nullable(),
|
|
957
|
+
revision: z12.string().regex(/^[0-9a-f]{40}$/u),
|
|
958
|
+
fieldContractHash: hash,
|
|
959
|
+
packages: z12.object({ siteplane: version, runtimeNext: version }).strict(),
|
|
960
|
+
configurationFingerprint: hash,
|
|
961
|
+
adminSecretGeneration: z12.string().uuid(),
|
|
962
|
+
revalidationGeneration: z12.number().int().positive(),
|
|
963
|
+
startedAt: z12.string().datetime(),
|
|
964
|
+
environmentWrittenAt: z12.string().datetime().nullable(),
|
|
965
|
+
deployRequestedAt: z12.string().datetime().nullable(),
|
|
966
|
+
deadline: z12.string().datetime(),
|
|
967
|
+
phase: z12.enum([
|
|
968
|
+
"planned",
|
|
969
|
+
"environment_written",
|
|
970
|
+
"deploy_requested",
|
|
971
|
+
"deployed",
|
|
972
|
+
"verified",
|
|
973
|
+
"failed"
|
|
974
|
+
]),
|
|
975
|
+
build: z12.object({
|
|
976
|
+
node: version,
|
|
977
|
+
packageManager: z12.enum(["npm", "pnpm"]),
|
|
978
|
+
packageManagerVersion: version,
|
|
979
|
+
lockfileHash: hash
|
|
980
|
+
}).strict().nullable()
|
|
981
|
+
}).strict();
|
|
982
|
+
var setupDeploymentInputSchema = z12.discriminatedUnion("action", [
|
|
983
|
+
z12.object({ action: z12.literal("read") }).strict(),
|
|
984
|
+
z12.object({
|
|
985
|
+
action: z12.literal("record"),
|
|
986
|
+
expectedOperationId: z12.string().uuid().nullable(),
|
|
987
|
+
record: setupDeploymentRecordSchema
|
|
988
|
+
}).strict()
|
|
989
|
+
]);
|
|
990
|
+
|
|
942
991
|
// ../shared/dist/site-setup.js
|
|
943
|
-
import { z as
|
|
992
|
+
import { z as z15 } from "zod";
|
|
944
993
|
|
|
945
994
|
// ../shared/dist/site-field-contract.js
|
|
946
|
-
import { z as
|
|
995
|
+
import { z as z14 } from "zod";
|
|
947
996
|
|
|
948
997
|
// ../shared/dist/fallback-hash.js
|
|
949
|
-
import { z as
|
|
998
|
+
import { z as z13 } from "zod";
|
|
950
999
|
var SAFE_REPRESENTATION_REASONS = [
|
|
951
1000
|
"asset_import_rewritten",
|
|
952
1001
|
"relative_url_normalized",
|
|
953
1002
|
"line_endings_normalized",
|
|
954
1003
|
"image_value_serialized"
|
|
955
1004
|
];
|
|
956
|
-
var safeRepresentationReasonSchema =
|
|
1005
|
+
var safeRepresentationReasonSchema = z13.enum(SAFE_REPRESENTATION_REASONS);
|
|
957
1006
|
function canonicalizeFallbackValue(input) {
|
|
958
1007
|
const fieldType = fieldTypeSchema.parse(input.fieldType);
|
|
959
1008
|
if (input.safeRepresentationReason !== void 0) {
|
|
@@ -989,10 +1038,10 @@ function createCanonicalFallbackHash(input) {
|
|
|
989
1038
|
const payload = stableJsonStringify(canonical);
|
|
990
1039
|
return `sha256:${sha256Hex(payload)}`;
|
|
991
1040
|
}
|
|
992
|
-
var linkFallbackSchema =
|
|
993
|
-
href:
|
|
994
|
-
label:
|
|
995
|
-
target:
|
|
1041
|
+
var linkFallbackSchema = z13.object({
|
|
1042
|
+
href: z13.string().trim().min(1),
|
|
1043
|
+
label: z13.string().trim().min(1).optional(),
|
|
1044
|
+
target: z13.enum(["_self", "_blank"]).optional()
|
|
996
1045
|
}).passthrough();
|
|
997
1046
|
function normalizeLineEndings(value) {
|
|
998
1047
|
return value.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
@@ -1170,67 +1219,67 @@ var SHA256_CONSTANTS = [
|
|
|
1170
1219
|
// ../shared/dist/site-field-contract.js
|
|
1171
1220
|
var fieldContractVersion = "1.0.0";
|
|
1172
1221
|
var scannerContractVersion = "1.0.0";
|
|
1173
|
-
var siteFieldStatusSchema =
|
|
1174
|
-
var semanticVersionSchema =
|
|
1175
|
-
var siteFieldRouteKeySchema =
|
|
1176
|
-
var repositoryRelativePosixPathSchema =
|
|
1177
|
-
var sourceMappingSchema =
|
|
1178
|
-
kind:
|
|
1222
|
+
var siteFieldStatusSchema = z14.enum(["hidden", "active"]);
|
|
1223
|
+
var semanticVersionSchema = z14.string().regex(/^\d+\.\d+\.\d+$/u);
|
|
1224
|
+
var siteFieldRouteKeySchema = z14.string().trim().refine((value) => value === "$global" || value.startsWith("/") && !value.includes("?") && !value.includes("#") && (value === "/" || !value.endsWith("/")), "Route keys must be canonical paths or $global.");
|
|
1225
|
+
var repositoryRelativePosixPathSchema = z14.string().trim().min(1).refine((value) => !value.startsWith("/") && !value.includes("\\") && !/^[a-z]:/iu.test(value) && !value.split("/").includes(".."), "Source paths must be repository-relative POSIX paths.");
|
|
1226
|
+
var sourceMappingSchema = z14.object({
|
|
1227
|
+
kind: z14.literal("function"),
|
|
1179
1228
|
filePath: repositoryRelativePosixPathSchema,
|
|
1180
|
-
exportName:
|
|
1181
|
-
sourcePath:
|
|
1229
|
+
exportName: z14.string().trim().min(1).optional(),
|
|
1230
|
+
sourcePath: z14.string().trim().min(1).optional(),
|
|
1182
1231
|
editTarget: fieldIdSchema.optional()
|
|
1183
1232
|
}).strict();
|
|
1184
|
-
var domMappingSchema =
|
|
1185
|
-
attribute:
|
|
1186
|
-
previewStrategy:
|
|
1233
|
+
var domMappingSchema = z14.object({
|
|
1234
|
+
attribute: z14.literal("data-siteplane-field-id"),
|
|
1235
|
+
previewStrategy: z14.enum(["dom", "event"])
|
|
1187
1236
|
}).strict();
|
|
1188
|
-
var fieldBaseSchema =
|
|
1237
|
+
var fieldBaseSchema = z14.object({
|
|
1189
1238
|
fieldId: fieldIdSchema,
|
|
1190
1239
|
routeKey: siteFieldRouteKeySchema,
|
|
1191
1240
|
source: sourceMappingSchema,
|
|
1192
1241
|
dom: domMappingSchema.optional()
|
|
1193
1242
|
}).strict();
|
|
1194
|
-
var linkFallbackSchema2 =
|
|
1195
|
-
href:
|
|
1196
|
-
label:
|
|
1197
|
-
target:
|
|
1243
|
+
var linkFallbackSchema2 = z14.object({
|
|
1244
|
+
href: z14.string().trim().min(1),
|
|
1245
|
+
label: z14.string().trim().min(1).optional(),
|
|
1246
|
+
target: z14.enum(["_self", "_blank"]).optional()
|
|
1198
1247
|
}).strict();
|
|
1199
|
-
var imageFallbackSchema =
|
|
1200
|
-
src:
|
|
1201
|
-
alt:
|
|
1202
|
-
width:
|
|
1203
|
-
height:
|
|
1204
|
-
crop:
|
|
1205
|
-
x:
|
|
1206
|
-
y:
|
|
1207
|
-
zoom:
|
|
1208
|
-
aspectRatio:
|
|
1248
|
+
var imageFallbackSchema = z14.object({
|
|
1249
|
+
src: z14.string().trim().min(1),
|
|
1250
|
+
alt: z14.string().trim().max(300),
|
|
1251
|
+
width: z14.number().int().positive().optional(),
|
|
1252
|
+
height: z14.number().int().positive().optional(),
|
|
1253
|
+
crop: z14.object({
|
|
1254
|
+
x: z14.number().min(0).max(100),
|
|
1255
|
+
y: z14.number().min(0).max(100),
|
|
1256
|
+
zoom: z14.number().min(1).max(4),
|
|
1257
|
+
aspectRatio: z14.number().positive().max(10)
|
|
1209
1258
|
}).strict().optional()
|
|
1210
1259
|
}).strict();
|
|
1211
|
-
var siteFieldContractFieldSchema =
|
|
1260
|
+
var siteFieldContractFieldSchema = z14.discriminatedUnion("fieldType", [
|
|
1212
1261
|
fieldBaseSchema.extend({
|
|
1213
|
-
fieldType:
|
|
1214
|
-
fallback:
|
|
1262
|
+
fieldType: z14.literal("text"),
|
|
1263
|
+
fallback: z14.string()
|
|
1215
1264
|
}),
|
|
1216
1265
|
fieldBaseSchema.extend({
|
|
1217
|
-
fieldType:
|
|
1218
|
-
fallback:
|
|
1266
|
+
fieldType: z14.literal("longText"),
|
|
1267
|
+
fallback: z14.string()
|
|
1219
1268
|
}),
|
|
1220
1269
|
fieldBaseSchema.extend({
|
|
1221
|
-
fieldType:
|
|
1270
|
+
fieldType: z14.literal("link"),
|
|
1222
1271
|
fallback: linkFallbackSchema2
|
|
1223
1272
|
}),
|
|
1224
1273
|
fieldBaseSchema.extend({
|
|
1225
|
-
fieldType:
|
|
1274
|
+
fieldType: z14.literal("image"),
|
|
1226
1275
|
fallback: imageFallbackSchema
|
|
1227
1276
|
})
|
|
1228
1277
|
]);
|
|
1229
|
-
var siteFieldContractV1Schema =
|
|
1230
|
-
version:
|
|
1278
|
+
var siteFieldContractV1Schema = z14.object({
|
|
1279
|
+
version: z14.literal(1),
|
|
1231
1280
|
fieldContractVersion: semanticVersionSchema,
|
|
1232
1281
|
scannerContractVersion: semanticVersionSchema,
|
|
1233
|
-
fields:
|
|
1282
|
+
fields: z14.array(siteFieldContractFieldSchema)
|
|
1234
1283
|
}).strict().superRefine((contract, context) => {
|
|
1235
1284
|
const seen = /* @__PURE__ */ new Set();
|
|
1236
1285
|
for (const [index, field] of contract.fields.entries()) {
|
|
@@ -1290,55 +1339,20 @@ var SITE_SETUP_OWNER_EDITABILITY_NOTE_MAX_LENGTH = 2e3;
|
|
|
1290
1339
|
var SITE_SETUP_OWNER_EDITABILITY_NOTE_MAX_BYTES = 8e3;
|
|
1291
1340
|
var CONTINUE_SITE_SETUP_ITEMS_MAX_BYTES = 32 * 1024;
|
|
1292
1341
|
var CONTINUE_SITE_SETUP_HTTP_BODY_MAX_BYTES = 64 * 1024;
|
|
1293
|
-
var siteSetupRunStatusSchema =
|
|
1294
|
-
var siteSetupRunModeSchema =
|
|
1295
|
-
var siteSetupErrorCodeSchema =
|
|
1296
|
-
var
|
|
1297
|
-
contractVersion: z14.literal("siteplane.setup-task.v1"),
|
|
1298
|
-
workflow: z14.enum(["initial_setup", "structure_update"]),
|
|
1299
|
-
goal: z14.string().min(1),
|
|
1300
|
-
modules: z14.tuple([z14.literal("visual_editor")]),
|
|
1301
|
-
packages: z14.object({
|
|
1302
|
-
siteplane: z14.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u),
|
|
1303
|
-
runtimeNext: z14.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u)
|
|
1304
|
-
}).strict(),
|
|
1305
|
-
communication: z14.object({
|
|
1306
|
-
beforeSourceChanges: z14.string().min(1),
|
|
1307
|
-
updates: z14.array(z14.string().min(1)).min(1),
|
|
1308
|
-
blocker: z14.string().min(1)
|
|
1309
|
-
}).strict(),
|
|
1310
|
-
discovery: z14.object({
|
|
1311
|
-
inspectPublicRoutes: z14.boolean(),
|
|
1312
|
-
contentKinds: z14.array(z14.enum(["text", "longText", "link", "image"])).min(1),
|
|
1313
|
-
rules: z14.array(z14.string().min(1)).min(1)
|
|
1314
|
-
}).strict(),
|
|
1315
|
-
steps: z14.array(z14.object({
|
|
1316
|
-
id: z14.enum([
|
|
1317
|
-
"inspect_repository",
|
|
1318
|
-
"install_packages",
|
|
1319
|
-
"instrument_fields",
|
|
1320
|
-
"apply_contract",
|
|
1321
|
-
"deploy_production",
|
|
1322
|
-
"verify_deployment"
|
|
1323
|
-
]),
|
|
1324
|
-
instruction: z14.string().min(1)
|
|
1325
|
-
}).strict()).length(6),
|
|
1326
|
-
completion: z14.object({
|
|
1327
|
-
verifiedStatus: z14.literal("ready_for_review"),
|
|
1328
|
-
instruction: z14.string().min(1)
|
|
1329
|
-
}).strict()
|
|
1330
|
-
}).strict();
|
|
1331
|
-
var editorFieldSchema = z14.object({
|
|
1342
|
+
var siteSetupRunStatusSchema = z15.enum(SITE_SETUP_RUN_STATUSES);
|
|
1343
|
+
var siteSetupRunModeSchema = z15.enum(SITE_SETUP_RUN_MODES);
|
|
1344
|
+
var siteSetupErrorCodeSchema = z15.enum(SITE_SETUP_ERROR_CODES);
|
|
1345
|
+
var editorFieldSchema = z15.object({
|
|
1332
1346
|
fieldId: fieldIdSchema,
|
|
1333
|
-
label:
|
|
1334
|
-
subsection:
|
|
1335
|
-
description:
|
|
1336
|
-
required:
|
|
1337
|
-
maxLength:
|
|
1347
|
+
label: z15.string().trim().min(1),
|
|
1348
|
+
subsection: z15.string().trim().min(1).optional(),
|
|
1349
|
+
description: z15.string().trim().min(1).optional(),
|
|
1350
|
+
required: z15.boolean().optional(),
|
|
1351
|
+
maxLength: z15.number().int().positive().optional()
|
|
1338
1352
|
}).strict();
|
|
1339
|
-
var editorSectionSchema =
|
|
1340
|
-
label:
|
|
1341
|
-
fields:
|
|
1353
|
+
var editorSectionSchema = z15.object({
|
|
1354
|
+
label: z15.string().trim().min(1),
|
|
1355
|
+
fields: z15.array(editorFieldSchema).min(1)
|
|
1342
1356
|
}).strict().superRefine((section, context) => {
|
|
1343
1357
|
const closedSubsections = /* @__PURE__ */ new Set();
|
|
1344
1358
|
let currentSubsection;
|
|
@@ -1357,10 +1371,10 @@ var editorSectionSchema = z14.object({
|
|
|
1357
1371
|
}
|
|
1358
1372
|
}
|
|
1359
1373
|
});
|
|
1360
|
-
var editorRouteSchema =
|
|
1374
|
+
var editorRouteSchema = z15.object({
|
|
1361
1375
|
routeKey: siteFieldRouteKeySchema,
|
|
1362
|
-
label:
|
|
1363
|
-
sections:
|
|
1376
|
+
label: z15.string().trim().min(1),
|
|
1377
|
+
sections: z15.array(editorSectionSchema).min(1)
|
|
1364
1378
|
}).strict().superRefine((route, context) => {
|
|
1365
1379
|
const sections = /* @__PURE__ */ new Set();
|
|
1366
1380
|
for (const [index, section] of route.sections.entries()) {
|
|
@@ -1374,9 +1388,9 @@ var editorRouteSchema = z14.object({
|
|
|
1374
1388
|
sections.add(section.label);
|
|
1375
1389
|
}
|
|
1376
1390
|
});
|
|
1377
|
-
var editorDefinitionV1Schema =
|
|
1378
|
-
version:
|
|
1379
|
-
routes:
|
|
1391
|
+
var editorDefinitionV1Schema = z15.object({
|
|
1392
|
+
version: z15.literal(1),
|
|
1393
|
+
routes: z15.array(editorRouteSchema).min(1)
|
|
1380
1394
|
}).strict().superRefine((definition, context) => {
|
|
1381
1395
|
const routes = /* @__PURE__ */ new Set();
|
|
1382
1396
|
const fields = /* @__PURE__ */ new Set();
|
|
@@ -1414,7 +1428,7 @@ var editorDefinitionV1Schema = z14.object({
|
|
|
1414
1428
|
var forbiddenControlOrBidi = /[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u;
|
|
1415
1429
|
var forbiddenOwnerNoteControlOrBidi = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u;
|
|
1416
1430
|
var unsafeLocator = /<\/?[a-z][^>]*>|\b(?:javascript|data:text\/html)\s*:|\bon[a-z]+\s*=/iu;
|
|
1417
|
-
var renderedPathSchema =
|
|
1431
|
+
var renderedPathSchema = z15.string().trim().transform((value) => value !== "/" ? value.replace(/\/+$/u, "") : value).superRefine((value, context) => {
|
|
1418
1432
|
if (!value.startsWith("/") || value === "$global" || value.includes("?") || value.includes("#")) {
|
|
1419
1433
|
context.addIssue({
|
|
1420
1434
|
code: "custom",
|
|
@@ -1424,7 +1438,7 @@ var renderedPathSchema = z14.string().trim().transform((value) => value !== "/"
|
|
|
1424
1438
|
addSafeByteIssues(value, CONTINUE_SITE_SETUP_RENDERED_PATH_MAX_BYTES, "renderedPath", context);
|
|
1425
1439
|
});
|
|
1426
1440
|
function normalizedSafeTextSchema(maxBytes, label) {
|
|
1427
|
-
return
|
|
1441
|
+
return z15.string().transform((value) => value.trim().replace(new RegExp("\\p{White_Space}+", "gu"), " ")).pipe(z15.string().min(1)).superRefine((value, context) => {
|
|
1428
1442
|
addSafeByteIssues(value, maxBytes, label, context);
|
|
1429
1443
|
if (label === "locatorHint" && unsafeLocator.test(value)) {
|
|
1430
1444
|
context.addIssue({
|
|
@@ -1441,12 +1455,12 @@ var correctionItemBase = {
|
|
|
1441
1455
|
requestedLabel: normalizedSafeTextSchema(CONTINUE_SITE_SETUP_REQUESTED_LABEL_MAX_BYTES, "requestedLabel").optional(),
|
|
1442
1456
|
requestedSection: normalizedSafeTextSchema(CONTINUE_SITE_SETUP_REQUESTED_SECTION_MAX_BYTES, "requestedSection").optional(),
|
|
1443
1457
|
requestedSubsection: normalizedSafeTextSchema(CONTINUE_SITE_SETUP_REQUESTED_SUBSECTION_MAX_BYTES, "requestedSubsection").optional(),
|
|
1444
|
-
requestedSortOrder:
|
|
1458
|
+
requestedSortOrder: z15.number().int().nonnegative().optional()
|
|
1445
1459
|
};
|
|
1446
|
-
var setupCorrectionItemSchema =
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1460
|
+
var setupCorrectionItemSchema = z15.discriminatedUnion("kind", [
|
|
1461
|
+
z15.object({ kind: z15.literal("text"), ...correctionItemBase }).strict(),
|
|
1462
|
+
z15.object({ kind: z15.literal("link"), ...correctionItemBase }).strict(),
|
|
1463
|
+
z15.object({ kind: z15.literal("image"), ...correctionItemBase }).strict()
|
|
1450
1464
|
]).superRefine((item, context) => {
|
|
1451
1465
|
if (item.requestedSubsection && !item.requestedSection) {
|
|
1452
1466
|
context.addIssue({
|
|
@@ -1456,7 +1470,7 @@ var setupCorrectionItemSchema = z14.discriminatedUnion("kind", [
|
|
|
1456
1470
|
});
|
|
1457
1471
|
}
|
|
1458
1472
|
});
|
|
1459
|
-
var setupCorrectionItemsSchema =
|
|
1473
|
+
var setupCorrectionItemsSchema = z15.array(setupCorrectionItemSchema).max(CONTINUE_SITE_SETUP_MAX_ITEMS).superRefine((items, context) => {
|
|
1460
1474
|
const bytes = utf8Bytes(stableJsonStringify(items));
|
|
1461
1475
|
if (bytes > CONTINUE_SITE_SETUP_ITEMS_MAX_BYTES) {
|
|
1462
1476
|
context.addIssue({
|
|
@@ -1465,7 +1479,7 @@ var setupCorrectionItemsSchema = z14.array(setupCorrectionItemSchema).max(CONTIN
|
|
|
1465
1479
|
});
|
|
1466
1480
|
}
|
|
1467
1481
|
});
|
|
1468
|
-
var siteSetupOwnerEditabilityNoteSchema =
|
|
1482
|
+
var siteSetupOwnerEditabilityNoteSchema = z15.string().max(SITE_SETUP_OWNER_EDITABILITY_NOTE_MAX_LENGTH).superRefine((value, context) => {
|
|
1469
1483
|
if (utf8Bytes(value) > SITE_SETUP_OWNER_EDITABILITY_NOTE_MAX_BYTES) {
|
|
1470
1484
|
context.addIssue({
|
|
1471
1485
|
code: "custom",
|
|
@@ -1479,27 +1493,190 @@ var siteSetupOwnerEditabilityNoteSchema = z14.string().trim().max(SITE_SETUP_OWN
|
|
|
1479
1493
|
});
|
|
1480
1494
|
}
|
|
1481
1495
|
});
|
|
1482
|
-
var
|
|
1496
|
+
var SITE_SETUP_MODULE_SCOPES = {
|
|
1497
|
+
editing: ["setup:read", "setup:write"],
|
|
1498
|
+
analytics: [
|
|
1499
|
+
"analytics:setup",
|
|
1500
|
+
"analytics:sync",
|
|
1501
|
+
"analytics:test",
|
|
1502
|
+
"analytics:readiness",
|
|
1503
|
+
"analytics:agent_instructions",
|
|
1504
|
+
"analytics:public_key_read"
|
|
1505
|
+
]
|
|
1506
|
+
};
|
|
1507
|
+
var siteSetupModulesSchema = z15.array(z15.enum(["editing", "analytics"])).min(1).max(2).refine((modules) => modules[0] === "editing" && (modules.length === 1 || modules[1] === "analytics"), "Choose Editing with optional Analytics.");
|
|
1508
|
+
function siteSetupScopes(modules) {
|
|
1509
|
+
return modules.flatMap((module) => [...SITE_SETUP_MODULE_SCOPES[module]]);
|
|
1510
|
+
}
|
|
1511
|
+
function hasExactSiteSetupScopes(scopes, modules) {
|
|
1512
|
+
const matches = (expected) => scopes.length === expected.length && expected.every((scope) => scopes.includes(scope));
|
|
1513
|
+
return modules ? matches(siteSetupScopes(modules)) : matches(siteSetupScopes(["editing"])) || matches(siteSetupScopes(["editing", "analytics"]));
|
|
1514
|
+
}
|
|
1515
|
+
var siteSetupActivityInputSchema = z15.object({
|
|
1516
|
+
phase: z15.enum([
|
|
1517
|
+
"inspecting",
|
|
1518
|
+
"integrating",
|
|
1519
|
+
"deploying",
|
|
1520
|
+
"verifying",
|
|
1521
|
+
"repairing"
|
|
1522
|
+
]),
|
|
1523
|
+
textId: z15.enum([
|
|
1524
|
+
"inspecting",
|
|
1525
|
+
"integrating",
|
|
1526
|
+
"deploying",
|
|
1527
|
+
"verifying",
|
|
1528
|
+
"repairing"
|
|
1529
|
+
])
|
|
1530
|
+
}).strict().refine((value) => value.phase === value.textId, "Activity text must describe its phase.");
|
|
1531
|
+
var nextActionFields = {
|
|
1532
|
+
actor: z15.enum(["agent", "user", "siteplane"]),
|
|
1533
|
+
boundary: z15.enum([
|
|
1534
|
+
"source",
|
|
1535
|
+
"environment",
|
|
1536
|
+
"deployment",
|
|
1537
|
+
"verification",
|
|
1538
|
+
"authorization",
|
|
1539
|
+
"review"
|
|
1540
|
+
]),
|
|
1541
|
+
precondition: z15.string().min(1).max(1e3),
|
|
1542
|
+
instruction: z15.string().min(1).max(2e3),
|
|
1543
|
+
expectedEvidence: z15.string().min(1).max(1e3)
|
|
1544
|
+
};
|
|
1545
|
+
var siteSetupNextActionSchema = z15.discriminatedUnion("kind", [
|
|
1546
|
+
z15.object({
|
|
1547
|
+
...nextActionFields,
|
|
1548
|
+
kind: z15.literal("run_command"),
|
|
1549
|
+
command: z15.string().min(1).max(500)
|
|
1550
|
+
}).strict(),
|
|
1551
|
+
z15.object({ ...nextActionFields, kind: z15.literal("agent_edit") }).strict(),
|
|
1552
|
+
z15.object({ ...nextActionFields, kind: z15.literal("needs_user_action") }).strict(),
|
|
1553
|
+
z15.object({ ...nextActionFields, kind: z15.literal("wait") }).strict(),
|
|
1554
|
+
z15.object({ ...nextActionFields, kind: z15.literal("done") }).strict()
|
|
1555
|
+
]);
|
|
1556
|
+
var siteSetupTaskRunSchema = z15.object({
|
|
1557
|
+
runId: z15.string().uuid(),
|
|
1558
|
+
mode: siteSetupRunModeSchema,
|
|
1559
|
+
status: siteSetupRunStatusSchema,
|
|
1560
|
+
fieldContractHash: z15.string().nullable(),
|
|
1561
|
+
deploymentOrigin: z15.string().url().nullable(),
|
|
1562
|
+
deploymentRevision: z15.string().nullable(),
|
|
1563
|
+
publicRuntimeKeyId: z15.string().uuid().nullable(),
|
|
1564
|
+
reviewVersion: z15.string().nullable()
|
|
1565
|
+
}).strict();
|
|
1566
|
+
var siteSetupTaskProgressSchema = z15.object({
|
|
1567
|
+
progressStage: z15.enum([
|
|
1568
|
+
"waiting_for_connection",
|
|
1569
|
+
"agent_connected",
|
|
1570
|
+
"project_context_loaded",
|
|
1571
|
+
"fields_prepared",
|
|
1572
|
+
"deployment_verification",
|
|
1573
|
+
"ready_for_review",
|
|
1574
|
+
"action_required",
|
|
1575
|
+
"completed"
|
|
1576
|
+
]),
|
|
1577
|
+
lastVerifiedAt: z15.string().datetime().nullable(),
|
|
1578
|
+
lastActivityAt: z15.string().datetime(),
|
|
1579
|
+
activitySource: z15.enum(["user", "server", "agent"]),
|
|
1580
|
+
activityPhase: siteSetupActivityInputSchema.shape.phase.nullable(),
|
|
1581
|
+
activityTextId: siteSetupActivityInputSchema.shape.textId.nullable()
|
|
1582
|
+
}).strict();
|
|
1583
|
+
var siteSetupTaskSchema = z15.object({
|
|
1584
|
+
contractVersion: z15.literal("siteplane.setup-task.v2"),
|
|
1585
|
+
workflow: z15.enum(["initial_setup", "structure_update"]),
|
|
1586
|
+
goal: z15.string().min(1),
|
|
1587
|
+
modules: siteSetupModulesSchema,
|
|
1588
|
+
authorizationVersion: z15.number().int().positive(),
|
|
1589
|
+
authorizedScopes: z15.array(z15.string()).min(2),
|
|
1590
|
+
site: z15.object({
|
|
1591
|
+
siteId: z15.string().uuid(),
|
|
1592
|
+
portalPath: z15.string(),
|
|
1593
|
+
adminOrigin: z15.string().url()
|
|
1594
|
+
}).strict().nullable(),
|
|
1595
|
+
run: siteSetupTaskRunSchema.nullable(),
|
|
1596
|
+
progress: siteSetupTaskProgressSchema.nullable(),
|
|
1597
|
+
deployment: setupDeploymentRecordSchema.nullable(),
|
|
1598
|
+
revalidationGeneration: z15.number().int().positive(),
|
|
1599
|
+
assignment: z15.literal("owned"),
|
|
1600
|
+
deadline: z15.string().datetime().nullable(),
|
|
1601
|
+
userInstructions: z15.object({
|
|
1602
|
+
text: siteSetupOwnerEditabilityNoteSchema,
|
|
1603
|
+
source: z15.literal("user"),
|
|
1604
|
+
trust: z15.literal("untrusted")
|
|
1605
|
+
}).strict(),
|
|
1606
|
+
corrections: setupCorrectionItemsSchema,
|
|
1607
|
+
contentSelectionSummary: z15.object({
|
|
1608
|
+
text: siteSetupOwnerEditabilityNoteSchema,
|
|
1609
|
+
source: z15.literal("agent"),
|
|
1610
|
+
trust: z15.literal("untrusted")
|
|
1611
|
+
}).strict().nullable(),
|
|
1612
|
+
environmentBindings: z15.array(z15.object({
|
|
1613
|
+
name: z15.string().regex(/^[A-Z][A-Z0-9_]+$/u),
|
|
1614
|
+
source: z15.string().min(1),
|
|
1615
|
+
sensitivity: z15.enum(["public", "secret"]),
|
|
1616
|
+
usage: z15.enum(["build", "server", "build_and_server"]),
|
|
1617
|
+
evidence: z15.string().min(1)
|
|
1618
|
+
}).strict()),
|
|
1619
|
+
nextAction: siteSetupNextActionSchema,
|
|
1620
|
+
error: z15.object({
|
|
1621
|
+
code: z15.string().regex(/^[a-z0-9_.]+$/u).max(160),
|
|
1622
|
+
boundary: nextActionFields.boundary,
|
|
1623
|
+
responsibleActor: nextActionFields.actor,
|
|
1624
|
+
retryable: z15.boolean(),
|
|
1625
|
+
retryAfter: z15.number().int().nonnegative().nullable(),
|
|
1626
|
+
requestId: z15.string().regex(/^[A-Za-z0-9_-]+$/u).max(128)
|
|
1627
|
+
}).strict().nullable(),
|
|
1628
|
+
packages: z15.object({
|
|
1629
|
+
siteplane: z15.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u),
|
|
1630
|
+
runtimeNext: z15.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u)
|
|
1631
|
+
}).strict(),
|
|
1632
|
+
communication: z15.object({
|
|
1633
|
+
beforeSourceChanges: z15.string().min(1),
|
|
1634
|
+
updates: z15.array(z15.string().min(1)).min(1),
|
|
1635
|
+
blocker: z15.string().min(1)
|
|
1636
|
+
}).strict(),
|
|
1637
|
+
discovery: z15.object({
|
|
1638
|
+
inspectPublicRoutes: z15.boolean(),
|
|
1639
|
+
contentKinds: z15.array(z15.enum(["text", "longText", "link", "image"])).min(1),
|
|
1640
|
+
rules: z15.array(z15.string().min(1)).min(1)
|
|
1641
|
+
}).strict(),
|
|
1642
|
+
steps: z15.array(z15.object({
|
|
1643
|
+
id: z15.enum([
|
|
1644
|
+
"inspect_repository",
|
|
1645
|
+
"install_packages",
|
|
1646
|
+
"instrument_fields",
|
|
1647
|
+
"apply_contract",
|
|
1648
|
+
"deploy_production",
|
|
1649
|
+
"verify_deployment"
|
|
1650
|
+
]),
|
|
1651
|
+
instruction: z15.string().min(1)
|
|
1652
|
+
}).strict()).length(6),
|
|
1653
|
+
completion: z15.object({
|
|
1654
|
+
verifiedStatus: z15.literal("ready_for_review"),
|
|
1655
|
+
instruction: z15.string().min(1)
|
|
1656
|
+
}).strict()
|
|
1657
|
+
}).strict().refine((task) => hasExactSiteSetupScopes(task.authorizedScopes, task.modules), "Task scopes must exactly match its modules.");
|
|
1658
|
+
var canonicalHashSchema = z15.string().regex(/^sha256:[0-9a-f]{64}$/iu);
|
|
1483
1659
|
var deploymentRevisionSchema = normalizedSafeTextSchema(256, "deploymentRevision");
|
|
1484
|
-
var continueSiteSetupInputSchema =
|
|
1485
|
-
siteId:
|
|
1486
|
-
runId:
|
|
1660
|
+
var continueSiteSetupInputSchema = z15.object({
|
|
1661
|
+
siteId: z15.string().uuid(),
|
|
1662
|
+
runId: z15.string().uuid(),
|
|
1487
1663
|
expectedFieldContractHash: canonicalHashSchema,
|
|
1488
1664
|
expectedDeploymentRevision: deploymentRevisionSchema,
|
|
1489
1665
|
correctionItems: setupCorrectionItemsSchema,
|
|
1490
1666
|
ownerEditabilityNote: siteSetupOwnerEditabilityNoteSchema.optional()
|
|
1491
1667
|
}).strict();
|
|
1492
|
-
var saveSiteSetupOwnerNoteInputSchema =
|
|
1493
|
-
siteId:
|
|
1494
|
-
runId:
|
|
1668
|
+
var saveSiteSetupOwnerNoteInputSchema = z15.object({
|
|
1669
|
+
siteId: z15.string().uuid(),
|
|
1670
|
+
runId: z15.string().uuid(),
|
|
1495
1671
|
expectedFieldContractHash: canonicalHashSchema,
|
|
1496
1672
|
expectedDeploymentRevision: deploymentRevisionSchema,
|
|
1497
1673
|
ownerEditabilityNote: siteSetupOwnerEditabilityNoteSchema
|
|
1498
1674
|
}).strict();
|
|
1499
|
-
var siteSetupContextInputSchema =
|
|
1500
|
-
var siteSetupApplyInputSchema =
|
|
1675
|
+
var siteSetupContextInputSchema = z15.object({}).strict();
|
|
1676
|
+
var siteSetupApplyInputSchema = z15.object({
|
|
1501
1677
|
fieldContract: siteFieldContractV1Schema,
|
|
1502
|
-
editorDefinition: editorDefinitionV1Schema
|
|
1678
|
+
editorDefinition: editorDefinitionV1Schema,
|
|
1679
|
+
contentSelectionSummary: siteSetupOwnerEditabilityNoteSchema.refine((text) => text.trim().length > 0, "Describe the selected content, important exclusions and any open gaps.")
|
|
1503
1680
|
}).strict().superRefine((input, context) => {
|
|
1504
1681
|
const contractRoutesByFieldId = new Map(input.fieldContract.fields.map((field) => [
|
|
1505
1682
|
field.fieldId,
|
|
@@ -1563,37 +1740,37 @@ var siteSetupApplyInputSchema = z14.object({
|
|
|
1563
1740
|
}
|
|
1564
1741
|
}
|
|
1565
1742
|
});
|
|
1566
|
-
var siteSetupVerifyInputSchema =
|
|
1567
|
-
deploymentUrl:
|
|
1743
|
+
var siteSetupVerifyInputSchema = z15.object({
|
|
1744
|
+
deploymentUrl: z15.string().url().refine((value) => value.startsWith("https://")),
|
|
1568
1745
|
deploymentRevision: deploymentRevisionSchema,
|
|
1569
|
-
wait:
|
|
1746
|
+
wait: z15.boolean().optional()
|
|
1570
1747
|
}).strict();
|
|
1571
|
-
var siteSetupEvidenceInputSchema =
|
|
1572
|
-
siteId:
|
|
1573
|
-
runId:
|
|
1748
|
+
var siteSetupEvidenceInputSchema = z15.object({
|
|
1749
|
+
siteId: z15.string().uuid(),
|
|
1750
|
+
runId: z15.string().uuid(),
|
|
1574
1751
|
expectedFieldContractHash: canonicalHashSchema,
|
|
1575
|
-
expectedDeploymentOrigin:
|
|
1752
|
+
expectedDeploymentOrigin: z15.string().url().refine((value) => value.startsWith("https://")),
|
|
1576
1753
|
expectedDeploymentRevision: deploymentRevisionSchema,
|
|
1577
|
-
publicRuntimeKeyId:
|
|
1754
|
+
publicRuntimeKeyId: z15.string().uuid(),
|
|
1578
1755
|
bridgeSessionId: normalizedSafeTextSchema(256, "bridgeSessionId"),
|
|
1579
|
-
observations:
|
|
1756
|
+
observations: z15.array(z15.object({
|
|
1580
1757
|
fieldId: fieldIdSchema,
|
|
1581
1758
|
routeKey: siteFieldRouteKeySchema,
|
|
1582
1759
|
renderedPath: renderedPathSchema,
|
|
1583
|
-
targetResolved:
|
|
1584
|
-
previewAck:
|
|
1760
|
+
targetResolved: z15.boolean(),
|
|
1761
|
+
previewAck: z15.boolean()
|
|
1585
1762
|
}).strict().refine((value) => !value.previewAck || value.targetResolved, "Preview ACK requires a resolved target.")).min(1).max(200)
|
|
1586
1763
|
}).strict();
|
|
1587
|
-
var completeSiteSetupInputSchema =
|
|
1588
|
-
siteId:
|
|
1589
|
-
runId:
|
|
1764
|
+
var completeSiteSetupInputSchema = z15.object({
|
|
1765
|
+
siteId: z15.string().uuid(),
|
|
1766
|
+
runId: z15.string().uuid(),
|
|
1590
1767
|
expectedFieldContractHash: canonicalHashSchema,
|
|
1591
1768
|
expectedDeploymentRevision: deploymentRevisionSchema,
|
|
1592
1769
|
expectedReadyFingerprint: canonicalHashSchema
|
|
1593
1770
|
}).strict();
|
|
1594
|
-
var refreshSiteSetupDeploymentInputSchema =
|
|
1595
|
-
siteId:
|
|
1596
|
-
deploymentUrl:
|
|
1771
|
+
var refreshSiteSetupDeploymentInputSchema = z15.object({
|
|
1772
|
+
siteId: z15.string().uuid(),
|
|
1773
|
+
deploymentUrl: z15.string().url().refine((value) => value.startsWith("https://")),
|
|
1597
1774
|
deploymentRevision: deploymentRevisionSchema
|
|
1598
1775
|
}).strict();
|
|
1599
1776
|
function addSafeByteIssues(value, maxBytes, label, context) {
|
|
@@ -1616,34 +1793,34 @@ function utf8Bytes(value) {
|
|
|
1616
1793
|
|
|
1617
1794
|
// ../shared/dist/bridge-protocol.js
|
|
1618
1795
|
var BRIDGE_PROTOCOL_VERSION = 2;
|
|
1619
|
-
var bridgeRectSchema =
|
|
1620
|
-
x:
|
|
1621
|
-
y:
|
|
1622
|
-
width:
|
|
1623
|
-
height:
|
|
1796
|
+
var bridgeRectSchema = z16.object({
|
|
1797
|
+
x: z16.number(),
|
|
1798
|
+
y: z16.number(),
|
|
1799
|
+
width: z16.number().nonnegative(),
|
|
1800
|
+
height: z16.number().nonnegative()
|
|
1624
1801
|
});
|
|
1625
|
-
var previewTextValueSchema =
|
|
1626
|
-
fieldType:
|
|
1627
|
-
value:
|
|
1802
|
+
var previewTextValueSchema = z16.object({
|
|
1803
|
+
fieldType: z16.enum(["text", "longText"]),
|
|
1804
|
+
value: z16.string()
|
|
1628
1805
|
});
|
|
1629
|
-
var previewLinkValueSchema =
|
|
1630
|
-
fieldType:
|
|
1631
|
-
value:
|
|
1632
|
-
href:
|
|
1633
|
-
label:
|
|
1806
|
+
var previewLinkValueSchema = z16.object({
|
|
1807
|
+
fieldType: z16.literal("link"),
|
|
1808
|
+
value: z16.object({
|
|
1809
|
+
href: z16.string().trim().min(1),
|
|
1810
|
+
label: z16.string().trim().min(1).optional()
|
|
1634
1811
|
})
|
|
1635
1812
|
});
|
|
1636
|
-
var previewImageValueSchema =
|
|
1637
|
-
fieldType:
|
|
1813
|
+
var previewImageValueSchema = z16.object({
|
|
1814
|
+
fieldType: z16.literal("image"),
|
|
1638
1815
|
value: imageValueSchema
|
|
1639
1816
|
});
|
|
1640
|
-
var bridgePreviewValueSchema =
|
|
1817
|
+
var bridgePreviewValueSchema = z16.discriminatedUnion("fieldType", [
|
|
1641
1818
|
previewTextValueSchema,
|
|
1642
1819
|
previewLinkValueSchema,
|
|
1643
1820
|
previewImageValueSchema
|
|
1644
1821
|
]);
|
|
1645
|
-
var fieldSelectionModeSchema =
|
|
1646
|
-
var fieldBridgeMissingContentActionSchema =
|
|
1822
|
+
var fieldSelectionModeSchema = z16.enum(["replace", "range", "toggle"]);
|
|
1823
|
+
var fieldBridgeMissingContentActionSchema = z16.enum([
|
|
1647
1824
|
"add",
|
|
1648
1825
|
"select",
|
|
1649
1826
|
"remove"
|
|
@@ -1662,77 +1839,77 @@ var FIELD_BRIDGE_MESSAGE_TYPES = [
|
|
|
1662
1839
|
"siteplane:field-bridge:preview-scrolled",
|
|
1663
1840
|
"siteplane:field-bridge:error"
|
|
1664
1841
|
];
|
|
1665
|
-
var canonicalFieldContractHashSchema =
|
|
1666
|
-
var bridgeSectionSchema =
|
|
1667
|
-
var realRenderedPathSchema =
|
|
1668
|
-
var fieldBridgeIdentitySchema =
|
|
1669
|
-
publicRuntimeKeyId:
|
|
1842
|
+
var canonicalFieldContractHashSchema = z16.string().regex(/^sha256:[0-9a-f]{64}$/u);
|
|
1843
|
+
var bridgeSectionSchema = z16.string().trim().min(1).max(256);
|
|
1844
|
+
var realRenderedPathSchema = z16.string().trim().refine((value) => value.startsWith("/") && value !== "$global" && !value.includes("?") && !value.includes("#"), "A field bridge rendered path must be a real canonical path.");
|
|
1845
|
+
var fieldBridgeIdentitySchema = z16.object({
|
|
1846
|
+
publicRuntimeKeyId: z16.string().uuid(),
|
|
1670
1847
|
fieldContractHash: canonicalFieldContractHashSchema,
|
|
1671
|
-
deploymentRevision:
|
|
1848
|
+
deploymentRevision: z16.string().trim().min(1).max(256)
|
|
1672
1849
|
}).strict();
|
|
1673
|
-
var fieldBridgeBaseSchema =
|
|
1674
|
-
version:
|
|
1675
|
-
type:
|
|
1676
|
-
siteId:
|
|
1677
|
-
sessionId:
|
|
1678
|
-
publicRuntimeKeyId:
|
|
1850
|
+
var fieldBridgeBaseSchema = z16.object({
|
|
1851
|
+
version: z16.literal(BRIDGE_PROTOCOL_VERSION),
|
|
1852
|
+
type: z16.enum(FIELD_BRIDGE_MESSAGE_TYPES),
|
|
1853
|
+
siteId: z16.string().uuid(),
|
|
1854
|
+
sessionId: z16.string().trim().min(1),
|
|
1855
|
+
publicRuntimeKeyId: z16.string().uuid(),
|
|
1679
1856
|
fieldContractHash: canonicalFieldContractHashSchema,
|
|
1680
|
-
deploymentRevision:
|
|
1681
|
-
requestId:
|
|
1682
|
-
sentAt:
|
|
1857
|
+
deploymentRevision: z16.string().trim().min(1).max(256),
|
|
1858
|
+
requestId: z16.string().trim().min(1),
|
|
1859
|
+
sentAt: z16.string().datetime()
|
|
1683
1860
|
}).strict();
|
|
1684
|
-
var fieldBridgeFieldSchema =
|
|
1861
|
+
var fieldBridgeFieldSchema = z16.object({
|
|
1685
1862
|
fieldId: fieldIdSchema,
|
|
1686
1863
|
fieldType: fieldTypeSchema,
|
|
1687
1864
|
routeKey: siteFieldRouteKeySchema,
|
|
1688
1865
|
renderedPath: realRenderedPathSchema,
|
|
1689
|
-
sourcePath:
|
|
1866
|
+
sourcePath: z16.string().trim().min(1).optional(),
|
|
1690
1867
|
editTarget: fieldIdSchema,
|
|
1691
1868
|
rect: bridgeRectSchema,
|
|
1692
|
-
targetResolved:
|
|
1869
|
+
targetResolved: z16.literal(true),
|
|
1693
1870
|
currentValue: bridgePreviewValueSchema.optional()
|
|
1694
1871
|
}).strict();
|
|
1695
1872
|
var fieldBridgeInitMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1696
|
-
type:
|
|
1697
|
-
payload:
|
|
1698
|
-
adminOrigin:
|
|
1699
|
-
customerOrigin:
|
|
1873
|
+
type: z16.literal("siteplane:field-bridge:init"),
|
|
1874
|
+
payload: z16.object({
|
|
1875
|
+
adminOrigin: z16.string().url(),
|
|
1876
|
+
customerOrigin: z16.string().url(),
|
|
1700
1877
|
renderedPath: realRenderedPathSchema
|
|
1701
1878
|
}).strict()
|
|
1702
1879
|
});
|
|
1703
1880
|
var fieldBridgeReadyMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1704
|
-
type:
|
|
1705
|
-
payload:
|
|
1881
|
+
type: z16.literal("siteplane:field-bridge:ready"),
|
|
1882
|
+
payload: z16.object({
|
|
1706
1883
|
renderedPath: realRenderedPathSchema,
|
|
1707
|
-
fieldCount:
|
|
1708
|
-
capabilities:
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1884
|
+
fieldCount: z16.number().int().nonnegative(),
|
|
1885
|
+
capabilities: z16.tuple([
|
|
1886
|
+
z16.literal("field_selection"),
|
|
1887
|
+
z16.literal("preview_values"),
|
|
1888
|
+
z16.literal("field_evidence"),
|
|
1889
|
+
z16.literal("visual_field_control")
|
|
1713
1890
|
])
|
|
1714
1891
|
}).strict()
|
|
1715
1892
|
});
|
|
1716
1893
|
var fieldBridgeFieldsMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1717
|
-
type:
|
|
1718
|
-
payload:
|
|
1894
|
+
type: z16.literal("siteplane:field-bridge:fields"),
|
|
1895
|
+
payload: z16.object({
|
|
1719
1896
|
renderedPath: realRenderedPathSchema,
|
|
1720
|
-
fields:
|
|
1897
|
+
fields: z16.array(fieldBridgeFieldSchema)
|
|
1721
1898
|
}).strict()
|
|
1722
1899
|
});
|
|
1723
1900
|
var fieldBridgeSelectFieldMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1724
|
-
type:
|
|
1725
|
-
payload:
|
|
1901
|
+
type: z16.literal("siteplane:field-bridge:select-field"),
|
|
1902
|
+
payload: z16.object({
|
|
1726
1903
|
fieldId: fieldIdSchema,
|
|
1727
1904
|
selectionMode: fieldSelectionModeSchema
|
|
1728
1905
|
}).strict()
|
|
1729
1906
|
});
|
|
1730
1907
|
var fieldBridgeSetSelectedFieldMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1731
|
-
type:
|
|
1732
|
-
payload:
|
|
1733
|
-
fieldIds:
|
|
1908
|
+
type: z16.literal("siteplane:field-bridge:set-selected-field"),
|
|
1909
|
+
payload: z16.object({
|
|
1910
|
+
fieldIds: z16.array(fieldIdSchema).max(200),
|
|
1734
1911
|
primaryFieldId: fieldIdSchema.nullable(),
|
|
1735
|
-
scrollIntoView:
|
|
1912
|
+
scrollIntoView: z16.boolean().optional()
|
|
1736
1913
|
}).strict().superRefine((value, context) => {
|
|
1737
1914
|
if (value.fieldIds.length > 0 && value.primaryFieldId === null) {
|
|
1738
1915
|
context.addIssue({
|
|
@@ -1751,58 +1928,58 @@ var fieldBridgeSetSelectedFieldMessageSchema = fieldBridgeBaseSchema.extend({
|
|
|
1751
1928
|
})
|
|
1752
1929
|
});
|
|
1753
1930
|
var fieldBridgeApplyPreviewValuesMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1754
|
-
type:
|
|
1755
|
-
payload:
|
|
1931
|
+
type: z16.literal("siteplane:field-bridge:apply-preview-values"),
|
|
1932
|
+
payload: z16.object({ values: z16.record(fieldIdSchema, bridgePreviewValueSchema) }).strict()
|
|
1756
1933
|
});
|
|
1757
1934
|
var fieldBridgeTestPreviewValuesMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1758
|
-
type:
|
|
1759
|
-
payload:
|
|
1935
|
+
type: z16.literal("siteplane:field-bridge:test-preview-values"),
|
|
1936
|
+
payload: z16.object({ fieldIds: z16.array(fieldIdSchema).min(1).max(200) }).strict()
|
|
1760
1937
|
});
|
|
1761
1938
|
var fieldBridgePreviewAckMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1762
|
-
type:
|
|
1763
|
-
payload:
|
|
1764
|
-
ackRequestId:
|
|
1765
|
-
kind:
|
|
1939
|
+
type: z16.literal("siteplane:field-bridge:preview-ack"),
|
|
1940
|
+
payload: z16.object({
|
|
1941
|
+
ackRequestId: z16.string().trim().min(1),
|
|
1942
|
+
kind: z16.enum(["apply", "test"]),
|
|
1766
1943
|
renderedPath: realRenderedPathSchema,
|
|
1767
|
-
fieldIds:
|
|
1944
|
+
fieldIds: z16.array(fieldIdSchema).max(200)
|
|
1768
1945
|
}).strict()
|
|
1769
1946
|
});
|
|
1770
1947
|
var fieldBridgeSetVisualControlStateMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1771
|
-
type:
|
|
1772
|
-
payload:
|
|
1773
|
-
enabled:
|
|
1774
|
-
fieldProximityEnabled:
|
|
1775
|
-
pendingKeys:
|
|
1776
|
-
activeFieldIds:
|
|
1777
|
-
fieldSections:
|
|
1778
|
-
selectedPending:
|
|
1779
|
-
key:
|
|
1780
|
-
locatorHint:
|
|
1948
|
+
type: z16.literal("siteplane:field-bridge:set-visual-control-state"),
|
|
1949
|
+
payload: z16.object({
|
|
1950
|
+
enabled: z16.boolean(),
|
|
1951
|
+
fieldProximityEnabled: z16.boolean(),
|
|
1952
|
+
pendingKeys: z16.array(z16.string().trim().min(1).max(2048)).max(50),
|
|
1953
|
+
activeFieldIds: z16.array(fieldIdSchema),
|
|
1954
|
+
fieldSections: z16.record(fieldIdSchema, bridgeSectionSchema),
|
|
1955
|
+
selectedPending: z16.array(z16.object({
|
|
1956
|
+
key: z16.string().trim().min(1).max(2048),
|
|
1957
|
+
locatorHint: z16.string().trim().min(1).max(2048)
|
|
1781
1958
|
}).strict()).max(50),
|
|
1782
|
-
scrollPendingIntoView:
|
|
1959
|
+
scrollPendingIntoView: z16.boolean().optional()
|
|
1783
1960
|
}).strict()
|
|
1784
1961
|
});
|
|
1785
1962
|
var fieldBridgeMissingContentMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1786
|
-
type:
|
|
1787
|
-
payload:
|
|
1963
|
+
type: z16.literal("siteplane:field-bridge:missing-content"),
|
|
1964
|
+
payload: z16.object({
|
|
1788
1965
|
action: fieldBridgeMissingContentActionSchema,
|
|
1789
1966
|
item: setupCorrectionItemSchema,
|
|
1790
1967
|
suggestedSection: bridgeSectionSchema.optional()
|
|
1791
1968
|
}).strict()
|
|
1792
1969
|
});
|
|
1793
1970
|
var fieldBridgePreviewScrolledMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1794
|
-
type:
|
|
1795
|
-
payload:
|
|
1971
|
+
type: z16.literal("siteplane:field-bridge:preview-scrolled"),
|
|
1972
|
+
payload: z16.object({ renderedPath: realRenderedPathSchema }).strict()
|
|
1796
1973
|
});
|
|
1797
1974
|
var fieldBridgeErrorMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1798
|
-
type:
|
|
1799
|
-
payload:
|
|
1800
|
-
code:
|
|
1801
|
-
message:
|
|
1802
|
-
recoverable:
|
|
1975
|
+
type: z16.literal("siteplane:field-bridge:error"),
|
|
1976
|
+
payload: z16.object({
|
|
1977
|
+
code: z16.string().trim().min(1),
|
|
1978
|
+
message: z16.string().trim().min(1),
|
|
1979
|
+
recoverable: z16.boolean().default(false)
|
|
1803
1980
|
}).strict()
|
|
1804
1981
|
});
|
|
1805
|
-
var fieldBridgeMessageSchema =
|
|
1982
|
+
var fieldBridgeMessageSchema = z16.discriminatedUnion("type", [
|
|
1806
1983
|
fieldBridgeInitMessageSchema,
|
|
1807
1984
|
fieldBridgeReadyMessageSchema,
|
|
1808
1985
|
fieldBridgeFieldsMessageSchema,
|
|
@@ -1818,51 +1995,51 @@ var fieldBridgeMessageSchema = z15.discriminatedUnion("type", [
|
|
|
1818
1995
|
]);
|
|
1819
1996
|
|
|
1820
1997
|
// ../shared/dist/commands.js
|
|
1821
|
-
import { z as
|
|
1998
|
+
import { z as z18 } from "zod";
|
|
1822
1999
|
|
|
1823
2000
|
// ../shared/dist/errors.js
|
|
1824
|
-
import { z as
|
|
2001
|
+
import { z as z17 } from "zod";
|
|
1825
2002
|
var UI_ERROR_CATEGORIES = [
|
|
1826
2003
|
"site_member",
|
|
1827
2004
|
"workspace_member",
|
|
1828
2005
|
"internal"
|
|
1829
2006
|
];
|
|
1830
|
-
var uiErrorCategorySchema =
|
|
1831
|
-
var commandErrorPayloadSchema =
|
|
1832
|
-
code:
|
|
1833
|
-
message:
|
|
2007
|
+
var uiErrorCategorySchema = z17.enum(UI_ERROR_CATEGORIES);
|
|
2008
|
+
var commandErrorPayloadSchema = z17.object({
|
|
2009
|
+
code: z17.string().min(1),
|
|
2010
|
+
message: z17.string().min(1),
|
|
1834
2011
|
category: uiErrorCategorySchema,
|
|
1835
|
-
details:
|
|
2012
|
+
details: z17.record(z17.string(), z17.unknown()).optional()
|
|
1836
2013
|
});
|
|
1837
2014
|
|
|
1838
2015
|
// ../shared/dist/commands.js
|
|
1839
|
-
var commandSuccessSchema =
|
|
1840
|
-
ok:
|
|
1841
|
-
data:
|
|
2016
|
+
var commandSuccessSchema = z18.object({
|
|
2017
|
+
ok: z18.literal(true),
|
|
2018
|
+
data: z18.unknown()
|
|
1842
2019
|
});
|
|
1843
|
-
var commandErrorSchema =
|
|
1844
|
-
ok:
|
|
2020
|
+
var commandErrorSchema = z18.object({
|
|
2021
|
+
ok: z18.literal(false),
|
|
1845
2022
|
error: commandErrorPayloadSchema
|
|
1846
2023
|
});
|
|
1847
|
-
var commandResultSchema =
|
|
2024
|
+
var commandResultSchema = z18.discriminatedUnion("ok", [
|
|
1848
2025
|
commandSuccessSchema,
|
|
1849
2026
|
commandErrorSchema
|
|
1850
2027
|
]);
|
|
1851
2028
|
|
|
1852
2029
|
// ../shared/dist/editor-settings.js
|
|
1853
|
-
import { z as
|
|
1854
|
-
var editorColorSchemeSchema =
|
|
2030
|
+
import { z as z19 } from "zod";
|
|
2031
|
+
var editorColorSchemeSchema = z19.enum([
|
|
1855
2032
|
"paper",
|
|
1856
2033
|
"slate",
|
|
1857
2034
|
"siteplane",
|
|
1858
2035
|
"bloom"
|
|
1859
2036
|
]);
|
|
1860
2037
|
var editorColorSchemes = editorColorSchemeSchema.options;
|
|
1861
|
-
var editorColorModeSchema =
|
|
2038
|
+
var editorColorModeSchema = z19.enum(["light", "dark"]);
|
|
1862
2039
|
var editorColorModes = editorColorModeSchema.options;
|
|
1863
2040
|
|
|
1864
2041
|
// ../shared/dist/site-slug.js
|
|
1865
|
-
import { z as
|
|
2042
|
+
import { z as z20 } from "zod";
|
|
1866
2043
|
var SITE_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
1867
2044
|
var RESERVED_SITE_SLUGS = /* @__PURE__ */ new Set([
|
|
1868
2045
|
"account",
|
|
@@ -1879,31 +2056,31 @@ var RESERVED_SITE_SLUGS = /* @__PURE__ */ new Set([
|
|
|
1879
2056
|
"support",
|
|
1880
2057
|
"workspace"
|
|
1881
2058
|
]);
|
|
1882
|
-
var siteSlugSchema =
|
|
2059
|
+
var siteSlugSchema = z20.string().regex(SITE_SLUG_PATTERN, "Invalid site slug").refine((value) => !RESERVED_SITE_SLUGS.has(value), "Reserved site slug");
|
|
1883
2060
|
|
|
1884
2061
|
// ../shared/dist/entitlements.js
|
|
1885
|
-
import { z as
|
|
1886
|
-
var entitlementSnapshotSchema =
|
|
1887
|
-
planKey:
|
|
1888
|
-
sitesLimit:
|
|
1889
|
-
siteMembersPerSiteLimit:
|
|
1890
|
-
customAdminDomainsEnabled:
|
|
1891
|
-
agentMcpEnabled:
|
|
1892
|
-
emailNotificationsEnabled:
|
|
1893
|
-
analyticsEnabled:
|
|
1894
|
-
analyticsSitesLimit:
|
|
1895
|
-
analyticsPortalPerformanceEnabled:
|
|
1896
|
-
analyticsMonthlyReportsEnabled:
|
|
1897
|
-
analyticsAiSummaryEnabled:
|
|
1898
|
-
analyticsRawEventRetentionDays:
|
|
1899
|
-
analyticsMonthlyEventLimit:
|
|
1900
|
-
bookingEnabled:
|
|
1901
|
-
bookingSitesLimit:
|
|
1902
|
-
bookingPortalManagementEnabled:
|
|
1903
|
-
bookingResourcesPerSiteLimit:
|
|
1904
|
-
bookingServicesPerSiteLimit:
|
|
1905
|
-
bookingMonthlyBookingsLimit:
|
|
1906
|
-
bookingRemindersEnabled:
|
|
2062
|
+
import { z as z21 } from "zod";
|
|
2063
|
+
var entitlementSnapshotSchema = z21.object({
|
|
2064
|
+
planKey: z21.string().trim().min(1),
|
|
2065
|
+
sitesLimit: z21.number().int().positive(),
|
|
2066
|
+
siteMembersPerSiteLimit: z21.number().int().positive(),
|
|
2067
|
+
customAdminDomainsEnabled: z21.boolean(),
|
|
2068
|
+
agentMcpEnabled: z21.boolean(),
|
|
2069
|
+
emailNotificationsEnabled: z21.boolean(),
|
|
2070
|
+
analyticsEnabled: z21.boolean(),
|
|
2071
|
+
analyticsSitesLimit: z21.number().int().min(0),
|
|
2072
|
+
analyticsPortalPerformanceEnabled: z21.boolean(),
|
|
2073
|
+
analyticsMonthlyReportsEnabled: z21.boolean(),
|
|
2074
|
+
analyticsAiSummaryEnabled: z21.boolean(),
|
|
2075
|
+
analyticsRawEventRetentionDays: z21.number().int().min(1).max(ANALYTICS_RETENTION_DEFAULTS.maxRawEventRetentionDaysWithoutAdr),
|
|
2076
|
+
analyticsMonthlyEventLimit: z21.number().int().min(0),
|
|
2077
|
+
bookingEnabled: z21.boolean().default(false),
|
|
2078
|
+
bookingSitesLimit: z21.number().int().min(0).default(0),
|
|
2079
|
+
bookingPortalManagementEnabled: z21.boolean().default(false),
|
|
2080
|
+
bookingResourcesPerSiteLimit: z21.number().int().min(0).default(0),
|
|
2081
|
+
bookingServicesPerSiteLimit: z21.number().int().min(0).default(0),
|
|
2082
|
+
bookingMonthlyBookingsLimit: z21.number().int().min(0).default(0),
|
|
2083
|
+
bookingRemindersEnabled: z21.boolean().default(false)
|
|
1907
2084
|
});
|
|
1908
2085
|
var EARLY_ACCESS_PLAN = {
|
|
1909
2086
|
planKey: "early_access_2026",
|
|
@@ -1929,7 +2106,7 @@ var EARLY_ACCESS_PLAN = {
|
|
|
1929
2106
|
};
|
|
1930
2107
|
|
|
1931
2108
|
// ../shared/dist/readiness.js
|
|
1932
|
-
import { z as
|
|
2109
|
+
import { z as z22 } from "zod";
|
|
1933
2110
|
var READINESS_CHECK_KEYS = [
|
|
1934
2111
|
"agent_instructions_installed",
|
|
1935
2112
|
"runtime_package_connected",
|
|
@@ -1944,19 +2121,19 @@ var READINESS_STATUSES = [
|
|
|
1944
2121
|
"failing",
|
|
1945
2122
|
"warning"
|
|
1946
2123
|
];
|
|
1947
|
-
var readinessCheckKeySchema =
|
|
1948
|
-
var readinessStatusSchema =
|
|
2124
|
+
var readinessCheckKeySchema = z22.enum(READINESS_CHECK_KEYS);
|
|
2125
|
+
var readinessStatusSchema = z22.enum(READINESS_STATUSES);
|
|
1949
2126
|
|
|
1950
2127
|
// ../shared/dist/site.js
|
|
1951
|
-
import { z as
|
|
2128
|
+
import { z as z23 } from "zod";
|
|
1952
2129
|
var SITE_STATUSES = [
|
|
1953
2130
|
"setup",
|
|
1954
2131
|
"active",
|
|
1955
2132
|
"disabled",
|
|
1956
2133
|
"archived"
|
|
1957
2134
|
];
|
|
1958
|
-
var siteStatusSchema =
|
|
1959
|
-
var websiteUrlSchema =
|
|
2135
|
+
var siteStatusSchema = z23.enum(SITE_STATUSES);
|
|
2136
|
+
var websiteUrlSchema = z23.string().trim().url().refine((value) => value.startsWith("https://") || value.startsWith("http://"), "Website URL must use http or https");
|
|
1960
2137
|
|
|
1961
2138
|
// ../cli/dist/analytics/manifest.js
|
|
1962
2139
|
async function readAnalyticsManifestFile(manifestPath) {
|
|
@@ -2283,17 +2460,17 @@ function readJsxAttributes(attributes) {
|
|
|
2283
2460
|
if (!ts.isJsxAttribute(attribute)) {
|
|
2284
2461
|
continue;
|
|
2285
2462
|
}
|
|
2286
|
-
const
|
|
2463
|
+
const name2 = attribute.name.getText();
|
|
2287
2464
|
if (!attribute.initializer) {
|
|
2288
|
-
values.set(
|
|
2465
|
+
values.set(name2, true);
|
|
2289
2466
|
continue;
|
|
2290
2467
|
}
|
|
2291
2468
|
if (ts.isStringLiteral(attribute.initializer)) {
|
|
2292
|
-
values.set(
|
|
2469
|
+
values.set(name2, attribute.initializer.text);
|
|
2293
2470
|
continue;
|
|
2294
2471
|
}
|
|
2295
2472
|
if (ts.isJsxExpression(attribute.initializer)) {
|
|
2296
|
-
values.set(
|
|
2473
|
+
values.set(name2, staticExpressionValue(attribute.initializer.expression));
|
|
2297
2474
|
}
|
|
2298
2475
|
}
|
|
2299
2476
|
return values;
|
|
@@ -2761,16 +2938,16 @@ async function analyticsReadinessCommand(input) {
|
|
|
2761
2938
|
}
|
|
2762
2939
|
|
|
2763
2940
|
// ../cli/dist/commands/analytics/public-key.js
|
|
2764
|
-
import { z as
|
|
2765
|
-
var responseSchema =
|
|
2766
|
-
ok:
|
|
2767
|
-
data:
|
|
2768
|
-
rawPublicKey:
|
|
2941
|
+
import { z as z24 } from "zod";
|
|
2942
|
+
var responseSchema = z24.object({
|
|
2943
|
+
ok: z24.literal(true),
|
|
2944
|
+
data: z24.object({
|
|
2945
|
+
rawPublicKey: z24.string().regex(/^pk_siteplane_[A-Za-z0-9_-]{16}_[A-Za-z0-9_-]{43}$/)
|
|
2769
2946
|
})
|
|
2770
2947
|
});
|
|
2771
|
-
var errorResponseSchema =
|
|
2772
|
-
error:
|
|
2773
|
-
code:
|
|
2948
|
+
var errorResponseSchema = z24.object({
|
|
2949
|
+
error: z24.object({
|
|
2950
|
+
code: z24.string().regex(/^[a-z][a-z0-9_.-]+$/u)
|
|
2774
2951
|
})
|
|
2775
2952
|
});
|
|
2776
2953
|
async function analyticsPublicKeyCommand(input) {
|
|
@@ -2990,14 +3167,22 @@ async function bookingTestCommand(input) {
|
|
|
2990
3167
|
import { access, chmod, open, readFile as readFile5, rename, rm } from "fs/promises";
|
|
2991
3168
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2992
3169
|
import { join as join3 } from "path";
|
|
2993
|
-
import { z as
|
|
2994
|
-
var siteplaneProjectConfigSchema =
|
|
2995
|
-
version:
|
|
2996
|
-
apiBaseUrl:
|
|
2997
|
-
siteId:
|
|
2998
|
-
publicSiteKeyId:
|
|
2999
|
-
publicSiteKey:
|
|
3000
|
-
|
|
3170
|
+
import { z as z25 } from "zod";
|
|
3171
|
+
var siteplaneProjectConfigSchema = z25.object({
|
|
3172
|
+
version: z25.literal(2),
|
|
3173
|
+
apiBaseUrl: z25.string().url(),
|
|
3174
|
+
siteId: z25.string().uuid(),
|
|
3175
|
+
publicSiteKeyId: z25.string().uuid(),
|
|
3176
|
+
publicSiteKey: z25.string().trim().min(1),
|
|
3177
|
+
runId: z25.string().uuid(),
|
|
3178
|
+
appDirectory: z25.string().min(1),
|
|
3179
|
+
taskContractVersion: z25.literal("siteplane.setup-task.v2"),
|
|
3180
|
+
adminProtocolVersion: z25.literal(2),
|
|
3181
|
+
packages: z25.object({
|
|
3182
|
+
siteplane: z25.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u),
|
|
3183
|
+
runtimeNext: z25.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u)
|
|
3184
|
+
}).strict(),
|
|
3185
|
+
fieldContractHash: z25.string().regex(/^sha256:[0-9a-f]{64}$/iu).optional()
|
|
3001
3186
|
}).strict();
|
|
3002
3187
|
async function readProjectPackageJson(projectDir) {
|
|
3003
3188
|
return JSON.parse(await readFile5(join3(projectDir, "package.json"), "utf8"));
|
|
@@ -3054,16 +3239,21 @@ async function writeJsonAtomically(path, value, mode) {
|
|
|
3054
3239
|
// ../cli/dist/config/credentials.js
|
|
3055
3240
|
import { execFile } from "child_process";
|
|
3056
3241
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3057
|
-
import { chmod as chmod2, mkdir as mkdir5, open as open2, readFile as readFile6, rename as rename2, rm as rm2 } from "fs/promises";
|
|
3242
|
+
import { chmod as chmod2, lstat, mkdir as mkdir5, open as open2, readFile as readFile6, rename as rename2, rm as rm2 } from "fs/promises";
|
|
3058
3243
|
import { dirname as dirname5, join as join4, relative } from "path";
|
|
3059
3244
|
import { promisify } from "util";
|
|
3060
|
-
import { z as
|
|
3061
|
-
var siteplaneCredentialsSchema =
|
|
3062
|
-
accessToken:
|
|
3063
|
-
siteRevalidationSecret:
|
|
3245
|
+
import { z as z26 } from "zod";
|
|
3246
|
+
var siteplaneCredentialsSchema = z26.object({
|
|
3247
|
+
accessToken: z26.string().trim().min(1),
|
|
3248
|
+
siteRevalidationSecret: z26.string().trim().min(1),
|
|
3249
|
+
adminSession: z26.object({
|
|
3250
|
+
secret: z26.string().regex(/^[A-Za-z0-9_-]{43}$/u),
|
|
3251
|
+
generation: z26.string().uuid()
|
|
3252
|
+
}).strict().optional()
|
|
3064
3253
|
}).strict();
|
|
3065
3254
|
async function assertLocalCredentialsPathSafe(projectDir, options = {}) {
|
|
3066
3255
|
const path = join4(projectDir, ".siteplane/credentials.json");
|
|
3256
|
+
await assertCredentialFilesystemSafe(projectDir);
|
|
3067
3257
|
const isTrackedFile = options.isTrackedFile ?? ((targetPath) => gitPathMatches(projectDir, targetPath, "ls-files"));
|
|
3068
3258
|
const isIgnoredFile = options.isIgnoredFile ?? ((targetPath) => gitPathMatches(projectDir, targetPath, "check-ignore"));
|
|
3069
3259
|
if (await isTrackedFile(path)) {
|
|
@@ -3074,6 +3264,27 @@ async function assertLocalCredentialsPathSafe(projectDir, options = {}) {
|
|
|
3074
3264
|
}
|
|
3075
3265
|
return path;
|
|
3076
3266
|
}
|
|
3267
|
+
async function assertCredentialFilesystemSafe(projectDir) {
|
|
3268
|
+
for (const [path, directory, mode] of [
|
|
3269
|
+
[projectDir, true, null],
|
|
3270
|
+
[join4(projectDir, ".siteplane"), true, 448],
|
|
3271
|
+
[join4(projectDir, ".siteplane/credentials.json"), false, 384]
|
|
3272
|
+
]) {
|
|
3273
|
+
const stat = await lstat(path).catch((error) => {
|
|
3274
|
+
if (error.code === "ENOENT")
|
|
3275
|
+
return null;
|
|
3276
|
+
throw error;
|
|
3277
|
+
});
|
|
3278
|
+
if (!stat)
|
|
3279
|
+
continue;
|
|
3280
|
+
if (stat.isSymbolicLink() || (directory ? !stat.isDirectory() : !stat.isFile())) {
|
|
3281
|
+
throw new Error("Siteplane credential paths must not contain symlinks or unexpected file types.");
|
|
3282
|
+
}
|
|
3283
|
+
if (mode !== null && (stat.mode & 511) !== mode) {
|
|
3284
|
+
throw new Error(`Siteplane credential ${directory ? "directory" : "file"} permissions must be ${mode.toString(8)} before setup.`);
|
|
3285
|
+
}
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3077
3288
|
async function writeLocalCredentials(projectDir, credentials, options = {}) {
|
|
3078
3289
|
const path = await assertLocalCredentialsPathSafe(projectDir, options);
|
|
3079
3290
|
const parsed = siteplaneCredentialsSchema.parse(credentials);
|
|
@@ -3082,6 +3293,7 @@ async function writeLocalCredentials(projectDir, credentials, options = {}) {
|
|
|
3082
3293
|
return path;
|
|
3083
3294
|
}
|
|
3084
3295
|
async function readLocalCredentials(projectDir) {
|
|
3296
|
+
await assertCredentialFilesystemSafe(projectDir);
|
|
3085
3297
|
return siteplaneCredentialsSchema.parse(JSON.parse(await readFile6(join4(projectDir, ".siteplane/credentials.json"), "utf8")));
|
|
3086
3298
|
}
|
|
3087
3299
|
async function writeCredentialsAtomically(path, credentials) {
|
|
@@ -3117,8 +3329,467 @@ async function gitPathMatches(projectDir, path, command) {
|
|
|
3117
3329
|
}
|
|
3118
3330
|
|
|
3119
3331
|
// ../cli/dist/commands/init.js
|
|
3332
|
+
import { access as access3 } from "fs/promises";
|
|
3333
|
+
import { join as join7, relative as relative3 } from "path";
|
|
3334
|
+
import { z as z29 } from "zod";
|
|
3335
|
+
|
|
3336
|
+
// ../cli/dist/commands/setup-preflight.js
|
|
3337
|
+
import { createHash as createHash2 } from "crypto";
|
|
3338
|
+
import semver from "semver";
|
|
3339
|
+
import { minimatch } from "minimatch";
|
|
3340
|
+
import { parse as parseYaml } from "yaml";
|
|
3341
|
+
|
|
3342
|
+
// ../cli/dist/config/installed-package.js
|
|
3343
|
+
import { createRequire } from "module";
|
|
3344
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
3120
3345
|
import { join as join5 } from "path";
|
|
3121
|
-
import { z as
|
|
3346
|
+
import { z as z27 } from "zod";
|
|
3347
|
+
var packageSchema = z27.object({
|
|
3348
|
+
name: z27.string().optional(),
|
|
3349
|
+
version: z27.string(),
|
|
3350
|
+
peerDependencies: z27.record(z27.string(), z27.string()).optional()
|
|
3351
|
+
});
|
|
3352
|
+
async function readInstalledPackage(projectDir, name2) {
|
|
3353
|
+
const require2 = createRequire(join5(projectDir, "package.json"));
|
|
3354
|
+
for (const directory of require2.resolve.paths(name2) ?? []) {
|
|
3355
|
+
try {
|
|
3356
|
+
return packageSchema.parse(JSON.parse(await readFile7(join5(directory, name2, "package.json"), "utf8")));
|
|
3357
|
+
} catch (error) {
|
|
3358
|
+
if (error.code !== "ENOENT")
|
|
3359
|
+
throw error;
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3362
|
+
throw new Error(`Install ${name2} with the canonical package manager before setup.`);
|
|
3363
|
+
}
|
|
3364
|
+
async function assertExactSetupPackages(projectDir, packages) {
|
|
3365
|
+
const manifest = z27.object({
|
|
3366
|
+
dependencies: z27.record(z27.string(), z27.string()).optional(),
|
|
3367
|
+
devDependencies: z27.record(z27.string(), z27.string()).optional()
|
|
3368
|
+
}).parse(JSON.parse(await readFile7(join5(projectDir, "package.json"), "utf8")));
|
|
3369
|
+
for (const [name2, version2] of [
|
|
3370
|
+
["siteplane", packages.siteplane],
|
|
3371
|
+
["@siteplane/runtime-next", packages.runtimeNext]
|
|
3372
|
+
]) {
|
|
3373
|
+
const declared = manifest.dependencies?.[name2] ?? manifest.devDependencies?.[name2];
|
|
3374
|
+
const installed = await readInstalledPackage(projectDir, name2);
|
|
3375
|
+
if (declared !== version2 || installed.version !== version2)
|
|
3376
|
+
throw new Error("The installed Siteplane packages must match the current task's exact pins.");
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
|
|
3380
|
+
// ../cli/dist/commands/setup-preflight.js
|
|
3381
|
+
import { access as access2, readFile as readFile8, realpath, readdir, readlink } from "fs/promises";
|
|
3382
|
+
import { dirname as dirname6, join as join6, relative as relative2, resolve, sep } from "path";
|
|
3383
|
+
import { z as z28 } from "zod";
|
|
3384
|
+
|
|
3385
|
+
// ../cli/dist/commands/setup-process.js
|
|
3386
|
+
import { spawn } from "child_process";
|
|
3387
|
+
var SetupFailure = class extends Error {
|
|
3388
|
+
code;
|
|
3389
|
+
constructor(code, message) {
|
|
3390
|
+
super(message);
|
|
3391
|
+
this.code = code;
|
|
3392
|
+
}
|
|
3393
|
+
};
|
|
3394
|
+
async function setupProcess(binary, args, options) {
|
|
3395
|
+
return new Promise((resolve5, reject) => {
|
|
3396
|
+
const child = spawn(binary, args, {
|
|
3397
|
+
cwd: options.cwd,
|
|
3398
|
+
env: process.env,
|
|
3399
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3400
|
+
});
|
|
3401
|
+
const chunks = [];
|
|
3402
|
+
const errors = [];
|
|
3403
|
+
let errorBytes = 0;
|
|
3404
|
+
let bytes = 0;
|
|
3405
|
+
let exceeded = false;
|
|
3406
|
+
const timer = setTimeout(() => {
|
|
3407
|
+
exceeded = true;
|
|
3408
|
+
child.kill("SIGKILL");
|
|
3409
|
+
}, options.timeoutMs ?? 12e4);
|
|
3410
|
+
const capture = (chunk) => {
|
|
3411
|
+
bytes += chunk.length;
|
|
3412
|
+
if (bytes > 4 * 1024 * 1024) {
|
|
3413
|
+
exceeded = true;
|
|
3414
|
+
child.kill("SIGKILL");
|
|
3415
|
+
} else
|
|
3416
|
+
chunks.push(chunk);
|
|
3417
|
+
};
|
|
3418
|
+
child.stdout.on("data", capture);
|
|
3419
|
+
child.stderr.on("data", (chunk) => {
|
|
3420
|
+
errorBytes += chunk.length;
|
|
3421
|
+
if (errorBytes <= 4 * 1024 * 1024)
|
|
3422
|
+
errors.push(chunk);
|
|
3423
|
+
else {
|
|
3424
|
+
exceeded = true;
|
|
3425
|
+
child.kill("SIGKILL");
|
|
3426
|
+
}
|
|
3427
|
+
});
|
|
3428
|
+
child.on("error", () => {
|
|
3429
|
+
clearTimeout(timer);
|
|
3430
|
+
reject(new SetupFailure("local_tool_unavailable", `${binary} could not be started. Check the local installation and access.`));
|
|
3431
|
+
});
|
|
3432
|
+
child.on("close", (code) => {
|
|
3433
|
+
clearTimeout(timer);
|
|
3434
|
+
if (exceeded)
|
|
3435
|
+
reject(new SetupFailure("provider_timeout", "The operation exceeded its time or response limit; inspect the existing operation before retrying."));
|
|
3436
|
+
else if (code !== 0) {
|
|
3437
|
+
const diagnostic = Buffer.concat([...chunks, ...errors]).toString("utf8");
|
|
3438
|
+
if (/\b(?:401|403|unauthorized|forbidden|not authenticated|not authorized|login required|E401|E403)\b/iu.test(diagnostic))
|
|
3439
|
+
reject(new SetupFailure("provider_access_denied", `${binary} denied access. Restore the existing local login and project/package permissions before retrying.`));
|
|
3440
|
+
else if (/\b(?:ECONNRESET|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|ECONNREFUSED|429|502|503|504)\b/iu.test(diagnostic))
|
|
3441
|
+
reject(new SetupFailure("provider_transport_failed", `${binary} could not reach its provider. Inspect the existing operation before retrying.`));
|
|
3442
|
+
else
|
|
3443
|
+
reject(new SetupFailure("local_command_failed", `${binary} failed. Check its local login, permissions and project configuration.`));
|
|
3444
|
+
} else
|
|
3445
|
+
resolve5(Buffer.concat(options.includeStderr ? [...chunks, ...errors] : chunks).toString("utf8"));
|
|
3446
|
+
});
|
|
3447
|
+
child.stdin.on("error", () => {
|
|
3448
|
+
});
|
|
3449
|
+
child.stdin.end(options.input);
|
|
3450
|
+
});
|
|
3451
|
+
}
|
|
3452
|
+
async function setupVercelApi(cwd, endpoint, body, method, options) {
|
|
3453
|
+
const args = ["api", endpoint, "--raw"];
|
|
3454
|
+
if (body !== void 0)
|
|
3455
|
+
args.push("--method", method ?? "POST", "--input", "-");
|
|
3456
|
+
else if (method)
|
|
3457
|
+
args.push("--method", method);
|
|
3458
|
+
let output = "";
|
|
3459
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
3460
|
+
const remaining = (options?.deadline ?? Date.now() + 3e4) - Date.now();
|
|
3461
|
+
if (remaining <= 0)
|
|
3462
|
+
throw new SetupFailure("provider_deadline_exceeded", "The provider deadline expired; inspect the recorded operation before continuing.");
|
|
3463
|
+
try {
|
|
3464
|
+
output = await setupProcess("vercel", args, {
|
|
3465
|
+
cwd,
|
|
3466
|
+
timeoutMs: Math.min(3e4, remaining),
|
|
3467
|
+
...body === void 0 ? {} : { input: JSON.stringify(body) }
|
|
3468
|
+
});
|
|
3469
|
+
break;
|
|
3470
|
+
} catch (error) {
|
|
3471
|
+
if (!(error instanceof SetupFailure) || error.code !== "provider_transport_failed" || attempt === 2)
|
|
3472
|
+
throw error;
|
|
3473
|
+
await new Promise((resolve5) => setTimeout(resolve5, Math.min(1e3 * 2 ** attempt, Math.max(0, (options?.deadline ?? Date.now() + 2e3) - Date.now()))));
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
try {
|
|
3477
|
+
return JSON.parse(output);
|
|
3478
|
+
} catch {
|
|
3479
|
+
throw new SetupFailure("invalid_provider_response", "Vercel did not return valid JSON for the bound project.");
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
|
|
3483
|
+
// ../cli/dist/commands/setup-preflight.js
|
|
3484
|
+
var manifestSchema = z28.object({
|
|
3485
|
+
packageManager: z28.string().optional(),
|
|
3486
|
+
engines: z28.object({ node: z28.string().optional() }).optional(),
|
|
3487
|
+
dependencies: z28.record(z28.string(), z28.string()).optional(),
|
|
3488
|
+
devDependencies: z28.record(z28.string(), z28.string()).optional(),
|
|
3489
|
+
scripts: z28.record(z28.string(), z28.string()).optional(),
|
|
3490
|
+
workspaces: z28.unknown().optional()
|
|
3491
|
+
});
|
|
3492
|
+
var vercelProjectSchema = z28.object({
|
|
3493
|
+
id: z28.string(),
|
|
3494
|
+
accountId: z28.string(),
|
|
3495
|
+
name: z28.string(),
|
|
3496
|
+
nodeVersion: z28.string().nullable().optional(),
|
|
3497
|
+
rootDirectory: z28.string().nullable().optional(),
|
|
3498
|
+
framework: z28.string().nullable().optional(),
|
|
3499
|
+
installCommand: z28.string().nullable().optional(),
|
|
3500
|
+
buildCommand: z28.string().nullable().optional(),
|
|
3501
|
+
link: z28.object({
|
|
3502
|
+
type: z28.string(),
|
|
3503
|
+
repo: z28.string().optional(),
|
|
3504
|
+
repoId: z28.union([z28.string(), z28.number()]).optional(),
|
|
3505
|
+
org: z28.string().optional(),
|
|
3506
|
+
productionBranch: z28.string().optional()
|
|
3507
|
+
}).nullable().optional()
|
|
3508
|
+
});
|
|
3509
|
+
var exists = async (path) => access2(path).then(() => true, () => false);
|
|
3510
|
+
var fail = (code, message) => {
|
|
3511
|
+
throw new SetupFailure(code, message);
|
|
3512
|
+
};
|
|
3513
|
+
var stable = (version2, major) => new RegExp(`^${major}\\.\\d+\\.\\d+$`, "u").test(version2);
|
|
3514
|
+
async function inspectSetupLocalProject(projectDir) {
|
|
3515
|
+
const projectDirectory = await realpath(resolve(projectDir));
|
|
3516
|
+
const manifest = manifestSchema.parse(JSON.parse(await readFile8(join6(projectDirectory, "package.json"), "utf8")));
|
|
3517
|
+
const apps = (await Promise.all(["app", "src/app"].map(async (path) => await exists(join6(projectDirectory, path)) ? path : null))).filter((path) => path !== null);
|
|
3518
|
+
if (apps.length !== 1)
|
|
3519
|
+
fail("ambiguous_app_directory", "Select one Next.js App Router application with exactly one app/ or src/app/ directory.");
|
|
3520
|
+
const repositoryRoot = (await setupProcess("git", ["rev-parse", "--show-toplevel"], {
|
|
3521
|
+
cwd: projectDirectory
|
|
3522
|
+
})).trim();
|
|
3523
|
+
const tracked = await setupProcess("git", ["ls-files", "--stage", "-z"], {
|
|
3524
|
+
cwd: repositoryRoot
|
|
3525
|
+
});
|
|
3526
|
+
for (const entry of tracked.split("\0").filter(Boolean)) {
|
|
3527
|
+
if (entry.startsWith("160000 "))
|
|
3528
|
+
fail("git_submodule_conflict", "The repository contains Git submodules. Resolve the explicit deployment source boundary before setup; the CLI will not copy external repositories.");
|
|
3529
|
+
if (!entry.startsWith("120000 "))
|
|
3530
|
+
continue;
|
|
3531
|
+
const path = join6(repositoryRoot, entry.slice(entry.indexOf(" ") + 1));
|
|
3532
|
+
const target = resolve(dirname6(path), await readlink(path));
|
|
3533
|
+
const bound = relative2(repositoryRoot, target);
|
|
3534
|
+
if (bound.startsWith("..") || bound.startsWith(sep))
|
|
3535
|
+
fail("external_symlink_conflict", "A tracked symbolic link points outside this repository. Resolve that source boundary before setup; local external files cannot enter a deployment.");
|
|
3536
|
+
}
|
|
3537
|
+
let lockfileRoot = projectDirectory;
|
|
3538
|
+
const lockNames = [
|
|
3539
|
+
"package-lock.json",
|
|
3540
|
+
"npm-shrinkwrap.json",
|
|
3541
|
+
"pnpm-lock.yaml",
|
|
3542
|
+
"yarn.lock",
|
|
3543
|
+
"bun.lock",
|
|
3544
|
+
"bun.lockb"
|
|
3545
|
+
];
|
|
3546
|
+
let locks = [];
|
|
3547
|
+
for (; ; ) {
|
|
3548
|
+
locks = (await Promise.all(lockNames.map(async (name3) => await exists(join6(lockfileRoot, name3)) ? name3 : null))).filter((name3) => name3 !== null);
|
|
3549
|
+
if (locks.length || lockfileRoot === repositoryRoot || dirname6(lockfileRoot) === lockfileRoot)
|
|
3550
|
+
break;
|
|
3551
|
+
lockfileRoot = dirname6(lockfileRoot);
|
|
3552
|
+
}
|
|
3553
|
+
if (locks.length > 1)
|
|
3554
|
+
fail("conflicting_lockfiles", "Resolve conflicting package-manager lockfiles before setup; Siteplane does not replace them.");
|
|
3555
|
+
if (!locks.length)
|
|
3556
|
+
fail("lockfile_missing", "Install the existing application with its chosen package manager to create one canonical lockfile.");
|
|
3557
|
+
const lockName = locks[0];
|
|
3558
|
+
if (!["package-lock.json", "pnpm-lock.yaml"].includes(lockName))
|
|
3559
|
+
fail("unsupported_package_manager", "This setup supports npm 10 or pnpm 9/10 with their canonical lockfile.");
|
|
3560
|
+
const rootManifest = manifestSchema.parse(JSON.parse(await readFile8(join6(lockfileRoot, "package.json"), "utf8")));
|
|
3561
|
+
if (lockfileRoot !== projectDirectory) {
|
|
3562
|
+
const workspaceFile = join6(lockfileRoot, "pnpm-workspace.yaml");
|
|
3563
|
+
const workspace = lockName === "pnpm-lock.yaml" && await exists(workspaceFile) ? z28.object({ packages: z28.array(z28.string()) }).parse(parseYaml(await readFile8(workspaceFile, "utf8"))).packages : z28.union([
|
|
3564
|
+
z28.array(z28.string()),
|
|
3565
|
+
z28.object({ packages: z28.array(z28.string()) }).transform((value) => value.packages)
|
|
3566
|
+
]).parse(rootManifest.workspaces);
|
|
3567
|
+
const selected = relative2(lockfileRoot, projectDirectory).split(sep).join("/");
|
|
3568
|
+
if (!workspace.some((pattern) => !pattern.startsWith("!") && minimatch(selected, pattern)) || workspace.some((pattern) => pattern.startsWith("!") && minimatch(selected, pattern.slice(1))))
|
|
3569
|
+
fail("unbound_workspace_app", "The selected application is excluded from the canonical lockfile's workspace packages. Select its actual application root.");
|
|
3570
|
+
}
|
|
3571
|
+
for (let ancestor = dirname6(lockfileRoot); ancestor.startsWith(repositoryRoot + sep) || ancestor === repositoryRoot; ancestor = dirname6(ancestor)) {
|
|
3572
|
+
const ancestorLocks = await Promise.all(lockNames.map((name3) => exists(join6(ancestor, name3))));
|
|
3573
|
+
if (ancestorLocks.some(Boolean))
|
|
3574
|
+
fail("ambiguous_lockfile_root", "Both the selected package and its repository ancestors contain lockfiles. Select one canonical workspace resolution before setup.");
|
|
3575
|
+
if (ancestor === repositoryRoot)
|
|
3576
|
+
break;
|
|
3577
|
+
}
|
|
3578
|
+
const lockText = await readFile8(join6(lockfileRoot, lockName), "utf8");
|
|
3579
|
+
const npmLock = lockName === "package-lock.json" ? z28.object({
|
|
3580
|
+
lockfileVersion: z28.number(),
|
|
3581
|
+
packages: z28.record(z28.string(), z28.object({
|
|
3582
|
+
version: z28.string().optional(),
|
|
3583
|
+
peerDependencies: z28.record(z28.string(), z28.string()).optional()
|
|
3584
|
+
})).optional()
|
|
3585
|
+
}).parse(JSON.parse(lockText)) : null;
|
|
3586
|
+
async function version2(name3) {
|
|
3587
|
+
try {
|
|
3588
|
+
return (await readInstalledPackage(projectDirectory, name3)).version;
|
|
3589
|
+
} catch {
|
|
3590
|
+
const resolved = npmLock?.packages?.[`node_modules/${name3}`]?.version;
|
|
3591
|
+
if (resolved)
|
|
3592
|
+
return resolved;
|
|
3593
|
+
return fail("dependencies_not_installed", `Install the existing ${name3} dependency using the canonical lockfile before setup.`);
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
const [nextVersion, reactVersion, reactDomVersion] = await Promise.all([
|
|
3597
|
+
version2("next"),
|
|
3598
|
+
version2("react"),
|
|
3599
|
+
version2("react-dom")
|
|
3600
|
+
]);
|
|
3601
|
+
if (!stable(nextVersion, 16))
|
|
3602
|
+
fail("unsupported_next_version", "Setup supports stable Next.js 16 App Router projects. Framework migration requires a separate owner decision.");
|
|
3603
|
+
if (!stable(reactVersion, 19) || reactDomVersion !== reactVersion)
|
|
3604
|
+
fail("react_version_conflict", "Use the same stable React 19/react-dom version compatible with the installed Next.js peers.");
|
|
3605
|
+
if (!stable(process.versions.node, 22))
|
|
3606
|
+
fail("unsupported_node_version", "Run setup under Node.js 22; this does not change your framework or package-manager versions.");
|
|
3607
|
+
const nextMetadata = await readInstalledPackage(projectDirectory, "next").catch(() => npmLock?.packages?.["node_modules/next"] ?? null);
|
|
3608
|
+
const peers = nextMetadata?.peerDependencies;
|
|
3609
|
+
if (!peers?.react || !peers["react-dom"])
|
|
3610
|
+
fail("next_peer_metadata_missing", "Install the locked Next.js dependency before setup so its actual React peer requirements can be checked.");
|
|
3611
|
+
if (!semver.satisfies(reactVersion, peers.react) || !semver.satisfies(reactDomVersion, peers["react-dom"]))
|
|
3612
|
+
fail("react_peer_conflict", "The installed React pair does not satisfy the installed Next.js peer requirements. Choose a compatible pair explicitly.");
|
|
3613
|
+
if (manifest.engines?.node && !semver.satisfies(process.versions.node, manifest.engines.node))
|
|
3614
|
+
fail("node_engine_conflict", "The selected application's Node engine declaration excludes the active supported Node.js 22 runtime.");
|
|
3615
|
+
const name2 = lockName === "package-lock.json" ? "npm" : "pnpm";
|
|
3616
|
+
const declared = rootManifest.packageManager ?? manifest.packageManager ?? null;
|
|
3617
|
+
if (rootManifest.packageManager && manifest.packageManager && rootManifest.packageManager !== manifest.packageManager)
|
|
3618
|
+
fail("package_manager_conflict", "Application and workspace packageManager declarations disagree.");
|
|
3619
|
+
if (declared && !new RegExp(`^${name2}@\\d+\\.\\d+\\.\\d+$`, "u").test(declared))
|
|
3620
|
+
fail("package_manager_conflict", "Declare an exact supported npm/pnpm packageManager version matching the canonical lockfile.");
|
|
3621
|
+
const localVersion = (await setupProcess(declared ? "npx" : name2, declared ? ["--yes", declared, "--version"] : ["--version"], { cwd: lockfileRoot })).trim();
|
|
3622
|
+
if (!(name2 === "npm" ? stable(localVersion, 10) : stable(localVersion, 9) || stable(localVersion, 10)))
|
|
3623
|
+
fail("unsupported_package_manager_version", "Use npm 10 or pnpm 9/10; other versions are outside the initial setup support range.");
|
|
3624
|
+
if (declared && declared !== `${name2}@${localVersion}`)
|
|
3625
|
+
fail("package_manager_conflict", "The actual local package-manager version differs from the project's exact packageManager declaration. Select the declared tool without rewriting the lockfile.");
|
|
3626
|
+
const format = npmLock ? String(npmLock.lockfileVersion) : /^lockfileVersion:\s*['"]?([\d.]+)/mu.exec(lockText)?.[1];
|
|
3627
|
+
if (name2 === "npm" && format !== "3" || name2 === "pnpm" && format !== "9.0" && format !== "9")
|
|
3628
|
+
fail("lockfile_format_conflict", "The canonical lockfile format does not match npm 10 or pnpm 9/10.");
|
|
3629
|
+
const sourceConflicts = [];
|
|
3630
|
+
for (const file of ["next.config.js", "next.config.mjs", "next.config.ts"]) {
|
|
3631
|
+
if (!await exists(join6(projectDirectory, file)))
|
|
3632
|
+
continue;
|
|
3633
|
+
const source = await readFile8(join6(projectDirectory, file), "utf8");
|
|
3634
|
+
if (/output\s*:\s*['"]export['"]/u.test(source))
|
|
3635
|
+
sourceConflicts.push("static_export");
|
|
3636
|
+
if (/\bbasePath\s*:/u.test(source))
|
|
3637
|
+
sourceConflicts.push("base_path");
|
|
3638
|
+
}
|
|
3639
|
+
if (sourceConflicts.length)
|
|
3640
|
+
fail("unsupported_server_configuration", "Resolve static export/basePath configuration explicitly; Siteplane requires its server-side admin and revalidation handlers.");
|
|
3641
|
+
for (const name3 of [
|
|
3642
|
+
"middleware.ts",
|
|
3643
|
+
"middleware.js",
|
|
3644
|
+
"proxy.ts",
|
|
3645
|
+
"proxy.js",
|
|
3646
|
+
"src/middleware.ts",
|
|
3647
|
+
"src/middleware.js",
|
|
3648
|
+
"src/proxy.ts",
|
|
3649
|
+
"src/proxy.js"
|
|
3650
|
+
]) {
|
|
3651
|
+
if (await exists(join6(projectDirectory, name3)))
|
|
3652
|
+
sourceConflicts.push(`inspect_routing:${name3}`);
|
|
3653
|
+
}
|
|
3654
|
+
async function inspectSource(directory) {
|
|
3655
|
+
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
3656
|
+
if (entry.isSymbolicLink() || ["node_modules", ".git", ".next", ".siteplane", ".vercel"].includes(entry.name))
|
|
3657
|
+
continue;
|
|
3658
|
+
const path = join6(directory, entry.name);
|
|
3659
|
+
if (entry.isDirectory()) {
|
|
3660
|
+
await inspectSource(path);
|
|
3661
|
+
continue;
|
|
3662
|
+
}
|
|
3663
|
+
if (!/\.[cm]?[jt]sx?$/u.test(entry.name))
|
|
3664
|
+
continue;
|
|
3665
|
+
const source = await readFile8(path, "utf8");
|
|
3666
|
+
if (/from\s*["']next\/image["']/u.test(source))
|
|
3667
|
+
sourceConflicts.push(`inspect_image_policy:${relative2(projectDirectory, path)}`);
|
|
3668
|
+
if (/\b(?:Cookiebot|OneTrust|cookieyes|consentManager|cookieConsent)\b/iu.test(source))
|
|
3669
|
+
sourceConflicts.push(`inspect_existing_consent:${relative2(projectDirectory, path)}`);
|
|
3670
|
+
if (/from\s*["']next\/image["']/u.test(source) && semver.lt(nextVersion, "16.3.3"))
|
|
3671
|
+
fail("next_security_advisory", `Next.js ${nextVersion} uses image optimization affected by the August 2026 AVIF advisory. Review https://nextjs.org/blog/august-2026-security-release and explicitly update the affected dependency before setup; no framework upgrade was performed.`);
|
|
3672
|
+
if (/["']use server["']/u.test(source) && semver.lt(nextVersion, "16.2.11"))
|
|
3673
|
+
fail("next_security_advisory", `Next.js ${nextVersion} contains Server Actions affected by the July 2026 advisory. Review https://nextjs.org/blog/july-2026-security-release and explicitly update the affected dependency before setup.`);
|
|
3674
|
+
}
|
|
3675
|
+
}
|
|
3676
|
+
await inspectSource(join6(projectDirectory, apps[0]));
|
|
3677
|
+
const gitRead = async (args) => setupProcess("git", args, { cwd: projectDirectory }).then((value) => value.trim(), () => null);
|
|
3678
|
+
return {
|
|
3679
|
+
projectDirectory,
|
|
3680
|
+
repositoryRoot,
|
|
3681
|
+
lockfileRoot,
|
|
3682
|
+
appDirectory: apps[0],
|
|
3683
|
+
nextVersion,
|
|
3684
|
+
reactVersion,
|
|
3685
|
+
nodeVersion: process.versions.node,
|
|
3686
|
+
packageManager: { name: name2, version: localVersion, declared },
|
|
3687
|
+
lockfile: {
|
|
3688
|
+
path: relative2(repositoryRoot, join6(lockfileRoot, lockName)),
|
|
3689
|
+
format,
|
|
3690
|
+
sha256: createHash2("sha256").update(lockText).digest("hex")
|
|
3691
|
+
},
|
|
3692
|
+
git: {
|
|
3693
|
+
branch: await gitRead(["branch", "--show-current"]),
|
|
3694
|
+
revision: await gitRead(["rev-parse", "HEAD"]),
|
|
3695
|
+
remote: safeGitRemote(await gitRead(["remote", "get-url", "origin"])),
|
|
3696
|
+
changes: (await gitRead(["status", "--porcelain=v1", "--untracked-files=all"]))?.split("\n").filter(Boolean) ?? []
|
|
3697
|
+
},
|
|
3698
|
+
sourceConflicts
|
|
3699
|
+
};
|
|
3700
|
+
}
|
|
3701
|
+
async function setupPreflightCommand(input) {
|
|
3702
|
+
let local;
|
|
3703
|
+
try {
|
|
3704
|
+
local = await inspectSetupLocalProject(input.projectDir);
|
|
3705
|
+
await assertLocalCredentialsPathSafe(input.projectDir);
|
|
3706
|
+
let linkDirectory = local.projectDirectory;
|
|
3707
|
+
while (!await exists(join6(linkDirectory, ".vercel/project.json")) && linkDirectory !== local.repositoryRoot && dirname6(linkDirectory) !== linkDirectory)
|
|
3708
|
+
linkDirectory = dirname6(linkDirectory);
|
|
3709
|
+
if (!await exists(join6(linkDirectory, ".vercel/project.json")))
|
|
3710
|
+
fail("vercel_project_not_linked", "Link this application to its intended Vercel project before setup; Siteplane will not guess a target.");
|
|
3711
|
+
const linked = z28.object({
|
|
3712
|
+
projectId: z28.string().regex(/^prj_[A-Za-z0-9]+$/u),
|
|
3713
|
+
orgId: z28.string().regex(/^(?:team|user)_[A-Za-z0-9]+$/u)
|
|
3714
|
+
}).parse(JSON.parse(await readFile8(join6(linkDirectory, ".vercel/project.json"), "utf8")));
|
|
3715
|
+
const project = vercelProjectSchema.parse(await setupVercelApi(linkDirectory, `/v9/projects/${linked.projectId}?teamId=${linked.orgId}`));
|
|
3716
|
+
if (project.id !== linked.projectId || project.accountId !== linked.orgId)
|
|
3717
|
+
fail("vercel_identity_conflict", "The provider project does not match the locally linked project and team.");
|
|
3718
|
+
const expectedRoot = relative2(local.repositoryRoot, local.projectDirectory).split(sep).join("/");
|
|
3719
|
+
if ((project.rootDirectory ?? "") !== expectedRoot)
|
|
3720
|
+
fail("vercel_root_conflict", "Vercel Root Directory and the selected local app must identify the same application.");
|
|
3721
|
+
if (project.nodeVersion !== "22.x" || project.framework !== "nextjs")
|
|
3722
|
+
fail("vercel_framework_conflict", "The bound Vercel project must use Next.js with Node.js 22 before setup.");
|
|
3723
|
+
if (project.link && project.link.productionBranch !== local.git.branch)
|
|
3724
|
+
fail("vercel_production_branch_conflict", "The local branch differs from the bound Vercel Production branch.");
|
|
3725
|
+
if (project.link?.repo && project.link.org && !local.git.remote?.replace(/\.git$/u, "").endsWith(`${project.link.org}/${project.link.repo}`))
|
|
3726
|
+
fail("vercel_git_conflict", "The local origin repository differs from the Vercel Git connection.");
|
|
3727
|
+
const env = z28.object({
|
|
3728
|
+
envs: z28.array(z28.object({
|
|
3729
|
+
key: z28.string(),
|
|
3730
|
+
value: z28.string().optional(),
|
|
3731
|
+
target: z28.array(z28.string()).optional()
|
|
3732
|
+
}))
|
|
3733
|
+
}).parse(await setupVercelApi(linkDirectory, `/v10/projects/${linked.projectId}/env?teamId=${linked.orgId}`));
|
|
3734
|
+
const corepack = env.envs.some((item) => item.key === "ENABLE_EXPERIMENTAL_COREPACK" && item.value === "1" && item.target?.includes("production"));
|
|
3735
|
+
const install = project.installCommand ?? null;
|
|
3736
|
+
const selected = corepack ? "corepack" : install ? "install_override" : "automatic";
|
|
3737
|
+
if (local.packageManager.declared && selected === "automatic")
|
|
3738
|
+
fail("vercel_package_manager_pin_unconfirmed", "This project declares an exact packageManager but Vercel automatic selection does not bind that version. Enable its supported Corepack selection or set an exact install AND build override in the existing project, then rerun preflight. No package, lockfile or provider setting was changed.");
|
|
3739
|
+
if (install && /\b(?:yarn|bun)\b/u.test(install))
|
|
3740
|
+
fail("vercel_package_manager_conflict", "The Vercel install override selects an unsupported package manager.");
|
|
3741
|
+
const overridePin = install ? /\b(npm|pnpm)@(\d+\.\d+\.\d+)\b/u.exec(install) : null;
|
|
3742
|
+
if (overridePin && overridePin[1] !== local.packageManager.name)
|
|
3743
|
+
fail("vercel_package_manager_conflict", "The provider override selects a different package manager than the canonical lockfile.");
|
|
3744
|
+
const overrideFamily = install ? /(?:^|[;&|]\s*)(npm|pnpm)\s+(?:ci|install)\b/u.exec(install)?.[1] : null;
|
|
3745
|
+
if (overrideFamily && overrideFamily !== local.packageManager.name)
|
|
3746
|
+
fail("vercel_package_manager_conflict", "The provider install command conflicts with the canonical lockfile.");
|
|
3747
|
+
if (overridePin && local.packageManager.declared && `${overridePin[1]}@${overridePin[2]}` !== local.packageManager.declared)
|
|
3748
|
+
fail("vercel_package_manager_conflict", "The Vercel install override conflicts with the exact packageManager declaration.");
|
|
3749
|
+
return {
|
|
3750
|
+
status: "ready",
|
|
3751
|
+
local,
|
|
3752
|
+
provider: {
|
|
3753
|
+
projectId: project.id,
|
|
3754
|
+
teamId: project.accountId,
|
|
3755
|
+
name: project.name,
|
|
3756
|
+
rootDirectory: expectedRoot,
|
|
3757
|
+
nodeVersion: project.nodeVersion,
|
|
3758
|
+
git: project.link,
|
|
3759
|
+
installCommand: install,
|
|
3760
|
+
buildCommand: project.buildCommand ?? null,
|
|
3761
|
+
packageManagerSelection: selected,
|
|
3762
|
+
declaredPackageManager: local.packageManager.declared,
|
|
3763
|
+
observedPackageManager: null
|
|
3764
|
+
}
|
|
3765
|
+
};
|
|
3766
|
+
} catch (error) {
|
|
3767
|
+
return {
|
|
3768
|
+
status: "action_required",
|
|
3769
|
+
code: error instanceof SetupFailure ? error.code : "invalid_local_project",
|
|
3770
|
+
message: error instanceof SetupFailure ? error.message : "The selected project or provider response could not be validated. Check the application manifest and local project access.",
|
|
3771
|
+
...local ? { local } : {}
|
|
3772
|
+
};
|
|
3773
|
+
}
|
|
3774
|
+
}
|
|
3775
|
+
function safeGitRemote(value) {
|
|
3776
|
+
if (!value)
|
|
3777
|
+
return null;
|
|
3778
|
+
if (!value.includes("://"))
|
|
3779
|
+
return value;
|
|
3780
|
+
try {
|
|
3781
|
+
const url = new URL(value);
|
|
3782
|
+
url.username = "";
|
|
3783
|
+
url.password = "";
|
|
3784
|
+
url.search = "";
|
|
3785
|
+
url.hash = "";
|
|
3786
|
+
return url.toString().replace(/\/$/u, "");
|
|
3787
|
+
} catch {
|
|
3788
|
+
return null;
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3791
|
+
|
|
3792
|
+
// ../cli/dist/commands/init.js
|
|
3122
3793
|
async function initCommand(input) {
|
|
3123
3794
|
if (!input.setupToken.trim()) {
|
|
3124
3795
|
throw new Error("A Siteplane setup token is required.");
|
|
@@ -3126,47 +3797,64 @@ async function initCommand(input) {
|
|
|
3126
3797
|
if (!await detectNextProject(input.projectDir)) {
|
|
3127
3798
|
throw new Error("Siteplane init currently supports Next.js projects.");
|
|
3128
3799
|
}
|
|
3800
|
+
await assertLocalCredentialsPathSafe(input.projectDir, {
|
|
3801
|
+
...input.isIgnoredFile ? { isIgnoredFile: input.isIgnoredFile } : {},
|
|
3802
|
+
...input.isTrackedFile ? { isTrackedFile: input.isTrackedFile } : {}
|
|
3803
|
+
});
|
|
3804
|
+
const preflight = await (input.preflight ?? setupPreflightCommand)({
|
|
3805
|
+
projectDir: input.projectDir
|
|
3806
|
+
});
|
|
3807
|
+
if (preflight.status !== "ready")
|
|
3808
|
+
throw new Error(`${preflight.code}: ${preflight.message}`);
|
|
3129
3809
|
const bootstrap = await bootstrapProjectConnection(input);
|
|
3130
|
-
const paths = bootstrap.credentialPurpose === "site_setup" ? await persistSiteSetupBootstrap(input, bootstrap) : await persistFeatureBootstrap(input, bootstrap);
|
|
3810
|
+
const paths = bootstrap.credentialPurpose === "site_setup" ? await persistSiteSetupBootstrap(input, bootstrap, relative3(preflight.local.lockfileRoot, preflight.local.projectDirectory) || ".") : await persistFeatureBootstrap(input, bootstrap);
|
|
3131
3811
|
await acknowledgeBootstrapResponse(input);
|
|
3132
3812
|
return {
|
|
3133
3813
|
...paths,
|
|
3134
3814
|
nextSteps: [
|
|
3135
3815
|
"Run npx siteplane setup context and read the returned task.",
|
|
3136
3816
|
"Before source changes, briefly tell the user what Siteplane requested and what you will do.",
|
|
3137
|
-
"Follow the returned steps through setup
|
|
3817
|
+
"Follow the returned steps through setup prepare and setup deploy --wait, which performs setup verify; stop only at ready_for_review or action_required."
|
|
3138
3818
|
]
|
|
3139
3819
|
};
|
|
3140
3820
|
}
|
|
3141
|
-
var siteSetupBootstrapDataSchema =
|
|
3142
|
-
credentialPurpose:
|
|
3143
|
-
apiBaseUrl:
|
|
3144
|
-
siteId:
|
|
3145
|
-
publicSiteKeyId:
|
|
3146
|
-
publicSiteKey:
|
|
3147
|
-
accessToken:
|
|
3148
|
-
siteRevalidationSecret:
|
|
3821
|
+
var siteSetupBootstrapDataSchema = z29.object({
|
|
3822
|
+
credentialPurpose: z29.literal("site_setup"),
|
|
3823
|
+
apiBaseUrl: z29.string().url(),
|
|
3824
|
+
siteId: z29.string().uuid(),
|
|
3825
|
+
publicSiteKeyId: z29.string().uuid(),
|
|
3826
|
+
publicSiteKey: z29.string().trim().min(1),
|
|
3827
|
+
accessToken: z29.string().trim().min(1),
|
|
3828
|
+
siteRevalidationSecret: z29.string().regex(/^[A-Za-z0-9_-]{43}$/u),
|
|
3829
|
+
runId: z29.string().uuid(),
|
|
3830
|
+
packages: z29.object({
|
|
3831
|
+
siteplane: z29.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u),
|
|
3832
|
+
runtimeNext: z29.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u)
|
|
3833
|
+
}).strict()
|
|
3149
3834
|
}).strict();
|
|
3150
|
-
var featureBootstrapDataSchema =
|
|
3151
|
-
credentialPurpose:
|
|
3152
|
-
apiBaseUrl:
|
|
3153
|
-
siteId:
|
|
3154
|
-
accessToken:
|
|
3835
|
+
var featureBootstrapDataSchema = z29.object({
|
|
3836
|
+
credentialPurpose: z29.enum(["analytics", "booking"]),
|
|
3837
|
+
apiBaseUrl: z29.string().url(),
|
|
3838
|
+
siteId: z29.string().uuid(),
|
|
3839
|
+
accessToken: z29.string().trim().min(1)
|
|
3155
3840
|
}).strict();
|
|
3156
|
-
var bootstrapResponseSchema =
|
|
3157
|
-
ok:
|
|
3158
|
-
data:
|
|
3841
|
+
var bootstrapResponseSchema = z29.object({
|
|
3842
|
+
ok: z29.literal(true),
|
|
3843
|
+
data: z29.discriminatedUnion("credentialPurpose", [
|
|
3159
3844
|
siteSetupBootstrapDataSchema,
|
|
3160
3845
|
featureBootstrapDataSchema
|
|
3161
3846
|
])
|
|
3162
3847
|
}).strict();
|
|
3163
3848
|
async function bootstrapProjectConnection(input) {
|
|
3164
3849
|
const fetcher = input.fetcher ?? fetch;
|
|
3850
|
+
const existing = await access3(join7(input.projectDir, "siteplane.config.json")).then(() => readProjectConfig(input.projectDir), () => null);
|
|
3165
3851
|
const response = await fetcher(`${input.apiBaseUrl.replace(/\/$/, "")}/api/cli/setup/bootstrap`, {
|
|
3166
3852
|
method: "POST",
|
|
3853
|
+
signal: AbortSignal.timeout(2e4),
|
|
3167
3854
|
headers: createBootstrapHeaders(input),
|
|
3168
3855
|
body: JSON.stringify({
|
|
3169
3856
|
action: "bootstrap",
|
|
3857
|
+
...existing ? { expectedSiteId: existing.siteId, expectedRunId: existing.runId } : {},
|
|
3170
3858
|
agentClient: input.agentClient ?? "codex"
|
|
3171
3859
|
})
|
|
3172
3860
|
});
|
|
@@ -3181,6 +3869,7 @@ async function acknowledgeBootstrapResponse(input) {
|
|
|
3181
3869
|
const fetcher = input.fetcher ?? fetch;
|
|
3182
3870
|
const response = await fetcher(`${input.apiBaseUrl.replace(/\/$/, "")}/api/cli/setup/bootstrap`, {
|
|
3183
3871
|
method: "POST",
|
|
3872
|
+
signal: AbortSignal.timeout(2e4),
|
|
3184
3873
|
headers: createBootstrapHeaders(input),
|
|
3185
3874
|
body: JSON.stringify({ action: "acknowledge" })
|
|
3186
3875
|
});
|
|
@@ -3188,8 +3877,13 @@ async function acknowledgeBootstrapResponse(input) {
|
|
|
3188
3877
|
throw new Error("Siteplane setup acknowledgement failed.");
|
|
3189
3878
|
}
|
|
3190
3879
|
}
|
|
3191
|
-
async function persistSiteSetupBootstrap(input, bootstrap) {
|
|
3880
|
+
async function persistSiteSetupBootstrap(input, bootstrap, appDirectory) {
|
|
3881
|
+
const previous = await access3(join7(input.projectDir, "siteplane.config.json")).then(() => readProjectConfig(input.projectDir), () => null);
|
|
3882
|
+
if (previous && (previous.siteId !== bootstrap.siteId || previous.runId !== bootstrap.runId || previous.publicSiteKeyId !== bootstrap.publicSiteKeyId || previous.publicSiteKey !== bootstrap.publicSiteKey))
|
|
3883
|
+
throw new Error("The grant does not match this local Siteplane run. Preserve this directory and choose the correct owner connection.");
|
|
3884
|
+
const previousCredentials = previous ? await access3(join7(input.projectDir, ".siteplane/credentials.json")).then(() => readLocalCredentials(input.projectDir), () => null) : null;
|
|
3192
3885
|
const credentialsPath = await writeLocalCredentials(input.projectDir, {
|
|
3886
|
+
...previousCredentials?.adminSession ? { adminSession: previousCredentials.adminSession } : {},
|
|
3193
3887
|
accessToken: bootstrap.accessToken,
|
|
3194
3888
|
siteRevalidationSecret: bootstrap.siteRevalidationSecret
|
|
3195
3889
|
}, {
|
|
@@ -3197,7 +3891,13 @@ async function persistSiteSetupBootstrap(input, bootstrap) {
|
|
|
3197
3891
|
...input.isTrackedFile ? { isTrackedFile: input.isTrackedFile } : {}
|
|
3198
3892
|
});
|
|
3199
3893
|
const configPath = await writeProjectConfig(input.projectDir, {
|
|
3200
|
-
|
|
3894
|
+
...previous ?? {},
|
|
3895
|
+
version: 2,
|
|
3896
|
+
appDirectory,
|
|
3897
|
+
runId: bootstrap.runId,
|
|
3898
|
+
packages: bootstrap.packages,
|
|
3899
|
+
taskContractVersion: "siteplane.setup-task.v2",
|
|
3900
|
+
adminProtocolVersion: 2,
|
|
3201
3901
|
apiBaseUrl: bootstrap.apiBaseUrl,
|
|
3202
3902
|
siteId: bootstrap.siteId,
|
|
3203
3903
|
publicSiteKeyId: bootstrap.publicSiteKeyId,
|
|
@@ -3207,7 +3907,7 @@ async function persistSiteSetupBootstrap(input, bootstrap) {
|
|
|
3207
3907
|
readLocalCredentials(input.projectDir),
|
|
3208
3908
|
readProjectConfig(input.projectDir)
|
|
3209
3909
|
]);
|
|
3210
|
-
if (credentials.accessToken !== bootstrap.accessToken || credentials.siteRevalidationSecret !== bootstrap.siteRevalidationSecret || config.apiBaseUrl !== bootstrap.apiBaseUrl || config.siteId !== bootstrap.siteId || config.publicSiteKeyId !== bootstrap.publicSiteKeyId || config.publicSiteKey !== bootstrap.publicSiteKey) {
|
|
3910
|
+
if (credentials.accessToken !== bootstrap.accessToken || credentials.siteRevalidationSecret !== bootstrap.siteRevalidationSecret || config.runId !== bootstrap.runId || config.packages.siteplane !== bootstrap.packages.siteplane || config.packages.runtimeNext !== bootstrap.packages.runtimeNext || config.apiBaseUrl !== bootstrap.apiBaseUrl || config.siteId !== bootstrap.siteId || config.publicSiteKeyId !== bootstrap.publicSiteKeyId || config.publicSiteKey !== bootstrap.publicSiteKey) {
|
|
3211
3911
|
throw new Error("Siteplane setup files could not be verified.");
|
|
3212
3912
|
}
|
|
3213
3913
|
return { configPath, credentialsPath };
|
|
@@ -3221,8 +3921,8 @@ async function persistFeatureBootstrap(input, bootstrap) {
|
|
|
3221
3921
|
throw new Error("The setup token belongs to a different Siteplane site.");
|
|
3222
3922
|
}
|
|
3223
3923
|
const credentialsPath = await writeLocalCredentials(input.projectDir, {
|
|
3224
|
-
|
|
3225
|
-
|
|
3924
|
+
...existingCredentials,
|
|
3925
|
+
accessToken: bootstrap.accessToken
|
|
3226
3926
|
}, {
|
|
3227
3927
|
...input.isIgnoredFile ? { isIgnoredFile: input.isIgnoredFile } : {},
|
|
3228
3928
|
...input.isTrackedFile ? { isTrackedFile: input.isTrackedFile } : {}
|
|
@@ -3232,7 +3932,7 @@ async function persistFeatureBootstrap(input, bootstrap) {
|
|
|
3232
3932
|
throw new Error("Siteplane credentials could not be verified.");
|
|
3233
3933
|
}
|
|
3234
3934
|
return {
|
|
3235
|
-
configPath:
|
|
3935
|
+
configPath: join7(input.projectDir, "siteplane.config.json"),
|
|
3236
3936
|
credentialsPath
|
|
3237
3937
|
};
|
|
3238
3938
|
}
|
|
@@ -3254,8 +3954,8 @@ function readErrorMessage(payload) {
|
|
|
3254
3954
|
}
|
|
3255
3955
|
|
|
3256
3956
|
// ../cli/dist/commands/scan.js
|
|
3257
|
-
import { readdir } from "fs/promises";
|
|
3258
|
-
import { extname, join as
|
|
3957
|
+
import { readdir as readdir2 } from "fs/promises";
|
|
3958
|
+
import { extname, join as join8 } from "path";
|
|
3259
3959
|
var defaultScanRoots = ["app", "src", "pages", "components", "lib"];
|
|
3260
3960
|
var excludedDirectories = /* @__PURE__ */ new Set([
|
|
3261
3961
|
".git",
|
|
@@ -3273,14 +3973,14 @@ async function resolveScanFilePaths(filePaths, options = {}) {
|
|
|
3273
3973
|
return filePaths;
|
|
3274
3974
|
}
|
|
3275
3975
|
const projectDir = options.projectDir ?? process.cwd();
|
|
3276
|
-
const discovered = await Promise.all(defaultScanRoots.map((root) => collectSourceFiles(
|
|
3976
|
+
const discovered = await Promise.all(defaultScanRoots.map((root) => collectSourceFiles(join8(projectDir, root))));
|
|
3277
3977
|
return [...new Set(discovered.flat())].sort();
|
|
3278
3978
|
}
|
|
3279
3979
|
async function collectSourceFiles(directory) {
|
|
3280
|
-
const entries = await
|
|
3980
|
+
const entries = await readdir2(directory, { withFileTypes: true }).catch(() => []);
|
|
3281
3981
|
const files = [];
|
|
3282
3982
|
for (const entry of entries) {
|
|
3283
|
-
const entryPath =
|
|
3983
|
+
const entryPath = join8(directory, entry.name);
|
|
3284
3984
|
if (entry.isDirectory()) {
|
|
3285
3985
|
if (!excludedDirectories.has(entry.name)) {
|
|
3286
3986
|
files.push(...await collectSourceFiles(entryPath));
|
|
@@ -3296,17 +3996,17 @@ async function collectSourceFiles(directory) {
|
|
|
3296
3996
|
|
|
3297
3997
|
// ../cli/dist/commands/site-setup.js
|
|
3298
3998
|
import { execFile as execFile2 } from "child_process";
|
|
3299
|
-
import { readFile as
|
|
3999
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
3300
4000
|
import { promisify as promisify2 } from "util";
|
|
3301
|
-
import { z as
|
|
4001
|
+
import { z as z30 } from "zod";
|
|
3302
4002
|
|
|
3303
4003
|
// ../cli/dist/scan/next-source-scan.js
|
|
3304
4004
|
import ts2 from "typescript";
|
|
3305
|
-
import { isAbsolute, relative as
|
|
4005
|
+
import { isAbsolute, relative as relative4 } from "path";
|
|
3306
4006
|
var eventPreviewHandlerMissingMessage = "Event preview fields must consume preview values with useSiteplanePreviewText from @siteplane/runtime-next/client or a siteplane:preview-value-applied listener.";
|
|
3307
4007
|
var valueCallUnusedMessage = "Siteplane value calls must feed rendered content. Assign or await the returned value and render that value instead of ignoring the call.";
|
|
3308
4008
|
var legacyEditableComponentPrefix = "Editable";
|
|
3309
|
-
var editableComponentNames = new Set(["Image", "Text", "LongText", "Link"].map((
|
|
4009
|
+
var editableComponentNames = new Set(["Image", "Text", "LongText", "Link"].map((name2) => `${legacyEditableComponentPrefix}${name2}`));
|
|
3310
4010
|
var functionTypeByName = {
|
|
3311
4011
|
text: "text",
|
|
3312
4012
|
longText: "longText",
|
|
@@ -3844,17 +4544,17 @@ function readObjectLiteral(expression) {
|
|
|
3844
4544
|
if (!ts2.isPropertyAssignment(property)) {
|
|
3845
4545
|
continue;
|
|
3846
4546
|
}
|
|
3847
|
-
const
|
|
3848
|
-
if (!
|
|
4547
|
+
const name2 = getPropertyName(property.name);
|
|
4548
|
+
if (!name2) {
|
|
3849
4549
|
continue;
|
|
3850
4550
|
}
|
|
3851
|
-
value[
|
|
4551
|
+
value[name2] = staticExpressionValue2(property.initializer);
|
|
3852
4552
|
}
|
|
3853
4553
|
return value;
|
|
3854
4554
|
}
|
|
3855
|
-
function getPropertyName(
|
|
3856
|
-
if (ts2.isIdentifier(
|
|
3857
|
-
return
|
|
4555
|
+
function getPropertyName(name2) {
|
|
4556
|
+
if (ts2.isIdentifier(name2) || ts2.isStringLiteral(name2)) {
|
|
4557
|
+
return name2.text;
|
|
3858
4558
|
}
|
|
3859
4559
|
return null;
|
|
3860
4560
|
}
|
|
@@ -3880,9 +4580,9 @@ function collectEventPreviewComponentPropUsages(node, eventPreviewAttrVariables,
|
|
|
3880
4580
|
});
|
|
3881
4581
|
}
|
|
3882
4582
|
}
|
|
3883
|
-
function getJsxAttributeName(
|
|
3884
|
-
if (ts2.isIdentifier(
|
|
3885
|
-
return
|
|
4583
|
+
function getJsxAttributeName(name2) {
|
|
4584
|
+
if (ts2.isIdentifier(name2)) {
|
|
4585
|
+
return name2.text;
|
|
3886
4586
|
}
|
|
3887
4587
|
return void 0;
|
|
3888
4588
|
}
|
|
@@ -3907,8 +4607,8 @@ function getFunctionComponentName(node) {
|
|
|
3907
4607
|
}
|
|
3908
4608
|
return void 0;
|
|
3909
4609
|
}
|
|
3910
|
-
function isComponentName(
|
|
3911
|
-
return /^[A-Z]/.test(
|
|
4610
|
+
function isComponentName(name2) {
|
|
4611
|
+
return /^[A-Z]/.test(name2);
|
|
3912
4612
|
}
|
|
3913
4613
|
function getAssignedVariableName(node) {
|
|
3914
4614
|
let current = node;
|
|
@@ -3956,7 +4656,7 @@ function inferRouteHint(filePath) {
|
|
|
3956
4656
|
return route ? `/${route}` : "/";
|
|
3957
4657
|
}
|
|
3958
4658
|
function toRepositoryRelativePosixPath(filePath, projectRoot) {
|
|
3959
|
-
const path = isAbsolute(filePath) ?
|
|
4659
|
+
const path = isAbsolute(filePath) ? relative4(projectRoot, filePath) : filePath;
|
|
3960
4660
|
return path.replaceAll("\\", "/");
|
|
3961
4661
|
}
|
|
3962
4662
|
function isPositionalFieldReference(value) {
|
|
@@ -3965,54 +4665,30 @@ function isPositionalFieldReference(value) {
|
|
|
3965
4665
|
|
|
3966
4666
|
// ../cli/dist/commands/site-setup.js
|
|
3967
4667
|
var execFileAsync2 = promisify2(execFile2);
|
|
3968
|
-
var safeErrorSchema =
|
|
3969
|
-
code:
|
|
3970
|
-
message:
|
|
4668
|
+
var safeErrorSchema = z30.object({
|
|
4669
|
+
code: z30.string().trim().min(1),
|
|
4670
|
+
message: z30.string().trim().min(1)
|
|
3971
4671
|
}).passthrough();
|
|
3972
|
-
var errorResponseSchema2 =
|
|
3973
|
-
var contextDataSchema =
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
"ready_for_review"
|
|
3984
|
-
]),
|
|
3985
|
-
fieldContractHash: z27.string().nullable(),
|
|
3986
|
-
deploymentOrigin: z27.string().nullable(),
|
|
3987
|
-
deploymentRevision: z27.string().nullable(),
|
|
3988
|
-
errorCode: z27.string().nullable(),
|
|
3989
|
-
errorMessage: z27.string().nullable()
|
|
3990
|
-
}),
|
|
3991
|
-
structureHandoff: z27.object({
|
|
3992
|
-
pendingFields: z27.array(setupCorrectionItemSchema).max(50),
|
|
3993
|
-
ownerEditabilityNote: siteSetupOwnerEditabilityNoteSchema
|
|
3994
|
-
}).strict().nullable(),
|
|
3995
|
-
allowedTasks: z27.array(z27.string()),
|
|
3996
|
-
requiredEnvironmentNames: z27.array(z27.string())
|
|
3997
|
-
}).strict();
|
|
3998
|
-
var contextResponseSchema = z27.object({ ok: z27.literal(true), data: contextDataSchema }).strict();
|
|
3999
|
-
var applyDataSchema = z27.object({
|
|
4000
|
-
status: z27.enum(["applied", "ready_for_review"]),
|
|
4001
|
-
runId: z27.string().uuid().optional(),
|
|
4002
|
-
contractVersionId: z27.string().uuid().optional(),
|
|
4003
|
-
fieldContractHash: z27.string().regex(/^sha256:[0-9a-f]{64}$/u),
|
|
4004
|
-
editorDefinitionHash: z27.string().regex(/^sha256:[0-9a-f]{64}$/u).optional(),
|
|
4005
|
-
fieldCount: z27.number().int().nonnegative().optional(),
|
|
4006
|
-
routeCount: z27.number().int().nonnegative().optional()
|
|
4672
|
+
var errorResponseSchema2 = z30.object({ ok: z30.literal(false), error: safeErrorSchema }).passthrough();
|
|
4673
|
+
var contextDataSchema = z30.object({ task: siteSetupTaskSchema }).strict();
|
|
4674
|
+
var contextResponseSchema = z30.object({ ok: z30.literal(true), data: contextDataSchema }).strict();
|
|
4675
|
+
var applyDataSchema = z30.object({
|
|
4676
|
+
status: z30.enum(["applied", "ready_for_review"]),
|
|
4677
|
+
runId: z30.string().uuid().optional(),
|
|
4678
|
+
contractVersionId: z30.string().uuid().optional(),
|
|
4679
|
+
fieldContractHash: z30.string().regex(/^sha256:[0-9a-f]{64}$/u),
|
|
4680
|
+
editorDefinitionHash: z30.string().regex(/^sha256:[0-9a-f]{64}$/u).optional(),
|
|
4681
|
+
fieldCount: z30.number().int().nonnegative().optional(),
|
|
4682
|
+
routeCount: z30.number().int().nonnegative().optional()
|
|
4007
4683
|
}).strict();
|
|
4008
|
-
var applyResponseSchema =
|
|
4009
|
-
var verifyDataSchema =
|
|
4010
|
-
status:
|
|
4011
|
-
runId:
|
|
4012
|
-
deploymentOrigin:
|
|
4013
|
-
deploymentRevision:
|
|
4684
|
+
var applyResponseSchema = z30.object({ ok: z30.literal(true), data: applyDataSchema }).strict();
|
|
4685
|
+
var verifyDataSchema = z30.object({
|
|
4686
|
+
status: z30.literal("ready_for_review"),
|
|
4687
|
+
runId: z30.string().uuid().optional(),
|
|
4688
|
+
deploymentOrigin: z30.string().url().optional(),
|
|
4689
|
+
deploymentRevision: z30.string().optional()
|
|
4014
4690
|
}).strict();
|
|
4015
|
-
var verifyResponseSchema =
|
|
4691
|
+
var verifyResponseSchema = z30.object({ ok: z30.literal(true), data: verifyDataSchema }).strict();
|
|
4016
4692
|
async function siteSetupContextCommand(input) {
|
|
4017
4693
|
const response = await postSiteSetup(input, "context", {});
|
|
4018
4694
|
const parsed = contextResponseSchema.safeParse(response.payload);
|
|
@@ -4022,9 +4698,19 @@ async function siteSetupContextCommand(input) {
|
|
|
4022
4698
|
return actionRequired(response.payload, "Siteplane setup context is unavailable.");
|
|
4023
4699
|
}
|
|
4024
4700
|
async function siteSetupApplyCommand(input) {
|
|
4701
|
+
const config = await readProjectConfig(input.projectDir);
|
|
4702
|
+
try {
|
|
4703
|
+
await assertExactSetupPackages(input.projectDir, config.packages);
|
|
4704
|
+
} catch {
|
|
4705
|
+
return {
|
|
4706
|
+
status: "action_required",
|
|
4707
|
+
code: "siteplane_package_pin_conflict",
|
|
4708
|
+
message: "Run setup prepare to install the exact Siteplane packages from the current task before applying fields."
|
|
4709
|
+
};
|
|
4710
|
+
}
|
|
4025
4711
|
const editorDefinition = editorDefinitionV1Schema.parse(input.editorDefinition);
|
|
4026
4712
|
const resolveSourceFiles = input.resolveSourceFiles ?? resolveScanFilePaths;
|
|
4027
|
-
const readSourceFile = input.readSourceFile ?? ((path) =>
|
|
4713
|
+
const readSourceFile = input.readSourceFile ?? ((path) => readFile9(path, "utf8"));
|
|
4028
4714
|
const scanSourceFiles = input.scanSourceFiles ?? scanNextSourceFilesToSiteFieldContract;
|
|
4029
4715
|
const paths = await resolveSourceFiles([], { projectDir: input.projectDir });
|
|
4030
4716
|
const files = await Promise.all(paths.map(async (filePath) => ({
|
|
@@ -4041,7 +4727,8 @@ async function siteSetupApplyCommand(input) {
|
|
|
4041
4727
|
}
|
|
4042
4728
|
const payload = siteSetupApplyInputSchema.parse({
|
|
4043
4729
|
fieldContract: scan.contract,
|
|
4044
|
-
editorDefinition
|
|
4730
|
+
editorDefinition,
|
|
4731
|
+
contentSelectionSummary: input.contentSelectionSummary
|
|
4045
4732
|
});
|
|
4046
4733
|
const response = await postSiteSetup(input, "apply", payload);
|
|
4047
4734
|
const parsed = applyResponseSchema.safeParse(response.payload);
|
|
@@ -4056,7 +4743,6 @@ async function siteSetupApplyCommand(input) {
|
|
|
4056
4743
|
message: "The applied field contract hash does not match the final local source scan."
|
|
4057
4744
|
};
|
|
4058
4745
|
}
|
|
4059
|
-
const config = await readProjectConfig(input.projectDir);
|
|
4060
4746
|
await writeProjectConfig(input.projectDir, {
|
|
4061
4747
|
...config,
|
|
4062
4748
|
fieldContractHash: parsed.data.data.fieldContractHash
|
|
@@ -4065,7 +4751,7 @@ async function siteSetupApplyCommand(input) {
|
|
|
4065
4751
|
}
|
|
4066
4752
|
async function siteSetupVerifyCommand(input) {
|
|
4067
4753
|
const resolveSourceFiles = input.resolveSourceFiles ?? resolveScanFilePaths;
|
|
4068
|
-
const readSourceFile = input.readSourceFile ?? ((path) =>
|
|
4754
|
+
const readSourceFile = input.readSourceFile ?? ((path) => readFile9(path, "utf8"));
|
|
4069
4755
|
const paths = await resolveSourceFiles([], { projectDir: input.projectDir });
|
|
4070
4756
|
const sources = await Promise.all(paths.map(readSourceFile));
|
|
4071
4757
|
if (!sources.some(hasMountedFieldRuntimeBridge)) {
|
|
@@ -4095,19 +4781,33 @@ async function postSiteSetup(input, operation, body) {
|
|
|
4095
4781
|
readProjectConfig(input.projectDir),
|
|
4096
4782
|
readLocalCredentials(input.projectDir)
|
|
4097
4783
|
]);
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
"
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4784
|
+
try {
|
|
4785
|
+
const response = await (input.fetcher ?? fetch)(`${config.apiBaseUrl.replace(/\/$/u, "")}/api/cli/setup/${operation}`, {
|
|
4786
|
+
method: "POST",
|
|
4787
|
+
signal: AbortSignal.timeout(Math.max(1, Math.min(2e4, (input.deadline ?? Date.now() + 2e4) - Date.now()))),
|
|
4788
|
+
headers: {
|
|
4789
|
+
authorization: `Bearer ${credentials.accessToken}`,
|
|
4790
|
+
"content-type": "application/json",
|
|
4791
|
+
...input.vercelAutomationBypassSecret?.trim() ? {
|
|
4792
|
+
"x-vercel-protection-bypass": input.vercelAutomationBypassSecret.trim()
|
|
4793
|
+
} : {}
|
|
4794
|
+
},
|
|
4795
|
+
body: JSON.stringify(body)
|
|
4796
|
+
});
|
|
4797
|
+
const payload = await response.json().catch(() => null);
|
|
4798
|
+
return { ok: response.ok, payload };
|
|
4799
|
+
} catch {
|
|
4800
|
+
return {
|
|
4801
|
+
ok: false,
|
|
4802
|
+
payload: {
|
|
4803
|
+
ok: false,
|
|
4804
|
+
error: {
|
|
4805
|
+
code: "site_setup.transport_failed",
|
|
4806
|
+
message: "The setup request was not acknowledged. Reload the existing task before retrying; no credential values are included."
|
|
4807
|
+
}
|
|
4808
|
+
}
|
|
4809
|
+
};
|
|
4810
|
+
}
|
|
4111
4811
|
}
|
|
4112
4812
|
function actionRequired(payload, fallbackMessage) {
|
|
4113
4813
|
const parsed = errorResponseSchema2.safeParse(payload);
|
|
@@ -4132,9 +4832,880 @@ async function readGitRevision(projectDir) {
|
|
|
4132
4832
|
}
|
|
4133
4833
|
return revision;
|
|
4134
4834
|
}
|
|
4835
|
+
async function siteSetupActivityCommand(input) {
|
|
4836
|
+
const activity = siteSetupActivityInputSchema.safeParse({
|
|
4837
|
+
phase: input.phase,
|
|
4838
|
+
textId: input.phase
|
|
4839
|
+
});
|
|
4840
|
+
if (!activity.success)
|
|
4841
|
+
return {
|
|
4842
|
+
status: "action_required",
|
|
4843
|
+
code: "site_setup.invalid_activity",
|
|
4844
|
+
message: "Choose inspecting, integrating, deploying, verifying or repairing."
|
|
4845
|
+
};
|
|
4846
|
+
const response = await postSiteSetup(input, "activity", activity.data);
|
|
4847
|
+
const parsed = z30.object({
|
|
4848
|
+
ok: z30.literal(true),
|
|
4849
|
+
data: z30.object({ status: z30.enum(["recorded", "rate_limited"]) }).strict()
|
|
4850
|
+
}).strict().safeParse(response.payload);
|
|
4851
|
+
return response.ok && parsed.success ? parsed.data.data : actionRequired(response.payload, "Setup activity could not be recorded.");
|
|
4852
|
+
}
|
|
4853
|
+
|
|
4854
|
+
// ../cli/dist/commands/setup-prepare.js
|
|
4855
|
+
import { access as access4, lstat as lstat2, mkdir as mkdir6, open as open3, readFile as readFile10, rename as rename3, rm as rm3 } from "fs/promises";
|
|
4856
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
4857
|
+
import { dirname as dirname7, join as join9, relative as relative5, sep as sep2 } from "path";
|
|
4858
|
+
import ts3 from "typescript";
|
|
4859
|
+
var exists2 = async (path) => access4(path).then(() => true, () => false);
|
|
4860
|
+
var importPath = (from, target) => {
|
|
4861
|
+
const path = relative5(dirname7(from), target).split(sep2).join("/");
|
|
4862
|
+
return path.startsWith(".") ? path : `./${path}`;
|
|
4863
|
+
};
|
|
4864
|
+
async function setupPrepareCommand(input) {
|
|
4865
|
+
try {
|
|
4866
|
+
const preflight = await setupPreflightCommand(input);
|
|
4867
|
+
if (preflight.status !== "ready")
|
|
4868
|
+
return preflight;
|
|
4869
|
+
const config = await readProjectConfig(input.projectDir);
|
|
4870
|
+
const context = await siteSetupContextCommand(input);
|
|
4871
|
+
if (context.status !== "ok")
|
|
4872
|
+
return context;
|
|
4873
|
+
const task = context.task;
|
|
4874
|
+
if (task.site?.siteId !== config.siteId || task.run?.runId !== config.runId || task.packages.siteplane !== config.packages.siteplane || task.packages.runtimeNext !== config.packages.runtimeNext) {
|
|
4875
|
+
throw new SetupFailure("setup_contract_conflict", "The local configuration does not match this server task and its exact package versions.");
|
|
4876
|
+
}
|
|
4877
|
+
if (task.run.status === "completed" || task.run.status === "ready_for_review")
|
|
4878
|
+
return {
|
|
4879
|
+
status: "unchanged",
|
|
4880
|
+
message: "The verified setup is available for owner review; no preparation writes are needed."
|
|
4881
|
+
};
|
|
4882
|
+
const directory = preflight.local.projectDirectory;
|
|
4883
|
+
const app = join9(directory, preflight.local.appDirectory);
|
|
4884
|
+
const layouts = (await Promise.all(["layout.tsx", "layout.jsx", "layout.js"].map(async (name2) => await exists2(join9(app, name2)) ? join9(app, name2) : null))).filter((path) => path !== null);
|
|
4885
|
+
if (layouts.length !== 1)
|
|
4886
|
+
throw new SetupFailure("layout_integration_conflict", "Select one root layout and mount the Siteplane preview bridge there; preserve existing providers and rendering.");
|
|
4887
|
+
const layoutPath = layouts[0];
|
|
4888
|
+
const typed = layoutPath.endsWith(".tsx");
|
|
4889
|
+
const routeExtension = typed ? "ts" : "js";
|
|
4890
|
+
const bridgePath = join9(app, "_siteplane", `bridge.${typed ? "tsx" : "jsx"}`);
|
|
4891
|
+
const portal = task.site.portalPath;
|
|
4892
|
+
if (!/^\/[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u.test(portal))
|
|
4893
|
+
throw new SetupFailure("portal_route_conflict", "The selected portal path is not a supported single route segment.");
|
|
4894
|
+
const adminRoot = join9(app, portal.slice(1));
|
|
4895
|
+
for (const name2 of [
|
|
4896
|
+
"page.tsx",
|
|
4897
|
+
"page.jsx",
|
|
4898
|
+
"page.js",
|
|
4899
|
+
"route.ts",
|
|
4900
|
+
"route.js"
|
|
4901
|
+
]) {
|
|
4902
|
+
if (await exists2(join9(adminRoot, name2)))
|
|
4903
|
+
throw new SetupFailure("portal_route_conflict", "The selected admin path already belongs to the website. Choose an unused editor path in Siteplane.");
|
|
4904
|
+
}
|
|
4905
|
+
const configPath = join9(directory, "siteplane.config.json");
|
|
4906
|
+
const adminPath = join9(adminRoot, "[[...siteplane]]", `route.${routeExtension}`);
|
|
4907
|
+
const revalidationPath = join9(app, "api/siteplane/revalidate", `route.${routeExtension}`);
|
|
4908
|
+
const commonOptions = `siteId: config.siteId,
|
|
4909
|
+
publicRuntimeKeyId: config.publicSiteKeyId,
|
|
4910
|
+
fieldContractHash: process.env.SITEPLANE_BUILD_FIELD_CONTRACT_HASH${typed ? "!" : ""},
|
|
4911
|
+
deploymentRevision: process.env.SITEPLANE_BUILD_REVISION${typed ? "!" : ""},`;
|
|
4912
|
+
const admin = `import { createSiteplaneAdminRouteHandler } from "@siteplane/runtime-next/server";
|
|
4913
|
+
import config from ${JSON.stringify(importPath(adminPath, configPath))};
|
|
4914
|
+
export const runtime = "nodejs";
|
|
4915
|
+
function handle(request${typed ? ": Request" : ""}) {
|
|
4916
|
+
return createSiteplaneAdminRouteHandler({
|
|
4917
|
+
${commonOptions}
|
|
4918
|
+
setupRunId: config.runId, portalPath: ${JSON.stringify(portal)},
|
|
4919
|
+
deploymentOrigin: process.env.SITEPLANE_SITE_ORIGIN${typed ? "!" : ""},
|
|
4920
|
+
publicSiteKey: config.publicSiteKey, apiBaseUrl: config.apiBaseUrl,
|
|
4921
|
+
revalidationSecret: process.env.SITEPLANE_REVALIDATION_SECRET${typed ? "!" : ""},
|
|
4922
|
+
sessionSecret: process.env.SITEPLANE_ADMIN_SESSION_SECRET${typed ? "!" : ""},
|
|
4923
|
+
})(request);
|
|
4924
|
+
}
|
|
4925
|
+
export { handle as GET, handle as POST };
|
|
4926
|
+
`;
|
|
4927
|
+
const revalidation = `import { revalidatePath, revalidateTag } from "next/cache";
|
|
4928
|
+
import { createSiteplaneRevalidationHandler } from "@siteplane/runtime-next/server";
|
|
4929
|
+
import config from ${JSON.stringify(importPath(revalidationPath, configPath))};
|
|
4930
|
+
export const runtime = "nodejs";
|
|
4931
|
+
export async function POST(request${typed ? ": Request" : ""}) {
|
|
4932
|
+
return createSiteplaneRevalidationHandler({
|
|
4933
|
+
${commonOptions}
|
|
4934
|
+
audienceOrigin: process.env.SITEPLANE_SITE_ORIGIN${typed ? "!" : ""},
|
|
4935
|
+
secret: process.env.SITEPLANE_REVALIDATION_SECRET,
|
|
4936
|
+
revalidate: async (_payload, context) => {
|
|
4937
|
+
revalidateTag(context.cacheTag, { expire: 0 });
|
|
4938
|
+
revalidatePath("/", "layout");
|
|
4939
|
+
},
|
|
4940
|
+
})(request);
|
|
4941
|
+
}
|
|
4942
|
+
`;
|
|
4943
|
+
const bridge = `"use client";
|
|
4944
|
+
import { useEffect, useState } from "react";
|
|
4945
|
+
import { FieldRuntimeBridge } from "@siteplane/runtime-next/client";
|
|
4946
|
+
import config from ${JSON.stringify(importPath(bridgePath, configPath))};
|
|
4947
|
+
export default function SiteplaneBridge() {
|
|
4948
|
+
const [sessionId, setSessionId] = useState${typed ? "<string | null>" : ""}(null);
|
|
4949
|
+
useEffect(() => {
|
|
4950
|
+
const query = new URL(window.location.href).searchParams;
|
|
4951
|
+
const session = query.get("siteplaneSessionId");
|
|
4952
|
+
if (query.get("siteplanePreview") === "1" && query.get("siteplaneAdminOrigin") === new URL(config.apiBaseUrl).origin && session && /^[A-Za-z0-9_-]{1,128}$/.test(session)) setSessionId(session);
|
|
4953
|
+
}, []);
|
|
4954
|
+
return sessionId ? <FieldRuntimeBridge siteId={config.siteId} adminOrigin={new URL(config.apiBaseUrl).origin} sessionId={sessionId} /> : null;
|
|
4955
|
+
}
|
|
4956
|
+
`;
|
|
4957
|
+
const files = /* @__PURE__ */ new Map([
|
|
4958
|
+
[adminPath, admin],
|
|
4959
|
+
[revalidationPath, revalidation],
|
|
4960
|
+
[bridgePath, bridge]
|
|
4961
|
+
]);
|
|
4962
|
+
const layoutSource = await readFile10(layoutPath, "utf8");
|
|
4963
|
+
if (!/import\s+SiteplaneBridge\s+from\s+["']\.\/_siteplane\/bridge["']/u.test(layoutSource)) {
|
|
4964
|
+
const ast = ts3.createSourceFile(layoutPath, layoutSource, ts3.ScriptTarget.Latest, true, ts3.ScriptKind.TSX);
|
|
4965
|
+
const closingBodies = [];
|
|
4966
|
+
const visit = (node) => {
|
|
4967
|
+
if (ts3.isJsxClosingElement(node) && node.tagName.getText(ast) === "body")
|
|
4968
|
+
closingBodies.push(node.getStart(ast));
|
|
4969
|
+
ts3.forEachChild(node, visit);
|
|
4970
|
+
};
|
|
4971
|
+
visit(ast);
|
|
4972
|
+
if (closingBodies.length !== 1)
|
|
4973
|
+
throw new SetupFailure("layout_integration_conflict", "Mount SiteplaneBridge inside the existing root body. The layout shape requires a deliberate agent edit.");
|
|
4974
|
+
const point = closingBodies[0];
|
|
4975
|
+
files.set(layoutPath, `import SiteplaneBridge from "./_siteplane/bridge";
|
|
4976
|
+
${layoutSource.slice(0, point)}<SiteplaneBridge />${layoutSource.slice(point)}`);
|
|
4977
|
+
}
|
|
4978
|
+
const configs = (await Promise.all(["next.config.ts", "next.config.mjs", "next.config.js"].map(async (name2) => await exists2(join9(directory, name2)) ? join9(directory, name2) : null))).filter((path) => path !== null);
|
|
4979
|
+
if (configs.length > 1)
|
|
4980
|
+
throw new SetupFailure("next_config_conflict", "Choose one existing Next.js configuration before setup.");
|
|
4981
|
+
const nextPath = configs[0] ?? join9(directory, "next.config.mjs");
|
|
4982
|
+
const nextSource = configs.length ? await readFile10(nextPath, "utf8") : "export default {};\n";
|
|
4983
|
+
if (!/\bwithSiteplane\s*\(/u.test(nextSource)) {
|
|
4984
|
+
const ast = ts3.createSourceFile(nextPath, nextSource, ts3.ScriptTarget.Latest, true);
|
|
4985
|
+
const exported = ast.statements.filter(ts3.isExportAssignment);
|
|
4986
|
+
if (exported.length !== 1 || exported[0].isExportEquals)
|
|
4987
|
+
throw new SetupFailure("next_config_conflict", "Wrap the existing default Next.js configuration with withSiteplane from siteplane/next, preserving its existing options.");
|
|
4988
|
+
const expression = exported[0].expression;
|
|
4989
|
+
files.set(nextPath, `import { withSiteplane } from "siteplane/next";
|
|
4990
|
+
${nextSource.slice(0, expression.getStart(ast))}withSiteplane(${expression.getText(ast)})${nextSource.slice(expression.end)}`);
|
|
4991
|
+
}
|
|
4992
|
+
files.set(join9(directory, ".siteplane/setup-guide-v2.md"), `# Siteplane setup task v2
|
|
4993
|
+
|
|
4994
|
+
${task.goal}
|
|
4995
|
+
|
|
4996
|
+
## Technical rules
|
|
4997
|
+
|
|
4998
|
+
${task.discovery.rules.map((rule) => `- ${rule}`).join("\n")}
|
|
4999
|
+
|
|
5000
|
+
Run setup context for the current user instructions and authorized modules. Keep the user instructions distinct from your selection summary. Use setup deploy --wait for environment transfer and deployment; never read or print credential values.
|
|
5001
|
+
`);
|
|
5002
|
+
for (const [path, content] of files) {
|
|
5003
|
+
if (path === layoutPath || path === nextPath)
|
|
5004
|
+
continue;
|
|
5005
|
+
if (await exists2(path) && await readFile10(path, "utf8") !== content)
|
|
5006
|
+
throw new SetupFailure("owned_file_conflict", `Preserve the existing ${relative5(directory, path)} and reconcile its integration with the current Siteplane guide before retrying.`);
|
|
5007
|
+
}
|
|
5008
|
+
for (const path of files.keys())
|
|
5009
|
+
await assertSourcePath(directory, path);
|
|
5010
|
+
const before = /* @__PURE__ */ new Map();
|
|
5011
|
+
for (const path of files.keys())
|
|
5012
|
+
before.set(path, await readFile10(path, "utf8").catch((error) => {
|
|
5013
|
+
if (error.code === "ENOENT")
|
|
5014
|
+
return null;
|
|
5015
|
+
throw error;
|
|
5016
|
+
}));
|
|
5017
|
+
const packageManifest = JSON.parse(await readFile10(join9(directory, "package.json"), "utf8"));
|
|
5018
|
+
const pm = preflight.local.packageManager;
|
|
5019
|
+
const command = (args) => setupProcess("npx", ["--yes", `${pm.name}@${pm.version}`, ...args], {
|
|
5020
|
+
cwd: directory,
|
|
5021
|
+
timeoutMs: 3e5
|
|
5022
|
+
});
|
|
5023
|
+
if (packageManifest.devDependencies?.siteplane !== task.packages.siteplane)
|
|
5024
|
+
await command(pm.name === "npm" ? [
|
|
5025
|
+
"install",
|
|
5026
|
+
"--save-dev",
|
|
5027
|
+
"--save-exact",
|
|
5028
|
+
`siteplane@${task.packages.siteplane}`
|
|
5029
|
+
] : [
|
|
5030
|
+
"add",
|
|
5031
|
+
"--save-dev",
|
|
5032
|
+
"--save-exact",
|
|
5033
|
+
`siteplane@${task.packages.siteplane}`
|
|
5034
|
+
]);
|
|
5035
|
+
if (packageManifest.dependencies?.["@siteplane/runtime-next"] !== task.packages.runtimeNext)
|
|
5036
|
+
await command(pm.name === "npm" ? [
|
|
5037
|
+
"install",
|
|
5038
|
+
"--save-exact",
|
|
5039
|
+
`@siteplane/runtime-next@${task.packages.runtimeNext}`
|
|
5040
|
+
] : [
|
|
5041
|
+
"add",
|
|
5042
|
+
"--save-exact",
|
|
5043
|
+
`@siteplane/runtime-next@${task.packages.runtimeNext}`
|
|
5044
|
+
]);
|
|
5045
|
+
try {
|
|
5046
|
+
await assertExactSetupPackages(directory, task.packages);
|
|
5047
|
+
} catch {
|
|
5048
|
+
throw new SetupFailure("siteplane_package_pin_conflict", "The actual installed Siteplane packages do not match the task's exact versions. Resolve package access and the canonical lockfile before preparation.");
|
|
5049
|
+
}
|
|
5050
|
+
const written = [];
|
|
5051
|
+
for (const [path, content] of files) {
|
|
5052
|
+
await assertSourcePath(directory, path);
|
|
5053
|
+
const current = await readFile10(path, "utf8").catch((error) => {
|
|
5054
|
+
if (error.code === "ENOENT")
|
|
5055
|
+
return null;
|
|
5056
|
+
throw error;
|
|
5057
|
+
});
|
|
5058
|
+
if (current !== before.get(path))
|
|
5059
|
+
throw new SetupFailure("source_changed_during_prepare", "The source changed while dependencies were installing. Preserve the concurrent edit and reconcile it before retrying preparation.");
|
|
5060
|
+
if (current === content)
|
|
5061
|
+
continue;
|
|
5062
|
+
await mkdir6(dirname7(path), {
|
|
5063
|
+
recursive: true,
|
|
5064
|
+
mode: path.includes(`${sep2}.siteplane${sep2}`) ? 448 : 493
|
|
5065
|
+
});
|
|
5066
|
+
const temporary = `${path}.${randomUUID4()}.tmp`;
|
|
5067
|
+
const handle = await open3(temporary, "wx", path.includes(`${sep2}.siteplane${sep2}`) ? 384 : 420);
|
|
5068
|
+
try {
|
|
5069
|
+
await handle.writeFile(content, "utf8");
|
|
5070
|
+
await handle.sync();
|
|
5071
|
+
await handle.close();
|
|
5072
|
+
await assertSourcePath(directory, path);
|
|
5073
|
+
const latest = await readFile10(path, "utf8").catch((error) => {
|
|
5074
|
+
if (error.code === "ENOENT")
|
|
5075
|
+
return null;
|
|
5076
|
+
throw error;
|
|
5077
|
+
});
|
|
5078
|
+
if (latest !== current)
|
|
5079
|
+
throw new SetupFailure("source_changed_during_prepare", "The source changed before the prepared write. The existing edit was preserved.");
|
|
5080
|
+
await rename3(temporary, path);
|
|
5081
|
+
} finally {
|
|
5082
|
+
await handle.close().catch(() => void 0);
|
|
5083
|
+
await rm3(temporary, { force: true });
|
|
5084
|
+
}
|
|
5085
|
+
written.push(relative5(directory, path));
|
|
5086
|
+
}
|
|
5087
|
+
return {
|
|
5088
|
+
status: "prepared",
|
|
5089
|
+
preexistingChanges: preflight.local.git.changes,
|
|
5090
|
+
files: written,
|
|
5091
|
+
packages: task.packages,
|
|
5092
|
+
nextAction: "Describe your content selection, instrument those values, then run setup apply with the editor definition and --selection-summary-file. Commit only the intended source before setup deploy --wait."
|
|
5093
|
+
};
|
|
5094
|
+
} catch (error) {
|
|
5095
|
+
return {
|
|
5096
|
+
status: "action_required",
|
|
5097
|
+
code: error instanceof SetupFailure ? error.code : "setup_preparation_failed",
|
|
5098
|
+
message: error instanceof SetupFailure ? error.message : "Setup preparation could not complete. Check the selected project, package access and current task; no credential values are included in this diagnostic."
|
|
5099
|
+
};
|
|
5100
|
+
}
|
|
5101
|
+
}
|
|
5102
|
+
async function assertSourcePath(root, target) {
|
|
5103
|
+
let current = root;
|
|
5104
|
+
for (const part of relative5(root, target).split(sep2)) {
|
|
5105
|
+
if (part === "..")
|
|
5106
|
+
throw new SetupFailure("unsafe_source_path", "The prepared file is outside the selected application.");
|
|
5107
|
+
current = join9(current, part);
|
|
5108
|
+
const stat = await lstat2(current).catch((error) => {
|
|
5109
|
+
if (error.code === "ENOENT")
|
|
5110
|
+
return null;
|
|
5111
|
+
throw error;
|
|
5112
|
+
});
|
|
5113
|
+
if (stat?.isSymbolicLink())
|
|
5114
|
+
throw new SetupFailure("unsafe_source_path", "Preparation cannot write through a symbolic link.");
|
|
5115
|
+
}
|
|
5116
|
+
}
|
|
5117
|
+
|
|
5118
|
+
// ../cli/dist/commands/setup-lock.js
|
|
5119
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
5120
|
+
import { lstat as lstat3, mkdir as mkdir7, open as open4, readFile as readFile11, rm as rm4, rmdir } from "fs/promises";
|
|
5121
|
+
import { resolve as resolve2 } from "path";
|
|
5122
|
+
import { z as z31 } from "zod";
|
|
5123
|
+
var ownerSchema = z31.object({ pid: z31.number().int().positive(), nonce: z31.string().uuid() }).strict();
|
|
5124
|
+
async function withSetupLock(projectDir, work) {
|
|
5125
|
+
const gitPath = (await setupProcess("git", ["rev-parse", "--git-path", "siteplane-setup.lock"], { cwd: projectDir })).trim();
|
|
5126
|
+
const path = resolve2(projectDir, gitPath);
|
|
5127
|
+
const owner = { pid: process.pid, nonce: randomUUID5() };
|
|
5128
|
+
for (let attempt = 0; ; attempt++) {
|
|
5129
|
+
try {
|
|
5130
|
+
const handle = await open4(path, "wx", 384);
|
|
5131
|
+
try {
|
|
5132
|
+
await handle.writeFile(JSON.stringify(owner));
|
|
5133
|
+
await handle.sync();
|
|
5134
|
+
} finally {
|
|
5135
|
+
await handle.close();
|
|
5136
|
+
}
|
|
5137
|
+
break;
|
|
5138
|
+
} catch (error) {
|
|
5139
|
+
if (error.code !== "EEXIST")
|
|
5140
|
+
throw error;
|
|
5141
|
+
if (attempt || (await lstat3(path)).isSymbolicLink())
|
|
5142
|
+
throw locked();
|
|
5143
|
+
const recovery = `${path}.recovery`;
|
|
5144
|
+
try {
|
|
5145
|
+
await mkdir7(recovery, { mode: 448 });
|
|
5146
|
+
} catch {
|
|
5147
|
+
throw locked();
|
|
5148
|
+
}
|
|
5149
|
+
try {
|
|
5150
|
+
const source = await readFile11(path, "utf8");
|
|
5151
|
+
const existing = ownerSchema.safeParse(JSON.parse(source));
|
|
5152
|
+
if (!existing.success)
|
|
5153
|
+
throw locked();
|
|
5154
|
+
try {
|
|
5155
|
+
process.kill(existing.data.pid, 0);
|
|
5156
|
+
throw locked();
|
|
5157
|
+
} catch (failure) {
|
|
5158
|
+
if (failure.code !== "ESRCH")
|
|
5159
|
+
throw locked();
|
|
5160
|
+
}
|
|
5161
|
+
if (await readFile11(path, "utf8") !== source)
|
|
5162
|
+
throw locked();
|
|
5163
|
+
await rm4(path);
|
|
5164
|
+
} finally {
|
|
5165
|
+
await rmdir(recovery);
|
|
5166
|
+
}
|
|
5167
|
+
}
|
|
5168
|
+
}
|
|
5169
|
+
try {
|
|
5170
|
+
return await work();
|
|
5171
|
+
} finally {
|
|
5172
|
+
const current = await readFile11(path, "utf8").catch(() => "");
|
|
5173
|
+
if (current === JSON.stringify(owner))
|
|
5174
|
+
await rm4(path);
|
|
5175
|
+
}
|
|
5176
|
+
}
|
|
5177
|
+
function locked() {
|
|
5178
|
+
return new SetupFailure("setup_process_active", "Another setup process owns this repository. Wait for it to finish; a live lock is never replaced.");
|
|
5179
|
+
}
|
|
5180
|
+
|
|
5181
|
+
// ../cli/dist/config/build-provenance.js
|
|
5182
|
+
import { createHash as createHash3 } from "crypto";
|
|
5183
|
+
import { execFile as execFile3 } from "child_process";
|
|
5184
|
+
import { lstat as lstat4, readFile as readFile12, writeFile as writeFile5, mkdir as mkdir8 } from "fs/promises";
|
|
5185
|
+
import { dirname as dirname8, isAbsolute as isAbsolute2, join as join10, relative as relative6, resolve as resolve3, sep as sep3 } from "path";
|
|
5186
|
+
import { isDeepStrictEqual, promisify as promisify3 } from "util";
|
|
5187
|
+
import { z as z32 } from "zod";
|
|
5188
|
+
var execute = promisify3(execFile3);
|
|
5189
|
+
var name = ".siteplane/build-source.json";
|
|
5190
|
+
var oid = z32.string().regex(/^[0-9a-f]{40}$/u);
|
|
5191
|
+
var entrySchema = z32.object({
|
|
5192
|
+
path: z32.string().min(1).max(4096),
|
|
5193
|
+
mode: z32.enum(["100644", "100755", "120000"]),
|
|
5194
|
+
oid
|
|
5195
|
+
}).strict();
|
|
5196
|
+
var schema = z32.object({
|
|
5197
|
+
version: z32.literal(2),
|
|
5198
|
+
revision: oid,
|
|
5199
|
+
commit: z32.string().min(1).max(1048576),
|
|
5200
|
+
entries: z32.array(entrySchema).min(1).max(1e5),
|
|
5201
|
+
providerConfigurations: z32.array(z32.object({
|
|
5202
|
+
path: z32.string().max(4096).regex(/(?:^|\/)vercel\.json$/u),
|
|
5203
|
+
source: z32.string().max(1048576)
|
|
5204
|
+
}).strict()).max(1e3)
|
|
5205
|
+
}).strict();
|
|
5206
|
+
async function writeBuildProvenance(checkout, revision) {
|
|
5207
|
+
const commit = (await execute("git", ["cat-file", "commit", revision], {
|
|
5208
|
+
cwd: checkout,
|
|
5209
|
+
maxBuffer: 1048576
|
|
5210
|
+
})).stdout;
|
|
5211
|
+
const tree = (await execute("git", ["ls-tree", "-rz", "--full-tree", revision], {
|
|
5212
|
+
cwd: checkout,
|
|
5213
|
+
maxBuffer: 16777216
|
|
5214
|
+
})).stdout;
|
|
5215
|
+
const entries = tree.split("\0").filter(Boolean).map((line) => {
|
|
5216
|
+
const match = /^(100644|100755|120000) blob ([0-9a-f]{40})\t([\s\S]+)$/u.exec(line);
|
|
5217
|
+
if (!match)
|
|
5218
|
+
throw new Error("Git submodules or unsupported tracked objects need an explicit deployment configuration.");
|
|
5219
|
+
return { path: match[3], mode: match[1], oid: match[2] };
|
|
5220
|
+
});
|
|
5221
|
+
for (const entry of entries) {
|
|
5222
|
+
if (entry.mode !== "120000")
|
|
5223
|
+
continue;
|
|
5224
|
+
const { readlink: readlink2 } = await import("fs/promises");
|
|
5225
|
+
const path = join10(checkout, entry.path);
|
|
5226
|
+
const target2 = resolve3(dirname8(path), await readlink2(path));
|
|
5227
|
+
const bound = relative6(checkout, target2);
|
|
5228
|
+
if (bound.startsWith("..") || isAbsolute2(bound))
|
|
5229
|
+
throw new Error("A tracked symbolic link escapes the committed upload; resolve that source boundary before deployment.");
|
|
5230
|
+
}
|
|
5231
|
+
const providerConfigurations = await Promise.all(entries.filter((entry) => /(?:^|\/)vercel\.json$/u.test(entry.path)).map(async (entry) => ({
|
|
5232
|
+
path: entry.path,
|
|
5233
|
+
source: await readFile12(join10(checkout, entry.path), "utf8")
|
|
5234
|
+
})));
|
|
5235
|
+
const record = schema.parse({
|
|
5236
|
+
version: 2,
|
|
5237
|
+
revision,
|
|
5238
|
+
commit,
|
|
5239
|
+
entries,
|
|
5240
|
+
providerConfigurations
|
|
5241
|
+
});
|
|
5242
|
+
const target = join10(checkout, name);
|
|
5243
|
+
await mkdir8(dirname8(target), { recursive: true, mode: 448 });
|
|
5244
|
+
await writeFile5(target, JSON.stringify(record), { flag: "wx", mode: 384 });
|
|
5245
|
+
}
|
|
5246
|
+
|
|
5247
|
+
// ../cli/dist/commands/setup-deploy.js
|
|
5248
|
+
import { createHash as createHash4, randomBytes, randomUUID as randomUUID6 } from "crypto";
|
|
5249
|
+
import { access as access5, mkdir as mkdir9, mkdtemp, rm as rm5, writeFile as writeFile6 } from "fs/promises";
|
|
5250
|
+
import { tmpdir } from "os";
|
|
5251
|
+
import { join as join11 } from "path";
|
|
5252
|
+
import { z as z33 } from "zod";
|
|
5253
|
+
var envSchema = z33.object({
|
|
5254
|
+
id: z33.string(),
|
|
5255
|
+
key: z33.string(),
|
|
5256
|
+
target: z33.array(z33.string()),
|
|
5257
|
+
type: z33.string(),
|
|
5258
|
+
comment: z33.string().optional(),
|
|
5259
|
+
updatedAt: z33.number().optional()
|
|
5260
|
+
});
|
|
5261
|
+
var deploymentSchema = z33.object({
|
|
5262
|
+
id: z33.string(),
|
|
5263
|
+
projectId: z33.string(),
|
|
5264
|
+
ownerId: z33.string().optional(),
|
|
5265
|
+
target: z33.string().nullable(),
|
|
5266
|
+
readyState: z33.string(),
|
|
5267
|
+
createdAt: z33.number(),
|
|
5268
|
+
url: z33.string(),
|
|
5269
|
+
meta: z33.record(z33.string(), z33.unknown()).optional(),
|
|
5270
|
+
gitSource: z33.object({ sha: z33.string().optional() }).nullable().optional()
|
|
5271
|
+
});
|
|
5272
|
+
var buildProofSchema = z33.object({
|
|
5273
|
+
node: z33.string(),
|
|
5274
|
+
packageManager: z33.enum(["npm", "pnpm"]),
|
|
5275
|
+
packageManagerVersion: z33.string(),
|
|
5276
|
+
lockfileHash: z33.string(),
|
|
5277
|
+
revision: z33.string(),
|
|
5278
|
+
fieldContractHash: z33.string(),
|
|
5279
|
+
siteplane: z33.string(),
|
|
5280
|
+
runtimeNext: z33.string(),
|
|
5281
|
+
next: z33.string(),
|
|
5282
|
+
react: z33.string()
|
|
5283
|
+
}).strict();
|
|
5284
|
+
var digest = (value) => `sha256:${createHash4("sha256").update(stableJsonStringify(value)).digest("hex")}`;
|
|
5285
|
+
function fail2(code, message) {
|
|
5286
|
+
throw new SetupFailure(code, message);
|
|
5287
|
+
}
|
|
5288
|
+
var defaults = {
|
|
5289
|
+
preflight: setupPreflightCommand,
|
|
5290
|
+
context: siteSetupContextCommand,
|
|
5291
|
+
api: setupVercelApi,
|
|
5292
|
+
process: setupProcess,
|
|
5293
|
+
verify: siteSetupVerifyCommand,
|
|
5294
|
+
post: postSiteSetup,
|
|
5295
|
+
writeBuildProvenance,
|
|
5296
|
+
now: Date.now,
|
|
5297
|
+
pause: (milliseconds) => new Promise((resolve5) => setTimeout(resolve5, milliseconds))
|
|
5298
|
+
};
|
|
5299
|
+
async function setupDeployCommand(input, deps = defaults) {
|
|
5300
|
+
let record = null;
|
|
5301
|
+
try {
|
|
5302
|
+
if (!await access5(join11(input.projectDir, "siteplane.config.json")).then(() => true, () => false) || !await access5(join11(input.projectDir, ".siteplane/credentials.json")).then(() => true, () => false))
|
|
5303
|
+
fail2("local_connection_missing", "Open this site's setup and choose Replace local connection, then initialize this folder with its new one-time grant.");
|
|
5304
|
+
await assertLocalCredentialsPathSafe(input.projectDir);
|
|
5305
|
+
const config = await readProjectConfig(input.projectDir);
|
|
5306
|
+
let credentials = await readLocalCredentials(input.projectDir);
|
|
5307
|
+
const context = await deps.context(input);
|
|
5308
|
+
if (context.status !== "ok")
|
|
5309
|
+
return context;
|
|
5310
|
+
const task = context.task;
|
|
5311
|
+
if (task.run?.runId !== config.runId || task.site?.siteId !== config.siteId || task.packages.siteplane !== config.packages.siteplane || task.packages.runtimeNext !== config.packages.runtimeNext || task.run.fieldContractHash !== config.fieldContractHash)
|
|
5312
|
+
fail2("setup_contract_conflict", "Local configuration and the current applied setup task must match before deployment.");
|
|
5313
|
+
if (!config.fieldContractHash)
|
|
5314
|
+
fail2("setup_apply_required", "Instrument the agreed content and run setup apply before deployment.");
|
|
5315
|
+
record = task.deployment;
|
|
5316
|
+
const preflight = await deps.preflight(input);
|
|
5317
|
+
if (preflight.status !== "ready")
|
|
5318
|
+
return {
|
|
5319
|
+
...preflight,
|
|
5320
|
+
...record ? { deploymentId: record.deploymentId, deadline: record.deadline } : {}
|
|
5321
|
+
};
|
|
5322
|
+
const { local, provider } = preflight;
|
|
5323
|
+
if (local.git.changes.length || !local.git.revision || !local.git.branch)
|
|
5324
|
+
fail2("uncommitted_source", "Commit only the intended setup source on a named branch before deployment. Siteplane does not stage, reset or publish unrelated local work.");
|
|
5325
|
+
const revision = local.git.revision;
|
|
5326
|
+
if (!/^[0-9a-f]{40}$/u.test(revision))
|
|
5327
|
+
fail2("invalid_git_revision", "A full committed Git revision is required.");
|
|
5328
|
+
try {
|
|
5329
|
+
await assertExactSetupPackages(local.projectDirectory, config.packages);
|
|
5330
|
+
} catch {
|
|
5331
|
+
fail2("siteplane_package_pin_conflict", "Install the exact Siteplane package versions from the current task and commit their canonical lockfile.");
|
|
5332
|
+
}
|
|
5333
|
+
let operationDeadline = record && !["verified", "failed"].includes(record.phase) ? Date.parse(record.deadline) : deps.now() + 9e5;
|
|
5334
|
+
const remaining = () => {
|
|
5335
|
+
const milliseconds = operationDeadline - deps.now();
|
|
5336
|
+
if (milliseconds <= 0)
|
|
5337
|
+
fail2("provider_deadline_exceeded", "The 15-minute provider deadline expired. Inspect the recorded operation; setup will not start a second deployment on resume.");
|
|
5338
|
+
return milliseconds;
|
|
5339
|
+
};
|
|
5340
|
+
const processCommand = async (binary, args, options) => {
|
|
5341
|
+
const output = await deps.process(binary, args, {
|
|
5342
|
+
...options,
|
|
5343
|
+
timeoutMs: Math.min(options.timeoutMs ?? 12e4, remaining())
|
|
5344
|
+
});
|
|
5345
|
+
remaining();
|
|
5346
|
+
return output;
|
|
5347
|
+
};
|
|
5348
|
+
const scope = `teamId=${provider.teamId}`;
|
|
5349
|
+
const api = async (endpoint, body, method) => {
|
|
5350
|
+
remaining();
|
|
5351
|
+
const result = await deps.api(local.projectDirectory, endpoint, body, method, { deadline: operationDeadline });
|
|
5352
|
+
remaining();
|
|
5353
|
+
return result;
|
|
5354
|
+
};
|
|
5355
|
+
const domains = z33.object({
|
|
5356
|
+
domains: z33.array(z33.object({
|
|
5357
|
+
name: z33.string(),
|
|
5358
|
+
verified: z33.boolean(),
|
|
5359
|
+
redirect: z33.string().nullable().optional(),
|
|
5360
|
+
gitBranch: z33.string().nullable().optional()
|
|
5361
|
+
}))
|
|
5362
|
+
}).parse(await api(`/v9/projects/${provider.projectId}/domains?${scope}`)).domains.filter((domain) => domain.verified && !domain.redirect && !domain.gitBranch);
|
|
5363
|
+
const origin = record?.origin ?? (domains.length === 1 ? `https://${domains[0].name}` : null);
|
|
5364
|
+
if (!origin || !domains.some((domain) => `https://${domain.name}` === origin))
|
|
5365
|
+
fail2("production_origin_ambiguous", "The linked project needs one verified canonical Production domain, or the existing setup domain must still belong to it.");
|
|
5366
|
+
if (record && (record.projectId !== provider.projectId || record.teamId !== provider.teamId))
|
|
5367
|
+
fail2("deployment_target_conflict", "This setup already belongs to a different provider project/team. Restore its original local link.");
|
|
5368
|
+
const listEnv = async () => z33.object({ envs: z33.array(envSchema) }).parse(await api(`/v10/projects/${provider.projectId}/env?${scope}`)).envs;
|
|
5369
|
+
let env = await listEnv();
|
|
5370
|
+
const owned = (key) => {
|
|
5371
|
+
const matches2 = env.filter((item) => item.key === key && item.target.includes("production"));
|
|
5372
|
+
if (matches2.length > 1 || matches2.some((item) => item.target.length !== 1))
|
|
5373
|
+
fail2("environment_scope_conflict", `Separate ${key} into an unambiguous Production-only binding before setup; other scopes are preserved.`);
|
|
5374
|
+
return matches2[0];
|
|
5375
|
+
};
|
|
5376
|
+
const admin = owned("SITEPLANE_ADMIN_SESSION_SECRET");
|
|
5377
|
+
if (record && !admin && !credentials.adminSession)
|
|
5378
|
+
fail2("admin_secret_recovery_required", "The previously configured admin-session secret is missing. Restore it through an explicit owner-directed configuration recovery; connection replacement never rotates it.");
|
|
5379
|
+
if (!credentials.adminSession && !record && admin)
|
|
5380
|
+
fail2("admin_secret_binding_unknown", "An existing admin-session secret has no matching setup generation. Resolve its ownership before configuration; Siteplane will not overwrite it.");
|
|
5381
|
+
if (!credentials.adminSession && !record && !admin) {
|
|
5382
|
+
credentials = {
|
|
5383
|
+
...credentials,
|
|
5384
|
+
adminSession: {
|
|
5385
|
+
secret: randomBytes(32).toString("base64url"),
|
|
5386
|
+
generation: randomUUID6()
|
|
5387
|
+
}
|
|
5388
|
+
};
|
|
5389
|
+
await writeLocalCredentials(input.projectDir, credentials);
|
|
5390
|
+
const persisted = await readLocalCredentials(input.projectDir);
|
|
5391
|
+
if (persisted.adminSession?.secret !== credentials.adminSession.secret)
|
|
5392
|
+
fail2("credential_write_failed", "The generated admin credential could not be read back securely.");
|
|
5393
|
+
}
|
|
5394
|
+
const generation = record?.adminSecretGeneration ?? credentials.adminSession.generation;
|
|
5395
|
+
if (credentials.adminSession && credentials.adminSession.generation !== generation)
|
|
5396
|
+
fail2("admin_secret_generation_conflict", "The local admin credential belongs to another configuration generation. Preserve the provider secret and resolve the local binding.");
|
|
5397
|
+
if (admin && admin.comment !== `Siteplane admin generation ${generation}`)
|
|
5398
|
+
fail2("admin_secret_generation_conflict", "The provider admin secret does not match the recorded setup generation. No secret was replaced.");
|
|
5399
|
+
const publicBindings = {
|
|
5400
|
+
SITEPLANE_SITE_ORIGIN: origin,
|
|
5401
|
+
SITEPLANE_SETUP_RUN_ID: config.runId,
|
|
5402
|
+
SITEPLANE_PORTAL_PATH: task.site.portalPath,
|
|
5403
|
+
SITEPLANE_BUILD_FIELD_CONTRACT_HASH: config.fieldContractHash,
|
|
5404
|
+
SITEPLANE_BUILD_REVISION: revision
|
|
5405
|
+
};
|
|
5406
|
+
const fingerprint = digest({
|
|
5407
|
+
publicBindings,
|
|
5408
|
+
siteId: config.siteId,
|
|
5409
|
+
publicKeyId: config.publicSiteKeyId,
|
|
5410
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
5411
|
+
packages: config.packages,
|
|
5412
|
+
bindings: [
|
|
5413
|
+
...Object.keys(publicBindings),
|
|
5414
|
+
"SITEPLANE_REVALIDATION_SECRET",
|
|
5415
|
+
"SITEPLANE_ADMIN_SESSION_SECRET"
|
|
5416
|
+
].sort(),
|
|
5417
|
+
adminSecretGeneration: generation,
|
|
5418
|
+
revalidationGeneration: task.revalidationGeneration,
|
|
5419
|
+
projectId: provider.projectId,
|
|
5420
|
+
teamId: provider.teamId,
|
|
5421
|
+
rootDirectory: provider.rootDirectory,
|
|
5422
|
+
nodeVersion: provider.nodeVersion,
|
|
5423
|
+
installCommand: provider.installCommand,
|
|
5424
|
+
buildCommand: provider.buildCommand,
|
|
5425
|
+
packageManagerSelection: provider.packageManagerSelection,
|
|
5426
|
+
declaredPackageManager: provider.declaredPackageManager,
|
|
5427
|
+
lockfileHash: local.lockfile.sha256
|
|
5428
|
+
});
|
|
5429
|
+
const save = async (next) => {
|
|
5430
|
+
const result = await deps.post({ ...input, deadline: operationDeadline }, "deployment", {
|
|
5431
|
+
action: "record",
|
|
5432
|
+
expectedOperationId: record?.operationId ?? null,
|
|
5433
|
+
record: next
|
|
5434
|
+
});
|
|
5435
|
+
const parsed = z33.object({
|
|
5436
|
+
ok: z33.literal(true),
|
|
5437
|
+
data: z33.object({ record: setupDeploymentRecordSchema }).strict()
|
|
5438
|
+
}).strict().safeParse(result.payload);
|
|
5439
|
+
if (!result.ok || !parsed.success || stableJsonStringify(parsed.data.data.record) !== stableJsonStringify(next))
|
|
5440
|
+
fail2("deployment_record_unconfirmed", "The setup deployment record was not acknowledged. Reload context before continuing; no deployment is blindly repeated.");
|
|
5441
|
+
record = parsed.data.data.record;
|
|
5442
|
+
if (next.phase === "planned")
|
|
5443
|
+
operationDeadline = Date.parse(next.deadline);
|
|
5444
|
+
};
|
|
5445
|
+
const fresh = () => ({
|
|
5446
|
+
version: 1,
|
|
5447
|
+
operationId: randomUUID6(),
|
|
5448
|
+
provider: "vercel",
|
|
5449
|
+
projectId: provider.projectId,
|
|
5450
|
+
teamId: provider.teamId,
|
|
5451
|
+
origin,
|
|
5452
|
+
deploymentId: null,
|
|
5453
|
+
revision,
|
|
5454
|
+
fieldContractHash: config.fieldContractHash,
|
|
5455
|
+
packages: config.packages,
|
|
5456
|
+
configurationFingerprint: fingerprint,
|
|
5457
|
+
adminSecretGeneration: generation,
|
|
5458
|
+
revalidationGeneration: task.revalidationGeneration,
|
|
5459
|
+
startedAt: new Date(deps.now()).toISOString(),
|
|
5460
|
+
environmentWrittenAt: null,
|
|
5461
|
+
deployRequestedAt: null,
|
|
5462
|
+
deadline: new Date(deps.now() + 9e5).toISOString(),
|
|
5463
|
+
phase: "planned",
|
|
5464
|
+
build: null
|
|
5465
|
+
});
|
|
5466
|
+
if (!record || record.configurationFingerprint !== fingerprint) {
|
|
5467
|
+
if (record && !["verified", "deployed", "failed"].includes(record.phase))
|
|
5468
|
+
fail2("previous_deployment_unresolved", "Resolve the previous provider operation before changing the source or configuration.");
|
|
5469
|
+
await save(fresh());
|
|
5470
|
+
}
|
|
5471
|
+
const progress = () => input.onProgress?.({
|
|
5472
|
+
phase: record.phase,
|
|
5473
|
+
deadline: record.deadline,
|
|
5474
|
+
deploymentId: record.deploymentId
|
|
5475
|
+
});
|
|
5476
|
+
progress();
|
|
5477
|
+
const values = {
|
|
5478
|
+
...publicBindings,
|
|
5479
|
+
SITEPLANE_REVALIDATION_SECRET: credentials.siteRevalidationSecret,
|
|
5480
|
+
...credentials.adminSession ? { SITEPLANE_ADMIN_SESSION_SECRET: credentials.adminSession.secret } : {}
|
|
5481
|
+
};
|
|
5482
|
+
const pending = [];
|
|
5483
|
+
for (const [key, value] of Object.entries(values)) {
|
|
5484
|
+
const item = owned(key);
|
|
5485
|
+
if (key === "SITEPLANE_ADMIN_SESSION_SECRET" && item && record.phase === "verified")
|
|
5486
|
+
continue;
|
|
5487
|
+
const current = item ? z33.object({ key: z33.literal(key), value: z33.string() }).parse(await api(`/v1/projects/${provider.projectId}/env/${encodeURIComponent(item.id)}?${scope}`)).value : null;
|
|
5488
|
+
if (current !== value)
|
|
5489
|
+
pending.push({
|
|
5490
|
+
key,
|
|
5491
|
+
value,
|
|
5492
|
+
type: key.endsWith("SECRET") ? "encrypted" : "plain",
|
|
5493
|
+
target: ["production"],
|
|
5494
|
+
comment: key === "SITEPLANE_ADMIN_SESSION_SECRET" ? `Siteplane admin generation ${generation}` : "Managed by Siteplane setup"
|
|
5495
|
+
});
|
|
5496
|
+
}
|
|
5497
|
+
if (pending.length && record.phase !== "planned") {
|
|
5498
|
+
if (!["verified", "deployed", "failed"].includes(record.phase))
|
|
5499
|
+
fail2("environment_changed_during_deploy", "The environment changed during an unresolved deployment. Inspect that operation before retrying.");
|
|
5500
|
+
await save(fresh());
|
|
5501
|
+
}
|
|
5502
|
+
if (record.phase === "planned") {
|
|
5503
|
+
if (deps.now() >= Date.parse(record.deadline))
|
|
5504
|
+
fail2("provider_deadline_exceeded", "The setup provider deadline expired before configuration. Inspect its existing record before a new attempt.");
|
|
5505
|
+
if (pending.length)
|
|
5506
|
+
await api(`/v10/projects/${provider.projectId}/env?${scope}&upsert=true`, pending);
|
|
5507
|
+
env = await listEnv();
|
|
5508
|
+
for (const [key, value] of Object.entries(values)) {
|
|
5509
|
+
const item = owned(key);
|
|
5510
|
+
if (!item)
|
|
5511
|
+
fail2("environment_write_unconfirmed", "A Siteplane Production binding was not persisted; retry the same configuration.");
|
|
5512
|
+
const observed = z33.object({ key: z33.literal(key), value: z33.string() }).parse(await api(`/v1/projects/${provider.projectId}/env/${encodeURIComponent(item.id)}?${scope}`));
|
|
5513
|
+
if (observed.value !== value)
|
|
5514
|
+
fail2("environment_write_unconfirmed", "The provider readback does not match the authorized Siteplane value; no deployment was started.");
|
|
5515
|
+
}
|
|
5516
|
+
await save({
|
|
5517
|
+
...record,
|
|
5518
|
+
phase: "environment_written",
|
|
5519
|
+
environmentWrittenAt: new Date(deps.now()).toISOString()
|
|
5520
|
+
});
|
|
5521
|
+
}
|
|
5522
|
+
const detail = async (id) => deploymentSchema.parse(await api(`/v13/deployments/${encodeURIComponent(id)}?${scope}&withGitRepoInfo=true`));
|
|
5523
|
+
const matches = (deployment2) => deployment2.projectId === provider.projectId && deployment2.target === "production" && (!deployment2.ownerId || deployment2.ownerId === provider.teamId) && deployment2.createdAt >= Date.parse(record.environmentWrittenAt) && (deployment2.meta?.siteplaneOperationId === record.operationId || provider.git && (deployment2.gitSource?.sha ?? deployment2.meta?.githubCommitSha ?? deployment2.meta?.gitlabCommitSha ?? deployment2.meta?.bitbucketCommitSha ?? deployment2.meta?.gitCommitSha) === revision);
|
|
5524
|
+
const discover = async () => {
|
|
5525
|
+
const result = z33.object({ deployments: z33.array(z33.object({ uid: z33.string() })) }).parse(await api(`/v6/deployments?${scope}&projectId=${provider.projectId}&target=production&since=${Date.parse(record.environmentWrittenAt)}&limit=100`));
|
|
5526
|
+
const candidates = [];
|
|
5527
|
+
for (const item of result.deployments) {
|
|
5528
|
+
const found = await detail(item.uid);
|
|
5529
|
+
if (matches(found))
|
|
5530
|
+
candidates.push(found);
|
|
5531
|
+
}
|
|
5532
|
+
if (candidates.length > 1)
|
|
5533
|
+
fail2("deployment_ambiguous", "Multiple matching provider deployments exist. Select the existing intended operation before continuing; no additional build was started.");
|
|
5534
|
+
return candidates[0] ?? null;
|
|
5535
|
+
};
|
|
5536
|
+
let deployment = record.deploymentId ? await detail(record.deploymentId) : await discover();
|
|
5537
|
+
if (deployment && !matches(deployment))
|
|
5538
|
+
fail2("deployment_binding_conflict", "The stored deployment does not match this project, commit and environment generation.");
|
|
5539
|
+
if (!deployment && record.phase === "environment_written") {
|
|
5540
|
+
const status = await processCommand("git", ["status", "--porcelain=v1", "--untracked-files=all"], { cwd: local.repositoryRoot });
|
|
5541
|
+
const head = (await processCommand("git", ["rev-parse", "HEAD"], {
|
|
5542
|
+
cwd: local.repositoryRoot
|
|
5543
|
+
})).trim();
|
|
5544
|
+
if (status.trim() || head !== revision)
|
|
5545
|
+
fail2("source_changed_before_deploy", "The source changed after preflight. Commit only the intended source and rerun setup deploy.");
|
|
5546
|
+
await save({
|
|
5547
|
+
...record,
|
|
5548
|
+
phase: "deploy_requested",
|
|
5549
|
+
deployRequestedAt: new Date(deps.now()).toISOString()
|
|
5550
|
+
});
|
|
5551
|
+
try {
|
|
5552
|
+
if (provider.git) {
|
|
5553
|
+
const remote = (await processCommand("git", ["ls-remote", "origin", `refs/heads/${local.git.branch}`], { cwd: local.repositoryRoot })).trim().split(/\s/u)[0];
|
|
5554
|
+
if (remote !== revision)
|
|
5555
|
+
await processCommand("git", ["push", "origin", `HEAD:refs/heads/${local.git.branch}`], { cwd: local.repositoryRoot });
|
|
5556
|
+
else {
|
|
5557
|
+
const gitSource = {
|
|
5558
|
+
type: provider.git.type,
|
|
5559
|
+
ref: revision,
|
|
5560
|
+
...provider.git.repoId ? { repoId: provider.git.repoId } : {}
|
|
5561
|
+
};
|
|
5562
|
+
const body = JSON.stringify({
|
|
5563
|
+
name: provider.name,
|
|
5564
|
+
project: provider.projectId,
|
|
5565
|
+
target: "production",
|
|
5566
|
+
gitSource,
|
|
5567
|
+
meta: { siteplaneOperationId: record.operationId }
|
|
5568
|
+
});
|
|
5569
|
+
await processCommand("vercel", [
|
|
5570
|
+
"api",
|
|
5571
|
+
`/v13/deployments?${scope}`,
|
|
5572
|
+
"--method",
|
|
5573
|
+
"POST",
|
|
5574
|
+
"--input",
|
|
5575
|
+
"-",
|
|
5576
|
+
"--raw"
|
|
5577
|
+
], { cwd: local.projectDirectory, input: body, timeoutMs: 3e4 });
|
|
5578
|
+
}
|
|
5579
|
+
} else {
|
|
5580
|
+
const upload = await mkdtemp(join11(tmpdir(), "siteplane-setup-upload-"));
|
|
5581
|
+
try {
|
|
5582
|
+
await processCommand("git", [
|
|
5583
|
+
"clone",
|
|
5584
|
+
"--no-hardlinks",
|
|
5585
|
+
"--no-checkout",
|
|
5586
|
+
"--",
|
|
5587
|
+
local.repositoryRoot,
|
|
5588
|
+
upload
|
|
5589
|
+
], { cwd: local.repositoryRoot });
|
|
5590
|
+
await processCommand("git", ["checkout", "--detach", revision], {
|
|
5591
|
+
cwd: upload
|
|
5592
|
+
});
|
|
5593
|
+
await deps.writeBuildProvenance(upload, revision);
|
|
5594
|
+
await mkdir9(join11(upload, ".vercel"), {
|
|
5595
|
+
recursive: true,
|
|
5596
|
+
mode: 448
|
|
5597
|
+
});
|
|
5598
|
+
await writeFile6(join11(upload, ".vercel/project.json"), JSON.stringify({
|
|
5599
|
+
projectId: provider.projectId,
|
|
5600
|
+
orgId: provider.teamId
|
|
5601
|
+
}), { mode: 384 });
|
|
5602
|
+
await processCommand("vercel", [
|
|
5603
|
+
"deploy",
|
|
5604
|
+
"--yes",
|
|
5605
|
+
"--prod",
|
|
5606
|
+
"--no-wait",
|
|
5607
|
+
"--scope",
|
|
5608
|
+
provider.teamId,
|
|
5609
|
+
"--meta",
|
|
5610
|
+
`siteplaneOperationId=${record.operationId}`
|
|
5611
|
+
], { cwd: upload, timeoutMs: 12e4 });
|
|
5612
|
+
} finally {
|
|
5613
|
+
await rm5(upload, { recursive: true, force: true });
|
|
5614
|
+
}
|
|
5615
|
+
}
|
|
5616
|
+
} catch (error) {
|
|
5617
|
+
if (error instanceof SetupFailure && error.code === "provider_access_denied") {
|
|
5618
|
+
await save({
|
|
5619
|
+
...record,
|
|
5620
|
+
phase: "environment_written",
|
|
5621
|
+
deployRequestedAt: null
|
|
5622
|
+
});
|
|
5623
|
+
throw error;
|
|
5624
|
+
}
|
|
5625
|
+
if (!(error instanceof SetupFailure) || !["provider_timeout", "provider_transport_failed"].includes(error.code))
|
|
5626
|
+
throw error;
|
|
5627
|
+
}
|
|
5628
|
+
}
|
|
5629
|
+
let poll = 0;
|
|
5630
|
+
while (!deployment || !["READY", "ERROR", "CANCELED"].includes(deployment.readyState)) {
|
|
5631
|
+
progress();
|
|
5632
|
+
const remaining2 = Date.parse(record.deadline) - deps.now();
|
|
5633
|
+
if (remaining2 <= 0)
|
|
5634
|
+
fail2("provider_deadline_exceeded", "The 15-minute provider deadline expired. Inspect the recorded operation; setup will not start a second deployment on resume.");
|
|
5635
|
+
await deps.pause(Math.min(remaining2, Math.min(15e3, 2e3 * 2 ** Math.min(poll++, 3)) + Math.floor(Math.random() * 500)));
|
|
5636
|
+
deployment = deployment ? await detail(deployment.id) : await discover();
|
|
5637
|
+
if (deployment && !matches(deployment))
|
|
5638
|
+
fail2("deployment_binding_conflict", "The provider operation no longer matches this setup deployment.");
|
|
5639
|
+
}
|
|
5640
|
+
if (deployment.readyState !== "READY") {
|
|
5641
|
+
await save({ ...record, phase: "failed", deploymentId: deployment.id });
|
|
5642
|
+
fail2("provider_build_failed", "The existing provider build failed or was canceled. Fix its diagnosed package/build configuration before requesting another deployment.");
|
|
5643
|
+
}
|
|
5644
|
+
if (record.phase !== "verified")
|
|
5645
|
+
await save({
|
|
5646
|
+
...record,
|
|
5647
|
+
phase: "deployed",
|
|
5648
|
+
deploymentId: deployment.id
|
|
5649
|
+
});
|
|
5650
|
+
const logs = await processCommand("vercel", ["inspect", deployment.url, "--logs", "--scope", provider.teamId], { cwd: local.projectDirectory, includeStderr: true });
|
|
5651
|
+
const markers = [
|
|
5652
|
+
...logs.matchAll(/SITEPLANE_BUILD_PROOF=(\{[^\n]+\})/gu)
|
|
5653
|
+
].map((match) => buildProofSchema.parse(JSON.parse(match[1])));
|
|
5654
|
+
const proof = markers.find((item) => item.revision === revision && item.fieldContractHash === config.fieldContractHash);
|
|
5655
|
+
if (!proof || !/^22\.\d+\.\d+$/u.test(proof.node) || proof.siteplane !== config.packages.siteplane || proof.runtimeNext !== config.packages.runtimeNext || proof.lockfileHash !== `sha256:${local.lockfile.sha256}` || proof.next !== local.nextVersion || proof.react !== local.reactVersion || !(proof.packageManager === "npm" ? /^10\./u : /^(9|10)\./u).test(proof.packageManagerVersion) || local.packageManager.declared && `${proof.packageManager}@${proof.packageManagerVersion}` !== local.packageManager.declared)
|
|
5656
|
+
fail2("provider_build_identity_missing", "The actual provider build did not prove the expected Node, package manager, lockfile, package pins and applied source. Inspect its install/build configuration.");
|
|
5657
|
+
const canonical = await detail(new URL(origin).hostname);
|
|
5658
|
+
if (canonical.id !== deployment.id)
|
|
5659
|
+
fail2("production_alias_not_ready", "The canonical Production domain does not yet point to the verified deployment. Inspect the existing promotion; do not create another build.");
|
|
5660
|
+
const verified = await deps.verify({ ...input, deploymentUrl: origin });
|
|
5661
|
+
if (verified.status !== "ready_for_review")
|
|
5662
|
+
return {
|
|
5663
|
+
...verified,
|
|
5664
|
+
deadline: record.deadline,
|
|
5665
|
+
deploymentId: deployment.id
|
|
5666
|
+
};
|
|
5667
|
+
await save({
|
|
5668
|
+
...record,
|
|
5669
|
+
phase: "verified",
|
|
5670
|
+
deploymentId: deployment.id,
|
|
5671
|
+
build: {
|
|
5672
|
+
node: proof.node,
|
|
5673
|
+
packageManager: proof.packageManager,
|
|
5674
|
+
packageManagerVersion: proof.packageManagerVersion,
|
|
5675
|
+
lockfileHash: proof.lockfileHash
|
|
5676
|
+
}
|
|
5677
|
+
});
|
|
5678
|
+
return {
|
|
5679
|
+
status: "ready_for_review",
|
|
5680
|
+
deploymentId: deployment.id,
|
|
5681
|
+
origin,
|
|
5682
|
+
configurationFingerprint: fingerprint,
|
|
5683
|
+
deadline: record.deadline,
|
|
5684
|
+
build: record.build
|
|
5685
|
+
};
|
|
5686
|
+
} catch (error) {
|
|
5687
|
+
return {
|
|
5688
|
+
status: "action_required",
|
|
5689
|
+
code: error instanceof SetupFailure ? error.code : "setup_deployment_failed",
|
|
5690
|
+
message: error instanceof SetupFailure ? error.message : "The setup deployment could not be validated. Inspect the local connection and bound provider operation; credential values are never included.",
|
|
5691
|
+
...record ? { deploymentId: record.deploymentId, deadline: record.deadline } : {}
|
|
5692
|
+
};
|
|
5693
|
+
}
|
|
5694
|
+
}
|
|
4135
5695
|
|
|
4136
5696
|
// src/bin/run-siteplane.ts
|
|
4137
5697
|
async function runSiteplaneCli(args, dependencies = createDefaultDependencies()) {
|
|
5698
|
+
const writes = args[0] === "init" || args[0] === "setup" && ["apply", "prepare", "deploy"].includes(args[1] ?? "");
|
|
5699
|
+
const missingDeploy = args[0] === "setup" && args[1] === "deploy" && !await access6(join12(dependencies.cwd(), "siteplane.config.json")).then(
|
|
5700
|
+
() => true,
|
|
5701
|
+
() => false
|
|
5702
|
+
);
|
|
5703
|
+
return writes && !missingDeploy ? dependencies.withSetupLock(
|
|
5704
|
+
dependencies.cwd(),
|
|
5705
|
+
() => runSiteplaneCliUnlocked(args, dependencies)
|
|
5706
|
+
) : runSiteplaneCliUnlocked(args, dependencies);
|
|
5707
|
+
}
|
|
5708
|
+
async function runSiteplaneCliUnlocked(args, dependencies = createDefaultDependencies()) {
|
|
4138
5709
|
const [command, ...commandArgs] = args;
|
|
4139
5710
|
if (command === "init") {
|
|
4140
5711
|
const setupToken = readOption(commandArgs, "--setup-token");
|
|
@@ -4207,7 +5778,7 @@ async function runAnalyticsCommand(args, dependencies) {
|
|
|
4207
5778
|
siteId: projectConfig.siteId,
|
|
4208
5779
|
...readOptionalVercelAutomationBypassSecret(dependencies.env)
|
|
4209
5780
|
};
|
|
4210
|
-
const manifestPath =
|
|
5781
|
+
const manifestPath = join12(projectDir, "siteplane.analytics.json");
|
|
4211
5782
|
if (subcommand === "init") {
|
|
4212
5783
|
const rawPublicKey = await dependencies.commands.analyticsPublicKeyCommand({
|
|
4213
5784
|
config
|
|
@@ -4239,18 +5810,18 @@ async function runAnalyticsCommand(args, dependencies) {
|
|
|
4239
5810
|
projectDir
|
|
4240
5811
|
}
|
|
4241
5812
|
);
|
|
4242
|
-
const artifactFilePaths = [
|
|
5813
|
+
const artifactFilePaths = [join12(projectDir, ".siteplane/analytics.md")];
|
|
4243
5814
|
const cspFilePaths = await existingPaths([
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
5815
|
+
join12(projectDir, "next.config.js"),
|
|
5816
|
+
join12(projectDir, "next.config.mjs"),
|
|
5817
|
+
join12(projectDir, "next.config.ts"),
|
|
5818
|
+
join12(projectDir, "middleware.ts"),
|
|
5819
|
+
join12(projectDir, "src/middleware.ts")
|
|
4249
5820
|
]);
|
|
4250
5821
|
const check = await dependencies.commands.analyticsCheckCommand({
|
|
4251
5822
|
manifestPath,
|
|
4252
5823
|
sourceFilePaths,
|
|
4253
|
-
packageJsonPath:
|
|
5824
|
+
packageJsonPath: join12(projectDir, "package.json"),
|
|
4254
5825
|
cspFilePaths,
|
|
4255
5826
|
artifactFilePaths
|
|
4256
5827
|
});
|
|
@@ -4318,6 +5889,28 @@ async function runSetupCommand(args, dependencies) {
|
|
|
4318
5889
|
const connectionOptions = readOptionalVercelAutomationBypassSecret(
|
|
4319
5890
|
dependencies.env
|
|
4320
5891
|
);
|
|
5892
|
+
if (area === "deploy") {
|
|
5893
|
+
const result = await dependencies.commands.setupDeployCommand({
|
|
5894
|
+
projectDir,
|
|
5895
|
+
onProgress: (progress) => dependencies.stderr(JSON.stringify({ status: "waiting", ...progress }))
|
|
5896
|
+
});
|
|
5897
|
+
dependencies.stdout(JSON.stringify(result, null, 2));
|
|
5898
|
+
return { exitCode: result.status === "ready_for_review" ? 0 : 1 };
|
|
5899
|
+
}
|
|
5900
|
+
if (area === "preflight") {
|
|
5901
|
+
const result = await dependencies.commands.setupPreflightCommand({
|
|
5902
|
+
projectDir
|
|
5903
|
+
});
|
|
5904
|
+
dependencies.stdout(JSON.stringify(result, null, 2));
|
|
5905
|
+
return { exitCode: result.status === "ready" ? 0 : 1 };
|
|
5906
|
+
}
|
|
5907
|
+
if (area === "prepare") {
|
|
5908
|
+
const result = await dependencies.commands.setupPrepareCommand({
|
|
5909
|
+
projectDir
|
|
5910
|
+
});
|
|
5911
|
+
dependencies.stdout(JSON.stringify(result, null, 2));
|
|
5912
|
+
return { exitCode: result.status === "action_required" ? 1 : 0 };
|
|
5913
|
+
}
|
|
4321
5914
|
if (area === "context") {
|
|
4322
5915
|
const result = await dependencies.commands.siteSetupContextCommand({
|
|
4323
5916
|
projectDir,
|
|
@@ -4326,6 +5919,15 @@ async function runSetupCommand(args, dependencies) {
|
|
|
4326
5919
|
dependencies.stdout(JSON.stringify(result, null, 2));
|
|
4327
5920
|
return { exitCode: result.status === "action_required" ? 1 : 0 };
|
|
4328
5921
|
}
|
|
5922
|
+
if (area === "activity") {
|
|
5923
|
+
const result = await dependencies.commands.siteSetupActivityCommand({
|
|
5924
|
+
projectDir,
|
|
5925
|
+
phase: readRequiredOption(areaArgs, "--phase"),
|
|
5926
|
+
...connectionOptions
|
|
5927
|
+
});
|
|
5928
|
+
dependencies.stdout(JSON.stringify(result, null, 2));
|
|
5929
|
+
return { exitCode: result.status === "action_required" ? 1 : 0 };
|
|
5930
|
+
}
|
|
4329
5931
|
if (area === "apply") {
|
|
4330
5932
|
const editorDefinition = await dependencies.readJsonFile(
|
|
4331
5933
|
readRequiredOption(areaArgs, "--definition")
|
|
@@ -4333,6 +5935,12 @@ async function runSetupCommand(args, dependencies) {
|
|
|
4333
5935
|
const result = await dependencies.commands.siteSetupApplyCommand({
|
|
4334
5936
|
projectDir,
|
|
4335
5937
|
editorDefinition,
|
|
5938
|
+
contentSelectionSummary: await dependencies.readTextFile(
|
|
5939
|
+
resolve4(
|
|
5940
|
+
projectDir,
|
|
5941
|
+
readRequiredOption(areaArgs, "--selection-summary-file")
|
|
5942
|
+
)
|
|
5943
|
+
),
|
|
4336
5944
|
...connectionOptions
|
|
4337
5945
|
});
|
|
4338
5946
|
dependencies.stdout(JSON.stringify(result, null, 2));
|
|
@@ -4348,13 +5956,15 @@ async function runSetupCommand(args, dependencies) {
|
|
|
4348
5956
|
dependencies.stdout(JSON.stringify(result, null, 2));
|
|
4349
5957
|
return { exitCode: result.status === "ready_for_review" ? 0 : 1 };
|
|
4350
5958
|
}
|
|
4351
|
-
dependencies.stdout(
|
|
5959
|
+
dependencies.stdout(
|
|
5960
|
+
"Usage: siteplane setup <preflight|prepare|context|activity|apply|deploy|verify>"
|
|
5961
|
+
);
|
|
4352
5962
|
return { exitCode: 1 };
|
|
4353
5963
|
}
|
|
4354
5964
|
async function runBookingCommand(args, dependencies) {
|
|
4355
5965
|
const [subcommand, ...subcommandArgs] = args;
|
|
4356
5966
|
const projectDir = dependencies.cwd();
|
|
4357
|
-
const manifestPath =
|
|
5967
|
+
const manifestPath = join12(projectDir, "siteplane.booking.json");
|
|
4358
5968
|
if (subcommand === "init") {
|
|
4359
5969
|
const siteId = readRequiredOption(subcommandArgs, "--site-id");
|
|
4360
5970
|
const siteKeyPrefix = readRequiredOption(
|
|
@@ -4433,7 +6043,7 @@ async function runBookingCommand(args, dependencies) {
|
|
|
4433
6043
|
resourceId: readOption(subcommandArgs, "--resource-id") ?? null,
|
|
4434
6044
|
startUtc: readRequiredOption(subcommandArgs, "--start-utc"),
|
|
4435
6045
|
customer,
|
|
4436
|
-
clientToken: readOption(subcommandArgs, "--client-token") ??
|
|
6046
|
+
clientToken: readOption(subcommandArgs, "--client-token") ?? randomUUID7(),
|
|
4437
6047
|
paymentChoice: "onsite"
|
|
4438
6048
|
}
|
|
4439
6049
|
});
|
|
@@ -4469,19 +6079,25 @@ function createDefaultDependencies() {
|
|
|
4469
6079
|
env: process.env,
|
|
4470
6080
|
stdout: (line) => console.log(line),
|
|
4471
6081
|
stderr: (line) => console.error(line),
|
|
6082
|
+
readTextFile: (path) => readFile13(path, "utf8"),
|
|
6083
|
+
withSetupLock,
|
|
4472
6084
|
readJsonFile: async (path) => JSON.parse(
|
|
4473
|
-
await
|
|
6085
|
+
await readFile13(resolve4(process.cwd(), path), "utf8")
|
|
4474
6086
|
),
|
|
4475
6087
|
readPackageVersion: async () => {
|
|
4476
6088
|
const packageJson = JSON.parse(
|
|
4477
|
-
await
|
|
6089
|
+
await readFile13(new URL("../../package.json", import.meta.url), "utf8")
|
|
4478
6090
|
);
|
|
4479
6091
|
return packageJson.version;
|
|
4480
6092
|
},
|
|
4481
6093
|
commands: {
|
|
4482
6094
|
siteSetupApplyCommand,
|
|
4483
6095
|
siteSetupContextCommand,
|
|
6096
|
+
siteSetupActivityCommand,
|
|
4484
6097
|
siteSetupVerifyCommand,
|
|
6098
|
+
setupPreflightCommand,
|
|
6099
|
+
setupPrepareCommand,
|
|
6100
|
+
setupDeployCommand,
|
|
4485
6101
|
initCommand,
|
|
4486
6102
|
bookingInitCommand,
|
|
4487
6103
|
bookingCheckCommand,
|
|
@@ -4542,7 +6158,7 @@ async function existingPaths(paths) {
|
|
|
4542
6158
|
const existing = [];
|
|
4543
6159
|
for (const path of paths) {
|
|
4544
6160
|
try {
|
|
4545
|
-
await
|
|
6161
|
+
await access6(path);
|
|
4546
6162
|
existing.push(path);
|
|
4547
6163
|
} catch {
|
|
4548
6164
|
}
|