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