@kubuild/core 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/document/document-utils.d.ts.map +1 -1
- package/dist/index.cjs +767 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +735 -28
- package/dist/index.js.map +1 -1
- package/dist/io/exporter.d.ts.map +1 -1
- package/dist/io/importer.d.ts +4 -2
- package/dist/io/importer.d.ts.map +1 -1
- package/dist/io/index.d.ts +1 -0
- package/dist/io/index.d.ts.map +1 -1
- package/dist/io/migration.d.ts +21 -2
- package/dist/io/migration.d.ts.map +1 -1
- package/dist/io/tracking-sanitizer.d.ts +23 -0
- package/dist/io/tracking-sanitizer.d.ts.map +1 -0
- package/dist/runtime/binding-resolver.d.ts.map +1 -1
- package/dist/runtime/index.d.ts +2 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/server-tracking.d.ts +143 -0
- package/dist/runtime/server-tracking.d.ts.map +1 -0
- package/dist/runtime/tracking-relay.d.ts +44 -0
- package/dist/runtime/tracking-relay.d.ts.map +1 -0
- package/dist/types/interfaces.d.ts +37 -1
- package/dist/types/interfaces.d.ts.map +1 -1
- package/dist/validation/validator.d.ts +24 -1
- package/dist/validation/validator.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -54,10 +54,12 @@ __export(index_exports, {
|
|
|
54
54
|
createPageArtboard: () => createPageArtboard,
|
|
55
55
|
createRuntimeStore: () => createRuntimeStore,
|
|
56
56
|
createTemplateRecord: () => createTemplateRecord,
|
|
57
|
+
createTrackingRelayHandler: () => createTrackingRelayHandler,
|
|
57
58
|
deepClone: () => deepClone,
|
|
58
59
|
defaultIdGenerator: () => defaultIdGenerator,
|
|
59
60
|
defaultMigrationRegistry: () => defaultMigrationRegistry,
|
|
60
61
|
defaultRenameAssetStrategy: () => defaultRenameAssetStrategy,
|
|
62
|
+
dispatchServerTracking: () => dispatchServerTracking,
|
|
61
63
|
duplicateNode: () => duplicateNode,
|
|
62
64
|
evaluateActionCondition: () => evaluateActionCondition,
|
|
63
65
|
evaluateCondition: () => evaluateCondition,
|
|
@@ -77,6 +79,7 @@ __export(index_exports, {
|
|
|
77
79
|
findMissingComponentNodes: () => findMissingComponentNodes,
|
|
78
80
|
findNodeById: () => findNodeById,
|
|
79
81
|
findNodeLocation: () => findNodeLocation,
|
|
82
|
+
generateTrackingEventId: () => generateTrackingEventId,
|
|
80
83
|
getActiveArtboard: () => getActiveArtboard,
|
|
81
84
|
getAncestorChain: () => getAncestorChain,
|
|
82
85
|
getComponentArtboards: () => getComponentArtboards,
|
|
@@ -86,6 +89,7 @@ __export(index_exports, {
|
|
|
86
89
|
getPageArtboards: () => getPageArtboards,
|
|
87
90
|
getParentNodeId: () => getParentNodeId,
|
|
88
91
|
hasTemplateExpressions: () => hasTemplateExpressions,
|
|
92
|
+
hashSha256: () => hashSha256,
|
|
89
93
|
importPackage: () => importPackage,
|
|
90
94
|
importStoraPackage: () => importStoraPackage,
|
|
91
95
|
insertNode: () => insertNode,
|
|
@@ -103,6 +107,9 @@ __export(index_exports, {
|
|
|
103
107
|
loadProjectDocument: () => loadProjectDocument,
|
|
104
108
|
migrateDocument: () => migrateDocument,
|
|
105
109
|
moveNode: () => moveNode,
|
|
110
|
+
normalizeEmail: () => normalizeEmail,
|
|
111
|
+
normalizePhone: () => normalizePhone,
|
|
112
|
+
normalizeText: () => normalizeText,
|
|
106
113
|
preflightPackage: () => preflightPackage,
|
|
107
114
|
previewImportPackage: () => previewImportPackage,
|
|
108
115
|
remapAssetReferences: () => remapAssetReferences,
|
|
@@ -114,12 +121,19 @@ __export(index_exports, {
|
|
|
114
121
|
resolveBinding: () => resolveBinding,
|
|
115
122
|
resolveBindingValue: () => resolveBindingValue,
|
|
116
123
|
resolvePropertyPath: () => resolvePropertyPath,
|
|
124
|
+
sanitizeDocumentTracking: () => sanitizeDocumentTracking,
|
|
117
125
|
sanitizeFilename: () => sanitizeFilename,
|
|
118
126
|
sanitizeHtml: () => sanitizeHtml,
|
|
119
127
|
sanitizeUrl: () => sanitizeUrl,
|
|
120
128
|
saveDraftAsTemplate: () => saveDraftAsTemplate,
|
|
129
|
+
sendCustomWebhookEvent: () => sendCustomWebhookEvent,
|
|
130
|
+
sendGa4MeasurementEvent: () => sendGa4MeasurementEvent,
|
|
131
|
+
sendMetaCapiEvent: () => sendMetaCapiEvent,
|
|
132
|
+
sendTikTokEventsApi: () => sendTikTokEventsApi,
|
|
121
133
|
setActiveArtboard: () => setActiveArtboard,
|
|
122
134
|
sha256Sync: () => sha256Sync,
|
|
135
|
+
stripDocumentTrackingSecretsInPlace: () => stripDocumentTrackingSecretsInPlace,
|
|
136
|
+
stripTrackingSecretsInPlace: () => stripTrackingSecretsInPlace,
|
|
123
137
|
ungroupNodeFrame: () => ungroupNodeFrame,
|
|
124
138
|
updateActions: () => updateActions,
|
|
125
139
|
updateAnimation: () => updateAnimation,
|
|
@@ -145,7 +159,7 @@ var import_schema = require("@kubuild/schema");
|
|
|
145
159
|
function createBlankDocument(title = "Untitled Page") {
|
|
146
160
|
return {
|
|
147
161
|
schema: import_schema.SCHEMA_NAME,
|
|
148
|
-
version:
|
|
162
|
+
version: import_schema.CURRENT_SCHEMA_VERSION,
|
|
149
163
|
metadata: {
|
|
150
164
|
title,
|
|
151
165
|
description: "",
|
|
@@ -1541,6 +1555,15 @@ var DocumentHistoryManager = class {
|
|
|
1541
1555
|
var FORBIDDEN_KEY_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
1542
1556
|
function resolveBinding(binding, context) {
|
|
1543
1557
|
const segments = binding.key.split(".").filter(Boolean);
|
|
1558
|
+
if (segments.some((segment) => FORBIDDEN_KEY_SEGMENTS.has(segment))) {
|
|
1559
|
+
return applyMissingPolicy(binding);
|
|
1560
|
+
}
|
|
1561
|
+
if (context?.variables && typeof context.variables === "object" && Object.prototype.hasOwnProperty.call(context.variables, binding.key)) {
|
|
1562
|
+
const directValue = context.variables[binding.key];
|
|
1563
|
+
if (directValue !== void 0 && typeof directValue !== "function") {
|
|
1564
|
+
return { status: "resolved", value: directValue };
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1544
1567
|
let current = context?.variables;
|
|
1545
1568
|
for (const segment of segments) {
|
|
1546
1569
|
if (FORBIDDEN_KEY_SEGMENTS.has(segment)) {
|
|
@@ -2112,14 +2135,14 @@ var ActionPipelineExecutor = class {
|
|
|
2112
2135
|
async executeSingleStep(step, context, parentSignal, options) {
|
|
2113
2136
|
const stepStartTime = Date.now();
|
|
2114
2137
|
if (step.condition && !evaluateActionCondition(step.condition, context)) {
|
|
2115
|
-
const
|
|
2138
|
+
const skippedResult2 = {
|
|
2116
2139
|
stepId: step.id,
|
|
2117
2140
|
stepType: step.type,
|
|
2118
2141
|
status: "skipped",
|
|
2119
2142
|
durationMs: Date.now() - stepStartTime
|
|
2120
2143
|
};
|
|
2121
|
-
options?.onStepComplete?.(step,
|
|
2122
|
-
return
|
|
2144
|
+
options?.onStepComplete?.(step, skippedResult2, context);
|
|
2145
|
+
return skippedResult2;
|
|
2123
2146
|
}
|
|
2124
2147
|
options?.onStepStart?.(step, context);
|
|
2125
2148
|
const stepTimeout = step.timeout;
|
|
@@ -2584,12 +2607,559 @@ function createRuntimeStore(initialState) {
|
|
|
2584
2607
|
return new RuntimeStateStore(initialState);
|
|
2585
2608
|
}
|
|
2586
2609
|
|
|
2610
|
+
// src/runtime/server-tracking.ts
|
|
2611
|
+
function normalizeEmail(email) {
|
|
2612
|
+
return (email || "").trim().toLowerCase();
|
|
2613
|
+
}
|
|
2614
|
+
function normalizePhone(phone) {
|
|
2615
|
+
return (phone || "").replace(/[^0-9]/g, "");
|
|
2616
|
+
}
|
|
2617
|
+
function normalizeText(text) {
|
|
2618
|
+
return (text || "").trim().toLowerCase();
|
|
2619
|
+
}
|
|
2620
|
+
async function hashSha256(value) {
|
|
2621
|
+
const normalized = (value || "").trim();
|
|
2622
|
+
if (!normalized) return "";
|
|
2623
|
+
if (typeof globalThis.crypto?.subtle?.digest === "function") {
|
|
2624
|
+
const encoder = new TextEncoder();
|
|
2625
|
+
const data = encoder.encode(normalized);
|
|
2626
|
+
const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", data);
|
|
2627
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
2628
|
+
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2629
|
+
}
|
|
2630
|
+
throw new Error("Web Crypto API (crypto.subtle) is not available in current runtime environment.");
|
|
2631
|
+
}
|
|
2632
|
+
function generateTrackingEventId(prefix = "evt") {
|
|
2633
|
+
const timestamp = Date.now().toString(36);
|
|
2634
|
+
const randomPart = Math.random().toString(36).substring(2, 10);
|
|
2635
|
+
return `${prefix}_${timestamp}_${randomPart}`;
|
|
2636
|
+
}
|
|
2637
|
+
async function sendMetaCapiEvent(event, config, secrets, options) {
|
|
2638
|
+
const fetcher = options?.fetchFn || globalThis.fetch;
|
|
2639
|
+
if (!config.pixelId) {
|
|
2640
|
+
return {
|
|
2641
|
+
provider: "meta",
|
|
2642
|
+
success: false,
|
|
2643
|
+
error: "Meta Pixel ID is missing"
|
|
2644
|
+
};
|
|
2645
|
+
}
|
|
2646
|
+
const rawUserData = event.userData || {};
|
|
2647
|
+
const hashedUserData = {};
|
|
2648
|
+
if (rawUserData.email) {
|
|
2649
|
+
hashedUserData.em = [await hashSha256(normalizeEmail(String(rawUserData.email)))];
|
|
2650
|
+
}
|
|
2651
|
+
if (rawUserData.phone) {
|
|
2652
|
+
hashedUserData.ph = [await hashSha256(normalizePhone(String(rawUserData.phone)))];
|
|
2653
|
+
}
|
|
2654
|
+
if (rawUserData.firstName) {
|
|
2655
|
+
hashedUserData.fn = [await hashSha256(normalizeText(String(rawUserData.firstName)))];
|
|
2656
|
+
}
|
|
2657
|
+
if (rawUserData.lastName) {
|
|
2658
|
+
hashedUserData.ln = [await hashSha256(normalizeText(String(rawUserData.lastName)))];
|
|
2659
|
+
}
|
|
2660
|
+
if (rawUserData.city) {
|
|
2661
|
+
hashedUserData.ct = [await hashSha256(normalizeText(String(rawUserData.city)))];
|
|
2662
|
+
}
|
|
2663
|
+
if (rawUserData.state) {
|
|
2664
|
+
hashedUserData.st = [await hashSha256(normalizeText(String(rawUserData.state)))];
|
|
2665
|
+
}
|
|
2666
|
+
if (rawUserData.zip) {
|
|
2667
|
+
hashedUserData.zp = [await hashSha256(normalizeText(String(rawUserData.zip)))];
|
|
2668
|
+
}
|
|
2669
|
+
if (rawUserData.country) {
|
|
2670
|
+
hashedUserData.country = [await hashSha256(normalizeText(String(rawUserData.country)))];
|
|
2671
|
+
}
|
|
2672
|
+
const clientIp = rawUserData.clientIp || options?.clientIp;
|
|
2673
|
+
if (clientIp) hashedUserData.client_ip_address = clientIp;
|
|
2674
|
+
const clientUserAgent = rawUserData.clientUserAgent || options?.clientUserAgent;
|
|
2675
|
+
if (clientUserAgent) hashedUserData.client_user_agent = clientUserAgent;
|
|
2676
|
+
if (rawUserData.fbp) hashedUserData.fbp = rawUserData.fbp;
|
|
2677
|
+
if (rawUserData.fbc) hashedUserData.fbc = rawUserData.fbc;
|
|
2678
|
+
if (rawUserData.externalId) {
|
|
2679
|
+
hashedUserData.external_id = [await hashSha256(String(rawUserData.externalId))];
|
|
2680
|
+
}
|
|
2681
|
+
const eventTime = event.eventTime || Math.floor(Date.now() / 1e3);
|
|
2682
|
+
const eventSourceUrl = event.eventSourceUrl || options?.sourceUrl;
|
|
2683
|
+
const capiPayload = {
|
|
2684
|
+
data: [
|
|
2685
|
+
{
|
|
2686
|
+
event_name: event.eventName,
|
|
2687
|
+
event_time: eventTime,
|
|
2688
|
+
event_id: event.eventId,
|
|
2689
|
+
event_source_url: eventSourceUrl,
|
|
2690
|
+
action_source: event.actionSource || "website",
|
|
2691
|
+
user_data: hashedUserData,
|
|
2692
|
+
custom_data: {
|
|
2693
|
+
...event.params || {},
|
|
2694
|
+
...event.customData || {}
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
]
|
|
2698
|
+
};
|
|
2699
|
+
if (config.testEventCode) {
|
|
2700
|
+
capiPayload.test_event_code = config.testEventCode;
|
|
2701
|
+
}
|
|
2702
|
+
if (options?.simulateInDebug) {
|
|
2703
|
+
options.onLog?.("[Meta CAPI SIMULATED]", capiPayload);
|
|
2704
|
+
return { provider: "meta", success: true, data: { simulated: true, payload: capiPayload } };
|
|
2705
|
+
}
|
|
2706
|
+
try {
|
|
2707
|
+
const url = `https://graph.facebook.com/v19.0/${encodeURIComponent(config.pixelId)}/events`;
|
|
2708
|
+
const headers = {
|
|
2709
|
+
"Content-Type": "application/json"
|
|
2710
|
+
};
|
|
2711
|
+
if (!secrets?.capiAccessToken) {
|
|
2712
|
+
return skippedResult("meta", "No Meta CAPI access token resolved for this credential");
|
|
2713
|
+
}
|
|
2714
|
+
headers["Authorization"] = `Bearer ${secrets.capiAccessToken}`;
|
|
2715
|
+
const res = await fetcher(url, {
|
|
2716
|
+
method: "POST",
|
|
2717
|
+
headers,
|
|
2718
|
+
body: JSON.stringify(capiPayload)
|
|
2719
|
+
});
|
|
2720
|
+
const resJson = await res.json().catch(() => ({}));
|
|
2721
|
+
if (!res.ok) {
|
|
2722
|
+
return {
|
|
2723
|
+
provider: "meta",
|
|
2724
|
+
success: false,
|
|
2725
|
+
status: res.status,
|
|
2726
|
+
error: resJson?.error?.message || `HTTP ${res.status}`,
|
|
2727
|
+
data: resJson
|
|
2728
|
+
};
|
|
2729
|
+
}
|
|
2730
|
+
return {
|
|
2731
|
+
provider: "meta",
|
|
2732
|
+
success: true,
|
|
2733
|
+
status: res.status,
|
|
2734
|
+
data: resJson
|
|
2735
|
+
};
|
|
2736
|
+
} catch (err) {
|
|
2737
|
+
return {
|
|
2738
|
+
provider: "meta",
|
|
2739
|
+
success: false,
|
|
2740
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2741
|
+
};
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
async function sendTikTokEventsApi(event, config, secrets, options) {
|
|
2745
|
+
const fetcher = options?.fetchFn || globalThis.fetch;
|
|
2746
|
+
if (!config.pixelId) {
|
|
2747
|
+
return { provider: "tiktok", success: false, error: "TikTok Pixel ID is missing" };
|
|
2748
|
+
}
|
|
2749
|
+
const rawUserData = event.userData || {};
|
|
2750
|
+
const user = {};
|
|
2751
|
+
if (rawUserData.email) {
|
|
2752
|
+
user.email = await hashSha256(normalizeEmail(String(rawUserData.email)));
|
|
2753
|
+
}
|
|
2754
|
+
if (rawUserData.phone) {
|
|
2755
|
+
user.phone_number = await hashSha256(normalizePhone(String(rawUserData.phone)));
|
|
2756
|
+
}
|
|
2757
|
+
if (rawUserData.clientIp || options?.clientIp) {
|
|
2758
|
+
user.ip = rawUserData.clientIp || options?.clientIp;
|
|
2759
|
+
}
|
|
2760
|
+
if (rawUserData.clientUserAgent || options?.clientUserAgent) {
|
|
2761
|
+
user.user_agent = rawUserData.clientUserAgent || options?.clientUserAgent;
|
|
2762
|
+
}
|
|
2763
|
+
const tiktokPayload = {
|
|
2764
|
+
event_source: "web",
|
|
2765
|
+
event_source_id: config.pixelId,
|
|
2766
|
+
data: [
|
|
2767
|
+
{
|
|
2768
|
+
event: event.eventName,
|
|
2769
|
+
event_id: event.eventId,
|
|
2770
|
+
timestamp: new Date(event.eventTime ? event.eventTime * 1e3 : Date.now()).toISOString(),
|
|
2771
|
+
user,
|
|
2772
|
+
properties: {
|
|
2773
|
+
...event.params || {},
|
|
2774
|
+
...event.customData || {}
|
|
2775
|
+
},
|
|
2776
|
+
page: {
|
|
2777
|
+
url: event.eventSourceUrl || options?.sourceUrl
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
]
|
|
2781
|
+
};
|
|
2782
|
+
if (config.testEventCode) {
|
|
2783
|
+
tiktokPayload.test_event_code = config.testEventCode;
|
|
2784
|
+
}
|
|
2785
|
+
if (options?.simulateInDebug) {
|
|
2786
|
+
options.onLog?.("[TikTok Events API SIMULATED]", tiktokPayload);
|
|
2787
|
+
return { provider: "tiktok", success: true, data: { simulated: true, payload: tiktokPayload } };
|
|
2788
|
+
}
|
|
2789
|
+
try {
|
|
2790
|
+
const url = "https://business-api.tiktok.com/open_api/v1.3/event/track/";
|
|
2791
|
+
const headers = {
|
|
2792
|
+
"Content-Type": "application/json"
|
|
2793
|
+
};
|
|
2794
|
+
if (!secrets?.accessToken) {
|
|
2795
|
+
return skippedResult("tiktok", "No TikTok Events API access token resolved for this credential");
|
|
2796
|
+
}
|
|
2797
|
+
headers["Access-Token"] = secrets.accessToken;
|
|
2798
|
+
const res = await fetcher(url, {
|
|
2799
|
+
method: "POST",
|
|
2800
|
+
headers,
|
|
2801
|
+
body: JSON.stringify(tiktokPayload)
|
|
2802
|
+
});
|
|
2803
|
+
const resJson = await res.json().catch(() => ({}));
|
|
2804
|
+
if (!res.ok) {
|
|
2805
|
+
return {
|
|
2806
|
+
provider: "tiktok",
|
|
2807
|
+
success: false,
|
|
2808
|
+
status: res.status,
|
|
2809
|
+
error: resJson?.message || `HTTP ${res.status}`,
|
|
2810
|
+
data: resJson
|
|
2811
|
+
};
|
|
2812
|
+
}
|
|
2813
|
+
return { provider: "tiktok", success: true, status: res.status, data: resJson };
|
|
2814
|
+
} catch (err) {
|
|
2815
|
+
return {
|
|
2816
|
+
provider: "tiktok",
|
|
2817
|
+
success: false,
|
|
2818
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2819
|
+
};
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
async function sendGa4MeasurementEvent(event, config, secrets, options) {
|
|
2823
|
+
const fetcher = options?.fetchFn || globalThis.fetch;
|
|
2824
|
+
if (!config.measurementId) {
|
|
2825
|
+
return { provider: "google", success: false, error: "GA4 Measurement ID is missing" };
|
|
2826
|
+
}
|
|
2827
|
+
const clientId = event.userData?.clientId || event.userData?.externalId || "anonymous_client";
|
|
2828
|
+
const ga4Payload = {
|
|
2829
|
+
client_id: clientId,
|
|
2830
|
+
events: [
|
|
2831
|
+
{
|
|
2832
|
+
name: event.eventName.toLowerCase().replace(/[^a-z0-9_]/g, "_"),
|
|
2833
|
+
params: {
|
|
2834
|
+
...event.params || {},
|
|
2835
|
+
...event.customData || {},
|
|
2836
|
+
event_id: event.eventId
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
]
|
|
2840
|
+
};
|
|
2841
|
+
if (options?.simulateInDebug) {
|
|
2842
|
+
options.onLog?.("[GA4 Measurement Protocol SIMULATED]", ga4Payload);
|
|
2843
|
+
return { provider: "google", success: true, data: { simulated: true, payload: ga4Payload } };
|
|
2844
|
+
}
|
|
2845
|
+
try {
|
|
2846
|
+
if (!secrets?.measurementProtocolSecret) {
|
|
2847
|
+
return skippedResult("google", "No GA4 Measurement Protocol API secret resolved for this credential");
|
|
2848
|
+
}
|
|
2849
|
+
const url = `https://www.google-analytics.com/mp/collect?measurement_id=${encodeURIComponent(config.measurementId)}&api_secret=${encodeURIComponent(secrets.measurementProtocolSecret)}`;
|
|
2850
|
+
const res = await fetcher(url, {
|
|
2851
|
+
method: "POST",
|
|
2852
|
+
headers: { "Content-Type": "application/json" },
|
|
2853
|
+
body: JSON.stringify(ga4Payload)
|
|
2854
|
+
});
|
|
2855
|
+
return {
|
|
2856
|
+
provider: "google",
|
|
2857
|
+
success: res.ok,
|
|
2858
|
+
status: res.status,
|
|
2859
|
+
data: { status: res.status }
|
|
2860
|
+
};
|
|
2861
|
+
} catch (err) {
|
|
2862
|
+
return {
|
|
2863
|
+
provider: "google",
|
|
2864
|
+
success: false,
|
|
2865
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2866
|
+
};
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
async function sendCustomWebhookEvent(event, _config, secrets, options) {
|
|
2870
|
+
const fetcher = options?.fetchFn || globalThis.fetch;
|
|
2871
|
+
const webhookPayload = {
|
|
2872
|
+
event: event.eventName,
|
|
2873
|
+
eventId: event.eventId,
|
|
2874
|
+
timestamp: event.eventTime ? event.eventTime * 1e3 : Date.now(),
|
|
2875
|
+
userData: event.userData,
|
|
2876
|
+
params: event.params,
|
|
2877
|
+
customData: event.customData,
|
|
2878
|
+
sourceUrl: event.eventSourceUrl || options?.sourceUrl
|
|
2879
|
+
};
|
|
2880
|
+
if (options?.simulateInDebug) {
|
|
2881
|
+
options.onLog?.("[Custom Webhook SIMULATED]", webhookPayload);
|
|
2882
|
+
return { provider: "custom", success: true, data: { simulated: true, payload: webhookPayload } };
|
|
2883
|
+
}
|
|
2884
|
+
if (!secrets?.endpointUrl) {
|
|
2885
|
+
return skippedResult("custom", "No custom webhook destination resolved for this credential");
|
|
2886
|
+
}
|
|
2887
|
+
try {
|
|
2888
|
+
const res = await fetcher(secrets.endpointUrl, {
|
|
2889
|
+
method: "POST",
|
|
2890
|
+
headers: {
|
|
2891
|
+
"Content-Type": "application/json",
|
|
2892
|
+
...secrets.headers || {}
|
|
2893
|
+
},
|
|
2894
|
+
body: JSON.stringify(webhookPayload)
|
|
2895
|
+
});
|
|
2896
|
+
const resJson = await res.json().catch(() => ({}));
|
|
2897
|
+
return {
|
|
2898
|
+
provider: "custom",
|
|
2899
|
+
success: res.ok,
|
|
2900
|
+
status: res.status,
|
|
2901
|
+
data: resJson
|
|
2902
|
+
};
|
|
2903
|
+
} catch (err) {
|
|
2904
|
+
return {
|
|
2905
|
+
provider: "custom",
|
|
2906
|
+
success: false,
|
|
2907
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2908
|
+
};
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
function skippedResult(provider, reason) {
|
|
2912
|
+
return { provider, success: true, skipped: true, reason };
|
|
2913
|
+
}
|
|
2914
|
+
async function dispatchServerTracking(event, config, options) {
|
|
2915
|
+
const eventId = event.eventId || generateTrackingEventId();
|
|
2916
|
+
const eventWithId = { ...event, eventId };
|
|
2917
|
+
if (!config || config.enabled === false) {
|
|
2918
|
+
return {
|
|
2919
|
+
success: true,
|
|
2920
|
+
eventId,
|
|
2921
|
+
skipped: true,
|
|
2922
|
+
reason: "Tracking is disabled globally",
|
|
2923
|
+
results: {}
|
|
2924
|
+
};
|
|
2925
|
+
}
|
|
2926
|
+
const isDebug = Boolean(config.debugMode);
|
|
2927
|
+
const simulate = isDebug && options?.simulateInDebug !== false;
|
|
2928
|
+
const mergedOptions = {
|
|
2929
|
+
...options,
|
|
2930
|
+
simulateInDebug: simulate,
|
|
2931
|
+
onLog: (msg, data) => {
|
|
2932
|
+
if (isDebug) {
|
|
2933
|
+
console.log(`[KUBUILD Tracking] ${msg}`, data || "");
|
|
2934
|
+
}
|
|
2935
|
+
options?.onLog?.(msg, data);
|
|
2936
|
+
}
|
|
2937
|
+
};
|
|
2938
|
+
const target = options?.provider || "all";
|
|
2939
|
+
const wants = (key) => target === "all" || target === key;
|
|
2940
|
+
const providers = config.providers || {};
|
|
2941
|
+
const tasks = [];
|
|
2942
|
+
const meta = providers.meta;
|
|
2943
|
+
if (wants("meta") && meta && meta.enabled !== false && meta.capiEnabled) {
|
|
2944
|
+
tasks.push({
|
|
2945
|
+
key: "meta",
|
|
2946
|
+
credentialId: meta.credentialId,
|
|
2947
|
+
run: (s) => sendMetaCapiEvent(eventWithId, meta, s, mergedOptions)
|
|
2948
|
+
});
|
|
2949
|
+
}
|
|
2950
|
+
const tiktok = providers.tiktok;
|
|
2951
|
+
if (wants("tiktok") && tiktok && tiktok.enabled !== false && tiktok.eventsApiEnabled) {
|
|
2952
|
+
tasks.push({
|
|
2953
|
+
key: "tiktok",
|
|
2954
|
+
credentialId: tiktok.credentialId,
|
|
2955
|
+
run: (s) => sendTikTokEventsApi(eventWithId, tiktok, s, mergedOptions)
|
|
2956
|
+
});
|
|
2957
|
+
}
|
|
2958
|
+
const google = providers.google;
|
|
2959
|
+
if (wants("google") && google && google.enabled !== false && google.measurementId) {
|
|
2960
|
+
tasks.push({
|
|
2961
|
+
key: "google",
|
|
2962
|
+
credentialId: google.credentialId,
|
|
2963
|
+
run: (s) => sendGa4MeasurementEvent(eventWithId, google, s, mergedOptions)
|
|
2964
|
+
});
|
|
2965
|
+
}
|
|
2966
|
+
const custom = providers.custom;
|
|
2967
|
+
if (wants("custom") && custom && custom.enabled !== false) {
|
|
2968
|
+
tasks.push({
|
|
2969
|
+
key: "custom",
|
|
2970
|
+
credentialId: custom.credentialId,
|
|
2971
|
+
run: (s) => sendCustomWebhookEvent(eventWithId, custom, s, mergedOptions)
|
|
2972
|
+
});
|
|
2973
|
+
}
|
|
2974
|
+
if (tasks.length === 0) {
|
|
2975
|
+
return {
|
|
2976
|
+
success: true,
|
|
2977
|
+
eventId,
|
|
2978
|
+
skipped: true,
|
|
2979
|
+
reason: target === "gtm" ? "GTM is client-side only (dataLayer); no server delivery" : "No server-side tracking provider is enabled",
|
|
2980
|
+
results: {}
|
|
2981
|
+
};
|
|
2982
|
+
}
|
|
2983
|
+
const resolve = options?.resolveSecrets;
|
|
2984
|
+
const runTask = async (task) => {
|
|
2985
|
+
let secrets = null;
|
|
2986
|
+
if (resolve) {
|
|
2987
|
+
try {
|
|
2988
|
+
secrets = await resolve({ provider: task.key, credentialId: task.credentialId, documentId: options?.documentId }) ?? null;
|
|
2989
|
+
} catch (err) {
|
|
2990
|
+
return {
|
|
2991
|
+
provider: task.key,
|
|
2992
|
+
success: false,
|
|
2993
|
+
error: `Secret resolution failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2994
|
+
};
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
if (!secrets && !simulate) {
|
|
2998
|
+
const reason = resolve ? `No secret resolved for provider "${task.key}"${task.credentialId ? ` (credentialId "${task.credentialId}")` : " (no credentialId linked)"}` : "No secret resolver configured (options.resolveSecrets)";
|
|
2999
|
+
mergedOptions.onLog?.(`[${task.key}] skipped: ${reason}`);
|
|
3000
|
+
return skippedResult(task.key, reason);
|
|
3001
|
+
}
|
|
3002
|
+
return task.run(secrets);
|
|
3003
|
+
};
|
|
3004
|
+
const settled = await Promise.allSettled(tasks.map((task) => runTask(task)));
|
|
3005
|
+
const results = {};
|
|
3006
|
+
let overallSuccess = true;
|
|
3007
|
+
let delivered = 0;
|
|
3008
|
+
settled.forEach((item, index) => {
|
|
3009
|
+
const key = tasks[index].key;
|
|
3010
|
+
if (item.status === "fulfilled") {
|
|
3011
|
+
results[key] = item.value;
|
|
3012
|
+
if (!item.value.success) overallSuccess = false;
|
|
3013
|
+
if (!item.value.skipped) delivered++;
|
|
3014
|
+
} else {
|
|
3015
|
+
overallSuccess = false;
|
|
3016
|
+
results[key] = {
|
|
3017
|
+
provider: key,
|
|
3018
|
+
success: false,
|
|
3019
|
+
error: item.reason instanceof Error ? item.reason.message : String(item.reason)
|
|
3020
|
+
};
|
|
3021
|
+
}
|
|
3022
|
+
});
|
|
3023
|
+
const allSkipped = delivered === 0 && overallSuccess;
|
|
3024
|
+
return {
|
|
3025
|
+
success: overallSuccess,
|
|
3026
|
+
eventId,
|
|
3027
|
+
...allSkipped ? { skipped: true, reason: "All server providers were skipped" } : {},
|
|
3028
|
+
results
|
|
3029
|
+
};
|
|
3030
|
+
}
|
|
3031
|
+
|
|
3032
|
+
// src/runtime/tracking-relay.ts
|
|
3033
|
+
var import_schema5 = require("@kubuild/schema");
|
|
3034
|
+
var DEFAULT_MAX_BODY_BYTES = 64 * 1024;
|
|
3035
|
+
function defaultClientIp(request) {
|
|
3036
|
+
const forwarded = request.headers.get("x-forwarded-for");
|
|
3037
|
+
if (forwarded) {
|
|
3038
|
+
const first = forwarded.split(",")[0]?.trim();
|
|
3039
|
+
if (first) return first;
|
|
3040
|
+
}
|
|
3041
|
+
return request.headers.get("cf-connecting-ip")?.trim() || request.headers.get("x-real-ip")?.trim() || void 0;
|
|
3042
|
+
}
|
|
3043
|
+
function isOriginAllowed(origin, allowed) {
|
|
3044
|
+
if (!allowed) return true;
|
|
3045
|
+
if (!origin) return false;
|
|
3046
|
+
if (typeof allowed === "function") return allowed(origin);
|
|
3047
|
+
return allowed.includes(origin);
|
|
3048
|
+
}
|
|
3049
|
+
function createTrackingRelayHandler(options) {
|
|
3050
|
+
const maxBodyBytes = options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;
|
|
3051
|
+
return async (request) => {
|
|
3052
|
+
const origin = request.headers.get("origin");
|
|
3053
|
+
const corsHeaders = {};
|
|
3054
|
+
if (options.allowedOrigins && origin && isOriginAllowed(origin, options.allowedOrigins)) {
|
|
3055
|
+
corsHeaders["Access-Control-Allow-Origin"] = origin;
|
|
3056
|
+
corsHeaders["Vary"] = "Origin";
|
|
3057
|
+
}
|
|
3058
|
+
const json = (body, status, extra) => new Response(JSON.stringify(body), {
|
|
3059
|
+
status,
|
|
3060
|
+
headers: { "Content-Type": "application/json", ...corsHeaders, ...extra || {} }
|
|
3061
|
+
});
|
|
3062
|
+
const fail = (code, message, status, extra) => json({ version: import_schema5.TRACKING_RELAY_PROTOCOL_VERSION, success: false, error: { code, message } }, status, extra);
|
|
3063
|
+
if (!isOriginAllowed(origin, options.allowedOrigins)) {
|
|
3064
|
+
return fail("FORBIDDEN_ORIGIN", "Origin is not allowed", 403);
|
|
3065
|
+
}
|
|
3066
|
+
if (request.method === "OPTIONS") {
|
|
3067
|
+
return new Response(null, {
|
|
3068
|
+
status: 204,
|
|
3069
|
+
headers: {
|
|
3070
|
+
...corsHeaders,
|
|
3071
|
+
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
|
3072
|
+
"Access-Control-Allow-Headers": "Content-Type",
|
|
3073
|
+
"Access-Control-Max-Age": "600"
|
|
3074
|
+
}
|
|
3075
|
+
});
|
|
3076
|
+
}
|
|
3077
|
+
if (request.method !== "POST") {
|
|
3078
|
+
return fail("METHOD_NOT_ALLOWED", "Only POST requests are accepted", 405, { Allow: "POST, OPTIONS" });
|
|
3079
|
+
}
|
|
3080
|
+
try {
|
|
3081
|
+
const text = await request.text();
|
|
3082
|
+
if (text.length > maxBodyBytes) {
|
|
3083
|
+
return fail("INVALID_REQUEST", `Request body exceeds ${maxBodyBytes} bytes`, 413);
|
|
3084
|
+
}
|
|
3085
|
+
let raw;
|
|
3086
|
+
try {
|
|
3087
|
+
raw = JSON.parse(text);
|
|
3088
|
+
} catch {
|
|
3089
|
+
return fail("INVALID_REQUEST", "Request body must be valid JSON", 400);
|
|
3090
|
+
}
|
|
3091
|
+
if (raw && typeof raw === "object" && "version" in raw && raw.version !== import_schema5.TRACKING_RELAY_PROTOCOL_VERSION) {
|
|
3092
|
+
return fail(
|
|
3093
|
+
"UNSUPPORTED_VERSION",
|
|
3094
|
+
`Unsupported relay protocol version; expected ${import_schema5.TRACKING_RELAY_PROTOCOL_VERSION}`,
|
|
3095
|
+
400
|
|
3096
|
+
);
|
|
3097
|
+
}
|
|
3098
|
+
const parsed = import_schema5.TrackingRelayRequestSchema.safeParse(raw);
|
|
3099
|
+
if (!parsed.success) {
|
|
3100
|
+
const issues = parsed.error.issues.slice(0, 5).map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
|
|
3101
|
+
return fail("INVALID_REQUEST", `Invalid relay request: ${issues}`, 400);
|
|
3102
|
+
}
|
|
3103
|
+
const body = parsed.data;
|
|
3104
|
+
const config = await options.getConfig({
|
|
3105
|
+
request,
|
|
3106
|
+
documentId: body.documentId,
|
|
3107
|
+
credentialId: body.credentialId,
|
|
3108
|
+
provider: body.provider
|
|
3109
|
+
});
|
|
3110
|
+
if (!config) {
|
|
3111
|
+
return fail("CONFIG_NOT_FOUND", "No tracking configuration found for this page", 404);
|
|
3112
|
+
}
|
|
3113
|
+
const clientIp = (options.getClientIp ?? defaultClientIp)(request);
|
|
3114
|
+
const clientUserAgent = request.headers.get("user-agent") || void 0;
|
|
3115
|
+
const userData = { ...body.event.userData || {} };
|
|
3116
|
+
delete userData.clientIp;
|
|
3117
|
+
delete userData.clientUserAgent;
|
|
3118
|
+
const event = { ...body.event, userData };
|
|
3119
|
+
const outcome = await dispatchServerTracking(event, config, {
|
|
3120
|
+
resolveSecrets: options.resolveSecrets,
|
|
3121
|
+
documentId: body.documentId,
|
|
3122
|
+
provider: body.provider,
|
|
3123
|
+
fetchFn: options.fetchFn,
|
|
3124
|
+
clientIp,
|
|
3125
|
+
clientUserAgent,
|
|
3126
|
+
onLog: options.onLog
|
|
3127
|
+
});
|
|
3128
|
+
const results = {};
|
|
3129
|
+
for (const [key, r] of Object.entries(outcome.results)) {
|
|
3130
|
+
results[key] = {
|
|
3131
|
+
provider: r.provider,
|
|
3132
|
+
success: r.success,
|
|
3133
|
+
...r.status !== void 0 ? { status: r.status } : {},
|
|
3134
|
+
...r.skipped ? { skipped: true } : {},
|
|
3135
|
+
...r.reason ? { reason: r.reason } : {},
|
|
3136
|
+
...r.error ? { error: r.error } : {}
|
|
3137
|
+
};
|
|
3138
|
+
}
|
|
3139
|
+
return json(
|
|
3140
|
+
{
|
|
3141
|
+
version: import_schema5.TRACKING_RELAY_PROTOCOL_VERSION,
|
|
3142
|
+
success: outcome.success,
|
|
3143
|
+
eventId: outcome.eventId,
|
|
3144
|
+
...outcome.skipped ? { skipped: true } : {},
|
|
3145
|
+
...outcome.reason ? { reason: outcome.reason } : {},
|
|
3146
|
+
results
|
|
3147
|
+
},
|
|
3148
|
+
200
|
|
3149
|
+
);
|
|
3150
|
+
} catch (err) {
|
|
3151
|
+
options.onLog?.("[Tracking relay] internal error", err);
|
|
3152
|
+
return fail("INTERNAL_ERROR", "Tracking relay failed", 500);
|
|
3153
|
+
}
|
|
3154
|
+
};
|
|
3155
|
+
}
|
|
3156
|
+
|
|
2587
3157
|
// src/io/exporter.ts
|
|
2588
3158
|
var import_fflate = require("fflate");
|
|
2589
|
-
var
|
|
3159
|
+
var import_schema8 = require("@kubuild/schema");
|
|
2590
3160
|
|
|
2591
3161
|
// src/validation/validator.ts
|
|
2592
|
-
var
|
|
3162
|
+
var import_schema6 = require("@kubuild/schema");
|
|
2593
3163
|
|
|
2594
3164
|
// src/validation/security.ts
|
|
2595
3165
|
var DEFAULT_DOCUMENT_SECURITY_LIMITS = {
|
|
@@ -2878,10 +3448,10 @@ function validateDocument(input, options = {}) {
|
|
|
2878
3448
|
};
|
|
2879
3449
|
}
|
|
2880
3450
|
}
|
|
2881
|
-
if (doc.schema !==
|
|
3451
|
+
if (doc.schema !== import_schema6.SCHEMA_NAME) {
|
|
2882
3452
|
errors.push({
|
|
2883
3453
|
code: "GLOBAL_SCHEMA_INVALID",
|
|
2884
|
-
message: `Invalid schema identifier. Expected "${
|
|
3454
|
+
message: `Invalid schema identifier. Expected "${import_schema6.SCHEMA_NAME}", received "${String(doc.schema)}"`,
|
|
2885
3455
|
path: "/schema"
|
|
2886
3456
|
});
|
|
2887
3457
|
}
|
|
@@ -2945,7 +3515,7 @@ function validateDocument(input, options = {}) {
|
|
|
2945
3515
|
warnings
|
|
2946
3516
|
);
|
|
2947
3517
|
if (errors.length === 0) {
|
|
2948
|
-
const zodResult =
|
|
3518
|
+
const zodResult = import_schema6.PageDocumentSchema.safeParse(input);
|
|
2949
3519
|
if (!zodResult.success) {
|
|
2950
3520
|
for (const issue of zodResult.error.issues) {
|
|
2951
3521
|
const jsonPath = "/" + issue.path.join("/");
|
|
@@ -3125,7 +3695,8 @@ function validateNodeRecursive(nodeObj, currentPath, parentNode, seenIds, visite
|
|
|
3125
3695
|
`${currentPath}/props`,
|
|
3126
3696
|
effectiveNodeId,
|
|
3127
3697
|
options,
|
|
3128
|
-
errors
|
|
3698
|
+
errors,
|
|
3699
|
+
warnings
|
|
3129
3700
|
);
|
|
3130
3701
|
}
|
|
3131
3702
|
}
|
|
@@ -3140,7 +3711,10 @@ function validateNodeRecursive(nodeObj, currentPath, parentNode, seenIds, visite
|
|
|
3140
3711
|
}
|
|
3141
3712
|
}
|
|
3142
3713
|
}
|
|
3143
|
-
function validatePropsBindings(propsObj, propsPath, nodeId, options, errors) {
|
|
3714
|
+
function validatePropsBindings(propsObj, propsPath, nodeId, options, errors, warnings) {
|
|
3715
|
+
const checkAssets = options.checkAssetReferences !== false;
|
|
3716
|
+
const checkVariables = options.checkVariableBindings !== false;
|
|
3717
|
+
const checkActions = options.checkActionBindings !== false;
|
|
3144
3718
|
for (const [key, value] of Object.entries(propsObj)) {
|
|
3145
3719
|
const currentPath = `${propsPath}/${key}`;
|
|
3146
3720
|
if (!value || typeof value !== "object") {
|
|
@@ -3154,7 +3728,8 @@ function validatePropsBindings(propsObj, propsPath, nodeId, options, errors) {
|
|
|
3154
3728
|
`${currentPath}/${index}`,
|
|
3155
3729
|
nodeId,
|
|
3156
3730
|
options,
|
|
3157
|
-
errors
|
|
3731
|
+
errors,
|
|
3732
|
+
warnings
|
|
3158
3733
|
);
|
|
3159
3734
|
}
|
|
3160
3735
|
});
|
|
@@ -3162,7 +3737,9 @@ function validatePropsBindings(propsObj, propsPath, nodeId, options, errors) {
|
|
|
3162
3737
|
}
|
|
3163
3738
|
const record = value;
|
|
3164
3739
|
if (record.type === "asset") {
|
|
3165
|
-
if (
|
|
3740
|
+
if (!checkAssets) continue;
|
|
3741
|
+
const assetId = typeof record.assetId === "string" && record.assetId.trim().length > 0 ? record.assetId : void 0;
|
|
3742
|
+
if (assetId === void 0) {
|
|
3166
3743
|
errors.push({
|
|
3167
3744
|
code: "INVALID_ASSET_REFERENCE",
|
|
3168
3745
|
message: 'Asset reference must have a non-empty "assetId"',
|
|
@@ -3178,7 +3755,27 @@ function validatePropsBindings(propsObj, propsPath, nodeId, options, errors) {
|
|
|
3178
3755
|
nodeId
|
|
3179
3756
|
});
|
|
3180
3757
|
}
|
|
3758
|
+
if (assetId !== void 0 && options.knownAssetIds && !isKnownId(options.knownAssetIds, assetId)) {
|
|
3759
|
+
if (typeof record.fallbackUrl === "string" && record.fallbackUrl.length > 0) {
|
|
3760
|
+
warnings.push({
|
|
3761
|
+
code: "UNRESOLVED_ASSET_REFERENCE",
|
|
3762
|
+
message: `Asset "${assetId}" is not among the known assets; its fallbackUrl will be used`,
|
|
3763
|
+
path: `${currentPath}/assetId`,
|
|
3764
|
+
nodeId,
|
|
3765
|
+
details: { assetId }
|
|
3766
|
+
});
|
|
3767
|
+
} else {
|
|
3768
|
+
errors.push({
|
|
3769
|
+
code: "INVALID_ASSET_REFERENCE",
|
|
3770
|
+
message: `Asset "${assetId}" is not among the known assets and has no fallbackUrl`,
|
|
3771
|
+
path: `${currentPath}/assetId`,
|
|
3772
|
+
nodeId,
|
|
3773
|
+
details: { assetId }
|
|
3774
|
+
});
|
|
3775
|
+
}
|
|
3776
|
+
}
|
|
3181
3777
|
} else if (record.type === "variable") {
|
|
3778
|
+
if (!checkVariables) continue;
|
|
3182
3779
|
if (typeof record.key !== "string" || record.key.trim().length === 0) {
|
|
3183
3780
|
errors.push({
|
|
3184
3781
|
code: "INVALID_VARIABLE_BINDING",
|
|
@@ -3188,6 +3785,7 @@ function validatePropsBindings(propsObj, propsPath, nodeId, options, errors) {
|
|
|
3188
3785
|
});
|
|
3189
3786
|
}
|
|
3190
3787
|
} else if (key === "action" || typeof record.type === "string" && record.payload !== void 0) {
|
|
3788
|
+
if (!checkActions) continue;
|
|
3191
3789
|
if (typeof record.type !== "string" || record.type.trim().length === 0) {
|
|
3192
3790
|
errors.push({
|
|
3193
3791
|
code: "INVALID_ACTION_BINDING",
|
|
@@ -3197,10 +3795,62 @@ function validatePropsBindings(propsObj, propsPath, nodeId, options, errors) {
|
|
|
3197
3795
|
});
|
|
3198
3796
|
}
|
|
3199
3797
|
} else {
|
|
3200
|
-
validatePropsBindings(record, currentPath, nodeId, options, errors);
|
|
3798
|
+
validatePropsBindings(record, currentPath, nodeId, options, errors, warnings);
|
|
3201
3799
|
}
|
|
3202
3800
|
}
|
|
3203
3801
|
}
|
|
3802
|
+
function isKnownId(ids, id) {
|
|
3803
|
+
return Array.isArray(ids) ? ids.includes(id) : ids.has(id);
|
|
3804
|
+
}
|
|
3805
|
+
|
|
3806
|
+
// src/io/tracking-sanitizer.ts
|
|
3807
|
+
var import_schema7 = require("@kubuild/schema");
|
|
3808
|
+
function stripTrackingSecretsInPlace(tracking, basePath = "tracking") {
|
|
3809
|
+
const removed = [];
|
|
3810
|
+
if (!tracking || typeof tracking !== "object" || Array.isArray(tracking)) return removed;
|
|
3811
|
+
const providers = tracking.providers;
|
|
3812
|
+
if (!providers || typeof providers !== "object" || Array.isArray(providers)) return removed;
|
|
3813
|
+
for (const provider of Object.keys(import_schema7.LEGACY_TRACKING_SECRET_KEYS)) {
|
|
3814
|
+
const cfg = providers[provider];
|
|
3815
|
+
if (!cfg || typeof cfg !== "object" || Array.isArray(cfg)) continue;
|
|
3816
|
+
for (const key of import_schema7.LEGACY_TRACKING_SECRET_KEYS[provider]) {
|
|
3817
|
+
if (Object.prototype.hasOwnProperty.call(cfg, key)) {
|
|
3818
|
+
delete cfg[key];
|
|
3819
|
+
removed.push(`${basePath}.providers.${provider}.${key}`);
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
3822
|
+
}
|
|
3823
|
+
return removed;
|
|
3824
|
+
}
|
|
3825
|
+
function stripDocumentTrackingSecretsInPlace(doc) {
|
|
3826
|
+
const removed = [];
|
|
3827
|
+
if (!doc || typeof doc !== "object" || Array.isArray(doc)) return removed;
|
|
3828
|
+
const record = doc;
|
|
3829
|
+
removed.push(...stripTrackingSecretsInPlace(record.tracking, "tracking"));
|
|
3830
|
+
const metadata = record.metadata;
|
|
3831
|
+
if (metadata && typeof metadata === "object" && !Array.isArray(metadata)) {
|
|
3832
|
+
removed.push(
|
|
3833
|
+
...stripTrackingSecretsInPlace(metadata.tracking, "metadata.tracking")
|
|
3834
|
+
);
|
|
3835
|
+
}
|
|
3836
|
+
if (Array.isArray(record.artboards)) {
|
|
3837
|
+
record.artboards.forEach((artboard, index) => {
|
|
3838
|
+
if (artboard && typeof artboard === "object") {
|
|
3839
|
+
const inner = artboard.document;
|
|
3840
|
+
for (const path of stripDocumentTrackingSecretsInPlace(inner)) {
|
|
3841
|
+
removed.push(`artboards.${index}.document.${path}`);
|
|
3842
|
+
}
|
|
3843
|
+
}
|
|
3844
|
+
});
|
|
3845
|
+
}
|
|
3846
|
+
return removed;
|
|
3847
|
+
}
|
|
3848
|
+
function sanitizeDocumentTracking(doc) {
|
|
3849
|
+
if (!doc || typeof doc !== "object") return { document: doc, removed: [] };
|
|
3850
|
+
const copy = JSON.parse(JSON.stringify(doc));
|
|
3851
|
+
const removed = stripDocumentTrackingSecretsInPlace(copy);
|
|
3852
|
+
return { document: copy, removed };
|
|
3853
|
+
}
|
|
3204
3854
|
|
|
3205
3855
|
// src/io/exporter.ts
|
|
3206
3856
|
function sha256Sync(data) {
|
|
@@ -3421,6 +4071,7 @@ async function exportPackage(document, options = {}) {
|
|
|
3421
4071
|
};
|
|
3422
4072
|
}
|
|
3423
4073
|
const pageDoc = JSON.parse(JSON.stringify(validation.data));
|
|
4074
|
+
stripDocumentTrackingSecretsInPlace(pageDoc);
|
|
3424
4075
|
if (options.metadata) {
|
|
3425
4076
|
pageDoc.metadata = {
|
|
3426
4077
|
...pageDoc.metadata || {
|
|
@@ -3544,8 +4195,8 @@ async function exportPackage(document, options = {}) {
|
|
|
3544
4195
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3545
4196
|
};
|
|
3546
4197
|
const manifest = {
|
|
3547
|
-
schema:
|
|
3548
|
-
schemaVersion: pageDoc.version ||
|
|
4198
|
+
schema: import_schema8.SCHEMA_NAME,
|
|
4199
|
+
schemaVersion: pageDoc.version || import_schema8.CURRENT_SCHEMA_VERSION,
|
|
3549
4200
|
packageVersion: options.packageVersion || "1.0.0",
|
|
3550
4201
|
builderCompatibility: options.builderCompatibility || ">=0.1.0",
|
|
3551
4202
|
requiredComponents: requirements.requiredComponents,
|
|
@@ -3570,10 +4221,10 @@ var exportStoraPackage = exportPackage;
|
|
|
3570
4221
|
|
|
3571
4222
|
// src/io/importer.ts
|
|
3572
4223
|
var import_fflate2 = require("fflate");
|
|
3573
|
-
var
|
|
4224
|
+
var import_schema10 = require("@kubuild/schema");
|
|
3574
4225
|
|
|
3575
4226
|
// src/io/migration.ts
|
|
3576
|
-
var
|
|
4227
|
+
var import_schema9 = require("@kubuild/schema");
|
|
3577
4228
|
var MigrationRegistry = class {
|
|
3578
4229
|
steps = /* @__PURE__ */ new Map();
|
|
3579
4230
|
/**
|
|
@@ -3652,7 +4303,7 @@ defaultMigrationRegistry.register({
|
|
|
3652
4303
|
description: "Migrate alpha 0.1.0 schema (root node key, flat styles) to v1.0.0",
|
|
3653
4304
|
migrate: (rawDoc) => {
|
|
3654
4305
|
const migrated = deepClone(rawDoc);
|
|
3655
|
-
migrated.schema =
|
|
4306
|
+
migrated.schema = import_schema9.SCHEMA_NAME;
|
|
3656
4307
|
migrated.version = "1.0.0";
|
|
3657
4308
|
if (migrated.root && !migrated.document) {
|
|
3658
4309
|
migrated.document = migrated.root;
|
|
@@ -3687,7 +4338,7 @@ defaultMigrationRegistry.register({
|
|
|
3687
4338
|
description: "Migrate 0.2.0 schema to v1.0.0",
|
|
3688
4339
|
migrate: (rawDoc) => {
|
|
3689
4340
|
const migrated = deepClone(rawDoc);
|
|
3690
|
-
migrated.schema =
|
|
4341
|
+
migrated.schema = import_schema9.SCHEMA_NAME;
|
|
3691
4342
|
migrated.version = "1.0.0";
|
|
3692
4343
|
if (migrated.document && typeof migrated.document === "object") {
|
|
3693
4344
|
convertNodeStylesToResponsive(migrated.document);
|
|
@@ -3701,7 +4352,7 @@ defaultMigrationRegistry.register({
|
|
|
3701
4352
|
description: "Migrate 0.9.0 beta schema to v1.0.0",
|
|
3702
4353
|
migrate: (rawDoc) => {
|
|
3703
4354
|
const migrated = deepClone(rawDoc);
|
|
3704
|
-
migrated.schema =
|
|
4355
|
+
migrated.schema = import_schema9.SCHEMA_NAME;
|
|
3705
4356
|
migrated.version = "1.0.0";
|
|
3706
4357
|
if (!migrated.metadata || typeof migrated.metadata !== "object") {
|
|
3707
4358
|
migrated.metadata = {
|
|
@@ -3726,17 +4377,36 @@ defaultMigrationRegistry.register({
|
|
|
3726
4377
|
return migrated;
|
|
3727
4378
|
}
|
|
3728
4379
|
});
|
|
3729
|
-
|
|
4380
|
+
defaultMigrationRegistry.register({
|
|
4381
|
+
fromVersion: "1.0.0",
|
|
4382
|
+
toVersion: "1.1.0",
|
|
4383
|
+
description: "Remove tracking secrets and server destinations (capiAccessToken, accessToken, measurementProtocolSecret, serverRelayUrl, custom endpointUrl/headers) from the document",
|
|
4384
|
+
migrate: (rawDoc, context) => {
|
|
4385
|
+
const migrated = deepClone(rawDoc);
|
|
4386
|
+
migrated.version = "1.1.0";
|
|
4387
|
+
const removed = stripDocumentTrackingSecretsInPlace(migrated);
|
|
4388
|
+
if (removed.length > 0) {
|
|
4389
|
+
context.warn({
|
|
4390
|
+
code: "TRACKING_SECRET_REMOVED",
|
|
4391
|
+
message: "Tracking secret removed; re-link credential via credentialId (secrets are now stored by the host and resolved server-side).",
|
|
4392
|
+
step: "1.0.0->1.1.0",
|
|
4393
|
+
paths: removed
|
|
4394
|
+
});
|
|
4395
|
+
}
|
|
4396
|
+
return migrated;
|
|
4397
|
+
}
|
|
4398
|
+
});
|
|
4399
|
+
function canMigrate(sourceVersion, targetVersion = import_schema9.CURRENT_SCHEMA_VERSION, registry = defaultMigrationRegistry) {
|
|
3730
4400
|
if (!sourceVersion || !targetVersion) return false;
|
|
3731
4401
|
if (sourceVersion === targetVersion) return true;
|
|
3732
4402
|
return registry.hasPath(sourceVersion, targetVersion);
|
|
3733
4403
|
}
|
|
3734
|
-
function getMigrationPath(sourceVersion, targetVersion =
|
|
4404
|
+
function getMigrationPath(sourceVersion, targetVersion = import_schema9.CURRENT_SCHEMA_VERSION, registry = defaultMigrationRegistry) {
|
|
3735
4405
|
if (!sourceVersion || !targetVersion) return null;
|
|
3736
4406
|
return registry.findPath(sourceVersion, targetVersion);
|
|
3737
4407
|
}
|
|
3738
4408
|
function migrateDocument(rawDocument, options = {}) {
|
|
3739
|
-
const targetVersion = options.targetVersion ??
|
|
4409
|
+
const targetVersion = options.targetVersion ?? import_schema9.CURRENT_SCHEMA_VERSION;
|
|
3740
4410
|
const dryRun = options.dryRun ?? false;
|
|
3741
4411
|
const registry = options.registry ?? defaultMigrationRegistry;
|
|
3742
4412
|
const validate = options.validate ?? true;
|
|
@@ -3764,6 +4434,14 @@ function migrateDocument(rawDocument, options = {}) {
|
|
|
3764
4434
|
const sourceVersion = typeof doc.version === "string" ? doc.version.trim() : "unknown";
|
|
3765
4435
|
if (sourceVersion === targetVersion) {
|
|
3766
4436
|
const cloned = deepClone(doc);
|
|
4437
|
+
const removedSecrets = stripDocumentTrackingSecretsInPlace(cloned);
|
|
4438
|
+
const currentWarnings = removedSecrets.length > 0 ? [
|
|
4439
|
+
{
|
|
4440
|
+
code: "TRACKING_SECRET_REMOVED",
|
|
4441
|
+
message: "Tracking secret removed; re-link credential via credentialId (secrets are stored by the host and resolved server-side).",
|
|
4442
|
+
paths: removedSecrets
|
|
4443
|
+
}
|
|
4444
|
+
] : [];
|
|
3767
4445
|
if (validate) {
|
|
3768
4446
|
const validation = validateDocument(cloned);
|
|
3769
4447
|
if (!validation.valid) {
|
|
@@ -3795,7 +4473,8 @@ function migrateDocument(rawDocument, options = {}) {
|
|
|
3795
4473
|
targetVersion,
|
|
3796
4474
|
migrationPath: [sourceVersion],
|
|
3797
4475
|
stepsApplied: 0,
|
|
3798
|
-
dryRun
|
|
4476
|
+
dryRun,
|
|
4477
|
+
...currentWarnings.length > 0 ? { warnings: currentWarnings } : {}
|
|
3799
4478
|
}
|
|
3800
4479
|
};
|
|
3801
4480
|
}
|
|
@@ -3835,6 +4514,8 @@ function migrateDocument(rawDocument, options = {}) {
|
|
|
3835
4514
|
}
|
|
3836
4515
|
let currentDoc = deepClone(doc);
|
|
3837
4516
|
let stepsApplied = 0;
|
|
4517
|
+
const warnings = [];
|
|
4518
|
+
const stepContext = { warn: (w) => warnings.push(w) };
|
|
3838
4519
|
for (let i = 0; i < path.length - 1; i++) {
|
|
3839
4520
|
const fromVer = path[i];
|
|
3840
4521
|
const toVer = path[i + 1];
|
|
@@ -3860,7 +4541,7 @@ function migrateDocument(rawDocument, options = {}) {
|
|
|
3860
4541
|
};
|
|
3861
4542
|
}
|
|
3862
4543
|
try {
|
|
3863
|
-
currentDoc = step.migrate(currentDoc);
|
|
4544
|
+
currentDoc = step.migrate(currentDoc, stepContext);
|
|
3864
4545
|
stepsApplied++;
|
|
3865
4546
|
} catch (err) {
|
|
3866
4547
|
const error = {
|
|
@@ -3916,7 +4597,8 @@ function migrateDocument(rawDocument, options = {}) {
|
|
|
3916
4597
|
targetVersion,
|
|
3917
4598
|
migrationPath: path,
|
|
3918
4599
|
stepsApplied,
|
|
3919
|
-
dryRun: false
|
|
4600
|
+
dryRun: false,
|
|
4601
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
3920
4602
|
}
|
|
3921
4603
|
};
|
|
3922
4604
|
}
|
|
@@ -3951,7 +4633,7 @@ async function preflightPackage(archiveData, options = {}) {
|
|
|
3951
4633
|
...DEFAULT_SECURITY_LIMITS,
|
|
3952
4634
|
...options.securityLimits
|
|
3953
4635
|
};
|
|
3954
|
-
const targetVersion = options.targetSchemaVersion ||
|
|
4636
|
+
const targetVersion = options.targetSchemaVersion || import_schema10.CURRENT_SCHEMA_VERSION;
|
|
3955
4637
|
const migrationRegistry = options.migrationRegistry || defaultMigrationRegistry;
|
|
3956
4638
|
const diagnostics = [];
|
|
3957
4639
|
let dependencyPolicy = options.dependencyPolicy || "cancel";
|
|
@@ -4084,7 +4766,7 @@ async function preflightPackage(archiveData, options = {}) {
|
|
|
4084
4766
|
path: manifestProtoCheck.path
|
|
4085
4767
|
});
|
|
4086
4768
|
}
|
|
4087
|
-
const parseResult =
|
|
4769
|
+
const parseResult = import_schema10.ManifestSchema.safeParse(manifestJson);
|
|
4088
4770
|
if (!parseResult.success) {
|
|
4089
4771
|
const issues = parseResult.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ");
|
|
4090
4772
|
diagnostics.push({
|
|
@@ -4114,6 +4796,16 @@ async function preflightPackage(archiveData, options = {}) {
|
|
|
4114
4796
|
let rawPageDoc;
|
|
4115
4797
|
try {
|
|
4116
4798
|
rawPageDoc = JSON.parse((0, import_fflate2.strFromU8)(pageEntry));
|
|
4799
|
+
const secretScan = sanitizeDocumentTracking(rawPageDoc);
|
|
4800
|
+
if (secretScan.removed.length > 0) {
|
|
4801
|
+
diagnostics.push({
|
|
4802
|
+
code: "TRACKING_SECRET_REMOVED",
|
|
4803
|
+
severity: "warning",
|
|
4804
|
+
message: "Tracking secrets found in page.json will be removed on import; re-link credentials via credentialId.",
|
|
4805
|
+
path: "page.json",
|
|
4806
|
+
details: { paths: secretScan.removed }
|
|
4807
|
+
});
|
|
4808
|
+
}
|
|
4117
4809
|
const pageProtoCheck = containsProhibitedKeys(rawPageDoc);
|
|
4118
4810
|
if (pageProtoCheck.found) {
|
|
4119
4811
|
diagnostics.push({
|
|
@@ -4360,7 +5052,7 @@ function buildReport(valid, canImport, fields) {
|
|
|
4360
5052
|
return {
|
|
4361
5053
|
valid,
|
|
4362
5054
|
canImport,
|
|
4363
|
-
targetVersion: fields.targetVersion ||
|
|
5055
|
+
targetVersion: fields.targetVersion || import_schema10.CURRENT_SCHEMA_VERSION,
|
|
4364
5056
|
requiresMigration: fields.requiresMigration || false,
|
|
4365
5057
|
missingComponents: fields.missingComponents || [],
|
|
4366
5058
|
missingCapabilities: fields.missingCapabilities || [],
|
|
@@ -4433,7 +5125,7 @@ async function importPackage(archiveData, options = {}) {
|
|
|
4433
5125
|
if (preflight.requiresMigration) {
|
|
4434
5126
|
const migrationRegistry = options.migrationRegistry || defaultMigrationRegistry;
|
|
4435
5127
|
const migrationRes = migrateDocument(pageRaw, {
|
|
4436
|
-
targetVersion: options.targetSchemaVersion ||
|
|
5128
|
+
targetVersion: options.targetSchemaVersion || import_schema10.CURRENT_SCHEMA_VERSION,
|
|
4437
5129
|
registry: migrationRegistry,
|
|
4438
5130
|
validate: true
|
|
4439
5131
|
});
|
|
@@ -4455,13 +5147,24 @@ async function importPackage(archiveData, options = {}) {
|
|
|
4455
5147
|
} else {
|
|
4456
5148
|
finalDocument = pageRaw;
|
|
4457
5149
|
}
|
|
5150
|
+
const importWarnings = [];
|
|
5151
|
+
const strippedSecrets = stripDocumentTrackingSecretsInPlace(finalDocument);
|
|
5152
|
+
if (strippedSecrets.length > 0) {
|
|
5153
|
+
importWarnings.push({
|
|
5154
|
+
code: "TRACKING_SECRET_REMOVED",
|
|
5155
|
+
severity: "warning",
|
|
5156
|
+
message: "Tracking secret removed from imported document; re-link credential via credentialId.",
|
|
5157
|
+
path: "page.json",
|
|
5158
|
+
details: { paths: strippedSecrets }
|
|
5159
|
+
});
|
|
5160
|
+
}
|
|
4458
5161
|
let metadata = finalDocument.metadata || {
|
|
4459
5162
|
title: "Imported Page",
|
|
4460
5163
|
description: "",
|
|
4461
5164
|
author: "",
|
|
4462
5165
|
tags: [],
|
|
4463
5166
|
category: "general",
|
|
4464
|
-
version: finalDocument.version ||
|
|
5167
|
+
version: finalDocument.version || import_schema10.CURRENT_SCHEMA_VERSION
|
|
4465
5168
|
};
|
|
4466
5169
|
if (unzipped["metadata.json"]) {
|
|
4467
5170
|
try {
|
|
@@ -4613,13 +5316,14 @@ async function importPackage(archiveData, options = {}) {
|
|
|
4613
5316
|
metadata,
|
|
4614
5317
|
extractedAssets,
|
|
4615
5318
|
renamedAssets: Object.keys(renameMap).length > 0 ? renameMap : void 0,
|
|
4616
|
-
preflight
|
|
5319
|
+
preflight,
|
|
5320
|
+
...importWarnings.length > 0 ? { warnings: importWarnings } : {}
|
|
4617
5321
|
};
|
|
4618
5322
|
}
|
|
4619
5323
|
var importStoraPackage = importPackage;
|
|
4620
5324
|
|
|
4621
5325
|
// src/io/load-project.ts
|
|
4622
|
-
var
|
|
5326
|
+
var import_schema11 = require("@kubuild/schema");
|
|
4623
5327
|
var DEFAULT_PAGE_ARTBOARD_ID = "artboard-page-1";
|
|
4624
5328
|
function wrapPageDocumentAsProject(document, options = {}) {
|
|
4625
5329
|
const artboardId = options.artboardId ?? DEFAULT_PAGE_ARTBOARD_ID;
|
|
@@ -4632,8 +5336,8 @@ function wrapPageDocumentAsProject(document, options = {}) {
|
|
|
4632
5336
|
...options.width !== void 0 ? { width: options.width } : {}
|
|
4633
5337
|
};
|
|
4634
5338
|
return {
|
|
4635
|
-
schema:
|
|
4636
|
-
version:
|
|
5339
|
+
schema: import_schema11.PROJECT_SCHEMA_NAME,
|
|
5340
|
+
version: import_schema11.CURRENT_PROJECT_SCHEMA_VERSION,
|
|
4637
5341
|
metadata: document.metadata,
|
|
4638
5342
|
artboards: [artboard],
|
|
4639
5343
|
activeArtboardId: artboardId
|
|
@@ -4652,8 +5356,8 @@ function loadProjectDocument(raw) {
|
|
|
4652
5356
|
]
|
|
4653
5357
|
};
|
|
4654
5358
|
}
|
|
4655
|
-
if ((0,
|
|
4656
|
-
const parsed =
|
|
5359
|
+
if ((0, import_schema11.looksLikeProjectDocument)(raw)) {
|
|
5360
|
+
const parsed = import_schema11.ProjectDocumentSchema.safeParse(raw);
|
|
4657
5361
|
if (!parsed.success) {
|
|
4658
5362
|
return {
|
|
4659
5363
|
success: false,
|
|
@@ -5138,7 +5842,7 @@ function compareManifestsSemantically(expected, actual, options = {}) {
|
|
|
5138
5842
|
}
|
|
5139
5843
|
|
|
5140
5844
|
// src/io/template-utils.ts
|
|
5141
|
-
var
|
|
5845
|
+
var import_schema12 = require("@kubuild/schema");
|
|
5142
5846
|
var CORE_BUILTIN_COMPONENTS = /* @__PURE__ */ new Set([
|
|
5143
5847
|
"page",
|
|
5144
5848
|
"section",
|
|
@@ -5152,7 +5856,7 @@ var CORE_BUILTIN_COMPONENTS = /* @__PURE__ */ new Set([
|
|
|
5152
5856
|
"collection"
|
|
5153
5857
|
]);
|
|
5154
5858
|
function validateTemplate(value) {
|
|
5155
|
-
const parseResult =
|
|
5859
|
+
const parseResult = import_schema12.TemplateRecordSchema.safeParse(value);
|
|
5156
5860
|
if (parseResult.success) {
|
|
5157
5861
|
return {
|
|
5158
5862
|
valid: true,
|
|
@@ -5280,7 +5984,7 @@ function cloneTreeWithFreshIds(root, idGen) {
|
|
|
5280
5984
|
function cloneTemplateAsPage(templateOrDoc, options = {}) {
|
|
5281
5985
|
let sourceDoc;
|
|
5282
5986
|
let templateOrigin = null;
|
|
5283
|
-
if ((0,
|
|
5987
|
+
if ((0, import_schema12.isTemplateRecord)(templateOrDoc)) {
|
|
5284
5988
|
if (!templateOrDoc.document) {
|
|
5285
5989
|
throw new Error(
|
|
5286
5990
|
`Template "${templateOrDoc.name}" (${templateOrDoc.id}) does not contain an inline document snapshot to clone from.`
|
|
@@ -5297,7 +6001,7 @@ function cloneTemplateAsPage(templateOrDoc, options = {}) {
|
|
|
5297
6001
|
} else {
|
|
5298
6002
|
throw new Error("Invalid input: expected a TemplateRecord or PageDocument.");
|
|
5299
6003
|
}
|
|
5300
|
-
const existingIdsInSource = new Set((0,
|
|
6004
|
+
const existingIdsInSource = new Set((0, import_schema12.collectNodeIds)(sourceDoc.document));
|
|
5301
6005
|
const assignedNewIds = /* @__PURE__ */ new Set();
|
|
5302
6006
|
const prefix = options.idPrefix || `page_${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 6)}`;
|
|
5303
6007
|
let counter = 1;
|
|
@@ -5346,8 +6050,8 @@ function cloneTemplateAsPage(templateOrDoc, options = {}) {
|
|
|
5346
6050
|
}
|
|
5347
6051
|
function areNodeIdsCompletelyDistinct(docA, docB) {
|
|
5348
6052
|
if (!docA?.document || !docB?.document) return true;
|
|
5349
|
-
const idsA = new Set((0,
|
|
5350
|
-
const idsB = (0,
|
|
6053
|
+
const idsA = new Set((0, import_schema12.collectNodeIds)(docA.document));
|
|
6054
|
+
const idsB = (0, import_schema12.collectNodeIds)(docB.document);
|
|
5351
6055
|
for (const id of idsB) {
|
|
5352
6056
|
if (idsA.has(id)) {
|
|
5353
6057
|
return false;
|
|
@@ -5357,7 +6061,7 @@ function areNodeIdsCompletelyDistinct(docA, docB) {
|
|
|
5357
6061
|
}
|
|
5358
6062
|
|
|
5359
6063
|
// src/validation/project-validator.ts
|
|
5360
|
-
var
|
|
6064
|
+
var import_schema13 = require("@kubuild/schema");
|
|
5361
6065
|
function walk(node, visit) {
|
|
5362
6066
|
visit(node);
|
|
5363
6067
|
for (const child of node.children ?? []) {
|
|
@@ -5367,7 +6071,7 @@ function walk(node, visit) {
|
|
|
5367
6071
|
function collectArtboardReferences(artboard) {
|
|
5368
6072
|
const refs = [];
|
|
5369
6073
|
walk(artboard.document.document, (node) => {
|
|
5370
|
-
if (node.type ===
|
|
6074
|
+
if (node.type === import_schema13.ARTBOARD_REFERENCE_NODE_TYPE) {
|
|
5371
6075
|
const target = node.props?.artboardId;
|
|
5372
6076
|
refs.push({
|
|
5373
6077
|
nodeId: node.id,
|
|
@@ -5394,7 +6098,7 @@ function validateProject(input, options = {}) {
|
|
|
5394
6098
|
artboardErrors
|
|
5395
6099
|
};
|
|
5396
6100
|
}
|
|
5397
|
-
const parsed =
|
|
6101
|
+
const parsed = import_schema13.ProjectDocumentSchema.safeParse(input);
|
|
5398
6102
|
if (!parsed.success) {
|
|
5399
6103
|
return {
|
|
5400
6104
|
valid: false,
|
|
@@ -5402,7 +6106,7 @@ function validateProject(input, options = {}) {
|
|
|
5402
6106
|
errors: [
|
|
5403
6107
|
{
|
|
5404
6108
|
code: "PROJECT_SCHEMA_INVALID",
|
|
5405
|
-
message: `Project failed schema validation. Expected schema "${
|
|
6109
|
+
message: `Project failed schema validation. Expected schema "${import_schema13.PROJECT_SCHEMA_NAME}".`,
|
|
5406
6110
|
path: "",
|
|
5407
6111
|
details: { issues: parsed.error.issues }
|
|
5408
6112
|
}
|
|
@@ -5495,7 +6199,7 @@ function validateProject(input, options = {}) {
|
|
|
5495
6199
|
function collectArtboardReferenceNodes(root) {
|
|
5496
6200
|
const refs = [];
|
|
5497
6201
|
walk(root, (node) => {
|
|
5498
|
-
if (node.type ===
|
|
6202
|
+
if (node.type === import_schema13.ARTBOARD_REFERENCE_NODE_TYPE) {
|
|
5499
6203
|
const target = node.props?.artboardId;
|
|
5500
6204
|
refs.push({ nodeId: node.id, artboardId: typeof target === "string" ? target : void 0 });
|
|
5501
6205
|
}
|
|
@@ -5744,10 +6448,12 @@ function isFormValid(errors) {
|
|
|
5744
6448
|
createPageArtboard,
|
|
5745
6449
|
createRuntimeStore,
|
|
5746
6450
|
createTemplateRecord,
|
|
6451
|
+
createTrackingRelayHandler,
|
|
5747
6452
|
deepClone,
|
|
5748
6453
|
defaultIdGenerator,
|
|
5749
6454
|
defaultMigrationRegistry,
|
|
5750
6455
|
defaultRenameAssetStrategy,
|
|
6456
|
+
dispatchServerTracking,
|
|
5751
6457
|
duplicateNode,
|
|
5752
6458
|
evaluateActionCondition,
|
|
5753
6459
|
evaluateCondition,
|
|
@@ -5767,6 +6473,7 @@ function isFormValid(errors) {
|
|
|
5767
6473
|
findMissingComponentNodes,
|
|
5768
6474
|
findNodeById,
|
|
5769
6475
|
findNodeLocation,
|
|
6476
|
+
generateTrackingEventId,
|
|
5770
6477
|
getActiveArtboard,
|
|
5771
6478
|
getAncestorChain,
|
|
5772
6479
|
getComponentArtboards,
|
|
@@ -5776,6 +6483,7 @@ function isFormValid(errors) {
|
|
|
5776
6483
|
getPageArtboards,
|
|
5777
6484
|
getParentNodeId,
|
|
5778
6485
|
hasTemplateExpressions,
|
|
6486
|
+
hashSha256,
|
|
5779
6487
|
importPackage,
|
|
5780
6488
|
importStoraPackage,
|
|
5781
6489
|
insertNode,
|
|
@@ -5793,6 +6501,9 @@ function isFormValid(errors) {
|
|
|
5793
6501
|
loadProjectDocument,
|
|
5794
6502
|
migrateDocument,
|
|
5795
6503
|
moveNode,
|
|
6504
|
+
normalizeEmail,
|
|
6505
|
+
normalizePhone,
|
|
6506
|
+
normalizeText,
|
|
5796
6507
|
preflightPackage,
|
|
5797
6508
|
previewImportPackage,
|
|
5798
6509
|
remapAssetReferences,
|
|
@@ -5804,12 +6515,19 @@ function isFormValid(errors) {
|
|
|
5804
6515
|
resolveBinding,
|
|
5805
6516
|
resolveBindingValue,
|
|
5806
6517
|
resolvePropertyPath,
|
|
6518
|
+
sanitizeDocumentTracking,
|
|
5807
6519
|
sanitizeFilename,
|
|
5808
6520
|
sanitizeHtml,
|
|
5809
6521
|
sanitizeUrl,
|
|
5810
6522
|
saveDraftAsTemplate,
|
|
6523
|
+
sendCustomWebhookEvent,
|
|
6524
|
+
sendGa4MeasurementEvent,
|
|
6525
|
+
sendMetaCapiEvent,
|
|
6526
|
+
sendTikTokEventsApi,
|
|
5811
6527
|
setActiveArtboard,
|
|
5812
6528
|
sha256Sync,
|
|
6529
|
+
stripDocumentTrackingSecretsInPlace,
|
|
6530
|
+
stripTrackingSecretsInPlace,
|
|
5813
6531
|
ungroupNodeFrame,
|
|
5814
6532
|
updateActions,
|
|
5815
6533
|
updateAnimation,
|