bsmnt 0.9.0 → 0.11.0
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/application/add-integration/index.d.ts.map +1 -1
- package/dist/application/add-integration/index.js +19 -2
- package/dist/application/add-integration/index.js.map +1 -1
- package/dist/application/shared/node-version.js.map +1 -1
- package/dist/core/create/internal-ids.d.ts +3 -0
- package/dist/core/create/internal-ids.d.ts.map +1 -1
- package/dist/core/create/internal-ids.js +4 -0
- package/dist/core/create/internal-ids.js.map +1 -1
- package/dist/domain/agents.d.ts.map +1 -1
- package/dist/domain/animations.d.ts.map +1 -1
- package/dist/domain/cms.d.ts.map +1 -1
- package/dist/domain/forms.d.ts.map +1 -1
- package/dist/domain/hooks.d.ts.map +1 -1
- package/dist/domain/skills.d.ts.map +1 -1
- package/dist/domain/templates.d.ts +10 -0
- package/dist/domain/templates.d.ts.map +1 -1
- package/dist/domain/templates.js +5 -0
- package/dist/domain/templates.js.map +1 -1
- package/dist/index.js +18 -5
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/agents/register-sanity-mcp.js +3 -8
- package/dist/infrastructure/agents/register-sanity-mcp.js.map +1 -1
- package/dist/infrastructure/agents/register-storybook-mcp.d.ts +18 -0
- package/dist/infrastructure/agents/register-storybook-mcp.d.ts.map +1 -0
- package/dist/infrastructure/agents/register-storybook-mcp.js +157 -0
- package/dist/infrastructure/agents/register-storybook-mcp.js.map +1 -0
- package/dist/infrastructure/create/lifecycle-registry.d.ts.map +1 -1
- package/dist/infrastructure/create/lifecycle-registry.js +39 -1
- package/dist/infrastructure/create/lifecycle-registry.js.map +1 -1
- package/dist/infrastructure/create/merge-registry.d.ts.map +1 -1
- package/dist/infrastructure/create/merge-registry.js +4 -0
- package/dist/infrastructure/create/merge-registry.js.map +1 -1
- package/dist/infrastructure/create/sanity-auth.js.map +1 -1
- package/dist/infrastructure/create/setup-agent.d.ts +6 -1
- package/dist/infrastructure/create/setup-agent.d.ts.map +1 -1
- package/dist/infrastructure/create/setup-agent.js +23 -21
- package/dist/infrastructure/create/setup-agent.js.map +1 -1
- package/dist/infrastructure/create/setup-dotenvx.js.map +1 -1
- package/dist/infrastructure/create/setup-remote.js.map +1 -1
- package/dist/infrastructure/create/setup-sanity.d.ts.map +1 -1
- package/dist/infrastructure/create/setup-sanity.js +12 -1
- package/dist/infrastructure/create/setup-sanity.js.map +1 -1
- package/dist/infrastructure/integrations/ensure-studio-headers.d.ts +6 -0
- package/dist/infrastructure/integrations/ensure-studio-headers.d.ts.map +1 -0
- package/dist/infrastructure/integrations/ensure-studio-headers.js +153 -0
- package/dist/infrastructure/integrations/ensure-studio-headers.js.map +1 -0
- package/dist/infrastructure/integrations/inject-integration.d.ts.map +1 -1
- package/dist/infrastructure/integrations/inject-integration.js +2 -0
- package/dist/infrastructure/integrations/inject-integration.js.map +1 -1
- package/dist/infrastructure/update/notifier.js.map +1 -1
- package/dist/modules/features/cms/index.d.ts.map +1 -1
- package/dist/modules/features/cms/index.js +2 -0
- package/dist/modules/features/cms/index.js.map +1 -1
- package/dist/modules/features/cms/sanity/config.d.ts.map +1 -1
- package/dist/modules/features/cms/sanity/config.js +3 -1
- package/dist/modules/features/cms/sanity/config.js.map +1 -1
- package/dist/modules/features/cms/sanity/index.d.ts.map +1 -1
- package/dist/modules/features/cms/sanity/index.js +10 -1
- package/dist/modules/features/cms/sanity/index.js.map +1 -1
- package/dist/modules/features/cms/sanity/mergers/check-integration.d.ts.map +1 -1
- package/dist/modules/features/cms/sanity/mergers/check-integration.js +15 -6
- package/dist/modules/features/cms/sanity/mergers/check-integration.js.map +1 -1
- package/dist/modules/features/cms/sanity/mergers/knip.d.ts.map +1 -1
- package/dist/modules/features/cms/sanity/mergers/knip.js +1 -0
- package/dist/modules/features/cms/sanity/mergers/knip.js.map +1 -1
- package/dist/modules/features/cms/sanity/mergers/layout.js.map +1 -1
- package/dist/modules/features/cms/sanity/mergers/package-json.d.ts +12 -0
- package/dist/modules/features/cms/sanity/mergers/package-json.d.ts.map +1 -0
- package/dist/modules/features/cms/sanity/mergers/package-json.js +19 -0
- package/dist/modules/features/cms/sanity/mergers/package-json.js.map +1 -0
- package/dist/modules/features/cms/sanity-pagebuilder/config.d.ts.map +1 -1
- package/dist/modules/features/cms/sanity-pagebuilder/config.js +2 -1
- package/dist/modules/features/cms/sanity-pagebuilder/config.js.map +1 -1
- package/dist/modules/features/cms/sanity-pagebuilder/index.d.ts.map +1 -1
- package/dist/modules/features/cms/sanity-pagebuilder/index.js +7 -1
- package/dist/modules/features/cms/sanity-pagebuilder/index.js.map +1 -1
- package/dist/modules/features/cms/sanity-pagebuilder-cache/config.d.ts +14 -0
- package/dist/modules/features/cms/sanity-pagebuilder-cache/config.d.ts.map +1 -0
- package/dist/modules/features/cms/sanity-pagebuilder-cache/config.js +50 -0
- package/dist/modules/features/cms/sanity-pagebuilder-cache/config.js.map +1 -0
- package/dist/modules/features/cms/sanity-pagebuilder-cache/index.d.ts +3 -0
- package/dist/modules/features/cms/sanity-pagebuilder-cache/index.d.ts.map +1 -0
- package/dist/modules/features/cms/sanity-pagebuilder-cache/index.js +55 -0
- package/dist/modules/features/cms/sanity-pagebuilder-cache/index.js.map +1 -0
- package/dist/modules/presets/index.d.ts.map +1 -1
- package/dist/modules/presets/index.js +2 -0
- package/dist/modules/presets/index.js.map +1 -1
- package/dist/modules/presets/next-pagebuilder-cache.d.ts +3 -0
- package/dist/modules/presets/next-pagebuilder-cache.d.ts.map +1 -0
- package/dist/modules/presets/next-pagebuilder-cache.js +10 -0
- package/dist/modules/presets/next-pagebuilder-cache.js.map +1 -0
- package/dist/paths.d.ts +6 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +6 -0
- package/dist/paths.js.map +1 -1
- package/package.json +6 -3
- package/src/modules/features/cms/sanity/files/app/api/blog/[slug]/route.ts +4 -1
- package/src/modules/features/cms/sanity/files/app/sitemap.md/route.ts +2 -1
- package/src/modules/features/cms/sanity/files/components/ui/sanity-image/index.tsx +19 -5
- package/src/modules/features/cms/sanity/files/lib/integrations/check-integration.ts +7 -6
- package/src/modules/features/cms/sanity/files/lib/integrations/sanity/README.md +32 -1
- package/src/modules/features/cms/sanity/files/lib/integrations/sanity/components/rich-text.tsx +1 -0
- package/src/modules/features/cms/sanity/files/lib/integrations/sanity/env.ts +2 -2
- package/src/modules/features/cms/sanity/files/proxy.ts +9 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/app/api/[[...slug]]/route.ts +12 -3
- package/src/modules/features/cms/sanity-pagebuilder/files/app/api/revalidate/route.ts +172 -24
- package/src/modules/features/cms/sanity-pagebuilder/files/app/feed.xml/route.ts +14 -7
- package/src/modules/features/cms/sanity-pagebuilder/files/app/sitemap.md/route.ts +8 -2
- package/src/modules/features/cms/sanity-pagebuilder/files/app/studio/[[...tool]]/page.tsx +16 -1
- package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/rich-text.tsx +1 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/visual-editing.tsx +2 -2
- package/src/modules/features/cms/sanity-pagebuilder/files/components/ui/sanity-image/index.tsx +15 -5
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/check-integration.ts +6 -5
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/client.ts +8 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/slug-field.tsx +13 -7
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/slug-notices.tsx +125 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/env.ts +2 -2
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/fetchers/layout.ts +22 -52
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/icons.ts +2 -2
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/live/index.tsx +4 -2
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/markdown-proxy.config.ts +1 -1
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-builder-config.ts +7 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-tree-pane.tsx +70 -57
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/queries.ts +119 -29
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/revalidate-paths.ts +70 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/index.ts +1 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/rich-text-block.ts +39 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/index.ts +2 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/redirect.ts +35 -4
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/page.ts +12 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/tags.ts +56 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/utils/page-builder-markdown.ts +12 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/sitemap.ts +11 -8
- package/src/modules/features/cms/sanity-pagebuilder/files/proxy.ts +9 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/.knip.json +8 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/api/[[...slug]]/route.ts +112 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/api/draft-mode/disable/route.ts +7 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/api/draft-mode/enable/route.ts +20 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/api/revalidate/route.ts +292 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/feed.xml/route.ts +66 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/sitemap.md/route.ts +132 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/studio/[[...tool]]/layout.tsx +26 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/studio/[[...tool]]/page.tsx +23 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/sanity/draft-mode-toggle.tsx +27 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/sanity/rich-text.tsx +88 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/sanity/visual-editing.tsx +11 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/ui/sanity-image/index.tsx +51 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/check-integration.ts +6 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/client.ts +37 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/block-actions-item.tsx +81 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/disable-draft-mode.tsx +23 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/page-builder-input.tsx +39 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/page-category-input.tsx +50 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/same-as-input.tsx +66 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/slug-field.tsx +24 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/slug-input.tsx +14 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/slug-notices.tsx +125 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/confirm-publish-action.ts +40 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/env.ts +34 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/fetchers/layout.ts +39 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/fetchers/redirects.ts +22 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/icons.ts +81 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/live/index.tsx +140 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/markdown-proxy.config.ts +50 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/page-builder-config.ts +144 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/page-category.ts +28 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/page-tree-pane.tsx +431 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/pinned-pages.ts +54 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/presentation.ts +119 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/queries.ts +548 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/revalidate-paths.ts +70 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/sanity.cli.ts +29 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/sanity.config.ts +116 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/index.ts +6 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/blog-content.ts +89 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/description.ts +29 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/gated-content.ts +178 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/hero.ts +28 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/rich-text-block.ts +39 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/singleton/content-collection.ts +45 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/content/author.ts +70 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/content/blog-category.ts +55 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/index.ts +134 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/company-data.ts +70 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/footer.ts +79 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/navbar.ts +75 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/redirect.ts +152 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/structured-data.ts +124 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/link.ts +125 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/logo-field.ts +9 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/metadata.ts +132 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/nav-objects.ts +192 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/page-builder.ts +43 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/page-folder.ts +124 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/page.ts +265 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/richText.ts +63 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/structured-data-blocks.ts +436 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/singletons.ts +51 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/structure.ts +334 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/tags.ts +89 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/utils/image.ts +8 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/utils/link.ts +140 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/utils/page-builder-markdown.ts +113 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/utils/page-json-ld.ts +208 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/scripts/sanity-typegen.ts +36 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/check-redirect.ts +30 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/json-ld.tsx +522 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/metadata.ts +189 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/rss.ts +96 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/sitemap.ts +58 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/proxy.ts +90 -0
- package/src/modules/features/env/dotenvx/files/vercelignore +5 -0
- package/src/templates/next-default/.env.example +4 -2
- package/src/templates/next-default/.github/workflows/ci.yml +1 -1
- package/src/templates/next-default/.vscode/settings.json +0 -2
- package/src/templates/next-default/app/robots.ts +2 -3
- package/src/templates/next-default/components/ui/image/index.tsx +2 -4
- package/src/templates/next-default/knip.json +1 -1
- package/src/templates/next-default/lefthook.yml +1 -1
- package/src/templates/next-default/lib/utils/README.md +18 -5
- package/src/templates/next-default/lib/utils/image-sizes.ts +72 -0
- package/src/templates/next-default/lib/utils/merge-refs.ts +56 -0
- package/src/templates/next-default/lib/utils/strings.ts +1 -51
- package/src/templates/next-default/next.config.ts +38 -5
- package/src/templates/next-default/package.json +9 -12
- package/src/templates/next-experiments/.env.example +4 -2
- package/src/templates/next-experiments/.github/workflows/ci.yml +1 -1
- package/src/templates/next-experiments/.vscode/settings.json +0 -2
- package/src/templates/next-experiments/app/page.tsx +1 -1
- package/src/templates/next-experiments/app/robots.ts +2 -3
- package/src/templates/next-experiments/components/ui/image/index.tsx +2 -4
- package/src/templates/next-experiments/knip.json +1 -1
- package/src/templates/next-experiments/lefthook.yml +1 -1
- package/src/templates/next-experiments/lib/utils/README.md +18 -5
- package/src/templates/next-experiments/lib/utils/image-sizes.ts +72 -0
- package/src/templates/next-experiments/lib/utils/merge-refs.ts +56 -0
- package/src/templates/next-experiments/lib/utils/strings.ts +1 -51
- package/src/templates/next-experiments/next.config.ts +38 -5
- package/src/templates/next-experiments/package.json +9 -12
- package/src/templates/next-pagebuilder/.env.example +3 -0
- package/src/templates/next-pagebuilder/.github/workflows/ci.yml +1 -1
- package/src/templates/next-pagebuilder/.vscode/settings.json +0 -2
- package/src/templates/next-pagebuilder/README.md +95 -0
- package/src/templates/next-pagebuilder/app/(content)/[[...slug]]/page.tsx +24 -9
- package/src/templates/next-pagebuilder/app/robots.ts +2 -3
- package/src/templates/next-pagebuilder/app/sitemap.xml/route.ts +9 -4
- package/src/templates/next-pagebuilder/biome.json +2 -1
- package/src/templates/next-pagebuilder/components/layout/footer/index.tsx +3 -6
- package/src/templates/next-pagebuilder/components/layout/header/index.tsx +3 -2
- package/src/templates/next-pagebuilder/components/layout/json-ld/index.tsx +3 -8
- package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/content-filters.tsx +23 -40
- package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/index.tsx +88 -58
- package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/skeleton.tsx +41 -0
- package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/types.ts +14 -1
- package/src/templates/next-pagebuilder/components/page-builder/components/rich-text-block.tsx +16 -0
- package/src/templates/next-pagebuilder/components/page-builder/renderer.tsx +23 -3
- package/src/templates/next-pagebuilder/components/page-document/index.tsx +1 -0
- package/src/templates/next-pagebuilder/components/ui/image/index.tsx +2 -1
- package/src/templates/next-pagebuilder/knip.json +3 -2
- package/src/templates/next-pagebuilder/lefthook.yml +1 -1
- package/src/templates/next-pagebuilder/lib/integrations/sanity/sanity.types.ts +1221 -977
- package/src/templates/next-pagebuilder/lib/utils/image-sizes.ts +72 -0
- package/src/templates/next-pagebuilder/lib/utils/merge-refs.ts +56 -0
- package/src/templates/next-pagebuilder/next.config.ts +35 -5
- package/src/templates/next-pagebuilder/package.json +8 -10
- package/src/templates/next-pagebuilder-cache/.biome/plugins/README.md +21 -0
- package/src/templates/next-pagebuilder-cache/.biome/plugins/no-anchor-element.grit +12 -0
- package/src/templates/next-pagebuilder-cache/.biome/plugins/no-relative-parent-imports.grit +10 -0
- package/src/templates/next-pagebuilder-cache/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
- package/src/templates/next-pagebuilder-cache/.env.example +40 -0
- package/src/templates/next-pagebuilder-cache/.github/PULL_REQUEST_TEMPLATE.md +13 -0
- package/src/templates/next-pagebuilder-cache/.github/workflows/ci.yml +105 -0
- package/src/templates/next-pagebuilder-cache/.vscode/extensions.json +19 -0
- package/src/templates/next-pagebuilder-cache/.vscode/settings.json +103 -0
- package/src/templates/next-pagebuilder-cache/.zed/settings.json +47 -0
- package/src/templates/next-pagebuilder-cache/README.md +212 -0
- package/src/templates/next-pagebuilder-cache/_gitignore +65 -0
- package/src/templates/next-pagebuilder-cache/app/(content)/[[...slug]]/page.tsx +124 -0
- package/src/templates/next-pagebuilder-cache/app/(content)/layout.tsx +28 -0
- package/src/templates/next-pagebuilder-cache/app/actions/unlock-gated-content.ts +53 -0
- package/src/templates/next-pagebuilder-cache/app/favicon.ico +0 -0
- package/src/templates/next-pagebuilder-cache/app/layout.tsx +83 -0
- package/src/templates/next-pagebuilder-cache/app/robots.ts +13 -0
- package/src/templates/next-pagebuilder-cache/app/sitemap.xml/route.ts +84 -0
- package/src/templates/next-pagebuilder-cache/biome.json +257 -0
- package/src/templates/next-pagebuilder-cache/components/layout/footer/index.tsx +127 -0
- package/src/templates/next-pagebuilder-cache/components/layout/header/components/cta-button.tsx +28 -0
- package/src/templates/next-pagebuilder-cache/components/layout/header/components/mega-menu-panel.tsx +90 -0
- package/src/templates/next-pagebuilder-cache/components/layout/header/components/nav-item-renderer.tsx +98 -0
- package/src/templates/next-pagebuilder-cache/components/layout/header/components/nav-leaf-item.tsx +33 -0
- package/src/templates/next-pagebuilder-cache/components/layout/header/components/types.ts +7 -0
- package/src/templates/next-pagebuilder-cache/components/layout/header/header-client.tsx +110 -0
- package/src/templates/next-pagebuilder-cache/components/layout/header/index.tsx +32 -0
- package/src/templates/next-pagebuilder-cache/components/layout/json-ld/index.tsx +79 -0
- package/src/templates/next-pagebuilder-cache/components/layout/wrapper/index.tsx +33 -0
- package/src/templates/next-pagebuilder-cache/components/page-builder/components/article-content/index.tsx +78 -0
- package/src/templates/next-pagebuilder-cache/components/page-builder/components/article-content/related-post-item.tsx +27 -0
- package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/content-card.tsx +66 -0
- package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/content-filters.tsx +76 -0
- package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/content-grid.tsx +40 -0
- package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/content-pagination-nav.tsx +71 -0
- package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/index.tsx +306 -0
- package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/skeleton.tsx +41 -0
- package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/types.ts +32 -0
- package/src/templates/next-pagebuilder-cache/components/page-builder/components/description.tsx +17 -0
- package/src/templates/next-pagebuilder-cache/components/page-builder/components/gated-content/index.tsx +169 -0
- package/src/templates/next-pagebuilder-cache/components/page-builder/components/hero.tsx +17 -0
- package/src/templates/next-pagebuilder-cache/components/page-builder/components/rich-text-block.tsx +16 -0
- package/src/templates/next-pagebuilder-cache/components/page-builder/renderer.tsx +71 -0
- package/src/templates/next-pagebuilder-cache/components/page-builder/types.ts +25 -0
- package/src/templates/next-pagebuilder-cache/components/page-document/index.tsx +147 -0
- package/src/templates/next-pagebuilder-cache/components/ui/image/index.tsx +216 -0
- package/src/templates/next-pagebuilder-cache/components/ui/link/index.tsx +152 -0
- package/src/templates/next-pagebuilder-cache/components.json +24 -0
- package/src/templates/next-pagebuilder-cache/knip.json +35 -0
- package/src/templates/next-pagebuilder-cache/lefthook.yml +25 -0
- package/src/templates/next-pagebuilder-cache/lib/integrations/sanity/sanity.types.ts +1588 -0
- package/src/templates/next-pagebuilder-cache/lib/scripts/setup-ci-secrets.ts +117 -0
- package/src/templates/next-pagebuilder-cache/lib/styles/cn.ts +5 -0
- package/src/templates/next-pagebuilder-cache/lib/styles/global.css +69 -0
- package/src/templates/next-pagebuilder-cache/lib/utils/base-url.ts +17 -0
- package/src/templates/next-pagebuilder-cache/lib/utils/format-date.ts +8 -0
- package/src/templates/next-pagebuilder-cache/lib/utils/slug-tag.ts +6 -0
- package/src/templates/next-pagebuilder-cache/next.config.ts +176 -0
- package/src/templates/next-pagebuilder-cache/package.json +62 -0
- package/src/templates/next-pagebuilder-cache/postcss.config.mjs +65 -0
- package/src/templates/next-pagebuilder-cache/svg.d.ts +6 -0
- package/src/templates/next-pagebuilder-cache/tsconfig.json +38 -0
- package/src/templates/next-webgl/.env.example +4 -2
- package/src/templates/next-webgl/.github/workflows/ci.yml +1 -1
- package/src/templates/next-webgl/.vscode/settings.json +0 -2
- package/src/templates/next-webgl/app/robots.ts +2 -3
- package/src/templates/next-webgl/components/ui/image/index.tsx +2 -4
- package/src/templates/next-webgl/knip.json +1 -1
- package/src/templates/next-webgl/lefthook.yml +1 -1
- package/src/templates/next-webgl/lib/utils/README.md +18 -5
- package/src/templates/next-webgl/lib/utils/image-sizes.ts +72 -0
- package/src/templates/next-webgl/lib/utils/merge-refs.ts +56 -0
- package/src/templates/next-webgl/lib/utils/strings.ts +1 -51
- package/src/templates/next-webgl/next.config.ts +38 -5
- package/src/templates/next-webgl/package.json +11 -14
- package/src/templates/next-experiments/tsconfig.tsbuildinfo +0 -1
- package/src/templates/next-pagebuilder/app/actions/refresh.ts +0 -5
- package/src/templates/next-webgl/tsconfig.tsbuildinfo +0 -1
|
@@ -1,16 +1,37 @@
|
|
|
1
1
|
import { revalidatePath, revalidateTag } from "next/cache";
|
|
2
2
|
import { type NextRequest, NextResponse } from "next/server";
|
|
3
3
|
import { parseBody } from "next-sanity/webhook";
|
|
4
|
-
|
|
4
|
+
// `directClient`, never `client`: the shared client runs `useCdn: true`, and every
|
|
5
|
+
// lookup here reads state the mutation that triggered this request just produced.
|
|
6
|
+
// A CDN hit returns the pre-mutation slug or folder chain, which purges the old
|
|
7
|
+
// path twice and never touches the new one.
|
|
8
|
+
import { directClient } from "@/lib/integrations/sanity/client";
|
|
5
9
|
import { SANITY_LAYOUT_TAGS } from "@/lib/integrations/sanity/fetchers/layout";
|
|
6
10
|
import { SANITY_REDIRECTS_TAG } from "@/lib/integrations/sanity/fetchers/redirects";
|
|
7
11
|
import { pageBuilderReferenceMembers as pbrm } from "@/lib/integrations/sanity/page-builder-config";
|
|
8
12
|
import {
|
|
9
|
-
|
|
13
|
+
FOLDER_PATH_QUERY,
|
|
14
|
+
P_REF_DOC_PAGES_QUERY,
|
|
15
|
+
P_REV_FOLDER_DESCENDANTS_QUERY,
|
|
10
16
|
P_REV_SLUGS_QUERY,
|
|
11
17
|
} from "@/lib/integrations/sanity/queries";
|
|
12
|
-
import
|
|
18
|
+
import {
|
|
19
|
+
composeDescendantPaths,
|
|
20
|
+
composeResolvedSlug,
|
|
21
|
+
} from "@/lib/integrations/sanity/revalidate-paths";
|
|
22
|
+
import type {
|
|
23
|
+
FOLDER_PATH_QUERY_RESULT,
|
|
24
|
+
P_REF_DOC_PAGES_QUERY_RESULT,
|
|
25
|
+
P_REV_FOLDER_DESCENDANTS_QUERY_RESULT,
|
|
26
|
+
} from "@/lib/integrations/sanity/sanity.types";
|
|
27
|
+
import {
|
|
28
|
+
BLOG_COLLECTION_TAGS,
|
|
29
|
+
BLOG_COLLECTION_TYPES,
|
|
30
|
+
CROSS_DOC_TAG_DEPENDENCIES,
|
|
31
|
+
REFERENCEABLE_TYPES,
|
|
32
|
+
} from "@/lib/integrations/sanity/tags";
|
|
13
33
|
import { getPagePath } from "@/lib/utils/sitemap";
|
|
34
|
+
import { getSlugTag } from "@/lib/utils/slug-tag";
|
|
14
35
|
|
|
15
36
|
const ERRORS = {
|
|
16
37
|
BAD_REQ: ["Bad Request", { status: 400 }],
|
|
@@ -21,11 +42,16 @@ type WebhookSlug = {
|
|
|
21
42
|
current?: string | null;
|
|
22
43
|
};
|
|
23
44
|
|
|
45
|
+
type WebhookReference = {
|
|
46
|
+
_ref?: string | null;
|
|
47
|
+
};
|
|
48
|
+
|
|
24
49
|
type WebhookDocument = {
|
|
25
50
|
_id: string;
|
|
26
51
|
_type: string;
|
|
27
|
-
resolvedSlug?: string | null;
|
|
28
52
|
slug?: WebhookSlug | null;
|
|
53
|
+
pageFolder?: WebhookReference | null;
|
|
54
|
+
parentFolder?: WebhookReference | null;
|
|
29
55
|
};
|
|
30
56
|
|
|
31
57
|
type MutationOperation = "create" | "update" | "delete";
|
|
@@ -36,7 +62,27 @@ type RevalidateWebhookBody = WebhookDocument & {
|
|
|
36
62
|
after?: Partial<WebhookDocument> | null;
|
|
37
63
|
};
|
|
38
64
|
|
|
39
|
-
const
|
|
65
|
+
const referenceableTypes = new Set<string>([
|
|
66
|
+
...pbrm.map((m) => m.documentType),
|
|
67
|
+
...REFERENCEABLE_TYPES,
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
// Not `"max"`: a named profile expires a year out, so Next serves the stale entry
|
|
71
|
+
// once while refreshing. A publish should land on the next request, not the one after.
|
|
72
|
+
const PURGE = { expire: 0 } as const;
|
|
73
|
+
|
|
74
|
+
const bustTags = (tags: readonly string[]) => {
|
|
75
|
+
for (const tag of tags) {
|
|
76
|
+
revalidateTag(tag, PURGE);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Both: the path clears the page's route, the tag clears what no page render
|
|
81
|
+
// touches — above all the markdown route, which answers at /api/<slug>.md.
|
|
82
|
+
const bustPage = (slug: string | null) => {
|
|
83
|
+
revalidatePath(getPagePath(slug));
|
|
84
|
+
revalidateTag(getSlugTag(slug), PURGE);
|
|
85
|
+
};
|
|
40
86
|
|
|
41
87
|
const normalizeRevalidationSlug = (slug: string | null | undefined) => {
|
|
42
88
|
if (slug === undefined) return null;
|
|
@@ -45,9 +91,9 @@ const normalizeRevalidationSlug = (slug: string | null | undefined) => {
|
|
|
45
91
|
};
|
|
46
92
|
|
|
47
93
|
const getCanonicalPageSlug = async (documentId: string) => {
|
|
48
|
-
if (!
|
|
94
|
+
if (!directClient) return null;
|
|
49
95
|
|
|
50
|
-
const pageSlugs = await
|
|
96
|
+
const pageSlugs = await directClient.fetch<Array<{ slug: string | null }>>(
|
|
51
97
|
P_REV_SLUGS_QUERY,
|
|
52
98
|
{ documentIds: [documentId] },
|
|
53
99
|
);
|
|
@@ -57,24 +103,114 @@ const getCanonicalPageSlug = async (documentId: string) => {
|
|
|
57
103
|
);
|
|
58
104
|
};
|
|
59
105
|
|
|
60
|
-
const
|
|
61
|
-
if (!
|
|
106
|
+
const getFolder = async (folderId: string | null | undefined) => {
|
|
107
|
+
if (!(directClient && folderId)) return null;
|
|
62
108
|
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
{
|
|
66
|
-
|
|
67
|
-
|
|
109
|
+
const folder = await directClient.fetch<FOLDER_PATH_QUERY_RESULT>(
|
|
110
|
+
FOLDER_PATH_QUERY,
|
|
111
|
+
{ folderId },
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
return folder ?? null;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const getPagesReferencing = async (documentIds: readonly string[]) => {
|
|
118
|
+
if (!(directClient && documentIds.length)) return [];
|
|
119
|
+
|
|
120
|
+
const pages = await directClient.fetch<P_REF_DOC_PAGES_QUERY_RESULT>(
|
|
121
|
+
P_REF_DOC_PAGES_QUERY,
|
|
122
|
+
{ documentIds },
|
|
68
123
|
);
|
|
69
124
|
|
|
70
125
|
return (pages ?? []).map((page) => normalizeRevalidationSlug(page.slug));
|
|
71
126
|
};
|
|
72
127
|
|
|
128
|
+
// The webhook carries the raw document, so a page's URL has to be rebuilt from
|
|
129
|
+
// its folder — `slug.current` alone is the page's own segment, which is empty
|
|
130
|
+
// for a folder-index page and missing the prefix for every nested one.
|
|
131
|
+
//
|
|
132
|
+
// `undefined` means "no such revision", distinct from the `null` that a page at
|
|
133
|
+
// the site root legitimately resolves to. Without that split, a payload missing
|
|
134
|
+
// its `before()` projection would purge the homepage on every delete.
|
|
135
|
+
const resolvePageSlug = async (
|
|
136
|
+
revision: Partial<WebhookDocument> | null | undefined,
|
|
137
|
+
) => {
|
|
138
|
+
if (!revision) return undefined;
|
|
139
|
+
|
|
140
|
+
const folder = await getFolder(revision.pageFolder?._ref);
|
|
141
|
+
|
|
142
|
+
return composeResolvedSlug(folder?.path ?? null, revision.slug?.current);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// A folder rename or reparent rewrites the URL of everything beneath it. Neither
|
|
146
|
+
// the folder nor its descendants are `page` mutations, so nothing else in this
|
|
147
|
+
// route would touch them.
|
|
148
|
+
const revalidateFolderSubtree = async (body: RevalidateWebhookBody) => {
|
|
149
|
+
const { before, after } = body;
|
|
150
|
+
|
|
151
|
+
const previousFolderSlug = before?.slug?.current;
|
|
152
|
+
const moved =
|
|
153
|
+
previousFolderSlug !== after?.slug?.current ||
|
|
154
|
+
before?.parentFolder?._ref !== after?.parentFolder?._ref;
|
|
155
|
+
|
|
156
|
+
// A folder edit that touches neither the slug nor the parent — a title, say —
|
|
157
|
+
// changes no URL. `pageFolder` still busts the navbar and collection tags
|
|
158
|
+
// through CROSS_DOC_TAG_DEPENDENCIES below.
|
|
159
|
+
//
|
|
160
|
+
// Deletion is skipped outright: `pageFolder` and `parentFolder` are plain hard
|
|
161
|
+
// references, so Sanity refuses to delete a folder that still has descendants.
|
|
162
|
+
// There is no orphaned subtree to unwind, and querying for one would just cost
|
|
163
|
+
// two round trips to find nothing.
|
|
164
|
+
if (body.operation === "delete") return [];
|
|
165
|
+
if (!(directClient && moved && previousFolderSlug)) return [];
|
|
166
|
+
|
|
167
|
+
const previousParent = await getFolder(before?.parentFolder?._ref);
|
|
168
|
+
|
|
169
|
+
const descendants =
|
|
170
|
+
await directClient.fetch<P_REV_FOLDER_DESCENDANTS_QUERY_RESULT>(
|
|
171
|
+
P_REV_FOLDER_DESCENDANTS_QUERY,
|
|
172
|
+
{ folderId: body._id },
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
const movedIds: string[] = [];
|
|
176
|
+
|
|
177
|
+
for (const descendant of descendants ?? []) {
|
|
178
|
+
const paths = composeDescendantPaths({
|
|
179
|
+
folderChain: descendant.folderChain ?? [],
|
|
180
|
+
ownSlug: descendant.ownSlug,
|
|
181
|
+
folderId: body._id,
|
|
182
|
+
previousFolderSlug,
|
|
183
|
+
previousParentPath: previousParent?.path ?? null,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
if (!paths) {
|
|
187
|
+
console.error("Revalidation: unresolvable folder chain", descendant._id);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
movedIds.push(descendant._id);
|
|
192
|
+
bustPage(paths.current);
|
|
193
|
+
bustPage(paths.previous);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Pages linking *into* the subtree inline the old resolved slugs too.
|
|
197
|
+
return getPagesReferencing(movedIds);
|
|
198
|
+
};
|
|
199
|
+
|
|
73
200
|
const getAffectedPageSlugsForWebhook = async (body: RevalidateWebhookBody) => {
|
|
74
201
|
const pages: Array<string | null> = [];
|
|
75
202
|
|
|
76
|
-
if (body.
|
|
77
|
-
|
|
203
|
+
if (body._type === "page" && body.before) {
|
|
204
|
+
const previousSlug = await resolvePageSlug(body.before);
|
|
205
|
+
|
|
206
|
+
// Resolved, not raw: a page moved to another folder keeps its own slug and
|
|
207
|
+
// still vacates its old URL, so comparing own slugs would miss it.
|
|
208
|
+
const vacated =
|
|
209
|
+
body.operation === "delete" ||
|
|
210
|
+
(body.operation === "update" &&
|
|
211
|
+
previousSlug !== (await resolvePageSlug(body.after)));
|
|
212
|
+
|
|
213
|
+
if (vacated && previousSlug !== undefined) bustPage(previousSlug);
|
|
78
214
|
}
|
|
79
215
|
|
|
80
216
|
// If the document is a page, get its slug
|
|
@@ -83,13 +219,17 @@ const getAffectedPageSlugsForWebhook = async (body: RevalidateWebhookBody) => {
|
|
|
83
219
|
if (data !== null) pages.push(data);
|
|
84
220
|
}
|
|
85
221
|
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
pages.push(...
|
|
222
|
+
// Page-builder blocks, `author`, `blogCategory` and other pages are all
|
|
223
|
+
// inlined into pages that no other branch here revalidates.
|
|
224
|
+
if (referenceableTypes.has(body._type)) {
|
|
225
|
+
pages.push(...(await getPagesReferencing([body._id])));
|
|
90
226
|
}
|
|
91
227
|
|
|
92
|
-
|
|
228
|
+
if (body._type === "pageFolder") {
|
|
229
|
+
pages.push(...(await revalidateFolderSubtree(body)));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return [...new Set(pages.filter((page): page is string => page !== null))];
|
|
93
233
|
};
|
|
94
234
|
|
|
95
235
|
export async function POST(request: NextRequest) {
|
|
@@ -107,15 +247,23 @@ export async function POST(request: NextRequest) {
|
|
|
107
247
|
const affectedPageSlugs = await getAffectedPageSlugsForWebhook(body);
|
|
108
248
|
|
|
109
249
|
for (const slug of affectedPageSlugs) {
|
|
110
|
-
|
|
250
|
+
bustPage(slug);
|
|
111
251
|
}
|
|
112
252
|
|
|
113
253
|
if (SANITY_LAYOUT_TAGS.includes(body?._type)) {
|
|
114
|
-
revalidateTag(body._type,
|
|
254
|
+
revalidateTag(body._type, PURGE);
|
|
115
255
|
}
|
|
116
256
|
|
|
257
|
+
// Queries that read across document types — a `structuredData` entry that
|
|
258
|
+
// folds in `companyData`, a navbar that derefs pages.
|
|
259
|
+
bustTags(CROSS_DOC_TAG_DEPENDENCIES[body._type] ?? []);
|
|
260
|
+
|
|
261
|
+
// The content-collection block caches list/count/categories under their own
|
|
262
|
+
// tags, which no per-page revalidation above touches.
|
|
263
|
+
if (BLOG_COLLECTION_TYPES.has(body._type)) bustTags(BLOG_COLLECTION_TAGS);
|
|
264
|
+
|
|
117
265
|
if (body._type === SANITY_REDIRECTS_TAG) {
|
|
118
|
-
revalidateTag(SANITY_REDIRECTS_TAG,
|
|
266
|
+
revalidateTag(SANITY_REDIRECTS_TAG, PURGE);
|
|
119
267
|
}
|
|
120
268
|
|
|
121
269
|
return NextResponse.json({ status: 200, now: Date.now() });
|
|
@@ -1,20 +1,27 @@
|
|
|
1
|
+
import { fetchSanity } from "@/lib/integrations/sanity/live";
|
|
1
2
|
import { ALL_BLOG_ARTICLES_QUERY } from "@/lib/integrations/sanity/queries";
|
|
3
|
+
import type { ALL_BLOG_ARTICLES_QUERY_RESULT } from "@/lib/integrations/sanity/sanity.types";
|
|
4
|
+
import { FEED_TAG } from "@/lib/integrations/sanity/tags";
|
|
2
5
|
import { generateRssFeed, rssErrorResponse } from "@/lib/utils/rss";
|
|
3
6
|
import { getPageUrl, getSitemapBaseUrl } from "@/lib/utils/sitemap";
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
|
|
8
|
+
// Not `force-dynamic`: feed readers poll, so per-request rendering charged a
|
|
9
|
+
// Sanity round trip per poll. The webhook busts FEED_TAG for `page`, `pageFolder`,
|
|
10
|
+
// `author` and `blogCategory` — this query derefs the byline and resolves slugs
|
|
11
|
+
// through folder chains.
|
|
7
12
|
export async function GET() {
|
|
8
13
|
const baseUrl = getSitemapBaseUrl();
|
|
9
14
|
|
|
10
15
|
try {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
const { data: articles } =
|
|
17
|
+
await fetchSanity<ALL_BLOG_ARTICLES_QUERY_RESULT>({
|
|
18
|
+
query: ALL_BLOG_ARTICLES_QUERY,
|
|
19
|
+
tags: [FEED_TAG],
|
|
20
|
+
});
|
|
14
21
|
|
|
15
|
-
|
|
22
|
+
if (!articles) return rssErrorResponse();
|
|
16
23
|
|
|
17
|
-
const items =
|
|
24
|
+
const items = articles
|
|
18
25
|
.filter((article) => article.resolvedSlug && !article.noIndex)
|
|
19
26
|
.map((article) => ({
|
|
20
27
|
title: article.title ?? "Untitled",
|
|
@@ -7,7 +7,8 @@ import {
|
|
|
7
7
|
getSitemapBaseUrl,
|
|
8
8
|
} from "@/lib/utils/sitemap";
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
// Cached, not per-request: this bills itself as being for AI agents, so its
|
|
11
|
+
// traffic is crawler-driven. `getSitemap` tags the read; the webhook busts it.
|
|
11
12
|
|
|
12
13
|
type PageDoc = PAGE_SITEMAP_QUERY_RESULT[number];
|
|
13
14
|
|
|
@@ -113,7 +114,12 @@ export const GET = async () => {
|
|
|
113
114
|
|
|
114
115
|
if (pages.length === 0) parts.push("No content published yet.", "");
|
|
115
116
|
|
|
116
|
-
return new NextResponse(parts.join("\n")
|
|
117
|
+
return new NextResponse(parts.join("\n"), {
|
|
118
|
+
headers: {
|
|
119
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
120
|
+
"X-Content-Type-Options": "nosniff",
|
|
121
|
+
},
|
|
122
|
+
});
|
|
117
123
|
} catch (error) {
|
|
118
124
|
console.error("Error generating sitemap.md:", error);
|
|
119
125
|
return new NextResponse("# Sitemap\n\nError fetching content.", {
|
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { NextStudio } from "next-sanity/studio";
|
|
4
|
+
import { isSanityConfigured } from "@/lib/integrations/check-integration";
|
|
4
5
|
import config from "@/lib/integrations/sanity/sanity.config";
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
// The Studio throws on an empty `projectId`, which `env.ts` falls back to when
|
|
8
|
+
// Sanity isn't configured — so this route would fail `next build`.
|
|
9
|
+
const StudioPage = () =>
|
|
10
|
+
isSanityConfigured() ? (
|
|
11
|
+
<NextStudio config={config} />
|
|
12
|
+
) : (
|
|
13
|
+
<main style={{ padding: "2rem", fontFamily: "system-ui, sans-serif" }}>
|
|
14
|
+
<h1>Studio unavailable</h1>
|
|
15
|
+
<p>
|
|
16
|
+
Set <code>NEXT_PUBLIC_SANITY_PROJECT_ID</code> and{" "}
|
|
17
|
+
<code>NEXT_PUBLIC_SANITY_DATASET</code>, then restart, to load the
|
|
18
|
+
embedded Studio.
|
|
19
|
+
</p>
|
|
20
|
+
</main>
|
|
21
|
+
);
|
|
7
22
|
|
|
8
23
|
export default StudioPage;
|
package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/visual-editing.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { draftMode } from "next/headers";
|
|
2
2
|
import { VisualEditing } from "next-sanity/visual-editing";
|
|
3
3
|
import { Suspense } from "react";
|
|
4
|
-
import { refreshAction } from "@/app/actions/refresh";
|
|
5
4
|
import { DraftModeToggle } from "@/components/sanity/draft-mode-toggle";
|
|
6
5
|
import { isSanityConfigured } from "@/lib/integrations/check-integration";
|
|
7
6
|
import { SanityLive } from "@/lib/integrations/sanity/live";
|
|
@@ -16,7 +15,8 @@ async function VisualEditingInner() {
|
|
|
16
15
|
<>
|
|
17
16
|
<DraftModeToggle />
|
|
18
17
|
<VisualEditing />
|
|
19
|
-
|
|
18
|
+
{/* No `action`: in draft mode the default is already `"refresh"`. */}
|
|
19
|
+
<SanityLive />
|
|
20
20
|
</>
|
|
21
21
|
);
|
|
22
22
|
}
|
package/src/modules/features/cms/sanity-pagebuilder/files/components/ui/sanity-image/index.tsx
CHANGED
|
@@ -10,7 +10,7 @@ interface SanityImageProps extends Omit<ImageProps, "src" | "aspectRatio"> {
|
|
|
10
10
|
};
|
|
11
11
|
alt?: string;
|
|
12
12
|
hotspot?: object;
|
|
13
|
-
crop?:
|
|
13
|
+
crop?: { left?: number; right?: number; top?: number; bottom?: number };
|
|
14
14
|
};
|
|
15
15
|
maxWidth?: number;
|
|
16
16
|
}
|
|
@@ -24,16 +24,26 @@ export function SanityImage({
|
|
|
24
24
|
}: SanityImageProps) {
|
|
25
25
|
if (!image?.asset) return null;
|
|
26
26
|
|
|
27
|
+
// getImageDimensions is uncropped; the URL builder applies the crop.
|
|
28
|
+
// Rounding order must match @sanity/image-url: origin first, then subtract.
|
|
27
29
|
const { width, height } = getImageDimensions(image.asset);
|
|
28
|
-
const
|
|
30
|
+
const { left = 0, right = 0, top = 0, bottom = 0 } = image.crop ?? {};
|
|
31
|
+
const cropLeft = Math.round(left * width);
|
|
32
|
+
const cropTop = Math.round(top * height);
|
|
33
|
+
const sourceWidth = Math.round(width - right * width - cropLeft);
|
|
34
|
+
const sourceHeight = Math.round(height - bottom * height - cropTop);
|
|
29
35
|
|
|
30
|
-
const imageProps = fill
|
|
36
|
+
const imageProps = fill
|
|
37
|
+
? { fill: true }
|
|
38
|
+
: { width: sourceWidth, height: sourceHeight };
|
|
31
39
|
|
|
32
40
|
return (
|
|
33
41
|
<Image
|
|
34
|
-
src={urlForImage(image)
|
|
42
|
+
src={urlForImage(image)
|
|
43
|
+
.width(Math.min(maxWidth, sourceWidth))
|
|
44
|
+
.url()}
|
|
35
45
|
alt={alt || image.alt || ""}
|
|
36
|
-
aspectRatio={
|
|
46
|
+
aspectRatio={sourceWidth / sourceHeight}
|
|
37
47
|
{...imageProps}
|
|
38
48
|
{...props}
|
|
39
49
|
/>
|
package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/check-integration.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { dataset, projectId } from "@/lib/integrations/sanity/env";
|
|
2
|
+
|
|
3
|
+
// Derived from env.ts, not a second read of `process.env`. Read directly these
|
|
4
|
+
// disagreed — env.ts also accepts `SANITY_STUDIO_PROJECT_ID` and defaults the
|
|
5
|
+
// dataset — so a resolvable project reported itself unconfigured.
|
|
6
|
+
export const isSanityConfigured = () => Boolean(projectId && dataset);
|
|
@@ -25,3 +25,11 @@ export const client: SanityClient | null = isSanityConfigured()
|
|
|
25
25
|
},
|
|
26
26
|
})
|
|
27
27
|
: null;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Non-CDN clone for the publish webhook, which has to see the mutation it was
|
|
31
|
+
* notified about rather than whatever the CDN still holds. Reading a stale slug
|
|
32
|
+
* there invalidates the old path and misses the new one.
|
|
33
|
+
*/
|
|
34
|
+
export const directClient: SanityClient | null =
|
|
35
|
+
client?.withConfig({ useCdn: false }) ?? null;
|
|
@@ -1,18 +1,24 @@
|
|
|
1
|
+
import { Stack } from "@sanity/ui";
|
|
1
2
|
import { type FieldProps, useFormValue } from "sanity";
|
|
3
|
+
import { SlugRedirectNotice } from "@/lib/integrations/sanity/components/slug-notices";
|
|
2
4
|
import { getPinnedPageById } from "@/lib/integrations/sanity/pinned-pages";
|
|
3
5
|
|
|
4
6
|
const DEFAULT_DESCRIPTION = "URL path without leading slash.";
|
|
5
7
|
const PINNED_DESCRIPTION =
|
|
6
8
|
"URL path without leading slash. Disabled on 📌 pinned pages.";
|
|
7
9
|
|
|
8
|
-
// Swaps the slug description
|
|
9
|
-
// (Sanity descriptions are plain strings, hence the 📌 character.)
|
|
10
|
+
// Swaps the slug description on pinned pages (plain-string field, hence 📌).
|
|
10
11
|
export function SlugField(props: FieldProps) {
|
|
11
12
|
const documentId = useFormValue(["_id"]) as string | undefined;
|
|
12
|
-
const
|
|
13
|
+
const pinned = getPinnedPageById(documentId);
|
|
13
14
|
|
|
14
|
-
return
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
return (
|
|
16
|
+
<Stack space={3}>
|
|
17
|
+
{props.renderDefault({
|
|
18
|
+
...props,
|
|
19
|
+
description: pinned ? PINNED_DESCRIPTION : DEFAULT_DESCRIPTION,
|
|
20
|
+
})}
|
|
21
|
+
<SlugRedirectNotice pinnedPath={pinned?.path} />
|
|
22
|
+
</Stack>
|
|
23
|
+
);
|
|
18
24
|
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { Box, Button, Card, Stack, Text } from "@sanity/ui";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { useClient, useFormValue } from "sanity";
|
|
4
|
+
import { useRouter } from "sanity/router";
|
|
5
|
+
import { apiVersion } from "@/lib/integrations/sanity/env";
|
|
6
|
+
import { isEmptyPageSlug } from "@/lib/integrations/sanity/page-category";
|
|
7
|
+
import { FOLDER_PATH_QUERY } from "@/lib/integrations/sanity/queries";
|
|
8
|
+
|
|
9
|
+
const REDIRECT_QUERY = `*[_type == "redirect" && source.current == $source][0]._id`;
|
|
10
|
+
|
|
11
|
+
// The slug field writes on every keystroke.
|
|
12
|
+
const LOOKUP_DEBOUNCE_MS = 400;
|
|
13
|
+
|
|
14
|
+
interface SlugRedirectNoticeProps {
|
|
15
|
+
/** Public path of a pinned page, whose URL is fixed rather than slug-derived. */
|
|
16
|
+
pinnedPath?: string | undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Warns when a published redirect claims this page's URL — the catch-all
|
|
21
|
+
* route only runs `checkRedirect` after a page lookup misses, so the page
|
|
22
|
+
* wins and the redirect silently never fires.
|
|
23
|
+
*
|
|
24
|
+
* Reads the `page` form shape; don't reuse for other slug types.
|
|
25
|
+
*/
|
|
26
|
+
export function SlugRedirectNotice({ pinnedPath }: SlugRedirectNoticeProps) {
|
|
27
|
+
const client = useClient({ apiVersion });
|
|
28
|
+
const router = useRouter();
|
|
29
|
+
const slug = useFormValue(["slug", "current"]) as string | undefined;
|
|
30
|
+
const folderId = useFormValue(["pageFolder", "_ref"]) as string | undefined;
|
|
31
|
+
const [folderPath, setFolderPath] = useState("");
|
|
32
|
+
const [redirectId, setRedirectId] = useState<string | null>(null);
|
|
33
|
+
|
|
34
|
+
// Separate effect so the folder chain isn't re-resolved on every keystroke.
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (!folderId) {
|
|
37
|
+
setFolderPath("");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let active = true;
|
|
42
|
+
const published = client.withConfig({ perspective: "published" });
|
|
43
|
+
|
|
44
|
+
published
|
|
45
|
+
// Leaf joins in JS below, so this tracks the live slug rather than the
|
|
46
|
+
// last saved draft.
|
|
47
|
+
.fetch<{ path: string | null } | null>(FOLDER_PATH_QUERY, { folderId })
|
|
48
|
+
.then((folder) => {
|
|
49
|
+
if (active) setFolderPath(folder?.path ?? "");
|
|
50
|
+
})
|
|
51
|
+
.catch(() => {
|
|
52
|
+
if (active) setFolderPath("");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return () => {
|
|
56
|
+
active = false;
|
|
57
|
+
};
|
|
58
|
+
}, [client, folderId]);
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
const leaf = isEmptyPageSlug(slug) ? "" : slug;
|
|
62
|
+
const source = pinnedPath
|
|
63
|
+
? pinnedPath.replace(/^\/+/, "")
|
|
64
|
+
: [folderPath, leaf].filter(Boolean).join("/");
|
|
65
|
+
|
|
66
|
+
if (!source) {
|
|
67
|
+
setRedirectId(null);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let active = true;
|
|
72
|
+
// Drafts never reach visitors, so an unpublished redirect isn't a conflict.
|
|
73
|
+
const published = client.withConfig({ perspective: "published" });
|
|
74
|
+
|
|
75
|
+
const timer = setTimeout(async () => {
|
|
76
|
+
try {
|
|
77
|
+
const id = await published.fetch<string | null>(REDIRECT_QUERY, {
|
|
78
|
+
source,
|
|
79
|
+
});
|
|
80
|
+
if (active) setRedirectId(id);
|
|
81
|
+
} catch {
|
|
82
|
+
if (active) setRedirectId(null);
|
|
83
|
+
}
|
|
84
|
+
}, LOOKUP_DEBOUNCE_MS);
|
|
85
|
+
|
|
86
|
+
return () => {
|
|
87
|
+
active = false;
|
|
88
|
+
clearTimeout(timer);
|
|
89
|
+
};
|
|
90
|
+
}, [client, folderPath, pinnedPath, slug]);
|
|
91
|
+
|
|
92
|
+
if (!redirectId) return null;
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<Card border padding={3} radius={2} tone="caution">
|
|
96
|
+
<Stack space={3}>
|
|
97
|
+
<Stack space={2}>
|
|
98
|
+
<Text size={1} weight="semibold">
|
|
99
|
+
A redirect already claims this URL
|
|
100
|
+
</Text>
|
|
101
|
+
<Text muted size={1}>
|
|
102
|
+
Visitors reach this page instead — a redirect only runs when no page
|
|
103
|
+
matches its path. Remove the redirect, or move this page to another
|
|
104
|
+
slug.
|
|
105
|
+
</Text>
|
|
106
|
+
</Stack>
|
|
107
|
+
<Box>
|
|
108
|
+
<Button
|
|
109
|
+
fontSize={1}
|
|
110
|
+
mode="ghost"
|
|
111
|
+
onClick={() =>
|
|
112
|
+
router.navigateIntent("edit", {
|
|
113
|
+
id: redirectId,
|
|
114
|
+
type: "redirect",
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
padding={3}
|
|
118
|
+
text="Go to redirect"
|
|
119
|
+
tone="caution"
|
|
120
|
+
/>
|
|
121
|
+
</Box>
|
|
122
|
+
</Stack>
|
|
123
|
+
</Card>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
* if Sanity is properly set up before making API calls.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
/**
|
|
9
|
+
/** Floor, not a preference: Content Releases need 2025-02-19 or later. */
|
|
10
10
|
export const apiVersion =
|
|
11
|
-
process.env.NEXT_PUBLIC_SANITY_API_VERSION || "
|
|
11
|
+
process.env.NEXT_PUBLIC_SANITY_API_VERSION || "2025-02-19";
|
|
12
12
|
|
|
13
13
|
/** Sanity dataset name - defaults to 'production' */
|
|
14
14
|
export const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET || "production";
|