create-nextblock 0.16.1 → 0.16.2
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.
- package/docker-template/.dockerignore +1 -1
- package/libs/db/tsconfig.lib.json +3 -3
- package/libs/editor/tsconfig.lib.json +3 -3
- package/libs/ui/tsconfig.lib.json +3 -3
- package/libs/utils/tsconfig.json +3 -3
- package/package.json +50 -50
- package/project.json +19 -19
- package/templates/nextblock-template/.dockerignore +1 -1
- package/templates/nextblock-template/.swcrc +30 -30
- package/templates/nextblock-template/AGENTS.md +9 -9
- package/templates/nextblock-template/CLAUDE.md +1 -1
- package/templates/nextblock-template/app/(auth-pages)/layout.tsx +9 -9
- package/templates/nextblock-template/app/(auth-pages)/post-sign-in/page.tsx +27 -27
- package/templates/nextblock-template/app/.well-known/ucp/route.ts +16 -16
- package/templates/nextblock-template/app/ToasterProvider.tsx +26 -26
- package/templates/nextblock-template/app/[slug]/pageClientActions.ts +7 -7
- package/templates/nextblock-template/app/actions/contactSellerActions.test.ts +280 -280
- package/templates/nextblock-template/app/actions/contactSellerActions.ts +222 -222
- package/templates/nextblock-template/app/actions/email-retry.test.ts +62 -62
- package/templates/nextblock-template/app/actions/email.ts +241 -241
- package/templates/nextblock-template/app/actions/formActions.ts +245 -245
- package/templates/nextblock-template/app/actions/interactions.ts +489 -489
- package/templates/nextblock-template/app/actions/threadActions.ts +166 -166
- package/templates/nextblock-template/app/actions/visibilityActions.ts +210 -210
- package/templates/nextblock-template/app/api/ai/seo/alt-text/route.ts +221 -221
- package/templates/nextblock-template/app/api/ai/seo/metadata/route.ts +186 -186
- package/templates/nextblock-template/app/api/checkout/freemius/sync/route.ts +29 -29
- package/templates/nextblock-template/app/api/checkout/route.ts +162 -162
- package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +3092 -3092
- package/templates/nextblock-template/app/api/mcp/route.ts +415 -415
- package/templates/nextblock-template/app/api/media/record/route.ts +160 -160
- package/templates/nextblock-template/app/api/search/route.ts +43 -43
- package/templates/nextblock-template/app/api/view/route.ts +114 -114
- package/templates/nextblock-template/app/api/visual-editing/block-draft/route.ts +47 -47
- package/templates/nextblock-template/app/api/visual-editing/product-draft/route.ts +47 -47
- package/templates/nextblock-template/app/auth/callback/route.ts +31 -31
- package/templates/nextblock-template/app/cart/page.tsx +7 -7
- package/templates/nextblock-template/app/checkout/UcpCartHydrator.tsx +20 -20
- package/templates/nextblock-template/app/checkout/page.tsx +57 -57
- package/templates/nextblock-template/app/cms/CmsClientLayout.tsx +558 -558
- package/templates/nextblock-template/app/cms/blocks/components/BlockEditorModal.tsx +241 -241
- package/templates/nextblock-template/app/cms/blocks/components/MediaLibraryModal.tsx +149 -149
- package/templates/nextblock-template/app/cms/blocks/editors/FormBlockEditor.tsx +304 -304
- package/templates/nextblock-template/app/cms/blocks/editors/ImageBlockEditor.tsx +406 -406
- package/templates/nextblock-template/app/cms/components/ContactReminderBanner.tsx +75 -75
- package/templates/nextblock-template/app/cms/components/CortexAiActiveContext.tsx +23 -23
- package/templates/nextblock-template/app/cms/components/CortexAiPageContext.tsx +58 -58
- package/templates/nextblock-template/app/cms/components/FeatureImageField.tsx +254 -254
- package/templates/nextblock-template/app/cms/components/FeedbackModal.tsx +36 -36
- package/templates/nextblock-template/app/cms/components/PaymentsReminderBanner.tsx +58 -58
- package/templates/nextblock-template/app/cms/components/VisibilityBadge.tsx +62 -62
- package/templates/nextblock-template/app/cms/components/VisibilityControl.tsx +542 -542
- package/templates/nextblock-template/app/cms/coupons/[id]/edit/page.tsx +16 -16
- package/templates/nextblock-template/app/cms/coupons/page.tsx +16 -16
- package/templates/nextblock-template/app/cms/dashboard/actions.ts +228 -228
- package/templates/nextblock-template/app/cms/dashboard/components/DashboardComponents.tsx +200 -200
- package/templates/nextblock-template/app/cms/inquiries/actions.ts +66 -66
- package/templates/nextblock-template/app/cms/inquiries/page.tsx +12 -12
- package/templates/nextblock-template/app/cms/interactions/page.tsx +12 -12
- package/templates/nextblock-template/app/cms/layout.tsx +101 -101
- package/templates/nextblock-template/app/cms/media/components/FolderNavigator.tsx +273 -273
- package/templates/nextblock-template/app/cms/media/components/FolderTree.tsx +122 -122
- package/templates/nextblock-template/app/cms/media/components/MediaGridClient.tsx +69 -69
- package/templates/nextblock-template/app/cms/messages/MessagesClient.tsx +661 -661
- package/templates/nextblock-template/app/cms/messages/actions.ts +404 -404
- package/templates/nextblock-template/app/cms/messages/loadInbox.ts +333 -333
- package/templates/nextblock-template/app/cms/messages/page.tsx +87 -87
- package/templates/nextblock-template/app/cms/messages/require-admin.ts +37 -37
- package/templates/nextblock-template/app/cms/navigation/components/NavigationMenuDnd.tsx +3 -3
- package/templates/nextblock-template/app/cms/pages/components/PageForm.tsx +649 -649
- package/templates/nextblock-template/app/cms/posts/components/PostForm.tsx +618 -618
- package/templates/nextblock-template/app/cms/products/[id]/edit/page.tsx +370 -370
- package/templates/nextblock-template/app/cms/products/attributes/page.tsx +12 -12
- package/templates/nextblock-template/app/cms/products/inventory/page.tsx +13 -13
- package/templates/nextblock-template/app/cms/products/productFormData.ts +133 -133
- package/templates/nextblock-template/app/cms/products/settings/page.tsx +5 -5
- package/templates/nextblock-template/app/cms/revisions/actions.ts +332 -332
- package/templates/nextblock-template/app/cms/revisions/service.test.ts +498 -498
- package/templates/nextblock-template/app/cms/revisions/service.ts +569 -569
- package/templates/nextblock-template/app/cms/revisions/utils.ts +304 -304
- package/templates/nextblock-template/app/cms/settings/cortex-ai/CortexAiSettingsClient.tsx +948 -948
- package/templates/nextblock-template/app/cms/settings/cortex-ai/McpServerSettingsCard.tsx +628 -628
- package/templates/nextblock-template/app/cms/settings/cortex-ai/mcp-actions.ts +230 -230
- package/templates/nextblock-template/app/cms/settings/cortex-ai/require-admin.ts +34 -34
- package/templates/nextblock-template/app/cms/settings/currencies/actions.ts +331 -331
- package/templates/nextblock-template/app/cms/settings/currencies/page.tsx +494 -494
- package/templates/nextblock-template/app/cms/settings/email/components/EmailForm.tsx +227 -227
- package/templates/nextblock-template/app/cms/settings/extra-translations/ExtraTranslationsWorkspace.tsx +767 -767
- package/templates/nextblock-template/app/cms/settings/extra-translations/actions.ts +276 -276
- package/templates/nextblock-template/app/cms/settings/extra-translations/page.tsx +93 -93
- package/templates/nextblock-template/app/cms/settings/global-css/components/ThemeEditor.tsx +382 -382
- package/templates/nextblock-template/app/cms/settings/global-css/components/ThemeManager.tsx +267 -267
- package/templates/nextblock-template/app/cms/settings/global-css/page.tsx +40 -40
- package/templates/nextblock-template/app/cms/settings/global-css/theme-actions.ts +259 -259
- package/templates/nextblock-template/app/cms/settings/logos/[id]/edit/page.tsx +7 -7
- package/templates/nextblock-template/app/cms/settings/logos/components/BrandingSettingsForm.tsx +339 -339
- package/templates/nextblock-template/app/cms/settings/logos/new/page.tsx +8 -8
- package/templates/nextblock-template/app/cms/settings/seo/RedirectsCard.tsx +514 -514
- package/templates/nextblock-template/app/cms/settings/seo/RobotsCard.tsx +529 -529
- package/templates/nextblock-template/app/cms/settings/seo/SeoSettingsClient.tsx +57 -57
- package/templates/nextblock-template/app/cms/settings/seo/actions.ts +448 -448
- package/templates/nextblock-template/app/cms/settings/seo/mappers.ts +93 -93
- package/templates/nextblock-template/app/cms/settings/seo/page.tsx +46 -46
- package/templates/nextblock-template/app/cms/settings/seo/require-admin.ts +47 -47
- package/templates/nextblock-template/app/cms/settings/site-scripts/page.tsx +51 -51
- package/templates/nextblock-template/app/cms/settings/taxes/page.tsx +21 -21
- package/templates/nextblock-template/app/cms/shipping/page.tsx +20 -20
- package/templates/nextblock-template/app/cms/users/components/DeleteUserButton.tsx +12 -12
- package/templates/nextblock-template/app/layout.tsx +671 -671
- package/templates/nextblock-template/app/lib/seo.ts +319 -319
- package/templates/nextblock-template/app/lib/ucp/protocol.ts +190 -190
- package/templates/nextblock-template/app/lib/ucp/server.test.ts +56 -56
- package/templates/nextblock-template/app/product/[slug]/page.tsx +502 -502
- package/templates/nextblock-template/app/profile/ProfilePageHeader.tsx +16 -16
- package/templates/nextblock-template/app/profile/ProfilePageMissingState.tsx +9 -9
- package/templates/nextblock-template/app/profile/account-links.ts +22 -22
- package/templates/nextblock-template/app/profile/orders/CustomerOrdersPageClient.tsx +124 -124
- package/templates/nextblock-template/app/profile/orders/page.tsx +19 -19
- package/templates/nextblock-template/app/profile/password/PasswordSettingsPageClient.tsx +128 -128
- package/templates/nextblock-template/app/profile/password/actions.ts +59 -59
- package/templates/nextblock-template/app/profile/password/page.tsx +27 -27
- package/templates/nextblock-template/app/providers.tsx +96 -96
- package/templates/nextblock-template/app/robots.ts +123 -123
- package/templates/nextblock-template/app/thread/ThreadView.tsx +164 -164
- package/templates/nextblock-template/app/thread/[token]/route.ts +57 -57
- package/templates/nextblock-template/app/thread/layout.tsx +15 -15
- package/templates/nextblock-template/app/thread/page.tsx +98 -98
- package/templates/nextblock-template/app/ucp/v1/carts/[id]/cancel/route.ts +38 -38
- package/templates/nextblock-template/app/ucp/v1/carts/[id]/route.ts +68 -68
- package/templates/nextblock-template/app/ucp/v1/carts/route.ts +35 -35
- package/templates/nextblock-template/app/ucp/v1/catalog/lookup/route.ts +35 -35
- package/templates/nextblock-template/app/ucp/v1/catalog/product/route.ts +35 -35
- package/templates/nextblock-template/app/ucp/v1/catalog/search/route.ts +34 -34
- package/templates/nextblock-template/components/BlockRenderer.tsx +312 -312
- package/templates/nextblock-template/components/CartDrawerLoader.tsx +7 -7
- package/templates/nextblock-template/components/CartTranslator.tsx +210 -210
- package/templates/nextblock-template/components/ContactSellerSection.tsx +188 -188
- package/templates/nextblock-template/components/DeferredCartDrawer.tsx +23 -23
- package/templates/nextblock-template/components/DeferredCartTranslator.tsx +51 -51
- package/templates/nextblock-template/components/DeferredGlobalSearch.tsx +68 -68
- package/templates/nextblock-template/components/DeferredGoogleTagManager.tsx +70 -70
- package/templates/nextblock-template/components/DeferredSpeedInsights.tsx +69 -69
- package/templates/nextblock-template/components/FooterNavigation.tsx +32 -32
- package/templates/nextblock-template/components/GlobalSearch.tsx +557 -557
- package/templates/nextblock-template/components/Header.tsx +38 -38
- package/templates/nextblock-template/components/HtmlScriptExecutor.tsx +47 -47
- package/templates/nextblock-template/components/LanguageSwitcher.tsx +2 -2
- package/templates/nextblock-template/components/PostCommentsSection.tsx +378 -378
- package/templates/nextblock-template/components/ProductReviewsSection.tsx +426 -426
- package/templates/nextblock-template/components/SiteScripts.tsx +56 -56
- package/templates/nextblock-template/components/StaffReplies.tsx +102 -102
- package/templates/nextblock-template/components/blocks/PostCardSkeleton.tsx +12 -12
- package/templates/nextblock-template/components/blocks/PostsGridBlock.tsx +12 -12
- package/templates/nextblock-template/components/blocks/PostsGridClient.tsx +48 -48
- package/templates/nextblock-template/components/blocks/TestimonialBlock.tsx +9 -9
- package/templates/nextblock-template/components/blocks/ecommerceRendererLoaders.ts +23 -23
- package/templates/nextblock-template/components/blocks/publicRendererLoaders.ts +25 -25
- package/templates/nextblock-template/components/blocks/renderers/ButtonBlockRenderer.tsx +92 -92
- package/templates/nextblock-template/components/blocks/renderers/CartBlockRenderer.tsx +18 -18
- package/templates/nextblock-template/components/blocks/renderers/CheckoutBlockRenderer.tsx +20 -20
- package/templates/nextblock-template/components/blocks/renderers/FeaturedProductBlockRenderer.tsx +25 -25
- package/templates/nextblock-template/components/blocks/renderers/FormBlockRenderer.tsx +385 -385
- package/templates/nextblock-template/components/blocks/renderers/PostsGridBlockRenderer.tsx +24 -24
- package/templates/nextblock-template/components/blocks/renderers/ProductDetailsBlockRenderer.tsx +157 -157
- package/templates/nextblock-template/components/blocks/renderers/ProductGridBlockRenderer.tsx +34 -34
- package/templates/nextblock-template/components/blocks/renderers/SectionBlockRenderer.tsx +612 -612
- package/templates/nextblock-template/components/blocks/renderers/TestimonialBlockRenderer.tsx +57 -57
- package/templates/nextblock-template/components/blocks/renderers/inline/AlertWidgetRenderer.tsx +2 -2
- package/templates/nextblock-template/components/blocks/renderers/inline/CtaWidgetRenderer.tsx +2 -2
- package/templates/nextblock-template/components/blocks/types.ts +7 -7
- package/templates/nextblock-template/components/commerce/PaymentReadinessBoundary.tsx +32 -32
- package/templates/nextblock-template/components/env-var-warning.tsx +3 -3
- package/templates/nextblock-template/components/form-message.tsx +32 -32
- package/templates/nextblock-template/components/seo/GenerateMetaButton.tsx +137 -137
- package/templates/nextblock-template/components/seo/PageSeoAuditSection.tsx +244 -244
- package/templates/nextblock-template/components/seo/SeoAuditPanel.tsx +749 -749
- package/templates/nextblock-template/components/seo/SeoIssueList.tsx +195 -195
- package/templates/nextblock-template/components/seo/SeoScoreDial.tsx +144 -144
- package/templates/nextblock-template/components/seo/SocialPreview.tsx +243 -243
- package/templates/nextblock-template/components/seo/SocialPreviewDialog.tsx +110 -110
- package/templates/nextblock-template/components/submit-button.tsx +23 -23
- package/templates/nextblock-template/components/theme-icon.tsx +78 -78
- package/templates/nextblock-template/components/theme-switcher.tsx +85 -85
- package/templates/nextblock-template/context/AuthContext.tsx +23 -23
- package/templates/nextblock-template/context/ThemeCatalogContext.tsx +44 -44
- package/templates/nextblock-template/docs/01-PROJECT-OVERVIEW.md +94 -94
- package/templates/nextblock-template/docs/03-CMS-AND-EDITOR.md +77 -77
- package/templates/nextblock-template/docs/13-STAYING-UP-TO-DATE.md +372 -372
- package/templates/nextblock-template/docs/14-MESSAGES-INBOX.md +309 -309
- package/templates/nextblock-template/docs/README.md +42 -42
- package/templates/nextblock-template/docs/TECHNICAL_SPECIFICATION.md +12506 -12506
- package/templates/nextblock-template/hooks/use-hotkeys.ts +21 -21
- package/templates/nextblock-template/hooks/useGlobalSearch.ts +101 -101
- package/templates/nextblock-template/index.d.ts +7 -7
- package/templates/nextblock-template/lib/auth-redirects.ts +46 -46
- package/templates/nextblock-template/lib/blocks/blockColors.test.ts +134 -134
- package/templates/nextblock-template/lib/blocks/blockColors.ts +175 -175
- package/templates/nextblock-template/lib/blocks/blockRegistry.ts +761 -761
- package/templates/nextblock-template/lib/blocks/inlineScriptNonce.ts +20 -20
- package/templates/nextblock-template/lib/cms/contact-reminder.ts +64 -64
- package/templates/nextblock-template/lib/cms/payments-reminder.test.ts +135 -0
- package/templates/nextblock-template/lib/cms/payments-reminder.ts +30 -23
- package/templates/nextblock-template/lib/cms/unread-messages.ts +42 -42
- package/templates/nextblock-template/lib/commerce/seller-contact.ts +162 -162
- package/templates/nextblock-template/lib/config/email-settings.ts +323 -323
- package/templates/nextblock-template/lib/config/email-tls.test.ts +57 -57
- package/templates/nextblock-template/lib/cortex-ai/alt-text-request.ts +86 -86
- package/templates/nextblock-template/lib/cortex-ai/sandbox-headers.ts +60 -60
- package/templates/nextblock-template/lib/email/placeholder-address.test.ts +59 -59
- package/templates/nextblock-template/lib/email/placeholder-address.ts +39 -39
- package/templates/nextblock-template/lib/messages/thread-reference.test.ts +70 -70
- package/templates/nextblock-template/lib/messages/thread-token.test.ts +93 -93
- package/templates/nextblock-template/lib/messages/thread-token.ts +157 -157
- package/templates/nextblock-template/lib/messages/threads.ts +579 -579
- package/templates/nextblock-template/lib/posts/readTime.ts +60 -60
- package/templates/nextblock-template/lib/publishing/viewUrl.ts +26 -26
- package/templates/nextblock-template/lib/search/types.ts +27 -27
- package/templates/nextblock-template/lib/seo/alt-text-write-back.test.ts +154 -154
- package/templates/nextblock-template/lib/seo/alt-text-write-back.ts +109 -109
- package/templates/nextblock-template/lib/seo/block-content.ts +123 -123
- package/templates/nextblock-template/lib/seo/fix-prompts.test.ts +242 -242
- package/templates/nextblock-template/lib/seo/fix-prompts.ts +204 -204
- package/templates/nextblock-template/lib/seo/page-audit-context.tsx +140 -140
- package/templates/nextblock-template/lib/seo/page-document.test.ts +350 -350
- package/templates/nextblock-template/lib/seo/page-document.ts +412 -412
- package/templates/nextblock-template/lib/seo/redirect-store.test.ts +479 -479
- package/templates/nextblock-template/lib/seo/redirect-store.ts +466 -466
- package/templates/nextblock-template/lib/seo/robots-settings-signature.test.ts +102 -102
- package/templates/nextblock-template/lib/seo/robots-settings-signature.ts +41 -41
- package/templates/nextblock-template/lib/seo/robots-txt.test.ts +370 -370
- package/templates/nextblock-template/lib/seo/robots-txt.ts +510 -510
- package/templates/nextblock-template/lib/setup/migrations-bundle.ts +177 -177
- package/templates/nextblock-template/lib/site-scripts/revisions.ts +71 -71
- package/templates/nextblock-template/lib/site-scripts/types.ts +46 -46
- package/templates/nextblock-template/lib/site-url.test.ts +89 -89
- package/templates/nextblock-template/lib/site-url.ts +102 -102
- package/templates/nextblock-template/lib/themes/buildThemeCss.ts +124 -124
- package/templates/nextblock-template/lib/themes/tokenColor.ts +31 -31
- package/templates/nextblock-template/lib/themes/tokens.ts +143 -143
- package/templates/nextblock-template/lib/visual-editing/draft-content.test.ts +105 -105
- package/templates/nextblock-template/lib/visual-editing/draft-route.test.ts +42 -42
- package/templates/nextblock-template/lib/visual-editing/edit-info.test.ts +143 -143
- package/templates/nextblock-template/lib/visual-editing/edit-info.ts +94 -94
- package/templates/nextblock-template/lib/visual-editing/product-drafts.test.ts +81 -81
- package/templates/nextblock-template/lib/zod-config.ts +5 -5
- package/templates/nextblock-template/next-env.d.ts +1 -2
- package/templates/nextblock-template/package.json +1 -1
- package/templates/nextblock-template/postcss.config.js +6 -6
- package/templates/nextblock-template/scripts/backup.js +115 -115
- package/templates/nextblock-template/scripts/restore.js +385 -385
- package/templates/nextblock-template/scripts/validate-editor-block-schema.ts +112 -112
- package/templates/nextblock-template/tailwind.config.js +25 -25
- package/templates/nextblock-template/tools/build-migrate.mjs +102 -102
- package/templates/nextblock-template/tools/configure-supabase-auth.js +282 -282
- package/templates/nextblock-template/tools/deploy-supabase.js +159 -159
- package/templates/nextblock-template/tools/lib/migrate-core.mjs +569 -569
- package/templates/nextblock-template/tools/update.mjs +1303 -1303
- package/tsconfig.base.json +3 -3
|
@@ -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
|
+
});
|