create-nextblock 0.12.9 → 0.12.12

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 (293) hide show
  1. package/docker-template/.dockerignore +23 -23
  2. package/docker-template/.env.docker.example +69 -69
  3. package/docker-template/docker/db/init/99-jwt.sql +6 -6
  4. package/docker-template/docker/db/init/99-roles.sql +25 -25
  5. package/docker-template/docker/kong/kong.yml +112 -112
  6. package/docker-template/scripts/docker-setup.mjs +310 -310
  7. package/libs/db/tsconfig.lib.json +3 -3
  8. package/libs/editor/tsconfig.lib.json +3 -3
  9. package/libs/ui/tsconfig.lib.json +3 -3
  10. package/libs/utils/tsconfig.json +3 -3
  11. package/package.json +1 -1
  12. package/project.json +19 -19
  13. package/templates/nextblock-template/.browserslistrc +11 -11
  14. package/templates/nextblock-template/.dockerignore +23 -23
  15. package/templates/nextblock-template/README.md +34 -34
  16. package/templates/nextblock-template/app/(auth-pages)/layout.tsx +9 -9
  17. package/templates/nextblock-template/app/(auth-pages)/post-sign-in/page.tsx +27 -27
  18. package/templates/nextblock-template/app/(auth-pages)/sign-up/SignUpForm.tsx +141 -141
  19. package/templates/nextblock-template/app/(auth-pages)/sign-up/page.tsx +46 -46
  20. package/templates/nextblock-template/app/(auth-pages)/two-factor/actions.ts +91 -91
  21. package/templates/nextblock-template/app/(auth-pages)/two-factor/components/TwoFactorForm.tsx +123 -123
  22. package/templates/nextblock-template/app/(auth-pages)/two-factor/page.tsx +51 -51
  23. package/templates/nextblock-template/app/ToasterProvider.tsx +17 -17
  24. package/templates/nextblock-template/app/[slug]/pageClientActions.ts +7 -7
  25. package/templates/nextblock-template/app/actions/consent.ts +57 -57
  26. package/templates/nextblock-template/app/actions/interactions.test.ts +301 -301
  27. package/templates/nextblock-template/app/actions/interactions.ts +373 -373
  28. package/templates/nextblock-template/app/actions/postActions.ts +146 -146
  29. package/templates/nextblock-template/app/actions/twoFactorEmail.ts +22 -22
  30. package/templates/nextblock-template/app/api/ai/cortex/build-widget/route.ts +153 -153
  31. package/templates/nextblock-template/app/api/ai/generate-blocks/route.ts +96 -96
  32. package/templates/nextblock-template/app/api/ai/global-agent/route.ts +964 -964
  33. package/templates/nextblock-template/app/api/brand/email-logo/route.ts +48 -48
  34. package/templates/nextblock-template/app/api/cms/check-updates/route.ts +44 -44
  35. package/templates/nextblock-template/app/api/cms/full-backup/export/route.ts +33 -33
  36. package/templates/nextblock-template/app/api/cms/full-backup/restore/route.ts +63 -63
  37. package/templates/nextblock-template/app/api/cron/reset-sandbox/route.ts +3490 -3490
  38. package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +5597 -5597
  39. package/templates/nextblock-template/app/api/cron/sync-currencies/route.ts +39 -39
  40. package/templates/nextblock-template/app/api/custom-blocks/db-relations/route.ts +92 -92
  41. package/templates/nextblock-template/app/api/custom-blocks/editor-definitions/route.ts +43 -43
  42. package/templates/nextblock-template/app/api/media/library/route.ts +69 -69
  43. package/templates/nextblock-template/app/api/media/r2-presigned/route.ts +53 -53
  44. package/templates/nextblock-template/app/api/media/record/route.ts +160 -160
  45. package/templates/nextblock-template/app/api/search/route.ts +43 -43
  46. package/templates/nextblock-template/app/article/[slug]/PostClientContent.tsx +441 -441
  47. package/templates/nextblock-template/app/auth/callback/route.ts +31 -31
  48. package/templates/nextblock-template/app/cart/page.tsx +7 -7
  49. package/templates/nextblock-template/app/cms/blocks/components/CustomBlockEditorPreview.tsx +160 -160
  50. package/templates/nextblock-template/app/cms/blocks/components/MediaLibraryModal.tsx +149 -149
  51. package/templates/nextblock-template/app/cms/blocks/editors/DynamicCustomBlockEditor.tsx +167 -167
  52. package/templates/nextblock-template/app/cms/blocks/editors/ImageBlockEditor.tsx +29 -29
  53. package/templates/nextblock-template/app/cms/blocks/editors/TextBlockEditor.tsx +90 -90
  54. package/templates/nextblock-template/app/cms/components/ConnectGitHubButton.tsx +122 -122
  55. package/templates/nextblock-template/app/cms/components/CortexAiPageContext.tsx +58 -58
  56. package/templates/nextblock-template/app/cms/components/FeatureImageField.tsx +244 -244
  57. package/templates/nextblock-template/app/cms/components/FeedbackModal.tsx +36 -36
  58. package/templates/nextblock-template/app/cms/components/SystemAlertsBanner.tsx +112 -112
  59. package/templates/nextblock-template/app/cms/components/TwoFactorReminderBanner.tsx +45 -45
  60. package/templates/nextblock-template/app/cms/components/github-connect-actions.ts +102 -102
  61. package/templates/nextblock-template/app/cms/components/system-alerts-actions.ts +31 -31
  62. package/templates/nextblock-template/app/cms/custom-blocks/[id]/edit/page.tsx +66 -66
  63. package/templates/nextblock-template/app/cms/custom-blocks/actions.ts +519 -519
  64. package/templates/nextblock-template/app/cms/custom-blocks/components/BlockComposer.tsx +1522 -1522
  65. package/templates/nextblock-template/app/cms/custom-blocks/components/BlocksLibraryTransferControls.tsx +256 -256
  66. package/templates/nextblock-template/app/cms/custom-blocks/components/DBRelationSelect.tsx +384 -384
  67. package/templates/nextblock-template/app/cms/custom-blocks/components/ImageR2Picker.tsx +221 -221
  68. package/templates/nextblock-template/app/cms/custom-blocks/new/page.tsx +12 -12
  69. package/templates/nextblock-template/app/cms/custom-blocks/page.tsx +438 -438
  70. package/templates/nextblock-template/app/cms/dashboard/actions.ts +228 -228
  71. package/templates/nextblock-template/app/cms/dashboard/components/DashboardOnboarding.tsx +130 -130
  72. package/templates/nextblock-template/app/cms/import-export/ContentTransferControls.tsx +431 -391
  73. package/templates/nextblock-template/app/cms/import-export/actions.ts +226 -226
  74. package/templates/nextblock-template/app/cms/interactions/InteractionsModerationClient.tsx +408 -408
  75. package/templates/nextblock-template/app/cms/interactions/page.tsx +51 -51
  76. package/templates/nextblock-template/app/cms/layout.tsx +73 -73
  77. package/templates/nextblock-template/app/cms/media/components/FolderNavigator.tsx +273 -273
  78. package/templates/nextblock-template/app/cms/media/components/FolderTree.tsx +122 -122
  79. package/templates/nextblock-template/app/cms/media/components/MediaEditForm.tsx +26 -26
  80. package/templates/nextblock-template/app/cms/media/components/MediaGridClient.tsx +69 -69
  81. package/templates/nextblock-template/app/cms/media/components/MediaPickerDialog.tsx +149 -158
  82. package/templates/nextblock-template/app/cms/media/components/MediaUploadForm.tsx +101 -101
  83. package/templates/nextblock-template/app/cms/navigation/components/NavigationMenuDnd.tsx +3 -3
  84. package/templates/nextblock-template/app/cms/pages/page.tsx +18 -17
  85. package/templates/nextblock-template/app/cms/posts/page.tsx +19 -18
  86. package/templates/nextblock-template/app/cms/products/[id]/edit/page.tsx +12 -0
  87. package/templates/nextblock-template/app/cms/products/attributes/page.tsx +12 -12
  88. package/templates/nextblock-template/app/cms/products/categories/page.tsx +12 -12
  89. package/templates/nextblock-template/app/cms/products/inventory/page.tsx +13 -13
  90. package/templates/nextblock-template/app/cms/products/new/page.tsx +135 -135
  91. package/templates/nextblock-template/app/cms/products/page.tsx +39 -27
  92. package/templates/nextblock-template/app/cms/products/productFormData.ts +133 -133
  93. package/templates/nextblock-template/app/cms/products/settings/page.tsx +5 -5
  94. package/templates/nextblock-template/app/cms/promotions/PromotionsWorkspace.tsx +456 -456
  95. package/templates/nextblock-template/app/cms/promotions/actions.ts +115 -115
  96. package/templates/nextblock-template/app/cms/promotions/page.tsx +31 -31
  97. package/templates/nextblock-template/app/cms/revisions/service.ts +19 -19
  98. package/templates/nextblock-template/app/cms/revisions/utils.ts +132 -132
  99. package/templates/nextblock-template/app/cms/settings/backup-restore/BackupRestoreWorkspace.tsx +1004 -1004
  100. package/templates/nextblock-template/app/cms/settings/backup-restore/page.tsx +29 -29
  101. package/templates/nextblock-template/app/cms/settings/bot-protection/actions.ts +93 -93
  102. package/templates/nextblock-template/app/cms/settings/bot-protection/components/BotProtectionForm.tsx +129 -129
  103. package/templates/nextblock-template/app/cms/settings/bot-protection/page.tsx +24 -24
  104. package/templates/nextblock-template/app/cms/settings/cortex-ai/SandboxCortexAiSettingsClient.tsx +497 -497
  105. package/templates/nextblock-template/app/cms/settings/cortex-ai/StoredCortexAiSettingsClient.tsx +410 -410
  106. package/templates/nextblock-template/app/cms/settings/cortex-ai/actions.ts +246 -246
  107. package/templates/nextblock-template/app/cms/settings/cortex-ai/page.tsx +80 -80
  108. package/templates/nextblock-template/app/cms/settings/currencies/actions.ts +331 -331
  109. package/templates/nextblock-template/app/cms/settings/currencies/page.tsx +494 -494
  110. package/templates/nextblock-template/app/cms/settings/email/actions.ts +64 -64
  111. package/templates/nextblock-template/app/cms/settings/email/components/EmailForm.tsx +181 -181
  112. package/templates/nextblock-template/app/cms/settings/email/page.tsx +28 -28
  113. package/templates/nextblock-template/app/cms/settings/extra-translations/ExtraTranslationsWorkspace.tsx +767 -767
  114. package/templates/nextblock-template/app/cms/settings/extra-translations/page.tsx +93 -93
  115. package/templates/nextblock-template/app/cms/settings/google-analytics/actions.ts +60 -60
  116. package/templates/nextblock-template/app/cms/settings/google-analytics/components/GoogleAnalyticsForm.tsx +129 -129
  117. package/templates/nextblock-template/app/cms/settings/google-analytics/page.tsx +26 -26
  118. package/templates/nextblock-template/app/cms/settings/languages/actions.ts +16 -16
  119. package/templates/nextblock-template/app/cms/settings/logos/[id]/edit/page.tsx +7 -7
  120. package/templates/nextblock-template/app/cms/settings/logos/components/BrandingSettingsForm.tsx +339 -339
  121. package/templates/nextblock-template/app/cms/settings/logos/components/DeleteLogoButton.tsx +21 -21
  122. package/templates/nextblock-template/app/cms/settings/logos/components/LogoForm.tsx +20 -20
  123. package/templates/nextblock-template/app/cms/settings/logos/components/SetActiveLogoButton.tsx +42 -42
  124. package/templates/nextblock-template/app/cms/settings/logos/components/SiteSeoSettingsForm.tsx +133 -133
  125. package/templates/nextblock-template/app/cms/settings/logos/new/page.tsx +8 -8
  126. package/templates/nextblock-template/app/cms/settings/packages/package-card.tsx +122 -122
  127. package/templates/nextblock-template/app/cms/settings/privacy/actions.ts +52 -52
  128. package/templates/nextblock-template/app/cms/settings/privacy/components/PrivacyForm.tsx +148 -148
  129. package/templates/nextblock-template/app/cms/settings/privacy/page.tsx +27 -27
  130. package/templates/nextblock-template/app/cms/settings/registration/actions.ts +44 -44
  131. package/templates/nextblock-template/app/cms/settings/registration/components/RegistrationForm.tsx +65 -65
  132. package/templates/nextblock-template/app/cms/settings/registration/page.tsx +27 -27
  133. package/templates/nextblock-template/app/cms/settings/security/actions.ts +284 -284
  134. package/templates/nextblock-template/app/cms/settings/security/components/SecurityPanel.tsx +522 -522
  135. package/templates/nextblock-template/app/cms/settings/security/page.tsx +33 -33
  136. package/templates/nextblock-template/app/cms/settings/taxes/page.tsx +21 -21
  137. package/templates/nextblock-template/app/cms/shipping/page.tsx +20 -20
  138. package/templates/nextblock-template/app/cms/users/actions.ts +222 -222
  139. package/templates/nextblock-template/app/cms/users/components/DeleteUserButton.tsx +12 -12
  140. package/templates/nextblock-template/app/cms/users/components/UserForm.tsx +44 -44
  141. package/templates/nextblock-template/app/lib/site-settings.ts +105 -105
  142. package/templates/nextblock-template/app/lib/sitemap-utils.ts +253 -253
  143. package/templates/nextblock-template/app/profile/ProfileAccountSidebar.tsx +73 -73
  144. package/templates/nextblock-template/app/profile/ProfilePageHeader.tsx +16 -16
  145. package/templates/nextblock-template/app/profile/ProfilePageMissingState.tsx +9 -9
  146. package/templates/nextblock-template/app/profile/account-data.ts +37 -37
  147. package/templates/nextblock-template/app/profile/account-links.ts +22 -22
  148. package/templates/nextblock-template/app/profile/account-types.ts +11 -11
  149. package/templates/nextblock-template/app/profile/orders/CustomerOrdersPageClient.tsx +124 -124
  150. package/templates/nextblock-template/app/profile/orders/[id]/CustomerOrderDetailPageClient.tsx +79 -79
  151. package/templates/nextblock-template/app/profile/orders/[id]/page.tsx +32 -32
  152. package/templates/nextblock-template/app/profile/orders/page.tsx +19 -19
  153. package/templates/nextblock-template/app/profile/page.tsx +42 -42
  154. package/templates/nextblock-template/app/profile/password/PasswordSettingsPageClient.tsx +128 -128
  155. package/templates/nextblock-template/app/profile/password/actions.ts +59 -59
  156. package/templates/nextblock-template/app/profile/password/page.tsx +27 -27
  157. package/templates/nextblock-template/app/setup/SetupWizard.tsx +678 -678
  158. package/templates/nextblock-template/app/setup/layout.tsx +13 -13
  159. package/templates/nextblock-template/app/setup/page.tsx +111 -111
  160. package/templates/nextblock-template/app/sitemap.ts +130 -130
  161. package/templates/nextblock-template/components/CartDrawerLoader.tsx +7 -7
  162. package/templates/nextblock-template/components/DeferredCartDrawer.tsx +23 -23
  163. package/templates/nextblock-template/components/DeferredGoogleAnalytics.tsx +70 -70
  164. package/templates/nextblock-template/components/DeferredGoogleTagManager.tsx +70 -70
  165. package/templates/nextblock-template/components/DeferredSpeedInsights.tsx +69 -69
  166. package/templates/nextblock-template/components/FeatureImageHero.tsx +47 -47
  167. package/templates/nextblock-template/components/FooterNavigation.tsx +32 -32
  168. package/templates/nextblock-template/components/GitHubLoginButton.tsx +15 -15
  169. package/templates/nextblock-template/components/HtmlScriptExecutor.tsx +47 -47
  170. package/templates/nextblock-template/components/LanguageSwitcher.tsx +2 -2
  171. package/templates/nextblock-template/components/PostCommentsSection.tsx +369 -369
  172. package/templates/nextblock-template/components/ProductReviewsSection.tsx +419 -419
  173. package/templates/nextblock-template/components/PublicEnvBootstrap.tsx +30 -30
  174. package/templates/nextblock-template/components/ResponsiveNav.tsx +14 -14
  175. package/templates/nextblock-template/components/auth/AuthBotProtection.tsx +182 -182
  176. package/templates/nextblock-template/components/blocks/PostCardSkeleton.tsx +12 -12
  177. package/templates/nextblock-template/components/blocks/PostsGridBlock.tsx +12 -12
  178. package/templates/nextblock-template/components/blocks/ecommerceRendererLoaders.ts +23 -23
  179. package/templates/nextblock-template/components/blocks/renderers/FormBlockRenderer.tsx +249 -249
  180. package/templates/nextblock-template/components/blocks/types.ts +7 -7
  181. package/templates/nextblock-template/components/env-var-warning.tsx +3 -3
  182. package/templates/nextblock-template/components/form-message.tsx +32 -32
  183. package/templates/nextblock-template/components/privacy/ConsentBanner.tsx +170 -170
  184. package/templates/nextblock-template/components/privacy/ConsentGatedAnalytics.tsx +70 -70
  185. package/templates/nextblock-template/components/renderers/CachedDynamicLayoutEngine.tsx +28 -28
  186. package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.test.tsx +166 -166
  187. package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.tsx +464 -464
  188. package/templates/nextblock-template/components/submit-button.tsx +23 -23
  189. package/templates/nextblock-template/components/theme-switcher.tsx +8 -8
  190. package/templates/nextblock-template/components/visual-editing/DeferredVisualEditing.tsx +21 -21
  191. package/templates/nextblock-template/context/AuthContext.tsx +23 -23
  192. package/templates/nextblock-template/context/LanguageContext.tsx +16 -16
  193. package/templates/nextblock-template/context/language-rest-client.ts +32 -32
  194. package/templates/nextblock-template/docker/db/init/99-jwt.sql +6 -6
  195. package/templates/nextblock-template/docker/db/init/99-roles.sql +25 -25
  196. package/templates/nextblock-template/docker/kong/kong.yml +112 -112
  197. package/templates/nextblock-template/docs/01-PROJECT-OVERVIEW.md +94 -94
  198. package/templates/nextblock-template/docs/02-ECOMMERCE-CAPABILITIES.md +364 -364
  199. package/templates/nextblock-template/docs/03-CMS-AND-EDITOR.md +202 -202
  200. package/templates/nextblock-template/docs/04-DATABASE-AND-AUTH.md +246 -246
  201. package/templates/nextblock-template/docs/06-CLI-AND-SCAFFOLDING.md +176 -176
  202. package/templates/nextblock-template/docs/07-BLOCK-SDK-AND-EXTENSIBILITY.md +146 -146
  203. package/templates/nextblock-template/docs/08-NEXTBLOCK-CORTEX-AI-ARCHITECTURE.md +1319 -1319
  204. package/templates/nextblock-template/docs/10-CUSTOM-BLOCKS.md +222 -222
  205. package/templates/nextblock-template/docs/11-SELF-HOSTED-DOCKER.md +173 -173
  206. package/templates/nextblock-template/docs/12-VERCEL-DEPLOYMENT.md +170 -170
  207. package/templates/nextblock-template/docs/13-STAYING-UP-TO-DATE.md +151 -151
  208. package/templates/nextblock-template/docs/README.md +39 -39
  209. package/templates/nextblock-template/docs/TECHNICAL_SPECIFICATION.md +12505 -12505
  210. package/templates/nextblock-template/hooks/use-hotkeys.ts +21 -21
  211. package/templates/nextblock-template/hooks/useGlobalSearch.ts +101 -101
  212. package/templates/nextblock-template/index.d.ts +7 -7
  213. package/templates/nextblock-template/lib/app-secrets.ts +39 -39
  214. package/templates/nextblock-template/lib/auth/cookies.ts +47 -47
  215. package/templates/nextblock-template/lib/auth/crypto.ts +45 -45
  216. package/templates/nextblock-template/lib/auth/trustedDevices.ts +92 -92
  217. package/templates/nextblock-template/lib/auth/twoFactor.ts +208 -208
  218. package/templates/nextblock-template/lib/auth-redirects.ts +46 -46
  219. package/templates/nextblock-template/lib/blocks/README.md +13 -13
  220. package/templates/nextblock-template/lib/blocks/blockRegistry.ts +6 -6
  221. package/templates/nextblock-template/lib/blocks/blockTypes.ts +18 -18
  222. package/templates/nextblock-template/lib/botProtection/verify.ts +134 -134
  223. package/templates/nextblock-template/lib/cms-transfer/csv.test.ts +100 -77
  224. package/templates/nextblock-template/lib/cms-transfer/csv.ts +403 -399
  225. package/templates/nextblock-template/lib/cms-transfer/server.ts +2243 -2243
  226. package/templates/nextblock-template/lib/cms-transfer/types.ts +145 -145
  227. package/templates/nextblock-template/lib/config/email-settings.ts +217 -217
  228. package/templates/nextblock-template/lib/custom-block-definitions.ts +87 -87
  229. package/templates/nextblock-template/lib/custom-block-r2-upload-shared.ts +178 -178
  230. package/templates/nextblock-template/lib/custom-block-r2-upload.test.ts +140 -140
  231. package/templates/nextblock-template/lib/custom-block-r2-upload.ts +88 -88
  232. package/templates/nextblock-template/lib/custom-block-relation-registry.ts +256 -256
  233. package/templates/nextblock-template/lib/custom-block-relations.test.ts +227 -227
  234. package/templates/nextblock-template/lib/custom-block-relations.ts +279 -279
  235. package/templates/nextblock-template/lib/custom-block-safelist.ts +14 -14
  236. package/templates/nextblock-template/lib/editor/dynamic-extension-core.test.ts +172 -172
  237. package/templates/nextblock-template/lib/editor/dynamic-extension-core.ts +213 -213
  238. package/templates/nextblock-template/lib/editor/dynamic-extension-loader.ts +22 -22
  239. package/templates/nextblock-template/lib/editor/dynamic-extensions.tsx +193 -193
  240. package/templates/nextblock-template/lib/email/branding-format.test.ts +133 -133
  241. package/templates/nextblock-template/lib/email/branding-format.ts +123 -123
  242. package/templates/nextblock-template/lib/email/branding.ts +76 -76
  243. package/templates/nextblock-template/lib/full-backup/manifest.test.ts +121 -121
  244. package/templates/nextblock-template/lib/full-backup/manifest.ts +206 -206
  245. package/templates/nextblock-template/lib/full-backup/server.ts +743 -743
  246. package/templates/nextblock-template/lib/logos/active-logo.ts +53 -53
  247. package/templates/nextblock-template/lib/media/resolveMediaUrl.ts +54 -54
  248. package/templates/nextblock-template/lib/onboarding/actions.ts +31 -31
  249. package/templates/nextblock-template/lib/onboarding/status.ts +222 -222
  250. package/templates/nextblock-template/lib/posts/readTime.ts +60 -60
  251. package/templates/nextblock-template/lib/privacy/consent-client.ts +57 -57
  252. package/templates/nextblock-template/lib/privacy/contact-emails.ts +64 -64
  253. package/templates/nextblock-template/lib/privacy/settings.ts +115 -115
  254. package/templates/nextblock-template/lib/privacy/types.ts +69 -69
  255. package/templates/nextblock-template/lib/promotions/server.test.ts +74 -74
  256. package/templates/nextblock-template/lib/promotions/server.ts +741 -741
  257. package/templates/nextblock-template/lib/resolve-block-relations.test.ts +142 -142
  258. package/templates/nextblock-template/lib/resolve-block-relations.ts +255 -255
  259. package/templates/nextblock-template/lib/search/server.ts +585 -585
  260. package/templates/nextblock-template/lib/search/types.ts +27 -27
  261. package/templates/nextblock-template/lib/setup/actions.ts +460 -460
  262. package/templates/nextblock-template/lib/setup/env-status.ts +125 -125
  263. package/templates/nextblock-template/lib/setup/env-write.ts +111 -111
  264. package/templates/nextblock-template/lib/setup/migrations-bundle.ts +37 -37
  265. package/templates/nextblock-template/lib/setup/provisioning.ts +59 -59
  266. package/templates/nextblock-template/lib/setup/schema-apply.ts +408 -408
  267. package/templates/nextblock-template/lib/setup/system-config.ts +105 -105
  268. package/templates/nextblock-template/lib/setup/types.ts +18 -18
  269. package/templates/nextblock-template/lib/site-url.ts +48 -48
  270. package/templates/nextblock-template/lib/storage/provider.ts +66 -66
  271. package/templates/nextblock-template/lib/storage/supabase-storage.ts +103 -103
  272. package/templates/nextblock-template/lib/updates/check-upstream.ts +441 -441
  273. package/templates/nextblock-template/lib/updates/github-device.ts +206 -206
  274. package/templates/nextblock-template/lib/updates/repo-identity.ts +56 -56
  275. package/templates/nextblock-template/package.json +1 -1
  276. package/templates/nextblock-template/postcss.config.js +6 -6
  277. package/templates/nextblock-template/scripts/backup.js +115 -115
  278. package/templates/nextblock-template/scripts/docker-setup.mjs +310 -310
  279. package/templates/nextblock-template/scripts/restore.js +385 -385
  280. package/templates/nextblock-template/scripts/verify-cortex-ai-build-widget.tsx +98 -98
  281. package/templates/nextblock-template/scripts/verify-cortex-ai-generate-blocks.ts +62 -62
  282. package/templates/nextblock-template/scripts/verify-cortex-ai-global-tools.ts +537 -537
  283. package/templates/nextblock-template/scripts/verify-cortex-ai-routing.ts +58 -58
  284. package/templates/nextblock-template/scripts/verify-custom-block-definitions.ts +188 -188
  285. package/templates/nextblock-template/scripts/verify-dynamic-custom-block-extensions.ts +123 -123
  286. package/templates/nextblock-template/scripts/verify-dynamic-layout-engine.tsx +133 -133
  287. package/templates/nextblock-template/scripts/verify-milestone-2-custom-blocks.ts +65 -65
  288. package/templates/nextblock-template/tailwind.config.js +25 -25
  289. package/templates/nextblock-template/tools/build-migrate.mjs +209 -209
  290. package/templates/nextblock-template/tools/configure-supabase-auth.js +282 -282
  291. package/templates/nextblock-template/types/jsdom.d.ts +6 -6
  292. package/tsconfig.base.json +3 -3
  293. package/templates/nextblock-template/tsconfig.tsbuildinfo +0 -1
@@ -1,293 +1,293 @@
1
1
  // apps/nextblock/scripts/restore.js
2
- const fs = require('fs');
3
- const path = require('path');
4
- const { spawn } = require('child_process');
5
- const readline = require('readline');
6
-
7
- function createPrefixSuppressor(regexes, label) {
8
- return (line) => {
9
- for (const regex of regexes) {
10
- if (regex.test(line)) {
11
- return label;
12
- }
13
- }
14
- return null;
15
- };
16
- }
17
-
18
- const stdoutSuppressors = [
19
- createPrefixSuppressor([/^SET\b/i, /^RESET\b/i], 'SET/RESET statements'),
20
- createPrefixSuppressor([/^CREATE\b/i], 'CREATE statements'),
21
- createPrefixSuppressor([/^ALTER\b/i], 'ALTER statements'),
22
- createPrefixSuppressor([/^DROP\b/i], 'DROP statements'),
23
- createPrefixSuppressor([/^COMMENT\b/i], 'COMMENT statements'),
24
- createPrefixSuppressor([/^GRANT\b/i], 'GRANT statements'),
25
- createPrefixSuppressor([/^REVOKE\b/i], 'REVOKE statements'),
26
- createPrefixSuppressor([/^COPY\b/i], 'COPY statements'),
27
- createPrefixSuppressor([/^SELECT\s+pg_catalog\.setval/i, /^\s*setval\b/i], 'setval outputs'),
28
- createPrefixSuppressor([/^\(\d+\s+rows?\)$/i, /^\(1 row\)$/i], 'row count outputs'),
29
- createPrefixSuppressor([/^\s*set_config\b/i], 'set_config outputs'),
30
- createPrefixSuppressor([/^\s*-{3,}\s*$/], 'table separators'),
31
- createPrefixSuppressor([/^\s*\d+\s*$/], 'numeric result lines'),
32
- ];
33
-
34
- function extractMultiplier(line) {
35
- const match = line.match(/\sx(\d+)$/i);
36
- return match ? parseInt(match[1], 10) : 1;
37
- }
38
-
39
- function createDuplicateCollapser(outputFn, options = {}) {
40
- const suppressors = options.suppressors || [];
41
- const suppressedCounts = new Map();
42
- let buffer = '';
43
- let lastLine = null;
44
- let repeatCount = 0;
45
- let finished = false;
46
-
47
- const flush = () => {
48
- if (lastLine === null) {
49
- return;
50
- }
51
- const baseLine = lastLine;
52
- const multiplier = extractMultiplier(baseLine);
53
- const occurrences = repeatCount * multiplier;
54
-
55
- for (const suppressor of suppressors) {
56
- const label = suppressor(baseLine);
57
- if (label) {
58
- suppressedCounts.set(label, (suppressedCounts.get(label) || 0) + occurrences);
59
- lastLine = null;
60
- repeatCount = 0;
61
- return;
62
- }
63
- }
64
-
65
- const line =
66
- repeatCount > 1 ? `${baseLine} x${repeatCount}` : baseLine;
67
- outputFn(line);
68
- lastLine = null;
69
- repeatCount = 0;
70
- };
71
-
72
- const handleLine = (line) => {
73
- const normalized = line.replace(/\r$/, '');
74
- if (!normalized.trim()) {
75
- return;
76
- }
77
- if (lastLine === null) {
78
- lastLine = normalized;
79
- repeatCount = 1;
80
- return;
81
- }
82
- if (normalized === lastLine) {
83
- repeatCount += 1;
84
- return;
85
- }
86
- flush();
87
- lastLine = normalized;
88
- repeatCount = 1;
89
- };
90
-
91
- return {
92
- write(chunk) {
93
- if (finished) {
94
- return;
95
- }
96
- buffer += chunk.toString();
97
- let newlineIndex;
98
- while ((newlineIndex = buffer.indexOf('\n')) !== -1) {
99
- const line = buffer.slice(0, newlineIndex);
100
- handleLine(line);
101
- buffer = buffer.slice(newlineIndex + 1);
102
- }
103
- },
104
- finish() {
105
- if (finished) {
106
- return suppressedCounts;
107
- }
108
- if (buffer.length > 0) {
109
- handleLine(buffer);
110
- buffer = '';
111
- }
112
- flush();
113
- finished = true;
114
- return suppressedCounts;
115
- },
116
- };
117
- }
118
-
119
- function createErrorFilter(outputFn) {
120
- let buffer = '';
121
- const suppressedCounts = new Map();
122
- let finished = false;
123
-
124
- const ignorePatterns = [
125
- {
126
- regex: /ERROR:\s+permission denied to change default privileges/i,
127
- label: 'permission denied to change default privileges',
128
- },
129
- {
130
- regex: /ERROR:\s+Non-superuser owned event trigger must execute a non-superuser owned function/i,
131
- label: 'event trigger requires non-superuser function',
132
- },
133
- {
134
- regex: /DETAIL:\s+The current user "postgres" is not a superuser and the function "extensions\.[^"]+" is owned by a superuser/i,
135
- },
136
- {
137
- regex: /ERROR:\s+cannot drop function/i,
138
- label: 'function drop dependency issues',
139
- },
140
- {
141
- regex: /ERROR:\s+cannot drop schema/i,
142
- label: 'schema drop dependency issues',
143
- },
144
- {
145
- regex: /ERROR:\s+schema "[^"]+" already exists/i,
146
- label: 'schema exists warnings',
147
- },
148
- {
149
- regex: /ERROR:\s+type "[^"]+" already exists/i,
150
- label: 'type exists warnings',
151
- },
152
- {
153
- regex: /ERROR:\s+function "[^"]+" already exists/i,
154
- label: 'function exists warnings',
155
- },
156
- {
157
- regex: /ERROR:\s+relation "[^"]+" already exists/i,
158
- label: 'relation exists warnings',
159
- },
160
- {
161
- regex: /ERROR:\s+permission denied for schema/i,
162
- label: 'schema permission warnings',
163
- },
164
- {
165
- regex: /^(event trigger\s+|function\s+extensions\.)/i,
166
- label: 'dependency detail lines',
167
- },
168
- {
169
- regex: /ERROR:\s+must be able to SET ROLE/i,
170
- label: 'SET ROLE permission issues',
171
- },
172
- {
173
- regex: /ERROR:\s+must be owner of/i,
174
- label: 'ownership warnings',
175
- },
176
- {
177
- regex: /ERROR:\s+permission denied for table/i,
178
- label: 'table permission warnings',
179
- },
180
- {
181
- regex: /ERROR:\s+duplicate key value violates unique constraint/i,
182
- label: 'duplicate key conflicts',
183
- },
184
- {
185
- regex: /ERROR:\s+insert or update on table .* violates foreign key constraint/i,
186
- label: 'foreign key conflicts',
187
- },
188
- {
189
- regex: /ERROR:\s+grant options cannot be granted back to your own grantor/i,
190
- label: 'grant option warnings',
191
- },
192
- {
193
- regex: /ERROR:\s+function extensions\.pg_stat_statements_reset\(oid, oid, bigint\) does not exist/i,
194
- label: 'pg_stat_statements_reset missing',
195
- },
196
- {
197
- regex: /ERROR:\s+trailing junk after numeric literal/i,
198
- label: 'copy trailing junk errors',
199
- },
200
- {
201
- regex: /ERROR:\s+invalid command \\\S+/i,
202
- label: 'copy invalid command errors',
203
- },
204
- {
205
- regex: /WARNING:\s+no privileges were granted/i,
206
- label: 'privilege grant warnings',
207
- },
208
- {
209
- regex: /WARNING:\s+no privileges could be revoked/i,
210
- label: 'privilege revoke warnings',
211
- },
212
- {
213
- regex: /WARNING:\s+not all privileges were granted/i,
214
- label: 'partial privilege warnings',
215
- },
216
- {
217
- regex: /ERROR:\s+permission denied to set parameter/i,
218
- label: 'configuration permission warnings',
219
- },
220
- {
221
- regex: /^DETAIL:/i,
222
- label: 'suppressed detail lines',
223
- },
224
- {
225
- regex: /^HINT:/i,
226
- label: 'suppressed hint lines',
227
- },
228
- {
229
- regex: /^CONTEXT:\s+/i,
230
- label: 'suppressed context lines',
231
- },
232
- {
233
- regex: /^LINE \d+:/i,
234
- label: 'suppressed context lines',
235
- },
236
- {
237
- regex: /^\s*\^$/i,
238
- label: 'suppressed context lines',
239
- },
240
- ];
241
-
242
- const recordSuppressed = (label) => {
243
- if (!label) {
244
- return;
245
- }
246
- suppressedCounts.set(label, (suppressedCounts.get(label) || 0) + 1);
247
- };
248
-
249
- const handleLine = (line) => {
250
- const normalized = line.replace(/\r$/, '');
251
- if (!normalized.trim()) {
252
- return;
253
- }
254
- for (const { regex, label } of ignorePatterns) {
255
- if (regex.test(normalized)) {
256
- recordSuppressed(label);
257
- return;
258
- }
259
- }
260
- outputFn(normalized);
261
- };
262
-
263
- const finish = () => {
264
- if (finished) {
265
- return suppressedCounts;
266
- }
267
- if (buffer.length > 0) {
268
- handleLine(buffer);
269
- buffer = '';
270
- }
271
- finished = true;
272
- return suppressedCounts;
273
- };
274
-
275
- return {
276
- write(chunk) {
277
- if (finished) {
278
- return;
279
- }
280
- buffer += chunk.toString();
281
- let newlineIndex;
282
- while ((newlineIndex = buffer.indexOf('\n')) !== -1) {
283
- const line = buffer.slice(0, newlineIndex);
284
- handleLine(line);
285
- buffer = buffer.slice(newlineIndex + 1);
286
- }
287
- },
288
- finish,
289
- };
290
- }
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const { spawn } = require('child_process');
5
+ const readline = require('readline');
6
+
7
+ function createPrefixSuppressor(regexes, label) {
8
+ return (line) => {
9
+ for (const regex of regexes) {
10
+ if (regex.test(line)) {
11
+ return label;
12
+ }
13
+ }
14
+ return null;
15
+ };
16
+ }
17
+
18
+ const stdoutSuppressors = [
19
+ createPrefixSuppressor([/^SET\b/i, /^RESET\b/i], 'SET/RESET statements'),
20
+ createPrefixSuppressor([/^CREATE\b/i], 'CREATE statements'),
21
+ createPrefixSuppressor([/^ALTER\b/i], 'ALTER statements'),
22
+ createPrefixSuppressor([/^DROP\b/i], 'DROP statements'),
23
+ createPrefixSuppressor([/^COMMENT\b/i], 'COMMENT statements'),
24
+ createPrefixSuppressor([/^GRANT\b/i], 'GRANT statements'),
25
+ createPrefixSuppressor([/^REVOKE\b/i], 'REVOKE statements'),
26
+ createPrefixSuppressor([/^COPY\b/i], 'COPY statements'),
27
+ createPrefixSuppressor([/^SELECT\s+pg_catalog\.setval/i, /^\s*setval\b/i], 'setval outputs'),
28
+ createPrefixSuppressor([/^\(\d+\s+rows?\)$/i, /^\(1 row\)$/i], 'row count outputs'),
29
+ createPrefixSuppressor([/^\s*set_config\b/i], 'set_config outputs'),
30
+ createPrefixSuppressor([/^\s*-{3,}\s*$/], 'table separators'),
31
+ createPrefixSuppressor([/^\s*\d+\s*$/], 'numeric result lines'),
32
+ ];
33
+
34
+ function extractMultiplier(line) {
35
+ const match = line.match(/\sx(\d+)$/i);
36
+ return match ? parseInt(match[1], 10) : 1;
37
+ }
38
+
39
+ function createDuplicateCollapser(outputFn, options = {}) {
40
+ const suppressors = options.suppressors || [];
41
+ const suppressedCounts = new Map();
42
+ let buffer = '';
43
+ let lastLine = null;
44
+ let repeatCount = 0;
45
+ let finished = false;
46
+
47
+ const flush = () => {
48
+ if (lastLine === null) {
49
+ return;
50
+ }
51
+ const baseLine = lastLine;
52
+ const multiplier = extractMultiplier(baseLine);
53
+ const occurrences = repeatCount * multiplier;
54
+
55
+ for (const suppressor of suppressors) {
56
+ const label = suppressor(baseLine);
57
+ if (label) {
58
+ suppressedCounts.set(label, (suppressedCounts.get(label) || 0) + occurrences);
59
+ lastLine = null;
60
+ repeatCount = 0;
61
+ return;
62
+ }
63
+ }
64
+
65
+ const line =
66
+ repeatCount > 1 ? `${baseLine} x${repeatCount}` : baseLine;
67
+ outputFn(line);
68
+ lastLine = null;
69
+ repeatCount = 0;
70
+ };
71
+
72
+ const handleLine = (line) => {
73
+ const normalized = line.replace(/\r$/, '');
74
+ if (!normalized.trim()) {
75
+ return;
76
+ }
77
+ if (lastLine === null) {
78
+ lastLine = normalized;
79
+ repeatCount = 1;
80
+ return;
81
+ }
82
+ if (normalized === lastLine) {
83
+ repeatCount += 1;
84
+ return;
85
+ }
86
+ flush();
87
+ lastLine = normalized;
88
+ repeatCount = 1;
89
+ };
90
+
91
+ return {
92
+ write(chunk) {
93
+ if (finished) {
94
+ return;
95
+ }
96
+ buffer += chunk.toString();
97
+ let newlineIndex;
98
+ while ((newlineIndex = buffer.indexOf('\n')) !== -1) {
99
+ const line = buffer.slice(0, newlineIndex);
100
+ handleLine(line);
101
+ buffer = buffer.slice(newlineIndex + 1);
102
+ }
103
+ },
104
+ finish() {
105
+ if (finished) {
106
+ return suppressedCounts;
107
+ }
108
+ if (buffer.length > 0) {
109
+ handleLine(buffer);
110
+ buffer = '';
111
+ }
112
+ flush();
113
+ finished = true;
114
+ return suppressedCounts;
115
+ },
116
+ };
117
+ }
118
+
119
+ function createErrorFilter(outputFn) {
120
+ let buffer = '';
121
+ const suppressedCounts = new Map();
122
+ let finished = false;
123
+
124
+ const ignorePatterns = [
125
+ {
126
+ regex: /ERROR:\s+permission denied to change default privileges/i,
127
+ label: 'permission denied to change default privileges',
128
+ },
129
+ {
130
+ regex: /ERROR:\s+Non-superuser owned event trigger must execute a non-superuser owned function/i,
131
+ label: 'event trigger requires non-superuser function',
132
+ },
133
+ {
134
+ regex: /DETAIL:\s+The current user "postgres" is not a superuser and the function "extensions\.[^"]+" is owned by a superuser/i,
135
+ },
136
+ {
137
+ regex: /ERROR:\s+cannot drop function/i,
138
+ label: 'function drop dependency issues',
139
+ },
140
+ {
141
+ regex: /ERROR:\s+cannot drop schema/i,
142
+ label: 'schema drop dependency issues',
143
+ },
144
+ {
145
+ regex: /ERROR:\s+schema "[^"]+" already exists/i,
146
+ label: 'schema exists warnings',
147
+ },
148
+ {
149
+ regex: /ERROR:\s+type "[^"]+" already exists/i,
150
+ label: 'type exists warnings',
151
+ },
152
+ {
153
+ regex: /ERROR:\s+function "[^"]+" already exists/i,
154
+ label: 'function exists warnings',
155
+ },
156
+ {
157
+ regex: /ERROR:\s+relation "[^"]+" already exists/i,
158
+ label: 'relation exists warnings',
159
+ },
160
+ {
161
+ regex: /ERROR:\s+permission denied for schema/i,
162
+ label: 'schema permission warnings',
163
+ },
164
+ {
165
+ regex: /^(event trigger\s+|function\s+extensions\.)/i,
166
+ label: 'dependency detail lines',
167
+ },
168
+ {
169
+ regex: /ERROR:\s+must be able to SET ROLE/i,
170
+ label: 'SET ROLE permission issues',
171
+ },
172
+ {
173
+ regex: /ERROR:\s+must be owner of/i,
174
+ label: 'ownership warnings',
175
+ },
176
+ {
177
+ regex: /ERROR:\s+permission denied for table/i,
178
+ label: 'table permission warnings',
179
+ },
180
+ {
181
+ regex: /ERROR:\s+duplicate key value violates unique constraint/i,
182
+ label: 'duplicate key conflicts',
183
+ },
184
+ {
185
+ regex: /ERROR:\s+insert or update on table .* violates foreign key constraint/i,
186
+ label: 'foreign key conflicts',
187
+ },
188
+ {
189
+ regex: /ERROR:\s+grant options cannot be granted back to your own grantor/i,
190
+ label: 'grant option warnings',
191
+ },
192
+ {
193
+ regex: /ERROR:\s+function extensions\.pg_stat_statements_reset\(oid, oid, bigint\) does not exist/i,
194
+ label: 'pg_stat_statements_reset missing',
195
+ },
196
+ {
197
+ regex: /ERROR:\s+trailing junk after numeric literal/i,
198
+ label: 'copy trailing junk errors',
199
+ },
200
+ {
201
+ regex: /ERROR:\s+invalid command \\\S+/i,
202
+ label: 'copy invalid command errors',
203
+ },
204
+ {
205
+ regex: /WARNING:\s+no privileges were granted/i,
206
+ label: 'privilege grant warnings',
207
+ },
208
+ {
209
+ regex: /WARNING:\s+no privileges could be revoked/i,
210
+ label: 'privilege revoke warnings',
211
+ },
212
+ {
213
+ regex: /WARNING:\s+not all privileges were granted/i,
214
+ label: 'partial privilege warnings',
215
+ },
216
+ {
217
+ regex: /ERROR:\s+permission denied to set parameter/i,
218
+ label: 'configuration permission warnings',
219
+ },
220
+ {
221
+ regex: /^DETAIL:/i,
222
+ label: 'suppressed detail lines',
223
+ },
224
+ {
225
+ regex: /^HINT:/i,
226
+ label: 'suppressed hint lines',
227
+ },
228
+ {
229
+ regex: /^CONTEXT:\s+/i,
230
+ label: 'suppressed context lines',
231
+ },
232
+ {
233
+ regex: /^LINE \d+:/i,
234
+ label: 'suppressed context lines',
235
+ },
236
+ {
237
+ regex: /^\s*\^$/i,
238
+ label: 'suppressed context lines',
239
+ },
240
+ ];
241
+
242
+ const recordSuppressed = (label) => {
243
+ if (!label) {
244
+ return;
245
+ }
246
+ suppressedCounts.set(label, (suppressedCounts.get(label) || 0) + 1);
247
+ };
248
+
249
+ const handleLine = (line) => {
250
+ const normalized = line.replace(/\r$/, '');
251
+ if (!normalized.trim()) {
252
+ return;
253
+ }
254
+ for (const { regex, label } of ignorePatterns) {
255
+ if (regex.test(normalized)) {
256
+ recordSuppressed(label);
257
+ return;
258
+ }
259
+ }
260
+ outputFn(normalized);
261
+ };
262
+
263
+ const finish = () => {
264
+ if (finished) {
265
+ return suppressedCounts;
266
+ }
267
+ if (buffer.length > 0) {
268
+ handleLine(buffer);
269
+ buffer = '';
270
+ }
271
+ finished = true;
272
+ return suppressedCounts;
273
+ };
274
+
275
+ return {
276
+ write(chunk) {
277
+ if (finished) {
278
+ return;
279
+ }
280
+ buffer += chunk.toString();
281
+ let newlineIndex;
282
+ while ((newlineIndex = buffer.indexOf('\n')) !== -1) {
283
+ const line = buffer.slice(0, newlineIndex);
284
+ handleLine(line);
285
+ buffer = buffer.slice(newlineIndex + 1);
286
+ }
287
+ },
288
+ finish,
289
+ };
290
+ }
291
291
 
292
292
  // Load target database connection from env
293
293
  require('dotenv').config({ path: '.env.local' }); // ensure this has the new DB credentials
@@ -317,22 +317,22 @@ const user = connectionUrl.username;
317
317
  const password = connectionUrl.password;
318
318
  const sslMode = connectionUrl.searchParams.get('sslmode') || 'require';
319
319
 
320
- function formatBackupLabel(dirName) {
321
- const delimiter = '__';
322
- const idx = dirName.indexOf(delimiter);
323
- if (idx === -1) {
324
- return dirName;
325
- }
326
- const label = dirName.slice(idx + delimiter.length);
327
- return label || dirName;
328
- }
329
-
330
- // List available backup folders
331
- const backupsPath = path.join(__dirname, '../backups');
332
- if (!fs.existsSync(backupsPath)) {
333
- console.error("❌ Backups directory not found.");
334
- process.exit(1);
335
- }
320
+ function formatBackupLabel(dirName) {
321
+ const delimiter = '__';
322
+ const idx = dirName.indexOf(delimiter);
323
+ if (idx === -1) {
324
+ return dirName;
325
+ }
326
+ const label = dirName.slice(idx + delimiter.length);
327
+ return label || dirName;
328
+ }
329
+
330
+ // List available backup folders
331
+ const backupsPath = path.join(__dirname, '../backups');
332
+ if (!fs.existsSync(backupsPath)) {
333
+ console.error("❌ Backups directory not found.");
334
+ process.exit(1);
335
+ }
336
336
  const backupDirs = fs.readdirSync(backupsPath, { withFileTypes: true })
337
337
  .filter(dirent => dirent.isDirectory())
338
338
  .map(dirent => dirent.name);
@@ -344,27 +344,27 @@ if (backupDirs.length === 0) {
344
344
  process.exit(1);
345
345
  }
346
346
 
347
- // Show list of backups
348
- console.log("Available backups:");
349
- backupDirs.forEach((dir, index) => {
350
- const label = formatBackupLabel(dir);
351
- const prefix = index === 0 ? '(latest) ' : '';
352
- console.log(`${index + 1}. ${prefix}${label}`);
353
- });
354
-
355
- // Prompt user to choose a backup
356
- const rl = readline.createInterface({
357
- input: process.stdin,
358
- output: process.stdout
359
- });
360
- rl.question("Enter the number of the backup to restore (press Enter for latest): ", (answer) => {
361
- rl.close();
362
- const trimmed = answer.trim();
363
- const choice = trimmed === '' ? 1 : parseInt(trimmed, 10);
364
- if (isNaN(choice) || choice < 1 || choice > backupDirs.length) {
365
- console.error("❌ Invalid selection. Exiting.");
366
- process.exit(1);
367
- }
347
+ // Show list of backups
348
+ console.log("Available backups:");
349
+ backupDirs.forEach((dir, index) => {
350
+ const label = formatBackupLabel(dir);
351
+ const prefix = index === 0 ? '(latest) ' : '';
352
+ console.log(`${index + 1}. ${prefix}${label}`);
353
+ });
354
+
355
+ // Prompt user to choose a backup
356
+ const rl = readline.createInterface({
357
+ input: process.stdin,
358
+ output: process.stdout
359
+ });
360
+ rl.question("Enter the number of the backup to restore (press Enter for latest): ", (answer) => {
361
+ rl.close();
362
+ const trimmed = answer.trim();
363
+ const choice = trimmed === '' ? 1 : parseInt(trimmed, 10);
364
+ if (isNaN(choice) || choice < 1 || choice > backupDirs.length) {
365
+ console.error("❌ Invalid selection. Exiting.");
366
+ process.exit(1);
367
+ }
368
368
 
369
369
  const selectedDir = backupDirs[choice - 1];
370
370
  const dumpFile = path.join(backupsPath, selectedDir, 'dump.sql');
@@ -373,62 +373,62 @@ rl.question("Enter the number of the backup to restore (press Enter for latest):
373
373
  process.exit(1);
374
374
  }
375
375
 
376
- console.log(`🔄 Restoring database from backup "${selectedDir}"...`);
377
-
378
- // Spawn psql to execute the dump file
379
- const restoreArgs = [
380
- '-h', host,
381
- '-U', user,
382
- '-p', port,
383
- '-d', dbName,
384
- '-f', dumpFile
385
- ];
386
- const envVars = { ...process.env, PGPASSWORD: password, PGSSLMODE: sslMode };
387
- const psql = spawn('psql', restoreArgs, { env: envVars });
388
- const stdoutCollapser = createDuplicateCollapser(line => {
389
- process.stdout.write(`${line}\n`);
390
- }, { suppressors: stdoutSuppressors });
391
- const stderrFilter = createErrorFilter(line => {
392
- process.stderr.write(`${line}\n`);
393
- });
394
-
395
- psql.stdout.on('data', chunk => stdoutCollapser.write(chunk));
396
- psql.stderr.on('data', chunk => stderrFilter.write(chunk));
397
-
398
- psql.on('close', code => {
399
- const stdoutSuppressed = stdoutCollapser.finish();
400
- const stderrSuppressed = stderrFilter.finish();
401
-
402
- const formatSummary = (label, map) => {
403
- if (!map || map.size === 0) {
404
- return null;
405
- }
406
- const parts = [];
407
- let subtotal = 0;
408
- for (const [entryLabel, count] of map.entries()) {
409
- subtotal += count;
410
- parts.push(`${entryLabel} x${count}`);
411
- }
412
- return { label, subtotal, parts };
413
- };
414
-
415
- const summaries = [
416
- formatSummary('stdout', stdoutSuppressed),
417
- formatSummary('stderr', stderrSuppressed),
418
- ].filter(Boolean);
419
-
420
- if (summaries.length > 0) {
421
- const totalSuppressed = summaries.reduce((acc, { subtotal }) => acc + subtotal, 0);
422
- const detail = summaries
423
- .map(({ label, parts }) => `${label}: ${parts.join(', ')}`)
424
- .join(' | ');
425
- console.log(`ℹ️ Suppressed ${totalSuppressed} known restore messages (${detail}).`);
426
- }
427
-
428
- if (code === 0) {
429
- console.log("✅ Restore completed successfully.");
430
- } else {
431
- console.error(`❌ psql exited with code ${code}. The restore may have errors.`);
432
- }
433
- });
434
- });
376
+ console.log(`🔄 Restoring database from backup "${selectedDir}"...`);
377
+
378
+ // Spawn psql to execute the dump file
379
+ const restoreArgs = [
380
+ '-h', host,
381
+ '-U', user,
382
+ '-p', port,
383
+ '-d', dbName,
384
+ '-f', dumpFile
385
+ ];
386
+ const envVars = { ...process.env, PGPASSWORD: password, PGSSLMODE: sslMode };
387
+ const psql = spawn('psql', restoreArgs, { env: envVars });
388
+ const stdoutCollapser = createDuplicateCollapser(line => {
389
+ process.stdout.write(`${line}\n`);
390
+ }, { suppressors: stdoutSuppressors });
391
+ const stderrFilter = createErrorFilter(line => {
392
+ process.stderr.write(`${line}\n`);
393
+ });
394
+
395
+ psql.stdout.on('data', chunk => stdoutCollapser.write(chunk));
396
+ psql.stderr.on('data', chunk => stderrFilter.write(chunk));
397
+
398
+ psql.on('close', code => {
399
+ const stdoutSuppressed = stdoutCollapser.finish();
400
+ const stderrSuppressed = stderrFilter.finish();
401
+
402
+ const formatSummary = (label, map) => {
403
+ if (!map || map.size === 0) {
404
+ return null;
405
+ }
406
+ const parts = [];
407
+ let subtotal = 0;
408
+ for (const [entryLabel, count] of map.entries()) {
409
+ subtotal += count;
410
+ parts.push(`${entryLabel} x${count}`);
411
+ }
412
+ return { label, subtotal, parts };
413
+ };
414
+
415
+ const summaries = [
416
+ formatSummary('stdout', stdoutSuppressed),
417
+ formatSummary('stderr', stderrSuppressed),
418
+ ].filter(Boolean);
419
+
420
+ if (summaries.length > 0) {
421
+ const totalSuppressed = summaries.reduce((acc, { subtotal }) => acc + subtotal, 0);
422
+ const detail = summaries
423
+ .map(({ label, parts }) => `${label}: ${parts.join(', ')}`)
424
+ .join(' | ');
425
+ console.log(`ℹ️ Suppressed ${totalSuppressed} known restore messages (${detail}).`);
426
+ }
427
+
428
+ if (code === 0) {
429
+ console.log("✅ Restore completed successfully.");
430
+ } else {
431
+ console.error(`❌ psql exited with code ${code}. The restore may have errors.`);
432
+ }
433
+ });
434
+ });