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/index.js
ADDED
|
@@ -0,0 +1,2260 @@
|
|
|
1
|
+
// ../cli/dist/analytics/manifest.js
|
|
2
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
3
|
+
import { dirname } from "path";
|
|
4
|
+
|
|
5
|
+
// ../shared/dist/actors.js
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
var ACTOR_TYPES = ["developer", "client", "agent", "system"];
|
|
8
|
+
var actorTypeSchema = z.enum(ACTOR_TYPES);
|
|
9
|
+
|
|
10
|
+
// ../shared/dist/analytics-defaults.js
|
|
11
|
+
var ANALYTICS_BATCH_LIMITS = {
|
|
12
|
+
maxEventsPerRequest: 50,
|
|
13
|
+
maxJsonBodyBytes: 64 * 1024,
|
|
14
|
+
maxPropertiesJsonBytes: 8 * 1024
|
|
15
|
+
};
|
|
16
|
+
var ANALYTICS_RETENTION_DEFAULTS = {
|
|
17
|
+
enabledRawEventRetentionDays: 30,
|
|
18
|
+
higherTierRawEventRetentionDays: 90,
|
|
19
|
+
maxRawEventRetentionDaysWithoutAdr: 180,
|
|
20
|
+
diagnosticsRetentionDays: 30,
|
|
21
|
+
rateLimitBucketRetentionDays: 2,
|
|
22
|
+
lateEventWindowHours: 72
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// ../shared/dist/analytics-booking.js
|
|
26
|
+
import { z as z2 } from "zod";
|
|
27
|
+
var bookingProviders = ["treatwell", "beautinda", "other"];
|
|
28
|
+
var bookingAttributionModes = [
|
|
29
|
+
"outbound_click_and_utm",
|
|
30
|
+
"provider_redirect",
|
|
31
|
+
"provider_reported",
|
|
32
|
+
"manual_import",
|
|
33
|
+
"verified_webhook"
|
|
34
|
+
];
|
|
35
|
+
var bookingSignalSources = [
|
|
36
|
+
"outbound_click",
|
|
37
|
+
"provider_redirect",
|
|
38
|
+
"provider_reported",
|
|
39
|
+
"manual_import",
|
|
40
|
+
"verified_webhook"
|
|
41
|
+
];
|
|
42
|
+
var bookingAttributionConfidences = [
|
|
43
|
+
"provider_redirect",
|
|
44
|
+
"provider_reported",
|
|
45
|
+
"manual_import",
|
|
46
|
+
"verified_webhook"
|
|
47
|
+
];
|
|
48
|
+
var bookingProviderSchema = z2.enum(bookingProviders);
|
|
49
|
+
var bookingAttributionModeSchema = z2.enum(bookingAttributionModes);
|
|
50
|
+
var bookingSignalSourceSchema = z2.enum(bookingSignalSources);
|
|
51
|
+
var bookingAttributionConfidenceSchema = z2.enum(bookingAttributionConfidences);
|
|
52
|
+
var bookingProviderHintSchema = z2.object({
|
|
53
|
+
bookingProvider: bookingProviderSchema,
|
|
54
|
+
bookingUrlHost: z2.string().min(1).max(253),
|
|
55
|
+
bookingAttributionMode: bookingAttributionModeSchema,
|
|
56
|
+
utmContent: z2.string().min(1).max(200).optional(),
|
|
57
|
+
successCondition: z2.literal("redirect_status=succeeded").optional()
|
|
58
|
+
}).superRefine((hint, context) => {
|
|
59
|
+
if (hint.successCondition !== void 0 && hint.bookingAttributionMode !== "provider_redirect") {
|
|
60
|
+
context.addIssue({
|
|
61
|
+
code: "custom",
|
|
62
|
+
message: "successCondition is allowed only for provider_redirect booking hints.",
|
|
63
|
+
path: ["successCondition"]
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// ../shared/dist/analytics-contract.js
|
|
69
|
+
import { z as z3 } from "zod";
|
|
70
|
+
var ANALYTICS_CONTRACT_VERSION = "analytics-contract.v1";
|
|
71
|
+
var ANALYTICS_CONTRACT_HASH = "sha256:a91bddcc3c5e4196933b0335d03eaf41ff929cb736bdd694fe54d07ab52dada1";
|
|
72
|
+
var ANALYTICS_IMPORT_SCHEMA_VERSION = "analytics-provider-import.v1";
|
|
73
|
+
var analyticsImportTargetIdSchema = z3.string().min(1).max(120).regex(/^[a-zA-Z][a-zA-Z0-9]*(?:[._-][a-zA-Z0-9]+)*$/u);
|
|
74
|
+
var analyticsProviderImportRowSchema = z3.object({
|
|
75
|
+
targetId: analyticsImportTargetIdSchema,
|
|
76
|
+
occurredAt: z3.string().datetime({ offset: true }),
|
|
77
|
+
attributionConfidence: z3.enum(["provider_reported", "manual_import"]),
|
|
78
|
+
providerEventId: z3.string().trim().min(1).max(160).optional(),
|
|
79
|
+
idempotencyKey: z3.string().trim().min(1).max(160).optional()
|
|
80
|
+
}).strict().superRefine((row, context) => {
|
|
81
|
+
if (!row.providerEventId && !row.idempotencyKey) {
|
|
82
|
+
context.addIssue({
|
|
83
|
+
code: "custom",
|
|
84
|
+
message: "Each import row requires providerEventId or idempotencyKey.",
|
|
85
|
+
path: ["providerEventId"]
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
var analyticsProviderImportSchema = z3.object({
|
|
90
|
+
schemaVersion: z3.literal(ANALYTICS_IMPORT_SCHEMA_VERSION),
|
|
91
|
+
provider: z3.enum(["treatwell", "beautinda", "other"]),
|
|
92
|
+
sourceName: z3.string().trim().min(1).max(160).optional(),
|
|
93
|
+
rows: z3.array(analyticsProviderImportRowSchema).min(1).max(1e3)
|
|
94
|
+
}).strict();
|
|
95
|
+
|
|
96
|
+
// ../shared/dist/analytics-events.js
|
|
97
|
+
import { z as z4 } from "zod";
|
|
98
|
+
var ANALYTICS_SCHEMA_VERSION = "analytics.v1";
|
|
99
|
+
var analyticsSessionIdSchema = z4.string().regex(/^cpa_s_[a-f0-9]{32}$/u);
|
|
100
|
+
var analyticsVisitorIdSchema = z4.string().regex(/^cpa_v_[a-f0-9]{32}$/u);
|
|
101
|
+
var analyticsPersistedIdentityHashSchema = z4.string().regex(/^[a-f0-9]{64}$/u);
|
|
102
|
+
var analyticsEventNames = [
|
|
103
|
+
"page_view",
|
|
104
|
+
"section_view",
|
|
105
|
+
"cta_click",
|
|
106
|
+
"outbound_click",
|
|
107
|
+
"email_click",
|
|
108
|
+
"phone_click",
|
|
109
|
+
"file_download",
|
|
110
|
+
"form_start",
|
|
111
|
+
"form_submit",
|
|
112
|
+
"form_error",
|
|
113
|
+
"lead_created",
|
|
114
|
+
"checkout_started",
|
|
115
|
+
"conversion",
|
|
116
|
+
"checkout_completed",
|
|
117
|
+
"payment_succeeded",
|
|
118
|
+
"subscription_started",
|
|
119
|
+
"purchase_completed",
|
|
120
|
+
"trial_started"
|
|
121
|
+
];
|
|
122
|
+
var serverOnlyAnalyticsEvents = [
|
|
123
|
+
"checkout_completed",
|
|
124
|
+
"payment_succeeded",
|
|
125
|
+
"subscription_started",
|
|
126
|
+
"purchase_completed",
|
|
127
|
+
"trial_started"
|
|
128
|
+
];
|
|
129
|
+
var analyticsEventSources = [
|
|
130
|
+
"browser",
|
|
131
|
+
"server",
|
|
132
|
+
"test",
|
|
133
|
+
"editor_preview",
|
|
134
|
+
"system"
|
|
135
|
+
];
|
|
136
|
+
var analyticsIngestionChannels = [
|
|
137
|
+
"browser_collector",
|
|
138
|
+
"server_sdk",
|
|
139
|
+
"provider_endpoint",
|
|
140
|
+
"provider_import",
|
|
141
|
+
"test",
|
|
142
|
+
"editor_preview",
|
|
143
|
+
"system_job"
|
|
144
|
+
];
|
|
145
|
+
var analyticsTargetTypes = [
|
|
146
|
+
"cta",
|
|
147
|
+
"form",
|
|
148
|
+
"section",
|
|
149
|
+
"file",
|
|
150
|
+
"link",
|
|
151
|
+
"conversion",
|
|
152
|
+
"server_conversion"
|
|
153
|
+
];
|
|
154
|
+
var analyticsDataAttributeEvents = [
|
|
155
|
+
"cta_click",
|
|
156
|
+
"outbound_click",
|
|
157
|
+
"email_click",
|
|
158
|
+
"phone_click",
|
|
159
|
+
"file_download",
|
|
160
|
+
"section_view",
|
|
161
|
+
"checkout_started",
|
|
162
|
+
"form"
|
|
163
|
+
];
|
|
164
|
+
var analyticsDeviceTypes = [
|
|
165
|
+
"desktop",
|
|
166
|
+
"tablet",
|
|
167
|
+
"mobile",
|
|
168
|
+
"unknown"
|
|
169
|
+
];
|
|
170
|
+
var analyticsBotClassifications = [
|
|
171
|
+
"normal",
|
|
172
|
+
"known_bot",
|
|
173
|
+
"suspicious",
|
|
174
|
+
"invalid"
|
|
175
|
+
];
|
|
176
|
+
var analyticsValidityStatuses = [
|
|
177
|
+
"accepted",
|
|
178
|
+
"rejected",
|
|
179
|
+
"ignored"
|
|
180
|
+
];
|
|
181
|
+
var analyticsEventNameSchema = z4.enum(analyticsEventNames);
|
|
182
|
+
var serverOnlyAnalyticsEventSchema = z4.enum(serverOnlyAnalyticsEvents);
|
|
183
|
+
var analyticsEventSourceSchema = z4.enum(analyticsEventSources);
|
|
184
|
+
var analyticsIngestionChannelSchema = z4.enum(analyticsIngestionChannels);
|
|
185
|
+
var analyticsTargetTypeSchema = z4.enum(analyticsTargetTypes);
|
|
186
|
+
var analyticsDataAttributeEventSchema = z4.enum(analyticsDataAttributeEvents);
|
|
187
|
+
var analyticsDeviceTypeSchema = z4.enum(analyticsDeviceTypes);
|
|
188
|
+
var analyticsBotClassificationSchema = z4.enum(analyticsBotClassifications);
|
|
189
|
+
var analyticsValidityStatusSchema = z4.enum(analyticsValidityStatuses);
|
|
190
|
+
var analyticsPropertiesSchema = z4.record(z4.string(), z4.unknown());
|
|
191
|
+
var analyticsTimestampSchema = z4.string().datetime({ offset: true });
|
|
192
|
+
var analyticsBrowserEventSchema = z4.object({
|
|
193
|
+
eventId: z4.string().min(1).max(160),
|
|
194
|
+
eventName: analyticsEventNameSchema,
|
|
195
|
+
eventSource: analyticsEventSourceSchema.optional().default("browser"),
|
|
196
|
+
targetId: z4.string().min(1).max(160).optional(),
|
|
197
|
+
targetType: analyticsTargetTypeSchema.optional(),
|
|
198
|
+
visitorId: analyticsVisitorIdSchema.optional(),
|
|
199
|
+
sessionId: analyticsSessionIdSchema,
|
|
200
|
+
idempotencyKey: z4.string().min(1).max(240).optional(),
|
|
201
|
+
occurredAt: analyticsTimestampSchema,
|
|
202
|
+
pageUrl: z4.string().url().max(2048).optional(),
|
|
203
|
+
pagePath: z4.string().min(1).max(1024).optional(),
|
|
204
|
+
routeTemplate: z4.string().min(1).max(1024).optional(),
|
|
205
|
+
referrer: z4.string().url().max(2048).optional(),
|
|
206
|
+
utmSource: z4.string().min(1).max(200).optional(),
|
|
207
|
+
utmMedium: z4.string().min(1).max(200).optional(),
|
|
208
|
+
utmCampaign: z4.string().min(1).max(200).optional(),
|
|
209
|
+
utmContent: z4.string().min(1).max(200).optional(),
|
|
210
|
+
utmTerm: z4.string().min(1).max(200).optional(),
|
|
211
|
+
deviceType: analyticsDeviceTypeSchema.optional(),
|
|
212
|
+
browserName: z4.string().min(1).max(120).optional(),
|
|
213
|
+
osName: z4.string().min(1).max(120).optional(),
|
|
214
|
+
country: z4.string().min(1).max(80).optional(),
|
|
215
|
+
region: z4.string().min(1).max(120).optional(),
|
|
216
|
+
properties: analyticsPropertiesSchema.optional().default({})
|
|
217
|
+
}).superRefine((event, context) => {
|
|
218
|
+
if (isServerOnlyAnalyticsEvent(event.eventName)) {
|
|
219
|
+
context.addIssue({
|
|
220
|
+
code: "custom",
|
|
221
|
+
message: "Browser analytics payloads cannot contain server-only events.",
|
|
222
|
+
path: ["eventName"]
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
if (event.eventSource === "server" || event.eventSource === "system") {
|
|
226
|
+
context.addIssue({
|
|
227
|
+
code: "custom",
|
|
228
|
+
message: "Browser analytics payloads cannot use server/system sources.",
|
|
229
|
+
path: ["eventSource"]
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
if (event.targetType === "server_conversion") {
|
|
233
|
+
context.addIssue({
|
|
234
|
+
code: "custom",
|
|
235
|
+
message: "Browser analytics payloads cannot use server conversion targets.",
|
|
236
|
+
path: ["targetType"]
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
var analyticsBrowserEventPayloadSchema = z4.object({
|
|
241
|
+
siteKey: z4.string().min(1),
|
|
242
|
+
schemaVersion: z4.literal(ANALYTICS_SCHEMA_VERSION),
|
|
243
|
+
mode: z4.enum(["live", "test"]).optional().default("live"),
|
|
244
|
+
events: z4.array(analyticsBrowserEventSchema).min(1).max(50)
|
|
245
|
+
});
|
|
246
|
+
var analyticsNormalizedEventBaseSchema = z4.object({
|
|
247
|
+
site_id: z4.string().uuid(),
|
|
248
|
+
event_id: z4.string().min(1).max(160),
|
|
249
|
+
schema_version: z4.literal(ANALYTICS_SCHEMA_VERSION),
|
|
250
|
+
event_name: analyticsEventNameSchema,
|
|
251
|
+
event_source: analyticsEventSourceSchema,
|
|
252
|
+
ingestion_channel: analyticsIngestionChannelSchema,
|
|
253
|
+
target_id: z4.string().min(1).max(160).nullable().optional(),
|
|
254
|
+
target_type: analyticsTargetTypeSchema.nullable().optional(),
|
|
255
|
+
dedupe_key: z4.string().regex(/^analytics_dedupe_v1_[a-f0-9]{64}$/u).nullable().optional(),
|
|
256
|
+
idempotency_key: z4.string().min(1).max(240).nullable().optional(),
|
|
257
|
+
bot_classification: analyticsBotClassificationSchema,
|
|
258
|
+
validity_status: analyticsValidityStatusSchema,
|
|
259
|
+
rejection_reason: z4.string().min(1).max(240).nullable().optional(),
|
|
260
|
+
occurred_at: analyticsTimestampSchema,
|
|
261
|
+
received_at: analyticsTimestampSchema,
|
|
262
|
+
page_url: z4.string().url().max(2048).nullable().optional(),
|
|
263
|
+
page_path: z4.string().min(1).max(1024).nullable().optional(),
|
|
264
|
+
route_template: z4.string().min(1).max(1024).nullable().optional(),
|
|
265
|
+
referrer: z4.string().url().max(2048).nullable().optional(),
|
|
266
|
+
utm_source: z4.string().min(1).max(200).nullable().optional(),
|
|
267
|
+
utm_medium: z4.string().min(1).max(200).nullable().optional(),
|
|
268
|
+
utm_campaign: z4.string().min(1).max(200).nullable().optional(),
|
|
269
|
+
utm_content: z4.string().min(1).max(200).nullable().optional(),
|
|
270
|
+
utm_term: z4.string().min(1).max(200).nullable().optional(),
|
|
271
|
+
country: z4.string().min(1).max(80).nullable().optional(),
|
|
272
|
+
region: z4.string().min(1).max(120).nullable().optional(),
|
|
273
|
+
device_type: analyticsDeviceTypeSchema,
|
|
274
|
+
browser_name: z4.string().min(1).max(120).nullable().optional(),
|
|
275
|
+
os_name: z4.string().min(1).max(120).nullable().optional(),
|
|
276
|
+
provider: z4.string().min(1).max(120).nullable().optional(),
|
|
277
|
+
provider_event_id: z4.string().min(1).max(240).nullable().optional(),
|
|
278
|
+
properties: analyticsPropertiesSchema
|
|
279
|
+
});
|
|
280
|
+
var analyticsRawNormalizedEventSchema = analyticsNormalizedEventBaseSchema.extend({
|
|
281
|
+
visitor_id: analyticsVisitorIdSchema.nullable().optional(),
|
|
282
|
+
session_id: analyticsSessionIdSchema.nullable().optional()
|
|
283
|
+
});
|
|
284
|
+
var analyticsNormalizedEventSchema = analyticsNormalizedEventBaseSchema.extend({
|
|
285
|
+
visitor_id: analyticsPersistedIdentityHashSchema.nullable().optional(),
|
|
286
|
+
session_id: analyticsPersistedIdentityHashSchema.nullable().optional()
|
|
287
|
+
});
|
|
288
|
+
function isServerOnlyAnalyticsEvent(eventName) {
|
|
289
|
+
return serverOnlyAnalyticsEvents.includes(eventName);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// ../shared/dist/analytics-manifest.js
|
|
293
|
+
import { z as z5 } from "zod";
|
|
294
|
+
var analyticsTargetIdSchema = z5.string().min(1).max(120).regex(/^[a-zA-Z][a-zA-Z0-9]*(?:[._-][a-zA-Z0-9]+)*$/u, {
|
|
295
|
+
message: "Analytics target IDs must be stable identifiers without whitespace or visible copy."
|
|
296
|
+
});
|
|
297
|
+
var analyticsManifestRecordSchema = z5.record(z5.string(), z5.unknown());
|
|
298
|
+
var analyticsFormPurposes = [
|
|
299
|
+
"contact",
|
|
300
|
+
"lead",
|
|
301
|
+
"newsletter",
|
|
302
|
+
"search",
|
|
303
|
+
"login",
|
|
304
|
+
"other"
|
|
305
|
+
];
|
|
306
|
+
var analyticsFormPurposeSchema = z5.enum(analyticsFormPurposes);
|
|
307
|
+
var analyticsManifestRouteSchema = z5.object({
|
|
308
|
+
path: z5.string().min(1).max(1024),
|
|
309
|
+
pageType: z5.string().min(1).max(120).optional(),
|
|
310
|
+
label: z5.string().min(1).max(200).optional()
|
|
311
|
+
}).passthrough();
|
|
312
|
+
var analyticsManifestTargetSchema = z5.object({
|
|
313
|
+
id: analyticsTargetIdSchema,
|
|
314
|
+
type: analyticsTargetTypeSchema,
|
|
315
|
+
event: analyticsEventNameSchema.optional(),
|
|
316
|
+
events: z5.array(analyticsEventNameSchema).min(1).max(20).optional(),
|
|
317
|
+
route: z5.string().min(1).max(1024).optional(),
|
|
318
|
+
label: z5.string().min(1).max(200).optional(),
|
|
319
|
+
description: z5.string().min(1).max(1e3).optional(),
|
|
320
|
+
autoActive: z5.boolean().optional(),
|
|
321
|
+
sourceHints: analyticsManifestRecordSchema.optional()
|
|
322
|
+
}).passthrough().superRefine((target, context) => {
|
|
323
|
+
if (target.event === void 0 && target.events === void 0) {
|
|
324
|
+
context.addIssue({
|
|
325
|
+
code: "custom",
|
|
326
|
+
message: "Analytics targets require event or events.",
|
|
327
|
+
path: ["event"]
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
if (target.event !== void 0 && target.events !== void 0) {
|
|
331
|
+
context.addIssue({
|
|
332
|
+
code: "custom",
|
|
333
|
+
message: "Analytics targets must use event or events, never both.",
|
|
334
|
+
path: ["events"]
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
if (target.type === "form" && !analyticsFormPurposeSchema.safeParse(target.sourceHints?.formPurpose).success) {
|
|
338
|
+
context.addIssue({
|
|
339
|
+
code: "custom",
|
|
340
|
+
message: "Analytics form targets require a typed sourceHints.formPurpose.",
|
|
341
|
+
path: ["sourceHints", "formPurpose"]
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
var analyticsManifestSchema = z5.object({
|
|
346
|
+
schemaVersion: z5.literal(ANALYTICS_SCHEMA_VERSION),
|
|
347
|
+
siteId: z5.string().min(1),
|
|
348
|
+
siteKeyRef: z5.string().regex(/^env:[A-Z][A-Z0-9_]*$/u, {
|
|
349
|
+
message: "siteKeyRef must be an environment-variable reference."
|
|
350
|
+
}),
|
|
351
|
+
siteKeyLookup: z5.string().regex(/^pk_siteplane_[A-Za-z0-9_-]{16}$/),
|
|
352
|
+
routes: z5.array(analyticsManifestRouteSchema),
|
|
353
|
+
targets: z5.array(analyticsManifestTargetSchema),
|
|
354
|
+
metadata: analyticsManifestRecordSchema.optional(),
|
|
355
|
+
siteKey: z5.string().optional(),
|
|
356
|
+
secretKey: z5.string().optional(),
|
|
357
|
+
rawKey: z5.string().optional(),
|
|
358
|
+
token: z5.string().optional(),
|
|
359
|
+
accessToken: z5.string().optional()
|
|
360
|
+
}).passthrough().superRefine((manifest, context) => {
|
|
361
|
+
const targetIds = /* @__PURE__ */ new Set();
|
|
362
|
+
for (const [index, target] of manifest.targets.entries()) {
|
|
363
|
+
if (targetIds.has(target.id)) {
|
|
364
|
+
context.addIssue({
|
|
365
|
+
code: "custom",
|
|
366
|
+
message: `Duplicate analytics target ID: ${target.id}`,
|
|
367
|
+
path: ["targets", index, "id"]
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
targetIds.add(target.id);
|
|
371
|
+
}
|
|
372
|
+
for (const issue of findUnsafeSecretReferences(manifest)) {
|
|
373
|
+
context.addIssue({
|
|
374
|
+
code: "custom",
|
|
375
|
+
message: "Raw secret material is not allowed in analytics manifests.",
|
|
376
|
+
path: issue
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
var secretValuePattern = /(?:\bsk_(?:live|test)_|\bcpb_|\bpmt_(?:pc|setup|cli)_|\bservice_role\b|bearer\s+[a-z0-9._~-]+)/iu;
|
|
381
|
+
function findUnsafeSecretReferences(value, path = []) {
|
|
382
|
+
if (Array.isArray(value)) {
|
|
383
|
+
return value.flatMap((item, index) => findUnsafeSecretReferences(item, [...path, index]));
|
|
384
|
+
}
|
|
385
|
+
if (!isPlainRecord(value)) {
|
|
386
|
+
return [];
|
|
387
|
+
}
|
|
388
|
+
return Object.entries(value).flatMap(([key, childValue]) => {
|
|
389
|
+
const childPath = [...path, key];
|
|
390
|
+
const referenceLikeKey = /(?:ref|lookup|prefix)$/iu.test(key);
|
|
391
|
+
const unsafeHere = referenceLikeKey && typeof childValue === "string" && secretValuePattern.test(childValue);
|
|
392
|
+
return [
|
|
393
|
+
...unsafeHere ? [childPath] : [],
|
|
394
|
+
...findUnsafeSecretReferences(childValue, childPath)
|
|
395
|
+
];
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
function isPlainRecord(value) {
|
|
399
|
+
return typeof value === "object" && value !== null;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// ../shared/dist/analytics-metrics.js
|
|
403
|
+
import { z as z6 } from "zod";
|
|
404
|
+
var analyticsVisitorQualities = ["exact", "estimated"];
|
|
405
|
+
var analyticsVisitorQualitySchema = z6.enum(analyticsVisitorQualities);
|
|
406
|
+
var analyticsKpiKeys = [
|
|
407
|
+
"pageViews",
|
|
408
|
+
"sessions",
|
|
409
|
+
"visitors",
|
|
410
|
+
"estimatedVisitors",
|
|
411
|
+
"inquiries",
|
|
412
|
+
"leads",
|
|
413
|
+
"conversions",
|
|
414
|
+
"conversionRate",
|
|
415
|
+
"inquiryRate"
|
|
416
|
+
];
|
|
417
|
+
var analyticsKpiKeySchema = z6.enum(analyticsKpiKeys);
|
|
418
|
+
|
|
419
|
+
// ../shared/dist/analytics-privacy.js
|
|
420
|
+
var serverPropertySchemas = {
|
|
421
|
+
lead_created: {
|
|
422
|
+
conversionId: "identifier",
|
|
423
|
+
formId: "identifier",
|
|
424
|
+
formType: "enum",
|
|
425
|
+
customerRef: "identifier"
|
|
426
|
+
},
|
|
427
|
+
conversion: {
|
|
428
|
+
conversionId: "identifier",
|
|
429
|
+
conversionType: "enum",
|
|
430
|
+
attributionConfidence: "enum",
|
|
431
|
+
bookingSignalSource: "enum",
|
|
432
|
+
bookingProvider: "enum",
|
|
433
|
+
redirectStatus: "enum",
|
|
434
|
+
importId: "identifier"
|
|
435
|
+
},
|
|
436
|
+
checkout_completed: revenuePropertySchema(),
|
|
437
|
+
payment_succeeded: revenuePropertySchema(),
|
|
438
|
+
subscription_started: revenuePropertySchema(),
|
|
439
|
+
purchase_completed: revenuePropertySchema(),
|
|
440
|
+
trial_started: revenuePropertySchema()
|
|
441
|
+
};
|
|
442
|
+
function revenuePropertySchema() {
|
|
443
|
+
return {
|
|
444
|
+
conversionId: "identifier",
|
|
445
|
+
checkoutId: "identifier",
|
|
446
|
+
customerRef: "identifier",
|
|
447
|
+
amount: "amount",
|
|
448
|
+
currency: "enum",
|
|
449
|
+
planId: "identifier",
|
|
450
|
+
productId: "identifier"
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// ../shared/dist/assets.js
|
|
455
|
+
import { z as z7 } from "zod";
|
|
456
|
+
var ASSET_STAGES = ["staging", "final", "deleted"];
|
|
457
|
+
var ASSET_VISIBILITIES = ["private", "public", "deleted"];
|
|
458
|
+
var ASSET_USAGE_ENTITY_TYPES = [
|
|
459
|
+
"branding",
|
|
460
|
+
"draft",
|
|
461
|
+
"change_set",
|
|
462
|
+
"published_value"
|
|
463
|
+
];
|
|
464
|
+
var assetStageSchema = z7.enum(ASSET_STAGES);
|
|
465
|
+
var assetVisibilitySchema = z7.enum(ASSET_VISIBILITIES);
|
|
466
|
+
var assetUsageEntityTypeSchema = z7.enum(ASSET_USAGE_ENTITY_TYPES);
|
|
467
|
+
var brandingAccentColorSchema = z7.string().regex(/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/);
|
|
468
|
+
|
|
469
|
+
// ../shared/dist/booking-contracts.js
|
|
470
|
+
import { z as z8 } from "zod";
|
|
471
|
+
var bookingStatuses = [
|
|
472
|
+
"pending_payment",
|
|
473
|
+
"confirmed",
|
|
474
|
+
"payment_expired",
|
|
475
|
+
"cancelled",
|
|
476
|
+
"no_show"
|
|
477
|
+
];
|
|
478
|
+
var bookingPaymentStatuses = [
|
|
479
|
+
"payment_not_required",
|
|
480
|
+
"checkout_open",
|
|
481
|
+
"processing",
|
|
482
|
+
"succeeded",
|
|
483
|
+
"failed",
|
|
484
|
+
"cancelled",
|
|
485
|
+
"expired"
|
|
486
|
+
];
|
|
487
|
+
var bookingAccountReadinessStatuses = [
|
|
488
|
+
"not_started",
|
|
489
|
+
"setup_required",
|
|
490
|
+
"under_review",
|
|
491
|
+
"payments_ready",
|
|
492
|
+
"payouts_restricted",
|
|
493
|
+
"payments_restricted",
|
|
494
|
+
"deactivated"
|
|
495
|
+
];
|
|
496
|
+
var bookingRefundStatuses = [
|
|
497
|
+
"none",
|
|
498
|
+
"pending",
|
|
499
|
+
"partially_refunded",
|
|
500
|
+
"refunded",
|
|
501
|
+
"failed"
|
|
502
|
+
];
|
|
503
|
+
var bookingDisputeStatuses = [
|
|
504
|
+
"none",
|
|
505
|
+
"needs_response",
|
|
506
|
+
"under_review",
|
|
507
|
+
"won",
|
|
508
|
+
"lost"
|
|
509
|
+
];
|
|
510
|
+
var bookingPaymentModes = [
|
|
511
|
+
"onsite_only",
|
|
512
|
+
"online_required",
|
|
513
|
+
"customer_choice"
|
|
514
|
+
];
|
|
515
|
+
var bookingPaymentCollectionTypes = [
|
|
516
|
+
"full_amount",
|
|
517
|
+
"deposit_percent"
|
|
518
|
+
];
|
|
519
|
+
var bookingResourceKinds = [
|
|
520
|
+
"person",
|
|
521
|
+
"room",
|
|
522
|
+
"equipment",
|
|
523
|
+
"other"
|
|
524
|
+
];
|
|
525
|
+
var bookingStatusSchema = z8.enum(bookingStatuses);
|
|
526
|
+
var bookingPaymentStatusSchema = z8.enum(bookingPaymentStatuses);
|
|
527
|
+
var bookingAccountReadinessStatusSchema = z8.enum(bookingAccountReadinessStatuses);
|
|
528
|
+
var bookingRefundStatusSchema = z8.enum(bookingRefundStatuses);
|
|
529
|
+
var bookingDisputeStatusSchema = z8.enum(bookingDisputeStatuses);
|
|
530
|
+
var bookingPaymentModeSchema = z8.enum(bookingPaymentModes);
|
|
531
|
+
var bookingPaymentCollectionTypeSchema = z8.enum(bookingPaymentCollectionTypes);
|
|
532
|
+
var bookingPaymentCollectionSchema = z8.discriminatedUnion("type", [
|
|
533
|
+
z8.object({ type: z8.literal("full_amount") }).strict(),
|
|
534
|
+
z8.object({
|
|
535
|
+
type: z8.literal("deposit_percent"),
|
|
536
|
+
depositPercent: z8.number().int().min(10).max(90)
|
|
537
|
+
}).strict()
|
|
538
|
+
]);
|
|
539
|
+
var bookingPaymentChoices = ["onsite", "online"];
|
|
540
|
+
var bookingPaymentChoiceSchema = z8.enum(bookingPaymentChoices);
|
|
541
|
+
var bookingPaymentSnapshotSchema = z8.object({
|
|
542
|
+
snapshotId: z8.string().uuid(),
|
|
543
|
+
snapshotVersion: z8.number().int().positive(),
|
|
544
|
+
currency: z8.literal("EUR"),
|
|
545
|
+
totalAmountCents: z8.number().int().min(0),
|
|
546
|
+
onlineAmountCents: z8.number().int().min(0),
|
|
547
|
+
onsiteRemainderCents: z8.number().int().min(0),
|
|
548
|
+
maxStripeRefundAmountCents: z8.number().int().min(0)
|
|
549
|
+
}).strict().superRefine((snapshot, context) => {
|
|
550
|
+
if (snapshot.onlineAmountCents + snapshot.onsiteRemainderCents !== snapshot.totalAmountCents) {
|
|
551
|
+
context.addIssue({
|
|
552
|
+
code: "custom",
|
|
553
|
+
message: "Payment snapshot amounts must add up to the total amount."
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
if (snapshot.maxStripeRefundAmountCents > snapshot.onlineAmountCents) {
|
|
557
|
+
context.addIssue({
|
|
558
|
+
code: "custom",
|
|
559
|
+
message: "Stripe refund basis cannot exceed the online amount."
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
var bookingServiceVariantSchema = z8.object({
|
|
564
|
+
id: z8.string().uuid(),
|
|
565
|
+
name: z8.string().trim().min(1).max(120),
|
|
566
|
+
durationMinutes: z8.number().int().min(1).max(1440),
|
|
567
|
+
priceCents: z8.number().int().min(0).nullable()
|
|
568
|
+
}).strict();
|
|
569
|
+
var bookingServiceVariantsSchema = z8.array(bookingServiceVariantSchema).min(1).max(20);
|
|
570
|
+
var isoDateSchema = z8.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
571
|
+
var utcDateTimeSchema = z8.string().datetime();
|
|
572
|
+
var bookingLocalTimeSchema = z8.string().regex(/^(?:[01]\d|2[0-3]):[0-5]\d$/);
|
|
573
|
+
var bookingAvailabilityIntervalsSchema = z8.array(z8.object({
|
|
574
|
+
start: bookingLocalTimeSchema,
|
|
575
|
+
end: bookingLocalTimeSchema
|
|
576
|
+
}).strict().refine((interval) => interval.end > interval.start)).superRefine((intervals, context) => {
|
|
577
|
+
const sorted = [...intervals].sort((left, right) => left.start.localeCompare(right.start));
|
|
578
|
+
for (let index = 1; index < sorted.length; index += 1) {
|
|
579
|
+
if (sorted[index].start < sorted[index - 1].end) {
|
|
580
|
+
context.addIssue({
|
|
581
|
+
code: "custom",
|
|
582
|
+
message: "Booking availability intervals must not overlap."
|
|
583
|
+
});
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
var bookingPublicConfigSchema = z8.object({
|
|
589
|
+
site: z8.object({
|
|
590
|
+
name: z8.string().trim().min(1),
|
|
591
|
+
timezone: z8.string().trim().min(1),
|
|
592
|
+
locale: z8.string().trim().min(2),
|
|
593
|
+
bookingPageSlug: z8.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).min(2).max(64),
|
|
594
|
+
privacyUrl: z8.string().url().nullable()
|
|
595
|
+
}),
|
|
596
|
+
branding: z8.object({
|
|
597
|
+
logoUrl: z8.string().url().nullable(),
|
|
598
|
+
accentColor: z8.string().trim().min(1).nullable()
|
|
599
|
+
}),
|
|
600
|
+
settings: z8.object({
|
|
601
|
+
slotIntervalMinutes: z8.number().int().positive(),
|
|
602
|
+
minLeadMinutes: z8.number().int().min(0),
|
|
603
|
+
maxAdvanceDays: z8.number().int().min(1),
|
|
604
|
+
requirePhone: z8.boolean(),
|
|
605
|
+
collectNote: z8.boolean(),
|
|
606
|
+
confirmationMessage: z8.string().nullable()
|
|
607
|
+
}),
|
|
608
|
+
/** Freitexte des Salons, im Kassen-Schritt vor der Bestaetigung sichtbar. */
|
|
609
|
+
policies: z8.object({
|
|
610
|
+
cancellation: z8.string().nullable(),
|
|
611
|
+
lateArrival: z8.string().nullable()
|
|
612
|
+
}),
|
|
613
|
+
payment: z8.object({
|
|
614
|
+
mode: bookingPaymentModeSchema,
|
|
615
|
+
collection: bookingPaymentCollectionSchema,
|
|
616
|
+
onlinePaymentsReady: z8.boolean()
|
|
617
|
+
}).strict(),
|
|
618
|
+
serviceCategories: z8.array(z8.object({
|
|
619
|
+
id: z8.string().uuid(),
|
|
620
|
+
name: z8.string().trim().min(1),
|
|
621
|
+
sortOrder: z8.number().int()
|
|
622
|
+
})),
|
|
623
|
+
serviceSubcategories: z8.array(z8.object({
|
|
624
|
+
id: z8.string().uuid(),
|
|
625
|
+
categoryId: z8.string().uuid(),
|
|
626
|
+
name: z8.string().trim().min(1),
|
|
627
|
+
sortOrder: z8.number().int()
|
|
628
|
+
})),
|
|
629
|
+
services: z8.array(z8.object({
|
|
630
|
+
id: z8.string().trim().min(1),
|
|
631
|
+
name: z8.string().trim().min(1),
|
|
632
|
+
durationMinutes: z8.number().int().positive(),
|
|
633
|
+
priceCents: z8.number().int().min(0).nullable(),
|
|
634
|
+
currency: z8.string().regex(/^[A-Z]{3}$/),
|
|
635
|
+
description: z8.string().nullable(),
|
|
636
|
+
categoryId: z8.string().uuid(),
|
|
637
|
+
subcategoryId: z8.string().uuid().nullable(),
|
|
638
|
+
variants: bookingServiceVariantsSchema,
|
|
639
|
+
allowsAnyResource: z8.boolean(),
|
|
640
|
+
sortOrder: z8.number().int()
|
|
641
|
+
})),
|
|
642
|
+
resources: z8.array(z8.object({
|
|
643
|
+
id: z8.string().trim().min(1),
|
|
644
|
+
name: z8.string().trim().min(1),
|
|
645
|
+
kind: z8.enum(bookingResourceKinds)
|
|
646
|
+
})),
|
|
647
|
+
serviceResources: z8.array(z8.object({
|
|
648
|
+
serviceId: z8.string().trim().min(1),
|
|
649
|
+
resourceId: z8.string().trim().min(1)
|
|
650
|
+
}))
|
|
651
|
+
});
|
|
652
|
+
var bookingItemSchema = z8.object({
|
|
653
|
+
serviceId: z8.string().trim().min(1),
|
|
654
|
+
variantId: z8.string().trim().min(1)
|
|
655
|
+
}).strict();
|
|
656
|
+
var bookingItemsSchema = z8.array(bookingItemSchema).min(1).max(10);
|
|
657
|
+
function parseBookingItemsParam(value) {
|
|
658
|
+
const items = value.split(",").map((entry) => entry.trim()).filter((entry) => entry !== "").map((entry) => {
|
|
659
|
+
const [serviceId, variantId, ...rest] = entry.split(":");
|
|
660
|
+
return serviceId && variantId && rest.length === 0 ? { serviceId, variantId } : null;
|
|
661
|
+
});
|
|
662
|
+
return items.every((item) => item !== null) ? items : null;
|
|
663
|
+
}
|
|
664
|
+
var bookingSlotsQuerySchema = z8.object({
|
|
665
|
+
items: z8.preprocess((value) => typeof value === "string" ? parseBookingItemsParam(value) : value, bookingItemsSchema),
|
|
666
|
+
resourceId: z8.string().trim().min(1).optional(),
|
|
667
|
+
fromDate: isoDateSchema,
|
|
668
|
+
toDate: isoDateSchema
|
|
669
|
+
});
|
|
670
|
+
var bookingSlotsResponseSchema = z8.object({
|
|
671
|
+
timezone: z8.string().trim().min(1),
|
|
672
|
+
items: bookingItemsSchema,
|
|
673
|
+
// Reine Behandlungsdauer ohne Puffer: das UI zeigt damit die Terminspanne,
|
|
674
|
+
// ohne selbst rechnen zu muessen.
|
|
675
|
+
totalDurationMinutes: z8.number().int().min(1),
|
|
676
|
+
mode: z8.enum(["any", "resource"]),
|
|
677
|
+
slots: z8.array(z8.object({
|
|
678
|
+
startUtc: utcDateTimeSchema,
|
|
679
|
+
resourceId: z8.string().trim().min(1).optional()
|
|
680
|
+
}))
|
|
681
|
+
});
|
|
682
|
+
var bookingAvailabilityQuerySchema = z8.object({
|
|
683
|
+
items: z8.preprocess((value) => typeof value === "string" ? parseBookingItemsParam(value) : value, bookingItemsSchema),
|
|
684
|
+
resourceId: z8.string().trim().min(1).optional(),
|
|
685
|
+
fromDate: isoDateSchema,
|
|
686
|
+
toDate: isoDateSchema
|
|
687
|
+
});
|
|
688
|
+
var bookingAvailabilityResponseSchema = z8.object({
|
|
689
|
+
timezone: z8.string().trim().min(1),
|
|
690
|
+
totalDurationMinutes: z8.number().int().min(1),
|
|
691
|
+
days: z8.array(z8.object({
|
|
692
|
+
date: isoDateSchema,
|
|
693
|
+
hasSlots: z8.boolean()
|
|
694
|
+
})),
|
|
695
|
+
/** Erster Tag mit Slot ab `fromDate`, auch ausserhalb des Fensters. */
|
|
696
|
+
nextAvailableDate: isoDateSchema.nullable()
|
|
697
|
+
});
|
|
698
|
+
var bookingPublicErrorCodes = [
|
|
699
|
+
"slot_unavailable",
|
|
700
|
+
"validation_error",
|
|
701
|
+
"origin_not_allowed",
|
|
702
|
+
"invalid_key",
|
|
703
|
+
"rate_limited",
|
|
704
|
+
"payment_required",
|
|
705
|
+
"payment_unavailable",
|
|
706
|
+
"payment_amount_too_small",
|
|
707
|
+
"already_cancelled",
|
|
708
|
+
"too_late",
|
|
709
|
+
"not_found"
|
|
710
|
+
];
|
|
711
|
+
var bookingPublicErrorSchema = z8.object({
|
|
712
|
+
error: z8.enum(bookingPublicErrorCodes),
|
|
713
|
+
message: z8.string().optional(),
|
|
714
|
+
requestId: z8.string().optional()
|
|
715
|
+
}).strict();
|
|
716
|
+
var bookingCreateRequestSchema = z8.object({
|
|
717
|
+
items: bookingItemsSchema,
|
|
718
|
+
resourceId: z8.string().min(1).nullable(),
|
|
719
|
+
startUtc: utcDateTimeSchema,
|
|
720
|
+
customer: z8.object({
|
|
721
|
+
name: z8.string().min(1).max(120),
|
|
722
|
+
email: z8.string().email().max(254),
|
|
723
|
+
phone: z8.string().max(32).optional(),
|
|
724
|
+
note: z8.string().max(1e3).optional()
|
|
725
|
+
}).strict(),
|
|
726
|
+
hp: z8.string().optional(),
|
|
727
|
+
clientToken: z8.string().uuid(),
|
|
728
|
+
paymentChoice: bookingPaymentChoiceSchema,
|
|
729
|
+
// Werbe-Einwilligung. Bestaetigung, Erinnerung und Storno-Link sind davon
|
|
730
|
+
// unabhaengig und werden immer versendet.
|
|
731
|
+
consent: z8.object({
|
|
732
|
+
marketingEmail: z8.boolean(),
|
|
733
|
+
marketingSms: z8.boolean()
|
|
734
|
+
}).strict().optional()
|
|
735
|
+
}).strict();
|
|
736
|
+
var bookingPublicPaymentResponseSchema = z8.object({
|
|
737
|
+
clientSecret: z8.string().min(1),
|
|
738
|
+
stripeAccountId: z8.string().startsWith("acct_")
|
|
739
|
+
}).strict();
|
|
740
|
+
var bookingCreateResponseSchema = z8.union([
|
|
741
|
+
z8.object({
|
|
742
|
+
status: z8.literal("confirmed"),
|
|
743
|
+
booking: z8.object({
|
|
744
|
+
id: z8.string().min(1),
|
|
745
|
+
startUtc: utcDateTimeSchema,
|
|
746
|
+
endUtc: utcDateTimeSchema,
|
|
747
|
+
timezone: z8.string().trim().min(1),
|
|
748
|
+
serviceName: z8.string().trim().min(1),
|
|
749
|
+
resourceName: z8.string().trim().min(1)
|
|
750
|
+
}).strict(),
|
|
751
|
+
manageUrl: z8.string().url(),
|
|
752
|
+
confirmationMessage: z8.string().nullable()
|
|
753
|
+
}).strict(),
|
|
754
|
+
z8.object({
|
|
755
|
+
status: z8.literal("pending_payment"),
|
|
756
|
+
bookingId: z8.string().min(1),
|
|
757
|
+
holdExpiresAt: utcDateTimeSchema,
|
|
758
|
+
payment: bookingPublicPaymentResponseSchema,
|
|
759
|
+
statusToken: z8.string().min(20),
|
|
760
|
+
manageUrl: z8.string().url().optional()
|
|
761
|
+
}).strict(),
|
|
762
|
+
bookingPublicErrorSchema
|
|
763
|
+
]);
|
|
764
|
+
var bookingStatusResponseSchema = z8.object({
|
|
765
|
+
bookingStatus: bookingStatusSchema,
|
|
766
|
+
paymentStatus: bookingPaymentStatusSchema,
|
|
767
|
+
refundStatus: bookingRefundStatusSchema,
|
|
768
|
+
disputeStatus: bookingDisputeStatusSchema,
|
|
769
|
+
holdExpiresAt: utcDateTimeSchema.nullable()
|
|
770
|
+
}).strict();
|
|
771
|
+
var bookingCancelRequestSchema = z8.object({
|
|
772
|
+
token: z8.string().min(20)
|
|
773
|
+
});
|
|
774
|
+
var bookingCancelResponseSchema = z8.union([
|
|
775
|
+
z8.object({
|
|
776
|
+
status: z8.enum(["cancelled", "already_cancelled", "too_late", "not_found"])
|
|
777
|
+
}),
|
|
778
|
+
bookingPublicErrorSchema
|
|
779
|
+
]);
|
|
780
|
+
var bookingMoveErrorCodes = [
|
|
781
|
+
"slot_unavailable",
|
|
782
|
+
"booking_not_movable",
|
|
783
|
+
"not_found"
|
|
784
|
+
];
|
|
785
|
+
var bookingMoveRequestSchema = z8.object({
|
|
786
|
+
bookingId: z8.string().uuid(),
|
|
787
|
+
newStartUtc: utcDateTimeSchema,
|
|
788
|
+
newResourceId: z8.string().uuid().optional(),
|
|
789
|
+
notifyCustomer: z8.boolean().default(false),
|
|
790
|
+
reason: z8.string().max(500).optional()
|
|
791
|
+
});
|
|
792
|
+
var bookingMoveResponseSchema = z8.union([
|
|
793
|
+
z8.object({
|
|
794
|
+
status: z8.literal("moved"),
|
|
795
|
+
bookingId: z8.string().uuid(),
|
|
796
|
+
startUtc: utcDateTimeSchema,
|
|
797
|
+
endUtc: utcDateTimeSchema,
|
|
798
|
+
resourceId: z8.string().uuid()
|
|
799
|
+
}),
|
|
800
|
+
z8.object({
|
|
801
|
+
error: z8.enum(bookingMoveErrorCodes),
|
|
802
|
+
message: z8.string().optional(),
|
|
803
|
+
requestId: z8.string().optional()
|
|
804
|
+
})
|
|
805
|
+
]);
|
|
806
|
+
var bookingManifestSchema = z8.object({
|
|
807
|
+
schemaVersion: z8.number().int().positive(),
|
|
808
|
+
siteId: z8.string().trim().min(1),
|
|
809
|
+
siteKeyPrefix: z8.string().startsWith("bk_pub_"),
|
|
810
|
+
bookingPageSlug: z8.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).min(2).max(64),
|
|
811
|
+
embedMode: z8.enum(["script", "web_component", "iframe"]).optional(),
|
|
812
|
+
productionOrigin: z8.string().url().optional()
|
|
813
|
+
}).passthrough();
|
|
814
|
+
var secretManifestKeys = /* @__PURE__ */ new Set([
|
|
815
|
+
"siteKey",
|
|
816
|
+
"rawKey",
|
|
817
|
+
"token",
|
|
818
|
+
"accessToken"
|
|
819
|
+
]);
|
|
820
|
+
function redactBookingManifest(value) {
|
|
821
|
+
if (Array.isArray(value)) {
|
|
822
|
+
return value.map((item) => redactBookingManifest(item));
|
|
823
|
+
}
|
|
824
|
+
if (value && typeof value === "object") {
|
|
825
|
+
return Object.fromEntries(Object.entries(value).filter(([key]) => !secretManifestKeys.has(key)).map(([key, item]) => [key, redactBookingManifest(item)]));
|
|
826
|
+
}
|
|
827
|
+
return value;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// ../shared/dist/booking-defaults.js
|
|
831
|
+
var BOOKING_CREATE_PAYLOAD_LIMIT_BYTES = 16 * 1024;
|
|
832
|
+
|
|
833
|
+
// ../shared/dist/bridge-protocol.js
|
|
834
|
+
import { z as z15 } from "zod";
|
|
835
|
+
|
|
836
|
+
// ../shared/dist/field-id.js
|
|
837
|
+
import { z as z9 } from "zod";
|
|
838
|
+
var FIELD_ID_PATTERN = /^[a-z][a-z0-9_-]*(\.[a-z][a-z0-9_-]*)+$/;
|
|
839
|
+
var fieldIdSchema = z9.string().regex(FIELD_ID_PATTERN, "Invalid field id");
|
|
840
|
+
|
|
841
|
+
// ../shared/dist/field-types.js
|
|
842
|
+
import { z as z10 } from "zod";
|
|
843
|
+
var FIELD_TYPES = ["text", "longText", "image", "link"];
|
|
844
|
+
var fieldTypeSchema = z10.enum(FIELD_TYPES);
|
|
845
|
+
|
|
846
|
+
// ../shared/dist/image-value.js
|
|
847
|
+
import { z as z11 } from "zod";
|
|
848
|
+
var SUPPORTED_IMAGE_CONTENT_TYPES = [
|
|
849
|
+
"image/jpeg",
|
|
850
|
+
"image/png",
|
|
851
|
+
"image/webp",
|
|
852
|
+
"image/avif"
|
|
853
|
+
];
|
|
854
|
+
var maxImageUploadBytes = 10 * 1024 * 1024;
|
|
855
|
+
var largeImageWarningBytes = 2 * 1024 * 1024;
|
|
856
|
+
var imageContentTypeSchema = z11.enum(SUPPORTED_IMAGE_CONTENT_TYPES);
|
|
857
|
+
var imageCropSchema = z11.object({
|
|
858
|
+
x: z11.number().min(0).max(100),
|
|
859
|
+
y: z11.number().min(0).max(100),
|
|
860
|
+
zoom: z11.number().min(1).max(4),
|
|
861
|
+
aspectRatio: z11.number().positive().max(10)
|
|
862
|
+
});
|
|
863
|
+
var imageValueSchema = z11.object({
|
|
864
|
+
src: z11.string().trim().min(1),
|
|
865
|
+
alt: z11.string().trim().min(1).max(300),
|
|
866
|
+
width: z11.number().int().positive().optional(),
|
|
867
|
+
height: z11.number().int().positive().optional(),
|
|
868
|
+
crop: imageCropSchema.optional(),
|
|
869
|
+
assetId: z11.string().uuid().optional(),
|
|
870
|
+
finalStorageProvider: z11.enum([
|
|
871
|
+
"repo_static_assets",
|
|
872
|
+
"repo_handoff",
|
|
873
|
+
"site_owned_storage",
|
|
874
|
+
"siteplane_managed_fallback"
|
|
875
|
+
]).optional()
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
// ../shared/dist/site-setup.js
|
|
879
|
+
import { z as z14 } from "zod";
|
|
880
|
+
|
|
881
|
+
// ../shared/dist/site-field-contract.js
|
|
882
|
+
import { z as z13 } from "zod";
|
|
883
|
+
|
|
884
|
+
// ../shared/dist/fallback-hash.js
|
|
885
|
+
import { z as z12 } from "zod";
|
|
886
|
+
var SAFE_REPRESENTATION_REASONS = [
|
|
887
|
+
"asset_import_rewritten",
|
|
888
|
+
"relative_url_normalized",
|
|
889
|
+
"line_endings_normalized",
|
|
890
|
+
"image_value_serialized"
|
|
891
|
+
];
|
|
892
|
+
var safeRepresentationReasonSchema = z12.enum(SAFE_REPRESENTATION_REASONS);
|
|
893
|
+
var linkFallbackSchema = z12.object({
|
|
894
|
+
href: z12.string().trim().min(1),
|
|
895
|
+
label: z12.string().trim().min(1).optional(),
|
|
896
|
+
target: z12.enum(["_self", "_blank"]).optional()
|
|
897
|
+
}).passthrough();
|
|
898
|
+
function stableJsonStringify(value) {
|
|
899
|
+
return JSON.stringify(sortObjectKeys(value));
|
|
900
|
+
}
|
|
901
|
+
function sortObjectKeys(value) {
|
|
902
|
+
if (Array.isArray(value)) {
|
|
903
|
+
return value.map(sortObjectKeys);
|
|
904
|
+
}
|
|
905
|
+
if (!value || typeof value !== "object") {
|
|
906
|
+
return value;
|
|
907
|
+
}
|
|
908
|
+
return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entryValue]) => [key, sortObjectKeys(entryValue)]));
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// ../shared/dist/site-field-contract.js
|
|
912
|
+
var siteFieldStatusSchema = z13.enum(["hidden", "active"]);
|
|
913
|
+
var semanticVersionSchema = z13.string().regex(/^\d+\.\d+\.\d+$/u);
|
|
914
|
+
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.");
|
|
915
|
+
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.");
|
|
916
|
+
var sourceMappingSchema = z13.object({
|
|
917
|
+
kind: z13.literal("function"),
|
|
918
|
+
filePath: repositoryRelativePosixPathSchema,
|
|
919
|
+
exportName: z13.string().trim().min(1).optional(),
|
|
920
|
+
sourcePath: z13.string().trim().min(1).optional(),
|
|
921
|
+
editTarget: fieldIdSchema.optional()
|
|
922
|
+
}).strict();
|
|
923
|
+
var domMappingSchema = z13.object({
|
|
924
|
+
attribute: z13.literal("data-siteplane-field-id"),
|
|
925
|
+
previewStrategy: z13.enum(["dom", "event"])
|
|
926
|
+
}).strict();
|
|
927
|
+
var fieldBaseSchema = z13.object({
|
|
928
|
+
fieldId: fieldIdSchema,
|
|
929
|
+
routeKey: siteFieldRouteKeySchema,
|
|
930
|
+
source: sourceMappingSchema,
|
|
931
|
+
dom: domMappingSchema.optional()
|
|
932
|
+
}).strict();
|
|
933
|
+
var linkFallbackSchema2 = z13.object({
|
|
934
|
+
href: z13.string().trim().min(1),
|
|
935
|
+
label: z13.string().trim().min(1).optional(),
|
|
936
|
+
target: z13.enum(["_self", "_blank"]).optional()
|
|
937
|
+
}).strict();
|
|
938
|
+
var imageFallbackSchema = z13.object({
|
|
939
|
+
src: z13.string().trim().min(1),
|
|
940
|
+
alt: z13.string().trim().max(300),
|
|
941
|
+
width: z13.number().int().positive().optional(),
|
|
942
|
+
height: z13.number().int().positive().optional(),
|
|
943
|
+
crop: z13.object({
|
|
944
|
+
x: z13.number().min(0).max(100),
|
|
945
|
+
y: z13.number().min(0).max(100),
|
|
946
|
+
zoom: z13.number().min(1).max(4),
|
|
947
|
+
aspectRatio: z13.number().positive().max(10)
|
|
948
|
+
}).strict().optional()
|
|
949
|
+
}).strict();
|
|
950
|
+
var siteFieldContractFieldSchema = z13.discriminatedUnion("fieldType", [
|
|
951
|
+
fieldBaseSchema.extend({
|
|
952
|
+
fieldType: z13.literal("text"),
|
|
953
|
+
fallback: z13.string()
|
|
954
|
+
}),
|
|
955
|
+
fieldBaseSchema.extend({
|
|
956
|
+
fieldType: z13.literal("longText"),
|
|
957
|
+
fallback: z13.string()
|
|
958
|
+
}),
|
|
959
|
+
fieldBaseSchema.extend({
|
|
960
|
+
fieldType: z13.literal("link"),
|
|
961
|
+
fallback: linkFallbackSchema2
|
|
962
|
+
}),
|
|
963
|
+
fieldBaseSchema.extend({
|
|
964
|
+
fieldType: z13.literal("image"),
|
|
965
|
+
fallback: imageFallbackSchema
|
|
966
|
+
})
|
|
967
|
+
]);
|
|
968
|
+
var siteFieldContractV1Schema = z13.object({
|
|
969
|
+
version: z13.literal(1),
|
|
970
|
+
fieldContractVersion: semanticVersionSchema,
|
|
971
|
+
scannerContractVersion: semanticVersionSchema,
|
|
972
|
+
fields: z13.array(siteFieldContractFieldSchema)
|
|
973
|
+
}).strict().superRefine((contract, context) => {
|
|
974
|
+
const seen = /* @__PURE__ */ new Set();
|
|
975
|
+
for (const [index, field] of contract.fields.entries()) {
|
|
976
|
+
if (seen.has(field.fieldId)) {
|
|
977
|
+
context.addIssue({
|
|
978
|
+
code: "custom",
|
|
979
|
+
message: `Duplicate field id: ${field.fieldId}`,
|
|
980
|
+
path: ["fields", index, "fieldId"]
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
seen.add(field.fieldId);
|
|
984
|
+
}
|
|
985
|
+
});
|
|
986
|
+
|
|
987
|
+
// ../shared/dist/site-setup.js
|
|
988
|
+
var SITE_SETUP_RUN_STATUSES = [
|
|
989
|
+
"waiting_for_agent",
|
|
990
|
+
"working",
|
|
991
|
+
"action_required",
|
|
992
|
+
"ready_for_review",
|
|
993
|
+
"completed"
|
|
994
|
+
];
|
|
995
|
+
var SITE_SETUP_RUN_MODES = ["initial", "update"];
|
|
996
|
+
var SITE_SETUP_ERROR_CODES = [
|
|
997
|
+
"action_required",
|
|
998
|
+
"deployment_evidence_required",
|
|
999
|
+
"field_contract_scan_failed",
|
|
1000
|
+
"invalid_editor_definition",
|
|
1001
|
+
"invalid_field_contract",
|
|
1002
|
+
"missing_build_revision",
|
|
1003
|
+
"missing_field_contract",
|
|
1004
|
+
"review_locked",
|
|
1005
|
+
"stale_deployment_contract",
|
|
1006
|
+
"stale_field_contract",
|
|
1007
|
+
"stale_setup_review",
|
|
1008
|
+
"update_required"
|
|
1009
|
+
];
|
|
1010
|
+
var CONTINUE_SITE_SETUP_MAX_ITEMS = 50;
|
|
1011
|
+
var CONTINUE_SITE_SETUP_RENDERED_PATH_MAX_BYTES = 512;
|
|
1012
|
+
var CONTINUE_SITE_SETUP_VISIBLE_VALUE_MAX_BYTES = 256;
|
|
1013
|
+
var CONTINUE_SITE_SETUP_LOCATOR_HINT_MAX_BYTES = 512;
|
|
1014
|
+
var CONTINUE_SITE_SETUP_ITEMS_MAX_BYTES = 32 * 1024;
|
|
1015
|
+
var CONTINUE_SITE_SETUP_HTTP_BODY_MAX_BYTES = 64 * 1024;
|
|
1016
|
+
var siteSetupRunStatusSchema = z14.enum(SITE_SETUP_RUN_STATUSES);
|
|
1017
|
+
var siteSetupRunModeSchema = z14.enum(SITE_SETUP_RUN_MODES);
|
|
1018
|
+
var siteSetupErrorCodeSchema = z14.enum(SITE_SETUP_ERROR_CODES);
|
|
1019
|
+
var editorFieldSchema = z14.object({
|
|
1020
|
+
fieldId: fieldIdSchema,
|
|
1021
|
+
label: z14.string().trim().min(1),
|
|
1022
|
+
description: z14.string().trim().min(1).optional(),
|
|
1023
|
+
required: z14.boolean().optional(),
|
|
1024
|
+
maxLength: z14.number().int().positive().optional()
|
|
1025
|
+
}).strict();
|
|
1026
|
+
var editorSectionSchema = z14.object({
|
|
1027
|
+
label: z14.string().trim().min(1),
|
|
1028
|
+
fields: z14.array(editorFieldSchema).min(1)
|
|
1029
|
+
}).strict();
|
|
1030
|
+
var editorRouteSchema = z14.object({
|
|
1031
|
+
routeKey: siteFieldRouteKeySchema,
|
|
1032
|
+
label: z14.string().trim().min(1),
|
|
1033
|
+
sections: z14.array(editorSectionSchema).min(1)
|
|
1034
|
+
}).strict().superRefine((route, context) => {
|
|
1035
|
+
const sections = /* @__PURE__ */ new Set();
|
|
1036
|
+
for (const [index, section] of route.sections.entries()) {
|
|
1037
|
+
if (sections.has(section.label)) {
|
|
1038
|
+
context.addIssue({
|
|
1039
|
+
code: "custom",
|
|
1040
|
+
message: `Duplicate section label: ${section.label}`,
|
|
1041
|
+
path: ["sections", index, "label"]
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
sections.add(section.label);
|
|
1045
|
+
}
|
|
1046
|
+
});
|
|
1047
|
+
var editorDefinitionV1Schema = z14.object({
|
|
1048
|
+
version: z14.literal(1),
|
|
1049
|
+
routes: z14.array(editorRouteSchema).min(1)
|
|
1050
|
+
}).strict().superRefine((definition, context) => {
|
|
1051
|
+
const routes = /* @__PURE__ */ new Set();
|
|
1052
|
+
const fields = /* @__PURE__ */ new Set();
|
|
1053
|
+
for (const [routeIndex, route] of definition.routes.entries()) {
|
|
1054
|
+
if (routes.has(route.routeKey)) {
|
|
1055
|
+
context.addIssue({
|
|
1056
|
+
code: "custom",
|
|
1057
|
+
message: `Duplicate route key: ${route.routeKey}`,
|
|
1058
|
+
path: ["routes", routeIndex, "routeKey"]
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
routes.add(route.routeKey);
|
|
1062
|
+
for (const [sectionIndex, section] of route.sections.entries()) {
|
|
1063
|
+
for (const [fieldIndex, field] of section.fields.entries()) {
|
|
1064
|
+
if (fields.has(field.fieldId)) {
|
|
1065
|
+
context.addIssue({
|
|
1066
|
+
code: "custom",
|
|
1067
|
+
message: `Duplicate editor field id: ${field.fieldId}`,
|
|
1068
|
+
path: [
|
|
1069
|
+
"routes",
|
|
1070
|
+
routeIndex,
|
|
1071
|
+
"sections",
|
|
1072
|
+
sectionIndex,
|
|
1073
|
+
"fields",
|
|
1074
|
+
fieldIndex,
|
|
1075
|
+
"fieldId"
|
|
1076
|
+
]
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
fields.add(field.fieldId);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
});
|
|
1084
|
+
var forbiddenControlOrBidi = /[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u;
|
|
1085
|
+
var unsafeLocator = /<\/?[a-z][^>]*>|\b(?:javascript|data:text\/html)\s*:|\bon[a-z]+\s*=/iu;
|
|
1086
|
+
var renderedPathSchema = z14.string().trim().transform((value) => value !== "/" ? value.replace(/\/+$/u, "") : value).superRefine((value, context) => {
|
|
1087
|
+
if (!value.startsWith("/") || value === "$global" || value.includes("?") || value.includes("#")) {
|
|
1088
|
+
context.addIssue({
|
|
1089
|
+
code: "custom",
|
|
1090
|
+
message: "renderedPath must be a canonical real path."
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
addSafeByteIssues(value, CONTINUE_SITE_SETUP_RENDERED_PATH_MAX_BYTES, "renderedPath", context);
|
|
1094
|
+
});
|
|
1095
|
+
function normalizedSafeTextSchema(maxBytes, label) {
|
|
1096
|
+
return z14.string().transform((value) => value.trim().replace(new RegExp("\\p{White_Space}+", "gu"), " ")).pipe(z14.string().min(1)).superRefine((value, context) => {
|
|
1097
|
+
addSafeByteIssues(value, maxBytes, label, context);
|
|
1098
|
+
if (label === "locatorHint" && unsafeLocator.test(value)) {
|
|
1099
|
+
context.addIssue({
|
|
1100
|
+
code: "custom",
|
|
1101
|
+
message: "locatorHint contains unsafe markup or executable content."
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
var correctionItemBase = {
|
|
1107
|
+
renderedPath: renderedPathSchema,
|
|
1108
|
+
visibleValue: normalizedSafeTextSchema(CONTINUE_SITE_SETUP_VISIBLE_VALUE_MAX_BYTES, "visibleValue"),
|
|
1109
|
+
locatorHint: normalizedSafeTextSchema(CONTINUE_SITE_SETUP_LOCATOR_HINT_MAX_BYTES, "locatorHint")
|
|
1110
|
+
};
|
|
1111
|
+
var setupCorrectionItemSchema = z14.discriminatedUnion("kind", [
|
|
1112
|
+
z14.object({ kind: z14.literal("text"), ...correctionItemBase }).strict(),
|
|
1113
|
+
z14.object({ kind: z14.literal("link"), ...correctionItemBase }).strict(),
|
|
1114
|
+
z14.object({ kind: z14.literal("image"), ...correctionItemBase }).strict()
|
|
1115
|
+
]);
|
|
1116
|
+
var canonicalHashSchema = z14.string().regex(/^sha256:[0-9a-f]{64}$/iu);
|
|
1117
|
+
var deploymentRevisionSchema = normalizedSafeTextSchema(256, "deploymentRevision");
|
|
1118
|
+
var continueSiteSetupInputSchema = z14.object({
|
|
1119
|
+
siteId: z14.string().uuid(),
|
|
1120
|
+
runId: z14.string().uuid(),
|
|
1121
|
+
expectedFieldContractHash: canonicalHashSchema,
|
|
1122
|
+
expectedDeploymentRevision: deploymentRevisionSchema,
|
|
1123
|
+
correctionItems: z14.array(setupCorrectionItemSchema).max(CONTINUE_SITE_SETUP_MAX_ITEMS)
|
|
1124
|
+
}).strict().superRefine((input, context) => {
|
|
1125
|
+
const bytes = utf8Bytes(stableJsonStringify(input.correctionItems));
|
|
1126
|
+
if (bytes > CONTINUE_SITE_SETUP_ITEMS_MAX_BYTES) {
|
|
1127
|
+
context.addIssue({
|
|
1128
|
+
code: "custom",
|
|
1129
|
+
message: "Correction items exceed the 32 KiB canonical payload limit.",
|
|
1130
|
+
path: ["correctionItems"]
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
});
|
|
1134
|
+
var siteSetupContextInputSchema = z14.object({}).strict();
|
|
1135
|
+
var siteSetupApplyInputSchema = z14.object({
|
|
1136
|
+
fieldContract: siteFieldContractV1Schema,
|
|
1137
|
+
editorDefinition: editorDefinitionV1Schema
|
|
1138
|
+
}).strict().superRefine((input, context) => {
|
|
1139
|
+
const contractRoutesByFieldId = new Map(input.fieldContract.fields.map((field) => [
|
|
1140
|
+
field.fieldId,
|
|
1141
|
+
field.routeKey
|
|
1142
|
+
]));
|
|
1143
|
+
const definitionFieldIds = /* @__PURE__ */ new Set();
|
|
1144
|
+
for (const [routeIndex, route] of input.editorDefinition.routes.entries()) {
|
|
1145
|
+
for (const [sectionIndex, section] of route.sections.entries()) {
|
|
1146
|
+
for (const [fieldIndex, field] of section.fields.entries()) {
|
|
1147
|
+
const fieldPath = [
|
|
1148
|
+
"editorDefinition",
|
|
1149
|
+
"routes",
|
|
1150
|
+
routeIndex,
|
|
1151
|
+
"sections",
|
|
1152
|
+
sectionIndex,
|
|
1153
|
+
"fields",
|
|
1154
|
+
fieldIndex,
|
|
1155
|
+
"fieldId"
|
|
1156
|
+
];
|
|
1157
|
+
const contractRouteKey = contractRoutesByFieldId.get(field.fieldId);
|
|
1158
|
+
const contractField = input.fieldContract.fields.find((candidate) => candidate.fieldId === field.fieldId);
|
|
1159
|
+
definitionFieldIds.add(field.fieldId);
|
|
1160
|
+
if (!contractRouteKey) {
|
|
1161
|
+
context.addIssue({
|
|
1162
|
+
code: "custom",
|
|
1163
|
+
message: `Unknown editor field id: ${field.fieldId}`,
|
|
1164
|
+
path: fieldPath
|
|
1165
|
+
});
|
|
1166
|
+
} else if (contractRouteKey !== route.routeKey) {
|
|
1167
|
+
context.addIssue({
|
|
1168
|
+
code: "custom",
|
|
1169
|
+
message: `Editor field ${field.fieldId} must use route ${contractRouteKey}.`,
|
|
1170
|
+
path: fieldPath
|
|
1171
|
+
});
|
|
1172
|
+
} else if (field.maxLength !== void 0 && contractField?.fieldType !== "text" && contractField?.fieldType !== "longText") {
|
|
1173
|
+
context.addIssue({
|
|
1174
|
+
code: "custom",
|
|
1175
|
+
message: `maxLength is not supported for ${contractField?.fieldType ?? "unknown"} fields.`,
|
|
1176
|
+
path: [
|
|
1177
|
+
"editorDefinition",
|
|
1178
|
+
"routes",
|
|
1179
|
+
routeIndex,
|
|
1180
|
+
"sections",
|
|
1181
|
+
sectionIndex,
|
|
1182
|
+
"fields",
|
|
1183
|
+
fieldIndex,
|
|
1184
|
+
"maxLength"
|
|
1185
|
+
]
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
for (const field of input.fieldContract.fields) {
|
|
1192
|
+
if (!definitionFieldIds.has(field.fieldId)) {
|
|
1193
|
+
context.addIssue({
|
|
1194
|
+
code: "custom",
|
|
1195
|
+
message: `Missing editor field id: ${field.fieldId}`,
|
|
1196
|
+
path: ["editorDefinition", "routes"]
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
});
|
|
1201
|
+
var siteSetupVerifyInputSchema = z14.object({
|
|
1202
|
+
deploymentUrl: z14.string().url().refine((value) => value.startsWith("https://")),
|
|
1203
|
+
deploymentRevision: deploymentRevisionSchema,
|
|
1204
|
+
wait: z14.boolean().optional()
|
|
1205
|
+
}).strict();
|
|
1206
|
+
var siteSetupEvidenceInputSchema = z14.object({
|
|
1207
|
+
siteId: z14.string().uuid(),
|
|
1208
|
+
runId: z14.string().uuid(),
|
|
1209
|
+
expectedFieldContractHash: canonicalHashSchema,
|
|
1210
|
+
expectedDeploymentOrigin: z14.string().url().refine((value) => value.startsWith("https://")),
|
|
1211
|
+
expectedDeploymentRevision: deploymentRevisionSchema,
|
|
1212
|
+
publicRuntimeKeyId: z14.string().uuid(),
|
|
1213
|
+
bridgeSessionId: normalizedSafeTextSchema(256, "bridgeSessionId"),
|
|
1214
|
+
observations: z14.array(z14.object({
|
|
1215
|
+
fieldId: fieldIdSchema,
|
|
1216
|
+
routeKey: siteFieldRouteKeySchema,
|
|
1217
|
+
renderedPath: renderedPathSchema,
|
|
1218
|
+
targetResolved: z14.boolean(),
|
|
1219
|
+
previewAck: z14.boolean()
|
|
1220
|
+
}).strict().refine((value) => !value.previewAck || value.targetResolved, "Preview ACK requires a resolved target.")).min(1).max(200)
|
|
1221
|
+
}).strict();
|
|
1222
|
+
var completeSiteSetupInputSchema = z14.object({
|
|
1223
|
+
siteId: z14.string().uuid(),
|
|
1224
|
+
runId: z14.string().uuid(),
|
|
1225
|
+
expectedFieldContractHash: canonicalHashSchema,
|
|
1226
|
+
expectedDeploymentRevision: deploymentRevisionSchema,
|
|
1227
|
+
accessMode: z14.enum(["fallback", "custom_domain"]),
|
|
1228
|
+
siteDomainId: z14.string().uuid().optional(),
|
|
1229
|
+
defaultCanPublish: z14.boolean()
|
|
1230
|
+
}).strict().superRefine((input, context) => {
|
|
1231
|
+
if (input.accessMode === "fallback" && input.siteDomainId) {
|
|
1232
|
+
context.addIssue({
|
|
1233
|
+
code: "custom",
|
|
1234
|
+
message: "Fallback access cannot use a custom domain.",
|
|
1235
|
+
path: ["siteDomainId"]
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
if (input.accessMode === "custom_domain" && !input.siteDomainId) {
|
|
1239
|
+
context.addIssue({
|
|
1240
|
+
code: "custom",
|
|
1241
|
+
message: "Custom-domain access requires a site domain.",
|
|
1242
|
+
path: ["siteDomainId"]
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
});
|
|
1246
|
+
var refreshSiteSetupDeploymentInputSchema = z14.object({
|
|
1247
|
+
siteId: z14.string().uuid(),
|
|
1248
|
+
deploymentUrl: z14.string().url().refine((value) => value.startsWith("https://")),
|
|
1249
|
+
deploymentRevision: deploymentRevisionSchema
|
|
1250
|
+
}).strict();
|
|
1251
|
+
function addSafeByteIssues(value, maxBytes, label, context) {
|
|
1252
|
+
if (utf8Bytes(value) > maxBytes) {
|
|
1253
|
+
context.addIssue({
|
|
1254
|
+
code: "custom",
|
|
1255
|
+
message: `${label} exceeds ${maxBytes} UTF-8 bytes.`
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
if (forbiddenControlOrBidi.test(value)) {
|
|
1259
|
+
context.addIssue({
|
|
1260
|
+
code: "custom",
|
|
1261
|
+
message: `${label} contains a forbidden control or bidi character.`
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
function utf8Bytes(value) {
|
|
1266
|
+
return new TextEncoder().encode(value).byteLength;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
// ../shared/dist/bridge-protocol.js
|
|
1270
|
+
var BRIDGE_PROTOCOL_VERSION = 1;
|
|
1271
|
+
var bridgeRectSchema = z15.object({
|
|
1272
|
+
x: z15.number(),
|
|
1273
|
+
y: z15.number(),
|
|
1274
|
+
width: z15.number().nonnegative(),
|
|
1275
|
+
height: z15.number().nonnegative()
|
|
1276
|
+
});
|
|
1277
|
+
var previewTextValueSchema = z15.object({
|
|
1278
|
+
fieldType: z15.enum(["text", "longText"]),
|
|
1279
|
+
value: z15.string()
|
|
1280
|
+
});
|
|
1281
|
+
var previewLinkValueSchema = z15.object({
|
|
1282
|
+
fieldType: z15.literal("link"),
|
|
1283
|
+
value: z15.object({
|
|
1284
|
+
href: z15.string().trim().min(1),
|
|
1285
|
+
label: z15.string().trim().min(1).optional()
|
|
1286
|
+
})
|
|
1287
|
+
});
|
|
1288
|
+
var previewImageValueSchema = z15.object({
|
|
1289
|
+
fieldType: z15.literal("image"),
|
|
1290
|
+
value: imageValueSchema
|
|
1291
|
+
});
|
|
1292
|
+
var bridgePreviewValueSchema = z15.discriminatedUnion("fieldType", [
|
|
1293
|
+
previewTextValueSchema,
|
|
1294
|
+
previewLinkValueSchema,
|
|
1295
|
+
previewImageValueSchema
|
|
1296
|
+
]);
|
|
1297
|
+
var FIELD_BRIDGE_MESSAGE_TYPES = [
|
|
1298
|
+
"siteplane:field-bridge:init",
|
|
1299
|
+
"siteplane:field-bridge:ready",
|
|
1300
|
+
"siteplane:field-bridge:fields",
|
|
1301
|
+
"siteplane:field-bridge:select-field",
|
|
1302
|
+
"siteplane:field-bridge:set-selected-field",
|
|
1303
|
+
"siteplane:field-bridge:apply-preview-values",
|
|
1304
|
+
"siteplane:field-bridge:test-preview-values",
|
|
1305
|
+
"siteplane:field-bridge:preview-ack",
|
|
1306
|
+
"siteplane:field-bridge:set-editability-review",
|
|
1307
|
+
"siteplane:field-bridge:missing-content",
|
|
1308
|
+
"siteplane:field-bridge:error"
|
|
1309
|
+
];
|
|
1310
|
+
var canonicalFieldContractHashSchema = z15.string().regex(/^sha256:[0-9a-f]{64}$/u);
|
|
1311
|
+
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.");
|
|
1312
|
+
var fieldBridgeIdentitySchema = z15.object({
|
|
1313
|
+
publicRuntimeKeyId: z15.string().uuid(),
|
|
1314
|
+
fieldContractHash: canonicalFieldContractHashSchema,
|
|
1315
|
+
deploymentRevision: z15.string().trim().min(1).max(256)
|
|
1316
|
+
}).strict();
|
|
1317
|
+
var fieldBridgeBaseSchema = z15.object({
|
|
1318
|
+
version: z15.literal(BRIDGE_PROTOCOL_VERSION),
|
|
1319
|
+
type: z15.enum(FIELD_BRIDGE_MESSAGE_TYPES),
|
|
1320
|
+
siteId: z15.string().uuid(),
|
|
1321
|
+
sessionId: z15.string().trim().min(1),
|
|
1322
|
+
publicRuntimeKeyId: z15.string().uuid(),
|
|
1323
|
+
fieldContractHash: canonicalFieldContractHashSchema,
|
|
1324
|
+
deploymentRevision: z15.string().trim().min(1).max(256),
|
|
1325
|
+
requestId: z15.string().trim().min(1),
|
|
1326
|
+
sentAt: z15.string().datetime()
|
|
1327
|
+
}).strict();
|
|
1328
|
+
var fieldBridgeFieldSchema = z15.object({
|
|
1329
|
+
fieldId: fieldIdSchema,
|
|
1330
|
+
fieldType: fieldTypeSchema,
|
|
1331
|
+
routeKey: siteFieldRouteKeySchema,
|
|
1332
|
+
renderedPath: realRenderedPathSchema,
|
|
1333
|
+
sourcePath: z15.string().trim().min(1).optional(),
|
|
1334
|
+
editTarget: fieldIdSchema,
|
|
1335
|
+
rect: bridgeRectSchema,
|
|
1336
|
+
targetResolved: z15.literal(true),
|
|
1337
|
+
currentValue: bridgePreviewValueSchema.optional()
|
|
1338
|
+
}).strict();
|
|
1339
|
+
var fieldBridgeInitMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1340
|
+
type: z15.literal("siteplane:field-bridge:init"),
|
|
1341
|
+
payload: z15.object({
|
|
1342
|
+
adminOrigin: z15.string().url(),
|
|
1343
|
+
customerOrigin: z15.string().url(),
|
|
1344
|
+
renderedPath: realRenderedPathSchema
|
|
1345
|
+
}).strict()
|
|
1346
|
+
});
|
|
1347
|
+
var fieldBridgeReadyMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1348
|
+
type: z15.literal("siteplane:field-bridge:ready"),
|
|
1349
|
+
payload: z15.object({
|
|
1350
|
+
renderedPath: realRenderedPathSchema,
|
|
1351
|
+
fieldCount: z15.number().int().nonnegative(),
|
|
1352
|
+
capabilities: z15.tuple([
|
|
1353
|
+
z15.literal("field_selection"),
|
|
1354
|
+
z15.literal("preview_values"),
|
|
1355
|
+
z15.literal("field_evidence"),
|
|
1356
|
+
z15.literal("editability_review")
|
|
1357
|
+
])
|
|
1358
|
+
}).strict()
|
|
1359
|
+
});
|
|
1360
|
+
var fieldBridgeFieldsMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1361
|
+
type: z15.literal("siteplane:field-bridge:fields"),
|
|
1362
|
+
payload: z15.object({
|
|
1363
|
+
renderedPath: realRenderedPathSchema,
|
|
1364
|
+
fields: z15.array(fieldBridgeFieldSchema)
|
|
1365
|
+
}).strict()
|
|
1366
|
+
});
|
|
1367
|
+
var fieldBridgeSelectFieldMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1368
|
+
type: z15.literal("siteplane:field-bridge:select-field"),
|
|
1369
|
+
payload: z15.object({ fieldId: fieldIdSchema }).strict()
|
|
1370
|
+
});
|
|
1371
|
+
var fieldBridgeSetSelectedFieldMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1372
|
+
type: z15.literal("siteplane:field-bridge:set-selected-field"),
|
|
1373
|
+
payload: z15.object({
|
|
1374
|
+
fieldId: fieldIdSchema.nullable(),
|
|
1375
|
+
scrollIntoView: z15.boolean().optional()
|
|
1376
|
+
}).strict()
|
|
1377
|
+
});
|
|
1378
|
+
var fieldBridgeApplyPreviewValuesMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1379
|
+
type: z15.literal("siteplane:field-bridge:apply-preview-values"),
|
|
1380
|
+
payload: z15.object({ values: z15.record(fieldIdSchema, bridgePreviewValueSchema) }).strict()
|
|
1381
|
+
});
|
|
1382
|
+
var fieldBridgeTestPreviewValuesMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1383
|
+
type: z15.literal("siteplane:field-bridge:test-preview-values"),
|
|
1384
|
+
payload: z15.object({ fieldIds: z15.array(fieldIdSchema).min(1).max(200) }).strict()
|
|
1385
|
+
});
|
|
1386
|
+
var fieldBridgePreviewAckMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1387
|
+
type: z15.literal("siteplane:field-bridge:preview-ack"),
|
|
1388
|
+
payload: z15.object({
|
|
1389
|
+
ackRequestId: z15.string().trim().min(1),
|
|
1390
|
+
kind: z15.enum(["apply", "test"]),
|
|
1391
|
+
renderedPath: realRenderedPathSchema,
|
|
1392
|
+
fieldIds: z15.array(fieldIdSchema).max(200)
|
|
1393
|
+
}).strict()
|
|
1394
|
+
});
|
|
1395
|
+
var fieldBridgeSetEditabilityReviewMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1396
|
+
type: z15.literal("siteplane:field-bridge:set-editability-review"),
|
|
1397
|
+
payload: z15.object({ enabled: z15.boolean() }).strict()
|
|
1398
|
+
});
|
|
1399
|
+
var fieldBridgeMissingContentMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1400
|
+
type: z15.literal("siteplane:field-bridge:missing-content"),
|
|
1401
|
+
payload: z15.object({ item: setupCorrectionItemSchema }).strict()
|
|
1402
|
+
});
|
|
1403
|
+
var fieldBridgeErrorMessageSchema = fieldBridgeBaseSchema.extend({
|
|
1404
|
+
type: z15.literal("siteplane:field-bridge:error"),
|
|
1405
|
+
payload: z15.object({
|
|
1406
|
+
code: z15.string().trim().min(1),
|
|
1407
|
+
message: z15.string().trim().min(1),
|
|
1408
|
+
recoverable: z15.boolean().default(false)
|
|
1409
|
+
}).strict()
|
|
1410
|
+
});
|
|
1411
|
+
var fieldBridgeMessageSchema = z15.discriminatedUnion("type", [
|
|
1412
|
+
fieldBridgeInitMessageSchema,
|
|
1413
|
+
fieldBridgeReadyMessageSchema,
|
|
1414
|
+
fieldBridgeFieldsMessageSchema,
|
|
1415
|
+
fieldBridgeSelectFieldMessageSchema,
|
|
1416
|
+
fieldBridgeSetSelectedFieldMessageSchema,
|
|
1417
|
+
fieldBridgeApplyPreviewValuesMessageSchema,
|
|
1418
|
+
fieldBridgeTestPreviewValuesMessageSchema,
|
|
1419
|
+
fieldBridgePreviewAckMessageSchema,
|
|
1420
|
+
fieldBridgeSetEditabilityReviewMessageSchema,
|
|
1421
|
+
fieldBridgeMissingContentMessageSchema,
|
|
1422
|
+
fieldBridgeErrorMessageSchema
|
|
1423
|
+
]);
|
|
1424
|
+
|
|
1425
|
+
// ../shared/dist/commands.js
|
|
1426
|
+
import { z as z17 } from "zod";
|
|
1427
|
+
|
|
1428
|
+
// ../shared/dist/errors.js
|
|
1429
|
+
import { z as z16 } from "zod";
|
|
1430
|
+
var UI_ERROR_CATEGORIES = [
|
|
1431
|
+
"client",
|
|
1432
|
+
"developer",
|
|
1433
|
+
"internal"
|
|
1434
|
+
];
|
|
1435
|
+
var uiErrorCategorySchema = z16.enum(UI_ERROR_CATEGORIES);
|
|
1436
|
+
var commandErrorPayloadSchema = z16.object({
|
|
1437
|
+
code: z16.string().min(1),
|
|
1438
|
+
message: z16.string().min(1),
|
|
1439
|
+
category: uiErrorCategorySchema,
|
|
1440
|
+
details: z16.record(z16.string(), z16.unknown()).optional()
|
|
1441
|
+
});
|
|
1442
|
+
|
|
1443
|
+
// ../shared/dist/commands.js
|
|
1444
|
+
var commandSuccessSchema = z17.object({
|
|
1445
|
+
ok: z17.literal(true),
|
|
1446
|
+
data: z17.unknown()
|
|
1447
|
+
});
|
|
1448
|
+
var commandErrorSchema = z17.object({
|
|
1449
|
+
ok: z17.literal(false),
|
|
1450
|
+
error: commandErrorPayloadSchema
|
|
1451
|
+
});
|
|
1452
|
+
var commandResultSchema = z17.discriminatedUnion("ok", [
|
|
1453
|
+
commandSuccessSchema,
|
|
1454
|
+
commandErrorSchema
|
|
1455
|
+
]);
|
|
1456
|
+
|
|
1457
|
+
// ../shared/dist/fallback-portal.js
|
|
1458
|
+
import { z as z18 } from "zod";
|
|
1459
|
+
var FALLBACK_PORTAL_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
1460
|
+
var fallbackPortalSlugSchema = z18.string().regex(FALLBACK_PORTAL_SLUG_PATTERN, "Invalid fallback portal slug");
|
|
1461
|
+
|
|
1462
|
+
// ../shared/dist/entitlements.js
|
|
1463
|
+
import { z as z19 } from "zod";
|
|
1464
|
+
var entitlementSnapshotSchema = z19.object({
|
|
1465
|
+
planKey: z19.string().trim().min(1),
|
|
1466
|
+
sitesLimit: z19.number().int().positive(),
|
|
1467
|
+
clientAccountsPerSiteLimit: z19.number().int().positive(),
|
|
1468
|
+
customAdminDomainsEnabled: z19.boolean(),
|
|
1469
|
+
agentMcpEnabled: z19.boolean(),
|
|
1470
|
+
emailNotificationsEnabled: z19.boolean(),
|
|
1471
|
+
analyticsEnabled: z19.boolean(),
|
|
1472
|
+
analyticsSitesLimit: z19.number().int().min(0),
|
|
1473
|
+
analyticsClientPerformanceEnabled: z19.boolean(),
|
|
1474
|
+
analyticsMonthlyReportsEnabled: z19.boolean(),
|
|
1475
|
+
analyticsAiSummaryEnabled: z19.boolean(),
|
|
1476
|
+
analyticsRawEventRetentionDays: z19.number().int().min(1).max(ANALYTICS_RETENTION_DEFAULTS.maxRawEventRetentionDaysWithoutAdr),
|
|
1477
|
+
analyticsMonthlyEventLimit: z19.number().int().min(0),
|
|
1478
|
+
bookingEnabled: z19.boolean().default(false),
|
|
1479
|
+
bookingSitesLimit: z19.number().int().min(0).default(0),
|
|
1480
|
+
bookingClientManagementEnabled: z19.boolean().default(false),
|
|
1481
|
+
bookingResourcesPerSiteLimit: z19.number().int().min(0).default(0),
|
|
1482
|
+
bookingServicesPerSiteLimit: z19.number().int().min(0).default(0),
|
|
1483
|
+
bookingMonthlyBookingsLimit: z19.number().int().min(0).default(0),
|
|
1484
|
+
bookingRemindersEnabled: z19.boolean().default(false)
|
|
1485
|
+
});
|
|
1486
|
+
var EARLY_ACCESS_PLAN = {
|
|
1487
|
+
planKey: "early_access_2026",
|
|
1488
|
+
sitesLimit: 3,
|
|
1489
|
+
clientAccountsPerSiteLimit: 5,
|
|
1490
|
+
customAdminDomainsEnabled: true,
|
|
1491
|
+
agentMcpEnabled: true,
|
|
1492
|
+
emailNotificationsEnabled: true,
|
|
1493
|
+
analyticsEnabled: false,
|
|
1494
|
+
analyticsSitesLimit: 0,
|
|
1495
|
+
analyticsClientPerformanceEnabled: false,
|
|
1496
|
+
analyticsMonthlyReportsEnabled: false,
|
|
1497
|
+
analyticsAiSummaryEnabled: false,
|
|
1498
|
+
analyticsRawEventRetentionDays: ANALYTICS_RETENTION_DEFAULTS.enabledRawEventRetentionDays,
|
|
1499
|
+
analyticsMonthlyEventLimit: 0,
|
|
1500
|
+
bookingEnabled: true,
|
|
1501
|
+
bookingSitesLimit: 3,
|
|
1502
|
+
bookingClientManagementEnabled: true,
|
|
1503
|
+
bookingResourcesPerSiteLimit: 10,
|
|
1504
|
+
bookingServicesPerSiteLimit: 50,
|
|
1505
|
+
bookingMonthlyBookingsLimit: 0,
|
|
1506
|
+
bookingRemindersEnabled: true
|
|
1507
|
+
};
|
|
1508
|
+
|
|
1509
|
+
// ../shared/dist/readiness.js
|
|
1510
|
+
import { z as z20 } from "zod";
|
|
1511
|
+
var READINESS_CHECK_KEYS = [
|
|
1512
|
+
"agent_instructions_installed",
|
|
1513
|
+
"runtime_package_connected",
|
|
1514
|
+
"secure_server_access_configured",
|
|
1515
|
+
"preview_bridge_working",
|
|
1516
|
+
"admin_access_url_selected",
|
|
1517
|
+
"default_publishing_mode_selected"
|
|
1518
|
+
];
|
|
1519
|
+
var READINESS_STATUSES = [
|
|
1520
|
+
"pending",
|
|
1521
|
+
"passing",
|
|
1522
|
+
"failing",
|
|
1523
|
+
"warning"
|
|
1524
|
+
];
|
|
1525
|
+
var readinessCheckKeySchema = z20.enum(READINESS_CHECK_KEYS);
|
|
1526
|
+
var readinessStatusSchema = z20.enum(READINESS_STATUSES);
|
|
1527
|
+
|
|
1528
|
+
// ../shared/dist/site.js
|
|
1529
|
+
import { z as z21 } from "zod";
|
|
1530
|
+
var SITE_STATUSES = [
|
|
1531
|
+
"setup",
|
|
1532
|
+
"client_ready",
|
|
1533
|
+
"disabled",
|
|
1534
|
+
"archived"
|
|
1535
|
+
];
|
|
1536
|
+
var siteStatusSchema = z21.enum(SITE_STATUSES);
|
|
1537
|
+
var websiteUrlSchema = z21.string().trim().url().refine((value) => value.startsWith("https://") || value.startsWith("http://"), "Website URL must use http or https");
|
|
1538
|
+
|
|
1539
|
+
// ../cli/dist/analytics/static-scan.js
|
|
1540
|
+
import ts from "typescript";
|
|
1541
|
+
|
|
1542
|
+
// ../cli/dist/booking/manifest.js
|
|
1543
|
+
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
1544
|
+
import { dirname as dirname2 } from "path";
|
|
1545
|
+
async function readBookingManifestFile(manifestPath) {
|
|
1546
|
+
return validateBookingManifestInput(JSON.parse(await readFile2(manifestPath, "utf8")));
|
|
1547
|
+
}
|
|
1548
|
+
async function writeBookingManifestFile(manifestPath, manifest) {
|
|
1549
|
+
await mkdir2(dirname2(manifestPath), {
|
|
1550
|
+
recursive: true
|
|
1551
|
+
});
|
|
1552
|
+
await writeFile2(manifestPath, `${JSON.stringify(bookingManifestSchema.parse(manifest), null, 2)}
|
|
1553
|
+
`, "utf8");
|
|
1554
|
+
}
|
|
1555
|
+
function validateBookingManifestInput(input) {
|
|
1556
|
+
const redactedKeys = collectRedactedBookingKeys(input);
|
|
1557
|
+
const redacted = redactBookingManifest(input);
|
|
1558
|
+
return {
|
|
1559
|
+
manifest: bookingManifestSchema.parse(redacted),
|
|
1560
|
+
redactedKeys: [...redactedKeys].sort()
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
var secretManifestKeys2 = /* @__PURE__ */ new Set([
|
|
1564
|
+
"siteKey",
|
|
1565
|
+
"rawKey",
|
|
1566
|
+
"token",
|
|
1567
|
+
"accessToken"
|
|
1568
|
+
]);
|
|
1569
|
+
function collectRedactedBookingKeys(input, prefix = "") {
|
|
1570
|
+
if (Array.isArray(input)) {
|
|
1571
|
+
return input.flatMap((item, index) => collectRedactedBookingKeys(item, `${prefix}[${index}]`));
|
|
1572
|
+
}
|
|
1573
|
+
if (!input || typeof input !== "object") {
|
|
1574
|
+
return [];
|
|
1575
|
+
}
|
|
1576
|
+
return Object.entries(input).flatMap(([key, value]) => {
|
|
1577
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
1578
|
+
if (secretManifestKeys2.has(key)) {
|
|
1579
|
+
return [path];
|
|
1580
|
+
}
|
|
1581
|
+
return collectRedactedBookingKeys(value, path);
|
|
1582
|
+
});
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
// ../cli/dist/booking/static-scan.js
|
|
1586
|
+
function scanBookingSourceFiles(files, input) {
|
|
1587
|
+
const embeds = files.flatMap(scanBookingEmbedFile);
|
|
1588
|
+
const findings = embeds.map(createEmbedFoundIssue);
|
|
1589
|
+
const hardErrors = embeds.length === 0 ? [
|
|
1590
|
+
{
|
|
1591
|
+
code: "booking_scan.embed_missing",
|
|
1592
|
+
message: "No Siteplane Booking embed script, web component or iframe was found.",
|
|
1593
|
+
filePath: "siteplane.booking.json"
|
|
1594
|
+
}
|
|
1595
|
+
] : [];
|
|
1596
|
+
const warnings = [
|
|
1597
|
+
...createPageSlugWarnings(embeds, input.bookingPageSlug),
|
|
1598
|
+
...createOriginWarnings(files, input.productionOrigin)
|
|
1599
|
+
];
|
|
1600
|
+
const selectedEmbed = selectManifestEmbed(embeds);
|
|
1601
|
+
const manifest = bookingManifestSchema.parse({
|
|
1602
|
+
schemaVersion: 1,
|
|
1603
|
+
siteId: input.siteId,
|
|
1604
|
+
siteKeyPrefix: input.siteKeyPrefix,
|
|
1605
|
+
bookingPageSlug: selectedEmbed?.pageSlug ?? input.bookingPageSlug,
|
|
1606
|
+
...selectedEmbed ? { embedMode: selectedEmbed.embedMode } : {},
|
|
1607
|
+
...input.productionOrigin ? { productionOrigin: input.productionOrigin } : {}
|
|
1608
|
+
});
|
|
1609
|
+
return {
|
|
1610
|
+
manifest,
|
|
1611
|
+
findings,
|
|
1612
|
+
hardErrors,
|
|
1613
|
+
warnings,
|
|
1614
|
+
suggestions: []
|
|
1615
|
+
};
|
|
1616
|
+
}
|
|
1617
|
+
function scanBookingEmbedFile(file) {
|
|
1618
|
+
const embeds = [];
|
|
1619
|
+
const source = file.sourceText;
|
|
1620
|
+
if (/\/embed\/booking\.js/u.test(source)) {
|
|
1621
|
+
embeds.push({
|
|
1622
|
+
filePath: file.filePath,
|
|
1623
|
+
embedMode: "script"
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
if (/<siteplane-booking\b/u.test(source)) {
|
|
1627
|
+
const pageSlug = readAttributeValue(source, "booking-page-slug");
|
|
1628
|
+
embeds.push({
|
|
1629
|
+
filePath: file.filePath,
|
|
1630
|
+
embedMode: "web_component",
|
|
1631
|
+
...pageSlug ? { pageSlug } : {}
|
|
1632
|
+
});
|
|
1633
|
+
}
|
|
1634
|
+
for (const match of source.matchAll(/<iframe\b[^>]*>/giu)) {
|
|
1635
|
+
const tag = match[0];
|
|
1636
|
+
const src = readAttributeValue(tag, "src");
|
|
1637
|
+
const pageSlug = src ? readBookingPageSlug(src) : void 0;
|
|
1638
|
+
if (/data-siteplane-booking/u.test(tag) || pageSlug !== void 0) {
|
|
1639
|
+
embeds.push({
|
|
1640
|
+
filePath: file.filePath,
|
|
1641
|
+
embedMode: "iframe",
|
|
1642
|
+
...pageSlug ? { pageSlug } : {}
|
|
1643
|
+
});
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
return embeds;
|
|
1647
|
+
}
|
|
1648
|
+
function createEmbedFoundIssue(embed) {
|
|
1649
|
+
return {
|
|
1650
|
+
code: "booking_scan.embed_found",
|
|
1651
|
+
message: "Siteplane Booking embed was found.",
|
|
1652
|
+
filePath: embed.filePath,
|
|
1653
|
+
embedMode: embed.embedMode,
|
|
1654
|
+
...embed.pageSlug ? { pageSlug: embed.pageSlug } : {}
|
|
1655
|
+
};
|
|
1656
|
+
}
|
|
1657
|
+
function createPageSlugWarnings(embeds, expectedSlug) {
|
|
1658
|
+
return embeds.filter((embed) => embed.pageSlug && embed.pageSlug !== expectedSlug).map((embed) => {
|
|
1659
|
+
const pageSlug = embed.pageSlug;
|
|
1660
|
+
return {
|
|
1661
|
+
code: "booking_scan.page_slug_mismatch",
|
|
1662
|
+
message: `Booking embed references page slug ${pageSlug}, but the manifest is configured for ${expectedSlug}.`,
|
|
1663
|
+
filePath: embed.filePath,
|
|
1664
|
+
embedMode: embed.embedMode,
|
|
1665
|
+
...pageSlug ? { pageSlug } : {}
|
|
1666
|
+
};
|
|
1667
|
+
});
|
|
1668
|
+
}
|
|
1669
|
+
function createOriginWarnings(files, productionOrigin) {
|
|
1670
|
+
if (!productionOrigin) {
|
|
1671
|
+
return [];
|
|
1672
|
+
}
|
|
1673
|
+
if (files.some((file) => file.sourceText.includes(productionOrigin))) {
|
|
1674
|
+
return [];
|
|
1675
|
+
}
|
|
1676
|
+
return [
|
|
1677
|
+
{
|
|
1678
|
+
code: "booking_scan.origin_not_detected",
|
|
1679
|
+
message: "The configured production origin was not found statically; verify it is registered as an allowed Booking origin in Siteplane.",
|
|
1680
|
+
filePath: "siteplane.booking.json"
|
|
1681
|
+
}
|
|
1682
|
+
];
|
|
1683
|
+
}
|
|
1684
|
+
function selectManifestEmbed(embeds) {
|
|
1685
|
+
return embeds.find((embed) => embed.embedMode === "web_component") ?? embeds.find((embed) => embed.embedMode === "iframe") ?? embeds[0];
|
|
1686
|
+
}
|
|
1687
|
+
function readAttributeValue(source, attributeName) {
|
|
1688
|
+
const escapedName = escapeRegExp(attributeName);
|
|
1689
|
+
const match = new RegExp(`${escapedName}\\s*=\\s*["']([^"']+)["']`, "iu").exec(source);
|
|
1690
|
+
return match?.[1];
|
|
1691
|
+
}
|
|
1692
|
+
function readBookingPageSlug(value) {
|
|
1693
|
+
return /\/book\/([a-z0-9]+(?:-[a-z0-9]+)*)(?:[/?#]|$)/u.exec(value)?.[1];
|
|
1694
|
+
}
|
|
1695
|
+
function escapeRegExp(value) {
|
|
1696
|
+
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
// ../cli/dist/commands/analytics/check.js
|
|
1700
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
1701
|
+
|
|
1702
|
+
// ../cli/dist/commands/analytics/init.js
|
|
1703
|
+
import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
|
|
1704
|
+
import { dirname as dirname3, join } from "path";
|
|
1705
|
+
var analyticsRulesTemplate = `# Siteplane Analytics Rules
|
|
1706
|
+
|
|
1707
|
+
Analytics contract version: ${ANALYTICS_CONTRACT_VERSION}
|
|
1708
|
+
Analytics contract hash: ${ANALYTICS_CONTRACT_HASH}
|
|
1709
|
+
|
|
1710
|
+
- Do not change visible layout, copy, styling, animation or business logic while adding analytics.
|
|
1711
|
+
- Use stable data-siteplane-target-id values that do not depend on visible copy.
|
|
1712
|
+
- Use public site keys only through environment references.
|
|
1713
|
+
- Do not write secret analytics keys, setup tokens or project CLI tokens into source files.
|
|
1714
|
+
- Suppress editor-preview traffic from live analytics.
|
|
1715
|
+
- Run npx siteplane analytics check, npx siteplane analytics sync and npx siteplane analytics test after setup.
|
|
1716
|
+
`;
|
|
1717
|
+
var analyticsCursorRulesTemplate = `---
|
|
1718
|
+
description: Siteplane Analytics rules
|
|
1719
|
+
alwaysApply: true
|
|
1720
|
+
---
|
|
1721
|
+
|
|
1722
|
+
${analyticsRulesTemplate}`;
|
|
1723
|
+
|
|
1724
|
+
// ../cli/dist/commands/analytics/import.js
|
|
1725
|
+
import { createHash } from "crypto";
|
|
1726
|
+
|
|
1727
|
+
// ../cli/dist/commands/analytics/public-key.js
|
|
1728
|
+
import { z as z22 } from "zod";
|
|
1729
|
+
var responseSchema = z22.object({
|
|
1730
|
+
ok: z22.literal(true),
|
|
1731
|
+
data: z22.object({
|
|
1732
|
+
rawPublicKey: z22.string().regex(/^pk_siteplane_[A-Za-z0-9_-]{16}_[A-Za-z0-9_-]{43}$/)
|
|
1733
|
+
})
|
|
1734
|
+
});
|
|
1735
|
+
var errorResponseSchema = z22.object({
|
|
1736
|
+
error: z22.object({
|
|
1737
|
+
code: z22.string().regex(/^[a-z][a-z0-9_.-]+$/u)
|
|
1738
|
+
})
|
|
1739
|
+
});
|
|
1740
|
+
|
|
1741
|
+
// ../cli/dist/commands/analytics/test.js
|
|
1742
|
+
import { randomUUID } from "crypto";
|
|
1743
|
+
|
|
1744
|
+
// ../cli/dist/commands/booking/check.js
|
|
1745
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
1746
|
+
async function bookingCheckCommand(input) {
|
|
1747
|
+
const validatedManifest = await readBookingManifestFile(input.manifestPath);
|
|
1748
|
+
const sourceFiles = await readSourceFiles(input.sourceFilePaths);
|
|
1749
|
+
const scanInput = {
|
|
1750
|
+
siteId: validatedManifest.manifest.siteId,
|
|
1751
|
+
siteKeyPrefix: validatedManifest.manifest.siteKeyPrefix,
|
|
1752
|
+
bookingPageSlug: validatedManifest.manifest.bookingPageSlug,
|
|
1753
|
+
...validatedManifest.manifest.productionOrigin ? { productionOrigin: validatedManifest.manifest.productionOrigin } : {}
|
|
1754
|
+
};
|
|
1755
|
+
const scanResult = scanBookingSourceFiles(sourceFiles, {
|
|
1756
|
+
...scanInput
|
|
1757
|
+
});
|
|
1758
|
+
const hardErrors = [
|
|
1759
|
+
...createBoundaryErrors(validatedManifest),
|
|
1760
|
+
...scanResult.hardErrors
|
|
1761
|
+
];
|
|
1762
|
+
return {
|
|
1763
|
+
ok: hardErrors.length === 0,
|
|
1764
|
+
manifest: validatedManifest.manifest,
|
|
1765
|
+
scannedManifest: scanResult.manifest,
|
|
1766
|
+
redactedKeys: validatedManifest.redactedKeys,
|
|
1767
|
+
hardErrors,
|
|
1768
|
+
warnings: scanResult.warnings,
|
|
1769
|
+
suggestions: scanResult.suggestions,
|
|
1770
|
+
findings: scanResult.findings
|
|
1771
|
+
};
|
|
1772
|
+
}
|
|
1773
|
+
async function readSourceFiles(filePaths) {
|
|
1774
|
+
return Promise.all(filePaths.map(async (filePath) => ({
|
|
1775
|
+
filePath,
|
|
1776
|
+
sourceText: await readFile4(filePath, "utf8")
|
|
1777
|
+
})));
|
|
1778
|
+
}
|
|
1779
|
+
function createBoundaryErrors(validation) {
|
|
1780
|
+
return validation.redactedKeys.map((key) => ({
|
|
1781
|
+
code: "booking_check.raw_key_in_manifest",
|
|
1782
|
+
message: `Booking manifest contains a raw key-like field: ${key}`,
|
|
1783
|
+
filePath: "siteplane.booking.json"
|
|
1784
|
+
}));
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
// ../cli/dist/commands/booking/init.js
|
|
1788
|
+
import { mkdir as mkdir4, writeFile as writeFile4 } from "fs/promises";
|
|
1789
|
+
import { dirname as dirname4, join as join2 } from "path";
|
|
1790
|
+
async function bookingInitCommand(input) {
|
|
1791
|
+
const manifest = {
|
|
1792
|
+
schemaVersion: 1,
|
|
1793
|
+
siteId: input.siteId,
|
|
1794
|
+
siteKeyPrefix: input.siteKeyPrefix,
|
|
1795
|
+
bookingPageSlug: input.bookingPageSlug,
|
|
1796
|
+
...input.productionOrigin ? { productionOrigin: input.productionOrigin } : {}
|
|
1797
|
+
};
|
|
1798
|
+
const manifestPath = join2(input.projectDir, "siteplane.booking.json");
|
|
1799
|
+
const artifacts = createBookingArtifacts(input.agentClient);
|
|
1800
|
+
await writeBookingManifestFile(manifestPath, manifest);
|
|
1801
|
+
for (const artifact of artifacts) {
|
|
1802
|
+
await writeProjectFile(input.projectDir, artifact.path, artifact.template);
|
|
1803
|
+
}
|
|
1804
|
+
return {
|
|
1805
|
+
manifestPath,
|
|
1806
|
+
manifest,
|
|
1807
|
+
artifactPaths: artifacts.map((artifact) => artifact.path)
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
function createBookingArtifacts(agentClient) {
|
|
1811
|
+
const artifacts = [
|
|
1812
|
+
{
|
|
1813
|
+
path: ".siteplane/booking.md",
|
|
1814
|
+
template: bookingRulesTemplate
|
|
1815
|
+
}
|
|
1816
|
+
];
|
|
1817
|
+
if (agentClient === "codex") {
|
|
1818
|
+
artifacts.push({
|
|
1819
|
+
path: ".agents/skills/siteplane-booking/SKILL.md",
|
|
1820
|
+
template: bookingSkillTemplate("Codex")
|
|
1821
|
+
});
|
|
1822
|
+
}
|
|
1823
|
+
if (agentClient === "claude") {
|
|
1824
|
+
artifacts.push({
|
|
1825
|
+
path: ".claude/skills/siteplane-booking/SKILL.md",
|
|
1826
|
+
template: bookingSkillTemplate("Claude Code")
|
|
1827
|
+
});
|
|
1828
|
+
}
|
|
1829
|
+
if (agentClient === "cursor") {
|
|
1830
|
+
artifacts.push({
|
|
1831
|
+
path: ".cursor/rules/siteplane-booking.mdc",
|
|
1832
|
+
template: bookingCursorRulesTemplate
|
|
1833
|
+
});
|
|
1834
|
+
}
|
|
1835
|
+
return artifacts;
|
|
1836
|
+
}
|
|
1837
|
+
async function writeProjectFile(projectDir, relativePath, contents) {
|
|
1838
|
+
const path = join2(projectDir, relativePath);
|
|
1839
|
+
await mkdir4(dirname4(path), { recursive: true });
|
|
1840
|
+
await writeFile4(path, contents, "utf8");
|
|
1841
|
+
}
|
|
1842
|
+
var bookingRulesTemplate = `# Siteplane Booking Rules
|
|
1843
|
+
|
|
1844
|
+
- Do not change layout, copy, styling, animation or business logic while adding Booking.
|
|
1845
|
+
- Add only the Siteplane Booking embed script and a booking iframe or web component.
|
|
1846
|
+
- Use the configured booking page slug from siteplane.booking.json.
|
|
1847
|
+
- Register the production website origin in Siteplane before testing cross-origin embeds.
|
|
1848
|
+
- Do not write setup tokens, project CLI tokens or private keys into source files.
|
|
1849
|
+
- Run npx siteplane booking check, npx siteplane booking sync and npx siteplane booking test after setup.
|
|
1850
|
+
- booking test stays in test_mode; live booking only starts after explicit dashboard activation.
|
|
1851
|
+
`;
|
|
1852
|
+
function bookingSkillTemplate(agentName) {
|
|
1853
|
+
return `# Siteplane Booking Skill
|
|
1854
|
+
|
|
1855
|
+
Use this skill in ${agentName} before adding Siteplane Booking embeds.
|
|
1856
|
+
|
|
1857
|
+
${bookingRulesTemplate}`;
|
|
1858
|
+
}
|
|
1859
|
+
var bookingCursorRulesTemplate = `---
|
|
1860
|
+
description: Siteplane Booking rules
|
|
1861
|
+
alwaysApply: true
|
|
1862
|
+
---
|
|
1863
|
+
|
|
1864
|
+
${bookingRulesTemplate}`;
|
|
1865
|
+
|
|
1866
|
+
// ../cli/dist/commands/booking/sync.js
|
|
1867
|
+
async function bookingSyncCommand(input) {
|
|
1868
|
+
const redacted = validateBookingManifestInput(input.manifest);
|
|
1869
|
+
const fetcher = input.fetchImpl ?? fetch;
|
|
1870
|
+
return fetcher(`${input.config.apiBaseUrl}/api/cli/booking/sync`, {
|
|
1871
|
+
method: "POST",
|
|
1872
|
+
headers: createBookingCliRequestHeaders(input.config),
|
|
1873
|
+
body: JSON.stringify({
|
|
1874
|
+
siteId: input.config.siteId,
|
|
1875
|
+
source: "cli",
|
|
1876
|
+
manifest: redacted.manifest
|
|
1877
|
+
})
|
|
1878
|
+
});
|
|
1879
|
+
}
|
|
1880
|
+
function createBookingCliRequestHeaders(config) {
|
|
1881
|
+
const headers = {
|
|
1882
|
+
authorization: `Bearer ${config.accessToken}`,
|
|
1883
|
+
"content-type": "application/json"
|
|
1884
|
+
};
|
|
1885
|
+
const bypassSecret = config.vercelAutomationBypassSecret?.trim();
|
|
1886
|
+
if (bypassSecret) {
|
|
1887
|
+
headers["x-vercel-protection-bypass"] = bypassSecret;
|
|
1888
|
+
}
|
|
1889
|
+
return headers;
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
// ../cli/dist/commands/booking/test.js
|
|
1893
|
+
async function bookingTestCommand(input) {
|
|
1894
|
+
const fetcher = input.fetchImpl ?? fetch;
|
|
1895
|
+
const booking = bookingCreateRequestSchema.parse(input.booking);
|
|
1896
|
+
return fetcher(`${input.config.apiBaseUrl}/api/cli/booking/test`, {
|
|
1897
|
+
method: "POST",
|
|
1898
|
+
headers: createBookingCliRequestHeaders(input.config),
|
|
1899
|
+
body: JSON.stringify({
|
|
1900
|
+
siteId: input.config.siteId,
|
|
1901
|
+
mode: "test",
|
|
1902
|
+
...input.manifest ? { manifest: input.manifest } : {},
|
|
1903
|
+
booking
|
|
1904
|
+
})
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
// ../cli/dist/commands/export.js
|
|
1909
|
+
async function exportCommand(input) {
|
|
1910
|
+
requireContentExportScope(input.tokenScopes);
|
|
1911
|
+
const values = await input.readPublishedValues();
|
|
1912
|
+
if (input.format === "prompt") {
|
|
1913
|
+
return {
|
|
1914
|
+
kind: "prompt",
|
|
1915
|
+
output: Object.entries(values).map(([fieldId, value]) => `${fieldId}: ${JSON.stringify(value)}`).join("\n")
|
|
1916
|
+
};
|
|
1917
|
+
}
|
|
1918
|
+
return {
|
|
1919
|
+
kind: "json",
|
|
1920
|
+
values
|
|
1921
|
+
};
|
|
1922
|
+
}
|
|
1923
|
+
function requireContentExportScope(scopes) {
|
|
1924
|
+
if (!scopes?.includes("content:export")) {
|
|
1925
|
+
throw new Error("A Project Connection with content:export is required.");
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
// ../cli/dist/config/project-config.js
|
|
1930
|
+
import { access, chmod, open, readFile as readFile5, rename, rm } from "fs/promises";
|
|
1931
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
1932
|
+
import { join as join3 } from "path";
|
|
1933
|
+
import { z as z23 } from "zod";
|
|
1934
|
+
var siteplaneProjectConfigSchema = z23.object({
|
|
1935
|
+
version: z23.literal(1),
|
|
1936
|
+
apiBaseUrl: z23.string().url(),
|
|
1937
|
+
siteId: z23.string().uuid(),
|
|
1938
|
+
publicSiteKeyId: z23.string().uuid(),
|
|
1939
|
+
publicSiteKey: z23.string().trim().min(1),
|
|
1940
|
+
fieldContractHash: z23.string().regex(/^sha256:[0-9a-f]{64}$/iu).optional()
|
|
1941
|
+
}).strict();
|
|
1942
|
+
async function readProjectPackageJson(projectDir) {
|
|
1943
|
+
return JSON.parse(await readFile5(join3(projectDir, "package.json"), "utf8"));
|
|
1944
|
+
}
|
|
1945
|
+
async function detectNextProject(projectDir) {
|
|
1946
|
+
const packageJson = await readProjectPackageJson(projectDir).catch(() => null);
|
|
1947
|
+
if (packageJson?.dependencies?.next || packageJson?.devDependencies?.next) {
|
|
1948
|
+
return true;
|
|
1949
|
+
}
|
|
1950
|
+
for (const fileName of [
|
|
1951
|
+
"next.config.js",
|
|
1952
|
+
"next.config.mjs",
|
|
1953
|
+
"next.config.ts"
|
|
1954
|
+
]) {
|
|
1955
|
+
try {
|
|
1956
|
+
await access(join3(projectDir, fileName));
|
|
1957
|
+
return true;
|
|
1958
|
+
} catch {
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
return false;
|
|
1962
|
+
}
|
|
1963
|
+
async function writeProjectConfig(projectDir, config) {
|
|
1964
|
+
const path = join3(projectDir, "siteplane.config.json");
|
|
1965
|
+
const parsed = siteplaneProjectConfigSchema.parse(config);
|
|
1966
|
+
await writeJsonAtomically(path, parsed, 420);
|
|
1967
|
+
return path;
|
|
1968
|
+
}
|
|
1969
|
+
async function readProjectConfig(projectDir) {
|
|
1970
|
+
return siteplaneProjectConfigSchema.parse(JSON.parse(await readFile5(join3(projectDir, "siteplane.config.json"), "utf8")));
|
|
1971
|
+
}
|
|
1972
|
+
async function writeJsonAtomically(path, value, mode) {
|
|
1973
|
+
const temporaryPath = `${path}.${randomUUID2()}.tmp`;
|
|
1974
|
+
const file = await open(temporaryPath, "wx", mode);
|
|
1975
|
+
try {
|
|
1976
|
+
await file.writeFile(`${JSON.stringify(value, null, 2)}
|
|
1977
|
+
`, "utf8");
|
|
1978
|
+
await file.sync();
|
|
1979
|
+
} catch (error) {
|
|
1980
|
+
await file.close();
|
|
1981
|
+
await rm(temporaryPath, { force: true });
|
|
1982
|
+
throw error;
|
|
1983
|
+
}
|
|
1984
|
+
await file.close();
|
|
1985
|
+
try {
|
|
1986
|
+
await rename(temporaryPath, path);
|
|
1987
|
+
await chmod(path, mode);
|
|
1988
|
+
} catch (error) {
|
|
1989
|
+
await rm(temporaryPath, { force: true });
|
|
1990
|
+
throw error;
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
// ../cli/dist/config/credentials.js
|
|
1995
|
+
import { execFile } from "child_process";
|
|
1996
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
1997
|
+
import { chmod as chmod2, mkdir as mkdir5, open as open2, readFile as readFile6, rename as rename2, rm as rm2 } from "fs/promises";
|
|
1998
|
+
import { dirname as dirname5, join as join4, relative } from "path";
|
|
1999
|
+
import { promisify } from "util";
|
|
2000
|
+
import { z as z24 } from "zod";
|
|
2001
|
+
var siteplaneCredentialsSchema = z24.object({
|
|
2002
|
+
accessToken: z24.string().trim().min(1),
|
|
2003
|
+
siteRevalidationSecret: z24.string().trim().min(1)
|
|
2004
|
+
}).strict();
|
|
2005
|
+
async function writeLocalCredentials(projectDir, credentials, options = {}) {
|
|
2006
|
+
const path = join4(projectDir, ".siteplane/credentials.json");
|
|
2007
|
+
const isTrackedFile = options.isTrackedFile ?? ((targetPath) => gitPathMatches(projectDir, targetPath, "ls-files"));
|
|
2008
|
+
const isIgnoredFile = options.isIgnoredFile ?? ((targetPath) => gitPathMatches(projectDir, targetPath, "check-ignore"));
|
|
2009
|
+
if (await isTrackedFile(path)) {
|
|
2010
|
+
throw new Error("Refusing to write Siteplane credentials into a tracked file.");
|
|
2011
|
+
}
|
|
2012
|
+
if (!await isIgnoredFile(path)) {
|
|
2013
|
+
throw new Error("Add .siteplane/credentials.json to .gitignore before running siteplane init.");
|
|
2014
|
+
}
|
|
2015
|
+
const parsed = siteplaneCredentialsSchema.parse(credentials);
|
|
2016
|
+
await mkdir5(dirname5(path), { recursive: true, mode: 448 });
|
|
2017
|
+
await writeCredentialsAtomically(path, parsed);
|
|
2018
|
+
return path;
|
|
2019
|
+
}
|
|
2020
|
+
async function readLocalCredentials(projectDir) {
|
|
2021
|
+
return siteplaneCredentialsSchema.parse(JSON.parse(await readFile6(join4(projectDir, ".siteplane/credentials.json"), "utf8")));
|
|
2022
|
+
}
|
|
2023
|
+
async function writeCredentialsAtomically(path, credentials) {
|
|
2024
|
+
const temporaryPath = `${path}.${randomUUID3()}.tmp`;
|
|
2025
|
+
const file = await open2(temporaryPath, "wx", 384);
|
|
2026
|
+
try {
|
|
2027
|
+
await file.writeFile(`${JSON.stringify(credentials, null, 2)}
|
|
2028
|
+
`, "utf8");
|
|
2029
|
+
await file.sync();
|
|
2030
|
+
} catch (error) {
|
|
2031
|
+
await file.close();
|
|
2032
|
+
await rm2(temporaryPath, { force: true });
|
|
2033
|
+
throw error;
|
|
2034
|
+
}
|
|
2035
|
+
await file.close();
|
|
2036
|
+
try {
|
|
2037
|
+
await rename2(temporaryPath, path);
|
|
2038
|
+
await chmod2(path, 384);
|
|
2039
|
+
} catch (error) {
|
|
2040
|
+
await rm2(temporaryPath, { force: true });
|
|
2041
|
+
throw error;
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
var execFileAsync = promisify(execFile);
|
|
2045
|
+
async function gitPathMatches(projectDir, path, command) {
|
|
2046
|
+
const args = command === "check-ignore" ? ["check-ignore", "--quiet", relative(projectDir, path)] : ["ls-files", "--error-unmatch", relative(projectDir, path)];
|
|
2047
|
+
try {
|
|
2048
|
+
await execFileAsync("git", args, { cwd: projectDir });
|
|
2049
|
+
return true;
|
|
2050
|
+
} catch {
|
|
2051
|
+
return false;
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
// ../cli/dist/commands/init.js
|
|
2056
|
+
import { join as join5 } from "path";
|
|
2057
|
+
import { z as z25 } from "zod";
|
|
2058
|
+
async function initCommand(input) {
|
|
2059
|
+
if (!input.setupToken.trim()) {
|
|
2060
|
+
throw new Error("A Siteplane setup token is required.");
|
|
2061
|
+
}
|
|
2062
|
+
if (!await detectNextProject(input.projectDir)) {
|
|
2063
|
+
throw new Error("Siteplane init currently supports Next.js projects.");
|
|
2064
|
+
}
|
|
2065
|
+
const bootstrap = await bootstrapProjectConnection(input);
|
|
2066
|
+
const paths = bootstrap.credentialPurpose === "site_setup" ? await persistSiteSetupBootstrap(input, bootstrap) : await persistFeatureBootstrap(input, bootstrap);
|
|
2067
|
+
await acknowledgeBootstrapResponse(input);
|
|
2068
|
+
return {
|
|
2069
|
+
...paths,
|
|
2070
|
+
nextSteps: [
|
|
2071
|
+
"npx siteplane setup context",
|
|
2072
|
+
"Instrument the site and create EditorDefinitionV1.",
|
|
2073
|
+
"npx siteplane setup apply --definition /tmp/siteplane-editor.json"
|
|
2074
|
+
]
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
var siteSetupBootstrapDataSchema = z25.object({
|
|
2078
|
+
credentialPurpose: z25.literal("site_setup"),
|
|
2079
|
+
apiBaseUrl: z25.string().url(),
|
|
2080
|
+
siteId: z25.string().uuid(),
|
|
2081
|
+
publicSiteKeyId: z25.string().uuid(),
|
|
2082
|
+
publicSiteKey: z25.string().trim().min(1),
|
|
2083
|
+
accessToken: z25.string().trim().min(1),
|
|
2084
|
+
siteRevalidationSecret: z25.string().regex(/^[A-Za-z0-9_-]{43}$/u)
|
|
2085
|
+
}).strict();
|
|
2086
|
+
var featureBootstrapDataSchema = z25.object({
|
|
2087
|
+
credentialPurpose: z25.enum(["analytics", "booking"]),
|
|
2088
|
+
apiBaseUrl: z25.string().url(),
|
|
2089
|
+
siteId: z25.string().uuid(),
|
|
2090
|
+
accessToken: z25.string().trim().min(1)
|
|
2091
|
+
}).strict();
|
|
2092
|
+
var bootstrapResponseSchema = z25.object({
|
|
2093
|
+
ok: z25.literal(true),
|
|
2094
|
+
data: z25.discriminatedUnion("credentialPurpose", [
|
|
2095
|
+
siteSetupBootstrapDataSchema,
|
|
2096
|
+
featureBootstrapDataSchema
|
|
2097
|
+
])
|
|
2098
|
+
}).strict();
|
|
2099
|
+
async function bootstrapProjectConnection(input) {
|
|
2100
|
+
const fetcher = input.fetcher ?? fetch;
|
|
2101
|
+
const response = await fetcher(`${input.apiBaseUrl.replace(/\/$/, "")}/api/cli/setup/bootstrap`, {
|
|
2102
|
+
method: "POST",
|
|
2103
|
+
headers: createBootstrapHeaders(input),
|
|
2104
|
+
body: JSON.stringify({
|
|
2105
|
+
action: "bootstrap",
|
|
2106
|
+
agentClient: input.agentClient ?? "codex"
|
|
2107
|
+
})
|
|
2108
|
+
});
|
|
2109
|
+
const payload = await response.json();
|
|
2110
|
+
const parsed = bootstrapResponseSchema.safeParse(payload);
|
|
2111
|
+
if (!response.ok || !parsed.success) {
|
|
2112
|
+
throw new Error(readErrorMessage(payload) ?? "Siteplane init failed.");
|
|
2113
|
+
}
|
|
2114
|
+
return parsed.data.data;
|
|
2115
|
+
}
|
|
2116
|
+
async function acknowledgeBootstrapResponse(input) {
|
|
2117
|
+
const fetcher = input.fetcher ?? fetch;
|
|
2118
|
+
const response = await fetcher(`${input.apiBaseUrl.replace(/\/$/, "")}/api/cli/setup/bootstrap`, {
|
|
2119
|
+
method: "POST",
|
|
2120
|
+
headers: createBootstrapHeaders(input),
|
|
2121
|
+
body: JSON.stringify({ action: "acknowledge" })
|
|
2122
|
+
});
|
|
2123
|
+
if (!response.ok) {
|
|
2124
|
+
throw new Error("Siteplane setup acknowledgement failed.");
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
async function persistSiteSetupBootstrap(input, bootstrap) {
|
|
2128
|
+
const credentialsPath = await writeLocalCredentials(input.projectDir, {
|
|
2129
|
+
accessToken: bootstrap.accessToken,
|
|
2130
|
+
siteRevalidationSecret: bootstrap.siteRevalidationSecret
|
|
2131
|
+
}, {
|
|
2132
|
+
...input.isIgnoredFile ? { isIgnoredFile: input.isIgnoredFile } : {},
|
|
2133
|
+
...input.isTrackedFile ? { isTrackedFile: input.isTrackedFile } : {}
|
|
2134
|
+
});
|
|
2135
|
+
const configPath = await writeProjectConfig(input.projectDir, {
|
|
2136
|
+
version: 1,
|
|
2137
|
+
apiBaseUrl: bootstrap.apiBaseUrl,
|
|
2138
|
+
siteId: bootstrap.siteId,
|
|
2139
|
+
publicSiteKeyId: bootstrap.publicSiteKeyId,
|
|
2140
|
+
publicSiteKey: bootstrap.publicSiteKey
|
|
2141
|
+
});
|
|
2142
|
+
const [credentials, config] = await Promise.all([
|
|
2143
|
+
readLocalCredentials(input.projectDir),
|
|
2144
|
+
readProjectConfig(input.projectDir)
|
|
2145
|
+
]);
|
|
2146
|
+
if (credentials.accessToken !== bootstrap.accessToken || credentials.siteRevalidationSecret !== bootstrap.siteRevalidationSecret || config.apiBaseUrl !== bootstrap.apiBaseUrl || config.siteId !== bootstrap.siteId || config.publicSiteKeyId !== bootstrap.publicSiteKeyId || config.publicSiteKey !== bootstrap.publicSiteKey) {
|
|
2147
|
+
throw new Error("Siteplane setup files could not be verified.");
|
|
2148
|
+
}
|
|
2149
|
+
return { configPath, credentialsPath };
|
|
2150
|
+
}
|
|
2151
|
+
async function persistFeatureBootstrap(input, bootstrap) {
|
|
2152
|
+
const [config, existingCredentials] = await Promise.all([
|
|
2153
|
+
readProjectConfig(input.projectDir),
|
|
2154
|
+
readLocalCredentials(input.projectDir)
|
|
2155
|
+
]);
|
|
2156
|
+
if (config.siteId !== bootstrap.siteId) {
|
|
2157
|
+
throw new Error("The setup token belongs to a different Siteplane site.");
|
|
2158
|
+
}
|
|
2159
|
+
const credentialsPath = await writeLocalCredentials(input.projectDir, {
|
|
2160
|
+
accessToken: bootstrap.accessToken,
|
|
2161
|
+
siteRevalidationSecret: existingCredentials.siteRevalidationSecret
|
|
2162
|
+
}, {
|
|
2163
|
+
...input.isIgnoredFile ? { isIgnoredFile: input.isIgnoredFile } : {},
|
|
2164
|
+
...input.isTrackedFile ? { isTrackedFile: input.isTrackedFile } : {}
|
|
2165
|
+
});
|
|
2166
|
+
const persistedCredentials = await readLocalCredentials(input.projectDir);
|
|
2167
|
+
if (persistedCredentials.accessToken !== bootstrap.accessToken || persistedCredentials.siteRevalidationSecret !== existingCredentials.siteRevalidationSecret) {
|
|
2168
|
+
throw new Error("Siteplane credentials could not be verified.");
|
|
2169
|
+
}
|
|
2170
|
+
return {
|
|
2171
|
+
configPath: join5(input.projectDir, "siteplane.config.json"),
|
|
2172
|
+
credentialsPath
|
|
2173
|
+
};
|
|
2174
|
+
}
|
|
2175
|
+
function createBootstrapHeaders(input) {
|
|
2176
|
+
return new Headers({
|
|
2177
|
+
authorization: `Bearer ${input.setupToken}`,
|
|
2178
|
+
"content-type": "application/json",
|
|
2179
|
+
...input.vercelAutomationBypassSecret?.trim() ? {
|
|
2180
|
+
"x-vercel-protection-bypass": input.vercelAutomationBypassSecret.trim()
|
|
2181
|
+
} : {}
|
|
2182
|
+
});
|
|
2183
|
+
}
|
|
2184
|
+
function readErrorMessage(payload) {
|
|
2185
|
+
if (payload && typeof payload === "object" && "error" in payload && payload.error && typeof payload.error === "object" && "message" in payload.error && typeof payload.error.message === "string") {
|
|
2186
|
+
const code = "code" in payload.error && typeof payload.error.code === "string" ? payload.error.code : null;
|
|
2187
|
+
return code ? `${code}: ${payload.error.message}` : payload.error.message;
|
|
2188
|
+
}
|
|
2189
|
+
return null;
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
// ../cli/dist/commands/scan.js
|
|
2193
|
+
import { readdir } from "fs/promises";
|
|
2194
|
+
import { extname, join as join6 } from "path";
|
|
2195
|
+
|
|
2196
|
+
// ../cli/dist/commands/site-setup.js
|
|
2197
|
+
import { execFile as execFile2 } from "child_process";
|
|
2198
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
2199
|
+
import { promisify as promisify2 } from "util";
|
|
2200
|
+
import { z as z26 } from "zod";
|
|
2201
|
+
|
|
2202
|
+
// ../cli/dist/scan/next-source-scan.js
|
|
2203
|
+
import ts2 from "typescript";
|
|
2204
|
+
import { isAbsolute, relative as relative2 } from "path";
|
|
2205
|
+
var legacyEditableComponentPrefix = "Editable";
|
|
2206
|
+
var editableComponentNames = new Set(["Image", "Text", "LongText", "Link"].map((name) => `${legacyEditableComponentPrefix}${name}`));
|
|
2207
|
+
|
|
2208
|
+
// ../cli/dist/commands/site-setup.js
|
|
2209
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
2210
|
+
var safeErrorSchema = z26.object({
|
|
2211
|
+
code: z26.string().trim().min(1),
|
|
2212
|
+
message: z26.string().trim().min(1)
|
|
2213
|
+
}).passthrough();
|
|
2214
|
+
var errorResponseSchema2 = z26.object({ ok: z26.literal(false), error: safeErrorSchema }).passthrough();
|
|
2215
|
+
var contextDataSchema = z26.object({
|
|
2216
|
+
site: z26.object({ siteId: z26.string().uuid(), websiteUrl: z26.string().url() }),
|
|
2217
|
+
run: z26.object({
|
|
2218
|
+
runId: z26.string().uuid(),
|
|
2219
|
+
mode: z26.enum(["initial", "update"]),
|
|
2220
|
+
status: z26.enum([
|
|
2221
|
+
"waiting_for_agent",
|
|
2222
|
+
"working",
|
|
2223
|
+
"action_required",
|
|
2224
|
+
"ready_for_review"
|
|
2225
|
+
]),
|
|
2226
|
+
fieldContractHash: z26.string().nullable(),
|
|
2227
|
+
deploymentOrigin: z26.string().nullable(),
|
|
2228
|
+
deploymentRevision: z26.string().nullable(),
|
|
2229
|
+
errorCode: z26.string().nullable(),
|
|
2230
|
+
errorMessage: z26.string().nullable()
|
|
2231
|
+
}),
|
|
2232
|
+
allowedTasks: z26.array(z26.string()),
|
|
2233
|
+
requiredEnvironmentNames: z26.array(z26.string())
|
|
2234
|
+
}).strict();
|
|
2235
|
+
var contextResponseSchema = z26.object({ ok: z26.literal(true), data: contextDataSchema }).strict();
|
|
2236
|
+
var applyDataSchema = z26.object({
|
|
2237
|
+
status: z26.enum(["applied", "ready_for_review"]),
|
|
2238
|
+
runId: z26.string().uuid().optional(),
|
|
2239
|
+
contractVersionId: z26.string().uuid().optional(),
|
|
2240
|
+
fieldContractHash: z26.string().regex(/^sha256:[0-9a-f]{64}$/u),
|
|
2241
|
+
editorDefinitionHash: z26.string().regex(/^sha256:[0-9a-f]{64}$/u).optional(),
|
|
2242
|
+
fieldCount: z26.number().int().nonnegative().optional(),
|
|
2243
|
+
routeCount: z26.number().int().nonnegative().optional()
|
|
2244
|
+
}).strict();
|
|
2245
|
+
var applyResponseSchema = z26.object({ ok: z26.literal(true), data: applyDataSchema }).strict();
|
|
2246
|
+
var verifyDataSchema = z26.object({
|
|
2247
|
+
status: z26.literal("ready_for_review"),
|
|
2248
|
+
runId: z26.string().uuid().optional(),
|
|
2249
|
+
deploymentOrigin: z26.string().url().optional(),
|
|
2250
|
+
deploymentRevision: z26.string().optional()
|
|
2251
|
+
}).strict();
|
|
2252
|
+
var verifyResponseSchema = z26.object({ ok: z26.literal(true), data: verifyDataSchema }).strict();
|
|
2253
|
+
export {
|
|
2254
|
+
bookingCheckCommand,
|
|
2255
|
+
bookingInitCommand,
|
|
2256
|
+
bookingSyncCommand,
|
|
2257
|
+
bookingTestCommand,
|
|
2258
|
+
exportCommand,
|
|
2259
|
+
initCommand
|
|
2260
|
+
};
|