@nextsparkjs/core 0.1.0-beta.187 → 0.1.0-beta.189

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 (414) hide show
  1. package/dist/components/api/keys/ApiKeyDisplay.js +19 -19
  2. package/dist/components/api/keys/CreateApiKeyDialog.js +21 -21
  3. package/dist/components/app/layouts/PublicNavbar.d.ts.map +1 -1
  4. package/dist/components/app/layouts/PublicNavbar.js +34 -16
  5. package/dist/components/app/misc/ThemeToggle.d.ts.map +1 -1
  6. package/dist/components/app/misc/ThemeToggle.js +3 -4
  7. package/dist/components/auth/SessionCookieRefresher.d.ts +5 -0
  8. package/dist/components/auth/SessionCookieRefresher.d.ts.map +1 -1
  9. package/dist/components/auth/SessionCookieRefresher.js +7 -1
  10. package/dist/components/auth/forms/LoginForm.d.ts.map +1 -1
  11. package/dist/components/auth/forms/LoginForm.js +49 -12
  12. package/dist/components/auth/forms/SignupForm.d.ts.map +1 -1
  13. package/dist/components/auth/forms/SignupForm.js +4 -3
  14. package/dist/components/dashboard/block-editor/block-settings-panel.js +1 -1
  15. package/dist/components/dashboard/block-editor/floating-block-toolbar.js +1 -1
  16. package/dist/components/dashboard/block-editor/sortable-block.js +1 -1
  17. package/dist/components/dashboard/layouts/Sidebar.d.ts.map +1 -1
  18. package/dist/components/dashboard/layouts/Sidebar.js +8 -6
  19. package/dist/components/dashboard/layouts/TopNavbar.d.ts.map +1 -1
  20. package/dist/components/dashboard/layouts/TopNavbar.js +51 -54
  21. package/dist/components/dashboard/misc/NotificationsDropdown.d.ts.map +1 -1
  22. package/dist/components/dashboard/misc/NotificationsDropdown.js +17 -15
  23. package/dist/components/dashboard/misc/SearchDropdown.d.ts.map +1 -1
  24. package/dist/components/dashboard/misc/SearchDropdown.js +19 -22
  25. package/dist/components/dashboard/mobile/MobileBottomNav.js +1 -1
  26. package/dist/components/dashboard/mobile/MobileTopBar.d.ts +10 -1
  27. package/dist/components/dashboard/mobile/MobileTopBar.d.ts.map +1 -1
  28. package/dist/components/dashboard/mobile/MobileTopBar.js +4 -3
  29. package/dist/components/dashboard/navigation/DynamicNavigation.js +5 -5
  30. package/dist/components/devtools/BlockDetailViewer.js +1 -1
  31. package/dist/components/devtools/BlocksViewer.js +1 -1
  32. package/dist/components/entities/EntityChildManager.js +5 -5
  33. package/dist/components/entities/EntityDetailPage.d.ts.map +1 -1
  34. package/dist/components/entities/EntityDetailPage.js +16 -15
  35. package/dist/components/entities/EntityFieldRenderer.d.ts.map +1 -1
  36. package/dist/components/entities/EntityFieldRenderer.js +12 -7
  37. package/dist/components/entities/EntityPageHeader.js +5 -5
  38. package/dist/components/entities/ErrorBoundary.d.ts.map +1 -1
  39. package/dist/components/entities/ErrorBoundary.js +20 -21
  40. package/dist/components/entities/wrappers/EntityDetailWrapper.js +5 -5
  41. package/dist/components/entities/wrappers/EntityFormWrapper.js +2 -2
  42. package/dist/components/entities/wrappers/EntityListWrapper.js +3 -3
  43. package/dist/components/permissions/NoPermission.js +10 -10
  44. package/dist/components/public/pageBuilder/PageRenderer.d.ts.map +1 -1
  45. package/dist/components/public/pageBuilder/PageRenderer.js +2 -1
  46. package/dist/components/settings/SettingsPageSkeleton.js +4 -4
  47. package/dist/components/settings/layouts/SettingsSidebar.js +6 -6
  48. package/dist/components/shared/MultiSelectFilter.d.ts +3 -1
  49. package/dist/components/shared/MultiSelectFilter.d.ts.map +1 -1
  50. package/dist/components/shared/MultiSelectFilter.js +7 -3
  51. package/dist/components/shared/SearchInput.d.ts +2 -0
  52. package/dist/components/shared/SearchInput.d.ts.map +1 -1
  53. package/dist/components/shared/SearchInput.js +64 -6
  54. package/dist/components/ui/address-input.js +18 -18
  55. package/dist/components/ui/audio-upload.js +9 -9
  56. package/dist/components/ui/combobox.d.ts.map +1 -1
  57. package/dist/components/ui/combobox.js +4 -4
  58. package/dist/components/ui/country-select.d.ts.map +1 -1
  59. package/dist/components/ui/country-select.js +3 -3
  60. package/dist/components/ui/currency-select.d.ts.map +1 -1
  61. package/dist/components/ui/currency-select.js +3 -3
  62. package/dist/components/ui/dynamic-icon.d.ts.map +1 -1
  63. package/dist/components/ui/dynamic-icon.js +3 -2
  64. package/dist/components/ui/file-upload.js +7 -7
  65. package/dist/components/ui/image-upload.js +8 -8
  66. package/dist/components/ui/multi-select.d.ts +2 -1
  67. package/dist/components/ui/multi-select.d.ts.map +1 -1
  68. package/dist/components/ui/multi-select.js +4 -3
  69. package/dist/components/ui/phone-input.d.ts.map +1 -1
  70. package/dist/components/ui/phone-input.js +3 -3
  71. package/dist/components/ui/relation-display.js +1 -1
  72. package/dist/components/ui/simple-relation-select.d.ts +2 -1
  73. package/dist/components/ui/simple-relation-select.d.ts.map +1 -1
  74. package/dist/components/ui/simple-relation-select.js +7 -6
  75. package/dist/components/ui/tags-input.d.ts.map +1 -1
  76. package/dist/components/ui/tags-input.js +1 -1
  77. package/dist/components/ui/timezone-select.d.ts.map +1 -1
  78. package/dist/components/ui/timezone-select.js +3 -3
  79. package/dist/components/ui/user-display.js +4 -4
  80. package/dist/components/ui/user-select.d.ts.map +1 -1
  81. package/dist/components/ui/user-select.js +9 -9
  82. package/dist/components/ui/video-upload.js +9 -9
  83. package/dist/contexts/SubscriptionContext.d.ts.map +1 -1
  84. package/dist/contexts/SubscriptionContext.js +5 -1
  85. package/dist/contexts/TeamContext.d.ts.map +1 -1
  86. package/dist/contexts/TeamContext.js +83 -26
  87. package/dist/emails/otp-verification.d.ts.map +1 -1
  88. package/dist/emails/otp-verification.js +3 -2
  89. package/dist/hooks/useAccountPreferencesSync.d.ts +13 -0
  90. package/dist/hooks/useAccountPreferencesSync.d.ts.map +1 -0
  91. package/dist/hooks/useAccountPreferencesSync.js +95 -0
  92. package/dist/hooks/useAuth.d.ts +202 -15
  93. package/dist/hooks/useAuth.d.ts.map +1 -1
  94. package/dist/hooks/useAuth.js +49 -13
  95. package/dist/hooks/useEntityConfig.js +2 -2
  96. package/dist/hooks/useEntitySearch.js +10 -10
  97. package/dist/hooks/useNotifications.js +12 -12
  98. package/dist/hooks/usePrefetch.d.ts.map +1 -1
  99. package/dist/hooks/usePrefetch.js +6 -2
  100. package/dist/hooks/useSearch.js +15 -15
  101. package/dist/hooks/useSessionCookieRefresh.d.ts +18 -1
  102. package/dist/hooks/useSessionCookieRefresh.d.ts.map +1 -1
  103. package/dist/hooks/useSessionCookieRefresh.js +17 -4
  104. package/dist/hooks/useSessionHint.d.ts +12 -0
  105. package/dist/hooks/useSessionHint.d.ts.map +1 -0
  106. package/dist/hooks/useSessionHint.js +15 -0
  107. package/dist/hooks/useTranslationPreload.js +6 -6
  108. package/dist/i18n.d.ts +13 -0
  109. package/dist/i18n.d.ts.map +1 -1
  110. package/dist/i18n.js +2 -27
  111. package/dist/lib/actions/entity.actions.d.ts.map +1 -1
  112. package/dist/lib/actions/entity.actions.js +3 -2
  113. package/dist/lib/api/auth/dual-auth.d.ts +4 -1
  114. package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
  115. package/dist/lib/api/auth/dual-auth.js +5 -16
  116. package/dist/lib/api/entities.js +33 -33
  117. package/dist/lib/api/keys.d.ts +27 -27
  118. package/dist/lib/api/keys.js +27 -27
  119. package/dist/lib/auth/callback-url.d.ts +11 -0
  120. package/dist/lib/auth/callback-url.d.ts.map +1 -0
  121. package/dist/lib/auth/callback-url.js +14 -0
  122. package/dist/lib/auth/otp-config.d.ts +55 -0
  123. package/dist/lib/auth/otp-config.d.ts.map +1 -0
  124. package/dist/lib/auth/otp-config.js +48 -0
  125. package/dist/lib/auth/registration-helpers.d.ts.map +1 -1
  126. package/dist/lib/auth/registration-helpers.js +3 -1
  127. package/dist/lib/auth/session-hint.d.ts +31 -0
  128. package/dist/lib/auth/session-hint.d.ts.map +1 -0
  129. package/dist/lib/auth/session-hint.js +37 -0
  130. package/dist/lib/auth.d.ts.map +1 -1
  131. package/dist/lib/auth.js +6 -4
  132. package/dist/lib/blocks/loader.d.ts +3 -11
  133. package/dist/lib/blocks/loader.d.ts.map +1 -1
  134. package/dist/lib/blocks/loader.js +1 -9
  135. package/dist/lib/blocks/loader.server.d.ts +18 -0
  136. package/dist/lib/blocks/loader.server.d.ts.map +1 -0
  137. package/dist/lib/blocks/loader.server.js +11 -0
  138. package/dist/lib/blocks/sanitize-html.d.ts +31 -0
  139. package/dist/lib/blocks/sanitize-html.d.ts.map +1 -0
  140. package/dist/lib/blocks/sanitize-html.js +60 -0
  141. package/dist/lib/config/config-sync.d.ts +1 -0
  142. package/dist/lib/config/config-sync.d.ts.map +1 -1
  143. package/dist/lib/config/config-sync.js +4 -1
  144. package/dist/lib/config/types.d.ts +48 -0
  145. package/dist/lib/config/types.d.ts.map +1 -1
  146. package/dist/lib/email/types.d.ts +6 -0
  147. package/dist/lib/email/types.d.ts.map +1 -1
  148. package/dist/lib/entities/serialization.d.ts.map +1 -1
  149. package/dist/lib/entities/serialization.js +3 -6
  150. package/dist/lib/icons.d.ts +12 -0
  151. package/dist/lib/icons.d.ts.map +1 -0
  152. package/dist/lib/icons.js +30 -0
  153. package/dist/lib/locale.d.ts +25 -1
  154. package/dist/lib/locale.d.ts.map +1 -1
  155. package/dist/lib/locale.js +49 -35
  156. package/dist/lib/mcp/engine.js +1 -1
  157. package/dist/lib/mcp/errors.js +17 -17
  158. package/dist/lib/mcp/schema-builder.js +16 -16
  159. package/dist/lib/mcp/tool-generator.js +18 -18
  160. package/dist/lib/search-highlighting.d.ts +7 -0
  161. package/dist/lib/search-highlighting.d.ts.map +1 -1
  162. package/dist/lib/search-highlighting.js +6 -2
  163. package/dist/lib/selectors/core-selectors.d.ts +2 -0
  164. package/dist/lib/selectors/core-selectors.d.ts.map +1 -1
  165. package/dist/lib/selectors/domains/auth.selectors.d.ts +1 -0
  166. package/dist/lib/selectors/domains/auth.selectors.d.ts.map +1 -1
  167. package/dist/lib/selectors/domains/auth.selectors.js +1 -0
  168. package/dist/lib/selectors/domains/entities.selectors.d.ts +2 -0
  169. package/dist/lib/selectors/domains/entities.selectors.d.ts.map +1 -1
  170. package/dist/lib/selectors/domains/entities.selectors.js +2 -0
  171. package/dist/lib/selectors/selectors.d.ts +4 -0
  172. package/dist/lib/selectors/selectors.d.ts.map +1 -1
  173. package/dist/lib/services/block.service.js +1 -1
  174. package/dist/lib/teams/active-team-cookie.d.ts +24 -0
  175. package/dist/lib/teams/active-team-cookie.d.ts.map +1 -0
  176. package/dist/lib/teams/active-team-cookie.js +17 -0
  177. package/dist/lib/teams/dashboard-team.d.ts +20 -0
  178. package/dist/lib/teams/dashboard-team.d.ts.map +1 -0
  179. package/dist/lib/teams/dashboard-team.js +38 -0
  180. package/dist/lib/teams/schema.d.ts.map +1 -1
  181. package/dist/lib/teams/schema.js +1 -1
  182. package/dist/lib/template-resolver.d.ts.map +1 -1
  183. package/dist/lib/template-resolver.js +0 -1
  184. package/dist/lib/theme/get-default-theme-mode.d.ts +7 -4
  185. package/dist/lib/theme/get-default-theme-mode.d.ts.map +1 -1
  186. package/dist/lib/theme/get-default-theme-mode.js +2 -44
  187. package/dist/messages/de/auth.json +4 -2
  188. package/dist/messages/de/common.json +83 -3
  189. package/dist/messages/de/email.json +1 -1
  190. package/dist/messages/de/index.d.ts +87 -0
  191. package/dist/messages/de/index.d.ts.map +1 -1
  192. package/dist/messages/de/settings.json +5 -0
  193. package/dist/messages/en/auth.json +4 -2
  194. package/dist/messages/en/common.json +83 -3
  195. package/dist/messages/en/email.json +1 -1
  196. package/dist/messages/en/index.d.ts +86 -0
  197. package/dist/messages/en/index.d.ts.map +1 -1
  198. package/dist/messages/en/settings.json +4 -0
  199. package/dist/messages/es/auth.json +4 -2
  200. package/dist/messages/es/common.json +83 -3
  201. package/dist/messages/es/email.json +1 -1
  202. package/dist/messages/es/index.d.ts +86 -0
  203. package/dist/messages/es/index.d.ts.map +1 -1
  204. package/dist/messages/es/settings.json +4 -0
  205. package/dist/messages/fr/auth.json +4 -2
  206. package/dist/messages/fr/common.json +83 -3
  207. package/dist/messages/fr/email.json +1 -1
  208. package/dist/messages/fr/index.d.ts +87 -0
  209. package/dist/messages/fr/index.d.ts.map +1 -1
  210. package/dist/messages/fr/settings.json +5 -0
  211. package/dist/messages/it/auth.json +4 -2
  212. package/dist/messages/it/common.json +83 -3
  213. package/dist/messages/it/email.json +1 -1
  214. package/dist/messages/it/index.d.ts +87 -0
  215. package/dist/messages/it/index.d.ts.map +1 -1
  216. package/dist/messages/it/settings.json +5 -0
  217. package/dist/messages/pt/auth.json +4 -2
  218. package/dist/messages/pt/common.json +83 -3
  219. package/dist/messages/pt/email.json +1 -1
  220. package/dist/messages/pt/index.d.ts +87 -0
  221. package/dist/messages/pt/index.d.ts.map +1 -1
  222. package/dist/messages/pt/settings.json +5 -0
  223. package/dist/nextspark-registries.d.ts +54 -0
  224. package/dist/providers/DashboardProviders.d.ts +13 -10
  225. package/dist/providers/DashboardProviders.d.ts.map +1 -1
  226. package/dist/providers/DashboardProviders.js +7 -1
  227. package/dist/templates/app/(auth)/forgot-password/page.tsx +2 -2
  228. package/dist/templates/app/(auth)/reset-password/page.tsx +6 -6
  229. package/dist/templates/app/403/page.tsx +6 -6
  230. package/dist/templates/app/api/v1/teams/switch/route.ts +24 -5
  231. package/dist/templates/app/dashboard/(main)/[entity]/layout.tsx +9 -7
  232. package/dist/templates/app/dashboard/(main)/layout.tsx +4 -3
  233. package/dist/templates/app/dashboard/settings/api-keys/page.tsx +36 -34
  234. package/dist/templates/app/dashboard/settings/profile/page.tsx +7 -6
  235. package/dist/templates/app/dashboard/settings/security/page.tsx +5 -4
  236. package/dist/templates/app/layout.ppr.tsx +8 -8
  237. package/dist/templates/app/layout.tsx +12 -17
  238. package/dist/templates/blocks/cta-section/component.tsx +5 -2
  239. package/dist/templates/blocks/cta-section/config.ts +3 -2
  240. package/dist/templates/blocks/cta-section/fields.ts +2 -2
  241. package/dist/templates/blocks/cta-section/schema.ts +2 -2
  242. package/dist/templates/blocks/features-grid/component.tsx +10 -2
  243. package/dist/templates/blocks/features-grid/config.ts +3 -2
  244. package/dist/templates/blocks/features-grid/fields.ts +2 -2
  245. package/dist/templates/blocks/features-grid/schema.ts +2 -2
  246. package/dist/templates/blocks/hero/component.tsx +6 -3
  247. package/dist/templates/blocks/hero/config.ts +3 -2
  248. package/dist/templates/blocks/hero/fields.ts +2 -2
  249. package/dist/templates/blocks/hero/schema.ts +2 -2
  250. package/dist/templates/blocks/testimonials/component.tsx +6 -3
  251. package/dist/templates/blocks/testimonials/config.ts +3 -2
  252. package/dist/templates/blocks/testimonials/fields.ts +2 -2
  253. package/dist/templates/blocks/testimonials/schema.ts +2 -2
  254. package/dist/templates/blocks/text-content/component.tsx +7 -3
  255. package/dist/templates/blocks/text-content/config.ts +3 -2
  256. package/dist/templates/blocks/text-content/fields.ts +2 -2
  257. package/dist/templates/blocks/text-content/schema.ts +2 -2
  258. package/dist/templates/contents/themes/starter/blocks/cta-section/component.tsx +102 -0
  259. package/dist/templates/contents/themes/starter/blocks/cta-section/config.ts +11 -0
  260. package/dist/templates/contents/themes/starter/blocks/cta-section/fields.ts +89 -0
  261. package/dist/templates/contents/themes/starter/blocks/cta-section/index.ts +6 -0
  262. package/dist/templates/contents/themes/starter/blocks/cta-section/schema.ts +32 -0
  263. package/dist/templates/contents/themes/starter/blocks/cta-section/thumbnail.png +0 -0
  264. package/dist/templates/contents/themes/starter/blocks/features-grid/component.tsx +119 -0
  265. package/dist/templates/contents/themes/starter/blocks/features-grid/config.ts +11 -0
  266. package/dist/templates/contents/themes/starter/blocks/features-grid/fields.ts +97 -0
  267. package/dist/templates/contents/themes/starter/blocks/features-grid/index.ts +6 -0
  268. package/dist/templates/contents/themes/starter/blocks/features-grid/schema.ts +40 -0
  269. package/dist/templates/contents/themes/starter/blocks/features-grid/thumbnail.png +0 -0
  270. package/dist/templates/contents/themes/starter/blocks/hero/component.tsx +105 -0
  271. package/dist/templates/contents/themes/starter/blocks/hero/config.ts +11 -0
  272. package/dist/templates/contents/themes/starter/blocks/hero/fields.ts +60 -0
  273. package/dist/templates/contents/themes/starter/blocks/hero/index.ts +6 -0
  274. package/dist/templates/contents/themes/starter/blocks/hero/schema.ts +33 -0
  275. package/dist/templates/contents/themes/starter/blocks/hero/thumbnail.png +0 -0
  276. package/dist/templates/contents/themes/starter/blocks/testimonials/component.tsx +116 -0
  277. package/dist/templates/contents/themes/starter/blocks/testimonials/config.ts +11 -0
  278. package/dist/templates/contents/themes/starter/blocks/testimonials/fields.ts +105 -0
  279. package/dist/templates/contents/themes/starter/blocks/testimonials/index.ts +6 -0
  280. package/dist/templates/contents/themes/starter/blocks/testimonials/schema.ts +41 -0
  281. package/dist/templates/contents/themes/starter/blocks/testimonials/thumbnail.png +0 -0
  282. package/dist/templates/contents/themes/starter/blocks/text-content/component.tsx +100 -0
  283. package/dist/templates/contents/themes/starter/blocks/text-content/config.ts +11 -0
  284. package/dist/templates/contents/themes/starter/blocks/text-content/fields.ts +88 -0
  285. package/dist/templates/contents/themes/starter/blocks/text-content/index.ts +6 -0
  286. package/dist/templates/contents/themes/starter/blocks/text-content/schema.ts +30 -0
  287. package/dist/templates/contents/themes/starter/blocks/text-content/thumbnail.png +0 -0
  288. package/dist/templates/contents/themes/starter/config/app.config.ts +8 -0
  289. package/dist/templates/contents/themes/starter/entities/tasks/tasks.config.ts +7 -0
  290. package/dist/templates/contents/themes/starter/tests/cypress/e2e/_utils/devtools/access.cy.ts +9 -1
  291. package/dist/templates/contents/themes/starter/tests/cypress/e2e/api/entities/tasks/TaskAPIController.js +2 -2
  292. package/dist/templates/contents/themes/starter/tests/cypress/e2e/uat/entities/tasks/tasks-crud.cy.ts +45 -38
  293. package/dist/templates/contents/themes/starter/tests/cypress/fixtures/entities.json +16 -0
  294. package/dist/templates/contents/themes/starter/tests/cypress/src/components/DevKeyringPOM.ts +3 -1
  295. package/dist/templates/contents/themes/starter/tests/cypress/src/core/BlockEditorBasePOM.ts +3 -3
  296. package/dist/templates/contents/themes/starter/tests/cypress/src/entities/PagesPOM.ts +137 -0
  297. package/dist/templates/contents/themes/starter/tests/cypress/src/entities/index.ts +2 -0
  298. package/dist/templates/contents/themes/starter/tests/cypress/src/index.ts +1 -0
  299. package/dist/templates/contents/themes/starter/tests/cypress/src/session-helpers.ts +42 -11
  300. package/dist/templates/contents/themes/starter/tests/cypress/support/commands.ts +29 -18
  301. package/dist/templates/features/blog/blocks/post-content/component.tsx +5 -1
  302. package/dist/templates/features/blog/blocks/post-content/schema.ts +1 -1
  303. package/dist/templates/features/pages/blocks/hero/component.tsx +3 -0
  304. package/dist/templates/features/pages/blocks/hero/schema.ts +1 -1
  305. package/dist/templates/proxy.ts +138 -53
  306. package/dist/types/blocks.d.ts.map +1 -1
  307. package/dist/types/blocks.js +1 -1
  308. package/dist/types/task.types.d.ts +1 -1
  309. package/dist/types/task.types.js +7 -7
  310. package/dist/utils/dev/TranslationDebugger.js +2 -2
  311. package/package.json +14 -5
  312. package/scripts/build/registry/__tests__/block-registry.test.mjs +73 -0
  313. package/scripts/build/registry/__tests__/icons-discovery.test.mjs +130 -0
  314. package/scripts/build/registry/__tests__/identifier.test.mjs +65 -0
  315. package/scripts/build/registry/__tests__/page-generator.test.mjs +430 -0
  316. package/scripts/build/registry/__tests__/scheduled-actions-registry.test.mjs +41 -0
  317. package/scripts/build/registry/__tests__/template-registry.test.mjs +89 -0
  318. package/scripts/build/registry/discovery/icons.mjs +275 -0
  319. package/scripts/build/registry/discovery/index.mjs +1 -0
  320. package/scripts/build/registry/generators/block-registry.mjs +203 -55
  321. package/scripts/build/registry/generators/feature-registry.mjs +9 -2
  322. package/scripts/build/registry/generators/icon-registry.mjs +56 -0
  323. package/scripts/build/registry/generators/identifier.mjs +28 -0
  324. package/scripts/build/registry/generators/index.mjs +1 -0
  325. package/scripts/build/registry/generators/plugin-registry.mjs +4 -3
  326. package/scripts/build/registry/generators/scheduled-actions-registry.mjs +4 -2
  327. package/scripts/build/registry/generators/template-registry.mjs +49 -41
  328. package/scripts/build/registry/generators/theme-registry.mjs +6 -7
  329. package/scripts/build/registry/post-build/page-generator.mjs +380 -59
  330. package/scripts/build/registry.mjs +9 -1
  331. package/templates/app/(auth)/forgot-password/page.tsx +2 -2
  332. package/templates/app/(auth)/reset-password/page.tsx +6 -6
  333. package/templates/app/403/page.tsx +6 -6
  334. package/templates/app/api/v1/teams/switch/route.ts +24 -5
  335. package/templates/app/dashboard/(main)/[entity]/layout.tsx +9 -7
  336. package/templates/app/dashboard/(main)/layout.tsx +4 -3
  337. package/templates/app/dashboard/settings/api-keys/page.tsx +36 -34
  338. package/templates/app/dashboard/settings/profile/page.tsx +7 -6
  339. package/templates/app/dashboard/settings/security/page.tsx +5 -4
  340. package/templates/app/layout.ppr.tsx +8 -8
  341. package/templates/app/layout.tsx +12 -17
  342. package/templates/blocks/cta-section/component.tsx +5 -2
  343. package/templates/blocks/cta-section/config.ts +3 -2
  344. package/templates/blocks/cta-section/fields.ts +2 -2
  345. package/templates/blocks/cta-section/schema.ts +2 -2
  346. package/templates/blocks/features-grid/component.tsx +10 -2
  347. package/templates/blocks/features-grid/config.ts +3 -2
  348. package/templates/blocks/features-grid/fields.ts +2 -2
  349. package/templates/blocks/features-grid/schema.ts +2 -2
  350. package/templates/blocks/hero/component.tsx +6 -3
  351. package/templates/blocks/hero/config.ts +3 -2
  352. package/templates/blocks/hero/fields.ts +2 -2
  353. package/templates/blocks/hero/schema.ts +2 -2
  354. package/templates/blocks/testimonials/component.tsx +6 -3
  355. package/templates/blocks/testimonials/config.ts +3 -2
  356. package/templates/blocks/testimonials/fields.ts +2 -2
  357. package/templates/blocks/testimonials/schema.ts +2 -2
  358. package/templates/blocks/text-content/component.tsx +7 -3
  359. package/templates/blocks/text-content/config.ts +3 -2
  360. package/templates/blocks/text-content/fields.ts +2 -2
  361. package/templates/blocks/text-content/schema.ts +2 -2
  362. package/templates/contents/themes/starter/blocks/cta-section/component.tsx +102 -0
  363. package/templates/contents/themes/starter/blocks/cta-section/config.ts +11 -0
  364. package/templates/contents/themes/starter/blocks/cta-section/fields.ts +89 -0
  365. package/templates/contents/themes/starter/blocks/cta-section/index.ts +6 -0
  366. package/templates/contents/themes/starter/blocks/cta-section/schema.ts +32 -0
  367. package/templates/contents/themes/starter/blocks/cta-section/thumbnail.png +0 -0
  368. package/templates/contents/themes/starter/blocks/features-grid/component.tsx +119 -0
  369. package/templates/contents/themes/starter/blocks/features-grid/config.ts +11 -0
  370. package/templates/contents/themes/starter/blocks/features-grid/fields.ts +97 -0
  371. package/templates/contents/themes/starter/blocks/features-grid/index.ts +6 -0
  372. package/templates/contents/themes/starter/blocks/features-grid/schema.ts +40 -0
  373. package/templates/contents/themes/starter/blocks/features-grid/thumbnail.png +0 -0
  374. package/templates/contents/themes/starter/blocks/hero/component.tsx +105 -0
  375. package/templates/contents/themes/starter/blocks/hero/config.ts +11 -0
  376. package/templates/contents/themes/starter/blocks/hero/fields.ts +60 -0
  377. package/templates/contents/themes/starter/blocks/hero/index.ts +6 -0
  378. package/templates/contents/themes/starter/blocks/hero/schema.ts +33 -0
  379. package/templates/contents/themes/starter/blocks/hero/thumbnail.png +0 -0
  380. package/templates/contents/themes/starter/blocks/testimonials/component.tsx +116 -0
  381. package/templates/contents/themes/starter/blocks/testimonials/config.ts +11 -0
  382. package/templates/contents/themes/starter/blocks/testimonials/fields.ts +105 -0
  383. package/templates/contents/themes/starter/blocks/testimonials/index.ts +6 -0
  384. package/templates/contents/themes/starter/blocks/testimonials/schema.ts +41 -0
  385. package/templates/contents/themes/starter/blocks/testimonials/thumbnail.png +0 -0
  386. package/templates/contents/themes/starter/blocks/text-content/component.tsx +100 -0
  387. package/templates/contents/themes/starter/blocks/text-content/config.ts +11 -0
  388. package/templates/contents/themes/starter/blocks/text-content/fields.ts +88 -0
  389. package/templates/contents/themes/starter/blocks/text-content/index.ts +6 -0
  390. package/templates/contents/themes/starter/blocks/text-content/schema.ts +30 -0
  391. package/templates/contents/themes/starter/blocks/text-content/thumbnail.png +0 -0
  392. package/templates/contents/themes/starter/config/app.config.ts +8 -0
  393. package/templates/contents/themes/starter/entities/tasks/tasks.config.ts +7 -0
  394. package/templates/contents/themes/starter/tests/cypress/e2e/_utils/devtools/access.cy.ts +9 -1
  395. package/templates/contents/themes/starter/tests/cypress/e2e/api/entities/tasks/TaskAPIController.js +2 -2
  396. package/templates/contents/themes/starter/tests/cypress/e2e/uat/entities/tasks/tasks-crud.cy.ts +45 -38
  397. package/templates/contents/themes/starter/tests/cypress/fixtures/entities.json +16 -0
  398. package/templates/contents/themes/starter/tests/cypress/src/components/DevKeyringPOM.ts +3 -1
  399. package/templates/contents/themes/starter/tests/cypress/src/core/BlockEditorBasePOM.ts +3 -3
  400. package/templates/contents/themes/starter/tests/cypress/src/entities/PagesPOM.ts +137 -0
  401. package/templates/contents/themes/starter/tests/cypress/src/entities/index.ts +2 -0
  402. package/templates/contents/themes/starter/tests/cypress/src/index.ts +1 -0
  403. package/templates/contents/themes/starter/tests/cypress/src/session-helpers.ts +42 -11
  404. package/templates/contents/themes/starter/tests/cypress/support/commands.ts +29 -18
  405. package/templates/features/blog/blocks/post-content/component.tsx +5 -1
  406. package/templates/features/blog/blocks/post-content/schema.ts +1 -1
  407. package/templates/features/pages/blocks/hero/component.tsx +3 -0
  408. package/templates/features/pages/blocks/hero/schema.ts +1 -1
  409. package/templates/proxy.ts +138 -53
  410. package/tests/jest/__mocks__/@nextsparkjs/registries/block-registry.client.ts +9 -0
  411. package/tests/jest/__mocks__/@nextsparkjs/registries/block-registry.lazy.ts +5 -0
  412. package/tests/jest/__mocks__/@nextsparkjs/registries/icon-registry.ts +42 -0
  413. package/tests/jest/__mocks__/next-intl-server.js +85 -5
  414. package/tests/jest/__mocks__/next-server.js +32 -0
@@ -6,6 +6,7 @@
6
6
  * @module core/scripts/build/registry/post-build/page-generator
7
7
  */
8
8
 
9
+ import { createRequire } from 'node:module'
9
10
  import { existsSync } from 'fs'
10
11
  import { readFile, writeFile, mkdir } from 'fs/promises'
11
12
  import { join, dirname } from 'path'
@@ -31,46 +32,351 @@ function toPascalCase(str) {
31
32
  }
32
33
 
33
34
  /**
34
- * Next.js route-level exports that only take effect when exported from the
35
- * route file itself. If a theme template defines any of these, the generated
36
- * route file MUST re-export them — otherwise Next.js silently ignores them
37
- * (e.g. generateMetadata never runs so the page has no <title>/<meta>,
38
- * generateStaticParams never prerenders so PPR pages stream the hero).
35
+ * Next.js route segment config keys (the `AppSegmentConfigSchema` keys from
36
+ * `next/dist/build/segment-config/app/app-segment-config`). Next.js only
37
+ * reads these when they're declared as a literal `export const` directly in
38
+ * the route file — a re-export doesn't work, so the generated route file
39
+ * must re-declare the value itself rather than forward it.
39
40
  */
40
- const ROUTE_LEVEL_EXPORTS = [
41
- 'generateMetadata',
42
- 'generateStaticParams',
43
- 'generateViewport',
44
- 'metadata',
45
- 'viewport',
41
+ export const SEGMENT_CONFIG_EXPORTS = [
46
42
  'revalidate',
47
- 'dynamic',
48
43
  'dynamicParams',
44
+ 'dynamic',
49
45
  'fetchCache',
50
- 'runtime',
51
46
  'preferredRegion',
47
+ 'experimental_ppr',
48
+ 'runtime',
52
49
  'maxDuration',
53
50
  ]
54
51
 
55
52
  /**
56
- * Detect which route-level exports a template file defines, so the generated
57
- * route file can re-export them.
53
+ * Next.js module-level route exports that Next.js is happy to pick up
54
+ * through a re-export (`export { x } from '...'`), unlike segment config.
58
55
  */
59
- async function detectRouteLevelExports(templatePath) {
60
- try {
61
- const baseTemplatePath = templatePath
62
- .replace('@/', rootDir + '/')
63
- .replace(/\.(tsx|ts)$/, '')
64
- const absoluteTemplatePath = existsSync(baseTemplatePath + '.tsx')
65
- ? baseTemplatePath + '.tsx'
66
- : baseTemplatePath + '.ts'
67
- const templateContent = await readFile(absoluteTemplatePath, 'utf8')
68
- return ROUTE_LEVEL_EXPORTS.filter(name =>
69
- new RegExp(`export\\s+(?:async\\s+)?(?:function|const|let|var)\\s+${name}\\b`).test(templateContent)
56
+ export const MODULE_LEVEL_EXPORTS = [
57
+ 'generateMetadata',
58
+ 'generateStaticParams',
59
+ 'generateViewport',
60
+ 'metadata',
61
+ 'viewport',
62
+ ]
63
+
64
+ /**
65
+ * Per-key validators for the segment config schema. A value that parses as a
66
+ * literal but doesn't match here would still make Next.js throw at build
67
+ * time (via `parseAppSegmentConfig`), just later and with less context about
68
+ * which theme template caused it.
69
+ */
70
+ const SEGMENT_CONFIG_VALIDATORS = {
71
+ revalidate: value => value === false || (typeof value === 'number' && Number.isInteger(value) && value >= 0),
72
+ dynamicParams: value => typeof value === 'boolean',
73
+ dynamic: value => ['auto', 'error', 'force-static', 'force-dynamic'].includes(value),
74
+ fetchCache: value =>
75
+ ['auto', 'default-cache', 'only-cache', 'force-cache', 'force-no-store', 'default-no-store', 'only-no-store'].includes(value),
76
+ preferredRegion: value => typeof value === 'string' || (Array.isArray(value) && value.every(item => typeof item === 'string')),
77
+ experimental_ppr: value => typeof value === 'boolean',
78
+ runtime: value => ['edge', 'nodejs'].includes(value),
79
+ maxDuration: value => typeof value === 'number' && Number.isInteger(value) && value >= 0,
80
+ }
81
+
82
+ // Loaded lazily (and only once) since not every consumer of this module needs
83
+ // to parse a template - npm-mode builds without a template needing this path
84
+ // shouldn't pay for loading a TypeScript compiler up front.
85
+ let typescriptModulePromise = null
86
+
87
+ /**
88
+ * The TypeScript compiler, resolved from core first and then from the project:
89
+ * it is a devDependency of core, so an installed core finds it through the
90
+ * project that builds it, which has it to compile its own `.tsx` files.
91
+ */
92
+ function loadTypeScript() {
93
+ if (!typescriptModulePromise) {
94
+ typescriptModulePromise = import('typescript')
95
+ .catch(() => createRequire(join(rootDir, 'package.json'))('typescript'))
96
+ .then(
97
+ module => module.default ?? module,
98
+ error => {
99
+ throw new Error(
100
+ 'Parsing route-level exports out of a theme template requires the "typescript" package, but it could not be loaded ' +
101
+ `from @nextsparkjs/core or from the project at ${rootDir}. Add "typescript" to the project's devDependencies. (${error.message})`
102
+ )
103
+ }
104
+ )
105
+ }
106
+ return typescriptModulePromise
107
+ }
108
+
109
+ let nextParseModulePromise = null
110
+
111
+ /**
112
+ * Next.js's own module parser (SWC), resolved from the project and then from
113
+ * core, or null when neither has Next.js.
114
+ */
115
+ function loadNextParseModule() {
116
+ if (!nextParseModulePromise) {
117
+ const path = 'next/dist/build/analysis/parse-module'
118
+ nextParseModulePromise = Promise.resolve()
119
+ .then(() => createRequire(join(rootDir, 'package.json'))(path))
120
+ .catch(() => createRequire(import.meta.url)(path))
121
+ .then(module => module.parseModule, () => null)
122
+ }
123
+ return nextParseModulePromise
124
+ }
125
+
126
+ /** Whether Next.js parses `source`; its parser answers null for a module it cannot. */
127
+ async function nextParses(filePath, source) {
128
+ const parseModule = await loadNextParseModule()
129
+ return Boolean(parseModule && (await parseModule(filePath, source)))
130
+ }
131
+
132
+ /**
133
+ * Unwrap the syntactic wrappers around a literal that don't change its
134
+ * runtime value: parentheses, `as const`, and `satisfies`.
135
+ */
136
+ function unwrapExpression(expression, ts) {
137
+ let current = expression
138
+ while (true) {
139
+ if (ts.isParenthesizedExpression(current)) {
140
+ current = current.expression
141
+ } else if (ts.isAsExpression(current) || ts.isSatisfiesExpression(current)) {
142
+ current = current.expression
143
+ } else {
144
+ return current
145
+ }
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Evaluate an expression node as a JSON-compatible literal. Returns
151
+ * `{ ok: false }` for anything that isn't a literal Next.js could read
152
+ * statically - an expression, an identifier reference, a call, etc.
153
+ */
154
+ function evaluateLiteral(expression, ts) {
155
+ const node = unwrapExpression(expression, ts)
156
+
157
+ if (node.kind === ts.SyntaxKind.TrueKeyword) return { ok: true, value: true }
158
+ if (node.kind === ts.SyntaxKind.FalseKeyword) return { ok: true, value: false }
159
+ if (ts.isNumericLiteral(node)) return { ok: true, value: Number(node.text.replace(/_/g, '')) }
160
+ if (ts.isStringLiteralLike(node)) return { ok: true, value: node.text }
161
+
162
+ if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.MinusToken) {
163
+ const operand = evaluateLiteral(node.operand, ts)
164
+ return operand.ok && typeof operand.value === 'number' ? { ok: true, value: -operand.value } : { ok: false }
165
+ }
166
+
167
+ if (ts.isArrayLiteralExpression(node)) {
168
+ const values = []
169
+ for (const element of node.elements) {
170
+ const item = evaluateLiteral(element, ts)
171
+ if (!item.ok) return { ok: false }
172
+ values.push(item.value)
173
+ }
174
+ return { ok: true, value: values }
175
+ }
176
+
177
+ return { ok: false }
178
+ }
179
+
180
+ function isExported(node, ts) {
181
+ return Boolean(node.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword))
182
+ }
183
+
184
+ /**
185
+ * Whether a module has a runtime default export: `export default <expr>`,
186
+ * `export default function`/`class`, or `default` named in an export clause
187
+ * (`export { Layout as default }`, `export { default } from './shell'`).
188
+ * Type-only exports give the route no component.
189
+ */
190
+ function declaresDefaultExport(sourceFile, ts) {
191
+ return sourceFile.statements.some(statement => {
192
+ if (ts.isExportAssignment(statement)) return !statement.isExportEquals
193
+ if (ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement)) {
194
+ return isExported(statement, ts) && Boolean(statement.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.DefaultKeyword))
195
+ }
196
+ if (ts.isExportDeclaration(statement) && !statement.isTypeOnly && statement.exportClause && ts.isNamedExports(statement.exportClause)) {
197
+ return statement.exportClause.elements.some(element => !element.isTypeOnly && element.name.text === 'default')
198
+ }
199
+ return false
200
+ })
201
+ }
202
+
203
+ /**
204
+ * Parse a theme template's source and split its route-level exports into:
205
+ * - `segmentConfig`: segment config keys that resolve to a literal value the
206
+ * generated route file can re-declare (`{ name: value }`).
207
+ * - `moduleExports`: exported names (functions, `metadata`, etc.) the
208
+ * generated route file can safely forward with `export { ... } from`.
209
+ * - `hasDefaultExport`: whether it exports a component as default, read from
210
+ * the syntax tree so a comment or string mentioning `export default` doesn't
211
+ * count and `export { Layout as default }` does.
212
+ *
213
+ * A segment config key that IS exported but not as a literal `export const`
214
+ * (an expression, an identifier, `let`/`var`, a re-export, or a value the
215
+ * Next.js schema rejects) throws instead of silently dropping it - Next.js
216
+ * would otherwise ignore the value and fall back to its default, and nothing
217
+ * in the build would say so beyond a console warning.
218
+ */
219
+ export async function extractRouteExports(source, filePath) {
220
+ const ts = await loadTypeScript()
221
+ // Parsed as its own kind of file: a TSX parse reads TypeScript-only syntax in a
222
+ // .ts template (`<T>(props) => ...`, `<Props>value`) as JSX and loses what follows
223
+ const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true, ts.getScriptKindFromFileName(filePath))
224
+
225
+ // The parser recovers from a syntax error by skipping code, exports included. A
226
+ // template Next.js cannot parse either is reported rather than read in part. One
227
+ // it can is read from what TypeScript recovered, with a warning: Next.js compiles
228
+ // templates with SWC, which may know syntax the installed TypeScript does not (a
229
+ // source phase import, for one).
230
+ const [parseError] = sourceFile.parseDiagnostics ?? []
231
+ if (parseError) {
232
+ const { line } = sourceFile.getLineAndCharacterOfPosition(parseError.start ?? 0)
233
+ const where = `${filePath}:${line + 1}`
234
+ const reason = ts.flattenDiagnosticMessageText(parseError.messageText, ' ')
235
+ if (!(await nextParses(filePath, source))) {
236
+ throw new Error(`${where}: the template does not parse (${reason}), so its route-level exports cannot be read.`)
237
+ }
238
+ console.warn(
239
+ `${where}: TypeScript cannot parse the template (${reason}) but Next.js can; its route-level exports are read from what TypeScript parsed.`
70
240
  )
241
+ }
242
+
243
+ const segmentConfig = {}
244
+ const moduleExports = []
245
+ const seenModuleExports = new Set()
246
+ const errors = []
247
+
248
+ const lineOf = node => sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1
249
+
250
+ const addModuleExport = name => {
251
+ if (!seenModuleExports.has(name)) {
252
+ seenModuleExports.add(name)
253
+ moduleExports.push(name)
254
+ }
255
+ }
256
+
257
+ const recordSegmentError = (name, node, reason) => {
258
+ errors.push(
259
+ `${filePath}:${lineOf(node)}: segment config export "${name}" ${reason}. ` +
260
+ `Next.js only reads segment config when it's a literal declared directly in the route file ` +
261
+ `(\`export const ${name} = ...\`) - anything else is silently ignored and the default is used instead. ` +
262
+ `Use a literal.`
263
+ )
264
+ }
265
+
266
+ for (const statement of sourceFile.statements) {
267
+ if (ts.isVariableStatement(statement) && isExported(statement, ts)) {
268
+ const isConst = (statement.declarationList.flags & ts.NodeFlags.Const) !== 0
269
+
270
+ for (const declaration of statement.declarationList.declarations) {
271
+ if (!ts.isIdentifier(declaration.name)) continue
272
+ const name = declaration.name.text
273
+
274
+ if (SEGMENT_CONFIG_EXPORTS.includes(name)) {
275
+ if (!isConst) {
276
+ recordSegmentError(name, declaration, 'is declared with "let" or "var"')
277
+ continue
278
+ }
279
+ if (!declaration.initializer) {
280
+ recordSegmentError(name, declaration, 'has no initializer')
281
+ continue
282
+ }
283
+
284
+ const literal = evaluateLiteral(declaration.initializer, ts)
285
+ if (!literal.ok) {
286
+ recordSegmentError(name, declaration, 'is not a literal (an expression or identifier)')
287
+ continue
288
+ }
289
+
290
+ const validate = SEGMENT_CONFIG_VALIDATORS[name]
291
+ if (!validate(literal.value)) {
292
+ recordSegmentError(name, declaration, `has a value (${JSON.stringify(literal.value)}) outside what Next.js accepts for "${name}"`)
293
+ continue
294
+ }
295
+
296
+ segmentConfig[name] = literal.value
297
+ } else if (MODULE_LEVEL_EXPORTS.includes(name)) {
298
+ addModuleExport(name)
299
+ }
300
+ }
301
+ } else if (ts.isFunctionDeclaration(statement) && statement.name && isExported(statement, ts)) {
302
+ const name = statement.name.text
303
+ if (SEGMENT_CONFIG_EXPORTS.includes(name)) {
304
+ recordSegmentError(name, statement, 'is declared as a function, not a literal')
305
+ } else if (MODULE_LEVEL_EXPORTS.includes(name)) {
306
+ addModuleExport(name)
307
+ }
308
+ } else if (ts.isExportDeclaration(statement) && statement.exportClause && ts.isNamedExports(statement.exportClause)) {
309
+ for (const element of statement.exportClause.elements) {
310
+ const exportedName = element.name.text
311
+
312
+ if (SEGMENT_CONFIG_EXPORTS.includes(exportedName)) {
313
+ recordSegmentError(
314
+ exportedName,
315
+ element,
316
+ statement.moduleSpecifier
317
+ ? 'is re-exported from another module'
318
+ : 'is exported via `export { }` instead of an inline `export const`'
319
+ )
320
+ } else if (MODULE_LEVEL_EXPORTS.includes(exportedName)) {
321
+ addModuleExport(exportedName)
322
+ }
323
+ }
324
+ }
325
+ }
326
+
327
+ if (errors.length > 0) {
328
+ throw new Error(errors.join('\n'))
329
+ }
330
+
331
+ return { segmentConfig, moduleExports, hasDefaultExport: declaresDefaultExport(sourceFile, ts) }
332
+ }
333
+
334
+ /**
335
+ * Resolve a `@/contents/...` template import path to its file on disk,
336
+ * trying both TSX and TS extensions.
337
+ */
338
+ function resolveTemplateFilePath(templatePath) {
339
+ const baseTemplatePath = templatePath
340
+ .replace('@/', rootDir + '/')
341
+ .replace(/\.(tsx|ts)$/, '')
342
+ return existsSync(baseTemplatePath + '.tsx') ? baseTemplatePath + '.tsx' : baseTemplatePath + '.ts'
343
+ }
344
+
345
+ /**
346
+ * Read a template file and extract its route-level exports. A missing file
347
+ * is treated as having none, since a build-time override may point at a
348
+ * template that hasn't been generated yet; a template that fails to parse
349
+ * its segment config is not swallowed the same way, and propagates instead.
350
+ */
351
+ async function readRouteExports(templatePath) {
352
+ const absoluteTemplatePath = resolveTemplateFilePath(templatePath)
353
+
354
+ let templateContent
355
+ try {
356
+ templateContent = await readFile(absoluteTemplatePath, 'utf8')
71
357
  } catch {
72
- return []
358
+ return { segmentConfig: {}, moduleExports: [], hasDefaultExport: false }
73
359
  }
360
+
361
+ return extractRouteExports(templateContent, absoluteTemplatePath)
362
+ }
363
+
364
+ /**
365
+ * Render the segment config keys a template exports as re-declared literals.
366
+ */
367
+ function renderSegmentConfigBlock(segmentConfig) {
368
+ return Object.entries(segmentConfig)
369
+ .map(([name, value]) => `export const ${name} = ${JSON.stringify(value)}\n`)
370
+ .join('')
371
+ }
372
+
373
+ /**
374
+ * Render the module-level exports a template exports as a re-export from it.
375
+ */
376
+ function renderModuleExportsBlock(moduleExports, templatePathWithoutExtension) {
377
+ return moduleExports.length > 0
378
+ ? `export { ${moduleExports.join(', ')} } from '${templatePathWithoutExtension}'\n`
379
+ : ''
74
380
  }
75
381
 
76
382
  /**
@@ -80,9 +386,16 @@ async function generateRegularPageContent(appPath, templatePath) {
80
386
  // Remove .tsx/.ts extension from import path (TypeScript doesn't allow file extensions in imports)
81
387
  const templatePathWithoutExtension = templatePath.replace(/\.(tsx|ts)$/, '')
82
388
 
83
- const routeExports = await detectRouteLevelExports(templatePath)
84
- const routeExportsBlock = routeExports.length > 0
85
- ? `\n// Re-export Next.js route-level exports from the theme template\nexport { ${routeExports.join(', ')} } from '${templatePathWithoutExtension}'\n`
389
+ const { segmentConfig, moduleExports } = await readRouteExports(templatePath)
390
+
391
+ const segmentConfigBlock = renderSegmentConfigBlock(segmentConfig)
392
+ const segmentConfigSection = segmentConfigBlock
393
+ ? `\n// Segment config re-declared as literals: Next.js only reads these when they're declared directly in the route file, not re-exported\n${segmentConfigBlock}`
394
+ : ''
395
+
396
+ const moduleExportsBlock = renderModuleExportsBlock(moduleExports, templatePathWithoutExtension)
397
+ const moduleExportsSection = moduleExportsBlock
398
+ ? `\n// Re-export Next.js route-level exports from the theme template\n${moduleExportsBlock}`
86
399
  : ''
87
400
 
88
401
  return `/**
@@ -94,7 +407,7 @@ import TemplateComponent from '${templatePathWithoutExtension}'
94
407
 
95
408
  // Direct export of the theme template (no fallback)
96
409
  export default TemplateComponent
97
- ${routeExportsBlock}`
410
+ ${segmentConfigSection}${moduleExportsSection}`
98
411
  }
99
412
 
100
413
  /**
@@ -104,32 +417,33 @@ async function generateLayoutPageContent(appPath, componentName, templatePath) {
104
417
  if (templatePath) {
105
418
  // Remove .tsx/.ts extension from import path (TypeScript doesn't allow file extensions in imports)
106
419
  const templatePathWithoutExtension = templatePath.replace(/\.(tsx|ts)$/, '')
420
+ const absoluteTemplatePath = resolveTemplateFilePath(templatePath)
107
421
 
108
- // Convert import path alias to absolute file system path
109
- // @/contents/themes/... → /absolute/path/contents/themes/...
110
- // Try both .tsx and .ts extensions
111
- const baseTemplatePath = templatePath
112
- .replace('@/', rootDir + '/')
113
- .replace(/\.(tsx|ts)$/, '')
114
- const absoluteTemplatePath = existsSync(baseTemplatePath + '.tsx')
115
- ? baseTemplatePath + '.tsx'
116
- : baseTemplatePath + '.ts'
117
-
118
- // Check if template file has a default export (component) or just metadata
119
- let hasDefaultExport = false
120
- let hasMetadata = false
121
-
422
+ // Check if template file has a default export (component), and what
423
+ // route-level exports it defines alongside (or instead of) it
424
+ let templateContent = null
122
425
  try {
123
- const templateContent = await readFile(absoluteTemplatePath, 'utf8')
124
- hasDefaultExport = /export\s+default/.test(templateContent)
125
- hasMetadata = /export\s+const\s+metadata\s*=/.test(templateContent)
126
- } catch (error) {
127
- // If we can't read the file, assume it has a default export
128
- hasDefaultExport = true
426
+ templateContent = await readFile(absoluteTemplatePath, 'utf8')
427
+ } catch {
428
+ // Can't read the file - fall through to the default-export assumption below
129
429
  }
130
430
 
431
+ let hasDefaultExport = true
432
+ let segmentConfig = {}
433
+ let moduleExports = []
434
+ if (templateContent !== null) {
435
+ ;({ segmentConfig, moduleExports, hasDefaultExport } = await extractRouteExports(templateContent, absoluteTemplatePath))
436
+ }
437
+
438
+ const segmentConfigBlock = renderSegmentConfigBlock(segmentConfig)
439
+ const moduleExportsBlock = renderModuleExportsBlock(moduleExports, templatePathWithoutExtension)
440
+
131
441
  if (hasDefaultExport) {
132
- // Template has a component - import and re-export it
442
+ // Template has a component - import and re-export it, plus any route-level exports it defines
443
+ const forwardedExportsSection = segmentConfigBlock || moduleExportsBlock
444
+ ? `\n// Re-export Next.js route-level exports from the theme template\n${segmentConfigBlock}${moduleExportsBlock}`
445
+ : ''
446
+
133
447
  return `/**
134
448
  * Layout template - directly imports from theme
135
449
  * Template: ${appPath}
@@ -139,9 +453,10 @@ import TemplateComponent from '${templatePathWithoutExtension}'
139
453
 
140
454
  // Direct export of the theme template (no fallback)
141
455
  export default TemplateComponent
142
- `
143
- } else if (hasMetadata) {
144
- // Metadata-only template - re-export metadata and provide pass-through component
456
+ ${forwardedExportsSection}`
457
+ } else if (segmentConfigBlock || moduleExportsBlock) {
458
+ // No component, but the template still defines segment config and/or metadata -
459
+ // re-export those and provide a pass-through component
145
460
  return `/**
146
461
  * Layout template - metadata-only (PROTECTED_RENDER)
147
462
  * Template: ${appPath}
@@ -149,9 +464,8 @@ export default TemplateComponent
149
464
  */
150
465
  import type { ReactNode } from 'react'
151
466
 
152
- // Re-export metadata from theme template
153
- export { metadata } from '${templatePathWithoutExtension}'
154
-
467
+ // Re-export Next.js route-level exports from the theme template
468
+ ${segmentConfigBlock}${moduleExportsBlock}
155
469
  // Pass-through component (actual rendering blocked by PROTECTED_RENDER)
156
470
  interface ${componentName}Props {
157
471
  children: ReactNode
@@ -275,6 +589,13 @@ async function duplicateLayoutDirect(sourcePath, targetPath, layoutPath, templat
275
589
  const templatePathWithoutExtension = layoutOverride.templatePath.replace(/\.(tsx|ts)$/, '')
276
590
  const componentName = `Layout${toPascalCase(layoutPath.replace(/[^\w]/g, '_'))}`
277
591
 
592
+ const { segmentConfig, moduleExports } = await readRouteExports(layoutOverride.templatePath)
593
+ const segmentConfigBlock = renderSegmentConfigBlock(segmentConfig)
594
+ const moduleExportsBlock = renderModuleExportsBlock(moduleExports, templatePathWithoutExtension)
595
+ const forwardedExportsSection = segmentConfigBlock || moduleExportsBlock
596
+ ? `\n// Re-export Next.js route-level exports from the theme template\n${segmentConfigBlock}${moduleExportsBlock}`
597
+ : ''
598
+
278
599
  const content = `/**
279
600
  * Layout with template override - directly imports from theme
280
601
  * Template: ${layoutPath}
@@ -284,7 +605,7 @@ async function duplicateLayoutDirect(sourcePath, targetPath, layoutPath, templat
284
605
  import TemplateLayout from '${templatePathWithoutExtension}'
285
606
 
286
607
  export default TemplateLayout
287
- `
608
+ ${forwardedExportsSection}`
288
609
  await writeFile(targetPath, content, 'utf8')
289
610
  verbose(`Generated layout with template override: ${layoutPath}`)
290
611
  return
@@ -53,6 +53,7 @@ import { discoverMiddlewares } from './registry/discovery/middlewares.mjs'
53
53
  import { discoverTemplates } from './registry/discovery/templates.mjs'
54
54
  import { discoverEmails } from './registry/discovery/emails.mjs'
55
55
  import { discoverBlocks } from './registry/discovery/blocks.mjs'
56
+ import { discoverIcons } from './registry/discovery/icons.mjs'
56
57
  import { discoverCoreRoutes } from './registry/discovery/core-routes.mjs'
57
58
  import { discoverApiPresets } from './registry/discovery/api-presets.mjs'
58
59
  import { discoverMcpOverrides } from './registry/discovery/mcp-overrides.mjs'
@@ -63,7 +64,8 @@ import { generateEntityTypes } from './registry/generators/entity-types.mjs'
63
64
  import { generateThemeRegistry } from './registry/generators/theme-registry.mjs'
64
65
  import { generateTemplateRegistry, generateTemplateRegistryClient } from './registry/generators/template-registry.mjs'
65
66
  import { generateEmailRegistry } from './registry/generators/email-registry.mjs'
66
- import { generateBlockRegistry } from './registry/generators/block-registry.mjs'
67
+ import { generateBlockRegistry, generateBlockRegistryClient, generateBlockRegistryLazy } from './registry/generators/block-registry.mjs'
68
+ import { generateIconRegistry } from './registry/generators/icon-registry.mjs'
67
69
  import { generateMiddlewareRegistry } from './registry/generators/middleware-registry.mjs'
68
70
  import { generateRouteHandlersRegistry } from './registry/generators/route-handlers.mjs'
69
71
  import { generateTranslationRegistry } from './registry/generators/translation-registry.mjs'
@@ -101,6 +103,9 @@ async function generateRegistryFiles(CONFIG, plugins, entities, themes, template
101
103
  // Generate client template registry (async - needs to check for server exports)
102
104
  const templateRegistryClientContent = await generateTemplateRegistryClient(templates, CONFIG)
103
105
 
106
+ // Collect the icon names configs can ask for by string (async - reads configs)
107
+ const iconNames = await discoverIcons(blocks, CONFIG)
108
+
104
109
  // Generate individual registries (pass CONFIG to all generators)
105
110
  const files = [
106
111
  { name: 'plugin-registry.ts', content: generatePluginRegistry(plugins, CONFIG) },
@@ -115,6 +120,9 @@ async function generateRegistryFiles(CONFIG, plugins, entities, themes, template
115
120
  { name: 'template-registry.client.ts', content: templateRegistryClientContent },
116
121
  { name: 'email-registry.ts', content: generateEmailRegistry(emails, CONFIG) },
117
122
  { name: 'block-registry.ts', content: generateBlockRegistry(blocks, CONFIG) },
123
+ { name: 'block-registry.client.ts', content: generateBlockRegistryClient(blocks, CONFIG) },
124
+ { name: 'block-registry.lazy.ts', content: generateBlockRegistryLazy(blocks) },
125
+ { name: 'icon-registry.ts', content: generateIconRegistry(iconNames, CONFIG) },
118
126
  { name: 'billing-registry.ts', content: await generateBillingRegistry(CONFIG.activeTheme, CONFIG.contentsDir, CONFIG) },
119
127
  { name: 'middleware-registry.ts', content: generateMiddlewareRegistry(middlewares, CONFIG) },
120
128
  { name: 'scope-registry.ts', content: generateScopeRegistry(entities, CONFIG) },
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { resetPasswordSchema, type ResetPasswordFormData } from '@nextsparkjs/core/lib/validation';
4
- import { useAuth } from '@nextsparkjs/core/hooks/useAuth';
4
+ import { useAuthActions } from '@nextsparkjs/core/hooks/useAuth';
5
5
  import { zodResolver } from '@hookform/resolvers/zod';
6
6
  import { AlertCircle, ArrowLeft, CheckCircle, Loader2, Mail } from 'lucide-react';
7
7
  import Link from 'next/link';
@@ -18,7 +18,7 @@ import { getTemplateOrDefaultClient } from '@nextsparkjs/registries/template-reg
18
18
 
19
19
 
20
20
  function ForgotPasswordPage() {
21
- const { resetPassword } = useAuth();
21
+ const { resetPassword } = useAuthActions();
22
22
  const [loading, setLoading] = useState(false);
23
23
  const [error, setError] = useState<string | null>(null);
24
24
  const [success, setSuccess] = useState(false);
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { newPasswordSchema, type NewPasswordFormData } from "@nextsparkjs/core/lib/validation";
4
- import { useAuth } from "@nextsparkjs/core/hooks/useAuth";
4
+ import { useAuthActions } from "@nextsparkjs/core/hooks/useAuth";
5
5
  import { zodResolver } from "@hookform/resolvers/zod";
6
6
  import {
7
7
  AlertCircle,
@@ -24,7 +24,7 @@ import { sel } from '@nextsparkjs/core/selectors'
24
24
  function ResetPasswordContent() {
25
25
  const searchParams = useSearchParams()!;
26
26
  const token = searchParams.get("token") || "";
27
- const { updatePassword } = useAuth();
27
+ const { updatePassword } = useAuthActions();
28
28
 
29
29
  const [loading, setLoading] = useState(false);
30
30
  const [error, setError] = useState<string | null>(null);
@@ -57,13 +57,13 @@ function ResetPasswordContent() {
57
57
 
58
58
  const onSubmit = useCallback(async (data: NewPasswordFormData) => {
59
59
  if (!token) {
60
- setError("Token de recuperación inválido");
60
+ setError("Invalid recovery token");
61
61
  return;
62
62
  }
63
63
 
64
64
  setLoading(true);
65
65
  setError(null);
66
- console.log('Actualizando contraseña...');
66
+ console.log('Updating password...');
67
67
 
68
68
  try {
69
69
  const result = await updatePassword(data.password, token);
@@ -73,10 +73,10 @@ function ResetPasswordContent() {
73
73
  setError(errorMsg);
74
74
  } else {
75
75
  setSuccess(true);
76
- console.log('Contraseña actualizada exitosamente');
76
+ console.log('Password updated successfully');
77
77
  }
78
78
  } catch {
79
- setError('Error inesperado al actualizar contraseña');
79
+ setError('Unexpected error updating password');
80
80
  } finally {
81
81
  setLoading(false);
82
82
  }
@@ -10,7 +10,7 @@ import { getTemplateOrDefaultClient } from '@nextsparkjs/registries/template-reg
10
10
 
11
11
  function ForbiddenContent() {
12
12
  const searchParams = useSearchParams()!
13
- const reason = searchParams.get('reason') || 'No tienes permisos para acceder a este recurso'
13
+ const reason = searchParams.get('reason') || 'You do not have permission to access this resource'
14
14
  const upgrade = searchParams.get('upgrade') === 'true'
15
15
 
16
16
  return (
@@ -21,7 +21,7 @@ function ForbiddenContent() {
21
21
  <AlertTriangle className="h-8 w-8 text-yellow-600" />
22
22
  </div>
23
23
  <CardTitle className="text-2xl">
24
- ⚠️ Ups, no tienes permisos para hacer esto
24
+ ⚠️ Oops, you do not have permission to do this
25
25
  </CardTitle>
26
26
  <CardDescription className="text-gray-600">
27
27
  {reason}
@@ -32,11 +32,11 @@ function ForbiddenContent() {
32
32
  {upgrade && (
33
33
  <div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
34
34
  <p className="text-sm text-blue-800">
35
- Esta funcionalidad requiere una mejora de tu plan actual.
35
+ This feature requires an upgrade to your current plan.
36
36
  </p>
37
37
  <Link href="/dashboard/settings/billing" className="block mt-2">
38
38
  <Button variant="outline" size="sm" className="w-full">
39
- Ver planes
39
+ View plans
40
40
  </Button>
41
41
  </Link>
42
42
  </div>
@@ -49,7 +49,7 @@ function ForbiddenContent() {
49
49
  onClick={() => window.history.back()}
50
50
  >
51
51
  <ArrowLeft className="h-4 w-4 mr-2" />
52
- Volver
52
+ Back
53
53
  </Button>
54
54
 
55
55
  <Link href="/dashboard" className="flex-1">
@@ -75,7 +75,7 @@ function ForbiddenPage() {
75
75
  <AlertTriangle className="h-8 w-8 text-yellow-600" />
76
76
  </div>
77
77
  <CardTitle className="text-2xl">
78
- ⚠️ Ups, no tienes permisos para hacer esto
78
+ ⚠️ Oops, you do not have permission to do this
79
79
  </CardTitle>
80
80
  </CardHeader>
81
81
  </Card>