bsmnt 0.10.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.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/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 +1 -0
- 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/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 +5 -2
- 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 +1 -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-pagebuilder/files/app/api/[[...slug]]/route.ts +4 -0
- 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 +2 -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/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-notices.tsx +6 -16
- 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 +3 -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/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-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 +2 -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/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 +2 -3
- package/src/templates/next-default/package.json +2 -5
- package/src/templates/next-experiments/.env.example +2 -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/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 +2 -3
- package/src/templates/next-experiments/package.json +2 -5
- package/src/templates/next-pagebuilder/.env.example +1 -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 +64 -0
- package/src/templates/next-pagebuilder/app/sitemap.xml/route.ts +3 -1
- 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 +4 -16
- 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 +21 -7
- 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 +2 -3
- package/src/templates/next-pagebuilder/package.json +4 -6
- 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 +2 -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/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 +2 -3
- package/src/templates/next-webgl/package.json +4 -7
- 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
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
import cn from "clsx"
|
|
10
10
|
import NextImage, { type ImageProps as NextImageProps } from "next/image"
|
|
11
11
|
import type { CSSProperties, Ref } from "react"
|
|
12
|
-
|
|
13
|
-
const DESKTOP_BREAKPOINT = 1440
|
|
12
|
+
import { buildImageSizes } from "@/lib/utils/image-sizes"
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
15
|
* Enhanced Image component props extending Next.js Image.
|
|
@@ -167,8 +166,7 @@ export function Image({
|
|
|
167
166
|
}: ImageProps) {
|
|
168
167
|
// Generate responsive sizes if not provided
|
|
169
168
|
const finalSizes =
|
|
170
|
-
sizes ||
|
|
171
|
-
`(max-width: ${DESKTOP_BREAKPOINT}px) ${mobileSize}, ${desktopSize}`
|
|
169
|
+
sizes || buildImageSizes({ base: mobileSize, desktop: desktopSize })
|
|
172
170
|
|
|
173
171
|
// Early return after hooks
|
|
174
172
|
if (!src) return null
|
|
@@ -5,10 +5,12 @@ Pure utility functions organized by concern.
|
|
|
5
5
|
Use explicit imports for clarity and better tree-shaking:
|
|
6
6
|
|
|
7
7
|
```tsx
|
|
8
|
-
import { clamp, lerp, mapRange } from '@/utils/math'
|
|
9
|
-
import { slugify } from '@/utils/strings'
|
|
10
|
-
import { generatePageMetadata } from '@/utils/metadata'
|
|
11
|
-
import { tovw, torem, toem } from '@/utils/viewport'
|
|
8
|
+
import { clamp, lerp, mapRange } from '@/lib/utils/math'
|
|
9
|
+
import { slugify } from '@/lib/utils/strings'
|
|
10
|
+
import { generatePageMetadata } from '@/lib/utils/metadata'
|
|
11
|
+
import { tovw, torem, toem } from '@/lib/utils/viewport'
|
|
12
|
+
import { buildImageSizes, buildGridImageSizes } from '@/lib/utils/image-sizes'
|
|
13
|
+
import { useMergeRefs } from '@/lib/utils/merge-refs'
|
|
12
14
|
```
|
|
13
15
|
|
|
14
16
|
## Modules
|
|
@@ -18,8 +20,10 @@ import { tovw, torem, toem } from '@/utils/viewport'
|
|
|
18
20
|
| `math` | `clamp`, `lerp`, `mapRange`, `modulo`, `normalize`, `distance` |
|
|
19
21
|
| `easings` | All easing functions (`easeOutCubic`, etc.) |
|
|
20
22
|
| `viewport` | `tovw`, `torem`, `toem` |
|
|
21
|
-
| `strings` | `slugify`, `
|
|
23
|
+
| `strings` | `slugify`, `capitalizeFirstLetter`, `twoDigits`, `numberWithCommas` |
|
|
22
24
|
| `metadata` | `generatePageMetadata` |
|
|
25
|
+
| `image-sizes` | `buildImageSizes`, `buildGridImageSizes` |
|
|
26
|
+
| `merge-refs` | `mergeRefs`, `useMergeRefs`, `assignRef` |
|
|
23
27
|
|
|
24
28
|
## Common Patterns
|
|
25
29
|
|
|
@@ -37,4 +41,13 @@ toem(16, 14) // → "1.14em"
|
|
|
37
41
|
|
|
38
42
|
// SEO
|
|
39
43
|
export const metadata = generatePageMetadata({ title: 'About' })
|
|
44
|
+
|
|
45
|
+
// next/image sizes — keys mirror the element's Tailwind variants
|
|
46
|
+
buildImageSizes({ base: '100vw', desktop: '796px' })
|
|
47
|
+
buildGridImageSizes({ base: 1, tablet: 2, desktop: 3 })
|
|
48
|
+
|
|
49
|
+
// Refs — in a component, use the hook: an inline mergeRefs([...]) is a new
|
|
50
|
+
// callback each render, so React detaches and reattaches every ref.
|
|
51
|
+
const mergedRef = useMergeRefs([localRef, ref])
|
|
52
|
+
return <input ref={mergedRef} />
|
|
40
53
|
```
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/** Mirrors the theme's `--breakpoint-*` scale, which clears Tailwind's defaults. */
|
|
2
|
+
export const BASEMENT_BREAKPOINTS = {
|
|
3
|
+
mobile: 640,
|
|
4
|
+
tablet: 768,
|
|
5
|
+
"tablet-lg": 1024,
|
|
6
|
+
desktop: 1440,
|
|
7
|
+
"desktop-large": 1920,
|
|
8
|
+
} as const
|
|
9
|
+
|
|
10
|
+
export type BreakpointMap = Readonly<Record<string, number>>
|
|
11
|
+
|
|
12
|
+
/** Mirrors the element's variants: `grid-cols-1 tablet:grid-cols-2` → `{ base: 1, tablet: 2 }`. */
|
|
13
|
+
export type ByBreakpoint<
|
|
14
|
+
T,
|
|
15
|
+
B extends BreakpointMap = typeof BASEMENT_BREAKPOINTS,
|
|
16
|
+
> = { base: T } & Partial<Record<Extract<keyof B, string>, T>>
|
|
17
|
+
|
|
18
|
+
function columnWidth(columns: number): string {
|
|
19
|
+
return `${Math.round(100 / columns)}vw`
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Widest first, since `sizes` takes the first matching condition and the last
|
|
24
|
+
* entry must be bare. `min-width` matches the Tailwind variant's boundary; a
|
|
25
|
+
* `max-width: <next> - 1` one would leave a fractional-pixel gap.
|
|
26
|
+
*/
|
|
27
|
+
function toSizes(
|
|
28
|
+
base: string,
|
|
29
|
+
overrides: Readonly<Record<string, string | undefined>>,
|
|
30
|
+
scale: BreakpointMap
|
|
31
|
+
): string {
|
|
32
|
+
const conditions: string[] = []
|
|
33
|
+
|
|
34
|
+
for (const [name, min] of Object.entries(scale).sort(
|
|
35
|
+
([, a], [, b]) => b - a
|
|
36
|
+
)) {
|
|
37
|
+
const value = overrides[name]
|
|
38
|
+
if (value !== undefined) {
|
|
39
|
+
conditions.push(`(min-width: ${min}px) ${value}`)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return [...conditions, base].join(", ")
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Builds `sizes` from the CSS length the image renders at per breakpoint. */
|
|
47
|
+
export function buildImageSizes<
|
|
48
|
+
B extends BreakpointMap = typeof BASEMENT_BREAKPOINTS,
|
|
49
|
+
>(widths: ByBreakpoint<string, B>, breakpoints?: B): string {
|
|
50
|
+
const { base, ...overrides } = widths
|
|
51
|
+
return toSizes(base, overrides, breakpoints ?? BASEMENT_BREAKPOINTS)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Builds `sizes` from a grid's column count per breakpoint. Rounded: `sizes`
|
|
56
|
+
* only steers which srcset candidate the browser picks, and gaps make the real
|
|
57
|
+
* column narrower than `100 / N` anyway.
|
|
58
|
+
*/
|
|
59
|
+
export function buildGridImageSizes<
|
|
60
|
+
B extends BreakpointMap = typeof BASEMENT_BREAKPOINTS,
|
|
61
|
+
>(columns: ByBreakpoint<number, B>, breakpoints?: B): string {
|
|
62
|
+
const { base, ...overrides } = columns
|
|
63
|
+
const widths: Record<string, string> = {}
|
|
64
|
+
|
|
65
|
+
for (const [name, count] of Object.entries(overrides)) {
|
|
66
|
+
if (count !== undefined) {
|
|
67
|
+
widths[name] = columnWidth(count)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return toSizes(columnWidth(base), widths, breakpoints ?? BASEMENT_BREAKPOINTS)
|
|
72
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type Ref, type RefCallback, useRef } from "react"
|
|
2
|
+
|
|
3
|
+
/** Returns the ref's own cleanup callback when it provides one (React 19). */
|
|
4
|
+
export function assignRef<T>(
|
|
5
|
+
ref: Ref<T> | undefined,
|
|
6
|
+
value: T | null
|
|
7
|
+
): ReturnType<RefCallback<T>> {
|
|
8
|
+
if (typeof ref === "function") {
|
|
9
|
+
return ref(value)
|
|
10
|
+
}
|
|
11
|
+
if (ref) {
|
|
12
|
+
ref.current = value
|
|
13
|
+
}
|
|
14
|
+
return undefined
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Merges object/callback refs into one callback ref, with React 19 ref cleanup. */
|
|
18
|
+
export function mergeRefs<T>(refs: (Ref<T> | undefined)[]): RefCallback<T> {
|
|
19
|
+
return (value) => {
|
|
20
|
+
const cleanups = refs.map((ref) => {
|
|
21
|
+
const cleanup = assignRef(ref, value)
|
|
22
|
+
return typeof cleanup === "function"
|
|
23
|
+
? cleanup
|
|
24
|
+
: () => assignRef(ref, null)
|
|
25
|
+
})
|
|
26
|
+
return () => {
|
|
27
|
+
for (const cleanup of cleanups) {
|
|
28
|
+
cleanup()
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Memoized {@link mergeRefs}, stable across renders for the same ref list.
|
|
36
|
+
* Compares the list here rather than passing it as `useMemo` deps: React only
|
|
37
|
+
* compares the common prefix, so a list that grows or shrinks would silently
|
|
38
|
+
* reuse the old callback and leave a ref unattached or stale.
|
|
39
|
+
*/
|
|
40
|
+
export function useMergeRefs<T>(refs: (Ref<T> | undefined)[]): RefCallback<T> {
|
|
41
|
+
const cache = useRef<{
|
|
42
|
+
refs: (Ref<T> | undefined)[]
|
|
43
|
+
merged: RefCallback<T>
|
|
44
|
+
} | null>(null)
|
|
45
|
+
|
|
46
|
+
if (
|
|
47
|
+
!cache.current ||
|
|
48
|
+
cache.current.refs.length !== refs.length ||
|
|
49
|
+
cache.current.refs.some((ref, i) => ref !== refs[i])
|
|
50
|
+
) {
|
|
51
|
+
// Snapshot: a caller may hand us a persistent array it mutates in place.
|
|
52
|
+
cache.current = { refs: [...refs], merged: mergeRefs(refs) }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return cache.current.merged
|
|
56
|
+
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* String & Object Utilities
|
|
3
3
|
*
|
|
4
|
-
* Helper functions for string manipulation
|
|
4
|
+
* Helper functions for string manipulation and object operations.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { Ref } from "react"
|
|
8
|
-
|
|
9
7
|
// =============================================================================
|
|
10
8
|
// STRING UTILITIES
|
|
11
9
|
// =============================================================================
|
|
@@ -196,51 +194,3 @@ export function iterableObject(
|
|
|
196
194
|
return value
|
|
197
195
|
})
|
|
198
196
|
}
|
|
199
|
-
|
|
200
|
-
// =============================================================================
|
|
201
|
-
// REF UTILITIES
|
|
202
|
-
// =============================================================================
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Merges multiple React refs into a single ref callback.
|
|
206
|
-
*
|
|
207
|
-
* Useful when you need to assign multiple refs to the same element,
|
|
208
|
-
* such as combining a local ref with a forwarded ref.
|
|
209
|
-
*
|
|
210
|
-
* @param refs - Refs to merge (functions and ref objects supported)
|
|
211
|
-
* @returns A ref callback that updates all provided refs
|
|
212
|
-
*
|
|
213
|
-
* @example
|
|
214
|
-
* ```tsx
|
|
215
|
-
* // Component that needs both local and forwarded ref
|
|
216
|
-
* function MyComponent({ ref }: { ref: React.Ref<HTMLDivElement> }) {
|
|
217
|
-
* const localRef = useRef<HTMLDivElement>(null)
|
|
218
|
-
* const mergedRef = mergeRefs(localRef, ref)
|
|
219
|
-
*
|
|
220
|
-
* return <div ref={mergedRef}>Content</div>
|
|
221
|
-
* }
|
|
222
|
-
*
|
|
223
|
-
* // Forward ref pattern
|
|
224
|
-
* const ForwardedComponent = forwardRef(MyComponent)
|
|
225
|
-
* ```
|
|
226
|
-
*/
|
|
227
|
-
export function mergeRefs<T>(...refs: (Ref<T> | undefined)[]): Ref<T> {
|
|
228
|
-
return (value) => {
|
|
229
|
-
const cleanups = refs.reduce<VoidFunction[]>((accumulator, ref) => {
|
|
230
|
-
if (typeof ref === "function") {
|
|
231
|
-
const cleanup = ref(value)
|
|
232
|
-
if (typeof cleanup === "function") {
|
|
233
|
-
accumulator.push(cleanup)
|
|
234
|
-
}
|
|
235
|
-
} else if (ref) {
|
|
236
|
-
ref.current = value
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
return accumulator
|
|
240
|
-
}, [])
|
|
241
|
-
|
|
242
|
-
return () => {
|
|
243
|
-
for (const cleanup of cleanups) cleanup()
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
@@ -5,9 +5,8 @@ const nextConfig: NextConfig = {
|
|
|
5
5
|
reactStrictMode: true,
|
|
6
6
|
reactCompiler: true,
|
|
7
7
|
typedRoutes: true,
|
|
8
|
-
// Bundle the committed encrypted env files
|
|
9
|
-
//
|
|
10
|
-
// (and is git/vercel-ignored); missing files are skipped.
|
|
8
|
+
// Bundle the committed encrypted env files so instrumentation.ts can decrypt
|
|
9
|
+
// them at runtime. `.env.keys` is never listed; missing files are skipped.
|
|
11
10
|
outputFileTracingIncludes: {
|
|
12
11
|
"/**": ["./.env", "./.env.production", "./.env.preview"],
|
|
13
12
|
},
|
|
@@ -22,9 +22,7 @@
|
|
|
22
22
|
"lint:fix": "biome check --write --unsafe --max-diagnostics=200",
|
|
23
23
|
"prepare": "lefthook install",
|
|
24
24
|
"start": "next start",
|
|
25
|
-
"typecheck": "
|
|
26
|
-
"typecheck:compare": "bun run typecheck:tsc && bun run typecheck",
|
|
27
|
-
"typecheck:tsc": "tsc --noEmit --incremental false"
|
|
25
|
+
"typecheck": "tsc --noEmit"
|
|
28
26
|
},
|
|
29
27
|
"dependencies": {
|
|
30
28
|
"@radix-ui/react-navigation-menu": "^1.2.5",
|
|
@@ -47,7 +45,6 @@
|
|
|
47
45
|
"@types/node": "^24.13.1",
|
|
48
46
|
"@types/react": "^19.2.14",
|
|
49
47
|
"@types/react-dom": "^19.2.3",
|
|
50
|
-
"@typescript/native-preview": "^7.0.0-dev.20260323.1",
|
|
51
48
|
"babel-plugin-react-compiler": "^1.0.0",
|
|
52
49
|
"cross-env": "^10.1.0",
|
|
53
50
|
"knip": "^6.16.1",
|
|
@@ -55,7 +52,7 @@
|
|
|
55
52
|
"postcss-preset-env": "^10.6.1",
|
|
56
53
|
"schema-dts": "^2.0.0",
|
|
57
54
|
"tailwindcss": "^4.2.2",
|
|
58
|
-
"typescript": "^
|
|
55
|
+
"typescript": "^7.0.2"
|
|
59
56
|
},
|
|
60
57
|
"trustedDependencies": [
|
|
61
58
|
"@biomejs/biome",
|
|
@@ -58,7 +58,6 @@
|
|
|
58
58
|
|
|
59
59
|
// General
|
|
60
60
|
"files.eol": "\n",
|
|
61
|
-
// "typescript.experimental.useTsgo": true, // Disabled: tsgo doesn't support Next.js plugin
|
|
62
61
|
"javascript.suggest.autoImports": true,
|
|
63
62
|
// Disable Prettier to prevent silent formatter conflicts
|
|
64
63
|
"prettier.enable": false,
|
|
@@ -90,7 +89,6 @@
|
|
|
90
89
|
|
|
91
90
|
// TypeScript
|
|
92
91
|
"typescript.suggest.autoImports": true,
|
|
93
|
-
"typescript.tsdk": "node_modules/typescript/lib",
|
|
94
92
|
|
|
95
93
|
// Editor labels (fixed for .tsx files)
|
|
96
94
|
"workbench.editor.customLabels.patterns": {
|
|
@@ -95,6 +95,70 @@ for *any* framing failure, including a wrong registered URL with already-correct
|
|
|
95
95
|
headers. Trust the browser console's `Framing '<url>' violates …` line instead —
|
|
96
96
|
it names the URL actually being framed.
|
|
97
97
|
|
|
98
|
+
## Content invalidation (publish webhook) — required
|
|
99
|
+
|
|
100
|
+
Cached content is cleared on publish by `app/api/revalidate/route.ts`, which only
|
|
101
|
+
runs if a Sanity webhook calls it. **Until you create that webhook, published
|
|
102
|
+
changes won't appear on the deployed site until its cache entries expire.**
|
|
103
|
+
|
|
104
|
+
`SANITY_REVALIDATE_SECRET` is generated for you during `bsmnt create` (see your
|
|
105
|
+
env file). The webhook itself is manual, because the deployed URL isn't known at
|
|
106
|
+
scaffold time. Once deployed, in **sanity.io/manage → API → Webhooks → Create**:
|
|
107
|
+
|
|
108
|
+
| Field | Value |
|
|
109
|
+
| --- | --- |
|
|
110
|
+
| URL | `https://<your-domain>/api/revalidate` |
|
|
111
|
+
| Dataset | `production` |
|
|
112
|
+
| Trigger on | Create, Update, Delete |
|
|
113
|
+
| Filter | leave empty (the route decides what to invalidate) |
|
|
114
|
+
| Projection | see below — the route needs `operation` and `before()` |
|
|
115
|
+
| HTTP method | `POST` |
|
|
116
|
+
| API version | `v2021-03-25` or later |
|
|
117
|
+
| Secret | the `SANITY_REVALIDATE_SECRET` value from your env |
|
|
118
|
+
|
|
119
|
+
The projection is not optional: the route reads `operation` and the pre-delete
|
|
120
|
+
document to invalidate a removed page, and neither is in the default payload.
|
|
121
|
+
|
|
122
|
+
```groq
|
|
123
|
+
{
|
|
124
|
+
"_id": _id,
|
|
125
|
+
"_type": _type,
|
|
126
|
+
"operation": delta::operation(),
|
|
127
|
+
"before": before(),
|
|
128
|
+
"after": after()
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Make sure the same secret is set in your hosting provider's environment, not just
|
|
133
|
+
locally. To check it end to end: publish a change in the Studio, then reload the
|
|
134
|
+
affected page — it should update without a redeploy. Close any other tab on the
|
|
135
|
+
site first: in draft mode `<SanityLive>` refreshes the router on its own, which can
|
|
136
|
+
make a broken webhook look like it works.
|
|
137
|
+
|
|
138
|
+
**sanity.io/manage → API → Webhooks → your webhook → Delivery log** is where to
|
|
139
|
+
check. A 401 means the secret doesn't match; repeated 5xx means the route is
|
|
140
|
+
erroring; no attempts at all means the filter or dataset is wrong.
|
|
141
|
+
|
|
142
|
+
### Why every route here is cached
|
|
143
|
+
|
|
144
|
+
`sanityFetch` from `next-sanity`'s `defineLive` issues **two** Sanity requests per
|
|
145
|
+
call: an uncached probe for the Live API's sync tags, then the real query with
|
|
146
|
+
`next: { revalidate: false, tags }`. Only the second is cached, so a route that
|
|
147
|
+
renders per request pays a Sanity round trip on every hit no matter what the Data
|
|
148
|
+
Cache holds.
|
|
149
|
+
|
|
150
|
+
That is why nothing in this template sets `dynamic = "force-dynamic"` — including
|
|
151
|
+
`/sitemap.xml`, `/sitemap.md` and `/feed.xml`, which are crawler-driven and used
|
|
152
|
+
to. With the route output cached, the probe runs when a route revalidates rather
|
|
153
|
+
than when someone visits it.
|
|
154
|
+
|
|
155
|
+
The exception is server actions, which by definition run per invocation:
|
|
156
|
+
`unlockGatedContent` costs two Sanity requests per form submission. If you add
|
|
157
|
+
actions that read Sanity on a hot path, read through `client.fetch` directly
|
|
158
|
+
rather than `sanityFetch` — but note that `client.fetch` does not resolve the
|
|
159
|
+
draft-mode perspective from cookies, so an editor previewing drafts would see
|
|
160
|
+
published content unless you thread that through yourself.
|
|
161
|
+
|
|
98
162
|
## Continuous Integration
|
|
99
163
|
|
|
100
164
|
A GitHub Actions workflow (`.github/workflows/ci.yml`) runs on every pull request to `main` and on pushes to `main`. It runs type checking, linting, the production build, tests, and `knip`.
|
|
@@ -3,7 +3,9 @@ import { isSanityConfigured } from "@/lib/integrations/check-integration"
|
|
|
3
3
|
import type { PAGE_SITEMAP_QUERY_RESULT } from "@/lib/integrations/sanity/sanity.types"
|
|
4
4
|
import { getPageUrl, getSitemap, getSitemapBaseUrl } from "@/lib/utils/sitemap"
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
// Not `force-dynamic`: per-request rendering meant a Sanity round trip per crawler
|
|
7
|
+
// hit. No `Cache-Control` either — a CDN copy is a second cache the webhook
|
|
8
|
+
// can't purge.
|
|
7
9
|
|
|
8
10
|
type PageDoc = PAGE_SITEMAP_QUERY_RESULT[number]
|
|
9
11
|
|
|
@@ -3,8 +3,7 @@ import { SanityImage } from "@/components/ui/sanity-image"
|
|
|
3
3
|
import {
|
|
4
4
|
type CompanyData,
|
|
5
5
|
type FooterData,
|
|
6
|
-
|
|
7
|
-
getFooterData,
|
|
6
|
+
getLayoutData,
|
|
8
7
|
} from "@/lib/integrations/sanity/fetchers/layout"
|
|
9
8
|
|
|
10
9
|
type FooterLinkGroup = NonNullable<FooterData["links"]>[number]
|
|
@@ -12,10 +11,8 @@ type FooterLinkItem = NonNullable<FooterLinkGroup["items"]>[number]
|
|
|
12
11
|
type SocialLink = NonNullable<CompanyData["socialLinks"]>[number]
|
|
13
12
|
|
|
14
13
|
export const Footer = async () => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
getCompanyData(),
|
|
18
|
-
])
|
|
14
|
+
// Shares one request-scoped fetch with the header and json-ld.
|
|
15
|
+
const { footer, companyData } = await getLayoutData()
|
|
19
16
|
|
|
20
17
|
const links = footer?.links ?? []
|
|
21
18
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getLayoutData } from "@/lib/integrations/sanity/fetchers/layout"
|
|
2
2
|
import { HeaderClient } from "./header-client"
|
|
3
3
|
|
|
4
4
|
export const Header = async () => {
|
|
5
|
-
|
|
5
|
+
// Shares one request-scoped fetch with the footer and json-ld.
|
|
6
|
+
const { navbar } = await getLayoutData()
|
|
6
7
|
|
|
7
8
|
return <HeaderClient data={navbar} />
|
|
8
9
|
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { stegaClean } from "next-sanity"
|
|
2
|
-
import {
|
|
3
|
-
getNavbarData,
|
|
4
|
-
getStructuredData,
|
|
5
|
-
} from "@/lib/integrations/sanity/fetchers/layout"
|
|
2
|
+
import { getLayoutData } from "@/lib/integrations/sanity/fetchers/layout"
|
|
6
3
|
import {
|
|
7
4
|
generateOrganizationJsonLd,
|
|
8
5
|
generateWebSiteJsonLd,
|
|
@@ -13,10 +10,8 @@ const nonEmpty = (value: unknown): string | undefined =>
|
|
|
13
10
|
typeof value === "string" && value.trim() !== "" ? value : undefined
|
|
14
11
|
|
|
15
12
|
export const JsonLd = async () => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
getNavbarData(),
|
|
19
|
-
])
|
|
13
|
+
// Shares one request-scoped fetch with the header and footer.
|
|
14
|
+
const { structuredData, navbar } = await getLayoutData()
|
|
20
15
|
|
|
21
16
|
// Deep-clean stega artifacts before any value reaches the emitted markup.
|
|
22
17
|
const data = stegaClean(structuredData)
|
|
@@ -1,29 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { usePathname, useRouter, useSearchParams } from "next/navigation"
|
|
1
|
+
import { Link } from "@/components/ui/link"
|
|
4
2
|
import { cn } from "@/lib/styles/cn"
|
|
5
3
|
import type { ContentCategory } from "./types"
|
|
6
4
|
|
|
7
5
|
type ContentFiltersProps = {
|
|
8
6
|
categories: ContentCategory[]
|
|
9
7
|
activeCategory: string
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
count: number
|
|
9
|
+
/** Category slug → the page pinned to it. Categories without one are omitted. */
|
|
10
|
+
categoryHrefs: Record<string, string>
|
|
11
|
+
/** Where "All content" goes. */
|
|
12
|
+
indexHref: string
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
function FilterChip({
|
|
15
16
|
active,
|
|
16
17
|
label,
|
|
17
|
-
|
|
18
|
+
href,
|
|
18
19
|
}: {
|
|
19
20
|
active: boolean
|
|
20
21
|
label: string
|
|
21
|
-
|
|
22
|
+
href: string
|
|
22
23
|
}) {
|
|
23
24
|
return (
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
<Link
|
|
26
|
+
href={href}
|
|
27
|
+
aria-current={active ? "page" : undefined}
|
|
27
28
|
className={cn(
|
|
28
29
|
"rounded-full border px-4 py-2 text-sm transition-colors",
|
|
29
30
|
active
|
|
@@ -32,61 +33,43 @@ function FilterChip({
|
|
|
32
33
|
)}
|
|
33
34
|
>
|
|
34
35
|
{label}
|
|
35
|
-
</
|
|
36
|
+
</Link>
|
|
36
37
|
)
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export function ContentFilters({
|
|
40
41
|
categories,
|
|
41
42
|
activeCategory,
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
count,
|
|
44
|
+
categoryHrefs,
|
|
45
|
+
indexHref,
|
|
44
46
|
}: ContentFiltersProps) {
|
|
45
|
-
const pathname = usePathname()
|
|
46
|
-
const router = useRouter()
|
|
47
|
-
const searchParams = useSearchParams()
|
|
48
|
-
|
|
49
|
-
const navigate = (category: string) => {
|
|
50
|
-
const nextParams = new URLSearchParams(searchParams.toString())
|
|
51
|
-
|
|
52
|
-
if (category) nextParams.set("category", category)
|
|
53
|
-
else nextParams.delete("category")
|
|
54
|
-
|
|
55
|
-
nextParams.delete("page")
|
|
56
|
-
|
|
57
|
-
const href = nextParams.toString()
|
|
58
|
-
? `${pathname}?${nextParams.toString()}`
|
|
59
|
-
: pathname
|
|
60
|
-
|
|
61
|
-
// biome-ignore lint/suspicious/noExplicitAny: typed routes do not accept dynamic query string helpers cleanly
|
|
62
|
-
router.push(href as any, { scroll: false })
|
|
63
|
-
}
|
|
64
|
-
|
|
65
47
|
return (
|
|
66
48
|
<div className="flex flex-col gap-4 rounded-[1.5rem] border border-black/10 bg-black/[0.02] p-4 sm:p-5">
|
|
67
|
-
<
|
|
49
|
+
<nav aria-label="Filter by category" className="flex flex-wrap gap-2">
|
|
68
50
|
<FilterChip
|
|
69
51
|
active={activeCategory === ""}
|
|
70
52
|
label="All content"
|
|
71
|
-
|
|
53
|
+
href={indexHref}
|
|
72
54
|
/>
|
|
73
55
|
{categories.map((category) => {
|
|
74
56
|
const slug = category.slug?.current
|
|
75
|
-
|
|
57
|
+
const href = slug ? categoryHrefs[slug] : undefined
|
|
58
|
+
// A category with no page of its own has nowhere to link to.
|
|
59
|
+
if (!slug || !href) return null
|
|
76
60
|
|
|
77
61
|
return (
|
|
78
62
|
<FilterChip
|
|
79
63
|
key={category._id}
|
|
80
64
|
active={activeCategory === slug}
|
|
81
65
|
label={category.title || "Untitled"}
|
|
82
|
-
|
|
66
|
+
href={href}
|
|
83
67
|
/>
|
|
84
68
|
)
|
|
85
69
|
})}
|
|
86
|
-
</
|
|
70
|
+
</nav>
|
|
87
71
|
<p className="text-current/60 text-sm">
|
|
88
|
-
Showing {
|
|
89
|
-
{filteredCount !== totalCount ? ` out of ${totalCount}` : ""}
|
|
72
|
+
Showing {count} {count === 1 ? "item" : "items"}
|
|
90
73
|
</p>
|
|
91
74
|
</div>
|
|
92
75
|
)
|