@kubuild/core 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -316,26 +316,84 @@ function defaultIdGenerator(oldId, existingIds) {
316
316
  }
317
317
  return candidate;
318
318
  }
319
- function cloneTreeWithNewIds(root, idGenerator, existingIds) {
319
+ var NODE_ID_REFERENCE_KEYS = /* @__PURE__ */ new Set([
320
+ "modalId",
321
+ "modalNodeId",
322
+ "targetModalId",
323
+ "targetNodeId",
324
+ "nodeId",
325
+ "formId",
326
+ "targetId"
327
+ ]);
328
+ var ANCHOR_REFERENCE_KEYS = /* @__PURE__ */ new Set(["url", "href"]);
329
+ function remapReferenceValue(key, value, idMap) {
330
+ if (typeof value !== "string") return value;
331
+ if (NODE_ID_REFERENCE_KEYS.has(key)) {
332
+ return idMap.get(value) ?? idMap.get(value.trim()) ?? value;
333
+ }
334
+ if (ANCHOR_REFERENCE_KEYS.has(key) && value.startsWith("#")) {
335
+ const mapped = idMap.get(value.slice(1));
336
+ return mapped ? `#${mapped}` : value;
337
+ }
338
+ return value;
339
+ }
340
+ function remapReferenceRecord(record, idMap) {
341
+ for (const [key, value] of Object.entries(record)) {
342
+ record[key] = remapReferenceValue(key, value, idMap);
343
+ }
344
+ }
345
+ function remapStepReferences(steps, idMap) {
346
+ if (!Array.isArray(steps)) return;
347
+ for (const step of steps) {
348
+ if (step.payload && typeof step.payload === "object") {
349
+ remapReferenceRecord(step.payload, idMap);
350
+ }
351
+ remapStepReferences(step.onSuccess, idMap);
352
+ remapStepReferences(step.onError, idMap);
353
+ }
354
+ }
355
+ function remapNodeReferences(node, idMap) {
356
+ if (node.props && typeof node.props === "object") {
357
+ remapReferenceRecord(node.props, idMap);
358
+ const legacyAction = node.props.action;
359
+ if (legacyAction && typeof legacyAction === "object" && legacyAction.payload && typeof legacyAction.payload === "object" && !Array.isArray(legacyAction.payload)) {
360
+ remapReferenceRecord(legacyAction.payload, idMap);
361
+ }
362
+ }
363
+ if (node.formConfig && typeof node.formConfig.formId === "string") {
364
+ node.formConfig.formId = idMap.get(node.formConfig.formId) ?? node.formConfig.formId;
365
+ }
366
+ if (Array.isArray(node.actions)) {
367
+ for (const pipeline of node.actions) {
368
+ remapStepReferences(pipeline.steps, idMap);
369
+ }
370
+ }
371
+ }
372
+ function cloneNodeTreeWithFreshIds(root, idGen) {
320
373
  const idMap = /* @__PURE__ */ new Map();
321
- const idGen = idGenerator || ((oldId) => defaultIdGenerator(oldId, existingIds));
374
+ const clonedNodes = [];
322
375
  function cloneRecursive(node) {
323
- const newId = idGen(node.id);
376
+ const newId = idGen(node.id, node);
324
377
  idMap.set(node.id, newId);
325
- const clonedProps = node.props ? deepClone(node.props) : void 0;
326
- const clonedStyles = node.styles ? deepClone(node.styles) : void 0;
327
- const clonedChildren = node.children ? node.children.map((child) => cloneRecursive(child)) : [];
328
- return {
378
+ const { children, ...rest } = node;
379
+ const cloned = {
380
+ ...deepClone(rest),
329
381
  id: newId,
330
- type: node.type,
331
- ...clonedProps ? { props: clonedProps } : {},
332
- ...clonedStyles ? { styles: clonedStyles } : {},
333
- children: clonedChildren
382
+ children: children ? children.map((child) => cloneRecursive(child)) : []
334
383
  };
384
+ clonedNodes.push(cloned);
385
+ return cloned;
335
386
  }
336
387
  const clonedNode = cloneRecursive(root);
388
+ for (const cloned of clonedNodes) {
389
+ remapNodeReferences(cloned, idMap);
390
+ }
337
391
  return { clonedNode, idMap };
338
392
  }
393
+ function cloneTreeWithNewIds(root, idGenerator, existingIds) {
394
+ const idGen = idGenerator || ((oldId) => defaultIdGenerator(oldId, existingIds));
395
+ return cloneNodeTreeWithFreshIds(root, (oldId) => idGen(oldId));
396
+ }
339
397
 
340
398
  // src/document/commands.ts
341
399
  import {
@@ -343,7 +401,9 @@ import {
343
401
  ResponsiveStylesSchema,
344
402
  AnimationConfigSchema,
345
403
  ActionPipelineSchema,
346
- FormConfigSchema
404
+ FormConfigSchema,
405
+ ThemeSchema,
406
+ THEME_TOKEN_GROUPS
347
407
  } from "@kubuild/schema";
348
408
  function insertNode(document, params) {
349
409
  const { parentId, node, index } = params;
@@ -883,6 +943,53 @@ function replaceNode(document, params) {
883
943
  }
884
944
  };
885
945
  }
946
+ function updateTheme(document, params) {
947
+ const { theme, merge = true } = params;
948
+ const newDoc = deepClone(document);
949
+ const previousTheme = newDoc.theme ? deepClone(newDoc.theme) : void 0;
950
+ if (theme === null) {
951
+ delete newDoc.theme;
952
+ } else {
953
+ const next = {};
954
+ for (const group of THEME_TOKEN_GROUPS) {
955
+ const base = merge ? { ...previousTheme?.[group] ?? {} } : {};
956
+ const patch = theme[group];
957
+ if (patch === null) {
958
+ continue;
959
+ }
960
+ if (patch) {
961
+ for (const [key, value] of Object.entries(patch)) {
962
+ if (value === null) {
963
+ delete base[key];
964
+ } else {
965
+ base[key] = value;
966
+ }
967
+ }
968
+ }
969
+ if (Object.keys(base).length > 0) {
970
+ next[group] = base;
971
+ }
972
+ }
973
+ const parsed = ThemeSchema.parse(next);
974
+ if (Object.keys(parsed).length > 0) {
975
+ newDoc.theme = parsed;
976
+ } else {
977
+ delete newDoc.theme;
978
+ }
979
+ }
980
+ return {
981
+ document: newDoc,
982
+ event: {
983
+ type: "THEME_UPDATED",
984
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
985
+ nodeId: newDoc.document.id,
986
+ payload: {
987
+ theme: newDoc.theme,
988
+ previousTheme
989
+ }
990
+ }
991
+ };
992
+ }
886
993
 
887
994
  // src/document/artboards.ts
888
995
  import {
@@ -4094,7 +4201,8 @@ import {
4094
4201
  // src/io/migration.ts
4095
4202
  import {
4096
4203
  CURRENT_SCHEMA_VERSION as CURRENT_SCHEMA_VERSION4,
4097
- SCHEMA_NAME as SCHEMA_NAME5
4204
+ SCHEMA_NAME as SCHEMA_NAME5,
4205
+ getCanonicalTextPropRule
4098
4206
  } from "@kubuild/schema";
4099
4207
  var MigrationRegistry = class {
4100
4208
  steps = /* @__PURE__ */ new Map();
@@ -4267,6 +4375,56 @@ defaultMigrationRegistry.register({
4267
4375
  return migrated;
4268
4376
  }
4269
4377
  });
4378
+ function canonicalizeTextPropsInPlace(root, basePath = "document") {
4379
+ const changed = [];
4380
+ const walk2 = (value, path) => {
4381
+ if (!value || typeof value !== "object" || Array.isArray(value)) return;
4382
+ const node = value;
4383
+ const props = node.props;
4384
+ const rule = typeof node.type === "string" ? getCanonicalTextPropRule(node.type) : void 0;
4385
+ if (rule && props && typeof props === "object" && !Array.isArray(props)) {
4386
+ const record = props;
4387
+ const presentAliases = rule.aliases.filter((alias) => record[alias] !== void 0);
4388
+ if (presentAliases.length > 0) {
4389
+ const displayedKey = rule.legacyReadOrder.find((key) => record[key] !== void 0);
4390
+ if (node.type === "text" && displayedKey === "content" && record.as === void 0 && record.tag === void 0) {
4391
+ record.as = "p";
4392
+ }
4393
+ if (displayedKey !== void 0 && displayedKey !== rule.canonical) {
4394
+ record[rule.canonical] = record[displayedKey];
4395
+ }
4396
+ for (const alias of presentAliases) {
4397
+ delete record[alias];
4398
+ changed.push(`${path}.props.${alias}`);
4399
+ }
4400
+ }
4401
+ }
4402
+ if (Array.isArray(node.children)) {
4403
+ node.children.forEach((child, index) => walk2(child, `${path}.children.${index}`));
4404
+ }
4405
+ };
4406
+ walk2(root, basePath);
4407
+ return changed;
4408
+ }
4409
+ defaultMigrationRegistry.register({
4410
+ fromVersion: "1.1.0",
4411
+ toVersion: "1.2.0",
4412
+ description: "Rename deprecated text prop aliases to canonical names (heading/text/paragraph/link/badge/blockquote -> text, button -> label)",
4413
+ migrate: (rawDoc, context) => {
4414
+ const migrated = deepClone(rawDoc);
4415
+ migrated.version = "1.2.0";
4416
+ const changed = canonicalizeTextPropsInPlace(migrated.document);
4417
+ if (changed.length > 0) {
4418
+ context.warn({
4419
+ code: "PROP_ALIAS_MIGRATED",
4420
+ message: "Deprecated text prop aliases were renamed to their canonical names.",
4421
+ step: "1.1.0->1.2.0",
4422
+ paths: changed
4423
+ });
4424
+ }
4425
+ return migrated;
4426
+ }
4427
+ });
4270
4428
  function canMigrate(sourceVersion, targetVersion = CURRENT_SCHEMA_VERSION4, registry = defaultMigrationRegistry) {
4271
4429
  if (!sourceVersion || !targetVersion) return false;
4272
4430
  if (sourceVersion === targetVersion) return true;
@@ -4371,6 +4529,16 @@ function migrateDocument(rawDocument, options = {}) {
4371
4529
  };
4372
4530
  }
4373
4531
  if (dryRun) {
4532
+ const simulatedWarnings = [];
4533
+ try {
4534
+ let simulated = deepClone(doc);
4535
+ for (let i = 0; i < path.length - 1; i++) {
4536
+ const step = registry.getStep(path[i], path[i + 1]);
4537
+ if (!step) break;
4538
+ simulated = step.migrate(simulated, { warn: (w) => simulatedWarnings.push(w) });
4539
+ }
4540
+ } catch {
4541
+ }
4374
4542
  return {
4375
4543
  success: true,
4376
4544
  diagnostic: {
@@ -4379,7 +4547,8 @@ function migrateDocument(rawDocument, options = {}) {
4379
4547
  targetVersion,
4380
4548
  migrationPath: path,
4381
4549
  stepsApplied: path.length - 1,
4382
- dryRun: true
4550
+ dryRun: true,
4551
+ ...simulatedWarnings.length > 0 ? { warnings: simulatedWarnings } : {}
4383
4552
  }
4384
4553
  };
4385
4554
  }
@@ -5198,8 +5367,21 @@ import {
5198
5367
  ProjectDocumentSchema,
5199
5368
  PROJECT_SCHEMA_NAME as PROJECT_SCHEMA_NAME2,
5200
5369
  CURRENT_PROJECT_SCHEMA_VERSION as CURRENT_PROJECT_SCHEMA_VERSION2,
5370
+ CURRENT_SCHEMA_VERSION as CURRENT_SCHEMA_VERSION6,
5201
5371
  looksLikeProjectDocument
5202
5372
  } from "@kubuild/schema";
5373
+ function migrateProjectArtboards(project) {
5374
+ let changed = false;
5375
+ const artboards = project.artboards.map((artboard) => {
5376
+ const version = artboard.document.version;
5377
+ if (version === CURRENT_SCHEMA_VERSION6 || !canMigrate(version)) return artboard;
5378
+ const migration = migrateDocument(artboard.document);
5379
+ if (!migration.success || !migration.document) return artboard;
5380
+ changed = true;
5381
+ return { ...artboard, document: migration.document };
5382
+ });
5383
+ return changed ? { ...project, artboards } : project;
5384
+ }
5203
5385
  var DEFAULT_PAGE_ARTBOARD_ID = "artboard-page-1";
5204
5386
  function wrapPageDocumentAsProject(document, options = {}) {
5205
5387
  const artboardId = options.artboardId ?? DEFAULT_PAGE_ARTBOARD_ID;
@@ -5249,7 +5431,7 @@ function loadProjectDocument(raw) {
5249
5431
  }
5250
5432
  return {
5251
5433
  success: true,
5252
- project: parsed.data,
5434
+ project: migrateProjectArtboards(parsed.data),
5253
5435
  wrappedFromLegacyPage: false,
5254
5436
  errors: []
5255
5437
  };
@@ -5720,21 +5902,11 @@ function compareManifestsSemantically(expected, actual, options = {}) {
5720
5902
  // src/io/template-utils.ts
5721
5903
  import {
5722
5904
  TemplateRecordSchema,
5905
+ BUILTIN_COMPONENT_TYPES,
5723
5906
  collectNodeIds as collectNodeIds2,
5724
5907
  isTemplateRecord
5725
5908
  } from "@kubuild/schema";
5726
- var CORE_BUILTIN_COMPONENTS = /* @__PURE__ */ new Set([
5727
- "page",
5728
- "section",
5729
- "container",
5730
- "columns",
5731
- "column",
5732
- "heading",
5733
- "text",
5734
- "image",
5735
- "button",
5736
- "collection"
5737
- ]);
5909
+ var CORE_BUILTIN_COMPONENTS = new Set(BUILTIN_COMPONENT_TYPES);
5738
5910
  function validateTemplate(value) {
5739
5911
  const parseResult = TemplateRecordSchema.safeParse(value);
5740
5912
  if (parseResult.success) {
@@ -5845,22 +6017,6 @@ function saveDraftAsTemplate(draft, metadata, options = {}) {
5845
6017
  custom: metadata.custom ? deepClone(metadata.custom) : void 0
5846
6018
  });
5847
6019
  }
5848
- function cloneTreeWithFreshIds(root, idGen) {
5849
- function cloneRec(node) {
5850
- const newId = idGen(node.id, node);
5851
- const clonedProps = node.props ? deepClone(node.props) : void 0;
5852
- const clonedStyles = node.styles ? deepClone(node.styles) : void 0;
5853
- const clonedChildren = node.children ? node.children.map((child) => cloneRec(child)) : [];
5854
- return {
5855
- id: newId,
5856
- type: node.type,
5857
- ...clonedProps ? { props: clonedProps } : {},
5858
- ...clonedStyles ? { styles: clonedStyles } : {},
5859
- children: clonedChildren
5860
- };
5861
- }
5862
- return cloneRec(root);
5863
- }
5864
6020
  function cloneTemplateAsPage(templateOrDoc, options = {}) {
5865
6021
  let sourceDoc;
5866
6022
  let templateOrigin = null;
@@ -5896,7 +6052,7 @@ function cloneTemplateAsPage(templateOrDoc, options = {}) {
5896
6052
  return candidate;
5897
6053
  };
5898
6054
  const idGen = options.idGenerator || defaultIdGen;
5899
- const clonedRootNode = cloneTreeWithFreshIds(sourceDoc.document, idGen);
6055
+ const { clonedNode: clonedRootNode } = cloneNodeTreeWithFreshIds(sourceDoc.document, idGen);
5900
6056
  const rootPageNode = {
5901
6057
  ...clonedRootNode,
5902
6058
  type: "page"
@@ -6307,6 +6463,7 @@ export {
6307
6463
  DocumentHistoryManager,
6308
6464
  HistoryEngine,
6309
6465
  MigrationRegistry,
6466
+ NODE_ID_REFERENCE_KEYS,
6310
6467
  RuntimeStateStore,
6311
6468
  addArtboard,
6312
6469
  applyFieldTransform,
@@ -6314,7 +6471,9 @@ export {
6314
6471
  buildSampleVariablesFromCatalog,
6315
6472
  calculateChecksum,
6316
6473
  canMigrate,
6474
+ canonicalizeTextPropsInPlace,
6317
6475
  checkZipBomb,
6476
+ cloneNodeTreeWithFreshIds,
6318
6477
  cloneTemplateAsPage,
6319
6478
  cloneTreeWithNewIds,
6320
6479
  collectArtboardReferenceNodes,
@@ -6391,6 +6550,7 @@ export {
6391
6550
  previewImportPackage,
6392
6551
  remapAssetReferences,
6393
6552
  remapDocumentAssetReferences,
6553
+ remapNodeReferences,
6394
6554
  removeArtboard,
6395
6555
  removeNode,
6396
6556
  renameArtboard,
@@ -6418,6 +6578,7 @@ export {
6418
6578
  updateFormConfig,
6419
6579
  updateProps,
6420
6580
  updateStyle,
6581
+ updateTheme,
6421
6582
  validateDocument,
6422
6583
  validateDocumentSecurity,
6423
6584
  validateFieldValue,