siteplane 0.1.68 → 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,2850 +1,159 @@
1
- // src/next.ts
2
- import { execFile } from "child_process";
3
- import { access as access2, readFile as readFile5 } from "fs/promises";
4
- import { join as join6, resolve } from "path";
5
- import { promisify } from "util";
6
-
7
- // ../cli/dist/booking/manifest.js
8
- import { mkdir, readFile, writeFile } from "fs/promises";
9
- import { dirname } from "path";
10
-
11
- // ../shared/dist/actors.js
12
- import { z } from "zod";
13
- var ACTOR_TYPES = ["workspace_member", "site_member", "agent", "system"];
14
- var actorTypeSchema = z.enum(ACTOR_TYPES);
15
-
16
- // ../shared/dist/agent-connection.js
17
- import { z as z2 } from "zod";
18
- var AGENT_CONNECTION_TOKEN_PREFIX = "sp_agent_";
19
- var deviceAuthorizationResponseSchema = z2.object({
20
- device_code: z2.string().min(43),
21
- user_code: z2.string().min(8),
22
- verification_uri: z2.string().url(),
23
- verification_uri_complete: z2.string().url(),
24
- expires_in: z2.number().int().positive(),
25
- interval: z2.number().int().positive()
26
- });
27
- var deviceTokenResponseSchema = z2.object({
28
- access_token: z2.string().startsWith(AGENT_CONNECTION_TOKEN_PREFIX).min(40),
29
- token_type: z2.literal("Bearer"),
30
- scope: z2.string(),
31
- expires_in: z2.number().int().positive()
32
- });
33
- var deviceTokenErrorSchema = z2.object({
34
- error: z2.string(),
35
- error_description: z2.string().optional(),
36
- interval: z2.number().int().positive().optional()
37
- });
38
-
39
- // ../shared/dist/analytics-defaults.js
40
- var ANALYTICS_BATCH_LIMITS = {
41
- maxEventsPerRequest: 50,
42
- maxJsonBodyBytes: 64 * 1024,
43
- maxPropertiesJsonBytes: 8 * 1024
44
- };
45
- var ANALYTICS_RETENTION_DEFAULTS = {
46
- enabledRawEventRetentionDays: 30,
47
- higherTierRawEventRetentionDays: 90,
48
- maxRawEventRetentionDaysWithoutAdr: 180,
49
- diagnosticsRetentionDays: 30,
50
- rateLimitBucketRetentionDays: 2,
51
- lateEventWindowHours: 72
52
- };
53
-
54
- // ../shared/dist/analytics-booking.js
55
- import { z as z3 } from "zod";
56
- var bookingProviders = ["treatwell", "beautinda", "other"];
57
- var bookingAttributionModes = [
58
- "outbound_click_and_utm",
59
- "provider_redirect",
60
- "provider_reported",
61
- "manual_import",
62
- "verified_webhook"
63
- ];
64
- var bookingSignalSources = [
65
- "outbound_click",
66
- "provider_redirect",
67
- "provider_reported",
68
- "manual_import",
69
- "verified_webhook"
70
- ];
71
- var bookingAttributionConfidences = [
72
- "provider_redirect",
73
- "provider_reported",
74
- "manual_import",
75
- "verified_webhook"
76
- ];
77
- var bookingProviderSchema = z3.enum(bookingProviders);
78
- var bookingAttributionModeSchema = z3.enum(bookingAttributionModes);
79
- var bookingSignalSourceSchema = z3.enum(bookingSignalSources);
80
- var bookingAttributionConfidenceSchema = z3.enum(bookingAttributionConfidences);
81
- var bookingProviderHintSchema = z3.object({
82
- bookingProvider: bookingProviderSchema,
83
- bookingUrlHost: z3.string().min(1).max(253),
84
- bookingAttributionMode: bookingAttributionModeSchema,
85
- utmContent: z3.string().min(1).max(200).optional(),
86
- successCondition: z3.literal("redirect_status=succeeded").optional()
87
- }).superRefine((hint, context) => {
88
- if (hint.successCondition !== void 0 && hint.bookingAttributionMode !== "provider_redirect") {
89
- context.addIssue({
90
- code: "custom",
91
- message: "successCondition is allowed only for provider_redirect booking hints.",
92
- path: ["successCondition"]
93
- });
94
- }
95
- });
96
-
97
- // ../shared/dist/analytics-contract.js
98
- import { z as z4 } from "zod";
99
- var ANALYTICS_IMPORT_SCHEMA_VERSION = "analytics-provider-import.v1";
100
- var analyticsImportTargetIdSchema = z4.string().min(1).max(120).regex(/^[a-zA-Z][a-zA-Z0-9]*(?:[._-][a-zA-Z0-9]+)*$/u);
101
- var analyticsProviderImportRowSchema = z4.object({
102
- targetId: analyticsImportTargetIdSchema,
103
- occurredAt: z4.string().datetime({ offset: true }),
104
- attributionConfidence: z4.enum(["provider_reported", "manual_import"]),
105
- providerEventId: z4.string().trim().min(1).max(160).optional(),
106
- idempotencyKey: z4.string().trim().min(1).max(160).optional()
107
- }).strict().superRefine((row, context) => {
108
- if (!row.providerEventId && !row.idempotencyKey) {
109
- context.addIssue({
110
- code: "custom",
111
- message: "Each import row requires providerEventId or idempotencyKey.",
112
- path: ["providerEventId"]
113
- });
114
- }
115
- });
116
- var analyticsProviderImportSchema = z4.object({
117
- schemaVersion: z4.literal(ANALYTICS_IMPORT_SCHEMA_VERSION),
118
- provider: z4.enum(["treatwell", "beautinda", "other"]),
119
- sourceName: z4.string().trim().min(1).max(160).optional(),
120
- rows: z4.array(analyticsProviderImportRowSchema).min(1).max(1e3)
121
- }).strict();
122
-
123
- // ../shared/dist/analytics-config.js
124
- import { z as z5 } from "zod";
125
- var ANALYTICS_CONFIG_LEASE_MS = 6e4;
126
- var analyticsMeasurementConfigSchema = z5.object({
127
- version: z5.literal(1),
128
- installationGeneration: z5.number().int().positive(),
129
- configRevision: z5.number().int().positive(),
130
- effectiveSiteMode: z5.enum(["disabled", "session_only", "full_analytics"]),
131
- measurementEnabled: z5.boolean(),
132
- issuedAt: z5.string().datetime({ offset: true }),
133
- expiresAt: z5.string().datetime({ offset: true })
134
- }).strict().superRefine((config, context) => {
135
- const lifetime = Date.parse(config.expiresAt) - Date.parse(config.issuedAt);
136
- if (lifetime <= 0 || lifetime > ANALYTICS_CONFIG_LEASE_MS) {
137
- context.addIssue({
138
- code: "custom",
139
- path: ["expiresAt"],
140
- message: "Measurement permission must expire within 60 seconds."
141
- });
142
- }
143
- if (config.measurementEnabled !== (config.effectiveSiteMode !== "disabled")) {
144
- context.addIssue({
145
- code: "custom",
146
- path: ["measurementEnabled"],
147
- message: "Measurement permission must match the effective site mode."
148
- });
149
- }
150
- });
151
-
152
- // ../shared/dist/analytics-events.js
153
- import { z as z6 } from "zod";
154
- var ANALYTICS_SCHEMA_VERSION = "analytics.v1";
155
- var analyticsSessionIdSchema = z6.string().regex(/^cpa_s_[a-f0-9]{32}$/u);
156
- var analyticsVisitorIdSchema = z6.string().regex(/^cpa_v_[a-f0-9]{32}$/u);
157
- var analyticsPersistedIdentityHashSchema = z6.string().regex(/^[a-f0-9]{64}$/u);
158
- var analyticsEventNames = [
159
- "page_view",
160
- "section_view",
161
- "cta_click",
162
- "outbound_click",
163
- "email_click",
164
- "phone_click",
165
- "file_download",
166
- "form_start",
167
- "form_submit",
168
- "form_error",
169
- "lead_created",
170
- "checkout_started",
171
- "conversion",
172
- "checkout_completed",
173
- "payment_succeeded",
174
- "subscription_started",
175
- "purchase_completed",
176
- "trial_started"
177
- ];
178
- var serverOnlyAnalyticsEvents = [
179
- "checkout_completed",
180
- "payment_succeeded",
181
- "subscription_started",
182
- "purchase_completed",
183
- "trial_started"
184
- ];
185
- var analyticsEventSources = [
186
- "browser",
187
- "server",
188
- "test",
189
- "editor_preview",
190
- "system"
191
- ];
192
- var analyticsIngestionChannels = [
193
- "browser_collector",
194
- "server_sdk",
195
- "provider_endpoint",
196
- "provider_import",
197
- "test",
198
- "editor_preview",
199
- "system_job"
200
- ];
201
- var analyticsTargetTypes = [
202
- "cta",
203
- "form",
204
- "section",
205
- "file",
206
- "link",
207
- "conversion",
208
- "server_conversion"
209
- ];
210
- var analyticsDataAttributeEvents = [
211
- "cta_click",
212
- "outbound_click",
213
- "email_click",
214
- "phone_click",
215
- "file_download",
216
- "section_view",
217
- "checkout_started",
218
- "form"
219
- ];
220
- var analyticsDeviceTypes = [
221
- "desktop",
222
- "tablet",
223
- "mobile",
224
- "unknown"
225
- ];
226
- var analyticsBotClassifications = [
227
- "normal",
228
- "known_bot",
229
- "suspicious",
230
- "invalid"
231
- ];
232
- var analyticsValidityStatuses = [
233
- "accepted",
234
- "rejected",
235
- "ignored"
236
- ];
237
- var analyticsEventNameSchema = z6.enum(analyticsEventNames);
238
- var serverOnlyAnalyticsEventSchema = z6.enum(serverOnlyAnalyticsEvents);
239
- var analyticsEventSourceSchema = z6.enum(analyticsEventSources);
240
- var analyticsIngestionChannelSchema = z6.enum(analyticsIngestionChannels);
241
- var analyticsTargetTypeSchema = z6.enum(analyticsTargetTypes);
242
- var analyticsDataAttributeEventSchema = z6.enum(analyticsDataAttributeEvents);
243
- var analyticsDeviceTypeSchema = z6.enum(analyticsDeviceTypes);
244
- var analyticsBotClassificationSchema = z6.enum(analyticsBotClassifications);
245
- var analyticsValidityStatusSchema = z6.enum(analyticsValidityStatuses);
246
- var analyticsPropertiesSchema = z6.record(z6.string(), z6.unknown());
247
- var analyticsTimestampSchema = z6.string().datetime({ offset: true });
248
- var analyticsBrowserEventSchema = z6.object({
249
- eventId: z6.string().min(1).max(160),
250
- installationGeneration: z6.number().int().positive().optional(),
251
- measurementMode: z6.enum(["session_only", "full_analytics"]).optional(),
252
- eventName: analyticsEventNameSchema,
253
- eventSource: analyticsEventSourceSchema.optional().default("browser"),
254
- targetId: z6.string().min(1).max(160).optional(),
255
- targetType: analyticsTargetTypeSchema.optional(),
256
- visitorId: analyticsVisitorIdSchema.optional(),
257
- sessionId: analyticsSessionIdSchema,
258
- idempotencyKey: z6.string().min(1).max(240).optional(),
259
- occurredAt: analyticsTimestampSchema,
260
- pageUrl: z6.string().url().max(2048).optional(),
261
- pagePath: z6.string().min(1).max(1024).optional(),
262
- routeTemplate: z6.string().min(1).max(1024).optional(),
263
- referrer: z6.string().url().max(2048).optional(),
264
- utmSource: z6.string().min(1).max(200).optional(),
265
- utmMedium: z6.string().min(1).max(200).optional(),
266
- utmCampaign: z6.string().min(1).max(200).optional(),
267
- utmContent: z6.string().min(1).max(200).optional(),
268
- utmTerm: z6.string().min(1).max(200).optional(),
269
- deviceType: analyticsDeviceTypeSchema.optional(),
270
- browserName: z6.string().min(1).max(120).optional(),
271
- osName: z6.string().min(1).max(120).optional(),
272
- country: z6.string().min(1).max(80).optional(),
273
- region: z6.string().min(1).max(120).optional(),
274
- properties: analyticsPropertiesSchema.optional().default({})
275
- }).superRefine((event, context) => {
276
- if (isServerOnlyAnalyticsEvent(event.eventName)) {
277
- context.addIssue({
278
- code: "custom",
279
- message: "Browser analytics payloads cannot contain server-only events.",
280
- path: ["eventName"]
281
- });
282
- }
283
- if (event.eventSource === "server" || event.eventSource === "system") {
284
- context.addIssue({
285
- code: "custom",
286
- message: "Browser analytics payloads cannot use server/system sources.",
287
- path: ["eventSource"]
288
- });
289
- }
290
- if (event.targetType === "server_conversion") {
291
- context.addIssue({
292
- code: "custom",
293
- message: "Browser analytics payloads cannot use server conversion targets.",
294
- path: ["targetType"]
295
- });
296
- }
297
- });
298
- var analyticsBrowserEventPayloadSchema = z6.object({
299
- siteKey: z6.string().min(1),
300
- schemaVersion: z6.literal(ANALYTICS_SCHEMA_VERSION),
301
- mode: z6.enum(["live", "test"]).optional().default("live"),
302
- events: z6.array(analyticsBrowserEventSchema).min(1).max(50)
303
- });
304
- var analyticsNormalizedEventBaseSchema = z6.object({
305
- site_id: z6.string().uuid(),
306
- event_id: z6.string().min(1).max(160),
307
- schema_version: z6.literal(ANALYTICS_SCHEMA_VERSION),
308
- event_name: analyticsEventNameSchema,
309
- event_source: analyticsEventSourceSchema,
310
- ingestion_channel: analyticsIngestionChannelSchema,
311
- target_id: z6.string().min(1).max(160).nullable().optional(),
312
- target_type: analyticsTargetTypeSchema.nullable().optional(),
313
- dedupe_key: z6.string().regex(/^analytics_dedupe_v1_[a-f0-9]{64}$/u).nullable().optional(),
314
- idempotency_key: z6.string().min(1).max(240).nullable().optional(),
315
- bot_classification: analyticsBotClassificationSchema,
316
- validity_status: analyticsValidityStatusSchema,
317
- rejection_reason: z6.string().min(1).max(240).nullable().optional(),
318
- occurred_at: analyticsTimestampSchema,
319
- received_at: analyticsTimestampSchema,
320
- page_url: z6.string().url().max(2048).nullable().optional(),
321
- page_path: z6.string().min(1).max(1024).nullable().optional(),
322
- route_template: z6.string().min(1).max(1024).nullable().optional(),
323
- referrer: z6.string().url().max(2048).nullable().optional(),
324
- utm_source: z6.string().min(1).max(200).nullable().optional(),
325
- utm_medium: z6.string().min(1).max(200).nullable().optional(),
326
- utm_campaign: z6.string().min(1).max(200).nullable().optional(),
327
- utm_content: z6.string().min(1).max(200).nullable().optional(),
328
- utm_term: z6.string().min(1).max(200).nullable().optional(),
329
- country: z6.string().min(1).max(80).nullable().optional(),
330
- region: z6.string().min(1).max(120).nullable().optional(),
331
- device_type: analyticsDeviceTypeSchema,
332
- browser_name: z6.string().min(1).max(120).nullable().optional(),
333
- os_name: z6.string().min(1).max(120).nullable().optional(),
334
- provider: z6.string().min(1).max(120).nullable().optional(),
335
- provider_event_id: z6.string().min(1).max(240).nullable().optional(),
336
- properties: analyticsPropertiesSchema
337
- });
338
- var analyticsRawNormalizedEventSchema = analyticsNormalizedEventBaseSchema.extend({
339
- visitor_id: analyticsVisitorIdSchema.nullable().optional(),
340
- session_id: analyticsSessionIdSchema.nullable().optional()
341
- });
342
- var analyticsNormalizedEventSchema = analyticsNormalizedEventBaseSchema.extend({
343
- visitor_id: analyticsPersistedIdentityHashSchema.nullable().optional(),
344
- session_id: analyticsPersistedIdentityHashSchema.nullable().optional()
345
- });
346
- function isServerOnlyAnalyticsEvent(eventName) {
347
- return serverOnlyAnalyticsEvents.includes(eventName);
348
- }
1
+ import {
2
+ SITEPLANE_CONFIG_FILE,
3
+ readSiteplaneConfig
4
+ } from "./chunk-KZ3AQ6OA.js";
349
5
 
350
- // ../shared/dist/analytics-manifest.js
351
- import { z as z7 } from "zod";
352
- var analyticsTargetIdSchema = z7.string().min(1).max(120).regex(/^[a-zA-Z][a-zA-Z0-9]*(?:[._-][a-zA-Z0-9]+)*$/u, {
353
- message: "Analytics target IDs must be stable identifiers without whitespace or visible copy."
354
- });
355
- var analyticsManifestRecordSchema = z7.record(z7.string(), z7.unknown());
356
- var analyticsFormPurposes = [
357
- "contact",
358
- "lead",
359
- "newsletter",
360
- "search",
361
- "login",
362
- "other"
363
- ];
364
- var analyticsFormPurposeSchema = z7.enum(analyticsFormPurposes);
365
- var analyticsManifestRouteSchema = z7.object({
366
- path: z7.string().min(1).max(1024),
367
- pageType: z7.string().min(1).max(120).optional(),
368
- label: z7.string().min(1).max(200).optional()
369
- }).passthrough();
370
- var analyticsManifestTargetSchema = z7.object({
371
- id: analyticsTargetIdSchema,
372
- type: analyticsTargetTypeSchema,
373
- event: analyticsEventNameSchema.optional(),
374
- events: z7.array(analyticsEventNameSchema).min(1).max(20).optional(),
375
- route: z7.string().min(1).max(1024).optional(),
376
- label: z7.string().min(1).max(200).optional(),
377
- description: z7.string().min(1).max(1e3).optional(),
378
- autoActive: z7.boolean().optional(),
379
- sourceHints: analyticsManifestRecordSchema.optional()
380
- }).passthrough().superRefine((target, context) => {
381
- if (target.event === void 0 && target.events === void 0) {
382
- context.addIssue({
383
- code: "custom",
384
- message: "Analytics targets require event or events.",
385
- path: ["event"]
386
- });
387
- }
388
- if (target.event !== void 0 && target.events !== void 0) {
389
- context.addIssue({
390
- code: "custom",
391
- message: "Analytics targets must use event or events, never both.",
392
- path: ["events"]
393
- });
394
- }
395
- if (target.type === "form" && !analyticsFormPurposeSchema.safeParse(target.sourceHints?.formPurpose).success) {
396
- context.addIssue({
397
- code: "custom",
398
- message: "Analytics form targets require a typed sourceHints.formPurpose.",
399
- path: ["sourceHints", "formPurpose"]
400
- });
401
- }
402
- });
403
- var analyticsManifestSchema = z7.object({
404
- schemaVersion: z7.literal(ANALYTICS_SCHEMA_VERSION),
405
- siteId: z7.string().min(1),
406
- siteKeyRef: z7.string().regex(/^env:[A-Z][A-Z0-9_]*$/u, {
407
- message: "siteKeyRef must be an environment-variable reference."
408
- }),
409
- siteKeyLookup: z7.string().regex(/^pk_siteplane_[A-Za-z0-9_-]{16}$/),
410
- routes: z7.array(analyticsManifestRouteSchema),
411
- targets: z7.array(analyticsManifestTargetSchema),
412
- metadata: analyticsManifestRecordSchema.optional(),
413
- siteKey: z7.string().optional(),
414
- secretKey: z7.string().optional(),
415
- rawKey: z7.string().optional(),
416
- token: z7.string().optional(),
417
- accessToken: z7.string().optional()
418
- }).passthrough().superRefine((manifest, context) => {
419
- const targetIds = /* @__PURE__ */ new Set();
420
- for (const [index, target] of manifest.targets.entries()) {
421
- if (targetIds.has(target.id)) {
422
- context.addIssue({
423
- code: "custom",
424
- message: `Duplicate analytics target ID: ${target.id}`,
425
- path: ["targets", index, "id"]
426
- });
6
+ // src/next.ts
7
+ import { existsSync, readFileSync } from "fs";
8
+ import { access } from "fs/promises";
9
+ import { createRequire } from "module";
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;
18
+ try {
19
+ const project = readProjectConfig(projectDir);
20
+ if (project) config = withEditor(config, project, { projectDir, admin: options.admin === true });
21
+ } catch (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
+ );
427
25
  }
428
- targetIds.add(target.id);
429
- }
430
- for (const issue of findUnsafeSecretReferences(manifest)) {
431
- context.addIssue({
432
- code: "custom",
433
- message: "Raw secret material is not allowed in analytics manifests.",
434
- path: issue
435
- });
436
- }
437
- });
438
- var secretValuePattern = /(?:\bsk_(?:live|test)_|\bcpb_|\bpmt_(?:pc|setup|cli)_|\bservice_role\b|bearer\s+[a-z0-9._~-]+)/iu;
439
- function findUnsafeSecretReferences(value, path = []) {
440
- if (Array.isArray(value)) {
441
- return value.flatMap((item, index) => findUnsafeSecretReferences(item, [...path, index]));
442
- }
443
- if (!isPlainRecord(value)) {
444
- return [];
445
- }
446
- return Object.entries(value).flatMap(([key, childValue]) => {
447
- const childPath = [...path, key];
448
- const referenceLikeKey = /(?:ref|lookup|prefix)$/iu.test(key);
449
- const unsafeHere = referenceLikeKey && typeof childValue === "string" && secretValuePattern.test(childValue);
450
- return [
451
- ...unsafeHere ? [childPath] : [],
452
- ...findUnsafeSecretReferences(childValue, childPath)
453
- ];
454
- });
455
- }
456
- function isPlainRecord(value) {
457
- return typeof value === "object" && value !== null;
458
- }
459
-
460
- // ../shared/dist/analytics-metrics.js
461
- import { z as z8 } from "zod";
462
- var analyticsVisitorQualities = ["exact", "estimated"];
463
- var analyticsVisitorQualitySchema = z8.enum(analyticsVisitorQualities);
464
- var analyticsKpiKeys = [
465
- "pageViews",
466
- "sessions",
467
- "visitors",
468
- "estimatedVisitors",
469
- "inquiries",
470
- "leads",
471
- "conversions",
472
- "conversionRate",
473
- "inquiryRate"
474
- ];
475
- var analyticsKpiKeySchema = z8.enum(analyticsKpiKeys);
476
-
477
- // ../shared/dist/analytics-privacy.js
478
- var serverPropertySchemas = {
479
- lead_created: {
480
- conversionId: "identifier",
481
- formId: "identifier",
482
- formType: "enum",
483
- customerRef: "identifier"
484
- },
485
- conversion: {
486
- conversionId: "identifier",
487
- conversionType: "enum",
488
- attributionConfidence: "enum",
489
- bookingSignalSource: "enum",
490
- bookingProvider: "enum",
491
- redirectStatus: "enum",
492
- importId: "identifier"
493
- },
494
- checkout_completed: revenuePropertySchema(),
495
- payment_succeeded: revenuePropertySchema(),
496
- subscription_started: revenuePropertySchema(),
497
- purchase_completed: revenuePropertySchema(),
498
- trial_started: revenuePropertySchema()
499
- };
500
- function revenuePropertySchema() {
501
- return {
502
- conversionId: "identifier",
503
- checkoutId: "identifier",
504
- customerRef: "identifier",
505
- amount: "amount",
506
- currency: "enum",
507
- planId: "identifier",
508
- productId: "identifier"
26
+ return options.booking ? await withBooking(config, options.booking.site, projectDir) : config;
509
27
  };
510
28
  }
511
-
512
- // ../shared/dist/assets.js
513
- import { z as z9 } from "zod";
514
- var ASSET_STAGES = ["staging", "final", "deleted"];
515
- var ASSET_VISIBILITIES = ["private", "public", "deleted"];
516
- var ASSET_USAGE_ENTITY_TYPES = [
517
- "branding",
518
- "draft",
519
- "change_set",
520
- "published_value"
521
- ];
522
- var assetStageSchema = z9.enum(ASSET_STAGES);
523
- var assetVisibilitySchema = z9.enum(ASSET_VISIBILITIES);
524
- var assetUsageEntityTypeSchema = z9.enum(ASSET_USAGE_ENTITY_TYPES);
525
- var brandingAccentColorSchema = z9.string().regex(/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/);
526
-
527
- // ../shared/dist/booking-contracts.js
528
- import { z as z10 } from "zod";
529
- var bookingStatuses = [
530
- "pending_payment",
531
- "confirmed",
532
- "payment_expired",
533
- "cancelled",
534
- "no_show"
535
- ];
536
- var bookingPaymentStatuses = [
537
- "payment_not_required",
538
- "checkout_open",
539
- "processing",
540
- "succeeded",
541
- "failed",
542
- "cancelled",
543
- "expired"
544
- ];
545
- var bookingAccountReadinessStatuses = [
546
- "not_started",
547
- "setup_required",
548
- "under_review",
549
- "payments_ready",
550
- "payouts_restricted",
551
- "payments_restricted",
552
- "deactivated"
553
- ];
554
- var bookingRefundStatuses = [
555
- "none",
556
- "pending",
557
- "partially_refunded",
558
- "refunded",
559
- "failed"
560
- ];
561
- var bookingDisputeStatuses = [
562
- "none",
563
- "needs_response",
564
- "under_review",
565
- "won",
566
- "lost"
567
- ];
568
- var bookingPaymentModes = [
569
- "onsite_only",
570
- "online_required",
571
- "customer_choice"
572
- ];
573
- var bookingPaymentCollectionTypes = [
574
- "full_amount",
575
- "deposit_percent"
576
- ];
577
- var bookingResourceKinds = [
578
- "person",
579
- "room",
580
- "equipment",
581
- "other"
582
- ];
583
- var bookingStatusSchema = z10.enum(bookingStatuses);
584
- var bookingPaymentStatusSchema = z10.enum(bookingPaymentStatuses);
585
- var bookingAccountReadinessStatusSchema = z10.enum(bookingAccountReadinessStatuses);
586
- var bookingRefundStatusSchema = z10.enum(bookingRefundStatuses);
587
- var bookingDisputeStatusSchema = z10.enum(bookingDisputeStatuses);
588
- var bookingPaymentModeSchema = z10.enum(bookingPaymentModes);
589
- var bookingPaymentCollectionTypeSchema = z10.enum(bookingPaymentCollectionTypes);
590
- var bookingPaymentCollectionSchema = z10.discriminatedUnion("type", [
591
- z10.object({ type: z10.literal("full_amount") }).strict(),
592
- z10.object({
593
- type: z10.literal("deposit_percent"),
594
- depositPercent: z10.number().int().min(10).max(90)
595
- }).strict()
596
- ]);
597
- var bookingPaymentChoices = ["onsite", "online"];
598
- var bookingPaymentChoiceSchema = z10.enum(bookingPaymentChoices);
599
- var bookingPaymentSnapshotSchema = z10.object({
600
- snapshotId: z10.string().uuid(),
601
- snapshotVersion: z10.number().int().positive(),
602
- currency: z10.literal("EUR"),
603
- totalAmountCents: z10.number().int().min(0),
604
- onlineAmountCents: z10.number().int().min(0),
605
- onsiteRemainderCents: z10.number().int().min(0),
606
- maxStripeRefundAmountCents: z10.number().int().min(0)
607
- }).strict().superRefine((snapshot, context) => {
608
- if (snapshot.onlineAmountCents + snapshot.onsiteRemainderCents !== snapshot.totalAmountCents) {
609
- context.addIssue({
610
- code: "custom",
611
- message: "Payment snapshot amounts must add up to the total amount."
612
- });
613
- }
614
- if (snapshot.maxStripeRefundAmountCents > snapshot.onlineAmountCents) {
615
- context.addIssue({
616
- code: "custom",
617
- message: "Stripe refund basis cannot exceed the online amount."
618
- });
619
- }
620
- });
621
- var bookingServiceVariantSchema = z10.object({
622
- id: z10.string().uuid(),
623
- name: z10.string().trim().min(1).max(120),
624
- durationMinutes: z10.number().int().min(1).max(1440),
625
- priceCents: z10.number().int().min(0).nullable()
626
- }).strict();
627
- var bookingServiceVariantsSchema = z10.array(bookingServiceVariantSchema).min(1).max(20);
628
- var isoDateSchema = z10.string().regex(/^\d{4}-\d{2}-\d{2}$/);
629
- var utcDateTimeSchema = z10.string().datetime();
630
- var bookingLocalTimeSchema = z10.string().regex(/^(?:[01]\d|2[0-3]):[0-5]\d$/);
631
- var bookingAvailabilityIntervalsSchema = z10.array(z10.object({
632
- start: bookingLocalTimeSchema,
633
- end: bookingLocalTimeSchema
634
- }).strict().refine((interval) => interval.end > interval.start)).superRefine((intervals, context) => {
635
- const sorted = [...intervals].sort((left, right) => left.start.localeCompare(right.start));
636
- for (let index = 1; index < sorted.length; index += 1) {
637
- if (sorted[index].start < sorted[index - 1].end) {
638
- context.addIssue({
639
- code: "custom",
640
- message: "Booking availability intervals must not overlap."
641
- });
642
- return;
643
- }
644
- }
645
- });
646
- var bookingPublicConfigSchema = z10.object({
647
- site: z10.object({
648
- displayName: z10.string().trim().min(1),
649
- timezone: z10.string().trim().min(1),
650
- locale: z10.string().trim().min(2),
651
- bookingPageSlug: z10.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).min(2).max(64),
652
- privacyUrl: z10.string().url().nullable()
653
- }),
654
- branding: z10.object({
655
- logoUrl: z10.string().url().nullable(),
656
- accentColor: z10.string().trim().min(1).nullable()
657
- }),
658
- settings: z10.object({
659
- slotIntervalMinutes: z10.number().int().positive(),
660
- minLeadMinutes: z10.number().int().min(0),
661
- minCancelLeadMinutes: z10.number().int().min(0),
662
- maxAdvanceDays: z10.number().int().min(1),
663
- requirePhone: z10.boolean(),
664
- collectNote: z10.boolean(),
665
- confirmationMessage: z10.string().nullable()
666
- }),
667
- /** Freitexte des Salons, im Kassen-Schritt vor der Bestaetigung sichtbar. */
668
- policies: z10.object({
669
- cancellation: z10.string().nullable(),
670
- lateArrival: z10.string().nullable()
671
- }),
672
- payment: z10.object({
673
- mode: bookingPaymentModeSchema,
674
- collection: bookingPaymentCollectionSchema,
675
- onlinePaymentsReady: z10.boolean()
676
- }).strict(),
677
- serviceCategories: z10.array(z10.object({
678
- id: z10.string().uuid(),
679
- name: z10.string().trim().min(1),
680
- sortOrder: z10.number().int()
681
- })),
682
- serviceSubcategories: z10.array(z10.object({
683
- id: z10.string().uuid(),
684
- categoryId: z10.string().uuid(),
685
- name: z10.string().trim().min(1),
686
- sortOrder: z10.number().int()
687
- })),
688
- services: z10.array(z10.object({
689
- id: z10.string().trim().min(1),
690
- name: z10.string().trim().min(1),
691
- durationMinutes: z10.number().int().positive(),
692
- priceCents: z10.number().int().min(0).nullable(),
693
- currency: z10.string().regex(/^[A-Z]{3}$/),
694
- description: z10.string().nullable(),
695
- categoryId: z10.string().uuid(),
696
- subcategoryId: z10.string().uuid().nullable(),
697
- variants: bookingServiceVariantsSchema,
698
- allowsAnyResource: z10.boolean(),
699
- sortOrder: z10.number().int()
700
- })),
701
- resources: z10.array(z10.object({
702
- id: z10.string().trim().min(1),
703
- name: z10.string().trim().min(1),
704
- kind: z10.enum(bookingResourceKinds),
705
- /** Profilfoto für „Bei wem?“; `null` ohne Foto. */
706
- photoUrl: z10.string().url().nullable()
707
- })),
708
- serviceResources: z10.array(z10.object({
709
- serviceId: z10.string().trim().min(1),
710
- resourceId: z10.string().trim().min(1)
711
- }))
712
- });
713
- var bookingItemSchema = z10.object({
714
- serviceId: z10.string().trim().min(1),
715
- variantId: z10.string().trim().min(1)
716
- }).strict();
717
- var bookingItemsSchema = z10.array(bookingItemSchema).min(1).max(10);
718
- function parseBookingItemsParam(value) {
719
- const items = value.split(",").map((entry) => entry.trim()).filter((entry) => entry !== "").map((entry) => {
720
- const [serviceId, variantId, ...rest] = entry.split(":");
721
- return serviceId && variantId && rest.length === 0 ? { serviceId, variantId } : null;
722
- });
723
- return items.every((item) => item !== null) ? items : null;
724
- }
725
- var bookingSlotsQuerySchema = z10.object({
726
- items: z10.preprocess((value) => typeof value === "string" ? parseBookingItemsParam(value) : value, bookingItemsSchema),
727
- resourceId: z10.string().trim().min(1).optional(),
728
- fromDate: isoDateSchema,
729
- toDate: isoDateSchema
730
- });
731
- var bookingSlotsResponseSchema = z10.object({
732
- timezone: z10.string().trim().min(1),
733
- items: bookingItemsSchema,
734
- // Reine Behandlungsdauer ohne Puffer: das UI zeigt damit die Terminspanne,
735
- // ohne selbst rechnen zu muessen.
736
- totalDurationMinutes: z10.number().int().min(1),
737
- mode: z10.enum(["any", "resource"]),
738
- slots: z10.array(z10.object({
739
- startUtc: utcDateTimeSchema,
740
- resourceId: z10.string().trim().min(1).optional()
741
- }))
742
- });
743
- var bookingAvailabilityQuerySchema = z10.object({
744
- items: z10.preprocess((value) => typeof value === "string" ? parseBookingItemsParam(value) : value, bookingItemsSchema),
745
- resourceId: z10.string().trim().min(1).optional(),
746
- fromDate: isoDateSchema,
747
- toDate: isoDateSchema
748
- });
749
- var bookingAvailabilityResponseSchema = z10.object({
750
- timezone: z10.string().trim().min(1),
751
- totalDurationMinutes: z10.number().int().min(1),
752
- days: z10.array(z10.object({
753
- date: isoDateSchema,
754
- hasSlots: z10.boolean()
755
- })),
756
- /** Erster Tag mit Slot ab `fromDate`, auch ausserhalb des Fensters. */
757
- nextAvailableDate: isoDateSchema.nullable()
758
- });
759
- var bookingPublicErrorCodes = [
760
- "slot_unavailable",
761
- "validation_error",
762
- "origin_not_allowed",
763
- "invalid_key",
764
- "rate_limited",
765
- "payment_required",
766
- "payment_unavailable",
767
- "payment_amount_too_small",
768
- "already_cancelled",
769
- "too_late",
770
- "not_found"
771
- ];
772
- var bookingPublicErrorSchema = z10.object({
773
- error: z10.enum(bookingPublicErrorCodes),
774
- message: z10.string().optional(),
775
- requestId: z10.string().optional()
776
- }).strict();
777
- var bookingCreateRequestSchema = z10.object({
778
- items: bookingItemsSchema,
779
- resourceId: z10.string().min(1).nullable(),
780
- startUtc: utcDateTimeSchema,
781
- customer: z10.object({
782
- name: z10.string().min(1).max(120),
783
- email: z10.string().email().max(254),
784
- phone: z10.string().max(32).optional(),
785
- note: z10.string().max(1e3).optional()
786
- }).strict(),
787
- hp: z10.string().optional(),
788
- clientToken: z10.string().uuid(),
789
- paymentChoice: bookingPaymentChoiceSchema,
790
- // Werbe-Einwilligung. Bestaetigung, Erinnerung und Storno-Link sind davon
791
- // unabhaengig und werden immer versendet.
792
- consent: z10.object({
793
- marketingEmail: z10.boolean(),
794
- marketingSms: z10.boolean()
795
- }).strict().optional()
796
- }).strict();
797
- var bookingPublicPaymentResponseSchema = z10.object({
798
- clientSecret: z10.string().min(1),
799
- stripeAccountId: z10.string().startsWith("acct_")
800
- }).strict();
801
- var bookingCreateResponseSchema = z10.union([
802
- z10.object({
803
- status: z10.literal("confirmed"),
804
- booking: z10.object({
805
- id: z10.string().min(1),
806
- startUtc: utcDateTimeSchema,
807
- endUtc: utcDateTimeSchema,
808
- timezone: z10.string().trim().min(1),
809
- serviceName: z10.string().trim().min(1),
810
- resourceName: z10.string().trim().min(1)
811
- }).strict(),
812
- manageUrl: z10.string().url(),
813
- confirmationMessage: z10.string().nullable()
814
- }).strict(),
815
- z10.object({
816
- status: z10.literal("pending_payment"),
817
- bookingId: z10.string().min(1),
818
- holdExpiresAt: utcDateTimeSchema,
819
- payment: bookingPublicPaymentResponseSchema,
820
- statusToken: z10.string().min(20),
821
- manageUrl: z10.string().url().optional()
822
- }).strict(),
823
- bookingPublicErrorSchema
824
- ]);
825
- var bookingStatusResponseSchema = z10.object({
826
- bookingStatus: bookingStatusSchema,
827
- paymentStatus: bookingPaymentStatusSchema,
828
- refundStatus: bookingRefundStatusSchema,
829
- disputeStatus: bookingDisputeStatusSchema,
830
- holdExpiresAt: utcDateTimeSchema.nullable()
831
- }).strict();
832
- var bookingCancelRequestSchema = z10.object({
833
- token: z10.string().min(20)
834
- });
835
- var bookingCancelResponseSchema = z10.union([
836
- z10.object({
837
- status: z10.enum(["cancelled", "already_cancelled", "too_late", "not_found"])
838
- }),
839
- bookingPublicErrorSchema
840
- ]);
841
- var bookingMoveErrorCodes = [
842
- "slot_unavailable",
843
- "booking_not_movable",
844
- "not_found"
845
- ];
846
- var bookingMoveRequestSchema = z10.object({
847
- bookingId: z10.string().uuid(),
848
- newStartUtc: utcDateTimeSchema,
849
- newResourceId: z10.string().uuid().optional(),
850
- notifyCustomer: z10.boolean().default(false),
851
- reason: z10.string().max(500).optional()
852
- });
853
- var bookingMoveResponseSchema = z10.union([
854
- z10.object({
855
- status: z10.literal("moved"),
856
- bookingId: z10.string().uuid(),
857
- startUtc: utcDateTimeSchema,
858
- endUtc: utcDateTimeSchema,
859
- resourceId: z10.string().uuid()
860
- }),
861
- z10.object({
862
- error: z10.enum(bookingMoveErrorCodes),
863
- message: z10.string().optional(),
864
- requestId: z10.string().optional()
865
- })
866
- ]);
867
- var bookingManifestSchema = z10.object({
868
- schemaVersion: z10.number().int().positive(),
869
- siteId: z10.string().trim().min(1),
870
- bookingPageSlug: z10.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).min(2).max(64),
871
- integration: z10.literal("rewrite").optional(),
872
- productionOrigin: z10.string().url().optional()
873
- }).passthrough();
874
-
875
- // ../shared/dist/booking-defaults.js
876
- var BOOKING_CREATE_PAYLOAD_LIMIT_BYTES = 16 * 1024;
877
-
878
- // ../shared/dist/bridge-protocol.js
879
- import { z as z17 } from "zod";
880
-
881
- // ../shared/dist/field-id.js
882
- import { z as z11 } from "zod";
883
- var FIELD_ID_PATTERN = /^[a-z][a-z0-9_-]*(\.[a-z][a-z0-9_-]*)+$/;
884
- var fieldIdSchema = z11.string().regex(FIELD_ID_PATTERN, "Invalid field id");
885
- function isFieldId(value) {
886
- return typeof value === "string" && fieldIdSchema.safeParse(value).success;
887
- }
888
-
889
- // ../shared/dist/field-types.js
890
- import { z as z12 } from "zod";
891
- var FIELD_TYPES = ["text", "longText", "image", "link"];
892
- var fieldTypeSchema = z12.enum(FIELD_TYPES);
893
-
894
- // ../shared/dist/image-value.js
895
- import { z as z13 } from "zod";
896
- var SUPPORTED_IMAGE_CONTENT_TYPES = [
897
- "image/jpeg",
898
- "image/png",
899
- "image/webp",
900
- "image/avif"
901
- ];
902
- var maxImageUploadBytes = 10 * 1024 * 1024;
903
- var largeImageWarningBytes = 2 * 1024 * 1024;
904
- var imageContentTypeSchema = z13.enum(SUPPORTED_IMAGE_CONTENT_TYPES);
905
- var imageCropSchema = z13.object({
906
- x: z13.number().min(0).max(100),
907
- y: z13.number().min(0).max(100),
908
- zoom: z13.number().min(1).max(4),
909
- aspectRatio: z13.number().positive().max(10)
910
- });
911
- var imageValueSchema = z13.object({
912
- src: z13.string().trim().min(1),
913
- alt: z13.string().trim().min(1).max(300),
914
- width: z13.number().int().positive().optional(),
915
- height: z13.number().int().positive().optional(),
916
- crop: imageCropSchema.optional(),
917
- assetId: z13.string().uuid().optional(),
918
- finalStorageProvider: z13.enum([
919
- "repo_static_assets",
920
- "repo_handoff",
921
- "site_owned_storage",
922
- "siteplane_managed_fallback"
923
- ]).optional()
924
- });
925
-
926
- // ../shared/dist/missing-content.js
927
- import { z as z14 } from "zod";
928
- var MISSING_CONTENT_RENDERED_PATH_MAX_BYTES = 512;
929
- var MISSING_CONTENT_VISIBLE_VALUE_MAX_BYTES = 256;
930
- var MISSING_CONTENT_LOCATOR_HINT_MAX_BYTES = 512;
931
- var MISSING_CONTENT_LABEL_MAX_BYTES = 120;
932
- var forbiddenControlOrBidi = /[\u0000-\u001f\u007f-\u009f‪-‮⁦-⁩]/u;
933
- var unsafeLocator = /<\/?[a-z][^>]*>|\b(?:javascript|data:text\/html)\s*:|\bon[a-z]+\s*=/iu;
934
- var renderedPathSchema = z14.string().trim().transform((value) => value !== "/" ? value.replace(/\/+$/u, "") : value).superRefine((value, context) => {
935
- if (!value.startsWith("/") || value === "$global" || value.includes("?") || value.includes("#")) {
936
- context.addIssue({
937
- code: "custom",
938
- message: "renderedPath must be a canonical real path."
939
- });
940
- }
941
- addSafeByteIssues(value, MISSING_CONTENT_RENDERED_PATH_MAX_BYTES, "renderedPath", context);
942
- });
943
- function normalizedSafeTextSchema(maxBytes, label) {
944
- return z14.string().transform((value) => value.trim().replace(new RegExp("\\p{White_Space}+", "gu"), " ")).pipe(z14.string().min(1)).superRefine((value, context) => {
945
- addSafeByteIssues(value, maxBytes, label, context);
946
- if (label === "locatorHint" && unsafeLocator.test(value)) {
947
- context.addIssue({
948
- code: "custom",
949
- message: "locatorHint contains unsafe markup or executable content."
950
- });
951
- }
952
- });
953
- }
954
- var missingContentItemBase = {
955
- renderedPath: renderedPathSchema,
956
- visibleValue: normalizedSafeTextSchema(MISSING_CONTENT_VISIBLE_VALUE_MAX_BYTES, "visibleValue"),
957
- locatorHint: normalizedSafeTextSchema(MISSING_CONTENT_LOCATOR_HINT_MAX_BYTES, "locatorHint"),
958
- requestedLabel: normalizedSafeTextSchema(MISSING_CONTENT_LABEL_MAX_BYTES, "requestedLabel").optional(),
959
- requestedSection: normalizedSafeTextSchema(MISSING_CONTENT_LABEL_MAX_BYTES, "requestedSection").optional(),
960
- requestedSubsection: normalizedSafeTextSchema(MISSING_CONTENT_LABEL_MAX_BYTES, "requestedSubsection").optional(),
961
- requestedSortOrder: z14.number().int().nonnegative().optional()
962
- };
963
- var missingContentItemSchema = z14.discriminatedUnion("kind", [
964
- z14.object({ kind: z14.literal("text"), ...missingContentItemBase }).strict(),
965
- z14.object({ kind: z14.literal("link"), ...missingContentItemBase }).strict(),
966
- z14.object({ kind: z14.literal("image"), ...missingContentItemBase }).strict()
967
- ]).superRefine((item, context) => {
968
- if (item.requestedSubsection && !item.requestedSection) {
969
- context.addIssue({
970
- code: "custom",
971
- message: "requestedSubsection requires requestedSection.",
972
- path: ["requestedSubsection"]
973
- });
974
- }
975
- });
976
- function addSafeByteIssues(value, maxBytes, label, context) {
977
- if (new TextEncoder().encode(value).byteLength > maxBytes) {
978
- context.addIssue({
979
- code: "custom",
980
- message: `${label} exceeds ${maxBytes} UTF-8 bytes.`
981
- });
982
- }
983
- if (forbiddenControlOrBidi.test(value)) {
984
- context.addIssue({
985
- code: "custom",
986
- message: `${label} contains a forbidden control or bidi character.`
987
- });
988
- }
989
- }
990
-
991
- // ../shared/dist/site-field-contract.js
992
- import { z as z16 } from "zod";
993
-
994
- // ../shared/dist/fallback-hash.js
995
- import { z as z15 } from "zod";
996
- var SAFE_REPRESENTATION_REASONS = [
997
- "asset_import_rewritten",
998
- "relative_url_normalized",
999
- "line_endings_normalized",
1000
- "image_value_serialized"
1001
- ];
1002
- var safeRepresentationReasonSchema = z15.enum(SAFE_REPRESENTATION_REASONS);
1003
- function canonicalizeFallbackValue(input) {
1004
- const fieldType = fieldTypeSchema.parse(input.fieldType);
1005
- if (input.safeRepresentationReason !== void 0) {
1006
- safeRepresentationReasonSchema.parse(input.safeRepresentationReason);
1007
- }
1008
- if (fieldType === "text" || fieldType === "longText") {
1009
- if (typeof input.fallbackValue !== "string") {
1010
- throw new Error("Text fallback values must be strings.");
1011
- }
1012
- return {
1013
- fieldType,
1014
- fallbackValue: normalizeLineEndings(input.fallbackValue)
1015
- };
1016
- }
1017
- if (fieldType === "link") {
1018
- const link = linkFallbackSchema.parse(input.fallbackValue);
1019
- return {
1020
- fieldType,
1021
- fallbackValue: sortObjectKeys({
1022
- ...link,
1023
- href: normalizeExplicitHref(link.href)
1024
- })
1025
- };
1026
- }
1027
- const image = imageValueSchema.parse(input.fallbackValue);
1028
- return {
1029
- fieldType,
1030
- fallbackValue: sortObjectKeys(image)
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;
1031
45
  };
1032
- }
1033
- function createCanonicalFallbackHash(input) {
1034
- const canonical = canonicalizeFallbackValue(input);
1035
- const payload = stableJsonStringify(canonical);
1036
- return `sha256:${sha256Hex(payload)}`;
1037
- }
1038
- var linkFallbackSchema = z15.object({
1039
- href: z15.string().trim().min(1),
1040
- label: z15.string().trim().min(1).optional(),
1041
- target: z15.enum(["_self", "_blank"]).optional()
1042
- }).passthrough();
1043
- function normalizeLineEndings(value) {
1044
- return value.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1045
- }
1046
- function normalizeExplicitHref(value) {
1047
- const href = value.trim();
1048
- try {
1049
- const parsed = new URL(href);
1050
- const hasRootSlashOnly = parsed.pathname === "/" && parsed.search === "" && parsed.hash === "";
1051
- if (hasRootSlashOnly) {
1052
- return `${parsed.protocol}//${parsed.host}`;
1053
- }
1054
- } catch {
1055
- return href;
1056
- }
1057
- return href;
1058
- }
1059
- function stableJsonStringify(value) {
1060
- return JSON.stringify(sortObjectKeys(value));
1061
- }
1062
- function sortObjectKeys(value) {
1063
- if (Array.isArray(value)) {
1064
- return value.map(sortObjectKeys);
1065
- }
1066
- if (!value || typeof value !== "object") {
1067
- return value;
1068
- }
1069
- return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entryValue]) => [key, sortObjectKeys(entryValue)]));
1070
- }
1071
- function sha256Hex(input) {
1072
- const bytes = new TextEncoder().encode(input);
1073
- const paddedLength = Math.ceil((bytes.length + 1 + 8) / 64) * 64;
1074
- const padded = new Uint8Array(paddedLength);
1075
- padded.set(bytes);
1076
- padded[bytes.length] = 128;
1077
- const bitLength = bytes.length * 8;
1078
- const view = new DataView(padded.buffer);
1079
- view.setUint32(paddedLength - 8, Math.floor(bitLength / 4294967296));
1080
- view.setUint32(paddedLength - 4, bitLength >>> 0);
1081
- let h0 = 1779033703;
1082
- let h1 = 3144134277;
1083
- let h2 = 1013904242;
1084
- let h3 = 2773480762;
1085
- let h4 = 1359893119;
1086
- let h5 = 2600822924;
1087
- let h6 = 528734635;
1088
- let h7 = 1541459225;
1089
- const words = new Array(64).fill(0);
1090
- for (let chunk = 0; chunk < paddedLength; chunk += 64) {
1091
- for (let index = 0; index < 16; index += 1) {
1092
- words[index] = view.getUint32(chunk + index * 4);
1093
- }
1094
- for (let index = 16; index < 64; index += 1) {
1095
- words[index] = smallSigma1(words[index - 2] ?? 0) + (words[index - 7] ?? 0) + smallSigma0(words[index - 15] ?? 0) + (words[index - 16] ?? 0) >>> 0;
1096
- }
1097
- let a = h0;
1098
- let b = h1;
1099
- let c = h2;
1100
- let d = h3;
1101
- let e = h4;
1102
- let f = h5;
1103
- let g = h6;
1104
- let h = h7;
1105
- for (let index = 0; index < 64; index += 1) {
1106
- const temp1 = h + bigSigma1(e) + choose(e, f, g) + SHA256_CONSTANTS[index] + (words[index] ?? 0) >>> 0;
1107
- const temp2 = bigSigma0(a) + majority(a, b, c) >>> 0;
1108
- h = g;
1109
- g = f;
1110
- f = e;
1111
- e = d + temp1 >>> 0;
1112
- d = c;
1113
- c = b;
1114
- b = a;
1115
- a = temp1 + temp2 >>> 0;
1116
- }
1117
- h0 = h0 + a >>> 0;
1118
- h1 = h1 + b >>> 0;
1119
- h2 = h2 + c >>> 0;
1120
- h3 = h3 + d >>> 0;
1121
- h4 = h4 + e >>> 0;
1122
- h5 = h5 + f >>> 0;
1123
- h6 = h6 + g >>> 0;
1124
- h7 = h7 + h >>> 0;
1125
- }
1126
- return [h0, h1, h2, h3, h4, h5, h6, h7].map((value) => value.toString(16).padStart(8, "0")).join("");
1127
- }
1128
- function rightRotate(value, bits) {
1129
- return value >>> bits | value << 32 - bits;
1130
- }
1131
- function choose(x, y, zValue) {
1132
- return x & y ^ ~x & zValue;
1133
- }
1134
- function majority(x, y, zValue) {
1135
- return x & y ^ x & zValue ^ y & zValue;
1136
- }
1137
- function bigSigma0(value) {
1138
- return rightRotate(value, 2) ^ rightRotate(value, 13) ^ rightRotate(value, 22);
1139
- }
1140
- function bigSigma1(value) {
1141
- return rightRotate(value, 6) ^ rightRotate(value, 11) ^ rightRotate(value, 25);
1142
- }
1143
- function smallSigma0(value) {
1144
- return rightRotate(value, 7) ^ rightRotate(value, 18) ^ value >>> 3;
1145
- }
1146
- function smallSigma1(value) {
1147
- return rightRotate(value, 17) ^ rightRotate(value, 19) ^ value >>> 10;
1148
- }
1149
- var SHA256_CONSTANTS = [
1150
- 1116352408,
1151
- 1899447441,
1152
- 3049323471,
1153
- 3921009573,
1154
- 961987163,
1155
- 1508970993,
1156
- 2453635748,
1157
- 2870763221,
1158
- 3624381080,
1159
- 310598401,
1160
- 607225278,
1161
- 1426881987,
1162
- 1925078388,
1163
- 2162078206,
1164
- 2614888103,
1165
- 3248222580,
1166
- 3835390401,
1167
- 4022224774,
1168
- 264347078,
1169
- 604807628,
1170
- 770255983,
1171
- 1249150122,
1172
- 1555081692,
1173
- 1996064986,
1174
- 2554220882,
1175
- 2821834349,
1176
- 2952996808,
1177
- 3210313671,
1178
- 3336571891,
1179
- 3584528711,
1180
- 113926993,
1181
- 338241895,
1182
- 666307205,
1183
- 773529912,
1184
- 1294757372,
1185
- 1396182291,
1186
- 1695183700,
1187
- 1986661051,
1188
- 2177026350,
1189
- 2456956037,
1190
- 2730485921,
1191
- 2820302411,
1192
- 3259730800,
1193
- 3345764771,
1194
- 3516065817,
1195
- 3600352804,
1196
- 4094571909,
1197
- 275423344,
1198
- 430227734,
1199
- 506948616,
1200
- 659060556,
1201
- 883997877,
1202
- 958139571,
1203
- 1322822218,
1204
- 1537002063,
1205
- 1747873779,
1206
- 1955562222,
1207
- 2024104815,
1208
- 2227730452,
1209
- 2361852424,
1210
- 2428436474,
1211
- 2756734187,
1212
- 3204031479,
1213
- 3329325298
1214
- ];
1215
-
1216
- // ../shared/dist/site-field-contract.js
1217
- var fieldContractVersion = "1.0.0";
1218
- var scannerContractVersion = "1.0.0";
1219
- var siteFieldStatusSchema = z16.enum(["hidden", "active"]);
1220
- var semanticVersionSchema = z16.string().regex(/^\d+\.\d+\.\d+$/u);
1221
- var siteFieldRouteKeySchema = z16.string().trim().refine((value) => value === "$global" || value.startsWith("/") && !value.includes("?") && !value.includes("#") && (value === "/" || !value.endsWith("/")), "Route keys must be canonical paths or $global.");
1222
- var repositoryRelativePosixPathSchema = z16.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.");
1223
- var sourceMappingSchema = z16.object({
1224
- kind: z16.literal("function"),
1225
- filePath: repositoryRelativePosixPathSchema,
1226
- exportName: z16.string().trim().min(1).optional(),
1227
- sourcePath: z16.string().trim().min(1).optional(),
1228
- editTarget: fieldIdSchema.optional()
1229
- }).strict();
1230
- var domMappingSchema = z16.object({
1231
- attribute: z16.literal("data-siteplane-field-id"),
1232
- previewStrategy: z16.enum(["dom", "event"])
1233
- }).strict();
1234
- var fieldBaseSchema = z16.object({
1235
- fieldId: fieldIdSchema,
1236
- routeKey: siteFieldRouteKeySchema,
1237
- source: sourceMappingSchema,
1238
- dom: domMappingSchema.optional()
1239
- }).strict();
1240
- var linkFallbackSchema2 = z16.object({
1241
- href: z16.string().trim().min(1),
1242
- label: z16.string().trim().min(1).optional(),
1243
- target: z16.enum(["_self", "_blank"]).optional()
1244
- }).strict();
1245
- var imageFallbackSchema = z16.object({
1246
- src: z16.string().trim().min(1),
1247
- alt: z16.string().trim().max(300),
1248
- width: z16.number().int().positive().optional(),
1249
- height: z16.number().int().positive().optional(),
1250
- crop: z16.object({
1251
- x: z16.number().min(0).max(100),
1252
- y: z16.number().min(0).max(100),
1253
- zoom: z16.number().min(1).max(4),
1254
- aspectRatio: z16.number().positive().max(10)
1255
- }).strict().optional()
1256
- }).strict();
1257
- var siteFieldContractFieldSchema = z16.discriminatedUnion("fieldType", [
1258
- fieldBaseSchema.extend({
1259
- fieldType: z16.literal("text"),
1260
- fallback: z16.string()
1261
- }),
1262
- fieldBaseSchema.extend({
1263
- fieldType: z16.literal("longText"),
1264
- fallback: z16.string()
1265
- }),
1266
- fieldBaseSchema.extend({
1267
- fieldType: z16.literal("link"),
1268
- fallback: linkFallbackSchema2
1269
- }),
1270
- fieldBaseSchema.extend({
1271
- fieldType: z16.literal("image"),
1272
- fallback: imageFallbackSchema
1273
- })
1274
- ]);
1275
- var siteFieldContractV1Schema = z16.object({
1276
- version: z16.literal(1),
1277
- fieldContractVersion: semanticVersionSchema,
1278
- scannerContractVersion: semanticVersionSchema,
1279
- fields: z16.array(siteFieldContractFieldSchema)
1280
- }).strict().superRefine((contract, context) => {
1281
- const seen = /* @__PURE__ */ new Set();
1282
- for (const [index, field] of contract.fields.entries()) {
1283
- if (seen.has(field.fieldId)) {
1284
- context.addIssue({
1285
- code: "custom",
1286
- message: `Duplicate field id: ${field.fieldId}`,
1287
- path: ["fields", index, "fieldId"]
1288
- });
1289
- }
1290
- seen.add(field.fieldId);
1291
- }
1292
- });
1293
- function normalizeSiteFieldContract(input) {
1294
- const contract = siteFieldContractV1Schema.parse(input);
1295
- return {
1296
- ...contract,
1297
- fields: [...contract.fields].sort((left, right) => left.fieldId.localeCompare(right.fieldId))
1298
- };
1299
- }
1300
- function createSiteFieldContractHash(input) {
1301
- const payload = stableJsonStringify(normalizeSiteFieldContract(input));
1302
- return `sha256:${sha256Hex(payload)}`;
1303
- }
1304
-
1305
- // ../shared/dist/bridge-protocol.js
1306
- var BRIDGE_PROTOCOL_VERSION = 2;
1307
- var bridgeRectSchema = z17.object({
1308
- x: z17.number(),
1309
- y: z17.number(),
1310
- width: z17.number().nonnegative(),
1311
- height: z17.number().nonnegative()
1312
- });
1313
- var previewTextValueSchema = z17.object({
1314
- fieldType: z17.enum(["text", "longText"]),
1315
- value: z17.string()
1316
- });
1317
- var previewLinkValueSchema = z17.object({
1318
- fieldType: z17.literal("link"),
1319
- value: z17.object({
1320
- href: z17.string().trim().min(1),
1321
- label: z17.string().trim().min(1).optional()
1322
- })
1323
- });
1324
- var previewImageValueSchema = z17.object({
1325
- fieldType: z17.literal("image"),
1326
- value: imageValueSchema
1327
- });
1328
- var bridgePreviewValueSchema = z17.discriminatedUnion("fieldType", [
1329
- previewTextValueSchema,
1330
- previewLinkValueSchema,
1331
- previewImageValueSchema
1332
- ]);
1333
- var fieldSelectionModeSchema = z17.enum(["replace", "range", "toggle"]);
1334
- var fieldBridgeMissingContentActionSchema = z17.enum([
1335
- "add",
1336
- "select",
1337
- "remove"
1338
- ]);
1339
- var FIELD_BRIDGE_MESSAGE_TYPES = [
1340
- "siteplane:field-bridge:init",
1341
- "siteplane:field-bridge:ready",
1342
- "siteplane:field-bridge:fields",
1343
- "siteplane:field-bridge:select-field",
1344
- "siteplane:field-bridge:set-selected-field",
1345
- "siteplane:field-bridge:apply-preview-values",
1346
- "siteplane:field-bridge:test-preview-values",
1347
- "siteplane:field-bridge:preview-ack",
1348
- "siteplane:field-bridge:set-visual-control-state",
1349
- "siteplane:field-bridge:missing-content",
1350
- "siteplane:field-bridge:preview-scrolled",
1351
- "siteplane:field-bridge:analytics-probe",
1352
- "siteplane:field-bridge:analytics-probe-result",
1353
- "siteplane:field-bridge:error"
1354
- ];
1355
- var canonicalFieldContractHashSchema = z17.string().regex(/^sha256:[0-9a-f]{64}$/u);
1356
- var bridgeSectionSchema = z17.string().trim().min(1).max(256);
1357
- 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.");
1358
- var fieldBridgeIdentitySchema = z17.object({
1359
- publicRuntimeKeyId: z17.string().uuid(),
1360
- fieldContractHash: canonicalFieldContractHashSchema,
1361
- deploymentRevision: z17.string().trim().min(1).max(256)
1362
- }).strict();
1363
- var fieldBridgeBaseSchema = z17.object({
1364
- version: z17.literal(BRIDGE_PROTOCOL_VERSION),
1365
- type: z17.enum(FIELD_BRIDGE_MESSAGE_TYPES),
1366
- siteId: z17.string().uuid(),
1367
- sessionId: z17.string().trim().min(1),
1368
- publicRuntimeKeyId: z17.string().uuid(),
1369
- fieldContractHash: canonicalFieldContractHashSchema,
1370
- deploymentRevision: z17.string().trim().min(1).max(256),
1371
- requestId: z17.string().trim().min(1),
1372
- sentAt: z17.string().datetime()
1373
- }).strict();
1374
- var fieldBridgeFieldSchema = z17.object({
1375
- fieldId: fieldIdSchema,
1376
- fieldType: fieldTypeSchema,
1377
- routeKey: siteFieldRouteKeySchema,
1378
- renderedPath: realRenderedPathSchema,
1379
- sourcePath: z17.string().trim().min(1).optional(),
1380
- editTarget: fieldIdSchema,
1381
- rect: bridgeRectSchema,
1382
- targetResolved: z17.literal(true),
1383
- currentValue: bridgePreviewValueSchema.optional()
1384
- }).strict();
1385
- var fieldBridgeInitMessageSchema = fieldBridgeBaseSchema.extend({
1386
- type: z17.literal("siteplane:field-bridge:init"),
1387
- payload: z17.object({
1388
- adminOrigin: z17.string().url(),
1389
- customerOrigin: z17.string().url(),
1390
- renderedPath: realRenderedPathSchema
1391
- }).strict()
1392
- });
1393
- var fieldBridgeReadyMessageSchema = fieldBridgeBaseSchema.extend({
1394
- type: z17.literal("siteplane:field-bridge:ready"),
1395
- payload: z17.object({
1396
- renderedPath: realRenderedPathSchema,
1397
- fieldCount: z17.number().int().nonnegative(),
1398
- capabilities: z17.tuple([
1399
- z17.literal("field_selection"),
1400
- z17.literal("preview_values"),
1401
- z17.literal("field_evidence"),
1402
- z17.literal("visual_field_control")
1403
- ]).rest(z17.enum(["analytics_preview_readiness"])).refine((capabilities) => capabilities.length <= 5)
1404
- }).strict()
1405
- });
1406
- var fieldBridgeFieldsMessageSchema = fieldBridgeBaseSchema.extend({
1407
- type: z17.literal("siteplane:field-bridge:fields"),
1408
- payload: z17.object({
1409
- renderedPath: realRenderedPathSchema,
1410
- fields: z17.array(fieldBridgeFieldSchema)
1411
- }).strict()
1412
- });
1413
- var fieldBridgeSelectFieldMessageSchema = fieldBridgeBaseSchema.extend({
1414
- type: z17.literal("siteplane:field-bridge:select-field"),
1415
- payload: z17.object({
1416
- fieldId: fieldIdSchema,
1417
- selectionMode: fieldSelectionModeSchema
1418
- }).strict()
1419
- });
1420
- var fieldBridgeSetSelectedFieldMessageSchema = fieldBridgeBaseSchema.extend({
1421
- type: z17.literal("siteplane:field-bridge:set-selected-field"),
1422
- payload: z17.object({
1423
- fieldIds: z17.array(fieldIdSchema).max(200),
1424
- primaryFieldId: fieldIdSchema.nullable(),
1425
- scrollIntoView: z17.boolean().optional()
1426
- }).strict().superRefine((value, context) => {
1427
- if (value.fieldIds.length > 0 && value.primaryFieldId === null) {
1428
- context.addIssue({
1429
- code: "custom",
1430
- message: "A non-empty selection needs a primary field.",
1431
- path: ["primaryFieldId"]
1432
- });
1433
- }
1434
- if (value.primaryFieldId !== null && !value.fieldIds.includes(value.primaryFieldId)) {
1435
- context.addIssue({
1436
- code: "custom",
1437
- message: "The primary field must be part of the selection.",
1438
- path: ["primaryFieldId"]
1439
- });
1440
- }
1441
- })
1442
- });
1443
- var fieldBridgeApplyPreviewValuesMessageSchema = fieldBridgeBaseSchema.extend({
1444
- type: z17.literal("siteplane:field-bridge:apply-preview-values"),
1445
- payload: z17.object({ values: z17.record(fieldIdSchema, bridgePreviewValueSchema) }).strict()
1446
- });
1447
- var fieldBridgeTestPreviewValuesMessageSchema = fieldBridgeBaseSchema.extend({
1448
- type: z17.literal("siteplane:field-bridge:test-preview-values"),
1449
- payload: z17.object({ fieldIds: z17.array(fieldIdSchema).min(1).max(200) }).strict()
1450
- });
1451
- var fieldBridgePreviewAckMessageSchema = fieldBridgeBaseSchema.extend({
1452
- type: z17.literal("siteplane:field-bridge:preview-ack"),
1453
- payload: z17.object({
1454
- ackRequestId: z17.string().trim().min(1),
1455
- kind: z17.enum(["apply", "test"]),
1456
- renderedPath: realRenderedPathSchema,
1457
- fieldIds: z17.array(fieldIdSchema).max(200)
1458
- }).strict()
1459
- });
1460
- var fieldBridgeSetVisualControlStateMessageSchema = fieldBridgeBaseSchema.extend({
1461
- type: z17.literal("siteplane:field-bridge:set-visual-control-state"),
1462
- payload: z17.object({
1463
- enabled: z17.boolean(),
1464
- fieldProximityEnabled: z17.boolean(),
1465
- pendingKeys: z17.array(z17.string().trim().min(1).max(2048)).max(50),
1466
- activeFieldIds: z17.array(fieldIdSchema),
1467
- fieldSections: z17.record(fieldIdSchema, bridgeSectionSchema),
1468
- selectedPending: z17.array(z17.object({
1469
- key: z17.string().trim().min(1).max(2048),
1470
- locatorHint: z17.string().trim().min(1).max(2048)
1471
- }).strict()).max(50),
1472
- scrollPendingIntoView: z17.boolean().optional()
1473
- }).strict()
1474
- });
1475
- var fieldBridgeMissingContentMessageSchema = fieldBridgeBaseSchema.extend({
1476
- type: z17.literal("siteplane:field-bridge:missing-content"),
1477
- payload: z17.object({
1478
- action: fieldBridgeMissingContentActionSchema,
1479
- item: missingContentItemSchema,
1480
- suggestedSection: bridgeSectionSchema.optional()
1481
- }).strict()
1482
- });
1483
- var fieldBridgePreviewScrolledMessageSchema = fieldBridgeBaseSchema.extend({
1484
- type: z17.literal("siteplane:field-bridge:preview-scrolled"),
1485
- payload: z17.object({ renderedPath: realRenderedPathSchema }).strict()
1486
- });
1487
- var analyticsPreviewChallengeSchema = z17.object({
1488
- bridgeSessionId: z17.string().uuid(),
1489
- challenge: z17.string().min(32).max(512),
1490
- testRunId: z17.string().trim().min(1).max(160),
1491
- expiresAt: z17.string().datetime()
1492
- }).strict();
1493
- var fieldBridgeAnalyticsProbeSchema = fieldBridgeBaseSchema.extend({
1494
- type: z17.literal("siteplane:field-bridge:analytics-probe"),
1495
- requestId: z17.string().min(1).max(256),
1496
- payload: analyticsPreviewChallengeSchema
1497
- });
1498
- var fieldBridgeAnalyticsProbeResultSchema = fieldBridgeBaseSchema.extend({
1499
- type: z17.literal("siteplane:field-bridge:analytics-probe-result"),
1500
- payload: z17.object({
1501
- ackRequestId: z17.string().min(1).max(256),
1502
- status: z17.enum(["recorded", "failed"])
1503
- }).strict()
1504
- });
1505
- var fieldBridgeErrorMessageSchema = fieldBridgeBaseSchema.extend({
1506
- type: z17.literal("siteplane:field-bridge:error"),
1507
- payload: z17.object({
1508
- code: z17.string().trim().min(1),
1509
- message: z17.string().trim().min(1),
1510
- recoverable: z17.boolean().default(false)
1511
- }).strict()
1512
- });
1513
- var fieldBridgeMessageSchema = z17.discriminatedUnion("type", [
1514
- fieldBridgeInitMessageSchema,
1515
- fieldBridgeReadyMessageSchema,
1516
- fieldBridgeFieldsMessageSchema,
1517
- fieldBridgeSelectFieldMessageSchema,
1518
- fieldBridgeSetSelectedFieldMessageSchema,
1519
- fieldBridgeApplyPreviewValuesMessageSchema,
1520
- fieldBridgeTestPreviewValuesMessageSchema,
1521
- fieldBridgePreviewAckMessageSchema,
1522
- fieldBridgeSetVisualControlStateMessageSchema,
1523
- fieldBridgeMissingContentMessageSchema,
1524
- fieldBridgePreviewScrolledMessageSchema,
1525
- fieldBridgeErrorMessageSchema,
1526
- fieldBridgeAnalyticsProbeSchema,
1527
- fieldBridgeAnalyticsProbeResultSchema
1528
- ]);
1529
-
1530
- // ../shared/dist/commands.js
1531
- import { z as z19 } from "zod";
1532
-
1533
- // ../shared/dist/errors.js
1534
- import { z as z18 } from "zod";
1535
- var UI_ERROR_CATEGORIES = [
1536
- "site_member",
1537
- "workspace_member",
1538
- "internal"
1539
- ];
1540
- var uiErrorCategorySchema = z18.enum(UI_ERROR_CATEGORIES);
1541
- var commandErrorPayloadSchema = z18.object({
1542
- code: z18.string().min(1),
1543
- message: z18.string().min(1),
1544
- category: uiErrorCategorySchema,
1545
- details: z18.record(z18.string(), z18.unknown()).optional()
1546
- });
1547
-
1548
- // ../shared/dist/commands.js
1549
- var commandSuccessSchema = z19.object({
1550
- ok: z19.literal(true),
1551
- data: z19.unknown()
1552
- });
1553
- var commandErrorSchema = z19.object({
1554
- ok: z19.literal(false),
1555
- error: commandErrorPayloadSchema
1556
- });
1557
- var commandResultSchema = z19.discriminatedUnion("ok", [
1558
- commandSuccessSchema,
1559
- commandErrorSchema
1560
- ]);
1561
-
1562
- // ../shared/dist/editor-settings.js
1563
- import { z as z20 } from "zod";
1564
- var editorColorSchemeSchema = z20.enum([
1565
- "paper",
1566
- "slate",
1567
- "siteplane",
1568
- "bloom"
1569
- ]);
1570
- var editorColorSchemes = editorColorSchemeSchema.options;
1571
- var editorColorModeSchema = z20.enum(["light", "dark"]);
1572
- var editorColorModes = editorColorModeSchema.options;
1573
-
1574
- // ../shared/dist/editor-organization.js
1575
- import { z as z21 } from "zod";
1576
- var labelSchema = z21.string().trim().min(1).max(120);
1577
- var updateFieldSchema = z21.object({
1578
- operation: z21.literal("update_field"),
1579
- siteId: z21.string().uuid(),
1580
- fieldId: fieldIdSchema,
1581
- label: labelSchema.optional(),
1582
- description: z21.string().trim().max(500).nullable().optional(),
1583
- section: labelSchema.nullable().optional(),
1584
- subsection: labelSchema.nullable().optional(),
1585
- sortOrder: z21.number().int().nonnegative().optional(),
1586
- status: z21.enum(["hidden", "active"]).optional()
1587
- });
1588
- var updateEditorOrganizationInputSchema = z21.discriminatedUnion("operation", [
1589
- updateFieldSchema,
1590
- z21.object({
1591
- operation: z21.literal("rename_section"),
1592
- siteId: z21.string().uuid(),
1593
- routeKey: siteFieldRouteKeySchema,
1594
- currentSection: labelSchema,
1595
- nextSection: labelSchema
1596
- }),
1597
- z21.object({
1598
- operation: z21.literal("create_group"),
1599
- siteId: z21.string().uuid(),
1600
- routeKey: siteFieldRouteKeySchema,
1601
- section: labelSchema,
1602
- fieldIds: z21.array(fieldIdSchema).max(200).default([])
1603
- }),
1604
- z21.object({
1605
- operation: z21.literal("delete_section"),
1606
- siteId: z21.string().uuid(),
1607
- routeKey: siteFieldRouteKeySchema,
1608
- section: labelSchema
1609
- }),
1610
- z21.object({
1611
- operation: z21.literal("move_section"),
1612
- siteId: z21.string().uuid(),
1613
- routeKey: siteFieldRouteKeySchema,
1614
- section: labelSchema,
1615
- direction: z21.enum(["up", "down"])
1616
- }),
1617
- z21.object({
1618
- operation: z21.literal("create_subgroup"),
1619
- siteId: z21.string().uuid(),
1620
- routeKey: siteFieldRouteKeySchema,
1621
- section: labelSchema,
1622
- subsection: labelSchema,
1623
- fieldIds: z21.array(fieldIdSchema).min(1).max(200)
1624
- }),
1625
- z21.object({
1626
- operation: z21.literal("update_subgroup"),
1627
- siteId: z21.string().uuid(),
1628
- routeKey: siteFieldRouteKeySchema,
1629
- currentSection: labelSchema,
1630
- currentSubsection: labelSchema,
1631
- section: labelSchema,
1632
- subsection: labelSchema.nullable()
1633
- }),
1634
- z21.object({
1635
- operation: z21.literal("rename_route"),
1636
- siteId: z21.string().uuid(),
1637
- routeKey: siteFieldRouteKeySchema.refine((value) => value !== "$global", "Global content has no page label."),
1638
- label: labelSchema
1639
- })
1640
- ]).superRefine((input, context) => {
1641
- 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) {
1642
- context.addIssue({
1643
- code: "custom",
1644
- message: "At least one field organization value is required."
1645
- });
1646
- }
1647
- if (input.operation === "update_field" && input.section === null && typeof input.subsection === "string") {
1648
- context.addIssue({
1649
- code: "custom",
1650
- message: "A subgroup requires a group.",
1651
- path: ["subsection"]
1652
- });
1653
- }
1654
- });
1655
-
1656
- // ../shared/dist/site-slug.js
1657
- import { z as z22 } from "zod";
1658
- var SITE_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
1659
- var RESERVED_SITE_SLUGS = /* @__PURE__ */ new Set([
1660
- "account",
1661
- "admin",
1662
- "api",
1663
- "app",
1664
- "auth",
1665
- "book",
1666
- "claim",
1667
- "dev",
1668
- "login",
1669
- "new",
1670
- "portal",
1671
- "support",
1672
- "workspace"
1673
- ]);
1674
- var siteSlugSchema = z22.string().regex(SITE_SLUG_PATTERN, "Invalid site slug").refine((value) => !RESERVED_SITE_SLUGS.has(value), "Reserved site slug");
1675
-
1676
- // ../shared/dist/entitlements.js
1677
- import { z as z23 } from "zod";
1678
- var entitlementSnapshotSchema = z23.object({
1679
- planKey: z23.string().trim().min(1),
1680
- sitesLimit: z23.number().int().positive(),
1681
- siteMembersPerSiteLimit: z23.number().int().positive(),
1682
- customAdminDomainsEnabled: z23.boolean(),
1683
- agentMcpEnabled: z23.boolean(),
1684
- emailNotificationsEnabled: z23.boolean(),
1685
- analyticsEnabled: z23.boolean(),
1686
- analyticsSitesLimit: z23.number().int().min(0),
1687
- analyticsPortalPerformanceEnabled: z23.boolean(),
1688
- analyticsMonthlyReportsEnabled: z23.boolean(),
1689
- analyticsAiSummaryEnabled: z23.boolean(),
1690
- analyticsRawEventRetentionDays: z23.number().int().min(1).max(ANALYTICS_RETENTION_DEFAULTS.maxRawEventRetentionDaysWithoutAdr),
1691
- analyticsMonthlyEventLimit: z23.number().int().min(0),
1692
- bookingEnabled: z23.boolean().default(false),
1693
- bookingSitesLimit: z23.number().int().min(0).default(0),
1694
- bookingPortalManagementEnabled: z23.boolean().default(false),
1695
- bookingResourcesPerSiteLimit: z23.number().int().min(0).default(0),
1696
- bookingServicesPerSiteLimit: z23.number().int().min(0).default(0),
1697
- bookingMonthlyBookingsLimit: z23.number().int().min(0).default(0),
1698
- bookingRemindersEnabled: z23.boolean().default(false)
1699
- });
1700
- var EARLY_ACCESS_PLAN = {
1701
- planKey: "early_access_2026",
1702
- sitesLimit: 3,
1703
- siteMembersPerSiteLimit: 5,
1704
- customAdminDomainsEnabled: true,
1705
- agentMcpEnabled: true,
1706
- emailNotificationsEnabled: true,
1707
- analyticsEnabled: true,
1708
- analyticsSitesLimit: 3,
1709
- analyticsPortalPerformanceEnabled: false,
1710
- analyticsMonthlyReportsEnabled: false,
1711
- analyticsAiSummaryEnabled: false,
1712
- analyticsRawEventRetentionDays: ANALYTICS_RETENTION_DEFAULTS.enabledRawEventRetentionDays,
1713
- analyticsMonthlyEventLimit: 0,
1714
- bookingEnabled: true,
1715
- bookingSitesLimit: 3,
1716
- bookingPortalManagementEnabled: true,
1717
- bookingResourcesPerSiteLimit: 10,
1718
- bookingServicesPerSiteLimit: 50,
1719
- bookingMonthlyBookingsLimit: 0,
1720
- bookingRemindersEnabled: true
1721
- };
1722
-
1723
- // ../shared/dist/readiness.js
1724
- import { z as z24 } from "zod";
1725
- var READINESS_CHECK_KEYS = [
1726
- "agent_instructions_installed",
1727
- "runtime_package_connected",
1728
- "secure_server_access_configured",
1729
- "preview_bridge_working",
1730
- "admin_access_url_selected",
1731
- "default_publishing_mode_selected"
1732
- ];
1733
- var READINESS_STATUSES = [
1734
- "pending",
1735
- "passing",
1736
- "failing",
1737
- "warning"
1738
- ];
1739
- var readinessCheckKeySchema = z24.enum(READINESS_CHECK_KEYS);
1740
- var readinessStatusSchema = z24.enum(READINESS_STATUSES);
1741
-
1742
- // ../shared/dist/site.js
1743
- import { z as z25 } from "zod";
1744
- var SITE_STATUSES = ["active", "disabled", "archived"];
1745
- var siteStatusSchema = z25.enum(SITE_STATUSES);
1746
- var websiteUrlSchema = z25.string().trim().url().refine((value) => value.startsWith("https://") || value.startsWith("http://"), "Website URL must use http or https");
1747
-
1748
- // ../shared/dist/field-sync.js
1749
- import { z as z26 } from "zod";
1750
- var FIELD_SYNC_MAX_FIELDS = 2e3;
1751
- var filePathSchema = z26.string().trim().min(1).max(500).refine((value) => !value.startsWith("/") && !value.includes("\\") && !/^[a-z]:/iu.test(value) && !value.split("/").includes(".."), "Use the file path relative to the project root.");
1752
- var fieldBase = {
1753
- id: fieldIdSchema.max(160),
1754
- file: filePathSchema,
1755
- label: z26.string().trim().min(1).max(120).optional()
1756
- };
1757
- var fieldSyncFieldSchema = z26.discriminatedUnion("type", [
1758
- z26.object({ ...fieldBase, type: z26.literal("text"), fallback: z26.string().max(256) }).strict(),
1759
- z26.object({ ...fieldBase, type: z26.literal("longText"), fallback: z26.string().max(5e4) }).strict(),
1760
- z26.object({
1761
- ...fieldBase,
1762
- type: z26.literal("link"),
1763
- fallback: z26.object({ href: z26.string().min(1).max(2048), label: z26.string().max(256) }).strict()
1764
- }).strict(),
1765
- z26.object({
1766
- ...fieldBase,
1767
- type: z26.literal("image"),
1768
- fallback: z26.object({ src: z26.string().min(1).max(2048), alt: z26.string().max(300) }).strict()
1769
- }).strict()
1770
- ]);
1771
- var fieldSyncRequestSchema = z26.object({ fields: z26.array(fieldSyncFieldSchema).max(FIELD_SYNC_MAX_FIELDS) }).strict();
1772
-
1773
- // ../shared/dist/signing-keys.js
1774
- var pkcs8Ed25519Prefix = Uint8Array.from([
1775
- 48,
1776
- 46,
1777
- 2,
1778
- 1,
1779
- 0,
1780
- 48,
1781
- 5,
1782
- 6,
1783
- 3,
1784
- 43,
1785
- 101,
1786
- 112,
1787
- 4,
1788
- 34,
1789
- 4,
1790
- 32
1791
- ]);
1792
-
1793
- // ../shared/dist/preview-token.js
1794
- var PREVIEW_TOKEN_MAX_SECONDS = 60 * 60;
1795
- var header = encodeJson({ alg: "EdDSA", typ: "JWT" });
1796
- function encodeJson(value) {
1797
- return toBase64Url(new TextEncoder().encode(JSON.stringify(value)));
1798
- }
1799
- function toBase64Url(bytes) {
1800
- let binary = "";
1801
- for (const byte of bytes)
1802
- binary += String.fromCharCode(byte);
1803
- return btoa(binary).replace(/\+/gu, "-").replace(/\//gu, "_").replace(/=+$/u, "");
1804
- }
1805
-
1806
- // ../shared/dist/webhook-signature.js
1807
- var WEBHOOK_TOLERANCE_SECONDS = 5 * 60;
1808
- var encoder = new TextEncoder();
1809
-
1810
- // ../cli/dist/booking/static-scan.js
1811
- import ts from "typescript";
1812
-
1813
- // ../cli/dist/commands/booking/check.js
1814
- import { dirname as dirname2, join } from "path";
1815
- import { access, readFile as readFile2 } from "fs/promises";
1816
-
1817
- // ../cli/dist/commands/booking/init.js
1818
- import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
1819
- import { dirname as dirname3, join as join2 } from "path";
1820
- var bookingRulesTemplate = `# Siteplane Booking Rules
1821
-
1822
- - Do not change layout, copy, styling, animation or business logic while adding Booking.
1823
- - Integrate /booking on the customer domain through withSiteplane(nextConfig, { booking: { site: "<site-id>" } }); use the immutable Site ID supplied by Siteplane.
1824
- - Use a normal <a href="/booking"> link, not next/link, an iframe or an embed script.
1825
- - Preserve existing rewrites. Booking-only must not require Editor setup or siteplane.config.json.
1826
- - 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.
1827
- - Preserve customer CSP and middleware; explain required Siteplane/Stripe permissions and exclude /booking from customer middleware.
1828
- - Do not write setup tokens, project CLI tokens or private keys into source files.
1829
- - Run npx siteplane booking check, npx siteplane booking sync and npx siteplane booking test after setup.
1830
- - The first booking sync prints a link and code for the Siteplane approval; give both to the user and wait until they click Connect. The connection stays outside the project.
1831
- - booking test stays in test_mode; live booking only starts after explicit dashboard activation.
1832
- `;
1833
- var bookingCursorRulesTemplate = `---
1834
- description: Siteplane Booking rules
1835
- alwaysApply: true
1836
- ---
1837
-
1838
- ${bookingRulesTemplate}`;
1839
-
1840
- // ../cli/dist/commands/scan.js
1841
- import { readdir } from "fs/promises";
1842
- import { extname, join as join3 } from "path";
1843
- var defaultScanRoots = ["app", "src", "pages", "components", "lib"];
1844
- var excludedDirectories = /* @__PURE__ */ new Set([
1845
- ".git",
1846
- ".next",
1847
- ".turbo",
1848
- ".vercel",
1849
- "build",
1850
- "coverage",
1851
- "dist",
1852
- "node_modules"
1853
- ]);
1854
- var sourceExtensions = /* @__PURE__ */ new Set([".js", ".jsx", ".ts", ".tsx"]);
1855
- async function resolveScanFilePaths(filePaths, options = {}) {
1856
- if (filePaths.length > 0) {
1857
- return filePaths;
1858
- }
1859
- const projectDir = options.projectDir ?? process.cwd();
1860
- const discovered = await Promise.all(defaultScanRoots.map((root) => collectSourceFiles(join3(projectDir, root))));
1861
- return [...new Set(discovered.flat())].sort();
1862
- }
1863
- async function collectSourceFiles(directory) {
1864
- const entries = await readdir(directory, { withFileTypes: true }).catch(() => []);
1865
- const files = [];
1866
- for (const entry of entries) {
1867
- const entryPath = join3(directory, entry.name);
1868
- if (entry.isDirectory()) {
1869
- if (!excludedDirectories.has(entry.name)) {
1870
- files.push(...await collectSourceFiles(entryPath));
1871
- }
1872
- continue;
1873
- }
1874
- if (entry.isFile() && sourceExtensions.has(extname(entry.name))) {
1875
- files.push(entryPath);
1876
- }
1877
- }
1878
- return files;
1879
- }
1880
-
1881
- // ../cli/dist/auth/device-login.js
1882
- import { spawn } from "child_process";
1883
- import { hostname } from "os";
1884
-
1885
- // ../cli/dist/config/agent-connections.js
1886
- import { randomUUID } from "crypto";
1887
- import { chmod, mkdir as mkdir3, open, readFile as readFile3, rename, rm } from "fs/promises";
1888
- import { join as join4 } from "path";
1889
- import { z as z27 } from "zod";
1890
- var storeSchema = z27.object({
1891
- connections: z27.array(z27.object({
1892
- apiOrigin: z27.string().url(),
1893
- siteId: z27.string().min(1),
1894
- accessToken: z27.string().min(1),
1895
- scopes: z27.array(z27.string()),
1896
- expiresAt: z27.string()
1897
- }))
1898
- });
1899
-
1900
- // ../cli/dist/config/project-config.js
1901
- import { readFile as readFile4 } from "fs/promises";
1902
- import { join as join5 } from "path";
1903
- import { z as z28 } from "zod";
1904
- var packageVersionSchema = z28.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u);
1905
- var projectPackagesSchema = z28.object({
1906
- siteplane: packageVersionSchema,
1907
- runtimeNext: packageVersionSchema,
1908
- analytics: packageVersionSchema.optional()
1909
- }).strict();
1910
- var siteplaneProjectConfigSchema = z28.object({
1911
- version: z28.literal(2),
1912
- apiBaseUrl: z28.string().url(),
1913
- siteId: z28.string().uuid(),
1914
- publicSiteKeyId: z28.string().uuid(),
1915
- publicSiteKey: z28.string().trim().min(1),
1916
- runId: z28.string().uuid(),
1917
- appDirectory: z28.string().min(1),
1918
- taskContractVersion: z28.literal("siteplane.setup-task.v2"),
1919
- adminProtocolVersion: z28.literal(2),
1920
- packages: projectPackagesSchema,
1921
- fieldContractHash: z28.string().regex(/^sha256:[0-9a-f]{64}$/iu).optional()
1922
- }).strict();
1923
- async function readProjectConfig(projectDir) {
1924
- return siteplaneProjectConfigSchema.parse(JSON.parse(await readFile4(join5(projectDir, "siteplane.config.json"), "utf8")));
1925
- }
1926
-
1927
- // ../cli/dist/scan/next-source-scan.js
1928
- import ts2 from "typescript";
1929
- import { isAbsolute, relative } from "path";
1930
- var eventPreviewHandlerMissingMessage = "Event preview fields must consume preview values with useSiteplanePreviewText from @siteplane/runtime-next/client or a siteplane:preview-value-applied listener.";
1931
- var valueCallUnusedMessage = "Siteplane value calls must feed rendered content. Assign or await the returned value and render that value instead of ignoring the call.";
1932
- var legacyEditableComponentPrefix = "Editable";
1933
- var editableComponentNames = new Set(["Image", "Text", "LongText", "Link"].map((name) => `${legacyEditableComponentPrefix}${name}`));
1934
- var functionTypeByName = {
1935
- text: "text",
1936
- longText: "longText",
1937
- link: "link",
1938
- image: "image"
1939
- };
1940
- function scanNextSourceFilesToSiteFieldContract(files, options) {
1941
- const scan = scanSourceFacts(files);
1942
- const fields = [];
1943
- for (const field of uniqueFields(scan.fields)) {
1944
- const routeKey = field.technicalRouteKey ?? field.routeHint;
1945
- if (routeKey === "/*") {
1946
- scan.warnings.push({
1947
- code: "field_scan.global_route_key_invalid",
1948
- 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.`,
1949
- fieldId: field.fieldId,
1950
- filePath: field.sourceFilePath ?? "unknown"
1951
- });
1952
- }
1953
- if (!routeKey) {
1954
- scan.hardErrors.push({
1955
- code: "field_scan.route_key_missing",
1956
- message: `Field ${field.fieldId} has no canonical route key.`,
1957
- fieldId: field.fieldId,
1958
- filePath: field.sourceFilePath ?? "unknown"
1959
- });
1960
- continue;
1961
- }
1962
- const source = {
1963
- kind: "function",
1964
- filePath: toRepositoryRelativePosixPath(field.sourceFilePath ?? "unknown", options.projectRoot)
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)];
1965
58
  };
1966
- assignIfDefined(source, "exportName", field.sourceExportName);
1967
- assignIfDefined(source, "sourcePath", field.sourcePath);
1968
- assignIfDefined(source, "editTarget", field.editTarget);
1969
- const parsed = siteFieldContractFieldSchema.safeParse({
1970
- fieldId: field.fieldId,
1971
- fieldType: field.fieldType,
1972
- fallback: field.fallback,
1973
- routeKey,
1974
- source,
1975
- ...field.hasDomBinding ? {
1976
- dom: {
1977
- attribute: "data-siteplane-field-id",
1978
- previewStrategy: field.previewStrategy ?? "dom"
1979
- }
1980
- } : {}
1981
- });
1982
- if (!parsed.success) {
1983
- scan.hardErrors.push({
1984
- code: "field_scan.invalid_site_field_contract",
1985
- message: parsed.error.issues[0]?.message ?? "Invalid field contract.",
1986
- fieldId: field.fieldId,
1987
- filePath: field.sourceFilePath ?? "unknown"
1988
- });
1989
- continue;
1990
- }
1991
- fields.push(parsed.data);
1992
- }
1993
- return {
1994
- contract: siteFieldContractV1Schema.parse({
1995
- version: 1,
1996
- fieldContractVersion,
1997
- scannerContractVersion,
1998
- fields
1999
- }),
2000
- hardErrors: scan.hardErrors,
2001
- warnings: scan.warnings,
2002
- suggestions: scan.suggestions
2003
- };
2004
- }
2005
- function scanSourceFacts(files) {
2006
- const fields = [];
2007
- const hardErrors = [];
2008
- const warnings = [];
2009
- const suggestions = [];
2010
- const directAttrs = [];
2011
- const eventPreviewAttrs = [];
2012
- const eventPreviewComponentPropConsumers = [];
2013
- const eventPreviewComponentPropUsages = [];
2014
- const eventPreviewConsumerFieldIds = /* @__PURE__ */ new Set();
2015
- let hasGenericEventPreviewConsumer = false;
2016
- for (const file of files) {
2017
- const facts = scanSourceFile(file, fields, hardErrors);
2018
- directAttrs.push(...facts.directAttrs);
2019
- eventPreviewAttrs.push(...facts.eventPreviewAttrs);
2020
- eventPreviewComponentPropConsumers.push(...facts.eventPreviewComponentPropConsumers);
2021
- eventPreviewComponentPropUsages.push(...facts.eventPreviewComponentPropUsages);
2022
- facts.eventPreviewConsumerFieldIds.forEach((fieldId) => {
2023
- eventPreviewConsumerFieldIds.add(fieldId);
2024
- });
2025
- hasGenericEventPreviewConsumer ||= facts.hasGenericEventPreviewConsumer;
2026
- }
2027
- for (const usage of eventPreviewComponentPropUsages) {
2028
- if (eventPreviewComponentPropConsumers.some((consumer) => consumer.componentName === usage.componentName && consumer.propName === usage.propName)) {
2029
- eventPreviewConsumerFieldIds.add(usage.fieldId);
2030
- }
2031
- }
2032
- for (const attr of directAttrs) {
2033
- const field = fields.find((candidate) => candidate.fieldId === attr.fieldId);
2034
- if (field && attr.fieldType !== field.fieldType) {
2035
- warnings.push({
2036
- code: "field_scan.attrs_field_type_mismatch",
2037
- 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.`,
2038
- fieldId: attr.fieldId,
2039
- filePath: attr.filePath
2040
- });
2041
- }
2042
- const expectedRoute = field?.technicalRouteKey ?? field?.routeHint;
2043
- if (!attr.routeKey || expectedRoute && attr.routeKey !== expectedRoute) {
2044
- hardErrors.push({
2045
- code: attr.routeKey ? "field_scan.attrs_route_key_mismatch" : "field_scan.attrs_route_key_missing",
2046
- 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.`,
2047
- fieldId: attr.fieldId,
2048
- filePath: attr.filePath
2049
- });
2050
- }
2051
- }
2052
- markMissingEventPreviewHandlerErrors(eventPreviewAttrs, eventPreviewConsumerFieldIds, hasGenericEventPreviewConsumer, hardErrors);
2053
- validateScannedFields(fields, hardErrors, warnings, suggestions);
2054
- return {
2055
- fields,
2056
- hardErrors,
2057
- warnings,
2058
- suggestions
2059
- };
2060
- }
2061
- function uniqueFields(fields) {
2062
- const seen = /* @__PURE__ */ new Set();
2063
- const unique = [];
2064
- for (const field of fields) {
2065
- if (seen.has(field.fieldId)) {
2066
- continue;
2067
- }
2068
- seen.add(field.fieldId);
2069
- unique.push(field);
2070
- }
2071
- return unique;
2072
- }
2073
- function scanSourceFile(file, fields, hardErrors) {
2074
- const fieldsInFile = [];
2075
- const attrFieldIds = /* @__PURE__ */ new Set();
2076
- const attrRouteRefs = /* @__PURE__ */ new Set();
2077
- const directAttrs = [];
2078
- const eventPreviewAttrs = [];
2079
- const eventPreviewAttrVariables = /* @__PURE__ */ new Map();
2080
- const eventPreviewComponentPropConsumers = [];
2081
- const eventPreviewComponentPropUsages = [];
2082
- const eventPreviewConsumerFieldIds = /* @__PURE__ */ new Set();
2083
- let hasGenericEventPreviewConsumer = false;
2084
- const sourceFile = ts2.createSourceFile(file.filePath, file.sourceText, ts2.ScriptTarget.Latest, true, ts2.ScriptKind.TSX);
2085
- function visit(node, currentComponentName) {
2086
- const nextComponentName = getFunctionComponentName(node) ?? currentComponentName;
2087
- if (ts2.isJsxSelfClosingElement(node) || ts2.isJsxOpeningElement(node)) {
2088
- const componentName = getJsxTagName(node.tagName);
2089
- if (editableComponentNames.has(componentName)) {
2090
- hardErrors.push({
2091
- code: "field_scan.value_first_required",
2092
- message: "Siteplane MVP uses value-first APIs. Replace this field with siteplane.route(...) or a field value call.",
2093
- filePath: file.filePath
2094
- });
2095
- }
2096
- collectEventPreviewComponentPropUsages(node, eventPreviewAttrVariables, eventPreviewComponentPropUsages, file);
2097
- }
2098
- if (ts2.isCallExpression(node)) {
2099
- collectAttrsCall(node, attrFieldIds, attrRouteRefs, eventPreviewAttrs, eventPreviewAttrVariables, directAttrs, file);
2100
- collectUnusedValueCallError(node, hardErrors, file);
2101
- const extractedFields = extractFunctionFields(node, file);
2102
- for (const field of extractedFields) {
2103
- fieldsInFile.push(field);
2104
- fields.push(field);
2105
- }
2106
- const previewHookFieldId = getPreviewHookFieldId(node, eventPreviewAttrVariables);
2107
- if (previewHookFieldId) {
2108
- eventPreviewConsumerFieldIds.add(previewHookFieldId);
2109
- } else if (nextComponentName) {
2110
- const previewHookPropName = getPreviewHookPropName(node, eventPreviewAttrVariables);
2111
- if (previewHookPropName) {
2112
- eventPreviewComponentPropConsumers.push({
2113
- componentName: nextComponentName,
2114
- propName: previewHookPropName
2115
- });
2116
- }
2117
- }
2118
- }
2119
- if (isGenericEventPreviewConsumerReference(node)) {
2120
- hasGenericEventPreviewConsumer = true;
2121
- }
2122
- ts2.forEachChild(node, (child) => visit(child, nextComponentName));
2123
- }
2124
- visit(sourceFile);
2125
- markMissingAttrsWarnings(fieldsInFile, attrFieldIds, attrRouteRefs);
2126
- return {
2127
- directAttrs,
2128
- eventPreviewAttrs,
2129
- eventPreviewComponentPropConsumers,
2130
- eventPreviewComponentPropUsages,
2131
- eventPreviewConsumerFieldIds: [...eventPreviewConsumerFieldIds],
2132
- hasGenericEventPreviewConsumer
2133
- };
2134
- }
2135
- function collectUnusedValueCallError(node, hardErrors, file) {
2136
- const valueFunctionName = getSiteplaneValueFunctionName(node);
2137
- if (!valueFunctionName || !isIgnoredValueCall(node)) {
2138
- return;
2139
- }
2140
- const fieldId = valueFunctionName === "route" ? void 0 : stringValue(staticExpressionValue(node.arguments[0]));
2141
- hardErrors.push({
2142
- code: "field_scan.value_call_unused",
2143
- message: valueCallUnusedMessage,
2144
- ...fieldId ? { fieldId } : {},
2145
- filePath: file.filePath
2146
- });
2147
- }
2148
- function getSiteplaneValueFunctionName(node) {
2149
- if (!ts2.isPropertyAccessExpression(node.expression)) {
2150
- return void 0;
2151
- }
2152
- if (node.expression.expression.getText() !== "siteplane") {
2153
- return void 0;
2154
- }
2155
- const functionName = node.expression.name.text;
2156
- if (functionName === "route" || functionTypeByName[functionName]) {
2157
- return functionName;
2158
- }
2159
- return void 0;
2160
- }
2161
- function isIgnoredValueCall(node) {
2162
- if (ts2.isVoidExpression(node.parent)) {
2163
- return true;
2164
- }
2165
- if (ts2.isExpressionStatement(node.parent)) {
2166
- return true;
2167
- }
2168
- return ts2.isAwaitExpression(node.parent) && ts2.isExpressionStatement(node.parent.parent);
2169
- }
2170
- function extractFunctionFields(node, file) {
2171
- if (!ts2.isPropertyAccessExpression(node.expression)) {
2172
- return [];
2173
- }
2174
- if (node.expression.expression.getText() !== "siteplane") {
2175
- return [];
2176
- }
2177
- const functionName = node.expression.name.text;
2178
- if (functionName === "route") {
2179
- return extractRouteFields(node, file);
2180
59
  }
2181
- const fieldType = functionTypeByName[functionName];
2182
- if (!fieldType) {
2183
- return [];
2184
- }
2185
- const fieldId = staticExpressionValue(node.arguments[0]);
2186
- if (typeof fieldId !== "string") {
2187
- return [];
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
+ ];
2188
68
  }
2189
- const input = {
2190
- fieldId,
2191
- fieldType,
2192
- fallback: staticExpressionValue(node.arguments[1]),
2193
- source: "function",
2194
- sourceFilePath: file.filePath,
2195
- sourceExportName: functionName,
2196
- editTarget: fieldId
2197
- };
2198
- const routeHint = inferRouteHint(file.filePath);
2199
- const options = staticExpressionValue(node.arguments[2]);
2200
- const fieldOptions = options && typeof options === "object" && !Array.isArray(options) ? options : {};
2201
- assignIfDefined(input, "routeHint", routeHint);
2202
- assignIfDefined(input, "technicalRouteKey", stringValue(fieldOptions.routeKey) ?? routeHint);
2203
- assignIfDefined(input, "previewStrategy", previewStrategyValue(fieldOptions.previewStrategy));
2204
- assignIfDefined(input, "label", stringValue(fieldOptions.label));
2205
- assignIfDefined(input, "description", stringValue(fieldOptions.description));
2206
- assignIfDefined(input, "required", booleanValue(fieldOptions.required));
2207
- assignIfDefined(input, "maxLength", numberValue(fieldOptions.maxLength));
2208
- return [createField(input)];
69
+ return next;
2209
70
  }
2210
- function extractRouteFields(node, file) {
2211
- const routeId = staticExpressionValue(node.arguments[0]);
2212
- const fallbacks = staticExpressionValue(node.arguments[1]);
2213
- const options = staticExpressionValue(node.arguments[2]);
2214
- if (typeof routeId !== "string" || !fallbacks || typeof fallbacks !== "object" || Array.isArray(fallbacks)) {
2215
- return [];
2216
- }
2217
- const routeVariableName = getAssignedVariableName(node);
2218
- const fieldsConfig = options && typeof options === "object" && !Array.isArray(options) ? options.fields : void 0;
2219
- const routeHint = inferRouteHint(file.filePath);
2220
- const fields = [];
2221
- for (const [key, fallback] of Object.entries(fallbacks)) {
2222
- const config = fieldsConfig && typeof fieldsConfig === "object" && !Array.isArray(fieldsConfig) ? fieldsConfig[key] : void 0;
2223
- if (!config || typeof config !== "object" || Array.isArray(config)) {
2224
- continue;
2225
- }
2226
- const fieldConfig = config;
2227
- const fieldId = stringValue(fieldConfig.id);
2228
- const fieldType = fieldTypeValue(fieldConfig.type);
2229
- if (!fieldId || !fieldType) {
2230
- continue;
2231
- }
2232
- const input = {
2233
- fieldId,
2234
- fieldType,
2235
- fallback,
2236
- source: "function",
2237
- sourceFilePath: file.filePath,
2238
- sourceExportName: "route",
2239
- routeId,
2240
- sourcePath: `$.${key}`,
2241
- editTarget: fieldId,
2242
- routeFieldKey: key
71
+ function moduleAlias(config, projectDir) {
72
+ const alias = { "siteplane/config": `./${SITEPLANE_CONFIG_FILE}` };
73
+ if (usesExperimentalTurbo(projectDir)) {
74
+ const turbo = config.experimental?.turbo;
75
+ return {
76
+ experimental: { ...config.experimental, turbo: { ...turbo, resolveAlias: { ...turbo?.resolveAlias, ...alias } } }
2243
77
  };
2244
- assignIfDefined(input, "routeVariableName", routeVariableName);
2245
- assignIfDefined(input, "routeHint", routeHint);
2246
- assignIfDefined(input, "technicalRouteKey", stringValue(fieldConfig.routeKey) ?? routeHint);
2247
- assignIfDefined(input, "previewStrategy", previewStrategyValue(fieldConfig.previewStrategy));
2248
- assignIfDefined(input, "label", stringValue(fieldConfig.label));
2249
- assignIfDefined(input, "description", stringValue(fieldConfig.description));
2250
- assignIfDefined(input, "required", booleanValue(fieldConfig.required));
2251
- assignIfDefined(input, "maxLength", numberValue(fieldConfig.maxLength));
2252
- fields.push(createField(input));
2253
78
  }
2254
- return fields;
79
+ return { turbopack: { ...config.turbopack, resolveAlias: { ...config.turbopack?.resolveAlias, ...alias } } };
2255
80
  }
2256
- function createField(input) {
2257
- const field = {
2258
- fieldId: input.fieldId,
2259
- fieldType: input.fieldType,
2260
- fallback: input.fallback,
2261
- source: input.source,
2262
- sourceFilePath: input.sourceFilePath,
2263
- warnings: [],
2264
- suggestions: []
2265
- };
2266
- assignIfDefined(field, "label", input.label);
2267
- assignIfDefined(field, "description", input.description);
2268
- assignIfDefined(field, "required", input.required);
2269
- assignIfDefined(field, "maxLength", input.maxLength);
2270
- assignIfDefined(field, "sourceExportName", input.sourceExportName);
2271
- assignIfDefined(field, "componentHint", input.componentHint);
2272
- assignIfDefined(field, "routeHint", input.routeHint);
2273
- assignIfDefined(field, "routeId", input.routeId);
2274
- assignIfDefined(field, "sourcePath", input.sourcePath);
2275
- assignIfDefined(field, "editTarget", input.editTarget);
2276
- assignMetadata(field, {
2277
- canonicalFallbackHash: createFallbackHash(input.fieldType, input.fallback)
2278
- });
2279
- assignIfDefined(field, "routeVariableName", input.routeVariableName);
2280
- assignIfDefined(field, "routeFieldKey", input.routeFieldKey);
2281
- assignIfDefined(field, "technicalRouteKey", input.technicalRouteKey);
2282
- assignIfDefined(field, "previewStrategy", input.previewStrategy);
2283
- if (field.fieldType === "text" && field.maxLength === void 0) {
2284
- field.warnings.push("field_scan.text_max_length_missing");
2285
- }
2286
- if (isPositionalFieldReference(field.fieldId) || isPositionalFieldReference(field.sourcePath)) {
2287
- field.warnings.push("field_scan.positional_repeating_id");
2288
- }
2289
- return field;
2290
- }
2291
- function assignMetadata(field, metadata) {
2292
- const entries = Object.entries(metadata).filter(([, value]) => value !== void 0);
2293
- if (entries.length === 0) {
2294
- return;
2295
- }
2296
- field.metadata = {
2297
- ...field.metadata ?? {},
2298
- ...Object.fromEntries(entries)
2299
- };
2300
- }
2301
- function createFallbackHash(fieldType, fallback) {
81
+ function usesExperimentalTurbo(projectDir) {
2302
82
  try {
2303
- 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;
2304
86
  } catch {
2305
- return void 0;
2306
- }
2307
- }
2308
- function collectAttrsCall(node, attrFieldIds, attrRouteRefs, eventPreviewAttrs, eventPreviewAttrVariables, directAttrs, file) {
2309
- if (!ts2.isPropertyAccessExpression(node.expression)) {
2310
- return;
2311
- }
2312
- if (node.expression.expression.getText() !== "siteplane" || node.expression.name.text !== "attrs") {
2313
- return;
2314
- }
2315
- const firstArg = node.arguments[0];
2316
- const secondArg = staticExpressionValue(node.arguments[1]);
2317
- const directFieldId = staticExpressionValue(firstArg);
2318
- if (typeof directFieldId === "string") {
2319
- attrFieldIds.add(directFieldId);
2320
- directAttrs.push({
2321
- fieldId: directFieldId,
2322
- routeKey: secondArg && typeof secondArg === "object" && "routeKey" in secondArg ? stringValue(secondArg.routeKey) : void 0,
2323
- fieldType: secondArg && typeof secondArg === "object" && "fieldType" in secondArg ? stringValue(secondArg.fieldType) ?? "text" : "text",
2324
- filePath: file.filePath
2325
- });
2326
- if (hasEventPreviewStrategy(secondArg)) {
2327
- eventPreviewAttrs.push({
2328
- fieldId: directFieldId,
2329
- filePath: file.filePath
2330
- });
2331
- const variableName = getAssignedVariableName(node);
2332
- if (variableName) {
2333
- eventPreviewAttrVariables.set(variableName, directFieldId);
2334
- }
2335
- }
2336
- return;
2337
- }
2338
- if (firstArg && ts2.isIdentifier(firstArg) && typeof secondArg === "string") {
2339
- attrRouteRefs.add(`${firstArg.text}:${secondArg}`);
2340
- }
2341
- }
2342
- function markMissingEventPreviewHandlerErrors(eventPreviewAttrs, eventPreviewConsumerFieldIds, hasGenericEventPreviewConsumer, hardErrors) {
2343
- if (hasGenericEventPreviewConsumer) {
2344
- return;
2345
- }
2346
- for (const attr of uniqueEventPreviewAttrs(eventPreviewAttrs)) {
2347
- if (eventPreviewConsumerFieldIds.has(attr.fieldId)) {
2348
- continue;
2349
- }
2350
- hardErrors.push({
2351
- code: "field_scan.event_preview_handler_missing",
2352
- message: eventPreviewHandlerMissingMessage,
2353
- fieldId: attr.fieldId,
2354
- filePath: attr.filePath
2355
- });
2356
- }
2357
- }
2358
- function uniqueEventPreviewAttrs(eventPreviewAttrs) {
2359
- const seen = /* @__PURE__ */ new Set();
2360
- const unique = [];
2361
- for (const attr of eventPreviewAttrs) {
2362
- const key = `${attr.filePath}:${attr.fieldId}`;
2363
- if (seen.has(key)) {
2364
- continue;
2365
- }
2366
- seen.add(key);
2367
- unique.push(attr);
2368
- }
2369
- return unique;
2370
- }
2371
- function hasEventPreviewStrategy(options) {
2372
- return !!options && typeof options === "object" && !Array.isArray(options) && options.previewStrategy === "event";
2373
- }
2374
- function isGenericEventPreviewConsumerReference(node) {
2375
- return (ts2.isStringLiteral(node) || ts2.isNoSubstitutionTemplateLiteral(node)) && node.text === "siteplane:preview-value-applied";
2376
- }
2377
- function getPreviewHookFieldId(node, eventPreviewAttrVariables) {
2378
- const firstArg = getUseSiteplanePreviewTextFirstArg(node);
2379
- if (!firstArg) {
2380
- return void 0;
2381
- }
2382
- const directFieldId = staticExpressionValue(firstArg);
2383
- if (typeof directFieldId === "string") {
2384
- return directFieldId;
2385
- }
2386
- if (ts2.isIdentifier(firstArg)) {
2387
- return eventPreviewAttrVariables.get(firstArg.text);
2388
- }
2389
- return void 0;
2390
- }
2391
- function getPreviewHookPropName(node, eventPreviewAttrVariables) {
2392
- const firstArg = getUseSiteplanePreviewTextFirstArg(node);
2393
- if (!firstArg) {
2394
- return void 0;
2395
- }
2396
- if (ts2.isIdentifier(firstArg) && !eventPreviewAttrVariables.has(firstArg.text)) {
2397
- return firstArg.text;
2398
- }
2399
- if (ts2.isPropertyAccessExpression(firstArg) && ts2.isIdentifier(firstArg.expression)) {
2400
- return firstArg.name.text;
2401
- }
2402
- return void 0;
2403
- }
2404
- function getUseSiteplanePreviewTextFirstArg(node) {
2405
- if (ts2.isIdentifier(node.expression) && node.expression.text === "useSiteplanePreviewText") {
2406
- return node.arguments[0];
2407
- }
2408
- return void 0;
2409
- }
2410
- function markMissingAttrsWarnings(fields, attrFieldIds, attrRouteRefs) {
2411
- for (const field of fields) {
2412
- const hasDirectAttrs = attrFieldIds.has(field.fieldId);
2413
- const hasRouteAttrs = field.routeVariableName && field.routeFieldKey ? attrRouteRefs.has(`${field.routeVariableName}:${field.routeFieldKey}`) : false;
2414
- field.hasDomBinding = Boolean(hasDirectAttrs || hasRouteAttrs);
2415
- if (!field.hasDomBinding && (field.fieldType === "text" || field.fieldType === "longText")) {
2416
- field.warnings.push("field_scan.attrs_missing");
2417
- }
2418
- }
2419
- }
2420
- function validateScannedFields(fields, hardErrors, warnings, suggestions) {
2421
- const seen = /* @__PURE__ */ new Map();
2422
- for (const field of fields) {
2423
- if (!isFieldId(field.fieldId)) {
2424
- hardErrors.push({
2425
- code: "field_scan.invalid_field_id",
2426
- message: `Invalid field id: ${field.fieldId}`,
2427
- fieldId: field.fieldId,
2428
- filePath: field.sourceFilePath ?? "unknown"
2429
- });
2430
- }
2431
- const existing = seen.get(field.fieldId);
2432
- if (existing) {
2433
- hardErrors.push({
2434
- code: "field_scan.duplicate_id",
2435
- message: `Duplicate field id: ${field.fieldId}`,
2436
- fieldId: field.fieldId,
2437
- filePath: field.sourceFilePath ?? "unknown"
2438
- });
2439
- if (existing.fieldType !== field.fieldType) {
2440
- hardErrors.push({
2441
- code: "field_scan.type_conflict",
2442
- message: `Field id ${field.fieldId} is used with multiple types.`,
2443
- fieldId: field.fieldId,
2444
- filePath: field.sourceFilePath ?? "unknown"
2445
- });
2446
- }
2447
- }
2448
- seen.set(field.fieldId, field);
2449
- for (const warning of field.warnings) {
2450
- warnings.push({
2451
- code: warning,
2452
- message: getScanIssueMessage(warning),
2453
- fieldId: field.fieldId,
2454
- filePath: field.sourceFilePath ?? "unknown"
2455
- });
2456
- }
2457
- for (const suggestion of field.suggestions) {
2458
- suggestions.push({
2459
- code: suggestion,
2460
- message: getScanIssueMessage(suggestion),
2461
- fieldId: field.fieldId,
2462
- filePath: field.sourceFilePath ?? "unknown"
2463
- });
2464
- }
2465
- }
2466
- }
2467
- function getScanIssueMessage(code) {
2468
- if (code === "field_scan.attrs_missing") {
2469
- 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.";
2470
- }
2471
- if (code === "field_scan.positional_repeating_id") {
2472
- 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.";
2473
- }
2474
- if (code === "field_scan.animation_wrapper") {
2475
- return 'Put siteplane.attrs(...) on the stable wrapper and make the animation respect data-siteplane-editor="true" or useSiteplaneEditorMode().';
2476
- }
2477
- if (code === "field_scan.event_preview_handler_missing") {
2478
- return eventPreviewHandlerMissingMessage;
2479
- }
2480
- return code;
2481
- }
2482
- function staticExpressionValue(expression) {
2483
- if (!expression) {
2484
- return void 0;
2485
- }
2486
- if (ts2.isStringLiteral(expression) || ts2.isNoSubstitutionTemplateLiteral(expression)) {
2487
- return expression.text;
2488
- }
2489
- if (ts2.isNumericLiteral(expression)) {
2490
- return Number(expression.text);
2491
- }
2492
- if (expression.kind === ts2.SyntaxKind.TrueKeyword) {
2493
- return true;
2494
- }
2495
- if (expression.kind === ts2.SyntaxKind.FalseKeyword) {
2496
87
  return false;
2497
88
  }
2498
- if (ts2.isObjectLiteralExpression(expression)) {
2499
- return readObjectLiteral(expression);
2500
- }
2501
- return void 0;
2502
- }
2503
- function readObjectLiteral(expression) {
2504
- const value = {};
2505
- for (const property of expression.properties) {
2506
- if (!ts2.isPropertyAssignment(property)) {
2507
- continue;
2508
- }
2509
- const name = getPropertyName(property.name);
2510
- if (!name) {
2511
- continue;
2512
- }
2513
- value[name] = staticExpressionValue(property.initializer);
2514
- }
2515
- return value;
2516
- }
2517
- function getPropertyName(name) {
2518
- if (ts2.isIdentifier(name) || ts2.isStringLiteral(name)) {
2519
- return name.text;
2520
- }
2521
- return null;
2522
- }
2523
- function collectEventPreviewComponentPropUsages(node, eventPreviewAttrVariables, eventPreviewComponentPropUsages, file) {
2524
- const componentName = getJsxTagName(node.tagName);
2525
- if (!isComponentName(componentName)) {
2526
- return;
2527
- }
2528
- for (const property of node.attributes.properties) {
2529
- if (!ts2.isJsxAttribute(property)) {
2530
- continue;
2531
- }
2532
- const propName = getJsxAttributeName(property.name);
2533
- const fieldId = getJsxFieldIdValue(property.initializer, eventPreviewAttrVariables);
2534
- if (!propName || !fieldId || !isFieldId(fieldId)) {
2535
- continue;
2536
- }
2537
- eventPreviewComponentPropUsages.push({
2538
- componentName,
2539
- fieldId,
2540
- filePath: file.filePath,
2541
- propName
2542
- });
2543
- }
2544
- }
2545
- function getJsxAttributeName(name) {
2546
- if (ts2.isIdentifier(name)) {
2547
- return name.text;
2548
- }
2549
- return void 0;
2550
- }
2551
- function getJsxFieldIdValue(initializer, eventPreviewAttrVariables) {
2552
- if (!initializer) {
2553
- return void 0;
2554
- }
2555
- if (ts2.isStringLiteral(initializer)) {
2556
- return initializer.text;
2557
- }
2558
- if (ts2.isJsxExpression(initializer) && initializer.expression && ts2.isIdentifier(initializer.expression)) {
2559
- return eventPreviewAttrVariables.get(initializer.expression.text);
2560
- }
2561
- return void 0;
2562
- }
2563
- function getFunctionComponentName(node) {
2564
- if (ts2.isFunctionDeclaration(node) && node.name && isComponentName(node.name.text)) {
2565
- return node.name.text;
2566
- }
2567
- if (ts2.isVariableDeclaration(node) && ts2.isIdentifier(node.name) && isComponentName(node.name.text) && node.initializer && (ts2.isArrowFunction(node.initializer) || ts2.isFunctionExpression(node.initializer))) {
2568
- return node.name.text;
2569
- }
2570
- return void 0;
2571
- }
2572
- function isComponentName(name) {
2573
- return /^[A-Z]/.test(name);
2574
- }
2575
- function getAssignedVariableName(node) {
2576
- let current = node;
2577
- while (ts2.isAwaitExpression(current.parent)) {
2578
- current = current.parent;
2579
- }
2580
- if (ts2.isVariableDeclaration(current.parent) && ts2.isIdentifier(current.parent.name)) {
2581
- return current.parent.name.text;
2582
- }
2583
- return void 0;
2584
- }
2585
- function getJsxTagName(tagName) {
2586
- return tagName.getText();
2587
- }
2588
- function stringValue(value) {
2589
- return typeof value === "string" ? value : void 0;
2590
- }
2591
- function fieldTypeValue(value) {
2592
- if (value === "text" || value === "longText" || value === "image" || value === "link") {
2593
- return value;
2594
- }
2595
- return void 0;
2596
- }
2597
- function previewStrategyValue(value) {
2598
- return value === "dom" || value === "event" ? value : void 0;
2599
- }
2600
- function numberValue(value) {
2601
- return typeof value === "number" ? value : void 0;
2602
- }
2603
- function booleanValue(value) {
2604
- return typeof value === "boolean" ? value : void 0;
2605
- }
2606
- function assignIfDefined(object, key, value) {
2607
- if (value !== void 0) {
2608
- object[key] = value;
2609
- }
2610
- }
2611
- function inferRouteHint(filePath) {
2612
- const normalized = filePath.replaceAll("\\", "/");
2613
- const appIndex = normalized.indexOf("app/");
2614
- if (appIndex === -1 || !normalized.endsWith("page.tsx")) {
2615
- return void 0;
2616
- }
2617
- const route = normalized.slice(appIndex + 4, -"page.tsx".length).split("/").filter((segment) => segment && !segment.startsWith("(")).join("/");
2618
- return route ? `/${route}` : "/";
2619
- }
2620
- function toRepositoryRelativePosixPath(filePath, projectRoot) {
2621
- const path = isAbsolute(filePath) ? relative(projectRoot, filePath) : filePath;
2622
- return path.replaceAll("\\", "/");
2623
- }
2624
- function isPositionalFieldReference(value) {
2625
- return value ? /\[\d+\]|\.\d+(?:\.|$)/.test(value) : false;
2626
89
  }
2627
-
2628
- // src/next.ts
2629
- var SiteplaneNextContractError = class extends Error {
2630
- constructor(code, message) {
2631
- super(`[${code}] ${message}`);
2632
- this.code = code;
2633
- this.name = "SiteplaneNextContractError";
2634
- }
2635
- code;
2636
- };
2637
- async function scanNextProjectFieldContract(projectDir) {
2638
- const resolvedProjectDir = resolve(projectDir);
2639
- const filePaths = await resolveScanFilePaths([], {
2640
- projectDir: resolvedProjectDir
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
2641
98
  });
2642
- const files = await Promise.all(
2643
- filePaths.map(async (filePath) => ({
2644
- filePath,
2645
- sourceText: await readFile5(filePath, "utf8")
2646
- }))
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
+ ])
2647
104
  );
2648
- const result = scanNextSourceFilesToSiteFieldContract(files, {
2649
- projectRoot: resolvedProjectDir
2650
- });
2651
- if (result.hardErrors.length > 0) {
2652
- throw new SiteplaneNextContractError(
2653
- "field_contract_scan_failed",
2654
- result.hardErrors.map((issue) => issue.message).join("\n")
2655
- );
2656
- }
2657
- return {
2658
- ...result,
2659
- fieldContractHash: createSiteFieldContractHash(result.contract)
2660
- };
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("; ");
2661
125
  }
2662
- async function withSiteplane(nextConfig, options = {}) {
2663
- const projectDir = options.projectDir ?? process.cwd();
2664
- const booking = options.booking;
2665
- let integrated = nextConfig;
2666
- if (booking) {
2667
- if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
2668
- booking.site
2669
- ))
2670
- throw new Error(
2671
- "Siteplane booking.site must be the Site ID from Booking settings."
2672
- );
2673
- for (const folder of ["app/booking", "src/app/booking"]) {
2674
- if (await access2(join6(projectDir, folder)).then(
2675
- () => true,
2676
- () => false
2677
- ))
2678
- console.warn(
2679
- "[Siteplane] An app/booking route exists. The Booking beforeFiles rewrite takes precedence; move that route or remove the Booking integration."
2680
- );
2681
- }
2682
- integrated = {
2683
- ...nextConfig,
2684
- rewrites: async () => {
2685
- const existing = await nextConfig.rewrites?.() ?? [];
2686
- const rules = Array.isArray(existing) ? { beforeFiles: [], afterFiles: existing, fallback: [] } : existing;
2687
- return {
2688
- beforeFiles: [
2689
- {
2690
- source: "/booking",
2691
- destination: `https://siteplane.io/booking?site=${booking.site}`
2692
- },
2693
- {
2694
- source: "/booking/:path*",
2695
- destination: `https://siteplane.io/booking/:path*?site=${booking.site}`
2696
- },
2697
- ...rules.beforeFiles ?? []
2698
- ],
2699
- afterFiles: rules.afterFiles ?? [],
2700
- fallback: rules.fallback ?? []
2701
- };
2702
- }
126
+ function prependRewrites(rewrites, rules) {
127
+ return async () => {
128
+ const existing = await rewrites?.() ?? [];
129
+ const sections = Array.isArray(existing) ? { afterFiles: existing } : existing;
130
+ return {
131
+ beforeFiles: [...rules, ...sections.beforeFiles ?? []],
132
+ afterFiles: sections.afterFiles ?? [],
133
+ fallback: sections.fallback ?? []
2703
134
  };
2704
- return { ...integrated, env: { ...nextConfig.env } };
2705
- }
2706
- const config = await readProjectConfig(projectDir);
2707
- const scan = await scanNextProjectFieldContract(projectDir);
2708
- const requestedRevision = options.buildRevision?.trim() || process.env.SITEPLANE_BUILD_REVISION?.trim();
2709
- const providerRevision = process.env.VERCEL === "1" ? process.env.VERCEL_GIT_COMMIT_SHA?.trim() : void 0;
2710
- const buildRevision = providerRevision || await readGitRevision(projectDir) || requestedRevision || "local";
2711
- const hasImages = scan.contract.fields.some(
2712
- (field) => field.fieldType === "image"
2713
- );
2714
- const imageOrigin = hasImages ? readImageWebsiteOrigin() : null;
2715
- return {
2716
- ...integrated,
2717
- ...hasImages ? { images: prepareImagePatterns(nextConfig.images, imageOrigin) } : {},
2718
- headers: async () => frameHeaders(
2719
- await nextConfig.headers?.() ?? [],
2720
- new URL(config.apiBaseUrl).origin,
2721
- imageOrigin
2722
- ),
2723
- env: {
2724
- ...nextConfig.env ?? {},
2725
- SITEPLANE_BUILD_FIELD_CONTRACT_HASH: scan.fieldContractHash,
2726
- SITEPLANE_BUILD_PUBLIC_SITE_KEY_ID: config.publicSiteKeyId,
2727
- SITEPLANE_BUILD_REVISION: buildRevision
2728
- }
2729
135
  };
2730
136
  }
2731
- var execFileAsync = promisify(execFile);
2732
- async function readGitRevision(projectDir) {
2733
- try {
2734
- const result = await execFileAsync("git", ["rev-parse", "HEAD"], {
2735
- cwd: projectDir
2736
- });
2737
- return result.stdout.trim();
2738
- } catch {
2739
- return "";
2740
- }
2741
- }
2742
- function frameHeaders(rules, adminOrigin, imageOrigin) {
2743
- const policy = `frame-ancestors 'self' ${adminOrigin}`;
2744
- const hasFrameOptions = rules.some(
2745
- (rule) => rule.headers.some(
2746
- (header2) => header2.key.toLowerCase() === "x-frame-options"
2747
- )
2748
- );
2749
- const prepared = rules.map((rule) => ({
2750
- ...rule,
2751
- headers: rule.headers.map((header2) => {
2752
- if (header2.key.toLowerCase() !== "content-security-policy") return header2;
2753
- const ancestors = /(?:^|;)\s*frame-ancestors\s+([^;]+)/iu.test(
2754
- header2.value
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(
141
+ () => true,
142
+ () => false
143
+ )) {
144
+ console.warn(
145
+ "[Siteplane] An app/booking route exists. The Booking beforeFiles rewrite takes precedence; move that route or remove the Booking integration."
2755
146
  );
2756
- const value = ancestors || hasFrameOptions ? header2.value : `${header2.value.replace(/;\s*$/u, "")}; ${policy}`;
2757
- return {
2758
- ...header2,
2759
- value: imageOrigin ? prepareImageCsp(value, imageOrigin) : value
2760
- };
2761
- })
2762
- }));
2763
- return hasFrameOptions ? prepared : [
2764
- {
2765
- source: "/:path*",
2766
- headers: [{ key: "Content-Security-Policy", value: policy }]
2767
- },
2768
- ...prepared
2769
- ];
2770
- }
2771
- var managedImageOrigin = "https://siteplane.io";
2772
- function readImageWebsiteOrigin() {
2773
- if (!process.env.SITEPLANE_SITE_ORIGIN) return null;
2774
- try {
2775
- const value = process.env.SITEPLANE_SITE_ORIGIN;
2776
- const url = new URL(value ?? "");
2777
- 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(":"))
2778
- throw new Error("Invalid image origin");
2779
- return url.origin;
2780
- } catch {
2781
- throw new SiteplaneNextContractError(
2782
- "image_origin_missing",
2783
- "If configured, SITEPLANE_SITE_ORIGIN must be the exact public HTTPS website origin; local fallback-image builds can omit it. Never substitute a wildcard or private host."
2784
- );
2785
- }
2786
- }
2787
- function prepareImagePatterns(images, websiteOrigin) {
2788
- const remotePatterns = [...images?.remotePatterns ?? []];
2789
- const destinations = [
2790
- [managedImageOrigin, "/api/assets/*/content"]
2791
- ];
2792
- if (websiteOrigin) destinations.push([websiteOrigin, "/siteplane-assets/**"]);
2793
- for (const [origin, pathname] of destinations) {
2794
- const pattern = {
2795
- protocol: "https",
2796
- hostname: new URL(origin).hostname,
2797
- port: "",
2798
- pathname,
2799
- search: ""
2800
- };
2801
- if (!remotePatterns.some(
2802
- (existing) => existing.protocol === pattern.protocol && existing.hostname === pattern.hostname && existing.port === "" && existing.pathname === pathname && existing.search === ""
2803
- ))
2804
- remotePatterns.push(pattern);
147
+ }
2805
148
  }
2806
- const localPatterns = images?.localPatterns ? [...images.localPatterns] : void 0;
2807
- if (localPatterns && !localPatterns.some(
2808
- (pattern) => pattern.pathname === "/siteplane-assets/**" && pattern.search === ""
2809
- ))
2810
- localPatterns.push({ pathname: "/siteplane-assets/**", search: "" });
2811
149
  return {
2812
- ...images,
2813
- remotePatterns,
2814
- ...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
+ ])
2815
155
  };
2816
156
  }
2817
- function prepareImageCsp(policy, websiteOrigin) {
2818
- if (policy.includes(","))
2819
- throw new SiteplaneNextContractError(
2820
- "image_policy_conflict",
2821
- "Split combined CSP policies into separate header rules before image preparation; preserve every existing directive."
2822
- );
2823
- const directives = policy.split(";").map((part) => part.trim()).filter(Boolean);
2824
- const find = (name) => directives.map((part, index) => ({ index, tokens: part.split(/\s+/u) })).filter((part) => part.tokens[0]?.toLowerCase() === name);
2825
- const image = find("img-src"), defaults = find("default-src");
2826
- if (image.length > 1 || defaults.length > 1)
2827
- throw new SiteplaneNextContractError(
2828
- "image_policy_conflict",
2829
- "Remove ambiguous duplicate img-src/default-src directives before image preparation without changing unrelated policies."
2830
- );
2831
- const existing = image[0] ?? defaults[0];
2832
- if (!existing) return policy;
2833
- const sources = existing.tokens.slice(1).filter((source) => source !== "'none'");
2834
- for (const source of [
2835
- `${managedImageOrigin}/api/assets/`,
2836
- `${websiteOrigin}/siteplane-assets/`,
2837
- `${websiteOrigin}/_next/image`
2838
- ]) {
2839
- if (!sources.includes(source)) sources.push(source);
2840
- }
2841
- const value = `img-src ${sources.join(" ")}`;
2842
- if (image[0]) directives[image[0].index] = value;
2843
- else directives.push(value);
2844
- return directives.join("; ");
2845
- }
2846
157
  export {
2847
- SiteplaneNextContractError,
2848
- scanNextProjectFieldContract,
2849
158
  withSiteplane
2850
159
  };