@delmaredigital/payload-puck 0.1.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.
Files changed (128) hide show
  1. package/LICENSE +73 -0
  2. package/README.md +1580 -0
  3. package/dist/AccordionClient.d.mts +24 -0
  4. package/dist/AccordionClient.d.ts +24 -0
  5. package/dist/AccordionClient.js +786 -0
  6. package/dist/AccordionClient.js.map +1 -0
  7. package/dist/AccordionClient.mjs +784 -0
  8. package/dist/AccordionClient.mjs.map +1 -0
  9. package/dist/AnimatedWrapper.d.mts +30 -0
  10. package/dist/AnimatedWrapper.d.ts +30 -0
  11. package/dist/AnimatedWrapper.js +379 -0
  12. package/dist/AnimatedWrapper.js.map +1 -0
  13. package/dist/AnimatedWrapper.mjs +377 -0
  14. package/dist/AnimatedWrapper.mjs.map +1 -0
  15. package/dist/admin/client.d.mts +108 -0
  16. package/dist/admin/client.d.ts +108 -0
  17. package/dist/admin/client.js +177 -0
  18. package/dist/admin/client.js.map +1 -0
  19. package/dist/admin/client.mjs +173 -0
  20. package/dist/admin/client.mjs.map +1 -0
  21. package/dist/admin/index.d.mts +157 -0
  22. package/dist/admin/index.d.ts +157 -0
  23. package/dist/admin/index.js +31 -0
  24. package/dist/admin/index.js.map +1 -0
  25. package/dist/admin/index.mjs +29 -0
  26. package/dist/admin/index.mjs.map +1 -0
  27. package/dist/api/index.d.mts +460 -0
  28. package/dist/api/index.d.ts +460 -0
  29. package/dist/api/index.js +588 -0
  30. package/dist/api/index.js.map +1 -0
  31. package/dist/api/index.mjs +578 -0
  32. package/dist/api/index.mjs.map +1 -0
  33. package/dist/components/index.css +339 -0
  34. package/dist/components/index.css.map +1 -0
  35. package/dist/components/index.d.mts +222 -0
  36. package/dist/components/index.d.ts +222 -0
  37. package/dist/components/index.js +9177 -0
  38. package/dist/components/index.js.map +1 -0
  39. package/dist/components/index.mjs +9130 -0
  40. package/dist/components/index.mjs.map +1 -0
  41. package/dist/config/config.editor.css +339 -0
  42. package/dist/config/config.editor.css.map +1 -0
  43. package/dist/config/config.editor.d.mts +153 -0
  44. package/dist/config/config.editor.d.ts +153 -0
  45. package/dist/config/config.editor.js +9400 -0
  46. package/dist/config/config.editor.js.map +1 -0
  47. package/dist/config/config.editor.mjs +9368 -0
  48. package/dist/config/config.editor.mjs.map +1 -0
  49. package/dist/config/index.d.mts +68 -0
  50. package/dist/config/index.d.ts +68 -0
  51. package/dist/config/index.js +2017 -0
  52. package/dist/config/index.js.map +1 -0
  53. package/dist/config/index.mjs +1991 -0
  54. package/dist/config/index.mjs.map +1 -0
  55. package/dist/editor/index.d.mts +784 -0
  56. package/dist/editor/index.d.ts +784 -0
  57. package/dist/editor/index.js +4517 -0
  58. package/dist/editor/index.js.map +1 -0
  59. package/dist/editor/index.mjs +4483 -0
  60. package/dist/editor/index.mjs.map +1 -0
  61. package/dist/fields/index.css +339 -0
  62. package/dist/fields/index.css.map +1 -0
  63. package/dist/fields/index.d.mts +600 -0
  64. package/dist/fields/index.d.ts +600 -0
  65. package/dist/fields/index.js +7739 -0
  66. package/dist/fields/index.js.map +1 -0
  67. package/dist/fields/index.mjs +7590 -0
  68. package/dist/fields/index.mjs.map +1 -0
  69. package/dist/index-CQu6SzDg.d.mts +327 -0
  70. package/dist/index-CoUQnyC3.d.ts +327 -0
  71. package/dist/index.d.mts +6 -0
  72. package/dist/index.d.ts +6 -0
  73. package/dist/index.js +569 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/index.mjs +555 -0
  76. package/dist/index.mjs.map +1 -0
  77. package/dist/layouts/index.d.mts +96 -0
  78. package/dist/layouts/index.d.ts +96 -0
  79. package/dist/layouts/index.js +394 -0
  80. package/dist/layouts/index.js.map +1 -0
  81. package/dist/layouts/index.mjs +378 -0
  82. package/dist/layouts/index.mjs.map +1 -0
  83. package/dist/plugin/index.d.mts +289 -0
  84. package/dist/plugin/index.d.ts +289 -0
  85. package/dist/plugin/index.js +569 -0
  86. package/dist/plugin/index.js.map +1 -0
  87. package/dist/plugin/index.mjs +555 -0
  88. package/dist/plugin/index.mjs.map +1 -0
  89. package/dist/render/index.d.mts +109 -0
  90. package/dist/render/index.d.ts +109 -0
  91. package/dist/render/index.js +2146 -0
  92. package/dist/render/index.js.map +1 -0
  93. package/dist/render/index.mjs +2123 -0
  94. package/dist/render/index.mjs.map +1 -0
  95. package/dist/shared-DMAF1AcH.d.mts +545 -0
  96. package/dist/shared-DMAF1AcH.d.ts +545 -0
  97. package/dist/theme/index.d.mts +155 -0
  98. package/dist/theme/index.d.ts +155 -0
  99. package/dist/theme/index.js +201 -0
  100. package/dist/theme/index.js.map +1 -0
  101. package/dist/theme/index.mjs +186 -0
  102. package/dist/theme/index.mjs.map +1 -0
  103. package/dist/types-D7D3rZ1J.d.mts +116 -0
  104. package/dist/types-D7D3rZ1J.d.ts +116 -0
  105. package/dist/types-_6MvjyKv.d.mts +104 -0
  106. package/dist/types-_6MvjyKv.d.ts +104 -0
  107. package/dist/utils/index.d.mts +267 -0
  108. package/dist/utils/index.d.ts +267 -0
  109. package/dist/utils/index.js +426 -0
  110. package/dist/utils/index.js.map +1 -0
  111. package/dist/utils/index.mjs +412 -0
  112. package/dist/utils/index.mjs.map +1 -0
  113. package/dist/utils-DaRs9t0J.d.mts +85 -0
  114. package/dist/utils-gAvt0Vhw.d.ts +85 -0
  115. package/examples/README.md +240 -0
  116. package/examples/api/puck/pages/[id]/route.ts +64 -0
  117. package/examples/api/puck/pages/[id]/versions/route.ts +47 -0
  118. package/examples/api/puck/pages/route.ts +45 -0
  119. package/examples/app/(frontend)/page.tsx +94 -0
  120. package/examples/app/[...slug]/page.tsx +101 -0
  121. package/examples/app/pages/[id]/edit/page.tsx +148 -0
  122. package/examples/components/CustomBanner.tsx +368 -0
  123. package/examples/config/custom-config.ts +223 -0
  124. package/examples/config/payload.config.example.ts +64 -0
  125. package/examples/lib/puck-layouts.ts +258 -0
  126. package/examples/lib/puck-theme.ts +94 -0
  127. package/examples/styles/puck-theme.css +171 -0
  128. package/package.json +157 -0
@@ -0,0 +1,267 @@
1
+ import { P as PuckPageData, M as MediaObject, a as PuckRootProps } from '../index-CoUQnyC3.js';
2
+ import 'payload';
3
+ import '@measured/puck';
4
+ import '../types-D7D3rZ1J.js';
5
+ import 'react';
6
+
7
+ /**
8
+ * Migration Utility for Converting Legacy Payload CMS Pages to Puck Format
9
+ *
10
+ * This utility converts legacy Payload CMS page block structures to the
11
+ * Puck visual editor format, enabling seamless migration of existing content.
12
+ */
13
+
14
+ /**
15
+ * Legacy block from Payload CMS pages.
16
+ * Each block has a blockType discriminator and various props.
17
+ */
18
+ interface LegacyBlock {
19
+ blockType: string;
20
+ id?: string | null;
21
+ blockName?: string | null;
22
+ [key: string]: unknown;
23
+ }
24
+ /**
25
+ * Subset of Page type used for migration.
26
+ * Only includes fields relevant to content migration.
27
+ */
28
+ interface LegacyPage {
29
+ title: string;
30
+ slug: string;
31
+ pageLayout?: string;
32
+ layout?: LegacyBlock[] | null;
33
+ [key: string]: unknown;
34
+ }
35
+ /**
36
+ * Media reference in Puck format
37
+ */
38
+ interface MediaReference {
39
+ id: string | number;
40
+ url?: string;
41
+ alt?: string;
42
+ width?: number;
43
+ height?: number;
44
+ }
45
+ /**
46
+ * Puck content item structure
47
+ */
48
+ interface PuckContentItem {
49
+ type: string;
50
+ props: {
51
+ id: string;
52
+ [key: string]: unknown;
53
+ };
54
+ }
55
+ /**
56
+ * Maps legacy Payload block types (camelCase) to Puck component types (PascalCase).
57
+ * Extend this map to support additional custom block types.
58
+ */
59
+ declare const blockTypeMap: Record<string, string>;
60
+ /**
61
+ * Generates a unique ID for Puck content items.
62
+ * Uses a format compatible with Puck's internal ID scheme.
63
+ */
64
+ declare function generatePuckId(legacyId?: string | null): string;
65
+ /**
66
+ * Transforms a Payload media reference (ID or Media object) to Puck MediaReference format.
67
+ * Handles both ID-only references and full Media objects.
68
+ */
69
+ declare function transformMediaReference(media: string | number | {
70
+ id: string | number;
71
+ url?: string;
72
+ alt?: string;
73
+ width?: number;
74
+ height?: number;
75
+ } | MediaObject | null | undefined): MediaReference | null;
76
+ /**
77
+ * Transforms Lexical rich text content to a serialized string format.
78
+ * Puck stores rich text as JSON strings for the custom editors.
79
+ */
80
+ declare function transformRichText(richText: unknown): string | undefined;
81
+ /**
82
+ * Transforms an array of relationships to Puck format.
83
+ */
84
+ declare function transformRelationshipArray<T extends {
85
+ id: string | number;
86
+ title?: string;
87
+ slug?: string;
88
+ }>(relations: (string | number | T)[] | null | undefined): Array<{
89
+ id: string | number;
90
+ title: string;
91
+ slug?: string;
92
+ }> | undefined;
93
+ /**
94
+ * Transform props for a specific block type.
95
+ * Handles any block-specific transformations needed.
96
+ */
97
+ declare function transformBlockProps(block: LegacyBlock, options?: {
98
+ richTextFields?: string[];
99
+ mediaFields?: string[];
100
+ customTransformers?: Record<string, (value: unknown) => unknown>;
101
+ }): Record<string, unknown>;
102
+ /**
103
+ * Options for the migration function
104
+ */
105
+ interface MigrateLegacyOptions {
106
+ /**
107
+ * Custom block type mapping to extend or override defaults
108
+ */
109
+ blockTypeMap?: Record<string, string>;
110
+ /**
111
+ * Additional rich text field names to transform
112
+ */
113
+ richTextFields?: string[];
114
+ /**
115
+ * Additional media field names to transform
116
+ */
117
+ mediaFields?: string[];
118
+ /**
119
+ * Custom prop transformers for specific field names
120
+ */
121
+ customTransformers?: Record<string, (value: unknown) => unknown>;
122
+ /**
123
+ * Whether to skip unknown block types (default: true)
124
+ * If false, unknown blocks will throw an error
125
+ */
126
+ skipUnknownBlocks?: boolean;
127
+ /**
128
+ * Callback for handling unknown block types
129
+ */
130
+ onUnknownBlock?: (block: LegacyBlock) => PuckContentItem | null;
131
+ }
132
+ /**
133
+ * Migrates a legacy Payload CMS page to Puck format.
134
+ *
135
+ * This function:
136
+ * 1. Extracts root props (title, pageLayout, etc.)
137
+ * 2. Converts each block in the layout array to a Puck content item
138
+ * 3. Maps block types from camelCase to PascalCase using blockTypeMap
139
+ * 4. Transforms block props appropriately
140
+ *
141
+ * @param page - The legacy Page object from Payload CMS
142
+ * @param options - Migration options
143
+ * @returns PuckPageData ready for use with the Puck editor
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * const legacyPage = await payload.findByID({ collection: 'pages', id: pageId })
148
+ * const puckData = migrateLegacyToPuck(legacyPage)
149
+ * await payload.update({
150
+ * collection: 'pages',
151
+ * id: pageId,
152
+ * data: { puckData },
153
+ * })
154
+ * ```
155
+ */
156
+ declare function migrateLegacyToPuck(page: LegacyPage, options?: MigrateLegacyOptions): PuckPageData;
157
+ /**
158
+ * Migration preview result
159
+ */
160
+ interface MigrationPreview {
161
+ title: string;
162
+ slug: string;
163
+ blockCount: number;
164
+ blockTypes: string[];
165
+ warnings: string[];
166
+ unmappedBlockTypes: string[];
167
+ }
168
+ /**
169
+ * Gets a summary of a legacy page for migration preview.
170
+ * Useful for showing users what will be migrated before committing.
171
+ *
172
+ * @param page - The legacy page to preview
173
+ * @param customBlockTypeMap - Optional custom block type mapping
174
+ * @returns Preview information about the migration
175
+ */
176
+ declare function getMigrationPreview(page: LegacyPage, customBlockTypeMap?: Record<string, string>): MigrationPreview;
177
+
178
+ /**
179
+ * Puck Data Validation Utilities
180
+ *
181
+ * Provides validation functions for ensuring Puck data structures are well-formed.
182
+ */
183
+
184
+ /**
185
+ * Result of a validation operation
186
+ */
187
+ interface ValidationResult {
188
+ valid: boolean;
189
+ errors: string[];
190
+ warnings: string[];
191
+ }
192
+ /**
193
+ * Options for validation
194
+ */
195
+ interface ValidationOptions {
196
+ /**
197
+ * Whether to validate that all content items have IDs
198
+ * @default true
199
+ */
200
+ requireContentIds?: boolean;
201
+ /**
202
+ * Whether to validate root props
203
+ * @default true
204
+ */
205
+ validateRoot?: boolean;
206
+ /**
207
+ * Required root prop fields
208
+ * @default ['title']
209
+ */
210
+ requiredRootProps?: string[];
211
+ /**
212
+ * Allowed component types (if specified, unknown types will be flagged)
213
+ */
214
+ allowedComponentTypes?: string[];
215
+ /**
216
+ * Whether to treat unknown component types as errors (vs warnings)
217
+ * @default false
218
+ */
219
+ strictComponentTypes?: boolean;
220
+ }
221
+ /**
222
+ * Type guard to check if a value is a valid PuckPageData structure.
223
+ * Performs basic structural validation.
224
+ */
225
+ declare function isPuckData(data: unknown): data is PuckPageData;
226
+ /**
227
+ * Type guard for PuckRootProps
228
+ */
229
+ declare function isPuckRootProps(props: unknown): props is PuckRootProps;
230
+ /**
231
+ * Validates that a PuckPageData structure is well-formed.
232
+ * Useful for testing and debugging migration issues.
233
+ *
234
+ * @param data - The data to validate
235
+ * @param options - Validation options
236
+ * @returns Validation result with errors and warnings
237
+ *
238
+ * @example
239
+ * ```ts
240
+ * const result = validatePuckData(puckData)
241
+ * if (!result.valid) {
242
+ * console.error('Validation errors:', result.errors)
243
+ * }
244
+ * ```
245
+ */
246
+ declare function validatePuckData(data: unknown, options?: ValidationOptions): ValidationResult;
247
+ /**
248
+ * Validates and returns typed PuckPageData, throwing if invalid.
249
+ *
250
+ * @param data - The data to validate
251
+ * @param options - Validation options
252
+ * @returns The validated PuckPageData
253
+ * @throws Error if validation fails
254
+ */
255
+ declare function assertPuckData(data: unknown, options?: ValidationOptions): PuckPageData;
256
+ /**
257
+ * Safely parses JSON and validates as PuckPageData.
258
+ *
259
+ * @param json - JSON string to parse
260
+ * @param options - Validation options
261
+ * @returns Validation result with parsed data if valid
262
+ */
263
+ declare function parsePuckDataJson(json: string, options?: ValidationOptions): ValidationResult & {
264
+ data?: PuckPageData;
265
+ };
266
+
267
+ export { type LegacyBlock, type LegacyPage, type MediaReference, type MigrateLegacyOptions, type MigrationPreview, type PuckContentItem, type ValidationOptions, type ValidationResult, assertPuckData, blockTypeMap, generatePuckId, getMigrationPreview, isPuckData, isPuckRootProps, migrateLegacyToPuck, parsePuckDataJson, transformBlockProps, transformMediaReference, transformRelationshipArray, transformRichText, validatePuckData };
@@ -0,0 +1,426 @@
1
+ 'use strict';
2
+
3
+ // src/utils/migration.ts
4
+ var blockTypeMap = {
5
+ heroBlock: "Hero",
6
+ richTextBlock: "RichText",
7
+ containerBlock: "Container",
8
+ flexBlock: "Flex",
9
+ gridBlock: "Grid",
10
+ sectionBlock: "Section",
11
+ spacerBlock: "Spacer",
12
+ headingBlock: "Heading",
13
+ textBlock: "Text",
14
+ imageBlock: "Image",
15
+ buttonBlock: "Button",
16
+ cardBlock: "Card",
17
+ dividerBlock: "Divider",
18
+ accordionBlock: "Accordion"
19
+ };
20
+ function generatePuckId(legacyId) {
21
+ if (legacyId) {
22
+ return legacyId;
23
+ }
24
+ return `puck-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
25
+ }
26
+ function transformMediaReference(media) {
27
+ if (media === null || media === void 0) {
28
+ return null;
29
+ }
30
+ if (typeof media === "number" || typeof media === "string") {
31
+ return { id: media };
32
+ }
33
+ return {
34
+ id: media.id,
35
+ url: media.url,
36
+ alt: media.alt,
37
+ width: media.width,
38
+ height: media.height
39
+ };
40
+ }
41
+ function transformRichText(richText) {
42
+ if (!richText) {
43
+ return void 0;
44
+ }
45
+ if (typeof richText === "string") {
46
+ return richText;
47
+ }
48
+ if (typeof richText === "object") {
49
+ try {
50
+ return JSON.stringify(richText);
51
+ } catch {
52
+ console.warn("[payload-puck] Failed to serialize rich text content");
53
+ return void 0;
54
+ }
55
+ }
56
+ return void 0;
57
+ }
58
+ function transformRelationshipArray(relations) {
59
+ if (!relations || !Array.isArray(relations)) {
60
+ return void 0;
61
+ }
62
+ return relations.map((rel) => {
63
+ if (typeof rel === "number" || typeof rel === "string") {
64
+ return { id: rel, title: "" };
65
+ }
66
+ return {
67
+ id: rel.id,
68
+ title: rel.title || "",
69
+ slug: rel.slug
70
+ };
71
+ });
72
+ }
73
+ var RICH_TEXT_FIELDS = [
74
+ "headingRich",
75
+ "subheadingRich",
76
+ "textRich",
77
+ "content",
78
+ "leftContent",
79
+ "rightContent",
80
+ "bodyContent",
81
+ "description"
82
+ ];
83
+ var MEDIA_FIELDS = [
84
+ "backgroundImage",
85
+ "image",
86
+ "rightImage",
87
+ "leftImage",
88
+ "profileImage",
89
+ "media",
90
+ "thumbnail",
91
+ "icon"
92
+ ];
93
+ function transformBlockProps(block, options) {
94
+ const { blockType, id, blockName, ...restProps } = block;
95
+ const richTextFields = options?.richTextFields || RICH_TEXT_FIELDS;
96
+ const mediaFields = options?.mediaFields || MEDIA_FIELDS;
97
+ const customTransformers = options?.customTransformers || {};
98
+ const props = {
99
+ id: generatePuckId(id)
100
+ };
101
+ for (const [key, value] of Object.entries(restProps)) {
102
+ if (customTransformers[key]) {
103
+ props[key] = customTransformers[key](value);
104
+ continue;
105
+ }
106
+ if (richTextFields.includes(key)) {
107
+ props[key] = transformRichText(value);
108
+ continue;
109
+ }
110
+ if (mediaFields.includes(key)) {
111
+ props[key] = transformMediaReference(
112
+ value
113
+ );
114
+ continue;
115
+ }
116
+ if (Array.isArray(value)) {
117
+ props[key] = value.map((item) => {
118
+ if (typeof item === "object" && item !== null) {
119
+ const { id: itemId, ...itemRest } = item;
120
+ return {
121
+ ...itemRest,
122
+ id: itemId || generatePuckId()
123
+ };
124
+ }
125
+ return item;
126
+ });
127
+ continue;
128
+ }
129
+ props[key] = value;
130
+ }
131
+ return props;
132
+ }
133
+ function migrateLegacyToPuck(page, options = {}) {
134
+ const {
135
+ blockTypeMap: customBlockTypeMap,
136
+ richTextFields,
137
+ mediaFields,
138
+ customTransformers,
139
+ skipUnknownBlocks = true,
140
+ onUnknownBlock
141
+ } = options;
142
+ const effectiveBlockTypeMap = {
143
+ ...blockTypeMap,
144
+ ...customBlockTypeMap
145
+ };
146
+ const rootProps = {
147
+ title: page.title,
148
+ pageLayout: page.pageLayout || "default"
149
+ };
150
+ const content = [];
151
+ if (page.layout && Array.isArray(page.layout)) {
152
+ for (const block of page.layout) {
153
+ const legacyBlock = block;
154
+ const puckType = effectiveBlockTypeMap[legacyBlock.blockType];
155
+ if (!puckType) {
156
+ if (onUnknownBlock) {
157
+ const customItem = onUnknownBlock(legacyBlock);
158
+ if (customItem) {
159
+ content.push(customItem);
160
+ }
161
+ continue;
162
+ }
163
+ if (skipUnknownBlocks) {
164
+ console.warn(
165
+ `[payload-puck] Unknown block type "${legacyBlock.blockType}" - skipping during migration`
166
+ );
167
+ continue;
168
+ }
169
+ throw new Error(
170
+ `Unknown block type "${legacyBlock.blockType}" encountered during migration`
171
+ );
172
+ }
173
+ const transformedProps = transformBlockProps(legacyBlock, {
174
+ richTextFields,
175
+ mediaFields,
176
+ customTransformers
177
+ });
178
+ const contentItem = {
179
+ type: puckType,
180
+ props: transformedProps
181
+ };
182
+ content.push(contentItem);
183
+ }
184
+ }
185
+ const puckData = {
186
+ root: {
187
+ props: rootProps
188
+ },
189
+ content,
190
+ zones: {}
191
+ };
192
+ return puckData;
193
+ }
194
+ function getMigrationPreview(page, customBlockTypeMap) {
195
+ const warnings = [];
196
+ const blockTypes = [];
197
+ const unmappedBlockTypes = [];
198
+ const effectiveBlockTypeMap = {
199
+ ...blockTypeMap,
200
+ ...customBlockTypeMap
201
+ };
202
+ if (page.layout && Array.isArray(page.layout)) {
203
+ for (const block of page.layout) {
204
+ const legacyBlock = block;
205
+ const puckType = effectiveBlockTypeMap[legacyBlock.blockType];
206
+ if (puckType) {
207
+ blockTypes.push(puckType);
208
+ } else {
209
+ warnings.push(`Unknown block type: ${legacyBlock.blockType}`);
210
+ if (!unmappedBlockTypes.includes(legacyBlock.blockType)) {
211
+ unmappedBlockTypes.push(legacyBlock.blockType);
212
+ }
213
+ }
214
+ }
215
+ }
216
+ return {
217
+ title: page.title,
218
+ slug: page.slug,
219
+ blockCount: page.layout?.length || 0,
220
+ blockTypes,
221
+ warnings,
222
+ unmappedBlockTypes
223
+ };
224
+ }
225
+
226
+ // src/utils/validation.ts
227
+ function isPuckData(data) {
228
+ if (!data || typeof data !== "object") {
229
+ return false;
230
+ }
231
+ const obj = data;
232
+ if (!obj.root || typeof obj.root !== "object") {
233
+ return false;
234
+ }
235
+ const root = obj.root;
236
+ if (!root.props || typeof root.props !== "object") {
237
+ return false;
238
+ }
239
+ if (!Array.isArray(obj.content)) {
240
+ return false;
241
+ }
242
+ for (const item of obj.content) {
243
+ if (!item || typeof item !== "object") {
244
+ return false;
245
+ }
246
+ const contentItem = item;
247
+ if (typeof contentItem.type !== "string") {
248
+ return false;
249
+ }
250
+ if (!contentItem.props || typeof contentItem.props !== "object") {
251
+ return false;
252
+ }
253
+ }
254
+ if (obj.zones !== void 0 && typeof obj.zones !== "object") {
255
+ return false;
256
+ }
257
+ return true;
258
+ }
259
+ function isPuckRootProps(props) {
260
+ if (!props || typeof props !== "object") {
261
+ return false;
262
+ }
263
+ return true;
264
+ }
265
+ function validatePuckData(data, options = {}) {
266
+ const {
267
+ requireContentIds = true,
268
+ validateRoot = true,
269
+ requiredRootProps = ["title"],
270
+ allowedComponentTypes,
271
+ strictComponentTypes = false
272
+ } = options;
273
+ const errors = [];
274
+ const warnings = [];
275
+ if (!data || typeof data !== "object") {
276
+ errors.push("Data must be a non-null object");
277
+ return { valid: false, errors, warnings };
278
+ }
279
+ const obj = data;
280
+ if (!obj.root || typeof obj.root !== "object") {
281
+ errors.push("Missing or invalid root object");
282
+ } else if (validateRoot) {
283
+ const root = obj.root;
284
+ if (!root.props || typeof root.props !== "object") {
285
+ errors.push("Missing root.props object");
286
+ } else {
287
+ const rootProps = root.props;
288
+ for (const prop of requiredRootProps) {
289
+ if (rootProps[prop] === void 0 || rootProps[prop] === null) {
290
+ errors.push(`Missing required root prop: ${prop}`);
291
+ }
292
+ }
293
+ if (rootProps.pageLayout !== void 0) {
294
+ if (typeof rootProps.pageLayout !== "string") {
295
+ errors.push("root.props.pageLayout must be a string");
296
+ }
297
+ }
298
+ }
299
+ }
300
+ if (!Array.isArray(obj.content)) {
301
+ errors.push("content must be an array");
302
+ } else {
303
+ const seenIds = /* @__PURE__ */ new Set();
304
+ obj.content.forEach((item, index) => {
305
+ if (!item || typeof item !== "object") {
306
+ errors.push(`Content item at index ${index} is not an object`);
307
+ return;
308
+ }
309
+ const contentItem = item;
310
+ if (!contentItem.type || typeof contentItem.type !== "string") {
311
+ errors.push(`Content item at index ${index} missing or invalid type`);
312
+ } else if (allowedComponentTypes) {
313
+ if (!allowedComponentTypes.includes(contentItem.type)) {
314
+ const message = `Unknown component type "${contentItem.type}" at index ${index}`;
315
+ if (strictComponentTypes) {
316
+ errors.push(message);
317
+ } else {
318
+ warnings.push(message);
319
+ }
320
+ }
321
+ }
322
+ if (!contentItem.props || typeof contentItem.props !== "object") {
323
+ errors.push(`Content item at index ${index} missing props object`);
324
+ } else if (requireContentIds) {
325
+ const props = contentItem.props;
326
+ if (!props.id || typeof props.id !== "string") {
327
+ errors.push(`Content item at index ${index} missing or invalid props.id`);
328
+ } else {
329
+ if (seenIds.has(props.id)) {
330
+ warnings.push(
331
+ `Duplicate content item ID "${props.id}" at index ${index}`
332
+ );
333
+ }
334
+ seenIds.add(props.id);
335
+ }
336
+ }
337
+ });
338
+ }
339
+ if (obj.zones !== void 0) {
340
+ if (typeof obj.zones !== "object" || obj.zones === null) {
341
+ errors.push("zones must be an object");
342
+ } else {
343
+ const zones = obj.zones;
344
+ for (const [zoneName, zoneContent] of Object.entries(zones)) {
345
+ if (!Array.isArray(zoneContent)) {
346
+ errors.push(`Zone "${zoneName}" content must be an array`);
347
+ continue;
348
+ }
349
+ zoneContent.forEach((item, index) => {
350
+ if (!item || typeof item !== "object") {
351
+ errors.push(`Zone "${zoneName}" item at index ${index} is not an object`);
352
+ return;
353
+ }
354
+ const contentItem = item;
355
+ if (!contentItem.type || typeof contentItem.type !== "string") {
356
+ errors.push(
357
+ `Zone "${zoneName}" item at index ${index} missing or invalid type`
358
+ );
359
+ }
360
+ if (!contentItem.props || typeof contentItem.props !== "object") {
361
+ errors.push(
362
+ `Zone "${zoneName}" item at index ${index} missing props object`
363
+ );
364
+ } else if (requireContentIds) {
365
+ const props = contentItem.props;
366
+ if (!props.id || typeof props.id !== "string") {
367
+ errors.push(
368
+ `Zone "${zoneName}" item at index ${index} missing or invalid props.id`
369
+ );
370
+ }
371
+ }
372
+ });
373
+ }
374
+ }
375
+ }
376
+ return {
377
+ valid: errors.length === 0,
378
+ errors,
379
+ warnings
380
+ };
381
+ }
382
+ function assertPuckData(data, options) {
383
+ const result = validatePuckData(data, options);
384
+ if (!result.valid) {
385
+ throw new Error(
386
+ `Invalid Puck data: ${result.errors.join("; ")}`
387
+ );
388
+ }
389
+ return data;
390
+ }
391
+ function parsePuckDataJson(json, options) {
392
+ let parsed;
393
+ try {
394
+ parsed = JSON.parse(json);
395
+ } catch (e) {
396
+ return {
397
+ valid: false,
398
+ errors: [`Invalid JSON: ${e instanceof Error ? e.message : "Parse error"}`],
399
+ warnings: []
400
+ };
401
+ }
402
+ const result = validatePuckData(parsed, options);
403
+ if (result.valid) {
404
+ return {
405
+ ...result,
406
+ data: parsed
407
+ };
408
+ }
409
+ return result;
410
+ }
411
+
412
+ exports.assertPuckData = assertPuckData;
413
+ exports.blockTypeMap = blockTypeMap;
414
+ exports.generatePuckId = generatePuckId;
415
+ exports.getMigrationPreview = getMigrationPreview;
416
+ exports.isPuckData = isPuckData;
417
+ exports.isPuckRootProps = isPuckRootProps;
418
+ exports.migrateLegacyToPuck = migrateLegacyToPuck;
419
+ exports.parsePuckDataJson = parsePuckDataJson;
420
+ exports.transformBlockProps = transformBlockProps;
421
+ exports.transformMediaReference = transformMediaReference;
422
+ exports.transformRelationshipArray = transformRelationshipArray;
423
+ exports.transformRichText = transformRichText;
424
+ exports.validatePuckData = validatePuckData;
425
+ //# sourceMappingURL=index.js.map
426
+ //# sourceMappingURL=index.js.map