@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
@@ -7,9 +7,8 @@
7
7
  * @module core/scripts/build/registry/generators/docs-registry
8
8
  */
9
9
 
10
- import fs from 'fs'
10
+ import { existsSync, readdirSync } from 'fs'
11
11
  import path from 'path'
12
- import { existsSync } from 'fs'
13
12
  import { CONFIG } from '../config.mjs'
14
13
 
15
14
  // Get config values - use themesDir which correctly handles monorepo mode
@@ -59,7 +58,7 @@ function scanDocsDirectory(docsPath, source) {
59
58
  }
60
59
 
61
60
  const sections = []
62
- const items = fs.readdirSync(docsPath, { withFileTypes: true })
61
+ const items = readdirSync(docsPath, { withFileTypes: true })
63
62
 
64
63
  for (const item of items) {
65
64
  if (!item.isDirectory()) continue
@@ -70,7 +69,7 @@ function scanDocsDirectory(docsPath, source) {
70
69
  const title = slugToTitle(slug)
71
70
 
72
71
  const pages = []
73
- const files = fs.readdirSync(sectionPath)
72
+ const files = readdirSync(sectionPath)
74
73
 
75
74
  for (const file of files) {
76
75
  if (!file.endsWith('.md')) continue
@@ -13,7 +13,7 @@
13
13
  * @module core/scripts/build/registry/generators/testing-registry
14
14
  */
15
15
 
16
- import { readFileSync, writeFileSync, existsSync } from 'fs'
16
+ import { readFileSync, existsSync } from 'fs'
17
17
  import { join } from 'path'
18
18
  import { discoverTestTags, validateTags } from '../discovery/test-tags.mjs'
19
19
 
@@ -10,7 +10,7 @@ import { readFile } from 'fs/promises'
10
10
  import { join, dirname } from 'path'
11
11
  import { fileURLToPath } from 'url'
12
12
 
13
- import { log, verbose } from '../../../utils/index.mjs'
13
+ import { errorWithLines, log, verbose } from '../../../utils/index.mjs'
14
14
 
15
15
  const __filename = fileURLToPath(import.meta.url)
16
16
  const __dirname = dirname(__filename)
@@ -187,7 +187,7 @@ export async function generatePermissionsRegistry(permissionsConfig, entities, c
187
187
  errorLines.push(' the "slug" field in the corresponding entity.config.ts')
188
188
  errorLines.push('')
189
189
 
190
- throw new Error(errorLines.join('\n'))
190
+ throw errorWithLines(errorLines)
191
191
  }
192
192
 
193
193
  log(' ✅ All permission entity keys validated against discovered entities', 'info')
@@ -15,6 +15,7 @@ import {
15
15
  canOverrideComponent
16
16
  } from '../../../../dist/config/protected-paths.js'
17
17
  import { convertCorePath } from '../config.mjs'
18
+ import { analyzeTemplates, routeFileAction, templateAnalysisFor } from '../post-build/page-generator.mjs'
18
19
 
19
20
  const __filename = fileURLToPath(import.meta.url)
20
21
  const __dirname = dirname(__filename)
@@ -23,13 +24,22 @@ const __dirname = dirname(__filename)
23
24
  const rootDir = join(__dirname, '../../../../../..')
24
25
 
25
26
  /**
26
- * Generate the template registry file
27
- * @param {Array} templates - Discovered templates
28
- * @param {object} config - Configuration object from getConfig()
29
- * @returns {string} Generated TypeScript content
27
+ * The analysis a generator works from: the one the registry build passes in,
28
+ * or - for a caller using the signature without it - one taken for this call,
29
+ * against the project root template files are resolved from here.
30
30
  */
31
- export function generateTemplateRegistry(templates, config) {
32
- // Group templates by app path to handle multiple themes overriding the same path
31
+ async function analysisFor(templates, config, analysis) {
32
+ return analysis ?? analyzeTemplates(templates, { projectRoot: config?.projectRoot || rootDir })
33
+ }
34
+
35
+ /**
36
+ * Group templates by app path (highest priority first), each with whether its
37
+ * highest-priority template has a default export. That answer comes from
38
+ * `analyzeTemplates`, which parsed the template once for the whole build: the
39
+ * server registry, the client registry and the page generator all take it
40
+ * from there, so none of them can see a different version of the file.
41
+ */
42
+ function resolveTemplateEntries(templates, analysis) {
33
43
  const templatesByPath = {}
34
44
 
35
45
  templates.forEach(template => {
@@ -39,16 +49,36 @@ export function generateTemplateRegistry(templates, config) {
39
49
  templatesByPath[template.appPath].push(template)
40
50
  })
41
51
 
42
- // Sort templates by priority (higher priority first)
43
52
  Object.keys(templatesByPath).forEach(appPath => {
44
53
  templatesByPath[appPath].sort((a, b) => b.priority - a.priority)
45
54
  })
46
55
 
56
+ // A template whose route file would break is rejected here too, whoever built the analysis
57
+ for (const template of templates) {
58
+ routeFileAction(template, templateAnalysisFor(analysis, template))
59
+ }
60
+
61
+ return Object.entries(templatesByPath).map(([appPath, pathTemplates]) => {
62
+ const highestPriorityTemplate = pathTemplates[0]
63
+ const { hasDefaultExport } = templateAnalysisFor(analysis, highestPriorityTemplate)
64
+ return { appPath, pathTemplates, highestPriorityTemplate, hasDefaultExport }
65
+ })
66
+ }
67
+
68
+ /**
69
+ * Generate the template registry file
70
+ * @param {Array} templates - Discovered templates
71
+ * @param {object} config - Configuration object from getConfig()
72
+ * @param {Map} [analysis] - What `analyzeTemplates` read from these templates; taken for this call when omitted
73
+ * @returns {Promise<string>} Generated TypeScript content
74
+ */
75
+ export async function generateTemplateRegistry(templates, config, analysis = null) {
76
+ const entries = resolveTemplateEntries(templates, await analysisFor(templates, config, analysis))
77
+
47
78
  // Generate registry entries. Each component is a deferred import, so a route
48
79
  // pulls in the template it renders and no others.
49
- const registryEntries = Object.entries(templatesByPath)
50
- .map(([appPath, pathTemplates]) => {
51
- const highestPriorityTemplate = pathTemplates[0]
80
+ const registryEntries = entries
81
+ .map(({ appPath, pathTemplates, highestPriorityTemplate, hasDefaultExport }) => {
52
82
  // Strip .tsx/.ts extension - Next.js resolves extensions automatically on all platforms
53
83
  let templatePath = highestPriorityTemplate.templatePath
54
84
  if (templatePath.endsWith('.tsx')) {
@@ -57,12 +87,12 @@ export function generateTemplateRegistry(templates, config) {
57
87
  templatePath = templatePath.slice(0, -3)
58
88
  }
59
89
 
60
- // Metadata-only templates (PROTECTED_RENDER or standalone .meta.ts) have
61
- // no component to load
90
+ // Metadata-only templates (PROTECTED_RENDER, standalone .meta.ts, or a
91
+ // template with no default export) have no component to load
62
92
  const isMetaOnly = highestPriorityTemplate.fileName?.endsWith('.meta.ts')
63
- const componentRef = (!canOverrideComponent(appPath) || isMetaOnly)
64
- ? 'null'
65
- : `lazyTemplate('${appPath}', () => import('${templatePath}'))`
93
+ const componentRef = canOverrideComponent(appPath) && !isMetaOnly && hasDefaultExport
94
+ ? `lazyTemplate('${appPath}', () => import('${templatePath}'))`
95
+ : 'null'
66
96
 
67
97
  return ` '${appPath}': {
68
98
  appPath: '${appPath}',
@@ -74,7 +104,7 @@ export function generateTemplateRegistry(templates, config) {
74
104
  .join(',\n')
75
105
 
76
106
  // Generate utility functions for template resolution
77
- const templatePaths = Object.keys(templatesByPath)
107
+ const templatePaths = entries.map(({ appPath }) => appPath)
78
108
  const templateTypeMap = {}
79
109
 
80
110
  templates.forEach(template => {
@@ -220,40 +250,27 @@ async function hasServerOnlyExports(filePath) {
220
250
  * Excludes templates with server-only exports (generateMetadata, revalidate, etc.)
221
251
  * @param {Array} templates - Discovered templates
222
252
  * @param {object} config - Configuration object from getConfig()
253
+ * @param {Map} [analysis] - What `analyzeTemplates` read from these templates; taken for this call when omitted
223
254
  * @returns {Promise<string>} Generated TypeScript content
224
255
  */
225
- export async function generateTemplateRegistryClient(templates, config) {
256
+ export async function generateTemplateRegistryClient(templates, config, analysis = null) {
226
257
  const outputFilePath = join(config.outputDir, 'template-registry.client.ts')
227
- // Group templates by app path
228
- const templatesByPath = {}
229
-
230
- templates.forEach(template => {
231
- if (!templatesByPath[template.appPath]) {
232
- templatesByPath[template.appPath] = []
233
- }
234
- templatesByPath[template.appPath].push(template)
235
- })
236
-
237
- // Sort templates by priority (higher priority first)
238
- Object.keys(templatesByPath).forEach(appPath => {
239
- templatesByPath[appPath].sort((a, b) => b.priority - a.priority)
240
- })
241
-
242
- // Filter for client-compatible templates (pages only, not layouts)
243
- // Also exclude templates with server-only exports
244
- const clientCompatibleTemplatesPromises = Object.entries(templatesByPath)
245
- .filter(([appPath, pathTemplates]) => {
246
- const template = pathTemplates[0]
247
- // Only include page templates (not layouts) that can override components
248
- return (template.templateType === 'page' || template.templateType === 'layout') && canOverrideComponent(appPath)
249
- })
250
- .map(async ([appPath, pathTemplates]) => {
251
- const template = pathTemplates[0]
258
+ const projectRoot = config.projectRoot || rootDir
259
+ const entries = resolveTemplateEntries(templates, await analysisFor(templates, config, analysis))
260
+
261
+ // Filter for client-compatible templates: pages and layouts, not protected,
262
+ // with a default export according to the build's analysis - the same answer
263
+ // the server registry uses. Also exclude templates with server-only exports.
264
+ const clientCompatibleTemplatesPromises = entries
265
+ .filter(({ appPath, highestPriorityTemplate, hasDefaultExport }) =>
266
+ (highestPriorityTemplate.templateType === 'page' || highestPriorityTemplate.templateType === 'layout') &&
267
+ canOverrideComponent(appPath) &&
268
+ hasDefaultExport
269
+ )
270
+ .map(async ({ appPath, highestPriorityTemplate }) => {
252
271
  // Get actual file path from template path
253
272
  // Template paths may or may not have extension, ensure we check the .tsx file
254
- // In npm mode, use config.projectRoot instead of rootDir (which is relative to script)
255
- const projectRoot = config.projectRoot || rootDir
256
- let actualFilePath = template.templatePath.replace('@/', projectRoot + '/')
273
+ let actualFilePath = highestPriorityTemplate.templatePath.replace('@/', projectRoot + '/')
257
274
  // If path doesn't end with .tsx or .ts, add .tsx
258
275
  if (!actualFilePath.endsWith('.tsx') && !actualFilePath.endsWith('.ts')) {
259
276
  actualFilePath += '.tsx'
@@ -265,7 +282,7 @@ export async function generateTemplateRegistryClient(templates, config) {
265
282
  verbose(`Excluding ${appPath} from client registry (has server-only exports)`)
266
283
  return null
267
284
  }
268
- return [appPath, pathTemplates]
285
+ return { appPath, highestPriorityTemplate }
269
286
  })
270
287
 
271
288
  const clientCompatibleTemplatesResults = await Promise.all(clientCompatibleTemplatesPromises)
@@ -274,10 +291,9 @@ export async function generateTemplateRegistryClient(templates, config) {
274
291
  // Generate registry entries as deferred imports, so a route loads the
275
292
  // template it renders and no others
276
293
  const registryEntries = clientCompatibleTemplates
277
- .map(([appPath, pathTemplates]) => {
278
- const template = pathTemplates[0]
294
+ .map(({ appPath, highestPriorityTemplate }) => {
279
295
  // Strip .tsx/.ts extension - Next.js resolves extensions automatically on all platforms
280
- let templatePath = template.templatePath
296
+ let templatePath = highestPriorityTemplate.templatePath
281
297
  if (templatePath.endsWith('.tsx')) {
282
298
  templatePath = templatePath.slice(0, -4)
283
299
  } else if (templatePath.endsWith('.ts')) {
@@ -29,7 +29,6 @@ export {
29
29
 
30
30
  // Generators
31
31
  export {
32
- generateAll,
33
32
  generatePluginRegistry,
34
33
  generatePluginRegistryClient,
35
34
  generateEntityRegistry,
@@ -52,7 +51,7 @@ export {
52
51
 
53
52
  // Post-build tasks
54
53
  export {
55
- runPostBuild,
54
+ analyzeTemplates,
56
55
  generateMissingPages,
57
56
  generateTemplatePage,
58
57
  displayTreeStructure,
@@ -6,7 +6,7 @@
6
6
  * @module core/scripts/build/registry/post-build
7
7
  */
8
8
 
9
- export { generateMissingPages, generateTemplatePage } from './page-generator.mjs'
9
+ export { analyzeTemplates, generateMissingPages, generateTemplatePage } from './page-generator.mjs'
10
10
  export { displayTreeStructure } from './tree-display.mjs'
11
11
  export { generateTestEntitiesJson, generateTestBlocksJson, extractEntityTestData } from './test-fixtures.mjs'
12
12
  export {
@@ -0,0 +1,220 @@
1
+ /**
2
+ * The .gitignore files nextspark keeps in the two directories none of whose
3
+ * content has a place in git: .nextspark/backups, where each backup of a file
4
+ * the registry build or `nextspark sync:app` replaces or removes goes into a
5
+ * directory named as it is created, and .nextspark/registries, which the
6
+ * registry build writes again on every run. A .gitignore in a directory that
7
+ * ignores everything in it covers all of it, named however and holding any
8
+ * .gitignore of its own: git takes the patterns of the deepest .gitignore over
9
+ * those of any other, the repository's exclude file and core.excludesFile, and
10
+ * doesn't look into a directory it ignores. So what goes there that git does
11
+ * not track yet stays out of git whatever command writes it and whatever the
12
+ * project's rules say. What git already tracks there - registries committed
13
+ * before - a .gitignore leaves tracked: each build that rewrites them leaves
14
+ * them modified in git, and says so, with how to stop tracking them.
15
+ *
16
+ * `nextspark sync:app` reads them, and writes the one for backups, through this
17
+ * module, loaded from the core installed in the project.
18
+ *
19
+ * @module core/scripts/build/registry/post-build/own-gitignores
20
+ */
21
+
22
+ import { execFileSync } from 'child_process'
23
+ import { lstatSync, readFileSync } from 'fs'
24
+ import { lstat } from 'fs/promises'
25
+ import { join, posix } from 'path'
26
+ import { projectFiles } from '../../safe-fs.mjs'
27
+
28
+ export const BACKUPS_GITIGNORE = '.nextspark/backups/.gitignore'
29
+
30
+ export const REGISTRIES_GITIGNORE = '.nextspark/registries/.gitignore'
31
+
32
+ export const BACKUPS_GITIGNORE_CONTENT = '# Backups nextspark keeps on this machine: none of them belongs in git\n*\n'
33
+
34
+ export const REGISTRIES_GITIGNORE_CONTENT = '# Registries the nextspark registry build writes again on every run: none of them belongs in git\n*\n'
35
+
36
+ const CONTENTS = {
37
+ [BACKUPS_GITIGNORE]: BACKUPS_GITIGNORE_CONTENT,
38
+ [REGISTRIES_GITIGNORE]: REGISTRIES_GITIGNORE_CONTENT,
39
+ }
40
+
41
+ /**
42
+ * A .gitignore line's pattern as git takes it: without a closing carriage
43
+ * return, or the spaces that end it unless a backslash escapes the last one.
44
+ */
45
+ function patternAsGitReadsIt(line) {
46
+ const withoutReturn = line.replace(/\r$/, '')
47
+ let end = withoutReturn.length
48
+ let lastSpace = -1
49
+ for (let index = 0; index < withoutReturn.length; index++) {
50
+ const character = withoutReturn[index]
51
+ if (character === ' ') {
52
+ if (lastSpace === -1) lastSpace = index
53
+ } else {
54
+ if (character === '\\') index++
55
+ lastSpace = -1
56
+ }
57
+ }
58
+ if (lastSpace !== -1) end = lastSpace
59
+ return withoutReturn.slice(0, end)
60
+ }
61
+
62
+ /**
63
+ * What one of those .gitignore files is: absent; in place, when `*` is the only
64
+ * pattern git reads in it, whatever its comments; a symlink, which git does not
65
+ * read; not a file; a file that can't be read, which may hold anything; or a
66
+ * file with other patterns, which can take back what is beside it.
67
+ *
68
+ * @param {string} rootDir - The project root
69
+ * @param {string} path - BACKUPS_GITIGNORE or REGISTRIES_GITIGNORE
70
+ * @returns {'missing' | 'in place' | 'symlink' | 'not a file' | 'unreadable' | 'other'}
71
+ */
72
+ export function ownGitignoreState(rootDir, path) {
73
+ const absolute = join(rootDir, path)
74
+ let stat
75
+ try {
76
+ stat = lstatSync(absolute)
77
+ } catch (error) {
78
+ return error.code === 'ENOENT' || error.code === 'ENOTDIR' ? 'missing' : 'unreadable'
79
+ }
80
+ if (stat.isSymbolicLink()) return 'symlink'
81
+ if (!stat.isFile()) return 'not a file'
82
+
83
+ let content
84
+ try {
85
+ content = readFileSync(absolute, 'utf8')
86
+ } catch {
87
+ return 'unreadable'
88
+ }
89
+ const patterns = content
90
+ .replace(/^\uFEFF/, '')
91
+ .split('\n')
92
+ .map(patternAsGitReadsIt)
93
+ .filter(line => line !== '' && !line.startsWith('#'))
94
+ return patterns.length === 1 && patterns[0] === '*' ? 'in place' : 'other'
95
+ }
96
+
97
+ /**
98
+ * What .nextspark/backups/.gitignore is, as `ownGitignoreState` reads it.
99
+ *
100
+ * @param {string} rootDir - The project root
101
+ */
102
+ export function backupsGitignoreState(rootDir) {
103
+ return ownGitignoreState(rootDir, BACKUPS_GITIGNORE)
104
+ }
105
+
106
+ /**
107
+ * What is wrong with one of those .gitignore files for keeping its directory
108
+ * out of git, by its state, and what to do about it; a state missing here is none.
109
+ */
110
+ export const OWN_GITIGNORE_PROBLEMS = {
111
+ symlink: 'is a symlink, which git does not read, so git would pick up what is beside it: make it a file with * as its only pattern, or remove it for nextspark to write it',
112
+ 'not a file': 'is not a file: remove it for nextspark to write it',
113
+ unreadable: "can't be read, so nothing says it keeps what is beside it out of git: make it readable, with * as its only pattern",
114
+ other: 'has patterns other than *, which can take what is beside it back into git: leave * as its only pattern, or remove it for nextspark to write it',
115
+ }
116
+
117
+ /**
118
+ * Why nothing can be written beside one of those .gitignore files with git kept
119
+ * out of it, and what to do about it, or null when it can.
120
+ */
121
+ async function ownGitignoreBlocker(rootDir, path) {
122
+ for (const directory of ['.nextspark', posix.dirname(path)]) {
123
+ let stat
124
+ try {
125
+ stat = await lstat(join(rootDir, directory))
126
+ } catch {
127
+ break
128
+ }
129
+ if (stat.isSymbolicLink() || !stat.isDirectory()) {
130
+ return stat.isSymbolicLink()
131
+ ? `${directory} is a symlink: what is written through it lands wherever it points, where no .gitignore of the project keeps it out of git. Make ${directory} a directory of its own.`
132
+ : `${directory} is not a directory. Make it one.`
133
+ }
134
+ }
135
+
136
+ const problem = OWN_GITIGNORE_PROBLEMS[ownGitignoreState(rootDir, path)]
137
+ return problem ? `${path} ${problem}.` : null
138
+ }
139
+
140
+ /**
141
+ * Give a directory the .gitignore of its own, before anything is written in it.
142
+ * One already there is never written over: what it holds is the project's. When
143
+ * it can't keep the directory out of git - a symlink, not a file, unreadable,
144
+ * other patterns - or .nextspark or the directory is a symlink or not a
145
+ * directory, this throws, starting with `refusal`, so nothing is written there.
146
+ */
147
+ async function ensureOwnGitignore(rootDir, path, refusal) {
148
+ const blocker = await ownGitignoreBlocker(rootDir, path)
149
+ if (blocker) throw new Error(`${refusal}: ${blocker}`)
150
+ if (ownGitignoreState(rootDir, path) === 'in place') return false
151
+
152
+ const files = projectFiles(rootDir)
153
+ await files.mkdir(join(rootDir, posix.dirname(path)), { recursive: true })
154
+ try {
155
+ await files.writeFile(join(rootDir, path), CONTENTS[path], { flag: 'wx' })
156
+ } catch (error) {
157
+ if (error.code !== 'EEXIST') throw error
158
+ // Another run wrote one in the meantime, which counts only if it is one that works
159
+ const blockerNow = await ownGitignoreBlocker(rootDir, path)
160
+ if (blockerNow) throw new Error(`${refusal}: ${blockerNow}`)
161
+ return false
162
+ }
163
+ return true
164
+ }
165
+
166
+ /**
167
+ * The files git tracks under `directory` of the project, as git names them from
168
+ * the project root; none when the project is not in a git repository or git is
169
+ * not installed.
170
+ *
171
+ * @param {string} rootDir - The project root
172
+ * @param {string} directory - A directory from the project root
173
+ * @returns {string[]}
174
+ */
175
+ export function trackedFilesUnder(rootDir, directory) {
176
+ try {
177
+ return execFileSync('git', ['ls-files', '-z', '--', directory], {
178
+ cwd: rootDir,
179
+ encoding: 'utf8',
180
+ stdio: ['ignore', 'pipe', 'ignore'],
181
+ maxBuffer: 64 * 1024 * 1024,
182
+ }).split('\0').filter(Boolean)
183
+ } catch {
184
+ return []
185
+ }
186
+ }
187
+
188
+ /**
189
+ * What to say when git tracks registries: that the build rewrites them and
190
+ * their .gitignore can't keep them out, and how to stop tracking them.
191
+ *
192
+ * @param {number} count - How many files git tracks under .nextspark/registries
193
+ * @returns {string[]} One line each
194
+ */
195
+ export function trackedRegistriesLines(count) {
196
+ return [
197
+ `.nextspark/registries is tracked by git (${count} file(s)), but every registry build rewrites it: its .gitignore keeps out only what git does not track yet, so each build leaves those files modified in git.`,
198
+ 'To stop tracking it: git rm -r --cached .nextspark/registries',
199
+ ]
200
+ }
201
+
202
+ /**
203
+ * Give .nextspark/backups its .gitignore before the first backup is written.
204
+ *
205
+ * @param {string} rootDir - The project root
206
+ * @returns {Promise<boolean>} Whether it was written
207
+ */
208
+ export function ensureBackupsGitignore(rootDir) {
209
+ return ensureOwnGitignore(rootDir, BACKUPS_GITIGNORE, 'Nothing is backed up under .nextspark/backups')
210
+ }
211
+
212
+ /**
213
+ * Give .nextspark/registries its .gitignore before the first registry is written.
214
+ *
215
+ * @param {string} rootDir - The project root
216
+ * @returns {Promise<boolean>} Whether it was written
217
+ */
218
+ export function ensureRegistriesGitignore(rootDir) {
219
+ return ensureOwnGitignore(rootDir, REGISTRIES_GITIGNORE, 'No registry is written under .nextspark/registries')
220
+ }