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

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 (456) hide show
  1. package/dist/components/api/keys/CreateApiKeyDialog.d.ts.map +1 -1
  2. package/dist/components/api/keys/CreateApiKeyDialog.js +2 -1
  3. package/dist/components/app/layouts/PublicFooter.d.ts.map +1 -1
  4. package/dist/components/app/layouts/PublicFooter.js +2 -23
  5. package/dist/components/app/misc/ThemeToggle.d.ts.map +1 -1
  6. package/dist/components/app/misc/ThemeToggle.js +2 -1
  7. package/dist/components/auth/forms/LoginForm.js +1 -1
  8. package/dist/components/auth/forms/SignupForm.d.ts.map +1 -1
  9. package/dist/components/auth/forms/SignupForm.js +2 -1
  10. package/dist/components/auth/pages/AuthErrorPage.d.ts.map +1 -1
  11. package/dist/components/auth/pages/AuthErrorPage.js +2 -1
  12. package/dist/components/billing/ManageBillingButton.d.ts.map +1 -1
  13. package/dist/components/billing/ManageBillingButton.js +2 -1
  14. package/dist/components/dashboard/block-editor/array-field.d.ts.map +1 -1
  15. package/dist/components/dashboard/block-editor/array-field.js +2 -1
  16. package/dist/components/dashboard/block-editor/block-picker.d.ts.map +1 -1
  17. package/dist/components/dashboard/block-editor/block-picker.js +2 -1
  18. package/dist/components/dashboard/block-editor/builder-editor-view.d.ts.map +1 -1
  19. package/dist/components/dashboard/block-editor/builder-editor-view.js +3 -2
  20. package/dist/components/dashboard/block-editor/config-panel.d.ts.map +1 -1
  21. package/dist/components/dashboard/block-editor/config-panel.js +3 -2
  22. package/dist/components/dashboard/block-editor/dynamic-form.d.ts.map +1 -1
  23. package/dist/components/dashboard/block-editor/dynamic-form.js +2 -1
  24. package/dist/components/dashboard/block-editor/entity-fields-sidebar.d.ts.map +1 -1
  25. package/dist/components/dashboard/block-editor/entity-fields-sidebar.js +3 -2
  26. package/dist/components/dashboard/block-editor/pattern-reference-preview.d.ts.map +1 -1
  27. package/dist/components/dashboard/block-editor/pattern-reference-preview.js +2 -1
  28. package/dist/components/dashboard/layouts/TopNavbar.d.ts.map +1 -1
  29. package/dist/components/dashboard/layouts/TopNavbar.js +4 -3
  30. package/dist/components/dashboard/misc/SearchDropdown.d.ts.map +1 -1
  31. package/dist/components/dashboard/misc/SearchDropdown.js +2 -1
  32. package/dist/components/dashboard/mobile/MobileTopBar.d.ts.map +1 -1
  33. package/dist/components/dashboard/mobile/MobileTopBar.js +2 -1
  34. package/dist/components/devtools/BlockDetailViewer.d.ts.map +1 -1
  35. package/dist/components/devtools/BlockDetailViewer.js +2 -1
  36. package/dist/components/devtools/BlocksViewer.d.ts.map +1 -1
  37. package/dist/components/devtools/BlocksViewer.js +2 -1
  38. package/dist/components/devtools/ConfigViewer.d.ts.map +1 -1
  39. package/dist/components/devtools/ConfigViewer.js +3 -2
  40. package/dist/components/devtools/TestCasesViewer.d.ts.map +1 -1
  41. package/dist/components/devtools/TestCasesViewer.js +3 -2
  42. package/dist/components/devtools/TestCoverageDashboard.d.ts.map +1 -1
  43. package/dist/components/devtools/TestCoverageDashboard.js +2 -1
  44. package/dist/components/devtools/api-explorer/ApiDocsModal.d.ts.map +1 -1
  45. package/dist/components/devtools/api-explorer/ApiDocsModal.js +0 -0
  46. package/dist/components/devtools/api-explorer/ApiExplorer.d.ts.map +1 -1
  47. package/dist/components/devtools/api-explorer/ApiExplorer.js +2 -1
  48. package/dist/components/devtools/scheduled-actions/actions-table.d.ts.map +1 -1
  49. package/dist/components/devtools/scheduled-actions/actions-table.js +3 -2
  50. package/dist/components/docs/docs-sidebar.d.ts.map +1 -1
  51. package/dist/components/docs/docs-sidebar.js +3 -2
  52. package/dist/components/entities/EntityFieldRenderer.d.ts.map +1 -1
  53. package/dist/components/entities/EntityFieldRenderer.js +2 -1
  54. package/dist/components/entities/EntityList.d.ts.map +1 -1
  55. package/dist/components/entities/EntityList.js +2 -1
  56. package/dist/components/entities/EntityTable.d.ts.map +1 -1
  57. package/dist/components/entities/EntityTable.js +3 -2
  58. package/dist/components/entities/ErrorBoundary.d.ts.map +1 -1
  59. package/dist/components/entities/ErrorBoundary.js +2 -1
  60. package/dist/components/entities/wrappers/EntityDetailWrapper.d.ts.map +1 -1
  61. package/dist/components/entities/wrappers/EntityDetailWrapper.js +3 -2
  62. package/dist/components/media/MediaCard.d.ts.map +1 -1
  63. package/dist/components/media/MediaCard.js +2 -1
  64. package/dist/components/media/MediaDetailPanel.d.ts.map +1 -1
  65. package/dist/components/media/MediaDetailPanel.js +2 -1
  66. package/dist/components/media/MediaList.d.ts.map +1 -1
  67. package/dist/components/media/MediaList.js +2 -1
  68. package/dist/components/media/MediaSelector.d.ts.map +1 -1
  69. package/dist/components/media/MediaSelector.js +2 -1
  70. package/dist/components/media/MediaUploadZone.d.ts.map +1 -1
  71. package/dist/components/media/MediaUploadZone.js +2 -1
  72. package/dist/components/public/entities/PublicEntityDetail.d.ts.map +1 -1
  73. package/dist/components/public/entities/PublicEntityDetail.js +2 -1
  74. package/dist/components/teams/TeamPendingInvitations.d.ts.map +1 -1
  75. package/dist/components/teams/TeamPendingInvitations.js +3 -2
  76. package/dist/components/ui/accordion.js +1 -1
  77. package/dist/components/ui/audio-upload.d.ts.map +1 -1
  78. package/dist/components/ui/audio-upload.js +2 -1
  79. package/dist/components/ui/avatar.d.ts +17 -1
  80. package/dist/components/ui/avatar.d.ts.map +1 -1
  81. package/dist/components/ui/avatar.js +29 -2
  82. package/dist/components/ui/badge.js +1 -1
  83. package/dist/components/ui/breadcrumb.d.ts +13 -0
  84. package/dist/components/ui/breadcrumb.d.ts.map +1 -1
  85. package/dist/components/ui/breadcrumb.js +23 -10
  86. package/dist/components/ui/button.js +1 -1
  87. package/dist/components/ui/card.js +1 -1
  88. package/dist/components/ui/checkbox.js +1 -1
  89. package/dist/components/ui/file-upload.d.ts.map +1 -1
  90. package/dist/components/ui/file-upload.js +2 -1
  91. package/dist/components/ui/image-upload.d.ts.map +1 -1
  92. package/dist/components/ui/image-upload.js +4 -3
  93. package/dist/components/ui/input.js +1 -1
  94. package/dist/components/ui/label.js +1 -1
  95. package/dist/components/ui/optimized-image.d.ts.map +1 -1
  96. package/dist/components/ui/optimized-image.js +5 -0
  97. package/dist/components/ui/pagination.d.ts +1 -1
  98. package/dist/components/ui/pagination.d.ts.map +1 -1
  99. package/dist/components/ui/pagination.js +3 -0
  100. package/dist/components/ui/progress.js +1 -1
  101. package/dist/components/ui/relation-display.d.ts.map +1 -1
  102. package/dist/components/ui/relation-display.js +3 -2
  103. package/dist/components/ui/separator.js +1 -1
  104. package/dist/components/ui/simple-relation-select.d.ts.map +1 -1
  105. package/dist/components/ui/simple-relation-select.js +7 -6
  106. package/dist/components/ui/skeleton.js +1 -1
  107. package/dist/components/ui/slider.js +1 -1
  108. package/dist/components/ui/switch.js +1 -1
  109. package/dist/components/ui/tabs.js +1 -1
  110. package/dist/components/ui/textarea.js +1 -1
  111. package/dist/components/ui/user-display.d.ts.map +1 -1
  112. package/dist/components/ui/user-display.js +2 -1
  113. package/dist/components/ui/user-select.d.ts.map +1 -1
  114. package/dist/components/ui/user-select.js +2 -1
  115. package/dist/components/ui/video-upload.d.ts.map +1 -1
  116. package/dist/components/ui/video-upload.js +3 -2
  117. package/dist/components/users/tables/UsersTable.d.ts.map +1 -1
  118. package/dist/components/users/tables/UsersTable.js +3 -2
  119. package/dist/contexts/SubscriptionContext.d.ts.map +1 -1
  120. package/dist/contexts/SubscriptionContext.js +4 -3
  121. package/dist/contexts/TeamContext.d.ts.map +1 -1
  122. package/dist/contexts/TeamContext.js +3 -2
  123. package/dist/hooks/useAccountPreferencesSync.d.ts.map +1 -1
  124. package/dist/hooks/useAccountPreferencesSync.js +2 -1
  125. package/dist/hooks/useApiExplorerNavigation.d.ts.map +1 -1
  126. package/dist/hooks/useApiExplorerNavigation.js +2 -1
  127. package/dist/hooks/useAuth.d.ts.map +1 -1
  128. package/dist/hooks/useAuth.js +3 -2
  129. package/dist/hooks/useEnsureUserMetadata.d.ts.map +1 -1
  130. package/dist/hooks/useEnsureUserMetadata.js +3 -2
  131. package/dist/hooks/useEntityWithMeta.d.ts.map +1 -1
  132. package/dist/hooks/useEntityWithMeta.js +3 -2
  133. package/dist/hooks/useInvoice.d.ts.map +1 -1
  134. package/dist/hooks/useInvoice.js +2 -1
  135. package/dist/hooks/useInvoices.d.ts.map +1 -1
  136. package/dist/hooks/useInvoices.js +2 -1
  137. package/dist/hooks/useMedia.d.ts.map +1 -1
  138. package/dist/hooks/useMedia.js +10 -9
  139. package/dist/hooks/useMediaUpload.d.ts.map +1 -1
  140. package/dist/hooks/useMediaUpload.js +2 -1
  141. package/dist/hooks/useMembership.d.ts.map +1 -1
  142. package/dist/hooks/useMembership.js +3 -2
  143. package/dist/hooks/usePatternUsages.d.ts.map +1 -1
  144. package/dist/hooks/usePatternUsages.js +3 -2
  145. package/dist/hooks/usePrefetch.d.ts.map +1 -1
  146. package/dist/hooks/usePrefetch.js +3 -2
  147. package/dist/hooks/useQuota.d.ts.map +1 -1
  148. package/dist/hooks/useQuota.js +2 -1
  149. package/dist/hooks/useTeamInvitations.d.ts.map +1 -1
  150. package/dist/hooks/useTeamInvitations.js +4 -3
  151. package/dist/hooks/useTeamMembers.d.ts.map +1 -1
  152. package/dist/hooks/useTeamMembers.js +5 -4
  153. package/dist/hooks/useTeamUpdate.d.ts.map +1 -1
  154. package/dist/hooks/useTeamUpdate.js +2 -1
  155. package/dist/hooks/useUserProfile.d.ts.map +1 -1
  156. package/dist/hooks/useUserProfile.js +3 -2
  157. package/dist/hooks/useUserSettings.d.ts.map +1 -1
  158. package/dist/hooks/useUserSettings.js +3 -2
  159. package/dist/lib/actions/entity.actions.d.ts.map +1 -1
  160. package/dist/lib/actions/entity.actions.js +2 -1
  161. package/dist/lib/api/entities.d.ts.map +1 -1
  162. package/dist/lib/api/entities.js +4 -3
  163. package/dist/lib/auth/verify-email-link.d.ts +10 -0
  164. package/dist/lib/auth/verify-email-link.d.ts.map +1 -0
  165. package/dist/lib/auth/verify-email-link.js +14 -0
  166. package/dist/lib/auth-client.d.ts +2 -0
  167. package/dist/lib/auth-client.d.ts.map +1 -1
  168. package/dist/lib/auth-client.js +4 -0
  169. package/dist/lib/auth.d.ts.map +1 -1
  170. package/dist/lib/auth.js +10 -2
  171. package/dist/lib/base-path.d.ts +85 -0
  172. package/dist/lib/base-path.d.ts.map +1 -0
  173. package/dist/lib/base-path.js +79 -0
  174. package/dist/lib/billing/schema.d.ts +1 -1
  175. package/dist/lib/billing/schema.d.ts.map +1 -1
  176. package/dist/lib/billing/schema.js +1 -1
  177. package/dist/lib/blocks/sanitize-html.d.ts.map +1 -1
  178. package/dist/lib/blocks/sanitize-html.js +18 -2
  179. package/dist/lib/cache/revalidate-tag.d.ts +14 -0
  180. package/dist/lib/cache/revalidate-tag.d.ts.map +1 -0
  181. package/dist/lib/cache/revalidate-tag.js +8 -0
  182. package/dist/lib/config/dashboard-schema.d.ts +1 -1
  183. package/dist/lib/config/dashboard-schema.d.ts.map +1 -1
  184. package/dist/lib/config/dashboard-schema.js +1 -1
  185. package/dist/lib/config/types.d.ts +36 -16
  186. package/dist/lib/config/types.d.ts.map +1 -1
  187. package/dist/lib/docs/access.d.ts +28 -0
  188. package/dist/lib/docs/access.d.ts.map +1 -0
  189. package/dist/lib/docs/access.js +16 -0
  190. package/dist/lib/docs/parser.d.ts.map +1 -1
  191. package/dist/lib/docs/parser.js +2 -1
  192. package/dist/lib/docs/remark-doc-links.d.ts +14 -0
  193. package/dist/lib/docs/remark-doc-links.d.ts.map +1 -0
  194. package/dist/lib/docs/remark-doc-links.js +38 -0
  195. package/dist/lib/entities/api-generator.d.ts +1 -1
  196. package/dist/lib/entities/api-generator.d.ts.map +1 -1
  197. package/dist/lib/entities/api-generator.js +1 -1
  198. package/dist/lib/entities/schema-generator.d.ts +1 -1
  199. package/dist/lib/entities/schema-generator.d.ts.map +1 -1
  200. package/dist/lib/entities/schema-generator.js +1 -1
  201. package/dist/lib/mcp/engine.js +1 -1
  202. package/dist/lib/mcp/schema-builder.d.ts +1 -1
  203. package/dist/lib/mcp/schema-builder.d.ts.map +1 -1
  204. package/dist/lib/mcp/schema-builder.js +1 -1
  205. package/dist/lib/mcp/tool-generator.js +1 -1
  206. package/dist/lib/mcp/types.d.ts +1 -1
  207. package/dist/lib/mcp/types.d.ts.map +1 -1
  208. package/dist/lib/media/schemas.d.ts +1 -1
  209. package/dist/lib/media/schemas.d.ts.map +1 -1
  210. package/dist/lib/media/schemas.js +1 -1
  211. package/dist/lib/middleware/index.d.ts +5 -1
  212. package/dist/lib/middleware/index.d.ts.map +1 -1
  213. package/dist/lib/middleware/index.js +3 -2
  214. package/dist/lib/services/meta.service.d.ts +60 -0
  215. package/dist/lib/services/meta.service.d.ts.map +1 -1
  216. package/dist/lib/services/meta.service.js +132 -12
  217. package/dist/lib/services/middleware.service.d.ts.map +1 -1
  218. package/dist/lib/services/middleware.service.js +3 -2
  219. package/dist/lib/teams/schema.d.ts +1 -1
  220. package/dist/lib/teams/schema.d.ts.map +1 -1
  221. package/dist/lib/teams/schema.js +1 -1
  222. package/dist/lib/teams/teams.api.d.ts.map +1 -1
  223. package/dist/lib/teams/teams.api.js +4 -3
  224. package/dist/lib/user-data-client.d.ts.map +1 -1
  225. package/dist/lib/user-data-client.js +3 -2
  226. package/dist/lib/validation/invoices.d.ts +1 -1
  227. package/dist/lib/validation/invoices.d.ts.map +1 -1
  228. package/dist/lib/validation/invoices.js +1 -1
  229. package/dist/lib/validation-schemas.d.ts +1 -1
  230. package/dist/lib/validation-schemas.d.ts.map +1 -1
  231. package/dist/lib/validation-schemas.js +1 -1
  232. package/dist/lib/validation.d.ts +1 -1
  233. package/dist/lib/validation.d.ts.map +1 -1
  234. package/dist/lib/validation.js +1 -1
  235. package/dist/messages/de/footer.json +0 -1
  236. package/dist/messages/de/index.d.ts +0 -1
  237. package/dist/messages/de/index.d.ts.map +1 -1
  238. package/dist/messages/en/footer.json +0 -1
  239. package/dist/messages/en/index.d.ts +0 -1
  240. package/dist/messages/en/index.d.ts.map +1 -1
  241. package/dist/messages/es/footer.json +0 -1
  242. package/dist/messages/es/index.d.ts +0 -1
  243. package/dist/messages/es/index.d.ts.map +1 -1
  244. package/dist/messages/fr/footer.json +0 -1
  245. package/dist/messages/fr/index.d.ts +0 -1
  246. package/dist/messages/fr/index.d.ts.map +1 -1
  247. package/dist/messages/it/footer.json +0 -1
  248. package/dist/messages/it/index.d.ts +0 -1
  249. package/dist/messages/it/index.d.ts.map +1 -1
  250. package/dist/messages/pt/footer.json +0 -1
  251. package/dist/messages/pt/index.d.ts +0 -1
  252. package/dist/messages/pt/index.d.ts.map +1 -1
  253. package/dist/templates/app/(auth)/accept-invite/[token]/page.tsx +4 -3
  254. package/dist/templates/app/(auth)/verify-email/page.tsx +4 -3
  255. package/dist/templates/app/(public)/docs/[section]/[page]/page.tsx +11 -0
  256. package/dist/templates/app/api/auth/[...all]/route.ts +15 -14
  257. package/dist/templates/app/api/internal/user-metadata/route.ts +6 -8
  258. package/dist/templates/app/api/user/plan-flags/route.ts +1 -1
  259. package/dist/templates/app/api/user/profile/route.ts +12 -17
  260. package/dist/templates/app/api/v1/api-keys/route.ts +1 -1
  261. package/dist/templates/app/api/v1/auth/signup-with-invite/route.ts +2 -1
  262. package/dist/templates/app/api/v1/billing/cancel/route.ts +1 -1
  263. package/dist/templates/app/api/v1/billing/change-plan/route.ts +1 -1
  264. package/dist/templates/app/api/v1/billing/check-action/route.ts +1 -1
  265. package/dist/templates/app/api/v1/billing/checkout/route.ts +1 -1
  266. package/dist/templates/app/api/v1/blocks/validate/route.ts +1 -1
  267. package/dist/templates/app/api/v1/media/[id]/tags/route.ts +1 -1
  268. package/dist/templates/app/api/v1/post-categories/[id]/route.ts +1 -1
  269. package/dist/templates/app/api/v1/post-categories/route.ts +1 -1
  270. package/dist/templates/app/api/v1/teams/[teamId]/members/route.ts +2 -1
  271. package/dist/templates/app/api/v1/teams/[teamId]/subscription/route.ts +3 -5
  272. package/dist/templates/app/api/v1/teams/docs.md +4 -9
  273. package/dist/templates/app/api/v1/teams/switch/route.ts +1 -1
  274. package/dist/templates/app/api/v1/users/[id]/meta/[key]/route.ts +1 -1
  275. package/dist/templates/app/api/v1/users/[id]/route.ts +1 -1
  276. package/dist/templates/app/api/v1/users/route.ts +1 -1
  277. package/dist/templates/app/dashboard/(main)/media/page.tsx +3 -1
  278. package/dist/templates/app/dashboard/settings/api-keys/page.tsx +5 -4
  279. package/dist/templates/app/dashboard/settings/profile/page.tsx +3 -2
  280. package/dist/templates/app/devtools/scheduled-actions/page.tsx +2 -1
  281. package/dist/templates/app/superadmin/docs/[section]/[page]/page.tsx +16 -0
  282. package/dist/templates/app/superadmin/subscriptions/page.tsx +3 -2
  283. package/dist/templates/app/superadmin/teams/[teamId]/page.tsx +5 -4
  284. package/dist/templates/app/superadmin/users/[userId]/page.tsx +2 -1
  285. package/dist/templates/app/superadmin/users/page.tsx +2 -1
  286. package/dist/templates/blocks/cta-section/component.tsx +3 -2
  287. package/dist/templates/blocks/cta-section/schema.ts +1 -1
  288. package/dist/templates/blocks/features-grid/component.tsx +2 -1
  289. package/dist/templates/blocks/features-grid/schema.ts +1 -1
  290. package/dist/templates/blocks/hero/component.tsx +3 -2
  291. package/dist/templates/blocks/hero/schema.ts +1 -1
  292. package/dist/templates/blocks/testimonials/component.tsx +2 -1
  293. package/dist/templates/blocks/testimonials/schema.ts +1 -1
  294. package/dist/templates/blocks/text-content/component.tsx +2 -1
  295. package/dist/templates/blocks/text-content/schema.ts +1 -1
  296. package/dist/templates/ci-workflows/README.md +1 -1
  297. package/dist/templates/ci-workflows/github/cypress-regression.yml +1 -1
  298. package/dist/templates/ci-workflows/github/cypress-smoke.yml +1 -1
  299. package/dist/templates/contents/plugins/starter/plugin/api/example/route.ts.template +1 -1
  300. package/dist/templates/contents/themes/starter/blocks/cta-section/component.tsx +3 -2
  301. package/dist/templates/contents/themes/starter/blocks/cta-section/schema.ts +1 -1
  302. package/dist/templates/contents/themes/starter/blocks/features-grid/component.tsx +2 -1
  303. package/dist/templates/contents/themes/starter/blocks/features-grid/schema.ts +1 -1
  304. package/dist/templates/contents/themes/starter/blocks/hero/component.tsx +3 -2
  305. package/dist/templates/contents/themes/starter/blocks/hero/schema.ts +1 -1
  306. package/dist/templates/contents/themes/starter/blocks/testimonials/component.tsx +3 -2
  307. package/dist/templates/contents/themes/starter/blocks/testimonials/schema.ts +1 -1
  308. package/dist/templates/contents/themes/starter/blocks/text-content/component.tsx +2 -1
  309. package/dist/templates/contents/themes/starter/blocks/text-content/schema.ts +1 -1
  310. package/dist/templates/contents/themes/starter/entities/posts/components/post-header.tsx +2 -1
  311. package/dist/templates/contents/themes/starter/lib/hooks/useAnalyticsData.ts +2 -1
  312. package/dist/templates/contents/themes/starter/lib/hooks/useTaskStats.ts +2 -1
  313. package/dist/templates/eslint.config.mjs +133 -9
  314. package/dist/templates/features/blog/blocks/post-content/component.tsx +2 -1
  315. package/dist/templates/features/blog/blocks/post-content/schema.ts +1 -1
  316. package/dist/templates/features/blog/entities/posts/components/post-header.tsx +2 -1
  317. package/dist/templates/features/pages/blocks/hero/component.tsx +3 -2
  318. package/dist/templates/features/pages/blocks/hero/schema.ts +1 -1
  319. package/dist/templates/next.config.mjs +50 -29
  320. package/dist/templates/proxy.ts +100 -21
  321. package/dist/templates/public/sw.js +2 -2
  322. package/dist/templates/scripts/cy-run-prod.cjs +5 -5
  323. package/dist/types/blocks.d.ts +1 -1
  324. package/dist/types/blocks.d.ts.map +1 -1
  325. package/dist/types/blocks.js +1 -1
  326. package/package.json +9 -5
  327. package/scripts/build/docs-registry.mjs +7 -7
  328. package/scripts/build/registry/__tests__/billing-registry.test.mjs +43 -0
  329. package/scripts/build/registry/__tests__/build-output.test.mjs +209 -0
  330. package/scripts/build/registry/__tests__/copy-race-child.mjs +14 -0
  331. package/scripts/build/registry/__tests__/direct-fs-writes.mjs +203 -0
  332. package/scripts/build/registry/__tests__/globals-css-tag.test.mjs +66 -0
  333. package/scripts/build/registry/__tests__/icons-discovery.test.mjs +530 -29
  334. package/scripts/build/registry/__tests__/page-generator.test.mjs +211 -37
  335. package/scripts/build/registry/__tests__/published-signatures.test.mjs +239 -0
  336. package/scripts/build/registry/__tests__/route-file-rule.test.mjs +170 -0
  337. package/scripts/build/registry/__tests__/safe-fs.test.mjs +333 -0
  338. package/scripts/build/registry/__tests__/template-analysis.test.mjs +142 -0
  339. package/scripts/build/registry/__tests__/template-registry.test.mjs +279 -9
  340. package/scripts/build/registry/__tests__/templates-plan.test.mjs +128 -0
  341. package/scripts/build/registry/__tests__/templates-tree.test.mjs +367 -0
  342. package/scripts/build/registry/__tests__/write-places.test.mjs +425 -0
  343. package/scripts/build/registry/config.mjs +6 -65
  344. package/scripts/build/registry/discovery/icons.mjs +498 -43
  345. package/scripts/build/registry/discovery/templates.mjs +1 -1
  346. package/scripts/build/registry/generators/billing-registry.mjs +1 -1
  347. package/scripts/build/registry/generators/docs-registry.mjs +3 -4
  348. package/scripts/build/registry/generators/feature-registry.mjs +1 -1
  349. package/scripts/build/registry/generators/permissions-registry.mjs +2 -2
  350. package/scripts/build/registry/generators/template-registry.mjs +65 -49
  351. package/scripts/build/registry/index.mjs +1 -2
  352. package/scripts/build/registry/post-build/index.mjs +1 -1
  353. package/scripts/build/registry/post-build/own-gitignores.mjs +220 -0
  354. package/scripts/build/registry/post-build/page-generator.mjs +512 -268
  355. package/scripts/build/registry/post-build/route-cleanup.mjs +16 -13
  356. package/scripts/build/registry/post-build/test-fixtures.mjs +26 -8
  357. package/scripts/build/registry/project-mode.mjs +76 -0
  358. package/scripts/build/registry/shared/typescript-compiler.mjs +77 -0
  359. package/scripts/build/registry/validate-env.test.mjs +12 -8
  360. package/scripts/build/registry/watch.mjs +3 -53
  361. package/scripts/build/registry/write-places.mjs +228 -0
  362. package/scripts/build/registry.mjs +53 -30
  363. package/scripts/build/safe-fs.mjs +279 -0
  364. package/scripts/build/templates-plan.mjs +44 -0
  365. package/scripts/build/theme.mjs +46 -42
  366. package/scripts/build/update-tsconfig.mjs +16 -15
  367. package/scripts/db/cluster-changes.mjs +205 -0
  368. package/scripts/db/connection-time-limits.mjs +53 -0
  369. package/scripts/db/inspect-server.mjs +108 -0
  370. package/scripts/db/migration-time-limit.mjs +695 -0
  371. package/scripts/db/run-migrations.mjs +46 -41
  372. package/scripts/db/ssl-config.mjs +194 -0
  373. package/scripts/db/theme-location.mjs +69 -0
  374. package/scripts/db/verify-tables.mjs +3 -8
  375. package/scripts/db/verify-teams.mjs +2 -6
  376. package/scripts/db/verify-theme-migrations.mjs +169 -0
  377. package/scripts/maintenance/core-updater.mjs +1003 -0
  378. package/scripts/maintenance/step-guard.mjs +74 -0
  379. package/scripts/maintenance/update-core.mjs +14 -831
  380. package/scripts/utils/console-guard.mjs +13 -0
  381. package/scripts/utils/generated-tag.mjs +66 -0
  382. package/scripts/utils/index.mjs +9 -1
  383. package/scripts/utils/logging.mjs +140 -0
  384. package/templates/app/(auth)/accept-invite/[token]/page.tsx +4 -3
  385. package/templates/app/(auth)/verify-email/page.tsx +4 -3
  386. package/templates/app/(public)/docs/[section]/[page]/page.tsx +11 -0
  387. package/templates/app/api/auth/[...all]/route.ts +15 -14
  388. package/templates/app/api/internal/user-metadata/route.ts +6 -8
  389. package/templates/app/api/user/plan-flags/route.ts +1 -1
  390. package/templates/app/api/user/profile/route.ts +12 -17
  391. package/templates/app/api/v1/api-keys/route.ts +1 -1
  392. package/templates/app/api/v1/auth/signup-with-invite/route.ts +2 -1
  393. package/templates/app/api/v1/billing/cancel/route.ts +1 -1
  394. package/templates/app/api/v1/billing/change-plan/route.ts +1 -1
  395. package/templates/app/api/v1/billing/check-action/route.ts +1 -1
  396. package/templates/app/api/v1/billing/checkout/route.ts +1 -1
  397. package/templates/app/api/v1/blocks/validate/route.ts +1 -1
  398. package/templates/app/api/v1/media/[id]/tags/route.ts +1 -1
  399. package/templates/app/api/v1/post-categories/[id]/route.ts +1 -1
  400. package/templates/app/api/v1/post-categories/route.ts +1 -1
  401. package/templates/app/api/v1/teams/[teamId]/members/route.ts +2 -1
  402. package/templates/app/api/v1/teams/[teamId]/subscription/route.ts +3 -5
  403. package/templates/app/api/v1/teams/docs.md +4 -9
  404. package/templates/app/api/v1/teams/switch/route.ts +1 -1
  405. package/templates/app/api/v1/users/[id]/meta/[key]/route.ts +1 -1
  406. package/templates/app/api/v1/users/[id]/route.ts +1 -1
  407. package/templates/app/api/v1/users/route.ts +1 -1
  408. package/templates/app/dashboard/(main)/media/page.tsx +3 -1
  409. package/templates/app/dashboard/settings/api-keys/page.tsx +5 -4
  410. package/templates/app/dashboard/settings/profile/page.tsx +3 -2
  411. package/templates/app/devtools/scheduled-actions/page.tsx +2 -1
  412. package/templates/app/superadmin/docs/[section]/[page]/page.tsx +16 -0
  413. package/templates/app/superadmin/subscriptions/page.tsx +3 -2
  414. package/templates/app/superadmin/teams/[teamId]/page.tsx +5 -4
  415. package/templates/app/superadmin/users/[userId]/page.tsx +2 -1
  416. package/templates/app/superadmin/users/page.tsx +2 -1
  417. package/templates/blocks/cta-section/component.tsx +3 -2
  418. package/templates/blocks/cta-section/schema.ts +1 -1
  419. package/templates/blocks/features-grid/component.tsx +2 -1
  420. package/templates/blocks/features-grid/schema.ts +1 -1
  421. package/templates/blocks/hero/component.tsx +3 -2
  422. package/templates/blocks/hero/schema.ts +1 -1
  423. package/templates/blocks/testimonials/component.tsx +2 -1
  424. package/templates/blocks/testimonials/schema.ts +1 -1
  425. package/templates/blocks/text-content/component.tsx +2 -1
  426. package/templates/blocks/text-content/schema.ts +1 -1
  427. package/templates/ci-workflows/README.md +1 -1
  428. package/templates/ci-workflows/github/cypress-regression.yml +1 -1
  429. package/templates/ci-workflows/github/cypress-smoke.yml +1 -1
  430. package/templates/contents/plugins/starter/plugin/api/example/route.ts.template +1 -1
  431. package/templates/contents/themes/starter/blocks/cta-section/component.tsx +3 -2
  432. package/templates/contents/themes/starter/blocks/cta-section/schema.ts +1 -1
  433. package/templates/contents/themes/starter/blocks/features-grid/component.tsx +2 -1
  434. package/templates/contents/themes/starter/blocks/features-grid/schema.ts +1 -1
  435. package/templates/contents/themes/starter/blocks/hero/component.tsx +3 -2
  436. package/templates/contents/themes/starter/blocks/hero/schema.ts +1 -1
  437. package/templates/contents/themes/starter/blocks/testimonials/component.tsx +3 -2
  438. package/templates/contents/themes/starter/blocks/testimonials/schema.ts +1 -1
  439. package/templates/contents/themes/starter/blocks/text-content/component.tsx +2 -1
  440. package/templates/contents/themes/starter/blocks/text-content/schema.ts +1 -1
  441. package/templates/contents/themes/starter/entities/posts/components/post-header.tsx +2 -1
  442. package/templates/contents/themes/starter/lib/hooks/useAnalyticsData.ts +2 -1
  443. package/templates/contents/themes/starter/lib/hooks/useTaskStats.ts +2 -1
  444. package/templates/eslint.config.mjs +133 -9
  445. package/templates/features/blog/blocks/post-content/component.tsx +2 -1
  446. package/templates/features/blog/blocks/post-content/schema.ts +1 -1
  447. package/templates/features/blog/entities/posts/components/post-header.tsx +2 -1
  448. package/templates/features/pages/blocks/hero/component.tsx +3 -2
  449. package/templates/features/pages/blocks/hero/schema.ts +1 -1
  450. package/templates/next.config.mjs +50 -29
  451. package/templates/proxy.ts +100 -21
  452. package/templates/public/sw.js +2 -2
  453. package/templates/scripts/cy-run-prod.cjs +5 -5
  454. package/tests/jest/__mocks__/@nextsparkjs/registries/template-registry.client.ts +11 -0
  455. package/tests/jest/__mocks__/next-server.js +10 -0
  456. package/scripts/build/docs.mjs +0 -392
@@ -1,4 +1,4 @@
1
- import { z } from 'zod'
1
+ import * as z from 'zod'
2
2
  import { baseBlockSchema } from '@nextsparkjs/core/types/blocks'
3
3
 
4
4
  /**
@@ -1,4 +1,5 @@
1
1
  import { Badge } from '@nextsparkjs/core/components/ui/badge'
2
+ import { withBasePathIfInApp } from '@nextsparkjs/core/lib/base-path'
2
3
  import Image from 'next/image'
3
4
 
4
5
  interface Category {
@@ -24,7 +25,7 @@ export function PostHeader({ post }: PostHeaderProps) {
24
25
  {post.featuredImage && (
25
26
  <div className="relative w-full h-[400px] md:h-[500px] mb-8" data-cy="post-featured-image-display">
26
27
  <Image
27
- src={post.featuredImage}
28
+ src={withBasePathIfInApp(post.featuredImage)}
28
29
  alt={post.title}
29
30
  fill
30
31
  className="object-cover rounded-lg"
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { useQuery } from '@tanstack/react-query'
4
4
  import { useTeamContext } from '@nextsparkjs/core/contexts/TeamContext'
5
+ import { withBasePath } from '@nextsparkjs/core/lib/base-path'
5
6
 
6
7
  interface WeeklyProgress {
7
8
  day: string
@@ -31,7 +32,7 @@ export function useAnalyticsData() {
31
32
  return useQuery({
32
33
  queryKey: ['analytics', currentTeam?.id],
33
34
  queryFn: async (): Promise<AnalyticsData> => {
34
- const res = await fetch(`/api/v1/analytics?teamId=${currentTeam?.id}`)
35
+ const res = await fetch(withBasePath(`/api/v1/analytics?teamId=${currentTeam?.id}`))
35
36
  if (!res.ok) throw new Error('Failed to fetch analytics')
36
37
  return res.json()
37
38
  },
@@ -4,6 +4,7 @@ import { useMemo } from 'react'
4
4
  import { useQuery } from '@tanstack/react-query'
5
5
  import { useTeamContext } from '@nextsparkjs/core/contexts/TeamContext'
6
6
  import { calculateTaskStats, type TaskStats } from '../utils'
7
+ import { withBasePath } from '@nextsparkjs/core/lib/base-path'
7
8
 
8
9
  interface Task {
9
10
  id: string
@@ -32,7 +33,7 @@ export function useTaskStats(): UseTaskStatsReturn {
32
33
  } = useQuery({
33
34
  queryKey: ['tasks', currentTeam?.id, 'stats'],
34
35
  queryFn: async (): Promise<Task[]> => {
35
- const response = await fetch(`/api/v1/tasks?teamId=${currentTeam?.id}&limit=1000`)
36
+ const response = await fetch(withBasePath(`/api/v1/tasks?teamId=${currentTeam?.id}&limit=1000`))
36
37
  if (!response.ok) {
37
38
  throw new Error('Failed to fetch tasks')
38
39
  }
@@ -1,16 +1,140 @@
1
- import { dirname } from "path";
2
- import { fileURLToPath } from "url";
3
- import { FlatCompat } from "@eslint/eslintrc";
1
+ import { createRequire } from "node:module";
2
+ import { dirname } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
4
 
5
- const __filename = fileURLToPath(import.meta.url);
6
- const __dirname = dirname(__filename);
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+ const require = createRequire(import.meta.url);
7
7
 
8
- const compat = new FlatCompat({
9
- baseDirectory: __dirname,
10
- });
8
+ /**
9
+ * Next's presets from the eslint-config-next the project installs, as flat config.
10
+ *
11
+ * Version 16 publishes flat config arrays on these subpaths; reaching them through FlatCompat
12
+ * throws `Converting circular structure to JSON` before a single file is read. Version 15 declares
13
+ * no `exports` and publishes eslintrc presets, which FlatCompat is the only way to read: importing
14
+ * the subpaths throws ERR_MODULE_NOT_FOUND, or, under a loader that adds file extensions, returns
15
+ * eslintrc objects. That is what a Next 15 project gets when `nextspark init` drops this file next
16
+ * to its eslint-config-next. FlatCompat ships in @eslint/eslintrc, which ESLint itself depends on,
17
+ * so it is resolved from there.
18
+ */
19
+ async function nextPresets() {
20
+ let presets;
21
+ try {
22
+ presets = await Promise.all([
23
+ import("eslint-config-next/core-web-vitals"),
24
+ import("eslint-config-next/typescript"),
25
+ ]);
26
+ } catch (error) {
27
+ if (error?.code !== "ERR_MODULE_NOT_FOUND") throw error;
28
+ }
29
+
30
+ if (presets?.every((preset) => Array.isArray(preset.default))) {
31
+ return presets.flatMap((preset) => preset.default);
32
+ }
33
+
34
+ const { FlatCompat } = createRequire(require.resolve("eslint"))("@eslint/eslintrc");
35
+ return new FlatCompat({ baseDirectory: __dirname }).extends("next/core-web-vitals", "next/typescript");
36
+ }
37
+
38
+ /** A config object holding nothing but `ignores` applies to every other object in the array. */
39
+ const isGlobalIgnore = (config) =>
40
+ Object.keys(config).every((key) => key === "ignores" || key === "name");
41
+
42
+ const presets = await nextPresets();
43
+
44
+ /** typescript-eslint's parser, which reads every syntax these trees use: JS, TS and JSX. */
45
+ const typescriptParser = presets
46
+ .map((config) => config.languageOptions?.parser)
47
+ .find((parser) => parser?.meta?.name === "typescript-eslint/parser");
48
+
49
+ const SOURCE_FILES = "**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}";
50
+
51
+ // Tests and fixtures, the project's own and those every theme and plugin brings under contents/:
52
+ // they mock, stub and cast by design, so Next's presets skip them and only the zod rule reaches
53
+ // them. Everything else -- app/, contents/ and what the project adds -- gets the presets.
54
+ const TEST_FILES = [
55
+ "**/tests/**",
56
+ "**/__tests__/**",
57
+ "**/__mocks__/**",
58
+ "**/fixtures/**",
59
+ "**/cypress/**",
60
+ "**/*.{test,spec,cy}.{ts,tsx,mts,cts,js,jsx,mjs,cjs}",
61
+ "**/{jest,cypress}.{config,setup}.{ts,mts,cts,js,mjs,cjs}",
62
+ ];
63
+
64
+ /** "error", 2 or ["error", ...]. */
65
+ const isError = (setting) => [setting, setting?.[0]].some((level) => level === "error" || level === 2);
66
+
67
+ /**
68
+ * The severity this project gives the rules Next's presets configure.
69
+ *
70
+ * `@next/next/no-img-element` is an error: the presets only warn, which leaves `eslint .` passing,
71
+ * and next/image is what serves an image resized and in a modern format.
72
+ *
73
+ * eslint-plugin-react-hooks 7, which Next 16's presets load, adds to rules-of-hooks and
74
+ * exhaustive-deps the rules that flag code the React Compiler cannot compile. A NextSpark project
75
+ * does not build with the React Compiler, and the components NextSpark ships in app/ and its themes
76
+ * and plugins break several of those rules, so they report as warnings. Version 5, which Next 15
77
+ * loads, has none of them.
78
+ */
79
+ function projectSeverity(rule, setting) {
80
+ if (rule === "@next/next/no-img-element") return "error";
81
+ const compilerRule = rule.startsWith("react-hooks/") && !["react-hooks/rules-of-hooks", "react-hooks/exhaustive-deps"].includes(rule);
82
+ return compilerRule && isError(setting) ? "warn" : setting;
83
+ }
11
84
 
12
85
  const eslintConfig = [
13
- ...compat.extends("next/core-web-vitals", "next/typescript"),
86
+ {
87
+ // Next writes next-env.d.ts itself; version 16's presets ignore it and version 15's do not.
88
+ ignores: [".next/**", ".nextspark/**", "next-env.d.ts"],
89
+ },
90
+ ...presets.map((config) =>
91
+ isGlobalIgnore(config)
92
+ ? config
93
+ : {
94
+ ...config,
95
+ ignores: [...(config.ignores ?? []), ...TEST_FILES],
96
+ ...(config.rules && {
97
+ rules: Object.fromEntries(Object.entries(config.rules).map(([rule, setting]) => [rule, projectSeverity(rule, setting)])),
98
+ }),
99
+ },
100
+ ),
101
+ {
102
+ // A .cjs file is CommonJS by definition, so `require` is the only spelling it has.
103
+ files: ["**/*.cjs"],
104
+ ignores: TEST_FILES,
105
+ rules: { "@typescript-eslint/no-require-imports": "off" },
106
+ },
107
+ {
108
+ // The presets would have supplied the parser here. Their plugins are registered so the disable
109
+ // comments in tests resolve the rules they name, without turning any of those rules on.
110
+ files: TEST_FILES,
111
+ languageOptions: {
112
+ parser: typescriptParser,
113
+ parserOptions: { ecmaFeatures: { jsx: true } },
114
+ },
115
+ plugins: Object.assign({}, ...presets.map((config) => config.plugins ?? {})),
116
+ linterOptions: { reportUnusedDisableDirectives: "off" },
117
+ },
118
+ {
119
+ // Spelled out rather than inherited: a block naming these extensions is what makes ESLint lint
120
+ // a .ts file at all.
121
+ files: [SOURCE_FILES],
122
+ rules: {
123
+ // `no-restricted-imports` with `importNames: ["z"]` also rejects `import * as z`, so the
124
+ // named specifier is matched by syntax instead.
125
+ "no-restricted-syntax": [
126
+ "error",
127
+ {
128
+ // `imported` is an Identifier for `{ z }` and a Literal for `{ "z" as zod }`, so the
129
+ // name lives under a different property in each.
130
+ selector:
131
+ "ImportDeclaration[source.value='zod'] > ImportSpecifier:matches([imported.name='z'], [imported.value='z']), ExportNamedDeclaration[source.value='zod'] > ExportSpecifier:matches([local.name='z'], [local.value='z'])",
132
+ message:
133
+ "Use `import * as z from 'zod'`. Turbopack doesn't tree-shake the named `z` import and bundles all of zod, its 63 locales included.",
134
+ },
135
+ ],
136
+ },
137
+ },
14
138
  ];
15
139
 
16
140
  export default eslintConfig;
@@ -4,6 +4,7 @@ import { buildSectionClasses } from '@nextsparkjs/core/types/blocks'
4
4
  import { sanitizeBlockHtml } from '@nextsparkjs/core/lib/blocks/sanitize-html'
5
5
  import { sel } from '../../lib/selectors'
6
6
  import type { PostContentBlockProps } from './schema'
7
+ import { withBasePathIfInApp } from '@nextsparkjs/core/lib/base-path'
7
8
 
8
9
  /**
9
10
  * Post Content Block Component
@@ -182,7 +183,7 @@ export function PostContentBlock({
182
183
  {cta && (
183
184
  <div className={cn('mt-12 text-center', maxWidthClasses[maxWidth], 'mx-auto')}>
184
185
  <a
185
- href={cta.link}
186
+ href={withBasePathIfInApp(cta.link)}
186
187
  target={cta.target}
187
188
  rel={cta.target === '_blank' ? 'noopener noreferrer' : undefined}
188
189
  className="inline-flex items-center justify-center rounded-md bg-primary px-8 py-3 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors shadow-md hover:shadow-lg"
@@ -1,4 +1,4 @@
1
- import { z } from 'zod'
1
+ import * as z from 'zod'
2
2
  import { baseBlockSchema } from '@nextsparkjs/core/types/blocks'
3
3
 
4
4
  /**
@@ -1,4 +1,5 @@
1
1
  import { Badge } from '@nextsparkjs/core/components/ui/badge'
2
+ import { withBasePathIfInApp } from '@nextsparkjs/core/lib/base-path'
2
3
  import Image from 'next/image'
3
4
 
4
5
  interface Category {
@@ -24,7 +25,7 @@ export function PostHeader({ post }: PostHeaderProps) {
24
25
  {post.featuredImage && (
25
26
  <div className="relative w-full h-[400px] md:h-[500px] mb-8" data-cy="post-featured-image-display">
26
27
  <Image
27
- src={post.featuredImage}
28
+ src={withBasePathIfInApp(post.featuredImage)}
28
29
  alt={post.title}
29
30
  fill
30
31
  className="object-cover rounded-lg"
@@ -4,6 +4,7 @@ import { cn } from '@nextsparkjs/core/lib/utils'
4
4
  import { buildSectionClasses, resolveMediaUrl } from '@nextsparkjs/core/types/blocks'
5
5
  import { sel } from '../../lib/selectors'
6
6
  import type { HeroBlockProps } from './schema'
7
+ import { withBasePathIfInApp } from '@nextsparkjs/core/lib/base-path'
7
8
 
8
9
  /**
9
10
  * Hero Block Component
@@ -71,7 +72,7 @@ export function HeroBlock({
71
72
  <div
72
73
  className="absolute inset-0 z-0"
73
74
  style={{
74
- backgroundImage: `url(${backgroundImageUrl})`,
75
+ backgroundImage: `url(${withBasePathIfInApp(backgroundImageUrl)})`,
75
76
  backgroundSize: 'cover',
76
77
  backgroundPosition: 'center',
77
78
  }}
@@ -100,7 +101,7 @@ export function HeroBlock({
100
101
  {ctaConfig && (
101
102
  <Button asChild size="lg" className="text-lg px-8 py-6">
102
103
  <a
103
- href={ctaConfig.link}
104
+ href={withBasePathIfInApp(ctaConfig.link)}
104
105
  target={ctaConfig.target}
105
106
  rel={ctaConfig.target === '_blank' ? 'noopener noreferrer' : undefined}
106
107
  data-cy={sel('blocks.hero.cta')}
@@ -1,4 +1,4 @@
1
- import { z } from 'zod'
1
+ import * as z from 'zod'
2
2
  import {
3
3
  baseBlockSchema,
4
4
  mediaRefSchema,
@@ -8,8 +8,52 @@ const __dirname = path.dirname(__filename);
8
8
  // For npm mode, use local i18n.ts that re-exports from @nextsparkjs/core
9
9
  const withNextIntl = createNextIntlPlugin('./i18n.ts');
10
10
 
11
+ // The app's Next.js basePath. Next.js adds it to routes and to <Link>, and to
12
+ // nothing a header names: the CSP report endpoints below are URLs the browser
13
+ // resolves against the origin, so they carry it from here.
14
+ const basePath = '';
15
+
16
+ /**
17
+ * Silences the Node built-ins that client bundles reach transitively, and points
18
+ * `@nextsparkjs/registries` at the generated directory.
19
+ *
20
+ * Only webpack reads this, and either major can build with it: Next 15 by
21
+ * default, Next 16 with `--webpack`. Turbopack resolves `@nextsparkjs/registries`
22
+ * through the `paths` of tsconfig.json, which Next's webpack skips for imports
23
+ * made from inside node_modules, as core's are, and leaves a Node built-in alone
24
+ * unless client code imports it for real.
25
+ */
26
+ const applyWebpackFallbacks = (config, { isServer }) => {
27
+ if (!isServer) {
28
+ config.resolve.fallback = {
29
+ ...config.resolve.fallback,
30
+ fs: false,
31
+ net: false,
32
+ tls: false,
33
+ crypto: false,
34
+ path: false,
35
+ os: false,
36
+ stream: false,
37
+ http: false,
38
+ https: false,
39
+ zlib: false,
40
+ dns: false,
41
+ }
42
+ }
43
+
44
+ // Add alias for @nextsparkjs/registries to fix ChunkLoadError
45
+ config.resolve.alias = {
46
+ ...config.resolve.alias,
47
+ 'pg-native': false,
48
+ '@nextsparkjs/registries': path.resolve(__dirname, '.nextspark/registries'),
49
+ }
50
+
51
+ return config
52
+ }
53
+
11
54
  /** @type {import('next').NextConfig} */
12
55
  const nextConfig = {
56
+ basePath,
13
57
  // Ignore TypeScript build errors - needed until DTS generation is enabled for @nextsparkjs/core
14
58
  // Without this, production builds fail due to missing declaration files for deep imports
15
59
  typescript: {
@@ -81,33 +125,10 @@ const nextConfig = {
81
125
  },
82
126
  ],
83
127
  },
84
- webpack: (config, { isServer }) => {
85
- if (!isServer) {
86
- config.resolve.fallback = {
87
- ...config.resolve.fallback,
88
- fs: false,
89
- net: false,
90
- tls: false,
91
- crypto: false,
92
- path: false,
93
- os: false,
94
- stream: false,
95
- http: false,
96
- https: false,
97
- zlib: false,
98
- dns: false,
99
- }
100
- }
101
-
102
- // Add alias for @nextsparkjs/registries to fix ChunkLoadError
103
- config.resolve.alias = {
104
- ...config.resolve.alias,
105
- 'pg-native': false,
106
- '@nextsparkjs/registries': path.resolve(__dirname, '.nextspark/registries'),
107
- }
108
-
109
- return config
110
- },
128
+ // Next.js sets TURBOPACK before it loads this file whenever Turbopack builds
129
+ // (on 15 with --turbopack, on 16 unless given --webpack), and next-intl picks
130
+ // its own webpack or Turbopack setup from the same variable.
131
+ ...(process.env.TURBOPACK ? {} : { webpack: applyWebpackFallbacks }),
111
132
  async headers() {
112
133
  const isProduction = process.env.NODE_ENV === 'production';
113
134
 
@@ -158,7 +179,7 @@ const nextConfig = {
158
179
  // CSP violation reporting - sends violations to /api/csp-report
159
180
  // report-uri is deprecated but has wider browser support
160
181
  // report-to is the modern replacement (configured via Reporting-Endpoints header)
161
- "report-uri /api/csp-report",
182
+ `report-uri ${basePath}/api/csp-report`,
162
183
  "report-to csp-endpoint",
163
184
  ];
164
185
 
@@ -167,7 +188,7 @@ const nextConfig = {
167
188
  // Reporting API endpoint for modern browsers (used by report-to CSP directive)
168
189
  {
169
190
  key: 'Reporting-Endpoints',
170
- value: 'csp-endpoint="/api/csp-report"'
191
+ value: `csp-endpoint="${basePath}/api/csp-report"`
171
192
  },
172
193
  {
173
194
  key: 'X-Content-Type-Options',
@@ -1,9 +1,8 @@
1
1
  /**
2
- * @nextspark-generated
3
- *
4
- * `nextspark sync:app` replaces this file while that tag is present, which is
5
- * how a release ships changes to it. Delete the tag line to own the file: from
6
- * then on sync reports it and leaves it alone.
2
+ * `nextspark sync:app` keeps this file up to date while its first line is the
3
+ * generated tag sync writes and the rest of the file is unchanged since then,
4
+ * which is how a release ships changes to it. Edit the file, or delete that
5
+ * line, to own it: from then on sync reports it and leaves it alone.
7
6
  *
8
7
  * NextSpark Proxy (Next.js 16+)
9
8
  *
@@ -12,9 +11,10 @@
12
11
  *
13
12
  * Key responsibilities:
14
13
  * 1. Theme middleware override support
15
- * 2. Documentation access control
16
- * 3. Protected route authentication, and the roles /superadmin and /devtools need
17
- * 4. User header injection for downstream use (x-user-id, x-pathname, x-active-team-id)
14
+ * 2. Redirecting historical 3-level docs URLs to their current route
15
+ * 3. Documentation access control
16
+ * 4. Protected route authentication, and the roles /superadmin and /devtools need
17
+ * 5. User header injection for downstream use (x-user-id, x-pathname, x-active-team-id)
18
18
  *
19
19
  * IMPORTANT: The EntityPermissionLayout depends on x-user-id and x-pathname
20
20
  * headers being set here for server-side permission validation.
@@ -28,6 +28,8 @@ import {
28
28
  } from '@nextsparkjs/core/lib/middleware'
29
29
  import { ACTIVE_TEAM_COOKIE, activeTeamIdForSession } from '@nextsparkjs/core/lib/teams/active-team-cookie'
30
30
  import { SESSION_HINT_COOKIE, SESSION_HINT_MAX_AGE, hasSessionCookie } from '@nextsparkjs/core/lib/auth/session-hint'
31
+ import { isDocsPublic, legacyDocsAccessMessage } from '@nextsparkjs/core/lib/docs/access'
32
+ import { DOCS_REGISTRY } from '@nextsparkjs/registries/docs-registry'
31
33
 
32
34
  /**
33
35
  * Session type for proxy (inline definition)
@@ -91,6 +93,45 @@ function appUrl(request: NextRequest, pathname: string): NextRequest['nextUrl']
91
93
  return url
92
94
  }
93
95
 
96
+ /**
97
+ * Whether `pathname` is a docs page route — `/docs/<section>/<page>` or
98
+ * `/superadmin/docs/<section>/<page>` — whose section and page the docs
99
+ * registry does not have together. Segments are compared decoded, the way the
100
+ * page reads its params.
101
+ */
102
+ function isMissingDocsPage(pathname: string): boolean {
103
+ const match = pathname.match(/^(\/superadmin)?\/docs\/([^/]+)\/([^/]+)$/)
104
+ if (!match) return false
105
+
106
+ let sectionSlug: string
107
+ let pageSlug: string
108
+ try {
109
+ sectionSlug = decodeURIComponent(match[2])
110
+ pageSlug = decodeURIComponent(match[3])
111
+ } catch {
112
+ return true
113
+ }
114
+
115
+ const sections = match[1] ? DOCS_REGISTRY.superadmin : DOCS_REGISTRY.public
116
+ return !sections.some(section => section.slug === sectionSlug && section.pages.some(page => page.slug === pageSlug))
117
+ }
118
+
119
+ /**
120
+ * Serve the app's not-found page with a 404 status for a docs page the
121
+ * registry does not have. The docs pages answer 404 on their own only where
122
+ * their `dynamicParams = false` has a list to check: in `next dev`, and for
123
+ * public docs prerendered by an app whose locale is fixed. Rendered on demand
124
+ * (superadmin docs always, public docs when the locale comes from the
125
+ * request), their notFound() runs inside the layouts' Suspense boundaries,
126
+ * after the response head has gone out with a 200. Deciding it here, before
127
+ * anything renders, gives those requests the 404.
128
+ */
129
+ function rewriteToNotFound(request: NextRequest, requestHeaders: Headers): NextResponse {
130
+ return syncSessionHint(request, NextResponse.rewrite(appUrl(request, '/_not-found'), {
131
+ request: { headers: requestHeaders },
132
+ }))
133
+ }
134
+
94
135
  /**
95
136
  * Send a visitor without a session to login. LoginForm reads `callbackUrl` and
96
137
  * returns them to the page they asked for, query included, once signed in.
@@ -178,6 +219,18 @@ function passThrough(request: NextRequest, requestHeaders: Headers): NextRespons
178
219
  }))
179
220
  }
180
221
 
222
+ let legacyDocsAccessWarned = false
223
+
224
+ /** Says once per server process what to change in an app config that still
225
+ * gates /docs with the older `docs.public` boolean. */
226
+ function warnLegacyDocsAccess(docsConfig: Parameters<typeof legacyDocsAccessMessage>[0]): void {
227
+ if (legacyDocsAccessWarned) return
228
+ const message = legacyDocsAccessMessage(docsConfig)
229
+ if (!message) return
230
+ legacyDocsAccessWarned = true
231
+ console.warn(`[NextSpark] ${message}`)
232
+ }
233
+
181
234
  export async function proxy(request: NextRequest) {
182
235
  const { pathname } = request.nextUrl
183
236
 
@@ -191,36 +244,58 @@ export async function proxy(request: NextRequest) {
191
244
  if (themeResponse) return themeResponse
192
245
  }
193
246
 
194
- // 2. Redirect old docs URLs to new structure (2-level -> 3-level)
195
- const oldDocsPattern = /^\/docs\/([^\/]+)\/([^\/]+)$/
196
- const oldDocsMatch = pathname.match(oldDocsPattern)
197
-
247
+ // 2. Redirect historical 3-level docs URLs (/docs/<core|theme>/<section>/<page>)
248
+ // to the current route. That split predates this app's history: `core` and
249
+ // `theme` were the only two categories ever generated (a third, `plugins`,
250
+ // was scaffolded but never activated), and both were collapsed into a single
251
+ // docs tree per source - `public` (served at /docs) and `superadmin` (served
252
+ // at /superadmin/docs) - without renaming any section that survived the
253
+ // collapse. A section from that era can therefore only be in one of the two
254
+ // registries today, or in neither if its docs were dropped rather than
255
+ // moved; the redirect follows the section, not the old category. The page
256
+ // has to survive too - a section can live on with a different set of pages -
257
+ // so both are checked against the registry rather than only the section,
258
+ // which is the one thing this route can actually confirm still resolves.
259
+ const oldDocsMatch = pathname.match(/^\/docs\/(?:core|theme)\/([^/]+)\/([^/]+)$/)
198
260
  if (oldDocsMatch) {
199
261
  const [, sectionSlug, pageSlug] = oldDocsMatch
200
- const themeSections = ['theme-overview', 'theme-features']
201
- const category = themeSections.includes(sectionSlug) ? 'theme' : 'core'
202
- const cleanSection = sectionSlug.replace(/^theme-/, '')
203
-
204
262
  const newUrl = request.nextUrl.clone()
205
- newUrl.pathname = `/docs/${category}/${cleanSection}/${pageSlug}`
263
+ const superadminSection = DOCS_REGISTRY.superadmin.find((section) => section.slug === sectionSlug)
264
+ const publicSection = DOCS_REGISTRY.public.find((section) => section.slug === sectionSlug)
265
+
266
+ if (superadminSection?.pages.some((page) => page.slug === pageSlug)) {
267
+ newUrl.pathname = `/superadmin/docs/${sectionSlug}/${pageSlug}`
268
+ } else if (publicSection?.pages.some((page) => page.slug === pageSlug)) {
269
+ newUrl.pathname = `/docs/${sectionSlug}/${pageSlug}`
270
+ } else {
271
+ // The section is gone, or survived without this exact page - either way
272
+ // there is no page left to send this to, so it goes to the docs home
273
+ // instead of a dead link.
274
+ newUrl.pathname = '/docs'
275
+ }
206
276
  return NextResponse.redirect(newUrl, 301)
207
277
  }
208
278
 
209
- // 3. Documentation access control
279
+ // 3. Documentation access control: docs.publicAccess, or the older
280
+ // docs.public boolean it replaced (see lib/docs/access)
210
281
  if (isUnder(pathname, '/docs')) {
211
- const appConfig = getThemeAppConfig(activeTheme as string)
282
+ const docsConfig = getThemeAppConfig(activeTheme as string)?.docs
283
+ warnLegacyDocsAccess(docsConfig)
212
284
 
213
- if (appConfig?.docs?.public === false) {
285
+ if (!isDocsPublic(docsConfig)) {
214
286
  try {
215
287
  const { data: session } = await getSession(request)
216
288
 
217
289
  if (!session) {
218
290
  return redirectToLogin(request)
219
291
  }
220
- } catch (error) {
292
+ } catch {
221
293
  return redirectToLogin(request)
222
294
  }
223
295
  }
296
+ if (isMissingDocsPage(pathname)) {
297
+ return rewriteToNotFound(request, requestHeaders)
298
+ }
224
299
  return passThrough(request, requestHeaders)
225
300
  }
226
301
 
@@ -290,6 +365,10 @@ export async function proxy(request: NextRequest) {
290
365
  requestHeaders.set('x-active-team-id', activeTeamId)
291
366
  }
292
367
 
368
+ if (isSuperadminRoute && isMissingDocsPage(pathname)) {
369
+ return rewriteToNotFound(request, requestHeaders)
370
+ }
371
+
293
372
  return passThrough(request, requestHeaders)
294
373
  } catch (error) {
295
374
  console.error('Proxy error:', error)
@@ -1,10 +1,10 @@
1
1
  // Empty service worker to prevent 404 errors
2
2
  // This file exists only to stop browsers from requesting a non-existent service worker
3
3
 
4
- self.addEventListener('install', function(event) {
4
+ self.addEventListener('install', function() {
5
5
  // No caching or special behavior
6
6
  });
7
7
 
8
- self.addEventListener('fetch', function(event) {
8
+ self.addEventListener('fetch', function() {
9
9
  // No fetch interception
10
10
  });
@@ -83,7 +83,7 @@ function killPort(port) {
83
83
  if (pid && /^\d+$/.test(pid)) {
84
84
  try {
85
85
  execSync(`taskkill /PID ${pid} /F`, { stdio: 'ignore' })
86
- } catch (e) {
86
+ } catch {
87
87
  // Process might already be dead
88
88
  }
89
89
  }
@@ -92,7 +92,7 @@ function killPort(port) {
92
92
  execSync(`lsof -ti:${port} | xargs kill -9 2>/dev/null || true`, { stdio: 'ignore' })
93
93
  }
94
94
  console.log(`[cy:run:prod] Port ${port} cleared`)
95
- } catch (e) {
95
+ } catch {
96
96
  // No process on port, that's fine
97
97
  console.log(`[cy:run:prod] Port ${port} was already free`)
98
98
  }
@@ -206,16 +206,16 @@ function cleanup(server) {
206
206
  // Kill process group
207
207
  try {
208
208
  process.kill(-server.pid, 'SIGKILL')
209
- } catch (e) {
209
+ } catch {
210
210
  // If process group kill fails, try direct kill
211
211
  try {
212
212
  server.kill('SIGKILL')
213
- } catch (e2) {
213
+ } catch {
214
214
  // Process might already be dead
215
215
  }
216
216
  }
217
217
  }
218
- } catch (e) {
218
+ } catch {
219
219
  // Process might already be dead
220
220
  }
221
221
  }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Mock Template Registry (client) for Jest tests
3
+ *
4
+ * No theme overrides a page in tests, so every page renders its default.
5
+ */
6
+
7
+ import type { ComponentType } from 'react'
8
+
9
+ export function getTemplateOrDefaultClient<P>(_path: string, defaultComponent: ComponentType<P>): ComponentType<P> {
10
+ return defaultComponent
11
+ }
@@ -98,6 +98,16 @@ class MockNextResponse {
98
98
  return response
99
99
  }
100
100
 
101
+ // Serves another URL of the app in place of the requested one, with the
102
+ // forwarded request headers kept for assertions like next() above.
103
+ static rewrite(url, init = {}) {
104
+ const response = new MockNextResponse(null, { status: 200, headers: init.headers })
105
+ response.type = 'rewrite'
106
+ response.rewriteUrl = String(url)
107
+ response.requestHeaders = init.request && init.request.headers ? init.request.headers : null
108
+ return response
109
+ }
110
+
101
111
  static redirect(url, status = 307) {
102
112
  const response = new MockNextResponse(null, { status, headers: { Location: String(url) } })
103
113
  response.type = 'redirect'