@cmssy/react 16.6.0 → 16.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.cjs CHANGED
@@ -12,6 +12,13 @@ function buildBlockMap(blocks) {
12
12
  for (const block of blocks) map[block.type] = block.component;
13
13
  return map;
14
14
  }
15
+ function buildLoaderMap(blocks) {
16
+ const map = /* @__PURE__ */ Object.create(null);
17
+ for (const block of blocks) {
18
+ if (block.loader) map[block.type] = block.loader;
19
+ }
20
+ return map;
21
+ }
15
22
 
16
23
  // src/bridge/shortcut-keys.ts
17
24
  function isMacPlatform() {
@@ -85,7 +92,14 @@ function collectMedia(block) {
85
92
  function laidOut(elements) {
86
93
  return elements.map(paint).filter((state) => state.laidOut);
87
94
  }
95
+ function hasArea(block) {
96
+ const rect = block.getBoundingClientRect();
97
+ return rect.width > 0 && rect.height > 0;
98
+ }
88
99
  function isBlockPainted(block) {
100
+ if (block.childElementCount === 0 && !holdsText(block) && !hasArea(block)) {
101
+ return false;
102
+ }
89
103
  const copy = laidOut(collectCopy(block));
90
104
  if (copy.length > 0) {
91
105
  const visible2 = copy.filter((state) => state.opacity > TRANSPARENT);
@@ -631,6 +645,69 @@ function useDragAgent(config) {
631
645
  }, [config.editorOrigin]);
632
646
  return { dropY };
633
647
  }
648
+ var CMSSY_BLOCK_DATA_PATH = "/api/cmssy/block-data";
649
+ var DEBOUNCE_MS = 300;
650
+ function useBlockLoaderData({
651
+ enabled,
652
+ url = CMSSY_BLOCK_DATA_PATH,
653
+ blocks,
654
+ locale,
655
+ defaultLocale,
656
+ enabledLocales,
657
+ page
658
+ }) {
659
+ const [data, setData] = react.useState({});
660
+ const warned2 = react.useRef(false);
661
+ const request = enabled ? JSON.stringify({
662
+ blocks,
663
+ locale,
664
+ defaultLocale,
665
+ enabledLocales,
666
+ page
667
+ }) : "";
668
+ react.useEffect(() => {
669
+ if (!request) return;
670
+ if (blocks.length === 0) {
671
+ setData((current) => Object.keys(current).length === 0 ? current : {});
672
+ return;
673
+ }
674
+ const controller = new AbortController();
675
+ const timer = setTimeout(() => {
676
+ void (async () => {
677
+ try {
678
+ const response = await fetch(url, {
679
+ method: "POST",
680
+ headers: { "content-type": "application/json" },
681
+ body: request,
682
+ signal: controller.signal
683
+ });
684
+ if (!response.ok) {
685
+ if (!warned2.current && typeof console !== "undefined") {
686
+ warned2.current = true;
687
+ console.warn(
688
+ `[cmssy] ${url} answered ${response.status}. Blocks with a loader keep the data the page was rendered with until it is saved. Mount createCmssyBlockDataRoute to resolve them as you edit.`
689
+ );
690
+ }
691
+ return;
692
+ }
693
+ const body = await response.json();
694
+ setData(body.data ?? {});
695
+ } catch (err) {
696
+ if (err?.name === "AbortError") return;
697
+ if (!warned2.current && typeof console !== "undefined") {
698
+ warned2.current = true;
699
+ console.warn(`[cmssy] could not reach ${url}`, err);
700
+ }
701
+ }
702
+ })();
703
+ }, DEBOUNCE_MS);
704
+ return () => {
705
+ clearTimeout(timer);
706
+ controller.abort();
707
+ };
708
+ }, [request, url, blocks.length]);
709
+ return data;
710
+ }
634
711
 
635
712
  // src/components/block-error.ts
636
713
  var BLOCK_ERROR_KEY = "__cmssyBlockError";
@@ -703,6 +780,10 @@ function BlockErrorCard({
703
780
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: messageStyle, children: error.message })
704
781
  ] });
705
782
  }
783
+ function foldBlockContent(block, locale, defaultLocale, patchedContent, resolvedContent) {
784
+ const base = resolvedContent ? { ...resolvedContent } : internal.getBlockContentForLanguage(block.content, locale, defaultLocale);
785
+ return patchedContent ? { ...base, ...patchedContent } : base;
786
+ }
706
787
  var WARN_CAP = 256;
707
788
  var warned = /* @__PURE__ */ new Set();
708
789
  function UnknownBlock({ type }) {
@@ -768,8 +849,13 @@ function CmssyBlock({
768
849
  )
769
850
  );
770
851
  }
771
- const base = resolvedContent ? { ...resolvedContent } : internal.getBlockContentForLanguage(block.content, locale, defaultLocale);
772
- const content = patchedContent ? { ...base, ...patchedContent } : base;
852
+ const content = foldBlockContent(
853
+ block,
854
+ locale,
855
+ defaultLocale,
856
+ patchedContent,
857
+ resolvedContent
858
+ );
773
859
  if (schema) internal.normalizeBlockContent(content, schema, resolvedContent);
774
860
  const style = patchedStyle ? { ...internal.asBucket(block.style), ...patchedStyle } : internal.asBucket(block.style);
775
861
  const advanced = patchedAdvanced ? { ...internal.asBucket(block.advanced), ...patchedAdvanced } : internal.asBucket(block.advanced);
@@ -881,6 +967,36 @@ function EditableBlocks({
881
967
  }
882
968
  return merged;
883
969
  }, [page.blocks, inserted, order, removed]);
970
+ const loaderMap = react.useMemo(() => buildLoaderMap(blocks), [blocks]);
971
+ const loaderBlocks = react.useMemo(
972
+ () => renderBlocks.filter((block) => Object.hasOwn(loaderMap, block.type)).map((block) => ({
973
+ id: block.id,
974
+ type: block.type,
975
+ content: foldBlockContent(
976
+ block,
977
+ locale,
978
+ defaultLocale,
979
+ patches[block.id],
980
+ resolvedContent?.[block.id]
981
+ )
982
+ })),
983
+ [renderBlocks, loaderMap, locale, defaultLocale, patches, resolvedContent]
984
+ );
985
+ const liveData = useBlockLoaderData({
986
+ enabled: loaderBlocks.length > 0,
987
+ ...edit.blockDataUrl ? { url: edit.blockDataUrl } : {},
988
+ blocks: loaderBlocks,
989
+ locale,
990
+ defaultLocale,
991
+ ...enabledLocales?.length ? { enabledLocales } : {},
992
+ ...page.slug ? {
993
+ page: {
994
+ id: page.id,
995
+ slug: page.slug,
996
+ pageType: page.pageType ?? null
997
+ }
998
+ } : {}
999
+ });
884
1000
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
885
1001
  renderBlocks.map((block) => /* @__PURE__ */ jsxRuntime.jsx(
886
1002
  CmssyBlock,
@@ -896,7 +1012,7 @@ function EditableBlocks({
896
1012
  blockMap,
897
1013
  editable: true,
898
1014
  context,
899
- data: data?.[block.id]
1015
+ data: Object.hasOwn(liveData, block.id) ? liveData[block.id] : data?.[block.id]
900
1016
  },
901
1017
  block.id
902
1018
  )),
package/dist/client.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { BlockSchema, BlockMeta, LayoutRegion, CmssyPageData, CmssyFormDefinition, CmssyLayoutGroup } from '@cmssy/core';
3
- import { B as BlockDefinition, L as LayoutBlockPage } from './internal-server-Bkracpae.cjs';
3
+ import { B as BlockDefinition, L as LayoutBlockPage } from './resolve-block-data-Bkracpae.cjs';
4
4
  import 'react';
5
5
 
6
6
  interface EditBridgeConfig {
@@ -8,6 +8,7 @@ interface EditBridgeConfig {
8
8
  schemas?: Record<string, BlockSchema>;
9
9
  blockMeta?: Record<string, BlockMeta>;
10
10
  layoutRegions?: readonly LayoutRegion[] | undefined;
11
+ blockDataUrl?: string;
11
12
  }
12
13
  type PatchMap = Partial<Record<string, Record<string, unknown>>>;
13
14
  interface InsertedBlock {
package/dist/client.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { BlockSchema, BlockMeta, LayoutRegion, CmssyPageData, CmssyFormDefinition, CmssyLayoutGroup } from '@cmssy/core';
3
- import { B as BlockDefinition, L as LayoutBlockPage } from './internal-server-Bkracpae.js';
3
+ import { B as BlockDefinition, L as LayoutBlockPage } from './resolve-block-data-Bkracpae.js';
4
4
  import 'react';
5
5
 
6
6
  interface EditBridgeConfig {
@@ -8,6 +8,7 @@ interface EditBridgeConfig {
8
8
  schemas?: Record<string, BlockSchema>;
9
9
  blockMeta?: Record<string, BlockMeta>;
10
10
  layoutRegions?: readonly LayoutRegion[] | undefined;
11
+ blockDataUrl?: string;
11
12
  }
12
13
  type PatchMap = Partial<Record<string, Record<string, unknown>>>;
13
14
  interface InsertedBlock {
package/dist/client.js CHANGED
@@ -10,6 +10,13 @@ function buildBlockMap(blocks) {
10
10
  for (const block of blocks) map[block.type] = block.component;
11
11
  return map;
12
12
  }
13
+ function buildLoaderMap(blocks) {
14
+ const map = /* @__PURE__ */ Object.create(null);
15
+ for (const block of blocks) {
16
+ if (block.loader) map[block.type] = block.loader;
17
+ }
18
+ return map;
19
+ }
13
20
 
14
21
  // src/bridge/shortcut-keys.ts
15
22
  function isMacPlatform() {
@@ -83,7 +90,14 @@ function collectMedia(block) {
83
90
  function laidOut(elements) {
84
91
  return elements.map(paint).filter((state) => state.laidOut);
85
92
  }
93
+ function hasArea(block) {
94
+ const rect = block.getBoundingClientRect();
95
+ return rect.width > 0 && rect.height > 0;
96
+ }
86
97
  function isBlockPainted(block) {
98
+ if (block.childElementCount === 0 && !holdsText(block) && !hasArea(block)) {
99
+ return false;
100
+ }
87
101
  const copy = laidOut(collectCopy(block));
88
102
  if (copy.length > 0) {
89
103
  const visible2 = copy.filter((state) => state.opacity > TRANSPARENT);
@@ -629,6 +643,69 @@ function useDragAgent(config) {
629
643
  }, [config.editorOrigin]);
630
644
  return { dropY };
631
645
  }
646
+ var CMSSY_BLOCK_DATA_PATH = "/api/cmssy/block-data";
647
+ var DEBOUNCE_MS = 300;
648
+ function useBlockLoaderData({
649
+ enabled,
650
+ url = CMSSY_BLOCK_DATA_PATH,
651
+ blocks,
652
+ locale,
653
+ defaultLocale,
654
+ enabledLocales,
655
+ page
656
+ }) {
657
+ const [data, setData] = useState({});
658
+ const warned2 = useRef(false);
659
+ const request = enabled ? JSON.stringify({
660
+ blocks,
661
+ locale,
662
+ defaultLocale,
663
+ enabledLocales,
664
+ page
665
+ }) : "";
666
+ useEffect(() => {
667
+ if (!request) return;
668
+ if (blocks.length === 0) {
669
+ setData((current) => Object.keys(current).length === 0 ? current : {});
670
+ return;
671
+ }
672
+ const controller = new AbortController();
673
+ const timer = setTimeout(() => {
674
+ void (async () => {
675
+ try {
676
+ const response = await fetch(url, {
677
+ method: "POST",
678
+ headers: { "content-type": "application/json" },
679
+ body: request,
680
+ signal: controller.signal
681
+ });
682
+ if (!response.ok) {
683
+ if (!warned2.current && typeof console !== "undefined") {
684
+ warned2.current = true;
685
+ console.warn(
686
+ `[cmssy] ${url} answered ${response.status}. Blocks with a loader keep the data the page was rendered with until it is saved. Mount createCmssyBlockDataRoute to resolve them as you edit.`
687
+ );
688
+ }
689
+ return;
690
+ }
691
+ const body = await response.json();
692
+ setData(body.data ?? {});
693
+ } catch (err) {
694
+ if (err?.name === "AbortError") return;
695
+ if (!warned2.current && typeof console !== "undefined") {
696
+ warned2.current = true;
697
+ console.warn(`[cmssy] could not reach ${url}`, err);
698
+ }
699
+ }
700
+ })();
701
+ }, DEBOUNCE_MS);
702
+ return () => {
703
+ clearTimeout(timer);
704
+ controller.abort();
705
+ };
706
+ }, [request, url, blocks.length]);
707
+ return data;
708
+ }
632
709
 
633
710
  // src/components/block-error.ts
634
711
  var BLOCK_ERROR_KEY = "__cmssyBlockError";
@@ -701,6 +778,10 @@ function BlockErrorCard({
701
778
  /* @__PURE__ */ jsx("div", { style: messageStyle, children: error.message })
702
779
  ] });
703
780
  }
781
+ function foldBlockContent(block, locale, defaultLocale, patchedContent, resolvedContent) {
782
+ const base = resolvedContent ? { ...resolvedContent } : getBlockContentForLanguage(block.content, locale, defaultLocale);
783
+ return patchedContent ? { ...base, ...patchedContent } : base;
784
+ }
704
785
  var WARN_CAP = 256;
705
786
  var warned = /* @__PURE__ */ new Set();
706
787
  function UnknownBlock({ type }) {
@@ -766,8 +847,13 @@ function CmssyBlock({
766
847
  )
767
848
  );
768
849
  }
769
- const base = resolvedContent ? { ...resolvedContent } : getBlockContentForLanguage(block.content, locale, defaultLocale);
770
- const content = patchedContent ? { ...base, ...patchedContent } : base;
850
+ const content = foldBlockContent(
851
+ block,
852
+ locale,
853
+ defaultLocale,
854
+ patchedContent,
855
+ resolvedContent
856
+ );
771
857
  if (schema) normalizeBlockContent(content, schema, resolvedContent);
772
858
  const style = patchedStyle ? { ...asBucket(block.style), ...patchedStyle } : asBucket(block.style);
773
859
  const advanced = patchedAdvanced ? { ...asBucket(block.advanced), ...patchedAdvanced } : asBucket(block.advanced);
@@ -879,6 +965,36 @@ function EditableBlocks({
879
965
  }
880
966
  return merged;
881
967
  }, [page.blocks, inserted, order, removed]);
968
+ const loaderMap = useMemo(() => buildLoaderMap(blocks), [blocks]);
969
+ const loaderBlocks = useMemo(
970
+ () => renderBlocks.filter((block) => Object.hasOwn(loaderMap, block.type)).map((block) => ({
971
+ id: block.id,
972
+ type: block.type,
973
+ content: foldBlockContent(
974
+ block,
975
+ locale,
976
+ defaultLocale,
977
+ patches[block.id],
978
+ resolvedContent?.[block.id]
979
+ )
980
+ })),
981
+ [renderBlocks, loaderMap, locale, defaultLocale, patches, resolvedContent]
982
+ );
983
+ const liveData = useBlockLoaderData({
984
+ enabled: loaderBlocks.length > 0,
985
+ ...edit.blockDataUrl ? { url: edit.blockDataUrl } : {},
986
+ blocks: loaderBlocks,
987
+ locale,
988
+ defaultLocale,
989
+ ...enabledLocales?.length ? { enabledLocales } : {},
990
+ ...page.slug ? {
991
+ page: {
992
+ id: page.id,
993
+ slug: page.slug,
994
+ pageType: page.pageType ?? null
995
+ }
996
+ } : {}
997
+ });
882
998
  return /* @__PURE__ */ jsxs(Fragment, { children: [
883
999
  renderBlocks.map((block) => /* @__PURE__ */ jsx(
884
1000
  CmssyBlock,
@@ -894,7 +1010,7 @@ function EditableBlocks({
894
1010
  blockMap,
895
1011
  editable: true,
896
1012
  context,
897
- data: data?.[block.id]
1013
+ data: Object.hasOwn(liveData, block.id) ? liveData[block.id] : data?.[block.id]
898
1014
  },
899
1015
  block.id
900
1016
  )),
package/dist/index.cjs CHANGED
@@ -173,15 +173,7 @@ function renderResolvedBlock(block, map, locale, defaultLocale, options = {}) {
173
173
  )
174
174
  );
175
175
  }
176
- async function resolveBlocks(blocks, loaderMap, locale, defaultLocale, context, enabledLocales, options) {
177
- const contents = blocks.map(
178
- (block) => internal.getBlockContentForLanguage(
179
- block.content,
180
- locale,
181
- defaultLocale,
182
- enabledLocales?.length ? enabledLocales : void 0
183
- )
184
- );
176
+ async function resolveFoldedBlocks(blocks, contents, loaderMap, locale, context, options) {
185
177
  const schemas = options?.schemas;
186
178
  if (schemas) {
187
179
  if (options?.config) {
@@ -221,6 +213,24 @@ async function resolveBlocks(blocks, loaderMap, locale, defaultLocale, context,
221
213
  })
222
214
  );
223
215
  }
216
+ async function resolveBlocks(blocks, loaderMap, locale, defaultLocale, context, enabledLocales, options) {
217
+ const contents = blocks.map(
218
+ (block) => internal.getBlockContentForLanguage(
219
+ block.content,
220
+ locale,
221
+ defaultLocale,
222
+ enabledLocales?.length ? enabledLocales : void 0
223
+ )
224
+ );
225
+ return resolveFoldedBlocks(
226
+ blocks,
227
+ contents,
228
+ loaderMap,
229
+ locale,
230
+ context,
231
+ options
232
+ );
233
+ }
224
234
  var warned2 = false;
225
235
  async function resolveRenderLocale({
226
236
  locale,
@@ -542,6 +552,10 @@ async function resolveCmssyLayout(config, options) {
542
552
  element
543
553
  };
544
554
  }
555
+ function foldBlockContent(block, locale, defaultLocale, patchedContent, resolvedContent) {
556
+ const base = resolvedContent ? { ...resolvedContent } : internal.getBlockContentForLanguage(block.content, locale, defaultLocale);
557
+ return patchedContent ? { ...base, ...patchedContent } : base;
558
+ }
545
559
  function CmssyBlock({
546
560
  block,
547
561
  locale,
@@ -597,8 +611,13 @@ function CmssyBlock({
597
611
  )
598
612
  );
599
613
  }
600
- const base = resolvedContent ? { ...resolvedContent } : internal.getBlockContentForLanguage(block.content, locale, defaultLocale);
601
- const content = patchedContent ? { ...base, ...patchedContent } : base;
614
+ const content = foldBlockContent(
615
+ block,
616
+ locale,
617
+ defaultLocale,
618
+ patchedContent,
619
+ resolvedContent
620
+ );
602
621
  if (schema) internal.normalizeBlockContent(content, schema, resolvedContent);
603
622
  const style = patchedStyle ? { ...internal.asBucket(block.style), ...patchedStyle } : internal.asBucket(block.style);
604
623
  const advanced = patchedAdvanced ? { ...internal.asBucket(block.advanced), ...patchedAdvanced } : internal.asBucket(block.advanced);
@@ -620,6 +639,101 @@ function CmssyBlock({
620
639
  )
621
640
  );
622
641
  }
642
+ var MAX_BLOCKS = 50;
643
+ function parseBlockDataRequest(body) {
644
+ if (typeof body !== "object" || body === null) {
645
+ throw new Error("cmssy: block data request must be an object");
646
+ }
647
+ const raw = body;
648
+ if (typeof raw.locale !== "string" || !raw.locale) {
649
+ throw new Error("cmssy: block data request needs a locale");
650
+ }
651
+ if (typeof raw.defaultLocale !== "string" || !raw.defaultLocale) {
652
+ throw new Error("cmssy: block data request needs a defaultLocale");
653
+ }
654
+ if (!Array.isArray(raw.blocks)) {
655
+ throw new Error("cmssy: block data request needs a blocks array");
656
+ }
657
+ if (raw.blocks.length > MAX_BLOCKS) {
658
+ throw new Error(
659
+ `cmssy: block data request carries more than ${MAX_BLOCKS} blocks`
660
+ );
661
+ }
662
+ const blocks = raw.blocks.map((entry) => {
663
+ const block = entry;
664
+ if (typeof block?.id !== "string" || typeof block?.type !== "string") {
665
+ throw new Error("cmssy: every block needs an id and a type");
666
+ }
667
+ if (typeof block.content !== "object" || block.content === null) {
668
+ throw new Error(`cmssy: block "${block.id}" needs a content object`);
669
+ }
670
+ return {
671
+ id: block.id,
672
+ type: block.type,
673
+ content: { ...block.content }
674
+ };
675
+ });
676
+ const enabled = Array.isArray(raw.enabledLocales) ? raw.enabledLocales.filter(
677
+ (locale) => typeof locale === "string"
678
+ ) : void 0;
679
+ return {
680
+ blocks,
681
+ locale: raw.locale,
682
+ defaultLocale: raw.defaultLocale,
683
+ ...enabled?.length ? { enabledLocales: enabled } : {},
684
+ ...raw.page ? { page: raw.page } : {}
685
+ };
686
+ }
687
+ async function resolveBlockDataRequest(request, options) {
688
+ const loaderMap = buildLoaderMap(options.blocks);
689
+ const wanted = request.blocks.filter(
690
+ (block) => Object.hasOwn(loaderMap, block.type)
691
+ );
692
+ if (wanted.length === 0) return { data: {}, errors: {} };
693
+ const context = internal.buildBlockContext(
694
+ request.locale,
695
+ request.defaultLocale,
696
+ request.enabledLocales,
697
+ true,
698
+ options.forms,
699
+ { page: request.page, app: options.appContext }
700
+ );
701
+ const resolved = await resolveFoldedBlocks(
702
+ wanted,
703
+ wanted.map((block) => block.content),
704
+ loaderMap,
705
+ request.locale,
706
+ context,
707
+ {
708
+ schemas: core.blocksToSchemas(options.blocks),
709
+ ...options.config ? { config: options.config } : {},
710
+ ...options.workspaceId ? { workspaceId: options.workspaceId } : {}
711
+ }
712
+ );
713
+ const data = {};
714
+ const errors = {};
715
+ wanted.forEach((block, i) => {
716
+ const entry = resolved[i];
717
+ if (entry.error) errors[block.id] = entry.error;
718
+ else data[block.id] = entry.data;
719
+ });
720
+ return { data, errors };
721
+ }
722
+ async function handleBlockDataRequest(body, options) {
723
+ let request;
724
+ try {
725
+ request = parseBlockDataRequest(body);
726
+ } catch (err) {
727
+ return Response.json(
728
+ { message: err instanceof Error ? err.message : String(err) },
729
+ { status: 400 }
730
+ );
731
+ }
732
+ const result = await resolveBlockDataRequest(request, options);
733
+ return Response.json(result, {
734
+ headers: { "cache-control": "no-store" }
735
+ });
736
+ }
623
737
 
624
738
  Object.defineProperty(exports, "CmssyRequestError", {
625
739
  enumerable: true,
@@ -683,6 +797,9 @@ exports.CmssyServerPage = CmssyServerPage;
683
797
  exports.UnknownBlock = UnknownBlock;
684
798
  exports.buildBlockMap = buildBlockMap;
685
799
  exports.defineBlock = defineBlock;
800
+ exports.handleBlockDataRequest = handleBlockDataRequest;
801
+ exports.parseBlockDataRequest = parseBlockDataRequest;
802
+ exports.resolveBlockDataRequest = resolveBlockDataRequest;
686
803
  exports.resolveCmssyLayout = resolveCmssyLayout;
687
804
  exports.resolveCmssyLayoutSlot = resolveCmssyLayoutSlot;
688
805
  exports.resolveEditorBlockData = resolveEditorBlockData;
package/dist/index.d.cts CHANGED
@@ -1,12 +1,13 @@
1
1
  import { CmssyPageData, CmssyClientConfig, CmssyFormDefinition, CmssyBlockAuthContext, CmssyBlockWorkspace, CmssyLayoutGroup, CmssyBlockPage, RetryOption, FetchLike, CmssyConfig, CmssyRegionOf, CmssyRegionSettingsOf, RawBlock, CmssyBlockContext } from '@cmssy/core';
2
2
  export { AppToEditorMessage, BlockMeta, BlockPropsSchema, BlockRect, BlockSchema, BoundsMessage, BuildBlockContextExtra, ClickMessage, CmssyBlockAuthContext, CmssyBlockContext, CmssyBlockMember, CmssyBlockPage, CmssyBlockWorkspace, CmssyBranding, CmssyClient, CmssyClientConfig, CmssyFormDefinition, CmssyFormField, CmssyFormSettings, CmssyFormSubmitResponse, CmssyLayout, CmssyLayoutGroup, CmssyLocaleContext, CmssyLocalizedValue, CmssyModelDefinition, CmssyModelRecord, CmssyPageData, CmssyPageMeta, CmssyPageSummary, CmssyRecordList, CmssyRegion, CmssyRegionOf, CmssyRegionSettings, CmssyRegionSettingsOf, CmssyRequestError, CmssyRetryMode, CmssySiteConfig, CmssyTypedDocument, EditorToAppMessage, FetchLike, FetchLikeResponse, FetchPageOptions, FieldControl, FieldDefinition, FieldType, GraphqlRequestOptions, InferBlockContent, LayoutRegion, LayoutRegionId, MediaLike, PROTOCOL_VERSION, ParentReadyMessage, PatchMessage, PostTarget, QueryScopedOptions, RawBlock, RawLayoutBlock, ReadyMessage, RetryOption, RetryPolicy, SelectMessage, SubmitFormInput, TypedField, createCmssyClient, defineCmssyLayout, fields, graphqlRequest, isProtocolCompatible, mediaAlt, mediaUrl, mediaUrls, normalizeOrigin, parseEditorMessage, postToEditor } from '@cmssy/core';
3
- import { B as BlockDefinition, L as LayoutBlockPage, a as BlockMap } from './internal-server-Bkracpae.cjs';
4
- export { b as BlockProps, E as EditorBlockData, R as ResolveBlockDataOptions, c as ResolveLayoutBlockDataOptions, d as buildBlockMap, e as defineBlock, r as resolveEditorBlockData, f as resolveEditorLayoutBlockData } from './internal-server-Bkracpae.cjs';
3
+ import { B as BlockDefinition, L as LayoutBlockPage, a as BlockMap } from './resolve-block-data-Bkracpae.cjs';
4
+ export { b as BlockProps, E as EditorBlockData, R as ResolveBlockDataOptions, c as ResolveLayoutBlockDataOptions, d as buildBlockMap, e as defineBlock, r as resolveEditorBlockData, f as resolveEditorLayoutBlockData } from './resolve-block-data-Bkracpae.cjs';
5
5
  import { FieldDefinition } from '@cmssy/types';
6
6
  export { FieldCondition, FieldConditionGroup, FieldConditionLogic } from '@cmssy/types';
7
7
  export { buildBlockContext } from '@cmssy/core/internal';
8
8
  import * as react_jsx_runtime from 'react/jsx-runtime';
9
9
  import { ReactElement, ComponentType } from 'react';
10
+ export { CmssyBlockDataRequest, CmssyBlockDataRequestBlock, CmssyBlockDataRequestPage, CmssyBlockDataResult, ResolveBlockDataRequestOptions, handleBlockDataRequest, parseBlockDataRequest, resolveBlockDataRequest } from './internal-server.cjs';
10
11
 
11
12
  interface CmssyServerPageProps {
12
13
  page: CmssyPageData | null;
package/dist/index.d.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  import { CmssyPageData, CmssyClientConfig, CmssyFormDefinition, CmssyBlockAuthContext, CmssyBlockWorkspace, CmssyLayoutGroup, CmssyBlockPage, RetryOption, FetchLike, CmssyConfig, CmssyRegionOf, CmssyRegionSettingsOf, RawBlock, CmssyBlockContext } from '@cmssy/core';
2
2
  export { AppToEditorMessage, BlockMeta, BlockPropsSchema, BlockRect, BlockSchema, BoundsMessage, BuildBlockContextExtra, ClickMessage, CmssyBlockAuthContext, CmssyBlockContext, CmssyBlockMember, CmssyBlockPage, CmssyBlockWorkspace, CmssyBranding, CmssyClient, CmssyClientConfig, CmssyFormDefinition, CmssyFormField, CmssyFormSettings, CmssyFormSubmitResponse, CmssyLayout, CmssyLayoutGroup, CmssyLocaleContext, CmssyLocalizedValue, CmssyModelDefinition, CmssyModelRecord, CmssyPageData, CmssyPageMeta, CmssyPageSummary, CmssyRecordList, CmssyRegion, CmssyRegionOf, CmssyRegionSettings, CmssyRegionSettingsOf, CmssyRequestError, CmssyRetryMode, CmssySiteConfig, CmssyTypedDocument, EditorToAppMessage, FetchLike, FetchLikeResponse, FetchPageOptions, FieldControl, FieldDefinition, FieldType, GraphqlRequestOptions, InferBlockContent, LayoutRegion, LayoutRegionId, MediaLike, PROTOCOL_VERSION, ParentReadyMessage, PatchMessage, PostTarget, QueryScopedOptions, RawBlock, RawLayoutBlock, ReadyMessage, RetryOption, RetryPolicy, SelectMessage, SubmitFormInput, TypedField, createCmssyClient, defineCmssyLayout, fields, graphqlRequest, isProtocolCompatible, mediaAlt, mediaUrl, mediaUrls, normalizeOrigin, parseEditorMessage, postToEditor } from '@cmssy/core';
3
- import { B as BlockDefinition, L as LayoutBlockPage, a as BlockMap } from './internal-server-Bkracpae.js';
4
- export { b as BlockProps, E as EditorBlockData, R as ResolveBlockDataOptions, c as ResolveLayoutBlockDataOptions, d as buildBlockMap, e as defineBlock, r as resolveEditorBlockData, f as resolveEditorLayoutBlockData } from './internal-server-Bkracpae.js';
3
+ import { B as BlockDefinition, L as LayoutBlockPage, a as BlockMap } from './resolve-block-data-Bkracpae.js';
4
+ export { b as BlockProps, E as EditorBlockData, R as ResolveBlockDataOptions, c as ResolveLayoutBlockDataOptions, d as buildBlockMap, e as defineBlock, r as resolveEditorBlockData, f as resolveEditorLayoutBlockData } from './resolve-block-data-Bkracpae.js';
5
5
  import { FieldDefinition } from '@cmssy/types';
6
6
  export { FieldCondition, FieldConditionGroup, FieldConditionLogic } from '@cmssy/types';
7
7
  export { buildBlockContext } from '@cmssy/core/internal';
8
8
  import * as react_jsx_runtime from 'react/jsx-runtime';
9
9
  import { ReactElement, ComponentType } from 'react';
10
+ export { CmssyBlockDataRequest, CmssyBlockDataRequestBlock, CmssyBlockDataRequestPage, CmssyBlockDataResult, ResolveBlockDataRequestOptions, handleBlockDataRequest, parseBlockDataRequest, resolveBlockDataRequest } from './internal-server.js';
10
11
 
11
12
  interface CmssyServerPageProps {
12
13
  page: CmssyPageData | null;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { blocksToSchemas, resolveEditorOrigin } from '@cmssy/core';
2
2
  export { CmssyRequestError, PROTOCOL_VERSION, createCmssyClient, defineCmssyLayout, fields, graphqlRequest, isProtocolCompatible, mediaAlt, mediaUrl, mediaUrls, normalizeOrigin, parseEditorMessage, postToEditor } from '@cmssy/core';
3
- import { buildBlockContext, fetchLayouts, getBlockContentForLanguage, normalizeBlockContent, asBucket, resolveSiteLocales as resolveSiteLocales$1, resolveRelationContent } from '@cmssy/core/internal';
3
+ import { buildBlockContext, fetchLayouts, normalizeBlockContent, asBucket, resolveSiteLocales as resolveSiteLocales$1, getBlockContentForLanguage, resolveRelationContent } from '@cmssy/core/internal';
4
4
  export { buildBlockContext } from '@cmssy/core/internal';
5
5
  import { createElement } from 'react';
6
6
  import { BlockErrorBoundary } from '@cmssy/react/block-error-boundary';
@@ -173,15 +173,7 @@ function renderResolvedBlock(block, map, locale, defaultLocale, options = {}) {
173
173
  )
174
174
  );
175
175
  }
176
- async function resolveBlocks(blocks, loaderMap, locale, defaultLocale, context, enabledLocales, options) {
177
- const contents = blocks.map(
178
- (block) => getBlockContentForLanguage(
179
- block.content,
180
- locale,
181
- defaultLocale,
182
- enabledLocales?.length ? enabledLocales : void 0
183
- )
184
- );
176
+ async function resolveFoldedBlocks(blocks, contents, loaderMap, locale, context, options) {
185
177
  const schemas = options?.schemas;
186
178
  if (schemas) {
187
179
  if (options?.config) {
@@ -221,6 +213,24 @@ async function resolveBlocks(blocks, loaderMap, locale, defaultLocale, context,
221
213
  })
222
214
  );
223
215
  }
216
+ async function resolveBlocks(blocks, loaderMap, locale, defaultLocale, context, enabledLocales, options) {
217
+ const contents = blocks.map(
218
+ (block) => getBlockContentForLanguage(
219
+ block.content,
220
+ locale,
221
+ defaultLocale,
222
+ enabledLocales?.length ? enabledLocales : void 0
223
+ )
224
+ );
225
+ return resolveFoldedBlocks(
226
+ blocks,
227
+ contents,
228
+ loaderMap,
229
+ locale,
230
+ context,
231
+ options
232
+ );
233
+ }
224
234
  var warned2 = false;
225
235
  async function resolveRenderLocale({
226
236
  locale,
@@ -542,6 +552,10 @@ async function resolveCmssyLayout(config, options) {
542
552
  element
543
553
  };
544
554
  }
555
+ function foldBlockContent(block, locale, defaultLocale, patchedContent, resolvedContent) {
556
+ const base = resolvedContent ? { ...resolvedContent } : getBlockContentForLanguage(block.content, locale, defaultLocale);
557
+ return patchedContent ? { ...base, ...patchedContent } : base;
558
+ }
545
559
  function CmssyBlock({
546
560
  block,
547
561
  locale,
@@ -597,8 +611,13 @@ function CmssyBlock({
597
611
  )
598
612
  );
599
613
  }
600
- const base = resolvedContent ? { ...resolvedContent } : getBlockContentForLanguage(block.content, locale, defaultLocale);
601
- const content = patchedContent ? { ...base, ...patchedContent } : base;
614
+ const content = foldBlockContent(
615
+ block,
616
+ locale,
617
+ defaultLocale,
618
+ patchedContent,
619
+ resolvedContent
620
+ );
602
621
  if (schema) normalizeBlockContent(content, schema, resolvedContent);
603
622
  const style = patchedStyle ? { ...asBucket(block.style), ...patchedStyle } : asBucket(block.style);
604
623
  const advanced = patchedAdvanced ? { ...asBucket(block.advanced), ...patchedAdvanced } : asBucket(block.advanced);
@@ -620,5 +639,100 @@ function CmssyBlock({
620
639
  )
621
640
  );
622
641
  }
642
+ var MAX_BLOCKS = 50;
643
+ function parseBlockDataRequest(body) {
644
+ if (typeof body !== "object" || body === null) {
645
+ throw new Error("cmssy: block data request must be an object");
646
+ }
647
+ const raw = body;
648
+ if (typeof raw.locale !== "string" || !raw.locale) {
649
+ throw new Error("cmssy: block data request needs a locale");
650
+ }
651
+ if (typeof raw.defaultLocale !== "string" || !raw.defaultLocale) {
652
+ throw new Error("cmssy: block data request needs a defaultLocale");
653
+ }
654
+ if (!Array.isArray(raw.blocks)) {
655
+ throw new Error("cmssy: block data request needs a blocks array");
656
+ }
657
+ if (raw.blocks.length > MAX_BLOCKS) {
658
+ throw new Error(
659
+ `cmssy: block data request carries more than ${MAX_BLOCKS} blocks`
660
+ );
661
+ }
662
+ const blocks = raw.blocks.map((entry) => {
663
+ const block = entry;
664
+ if (typeof block?.id !== "string" || typeof block?.type !== "string") {
665
+ throw new Error("cmssy: every block needs an id and a type");
666
+ }
667
+ if (typeof block.content !== "object" || block.content === null) {
668
+ throw new Error(`cmssy: block "${block.id}" needs a content object`);
669
+ }
670
+ return {
671
+ id: block.id,
672
+ type: block.type,
673
+ content: { ...block.content }
674
+ };
675
+ });
676
+ const enabled = Array.isArray(raw.enabledLocales) ? raw.enabledLocales.filter(
677
+ (locale) => typeof locale === "string"
678
+ ) : void 0;
679
+ return {
680
+ blocks,
681
+ locale: raw.locale,
682
+ defaultLocale: raw.defaultLocale,
683
+ ...enabled?.length ? { enabledLocales: enabled } : {},
684
+ ...raw.page ? { page: raw.page } : {}
685
+ };
686
+ }
687
+ async function resolveBlockDataRequest(request, options) {
688
+ const loaderMap = buildLoaderMap(options.blocks);
689
+ const wanted = request.blocks.filter(
690
+ (block) => Object.hasOwn(loaderMap, block.type)
691
+ );
692
+ if (wanted.length === 0) return { data: {}, errors: {} };
693
+ const context = buildBlockContext(
694
+ request.locale,
695
+ request.defaultLocale,
696
+ request.enabledLocales,
697
+ true,
698
+ options.forms,
699
+ { page: request.page, app: options.appContext }
700
+ );
701
+ const resolved = await resolveFoldedBlocks(
702
+ wanted,
703
+ wanted.map((block) => block.content),
704
+ loaderMap,
705
+ request.locale,
706
+ context,
707
+ {
708
+ schemas: blocksToSchemas(options.blocks),
709
+ ...options.config ? { config: options.config } : {},
710
+ ...options.workspaceId ? { workspaceId: options.workspaceId } : {}
711
+ }
712
+ );
713
+ const data = {};
714
+ const errors = {};
715
+ wanted.forEach((block, i) => {
716
+ const entry = resolved[i];
717
+ if (entry.error) errors[block.id] = entry.error;
718
+ else data[block.id] = entry.data;
719
+ });
720
+ return { data, errors };
721
+ }
722
+ async function handleBlockDataRequest(body, options) {
723
+ let request;
724
+ try {
725
+ request = parseBlockDataRequest(body);
726
+ } catch (err) {
727
+ return Response.json(
728
+ { message: err instanceof Error ? err.message : String(err) },
729
+ { status: 400 }
730
+ );
731
+ }
732
+ const result = await resolveBlockDataRequest(request, options);
733
+ return Response.json(result, {
734
+ headers: { "cache-control": "no-store" }
735
+ });
736
+ }
623
737
 
624
- export { CmssyBlock, CmssyServerLayout, CmssyServerPage, UnknownBlock, buildBlockMap, defineBlock, resolveCmssyLayout, resolveCmssyLayoutSlot, resolveEditorBlockData, resolveEditorLayoutBlockData };
738
+ export { CmssyBlock, CmssyServerLayout, CmssyServerPage, UnknownBlock, buildBlockMap, defineBlock, handleBlockDataRequest, parseBlockDataRequest, resolveBlockDataRequest, resolveCmssyLayout, resolveCmssyLayoutSlot, resolveEditorBlockData, resolveEditorLayoutBlockData };
@@ -20,15 +20,7 @@ function blockErrorMessage(err) {
20
20
  function markBlockError(error) {
21
21
  return { [BLOCK_ERROR_KEY]: error };
22
22
  }
23
- async function resolveBlocks(blocks, loaderMap, locale, defaultLocale, context, enabledLocales, options) {
24
- const contents = blocks.map(
25
- (block) => internal.getBlockContentForLanguage(
26
- block.content,
27
- locale,
28
- defaultLocale,
29
- enabledLocales?.length ? enabledLocales : void 0
30
- )
31
- );
23
+ async function resolveFoldedBlocks(blocks, contents, loaderMap, locale, context, options) {
32
24
  const schemas = options?.schemas;
33
25
  if (schemas) {
34
26
  if (options?.config) {
@@ -68,6 +60,24 @@ async function resolveBlocks(blocks, loaderMap, locale, defaultLocale, context,
68
60
  })
69
61
  );
70
62
  }
63
+ async function resolveBlocks(blocks, loaderMap, locale, defaultLocale, context, enabledLocales, options) {
64
+ const contents = blocks.map(
65
+ (block) => internal.getBlockContentForLanguage(
66
+ block.content,
67
+ locale,
68
+ defaultLocale,
69
+ enabledLocales?.length ? enabledLocales : void 0
70
+ )
71
+ );
72
+ return resolveFoldedBlocks(
73
+ blocks,
74
+ contents,
75
+ loaderMap,
76
+ locale,
77
+ context,
78
+ options
79
+ );
80
+ }
71
81
 
72
82
  // src/components/resolve-block-data.ts
73
83
  function collectBlockData(blocks, resolved, isPreview) {
@@ -159,12 +169,110 @@ async function resolveEditorLayoutBlockData({
159
169
  async function resolveLayoutBlockData(options) {
160
170
  return (await resolveEditorLayoutBlockData(options)).data;
161
171
  }
172
+ var MAX_BLOCKS = 50;
173
+ function parseBlockDataRequest(body) {
174
+ if (typeof body !== "object" || body === null) {
175
+ throw new Error("cmssy: block data request must be an object");
176
+ }
177
+ const raw = body;
178
+ if (typeof raw.locale !== "string" || !raw.locale) {
179
+ throw new Error("cmssy: block data request needs a locale");
180
+ }
181
+ if (typeof raw.defaultLocale !== "string" || !raw.defaultLocale) {
182
+ throw new Error("cmssy: block data request needs a defaultLocale");
183
+ }
184
+ if (!Array.isArray(raw.blocks)) {
185
+ throw new Error("cmssy: block data request needs a blocks array");
186
+ }
187
+ if (raw.blocks.length > MAX_BLOCKS) {
188
+ throw new Error(
189
+ `cmssy: block data request carries more than ${MAX_BLOCKS} blocks`
190
+ );
191
+ }
192
+ const blocks = raw.blocks.map((entry) => {
193
+ const block = entry;
194
+ if (typeof block?.id !== "string" || typeof block?.type !== "string") {
195
+ throw new Error("cmssy: every block needs an id and a type");
196
+ }
197
+ if (typeof block.content !== "object" || block.content === null) {
198
+ throw new Error(`cmssy: block "${block.id}" needs a content object`);
199
+ }
200
+ return {
201
+ id: block.id,
202
+ type: block.type,
203
+ content: { ...block.content }
204
+ };
205
+ });
206
+ const enabled = Array.isArray(raw.enabledLocales) ? raw.enabledLocales.filter(
207
+ (locale) => typeof locale === "string"
208
+ ) : void 0;
209
+ return {
210
+ blocks,
211
+ locale: raw.locale,
212
+ defaultLocale: raw.defaultLocale,
213
+ ...enabled?.length ? { enabledLocales: enabled } : {},
214
+ ...raw.page ? { page: raw.page } : {}
215
+ };
216
+ }
217
+ async function resolveBlockDataRequest(request, options) {
218
+ const loaderMap = buildLoaderMap(options.blocks);
219
+ const wanted = request.blocks.filter(
220
+ (block) => Object.hasOwn(loaderMap, block.type)
221
+ );
222
+ if (wanted.length === 0) return { data: {}, errors: {} };
223
+ const context = internal.buildBlockContext(
224
+ request.locale,
225
+ request.defaultLocale,
226
+ request.enabledLocales,
227
+ true,
228
+ options.forms,
229
+ { page: request.page, app: options.appContext }
230
+ );
231
+ const resolved = await resolveFoldedBlocks(
232
+ wanted,
233
+ wanted.map((block) => block.content),
234
+ loaderMap,
235
+ request.locale,
236
+ context,
237
+ {
238
+ schemas: core.blocksToSchemas(options.blocks),
239
+ ...options.config ? { config: options.config } : {},
240
+ ...options.workspaceId ? { workspaceId: options.workspaceId } : {}
241
+ }
242
+ );
243
+ const data = {};
244
+ const errors = {};
245
+ wanted.forEach((block, i) => {
246
+ const entry = resolved[i];
247
+ if (entry.error) errors[block.id] = entry.error;
248
+ else data[block.id] = entry.data;
249
+ });
250
+ return { data, errors };
251
+ }
252
+ async function handleBlockDataRequest(body, options) {
253
+ let request;
254
+ try {
255
+ request = parseBlockDataRequest(body);
256
+ } catch (err) {
257
+ return Response.json(
258
+ { message: err instanceof Error ? err.message : String(err) },
259
+ { status: 400 }
260
+ );
261
+ }
262
+ const result = await resolveBlockDataRequest(request, options);
263
+ return Response.json(result, {
264
+ headers: { "cache-control": "no-store" }
265
+ });
266
+ }
162
267
 
163
268
  Object.defineProperty(exports, "blocksToSchemas", {
164
269
  enumerable: true,
165
270
  get: function () { return core.blocksToSchemas; }
166
271
  });
272
+ exports.handleBlockDataRequest = handleBlockDataRequest;
273
+ exports.parseBlockDataRequest = parseBlockDataRequest;
167
274
  exports.resolveBlockData = resolveBlockData;
275
+ exports.resolveBlockDataRequest = resolveBlockDataRequest;
168
276
  exports.resolveEditorBlockData = resolveEditorBlockData;
169
277
  exports.resolveEditorLayoutBlockData = resolveEditorLayoutBlockData;
170
278
  exports.resolveLayoutBlockData = resolveLayoutBlockData;
@@ -1,3 +1,45 @@
1
- export { E as EditorBlockData, R as ResolveBlockDataOptions, c as ResolveLayoutBlockDataOptions, g as resolveBlockData, r as resolveEditorBlockData, f as resolveEditorLayoutBlockData, h as resolveLayoutBlockData } from './internal-server-Bkracpae.cjs';
1
+ import { B as BlockDefinition } from './resolve-block-data-Bkracpae.cjs';
2
+ export { E as EditorBlockData, R as ResolveBlockDataOptions, c as ResolveLayoutBlockDataOptions, g as resolveBlockData, r as resolveEditorBlockData, f as resolveEditorLayoutBlockData, h as resolveLayoutBlockData } from './resolve-block-data-Bkracpae.cjs';
3
+ import { CmssyClientConfig, CmssyFormDefinition } from '@cmssy/core';
2
4
  export { blocksToSchemas } from '@cmssy/core';
3
5
  import 'react';
6
+
7
+ type CmssyBlockErrorSource = "loader" | "render" | "unregistered";
8
+ interface CmssyBlockError {
9
+ message: string;
10
+ source: CmssyBlockErrorSource;
11
+ }
12
+
13
+ interface CmssyBlockDataRequestBlock {
14
+ id: string;
15
+ type: string;
16
+ content: Record<string, unknown>;
17
+ }
18
+ interface CmssyBlockDataRequestPage {
19
+ id?: string;
20
+ slug: string;
21
+ pageType?: string | null;
22
+ }
23
+ interface CmssyBlockDataRequest {
24
+ blocks: CmssyBlockDataRequestBlock[];
25
+ locale: string;
26
+ defaultLocale: string;
27
+ enabledLocales?: string[];
28
+ page?: CmssyBlockDataRequestPage;
29
+ }
30
+ interface CmssyBlockDataResult {
31
+ data: Record<string, unknown>;
32
+ errors: Record<string, CmssyBlockError>;
33
+ }
34
+ interface ResolveBlockDataRequestOptions {
35
+ blocks: BlockDefinition[];
36
+ config?: CmssyClientConfig;
37
+ forms?: Record<string, CmssyFormDefinition>;
38
+ appContext?: Record<string, unknown>;
39
+ workspaceId?: string;
40
+ }
41
+ declare function parseBlockDataRequest(body: unknown): CmssyBlockDataRequest;
42
+ declare function resolveBlockDataRequest(request: CmssyBlockDataRequest, options: ResolveBlockDataRequestOptions): Promise<CmssyBlockDataResult>;
43
+ declare function handleBlockDataRequest(body: unknown, options: ResolveBlockDataRequestOptions): Promise<Response>;
44
+
45
+ export { type CmssyBlockDataRequest, type CmssyBlockDataRequestBlock, type CmssyBlockDataRequestPage, type CmssyBlockDataResult, type ResolveBlockDataRequestOptions, handleBlockDataRequest, parseBlockDataRequest, resolveBlockDataRequest };
@@ -1,3 +1,45 @@
1
- export { E as EditorBlockData, R as ResolveBlockDataOptions, c as ResolveLayoutBlockDataOptions, g as resolveBlockData, r as resolveEditorBlockData, f as resolveEditorLayoutBlockData, h as resolveLayoutBlockData } from './internal-server-Bkracpae.js';
1
+ import { B as BlockDefinition } from './resolve-block-data-Bkracpae.js';
2
+ export { E as EditorBlockData, R as ResolveBlockDataOptions, c as ResolveLayoutBlockDataOptions, g as resolveBlockData, r as resolveEditorBlockData, f as resolveEditorLayoutBlockData, h as resolveLayoutBlockData } from './resolve-block-data-Bkracpae.js';
3
+ import { CmssyClientConfig, CmssyFormDefinition } from '@cmssy/core';
2
4
  export { blocksToSchemas } from '@cmssy/core';
3
5
  import 'react';
6
+
7
+ type CmssyBlockErrorSource = "loader" | "render" | "unregistered";
8
+ interface CmssyBlockError {
9
+ message: string;
10
+ source: CmssyBlockErrorSource;
11
+ }
12
+
13
+ interface CmssyBlockDataRequestBlock {
14
+ id: string;
15
+ type: string;
16
+ content: Record<string, unknown>;
17
+ }
18
+ interface CmssyBlockDataRequestPage {
19
+ id?: string;
20
+ slug: string;
21
+ pageType?: string | null;
22
+ }
23
+ interface CmssyBlockDataRequest {
24
+ blocks: CmssyBlockDataRequestBlock[];
25
+ locale: string;
26
+ defaultLocale: string;
27
+ enabledLocales?: string[];
28
+ page?: CmssyBlockDataRequestPage;
29
+ }
30
+ interface CmssyBlockDataResult {
31
+ data: Record<string, unknown>;
32
+ errors: Record<string, CmssyBlockError>;
33
+ }
34
+ interface ResolveBlockDataRequestOptions {
35
+ blocks: BlockDefinition[];
36
+ config?: CmssyClientConfig;
37
+ forms?: Record<string, CmssyFormDefinition>;
38
+ appContext?: Record<string, unknown>;
39
+ workspaceId?: string;
40
+ }
41
+ declare function parseBlockDataRequest(body: unknown): CmssyBlockDataRequest;
42
+ declare function resolveBlockDataRequest(request: CmssyBlockDataRequest, options: ResolveBlockDataRequestOptions): Promise<CmssyBlockDataResult>;
43
+ declare function handleBlockDataRequest(body: unknown, options: ResolveBlockDataRequestOptions): Promise<Response>;
44
+
45
+ export { type CmssyBlockDataRequest, type CmssyBlockDataRequestBlock, type CmssyBlockDataRequestPage, type CmssyBlockDataResult, type ResolveBlockDataRequestOptions, handleBlockDataRequest, parseBlockDataRequest, resolveBlockDataRequest };
@@ -19,15 +19,7 @@ function blockErrorMessage(err) {
19
19
  function markBlockError(error) {
20
20
  return { [BLOCK_ERROR_KEY]: error };
21
21
  }
22
- async function resolveBlocks(blocks, loaderMap, locale, defaultLocale, context, enabledLocales, options) {
23
- const contents = blocks.map(
24
- (block) => getBlockContentForLanguage(
25
- block.content,
26
- locale,
27
- defaultLocale,
28
- enabledLocales?.length ? enabledLocales : void 0
29
- )
30
- );
22
+ async function resolveFoldedBlocks(blocks, contents, loaderMap, locale, context, options) {
31
23
  const schemas = options?.schemas;
32
24
  if (schemas) {
33
25
  if (options?.config) {
@@ -67,6 +59,24 @@ async function resolveBlocks(blocks, loaderMap, locale, defaultLocale, context,
67
59
  })
68
60
  );
69
61
  }
62
+ async function resolveBlocks(blocks, loaderMap, locale, defaultLocale, context, enabledLocales, options) {
63
+ const contents = blocks.map(
64
+ (block) => getBlockContentForLanguage(
65
+ block.content,
66
+ locale,
67
+ defaultLocale,
68
+ enabledLocales?.length ? enabledLocales : void 0
69
+ )
70
+ );
71
+ return resolveFoldedBlocks(
72
+ blocks,
73
+ contents,
74
+ loaderMap,
75
+ locale,
76
+ context,
77
+ options
78
+ );
79
+ }
70
80
 
71
81
  // src/components/resolve-block-data.ts
72
82
  function collectBlockData(blocks, resolved, isPreview) {
@@ -158,5 +168,100 @@ async function resolveEditorLayoutBlockData({
158
168
  async function resolveLayoutBlockData(options) {
159
169
  return (await resolveEditorLayoutBlockData(options)).data;
160
170
  }
171
+ var MAX_BLOCKS = 50;
172
+ function parseBlockDataRequest(body) {
173
+ if (typeof body !== "object" || body === null) {
174
+ throw new Error("cmssy: block data request must be an object");
175
+ }
176
+ const raw = body;
177
+ if (typeof raw.locale !== "string" || !raw.locale) {
178
+ throw new Error("cmssy: block data request needs a locale");
179
+ }
180
+ if (typeof raw.defaultLocale !== "string" || !raw.defaultLocale) {
181
+ throw new Error("cmssy: block data request needs a defaultLocale");
182
+ }
183
+ if (!Array.isArray(raw.blocks)) {
184
+ throw new Error("cmssy: block data request needs a blocks array");
185
+ }
186
+ if (raw.blocks.length > MAX_BLOCKS) {
187
+ throw new Error(
188
+ `cmssy: block data request carries more than ${MAX_BLOCKS} blocks`
189
+ );
190
+ }
191
+ const blocks = raw.blocks.map((entry) => {
192
+ const block = entry;
193
+ if (typeof block?.id !== "string" || typeof block?.type !== "string") {
194
+ throw new Error("cmssy: every block needs an id and a type");
195
+ }
196
+ if (typeof block.content !== "object" || block.content === null) {
197
+ throw new Error(`cmssy: block "${block.id}" needs a content object`);
198
+ }
199
+ return {
200
+ id: block.id,
201
+ type: block.type,
202
+ content: { ...block.content }
203
+ };
204
+ });
205
+ const enabled = Array.isArray(raw.enabledLocales) ? raw.enabledLocales.filter(
206
+ (locale) => typeof locale === "string"
207
+ ) : void 0;
208
+ return {
209
+ blocks,
210
+ locale: raw.locale,
211
+ defaultLocale: raw.defaultLocale,
212
+ ...enabled?.length ? { enabledLocales: enabled } : {},
213
+ ...raw.page ? { page: raw.page } : {}
214
+ };
215
+ }
216
+ async function resolveBlockDataRequest(request, options) {
217
+ const loaderMap = buildLoaderMap(options.blocks);
218
+ const wanted = request.blocks.filter(
219
+ (block) => Object.hasOwn(loaderMap, block.type)
220
+ );
221
+ if (wanted.length === 0) return { data: {}, errors: {} };
222
+ const context = buildBlockContext(
223
+ request.locale,
224
+ request.defaultLocale,
225
+ request.enabledLocales,
226
+ true,
227
+ options.forms,
228
+ { page: request.page, app: options.appContext }
229
+ );
230
+ const resolved = await resolveFoldedBlocks(
231
+ wanted,
232
+ wanted.map((block) => block.content),
233
+ loaderMap,
234
+ request.locale,
235
+ context,
236
+ {
237
+ schemas: blocksToSchemas(options.blocks),
238
+ ...options.config ? { config: options.config } : {},
239
+ ...options.workspaceId ? { workspaceId: options.workspaceId } : {}
240
+ }
241
+ );
242
+ const data = {};
243
+ const errors = {};
244
+ wanted.forEach((block, i) => {
245
+ const entry = resolved[i];
246
+ if (entry.error) errors[block.id] = entry.error;
247
+ else data[block.id] = entry.data;
248
+ });
249
+ return { data, errors };
250
+ }
251
+ async function handleBlockDataRequest(body, options) {
252
+ let request;
253
+ try {
254
+ request = parseBlockDataRequest(body);
255
+ } catch (err) {
256
+ return Response.json(
257
+ { message: err instanceof Error ? err.message : String(err) },
258
+ { status: 400 }
259
+ );
260
+ }
261
+ const result = await resolveBlockDataRequest(request, options);
262
+ return Response.json(result, {
263
+ headers: { "cache-control": "no-store" }
264
+ });
265
+ }
161
266
 
162
- export { resolveBlockData, resolveEditorBlockData, resolveEditorLayoutBlockData, resolveLayoutBlockData };
267
+ export { handleBlockDataRequest, parseBlockDataRequest, resolveBlockData, resolveBlockDataRequest, resolveEditorBlockData, resolveEditorLayoutBlockData, resolveLayoutBlockData };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmssy/react",
3
- "version": "16.6.0",
3
+ "version": "16.7.0",
4
4
  "description": "React blocks, renderers, data client and editor bridge for cmssy headless sites",
5
5
  "keywords": [
6
6
  "cmssy",
@@ -97,7 +97,7 @@
97
97
  },
98
98
  "dependencies": {
99
99
  "@cmssy/types": "0.49.0",
100
- "@cmssy/core": "16.6.0"
100
+ "@cmssy/core": "16.7.0"
101
101
  },
102
102
  "scripts": {
103
103
  "build": "tsup",