create-nextblock 0.16.1 → 0.16.2

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 (258) hide show
  1. package/docker-template/.dockerignore +1 -1
  2. package/libs/db/tsconfig.lib.json +3 -3
  3. package/libs/editor/tsconfig.lib.json +3 -3
  4. package/libs/ui/tsconfig.lib.json +3 -3
  5. package/libs/utils/tsconfig.json +3 -3
  6. package/package.json +50 -50
  7. package/project.json +19 -19
  8. package/templates/nextblock-template/.dockerignore +1 -1
  9. package/templates/nextblock-template/.swcrc +30 -30
  10. package/templates/nextblock-template/AGENTS.md +9 -9
  11. package/templates/nextblock-template/CLAUDE.md +1 -1
  12. package/templates/nextblock-template/app/(auth-pages)/layout.tsx +9 -9
  13. package/templates/nextblock-template/app/(auth-pages)/post-sign-in/page.tsx +27 -27
  14. package/templates/nextblock-template/app/.well-known/ucp/route.ts +16 -16
  15. package/templates/nextblock-template/app/ToasterProvider.tsx +26 -26
  16. package/templates/nextblock-template/app/[slug]/pageClientActions.ts +7 -7
  17. package/templates/nextblock-template/app/actions/contactSellerActions.test.ts +280 -280
  18. package/templates/nextblock-template/app/actions/contactSellerActions.ts +222 -222
  19. package/templates/nextblock-template/app/actions/email-retry.test.ts +62 -62
  20. package/templates/nextblock-template/app/actions/email.ts +241 -241
  21. package/templates/nextblock-template/app/actions/formActions.ts +245 -245
  22. package/templates/nextblock-template/app/actions/interactions.ts +489 -489
  23. package/templates/nextblock-template/app/actions/threadActions.ts +166 -166
  24. package/templates/nextblock-template/app/actions/visibilityActions.ts +210 -210
  25. package/templates/nextblock-template/app/api/ai/seo/alt-text/route.ts +221 -221
  26. package/templates/nextblock-template/app/api/ai/seo/metadata/route.ts +186 -186
  27. package/templates/nextblock-template/app/api/checkout/freemius/sync/route.ts +29 -29
  28. package/templates/nextblock-template/app/api/checkout/route.ts +162 -162
  29. package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +3092 -3092
  30. package/templates/nextblock-template/app/api/mcp/route.ts +415 -415
  31. package/templates/nextblock-template/app/api/media/record/route.ts +160 -160
  32. package/templates/nextblock-template/app/api/search/route.ts +43 -43
  33. package/templates/nextblock-template/app/api/view/route.ts +114 -114
  34. package/templates/nextblock-template/app/api/visual-editing/block-draft/route.ts +47 -47
  35. package/templates/nextblock-template/app/api/visual-editing/product-draft/route.ts +47 -47
  36. package/templates/nextblock-template/app/auth/callback/route.ts +31 -31
  37. package/templates/nextblock-template/app/cart/page.tsx +7 -7
  38. package/templates/nextblock-template/app/checkout/UcpCartHydrator.tsx +20 -20
  39. package/templates/nextblock-template/app/checkout/page.tsx +57 -57
  40. package/templates/nextblock-template/app/cms/CmsClientLayout.tsx +558 -558
  41. package/templates/nextblock-template/app/cms/blocks/components/BlockEditorModal.tsx +241 -241
  42. package/templates/nextblock-template/app/cms/blocks/components/MediaLibraryModal.tsx +149 -149
  43. package/templates/nextblock-template/app/cms/blocks/editors/FormBlockEditor.tsx +304 -304
  44. package/templates/nextblock-template/app/cms/blocks/editors/ImageBlockEditor.tsx +406 -406
  45. package/templates/nextblock-template/app/cms/components/ContactReminderBanner.tsx +75 -75
  46. package/templates/nextblock-template/app/cms/components/CortexAiActiveContext.tsx +23 -23
  47. package/templates/nextblock-template/app/cms/components/CortexAiPageContext.tsx +58 -58
  48. package/templates/nextblock-template/app/cms/components/FeatureImageField.tsx +254 -254
  49. package/templates/nextblock-template/app/cms/components/FeedbackModal.tsx +36 -36
  50. package/templates/nextblock-template/app/cms/components/PaymentsReminderBanner.tsx +58 -58
  51. package/templates/nextblock-template/app/cms/components/VisibilityBadge.tsx +62 -62
  52. package/templates/nextblock-template/app/cms/components/VisibilityControl.tsx +542 -542
  53. package/templates/nextblock-template/app/cms/coupons/[id]/edit/page.tsx +16 -16
  54. package/templates/nextblock-template/app/cms/coupons/page.tsx +16 -16
  55. package/templates/nextblock-template/app/cms/dashboard/actions.ts +228 -228
  56. package/templates/nextblock-template/app/cms/dashboard/components/DashboardComponents.tsx +200 -200
  57. package/templates/nextblock-template/app/cms/inquiries/actions.ts +66 -66
  58. package/templates/nextblock-template/app/cms/inquiries/page.tsx +12 -12
  59. package/templates/nextblock-template/app/cms/interactions/page.tsx +12 -12
  60. package/templates/nextblock-template/app/cms/layout.tsx +101 -101
  61. package/templates/nextblock-template/app/cms/media/components/FolderNavigator.tsx +273 -273
  62. package/templates/nextblock-template/app/cms/media/components/FolderTree.tsx +122 -122
  63. package/templates/nextblock-template/app/cms/media/components/MediaGridClient.tsx +69 -69
  64. package/templates/nextblock-template/app/cms/messages/MessagesClient.tsx +661 -661
  65. package/templates/nextblock-template/app/cms/messages/actions.ts +404 -404
  66. package/templates/nextblock-template/app/cms/messages/loadInbox.ts +333 -333
  67. package/templates/nextblock-template/app/cms/messages/page.tsx +87 -87
  68. package/templates/nextblock-template/app/cms/messages/require-admin.ts +37 -37
  69. package/templates/nextblock-template/app/cms/navigation/components/NavigationMenuDnd.tsx +3 -3
  70. package/templates/nextblock-template/app/cms/pages/components/PageForm.tsx +649 -649
  71. package/templates/nextblock-template/app/cms/posts/components/PostForm.tsx +618 -618
  72. package/templates/nextblock-template/app/cms/products/[id]/edit/page.tsx +370 -370
  73. package/templates/nextblock-template/app/cms/products/attributes/page.tsx +12 -12
  74. package/templates/nextblock-template/app/cms/products/inventory/page.tsx +13 -13
  75. package/templates/nextblock-template/app/cms/products/productFormData.ts +133 -133
  76. package/templates/nextblock-template/app/cms/products/settings/page.tsx +5 -5
  77. package/templates/nextblock-template/app/cms/revisions/actions.ts +332 -332
  78. package/templates/nextblock-template/app/cms/revisions/service.test.ts +498 -498
  79. package/templates/nextblock-template/app/cms/revisions/service.ts +569 -569
  80. package/templates/nextblock-template/app/cms/revisions/utils.ts +304 -304
  81. package/templates/nextblock-template/app/cms/settings/cortex-ai/CortexAiSettingsClient.tsx +948 -948
  82. package/templates/nextblock-template/app/cms/settings/cortex-ai/McpServerSettingsCard.tsx +628 -628
  83. package/templates/nextblock-template/app/cms/settings/cortex-ai/mcp-actions.ts +230 -230
  84. package/templates/nextblock-template/app/cms/settings/cortex-ai/require-admin.ts +34 -34
  85. package/templates/nextblock-template/app/cms/settings/currencies/actions.ts +331 -331
  86. package/templates/nextblock-template/app/cms/settings/currencies/page.tsx +494 -494
  87. package/templates/nextblock-template/app/cms/settings/email/components/EmailForm.tsx +227 -227
  88. package/templates/nextblock-template/app/cms/settings/extra-translations/ExtraTranslationsWorkspace.tsx +767 -767
  89. package/templates/nextblock-template/app/cms/settings/extra-translations/actions.ts +276 -276
  90. package/templates/nextblock-template/app/cms/settings/extra-translations/page.tsx +93 -93
  91. package/templates/nextblock-template/app/cms/settings/global-css/components/ThemeEditor.tsx +382 -382
  92. package/templates/nextblock-template/app/cms/settings/global-css/components/ThemeManager.tsx +267 -267
  93. package/templates/nextblock-template/app/cms/settings/global-css/page.tsx +40 -40
  94. package/templates/nextblock-template/app/cms/settings/global-css/theme-actions.ts +259 -259
  95. package/templates/nextblock-template/app/cms/settings/logos/[id]/edit/page.tsx +7 -7
  96. package/templates/nextblock-template/app/cms/settings/logos/components/BrandingSettingsForm.tsx +339 -339
  97. package/templates/nextblock-template/app/cms/settings/logos/new/page.tsx +8 -8
  98. package/templates/nextblock-template/app/cms/settings/seo/RedirectsCard.tsx +514 -514
  99. package/templates/nextblock-template/app/cms/settings/seo/RobotsCard.tsx +529 -529
  100. package/templates/nextblock-template/app/cms/settings/seo/SeoSettingsClient.tsx +57 -57
  101. package/templates/nextblock-template/app/cms/settings/seo/actions.ts +448 -448
  102. package/templates/nextblock-template/app/cms/settings/seo/mappers.ts +93 -93
  103. package/templates/nextblock-template/app/cms/settings/seo/page.tsx +46 -46
  104. package/templates/nextblock-template/app/cms/settings/seo/require-admin.ts +47 -47
  105. package/templates/nextblock-template/app/cms/settings/site-scripts/page.tsx +51 -51
  106. package/templates/nextblock-template/app/cms/settings/taxes/page.tsx +21 -21
  107. package/templates/nextblock-template/app/cms/shipping/page.tsx +20 -20
  108. package/templates/nextblock-template/app/cms/users/components/DeleteUserButton.tsx +12 -12
  109. package/templates/nextblock-template/app/layout.tsx +671 -671
  110. package/templates/nextblock-template/app/lib/seo.ts +319 -319
  111. package/templates/nextblock-template/app/lib/ucp/protocol.ts +190 -190
  112. package/templates/nextblock-template/app/lib/ucp/server.test.ts +56 -56
  113. package/templates/nextblock-template/app/product/[slug]/page.tsx +502 -502
  114. package/templates/nextblock-template/app/profile/ProfilePageHeader.tsx +16 -16
  115. package/templates/nextblock-template/app/profile/ProfilePageMissingState.tsx +9 -9
  116. package/templates/nextblock-template/app/profile/account-links.ts +22 -22
  117. package/templates/nextblock-template/app/profile/orders/CustomerOrdersPageClient.tsx +124 -124
  118. package/templates/nextblock-template/app/profile/orders/page.tsx +19 -19
  119. package/templates/nextblock-template/app/profile/password/PasswordSettingsPageClient.tsx +128 -128
  120. package/templates/nextblock-template/app/profile/password/actions.ts +59 -59
  121. package/templates/nextblock-template/app/profile/password/page.tsx +27 -27
  122. package/templates/nextblock-template/app/providers.tsx +96 -96
  123. package/templates/nextblock-template/app/robots.ts +123 -123
  124. package/templates/nextblock-template/app/thread/ThreadView.tsx +164 -164
  125. package/templates/nextblock-template/app/thread/[token]/route.ts +57 -57
  126. package/templates/nextblock-template/app/thread/layout.tsx +15 -15
  127. package/templates/nextblock-template/app/thread/page.tsx +98 -98
  128. package/templates/nextblock-template/app/ucp/v1/carts/[id]/cancel/route.ts +38 -38
  129. package/templates/nextblock-template/app/ucp/v1/carts/[id]/route.ts +68 -68
  130. package/templates/nextblock-template/app/ucp/v1/carts/route.ts +35 -35
  131. package/templates/nextblock-template/app/ucp/v1/catalog/lookup/route.ts +35 -35
  132. package/templates/nextblock-template/app/ucp/v1/catalog/product/route.ts +35 -35
  133. package/templates/nextblock-template/app/ucp/v1/catalog/search/route.ts +34 -34
  134. package/templates/nextblock-template/components/BlockRenderer.tsx +312 -312
  135. package/templates/nextblock-template/components/CartDrawerLoader.tsx +7 -7
  136. package/templates/nextblock-template/components/CartTranslator.tsx +210 -210
  137. package/templates/nextblock-template/components/ContactSellerSection.tsx +188 -188
  138. package/templates/nextblock-template/components/DeferredCartDrawer.tsx +23 -23
  139. package/templates/nextblock-template/components/DeferredCartTranslator.tsx +51 -51
  140. package/templates/nextblock-template/components/DeferredGlobalSearch.tsx +68 -68
  141. package/templates/nextblock-template/components/DeferredGoogleTagManager.tsx +70 -70
  142. package/templates/nextblock-template/components/DeferredSpeedInsights.tsx +69 -69
  143. package/templates/nextblock-template/components/FooterNavigation.tsx +32 -32
  144. package/templates/nextblock-template/components/GlobalSearch.tsx +557 -557
  145. package/templates/nextblock-template/components/Header.tsx +38 -38
  146. package/templates/nextblock-template/components/HtmlScriptExecutor.tsx +47 -47
  147. package/templates/nextblock-template/components/LanguageSwitcher.tsx +2 -2
  148. package/templates/nextblock-template/components/PostCommentsSection.tsx +378 -378
  149. package/templates/nextblock-template/components/ProductReviewsSection.tsx +426 -426
  150. package/templates/nextblock-template/components/SiteScripts.tsx +56 -56
  151. package/templates/nextblock-template/components/StaffReplies.tsx +102 -102
  152. package/templates/nextblock-template/components/blocks/PostCardSkeleton.tsx +12 -12
  153. package/templates/nextblock-template/components/blocks/PostsGridBlock.tsx +12 -12
  154. package/templates/nextblock-template/components/blocks/PostsGridClient.tsx +48 -48
  155. package/templates/nextblock-template/components/blocks/TestimonialBlock.tsx +9 -9
  156. package/templates/nextblock-template/components/blocks/ecommerceRendererLoaders.ts +23 -23
  157. package/templates/nextblock-template/components/blocks/publicRendererLoaders.ts +25 -25
  158. package/templates/nextblock-template/components/blocks/renderers/ButtonBlockRenderer.tsx +92 -92
  159. package/templates/nextblock-template/components/blocks/renderers/CartBlockRenderer.tsx +18 -18
  160. package/templates/nextblock-template/components/blocks/renderers/CheckoutBlockRenderer.tsx +20 -20
  161. package/templates/nextblock-template/components/blocks/renderers/FeaturedProductBlockRenderer.tsx +25 -25
  162. package/templates/nextblock-template/components/blocks/renderers/FormBlockRenderer.tsx +385 -385
  163. package/templates/nextblock-template/components/blocks/renderers/PostsGridBlockRenderer.tsx +24 -24
  164. package/templates/nextblock-template/components/blocks/renderers/ProductDetailsBlockRenderer.tsx +157 -157
  165. package/templates/nextblock-template/components/blocks/renderers/ProductGridBlockRenderer.tsx +34 -34
  166. package/templates/nextblock-template/components/blocks/renderers/SectionBlockRenderer.tsx +612 -612
  167. package/templates/nextblock-template/components/blocks/renderers/TestimonialBlockRenderer.tsx +57 -57
  168. package/templates/nextblock-template/components/blocks/renderers/inline/AlertWidgetRenderer.tsx +2 -2
  169. package/templates/nextblock-template/components/blocks/renderers/inline/CtaWidgetRenderer.tsx +2 -2
  170. package/templates/nextblock-template/components/blocks/types.ts +7 -7
  171. package/templates/nextblock-template/components/commerce/PaymentReadinessBoundary.tsx +32 -32
  172. package/templates/nextblock-template/components/env-var-warning.tsx +3 -3
  173. package/templates/nextblock-template/components/form-message.tsx +32 -32
  174. package/templates/nextblock-template/components/seo/GenerateMetaButton.tsx +137 -137
  175. package/templates/nextblock-template/components/seo/PageSeoAuditSection.tsx +244 -244
  176. package/templates/nextblock-template/components/seo/SeoAuditPanel.tsx +749 -749
  177. package/templates/nextblock-template/components/seo/SeoIssueList.tsx +195 -195
  178. package/templates/nextblock-template/components/seo/SeoScoreDial.tsx +144 -144
  179. package/templates/nextblock-template/components/seo/SocialPreview.tsx +243 -243
  180. package/templates/nextblock-template/components/seo/SocialPreviewDialog.tsx +110 -110
  181. package/templates/nextblock-template/components/submit-button.tsx +23 -23
  182. package/templates/nextblock-template/components/theme-icon.tsx +78 -78
  183. package/templates/nextblock-template/components/theme-switcher.tsx +85 -85
  184. package/templates/nextblock-template/context/AuthContext.tsx +23 -23
  185. package/templates/nextblock-template/context/ThemeCatalogContext.tsx +44 -44
  186. package/templates/nextblock-template/docs/01-PROJECT-OVERVIEW.md +94 -94
  187. package/templates/nextblock-template/docs/03-CMS-AND-EDITOR.md +77 -77
  188. package/templates/nextblock-template/docs/13-STAYING-UP-TO-DATE.md +372 -372
  189. package/templates/nextblock-template/docs/14-MESSAGES-INBOX.md +309 -309
  190. package/templates/nextblock-template/docs/README.md +42 -42
  191. package/templates/nextblock-template/docs/TECHNICAL_SPECIFICATION.md +12506 -12506
  192. package/templates/nextblock-template/hooks/use-hotkeys.ts +21 -21
  193. package/templates/nextblock-template/hooks/useGlobalSearch.ts +101 -101
  194. package/templates/nextblock-template/index.d.ts +7 -7
  195. package/templates/nextblock-template/lib/auth-redirects.ts +46 -46
  196. package/templates/nextblock-template/lib/blocks/blockColors.test.ts +134 -134
  197. package/templates/nextblock-template/lib/blocks/blockColors.ts +175 -175
  198. package/templates/nextblock-template/lib/blocks/blockRegistry.ts +761 -761
  199. package/templates/nextblock-template/lib/blocks/inlineScriptNonce.ts +20 -20
  200. package/templates/nextblock-template/lib/cms/contact-reminder.ts +64 -64
  201. package/templates/nextblock-template/lib/cms/payments-reminder.test.ts +135 -0
  202. package/templates/nextblock-template/lib/cms/payments-reminder.ts +30 -23
  203. package/templates/nextblock-template/lib/cms/unread-messages.ts +42 -42
  204. package/templates/nextblock-template/lib/commerce/seller-contact.ts +162 -162
  205. package/templates/nextblock-template/lib/config/email-settings.ts +323 -323
  206. package/templates/nextblock-template/lib/config/email-tls.test.ts +57 -57
  207. package/templates/nextblock-template/lib/cortex-ai/alt-text-request.ts +86 -86
  208. package/templates/nextblock-template/lib/cortex-ai/sandbox-headers.ts +60 -60
  209. package/templates/nextblock-template/lib/email/placeholder-address.test.ts +59 -59
  210. package/templates/nextblock-template/lib/email/placeholder-address.ts +39 -39
  211. package/templates/nextblock-template/lib/messages/thread-reference.test.ts +70 -70
  212. package/templates/nextblock-template/lib/messages/thread-token.test.ts +93 -93
  213. package/templates/nextblock-template/lib/messages/thread-token.ts +157 -157
  214. package/templates/nextblock-template/lib/messages/threads.ts +579 -579
  215. package/templates/nextblock-template/lib/posts/readTime.ts +60 -60
  216. package/templates/nextblock-template/lib/publishing/viewUrl.ts +26 -26
  217. package/templates/nextblock-template/lib/search/types.ts +27 -27
  218. package/templates/nextblock-template/lib/seo/alt-text-write-back.test.ts +154 -154
  219. package/templates/nextblock-template/lib/seo/alt-text-write-back.ts +109 -109
  220. package/templates/nextblock-template/lib/seo/block-content.ts +123 -123
  221. package/templates/nextblock-template/lib/seo/fix-prompts.test.ts +242 -242
  222. package/templates/nextblock-template/lib/seo/fix-prompts.ts +204 -204
  223. package/templates/nextblock-template/lib/seo/page-audit-context.tsx +140 -140
  224. package/templates/nextblock-template/lib/seo/page-document.test.ts +350 -350
  225. package/templates/nextblock-template/lib/seo/page-document.ts +412 -412
  226. package/templates/nextblock-template/lib/seo/redirect-store.test.ts +479 -479
  227. package/templates/nextblock-template/lib/seo/redirect-store.ts +466 -466
  228. package/templates/nextblock-template/lib/seo/robots-settings-signature.test.ts +102 -102
  229. package/templates/nextblock-template/lib/seo/robots-settings-signature.ts +41 -41
  230. package/templates/nextblock-template/lib/seo/robots-txt.test.ts +370 -370
  231. package/templates/nextblock-template/lib/seo/robots-txt.ts +510 -510
  232. package/templates/nextblock-template/lib/setup/migrations-bundle.ts +177 -177
  233. package/templates/nextblock-template/lib/site-scripts/revisions.ts +71 -71
  234. package/templates/nextblock-template/lib/site-scripts/types.ts +46 -46
  235. package/templates/nextblock-template/lib/site-url.test.ts +89 -89
  236. package/templates/nextblock-template/lib/site-url.ts +102 -102
  237. package/templates/nextblock-template/lib/themes/buildThemeCss.ts +124 -124
  238. package/templates/nextblock-template/lib/themes/tokenColor.ts +31 -31
  239. package/templates/nextblock-template/lib/themes/tokens.ts +143 -143
  240. package/templates/nextblock-template/lib/visual-editing/draft-content.test.ts +105 -105
  241. package/templates/nextblock-template/lib/visual-editing/draft-route.test.ts +42 -42
  242. package/templates/nextblock-template/lib/visual-editing/edit-info.test.ts +143 -143
  243. package/templates/nextblock-template/lib/visual-editing/edit-info.ts +94 -94
  244. package/templates/nextblock-template/lib/visual-editing/product-drafts.test.ts +81 -81
  245. package/templates/nextblock-template/lib/zod-config.ts +5 -5
  246. package/templates/nextblock-template/next-env.d.ts +1 -2
  247. package/templates/nextblock-template/package.json +1 -1
  248. package/templates/nextblock-template/postcss.config.js +6 -6
  249. package/templates/nextblock-template/scripts/backup.js +115 -115
  250. package/templates/nextblock-template/scripts/restore.js +385 -385
  251. package/templates/nextblock-template/scripts/validate-editor-block-schema.ts +112 -112
  252. package/templates/nextblock-template/tailwind.config.js +25 -25
  253. package/templates/nextblock-template/tools/build-migrate.mjs +102 -102
  254. package/templates/nextblock-template/tools/configure-supabase-auth.js +282 -282
  255. package/templates/nextblock-template/tools/deploy-supabase.js +159 -159
  256. package/templates/nextblock-template/tools/lib/migrate-core.mjs +569 -569
  257. package/templates/nextblock-template/tools/update.mjs +1303 -1303
  258. package/tsconfig.base.json +3 -3
@@ -1,105 +1,105 @@
1
- import { describe, expect, it, vi } from "vitest";
2
-
3
- vi.mock("server-only", () => ({}));
4
- vi.mock("@nextblock-cms/db/server", () => ({
5
- createClient: vi.fn(),
6
- getServiceRoleSupabaseClient: vi.fn(),
7
- getSsgSupabaseClient: vi.fn(),
8
- }));
9
-
10
- import {
11
- findDraftBlock,
12
- updateDraftBlockContent,
13
- } from "./draft-content";
14
- import type { ContentDraftSnapshot } from "./types";
15
-
16
- describe("draft content merge helpers", () => {
17
- it("updates top-level draft block content without mutating the original snapshot", () => {
18
- const snapshot: ContentDraftSnapshot = {
19
- meta: {},
20
- blocks: [
21
- {
22
- id: 10,
23
- page_id: 1,
24
- post_id: null,
25
- language_id: 1,
26
- block_type: "text",
27
- content: { html_content: "<p>Old</p>" },
28
- order: 0,
29
- },
30
- ],
31
- };
32
-
33
- const next = updateDraftBlockContent(
34
- snapshot,
35
- {
36
- parentType: "page",
37
- parentId: 1,
38
- target: {
39
- kind: "top-level",
40
- blockId: 10,
41
- blockIndex: 0,
42
- blockType: "text",
43
- },
44
- },
45
- { html_content: "<p>New</p>" }
46
- );
47
-
48
- expect(snapshot.blocks[0].content).toEqual({ html_content: "<p>Old</p>" });
49
- expect(next.blocks[0].content).toEqual({ html_content: "<p>New</p>" });
50
- });
51
-
52
- it("finds and updates nested draft block content", () => {
53
- const snapshot: ContentDraftSnapshot = {
54
- meta: {},
55
- blocks: [
56
- {
57
- id: 20,
58
- page_id: 1,
59
- post_id: null,
60
- language_id: 1,
61
- block_type: "section",
62
- content: {
63
- column_blocks: [
64
- [
65
- {
66
- block_type: "heading",
67
- content: { text_content: "Old heading", level: 2 },
68
- },
69
- ],
70
- ],
71
- },
72
- order: 0,
73
- },
74
- ],
75
- };
76
- const request = {
77
- parentType: "page" as const,
78
- parentId: 1,
79
- target: {
80
- kind: "nested" as const,
81
- parentBlockId: 20,
82
- parentBlockIndex: 0,
83
- parentBlockType: "section" as const,
84
- columnIndex: 0,
85
- blockIndex: 0,
86
- blockType: "heading",
87
- },
88
- };
89
-
90
- expect(findDraftBlock(snapshot, request)).toMatchObject({
91
- block_type: "heading",
92
- content: { text_content: "Old heading", level: 2 },
93
- });
94
-
95
- const next = updateDraftBlockContent(snapshot, request, {
96
- text_content: "New heading",
97
- level: 2,
98
- });
99
-
100
- expect((next.blocks[0].content as any).column_blocks[0][0].content).toEqual({
101
- text_content: "New heading",
102
- level: 2,
103
- });
104
- });
105
- });
1
+ import { describe, expect, it, vi } from "vitest";
2
+
3
+ vi.mock("server-only", () => ({}));
4
+ vi.mock("@nextblock-cms/db/server", () => ({
5
+ createClient: vi.fn(),
6
+ getServiceRoleSupabaseClient: vi.fn(),
7
+ getSsgSupabaseClient: vi.fn(),
8
+ }));
9
+
10
+ import {
11
+ findDraftBlock,
12
+ updateDraftBlockContent,
13
+ } from "./draft-content";
14
+ import type { ContentDraftSnapshot } from "./types";
15
+
16
+ describe("draft content merge helpers", () => {
17
+ it("updates top-level draft block content without mutating the original snapshot", () => {
18
+ const snapshot: ContentDraftSnapshot = {
19
+ meta: {},
20
+ blocks: [
21
+ {
22
+ id: 10,
23
+ page_id: 1,
24
+ post_id: null,
25
+ language_id: 1,
26
+ block_type: "text",
27
+ content: { html_content: "<p>Old</p>" },
28
+ order: 0,
29
+ },
30
+ ],
31
+ };
32
+
33
+ const next = updateDraftBlockContent(
34
+ snapshot,
35
+ {
36
+ parentType: "page",
37
+ parentId: 1,
38
+ target: {
39
+ kind: "top-level",
40
+ blockId: 10,
41
+ blockIndex: 0,
42
+ blockType: "text",
43
+ },
44
+ },
45
+ { html_content: "<p>New</p>" }
46
+ );
47
+
48
+ expect(snapshot.blocks[0].content).toEqual({ html_content: "<p>Old</p>" });
49
+ expect(next.blocks[0].content).toEqual({ html_content: "<p>New</p>" });
50
+ });
51
+
52
+ it("finds and updates nested draft block content", () => {
53
+ const snapshot: ContentDraftSnapshot = {
54
+ meta: {},
55
+ blocks: [
56
+ {
57
+ id: 20,
58
+ page_id: 1,
59
+ post_id: null,
60
+ language_id: 1,
61
+ block_type: "section",
62
+ content: {
63
+ column_blocks: [
64
+ [
65
+ {
66
+ block_type: "heading",
67
+ content: { text_content: "Old heading", level: 2 },
68
+ },
69
+ ],
70
+ ],
71
+ },
72
+ order: 0,
73
+ },
74
+ ],
75
+ };
76
+ const request = {
77
+ parentType: "page" as const,
78
+ parentId: 1,
79
+ target: {
80
+ kind: "nested" as const,
81
+ parentBlockId: 20,
82
+ parentBlockIndex: 0,
83
+ parentBlockType: "section" as const,
84
+ columnIndex: 0,
85
+ blockIndex: 0,
86
+ blockType: "heading",
87
+ },
88
+ };
89
+
90
+ expect(findDraftBlock(snapshot, request)).toMatchObject({
91
+ block_type: "heading",
92
+ content: { text_content: "Old heading", level: 2 },
93
+ });
94
+
95
+ const next = updateDraftBlockContent(snapshot, request, {
96
+ text_content: "New heading",
97
+ level: 2,
98
+ });
99
+
100
+ expect((next.blocks[0].content as any).column_blocks[0][0].content).toEqual({
101
+ text_content: "New heading",
102
+ level: 2,
103
+ });
104
+ });
105
+ });
@@ -1,42 +1,42 @@
1
- import { describe, expect, it } from "vitest";
2
- import {
3
- normalizeDraftRedirectPath,
4
- resolveDraftPathTarget,
5
- } from "./draft-route";
6
-
7
- describe("draft-route helpers", () => {
8
- it("normalizes relative draft redirect paths", () => {
9
- expect(normalizeDraftRedirectPath("about")).toBe("/about");
10
- expect(normalizeDraftRedirectPath("/article/post-one")).toBe("/article/post-one");
11
- expect(normalizeDraftRedirectPath("/product/sku-one")).toBe("/product/sku-one");
12
- expect(normalizeDraftRedirectPath("//evil.example/path")).toBeNull();
13
- expect(normalizeDraftRedirectPath("https://evil.example/path")).toBeNull();
14
- expect(normalizeDraftRedirectPath("/admin\\settings")).toBeNull();
15
- });
16
-
17
- it("resolves page and post targets without allowing nested open-ended paths", () => {
18
- expect(resolveDraftPathTarget("/")).toEqual({
19
- parentType: "page",
20
- slug: "home",
21
- path: "/",
22
- });
23
- expect(resolveDraftPathTarget("/about")).toEqual({
24
- parentType: "page",
25
- slug: "about",
26
- path: "/about",
27
- });
28
- expect(resolveDraftPathTarget("/article/hello-world")).toEqual({
29
- parentType: "post",
30
- slug: "hello-world",
31
- path: "/article/hello-world",
32
- });
33
- expect(resolveDraftPathTarget("/product/commerce-license")).toEqual({
34
- parentType: "product",
35
- slug: "commerce-license",
36
- path: "/product/commerce-license",
37
- });
38
- expect(resolveDraftPathTarget("/nested/page")).toBeNull();
39
- expect(resolveDraftPathTarget("/article/nested/post")).toBeNull();
40
- expect(resolveDraftPathTarget("/product/nested/product")).toBeNull();
41
- });
42
- });
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ normalizeDraftRedirectPath,
4
+ resolveDraftPathTarget,
5
+ } from "./draft-route";
6
+
7
+ describe("draft-route helpers", () => {
8
+ it("normalizes relative draft redirect paths", () => {
9
+ expect(normalizeDraftRedirectPath("about")).toBe("/about");
10
+ expect(normalizeDraftRedirectPath("/article/post-one")).toBe("/article/post-one");
11
+ expect(normalizeDraftRedirectPath("/product/sku-one")).toBe("/product/sku-one");
12
+ expect(normalizeDraftRedirectPath("//evil.example/path")).toBeNull();
13
+ expect(normalizeDraftRedirectPath("https://evil.example/path")).toBeNull();
14
+ expect(normalizeDraftRedirectPath("/admin\\settings")).toBeNull();
15
+ });
16
+
17
+ it("resolves page and post targets without allowing nested open-ended paths", () => {
18
+ expect(resolveDraftPathTarget("/")).toEqual({
19
+ parentType: "page",
20
+ slug: "home",
21
+ path: "/",
22
+ });
23
+ expect(resolveDraftPathTarget("/about")).toEqual({
24
+ parentType: "page",
25
+ slug: "about",
26
+ path: "/about",
27
+ });
28
+ expect(resolveDraftPathTarget("/article/hello-world")).toEqual({
29
+ parentType: "post",
30
+ slug: "hello-world",
31
+ path: "/article/hello-world",
32
+ });
33
+ expect(resolveDraftPathTarget("/product/commerce-license")).toEqual({
34
+ parentType: "product",
35
+ slug: "commerce-license",
36
+ path: "/product/commerce-license",
37
+ });
38
+ expect(resolveDraftPathTarget("/nested/page")).toBeNull();
39
+ expect(resolveDraftPathTarget("/article/nested/post")).toBeNull();
40
+ expect(resolveDraftPathTarget("/product/nested/product")).toBeNull();
41
+ });
42
+ });
@@ -1,143 +1,143 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
-
3
- vi.mock("server-only", () => ({}));
4
-
5
- import { buildVisualEditAttributes } from "./edit-info";
6
-
7
- describe("buildVisualEditAttributes", () => {
8
- beforeEach(() => {
9
- delete process.env.NEXTBLOCK_VERCEL_PROJECT_ID;
10
- delete process.env.NEXTBLOCK_VERCEL_WORKSPACE_ID;
11
- delete process.env.NEXT_PUBLIC_URL;
12
- delete process.env.TARGET_URL;
13
- });
14
-
15
- it("returns no attributes when visual editing is disabled", () => {
16
- expect(
17
- buildVisualEditAttributes(
18
- {
19
- enabled: false,
20
- documentType: "page",
21
- documentId: 12,
22
- slug: "about",
23
- languageId: 1,
24
- },
25
- {
26
- kind: "top-level",
27
- blockId: 99,
28
- blockIndex: 0,
29
- blockType: "text",
30
- }
31
- )
32
- ).toBeUndefined();
33
- });
34
-
35
- it("builds a stringified Vercel edit-info payload with document and block metadata", () => {
36
- process.env.NEXTBLOCK_VERCEL_PROJECT_ID = "prj_123";
37
- process.env.NEXTBLOCK_VERCEL_WORKSPACE_ID = "team_123";
38
-
39
- const attrs = buildVisualEditAttributes(
40
- {
41
- enabled: true,
42
- documentType: "post",
43
- documentId: 42,
44
- slug: "hello-world",
45
- languageId: 1,
46
- draftId: 7,
47
- },
48
- {
49
- kind: "top-level",
50
- blockId: 99,
51
- blockIndex: 0,
52
- blockType: "heading",
53
- }
54
- );
55
-
56
- expect(attrs).toBeDefined();
57
- const payload = JSON.parse(attrs?.["data-vercel-edit-info"] ?? "{}");
58
- const target = JSON.parse(attrs?.["data-vercel-edit-target"] ?? "{}");
59
-
60
- expect(payload).toMatchObject({
61
- origin: "localhost",
62
- projectId: "prj_123",
63
- workspaceId: "team_123",
64
- editUrl: "http://localhost:3000/cms/posts/42/edit",
65
- data: {
66
- parentType: "post",
67
- parentId: 42,
68
- slug: "hello-world",
69
- languageId: 1,
70
- draftId: 7,
71
- },
72
- });
73
- expect(target).toEqual({
74
- kind: "top-level",
75
- blockId: 99,
76
- blockIndex: 0,
77
- blockType: "heading",
78
- });
79
- });
80
-
81
- it("dynamically overrides origin with NEXT_PUBLIC_URL and TARGET_URL", () => {
82
- process.env.NEXT_PUBLIC_URL = "https://custom-domain.com/";
83
- const attrs = buildVisualEditAttributes(
84
- {
85
- enabled: true,
86
- documentType: "page",
87
- documentId: 10,
88
- slug: "home",
89
- languageId: 1,
90
- },
91
- {
92
- kind: "top-level",
93
- blockId: 1,
94
- blockIndex: 0,
95
- blockType: "text",
96
- }
97
- );
98
- const payload = JSON.parse(attrs?.["data-vercel-edit-info"] ?? "{}");
99
- expect(payload.origin).toBe("custom-domain.com");
100
-
101
- // Test TARGET_URL fallback if NEXT_PUBLIC_URL is empty
102
- delete process.env.NEXT_PUBLIC_URL;
103
- process.env.TARGET_URL = "https://another-domain.xyz/subpath/";
104
- const attrsTarget = buildVisualEditAttributes(
105
- {
106
- enabled: true,
107
- documentType: "page",
108
- documentId: 10,
109
- slug: "home",
110
- languageId: 1,
111
- },
112
- {
113
- kind: "top-level",
114
- blockId: 1,
115
- blockIndex: 0,
116
- blockType: "text",
117
- }
118
- );
119
- const payloadTarget = JSON.parse(attrsTarget?.["data-vercel-edit-info"] ?? "{}");
120
- expect(payloadTarget.origin).toBe("another-domain.xyz");
121
- });
122
-
123
- it("uses pageOrigin from context if provided", () => {
124
- const attrs = buildVisualEditAttributes(
125
- {
126
- enabled: true,
127
- documentType: "page",
128
- documentId: 10,
129
- slug: "home",
130
- languageId: 1,
131
- pageOrigin: "https://my-custom-origin.dev/",
132
- },
133
- {
134
- kind: "top-level",
135
- blockId: 1,
136
- blockIndex: 0,
137
- blockType: "text",
138
- }
139
- );
140
- const payload = JSON.parse(attrs?.["data-vercel-edit-info"] ?? "{}");
141
- expect(payload.origin).toBe("my-custom-origin.dev");
142
- });
143
- });
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+
3
+ vi.mock("server-only", () => ({}));
4
+
5
+ import { buildVisualEditAttributes } from "./edit-info";
6
+
7
+ describe("buildVisualEditAttributes", () => {
8
+ beforeEach(() => {
9
+ delete process.env.NEXTBLOCK_VERCEL_PROJECT_ID;
10
+ delete process.env.NEXTBLOCK_VERCEL_WORKSPACE_ID;
11
+ delete process.env.NEXT_PUBLIC_URL;
12
+ delete process.env.TARGET_URL;
13
+ });
14
+
15
+ it("returns no attributes when visual editing is disabled", () => {
16
+ expect(
17
+ buildVisualEditAttributes(
18
+ {
19
+ enabled: false,
20
+ documentType: "page",
21
+ documentId: 12,
22
+ slug: "about",
23
+ languageId: 1,
24
+ },
25
+ {
26
+ kind: "top-level",
27
+ blockId: 99,
28
+ blockIndex: 0,
29
+ blockType: "text",
30
+ }
31
+ )
32
+ ).toBeUndefined();
33
+ });
34
+
35
+ it("builds a stringified Vercel edit-info payload with document and block metadata", () => {
36
+ process.env.NEXTBLOCK_VERCEL_PROJECT_ID = "prj_123";
37
+ process.env.NEXTBLOCK_VERCEL_WORKSPACE_ID = "team_123";
38
+
39
+ const attrs = buildVisualEditAttributes(
40
+ {
41
+ enabled: true,
42
+ documentType: "post",
43
+ documentId: 42,
44
+ slug: "hello-world",
45
+ languageId: 1,
46
+ draftId: 7,
47
+ },
48
+ {
49
+ kind: "top-level",
50
+ blockId: 99,
51
+ blockIndex: 0,
52
+ blockType: "heading",
53
+ }
54
+ );
55
+
56
+ expect(attrs).toBeDefined();
57
+ const payload = JSON.parse(attrs?.["data-vercel-edit-info"] ?? "{}");
58
+ const target = JSON.parse(attrs?.["data-vercel-edit-target"] ?? "{}");
59
+
60
+ expect(payload).toMatchObject({
61
+ origin: "localhost",
62
+ projectId: "prj_123",
63
+ workspaceId: "team_123",
64
+ editUrl: "http://localhost:3000/cms/posts/42/edit",
65
+ data: {
66
+ parentType: "post",
67
+ parentId: 42,
68
+ slug: "hello-world",
69
+ languageId: 1,
70
+ draftId: 7,
71
+ },
72
+ });
73
+ expect(target).toEqual({
74
+ kind: "top-level",
75
+ blockId: 99,
76
+ blockIndex: 0,
77
+ blockType: "heading",
78
+ });
79
+ });
80
+
81
+ it("dynamically overrides origin with NEXT_PUBLIC_URL and TARGET_URL", () => {
82
+ process.env.NEXT_PUBLIC_URL = "https://custom-domain.com/";
83
+ const attrs = buildVisualEditAttributes(
84
+ {
85
+ enabled: true,
86
+ documentType: "page",
87
+ documentId: 10,
88
+ slug: "home",
89
+ languageId: 1,
90
+ },
91
+ {
92
+ kind: "top-level",
93
+ blockId: 1,
94
+ blockIndex: 0,
95
+ blockType: "text",
96
+ }
97
+ );
98
+ const payload = JSON.parse(attrs?.["data-vercel-edit-info"] ?? "{}");
99
+ expect(payload.origin).toBe("custom-domain.com");
100
+
101
+ // Test TARGET_URL fallback if NEXT_PUBLIC_URL is empty
102
+ delete process.env.NEXT_PUBLIC_URL;
103
+ process.env.TARGET_URL = "https://another-domain.xyz/subpath/";
104
+ const attrsTarget = buildVisualEditAttributes(
105
+ {
106
+ enabled: true,
107
+ documentType: "page",
108
+ documentId: 10,
109
+ slug: "home",
110
+ languageId: 1,
111
+ },
112
+ {
113
+ kind: "top-level",
114
+ blockId: 1,
115
+ blockIndex: 0,
116
+ blockType: "text",
117
+ }
118
+ );
119
+ const payloadTarget = JSON.parse(attrsTarget?.["data-vercel-edit-info"] ?? "{}");
120
+ expect(payloadTarget.origin).toBe("another-domain.xyz");
121
+ });
122
+
123
+ it("uses pageOrigin from context if provided", () => {
124
+ const attrs = buildVisualEditAttributes(
125
+ {
126
+ enabled: true,
127
+ documentType: "page",
128
+ documentId: 10,
129
+ slug: "home",
130
+ languageId: 1,
131
+ pageOrigin: "https://my-custom-origin.dev/",
132
+ },
133
+ {
134
+ kind: "top-level",
135
+ blockId: 1,
136
+ blockIndex: 0,
137
+ blockType: "text",
138
+ }
139
+ );
140
+ const payload = JSON.parse(attrs?.["data-vercel-edit-info"] ?? "{}");
141
+ expect(payload.origin).toBe("my-custom-origin.dev");
142
+ });
143
+ });