@kubuild/core 0.6.0 → 0.8.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/command-tree-utils.d.ts +22 -1
- package/dist/document/command-tree-utils.d.ts.map +1 -1
- package/dist/document/commands.d.ts +19 -2
- package/dist/document/commands.d.ts.map +1 -1
- package/dist/document/document-utils.d.ts.map +1 -1
- package/dist/index.cjs +625 -145
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +605 -126
- 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/load-project.d.ts.map +1 -1
- package/dist/io/migration.d.ts +28 -2
- package/dist/io/migration.d.ts.map +1 -1
- package/dist/io/template-utils.d.ts +8 -3
- package/dist/io/template-utils.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 +79 -2
- 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
|
@@ -30,6 +30,7 @@ __export(index_exports, {
|
|
|
30
30
|
DocumentHistoryManager: () => DocumentHistoryManager,
|
|
31
31
|
HistoryEngine: () => HistoryEngine,
|
|
32
32
|
MigrationRegistry: () => MigrationRegistry,
|
|
33
|
+
NODE_ID_REFERENCE_KEYS: () => NODE_ID_REFERENCE_KEYS,
|
|
33
34
|
RuntimeStateStore: () => RuntimeStateStore,
|
|
34
35
|
addArtboard: () => addArtboard,
|
|
35
36
|
applyFieldTransform: () => applyFieldTransform,
|
|
@@ -37,7 +38,9 @@ __export(index_exports, {
|
|
|
37
38
|
buildSampleVariablesFromCatalog: () => buildSampleVariablesFromCatalog,
|
|
38
39
|
calculateChecksum: () => calculateChecksum,
|
|
39
40
|
canMigrate: () => canMigrate,
|
|
41
|
+
canonicalizeTextPropsInPlace: () => canonicalizeTextPropsInPlace,
|
|
40
42
|
checkZipBomb: () => checkZipBomb,
|
|
43
|
+
cloneNodeTreeWithFreshIds: () => cloneNodeTreeWithFreshIds,
|
|
41
44
|
cloneTemplateAsPage: () => cloneTemplateAsPage,
|
|
42
45
|
cloneTreeWithNewIds: () => cloneTreeWithNewIds,
|
|
43
46
|
collectArtboardReferenceNodes: () => collectArtboardReferenceNodes,
|
|
@@ -54,6 +57,7 @@ __export(index_exports, {
|
|
|
54
57
|
createPageArtboard: () => createPageArtboard,
|
|
55
58
|
createRuntimeStore: () => createRuntimeStore,
|
|
56
59
|
createTemplateRecord: () => createTemplateRecord,
|
|
60
|
+
createTrackingRelayHandler: () => createTrackingRelayHandler,
|
|
57
61
|
deepClone: () => deepClone,
|
|
58
62
|
defaultIdGenerator: () => defaultIdGenerator,
|
|
59
63
|
defaultMigrationRegistry: () => defaultMigrationRegistry,
|
|
@@ -113,6 +117,7 @@ __export(index_exports, {
|
|
|
113
117
|
previewImportPackage: () => previewImportPackage,
|
|
114
118
|
remapAssetReferences: () => remapAssetReferences,
|
|
115
119
|
remapDocumentAssetReferences: () => remapDocumentAssetReferences,
|
|
120
|
+
remapNodeReferences: () => remapNodeReferences,
|
|
116
121
|
removeArtboard: () => removeArtboard,
|
|
117
122
|
removeNode: () => removeNode,
|
|
118
123
|
renameArtboard: () => renameArtboard,
|
|
@@ -120,6 +125,7 @@ __export(index_exports, {
|
|
|
120
125
|
resolveBinding: () => resolveBinding,
|
|
121
126
|
resolveBindingValue: () => resolveBindingValue,
|
|
122
127
|
resolvePropertyPath: () => resolvePropertyPath,
|
|
128
|
+
sanitizeDocumentTracking: () => sanitizeDocumentTracking,
|
|
123
129
|
sanitizeFilename: () => sanitizeFilename,
|
|
124
130
|
sanitizeHtml: () => sanitizeHtml,
|
|
125
131
|
sanitizeUrl: () => sanitizeUrl,
|
|
@@ -130,6 +136,8 @@ __export(index_exports, {
|
|
|
130
136
|
sendTikTokEventsApi: () => sendTikTokEventsApi,
|
|
131
137
|
setActiveArtboard: () => setActiveArtboard,
|
|
132
138
|
sha256Sync: () => sha256Sync,
|
|
139
|
+
stripDocumentTrackingSecretsInPlace: () => stripDocumentTrackingSecretsInPlace,
|
|
140
|
+
stripTrackingSecretsInPlace: () => stripTrackingSecretsInPlace,
|
|
133
141
|
ungroupNodeFrame: () => ungroupNodeFrame,
|
|
134
142
|
updateActions: () => updateActions,
|
|
135
143
|
updateAnimation: () => updateAnimation,
|
|
@@ -137,6 +145,7 @@ __export(index_exports, {
|
|
|
137
145
|
updateFormConfig: () => updateFormConfig,
|
|
138
146
|
updateProps: () => updateProps,
|
|
139
147
|
updateStyle: () => updateStyle,
|
|
148
|
+
updateTheme: () => updateTheme,
|
|
140
149
|
validateDocument: () => validateDocument,
|
|
141
150
|
validateDocumentSecurity: () => validateDocumentSecurity,
|
|
142
151
|
validateFieldValue: () => validateFieldValue,
|
|
@@ -155,7 +164,7 @@ var import_schema = require("@kubuild/schema");
|
|
|
155
164
|
function createBlankDocument(title = "Untitled Page") {
|
|
156
165
|
return {
|
|
157
166
|
schema: import_schema.SCHEMA_NAME,
|
|
158
|
-
version:
|
|
167
|
+
version: import_schema.CURRENT_SCHEMA_VERSION,
|
|
159
168
|
metadata: {
|
|
160
169
|
title,
|
|
161
170
|
description: "",
|
|
@@ -468,26 +477,84 @@ function defaultIdGenerator(oldId, existingIds) {
|
|
|
468
477
|
}
|
|
469
478
|
return candidate;
|
|
470
479
|
}
|
|
471
|
-
|
|
480
|
+
var NODE_ID_REFERENCE_KEYS = /* @__PURE__ */ new Set([
|
|
481
|
+
"modalId",
|
|
482
|
+
"modalNodeId",
|
|
483
|
+
"targetModalId",
|
|
484
|
+
"targetNodeId",
|
|
485
|
+
"nodeId",
|
|
486
|
+
"formId",
|
|
487
|
+
"targetId"
|
|
488
|
+
]);
|
|
489
|
+
var ANCHOR_REFERENCE_KEYS = /* @__PURE__ */ new Set(["url", "href"]);
|
|
490
|
+
function remapReferenceValue(key, value, idMap) {
|
|
491
|
+
if (typeof value !== "string") return value;
|
|
492
|
+
if (NODE_ID_REFERENCE_KEYS.has(key)) {
|
|
493
|
+
return idMap.get(value) ?? idMap.get(value.trim()) ?? value;
|
|
494
|
+
}
|
|
495
|
+
if (ANCHOR_REFERENCE_KEYS.has(key) && value.startsWith("#")) {
|
|
496
|
+
const mapped = idMap.get(value.slice(1));
|
|
497
|
+
return mapped ? `#${mapped}` : value;
|
|
498
|
+
}
|
|
499
|
+
return value;
|
|
500
|
+
}
|
|
501
|
+
function remapReferenceRecord(record, idMap) {
|
|
502
|
+
for (const [key, value] of Object.entries(record)) {
|
|
503
|
+
record[key] = remapReferenceValue(key, value, idMap);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
function remapStepReferences(steps, idMap) {
|
|
507
|
+
if (!Array.isArray(steps)) return;
|
|
508
|
+
for (const step of steps) {
|
|
509
|
+
if (step.payload && typeof step.payload === "object") {
|
|
510
|
+
remapReferenceRecord(step.payload, idMap);
|
|
511
|
+
}
|
|
512
|
+
remapStepReferences(step.onSuccess, idMap);
|
|
513
|
+
remapStepReferences(step.onError, idMap);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
function remapNodeReferences(node, idMap) {
|
|
517
|
+
if (node.props && typeof node.props === "object") {
|
|
518
|
+
remapReferenceRecord(node.props, idMap);
|
|
519
|
+
const legacyAction = node.props.action;
|
|
520
|
+
if (legacyAction && typeof legacyAction === "object" && legacyAction.payload && typeof legacyAction.payload === "object" && !Array.isArray(legacyAction.payload)) {
|
|
521
|
+
remapReferenceRecord(legacyAction.payload, idMap);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (node.formConfig && typeof node.formConfig.formId === "string") {
|
|
525
|
+
node.formConfig.formId = idMap.get(node.formConfig.formId) ?? node.formConfig.formId;
|
|
526
|
+
}
|
|
527
|
+
if (Array.isArray(node.actions)) {
|
|
528
|
+
for (const pipeline of node.actions) {
|
|
529
|
+
remapStepReferences(pipeline.steps, idMap);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
function cloneNodeTreeWithFreshIds(root, idGen) {
|
|
472
534
|
const idMap = /* @__PURE__ */ new Map();
|
|
473
|
-
const
|
|
535
|
+
const clonedNodes = [];
|
|
474
536
|
function cloneRecursive(node) {
|
|
475
|
-
const newId = idGen(node.id);
|
|
537
|
+
const newId = idGen(node.id, node);
|
|
476
538
|
idMap.set(node.id, newId);
|
|
477
|
-
const
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
return {
|
|
539
|
+
const { children, ...rest } = node;
|
|
540
|
+
const cloned = {
|
|
541
|
+
...deepClone(rest),
|
|
481
542
|
id: newId,
|
|
482
|
-
|
|
483
|
-
...clonedProps ? { props: clonedProps } : {},
|
|
484
|
-
...clonedStyles ? { styles: clonedStyles } : {},
|
|
485
|
-
children: clonedChildren
|
|
543
|
+
children: children ? children.map((child) => cloneRecursive(child)) : []
|
|
486
544
|
};
|
|
545
|
+
clonedNodes.push(cloned);
|
|
546
|
+
return cloned;
|
|
487
547
|
}
|
|
488
548
|
const clonedNode = cloneRecursive(root);
|
|
549
|
+
for (const cloned of clonedNodes) {
|
|
550
|
+
remapNodeReferences(cloned, idMap);
|
|
551
|
+
}
|
|
489
552
|
return { clonedNode, idMap };
|
|
490
553
|
}
|
|
554
|
+
function cloneTreeWithNewIds(root, idGenerator, existingIds) {
|
|
555
|
+
const idGen = idGenerator || ((oldId) => defaultIdGenerator(oldId, existingIds));
|
|
556
|
+
return cloneNodeTreeWithFreshIds(root, (oldId) => idGen(oldId));
|
|
557
|
+
}
|
|
491
558
|
|
|
492
559
|
// src/document/commands.ts
|
|
493
560
|
var import_schema3 = require("@kubuild/schema");
|
|
@@ -1029,6 +1096,53 @@ function replaceNode(document, params) {
|
|
|
1029
1096
|
}
|
|
1030
1097
|
};
|
|
1031
1098
|
}
|
|
1099
|
+
function updateTheme(document, params) {
|
|
1100
|
+
const { theme, merge = true } = params;
|
|
1101
|
+
const newDoc = deepClone(document);
|
|
1102
|
+
const previousTheme = newDoc.theme ? deepClone(newDoc.theme) : void 0;
|
|
1103
|
+
if (theme === null) {
|
|
1104
|
+
delete newDoc.theme;
|
|
1105
|
+
} else {
|
|
1106
|
+
const next = {};
|
|
1107
|
+
for (const group of import_schema3.THEME_TOKEN_GROUPS) {
|
|
1108
|
+
const base = merge ? { ...previousTheme?.[group] ?? {} } : {};
|
|
1109
|
+
const patch = theme[group];
|
|
1110
|
+
if (patch === null) {
|
|
1111
|
+
continue;
|
|
1112
|
+
}
|
|
1113
|
+
if (patch) {
|
|
1114
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
1115
|
+
if (value === null) {
|
|
1116
|
+
delete base[key];
|
|
1117
|
+
} else {
|
|
1118
|
+
base[key] = value;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
if (Object.keys(base).length > 0) {
|
|
1123
|
+
next[group] = base;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
const parsed = import_schema3.ThemeSchema.parse(next);
|
|
1127
|
+
if (Object.keys(parsed).length > 0) {
|
|
1128
|
+
newDoc.theme = parsed;
|
|
1129
|
+
} else {
|
|
1130
|
+
delete newDoc.theme;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
return {
|
|
1134
|
+
document: newDoc,
|
|
1135
|
+
event: {
|
|
1136
|
+
type: "THEME_UPDATED",
|
|
1137
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1138
|
+
nodeId: newDoc.document.id,
|
|
1139
|
+
payload: {
|
|
1140
|
+
theme: newDoc.theme,
|
|
1141
|
+
previousTheme
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1032
1146
|
|
|
1033
1147
|
// src/document/artboards.ts
|
|
1034
1148
|
var import_schema4 = require("@kubuild/schema");
|
|
@@ -1551,6 +1665,15 @@ var DocumentHistoryManager = class {
|
|
|
1551
1665
|
var FORBIDDEN_KEY_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
1552
1666
|
function resolveBinding(binding, context) {
|
|
1553
1667
|
const segments = binding.key.split(".").filter(Boolean);
|
|
1668
|
+
if (segments.some((segment) => FORBIDDEN_KEY_SEGMENTS.has(segment))) {
|
|
1669
|
+
return applyMissingPolicy(binding);
|
|
1670
|
+
}
|
|
1671
|
+
if (context?.variables && typeof context.variables === "object" && Object.prototype.hasOwnProperty.call(context.variables, binding.key)) {
|
|
1672
|
+
const directValue = context.variables[binding.key];
|
|
1673
|
+
if (directValue !== void 0 && typeof directValue !== "function") {
|
|
1674
|
+
return { status: "resolved", value: directValue };
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1554
1677
|
let current = context?.variables;
|
|
1555
1678
|
for (const segment of segments) {
|
|
1556
1679
|
if (FORBIDDEN_KEY_SEGMENTS.has(segment)) {
|
|
@@ -2122,14 +2245,14 @@ var ActionPipelineExecutor = class {
|
|
|
2122
2245
|
async executeSingleStep(step, context, parentSignal, options) {
|
|
2123
2246
|
const stepStartTime = Date.now();
|
|
2124
2247
|
if (step.condition && !evaluateActionCondition(step.condition, context)) {
|
|
2125
|
-
const
|
|
2248
|
+
const skippedResult2 = {
|
|
2126
2249
|
stepId: step.id,
|
|
2127
2250
|
stepType: step.type,
|
|
2128
2251
|
status: "skipped",
|
|
2129
2252
|
durationMs: Date.now() - stepStartTime
|
|
2130
2253
|
};
|
|
2131
|
-
options?.onStepComplete?.(step,
|
|
2132
|
-
return
|
|
2254
|
+
options?.onStepComplete?.(step, skippedResult2, context);
|
|
2255
|
+
return skippedResult2;
|
|
2133
2256
|
}
|
|
2134
2257
|
options?.onStepStart?.(step, context);
|
|
2135
2258
|
const stepTimeout = step.timeout;
|
|
@@ -2621,7 +2744,7 @@ function generateTrackingEventId(prefix = "evt") {
|
|
|
2621
2744
|
const randomPart = Math.random().toString(36).substring(2, 10);
|
|
2622
2745
|
return `${prefix}_${timestamp}_${randomPart}`;
|
|
2623
2746
|
}
|
|
2624
|
-
async function sendMetaCapiEvent(event, config, options) {
|
|
2747
|
+
async function sendMetaCapiEvent(event, config, secrets, options) {
|
|
2625
2748
|
const fetcher = options?.fetchFn || globalThis.fetch;
|
|
2626
2749
|
if (!config.pixelId) {
|
|
2627
2750
|
return {
|
|
@@ -2695,9 +2818,10 @@ async function sendMetaCapiEvent(event, config, options) {
|
|
|
2695
2818
|
const headers = {
|
|
2696
2819
|
"Content-Type": "application/json"
|
|
2697
2820
|
};
|
|
2698
|
-
if (
|
|
2699
|
-
|
|
2821
|
+
if (!secrets?.capiAccessToken) {
|
|
2822
|
+
return skippedResult("meta", "No Meta CAPI access token resolved for this credential");
|
|
2700
2823
|
}
|
|
2824
|
+
headers["Authorization"] = `Bearer ${secrets.capiAccessToken}`;
|
|
2701
2825
|
const res = await fetcher(url, {
|
|
2702
2826
|
method: "POST",
|
|
2703
2827
|
headers,
|
|
@@ -2727,7 +2851,7 @@ async function sendMetaCapiEvent(event, config, options) {
|
|
|
2727
2851
|
};
|
|
2728
2852
|
}
|
|
2729
2853
|
}
|
|
2730
|
-
async function sendTikTokEventsApi(event, config, options) {
|
|
2854
|
+
async function sendTikTokEventsApi(event, config, secrets, options) {
|
|
2731
2855
|
const fetcher = options?.fetchFn || globalThis.fetch;
|
|
2732
2856
|
if (!config.pixelId) {
|
|
2733
2857
|
return { provider: "tiktok", success: false, error: "TikTok Pixel ID is missing" };
|
|
@@ -2777,9 +2901,10 @@ async function sendTikTokEventsApi(event, config, options) {
|
|
|
2777
2901
|
const headers = {
|
|
2778
2902
|
"Content-Type": "application/json"
|
|
2779
2903
|
};
|
|
2780
|
-
if (
|
|
2781
|
-
|
|
2904
|
+
if (!secrets?.accessToken) {
|
|
2905
|
+
return skippedResult("tiktok", "No TikTok Events API access token resolved for this credential");
|
|
2782
2906
|
}
|
|
2907
|
+
headers["Access-Token"] = secrets.accessToken;
|
|
2783
2908
|
const res = await fetcher(url, {
|
|
2784
2909
|
method: "POST",
|
|
2785
2910
|
headers,
|
|
@@ -2804,7 +2929,7 @@ async function sendTikTokEventsApi(event, config, options) {
|
|
|
2804
2929
|
};
|
|
2805
2930
|
}
|
|
2806
2931
|
}
|
|
2807
|
-
async function sendGa4MeasurementEvent(event, config, options) {
|
|
2932
|
+
async function sendGa4MeasurementEvent(event, config, secrets, options) {
|
|
2808
2933
|
const fetcher = options?.fetchFn || globalThis.fetch;
|
|
2809
2934
|
if (!config.measurementId) {
|
|
2810
2935
|
return { provider: "google", success: false, error: "GA4 Measurement ID is missing" };
|
|
@@ -2828,12 +2953,10 @@ async function sendGa4MeasurementEvent(event, config, options) {
|
|
|
2828
2953
|
return { provider: "google", success: true, data: { simulated: true, payload: ga4Payload } };
|
|
2829
2954
|
}
|
|
2830
2955
|
try {
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
)}`;
|
|
2834
|
-
if (config.measurementProtocolSecret) {
|
|
2835
|
-
url += `&api_secret=${encodeURIComponent(config.measurementProtocolSecret)}`;
|
|
2956
|
+
if (!secrets?.measurementProtocolSecret) {
|
|
2957
|
+
return skippedResult("google", "No GA4 Measurement Protocol API secret resolved for this credential");
|
|
2836
2958
|
}
|
|
2959
|
+
const url = `https://www.google-analytics.com/mp/collect?measurement_id=${encodeURIComponent(config.measurementId)}&api_secret=${encodeURIComponent(secrets.measurementProtocolSecret)}`;
|
|
2837
2960
|
const res = await fetcher(url, {
|
|
2838
2961
|
method: "POST",
|
|
2839
2962
|
headers: { "Content-Type": "application/json" },
|
|
@@ -2853,11 +2976,8 @@ async function sendGa4MeasurementEvent(event, config, options) {
|
|
|
2853
2976
|
};
|
|
2854
2977
|
}
|
|
2855
2978
|
}
|
|
2856
|
-
async function sendCustomWebhookEvent(event,
|
|
2979
|
+
async function sendCustomWebhookEvent(event, _config, secrets, options) {
|
|
2857
2980
|
const fetcher = options?.fetchFn || globalThis.fetch;
|
|
2858
|
-
if (!config.endpointUrl) {
|
|
2859
|
-
return { provider: "custom", success: false, error: "Custom Webhook URL is missing" };
|
|
2860
|
-
}
|
|
2861
2981
|
const webhookPayload = {
|
|
2862
2982
|
event: event.eventName,
|
|
2863
2983
|
eventId: event.eventId,
|
|
@@ -2871,12 +2991,15 @@ async function sendCustomWebhookEvent(event, config, options) {
|
|
|
2871
2991
|
options.onLog?.("[Custom Webhook SIMULATED]", webhookPayload);
|
|
2872
2992
|
return { provider: "custom", success: true, data: { simulated: true, payload: webhookPayload } };
|
|
2873
2993
|
}
|
|
2994
|
+
if (!secrets?.endpointUrl) {
|
|
2995
|
+
return skippedResult("custom", "No custom webhook destination resolved for this credential");
|
|
2996
|
+
}
|
|
2874
2997
|
try {
|
|
2875
|
-
const res = await fetcher(
|
|
2998
|
+
const res = await fetcher(secrets.endpointUrl, {
|
|
2876
2999
|
method: "POST",
|
|
2877
3000
|
headers: {
|
|
2878
3001
|
"Content-Type": "application/json",
|
|
2879
|
-
...
|
|
3002
|
+
...secrets.headers || {}
|
|
2880
3003
|
},
|
|
2881
3004
|
body: JSON.stringify(webhookPayload)
|
|
2882
3005
|
});
|
|
@@ -2895,6 +3018,9 @@ async function sendCustomWebhookEvent(event, config, options) {
|
|
|
2895
3018
|
};
|
|
2896
3019
|
}
|
|
2897
3020
|
}
|
|
3021
|
+
function skippedResult(provider, reason) {
|
|
3022
|
+
return { provider, success: true, skipped: true, reason };
|
|
3023
|
+
}
|
|
2898
3024
|
async function dispatchServerTracking(event, config, options) {
|
|
2899
3025
|
const eventId = event.eventId || generateTrackingEventId();
|
|
2900
3026
|
const eventWithId = { ...event, eventId };
|
|
@@ -2919,66 +3045,231 @@ async function dispatchServerTracking(event, config, options) {
|
|
|
2919
3045
|
options?.onLog?.(msg, data);
|
|
2920
3046
|
}
|
|
2921
3047
|
};
|
|
3048
|
+
const target = options?.provider || "all";
|
|
3049
|
+
const wants = (key) => target === "all" || target === key;
|
|
2922
3050
|
const providers = config.providers || {};
|
|
2923
|
-
const
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
);
|
|
3051
|
+
const tasks = [];
|
|
3052
|
+
const meta = providers.meta;
|
|
3053
|
+
if (wants("meta") && meta && meta.enabled !== false && meta.capiEnabled) {
|
|
3054
|
+
tasks.push({
|
|
3055
|
+
key: "meta",
|
|
3056
|
+
credentialId: meta.credentialId,
|
|
3057
|
+
run: (s) => sendMetaCapiEvent(eventWithId, meta, s, mergedOptions)
|
|
3058
|
+
});
|
|
2931
3059
|
}
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
);
|
|
3060
|
+
const tiktok = providers.tiktok;
|
|
3061
|
+
if (wants("tiktok") && tiktok && tiktok.enabled !== false && tiktok.eventsApiEnabled) {
|
|
3062
|
+
tasks.push({
|
|
3063
|
+
key: "tiktok",
|
|
3064
|
+
credentialId: tiktok.credentialId,
|
|
3065
|
+
run: (s) => sendTikTokEventsApi(eventWithId, tiktok, s, mergedOptions)
|
|
3066
|
+
});
|
|
2939
3067
|
}
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
);
|
|
3068
|
+
const google = providers.google;
|
|
3069
|
+
if (wants("google") && google && google.enabled !== false && google.measurementId) {
|
|
3070
|
+
tasks.push({
|
|
3071
|
+
key: "google",
|
|
3072
|
+
credentialId: google.credentialId,
|
|
3073
|
+
run: (s) => sendGa4MeasurementEvent(eventWithId, google, s, mergedOptions)
|
|
3074
|
+
});
|
|
2947
3075
|
}
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
);
|
|
3076
|
+
const custom = providers.custom;
|
|
3077
|
+
if (wants("custom") && custom && custom.enabled !== false) {
|
|
3078
|
+
tasks.push({
|
|
3079
|
+
key: "custom",
|
|
3080
|
+
credentialId: custom.credentialId,
|
|
3081
|
+
run: (s) => sendCustomWebhookEvent(eventWithId, custom, s, mergedOptions)
|
|
3082
|
+
});
|
|
3083
|
+
}
|
|
3084
|
+
if (tasks.length === 0) {
|
|
3085
|
+
return {
|
|
3086
|
+
success: true,
|
|
3087
|
+
eventId,
|
|
3088
|
+
skipped: true,
|
|
3089
|
+
reason: target === "gtm" ? "GTM is client-side only (dataLayer); no server delivery" : "No server-side tracking provider is enabled",
|
|
3090
|
+
results: {}
|
|
3091
|
+
};
|
|
2955
3092
|
}
|
|
2956
|
-
const
|
|
3093
|
+
const resolve = options?.resolveSecrets;
|
|
3094
|
+
const runTask = async (task) => {
|
|
3095
|
+
let secrets = null;
|
|
3096
|
+
if (resolve) {
|
|
3097
|
+
try {
|
|
3098
|
+
secrets = await resolve({ provider: task.key, credentialId: task.credentialId, documentId: options?.documentId }) ?? null;
|
|
3099
|
+
} catch (err) {
|
|
3100
|
+
return {
|
|
3101
|
+
provider: task.key,
|
|
3102
|
+
success: false,
|
|
3103
|
+
error: `Secret resolution failed: ${err instanceof Error ? err.message : String(err)}`
|
|
3104
|
+
};
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
if (!secrets && !simulate) {
|
|
3108
|
+
const reason = resolve ? `No secret resolved for provider "${task.key}"${task.credentialId ? ` (credentialId "${task.credentialId}")` : " (no credentialId linked)"}` : "No secret resolver configured (options.resolveSecrets)";
|
|
3109
|
+
mergedOptions.onLog?.(`[${task.key}] skipped: ${reason}`);
|
|
3110
|
+
return skippedResult(task.key, reason);
|
|
3111
|
+
}
|
|
3112
|
+
return task.run(secrets);
|
|
3113
|
+
};
|
|
3114
|
+
const settled = await Promise.allSettled(tasks.map((task) => runTask(task)));
|
|
2957
3115
|
const results = {};
|
|
2958
3116
|
let overallSuccess = true;
|
|
2959
|
-
|
|
3117
|
+
let delivered = 0;
|
|
3118
|
+
settled.forEach((item, index) => {
|
|
3119
|
+
const key = tasks[index].key;
|
|
2960
3120
|
if (item.status === "fulfilled") {
|
|
2961
|
-
results[
|
|
2962
|
-
if (!item.value.
|
|
2963
|
-
|
|
2964
|
-
}
|
|
3121
|
+
results[key] = item.value;
|
|
3122
|
+
if (!item.value.success) overallSuccess = false;
|
|
3123
|
+
if (!item.value.skipped) delivered++;
|
|
2965
3124
|
} else {
|
|
2966
3125
|
overallSuccess = false;
|
|
3126
|
+
results[key] = {
|
|
3127
|
+
provider: key,
|
|
3128
|
+
success: false,
|
|
3129
|
+
error: item.reason instanceof Error ? item.reason.message : String(item.reason)
|
|
3130
|
+
};
|
|
2967
3131
|
}
|
|
2968
|
-
}
|
|
3132
|
+
});
|
|
3133
|
+
const allSkipped = delivered === 0 && overallSuccess;
|
|
2969
3134
|
return {
|
|
2970
3135
|
success: overallSuccess,
|
|
2971
3136
|
eventId,
|
|
3137
|
+
...allSkipped ? { skipped: true, reason: "All server providers were skipped" } : {},
|
|
2972
3138
|
results
|
|
2973
3139
|
};
|
|
2974
3140
|
}
|
|
2975
3141
|
|
|
3142
|
+
// src/runtime/tracking-relay.ts
|
|
3143
|
+
var import_schema5 = require("@kubuild/schema");
|
|
3144
|
+
var DEFAULT_MAX_BODY_BYTES = 64 * 1024;
|
|
3145
|
+
function defaultClientIp(request) {
|
|
3146
|
+
const forwarded = request.headers.get("x-forwarded-for");
|
|
3147
|
+
if (forwarded) {
|
|
3148
|
+
const first = forwarded.split(",")[0]?.trim();
|
|
3149
|
+
if (first) return first;
|
|
3150
|
+
}
|
|
3151
|
+
return request.headers.get("cf-connecting-ip")?.trim() || request.headers.get("x-real-ip")?.trim() || void 0;
|
|
3152
|
+
}
|
|
3153
|
+
function isOriginAllowed(origin, allowed) {
|
|
3154
|
+
if (!allowed) return true;
|
|
3155
|
+
if (!origin) return false;
|
|
3156
|
+
if (typeof allowed === "function") return allowed(origin);
|
|
3157
|
+
return allowed.includes(origin);
|
|
3158
|
+
}
|
|
3159
|
+
function createTrackingRelayHandler(options) {
|
|
3160
|
+
const maxBodyBytes = options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;
|
|
3161
|
+
return async (request) => {
|
|
3162
|
+
const origin = request.headers.get("origin");
|
|
3163
|
+
const corsHeaders = {};
|
|
3164
|
+
if (options.allowedOrigins && origin && isOriginAllowed(origin, options.allowedOrigins)) {
|
|
3165
|
+
corsHeaders["Access-Control-Allow-Origin"] = origin;
|
|
3166
|
+
corsHeaders["Vary"] = "Origin";
|
|
3167
|
+
}
|
|
3168
|
+
const json = (body, status, extra) => new Response(JSON.stringify(body), {
|
|
3169
|
+
status,
|
|
3170
|
+
headers: { "Content-Type": "application/json", ...corsHeaders, ...extra || {} }
|
|
3171
|
+
});
|
|
3172
|
+
const fail = (code, message, status, extra) => json({ version: import_schema5.TRACKING_RELAY_PROTOCOL_VERSION, success: false, error: { code, message } }, status, extra);
|
|
3173
|
+
if (!isOriginAllowed(origin, options.allowedOrigins)) {
|
|
3174
|
+
return fail("FORBIDDEN_ORIGIN", "Origin is not allowed", 403);
|
|
3175
|
+
}
|
|
3176
|
+
if (request.method === "OPTIONS") {
|
|
3177
|
+
return new Response(null, {
|
|
3178
|
+
status: 204,
|
|
3179
|
+
headers: {
|
|
3180
|
+
...corsHeaders,
|
|
3181
|
+
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
|
3182
|
+
"Access-Control-Allow-Headers": "Content-Type",
|
|
3183
|
+
"Access-Control-Max-Age": "600"
|
|
3184
|
+
}
|
|
3185
|
+
});
|
|
3186
|
+
}
|
|
3187
|
+
if (request.method !== "POST") {
|
|
3188
|
+
return fail("METHOD_NOT_ALLOWED", "Only POST requests are accepted", 405, { Allow: "POST, OPTIONS" });
|
|
3189
|
+
}
|
|
3190
|
+
try {
|
|
3191
|
+
const text = await request.text();
|
|
3192
|
+
if (text.length > maxBodyBytes) {
|
|
3193
|
+
return fail("INVALID_REQUEST", `Request body exceeds ${maxBodyBytes} bytes`, 413);
|
|
3194
|
+
}
|
|
3195
|
+
let raw;
|
|
3196
|
+
try {
|
|
3197
|
+
raw = JSON.parse(text);
|
|
3198
|
+
} catch {
|
|
3199
|
+
return fail("INVALID_REQUEST", "Request body must be valid JSON", 400);
|
|
3200
|
+
}
|
|
3201
|
+
if (raw && typeof raw === "object" && "version" in raw && raw.version !== import_schema5.TRACKING_RELAY_PROTOCOL_VERSION) {
|
|
3202
|
+
return fail(
|
|
3203
|
+
"UNSUPPORTED_VERSION",
|
|
3204
|
+
`Unsupported relay protocol version; expected ${import_schema5.TRACKING_RELAY_PROTOCOL_VERSION}`,
|
|
3205
|
+
400
|
|
3206
|
+
);
|
|
3207
|
+
}
|
|
3208
|
+
const parsed = import_schema5.TrackingRelayRequestSchema.safeParse(raw);
|
|
3209
|
+
if (!parsed.success) {
|
|
3210
|
+
const issues = parsed.error.issues.slice(0, 5).map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
|
|
3211
|
+
return fail("INVALID_REQUEST", `Invalid relay request: ${issues}`, 400);
|
|
3212
|
+
}
|
|
3213
|
+
const body = parsed.data;
|
|
3214
|
+
const config = await options.getConfig({
|
|
3215
|
+
request,
|
|
3216
|
+
documentId: body.documentId,
|
|
3217
|
+
credentialId: body.credentialId,
|
|
3218
|
+
provider: body.provider
|
|
3219
|
+
});
|
|
3220
|
+
if (!config) {
|
|
3221
|
+
return fail("CONFIG_NOT_FOUND", "No tracking configuration found for this page", 404);
|
|
3222
|
+
}
|
|
3223
|
+
const clientIp = (options.getClientIp ?? defaultClientIp)(request);
|
|
3224
|
+
const clientUserAgent = request.headers.get("user-agent") || void 0;
|
|
3225
|
+
const userData = { ...body.event.userData || {} };
|
|
3226
|
+
delete userData.clientIp;
|
|
3227
|
+
delete userData.clientUserAgent;
|
|
3228
|
+
const event = { ...body.event, userData };
|
|
3229
|
+
const outcome = await dispatchServerTracking(event, config, {
|
|
3230
|
+
resolveSecrets: options.resolveSecrets,
|
|
3231
|
+
documentId: body.documentId,
|
|
3232
|
+
provider: body.provider,
|
|
3233
|
+
fetchFn: options.fetchFn,
|
|
3234
|
+
clientIp,
|
|
3235
|
+
clientUserAgent,
|
|
3236
|
+
onLog: options.onLog
|
|
3237
|
+
});
|
|
3238
|
+
const results = {};
|
|
3239
|
+
for (const [key, r] of Object.entries(outcome.results)) {
|
|
3240
|
+
results[key] = {
|
|
3241
|
+
provider: r.provider,
|
|
3242
|
+
success: r.success,
|
|
3243
|
+
...r.status !== void 0 ? { status: r.status } : {},
|
|
3244
|
+
...r.skipped ? { skipped: true } : {},
|
|
3245
|
+
...r.reason ? { reason: r.reason } : {},
|
|
3246
|
+
...r.error ? { error: r.error } : {}
|
|
3247
|
+
};
|
|
3248
|
+
}
|
|
3249
|
+
return json(
|
|
3250
|
+
{
|
|
3251
|
+
version: import_schema5.TRACKING_RELAY_PROTOCOL_VERSION,
|
|
3252
|
+
success: outcome.success,
|
|
3253
|
+
eventId: outcome.eventId,
|
|
3254
|
+
...outcome.skipped ? { skipped: true } : {},
|
|
3255
|
+
...outcome.reason ? { reason: outcome.reason } : {},
|
|
3256
|
+
results
|
|
3257
|
+
},
|
|
3258
|
+
200
|
|
3259
|
+
);
|
|
3260
|
+
} catch (err) {
|
|
3261
|
+
options.onLog?.("[Tracking relay] internal error", err);
|
|
3262
|
+
return fail("INTERNAL_ERROR", "Tracking relay failed", 500);
|
|
3263
|
+
}
|
|
3264
|
+
};
|
|
3265
|
+
}
|
|
3266
|
+
|
|
2976
3267
|
// src/io/exporter.ts
|
|
2977
3268
|
var import_fflate = require("fflate");
|
|
2978
|
-
var
|
|
3269
|
+
var import_schema8 = require("@kubuild/schema");
|
|
2979
3270
|
|
|
2980
3271
|
// src/validation/validator.ts
|
|
2981
|
-
var
|
|
3272
|
+
var import_schema6 = require("@kubuild/schema");
|
|
2982
3273
|
|
|
2983
3274
|
// src/validation/security.ts
|
|
2984
3275
|
var DEFAULT_DOCUMENT_SECURITY_LIMITS = {
|
|
@@ -3267,10 +3558,10 @@ function validateDocument(input, options = {}) {
|
|
|
3267
3558
|
};
|
|
3268
3559
|
}
|
|
3269
3560
|
}
|
|
3270
|
-
if (doc.schema !==
|
|
3561
|
+
if (doc.schema !== import_schema6.SCHEMA_NAME) {
|
|
3271
3562
|
errors.push({
|
|
3272
3563
|
code: "GLOBAL_SCHEMA_INVALID",
|
|
3273
|
-
message: `Invalid schema identifier. Expected "${
|
|
3564
|
+
message: `Invalid schema identifier. Expected "${import_schema6.SCHEMA_NAME}", received "${String(doc.schema)}"`,
|
|
3274
3565
|
path: "/schema"
|
|
3275
3566
|
});
|
|
3276
3567
|
}
|
|
@@ -3334,7 +3625,7 @@ function validateDocument(input, options = {}) {
|
|
|
3334
3625
|
warnings
|
|
3335
3626
|
);
|
|
3336
3627
|
if (errors.length === 0) {
|
|
3337
|
-
const zodResult =
|
|
3628
|
+
const zodResult = import_schema6.PageDocumentSchema.safeParse(input);
|
|
3338
3629
|
if (!zodResult.success) {
|
|
3339
3630
|
for (const issue of zodResult.error.issues) {
|
|
3340
3631
|
const jsonPath = "/" + issue.path.join("/");
|
|
@@ -3514,7 +3805,8 @@ function validateNodeRecursive(nodeObj, currentPath, parentNode, seenIds, visite
|
|
|
3514
3805
|
`${currentPath}/props`,
|
|
3515
3806
|
effectiveNodeId,
|
|
3516
3807
|
options,
|
|
3517
|
-
errors
|
|
3808
|
+
errors,
|
|
3809
|
+
warnings
|
|
3518
3810
|
);
|
|
3519
3811
|
}
|
|
3520
3812
|
}
|
|
@@ -3529,7 +3821,10 @@ function validateNodeRecursive(nodeObj, currentPath, parentNode, seenIds, visite
|
|
|
3529
3821
|
}
|
|
3530
3822
|
}
|
|
3531
3823
|
}
|
|
3532
|
-
function validatePropsBindings(propsObj, propsPath, nodeId, options, errors) {
|
|
3824
|
+
function validatePropsBindings(propsObj, propsPath, nodeId, options, errors, warnings) {
|
|
3825
|
+
const checkAssets = options.checkAssetReferences !== false;
|
|
3826
|
+
const checkVariables = options.checkVariableBindings !== false;
|
|
3827
|
+
const checkActions = options.checkActionBindings !== false;
|
|
3533
3828
|
for (const [key, value] of Object.entries(propsObj)) {
|
|
3534
3829
|
const currentPath = `${propsPath}/${key}`;
|
|
3535
3830
|
if (!value || typeof value !== "object") {
|
|
@@ -3543,7 +3838,8 @@ function validatePropsBindings(propsObj, propsPath, nodeId, options, errors) {
|
|
|
3543
3838
|
`${currentPath}/${index}`,
|
|
3544
3839
|
nodeId,
|
|
3545
3840
|
options,
|
|
3546
|
-
errors
|
|
3841
|
+
errors,
|
|
3842
|
+
warnings
|
|
3547
3843
|
);
|
|
3548
3844
|
}
|
|
3549
3845
|
});
|
|
@@ -3551,7 +3847,9 @@ function validatePropsBindings(propsObj, propsPath, nodeId, options, errors) {
|
|
|
3551
3847
|
}
|
|
3552
3848
|
const record = value;
|
|
3553
3849
|
if (record.type === "asset") {
|
|
3554
|
-
if (
|
|
3850
|
+
if (!checkAssets) continue;
|
|
3851
|
+
const assetId = typeof record.assetId === "string" && record.assetId.trim().length > 0 ? record.assetId : void 0;
|
|
3852
|
+
if (assetId === void 0) {
|
|
3555
3853
|
errors.push({
|
|
3556
3854
|
code: "INVALID_ASSET_REFERENCE",
|
|
3557
3855
|
message: 'Asset reference must have a non-empty "assetId"',
|
|
@@ -3567,7 +3865,27 @@ function validatePropsBindings(propsObj, propsPath, nodeId, options, errors) {
|
|
|
3567
3865
|
nodeId
|
|
3568
3866
|
});
|
|
3569
3867
|
}
|
|
3868
|
+
if (assetId !== void 0 && options.knownAssetIds && !isKnownId(options.knownAssetIds, assetId)) {
|
|
3869
|
+
if (typeof record.fallbackUrl === "string" && record.fallbackUrl.length > 0) {
|
|
3870
|
+
warnings.push({
|
|
3871
|
+
code: "UNRESOLVED_ASSET_REFERENCE",
|
|
3872
|
+
message: `Asset "${assetId}" is not among the known assets; its fallbackUrl will be used`,
|
|
3873
|
+
path: `${currentPath}/assetId`,
|
|
3874
|
+
nodeId,
|
|
3875
|
+
details: { assetId }
|
|
3876
|
+
});
|
|
3877
|
+
} else {
|
|
3878
|
+
errors.push({
|
|
3879
|
+
code: "INVALID_ASSET_REFERENCE",
|
|
3880
|
+
message: `Asset "${assetId}" is not among the known assets and has no fallbackUrl`,
|
|
3881
|
+
path: `${currentPath}/assetId`,
|
|
3882
|
+
nodeId,
|
|
3883
|
+
details: { assetId }
|
|
3884
|
+
});
|
|
3885
|
+
}
|
|
3886
|
+
}
|
|
3570
3887
|
} else if (record.type === "variable") {
|
|
3888
|
+
if (!checkVariables) continue;
|
|
3571
3889
|
if (typeof record.key !== "string" || record.key.trim().length === 0) {
|
|
3572
3890
|
errors.push({
|
|
3573
3891
|
code: "INVALID_VARIABLE_BINDING",
|
|
@@ -3577,6 +3895,7 @@ function validatePropsBindings(propsObj, propsPath, nodeId, options, errors) {
|
|
|
3577
3895
|
});
|
|
3578
3896
|
}
|
|
3579
3897
|
} else if (key === "action" || typeof record.type === "string" && record.payload !== void 0) {
|
|
3898
|
+
if (!checkActions) continue;
|
|
3580
3899
|
if (typeof record.type !== "string" || record.type.trim().length === 0) {
|
|
3581
3900
|
errors.push({
|
|
3582
3901
|
code: "INVALID_ACTION_BINDING",
|
|
@@ -3586,10 +3905,62 @@ function validatePropsBindings(propsObj, propsPath, nodeId, options, errors) {
|
|
|
3586
3905
|
});
|
|
3587
3906
|
}
|
|
3588
3907
|
} else {
|
|
3589
|
-
validatePropsBindings(record, currentPath, nodeId, options, errors);
|
|
3908
|
+
validatePropsBindings(record, currentPath, nodeId, options, errors, warnings);
|
|
3590
3909
|
}
|
|
3591
3910
|
}
|
|
3592
3911
|
}
|
|
3912
|
+
function isKnownId(ids, id) {
|
|
3913
|
+
return Array.isArray(ids) ? ids.includes(id) : ids.has(id);
|
|
3914
|
+
}
|
|
3915
|
+
|
|
3916
|
+
// src/io/tracking-sanitizer.ts
|
|
3917
|
+
var import_schema7 = require("@kubuild/schema");
|
|
3918
|
+
function stripTrackingSecretsInPlace(tracking, basePath = "tracking") {
|
|
3919
|
+
const removed = [];
|
|
3920
|
+
if (!tracking || typeof tracking !== "object" || Array.isArray(tracking)) return removed;
|
|
3921
|
+
const providers = tracking.providers;
|
|
3922
|
+
if (!providers || typeof providers !== "object" || Array.isArray(providers)) return removed;
|
|
3923
|
+
for (const provider of Object.keys(import_schema7.LEGACY_TRACKING_SECRET_KEYS)) {
|
|
3924
|
+
const cfg = providers[provider];
|
|
3925
|
+
if (!cfg || typeof cfg !== "object" || Array.isArray(cfg)) continue;
|
|
3926
|
+
for (const key of import_schema7.LEGACY_TRACKING_SECRET_KEYS[provider]) {
|
|
3927
|
+
if (Object.prototype.hasOwnProperty.call(cfg, key)) {
|
|
3928
|
+
delete cfg[key];
|
|
3929
|
+
removed.push(`${basePath}.providers.${provider}.${key}`);
|
|
3930
|
+
}
|
|
3931
|
+
}
|
|
3932
|
+
}
|
|
3933
|
+
return removed;
|
|
3934
|
+
}
|
|
3935
|
+
function stripDocumentTrackingSecretsInPlace(doc) {
|
|
3936
|
+
const removed = [];
|
|
3937
|
+
if (!doc || typeof doc !== "object" || Array.isArray(doc)) return removed;
|
|
3938
|
+
const record = doc;
|
|
3939
|
+
removed.push(...stripTrackingSecretsInPlace(record.tracking, "tracking"));
|
|
3940
|
+
const metadata = record.metadata;
|
|
3941
|
+
if (metadata && typeof metadata === "object" && !Array.isArray(metadata)) {
|
|
3942
|
+
removed.push(
|
|
3943
|
+
...stripTrackingSecretsInPlace(metadata.tracking, "metadata.tracking")
|
|
3944
|
+
);
|
|
3945
|
+
}
|
|
3946
|
+
if (Array.isArray(record.artboards)) {
|
|
3947
|
+
record.artboards.forEach((artboard, index) => {
|
|
3948
|
+
if (artboard && typeof artboard === "object") {
|
|
3949
|
+
const inner = artboard.document;
|
|
3950
|
+
for (const path of stripDocumentTrackingSecretsInPlace(inner)) {
|
|
3951
|
+
removed.push(`artboards.${index}.document.${path}`);
|
|
3952
|
+
}
|
|
3953
|
+
}
|
|
3954
|
+
});
|
|
3955
|
+
}
|
|
3956
|
+
return removed;
|
|
3957
|
+
}
|
|
3958
|
+
function sanitizeDocumentTracking(doc) {
|
|
3959
|
+
if (!doc || typeof doc !== "object") return { document: doc, removed: [] };
|
|
3960
|
+
const copy = JSON.parse(JSON.stringify(doc));
|
|
3961
|
+
const removed = stripDocumentTrackingSecretsInPlace(copy);
|
|
3962
|
+
return { document: copy, removed };
|
|
3963
|
+
}
|
|
3593
3964
|
|
|
3594
3965
|
// src/io/exporter.ts
|
|
3595
3966
|
function sha256Sync(data) {
|
|
@@ -3810,6 +4181,7 @@ async function exportPackage(document, options = {}) {
|
|
|
3810
4181
|
};
|
|
3811
4182
|
}
|
|
3812
4183
|
const pageDoc = JSON.parse(JSON.stringify(validation.data));
|
|
4184
|
+
stripDocumentTrackingSecretsInPlace(pageDoc);
|
|
3813
4185
|
if (options.metadata) {
|
|
3814
4186
|
pageDoc.metadata = {
|
|
3815
4187
|
...pageDoc.metadata || {
|
|
@@ -3933,8 +4305,8 @@ async function exportPackage(document, options = {}) {
|
|
|
3933
4305
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3934
4306
|
};
|
|
3935
4307
|
const manifest = {
|
|
3936
|
-
schema:
|
|
3937
|
-
schemaVersion: pageDoc.version ||
|
|
4308
|
+
schema: import_schema8.SCHEMA_NAME,
|
|
4309
|
+
schemaVersion: pageDoc.version || import_schema8.CURRENT_SCHEMA_VERSION,
|
|
3938
4310
|
packageVersion: options.packageVersion || "1.0.0",
|
|
3939
4311
|
builderCompatibility: options.builderCompatibility || ">=0.1.0",
|
|
3940
4312
|
requiredComponents: requirements.requiredComponents,
|
|
@@ -3959,10 +4331,10 @@ var exportStoraPackage = exportPackage;
|
|
|
3959
4331
|
|
|
3960
4332
|
// src/io/importer.ts
|
|
3961
4333
|
var import_fflate2 = require("fflate");
|
|
3962
|
-
var
|
|
4334
|
+
var import_schema10 = require("@kubuild/schema");
|
|
3963
4335
|
|
|
3964
4336
|
// src/io/migration.ts
|
|
3965
|
-
var
|
|
4337
|
+
var import_schema9 = require("@kubuild/schema");
|
|
3966
4338
|
var MigrationRegistry = class {
|
|
3967
4339
|
steps = /* @__PURE__ */ new Map();
|
|
3968
4340
|
/**
|
|
@@ -4041,7 +4413,7 @@ defaultMigrationRegistry.register({
|
|
|
4041
4413
|
description: "Migrate alpha 0.1.0 schema (root node key, flat styles) to v1.0.0",
|
|
4042
4414
|
migrate: (rawDoc) => {
|
|
4043
4415
|
const migrated = deepClone(rawDoc);
|
|
4044
|
-
migrated.schema =
|
|
4416
|
+
migrated.schema = import_schema9.SCHEMA_NAME;
|
|
4045
4417
|
migrated.version = "1.0.0";
|
|
4046
4418
|
if (migrated.root && !migrated.document) {
|
|
4047
4419
|
migrated.document = migrated.root;
|
|
@@ -4076,7 +4448,7 @@ defaultMigrationRegistry.register({
|
|
|
4076
4448
|
description: "Migrate 0.2.0 schema to v1.0.0",
|
|
4077
4449
|
migrate: (rawDoc) => {
|
|
4078
4450
|
const migrated = deepClone(rawDoc);
|
|
4079
|
-
migrated.schema =
|
|
4451
|
+
migrated.schema = import_schema9.SCHEMA_NAME;
|
|
4080
4452
|
migrated.version = "1.0.0";
|
|
4081
4453
|
if (migrated.document && typeof migrated.document === "object") {
|
|
4082
4454
|
convertNodeStylesToResponsive(migrated.document);
|
|
@@ -4090,7 +4462,7 @@ defaultMigrationRegistry.register({
|
|
|
4090
4462
|
description: "Migrate 0.9.0 beta schema to v1.0.0",
|
|
4091
4463
|
migrate: (rawDoc) => {
|
|
4092
4464
|
const migrated = deepClone(rawDoc);
|
|
4093
|
-
migrated.schema =
|
|
4465
|
+
migrated.schema = import_schema9.SCHEMA_NAME;
|
|
4094
4466
|
migrated.version = "1.0.0";
|
|
4095
4467
|
if (!migrated.metadata || typeof migrated.metadata !== "object") {
|
|
4096
4468
|
migrated.metadata = {
|
|
@@ -4115,17 +4487,86 @@ defaultMigrationRegistry.register({
|
|
|
4115
4487
|
return migrated;
|
|
4116
4488
|
}
|
|
4117
4489
|
});
|
|
4118
|
-
|
|
4490
|
+
defaultMigrationRegistry.register({
|
|
4491
|
+
fromVersion: "1.0.0",
|
|
4492
|
+
toVersion: "1.1.0",
|
|
4493
|
+
description: "Remove tracking secrets and server destinations (capiAccessToken, accessToken, measurementProtocolSecret, serverRelayUrl, custom endpointUrl/headers) from the document",
|
|
4494
|
+
migrate: (rawDoc, context) => {
|
|
4495
|
+
const migrated = deepClone(rawDoc);
|
|
4496
|
+
migrated.version = "1.1.0";
|
|
4497
|
+
const removed = stripDocumentTrackingSecretsInPlace(migrated);
|
|
4498
|
+
if (removed.length > 0) {
|
|
4499
|
+
context.warn({
|
|
4500
|
+
code: "TRACKING_SECRET_REMOVED",
|
|
4501
|
+
message: "Tracking secret removed; re-link credential via credentialId (secrets are now stored by the host and resolved server-side).",
|
|
4502
|
+
step: "1.0.0->1.1.0",
|
|
4503
|
+
paths: removed
|
|
4504
|
+
});
|
|
4505
|
+
}
|
|
4506
|
+
return migrated;
|
|
4507
|
+
}
|
|
4508
|
+
});
|
|
4509
|
+
function canonicalizeTextPropsInPlace(root, basePath = "document") {
|
|
4510
|
+
const changed = [];
|
|
4511
|
+
const walk2 = (value, path) => {
|
|
4512
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
4513
|
+
const node = value;
|
|
4514
|
+
const props = node.props;
|
|
4515
|
+
const rule = typeof node.type === "string" ? (0, import_schema9.getCanonicalTextPropRule)(node.type) : void 0;
|
|
4516
|
+
if (rule && props && typeof props === "object" && !Array.isArray(props)) {
|
|
4517
|
+
const record = props;
|
|
4518
|
+
const presentAliases = rule.aliases.filter((alias) => record[alias] !== void 0);
|
|
4519
|
+
if (presentAliases.length > 0) {
|
|
4520
|
+
const displayedKey = rule.legacyReadOrder.find((key) => record[key] !== void 0);
|
|
4521
|
+
if (node.type === "text" && displayedKey === "content" && record.as === void 0 && record.tag === void 0) {
|
|
4522
|
+
record.as = "p";
|
|
4523
|
+
}
|
|
4524
|
+
if (displayedKey !== void 0 && displayedKey !== rule.canonical) {
|
|
4525
|
+
record[rule.canonical] = record[displayedKey];
|
|
4526
|
+
}
|
|
4527
|
+
for (const alias of presentAliases) {
|
|
4528
|
+
delete record[alias];
|
|
4529
|
+
changed.push(`${path}.props.${alias}`);
|
|
4530
|
+
}
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4533
|
+
if (Array.isArray(node.children)) {
|
|
4534
|
+
node.children.forEach((child, index) => walk2(child, `${path}.children.${index}`));
|
|
4535
|
+
}
|
|
4536
|
+
};
|
|
4537
|
+
walk2(root, basePath);
|
|
4538
|
+
return changed;
|
|
4539
|
+
}
|
|
4540
|
+
defaultMigrationRegistry.register({
|
|
4541
|
+
fromVersion: "1.1.0",
|
|
4542
|
+
toVersion: "1.2.0",
|
|
4543
|
+
description: "Rename deprecated text prop aliases to canonical names (heading/text/paragraph/link/badge/blockquote -> text, button -> label)",
|
|
4544
|
+
migrate: (rawDoc, context) => {
|
|
4545
|
+
const migrated = deepClone(rawDoc);
|
|
4546
|
+
migrated.version = "1.2.0";
|
|
4547
|
+
const changed = canonicalizeTextPropsInPlace(migrated.document);
|
|
4548
|
+
if (changed.length > 0) {
|
|
4549
|
+
context.warn({
|
|
4550
|
+
code: "PROP_ALIAS_MIGRATED",
|
|
4551
|
+
message: "Deprecated text prop aliases were renamed to their canonical names.",
|
|
4552
|
+
step: "1.1.0->1.2.0",
|
|
4553
|
+
paths: changed
|
|
4554
|
+
});
|
|
4555
|
+
}
|
|
4556
|
+
return migrated;
|
|
4557
|
+
}
|
|
4558
|
+
});
|
|
4559
|
+
function canMigrate(sourceVersion, targetVersion = import_schema9.CURRENT_SCHEMA_VERSION, registry = defaultMigrationRegistry) {
|
|
4119
4560
|
if (!sourceVersion || !targetVersion) return false;
|
|
4120
4561
|
if (sourceVersion === targetVersion) return true;
|
|
4121
4562
|
return registry.hasPath(sourceVersion, targetVersion);
|
|
4122
4563
|
}
|
|
4123
|
-
function getMigrationPath(sourceVersion, targetVersion =
|
|
4564
|
+
function getMigrationPath(sourceVersion, targetVersion = import_schema9.CURRENT_SCHEMA_VERSION, registry = defaultMigrationRegistry) {
|
|
4124
4565
|
if (!sourceVersion || !targetVersion) return null;
|
|
4125
4566
|
return registry.findPath(sourceVersion, targetVersion);
|
|
4126
4567
|
}
|
|
4127
4568
|
function migrateDocument(rawDocument, options = {}) {
|
|
4128
|
-
const targetVersion = options.targetVersion ??
|
|
4569
|
+
const targetVersion = options.targetVersion ?? import_schema9.CURRENT_SCHEMA_VERSION;
|
|
4129
4570
|
const dryRun = options.dryRun ?? false;
|
|
4130
4571
|
const registry = options.registry ?? defaultMigrationRegistry;
|
|
4131
4572
|
const validate = options.validate ?? true;
|
|
@@ -4153,6 +4594,14 @@ function migrateDocument(rawDocument, options = {}) {
|
|
|
4153
4594
|
const sourceVersion = typeof doc.version === "string" ? doc.version.trim() : "unknown";
|
|
4154
4595
|
if (sourceVersion === targetVersion) {
|
|
4155
4596
|
const cloned = deepClone(doc);
|
|
4597
|
+
const removedSecrets = stripDocumentTrackingSecretsInPlace(cloned);
|
|
4598
|
+
const currentWarnings = removedSecrets.length > 0 ? [
|
|
4599
|
+
{
|
|
4600
|
+
code: "TRACKING_SECRET_REMOVED",
|
|
4601
|
+
message: "Tracking secret removed; re-link credential via credentialId (secrets are stored by the host and resolved server-side).",
|
|
4602
|
+
paths: removedSecrets
|
|
4603
|
+
}
|
|
4604
|
+
] : [];
|
|
4156
4605
|
if (validate) {
|
|
4157
4606
|
const validation = validateDocument(cloned);
|
|
4158
4607
|
if (!validation.valid) {
|
|
@@ -4184,7 +4633,8 @@ function migrateDocument(rawDocument, options = {}) {
|
|
|
4184
4633
|
targetVersion,
|
|
4185
4634
|
migrationPath: [sourceVersion],
|
|
4186
4635
|
stepsApplied: 0,
|
|
4187
|
-
dryRun
|
|
4636
|
+
dryRun,
|
|
4637
|
+
...currentWarnings.length > 0 ? { warnings: currentWarnings } : {}
|
|
4188
4638
|
}
|
|
4189
4639
|
};
|
|
4190
4640
|
}
|
|
@@ -4210,6 +4660,16 @@ function migrateDocument(rawDocument, options = {}) {
|
|
|
4210
4660
|
};
|
|
4211
4661
|
}
|
|
4212
4662
|
if (dryRun) {
|
|
4663
|
+
const simulatedWarnings = [];
|
|
4664
|
+
try {
|
|
4665
|
+
let simulated = deepClone(doc);
|
|
4666
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
4667
|
+
const step = registry.getStep(path[i], path[i + 1]);
|
|
4668
|
+
if (!step) break;
|
|
4669
|
+
simulated = step.migrate(simulated, { warn: (w) => simulatedWarnings.push(w) });
|
|
4670
|
+
}
|
|
4671
|
+
} catch {
|
|
4672
|
+
}
|
|
4213
4673
|
return {
|
|
4214
4674
|
success: true,
|
|
4215
4675
|
diagnostic: {
|
|
@@ -4218,12 +4678,15 @@ function migrateDocument(rawDocument, options = {}) {
|
|
|
4218
4678
|
targetVersion,
|
|
4219
4679
|
migrationPath: path,
|
|
4220
4680
|
stepsApplied: path.length - 1,
|
|
4221
|
-
dryRun: true
|
|
4681
|
+
dryRun: true,
|
|
4682
|
+
...simulatedWarnings.length > 0 ? { warnings: simulatedWarnings } : {}
|
|
4222
4683
|
}
|
|
4223
4684
|
};
|
|
4224
4685
|
}
|
|
4225
4686
|
let currentDoc = deepClone(doc);
|
|
4226
4687
|
let stepsApplied = 0;
|
|
4688
|
+
const warnings = [];
|
|
4689
|
+
const stepContext = { warn: (w) => warnings.push(w) };
|
|
4227
4690
|
for (let i = 0; i < path.length - 1; i++) {
|
|
4228
4691
|
const fromVer = path[i];
|
|
4229
4692
|
const toVer = path[i + 1];
|
|
@@ -4249,7 +4712,7 @@ function migrateDocument(rawDocument, options = {}) {
|
|
|
4249
4712
|
};
|
|
4250
4713
|
}
|
|
4251
4714
|
try {
|
|
4252
|
-
currentDoc = step.migrate(currentDoc);
|
|
4715
|
+
currentDoc = step.migrate(currentDoc, stepContext);
|
|
4253
4716
|
stepsApplied++;
|
|
4254
4717
|
} catch (err) {
|
|
4255
4718
|
const error = {
|
|
@@ -4305,7 +4768,8 @@ function migrateDocument(rawDocument, options = {}) {
|
|
|
4305
4768
|
targetVersion,
|
|
4306
4769
|
migrationPath: path,
|
|
4307
4770
|
stepsApplied,
|
|
4308
|
-
dryRun: false
|
|
4771
|
+
dryRun: false,
|
|
4772
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
4309
4773
|
}
|
|
4310
4774
|
};
|
|
4311
4775
|
}
|
|
@@ -4340,7 +4804,7 @@ async function preflightPackage(archiveData, options = {}) {
|
|
|
4340
4804
|
...DEFAULT_SECURITY_LIMITS,
|
|
4341
4805
|
...options.securityLimits
|
|
4342
4806
|
};
|
|
4343
|
-
const targetVersion = options.targetSchemaVersion ||
|
|
4807
|
+
const targetVersion = options.targetSchemaVersion || import_schema10.CURRENT_SCHEMA_VERSION;
|
|
4344
4808
|
const migrationRegistry = options.migrationRegistry || defaultMigrationRegistry;
|
|
4345
4809
|
const diagnostics = [];
|
|
4346
4810
|
let dependencyPolicy = options.dependencyPolicy || "cancel";
|
|
@@ -4473,7 +4937,7 @@ async function preflightPackage(archiveData, options = {}) {
|
|
|
4473
4937
|
path: manifestProtoCheck.path
|
|
4474
4938
|
});
|
|
4475
4939
|
}
|
|
4476
|
-
const parseResult =
|
|
4940
|
+
const parseResult = import_schema10.ManifestSchema.safeParse(manifestJson);
|
|
4477
4941
|
if (!parseResult.success) {
|
|
4478
4942
|
const issues = parseResult.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ");
|
|
4479
4943
|
diagnostics.push({
|
|
@@ -4503,6 +4967,16 @@ async function preflightPackage(archiveData, options = {}) {
|
|
|
4503
4967
|
let rawPageDoc;
|
|
4504
4968
|
try {
|
|
4505
4969
|
rawPageDoc = JSON.parse((0, import_fflate2.strFromU8)(pageEntry));
|
|
4970
|
+
const secretScan = sanitizeDocumentTracking(rawPageDoc);
|
|
4971
|
+
if (secretScan.removed.length > 0) {
|
|
4972
|
+
diagnostics.push({
|
|
4973
|
+
code: "TRACKING_SECRET_REMOVED",
|
|
4974
|
+
severity: "warning",
|
|
4975
|
+
message: "Tracking secrets found in page.json will be removed on import; re-link credentials via credentialId.",
|
|
4976
|
+
path: "page.json",
|
|
4977
|
+
details: { paths: secretScan.removed }
|
|
4978
|
+
});
|
|
4979
|
+
}
|
|
4506
4980
|
const pageProtoCheck = containsProhibitedKeys(rawPageDoc);
|
|
4507
4981
|
if (pageProtoCheck.found) {
|
|
4508
4982
|
diagnostics.push({
|
|
@@ -4749,7 +5223,7 @@ function buildReport(valid, canImport, fields) {
|
|
|
4749
5223
|
return {
|
|
4750
5224
|
valid,
|
|
4751
5225
|
canImport,
|
|
4752
|
-
targetVersion: fields.targetVersion ||
|
|
5226
|
+
targetVersion: fields.targetVersion || import_schema10.CURRENT_SCHEMA_VERSION,
|
|
4753
5227
|
requiresMigration: fields.requiresMigration || false,
|
|
4754
5228
|
missingComponents: fields.missingComponents || [],
|
|
4755
5229
|
missingCapabilities: fields.missingCapabilities || [],
|
|
@@ -4822,7 +5296,7 @@ async function importPackage(archiveData, options = {}) {
|
|
|
4822
5296
|
if (preflight.requiresMigration) {
|
|
4823
5297
|
const migrationRegistry = options.migrationRegistry || defaultMigrationRegistry;
|
|
4824
5298
|
const migrationRes = migrateDocument(pageRaw, {
|
|
4825
|
-
targetVersion: options.targetSchemaVersion ||
|
|
5299
|
+
targetVersion: options.targetSchemaVersion || import_schema10.CURRENT_SCHEMA_VERSION,
|
|
4826
5300
|
registry: migrationRegistry,
|
|
4827
5301
|
validate: true
|
|
4828
5302
|
});
|
|
@@ -4844,13 +5318,24 @@ async function importPackage(archiveData, options = {}) {
|
|
|
4844
5318
|
} else {
|
|
4845
5319
|
finalDocument = pageRaw;
|
|
4846
5320
|
}
|
|
5321
|
+
const importWarnings = [];
|
|
5322
|
+
const strippedSecrets = stripDocumentTrackingSecretsInPlace(finalDocument);
|
|
5323
|
+
if (strippedSecrets.length > 0) {
|
|
5324
|
+
importWarnings.push({
|
|
5325
|
+
code: "TRACKING_SECRET_REMOVED",
|
|
5326
|
+
severity: "warning",
|
|
5327
|
+
message: "Tracking secret removed from imported document; re-link credential via credentialId.",
|
|
5328
|
+
path: "page.json",
|
|
5329
|
+
details: { paths: strippedSecrets }
|
|
5330
|
+
});
|
|
5331
|
+
}
|
|
4847
5332
|
let metadata = finalDocument.metadata || {
|
|
4848
5333
|
title: "Imported Page",
|
|
4849
5334
|
description: "",
|
|
4850
5335
|
author: "",
|
|
4851
5336
|
tags: [],
|
|
4852
5337
|
category: "general",
|
|
4853
|
-
version: finalDocument.version ||
|
|
5338
|
+
version: finalDocument.version || import_schema10.CURRENT_SCHEMA_VERSION
|
|
4854
5339
|
};
|
|
4855
5340
|
if (unzipped["metadata.json"]) {
|
|
4856
5341
|
try {
|
|
@@ -5002,13 +5487,26 @@ async function importPackage(archiveData, options = {}) {
|
|
|
5002
5487
|
metadata,
|
|
5003
5488
|
extractedAssets,
|
|
5004
5489
|
renamedAssets: Object.keys(renameMap).length > 0 ? renameMap : void 0,
|
|
5005
|
-
preflight
|
|
5490
|
+
preflight,
|
|
5491
|
+
...importWarnings.length > 0 ? { warnings: importWarnings } : {}
|
|
5006
5492
|
};
|
|
5007
5493
|
}
|
|
5008
5494
|
var importStoraPackage = importPackage;
|
|
5009
5495
|
|
|
5010
5496
|
// src/io/load-project.ts
|
|
5011
|
-
var
|
|
5497
|
+
var import_schema11 = require("@kubuild/schema");
|
|
5498
|
+
function migrateProjectArtboards(project) {
|
|
5499
|
+
let changed = false;
|
|
5500
|
+
const artboards = project.artboards.map((artboard) => {
|
|
5501
|
+
const version = artboard.document.version;
|
|
5502
|
+
if (version === import_schema11.CURRENT_SCHEMA_VERSION || !canMigrate(version)) return artboard;
|
|
5503
|
+
const migration = migrateDocument(artboard.document);
|
|
5504
|
+
if (!migration.success || !migration.document) return artboard;
|
|
5505
|
+
changed = true;
|
|
5506
|
+
return { ...artboard, document: migration.document };
|
|
5507
|
+
});
|
|
5508
|
+
return changed ? { ...project, artboards } : project;
|
|
5509
|
+
}
|
|
5012
5510
|
var DEFAULT_PAGE_ARTBOARD_ID = "artboard-page-1";
|
|
5013
5511
|
function wrapPageDocumentAsProject(document, options = {}) {
|
|
5014
5512
|
const artboardId = options.artboardId ?? DEFAULT_PAGE_ARTBOARD_ID;
|
|
@@ -5021,8 +5519,8 @@ function wrapPageDocumentAsProject(document, options = {}) {
|
|
|
5021
5519
|
...options.width !== void 0 ? { width: options.width } : {}
|
|
5022
5520
|
};
|
|
5023
5521
|
return {
|
|
5024
|
-
schema:
|
|
5025
|
-
version:
|
|
5522
|
+
schema: import_schema11.PROJECT_SCHEMA_NAME,
|
|
5523
|
+
version: import_schema11.CURRENT_PROJECT_SCHEMA_VERSION,
|
|
5026
5524
|
metadata: document.metadata,
|
|
5027
5525
|
artboards: [artboard],
|
|
5028
5526
|
activeArtboardId: artboardId
|
|
@@ -5041,8 +5539,8 @@ function loadProjectDocument(raw) {
|
|
|
5041
5539
|
]
|
|
5042
5540
|
};
|
|
5043
5541
|
}
|
|
5044
|
-
if ((0,
|
|
5045
|
-
const parsed =
|
|
5542
|
+
if ((0, import_schema11.looksLikeProjectDocument)(raw)) {
|
|
5543
|
+
const parsed = import_schema11.ProjectDocumentSchema.safeParse(raw);
|
|
5046
5544
|
if (!parsed.success) {
|
|
5047
5545
|
return {
|
|
5048
5546
|
success: false,
|
|
@@ -5058,7 +5556,7 @@ function loadProjectDocument(raw) {
|
|
|
5058
5556
|
}
|
|
5059
5557
|
return {
|
|
5060
5558
|
success: true,
|
|
5061
|
-
project: parsed.data,
|
|
5559
|
+
project: migrateProjectArtboards(parsed.data),
|
|
5062
5560
|
wrappedFromLegacyPage: false,
|
|
5063
5561
|
errors: []
|
|
5064
5562
|
};
|
|
@@ -5527,21 +6025,10 @@ function compareManifestsSemantically(expected, actual, options = {}) {
|
|
|
5527
6025
|
}
|
|
5528
6026
|
|
|
5529
6027
|
// src/io/template-utils.ts
|
|
5530
|
-
var
|
|
5531
|
-
var CORE_BUILTIN_COMPONENTS =
|
|
5532
|
-
"page",
|
|
5533
|
-
"section",
|
|
5534
|
-
"container",
|
|
5535
|
-
"columns",
|
|
5536
|
-
"column",
|
|
5537
|
-
"heading",
|
|
5538
|
-
"text",
|
|
5539
|
-
"image",
|
|
5540
|
-
"button",
|
|
5541
|
-
"collection"
|
|
5542
|
-
]);
|
|
6028
|
+
var import_schema12 = require("@kubuild/schema");
|
|
6029
|
+
var CORE_BUILTIN_COMPONENTS = new Set(import_schema12.BUILTIN_COMPONENT_TYPES);
|
|
5543
6030
|
function validateTemplate(value) {
|
|
5544
|
-
const parseResult =
|
|
6031
|
+
const parseResult = import_schema12.TemplateRecordSchema.safeParse(value);
|
|
5545
6032
|
if (parseResult.success) {
|
|
5546
6033
|
return {
|
|
5547
6034
|
valid: true,
|
|
@@ -5650,26 +6137,10 @@ function saveDraftAsTemplate(draft, metadata, options = {}) {
|
|
|
5650
6137
|
custom: metadata.custom ? deepClone(metadata.custom) : void 0
|
|
5651
6138
|
});
|
|
5652
6139
|
}
|
|
5653
|
-
function cloneTreeWithFreshIds(root, idGen) {
|
|
5654
|
-
function cloneRec(node) {
|
|
5655
|
-
const newId = idGen(node.id, node);
|
|
5656
|
-
const clonedProps = node.props ? deepClone(node.props) : void 0;
|
|
5657
|
-
const clonedStyles = node.styles ? deepClone(node.styles) : void 0;
|
|
5658
|
-
const clonedChildren = node.children ? node.children.map((child) => cloneRec(child)) : [];
|
|
5659
|
-
return {
|
|
5660
|
-
id: newId,
|
|
5661
|
-
type: node.type,
|
|
5662
|
-
...clonedProps ? { props: clonedProps } : {},
|
|
5663
|
-
...clonedStyles ? { styles: clonedStyles } : {},
|
|
5664
|
-
children: clonedChildren
|
|
5665
|
-
};
|
|
5666
|
-
}
|
|
5667
|
-
return cloneRec(root);
|
|
5668
|
-
}
|
|
5669
6140
|
function cloneTemplateAsPage(templateOrDoc, options = {}) {
|
|
5670
6141
|
let sourceDoc;
|
|
5671
6142
|
let templateOrigin = null;
|
|
5672
|
-
if ((0,
|
|
6143
|
+
if ((0, import_schema12.isTemplateRecord)(templateOrDoc)) {
|
|
5673
6144
|
if (!templateOrDoc.document) {
|
|
5674
6145
|
throw new Error(
|
|
5675
6146
|
`Template "${templateOrDoc.name}" (${templateOrDoc.id}) does not contain an inline document snapshot to clone from.`
|
|
@@ -5686,7 +6157,7 @@ function cloneTemplateAsPage(templateOrDoc, options = {}) {
|
|
|
5686
6157
|
} else {
|
|
5687
6158
|
throw new Error("Invalid input: expected a TemplateRecord or PageDocument.");
|
|
5688
6159
|
}
|
|
5689
|
-
const existingIdsInSource = new Set((0,
|
|
6160
|
+
const existingIdsInSource = new Set((0, import_schema12.collectNodeIds)(sourceDoc.document));
|
|
5690
6161
|
const assignedNewIds = /* @__PURE__ */ new Set();
|
|
5691
6162
|
const prefix = options.idPrefix || `page_${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 6)}`;
|
|
5692
6163
|
let counter = 1;
|
|
@@ -5701,7 +6172,7 @@ function cloneTemplateAsPage(templateOrDoc, options = {}) {
|
|
|
5701
6172
|
return candidate;
|
|
5702
6173
|
};
|
|
5703
6174
|
const idGen = options.idGenerator || defaultIdGen;
|
|
5704
|
-
const clonedRootNode =
|
|
6175
|
+
const { clonedNode: clonedRootNode } = cloneNodeTreeWithFreshIds(sourceDoc.document, idGen);
|
|
5705
6176
|
const rootPageNode = {
|
|
5706
6177
|
...clonedRootNode,
|
|
5707
6178
|
type: "page"
|
|
@@ -5735,8 +6206,8 @@ function cloneTemplateAsPage(templateOrDoc, options = {}) {
|
|
|
5735
6206
|
}
|
|
5736
6207
|
function areNodeIdsCompletelyDistinct(docA, docB) {
|
|
5737
6208
|
if (!docA?.document || !docB?.document) return true;
|
|
5738
|
-
const idsA = new Set((0,
|
|
5739
|
-
const idsB = (0,
|
|
6209
|
+
const idsA = new Set((0, import_schema12.collectNodeIds)(docA.document));
|
|
6210
|
+
const idsB = (0, import_schema12.collectNodeIds)(docB.document);
|
|
5740
6211
|
for (const id of idsB) {
|
|
5741
6212
|
if (idsA.has(id)) {
|
|
5742
6213
|
return false;
|
|
@@ -5746,7 +6217,7 @@ function areNodeIdsCompletelyDistinct(docA, docB) {
|
|
|
5746
6217
|
}
|
|
5747
6218
|
|
|
5748
6219
|
// src/validation/project-validator.ts
|
|
5749
|
-
var
|
|
6220
|
+
var import_schema13 = require("@kubuild/schema");
|
|
5750
6221
|
function walk(node, visit) {
|
|
5751
6222
|
visit(node);
|
|
5752
6223
|
for (const child of node.children ?? []) {
|
|
@@ -5756,7 +6227,7 @@ function walk(node, visit) {
|
|
|
5756
6227
|
function collectArtboardReferences(artboard) {
|
|
5757
6228
|
const refs = [];
|
|
5758
6229
|
walk(artboard.document.document, (node) => {
|
|
5759
|
-
if (node.type ===
|
|
6230
|
+
if (node.type === import_schema13.ARTBOARD_REFERENCE_NODE_TYPE) {
|
|
5760
6231
|
const target = node.props?.artboardId;
|
|
5761
6232
|
refs.push({
|
|
5762
6233
|
nodeId: node.id,
|
|
@@ -5783,7 +6254,7 @@ function validateProject(input, options = {}) {
|
|
|
5783
6254
|
artboardErrors
|
|
5784
6255
|
};
|
|
5785
6256
|
}
|
|
5786
|
-
const parsed =
|
|
6257
|
+
const parsed = import_schema13.ProjectDocumentSchema.safeParse(input);
|
|
5787
6258
|
if (!parsed.success) {
|
|
5788
6259
|
return {
|
|
5789
6260
|
valid: false,
|
|
@@ -5791,7 +6262,7 @@ function validateProject(input, options = {}) {
|
|
|
5791
6262
|
errors: [
|
|
5792
6263
|
{
|
|
5793
6264
|
code: "PROJECT_SCHEMA_INVALID",
|
|
5794
|
-
message: `Project failed schema validation. Expected schema "${
|
|
6265
|
+
message: `Project failed schema validation. Expected schema "${import_schema13.PROJECT_SCHEMA_NAME}".`,
|
|
5795
6266
|
path: "",
|
|
5796
6267
|
details: { issues: parsed.error.issues }
|
|
5797
6268
|
}
|
|
@@ -5884,7 +6355,7 @@ function validateProject(input, options = {}) {
|
|
|
5884
6355
|
function collectArtboardReferenceNodes(root) {
|
|
5885
6356
|
const refs = [];
|
|
5886
6357
|
walk(root, (node) => {
|
|
5887
|
-
if (node.type ===
|
|
6358
|
+
if (node.type === import_schema13.ARTBOARD_REFERENCE_NODE_TYPE) {
|
|
5888
6359
|
const target = node.props?.artboardId;
|
|
5889
6360
|
refs.push({ nodeId: node.id, artboardId: typeof target === "string" ? target : void 0 });
|
|
5890
6361
|
}
|
|
@@ -6109,6 +6580,7 @@ function isFormValid(errors) {
|
|
|
6109
6580
|
DocumentHistoryManager,
|
|
6110
6581
|
HistoryEngine,
|
|
6111
6582
|
MigrationRegistry,
|
|
6583
|
+
NODE_ID_REFERENCE_KEYS,
|
|
6112
6584
|
RuntimeStateStore,
|
|
6113
6585
|
addArtboard,
|
|
6114
6586
|
applyFieldTransform,
|
|
@@ -6116,7 +6588,9 @@ function isFormValid(errors) {
|
|
|
6116
6588
|
buildSampleVariablesFromCatalog,
|
|
6117
6589
|
calculateChecksum,
|
|
6118
6590
|
canMigrate,
|
|
6591
|
+
canonicalizeTextPropsInPlace,
|
|
6119
6592
|
checkZipBomb,
|
|
6593
|
+
cloneNodeTreeWithFreshIds,
|
|
6120
6594
|
cloneTemplateAsPage,
|
|
6121
6595
|
cloneTreeWithNewIds,
|
|
6122
6596
|
collectArtboardReferenceNodes,
|
|
@@ -6133,6 +6607,7 @@ function isFormValid(errors) {
|
|
|
6133
6607
|
createPageArtboard,
|
|
6134
6608
|
createRuntimeStore,
|
|
6135
6609
|
createTemplateRecord,
|
|
6610
|
+
createTrackingRelayHandler,
|
|
6136
6611
|
deepClone,
|
|
6137
6612
|
defaultIdGenerator,
|
|
6138
6613
|
defaultMigrationRegistry,
|
|
@@ -6192,6 +6667,7 @@ function isFormValid(errors) {
|
|
|
6192
6667
|
previewImportPackage,
|
|
6193
6668
|
remapAssetReferences,
|
|
6194
6669
|
remapDocumentAssetReferences,
|
|
6670
|
+
remapNodeReferences,
|
|
6195
6671
|
removeArtboard,
|
|
6196
6672
|
removeNode,
|
|
6197
6673
|
renameArtboard,
|
|
@@ -6199,6 +6675,7 @@ function isFormValid(errors) {
|
|
|
6199
6675
|
resolveBinding,
|
|
6200
6676
|
resolveBindingValue,
|
|
6201
6677
|
resolvePropertyPath,
|
|
6678
|
+
sanitizeDocumentTracking,
|
|
6202
6679
|
sanitizeFilename,
|
|
6203
6680
|
sanitizeHtml,
|
|
6204
6681
|
sanitizeUrl,
|
|
@@ -6209,6 +6686,8 @@ function isFormValid(errors) {
|
|
|
6209
6686
|
sendTikTokEventsApi,
|
|
6210
6687
|
setActiveArtboard,
|
|
6211
6688
|
sha256Sync,
|
|
6689
|
+
stripDocumentTrackingSecretsInPlace,
|
|
6690
|
+
stripTrackingSecretsInPlace,
|
|
6212
6691
|
ungroupNodeFrame,
|
|
6213
6692
|
updateActions,
|
|
6214
6693
|
updateAnimation,
|
|
@@ -6216,6 +6695,7 @@ function isFormValid(errors) {
|
|
|
6216
6695
|
updateFormConfig,
|
|
6217
6696
|
updateProps,
|
|
6218
6697
|
updateStyle,
|
|
6698
|
+
updateTheme,
|
|
6219
6699
|
validateDocument,
|
|
6220
6700
|
validateDocumentSecurity,
|
|
6221
6701
|
validateFieldValue,
|