siteplane 0.1.67 → 0.2.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/next.js CHANGED
@@ -1,4545 +1,159 @@
1
- // src/next.ts
2
- import { execFile as execFile4 } from "child_process";
3
- import { createHash as createHash5 } from "crypto";
4
- import { access as access8, readFile as readFile13 } from "fs/promises";
5
- import { dirname as dirname10, join as join14, resolve as resolve4 } from "path";
6
- import { promisify as promisify4 } from "util";
7
-
8
- // ../cli/dist/analytics/manifest.js
9
- import { mkdir, readFile, writeFile } from "fs/promises";
10
- import { dirname } from "path";
11
-
12
- // ../shared/dist/actors.js
13
- import { z } from "zod";
14
- var ACTOR_TYPES = ["workspace_member", "site_member", "agent", "setup_guest", "system"];
15
- var actorTypeSchema = z.enum(ACTOR_TYPES);
16
-
17
- // ../shared/dist/analytics-defaults.js
18
- var ANALYTICS_BATCH_LIMITS = {
19
- maxEventsPerRequest: 50,
20
- maxJsonBodyBytes: 64 * 1024,
21
- maxPropertiesJsonBytes: 8 * 1024
22
- };
23
- var ANALYTICS_RETENTION_DEFAULTS = {
24
- enabledRawEventRetentionDays: 30,
25
- higherTierRawEventRetentionDays: 90,
26
- maxRawEventRetentionDaysWithoutAdr: 180,
27
- diagnosticsRetentionDays: 30,
28
- rateLimitBucketRetentionDays: 2,
29
- lateEventWindowHours: 72
30
- };
31
-
32
- // ../shared/dist/analytics-booking.js
33
- import { z as z2 } from "zod";
34
- var bookingProviders = ["treatwell", "beautinda", "other"];
35
- var bookingAttributionModes = [
36
- "outbound_click_and_utm",
37
- "provider_redirect",
38
- "provider_reported",
39
- "manual_import",
40
- "verified_webhook"
41
- ];
42
- var bookingSignalSources = [
43
- "outbound_click",
44
- "provider_redirect",
45
- "provider_reported",
46
- "manual_import",
47
- "verified_webhook"
48
- ];
49
- var bookingAttributionConfidences = [
50
- "provider_redirect",
51
- "provider_reported",
52
- "manual_import",
53
- "verified_webhook"
54
- ];
55
- var bookingProviderSchema = z2.enum(bookingProviders);
56
- var bookingAttributionModeSchema = z2.enum(bookingAttributionModes);
57
- var bookingSignalSourceSchema = z2.enum(bookingSignalSources);
58
- var bookingAttributionConfidenceSchema = z2.enum(bookingAttributionConfidences);
59
- var bookingProviderHintSchema = z2.object({
60
- bookingProvider: bookingProviderSchema,
61
- bookingUrlHost: z2.string().min(1).max(253),
62
- bookingAttributionMode: bookingAttributionModeSchema,
63
- utmContent: z2.string().min(1).max(200).optional(),
64
- successCondition: z2.literal("redirect_status=succeeded").optional()
65
- }).superRefine((hint, context) => {
66
- if (hint.successCondition !== void 0 && hint.bookingAttributionMode !== "provider_redirect") {
67
- context.addIssue({
68
- code: "custom",
69
- message: "successCondition is allowed only for provider_redirect booking hints.",
70
- path: ["successCondition"]
71
- });
72
- }
73
- });
74
-
75
- // ../shared/dist/analytics-contract.js
76
- import { z as z3 } from "zod";
77
- var ANALYTICS_CONTRACT_VERSION = "analytics-contract.v1";
78
- var ANALYTICS_CONTRACT_HASH = "sha256:a91bddcc3c5e4196933b0335d03eaf41ff929cb736bdd694fe54d07ab52dada1";
79
- var ANALYTICS_IMPORT_SCHEMA_VERSION = "analytics-provider-import.v1";
80
- var analyticsImportTargetIdSchema = z3.string().min(1).max(120).regex(/^[a-zA-Z][a-zA-Z0-9]*(?:[._-][a-zA-Z0-9]+)*$/u);
81
- var analyticsProviderImportRowSchema = z3.object({
82
- targetId: analyticsImportTargetIdSchema,
83
- occurredAt: z3.string().datetime({ offset: true }),
84
- attributionConfidence: z3.enum(["provider_reported", "manual_import"]),
85
- providerEventId: z3.string().trim().min(1).max(160).optional(),
86
- idempotencyKey: z3.string().trim().min(1).max(160).optional()
87
- }).strict().superRefine((row, context) => {
88
- if (!row.providerEventId && !row.idempotencyKey) {
89
- context.addIssue({
90
- code: "custom",
91
- message: "Each import row requires providerEventId or idempotencyKey.",
92
- path: ["providerEventId"]
93
- });
94
- }
95
- });
96
- var analyticsProviderImportSchema = z3.object({
97
- schemaVersion: z3.literal(ANALYTICS_IMPORT_SCHEMA_VERSION),
98
- provider: z3.enum(["treatwell", "beautinda", "other"]),
99
- sourceName: z3.string().trim().min(1).max(160).optional(),
100
- rows: z3.array(analyticsProviderImportRowSchema).min(1).max(1e3)
101
- }).strict();
102
-
103
- // ../shared/dist/analytics-config.js
104
- import { z as z4 } from "zod";
105
- var ANALYTICS_CONFIG_LEASE_MS = 6e4;
106
- var analyticsMeasurementConfigSchema = z4.object({
107
- version: z4.literal(1),
108
- installationGeneration: z4.number().int().positive(),
109
- configRevision: z4.number().int().positive(),
110
- effectiveSiteMode: z4.enum(["disabled", "session_only", "full_analytics"]),
111
- measurementEnabled: z4.boolean(),
112
- issuedAt: z4.string().datetime({ offset: true }),
113
- expiresAt: z4.string().datetime({ offset: true })
114
- }).strict().superRefine((config, context) => {
115
- const lifetime = Date.parse(config.expiresAt) - Date.parse(config.issuedAt);
116
- if (lifetime <= 0 || lifetime > ANALYTICS_CONFIG_LEASE_MS) {
117
- context.addIssue({
118
- code: "custom",
119
- path: ["expiresAt"],
120
- message: "Measurement permission must expire within 60 seconds."
121
- });
122
- }
123
- if (config.measurementEnabled !== (config.effectiveSiteMode !== "disabled")) {
124
- context.addIssue({
125
- code: "custom",
126
- path: ["measurementEnabled"],
127
- message: "Measurement permission must match the effective site mode."
128
- });
129
- }
130
- });
131
-
132
- // ../shared/dist/analytics-events.js
133
- import { z as z5 } from "zod";
134
- var ANALYTICS_SCHEMA_VERSION = "analytics.v1";
135
- var analyticsSessionIdSchema = z5.string().regex(/^cpa_s_[a-f0-9]{32}$/u);
136
- var analyticsVisitorIdSchema = z5.string().regex(/^cpa_v_[a-f0-9]{32}$/u);
137
- var analyticsPersistedIdentityHashSchema = z5.string().regex(/^[a-f0-9]{64}$/u);
138
- var analyticsEventNames = [
139
- "page_view",
140
- "section_view",
141
- "cta_click",
142
- "outbound_click",
143
- "email_click",
144
- "phone_click",
145
- "file_download",
146
- "form_start",
147
- "form_submit",
148
- "form_error",
149
- "lead_created",
150
- "checkout_started",
151
- "conversion",
152
- "checkout_completed",
153
- "payment_succeeded",
154
- "subscription_started",
155
- "purchase_completed",
156
- "trial_started"
157
- ];
158
- var serverOnlyAnalyticsEvents = [
159
- "checkout_completed",
160
- "payment_succeeded",
161
- "subscription_started",
162
- "purchase_completed",
163
- "trial_started"
164
- ];
165
- var analyticsEventSources = [
166
- "browser",
167
- "server",
168
- "test",
169
- "editor_preview",
170
- "system"
171
- ];
172
- var analyticsIngestionChannels = [
173
- "browser_collector",
174
- "server_sdk",
175
- "provider_endpoint",
176
- "provider_import",
177
- "test",
178
- "editor_preview",
179
- "system_job"
180
- ];
181
- var analyticsTargetTypes = [
182
- "cta",
183
- "form",
184
- "section",
185
- "file",
186
- "link",
187
- "conversion",
188
- "server_conversion"
189
- ];
190
- var analyticsDataAttributeEvents = [
191
- "cta_click",
192
- "outbound_click",
193
- "email_click",
194
- "phone_click",
195
- "file_download",
196
- "section_view",
197
- "checkout_started",
198
- "form"
199
- ];
200
- var analyticsDeviceTypes = [
201
- "desktop",
202
- "tablet",
203
- "mobile",
204
- "unknown"
205
- ];
206
- var analyticsBotClassifications = [
207
- "normal",
208
- "known_bot",
209
- "suspicious",
210
- "invalid"
211
- ];
212
- var analyticsValidityStatuses = [
213
- "accepted",
214
- "rejected",
215
- "ignored"
216
- ];
217
- var analyticsEventNameSchema = z5.enum(analyticsEventNames);
218
- var serverOnlyAnalyticsEventSchema = z5.enum(serverOnlyAnalyticsEvents);
219
- var analyticsEventSourceSchema = z5.enum(analyticsEventSources);
220
- var analyticsIngestionChannelSchema = z5.enum(analyticsIngestionChannels);
221
- var analyticsTargetTypeSchema = z5.enum(analyticsTargetTypes);
222
- var analyticsDataAttributeEventSchema = z5.enum(analyticsDataAttributeEvents);
223
- var analyticsDeviceTypeSchema = z5.enum(analyticsDeviceTypes);
224
- var analyticsBotClassificationSchema = z5.enum(analyticsBotClassifications);
225
- var analyticsValidityStatusSchema = z5.enum(analyticsValidityStatuses);
226
- var analyticsPropertiesSchema = z5.record(z5.string(), z5.unknown());
227
- var analyticsTimestampSchema = z5.string().datetime({ offset: true });
228
- var analyticsBrowserEventSchema = z5.object({
229
- eventId: z5.string().min(1).max(160),
230
- installationGeneration: z5.number().int().positive().optional(),
231
- measurementMode: z5.enum(["session_only", "full_analytics"]).optional(),
232
- eventName: analyticsEventNameSchema,
233
- eventSource: analyticsEventSourceSchema.optional().default("browser"),
234
- targetId: z5.string().min(1).max(160).optional(),
235
- targetType: analyticsTargetTypeSchema.optional(),
236
- visitorId: analyticsVisitorIdSchema.optional(),
237
- sessionId: analyticsSessionIdSchema,
238
- idempotencyKey: z5.string().min(1).max(240).optional(),
239
- occurredAt: analyticsTimestampSchema,
240
- pageUrl: z5.string().url().max(2048).optional(),
241
- pagePath: z5.string().min(1).max(1024).optional(),
242
- routeTemplate: z5.string().min(1).max(1024).optional(),
243
- referrer: z5.string().url().max(2048).optional(),
244
- utmSource: z5.string().min(1).max(200).optional(),
245
- utmMedium: z5.string().min(1).max(200).optional(),
246
- utmCampaign: z5.string().min(1).max(200).optional(),
247
- utmContent: z5.string().min(1).max(200).optional(),
248
- utmTerm: z5.string().min(1).max(200).optional(),
249
- deviceType: analyticsDeviceTypeSchema.optional(),
250
- browserName: z5.string().min(1).max(120).optional(),
251
- osName: z5.string().min(1).max(120).optional(),
252
- country: z5.string().min(1).max(80).optional(),
253
- region: z5.string().min(1).max(120).optional(),
254
- properties: analyticsPropertiesSchema.optional().default({})
255
- }).superRefine((event, context) => {
256
- if (isServerOnlyAnalyticsEvent(event.eventName)) {
257
- context.addIssue({
258
- code: "custom",
259
- message: "Browser analytics payloads cannot contain server-only events.",
260
- path: ["eventName"]
261
- });
262
- }
263
- if (event.eventSource === "server" || event.eventSource === "system") {
264
- context.addIssue({
265
- code: "custom",
266
- message: "Browser analytics payloads cannot use server/system sources.",
267
- path: ["eventSource"]
268
- });
269
- }
270
- if (event.targetType === "server_conversion") {
271
- context.addIssue({
272
- code: "custom",
273
- message: "Browser analytics payloads cannot use server conversion targets.",
274
- path: ["targetType"]
275
- });
276
- }
277
- });
278
- var analyticsBrowserEventPayloadSchema = z5.object({
279
- siteKey: z5.string().min(1),
280
- schemaVersion: z5.literal(ANALYTICS_SCHEMA_VERSION),
281
- mode: z5.enum(["live", "test"]).optional().default("live"),
282
- events: z5.array(analyticsBrowserEventSchema).min(1).max(50)
283
- });
284
- var analyticsNormalizedEventBaseSchema = z5.object({
285
- site_id: z5.string().uuid(),
286
- event_id: z5.string().min(1).max(160),
287
- schema_version: z5.literal(ANALYTICS_SCHEMA_VERSION),
288
- event_name: analyticsEventNameSchema,
289
- event_source: analyticsEventSourceSchema,
290
- ingestion_channel: analyticsIngestionChannelSchema,
291
- target_id: z5.string().min(1).max(160).nullable().optional(),
292
- target_type: analyticsTargetTypeSchema.nullable().optional(),
293
- dedupe_key: z5.string().regex(/^analytics_dedupe_v1_[a-f0-9]{64}$/u).nullable().optional(),
294
- idempotency_key: z5.string().min(1).max(240).nullable().optional(),
295
- bot_classification: analyticsBotClassificationSchema,
296
- validity_status: analyticsValidityStatusSchema,
297
- rejection_reason: z5.string().min(1).max(240).nullable().optional(),
298
- occurred_at: analyticsTimestampSchema,
299
- received_at: analyticsTimestampSchema,
300
- page_url: z5.string().url().max(2048).nullable().optional(),
301
- page_path: z5.string().min(1).max(1024).nullable().optional(),
302
- route_template: z5.string().min(1).max(1024).nullable().optional(),
303
- referrer: z5.string().url().max(2048).nullable().optional(),
304
- utm_source: z5.string().min(1).max(200).nullable().optional(),
305
- utm_medium: z5.string().min(1).max(200).nullable().optional(),
306
- utm_campaign: z5.string().min(1).max(200).nullable().optional(),
307
- utm_content: z5.string().min(1).max(200).nullable().optional(),
308
- utm_term: z5.string().min(1).max(200).nullable().optional(),
309
- country: z5.string().min(1).max(80).nullable().optional(),
310
- region: z5.string().min(1).max(120).nullable().optional(),
311
- device_type: analyticsDeviceTypeSchema,
312
- browser_name: z5.string().min(1).max(120).nullable().optional(),
313
- os_name: z5.string().min(1).max(120).nullable().optional(),
314
- provider: z5.string().min(1).max(120).nullable().optional(),
315
- provider_event_id: z5.string().min(1).max(240).nullable().optional(),
316
- properties: analyticsPropertiesSchema
317
- });
318
- var analyticsRawNormalizedEventSchema = analyticsNormalizedEventBaseSchema.extend({
319
- visitor_id: analyticsVisitorIdSchema.nullable().optional(),
320
- session_id: analyticsSessionIdSchema.nullable().optional()
321
- });
322
- var analyticsNormalizedEventSchema = analyticsNormalizedEventBaseSchema.extend({
323
- visitor_id: analyticsPersistedIdentityHashSchema.nullable().optional(),
324
- session_id: analyticsPersistedIdentityHashSchema.nullable().optional()
325
- });
326
- function isServerOnlyAnalyticsEvent(eventName) {
327
- return serverOnlyAnalyticsEvents.includes(eventName);
328
- }
329
-
330
- // ../shared/dist/analytics-manifest.js
331
- import { z as z6 } from "zod";
332
- var analyticsTargetIdSchema = z6.string().min(1).max(120).regex(/^[a-zA-Z][a-zA-Z0-9]*(?:[._-][a-zA-Z0-9]+)*$/u, {
333
- message: "Analytics target IDs must be stable identifiers without whitespace or visible copy."
334
- });
335
- var analyticsManifestRecordSchema = z6.record(z6.string(), z6.unknown());
336
- var analyticsFormPurposes = [
337
- "contact",
338
- "lead",
339
- "newsletter",
340
- "search",
341
- "login",
342
- "other"
343
- ];
344
- var analyticsFormPurposeSchema = z6.enum(analyticsFormPurposes);
345
- var analyticsManifestRouteSchema = z6.object({
346
- path: z6.string().min(1).max(1024),
347
- pageType: z6.string().min(1).max(120).optional(),
348
- label: z6.string().min(1).max(200).optional()
349
- }).passthrough();
350
- var analyticsManifestTargetSchema = z6.object({
351
- id: analyticsTargetIdSchema,
352
- type: analyticsTargetTypeSchema,
353
- event: analyticsEventNameSchema.optional(),
354
- events: z6.array(analyticsEventNameSchema).min(1).max(20).optional(),
355
- route: z6.string().min(1).max(1024).optional(),
356
- label: z6.string().min(1).max(200).optional(),
357
- description: z6.string().min(1).max(1e3).optional(),
358
- autoActive: z6.boolean().optional(),
359
- sourceHints: analyticsManifestRecordSchema.optional()
360
- }).passthrough().superRefine((target, context) => {
361
- if (target.event === void 0 && target.events === void 0) {
362
- context.addIssue({
363
- code: "custom",
364
- message: "Analytics targets require event or events.",
365
- path: ["event"]
366
- });
367
- }
368
- if (target.event !== void 0 && target.events !== void 0) {
369
- context.addIssue({
370
- code: "custom",
371
- message: "Analytics targets must use event or events, never both.",
372
- path: ["events"]
373
- });
374
- }
375
- if (target.type === "form" && !analyticsFormPurposeSchema.safeParse(target.sourceHints?.formPurpose).success) {
376
- context.addIssue({
377
- code: "custom",
378
- message: "Analytics form targets require a typed sourceHints.formPurpose.",
379
- path: ["sourceHints", "formPurpose"]
380
- });
381
- }
382
- });
383
- var analyticsManifestSchema = z6.object({
384
- schemaVersion: z6.literal(ANALYTICS_SCHEMA_VERSION),
385
- siteId: z6.string().min(1),
386
- siteKeyRef: z6.string().regex(/^env:[A-Z][A-Z0-9_]*$/u, {
387
- message: "siteKeyRef must be an environment-variable reference."
388
- }),
389
- siteKeyLookup: z6.string().regex(/^pk_siteplane_[A-Za-z0-9_-]{16}$/),
390
- routes: z6.array(analyticsManifestRouteSchema),
391
- targets: z6.array(analyticsManifestTargetSchema),
392
- metadata: analyticsManifestRecordSchema.optional(),
393
- siteKey: z6.string().optional(),
394
- secretKey: z6.string().optional(),
395
- rawKey: z6.string().optional(),
396
- token: z6.string().optional(),
397
- accessToken: z6.string().optional()
398
- }).passthrough().superRefine((manifest, context) => {
399
- const targetIds = /* @__PURE__ */ new Set();
400
- for (const [index, target] of manifest.targets.entries()) {
401
- if (targetIds.has(target.id)) {
402
- context.addIssue({
403
- code: "custom",
404
- message: `Duplicate analytics target ID: ${target.id}`,
405
- path: ["targets", index, "id"]
406
- });
407
- }
408
- targetIds.add(target.id);
409
- }
410
- for (const issue of findUnsafeSecretReferences(manifest)) {
411
- context.addIssue({
412
- code: "custom",
413
- message: "Raw secret material is not allowed in analytics manifests.",
414
- path: issue
415
- });
416
- }
417
- });
418
- var secretValuePattern = /(?:\bsk_(?:live|test)_|\bcpb_|\bpmt_(?:pc|setup|cli)_|\bservice_role\b|bearer\s+[a-z0-9._~-]+)/iu;
419
- function findUnsafeSecretReferences(value, path = []) {
420
- if (Array.isArray(value)) {
421
- return value.flatMap((item, index) => findUnsafeSecretReferences(item, [...path, index]));
422
- }
423
- if (!isPlainRecord(value)) {
424
- return [];
425
- }
426
- return Object.entries(value).flatMap(([key, childValue]) => {
427
- const childPath = [...path, key];
428
- const referenceLikeKey = /(?:ref|lookup|prefix)$/iu.test(key);
429
- const unsafeHere = referenceLikeKey && typeof childValue === "string" && secretValuePattern.test(childValue);
430
- return [
431
- ...unsafeHere ? [childPath] : [],
432
- ...findUnsafeSecretReferences(childValue, childPath)
433
- ];
434
- });
435
- }
436
- function isPlainRecord(value) {
437
- return typeof value === "object" && value !== null;
438
- }
439
-
440
- // ../shared/dist/analytics-metrics.js
441
- import { z as z7 } from "zod";
442
- var analyticsVisitorQualities = ["exact", "estimated"];
443
- var analyticsVisitorQualitySchema = z7.enum(analyticsVisitorQualities);
444
- var analyticsKpiKeys = [
445
- "pageViews",
446
- "sessions",
447
- "visitors",
448
- "estimatedVisitors",
449
- "inquiries",
450
- "leads",
451
- "conversions",
452
- "conversionRate",
453
- "inquiryRate"
454
- ];
455
- var analyticsKpiKeySchema = z7.enum(analyticsKpiKeys);
456
-
457
- // ../shared/dist/analytics-privacy.js
458
- var serverPropertySchemas = {
459
- lead_created: {
460
- conversionId: "identifier",
461
- formId: "identifier",
462
- formType: "enum",
463
- customerRef: "identifier"
464
- },
465
- conversion: {
466
- conversionId: "identifier",
467
- conversionType: "enum",
468
- attributionConfidence: "enum",
469
- bookingSignalSource: "enum",
470
- bookingProvider: "enum",
471
- redirectStatus: "enum",
472
- importId: "identifier"
473
- },
474
- checkout_completed: revenuePropertySchema(),
475
- payment_succeeded: revenuePropertySchema(),
476
- subscription_started: revenuePropertySchema(),
477
- purchase_completed: revenuePropertySchema(),
478
- trial_started: revenuePropertySchema()
479
- };
480
- function revenuePropertySchema() {
481
- return {
482
- conversionId: "identifier",
483
- checkoutId: "identifier",
484
- customerRef: "identifier",
485
- amount: "amount",
486
- currency: "enum",
487
- planId: "identifier",
488
- productId: "identifier"
489
- };
490
- }
491
-
492
- // ../shared/dist/assets.js
493
- import { z as z8 } from "zod";
494
- var ASSET_STAGES = ["staging", "final", "deleted"];
495
- var ASSET_VISIBILITIES = ["private", "public", "deleted"];
496
- var ASSET_USAGE_ENTITY_TYPES = [
497
- "branding",
498
- "draft",
499
- "change_set",
500
- "published_value"
501
- ];
502
- var assetStageSchema = z8.enum(ASSET_STAGES);
503
- var assetVisibilitySchema = z8.enum(ASSET_VISIBILITIES);
504
- var assetUsageEntityTypeSchema = z8.enum(ASSET_USAGE_ENTITY_TYPES);
505
- var brandingAccentColorSchema = z8.string().regex(/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/);
506
-
507
- // ../shared/dist/booking-contracts.js
508
- import { z as z9 } from "zod";
509
- var bookingStatuses = [
510
- "pending_payment",
511
- "confirmed",
512
- "payment_expired",
513
- "cancelled",
514
- "no_show"
515
- ];
516
- var bookingPaymentStatuses = [
517
- "payment_not_required",
518
- "checkout_open",
519
- "processing",
520
- "succeeded",
521
- "failed",
522
- "cancelled",
523
- "expired"
524
- ];
525
- var bookingAccountReadinessStatuses = [
526
- "not_started",
527
- "setup_required",
528
- "under_review",
529
- "payments_ready",
530
- "payouts_restricted",
531
- "payments_restricted",
532
- "deactivated"
533
- ];
534
- var bookingRefundStatuses = [
535
- "none",
536
- "pending",
537
- "partially_refunded",
538
- "refunded",
539
- "failed"
540
- ];
541
- var bookingDisputeStatuses = [
542
- "none",
543
- "needs_response",
544
- "under_review",
545
- "won",
546
- "lost"
547
- ];
548
- var bookingPaymentModes = [
549
- "onsite_only",
550
- "online_required",
551
- "customer_choice"
552
- ];
553
- var bookingPaymentCollectionTypes = [
554
- "full_amount",
555
- "deposit_percent"
556
- ];
557
- var bookingResourceKinds = [
558
- "person",
559
- "room",
560
- "equipment",
561
- "other"
562
- ];
563
- var bookingStatusSchema = z9.enum(bookingStatuses);
564
- var bookingPaymentStatusSchema = z9.enum(bookingPaymentStatuses);
565
- var bookingAccountReadinessStatusSchema = z9.enum(bookingAccountReadinessStatuses);
566
- var bookingRefundStatusSchema = z9.enum(bookingRefundStatuses);
567
- var bookingDisputeStatusSchema = z9.enum(bookingDisputeStatuses);
568
- var bookingPaymentModeSchema = z9.enum(bookingPaymentModes);
569
- var bookingPaymentCollectionTypeSchema = z9.enum(bookingPaymentCollectionTypes);
570
- var bookingPaymentCollectionSchema = z9.discriminatedUnion("type", [
571
- z9.object({ type: z9.literal("full_amount") }).strict(),
572
- z9.object({
573
- type: z9.literal("deposit_percent"),
574
- depositPercent: z9.number().int().min(10).max(90)
575
- }).strict()
576
- ]);
577
- var bookingPaymentChoices = ["onsite", "online"];
578
- var bookingPaymentChoiceSchema = z9.enum(bookingPaymentChoices);
579
- var bookingPaymentSnapshotSchema = z9.object({
580
- snapshotId: z9.string().uuid(),
581
- snapshotVersion: z9.number().int().positive(),
582
- currency: z9.literal("EUR"),
583
- totalAmountCents: z9.number().int().min(0),
584
- onlineAmountCents: z9.number().int().min(0),
585
- onsiteRemainderCents: z9.number().int().min(0),
586
- maxStripeRefundAmountCents: z9.number().int().min(0)
587
- }).strict().superRefine((snapshot, context) => {
588
- if (snapshot.onlineAmountCents + snapshot.onsiteRemainderCents !== snapshot.totalAmountCents) {
589
- context.addIssue({
590
- code: "custom",
591
- message: "Payment snapshot amounts must add up to the total amount."
592
- });
593
- }
594
- if (snapshot.maxStripeRefundAmountCents > snapshot.onlineAmountCents) {
595
- context.addIssue({
596
- code: "custom",
597
- message: "Stripe refund basis cannot exceed the online amount."
598
- });
599
- }
600
- });
601
- var bookingServiceVariantSchema = z9.object({
602
- id: z9.string().uuid(),
603
- name: z9.string().trim().min(1).max(120),
604
- durationMinutes: z9.number().int().min(1).max(1440),
605
- priceCents: z9.number().int().min(0).nullable()
606
- }).strict();
607
- var bookingServiceVariantsSchema = z9.array(bookingServiceVariantSchema).min(1).max(20);
608
- var isoDateSchema = z9.string().regex(/^\d{4}-\d{2}-\d{2}$/);
609
- var utcDateTimeSchema = z9.string().datetime();
610
- var bookingLocalTimeSchema = z9.string().regex(/^(?:[01]\d|2[0-3]):[0-5]\d$/);
611
- var bookingAvailabilityIntervalsSchema = z9.array(z9.object({
612
- start: bookingLocalTimeSchema,
613
- end: bookingLocalTimeSchema
614
- }).strict().refine((interval) => interval.end > interval.start)).superRefine((intervals, context) => {
615
- const sorted = [...intervals].sort((left, right) => left.start.localeCompare(right.start));
616
- for (let index = 1; index < sorted.length; index += 1) {
617
- if (sorted[index].start < sorted[index - 1].end) {
618
- context.addIssue({
619
- code: "custom",
620
- message: "Booking availability intervals must not overlap."
621
- });
622
- return;
623
- }
624
- }
625
- });
626
- var bookingPublicConfigSchema = z9.object({
627
- site: z9.object({
628
- displayName: z9.string().trim().min(1),
629
- timezone: z9.string().trim().min(1),
630
- locale: z9.string().trim().min(2),
631
- bookingPageSlug: z9.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).min(2).max(64),
632
- privacyUrl: z9.string().url().nullable()
633
- }),
634
- branding: z9.object({
635
- logoUrl: z9.string().url().nullable(),
636
- accentColor: z9.string().trim().min(1).nullable()
637
- }),
638
- settings: z9.object({
639
- slotIntervalMinutes: z9.number().int().positive(),
640
- minLeadMinutes: z9.number().int().min(0),
641
- minCancelLeadMinutes: z9.number().int().min(0),
642
- maxAdvanceDays: z9.number().int().min(1),
643
- requirePhone: z9.boolean(),
644
- collectNote: z9.boolean(),
645
- confirmationMessage: z9.string().nullable()
646
- }),
647
- /** Freitexte des Salons, im Kassen-Schritt vor der Bestaetigung sichtbar. */
648
- policies: z9.object({
649
- cancellation: z9.string().nullable(),
650
- lateArrival: z9.string().nullable()
651
- }),
652
- payment: z9.object({
653
- mode: bookingPaymentModeSchema,
654
- collection: bookingPaymentCollectionSchema,
655
- onlinePaymentsReady: z9.boolean()
656
- }).strict(),
657
- serviceCategories: z9.array(z9.object({
658
- id: z9.string().uuid(),
659
- name: z9.string().trim().min(1),
660
- sortOrder: z9.number().int()
661
- })),
662
- serviceSubcategories: z9.array(z9.object({
663
- id: z9.string().uuid(),
664
- categoryId: z9.string().uuid(),
665
- name: z9.string().trim().min(1),
666
- sortOrder: z9.number().int()
667
- })),
668
- services: z9.array(z9.object({
669
- id: z9.string().trim().min(1),
670
- name: z9.string().trim().min(1),
671
- durationMinutes: z9.number().int().positive(),
672
- priceCents: z9.number().int().min(0).nullable(),
673
- currency: z9.string().regex(/^[A-Z]{3}$/),
674
- description: z9.string().nullable(),
675
- categoryId: z9.string().uuid(),
676
- subcategoryId: z9.string().uuid().nullable(),
677
- variants: bookingServiceVariantsSchema,
678
- allowsAnyResource: z9.boolean(),
679
- sortOrder: z9.number().int()
680
- })),
681
- resources: z9.array(z9.object({
682
- id: z9.string().trim().min(1),
683
- name: z9.string().trim().min(1),
684
- kind: z9.enum(bookingResourceKinds)
685
- })),
686
- serviceResources: z9.array(z9.object({
687
- serviceId: z9.string().trim().min(1),
688
- resourceId: z9.string().trim().min(1)
689
- }))
690
- });
691
- var bookingItemSchema = z9.object({
692
- serviceId: z9.string().trim().min(1),
693
- variantId: z9.string().trim().min(1)
694
- }).strict();
695
- var bookingItemsSchema = z9.array(bookingItemSchema).min(1).max(10);
696
- function parseBookingItemsParam(value) {
697
- const items = value.split(",").map((entry) => entry.trim()).filter((entry) => entry !== "").map((entry) => {
698
- const [serviceId, variantId, ...rest] = entry.split(":");
699
- return serviceId && variantId && rest.length === 0 ? { serviceId, variantId } : null;
700
- });
701
- return items.every((item) => item !== null) ? items : null;
702
- }
703
- var bookingSlotsQuerySchema = z9.object({
704
- items: z9.preprocess((value) => typeof value === "string" ? parseBookingItemsParam(value) : value, bookingItemsSchema),
705
- resourceId: z9.string().trim().min(1).optional(),
706
- fromDate: isoDateSchema,
707
- toDate: isoDateSchema
708
- });
709
- var bookingSlotsResponseSchema = z9.object({
710
- timezone: z9.string().trim().min(1),
711
- items: bookingItemsSchema,
712
- // Reine Behandlungsdauer ohne Puffer: das UI zeigt damit die Terminspanne,
713
- // ohne selbst rechnen zu muessen.
714
- totalDurationMinutes: z9.number().int().min(1),
715
- mode: z9.enum(["any", "resource"]),
716
- slots: z9.array(z9.object({
717
- startUtc: utcDateTimeSchema,
718
- resourceId: z9.string().trim().min(1).optional()
719
- }))
720
- });
721
- var bookingAvailabilityQuerySchema = z9.object({
722
- items: z9.preprocess((value) => typeof value === "string" ? parseBookingItemsParam(value) : value, bookingItemsSchema),
723
- resourceId: z9.string().trim().min(1).optional(),
724
- fromDate: isoDateSchema,
725
- toDate: isoDateSchema
726
- });
727
- var bookingAvailabilityResponseSchema = z9.object({
728
- timezone: z9.string().trim().min(1),
729
- totalDurationMinutes: z9.number().int().min(1),
730
- days: z9.array(z9.object({
731
- date: isoDateSchema,
732
- hasSlots: z9.boolean()
733
- })),
734
- /** Erster Tag mit Slot ab `fromDate`, auch ausserhalb des Fensters. */
735
- nextAvailableDate: isoDateSchema.nullable()
736
- });
737
- var bookingPublicErrorCodes = [
738
- "slot_unavailable",
739
- "validation_error",
740
- "origin_not_allowed",
741
- "invalid_key",
742
- "rate_limited",
743
- "payment_required",
744
- "payment_unavailable",
745
- "payment_amount_too_small",
746
- "already_cancelled",
747
- "too_late",
748
- "not_found"
749
- ];
750
- var bookingPublicErrorSchema = z9.object({
751
- error: z9.enum(bookingPublicErrorCodes),
752
- message: z9.string().optional(),
753
- requestId: z9.string().optional()
754
- }).strict();
755
- var bookingCreateRequestSchema = z9.object({
756
- items: bookingItemsSchema,
757
- resourceId: z9.string().min(1).nullable(),
758
- startUtc: utcDateTimeSchema,
759
- customer: z9.object({
760
- name: z9.string().min(1).max(120),
761
- email: z9.string().email().max(254),
762
- phone: z9.string().max(32).optional(),
763
- note: z9.string().max(1e3).optional()
764
- }).strict(),
765
- hp: z9.string().optional(),
766
- clientToken: z9.string().uuid(),
767
- paymentChoice: bookingPaymentChoiceSchema,
768
- // Werbe-Einwilligung. Bestaetigung, Erinnerung und Storno-Link sind davon
769
- // unabhaengig und werden immer versendet.
770
- consent: z9.object({
771
- marketingEmail: z9.boolean(),
772
- marketingSms: z9.boolean()
773
- }).strict().optional()
774
- }).strict();
775
- var bookingPublicPaymentResponseSchema = z9.object({
776
- clientSecret: z9.string().min(1),
777
- stripeAccountId: z9.string().startsWith("acct_")
778
- }).strict();
779
- var bookingCreateResponseSchema = z9.union([
780
- z9.object({
781
- status: z9.literal("confirmed"),
782
- booking: z9.object({
783
- id: z9.string().min(1),
784
- startUtc: utcDateTimeSchema,
785
- endUtc: utcDateTimeSchema,
786
- timezone: z9.string().trim().min(1),
787
- serviceName: z9.string().trim().min(1),
788
- resourceName: z9.string().trim().min(1)
789
- }).strict(),
790
- manageUrl: z9.string().url(),
791
- confirmationMessage: z9.string().nullable()
792
- }).strict(),
793
- z9.object({
794
- status: z9.literal("pending_payment"),
795
- bookingId: z9.string().min(1),
796
- holdExpiresAt: utcDateTimeSchema,
797
- payment: bookingPublicPaymentResponseSchema,
798
- statusToken: z9.string().min(20),
799
- manageUrl: z9.string().url().optional()
800
- }).strict(),
801
- bookingPublicErrorSchema
802
- ]);
803
- var bookingStatusResponseSchema = z9.object({
804
- bookingStatus: bookingStatusSchema,
805
- paymentStatus: bookingPaymentStatusSchema,
806
- refundStatus: bookingRefundStatusSchema,
807
- disputeStatus: bookingDisputeStatusSchema,
808
- holdExpiresAt: utcDateTimeSchema.nullable()
809
- }).strict();
810
- var bookingCancelRequestSchema = z9.object({
811
- token: z9.string().min(20)
812
- });
813
- var bookingCancelResponseSchema = z9.union([
814
- z9.object({
815
- status: z9.enum(["cancelled", "already_cancelled", "too_late", "not_found"])
816
- }),
817
- bookingPublicErrorSchema
818
- ]);
819
- var bookingMoveErrorCodes = [
820
- "slot_unavailable",
821
- "booking_not_movable",
822
- "not_found"
823
- ];
824
- var bookingMoveRequestSchema = z9.object({
825
- bookingId: z9.string().uuid(),
826
- newStartUtc: utcDateTimeSchema,
827
- newResourceId: z9.string().uuid().optional(),
828
- notifyCustomer: z9.boolean().default(false),
829
- reason: z9.string().max(500).optional()
830
- });
831
- var bookingMoveResponseSchema = z9.union([
832
- z9.object({
833
- status: z9.literal("moved"),
834
- bookingId: z9.string().uuid(),
835
- startUtc: utcDateTimeSchema,
836
- endUtc: utcDateTimeSchema,
837
- resourceId: z9.string().uuid()
838
- }),
839
- z9.object({
840
- error: z9.enum(bookingMoveErrorCodes),
841
- message: z9.string().optional(),
842
- requestId: z9.string().optional()
843
- })
844
- ]);
845
- var bookingManifestSchema = z9.object({
846
- schemaVersion: z9.number().int().positive(),
847
- siteId: z9.string().trim().min(1),
848
- bookingPageSlug: z9.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).min(2).max(64),
849
- integration: z9.literal("rewrite").optional(),
850
- productionOrigin: z9.string().url().optional()
851
- }).passthrough();
1
+ import {
2
+ SITEPLANE_CONFIG_FILE,
3
+ readSiteplaneConfig
4
+ } from "./chunk-KZ3AQ6OA.js";
852
5
 
853
- // ../shared/dist/booking-defaults.js
854
- var BOOKING_CREATE_PAYLOAD_LIMIT_BYTES = 16 * 1024;
855
-
856
- // ../shared/dist/bridge-protocol.js
857
- import { z as z17 } from "zod";
858
-
859
- // ../shared/dist/field-id.js
860
- import { z as z10 } from "zod";
861
- var FIELD_ID_PATTERN = /^[a-z][a-z0-9_-]*(\.[a-z][a-z0-9_-]*)+$/;
862
- var fieldIdSchema = z10.string().regex(FIELD_ID_PATTERN, "Invalid field id");
863
- function isFieldId(value) {
864
- return typeof value === "string" && fieldIdSchema.safeParse(value).success;
865
- }
866
-
867
- // ../shared/dist/field-types.js
868
- import { z as z11 } from "zod";
869
- var FIELD_TYPES = ["text", "longText", "image", "link"];
870
- var fieldTypeSchema = z11.enum(FIELD_TYPES);
871
-
872
- // ../shared/dist/image-value.js
873
- import { z as z12 } from "zod";
874
- var SUPPORTED_IMAGE_CONTENT_TYPES = [
875
- "image/jpeg",
876
- "image/png",
877
- "image/webp",
878
- "image/avif"
879
- ];
880
- var maxImageUploadBytes = 10 * 1024 * 1024;
881
- var largeImageWarningBytes = 2 * 1024 * 1024;
882
- var imageContentTypeSchema = z12.enum(SUPPORTED_IMAGE_CONTENT_TYPES);
883
- var imageCropSchema = z12.object({
884
- x: z12.number().min(0).max(100),
885
- y: z12.number().min(0).max(100),
886
- zoom: z12.number().min(1).max(4),
887
- aspectRatio: z12.number().positive().max(10)
888
- });
889
- var imageValueSchema = z12.object({
890
- src: z12.string().trim().min(1),
891
- alt: z12.string().trim().min(1).max(300),
892
- width: z12.number().int().positive().optional(),
893
- height: z12.number().int().positive().optional(),
894
- crop: imageCropSchema.optional(),
895
- assetId: z12.string().uuid().optional(),
896
- finalStorageProvider: z12.enum([
897
- "repo_static_assets",
898
- "repo_handoff",
899
- "site_owned_storage",
900
- "siteplane_managed_fallback"
901
- ]).optional()
902
- });
903
-
904
- // ../shared/dist/setup-deployment.js
905
- import { z as z13 } from "zod";
906
- var version = z13.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u);
907
- var siteSetupPackagesSchema = z13.object({
908
- siteplane: version,
909
- runtimeNext: version,
910
- analytics: version.optional()
911
- }).strict();
912
- var hash = z13.string().regex(/^sha256:[0-9a-f]{64}$/u);
913
- var setupDeploymentRecordSchema = z13.object({
914
- version: z13.literal(1),
915
- operationId: z13.string().uuid(),
916
- provider: z13.literal("vercel"),
917
- projectId: z13.string().regex(/^prj_[A-Za-z0-9]+$/u),
918
- teamId: z13.string().regex(/^(?:team|user)_[A-Za-z0-9]+$/u),
919
- origin: z13.string().url().refine((value) => {
920
- const url = new URL(value);
921
- return url.protocol === "https:" && url.origin === value && !url.username && !url.password;
922
- }),
923
- deploymentId: z13.string().regex(/^dpl_[A-Za-z0-9]+$/u).nullable(),
924
- revision: z13.string().regex(/^[0-9a-f]{40}$/u),
925
- fieldContractHash: hash,
926
- packages: siteSetupPackagesSchema,
927
- configurationFingerprint: hash,
928
- adminSecretGeneration: z13.string().uuid(),
929
- revalidationGeneration: z13.number().int().positive(),
930
- startedAt: z13.string().datetime(),
931
- environmentWrittenAt: z13.string().datetime().nullable(),
932
- deployRequestedAt: z13.string().datetime().nullable(),
933
- deadline: z13.string().datetime(),
934
- phase: z13.enum([
935
- "planned",
936
- "environment_written",
937
- "deploy_requested",
938
- "deployed",
939
- "verified",
940
- "failed"
941
- ]),
942
- build: z13.object({
943
- node: version,
944
- packageManager: z13.enum(["npm", "pnpm"]),
945
- packageManagerVersion: version,
946
- lockfileHash: hash
947
- }).strict().nullable()
948
- }).strict();
949
- var setupDeploymentInputSchema = z13.discriminatedUnion("action", [
950
- z13.object({ action: z13.literal("read") }).strict(),
951
- z13.object({
952
- action: z13.literal("record"),
953
- expectedOperationId: z13.string().uuid().nullable(),
954
- record: setupDeploymentRecordSchema
955
- }).strict()
956
- ]);
957
-
958
- // ../shared/dist/site-setup.js
959
- import { z as z16 } from "zod";
960
-
961
- // ../shared/dist/site-field-contract.js
962
- import { z as z15 } from "zod";
963
-
964
- // ../shared/dist/fallback-hash.js
965
- import { z as z14 } from "zod";
966
- var SAFE_REPRESENTATION_REASONS = [
967
- "asset_import_rewritten",
968
- "relative_url_normalized",
969
- "line_endings_normalized",
970
- "image_value_serialized"
971
- ];
972
- var safeRepresentationReasonSchema = z14.enum(SAFE_REPRESENTATION_REASONS);
973
- function canonicalizeFallbackValue(input) {
974
- const fieldType = fieldTypeSchema.parse(input.fieldType);
975
- if (input.safeRepresentationReason !== void 0) {
976
- safeRepresentationReasonSchema.parse(input.safeRepresentationReason);
977
- }
978
- if (fieldType === "text" || fieldType === "longText") {
979
- if (typeof input.fallbackValue !== "string") {
980
- throw new Error("Text fallback values must be strings.");
981
- }
982
- return {
983
- fieldType,
984
- fallbackValue: normalizeLineEndings(input.fallbackValue)
985
- };
986
- }
987
- if (fieldType === "link") {
988
- const link = linkFallbackSchema.parse(input.fallbackValue);
989
- return {
990
- fieldType,
991
- fallbackValue: sortObjectKeys({
992
- ...link,
993
- href: normalizeExplicitHref(link.href)
994
- })
995
- };
996
- }
997
- const image = imageValueSchema.parse(input.fallbackValue);
998
- return {
999
- fieldType,
1000
- fallbackValue: sortObjectKeys(image)
1001
- };
1002
- }
1003
- function createCanonicalFallbackHash(input) {
1004
- const canonical = canonicalizeFallbackValue(input);
1005
- const payload = stableJsonStringify(canonical);
1006
- return `sha256:${sha256Hex(payload)}`;
1007
- }
1008
- var linkFallbackSchema = z14.object({
1009
- href: z14.string().trim().min(1),
1010
- label: z14.string().trim().min(1).optional(),
1011
- target: z14.enum(["_self", "_blank"]).optional()
1012
- }).passthrough();
1013
- function normalizeLineEndings(value) {
1014
- return value.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1015
- }
1016
- function normalizeExplicitHref(value) {
1017
- const href = value.trim();
1018
- try {
1019
- const parsed = new URL(href);
1020
- const hasRootSlashOnly = parsed.pathname === "/" && parsed.search === "" && parsed.hash === "";
1021
- if (hasRootSlashOnly) {
1022
- return `${parsed.protocol}//${parsed.host}`;
1023
- }
1024
- } catch {
1025
- return href;
1026
- }
1027
- return href;
1028
- }
1029
- function stableJsonStringify(value) {
1030
- return JSON.stringify(sortObjectKeys(value));
1031
- }
1032
- function sortObjectKeys(value) {
1033
- if (Array.isArray(value)) {
1034
- return value.map(sortObjectKeys);
1035
- }
1036
- if (!value || typeof value !== "object") {
1037
- return value;
1038
- }
1039
- return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entryValue]) => [key, sortObjectKeys(entryValue)]));
1040
- }
1041
- function sha256Hex(input) {
1042
- const bytes = new TextEncoder().encode(input);
1043
- const paddedLength = Math.ceil((bytes.length + 1 + 8) / 64) * 64;
1044
- const padded = new Uint8Array(paddedLength);
1045
- padded.set(bytes);
1046
- padded[bytes.length] = 128;
1047
- const bitLength = bytes.length * 8;
1048
- const view = new DataView(padded.buffer);
1049
- view.setUint32(paddedLength - 8, Math.floor(bitLength / 4294967296));
1050
- view.setUint32(paddedLength - 4, bitLength >>> 0);
1051
- let h0 = 1779033703;
1052
- let h1 = 3144134277;
1053
- let h2 = 1013904242;
1054
- let h3 = 2773480762;
1055
- let h4 = 1359893119;
1056
- let h5 = 2600822924;
1057
- let h6 = 528734635;
1058
- let h7 = 1541459225;
1059
- const words = new Array(64).fill(0);
1060
- for (let chunk = 0; chunk < paddedLength; chunk += 64) {
1061
- for (let index = 0; index < 16; index += 1) {
1062
- words[index] = view.getUint32(chunk + index * 4);
1063
- }
1064
- for (let index = 16; index < 64; index += 1) {
1065
- words[index] = smallSigma1(words[index - 2] ?? 0) + (words[index - 7] ?? 0) + smallSigma0(words[index - 15] ?? 0) + (words[index - 16] ?? 0) >>> 0;
1066
- }
1067
- let a = h0;
1068
- let b = h1;
1069
- let c = h2;
1070
- let d = h3;
1071
- let e = h4;
1072
- let f = h5;
1073
- let g = h6;
1074
- let h = h7;
1075
- for (let index = 0; index < 64; index += 1) {
1076
- const temp1 = h + bigSigma1(e) + choose(e, f, g) + SHA256_CONSTANTS[index] + (words[index] ?? 0) >>> 0;
1077
- const temp2 = bigSigma0(a) + majority(a, b, c) >>> 0;
1078
- h = g;
1079
- g = f;
1080
- f = e;
1081
- e = d + temp1 >>> 0;
1082
- d = c;
1083
- c = b;
1084
- b = a;
1085
- a = temp1 + temp2 >>> 0;
1086
- }
1087
- h0 = h0 + a >>> 0;
1088
- h1 = h1 + b >>> 0;
1089
- h2 = h2 + c >>> 0;
1090
- h3 = h3 + d >>> 0;
1091
- h4 = h4 + e >>> 0;
1092
- h5 = h5 + f >>> 0;
1093
- h6 = h6 + g >>> 0;
1094
- h7 = h7 + h >>> 0;
1095
- }
1096
- return [h0, h1, h2, h3, h4, h5, h6, h7].map((value) => value.toString(16).padStart(8, "0")).join("");
1097
- }
1098
- function rightRotate(value, bits) {
1099
- return value >>> bits | value << 32 - bits;
1100
- }
1101
- function choose(x, y, zValue) {
1102
- return x & y ^ ~x & zValue;
1103
- }
1104
- function majority(x, y, zValue) {
1105
- return x & y ^ x & zValue ^ y & zValue;
1106
- }
1107
- function bigSigma0(value) {
1108
- return rightRotate(value, 2) ^ rightRotate(value, 13) ^ rightRotate(value, 22);
1109
- }
1110
- function bigSigma1(value) {
1111
- return rightRotate(value, 6) ^ rightRotate(value, 11) ^ rightRotate(value, 25);
1112
- }
1113
- function smallSigma0(value) {
1114
- return rightRotate(value, 7) ^ rightRotate(value, 18) ^ value >>> 3;
1115
- }
1116
- function smallSigma1(value) {
1117
- return rightRotate(value, 17) ^ rightRotate(value, 19) ^ value >>> 10;
1118
- }
1119
- var SHA256_CONSTANTS = [
1120
- 1116352408,
1121
- 1899447441,
1122
- 3049323471,
1123
- 3921009573,
1124
- 961987163,
1125
- 1508970993,
1126
- 2453635748,
1127
- 2870763221,
1128
- 3624381080,
1129
- 310598401,
1130
- 607225278,
1131
- 1426881987,
1132
- 1925078388,
1133
- 2162078206,
1134
- 2614888103,
1135
- 3248222580,
1136
- 3835390401,
1137
- 4022224774,
1138
- 264347078,
1139
- 604807628,
1140
- 770255983,
1141
- 1249150122,
1142
- 1555081692,
1143
- 1996064986,
1144
- 2554220882,
1145
- 2821834349,
1146
- 2952996808,
1147
- 3210313671,
1148
- 3336571891,
1149
- 3584528711,
1150
- 113926993,
1151
- 338241895,
1152
- 666307205,
1153
- 773529912,
1154
- 1294757372,
1155
- 1396182291,
1156
- 1695183700,
1157
- 1986661051,
1158
- 2177026350,
1159
- 2456956037,
1160
- 2730485921,
1161
- 2820302411,
1162
- 3259730800,
1163
- 3345764771,
1164
- 3516065817,
1165
- 3600352804,
1166
- 4094571909,
1167
- 275423344,
1168
- 430227734,
1169
- 506948616,
1170
- 659060556,
1171
- 883997877,
1172
- 958139571,
1173
- 1322822218,
1174
- 1537002063,
1175
- 1747873779,
1176
- 1955562222,
1177
- 2024104815,
1178
- 2227730452,
1179
- 2361852424,
1180
- 2428436474,
1181
- 2756734187,
1182
- 3204031479,
1183
- 3329325298
1184
- ];
1185
-
1186
- // ../shared/dist/site-field-contract.js
1187
- var fieldContractVersion = "1.0.0";
1188
- var scannerContractVersion = "1.0.0";
1189
- var siteFieldStatusSchema = z15.enum(["hidden", "active"]);
1190
- var semanticVersionSchema = z15.string().regex(/^\d+\.\d+\.\d+$/u);
1191
- var siteFieldRouteKeySchema = z15.string().trim().refine((value) => value === "$global" || value.startsWith("/") && !value.includes("?") && !value.includes("#") && (value === "/" || !value.endsWith("/")), "Route keys must be canonical paths or $global.");
1192
- var repositoryRelativePosixPathSchema = z15.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.");
1193
- var sourceMappingSchema = z15.object({
1194
- kind: z15.literal("function"),
1195
- filePath: repositoryRelativePosixPathSchema,
1196
- exportName: z15.string().trim().min(1).optional(),
1197
- sourcePath: z15.string().trim().min(1).optional(),
1198
- editTarget: fieldIdSchema.optional()
1199
- }).strict();
1200
- var domMappingSchema = z15.object({
1201
- attribute: z15.literal("data-siteplane-field-id"),
1202
- previewStrategy: z15.enum(["dom", "event"])
1203
- }).strict();
1204
- var fieldBaseSchema = z15.object({
1205
- fieldId: fieldIdSchema,
1206
- routeKey: siteFieldRouteKeySchema,
1207
- source: sourceMappingSchema,
1208
- dom: domMappingSchema.optional()
1209
- }).strict();
1210
- var linkFallbackSchema2 = z15.object({
1211
- href: z15.string().trim().min(1),
1212
- label: z15.string().trim().min(1).optional(),
1213
- target: z15.enum(["_self", "_blank"]).optional()
1214
- }).strict();
1215
- var imageFallbackSchema = z15.object({
1216
- src: z15.string().trim().min(1),
1217
- alt: z15.string().trim().max(300),
1218
- width: z15.number().int().positive().optional(),
1219
- height: z15.number().int().positive().optional(),
1220
- crop: z15.object({
1221
- x: z15.number().min(0).max(100),
1222
- y: z15.number().min(0).max(100),
1223
- zoom: z15.number().min(1).max(4),
1224
- aspectRatio: z15.number().positive().max(10)
1225
- }).strict().optional()
1226
- }).strict();
1227
- var siteFieldContractFieldSchema = z15.discriminatedUnion("fieldType", [
1228
- fieldBaseSchema.extend({
1229
- fieldType: z15.literal("text"),
1230
- fallback: z15.string()
1231
- }),
1232
- fieldBaseSchema.extend({
1233
- fieldType: z15.literal("longText"),
1234
- fallback: z15.string()
1235
- }),
1236
- fieldBaseSchema.extend({
1237
- fieldType: z15.literal("link"),
1238
- fallback: linkFallbackSchema2
1239
- }),
1240
- fieldBaseSchema.extend({
1241
- fieldType: z15.literal("image"),
1242
- fallback: imageFallbackSchema
1243
- })
1244
- ]);
1245
- var siteFieldContractV1Schema = z15.object({
1246
- version: z15.literal(1),
1247
- fieldContractVersion: semanticVersionSchema,
1248
- scannerContractVersion: semanticVersionSchema,
1249
- fields: z15.array(siteFieldContractFieldSchema)
1250
- }).strict().superRefine((contract, context) => {
1251
- const seen = /* @__PURE__ */ new Set();
1252
- for (const [index, field] of contract.fields.entries()) {
1253
- if (seen.has(field.fieldId)) {
1254
- context.addIssue({
1255
- code: "custom",
1256
- message: `Duplicate field id: ${field.fieldId}`,
1257
- path: ["fields", index, "fieldId"]
1258
- });
1259
- }
1260
- seen.add(field.fieldId);
1261
- }
1262
- });
1263
- function normalizeSiteFieldContract(input) {
1264
- const contract = siteFieldContractV1Schema.parse(input);
1265
- return {
1266
- ...contract,
1267
- fields: [...contract.fields].sort((left, right) => left.fieldId.localeCompare(right.fieldId))
1268
- };
1269
- }
1270
- function createSiteFieldContractHash(input) {
1271
- const payload = stableJsonStringify(normalizeSiteFieldContract(input));
1272
- return `sha256:${sha256Hex(payload)}`;
1273
- }
1274
-
1275
- // ../shared/dist/site-setup.js
1276
- var SITE_SETUP_RUN_STATUSES = [
1277
- "waiting_for_agent",
1278
- "working",
1279
- "action_required",
1280
- "ready_for_review",
1281
- "completed"
1282
- ];
1283
- var SITE_SETUP_RUN_MODES = ["initial", "update"];
1284
- var SITE_SETUP_ERROR_CODES = [
1285
- "action_required",
1286
- "deployment_evidence_required",
1287
- "field_contract_scan_failed",
1288
- "invalid_editor_definition",
1289
- "invalid_field_contract",
1290
- "missing_build_revision",
1291
- "missing_field_contract",
1292
- "review_locked",
1293
- "stale_deployment_contract",
1294
- "stale_field_contract",
1295
- "stale_setup_review",
1296
- "update_required"
1297
- ];
1298
- var CONTINUE_SITE_SETUP_MAX_ITEMS = 50;
1299
- var CONTINUE_SITE_SETUP_RENDERED_PATH_MAX_BYTES = 512;
1300
- var CONTINUE_SITE_SETUP_VISIBLE_VALUE_MAX_BYTES = 256;
1301
- var CONTINUE_SITE_SETUP_LOCATOR_HINT_MAX_BYTES = 512;
1302
- var CONTINUE_SITE_SETUP_REQUESTED_LABEL_MAX_BYTES = 120;
1303
- var CONTINUE_SITE_SETUP_REQUESTED_SECTION_MAX_BYTES = 120;
1304
- var CONTINUE_SITE_SETUP_REQUESTED_SUBSECTION_MAX_BYTES = 120;
1305
- var SITE_SETUP_OWNER_EDITABILITY_NOTE_MAX_LENGTH = 2e3;
1306
- var SITE_SETUP_OWNER_EDITABILITY_NOTE_MAX_BYTES = 8e3;
1307
- var CONTINUE_SITE_SETUP_ITEMS_MAX_BYTES = 32 * 1024;
1308
- var CONTINUE_SITE_SETUP_HTTP_BODY_MAX_BYTES = 64 * 1024;
1309
- var siteSetupRunStatusSchema = z16.enum(SITE_SETUP_RUN_STATUSES);
1310
- var siteSetupRunModeSchema = z16.enum(SITE_SETUP_RUN_MODES);
1311
- var siteSetupErrorCodeSchema = z16.enum(SITE_SETUP_ERROR_CODES);
1312
- var editorFieldSchema = z16.object({
1313
- fieldId: fieldIdSchema,
1314
- label: z16.string().trim().min(1),
1315
- subsection: z16.string().trim().min(1).optional(),
1316
- description: z16.string().trim().min(1).optional(),
1317
- required: z16.boolean().optional(),
1318
- maxLength: z16.number().int().positive().optional()
1319
- }).strict();
1320
- var editorSectionSchema = z16.object({
1321
- label: z16.string().trim().min(1),
1322
- fields: z16.array(editorFieldSchema).min(1)
1323
- }).strict().superRefine((section, context) => {
1324
- const closedSubsections = /* @__PURE__ */ new Set();
1325
- let currentSubsection;
1326
- for (const [index, field] of section.fields.entries()) {
1327
- if (field.subsection === currentSubsection)
1328
- continue;
1329
- if (currentSubsection)
1330
- closedSubsections.add(currentSubsection);
1331
- currentSubsection = field.subsection;
1332
- if (currentSubsection && closedSubsections.has(currentSubsection)) {
1333
- context.addIssue({
1334
- code: "custom",
1335
- message: `Subgroup fields must stay contiguous: ${currentSubsection}`,
1336
- path: ["fields", index, "subsection"]
1337
- });
1338
- }
1339
- }
1340
- });
1341
- var editorRouteSchema = z16.object({
1342
- routeKey: siteFieldRouteKeySchema,
1343
- previewPath: z16.string().trim().refine((path) => path.startsWith("/") && !path.startsWith("//") && !/[\\\[\]?#\s]/u.test(path) && (path === "/" || !path.endsWith("/")), "previewPath must be a concrete same-site path, for example /services/consultation.").optional(),
1344
- label: z16.string().trim().min(1),
1345
- sections: z16.array(editorSectionSchema).min(1)
1346
- }).strict().superRefine((route, context) => {
1347
- if (route.routeKey.includes("[") && !route.previewPath) {
1348
- context.addIssue({
1349
- code: "custom",
1350
- path: ["previewPath"],
1351
- message: "Dynamic editor routes need a previewPath for an existing page; keep the canonical routeKey unchanged."
1352
- });
1353
- }
1354
- const sections = /* @__PURE__ */ new Set();
1355
- for (const [index, section] of route.sections.entries()) {
1356
- if (sections.has(section.label)) {
1357
- context.addIssue({
1358
- code: "custom",
1359
- message: `Duplicate section label: ${section.label}`,
1360
- path: ["sections", index, "label"]
1361
- });
1362
- }
1363
- sections.add(section.label);
1364
- }
1365
- });
1366
- var editorDefinitionV1Schema = z16.object({
1367
- version: z16.literal(1),
1368
- routes: z16.array(editorRouteSchema).min(1)
1369
- }).strict().superRefine((definition, context) => {
1370
- const routes = /* @__PURE__ */ new Set();
1371
- const fields = /* @__PURE__ */ new Set();
1372
- for (const [routeIndex, route] of definition.routes.entries()) {
1373
- if (routes.has(route.routeKey)) {
1374
- context.addIssue({
1375
- code: "custom",
1376
- message: `Duplicate route key: ${route.routeKey}`,
1377
- path: ["routes", routeIndex, "routeKey"]
1378
- });
1379
- }
1380
- routes.add(route.routeKey);
1381
- for (const [sectionIndex, section] of route.sections.entries()) {
1382
- for (const [fieldIndex, field] of section.fields.entries()) {
1383
- if (fields.has(field.fieldId)) {
1384
- context.addIssue({
1385
- code: "custom",
1386
- message: `Duplicate editor field id: ${field.fieldId}`,
1387
- path: [
1388
- "routes",
1389
- routeIndex,
1390
- "sections",
1391
- sectionIndex,
1392
- "fields",
1393
- fieldIndex,
1394
- "fieldId"
1395
- ]
1396
- });
1397
- }
1398
- fields.add(field.fieldId);
1399
- }
1400
- }
1401
- }
1402
- });
1403
- var forbiddenControlOrBidi = /[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u;
1404
- var forbiddenOwnerNoteControlOrBidi = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u;
1405
- var unsafeLocator = /<\/?[a-z][^>]*>|\b(?:javascript|data:text\/html)\s*:|\bon[a-z]+\s*=/iu;
1406
- var renderedPathSchema = z16.string().trim().transform((value) => value !== "/" ? value.replace(/\/+$/u, "") : value).superRefine((value, context) => {
1407
- if (!value.startsWith("/") || value === "$global" || value.includes("?") || value.includes("#")) {
1408
- context.addIssue({
1409
- code: "custom",
1410
- message: "renderedPath must be a canonical real path."
1411
- });
1412
- }
1413
- addSafeByteIssues(value, CONTINUE_SITE_SETUP_RENDERED_PATH_MAX_BYTES, "renderedPath", context);
1414
- });
1415
- function normalizedSafeTextSchema(maxBytes, label) {
1416
- return z16.string().transform((value) => value.trim().replace(new RegExp("\\p{White_Space}+", "gu"), " ")).pipe(z16.string().min(1)).superRefine((value, context) => {
1417
- addSafeByteIssues(value, maxBytes, label, context);
1418
- if (label === "locatorHint" && unsafeLocator.test(value)) {
1419
- context.addIssue({
1420
- code: "custom",
1421
- message: "locatorHint contains unsafe markup or executable content."
1422
- });
1423
- }
1424
- });
1425
- }
1426
- var correctionItemBase = {
1427
- renderedPath: renderedPathSchema,
1428
- visibleValue: normalizedSafeTextSchema(CONTINUE_SITE_SETUP_VISIBLE_VALUE_MAX_BYTES, "visibleValue"),
1429
- locatorHint: normalizedSafeTextSchema(CONTINUE_SITE_SETUP_LOCATOR_HINT_MAX_BYTES, "locatorHint"),
1430
- requestedLabel: normalizedSafeTextSchema(CONTINUE_SITE_SETUP_REQUESTED_LABEL_MAX_BYTES, "requestedLabel").optional(),
1431
- requestedSection: normalizedSafeTextSchema(CONTINUE_SITE_SETUP_REQUESTED_SECTION_MAX_BYTES, "requestedSection").optional(),
1432
- requestedSubsection: normalizedSafeTextSchema(CONTINUE_SITE_SETUP_REQUESTED_SUBSECTION_MAX_BYTES, "requestedSubsection").optional(),
1433
- requestedSortOrder: z16.number().int().nonnegative().optional()
1434
- };
1435
- var setupCorrectionItemSchema = z16.discriminatedUnion("kind", [
1436
- z16.object({ kind: z16.literal("text"), ...correctionItemBase }).strict(),
1437
- z16.object({ kind: z16.literal("link"), ...correctionItemBase }).strict(),
1438
- z16.object({ kind: z16.literal("image"), ...correctionItemBase }).strict()
1439
- ]).superRefine((item, context) => {
1440
- if (item.requestedSubsection && !item.requestedSection) {
1441
- context.addIssue({
1442
- code: "custom",
1443
- message: "requestedSubsection requires requestedSection.",
1444
- path: ["requestedSubsection"]
1445
- });
1446
- }
1447
- });
1448
- var setupCorrectionItemsSchema = z16.array(setupCorrectionItemSchema).max(CONTINUE_SITE_SETUP_MAX_ITEMS).superRefine((items, context) => {
1449
- const bytes = utf8Bytes(stableJsonStringify(items));
1450
- if (bytes > CONTINUE_SITE_SETUP_ITEMS_MAX_BYTES) {
1451
- context.addIssue({
1452
- code: "custom",
1453
- message: "Correction items exceed the 32 KiB canonical payload limit."
1454
- });
1455
- }
1456
- });
1457
- var siteSetupOwnerEditabilityNoteSchema = z16.string().max(SITE_SETUP_OWNER_EDITABILITY_NOTE_MAX_LENGTH).superRefine((value, context) => {
1458
- if (utf8Bytes(value) > SITE_SETUP_OWNER_EDITABILITY_NOTE_MAX_BYTES) {
1459
- context.addIssue({
1460
- code: "custom",
1461
- message: `ownerEditabilityNote exceeds ${SITE_SETUP_OWNER_EDITABILITY_NOTE_MAX_BYTES} UTF-8 bytes.`
1462
- });
1463
- }
1464
- if (forbiddenOwnerNoteControlOrBidi.test(value)) {
1465
- context.addIssue({
1466
- code: "custom",
1467
- message: "ownerEditabilityNote contains a forbidden control or bidi character."
1468
- });
1469
- }
1470
- });
1471
- var SITE_SETUP_MODULE_SCOPES = {
1472
- editing: ["setup:read", "setup:write"],
1473
- analytics: [
1474
- "analytics:setup",
1475
- "analytics:sync",
1476
- "analytics:test",
1477
- "analytics:readiness",
1478
- "analytics:agent_instructions",
1479
- "analytics:public_key_read"
1480
- ]
1481
- };
1482
- var siteSetupModulesSchema = z16.array(z16.enum(["editing", "analytics"])).min(1).max(2).refine((modules) => modules[0] === "editing" && (modules.length === 1 || modules[1] === "analytics"), "Choose Editing with optional Analytics.");
1483
- function siteSetupScopes(modules) {
1484
- return modules.flatMap((module) => [...SITE_SETUP_MODULE_SCOPES[module]]);
1485
- }
1486
- function hasExactSiteSetupScopes(scopes, modules) {
1487
- const matches = (expected) => scopes.length === expected.length && expected.every((scope) => scopes.includes(scope));
1488
- return modules ? matches(siteSetupScopes(modules)) : matches(siteSetupScopes(["editing"])) || matches(siteSetupScopes(["editing", "analytics"]));
1489
- }
1490
- var siteSetupActivityInputSchema = z16.object({
1491
- phase: z16.enum([
1492
- "inspecting",
1493
- "integrating",
1494
- "deploying",
1495
- "verifying",
1496
- "repairing"
1497
- ]),
1498
- textId: z16.enum([
1499
- "inspecting",
1500
- "integrating",
1501
- "deploying",
1502
- "verifying",
1503
- "repairing"
1504
- ])
1505
- }).strict().refine((value) => value.phase === value.textId, "Activity text must describe its phase.");
1506
- var nextActionFields = {
1507
- actor: z16.enum(["agent", "user", "siteplane"]),
1508
- boundary: z16.enum([
1509
- "source",
1510
- "environment",
1511
- "deployment",
1512
- "verification",
1513
- "authorization",
1514
- "review"
1515
- ]),
1516
- precondition: z16.string().min(1).max(1e3),
1517
- instruction: z16.string().min(1).max(2e3),
1518
- expectedEvidence: z16.string().min(1).max(1e3)
1519
- };
1520
- var siteSetupNextActionSchema = z16.discriminatedUnion("kind", [
1521
- z16.object({
1522
- ...nextActionFields,
1523
- kind: z16.literal("run_command"),
1524
- command: z16.string().min(1).max(500)
1525
- }).strict(),
1526
- z16.object({ ...nextActionFields, kind: z16.literal("agent_edit") }).strict(),
1527
- z16.object({ ...nextActionFields, kind: z16.literal("needs_user_action") }).strict(),
1528
- z16.object({ ...nextActionFields, kind: z16.literal("wait") }).strict(),
1529
- z16.object({ ...nextActionFields, kind: z16.literal("done") }).strict()
1530
- ]);
1531
- var siteSetupTaskRunSchema = z16.object({
1532
- runId: z16.string().uuid(),
1533
- mode: siteSetupRunModeSchema,
1534
- status: siteSetupRunStatusSchema,
1535
- fieldContractHash: z16.string().nullable(),
1536
- deploymentOrigin: z16.string().url().nullable(),
1537
- deploymentRevision: z16.string().nullable(),
1538
- publicRuntimeKeyId: z16.string().uuid().nullable(),
1539
- reviewVersion: z16.number().int().positive()
1540
- }).strict();
1541
- var siteSetupTaskProgressSchema = z16.object({
1542
- progressStage: z16.enum([
1543
- "waiting_for_connection",
1544
- "agent_connected",
1545
- "project_context_loaded",
1546
- "fields_prepared",
1547
- "deployment_verification",
1548
- "ready_for_review",
1549
- "action_required",
1550
- "completed"
1551
- ]),
1552
- lastVerifiedAt: z16.string().datetime().nullable(),
1553
- lastActivityAt: z16.string().datetime(),
1554
- activitySource: z16.enum(["user", "server", "agent"]),
1555
- activityPhase: siteSetupActivityInputSchema.shape.phase.nullable(),
1556
- activityTextId: siteSetupActivityInputSchema.shape.textId.nullable()
1557
- }).strict();
1558
- var siteSetupTaskSchema = z16.object({
1559
- contractVersion: z16.literal("siteplane.setup-task.v2"),
1560
- workflow: z16.enum(["initial_setup", "structure_update"]),
1561
- goal: z16.string().min(1),
1562
- modules: siteSetupModulesSchema,
1563
- authorizationVersion: z16.number().int().positive(),
1564
- authorizedScopes: z16.array(z16.string()).min(2),
1565
- site: z16.object({
1566
- siteId: z16.string().uuid(),
1567
- portalPath: z16.string(),
1568
- adminOrigin: z16.string().url()
1569
- }).strict().nullable(),
1570
- run: siteSetupTaskRunSchema.nullable(),
1571
- progress: siteSetupTaskProgressSchema.nullable(),
1572
- deployment: setupDeploymentRecordSchema.nullable(),
1573
- revalidationGeneration: z16.number().int().positive(),
1574
- assignment: z16.enum(["owned", "provisional", "claimed"]),
1575
- deadline: z16.string().datetime().nullable(),
1576
- userInstructions: z16.object({
1577
- text: siteSetupOwnerEditabilityNoteSchema,
1578
- source: z16.literal("user"),
1579
- trust: z16.literal("untrusted")
1580
- }).strict(),
1581
- corrections: setupCorrectionItemsSchema,
1582
- contentSelectionSummary: z16.object({
1583
- text: siteSetupOwnerEditabilityNoteSchema,
1584
- source: z16.literal("agent"),
1585
- trust: z16.literal("untrusted")
1586
- }).strict().nullable(),
1587
- environmentBindings: z16.array(z16.object({
1588
- name: z16.string().regex(/^[A-Z][A-Z0-9_]+$/u),
1589
- source: z16.string().min(1),
1590
- sensitivity: z16.enum(["public", "secret"]),
1591
- usage: z16.enum(["build", "server", "build_and_server"]),
1592
- evidence: z16.string().min(1)
1593
- }).strict()),
1594
- nextAction: siteSetupNextActionSchema,
1595
- error: z16.object({
1596
- code: z16.string().regex(/^[a-z0-9_.]+$/u).max(160),
1597
- boundary: nextActionFields.boundary,
1598
- responsibleActor: nextActionFields.actor,
1599
- retryable: z16.boolean(),
1600
- retryAfter: z16.number().int().nonnegative().nullable(),
1601
- requestId: z16.string().regex(/^[A-Za-z0-9_-]+$/u).max(128)
1602
- }).strict().nullable(),
1603
- packages: siteSetupPackagesSchema,
1604
- communication: z16.object({
1605
- beforeSourceChanges: z16.string().min(1),
1606
- updates: z16.array(z16.string().min(1)).min(1),
1607
- blocker: z16.string().min(1)
1608
- }).strict(),
1609
- discovery: z16.object({
1610
- inspectPublicRoutes: z16.boolean(),
1611
- contentKinds: z16.array(z16.enum(["text", "longText", "link", "image"])).min(1),
1612
- rules: z16.array(z16.string().min(1)).min(1)
1613
- }).strict(),
1614
- steps: z16.array(z16.object({
1615
- id: z16.enum([
1616
- "inspect_repository",
1617
- "install_packages",
1618
- "instrument_fields",
1619
- "apply_contract",
1620
- "deploy_production",
1621
- "verify_deployment"
1622
- ]),
1623
- instruction: z16.string().min(1)
1624
- }).strict()).length(6),
1625
- completion: z16.object({
1626
- verifiedStatus: z16.literal("ready_for_review"),
1627
- instruction: z16.string().min(1)
1628
- }).strict()
1629
- }).strict().refine((task) => hasExactSiteSetupScopes(task.authorizedScopes, task.modules), "Task scopes must exactly match its modules.").refine((task) => task.modules.includes("analytics") === (task.packages.analytics !== void 0), "The Analytics package pin must be present exactly when Analytics is selected.");
1630
- var canonicalHashSchema = z16.string().regex(/^sha256:[0-9a-f]{64}$/iu);
1631
- var deploymentRevisionSchema = normalizedSafeTextSchema(256, "deploymentRevision");
1632
- var continueSiteSetupInputSchema = z16.object({
1633
- siteId: z16.string().uuid(),
1634
- runId: z16.string().uuid(),
1635
- expectedReviewVersion: z16.number().int().positive(),
1636
- mutationId: z16.string().uuid(),
1637
- intent: z16.enum(["continue_review", "correct_completed_review"]),
1638
- expectedFieldContractHash: canonicalHashSchema,
1639
- expectedDeploymentRevision: deploymentRevisionSchema,
1640
- correctionItems: setupCorrectionItemsSchema,
1641
- ownerEditabilityNote: siteSetupOwnerEditabilityNoteSchema.optional()
1642
- }).strict();
1643
- var saveSiteSetupOwnerNoteInputSchema = z16.object({
1644
- siteId: z16.string().uuid(),
1645
- runId: z16.string().uuid(),
1646
- expectedReviewVersion: z16.number().int().positive(),
1647
- expectedFieldContractHash: canonicalHashSchema,
1648
- expectedDeploymentRevision: deploymentRevisionSchema,
1649
- ownerEditabilityNote: siteSetupOwnerEditabilityNoteSchema
1650
- }).strict();
1651
- var setupReviewPreferenceSelectionSchema = z16.object({
1652
- analyticsMode: z16.enum(["disabled", "session_only"]).nullable(),
1653
- assetTargetType: z16.enum(["repo_static_assets", "siteplane_managed_fallback"]).nullable(),
1654
- assetTargetId: z16.string().uuid().nullable()
1655
- }).strict().refine((value) => value.assetTargetType !== null || value.assetTargetId === null, {
1656
- message: "An asset target requires its delivery type."
1657
- });
1658
- var setupReviewPreferencesSchema = setupReviewPreferenceSelectionSchema.safeExtend({
1659
- managedHostingApproved: z16.boolean()
1660
- });
1661
- var saveSetupReviewPreferencesInputSchema = z16.object({
1662
- siteId: z16.string().uuid(),
1663
- runId: z16.string().uuid(),
1664
- expectedReviewVersion: z16.number().int().positive(),
1665
- preferences: setupReviewPreferenceSelectionSchema
1666
- }).strict();
1667
- var siteSetupContextInputSchema = z16.object({}).strict();
1668
- var siteSetupApplyInputSchema = z16.object({
1669
- fieldContract: siteFieldContractV1Schema,
1670
- editorDefinition: editorDefinitionV1Schema,
1671
- contentSelectionSummary: siteSetupOwnerEditabilityNoteSchema.refine((text) => text.trim().length > 0, "Describe the selected content, important exclusions and any open gaps.")
1672
- }).strict().superRefine((input, context) => {
1673
- const contractRoutesByFieldId = new Map(input.fieldContract.fields.map((field) => [
1674
- field.fieldId,
1675
- field.routeKey
1676
- ]));
1677
- const definitionFieldIds = /* @__PURE__ */ new Set();
1678
- for (const [routeIndex, route] of input.editorDefinition.routes.entries()) {
1679
- for (const [sectionIndex, section] of route.sections.entries()) {
1680
- for (const [fieldIndex, field] of section.fields.entries()) {
1681
- const fieldPath = [
1682
- "editorDefinition",
1683
- "routes",
1684
- routeIndex,
1685
- "sections",
1686
- sectionIndex,
1687
- "fields",
1688
- fieldIndex,
1689
- "fieldId"
1690
- ];
1691
- const contractRouteKey = contractRoutesByFieldId.get(field.fieldId);
1692
- const contractField = input.fieldContract.fields.find((candidate) => candidate.fieldId === field.fieldId);
1693
- definitionFieldIds.add(field.fieldId);
1694
- if (!contractRouteKey) {
1695
- context.addIssue({
1696
- code: "custom",
1697
- message: `Unknown editor field id: ${field.fieldId}`,
1698
- path: fieldPath
1699
- });
1700
- } else if (contractRouteKey !== route.routeKey) {
1701
- context.addIssue({
1702
- code: "custom",
1703
- message: `Editor field ${field.fieldId} must use route ${contractRouteKey}.`,
1704
- path: fieldPath
1705
- });
1706
- } else if (field.maxLength !== void 0 && contractField?.fieldType !== "text" && contractField?.fieldType !== "longText") {
1707
- context.addIssue({
1708
- code: "custom",
1709
- message: `maxLength is not supported for ${contractField?.fieldType ?? "unknown"} fields.`,
1710
- path: [
1711
- "editorDefinition",
1712
- "routes",
1713
- routeIndex,
1714
- "sections",
1715
- sectionIndex,
1716
- "fields",
1717
- fieldIndex,
1718
- "maxLength"
1719
- ]
1720
- });
1721
- }
1722
- }
1723
- }
1724
- }
1725
- for (const field of input.fieldContract.fields) {
1726
- if (!definitionFieldIds.has(field.fieldId)) {
1727
- context.addIssue({
1728
- code: "custom",
1729
- message: `Missing editor field id: ${field.fieldId}`,
1730
- path: ["editorDefinition", "routes"]
1731
- });
1732
- }
1733
- }
1734
- });
1735
- var siteSetupVerifyInputSchema = z16.object({
1736
- deploymentUrl: z16.string().url().refine((value) => value.startsWith("https://")),
1737
- deploymentRevision: deploymentRevisionSchema,
1738
- wait: z16.boolean().optional()
1739
- }).strict();
1740
- var siteSetupEvidenceInputSchema = z16.object({
1741
- siteId: z16.string().uuid(),
1742
- runId: z16.string().uuid(),
1743
- expectedFieldContractHash: canonicalHashSchema,
1744
- expectedDeploymentOrigin: z16.string().url().refine((value) => value.startsWith("https://")),
1745
- expectedDeploymentRevision: deploymentRevisionSchema,
1746
- publicRuntimeKeyId: z16.string().uuid(),
1747
- bridgeSessionId: normalizedSafeTextSchema(256, "bridgeSessionId"),
1748
- observations: z16.array(z16.object({
1749
- fieldId: fieldIdSchema,
1750
- fieldType: fieldTypeSchema,
1751
- routeKey: siteFieldRouteKeySchema,
1752
- renderedPath: renderedPathSchema,
1753
- targetResolved: z16.boolean(),
1754
- previewAck: z16.boolean()
1755
- }).strict().refine((value) => !value.previewAck || value.targetResolved, "Preview ACK requires a resolved target.")).min(1).max(200)
1756
- }).strict();
1757
- var completeSetupReviewInputSchema = z16.object({
1758
- siteId: z16.string().uuid(),
1759
- runId: z16.string().uuid(),
1760
- expectedReviewVersion: z16.number().int().positive(),
1761
- expectedFieldContractHash: canonicalHashSchema,
1762
- expectedDeploymentRevision: deploymentRevisionSchema,
1763
- expectedReadyFingerprint: canonicalHashSchema
1764
- }).strict();
1765
- var activateSiteSetupInputSchema = z16.object({
1766
- siteId: z16.string().uuid(),
1767
- runId: z16.string().uuid(),
1768
- expectedReviewVersion: z16.number().int().positive(),
1769
- expectedSnapshotHash: canonicalHashSchema
1770
- }).strict();
1771
- var refreshSiteSetupDeploymentInputSchema = z16.object({
1772
- siteId: z16.string().uuid(),
1773
- deploymentUrl: z16.string().url().refine((value) => value.startsWith("https://")),
1774
- deploymentRevision: deploymentRevisionSchema
1775
- }).strict();
1776
- function addSafeByteIssues(value, maxBytes, label, context) {
1777
- if (utf8Bytes(value) > maxBytes) {
1778
- context.addIssue({
1779
- code: "custom",
1780
- message: `${label} exceeds ${maxBytes} UTF-8 bytes.`
1781
- });
1782
- }
1783
- if (forbiddenControlOrBidi.test(value)) {
1784
- context.addIssue({
1785
- code: "custom",
1786
- message: `${label} contains a forbidden control or bidi character.`
1787
- });
1788
- }
1789
- }
1790
- function utf8Bytes(value) {
1791
- return new TextEncoder().encode(value).byteLength;
1792
- }
1793
- var setupActivationApprovalSchema = z16.object({
1794
- reviewVersion: z16.number().int().positive(),
1795
- snapshotHash: canonicalHashSchema,
1796
- analytics: z16.object({
1797
- status: z16.enum(["active", "skipped"]),
1798
- reviewVersion: z16.number().int().positive(),
1799
- generation: z16.number().int().nonnegative()
1800
- }).optional()
1801
- });
1802
- var activateSetupModulesInputSchema = activateSiteSetupInputSchema.extend({
1803
- analytics: z16.discriminatedUnion("decision", [
1804
- z16.object({ decision: z16.literal("pending") }).strict(),
1805
- z16.object({
1806
- decision: z16.literal("enable"),
1807
- readinessTestRunId: z16.string().regex(/^analytics_owner_[0-9a-f-]{36}$/u),
1808
- expectedInstallationGeneration: z16.number().int().positive(),
1809
- measurementApproved: z16.literal(true)
1810
- }).strict(),
1811
- z16.object({
1812
- decision: z16.literal("skip"),
1813
- expectedInstallationGeneration: z16.number().int().nonnegative(),
1814
- continueWithoutAnalytics: z16.literal(true)
1815
- }).strict()
1816
- ])
1817
- }).strict();
1818
-
1819
- // ../shared/dist/bridge-protocol.js
1820
- var BRIDGE_PROTOCOL_VERSION = 2;
1821
- var bridgeRectSchema = z17.object({
1822
- x: z17.number(),
1823
- y: z17.number(),
1824
- width: z17.number().nonnegative(),
1825
- height: z17.number().nonnegative()
1826
- });
1827
- var previewTextValueSchema = z17.object({
1828
- fieldType: z17.enum(["text", "longText"]),
1829
- value: z17.string()
1830
- });
1831
- var previewLinkValueSchema = z17.object({
1832
- fieldType: z17.literal("link"),
1833
- value: z17.object({
1834
- href: z17.string().trim().min(1),
1835
- label: z17.string().trim().min(1).optional()
1836
- })
1837
- });
1838
- var previewImageValueSchema = z17.object({
1839
- fieldType: z17.literal("image"),
1840
- value: imageValueSchema
1841
- });
1842
- var bridgePreviewValueSchema = z17.discriminatedUnion("fieldType", [
1843
- previewTextValueSchema,
1844
- previewLinkValueSchema,
1845
- previewImageValueSchema
1846
- ]);
1847
- var fieldSelectionModeSchema = z17.enum(["replace", "range", "toggle"]);
1848
- var fieldBridgeMissingContentActionSchema = z17.enum([
1849
- "add",
1850
- "select",
1851
- "remove"
1852
- ]);
1853
- var FIELD_BRIDGE_MESSAGE_TYPES = [
1854
- "siteplane:field-bridge:init",
1855
- "siteplane:field-bridge:ready",
1856
- "siteplane:field-bridge:fields",
1857
- "siteplane:field-bridge:select-field",
1858
- "siteplane:field-bridge:set-selected-field",
1859
- "siteplane:field-bridge:apply-preview-values",
1860
- "siteplane:field-bridge:test-preview-values",
1861
- "siteplane:field-bridge:preview-ack",
1862
- "siteplane:field-bridge:set-visual-control-state",
1863
- "siteplane:field-bridge:missing-content",
1864
- "siteplane:field-bridge:preview-scrolled",
1865
- "siteplane:field-bridge:analytics-probe",
1866
- "siteplane:field-bridge:analytics-probe-result",
1867
- "siteplane:field-bridge:error"
1868
- ];
1869
- var canonicalFieldContractHashSchema = z17.string().regex(/^sha256:[0-9a-f]{64}$/u);
1870
- var bridgeSectionSchema = z17.string().trim().min(1).max(256);
1871
- var realRenderedPathSchema = z17.string().trim().refine((value) => value.startsWith("/") && value !== "$global" && !value.includes("?") && !value.includes("#"), "A field bridge rendered path must be a real canonical path.");
1872
- var fieldBridgeIdentitySchema = z17.object({
1873
- publicRuntimeKeyId: z17.string().uuid(),
1874
- fieldContractHash: canonicalFieldContractHashSchema,
1875
- deploymentRevision: z17.string().trim().min(1).max(256)
1876
- }).strict();
1877
- var setupPreviewAccessSchema = z17.object({
1878
- capability: z17.string().regex(/^v1\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]{43}$/u).max(4096),
1879
- expiresAt: z17.string().datetime()
1880
- }).strict();
1881
- var setupPreviewReadSchema = fieldBridgeIdentitySchema.extend({
1882
- scope: z17.literal("setup_preview"),
1883
- siteId: z17.string().uuid(),
1884
- runId: z17.string().uuid(),
1885
- bridgeSessionId: z17.string().min(1),
1886
- expiresAt: z17.string().datetime(),
1887
- contract: siteFieldContractV1Schema
1888
- }).strict();
1889
- var fieldBridgeBaseSchema = z17.object({
1890
- version: z17.literal(BRIDGE_PROTOCOL_VERSION),
1891
- type: z17.enum(FIELD_BRIDGE_MESSAGE_TYPES),
1892
- siteId: z17.string().uuid(),
1893
- sessionId: z17.string().trim().min(1),
1894
- publicRuntimeKeyId: z17.string().uuid(),
1895
- fieldContractHash: canonicalFieldContractHashSchema,
1896
- deploymentRevision: z17.string().trim().min(1).max(256),
1897
- requestId: z17.string().trim().min(1),
1898
- sentAt: z17.string().datetime()
1899
- }).strict();
1900
- var fieldBridgeFieldSchema = z17.object({
1901
- fieldId: fieldIdSchema,
1902
- fieldType: fieldTypeSchema,
1903
- routeKey: siteFieldRouteKeySchema,
1904
- renderedPath: realRenderedPathSchema,
1905
- sourcePath: z17.string().trim().min(1).optional(),
1906
- editTarget: fieldIdSchema,
1907
- rect: bridgeRectSchema,
1908
- targetResolved: z17.literal(true),
1909
- currentValue: bridgePreviewValueSchema.optional()
1910
- }).strict();
1911
- var fieldBridgeInitMessageSchema = fieldBridgeBaseSchema.extend({
1912
- type: z17.literal("siteplane:field-bridge:init"),
1913
- payload: z17.object({
1914
- adminOrigin: z17.string().url(),
1915
- customerOrigin: z17.string().url(),
1916
- renderedPath: realRenderedPathSchema,
1917
- setupPreview: setupPreviewAccessSchema.optional()
1918
- }).strict()
1919
- });
1920
- var fieldBridgeReadyMessageSchema = fieldBridgeBaseSchema.extend({
1921
- type: z17.literal("siteplane:field-bridge:ready"),
1922
- payload: z17.object({
1923
- renderedPath: realRenderedPathSchema,
1924
- fieldCount: z17.number().int().nonnegative(),
1925
- capabilities: z17.tuple([
1926
- z17.literal("field_selection"),
1927
- z17.literal("preview_values"),
1928
- z17.literal("field_evidence"),
1929
- z17.literal("visual_field_control")
1930
- ]).rest(z17.enum(["analytics_preview_readiness", "setup_preview"])).refine((capabilities) => capabilities.length <= 6)
1931
- }).strict()
1932
- });
1933
- var fieldBridgeFieldsMessageSchema = fieldBridgeBaseSchema.extend({
1934
- type: z17.literal("siteplane:field-bridge:fields"),
1935
- payload: z17.object({
1936
- renderedPath: realRenderedPathSchema,
1937
- fields: z17.array(fieldBridgeFieldSchema)
1938
- }).strict()
1939
- });
1940
- var fieldBridgeSelectFieldMessageSchema = fieldBridgeBaseSchema.extend({
1941
- type: z17.literal("siteplane:field-bridge:select-field"),
1942
- payload: z17.object({
1943
- fieldId: fieldIdSchema,
1944
- selectionMode: fieldSelectionModeSchema
1945
- }).strict()
1946
- });
1947
- var fieldBridgeSetSelectedFieldMessageSchema = fieldBridgeBaseSchema.extend({
1948
- type: z17.literal("siteplane:field-bridge:set-selected-field"),
1949
- payload: z17.object({
1950
- fieldIds: z17.array(fieldIdSchema).max(200),
1951
- primaryFieldId: fieldIdSchema.nullable(),
1952
- scrollIntoView: z17.boolean().optional()
1953
- }).strict().superRefine((value, context) => {
1954
- if (value.fieldIds.length > 0 && value.primaryFieldId === null) {
1955
- context.addIssue({
1956
- code: "custom",
1957
- message: "A non-empty selection needs a primary field.",
1958
- path: ["primaryFieldId"]
1959
- });
1960
- }
1961
- if (value.primaryFieldId !== null && !value.fieldIds.includes(value.primaryFieldId)) {
1962
- context.addIssue({
1963
- code: "custom",
1964
- message: "The primary field must be part of the selection.",
1965
- path: ["primaryFieldId"]
1966
- });
1967
- }
1968
- })
1969
- });
1970
- var fieldBridgeApplyPreviewValuesMessageSchema = fieldBridgeBaseSchema.extend({
1971
- type: z17.literal("siteplane:field-bridge:apply-preview-values"),
1972
- payload: z17.object({ values: z17.record(fieldIdSchema, bridgePreviewValueSchema) }).strict()
1973
- });
1974
- var fieldBridgeTestPreviewValuesMessageSchema = fieldBridgeBaseSchema.extend({
1975
- type: z17.literal("siteplane:field-bridge:test-preview-values"),
1976
- payload: z17.object({ fieldIds: z17.array(fieldIdSchema).min(1).max(200) }).strict()
1977
- });
1978
- var fieldBridgePreviewAckMessageSchema = fieldBridgeBaseSchema.extend({
1979
- type: z17.literal("siteplane:field-bridge:preview-ack"),
1980
- payload: z17.object({
1981
- ackRequestId: z17.string().trim().min(1),
1982
- kind: z17.enum(["apply", "test"]),
1983
- renderedPath: realRenderedPathSchema,
1984
- fieldIds: z17.array(fieldIdSchema).max(200)
1985
- }).strict()
1986
- });
1987
- var fieldBridgeSetVisualControlStateMessageSchema = fieldBridgeBaseSchema.extend({
1988
- type: z17.literal("siteplane:field-bridge:set-visual-control-state"),
1989
- payload: z17.object({
1990
- enabled: z17.boolean(),
1991
- fieldProximityEnabled: z17.boolean(),
1992
- pendingKeys: z17.array(z17.string().trim().min(1).max(2048)).max(50),
1993
- activeFieldIds: z17.array(fieldIdSchema),
1994
- fieldSections: z17.record(fieldIdSchema, bridgeSectionSchema),
1995
- selectedPending: z17.array(z17.object({
1996
- key: z17.string().trim().min(1).max(2048),
1997
- locatorHint: z17.string().trim().min(1).max(2048)
1998
- }).strict()).max(50),
1999
- scrollPendingIntoView: z17.boolean().optional()
2000
- }).strict()
2001
- });
2002
- var fieldBridgeMissingContentMessageSchema = fieldBridgeBaseSchema.extend({
2003
- type: z17.literal("siteplane:field-bridge:missing-content"),
2004
- payload: z17.object({
2005
- action: fieldBridgeMissingContentActionSchema,
2006
- item: setupCorrectionItemSchema,
2007
- suggestedSection: bridgeSectionSchema.optional()
2008
- }).strict()
2009
- });
2010
- var fieldBridgePreviewScrolledMessageSchema = fieldBridgeBaseSchema.extend({
2011
- type: z17.literal("siteplane:field-bridge:preview-scrolled"),
2012
- payload: z17.object({ renderedPath: realRenderedPathSchema }).strict()
2013
- });
2014
- var analyticsPreviewChallengeSchema = z17.object({
2015
- bridgeSessionId: z17.string().uuid(),
2016
- challenge: z17.string().min(32).max(512),
2017
- testRunId: z17.string().trim().min(1).max(160),
2018
- expiresAt: z17.string().datetime()
2019
- }).strict();
2020
- var fieldBridgeAnalyticsProbeSchema = fieldBridgeBaseSchema.extend({
2021
- type: z17.literal("siteplane:field-bridge:analytics-probe"),
2022
- requestId: z17.string().min(1).max(256),
2023
- payload: analyticsPreviewChallengeSchema
2024
- });
2025
- var fieldBridgeAnalyticsProbeResultSchema = fieldBridgeBaseSchema.extend({
2026
- type: z17.literal("siteplane:field-bridge:analytics-probe-result"),
2027
- payload: z17.object({
2028
- ackRequestId: z17.string().min(1).max(256),
2029
- status: z17.enum(["recorded", "failed"])
2030
- }).strict()
2031
- });
2032
- var fieldBridgeErrorMessageSchema = fieldBridgeBaseSchema.extend({
2033
- type: z17.literal("siteplane:field-bridge:error"),
2034
- payload: z17.object({
2035
- code: z17.string().trim().min(1),
2036
- message: z17.string().trim().min(1),
2037
- recoverable: z17.boolean().default(false)
2038
- }).strict()
2039
- });
2040
- var fieldBridgeMessageSchema = z17.discriminatedUnion("type", [
2041
- fieldBridgeInitMessageSchema,
2042
- fieldBridgeReadyMessageSchema,
2043
- fieldBridgeFieldsMessageSchema,
2044
- fieldBridgeSelectFieldMessageSchema,
2045
- fieldBridgeSetSelectedFieldMessageSchema,
2046
- fieldBridgeApplyPreviewValuesMessageSchema,
2047
- fieldBridgeTestPreviewValuesMessageSchema,
2048
- fieldBridgePreviewAckMessageSchema,
2049
- fieldBridgeSetVisualControlStateMessageSchema,
2050
- fieldBridgeMissingContentMessageSchema,
2051
- fieldBridgePreviewScrolledMessageSchema,
2052
- fieldBridgeErrorMessageSchema,
2053
- fieldBridgeAnalyticsProbeSchema,
2054
- fieldBridgeAnalyticsProbeResultSchema
2055
- ]);
2056
-
2057
- // ../shared/dist/commands.js
2058
- import { z as z19 } from "zod";
2059
-
2060
- // ../shared/dist/errors.js
2061
- import { z as z18 } from "zod";
2062
- var UI_ERROR_CATEGORIES = [
2063
- "site_member",
2064
- "workspace_member",
2065
- "internal"
2066
- ];
2067
- var uiErrorCategorySchema = z18.enum(UI_ERROR_CATEGORIES);
2068
- var commandErrorPayloadSchema = z18.object({
2069
- code: z18.string().min(1),
2070
- message: z18.string().min(1),
2071
- category: uiErrorCategorySchema,
2072
- details: z18.record(z18.string(), z18.unknown()).optional()
2073
- });
2074
-
2075
- // ../shared/dist/commands.js
2076
- var commandSuccessSchema = z19.object({
2077
- ok: z19.literal(true),
2078
- data: z19.unknown()
2079
- });
2080
- var commandErrorSchema = z19.object({
2081
- ok: z19.literal(false),
2082
- error: commandErrorPayloadSchema
2083
- });
2084
- var commandResultSchema = z19.discriminatedUnion("ok", [
2085
- commandSuccessSchema,
2086
- commandErrorSchema
2087
- ]);
2088
-
2089
- // ../shared/dist/editor-settings.js
2090
- import { z as z20 } from "zod";
2091
- var editorColorSchemeSchema = z20.enum([
2092
- "paper",
2093
- "slate",
2094
- "siteplane",
2095
- "bloom"
2096
- ]);
2097
- var editorColorSchemes = editorColorSchemeSchema.options;
2098
- var editorColorModeSchema = z20.enum(["light", "dark"]);
2099
- var editorColorModes = editorColorModeSchema.options;
2100
-
2101
- // ../shared/dist/editor-organization.js
2102
- import { z as z21 } from "zod";
2103
- var labelSchema = z21.string().trim().min(1).max(120);
2104
- var updateFieldSchema = z21.object({
2105
- operation: z21.literal("update_field"),
2106
- siteId: z21.string().uuid(),
2107
- fieldId: fieldIdSchema,
2108
- label: labelSchema.optional(),
2109
- description: z21.string().trim().max(500).nullable().optional(),
2110
- section: labelSchema.nullable().optional(),
2111
- subsection: labelSchema.nullable().optional(),
2112
- sortOrder: z21.number().int().nonnegative().optional(),
2113
- status: z21.enum(["hidden", "active"]).optional()
2114
- });
2115
- var updateEditorOrganizationInputSchema = z21.discriminatedUnion("operation", [
2116
- updateFieldSchema,
2117
- z21.object({
2118
- operation: z21.literal("rename_section"),
2119
- siteId: z21.string().uuid(),
2120
- routeKey: siteFieldRouteKeySchema,
2121
- currentSection: labelSchema,
2122
- nextSection: labelSchema
2123
- }),
2124
- z21.object({
2125
- operation: z21.literal("create_group"),
2126
- siteId: z21.string().uuid(),
2127
- routeKey: siteFieldRouteKeySchema,
2128
- section: labelSchema,
2129
- fieldIds: z21.array(fieldIdSchema).max(200).default([])
2130
- }),
2131
- z21.object({
2132
- operation: z21.literal("delete_section"),
2133
- siteId: z21.string().uuid(),
2134
- routeKey: siteFieldRouteKeySchema,
2135
- section: labelSchema
2136
- }),
2137
- z21.object({
2138
- operation: z21.literal("move_section"),
2139
- siteId: z21.string().uuid(),
2140
- routeKey: siteFieldRouteKeySchema,
2141
- section: labelSchema,
2142
- direction: z21.enum(["up", "down"])
2143
- }),
2144
- z21.object({
2145
- operation: z21.literal("create_subgroup"),
2146
- siteId: z21.string().uuid(),
2147
- routeKey: siteFieldRouteKeySchema,
2148
- section: labelSchema,
2149
- subsection: labelSchema,
2150
- fieldIds: z21.array(fieldIdSchema).min(1).max(200)
2151
- }),
2152
- z21.object({
2153
- operation: z21.literal("update_subgroup"),
2154
- siteId: z21.string().uuid(),
2155
- routeKey: siteFieldRouteKeySchema,
2156
- currentSection: labelSchema,
2157
- currentSubsection: labelSchema,
2158
- section: labelSchema,
2159
- subsection: labelSchema.nullable()
2160
- }),
2161
- z21.object({
2162
- operation: z21.literal("rename_route"),
2163
- siteId: z21.string().uuid(),
2164
- routeKey: siteFieldRouteKeySchema.refine((value) => value !== "$global", "Global content has no page label."),
2165
- label: labelSchema
2166
- })
2167
- ]).and(z21.object({
2168
- review: z21.object({
2169
- runId: z21.string().uuid(),
2170
- expectedReviewVersion: z21.number().int().positive()
2171
- }).strict().optional()
2172
- })).superRefine((input, context) => {
2173
- if (input.operation === "update_field" && input.label === void 0 && input.description === void 0 && input.section === void 0 && input.subsection === void 0 && input.sortOrder === void 0 && input.status === void 0) {
2174
- context.addIssue({
2175
- code: "custom",
2176
- message: "At least one field organization value is required."
2177
- });
2178
- }
2179
- if (input.operation === "update_field" && input.section === null && typeof input.subsection === "string") {
2180
- context.addIssue({
2181
- code: "custom",
2182
- message: "A subgroup requires a group.",
2183
- path: ["subsection"]
2184
- });
2185
- }
2186
- });
2187
-
2188
- // ../shared/dist/site-slug.js
2189
- import { z as z22 } from "zod";
2190
- var SITE_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
2191
- var RESERVED_SITE_SLUGS = /* @__PURE__ */ new Set([
2192
- "account",
2193
- "admin",
2194
- "api",
2195
- "app",
2196
- "auth",
2197
- "book",
2198
- "claim",
2199
- "dev",
2200
- "login",
2201
- "new",
2202
- "portal",
2203
- "support",
2204
- "workspace"
2205
- ]);
2206
- var siteSlugSchema = z22.string().regex(SITE_SLUG_PATTERN, "Invalid site slug").refine((value) => !RESERVED_SITE_SLUGS.has(value), "Reserved site slug");
2207
-
2208
- // ../shared/dist/entitlements.js
2209
- import { z as z23 } from "zod";
2210
- var entitlementSnapshotSchema = z23.object({
2211
- planKey: z23.string().trim().min(1),
2212
- sitesLimit: z23.number().int().positive(),
2213
- siteMembersPerSiteLimit: z23.number().int().positive(),
2214
- customAdminDomainsEnabled: z23.boolean(),
2215
- agentMcpEnabled: z23.boolean(),
2216
- emailNotificationsEnabled: z23.boolean(),
2217
- analyticsEnabled: z23.boolean(),
2218
- analyticsSitesLimit: z23.number().int().min(0),
2219
- analyticsPortalPerformanceEnabled: z23.boolean(),
2220
- analyticsMonthlyReportsEnabled: z23.boolean(),
2221
- analyticsAiSummaryEnabled: z23.boolean(),
2222
- analyticsRawEventRetentionDays: z23.number().int().min(1).max(ANALYTICS_RETENTION_DEFAULTS.maxRawEventRetentionDaysWithoutAdr),
2223
- analyticsMonthlyEventLimit: z23.number().int().min(0),
2224
- bookingEnabled: z23.boolean().default(false),
2225
- bookingSitesLimit: z23.number().int().min(0).default(0),
2226
- bookingPortalManagementEnabled: z23.boolean().default(false),
2227
- bookingResourcesPerSiteLimit: z23.number().int().min(0).default(0),
2228
- bookingServicesPerSiteLimit: z23.number().int().min(0).default(0),
2229
- bookingMonthlyBookingsLimit: z23.number().int().min(0).default(0),
2230
- bookingRemindersEnabled: z23.boolean().default(false)
2231
- });
2232
- var EARLY_ACCESS_PLAN = {
2233
- planKey: "early_access_2026",
2234
- sitesLimit: 3,
2235
- siteMembersPerSiteLimit: 5,
2236
- customAdminDomainsEnabled: true,
2237
- agentMcpEnabled: true,
2238
- emailNotificationsEnabled: true,
2239
- analyticsEnabled: true,
2240
- analyticsSitesLimit: 3,
2241
- analyticsPortalPerformanceEnabled: false,
2242
- analyticsMonthlyReportsEnabled: false,
2243
- analyticsAiSummaryEnabled: false,
2244
- analyticsRawEventRetentionDays: ANALYTICS_RETENTION_DEFAULTS.enabledRawEventRetentionDays,
2245
- analyticsMonthlyEventLimit: 0,
2246
- bookingEnabled: true,
2247
- bookingSitesLimit: 3,
2248
- bookingPortalManagementEnabled: true,
2249
- bookingResourcesPerSiteLimit: 10,
2250
- bookingServicesPerSiteLimit: 50,
2251
- bookingMonthlyBookingsLimit: 0,
2252
- bookingRemindersEnabled: true
2253
- };
2254
-
2255
- // ../shared/dist/first-party-image-upload.js
2256
- var maxFirstPartyImageUploadBytes = 4 * 1024 * 1024;
2257
-
2258
- // ../shared/dist/readiness.js
2259
- import { z as z24 } from "zod";
2260
- var READINESS_CHECK_KEYS = [
2261
- "agent_instructions_installed",
2262
- "runtime_package_connected",
2263
- "secure_server_access_configured",
2264
- "preview_bridge_working",
2265
- "admin_access_url_selected",
2266
- "default_publishing_mode_selected"
2267
- ];
2268
- var READINESS_STATUSES = [
2269
- "pending",
2270
- "passing",
2271
- "failing",
2272
- "warning"
2273
- ];
2274
- var readinessCheckKeySchema = z24.enum(READINESS_CHECK_KEYS);
2275
- var readinessStatusSchema = z24.enum(READINESS_STATUSES);
2276
-
2277
- // ../shared/dist/site.js
2278
- import { z as z25 } from "zod";
2279
- var SITE_STATUSES = [
2280
- "setup",
2281
- "active",
2282
- "disabled",
2283
- "archived"
2284
- ];
2285
- var siteStatusSchema = z25.enum(SITE_STATUSES);
2286
- var websiteUrlSchema = z25.string().trim().url().refine((value) => value.startsWith("https://") || value.startsWith("http://"), "Website URL must use http or https");
2287
-
2288
- // ../shared/dist/setup-assets.js
2289
- import { z as z26 } from "zod";
2290
- var configureSetupAssetTargetInputSchema = z26.object({
2291
- siteId: z26.string().uuid(),
2292
- runId: z26.string().uuid(),
2293
- expectedReviewVersion: z26.number().int().positive(),
2294
- expectedSnapshotHash: z26.string().regex(/^sha256:[0-9a-f]{64}$/u),
2295
- mutationId: z26.string().uuid(),
2296
- expectedApprovalId: z26.string().uuid().nullable(),
2297
- targetId: z26.string().uuid().nullable(),
2298
- targetType: z26.enum(["repo_static_assets", "siteplane_managed_fallback"]),
2299
- approveManagedHosting: z26.boolean()
2300
- }).strict().refine((value) => value.targetType === "siteplane_managed_fallback" || !value.approveManagedHosting);
2301
- var setupAssetTargetApprovalSchema = z26.object({
2302
- id: z26.string().uuid(),
2303
- runId: z26.string().uuid(),
2304
- reviewVersion: z26.number().int().positive(),
2305
- snapshotHash: z26.string().regex(/^sha256:[0-9a-f]{64}$/u),
2306
- targetId: z26.string().uuid(),
2307
- targetType: z26.enum(["repo_static_assets", "siteplane_managed_fallback"]),
2308
- targetRevision: z26.number().int().positive(),
2309
- deploymentOrigin: z26.string().url(),
2310
- gitConnectionId: z26.string().uuid().nullable(),
2311
- gitRevision: z26.number().int().positive().nullable(),
2312
- managedHostingApproved: z26.boolean(),
2313
- actorUserId: z26.string().uuid(),
2314
- approvedAt: z26.string().datetime({ offset: true })
2315
- }).strict();
2316
- var setupAssetReadinessSchema = z26.union([
2317
- z26.object({
2318
- status: z26.enum([
2319
- "not_required",
2320
- "needs_choice",
2321
- "stale_setup_review",
2322
- "permission_denied"
2323
- ])
2324
- }).strict(),
2325
- z26.object({
2326
- status: z26.literal("stale_approval"),
2327
- approval: setupAssetTargetApprovalSchema.optional()
2328
- }).strict(),
2329
- z26.object({
2330
- status: z26.literal("not_verified"),
2331
- approval: setupAssetTargetApprovalSchema,
2332
- jobId: z26.string().uuid().optional(),
2333
- checkedAt: z26.string().datetime({ offset: true }).nullable().optional()
2334
- }).strict(),
2335
- z26.object({
2336
- status: z26.enum(["ready", "checking", "retrying"]),
2337
- approval: setupAssetTargetApprovalSchema,
2338
- jobId: z26.string().uuid(),
2339
- checkedAt: z26.string().datetime({ offset: true }).nullable()
2340
- }).strict().refine((value) => value.status !== "ready" || value.checkedAt !== null)
2341
- ]);
2342
- var checkSetupAssetTargetInputSchema = z26.object({
2343
- siteId: z26.string().uuid(),
2344
- runId: z26.string().uuid(),
2345
- approvalId: z26.string().uuid(),
2346
- intent: z26.enum(["start", "poll"])
2347
- }).strict();
2348
-
2349
- // ../shared/dist/setup-installation.js
2350
- import { z as z27 } from "zod";
2351
- var setupInstallationRequestSchema = z27.object({
2352
- modules: siteSetupModulesSchema,
2353
- ownerEditabilityNote: siteSetupOwnerEditabilityNoteSchema,
2354
- provider: z27.object({
2355
- projectId: z27.string().min(1).max(200),
2356
- teamId: z27.string().min(1).max(200)
2357
- }).strict()
2358
- }).strict();
2359
-
2360
- // ../shared/dist/setup-claim.js
2361
- import { z as z28 } from "zod";
2362
- var setupClaimCodeSchema = z28.string().regex(/^[0-9A-HJKMNP-TV-Z]{8}$/u);
2363
- var setupClaimBindingSchema = z28.object({
2364
- siteId: z28.string().uuid(),
2365
- runId: z28.string().uuid()
2366
- });
2367
- var setupClaimReceiptSchema = setupClaimBindingSchema.extend({
2368
- status: z28.literal("claimed"),
2369
- requestId: z28.string().uuid(),
2370
- workspaceId: z28.string().uuid(),
2371
- userId: z28.string().uuid(),
2372
- claimedAt: z28.string().datetime({ offset: true })
2373
- }).strict();
2374
- var setupClaimInspectionSchema = setupClaimBindingSchema.extend({
2375
- status: z28.literal("pending"),
2376
- requestId: z28.string().uuid(),
2377
- siteName: z28.string(),
2378
- userId: z28.string().uuid(),
2379
- accountEmail: z28.string().email(),
2380
- workspaceId: z28.string().uuid(),
2381
- workspaceName: z28.string(),
2382
- expiresAt: z28.string().datetime({ offset: true })
2383
- }).strict();
2384
- var setupClaimDenialSchema = z28.object({
2385
- status: z28.enum([
2386
- "invalid_claim_request",
2387
- "invalid_installation",
2388
- "already_claimed",
2389
- "project_expired",
2390
- "invalid_guest_session",
2391
- "stale_setup_review",
2392
- "verified_account_required",
2393
- "workspace_access_denied",
2394
- "workspace_entitlement_required",
2395
- "site_limit_reached",
2396
- "provisional_relationship_conflict",
2397
- "rate_limited"
2398
- ])
2399
- }).strict();
2400
-
2401
- // ../cli/dist/analytics/static-scan.js
2402
- import ts from "typescript";
2403
-
2404
- // ../cli/dist/booking/manifest.js
2405
- import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
2406
- import { dirname as dirname2 } from "path";
2407
-
2408
- // ../cli/dist/booking/static-scan.js
2409
- import ts2 from "typescript";
2410
-
2411
- // ../cli/dist/commands/analytics/check.js
2412
- import { readFile as readFile3 } from "fs/promises";
2413
-
2414
- // ../cli/dist/commands/analytics/init.js
2415
- import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
2416
- import { dirname as dirname3, join } from "path";
2417
- var analyticsRulesTemplate = `# Siteplane Analytics Rules
2418
-
2419
- Analytics contract version: ${ANALYTICS_CONTRACT_VERSION}
2420
- Analytics contract hash: ${ANALYTICS_CONTRACT_HASH}
2421
-
2422
- - Do not change visible layout, copy, styling, animation or business logic while adding analytics.
2423
- - Use stable data-siteplane-target-id values that do not depend on visible copy.
2424
- - Use public site keys only through environment references.
2425
- - Do not write secret analytics keys, setup tokens or project CLI tokens into source files.
2426
- - Mount the SDK once in the existing client lifecycle. Wire installDataAttributeTracking and createNextAppRouterNavigationAdapter to analytics.track/getRouteViewId/setPageContext, feed initial and subsequent Next.js pathname changes, and refresh attribute targets after navigation. Dispose attribute tracking and the SDK on unmount.
2427
- - Inspect existing consent management. Pass consentAdapter with getConsent and subscribe to initSiteplaneAnalytics for an existing CMP; return unknown while unresolved, disabled for denial and a permitted mode only for its actual visitor grant. Subscribe returns cleanup. Do not mirror CMP snapshots with setConsent or replace stored opt-outs.
2428
- - Omit consentAdapter only after confirming no existing CMP. An unrecognized CMP blocks Analytics with a concrete repair diagnostic; do not invent a grant or a no-op adapter to pass a check.
2429
- - Suppress editor-preview traffic from live analytics.
2430
- - Run npx siteplane analytics check, npx siteplane analytics sync and npx siteplane analytics test after setup.
2431
- `;
2432
- var analyticsCursorRulesTemplate = `---
2433
- description: Siteplane Analytics rules
2434
- alwaysApply: true
2435
- ---
2436
-
2437
- ${analyticsRulesTemplate}`;
2438
-
2439
- // ../cli/dist/commands/analytics/import.js
2440
- import { createHash } from "crypto";
2441
-
2442
- // ../cli/dist/commands/analytics/sync.js
2443
- function createAnalyticsCliRequestHeaders(config) {
2444
- const headers = {
2445
- authorization: `Bearer ${config.accessToken}`,
2446
- "content-type": "application/json"
2447
- };
2448
- const bypassSecret = config.vercelAutomationBypassSecret?.trim();
2449
- if (bypassSecret) {
2450
- headers["x-vercel-protection-bypass"] = bypassSecret;
2451
- }
2452
- return headers;
2453
- }
2454
-
2455
- // ../cli/dist/commands/analytics/public-key.js
2456
- import { z as z29 } from "zod";
2457
- var responseSchema = z29.object({
2458
- ok: z29.literal(true),
2459
- data: z29.object({
2460
- rawPublicKey: z29.string().regex(/^pk_siteplane_[A-Za-z0-9_-]{16}_[A-Za-z0-9_-]{43}$/)
2461
- })
2462
- });
2463
- var errorResponseSchema = z29.object({
2464
- error: z29.object({
2465
- code: z29.string().regex(/^[a-z][a-z0-9_.-]+$/u)
2466
- })
2467
- });
2468
- async function analyticsPublicKeyCommand(input) {
2469
- const fetcher = input.fetchImpl ?? fetch;
2470
- const response = await fetcher(`${input.config.apiBaseUrl.replace(/\/$/u, "")}/api/cli/analytics/public-key`, {
2471
- method: "POST",
2472
- redirect: "error",
2473
- signal: AbortSignal.timeout(2e4),
2474
- headers: createAnalyticsCliRequestHeaders(input.config),
2475
- body: JSON.stringify({ siteId: input.config.siteId })
2476
- });
2477
- const payload = await response.json().catch(() => null);
2478
- const parsed = responseSchema.safeParse(payload);
2479
- if (!response.ok || !parsed.success) {
2480
- const error = errorResponseSchema.safeParse(payload);
2481
- throw new Error(`${error.success ? error.data.error.code : "analytics_public_key.invalid_response"}: Unable to read the active Analytics public key.`);
2482
- }
2483
- return parsed.data.data.rawPublicKey;
2484
- }
2485
-
2486
- // ../cli/dist/commands/analytics/prepare.js
2487
- import { z as z30 } from "zod";
2488
- var resultSchema = z30.object({
2489
- ok: z30.literal(true),
2490
- data: z30.object({
2491
- rawPublicKey: z30.string().regex(/^pk_siteplane_[A-Za-z0-9_-]{16}_[A-Za-z0-9_-]{43}$/u),
2492
- generation: z30.number().int().positive(),
2493
- status: z30.enum([
2494
- "setup_started",
2495
- "test_mode",
2496
- "active",
2497
- "paused",
2498
- "error",
2499
- "deleting"
2500
- ])
2501
- }).strict()
2502
- }).strict();
2503
- var errorSchema = z30.object({
2504
- error: z30.object({ code: z30.string().regex(/^[a-z][a-z0-9_.-]+$/u) })
2505
- });
2506
-
2507
- // ../cli/dist/commands/analytics/test.js
2508
- import { randomUUID } from "crypto";
2509
-
2510
- // ../cli/dist/commands/booking/check.js
2511
- import { dirname as dirname4, join as join2 } from "path";
2512
- import { access, readFile as readFile4 } from "fs/promises";
2513
-
2514
- // ../cli/dist/commands/booking/init.js
2515
- import { mkdir as mkdir4, writeFile as writeFile4 } from "fs/promises";
2516
- import { dirname as dirname5, join as join3 } from "path";
2517
- var bookingRulesTemplate = `# Siteplane Booking Rules
2518
-
2519
- - Do not change layout, copy, styling, animation or business logic while adding Booking.
2520
- - Integrate /booking on the customer domain through withSiteplane(nextConfig, { booking: { site: "<site-id>" } }); use the immutable Site ID supplied by Siteplane.
2521
- - Use a normal <a href="/booking"> link, not next/link, an iframe or an embed script.
2522
- - Preserve existing rewrites. Booking-only must not require Editor setup or siteplane.config.json.
2523
- - Configure the public booking address in Siteplane; register its origin and call https://siteplane.io/api/booking/* directly with CORS, without a raw public key.
2524
- - Preserve customer CSP and middleware; explain required Siteplane/Stripe permissions and exclude /booking from customer middleware.
2525
- - Do not write setup tokens, project CLI tokens or private keys into source files.
2526
- - Run npx siteplane booking check, npx siteplane booking sync and npx siteplane booking test after setup.
2527
- - booking test stays in test_mode; live booking only starts after explicit dashboard activation.
2528
- `;
2529
- var bookingCursorRulesTemplate = `---
2530
- description: Siteplane Booking rules
2531
- alwaysApply: true
2532
- ---
2533
-
2534
- ${bookingRulesTemplate}`;
2535
-
2536
- // ../cli/dist/config/project-config.js
2537
- import { access as access2, chmod, open, readFile as readFile5, rename, rm } from "fs/promises";
2538
- import { randomUUID as randomUUID2 } from "crypto";
2539
- import { join as join4 } from "path";
2540
- import { z as z31 } from "zod";
2541
- var siteplaneProjectConfigSchema = z31.object({
2542
- version: z31.literal(2),
2543
- apiBaseUrl: z31.string().url(),
2544
- siteId: z31.string().uuid(),
2545
- publicSiteKeyId: z31.string().uuid(),
2546
- publicSiteKey: z31.string().trim().min(1),
2547
- runId: z31.string().uuid(),
2548
- appDirectory: z31.string().min(1),
2549
- taskContractVersion: z31.literal("siteplane.setup-task.v2"),
2550
- adminProtocolVersion: z31.literal(2),
2551
- packages: siteSetupPackagesSchema,
2552
- fieldContractHash: z31.string().regex(/^sha256:[0-9a-f]{64}$/iu).optional()
2553
- }).strict();
2554
- async function readProjectConfig(projectDir) {
2555
- return siteplaneProjectConfigSchema.parse(JSON.parse(await readFile5(join4(projectDir, "siteplane.config.json"), "utf8")));
2556
- }
2557
-
2558
- // ../cli/dist/config/credentials.js
2559
- import { execFile } from "child_process";
2560
- import { randomUUID as randomUUID3 } from "crypto";
2561
- import { chmod as chmod2, lstat, mkdir as mkdir5, open as open2, readFile as readFile6, rename as rename2, rm as rm2 } from "fs/promises";
2562
- import { dirname as dirname6, join as join5, relative } from "path";
2563
- import { promisify } from "util";
2564
- import { z as z32 } from "zod";
2565
- var localSetupInstallationSchema = z32.object({
2566
- key: z32.string().regex(/^[A-Za-z0-9_-]{43}$/u),
2567
- idempotencyKey: z32.string().uuid(),
2568
- apiBaseUrl: z32.string().url(),
2569
- request: setupInstallationRequestSchema,
2570
- bootstrapAcknowledged: z32.boolean()
2571
- }).strict();
2572
- var siteplaneCredentialsSchema = z32.object({
2573
- accessToken: z32.string().trim().min(1),
2574
- siteRevalidationSecret: z32.string().trim().min(1),
2575
- installation: localSetupInstallationSchema.optional(),
2576
- claimReceipt: setupClaimReceiptSchema.optional(),
2577
- adminSession: z32.object({
2578
- secret: z32.string().regex(/^[A-Za-z0-9_-]{43}$/u),
2579
- generation: z32.string().uuid()
2580
- }).strict().optional()
2581
- }).strict();
2582
- var localSetupCredentialsSchema = z32.union([
2583
- siteplaneCredentialsSchema,
2584
- z32.object({ installation: localSetupInstallationSchema }).strict()
2585
- ]);
2586
- async function assertLocalCredentialsPathSafe(projectDir, options = {}) {
2587
- const path = join5(projectDir, ".siteplane/credentials.json");
2588
- await assertCredentialFilesystemSafe(projectDir);
2589
- const isTrackedFile = options.isTrackedFile ?? ((targetPath) => gitPathMatches(projectDir, targetPath, "ls-files"));
2590
- const isIgnoredFile = options.isIgnoredFile ?? ((targetPath) => gitPathMatches(projectDir, targetPath, "check-ignore"));
2591
- if (await isTrackedFile(path)) {
2592
- throw new Error("Refusing to write Siteplane credentials into a tracked file.");
2593
- }
2594
- if (!await isIgnoredFile(path)) {
2595
- throw new Error("Add .siteplane/credentials.json to .gitignore before running Siteplane init.");
2596
- }
2597
- return path;
2598
- }
2599
- async function assertCredentialFilesystemSafe(projectDir) {
2600
- for (const [path, directory, mode] of [
2601
- [projectDir, true, null],
2602
- [join5(projectDir, ".siteplane"), true, 448],
2603
- [join5(projectDir, ".siteplane/credentials.json"), false, 384]
2604
- ]) {
2605
- const stat = await lstat(path).catch((error) => {
2606
- if (error.code === "ENOENT")
2607
- return null;
2608
- throw error;
2609
- });
2610
- if (!stat)
2611
- continue;
2612
- if (stat.isSymbolicLink() || (directory ? !stat.isDirectory() : !stat.isFile())) {
2613
- throw new Error("Siteplane credential paths must not contain symlinks or unexpected file types.");
2614
- }
2615
- if (mode !== null && (stat.mode & 511) !== mode) {
2616
- throw new Error(`Siteplane credential ${directory ? "directory" : "file"} permissions must be ${mode.toString(8)} before setup.`);
2617
- }
2618
- }
2619
- }
2620
- async function readLocalCredentials(projectDir) {
2621
- const credentials = await readLocalSetupCredentials(projectDir);
2622
- if (!("accessToken" in credentials)) {
2623
- throw new Error("setup_exchange_pending: Run siteplane setup start to finish the stored installation exchange.");
2624
- }
2625
- return credentials;
2626
- }
2627
- async function readLocalSetupCredentials(projectDir) {
2628
- await assertCredentialFilesystemSafe(projectDir);
2629
- return localSetupCredentialsSchema.parse(JSON.parse(await readFile6(join5(projectDir, ".siteplane/credentials.json"), "utf8")));
2630
- }
2631
- var execFileAsync = promisify(execFile);
2632
- async function gitPathMatches(projectDir, path, command) {
2633
- const args = command === "check-ignore" ? ["check-ignore", "--quiet", relative(projectDir, path)] : ["ls-files", "--error-unmatch", relative(projectDir, path)];
2634
- try {
2635
- await execFileAsync("git", args, { cwd: projectDir });
2636
- return true;
2637
- } catch {
2638
- return false;
2639
- }
2640
- }
2641
-
2642
- // ../cli/dist/commands/init.js
2643
- import { access as access4 } from "fs/promises";
2644
- import { join as join8, relative as relative3 } from "path";
2645
- import { z as z35 } from "zod";
2646
-
2647
- // ../cli/dist/commands/setup-preflight.js
2648
- import { createHash as createHash2 } from "crypto";
2649
- import semver from "semver";
2650
- import { minimatch } from "minimatch";
2651
- import { parse as parseYaml } from "yaml";
2652
-
2653
- // ../cli/dist/config/installed-package.js
6
+ // src/next.ts
7
+ import { existsSync, readFileSync } from "fs";
8
+ import { access } from "fs/promises";
2654
9
  import { createRequire } from "module";
2655
- import { readFile as readFile7 } from "fs/promises";
2656
- import { join as join6 } from "path";
2657
- import { z as z33 } from "zod";
2658
- var packageSchema = z33.object({
2659
- name: z33.string().optional(),
2660
- version: z33.string(),
2661
- peerDependencies: z33.record(z33.string(), z33.string()).optional()
2662
- });
2663
- async function readInstalledPackage(projectDir, name2) {
2664
- const require2 = createRequire(join6(projectDir, "package.json"));
2665
- for (const directory of require2.resolve.paths(name2) ?? []) {
10
+ import { join } from "path";
11
+ var DRAFT_MODE_COOKIE = { type: "cookie", key: "__prerender_bypass" };
12
+ var siteIdPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
13
+ function withSiteplane(nextConfig, options = {}) {
14
+ return async (phase, context) => {
15
+ const base = await (typeof nextConfig === "function" ? nextConfig(phase, context) : nextConfig);
16
+ const projectDir = options.projectDir ?? process.cwd();
17
+ let config = base;
2666
18
  try {
2667
- return packageSchema.parse(JSON.parse(await readFile7(join6(directory, name2, "package.json"), "utf8")));
19
+ const project = readProjectConfig(projectDir);
20
+ if (project) config = withEditor(config, project, { projectDir, admin: options.admin === true });
2668
21
  } catch (error) {
2669
- if (error.code !== "ENOENT")
2670
- throw error;
22
+ console.warn(
23
+ `[Siteplane] ${SITEPLANE_CONFIG_FILE} could not be read, so the website builds without Siteplane content: ${error instanceof Error ? error.message : String(error)}`
24
+ );
2671
25
  }
2672
- }
2673
- throw new Error(`Install ${name2} with the canonical package manager before setup.`);
2674
- }
2675
-
2676
- // ../cli/dist/commands/setup-preflight.js
2677
- import { access as access3, readFile as readFile8, realpath, readdir, readlink } from "fs/promises";
2678
- import { dirname as dirname7, join as join7, relative as relative2, resolve, sep } from "path";
2679
- import { z as z34 } from "zod";
2680
-
2681
- // ../cli/dist/commands/setup-process.js
2682
- import { spawn } from "child_process";
2683
- var SetupFailure = class extends Error {
2684
- code;
2685
- constructor(code, message) {
2686
- super(message);
2687
- this.code = code;
2688
- }
2689
- };
2690
- async function setupProcess(binary, args, options) {
2691
- return new Promise((resolve5, reject) => {
2692
- const child = spawn(binary, args, {
2693
- cwd: options.cwd,
2694
- env: process.env,
2695
- stdio: ["pipe", "pipe", "pipe"]
2696
- });
2697
- const chunks = [];
2698
- const errors = [];
2699
- let errorBytes = 0;
2700
- let bytes = 0;
2701
- let exceeded = false;
2702
- const timer = setTimeout(() => {
2703
- exceeded = true;
2704
- child.kill("SIGKILL");
2705
- }, options.timeoutMs ?? 12e4);
2706
- const capture = (chunk) => {
2707
- bytes += chunk.length;
2708
- if (bytes > 4 * 1024 * 1024) {
2709
- exceeded = true;
2710
- child.kill("SIGKILL");
2711
- } else
2712
- chunks.push(chunk);
26
+ return options.booking ? await withBooking(config, options.booking.site, projectDir) : config;
27
+ };
28
+ }
29
+ function readProjectConfig(projectDir) {
30
+ const path = join(projectDir, SITEPLANE_CONFIG_FILE);
31
+ if (!existsSync(path)) return null;
32
+ const config = readSiteplaneConfig(JSON.parse(readFileSync(path, "utf8")));
33
+ if (!config) throw new Error("run npx siteplane init to repair it");
34
+ return config;
35
+ }
36
+ function withEditor(config, project, options) {
37
+ const configPath = join(options.projectDir, SITEPLANE_CONFIG_FILE);
38
+ const next = { ...config, ...moduleAlias(config, options.projectDir) };
39
+ next.webpack = (webpackConfig, context) => {
40
+ webpackConfig.resolve ??= {};
41
+ const alias = webpackConfig.resolve.alias;
42
+ if (Array.isArray(alias)) alias.push({ name: "siteplane/config", alias: configPath, onlyModule: true });
43
+ else webpackConfig.resolve.alias = Object.assign(alias ?? {}, { "siteplane/config$": configPath });
44
+ return config.webpack ? config.webpack(webpackConfig, context) : webpackConfig;
45
+ };
46
+ const api = new URL(project.apiOrigin);
47
+ if (api.protocol === "https:") {
48
+ const pattern = { protocol: "https", hostname: api.hostname, pathname: "/**" };
49
+ const patterns = config.images?.remotePatterns ?? [];
50
+ const known = patterns.some((entry) => JSON.stringify(entry) === JSON.stringify(pattern));
51
+ next.images = { ...config.images, remotePatterns: known ? patterns : [...patterns, pattern] };
52
+ }
53
+ const websiteHeaders = config.headers;
54
+ if (websiteHeaders) {
55
+ next.headers = async () => {
56
+ const rules = await websiteHeaders();
57
+ return [...rules, ...draftFrameRules(rules, project.editorOrigins)];
2713
58
  };
2714
- child.stdout.on("data", capture);
2715
- child.stderr.on("data", (chunk) => {
2716
- errorBytes += chunk.length;
2717
- if (errorBytes <= 4 * 1024 * 1024)
2718
- errors.push(chunk);
2719
- else {
2720
- exceeded = true;
2721
- child.kill("SIGKILL");
2722
- }
2723
- });
2724
- child.on("error", () => {
2725
- clearTimeout(timer);
2726
- reject(new SetupFailure("local_tool_unavailable", `${binary} could not be started. Check the local installation and access.`));
2727
- });
2728
- child.on("close", (code) => {
2729
- clearTimeout(timer);
2730
- if (exceeded)
2731
- reject(new SetupFailure("provider_timeout", "The operation exceeded its time or response limit; inspect the existing operation before retrying."));
2732
- else if (code !== 0) {
2733
- const diagnostic = Buffer.concat([...chunks, ...errors]).toString("utf8");
2734
- if (/\b(?:401|403|unauthorized|forbidden|not authenticated|not authorized|login required|E401|E403)\b/iu.test(diagnostic))
2735
- reject(new SetupFailure("provider_access_denied", `${binary} denied access. Restore the existing local login and project/package permissions before retrying.`));
2736
- else if (/\b(?:ECONNRESET|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|ECONNREFUSED|429|502|503|504)\b/iu.test(diagnostic))
2737
- reject(new SetupFailure("provider_transport_failed", `${binary} could not reach its provider. Inspect the existing operation before retrying.`));
2738
- else
2739
- reject(new SetupFailure("local_command_failed", `${binary} failed. Check its local login, permissions and project configuration.`));
2740
- } else
2741
- resolve5(Buffer.concat(options.includeStderr ? [...chunks, ...errors] : chunks).toString("utf8"));
2742
- });
2743
- child.stdin.on("error", () => {
2744
- });
2745
- child.stdin.end(options.input);
2746
- });
2747
- }
2748
- async function setupVercelApi(cwd, endpoint, body, method, options) {
2749
- const args = ["api", endpoint, "--raw"];
2750
- if (body !== void 0)
2751
- args.push("--method", method ?? "POST", "--input", "-");
2752
- else if (method)
2753
- args.push("--method", method);
2754
- let output = "";
2755
- for (let attempt = 0; attempt < 3; attempt++) {
2756
- const remaining = (options?.deadline ?? Date.now() + 3e4) - Date.now();
2757
- if (remaining <= 0)
2758
- throw new SetupFailure("provider_deadline_exceeded", "The provider deadline expired; inspect the recorded operation before continuing.");
2759
- try {
2760
- output = await setupProcess("vercel", args, {
2761
- cwd,
2762
- timeoutMs: Math.min(3e4, remaining),
2763
- ...body === void 0 ? {} : { input: JSON.stringify(body) }
2764
- });
2765
- break;
2766
- } catch (error) {
2767
- if (!(error instanceof SetupFailure) || error.code !== "provider_transport_failed" || attempt === 2)
2768
- throw error;
2769
- await new Promise((resolve5) => setTimeout(resolve5, Math.min(1e3 * 2 ** attempt, Math.max(0, (options?.deadline ?? Date.now() + 2e3) - Date.now()))));
2770
- }
2771
59
  }
2772
- try {
2773
- return JSON.parse(output);
2774
- } catch {
2775
- throw new SetupFailure("invalid_provider_response", "Vercel did not return valid JSON for the bound project.");
2776
- }
2777
- }
2778
-
2779
- // ../cli/dist/commands/setup-preflight.js
2780
- var manifestSchema = z34.object({
2781
- packageManager: z34.string().optional(),
2782
- engines: z34.object({ node: z34.string().optional() }).optional(),
2783
- dependencies: z34.record(z34.string(), z34.string()).optional(),
2784
- devDependencies: z34.record(z34.string(), z34.string()).optional(),
2785
- scripts: z34.record(z34.string(), z34.string()).optional(),
2786
- workspaces: z34.unknown().optional()
2787
- });
2788
- var vercelProjectSchema = z34.object({
2789
- id: z34.string(),
2790
- accountId: z34.string(),
2791
- name: z34.string(),
2792
- nodeVersion: z34.string().nullable().optional(),
2793
- rootDirectory: z34.string().nullable().optional(),
2794
- framework: z34.string().nullable().optional(),
2795
- installCommand: z34.string().nullable().optional(),
2796
- buildCommand: z34.string().nullable().optional(),
2797
- link: z34.object({
2798
- type: z34.string(),
2799
- repo: z34.string().optional(),
2800
- repoId: z34.union([z34.string(), z34.number()]).optional(),
2801
- org: z34.string().optional(),
2802
- productionBranch: z34.string().optional()
2803
- }).nullable().optional()
2804
- });
2805
- var exists = async (path) => access3(path).then(() => true, () => false);
2806
- var fail = (code, message) => {
2807
- throw new SetupFailure(code, message);
2808
- };
2809
- var stable = (version2, major) => new RegExp(`^${major}\\.\\d+\\.\\d+$`, "u").test(version2);
2810
- async function inspectSetupLocalProject(projectDir) {
2811
- const projectDirectory = await realpath(resolve(projectDir));
2812
- const manifest = manifestSchema.parse(JSON.parse(await readFile8(join7(projectDirectory, "package.json"), "utf8")));
2813
- const apps = (await Promise.all(["app", "src/app"].map(async (path) => await exists(join7(projectDirectory, path)) ? path : null))).filter((path) => path !== null);
2814
- if (apps.length !== 1)
2815
- fail("ambiguous_app_directory", "Select one Next.js App Router application with exactly one app/ or src/app/ directory.");
2816
- const repositoryRoot = (await setupProcess("git", ["rev-parse", "--show-toplevel"], {
2817
- cwd: projectDirectory
2818
- })).trim();
2819
- const tracked = await setupProcess("git", ["ls-files", "--stage", "-z"], {
2820
- cwd: repositoryRoot
2821
- });
2822
- for (const entry of tracked.split("\0").filter(Boolean)) {
2823
- if (entry.startsWith("160000 "))
2824
- fail("git_submodule_conflict", "The repository contains Git submodules. Resolve the explicit deployment source boundary before setup; the CLI will not copy external repositories.");
2825
- if (!entry.startsWith("120000 "))
2826
- continue;
2827
- const path = join7(repositoryRoot, entry.slice(entry.indexOf(" ") + 1));
2828
- const target = resolve(dirname7(path), await readlink(path));
2829
- const bound = relative2(repositoryRoot, target);
2830
- if (bound.startsWith("..") || bound.startsWith(sep))
2831
- 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.");
2832
- }
2833
- let lockfileRoot = projectDirectory;
2834
- const lockNames = [
2835
- "package-lock.json",
2836
- "npm-shrinkwrap.json",
2837
- "pnpm-lock.yaml",
2838
- "yarn.lock",
2839
- "bun.lock",
2840
- "bun.lockb"
2841
- ];
2842
- let locks = [];
2843
- for (; ; ) {
2844
- locks = (await Promise.all(lockNames.map(async (name3) => await exists(join7(lockfileRoot, name3)) ? name3 : null))).filter((name3) => name3 !== null);
2845
- if (locks.length || lockfileRoot === repositoryRoot || dirname7(lockfileRoot) === lockfileRoot)
2846
- break;
2847
- lockfileRoot = dirname7(lockfileRoot);
2848
- }
2849
- if (locks.length > 1)
2850
- fail("conflicting_lockfiles", "Resolve conflicting package-manager lockfiles before setup; Siteplane does not replace them.");
2851
- if (!locks.length)
2852
- fail("lockfile_missing", "Install the existing application with its chosen package manager to create one canonical lockfile.");
2853
- const lockName = locks[0];
2854
- if (!["package-lock.json", "pnpm-lock.yaml"].includes(lockName))
2855
- fail("unsupported_package_manager", "This setup supports npm 10 or pnpm 9/10 with their canonical lockfile.");
2856
- const rootManifest = manifestSchema.parse(JSON.parse(await readFile8(join7(lockfileRoot, "package.json"), "utf8")));
2857
- if (lockfileRoot !== projectDirectory) {
2858
- const workspaceFile = join7(lockfileRoot, "pnpm-workspace.yaml");
2859
- const workspace = lockName === "pnpm-lock.yaml" && await exists(workspaceFile) ? z34.object({ packages: z34.array(z34.string()) }).parse(parseYaml(await readFile8(workspaceFile, "utf8"))).packages : z34.union([
2860
- z34.array(z34.string()),
2861
- z34.object({ packages: z34.array(z34.string()) }).transform((value) => value.packages)
2862
- ]).parse(rootManifest.workspaces);
2863
- const selected = relative2(lockfileRoot, projectDirectory).split(sep).join("/");
2864
- if (!workspace.some((pattern) => !pattern.startsWith("!") && minimatch(selected, pattern)) || workspace.some((pattern) => pattern.startsWith("!") && minimatch(selected, pattern.slice(1))))
2865
- fail("unbound_workspace_app", "The selected application is excluded from the canonical lockfile's workspace packages. Select its actual application root.");
2866
- }
2867
- for (let ancestor = dirname7(lockfileRoot); ancestor.startsWith(repositoryRoot + sep) || ancestor === repositoryRoot; ancestor = dirname7(ancestor)) {
2868
- const ancestorLocks = await Promise.all(lockNames.map((name3) => exists(join7(ancestor, name3))));
2869
- if (ancestorLocks.some(Boolean))
2870
- fail("ambiguous_lockfile_root", "Both the selected package and its repository ancestors contain lockfiles. Select one canonical workspace resolution before setup.");
2871
- if (ancestor === repositoryRoot)
2872
- break;
2873
- }
2874
- const lockText = await readFile8(join7(lockfileRoot, lockName), "utf8");
2875
- const npmLock = lockName === "package-lock.json" ? z34.object({
2876
- lockfileVersion: z34.number(),
2877
- packages: z34.record(z34.string(), z34.object({
2878
- version: z34.string().optional(),
2879
- peerDependencies: z34.record(z34.string(), z34.string()).optional()
2880
- })).optional()
2881
- }).parse(JSON.parse(lockText)) : null;
2882
- async function version2(name3) {
2883
- try {
2884
- return (await readInstalledPackage(projectDirectory, name3)).version;
2885
- } catch {
2886
- const resolved = npmLock?.packages?.[`node_modules/${name3}`]?.version;
2887
- if (resolved)
2888
- return resolved;
2889
- return fail("dependencies_not_installed", `Install the existing ${name3} dependency using the canonical lockfile before setup.`);
2890
- }
2891
- }
2892
- const [nextVersion, reactVersion, reactDomVersion] = await Promise.all([
2893
- version2("next"),
2894
- version2("react"),
2895
- version2("react-dom")
2896
- ]);
2897
- if (!stable(nextVersion, 16))
2898
- fail("unsupported_next_version", "Setup supports stable Next.js 16 App Router projects. Framework migration requires a separate owner decision.");
2899
- if (!stable(reactVersion, 19) || reactDomVersion !== reactVersion)
2900
- fail("react_version_conflict", "Use the same stable React 19/react-dom version compatible with the installed Next.js peers.");
2901
- if (!stable(process.versions.node, 22))
2902
- fail("unsupported_node_version", "Run setup under Node.js 22; this does not change your framework or package-manager versions.");
2903
- const nextMetadata = await readInstalledPackage(projectDirectory, "next").catch(() => npmLock?.packages?.["node_modules/next"] ?? null);
2904
- const peers = nextMetadata?.peerDependencies;
2905
- if (!peers?.react || !peers["react-dom"])
2906
- fail("next_peer_metadata_missing", "Install the locked Next.js dependency before setup so its actual React peer requirements can be checked.");
2907
- if (!semver.satisfies(reactVersion, peers.react) || !semver.satisfies(reactDomVersion, peers["react-dom"]))
2908
- fail("react_peer_conflict", "The installed React pair does not satisfy the installed Next.js peer requirements. Choose a compatible pair explicitly.");
2909
- if (manifest.engines?.node && !semver.satisfies(process.versions.node, manifest.engines.node))
2910
- fail("node_engine_conflict", "The selected application's Node engine declaration excludes the active supported Node.js 22 runtime.");
2911
- const name2 = lockName === "package-lock.json" ? "npm" : "pnpm";
2912
- const declared = rootManifest.packageManager ?? manifest.packageManager ?? null;
2913
- if (rootManifest.packageManager && manifest.packageManager && rootManifest.packageManager !== manifest.packageManager)
2914
- fail("package_manager_conflict", "Application and workspace packageManager declarations disagree.");
2915
- if (declared && !new RegExp(`^${name2}@\\d+\\.\\d+\\.\\d+$`, "u").test(declared))
2916
- fail("package_manager_conflict", "Declare an exact supported npm/pnpm packageManager version matching the canonical lockfile.");
2917
- const localVersion = (await setupProcess(declared ? "npx" : name2, declared ? ["--yes", "--package", declared, name2, "--version"] : ["--version"], { cwd: lockfileRoot })).trim();
2918
- if (!(name2 === "npm" ? stable(localVersion, 10) : stable(localVersion, 9) || stable(localVersion, 10)))
2919
- fail("unsupported_package_manager_version", "Use npm 10 or pnpm 9/10; other versions are outside the initial setup support range.");
2920
- if (declared && declared !== `${name2}@${localVersion}`)
2921
- 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.");
2922
- const format = npmLock ? String(npmLock.lockfileVersion) : /^lockfileVersion:\s*['"]?([\d.]+)/mu.exec(lockText)?.[1];
2923
- if (name2 === "npm" && format !== "3" || name2 === "pnpm" && format !== "9.0" && format !== "9")
2924
- fail("lockfile_format_conflict", "The canonical lockfile format does not match npm 10 or pnpm 9/10.");
2925
- const sourceConflicts = [];
2926
- for (const file of ["next.config.js", "next.config.mjs", "next.config.ts"]) {
2927
- if (!await exists(join7(projectDirectory, file)))
2928
- continue;
2929
- const source = await readFile8(join7(projectDirectory, file), "utf8");
2930
- if (/output\s*:\s*['"]export['"]/u.test(source))
2931
- sourceConflicts.push("static_export");
2932
- if (/\bbasePath\s*:/u.test(source))
2933
- sourceConflicts.push("base_path");
2934
- if (/\bloaderFile\s*:|\bloader\s*:\s*['"](?:custom|imgix|cloudinary|akamai)['"]/u.test(source))
2935
- sourceConflicts.push(`inspect_image_loader:${file}`);
2936
- }
2937
- if (sourceConflicts.some((conflict) => conflict === "static_export" || conflict === "base_path"))
2938
- fail("unsupported_server_configuration", "Resolve static export/basePath configuration explicitly; Siteplane requires its server-side admin and revalidation handlers.");
2939
- for (const name3 of [
2940
- "middleware.ts",
2941
- "middleware.js",
2942
- "proxy.ts",
2943
- "proxy.js",
2944
- "src/middleware.ts",
2945
- "src/middleware.js",
2946
- "src/proxy.ts",
2947
- "src/proxy.js"
2948
- ]) {
2949
- if (await exists(join7(projectDirectory, name3))) {
2950
- sourceConflicts.push(`inspect_routing:${name3}`);
2951
- if (await readlink(join7(projectDirectory, name3)).catch(() => null))
2952
- continue;
2953
- const policySource = await readFile8(join7(projectDirectory, name3), "utf8");
2954
- if (/Content-Security-Policy|img-src|default-src/iu.test(policySource))
2955
- sourceConflicts.push(`inspect_image_csp:${name3}`);
2956
- }
2957
- }
2958
- async function inspectSource(directory) {
2959
- for (const entry of await readdir(directory, { withFileTypes: true })) {
2960
- if (entry.isSymbolicLink() || ["node_modules", ".git", ".next", ".siteplane", ".vercel"].includes(entry.name))
2961
- continue;
2962
- const path = join7(directory, entry.name);
2963
- if (entry.isDirectory()) {
2964
- await inspectSource(path);
2965
- continue;
2966
- }
2967
- if (!/\.[cm]?[jt]sx?$/u.test(entry.name))
2968
- continue;
2969
- const source = await readFile8(path, "utf8");
2970
- if (/Content-Security-Policy|img-src|default-src/iu.test(source))
2971
- sourceConflicts.push(`inspect_image_csp:${relative2(projectDirectory, path)}`);
2972
- if (/from\s*["']next\/image["']/u.test(source))
2973
- sourceConflicts.push(`inspect_image_policy:${relative2(projectDirectory, path)}`);
2974
- if (/\b(?:Cookiebot|OneTrust|cookieyes|consentManager|cookieConsent)\b/iu.test(source))
2975
- sourceConflicts.push(`inspect_existing_consent:${relative2(projectDirectory, path)}`);
2976
- if (/from\s*["']next\/image["']/u.test(source) && semver.lt(nextVersion, "16.3.3"))
2977
- 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.`);
2978
- if (/["']use server["']/u.test(source) && semver.lt(nextVersion, "16.2.11"))
2979
- 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.`);
2980
- }
60
+ if (options.admin) {
61
+ const portal = `${project.apiOrigin}/portal?site=${project.siteId}`;
62
+ const websiteRedirects = config.redirects;
63
+ next.redirects = async () => [
64
+ ...await websiteRedirects?.() ?? [],
65
+ { source: "/admin", destination: portal, permanent: false },
66
+ { source: "/admin/:path*", destination: portal, permanent: false }
67
+ ];
2981
68
  }
2982
- await inspectSource(join7(projectDirectory, apps[0]));
2983
- const gitRead = async (args) => setupProcess("git", args, { cwd: projectDirectory }).then((value) => value.trim(), () => null);
2984
- return {
2985
- projectDirectory,
2986
- repositoryRoot,
2987
- lockfileRoot,
2988
- appDirectory: apps[0],
2989
- nextVersion,
2990
- reactVersion,
2991
- nodeVersion: process.versions.node,
2992
- packageManager: { name: name2, version: localVersion, declared },
2993
- lockfile: {
2994
- path: relative2(repositoryRoot, join7(lockfileRoot, lockName)),
2995
- format,
2996
- sha256: createHash2("sha256").update(lockText).digest("hex")
2997
- },
2998
- git: {
2999
- branch: await gitRead(["branch", "--show-current"]),
3000
- revision: await gitRead(["rev-parse", "HEAD"]),
3001
- remote: safeGitRemote(await gitRead(["remote", "get-url", "origin"])),
3002
- changes: (await gitRead(["status", "--porcelain=v1", "--untracked-files=all"]))?.split("\n").filter(Boolean) ?? []
3003
- },
3004
- sourceConflicts
3005
- };
69
+ return next;
3006
70
  }
3007
- async function setupPreflightCommand(input) {
3008
- let local;
3009
- try {
3010
- local = await inspectSetupLocalProject(input.projectDir);
3011
- await assertLocalCredentialsPathSafe(input.projectDir);
3012
- let linkDirectory = local.projectDirectory;
3013
- while (!await exists(join7(linkDirectory, ".vercel/project.json")) && linkDirectory !== local.repositoryRoot && dirname7(linkDirectory) !== linkDirectory)
3014
- linkDirectory = dirname7(linkDirectory);
3015
- if (!await exists(join7(linkDirectory, ".vercel/project.json")))
3016
- fail("vercel_project_not_linked", "Link this application to its intended Vercel project before setup; Siteplane will not guess a target.");
3017
- const linked = z34.object({
3018
- projectId: z34.string().regex(/^prj_[A-Za-z0-9]+$/u),
3019
- orgId: z34.string().regex(/^(?:team|user)_[A-Za-z0-9]+$/u)
3020
- }).parse(JSON.parse(await readFile8(join7(linkDirectory, ".vercel/project.json"), "utf8")));
3021
- const project = vercelProjectSchema.parse(await setupVercelApi(linkDirectory, `/v9/projects/${linked.projectId}?teamId=${linked.orgId}`));
3022
- if (project.id !== linked.projectId || project.accountId !== linked.orgId)
3023
- fail("vercel_identity_conflict", "The provider project does not match the locally linked project and team.");
3024
- const expectedRoot = relative2(local.repositoryRoot, local.projectDirectory).split(sep).join("/");
3025
- if ((project.rootDirectory ?? "") !== expectedRoot)
3026
- fail("vercel_root_conflict", "Vercel Root Directory and the selected local app must identify the same application.");
3027
- if (project.nodeVersion !== "22.x" || project.framework !== "nextjs")
3028
- fail("vercel_framework_conflict", "The bound Vercel project must use Next.js with Node.js 22 before setup.");
3029
- if (project.link && project.link.productionBranch !== local.git.branch)
3030
- fail("vercel_production_branch_conflict", "The local branch differs from the bound Vercel Production branch.");
3031
- if (project.link?.repo && project.link.org && !local.git.remote?.replace(/\.git$/u, "").endsWith(`${project.link.org}/${project.link.repo}`))
3032
- fail("vercel_git_conflict", "The local origin repository differs from the Vercel Git connection.");
3033
- const env = z34.object({
3034
- envs: z34.array(z34.object({
3035
- key: z34.string(),
3036
- value: z34.string().optional(),
3037
- target: z34.array(z34.string()).optional()
3038
- }))
3039
- }).parse(await setupVercelApi(linkDirectory, `/v10/projects/${linked.projectId}/env?teamId=${linked.orgId}`));
3040
- const corepack = env.envs.some((item) => item.key === "ENABLE_EXPERIMENTAL_COREPACK" && item.value === "1" && item.target?.includes("production"));
3041
- const install = project.installCommand ?? null;
3042
- const selected = corepack ? "corepack" : install ? "install_override" : "automatic";
3043
- if (local.packageManager.declared && selected === "automatic")
3044
- 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.");
3045
- if (install && /\b(?:yarn|bun)\b/u.test(install))
3046
- fail("vercel_package_manager_conflict", "The Vercel install override selects an unsupported package manager.");
3047
- const overridePin = install ? /\b(npm|pnpm)@(\d+\.\d+\.\d+)\b/u.exec(install) : null;
3048
- if (overridePin && overridePin[1] !== local.packageManager.name)
3049
- fail("vercel_package_manager_conflict", "The provider override selects a different package manager than the canonical lockfile.");
3050
- const overrideFamily = install ? /(?:^|[;&|]\s*)(npm|pnpm)\s+(?:ci|install)\b/u.exec(install)?.[1] : null;
3051
- if (overrideFamily && overrideFamily !== local.packageManager.name)
3052
- fail("vercel_package_manager_conflict", "The provider install command conflicts with the canonical lockfile.");
3053
- if (overridePin && local.packageManager.declared && `${overridePin[1]}@${overridePin[2]}` !== local.packageManager.declared)
3054
- fail("vercel_package_manager_conflict", "The Vercel install override conflicts with the exact packageManager declaration.");
3055
- const buildRunners = (project.buildCommand ?? "").matchAll(/(?:^|[;&|\n])\s*(?:(?:corepack|npx(?:\s+(?:--yes|-y))?)\s+)?(npm|pnpm|yarn|bun)(?:@(\d+\.\d+\.\d+))?\s+(?:run\s+)?build\b/gu);
3056
- for (const [, family, version2] of buildRunners) {
3057
- if (family !== local.packageManager.name || version2 && local.packageManager.declared && `${family}@${version2}` !== local.packageManager.declared)
3058
- fail("vercel_package_manager_conflict", "The explicit Vercel build command conflicts with the selected package manager or its declared version. Align the existing build override and rerun preflight; no setup writes were performed.");
3059
- }
71
+ function moduleAlias(config, projectDir) {
72
+ const alias = { "siteplane/config": `./${SITEPLANE_CONFIG_FILE}` };
73
+ if (usesExperimentalTurbo(projectDir)) {
74
+ const turbo = config.experimental?.turbo;
3060
75
  return {
3061
- status: "ready",
3062
- local,
3063
- provider: {
3064
- projectId: project.id,
3065
- teamId: project.accountId,
3066
- name: project.name,
3067
- rootDirectory: expectedRoot,
3068
- nodeVersion: project.nodeVersion,
3069
- git: project.link,
3070
- installCommand: install,
3071
- buildCommand: project.buildCommand ?? null,
3072
- packageManagerSelection: selected,
3073
- declaredPackageManager: local.packageManager.declared,
3074
- observedPackageManager: null
3075
- }
3076
- };
3077
- } catch (error) {
3078
- return {
3079
- status: "action_required",
3080
- code: error instanceof SetupFailure ? error.code : "invalid_local_project",
3081
- message: error instanceof SetupFailure ? error.message : "The selected project or provider response could not be validated. Check the application manifest and local project access.",
3082
- ...local ? { local } : {}
3083
- };
3084
- }
3085
- }
3086
- function safeGitRemote(value) {
3087
- if (!value)
3088
- return null;
3089
- if (!value.includes("://"))
3090
- return value;
3091
- try {
3092
- const url = new URL(value);
3093
- url.username = "";
3094
- url.password = "";
3095
- url.search = "";
3096
- url.hash = "";
3097
- return url.toString().replace(/\/$/u, "");
3098
- } catch {
3099
- return null;
3100
- }
3101
- }
3102
-
3103
- // ../cli/dist/commands/init.js
3104
- var siteSetupBootstrapDataSchema = z35.object({
3105
- credentialPurpose: z35.literal("site_setup"),
3106
- apiBaseUrl: z35.string().url(),
3107
- siteId: z35.string().uuid(),
3108
- publicSiteKeyId: z35.string().uuid(),
3109
- publicSiteKey: z35.string().trim().min(1),
3110
- accessToken: z35.string().trim().min(1),
3111
- siteRevalidationSecret: z35.string().regex(/^[A-Za-z0-9_-]{43}$/u),
3112
- runId: z35.string().uuid(),
3113
- packages: siteSetupPackagesSchema
3114
- }).strict();
3115
- var featureBootstrapDataSchema = z35.object({
3116
- credentialPurpose: z35.enum(["analytics", "booking"]),
3117
- apiBaseUrl: z35.string().url(),
3118
- siteId: z35.string().uuid(),
3119
- accessToken: z35.string().trim().min(1)
3120
- }).strict();
3121
- var bootstrapResponseSchema = z35.object({
3122
- ok: z35.literal(true),
3123
- data: z35.discriminatedUnion("credentialPurpose", [
3124
- siteSetupBootstrapDataSchema,
3125
- featureBootstrapDataSchema
3126
- ])
3127
- }).strict();
3128
-
3129
- // ../cli/dist/commands/scan.js
3130
- import { readdir as readdir2 } from "fs/promises";
3131
- import { extname, join as join9 } from "path";
3132
- var defaultScanRoots = ["app", "src", "pages", "components", "lib"];
3133
- var excludedDirectories = /* @__PURE__ */ new Set([
3134
- ".git",
3135
- ".next",
3136
- ".turbo",
3137
- ".vercel",
3138
- "build",
3139
- "coverage",
3140
- "dist",
3141
- "node_modules"
3142
- ]);
3143
- var sourceExtensions = /* @__PURE__ */ new Set([".js", ".jsx", ".ts", ".tsx"]);
3144
- async function resolveScanFilePaths(filePaths, options = {}) {
3145
- if (filePaths.length > 0) {
3146
- return filePaths;
3147
- }
3148
- const projectDir = options.projectDir ?? process.cwd();
3149
- const discovered = await Promise.all(defaultScanRoots.map((root) => collectSourceFiles(join9(projectDir, root))));
3150
- return [...new Set(discovered.flat())].sort();
3151
- }
3152
- async function collectSourceFiles(directory) {
3153
- const entries = await readdir2(directory, { withFileTypes: true }).catch(() => []);
3154
- const files = [];
3155
- for (const entry of entries) {
3156
- const entryPath = join9(directory, entry.name);
3157
- if (entry.isDirectory()) {
3158
- if (!excludedDirectories.has(entry.name)) {
3159
- files.push(...await collectSourceFiles(entryPath));
3160
- }
3161
- continue;
3162
- }
3163
- if (entry.isFile() && sourceExtensions.has(extname(entry.name))) {
3164
- files.push(entryPath);
3165
- }
3166
- }
3167
- return files;
3168
- }
3169
-
3170
- // ../cli/dist/commands/site-setup.js
3171
- import { execFile as execFile2 } from "child_process";
3172
- import { readFile as readFile9 } from "fs/promises";
3173
- import { promisify as promisify2 } from "util";
3174
- import { z as z36 } from "zod";
3175
-
3176
- // ../cli/dist/scan/next-source-scan.js
3177
- import ts3 from "typescript";
3178
- import { isAbsolute, relative as relative4 } from "path";
3179
- var eventPreviewHandlerMissingMessage = "Event preview fields must consume preview values with useSiteplanePreviewText from @siteplane/runtime-next/client or a siteplane:preview-value-applied listener.";
3180
- var valueCallUnusedMessage = "Siteplane value calls must feed rendered content. Assign or await the returned value and render that value instead of ignoring the call.";
3181
- var legacyEditableComponentPrefix = "Editable";
3182
- var editableComponentNames = new Set(["Image", "Text", "LongText", "Link"].map((name2) => `${legacyEditableComponentPrefix}${name2}`));
3183
- var functionTypeByName = {
3184
- text: "text",
3185
- longText: "longText",
3186
- link: "link",
3187
- image: "image"
3188
- };
3189
- function scanNextSourceFilesToSiteFieldContract(files, options) {
3190
- const scan = scanSourceFacts(files);
3191
- const fields = [];
3192
- for (const field of uniqueFields(scan.fields)) {
3193
- const routeKey = field.technicalRouteKey ?? field.routeHint;
3194
- if (routeKey === "/*") {
3195
- scan.warnings.push({
3196
- code: "field_scan.global_route_key_invalid",
3197
- message: `Field ${field.fieldId} uses /* as its routeKey. Use $global in both the value call and DOM attrs for content shared across routes; /* cannot produce matching editor evidence. This affects the editor integration, not the website build.`,
3198
- fieldId: field.fieldId,
3199
- filePath: field.sourceFilePath ?? "unknown"
3200
- });
3201
- }
3202
- if (!routeKey) {
3203
- scan.hardErrors.push({
3204
- code: "field_scan.route_key_missing",
3205
- message: `Field ${field.fieldId} has no canonical route key.`,
3206
- fieldId: field.fieldId,
3207
- filePath: field.sourceFilePath ?? "unknown"
3208
- });
3209
- continue;
3210
- }
3211
- const source = {
3212
- kind: "function",
3213
- filePath: toRepositoryRelativePosixPath(field.sourceFilePath ?? "unknown", options.projectRoot)
3214
- };
3215
- assignIfDefined(source, "exportName", field.sourceExportName);
3216
- assignIfDefined(source, "sourcePath", field.sourcePath);
3217
- assignIfDefined(source, "editTarget", field.editTarget);
3218
- const parsed = siteFieldContractFieldSchema.safeParse({
3219
- fieldId: field.fieldId,
3220
- fieldType: field.fieldType,
3221
- fallback: field.fallback,
3222
- routeKey,
3223
- source,
3224
- ...field.hasDomBinding ? {
3225
- dom: {
3226
- attribute: "data-siteplane-field-id",
3227
- previewStrategy: field.previewStrategy ?? "dom"
3228
- }
3229
- } : {}
3230
- });
3231
- if (!parsed.success) {
3232
- scan.hardErrors.push({
3233
- code: "field_scan.invalid_site_field_contract",
3234
- message: parsed.error.issues[0]?.message ?? "Invalid field contract.",
3235
- fieldId: field.fieldId,
3236
- filePath: field.sourceFilePath ?? "unknown"
3237
- });
3238
- continue;
3239
- }
3240
- fields.push(parsed.data);
3241
- }
3242
- return {
3243
- contract: siteFieldContractV1Schema.parse({
3244
- version: 1,
3245
- fieldContractVersion,
3246
- scannerContractVersion,
3247
- fields
3248
- }),
3249
- hardErrors: scan.hardErrors,
3250
- warnings: scan.warnings,
3251
- suggestions: scan.suggestions
3252
- };
3253
- }
3254
- function scanSourceFacts(files) {
3255
- const fields = [];
3256
- const hardErrors = [];
3257
- const warnings = [];
3258
- const suggestions = [];
3259
- const directAttrs = [];
3260
- const eventPreviewAttrs = [];
3261
- const eventPreviewComponentPropConsumers = [];
3262
- const eventPreviewComponentPropUsages = [];
3263
- const eventPreviewConsumerFieldIds = /* @__PURE__ */ new Set();
3264
- let hasGenericEventPreviewConsumer = false;
3265
- for (const file of files) {
3266
- const facts = scanSourceFile(file, fields, hardErrors);
3267
- directAttrs.push(...facts.directAttrs);
3268
- eventPreviewAttrs.push(...facts.eventPreviewAttrs);
3269
- eventPreviewComponentPropConsumers.push(...facts.eventPreviewComponentPropConsumers);
3270
- eventPreviewComponentPropUsages.push(...facts.eventPreviewComponentPropUsages);
3271
- facts.eventPreviewConsumerFieldIds.forEach((fieldId) => {
3272
- eventPreviewConsumerFieldIds.add(fieldId);
3273
- });
3274
- hasGenericEventPreviewConsumer ||= facts.hasGenericEventPreviewConsumer;
3275
- }
3276
- for (const usage of eventPreviewComponentPropUsages) {
3277
- if (eventPreviewComponentPropConsumers.some((consumer) => consumer.componentName === usage.componentName && consumer.propName === usage.propName)) {
3278
- eventPreviewConsumerFieldIds.add(usage.fieldId);
3279
- }
3280
- }
3281
- for (const attr of directAttrs) {
3282
- const field = fields.find((candidate) => candidate.fieldId === attr.fieldId);
3283
- if (field && attr.fieldType !== field.fieldType) {
3284
- warnings.push({
3285
- code: "field_scan.attrs_field_type_mismatch",
3286
- message: `DOM attrs for ${attr.fieldId} declare ${attr.fieldType}, but its value call returns ${field.fieldType}. Pass fieldType: "${field.fieldType}" with the matching routeKey to siteplane.attrs on the visible element. This affects editor preview, not the website build.`,
3287
- fieldId: attr.fieldId,
3288
- filePath: attr.filePath
3289
- });
3290
- }
3291
- const expectedRoute = field?.technicalRouteKey ?? field?.routeHint;
3292
- if (!attr.routeKey || expectedRoute && attr.routeKey !== expectedRoute) {
3293
- hardErrors.push({
3294
- code: attr.routeKey ? "field_scan.attrs_route_key_mismatch" : "field_scan.attrs_route_key_missing",
3295
- message: `DOM attrs for ${attr.fieldId} must include the same canonical routeKey as its value call${expectedRoute ? ` (${expectedRoute})` : ""}. Pass it explicitly to siteplane.attrs(fieldId, { routeKey }); the value call does not attach DOM metadata.`,
3296
- fieldId: attr.fieldId,
3297
- filePath: attr.filePath
3298
- });
3299
- }
3300
- }
3301
- markMissingEventPreviewHandlerErrors(eventPreviewAttrs, eventPreviewConsumerFieldIds, hasGenericEventPreviewConsumer, hardErrors);
3302
- validateScannedFields(fields, hardErrors, warnings, suggestions);
3303
- return {
3304
- fields,
3305
- hardErrors,
3306
- warnings,
3307
- suggestions
3308
- };
3309
- }
3310
- function uniqueFields(fields) {
3311
- const seen = /* @__PURE__ */ new Set();
3312
- const unique = [];
3313
- for (const field of fields) {
3314
- if (seen.has(field.fieldId)) {
3315
- continue;
3316
- }
3317
- seen.add(field.fieldId);
3318
- unique.push(field);
3319
- }
3320
- return unique;
3321
- }
3322
- function scanSourceFile(file, fields, hardErrors) {
3323
- const fieldsInFile = [];
3324
- const attrFieldIds = /* @__PURE__ */ new Set();
3325
- const attrRouteRefs = /* @__PURE__ */ new Set();
3326
- const directAttrs = [];
3327
- const eventPreviewAttrs = [];
3328
- const eventPreviewAttrVariables = /* @__PURE__ */ new Map();
3329
- const eventPreviewComponentPropConsumers = [];
3330
- const eventPreviewComponentPropUsages = [];
3331
- const eventPreviewConsumerFieldIds = /* @__PURE__ */ new Set();
3332
- let hasGenericEventPreviewConsumer = false;
3333
- const sourceFile = ts3.createSourceFile(file.filePath, file.sourceText, ts3.ScriptTarget.Latest, true, ts3.ScriptKind.TSX);
3334
- function visit(node, currentComponentName) {
3335
- const nextComponentName = getFunctionComponentName(node) ?? currentComponentName;
3336
- if (ts3.isJsxSelfClosingElement(node) || ts3.isJsxOpeningElement(node)) {
3337
- const componentName = getJsxTagName(node.tagName);
3338
- if (editableComponentNames.has(componentName)) {
3339
- hardErrors.push({
3340
- code: "field_scan.value_first_required",
3341
- message: "Siteplane MVP uses value-first APIs. Replace this field with siteplane.route(...) or a field value call.",
3342
- filePath: file.filePath
3343
- });
3344
- }
3345
- collectEventPreviewComponentPropUsages(node, eventPreviewAttrVariables, eventPreviewComponentPropUsages, file);
3346
- }
3347
- if (ts3.isCallExpression(node)) {
3348
- collectAttrsCall(node, attrFieldIds, attrRouteRefs, eventPreviewAttrs, eventPreviewAttrVariables, directAttrs, file);
3349
- collectUnusedValueCallError(node, hardErrors, file);
3350
- const extractedFields = extractFunctionFields(node, file);
3351
- for (const field of extractedFields) {
3352
- fieldsInFile.push(field);
3353
- fields.push(field);
3354
- }
3355
- const previewHookFieldId = getPreviewHookFieldId(node, eventPreviewAttrVariables);
3356
- if (previewHookFieldId) {
3357
- eventPreviewConsumerFieldIds.add(previewHookFieldId);
3358
- } else if (nextComponentName) {
3359
- const previewHookPropName = getPreviewHookPropName(node, eventPreviewAttrVariables);
3360
- if (previewHookPropName) {
3361
- eventPreviewComponentPropConsumers.push({
3362
- componentName: nextComponentName,
3363
- propName: previewHookPropName
3364
- });
3365
- }
3366
- }
3367
- }
3368
- if (isGenericEventPreviewConsumerReference(node)) {
3369
- hasGenericEventPreviewConsumer = true;
3370
- }
3371
- ts3.forEachChild(node, (child) => visit(child, nextComponentName));
3372
- }
3373
- visit(sourceFile);
3374
- markMissingAttrsWarnings(fieldsInFile, attrFieldIds, attrRouteRefs);
3375
- return {
3376
- directAttrs,
3377
- eventPreviewAttrs,
3378
- eventPreviewComponentPropConsumers,
3379
- eventPreviewComponentPropUsages,
3380
- eventPreviewConsumerFieldIds: [...eventPreviewConsumerFieldIds],
3381
- hasGenericEventPreviewConsumer
3382
- };
3383
- }
3384
- function collectUnusedValueCallError(node, hardErrors, file) {
3385
- const valueFunctionName = getSiteplaneValueFunctionName(node);
3386
- if (!valueFunctionName || !isIgnoredValueCall(node)) {
3387
- return;
3388
- }
3389
- const fieldId = valueFunctionName === "route" ? void 0 : stringValue(staticExpressionValue(node.arguments[0]));
3390
- hardErrors.push({
3391
- code: "field_scan.value_call_unused",
3392
- message: valueCallUnusedMessage,
3393
- ...fieldId ? { fieldId } : {},
3394
- filePath: file.filePath
3395
- });
3396
- }
3397
- function getSiteplaneValueFunctionName(node) {
3398
- if (!ts3.isPropertyAccessExpression(node.expression)) {
3399
- return void 0;
3400
- }
3401
- if (node.expression.expression.getText() !== "siteplane") {
3402
- return void 0;
3403
- }
3404
- const functionName = node.expression.name.text;
3405
- if (functionName === "route" || functionTypeByName[functionName]) {
3406
- return functionName;
3407
- }
3408
- return void 0;
3409
- }
3410
- function isIgnoredValueCall(node) {
3411
- if (ts3.isVoidExpression(node.parent)) {
3412
- return true;
3413
- }
3414
- if (ts3.isExpressionStatement(node.parent)) {
3415
- return true;
3416
- }
3417
- return ts3.isAwaitExpression(node.parent) && ts3.isExpressionStatement(node.parent.parent);
3418
- }
3419
- function extractFunctionFields(node, file) {
3420
- if (!ts3.isPropertyAccessExpression(node.expression)) {
3421
- return [];
3422
- }
3423
- if (node.expression.expression.getText() !== "siteplane") {
3424
- return [];
3425
- }
3426
- const functionName = node.expression.name.text;
3427
- if (functionName === "route") {
3428
- return extractRouteFields(node, file);
3429
- }
3430
- const fieldType = functionTypeByName[functionName];
3431
- if (!fieldType) {
3432
- return [];
3433
- }
3434
- const fieldId = staticExpressionValue(node.arguments[0]);
3435
- if (typeof fieldId !== "string") {
3436
- return [];
3437
- }
3438
- const input = {
3439
- fieldId,
3440
- fieldType,
3441
- fallback: staticExpressionValue(node.arguments[1]),
3442
- source: "function",
3443
- sourceFilePath: file.filePath,
3444
- sourceExportName: functionName,
3445
- editTarget: fieldId
3446
- };
3447
- const routeHint = inferRouteHint(file.filePath);
3448
- const options = staticExpressionValue(node.arguments[2]);
3449
- const fieldOptions = options && typeof options === "object" && !Array.isArray(options) ? options : {};
3450
- assignIfDefined(input, "routeHint", routeHint);
3451
- assignIfDefined(input, "technicalRouteKey", stringValue(fieldOptions.routeKey) ?? routeHint);
3452
- assignIfDefined(input, "previewStrategy", previewStrategyValue(fieldOptions.previewStrategy));
3453
- assignIfDefined(input, "label", stringValue(fieldOptions.label));
3454
- assignIfDefined(input, "description", stringValue(fieldOptions.description));
3455
- assignIfDefined(input, "required", booleanValue(fieldOptions.required));
3456
- assignIfDefined(input, "maxLength", numberValue(fieldOptions.maxLength));
3457
- return [createField(input)];
3458
- }
3459
- function extractRouteFields(node, file) {
3460
- const routeId = staticExpressionValue(node.arguments[0]);
3461
- const fallbacks = staticExpressionValue(node.arguments[1]);
3462
- const options = staticExpressionValue(node.arguments[2]);
3463
- if (typeof routeId !== "string" || !fallbacks || typeof fallbacks !== "object" || Array.isArray(fallbacks)) {
3464
- return [];
3465
- }
3466
- const routeVariableName = getAssignedVariableName(node);
3467
- const fieldsConfig = options && typeof options === "object" && !Array.isArray(options) ? options.fields : void 0;
3468
- const routeHint = inferRouteHint(file.filePath);
3469
- const fields = [];
3470
- for (const [key, fallback] of Object.entries(fallbacks)) {
3471
- const config = fieldsConfig && typeof fieldsConfig === "object" && !Array.isArray(fieldsConfig) ? fieldsConfig[key] : void 0;
3472
- if (!config || typeof config !== "object" || Array.isArray(config)) {
3473
- continue;
3474
- }
3475
- const fieldConfig = config;
3476
- const fieldId = stringValue(fieldConfig.id);
3477
- const fieldType = fieldTypeValue(fieldConfig.type);
3478
- if (!fieldId || !fieldType) {
3479
- continue;
3480
- }
3481
- const input = {
3482
- fieldId,
3483
- fieldType,
3484
- fallback,
3485
- source: "function",
3486
- sourceFilePath: file.filePath,
3487
- sourceExportName: "route",
3488
- routeId,
3489
- sourcePath: `$.${key}`,
3490
- editTarget: fieldId,
3491
- routeFieldKey: key
76
+ experimental: { ...config.experimental, turbo: { ...turbo, resolveAlias: { ...turbo?.resolveAlias, ...alias } } }
3492
77
  };
3493
- assignIfDefined(input, "routeVariableName", routeVariableName);
3494
- assignIfDefined(input, "routeHint", routeHint);
3495
- assignIfDefined(input, "technicalRouteKey", stringValue(fieldConfig.routeKey) ?? routeHint);
3496
- assignIfDefined(input, "previewStrategy", previewStrategyValue(fieldConfig.previewStrategy));
3497
- assignIfDefined(input, "label", stringValue(fieldConfig.label));
3498
- assignIfDefined(input, "description", stringValue(fieldConfig.description));
3499
- assignIfDefined(input, "required", booleanValue(fieldConfig.required));
3500
- assignIfDefined(input, "maxLength", numberValue(fieldConfig.maxLength));
3501
- fields.push(createField(input));
3502
78
  }
3503
- return fields;
79
+ return { turbopack: { ...config.turbopack, resolveAlias: { ...config.turbopack?.resolveAlias, ...alias } } };
3504
80
  }
3505
- function createField(input) {
3506
- const field = {
3507
- fieldId: input.fieldId,
3508
- fieldType: input.fieldType,
3509
- fallback: input.fallback,
3510
- source: input.source,
3511
- sourceFilePath: input.sourceFilePath,
3512
- warnings: [],
3513
- suggestions: []
3514
- };
3515
- assignIfDefined(field, "label", input.label);
3516
- assignIfDefined(field, "description", input.description);
3517
- assignIfDefined(field, "required", input.required);
3518
- assignIfDefined(field, "maxLength", input.maxLength);
3519
- assignIfDefined(field, "sourceExportName", input.sourceExportName);
3520
- assignIfDefined(field, "componentHint", input.componentHint);
3521
- assignIfDefined(field, "routeHint", input.routeHint);
3522
- assignIfDefined(field, "routeId", input.routeId);
3523
- assignIfDefined(field, "sourcePath", input.sourcePath);
3524
- assignIfDefined(field, "editTarget", input.editTarget);
3525
- assignMetadata(field, {
3526
- canonicalFallbackHash: createFallbackHash(input.fieldType, input.fallback)
3527
- });
3528
- assignIfDefined(field, "routeVariableName", input.routeVariableName);
3529
- assignIfDefined(field, "routeFieldKey", input.routeFieldKey);
3530
- assignIfDefined(field, "technicalRouteKey", input.technicalRouteKey);
3531
- assignIfDefined(field, "previewStrategy", input.previewStrategy);
3532
- if (field.fieldType === "text" && field.maxLength === void 0) {
3533
- field.warnings.push("field_scan.text_max_length_missing");
3534
- }
3535
- if (isPositionalFieldReference(field.fieldId) || isPositionalFieldReference(field.sourcePath)) {
3536
- field.warnings.push("field_scan.positional_repeating_id");
3537
- }
3538
- return field;
3539
- }
3540
- function assignMetadata(field, metadata) {
3541
- const entries = Object.entries(metadata).filter(([, value]) => value !== void 0);
3542
- if (entries.length === 0) {
3543
- return;
3544
- }
3545
- field.metadata = {
3546
- ...field.metadata ?? {},
3547
- ...Object.fromEntries(entries)
3548
- };
3549
- }
3550
- function createFallbackHash(fieldType, fallback) {
81
+ function usesExperimentalTurbo(projectDir) {
3551
82
  try {
3552
- return createCanonicalFallbackHash({ fieldType, fallbackValue: fallback });
83
+ const manifest = createRequire(join(projectDir, "package.json")).resolve("next/package.json");
84
+ const [major = 0, minor = 0] = JSON.parse(readFileSync(manifest, "utf8")).version.split(".").map(Number);
85
+ return major < 15 || major === 15 && minor < 3;
3553
86
  } catch {
3554
- return void 0;
3555
- }
3556
- }
3557
- function collectAttrsCall(node, attrFieldIds, attrRouteRefs, eventPreviewAttrs, eventPreviewAttrVariables, directAttrs, file) {
3558
- if (!ts3.isPropertyAccessExpression(node.expression)) {
3559
- return;
3560
- }
3561
- if (node.expression.expression.getText() !== "siteplane" || node.expression.name.text !== "attrs") {
3562
- return;
3563
- }
3564
- const firstArg = node.arguments[0];
3565
- const secondArg = staticExpressionValue(node.arguments[1]);
3566
- const directFieldId = staticExpressionValue(firstArg);
3567
- if (typeof directFieldId === "string") {
3568
- attrFieldIds.add(directFieldId);
3569
- directAttrs.push({
3570
- fieldId: directFieldId,
3571
- routeKey: secondArg && typeof secondArg === "object" && "routeKey" in secondArg ? stringValue(secondArg.routeKey) : void 0,
3572
- fieldType: secondArg && typeof secondArg === "object" && "fieldType" in secondArg ? stringValue(secondArg.fieldType) ?? "text" : "text",
3573
- filePath: file.filePath
3574
- });
3575
- if (hasEventPreviewStrategy(secondArg)) {
3576
- eventPreviewAttrs.push({
3577
- fieldId: directFieldId,
3578
- filePath: file.filePath
3579
- });
3580
- const variableName = getAssignedVariableName(node);
3581
- if (variableName) {
3582
- eventPreviewAttrVariables.set(variableName, directFieldId);
3583
- }
3584
- }
3585
- return;
3586
- }
3587
- if (firstArg && ts3.isIdentifier(firstArg) && typeof secondArg === "string") {
3588
- attrRouteRefs.add(`${firstArg.text}:${secondArg}`);
3589
- }
3590
- }
3591
- function markMissingEventPreviewHandlerErrors(eventPreviewAttrs, eventPreviewConsumerFieldIds, hasGenericEventPreviewConsumer, hardErrors) {
3592
- if (hasGenericEventPreviewConsumer) {
3593
- return;
3594
- }
3595
- for (const attr of uniqueEventPreviewAttrs(eventPreviewAttrs)) {
3596
- if (eventPreviewConsumerFieldIds.has(attr.fieldId)) {
3597
- continue;
3598
- }
3599
- hardErrors.push({
3600
- code: "field_scan.event_preview_handler_missing",
3601
- message: eventPreviewHandlerMissingMessage,
3602
- fieldId: attr.fieldId,
3603
- filePath: attr.filePath
3604
- });
3605
- }
3606
- }
3607
- function uniqueEventPreviewAttrs(eventPreviewAttrs) {
3608
- const seen = /* @__PURE__ */ new Set();
3609
- const unique = [];
3610
- for (const attr of eventPreviewAttrs) {
3611
- const key = `${attr.filePath}:${attr.fieldId}`;
3612
- if (seen.has(key)) {
3613
- continue;
3614
- }
3615
- seen.add(key);
3616
- unique.push(attr);
3617
- }
3618
- return unique;
3619
- }
3620
- function hasEventPreviewStrategy(options) {
3621
- return !!options && typeof options === "object" && !Array.isArray(options) && options.previewStrategy === "event";
3622
- }
3623
- function isGenericEventPreviewConsumerReference(node) {
3624
- return (ts3.isStringLiteral(node) || ts3.isNoSubstitutionTemplateLiteral(node)) && node.text === "siteplane:preview-value-applied";
3625
- }
3626
- function getPreviewHookFieldId(node, eventPreviewAttrVariables) {
3627
- const firstArg = getUseSiteplanePreviewTextFirstArg(node);
3628
- if (!firstArg) {
3629
- return void 0;
3630
- }
3631
- const directFieldId = staticExpressionValue(firstArg);
3632
- if (typeof directFieldId === "string") {
3633
- return directFieldId;
3634
- }
3635
- if (ts3.isIdentifier(firstArg)) {
3636
- return eventPreviewAttrVariables.get(firstArg.text);
3637
- }
3638
- return void 0;
3639
- }
3640
- function getPreviewHookPropName(node, eventPreviewAttrVariables) {
3641
- const firstArg = getUseSiteplanePreviewTextFirstArg(node);
3642
- if (!firstArg) {
3643
- return void 0;
3644
- }
3645
- if (ts3.isIdentifier(firstArg) && !eventPreviewAttrVariables.has(firstArg.text)) {
3646
- return firstArg.text;
3647
- }
3648
- if (ts3.isPropertyAccessExpression(firstArg) && ts3.isIdentifier(firstArg.expression)) {
3649
- return firstArg.name.text;
3650
- }
3651
- return void 0;
3652
- }
3653
- function getUseSiteplanePreviewTextFirstArg(node) {
3654
- if (ts3.isIdentifier(node.expression) && node.expression.text === "useSiteplanePreviewText") {
3655
- return node.arguments[0];
3656
- }
3657
- return void 0;
3658
- }
3659
- function markMissingAttrsWarnings(fields, attrFieldIds, attrRouteRefs) {
3660
- for (const field of fields) {
3661
- const hasDirectAttrs = attrFieldIds.has(field.fieldId);
3662
- const hasRouteAttrs = field.routeVariableName && field.routeFieldKey ? attrRouteRefs.has(`${field.routeVariableName}:${field.routeFieldKey}`) : false;
3663
- field.hasDomBinding = Boolean(hasDirectAttrs || hasRouteAttrs);
3664
- if (!field.hasDomBinding && (field.fieldType === "text" || field.fieldType === "longText")) {
3665
- field.warnings.push("field_scan.attrs_missing");
3666
- }
3667
- }
3668
- }
3669
- function validateScannedFields(fields, hardErrors, warnings, suggestions) {
3670
- const seen = /* @__PURE__ */ new Map();
3671
- for (const field of fields) {
3672
- if (!isFieldId(field.fieldId)) {
3673
- hardErrors.push({
3674
- code: "field_scan.invalid_field_id",
3675
- message: `Invalid field id: ${field.fieldId}`,
3676
- fieldId: field.fieldId,
3677
- filePath: field.sourceFilePath ?? "unknown"
3678
- });
3679
- }
3680
- const existing = seen.get(field.fieldId);
3681
- if (existing) {
3682
- hardErrors.push({
3683
- code: "field_scan.duplicate_id",
3684
- message: `Duplicate field id: ${field.fieldId}`,
3685
- fieldId: field.fieldId,
3686
- filePath: field.sourceFilePath ?? "unknown"
3687
- });
3688
- if (existing.fieldType !== field.fieldType) {
3689
- hardErrors.push({
3690
- code: "field_scan.type_conflict",
3691
- message: `Field id ${field.fieldId} is used with multiple types.`,
3692
- fieldId: field.fieldId,
3693
- filePath: field.sourceFilePath ?? "unknown"
3694
- });
3695
- }
3696
- }
3697
- seen.set(field.fieldId, field);
3698
- for (const warning of field.warnings) {
3699
- warnings.push({
3700
- code: warning,
3701
- message: getScanIssueMessage(warning),
3702
- fieldId: field.fieldId,
3703
- filePath: field.sourceFilePath ?? "unknown"
3704
- });
3705
- }
3706
- for (const suggestion of field.suggestions) {
3707
- suggestions.push({
3708
- code: suggestion,
3709
- message: getScanIssueMessage(suggestion),
3710
- fieldId: field.fieldId,
3711
- filePath: field.sourceFilePath ?? "unknown"
3712
- });
3713
- }
3714
- }
3715
- }
3716
- function getScanIssueMessage(code) {
3717
- if (code === "field_scan.attrs_missing") {
3718
- return "Field renders a value but has no DOM discovery attrs. Add siteplane.attrs(...) to the nearest stable element so the visual editor can select it.";
3719
- }
3720
- if (code === "field_scan.positional_repeating_id") {
3721
- return "Repeating content needs stable Siteplane field IDs for the MVP. Replace positional array/index field ids with stable item ids such as services.haircut.price.";
3722
- }
3723
- if (code === "field_scan.animation_wrapper") {
3724
- return 'Put siteplane.attrs(...) on the stable wrapper and make the animation respect data-siteplane-editor="true" or useSiteplaneEditorMode().';
3725
- }
3726
- if (code === "field_scan.event_preview_handler_missing") {
3727
- return eventPreviewHandlerMissingMessage;
3728
- }
3729
- return code;
3730
- }
3731
- function staticExpressionValue(expression) {
3732
- if (!expression) {
3733
- return void 0;
3734
- }
3735
- if (ts3.isStringLiteral(expression) || ts3.isNoSubstitutionTemplateLiteral(expression)) {
3736
- return expression.text;
3737
- }
3738
- if (ts3.isNumericLiteral(expression)) {
3739
- return Number(expression.text);
3740
- }
3741
- if (expression.kind === ts3.SyntaxKind.TrueKeyword) {
3742
- return true;
3743
- }
3744
- if (expression.kind === ts3.SyntaxKind.FalseKeyword) {
3745
87
  return false;
3746
88
  }
3747
- if (ts3.isObjectLiteralExpression(expression)) {
3748
- return readObjectLiteral(expression);
3749
- }
3750
- return void 0;
3751
- }
3752
- function readObjectLiteral(expression) {
3753
- const value = {};
3754
- for (const property of expression.properties) {
3755
- if (!ts3.isPropertyAssignment(property)) {
3756
- continue;
3757
- }
3758
- const name2 = getPropertyName(property.name);
3759
- if (!name2) {
3760
- continue;
3761
- }
3762
- value[name2] = staticExpressionValue(property.initializer);
3763
- }
3764
- return value;
3765
89
  }
3766
- function getPropertyName(name2) {
3767
- if (ts3.isIdentifier(name2) || ts3.isStringLiteral(name2)) {
3768
- return name2.text;
3769
- }
3770
- return null;
3771
- }
3772
- function collectEventPreviewComponentPropUsages(node, eventPreviewAttrVariables, eventPreviewComponentPropUsages, file) {
3773
- const componentName = getJsxTagName(node.tagName);
3774
- if (!isComponentName(componentName)) {
3775
- return;
3776
- }
3777
- for (const property of node.attributes.properties) {
3778
- if (!ts3.isJsxAttribute(property)) {
3779
- continue;
3780
- }
3781
- const propName = getJsxAttributeName(property.name);
3782
- const fieldId = getJsxFieldIdValue(property.initializer, eventPreviewAttrVariables);
3783
- if (!propName || !fieldId || !isFieldId(fieldId)) {
3784
- continue;
3785
- }
3786
- eventPreviewComponentPropUsages.push({
3787
- componentName,
3788
- fieldId,
3789
- filePath: file.filePath,
3790
- propName
3791
- });
3792
- }
3793
- }
3794
- function getJsxAttributeName(name2) {
3795
- if (ts3.isIdentifier(name2)) {
3796
- return name2.text;
3797
- }
3798
- return void 0;
3799
- }
3800
- function getJsxFieldIdValue(initializer, eventPreviewAttrVariables) {
3801
- if (!initializer) {
3802
- return void 0;
3803
- }
3804
- if (ts3.isStringLiteral(initializer)) {
3805
- return initializer.text;
3806
- }
3807
- if (ts3.isJsxExpression(initializer) && initializer.expression && ts3.isIdentifier(initializer.expression)) {
3808
- return eventPreviewAttrVariables.get(initializer.expression.text);
3809
- }
3810
- return void 0;
3811
- }
3812
- function getFunctionComponentName(node) {
3813
- if (ts3.isFunctionDeclaration(node) && node.name && isComponentName(node.name.text)) {
3814
- return node.name.text;
3815
- }
3816
- if (ts3.isVariableDeclaration(node) && ts3.isIdentifier(node.name) && isComponentName(node.name.text) && node.initializer && (ts3.isArrowFunction(node.initializer) || ts3.isFunctionExpression(node.initializer))) {
3817
- return node.name.text;
3818
- }
3819
- return void 0;
3820
- }
3821
- function isComponentName(name2) {
3822
- return /^[A-Z]/.test(name2);
3823
- }
3824
- function getAssignedVariableName(node) {
3825
- let current = node;
3826
- while (ts3.isAwaitExpression(current.parent)) {
3827
- current = current.parent;
3828
- }
3829
- if (ts3.isVariableDeclaration(current.parent) && ts3.isIdentifier(current.parent.name)) {
3830
- return current.parent.name.text;
3831
- }
3832
- return void 0;
3833
- }
3834
- function getJsxTagName(tagName) {
3835
- return tagName.getText();
3836
- }
3837
- function stringValue(value) {
3838
- return typeof value === "string" ? value : void 0;
3839
- }
3840
- function fieldTypeValue(value) {
3841
- if (value === "text" || value === "longText" || value === "image" || value === "link") {
3842
- return value;
3843
- }
3844
- return void 0;
3845
- }
3846
- function previewStrategyValue(value) {
3847
- return value === "dom" || value === "event" ? value : void 0;
3848
- }
3849
- function numberValue(value) {
3850
- return typeof value === "number" ? value : void 0;
3851
- }
3852
- function booleanValue(value) {
3853
- return typeof value === "boolean" ? value : void 0;
3854
- }
3855
- function assignIfDefined(object, key, value) {
3856
- if (value !== void 0) {
3857
- object[key] = value;
3858
- }
3859
- }
3860
- function inferRouteHint(filePath) {
3861
- const normalized = filePath.replaceAll("\\", "/");
3862
- const appIndex = normalized.indexOf("app/");
3863
- if (appIndex === -1 || !normalized.endsWith("page.tsx")) {
3864
- return void 0;
3865
- }
3866
- const route = normalized.slice(appIndex + 4, -"page.tsx".length).split("/").filter((segment) => segment && !segment.startsWith("(")).join("/");
3867
- return route ? `/${route}` : "/";
3868
- }
3869
- function toRepositoryRelativePosixPath(filePath, projectRoot) {
3870
- const path = isAbsolute(filePath) ? relative4(projectRoot, filePath) : filePath;
3871
- return path.replaceAll("\\", "/");
3872
- }
3873
- function isPositionalFieldReference(value) {
3874
- return value ? /\[\d+\]|\.\d+(?:\.|$)/.test(value) : false;
3875
- }
3876
-
3877
- // ../cli/dist/commands/site-setup.js
3878
- var execFileAsync2 = promisify2(execFile2);
3879
- var safeErrorSchema = z36.object({
3880
- code: z36.string().trim().min(1),
3881
- message: z36.string().trim().min(1)
3882
- }).passthrough();
3883
- var errorResponseSchema2 = z36.object({ ok: z36.literal(false), error: safeErrorSchema }).passthrough();
3884
- var contextDataSchema = z36.object({
3885
- task: siteSetupTaskSchema.refine((task) => task.run !== null)
3886
- }).strict();
3887
- var contextResponseSchema = z36.object({ ok: z36.literal(true), data: contextDataSchema }).strict();
3888
- var applyDataSchema = z36.object({
3889
- status: z36.enum(["applied", "ready_for_review"]),
3890
- runId: z36.string().uuid().optional(),
3891
- contractVersionId: z36.string().uuid().optional(),
3892
- fieldContractHash: z36.string().regex(/^sha256:[0-9a-f]{64}$/u),
3893
- editorDefinitionHash: z36.string().regex(/^sha256:[0-9a-f]{64}$/u).optional(),
3894
- fieldCount: z36.number().int().nonnegative().optional(),
3895
- routeCount: z36.number().int().nonnegative().optional()
3896
- }).strict();
3897
- var applyResponseSchema = z36.object({ ok: z36.literal(true), data: applyDataSchema }).strict();
3898
- var verifyDataSchema = z36.object({
3899
- status: z36.literal("ready_for_review"),
3900
- runId: z36.string().uuid().optional(),
3901
- deploymentOrigin: z36.string().url().optional(),
3902
- deploymentRevision: z36.string().optional()
3903
- }).strict();
3904
- var verifyResponseSchema = z36.object({ ok: z36.literal(true), data: verifyDataSchema }).strict();
3905
- async function siteSetupContextCommand(input) {
3906
- const response = await postSiteSetup(input, "context", {});
3907
- const parsed = contextResponseSchema.safeParse(response.payload);
3908
- if (response.ok && parsed.success) {
3909
- return { status: "ok", ...parsed.data.data };
3910
- }
3911
- return actionRequired(response.payload, "Siteplane setup context is unavailable.");
3912
- }
3913
- async function siteSetupVerifyCommand(input) {
3914
- const resolveSourceFiles = input.resolveSourceFiles ?? resolveScanFilePaths;
3915
- const readSourceFile = input.readSourceFile ?? ((path) => readFile9(path, "utf8"));
3916
- const paths = await resolveSourceFiles([], { projectDir: input.projectDir });
3917
- const sources = await Promise.all(paths.map(readSourceFile));
3918
- if (!sources.some(hasMountedFieldRuntimeBridge)) {
3919
- return {
3920
- status: "action_required",
3921
- code: "missing_field_runtime_bridge",
3922
- message: "Mount FieldRuntimeBridge from @siteplane/runtime-next/client before deployment verification."
3923
- };
3924
- }
3925
- const deploymentRevision = await (input.readRevision ?? readGitRevision)(input.projectDir);
3926
- const response = await postSiteSetup(input, "verify", {
3927
- deploymentUrl: input.deploymentUrl,
3928
- deploymentRevision,
3929
- ...input.wait ? { wait: true } : {}
90
+ function draftFrameRules(rules, editorOrigins) {
91
+ const isCsp = (key) => key.toLowerCase() === "content-security-policy";
92
+ const isXfo = (key) => key.toLowerCase() === "x-frame-options";
93
+ const hasFrameOptions = rules.some((rule) => rule.headers.some((header) => isXfo(header.key)));
94
+ const draftRule = (rule, headers) => ({
95
+ ...rule,
96
+ has: [...rule.has ?? [], DRAFT_MODE_COOKIE],
97
+ headers
3930
98
  });
3931
- const parsed = verifyResponseSchema.safeParse(response.payload);
3932
- if (response.ok && parsed.success) {
3933
- return parsed.data.data;
3934
- }
3935
- return actionRequired(response.payload, "Siteplane deployment verification failed.");
3936
- }
3937
- function hasMountedFieldRuntimeBridge(source) {
3938
- return /from\s+["']@siteplane\/runtime-next(?:\/client)?["']/u.test(source) && /<FieldRuntimeBridge(?:\s|\/|>)/u.test(source);
99
+ const optionsOnly = rules.filter((rule) => rule.headers.some((header) => isXfo(header.key)) && !rule.headers.some((header) => isCsp(header.key))).map(
100
+ (rule) => draftRule(rule, [
101
+ ...rule.headers,
102
+ { key: "Content-Security-Policy", value: `frame-ancestors 'self' ${editorOrigins.join(" ")}` }
103
+ ])
104
+ );
105
+ const policies = rules.filter(
106
+ (rule) => rule.headers.some((header) => isCsp(header.key) && (hasFrameOptions || /frame-ancestors/iu.test(header.value)))
107
+ ).map(
108
+ (rule) => draftRule(
109
+ rule,
110
+ rule.headers.map(
111
+ (header) => isCsp(header.key) ? { ...header, value: allowEditorFrames(header.value, editorOrigins) } : header
112
+ )
113
+ )
114
+ );
115
+ return [...optionsOnly, ...policies];
116
+ }
117
+ function allowEditorFrames(policy, editorOrigins) {
118
+ const directives = policy.split(";").map((directive) => directive.trim()).filter(Boolean);
119
+ const index = directives.findIndex((directive) => /^frame-ancestors(\s|$)/iu.test(directive));
120
+ if (index === -1) return [...directives, `frame-ancestors 'self' ${editorOrigins.join(" ")}`].join("; ");
121
+ const sources = (directives[index] ?? "").split(/\s+/u).slice(1);
122
+ const allowed = sources.includes("'none'") ? editorOrigins : [...sources, ...editorOrigins.filter((origin) => !sources.includes(origin))];
123
+ directives[index] = `frame-ancestors ${allowed.join(" ")}`;
124
+ return directives.join("; ");
3939
125
  }
3940
- async function postSiteSetup(input, operation, body) {
3941
- const [config, credentials] = await Promise.all([
3942
- readProjectConfig(input.projectDir),
3943
- readLocalCredentials(input.projectDir)
3944
- ]);
3945
- try {
3946
- const response = await (input.fetcher ?? fetch)(`${config.apiBaseUrl.replace(/\/$/u, "")}/api/cli/setup/${operation}`, {
3947
- method: "POST",
3948
- signal: AbortSignal.timeout(Math.max(1, Math.min(2e4, (input.deadline ?? Date.now() + 2e4) - Date.now()))),
3949
- headers: {
3950
- authorization: `Bearer ${credentials.accessToken}`,
3951
- "content-type": "application/json",
3952
- ...input.vercelAutomationBypassSecret?.trim() ? {
3953
- "x-vercel-protection-bypass": input.vercelAutomationBypassSecret.trim()
3954
- } : {}
3955
- },
3956
- body: JSON.stringify(body)
3957
- });
3958
- const payload = await response.json().catch(() => null);
3959
- return { ok: response.ok, payload };
3960
- } catch {
126
+ function prependRewrites(rewrites, rules) {
127
+ return async () => {
128
+ const existing = await rewrites?.() ?? [];
129
+ const sections = Array.isArray(existing) ? { afterFiles: existing } : existing;
3961
130
  return {
3962
- ok: false,
3963
- payload: {
3964
- ok: false,
3965
- error: {
3966
- code: "site_setup.transport_failed",
3967
- message: "The setup request was not acknowledged. Reload the existing task before retrying; no credential values are included."
3968
- }
3969
- }
131
+ beforeFiles: [...rules, ...sections.beforeFiles ?? []],
132
+ afterFiles: sections.afterFiles ?? [],
133
+ fallback: sections.fallback ?? []
3970
134
  };
3971
- }
3972
- }
3973
- function actionRequired(payload, fallbackMessage) {
3974
- const parsed = errorResponseSchema2.safeParse(payload);
3975
- return parsed.success ? {
3976
- status: "action_required",
3977
- code: parsed.data.error.code,
3978
- message: parsed.data.error.message
3979
- } : {
3980
- status: "action_required",
3981
- code: "invalid_server_response",
3982
- message: fallbackMessage
3983
- };
3984
- }
3985
- async function readGitRevision(projectDir) {
3986
- const { stdout } = await execFileAsync2("git", ["rev-parse", "HEAD"], {
3987
- cwd: projectDir,
3988
- encoding: "utf8"
3989
- });
3990
- const revision = stdout.trim();
3991
- if (!revision) {
3992
- throw new Error("A committed Git revision is required before setup verify.");
3993
- }
3994
- return revision;
3995
- }
3996
-
3997
- // ../cli/dist/commands/setup-start.js
3998
- import { randomBytes, randomUUID as randomUUID4 } from "crypto";
3999
- import { access as access5 } from "fs/promises";
4000
- import { join as join10, relative as relative5 } from "path";
4001
- import { z as z37 } from "zod";
4002
- var bootstrapResponseSchema2 = z37.object({
4003
- ok: z37.literal(true),
4004
- data: siteSetupBootstrapDataSchema
4005
- }).strict();
4006
-
4007
- // ../cli/dist/commands/setup-review.js
4008
- import { z as z38 } from "zod";
4009
- var responseSchema2 = z38.object({
4010
- ok: z38.literal(true),
4011
- data: z38.object({
4012
- reviewUrl: z38.string().url(),
4013
- expiresAt: z38.string().datetime({ offset: true })
4014
- }).strict()
4015
- }).strict();
4016
-
4017
- // ../cli/dist/commands/setup-prepare.js
4018
- import { access as access6, lstat as lstat2, mkdir as mkdir6, open as open3, readFile as readFile10, rename as rename3, rm as rm3 } from "fs/promises";
4019
- import { randomUUID as randomUUID5 } from "crypto";
4020
- import { dirname as dirname8, join as join11, relative as relative6, sep as sep2 } from "path";
4021
- import ts4 from "typescript";
4022
-
4023
- // ../cli/dist/commands/setup-lock.js
4024
- import { randomUUID as randomUUID6 } from "crypto";
4025
- import { lstat as lstat3, mkdir as mkdir7, open as open4, readFile as readFile11, rm as rm4, rmdir } from "fs/promises";
4026
- import { resolve as resolve2 } from "path";
4027
- import { z as z39 } from "zod";
4028
- var ownerSchema = z39.object({ pid: z39.number().int().positive(), nonce: z39.string().uuid() }).strict();
4029
-
4030
- // ../cli/dist/config/build-provenance.js
4031
- import { createHash as createHash3 } from "crypto";
4032
- import { execFile as execFile3 } from "child_process";
4033
- import { lstat as lstat4, readFile as readFile12, writeFile as writeFile5, mkdir as mkdir8 } from "fs/promises";
4034
- import { dirname as dirname9, isAbsolute as isAbsolute2, join as join12, relative as relative7, resolve as resolve3, sep as sep3 } from "path";
4035
- import { isDeepStrictEqual, promisify as promisify3 } from "util";
4036
- import { z as z40 } from "zod";
4037
- var execute = promisify3(execFile3);
4038
- var name = ".siteplane/build-source.json";
4039
- var oid = z40.string().regex(/^[0-9a-f]{40}$/u);
4040
- var entrySchema = z40.object({
4041
- path: z40.string().min(1).max(4096),
4042
- mode: z40.enum(["100644", "100755", "120000"]),
4043
- oid
4044
- }).strict();
4045
- var schema = z40.object({
4046
- version: z40.literal(2),
4047
- revision: oid,
4048
- commit: z40.string().min(1).max(1048576),
4049
- entries: z40.array(entrySchema).min(1).max(1e5),
4050
- providerConfigurations: z40.array(z40.object({
4051
- path: z40.string().max(4096).regex(/(?:^|\/)vercel\.json$/u),
4052
- source: z40.string().max(1048576)
4053
- }).strict()).max(1e3)
4054
- }).strict();
4055
- var gitHash = (type, content) => createHash3("sha1").update(`${type} ${content.length}\0`).update(content).digest("hex");
4056
- async function writeBuildProvenance(checkout, revision) {
4057
- const commit = (await execute("git", ["cat-file", "commit", revision], {
4058
- cwd: checkout,
4059
- maxBuffer: 1048576
4060
- })).stdout;
4061
- const tree = (await execute("git", ["ls-tree", "-rz", "--full-tree", revision], {
4062
- cwd: checkout,
4063
- maxBuffer: 16777216
4064
- })).stdout;
4065
- const entries = tree.split("\0").filter(Boolean).map((line) => {
4066
- const match = /^(100644|100755|120000) blob ([0-9a-f]{40})\t([\s\S]+)$/u.exec(line);
4067
- if (!match)
4068
- throw new Error("Git submodules or unsupported tracked objects need an explicit deployment configuration.");
4069
- return { path: match[3], mode: match[1], oid: match[2] };
4070
- });
4071
- for (const entry of entries) {
4072
- if (entry.mode !== "120000")
4073
- continue;
4074
- const { readlink: readlink2 } = await import("fs/promises");
4075
- const path = join12(checkout, entry.path);
4076
- const target2 = resolve3(dirname9(path), await readlink2(path));
4077
- const bound = relative7(checkout, target2);
4078
- if (bound.startsWith("..") || isAbsolute2(bound))
4079
- throw new Error("A tracked symbolic link escapes the committed upload; resolve that source boundary before deployment.");
4080
- }
4081
- const providerConfigurations = await Promise.all(entries.filter((entry) => /(?:^|\/)vercel\.json$/u.test(entry.path)).map(async (entry) => ({
4082
- path: entry.path,
4083
- source: await readFile12(join12(checkout, entry.path), "utf8")
4084
- })));
4085
- const record = schema.parse({
4086
- version: 2,
4087
- revision,
4088
- commit,
4089
- entries,
4090
- providerConfigurations
4091
- });
4092
- const target = join12(checkout, name);
4093
- await mkdir8(dirname9(target), { recursive: true, mode: 448 });
4094
- await writeFile5(target, JSON.stringify(record), { flag: "wx", mode: 384 });
4095
- }
4096
- async function readBuildProvenance(projectDirectory) {
4097
- let root = resolve3(projectDirectory);
4098
- let raw = null;
4099
- for (; ; ) {
4100
- raw = await readFile12(join12(root, name), "utf8").catch((error) => {
4101
- if (error.code === "ENOENT")
4102
- return null;
4103
- throw error;
4104
- });
4105
- if (raw !== null || dirname9(root) === root)
4106
- break;
4107
- root = dirname9(root);
4108
- }
4109
- if (raw === null)
4110
- return null;
4111
- if (Buffer.byteLength(raw) > 16777216)
4112
- throw new Error("Build provenance is oversized.");
4113
- const record = schema.parse(JSON.parse(raw));
4114
- if (gitHash("commit", Buffer.from(record.commit)) !== record.revision)
4115
- throw new Error("The build revision is not the supplied Git commit.");
4116
- const expectedTree = /^tree ([0-9a-f]{40})\n/u.exec(record.commit)?.[1];
4117
- if (treeHash(record.entries) !== expectedTree)
4118
- throw new Error("The build file inventory does not belong to this Git commit.");
4119
- const bound = new Map(record.entries.map((entry) => [entry.path, entry]));
4120
- const configurations = /* @__PURE__ */ new Map();
4121
- for (const config of record.providerConfigurations) {
4122
- const entry = bound.get(config.path);
4123
- if (!entry || entry.mode === "120000" || configurations.has(config.path) || gitHash("blob", Buffer.from(config.source)) !== entry.oid)
4124
- throw new Error("The original provider configuration is not bound to the Git commit.");
4125
- configurations.set(config.path, config.source);
4126
- }
4127
- if (record.entries.some((entry) => /(?:^|\/)vercel\.json$/u.test(entry.path) && !configurations.has(entry.path)))
4128
- throw new Error("The committed provider configuration is missing from the build provenance.");
4129
- const appRelative = relative7(root, projectDirectory).split(sep3).join("/");
4130
- if (!bound.has(appRelative ? `${appRelative}/package.json` : "package.json"))
4131
- throw new Error("The selected app is not part of the committed upload.");
4132
- for (const entry of record.entries) {
4133
- const path = join12(root, entry.path);
4134
- const stat = await lstat4(path).catch((error) => {
4135
- if (error.code === "ENOENT")
4136
- return null;
4137
- throw error;
4138
- });
4139
- if (!stat)
4140
- continue;
4141
- if (stat.isSymbolicLink()) {
4142
- const { readlink: readlink2 } = await import("fs/promises");
4143
- if (entry.mode !== "120000" || gitHash("blob", Buffer.from(await readlink2(path))) !== entry.oid)
4144
- throw new Error("A deployed symbolic link differs from the commit.");
4145
- } else {
4146
- const contents = stat.isFile() ? await readFile12(path) : null;
4147
- if (contents && entry.mode !== "120000" && gitHash("blob", contents) === entry.oid)
4148
- continue;
4149
- const original = configurations.get(entry.path);
4150
- if (contents && original && entry.mode !== "120000") {
4151
- try {
4152
- const object = z40.record(z40.string(), z40.unknown());
4153
- const actual = object.parse(JSON.parse(contents.toString("utf8")));
4154
- const expected = object.parse(JSON.parse(original));
4155
- if (!Object.hasOwn(expected, "version") && actual.version === 2)
4156
- delete actual.version;
4157
- if (!Object.hasOwn(expected, "name") && typeof actual.name === "string" && /^[a-z0-9][a-z0-9._-]{0,99}$/iu.test(actual.name))
4158
- delete actual.name;
4159
- if (isDeepStrictEqual(actual, expected))
4160
- continue;
4161
- } catch {
4162
- }
4163
- }
4164
- throw new Error(`A deployed file ${JSON.stringify(entry.path).slice(0, 260)} differs from the committed upload.`);
4165
- }
4166
- }
4167
- return record.revision;
4168
- }
4169
- function treeHash(entries) {
4170
- const root = /* @__PURE__ */ new Map();
4171
- for (const entry of entries) {
4172
- const parts = entry.path.split("/");
4173
- if (parts.some((part) => !part || part === "." || part === ".." || part.includes("\0") || part.includes("\\")))
4174
- throw new Error("Unsafe Git inventory path.");
4175
- let tree = root;
4176
- for (const part of parts.slice(0, -1)) {
4177
- let child = tree.get(part);
4178
- if (!child) {
4179
- child = /* @__PURE__ */ new Map();
4180
- tree.set(part, child);
4181
- }
4182
- if (!(child instanceof Map))
4183
- throw new Error("Conflicting Git inventory path.");
4184
- tree = child;
4185
- }
4186
- const leaf = parts.at(-1);
4187
- if (tree.has(leaf))
4188
- throw new Error("Duplicate Git inventory path.");
4189
- tree.set(leaf, { mode: entry.mode, oid: entry.oid });
4190
- }
4191
- const hash2 = (tree) => {
4192
- const sorted = [...tree].sort(([a, av], [b, bv]) => Buffer.compare(Buffer.from(a + (av instanceof Map ? "/" : "")), Buffer.from(b + (bv instanceof Map ? "/" : ""))));
4193
- return gitHash("tree", Buffer.concat(sorted.map(([path, value]) => Buffer.concat([
4194
- Buffer.from(`${value instanceof Map ? "40000" : value.mode} ${path}\0`),
4195
- Buffer.from(value instanceof Map ? hash2(value) : value.oid, "hex")
4196
- ]))));
4197
- };
4198
- return hash2(root);
4199
- }
4200
-
4201
- // ../cli/dist/commands/setup-deploy.js
4202
- import { createHash as createHash4, randomBytes as randomBytes2, randomUUID as randomUUID7 } from "crypto";
4203
- import { access as access7, mkdir as mkdir9, mkdtemp, rm as rm5, writeFile as writeFile6 } from "fs/promises";
4204
- import { tmpdir } from "os";
4205
- import { join as join13 } from "path";
4206
- import { z as z41 } from "zod";
4207
- var envSchema = z41.object({
4208
- id: z41.string(),
4209
- key: z41.string(),
4210
- target: z41.array(z41.string()),
4211
- type: z41.string(),
4212
- comment: z41.string().optional(),
4213
- updatedAt: z41.number().optional()
4214
- });
4215
- var deploymentSchema = z41.object({
4216
- id: z41.string(),
4217
- projectId: z41.string(),
4218
- ownerId: z41.string().optional(),
4219
- target: z41.string().nullable(),
4220
- readyState: z41.string(),
4221
- createdAt: z41.number(),
4222
- url: z41.string(),
4223
- meta: z41.record(z41.string(), z41.unknown()).optional(),
4224
- gitSource: z41.object({ sha: z41.string().optional() }).nullable().optional()
4225
- });
4226
- var buildProofSchema = z41.object({
4227
- node: z41.string(),
4228
- packageManager: z41.enum(["npm", "pnpm"]),
4229
- packageManagerVersion: z41.string(),
4230
- lockfileHash: z41.string(),
4231
- revision: z41.string(),
4232
- fieldContractHash: z41.string(),
4233
- siteplane: z41.string(),
4234
- runtimeNext: z41.string(),
4235
- analytics: z41.string().optional(),
4236
- next: z41.string(),
4237
- react: z41.string()
4238
- }).strict();
4239
- var defaults = {
4240
- preflight: setupPreflightCommand,
4241
- context: siteSetupContextCommand,
4242
- api: setupVercelApi,
4243
- process: setupProcess,
4244
- verify: siteSetupVerifyCommand,
4245
- post: postSiteSetup,
4246
- writeBuildProvenance,
4247
- analyticsPublicKey: analyticsPublicKeyCommand,
4248
- now: Date.now,
4249
- pause: (milliseconds) => new Promise((resolve5) => setTimeout(resolve5, milliseconds))
4250
- };
4251
-
4252
- // ../cli/dist/commands/setup-claim.js
4253
- import { z as z42 } from "zod";
4254
-
4255
- // src/next.ts
4256
- var SiteplaneNextContractError = class extends Error {
4257
- constructor(code, message) {
4258
- super(`[${code}] ${message}`);
4259
- this.code = code;
4260
- this.name = "SiteplaneNextContractError";
4261
- }
4262
- code;
4263
- };
4264
- async function scanNextProjectFieldContract(projectDir) {
4265
- const resolvedProjectDir = resolve4(projectDir);
4266
- const filePaths = await resolveScanFilePaths([], {
4267
- projectDir: resolvedProjectDir
4268
- });
4269
- const files = await Promise.all(
4270
- filePaths.map(async (filePath) => ({
4271
- filePath,
4272
- sourceText: await readFile13(filePath, "utf8")
4273
- }))
4274
- );
4275
- const result = scanNextSourceFilesToSiteFieldContract(files, {
4276
- projectRoot: resolvedProjectDir
4277
- });
4278
- if (result.hardErrors.length > 0) {
4279
- throw new SiteplaneNextContractError(
4280
- "field_contract_scan_failed",
4281
- result.hardErrors.map((issue) => issue.message).join("\n")
4282
- );
4283
- }
4284
- return {
4285
- ...result,
4286
- fieldContractHash: createSiteFieldContractHash(result.contract)
4287
135
  };
4288
136
  }
4289
- async function withSiteplane(nextConfig, options = {}) {
4290
- const projectDir = options.projectDir ?? process.cwd();
4291
- const booking = options.booking;
4292
- let integrated = nextConfig;
4293
- if (booking) {
4294
- if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
4295
- booking.site
4296
- ))
4297
- throw new Error(
4298
- "Siteplane booking.site must be the Site ID from Booking settings."
4299
- );
4300
- for (const folder of ["app/booking", "src/app/booking"]) {
4301
- if (await access8(join14(projectDir, folder)).then(
4302
- () => true,
4303
- () => false
4304
- ))
4305
- console.warn(
4306
- "[Siteplane] An app/booking route exists. The Booking beforeFiles rewrite takes precedence; move that route or remove the Booking integration."
4307
- );
4308
- }
4309
- integrated = {
4310
- ...nextConfig,
4311
- rewrites: async () => {
4312
- const existing = await nextConfig.rewrites?.() ?? [];
4313
- const rules = Array.isArray(existing) ? { beforeFiles: [], afterFiles: existing, fallback: [] } : existing;
4314
- return {
4315
- beforeFiles: [
4316
- {
4317
- source: "/booking",
4318
- destination: `https://siteplane.io/booking?site=${booking.site}`
4319
- },
4320
- {
4321
- source: "/booking/:path*",
4322
- destination: `https://siteplane.io/booking/:path*?site=${booking.site}`
4323
- },
4324
- ...rules.beforeFiles ?? []
4325
- ],
4326
- afterFiles: rules.afterFiles ?? [],
4327
- fallback: rules.fallback ?? []
4328
- };
4329
- }
4330
- };
4331
- const hasEditor = await access8(
4332
- join14(projectDir, "siteplane.config.json")
4333
- ).then(
137
+ async function withBooking(config, site, projectDir) {
138
+ if (!siteIdPattern.test(site)) throw new Error("Siteplane booking.site must be the Site ID from Booking settings.");
139
+ for (const folder of ["app/booking", "src/app/booking"]) {
140
+ if (await access(join(projectDir, folder)).then(
4334
141
  () => true,
4335
142
  () => false
4336
- );
4337
- if (!hasEditor) return { ...integrated, env: { ...nextConfig.env } };
4338
- }
4339
- const config = await readProjectConfig(projectDir);
4340
- const scan = await scanNextProjectFieldContract(projectDir);
4341
- const requestedRevision = options.buildRevision?.trim() || process.env.SITEPLANE_BUILD_REVISION?.trim();
4342
- const providerRevision = process.env.VERCEL === "1" ? process.env.VERCEL_GIT_COMMIT_SHA?.trim() : void 0;
4343
- const buildRevision = providerRevision || await readGitRevision2(projectDir) || await readBuildProvenance(projectDir) || requestedRevision || "local";
4344
- if (process.env.VERCEL === "1" && /^[0-9a-f]{40}$/u.test(buildRevision)) {
4345
- try {
4346
- await emitBuildProof(
4347
- projectDir,
4348
- config.packages,
4349
- buildRevision,
4350
- scan.fieldContractHash
4351
- );
4352
- } catch (error) {
4353
- const detail = error instanceof SiteplaneNextContractError ? error.code : "unavailable";
143
+ )) {
4354
144
  console.warn(
4355
- `[Siteplane] Build proof ${detail}; the website build continues. Run the scoped Siteplane setup verification when editor evidence must be refreshed.`
145
+ "[Siteplane] An app/booking route exists. The Booking beforeFiles rewrite takes precedence; move that route or remove the Booking integration."
4356
146
  );
4357
147
  }
4358
148
  }
4359
- const hasImages = scan.contract.fields.some(
4360
- (field) => field.fieldType === "image"
4361
- );
4362
- const imageOrigin = hasImages ? readImageWebsiteOrigin() : null;
4363
149
  return {
4364
- ...integrated,
4365
- ...hasImages ? { images: prepareImagePatterns(nextConfig.images, imageOrigin) } : {},
4366
- headers: async () => frameHeaders(
4367
- await nextConfig.headers?.() ?? [],
4368
- new URL(config.apiBaseUrl).origin,
4369
- imageOrigin
4370
- ),
4371
- env: {
4372
- ...nextConfig.env ?? {},
4373
- SITEPLANE_BUILD_FIELD_CONTRACT_HASH: scan.fieldContractHash,
4374
- SITEPLANE_BUILD_PUBLIC_SITE_KEY_ID: config.publicSiteKeyId,
4375
- SITEPLANE_BUILD_REVISION: buildRevision
4376
- }
4377
- };
4378
- }
4379
- var execFileAsync3 = promisify4(execFile4);
4380
- async function readGitRevision2(projectDir) {
4381
- try {
4382
- const result = await execFileAsync3("git", ["rev-parse", "HEAD"], {
4383
- cwd: projectDir
4384
- });
4385
- return result.stdout.trim();
4386
- } catch {
4387
- return "";
4388
- }
4389
- }
4390
- function frameHeaders(rules, adminOrigin, imageOrigin) {
4391
- const policy = `frame-ancestors 'self' ${adminOrigin}`;
4392
- const hasFrameOptions = rules.some(
4393
- (rule) => rule.headers.some(
4394
- (header) => header.key.toLowerCase() === "x-frame-options"
4395
- )
4396
- );
4397
- const prepared = rules.map((rule) => ({
4398
- ...rule,
4399
- headers: rule.headers.map((header) => {
4400
- if (header.key.toLowerCase() !== "content-security-policy") return header;
4401
- const ancestors = /(?:^|;)\s*frame-ancestors\s+([^;]+)/iu.test(
4402
- header.value
4403
- );
4404
- const value = ancestors || hasFrameOptions ? header.value : `${header.value.replace(/;\s*$/u, "")}; ${policy}`;
4405
- return {
4406
- ...header,
4407
- value: imageOrigin ? prepareImageCsp(value, imageOrigin) : value
4408
- };
4409
- })
4410
- }));
4411
- return hasFrameOptions ? prepared : [
4412
- {
4413
- source: "/:path*",
4414
- headers: [{ key: "Content-Security-Policy", value: policy }]
4415
- },
4416
- ...prepared
4417
- ];
4418
- }
4419
- var managedImageOrigin = "https://siteplane.io";
4420
- function readImageWebsiteOrigin() {
4421
- if (!process.env.SITEPLANE_SITE_ORIGIN) return null;
4422
- try {
4423
- const value = process.env.SITEPLANE_SITE_ORIGIN;
4424
- const url = new URL(value ?? "");
4425
- if (url.protocol !== "https:" || url.username || url.password || url.port || url.pathname !== "/" || url.search || url.hash || !url.hostname.includes(".") || /(?:^|\.)(?:localhost|local|internal)$/iu.test(url.hostname) || /^[\d.]+$/u.test(url.hostname) || url.hostname.includes(":"))
4426
- throw new Error("Invalid image origin");
4427
- return url.origin;
4428
- } catch {
4429
- throw new SiteplaneNextContractError(
4430
- "image_origin_missing",
4431
- "If configured, SITEPLANE_SITE_ORIGIN must be the exact public HTTPS website origin. setup deploy sets it from the verified provider domain; local fallback-image builds can omit it. Never substitute a wildcard or private host."
4432
- );
4433
- }
4434
- }
4435
- function prepareImagePatterns(images, websiteOrigin) {
4436
- const remotePatterns = [...images?.remotePatterns ?? []];
4437
- const destinations = [
4438
- [managedImageOrigin, "/api/assets/*/content"]
4439
- ];
4440
- if (websiteOrigin) destinations.push([websiteOrigin, "/siteplane-assets/**"]);
4441
- for (const [origin, pathname] of destinations) {
4442
- const pattern = {
4443
- protocol: "https",
4444
- hostname: new URL(origin).hostname,
4445
- port: "",
4446
- pathname,
4447
- search: ""
4448
- };
4449
- if (!remotePatterns.some(
4450
- (existing) => existing.protocol === pattern.protocol && existing.hostname === pattern.hostname && existing.port === "" && existing.pathname === pathname && existing.search === ""
4451
- ))
4452
- remotePatterns.push(pattern);
4453
- }
4454
- const localPatterns = images?.localPatterns ? [...images.localPatterns] : void 0;
4455
- if (localPatterns && !localPatterns.some(
4456
- (pattern) => pattern.pathname === "/siteplane-assets/**" && pattern.search === ""
4457
- ))
4458
- localPatterns.push({ pathname: "/siteplane-assets/**", search: "" });
4459
- return {
4460
- ...images,
4461
- remotePatterns,
4462
- ...localPatterns ? { localPatterns } : {}
150
+ ...config,
151
+ rewrites: prependRewrites(config.rewrites, [
152
+ { source: "/booking", destination: `https://siteplane.io/booking?site=${site}` },
153
+ { source: "/booking/:path*", destination: `https://siteplane.io/booking/:path*?site=${site}` }
154
+ ])
4463
155
  };
4464
156
  }
4465
- function prepareImageCsp(policy, websiteOrigin) {
4466
- if (policy.includes(","))
4467
- throw new SiteplaneNextContractError(
4468
- "image_policy_conflict",
4469
- "Split combined CSP policies into separate header rules before image preparation; preserve every existing directive."
4470
- );
4471
- const directives = policy.split(";").map((part) => part.trim()).filter(Boolean);
4472
- const find = (name2) => directives.map((part, index) => ({ index, tokens: part.split(/\s+/u) })).filter((part) => part.tokens[0]?.toLowerCase() === name2);
4473
- const image = find("img-src"), defaults2 = find("default-src");
4474
- if (image.length > 1 || defaults2.length > 1)
4475
- throw new SiteplaneNextContractError(
4476
- "image_policy_conflict",
4477
- "Remove ambiguous duplicate img-src/default-src directives before image preparation without changing unrelated policies."
4478
- );
4479
- const existing = image[0] ?? defaults2[0];
4480
- if (!existing) return policy;
4481
- const sources = existing.tokens.slice(1).filter((source) => source !== "'none'");
4482
- for (const source of [
4483
- `${managedImageOrigin}/api/assets/`,
4484
- `${websiteOrigin}/siteplane-assets/`,
4485
- `${websiteOrigin}/_next/image`
4486
- ]) {
4487
- if (!sources.includes(source)) sources.push(source);
4488
- }
4489
- const value = `img-src ${sources.join(" ")}`;
4490
- if (image[0]) directives[image[0].index] = value;
4491
- else directives.push(value);
4492
- return directives.join("; ");
4493
- }
4494
- async function emitBuildProof(projectDir, packages, revision, fieldContractHash) {
4495
- const [cli, runtime, next, react] = await Promise.all(
4496
- ["siteplane", "@siteplane/runtime-next", "next", "react"].map(
4497
- (name2) => readInstalledPackage(projectDir, name2)
4498
- )
4499
- );
4500
- const analytics = packages.analytics ? await readInstalledPackage(projectDir, "@siteplane/analytics") : void 0;
4501
- if (analytics?.version !== packages.analytics || cli.version !== packages.siteplane || runtime.version !== packages.runtimeNext)
4502
- throw new SiteplaneNextContractError(
4503
- "build_identity_missing",
4504
- "The provider installed different Siteplane package versions than the task requires."
4505
- );
4506
- const executable = process.env.npm_execpath;
4507
- if (!executable)
4508
- throw new SiteplaneNextContractError(
4509
- "build_identity_missing",
4510
- "Run the provider build through the selected npm/pnpm executable so its actual version can be verified."
4511
- );
4512
- const packageManager = /pnpm/iu.test(executable) ? "pnpm" : "npm";
4513
- const packageManagerVersion = (await execFileAsync3(process.execPath, [executable, "--version"], {
4514
- cwd: projectDir
4515
- })).stdout.trim();
4516
- let directory = resolve4(projectDir);
4517
- let lock = null;
4518
- while (true) {
4519
- lock = await readFile13(
4520
- join14(
4521
- directory,
4522
- packageManager === "npm" ? "package-lock.json" : "pnpm-lock.yaml"
4523
- ),
4524
- "utf8"
4525
- ).catch((error) => {
4526
- if (error.code === "ENOENT") return null;
4527
- throw error;
4528
- });
4529
- if (lock !== null || dirname10(directory) === directory) break;
4530
- directory = dirname10(directory);
4531
- }
4532
- if (!lock)
4533
- throw new SiteplaneNextContractError(
4534
- "build_identity_missing",
4535
- "The provider build is missing the selected canonical lockfile."
4536
- );
4537
- console.info(
4538
- `SITEPLANE_BUILD_PROOF=${JSON.stringify({ node: process.versions.node, packageManager, packageManagerVersion, lockfileHash: `sha256:${createHash5("sha256").update(lock).digest("hex")}`, revision, fieldContractHash, siteplane: cli.version, runtimeNext: runtime.version, ...analytics ? { analytics: analytics.version } : {}, next: next.version, react: react.version })}`
4539
- );
4540
- }
4541
157
  export {
4542
- SiteplaneNextContractError,
4543
- scanNextProjectFieldContract,
4544
158
  withSiteplane
4545
159
  };