@createcms/core 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +169 -0
  2. package/dist/ab-edge/index.cjs +214 -0
  3. package/dist/ab-edge/index.d.cts +121 -0
  4. package/dist/ab-edge/index.d.ts +121 -0
  5. package/dist/ab-edge/index.js +205 -0
  6. package/dist/bin/createcms.js +3082 -0
  7. package/dist/db.cjs +496 -0
  8. package/dist/db.d.cts +128 -0
  9. package/dist/db.d.ts +128 -0
  10. package/dist/db.js +488 -0
  11. package/dist/index.cjs +13789 -0
  12. package/dist/index.d.cts +10277 -0
  13. package/dist/index.d.ts +10277 -0
  14. package/dist/index.js +13737 -0
  15. package/dist/nanoid.cjs +50 -0
  16. package/dist/nanoid.d.cts +29 -0
  17. package/dist/nanoid.d.ts +29 -0
  18. package/dist/nanoid.js +47 -0
  19. package/dist/next/index.cjs +60 -0
  20. package/dist/next/index.d.cts +141 -0
  21. package/dist/next/index.d.ts +141 -0
  22. package/dist/next/index.js +58 -0
  23. package/dist/next/middleware.cjs +113 -0
  24. package/dist/next/middleware.d.cts +77 -0
  25. package/dist/next/middleware.d.ts +77 -0
  26. package/dist/next/middleware.js +111 -0
  27. package/dist/plugins/ab-test/analytics/upstash.cjs +345 -0
  28. package/dist/plugins/ab-test/analytics/upstash.d.cts +193 -0
  29. package/dist/plugins/ab-test/analytics/upstash.d.ts +193 -0
  30. package/dist/plugins/ab-test/analytics/upstash.js +343 -0
  31. package/dist/plugins/ab-test/client.cjs +686 -0
  32. package/dist/plugins/ab-test/client.d.cts +233 -0
  33. package/dist/plugins/ab-test/client.d.ts +233 -0
  34. package/dist/plugins/ab-test/client.js +684 -0
  35. package/dist/plugins/ab-test/index.cjs +3400 -0
  36. package/dist/plugins/ab-test/index.d.cts +1131 -0
  37. package/dist/plugins/ab-test/index.d.ts +1131 -0
  38. package/dist/plugins/ab-test/index.js +3367 -0
  39. package/dist/plugins/client.cjs +20 -0
  40. package/dist/plugins/client.d.cts +3 -0
  41. package/dist/plugins/client.d.ts +3 -0
  42. package/dist/plugins/client.js +3 -0
  43. package/dist/plugins/consent/client.cjs +315 -0
  44. package/dist/plugins/consent/client.d.cts +145 -0
  45. package/dist/plugins/consent/client.d.ts +145 -0
  46. package/dist/plugins/consent/client.js +313 -0
  47. package/dist/plugins/consent/index.cjs +267 -0
  48. package/dist/plugins/consent/index.d.cts +618 -0
  49. package/dist/plugins/consent/index.d.ts +618 -0
  50. package/dist/plugins/consent/index.js +258 -0
  51. package/dist/plugins/i18n/index.cjs +2177 -0
  52. package/dist/plugins/i18n/index.d.cts +562 -0
  53. package/dist/plugins/i18n/index.d.ts +562 -0
  54. package/dist/plugins/i18n/index.js +2150 -0
  55. package/dist/plugins/media-optimize/index.cjs +315 -0
  56. package/dist/plugins/media-optimize/index.d.cts +144 -0
  57. package/dist/plugins/media-optimize/index.d.ts +144 -0
  58. package/dist/plugins/media-optimize/index.js +311 -0
  59. package/dist/plugins/multi-tenant/index.cjs +210 -0
  60. package/dist/plugins/multi-tenant/index.d.cts +431 -0
  61. package/dist/plugins/multi-tenant/index.d.ts +431 -0
  62. package/dist/plugins/multi-tenant/index.js +207 -0
  63. package/dist/plugins/server.cjs +24 -0
  64. package/dist/plugins/server.d.cts +3 -0
  65. package/dist/plugins/server.d.ts +3 -0
  66. package/dist/plugins/server.js +3 -0
  67. package/dist/react/blocks.cjs +233 -0
  68. package/dist/react/blocks.d.cts +320 -0
  69. package/dist/react/blocks.d.ts +320 -0
  70. package/dist/react/blocks.js +226 -0
  71. package/dist/react/index.cjs +901 -0
  72. package/dist/react/index.d.cts +992 -0
  73. package/dist/react/index.d.ts +992 -0
  74. package/dist/react/index.js +872 -0
  75. package/dist/react/tracking.cjs +243 -0
  76. package/dist/react/tracking.d.cts +364 -0
  77. package/dist/react/tracking.d.ts +364 -0
  78. package/dist/react/tracking.js +216 -0
  79. package/dist/react/variant.cjs +59 -0
  80. package/dist/react/variant.d.cts +26 -0
  81. package/dist/react/variant.d.ts +26 -0
  82. package/dist/react/variant.js +57 -0
  83. package/package.json +303 -0
@@ -0,0 +1,320 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ type BlockTreeNode = {
4
+ blockId: string;
5
+ type: string;
6
+ properties: Record<string, unknown>;
7
+ children: BlockTreeNode[];
8
+ };
9
+
10
+ /**
11
+ * One published branch of a root, as a snapshot. Shared by the page-level
12
+ * variant shape and the block-level A/B `variants` (below) so the two can't
13
+ * drift. `properties` mirrors the (depth-1 typed) reference `properties`.
14
+ */
15
+ type PublishedBranchSnapshot<TProps = Record<string, unknown>> = {
16
+ branchId: string;
17
+ isControl: boolean;
18
+ properties: TProps;
19
+ tree: BlockTreeNode;
20
+ };
21
+ type ResolvedReference<TProps = Record<string, unknown>> = {
22
+ rootId: string;
23
+ collection: string;
24
+ properties: TProps;
25
+ tree: BlockTreeNode;
26
+ /**
27
+ * Present only when this referenced root has a RUNNING A/B test (AB_FANOUT
28
+ * F2 server fan-out). The server stays a pure, cacheable function: top-level
29
+ * `tree`/`properties` are the CONTROL branch (a no-JS / AB-disabled client
30
+ * renders it as-is), and the client pre-render pass (F3) picks the visitor's
31
+ * variant from `variants` and swaps it in. `variants` includes the control.
32
+ * An OPTIONAL field (not a discriminated union) so `isResolvedReference` —
33
+ * which narrows on `tree`/`properties` — keeps matching.
34
+ */
35
+ abTest?: {
36
+ testId: string;
37
+ trafficPercentage: number;
38
+ variants: PublishedBranchSnapshot<TProps>[];
39
+ };
40
+ };
41
+ type BlockTypes = {
42
+ string: string;
43
+ number: number;
44
+ boolean: boolean;
45
+ date: string;
46
+ richText: string;
47
+ image: string;
48
+ select: string;
49
+ reference: string;
50
+ };
51
+ /** Reference inference mode: `raw` (write input + getBlockTree editor read) keeps
52
+ * a `reference` as its stored rootId string; `resolved` (getPublishedContent)
53
+ * surfaces the inlined `ResolvedReference`. */
54
+ type RefMode = 'raw' | 'resolved';
55
+ type BlockPropertyType = keyof BlockTypes;
56
+ type SelectOption = {
57
+ readonly label: string;
58
+ readonly value: string;
59
+ };
60
+ type BlockPropertySpec<T extends BlockPropertyType> = {
61
+ type: T;
62
+ required?: boolean;
63
+ defaultValue?: BlockTypes[T];
64
+ label: string;
65
+ description?: string;
66
+ placeholder?: string;
67
+ } & (T extends 'select' ? {
68
+ options: readonly SelectOption[];
69
+ } : {}) & (T extends 'reference' ? {
70
+ collection: string;
71
+ } : {});
72
+ /** Discriminated union over all concrete block-property specs. */
73
+ type BlockProperty = {
74
+ [K in BlockPropertyType]: BlockPropertySpec<K>;
75
+ }[BlockPropertyType];
76
+ type Simplify<T> = {
77
+ [K in keyof T]: T[K];
78
+ };
79
+ /** Extracts the runtime value type for a block property.
80
+ * For `select` properties with options, returns the union of option values.
81
+ * A `reference` is a rootId string in `raw` mode (write input + editor read) and
82
+ * a `ResolvedReference` in `resolved` mode (published read).
83
+ * For all other types, returns the corresponding primitive type. */
84
+ type InferPropertyValue<T extends BlockProperty, M extends RefMode = 'raw', TCol extends Record<string, AnyCollectionDefinition> = {}> = T extends {
85
+ type: 'select';
86
+ options: readonly {
87
+ readonly value: infer V extends string;
88
+ }[];
89
+ } ? V : T extends {
90
+ type: 'reference';
91
+ collection: infer C extends string;
92
+ } ? M extends 'resolved' ? C extends keyof TCol ? ResolvedReference<NonNullable<InferBlockProperties<TCol[C]['root']['properties'], 'resolved'>>> : ResolvedReference : string : BlockTypes[T['type']];
93
+ type RequiredPart<T extends Record<string, BlockProperty>, M extends RefMode, TCol extends Record<string, AnyCollectionDefinition>> = {
94
+ [K in keyof T as T[K] extends {
95
+ required: true;
96
+ } ? K : never]: InferPropertyValue<T[K], M, TCol>;
97
+ };
98
+ type OptionalPart<T extends Record<string, BlockProperty>, M extends RefMode, TCol extends Record<string, AnyCollectionDefinition>> = {
99
+ [K in keyof T as T[K] extends {
100
+ required: true;
101
+ } ? never : K]?: InferPropertyValue<T[K], M, TCol>;
102
+ };
103
+ type HasRequiredKeys<T extends Record<string, BlockProperty>> = true extends {
104
+ [K in keyof T]: T[K] extends {
105
+ required: true;
106
+ } ? true : never;
107
+ }[keyof T] ? true : false;
108
+ /** Maps a properties record to its runtime value types, respecting `required`.
109
+ * When all properties are optional, the entire input becomes optional (| undefined). */
110
+ type InferBlockProperties<T extends Record<string, BlockProperty>, M extends RefMode = 'raw', TCol extends Record<string, AnyCollectionDefinition> = {}> = HasRequiredKeys<T> extends true ? Simplify<RequiredPart<T, M, TCol> & OptionalPart<T, M, TCol>> : Simplify<RequiredPart<T, M, TCol> & OptionalPart<T, M, TCol>> | undefined;
111
+ /** Scalar property subset usable as an event parameter (no references/media). */
112
+ type ScalarBlockProperty = Extract<BlockProperty, {
113
+ type: 'string' | 'number' | 'boolean' | 'select' | 'date';
114
+ }>;
115
+ /**
116
+ * Declares a meaningful event a functional block can emit (e.g. a form's
117
+ * `submitSuccess`). Living on the block DEFINITION makes it the single source of
118
+ * truth for the typed `fire(...)` union, the test-creation goal picker, and the
119
+ * analytics wire name. `name` overrides the GA4/dataLayer wire name (defaults to
120
+ * `cms_<blockType>_<eventKey>`, computed by the measurement layer). Whether an
121
+ * event counts as a conversion is decided per test in the UI, not here.
122
+ */
123
+ type EventDeclaration = {
124
+ /** Analytics wire-name override (snake_case). Defaults to cms_<type>_<key>. */
125
+ name?: string;
126
+ /** Typed parameters carried with the event (scalar only). */
127
+ params?: Record<string, ScalarBlockProperty>;
128
+ /** Human label for the goal picker. */
129
+ label?: string;
130
+ };
131
+ type BlockDefinition<TProps extends Record<string, BlockProperty> = Record<string, BlockProperty>, TEvents extends Record<string, EventDeclaration> = Record<string, never>> = {
132
+ properties: TProps;
133
+ label: string;
134
+ description?: string;
135
+ previewImageUrl?: string;
136
+ /** Events this (functional) block can emit — see {@link EventDeclaration}. */
137
+ events?: TEvents;
138
+ } & ({
139
+ allowChildren?: false;
140
+ } | {
141
+ allowChildren: true;
142
+ allowedChildBlocks?: string[];
143
+ });
144
+ type AnyBlockDefinition = BlockDefinition<Record<string, BlockProperty>, Record<string, EventDeclaration>>;
145
+ type RootDefinition<TProps extends Record<string, BlockProperty> = Record<string, BlockProperty>> = {
146
+ properties: TProps;
147
+ };
148
+ type SlugConfig = {
149
+ enabled: false;
150
+ } | {
151
+ enabled: true;
152
+ root: string;
153
+ allowRoot?: boolean;
154
+ normalize?: boolean;
155
+ nested?: boolean;
156
+ };
157
+ type CollectionDefinition<TProps extends Record<string, BlockProperty> = Record<string, BlockProperty>, TBlocks extends Record<string, AnyBlockDefinition> = Record<string, AnyBlockDefinition>> = {
158
+ slug?: SlugConfig;
159
+ root: RootDefinition<TProps>;
160
+ blocks?: TBlocks;
161
+ label: string;
162
+ description?: string;
163
+ /**
164
+ * Marks this collection as one whose roots are meant to be EMBEDDED into other
165
+ * roots via a `reference` property (a "reusable block" library). Purely an
166
+ * ergonomic hint — it informs editor pickers and which endpoints to surface; it
167
+ * NEVER gates safety (the delete-in-use guard protects every referenced root
168
+ * regardless of this flag). Any collection can still be a reference target.
169
+ */
170
+ reusableBlock?: boolean;
171
+ };
172
+ type AnyCollectionDefinition = CollectionDefinition<Record<string, BlockProperty>, Record<string, AnyBlockDefinition>>;
173
+
174
+ /** Props passed to each block component in the renderer map. The renderer
175
+ * consumes RESOLVED content (getPublishedContent), so `reference` properties
176
+ * surface as `ResolvedReference` objects — the `resolved` inference mode. */
177
+ type BlockComponentProps<TProps extends Record<string, BlockProperty> = Record<string, BlockProperty>> = {
178
+ properties: InferBlockProperties<TProps, 'resolved'>;
179
+ children: ReactNode;
180
+ blockId: string;
181
+ node: BlockTreeNode;
182
+ };
183
+ /** Shorthand to derive block component props from a collection definition. */
184
+ type BlockProps<TCollection extends {
185
+ blocks: Record<string, AnyBlockDefinition>;
186
+ }, TBlock extends keyof TCollection['blocks'] & string> = BlockComponentProps<TCollection['blocks'][TBlock]['properties']>;
187
+ type BlockComponentMap<TBlocks extends Record<string, AnyBlockDefinition>> = {
188
+ [K in keyof TBlocks & string]: (props: BlockComponentProps<TBlocks[K]['properties']>) => ReactNode;
189
+ };
190
+ declare function isResolvedReference(value: unknown): value is ResolvedReference;
191
+ /**
192
+ * Opaque handle returned by `createBlocksMap`. Pass it to `<BlocksRenderer>`.
193
+ * Carries the React component map AND the per-block-type event declarations
194
+ * (the runtime half of the M2a typed-events seam) so the renderer can tell a
195
+ * functional block (one that declared `events`) from a presentational one.
196
+ */
197
+ type BlocksMap = {
198
+ readonly __brand: 'BlocksMap';
199
+ readonly _components: Record<string, (props: any) => ReactNode>;
200
+ readonly _events: Record<string, Record<string, EventDeclaration>>;
201
+ };
202
+ /**
203
+ * Extracts the per-block-type event declarations from a collection definition —
204
+ * the runtime half of the M2a typed-events seam. Only functional blocks (those
205
+ * that declared a non-empty `events`) get an entry; presentational blocks are
206
+ * omitted, so `type in blocksMap._events` is the runtime "is this block
207
+ * functional?" test the M3 BlockTracker keys on.
208
+ */
209
+ declare function extractBlockEvents(blocks: Record<string, AnyBlockDefinition> | undefined): Record<string, Record<string, EventDeclaration>>;
210
+ /**
211
+ * Creates a type-safe block component map for a CMS collection. Pass the
212
+ * collection DEFINITION (the runtime object) as the single source of truth:
213
+ * the component-prop types are inferred from its blocks, and its `events`
214
+ * declarations are carried into the map for the M3 tracker.
215
+ *
216
+ * @example
217
+ * ```tsx
218
+ * import { createBlocksMap } from '@createcms/core/react';
219
+ * import { pagesCollection } from '@/cms/collections/pages/definition';
220
+ *
221
+ * export const pageBlocks = createBlocksMap(pagesCollection, {
222
+ * headline: ({ properties }) => <h1>{properties.text}</h1>,
223
+ * hero: ({ properties, children }) => (
224
+ * <section>
225
+ * <h1>{properties.headline}</h1>
226
+ * {children}
227
+ * </section>
228
+ * ),
229
+ * });
230
+ * ```
231
+ */
232
+ declare function createBlocksMap<TProps extends Record<string, BlockProperty>, TBlocks extends Record<string, AnyBlockDefinition>>(collection: CollectionDefinition<TProps, TBlocks>, components: BlockComponentMap<TBlocks>): BlocksMap;
233
+ /**
234
+ * Renders a `BlockTreeNode` tree using a block component map.
235
+ *
236
+ * Delegates to the reference-aware `renderContentNode`, which is a strict
237
+ * superset: for the reference-free trees that `getBlockTree` produces it
238
+ * behaves identically, and it additionally resolves inline references should a
239
+ * tree ever carry them.
240
+ *
241
+ * @example
242
+ * ```tsx
243
+ * import { BlocksRenderer } from '@createcms/core/react';
244
+ * import { pageBlocks } from '@/lib/blocks/pages';
245
+ *
246
+ * export default async function Page() {
247
+ * const tree = await cms.api.pages.getBlockTree(...);
248
+ * return <BlocksRenderer blocks={pageBlocks} tree={tree} />;
249
+ * }
250
+ * ```
251
+ */
252
+ declare function BlocksRenderer({ blocks, tree, }: {
253
+ blocks: BlocksMap;
254
+ tree: BlockTreeNode;
255
+ }): ReactNode;
256
+ /**
257
+ * Creates a type-safe block renderer component for a CMS collection.
258
+ * Convenience shorthand that combines `createBlocksMap` + `BlocksRenderer`.
259
+ *
260
+ * @example
261
+ * ```tsx
262
+ * import { createBlocksRenderer } from '@createcms/core/react';
263
+ * import { pagesCollection } from '@/cms/collections/pages/definition';
264
+ *
265
+ * const PageBlocks = createBlocksRenderer(pagesCollection, {
266
+ * headline: ({ properties }) => <h1>{properties.text}</h1>,
267
+ * hero: ({ properties, children }) => (
268
+ * <section>
269
+ * <h1>{properties.headline}</h1>
270
+ * {children}
271
+ * </section>
272
+ * ),
273
+ * });
274
+ *
275
+ * // In a page component:
276
+ * <PageBlocks tree={tree} />
277
+ * ```
278
+ */
279
+ declare function createBlocksRenderer<TProps extends Record<string, BlockProperty>, TBlocks extends Record<string, AnyBlockDefinition>>(collection: CollectionDefinition<TProps, TBlocks>, components: BlockComponentMap<TBlocks>): {
280
+ ({ tree }: {
281
+ tree: BlockTreeNode;
282
+ }): ReactNode;
283
+ displayName: string;
284
+ };
285
+ /**
286
+ * Renders a block tree with automatic reference resolution.
287
+ *
288
+ * When a block has a `reference` property that was resolved by
289
+ * `getPublishedContent`, the referenced block tree is rendered inline
290
+ * using the same block components. Data-only references (collections
291
+ * without blocks) are available directly in `properties`.
292
+ *
293
+ * @example
294
+ * ```tsx
295
+ * import { createContentRenderer } from '@createcms/core/react';
296
+ * import { pagesCollection } from '@/cms/collections/pages/definition';
297
+ *
298
+ * const RenderPage = createContentRenderer(pagesCollection, {
299
+ * headline: ({ properties }) => <h1>{properties.text}</h1>,
300
+ * paragraph: ({ properties }) => <p>{properties.text}</p>,
301
+ * authorCard: ({ properties }) => (
302
+ * <div>{properties.author.properties.name}</div>
303
+ * ),
304
+ * // No component needed for blocks that just embed a referenced tree —
305
+ * // the referenced content renders inline automatically.
306
+ * });
307
+ *
308
+ * // Usage:
309
+ * <RenderPage tree={tree} />
310
+ * ```
311
+ */
312
+ declare function createContentRenderer<TProps extends Record<string, BlockProperty>, TBlocks extends Record<string, AnyBlockDefinition>>(collection: CollectionDefinition<TProps, TBlocks>, components: Partial<BlockComponentMap<TBlocks>>): {
313
+ ({ tree, }: {
314
+ tree: BlockTreeNode;
315
+ }): ReactNode;
316
+ displayName: string;
317
+ };
318
+
319
+ export { BlocksRenderer, createBlocksMap, createBlocksRenderer, createContentRenderer, extractBlockEvents, isResolvedReference };
320
+ export type { BlockComponentProps, BlockProps, BlocksMap };
@@ -0,0 +1,226 @@
1
+ import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
2
+ import { BlockTracker } from './tracking.js';
3
+
4
+ function isResolvedReference(value) {
5
+ return typeof value === 'object' && value !== null && 'rootId' in value && 'collection' in value && 'tree' in value && 'properties' in value;
6
+ }
7
+ /**
8
+ * Extracts the per-block-type event declarations from a collection definition —
9
+ * the runtime half of the M2a typed-events seam. Only functional blocks (those
10
+ * that declared a non-empty `events`) get an entry; presentational blocks are
11
+ * omitted, so `type in blocksMap._events` is the runtime "is this block
12
+ * functional?" test the M3 BlockTracker keys on.
13
+ */ function extractBlockEvents(blocks) {
14
+ const out = {};
15
+ if (!blocks) return out;
16
+ for (const [type, def] of Object.entries(blocks)){
17
+ if (def.events && Object.keys(def.events).length > 0) {
18
+ // Shallow-copy so the map OWNS its event records (the `readonly` on
19
+ // BlocksMap._events is a real contract): never alias the live collection
20
+ // definition, so a consumer can't mutate it through the map.
21
+ out[type] = {
22
+ ...def.events
23
+ };
24
+ }
25
+ }
26
+ return out;
27
+ }
28
+ /**
29
+ * Creates a type-safe block component map for a CMS collection. Pass the
30
+ * collection DEFINITION (the runtime object) as the single source of truth:
31
+ * the component-prop types are inferred from its blocks, and its `events`
32
+ * declarations are carried into the map for the M3 tracker.
33
+ *
34
+ * @example
35
+ * ```tsx
36
+ * import { createBlocksMap } from '@createcms/core/react';
37
+ * import { pagesCollection } from '@/cms/collections/pages/definition';
38
+ *
39
+ * export const pageBlocks = createBlocksMap(pagesCollection, {
40
+ * headline: ({ properties }) => <h1>{properties.text}</h1>,
41
+ * hero: ({ properties, children }) => (
42
+ * <section>
43
+ * <h1>{properties.headline}</h1>
44
+ * {children}
45
+ * </section>
46
+ * ),
47
+ * });
48
+ * ```
49
+ */ function createBlocksMap(collection, components) {
50
+ return {
51
+ __brand: 'BlocksMap',
52
+ _components: components,
53
+ _events: extractBlockEvents(collection.blocks)
54
+ };
55
+ }
56
+ // ============================================================================
57
+ // BlocksRenderer
58
+ // ============================================================================
59
+ /**
60
+ * Renders a `BlockTreeNode` tree using a block component map.
61
+ *
62
+ * Delegates to the reference-aware `renderContentNode`, which is a strict
63
+ * superset: for the reference-free trees that `getBlockTree` produces it
64
+ * behaves identically, and it additionally resolves inline references should a
65
+ * tree ever carry them.
66
+ *
67
+ * @example
68
+ * ```tsx
69
+ * import { BlocksRenderer } from '@createcms/core/react';
70
+ * import { pageBlocks } from '@/lib/blocks/pages';
71
+ *
72
+ * export default async function Page() {
73
+ * const tree = await cms.api.pages.getBlockTree(...);
74
+ * return <BlocksRenderer blocks={pageBlocks} tree={tree} />;
75
+ * }
76
+ * ```
77
+ */ function BlocksRenderer({ blocks, tree }) {
78
+ return renderContentNode(tree, blocks._components, blocks._events);
79
+ }
80
+ // ============================================================================
81
+ // createBlocksRenderer (convenience shorthand)
82
+ // ============================================================================
83
+ /**
84
+ * Creates a type-safe block renderer component for a CMS collection.
85
+ * Convenience shorthand that combines `createBlocksMap` + `BlocksRenderer`.
86
+ *
87
+ * @example
88
+ * ```tsx
89
+ * import { createBlocksRenderer } from '@createcms/core/react';
90
+ * import { pagesCollection } from '@/cms/collections/pages/definition';
91
+ *
92
+ * const PageBlocks = createBlocksRenderer(pagesCollection, {
93
+ * headline: ({ properties }) => <h1>{properties.text}</h1>,
94
+ * hero: ({ properties, children }) => (
95
+ * <section>
96
+ * <h1>{properties.headline}</h1>
97
+ * {children}
98
+ * </section>
99
+ * ),
100
+ * });
101
+ *
102
+ * // In a page component:
103
+ * <PageBlocks tree={tree} />
104
+ * ```
105
+ */ function createBlocksRenderer(collection, components) {
106
+ const blocksMap = createBlocksMap(collection, components);
107
+ function Renderer({ tree }) {
108
+ return /*#__PURE__*/ jsx(BlocksRenderer, {
109
+ blocks: blocksMap,
110
+ tree: tree
111
+ });
112
+ }
113
+ Renderer.displayName = `BlocksRenderer(${collection.label})`;
114
+ return Renderer;
115
+ }
116
+ // ============================================================================
117
+ // ContentRenderer (reference-aware tree rendering)
118
+ // ============================================================================
119
+ function renderContentNode(node, components, events, fromReference = false) {
120
+ const renderedChildren = node.children.map((child)=>renderContentNode(child, components, events));
121
+ const childrenNode = renderedChildren.length > 0 ? /*#__PURE__*/ jsx(Fragment, {
122
+ children: renderedChildren
123
+ }) : null;
124
+ if (node.type === 'root') {
125
+ return /*#__PURE__*/ jsx(Fragment, {
126
+ children: childrenNode
127
+ });
128
+ }
129
+ const Component = components[node.type];
130
+ if (!Component) {
131
+ // No component mapped for this block type.
132
+ // Check if any property is a resolved reference with a tree —
133
+ // if so, render the referenced tree inline using the same components.
134
+ for (const value of Object.values(node.properties)){
135
+ if (isResolvedReference(value) && value.tree.children.length > 0) {
136
+ const refChildren = value.tree.children.map((child)=>renderContentNode(child, components, events));
137
+ return /*#__PURE__*/ jsx(Fragment, {
138
+ children: refChildren
139
+ });
140
+ }
141
+ }
142
+ // Blocks from referenced data-only collections won't have a mapped
143
+ // component — that's expected, not an error.
144
+ if (!fromReference && process.env.NODE_ENV !== 'production') {
145
+ console.warn(`[cms] No component mapped for block type "${node.type}"`);
146
+ }
147
+ return null;
148
+ }
149
+ // If the block has reference properties with block trees, render them
150
+ // and pass as children (appended after the block's own children). The same
151
+ // `events` map flows down so a functional block embedded via a reference
152
+ // still binds to the host page's ambient ab-context.
153
+ let refRendered = [];
154
+ for (const value of Object.values(node.properties)){
155
+ if (isResolvedReference(value) && value.tree.children.length > 0) {
156
+ for (const child of value.tree.children){
157
+ refRendered.push(renderContentNode(child, components, events, true));
158
+ }
159
+ }
160
+ }
161
+ const allChildren = renderedChildren.length > 0 || refRendered.length > 0 ? /*#__PURE__*/ jsxs(Fragment, {
162
+ children: [
163
+ renderedChildren,
164
+ refRendered
165
+ ]
166
+ }) : null;
167
+ const element = /*#__PURE__*/ jsx(Component, {
168
+ properties: node.properties,
169
+ children: allChildren,
170
+ blockId: node.blockId,
171
+ node: node
172
+ }, node.blockId);
173
+ // M3c — a FUNCTIONAL block (declared `events`, carried in BlocksMap._events)
174
+ // is wrapped in the 'use client' <BlockTracker> so it can fire its declared
175
+ // events. children-as-props: `element` is server-rendered and just passed
176
+ // through, so presentational subtrees stay RSC. The dispatch + ab-context come
177
+ // from the consumer's <TrackingRuntimeProvider>, not from here.
178
+ if (node.type in events) {
179
+ const rawTrackingId = node.properties.trackingId;
180
+ return /*#__PURE__*/ jsx(BlockTracker, {
181
+ blockType: node.type,
182
+ blockId: node.blockId,
183
+ trackingId: typeof rawTrackingId === 'string' ? rawTrackingId : undefined,
184
+ events: events[node.type],
185
+ children: element
186
+ }, node.blockId);
187
+ }
188
+ return element;
189
+ }
190
+ /**
191
+ * Renders a block tree with automatic reference resolution.
192
+ *
193
+ * When a block has a `reference` property that was resolved by
194
+ * `getPublishedContent`, the referenced block tree is rendered inline
195
+ * using the same block components. Data-only references (collections
196
+ * without blocks) are available directly in `properties`.
197
+ *
198
+ * @example
199
+ * ```tsx
200
+ * import { createContentRenderer } from '@createcms/core/react';
201
+ * import { pagesCollection } from '@/cms/collections/pages/definition';
202
+ *
203
+ * const RenderPage = createContentRenderer(pagesCollection, {
204
+ * headline: ({ properties }) => <h1>{properties.text}</h1>,
205
+ * paragraph: ({ properties }) => <p>{properties.text}</p>,
206
+ * authorCard: ({ properties }) => (
207
+ * <div>{properties.author.properties.name}</div>
208
+ * ),
209
+ * // No component needed for blocks that just embed a referenced tree —
210
+ * // the referenced content renders inline automatically.
211
+ * });
212
+ *
213
+ * // Usage:
214
+ * <RenderPage tree={tree} />
215
+ * ```
216
+ */ function createContentRenderer(collection, components) {
217
+ const componentMap = components;
218
+ const events = extractBlockEvents(collection.blocks);
219
+ function ContentRendererComponent({ tree }) {
220
+ return renderContentNode(tree, componentMap, events);
221
+ }
222
+ ContentRendererComponent.displayName = `ContentRenderer(${collection.label})`;
223
+ return ContentRendererComponent;
224
+ }
225
+
226
+ export { BlocksRenderer, createBlocksMap, createBlocksRenderer, createContentRenderer, extractBlockEvents, isResolvedReference };