@nextsparkjs/core 0.1.0-beta.189 → 0.1.0-beta.190
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/api/keys/CreateApiKeyDialog.d.ts.map +1 -1
- package/dist/components/api/keys/CreateApiKeyDialog.js +2 -1
- package/dist/components/app/layouts/PublicFooter.d.ts.map +1 -1
- package/dist/components/app/layouts/PublicFooter.js +2 -23
- package/dist/components/app/misc/ThemeToggle.d.ts.map +1 -1
- package/dist/components/app/misc/ThemeToggle.js +2 -1
- package/dist/components/auth/forms/LoginForm.js +1 -1
- package/dist/components/auth/forms/SignupForm.d.ts.map +1 -1
- package/dist/components/auth/forms/SignupForm.js +2 -1
- package/dist/components/auth/pages/AuthErrorPage.d.ts.map +1 -1
- package/dist/components/auth/pages/AuthErrorPage.js +2 -1
- package/dist/components/billing/ManageBillingButton.d.ts.map +1 -1
- package/dist/components/billing/ManageBillingButton.js +2 -1
- package/dist/components/dashboard/block-editor/array-field.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/array-field.js +2 -1
- package/dist/components/dashboard/block-editor/block-picker.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/block-picker.js +2 -1
- package/dist/components/dashboard/block-editor/builder-editor-view.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/builder-editor-view.js +3 -2
- package/dist/components/dashboard/block-editor/config-panel.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/config-panel.js +3 -2
- package/dist/components/dashboard/block-editor/dynamic-form.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/dynamic-form.js +2 -1
- package/dist/components/dashboard/block-editor/entity-fields-sidebar.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/entity-fields-sidebar.js +3 -2
- package/dist/components/dashboard/block-editor/pattern-reference-preview.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/pattern-reference-preview.js +2 -1
- package/dist/components/dashboard/layouts/TopNavbar.d.ts.map +1 -1
- package/dist/components/dashboard/layouts/TopNavbar.js +4 -3
- package/dist/components/dashboard/misc/SearchDropdown.d.ts.map +1 -1
- package/dist/components/dashboard/misc/SearchDropdown.js +2 -1
- package/dist/components/dashboard/mobile/MobileTopBar.d.ts.map +1 -1
- package/dist/components/dashboard/mobile/MobileTopBar.js +2 -1
- package/dist/components/devtools/BlockDetailViewer.d.ts.map +1 -1
- package/dist/components/devtools/BlockDetailViewer.js +2 -1
- package/dist/components/devtools/BlocksViewer.d.ts.map +1 -1
- package/dist/components/devtools/BlocksViewer.js +2 -1
- package/dist/components/devtools/ConfigViewer.d.ts.map +1 -1
- package/dist/components/devtools/ConfigViewer.js +3 -2
- package/dist/components/devtools/TestCasesViewer.d.ts.map +1 -1
- package/dist/components/devtools/TestCasesViewer.js +3 -2
- package/dist/components/devtools/TestCoverageDashboard.d.ts.map +1 -1
- package/dist/components/devtools/TestCoverageDashboard.js +2 -1
- package/dist/components/devtools/api-explorer/ApiDocsModal.d.ts.map +1 -1
- package/dist/components/devtools/api-explorer/ApiDocsModal.js +0 -0
- package/dist/components/devtools/api-explorer/ApiExplorer.d.ts.map +1 -1
- package/dist/components/devtools/api-explorer/ApiExplorer.js +2 -1
- package/dist/components/devtools/scheduled-actions/actions-table.d.ts.map +1 -1
- package/dist/components/devtools/scheduled-actions/actions-table.js +3 -2
- package/dist/components/docs/docs-sidebar.d.ts.map +1 -1
- package/dist/components/docs/docs-sidebar.js +3 -2
- package/dist/components/entities/EntityFieldRenderer.d.ts.map +1 -1
- package/dist/components/entities/EntityFieldRenderer.js +2 -1
- package/dist/components/entities/EntityList.d.ts.map +1 -1
- package/dist/components/entities/EntityList.js +2 -1
- package/dist/components/entities/EntityTable.d.ts.map +1 -1
- package/dist/components/entities/EntityTable.js +3 -2
- package/dist/components/entities/ErrorBoundary.d.ts.map +1 -1
- package/dist/components/entities/ErrorBoundary.js +2 -1
- package/dist/components/entities/wrappers/EntityDetailWrapper.d.ts.map +1 -1
- package/dist/components/entities/wrappers/EntityDetailWrapper.js +3 -2
- package/dist/components/media/MediaCard.d.ts.map +1 -1
- package/dist/components/media/MediaCard.js +2 -1
- package/dist/components/media/MediaDetailPanel.d.ts.map +1 -1
- package/dist/components/media/MediaDetailPanel.js +2 -1
- package/dist/components/media/MediaList.d.ts.map +1 -1
- package/dist/components/media/MediaList.js +2 -1
- package/dist/components/media/MediaSelector.d.ts.map +1 -1
- package/dist/components/media/MediaSelector.js +2 -1
- package/dist/components/media/MediaUploadZone.d.ts.map +1 -1
- package/dist/components/media/MediaUploadZone.js +2 -1
- package/dist/components/public/entities/PublicEntityDetail.d.ts.map +1 -1
- package/dist/components/public/entities/PublicEntityDetail.js +2 -1
- package/dist/components/teams/TeamPendingInvitations.d.ts.map +1 -1
- package/dist/components/teams/TeamPendingInvitations.js +3 -2
- package/dist/components/ui/accordion.js +1 -1
- package/dist/components/ui/audio-upload.d.ts.map +1 -1
- package/dist/components/ui/audio-upload.js +2 -1
- package/dist/components/ui/avatar.d.ts +17 -1
- package/dist/components/ui/avatar.d.ts.map +1 -1
- package/dist/components/ui/avatar.js +29 -2
- package/dist/components/ui/badge.js +1 -1
- package/dist/components/ui/breadcrumb.d.ts +13 -0
- package/dist/components/ui/breadcrumb.d.ts.map +1 -1
- package/dist/components/ui/breadcrumb.js +23 -10
- package/dist/components/ui/button.js +1 -1
- package/dist/components/ui/card.js +1 -1
- package/dist/components/ui/checkbox.js +1 -1
- package/dist/components/ui/file-upload.d.ts.map +1 -1
- package/dist/components/ui/file-upload.js +2 -1
- package/dist/components/ui/image-upload.d.ts.map +1 -1
- package/dist/components/ui/image-upload.js +4 -3
- package/dist/components/ui/input.js +1 -1
- package/dist/components/ui/label.js +1 -1
- package/dist/components/ui/optimized-image.d.ts.map +1 -1
- package/dist/components/ui/optimized-image.js +5 -0
- package/dist/components/ui/pagination.d.ts +1 -1
- package/dist/components/ui/pagination.d.ts.map +1 -1
- package/dist/components/ui/pagination.js +3 -0
- package/dist/components/ui/progress.js +1 -1
- package/dist/components/ui/relation-display.d.ts.map +1 -1
- package/dist/components/ui/relation-display.js +3 -2
- package/dist/components/ui/separator.js +1 -1
- package/dist/components/ui/simple-relation-select.d.ts.map +1 -1
- package/dist/components/ui/simple-relation-select.js +7 -6
- package/dist/components/ui/skeleton.js +1 -1
- package/dist/components/ui/slider.js +1 -1
- package/dist/components/ui/switch.js +1 -1
- package/dist/components/ui/tabs.js +1 -1
- package/dist/components/ui/textarea.js +1 -1
- package/dist/components/ui/user-display.d.ts.map +1 -1
- package/dist/components/ui/user-display.js +2 -1
- package/dist/components/ui/user-select.d.ts.map +1 -1
- package/dist/components/ui/user-select.js +2 -1
- package/dist/components/ui/video-upload.d.ts.map +1 -1
- package/dist/components/ui/video-upload.js +3 -2
- package/dist/components/users/tables/UsersTable.d.ts.map +1 -1
- package/dist/components/users/tables/UsersTable.js +3 -2
- package/dist/contexts/SubscriptionContext.d.ts.map +1 -1
- package/dist/contexts/SubscriptionContext.js +4 -3
- package/dist/contexts/TeamContext.d.ts.map +1 -1
- package/dist/contexts/TeamContext.js +3 -2
- package/dist/hooks/useAccountPreferencesSync.d.ts.map +1 -1
- package/dist/hooks/useAccountPreferencesSync.js +2 -1
- package/dist/hooks/useApiExplorerNavigation.d.ts.map +1 -1
- package/dist/hooks/useApiExplorerNavigation.js +2 -1
- package/dist/hooks/useAuth.d.ts.map +1 -1
- package/dist/hooks/useAuth.js +3 -2
- package/dist/hooks/useEnsureUserMetadata.d.ts.map +1 -1
- package/dist/hooks/useEnsureUserMetadata.js +3 -2
- package/dist/hooks/useEntityWithMeta.d.ts.map +1 -1
- package/dist/hooks/useEntityWithMeta.js +3 -2
- package/dist/hooks/useInvoice.d.ts.map +1 -1
- package/dist/hooks/useInvoice.js +2 -1
- package/dist/hooks/useInvoices.d.ts.map +1 -1
- package/dist/hooks/useInvoices.js +2 -1
- package/dist/hooks/useMedia.d.ts.map +1 -1
- package/dist/hooks/useMedia.js +10 -9
- package/dist/hooks/useMediaUpload.d.ts.map +1 -1
- package/dist/hooks/useMediaUpload.js +2 -1
- package/dist/hooks/useMembership.d.ts.map +1 -1
- package/dist/hooks/useMembership.js +3 -2
- package/dist/hooks/usePatternUsages.d.ts.map +1 -1
- package/dist/hooks/usePatternUsages.js +3 -2
- package/dist/hooks/usePrefetch.d.ts.map +1 -1
- package/dist/hooks/usePrefetch.js +3 -2
- package/dist/hooks/useQuota.d.ts.map +1 -1
- package/dist/hooks/useQuota.js +2 -1
- package/dist/hooks/useTeamInvitations.d.ts.map +1 -1
- package/dist/hooks/useTeamInvitations.js +4 -3
- package/dist/hooks/useTeamMembers.d.ts.map +1 -1
- package/dist/hooks/useTeamMembers.js +5 -4
- package/dist/hooks/useTeamUpdate.d.ts.map +1 -1
- package/dist/hooks/useTeamUpdate.js +2 -1
- package/dist/hooks/useUserProfile.d.ts.map +1 -1
- package/dist/hooks/useUserProfile.js +3 -2
- package/dist/hooks/useUserSettings.d.ts.map +1 -1
- package/dist/hooks/useUserSettings.js +3 -2
- package/dist/lib/actions/entity.actions.d.ts.map +1 -1
- package/dist/lib/actions/entity.actions.js +2 -1
- package/dist/lib/api/entities.d.ts.map +1 -1
- package/dist/lib/api/entities.js +4 -3
- package/dist/lib/auth/verify-email-link.d.ts +10 -0
- package/dist/lib/auth/verify-email-link.d.ts.map +1 -0
- package/dist/lib/auth/verify-email-link.js +14 -0
- package/dist/lib/auth-client.d.ts +2 -0
- package/dist/lib/auth-client.d.ts.map +1 -1
- package/dist/lib/auth-client.js +4 -0
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +10 -2
- package/dist/lib/base-path.d.ts +85 -0
- package/dist/lib/base-path.d.ts.map +1 -0
- package/dist/lib/base-path.js +79 -0
- package/dist/lib/billing/schema.d.ts +1 -1
- package/dist/lib/billing/schema.d.ts.map +1 -1
- package/dist/lib/billing/schema.js +1 -1
- package/dist/lib/blocks/sanitize-html.d.ts.map +1 -1
- package/dist/lib/blocks/sanitize-html.js +18 -2
- package/dist/lib/cache/revalidate-tag.d.ts +14 -0
- package/dist/lib/cache/revalidate-tag.d.ts.map +1 -0
- package/dist/lib/cache/revalidate-tag.js +8 -0
- package/dist/lib/config/dashboard-schema.d.ts +1 -1
- package/dist/lib/config/dashboard-schema.d.ts.map +1 -1
- package/dist/lib/config/dashboard-schema.js +1 -1
- package/dist/lib/config/types.d.ts +36 -16
- package/dist/lib/config/types.d.ts.map +1 -1
- package/dist/lib/docs/access.d.ts +28 -0
- package/dist/lib/docs/access.d.ts.map +1 -0
- package/dist/lib/docs/access.js +16 -0
- package/dist/lib/docs/parser.d.ts.map +1 -1
- package/dist/lib/docs/parser.js +2 -1
- package/dist/lib/docs/remark-doc-links.d.ts +14 -0
- package/dist/lib/docs/remark-doc-links.d.ts.map +1 -0
- package/dist/lib/docs/remark-doc-links.js +38 -0
- package/dist/lib/entities/api-generator.d.ts +1 -1
- package/dist/lib/entities/api-generator.d.ts.map +1 -1
- package/dist/lib/entities/api-generator.js +1 -1
- package/dist/lib/entities/schema-generator.d.ts +1 -1
- package/dist/lib/entities/schema-generator.d.ts.map +1 -1
- package/dist/lib/entities/schema-generator.js +1 -1
- package/dist/lib/mcp/engine.js +1 -1
- package/dist/lib/mcp/schema-builder.d.ts +1 -1
- package/dist/lib/mcp/schema-builder.d.ts.map +1 -1
- package/dist/lib/mcp/schema-builder.js +1 -1
- package/dist/lib/mcp/tool-generator.js +1 -1
- package/dist/lib/mcp/types.d.ts +1 -1
- package/dist/lib/mcp/types.d.ts.map +1 -1
- package/dist/lib/media/schemas.d.ts +1 -1
- package/dist/lib/media/schemas.d.ts.map +1 -1
- package/dist/lib/media/schemas.js +1 -1
- package/dist/lib/middleware/index.d.ts +5 -1
- package/dist/lib/middleware/index.d.ts.map +1 -1
- package/dist/lib/middleware/index.js +3 -2
- package/dist/lib/services/meta.service.d.ts +60 -0
- package/dist/lib/services/meta.service.d.ts.map +1 -1
- package/dist/lib/services/meta.service.js +132 -12
- package/dist/lib/services/middleware.service.d.ts.map +1 -1
- package/dist/lib/services/middleware.service.js +3 -2
- package/dist/lib/teams/schema.d.ts +1 -1
- package/dist/lib/teams/schema.d.ts.map +1 -1
- package/dist/lib/teams/schema.js +1 -1
- package/dist/lib/teams/teams.api.d.ts.map +1 -1
- package/dist/lib/teams/teams.api.js +4 -3
- package/dist/lib/user-data-client.d.ts.map +1 -1
- package/dist/lib/user-data-client.js +3 -2
- package/dist/lib/validation/invoices.d.ts +1 -1
- package/dist/lib/validation/invoices.d.ts.map +1 -1
- package/dist/lib/validation/invoices.js +1 -1
- package/dist/lib/validation-schemas.d.ts +1 -1
- package/dist/lib/validation-schemas.d.ts.map +1 -1
- package/dist/lib/validation-schemas.js +1 -1
- package/dist/lib/validation.d.ts +1 -1
- package/dist/lib/validation.d.ts.map +1 -1
- package/dist/lib/validation.js +1 -1
- package/dist/messages/de/footer.json +0 -1
- package/dist/messages/de/index.d.ts +0 -1
- package/dist/messages/de/index.d.ts.map +1 -1
- package/dist/messages/en/footer.json +0 -1
- package/dist/messages/en/index.d.ts +0 -1
- package/dist/messages/en/index.d.ts.map +1 -1
- package/dist/messages/es/footer.json +0 -1
- package/dist/messages/es/index.d.ts +0 -1
- package/dist/messages/es/index.d.ts.map +1 -1
- package/dist/messages/fr/footer.json +0 -1
- package/dist/messages/fr/index.d.ts +0 -1
- package/dist/messages/fr/index.d.ts.map +1 -1
- package/dist/messages/it/footer.json +0 -1
- package/dist/messages/it/index.d.ts +0 -1
- package/dist/messages/it/index.d.ts.map +1 -1
- package/dist/messages/pt/footer.json +0 -1
- package/dist/messages/pt/index.d.ts +0 -1
- package/dist/messages/pt/index.d.ts.map +1 -1
- package/dist/templates/app/(auth)/accept-invite/[token]/page.tsx +4 -3
- package/dist/templates/app/(auth)/verify-email/page.tsx +4 -3
- package/dist/templates/app/(public)/docs/[section]/[page]/page.tsx +11 -0
- package/dist/templates/app/api/auth/[...all]/route.ts +15 -14
- package/dist/templates/app/api/internal/user-metadata/route.ts +6 -8
- package/dist/templates/app/api/user/plan-flags/route.ts +1 -1
- package/dist/templates/app/api/user/profile/route.ts +12 -17
- package/dist/templates/app/api/v1/api-keys/route.ts +1 -1
- package/dist/templates/app/api/v1/auth/signup-with-invite/route.ts +2 -1
- package/dist/templates/app/api/v1/billing/cancel/route.ts +1 -1
- package/dist/templates/app/api/v1/billing/change-plan/route.ts +1 -1
- package/dist/templates/app/api/v1/billing/check-action/route.ts +1 -1
- package/dist/templates/app/api/v1/billing/checkout/route.ts +1 -1
- package/dist/templates/app/api/v1/blocks/validate/route.ts +1 -1
- package/dist/templates/app/api/v1/media/[id]/tags/route.ts +1 -1
- package/dist/templates/app/api/v1/post-categories/[id]/route.ts +1 -1
- package/dist/templates/app/api/v1/post-categories/route.ts +1 -1
- package/dist/templates/app/api/v1/teams/[teamId]/members/route.ts +2 -1
- package/dist/templates/app/api/v1/teams/[teamId]/subscription/route.ts +3 -5
- package/dist/templates/app/api/v1/teams/docs.md +4 -9
- package/dist/templates/app/api/v1/teams/switch/route.ts +1 -1
- package/dist/templates/app/api/v1/users/[id]/meta/[key]/route.ts +1 -1
- package/dist/templates/app/api/v1/users/[id]/route.ts +1 -1
- package/dist/templates/app/api/v1/users/route.ts +1 -1
- package/dist/templates/app/dashboard/(main)/media/page.tsx +3 -1
- package/dist/templates/app/dashboard/settings/api-keys/page.tsx +5 -4
- package/dist/templates/app/dashboard/settings/profile/page.tsx +3 -2
- package/dist/templates/app/devtools/scheduled-actions/page.tsx +2 -1
- package/dist/templates/app/superadmin/docs/[section]/[page]/page.tsx +16 -0
- package/dist/templates/app/superadmin/subscriptions/page.tsx +3 -2
- package/dist/templates/app/superadmin/teams/[teamId]/page.tsx +5 -4
- package/dist/templates/app/superadmin/users/[userId]/page.tsx +2 -1
- package/dist/templates/app/superadmin/users/page.tsx +2 -1
- package/dist/templates/blocks/cta-section/component.tsx +3 -2
- package/dist/templates/blocks/cta-section/schema.ts +1 -1
- package/dist/templates/blocks/features-grid/component.tsx +2 -1
- package/dist/templates/blocks/features-grid/schema.ts +1 -1
- package/dist/templates/blocks/hero/component.tsx +3 -2
- package/dist/templates/blocks/hero/schema.ts +1 -1
- package/dist/templates/blocks/testimonials/component.tsx +2 -1
- package/dist/templates/blocks/testimonials/schema.ts +1 -1
- package/dist/templates/blocks/text-content/component.tsx +2 -1
- package/dist/templates/blocks/text-content/schema.ts +1 -1
- package/dist/templates/ci-workflows/README.md +1 -1
- package/dist/templates/ci-workflows/github/cypress-regression.yml +1 -1
- package/dist/templates/ci-workflows/github/cypress-smoke.yml +1 -1
- package/dist/templates/contents/plugins/starter/plugin/api/example/route.ts.template +1 -1
- package/dist/templates/contents/themes/starter/blocks/cta-section/component.tsx +3 -2
- package/dist/templates/contents/themes/starter/blocks/cta-section/schema.ts +1 -1
- package/dist/templates/contents/themes/starter/blocks/features-grid/component.tsx +2 -1
- package/dist/templates/contents/themes/starter/blocks/features-grid/schema.ts +1 -1
- package/dist/templates/contents/themes/starter/blocks/hero/component.tsx +3 -2
- package/dist/templates/contents/themes/starter/blocks/hero/schema.ts +1 -1
- package/dist/templates/contents/themes/starter/blocks/testimonials/component.tsx +3 -2
- package/dist/templates/contents/themes/starter/blocks/testimonials/schema.ts +1 -1
- package/dist/templates/contents/themes/starter/blocks/text-content/component.tsx +2 -1
- package/dist/templates/contents/themes/starter/blocks/text-content/schema.ts +1 -1
- package/dist/templates/contents/themes/starter/entities/posts/components/post-header.tsx +2 -1
- package/dist/templates/contents/themes/starter/lib/hooks/useAnalyticsData.ts +2 -1
- package/dist/templates/contents/themes/starter/lib/hooks/useTaskStats.ts +2 -1
- package/dist/templates/eslint.config.mjs +133 -9
- package/dist/templates/features/blog/blocks/post-content/component.tsx +2 -1
- package/dist/templates/features/blog/blocks/post-content/schema.ts +1 -1
- package/dist/templates/features/blog/entities/posts/components/post-header.tsx +2 -1
- package/dist/templates/features/pages/blocks/hero/component.tsx +3 -2
- package/dist/templates/features/pages/blocks/hero/schema.ts +1 -1
- package/dist/templates/next.config.mjs +50 -29
- package/dist/templates/proxy.ts +100 -21
- package/dist/templates/public/sw.js +2 -2
- package/dist/templates/scripts/cy-run-prod.cjs +5 -5
- package/dist/types/blocks.d.ts +1 -1
- package/dist/types/blocks.d.ts.map +1 -1
- package/dist/types/blocks.js +1 -1
- package/package.json +9 -5
- package/scripts/build/docs-registry.mjs +7 -7
- package/scripts/build/registry/__tests__/billing-registry.test.mjs +43 -0
- package/scripts/build/registry/__tests__/build-output.test.mjs +209 -0
- package/scripts/build/registry/__tests__/copy-race-child.mjs +14 -0
- package/scripts/build/registry/__tests__/direct-fs-writes.mjs +203 -0
- package/scripts/build/registry/__tests__/globals-css-tag.test.mjs +66 -0
- package/scripts/build/registry/__tests__/icons-discovery.test.mjs +530 -29
- package/scripts/build/registry/__tests__/page-generator.test.mjs +211 -37
- package/scripts/build/registry/__tests__/published-signatures.test.mjs +239 -0
- package/scripts/build/registry/__tests__/route-file-rule.test.mjs +170 -0
- package/scripts/build/registry/__tests__/safe-fs.test.mjs +333 -0
- package/scripts/build/registry/__tests__/template-analysis.test.mjs +142 -0
- package/scripts/build/registry/__tests__/template-registry.test.mjs +279 -9
- package/scripts/build/registry/__tests__/templates-plan.test.mjs +128 -0
- package/scripts/build/registry/__tests__/templates-tree.test.mjs +367 -0
- package/scripts/build/registry/__tests__/write-places.test.mjs +425 -0
- package/scripts/build/registry/config.mjs +6 -65
- package/scripts/build/registry/discovery/icons.mjs +498 -43
- package/scripts/build/registry/discovery/templates.mjs +1 -1
- package/scripts/build/registry/generators/billing-registry.mjs +1 -1
- package/scripts/build/registry/generators/docs-registry.mjs +3 -4
- package/scripts/build/registry/generators/feature-registry.mjs +1 -1
- package/scripts/build/registry/generators/permissions-registry.mjs +2 -2
- package/scripts/build/registry/generators/template-registry.mjs +65 -49
- package/scripts/build/registry/index.mjs +1 -2
- package/scripts/build/registry/post-build/index.mjs +1 -1
- package/scripts/build/registry/post-build/own-gitignores.mjs +220 -0
- package/scripts/build/registry/post-build/page-generator.mjs +512 -268
- package/scripts/build/registry/post-build/route-cleanup.mjs +16 -13
- package/scripts/build/registry/post-build/test-fixtures.mjs +26 -8
- package/scripts/build/registry/project-mode.mjs +76 -0
- package/scripts/build/registry/shared/typescript-compiler.mjs +77 -0
- package/scripts/build/registry/validate-env.test.mjs +12 -8
- package/scripts/build/registry/watch.mjs +3 -53
- package/scripts/build/registry/write-places.mjs +228 -0
- package/scripts/build/registry.mjs +53 -30
- package/scripts/build/safe-fs.mjs +279 -0
- package/scripts/build/templates-plan.mjs +44 -0
- package/scripts/build/theme.mjs +46 -42
- package/scripts/build/update-tsconfig.mjs +16 -15
- package/scripts/db/cluster-changes.mjs +205 -0
- package/scripts/db/connection-time-limits.mjs +53 -0
- package/scripts/db/inspect-server.mjs +108 -0
- package/scripts/db/migration-time-limit.mjs +695 -0
- package/scripts/db/run-migrations.mjs +46 -41
- package/scripts/db/ssl-config.mjs +194 -0
- package/scripts/db/theme-location.mjs +69 -0
- package/scripts/db/verify-tables.mjs +3 -8
- package/scripts/db/verify-teams.mjs +2 -6
- package/scripts/db/verify-theme-migrations.mjs +169 -0
- package/scripts/maintenance/core-updater.mjs +1003 -0
- package/scripts/maintenance/step-guard.mjs +74 -0
- package/scripts/maintenance/update-core.mjs +14 -831
- package/scripts/utils/console-guard.mjs +13 -0
- package/scripts/utils/generated-tag.mjs +66 -0
- package/scripts/utils/index.mjs +9 -1
- package/scripts/utils/logging.mjs +140 -0
- package/templates/app/(auth)/accept-invite/[token]/page.tsx +4 -3
- package/templates/app/(auth)/verify-email/page.tsx +4 -3
- package/templates/app/(public)/docs/[section]/[page]/page.tsx +11 -0
- package/templates/app/api/auth/[...all]/route.ts +15 -14
- package/templates/app/api/internal/user-metadata/route.ts +6 -8
- package/templates/app/api/user/plan-flags/route.ts +1 -1
- package/templates/app/api/user/profile/route.ts +12 -17
- package/templates/app/api/v1/api-keys/route.ts +1 -1
- package/templates/app/api/v1/auth/signup-with-invite/route.ts +2 -1
- package/templates/app/api/v1/billing/cancel/route.ts +1 -1
- package/templates/app/api/v1/billing/change-plan/route.ts +1 -1
- package/templates/app/api/v1/billing/check-action/route.ts +1 -1
- package/templates/app/api/v1/billing/checkout/route.ts +1 -1
- package/templates/app/api/v1/blocks/validate/route.ts +1 -1
- package/templates/app/api/v1/media/[id]/tags/route.ts +1 -1
- package/templates/app/api/v1/post-categories/[id]/route.ts +1 -1
- package/templates/app/api/v1/post-categories/route.ts +1 -1
- package/templates/app/api/v1/teams/[teamId]/members/route.ts +2 -1
- package/templates/app/api/v1/teams/[teamId]/subscription/route.ts +3 -5
- package/templates/app/api/v1/teams/docs.md +4 -9
- package/templates/app/api/v1/teams/switch/route.ts +1 -1
- package/templates/app/api/v1/users/[id]/meta/[key]/route.ts +1 -1
- package/templates/app/api/v1/users/[id]/route.ts +1 -1
- package/templates/app/api/v1/users/route.ts +1 -1
- package/templates/app/dashboard/(main)/media/page.tsx +3 -1
- package/templates/app/dashboard/settings/api-keys/page.tsx +5 -4
- package/templates/app/dashboard/settings/profile/page.tsx +3 -2
- package/templates/app/devtools/scheduled-actions/page.tsx +2 -1
- package/templates/app/superadmin/docs/[section]/[page]/page.tsx +16 -0
- package/templates/app/superadmin/subscriptions/page.tsx +3 -2
- package/templates/app/superadmin/teams/[teamId]/page.tsx +5 -4
- package/templates/app/superadmin/users/[userId]/page.tsx +2 -1
- package/templates/app/superadmin/users/page.tsx +2 -1
- package/templates/blocks/cta-section/component.tsx +3 -2
- package/templates/blocks/cta-section/schema.ts +1 -1
- package/templates/blocks/features-grid/component.tsx +2 -1
- package/templates/blocks/features-grid/schema.ts +1 -1
- package/templates/blocks/hero/component.tsx +3 -2
- package/templates/blocks/hero/schema.ts +1 -1
- package/templates/blocks/testimonials/component.tsx +2 -1
- package/templates/blocks/testimonials/schema.ts +1 -1
- package/templates/blocks/text-content/component.tsx +2 -1
- package/templates/blocks/text-content/schema.ts +1 -1
- package/templates/ci-workflows/README.md +1 -1
- package/templates/ci-workflows/github/cypress-regression.yml +1 -1
- package/templates/ci-workflows/github/cypress-smoke.yml +1 -1
- package/templates/contents/plugins/starter/plugin/api/example/route.ts.template +1 -1
- package/templates/contents/themes/starter/blocks/cta-section/component.tsx +3 -2
- package/templates/contents/themes/starter/blocks/cta-section/schema.ts +1 -1
- package/templates/contents/themes/starter/blocks/features-grid/component.tsx +2 -1
- package/templates/contents/themes/starter/blocks/features-grid/schema.ts +1 -1
- package/templates/contents/themes/starter/blocks/hero/component.tsx +3 -2
- package/templates/contents/themes/starter/blocks/hero/schema.ts +1 -1
- package/templates/contents/themes/starter/blocks/testimonials/component.tsx +3 -2
- package/templates/contents/themes/starter/blocks/testimonials/schema.ts +1 -1
- package/templates/contents/themes/starter/blocks/text-content/component.tsx +2 -1
- package/templates/contents/themes/starter/blocks/text-content/schema.ts +1 -1
- package/templates/contents/themes/starter/entities/posts/components/post-header.tsx +2 -1
- package/templates/contents/themes/starter/lib/hooks/useAnalyticsData.ts +2 -1
- package/templates/contents/themes/starter/lib/hooks/useTaskStats.ts +2 -1
- package/templates/eslint.config.mjs +133 -9
- package/templates/features/blog/blocks/post-content/component.tsx +2 -1
- package/templates/features/blog/blocks/post-content/schema.ts +1 -1
- package/templates/features/blog/entities/posts/components/post-header.tsx +2 -1
- package/templates/features/pages/blocks/hero/component.tsx +3 -2
- package/templates/features/pages/blocks/hero/schema.ts +1 -1
- package/templates/next.config.mjs +50 -29
- package/templates/proxy.ts +100 -21
- package/templates/public/sw.js +2 -2
- package/templates/scripts/cy-run-prod.cjs +5 -5
- package/tests/jest/__mocks__/@nextsparkjs/registries/template-registry.client.ts +11 -0
- package/tests/jest/__mocks__/next-server.js +10 -0
- package/scripts/build/docs.mjs +0 -392
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// What a theme verification run changes outside the database it was given.
|
|
2
|
+
//
|
|
3
|
+
// A migration run is not contained in its database: 001 creates the
|
|
4
|
+
// `authenticated`, `anon` and `service_role` roles, and 022 creates
|
|
5
|
+
// `nextspark_app`, sets it to INHERIT and grants it `authenticated` plus
|
|
6
|
+
// membership to the connecting user. Roles are cluster-wide, so those
|
|
7
|
+
// statements reach every other database on the same Postgres server, including
|
|
8
|
+
// ones the run was never pointed at.
|
|
9
|
+
//
|
|
10
|
+
// The database being empty says nothing about that, which is why the checks
|
|
11
|
+
// here look at the cluster: the roles the migrations touch, and whether any
|
|
12
|
+
// other database is using this server.
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Roles the core migrations create or alter, and what they do to each.
|
|
16
|
+
* `altered` separates the one whose attributes and memberships the migrations
|
|
17
|
+
* change even when it is already there from the ones a Supabase project
|
|
18
|
+
* already has and 001 only fills in.
|
|
19
|
+
*/
|
|
20
|
+
export const GLOBAL_OBJECTS = [
|
|
21
|
+
{ role: 'authenticated', change: 'created if missing', altered: false },
|
|
22
|
+
{ role: 'anon', change: 'created if missing', altered: false },
|
|
23
|
+
{ role: 'service_role', change: 'created if missing', altered: false },
|
|
24
|
+
{
|
|
25
|
+
role: 'nextspark_app',
|
|
26
|
+
change: 'created if missing, set to INHERIT, granted authenticated',
|
|
27
|
+
altered: true,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
role: 'current_user',
|
|
31
|
+
change: 'granted nextspark_app, so whoever runs this can SET ROLE to it',
|
|
32
|
+
altered: true,
|
|
33
|
+
connecting: true,
|
|
34
|
+
},
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
// `current_user` is whoever connects, so it is announced but never looked for
|
|
38
|
+
// among the roles the server already has.
|
|
39
|
+
const ALTERED_ROLES = new Set(
|
|
40
|
+
GLOBAL_OBJECTS.filter(object => object.altered && !object.connecting).map(object => object.role)
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Databases a Postgres server creates for itself, or a managed one adds. Their
|
|
45
|
+
* presence says nothing about the server being in use — `postgres` is asked
|
|
46
|
+
* about separately, since a hosted project can keep the app's own schema there.
|
|
47
|
+
*
|
|
48
|
+
* Only `template0` and `template1` among templates: any database can be marked
|
|
49
|
+
* `datistemplate`, and one a user marked that way shares the cluster's roles
|
|
50
|
+
* like any other.
|
|
51
|
+
*/
|
|
52
|
+
const HOUSEKEEPING_DATABASES = new Set([
|
|
53
|
+
'postgres', 'template0', 'template1', 'rdsadmin', 'azure_maintenance', 'azure_sys', 'cloudsqladmin',
|
|
54
|
+
])
|
|
55
|
+
|
|
56
|
+
/** The database a client connects to when it only needs the server. */
|
|
57
|
+
export const MAINTENANCE_DATABASE = 'postgres'
|
|
58
|
+
|
|
59
|
+
export const ROLES_SQL = `
|
|
60
|
+
SELECT r.rolname AS name,
|
|
61
|
+
r.rolcanlogin AS can_login,
|
|
62
|
+
COALESCE(array_agg(g.rolname) FILTER (WHERE g.rolname IS NOT NULL), '{}'::text[]) AS member_of
|
|
63
|
+
FROM pg_roles r
|
|
64
|
+
LEFT JOIN pg_auth_members m ON m.member = r.oid
|
|
65
|
+
LEFT JOIN pg_roles g ON g.oid = m.roleid
|
|
66
|
+
WHERE r.rolname IN ('authenticated', 'anon', 'service_role')
|
|
67
|
+
OR r.rolname LIKE 'nextspark\\_%'
|
|
68
|
+
GROUP BY r.rolname, r.rolcanlogin
|
|
69
|
+
ORDER BY r.rolname
|
|
70
|
+
`
|
|
71
|
+
|
|
72
|
+
// A database that refuses connections (`datallowconn = false`) or is marked as
|
|
73
|
+
// a template is still a database on this server, sharing the roles this run
|
|
74
|
+
// alters, so it counts. The server's own templates are left out by name, in
|
|
75
|
+
// HOUSEKEEPING_DATABASES.
|
|
76
|
+
export const DATABASES_SQL = `
|
|
77
|
+
SELECT datname AS name
|
|
78
|
+
FROM pg_database
|
|
79
|
+
WHERE datname <> current_database()
|
|
80
|
+
ORDER BY datname
|
|
81
|
+
`
|
|
82
|
+
|
|
83
|
+
/** An array of role names, whether the driver hands over an array or `{a,b}`. */
|
|
84
|
+
function asList(value) {
|
|
85
|
+
if (Array.isArray(value)) return value
|
|
86
|
+
if (typeof value !== 'string') return []
|
|
87
|
+
return value.replace(/^\{|\}$/g, '').split(',').filter(Boolean)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* What the run would change beyond its own database, and whether the cluster
|
|
92
|
+
* shows signs of being used by anything else.
|
|
93
|
+
*
|
|
94
|
+
* A role the migrations touch that already exists is the stronger signal: the
|
|
95
|
+
* run alters it for whoever else is using it. Another database on the server is
|
|
96
|
+
* the weaker one, and enough on its own — a role created now lands in a cluster
|
|
97
|
+
* that is not this run's to change.
|
|
98
|
+
*
|
|
99
|
+
* `maintenance` is what looking at the `postgres` database answered:
|
|
100
|
+
* `{ objects }` when it could be read, `{ unreachable: true, reason }` when it
|
|
101
|
+
* could not. Not being able to look is itself a reason to refuse — a server
|
|
102
|
+
* that denies CONNECT to its own maintenance database is a managed one with
|
|
103
|
+
* someone else's rules on it, not a container this run may rearrange.
|
|
104
|
+
*/
|
|
105
|
+
export function inspectCluster({ roles = [], databases = [], maintenance = {} } = {}) {
|
|
106
|
+
const maintenanceObjects = maintenance.objects ?? []
|
|
107
|
+
const existingRoles = roles.map(role => ({
|
|
108
|
+
name: role.name,
|
|
109
|
+
canLogin: role.can_login ?? role.canLogin ?? false,
|
|
110
|
+
memberOf: asList(role.member_of ?? role.memberOf),
|
|
111
|
+
}))
|
|
112
|
+
const otherDatabases = databases
|
|
113
|
+
.map(database => database.name ?? database)
|
|
114
|
+
.filter(name => !HOUSEKEEPING_DATABASES.has(name))
|
|
115
|
+
|
|
116
|
+
const describe = role => (role.memberOf.length ? `${role.name} (member of ${role.memberOf.join(', ')})` : role.name)
|
|
117
|
+
const altered = existingRoles.filter(role => ALTERED_ROLES.has(role.name) || role.name.startsWith('nextspark_'))
|
|
118
|
+
const shared = existingRoles.filter(role => !altered.includes(role))
|
|
119
|
+
|
|
120
|
+
const reasons = []
|
|
121
|
+
if (altered.length > 0) {
|
|
122
|
+
reasons.push(`this run alters roles that are already here: ${altered.map(describe).join(', ')}`)
|
|
123
|
+
}
|
|
124
|
+
if (shared.length > 0) {
|
|
125
|
+
reasons.push(
|
|
126
|
+
`roles these migrations create already exist, so something else set this server up: ${shared.map(describe).join(', ')}`
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
if (otherDatabases.length > 0) {
|
|
130
|
+
const shown = otherDatabases.slice(0, 5).join(', ')
|
|
131
|
+
const rest = otherDatabases.length > 5 ? `, and ${otherDatabases.length - 5} more` : ''
|
|
132
|
+
reasons.push(`other databases share this server: ${shown}${rest}`)
|
|
133
|
+
}
|
|
134
|
+
if (maintenanceObjects.length > 0) {
|
|
135
|
+
const examples = maintenanceObjects.slice(0, 3).map(object => object.name ?? object).join(', ')
|
|
136
|
+
reasons.push(`the ${MAINTENANCE_DATABASE} database holds objects of its own, for example ${examples}`)
|
|
137
|
+
}
|
|
138
|
+
if (maintenance.unreachable) {
|
|
139
|
+
const detail = maintenance.reason ? `: ${maintenance.reason}` : ''
|
|
140
|
+
reasons.push(
|
|
141
|
+
`the ${MAINTENANCE_DATABASE} database could not be read${detail}, so what else is on this server is unknown`
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return { disposable: reasons.length === 0, reasons, existingRoles, otherDatabases }
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Words that can stand where a role name would, in the statements above:
|
|
150
|
+
* GRANT's own syntax, the privileges it grants, and the objects it grants them
|
|
151
|
+
* on. None of them is a role.
|
|
152
|
+
*/
|
|
153
|
+
const GRANT_KEYWORDS = new Set([
|
|
154
|
+
'ALL', 'PRIVILEGES', 'SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER',
|
|
155
|
+
'CREATE', 'CONNECT', 'TEMPORARY', 'TEMP', 'EXECUTE', 'USAGE', 'SET', 'ALTER', 'MAINTAIN',
|
|
156
|
+
'ON', 'IN', 'SCHEMA', 'TABLE', 'TABLES', 'SEQUENCE', 'SEQUENCES', 'FUNCTION', 'FUNCTIONS',
|
|
157
|
+
'ROUTINE', 'ROUTINES', 'DATABASE', 'DOMAIN', 'TYPE', 'LANGUAGE', 'PUBLIC', 'SYSTEM',
|
|
158
|
+
'FOREIGN', 'DATA', 'WRAPPER', 'SERVER', 'LARGE', 'OBJECT', 'TABLESPACE', 'PARAMETER',
|
|
159
|
+
'OPTION', 'ADMIN', 'INHERIT', 'GRANTED', 'BY', 'FOR', 'ROLE', 'COLUMN',
|
|
160
|
+
])
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Every role a migration names, whichever statement names it.
|
|
164
|
+
*
|
|
165
|
+
* Both sides of a grant count: `GRANT authenticated TO reporting` alters
|
|
166
|
+
* `reporting` as much as it uses `authenticated`, and a grant of privileges —
|
|
167
|
+
* `GRANT SELECT ON ... TO reporting` — names only the grantee. A grant can list
|
|
168
|
+
* several of either, so each list is split on commas.
|
|
169
|
+
*/
|
|
170
|
+
export function rolesNamedIn(sql) {
|
|
171
|
+
const named = new Set()
|
|
172
|
+
// Comments and RAISE messages are prose: a GRANT quoted inside either is
|
|
173
|
+
// being talked about, not run. A GRANT inside format() is run, so string
|
|
174
|
+
// literals in general stay.
|
|
175
|
+
const statements = sql
|
|
176
|
+
.replace(/\/\*[\s\S]*?\*\//g, ' ')
|
|
177
|
+
.replace(/--[^\n]*/g, ' ')
|
|
178
|
+
.replace(/\bRAISE\s+(?:DEBUG|LOG|INFO|NOTICE|WARNING|EXCEPTION)\b[^;]*;/gi, ' ')
|
|
179
|
+
|
|
180
|
+
const add = list => {
|
|
181
|
+
for (const name of list.split(',')) {
|
|
182
|
+
// A grantee list can end inside a format() call, so the string and the
|
|
183
|
+
// call close on the last name: `format('GRANT x TO %I', current_user)`.
|
|
184
|
+
const role = name.trim().replace(/[)'"]+$/, '').replace(/^["']+/, '')
|
|
185
|
+
// GRANT's own keywords, and the object a privilege is granted on, are
|
|
186
|
+
// not roles; `format('... %I', current_user)` names the connecting user.
|
|
187
|
+
if (/^[a-z_]\w*$/i.test(role) && !GRANT_KEYWORDS.has(role.toUpperCase())) named.add(role)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
for (const [, list] of statements.matchAll(/\b(?:CREATE|ALTER|DROP)\s+(?:ROLE|USER|GROUP)\s+(?:IF\s+(?:NOT\s+)?EXISTS\s+)?("?\w+"?)/gi)) {
|
|
192
|
+
add(list)
|
|
193
|
+
}
|
|
194
|
+
// The grantee list runs from the last TO/FROM to the end of the statement,
|
|
195
|
+
// past any WITH ADMIN OPTION / GRANTED BY tail.
|
|
196
|
+
for (const [, grantees] of statements.matchAll(/\b(?:GRANT|REVOKE)\b[^;]*?\s(?:TO|FROM)\s+([^;]+)/gi)) {
|
|
197
|
+
add(grantees.split(/\bWITH\b|\bGRANTED\s+BY\b|\bCASCADE\b|\bRESTRICT\b/i)[0])
|
|
198
|
+
}
|
|
199
|
+
// What a grant of role membership hands over, which is a role too.
|
|
200
|
+
for (const [, roles] of statements.matchAll(/\b(?:GRANT|REVOKE)\s+((?:"?\w+"?\s*,\s*)*"?\w+"?)\s+(?:TO|FROM)\s/gi)) {
|
|
201
|
+
add(roles)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return named
|
|
205
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Time limits on a pg connection that hold whatever the connection string says.
|
|
2
|
+
//
|
|
3
|
+
// pg reads a connection string's parameters over the options passed next to it,
|
|
4
|
+
// so a URL ending in `?statement_timeout=0&query_timeout=` would switch off both
|
|
5
|
+
// the server's limit and the client's. The client is therefore built exactly as
|
|
6
|
+
// pg builds one for the string, with no limits among the options, and the two
|
|
7
|
+
// limits are then set on the connection parameters pg resolved for it. Which of
|
|
8
|
+
// the string's parameters a client acts on, and which it ignores, is decided by
|
|
9
|
+
// the installed version of pg in the same way it would be without the limits;
|
|
10
|
+
// only the two limits differ. pg reads them from those parameters when it
|
|
11
|
+
// connects (statement_timeout, sent in the startup message) and each time it
|
|
12
|
+
// sends a query (query_timeout).
|
|
13
|
+
//
|
|
14
|
+
// Nothing else a connection brings along lifts the server's limit: the
|
|
15
|
+
// statement_timeout sent when connecting outranks one in
|
|
16
|
+
// `options=-c statement_timeout=…`, in PGOPTIONS, or set on the database or the
|
|
17
|
+
// role.
|
|
18
|
+
|
|
19
|
+
import { scriptClient } from './ssl-config.mjs';
|
|
20
|
+
|
|
21
|
+
/** The connection-string parameters pg reads over the time limits given next to them. */
|
|
22
|
+
export const TIME_LIMIT_PARAMETERS = ['statement_timeout', 'query_timeout'];
|
|
23
|
+
|
|
24
|
+
/** How pg builds a client for the string, with the shared application SSL policy. */
|
|
25
|
+
function clientFor(connectionString, options = {}) {
|
|
26
|
+
return scriptClient(connectionString, options);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The time-limit parameters the connection string sets, as pg reads them: the
|
|
31
|
+
* ones whose value stays the same whatever limit is passed next to the string.
|
|
32
|
+
*/
|
|
33
|
+
export function timeLimitParametersIn(connectionString) {
|
|
34
|
+
const withLimits = limit => clientFor(connectionString, { statement_timeout: limit, query_timeout: limit }).connectionParameters;
|
|
35
|
+
const one = withLimits(1);
|
|
36
|
+
const two = withLimits(2);
|
|
37
|
+
return TIME_LIMIT_PARAMETERS.filter(name => one[name] === two[name]);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A client whose limits are the ones given: `connectMs` to connect,
|
|
42
|
+
* `statementMs` for the server to cancel a statement, `queryMs` for the client
|
|
43
|
+
* to give up on an answer. `database` connects to another database on the same
|
|
44
|
+
* server, as whoever the connection string names.
|
|
45
|
+
*/
|
|
46
|
+
export function timeLimitedClient(connectionString, { connectMs, statementMs, queryMs }, { database } = {}) {
|
|
47
|
+
const client = clientFor(connectionString, { connectionTimeoutMillis: connectMs });
|
|
48
|
+
const parameters = client.connectionParameters;
|
|
49
|
+
parameters.statement_timeout = statementMs;
|
|
50
|
+
parameters.query_timeout = queryMs;
|
|
51
|
+
if (database !== undefined) client.database = parameters.database = database;
|
|
52
|
+
return client;
|
|
53
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// What db:verify-theme asks a Postgres server before it migrates anything.
|
|
2
|
+
//
|
|
3
|
+
// Every question has a time limit, not only the connection. A query that waits
|
|
4
|
+
// on a lock someone else holds on a catalog, or a server that stops answering
|
|
5
|
+
// once the connection is up, would otherwise leave the command waiting until
|
|
6
|
+
// something outside kills it, when what the command owes is a refusal. The
|
|
7
|
+
// limits hold whatever time limits the database URL sets (see
|
|
8
|
+
// connection-time-limits.mjs).
|
|
9
|
+
|
|
10
|
+
import { ROLES_SQL, DATABASES_SQL, MAINTENANCE_DATABASE } from './cluster-changes.mjs';
|
|
11
|
+
import { timeLimitedClient } from './connection-time-limits.mjs';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* How long each step may take. The server cancels a statement past
|
|
15
|
+
* `statementMs`; `queryMs`, a little longer, is the client giving up on a
|
|
16
|
+
* server that does not even answer that cancellation.
|
|
17
|
+
*/
|
|
18
|
+
export const TIMEOUTS = { connectMs: 10000, statementMs: 15000, queryMs: 20000 };
|
|
19
|
+
|
|
20
|
+
// Everything that can be created in a non-system schema: relations (tables,
|
|
21
|
+
// views, sequences, indexes), functions and standalone types. Objects that
|
|
22
|
+
// belong to an extension don't count, since the server's template database
|
|
23
|
+
// can bring extensions along. Functions and types count because the
|
|
24
|
+
// migrations' CREATE OR REPLACE would replace them without an error.
|
|
25
|
+
export const EXISTING_OBJECTS_SQL = `
|
|
26
|
+
SELECT kind, name
|
|
27
|
+
FROM (
|
|
28
|
+
SELECT 'relation' AS kind, n.nspname || '.' || c.relname AS name, n.nspname,
|
|
29
|
+
'pg_class'::regclass AS classid, c.oid AS objid
|
|
30
|
+
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
31
|
+
UNION ALL
|
|
32
|
+
SELECT 'function', n.nspname || '.' || p.proname, n.nspname,
|
|
33
|
+
'pg_proc'::regclass, p.oid
|
|
34
|
+
FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace
|
|
35
|
+
UNION ALL
|
|
36
|
+
SELECT 'type', n.nspname || '.' || t.typname, n.nspname,
|
|
37
|
+
'pg_type'::regclass, t.oid
|
|
38
|
+
FROM pg_type t JOIN pg_namespace n ON n.oid = t.typnamespace
|
|
39
|
+
WHERE t.typrelid = 0 AND t.typcategory <> 'A'
|
|
40
|
+
) objects
|
|
41
|
+
WHERE nspname NOT IN ('pg_catalog', 'information_schema')
|
|
42
|
+
AND nspname !~ '^pg_(toast|temp)'
|
|
43
|
+
AND NOT EXISTS (
|
|
44
|
+
SELECT 1 FROM pg_depend d
|
|
45
|
+
WHERE d.classid = objects.classid AND d.objid = objects.objid AND d.deptype = 'e'
|
|
46
|
+
)
|
|
47
|
+
ORDER BY kind, name
|
|
48
|
+
LIMIT 5
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
export function connect(connectionString, timeouts = TIMEOUTS, target = {}) {
|
|
52
|
+
return timeLimitedClient(connectionString, timeouts, target);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// `end()` drops the socket when a query is still active, so a connection whose
|
|
56
|
+
// query timed out closes instead of waiting for a server that is not answering.
|
|
57
|
+
async function withClient(connectionString, timeouts, work, target = {}) {
|
|
58
|
+
const client = connect(connectionString, timeouts, target);
|
|
59
|
+
await client.connect();
|
|
60
|
+
try {
|
|
61
|
+
return await work(client);
|
|
62
|
+
} finally {
|
|
63
|
+
await client.end();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** What the target database holds, and what the cluster around it shows. */
|
|
68
|
+
export function inspectTarget(connectionString, timeouts = TIMEOUTS) {
|
|
69
|
+
return withClient(connectionString, timeouts, async client => {
|
|
70
|
+
const [objects, roles, databases] = await Promise.all([
|
|
71
|
+
client.query(EXISTING_OBJECTS_SQL),
|
|
72
|
+
client.query(ROLES_SQL),
|
|
73
|
+
client.query(DATABASES_SQL),
|
|
74
|
+
]);
|
|
75
|
+
return { existingObjects: objects.rows, roles: roles.rows, databases: databases.rows };
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* What the maintenance database holds. A hosted Postgres keeps `postgres` for
|
|
81
|
+
* connections that only need the server, and a project whose own schema lives
|
|
82
|
+
* there looks, from the outside, like a server with nothing on it.
|
|
83
|
+
*
|
|
84
|
+
* The connection goes to the server and user the database URL names, as pg
|
|
85
|
+
* reads them, with only the database changed. The target database is compared
|
|
86
|
+
* the way pg resolves it too, so a URL with no database whose user is
|
|
87
|
+
* `postgres` is already the maintenance database.
|
|
88
|
+
*
|
|
89
|
+
* A server that refuses the connection, or the query, or does not answer it in
|
|
90
|
+
* time, is reported as unreachable rather than as empty: not being able to look
|
|
91
|
+
* is what a managed cluster looks like, and reading it as "nothing there" is
|
|
92
|
+
* how a run ends up creating cluster-wide roles on a server it was never
|
|
93
|
+
* allowed to inspect.
|
|
94
|
+
*/
|
|
95
|
+
export async function inspectMaintenanceDatabase(databaseUrl, timeouts = TIMEOUTS) {
|
|
96
|
+
if (connect(databaseUrl, timeouts).database === MAINTENANCE_DATABASE) return { objects: [] };
|
|
97
|
+
try {
|
|
98
|
+
const result = await withClient(
|
|
99
|
+
databaseUrl,
|
|
100
|
+
timeouts,
|
|
101
|
+
client => client.query(EXISTING_OBJECTS_SQL),
|
|
102
|
+
{ database: MAINTENANCE_DATABASE }
|
|
103
|
+
);
|
|
104
|
+
return { objects: result.rows };
|
|
105
|
+
} catch (error) {
|
|
106
|
+
return { unreachable: true, reason: error.message };
|
|
107
|
+
}
|
|
108
|
+
}
|