@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 CHANGED
@@ -58,6 +58,7 @@ __export(index_exports, {
58
58
  defaultIdGenerator: () => defaultIdGenerator,
59
59
  defaultMigrationRegistry: () => defaultMigrationRegistry,
60
60
  defaultRenameAssetStrategy: () => defaultRenameAssetStrategy,
61
+ dispatchServerTracking: () => dispatchServerTracking,
61
62
  duplicateNode: () => duplicateNode,
62
63
  evaluateActionCondition: () => evaluateActionCondition,
63
64
  evaluateCondition: () => evaluateCondition,
@@ -77,6 +78,7 @@ __export(index_exports, {
77
78
  findMissingComponentNodes: () => findMissingComponentNodes,
78
79
  findNodeById: () => findNodeById,
79
80
  findNodeLocation: () => findNodeLocation,
81
+ generateTrackingEventId: () => generateTrackingEventId,
80
82
  getActiveArtboard: () => getActiveArtboard,
81
83
  getAncestorChain: () => getAncestorChain,
82
84
  getComponentArtboards: () => getComponentArtboards,
@@ -86,6 +88,7 @@ __export(index_exports, {
86
88
  getPageArtboards: () => getPageArtboards,
87
89
  getParentNodeId: () => getParentNodeId,
88
90
  hasTemplateExpressions: () => hasTemplateExpressions,
91
+ hashSha256: () => hashSha256,
89
92
  importPackage: () => importPackage,
90
93
  importStoraPackage: () => importStoraPackage,
91
94
  insertNode: () => insertNode,
@@ -103,6 +106,9 @@ __export(index_exports, {
103
106
  loadProjectDocument: () => loadProjectDocument,
104
107
  migrateDocument: () => migrateDocument,
105
108
  moveNode: () => moveNode,
109
+ normalizeEmail: () => normalizeEmail,
110
+ normalizePhone: () => normalizePhone,
111
+ normalizeText: () => normalizeText,
106
112
  preflightPackage: () => preflightPackage,
107
113
  previewImportPackage: () => previewImportPackage,
108
114
  remapAssetReferences: () => remapAssetReferences,
@@ -118,6 +124,10 @@ __export(index_exports, {
118
124
  sanitizeHtml: () => sanitizeHtml,
119
125
  sanitizeUrl: () => sanitizeUrl,
120
126
  saveDraftAsTemplate: () => saveDraftAsTemplate,
127
+ sendCustomWebhookEvent: () => sendCustomWebhookEvent,
128
+ sendGa4MeasurementEvent: () => sendGa4MeasurementEvent,
129
+ sendMetaCapiEvent: () => sendMetaCapiEvent,
130
+ sendTikTokEventsApi: () => sendTikTokEventsApi,
121
131
  setActiveArtboard: () => setActiveArtboard,
122
132
  sha256Sync: () => sha256Sync,
123
133
  ungroupNodeFrame: () => ungroupNodeFrame,
@@ -2584,6 +2594,385 @@ function createRuntimeStore(initialState) {
2584
2594
  return new RuntimeStateStore(initialState);
2585
2595
  }
2586
2596
 
2597
+ // src/runtime/server-tracking.ts
2598
+ function normalizeEmail(email) {
2599
+ return (email || "").trim().toLowerCase();
2600
+ }
2601
+ function normalizePhone(phone) {
2602
+ return (phone || "").replace(/[^0-9]/g, "");
2603
+ }
2604
+ function normalizeText(text) {
2605
+ return (text || "").trim().toLowerCase();
2606
+ }
2607
+ async function hashSha256(value) {
2608
+ const normalized = (value || "").trim();
2609
+ if (!normalized) return "";
2610
+ if (typeof globalThis.crypto?.subtle?.digest === "function") {
2611
+ const encoder = new TextEncoder();
2612
+ const data = encoder.encode(normalized);
2613
+ const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", data);
2614
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
2615
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
2616
+ }
2617
+ throw new Error("Web Crypto API (crypto.subtle) is not available in current runtime environment.");
2618
+ }
2619
+ function generateTrackingEventId(prefix = "evt") {
2620
+ const timestamp = Date.now().toString(36);
2621
+ const randomPart = Math.random().toString(36).substring(2, 10);
2622
+ return `${prefix}_${timestamp}_${randomPart}`;
2623
+ }
2624
+ async function sendMetaCapiEvent(event, config, options) {
2625
+ const fetcher = options?.fetchFn || globalThis.fetch;
2626
+ if (!config.pixelId) {
2627
+ return {
2628
+ provider: "meta",
2629
+ success: false,
2630
+ error: "Meta Pixel ID is missing"
2631
+ };
2632
+ }
2633
+ const rawUserData = event.userData || {};
2634
+ const hashedUserData = {};
2635
+ if (rawUserData.email) {
2636
+ hashedUserData.em = [await hashSha256(normalizeEmail(String(rawUserData.email)))];
2637
+ }
2638
+ if (rawUserData.phone) {
2639
+ hashedUserData.ph = [await hashSha256(normalizePhone(String(rawUserData.phone)))];
2640
+ }
2641
+ if (rawUserData.firstName) {
2642
+ hashedUserData.fn = [await hashSha256(normalizeText(String(rawUserData.firstName)))];
2643
+ }
2644
+ if (rawUserData.lastName) {
2645
+ hashedUserData.ln = [await hashSha256(normalizeText(String(rawUserData.lastName)))];
2646
+ }
2647
+ if (rawUserData.city) {
2648
+ hashedUserData.ct = [await hashSha256(normalizeText(String(rawUserData.city)))];
2649
+ }
2650
+ if (rawUserData.state) {
2651
+ hashedUserData.st = [await hashSha256(normalizeText(String(rawUserData.state)))];
2652
+ }
2653
+ if (rawUserData.zip) {
2654
+ hashedUserData.zp = [await hashSha256(normalizeText(String(rawUserData.zip)))];
2655
+ }
2656
+ if (rawUserData.country) {
2657
+ hashedUserData.country = [await hashSha256(normalizeText(String(rawUserData.country)))];
2658
+ }
2659
+ const clientIp = rawUserData.clientIp || options?.clientIp;
2660
+ if (clientIp) hashedUserData.client_ip_address = clientIp;
2661
+ const clientUserAgent = rawUserData.clientUserAgent || options?.clientUserAgent;
2662
+ if (clientUserAgent) hashedUserData.client_user_agent = clientUserAgent;
2663
+ if (rawUserData.fbp) hashedUserData.fbp = rawUserData.fbp;
2664
+ if (rawUserData.fbc) hashedUserData.fbc = rawUserData.fbc;
2665
+ if (rawUserData.externalId) {
2666
+ hashedUserData.external_id = [await hashSha256(String(rawUserData.externalId))];
2667
+ }
2668
+ const eventTime = event.eventTime || Math.floor(Date.now() / 1e3);
2669
+ const eventSourceUrl = event.eventSourceUrl || options?.sourceUrl;
2670
+ const capiPayload = {
2671
+ data: [
2672
+ {
2673
+ event_name: event.eventName,
2674
+ event_time: eventTime,
2675
+ event_id: event.eventId,
2676
+ event_source_url: eventSourceUrl,
2677
+ action_source: event.actionSource || "website",
2678
+ user_data: hashedUserData,
2679
+ custom_data: {
2680
+ ...event.params || {},
2681
+ ...event.customData || {}
2682
+ }
2683
+ }
2684
+ ]
2685
+ };
2686
+ if (config.testEventCode) {
2687
+ capiPayload.test_event_code = config.testEventCode;
2688
+ }
2689
+ if (options?.simulateInDebug) {
2690
+ options.onLog?.("[Meta CAPI SIMULATED]", capiPayload);
2691
+ return { provider: "meta", success: true, data: { simulated: true, payload: capiPayload } };
2692
+ }
2693
+ try {
2694
+ const url = `https://graph.facebook.com/v19.0/${encodeURIComponent(config.pixelId)}/events`;
2695
+ const headers = {
2696
+ "Content-Type": "application/json"
2697
+ };
2698
+ if (config.capiAccessToken) {
2699
+ headers["Authorization"] = `Bearer ${config.capiAccessToken}`;
2700
+ }
2701
+ const res = await fetcher(url, {
2702
+ method: "POST",
2703
+ headers,
2704
+ body: JSON.stringify(capiPayload)
2705
+ });
2706
+ const resJson = await res.json().catch(() => ({}));
2707
+ if (!res.ok) {
2708
+ return {
2709
+ provider: "meta",
2710
+ success: false,
2711
+ status: res.status,
2712
+ error: resJson?.error?.message || `HTTP ${res.status}`,
2713
+ data: resJson
2714
+ };
2715
+ }
2716
+ return {
2717
+ provider: "meta",
2718
+ success: true,
2719
+ status: res.status,
2720
+ data: resJson
2721
+ };
2722
+ } catch (err) {
2723
+ return {
2724
+ provider: "meta",
2725
+ success: false,
2726
+ error: err instanceof Error ? err.message : String(err)
2727
+ };
2728
+ }
2729
+ }
2730
+ async function sendTikTokEventsApi(event, config, options) {
2731
+ const fetcher = options?.fetchFn || globalThis.fetch;
2732
+ if (!config.pixelId) {
2733
+ return { provider: "tiktok", success: false, error: "TikTok Pixel ID is missing" };
2734
+ }
2735
+ const rawUserData = event.userData || {};
2736
+ const user = {};
2737
+ if (rawUserData.email) {
2738
+ user.email = await hashSha256(normalizeEmail(String(rawUserData.email)));
2739
+ }
2740
+ if (rawUserData.phone) {
2741
+ user.phone_number = await hashSha256(normalizePhone(String(rawUserData.phone)));
2742
+ }
2743
+ if (rawUserData.clientIp || options?.clientIp) {
2744
+ user.ip = rawUserData.clientIp || options?.clientIp;
2745
+ }
2746
+ if (rawUserData.clientUserAgent || options?.clientUserAgent) {
2747
+ user.user_agent = rawUserData.clientUserAgent || options?.clientUserAgent;
2748
+ }
2749
+ const tiktokPayload = {
2750
+ event_source: "web",
2751
+ event_source_id: config.pixelId,
2752
+ data: [
2753
+ {
2754
+ event: event.eventName,
2755
+ event_id: event.eventId,
2756
+ timestamp: new Date(event.eventTime ? event.eventTime * 1e3 : Date.now()).toISOString(),
2757
+ user,
2758
+ properties: {
2759
+ ...event.params || {},
2760
+ ...event.customData || {}
2761
+ },
2762
+ page: {
2763
+ url: event.eventSourceUrl || options?.sourceUrl
2764
+ }
2765
+ }
2766
+ ]
2767
+ };
2768
+ if (config.testEventCode) {
2769
+ tiktokPayload.test_event_code = config.testEventCode;
2770
+ }
2771
+ if (options?.simulateInDebug) {
2772
+ options.onLog?.("[TikTok Events API SIMULATED]", tiktokPayload);
2773
+ return { provider: "tiktok", success: true, data: { simulated: true, payload: tiktokPayload } };
2774
+ }
2775
+ try {
2776
+ const url = "https://business-api.tiktok.com/open_api/v1.3/event/track/";
2777
+ const headers = {
2778
+ "Content-Type": "application/json"
2779
+ };
2780
+ if (config.accessToken) {
2781
+ headers["Access-Token"] = config.accessToken;
2782
+ }
2783
+ const res = await fetcher(url, {
2784
+ method: "POST",
2785
+ headers,
2786
+ body: JSON.stringify(tiktokPayload)
2787
+ });
2788
+ const resJson = await res.json().catch(() => ({}));
2789
+ if (!res.ok) {
2790
+ return {
2791
+ provider: "tiktok",
2792
+ success: false,
2793
+ status: res.status,
2794
+ error: resJson?.message || `HTTP ${res.status}`,
2795
+ data: resJson
2796
+ };
2797
+ }
2798
+ return { provider: "tiktok", success: true, status: res.status, data: resJson };
2799
+ } catch (err) {
2800
+ return {
2801
+ provider: "tiktok",
2802
+ success: false,
2803
+ error: err instanceof Error ? err.message : String(err)
2804
+ };
2805
+ }
2806
+ }
2807
+ async function sendGa4MeasurementEvent(event, config, options) {
2808
+ const fetcher = options?.fetchFn || globalThis.fetch;
2809
+ if (!config.measurementId) {
2810
+ return { provider: "google", success: false, error: "GA4 Measurement ID is missing" };
2811
+ }
2812
+ const clientId = event.userData?.clientId || event.userData?.externalId || "anonymous_client";
2813
+ const ga4Payload = {
2814
+ client_id: clientId,
2815
+ events: [
2816
+ {
2817
+ name: event.eventName.toLowerCase().replace(/[^a-z0-9_]/g, "_"),
2818
+ params: {
2819
+ ...event.params || {},
2820
+ ...event.customData || {},
2821
+ event_id: event.eventId
2822
+ }
2823
+ }
2824
+ ]
2825
+ };
2826
+ if (options?.simulateInDebug) {
2827
+ options.onLog?.("[GA4 Measurement Protocol SIMULATED]", ga4Payload);
2828
+ return { provider: "google", success: true, data: { simulated: true, payload: ga4Payload } };
2829
+ }
2830
+ try {
2831
+ let url = `https://www.google-analytics.com/mp/collect?measurement_id=${encodeURIComponent(
2832
+ config.measurementId
2833
+ )}`;
2834
+ if (config.measurementProtocolSecret) {
2835
+ url += `&api_secret=${encodeURIComponent(config.measurementProtocolSecret)}`;
2836
+ }
2837
+ const res = await fetcher(url, {
2838
+ method: "POST",
2839
+ headers: { "Content-Type": "application/json" },
2840
+ body: JSON.stringify(ga4Payload)
2841
+ });
2842
+ return {
2843
+ provider: "google",
2844
+ success: res.ok,
2845
+ status: res.status,
2846
+ data: { status: res.status }
2847
+ };
2848
+ } catch (err) {
2849
+ return {
2850
+ provider: "google",
2851
+ success: false,
2852
+ error: err instanceof Error ? err.message : String(err)
2853
+ };
2854
+ }
2855
+ }
2856
+ async function sendCustomWebhookEvent(event, config, options) {
2857
+ const fetcher = options?.fetchFn || globalThis.fetch;
2858
+ if (!config.endpointUrl) {
2859
+ return { provider: "custom", success: false, error: "Custom Webhook URL is missing" };
2860
+ }
2861
+ const webhookPayload = {
2862
+ event: event.eventName,
2863
+ eventId: event.eventId,
2864
+ timestamp: event.eventTime ? event.eventTime * 1e3 : Date.now(),
2865
+ userData: event.userData,
2866
+ params: event.params,
2867
+ customData: event.customData,
2868
+ sourceUrl: event.eventSourceUrl || options?.sourceUrl
2869
+ };
2870
+ if (options?.simulateInDebug) {
2871
+ options.onLog?.("[Custom Webhook SIMULATED]", webhookPayload);
2872
+ return { provider: "custom", success: true, data: { simulated: true, payload: webhookPayload } };
2873
+ }
2874
+ try {
2875
+ const res = await fetcher(config.endpointUrl, {
2876
+ method: "POST",
2877
+ headers: {
2878
+ "Content-Type": "application/json",
2879
+ ...config.headers || {}
2880
+ },
2881
+ body: JSON.stringify(webhookPayload)
2882
+ });
2883
+ const resJson = await res.json().catch(() => ({}));
2884
+ return {
2885
+ provider: "custom",
2886
+ success: res.ok,
2887
+ status: res.status,
2888
+ data: resJson
2889
+ };
2890
+ } catch (err) {
2891
+ return {
2892
+ provider: "custom",
2893
+ success: false,
2894
+ error: err instanceof Error ? err.message : String(err)
2895
+ };
2896
+ }
2897
+ }
2898
+ async function dispatchServerTracking(event, config, options) {
2899
+ const eventId = event.eventId || generateTrackingEventId();
2900
+ const eventWithId = { ...event, eventId };
2901
+ if (!config || config.enabled === false) {
2902
+ return {
2903
+ success: true,
2904
+ eventId,
2905
+ skipped: true,
2906
+ reason: "Tracking is disabled globally",
2907
+ results: {}
2908
+ };
2909
+ }
2910
+ const isDebug = Boolean(config.debugMode);
2911
+ const simulate = isDebug && options?.simulateInDebug !== false;
2912
+ const mergedOptions = {
2913
+ ...options,
2914
+ simulateInDebug: simulate,
2915
+ onLog: (msg, data) => {
2916
+ if (isDebug) {
2917
+ console.log(`[KUBUILD Tracking] ${msg}`, data || "");
2918
+ }
2919
+ options?.onLog?.(msg, data);
2920
+ }
2921
+ };
2922
+ const providers = config.providers || {};
2923
+ const dispatchPromises = [];
2924
+ if (providers.meta && providers.meta.enabled !== false && providers.meta.capiEnabled) {
2925
+ dispatchPromises.push(
2926
+ sendMetaCapiEvent(eventWithId, providers.meta, mergedOptions).then((result) => ({
2927
+ key: "meta",
2928
+ result
2929
+ }))
2930
+ );
2931
+ }
2932
+ if (providers.tiktok && providers.tiktok.enabled !== false && providers.tiktok.eventsApiEnabled) {
2933
+ dispatchPromises.push(
2934
+ sendTikTokEventsApi(eventWithId, providers.tiktok, mergedOptions).then((result) => ({
2935
+ key: "tiktok",
2936
+ result
2937
+ }))
2938
+ );
2939
+ }
2940
+ if (providers.google && providers.google.enabled !== false && providers.google.measurementId && providers.google.measurementProtocolSecret) {
2941
+ dispatchPromises.push(
2942
+ sendGa4MeasurementEvent(eventWithId, providers.google, mergedOptions).then((result) => ({
2943
+ key: "google",
2944
+ result
2945
+ }))
2946
+ );
2947
+ }
2948
+ if (providers.custom && providers.custom.enabled !== false && providers.custom.endpointUrl) {
2949
+ dispatchPromises.push(
2950
+ sendCustomWebhookEvent(eventWithId, providers.custom, mergedOptions).then((result) => ({
2951
+ key: "custom",
2952
+ result
2953
+ }))
2954
+ );
2955
+ }
2956
+ const settled = await Promise.allSettled(dispatchPromises);
2957
+ const results = {};
2958
+ let overallSuccess = true;
2959
+ for (const item of settled) {
2960
+ if (item.status === "fulfilled") {
2961
+ results[item.value.key] = item.value.result;
2962
+ if (!item.value.result.success) {
2963
+ overallSuccess = false;
2964
+ }
2965
+ } else {
2966
+ overallSuccess = false;
2967
+ }
2968
+ }
2969
+ return {
2970
+ success: overallSuccess,
2971
+ eventId,
2972
+ results
2973
+ };
2974
+ }
2975
+
2587
2976
  // src/io/exporter.ts
2588
2977
  var import_fflate = require("fflate");
2589
2978
  var import_schema6 = require("@kubuild/schema");
@@ -2843,6 +3232,7 @@ function sanitizeHtml(rawHtml) {
2843
3232
  // src/validation/validator.ts
2844
3233
  function validateDocument(input, options = {}) {
2845
3234
  const errors = [];
3235
+ const warnings = [];
2846
3236
  if (!input || typeof input !== "object" || Array.isArray(input)) {
2847
3237
  return {
2848
3238
  valid: false,
@@ -2853,7 +3243,8 @@ function validateDocument(input, options = {}) {
2853
3243
  message: "Document must be a valid non-null object",
2854
3244
  path: ""
2855
3245
  }
2856
- ]
3246
+ ],
3247
+ warnings: []
2857
3248
  };
2858
3249
  }
2859
3250
  const doc = input;
@@ -2871,7 +3262,8 @@ function validateDocument(input, options = {}) {
2871
3262
  return {
2872
3263
  valid: false,
2873
3264
  success: false,
2874
- errors
3265
+ errors,
3266
+ warnings: []
2875
3267
  };
2876
3268
  }
2877
3269
  }
@@ -2898,7 +3290,8 @@ function validateDocument(input, options = {}) {
2898
3290
  return {
2899
3291
  valid: false,
2900
3292
  success: false,
2901
- errors
3293
+ errors,
3294
+ warnings
2902
3295
  };
2903
3296
  }
2904
3297
  const rootNode = doc.document;
@@ -2937,7 +3330,8 @@ function validateDocument(input, options = {}) {
2937
3330
  seenIds,
2938
3331
  visitedObjects,
2939
3332
  options,
2940
- errors
3333
+ errors,
3334
+ warnings
2941
3335
  );
2942
3336
  if (errors.length === 0) {
2943
3337
  const zodResult = import_schema5.PageDocumentSchema.safeParse(input);
@@ -2955,6 +3349,7 @@ function validateDocument(input, options = {}) {
2955
3349
  valid: true,
2956
3350
  success: true,
2957
3351
  errors: [],
3352
+ warnings,
2958
3353
  data: zodResult.data
2959
3354
  };
2960
3355
  }
@@ -2962,10 +3357,11 @@ function validateDocument(input, options = {}) {
2962
3357
  return {
2963
3358
  valid: errors.length === 0,
2964
3359
  success: errors.length === 0,
2965
- errors
3360
+ errors,
3361
+ warnings
2966
3362
  };
2967
3363
  }
2968
- function validateNodeRecursive(nodeObj, currentPath, parentNode, seenIds, visitedObjects, options, errors) {
3364
+ function validateNodeRecursive(nodeObj, currentPath, parentNode, seenIds, visitedObjects, options, errors, warnings) {
2969
3365
  if (visitedObjects.has(nodeObj)) {
2970
3366
  errors.push({
2971
3367
  code: "TREE_CYCLE_DETECTED",
@@ -3074,12 +3470,22 @@ function validateNodeRecursive(nodeObj, currentPath, parentNode, seenIds, visite
3074
3470
  const childIsKnown = !options.componentRegistry || options.componentRegistry.has(childType);
3075
3471
  const childCategory = options.componentRegistry?.get(childType)?.category;
3076
3472
  if (componentDef?.allowedChildren && childType && childIsKnown && !componentDef.allowedChildren.includes(childType) && !componentDef.allowedChildren.includes("*") && !(childCategory && componentDef.allowedChildren.includes(childCategory))) {
3077
- errors.push({
3078
- code: "CHILD_POLICY_VIOLATION",
3079
- message: `Component type "${childType}" is not allowed as a child of "${effectiveNodeType}". Allowed types: ${componentDef.allowedChildren.join(", ")}`,
3080
- path: `${childPath}/type`,
3081
- nodeId: typeof childRecord.id === "string" ? childRecord.id : void 0
3082
- });
3473
+ const violationMessage = `Component type "${childType}" is not allowed as a child of "${effectiveNodeType}". Allowed types: ${componentDef.allowedChildren.join(", ")}`;
3474
+ if (options.strictChildPolicy) {
3475
+ errors.push({
3476
+ code: "CHILD_POLICY_VIOLATION",
3477
+ message: violationMessage,
3478
+ path: `${childPath}/type`,
3479
+ nodeId: typeof childRecord.id === "string" ? childRecord.id : void 0
3480
+ });
3481
+ } else {
3482
+ warnings.push({
3483
+ code: "CHILD_POLICY_VIOLATION",
3484
+ message: violationMessage,
3485
+ path: `${childPath}/type`,
3486
+ nodeId: typeof childRecord.id === "string" ? childRecord.id : void 0
3487
+ });
3488
+ }
3083
3489
  }
3084
3490
  validateNodeRecursive(
3085
3491
  childRecord,
@@ -3088,7 +3494,8 @@ function validateNodeRecursive(nodeObj, currentPath, parentNode, seenIds, visite
3088
3494
  seenIds,
3089
3495
  visitedObjects,
3090
3496
  options,
3091
- errors
3497
+ errors,
3498
+ warnings
3092
3499
  );
3093
3500
  }
3094
3501
  }
@@ -5730,6 +6137,7 @@ function isFormValid(errors) {
5730
6137
  defaultIdGenerator,
5731
6138
  defaultMigrationRegistry,
5732
6139
  defaultRenameAssetStrategy,
6140
+ dispatchServerTracking,
5733
6141
  duplicateNode,
5734
6142
  evaluateActionCondition,
5735
6143
  evaluateCondition,
@@ -5749,6 +6157,7 @@ function isFormValid(errors) {
5749
6157
  findMissingComponentNodes,
5750
6158
  findNodeById,
5751
6159
  findNodeLocation,
6160
+ generateTrackingEventId,
5752
6161
  getActiveArtboard,
5753
6162
  getAncestorChain,
5754
6163
  getComponentArtboards,
@@ -5758,6 +6167,7 @@ function isFormValid(errors) {
5758
6167
  getPageArtboards,
5759
6168
  getParentNodeId,
5760
6169
  hasTemplateExpressions,
6170
+ hashSha256,
5761
6171
  importPackage,
5762
6172
  importStoraPackage,
5763
6173
  insertNode,
@@ -5775,6 +6185,9 @@ function isFormValid(errors) {
5775
6185
  loadProjectDocument,
5776
6186
  migrateDocument,
5777
6187
  moveNode,
6188
+ normalizeEmail,
6189
+ normalizePhone,
6190
+ normalizeText,
5778
6191
  preflightPackage,
5779
6192
  previewImportPackage,
5780
6193
  remapAssetReferences,
@@ -5790,6 +6203,10 @@ function isFormValid(errors) {
5790
6203
  sanitizeHtml,
5791
6204
  sanitizeUrl,
5792
6205
  saveDraftAsTemplate,
6206
+ sendCustomWebhookEvent,
6207
+ sendGa4MeasurementEvent,
6208
+ sendMetaCapiEvent,
6209
+ sendTikTokEventsApi,
5793
6210
  setActiveArtboard,
5794
6211
  sha256Sync,
5795
6212
  ungroupNodeFrame,