@kubuild/core 0.4.0 → 0.6.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/index.cjs +430 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +420 -13
- package/dist/index.js.map +1 -1
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/server-tracking.d.ts +110 -0
- package/dist/runtime/server-tracking.d.ts.map +1 -0
- package/dist/validation/validator.d.ts +14 -0
- package/dist/validation/validator.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2454,6 +2454,385 @@ function createRuntimeStore(initialState) {
|
|
|
2454
2454
|
return new RuntimeStateStore(initialState);
|
|
2455
2455
|
}
|
|
2456
2456
|
|
|
2457
|
+
// src/runtime/server-tracking.ts
|
|
2458
|
+
function normalizeEmail(email) {
|
|
2459
|
+
return (email || "").trim().toLowerCase();
|
|
2460
|
+
}
|
|
2461
|
+
function normalizePhone(phone) {
|
|
2462
|
+
return (phone || "").replace(/[^0-9]/g, "");
|
|
2463
|
+
}
|
|
2464
|
+
function normalizeText(text) {
|
|
2465
|
+
return (text || "").trim().toLowerCase();
|
|
2466
|
+
}
|
|
2467
|
+
async function hashSha256(value) {
|
|
2468
|
+
const normalized = (value || "").trim();
|
|
2469
|
+
if (!normalized) return "";
|
|
2470
|
+
if (typeof globalThis.crypto?.subtle?.digest === "function") {
|
|
2471
|
+
const encoder = new TextEncoder();
|
|
2472
|
+
const data = encoder.encode(normalized);
|
|
2473
|
+
const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", data);
|
|
2474
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
2475
|
+
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2476
|
+
}
|
|
2477
|
+
throw new Error("Web Crypto API (crypto.subtle) is not available in current runtime environment.");
|
|
2478
|
+
}
|
|
2479
|
+
function generateTrackingEventId(prefix = "evt") {
|
|
2480
|
+
const timestamp = Date.now().toString(36);
|
|
2481
|
+
const randomPart = Math.random().toString(36).substring(2, 10);
|
|
2482
|
+
return `${prefix}_${timestamp}_${randomPart}`;
|
|
2483
|
+
}
|
|
2484
|
+
async function sendMetaCapiEvent(event, config, options) {
|
|
2485
|
+
const fetcher = options?.fetchFn || globalThis.fetch;
|
|
2486
|
+
if (!config.pixelId) {
|
|
2487
|
+
return {
|
|
2488
|
+
provider: "meta",
|
|
2489
|
+
success: false,
|
|
2490
|
+
error: "Meta Pixel ID is missing"
|
|
2491
|
+
};
|
|
2492
|
+
}
|
|
2493
|
+
const rawUserData = event.userData || {};
|
|
2494
|
+
const hashedUserData = {};
|
|
2495
|
+
if (rawUserData.email) {
|
|
2496
|
+
hashedUserData.em = [await hashSha256(normalizeEmail(String(rawUserData.email)))];
|
|
2497
|
+
}
|
|
2498
|
+
if (rawUserData.phone) {
|
|
2499
|
+
hashedUserData.ph = [await hashSha256(normalizePhone(String(rawUserData.phone)))];
|
|
2500
|
+
}
|
|
2501
|
+
if (rawUserData.firstName) {
|
|
2502
|
+
hashedUserData.fn = [await hashSha256(normalizeText(String(rawUserData.firstName)))];
|
|
2503
|
+
}
|
|
2504
|
+
if (rawUserData.lastName) {
|
|
2505
|
+
hashedUserData.ln = [await hashSha256(normalizeText(String(rawUserData.lastName)))];
|
|
2506
|
+
}
|
|
2507
|
+
if (rawUserData.city) {
|
|
2508
|
+
hashedUserData.ct = [await hashSha256(normalizeText(String(rawUserData.city)))];
|
|
2509
|
+
}
|
|
2510
|
+
if (rawUserData.state) {
|
|
2511
|
+
hashedUserData.st = [await hashSha256(normalizeText(String(rawUserData.state)))];
|
|
2512
|
+
}
|
|
2513
|
+
if (rawUserData.zip) {
|
|
2514
|
+
hashedUserData.zp = [await hashSha256(normalizeText(String(rawUserData.zip)))];
|
|
2515
|
+
}
|
|
2516
|
+
if (rawUserData.country) {
|
|
2517
|
+
hashedUserData.country = [await hashSha256(normalizeText(String(rawUserData.country)))];
|
|
2518
|
+
}
|
|
2519
|
+
const clientIp = rawUserData.clientIp || options?.clientIp;
|
|
2520
|
+
if (clientIp) hashedUserData.client_ip_address = clientIp;
|
|
2521
|
+
const clientUserAgent = rawUserData.clientUserAgent || options?.clientUserAgent;
|
|
2522
|
+
if (clientUserAgent) hashedUserData.client_user_agent = clientUserAgent;
|
|
2523
|
+
if (rawUserData.fbp) hashedUserData.fbp = rawUserData.fbp;
|
|
2524
|
+
if (rawUserData.fbc) hashedUserData.fbc = rawUserData.fbc;
|
|
2525
|
+
if (rawUserData.externalId) {
|
|
2526
|
+
hashedUserData.external_id = [await hashSha256(String(rawUserData.externalId))];
|
|
2527
|
+
}
|
|
2528
|
+
const eventTime = event.eventTime || Math.floor(Date.now() / 1e3);
|
|
2529
|
+
const eventSourceUrl = event.eventSourceUrl || options?.sourceUrl;
|
|
2530
|
+
const capiPayload = {
|
|
2531
|
+
data: [
|
|
2532
|
+
{
|
|
2533
|
+
event_name: event.eventName,
|
|
2534
|
+
event_time: eventTime,
|
|
2535
|
+
event_id: event.eventId,
|
|
2536
|
+
event_source_url: eventSourceUrl,
|
|
2537
|
+
action_source: event.actionSource || "website",
|
|
2538
|
+
user_data: hashedUserData,
|
|
2539
|
+
custom_data: {
|
|
2540
|
+
...event.params || {},
|
|
2541
|
+
...event.customData || {}
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
]
|
|
2545
|
+
};
|
|
2546
|
+
if (config.testEventCode) {
|
|
2547
|
+
capiPayload.test_event_code = config.testEventCode;
|
|
2548
|
+
}
|
|
2549
|
+
if (options?.simulateInDebug) {
|
|
2550
|
+
options.onLog?.("[Meta CAPI SIMULATED]", capiPayload);
|
|
2551
|
+
return { provider: "meta", success: true, data: { simulated: true, payload: capiPayload } };
|
|
2552
|
+
}
|
|
2553
|
+
try {
|
|
2554
|
+
const url = `https://graph.facebook.com/v19.0/${encodeURIComponent(config.pixelId)}/events`;
|
|
2555
|
+
const headers = {
|
|
2556
|
+
"Content-Type": "application/json"
|
|
2557
|
+
};
|
|
2558
|
+
if (config.capiAccessToken) {
|
|
2559
|
+
headers["Authorization"] = `Bearer ${config.capiAccessToken}`;
|
|
2560
|
+
}
|
|
2561
|
+
const res = await fetcher(url, {
|
|
2562
|
+
method: "POST",
|
|
2563
|
+
headers,
|
|
2564
|
+
body: JSON.stringify(capiPayload)
|
|
2565
|
+
});
|
|
2566
|
+
const resJson = await res.json().catch(() => ({}));
|
|
2567
|
+
if (!res.ok) {
|
|
2568
|
+
return {
|
|
2569
|
+
provider: "meta",
|
|
2570
|
+
success: false,
|
|
2571
|
+
status: res.status,
|
|
2572
|
+
error: resJson?.error?.message || `HTTP ${res.status}`,
|
|
2573
|
+
data: resJson
|
|
2574
|
+
};
|
|
2575
|
+
}
|
|
2576
|
+
return {
|
|
2577
|
+
provider: "meta",
|
|
2578
|
+
success: true,
|
|
2579
|
+
status: res.status,
|
|
2580
|
+
data: resJson
|
|
2581
|
+
};
|
|
2582
|
+
} catch (err) {
|
|
2583
|
+
return {
|
|
2584
|
+
provider: "meta",
|
|
2585
|
+
success: false,
|
|
2586
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2587
|
+
};
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
async function sendTikTokEventsApi(event, config, options) {
|
|
2591
|
+
const fetcher = options?.fetchFn || globalThis.fetch;
|
|
2592
|
+
if (!config.pixelId) {
|
|
2593
|
+
return { provider: "tiktok", success: false, error: "TikTok Pixel ID is missing" };
|
|
2594
|
+
}
|
|
2595
|
+
const rawUserData = event.userData || {};
|
|
2596
|
+
const user = {};
|
|
2597
|
+
if (rawUserData.email) {
|
|
2598
|
+
user.email = await hashSha256(normalizeEmail(String(rawUserData.email)));
|
|
2599
|
+
}
|
|
2600
|
+
if (rawUserData.phone) {
|
|
2601
|
+
user.phone_number = await hashSha256(normalizePhone(String(rawUserData.phone)));
|
|
2602
|
+
}
|
|
2603
|
+
if (rawUserData.clientIp || options?.clientIp) {
|
|
2604
|
+
user.ip = rawUserData.clientIp || options?.clientIp;
|
|
2605
|
+
}
|
|
2606
|
+
if (rawUserData.clientUserAgent || options?.clientUserAgent) {
|
|
2607
|
+
user.user_agent = rawUserData.clientUserAgent || options?.clientUserAgent;
|
|
2608
|
+
}
|
|
2609
|
+
const tiktokPayload = {
|
|
2610
|
+
event_source: "web",
|
|
2611
|
+
event_source_id: config.pixelId,
|
|
2612
|
+
data: [
|
|
2613
|
+
{
|
|
2614
|
+
event: event.eventName,
|
|
2615
|
+
event_id: event.eventId,
|
|
2616
|
+
timestamp: new Date(event.eventTime ? event.eventTime * 1e3 : Date.now()).toISOString(),
|
|
2617
|
+
user,
|
|
2618
|
+
properties: {
|
|
2619
|
+
...event.params || {},
|
|
2620
|
+
...event.customData || {}
|
|
2621
|
+
},
|
|
2622
|
+
page: {
|
|
2623
|
+
url: event.eventSourceUrl || options?.sourceUrl
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
]
|
|
2627
|
+
};
|
|
2628
|
+
if (config.testEventCode) {
|
|
2629
|
+
tiktokPayload.test_event_code = config.testEventCode;
|
|
2630
|
+
}
|
|
2631
|
+
if (options?.simulateInDebug) {
|
|
2632
|
+
options.onLog?.("[TikTok Events API SIMULATED]", tiktokPayload);
|
|
2633
|
+
return { provider: "tiktok", success: true, data: { simulated: true, payload: tiktokPayload } };
|
|
2634
|
+
}
|
|
2635
|
+
try {
|
|
2636
|
+
const url = "https://business-api.tiktok.com/open_api/v1.3/event/track/";
|
|
2637
|
+
const headers = {
|
|
2638
|
+
"Content-Type": "application/json"
|
|
2639
|
+
};
|
|
2640
|
+
if (config.accessToken) {
|
|
2641
|
+
headers["Access-Token"] = config.accessToken;
|
|
2642
|
+
}
|
|
2643
|
+
const res = await fetcher(url, {
|
|
2644
|
+
method: "POST",
|
|
2645
|
+
headers,
|
|
2646
|
+
body: JSON.stringify(tiktokPayload)
|
|
2647
|
+
});
|
|
2648
|
+
const resJson = await res.json().catch(() => ({}));
|
|
2649
|
+
if (!res.ok) {
|
|
2650
|
+
return {
|
|
2651
|
+
provider: "tiktok",
|
|
2652
|
+
success: false,
|
|
2653
|
+
status: res.status,
|
|
2654
|
+
error: resJson?.message || `HTTP ${res.status}`,
|
|
2655
|
+
data: resJson
|
|
2656
|
+
};
|
|
2657
|
+
}
|
|
2658
|
+
return { provider: "tiktok", success: true, status: res.status, data: resJson };
|
|
2659
|
+
} catch (err) {
|
|
2660
|
+
return {
|
|
2661
|
+
provider: "tiktok",
|
|
2662
|
+
success: false,
|
|
2663
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2664
|
+
};
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
async function sendGa4MeasurementEvent(event, config, options) {
|
|
2668
|
+
const fetcher = options?.fetchFn || globalThis.fetch;
|
|
2669
|
+
if (!config.measurementId) {
|
|
2670
|
+
return { provider: "google", success: false, error: "GA4 Measurement ID is missing" };
|
|
2671
|
+
}
|
|
2672
|
+
const clientId = event.userData?.clientId || event.userData?.externalId || "anonymous_client";
|
|
2673
|
+
const ga4Payload = {
|
|
2674
|
+
client_id: clientId,
|
|
2675
|
+
events: [
|
|
2676
|
+
{
|
|
2677
|
+
name: event.eventName.toLowerCase().replace(/[^a-z0-9_]/g, "_"),
|
|
2678
|
+
params: {
|
|
2679
|
+
...event.params || {},
|
|
2680
|
+
...event.customData || {},
|
|
2681
|
+
event_id: event.eventId
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
]
|
|
2685
|
+
};
|
|
2686
|
+
if (options?.simulateInDebug) {
|
|
2687
|
+
options.onLog?.("[GA4 Measurement Protocol SIMULATED]", ga4Payload);
|
|
2688
|
+
return { provider: "google", success: true, data: { simulated: true, payload: ga4Payload } };
|
|
2689
|
+
}
|
|
2690
|
+
try {
|
|
2691
|
+
let url = `https://www.google-analytics.com/mp/collect?measurement_id=${encodeURIComponent(
|
|
2692
|
+
config.measurementId
|
|
2693
|
+
)}`;
|
|
2694
|
+
if (config.measurementProtocolSecret) {
|
|
2695
|
+
url += `&api_secret=${encodeURIComponent(config.measurementProtocolSecret)}`;
|
|
2696
|
+
}
|
|
2697
|
+
const res = await fetcher(url, {
|
|
2698
|
+
method: "POST",
|
|
2699
|
+
headers: { "Content-Type": "application/json" },
|
|
2700
|
+
body: JSON.stringify(ga4Payload)
|
|
2701
|
+
});
|
|
2702
|
+
return {
|
|
2703
|
+
provider: "google",
|
|
2704
|
+
success: res.ok,
|
|
2705
|
+
status: res.status,
|
|
2706
|
+
data: { status: res.status }
|
|
2707
|
+
};
|
|
2708
|
+
} catch (err) {
|
|
2709
|
+
return {
|
|
2710
|
+
provider: "google",
|
|
2711
|
+
success: false,
|
|
2712
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2713
|
+
};
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
async function sendCustomWebhookEvent(event, config, options) {
|
|
2717
|
+
const fetcher = options?.fetchFn || globalThis.fetch;
|
|
2718
|
+
if (!config.endpointUrl) {
|
|
2719
|
+
return { provider: "custom", success: false, error: "Custom Webhook URL is missing" };
|
|
2720
|
+
}
|
|
2721
|
+
const webhookPayload = {
|
|
2722
|
+
event: event.eventName,
|
|
2723
|
+
eventId: event.eventId,
|
|
2724
|
+
timestamp: event.eventTime ? event.eventTime * 1e3 : Date.now(),
|
|
2725
|
+
userData: event.userData,
|
|
2726
|
+
params: event.params,
|
|
2727
|
+
customData: event.customData,
|
|
2728
|
+
sourceUrl: event.eventSourceUrl || options?.sourceUrl
|
|
2729
|
+
};
|
|
2730
|
+
if (options?.simulateInDebug) {
|
|
2731
|
+
options.onLog?.("[Custom Webhook SIMULATED]", webhookPayload);
|
|
2732
|
+
return { provider: "custom", success: true, data: { simulated: true, payload: webhookPayload } };
|
|
2733
|
+
}
|
|
2734
|
+
try {
|
|
2735
|
+
const res = await fetcher(config.endpointUrl, {
|
|
2736
|
+
method: "POST",
|
|
2737
|
+
headers: {
|
|
2738
|
+
"Content-Type": "application/json",
|
|
2739
|
+
...config.headers || {}
|
|
2740
|
+
},
|
|
2741
|
+
body: JSON.stringify(webhookPayload)
|
|
2742
|
+
});
|
|
2743
|
+
const resJson = await res.json().catch(() => ({}));
|
|
2744
|
+
return {
|
|
2745
|
+
provider: "custom",
|
|
2746
|
+
success: res.ok,
|
|
2747
|
+
status: res.status,
|
|
2748
|
+
data: resJson
|
|
2749
|
+
};
|
|
2750
|
+
} catch (err) {
|
|
2751
|
+
return {
|
|
2752
|
+
provider: "custom",
|
|
2753
|
+
success: false,
|
|
2754
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2755
|
+
};
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2758
|
+
async function dispatchServerTracking(event, config, options) {
|
|
2759
|
+
const eventId = event.eventId || generateTrackingEventId();
|
|
2760
|
+
const eventWithId = { ...event, eventId };
|
|
2761
|
+
if (!config || config.enabled === false) {
|
|
2762
|
+
return {
|
|
2763
|
+
success: true,
|
|
2764
|
+
eventId,
|
|
2765
|
+
skipped: true,
|
|
2766
|
+
reason: "Tracking is disabled globally",
|
|
2767
|
+
results: {}
|
|
2768
|
+
};
|
|
2769
|
+
}
|
|
2770
|
+
const isDebug = Boolean(config.debugMode);
|
|
2771
|
+
const simulate = isDebug && options?.simulateInDebug !== false;
|
|
2772
|
+
const mergedOptions = {
|
|
2773
|
+
...options,
|
|
2774
|
+
simulateInDebug: simulate,
|
|
2775
|
+
onLog: (msg, data) => {
|
|
2776
|
+
if (isDebug) {
|
|
2777
|
+
console.log(`[KUBUILD Tracking] ${msg}`, data || "");
|
|
2778
|
+
}
|
|
2779
|
+
options?.onLog?.(msg, data);
|
|
2780
|
+
}
|
|
2781
|
+
};
|
|
2782
|
+
const providers = config.providers || {};
|
|
2783
|
+
const dispatchPromises = [];
|
|
2784
|
+
if (providers.meta && providers.meta.enabled !== false && providers.meta.capiEnabled) {
|
|
2785
|
+
dispatchPromises.push(
|
|
2786
|
+
sendMetaCapiEvent(eventWithId, providers.meta, mergedOptions).then((result) => ({
|
|
2787
|
+
key: "meta",
|
|
2788
|
+
result
|
|
2789
|
+
}))
|
|
2790
|
+
);
|
|
2791
|
+
}
|
|
2792
|
+
if (providers.tiktok && providers.tiktok.enabled !== false && providers.tiktok.eventsApiEnabled) {
|
|
2793
|
+
dispatchPromises.push(
|
|
2794
|
+
sendTikTokEventsApi(eventWithId, providers.tiktok, mergedOptions).then((result) => ({
|
|
2795
|
+
key: "tiktok",
|
|
2796
|
+
result
|
|
2797
|
+
}))
|
|
2798
|
+
);
|
|
2799
|
+
}
|
|
2800
|
+
if (providers.google && providers.google.enabled !== false && providers.google.measurementId && providers.google.measurementProtocolSecret) {
|
|
2801
|
+
dispatchPromises.push(
|
|
2802
|
+
sendGa4MeasurementEvent(eventWithId, providers.google, mergedOptions).then((result) => ({
|
|
2803
|
+
key: "google",
|
|
2804
|
+
result
|
|
2805
|
+
}))
|
|
2806
|
+
);
|
|
2807
|
+
}
|
|
2808
|
+
if (providers.custom && providers.custom.enabled !== false && providers.custom.endpointUrl) {
|
|
2809
|
+
dispatchPromises.push(
|
|
2810
|
+
sendCustomWebhookEvent(eventWithId, providers.custom, mergedOptions).then((result) => ({
|
|
2811
|
+
key: "custom",
|
|
2812
|
+
result
|
|
2813
|
+
}))
|
|
2814
|
+
);
|
|
2815
|
+
}
|
|
2816
|
+
const settled = await Promise.allSettled(dispatchPromises);
|
|
2817
|
+
const results = {};
|
|
2818
|
+
let overallSuccess = true;
|
|
2819
|
+
for (const item of settled) {
|
|
2820
|
+
if (item.status === "fulfilled") {
|
|
2821
|
+
results[item.value.key] = item.value.result;
|
|
2822
|
+
if (!item.value.result.success) {
|
|
2823
|
+
overallSuccess = false;
|
|
2824
|
+
}
|
|
2825
|
+
} else {
|
|
2826
|
+
overallSuccess = false;
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
return {
|
|
2830
|
+
success: overallSuccess,
|
|
2831
|
+
eventId,
|
|
2832
|
+
results
|
|
2833
|
+
};
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2457
2836
|
// src/io/exporter.ts
|
|
2458
2837
|
import { zipSync, strToU8 } from "fflate";
|
|
2459
2838
|
import {
|
|
@@ -2719,6 +3098,7 @@ function sanitizeHtml(rawHtml) {
|
|
|
2719
3098
|
// src/validation/validator.ts
|
|
2720
3099
|
function validateDocument(input, options = {}) {
|
|
2721
3100
|
const errors = [];
|
|
3101
|
+
const warnings = [];
|
|
2722
3102
|
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
2723
3103
|
return {
|
|
2724
3104
|
valid: false,
|
|
@@ -2729,7 +3109,8 @@ function validateDocument(input, options = {}) {
|
|
|
2729
3109
|
message: "Document must be a valid non-null object",
|
|
2730
3110
|
path: ""
|
|
2731
3111
|
}
|
|
2732
|
-
]
|
|
3112
|
+
],
|
|
3113
|
+
warnings: []
|
|
2733
3114
|
};
|
|
2734
3115
|
}
|
|
2735
3116
|
const doc = input;
|
|
@@ -2747,7 +3128,8 @@ function validateDocument(input, options = {}) {
|
|
|
2747
3128
|
return {
|
|
2748
3129
|
valid: false,
|
|
2749
3130
|
success: false,
|
|
2750
|
-
errors
|
|
3131
|
+
errors,
|
|
3132
|
+
warnings: []
|
|
2751
3133
|
};
|
|
2752
3134
|
}
|
|
2753
3135
|
}
|
|
@@ -2774,7 +3156,8 @@ function validateDocument(input, options = {}) {
|
|
|
2774
3156
|
return {
|
|
2775
3157
|
valid: false,
|
|
2776
3158
|
success: false,
|
|
2777
|
-
errors
|
|
3159
|
+
errors,
|
|
3160
|
+
warnings
|
|
2778
3161
|
};
|
|
2779
3162
|
}
|
|
2780
3163
|
const rootNode = doc.document;
|
|
@@ -2813,7 +3196,8 @@ function validateDocument(input, options = {}) {
|
|
|
2813
3196
|
seenIds,
|
|
2814
3197
|
visitedObjects,
|
|
2815
3198
|
options,
|
|
2816
|
-
errors
|
|
3199
|
+
errors,
|
|
3200
|
+
warnings
|
|
2817
3201
|
);
|
|
2818
3202
|
if (errors.length === 0) {
|
|
2819
3203
|
const zodResult = PageDocumentSchema.safeParse(input);
|
|
@@ -2831,6 +3215,7 @@ function validateDocument(input, options = {}) {
|
|
|
2831
3215
|
valid: true,
|
|
2832
3216
|
success: true,
|
|
2833
3217
|
errors: [],
|
|
3218
|
+
warnings,
|
|
2834
3219
|
data: zodResult.data
|
|
2835
3220
|
};
|
|
2836
3221
|
}
|
|
@@ -2838,10 +3223,11 @@ function validateDocument(input, options = {}) {
|
|
|
2838
3223
|
return {
|
|
2839
3224
|
valid: errors.length === 0,
|
|
2840
3225
|
success: errors.length === 0,
|
|
2841
|
-
errors
|
|
3226
|
+
errors,
|
|
3227
|
+
warnings
|
|
2842
3228
|
};
|
|
2843
3229
|
}
|
|
2844
|
-
function validateNodeRecursive(nodeObj, currentPath, parentNode, seenIds, visitedObjects, options, errors) {
|
|
3230
|
+
function validateNodeRecursive(nodeObj, currentPath, parentNode, seenIds, visitedObjects, options, errors, warnings) {
|
|
2845
3231
|
if (visitedObjects.has(nodeObj)) {
|
|
2846
3232
|
errors.push({
|
|
2847
3233
|
code: "TREE_CYCLE_DETECTED",
|
|
@@ -2950,12 +3336,22 @@ function validateNodeRecursive(nodeObj, currentPath, parentNode, seenIds, visite
|
|
|
2950
3336
|
const childIsKnown = !options.componentRegistry || options.componentRegistry.has(childType);
|
|
2951
3337
|
const childCategory = options.componentRegistry?.get(childType)?.category;
|
|
2952
3338
|
if (componentDef?.allowedChildren && childType && childIsKnown && !componentDef.allowedChildren.includes(childType) && !componentDef.allowedChildren.includes("*") && !(childCategory && componentDef.allowedChildren.includes(childCategory))) {
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
3339
|
+
const violationMessage = `Component type "${childType}" is not allowed as a child of "${effectiveNodeType}". Allowed types: ${componentDef.allowedChildren.join(", ")}`;
|
|
3340
|
+
if (options.strictChildPolicy) {
|
|
3341
|
+
errors.push({
|
|
3342
|
+
code: "CHILD_POLICY_VIOLATION",
|
|
3343
|
+
message: violationMessage,
|
|
3344
|
+
path: `${childPath}/type`,
|
|
3345
|
+
nodeId: typeof childRecord.id === "string" ? childRecord.id : void 0
|
|
3346
|
+
});
|
|
3347
|
+
} else {
|
|
3348
|
+
warnings.push({
|
|
3349
|
+
code: "CHILD_POLICY_VIOLATION",
|
|
3350
|
+
message: violationMessage,
|
|
3351
|
+
path: `${childPath}/type`,
|
|
3352
|
+
nodeId: typeof childRecord.id === "string" ? childRecord.id : void 0
|
|
3353
|
+
});
|
|
3354
|
+
}
|
|
2959
3355
|
}
|
|
2960
3356
|
validateNodeRecursive(
|
|
2961
3357
|
childRecord,
|
|
@@ -2964,7 +3360,8 @@ function validateNodeRecursive(nodeObj, currentPath, parentNode, seenIds, visite
|
|
|
2964
3360
|
seenIds,
|
|
2965
3361
|
visitedObjects,
|
|
2966
3362
|
options,
|
|
2967
|
-
errors
|
|
3363
|
+
errors,
|
|
3364
|
+
warnings
|
|
2968
3365
|
);
|
|
2969
3366
|
}
|
|
2970
3367
|
}
|
|
@@ -5624,6 +6021,7 @@ export {
|
|
|
5624
6021
|
defaultIdGenerator,
|
|
5625
6022
|
defaultMigrationRegistry,
|
|
5626
6023
|
defaultRenameAssetStrategy,
|
|
6024
|
+
dispatchServerTracking,
|
|
5627
6025
|
duplicateNode,
|
|
5628
6026
|
evaluateActionCondition,
|
|
5629
6027
|
evaluateCondition,
|
|
@@ -5643,6 +6041,7 @@ export {
|
|
|
5643
6041
|
findMissingComponentNodes,
|
|
5644
6042
|
findNodeById,
|
|
5645
6043
|
findNodeLocation,
|
|
6044
|
+
generateTrackingEventId,
|
|
5646
6045
|
getActiveArtboard,
|
|
5647
6046
|
getAncestorChain,
|
|
5648
6047
|
getComponentArtboards,
|
|
@@ -5652,6 +6051,7 @@ export {
|
|
|
5652
6051
|
getPageArtboards,
|
|
5653
6052
|
getParentNodeId,
|
|
5654
6053
|
hasTemplateExpressions,
|
|
6054
|
+
hashSha256,
|
|
5655
6055
|
importPackage,
|
|
5656
6056
|
importStoraPackage,
|
|
5657
6057
|
insertNode,
|
|
@@ -5669,6 +6069,9 @@ export {
|
|
|
5669
6069
|
loadProjectDocument,
|
|
5670
6070
|
migrateDocument,
|
|
5671
6071
|
moveNode,
|
|
6072
|
+
normalizeEmail,
|
|
6073
|
+
normalizePhone,
|
|
6074
|
+
normalizeText,
|
|
5672
6075
|
preflightPackage,
|
|
5673
6076
|
previewImportPackage,
|
|
5674
6077
|
remapAssetReferences,
|
|
@@ -5684,6 +6087,10 @@ export {
|
|
|
5684
6087
|
sanitizeHtml,
|
|
5685
6088
|
sanitizeUrl,
|
|
5686
6089
|
saveDraftAsTemplate,
|
|
6090
|
+
sendCustomWebhookEvent,
|
|
6091
|
+
sendGa4MeasurementEvent,
|
|
6092
|
+
sendMetaCapiEvent,
|
|
6093
|
+
sendTikTokEventsApi,
|
|
5687
6094
|
setActiveArtboard,
|
|
5688
6095
|
sha256Sync,
|
|
5689
6096
|
ungroupNodeFrame,
|