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.
Files changed (321) hide show
  1. package/dist/application/add-integration/index.js.map +1 -1
  2. package/dist/application/shared/node-version.js.map +1 -1
  3. package/dist/core/create/internal-ids.d.ts +3 -0
  4. package/dist/core/create/internal-ids.d.ts.map +1 -1
  5. package/dist/core/create/internal-ids.js +4 -0
  6. package/dist/core/create/internal-ids.js.map +1 -1
  7. package/dist/domain/agents.d.ts.map +1 -1
  8. package/dist/domain/animations.d.ts.map +1 -1
  9. package/dist/domain/cms.d.ts.map +1 -1
  10. package/dist/domain/forms.d.ts.map +1 -1
  11. package/dist/domain/hooks.d.ts.map +1 -1
  12. package/dist/domain/skills.d.ts.map +1 -1
  13. package/dist/domain/templates.d.ts +10 -0
  14. package/dist/domain/templates.d.ts.map +1 -1
  15. package/dist/domain/templates.js +5 -0
  16. package/dist/domain/templates.js.map +1 -1
  17. package/dist/index.js +18 -5
  18. package/dist/index.js.map +1 -1
  19. package/dist/infrastructure/agents/register-sanity-mcp.js +3 -8
  20. package/dist/infrastructure/agents/register-sanity-mcp.js.map +1 -1
  21. package/dist/infrastructure/agents/register-storybook-mcp.d.ts +18 -0
  22. package/dist/infrastructure/agents/register-storybook-mcp.d.ts.map +1 -0
  23. package/dist/infrastructure/agents/register-storybook-mcp.js +157 -0
  24. package/dist/infrastructure/agents/register-storybook-mcp.js.map +1 -0
  25. package/dist/infrastructure/create/lifecycle-registry.d.ts.map +1 -1
  26. package/dist/infrastructure/create/lifecycle-registry.js +39 -1
  27. package/dist/infrastructure/create/lifecycle-registry.js.map +1 -1
  28. package/dist/infrastructure/create/merge-registry.d.ts.map +1 -1
  29. package/dist/infrastructure/create/merge-registry.js +4 -0
  30. package/dist/infrastructure/create/merge-registry.js.map +1 -1
  31. package/dist/infrastructure/create/sanity-auth.js.map +1 -1
  32. package/dist/infrastructure/create/setup-agent.d.ts +6 -1
  33. package/dist/infrastructure/create/setup-agent.d.ts.map +1 -1
  34. package/dist/infrastructure/create/setup-agent.js +23 -21
  35. package/dist/infrastructure/create/setup-agent.js.map +1 -1
  36. package/dist/infrastructure/create/setup-dotenvx.js.map +1 -1
  37. package/dist/infrastructure/create/setup-remote.js.map +1 -1
  38. package/dist/infrastructure/create/setup-sanity.d.ts.map +1 -1
  39. package/dist/infrastructure/create/setup-sanity.js +12 -1
  40. package/dist/infrastructure/create/setup-sanity.js.map +1 -1
  41. package/dist/infrastructure/integrations/inject-integration.d.ts.map +1 -1
  42. package/dist/infrastructure/integrations/inject-integration.js +2 -0
  43. package/dist/infrastructure/integrations/inject-integration.js.map +1 -1
  44. package/dist/infrastructure/update/notifier.js.map +1 -1
  45. package/dist/modules/features/cms/index.d.ts.map +1 -1
  46. package/dist/modules/features/cms/index.js +2 -0
  47. package/dist/modules/features/cms/index.js.map +1 -1
  48. package/dist/modules/features/cms/sanity/config.d.ts.map +1 -1
  49. package/dist/modules/features/cms/sanity/config.js +1 -0
  50. package/dist/modules/features/cms/sanity/config.js.map +1 -1
  51. package/dist/modules/features/cms/sanity/index.d.ts.map +1 -1
  52. package/dist/modules/features/cms/sanity/index.js +10 -1
  53. package/dist/modules/features/cms/sanity/index.js.map +1 -1
  54. package/dist/modules/features/cms/sanity/mergers/check-integration.d.ts.map +1 -1
  55. package/dist/modules/features/cms/sanity/mergers/check-integration.js +15 -6
  56. package/dist/modules/features/cms/sanity/mergers/check-integration.js.map +1 -1
  57. package/dist/modules/features/cms/sanity/mergers/knip.d.ts.map +1 -1
  58. package/dist/modules/features/cms/sanity/mergers/knip.js +1 -0
  59. package/dist/modules/features/cms/sanity/mergers/knip.js.map +1 -1
  60. package/dist/modules/features/cms/sanity/mergers/layout.js.map +1 -1
  61. package/dist/modules/features/cms/sanity/mergers/package-json.d.ts +12 -0
  62. package/dist/modules/features/cms/sanity/mergers/package-json.d.ts.map +1 -0
  63. package/dist/modules/features/cms/sanity/mergers/package-json.js +19 -0
  64. package/dist/modules/features/cms/sanity/mergers/package-json.js.map +1 -0
  65. package/dist/modules/features/cms/sanity-pagebuilder/index.d.ts.map +1 -1
  66. package/dist/modules/features/cms/sanity-pagebuilder/index.js +7 -1
  67. package/dist/modules/features/cms/sanity-pagebuilder/index.js.map +1 -1
  68. package/dist/modules/features/cms/sanity-pagebuilder-cache/config.d.ts +14 -0
  69. package/dist/modules/features/cms/sanity-pagebuilder-cache/config.d.ts.map +1 -0
  70. package/dist/modules/features/cms/sanity-pagebuilder-cache/config.js +50 -0
  71. package/dist/modules/features/cms/sanity-pagebuilder-cache/config.js.map +1 -0
  72. package/dist/modules/features/cms/sanity-pagebuilder-cache/index.d.ts +3 -0
  73. package/dist/modules/features/cms/sanity-pagebuilder-cache/index.d.ts.map +1 -0
  74. package/dist/modules/features/cms/sanity-pagebuilder-cache/index.js +55 -0
  75. package/dist/modules/features/cms/sanity-pagebuilder-cache/index.js.map +1 -0
  76. package/dist/modules/presets/index.d.ts.map +1 -1
  77. package/dist/modules/presets/index.js +2 -0
  78. package/dist/modules/presets/index.js.map +1 -1
  79. package/dist/modules/presets/next-pagebuilder-cache.d.ts +3 -0
  80. package/dist/modules/presets/next-pagebuilder-cache.d.ts.map +1 -0
  81. package/dist/modules/presets/next-pagebuilder-cache.js +10 -0
  82. package/dist/modules/presets/next-pagebuilder-cache.js.map +1 -0
  83. package/dist/paths.d.ts +6 -0
  84. package/dist/paths.d.ts.map +1 -1
  85. package/dist/paths.js +6 -0
  86. package/dist/paths.js.map +1 -1
  87. package/package.json +5 -2
  88. package/src/modules/features/cms/sanity/files/lib/integrations/check-integration.ts +7 -6
  89. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/README.md +1 -1
  90. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/components/rich-text.tsx +1 -0
  91. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/env.ts +2 -2
  92. package/src/modules/features/cms/sanity-pagebuilder/files/app/api/[[...slug]]/route.ts +4 -0
  93. package/src/modules/features/cms/sanity-pagebuilder/files/app/api/revalidate/route.ts +172 -24
  94. package/src/modules/features/cms/sanity-pagebuilder/files/app/feed.xml/route.ts +14 -7
  95. package/src/modules/features/cms/sanity-pagebuilder/files/app/sitemap.md/route.ts +2 -2
  96. package/src/modules/features/cms/sanity-pagebuilder/files/app/studio/[[...tool]]/page.tsx +16 -1
  97. package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/rich-text.tsx +1 -0
  98. package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/visual-editing.tsx +2 -2
  99. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/check-integration.ts +6 -5
  100. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/client.ts +8 -0
  101. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/slug-notices.tsx +6 -16
  102. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/env.ts +2 -2
  103. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/fetchers/layout.ts +22 -52
  104. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/icons.ts +2 -2
  105. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/live/index.tsx +3 -1
  106. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-builder-config.ts +7 -0
  107. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-tree-pane.tsx +70 -57
  108. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/queries.ts +119 -29
  109. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/revalidate-paths.ts +70 -0
  110. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/index.ts +1 -0
  111. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/rich-text-block.ts +39 -0
  112. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/index.ts +2 -0
  113. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/page.ts +12 -0
  114. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/tags.ts +56 -0
  115. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/utils/page-builder-markdown.ts +12 -0
  116. package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/sitemap.ts +11 -8
  117. package/src/modules/features/cms/sanity-pagebuilder-cache/files/.knip.json +8 -0
  118. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/api/[[...slug]]/route.ts +112 -0
  119. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/api/draft-mode/disable/route.ts +7 -0
  120. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/api/draft-mode/enable/route.ts +20 -0
  121. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/api/revalidate/route.ts +292 -0
  122. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/feed.xml/route.ts +66 -0
  123. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/sitemap.md/route.ts +132 -0
  124. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/studio/[[...tool]]/layout.tsx +26 -0
  125. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/studio/[[...tool]]/page.tsx +23 -0
  126. package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/sanity/draft-mode-toggle.tsx +27 -0
  127. package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/sanity/rich-text.tsx +88 -0
  128. package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/sanity/visual-editing.tsx +11 -0
  129. package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/ui/sanity-image/index.tsx +51 -0
  130. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/check-integration.ts +6 -0
  131. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/client.ts +37 -0
  132. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/block-actions-item.tsx +81 -0
  133. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/disable-draft-mode.tsx +23 -0
  134. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/page-builder-input.tsx +39 -0
  135. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/page-category-input.tsx +50 -0
  136. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/same-as-input.tsx +66 -0
  137. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/slug-field.tsx +24 -0
  138. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/slug-input.tsx +14 -0
  139. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/slug-notices.tsx +125 -0
  140. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/confirm-publish-action.ts +40 -0
  141. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/env.ts +34 -0
  142. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/fetchers/layout.ts +39 -0
  143. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/fetchers/redirects.ts +22 -0
  144. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/icons.ts +81 -0
  145. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/live/index.tsx +140 -0
  146. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/markdown-proxy.config.ts +50 -0
  147. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/page-builder-config.ts +144 -0
  148. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/page-category.ts +28 -0
  149. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/page-tree-pane.tsx +431 -0
  150. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/pinned-pages.ts +54 -0
  151. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/presentation.ts +119 -0
  152. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/queries.ts +548 -0
  153. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/revalidate-paths.ts +70 -0
  154. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/sanity.cli.ts +29 -0
  155. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/sanity.config.ts +116 -0
  156. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/index.ts +6 -0
  157. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/blog-content.ts +89 -0
  158. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/description.ts +29 -0
  159. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/gated-content.ts +178 -0
  160. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/hero.ts +28 -0
  161. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/rich-text-block.ts +39 -0
  162. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/singleton/content-collection.ts +45 -0
  163. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/content/author.ts +70 -0
  164. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/content/blog-category.ts +55 -0
  165. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/index.ts +134 -0
  166. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/company-data.ts +70 -0
  167. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/footer.ts +79 -0
  168. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/navbar.ts +75 -0
  169. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/redirect.ts +152 -0
  170. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/structured-data.ts +124 -0
  171. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/link.ts +125 -0
  172. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/logo-field.ts +9 -0
  173. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/metadata.ts +132 -0
  174. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/nav-objects.ts +192 -0
  175. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/page-builder.ts +43 -0
  176. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/page-folder.ts +124 -0
  177. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/page.ts +265 -0
  178. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/richText.ts +63 -0
  179. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/structured-data-blocks.ts +436 -0
  180. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/singletons.ts +51 -0
  181. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/structure.ts +334 -0
  182. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/tags.ts +89 -0
  183. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/utils/image.ts +8 -0
  184. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/utils/link.ts +140 -0
  185. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/utils/page-builder-markdown.ts +113 -0
  186. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/utils/page-json-ld.ts +208 -0
  187. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/scripts/sanity-typegen.ts +36 -0
  188. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/check-redirect.ts +30 -0
  189. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/json-ld.tsx +522 -0
  190. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/metadata.ts +189 -0
  191. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/rss.ts +96 -0
  192. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/sitemap.ts +58 -0
  193. package/src/modules/features/cms/sanity-pagebuilder-cache/files/proxy.ts +90 -0
  194. package/src/modules/features/env/dotenvx/files/vercelignore +5 -0
  195. package/src/templates/next-default/.env.example +2 -2
  196. package/src/templates/next-default/.github/workflows/ci.yml +1 -1
  197. package/src/templates/next-default/.vscode/settings.json +0 -2
  198. package/src/templates/next-default/components/ui/image/index.tsx +2 -4
  199. package/src/templates/next-default/knip.json +1 -1
  200. package/src/templates/next-default/lefthook.yml +1 -1
  201. package/src/templates/next-default/lib/utils/README.md +18 -5
  202. package/src/templates/next-default/lib/utils/image-sizes.ts +72 -0
  203. package/src/templates/next-default/lib/utils/merge-refs.ts +56 -0
  204. package/src/templates/next-default/lib/utils/strings.ts +1 -51
  205. package/src/templates/next-default/next.config.ts +2 -3
  206. package/src/templates/next-default/package.json +2 -5
  207. package/src/templates/next-experiments/.env.example +2 -2
  208. package/src/templates/next-experiments/.github/workflows/ci.yml +1 -1
  209. package/src/templates/next-experiments/.vscode/settings.json +0 -2
  210. package/src/templates/next-experiments/app/page.tsx +1 -1
  211. package/src/templates/next-experiments/components/ui/image/index.tsx +2 -4
  212. package/src/templates/next-experiments/knip.json +1 -1
  213. package/src/templates/next-experiments/lefthook.yml +1 -1
  214. package/src/templates/next-experiments/lib/utils/README.md +18 -5
  215. package/src/templates/next-experiments/lib/utils/image-sizes.ts +72 -0
  216. package/src/templates/next-experiments/lib/utils/merge-refs.ts +56 -0
  217. package/src/templates/next-experiments/lib/utils/strings.ts +1 -51
  218. package/src/templates/next-experiments/next.config.ts +2 -3
  219. package/src/templates/next-experiments/package.json +2 -5
  220. package/src/templates/next-pagebuilder/.env.example +1 -0
  221. package/src/templates/next-pagebuilder/.github/workflows/ci.yml +1 -1
  222. package/src/templates/next-pagebuilder/.vscode/settings.json +0 -2
  223. package/src/templates/next-pagebuilder/README.md +64 -0
  224. package/src/templates/next-pagebuilder/app/sitemap.xml/route.ts +3 -1
  225. package/src/templates/next-pagebuilder/biome.json +2 -1
  226. package/src/templates/next-pagebuilder/components/layout/footer/index.tsx +3 -6
  227. package/src/templates/next-pagebuilder/components/layout/header/index.tsx +3 -2
  228. package/src/templates/next-pagebuilder/components/layout/json-ld/index.tsx +3 -8
  229. package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/content-filters.tsx +23 -40
  230. package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/index.tsx +88 -58
  231. package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/skeleton.tsx +4 -16
  232. package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/types.ts +14 -1
  233. package/src/templates/next-pagebuilder/components/page-builder/components/rich-text-block.tsx +16 -0
  234. package/src/templates/next-pagebuilder/components/page-builder/renderer.tsx +21 -7
  235. package/src/templates/next-pagebuilder/components/page-document/index.tsx +1 -0
  236. package/src/templates/next-pagebuilder/components/ui/image/index.tsx +2 -1
  237. package/src/templates/next-pagebuilder/knip.json +3 -2
  238. package/src/templates/next-pagebuilder/lefthook.yml +1 -1
  239. package/src/templates/next-pagebuilder/lib/integrations/sanity/sanity.types.ts +1221 -977
  240. package/src/templates/next-pagebuilder/lib/utils/image-sizes.ts +72 -0
  241. package/src/templates/next-pagebuilder/lib/utils/merge-refs.ts +56 -0
  242. package/src/templates/next-pagebuilder/next.config.ts +2 -3
  243. package/src/templates/next-pagebuilder/package.json +4 -6
  244. package/src/templates/next-pagebuilder-cache/.biome/plugins/README.md +21 -0
  245. package/src/templates/next-pagebuilder-cache/.biome/plugins/no-anchor-element.grit +12 -0
  246. package/src/templates/next-pagebuilder-cache/.biome/plugins/no-relative-parent-imports.grit +10 -0
  247. package/src/templates/next-pagebuilder-cache/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
  248. package/src/templates/next-pagebuilder-cache/.env.example +40 -0
  249. package/src/templates/next-pagebuilder-cache/.github/PULL_REQUEST_TEMPLATE.md +13 -0
  250. package/src/templates/next-pagebuilder-cache/.github/workflows/ci.yml +105 -0
  251. package/src/templates/next-pagebuilder-cache/.vscode/extensions.json +19 -0
  252. package/src/templates/next-pagebuilder-cache/.vscode/settings.json +103 -0
  253. package/src/templates/next-pagebuilder-cache/.zed/settings.json +47 -0
  254. package/src/templates/next-pagebuilder-cache/README.md +212 -0
  255. package/src/templates/next-pagebuilder-cache/_gitignore +65 -0
  256. package/src/templates/next-pagebuilder-cache/app/(content)/[[...slug]]/page.tsx +124 -0
  257. package/src/templates/next-pagebuilder-cache/app/(content)/layout.tsx +28 -0
  258. package/src/templates/next-pagebuilder-cache/app/actions/unlock-gated-content.ts +53 -0
  259. package/src/templates/next-pagebuilder-cache/app/favicon.ico +0 -0
  260. package/src/templates/next-pagebuilder-cache/app/layout.tsx +83 -0
  261. package/src/templates/next-pagebuilder-cache/app/robots.ts +13 -0
  262. package/src/templates/next-pagebuilder-cache/app/sitemap.xml/route.ts +84 -0
  263. package/src/templates/next-pagebuilder-cache/biome.json +257 -0
  264. package/src/templates/next-pagebuilder-cache/components/layout/footer/index.tsx +127 -0
  265. package/src/templates/next-pagebuilder-cache/components/layout/header/components/cta-button.tsx +28 -0
  266. package/src/templates/next-pagebuilder-cache/components/layout/header/components/mega-menu-panel.tsx +90 -0
  267. package/src/templates/next-pagebuilder-cache/components/layout/header/components/nav-item-renderer.tsx +98 -0
  268. package/src/templates/next-pagebuilder-cache/components/layout/header/components/nav-leaf-item.tsx +33 -0
  269. package/src/templates/next-pagebuilder-cache/components/layout/header/components/types.ts +7 -0
  270. package/src/templates/next-pagebuilder-cache/components/layout/header/header-client.tsx +110 -0
  271. package/src/templates/next-pagebuilder-cache/components/layout/header/index.tsx +32 -0
  272. package/src/templates/next-pagebuilder-cache/components/layout/json-ld/index.tsx +79 -0
  273. package/src/templates/next-pagebuilder-cache/components/layout/wrapper/index.tsx +33 -0
  274. package/src/templates/next-pagebuilder-cache/components/page-builder/components/article-content/index.tsx +78 -0
  275. package/src/templates/next-pagebuilder-cache/components/page-builder/components/article-content/related-post-item.tsx +27 -0
  276. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/content-card.tsx +66 -0
  277. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/content-filters.tsx +76 -0
  278. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/content-grid.tsx +40 -0
  279. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/content-pagination-nav.tsx +71 -0
  280. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/index.tsx +306 -0
  281. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/skeleton.tsx +41 -0
  282. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/types.ts +32 -0
  283. package/src/templates/next-pagebuilder-cache/components/page-builder/components/description.tsx +17 -0
  284. package/src/templates/next-pagebuilder-cache/components/page-builder/components/gated-content/index.tsx +169 -0
  285. package/src/templates/next-pagebuilder-cache/components/page-builder/components/hero.tsx +17 -0
  286. package/src/templates/next-pagebuilder-cache/components/page-builder/components/rich-text-block.tsx +16 -0
  287. package/src/templates/next-pagebuilder-cache/components/page-builder/renderer.tsx +71 -0
  288. package/src/templates/next-pagebuilder-cache/components/page-builder/types.ts +25 -0
  289. package/src/templates/next-pagebuilder-cache/components/page-document/index.tsx +147 -0
  290. package/src/templates/next-pagebuilder-cache/components/ui/image/index.tsx +216 -0
  291. package/src/templates/next-pagebuilder-cache/components/ui/link/index.tsx +152 -0
  292. package/src/templates/next-pagebuilder-cache/components.json +24 -0
  293. package/src/templates/next-pagebuilder-cache/knip.json +35 -0
  294. package/src/templates/next-pagebuilder-cache/lefthook.yml +25 -0
  295. package/src/templates/next-pagebuilder-cache/lib/integrations/sanity/sanity.types.ts +1588 -0
  296. package/src/templates/next-pagebuilder-cache/lib/scripts/setup-ci-secrets.ts +117 -0
  297. package/src/templates/next-pagebuilder-cache/lib/styles/cn.ts +5 -0
  298. package/src/templates/next-pagebuilder-cache/lib/styles/global.css +69 -0
  299. package/src/templates/next-pagebuilder-cache/lib/utils/base-url.ts +17 -0
  300. package/src/templates/next-pagebuilder-cache/lib/utils/format-date.ts +8 -0
  301. package/src/templates/next-pagebuilder-cache/lib/utils/slug-tag.ts +6 -0
  302. package/src/templates/next-pagebuilder-cache/next.config.ts +176 -0
  303. package/src/templates/next-pagebuilder-cache/package.json +62 -0
  304. package/src/templates/next-pagebuilder-cache/postcss.config.mjs +65 -0
  305. package/src/templates/next-pagebuilder-cache/svg.d.ts +6 -0
  306. package/src/templates/next-pagebuilder-cache/tsconfig.json +38 -0
  307. package/src/templates/next-webgl/.env.example +2 -2
  308. package/src/templates/next-webgl/.github/workflows/ci.yml +1 -1
  309. package/src/templates/next-webgl/.vscode/settings.json +0 -2
  310. package/src/templates/next-webgl/components/ui/image/index.tsx +2 -4
  311. package/src/templates/next-webgl/knip.json +1 -1
  312. package/src/templates/next-webgl/lefthook.yml +1 -1
  313. package/src/templates/next-webgl/lib/utils/README.md +18 -5
  314. package/src/templates/next-webgl/lib/utils/image-sizes.ts +72 -0
  315. package/src/templates/next-webgl/lib/utils/merge-refs.ts +56 -0
  316. package/src/templates/next-webgl/lib/utils/strings.ts +1 -51
  317. package/src/templates/next-webgl/next.config.ts +2 -3
  318. package/src/templates/next-webgl/package.json +4 -7
  319. package/src/templates/next-experiments/tsconfig.tsbuildinfo +0 -1
  320. package/src/templates/next-pagebuilder/app/actions/refresh.ts +0 -5
  321. package/src/templates/next-webgl/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,119 @@
1
+ import type { Observable } from "rxjs";
2
+ import { map } from "rxjs/operators";
3
+ import type { DocumentStore } from "sanity";
4
+ import {
5
+ type DocumentLocationsState,
6
+ defineDocuments,
7
+ presentationTool,
8
+ } from "sanity/presentation";
9
+ import { previewURL } from "./env";
10
+ import { getPinnedPageById, HOMEPAGE_PINNED_PAGE } from "./pinned-pages";
11
+
12
+ const MAX_FOLDER_DEPTH = 6;
13
+
14
+ const buildNestedSlugExpression = (
15
+ parentReferenceField: string,
16
+ leafSlugField: string,
17
+ ) => {
18
+ const cases = Array.from({ length: MAX_FOLDER_DEPTH }, (_, index) => {
19
+ const depth = MAX_FOLDER_DEPTH - index;
20
+ const parentSlugRefs = Array.from({ length: depth }, (_, parentIndex) => {
21
+ const remainingDepth = depth - parentIndex - 1;
22
+ return `${parentReferenceField}${"->parentFolder".repeat(remainingDepth)}->slug.current`;
23
+ });
24
+
25
+ const pathDefinedChecks = parentSlugRefs
26
+ .map((field) => `defined(${field})`)
27
+ .join(" && ");
28
+
29
+ const childPath = [...parentSlugRefs, leafSlugField].join(' + "/" + ');
30
+ const folderRootPath = parentSlugRefs.join(' + "/" + ');
31
+
32
+ return [
33
+ `${pathDefinedChecks} && ${leafSlugField} == "/" => ${folderRootPath}`,
34
+ `${pathDefinedChecks} && defined(${leafSlugField}) => ${childPath}`,
35
+ ];
36
+ }).flat();
37
+
38
+ return `select(${cases.join(",\n ")},${leafSlugField})`;
39
+ };
40
+
41
+ const pageResolvedSlugExpression = buildNestedSlugExpression(
42
+ "pageFolder",
43
+ "slug.current",
44
+ );
45
+
46
+ const CONTENT_TYPE_PREFIXES: Record<string, string> = {
47
+ blogContent: "blog",
48
+ };
49
+
50
+ const resolveSlugLocation = <T extends { title?: string }>(
51
+ documentStore: DocumentStore,
52
+ id: string,
53
+ projection: string,
54
+ toHref: (doc: T) => string | null,
55
+ fallbackTitle: string,
56
+ ): Observable<DocumentLocationsState> =>
57
+ documentStore
58
+ .listenQuery(`*[_id == $id][0]{ ${projection} }`, { id }, {})
59
+ .pipe(
60
+ map((doc: T | null) => {
61
+ const href = doc ? toHref(doc) : null;
62
+ if (!href) return { locations: [] };
63
+ return { locations: [{ title: doc?.title || fallbackTitle, href }] };
64
+ }),
65
+ );
66
+
67
+ export const presentation = presentationTool({
68
+ name: "preview",
69
+ title: "Preview",
70
+ resolve: {
71
+ mainDocuments: defineDocuments([
72
+ {
73
+ route: "/",
74
+ filter: `_type == "page" && _id == "${HOMEPAGE_PINNED_PAGE.id}"`,
75
+ },
76
+ ...Object.entries(CONTENT_TYPE_PREFIXES).map(([type, prefix]) => ({
77
+ route: `/${prefix}/:slug`,
78
+ filter: `_type == "${type}" && slug.current == $slug`,
79
+ })),
80
+ {
81
+ route: "/:slug+",
82
+ filter: `_type == "page" && ${pageResolvedSlugExpression} == $slug`,
83
+ },
84
+ ]),
85
+ locations: (params, { documentStore }) => {
86
+ if (params.type === "page") {
87
+ const pinned = getPinnedPageById(params.id);
88
+ if (pinned) {
89
+ return { locations: [{ title: pinned.title, href: pinned.path }] };
90
+ }
91
+ return resolveSlugLocation<{ title?: string; resolvedSlug?: string }>(
92
+ documentStore,
93
+ params.id,
94
+ `title, "resolvedSlug": ${pageResolvedSlugExpression}`,
95
+ (doc) => (doc.resolvedSlug ? `/${doc.resolvedSlug}` : null),
96
+ "Page",
97
+ );
98
+ }
99
+
100
+ const prefix = CONTENT_TYPE_PREFIXES[params.type];
101
+ if (!prefix) return null;
102
+
103
+ return resolveSlugLocation<{ title?: string; slug?: string }>(
104
+ documentStore,
105
+ params.id,
106
+ `title, "slug": slug.current`,
107
+ (doc) => (doc.slug ? `/${prefix}/${doc.slug}` : null),
108
+ "Untitled",
109
+ );
110
+ },
111
+ },
112
+ previewUrl: {
113
+ origin: previewURL,
114
+ draftMode: {
115
+ enable: "/api/draft-mode/enable",
116
+ disable: "/api/draft-mode/disable",
117
+ },
118
+ },
119
+ });
@@ -0,0 +1,548 @@
1
+ import { defineQuery } from "next-sanity";
2
+
3
+ const pageResolvedSlugExpression = `
4
+ select(
5
+ defined(pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->slug.current) && defined(pageFolder->slug.current) && (!defined(slug.current) || slug.current == "" || slug.current == "/") => pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->slug.current + "/" + pageFolder->slug.current,
6
+ defined(pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->slug.current) && defined(pageFolder->slug.current) && (!defined(slug.current) || slug.current == "" || slug.current == "/") => pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->slug.current + "/" + pageFolder->slug.current,
7
+ defined(pageFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->slug.current) && defined(pageFolder->slug.current) && (!defined(slug.current) || slug.current == "" || slug.current == "/") => pageFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->slug.current + "/" + pageFolder->slug.current,
8
+ defined(pageFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->slug.current) && defined(pageFolder->slug.current) && (!defined(slug.current) || slug.current == "" || slug.current == "/") => pageFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->slug.current + "/" + pageFolder->slug.current,
9
+ defined(pageFolder->parentFolder->slug.current) && defined(pageFolder->slug.current) && (!defined(slug.current) || slug.current == "" || slug.current == "/") => pageFolder->parentFolder->slug.current + "/" + pageFolder->slug.current,
10
+ defined(pageFolder->slug.current) && (!defined(slug.current) || slug.current == "" || slug.current == "/") => pageFolder->slug.current,
11
+ defined(pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->slug.current) && defined(pageFolder->slug.current) && defined(slug.current) && slug.current != "" && slug.current != "/" => pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->slug.current + "/" + pageFolder->slug.current + "/" + slug.current,
12
+ defined(pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->slug.current) && defined(pageFolder->slug.current) && defined(slug.current) && slug.current != "" && slug.current != "/" => pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->slug.current + "/" + pageFolder->slug.current + "/" + slug.current,
13
+ defined(pageFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->slug.current) && defined(pageFolder->slug.current) && defined(slug.current) && slug.current != "" && slug.current != "/" => pageFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->slug.current + "/" + pageFolder->slug.current + "/" + slug.current,
14
+ defined(pageFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->slug.current) && defined(pageFolder->slug.current) && defined(slug.current) && slug.current != "" && slug.current != "/" => pageFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->slug.current + "/" + pageFolder->slug.current + "/" + slug.current,
15
+ defined(pageFolder->parentFolder->slug.current) && defined(pageFolder->slug.current) && defined(slug.current) && slug.current != "" && slug.current != "/" => pageFolder->parentFolder->slug.current + "/" + pageFolder->slug.current + "/" + slug.current,
16
+ defined(pageFolder->slug.current) && defined(slug.current) && slug.current != "" && slug.current != "/" => pageFolder->slug.current + "/" + slug.current,
17
+ slug.current
18
+ )
19
+ `;
20
+
21
+ // Hand-expanded, not generated: typegen walks the AST for `defineQuery` args and
22
+ // silently drops the result type for anything built by `.map().join()`.
23
+
24
+ // Root-to-leaf so joining yields the prefix in order. Paired id/slug, not two
25
+ // arrays: `array::compact` can drop from one and not the other.
26
+ const folderChainProjection = `pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->parentFolder->{ "id": _id, "slug": slug.current },
27
+ pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->{ "id": _id, "slug": slug.current },
28
+ pageFolder->parentFolder->parentFolder->parentFolder->{ "id": _id, "slug": slug.current },
29
+ pageFolder->parentFolder->parentFolder->{ "id": _id, "slug": slug.current },
30
+ pageFolder->parentFolder->{ "id": _id, "slug": slug.current },
31
+ pageFolder->{ "id": _id, "slug": slug.current }`;
32
+
33
+ const folderDescendantConditions = `pageFolder._ref == $folderId ||
34
+ pageFolder->parentFolder._ref == $folderId ||
35
+ pageFolder->parentFolder->parentFolder._ref == $folderId ||
36
+ pageFolder->parentFolder->parentFolder->parentFolder._ref == $folderId ||
37
+ pageFolder->parentFolder->parentFolder->parentFolder->parentFolder._ref == $folderId ||
38
+ pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->parentFolder._ref == $folderId`;
39
+
40
+ // Rooted at a `pageFolder`, so the walk starts at `parentFolder`.
41
+ const folderParentChain = `parentFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current,
42
+ parentFolder->parentFolder->parentFolder->parentFolder->slug.current,
43
+ parentFolder->parentFolder->parentFolder->slug.current,
44
+ parentFolder->parentFolder->slug.current,
45
+ parentFolder->slug.current`;
46
+
47
+ const linkItemProjection = `
48
+ ...,
49
+ _type == "pageReference" => {
50
+ page->{
51
+ _id,
52
+ _type,
53
+ slug,
54
+ title,
55
+ "resolvedSlug": select(
56
+ _type == "page" => ${pageResolvedSlugExpression},
57
+ slug.current
58
+ )
59
+ }
60
+ }
61
+ `;
62
+
63
+ // Helper for rich text link projections (expandable inside any array field)
64
+ const richTextLinkProjection = `
65
+ ...,
66
+ markDefs[]{
67
+ ...,
68
+ _type == "pageReference" => {
69
+ page->{
70
+ _id,
71
+ _type,
72
+ slug,
73
+ title,
74
+ "resolvedSlug": select(
75
+ _type == "page" => ${pageResolvedSlugExpression},
76
+ slug.current
77
+ )
78
+ }
79
+ }
80
+ }
81
+ `;
82
+
83
+ // `^` is the owning page document, so blocks whose `_key` is in the page's
84
+ // `hiddenPageBuilderKeys` list are filtered out here — a single choke point, so
85
+ // hidden blocks never reach the renderer, the preview, or any derived output
86
+ // built from this projection. `coalesce(..., [])` keeps pages that predate the
87
+ // field (no hidden blocks) rendering normally.
88
+ const pageBuilderProjection = `
89
+ pageBuilder[!(_key in coalesce(^.hiddenPageBuilderKeys, []))]{
90
+ _key,
91
+ ...@->{
92
+ // Gated content withholds its payload from the page query — only the
93
+ // teaser and gate config ship to the client. GROQ has no field exclusion,
94
+ // so this branch lists safe fields explicitly rather than spreading every
95
+ // field. The payload is fetched separately via GATED_CONTENT_QUERY.
96
+ _type == "gatedContent" => {
97
+ _id,
98
+ _type,
99
+ internalTitle,
100
+ teaserHeading,
101
+ teaserBody[]{
102
+ ${richTextLinkProjection}
103
+ },
104
+ gateType,
105
+ gateHeadline,
106
+ gateDescription,
107
+ submitLabel,
108
+ successMessage,
109
+ kind
110
+ },
111
+ _type != "gatedContent" => {
112
+ ...,
113
+ _type == "richTextBlock" => {
114
+ ...,
115
+ content[]{
116
+ ${richTextLinkProjection}
117
+ }
118
+ },
119
+ _type == "blogContent" => {
120
+ ...,
121
+ categories[]->{
122
+ _id,
123
+ title,
124
+ slug
125
+ },
126
+ author->{
127
+ _id,
128
+ name,
129
+ avatar,
130
+ role
131
+ },
132
+ body[]{
133
+ ${richTextLinkProjection}
134
+ },
135
+ relatedPosts[]->{
136
+ _id,
137
+ title,
138
+ "resolvedSlug": ${pageResolvedSlugExpression},
139
+ metadata
140
+ }
141
+ }
142
+ }
143
+ }
144
+ }
145
+ `;
146
+
147
+ // Gated content payload — fetched on demand by the unlock server action, keyed
148
+ // by the block's document id. Kept out of the page query so it never ships to
149
+ // the client until the visitor completes the gate.
150
+ export const GATED_CONTENT_QUERY = defineQuery(`
151
+ *[_type == "gatedContent" && _id == $id][0]{
152
+ kind,
153
+ content[]{
154
+ ${richTextLinkProjection}
155
+ },
156
+ "file": file.asset->{
157
+ url,
158
+ originalFilename,
159
+ mimeType,
160
+ size
161
+ },
162
+ "video": {
163
+ "url": videoUrl,
164
+ "file": videoFile.asset->{
165
+ url,
166
+ mimeType
167
+ }
168
+ }
169
+ }
170
+ `);
171
+
172
+ const pageProjection = `
173
+ _id,
174
+ _type,
175
+ title,
176
+ slug,
177
+ "resolvedSlug": ${pageResolvedSlugExpression},
178
+ "collectionCategory": collectionCategory->{title, "slug": slug.current},
179
+ ${pageBuilderProjection},
180
+ metadata,
181
+ _updatedAt
182
+ `;
183
+
184
+ const pageMetadataProjection = `
185
+ _id,
186
+ title,
187
+ metadata,
188
+ _updatedAt
189
+ `;
190
+
191
+ export const PAGE_QUERY = defineQuery(`
192
+ *[
193
+ _type == "page" &&
194
+ (
195
+ ($slug == null && !defined(pageFolder._ref) && (!defined(slug.current) || slug.current == "" || slug.current == "/")) ||
196
+ ${pageResolvedSlugExpression} == $slug
197
+ )
198
+ ][0] {
199
+ ${pageProjection}
200
+ }
201
+ `);
202
+
203
+ export const PAGE_METADATA_QUERY = defineQuery(`
204
+ *[
205
+ _type == "page" &&
206
+ (
207
+ ($slug == null && !defined(pageFolder._ref) && (!defined(slug.current) || slug.current == "" || slug.current == "/")) ||
208
+ ${pageResolvedSlugExpression} == $slug
209
+ )
210
+ ][0] {
211
+ ${pageMetadataProjection}
212
+ }
213
+ `);
214
+
215
+ export const ALL_PAGE_SLUGS_QUERY = defineQuery(`
216
+ *[_type == "page" && (defined(pageFolder->slug.current) || defined(slug.current))] | order(title asc) {
217
+ title,
218
+ "slug": ${pageResolvedSlugExpression},
219
+ _updatedAt
220
+ }
221
+ `);
222
+
223
+ export const P_REV_SLUGS_QUERY = defineQuery(`
224
+ *[
225
+ _type == "page" &&
226
+ _id in $documentIds
227
+ ] | order(_updatedAt desc) {
228
+ "slug": ${pageResolvedSlugExpression}
229
+ }
230
+ `);
231
+
232
+ // Pages depending on `$documentIds`, one or two hops out: one covers
233
+ // `collectionCategory->`, two covers anything reached through a page-builder
234
+ // block. All inline the referenced title or slug, so a change there goes stale.
235
+ export const P_REF_DOC_PAGES_QUERY = defineQuery(`
236
+ *[
237
+ _type == "page" &&
238
+ (
239
+ references($documentIds) ||
240
+ count(pageBuilder[_ref in *[references($documentIds)]._id]) > 0
241
+ )
242
+ ] | order(title asc) {
243
+ _id,
244
+ title,
245
+ "slug": ${pageResolvedSlugExpression}
246
+ }
247
+ `);
248
+
249
+ // A folder's resolved path, plus its own segment and parent path separately —
250
+ // what the webhook needs to rebuild where it used to be.
251
+ export const FOLDER_PATH_QUERY = defineQuery(`
252
+ *[_type == "pageFolder" && _id == $folderId][0]{
253
+ "slug": slug.current,
254
+ "parentPath": array::join(array::compact([
255
+ ${folderParentChain}
256
+ ]), "/"),
257
+ "path": array::join(array::compact([
258
+ ${folderParentChain},
259
+ slug.current
260
+ ]), "/")
261
+ }
262
+ `);
263
+
264
+ // Every page under `$folderId`, at any depth. The folder chain comes back as
265
+ // id/slug pairs rather than two parallel arrays -- `array::compact` on two
266
+ // independent lists can drop entries from one and not the other, and the webhook
267
+ // indexes into the chain by id. The page's own slug stays *out* of the array:
268
+ // `array::compact` removes null but not "" or "/", so folding a folder-index
269
+ // page's empty slug in here would yield "parent/" where the resolved path is
270
+ // "parent". `composeResolvedSlug` joins the leaf instead.
271
+ export const P_REV_FOLDER_DESCENDANTS_QUERY = defineQuery(`
272
+ *[
273
+ _type == "page" &&
274
+ (
275
+ ${folderDescendantConditions}
276
+ )
277
+ ]{
278
+ _id,
279
+ "folderChain": array::compact([
280
+ ${folderChainProjection}
281
+ ]),
282
+ "ownSlug": slug.current
283
+ }
284
+ `);
285
+
286
+ export const PAGE_SITEMAP_QUERY = defineQuery(`
287
+ *[
288
+ _type == "page" &&
289
+ (
290
+ _id in $pinnedIds ||
291
+ defined(pageFolder->slug.current) ||
292
+ defined(slug.current)
293
+ )
294
+ ] | order(title asc) {
295
+ title,
296
+ "slug": ${pageResolvedSlugExpression},
297
+ _updatedAt,
298
+ "noIndex": select(
299
+ defined(metadata.index) => !metadata.index,
300
+ defined(metadata.noIndex) => metadata.noIndex,
301
+ false
302
+ )
303
+ }
304
+ `);
305
+
306
+ // Blog collection -- fetches pages under a "blog" folder
307
+ // Reaches into the blogContent page-builder block for article-level fields
308
+ export const ALL_BLOG_ARTICLES_QUERY = defineQuery(`
309
+ *[
310
+ _type == "page" &&
311
+ pageFolder->slug.current == "blog" &&
312
+ defined(slug.current) &&
313
+ slug.current != "" &&
314
+ slug.current != "/"
315
+ ] | order(_updatedAt desc) {
316
+ _id,
317
+ title,
318
+ "slug": slug,
319
+ "resolvedSlug": ${pageResolvedSlugExpression},
320
+ "blogContent": pageBuilder[_ref in *[_type == "blogContent"]._id][0]->{
321
+ thumbnail{
322
+ ...,
323
+ alt
324
+ },
325
+ categories[]->{
326
+ _id,
327
+ title,
328
+ slug
329
+ },
330
+ date,
331
+ author->{
332
+ _id,
333
+ name,
334
+ avatar
335
+ }
336
+ },
337
+ metadata,
338
+ "noIndex": select(
339
+ defined(metadata.index) => !metadata.index,
340
+ defined(metadata.noIndex) => metadata.noIndex,
341
+ false
342
+ ),
343
+ _updatedAt
344
+ }
345
+ `);
346
+
347
+ export const BLOG_ARTICLES_PAGINATED_QUERY = defineQuery(`
348
+ *[
349
+ _type == "page" &&
350
+ pageFolder->slug.current == "blog" &&
351
+ defined(slug.current) &&
352
+ slug.current != "" &&
353
+ slug.current != "/" &&
354
+ (
355
+ !defined($category) ||
356
+ $category == "" ||
357
+ $category in pageBuilder[_ref in *[_type == "blogContent"]._id][0]->categories[]->slug.current
358
+ )
359
+ ] | order(_updatedAt desc)[$offset...$end] {
360
+ _id,
361
+ title,
362
+ "slug": slug,
363
+ "resolvedSlug": ${pageResolvedSlugExpression},
364
+ "blogContent": pageBuilder[_ref in *[_type == "blogContent"]._id][0]->{
365
+ thumbnail{
366
+ ...,
367
+ alt
368
+ },
369
+ categories[]->{
370
+ _id,
371
+ title,
372
+ slug
373
+ },
374
+ date,
375
+ author->{
376
+ _id,
377
+ name,
378
+ avatar
379
+ }
380
+ },
381
+ metadata,
382
+ _updatedAt
383
+ }
384
+ `);
385
+
386
+ export const BLOG_ARTICLES_COUNT_QUERY = defineQuery(`
387
+ count(*[
388
+ _type == "page" &&
389
+ pageFolder->slug.current == "blog" &&
390
+ defined(slug.current) &&
391
+ slug.current != "" &&
392
+ slug.current != "/" &&
393
+ (
394
+ !defined($category) ||
395
+ $category == "" ||
396
+ $category in pageBuilder[_ref in *[_type == "blogContent"]._id][0]->categories[]->slug.current
397
+ )
398
+ ])
399
+ `);
400
+
401
+ export const ALL_BLOG_CATEGORIES_QUERY = defineQuery(`
402
+ *[_type == "blogCategory"] | order(title asc) {
403
+ _id,
404
+ title,
405
+ slug
406
+ }
407
+ `);
408
+
409
+ // Pages that host a content collection: the ones pinned to a category become
410
+ // that category's landing page, and an unpinned one is the "all content" index.
411
+ // Hidden keys are filtered like `pageBuilderProjection` does — a page whose
412
+ // collection block is hidden renders no collection, so it can't be a chip target.
413
+ export const COLLECTION_PAGES_QUERY = defineQuery(`
414
+ *[
415
+ _type == "page" &&
416
+ count(pageBuilder[!(_key in coalesce(^.hiddenPageBuilderKeys, [])) && _ref in *[_type == "blogCollection"]._id]) > 0
417
+ ] {
418
+ "path": ${pageResolvedSlugExpression},
419
+ "category": collectionCategory->slug.current
420
+ }
421
+ `);
422
+
423
+ // Company Data -- folded into LAYOUT_QUERY below.
424
+ const companyDataProjection = `
425
+ socialLinks[]{
426
+ _key,
427
+ name,
428
+ icon,
429
+ url,
430
+ label
431
+ }
432
+ `;
433
+
434
+ // Navbar
435
+ // Shared link projection for navbar -- flattens array-of-one to single object
436
+ const navLinkProjection = `
437
+ "link": link[0]{
438
+ ${linkItemProjection}
439
+ }
440
+ `;
441
+
442
+ const navLeafItemProjection = `
443
+ _key,
444
+ title,
445
+ ${navLinkProjection}
446
+ `;
447
+
448
+ const navColumnProjection = `
449
+ _key,
450
+ title,
451
+ ${navLinkProjection},
452
+ items[]{
453
+ ${navLeafItemProjection}
454
+ }
455
+ `;
456
+
457
+ const navMegaMenuProjection = `
458
+ columns[]{
459
+ ${navColumnProjection}
460
+ }
461
+ `;
462
+
463
+ const navbarProjection = `
464
+ logo{
465
+ ...,
466
+ asset->{
467
+ _id,
468
+ url,
469
+ metadata { dimensions }
470
+ }
471
+ },
472
+ navigationItems[]{
473
+ _key,
474
+ title,
475
+ itemType,
476
+ ${navLinkProjection},
477
+ megaMenu{
478
+ ${navMegaMenuProjection}
479
+ }
480
+ },
481
+ ctaButtons[]{
482
+ _key,
483
+ label,
484
+ "link": link[0]{
485
+ ${linkItemProjection}
486
+ },
487
+ variant
488
+ }
489
+ `;
490
+
491
+ const footerProjection = `
492
+ links[]{
493
+ _key,
494
+ title,
495
+ items[]{
496
+ _key,
497
+ label,
498
+ href
499
+ }
500
+ }
501
+ `;
502
+
503
+ // sameAs: flagged Company Data social links + manual profiles, deduped.
504
+ const structuredDataProjection = `
505
+ name,
506
+ legalName,
507
+ description,
508
+ url,
509
+ "logo": logo.asset->url,
510
+ contactPoint,
511
+ address,
512
+ "sameAs": array::unique(array::compact(
513
+ coalesce(*[_type == "companyData"][0].socialLinks[includeInStructuredData == true].url, [])
514
+ + coalesce(sameAs, [])
515
+ ))
516
+ `;
517
+
518
+ // All four layout singletons in one round trip: every route's shell reads three of
519
+ // them, and as separate parameterless queries that was four cache entries and four
520
+ // fetches per revalidation. The single-document NAVBAR_QUERY / FOOTER_QUERY /
521
+ // COMPANY_DATA_QUERY / STRUCTURED_DATA_QUERY exports are gone on purpose — nothing
522
+ // consumed them, and they were a second untagged path to the same documents.
523
+ export const LAYOUT_QUERY = defineQuery(`
524
+ {
525
+ "navbar": *[_type == "navbar"][0]{
526
+ ${navbarProjection}
527
+ },
528
+ "footer": *[_type == "footer"][0]{
529
+ ${footerProjection}
530
+ },
531
+ "companyData": *[_type == "companyData"][0]{
532
+ ${companyDataProjection}
533
+ },
534
+ "structuredData": *[_type == "structuredData"][0]{
535
+ ${structuredDataProjection}
536
+ }
537
+ }
538
+ `);
539
+
540
+ // Redirects -- source/destination rules applied by the proxy.
541
+ // `source` is a slug; flatten to its slash-free path string.
542
+ export const REDIRECTS_QUERY = defineQuery(`
543
+ *[_type == "redirect" && defined(source.current) && defined(destination)]{
544
+ "source": source.current,
545
+ destination,
546
+ permanent
547
+ }
548
+ `);