meno-core 1.0.47 → 1.0.49

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.
Files changed (97) hide show
  1. package/build-astro.ts +2 -2
  2. package/dist/build-static.js +7 -7
  3. package/dist/chunks/{chunk-UUA5LEWF.js → chunk-6IVUG7FY.js} +138 -7
  4. package/dist/chunks/chunk-6IVUG7FY.js.map +7 -0
  5. package/dist/chunks/{chunk-XSWR3QLI.js → chunk-AZQYF6KE.js} +261 -130
  6. package/dist/chunks/chunk-AZQYF6KE.js.map +7 -0
  7. package/dist/chunks/{chunk-47UNLQUU.js → chunk-CHD5UCFF.js} +57 -12
  8. package/dist/chunks/chunk-CHD5UCFF.js.map +7 -0
  9. package/dist/chunks/{chunk-FGUZOYJX.js → chunk-EQYDSPBB.js} +435 -131
  10. package/dist/chunks/chunk-EQYDSPBB.js.map +7 -0
  11. package/dist/chunks/{chunk-IF3RATBY.js → chunk-H4JSCDNW.js} +2 -2
  12. package/dist/chunks/{chunk-KITQJYZV.js → chunk-J23ZX5AP.js} +40 -4
  13. package/dist/chunks/chunk-J23ZX5AP.js.map +7 -0
  14. package/dist/chunks/{chunk-LJFB5EBT.js → chunk-JER5NQVM.js} +5 -5
  15. package/dist/chunks/{chunk-ZTKHJQ2Z.js → chunk-KPU2XHOS.js} +5 -2
  16. package/dist/chunks/{chunk-ZTKHJQ2Z.js.map → chunk-KPU2XHOS.js.map} +2 -2
  17. package/dist/chunks/{chunk-BCLGRZ3U.js → chunk-LKAGAQ3M.js} +2 -2
  18. package/dist/chunks/{chunk-FED5MME6.js → chunk-S2CX6HFM.js} +262 -26
  19. package/dist/chunks/chunk-S2CX6HFM.js.map +7 -0
  20. package/dist/chunks/{configService-DYCUEURL.js → configService-CCA6AIDI.js} +3 -3
  21. package/dist/entries/server-router.js +9 -9
  22. package/dist/entries/server-router.js.map +2 -2
  23. package/dist/lib/client/index.js +64 -20
  24. package/dist/lib/client/index.js.map +3 -3
  25. package/dist/lib/server/index.js +1737 -296
  26. package/dist/lib/server/index.js.map +4 -4
  27. package/dist/lib/shared/index.js +50 -10
  28. package/dist/lib/shared/index.js.map +3 -3
  29. package/entries/server-router.tsx +6 -2
  30. package/lib/client/core/ComponentBuilder.test.ts +17 -0
  31. package/lib/client/core/ComponentBuilder.ts +25 -1
  32. package/lib/client/core/builders/embedBuilder.ts +15 -2
  33. package/lib/client/core/builders/linkNodeBuilder.ts +15 -2
  34. package/lib/client/core/builders/localeListBuilder.ts +17 -6
  35. package/lib/client/styles/StyleInjector.ts +3 -2
  36. package/lib/client/theme.ts +4 -4
  37. package/lib/server/cssGenerator.test.ts +64 -1
  38. package/lib/server/cssGenerator.ts +48 -9
  39. package/lib/server/index.ts +1 -1
  40. package/lib/server/jsonLoader.test.ts +0 -17
  41. package/lib/server/jsonLoader.ts +0 -81
  42. package/lib/server/providers/fileSystemCMSProvider.test.ts +163 -0
  43. package/lib/server/providers/fileSystemCMSProvider.ts +200 -11
  44. package/lib/server/routes/api/variables.ts +4 -2
  45. package/lib/server/routes/index.ts +1 -1
  46. package/lib/server/routes/pages.ts +23 -1
  47. package/lib/server/services/cmsService.test.ts +246 -0
  48. package/lib/server/services/cmsService.ts +122 -5
  49. package/lib/server/services/configService.ts +5 -0
  50. package/lib/server/ssr/attributeBuilder.ts +41 -0
  51. package/lib/server/ssr/htmlGenerator.test.ts +114 -2
  52. package/lib/server/ssr/htmlGenerator.ts +53 -6
  53. package/lib/server/ssr/liveReloadIntegration.test.ts +209 -0
  54. package/lib/server/ssr/ssrRenderer.test.ts +362 -1
  55. package/lib/server/ssr/ssrRenderer.ts +216 -72
  56. package/lib/server/utils/jsonLineMapper.test.ts +53 -1
  57. package/lib/server/utils/jsonLineMapper.ts +43 -3
  58. package/lib/server/webflow/buildWebflow.ts +343 -123
  59. package/lib/server/webflow/index.ts +1 -0
  60. package/lib/server/webflow/nodeToWebflow.test.ts +3170 -0
  61. package/lib/server/webflow/nodeToWebflow.ts +2141 -129
  62. package/lib/server/webflow/styleMapper.test.ts +389 -0
  63. package/lib/server/webflow/styleMapper.ts +517 -63
  64. package/lib/server/webflow/templateWrapper.ts +49 -0
  65. package/lib/server/webflow/types.ts +218 -18
  66. package/lib/shared/cssGeneration.test.ts +267 -1
  67. package/lib/shared/cssGeneration.ts +240 -18
  68. package/lib/shared/cssProperties.test.ts +247 -1
  69. package/lib/shared/cssProperties.ts +196 -6
  70. package/lib/shared/elementClassName.test.ts +15 -0
  71. package/lib/shared/elementClassName.ts +7 -3
  72. package/lib/shared/interfaces/contentProvider.ts +39 -6
  73. package/lib/shared/pathSecurity.ts +16 -0
  74. package/lib/shared/registry/nodeTypes/ListNodeType.ts +1 -1
  75. package/lib/shared/responsiveScaling.test.ts +143 -0
  76. package/lib/shared/responsiveScaling.ts +253 -2
  77. package/lib/shared/themeDefaults.test.ts +3 -3
  78. package/lib/shared/themeDefaults.ts +3 -3
  79. package/lib/shared/types/cms.ts +28 -3
  80. package/lib/shared/types/index.ts +2 -0
  81. package/lib/shared/types/variables.ts +37 -0
  82. package/lib/shared/utilityClassConfig.ts +3 -0
  83. package/lib/shared/utilityClassMapper.test.ts +123 -0
  84. package/lib/shared/utilityClassMapper.ts +179 -8
  85. package/lib/shared/validation/schemas.ts +15 -1
  86. package/lib/shared/validation/validators.ts +26 -1
  87. package/package.json +1 -1
  88. package/dist/chunks/chunk-47UNLQUU.js.map +0 -7
  89. package/dist/chunks/chunk-FED5MME6.js.map +0 -7
  90. package/dist/chunks/chunk-FGUZOYJX.js.map +0 -7
  91. package/dist/chunks/chunk-KITQJYZV.js.map +0 -7
  92. package/dist/chunks/chunk-UUA5LEWF.js.map +0 -7
  93. package/dist/chunks/chunk-XSWR3QLI.js.map +0 -7
  94. /package/dist/chunks/{chunk-IF3RATBY.js.map → chunk-H4JSCDNW.js.map} +0 -0
  95. /package/dist/chunks/{chunk-LJFB5EBT.js.map → chunk-JER5NQVM.js.map} +0 -0
  96. /package/dist/chunks/{chunk-BCLGRZ3U.js.map → chunk-LKAGAQ3M.js.map} +0 -0
  97. /package/dist/chunks/{configService-DYCUEURL.js.map → configService-CCA6AIDI.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  configService
3
- } from "./chunk-ZTKHJQ2Z.js";
3
+ } from "./chunk-KPU2XHOS.js";
4
4
  import {
5
5
  projectPaths,
6
6
  resolveProjectPath,
@@ -13,17 +13,20 @@ import {
13
13
  writeFile
14
14
  } from "./chunk-WQFG7PAH.js";
15
15
  import {
16
+ CMS_DRAFT_SUFFIX,
17
+ isReservedDraftFilename,
16
18
  isSafePathSegment,
17
19
  isValidIdentifier,
18
20
  resolvePaletteColor
19
- } from "./chunk-KITQJYZV.js";
21
+ } from "./chunk-J23ZX5AP.js";
20
22
  import {
21
23
  extractAttributesFromNode,
22
24
  isHtmlMapping,
25
+ mergeNodeStyles,
23
26
  processStructure,
24
27
  resolveHtmlMapping,
25
28
  skipEmptyTemplateAttributes
26
- } from "./chunk-BCLGRZ3U.js";
29
+ } from "./chunk-LKAGAQ3M.js";
27
30
  import {
28
31
  DEFAULT_PREFETCH_CONFIG,
29
32
  SSRRegistry,
@@ -60,21 +63,26 @@ import {
60
63
  resolveTemplateRawValue,
61
64
  responsiveStylesToClasses,
62
65
  singularize,
66
+ validateCMSDraftItem,
63
67
  validateCMSItem,
64
68
  validateComponentDefinition
65
- } from "./chunk-FED5MME6.js";
69
+ } from "./chunk-S2CX6HFM.js";
66
70
  import {
67
71
  DEFAULT_BREAKPOINTS,
72
+ DEFAULT_FLUID_RANGE,
68
73
  DEFAULT_I18N_CONFIG,
69
- DEFAULT_RESPONSIVE_SCALES,
74
+ DEFAULT_SITE_MARGIN,
75
+ buildFluidPropertyValue,
70
76
  buildLocalizedPath,
77
+ buildSiteMarginClamp,
71
78
  extractLocaleFromPath,
79
+ getSmallestBreakpointName,
72
80
  isI18nValue,
73
81
  migrateI18nConfig,
74
82
  normalizeBreakpointConfig,
75
83
  resolveI18nValue,
76
84
  scalePropertyValue
77
- } from "./chunk-XSWR3QLI.js";
85
+ } from "./chunk-AZQYF6KE.js";
78
86
  import {
79
87
  isTiptapDocument,
80
88
  tiptapToHtml
@@ -361,48 +369,6 @@ async function loadBreakpointConfig() {
361
369
  }
362
370
  return { ...DEFAULT_BREAKPOINTS };
363
371
  }
364
- function mergeScaleCategory(userScales, defaultScales) {
365
- if (!userScales && !defaultScales) return void 0;
366
- if (!userScales) return defaultScales ? { ...defaultScales } : void 0;
367
- if (!defaultScales) return { ...userScales };
368
- return {
369
- ...defaultScales,
370
- ...userScales
371
- };
372
- }
373
- async function loadResponsiveScalesConfig() {
374
- try {
375
- const configContent = await loadJSONFile(projectPaths.config());
376
- if (configContent) {
377
- const config = parseJSON(configContent);
378
- if (config.responsiveScales && typeof config.responsiveScales === "object") {
379
- const scales = {
380
- enabled: config.responsiveScales.enabled ?? DEFAULT_RESPONSIVE_SCALES.enabled,
381
- baseReference: config.responsiveScales.baseReference ?? DEFAULT_RESPONSIVE_SCALES.baseReference,
382
- fontSize: mergeScaleCategory(
383
- config.responsiveScales.fontSize,
384
- DEFAULT_RESPONSIVE_SCALES.fontSize
385
- ),
386
- padding: mergeScaleCategory(
387
- config.responsiveScales.padding,
388
- DEFAULT_RESPONSIVE_SCALES.padding
389
- ),
390
- margin: mergeScaleCategory(
391
- config.responsiveScales.margin,
392
- DEFAULT_RESPONSIVE_SCALES.margin
393
- ),
394
- gap: mergeScaleCategory(
395
- config.responsiveScales.gap,
396
- DEFAULT_RESPONSIVE_SCALES.gap
397
- )
398
- };
399
- return scales;
400
- }
401
- }
402
- } catch (error) {
403
- }
404
- return { ...DEFAULT_RESPONSIVE_SCALES };
405
- }
406
372
  async function loadI18nConfig() {
407
373
  try {
408
374
  const configContent = await loadJSONFile(projectPaths.config());
@@ -759,6 +725,7 @@ var CMSService = class {
759
725
  schemaCache = /* @__PURE__ */ new Map();
760
726
  routePatterns = [];
761
727
  provider;
728
+ previewMode;
762
729
  /** Item cache with TTL-based expiration */
763
730
  itemsCache = /* @__PURE__ */ new Map();
764
731
  /** Cache TTL in milliseconds (5 seconds) */
@@ -766,9 +733,11 @@ var CMSService = class {
766
733
  /**
767
734
  * Creates a new CMSService instance
768
735
  * @param provider - Optional CMSProvider for loading data (enables DI for testing)
736
+ * @param options - Service-level flags (preview mode for dev server)
769
737
  */
770
- constructor(provider) {
738
+ constructor(provider, options = {}) {
771
739
  this.provider = provider;
740
+ this.previewMode = options.previewMode === true;
772
741
  }
773
742
  /**
774
743
  * Set the CMS provider
@@ -779,9 +748,14 @@ var CMSService = class {
779
748
  this.provider = provider;
780
749
  }
781
750
  /**
782
- * Get items with caching
783
- * Returns cached items if available and not expired, otherwise fetches fresh data
784
- * Rich-text fields are automatically preprocessed to HTML for template interpolation
751
+ * Get items with caching, used by SSR-facing read methods.
752
+ *
753
+ * In preview mode (dev server) drafts are merged over published — drafts
754
+ * win on a per-`_filename` basis and draft-only items are included — so
755
+ * the editor preview reflects unpublished edits. Returns published-only
756
+ * otherwise. Rich-text fields are preprocessed to HTML for template
757
+ * interpolation either way.
758
+ *
785
759
  * @param collection - Collection ID to fetch items for
786
760
  * @returns Array of CMSItems with rich-text fields converted to HTML markers
787
761
  */
@@ -791,7 +765,20 @@ var CMSService = class {
791
765
  if (cached && now - cached.timestamp < this.ITEMS_CACHE_TTL) {
792
766
  return cached.items;
793
767
  }
794
- const rawItems = await this.provider.getItems(collection);
768
+ let rawItems = await this.provider.getItems(collection);
769
+ if (this.previewMode) {
770
+ const drafts = await this.provider.getAllDrafts(collection);
771
+ if (drafts.length > 0) {
772
+ const byFilename = /* @__PURE__ */ new Map();
773
+ for (const item of rawItems) {
774
+ if (item._filename) byFilename.set(item._filename, item);
775
+ }
776
+ for (const draft of drafts) {
777
+ if (draft._filename) byFilename.set(draft._filename, draft);
778
+ }
779
+ rawItems = Array.from(byFilename.values());
780
+ }
781
+ }
795
782
  const items = this.preprocessRichTextFields(collection, rawItems);
796
783
  this.itemsCache.set(collection, { items, timestamp: now });
797
784
  return items;
@@ -1029,6 +1016,71 @@ var CMSService = class {
1029
1016
  * Only clears items cache and provider cache before re-initializing.
1030
1017
  * Schema/route caches are swapped atomically inside initialize().
1031
1018
  */
1019
+ // ----------------------------------------------------------------------
1020
+ // Draft-version methods (Studio-only — never used by SSR / static export)
1021
+ // ----------------------------------------------------------------------
1022
+ /**
1023
+ * Load both published and draft versions of an item, used by the editor.
1024
+ * Returns `{}` when neither version exists.
1025
+ */
1026
+ async getItemVersions(collection, filename) {
1027
+ if (!this.provider) return {};
1028
+ const [published, draft] = await Promise.all([
1029
+ this.provider.getItemByFilename(collection, filename),
1030
+ this.provider.getDraft(collection, filename)
1031
+ ]);
1032
+ const result = {};
1033
+ if (published) result.published = published;
1034
+ if (draft) result.draft = draft;
1035
+ return result;
1036
+ }
1037
+ /**
1038
+ * List all items in a collection for the Studio item list:
1039
+ * - Published items annotated with `_hasDraft: true` when a draft sibling exists.
1040
+ * - Draft-only items (no published file yet) returned with `_isDraft: true`.
1041
+ */
1042
+ async listItemsWithDraftFlag(collection) {
1043
+ if (!this.provider) return [];
1044
+ const [published, drafts] = await Promise.all([
1045
+ this.provider.getItems(collection),
1046
+ this.provider.getAllDrafts(collection)
1047
+ ]);
1048
+ const draftFilenames = new Set(drafts.map((d) => d._filename).filter(Boolean));
1049
+ const annotatedPublished = published.map((item) => {
1050
+ if (item._filename && draftFilenames.has(item._filename)) {
1051
+ return { ...item, _hasDraft: true };
1052
+ }
1053
+ return item;
1054
+ });
1055
+ const publishedFilenames = new Set(published.map((i) => i._filename).filter(Boolean));
1056
+ const draftOnly = drafts.filter((d) => d._filename && !publishedFilenames.has(d._filename));
1057
+ return [...annotatedPublished, ...draftOnly];
1058
+ }
1059
+ /** Pass-through to provider; no caching (drafts are read fresh in the editor). */
1060
+ async getDraft(collection, filename) {
1061
+ if (!this.provider) return null;
1062
+ return this.provider.getDraft(collection, filename);
1063
+ }
1064
+ async hasDraft(collection, filename) {
1065
+ if (!this.provider) return false;
1066
+ return this.provider.hasDraft(collection, filename);
1067
+ }
1068
+ async saveDraft(collection, item) {
1069
+ if (!this.provider) throw new Error("CMS provider not configured");
1070
+ await this.provider.saveDraft(collection, item);
1071
+ this.itemsCache.delete(collection);
1072
+ }
1073
+ async discardDraft(collection, filename) {
1074
+ if (!this.provider) throw new Error("CMS provider not configured");
1075
+ await this.provider.discardDraft(collection, filename);
1076
+ this.itemsCache.delete(collection);
1077
+ }
1078
+ async publishDraft(collection, filename) {
1079
+ if (!this.provider) throw new Error("CMS provider not configured");
1080
+ const item = await this.provider.publishDraft(collection, filename);
1081
+ this.itemsCache.delete(collection);
1082
+ return item;
1083
+ }
1032
1084
  async refreshSchemas() {
1033
1085
  if (!this.provider) {
1034
1086
  return;
@@ -1337,20 +1389,45 @@ ${cssVars.join("\n")}
1337
1389
  return cssBlocks.join("\n\n");
1338
1390
  }
1339
1391
  function generateVariablesCSS(config, breakpoints, responsiveScales) {
1340
- if (!config.variables || config.variables.length === 0) {
1392
+ const cssBlocks = [];
1393
+ const fluidActive = responsiveScales?.enabled === true && responsiveScales?.mode === "fluid";
1394
+ const fluidRange = responsiveScales?.fluidRange ?? DEFAULT_FLUID_RANGE;
1395
+ const fluidBaseRef = responsiveScales?.baseReference || 16;
1396
+ const smallestBp = fluidActive ? getSmallestBreakpointName(breakpoints) : null;
1397
+ const baseVars = (config.variables ?? []).map((v) => {
1398
+ if (fluidActive && smallestBp && v.type !== "none") {
1399
+ const categoryScales = responsiveScales?.[v.type];
1400
+ const scale = categoryScales?.[smallestBp];
1401
+ if (scale != null && scale !== 1) {
1402
+ const fluid = buildFluidPropertyValue(
1403
+ v.value,
1404
+ scale,
1405
+ fluidRange.min,
1406
+ fluidRange.max,
1407
+ fluidBaseRef
1408
+ );
1409
+ if (fluid) return ` ${v.cssVar}: ${fluid};`;
1410
+ }
1411
+ }
1412
+ return ` ${v.cssVar}: ${v.value};`;
1413
+ });
1414
+ if (fluidActive) {
1415
+ const siteMargin = responsiveScales?.siteMargin ?? DEFAULT_SITE_MARGIN;
1416
+ baseVars.push(` --site-margin: ${buildSiteMarginClamp(siteMargin, fluidRange)};`);
1417
+ }
1418
+ if (baseVars.length === 0) {
1341
1419
  return "";
1342
1420
  }
1343
- const cssBlocks = [];
1344
- const baseVars = config.variables.map((v) => ` ${v.cssVar}: ${v.value};`);
1345
1421
  cssBlocks.push(`:root {
1346
1422
  ${baseVars.join("\n")}
1347
1423
  }`);
1348
- if (breakpoints && responsiveScales?.enabled) {
1424
+ const userVariables = config.variables ?? [];
1425
+ if (breakpoints && responsiveScales?.enabled && !fluidActive && userVariables.length > 0) {
1349
1426
  const baseRef = responsiveScales.baseReference || 16;
1350
1427
  const sortedBreakpoints = Object.entries(breakpoints).sort((a, b) => b[1].breakpoint - a[1].breakpoint);
1351
1428
  for (const [bpName, bpEntry] of sortedBreakpoints) {
1352
1429
  const scaledVars = [];
1353
- for (const variable of config.variables) {
1430
+ for (const variable of userVariables) {
1354
1431
  if (variable.scales && variable.scales[bpName]) {
1355
1432
  const overrideValue = variable.scales[bpName];
1356
1433
  if (overrideValue !== variable.value) {
@@ -1431,6 +1508,27 @@ function buildAttributes(props, exclude = []) {
1431
1508
  }
1432
1509
  return attrs.length > 0 ? " " + attrs.join(" ") : "";
1433
1510
  }
1511
+ function buildEditorAttrs(opts) {
1512
+ const { elementPath } = opts;
1513
+ if (!elementPath) return "";
1514
+ const parts = [`data-element-path="${escapeHtml(elementPath.join(","))}"`];
1515
+ if (opts.cmsItemIndexPath && opts.cmsItemIndexPath.length > 0) {
1516
+ parts.push(`data-cms-item-index="${escapeHtml(opts.cmsItemIndexPath.join("."))}"`);
1517
+ if (opts.cmsListPaths && opts.cmsListPaths.length === opts.cmsItemIndexPath.length) {
1518
+ const ctx = JSON.stringify({ itemIndexPath: opts.cmsItemIndexPath, listPaths: opts.cmsListPaths });
1519
+ parts.push(`data-cms-context="${escapeHtml(ctx)}"`);
1520
+ }
1521
+ }
1522
+ if (opts.isCMSListContainer) parts.push(`data-cms-list="true"`);
1523
+ if (opts.isComponentRoot) parts.push(`data-component-root="true"`);
1524
+ if (opts.parentComponentName) {
1525
+ parts.push(`data-parent-component="${escapeHtml(opts.parentComponentName)}"`);
1526
+ }
1527
+ if (opts.componentContext && !opts.isSlotContent) {
1528
+ parts.push(`data-component-context="${escapeHtml(opts.componentContext)}"`);
1529
+ }
1530
+ return " " + parts.join(" ");
1531
+ }
1434
1532
  function styleToString(style) {
1435
1533
  if (!style || Object.keys(style).length === 0) return "";
1436
1534
  const declarations = [];
@@ -1943,6 +2041,12 @@ function getDOMPurify() {
1943
2041
  function getTemplateContext(ctx) {
1944
2042
  return ctx.templateContext || null;
1945
2043
  }
2044
+ function buildListResolutionScope(ctx) {
2045
+ const tplCtx = ctx.templateContext;
2046
+ const props = ctx.componentResolvedProps;
2047
+ if (!tplCtx && !props) return void 0;
2048
+ return { ...props ?? {}, ...tplCtx ?? {} };
2049
+ }
1946
2050
  function getI18nResolver(ctx) {
1947
2051
  return createI18nResolver(ctx.locale, ctx.i18nConfig);
1948
2052
  }
@@ -1981,7 +2085,11 @@ function processStyleToClasses(style, ctx) {
1981
2085
  getI18nResolver(ctx)
1982
2086
  );
1983
2087
  }
1984
- return responsiveStylesToClasses(processedStyle);
2088
+ const fluidActive = ctx.responsiveScales?.enabled === true && ctx.responsiveScales?.mode === "fluid";
2089
+ return responsiveStylesToClasses(
2090
+ processedStyle,
2091
+ { fluidActive, responsiveScales: ctx.responsiveScales }
2092
+ );
1985
2093
  }
1986
2094
  function evaluateIfCondition(node, ctx) {
1987
2095
  const ifValue = hasIf(node) ? node.if : void 0;
@@ -2006,35 +2114,38 @@ function evaluateIfCondition(node, ctx) {
2006
2114
  if (ctx.cmsContext?.cms && resolved.includes("{{cms.")) {
2007
2115
  resolved = processCMSTemplate(resolved, ctx.cmsContext.cms, ctx.locale, ctx.i18nConfig);
2008
2116
  }
2117
+ if (resolved.includes("{{") && resolved.includes("}}")) {
2118
+ return false;
2119
+ }
2009
2120
  return Boolean(resolved) && resolved !== "false" && resolved !== "0" && resolved !== "";
2010
2121
  }
2011
2122
  return true;
2012
2123
  }
2013
- function resolveFilterValue(value, templateContext) {
2014
- if (!templateContext || typeof value !== "string" || !value.startsWith("{{") || !value.endsWith("}}")) {
2124
+ function resolveFilterValue(value, scope) {
2125
+ if (!scope || typeof value !== "string" || !value.startsWith("{{") || !value.endsWith("}}")) {
2015
2126
  return value;
2016
2127
  }
2017
2128
  const path2 = value.slice(2, -2).trim();
2018
- const resolved = getNestedValue(templateContext, path2);
2129
+ const resolved = getNestedValue(scope, path2);
2019
2130
  return resolved !== void 0 ? resolved : value;
2020
2131
  }
2021
- function resolveFilterTemplates(filter, templateContext) {
2022
- if (!filter || !templateContext) return filter;
2132
+ function resolveFilterTemplates(filter, scope) {
2133
+ if (!filter || !scope) return filter;
2023
2134
  if (Array.isArray(filter)) {
2024
2135
  return filter.map((cond) => ({
2025
2136
  ...cond,
2026
- value: resolveFilterValue(cond.value, templateContext)
2137
+ value: resolveFilterValue(cond.value, scope)
2027
2138
  }));
2028
2139
  }
2029
2140
  if ("field" in filter && "value" in filter) {
2030
2141
  return {
2031
2142
  ...filter,
2032
- value: resolveFilterValue(filter.value, templateContext)
2143
+ value: resolveFilterValue(filter.value, scope)
2033
2144
  };
2034
2145
  }
2035
2146
  const resolved = {};
2036
2147
  for (const [key, value] of Object.entries(filter)) {
2037
- resolved[key] = resolveFilterValue(value, templateContext);
2148
+ resolved[key] = resolveFilterValue(value, scope);
2038
2149
  }
2039
2150
  return resolved;
2040
2151
  }
@@ -2071,7 +2182,7 @@ async function expandRichTextComponents(html, ctx) {
2071
2182
  return resolved.join("");
2072
2183
  }
2073
2184
  var ssrComponentRegistry = new SSRRegistry();
2074
- async function buildComponentHTML(node, globalComponents = {}, pageComponents = {}, locale, i18nConfig, slugMappings, pagePath, cmsContext, cmsService, isProductionBuild) {
2185
+ async function buildComponentHTML(node, globalComponents = {}, pageComponents = {}, locale, i18nConfig, slugMappings, pagePath, cmsContext, cmsService, isProductionBuild, injectEditorAttrs) {
2075
2186
  const interactiveStylesMap = /* @__PURE__ */ new Map();
2076
2187
  const preloadImages = [];
2077
2188
  const neededCollections = /* @__PURE__ */ new Set();
@@ -2106,7 +2217,9 @@ async function buildComponentHTML(node, globalComponents = {}, pageComponents =
2106
2217
  // Collect SSR fallback HTML for complex nodes
2107
2218
  processedRawHtmlCollector,
2108
2219
  // Collect raw→processed HTML for Astro exporter
2109
- imageFormat: configService.getImageFormat()
2220
+ imageFormat: configService.getImageFormat(),
2221
+ injectEditorAttrs,
2222
+ responsiveScales: configService.getResponsiveScales()
2110
2223
  };
2111
2224
  const html = await renderNode(node, ctx);
2112
2225
  return { html, interactiveStylesMap, preloadImages, neededCollections, ssrFallbackCollector, processedRawHtmlCollector };
@@ -2137,7 +2250,7 @@ async function renderNestedListPlaceholder(node, ctx) {
2137
2250
  };
2138
2251
  const templateContent = node.children ? await renderChildrenAsync(node.children, childTemplateCtx) : "";
2139
2252
  const configJson = escapeHtml(JSON.stringify(config));
2140
- return `<div data-cms-list-nested="true" data-collection="${escapeHtml(sourceStr)}" data-cms-config="${configJson}"><template data-nested-template>${templateContent}</template></div>`;
2253
+ return `<div data-cms-list-nested="true" data-collection="${escapeHtml(sourceStr)}" data-cms-config="${configJson}"${editorAttrs(ctx, { isCMSListContainer: true })}><template data-nested-template>${templateContent}</template></div>`;
2141
2254
  }
2142
2255
  async function processList(node, ctx) {
2143
2256
  const nodeType = node.type;
@@ -2196,7 +2309,9 @@ async function processList(node, ctx) {
2196
2309
  );
2197
2310
  const itemCtx = {
2198
2311
  ...ctx,
2199
- templateContext
2312
+ templateContext,
2313
+ cmsItemIndexPath: [...ctx.cmsItemIndexPath ?? [], i],
2314
+ cmsListPaths: [...ctx.cmsListPaths ?? [], ctx.elementPath ?? []]
2200
2315
  };
2201
2316
  const childrenHtml = await renderChildrenAsync(node.children || [], itemCtx);
2202
2317
  renderedItems.push(childrenHtml);
@@ -2240,7 +2355,8 @@ async function getCollectionItems(node, source, ctx) {
2240
2355
  resolvedIds = Array.isArray(value) ? value.map((v) => String(v)) : String(value);
2241
2356
  }
2242
2357
  } else {
2243
- const parentContext = ctx.templateContext || { _type: "template" };
2358
+ const mergedScope = buildListResolutionScope(ctx) ?? {};
2359
+ const parentContext = { _type: "template", ...mergedScope };
2244
2360
  resolvedIds = resolveItemsTemplate(node.items, parentContext);
2245
2361
  }
2246
2362
  if (!resolvedIds) {
@@ -2258,7 +2374,7 @@ async function getCollectionItems(node, source, ctx) {
2258
2374
  } else {
2259
2375
  const query = {
2260
2376
  collection: source,
2261
- filter: resolveFilterTemplates(node.filter, ctx.templateContext),
2377
+ filter: resolveFilterTemplates(node.filter, buildListResolutionScope(ctx)),
2262
2378
  sort: node.sort,
2263
2379
  limit: node.limit,
2264
2380
  offset: node.offset,
@@ -2313,11 +2429,13 @@ function buildNodeElementClass(ctx, label, isSlotContent2) {
2313
2429
  const useComponentContext = !isSlotContent2 && Boolean(ctx.componentContext);
2314
2430
  const effectiveFileType = useComponentContext ? "component" : "page";
2315
2431
  const effectiveFileName = useComponentContext ? ctx.componentContext : pagePath ? pagePath.replace(/^\//, "").replace(/\//g, "_") || "index" : "page";
2432
+ const rawPath = ctx.elementPath || [];
2433
+ const path2 = useComponentContext && ctx.componentRootPath ? rawPath.slice(ctx.componentRootPath.length) : rawPath;
2316
2434
  const elementClassCtx = {
2317
2435
  fileType: effectiveFileType,
2318
2436
  fileName: effectiveFileName || "page",
2319
2437
  label,
2320
- path: ctx.elementPath || []
2438
+ path: path2
2321
2439
  };
2322
2440
  return generateElementClassName(elementClassCtx);
2323
2441
  }
@@ -2337,6 +2455,26 @@ function buildCssVariableStyleAttr(cssVariables) {
2337
2455
  const styleString = Object.entries(cssVariables).map(([k, v]) => `${k}: ${v}`).join("; ");
2338
2456
  return ` style="${escapeHtml(styleString)}"`;
2339
2457
  }
2458
+ function arraysEqual(a, b) {
2459
+ if (!a || !b || a.length !== b.length) return false;
2460
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
2461
+ return true;
2462
+ }
2463
+ function editorAttrs(ctx, opts = {}) {
2464
+ if (!ctx.injectEditorAttrs) return "";
2465
+ const isComponentRoot = !!ctx.componentContext && !opts.isSlotContent && arraysEqual(ctx.elementPath, ctx.componentRootPath);
2466
+ const effectiveParent = opts.isSlotContent ? ctx.parentComponentName : isComponentRoot ? ctx.parentComponentName : ctx.componentContext ?? ctx.parentComponentName;
2467
+ return buildEditorAttrs({
2468
+ elementPath: ctx.elementPath,
2469
+ cmsItemIndexPath: ctx.cmsItemIndexPath,
2470
+ cmsListPaths: ctx.cmsListPaths,
2471
+ componentContext: ctx.componentContext,
2472
+ parentComponentName: effectiveParent,
2473
+ isComponentRoot,
2474
+ isSlotContent: opts.isSlotContent,
2475
+ isCMSListContainer: opts.isCMSListContainer
2476
+ });
2477
+ }
2340
2478
  async function renderNode(node, ctx) {
2341
2479
  const { breakpoints, viewportWidth, locale, i18nConfig, slugMappings, pagePath } = ctx;
2342
2480
  if (node === null || node === void 0) return "";
@@ -2445,7 +2583,7 @@ async function renderNode(node, ctx) {
2445
2583
  delete nodeAttributes2.class;
2446
2584
  const attrs2 = buildAttributes(nodeAttributes2);
2447
2585
  const classAttr2 = classNames.length > 0 ? ` class="${escapeHtml(classNames.filter(Boolean).join(" "))}"` : "";
2448
- return `<span${classAttr2}${embedStyleAttr}${attrs2}>${optimizedHtml}</span>`;
2586
+ return `<span${classAttr2}${embedStyleAttr}${attrs2}${editorAttrs(ctx, { isSlotContent: isSlotContent(node) })}>${optimizedHtml}</span>`;
2449
2587
  }
2450
2588
  const attrClassName2 = nodeAttributes2.className || nodeAttributes2.class || "";
2451
2589
  if (attrClassName2) {
@@ -2455,7 +2593,7 @@ async function renderNode(node, ctx) {
2455
2593
  delete nodeAttributes2.class;
2456
2594
  const attrs = buildAttributes(nodeAttributes2);
2457
2595
  const classAttr = classNames.length > 0 ? ` class="${escapeHtml(classNames.filter(Boolean).join(" "))}"` : "";
2458
- return `<span${classAttr}${attrs}>${optimizedHtml}</span>`;
2596
+ return `<span${classAttr}${attrs}${editorAttrs(ctx, { isSlotContent: isSlotContent(node) })}>${optimizedHtml}</span>`;
2459
2597
  }
2460
2598
  if (isLinkNode(node)) {
2461
2599
  let href = typeof node.href === "string" ? node.href : "#";
@@ -2516,7 +2654,7 @@ async function renderNode(node, ctx) {
2516
2654
  const childPath = ctx.elementPath ? [...ctx.elementPath, index] : [index];
2517
2655
  return renderNode(child, { ...ctx, elementPath: childPath });
2518
2656
  }))).join("") : await renderNode(children, { ...ctx, elementPath: ctx.elementPath ? [...ctx.elementPath, 0] : [0] });
2519
- return `<a href="${escapeHtml(String(href))}"${classAttr}${olinkStyleAttr}${attrs}>${childrenHTML}</a>`;
2657
+ return `<a href="${escapeHtml(String(href))}"${classAttr}${olinkStyleAttr}${attrs}${editorAttrs(ctx, { isSlotContent: isSlotContent(node) })}>${childrenHTML}</a>`;
2520
2658
  }
2521
2659
  if (isLocaleListNode(node)) {
2522
2660
  return renderLocaleList(node, ctx);
@@ -2546,9 +2684,15 @@ async function renderNode(node, ctx) {
2546
2684
  if (!tag && !componentName) return "";
2547
2685
  let utilityClasses = [];
2548
2686
  let resolvedStyle = {};
2687
+ const isCustomComponentNode = nodeType === NODE_TYPE.COMPONENT && isComponentNode(node) && ssrComponentRegistry.has(node.component);
2688
+ let deferredComponentStyle;
2549
2689
  if (nodeStyle) {
2550
- validateStyleCoverage(nodeStyle, `Node: ${nodeType || "unknown"}`);
2551
- utilityClasses = processStyleToClasses(nodeStyle, ctx);
2690
+ if (isCustomComponentNode) {
2691
+ deferredComponentStyle = nodeStyle;
2692
+ } else {
2693
+ validateStyleCoverage(nodeStyle, `Node: ${nodeType || "unknown"}`);
2694
+ utilityClasses = processStyleToClasses(nodeStyle, ctx);
2695
+ }
2552
2696
  } else if (nodeProps.style) {
2553
2697
  if (isResponsiveStyle(nodeProps.style) && breakpoints && viewportWidth) {
2554
2698
  resolvedStyle = mergeResponsiveStyles(nodeProps.style, "viewport", viewportWidth, breakpoints);
@@ -2582,7 +2726,7 @@ async function renderNode(node, ctx) {
2582
2726
  ...nodeProps,
2583
2727
  ...nodeAttributesWithoutClass,
2584
2728
  ...mergedClassName ? { className: mergedClassName } : {},
2585
- ...Object.keys(resolvedStyle).length > 0 ? { style: resolvedStyle } : {}
2729
+ ...deferredComponentStyle ? { style: deferredComponentStyle } : Object.keys(resolvedStyle).length > 0 ? { style: resolvedStyle } : {}
2586
2730
  };
2587
2731
  if (nodeType === NODE_TYPE.COMPONENT && componentName && ssrComponentRegistry.has(componentName)) {
2588
2732
  return renderComponent(componentName, propsWithStyleAndAttrs, children, nodeAttributes, ctx);
@@ -2631,27 +2775,8 @@ async function renderComponent(componentName, propsWithStyleAndAttrs, children,
2631
2775
  if (!rootNode.props) {
2632
2776
  rootNode.props = {};
2633
2777
  }
2634
- if (isHtmlNode(rootNode) || isLinkNode(rootNode)) {
2635
- if (propsWithStyleAndAttrs.style) {
2636
- const existingStyle = rootNode.style;
2637
- if (existingStyle && typeof existingStyle === "object") {
2638
- rootNode.style = {
2639
- ...existingStyle,
2640
- ...propsWithStyleAndAttrs.style
2641
- };
2642
- } else {
2643
- rootNode.style = propsWithStyleAndAttrs.style;
2644
- }
2645
- }
2646
- } else {
2647
- if (rootNode.props.style && typeof rootNode.props.style === "object") {
2648
- rootNode.props.style = {
2649
- ...rootNode.props.style,
2650
- ...propsWithStyleAndAttrs.style || {}
2651
- };
2652
- } else if (propsWithStyleAndAttrs.style) {
2653
- rootNode.props.style = propsWithStyleAndAttrs.style;
2654
- }
2778
+ if (propsWithStyleAndAttrs.style) {
2779
+ mergeNodeStyles(rootNode, propsWithStyleAndAttrs.style);
2655
2780
  }
2656
2781
  if (propsWithStyleAndAttrs.className) {
2657
2782
  if (isHtmlNode(rootNode) || isLinkNode(rootNode)) {
@@ -2703,8 +2828,10 @@ async function renderComponent(componentName, propsWithStyleAndAttrs, children,
2703
2828
  }
2704
2829
  return await renderNode(processedStructure, {
2705
2830
  ...ctx,
2831
+ // The previously-active component (if any) becomes the parent for editor attrs
2832
+ parentComponentName: ctx.componentContext,
2706
2833
  componentContext: componentName,
2707
- elementPath: [0],
2834
+ componentRootPath: ctx.elementPath,
2708
2835
  componentResolvedProps: resolvedProps
2709
2836
  });
2710
2837
  } catch (error) {
@@ -2724,7 +2851,7 @@ async function renderLinkNode(propsWithStyleAndAttrs, children, ctx) {
2724
2851
  const childPath = ctx.elementPath ? [...ctx.elementPath, index] : [index];
2725
2852
  return renderNode(child, { ...ctx, elementPath: childPath });
2726
2853
  }))).join("") : await renderNode(children, { ...ctx, elementPath: ctx.elementPath ? [...ctx.elementPath, 0] : [0] });
2727
- return `<a href="${escapeHtml(String(href))}"${linkClassAttr}${attrs}>${childrenHTML}</a>`;
2854
+ return `<a href="${escapeHtml(String(href))}"${linkClassAttr}${attrs}${editorAttrs(ctx)}>${childrenHTML}</a>`;
2728
2855
  }
2729
2856
  async function renderHtmlElement(tag, propsWithStyleAndAttrs, children, ctx) {
2730
2857
  let classValue = propsWithStyleAndAttrs.className ? String(propsWithStyleAndAttrs.className) : "";
@@ -2746,18 +2873,32 @@ async function renderHtmlElement(tag, propsWithStyleAndAttrs, children, ctx) {
2746
2873
  const imageProps = ["src", "alt", "loading", "width", "height", "sizes", "srcset", "fetchpriority"];
2747
2874
  const excludeProps = tag.toLowerCase() === "img" ? ["style", "className", ...imageProps] : ["style", "className"];
2748
2875
  const attrs = buildAttributes(propsWithStyleAndAttrs, excludeProps);
2749
- const childrenHTML = Array.isArray(children) ? (await Promise.all(children.map((child, index) => {
2750
- const childPath = ctx.elementPath ? [...ctx.elementPath, index] : [index];
2751
- return renderNode(child, { ...ctx, elementPath: childPath });
2752
- }))).join("") : await renderNode(children, { ...ctx, elementPath: ctx.elementPath ? [...ctx.elementPath, 0] : [0] });
2876
+ const isRawTextElement = tag.toLowerCase() === "style" || tag.toLowerCase() === "script";
2877
+ let childrenHTML;
2878
+ if (isRawTextElement) {
2879
+ const flatten = (node) => {
2880
+ if (node == null) return "";
2881
+ if (typeof node === "string") return node;
2882
+ if (typeof node === "number") return String(node);
2883
+ if (Array.isArray(node)) return node.map(flatten).join("");
2884
+ return "";
2885
+ };
2886
+ childrenHTML = flatten(children);
2887
+ } else {
2888
+ childrenHTML = Array.isArray(children) ? (await Promise.all(children.map((child, index) => {
2889
+ const childPath = ctx.elementPath ? [...ctx.elementPath, index] : [index];
2890
+ return renderNode(child, { ...ctx, elementPath: childPath });
2891
+ }))).join("") : await renderNode(children, { ...ctx, elementPath: ctx.elementPath ? [...ctx.elementPath, 0] : [0] });
2892
+ }
2893
+ const ea = editorAttrs(ctx);
2753
2894
  const voidElements = ["img", "input", "br", "hr", "meta", "link", "area", "base", "col", "embed", "source", "track", "wbr"];
2754
2895
  if (voidElements.includes(tag.toLowerCase())) {
2755
2896
  if (tag.toLowerCase() === "img") {
2756
- return renderImageElement(propsWithStyleAndAttrs, classAttr, styleAttr, attrs, ctx);
2897
+ return renderImageElement(propsWithStyleAndAttrs, classAttr, styleAttr, attrs + ea, ctx);
2757
2898
  }
2758
- return `<${tag}${classAttr}${styleAttr}${attrs} />`;
2899
+ return `<${tag}${classAttr}${styleAttr}${attrs}${ea} />`;
2759
2900
  }
2760
- return `<${tag}${classAttr}${styleAttr}${attrs}>${childrenHTML}</${tag}>`;
2901
+ return `<${tag}${classAttr}${styleAttr}${attrs}${ea}>${childrenHTML}</${tag}>`;
2761
2902
  }
2762
2903
  function renderImageElement(propsWithStyleAndAttrs, classAttr, styleAttr, attrs, ctx) {
2763
2904
  const imgProps = propsWithStyleAndAttrs;
@@ -2845,9 +2986,13 @@ function renderLocaleList(node, ctx) {
2845
2986
  localeIconMap.set(localeConfig.code, localeConfig.icon);
2846
2987
  }
2847
2988
  }
2989
+ const localeStyleOpts = {
2990
+ fluidActive: ctx.responsiveScales?.enabled === true && ctx.responsiveScales?.mode === "fluid",
2991
+ responsiveScales: ctx.responsiveScales
2992
+ };
2848
2993
  let containerClasses = [];
2849
2994
  if (nodeStyle) {
2850
- containerClasses = responsiveStylesToClasses(nodeStyle);
2995
+ containerClasses = responsiveStylesToClasses(nodeStyle, localeStyleOpts);
2851
2996
  }
2852
2997
  const localeListInteractiveStyles = node.interactiveStyles;
2853
2998
  const localeListGenerateElementClass = node.generateElementClass;
@@ -2864,21 +3009,21 @@ function renderLocaleList(node, ctx) {
2864
3009
  const containerClassAttr = containerClasses.length > 0 ? ` class="${escapeHtml(containerClasses.join(" "))}"` : "";
2865
3010
  let itemClasses = [];
2866
3011
  if (node.itemStyle) {
2867
- itemClasses = responsiveStylesToClasses(node.itemStyle);
3012
+ itemClasses = responsiveStylesToClasses(node.itemStyle, localeStyleOpts);
2868
3013
  }
2869
3014
  const itemClassAttr = itemClasses.length > 0 ? ` class="${escapeHtml(itemClasses.join(" "))}"` : "";
2870
3015
  let activeItemClasses = [];
2871
3016
  if (node.activeItemStyle) {
2872
- activeItemClasses = responsiveStylesToClasses(node.activeItemStyle);
3017
+ activeItemClasses = responsiveStylesToClasses(node.activeItemStyle, localeStyleOpts);
2873
3018
  }
2874
3019
  let separatorClasses = [];
2875
3020
  if (node.separatorStyle) {
2876
- separatorClasses = responsiveStylesToClasses(node.separatorStyle);
3021
+ separatorClasses = responsiveStylesToClasses(node.separatorStyle, localeStyleOpts);
2877
3022
  }
2878
3023
  const separatorClassAttr = separatorClasses.length > 0 ? ` class="${escapeHtml(separatorClasses.join(" "))}"` : "";
2879
3024
  let flagClasses = [];
2880
3025
  if (node.flagStyle) {
2881
- flagClasses = responsiveStylesToClasses(node.flagStyle);
3026
+ flagClasses = responsiveStylesToClasses(node.flagStyle, localeStyleOpts);
2882
3027
  }
2883
3028
  const flagClassAttr = flagClasses.length > 0 ? ` class="${escapeHtml(flagClasses.join(" "))}"` : "";
2884
3029
  const currentItemClasses = [...itemClasses, ...activeItemClasses];
@@ -2913,7 +3058,7 @@ function renderLocaleList(node, ctx) {
2913
3058
  const linksHTML = showSeparator ? links.join(`<span${separatorClassAttr}></span>`) : links.join("");
2914
3059
  const nodeAttributes = extractAttributesFromNode(node);
2915
3060
  const attrsStr = buildAttributes(nodeAttributes);
2916
- const localeListResult = `<div data-locale-list="true"${containerClassAttr}${localeListStyleAttr}${attrsStr}>${linksHTML}</div>`;
3061
+ const localeListResult = `<div data-locale-list="true"${containerClassAttr}${localeListStyleAttr}${attrsStr}${editorAttrs(ctx)}>${linksHTML}</div>`;
2917
3062
  if (ctx.ssrFallbackCollector && ctx.elementPath) {
2918
3063
  ctx.ssrFallbackCollector.set(ctx.elementPath.join("."), localeListResult);
2919
3064
  }
@@ -2921,7 +3066,7 @@ function renderLocaleList(node, ctx) {
2921
3066
  }
2922
3067
  return '<div data-locale-list="true"></div>';
2923
3068
  }
2924
- async function renderPageSSR(pageData, globalComponents = {}, pagePath = "/", baseUrl = "", locale, i18nConfig, slugMappings, cmsContext, cmsService, isProductionBuild) {
3069
+ async function renderPageSSR(pageData, globalComponents = {}, pagePath = "/", baseUrl = "", locale, i18nConfig, slugMappings, cmsContext, cmsService, isProductionBuild, injectEditorAttrs) {
2925
3070
  const rootNode = pageData?.root || void 0;
2926
3071
  if (!rootNode) {
2927
3072
  throw new Error("Page data must have a root node");
@@ -2945,7 +3090,7 @@ async function renderPageSSR(pageData, globalComponents = {}, pagePath = "/", ba
2945
3090
  }
2946
3091
  }
2947
3092
  const pageComponents = pageData?.components || {};
2948
- const { html: contentHTML, interactiveStylesMap, preloadImages, neededCollections, ssrFallbackCollector, processedRawHtmlCollector } = rootNode ? await buildComponentHTML(rootNode, globalComponents, pageComponents, effectiveLocale, config, slugMappings, pagePath, cmsContext, cmsService, isProductionBuild) : { html: "", interactiveStylesMap: /* @__PURE__ */ new Map(), preloadImages: [], neededCollections: /* @__PURE__ */ new Set(), ssrFallbackCollector: /* @__PURE__ */ new Map(), processedRawHtmlCollector: /* @__PURE__ */ new Map() };
3093
+ const { html: contentHTML, interactiveStylesMap, preloadImages, neededCollections, ssrFallbackCollector, processedRawHtmlCollector } = rootNode ? await buildComponentHTML(rootNode, globalComponents, pageComponents, effectiveLocale, config, slugMappings, pagePath, cmsContext, cmsService, isProductionBuild, injectEditorAttrs) : { html: "", interactiveStylesMap: /* @__PURE__ */ new Map(), preloadImages: [], neededCollections: /* @__PURE__ */ new Set(), ssrFallbackCollector: /* @__PURE__ */ new Map(), processedRawHtmlCollector: /* @__PURE__ */ new Map() };
2949
3094
  const javascript = await collectComponentJavaScript(globalComponents, pageComponents);
2950
3095
  const componentCSS = collectComponentCSS(globalComponents, pageComponents);
2951
3096
  const fullUrl = baseUrl ? `${baseUrl}${pagePath}` : pagePath;
@@ -3269,7 +3414,7 @@ function generateImagePreloadTags(preloadImages) {
3269
3414
  }
3270
3415
  function minifyCSS(code) {
3271
3416
  if (!code.trim()) return code;
3272
- return code.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\s*([{};:,>~+])\s*/g, "$1").replace(/\s+/g, " ").replace(/\{\s+/g, "{").replace(/\s+\}/g, "}").replace(/;}/g, "}").trim();
3417
+ return code.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\s*([{};:,>~])\s*/g, "$1").replace(/\s+/g, " ").replace(/\{\s+/g, "{").replace(/\s+\}/g, "}").replace(/;}/g, "}").trim();
3273
3418
  }
3274
3419
  async function generateSSRHTML(pageDataOrOptions, globalComponents = {}, pagePath = "/", baseUrl = "", useBuiltBundle = false, locale, slugMappings, cmsContext, cmsService, externalScriptPath) {
3275
3420
  let options;
@@ -3306,11 +3451,12 @@ async function generateSSRHTML(pageDataOrOptions, globalComponents = {}, pagePat
3306
3451
  pageCustomCode,
3307
3452
  clientDataCollections,
3308
3453
  injectLiveReload = false,
3454
+ injectEditorAttrs = false,
3309
3455
  isEditor = false,
3310
3456
  isProductionBuild = false,
3311
3457
  serverPort
3312
3458
  } = options;
3313
- const rendered = await renderPageSSR(pageData, components, path2, base, loc, void 0, slugs, cms, cmsServ, isProductionBuild);
3459
+ const rendered = await renderPageSSR(pageData, components, path2, base, loc, void 0, slugs, cms, cmsServ, isProductionBuild, injectEditorAttrs);
3314
3460
  let finalClientDataCollections = clientDataCollections;
3315
3461
  if (rendered.neededCollections.size > 0 && cmsServ) {
3316
3462
  finalClientDataCollections = clientDataCollections ? new Map(clientDataCollections) : /* @__PURE__ */ new Map();
@@ -3423,7 +3569,7 @@ ${escapedJavaScript}
3423
3569
  const componentCSS = rendered.componentCSS || "";
3424
3570
  const usedUtilityClasses = extractUtilityClassesFromHTML(rendered.html);
3425
3571
  const breakpointConfig = await loadBreakpointConfig();
3426
- const responsiveScalesConfig = await loadResponsiveScalesConfig();
3572
+ const responsiveScalesConfig = configService.getResponsiveScales();
3427
3573
  const variablesConfig = await variableService.loadConfig();
3428
3574
  const variablesCSS = generateVariablesCSS(variablesConfig, breakpointConfig, responsiveScalesConfig);
3429
3575
  const remConversionConfig = configService.getRemConversion();
@@ -3492,7 +3638,7 @@ picture {
3492
3638
  const scriptPreloadTag = extScriptPath ? `<link rel="preload" href="${extScriptPath}" as="script">` : "";
3493
3639
  const imagePreloadTags = generateImagePreloadTags(rendered.preloadImages);
3494
3640
  const wsUrl = serverPort ? `'ws://localhost:${serverPort}/hmr'` : `location.origin.replace('http','ws')+'/hmr'`;
3495
- const liveReloadScript = injectLiveReload ? `<script>(function(){var ws,timer,gen=0;function connect(){ws=new WebSocket(${wsUrl});ws.onmessage=function(e){var d=JSON.parse(e.data);if(d.type==='hmr:libraries-update'){location.reload()}else if(d.type==='hmr:update'||d.type==='hmr:cms-update'||d.type==='hmr:colors-update'||d.type==='hmr:variables-update')hotReload()};ws.onclose=function(){clearTimeout(timer);timer=setTimeout(connect,1000)}}function hotReload(){var g=++gen;var sx=window.scrollX,sy=window.scrollY;fetch(location.href,{cache:'no-store'}).then(function(r){return r.text()}).then(function(html){if(g!==gen)return;var p=new DOMParser();var d=p.parseFromString(html,'text/html');var or=document.getElementById('root');var nr=d.getElementById('root');if(or&&nr)or.innerHTML=nr.innerHTML;var os=document.getElementById('meno-styles');var ns=d.getElementById('meno-styles');if(os&&ns){os.parentNode.replaceChild(ns.cloneNode(true),os)}var nh=d.documentElement;if(nh){document.documentElement.setAttribute('lang',nh.getAttribute('lang')||'en');document.documentElement.setAttribute('theme',nh.getAttribute('theme')||'light')}document.querySelectorAll('script[id^="meno-cms-"]').forEach(function(s){s.remove()});d.querySelectorAll('script[id^="meno-cms-"]').forEach(function(s){var c=document.createElement('script');c.type=s.type;c.id=s.id;c.textContent=s.textContent;document.head.appendChild(c)});window.__menoHotReload=true;document.querySelectorAll('body > script[src^="/libraries/"]').forEach(function(o){o.remove()});d.querySelectorAll('body > script[src^="/libraries/"]').forEach(function(n){var ls=document.createElement('script');ls.src=n.getAttribute('src')+(n.getAttribute('src').indexOf('?')>-1?'&':'?')+'_r='+Date.now();document.body.appendChild(ls)});var oscr=document.querySelector('script[src^="/_scripts/"]');var nscr=d.querySelector('script[src^="/_scripts/"]');if(nscr){var src=nscr.getAttribute('src');if(oscr)oscr.remove();var s=document.createElement('script');s.src=src+(src.indexOf('?')>-1?'&':'?')+'_r='+Date.now();s.onload=function(){document.dispatchEvent(new Event('DOMContentLoaded'));window.scrollTo(sx,sy)};s.onerror=function(){window.scrollTo(sx,sy)};document.body.appendChild(s)}else{if(oscr)oscr.remove();document.dispatchEvent(new Event('DOMContentLoaded'));window.scrollTo(sx,sy)}}).catch(function(){location.reload()})}connect()})()</script>` : "";
3641
+ const liveReloadScript = injectLiveReload ? `<script>(function(){var ws,timer,gen=0,lastSrvRoot=null;function strip(s){return s?s.replace(/[?&]_r=\\d+/,''):''}function classList(el){return (el.getAttribute('class')||'').split(/\\s+/).filter(Boolean)}function syncEl(cur,srv,old){var cc=classList(cur),sc=classList(srv),oc=old?new Set(classList(old)):new Set();var rt=cc.filter(function(c){return !oc.has(c)});var seen=new Set(),fin=[];sc.concat(rt).forEach(function(c){if(!seen.has(c)){seen.add(c);fin.push(c)}});var fs=fin.join(' ');if((cur.getAttribute('class')||'')!==fs){if(fs)cur.setAttribute('class',fs);else cur.removeAttribute('class')}for(var i=0;i<srv.attributes.length;i++){var a=srv.attributes[i];if(a.name==='class')continue;if(cur.getAttribute(a.name)!==a.value)cur.setAttribute(a.name,a.value)}if(old){for(var i=0;i<old.attributes.length;i++){var a=old.attributes[i];if(a.name==='class')continue;if(!srv.hasAttribute(a.name)&&cur.hasAttribute(a.name))cur.removeAttribute(a.name)}}}function syncText(cur,srv){var cc=cur.childNodes,sc=srv.childNodes;for(var i=0;i<sc.length;i++){var s=sc[i],c=cc[i];if(s.nodeType===3&&c&&c.nodeType===3){if(c.textContent!==s.textContent)c.textContent=s.textContent}}}function smartUpdate(curR,srvR,oldR){var ce=curR.querySelectorAll('[data-element-path]'),se=srvR.querySelectorAll('[data-element-path]');if(ce.length!==se.length){if(curR.innerHTML!==srvR.innerHTML)curR.innerHTML=srvR.innerHTML;return}var sbp={};for(var i=0;i<se.length;i++)sbp[se[i].getAttribute('data-element-path')]=se[i];var obp={};if(oldR){var oe=oldR.querySelectorAll('[data-element-path]');for(var i=0;i<oe.length;i++)obp[oe[i].getAttribute('data-element-path')]=oe[i]}for(var i=0;i<ce.length;i++){var c=ce[i],p=c.getAttribute('data-element-path'),s=sbp[p];if(!s){if(curR.innerHTML!==srvR.innerHTML)curR.innerHTML=srvR.innerHTML;return}syncEl(c,s,obp[p]);syncText(c,s)}syncText(curR,srvR)}function connect(){ws=new WebSocket(${wsUrl});ws.onmessage=function(e){var d=JSON.parse(e.data);if(d.type==='hmr:libraries-update'){location.reload()}else if(d.type==='hmr:update'||d.type==='hmr:cms-update'||d.type==='hmr:colors-update'||d.type==='hmr:variables-update')hotReload()};ws.onclose=function(){clearTimeout(timer);timer=setTimeout(connect,1000)}}function hotReload(){var g=++gen;var sx=window.scrollX,sy=window.scrollY;fetch(location.href,{cache:'no-store'}).then(function(r){return r.text()}).then(function(html){if(g!==gen)return;var p=new DOMParser();var d=p.parseFromString(html,'text/html');var or=document.getElementById('root'),nr=d.getElementById('root');if(or&&nr)smartUpdate(or,nr,lastSrvRoot);if(nr)lastSrvRoot=nr.cloneNode(true);var os=document.getElementById('meno-styles'),ns=d.getElementById('meno-styles');if(os&&ns&&os.textContent!==ns.textContent)os.parentNode.replaceChild(ns.cloneNode(true),os);var nh=d.documentElement;if(nh){var nl=nh.getAttribute('lang')||'en',nt=nh.getAttribute('theme')||'light';if(document.documentElement.getAttribute('lang')!==nl)document.documentElement.setAttribute('lang',nl);if(document.documentElement.getAttribute('theme')!==nt)document.documentElement.setAttribute('theme',nt)}var ocms=document.querySelectorAll('script[id^="meno-cms-"]'),ncms=d.querySelectorAll('script[id^="meno-cms-"]');var ock=JSON.stringify(Array.prototype.map.call(ocms,function(s){return [s.id,s.textContent]}));var nck=JSON.stringify(Array.prototype.map.call(ncms,function(s){return [s.id,s.textContent]}));if(ock!==nck){ocms.forEach(function(s){s.remove()});ncms.forEach(function(s){var c=document.createElement('script');c.type=s.type;c.id=s.id;c.textContent=s.textContent;document.head.appendChild(c)})}window.__menoHotReload=true;var olib=document.querySelectorAll('body > script[src^="/libraries/"]'),nlib=d.querySelectorAll('body > script[src^="/libraries/"]');var olk=JSON.stringify(Array.prototype.map.call(olib,function(s){return strip(s.getAttribute('src'))}).sort());var nlk=JSON.stringify(Array.prototype.map.call(nlib,function(s){return strip(s.getAttribute('src'))}).sort());if(olk!==nlk){olib.forEach(function(o){o.remove()});nlib.forEach(function(n){var src=n.getAttribute('src');var ls=document.createElement('script');ls.src=src+(src.indexOf('?')>-1?'&':'?')+'_r='+Date.now();document.body.appendChild(ls)})}var oscr=document.querySelector('script[src^="/_scripts/"]'),nscr=d.querySelector('script[src^="/_scripts/"]');var oss=oscr?strip(oscr.getAttribute('src')):'',nss=nscr?strip(nscr.getAttribute('src')):'';if(oss===nss){window.scrollTo(sx,sy)}else{if(oscr)oscr.remove();if(nscr){var src=nscr.getAttribute('src');var s=document.createElement('script');s.src=src+(src.indexOf('?')>-1?'&':'?')+'_r='+Date.now();s.onload=function(){document.dispatchEvent(new Event('DOMContentLoaded'));window.scrollTo(sx,sy)};s.onerror=function(){window.scrollTo(sx,sy)};document.body.appendChild(s)}else{document.dispatchEvent(new Event('DOMContentLoaded'));window.scrollTo(sx,sy)}}}).catch(function(){location.reload()})}var iR=document.getElementById('root');if(iR)lastSrvRoot=iR.cloneNode(true);connect()})()</script>` : "";
3496
3642
  const scrollHandlerScript = injectLiveReload ? `<script>(function(){window.addEventListener('message',function(e){if(e.data.type==='GET_SCROLL_POSITION'){window.parent.postMessage({type:'SCROLL_POSITION_RESPONSE',scrollX:window.scrollX,scrollY:window.scrollY},'*')}else if(e.data.type==='SET_SCROLL_POSITION'){window.scrollTo(e.data.scrollX,e.data.scrollY)}})})()</script>` : "";
3497
3643
  const styleContent = useBundled ? finalCSS : `
3498
3644
  ${combinedCSS.split("\n").join("\n ")}
@@ -5674,6 +5820,7 @@ autoInit();
5674
5820
  // lib/server/providers/fileSystemCMSProvider.ts
5675
5821
  import { existsSync as existsSync2, readdirSync as readdirSync2, mkdirSync } from "fs";
5676
5822
  import { join as join2 } from "path";
5823
+ var DRAFT_FILE_SUFFIX = `${CMS_DRAFT_SUFFIX}.json`;
5677
5824
  async function loadJSONFile2(filePath) {
5678
5825
  try {
5679
5826
  if (await fileExists(filePath)) {
@@ -5685,12 +5832,18 @@ async function loadJSONFile2(filePath) {
5685
5832
  return null;
5686
5833
  }
5687
5834
  }
5688
- function normalizeItem(content, filename) {
5689
- return {
5835
+ function normalizeItem(content, filename, isDraft = false) {
5836
+ const base = {
5690
5837
  ...content,
5691
5838
  _slug: filename,
5692
5839
  _filename: filename
5693
5840
  };
5841
+ if (isDraft) base._isDraft = true;
5842
+ return base;
5843
+ }
5844
+ function stripTransient(item) {
5845
+ const { _slug, _isDraft, _hasDraft, _url, ...rest } = item;
5846
+ return rest;
5694
5847
  }
5695
5848
  var FileSystemCMSProvider = class {
5696
5849
  constructor(templatesDir, cmsDir) {
@@ -5708,13 +5861,16 @@ var FileSystemCMSProvider = class {
5708
5861
  }
5709
5862
  }
5710
5863
  /**
5711
- * Validate filename to prevent path traversal attacks
5864
+ * Validate filename to prevent path traversal attacks and reserved-suffix collisions.
5712
5865
  * @throws Error if filename is invalid
5713
5866
  */
5714
5867
  validateFilename(filename) {
5715
5868
  if (!isSafePathSegment(filename)) {
5716
5869
  throw new Error(`Invalid filename: "${filename}". Filenames cannot contain path separators or traversal sequences.`);
5717
5870
  }
5871
+ if (isReservedDraftFilename(filename)) {
5872
+ throw new Error(`Invalid filename: "${filename}". The "${CMS_DRAFT_SUFFIX}" suffix is reserved for draft files.`);
5873
+ }
5718
5874
  }
5719
5875
  /**
5720
5876
  * Load all CMS schemas from page files with source: 'cms' in templates/
@@ -5761,7 +5917,7 @@ var FileSystemCMSProvider = class {
5761
5917
  return [];
5762
5918
  }
5763
5919
  const files = readdirSync2(collectionDir);
5764
- const jsonFiles = files.filter((f) => f.endsWith(".json"));
5920
+ const jsonFiles = files.filter((f) => f.endsWith(".json") && !f.endsWith(DRAFT_FILE_SUFFIX));
5765
5921
  const results = await Promise.all(
5766
5922
  jsonFiles.map(async (file) => {
5767
5923
  const filePath = join2(collectionDir, file);
@@ -5845,22 +6001,170 @@ var FileSystemCMSProvider = class {
5845
6001
  if (!existsSync2(collectionDir)) {
5846
6002
  mkdirSync(collectionDir, { recursive: true });
5847
6003
  }
5848
- const { _slug, ...itemData } = item;
6004
+ const itemData = stripTransient(item);
5849
6005
  const filePath = join2(collectionDir, `${filename}.json`);
5850
6006
  await writeFile2(filePath, JSON.stringify(itemData, null, 2), "utf-8");
5851
6007
  }
5852
6008
  /**
5853
- * Delete item by filename (or slug for backward compat)
6009
+ * Delete item by filename. Removes the published file AND any draft sibling.
5854
6010
  */
5855
6011
  async deleteItem(collection, filename) {
5856
6012
  this.validateCollection(collection);
5857
6013
  this.validateFilename(filename);
5858
6014
  const { unlink } = await import("fs/promises");
5859
- const filePath = join2(this.cmsDir, collection, `${filename}.json`);
6015
+ const publishedPath = join2(this.cmsDir, collection, `${filename}.json`);
6016
+ if (existsSync2(publishedPath)) {
6017
+ await unlink(publishedPath);
6018
+ }
6019
+ const draftPath = this.draftPath(collection, filename);
6020
+ if (existsSync2(draftPath)) {
6021
+ await unlink(draftPath);
6022
+ }
6023
+ }
6024
+ // ---- Draft helpers ----------------------------------------------------
6025
+ draftPath(collection, filename) {
6026
+ return join2(this.cmsDir, collection, `${filename}${DRAFT_FILE_SUFFIX}`);
6027
+ }
6028
+ /**
6029
+ * Get the draft version of an item, or null if no draft file exists.
6030
+ * Drafts skip strict validation — they may be partial / WIP.
6031
+ */
6032
+ async getDraft(collection, filename) {
6033
+ this.validateCollection(collection);
6034
+ this.validateFilename(filename);
6035
+ const filePath = this.draftPath(collection, filename);
6036
+ const content = await loadJSONFile2(filePath);
6037
+ if (!content || typeof content !== "object") {
6038
+ return null;
6039
+ }
6040
+ return normalizeItem(
6041
+ content,
6042
+ filename,
6043
+ /*isDraft*/
6044
+ true
6045
+ );
6046
+ }
6047
+ /**
6048
+ * List all drafts in a collection. Used by the Studio item list to mark
6049
+ * items that have an outstanding draft sibling (or are draft-only).
6050
+ */
6051
+ async getAllDrafts(collection) {
6052
+ this.validateCollection(collection);
6053
+ const collectionDir = join2(this.cmsDir, collection);
6054
+ if (!existsSync2(collectionDir)) return [];
6055
+ const files = readdirSync2(collectionDir).filter((f) => f.endsWith(DRAFT_FILE_SUFFIX));
6056
+ const results = await Promise.all(
6057
+ files.map(async (file) => {
6058
+ const filePath = join2(collectionDir, file);
6059
+ const content = await loadJSONFile2(filePath);
6060
+ return { file, content };
6061
+ })
6062
+ );
6063
+ const drafts = [];
6064
+ for (const { file, content } of results) {
6065
+ if (content && typeof content === "object") {
6066
+ const filename = file.slice(0, -DRAFT_FILE_SUFFIX.length);
6067
+ drafts.push(normalizeItem(
6068
+ content,
6069
+ filename,
6070
+ /*isDraft*/
6071
+ true
6072
+ ));
6073
+ }
6074
+ }
6075
+ return drafts;
6076
+ }
6077
+ async hasDraft(collection, filename) {
6078
+ this.validateCollection(collection);
6079
+ this.validateFilename(filename);
6080
+ return existsSync2(this.draftPath(collection, filename));
6081
+ }
6082
+ /**
6083
+ * Save the draft version of an item. Loose validation — drafts may have
6084
+ * missing required fields or partial data. Strict validation only runs at
6085
+ * publish time. The item's `_filename` determines the target file.
6086
+ */
6087
+ async saveDraft(collection, item) {
6088
+ this.validateCollection(collection);
6089
+ const { writeFile: writeFile2 } = await import("fs/promises");
6090
+ const schemas = await this.getAllSchemas();
6091
+ const schemaInfo = schemas.get(collection);
6092
+ if (!schemaInfo) {
6093
+ throw new Error(`Unknown collection: ${collection}`);
6094
+ }
6095
+ let filename;
6096
+ if (item._filename) {
6097
+ filename = item._filename;
6098
+ } else {
6099
+ const slugField = schemaInfo.schema.slugField;
6100
+ const slugValue = item[slugField];
6101
+ filename = typeof slugValue === "string" ? slugValue : String(slugValue);
6102
+ }
6103
+ if (!filename || filename === "[object Object]") {
6104
+ throw new Error("Missing _filename field. Drafts must have _filename set on creation.");
6105
+ }
6106
+ this.validateFilename(filename);
6107
+ const collectionDir = join2(this.cmsDir, collection);
6108
+ if (!existsSync2(collectionDir)) {
6109
+ mkdirSync(collectionDir, { recursive: true });
6110
+ }
6111
+ const itemData = stripTransient(item);
6112
+ const validation = validateCMSDraftItem(itemData);
6113
+ if (!validation.valid) {
6114
+ const messages = validation.errors.map((e) => `${e.path}: ${e.message}`).join(", ");
6115
+ throw new Error(`Invalid draft: ${messages}`);
6116
+ }
6117
+ const filePath = this.draftPath(collection, filename);
6118
+ await writeFile2(filePath, JSON.stringify(itemData, null, 2), "utf-8");
6119
+ }
6120
+ /**
6121
+ * Discard the draft version of an item. No-op if no draft exists.
6122
+ */
6123
+ async discardDraft(collection, filename) {
6124
+ this.validateCollection(collection);
6125
+ this.validateFilename(filename);
6126
+ const { unlink } = await import("fs/promises");
6127
+ const filePath = this.draftPath(collection, filename);
5860
6128
  if (existsSync2(filePath)) {
5861
6129
  await unlink(filePath);
5862
6130
  }
5863
6131
  }
6132
+ /**
6133
+ * Promote a draft to published. Reads `{filename}.draft.json`, writes the
6134
+ * content to `{filename}.json`, then unlinks the draft. The published write
6135
+ * happens first so a crash mid-operation leaves a valid published file plus
6136
+ * an orphan draft (recoverable via the editor's Discard button) — never a
6137
+ * gap with no published content.
6138
+ *
6139
+ * Throws if no draft exists.
6140
+ */
6141
+ async publishDraft(collection, filename) {
6142
+ this.validateCollection(collection);
6143
+ this.validateFilename(filename);
6144
+ const { writeFile: writeFile2, unlink } = await import("fs/promises");
6145
+ const draftFilePath = this.draftPath(collection, filename);
6146
+ const content = await loadJSONFile2(draftFilePath);
6147
+ if (!content || typeof content !== "object") {
6148
+ throw new Error(`No draft to publish for ${collection}/${filename}`);
6149
+ }
6150
+ const item = normalizeItem(content, filename);
6151
+ const validation = validateCMSItem(item);
6152
+ if (!validation.valid) {
6153
+ const messages = validation.errors.map((e) => `${e.path}: ${e.message}`).join(", ");
6154
+ throw new Error(`Cannot publish invalid draft: ${messages}`);
6155
+ }
6156
+ const collectionDir = join2(this.cmsDir, collection);
6157
+ if (!existsSync2(collectionDir)) {
6158
+ mkdirSync(collectionDir, { recursive: true });
6159
+ }
6160
+ const itemData = stripTransient(validation.data);
6161
+ const publishedPath = join2(collectionDir, `${filename}.json`);
6162
+ await writeFile2(publishedPath, JSON.stringify(itemData, null, 2), "utf-8");
6163
+ if (existsSync2(draftFilePath)) {
6164
+ await unlink(draftFilePath);
6165
+ }
6166
+ return normalizeItem(itemData, filename);
6167
+ }
5864
6168
  /**
5865
6169
  * Clear schema cache (useful when pages are modified)
5866
6170
  */
@@ -5935,7 +6239,6 @@ export {
5935
6239
  loadComponentDirectory,
5936
6240
  mapPageNameToPath,
5937
6241
  loadBreakpointConfig,
5938
- loadResponsiveScalesConfig,
5939
6242
  loadI18nConfig,
5940
6243
  loadIconsConfig,
5941
6244
  CachedConfigLoader,
@@ -5952,6 +6255,7 @@ export {
5952
6255
  generateVariablesCSS,
5953
6256
  buildSlugIndex,
5954
6257
  translatePath,
6258
+ getLocaleLinks,
5955
6259
  resolveSlugToPageId,
5956
6260
  escapeHtml,
5957
6261
  buildAttributes,
@@ -5984,4 +6288,4 @@ export {
5984
6288
  FileSystemCMSProvider,
5985
6289
  migrateTemplatesDirectory
5986
6290
  };
5987
- //# sourceMappingURL=chunk-FGUZOYJX.js.map
6291
+ //# sourceMappingURL=chunk-EQYDSPBB.js.map