bsmnt 0.10.0 → 0.12.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 (343) 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/components/ui/sanity-image/index.tsx +36 -4
  89. package/src/modules/features/cms/sanity/files/lib/integrations/check-integration.ts +7 -6
  90. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/README.md +1 -1
  91. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/components/rich-text.tsx +8 -1
  92. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/env.ts +2 -2
  93. package/src/modules/features/cms/sanity-pagebuilder/files/app/api/[[...slug]]/route.ts +4 -0
  94. package/src/modules/features/cms/sanity-pagebuilder/files/app/api/revalidate/route.ts +172 -24
  95. package/src/modules/features/cms/sanity-pagebuilder/files/app/feed.xml/route.ts +14 -7
  96. package/src/modules/features/cms/sanity-pagebuilder/files/app/sitemap.md/route.ts +2 -2
  97. package/src/modules/features/cms/sanity-pagebuilder/files/app/studio/[[...tool]]/page.tsx +16 -1
  98. package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/rich-text.tsx +8 -1
  99. package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/visual-editing.tsx +2 -2
  100. package/src/modules/features/cms/sanity-pagebuilder/files/components/ui/sanity-image/index.tsx +36 -4
  101. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/check-integration.ts +6 -5
  102. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/client.ts +8 -0
  103. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/page-builder-input.tsx +26 -7
  104. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/page-builder-insert-menu.tsx +242 -0
  105. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/slug-notices.tsx +6 -16
  106. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/use-studio-preview-urls.ts +62 -0
  107. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/env.ts +2 -2
  108. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/fetchers/layout.ts +22 -52
  109. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/icons.ts +2 -2
  110. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/live/index.tsx +3 -1
  111. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-builder-config.ts +78 -33
  112. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-tree-pane.tsx +70 -57
  113. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/queries.ts +119 -29
  114. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/revalidate-paths.ts +70 -0
  115. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/index.ts +2 -0
  116. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/rich-text-block.ts +39 -0
  117. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/studio-preview.ts +37 -0
  118. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/index.ts +5 -0
  119. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/page.ts +12 -0
  120. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/singletons.ts +2 -0
  121. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/structure.ts +5 -0
  122. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/tags.ts +56 -0
  123. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/utils/page-builder-markdown.ts +12 -0
  124. package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/sitemap.ts +11 -8
  125. package/src/modules/features/cms/sanity-pagebuilder-cache/files/.knip.json +8 -0
  126. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/api/[[...slug]]/route.ts +112 -0
  127. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/api/draft-mode/disable/route.ts +7 -0
  128. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/api/draft-mode/enable/route.ts +20 -0
  129. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/api/revalidate/route.ts +292 -0
  130. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/feed.xml/route.ts +66 -0
  131. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/sitemap.md/route.ts +132 -0
  132. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/studio/[[...tool]]/layout.tsx +26 -0
  133. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/studio/[[...tool]]/page.tsx +23 -0
  134. package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/sanity/draft-mode-toggle.tsx +27 -0
  135. package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/sanity/rich-text.tsx +94 -0
  136. package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/sanity/visual-editing.tsx +11 -0
  137. package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/ui/sanity-image/index.tsx +83 -0
  138. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/check-integration.ts +6 -0
  139. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/client.ts +37 -0
  140. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/block-actions-item.tsx +81 -0
  141. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/disable-draft-mode.tsx +23 -0
  142. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/page-builder-input.tsx +58 -0
  143. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/page-builder-insert-menu.tsx +242 -0
  144. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/page-category-input.tsx +50 -0
  145. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/same-as-input.tsx +66 -0
  146. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/slug-field.tsx +24 -0
  147. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/slug-input.tsx +14 -0
  148. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/slug-notices.tsx +125 -0
  149. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/use-studio-preview-urls.ts +62 -0
  150. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/confirm-publish-action.ts +40 -0
  151. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/env.ts +34 -0
  152. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/fetchers/layout.ts +39 -0
  153. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/fetchers/redirects.ts +22 -0
  154. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/icons.ts +81 -0
  155. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/live/index.tsx +140 -0
  156. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/markdown-proxy.config.ts +50 -0
  157. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/page-builder-config.ts +182 -0
  158. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/page-category.ts +28 -0
  159. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/page-tree-pane.tsx +431 -0
  160. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/pinned-pages.ts +54 -0
  161. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/presentation.ts +119 -0
  162. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/queries.ts +548 -0
  163. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/revalidate-paths.ts +70 -0
  164. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/sanity.cli.ts +29 -0
  165. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/sanity.config.ts +116 -0
  166. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/index.ts +7 -0
  167. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/blog-content.ts +89 -0
  168. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/description.ts +29 -0
  169. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/gated-content.ts +178 -0
  170. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/hero.ts +28 -0
  171. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/rich-text-block.ts +39 -0
  172. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/singleton/content-collection.ts +45 -0
  173. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/studio-preview.ts +37 -0
  174. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/content/author.ts +70 -0
  175. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/content/blog-category.ts +55 -0
  176. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/index.ts +137 -0
  177. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/company-data.ts +70 -0
  178. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/footer.ts +79 -0
  179. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/navbar.ts +75 -0
  180. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/redirect.ts +152 -0
  181. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/structured-data.ts +124 -0
  182. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/link.ts +125 -0
  183. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/logo-field.ts +9 -0
  184. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/metadata.ts +132 -0
  185. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/nav-objects.ts +192 -0
  186. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/page-builder.ts +43 -0
  187. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/page-folder.ts +124 -0
  188. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/page.ts +265 -0
  189. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/richText.ts +63 -0
  190. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/structured-data-blocks.ts +436 -0
  191. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/singletons.ts +53 -0
  192. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/structure.ts +339 -0
  193. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/tags.ts +89 -0
  194. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/utils/image.ts +8 -0
  195. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/utils/link.ts +140 -0
  196. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/utils/page-builder-markdown.ts +113 -0
  197. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/utils/page-json-ld.ts +208 -0
  198. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/scripts/sanity-typegen.ts +36 -0
  199. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/check-redirect.ts +30 -0
  200. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/json-ld.tsx +522 -0
  201. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/metadata.ts +189 -0
  202. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/rss.ts +96 -0
  203. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/sitemap.ts +58 -0
  204. package/src/modules/features/cms/sanity-pagebuilder-cache/files/proxy.ts +90 -0
  205. package/src/modules/features/env/dotenvx/files/vercelignore +5 -0
  206. package/src/templates/next-default/.biome/plugins/no-fill-without-sizes.grit +11 -0
  207. package/src/templates/next-default/.env.example +2 -2
  208. package/src/templates/next-default/.github/workflows/ci.yml +1 -1
  209. package/src/templates/next-default/.vscode/settings.json +0 -2
  210. package/src/templates/next-default/biome.json +2 -1
  211. package/src/templates/next-default/components/ui/image/index.tsx +21 -15
  212. package/src/templates/next-default/knip.json +1 -1
  213. package/src/templates/next-default/lefthook.yml +1 -1
  214. package/src/templates/next-default/lib/utils/README.md +18 -5
  215. package/src/templates/next-default/lib/utils/image-sizes.ts +72 -0
  216. package/src/templates/next-default/lib/utils/merge-refs.ts +56 -0
  217. package/src/templates/next-default/lib/utils/strings.ts +1 -51
  218. package/src/templates/next-default/next.config.ts +12 -7
  219. package/src/templates/next-default/package.json +2 -5
  220. package/src/templates/next-experiments/.biome/plugins/no-fill-without-sizes.grit +11 -0
  221. package/src/templates/next-experiments/.env.example +2 -2
  222. package/src/templates/next-experiments/.github/workflows/ci.yml +1 -1
  223. package/src/templates/next-experiments/.vscode/settings.json +0 -2
  224. package/src/templates/next-experiments/app/page.tsx +1 -1
  225. package/src/templates/next-experiments/biome.json +2 -1
  226. package/src/templates/next-experiments/components/ui/image/index.tsx +21 -15
  227. package/src/templates/next-experiments/knip.json +1 -1
  228. package/src/templates/next-experiments/lefthook.yml +1 -1
  229. package/src/templates/next-experiments/lib/utils/README.md +18 -5
  230. package/src/templates/next-experiments/lib/utils/image-sizes.ts +72 -0
  231. package/src/templates/next-experiments/lib/utils/merge-refs.ts +56 -0
  232. package/src/templates/next-experiments/lib/utils/strings.ts +1 -51
  233. package/src/templates/next-experiments/next.config.ts +12 -7
  234. package/src/templates/next-experiments/package.json +2 -5
  235. package/src/templates/next-pagebuilder/.biome/plugins/no-fill-without-sizes.grit +11 -0
  236. package/src/templates/next-pagebuilder/.env.example +1 -0
  237. package/src/templates/next-pagebuilder/.github/workflows/ci.yml +1 -1
  238. package/src/templates/next-pagebuilder/.vscode/settings.json +0 -2
  239. package/src/templates/next-pagebuilder/README.md +64 -0
  240. package/src/templates/next-pagebuilder/app/sitemap.xml/route.ts +3 -1
  241. package/src/templates/next-pagebuilder/biome.json +4 -2
  242. package/src/templates/next-pagebuilder/components/layout/footer/index.tsx +3 -6
  243. package/src/templates/next-pagebuilder/components/layout/header/index.tsx +3 -2
  244. package/src/templates/next-pagebuilder/components/layout/json-ld/index.tsx +3 -8
  245. package/src/templates/next-pagebuilder/components/page-builder/components/article-content/index.tsx +5 -0
  246. package/src/templates/next-pagebuilder/components/page-builder/components/article-content/related-post-item.tsx +1 -0
  247. package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/content-filters.tsx +23 -40
  248. package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/index.tsx +88 -58
  249. package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/skeleton.tsx +4 -16
  250. package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/types.ts +14 -1
  251. package/src/templates/next-pagebuilder/components/page-builder/components/rich-text-block.tsx +16 -0
  252. package/src/templates/next-pagebuilder/components/page-builder/renderer.tsx +21 -7
  253. package/src/templates/next-pagebuilder/components/page-document/index.tsx +1 -0
  254. package/src/templates/next-pagebuilder/components/ui/image/index.tsx +21 -12
  255. package/src/templates/next-pagebuilder/knip.json +3 -2
  256. package/src/templates/next-pagebuilder/lefthook.yml +1 -1
  257. package/src/templates/next-pagebuilder/lib/integrations/sanity/sanity.types.ts +1221 -977
  258. package/src/templates/next-pagebuilder/lib/utils/image-sizes.ts +72 -0
  259. package/src/templates/next-pagebuilder/lib/utils/merge-refs.ts +56 -0
  260. package/src/templates/next-pagebuilder/next.config.ts +6 -6
  261. package/src/templates/next-pagebuilder/package.json +4 -6
  262. package/src/templates/next-pagebuilder-cache/.biome/plugins/README.md +21 -0
  263. package/src/templates/next-pagebuilder-cache/.biome/plugins/no-anchor-element.grit +12 -0
  264. package/src/templates/next-pagebuilder-cache/.biome/plugins/no-fill-without-sizes.grit +11 -0
  265. package/src/templates/next-pagebuilder-cache/.biome/plugins/no-relative-parent-imports.grit +10 -0
  266. package/src/templates/next-pagebuilder-cache/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
  267. package/src/templates/next-pagebuilder-cache/.env.example +40 -0
  268. package/src/templates/next-pagebuilder-cache/.github/PULL_REQUEST_TEMPLATE.md +13 -0
  269. package/src/templates/next-pagebuilder-cache/.github/workflows/ci.yml +105 -0
  270. package/src/templates/next-pagebuilder-cache/.vscode/extensions.json +19 -0
  271. package/src/templates/next-pagebuilder-cache/.vscode/settings.json +103 -0
  272. package/src/templates/next-pagebuilder-cache/.zed/settings.json +47 -0
  273. package/src/templates/next-pagebuilder-cache/README.md +212 -0
  274. package/src/templates/next-pagebuilder-cache/_gitignore +65 -0
  275. package/src/templates/next-pagebuilder-cache/app/(content)/[[...slug]]/page.tsx +124 -0
  276. package/src/templates/next-pagebuilder-cache/app/(content)/layout.tsx +28 -0
  277. package/src/templates/next-pagebuilder-cache/app/actions/unlock-gated-content.ts +53 -0
  278. package/src/templates/next-pagebuilder-cache/app/favicon.ico +0 -0
  279. package/src/templates/next-pagebuilder-cache/app/layout.tsx +83 -0
  280. package/src/templates/next-pagebuilder-cache/app/robots.ts +13 -0
  281. package/src/templates/next-pagebuilder-cache/app/sitemap.xml/route.ts +84 -0
  282. package/src/templates/next-pagebuilder-cache/biome.json +258 -0
  283. package/src/templates/next-pagebuilder-cache/components/layout/footer/index.tsx +127 -0
  284. package/src/templates/next-pagebuilder-cache/components/layout/header/components/cta-button.tsx +28 -0
  285. package/src/templates/next-pagebuilder-cache/components/layout/header/components/mega-menu-panel.tsx +90 -0
  286. package/src/templates/next-pagebuilder-cache/components/layout/header/components/nav-item-renderer.tsx +98 -0
  287. package/src/templates/next-pagebuilder-cache/components/layout/header/components/nav-leaf-item.tsx +33 -0
  288. package/src/templates/next-pagebuilder-cache/components/layout/header/components/types.ts +7 -0
  289. package/src/templates/next-pagebuilder-cache/components/layout/header/header-client.tsx +110 -0
  290. package/src/templates/next-pagebuilder-cache/components/layout/header/index.tsx +32 -0
  291. package/src/templates/next-pagebuilder-cache/components/layout/json-ld/index.tsx +79 -0
  292. package/src/templates/next-pagebuilder-cache/components/layout/wrapper/index.tsx +33 -0
  293. package/src/templates/next-pagebuilder-cache/components/page-builder/components/article-content/index.tsx +83 -0
  294. package/src/templates/next-pagebuilder-cache/components/page-builder/components/article-content/related-post-item.tsx +28 -0
  295. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/content-card.tsx +66 -0
  296. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/content-filters.tsx +76 -0
  297. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/content-grid.tsx +40 -0
  298. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/content-pagination-nav.tsx +71 -0
  299. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/index.tsx +306 -0
  300. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/skeleton.tsx +41 -0
  301. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/types.ts +32 -0
  302. package/src/templates/next-pagebuilder-cache/components/page-builder/components/description.tsx +17 -0
  303. package/src/templates/next-pagebuilder-cache/components/page-builder/components/gated-content/index.tsx +169 -0
  304. package/src/templates/next-pagebuilder-cache/components/page-builder/components/hero.tsx +17 -0
  305. package/src/templates/next-pagebuilder-cache/components/page-builder/components/rich-text-block.tsx +16 -0
  306. package/src/templates/next-pagebuilder-cache/components/page-builder/renderer.tsx +71 -0
  307. package/src/templates/next-pagebuilder-cache/components/page-builder/types.ts +25 -0
  308. package/src/templates/next-pagebuilder-cache/components/page-document/index.tsx +147 -0
  309. package/src/templates/next-pagebuilder-cache/components/ui/image/index.tsx +225 -0
  310. package/src/templates/next-pagebuilder-cache/components/ui/link/index.tsx +152 -0
  311. package/src/templates/next-pagebuilder-cache/components.json +24 -0
  312. package/src/templates/next-pagebuilder-cache/knip.json +35 -0
  313. package/src/templates/next-pagebuilder-cache/lefthook.yml +25 -0
  314. package/src/templates/next-pagebuilder-cache/lib/integrations/sanity/sanity.types.ts +1588 -0
  315. package/src/templates/next-pagebuilder-cache/lib/scripts/setup-ci-secrets.ts +117 -0
  316. package/src/templates/next-pagebuilder-cache/lib/styles/cn.ts +5 -0
  317. package/src/templates/next-pagebuilder-cache/lib/styles/global.css +69 -0
  318. package/src/templates/next-pagebuilder-cache/lib/utils/base-url.ts +17 -0
  319. package/src/templates/next-pagebuilder-cache/lib/utils/format-date.ts +8 -0
  320. package/src/templates/next-pagebuilder-cache/lib/utils/image-sizes.ts +72 -0
  321. package/src/templates/next-pagebuilder-cache/lib/utils/slug-tag.ts +6 -0
  322. package/src/templates/next-pagebuilder-cache/next.config.ts +177 -0
  323. package/src/templates/next-pagebuilder-cache/package.json +62 -0
  324. package/src/templates/next-pagebuilder-cache/postcss.config.mjs +65 -0
  325. package/src/templates/next-pagebuilder-cache/svg.d.ts +6 -0
  326. package/src/templates/next-pagebuilder-cache/tsconfig.json +38 -0
  327. package/src/templates/next-webgl/.biome/plugins/no-fill-without-sizes.grit +11 -0
  328. package/src/templates/next-webgl/.env.example +2 -2
  329. package/src/templates/next-webgl/.github/workflows/ci.yml +1 -1
  330. package/src/templates/next-webgl/.vscode/settings.json +0 -2
  331. package/src/templates/next-webgl/biome.json +2 -1
  332. package/src/templates/next-webgl/components/ui/image/index.tsx +21 -15
  333. package/src/templates/next-webgl/knip.json +1 -1
  334. package/src/templates/next-webgl/lefthook.yml +1 -1
  335. package/src/templates/next-webgl/lib/utils/README.md +18 -5
  336. package/src/templates/next-webgl/lib/utils/image-sizes.ts +72 -0
  337. package/src/templates/next-webgl/lib/utils/merge-refs.ts +56 -0
  338. package/src/templates/next-webgl/lib/utils/strings.ts +1 -51
  339. package/src/templates/next-webgl/next.config.ts +12 -7
  340. package/src/templates/next-webgl/package.json +4 -7
  341. package/src/templates/next-experiments/tsconfig.tsbuildinfo +0 -1
  342. package/src/templates/next-pagebuilder/app/actions/refresh.ts +0 -5
  343. package/src/templates/next-webgl/tsconfig.tsbuildinfo +0 -1
@@ -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
+ `);
@@ -0,0 +1,70 @@
1
+ // Sibling-relative, not `@/`: the repo's test suite imports this file directly.
2
+ import { isEmptyPageSlug } from "./page-category";
3
+
4
+ // Path arithmetic for the publish webhook. Free of `next/*` so it can be unit
5
+ // tested, because the payload carries the raw document, never the resolved URL.
6
+
7
+ export interface FolderChainEntry {
8
+ id: string | null;
9
+ slug: string | null;
10
+ }
11
+
12
+ export interface DescendantPaths {
13
+ current: string | null;
14
+ previous: string | null;
15
+ }
16
+
17
+ interface DescendantPathsInput {
18
+ /** Root-to-leaf folder chain of the descendant, as projected by GROQ. */
19
+ folderChain: readonly FolderChainEntry[];
20
+ ownSlug: string | null | undefined;
21
+ /** The mutated folder. */
22
+ folderId: string;
23
+ previousFolderSlug: string;
24
+ /** Resolved path of the folder's *previous* parent; null at the root. */
25
+ previousParentPath: string | null;
26
+ }
27
+
28
+ // The JS half of `pageResolvedSlugExpression`. An empty own slug means the page
29
+ // *is* its folder's index, so the folder path is the whole URL.
30
+ export const composeResolvedSlug = (
31
+ folderPath: string | null,
32
+ ownSlug: string | null | undefined,
33
+ ): string | null => {
34
+ if (isEmptyPageSlug(ownSlug)) return folderPath || null;
35
+
36
+ return folderPath ? `${folderPath}/${ownSlug}` : (ownSlug ?? null);
37
+ };
38
+
39
+ // Where a descendant of a moved folder lives now and where it lived before.
40
+ // Null when the chain can't be trusted: a fabricated old path would purge an
41
+ // unrelated URL and leave the real one stale.
42
+ export const composeDescendantPaths = ({
43
+ folderChain,
44
+ ownSlug,
45
+ folderId,
46
+ previousFolderSlug,
47
+ previousParentPath,
48
+ }: DescendantPathsInput): DescendantPaths | null => {
49
+ const index = folderChain.findIndex((folder) => folder.id === folderId);
50
+ // Selected *by* this id, so an absent one means chain and filter disagree.
51
+ if (index === -1) return null;
52
+
53
+ const segments: string[] = [];
54
+ for (const folder of folderChain) {
55
+ // Required on `pageFolder`; a gap would silently shift every segment.
56
+ if (!folder.slug) return null;
57
+ segments.push(folder.slug);
58
+ }
59
+
60
+ const previousFolderPath = [
61
+ ...(previousParentPath ? [previousParentPath] : []),
62
+ previousFolderSlug,
63
+ ...segments.slice(index + 1),
64
+ ].join("/");
65
+
66
+ return {
67
+ current: composeResolvedSlug(segments.join("/"), ownSlug),
68
+ previous: composeResolvedSlug(previousFolderPath, ownSlug),
69
+ };
70
+ };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * This configuration file lets you run `$ sanity [command]` in this folder
3
+ * Go to https://www.sanity.io/docs/cli to learn more.
4
+ **/
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import { defineCliConfig } from "sanity/cli";
8
+ import { dataset, projectId } from "./env";
9
+
10
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
+
12
+ export default defineCliConfig({
13
+ api: { projectId, dataset },
14
+ project: {
15
+ basePath: "/studio",
16
+ },
17
+ typegen: {
18
+ path: "./queries.ts",
19
+ schema: "./schema.json",
20
+ generates: "./sanity.types.ts",
21
+ },
22
+ vite: {
23
+ resolve: {
24
+ alias: {
25
+ "~": __dirname,
26
+ },
27
+ },
28
+ },
29
+ });