@kubuild/schema 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/dist/actions.d.ts +5 -2
- package/dist/actions.d.ts.map +1 -1
- package/dist/breakpoints.d.ts +35 -0
- package/dist/breakpoints.d.ts.map +1 -0
- package/dist/builtin-components.d.ts +17 -0
- package/dist/builtin-components.d.ts.map +1 -0
- package/dist/canonical-props.d.ts +39 -0
- package/dist/canonical-props.d.ts.map +1 -0
- package/dist/chunk-MT3M7D67.js +1041 -0
- package/dist/chunk-MT3M7D67.js.map +1 -0
- package/dist/chunk-PECTZDCI.js +1038 -0
- package/dist/chunk-PECTZDCI.js.map +1 -0
- package/dist/document.d.ts +21 -1
- package/dist/document.d.ts.map +1 -1
- package/dist/index.cjs +437 -126
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +316 -819
- package/dist/index.js.map +1 -1
- package/dist/json-schema.d.ts +74 -6
- package/dist/json-schema.d.ts.map +1 -1
- package/dist/template.d.ts +6 -0
- package/dist/template.d.ts.map +1 -1
- package/dist/theme.d.ts +80 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/types.cjs +19 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.ts +331 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/dist/validate.cjs +715 -0
- package/dist/validate.cjs.map +1 -0
- package/dist/validate.d.ts +16 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +53 -0
- package/dist/validate.js.map +1 -0
- package/package.json +22 -2
|
@@ -0,0 +1,1041 @@
|
|
|
1
|
+
// src/tracking.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var TrackingProviderTypeSchema = z.enum([
|
|
4
|
+
"meta",
|
|
5
|
+
"google",
|
|
6
|
+
"gtm",
|
|
7
|
+
"tiktok",
|
|
8
|
+
"custom"
|
|
9
|
+
]);
|
|
10
|
+
var TrackingDeliverySchema = z.enum(["both", "client_only", "server_only"]);
|
|
11
|
+
var MetaTrackingProviderConfigSchema = z.object({
|
|
12
|
+
enabled: z.boolean().default(true),
|
|
13
|
+
/** Host credential id; the server resolves the CAPI access token from it. */
|
|
14
|
+
credentialId: z.string().optional(),
|
|
15
|
+
/** Meta Pixel ID (e.g. 123456789012345) — public */
|
|
16
|
+
pixelId: z.string().optional().default(""),
|
|
17
|
+
/** Enable Server-Side Conversions API (delivered through the host relay) */
|
|
18
|
+
capiEnabled: z.boolean().optional().default(false),
|
|
19
|
+
/** Test Event Code from Meta Events Manager (e.g. "TEST12345") — not a secret */
|
|
20
|
+
testEventCode: z.string().optional().default("")
|
|
21
|
+
});
|
|
22
|
+
var GoogleTrackingProviderConfigSchema = z.object({
|
|
23
|
+
enabled: z.boolean().default(true),
|
|
24
|
+
/** Host credential id; the server resolves the Measurement Protocol API secret from it. */
|
|
25
|
+
credentialId: z.string().optional(),
|
|
26
|
+
/** GA4 Measurement ID (e.g. "G-XXXXXXXXXX") — public */
|
|
27
|
+
measurementId: z.string().optional().default("")
|
|
28
|
+
});
|
|
29
|
+
var GtmTrackingProviderConfigSchema = z.object({
|
|
30
|
+
enabled: z.boolean().default(true),
|
|
31
|
+
/** Optional linked account/workspace credential ID */
|
|
32
|
+
credentialId: z.string().optional(),
|
|
33
|
+
/** GTM Container ID (e.g. "GTM-XXXXXXX") — public */
|
|
34
|
+
containerId: z.string().optional().default("")
|
|
35
|
+
});
|
|
36
|
+
var TikTokTrackingProviderConfigSchema = z.object({
|
|
37
|
+
enabled: z.boolean().default(true),
|
|
38
|
+
/** Host credential id; the server resolves the Events API access token from it. */
|
|
39
|
+
credentialId: z.string().optional(),
|
|
40
|
+
/** TikTok Pixel ID (e.g. "C1234567890") — public */
|
|
41
|
+
pixelId: z.string().optional().default(""),
|
|
42
|
+
/** Enable TikTok Events API (server-side, delivered through the host relay) */
|
|
43
|
+
eventsApiEnabled: z.boolean().optional().default(false),
|
|
44
|
+
/** Test Event Code from TikTok Event Manager — not a secret */
|
|
45
|
+
testEventCode: z.string().optional().default("")
|
|
46
|
+
});
|
|
47
|
+
var CustomTrackingProviderConfigSchema = z.object({
|
|
48
|
+
enabled: z.boolean().default(true),
|
|
49
|
+
/** Host credential id; the server resolves the webhook `endpointUrl` + `headers` from it. */
|
|
50
|
+
credentialId: z.string().optional()
|
|
51
|
+
});
|
|
52
|
+
var LEGACY_TRACKING_SECRET_KEYS = Object.freeze({
|
|
53
|
+
meta: ["capiAccessToken", "serverRelayUrl"],
|
|
54
|
+
google: ["measurementProtocolSecret", "serverRelayUrl"],
|
|
55
|
+
gtm: ["serverRelayUrl"],
|
|
56
|
+
tiktok: ["accessToken", "serverRelayUrl"],
|
|
57
|
+
custom: ["endpointUrl", "headers"]
|
|
58
|
+
});
|
|
59
|
+
var TrackingConfigSchema = z.object({
|
|
60
|
+
/** Master toggle: dynamically enables or disables all tracking operations */
|
|
61
|
+
enabled: z.boolean().default(true),
|
|
62
|
+
/** When enabled, events are logged to console and can bypass live API calls for safe debugging */
|
|
63
|
+
debugMode: z.boolean().optional().default(false),
|
|
64
|
+
/** Automatically trigger PageView event upon page load */
|
|
65
|
+
autoPageView: z.boolean().optional().default(true),
|
|
66
|
+
/** Default delivery method when not explicitly overridden in an action step */
|
|
67
|
+
defaultDelivery: TrackingDeliverySchema.optional().default("both"),
|
|
68
|
+
/** Provider-specific settings */
|
|
69
|
+
providers: z.object({
|
|
70
|
+
meta: MetaTrackingProviderConfigSchema.optional(),
|
|
71
|
+
google: GoogleTrackingProviderConfigSchema.optional(),
|
|
72
|
+
gtm: GtmTrackingProviderConfigSchema.optional(),
|
|
73
|
+
tiktok: TikTokTrackingProviderConfigSchema.optional(),
|
|
74
|
+
custom: CustomTrackingProviderConfigSchema.optional()
|
|
75
|
+
}).optional().default({})
|
|
76
|
+
});
|
|
77
|
+
var MetaTrackingSecretsSchema = z.object({
|
|
78
|
+
/** System User access token for the Meta Graph API. */
|
|
79
|
+
capiAccessToken: z.string().min(1)
|
|
80
|
+
});
|
|
81
|
+
var GoogleTrackingSecretsSchema = z.object({
|
|
82
|
+
/** Measurement Protocol `api_secret`. */
|
|
83
|
+
measurementProtocolSecret: z.string().min(1)
|
|
84
|
+
});
|
|
85
|
+
var TikTokTrackingSecretsSchema = z.object({
|
|
86
|
+
/** Long-term Events API access token. */
|
|
87
|
+
accessToken: z.string().min(1)
|
|
88
|
+
});
|
|
89
|
+
var CustomTrackingSecretsSchema = z.object({
|
|
90
|
+
endpointUrl: z.string().url(),
|
|
91
|
+
headers: z.record(z.string(), z.string()).optional()
|
|
92
|
+
});
|
|
93
|
+
var StandardTrackingEventSchema = z.enum([
|
|
94
|
+
"PageView",
|
|
95
|
+
"ViewContent",
|
|
96
|
+
"AddToCart",
|
|
97
|
+
"InitiateCheckout",
|
|
98
|
+
"Purchase",
|
|
99
|
+
"Lead",
|
|
100
|
+
"Contact",
|
|
101
|
+
"CompleteRegistration",
|
|
102
|
+
"Subscribe",
|
|
103
|
+
"Search",
|
|
104
|
+
"SubmitApplication",
|
|
105
|
+
"Schedule"
|
|
106
|
+
]);
|
|
107
|
+
var TrackEventProviderSchema = z.enum(["all", "meta", "google", "gtm", "tiktok", "custom"]);
|
|
108
|
+
var TrackEventStepPayloadSchema = z.object({
|
|
109
|
+
/** Event name: e.g. "Purchase", "Lead", or custom string */
|
|
110
|
+
eventName: z.string().min(1, "Event name cannot be empty"),
|
|
111
|
+
/** Standard event vs custom event identifier */
|
|
112
|
+
eventType: z.enum(["standard", "custom"]).optional().default("standard"),
|
|
113
|
+
/**
|
|
114
|
+
* Target provider: 'all' or a specific provider.
|
|
115
|
+
* 'gtm' pushes the event to `window.dataLayer` (client-side only).
|
|
116
|
+
*/
|
|
117
|
+
provider: TrackEventProviderSchema.optional().default("all"),
|
|
118
|
+
/** Delivery channel: 'both', 'client_only', or 'server_only' */
|
|
119
|
+
delivery: TrackingDeliverySchema.optional().default("both"),
|
|
120
|
+
/**
|
|
121
|
+
* Deduplication Event ID.
|
|
122
|
+
* If omitted, a unique ID is automatically generated so Meta/TikTok CAPI
|
|
123
|
+
* deduplicates cleanly with the browser pixel.
|
|
124
|
+
*/
|
|
125
|
+
eventId: z.string().optional(),
|
|
126
|
+
/**
|
|
127
|
+
* Event parameters/properties (e.g. currency, value, content_name, order_id).
|
|
128
|
+
* Supports runtime variable interpolation (e.g. {{ form.price }} or {{ variables.currency }}).
|
|
129
|
+
*/
|
|
130
|
+
params: z.record(z.string(), z.unknown()).optional().default({}),
|
|
131
|
+
/**
|
|
132
|
+
* User Data for advanced matching and server CAPI (e.g. email, phone, firstName, lastName).
|
|
133
|
+
* Supports runtime variable interpolation (e.g. {{ form.email }}).
|
|
134
|
+
*/
|
|
135
|
+
userData: z.record(z.string(), z.unknown()).optional().default({}),
|
|
136
|
+
/** Step-level active toggle */
|
|
137
|
+
enabled: z.boolean().optional().default(true)
|
|
138
|
+
});
|
|
139
|
+
var TRACKING_RELAY_PROTOCOL_VERSION = 1;
|
|
140
|
+
var ServerTrackingEventSchema = z.object({
|
|
141
|
+
eventName: z.string().min(1).max(256),
|
|
142
|
+
eventType: z.enum(["standard", "custom"]).optional(),
|
|
143
|
+
eventId: z.string().max(256).optional(),
|
|
144
|
+
/** Unix epoch seconds. */
|
|
145
|
+
eventTime: z.number().int().nonnegative().optional(),
|
|
146
|
+
eventSourceUrl: z.string().max(4096).optional(),
|
|
147
|
+
actionSource: z.enum(["website", "app", "system_generated"]).optional(),
|
|
148
|
+
params: z.record(z.string(), z.unknown()).optional(),
|
|
149
|
+
/** Raw (unhashed) user data; the relay hashes PII per provider spec before forwarding. */
|
|
150
|
+
userData: z.record(z.string(), z.unknown()).optional(),
|
|
151
|
+
customData: z.record(z.string(), z.unknown()).optional()
|
|
152
|
+
});
|
|
153
|
+
var TrackingRelayRequestSchema = z.object({
|
|
154
|
+
version: z.literal(TRACKING_RELAY_PROTOCOL_VERSION),
|
|
155
|
+
event: ServerTrackingEventSchema,
|
|
156
|
+
provider: TrackEventProviderSchema.optional().default("all"),
|
|
157
|
+
/** Optional hint only; the relay uses the credentialId from its trusted config. */
|
|
158
|
+
credentialId: z.string().max(256).optional(),
|
|
159
|
+
/** Host document/page id so the relay can look up the trusted tracking config. */
|
|
160
|
+
documentId: z.string().max(256).optional()
|
|
161
|
+
});
|
|
162
|
+
var TrackingRelayProviderResultSchema = z.object({
|
|
163
|
+
provider: z.string(),
|
|
164
|
+
success: z.boolean(),
|
|
165
|
+
status: z.number().int().optional(),
|
|
166
|
+
skipped: z.boolean().optional(),
|
|
167
|
+
reason: z.string().optional(),
|
|
168
|
+
error: z.string().optional()
|
|
169
|
+
});
|
|
170
|
+
var TrackingRelayErrorCodeSchema = z.enum([
|
|
171
|
+
"INVALID_REQUEST",
|
|
172
|
+
"UNSUPPORTED_VERSION",
|
|
173
|
+
"FORBIDDEN_ORIGIN",
|
|
174
|
+
"METHOD_NOT_ALLOWED",
|
|
175
|
+
"CONFIG_NOT_FOUND",
|
|
176
|
+
"INTERNAL_ERROR"
|
|
177
|
+
]);
|
|
178
|
+
var TrackingRelayResponseSchema = z.object({
|
|
179
|
+
version: z.literal(TRACKING_RELAY_PROTOCOL_VERSION),
|
|
180
|
+
success: z.boolean(),
|
|
181
|
+
eventId: z.string().optional(),
|
|
182
|
+
skipped: z.boolean().optional(),
|
|
183
|
+
reason: z.string().optional(),
|
|
184
|
+
results: z.record(z.string(), TrackingRelayProviderResultSchema).optional(),
|
|
185
|
+
error: z.object({
|
|
186
|
+
code: TrackingRelayErrorCodeSchema,
|
|
187
|
+
message: z.string()
|
|
188
|
+
}).optional()
|
|
189
|
+
});
|
|
190
|
+
function isTrackingConfig(value) {
|
|
191
|
+
return TrackingConfigSchema.safeParse(value).success;
|
|
192
|
+
}
|
|
193
|
+
function isTrackEventStepPayload(value) {
|
|
194
|
+
return TrackEventStepPayloadSchema.safeParse(value).success;
|
|
195
|
+
}
|
|
196
|
+
function isTrackingRelayRequest(value) {
|
|
197
|
+
return TrackingRelayRequestSchema.safeParse(value).success;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/actions.ts
|
|
201
|
+
import { z as z2 } from "zod";
|
|
202
|
+
var ActionTriggerTypeSchema = z2.enum([
|
|
203
|
+
"click",
|
|
204
|
+
"submit",
|
|
205
|
+
"change",
|
|
206
|
+
"blur",
|
|
207
|
+
"focus",
|
|
208
|
+
"load",
|
|
209
|
+
// Fired once by time-bound components (e.g. `countdown`) when they reach zero.
|
|
210
|
+
"expire"
|
|
211
|
+
]);
|
|
212
|
+
var ActionTriggerSchema = ActionTriggerTypeSchema;
|
|
213
|
+
var ActionStepTypeSchema = z2.enum([
|
|
214
|
+
"api_request",
|
|
215
|
+
"navigate",
|
|
216
|
+
"set_state",
|
|
217
|
+
"reset_form",
|
|
218
|
+
"show_toast",
|
|
219
|
+
"open_modal",
|
|
220
|
+
"close_modal",
|
|
221
|
+
"toggle_modal",
|
|
222
|
+
"copy_clipboard",
|
|
223
|
+
"custom_event",
|
|
224
|
+
"track_event"
|
|
225
|
+
]);
|
|
226
|
+
var ConditionOperatorSchema = z2.enum([
|
|
227
|
+
"equals",
|
|
228
|
+
"not_equals",
|
|
229
|
+
"contains",
|
|
230
|
+
"not_contains",
|
|
231
|
+
"is_truthy",
|
|
232
|
+
"is_falsy",
|
|
233
|
+
"gt",
|
|
234
|
+
"gte",
|
|
235
|
+
"lt",
|
|
236
|
+
"lte",
|
|
237
|
+
"regex"
|
|
238
|
+
]);
|
|
239
|
+
var ActionStepConditionSchema = z2.object({
|
|
240
|
+
field: z2.string().min(1, "Condition field cannot be empty"),
|
|
241
|
+
operator: ConditionOperatorSchema,
|
|
242
|
+
value: z2.unknown().optional()
|
|
243
|
+
});
|
|
244
|
+
var DANGEROUS_URL_PATTERN = /(javascript:|vbscript:|data:text\/html)/i;
|
|
245
|
+
var DANGEROUS_SCRIPT_PATTERN = /<\s*script/i;
|
|
246
|
+
function isSafeActionUrl(url) {
|
|
247
|
+
if (!url || typeof url !== "string") return false;
|
|
248
|
+
const trimmed = url.trim();
|
|
249
|
+
if (DANGEROUS_URL_PATTERN.test(trimmed)) return false;
|
|
250
|
+
if (DANGEROUS_SCRIPT_PATTERN.test(trimmed)) return false;
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
var SafeUrlStringSchema = z2.string().min(1, "URL cannot be empty").refine(isSafeActionUrl, {
|
|
254
|
+
message: "URL contains disallowed or unsafe protocol/script pattern"
|
|
255
|
+
});
|
|
256
|
+
var SafeEventNameSchema = z2.string().min(1, "Event name cannot be empty").regex(/^[a-zA-Z0-9_\-:]+$/, {
|
|
257
|
+
message: "Event name must contain only alphanumeric characters, dashes, colons, or underscores"
|
|
258
|
+
});
|
|
259
|
+
var ApiRequestStepPayloadSchema = z2.object({
|
|
260
|
+
url: SafeUrlStringSchema,
|
|
261
|
+
method: z2.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]).default("GET"),
|
|
262
|
+
headers: z2.record(z2.string(), z2.string()).optional(),
|
|
263
|
+
queryParams: z2.record(z2.string(), z2.union([z2.string(), z2.number(), z2.boolean()])).optional(),
|
|
264
|
+
/**
|
|
265
|
+
* Request body. Omitted inside a form `submit` pipeline → the form's values are sent;
|
|
266
|
+
* `null` → explicitly no body.
|
|
267
|
+
*/
|
|
268
|
+
body: z2.union([z2.string(), z2.record(z2.string(), z2.unknown()), z2.array(z2.unknown())]).nullable().optional(),
|
|
269
|
+
bodyFormat: z2.enum(["json", "form-data", "formData", "urlencoded", "url-encoded", "raw", "text"]).optional(),
|
|
270
|
+
bodyType: z2.enum(["json", "form-data", "formData", "urlencoded", "url-encoded", "raw", "text"]).optional(),
|
|
271
|
+
timeout: z2.number().positive("Timeout must be greater than 0 ms").optional(),
|
|
272
|
+
responseMapping: z2.record(z2.string(), z2.string()).optional()
|
|
273
|
+
});
|
|
274
|
+
var NavigateStepPayloadSchema = z2.object({
|
|
275
|
+
url: SafeUrlStringSchema,
|
|
276
|
+
target: z2.enum(["_self", "_blank", "_parent", "_top"]).optional().default("_self"),
|
|
277
|
+
replace: z2.boolean().optional(),
|
|
278
|
+
scroll: z2.boolean().optional(),
|
|
279
|
+
behavior: z2.enum(["smooth", "auto"]).optional()
|
|
280
|
+
});
|
|
281
|
+
var SetStateStepPayloadSchema = z2.object({
|
|
282
|
+
key: z2.string().min(1, "State key cannot be empty"),
|
|
283
|
+
value: z2.unknown(),
|
|
284
|
+
scope: z2.enum(["runtime", "document", "session", "local"]).optional().default("runtime")
|
|
285
|
+
});
|
|
286
|
+
var ResetFormStepPayloadSchema = z2.object({
|
|
287
|
+
formId: z2.string().optional()
|
|
288
|
+
});
|
|
289
|
+
var ShowToastStepPayloadSchema = z2.object({
|
|
290
|
+
message: z2.string().min(1, "Toast message cannot be empty"),
|
|
291
|
+
type: z2.enum(["success", "error", "warning", "info"]).optional().default("info"),
|
|
292
|
+
variant: z2.enum(["success", "error", "warning", "info"]).optional(),
|
|
293
|
+
title: z2.string().optional(),
|
|
294
|
+
duration: z2.number().nonnegative("Duration must be >= 0 ms").optional(),
|
|
295
|
+
position: z2.enum(["top-right", "top-left", "bottom-right", "bottom-left", "top-center", "bottom-center"]).optional()
|
|
296
|
+
});
|
|
297
|
+
var OpenModalStepPayloadSchema = z2.object({
|
|
298
|
+
modalId: z2.string().optional(),
|
|
299
|
+
modalNodeId: z2.string().optional(),
|
|
300
|
+
toggle: z2.boolean().optional()
|
|
301
|
+
}).refine((data) => Boolean(data.modalId || data.modalNodeId), {
|
|
302
|
+
message: "Modal ID or Modal Node ID cannot be empty"
|
|
303
|
+
});
|
|
304
|
+
var CloseModalStepPayloadSchema = z2.object({
|
|
305
|
+
modalId: z2.string().optional(),
|
|
306
|
+
modalNodeId: z2.string().optional()
|
|
307
|
+
});
|
|
308
|
+
var ToggleModalStepPayloadSchema = z2.object({
|
|
309
|
+
modalId: z2.string().optional(),
|
|
310
|
+
modalNodeId: z2.string().optional(),
|
|
311
|
+
targetNodeId: z2.string().optional(),
|
|
312
|
+
nodeId: z2.string().optional()
|
|
313
|
+
}).refine(
|
|
314
|
+
(data) => [data.modalId, data.modalNodeId, data.targetNodeId, data.nodeId].some(
|
|
315
|
+
(id) => typeof id === "string" && id.trim().length > 0
|
|
316
|
+
),
|
|
317
|
+
{
|
|
318
|
+
message: "Modal ID or Modal Node ID cannot be empty"
|
|
319
|
+
}
|
|
320
|
+
);
|
|
321
|
+
var CopyClipboardStepPayloadSchema = z2.object({
|
|
322
|
+
text: z2.string().optional(),
|
|
323
|
+
value: z2.unknown().optional(),
|
|
324
|
+
notify: z2.boolean().optional(),
|
|
325
|
+
toastMessage: z2.string().optional()
|
|
326
|
+
});
|
|
327
|
+
var CustomEventStepPayloadSchema = z2.object({
|
|
328
|
+
eventName: SafeEventNameSchema,
|
|
329
|
+
detail: z2.record(z2.string(), z2.unknown()).optional(),
|
|
330
|
+
bubbles: z2.boolean().optional().default(true),
|
|
331
|
+
cancelable: z2.boolean().optional().default(true)
|
|
332
|
+
});
|
|
333
|
+
var StepPayloadSchemas = {
|
|
334
|
+
api_request: ApiRequestStepPayloadSchema,
|
|
335
|
+
navigate: NavigateStepPayloadSchema,
|
|
336
|
+
set_state: SetStateStepPayloadSchema,
|
|
337
|
+
reset_form: ResetFormStepPayloadSchema,
|
|
338
|
+
show_toast: ShowToastStepPayloadSchema,
|
|
339
|
+
open_modal: OpenModalStepPayloadSchema,
|
|
340
|
+
close_modal: CloseModalStepPayloadSchema,
|
|
341
|
+
toggle_modal: ToggleModalStepPayloadSchema,
|
|
342
|
+
copy_clipboard: CopyClipboardStepPayloadSchema,
|
|
343
|
+
custom_event: CustomEventStepPayloadSchema,
|
|
344
|
+
track_event: TrackEventStepPayloadSchema
|
|
345
|
+
};
|
|
346
|
+
var ActionStepSchema = z2.lazy(
|
|
347
|
+
() => z2.object({
|
|
348
|
+
id: z2.string().min(1, "Step ID cannot be empty"),
|
|
349
|
+
type: ActionStepTypeSchema,
|
|
350
|
+
label: z2.string().optional(),
|
|
351
|
+
payload: z2.record(z2.string(), z2.unknown()).optional().default({}),
|
|
352
|
+
condition: ActionStepConditionSchema.optional(),
|
|
353
|
+
timeout: z2.number().positive().optional(),
|
|
354
|
+
continueOnError: z2.boolean().optional(),
|
|
355
|
+
onSuccess: z2.array(ActionStepSchema).optional(),
|
|
356
|
+
onError: z2.array(ActionStepSchema).optional()
|
|
357
|
+
})
|
|
358
|
+
);
|
|
359
|
+
var ActionPipelineSchema = z2.object({
|
|
360
|
+
id: z2.string().min(1, "Pipeline ID cannot be empty"),
|
|
361
|
+
trigger: ActionTriggerTypeSchema,
|
|
362
|
+
label: z2.string().optional(),
|
|
363
|
+
debounceMs: z2.number().nonnegative("Debounce ms must be >= 0").optional(),
|
|
364
|
+
preventDuplicate: z2.boolean().optional(),
|
|
365
|
+
enabled: z2.boolean().optional().default(true),
|
|
366
|
+
steps: z2.array(ActionStepSchema).min(1, "Action pipeline must contain at least one step")
|
|
367
|
+
});
|
|
368
|
+
function isActionTriggerType(value) {
|
|
369
|
+
return ActionTriggerTypeSchema.safeParse(value).success;
|
|
370
|
+
}
|
|
371
|
+
function isActionStepType(value) {
|
|
372
|
+
return ActionStepTypeSchema.safeParse(value).success;
|
|
373
|
+
}
|
|
374
|
+
function isConditionOperator(value) {
|
|
375
|
+
return ConditionOperatorSchema.safeParse(value).success;
|
|
376
|
+
}
|
|
377
|
+
function isActionStepCondition(value) {
|
|
378
|
+
return ActionStepConditionSchema.safeParse(value).success;
|
|
379
|
+
}
|
|
380
|
+
function isActionStep(value) {
|
|
381
|
+
return ActionStepSchema.safeParse(value).success;
|
|
382
|
+
}
|
|
383
|
+
function isActionPipeline(value) {
|
|
384
|
+
return ActionPipelineSchema.safeParse(value).success;
|
|
385
|
+
}
|
|
386
|
+
function validateActionStepPayload(type, payload) {
|
|
387
|
+
const schema = StepPayloadSchemas[type];
|
|
388
|
+
if (!schema) {
|
|
389
|
+
return {
|
|
390
|
+
success: false,
|
|
391
|
+
error: new z2.ZodError([
|
|
392
|
+
{
|
|
393
|
+
code: z2.ZodIssueCode.custom,
|
|
394
|
+
message: `Unknown step type: ${type}`,
|
|
395
|
+
path: ["type"]
|
|
396
|
+
}
|
|
397
|
+
])
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
const result = schema.safeParse(payload);
|
|
401
|
+
if (result.success) {
|
|
402
|
+
return { success: true, data: result.data };
|
|
403
|
+
}
|
|
404
|
+
return { success: false, error: result.error };
|
|
405
|
+
}
|
|
406
|
+
function sanitizeActionUrl(url, fallback = "") {
|
|
407
|
+
if (typeof url !== "string") return fallback;
|
|
408
|
+
const trimmed = url.trim();
|
|
409
|
+
if (!isSafeActionUrl(trimmed)) {
|
|
410
|
+
return fallback;
|
|
411
|
+
}
|
|
412
|
+
return trimmed;
|
|
413
|
+
}
|
|
414
|
+
function sanitizeActionString(value, fallback = "") {
|
|
415
|
+
if (typeof value !== "string") return fallback;
|
|
416
|
+
if (DANGEROUS_SCRIPT_PATTERN.test(value)) {
|
|
417
|
+
return value.replace(/<\s*script[^>]*>[\s\S]*?<\s*\/\s*script\s*>/gi, "").replace(/<\s*script[^>]*>/gi, "");
|
|
418
|
+
}
|
|
419
|
+
return value;
|
|
420
|
+
}
|
|
421
|
+
function sanitizeActionValue(value) {
|
|
422
|
+
if (value === null || value === void 0) return value;
|
|
423
|
+
if (typeof value === "string") {
|
|
424
|
+
const trimmed = value.trim();
|
|
425
|
+
if (DANGEROUS_URL_PATTERN.test(trimmed)) {
|
|
426
|
+
return "";
|
|
427
|
+
}
|
|
428
|
+
return sanitizeActionString(value);
|
|
429
|
+
}
|
|
430
|
+
if (Array.isArray(value)) {
|
|
431
|
+
return value.map((item) => sanitizeActionValue(item));
|
|
432
|
+
}
|
|
433
|
+
if (typeof value === "object") {
|
|
434
|
+
const result = {};
|
|
435
|
+
for (const [k, v] of Object.entries(value)) {
|
|
436
|
+
if (/^(url|href|src|endpoint|targetUrl)$/i.test(k) && typeof v === "string") {
|
|
437
|
+
result[k] = sanitizeActionUrl(v);
|
|
438
|
+
} else {
|
|
439
|
+
result[k] = sanitizeActionValue(v);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return result;
|
|
443
|
+
}
|
|
444
|
+
return value;
|
|
445
|
+
}
|
|
446
|
+
function sanitizeActionPayload(payload) {
|
|
447
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
448
|
+
return {};
|
|
449
|
+
}
|
|
450
|
+
return sanitizeActionValue(payload);
|
|
451
|
+
}
|
|
452
|
+
function sanitizeActionStep(step) {
|
|
453
|
+
const sanitized = {
|
|
454
|
+
...step,
|
|
455
|
+
label: step.label ? sanitizeActionString(step.label) : step.label,
|
|
456
|
+
payload: sanitizeActionPayload(step.payload)
|
|
457
|
+
};
|
|
458
|
+
if (step.condition && step.condition.value !== void 0) {
|
|
459
|
+
sanitized.condition = {
|
|
460
|
+
...step.condition,
|
|
461
|
+
value: sanitizeActionValue(step.condition.value)
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
if (step.onSuccess && Array.isArray(step.onSuccess)) {
|
|
465
|
+
sanitized.onSuccess = step.onSuccess.map((s) => sanitizeActionStep(s));
|
|
466
|
+
}
|
|
467
|
+
if (step.onError && Array.isArray(step.onError)) {
|
|
468
|
+
sanitized.onError = step.onError.map((s) => sanitizeActionStep(s));
|
|
469
|
+
}
|
|
470
|
+
return sanitized;
|
|
471
|
+
}
|
|
472
|
+
function sanitizeActionPipeline(pipeline) {
|
|
473
|
+
return {
|
|
474
|
+
...pipeline,
|
|
475
|
+
label: pipeline.label ? sanitizeActionString(pipeline.label) : pipeline.label,
|
|
476
|
+
steps: Array.isArray(pipeline.steps) ? pipeline.steps.map((s) => sanitizeActionStep(s)) : []
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// src/form.ts
|
|
481
|
+
import { z as z3 } from "zod";
|
|
482
|
+
var ValidationRuleTypeSchema = z3.enum([
|
|
483
|
+
"required",
|
|
484
|
+
"email",
|
|
485
|
+
"url",
|
|
486
|
+
"min_length",
|
|
487
|
+
"max_length",
|
|
488
|
+
"numeric_min",
|
|
489
|
+
"numeric_max",
|
|
490
|
+
"pattern",
|
|
491
|
+
"match_field",
|
|
492
|
+
"custom_regex"
|
|
493
|
+
]);
|
|
494
|
+
var ValidateOnEventSchema = z3.enum(["blur", "change", "submit"]);
|
|
495
|
+
var ValidationRuleSchema = z3.object({
|
|
496
|
+
type: ValidationRuleTypeSchema,
|
|
497
|
+
value: z3.unknown().optional(),
|
|
498
|
+
message: z3.string().min(1, "Validation error message cannot be empty")
|
|
499
|
+
});
|
|
500
|
+
var FormFieldBindingSchema = z3.object({
|
|
501
|
+
name: z3.string().min(1, "Field name cannot be empty"),
|
|
502
|
+
label: z3.string().optional(),
|
|
503
|
+
defaultValue: z3.unknown().optional(),
|
|
504
|
+
rules: z3.array(ValidationRuleSchema).optional().default([]),
|
|
505
|
+
validateOn: ValidateOnEventSchema.optional().default("blur"),
|
|
506
|
+
transform: z3.enum(["trim", "lowercase", "uppercase", "number"]).optional(),
|
|
507
|
+
disabled: z3.boolean().optional(),
|
|
508
|
+
required: z3.boolean().optional()
|
|
509
|
+
});
|
|
510
|
+
var FormConfigSchema = z3.object({
|
|
511
|
+
formId: z3.string().min(1, "Form ID cannot be empty"),
|
|
512
|
+
resetOnSubmit: z3.boolean().optional().default(false),
|
|
513
|
+
scrollToFirstError: z3.boolean().optional().default(true),
|
|
514
|
+
validateOn: ValidateOnEventSchema.optional().default("blur"),
|
|
515
|
+
initialValues: z3.record(z3.string(), z3.unknown()).optional(),
|
|
516
|
+
rules: z3.array(ValidationRuleSchema).optional()
|
|
517
|
+
});
|
|
518
|
+
function isValidationRuleType(value) {
|
|
519
|
+
return ValidationRuleTypeSchema.safeParse(value).success;
|
|
520
|
+
}
|
|
521
|
+
function isValidationRule(value) {
|
|
522
|
+
return ValidationRuleSchema.safeParse(value).success;
|
|
523
|
+
}
|
|
524
|
+
function isValidateOnEvent(value) {
|
|
525
|
+
return ValidateOnEventSchema.safeParse(value).success;
|
|
526
|
+
}
|
|
527
|
+
function isFormFieldBinding(value) {
|
|
528
|
+
return FormFieldBindingSchema.safeParse(value).success;
|
|
529
|
+
}
|
|
530
|
+
function isFormConfig(value) {
|
|
531
|
+
return FormConfigSchema.safeParse(value).success;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// src/theme.ts
|
|
535
|
+
import { z as z4 } from "zod";
|
|
536
|
+
var THEME_TOKEN_GROUPS = ["colors", "fonts", "radii", "spacing"];
|
|
537
|
+
var THEME_TOKEN_CSS_PREFIX = Object.freeze({
|
|
538
|
+
colors: "--kb-color-",
|
|
539
|
+
fonts: "--kb-font-",
|
|
540
|
+
radii: "--kb-radius-",
|
|
541
|
+
spacing: "--kb-space-"
|
|
542
|
+
});
|
|
543
|
+
var THEME_TOKEN_KEY_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/;
|
|
544
|
+
var DANGEROUS_THEME_VALUE_PATTERN = /javascript:|expression\(|@import|<script|vbscript:|data:text\/html|url\s*\(/i;
|
|
545
|
+
var FORBIDDEN_THEME_VALUE_CHARS = /[;{}<>\\]/;
|
|
546
|
+
var MAX_THEME_VALUE_LENGTH = 512;
|
|
547
|
+
function isSafeThemeTokenKey(key) {
|
|
548
|
+
return typeof key === "string" && THEME_TOKEN_KEY_PATTERN.test(key);
|
|
549
|
+
}
|
|
550
|
+
function isSafeThemeTokenValue(value) {
|
|
551
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
552
|
+
if (typeof value !== "string") return false;
|
|
553
|
+
if (value.trim().length === 0 || value.length > MAX_THEME_VALUE_LENGTH) return false;
|
|
554
|
+
if (FORBIDDEN_THEME_VALUE_CHARS.test(value)) return false;
|
|
555
|
+
if (DANGEROUS_THEME_VALUE_PATTERN.test(value)) return false;
|
|
556
|
+
return true;
|
|
557
|
+
}
|
|
558
|
+
var ThemeTokenKeySchema = z4.string().refine(isSafeThemeTokenKey, {
|
|
559
|
+
message: 'Theme token key must be 1-64 characters of letters, digits, "-" or "_" (starting with a letter or digit)'
|
|
560
|
+
});
|
|
561
|
+
var ThemeTokenValueSchema = z4.union([
|
|
562
|
+
z4.string().refine((value) => isSafeThemeTokenValue(value), {
|
|
563
|
+
message: "Theme token value contains a disallowed or unsafe pattern"
|
|
564
|
+
}),
|
|
565
|
+
z4.number().refine((value) => Number.isFinite(value), { message: "Theme token value must be finite" })
|
|
566
|
+
]);
|
|
567
|
+
var ThemeTokenMapSchema = z4.record(ThemeTokenKeySchema, ThemeTokenValueSchema);
|
|
568
|
+
var ThemeSchema = z4.object({
|
|
569
|
+
colors: ThemeTokenMapSchema.optional(),
|
|
570
|
+
fonts: ThemeTokenMapSchema.optional(),
|
|
571
|
+
radii: ThemeTokenMapSchema.optional(),
|
|
572
|
+
spacing: ThemeTokenMapSchema.optional()
|
|
573
|
+
});
|
|
574
|
+
function themeTokenCssVarName(group, key) {
|
|
575
|
+
if (!isSafeThemeTokenKey(key)) return null;
|
|
576
|
+
return `${THEME_TOKEN_CSS_PREFIX[group]}${key}`;
|
|
577
|
+
}
|
|
578
|
+
function themeTokenRef(group, key) {
|
|
579
|
+
const name = themeTokenCssVarName(group, key);
|
|
580
|
+
if (!name) {
|
|
581
|
+
throw new Error(`Invalid theme token key "${key}"`);
|
|
582
|
+
}
|
|
583
|
+
return `var(${name})`;
|
|
584
|
+
}
|
|
585
|
+
var TOKEN_REF_PATTERN = /^var\(\s*--kb-(color|font|radius|space)-([a-zA-Z0-9][a-zA-Z0-9_-]*)\s*(?:,[^)]*)?\)$/;
|
|
586
|
+
var PREFIX_TO_GROUP = {
|
|
587
|
+
color: "colors",
|
|
588
|
+
font: "fonts",
|
|
589
|
+
radius: "radii",
|
|
590
|
+
space: "spacing"
|
|
591
|
+
};
|
|
592
|
+
function parseThemeTokenRef(value) {
|
|
593
|
+
if (typeof value !== "string") return null;
|
|
594
|
+
const match = TOKEN_REF_PATTERN.exec(value.trim());
|
|
595
|
+
if (!match) return null;
|
|
596
|
+
return { group: PREFIX_TO_GROUP[match[1]], key: match[2] };
|
|
597
|
+
}
|
|
598
|
+
function mergeThemes(...layers) {
|
|
599
|
+
const merged = {};
|
|
600
|
+
for (const layer of layers) {
|
|
601
|
+
if (!layer || typeof layer !== "object") continue;
|
|
602
|
+
for (const group of THEME_TOKEN_GROUPS) {
|
|
603
|
+
const tokens = layer[group];
|
|
604
|
+
if (!tokens || typeof tokens !== "object") continue;
|
|
605
|
+
for (const [key, value] of Object.entries(tokens)) {
|
|
606
|
+
if (!isSafeThemeTokenKey(key) || !isSafeThemeTokenValue(value)) continue;
|
|
607
|
+
merged[group] = { ...merged[group] ?? {}, [key]: value };
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return merged;
|
|
612
|
+
}
|
|
613
|
+
function themeToCssVariables(theme) {
|
|
614
|
+
const entries = [];
|
|
615
|
+
if (!theme || typeof theme !== "object") return entries;
|
|
616
|
+
for (const group of THEME_TOKEN_GROUPS) {
|
|
617
|
+
const tokens = theme[group];
|
|
618
|
+
if (!tokens || typeof tokens !== "object") continue;
|
|
619
|
+
for (const [key, value] of Object.entries(tokens)) {
|
|
620
|
+
const name = themeTokenCssVarName(group, key);
|
|
621
|
+
if (!name || !isSafeThemeTokenValue(value)) continue;
|
|
622
|
+
entries.push([
|
|
623
|
+
name,
|
|
624
|
+
typeof value === "number" && (group === "radii" || group === "spacing") ? `${value}px` : String(value)
|
|
625
|
+
]);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
return entries;
|
|
629
|
+
}
|
|
630
|
+
function isTheme(value) {
|
|
631
|
+
return ThemeSchema.safeParse(value).success;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// src/document.ts
|
|
635
|
+
import { z as z5 } from "zod";
|
|
636
|
+
var SCHEMA_NAME = "stora.page";
|
|
637
|
+
var CURRENT_SCHEMA_VERSION = "1.2.0";
|
|
638
|
+
var AssetReferenceSchema = z5.object({
|
|
639
|
+
type: z5.literal("asset"),
|
|
640
|
+
assetId: z5.string().min(1, "Asset ID cannot be empty"),
|
|
641
|
+
filename: z5.string().optional(),
|
|
642
|
+
mimeType: z5.string().optional(),
|
|
643
|
+
fallbackUrl: z5.string().url().optional()
|
|
644
|
+
});
|
|
645
|
+
var VariableBindingSchema = z5.object({
|
|
646
|
+
type: z5.literal("variable"),
|
|
647
|
+
key: z5.string().min(1, "Variable key cannot be empty"),
|
|
648
|
+
fallback: z5.unknown().optional()
|
|
649
|
+
});
|
|
650
|
+
var ActionBindingSchema = z5.object({
|
|
651
|
+
type: z5.string().min(1, "Action type cannot be empty"),
|
|
652
|
+
payload: z5.record(z5.string(), z5.unknown()).optional()
|
|
653
|
+
});
|
|
654
|
+
var DANGEROUS_STYLE_VALUE_PATTERN = /javascript:|expression\(|@import|<script|vbscript:|data:text\/html/i;
|
|
655
|
+
var StyleValueSchema = z5.union([
|
|
656
|
+
z5.string().refine((value) => !DANGEROUS_STYLE_VALUE_PATTERN.test(value), {
|
|
657
|
+
message: "Style value contains a disallowed or unsafe pattern"
|
|
658
|
+
}),
|
|
659
|
+
z5.number(),
|
|
660
|
+
z5.boolean(),
|
|
661
|
+
z5.null(),
|
|
662
|
+
z5.undefined()
|
|
663
|
+
]);
|
|
664
|
+
var StyleDefinitionSchema = z5.record(z5.string(), StyleValueSchema);
|
|
665
|
+
var PseudoStateStylesSchema = z5.record(z5.string(), StyleDefinitionSchema);
|
|
666
|
+
var ResponsiveStylesSchema = z5.object({
|
|
667
|
+
base: StyleDefinitionSchema.optional(),
|
|
668
|
+
desktop: StyleDefinitionSchema.optional(),
|
|
669
|
+
tablet: StyleDefinitionSchema.optional(),
|
|
670
|
+
mobile: StyleDefinitionSchema.optional(),
|
|
671
|
+
states: PseudoStateStylesSchema.optional()
|
|
672
|
+
}).passthrough().default({});
|
|
673
|
+
var AnimationConfigSchema = z5.object({
|
|
674
|
+
type: z5.string().refine((value) => !DANGEROUS_STYLE_VALUE_PATTERN.test(value), {
|
|
675
|
+
message: "Animation type contains a disallowed or unsafe pattern"
|
|
676
|
+
}).optional().default("none"),
|
|
677
|
+
duration: z5.number().min(0, "Duration must be non-negative").optional().default(600),
|
|
678
|
+
delay: z5.number().min(0, "Delay must be non-negative").optional().default(0),
|
|
679
|
+
easing: z5.string().refine((value) => !DANGEROUS_STYLE_VALUE_PATTERN.test(value), {
|
|
680
|
+
message: "Animation easing contains a disallowed or unsafe pattern"
|
|
681
|
+
}).optional().default("ease-out"),
|
|
682
|
+
once: z5.boolean().optional().default(true),
|
|
683
|
+
hoverEffect: z5.string().refine((value) => !DANGEROUS_STYLE_VALUE_PATTERN.test(value), {
|
|
684
|
+
message: "Hover effect contains a disallowed or unsafe pattern"
|
|
685
|
+
}).optional().default("none"),
|
|
686
|
+
loopEffect: z5.string().refine((value) => !DANGEROUS_STYLE_VALUE_PATTERN.test(value), {
|
|
687
|
+
message: "Loop effect contains a disallowed or unsafe pattern"
|
|
688
|
+
}).optional().default("none")
|
|
689
|
+
});
|
|
690
|
+
var DEFAULT_ANIMATION_CONFIG = {
|
|
691
|
+
type: "none",
|
|
692
|
+
duration: 600,
|
|
693
|
+
delay: 0,
|
|
694
|
+
easing: "ease-out",
|
|
695
|
+
once: true,
|
|
696
|
+
hoverEffect: "none",
|
|
697
|
+
loopEffect: "none"
|
|
698
|
+
};
|
|
699
|
+
var NodeSchema = z5.lazy(
|
|
700
|
+
() => z5.object({
|
|
701
|
+
id: z5.string().min(1, "Node ID must be a non-empty string"),
|
|
702
|
+
type: z5.string().min(1, "Node type must be a non-empty string"),
|
|
703
|
+
props: z5.record(z5.string(), z5.unknown()).optional().default({}),
|
|
704
|
+
styles: ResponsiveStylesSchema.optional().default({}),
|
|
705
|
+
animation: AnimationConfigSchema.optional(),
|
|
706
|
+
actions: z5.array(ActionPipelineSchema).optional(),
|
|
707
|
+
formConfig: FormConfigSchema.optional(),
|
|
708
|
+
children: z5.array(NodeSchema).optional().default([])
|
|
709
|
+
})
|
|
710
|
+
);
|
|
711
|
+
var RootPageNodeSchema = NodeSchema.refine(
|
|
712
|
+
(node) => node.type === "page",
|
|
713
|
+
{
|
|
714
|
+
message: 'Root node must have type "page"',
|
|
715
|
+
path: ["type"]
|
|
716
|
+
}
|
|
717
|
+
);
|
|
718
|
+
var DocumentMetadataSchema = z5.object({
|
|
719
|
+
title: z5.string().min(1, "Title is required").default("Untitled Page"),
|
|
720
|
+
description: z5.string().optional().default(""),
|
|
721
|
+
author: z5.string().optional().default(""),
|
|
722
|
+
createdAt: z5.string().optional(),
|
|
723
|
+
updatedAt: z5.string().optional(),
|
|
724
|
+
tags: z5.array(z5.string()).optional().default([]),
|
|
725
|
+
category: z5.string().optional().default("general"),
|
|
726
|
+
version: z5.string().optional().default("1.0.0"),
|
|
727
|
+
custom: z5.record(z5.string(), z5.unknown()).optional(),
|
|
728
|
+
tracking: TrackingConfigSchema.optional()
|
|
729
|
+
});
|
|
730
|
+
var PageDocumentSchema = z5.object({
|
|
731
|
+
schema: z5.literal(SCHEMA_NAME),
|
|
732
|
+
version: z5.string().min(1, "Schema version is required").default(CURRENT_SCHEMA_VERSION),
|
|
733
|
+
metadata: DocumentMetadataSchema.optional(),
|
|
734
|
+
tracking: TrackingConfigSchema.optional(),
|
|
735
|
+
theme: ThemeSchema.optional(),
|
|
736
|
+
document: RootPageNodeSchema
|
|
737
|
+
});
|
|
738
|
+
var PROJECT_SCHEMA_NAME = "stora.project";
|
|
739
|
+
var CURRENT_PROJECT_SCHEMA_VERSION = "1.0.0";
|
|
740
|
+
var ARTBOARD_REFERENCE_NODE_TYPE = "artboard-reference";
|
|
741
|
+
var ArtboardTypeSchema = z5.enum(["page", "component"]);
|
|
742
|
+
var ArtboardSchema = z5.object({
|
|
743
|
+
id: z5.string().min(1, "Artboard ID is required"),
|
|
744
|
+
name: z5.string().min(1, "Artboard name is required"),
|
|
745
|
+
artboardType: ArtboardTypeSchema,
|
|
746
|
+
/** Route for page artboards (e.g. "/about"). */
|
|
747
|
+
slug: z5.string().optional(),
|
|
748
|
+
/**
|
|
749
|
+
* For component artboards: the id that `open_modal` / `close_modal` actions target.
|
|
750
|
+
* Mirrors the detached node's own `props.modalId` so ModalManager keeps working unchanged.
|
|
751
|
+
*/
|
|
752
|
+
triggerId: z5.string().optional(),
|
|
753
|
+
/**
|
|
754
|
+
* Viewport/breakpoint this artboard is currently authored at. Declared inline
|
|
755
|
+
* (rather than imported) because viewport is otherwise an editor/renderer concept.
|
|
756
|
+
*/
|
|
757
|
+
viewport: z5.enum(["desktop", "tablet", "mobile"]).optional(),
|
|
758
|
+
/** Persisted artboard width in px, matching the canvas ViewportResizer's `width`. */
|
|
759
|
+
width: z5.number().positive().optional(),
|
|
760
|
+
/**
|
|
761
|
+
* Free position on the builder's infinite canvas, in unscaled canvas px. Absent means
|
|
762
|
+
* "not placed yet" — the canvas then falls back to laying the artboard out in a row.
|
|
763
|
+
*/
|
|
764
|
+
position: z5.object({
|
|
765
|
+
x: z5.number(),
|
|
766
|
+
y: z5.number()
|
|
767
|
+
}).optional(),
|
|
768
|
+
document: PageDocumentSchema
|
|
769
|
+
});
|
|
770
|
+
var ProjectDocumentSchema = z5.object({
|
|
771
|
+
schema: z5.literal(PROJECT_SCHEMA_NAME),
|
|
772
|
+
version: z5.string().min(1, "Schema version is required").default(CURRENT_PROJECT_SCHEMA_VERSION),
|
|
773
|
+
metadata: DocumentMetadataSchema.optional(),
|
|
774
|
+
tracking: TrackingConfigSchema.optional(),
|
|
775
|
+
artboards: z5.array(ArtboardSchema).min(1, "A project needs at least one artboard"),
|
|
776
|
+
activeArtboardId: z5.string().optional()
|
|
777
|
+
});
|
|
778
|
+
function isAssetReference(value) {
|
|
779
|
+
return AssetReferenceSchema.safeParse(value).success;
|
|
780
|
+
}
|
|
781
|
+
function isVariableBinding(value) {
|
|
782
|
+
return VariableBindingSchema.safeParse(value).success;
|
|
783
|
+
}
|
|
784
|
+
function isActionBinding(value) {
|
|
785
|
+
return ActionBindingSchema.safeParse(value).success;
|
|
786
|
+
}
|
|
787
|
+
function isAnimationConfig(value) {
|
|
788
|
+
return AnimationConfigSchema.safeParse(value).success;
|
|
789
|
+
}
|
|
790
|
+
function isProjectDocument(value) {
|
|
791
|
+
return ProjectDocumentSchema.safeParse(value).success;
|
|
792
|
+
}
|
|
793
|
+
function looksLikeProjectDocument(value) {
|
|
794
|
+
if (typeof value !== "object" || value === null) return false;
|
|
795
|
+
const candidate = value;
|
|
796
|
+
return candidate.schema === PROJECT_SCHEMA_NAME || Array.isArray(candidate.artboards);
|
|
797
|
+
}
|
|
798
|
+
function isArtboardReferenceNode(node) {
|
|
799
|
+
return node.type === ARTBOARD_REFERENCE_NODE_TYPE;
|
|
800
|
+
}
|
|
801
|
+
function generateDeterministicNodeId(prefix, indexOrKey) {
|
|
802
|
+
const cleanPrefix = prefix.toLowerCase().replace(/[^a-z0-9_-]/g, "-");
|
|
803
|
+
return `${cleanPrefix}_${indexOrKey}`;
|
|
804
|
+
}
|
|
805
|
+
function collectNodeIds(node) {
|
|
806
|
+
const ids = [node.id];
|
|
807
|
+
if (node.children && Array.isArray(node.children)) {
|
|
808
|
+
for (const child of node.children) {
|
|
809
|
+
ids.push(...collectNodeIds(child));
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
return ids;
|
|
813
|
+
}
|
|
814
|
+
function validateNodeIdUniqueness(node) {
|
|
815
|
+
const allIds = collectNodeIds(node);
|
|
816
|
+
const seen = /* @__PURE__ */ new Set();
|
|
817
|
+
const duplicates = /* @__PURE__ */ new Set();
|
|
818
|
+
for (const id of allIds) {
|
|
819
|
+
if (seen.has(id)) {
|
|
820
|
+
duplicates.add(id);
|
|
821
|
+
} else {
|
|
822
|
+
seen.add(id);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
return {
|
|
826
|
+
valid: duplicates.size === 0,
|
|
827
|
+
duplicateIds: Array.from(duplicates)
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// src/manifest.ts
|
|
832
|
+
import { z as z6 } from "zod";
|
|
833
|
+
var ManifestAssetItemSchema = z6.object({
|
|
834
|
+
id: z6.string().min(1, "Asset ID cannot be empty"),
|
|
835
|
+
path: z6.string().min(1, "Asset archive path cannot be empty"),
|
|
836
|
+
mimeType: z6.string().min(1, "MIME type cannot be empty"),
|
|
837
|
+
size: z6.number().nonnegative("Asset size must be non-negative"),
|
|
838
|
+
checksum: z6.string().optional()
|
|
839
|
+
});
|
|
840
|
+
var ManifestSchema = z6.object({
|
|
841
|
+
schema: z6.literal(SCHEMA_NAME).default(SCHEMA_NAME),
|
|
842
|
+
schemaVersion: z6.string().min(1, "Schema version cannot be empty").default("1.0.0"),
|
|
843
|
+
packageVersion: z6.string().min(1, "Package version cannot be empty").default("1.0.0"),
|
|
844
|
+
builderCompatibility: z6.string().default(">=0.1.0"),
|
|
845
|
+
requiredComponents: z6.array(z6.string()).default([]),
|
|
846
|
+
requiredCapabilities: z6.array(z6.string()).default([]),
|
|
847
|
+
assets: z6.array(ManifestAssetItemSchema).default([]),
|
|
848
|
+
createdAt: z6.string().optional()
|
|
849
|
+
});
|
|
850
|
+
function isManifest(value) {
|
|
851
|
+
return ManifestSchema.safeParse(value).success;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// src/template.ts
|
|
855
|
+
import { z as z7 } from "zod";
|
|
856
|
+
var DANGEROUS_URI_PATTERN = /^(javascript:|vbscript:|data:(?!image\/))|<script/i;
|
|
857
|
+
function isSafeThumbnailUrl(url) {
|
|
858
|
+
if (!url || typeof url !== "string") return false;
|
|
859
|
+
const trimmed = url.trim();
|
|
860
|
+
if (trimmed.length === 0) return false;
|
|
861
|
+
if (DANGEROUS_URI_PATTERN.test(trimmed)) return false;
|
|
862
|
+
return true;
|
|
863
|
+
}
|
|
864
|
+
var SafeThumbnailUrlSchema = z7.string().min(1, "Thumbnail URL cannot be empty").refine(
|
|
865
|
+
(url) => isSafeThumbnailUrl(url),
|
|
866
|
+
{ message: "Thumbnail URL contains an unsafe protocol or payload" }
|
|
867
|
+
);
|
|
868
|
+
var SafeThumbnailObjectSchema = z7.object({
|
|
869
|
+
url: SafeThumbnailUrlSchema,
|
|
870
|
+
alt: z7.string().optional(),
|
|
871
|
+
width: z7.number().positive().optional(),
|
|
872
|
+
height: z7.number().positive().optional()
|
|
873
|
+
});
|
|
874
|
+
var SafeThumbnailSchema = z7.union([
|
|
875
|
+
AssetReferenceSchema,
|
|
876
|
+
SafeThumbnailObjectSchema,
|
|
877
|
+
SafeThumbnailUrlSchema
|
|
878
|
+
]);
|
|
879
|
+
var TemplatePackageRefSchema = z7.object({
|
|
880
|
+
path: z7.string().optional(),
|
|
881
|
+
url: SafeThumbnailUrlSchema.optional(),
|
|
882
|
+
checksum: z7.string().optional(),
|
|
883
|
+
format: z7.enum(["stora", "json"]).default("stora")
|
|
884
|
+
});
|
|
885
|
+
var TemplateRequirementsSchema = z7.object({
|
|
886
|
+
requiredComponents: z7.array(z7.string()).default([]),
|
|
887
|
+
requiredCapabilities: z7.array(z7.string()).default([])
|
|
888
|
+
});
|
|
889
|
+
var TemplateRecordSchema = z7.object({
|
|
890
|
+
id: z7.string().min(1, "Template ID must be a non-empty string"),
|
|
891
|
+
name: z7.string().min(1, "Template name must be a non-empty string"),
|
|
892
|
+
description: z7.string().optional().default(""),
|
|
893
|
+
category: z7.string().optional().default("general"),
|
|
894
|
+
tags: z7.array(z7.string()).optional().default([]),
|
|
895
|
+
thumbnail: SafeThumbnailSchema.optional(),
|
|
896
|
+
author: z7.string().optional().default(""),
|
|
897
|
+
version: z7.string().optional().default("1.0.0"),
|
|
898
|
+
document: PageDocumentSchema.optional(),
|
|
899
|
+
packageReference: TemplatePackageRefSchema.optional(),
|
|
900
|
+
requirements: TemplateRequirementsSchema.default({
|
|
901
|
+
requiredComponents: [],
|
|
902
|
+
requiredCapabilities: []
|
|
903
|
+
}),
|
|
904
|
+
createdAt: z7.string().optional(),
|
|
905
|
+
updatedAt: z7.string().optional(),
|
|
906
|
+
custom: z7.record(z7.string(), z7.unknown()).optional()
|
|
907
|
+
});
|
|
908
|
+
function isTemplateRecord(value) {
|
|
909
|
+
return TemplateRecordSchema.safeParse(value).success;
|
|
910
|
+
}
|
|
911
|
+
function isSafeThumbnail(value) {
|
|
912
|
+
return SafeThumbnailSchema.safeParse(value).success;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
export {
|
|
916
|
+
TrackingProviderTypeSchema,
|
|
917
|
+
TrackingDeliverySchema,
|
|
918
|
+
MetaTrackingProviderConfigSchema,
|
|
919
|
+
GoogleTrackingProviderConfigSchema,
|
|
920
|
+
GtmTrackingProviderConfigSchema,
|
|
921
|
+
TikTokTrackingProviderConfigSchema,
|
|
922
|
+
CustomTrackingProviderConfigSchema,
|
|
923
|
+
LEGACY_TRACKING_SECRET_KEYS,
|
|
924
|
+
TrackingConfigSchema,
|
|
925
|
+
MetaTrackingSecretsSchema,
|
|
926
|
+
GoogleTrackingSecretsSchema,
|
|
927
|
+
TikTokTrackingSecretsSchema,
|
|
928
|
+
CustomTrackingSecretsSchema,
|
|
929
|
+
StandardTrackingEventSchema,
|
|
930
|
+
TrackEventProviderSchema,
|
|
931
|
+
TrackEventStepPayloadSchema,
|
|
932
|
+
TRACKING_RELAY_PROTOCOL_VERSION,
|
|
933
|
+
ServerTrackingEventSchema,
|
|
934
|
+
TrackingRelayRequestSchema,
|
|
935
|
+
TrackingRelayProviderResultSchema,
|
|
936
|
+
TrackingRelayErrorCodeSchema,
|
|
937
|
+
TrackingRelayResponseSchema,
|
|
938
|
+
isTrackingConfig,
|
|
939
|
+
isTrackEventStepPayload,
|
|
940
|
+
isTrackingRelayRequest,
|
|
941
|
+
ActionTriggerTypeSchema,
|
|
942
|
+
ActionTriggerSchema,
|
|
943
|
+
ActionStepTypeSchema,
|
|
944
|
+
ConditionOperatorSchema,
|
|
945
|
+
ActionStepConditionSchema,
|
|
946
|
+
isSafeActionUrl,
|
|
947
|
+
ApiRequestStepPayloadSchema,
|
|
948
|
+
NavigateStepPayloadSchema,
|
|
949
|
+
SetStateStepPayloadSchema,
|
|
950
|
+
ResetFormStepPayloadSchema,
|
|
951
|
+
ShowToastStepPayloadSchema,
|
|
952
|
+
OpenModalStepPayloadSchema,
|
|
953
|
+
CloseModalStepPayloadSchema,
|
|
954
|
+
ToggleModalStepPayloadSchema,
|
|
955
|
+
CopyClipboardStepPayloadSchema,
|
|
956
|
+
CustomEventStepPayloadSchema,
|
|
957
|
+
StepPayloadSchemas,
|
|
958
|
+
ActionStepSchema,
|
|
959
|
+
ActionPipelineSchema,
|
|
960
|
+
isActionTriggerType,
|
|
961
|
+
isActionStepType,
|
|
962
|
+
isConditionOperator,
|
|
963
|
+
isActionStepCondition,
|
|
964
|
+
isActionStep,
|
|
965
|
+
isActionPipeline,
|
|
966
|
+
validateActionStepPayload,
|
|
967
|
+
sanitizeActionUrl,
|
|
968
|
+
sanitizeActionString,
|
|
969
|
+
sanitizeActionValue,
|
|
970
|
+
sanitizeActionPayload,
|
|
971
|
+
sanitizeActionStep,
|
|
972
|
+
sanitizeActionPipeline,
|
|
973
|
+
ValidationRuleTypeSchema,
|
|
974
|
+
ValidateOnEventSchema,
|
|
975
|
+
ValidationRuleSchema,
|
|
976
|
+
FormFieldBindingSchema,
|
|
977
|
+
FormConfigSchema,
|
|
978
|
+
isValidationRuleType,
|
|
979
|
+
isValidationRule,
|
|
980
|
+
isValidateOnEvent,
|
|
981
|
+
isFormFieldBinding,
|
|
982
|
+
isFormConfig,
|
|
983
|
+
THEME_TOKEN_GROUPS,
|
|
984
|
+
THEME_TOKEN_CSS_PREFIX,
|
|
985
|
+
isSafeThemeTokenKey,
|
|
986
|
+
isSafeThemeTokenValue,
|
|
987
|
+
ThemeTokenKeySchema,
|
|
988
|
+
ThemeTokenValueSchema,
|
|
989
|
+
ThemeTokenMapSchema,
|
|
990
|
+
ThemeSchema,
|
|
991
|
+
themeTokenCssVarName,
|
|
992
|
+
themeTokenRef,
|
|
993
|
+
parseThemeTokenRef,
|
|
994
|
+
mergeThemes,
|
|
995
|
+
themeToCssVariables,
|
|
996
|
+
isTheme,
|
|
997
|
+
SCHEMA_NAME,
|
|
998
|
+
CURRENT_SCHEMA_VERSION,
|
|
999
|
+
AssetReferenceSchema,
|
|
1000
|
+
VariableBindingSchema,
|
|
1001
|
+
ActionBindingSchema,
|
|
1002
|
+
StyleValueSchema,
|
|
1003
|
+
StyleDefinitionSchema,
|
|
1004
|
+
PseudoStateStylesSchema,
|
|
1005
|
+
ResponsiveStylesSchema,
|
|
1006
|
+
AnimationConfigSchema,
|
|
1007
|
+
DEFAULT_ANIMATION_CONFIG,
|
|
1008
|
+
NodeSchema,
|
|
1009
|
+
RootPageNodeSchema,
|
|
1010
|
+
DocumentMetadataSchema,
|
|
1011
|
+
PageDocumentSchema,
|
|
1012
|
+
PROJECT_SCHEMA_NAME,
|
|
1013
|
+
CURRENT_PROJECT_SCHEMA_VERSION,
|
|
1014
|
+
ARTBOARD_REFERENCE_NODE_TYPE,
|
|
1015
|
+
ArtboardTypeSchema,
|
|
1016
|
+
ArtboardSchema,
|
|
1017
|
+
ProjectDocumentSchema,
|
|
1018
|
+
isAssetReference,
|
|
1019
|
+
isVariableBinding,
|
|
1020
|
+
isActionBinding,
|
|
1021
|
+
isAnimationConfig,
|
|
1022
|
+
isProjectDocument,
|
|
1023
|
+
looksLikeProjectDocument,
|
|
1024
|
+
isArtboardReferenceNode,
|
|
1025
|
+
generateDeterministicNodeId,
|
|
1026
|
+
collectNodeIds,
|
|
1027
|
+
validateNodeIdUniqueness,
|
|
1028
|
+
ManifestAssetItemSchema,
|
|
1029
|
+
ManifestSchema,
|
|
1030
|
+
isManifest,
|
|
1031
|
+
isSafeThumbnailUrl,
|
|
1032
|
+
SafeThumbnailUrlSchema,
|
|
1033
|
+
SafeThumbnailObjectSchema,
|
|
1034
|
+
SafeThumbnailSchema,
|
|
1035
|
+
TemplatePackageRefSchema,
|
|
1036
|
+
TemplateRequirementsSchema,
|
|
1037
|
+
TemplateRecordSchema,
|
|
1038
|
+
isTemplateRecord,
|
|
1039
|
+
isSafeThumbnail
|
|
1040
|
+
};
|
|
1041
|
+
//# sourceMappingURL=chunk-MT3M7D67.js.map
|