@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
@@ -12,6 +12,7 @@ import { join, dirname } from 'path'
12
12
  import { fileURLToPath } from 'url'
13
13
 
14
14
  import { log, verbose } from '../../../utils/index.mjs'
15
+ import { isUnsafeWrite, projectFiles } from '../../safe-fs.mjs'
15
16
 
16
17
  const __filename = fileURLToPath(import.meta.url)
17
18
  const __dirname = dirname(__filename)
@@ -39,9 +40,7 @@ export async function cleanupOldRouteFiles(CONFIG) {
39
40
  const content = await readFile(routeFile, 'utf8')
40
41
  if (content.includes('Auto-generated Plugin Route Proxy')) {
41
42
  // Remove the entire plugin route directory
42
- await import('fs/promises').then(fs =>
43
- fs.rm(pluginDir, { recursive: true, force: true })
44
- )
43
+ await projectFiles(rootDir).rm(pluginDir, { recursive: true, force: true })
45
44
  verbose(`Cleaned up old route: ${pluginDir}`)
46
45
  }
47
46
  }
@@ -49,6 +48,8 @@ export async function cleanupOldRouteFiles(CONFIG) {
49
48
  }
50
49
  }
51
50
  } catch (error) {
51
+ // An UNSAFE_WRITE refusal propagates and stops the build; every other error here is a non-fatal cleanup failure
52
+ if (isUnsafeWrite(error)) throw error
52
53
  verbose(`Error during route cleanup: ${error.message}`)
53
54
  }
54
55
  }
@@ -80,7 +81,6 @@ export async function cleanupOrphanedTemplates(activeTemplates, CONFIG) {
80
81
 
81
82
  async function scanAndCleanDirectory(dir, relativePath = '') {
82
83
  try {
83
- const { readdir, stat, unlink } = await import('fs/promises')
84
84
  const entries = await readdir(dir, { withFileTypes: true })
85
85
 
86
86
  for (const entry of entries) {
@@ -103,13 +103,14 @@ export async function cleanupOrphanedTemplates(activeTemplates, CONFIG) {
103
103
 
104
104
  // Check if this generated file has a corresponding active template
105
105
  if (!activeTemplatePaths.has(fullPath)) {
106
- await unlink(fullPath)
106
+ await projectFiles(rootDir).unlink(fullPath)
107
107
  log(`Cleaned orphaned file: ${fullPath.replace(rootDir, '')}`, 'info')
108
108
  cleaned++
109
109
  }
110
110
  }
111
111
  }
112
112
  } catch (error) {
113
+ if (isUnsafeWrite(error)) throw error
113
114
  verbose(`Error scanning directory ${dir}: ${error.message}`)
114
115
  }
115
116
  }
@@ -118,12 +119,12 @@ export async function cleanupOrphanedTemplates(activeTemplates, CONFIG) {
118
119
 
119
120
  // Clean up empty directories (in reverse order to handle nested structures)
120
121
  if (dirsToCleanup.length > 0) {
121
- const { rmdir } = await import('fs/promises')
122
122
  for (const dir of dirsToCleanup.reverse()) {
123
123
  try {
124
- await rmdir(dir)
124
+ await projectFiles(rootDir).rmdir(dir)
125
125
  log(`Removed empty directory: ${dir.replace(rootDir, '')}`, 'info')
126
- } catch {
126
+ } catch (error) {
127
+ if (isUnsafeWrite(error)) throw error
127
128
  // Directory might not be empty anymore, ignore
128
129
  }
129
130
  }
@@ -166,10 +167,10 @@ export async function cleanupDeletedTemplate(templateFilePath, CONFIG) {
166
167
  }
167
168
 
168
169
  try {
169
- const { unlink, rmdir } = await import('fs/promises')
170
+ const files = projectFiles(rootDir)
170
171
 
171
172
  // Remove the generated template file
172
- await unlink(appTemplatePath)
173
+ await files.unlink(appTemplatePath)
173
174
  log(`Removed generated template: ${appTemplatePath.replace(rootDir, '')}`, 'info')
174
175
 
175
176
  // Try to remove empty parent directories (but don't fail if they're not empty)
@@ -178,19 +179,21 @@ export async function cleanupDeletedTemplate(templateFilePath, CONFIG) {
178
179
 
179
180
  while (parentDir !== templatesDir && parentDir !== rootDir) {
180
181
  try {
181
- const entries = await import('fs/promises').then(fs => fs.readdir(parentDir))
182
+ const entries = await readdir(parentDir)
182
183
  if (entries.length === 0) {
183
- await rmdir(parentDir)
184
+ await files.rmdir(parentDir)
184
185
  log(`Removed empty directory: ${parentDir.replace(rootDir, '')}`, 'info')
185
186
  parentDir = dirname(parentDir)
186
187
  } else {
187
188
  break // Directory is not empty, stop cleanup
188
189
  }
189
- } catch {
190
+ } catch (error) {
191
+ if (isUnsafeWrite(error)) throw error
190
192
  break // Error reading directory or removing, stop cleanup
191
193
  }
192
194
  }
193
195
  } catch (error) {
196
+ if (isUnsafeWrite(error)) throw error
194
197
  verbose(`Failed to cleanup template file ${appTemplatePath}: ${error.message}`)
195
198
  }
196
199
  }
@@ -7,18 +7,36 @@
7
7
  */
8
8
 
9
9
  import { existsSync } from 'fs'
10
- import { readFile, writeFile } from 'fs/promises'
10
+ import { readFile } from 'fs/promises'
11
11
  import { join, dirname } from 'path'
12
12
  import { fileURLToPath } from 'url'
13
13
 
14
14
  import { log, verbose } from '../../../utils/index.mjs'
15
- import { CONFIG } from '../config.mjs'
15
+ import { CONFIG as DEFAULT_CONFIG } from '../config.mjs'
16
+ import { projectFiles } from '../../safe-fs.mjs'
16
17
 
17
18
  const __filename = fileURLToPath(import.meta.url)
18
19
  const __dirname = dirname(__filename)
19
20
  // Path from packages/core/scripts/build/registry/post-build/ to project root (6 levels up)
20
21
  const rootDir = join(__dirname, '../../../../../..')
21
22
 
23
+ /**
24
+ * The directory of the active theme's Cypress fixtures, which the build writes
25
+ * entities.json and blocks.json into when it exists, and the root those writes
26
+ * may land under: the project's, or the monorepo's, where the themes of the
27
+ * monorepo are.
28
+ *
29
+ * @param {object} config - Configuration object from getConfig()
30
+ * @returns {{ fixturesDir: string, root: string } | null} Null when no theme is active
31
+ */
32
+ export function testFixturesPlace(config) {
33
+ if (!config.activeTheme) return null
34
+ return {
35
+ fixturesDir: join(config.themesDir, config.activeTheme, 'tests', 'cypress', 'fixtures'),
36
+ root: config.isMonorepoMode ? config.monorepoRoot : config.projectRoot,
37
+ }
38
+ }
39
+
22
40
  /**
23
41
  * Extract test-relevant data from an entity config file using regex
24
42
  * @param {string} configPath - Path to the entity config file
@@ -88,7 +106,7 @@ export async function extractEntityTestData(configPath, entityDir) {
88
106
  * @param {Array} entities - All discovered entities
89
107
  * @param {Array} themes - All discovered themes
90
108
  */
91
- export async function generateTestEntitiesJson(entities, themes) {
109
+ export async function generateTestEntitiesJson(entities, themes, CONFIG = DEFAULT_CONFIG) {
92
110
  const activeTheme = CONFIG.activeTheme
93
111
  if (!activeTheme) {
94
112
  verbose('No active theme set, skipping test fixtures generation')
@@ -103,7 +121,7 @@ export async function generateTestEntitiesJson(entities, themes) {
103
121
  }
104
122
 
105
123
  // Check if tests directory exists
106
- const fixturesDir = join(CONFIG.themesDir, activeTheme, 'tests', 'cypress', 'fixtures')
124
+ const { fixturesDir, root } = testFixturesPlace(CONFIG)
107
125
  if (!existsSync(fixturesDir)) {
108
126
  verbose(`Test fixtures directory not found for theme "${activeTheme}", skipping`)
109
127
  return
@@ -178,7 +196,7 @@ export async function generateTestEntitiesJson(entities, themes) {
178
196
 
179
197
  // Write the file
180
198
  const outputPath = join(fixturesDir, 'entities.json')
181
- await writeFile(outputPath, JSON.stringify(jsonContent, null, 2), 'utf8')
199
+ await projectFiles(root).writeFile(outputPath, JSON.stringify(jsonContent, null, 2), 'utf8')
182
200
  log(`entities.json generated for theme "${activeTheme}"`, 'success')
183
201
  }
184
202
 
@@ -187,7 +205,7 @@ export async function generateTestEntitiesJson(entities, themes) {
187
205
  * This file is used by Cypress tests for block-related testing
188
206
  * @param {Array} blocks - All discovered blocks
189
207
  */
190
- export async function generateTestBlocksJson(blocks) {
208
+ export async function generateTestBlocksJson(blocks, CONFIG = DEFAULT_CONFIG) {
191
209
  const activeTheme = CONFIG.activeTheme
192
210
  if (!activeTheme) {
193
211
  verbose('No active theme set, skipping blocks.json generation')
@@ -195,7 +213,7 @@ export async function generateTestBlocksJson(blocks) {
195
213
  }
196
214
 
197
215
  // Check if tests directory exists
198
- const fixturesDir = join(CONFIG.themesDir, activeTheme, 'tests', 'cypress', 'fixtures')
216
+ const { fixturesDir, root } = testFixturesPlace(CONFIG)
199
217
  if (!existsSync(fixturesDir)) {
200
218
  verbose(`Test fixtures directory not found for theme "${activeTheme}", skipping blocks.json`)
201
219
  return
@@ -229,6 +247,6 @@ export async function generateTestBlocksJson(blocks) {
229
247
  }
230
248
 
231
249
  const outputPath = join(fixturesDir, 'blocks.json')
232
- await writeFile(outputPath, JSON.stringify(jsonContent, null, 2), 'utf8')
250
+ await projectFiles(root).writeFile(outputPath, JSON.stringify(jsonContent, null, 2), 'utf8')
233
251
  log(`blocks.json generated for theme "${activeTheme}" (${blocks.length} blocks)`, 'success')
234
252
  }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * How core is installed in a project, told from the project alone: from npm, or
3
+ * as a package of the NextSpark monorepo. Nothing here reads configuration or
4
+ * loads a .env, so a check that runs before the build - in the build itself or
5
+ * in `nextspark` - can ask it.
6
+ *
7
+ * @module core/scripts/build/registry/project-mode
8
+ */
9
+
10
+ import { existsSync, lstatSync, readlinkSync } from 'fs'
11
+ import { dirname, join } from 'path'
12
+
13
+ /**
14
+ * Detect monorepo root by searching for pnpm-workspace.yaml
15
+ * @param {string} startDir - Directory to start searching from
16
+ * @returns {string|null} Monorepo root path or null if not in monorepo
17
+ */
18
+ export function detectMonorepoRoot(startDir = process.cwd()) {
19
+ let dir = startDir
20
+ const maxDepth = 10
21
+ let depth = 0
22
+
23
+ while (dir !== '/' && depth < maxDepth) {
24
+ if (existsSync(join(dir, 'pnpm-workspace.yaml'))) {
25
+ return dir
26
+ }
27
+ dir = dirname(dir)
28
+ depth++
29
+ }
30
+
31
+ return null
32
+ }
33
+
34
+ /**
35
+ * Check if NextSpark is installed as a package (from npm registry)
36
+ *
37
+ * This handles both traditional npm/yarn AND pnpm:
38
+ * - npm/yarn: packages are copied to node_modules (not symlinks)
39
+ * - pnpm: ALL packages are symlinks, but npm packages point to .pnpm/ store
40
+ * - pnpm workspace: local packages symlink to the actual package path
41
+ *
42
+ * @param {string} root - Project root path
43
+ * @returns {boolean} True if installed from npm registry
44
+ */
45
+ export function isInstalledAsPackage(root) {
46
+ const corePath = join(root, 'node_modules/@nextsparkjs/core')
47
+ if (!existsSync(corePath)) {
48
+ return false
49
+ }
50
+
51
+ try {
52
+ const stat = lstatSync(corePath)
53
+
54
+ // If not a symlink, it's a traditional npm/yarn install
55
+ if (!stat.isSymbolicLink()) {
56
+ return true
57
+ }
58
+
59
+ // For symlinks (pnpm), check WHERE it points to:
60
+ // - npm install: points to .pnpm/@nextsparkjs+core@version.../node_modules/@nextsparkjs/core
61
+ // - workspace: points to ../../packages/core or similar
62
+ const linkTarget = readlinkSync(corePath)
63
+
64
+ // If symlink points to .pnpm/ or .pnpmN/ directory, it's a real npm installation via pnpm
65
+ // The .pnpm/.pnpm2/etc directory is pnpm's content-addressable store
66
+ if (/\.pnpm\d?[/\\]/.test(linkTarget)) {
67
+ return true
68
+ }
69
+
70
+ // Otherwise it's a workspace symlink (monorepo development)
71
+ return false
72
+ } catch {
73
+ return false
74
+ }
75
+ }
76
+
@@ -0,0 +1,77 @@
1
+ /**
2
+ * TypeScript Compiler Loader
3
+ *
4
+ * Resolves the TypeScript 5/6 compiler API a registry build step needs to
5
+ * read a source file's real syntax tree, shared by every discovery or
6
+ * generation step that parses TypeScript instead of scanning raw text.
7
+ *
8
+ * @module core/scripts/build/registry/shared/typescript-compiler
9
+ */
10
+
11
+ import { createRequire } from 'node:module'
12
+ import { join } from 'node:path'
13
+ import { errorWithLines } from '../../../utils/logging.mjs'
14
+
15
+ /** TypeScript 7's native port doesn't ship the compiler API - its package
16
+ * exports are `./lib/version.cjs` plus `./unstable/*` - so a candidate module
17
+ * is checked for the API surface this build needs, not just for existing. */
18
+ function hasCompilerApi(ts) {
19
+ return Boolean(ts && typeof ts.createSourceFile === 'function' && typeof ts.getScriptKindFromFileName === 'function' && ts.ScriptKind)
20
+ }
21
+
22
+ /**
23
+ * Try each TypeScript candidate in order and return the first module whose API this
24
+ * build needs. A candidate that loads but lacks the API is recorded and the next
25
+ * one is tried, rather than failing on the first hit - the point of trying more than
26
+ * one candidate at all.
27
+ */
28
+ export async function selectTypeScriptModule(candidates) {
29
+ const rejected = []
30
+
31
+ for (const candidate of candidates) {
32
+ let loaded
33
+ try {
34
+ loaded = await candidate.load()
35
+ } catch (error) {
36
+ rejected.push(`${candidate.label}: could not be loaded (${error.message})`)
37
+ continue
38
+ }
39
+
40
+ const ts = loaded?.default ?? loaded
41
+ if (hasCompilerApi(ts)) {
42
+ return ts
43
+ }
44
+ rejected.push(
45
+ `${candidate.label}: found TypeScript ${ts?.version ?? 'of an unknown version'}, which does not export the TypeScript 5/6 ` +
46
+ 'compiler API (createSourceFile, getScriptKindFromFileName, ScriptKind) the registry build needs'
47
+ )
48
+ }
49
+
50
+ throw errorWithLines([
51
+ 'Parsing a source file for the registry build requires the TypeScript 5 or 6 compiler API, but no candidate provided it:',
52
+ ...rejected.map(reason => ` - ${reason}`),
53
+ ])
54
+ }
55
+
56
+ const modulePromises = new Map()
57
+
58
+ /**
59
+ * The TypeScript compiler, resolved from core first and then from `projectRoot`:
60
+ * it is a dependency of core (#195), so it is always found there regardless of
61
+ * the TypeScript version the project itself has installed; the project is a
62
+ * fallback for setups where core's own copy is not reachable from the caller's
63
+ * location on disk. Cached per project root so repeated calls in the same
64
+ * build don't re-resolve the module.
65
+ */
66
+ export function loadTypeScriptFor(projectRoot) {
67
+ if (!modulePromises.has(projectRoot)) {
68
+ modulePromises.set(
69
+ projectRoot,
70
+ selectTypeScriptModule([
71
+ { label: '@nextsparkjs/core', load: () => import('typescript') },
72
+ { label: `the project at ${projectRoot}`, load: async () => createRequire(join(projectRoot, 'package.json'))('typescript') },
73
+ ])
74
+ )
75
+ }
76
+ return modulePromises.get(projectRoot)
77
+ }
@@ -6,19 +6,23 @@
6
6
 
7
7
  import { describe, it } from 'node:test'
8
8
  import assert from 'node:assert'
9
+ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
10
+ import { tmpdir } from 'node:os'
11
+ import { join } from 'node:path'
9
12
  import { validateEnvironment } from './config.mjs'
10
13
 
11
14
  describe('validateEnvironment', () => {
12
15
  it('should return valid when .env exists and activeTheme is set', () => {
13
- // Use the current working directory which has a .env file
14
- const config = {
15
- projectRoot: process.cwd(),
16
- activeTheme: 'default'
16
+ const projectRoot = mkdtempSync(join(tmpdir(), 'nextspark-validate-env-test-'))
17
+
18
+ try {
19
+ writeFileSync(join(projectRoot, '.env'), '')
20
+ const result = validateEnvironment({ projectRoot, activeTheme: 'default' })
21
+ assert.strictEqual(result.valid, true)
22
+ assert.strictEqual(result.errors.length, 0)
23
+ } finally {
24
+ rmSync(projectRoot, { recursive: true, force: true })
17
25
  }
18
-
19
- const result = validateEnvironment(config)
20
- assert.strictEqual(result.valid, true)
21
- assert.strictEqual(result.errors.length, 0)
22
26
  })
23
27
 
24
28
  it('should return error when .env file is missing', () => {
@@ -9,9 +9,8 @@
9
9
  import { existsSync, watch } from 'fs'
10
10
  import { join } from 'path'
11
11
 
12
- import { log, verbose } from '../../utils/index.mjs'
12
+ import { log } from '../../utils/index.mjs'
13
13
  import { CONFIG } from './config.mjs'
14
- import { cleanupDeletedTemplate } from './post-build/route-cleanup.mjs'
15
14
 
16
15
  /**
17
16
  * Start watching contents directory for changes
@@ -29,35 +28,6 @@ export async function watchContents(buildRegistries) {
29
28
  ]
30
29
 
31
30
  let debounceTimer = null
32
- const templateFileTracker = new Map() // Track existing template files
33
-
34
- // Initialize template file tracker
35
- for (const watchPath of watchPaths) {
36
- if (existsSync(watchPath) && watchPath.includes('themes')) {
37
- const { readdir } = await import('fs/promises')
38
- try {
39
- const trackTemplates = async (dir, basePath = '') => {
40
- const entries = await readdir(dir, { withFileTypes: true })
41
- for (const entry of entries) {
42
- const fullPath = join(dir, entry.name)
43
- const relativePath = join(basePath, entry.name)
44
-
45
- if (entry.isDirectory()) {
46
- await trackTemplates(fullPath, relativePath)
47
- } else if (entry.isFile() && relativePath.includes('/templates/') &&
48
- (entry.name.endsWith('.tsx') || entry.name.endsWith('.ts'))) {
49
- templateFileTracker.set(relativePath, true)
50
- }
51
- }
52
- }
53
-
54
- await trackTemplates(watchPath)
55
- verbose(`Tracking ${templateFileTracker.size} template files for deletion detection`)
56
- } catch (error) {
57
- verbose(`Failed to initialize template tracker: ${error.message}`)
58
- }
59
- }
60
- }
61
31
 
62
32
  for (const watchPath of watchPaths) {
63
33
  if (existsSync(watchPath)) {
@@ -81,7 +51,8 @@ export async function watchContents(buildRegistries) {
81
51
  return // Skip rebuild for test artifacts
82
52
  }
83
53
 
84
- // ENHANCED: Handle template deletions before rebuilding
54
+ // A deleted template needs nothing of its own here: the rebuild leaves its
55
+ // route out of app/(templates) and backs the old file up.
85
56
  const shouldRebuild = !filename || // Always rebuild if filename is null (deletions)
86
57
  filename.endsWith('.config.ts') ||
87
58
  filename.endsWith('route.ts') ||
@@ -90,27 +61,6 @@ export async function watchContents(buildRegistries) {
90
61
  filename.includes('/templates/') || // Any change in templates directory
91
62
  eventType === 'rename' // Catch rename events (often deletions)
92
63
 
93
- // Handle template file deletions
94
- if (filename && filename.includes('/templates/') &&
95
- (filename.endsWith('.tsx') || filename.endsWith('.ts'))) {
96
-
97
- const fullTemplatePath = join(watchPath, filename)
98
- const wasTracked = templateFileTracker.has(filename)
99
- const currentlyExists = existsSync(fullTemplatePath)
100
-
101
- // File was deleted (was tracked but no longer exists)
102
- if (wasTracked && !currentlyExists) {
103
- log(`Template deleted: ${filename}`, 'info')
104
- await cleanupDeletedTemplate(fullTemplatePath, CONFIG)
105
- templateFileTracker.delete(filename)
106
- }
107
- // File was added (exists but wasn't tracked)
108
- else if (!wasTracked && currentlyExists) {
109
- templateFileTracker.set(filename, true)
110
- verbose(`Template added to tracker: ${filename}`)
111
- }
112
- }
113
-
114
64
  if (shouldRebuild) {
115
65
  // Debounce rebuilds
116
66
  clearTimeout(debounceTimer)