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,102 +1,102 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import type { RobotsSettings } from '@nextblock-cms/utils/seo';
|
|
3
|
-
|
|
4
|
-
import { robotsSettingsSignature } from './robots-settings-signature';
|
|
5
|
-
|
|
6
|
-
function settings(overrides: Partial<RobotsSettings> = {}): RobotsSettings {
|
|
7
|
-
return {
|
|
8
|
-
customRules: '',
|
|
9
|
-
isIndexingEnabled: true,
|
|
10
|
-
sitemapEnabled: true,
|
|
11
|
-
userAgentRules: [{ allow: ['/'], disallow: ['/cms'], userAgent: '*' }],
|
|
12
|
-
...overrides,
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
describe('robotsSettingsSignature', () => {
|
|
17
|
-
it('matches for two structurally identical objects that are not the same object', () => {
|
|
18
|
-
// This is the case that matters most: a server component hands down a brand-new object
|
|
19
|
-
// on every render, and re-seeding the form on that would destroy in-progress typing.
|
|
20
|
-
expect(robotsSettingsSignature(settings())).toBe(robotsSettingsSignature(settings()));
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('ignores the key order of the object literal it is handed', () => {
|
|
24
|
-
const alphabetical = settings();
|
|
25
|
-
const reordered = {
|
|
26
|
-
userAgentRules: [{ userAgent: '*', disallow: ['/cms'], allow: ['/'] }],
|
|
27
|
-
sitemapEnabled: true,
|
|
28
|
-
isIndexingEnabled: true,
|
|
29
|
-
customRules: '',
|
|
30
|
-
} as RobotsSettings;
|
|
31
|
-
|
|
32
|
-
expect(robotsSettingsSignature(reordered)).toBe(robotsSettingsSignature(alphabetical));
|
|
33
|
-
// The old comparison this replaces was raw JSON.stringify, which does not.
|
|
34
|
-
expect(JSON.stringify(reordered)).not.toBe(JSON.stringify(alphabetical));
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('changes when the server normalises a path the operator typed', () => {
|
|
38
|
-
const typed = settings({ userAgentRules: [{ allow: ['/'], disallow: ['cms'], userAgent: '*' }] });
|
|
39
|
-
const normalised = settings({
|
|
40
|
-
userAgentRules: [{ allow: ['/'], disallow: ['/cms'], userAgent: '*' }],
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
expect(robotsSettingsSignature(typed)).not.toBe(robotsSettingsSignature(normalised));
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('changes when a rule is dropped, added, or emptied', () => {
|
|
47
|
-
expect(robotsSettingsSignature(settings({ userAgentRules: [] }))).not.toBe(
|
|
48
|
-
robotsSettingsSignature(settings())
|
|
49
|
-
);
|
|
50
|
-
expect(
|
|
51
|
-
robotsSettingsSignature(
|
|
52
|
-
settings({
|
|
53
|
-
userAgentRules: [
|
|
54
|
-
{ allow: ['/'], disallow: ['/cms'], userAgent: '*' },
|
|
55
|
-
{ allow: [], disallow: ['/'], userAgent: 'GPTBot' },
|
|
56
|
-
],
|
|
57
|
-
})
|
|
58
|
-
)
|
|
59
|
-
).not.toBe(robotsSettingsSignature(settings()));
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('changes when only the order of the user-agent blocks differs', () => {
|
|
63
|
-
const first = settings({
|
|
64
|
-
userAgentRules: [
|
|
65
|
-
{ allow: [], disallow: ['/'], userAgent: 'GPTBot' },
|
|
66
|
-
{ allow: ['/'], disallow: [], userAgent: '*' },
|
|
67
|
-
],
|
|
68
|
-
});
|
|
69
|
-
const swapped = settings({
|
|
70
|
-
userAgentRules: [
|
|
71
|
-
{ allow: ['/'], disallow: [], userAgent: '*' },
|
|
72
|
-
{ allow: [], disallow: ['/'], userAgent: 'GPTBot' },
|
|
73
|
-
],
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
expect(robotsSettingsSignature(first)).not.toBe(robotsSettingsSignature(swapped));
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('changes for each of the scalar fields', () => {
|
|
80
|
-
expect(robotsSettingsSignature(settings({ customRules: '# hi' }))).not.toBe(
|
|
81
|
-
robotsSettingsSignature(settings())
|
|
82
|
-
);
|
|
83
|
-
expect(robotsSettingsSignature(settings({ isIndexingEnabled: false }))).not.toBe(
|
|
84
|
-
robotsSettingsSignature(settings())
|
|
85
|
-
);
|
|
86
|
-
expect(robotsSettingsSignature(settings({ sitemapEnabled: false }))).not.toBe(
|
|
87
|
-
robotsSettingsSignature(settings())
|
|
88
|
-
);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('does not confuse an allow list with a disallow list of the same paths', () => {
|
|
92
|
-
expect(
|
|
93
|
-
robotsSettingsSignature(
|
|
94
|
-
settings({ userAgentRules: [{ allow: ['/a'], disallow: [], userAgent: '*' }] })
|
|
95
|
-
)
|
|
96
|
-
).not.toBe(
|
|
97
|
-
robotsSettingsSignature(
|
|
98
|
-
settings({ userAgentRules: [{ allow: [], disallow: ['/a'], userAgent: '*' }] })
|
|
99
|
-
)
|
|
100
|
-
);
|
|
101
|
-
});
|
|
102
|
-
});
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { RobotsSettings } from '@nextblock-cms/utils/seo';
|
|
3
|
+
|
|
4
|
+
import { robotsSettingsSignature } from './robots-settings-signature';
|
|
5
|
+
|
|
6
|
+
function settings(overrides: Partial<RobotsSettings> = {}): RobotsSettings {
|
|
7
|
+
return {
|
|
8
|
+
customRules: '',
|
|
9
|
+
isIndexingEnabled: true,
|
|
10
|
+
sitemapEnabled: true,
|
|
11
|
+
userAgentRules: [{ allow: ['/'], disallow: ['/cms'], userAgent: '*' }],
|
|
12
|
+
...overrides,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe('robotsSettingsSignature', () => {
|
|
17
|
+
it('matches for two structurally identical objects that are not the same object', () => {
|
|
18
|
+
// This is the case that matters most: a server component hands down a brand-new object
|
|
19
|
+
// on every render, and re-seeding the form on that would destroy in-progress typing.
|
|
20
|
+
expect(robotsSettingsSignature(settings())).toBe(robotsSettingsSignature(settings()));
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('ignores the key order of the object literal it is handed', () => {
|
|
24
|
+
const alphabetical = settings();
|
|
25
|
+
const reordered = {
|
|
26
|
+
userAgentRules: [{ userAgent: '*', disallow: ['/cms'], allow: ['/'] }],
|
|
27
|
+
sitemapEnabled: true,
|
|
28
|
+
isIndexingEnabled: true,
|
|
29
|
+
customRules: '',
|
|
30
|
+
} as RobotsSettings;
|
|
31
|
+
|
|
32
|
+
expect(robotsSettingsSignature(reordered)).toBe(robotsSettingsSignature(alphabetical));
|
|
33
|
+
// The old comparison this replaces was raw JSON.stringify, which does not.
|
|
34
|
+
expect(JSON.stringify(reordered)).not.toBe(JSON.stringify(alphabetical));
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('changes when the server normalises a path the operator typed', () => {
|
|
38
|
+
const typed = settings({ userAgentRules: [{ allow: ['/'], disallow: ['cms'], userAgent: '*' }] });
|
|
39
|
+
const normalised = settings({
|
|
40
|
+
userAgentRules: [{ allow: ['/'], disallow: ['/cms'], userAgent: '*' }],
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
expect(robotsSettingsSignature(typed)).not.toBe(robotsSettingsSignature(normalised));
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('changes when a rule is dropped, added, or emptied', () => {
|
|
47
|
+
expect(robotsSettingsSignature(settings({ userAgentRules: [] }))).not.toBe(
|
|
48
|
+
robotsSettingsSignature(settings())
|
|
49
|
+
);
|
|
50
|
+
expect(
|
|
51
|
+
robotsSettingsSignature(
|
|
52
|
+
settings({
|
|
53
|
+
userAgentRules: [
|
|
54
|
+
{ allow: ['/'], disallow: ['/cms'], userAgent: '*' },
|
|
55
|
+
{ allow: [], disallow: ['/'], userAgent: 'GPTBot' },
|
|
56
|
+
],
|
|
57
|
+
})
|
|
58
|
+
)
|
|
59
|
+
).not.toBe(robotsSettingsSignature(settings()));
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('changes when only the order of the user-agent blocks differs', () => {
|
|
63
|
+
const first = settings({
|
|
64
|
+
userAgentRules: [
|
|
65
|
+
{ allow: [], disallow: ['/'], userAgent: 'GPTBot' },
|
|
66
|
+
{ allow: ['/'], disallow: [], userAgent: '*' },
|
|
67
|
+
],
|
|
68
|
+
});
|
|
69
|
+
const swapped = settings({
|
|
70
|
+
userAgentRules: [
|
|
71
|
+
{ allow: ['/'], disallow: [], userAgent: '*' },
|
|
72
|
+
{ allow: [], disallow: ['/'], userAgent: 'GPTBot' },
|
|
73
|
+
],
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
expect(robotsSettingsSignature(first)).not.toBe(robotsSettingsSignature(swapped));
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('changes for each of the scalar fields', () => {
|
|
80
|
+
expect(robotsSettingsSignature(settings({ customRules: '# hi' }))).not.toBe(
|
|
81
|
+
robotsSettingsSignature(settings())
|
|
82
|
+
);
|
|
83
|
+
expect(robotsSettingsSignature(settings({ isIndexingEnabled: false }))).not.toBe(
|
|
84
|
+
robotsSettingsSignature(settings())
|
|
85
|
+
);
|
|
86
|
+
expect(robotsSettingsSignature(settings({ sitemapEnabled: false }))).not.toBe(
|
|
87
|
+
robotsSettingsSignature(settings())
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('does not confuse an allow list with a disallow list of the same paths', () => {
|
|
92
|
+
expect(
|
|
93
|
+
robotsSettingsSignature(
|
|
94
|
+
settings({ userAgentRules: [{ allow: ['/a'], disallow: [], userAgent: '*' }] })
|
|
95
|
+
)
|
|
96
|
+
).not.toBe(
|
|
97
|
+
robotsSettingsSignature(
|
|
98
|
+
settings({ userAgentRules: [{ allow: [], disallow: ['/a'], userAgent: '*' }] })
|
|
99
|
+
)
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A canonical, order-independent fingerprint of a `RobotsSettings` value.
|
|
3
|
-
*
|
|
4
|
-
* `RobotsCard` needs the same question answered twice, and both answers have to be about
|
|
5
|
-
* VALUES rather than object identity. It asks "has the operator changed anything?" to gate
|
|
6
|
-
* the Save button, and "did the settings the server sent actually change?" to decide
|
|
7
|
-
* whether to re-seed its local state from the prop. The second question is the one that
|
|
8
|
-
* used to go unasked: the card seeded its state once and never looked at the prop again,
|
|
9
|
-
* so after a save the server's normalised row — blank paths dropped, leading slashes added
|
|
10
|
-
* — arrived and was ignored, leaving the dirty indicator lit forever and the preview
|
|
11
|
-
* showing rules that had already been tidied away.
|
|
12
|
-
*
|
|
13
|
-
* The naive fix, an effect keyed on the `settings` object, is wrong in the other
|
|
14
|
-
* direction, and for a reason worth writing down: a server component hands down a fresh
|
|
15
|
-
* object on every render, so `settings` changes identity on every `router.refresh()` even
|
|
16
|
-
* when nothing about it changed. Keying on identity would therefore blow away whatever the
|
|
17
|
-
* operator was typing whenever anything else on the page revalidated. Comparing
|
|
18
|
-
* fingerprints reduces "the prop changed" to "the prop's content changed", which is the
|
|
19
|
-
* only version of the question that is safe to act on.
|
|
20
|
-
*
|
|
21
|
-
* Serialising an explicit array rather than `JSON.stringify(settings)` removes the last
|
|
22
|
-
* hidden assumption in the old dirty check: `JSON.stringify` preserves insertion order, so
|
|
23
|
-
* that comparison silently relied on the form's object literal and
|
|
24
|
-
* `normalizeRobotsSettings`' object literal listing their keys identically. They do today
|
|
25
|
-
* — the house convention is alphabetical — but a future reorder would have turned a clean
|
|
26
|
-
* form permanently dirty, and nothing would have pointed at the cause.
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
import type { RobotsSettings } from '@nextblock-cms/utils/seo';
|
|
30
|
-
|
|
31
|
-
export function robotsSettingsSignature(settings: RobotsSettings): string {
|
|
32
|
-
return JSON.stringify([
|
|
33
|
-
settings.customRules,
|
|
34
|
-
settings.isIndexingEnabled,
|
|
35
|
-
settings.sitemapEnabled,
|
|
36
|
-
// Rule ORDER is deliberately significant: robots.txt groups are matched in order, so
|
|
37
|
-
// two settings differing only in how their user-agent blocks are sequenced are two
|
|
38
|
-
// different files and must not fingerprint the same.
|
|
39
|
-
settings.userAgentRules.map((rule) => [rule.userAgent, rule.allow, rule.disallow]),
|
|
40
|
-
]);
|
|
41
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* A canonical, order-independent fingerprint of a `RobotsSettings` value.
|
|
3
|
+
*
|
|
4
|
+
* `RobotsCard` needs the same question answered twice, and both answers have to be about
|
|
5
|
+
* VALUES rather than object identity. It asks "has the operator changed anything?" to gate
|
|
6
|
+
* the Save button, and "did the settings the server sent actually change?" to decide
|
|
7
|
+
* whether to re-seed its local state from the prop. The second question is the one that
|
|
8
|
+
* used to go unasked: the card seeded its state once and never looked at the prop again,
|
|
9
|
+
* so after a save the server's normalised row — blank paths dropped, leading slashes added
|
|
10
|
+
* — arrived and was ignored, leaving the dirty indicator lit forever and the preview
|
|
11
|
+
* showing rules that had already been tidied away.
|
|
12
|
+
*
|
|
13
|
+
* The naive fix, an effect keyed on the `settings` object, is wrong in the other
|
|
14
|
+
* direction, and for a reason worth writing down: a server component hands down a fresh
|
|
15
|
+
* object on every render, so `settings` changes identity on every `router.refresh()` even
|
|
16
|
+
* when nothing about it changed. Keying on identity would therefore blow away whatever the
|
|
17
|
+
* operator was typing whenever anything else on the page revalidated. Comparing
|
|
18
|
+
* fingerprints reduces "the prop changed" to "the prop's content changed", which is the
|
|
19
|
+
* only version of the question that is safe to act on.
|
|
20
|
+
*
|
|
21
|
+
* Serialising an explicit array rather than `JSON.stringify(settings)` removes the last
|
|
22
|
+
* hidden assumption in the old dirty check: `JSON.stringify` preserves insertion order, so
|
|
23
|
+
* that comparison silently relied on the form's object literal and
|
|
24
|
+
* `normalizeRobotsSettings`' object literal listing their keys identically. They do today
|
|
25
|
+
* — the house convention is alphabetical — but a future reorder would have turned a clean
|
|
26
|
+
* form permanently dirty, and nothing would have pointed at the cause.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import type { RobotsSettings } from '@nextblock-cms/utils/seo';
|
|
30
|
+
|
|
31
|
+
export function robotsSettingsSignature(settings: RobotsSettings): string {
|
|
32
|
+
return JSON.stringify([
|
|
33
|
+
settings.customRules,
|
|
34
|
+
settings.isIndexingEnabled,
|
|
35
|
+
settings.sitemapEnabled,
|
|
36
|
+
// Rule ORDER is deliberately significant: robots.txt groups are matched in order, so
|
|
37
|
+
// two settings differing only in how their user-agent blocks are sequenced are two
|
|
38
|
+
// different files and must not fingerprint the same.
|
|
39
|
+
settings.userAgentRules.map((rule) => [rule.userAgent, rule.allow, rule.disallow]),
|
|
40
|
+
]);
|
|
41
|
+
}
|