bsmnt 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. package/dist/application/add-integration/index.d.ts.map +1 -1
  2. package/dist/application/add-integration/index.js +19 -2
  3. package/dist/application/add-integration/index.js.map +1 -1
  4. package/dist/application/shared/node-version.js.map +1 -1
  5. package/dist/core/create/internal-ids.d.ts +3 -0
  6. package/dist/core/create/internal-ids.d.ts.map +1 -1
  7. package/dist/core/create/internal-ids.js +4 -0
  8. package/dist/core/create/internal-ids.js.map +1 -1
  9. package/dist/domain/agents.d.ts.map +1 -1
  10. package/dist/domain/animations.d.ts.map +1 -1
  11. package/dist/domain/cms.d.ts.map +1 -1
  12. package/dist/domain/forms.d.ts.map +1 -1
  13. package/dist/domain/hooks.d.ts.map +1 -1
  14. package/dist/domain/skills.d.ts.map +1 -1
  15. package/dist/domain/templates.d.ts +10 -0
  16. package/dist/domain/templates.d.ts.map +1 -1
  17. package/dist/domain/templates.js +5 -0
  18. package/dist/domain/templates.js.map +1 -1
  19. package/dist/index.js +18 -5
  20. package/dist/index.js.map +1 -1
  21. package/dist/infrastructure/agents/register-sanity-mcp.js +3 -8
  22. package/dist/infrastructure/agents/register-sanity-mcp.js.map +1 -1
  23. package/dist/infrastructure/agents/register-storybook-mcp.d.ts +18 -0
  24. package/dist/infrastructure/agents/register-storybook-mcp.d.ts.map +1 -0
  25. package/dist/infrastructure/agents/register-storybook-mcp.js +157 -0
  26. package/dist/infrastructure/agents/register-storybook-mcp.js.map +1 -0
  27. package/dist/infrastructure/create/lifecycle-registry.d.ts.map +1 -1
  28. package/dist/infrastructure/create/lifecycle-registry.js +39 -1
  29. package/dist/infrastructure/create/lifecycle-registry.js.map +1 -1
  30. package/dist/infrastructure/create/merge-registry.d.ts.map +1 -1
  31. package/dist/infrastructure/create/merge-registry.js +4 -0
  32. package/dist/infrastructure/create/merge-registry.js.map +1 -1
  33. package/dist/infrastructure/create/sanity-auth.js.map +1 -1
  34. package/dist/infrastructure/create/setup-agent.d.ts +6 -1
  35. package/dist/infrastructure/create/setup-agent.d.ts.map +1 -1
  36. package/dist/infrastructure/create/setup-agent.js +23 -21
  37. package/dist/infrastructure/create/setup-agent.js.map +1 -1
  38. package/dist/infrastructure/create/setup-dotenvx.js.map +1 -1
  39. package/dist/infrastructure/create/setup-remote.js.map +1 -1
  40. package/dist/infrastructure/create/setup-sanity.d.ts.map +1 -1
  41. package/dist/infrastructure/create/setup-sanity.js +12 -1
  42. package/dist/infrastructure/create/setup-sanity.js.map +1 -1
  43. package/dist/infrastructure/integrations/ensure-studio-headers.d.ts +6 -0
  44. package/dist/infrastructure/integrations/ensure-studio-headers.d.ts.map +1 -0
  45. package/dist/infrastructure/integrations/ensure-studio-headers.js +153 -0
  46. package/dist/infrastructure/integrations/ensure-studio-headers.js.map +1 -0
  47. package/dist/infrastructure/integrations/inject-integration.d.ts.map +1 -1
  48. package/dist/infrastructure/integrations/inject-integration.js +2 -0
  49. package/dist/infrastructure/integrations/inject-integration.js.map +1 -1
  50. package/dist/infrastructure/update/notifier.js.map +1 -1
  51. package/dist/modules/features/cms/index.d.ts.map +1 -1
  52. package/dist/modules/features/cms/index.js +2 -0
  53. package/dist/modules/features/cms/index.js.map +1 -1
  54. package/dist/modules/features/cms/sanity/config.d.ts.map +1 -1
  55. package/dist/modules/features/cms/sanity/config.js +3 -1
  56. package/dist/modules/features/cms/sanity/config.js.map +1 -1
  57. package/dist/modules/features/cms/sanity/index.d.ts.map +1 -1
  58. package/dist/modules/features/cms/sanity/index.js +10 -1
  59. package/dist/modules/features/cms/sanity/index.js.map +1 -1
  60. package/dist/modules/features/cms/sanity/mergers/check-integration.d.ts.map +1 -1
  61. package/dist/modules/features/cms/sanity/mergers/check-integration.js +15 -6
  62. package/dist/modules/features/cms/sanity/mergers/check-integration.js.map +1 -1
  63. package/dist/modules/features/cms/sanity/mergers/knip.d.ts.map +1 -1
  64. package/dist/modules/features/cms/sanity/mergers/knip.js +1 -0
  65. package/dist/modules/features/cms/sanity/mergers/knip.js.map +1 -1
  66. package/dist/modules/features/cms/sanity/mergers/layout.js.map +1 -1
  67. package/dist/modules/features/cms/sanity/mergers/package-json.d.ts +12 -0
  68. package/dist/modules/features/cms/sanity/mergers/package-json.d.ts.map +1 -0
  69. package/dist/modules/features/cms/sanity/mergers/package-json.js +19 -0
  70. package/dist/modules/features/cms/sanity/mergers/package-json.js.map +1 -0
  71. package/dist/modules/features/cms/sanity-pagebuilder/config.d.ts.map +1 -1
  72. package/dist/modules/features/cms/sanity-pagebuilder/config.js +2 -1
  73. package/dist/modules/features/cms/sanity-pagebuilder/config.js.map +1 -1
  74. package/dist/modules/features/cms/sanity-pagebuilder/index.d.ts.map +1 -1
  75. package/dist/modules/features/cms/sanity-pagebuilder/index.js +7 -1
  76. package/dist/modules/features/cms/sanity-pagebuilder/index.js.map +1 -1
  77. package/dist/modules/features/cms/sanity-pagebuilder-cache/config.d.ts +14 -0
  78. package/dist/modules/features/cms/sanity-pagebuilder-cache/config.d.ts.map +1 -0
  79. package/dist/modules/features/cms/sanity-pagebuilder-cache/config.js +50 -0
  80. package/dist/modules/features/cms/sanity-pagebuilder-cache/config.js.map +1 -0
  81. package/dist/modules/features/cms/sanity-pagebuilder-cache/index.d.ts +3 -0
  82. package/dist/modules/features/cms/sanity-pagebuilder-cache/index.d.ts.map +1 -0
  83. package/dist/modules/features/cms/sanity-pagebuilder-cache/index.js +55 -0
  84. package/dist/modules/features/cms/sanity-pagebuilder-cache/index.js.map +1 -0
  85. package/dist/modules/presets/index.d.ts.map +1 -1
  86. package/dist/modules/presets/index.js +2 -0
  87. package/dist/modules/presets/index.js.map +1 -1
  88. package/dist/modules/presets/next-pagebuilder-cache.d.ts +3 -0
  89. package/dist/modules/presets/next-pagebuilder-cache.d.ts.map +1 -0
  90. package/dist/modules/presets/next-pagebuilder-cache.js +10 -0
  91. package/dist/modules/presets/next-pagebuilder-cache.js.map +1 -0
  92. package/dist/paths.d.ts +6 -0
  93. package/dist/paths.d.ts.map +1 -1
  94. package/dist/paths.js +6 -0
  95. package/dist/paths.js.map +1 -1
  96. package/package.json +6 -3
  97. package/src/modules/features/cms/sanity/files/app/api/blog/[slug]/route.ts +4 -1
  98. package/src/modules/features/cms/sanity/files/app/sitemap.md/route.ts +2 -1
  99. package/src/modules/features/cms/sanity/files/components/ui/sanity-image/index.tsx +19 -5
  100. package/src/modules/features/cms/sanity/files/lib/integrations/check-integration.ts +7 -6
  101. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/README.md +32 -1
  102. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/components/rich-text.tsx +1 -0
  103. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/env.ts +2 -2
  104. package/src/modules/features/cms/sanity/files/proxy.ts +9 -0
  105. package/src/modules/features/cms/sanity-pagebuilder/files/app/api/[[...slug]]/route.ts +12 -3
  106. package/src/modules/features/cms/sanity-pagebuilder/files/app/api/revalidate/route.ts +172 -24
  107. package/src/modules/features/cms/sanity-pagebuilder/files/app/feed.xml/route.ts +14 -7
  108. package/src/modules/features/cms/sanity-pagebuilder/files/app/sitemap.md/route.ts +8 -2
  109. package/src/modules/features/cms/sanity-pagebuilder/files/app/studio/[[...tool]]/page.tsx +16 -1
  110. package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/rich-text.tsx +1 -0
  111. package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/visual-editing.tsx +2 -2
  112. package/src/modules/features/cms/sanity-pagebuilder/files/components/ui/sanity-image/index.tsx +15 -5
  113. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/check-integration.ts +6 -5
  114. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/client.ts +8 -0
  115. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/slug-field.tsx +13 -7
  116. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/slug-notices.tsx +125 -0
  117. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/env.ts +2 -2
  118. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/fetchers/layout.ts +22 -52
  119. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/icons.ts +2 -2
  120. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/live/index.tsx +4 -2
  121. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/markdown-proxy.config.ts +1 -1
  122. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-builder-config.ts +7 -0
  123. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-tree-pane.tsx +70 -57
  124. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/queries.ts +119 -29
  125. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/revalidate-paths.ts +70 -0
  126. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/index.ts +1 -0
  127. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/rich-text-block.ts +39 -0
  128. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/index.ts +2 -0
  129. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/redirect.ts +35 -4
  130. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/page.ts +12 -0
  131. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/tags.ts +56 -0
  132. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/utils/page-builder-markdown.ts +12 -0
  133. package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/sitemap.ts +11 -8
  134. package/src/modules/features/cms/sanity-pagebuilder/files/proxy.ts +9 -0
  135. package/src/modules/features/cms/sanity-pagebuilder-cache/files/.knip.json +8 -0
  136. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/api/[[...slug]]/route.ts +112 -0
  137. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/api/draft-mode/disable/route.ts +7 -0
  138. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/api/draft-mode/enable/route.ts +20 -0
  139. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/api/revalidate/route.ts +292 -0
  140. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/feed.xml/route.ts +66 -0
  141. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/sitemap.md/route.ts +132 -0
  142. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/studio/[[...tool]]/layout.tsx +26 -0
  143. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/studio/[[...tool]]/page.tsx +23 -0
  144. package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/sanity/draft-mode-toggle.tsx +27 -0
  145. package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/sanity/rich-text.tsx +88 -0
  146. package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/sanity/visual-editing.tsx +11 -0
  147. package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/ui/sanity-image/index.tsx +51 -0
  148. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/check-integration.ts +6 -0
  149. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/client.ts +37 -0
  150. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/block-actions-item.tsx +81 -0
  151. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/disable-draft-mode.tsx +23 -0
  152. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/page-builder-input.tsx +39 -0
  153. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/page-category-input.tsx +50 -0
  154. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/same-as-input.tsx +66 -0
  155. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/slug-field.tsx +24 -0
  156. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/slug-input.tsx +14 -0
  157. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/slug-notices.tsx +125 -0
  158. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/confirm-publish-action.ts +40 -0
  159. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/env.ts +34 -0
  160. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/fetchers/layout.ts +39 -0
  161. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/fetchers/redirects.ts +22 -0
  162. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/icons.ts +81 -0
  163. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/live/index.tsx +140 -0
  164. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/markdown-proxy.config.ts +50 -0
  165. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/page-builder-config.ts +144 -0
  166. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/page-category.ts +28 -0
  167. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/page-tree-pane.tsx +431 -0
  168. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/pinned-pages.ts +54 -0
  169. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/presentation.ts +119 -0
  170. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/queries.ts +548 -0
  171. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/revalidate-paths.ts +70 -0
  172. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/sanity.cli.ts +29 -0
  173. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/sanity.config.ts +116 -0
  174. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/index.ts +6 -0
  175. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/blog-content.ts +89 -0
  176. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/description.ts +29 -0
  177. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/gated-content.ts +178 -0
  178. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/hero.ts +28 -0
  179. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/rich-text-block.ts +39 -0
  180. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/singleton/content-collection.ts +45 -0
  181. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/content/author.ts +70 -0
  182. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/content/blog-category.ts +55 -0
  183. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/index.ts +134 -0
  184. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/company-data.ts +70 -0
  185. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/footer.ts +79 -0
  186. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/navbar.ts +75 -0
  187. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/redirect.ts +152 -0
  188. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/structured-data.ts +124 -0
  189. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/link.ts +125 -0
  190. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/logo-field.ts +9 -0
  191. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/metadata.ts +132 -0
  192. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/nav-objects.ts +192 -0
  193. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/page-builder.ts +43 -0
  194. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/page-folder.ts +124 -0
  195. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/page.ts +265 -0
  196. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/richText.ts +63 -0
  197. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/structured-data-blocks.ts +436 -0
  198. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/singletons.ts +51 -0
  199. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/structure.ts +334 -0
  200. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/tags.ts +89 -0
  201. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/utils/image.ts +8 -0
  202. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/utils/link.ts +140 -0
  203. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/utils/page-builder-markdown.ts +113 -0
  204. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/utils/page-json-ld.ts +208 -0
  205. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/scripts/sanity-typegen.ts +36 -0
  206. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/check-redirect.ts +30 -0
  207. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/json-ld.tsx +522 -0
  208. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/metadata.ts +189 -0
  209. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/rss.ts +96 -0
  210. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/sitemap.ts +58 -0
  211. package/src/modules/features/cms/sanity-pagebuilder-cache/files/proxy.ts +90 -0
  212. package/src/modules/features/env/dotenvx/files/vercelignore +5 -0
  213. package/src/templates/next-default/.env.example +4 -2
  214. package/src/templates/next-default/.github/workflows/ci.yml +1 -1
  215. package/src/templates/next-default/.vscode/settings.json +0 -2
  216. package/src/templates/next-default/app/robots.ts +2 -3
  217. package/src/templates/next-default/components/ui/image/index.tsx +2 -4
  218. package/src/templates/next-default/knip.json +1 -1
  219. package/src/templates/next-default/lefthook.yml +1 -1
  220. package/src/templates/next-default/lib/utils/README.md +18 -5
  221. package/src/templates/next-default/lib/utils/image-sizes.ts +72 -0
  222. package/src/templates/next-default/lib/utils/merge-refs.ts +56 -0
  223. package/src/templates/next-default/lib/utils/strings.ts +1 -51
  224. package/src/templates/next-default/next.config.ts +38 -5
  225. package/src/templates/next-default/package.json +9 -12
  226. package/src/templates/next-experiments/.env.example +4 -2
  227. package/src/templates/next-experiments/.github/workflows/ci.yml +1 -1
  228. package/src/templates/next-experiments/.vscode/settings.json +0 -2
  229. package/src/templates/next-experiments/app/page.tsx +1 -1
  230. package/src/templates/next-experiments/app/robots.ts +2 -3
  231. package/src/templates/next-experiments/components/ui/image/index.tsx +2 -4
  232. package/src/templates/next-experiments/knip.json +1 -1
  233. package/src/templates/next-experiments/lefthook.yml +1 -1
  234. package/src/templates/next-experiments/lib/utils/README.md +18 -5
  235. package/src/templates/next-experiments/lib/utils/image-sizes.ts +72 -0
  236. package/src/templates/next-experiments/lib/utils/merge-refs.ts +56 -0
  237. package/src/templates/next-experiments/lib/utils/strings.ts +1 -51
  238. package/src/templates/next-experiments/next.config.ts +38 -5
  239. package/src/templates/next-experiments/package.json +9 -12
  240. package/src/templates/next-pagebuilder/.env.example +3 -0
  241. package/src/templates/next-pagebuilder/.github/workflows/ci.yml +1 -1
  242. package/src/templates/next-pagebuilder/.vscode/settings.json +0 -2
  243. package/src/templates/next-pagebuilder/README.md +95 -0
  244. package/src/templates/next-pagebuilder/app/(content)/[[...slug]]/page.tsx +24 -9
  245. package/src/templates/next-pagebuilder/app/robots.ts +2 -3
  246. package/src/templates/next-pagebuilder/app/sitemap.xml/route.ts +9 -4
  247. package/src/templates/next-pagebuilder/biome.json +2 -1
  248. package/src/templates/next-pagebuilder/components/layout/footer/index.tsx +3 -6
  249. package/src/templates/next-pagebuilder/components/layout/header/index.tsx +3 -2
  250. package/src/templates/next-pagebuilder/components/layout/json-ld/index.tsx +3 -8
  251. package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/content-filters.tsx +23 -40
  252. package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/index.tsx +88 -58
  253. package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/skeleton.tsx +41 -0
  254. package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/types.ts +14 -1
  255. package/src/templates/next-pagebuilder/components/page-builder/components/rich-text-block.tsx +16 -0
  256. package/src/templates/next-pagebuilder/components/page-builder/renderer.tsx +23 -3
  257. package/src/templates/next-pagebuilder/components/page-document/index.tsx +1 -0
  258. package/src/templates/next-pagebuilder/components/ui/image/index.tsx +2 -1
  259. package/src/templates/next-pagebuilder/knip.json +3 -2
  260. package/src/templates/next-pagebuilder/lefthook.yml +1 -1
  261. package/src/templates/next-pagebuilder/lib/integrations/sanity/sanity.types.ts +1221 -977
  262. package/src/templates/next-pagebuilder/lib/utils/image-sizes.ts +72 -0
  263. package/src/templates/next-pagebuilder/lib/utils/merge-refs.ts +56 -0
  264. package/src/templates/next-pagebuilder/next.config.ts +35 -5
  265. package/src/templates/next-pagebuilder/package.json +8 -10
  266. package/src/templates/next-pagebuilder-cache/.biome/plugins/README.md +21 -0
  267. package/src/templates/next-pagebuilder-cache/.biome/plugins/no-anchor-element.grit +12 -0
  268. package/src/templates/next-pagebuilder-cache/.biome/plugins/no-relative-parent-imports.grit +10 -0
  269. package/src/templates/next-pagebuilder-cache/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
  270. package/src/templates/next-pagebuilder-cache/.env.example +40 -0
  271. package/src/templates/next-pagebuilder-cache/.github/PULL_REQUEST_TEMPLATE.md +13 -0
  272. package/src/templates/next-pagebuilder-cache/.github/workflows/ci.yml +105 -0
  273. package/src/templates/next-pagebuilder-cache/.vscode/extensions.json +19 -0
  274. package/src/templates/next-pagebuilder-cache/.vscode/settings.json +103 -0
  275. package/src/templates/next-pagebuilder-cache/.zed/settings.json +47 -0
  276. package/src/templates/next-pagebuilder-cache/README.md +212 -0
  277. package/src/templates/next-pagebuilder-cache/_gitignore +65 -0
  278. package/src/templates/next-pagebuilder-cache/app/(content)/[[...slug]]/page.tsx +124 -0
  279. package/src/templates/next-pagebuilder-cache/app/(content)/layout.tsx +28 -0
  280. package/src/templates/next-pagebuilder-cache/app/actions/unlock-gated-content.ts +53 -0
  281. package/src/templates/next-pagebuilder-cache/app/favicon.ico +0 -0
  282. package/src/templates/next-pagebuilder-cache/app/layout.tsx +83 -0
  283. package/src/templates/next-pagebuilder-cache/app/robots.ts +13 -0
  284. package/src/templates/next-pagebuilder-cache/app/sitemap.xml/route.ts +84 -0
  285. package/src/templates/next-pagebuilder-cache/biome.json +257 -0
  286. package/src/templates/next-pagebuilder-cache/components/layout/footer/index.tsx +127 -0
  287. package/src/templates/next-pagebuilder-cache/components/layout/header/components/cta-button.tsx +28 -0
  288. package/src/templates/next-pagebuilder-cache/components/layout/header/components/mega-menu-panel.tsx +90 -0
  289. package/src/templates/next-pagebuilder-cache/components/layout/header/components/nav-item-renderer.tsx +98 -0
  290. package/src/templates/next-pagebuilder-cache/components/layout/header/components/nav-leaf-item.tsx +33 -0
  291. package/src/templates/next-pagebuilder-cache/components/layout/header/components/types.ts +7 -0
  292. package/src/templates/next-pagebuilder-cache/components/layout/header/header-client.tsx +110 -0
  293. package/src/templates/next-pagebuilder-cache/components/layout/header/index.tsx +32 -0
  294. package/src/templates/next-pagebuilder-cache/components/layout/json-ld/index.tsx +79 -0
  295. package/src/templates/next-pagebuilder-cache/components/layout/wrapper/index.tsx +33 -0
  296. package/src/templates/next-pagebuilder-cache/components/page-builder/components/article-content/index.tsx +78 -0
  297. package/src/templates/next-pagebuilder-cache/components/page-builder/components/article-content/related-post-item.tsx +27 -0
  298. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/content-card.tsx +66 -0
  299. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/content-filters.tsx +76 -0
  300. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/content-grid.tsx +40 -0
  301. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/content-pagination-nav.tsx +71 -0
  302. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/index.tsx +306 -0
  303. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/skeleton.tsx +41 -0
  304. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/types.ts +32 -0
  305. package/src/templates/next-pagebuilder-cache/components/page-builder/components/description.tsx +17 -0
  306. package/src/templates/next-pagebuilder-cache/components/page-builder/components/gated-content/index.tsx +169 -0
  307. package/src/templates/next-pagebuilder-cache/components/page-builder/components/hero.tsx +17 -0
  308. package/src/templates/next-pagebuilder-cache/components/page-builder/components/rich-text-block.tsx +16 -0
  309. package/src/templates/next-pagebuilder-cache/components/page-builder/renderer.tsx +71 -0
  310. package/src/templates/next-pagebuilder-cache/components/page-builder/types.ts +25 -0
  311. package/src/templates/next-pagebuilder-cache/components/page-document/index.tsx +147 -0
  312. package/src/templates/next-pagebuilder-cache/components/ui/image/index.tsx +216 -0
  313. package/src/templates/next-pagebuilder-cache/components/ui/link/index.tsx +152 -0
  314. package/src/templates/next-pagebuilder-cache/components.json +24 -0
  315. package/src/templates/next-pagebuilder-cache/knip.json +35 -0
  316. package/src/templates/next-pagebuilder-cache/lefthook.yml +25 -0
  317. package/src/templates/next-pagebuilder-cache/lib/integrations/sanity/sanity.types.ts +1588 -0
  318. package/src/templates/next-pagebuilder-cache/lib/scripts/setup-ci-secrets.ts +117 -0
  319. package/src/templates/next-pagebuilder-cache/lib/styles/cn.ts +5 -0
  320. package/src/templates/next-pagebuilder-cache/lib/styles/global.css +69 -0
  321. package/src/templates/next-pagebuilder-cache/lib/utils/base-url.ts +17 -0
  322. package/src/templates/next-pagebuilder-cache/lib/utils/format-date.ts +8 -0
  323. package/src/templates/next-pagebuilder-cache/lib/utils/slug-tag.ts +6 -0
  324. package/src/templates/next-pagebuilder-cache/next.config.ts +176 -0
  325. package/src/templates/next-pagebuilder-cache/package.json +62 -0
  326. package/src/templates/next-pagebuilder-cache/postcss.config.mjs +65 -0
  327. package/src/templates/next-pagebuilder-cache/svg.d.ts +6 -0
  328. package/src/templates/next-pagebuilder-cache/tsconfig.json +38 -0
  329. package/src/templates/next-webgl/.env.example +4 -2
  330. package/src/templates/next-webgl/.github/workflows/ci.yml +1 -1
  331. package/src/templates/next-webgl/.vscode/settings.json +0 -2
  332. package/src/templates/next-webgl/app/robots.ts +2 -3
  333. package/src/templates/next-webgl/components/ui/image/index.tsx +2 -4
  334. package/src/templates/next-webgl/knip.json +1 -1
  335. package/src/templates/next-webgl/lefthook.yml +1 -1
  336. package/src/templates/next-webgl/lib/utils/README.md +18 -5
  337. package/src/templates/next-webgl/lib/utils/image-sizes.ts +72 -0
  338. package/src/templates/next-webgl/lib/utils/merge-refs.ts +56 -0
  339. package/src/templates/next-webgl/lib/utils/strings.ts +1 -51
  340. package/src/templates/next-webgl/next.config.ts +38 -5
  341. package/src/templates/next-webgl/package.json +11 -14
  342. package/src/templates/next-experiments/tsconfig.tsbuildinfo +0 -1
  343. package/src/templates/next-pagebuilder/app/actions/refresh.ts +0 -5
  344. package/src/templates/next-webgl/tsconfig.tsbuildinfo +0 -1
@@ -22,38 +22,35 @@
22
22
  "lint:fix": "biome check --write --unsafe --max-diagnostics=200",
23
23
  "prepare": "lefthook install",
24
24
  "start": "next start",
25
- "typecheck": "tsgo --noEmit",
26
- "typecheck:compare": "bun run typecheck:tsc && bun run typecheck",
27
- "typecheck:tsc": "tsc --noEmit --incremental false",
25
+ "typecheck": "tsc --noEmit",
28
26
  "update:packages": "cross-env NPM_CHECK_INSTALLER=bun bunx npm-check -u"
29
27
  },
30
28
  "dependencies": {
31
29
  "class-variance-authority": "^0.7.1",
32
30
  "clsx": "^2.1.1",
33
- "next": "^16",
34
- "react": "^19",
35
- "react-dom": "^19",
31
+ "next": "^16.3.0",
32
+ "react": "^19.2.8",
33
+ "react-dom": "^19.2.8",
36
34
  "tailwind-merge": "^3.4.0",
37
35
  "zod": "^4.3.6"
38
36
  },
39
37
  "devDependencies": {
40
38
  "@biomejs/biome": "^2.4.14",
41
- "@next/bundle-analyzer": "^16",
39
+ "@next/bundle-analyzer": "^16.3.0",
42
40
  "@svgr/webpack": "^8.1.0",
43
- "@tailwindcss/postcss": "^4",
41
+ "@tailwindcss/postcss": "^4.2.2",
44
42
  "@types/bun": "^1.3.6",
45
43
  "@types/node": "^24.13.1",
46
- "@types/react": "^19.2.9",
44
+ "@types/react": "^19.2.14",
47
45
  "@types/react-dom": "^19.2.3",
48
- "@typescript/native-preview": "^7.0.0-dev.20260122.3",
49
46
  "babel-plugin-react-compiler": "^1.0.0",
50
47
  "cross-env": "^10.1.0",
51
48
  "knip": "^6.16.1",
52
49
  "lefthook": "^1.13.0",
53
50
  "postcss-preset-env": "^10.6.1",
54
51
  "schema-dts": "^2.0.0",
55
- "tailwindcss": "^4",
56
- "typescript": "^5"
52
+ "tailwindcss": "^4.2.2",
53
+ "typescript": "^7.0.2"
57
54
  },
58
55
  "trustedDependencies": [
59
56
  "@biomejs/biome",
@@ -32,6 +32,8 @@
32
32
  # Base URL for your site (used for SEO, canonical URLs, etc.)
33
33
  # Development: http://localhost:3000
34
34
  # Production: https://your-domain.com
35
+ # Inlined at build time — scope this to the Production environment only in
36
+ # Vercel so Preview deployments fall through to VERCEL_PROJECT_PRODUCTION_URL.
35
37
  NEXT_PUBLIC_BASE_URL=""
36
38
 
37
39
  # Draft mode secret for preview functionality
@@ -54,8 +56,8 @@ NEXT_PUBLIC_SANITY_API_READ_TOKEN=""
54
56
  # Optional - for server-side mutations and visual editing
55
57
  # SANITY_PRIVATE_TOKEN=""
56
58
 
57
- # Optional - API version (defaults to 2024-03-15)
58
- # NEXT_PUBLIC_SANITY_API_VERSION="2024-03-15"
59
+ # Optional - API version (defaults to 2025-02-19, the floor for Content Releases)
60
+ # NEXT_PUBLIC_SANITY_API_VERSION="2025-02-19"
59
61
 
60
62
 
61
63
  # ============================================
@@ -41,7 +41,7 @@ jobs:
41
41
  run: bun install --frozen-lockfile
42
42
 
43
43
  - name: Type check
44
- run: bun run typecheck:compare
44
+ run: bun run typecheck
45
45
 
46
46
  - name: Lint
47
47
  run: bun run lint
@@ -58,7 +58,6 @@
58
58
 
59
59
  // General
60
60
  "files.eol": "\n",
61
- // "typescript.experimental.useTsgo": true, // Disabled: tsgo doesn't support Next.js plugin
62
61
  "javascript.suggest.autoImports": true,
63
62
  // Disable Prettier to prevent silent formatter conflicts
64
63
  "prettier.enable": false,
@@ -90,7 +89,6 @@
90
89
 
91
90
  // TypeScript
92
91
  "typescript.suggest.autoImports": true,
93
- "typescript.tsdk": "node_modules/typescript/lib",
94
92
 
95
93
  // Editor labels (fixed for .tsx files)
96
94
  "workbench.editor.customLabels.patterns": {
@@ -62,7 +62,7 @@ const SCRIPTS = [
62
62
  { cmd: "bun dev:https", desc: "Start with HTTPS" },
63
63
  { cmd: "bun build", desc: "Build for production" },
64
64
  { cmd: "bun lint:fix", desc: "Fix lint issues" },
65
- { cmd: "bun typecheck", desc: "Type check with tsgo" },
65
+ { cmd: "bun typecheck", desc: "Type check with tsc" },
66
66
  ]
67
67
 
68
68
  export default function Home() {
@@ -1,6 +1,5 @@
1
1
  import type { MetadataRoute } from "next"
2
-
3
- const APP_BASE_URL = process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:3000"
2
+ import { getBaseUrl } from "@/lib/utils/url"
4
3
 
5
4
  export default function robots(): MetadataRoute.Robots {
6
5
  return {
@@ -9,6 +8,6 @@ export default function robots(): MetadataRoute.Robots {
9
8
  allow: "/",
10
9
  disallow: [],
11
10
  },
12
- sitemap: `${APP_BASE_URL}/sitemap.xml`,
11
+ sitemap: `${getBaseUrl()}/sitemap.xml`,
13
12
  }
14
13
  }
@@ -9,8 +9,7 @@
9
9
  import cn from "clsx"
10
10
  import NextImage, { type ImageProps as NextImageProps } from "next/image"
11
11
  import type { CSSProperties, Ref } from "react"
12
-
13
- const DESKTOP_BREAKPOINT = 1440
12
+ import { buildImageSizes } from "@/lib/utils/image-sizes"
14
13
 
15
14
  /**
16
15
  * Enhanced Image component props extending Next.js Image.
@@ -167,8 +166,7 @@ export function Image({
167
166
  }: ImageProps) {
168
167
  // Generate responsive sizes if not provided
169
168
  const finalSizes =
170
- sizes ||
171
- `(max-width: ${DESKTOP_BREAKPOINT}px) ${mobileSize}, ${desktopSize}`
169
+ sizes || buildImageSizes({ base: mobileSize, desktop: desktopSize })
172
170
 
173
171
  // Early return after hooks
174
172
  if (!src) return null
@@ -20,5 +20,5 @@
20
20
  "zustand"
21
21
  ],
22
22
  "ignoreBinaries": ["op", "gh", "vercel"],
23
- "ignoreFiles": [".agents/**", ".claude/**", "hooks/**"]
23
+ "ignoreFiles": [".agents/**", ".claude/**", "hooks/**", "lib/integrations/hubspot/**"]
24
24
  }
@@ -11,7 +11,7 @@ pre-commit:
11
11
  stage_fixed: true
12
12
  typecheck:
13
13
  glob: '*.{ts,tsx}'
14
- run: bunx tsgo --noEmit --incremental false
14
+ run: bunx tsc --noEmit --incremental false
15
15
  stage_fixed: false
16
16
 
17
17
  post-merge:
@@ -5,10 +5,12 @@ Pure utility functions organized by concern.
5
5
  Use explicit imports for clarity and better tree-shaking:
6
6
 
7
7
  ```tsx
8
- import { clamp, lerp, mapRange } from '@/utils/math'
9
- import { slugify } from '@/utils/strings'
10
- import { generatePageMetadata } from '@/utils/metadata'
11
- import { tovw, torem, toem } from '@/utils/viewport'
8
+ import { clamp, lerp, mapRange } from '@/lib/utils/math'
9
+ import { slugify } from '@/lib/utils/strings'
10
+ import { generatePageMetadata } from '@/lib/utils/metadata'
11
+ import { tovw, torem, toem } from '@/lib/utils/viewport'
12
+ import { buildImageSizes, buildGridImageSizes } from '@/lib/utils/image-sizes'
13
+ import { useMergeRefs } from '@/lib/utils/merge-refs'
12
14
  ```
13
15
 
14
16
  ## Modules
@@ -18,8 +20,10 @@ import { tovw, torem, toem } from '@/utils/viewport'
18
20
  | `math` | `clamp`, `lerp`, `mapRange`, `modulo`, `normalize`, `distance` |
19
21
  | `easings` | All easing functions (`easeOutCubic`, etc.) |
20
22
  | `viewport` | `tovw`, `torem`, `toem` |
21
- | `strings` | `slugify`, `mergeRefs`, `capitalizeFirstLetter` |
23
+ | `strings` | `slugify`, `capitalizeFirstLetter`, `twoDigits`, `numberWithCommas` |
22
24
  | `metadata` | `generatePageMetadata` |
25
+ | `image-sizes` | `buildImageSizes`, `buildGridImageSizes` |
26
+ | `merge-refs` | `mergeRefs`, `useMergeRefs`, `assignRef` |
23
27
 
24
28
  ## Common Patterns
25
29
 
@@ -37,4 +41,13 @@ toem(16, 14) // → "1.14em"
37
41
 
38
42
  // SEO
39
43
  export const metadata = generatePageMetadata({ title: 'About' })
44
+
45
+ // next/image sizes — keys mirror the element's Tailwind variants
46
+ buildImageSizes({ base: '100vw', desktop: '796px' })
47
+ buildGridImageSizes({ base: 1, tablet: 2, desktop: 3 })
48
+
49
+ // Refs — in a component, use the hook: an inline mergeRefs([...]) is a new
50
+ // callback each render, so React detaches and reattaches every ref.
51
+ const mergedRef = useMergeRefs([localRef, ref])
52
+ return <input ref={mergedRef} />
40
53
  ```
@@ -0,0 +1,72 @@
1
+ /** Mirrors the theme's `--breakpoint-*` scale, which clears Tailwind's defaults. */
2
+ export const BASEMENT_BREAKPOINTS = {
3
+ mobile: 640,
4
+ tablet: 768,
5
+ "tablet-lg": 1024,
6
+ desktop: 1440,
7
+ "desktop-large": 1920,
8
+ } as const
9
+
10
+ export type BreakpointMap = Readonly<Record<string, number>>
11
+
12
+ /** Mirrors the element's variants: `grid-cols-1 tablet:grid-cols-2` → `{ base: 1, tablet: 2 }`. */
13
+ export type ByBreakpoint<
14
+ T,
15
+ B extends BreakpointMap = typeof BASEMENT_BREAKPOINTS,
16
+ > = { base: T } & Partial<Record<Extract<keyof B, string>, T>>
17
+
18
+ function columnWidth(columns: number): string {
19
+ return `${Math.round(100 / columns)}vw`
20
+ }
21
+
22
+ /**
23
+ * Widest first, since `sizes` takes the first matching condition and the last
24
+ * entry must be bare. `min-width` matches the Tailwind variant's boundary; a
25
+ * `max-width: <next> - 1` one would leave a fractional-pixel gap.
26
+ */
27
+ function toSizes(
28
+ base: string,
29
+ overrides: Readonly<Record<string, string | undefined>>,
30
+ scale: BreakpointMap
31
+ ): string {
32
+ const conditions: string[] = []
33
+
34
+ for (const [name, min] of Object.entries(scale).sort(
35
+ ([, a], [, b]) => b - a
36
+ )) {
37
+ const value = overrides[name]
38
+ if (value !== undefined) {
39
+ conditions.push(`(min-width: ${min}px) ${value}`)
40
+ }
41
+ }
42
+
43
+ return [...conditions, base].join(", ")
44
+ }
45
+
46
+ /** Builds `sizes` from the CSS length the image renders at per breakpoint. */
47
+ export function buildImageSizes<
48
+ B extends BreakpointMap = typeof BASEMENT_BREAKPOINTS,
49
+ >(widths: ByBreakpoint<string, B>, breakpoints?: B): string {
50
+ const { base, ...overrides } = widths
51
+ return toSizes(base, overrides, breakpoints ?? BASEMENT_BREAKPOINTS)
52
+ }
53
+
54
+ /**
55
+ * Builds `sizes` from a grid's column count per breakpoint. Rounded: `sizes`
56
+ * only steers which srcset candidate the browser picks, and gaps make the real
57
+ * column narrower than `100 / N` anyway.
58
+ */
59
+ export function buildGridImageSizes<
60
+ B extends BreakpointMap = typeof BASEMENT_BREAKPOINTS,
61
+ >(columns: ByBreakpoint<number, B>, breakpoints?: B): string {
62
+ const { base, ...overrides } = columns
63
+ const widths: Record<string, string> = {}
64
+
65
+ for (const [name, count] of Object.entries(overrides)) {
66
+ if (count !== undefined) {
67
+ widths[name] = columnWidth(count)
68
+ }
69
+ }
70
+
71
+ return toSizes(columnWidth(base), widths, breakpoints ?? BASEMENT_BREAKPOINTS)
72
+ }
@@ -0,0 +1,56 @@
1
+ import { type Ref, type RefCallback, useRef } from "react"
2
+
3
+ /** Returns the ref's own cleanup callback when it provides one (React 19). */
4
+ export function assignRef<T>(
5
+ ref: Ref<T> | undefined,
6
+ value: T | null
7
+ ): ReturnType<RefCallback<T>> {
8
+ if (typeof ref === "function") {
9
+ return ref(value)
10
+ }
11
+ if (ref) {
12
+ ref.current = value
13
+ }
14
+ return undefined
15
+ }
16
+
17
+ /** Merges object/callback refs into one callback ref, with React 19 ref cleanup. */
18
+ export function mergeRefs<T>(refs: (Ref<T> | undefined)[]): RefCallback<T> {
19
+ return (value) => {
20
+ const cleanups = refs.map((ref) => {
21
+ const cleanup = assignRef(ref, value)
22
+ return typeof cleanup === "function"
23
+ ? cleanup
24
+ : () => assignRef(ref, null)
25
+ })
26
+ return () => {
27
+ for (const cleanup of cleanups) {
28
+ cleanup()
29
+ }
30
+ }
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Memoized {@link mergeRefs}, stable across renders for the same ref list.
36
+ * Compares the list here rather than passing it as `useMemo` deps: React only
37
+ * compares the common prefix, so a list that grows or shrinks would silently
38
+ * reuse the old callback and leave a ref unattached or stale.
39
+ */
40
+ export function useMergeRefs<T>(refs: (Ref<T> | undefined)[]): RefCallback<T> {
41
+ const cache = useRef<{
42
+ refs: (Ref<T> | undefined)[]
43
+ merged: RefCallback<T>
44
+ } | null>(null)
45
+
46
+ if (
47
+ !cache.current ||
48
+ cache.current.refs.length !== refs.length ||
49
+ cache.current.refs.some((ref, i) => ref !== refs[i])
50
+ ) {
51
+ // Snapshot: a caller may hand us a persistent array it mutates in place.
52
+ cache.current = { refs: [...refs], merged: mergeRefs(refs) }
53
+ }
54
+
55
+ return cache.current.merged
56
+ }
@@ -1,11 +1,9 @@
1
1
  /**
2
2
  * String & Object Utilities
3
3
  *
4
- * Helper functions for string manipulation, object operations, and refs.
4
+ * Helper functions for string manipulation and object operations.
5
5
  */
6
6
 
7
- import type { Ref } from "react"
8
-
9
7
  // =============================================================================
10
8
  // STRING UTILITIES
11
9
  // =============================================================================
@@ -196,51 +194,3 @@ export function iterableObject(
196
194
  return value
197
195
  })
198
196
  }
199
-
200
- // =============================================================================
201
- // REF UTILITIES
202
- // =============================================================================
203
-
204
- /**
205
- * Merges multiple React refs into a single ref callback.
206
- *
207
- * Useful when you need to assign multiple refs to the same element,
208
- * such as combining a local ref with a forwarded ref.
209
- *
210
- * @param refs - Refs to merge (functions and ref objects supported)
211
- * @returns A ref callback that updates all provided refs
212
- *
213
- * @example
214
- * ```tsx
215
- * // Component that needs both local and forwarded ref
216
- * function MyComponent({ ref }: { ref: React.Ref<HTMLDivElement> }) {
217
- * const localRef = useRef<HTMLDivElement>(null)
218
- * const mergedRef = mergeRefs(localRef, ref)
219
- *
220
- * return <div ref={mergedRef}>Content</div>
221
- * }
222
- *
223
- * // Forward ref pattern
224
- * const ForwardedComponent = forwardRef(MyComponent)
225
- * ```
226
- */
227
- export function mergeRefs<T>(...refs: (Ref<T> | undefined)[]): Ref<T> {
228
- return (value) => {
229
- const cleanups = refs.reduce<VoidFunction[]>((accumulator, ref) => {
230
- if (typeof ref === "function") {
231
- const cleanup = ref(value)
232
- if (typeof cleanup === "function") {
233
- accumulator.push(cleanup)
234
- }
235
- } else if (ref) {
236
- ref.current = value
237
- }
238
-
239
- return accumulator
240
- }, [])
241
-
242
- return () => {
243
- for (const cleanup of cleanups) cleanup()
244
- }
245
- }
246
- }
@@ -5,9 +5,8 @@ const nextConfig: NextConfig = {
5
5
  reactStrictMode: true,
6
6
  reactCompiler: true,
7
7
  typedRoutes: true,
8
- // Bundle the committed encrypted env files into serverless functions so
9
- // instrumentation.ts can decrypt them at runtime. .env.keys is never listed
10
- // (and is git/vercel-ignored); missing files are skipped.
8
+ // Bundle the committed encrypted env files so instrumentation.ts can decrypt
9
+ // them at runtime. `.env.keys` is never listed; missing files are skipped.
11
10
  outputFileTracingIncludes: {
12
11
  "/**": ["./.env", "./.env.production", "./.env.preview"],
13
12
  },
@@ -64,8 +63,10 @@ const nextConfig: NextConfig = {
64
63
  },
65
64
  },
66
65
  },
66
+ logging: {
67
+ browserToTerminal: true,
68
+ },
67
69
  experimental: {
68
- browserDebugInfoInTerminal: true,
69
70
  optimizePackageImports: [
70
71
  "@react-three/drei",
71
72
  "@react-three/fiber",
@@ -86,7 +87,8 @@ const nextConfig: NextConfig = {
86
87
  },
87
88
  headers: async () => [
88
89
  {
89
- source: "/(.*)",
90
+ // Excludes /studio* — must stay mutually exclusive with the rule below.
91
+ source: "/((?!studio(?:/|$)).*)",
90
92
  headers: [
91
93
  {
92
94
  key: "X-Content-Type-Options",
@@ -118,6 +120,37 @@ const nextConfig: NextConfig = {
118
120
  },
119
121
  ],
120
122
  },
123
+ {
124
+ // X-Frame-Options can't allow a third-party origin, and Next can't unset
125
+ // it from another rule — the iframe needs it absent, not overridden.
126
+ source: "/studio/:path*",
127
+ headers: [
128
+ {
129
+ key: "X-Content-Type-Options",
130
+ value: "nosniff",
131
+ },
132
+ {
133
+ key: "Content-Security-Policy",
134
+ value: "frame-ancestors 'self' https://*.sanity.io;",
135
+ },
136
+ {
137
+ key: "X-XSS-Protection",
138
+ value: "1; mode=block",
139
+ },
140
+ {
141
+ key: "X-DNS-Prefetch-Control",
142
+ value: "on",
143
+ },
144
+ {
145
+ key: "Strict-Transport-Security",
146
+ value: "max-age=63072000; includeSubDomains; preload",
147
+ },
148
+ {
149
+ key: "Permissions-Policy",
150
+ value: "camera=(), microphone=(), geolocation=()",
151
+ },
152
+ ],
153
+ },
121
154
  ],
122
155
  redirects: async () => [
123
156
  {
@@ -22,40 +22,37 @@
22
22
  "lint:fix": "biome check --write --unsafe --max-diagnostics=200",
23
23
  "prepare": "lefthook install",
24
24
  "start": "next start",
25
- "typecheck": "tsgo --noEmit",
26
- "typecheck:compare": "bun run typecheck:tsc && bun run typecheck",
27
- "typecheck:tsc": "tsc --noEmit --incremental false"
25
+ "typecheck": "tsc --noEmit"
28
26
  },
29
27
  "dependencies": {
30
28
  "@radix-ui/react-navigation-menu": "^1.2.5",
31
29
  "class-variance-authority": "^0.7.1",
32
30
  "clsx": "^2.1.1",
33
31
  "lucide-react": "^0.474.0",
34
- "next": "^16",
35
- "react": "^19",
36
- "react-dom": "^19",
32
+ "next": "^16.3.0",
33
+ "react": "^19.2.8",
34
+ "react-dom": "^19.2.8",
37
35
  "tailwind-merge": "^3.4.0",
38
36
  "three": "^0.182.0",
39
37
  "zod": "^4.3.6"
40
38
  },
41
39
  "devDependencies": {
42
40
  "@biomejs/biome": "^2.4.14",
43
- "@next/bundle-analyzer": "^16",
41
+ "@next/bundle-analyzer": "^16.3.0",
44
42
  "@svgr/webpack": "^8.1.0",
45
- "@tailwindcss/postcss": "^4",
43
+ "@tailwindcss/postcss": "^4.2.2",
46
44
  "@types/bun": "^1.3.6",
47
45
  "@types/node": "^24.13.1",
48
- "@types/react": "^19.2.9",
46
+ "@types/react": "^19.2.14",
49
47
  "@types/react-dom": "^19.2.3",
50
- "@typescript/native-preview": "^7.0.0-dev.20260122.3",
51
48
  "babel-plugin-react-compiler": "^1.0.0",
52
49
  "cross-env": "^10.1.0",
53
50
  "knip": "^6.16.1",
54
51
  "lefthook": "^1.13.0",
55
52
  "postcss-preset-env": "^10.6.1",
56
53
  "schema-dts": "^2.0.0",
57
- "tailwindcss": "^4",
58
- "typescript": "^5"
54
+ "tailwindcss": "^4.2.2",
55
+ "typescript": "^7.0.2"
59
56
  },
60
57
  "trustedDependencies": [
61
58
  "@biomejs/biome",
@@ -25,6 +25,8 @@
25
25
  # bsmnt:endif
26
26
 
27
27
  # Base URL
28
+ # Inlined at build time — scope this to the Production environment only in
29
+ # Vercel so Preview deployments fall through to VERCEL_PROJECT_PRODUCTION_URL.
28
30
  NEXT_PUBLIC_BASE_URL=
29
31
 
30
32
  # Draft Mode
@@ -33,5 +35,6 @@ DRAFT_MODE_TOKEN=
33
35
  # Sanity CMS
34
36
  NEXT_PUBLIC_SANITY_PROJECT_ID=
35
37
  NEXT_PUBLIC_SANITY_DATASET=production
38
+ NEXT_PUBLIC_SANITY_API_VERSION=2025-02-19
36
39
  SANITY_API_READ_TOKEN=
37
40
  SANITY_REVALIDATE_SECRET=
@@ -41,7 +41,7 @@ jobs:
41
41
  run: bun install --frozen-lockfile
42
42
 
43
43
  - name: Type check
44
- run: bun run typecheck:compare
44
+ run: bun run typecheck
45
45
 
46
46
  - name: Lint
47
47
  run: bun run lint
@@ -58,7 +58,6 @@
58
58
 
59
59
  // General
60
60
  "files.eol": "\n",
61
- // "typescript.experimental.useTsgo": true, // Disabled: tsgo doesn't support Next.js plugin
62
61
  "javascript.suggest.autoImports": true,
63
62
  // Disable Prettier to prevent silent formatter conflicts
64
63
  "prettier.enable": false,
@@ -90,7 +89,6 @@
90
89
 
91
90
  // TypeScript
92
91
  "typescript.suggest.autoImports": true,
93
- "typescript.tsdk": "node_modules/typescript/lib",
94
92
 
95
93
  // Editor labels (fixed for .tsx files)
96
94
  "workbench.editor.customLabels.patterns": {
@@ -64,6 +64,101 @@ Copy `.env.example` to `.env.local` and fill in the values you need. `.env.local
64
64
  For deploys, set the same variables in your host's dashboard (e.g. Vercel → Settings → Environment Variables).
65
65
 
66
66
  <!-- bsmnt:endif -->
67
+ ## Sanity Dashboard (Studio in an iframe)
68
+
69
+ The Studio at `/studio` can be embedded in the Sanity Dashboard
70
+ (`https://www.sanity.io/@<org>/studio/<appId>/<workspace>`), which loads it in an
71
+ iframe from `www.sanity.io`. Three things have to be true for that to work:
72
+
73
+ 1. **Headers.** `next.config.ts` already exempts `/studio/:path*` from the
74
+ site-wide `X-Frame-Options` / `frame-ancestors 'self'` headers, sending
75
+ `frame-ancestors 'self' https://*.sanity.io` there instead and dropping
76
+ `X-Frame-Options` entirely for that path (it has no "allow this origin"
77
+ form — it must be absent, not overridden). Nothing to do here unless
78
+ you've hand-edited `headers()`.
79
+ 2. **Bridge script.** Already wired in: `next-sanity/studio`'s `NextStudio`
80
+ re-exports `NextStudioWithBridge`, which renders the
81
+ `core.sanity-cdn.com/bridge.js` tag the Dashboard needs. Don't hand-roll
82
+ that script tag, and don't switch the Studio route to
83
+ `next-sanity/studio/client-component` (the bridge-less variant).
84
+ 3. **Registered URL.** In [Sanity Manage](https://sanity.io/manage), the
85
+ Studio application's URL must be the **full path**
86
+ (`https://example.com/studio`), not just the domain. This can't be set
87
+ from code — either click through Manage, or run
88
+ `sanity deploy --external --url https://example.com/studio --yes` from
89
+ `lib/integrations/sanity`. Writing it via the API needs the
90
+ `sanity.project.deployStudio` grant, which read/write tokens don't have.
91
+
92
+ Verify against a production build (`bun build && bun start`), not dev. One trap:
93
+ the Dashboard reports `Failed to load iframe due to X-Frame-Options: SAMEORIGIN`
94
+ for *any* framing failure, including a wrong registered URL with already-correct
95
+ headers. Trust the browser console's `Framing '<url>' violates …` line instead —
96
+ it names the URL actually being framed.
97
+
98
+ ## Content invalidation (publish webhook) — required
99
+
100
+ Cached content is cleared on publish by `app/api/revalidate/route.ts`, which only
101
+ runs if a Sanity webhook calls it. **Until you create that webhook, published
102
+ changes won't appear on the deployed site until its cache entries expire.**
103
+
104
+ `SANITY_REVALIDATE_SECRET` is generated for you during `bsmnt create` (see your
105
+ env file). The webhook itself is manual, because the deployed URL isn't known at
106
+ scaffold time. Once deployed, in **sanity.io/manage → API → Webhooks → Create**:
107
+
108
+ | Field | Value |
109
+ | --- | --- |
110
+ | URL | `https://<your-domain>/api/revalidate` |
111
+ | Dataset | `production` |
112
+ | Trigger on | Create, Update, Delete |
113
+ | Filter | leave empty (the route decides what to invalidate) |
114
+ | Projection | see below — the route needs `operation` and `before()` |
115
+ | HTTP method | `POST` |
116
+ | API version | `v2021-03-25` or later |
117
+ | Secret | the `SANITY_REVALIDATE_SECRET` value from your env |
118
+
119
+ The projection is not optional: the route reads `operation` and the pre-delete
120
+ document to invalidate a removed page, and neither is in the default payload.
121
+
122
+ ```groq
123
+ {
124
+ "_id": _id,
125
+ "_type": _type,
126
+ "operation": delta::operation(),
127
+ "before": before(),
128
+ "after": after()
129
+ }
130
+ ```
131
+
132
+ Make sure the same secret is set in your hosting provider's environment, not just
133
+ locally. To check it end to end: publish a change in the Studio, then reload the
134
+ affected page — it should update without a redeploy. Close any other tab on the
135
+ site first: in draft mode `<SanityLive>` refreshes the router on its own, which can
136
+ make a broken webhook look like it works.
137
+
138
+ **sanity.io/manage → API → Webhooks → your webhook → Delivery log** is where to
139
+ check. A 401 means the secret doesn't match; repeated 5xx means the route is
140
+ erroring; no attempts at all means the filter or dataset is wrong.
141
+
142
+ ### Why every route here is cached
143
+
144
+ `sanityFetch` from `next-sanity`'s `defineLive` issues **two** Sanity requests per
145
+ call: an uncached probe for the Live API's sync tags, then the real query with
146
+ `next: { revalidate: false, tags }`. Only the second is cached, so a route that
147
+ renders per request pays a Sanity round trip on every hit no matter what the Data
148
+ Cache holds.
149
+
150
+ That is why nothing in this template sets `dynamic = "force-dynamic"` — including
151
+ `/sitemap.xml`, `/sitemap.md` and `/feed.xml`, which are crawler-driven and used
152
+ to. With the route output cached, the probe runs when a route revalidates rather
153
+ than when someone visits it.
154
+
155
+ The exception is server actions, which by definition run per invocation:
156
+ `unlockGatedContent` costs two Sanity requests per form submission. If you add
157
+ actions that read Sanity on a hot path, read through `client.fetch` directly
158
+ rather than `sanityFetch` — but note that `client.fetch` does not resolve the
159
+ draft-mode perspective from cookies, so an editor previewing drafts would see
160
+ published content unless you thread that through yourself.
161
+
67
162
  ## Continuous Integration
68
163
 
69
164
  A GitHub Actions workflow (`.github/workflows/ci.yml`) runs on every pull request to `main` and on pushes to `main`. It runs type checking, linting, the production build, tests, and `knip`.