@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
@@ -13,11 +13,15 @@
13
13
 
14
14
  import { test } from 'node:test'
15
15
  import assert from 'node:assert/strict'
16
+ import { mkdtemp, mkdir, writeFile, rm } from 'node:fs/promises'
17
+ import { tmpdir } from 'node:os'
18
+ import { join, dirname } from 'node:path'
16
19
 
17
20
  import {
18
21
  generateTemplateRegistry,
19
22
  generateTemplateRegistryClient
20
23
  } from '../generators/template-registry.mjs'
24
+ import { analyzeTemplates } from '../post-build/page-generator.mjs'
21
25
 
22
26
  const pageTemplate = {
23
27
  name: '(public)/page',
@@ -42,8 +46,18 @@ const protectedTemplate = {
42
46
 
43
47
  const config = { outputDir: '/tmp/registries', projectRoot: '/tmp/project' }
44
48
 
45
- test('server registry defers the import instead of hoisting a static one', () => {
46
- const out = generateTemplateRegistry([pageTemplate], config)
49
+ // The registry build parses the templates once, with analyzeTemplates, and
50
+ // hands the same analysis to both registries. These do the same.
51
+ async function generateServerRegistry(templates, config) {
52
+ return generateTemplateRegistry(templates, config, await analyzeTemplates(templates, config))
53
+ }
54
+
55
+ async function generateClientRegistry(templates, config) {
56
+ return generateTemplateRegistryClient(templates, config, await analyzeTemplates(templates, config))
57
+ }
58
+
59
+ test('server registry defers the import instead of hoisting a static one', async () => {
60
+ const out = await generateServerRegistry([pageTemplate], config)
47
61
 
48
62
  assert.match(
49
63
  out,
@@ -52,8 +66,8 @@ test('server registry defers the import instead of hoisting a static one', () =>
52
66
  assert.doesNotMatch(out, /^import Template_\d+ from/m)
53
67
  })
54
68
 
55
- test('names the template in the error when it has no default export', () => {
56
- const out = generateTemplateRegistry([pageTemplate], config)
69
+ test('names the template in the error when it has no default export', async () => {
70
+ const out = await generateServerRegistry([pageTemplate], config)
57
71
 
58
72
  // Without the module loaded up front, React would otherwise fail with a bare
59
73
  // "invalid element type" and no clue which template is at fault
@@ -61,8 +75,8 @@ test('names the template in the error when it has no default export', () => {
61
75
  assert.match(out, /if \(!templateModule\.default\)/)
62
76
  })
63
77
 
64
- test('server registry loads templates without a Suspense boundary', () => {
65
- const out = generateTemplateRegistry([pageTemplate], config)
78
+ test('server registry loads templates without a Suspense boundary', async () => {
79
+ const out = await generateServerRegistry([pageTemplate], config)
66
80
 
67
81
  // An async Server Component awaits inline; next/dynamic would wrap the
68
82
  // template in Suspense and break notFound()'s hold on the status code.
@@ -70,15 +84,15 @@ test('server registry loads templates without a Suspense boundary', () => {
70
84
  assert.doesNotMatch(out, /^import .* from 'next\/dynamic'/m)
71
85
  })
72
86
 
73
- test('metadata-only templates still carry no component', () => {
74
- const out = generateTemplateRegistry([protectedTemplate], config)
87
+ test('metadata-only templates still carry no component', async () => {
88
+ const out = await generateServerRegistry([protectedTemplate], config)
75
89
 
76
90
  assert.match(out, /component: null/)
77
91
  assert.doesNotMatch(out, /layout\.meta'\)\)/)
78
92
  })
79
93
 
80
94
  test('client registry defers the import through next/dynamic', async () => {
81
- const out = await generateTemplateRegistryClient([pageTemplate], config)
95
+ const out = await generateClientRegistry([pageTemplate], config)
82
96
 
83
97
  assert.match(out, /import dynamic from 'next\/dynamic'/)
84
98
  assert.match(
@@ -87,3 +101,259 @@ test('client registry defers the import through next/dynamic', async () => {
87
101
  )
88
102
  assert.doesNotMatch(out, /^import ClientTemplate_\d+ from/m)
89
103
  })
104
+
105
+ // --- a template file with no default export registers as metadata-only (#197) --
106
+
107
+ async function writeThemeTemplate(root, relativePath, content) {
108
+ const absolutePath = join(root, 'contents/themes/testtheme/templates', relativePath)
109
+ await mkdir(dirname(absolutePath), { recursive: true })
110
+ await writeFile(absolutePath, content, 'utf8')
111
+ return `@/contents/themes/testtheme/templates/${relativePath}`
112
+ }
113
+
114
+ async function writeAppRoute(root, appPath, content = 'export default function Page() { return null }\n') {
115
+ const absolutePath = join(root, appPath)
116
+ await mkdir(dirname(absolutePath), { recursive: true })
117
+ await writeFile(absolutePath, content, 'utf8')
118
+ }
119
+
120
+ test('a layout template with no default export is registered as metadata-only, like a .meta.ts file', async () => {
121
+ const root = await mkdtemp(join(tmpdir(), 'nextspark-template-registry-test-'))
122
+ try {
123
+ const templatePath = await writeThemeTemplate(
124
+ root,
125
+ 'docs/layout.tsx',
126
+ "export const metadata = { title: 'Docs' }\n"
127
+ )
128
+ const template = {
129
+ name: 'docs/layout',
130
+ themeName: 'testtheme',
131
+ templateType: 'layout',
132
+ fileName: 'layout.tsx',
133
+ relativePath: 'docs/layout.tsx',
134
+ appPath: 'app/docs/layout.tsx',
135
+ templatePath,
136
+ priority: 10
137
+ }
138
+
139
+ const out = await generateServerRegistry([template], { outputDir: '/tmp/registries', projectRoot: root })
140
+
141
+ assert.match(out, /'app\/docs\/layout\.tsx': \{\n\s*appPath: 'app\/docs\/layout\.tsx',\n\s*component: null,/)
142
+ assert.doesNotMatch(out, /layout'\)\)/)
143
+ } finally {
144
+ await rm(root, { recursive: true, force: true })
145
+ }
146
+ })
147
+
148
+ test('a layout template exported through `export { X as default }` is registered as a deferred import', async () => {
149
+ const root = await mkdtemp(join(tmpdir(), 'nextspark-template-registry-test-'))
150
+ try {
151
+ const templatePath = await writeThemeTemplate(
152
+ root,
153
+ 'docs/layout.tsx',
154
+ 'function DocsLayout({ children }) { return children }\nexport { DocsLayout as default }\n'
155
+ )
156
+ const template = {
157
+ name: 'docs/layout',
158
+ themeName: 'testtheme',
159
+ templateType: 'layout',
160
+ fileName: 'layout.tsx',
161
+ relativePath: 'docs/layout.tsx',
162
+ appPath: 'app/docs/layout.tsx',
163
+ templatePath,
164
+ priority: 10
165
+ }
166
+
167
+ const out = await generateServerRegistry([template], { outputDir: '/tmp/registries', projectRoot: root })
168
+
169
+ assert.match(
170
+ out,
171
+ /component: lazyTemplate\('app\/docs\/layout\.tsx', \(\) => import\('@\/contents\/themes\/testtheme\/templates\/docs\/layout'\)\)/
172
+ )
173
+ } finally {
174
+ await rm(root, { recursive: true, force: true })
175
+ }
176
+ })
177
+
178
+ // --- a page template with no default export is metadata-only only over an app page that already exists (#197) --
179
+
180
+ test('a page template with no default export and no existing app page fails the registry build with a clear message', async () => {
181
+ const root = await mkdtemp(join(tmpdir(), 'nextspark-template-registry-test-'))
182
+ try {
183
+ const templatePath = await writeThemeTemplate(root, 'docs/page.tsx', "export const metadata = { title: 'Docs' }\n")
184
+ const template = {
185
+ name: 'docs/page',
186
+ themeName: 'testtheme',
187
+ templateType: 'page',
188
+ fileName: 'page.tsx',
189
+ relativePath: 'docs/page.tsx',
190
+ appPath: 'app/docs/page.tsx',
191
+ templatePath,
192
+ priority: 10
193
+ }
194
+
195
+ // No app/docs/page.tsx written - the page generator would write one importing
196
+ // this template's (missing) default export.
197
+ await assert.rejects(
198
+ () => generateServerRegistry([template], { outputDir: '/tmp/registries', projectRoot: root }),
199
+ error => {
200
+ assert.match(error.message, new RegExp(templatePath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')))
201
+ assert.match(error.message, /no existing route at "app\/docs\/page\.tsx"/)
202
+ assert.match(error.message, /default export/)
203
+ assert.match(error.message, /\.meta\.ts/)
204
+ return true
205
+ }
206
+ )
207
+ } finally {
208
+ await rm(root, { recursive: true, force: true })
209
+ }
210
+ })
211
+
212
+ test('a page template with no default export overriding an existing app page is registered as metadata-only', async () => {
213
+ const root = await mkdtemp(join(tmpdir(), 'nextspark-template-registry-test-'))
214
+ try {
215
+ const templatePath = await writeThemeTemplate(root, 'docs/page.tsx', "export const metadata = { title: 'Docs' }\n")
216
+ await writeAppRoute(root, 'app/docs/page.tsx')
217
+ const template = {
218
+ name: 'docs/page',
219
+ themeName: 'testtheme',
220
+ templateType: 'page',
221
+ fileName: 'page.tsx',
222
+ relativePath: 'docs/page.tsx',
223
+ appPath: 'app/docs/page.tsx',
224
+ templatePath,
225
+ priority: 10
226
+ }
227
+
228
+ const out = await generateServerRegistry([template], { outputDir: '/tmp/registries', projectRoot: root })
229
+
230
+ assert.match(out, /'app\/docs\/page\.tsx': \{\n\s*appPath: 'app\/docs\/page\.tsx',\n\s*component: null,/)
231
+ assert.doesNotMatch(out, /page'\)\)/)
232
+ } finally {
233
+ await rm(root, { recursive: true, force: true })
234
+ }
235
+ })
236
+
237
+ // --- segment config is only validated for a template the page generator writes a route file for (#197) --
238
+
239
+ test('a page template with a default export and an invalid segment config export does not fail the registry build', async () => {
240
+ const root = await mkdtemp(join(tmpdir(), 'nextspark-template-registry-test-'))
241
+ try {
242
+ // revalidate = 60 * 60 is an expression, not a literal - a generated route
243
+ // file couldn't carry it, but this app page already exists, so none is generated.
244
+ const templatePath = await writeThemeTemplate(
245
+ root,
246
+ 'pricing/page.tsx',
247
+ 'export const revalidate = 60 * 60\nexport default function Page() { return null }\n'
248
+ )
249
+ await writeAppRoute(root, 'app/pricing/page.tsx')
250
+ const template = {
251
+ name: 'pricing/page',
252
+ themeName: 'testtheme',
253
+ templateType: 'page',
254
+ fileName: 'page.tsx',
255
+ relativePath: 'pricing/page.tsx',
256
+ appPath: 'app/pricing/page.tsx',
257
+ templatePath,
258
+ priority: 10
259
+ }
260
+
261
+ const out = await generateServerRegistry([template], { outputDir: '/tmp/registries', projectRoot: root })
262
+
263
+ assert.match(
264
+ out,
265
+ /component: lazyTemplate\('app\/pricing\/page\.tsx', \(\) => import\('@\/contents\/themes\/testtheme\/templates\/pricing\/page'\)\)/
266
+ )
267
+ } finally {
268
+ await rm(root, { recursive: true, force: true })
269
+ }
270
+ })
271
+
272
+ // --- a standalone .meta.ts page needs an existing app page too, same as a .tsx with no default export (#197) --
273
+
274
+ test('a standalone .meta.ts page template with no existing app page fails the registry build with a clear message', async () => {
275
+ const root = await mkdtemp(join(tmpdir(), 'nextspark-template-registry-test-'))
276
+ try {
277
+ // A standalone .meta.ts never has a default export - it only exports metadata.
278
+ const templatePath = await writeThemeTemplate(root, 'docs/page.meta.ts', "export const metadata = { title: 'Docs' }\n")
279
+ const template = {
280
+ name: 'docs/page',
281
+ themeName: 'testtheme',
282
+ templateType: 'page',
283
+ fileName: 'page.meta.ts',
284
+ relativePath: 'docs/page.meta.ts',
285
+ appPath: 'app/docs/page.tsx',
286
+ templatePath,
287
+ priority: 10
288
+ }
289
+
290
+ // No app/docs/page.tsx - the page generator would write one importing this
291
+ // .meta.ts file's (nonexistent) default export.
292
+ await assert.rejects(
293
+ () => generateServerRegistry([template], { outputDir: '/tmp/registries', projectRoot: root }),
294
+ error => {
295
+ assert.match(error.message, new RegExp(templatePath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')))
296
+ assert.match(error.message, /no existing route at "app\/docs\/page\.tsx"/)
297
+ return true
298
+ }
299
+ )
300
+ } finally {
301
+ await rm(root, { recursive: true, force: true })
302
+ }
303
+ })
304
+
305
+ test('a standalone .meta.ts page template overriding an existing app page is registered as metadata-only', async () => {
306
+ const root = await mkdtemp(join(tmpdir(), 'nextspark-template-registry-test-'))
307
+ try {
308
+ const templatePath = await writeThemeTemplate(root, 'docs/page.meta.ts', "export const metadata = { title: 'Docs' }\n")
309
+ await writeAppRoute(root, 'app/docs/page.tsx')
310
+ const template = {
311
+ name: 'docs/page',
312
+ themeName: 'testtheme',
313
+ templateType: 'page',
314
+ fileName: 'page.meta.ts',
315
+ relativePath: 'docs/page.meta.ts',
316
+ appPath: 'app/docs/page.tsx',
317
+ templatePath,
318
+ priority: 10
319
+ }
320
+
321
+ const out = await generateServerRegistry([template], { outputDir: '/tmp/registries', projectRoot: root })
322
+
323
+ assert.match(out, /'app\/docs\/page\.tsx': \{\n\s*appPath: 'app\/docs\/page\.tsx',\n\s*component: null,/)
324
+ } finally {
325
+ await rm(root, { recursive: true, force: true })
326
+ }
327
+ })
328
+
329
+ // --- both registries take the default-export answer from the same analysis (#197) --
330
+
331
+ test('a layout with no default export is component: null on the server and has no entry at all on the client', async () => {
332
+ const root = await mkdtemp(join(tmpdir(), 'nextspark-template-registry-test-'))
333
+ try {
334
+ // viewport isn't one of hasServerOnlyExports' patterns, so nothing but the
335
+ // default-export answer keeps this out of the client registry.
336
+ const templatePath = await writeThemeTemplate(root, 'docs/layout.tsx', "export const viewport = { themeColor: 'black' }\n")
337
+ const template = {
338
+ name: 'docs/layout',
339
+ themeName: 'testtheme',
340
+ templateType: 'layout',
341
+ fileName: 'layout.tsx',
342
+ relativePath: 'docs/layout.tsx',
343
+ appPath: 'app/docs/layout.tsx',
344
+ templatePath,
345
+ priority: 10
346
+ }
347
+ const config = { outputDir: '/tmp/registries', projectRoot: root }
348
+ const analysis = await analyzeTemplates([template], config)
349
+
350
+ const serverOut = await generateTemplateRegistry([template], config, analysis)
351
+ assert.match(serverOut, /'app\/docs\/layout\.tsx': \{\n\s*appPath: 'app\/docs\/layout\.tsx',\n\s*component: null,/)
352
+
353
+ const clientOut = await generateTemplateRegistryClient([template], config, analysis)
354
+ assert.doesNotMatch(clientOut, /app\/docs\/layout\.tsx/)
355
+ assert.doesNotMatch(clientOut, /docs\/layout'\)\)/)
356
+ } finally {
357
+ await rm(root, { recursive: true, force: true })
358
+ }
359
+ })
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Tests for the plan of app/(templates) that sync:app asks core for: what the
3
+ * registry build would create, replace and remove, worked out against app/ as
4
+ * the sync about to run leaves it, with nothing written.
5
+ *
6
+ * Run: node --test packages/core/scripts/build/registry/__tests__/templates-plan.test.mjs
7
+ */
8
+
9
+ import { test } from 'node:test'
10
+ import assert from 'node:assert/strict'
11
+ import { execFileSync } from 'node:child_process'
12
+ import { mkdtemp, mkdir, writeFile, readFile, rm } from 'node:fs/promises'
13
+ import { existsSync } from 'node:fs'
14
+ import { tmpdir } from 'node:os'
15
+ import { join, dirname } from 'node:path'
16
+ import { fileURLToPath } from 'node:url'
17
+
18
+ import { generateMissingPages } from '../post-build/page-generator.mjs'
19
+
20
+ const coreDir = join(dirname(fileURLToPath(import.meta.url)), '../../../..')
21
+ const SHOP_LAYOUT = 'export default function ShopLayout({ children }) { return children }\n'
22
+ const PAGE = 'export default function Page() { return null }\n'
23
+
24
+ async function writeProjectFile(root, relativePath, content) {
25
+ await mkdir(dirname(join(root, relativePath)), { recursive: true })
26
+ await writeFile(join(root, relativePath), content, 'utf8')
27
+ }
28
+
29
+ /** A project whose theme has a page template under app/shop, which has a layout of its own. */
30
+ async function createProject() {
31
+ const root = await mkdtemp(join(tmpdir(), 'nextspark-templates-plan-test-'))
32
+ await writeProjectFile(root, 'app/shop/layout.tsx', SHOP_LAYOUT)
33
+ await writeProjectFile(root, 'contents/themes/testtheme/templates/shop/pricing/page.tsx', PAGE)
34
+ return root
35
+ }
36
+
37
+ /** The page template as template discovery reports it. */
38
+ const pricingTemplate = {
39
+ name: 'shop/pricing/page',
40
+ themeName: 'testtheme',
41
+ templateType: 'page',
42
+ fileName: 'page.tsx',
43
+ relativePath: 'shop/pricing/page.tsx',
44
+ appPath: 'app/shop/pricing/page.tsx',
45
+ templatePath: '@/contents/themes/testtheme/templates/shop/pricing/page.tsx',
46
+ priority: 10
47
+ }
48
+
49
+ /** Run templates-plan.mjs for `root`, with `appFiles` on stdin, and return its result. */
50
+ function plan(root, appFiles = {}) {
51
+ const output = execFileSync(process.execPath, ['scripts/build/templates-plan.mjs'], {
52
+ cwd: coreDir,
53
+ input: JSON.stringify(appFiles),
54
+ encoding: 'utf8',
55
+ env: { ...process.env, NEXTSPARK_PROJECT_ROOT: root, NEXT_PUBLIC_ACTIVE_THEME: 'testtheme' }
56
+ })
57
+ const line = output.split('\n').find(candidate => candidate.startsWith('nextspark-templates-plan:'))
58
+ assert.ok(line, `no result in:\n${output}`)
59
+ return JSON.parse(line.slice('nextspark-templates-plan:'.length))
60
+ }
61
+
62
+ test('a tree not generated yet is planned as created, and a stray file in it as removed, with nothing written', async () => {
63
+ const root = await createProject()
64
+ try {
65
+ await writeProjectFile(root, 'app/(templates)/no-confirm.txt', 'mine\n')
66
+
67
+ assert.deepEqual(plan(root), {
68
+ create: ['app/(templates)/shop/layout.tsx', 'app/(templates)/shop/pricing/page.tsx'],
69
+ replace: [],
70
+ remove: ['app/(templates)/no-confirm.txt']
71
+ })
72
+ assert.equal(await readFile(join(root, 'app/(templates)/no-confirm.txt'), 'utf8'), 'mine\n')
73
+ assert.equal(existsSync(join(root, 'app/(templates)/shop')), false)
74
+ } finally {
75
+ await rm(root, { recursive: true, force: true })
76
+ }
77
+ })
78
+
79
+ test('the copy of an app layout the sync is about to change is planned as replaced, though the layout on disk still matches it', async () => {
80
+ const root = await createProject()
81
+ try {
82
+ await generateMissingPages([pricingTemplate], { projectRoot: root })
83
+ const copy = await readFile(join(root, 'app/(templates)/shop/layout.tsx'), 'utf8')
84
+
85
+ assert.deepEqual(plan(root), { create: [], replace: [], remove: [] })
86
+ assert.deepEqual(plan(root, { 'app/shop/layout.tsx': `// @nextspark-generated tag line\n${SHOP_LAYOUT}` }), {
87
+ create: [],
88
+ replace: ['app/(templates)/shop/layout.tsx'],
89
+ remove: []
90
+ })
91
+ assert.equal(await readFile(join(root, 'app/(templates)/shop/layout.tsx'), 'utf8'), copy)
92
+ assert.equal(existsSync(join(root, '.nextspark/backups')), false)
93
+ } finally {
94
+ await rm(root, { recursive: true, force: true })
95
+ }
96
+ })
97
+
98
+ test('a route the sync is about to create takes the template route file out of the plan as removed', async () => {
99
+ const root = await createProject()
100
+ try {
101
+ await generateMissingPages([pricingTemplate], { projectRoot: root })
102
+
103
+ assert.deepEqual(plan(root, { 'app/shop/pricing/page.tsx': PAGE }), {
104
+ create: [],
105
+ replace: [],
106
+ remove: ['app/(templates)/shop/pricing/page.tsx']
107
+ })
108
+ } finally {
109
+ await rm(root, { recursive: true, force: true })
110
+ }
111
+ })
112
+
113
+ test('a route the sync is about to remove puts the template route file back in the plan as created', async () => {
114
+ const root = await createProject()
115
+ try {
116
+ await writeProjectFile(root, 'app/shop/pricing/page.tsx', PAGE)
117
+ await generateMissingPages([pricingTemplate], { projectRoot: root })
118
+
119
+ assert.deepEqual(plan(root), { create: [], replace: [], remove: [] })
120
+ assert.deepEqual(plan(root, { 'app/shop/pricing/page.tsx': null }), {
121
+ create: ['app/(templates)/shop/pricing/page.tsx'],
122
+ replace: [],
123
+ remove: []
124
+ })
125
+ } finally {
126
+ await rm(root, { recursive: true, force: true })
127
+ }
128
+ })