@cmssy/react 7.0.1 → 8.0.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
@@ -967,6 +967,14 @@ function useCart() {
967
967
  }
968
968
  return ctx;
969
969
  }
970
+ var productProps = {
971
+ modelSlug: core.fields.text({ label: "Model slug" }),
972
+ slugField: core.fields.text({ label: "Slug field" }),
973
+ slug: core.fields.text({ label: "Product slug" }),
974
+ nameField: core.fields.text({ label: "Name field" }),
975
+ priceField: core.fields.text({ label: "Price field" }),
976
+ imageField: core.fields.text({ label: "Image field" })
977
+ };
970
978
  function deriveAxes(variants) {
971
979
  const axes = /* @__PURE__ */ new Map();
972
980
  for (const variant of variants) {
@@ -987,8 +995,7 @@ function matchVariant(variants, selections) {
987
995
  )
988
996
  ) ?? null;
989
997
  }
990
- function ProductComponent({ content }) {
991
- const c = content;
998
+ function ProductComponent({ content: c }) {
992
999
  const { fetchProduct, addToCart } = useCart();
993
1000
  const injected = c.product ?? null;
994
1001
  const [product, setProduct] = react.useState(injected);
@@ -1096,14 +1103,7 @@ var productBlock = defineBlock({
1096
1103
  type: "product",
1097
1104
  label: "Product",
1098
1105
  category: "Commerce",
1099
- props: {
1100
- modelSlug: core.fields.text({ label: "Model slug" }),
1101
- slugField: core.fields.text({ label: "Slug field" }),
1102
- slug: core.fields.text({ label: "Product slug" }),
1103
- nameField: core.fields.text({ label: "Name field" }),
1104
- priceField: core.fields.text({ label: "Price field" }),
1105
- imageField: core.fields.text({ label: "Image field" })
1106
- },
1106
+ props: productProps,
1107
1107
  component: ProductComponent
1108
1108
  });
1109
1109
  function CartComponent() {
@@ -1221,9 +1221,13 @@ var cartBlock = defineBlock({
1221
1221
  props: {},
1222
1222
  component: CartComponent
1223
1223
  });
1224
+ var checkoutProps = {
1225
+ successMessage: core.fields.text({ label: "Success message" })
1226
+ };
1224
1227
  var EMAIL_RE = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
1225
- function CheckoutComponent({ content }) {
1226
- const c = content;
1228
+ function CheckoutComponent({
1229
+ content: c
1230
+ }) {
1227
1231
  const { cart, loading, checkout } = useCart();
1228
1232
  const [email, setEmail] = react.useState("");
1229
1233
  const [busy, setBusy] = react.useState(false);
@@ -1295,9 +1299,7 @@ var checkoutBlock = defineBlock({
1295
1299
  type: "checkout",
1296
1300
  label: "Checkout",
1297
1301
  category: "Commerce",
1298
- props: {
1299
- successMessage: core.fields.text({ label: "Success message" })
1300
- },
1302
+ props: checkoutProps,
1301
1303
  component: CheckoutComponent
1302
1304
  });
1303
1305
  function useCmssyOrders(options = {}) {
package/dist/client.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { BlockSchema, BlockMeta, CmssyPageData, CmssyFormDefinition, CmssyLayoutGroup, CmssyLocaleContext } from '@cmssy/core';
3
3
  export { CmssyCart, CmssyCartDiscount, CmssyCartItem, CmssyCartItemSnapshot, CmssyOrder, CmssyOrderItem, CmssyProduct, CmssyProductVariant, formatPrice, fractionDigits, fromMinorUnits, toMinorUnits } from '@cmssy/core';
4
- import { B as BlockDefinition } from './registry-DnhIHuzF.cjs';
4
+ import { B as BlockDefinition } from './registry-Bfq4Pq18.cjs';
5
5
  import { ReactNode } from 'react';
6
6
  import { CmssyAuthUser, CmssyAuthActionResult, CmssyCart, CmssyAddToCartOptions, CmssyCheckoutInput, CmssyOrder, CmssyProduct, UseCmssyOrderOptions, UseCmssyOrdersOptions } from '@cmssy/types';
7
7
  export { CmssyAddToCartOptions, CmssyAuthActionResult, CmssyAuthUser, CmssyCheckoutInput, UseCmssyOrderOptions, UseCmssyOrdersOptions } from '@cmssy/types';
package/dist/client.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { BlockSchema, BlockMeta, CmssyPageData, CmssyFormDefinition, CmssyLayoutGroup, CmssyLocaleContext } from '@cmssy/core';
3
3
  export { CmssyCart, CmssyCartDiscount, CmssyCartItem, CmssyCartItemSnapshot, CmssyOrder, CmssyOrderItem, CmssyProduct, CmssyProductVariant, formatPrice, fractionDigits, fromMinorUnits, toMinorUnits } from '@cmssy/core';
4
- import { B as BlockDefinition } from './registry-DnhIHuzF.js';
4
+ import { B as BlockDefinition } from './registry-Bfq4Pq18.js';
5
5
  import { ReactNode } from 'react';
6
6
  import { CmssyAuthUser, CmssyAuthActionResult, CmssyCart, CmssyAddToCartOptions, CmssyCheckoutInput, CmssyOrder, CmssyProduct, UseCmssyOrderOptions, UseCmssyOrdersOptions } from '@cmssy/types';
7
7
  export { CmssyAddToCartOptions, CmssyAuthActionResult, CmssyAuthUser, CmssyCheckoutInput, UseCmssyOrderOptions, UseCmssyOrdersOptions } from '@cmssy/types';
package/dist/client.js CHANGED
@@ -966,6 +966,14 @@ function useCart() {
966
966
  }
967
967
  return ctx;
968
968
  }
969
+ var productProps = {
970
+ modelSlug: fields.text({ label: "Model slug" }),
971
+ slugField: fields.text({ label: "Slug field" }),
972
+ slug: fields.text({ label: "Product slug" }),
973
+ nameField: fields.text({ label: "Name field" }),
974
+ priceField: fields.text({ label: "Price field" }),
975
+ imageField: fields.text({ label: "Image field" })
976
+ };
969
977
  function deriveAxes(variants) {
970
978
  const axes = /* @__PURE__ */ new Map();
971
979
  for (const variant of variants) {
@@ -986,8 +994,7 @@ function matchVariant(variants, selections) {
986
994
  )
987
995
  ) ?? null;
988
996
  }
989
- function ProductComponent({ content }) {
990
- const c = content;
997
+ function ProductComponent({ content: c }) {
991
998
  const { fetchProduct, addToCart } = useCart();
992
999
  const injected = c.product ?? null;
993
1000
  const [product, setProduct] = useState(injected);
@@ -1095,14 +1102,7 @@ var productBlock = defineBlock({
1095
1102
  type: "product",
1096
1103
  label: "Product",
1097
1104
  category: "Commerce",
1098
- props: {
1099
- modelSlug: fields.text({ label: "Model slug" }),
1100
- slugField: fields.text({ label: "Slug field" }),
1101
- slug: fields.text({ label: "Product slug" }),
1102
- nameField: fields.text({ label: "Name field" }),
1103
- priceField: fields.text({ label: "Price field" }),
1104
- imageField: fields.text({ label: "Image field" })
1105
- },
1105
+ props: productProps,
1106
1106
  component: ProductComponent
1107
1107
  });
1108
1108
  function CartComponent() {
@@ -1220,9 +1220,13 @@ var cartBlock = defineBlock({
1220
1220
  props: {},
1221
1221
  component: CartComponent
1222
1222
  });
1223
+ var checkoutProps = {
1224
+ successMessage: fields.text({ label: "Success message" })
1225
+ };
1223
1226
  var EMAIL_RE = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
1224
- function CheckoutComponent({ content }) {
1225
- const c = content;
1227
+ function CheckoutComponent({
1228
+ content: c
1229
+ }) {
1226
1230
  const { cart, loading, checkout } = useCart();
1227
1231
  const [email, setEmail] = useState("");
1228
1232
  const [busy, setBusy] = useState(false);
@@ -1294,9 +1298,7 @@ var checkoutBlock = defineBlock({
1294
1298
  type: "checkout",
1295
1299
  label: "Checkout",
1296
1300
  category: "Commerce",
1297
- props: {
1298
- successMessage: fields.text({ label: "Success message" })
1299
- },
1301
+ props: checkoutProps,
1300
1302
  component: CheckoutComponent
1301
1303
  });
1302
1304
  function useCmssyOrders(options = {}) {
package/dist/index.cjs CHANGED
@@ -130,7 +130,7 @@ async function resolveRenderLocale({
130
130
  if (process.env.NODE_ENV !== "production" && !warned2) {
131
131
  warned2 = true;
132
132
  console.warn(
133
- '[cmssy] Rendering in "en": no locale was passed and no config was given to look the workspace\'s default language up. Pass `locale` (from the route) or `config`, or a workspace whose default language is not English will render its chrome in the wrong one.'
133
+ '[cmssy] Rendering in "en": no locale was passed and no config was given to look the workspace\'s default language up. Pass `locale` (from the route) or `config`, or a workspace whose default language is not English will render its header and footer in the wrong one.'
134
134
  );
135
135
  }
136
136
  return {
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { CmssyPageData, CmssyClientConfig, CmssyFormDefinition, CmssyBlockAuthContext, CmssyBlockWorkspace, CmssyLayoutGroup, RawBlock, CmssyBlockContext } from '@cmssy/core';
2
- export { AppToEditorMessage, BlockMeta, BlockRect, BlockSchema, BoundsMessage, BuildBlockContextExtra, ClickMessage, CmssyAddress, CmssyBlockAuthContext, CmssyBlockContext, CmssyBlockMember, CmssyBlockWorkspace, CmssyBranding, CmssyCart, CmssyCartDiscount, CmssyCartItem, CmssyCartItemSnapshot, CmssyClient, CmssyClientConfig, CmssyFormDefinition, CmssyFormField, CmssyFormSettings, CmssyFormSubmitResponse, CmssyLayoutGroup, CmssyLayoutSettings, CmssyLocaleContext, CmssyLocalizedValue, CmssyModelDefinition, CmssyModelRecord, CmssyOrder, CmssyOrderDiscount, CmssyOrderItem, CmssyOrderTaxSummaryLine, CmssyPageData, CmssyPageMeta, CmssyPageSummary, CmssyPriceTier, CmssyProduct, CmssyProductVariant, CmssyRecordList, CmssyRequestError, CmssyShippingMethod, CmssySiteConfig, CmssySiteLocales, CmssyTaxSummaryLine, DEFAULT_CMSSY_API_URL, EditorToAppMessage, FORM_QUERY, FetchLike, FetchLikeResponse, FetchPageOptions, FieldControl, FieldDefinition, FieldType, GraphqlRequestOptions, MODEL_DEFINITIONS_QUERY, MODEL_RECORDS_QUERY, PROTOCOL_VERSION, ParentReadyMessage, PatchMessage, PostTarget, QueryScopedOptions, RawBlock, RawLayoutBlock, ReadyMessage, RetryPolicy, SITE_CONFIG_QUERY, SUBMIT_FORM_MUTATION, SelectMessage, SubmitFormInput, buildBlockContext, buildLocaleSwitchHref, collectFormIds, createCmssyClient, fetchLayouts, fetchPage, fetchPageById, fetchPageMeta, fetchPages, fetchSiteConfig, fields, getBlockContentForLanguage, graphqlRequest, isProtocolCompatible, localizeHref, localizeHtmlLinks, normalizeOrigin, normalizeSlug, parseEditorMessage, postToEditor, resolveApiUrl, resolveForms, resolvePublicUrl, resolveSiteLocales, resolveWorkspaceId, splitLocaleFromPath } from '@cmssy/core';
3
- import { B as BlockDefinition, a as BlockMap } from './registry-DnhIHuzF.cjs';
4
- export { b as blocksToMeta, c as blocksToSchemas, d as buildBlockMap, e as defineBlock } from './registry-DnhIHuzF.cjs';
2
+ export { AppToEditorMessage, BlockMeta, BlockPropsSchema, BlockRect, BlockSchema, BoundsMessage, BuildBlockContextExtra, ClickMessage, CmssyAddress, CmssyBlockAuthContext, CmssyBlockContext, CmssyBlockMember, CmssyBlockWorkspace, CmssyBranding, CmssyCart, CmssyCartDiscount, CmssyCartItem, CmssyCartItemSnapshot, CmssyClient, CmssyClientConfig, CmssyFormDefinition, CmssyFormField, CmssyFormSettings, CmssyFormSubmitResponse, CmssyLayoutGroup, CmssyLayoutSettings, CmssyLocaleContext, CmssyLocalizedValue, CmssyModelDefinition, CmssyModelRecord, CmssyOrder, CmssyOrderDiscount, CmssyOrderItem, CmssyOrderTaxSummaryLine, CmssyPageData, CmssyPageMeta, CmssyPageSummary, CmssyPriceTier, CmssyProduct, CmssyProductVariant, CmssyRecordList, CmssyRequestError, CmssyShippingMethod, CmssySiteConfig, CmssySiteLocales, CmssyTaxSummaryLine, DEFAULT_CMSSY_API_URL, EditorToAppMessage, FORM_QUERY, FetchLike, FetchLikeResponse, FetchPageOptions, FieldControl, FieldDefinition, FieldType, GraphqlRequestOptions, InferBlockContent, MODEL_DEFINITIONS_QUERY, MODEL_RECORDS_QUERY, PROTOCOL_VERSION, ParentReadyMessage, PatchMessage, PostTarget, QueryScopedOptions, RawBlock, RawLayoutBlock, ReadyMessage, RetryPolicy, SITE_CONFIG_QUERY, SUBMIT_FORM_MUTATION, SelectMessage, SubmitFormInput, TypedField, buildBlockContext, buildLocaleSwitchHref, collectFormIds, createCmssyClient, fetchLayouts, fetchPage, fetchPageById, fetchPageMeta, fetchPages, fetchSiteConfig, fields, getBlockContentForLanguage, graphqlRequest, isProtocolCompatible, localizeHref, localizeHtmlLinks, normalizeOrigin, normalizeSlug, parseEditorMessage, postToEditor, resolveApiUrl, resolveForms, resolvePublicUrl, resolveSiteLocales, resolveWorkspaceId, splitLocaleFromPath } from '@cmssy/core';
3
+ import { B as BlockDefinition, a as BlockMap } from './registry-Bfq4Pq18.cjs';
4
+ export { b as BlockProps, c as blocksToMeta, d as blocksToSchemas, e as buildBlockMap, f as defineBlock } from './registry-Bfq4Pq18.cjs';
5
5
  import * as react_jsx_runtime from 'react/jsx-runtime';
6
6
  export { FieldCondition, FieldConditionGroup, FieldConditionLogic, evaluateFieldConditionGroup } from '@cmssy/types';
7
7
  import 'react';
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { CmssyPageData, CmssyClientConfig, CmssyFormDefinition, CmssyBlockAuthContext, CmssyBlockWorkspace, CmssyLayoutGroup, RawBlock, CmssyBlockContext } from '@cmssy/core';
2
- export { AppToEditorMessage, BlockMeta, BlockRect, BlockSchema, BoundsMessage, BuildBlockContextExtra, ClickMessage, CmssyAddress, CmssyBlockAuthContext, CmssyBlockContext, CmssyBlockMember, CmssyBlockWorkspace, CmssyBranding, CmssyCart, CmssyCartDiscount, CmssyCartItem, CmssyCartItemSnapshot, CmssyClient, CmssyClientConfig, CmssyFormDefinition, CmssyFormField, CmssyFormSettings, CmssyFormSubmitResponse, CmssyLayoutGroup, CmssyLayoutSettings, CmssyLocaleContext, CmssyLocalizedValue, CmssyModelDefinition, CmssyModelRecord, CmssyOrder, CmssyOrderDiscount, CmssyOrderItem, CmssyOrderTaxSummaryLine, CmssyPageData, CmssyPageMeta, CmssyPageSummary, CmssyPriceTier, CmssyProduct, CmssyProductVariant, CmssyRecordList, CmssyRequestError, CmssyShippingMethod, CmssySiteConfig, CmssySiteLocales, CmssyTaxSummaryLine, DEFAULT_CMSSY_API_URL, EditorToAppMessage, FORM_QUERY, FetchLike, FetchLikeResponse, FetchPageOptions, FieldControl, FieldDefinition, FieldType, GraphqlRequestOptions, MODEL_DEFINITIONS_QUERY, MODEL_RECORDS_QUERY, PROTOCOL_VERSION, ParentReadyMessage, PatchMessage, PostTarget, QueryScopedOptions, RawBlock, RawLayoutBlock, ReadyMessage, RetryPolicy, SITE_CONFIG_QUERY, SUBMIT_FORM_MUTATION, SelectMessage, SubmitFormInput, buildBlockContext, buildLocaleSwitchHref, collectFormIds, createCmssyClient, fetchLayouts, fetchPage, fetchPageById, fetchPageMeta, fetchPages, fetchSiteConfig, fields, getBlockContentForLanguage, graphqlRequest, isProtocolCompatible, localizeHref, localizeHtmlLinks, normalizeOrigin, normalizeSlug, parseEditorMessage, postToEditor, resolveApiUrl, resolveForms, resolvePublicUrl, resolveSiteLocales, resolveWorkspaceId, splitLocaleFromPath } from '@cmssy/core';
3
- import { B as BlockDefinition, a as BlockMap } from './registry-DnhIHuzF.js';
4
- export { b as blocksToMeta, c as blocksToSchemas, d as buildBlockMap, e as defineBlock } from './registry-DnhIHuzF.js';
2
+ export { AppToEditorMessage, BlockMeta, BlockPropsSchema, BlockRect, BlockSchema, BoundsMessage, BuildBlockContextExtra, ClickMessage, CmssyAddress, CmssyBlockAuthContext, CmssyBlockContext, CmssyBlockMember, CmssyBlockWorkspace, CmssyBranding, CmssyCart, CmssyCartDiscount, CmssyCartItem, CmssyCartItemSnapshot, CmssyClient, CmssyClientConfig, CmssyFormDefinition, CmssyFormField, CmssyFormSettings, CmssyFormSubmitResponse, CmssyLayoutGroup, CmssyLayoutSettings, CmssyLocaleContext, CmssyLocalizedValue, CmssyModelDefinition, CmssyModelRecord, CmssyOrder, CmssyOrderDiscount, CmssyOrderItem, CmssyOrderTaxSummaryLine, CmssyPageData, CmssyPageMeta, CmssyPageSummary, CmssyPriceTier, CmssyProduct, CmssyProductVariant, CmssyRecordList, CmssyRequestError, CmssyShippingMethod, CmssySiteConfig, CmssySiteLocales, CmssyTaxSummaryLine, DEFAULT_CMSSY_API_URL, EditorToAppMessage, FORM_QUERY, FetchLike, FetchLikeResponse, FetchPageOptions, FieldControl, FieldDefinition, FieldType, GraphqlRequestOptions, InferBlockContent, MODEL_DEFINITIONS_QUERY, MODEL_RECORDS_QUERY, PROTOCOL_VERSION, ParentReadyMessage, PatchMessage, PostTarget, QueryScopedOptions, RawBlock, RawLayoutBlock, ReadyMessage, RetryPolicy, SITE_CONFIG_QUERY, SUBMIT_FORM_MUTATION, SelectMessage, SubmitFormInput, TypedField, buildBlockContext, buildLocaleSwitchHref, collectFormIds, createCmssyClient, fetchLayouts, fetchPage, fetchPageById, fetchPageMeta, fetchPages, fetchSiteConfig, fields, getBlockContentForLanguage, graphqlRequest, isProtocolCompatible, localizeHref, localizeHtmlLinks, normalizeOrigin, normalizeSlug, parseEditorMessage, postToEditor, resolveApiUrl, resolveForms, resolvePublicUrl, resolveSiteLocales, resolveWorkspaceId, splitLocaleFromPath } from '@cmssy/core';
3
+ import { B as BlockDefinition, a as BlockMap } from './registry-Bfq4Pq18.js';
4
+ export { b as BlockProps, c as blocksToMeta, d as blocksToSchemas, e as buildBlockMap, f as defineBlock } from './registry-Bfq4Pq18.js';
5
5
  import * as react_jsx_runtime from 'react/jsx-runtime';
6
6
  export { FieldCondition, FieldConditionGroup, FieldConditionLogic, evaluateFieldConditionGroup } from '@cmssy/types';
7
7
  import 'react';
package/dist/index.js CHANGED
@@ -129,7 +129,7 @@ async function resolveRenderLocale({
129
129
  if (process.env.NODE_ENV !== "production" && !warned2) {
130
130
  warned2 = true;
131
131
  console.warn(
132
- '[cmssy] Rendering in "en": no locale was passed and no config was given to look the workspace\'s default language up. Pass `locale` (from the route) or `config`, or a workspace whose default language is not English will render its chrome in the wrong one.'
132
+ '[cmssy] Rendering in "en": no locale was passed and no config was given to look the workspace\'s default language up. Pass `locale` (from the route) or `config`, or a workspace whose default language is not English will render its header and footer in the wrong one.'
133
133
  );
134
134
  }
135
135
  return {
@@ -0,0 +1,107 @@
1
+ import { ComponentType } from 'react';
2
+ import { FieldDefinition, CmssyBlockContext, BlockPropsSchema, InferBlockContent, BlockMeta, BlockSchema } from '@cmssy/core';
3
+
4
+ /**
5
+ * The props a block component receives, derived from the fields it declares:
6
+ *
7
+ * const props = { headline: fields.text({ required: true }) };
8
+ * export function Hero({ content }: BlockProps<typeof props>) { … }
9
+ *
10
+ * Type the component this way and the schema is the only place a field is
11
+ * named. Rename `headline` and the component stops compiling, instead of
12
+ * quietly rendering nothing.
13
+ */
14
+ interface BlockProps<P extends BlockPropsSchema, D = unknown> {
15
+ content: InferBlockContent<P>;
16
+ style?: Record<string, unknown>;
17
+ advanced?: Record<string, unknown>;
18
+ context?: CmssyBlockContext;
19
+ data?: D;
20
+ }
21
+ type Identical<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false;
22
+ /** What the component says about the fields the schema declares - nothing else. */
23
+ type SchemaSlice<C, Content> = {
24
+ [K in keyof Content]: K extends keyof C ? C[K] : never;
25
+ };
26
+ /**
27
+ * Rejects a component whose `content` disagrees with the schema about a field
28
+ * the schema declares. Extra keys are fine - a loader may inject content the
29
+ * editor knows nothing about (see `productBlock`) - but a declared field must
30
+ * have the name and the type the schema gives it.
31
+ *
32
+ * Without this, a hand-written content type only had to be *compatible* with the
33
+ * schema, and since every optional field is optional on both sides, a field the
34
+ * component invented was compatible with the one it forgot.
35
+ */
36
+ type ContentGuard<C, P extends BlockPropsSchema> = Identical<SchemaSlice<C, InferBlockContent<P>>, InferBlockContent<P>> extends true ? unknown : {
37
+ CONTENT_MUST_BE_TYPED_AS_BlockProps_OF_THIS_PROPS: InferBlockContent<P>;
38
+ };
39
+ interface BlockLoaderArgs {
40
+ content: Record<string, unknown>;
41
+ context?: CmssyBlockContext;
42
+ }
43
+ type BlockLoader = (args: BlockLoaderArgs) => Promise<unknown> | unknown;
44
+ interface BlockDefinition {
45
+ type: string;
46
+ label?: string;
47
+ category?: string;
48
+ icon?: string;
49
+ layoutPositions?: string[];
50
+ /**
51
+ * One-line semantic description of what the block is and when it belongs on a
52
+ * page. Surfaced to the AI page composer to guide block selection and order.
53
+ */
54
+ description?: string;
55
+ props: Record<string, FieldDefinition>;
56
+ /**
57
+ * Optional server-side data loader. Run by CmssyServerPage during SSR; its
58
+ * result is passed to the component as the `data` prop. Not run in the
59
+ * editor (the component receives `data: undefined` there).
60
+ *
61
+ * The result crosses the server→client boundary when the block component is a
62
+ * Client Component, so it must be RSC-serializable (plain objects, arrays and
63
+ * primitives - no functions, class instances, etc.).
64
+ */
65
+ loader?: BlockLoader;
66
+ component: ComponentType<{
67
+ content: Record<string, unknown>;
68
+ style?: Record<string, unknown>;
69
+ advanced?: Record<string, unknown>;
70
+ context?: CmssyBlockContext;
71
+ data?: unknown;
72
+ }>;
73
+ }
74
+ declare function defineBlock<P extends BlockPropsSchema, C = InferBlockContent<P>, D = unknown, S extends Record<string, unknown> = Record<string, unknown>, A extends Record<string, unknown> = Record<string, unknown>>(def: {
75
+ type: string;
76
+ label?: string;
77
+ category?: string;
78
+ icon?: string;
79
+ layoutPositions?: string[];
80
+ description?: string;
81
+ props: P;
82
+ loader?: (args: {
83
+ content: InferBlockContent<P>;
84
+ context?: CmssyBlockContext;
85
+ }) => Promise<D> | D;
86
+ component: ComponentType<{
87
+ content: C;
88
+ style?: S;
89
+ advanced?: A;
90
+ context?: CmssyBlockContext;
91
+ data?: D;
92
+ }>;
93
+ } & ContentGuard<C, P>): BlockDefinition;
94
+ type BlockMap = Record<string, ComponentType<{
95
+ content: Record<string, unknown>;
96
+ style?: Record<string, unknown>;
97
+ advanced?: Record<string, unknown>;
98
+ context?: CmssyBlockContext;
99
+ data?: unknown;
100
+ }>>;
101
+ declare function buildBlockMap(blocks: BlockDefinition[]): BlockMap;
102
+ declare function blocksToSchemas(blocks: BlockDefinition[]): Record<string, BlockSchema>;
103
+ declare function blocksToMeta(blocks: BlockDefinition[], defaults?: {
104
+ category?: string;
105
+ }): Record<string, BlockMeta>;
106
+
107
+ export { type BlockDefinition as B, type BlockMap as a, type BlockProps as b, blocksToMeta as c, blocksToSchemas as d, buildBlockMap as e, defineBlock as f };
@@ -0,0 +1,107 @@
1
+ import { ComponentType } from 'react';
2
+ import { FieldDefinition, CmssyBlockContext, BlockPropsSchema, InferBlockContent, BlockMeta, BlockSchema } from '@cmssy/core';
3
+
4
+ /**
5
+ * The props a block component receives, derived from the fields it declares:
6
+ *
7
+ * const props = { headline: fields.text({ required: true }) };
8
+ * export function Hero({ content }: BlockProps<typeof props>) { … }
9
+ *
10
+ * Type the component this way and the schema is the only place a field is
11
+ * named. Rename `headline` and the component stops compiling, instead of
12
+ * quietly rendering nothing.
13
+ */
14
+ interface BlockProps<P extends BlockPropsSchema, D = unknown> {
15
+ content: InferBlockContent<P>;
16
+ style?: Record<string, unknown>;
17
+ advanced?: Record<string, unknown>;
18
+ context?: CmssyBlockContext;
19
+ data?: D;
20
+ }
21
+ type Identical<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false;
22
+ /** What the component says about the fields the schema declares - nothing else. */
23
+ type SchemaSlice<C, Content> = {
24
+ [K in keyof Content]: K extends keyof C ? C[K] : never;
25
+ };
26
+ /**
27
+ * Rejects a component whose `content` disagrees with the schema about a field
28
+ * the schema declares. Extra keys are fine - a loader may inject content the
29
+ * editor knows nothing about (see `productBlock`) - but a declared field must
30
+ * have the name and the type the schema gives it.
31
+ *
32
+ * Without this, a hand-written content type only had to be *compatible* with the
33
+ * schema, and since every optional field is optional on both sides, a field the
34
+ * component invented was compatible with the one it forgot.
35
+ */
36
+ type ContentGuard<C, P extends BlockPropsSchema> = Identical<SchemaSlice<C, InferBlockContent<P>>, InferBlockContent<P>> extends true ? unknown : {
37
+ CONTENT_MUST_BE_TYPED_AS_BlockProps_OF_THIS_PROPS: InferBlockContent<P>;
38
+ };
39
+ interface BlockLoaderArgs {
40
+ content: Record<string, unknown>;
41
+ context?: CmssyBlockContext;
42
+ }
43
+ type BlockLoader = (args: BlockLoaderArgs) => Promise<unknown> | unknown;
44
+ interface BlockDefinition {
45
+ type: string;
46
+ label?: string;
47
+ category?: string;
48
+ icon?: string;
49
+ layoutPositions?: string[];
50
+ /**
51
+ * One-line semantic description of what the block is and when it belongs on a
52
+ * page. Surfaced to the AI page composer to guide block selection and order.
53
+ */
54
+ description?: string;
55
+ props: Record<string, FieldDefinition>;
56
+ /**
57
+ * Optional server-side data loader. Run by CmssyServerPage during SSR; its
58
+ * result is passed to the component as the `data` prop. Not run in the
59
+ * editor (the component receives `data: undefined` there).
60
+ *
61
+ * The result crosses the server→client boundary when the block component is a
62
+ * Client Component, so it must be RSC-serializable (plain objects, arrays and
63
+ * primitives - no functions, class instances, etc.).
64
+ */
65
+ loader?: BlockLoader;
66
+ component: ComponentType<{
67
+ content: Record<string, unknown>;
68
+ style?: Record<string, unknown>;
69
+ advanced?: Record<string, unknown>;
70
+ context?: CmssyBlockContext;
71
+ data?: unknown;
72
+ }>;
73
+ }
74
+ declare function defineBlock<P extends BlockPropsSchema, C = InferBlockContent<P>, D = unknown, S extends Record<string, unknown> = Record<string, unknown>, A extends Record<string, unknown> = Record<string, unknown>>(def: {
75
+ type: string;
76
+ label?: string;
77
+ category?: string;
78
+ icon?: string;
79
+ layoutPositions?: string[];
80
+ description?: string;
81
+ props: P;
82
+ loader?: (args: {
83
+ content: InferBlockContent<P>;
84
+ context?: CmssyBlockContext;
85
+ }) => Promise<D> | D;
86
+ component: ComponentType<{
87
+ content: C;
88
+ style?: S;
89
+ advanced?: A;
90
+ context?: CmssyBlockContext;
91
+ data?: D;
92
+ }>;
93
+ } & ContentGuard<C, P>): BlockDefinition;
94
+ type BlockMap = Record<string, ComponentType<{
95
+ content: Record<string, unknown>;
96
+ style?: Record<string, unknown>;
97
+ advanced?: Record<string, unknown>;
98
+ context?: CmssyBlockContext;
99
+ data?: unknown;
100
+ }>>;
101
+ declare function buildBlockMap(blocks: BlockDefinition[]): BlockMap;
102
+ declare function blocksToSchemas(blocks: BlockDefinition[]): Record<string, BlockSchema>;
103
+ declare function blocksToMeta(blocks: BlockDefinition[], defaults?: {
104
+ category?: string;
105
+ }): Record<string, BlockMeta>;
106
+
107
+ export { type BlockDefinition as B, type BlockMap as a, type BlockProps as b, blocksToMeta as c, blocksToSchemas as d, buildBlockMap as e, defineBlock as f };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmssy/react",
3
- "version": "7.0.1",
3
+ "version": "8.0.0",
4
4
  "description": "React blocks, renderers, data client and editor bridge for cmssy headless sites",
5
5
  "keywords": [
6
6
  "cmssy",
@@ -56,8 +56,8 @@
56
56
  "vitest": "^2.1.0"
57
57
  },
58
58
  "dependencies": {
59
- "@cmssy/types": "0.27.0",
60
- "@cmssy/core": "7.0.1"
59
+ "@cmssy/types": "0.28.0",
60
+ "@cmssy/core": "8.0.0"
61
61
  },
62
62
  "scripts": {
63
63
  "build": "tsup",
@@ -1,72 +0,0 @@
1
- import { ComponentType } from 'react';
2
- import { FieldDefinition, CmssyBlockContext, BlockMeta, BlockSchema } from '@cmssy/core';
3
-
4
- interface BlockLoaderArgs {
5
- content: Record<string, unknown>;
6
- context?: CmssyBlockContext;
7
- }
8
- type BlockLoader = (args: BlockLoaderArgs) => Promise<unknown> | unknown;
9
- interface BlockDefinition {
10
- type: string;
11
- label?: string;
12
- category?: string;
13
- icon?: string;
14
- layoutPositions?: string[];
15
- /**
16
- * One-line semantic description of what the block is and when it belongs on a
17
- * page. Surfaced to the AI page composer to guide block selection and order.
18
- */
19
- description?: string;
20
- props: Record<string, FieldDefinition>;
21
- /**
22
- * Optional server-side data loader. Run by CmssyServerPage during SSR; its
23
- * result is passed to the component as the `data` prop. Not run in the
24
- * editor (the component receives `data: undefined` there).
25
- *
26
- * The result crosses the server→client boundary when the block component is a
27
- * Client Component, so it must be RSC-serializable (plain objects, arrays and
28
- * primitives - no functions, class instances, etc.).
29
- */
30
- loader?: BlockLoader;
31
- component: ComponentType<{
32
- content: Record<string, unknown>;
33
- style?: Record<string, unknown>;
34
- advanced?: Record<string, unknown>;
35
- context?: CmssyBlockContext;
36
- data?: unknown;
37
- }>;
38
- }
39
- declare function defineBlock<C extends Record<string, unknown>, D = unknown, S extends Record<string, unknown> = Record<string, unknown>, A extends Record<string, unknown> = Record<string, unknown>>(def: {
40
- type: string;
41
- label?: string;
42
- category?: string;
43
- icon?: string;
44
- layoutPositions?: string[];
45
- description?: string;
46
- props: Record<string, FieldDefinition>;
47
- loader?: (args: {
48
- content: C;
49
- context?: CmssyBlockContext;
50
- }) => Promise<D> | D;
51
- component: ComponentType<{
52
- content: C;
53
- style?: S;
54
- advanced?: A;
55
- context?: CmssyBlockContext;
56
- data?: D;
57
- }>;
58
- }): BlockDefinition;
59
- type BlockMap = Record<string, ComponentType<{
60
- content: Record<string, unknown>;
61
- style?: Record<string, unknown>;
62
- advanced?: Record<string, unknown>;
63
- context?: CmssyBlockContext;
64
- data?: unknown;
65
- }>>;
66
- declare function buildBlockMap(blocks: BlockDefinition[]): BlockMap;
67
- declare function blocksToSchemas(blocks: BlockDefinition[]): Record<string, BlockSchema>;
68
- declare function blocksToMeta(blocks: BlockDefinition[], defaults?: {
69
- category?: string;
70
- }): Record<string, BlockMeta>;
71
-
72
- export { type BlockDefinition as B, type BlockMap as a, blocksToMeta as b, blocksToSchemas as c, buildBlockMap as d, defineBlock as e };
@@ -1,72 +0,0 @@
1
- import { ComponentType } from 'react';
2
- import { FieldDefinition, CmssyBlockContext, BlockMeta, BlockSchema } from '@cmssy/core';
3
-
4
- interface BlockLoaderArgs {
5
- content: Record<string, unknown>;
6
- context?: CmssyBlockContext;
7
- }
8
- type BlockLoader = (args: BlockLoaderArgs) => Promise<unknown> | unknown;
9
- interface BlockDefinition {
10
- type: string;
11
- label?: string;
12
- category?: string;
13
- icon?: string;
14
- layoutPositions?: string[];
15
- /**
16
- * One-line semantic description of what the block is and when it belongs on a
17
- * page. Surfaced to the AI page composer to guide block selection and order.
18
- */
19
- description?: string;
20
- props: Record<string, FieldDefinition>;
21
- /**
22
- * Optional server-side data loader. Run by CmssyServerPage during SSR; its
23
- * result is passed to the component as the `data` prop. Not run in the
24
- * editor (the component receives `data: undefined` there).
25
- *
26
- * The result crosses the server→client boundary when the block component is a
27
- * Client Component, so it must be RSC-serializable (plain objects, arrays and
28
- * primitives - no functions, class instances, etc.).
29
- */
30
- loader?: BlockLoader;
31
- component: ComponentType<{
32
- content: Record<string, unknown>;
33
- style?: Record<string, unknown>;
34
- advanced?: Record<string, unknown>;
35
- context?: CmssyBlockContext;
36
- data?: unknown;
37
- }>;
38
- }
39
- declare function defineBlock<C extends Record<string, unknown>, D = unknown, S extends Record<string, unknown> = Record<string, unknown>, A extends Record<string, unknown> = Record<string, unknown>>(def: {
40
- type: string;
41
- label?: string;
42
- category?: string;
43
- icon?: string;
44
- layoutPositions?: string[];
45
- description?: string;
46
- props: Record<string, FieldDefinition>;
47
- loader?: (args: {
48
- content: C;
49
- context?: CmssyBlockContext;
50
- }) => Promise<D> | D;
51
- component: ComponentType<{
52
- content: C;
53
- style?: S;
54
- advanced?: A;
55
- context?: CmssyBlockContext;
56
- data?: D;
57
- }>;
58
- }): BlockDefinition;
59
- type BlockMap = Record<string, ComponentType<{
60
- content: Record<string, unknown>;
61
- style?: Record<string, unknown>;
62
- advanced?: Record<string, unknown>;
63
- context?: CmssyBlockContext;
64
- data?: unknown;
65
- }>>;
66
- declare function buildBlockMap(blocks: BlockDefinition[]): BlockMap;
67
- declare function blocksToSchemas(blocks: BlockDefinition[]): Record<string, BlockSchema>;
68
- declare function blocksToMeta(blocks: BlockDefinition[], defaults?: {
69
- category?: string;
70
- }): Record<string, BlockMeta>;
71
-
72
- export { type BlockDefinition as B, type BlockMap as a, blocksToMeta as b, blocksToSchemas as c, buildBlockMap as d, defineBlock as e };