@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,460 @@
1
+ import * as payload from 'payload';
2
+ import { NextRequest } from 'next/server';
3
+ import { Data } from '@measured/puck';
4
+
5
+ /**
6
+ * Authenticated user from the auth system
7
+ */
8
+ interface AuthenticatedUser {
9
+ id: string;
10
+ [key: string]: unknown;
11
+ }
12
+ /**
13
+ * Result of an authentication check
14
+ */
15
+ interface AuthResult {
16
+ authenticated: boolean;
17
+ user?: AuthenticatedUser;
18
+ error?: string;
19
+ }
20
+ /**
21
+ * Result of a permission check
22
+ */
23
+ interface PermissionResult {
24
+ allowed: boolean;
25
+ error?: string;
26
+ }
27
+ /**
28
+ * Authentication and authorization hooks for Puck API routes
29
+ *
30
+ * These hooks allow you to integrate with any authentication system
31
+ * (Better Auth, NextAuth, Payload auth, custom JWT, etc.)
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * // Example with Better Auth
36
+ * const authHooks: PuckApiAuthHooks = {
37
+ * authenticate: async (request) => {
38
+ * const session = await auth.api.getSession({ headers: await headers() })
39
+ * if (!session?.user) return { authenticated: false }
40
+ * return { authenticated: true, user: session.user }
41
+ * },
42
+ * canEdit: async (user, pageId) => {
43
+ * return { allowed: hasRole(user, 'editor') }
44
+ * },
45
+ * }
46
+ * ```
47
+ */
48
+ interface PuckApiAuthHooks {
49
+ /**
50
+ * Authenticate the incoming request
51
+ * Should return the authenticated user or authentication failure
52
+ */
53
+ authenticate: (request: NextRequest) => Promise<AuthResult>;
54
+ /**
55
+ * Check if user can list pages
56
+ * @default Always allowed for authenticated users
57
+ */
58
+ canList?: (user: AuthenticatedUser) => Promise<PermissionResult> | PermissionResult;
59
+ /**
60
+ * Check if user can view a specific page
61
+ * @default Always allowed for authenticated users
62
+ */
63
+ canView?: (user: AuthenticatedUser, pageId: string) => Promise<PermissionResult> | PermissionResult;
64
+ /**
65
+ * Check if user can create new pages
66
+ * @default Always allowed for authenticated users
67
+ */
68
+ canCreate?: (user: AuthenticatedUser) => Promise<PermissionResult> | PermissionResult;
69
+ /**
70
+ * Check if user can edit a specific page
71
+ * @default Always allowed for authenticated users
72
+ */
73
+ canEdit?: (user: AuthenticatedUser, pageId: string) => Promise<PermissionResult> | PermissionResult;
74
+ /**
75
+ * Check if user can publish a specific page (change status to published)
76
+ * @default Same as canEdit
77
+ */
78
+ canPublish?: (user: AuthenticatedUser, pageId: string) => Promise<PermissionResult> | PermissionResult;
79
+ /**
80
+ * Check if user can delete a specific page
81
+ * @default Always allowed for authenticated users
82
+ */
83
+ canDelete?: (user: AuthenticatedUser, pageId: string) => Promise<PermissionResult> | PermissionResult;
84
+ }
85
+ /**
86
+ * Mapping configuration for syncing Puck root.props to Payload fields
87
+ *
88
+ * @example
89
+ * ```typescript
90
+ * // Simple mapping
91
+ * { from: 'pageLayout', to: 'pageLayout' }
92
+ *
93
+ * // Nested field mapping (uses official @payloadcms/plugin-seo convention)
94
+ * { from: 'title', to: 'meta.title' }
95
+ *
96
+ * // With transformation
97
+ * {
98
+ * from: 'publishDate',
99
+ * to: 'publishedAt',
100
+ * transform: (value) => value ? new Date(value as string).toISOString() : null
101
+ * }
102
+ * ```
103
+ */
104
+ interface RootPropsMapping {
105
+ /**
106
+ * Property name in Puck root.props
107
+ */
108
+ from: string;
109
+ /**
110
+ * Field path in Payload document (supports dot notation for nested fields)
111
+ */
112
+ to: string;
113
+ /**
114
+ * Optional transformation function
115
+ */
116
+ transform?: (value: unknown) => unknown;
117
+ }
118
+ /**
119
+ * Error context passed to onError callback
120
+ */
121
+ interface ErrorContext {
122
+ operation: string;
123
+ request: NextRequest;
124
+ pageId?: string;
125
+ }
126
+ /**
127
+ * Configuration for Puck API routes
128
+ *
129
+ * @example
130
+ * ```typescript
131
+ * const config: PuckApiRoutesConfig = {
132
+ * collection: 'pages',
133
+ * auth: {
134
+ * authenticate: async (request) => {
135
+ * // Your auth logic here
136
+ * },
137
+ * canPublish: async (user) => {
138
+ * return { allowed: user.role === 'admin' }
139
+ * },
140
+ * },
141
+ * rootPropsMapping: [
142
+ * { from: 'title', to: 'meta.title' },
143
+ * { from: 'description', to: 'meta.description' },
144
+ * ],
145
+ * enableDrafts: true,
146
+ * }
147
+ * ```
148
+ */
149
+ interface PuckApiRoutesConfig {
150
+ /**
151
+ * Payload collection slug for pages
152
+ * @default 'pages'
153
+ */
154
+ collection?: string;
155
+ /**
156
+ * Payload configuration - import from @payload-config
157
+ * Required for Turbopack/Next.js 16+ compatibility
158
+ *
159
+ * @example
160
+ * ```typescript
161
+ * import config from '@payload-config'
162
+ *
163
+ * createPuckApiRoutesWithId({
164
+ * payloadConfig: config,
165
+ * // ...
166
+ * })
167
+ * ```
168
+ */
169
+ payloadConfig: Promise<payload.SanitizedConfig>;
170
+ /**
171
+ * Authentication and authorization hooks
172
+ */
173
+ auth: PuckApiAuthHooks;
174
+ /**
175
+ * Custom mappings from Puck root.props to Payload fields
176
+ * These are merged with the default mappings
177
+ */
178
+ rootPropsMapping?: RootPropsMapping[];
179
+ /**
180
+ * Default Puck data for new pages
181
+ */
182
+ defaultPuckData?: Data;
183
+ /**
184
+ * Enable draft mode for page creation/updates
185
+ * @default true
186
+ */
187
+ enableDrafts?: boolean;
188
+ /**
189
+ * Custom error handler for logging/monitoring
190
+ */
191
+ onError?: (error: unknown, context: ErrorContext) => void;
192
+ }
193
+ /**
194
+ * Context passed to Next.js App Router route handlers
195
+ */
196
+ interface RouteHandlerContext {
197
+ params: Promise<Record<string, string>>;
198
+ }
199
+ /**
200
+ * Context for route handlers that require an id parameter
201
+ */
202
+ interface RouteHandlerWithIdContext {
203
+ params: Promise<{
204
+ id: string;
205
+ }>;
206
+ }
207
+ /**
208
+ * Next.js App Router route handler type
209
+ */
210
+ type RouteHandler = (request: NextRequest, context: RouteHandlerContext) => Promise<Response>;
211
+ /**
212
+ * Next.js App Router route handler type for [id] routes
213
+ */
214
+ type RouteHandlerWithId = (request: NextRequest, context: RouteHandlerWithIdContext) => Promise<Response>;
215
+ /**
216
+ * Route handlers returned by createPuckApiRoutes
217
+ */
218
+ interface PuckApiRouteHandlers {
219
+ GET: RouteHandler;
220
+ POST: RouteHandler;
221
+ }
222
+ /**
223
+ * Route handlers returned by createPuckApiRoutesWithId
224
+ */
225
+ interface PuckApiRouteWithIdHandlers {
226
+ GET: RouteHandlerWithId;
227
+ PATCH: RouteHandlerWithId;
228
+ DELETE: RouteHandlerWithId;
229
+ }
230
+ /**
231
+ * Request body for creating a new page
232
+ */
233
+ interface CreatePageBody {
234
+ title: string;
235
+ slug: string;
236
+ puckData?: Data;
237
+ status?: 'draft' | 'published';
238
+ }
239
+ /**
240
+ * Request body for updating a page
241
+ */
242
+ interface UpdatePageBody {
243
+ puckData?: Data;
244
+ title?: string;
245
+ slug?: string;
246
+ status?: 'draft' | 'published';
247
+ /**
248
+ * When true, save as draft without publishing.
249
+ * Used by Payload's versions.drafts system.
250
+ */
251
+ draft?: boolean;
252
+ }
253
+ /**
254
+ * Standard API response wrapper
255
+ */
256
+ interface ApiResponse<T = unknown> {
257
+ doc?: T;
258
+ docs?: T[];
259
+ error?: string;
260
+ success?: boolean;
261
+ totalDocs?: number;
262
+ totalPages?: number;
263
+ page?: number;
264
+ limit?: number;
265
+ hasPrevPage?: boolean;
266
+ hasNextPage?: boolean;
267
+ }
268
+ /**
269
+ * A version entry from Payload's versions system
270
+ */
271
+ interface PageVersion {
272
+ id: string;
273
+ parent: string;
274
+ version: {
275
+ title?: string;
276
+ slug?: string;
277
+ puckData?: Data;
278
+ _status?: 'draft' | 'published';
279
+ updatedAt: string;
280
+ createdAt: string;
281
+ };
282
+ createdAt: string;
283
+ updatedAt: string;
284
+ autosave?: boolean;
285
+ latest?: boolean;
286
+ }
287
+ /**
288
+ * Route handlers returned by createPuckApiRoutesVersions
289
+ */
290
+ interface PuckApiVersionsRouteHandlers {
291
+ GET: RouteHandlerWithId;
292
+ POST: RouteHandlerWithId;
293
+ }
294
+
295
+ /**
296
+ * Create API route handlers for /api/puck/pages
297
+ *
298
+ * Provides GET (list pages) and POST (create page) handlers
299
+ * with configurable authentication and authorization.
300
+ *
301
+ * @example
302
+ * ```typescript
303
+ * // src/app/api/puck/pages/route.ts
304
+ * import { createPuckApiRoutes } from '@delmaredigital/payload-puck/api'
305
+ * import config from '@payload-config'
306
+ *
307
+ * export const { GET, POST } = createPuckApiRoutes({
308
+ * collection: 'pages',
309
+ * payloadConfig: config,
310
+ * auth: {
311
+ * authenticate: async (request) => {
312
+ * const session = await getSession(request)
313
+ * if (!session?.user) return { authenticated: false }
314
+ * return { authenticated: true, user: session.user }
315
+ * },
316
+ * canCreate: async (user) => {
317
+ * return { allowed: user.role === 'admin' || user.role === 'editor' }
318
+ * },
319
+ * },
320
+ * })
321
+ * ```
322
+ */
323
+ declare function createPuckApiRoutes(routeConfig: PuckApiRoutesConfig): PuckApiRouteHandlers;
324
+
325
+ /**
326
+ * Create API route handlers for /api/puck/pages/[id]
327
+ *
328
+ * Provides GET (fetch page), PATCH (update page), and DELETE (delete page)
329
+ * handlers with configurable authentication and authorization.
330
+ *
331
+ * @example
332
+ * ```typescript
333
+ * // src/app/api/puck/pages/[id]/route.ts
334
+ * import { createPuckApiRoutesWithId } from '@delmaredigital/payload-puck/api'
335
+ * import config from '@payload-config'
336
+ *
337
+ * export const { GET, PATCH, DELETE } = createPuckApiRoutesWithId({
338
+ * collection: 'pages',
339
+ * payloadConfig: config,
340
+ * auth: {
341
+ * authenticate: async (request) => {
342
+ * const session = await getSession(request)
343
+ * if (!session?.user) return { authenticated: false }
344
+ * return { authenticated: true, user: session.user }
345
+ * },
346
+ * canPublish: async (user) => {
347
+ * return { allowed: user.role === 'admin' }
348
+ * },
349
+ * },
350
+ * rootPropsMapping: [
351
+ * { from: 'customField', to: 'customPayloadField' },
352
+ * ],
353
+ * })
354
+ * ```
355
+ */
356
+ declare function createPuckApiRoutesWithId(routeConfig: PuckApiRoutesConfig): PuckApiRouteWithIdHandlers;
357
+
358
+ /**
359
+ * Create API route handlers for /api/puck/pages/[id]/versions
360
+ *
361
+ * Provides GET (list versions) and POST (restore version) handlers
362
+ * for managing page version history.
363
+ *
364
+ * @example
365
+ * ```typescript
366
+ * // src/app/api/puck/pages/[id]/versions/route.ts
367
+ * import { createPuckApiRoutesVersions } from '@delmaredigital/payload-puck/api'
368
+ * import config from '@payload-config'
369
+ *
370
+ * export const { GET, POST } = createPuckApiRoutesVersions({
371
+ * collection: 'pages',
372
+ * payloadConfig: config,
373
+ * auth: {
374
+ * authenticate: async (request) => {
375
+ * const session = await getSession(request)
376
+ * if (!session?.user) return { authenticated: false }
377
+ * return { authenticated: true, user: session.user }
378
+ * },
379
+ * },
380
+ * })
381
+ * ```
382
+ */
383
+ declare function createPuckApiRoutesVersions(routeConfig: PuckApiRoutesConfig): PuckApiVersionsRouteHandlers;
384
+
385
+ /**
386
+ * Default mappings from Puck root.props to Payload fields
387
+ *
388
+ * These mappings sync common page properties from Puck's visual editor
389
+ * to the corresponding Payload collection fields.
390
+ *
391
+ * SEO fields use the official @payloadcms/plugin-seo convention: meta.title, meta.description
392
+ */
393
+ declare const DEFAULT_ROOT_PROPS_MAPPINGS: RootPropsMapping[];
394
+ /**
395
+ * Set a nested value in an object using dot notation path
396
+ *
397
+ * @example
398
+ * ```typescript
399
+ * const obj = {}
400
+ * setNestedValue(obj, 'meta.title', 'My Title')
401
+ * // Result: { meta: { title: 'My Title' } }
402
+ * ```
403
+ */
404
+ declare function setNestedValue(obj: Record<string, unknown>, path: string, value: unknown): void;
405
+ /**
406
+ * Get a nested value from an object using dot notation path
407
+ *
408
+ * @example
409
+ * ```typescript
410
+ * const obj = { meta: { title: 'My Title' } }
411
+ * getNestedValue(obj, 'meta.title') // 'My Title'
412
+ * ```
413
+ */
414
+ declare function getNestedValue(obj: Record<string, unknown>, path: string): unknown;
415
+ /**
416
+ * Merge mappings, with custom mappings taking precedence over defaults
417
+ *
418
+ * @param customMappings - Custom mappings to merge with defaults
419
+ * @returns Merged array of mappings with duplicates resolved (custom wins)
420
+ */
421
+ declare function mergeMappings(customMappings?: RootPropsMapping[]): RootPropsMapping[];
422
+ /**
423
+ * Map Puck root.props to Payload field update data
424
+ *
425
+ * Takes the root.props from Puck data and maps them to the appropriate
426
+ * Payload fields based on the provided mappings.
427
+ *
428
+ * @param rootProps - The root.props object from Puck data
429
+ * @param customMappings - Optional custom mappings to merge with defaults
430
+ * @returns Object ready to be spread into Payload update data
431
+ *
432
+ * @example
433
+ * ```typescript
434
+ * const rootProps = {
435
+ * title: 'My Page',
436
+ * metaTitle: 'My Page | Site Name',
437
+ * metaDescription: 'Description here',
438
+ * }
439
+ *
440
+ * const updateData = mapRootPropsToPayloadFields(rootProps)
441
+ * // Result: {
442
+ * // title: 'My Page',
443
+ * // meta: {
444
+ * // title: 'My Page | Site Name',
445
+ * // description: 'Description here',
446
+ * // },
447
+ * // }
448
+ * ```
449
+ */
450
+ declare function mapRootPropsToPayloadFields(rootProps: Record<string, unknown>, customMappings?: RootPropsMapping[]): Record<string, unknown>;
451
+ /**
452
+ * Deep merge objects, with source values overwriting target values
453
+ *
454
+ * @param target - The target object to merge into
455
+ * @param source - The source object to merge from
456
+ * @returns The merged object (target is modified in place)
457
+ */
458
+ declare function deepMerge(target: Record<string, unknown>, source: Record<string, unknown>): Record<string, unknown>;
459
+
460
+ export { type ApiResponse, type AuthResult, type AuthenticatedUser, type CreatePageBody, DEFAULT_ROOT_PROPS_MAPPINGS, type ErrorContext, type PageVersion, type PermissionResult, type PuckApiAuthHooks, type PuckApiRouteHandlers, type PuckApiRouteWithIdHandlers, type PuckApiRoutesConfig, type PuckApiVersionsRouteHandlers, type RootPropsMapping, type RouteHandler, type RouteHandlerContext, type RouteHandlerWithId, type RouteHandlerWithIdContext, type UpdatePageBody, createPuckApiRoutes, createPuckApiRoutesVersions, createPuckApiRoutesWithId, deepMerge, getNestedValue, mapRootPropsToPayloadFields, mergeMappings, setNestedValue };