@kubuild/core 0.6.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 +422 -103
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +400 -82
- 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 +1 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/server-tracking.d.ts +40 -7
- package/dist/runtime/server-tracking.d.ts.map +1 -1
- 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,6 +54,7 @@ __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,
|
|
@@ -120,6 +121,7 @@ __export(index_exports, {
|
|
|
120
121
|
resolveBinding: () => resolveBinding,
|
|
121
122
|
resolveBindingValue: () => resolveBindingValue,
|
|
122
123
|
resolvePropertyPath: () => resolvePropertyPath,
|
|
124
|
+
sanitizeDocumentTracking: () => sanitizeDocumentTracking,
|
|
123
125
|
sanitizeFilename: () => sanitizeFilename,
|
|
124
126
|
sanitizeHtml: () => sanitizeHtml,
|
|
125
127
|
sanitizeUrl: () => sanitizeUrl,
|
|
@@ -130,6 +132,8 @@ __export(index_exports, {
|
|
|
130
132
|
sendTikTokEventsApi: () => sendTikTokEventsApi,
|
|
131
133
|
setActiveArtboard: () => setActiveArtboard,
|
|
132
134
|
sha256Sync: () => sha256Sync,
|
|
135
|
+
stripDocumentTrackingSecretsInPlace: () => stripDocumentTrackingSecretsInPlace,
|
|
136
|
+
stripTrackingSecretsInPlace: () => stripTrackingSecretsInPlace,
|
|
133
137
|
ungroupNodeFrame: () => ungroupNodeFrame,
|
|
134
138
|
updateActions: () => updateActions,
|
|
135
139
|
updateAnimation: () => updateAnimation,
|
|
@@ -155,7 +159,7 @@ var import_schema = require("@kubuild/schema");
|
|
|
155
159
|
function createBlankDocument(title = "Untitled Page") {
|
|
156
160
|
return {
|
|
157
161
|
schema: import_schema.SCHEMA_NAME,
|
|
158
|
-
version:
|
|
162
|
+
version: import_schema.CURRENT_SCHEMA_VERSION,
|
|
159
163
|
metadata: {
|
|
160
164
|
title,
|
|
161
165
|
description: "",
|
|
@@ -1551,6 +1555,15 @@ var DocumentHistoryManager = class {
|
|
|
1551
1555
|
var FORBIDDEN_KEY_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
1552
1556
|
function resolveBinding(binding, context) {
|
|
1553
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
|
+
}
|
|
1554
1567
|
let current = context?.variables;
|
|
1555
1568
|
for (const segment of segments) {
|
|
1556
1569
|
if (FORBIDDEN_KEY_SEGMENTS.has(segment)) {
|
|
@@ -2122,14 +2135,14 @@ var ActionPipelineExecutor = class {
|
|
|
2122
2135
|
async executeSingleStep(step, context, parentSignal, options) {
|
|
2123
2136
|
const stepStartTime = Date.now();
|
|
2124
2137
|
if (step.condition && !evaluateActionCondition(step.condition, context)) {
|
|
2125
|
-
const
|
|
2138
|
+
const skippedResult2 = {
|
|
2126
2139
|
stepId: step.id,
|
|
2127
2140
|
stepType: step.type,
|
|
2128
2141
|
status: "skipped",
|
|
2129
2142
|
durationMs: Date.now() - stepStartTime
|
|
2130
2143
|
};
|
|
2131
|
-
options?.onStepComplete?.(step,
|
|
2132
|
-
return
|
|
2144
|
+
options?.onStepComplete?.(step, skippedResult2, context);
|
|
2145
|
+
return skippedResult2;
|
|
2133
2146
|
}
|
|
2134
2147
|
options?.onStepStart?.(step, context);
|
|
2135
2148
|
const stepTimeout = step.timeout;
|
|
@@ -2621,7 +2634,7 @@ function generateTrackingEventId(prefix = "evt") {
|
|
|
2621
2634
|
const randomPart = Math.random().toString(36).substring(2, 10);
|
|
2622
2635
|
return `${prefix}_${timestamp}_${randomPart}`;
|
|
2623
2636
|
}
|
|
2624
|
-
async function sendMetaCapiEvent(event, config, options) {
|
|
2637
|
+
async function sendMetaCapiEvent(event, config, secrets, options) {
|
|
2625
2638
|
const fetcher = options?.fetchFn || globalThis.fetch;
|
|
2626
2639
|
if (!config.pixelId) {
|
|
2627
2640
|
return {
|
|
@@ -2695,9 +2708,10 @@ async function sendMetaCapiEvent(event, config, options) {
|
|
|
2695
2708
|
const headers = {
|
|
2696
2709
|
"Content-Type": "application/json"
|
|
2697
2710
|
};
|
|
2698
|
-
if (
|
|
2699
|
-
|
|
2711
|
+
if (!secrets?.capiAccessToken) {
|
|
2712
|
+
return skippedResult("meta", "No Meta CAPI access token resolved for this credential");
|
|
2700
2713
|
}
|
|
2714
|
+
headers["Authorization"] = `Bearer ${secrets.capiAccessToken}`;
|
|
2701
2715
|
const res = await fetcher(url, {
|
|
2702
2716
|
method: "POST",
|
|
2703
2717
|
headers,
|
|
@@ -2727,7 +2741,7 @@ async function sendMetaCapiEvent(event, config, options) {
|
|
|
2727
2741
|
};
|
|
2728
2742
|
}
|
|
2729
2743
|
}
|
|
2730
|
-
async function sendTikTokEventsApi(event, config, options) {
|
|
2744
|
+
async function sendTikTokEventsApi(event, config, secrets, options) {
|
|
2731
2745
|
const fetcher = options?.fetchFn || globalThis.fetch;
|
|
2732
2746
|
if (!config.pixelId) {
|
|
2733
2747
|
return { provider: "tiktok", success: false, error: "TikTok Pixel ID is missing" };
|
|
@@ -2777,9 +2791,10 @@ async function sendTikTokEventsApi(event, config, options) {
|
|
|
2777
2791
|
const headers = {
|
|
2778
2792
|
"Content-Type": "application/json"
|
|
2779
2793
|
};
|
|
2780
|
-
if (
|
|
2781
|
-
|
|
2794
|
+
if (!secrets?.accessToken) {
|
|
2795
|
+
return skippedResult("tiktok", "No TikTok Events API access token resolved for this credential");
|
|
2782
2796
|
}
|
|
2797
|
+
headers["Access-Token"] = secrets.accessToken;
|
|
2783
2798
|
const res = await fetcher(url, {
|
|
2784
2799
|
method: "POST",
|
|
2785
2800
|
headers,
|
|
@@ -2804,7 +2819,7 @@ async function sendTikTokEventsApi(event, config, options) {
|
|
|
2804
2819
|
};
|
|
2805
2820
|
}
|
|
2806
2821
|
}
|
|
2807
|
-
async function sendGa4MeasurementEvent(event, config, options) {
|
|
2822
|
+
async function sendGa4MeasurementEvent(event, config, secrets, options) {
|
|
2808
2823
|
const fetcher = options?.fetchFn || globalThis.fetch;
|
|
2809
2824
|
if (!config.measurementId) {
|
|
2810
2825
|
return { provider: "google", success: false, error: "GA4 Measurement ID is missing" };
|
|
@@ -2828,12 +2843,10 @@ async function sendGa4MeasurementEvent(event, config, options) {
|
|
|
2828
2843
|
return { provider: "google", success: true, data: { simulated: true, payload: ga4Payload } };
|
|
2829
2844
|
}
|
|
2830
2845
|
try {
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
)}`;
|
|
2834
|
-
if (config.measurementProtocolSecret) {
|
|
2835
|
-
url += `&api_secret=${encodeURIComponent(config.measurementProtocolSecret)}`;
|
|
2846
|
+
if (!secrets?.measurementProtocolSecret) {
|
|
2847
|
+
return skippedResult("google", "No GA4 Measurement Protocol API secret resolved for this credential");
|
|
2836
2848
|
}
|
|
2849
|
+
const url = `https://www.google-analytics.com/mp/collect?measurement_id=${encodeURIComponent(config.measurementId)}&api_secret=${encodeURIComponent(secrets.measurementProtocolSecret)}`;
|
|
2837
2850
|
const res = await fetcher(url, {
|
|
2838
2851
|
method: "POST",
|
|
2839
2852
|
headers: { "Content-Type": "application/json" },
|
|
@@ -2853,11 +2866,8 @@ async function sendGa4MeasurementEvent(event, config, options) {
|
|
|
2853
2866
|
};
|
|
2854
2867
|
}
|
|
2855
2868
|
}
|
|
2856
|
-
async function sendCustomWebhookEvent(event,
|
|
2869
|
+
async function sendCustomWebhookEvent(event, _config, secrets, options) {
|
|
2857
2870
|
const fetcher = options?.fetchFn || globalThis.fetch;
|
|
2858
|
-
if (!config.endpointUrl) {
|
|
2859
|
-
return { provider: "custom", success: false, error: "Custom Webhook URL is missing" };
|
|
2860
|
-
}
|
|
2861
2871
|
const webhookPayload = {
|
|
2862
2872
|
event: event.eventName,
|
|
2863
2873
|
eventId: event.eventId,
|
|
@@ -2871,12 +2881,15 @@ async function sendCustomWebhookEvent(event, config, options) {
|
|
|
2871
2881
|
options.onLog?.("[Custom Webhook SIMULATED]", webhookPayload);
|
|
2872
2882
|
return { provider: "custom", success: true, data: { simulated: true, payload: webhookPayload } };
|
|
2873
2883
|
}
|
|
2884
|
+
if (!secrets?.endpointUrl) {
|
|
2885
|
+
return skippedResult("custom", "No custom webhook destination resolved for this credential");
|
|
2886
|
+
}
|
|
2874
2887
|
try {
|
|
2875
|
-
const res = await fetcher(
|
|
2888
|
+
const res = await fetcher(secrets.endpointUrl, {
|
|
2876
2889
|
method: "POST",
|
|
2877
2890
|
headers: {
|
|
2878
2891
|
"Content-Type": "application/json",
|
|
2879
|
-
...
|
|
2892
|
+
...secrets.headers || {}
|
|
2880
2893
|
},
|
|
2881
2894
|
body: JSON.stringify(webhookPayload)
|
|
2882
2895
|
});
|
|
@@ -2895,6 +2908,9 @@ async function sendCustomWebhookEvent(event, config, options) {
|
|
|
2895
2908
|
};
|
|
2896
2909
|
}
|
|
2897
2910
|
}
|
|
2911
|
+
function skippedResult(provider, reason) {
|
|
2912
|
+
return { provider, success: true, skipped: true, reason };
|
|
2913
|
+
}
|
|
2898
2914
|
async function dispatchServerTracking(event, config, options) {
|
|
2899
2915
|
const eventId = event.eventId || generateTrackingEventId();
|
|
2900
2916
|
const eventWithId = { ...event, eventId };
|
|
@@ -2919,66 +2935,231 @@ async function dispatchServerTracking(event, config, options) {
|
|
|
2919
2935
|
options?.onLog?.(msg, data);
|
|
2920
2936
|
}
|
|
2921
2937
|
};
|
|
2938
|
+
const target = options?.provider || "all";
|
|
2939
|
+
const wants = (key) => target === "all" || target === key;
|
|
2922
2940
|
const providers = config.providers || {};
|
|
2923
|
-
const
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
);
|
|
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
|
+
});
|
|
2931
2949
|
}
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
);
|
|
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
|
+
});
|
|
2939
2957
|
}
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
);
|
|
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
|
+
});
|
|
2947
2965
|
}
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
);
|
|
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
|
+
};
|
|
2955
2982
|
}
|
|
2956
|
-
const
|
|
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)));
|
|
2957
3005
|
const results = {};
|
|
2958
3006
|
let overallSuccess = true;
|
|
2959
|
-
|
|
3007
|
+
let delivered = 0;
|
|
3008
|
+
settled.forEach((item, index) => {
|
|
3009
|
+
const key = tasks[index].key;
|
|
2960
3010
|
if (item.status === "fulfilled") {
|
|
2961
|
-
results[
|
|
2962
|
-
if (!item.value.
|
|
2963
|
-
|
|
2964
|
-
}
|
|
3011
|
+
results[key] = item.value;
|
|
3012
|
+
if (!item.value.success) overallSuccess = false;
|
|
3013
|
+
if (!item.value.skipped) delivered++;
|
|
2965
3014
|
} else {
|
|
2966
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
|
+
};
|
|
2967
3021
|
}
|
|
2968
|
-
}
|
|
3022
|
+
});
|
|
3023
|
+
const allSkipped = delivered === 0 && overallSuccess;
|
|
2969
3024
|
return {
|
|
2970
3025
|
success: overallSuccess,
|
|
2971
3026
|
eventId,
|
|
3027
|
+
...allSkipped ? { skipped: true, reason: "All server providers were skipped" } : {},
|
|
2972
3028
|
results
|
|
2973
3029
|
};
|
|
2974
3030
|
}
|
|
2975
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
|
+
|
|
2976
3157
|
// src/io/exporter.ts
|
|
2977
3158
|
var import_fflate = require("fflate");
|
|
2978
|
-
var
|
|
3159
|
+
var import_schema8 = require("@kubuild/schema");
|
|
2979
3160
|
|
|
2980
3161
|
// src/validation/validator.ts
|
|
2981
|
-
var
|
|
3162
|
+
var import_schema6 = require("@kubuild/schema");
|
|
2982
3163
|
|
|
2983
3164
|
// src/validation/security.ts
|
|
2984
3165
|
var DEFAULT_DOCUMENT_SECURITY_LIMITS = {
|
|
@@ -3267,10 +3448,10 @@ function validateDocument(input, options = {}) {
|
|
|
3267
3448
|
};
|
|
3268
3449
|
}
|
|
3269
3450
|
}
|
|
3270
|
-
if (doc.schema !==
|
|
3451
|
+
if (doc.schema !== import_schema6.SCHEMA_NAME) {
|
|
3271
3452
|
errors.push({
|
|
3272
3453
|
code: "GLOBAL_SCHEMA_INVALID",
|
|
3273
|
-
message: `Invalid schema identifier. Expected "${
|
|
3454
|
+
message: `Invalid schema identifier. Expected "${import_schema6.SCHEMA_NAME}", received "${String(doc.schema)}"`,
|
|
3274
3455
|
path: "/schema"
|
|
3275
3456
|
});
|
|
3276
3457
|
}
|
|
@@ -3334,7 +3515,7 @@ function validateDocument(input, options = {}) {
|
|
|
3334
3515
|
warnings
|
|
3335
3516
|
);
|
|
3336
3517
|
if (errors.length === 0) {
|
|
3337
|
-
const zodResult =
|
|
3518
|
+
const zodResult = import_schema6.PageDocumentSchema.safeParse(input);
|
|
3338
3519
|
if (!zodResult.success) {
|
|
3339
3520
|
for (const issue of zodResult.error.issues) {
|
|
3340
3521
|
const jsonPath = "/" + issue.path.join("/");
|
|
@@ -3514,7 +3695,8 @@ function validateNodeRecursive(nodeObj, currentPath, parentNode, seenIds, visite
|
|
|
3514
3695
|
`${currentPath}/props`,
|
|
3515
3696
|
effectiveNodeId,
|
|
3516
3697
|
options,
|
|
3517
|
-
errors
|
|
3698
|
+
errors,
|
|
3699
|
+
warnings
|
|
3518
3700
|
);
|
|
3519
3701
|
}
|
|
3520
3702
|
}
|
|
@@ -3529,7 +3711,10 @@ function validateNodeRecursive(nodeObj, currentPath, parentNode, seenIds, visite
|
|
|
3529
3711
|
}
|
|
3530
3712
|
}
|
|
3531
3713
|
}
|
|
3532
|
-
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;
|
|
3533
3718
|
for (const [key, value] of Object.entries(propsObj)) {
|
|
3534
3719
|
const currentPath = `${propsPath}/${key}`;
|
|
3535
3720
|
if (!value || typeof value !== "object") {
|
|
@@ -3543,7 +3728,8 @@ function validatePropsBindings(propsObj, propsPath, nodeId, options, errors) {
|
|
|
3543
3728
|
`${currentPath}/${index}`,
|
|
3544
3729
|
nodeId,
|
|
3545
3730
|
options,
|
|
3546
|
-
errors
|
|
3731
|
+
errors,
|
|
3732
|
+
warnings
|
|
3547
3733
|
);
|
|
3548
3734
|
}
|
|
3549
3735
|
});
|
|
@@ -3551,7 +3737,9 @@ function validatePropsBindings(propsObj, propsPath, nodeId, options, errors) {
|
|
|
3551
3737
|
}
|
|
3552
3738
|
const record = value;
|
|
3553
3739
|
if (record.type === "asset") {
|
|
3554
|
-
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) {
|
|
3555
3743
|
errors.push({
|
|
3556
3744
|
code: "INVALID_ASSET_REFERENCE",
|
|
3557
3745
|
message: 'Asset reference must have a non-empty "assetId"',
|
|
@@ -3567,7 +3755,27 @@ function validatePropsBindings(propsObj, propsPath, nodeId, options, errors) {
|
|
|
3567
3755
|
nodeId
|
|
3568
3756
|
});
|
|
3569
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
|
+
}
|
|
3570
3777
|
} else if (record.type === "variable") {
|
|
3778
|
+
if (!checkVariables) continue;
|
|
3571
3779
|
if (typeof record.key !== "string" || record.key.trim().length === 0) {
|
|
3572
3780
|
errors.push({
|
|
3573
3781
|
code: "INVALID_VARIABLE_BINDING",
|
|
@@ -3577,6 +3785,7 @@ function validatePropsBindings(propsObj, propsPath, nodeId, options, errors) {
|
|
|
3577
3785
|
});
|
|
3578
3786
|
}
|
|
3579
3787
|
} else if (key === "action" || typeof record.type === "string" && record.payload !== void 0) {
|
|
3788
|
+
if (!checkActions) continue;
|
|
3580
3789
|
if (typeof record.type !== "string" || record.type.trim().length === 0) {
|
|
3581
3790
|
errors.push({
|
|
3582
3791
|
code: "INVALID_ACTION_BINDING",
|
|
@@ -3586,10 +3795,62 @@ function validatePropsBindings(propsObj, propsPath, nodeId, options, errors) {
|
|
|
3586
3795
|
});
|
|
3587
3796
|
}
|
|
3588
3797
|
} else {
|
|
3589
|
-
validatePropsBindings(record, currentPath, nodeId, options, errors);
|
|
3798
|
+
validatePropsBindings(record, currentPath, nodeId, options, errors, warnings);
|
|
3590
3799
|
}
|
|
3591
3800
|
}
|
|
3592
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
|
+
}
|
|
3593
3854
|
|
|
3594
3855
|
// src/io/exporter.ts
|
|
3595
3856
|
function sha256Sync(data) {
|
|
@@ -3810,6 +4071,7 @@ async function exportPackage(document, options = {}) {
|
|
|
3810
4071
|
};
|
|
3811
4072
|
}
|
|
3812
4073
|
const pageDoc = JSON.parse(JSON.stringify(validation.data));
|
|
4074
|
+
stripDocumentTrackingSecretsInPlace(pageDoc);
|
|
3813
4075
|
if (options.metadata) {
|
|
3814
4076
|
pageDoc.metadata = {
|
|
3815
4077
|
...pageDoc.metadata || {
|
|
@@ -3933,8 +4195,8 @@ async function exportPackage(document, options = {}) {
|
|
|
3933
4195
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3934
4196
|
};
|
|
3935
4197
|
const manifest = {
|
|
3936
|
-
schema:
|
|
3937
|
-
schemaVersion: pageDoc.version ||
|
|
4198
|
+
schema: import_schema8.SCHEMA_NAME,
|
|
4199
|
+
schemaVersion: pageDoc.version || import_schema8.CURRENT_SCHEMA_VERSION,
|
|
3938
4200
|
packageVersion: options.packageVersion || "1.0.0",
|
|
3939
4201
|
builderCompatibility: options.builderCompatibility || ">=0.1.0",
|
|
3940
4202
|
requiredComponents: requirements.requiredComponents,
|
|
@@ -3959,10 +4221,10 @@ var exportStoraPackage = exportPackage;
|
|
|
3959
4221
|
|
|
3960
4222
|
// src/io/importer.ts
|
|
3961
4223
|
var import_fflate2 = require("fflate");
|
|
3962
|
-
var
|
|
4224
|
+
var import_schema10 = require("@kubuild/schema");
|
|
3963
4225
|
|
|
3964
4226
|
// src/io/migration.ts
|
|
3965
|
-
var
|
|
4227
|
+
var import_schema9 = require("@kubuild/schema");
|
|
3966
4228
|
var MigrationRegistry = class {
|
|
3967
4229
|
steps = /* @__PURE__ */ new Map();
|
|
3968
4230
|
/**
|
|
@@ -4041,7 +4303,7 @@ defaultMigrationRegistry.register({
|
|
|
4041
4303
|
description: "Migrate alpha 0.1.0 schema (root node key, flat styles) to v1.0.0",
|
|
4042
4304
|
migrate: (rawDoc) => {
|
|
4043
4305
|
const migrated = deepClone(rawDoc);
|
|
4044
|
-
migrated.schema =
|
|
4306
|
+
migrated.schema = import_schema9.SCHEMA_NAME;
|
|
4045
4307
|
migrated.version = "1.0.0";
|
|
4046
4308
|
if (migrated.root && !migrated.document) {
|
|
4047
4309
|
migrated.document = migrated.root;
|
|
@@ -4076,7 +4338,7 @@ defaultMigrationRegistry.register({
|
|
|
4076
4338
|
description: "Migrate 0.2.0 schema to v1.0.0",
|
|
4077
4339
|
migrate: (rawDoc) => {
|
|
4078
4340
|
const migrated = deepClone(rawDoc);
|
|
4079
|
-
migrated.schema =
|
|
4341
|
+
migrated.schema = import_schema9.SCHEMA_NAME;
|
|
4080
4342
|
migrated.version = "1.0.0";
|
|
4081
4343
|
if (migrated.document && typeof migrated.document === "object") {
|
|
4082
4344
|
convertNodeStylesToResponsive(migrated.document);
|
|
@@ -4090,7 +4352,7 @@ defaultMigrationRegistry.register({
|
|
|
4090
4352
|
description: "Migrate 0.9.0 beta schema to v1.0.0",
|
|
4091
4353
|
migrate: (rawDoc) => {
|
|
4092
4354
|
const migrated = deepClone(rawDoc);
|
|
4093
|
-
migrated.schema =
|
|
4355
|
+
migrated.schema = import_schema9.SCHEMA_NAME;
|
|
4094
4356
|
migrated.version = "1.0.0";
|
|
4095
4357
|
if (!migrated.metadata || typeof migrated.metadata !== "object") {
|
|
4096
4358
|
migrated.metadata = {
|
|
@@ -4115,17 +4377,36 @@ defaultMigrationRegistry.register({
|
|
|
4115
4377
|
return migrated;
|
|
4116
4378
|
}
|
|
4117
4379
|
});
|
|
4118
|
-
|
|
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) {
|
|
4119
4400
|
if (!sourceVersion || !targetVersion) return false;
|
|
4120
4401
|
if (sourceVersion === targetVersion) return true;
|
|
4121
4402
|
return registry.hasPath(sourceVersion, targetVersion);
|
|
4122
4403
|
}
|
|
4123
|
-
function getMigrationPath(sourceVersion, targetVersion =
|
|
4404
|
+
function getMigrationPath(sourceVersion, targetVersion = import_schema9.CURRENT_SCHEMA_VERSION, registry = defaultMigrationRegistry) {
|
|
4124
4405
|
if (!sourceVersion || !targetVersion) return null;
|
|
4125
4406
|
return registry.findPath(sourceVersion, targetVersion);
|
|
4126
4407
|
}
|
|
4127
4408
|
function migrateDocument(rawDocument, options = {}) {
|
|
4128
|
-
const targetVersion = options.targetVersion ??
|
|
4409
|
+
const targetVersion = options.targetVersion ?? import_schema9.CURRENT_SCHEMA_VERSION;
|
|
4129
4410
|
const dryRun = options.dryRun ?? false;
|
|
4130
4411
|
const registry = options.registry ?? defaultMigrationRegistry;
|
|
4131
4412
|
const validate = options.validate ?? true;
|
|
@@ -4153,6 +4434,14 @@ function migrateDocument(rawDocument, options = {}) {
|
|
|
4153
4434
|
const sourceVersion = typeof doc.version === "string" ? doc.version.trim() : "unknown";
|
|
4154
4435
|
if (sourceVersion === targetVersion) {
|
|
4155
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
|
+
] : [];
|
|
4156
4445
|
if (validate) {
|
|
4157
4446
|
const validation = validateDocument(cloned);
|
|
4158
4447
|
if (!validation.valid) {
|
|
@@ -4184,7 +4473,8 @@ function migrateDocument(rawDocument, options = {}) {
|
|
|
4184
4473
|
targetVersion,
|
|
4185
4474
|
migrationPath: [sourceVersion],
|
|
4186
4475
|
stepsApplied: 0,
|
|
4187
|
-
dryRun
|
|
4476
|
+
dryRun,
|
|
4477
|
+
...currentWarnings.length > 0 ? { warnings: currentWarnings } : {}
|
|
4188
4478
|
}
|
|
4189
4479
|
};
|
|
4190
4480
|
}
|
|
@@ -4224,6 +4514,8 @@ function migrateDocument(rawDocument, options = {}) {
|
|
|
4224
4514
|
}
|
|
4225
4515
|
let currentDoc = deepClone(doc);
|
|
4226
4516
|
let stepsApplied = 0;
|
|
4517
|
+
const warnings = [];
|
|
4518
|
+
const stepContext = { warn: (w) => warnings.push(w) };
|
|
4227
4519
|
for (let i = 0; i < path.length - 1; i++) {
|
|
4228
4520
|
const fromVer = path[i];
|
|
4229
4521
|
const toVer = path[i + 1];
|
|
@@ -4249,7 +4541,7 @@ function migrateDocument(rawDocument, options = {}) {
|
|
|
4249
4541
|
};
|
|
4250
4542
|
}
|
|
4251
4543
|
try {
|
|
4252
|
-
currentDoc = step.migrate(currentDoc);
|
|
4544
|
+
currentDoc = step.migrate(currentDoc, stepContext);
|
|
4253
4545
|
stepsApplied++;
|
|
4254
4546
|
} catch (err) {
|
|
4255
4547
|
const error = {
|
|
@@ -4305,7 +4597,8 @@ function migrateDocument(rawDocument, options = {}) {
|
|
|
4305
4597
|
targetVersion,
|
|
4306
4598
|
migrationPath: path,
|
|
4307
4599
|
stepsApplied,
|
|
4308
|
-
dryRun: false
|
|
4600
|
+
dryRun: false,
|
|
4601
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
4309
4602
|
}
|
|
4310
4603
|
};
|
|
4311
4604
|
}
|
|
@@ -4340,7 +4633,7 @@ async function preflightPackage(archiveData, options = {}) {
|
|
|
4340
4633
|
...DEFAULT_SECURITY_LIMITS,
|
|
4341
4634
|
...options.securityLimits
|
|
4342
4635
|
};
|
|
4343
|
-
const targetVersion = options.targetSchemaVersion ||
|
|
4636
|
+
const targetVersion = options.targetSchemaVersion || import_schema10.CURRENT_SCHEMA_VERSION;
|
|
4344
4637
|
const migrationRegistry = options.migrationRegistry || defaultMigrationRegistry;
|
|
4345
4638
|
const diagnostics = [];
|
|
4346
4639
|
let dependencyPolicy = options.dependencyPolicy || "cancel";
|
|
@@ -4473,7 +4766,7 @@ async function preflightPackage(archiveData, options = {}) {
|
|
|
4473
4766
|
path: manifestProtoCheck.path
|
|
4474
4767
|
});
|
|
4475
4768
|
}
|
|
4476
|
-
const parseResult =
|
|
4769
|
+
const parseResult = import_schema10.ManifestSchema.safeParse(manifestJson);
|
|
4477
4770
|
if (!parseResult.success) {
|
|
4478
4771
|
const issues = parseResult.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ");
|
|
4479
4772
|
diagnostics.push({
|
|
@@ -4503,6 +4796,16 @@ async function preflightPackage(archiveData, options = {}) {
|
|
|
4503
4796
|
let rawPageDoc;
|
|
4504
4797
|
try {
|
|
4505
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
|
+
}
|
|
4506
4809
|
const pageProtoCheck = containsProhibitedKeys(rawPageDoc);
|
|
4507
4810
|
if (pageProtoCheck.found) {
|
|
4508
4811
|
diagnostics.push({
|
|
@@ -4749,7 +5052,7 @@ function buildReport(valid, canImport, fields) {
|
|
|
4749
5052
|
return {
|
|
4750
5053
|
valid,
|
|
4751
5054
|
canImport,
|
|
4752
|
-
targetVersion: fields.targetVersion ||
|
|
5055
|
+
targetVersion: fields.targetVersion || import_schema10.CURRENT_SCHEMA_VERSION,
|
|
4753
5056
|
requiresMigration: fields.requiresMigration || false,
|
|
4754
5057
|
missingComponents: fields.missingComponents || [],
|
|
4755
5058
|
missingCapabilities: fields.missingCapabilities || [],
|
|
@@ -4822,7 +5125,7 @@ async function importPackage(archiveData, options = {}) {
|
|
|
4822
5125
|
if (preflight.requiresMigration) {
|
|
4823
5126
|
const migrationRegistry = options.migrationRegistry || defaultMigrationRegistry;
|
|
4824
5127
|
const migrationRes = migrateDocument(pageRaw, {
|
|
4825
|
-
targetVersion: options.targetSchemaVersion ||
|
|
5128
|
+
targetVersion: options.targetSchemaVersion || import_schema10.CURRENT_SCHEMA_VERSION,
|
|
4826
5129
|
registry: migrationRegistry,
|
|
4827
5130
|
validate: true
|
|
4828
5131
|
});
|
|
@@ -4844,13 +5147,24 @@ async function importPackage(archiveData, options = {}) {
|
|
|
4844
5147
|
} else {
|
|
4845
5148
|
finalDocument = pageRaw;
|
|
4846
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
|
+
}
|
|
4847
5161
|
let metadata = finalDocument.metadata || {
|
|
4848
5162
|
title: "Imported Page",
|
|
4849
5163
|
description: "",
|
|
4850
5164
|
author: "",
|
|
4851
5165
|
tags: [],
|
|
4852
5166
|
category: "general",
|
|
4853
|
-
version: finalDocument.version ||
|
|
5167
|
+
version: finalDocument.version || import_schema10.CURRENT_SCHEMA_VERSION
|
|
4854
5168
|
};
|
|
4855
5169
|
if (unzipped["metadata.json"]) {
|
|
4856
5170
|
try {
|
|
@@ -5002,13 +5316,14 @@ async function importPackage(archiveData, options = {}) {
|
|
|
5002
5316
|
metadata,
|
|
5003
5317
|
extractedAssets,
|
|
5004
5318
|
renamedAssets: Object.keys(renameMap).length > 0 ? renameMap : void 0,
|
|
5005
|
-
preflight
|
|
5319
|
+
preflight,
|
|
5320
|
+
...importWarnings.length > 0 ? { warnings: importWarnings } : {}
|
|
5006
5321
|
};
|
|
5007
5322
|
}
|
|
5008
5323
|
var importStoraPackage = importPackage;
|
|
5009
5324
|
|
|
5010
5325
|
// src/io/load-project.ts
|
|
5011
|
-
var
|
|
5326
|
+
var import_schema11 = require("@kubuild/schema");
|
|
5012
5327
|
var DEFAULT_PAGE_ARTBOARD_ID = "artboard-page-1";
|
|
5013
5328
|
function wrapPageDocumentAsProject(document, options = {}) {
|
|
5014
5329
|
const artboardId = options.artboardId ?? DEFAULT_PAGE_ARTBOARD_ID;
|
|
@@ -5021,8 +5336,8 @@ function wrapPageDocumentAsProject(document, options = {}) {
|
|
|
5021
5336
|
...options.width !== void 0 ? { width: options.width } : {}
|
|
5022
5337
|
};
|
|
5023
5338
|
return {
|
|
5024
|
-
schema:
|
|
5025
|
-
version:
|
|
5339
|
+
schema: import_schema11.PROJECT_SCHEMA_NAME,
|
|
5340
|
+
version: import_schema11.CURRENT_PROJECT_SCHEMA_VERSION,
|
|
5026
5341
|
metadata: document.metadata,
|
|
5027
5342
|
artboards: [artboard],
|
|
5028
5343
|
activeArtboardId: artboardId
|
|
@@ -5041,8 +5356,8 @@ function loadProjectDocument(raw) {
|
|
|
5041
5356
|
]
|
|
5042
5357
|
};
|
|
5043
5358
|
}
|
|
5044
|
-
if ((0,
|
|
5045
|
-
const parsed =
|
|
5359
|
+
if ((0, import_schema11.looksLikeProjectDocument)(raw)) {
|
|
5360
|
+
const parsed = import_schema11.ProjectDocumentSchema.safeParse(raw);
|
|
5046
5361
|
if (!parsed.success) {
|
|
5047
5362
|
return {
|
|
5048
5363
|
success: false,
|
|
@@ -5527,7 +5842,7 @@ function compareManifestsSemantically(expected, actual, options = {}) {
|
|
|
5527
5842
|
}
|
|
5528
5843
|
|
|
5529
5844
|
// src/io/template-utils.ts
|
|
5530
|
-
var
|
|
5845
|
+
var import_schema12 = require("@kubuild/schema");
|
|
5531
5846
|
var CORE_BUILTIN_COMPONENTS = /* @__PURE__ */ new Set([
|
|
5532
5847
|
"page",
|
|
5533
5848
|
"section",
|
|
@@ -5541,7 +5856,7 @@ var CORE_BUILTIN_COMPONENTS = /* @__PURE__ */ new Set([
|
|
|
5541
5856
|
"collection"
|
|
5542
5857
|
]);
|
|
5543
5858
|
function validateTemplate(value) {
|
|
5544
|
-
const parseResult =
|
|
5859
|
+
const parseResult = import_schema12.TemplateRecordSchema.safeParse(value);
|
|
5545
5860
|
if (parseResult.success) {
|
|
5546
5861
|
return {
|
|
5547
5862
|
valid: true,
|
|
@@ -5669,7 +5984,7 @@ function cloneTreeWithFreshIds(root, idGen) {
|
|
|
5669
5984
|
function cloneTemplateAsPage(templateOrDoc, options = {}) {
|
|
5670
5985
|
let sourceDoc;
|
|
5671
5986
|
let templateOrigin = null;
|
|
5672
|
-
if ((0,
|
|
5987
|
+
if ((0, import_schema12.isTemplateRecord)(templateOrDoc)) {
|
|
5673
5988
|
if (!templateOrDoc.document) {
|
|
5674
5989
|
throw new Error(
|
|
5675
5990
|
`Template "${templateOrDoc.name}" (${templateOrDoc.id}) does not contain an inline document snapshot to clone from.`
|
|
@@ -5686,7 +6001,7 @@ function cloneTemplateAsPage(templateOrDoc, options = {}) {
|
|
|
5686
6001
|
} else {
|
|
5687
6002
|
throw new Error("Invalid input: expected a TemplateRecord or PageDocument.");
|
|
5688
6003
|
}
|
|
5689
|
-
const existingIdsInSource = new Set((0,
|
|
6004
|
+
const existingIdsInSource = new Set((0, import_schema12.collectNodeIds)(sourceDoc.document));
|
|
5690
6005
|
const assignedNewIds = /* @__PURE__ */ new Set();
|
|
5691
6006
|
const prefix = options.idPrefix || `page_${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 6)}`;
|
|
5692
6007
|
let counter = 1;
|
|
@@ -5735,8 +6050,8 @@ function cloneTemplateAsPage(templateOrDoc, options = {}) {
|
|
|
5735
6050
|
}
|
|
5736
6051
|
function areNodeIdsCompletelyDistinct(docA, docB) {
|
|
5737
6052
|
if (!docA?.document || !docB?.document) return true;
|
|
5738
|
-
const idsA = new Set((0,
|
|
5739
|
-
const idsB = (0,
|
|
6053
|
+
const idsA = new Set((0, import_schema12.collectNodeIds)(docA.document));
|
|
6054
|
+
const idsB = (0, import_schema12.collectNodeIds)(docB.document);
|
|
5740
6055
|
for (const id of idsB) {
|
|
5741
6056
|
if (idsA.has(id)) {
|
|
5742
6057
|
return false;
|
|
@@ -5746,7 +6061,7 @@ function areNodeIdsCompletelyDistinct(docA, docB) {
|
|
|
5746
6061
|
}
|
|
5747
6062
|
|
|
5748
6063
|
// src/validation/project-validator.ts
|
|
5749
|
-
var
|
|
6064
|
+
var import_schema13 = require("@kubuild/schema");
|
|
5750
6065
|
function walk(node, visit) {
|
|
5751
6066
|
visit(node);
|
|
5752
6067
|
for (const child of node.children ?? []) {
|
|
@@ -5756,7 +6071,7 @@ function walk(node, visit) {
|
|
|
5756
6071
|
function collectArtboardReferences(artboard) {
|
|
5757
6072
|
const refs = [];
|
|
5758
6073
|
walk(artboard.document.document, (node) => {
|
|
5759
|
-
if (node.type ===
|
|
6074
|
+
if (node.type === import_schema13.ARTBOARD_REFERENCE_NODE_TYPE) {
|
|
5760
6075
|
const target = node.props?.artboardId;
|
|
5761
6076
|
refs.push({
|
|
5762
6077
|
nodeId: node.id,
|
|
@@ -5783,7 +6098,7 @@ function validateProject(input, options = {}) {
|
|
|
5783
6098
|
artboardErrors
|
|
5784
6099
|
};
|
|
5785
6100
|
}
|
|
5786
|
-
const parsed =
|
|
6101
|
+
const parsed = import_schema13.ProjectDocumentSchema.safeParse(input);
|
|
5787
6102
|
if (!parsed.success) {
|
|
5788
6103
|
return {
|
|
5789
6104
|
valid: false,
|
|
@@ -5791,7 +6106,7 @@ function validateProject(input, options = {}) {
|
|
|
5791
6106
|
errors: [
|
|
5792
6107
|
{
|
|
5793
6108
|
code: "PROJECT_SCHEMA_INVALID",
|
|
5794
|
-
message: `Project failed schema validation. Expected schema "${
|
|
6109
|
+
message: `Project failed schema validation. Expected schema "${import_schema13.PROJECT_SCHEMA_NAME}".`,
|
|
5795
6110
|
path: "",
|
|
5796
6111
|
details: { issues: parsed.error.issues }
|
|
5797
6112
|
}
|
|
@@ -5884,7 +6199,7 @@ function validateProject(input, options = {}) {
|
|
|
5884
6199
|
function collectArtboardReferenceNodes(root) {
|
|
5885
6200
|
const refs = [];
|
|
5886
6201
|
walk(root, (node) => {
|
|
5887
|
-
if (node.type ===
|
|
6202
|
+
if (node.type === import_schema13.ARTBOARD_REFERENCE_NODE_TYPE) {
|
|
5888
6203
|
const target = node.props?.artboardId;
|
|
5889
6204
|
refs.push({ nodeId: node.id, artboardId: typeof target === "string" ? target : void 0 });
|
|
5890
6205
|
}
|
|
@@ -6133,6 +6448,7 @@ function isFormValid(errors) {
|
|
|
6133
6448
|
createPageArtboard,
|
|
6134
6449
|
createRuntimeStore,
|
|
6135
6450
|
createTemplateRecord,
|
|
6451
|
+
createTrackingRelayHandler,
|
|
6136
6452
|
deepClone,
|
|
6137
6453
|
defaultIdGenerator,
|
|
6138
6454
|
defaultMigrationRegistry,
|
|
@@ -6199,6 +6515,7 @@ function isFormValid(errors) {
|
|
|
6199
6515
|
resolveBinding,
|
|
6200
6516
|
resolveBindingValue,
|
|
6201
6517
|
resolvePropertyPath,
|
|
6518
|
+
sanitizeDocumentTracking,
|
|
6202
6519
|
sanitizeFilename,
|
|
6203
6520
|
sanitizeHtml,
|
|
6204
6521
|
sanitizeUrl,
|
|
@@ -6209,6 +6526,8 @@ function isFormValid(errors) {
|
|
|
6209
6526
|
sendTikTokEventsApi,
|
|
6210
6527
|
setActiveArtboard,
|
|
6211
6528
|
sha256Sync,
|
|
6529
|
+
stripDocumentTrackingSecretsInPlace,
|
|
6530
|
+
stripTrackingSecretsInPlace,
|
|
6212
6531
|
ungroupNodeFrame,
|
|
6213
6532
|
updateActions,
|
|
6214
6533
|
updateAnimation,
|