siteplane 0.1.42 → 0.1.44
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 +20 -1
- package/dist/bin/siteplane.js +1927 -433
- package/dist/index.js +1198 -377
- package/dist/next.js +1382 -396
- 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,20 +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 editorFieldSchema =
|
|
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({
|
|
1297
1346
|
fieldId: fieldIdSchema,
|
|
1298
|
-
label:
|
|
1299
|
-
subsection:
|
|
1300
|
-
description:
|
|
1301
|
-
required:
|
|
1302
|
-
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()
|
|
1303
1352
|
}).strict();
|
|
1304
|
-
var editorSectionSchema =
|
|
1305
|
-
label:
|
|
1306
|
-
fields:
|
|
1353
|
+
var editorSectionSchema = z15.object({
|
|
1354
|
+
label: z15.string().trim().min(1),
|
|
1355
|
+
fields: z15.array(editorFieldSchema).min(1)
|
|
1307
1356
|
}).strict().superRefine((section, context) => {
|
|
1308
1357
|
const closedSubsections = /* @__PURE__ */ new Set();
|
|
1309
1358
|
let currentSubsection;
|
|
@@ -1322,10 +1371,10 @@ var editorSectionSchema = z14.object({
|
|
|
1322
1371
|
}
|
|
1323
1372
|
}
|
|
1324
1373
|
});
|
|
1325
|
-
var editorRouteSchema =
|
|
1374
|
+
var editorRouteSchema = z15.object({
|
|
1326
1375
|
routeKey: siteFieldRouteKeySchema,
|
|
1327
|
-
label:
|
|
1328
|
-
sections:
|
|
1376
|
+
label: z15.string().trim().min(1),
|
|
1377
|
+
sections: z15.array(editorSectionSchema).min(1)
|
|
1329
1378
|
}).strict().superRefine((route, context) => {
|
|
1330
1379
|
const sections = /* @__PURE__ */ new Set();
|
|
1331
1380
|
for (const [index, section] of route.sections.entries()) {
|
|
@@ -1339,9 +1388,9 @@ var editorRouteSchema = z14.object({
|
|
|
1339
1388
|
sections.add(section.label);
|
|
1340
1389
|
}
|
|
1341
1390
|
});
|
|
1342
|
-
var editorDefinitionV1Schema =
|
|
1343
|
-
version:
|
|
1344
|
-
routes:
|
|
1391
|
+
var editorDefinitionV1Schema = z15.object({
|
|
1392
|
+
version: z15.literal(1),
|
|
1393
|
+
routes: z15.array(editorRouteSchema).min(1)
|
|
1345
1394
|
}).strict().superRefine((definition, context) => {
|
|
1346
1395
|
const routes = /* @__PURE__ */ new Set();
|
|
1347
1396
|
const fields = /* @__PURE__ */ new Set();
|
|
@@ -1379,7 +1428,7 @@ var editorDefinitionV1Schema = z14.object({
|
|
|
1379
1428
|
var forbiddenControlOrBidi = /[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u;
|
|
1380
1429
|
var forbiddenOwnerNoteControlOrBidi = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u;
|
|
1381
1430
|
var unsafeLocator = /<\/?[a-z][^>]*>|\b(?:javascript|data:text\/html)\s*:|\bon[a-z]+\s*=/iu;
|
|
1382
|
-
var renderedPathSchema =
|
|
1431
|
+
var renderedPathSchema = z15.string().trim().transform((value) => value !== "/" ? value.replace(/\/+$/u, "") : value).superRefine((value, context) => {
|
|
1383
1432
|
if (!value.startsWith("/") || value === "$global" || value.includes("?") || value.includes("#")) {
|
|
1384
1433
|
context.addIssue({
|
|
1385
1434
|
code: "custom",
|
|
@@ -1389,7 +1438,7 @@ var renderedPathSchema = z14.string().trim().transform((value) => value !== "/"
|
|
|
1389
1438
|
addSafeByteIssues(value, CONTINUE_SITE_SETUP_RENDERED_PATH_MAX_BYTES, "renderedPath", context);
|
|
1390
1439
|
});
|
|
1391
1440
|
function normalizedSafeTextSchema(maxBytes, label) {
|
|
1392
|
-
return
|
|
1441
|
+
return z15.string().transform((value) => value.trim().replace(new RegExp("\\p{White_Space}+", "gu"), " ")).pipe(z15.string().min(1)).superRefine((value, context) => {
|
|
1393
1442
|
addSafeByteIssues(value, maxBytes, label, context);
|
|
1394
1443
|
if (label === "locatorHint" && unsafeLocator.test(value)) {
|
|
1395
1444
|
context.addIssue({
|
|
@@ -1406,12 +1455,12 @@ var correctionItemBase = {
|
|
|
1406
1455
|
requestedLabel: normalizedSafeTextSchema(CONTINUE_SITE_SETUP_REQUESTED_LABEL_MAX_BYTES, "requestedLabel").optional(),
|
|
1407
1456
|
requestedSection: normalizedSafeTextSchema(CONTINUE_SITE_SETUP_REQUESTED_SECTION_MAX_BYTES, "requestedSection").optional(),
|
|
1408
1457
|
requestedSubsection: normalizedSafeTextSchema(CONTINUE_SITE_SETUP_REQUESTED_SUBSECTION_MAX_BYTES, "requestedSubsection").optional(),
|
|
1409
|
-
requestedSortOrder:
|
|
1458
|
+
requestedSortOrder: z15.number().int().nonnegative().optional()
|
|
1410
1459
|
};
|
|
1411
|
-
var setupCorrectionItemSchema =
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
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()
|
|
1415
1464
|
]).superRefine((item, context) => {
|
|
1416
1465
|
if (item.requestedSubsection && !item.requestedSection) {
|
|
1417
1466
|
context.addIssue({
|
|
@@ -1421,7 +1470,7 @@ var setupCorrectionItemSchema = z14.discriminatedUnion("kind", [
|
|
|
1421
1470
|
});
|
|
1422
1471
|
}
|
|
1423
1472
|
});
|
|
1424
|
-
var setupCorrectionItemsSchema =
|
|
1473
|
+
var setupCorrectionItemsSchema = z15.array(setupCorrectionItemSchema).max(CONTINUE_SITE_SETUP_MAX_ITEMS).superRefine((items, context) => {
|
|
1425
1474
|
const bytes = utf8Bytes(stableJsonStringify(items));
|
|
1426
1475
|
if (bytes > CONTINUE_SITE_SETUP_ITEMS_MAX_BYTES) {
|
|
1427
1476
|
context.addIssue({
|
|
@@ -1430,7 +1479,7 @@ var setupCorrectionItemsSchema = z14.array(setupCorrectionItemSchema).max(CONTIN
|
|
|
1430
1479
|
});
|
|
1431
1480
|
}
|
|
1432
1481
|
});
|
|
1433
|
-
var siteSetupOwnerEditabilityNoteSchema =
|
|
1482
|
+
var siteSetupOwnerEditabilityNoteSchema = z15.string().max(SITE_SETUP_OWNER_EDITABILITY_NOTE_MAX_LENGTH).superRefine((value, context) => {
|
|
1434
1483
|
if (utf8Bytes(value) > SITE_SETUP_OWNER_EDITABILITY_NOTE_MAX_BYTES) {
|
|
1435
1484
|
context.addIssue({
|
|
1436
1485
|
code: "custom",
|
|
@@ -1455,7 +1504,7 @@ var SITE_SETUP_MODULE_SCOPES = {
|
|
|
1455
1504
|
"analytics:public_key_read"
|
|
1456
1505
|
]
|
|
1457
1506
|
};
|
|
1458
|
-
var siteSetupModulesSchema =
|
|
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.");
|
|
1459
1508
|
function siteSetupScopes(modules) {
|
|
1460
1509
|
return modules.flatMap((module) => [...SITE_SETUP_MODULE_SCOPES[module]]);
|
|
1461
1510
|
}
|
|
@@ -1463,15 +1512,15 @@ function hasExactSiteSetupScopes(scopes, modules) {
|
|
|
1463
1512
|
const matches = (expected) => scopes.length === expected.length && expected.every((scope) => scopes.includes(scope));
|
|
1464
1513
|
return modules ? matches(siteSetupScopes(modules)) : matches(siteSetupScopes(["editing"])) || matches(siteSetupScopes(["editing", "analytics"]));
|
|
1465
1514
|
}
|
|
1466
|
-
var siteSetupActivityInputSchema =
|
|
1467
|
-
phase:
|
|
1515
|
+
var siteSetupActivityInputSchema = z15.object({
|
|
1516
|
+
phase: z15.enum([
|
|
1468
1517
|
"inspecting",
|
|
1469
1518
|
"integrating",
|
|
1470
1519
|
"deploying",
|
|
1471
1520
|
"verifying",
|
|
1472
1521
|
"repairing"
|
|
1473
1522
|
]),
|
|
1474
|
-
textId:
|
|
1523
|
+
textId: z15.enum([
|
|
1475
1524
|
"inspecting",
|
|
1476
1525
|
"integrating",
|
|
1477
1526
|
"deploying",
|
|
@@ -1480,8 +1529,8 @@ var siteSetupActivityInputSchema = z14.object({
|
|
|
1480
1529
|
])
|
|
1481
1530
|
}).strict().refine((value) => value.phase === value.textId, "Activity text must describe its phase.");
|
|
1482
1531
|
var nextActionFields = {
|
|
1483
|
-
actor:
|
|
1484
|
-
boundary:
|
|
1532
|
+
actor: z15.enum(["agent", "user", "siteplane"]),
|
|
1533
|
+
boundary: z15.enum([
|
|
1485
1534
|
"source",
|
|
1486
1535
|
"environment",
|
|
1487
1536
|
"deployment",
|
|
@@ -1489,33 +1538,33 @@ var nextActionFields = {
|
|
|
1489
1538
|
"authorization",
|
|
1490
1539
|
"review"
|
|
1491
1540
|
]),
|
|
1492
|
-
precondition:
|
|
1493
|
-
instruction:
|
|
1494
|
-
expectedEvidence:
|
|
1541
|
+
precondition: z15.string().min(1).max(1e3),
|
|
1542
|
+
instruction: z15.string().min(1).max(2e3),
|
|
1543
|
+
expectedEvidence: z15.string().min(1).max(1e3)
|
|
1495
1544
|
};
|
|
1496
|
-
var siteSetupNextActionSchema =
|
|
1497
|
-
|
|
1545
|
+
var siteSetupNextActionSchema = z15.discriminatedUnion("kind", [
|
|
1546
|
+
z15.object({
|
|
1498
1547
|
...nextActionFields,
|
|
1499
|
-
kind:
|
|
1500
|
-
command:
|
|
1548
|
+
kind: z15.literal("run_command"),
|
|
1549
|
+
command: z15.string().min(1).max(500)
|
|
1501
1550
|
}).strict(),
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
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()
|
|
1506
1555
|
]);
|
|
1507
|
-
var siteSetupTaskRunSchema =
|
|
1508
|
-
runId:
|
|
1556
|
+
var siteSetupTaskRunSchema = z15.object({
|
|
1557
|
+
runId: z15.string().uuid(),
|
|
1509
1558
|
mode: siteSetupRunModeSchema,
|
|
1510
1559
|
status: siteSetupRunStatusSchema,
|
|
1511
|
-
fieldContractHash:
|
|
1512
|
-
deploymentOrigin:
|
|
1513
|
-
deploymentRevision:
|
|
1514
|
-
publicRuntimeKeyId:
|
|
1515
|
-
reviewVersion:
|
|
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()
|
|
1516
1565
|
}).strict();
|
|
1517
|
-
var siteSetupTaskProgressSchema =
|
|
1518
|
-
progressStage:
|
|
1566
|
+
var siteSetupTaskProgressSchema = z15.object({
|
|
1567
|
+
progressStage: z15.enum([
|
|
1519
1568
|
"waiting_for_connection",
|
|
1520
1569
|
"agent_connected",
|
|
1521
1570
|
"project_context_loaded",
|
|
@@ -1525,66 +1574,73 @@ var siteSetupTaskProgressSchema = z14.object({
|
|
|
1525
1574
|
"action_required",
|
|
1526
1575
|
"completed"
|
|
1527
1576
|
]),
|
|
1528
|
-
lastVerifiedAt:
|
|
1529
|
-
lastActivityAt:
|
|
1530
|
-
activitySource:
|
|
1577
|
+
lastVerifiedAt: z15.string().datetime().nullable(),
|
|
1578
|
+
lastActivityAt: z15.string().datetime(),
|
|
1579
|
+
activitySource: z15.enum(["user", "server", "agent"]),
|
|
1531
1580
|
activityPhase: siteSetupActivityInputSchema.shape.phase.nullable(),
|
|
1532
1581
|
activityTextId: siteSetupActivityInputSchema.shape.textId.nullable()
|
|
1533
1582
|
}).strict();
|
|
1534
|
-
var siteSetupTaskSchema =
|
|
1535
|
-
contractVersion:
|
|
1536
|
-
workflow:
|
|
1537
|
-
goal:
|
|
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),
|
|
1538
1587
|
modules: siteSetupModulesSchema,
|
|
1539
|
-
authorizationVersion:
|
|
1540
|
-
authorizedScopes:
|
|
1541
|
-
site:
|
|
1542
|
-
siteId:
|
|
1543
|
-
portalPath:
|
|
1544
|
-
adminOrigin:
|
|
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()
|
|
1545
1594
|
}).strict().nullable(),
|
|
1546
1595
|
run: siteSetupTaskRunSchema.nullable(),
|
|
1547
1596
|
progress: siteSetupTaskProgressSchema.nullable(),
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
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({
|
|
1551
1602
|
text: siteSetupOwnerEditabilityNoteSchema,
|
|
1552
|
-
source:
|
|
1553
|
-
trust:
|
|
1603
|
+
source: z15.literal("user"),
|
|
1604
|
+
trust: z15.literal("untrusted")
|
|
1554
1605
|
}).strict(),
|
|
1555
1606
|
corrections: setupCorrectionItemsSchema,
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
source:
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
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)
|
|
1562
1618
|
}).strict()),
|
|
1563
1619
|
nextAction: siteSetupNextActionSchema,
|
|
1564
|
-
error:
|
|
1565
|
-
code:
|
|
1620
|
+
error: z15.object({
|
|
1621
|
+
code: z15.string().regex(/^[a-z0-9_.]+$/u).max(160),
|
|
1566
1622
|
boundary: nextActionFields.boundary,
|
|
1567
1623
|
responsibleActor: nextActionFields.actor,
|
|
1568
|
-
retryable:
|
|
1569
|
-
retryAfter:
|
|
1570
|
-
requestId:
|
|
1624
|
+
retryable: z15.boolean(),
|
|
1625
|
+
retryAfter: z15.number().int().nonnegative().nullable(),
|
|
1626
|
+
requestId: z15.string().regex(/^[A-Za-z0-9_-]+$/u).max(128)
|
|
1571
1627
|
}).strict().nullable(),
|
|
1572
|
-
packages:
|
|
1573
|
-
siteplane:
|
|
1574
|
-
runtimeNext:
|
|
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)
|
|
1575
1631
|
}).strict(),
|
|
1576
|
-
communication:
|
|
1577
|
-
beforeSourceChanges:
|
|
1578
|
-
updates:
|
|
1579
|
-
blocker:
|
|
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)
|
|
1580
1636
|
}).strict(),
|
|
1581
|
-
discovery:
|
|
1582
|
-
inspectPublicRoutes:
|
|
1583
|
-
contentKinds:
|
|
1584
|
-
rules:
|
|
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)
|
|
1585
1641
|
}).strict(),
|
|
1586
|
-
steps:
|
|
1587
|
-
id:
|
|
1642
|
+
steps: z15.array(z15.object({
|
|
1643
|
+
id: z15.enum([
|
|
1588
1644
|
"inspect_repository",
|
|
1589
1645
|
"install_packages",
|
|
1590
1646
|
"instrument_fields",
|
|
@@ -1592,34 +1648,35 @@ var siteSetupTaskSchema = z14.object({
|
|
|
1592
1648
|
"deploy_production",
|
|
1593
1649
|
"verify_deployment"
|
|
1594
1650
|
]),
|
|
1595
|
-
instruction:
|
|
1651
|
+
instruction: z15.string().min(1)
|
|
1596
1652
|
}).strict()).length(6),
|
|
1597
|
-
completion:
|
|
1598
|
-
verifiedStatus:
|
|
1599
|
-
instruction:
|
|
1653
|
+
completion: z15.object({
|
|
1654
|
+
verifiedStatus: z15.literal("ready_for_review"),
|
|
1655
|
+
instruction: z15.string().min(1)
|
|
1600
1656
|
}).strict()
|
|
1601
1657
|
}).strict().refine((task) => hasExactSiteSetupScopes(task.authorizedScopes, task.modules), "Task scopes must exactly match its modules.");
|
|
1602
|
-
var canonicalHashSchema =
|
|
1658
|
+
var canonicalHashSchema = z15.string().regex(/^sha256:[0-9a-f]{64}$/iu);
|
|
1603
1659
|
var deploymentRevisionSchema = normalizedSafeTextSchema(256, "deploymentRevision");
|
|
1604
|
-
var continueSiteSetupInputSchema =
|
|
1605
|
-
siteId:
|
|
1606
|
-
runId:
|
|
1660
|
+
var continueSiteSetupInputSchema = z15.object({
|
|
1661
|
+
siteId: z15.string().uuid(),
|
|
1662
|
+
runId: z15.string().uuid(),
|
|
1607
1663
|
expectedFieldContractHash: canonicalHashSchema,
|
|
1608
1664
|
expectedDeploymentRevision: deploymentRevisionSchema,
|
|
1609
1665
|
correctionItems: setupCorrectionItemsSchema,
|
|
1610
1666
|
ownerEditabilityNote: siteSetupOwnerEditabilityNoteSchema.optional()
|
|
1611
1667
|
}).strict();
|
|
1612
|
-
var saveSiteSetupOwnerNoteInputSchema =
|
|
1613
|
-
siteId:
|
|
1614
|
-
runId:
|
|
1668
|
+
var saveSiteSetupOwnerNoteInputSchema = z15.object({
|
|
1669
|
+
siteId: z15.string().uuid(),
|
|
1670
|
+
runId: z15.string().uuid(),
|
|
1615
1671
|
expectedFieldContractHash: canonicalHashSchema,
|
|
1616
1672
|
expectedDeploymentRevision: deploymentRevisionSchema,
|
|
1617
1673
|
ownerEditabilityNote: siteSetupOwnerEditabilityNoteSchema
|
|
1618
1674
|
}).strict();
|
|
1619
|
-
var siteSetupContextInputSchema =
|
|
1620
|
-
var siteSetupApplyInputSchema =
|
|
1675
|
+
var siteSetupContextInputSchema = z15.object({}).strict();
|
|
1676
|
+
var siteSetupApplyInputSchema = z15.object({
|
|
1621
1677
|
fieldContract: siteFieldContractV1Schema,
|
|
1622
|
-
editorDefinition: editorDefinitionV1Schema
|
|
1678
|
+
editorDefinition: editorDefinitionV1Schema,
|
|
1679
|
+
contentSelectionSummary: siteSetupOwnerEditabilityNoteSchema.refine((text) => text.trim().length > 0, "Describe the selected content, important exclusions and any open gaps.")
|
|
1623
1680
|
}).strict().superRefine((input, context) => {
|
|
1624
1681
|
const contractRoutesByFieldId = new Map(input.fieldContract.fields.map((field) => [
|
|
1625
1682
|
field.fieldId,
|
|
@@ -1683,37 +1740,37 @@ var siteSetupApplyInputSchema = z14.object({
|
|
|
1683
1740
|
}
|
|
1684
1741
|
}
|
|
1685
1742
|
});
|
|
1686
|
-
var siteSetupVerifyInputSchema =
|
|
1687
|
-
deploymentUrl:
|
|
1743
|
+
var siteSetupVerifyInputSchema = z15.object({
|
|
1744
|
+
deploymentUrl: z15.string().url().refine((value) => value.startsWith("https://")),
|
|
1688
1745
|
deploymentRevision: deploymentRevisionSchema,
|
|
1689
|
-
wait:
|
|
1746
|
+
wait: z15.boolean().optional()
|
|
1690
1747
|
}).strict();
|
|
1691
|
-
var siteSetupEvidenceInputSchema =
|
|
1692
|
-
siteId:
|
|
1693
|
-
runId:
|
|
1748
|
+
var siteSetupEvidenceInputSchema = z15.object({
|
|
1749
|
+
siteId: z15.string().uuid(),
|
|
1750
|
+
runId: z15.string().uuid(),
|
|
1694
1751
|
expectedFieldContractHash: canonicalHashSchema,
|
|
1695
|
-
expectedDeploymentOrigin:
|
|
1752
|
+
expectedDeploymentOrigin: z15.string().url().refine((value) => value.startsWith("https://")),
|
|
1696
1753
|
expectedDeploymentRevision: deploymentRevisionSchema,
|
|
1697
|
-
publicRuntimeKeyId:
|
|
1754
|
+
publicRuntimeKeyId: z15.string().uuid(),
|
|
1698
1755
|
bridgeSessionId: normalizedSafeTextSchema(256, "bridgeSessionId"),
|
|
1699
|
-
observations:
|
|
1756
|
+
observations: z15.array(z15.object({
|
|
1700
1757
|
fieldId: fieldIdSchema,
|
|
1701
1758
|
routeKey: siteFieldRouteKeySchema,
|
|
1702
1759
|
renderedPath: renderedPathSchema,
|
|
1703
|
-
targetResolved:
|
|
1704
|
-
previewAck:
|
|
1760
|
+
targetResolved: z15.boolean(),
|
|
1761
|
+
previewAck: z15.boolean()
|
|
1705
1762
|
}).strict().refine((value) => !value.previewAck || value.targetResolved, "Preview ACK requires a resolved target.")).min(1).max(200)
|
|
1706
1763
|
}).strict();
|
|
1707
|
-
var completeSiteSetupInputSchema =
|
|
1708
|
-
siteId:
|
|
1709
|
-
runId:
|
|
1764
|
+
var completeSiteSetupInputSchema = z15.object({
|
|
1765
|
+
siteId: z15.string().uuid(),
|
|
1766
|
+
runId: z15.string().uuid(),
|
|
1710
1767
|
expectedFieldContractHash: canonicalHashSchema,
|
|
1711
1768
|
expectedDeploymentRevision: deploymentRevisionSchema,
|
|
1712
1769
|
expectedReadyFingerprint: canonicalHashSchema
|
|
1713
1770
|
}).strict();
|
|
1714
|
-
var refreshSiteSetupDeploymentInputSchema =
|
|
1715
|
-
siteId:
|
|
1716
|
-
deploymentUrl:
|
|
1771
|
+
var refreshSiteSetupDeploymentInputSchema = z15.object({
|
|
1772
|
+
siteId: z15.string().uuid(),
|
|
1773
|
+
deploymentUrl: z15.string().url().refine((value) => value.startsWith("https://")),
|
|
1717
1774
|
deploymentRevision: deploymentRevisionSchema
|
|
1718
1775
|
}).strict();
|
|
1719
1776
|
function addSafeByteIssues(value, maxBytes, label, context) {
|
|
@@ -1736,34 +1793,34 @@ function utf8Bytes(value) {
|
|
|
1736
1793
|
|
|
1737
1794
|
// ../shared/dist/bridge-protocol.js
|
|
1738
1795
|
var BRIDGE_PROTOCOL_VERSION = 2;
|
|
1739
|
-
var bridgeRectSchema =
|
|
1740
|
-
x:
|
|
1741
|
-
y:
|
|
1742
|
-
width:
|
|
1743
|
-
height:
|
|
1796
|
+
var bridgeRectSchema = z16.object({
|
|
1797
|
+
x: z16.number(),
|
|
1798
|
+
y: z16.number(),
|
|
1799
|
+
width: z16.number().nonnegative(),
|
|
1800
|
+
height: z16.number().nonnegative()
|
|
1744
1801
|
});
|
|
1745
|
-
var previewTextValueSchema =
|
|
1746
|
-
fieldType:
|
|
1747
|
-
value:
|
|
1802
|
+
var previewTextValueSchema = z16.object({
|
|
1803
|
+
fieldType: z16.enum(["text", "longText"]),
|
|
1804
|
+
value: z16.string()
|
|
1748
1805
|
});
|
|
1749
|
-
var previewLinkValueSchema =
|
|
1750
|
-
fieldType:
|
|
1751
|
-
value:
|
|
1752
|
-
href:
|
|
1753
|
-
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()
|
|
1754
1811
|
})
|
|
1755
1812
|
});
|
|
1756
|
-
var previewImageValueSchema =
|
|
1757
|
-
fieldType:
|
|
1813
|
+
var previewImageValueSchema = z16.object({
|
|
1814
|
+
fieldType: z16.literal("image"),
|
|
1758
1815
|
value: imageValueSchema
|
|
1759
1816
|
});
|
|
1760
|
-
var bridgePreviewValueSchema =
|
|
1817
|
+
var bridgePreviewValueSchema = z16.discriminatedUnion("fieldType", [
|
|
1761
1818
|
previewTextValueSchema,
|
|
1762
1819
|
previewLinkValueSchema,
|
|
1763
1820
|
previewImageValueSchema
|
|
1764
1821
|
]);
|
|
1765
|
-
var fieldSelectionModeSchema =
|
|
1766
|
-
var fieldBridgeMissingContentActionSchema =
|
|
1822
|
+
var fieldSelectionModeSchema = z16.enum(["replace", "range", "toggle"]);
|
|
1823
|
+
var fieldBridgeMissingContentActionSchema = z16.enum([
|
|
1767
1824
|
"add",
|
|
1768
1825
|
"select",
|
|
1769
1826
|
"remove"
|
|
@@ -1782,77 +1839,77 @@ var FIELD_BRIDGE_MESSAGE_TYPES = [
|
|
|
1782
1839
|
"siteplane:field-bridge:preview-scrolled",
|
|
1783
1840
|
"siteplane:field-bridge:error"
|
|
1784
1841
|
];
|
|
1785
|
-
var canonicalFieldContractHashSchema =
|
|
1786
|
-
var bridgeSectionSchema =
|
|
1787
|
-
var realRenderedPathSchema =
|
|
1788
|
-
var fieldBridgeIdentitySchema =
|
|
1789
|
-
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(),
|
|
1790
1847
|
fieldContractHash: canonicalFieldContractHashSchema,
|
|
1791
|
-
deploymentRevision:
|
|
1848
|
+
deploymentRevision: z16.string().trim().min(1).max(256)
|
|
1792
1849
|
}).strict();
|
|
1793
|
-
var fieldBridgeBaseSchema =
|
|
1794
|
-
version:
|
|
1795
|
-
type:
|
|
1796
|
-
siteId:
|
|
1797
|
-
sessionId:
|
|
1798
|
-
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(),
|
|
1799
1856
|
fieldContractHash: canonicalFieldContractHashSchema,
|
|
1800
|
-
deploymentRevision:
|
|
1801
|
-
requestId:
|
|
1802
|
-
sentAt:
|
|
1857
|
+
deploymentRevision: z16.string().trim().min(1).max(256),
|
|
1858
|
+
requestId: z16.string().trim().min(1),
|
|
1859
|
+
sentAt: z16.string().datetime()
|
|
1803
1860
|
}).strict();
|
|
1804
|
-
var fieldBridgeFieldSchema =
|
|
1861
|
+
var fieldBridgeFieldSchema = z16.object({
|
|
1805
1862
|
fieldId: fieldIdSchema,
|
|
1806
1863
|
fieldType: fieldTypeSchema,
|
|
1807
1864
|
routeKey: siteFieldRouteKeySchema,
|
|
1808
1865
|
renderedPath: realRenderedPathSchema,
|
|
1809
|
-
sourcePath:
|
|
1866
|
+
sourcePath: z16.string().trim().min(1).optional(),
|
|
1810
1867
|
editTarget: fieldIdSchema,
|
|
1811
1868
|
rect: bridgeRectSchema,
|
|
1812
|
-
targetResolved:
|
|
1869
|
+
targetResolved: z16.literal(true),
|
|
1813
1870
|
currentValue: bridgePreviewValueSchema.optional()
|
|
1814
1871
|
}).strict();
|
|
1815
1872
|
var fieldBridgeInitMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1816
|
-
type:
|
|
1817
|
-
payload:
|
|
1818
|
-
adminOrigin:
|
|
1819
|
-
customerOrigin:
|
|
1873
|
+
type: z16.literal("siteplane:field-bridge:init"),
|
|
1874
|
+
payload: z16.object({
|
|
1875
|
+
adminOrigin: z16.string().url(),
|
|
1876
|
+
customerOrigin: z16.string().url(),
|
|
1820
1877
|
renderedPath: realRenderedPathSchema
|
|
1821
1878
|
}).strict()
|
|
1822
1879
|
});
|
|
1823
1880
|
var fieldBridgeReadyMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1824
|
-
type:
|
|
1825
|
-
payload:
|
|
1881
|
+
type: z16.literal("siteplane:field-bridge:ready"),
|
|
1882
|
+
payload: z16.object({
|
|
1826
1883
|
renderedPath: realRenderedPathSchema,
|
|
1827
|
-
fieldCount:
|
|
1828
|
-
capabilities:
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
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")
|
|
1833
1890
|
])
|
|
1834
1891
|
}).strict()
|
|
1835
1892
|
});
|
|
1836
1893
|
var fieldBridgeFieldsMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1837
|
-
type:
|
|
1838
|
-
payload:
|
|
1894
|
+
type: z16.literal("siteplane:field-bridge:fields"),
|
|
1895
|
+
payload: z16.object({
|
|
1839
1896
|
renderedPath: realRenderedPathSchema,
|
|
1840
|
-
fields:
|
|
1897
|
+
fields: z16.array(fieldBridgeFieldSchema)
|
|
1841
1898
|
}).strict()
|
|
1842
1899
|
});
|
|
1843
1900
|
var fieldBridgeSelectFieldMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1844
|
-
type:
|
|
1845
|
-
payload:
|
|
1901
|
+
type: z16.literal("siteplane:field-bridge:select-field"),
|
|
1902
|
+
payload: z16.object({
|
|
1846
1903
|
fieldId: fieldIdSchema,
|
|
1847
1904
|
selectionMode: fieldSelectionModeSchema
|
|
1848
1905
|
}).strict()
|
|
1849
1906
|
});
|
|
1850
1907
|
var fieldBridgeSetSelectedFieldMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1851
|
-
type:
|
|
1852
|
-
payload:
|
|
1853
|
-
fieldIds:
|
|
1908
|
+
type: z16.literal("siteplane:field-bridge:set-selected-field"),
|
|
1909
|
+
payload: z16.object({
|
|
1910
|
+
fieldIds: z16.array(fieldIdSchema).max(200),
|
|
1854
1911
|
primaryFieldId: fieldIdSchema.nullable(),
|
|
1855
|
-
scrollIntoView:
|
|
1912
|
+
scrollIntoView: z16.boolean().optional()
|
|
1856
1913
|
}).strict().superRefine((value, context) => {
|
|
1857
1914
|
if (value.fieldIds.length > 0 && value.primaryFieldId === null) {
|
|
1858
1915
|
context.addIssue({
|
|
@@ -1871,58 +1928,58 @@ var fieldBridgeSetSelectedFieldMessageSchema = fieldBridgeBaseSchema.extend({
|
|
|
1871
1928
|
})
|
|
1872
1929
|
});
|
|
1873
1930
|
var fieldBridgeApplyPreviewValuesMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1874
|
-
type:
|
|
1875
|
-
payload:
|
|
1931
|
+
type: z16.literal("siteplane:field-bridge:apply-preview-values"),
|
|
1932
|
+
payload: z16.object({ values: z16.record(fieldIdSchema, bridgePreviewValueSchema) }).strict()
|
|
1876
1933
|
});
|
|
1877
1934
|
var fieldBridgeTestPreviewValuesMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1878
|
-
type:
|
|
1879
|
-
payload:
|
|
1935
|
+
type: z16.literal("siteplane:field-bridge:test-preview-values"),
|
|
1936
|
+
payload: z16.object({ fieldIds: z16.array(fieldIdSchema).min(1).max(200) }).strict()
|
|
1880
1937
|
});
|
|
1881
1938
|
var fieldBridgePreviewAckMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1882
|
-
type:
|
|
1883
|
-
payload:
|
|
1884
|
-
ackRequestId:
|
|
1885
|
-
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"]),
|
|
1886
1943
|
renderedPath: realRenderedPathSchema,
|
|
1887
|
-
fieldIds:
|
|
1944
|
+
fieldIds: z16.array(fieldIdSchema).max(200)
|
|
1888
1945
|
}).strict()
|
|
1889
1946
|
});
|
|
1890
1947
|
var fieldBridgeSetVisualControlStateMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1891
|
-
type:
|
|
1892
|
-
payload:
|
|
1893
|
-
enabled:
|
|
1894
|
-
fieldProximityEnabled:
|
|
1895
|
-
pendingKeys:
|
|
1896
|
-
activeFieldIds:
|
|
1897
|
-
fieldSections:
|
|
1898
|
-
selectedPending:
|
|
1899
|
-
key:
|
|
1900
|
-
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)
|
|
1901
1958
|
}).strict()).max(50),
|
|
1902
|
-
scrollPendingIntoView:
|
|
1959
|
+
scrollPendingIntoView: z16.boolean().optional()
|
|
1903
1960
|
}).strict()
|
|
1904
1961
|
});
|
|
1905
1962
|
var fieldBridgeMissingContentMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1906
|
-
type:
|
|
1907
|
-
payload:
|
|
1963
|
+
type: z16.literal("siteplane:field-bridge:missing-content"),
|
|
1964
|
+
payload: z16.object({
|
|
1908
1965
|
action: fieldBridgeMissingContentActionSchema,
|
|
1909
1966
|
item: setupCorrectionItemSchema,
|
|
1910
1967
|
suggestedSection: bridgeSectionSchema.optional()
|
|
1911
1968
|
}).strict()
|
|
1912
1969
|
});
|
|
1913
1970
|
var fieldBridgePreviewScrolledMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1914
|
-
type:
|
|
1915
|
-
payload:
|
|
1971
|
+
type: z16.literal("siteplane:field-bridge:preview-scrolled"),
|
|
1972
|
+
payload: z16.object({ renderedPath: realRenderedPathSchema }).strict()
|
|
1916
1973
|
});
|
|
1917
1974
|
var fieldBridgeErrorMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1918
|
-
type:
|
|
1919
|
-
payload:
|
|
1920
|
-
code:
|
|
1921
|
-
message:
|
|
1922
|
-
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)
|
|
1923
1980
|
}).strict()
|
|
1924
1981
|
});
|
|
1925
|
-
var fieldBridgeMessageSchema =
|
|
1982
|
+
var fieldBridgeMessageSchema = z16.discriminatedUnion("type", [
|
|
1926
1983
|
fieldBridgeInitMessageSchema,
|
|
1927
1984
|
fieldBridgeReadyMessageSchema,
|
|
1928
1985
|
fieldBridgeFieldsMessageSchema,
|
|
@@ -1938,51 +1995,51 @@ var fieldBridgeMessageSchema = z15.discriminatedUnion("type", [
|
|
|
1938
1995
|
]);
|
|
1939
1996
|
|
|
1940
1997
|
// ../shared/dist/commands.js
|
|
1941
|
-
import { z as
|
|
1998
|
+
import { z as z18 } from "zod";
|
|
1942
1999
|
|
|
1943
2000
|
// ../shared/dist/errors.js
|
|
1944
|
-
import { z as
|
|
2001
|
+
import { z as z17 } from "zod";
|
|
1945
2002
|
var UI_ERROR_CATEGORIES = [
|
|
1946
2003
|
"site_member",
|
|
1947
2004
|
"workspace_member",
|
|
1948
2005
|
"internal"
|
|
1949
2006
|
];
|
|
1950
|
-
var uiErrorCategorySchema =
|
|
1951
|
-
var commandErrorPayloadSchema =
|
|
1952
|
-
code:
|
|
1953
|
-
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),
|
|
1954
2011
|
category: uiErrorCategorySchema,
|
|
1955
|
-
details:
|
|
2012
|
+
details: z17.record(z17.string(), z17.unknown()).optional()
|
|
1956
2013
|
});
|
|
1957
2014
|
|
|
1958
2015
|
// ../shared/dist/commands.js
|
|
1959
|
-
var commandSuccessSchema =
|
|
1960
|
-
ok:
|
|
1961
|
-
data:
|
|
2016
|
+
var commandSuccessSchema = z18.object({
|
|
2017
|
+
ok: z18.literal(true),
|
|
2018
|
+
data: z18.unknown()
|
|
1962
2019
|
});
|
|
1963
|
-
var commandErrorSchema =
|
|
1964
|
-
ok:
|
|
2020
|
+
var commandErrorSchema = z18.object({
|
|
2021
|
+
ok: z18.literal(false),
|
|
1965
2022
|
error: commandErrorPayloadSchema
|
|
1966
2023
|
});
|
|
1967
|
-
var commandResultSchema =
|
|
2024
|
+
var commandResultSchema = z18.discriminatedUnion("ok", [
|
|
1968
2025
|
commandSuccessSchema,
|
|
1969
2026
|
commandErrorSchema
|
|
1970
2027
|
]);
|
|
1971
2028
|
|
|
1972
2029
|
// ../shared/dist/editor-settings.js
|
|
1973
|
-
import { z as
|
|
1974
|
-
var editorColorSchemeSchema =
|
|
2030
|
+
import { z as z19 } from "zod";
|
|
2031
|
+
var editorColorSchemeSchema = z19.enum([
|
|
1975
2032
|
"paper",
|
|
1976
2033
|
"slate",
|
|
1977
2034
|
"siteplane",
|
|
1978
2035
|
"bloom"
|
|
1979
2036
|
]);
|
|
1980
2037
|
var editorColorSchemes = editorColorSchemeSchema.options;
|
|
1981
|
-
var editorColorModeSchema =
|
|
2038
|
+
var editorColorModeSchema = z19.enum(["light", "dark"]);
|
|
1982
2039
|
var editorColorModes = editorColorModeSchema.options;
|
|
1983
2040
|
|
|
1984
2041
|
// ../shared/dist/site-slug.js
|
|
1985
|
-
import { z as
|
|
2042
|
+
import { z as z20 } from "zod";
|
|
1986
2043
|
var SITE_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
1987
2044
|
var RESERVED_SITE_SLUGS = /* @__PURE__ */ new Set([
|
|
1988
2045
|
"account",
|
|
@@ -1999,31 +2056,31 @@ var RESERVED_SITE_SLUGS = /* @__PURE__ */ new Set([
|
|
|
1999
2056
|
"support",
|
|
2000
2057
|
"workspace"
|
|
2001
2058
|
]);
|
|
2002
|
-
var siteSlugSchema =
|
|
2059
|
+
var siteSlugSchema = z20.string().regex(SITE_SLUG_PATTERN, "Invalid site slug").refine((value) => !RESERVED_SITE_SLUGS.has(value), "Reserved site slug");
|
|
2003
2060
|
|
|
2004
2061
|
// ../shared/dist/entitlements.js
|
|
2005
|
-
import { z as
|
|
2006
|
-
var entitlementSnapshotSchema =
|
|
2007
|
-
planKey:
|
|
2008
|
-
sitesLimit:
|
|
2009
|
-
siteMembersPerSiteLimit:
|
|
2010
|
-
customAdminDomainsEnabled:
|
|
2011
|
-
agentMcpEnabled:
|
|
2012
|
-
emailNotificationsEnabled:
|
|
2013
|
-
analyticsEnabled:
|
|
2014
|
-
analyticsSitesLimit:
|
|
2015
|
-
analyticsPortalPerformanceEnabled:
|
|
2016
|
-
analyticsMonthlyReportsEnabled:
|
|
2017
|
-
analyticsAiSummaryEnabled:
|
|
2018
|
-
analyticsRawEventRetentionDays:
|
|
2019
|
-
analyticsMonthlyEventLimit:
|
|
2020
|
-
bookingEnabled:
|
|
2021
|
-
bookingSitesLimit:
|
|
2022
|
-
bookingPortalManagementEnabled:
|
|
2023
|
-
bookingResourcesPerSiteLimit:
|
|
2024
|
-
bookingServicesPerSiteLimit:
|
|
2025
|
-
bookingMonthlyBookingsLimit:
|
|
2026
|
-
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)
|
|
2027
2084
|
});
|
|
2028
2085
|
var EARLY_ACCESS_PLAN = {
|
|
2029
2086
|
planKey: "early_access_2026",
|
|
@@ -2049,7 +2106,7 @@ var EARLY_ACCESS_PLAN = {
|
|
|
2049
2106
|
};
|
|
2050
2107
|
|
|
2051
2108
|
// ../shared/dist/readiness.js
|
|
2052
|
-
import { z as
|
|
2109
|
+
import { z as z22 } from "zod";
|
|
2053
2110
|
var READINESS_CHECK_KEYS = [
|
|
2054
2111
|
"agent_instructions_installed",
|
|
2055
2112
|
"runtime_package_connected",
|
|
@@ -2064,19 +2121,19 @@ var READINESS_STATUSES = [
|
|
|
2064
2121
|
"failing",
|
|
2065
2122
|
"warning"
|
|
2066
2123
|
];
|
|
2067
|
-
var readinessCheckKeySchema =
|
|
2068
|
-
var readinessStatusSchema =
|
|
2124
|
+
var readinessCheckKeySchema = z22.enum(READINESS_CHECK_KEYS);
|
|
2125
|
+
var readinessStatusSchema = z22.enum(READINESS_STATUSES);
|
|
2069
2126
|
|
|
2070
2127
|
// ../shared/dist/site.js
|
|
2071
|
-
import { z as
|
|
2128
|
+
import { z as z23 } from "zod";
|
|
2072
2129
|
var SITE_STATUSES = [
|
|
2073
2130
|
"setup",
|
|
2074
2131
|
"active",
|
|
2075
2132
|
"disabled",
|
|
2076
2133
|
"archived"
|
|
2077
2134
|
];
|
|
2078
|
-
var siteStatusSchema =
|
|
2079
|
-
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");
|
|
2080
2137
|
|
|
2081
2138
|
// ../cli/dist/analytics/manifest.js
|
|
2082
2139
|
async function readAnalyticsManifestFile(manifestPath) {
|
|
@@ -2403,17 +2460,17 @@ function readJsxAttributes(attributes) {
|
|
|
2403
2460
|
if (!ts.isJsxAttribute(attribute)) {
|
|
2404
2461
|
continue;
|
|
2405
2462
|
}
|
|
2406
|
-
const
|
|
2463
|
+
const name2 = attribute.name.getText();
|
|
2407
2464
|
if (!attribute.initializer) {
|
|
2408
|
-
values.set(
|
|
2465
|
+
values.set(name2, true);
|
|
2409
2466
|
continue;
|
|
2410
2467
|
}
|
|
2411
2468
|
if (ts.isStringLiteral(attribute.initializer)) {
|
|
2412
|
-
values.set(
|
|
2469
|
+
values.set(name2, attribute.initializer.text);
|
|
2413
2470
|
continue;
|
|
2414
2471
|
}
|
|
2415
2472
|
if (ts.isJsxExpression(attribute.initializer)) {
|
|
2416
|
-
values.set(
|
|
2473
|
+
values.set(name2, staticExpressionValue(attribute.initializer.expression));
|
|
2417
2474
|
}
|
|
2418
2475
|
}
|
|
2419
2476
|
return values;
|
|
@@ -2881,16 +2938,16 @@ async function analyticsReadinessCommand(input) {
|
|
|
2881
2938
|
}
|
|
2882
2939
|
|
|
2883
2940
|
// ../cli/dist/commands/analytics/public-key.js
|
|
2884
|
-
import { z as
|
|
2885
|
-
var responseSchema =
|
|
2886
|
-
ok:
|
|
2887
|
-
data:
|
|
2888
|
-
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}$/)
|
|
2889
2946
|
})
|
|
2890
2947
|
});
|
|
2891
|
-
var errorResponseSchema =
|
|
2892
|
-
error:
|
|
2893
|
-
code:
|
|
2948
|
+
var errorResponseSchema = z24.object({
|
|
2949
|
+
error: z24.object({
|
|
2950
|
+
code: z24.string().regex(/^[a-z][a-z0-9_.-]+$/u)
|
|
2894
2951
|
})
|
|
2895
2952
|
});
|
|
2896
2953
|
async function analyticsPublicKeyCommand(input) {
|
|
@@ -3110,14 +3167,22 @@ async function bookingTestCommand(input) {
|
|
|
3110
3167
|
import { access, chmod, open, readFile as readFile5, rename, rm } from "fs/promises";
|
|
3111
3168
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3112
3169
|
import { join as join3 } from "path";
|
|
3113
|
-
import { z as
|
|
3114
|
-
var siteplaneProjectConfigSchema =
|
|
3115
|
-
version:
|
|
3116
|
-
apiBaseUrl:
|
|
3117
|
-
siteId:
|
|
3118
|
-
publicSiteKeyId:
|
|
3119
|
-
publicSiteKey:
|
|
3120
|
-
|
|
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()
|
|
3121
3186
|
}).strict();
|
|
3122
3187
|
async function readProjectPackageJson(projectDir) {
|
|
3123
3188
|
return JSON.parse(await readFile5(join3(projectDir, "package.json"), "utf8"));
|
|
@@ -3174,16 +3239,21 @@ async function writeJsonAtomically(path, value, mode) {
|
|
|
3174
3239
|
// ../cli/dist/config/credentials.js
|
|
3175
3240
|
import { execFile } from "child_process";
|
|
3176
3241
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3177
|
-
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";
|
|
3178
3243
|
import { dirname as dirname5, join as join4, relative } from "path";
|
|
3179
3244
|
import { promisify } from "util";
|
|
3180
|
-
import { z as
|
|
3181
|
-
var siteplaneCredentialsSchema =
|
|
3182
|
-
accessToken:
|
|
3183
|
-
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()
|
|
3184
3253
|
}).strict();
|
|
3185
3254
|
async function assertLocalCredentialsPathSafe(projectDir, options = {}) {
|
|
3186
3255
|
const path = join4(projectDir, ".siteplane/credentials.json");
|
|
3256
|
+
await assertCredentialFilesystemSafe(projectDir);
|
|
3187
3257
|
const isTrackedFile = options.isTrackedFile ?? ((targetPath) => gitPathMatches(projectDir, targetPath, "ls-files"));
|
|
3188
3258
|
const isIgnoredFile = options.isIgnoredFile ?? ((targetPath) => gitPathMatches(projectDir, targetPath, "check-ignore"));
|
|
3189
3259
|
if (await isTrackedFile(path)) {
|
|
@@ -3194,6 +3264,27 @@ async function assertLocalCredentialsPathSafe(projectDir, options = {}) {
|
|
|
3194
3264
|
}
|
|
3195
3265
|
return path;
|
|
3196
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
|
+
}
|
|
3197
3288
|
async function writeLocalCredentials(projectDir, credentials, options = {}) {
|
|
3198
3289
|
const path = await assertLocalCredentialsPathSafe(projectDir, options);
|
|
3199
3290
|
const parsed = siteplaneCredentialsSchema.parse(credentials);
|
|
@@ -3202,6 +3293,7 @@ async function writeLocalCredentials(projectDir, credentials, options = {}) {
|
|
|
3202
3293
|
return path;
|
|
3203
3294
|
}
|
|
3204
3295
|
async function readLocalCredentials(projectDir) {
|
|
3296
|
+
await assertCredentialFilesystemSafe(projectDir);
|
|
3205
3297
|
return siteplaneCredentialsSchema.parse(JSON.parse(await readFile6(join4(projectDir, ".siteplane/credentials.json"), "utf8")));
|
|
3206
3298
|
}
|
|
3207
3299
|
async function writeCredentialsAtomically(path, credentials) {
|
|
@@ -3237,8 +3329,467 @@ async function gitPathMatches(projectDir, path, command) {
|
|
|
3237
3329
|
}
|
|
3238
3330
|
|
|
3239
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";
|
|
3240
3345
|
import { join as join5 } from "path";
|
|
3241
|
-
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
|
|
3242
3793
|
async function initCommand(input) {
|
|
3243
3794
|
if (!input.setupToken.trim()) {
|
|
3244
3795
|
throw new Error("A Siteplane setup token is required.");
|
|
@@ -3246,47 +3797,64 @@ async function initCommand(input) {
|
|
|
3246
3797
|
if (!await detectNextProject(input.projectDir)) {
|
|
3247
3798
|
throw new Error("Siteplane init currently supports Next.js projects.");
|
|
3248
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}`);
|
|
3249
3809
|
const bootstrap = await bootstrapProjectConnection(input);
|
|
3250
|
-
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);
|
|
3251
3811
|
await acknowledgeBootstrapResponse(input);
|
|
3252
3812
|
return {
|
|
3253
3813
|
...paths,
|
|
3254
3814
|
nextSteps: [
|
|
3255
3815
|
"Run npx siteplane setup context and read the returned task.",
|
|
3256
3816
|
"Before source changes, briefly tell the user what Siteplane requested and what you will do.",
|
|
3257
|
-
"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."
|
|
3258
3818
|
]
|
|
3259
3819
|
};
|
|
3260
3820
|
}
|
|
3261
|
-
var siteSetupBootstrapDataSchema =
|
|
3262
|
-
credentialPurpose:
|
|
3263
|
-
apiBaseUrl:
|
|
3264
|
-
siteId:
|
|
3265
|
-
publicSiteKeyId:
|
|
3266
|
-
publicSiteKey:
|
|
3267
|
-
accessToken:
|
|
3268
|
-
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()
|
|
3269
3834
|
}).strict();
|
|
3270
|
-
var featureBootstrapDataSchema =
|
|
3271
|
-
credentialPurpose:
|
|
3272
|
-
apiBaseUrl:
|
|
3273
|
-
siteId:
|
|
3274
|
-
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)
|
|
3275
3840
|
}).strict();
|
|
3276
|
-
var bootstrapResponseSchema =
|
|
3277
|
-
ok:
|
|
3278
|
-
data:
|
|
3841
|
+
var bootstrapResponseSchema = z29.object({
|
|
3842
|
+
ok: z29.literal(true),
|
|
3843
|
+
data: z29.discriminatedUnion("credentialPurpose", [
|
|
3279
3844
|
siteSetupBootstrapDataSchema,
|
|
3280
3845
|
featureBootstrapDataSchema
|
|
3281
3846
|
])
|
|
3282
3847
|
}).strict();
|
|
3283
3848
|
async function bootstrapProjectConnection(input) {
|
|
3284
3849
|
const fetcher = input.fetcher ?? fetch;
|
|
3850
|
+
const existing = await access3(join7(input.projectDir, "siteplane.config.json")).then(() => readProjectConfig(input.projectDir), () => null);
|
|
3285
3851
|
const response = await fetcher(`${input.apiBaseUrl.replace(/\/$/, "")}/api/cli/setup/bootstrap`, {
|
|
3286
3852
|
method: "POST",
|
|
3853
|
+
signal: AbortSignal.timeout(2e4),
|
|
3287
3854
|
headers: createBootstrapHeaders(input),
|
|
3288
3855
|
body: JSON.stringify({
|
|
3289
3856
|
action: "bootstrap",
|
|
3857
|
+
...existing ? { expectedSiteId: existing.siteId, expectedRunId: existing.runId } : {},
|
|
3290
3858
|
agentClient: input.agentClient ?? "codex"
|
|
3291
3859
|
})
|
|
3292
3860
|
});
|
|
@@ -3301,6 +3869,7 @@ async function acknowledgeBootstrapResponse(input) {
|
|
|
3301
3869
|
const fetcher = input.fetcher ?? fetch;
|
|
3302
3870
|
const response = await fetcher(`${input.apiBaseUrl.replace(/\/$/, "")}/api/cli/setup/bootstrap`, {
|
|
3303
3871
|
method: "POST",
|
|
3872
|
+
signal: AbortSignal.timeout(2e4),
|
|
3304
3873
|
headers: createBootstrapHeaders(input),
|
|
3305
3874
|
body: JSON.stringify({ action: "acknowledge" })
|
|
3306
3875
|
});
|
|
@@ -3308,8 +3877,13 @@ async function acknowledgeBootstrapResponse(input) {
|
|
|
3308
3877
|
throw new Error("Siteplane setup acknowledgement failed.");
|
|
3309
3878
|
}
|
|
3310
3879
|
}
|
|
3311
|
-
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;
|
|
3312
3885
|
const credentialsPath = await writeLocalCredentials(input.projectDir, {
|
|
3886
|
+
...previousCredentials?.adminSession ? { adminSession: previousCredentials.adminSession } : {},
|
|
3313
3887
|
accessToken: bootstrap.accessToken,
|
|
3314
3888
|
siteRevalidationSecret: bootstrap.siteRevalidationSecret
|
|
3315
3889
|
}, {
|
|
@@ -3317,7 +3891,13 @@ async function persistSiteSetupBootstrap(input, bootstrap) {
|
|
|
3317
3891
|
...input.isTrackedFile ? { isTrackedFile: input.isTrackedFile } : {}
|
|
3318
3892
|
});
|
|
3319
3893
|
const configPath = await writeProjectConfig(input.projectDir, {
|
|
3320
|
-
|
|
3894
|
+
...previous ?? {},
|
|
3895
|
+
version: 2,
|
|
3896
|
+
appDirectory,
|
|
3897
|
+
runId: bootstrap.runId,
|
|
3898
|
+
packages: bootstrap.packages,
|
|
3899
|
+
taskContractVersion: "siteplane.setup-task.v2",
|
|
3900
|
+
adminProtocolVersion: 2,
|
|
3321
3901
|
apiBaseUrl: bootstrap.apiBaseUrl,
|
|
3322
3902
|
siteId: bootstrap.siteId,
|
|
3323
3903
|
publicSiteKeyId: bootstrap.publicSiteKeyId,
|
|
@@ -3327,7 +3907,7 @@ async function persistSiteSetupBootstrap(input, bootstrap) {
|
|
|
3327
3907
|
readLocalCredentials(input.projectDir),
|
|
3328
3908
|
readProjectConfig(input.projectDir)
|
|
3329
3909
|
]);
|
|
3330
|
-
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) {
|
|
3331
3911
|
throw new Error("Siteplane setup files could not be verified.");
|
|
3332
3912
|
}
|
|
3333
3913
|
return { configPath, credentialsPath };
|
|
@@ -3341,8 +3921,8 @@ async function persistFeatureBootstrap(input, bootstrap) {
|
|
|
3341
3921
|
throw new Error("The setup token belongs to a different Siteplane site.");
|
|
3342
3922
|
}
|
|
3343
3923
|
const credentialsPath = await writeLocalCredentials(input.projectDir, {
|
|
3344
|
-
|
|
3345
|
-
|
|
3924
|
+
...existingCredentials,
|
|
3925
|
+
accessToken: bootstrap.accessToken
|
|
3346
3926
|
}, {
|
|
3347
3927
|
...input.isIgnoredFile ? { isIgnoredFile: input.isIgnoredFile } : {},
|
|
3348
3928
|
...input.isTrackedFile ? { isTrackedFile: input.isTrackedFile } : {}
|
|
@@ -3352,7 +3932,7 @@ async function persistFeatureBootstrap(input, bootstrap) {
|
|
|
3352
3932
|
throw new Error("Siteplane credentials could not be verified.");
|
|
3353
3933
|
}
|
|
3354
3934
|
return {
|
|
3355
|
-
configPath:
|
|
3935
|
+
configPath: join7(input.projectDir, "siteplane.config.json"),
|
|
3356
3936
|
credentialsPath
|
|
3357
3937
|
};
|
|
3358
3938
|
}
|
|
@@ -3374,8 +3954,8 @@ function readErrorMessage(payload) {
|
|
|
3374
3954
|
}
|
|
3375
3955
|
|
|
3376
3956
|
// ../cli/dist/commands/scan.js
|
|
3377
|
-
import { readdir } from "fs/promises";
|
|
3378
|
-
import { extname, join as
|
|
3957
|
+
import { readdir as readdir2 } from "fs/promises";
|
|
3958
|
+
import { extname, join as join8 } from "path";
|
|
3379
3959
|
var defaultScanRoots = ["app", "src", "pages", "components", "lib"];
|
|
3380
3960
|
var excludedDirectories = /* @__PURE__ */ new Set([
|
|
3381
3961
|
".git",
|
|
@@ -3393,14 +3973,14 @@ async function resolveScanFilePaths(filePaths, options = {}) {
|
|
|
3393
3973
|
return filePaths;
|
|
3394
3974
|
}
|
|
3395
3975
|
const projectDir = options.projectDir ?? process.cwd();
|
|
3396
|
-
const discovered = await Promise.all(defaultScanRoots.map((root) => collectSourceFiles(
|
|
3976
|
+
const discovered = await Promise.all(defaultScanRoots.map((root) => collectSourceFiles(join8(projectDir, root))));
|
|
3397
3977
|
return [...new Set(discovered.flat())].sort();
|
|
3398
3978
|
}
|
|
3399
3979
|
async function collectSourceFiles(directory) {
|
|
3400
|
-
const entries = await
|
|
3980
|
+
const entries = await readdir2(directory, { withFileTypes: true }).catch(() => []);
|
|
3401
3981
|
const files = [];
|
|
3402
3982
|
for (const entry of entries) {
|
|
3403
|
-
const entryPath =
|
|
3983
|
+
const entryPath = join8(directory, entry.name);
|
|
3404
3984
|
if (entry.isDirectory()) {
|
|
3405
3985
|
if (!excludedDirectories.has(entry.name)) {
|
|
3406
3986
|
files.push(...await collectSourceFiles(entryPath));
|
|
@@ -3416,17 +3996,17 @@ async function collectSourceFiles(directory) {
|
|
|
3416
3996
|
|
|
3417
3997
|
// ../cli/dist/commands/site-setup.js
|
|
3418
3998
|
import { execFile as execFile2 } from "child_process";
|
|
3419
|
-
import { readFile as
|
|
3999
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
3420
4000
|
import { promisify as promisify2 } from "util";
|
|
3421
|
-
import { z as
|
|
4001
|
+
import { z as z30 } from "zod";
|
|
3422
4002
|
|
|
3423
4003
|
// ../cli/dist/scan/next-source-scan.js
|
|
3424
4004
|
import ts2 from "typescript";
|
|
3425
|
-
import { isAbsolute, relative as
|
|
4005
|
+
import { isAbsolute, relative as relative4 } from "path";
|
|
3426
4006
|
var eventPreviewHandlerMissingMessage = "Event preview fields must consume preview values with useSiteplanePreviewText from @siteplane/runtime-next/client or a siteplane:preview-value-applied listener.";
|
|
3427
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.";
|
|
3428
4008
|
var legacyEditableComponentPrefix = "Editable";
|
|
3429
|
-
var editableComponentNames = new Set(["Image", "Text", "LongText", "Link"].map((
|
|
4009
|
+
var editableComponentNames = new Set(["Image", "Text", "LongText", "Link"].map((name2) => `${legacyEditableComponentPrefix}${name2}`));
|
|
3430
4010
|
var functionTypeByName = {
|
|
3431
4011
|
text: "text",
|
|
3432
4012
|
longText: "longText",
|
|
@@ -3964,17 +4544,17 @@ function readObjectLiteral(expression) {
|
|
|
3964
4544
|
if (!ts2.isPropertyAssignment(property)) {
|
|
3965
4545
|
continue;
|
|
3966
4546
|
}
|
|
3967
|
-
const
|
|
3968
|
-
if (!
|
|
4547
|
+
const name2 = getPropertyName(property.name);
|
|
4548
|
+
if (!name2) {
|
|
3969
4549
|
continue;
|
|
3970
4550
|
}
|
|
3971
|
-
value[
|
|
4551
|
+
value[name2] = staticExpressionValue2(property.initializer);
|
|
3972
4552
|
}
|
|
3973
4553
|
return value;
|
|
3974
4554
|
}
|
|
3975
|
-
function getPropertyName(
|
|
3976
|
-
if (ts2.isIdentifier(
|
|
3977
|
-
return
|
|
4555
|
+
function getPropertyName(name2) {
|
|
4556
|
+
if (ts2.isIdentifier(name2) || ts2.isStringLiteral(name2)) {
|
|
4557
|
+
return name2.text;
|
|
3978
4558
|
}
|
|
3979
4559
|
return null;
|
|
3980
4560
|
}
|
|
@@ -4000,9 +4580,9 @@ function collectEventPreviewComponentPropUsages(node, eventPreviewAttrVariables,
|
|
|
4000
4580
|
});
|
|
4001
4581
|
}
|
|
4002
4582
|
}
|
|
4003
|
-
function getJsxAttributeName(
|
|
4004
|
-
if (ts2.isIdentifier(
|
|
4005
|
-
return
|
|
4583
|
+
function getJsxAttributeName(name2) {
|
|
4584
|
+
if (ts2.isIdentifier(name2)) {
|
|
4585
|
+
return name2.text;
|
|
4006
4586
|
}
|
|
4007
4587
|
return void 0;
|
|
4008
4588
|
}
|
|
@@ -4027,8 +4607,8 @@ function getFunctionComponentName(node) {
|
|
|
4027
4607
|
}
|
|
4028
4608
|
return void 0;
|
|
4029
4609
|
}
|
|
4030
|
-
function isComponentName(
|
|
4031
|
-
return /^[A-Z]/.test(
|
|
4610
|
+
function isComponentName(name2) {
|
|
4611
|
+
return /^[A-Z]/.test(name2);
|
|
4032
4612
|
}
|
|
4033
4613
|
function getAssignedVariableName(node) {
|
|
4034
4614
|
let current = node;
|
|
@@ -4076,7 +4656,7 @@ function inferRouteHint(filePath) {
|
|
|
4076
4656
|
return route ? `/${route}` : "/";
|
|
4077
4657
|
}
|
|
4078
4658
|
function toRepositoryRelativePosixPath(filePath, projectRoot) {
|
|
4079
|
-
const path = isAbsolute(filePath) ?
|
|
4659
|
+
const path = isAbsolute(filePath) ? relative4(projectRoot, filePath) : filePath;
|
|
4080
4660
|
return path.replaceAll("\\", "/");
|
|
4081
4661
|
}
|
|
4082
4662
|
function isPositionalFieldReference(value) {
|
|
@@ -4085,30 +4665,30 @@ function isPositionalFieldReference(value) {
|
|
|
4085
4665
|
|
|
4086
4666
|
// ../cli/dist/commands/site-setup.js
|
|
4087
4667
|
var execFileAsync2 = promisify2(execFile2);
|
|
4088
|
-
var safeErrorSchema =
|
|
4089
|
-
code:
|
|
4090
|
-
message:
|
|
4668
|
+
var safeErrorSchema = z30.object({
|
|
4669
|
+
code: z30.string().trim().min(1),
|
|
4670
|
+
message: z30.string().trim().min(1)
|
|
4091
4671
|
}).passthrough();
|
|
4092
|
-
var errorResponseSchema2 =
|
|
4093
|
-
var contextDataSchema =
|
|
4094
|
-
var contextResponseSchema =
|
|
4095
|
-
var applyDataSchema =
|
|
4096
|
-
status:
|
|
4097
|
-
runId:
|
|
4098
|
-
contractVersionId:
|
|
4099
|
-
fieldContractHash:
|
|
4100
|
-
editorDefinitionHash:
|
|
4101
|
-
fieldCount:
|
|
4102
|
-
routeCount:
|
|
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()
|
|
4103
4683
|
}).strict();
|
|
4104
|
-
var applyResponseSchema =
|
|
4105
|
-
var verifyDataSchema =
|
|
4106
|
-
status:
|
|
4107
|
-
runId:
|
|
4108
|
-
deploymentOrigin:
|
|
4109
|
-
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()
|
|
4110
4690
|
}).strict();
|
|
4111
|
-
var verifyResponseSchema =
|
|
4691
|
+
var verifyResponseSchema = z30.object({ ok: z30.literal(true), data: verifyDataSchema }).strict();
|
|
4112
4692
|
async function siteSetupContextCommand(input) {
|
|
4113
4693
|
const response = await postSiteSetup(input, "context", {});
|
|
4114
4694
|
const parsed = contextResponseSchema.safeParse(response.payload);
|
|
@@ -4118,9 +4698,19 @@ async function siteSetupContextCommand(input) {
|
|
|
4118
4698
|
return actionRequired(response.payload, "Siteplane setup context is unavailable.");
|
|
4119
4699
|
}
|
|
4120
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
|
+
}
|
|
4121
4711
|
const editorDefinition = editorDefinitionV1Schema.parse(input.editorDefinition);
|
|
4122
4712
|
const resolveSourceFiles = input.resolveSourceFiles ?? resolveScanFilePaths;
|
|
4123
|
-
const readSourceFile = input.readSourceFile ?? ((path) =>
|
|
4713
|
+
const readSourceFile = input.readSourceFile ?? ((path) => readFile9(path, "utf8"));
|
|
4124
4714
|
const scanSourceFiles = input.scanSourceFiles ?? scanNextSourceFilesToSiteFieldContract;
|
|
4125
4715
|
const paths = await resolveSourceFiles([], { projectDir: input.projectDir });
|
|
4126
4716
|
const files = await Promise.all(paths.map(async (filePath) => ({
|
|
@@ -4137,7 +4727,8 @@ async function siteSetupApplyCommand(input) {
|
|
|
4137
4727
|
}
|
|
4138
4728
|
const payload = siteSetupApplyInputSchema.parse({
|
|
4139
4729
|
fieldContract: scan.contract,
|
|
4140
|
-
editorDefinition
|
|
4730
|
+
editorDefinition,
|
|
4731
|
+
contentSelectionSummary: input.contentSelectionSummary
|
|
4141
4732
|
});
|
|
4142
4733
|
const response = await postSiteSetup(input, "apply", payload);
|
|
4143
4734
|
const parsed = applyResponseSchema.safeParse(response.payload);
|
|
@@ -4152,7 +4743,6 @@ async function siteSetupApplyCommand(input) {
|
|
|
4152
4743
|
message: "The applied field contract hash does not match the final local source scan."
|
|
4153
4744
|
};
|
|
4154
4745
|
}
|
|
4155
|
-
const config = await readProjectConfig(input.projectDir);
|
|
4156
4746
|
await writeProjectConfig(input.projectDir, {
|
|
4157
4747
|
...config,
|
|
4158
4748
|
fieldContractHash: parsed.data.data.fieldContractHash
|
|
@@ -4161,7 +4751,7 @@ async function siteSetupApplyCommand(input) {
|
|
|
4161
4751
|
}
|
|
4162
4752
|
async function siteSetupVerifyCommand(input) {
|
|
4163
4753
|
const resolveSourceFiles = input.resolveSourceFiles ?? resolveScanFilePaths;
|
|
4164
|
-
const readSourceFile = input.readSourceFile ?? ((path) =>
|
|
4754
|
+
const readSourceFile = input.readSourceFile ?? ((path) => readFile9(path, "utf8"));
|
|
4165
4755
|
const paths = await resolveSourceFiles([], { projectDir: input.projectDir });
|
|
4166
4756
|
const sources = await Promise.all(paths.map(readSourceFile));
|
|
4167
4757
|
if (!sources.some(hasMountedFieldRuntimeBridge)) {
|
|
@@ -4191,19 +4781,33 @@ async function postSiteSetup(input, operation, body) {
|
|
|
4191
4781
|
readProjectConfig(input.projectDir),
|
|
4192
4782
|
readLocalCredentials(input.projectDir)
|
|
4193
4783
|
]);
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
"
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
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
|
+
}
|
|
4207
4811
|
}
|
|
4208
4812
|
function actionRequired(payload, fallbackMessage) {
|
|
4209
4813
|
const parsed = errorResponseSchema2.safeParse(payload);
|
|
@@ -4240,15 +4844,870 @@ async function siteSetupActivityCommand(input) {
|
|
|
4240
4844
|
message: "Choose inspecting, integrating, deploying, verifying or repairing."
|
|
4241
4845
|
};
|
|
4242
4846
|
const response = await postSiteSetup(input, "activity", activity.data);
|
|
4243
|
-
const parsed =
|
|
4244
|
-
ok:
|
|
4245
|
-
data:
|
|
4847
|
+
const parsed = z30.object({
|
|
4848
|
+
ok: z30.literal(true),
|
|
4849
|
+
data: z30.object({ status: z30.enum(["recorded", "rate_limited"]) }).strict()
|
|
4246
4850
|
}).strict().safeParse(response.payload);
|
|
4247
4851
|
return response.ok && parsed.success ? parsed.data.data : actionRequired(response.payload, "Setup activity could not be recorded.");
|
|
4248
4852
|
}
|
|
4249
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_KEY: config.publicSiteKey,
|
|
5401
|
+
RUNTIME_API_BASE_URL: config.apiBaseUrl,
|
|
5402
|
+
SITEPLANE_SITE_ORIGIN: origin,
|
|
5403
|
+
SITEPLANE_SETUP_RUN_ID: config.runId,
|
|
5404
|
+
SITEPLANE_PORTAL_PATH: task.site.portalPath,
|
|
5405
|
+
SITEPLANE_BUILD_FIELD_CONTRACT_HASH: config.fieldContractHash,
|
|
5406
|
+
SITEPLANE_BUILD_REVISION: revision
|
|
5407
|
+
};
|
|
5408
|
+
const fingerprint = digest({
|
|
5409
|
+
publicBindings,
|
|
5410
|
+
siteId: config.siteId,
|
|
5411
|
+
publicKeyId: config.publicSiteKeyId,
|
|
5412
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
5413
|
+
packages: config.packages,
|
|
5414
|
+
bindings: [
|
|
5415
|
+
...Object.keys(publicBindings),
|
|
5416
|
+
"SITEPLANE_REVALIDATION_SECRET",
|
|
5417
|
+
"SITEPLANE_ADMIN_SESSION_SECRET"
|
|
5418
|
+
].sort(),
|
|
5419
|
+
adminSecretGeneration: generation,
|
|
5420
|
+
revalidationGeneration: task.revalidationGeneration,
|
|
5421
|
+
projectId: provider.projectId,
|
|
5422
|
+
teamId: provider.teamId,
|
|
5423
|
+
rootDirectory: provider.rootDirectory,
|
|
5424
|
+
nodeVersion: provider.nodeVersion,
|
|
5425
|
+
installCommand: provider.installCommand,
|
|
5426
|
+
buildCommand: provider.buildCommand,
|
|
5427
|
+
packageManagerSelection: provider.packageManagerSelection,
|
|
5428
|
+
declaredPackageManager: provider.declaredPackageManager,
|
|
5429
|
+
lockfileHash: local.lockfile.sha256
|
|
5430
|
+
});
|
|
5431
|
+
const save = async (next) => {
|
|
5432
|
+
const result = await deps.post({ ...input, deadline: operationDeadline }, "deployment", {
|
|
5433
|
+
action: "record",
|
|
5434
|
+
expectedOperationId: record?.operationId ?? null,
|
|
5435
|
+
record: next
|
|
5436
|
+
});
|
|
5437
|
+
const parsed = z33.object({
|
|
5438
|
+
ok: z33.literal(true),
|
|
5439
|
+
data: z33.object({ record: setupDeploymentRecordSchema }).strict()
|
|
5440
|
+
}).strict().safeParse(result.payload);
|
|
5441
|
+
if (!result.ok || !parsed.success || stableJsonStringify(parsed.data.data.record) !== stableJsonStringify(next))
|
|
5442
|
+
fail2("deployment_record_unconfirmed", "The setup deployment record was not acknowledged. Reload context before continuing; no deployment is blindly repeated.");
|
|
5443
|
+
record = parsed.data.data.record;
|
|
5444
|
+
if (next.phase === "planned")
|
|
5445
|
+
operationDeadline = Date.parse(next.deadline);
|
|
5446
|
+
};
|
|
5447
|
+
const fresh = () => ({
|
|
5448
|
+
version: 1,
|
|
5449
|
+
operationId: randomUUID6(),
|
|
5450
|
+
provider: "vercel",
|
|
5451
|
+
projectId: provider.projectId,
|
|
5452
|
+
teamId: provider.teamId,
|
|
5453
|
+
origin,
|
|
5454
|
+
deploymentId: null,
|
|
5455
|
+
revision,
|
|
5456
|
+
fieldContractHash: config.fieldContractHash,
|
|
5457
|
+
packages: config.packages,
|
|
5458
|
+
configurationFingerprint: fingerprint,
|
|
5459
|
+
adminSecretGeneration: generation,
|
|
5460
|
+
revalidationGeneration: task.revalidationGeneration,
|
|
5461
|
+
startedAt: new Date(deps.now()).toISOString(),
|
|
5462
|
+
environmentWrittenAt: null,
|
|
5463
|
+
deployRequestedAt: null,
|
|
5464
|
+
deadline: new Date(deps.now() + 9e5).toISOString(),
|
|
5465
|
+
phase: "planned",
|
|
5466
|
+
build: null
|
|
5467
|
+
});
|
|
5468
|
+
if (!record || record.configurationFingerprint !== fingerprint) {
|
|
5469
|
+
if (record && !["verified", "deployed", "failed"].includes(record.phase))
|
|
5470
|
+
fail2("previous_deployment_unresolved", "Resolve the previous provider operation before changing the source or configuration.");
|
|
5471
|
+
await save(fresh());
|
|
5472
|
+
}
|
|
5473
|
+
const progress = () => input.onProgress?.({
|
|
5474
|
+
phase: record.phase,
|
|
5475
|
+
deadline: record.deadline,
|
|
5476
|
+
deploymentId: record.deploymentId
|
|
5477
|
+
});
|
|
5478
|
+
progress();
|
|
5479
|
+
const values = {
|
|
5480
|
+
...publicBindings,
|
|
5481
|
+
SITEPLANE_REVALIDATION_SECRET: credentials.siteRevalidationSecret,
|
|
5482
|
+
...credentials.adminSession ? { SITEPLANE_ADMIN_SESSION_SECRET: credentials.adminSession.secret } : {}
|
|
5483
|
+
};
|
|
5484
|
+
const pending = [];
|
|
5485
|
+
for (const [key, value] of Object.entries(values)) {
|
|
5486
|
+
const item = owned(key);
|
|
5487
|
+
if (key === "SITEPLANE_ADMIN_SESSION_SECRET" && item && record.phase === "verified")
|
|
5488
|
+
continue;
|
|
5489
|
+
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;
|
|
5490
|
+
if (current !== value)
|
|
5491
|
+
pending.push({
|
|
5492
|
+
key,
|
|
5493
|
+
value,
|
|
5494
|
+
type: key.endsWith("SECRET") ? "encrypted" : "plain",
|
|
5495
|
+
target: ["production"],
|
|
5496
|
+
comment: key === "SITEPLANE_ADMIN_SESSION_SECRET" ? `Siteplane admin generation ${generation}` : "Managed by Siteplane setup"
|
|
5497
|
+
});
|
|
5498
|
+
}
|
|
5499
|
+
if (pending.length && record.phase !== "planned") {
|
|
5500
|
+
if (!["verified", "deployed", "failed"].includes(record.phase))
|
|
5501
|
+
fail2("environment_changed_during_deploy", "The environment changed during an unresolved deployment. Inspect that operation before retrying.");
|
|
5502
|
+
await save(fresh());
|
|
5503
|
+
}
|
|
5504
|
+
if (record.phase === "planned") {
|
|
5505
|
+
if (deps.now() >= Date.parse(record.deadline))
|
|
5506
|
+
fail2("provider_deadline_exceeded", "The setup provider deadline expired before configuration. Inspect its existing record before a new attempt.");
|
|
5507
|
+
for (const binding of pending)
|
|
5508
|
+
await api(`/v10/projects/${provider.projectId}/env?${scope}&upsert=true`, binding);
|
|
5509
|
+
env = await listEnv();
|
|
5510
|
+
for (const [key, value] of Object.entries(values)) {
|
|
5511
|
+
const item = owned(key);
|
|
5512
|
+
if (!item)
|
|
5513
|
+
fail2("environment_write_unconfirmed", "A Siteplane Production binding was not persisted; retry the same configuration.");
|
|
5514
|
+
const observed = z33.object({ key: z33.literal(key), value: z33.string() }).parse(await api(`/v1/projects/${provider.projectId}/env/${encodeURIComponent(item.id)}?${scope}`));
|
|
5515
|
+
if (observed.value !== value)
|
|
5516
|
+
fail2("environment_write_unconfirmed", "The provider readback does not match the authorized Siteplane value; no deployment was started.");
|
|
5517
|
+
}
|
|
5518
|
+
await save({
|
|
5519
|
+
...record,
|
|
5520
|
+
phase: "environment_written",
|
|
5521
|
+
environmentWrittenAt: new Date(deps.now()).toISOString()
|
|
5522
|
+
});
|
|
5523
|
+
}
|
|
5524
|
+
const detail = async (id) => deploymentSchema.parse(await api(`/v13/deployments/${encodeURIComponent(id)}?${scope}&withGitRepoInfo=true`));
|
|
5525
|
+
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);
|
|
5526
|
+
const discover = async () => {
|
|
5527
|
+
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`));
|
|
5528
|
+
const candidates = [];
|
|
5529
|
+
for (const item of result.deployments) {
|
|
5530
|
+
const found = await detail(item.uid);
|
|
5531
|
+
if (matches(found))
|
|
5532
|
+
candidates.push(found);
|
|
5533
|
+
}
|
|
5534
|
+
if (candidates.length > 1)
|
|
5535
|
+
fail2("deployment_ambiguous", "Multiple matching provider deployments exist. Select the existing intended operation before continuing; no additional build was started.");
|
|
5536
|
+
return candidates[0] ?? null;
|
|
5537
|
+
};
|
|
5538
|
+
let deployment = record.deploymentId ? await detail(record.deploymentId) : await discover();
|
|
5539
|
+
if (deployment && !matches(deployment))
|
|
5540
|
+
fail2("deployment_binding_conflict", "The stored deployment does not match this project, commit and environment generation.");
|
|
5541
|
+
if (!deployment && record.phase === "environment_written") {
|
|
5542
|
+
const status = await processCommand("git", ["status", "--porcelain=v1", "--untracked-files=all"], { cwd: local.repositoryRoot });
|
|
5543
|
+
const head = (await processCommand("git", ["rev-parse", "HEAD"], {
|
|
5544
|
+
cwd: local.repositoryRoot
|
|
5545
|
+
})).trim();
|
|
5546
|
+
if (status.trim() || head !== revision)
|
|
5547
|
+
fail2("source_changed_before_deploy", "The source changed after preflight. Commit only the intended source and rerun setup deploy.");
|
|
5548
|
+
await save({
|
|
5549
|
+
...record,
|
|
5550
|
+
phase: "deploy_requested",
|
|
5551
|
+
deployRequestedAt: new Date(deps.now()).toISOString()
|
|
5552
|
+
});
|
|
5553
|
+
try {
|
|
5554
|
+
if (provider.git) {
|
|
5555
|
+
const remote = (await processCommand("git", ["ls-remote", "origin", `refs/heads/${local.git.branch}`], { cwd: local.repositoryRoot })).trim().split(/\s/u)[0];
|
|
5556
|
+
if (remote !== revision)
|
|
5557
|
+
await processCommand("git", ["push", "origin", `HEAD:refs/heads/${local.git.branch}`], { cwd: local.repositoryRoot });
|
|
5558
|
+
else {
|
|
5559
|
+
const gitSource = {
|
|
5560
|
+
type: provider.git.type,
|
|
5561
|
+
ref: revision,
|
|
5562
|
+
...provider.git.repoId ? { repoId: provider.git.repoId } : {}
|
|
5563
|
+
};
|
|
5564
|
+
const body = JSON.stringify({
|
|
5565
|
+
name: provider.name,
|
|
5566
|
+
project: provider.projectId,
|
|
5567
|
+
target: "production",
|
|
5568
|
+
gitSource,
|
|
5569
|
+
meta: { siteplaneOperationId: record.operationId }
|
|
5570
|
+
});
|
|
5571
|
+
await processCommand("vercel", [
|
|
5572
|
+
"api",
|
|
5573
|
+
`/v13/deployments?${scope}`,
|
|
5574
|
+
"--method",
|
|
5575
|
+
"POST",
|
|
5576
|
+
"--input",
|
|
5577
|
+
"-",
|
|
5578
|
+
"--raw"
|
|
5579
|
+
], { cwd: local.projectDirectory, input: body, timeoutMs: 3e4 });
|
|
5580
|
+
}
|
|
5581
|
+
} else {
|
|
5582
|
+
const upload = await mkdtemp(join11(tmpdir(), "siteplane-setup-upload-"));
|
|
5583
|
+
try {
|
|
5584
|
+
await processCommand("git", [
|
|
5585
|
+
"clone",
|
|
5586
|
+
"--no-hardlinks",
|
|
5587
|
+
"--no-checkout",
|
|
5588
|
+
"--",
|
|
5589
|
+
local.repositoryRoot,
|
|
5590
|
+
upload
|
|
5591
|
+
], { cwd: local.repositoryRoot });
|
|
5592
|
+
await processCommand("git", ["checkout", "--detach", revision], {
|
|
5593
|
+
cwd: upload
|
|
5594
|
+
});
|
|
5595
|
+
await deps.writeBuildProvenance(upload, revision);
|
|
5596
|
+
await mkdir9(join11(upload, ".vercel"), {
|
|
5597
|
+
recursive: true,
|
|
5598
|
+
mode: 448
|
|
5599
|
+
});
|
|
5600
|
+
await writeFile6(join11(upload, ".vercel/project.json"), JSON.stringify({
|
|
5601
|
+
projectId: provider.projectId,
|
|
5602
|
+
orgId: provider.teamId
|
|
5603
|
+
}), { mode: 384 });
|
|
5604
|
+
await processCommand("vercel", [
|
|
5605
|
+
"deploy",
|
|
5606
|
+
"--yes",
|
|
5607
|
+
"--prod",
|
|
5608
|
+
"--no-wait",
|
|
5609
|
+
"--scope",
|
|
5610
|
+
provider.teamId,
|
|
5611
|
+
"--meta",
|
|
5612
|
+
`siteplaneOperationId=${record.operationId}`
|
|
5613
|
+
], { cwd: upload, timeoutMs: 12e4 });
|
|
5614
|
+
} finally {
|
|
5615
|
+
await rm5(upload, { recursive: true, force: true });
|
|
5616
|
+
}
|
|
5617
|
+
}
|
|
5618
|
+
} catch (error) {
|
|
5619
|
+
if (error instanceof SetupFailure && error.code === "provider_access_denied") {
|
|
5620
|
+
await save({
|
|
5621
|
+
...record,
|
|
5622
|
+
phase: "environment_written",
|
|
5623
|
+
deployRequestedAt: null
|
|
5624
|
+
});
|
|
5625
|
+
throw error;
|
|
5626
|
+
}
|
|
5627
|
+
if (!(error instanceof SetupFailure) || !["provider_timeout", "provider_transport_failed"].includes(error.code))
|
|
5628
|
+
throw error;
|
|
5629
|
+
}
|
|
5630
|
+
}
|
|
5631
|
+
let poll = 0;
|
|
5632
|
+
while (!deployment || !["READY", "ERROR", "CANCELED"].includes(deployment.readyState)) {
|
|
5633
|
+
progress();
|
|
5634
|
+
const remaining2 = Date.parse(record.deadline) - deps.now();
|
|
5635
|
+
if (remaining2 <= 0)
|
|
5636
|
+
fail2("provider_deadline_exceeded", "The 15-minute provider deadline expired. Inspect the recorded operation; setup will not start a second deployment on resume.");
|
|
5637
|
+
await deps.pause(Math.min(remaining2, Math.min(15e3, 2e3 * 2 ** Math.min(poll++, 3)) + Math.floor(Math.random() * 500)));
|
|
5638
|
+
deployment = deployment ? await detail(deployment.id) : await discover();
|
|
5639
|
+
if (deployment && !matches(deployment))
|
|
5640
|
+
fail2("deployment_binding_conflict", "The provider operation no longer matches this setup deployment.");
|
|
5641
|
+
}
|
|
5642
|
+
if (deployment.readyState !== "READY") {
|
|
5643
|
+
await save({ ...record, phase: "failed", deploymentId: deployment.id });
|
|
5644
|
+
fail2("provider_build_failed", "The existing provider build failed or was canceled. Fix its diagnosed package/build configuration before requesting another deployment.");
|
|
5645
|
+
}
|
|
5646
|
+
if (record.phase !== "verified")
|
|
5647
|
+
await save({
|
|
5648
|
+
...record,
|
|
5649
|
+
phase: "deployed",
|
|
5650
|
+
deploymentId: deployment.id
|
|
5651
|
+
});
|
|
5652
|
+
const logs = await processCommand("vercel", ["inspect", deployment.url, "--logs", "--scope", provider.teamId], { cwd: local.projectDirectory, includeStderr: true });
|
|
5653
|
+
const markers = [
|
|
5654
|
+
...logs.matchAll(/SITEPLANE_BUILD_PROOF=(\{[^\n]+\})/gu)
|
|
5655
|
+
].map((match) => buildProofSchema.parse(JSON.parse(match[1])));
|
|
5656
|
+
const proof = markers.find((item) => item.revision === revision && item.fieldContractHash === config.fieldContractHash);
|
|
5657
|
+
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)
|
|
5658
|
+
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.");
|
|
5659
|
+
const canonical = await detail(new URL(origin).hostname);
|
|
5660
|
+
if (canonical.id !== deployment.id)
|
|
5661
|
+
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.");
|
|
5662
|
+
const verified = await deps.verify({ ...input, deploymentUrl: origin });
|
|
5663
|
+
if (verified.status !== "ready_for_review")
|
|
5664
|
+
return {
|
|
5665
|
+
...verified,
|
|
5666
|
+
deadline: record.deadline,
|
|
5667
|
+
deploymentId: deployment.id
|
|
5668
|
+
};
|
|
5669
|
+
await save({
|
|
5670
|
+
...record,
|
|
5671
|
+
phase: "verified",
|
|
5672
|
+
deploymentId: deployment.id,
|
|
5673
|
+
build: {
|
|
5674
|
+
node: proof.node,
|
|
5675
|
+
packageManager: proof.packageManager,
|
|
5676
|
+
packageManagerVersion: proof.packageManagerVersion,
|
|
5677
|
+
lockfileHash: proof.lockfileHash
|
|
5678
|
+
}
|
|
5679
|
+
});
|
|
5680
|
+
return {
|
|
5681
|
+
status: "ready_for_review",
|
|
5682
|
+
deploymentId: deployment.id,
|
|
5683
|
+
origin,
|
|
5684
|
+
configurationFingerprint: fingerprint,
|
|
5685
|
+
deadline: record.deadline,
|
|
5686
|
+
build: record.build
|
|
5687
|
+
};
|
|
5688
|
+
} catch (error) {
|
|
5689
|
+
return {
|
|
5690
|
+
status: "action_required",
|
|
5691
|
+
code: error instanceof SetupFailure ? error.code : "setup_deployment_failed",
|
|
5692
|
+
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.",
|
|
5693
|
+
...record ? { deploymentId: record.deploymentId, deadline: record.deadline } : {}
|
|
5694
|
+
};
|
|
5695
|
+
}
|
|
5696
|
+
}
|
|
5697
|
+
|
|
4250
5698
|
// src/bin/run-siteplane.ts
|
|
4251
5699
|
async function runSiteplaneCli(args, dependencies = createDefaultDependencies()) {
|
|
5700
|
+
const writes = args[0] === "init" || args[0] === "setup" && ["apply", "prepare", "deploy"].includes(args[1] ?? "");
|
|
5701
|
+
const missingDeploy = args[0] === "setup" && args[1] === "deploy" && !await access6(join12(dependencies.cwd(), "siteplane.config.json")).then(
|
|
5702
|
+
() => true,
|
|
5703
|
+
() => false
|
|
5704
|
+
);
|
|
5705
|
+
return writes && !missingDeploy ? dependencies.withSetupLock(
|
|
5706
|
+
dependencies.cwd(),
|
|
5707
|
+
() => runSiteplaneCliUnlocked(args, dependencies)
|
|
5708
|
+
) : runSiteplaneCliUnlocked(args, dependencies);
|
|
5709
|
+
}
|
|
5710
|
+
async function runSiteplaneCliUnlocked(args, dependencies = createDefaultDependencies()) {
|
|
4252
5711
|
const [command, ...commandArgs] = args;
|
|
4253
5712
|
if (command === "init") {
|
|
4254
5713
|
const setupToken = readOption(commandArgs, "--setup-token");
|
|
@@ -4321,7 +5780,7 @@ async function runAnalyticsCommand(args, dependencies) {
|
|
|
4321
5780
|
siteId: projectConfig.siteId,
|
|
4322
5781
|
...readOptionalVercelAutomationBypassSecret(dependencies.env)
|
|
4323
5782
|
};
|
|
4324
|
-
const manifestPath =
|
|
5783
|
+
const manifestPath = join12(projectDir, "siteplane.analytics.json");
|
|
4325
5784
|
if (subcommand === "init") {
|
|
4326
5785
|
const rawPublicKey = await dependencies.commands.analyticsPublicKeyCommand({
|
|
4327
5786
|
config
|
|
@@ -4353,18 +5812,18 @@ async function runAnalyticsCommand(args, dependencies) {
|
|
|
4353
5812
|
projectDir
|
|
4354
5813
|
}
|
|
4355
5814
|
);
|
|
4356
|
-
const artifactFilePaths = [
|
|
5815
|
+
const artifactFilePaths = [join12(projectDir, ".siteplane/analytics.md")];
|
|
4357
5816
|
const cspFilePaths = await existingPaths([
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
5817
|
+
join12(projectDir, "next.config.js"),
|
|
5818
|
+
join12(projectDir, "next.config.mjs"),
|
|
5819
|
+
join12(projectDir, "next.config.ts"),
|
|
5820
|
+
join12(projectDir, "middleware.ts"),
|
|
5821
|
+
join12(projectDir, "src/middleware.ts")
|
|
4363
5822
|
]);
|
|
4364
5823
|
const check = await dependencies.commands.analyticsCheckCommand({
|
|
4365
5824
|
manifestPath,
|
|
4366
5825
|
sourceFilePaths,
|
|
4367
|
-
packageJsonPath:
|
|
5826
|
+
packageJsonPath: join12(projectDir, "package.json"),
|
|
4368
5827
|
cspFilePaths,
|
|
4369
5828
|
artifactFilePaths
|
|
4370
5829
|
});
|
|
@@ -4432,6 +5891,28 @@ async function runSetupCommand(args, dependencies) {
|
|
|
4432
5891
|
const connectionOptions = readOptionalVercelAutomationBypassSecret(
|
|
4433
5892
|
dependencies.env
|
|
4434
5893
|
);
|
|
5894
|
+
if (area === "deploy") {
|
|
5895
|
+
const result = await dependencies.commands.setupDeployCommand({
|
|
5896
|
+
projectDir,
|
|
5897
|
+
onProgress: (progress) => dependencies.stderr(JSON.stringify({ status: "waiting", ...progress }))
|
|
5898
|
+
});
|
|
5899
|
+
dependencies.stdout(JSON.stringify(result, null, 2));
|
|
5900
|
+
return { exitCode: result.status === "ready_for_review" ? 0 : 1 };
|
|
5901
|
+
}
|
|
5902
|
+
if (area === "preflight") {
|
|
5903
|
+
const result = await dependencies.commands.setupPreflightCommand({
|
|
5904
|
+
projectDir
|
|
5905
|
+
});
|
|
5906
|
+
dependencies.stdout(JSON.stringify(result, null, 2));
|
|
5907
|
+
return { exitCode: result.status === "ready" ? 0 : 1 };
|
|
5908
|
+
}
|
|
5909
|
+
if (area === "prepare") {
|
|
5910
|
+
const result = await dependencies.commands.setupPrepareCommand({
|
|
5911
|
+
projectDir
|
|
5912
|
+
});
|
|
5913
|
+
dependencies.stdout(JSON.stringify(result, null, 2));
|
|
5914
|
+
return { exitCode: result.status === "action_required" ? 1 : 0 };
|
|
5915
|
+
}
|
|
4435
5916
|
if (area === "context") {
|
|
4436
5917
|
const result = await dependencies.commands.siteSetupContextCommand({
|
|
4437
5918
|
projectDir,
|
|
@@ -4456,6 +5937,12 @@ async function runSetupCommand(args, dependencies) {
|
|
|
4456
5937
|
const result = await dependencies.commands.siteSetupApplyCommand({
|
|
4457
5938
|
projectDir,
|
|
4458
5939
|
editorDefinition,
|
|
5940
|
+
contentSelectionSummary: await dependencies.readTextFile(
|
|
5941
|
+
resolve4(
|
|
5942
|
+
projectDir,
|
|
5943
|
+
readRequiredOption(areaArgs, "--selection-summary-file")
|
|
5944
|
+
)
|
|
5945
|
+
),
|
|
4459
5946
|
...connectionOptions
|
|
4460
5947
|
});
|
|
4461
5948
|
dependencies.stdout(JSON.stringify(result, null, 2));
|
|
@@ -4471,13 +5958,15 @@ async function runSetupCommand(args, dependencies) {
|
|
|
4471
5958
|
dependencies.stdout(JSON.stringify(result, null, 2));
|
|
4472
5959
|
return { exitCode: result.status === "ready_for_review" ? 0 : 1 };
|
|
4473
5960
|
}
|
|
4474
|
-
dependencies.stdout(
|
|
5961
|
+
dependencies.stdout(
|
|
5962
|
+
"Usage: siteplane setup <preflight|prepare|context|activity|apply|deploy|verify>"
|
|
5963
|
+
);
|
|
4475
5964
|
return { exitCode: 1 };
|
|
4476
5965
|
}
|
|
4477
5966
|
async function runBookingCommand(args, dependencies) {
|
|
4478
5967
|
const [subcommand, ...subcommandArgs] = args;
|
|
4479
5968
|
const projectDir = dependencies.cwd();
|
|
4480
|
-
const manifestPath =
|
|
5969
|
+
const manifestPath = join12(projectDir, "siteplane.booking.json");
|
|
4481
5970
|
if (subcommand === "init") {
|
|
4482
5971
|
const siteId = readRequiredOption(subcommandArgs, "--site-id");
|
|
4483
5972
|
const siteKeyPrefix = readRequiredOption(
|
|
@@ -4556,7 +6045,7 @@ async function runBookingCommand(args, dependencies) {
|
|
|
4556
6045
|
resourceId: readOption(subcommandArgs, "--resource-id") ?? null,
|
|
4557
6046
|
startUtc: readRequiredOption(subcommandArgs, "--start-utc"),
|
|
4558
6047
|
customer,
|
|
4559
|
-
clientToken: readOption(subcommandArgs, "--client-token") ??
|
|
6048
|
+
clientToken: readOption(subcommandArgs, "--client-token") ?? randomUUID7(),
|
|
4560
6049
|
paymentChoice: "onsite"
|
|
4561
6050
|
}
|
|
4562
6051
|
});
|
|
@@ -4592,12 +6081,14 @@ function createDefaultDependencies() {
|
|
|
4592
6081
|
env: process.env,
|
|
4593
6082
|
stdout: (line) => console.log(line),
|
|
4594
6083
|
stderr: (line) => console.error(line),
|
|
6084
|
+
readTextFile: (path) => readFile13(path, "utf8"),
|
|
6085
|
+
withSetupLock,
|
|
4595
6086
|
readJsonFile: async (path) => JSON.parse(
|
|
4596
|
-
await
|
|
6087
|
+
await readFile13(resolve4(process.cwd(), path), "utf8")
|
|
4597
6088
|
),
|
|
4598
6089
|
readPackageVersion: async () => {
|
|
4599
6090
|
const packageJson = JSON.parse(
|
|
4600
|
-
await
|
|
6091
|
+
await readFile13(new URL("../../package.json", import.meta.url), "utf8")
|
|
4601
6092
|
);
|
|
4602
6093
|
return packageJson.version;
|
|
4603
6094
|
},
|
|
@@ -4606,6 +6097,9 @@ function createDefaultDependencies() {
|
|
|
4606
6097
|
siteSetupContextCommand,
|
|
4607
6098
|
siteSetupActivityCommand,
|
|
4608
6099
|
siteSetupVerifyCommand,
|
|
6100
|
+
setupPreflightCommand,
|
|
6101
|
+
setupPrepareCommand,
|
|
6102
|
+
setupDeployCommand,
|
|
4609
6103
|
initCommand,
|
|
4610
6104
|
bookingInitCommand,
|
|
4611
6105
|
bookingCheckCommand,
|
|
@@ -4666,7 +6160,7 @@ async function existingPaths(paths) {
|
|
|
4666
6160
|
const existing = [];
|
|
4667
6161
|
for (const path of paths) {
|
|
4668
6162
|
try {
|
|
4669
|
-
await
|
|
6163
|
+
await access6(path);
|
|
4670
6164
|
existing.push(path);
|
|
4671
6165
|
} catch {
|
|
4672
6166
|
}
|