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
@@ -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
+ }
@@ -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
  },
@@ -68,7 +67,6 @@ const nextConfig: NextConfig = {
68
67
  browserToTerminal: true,
69
68
  },
70
69
  experimental: {
71
- prefetchInlining: true,
72
70
  optimizePackageImports: [
73
71
  "@react-three/drei",
74
72
  "@react-three/fiber",
@@ -94,7 +92,8 @@ const nextConfig: NextConfig = {
94
92
  },
95
93
  headers: async () => [
96
94
  {
97
- source: "/(.*)",
95
+ // Excludes /studio* — must stay mutually exclusive with the rule below.
96
+ source: "/((?!studio(?:/|$)).*)",
98
97
  headers: [
99
98
  {
100
99
  key: "X-Content-Type-Options",
@@ -126,6 +125,37 @@ const nextConfig: NextConfig = {
126
125
  },
127
126
  ],
128
127
  },
128
+ {
129
+ // X-Frame-Options can't allow a third-party origin, and Next can't unset
130
+ // it from another rule — the iframe needs it absent, not overridden.
131
+ source: "/studio/:path*",
132
+ headers: [
133
+ {
134
+ key: "X-Content-Type-Options",
135
+ value: "nosniff",
136
+ },
137
+ {
138
+ key: "Content-Security-Policy",
139
+ value: "frame-ancestors 'self' https://*.sanity.io;",
140
+ },
141
+ {
142
+ key: "X-XSS-Protection",
143
+ value: "1; mode=block",
144
+ },
145
+ {
146
+ key: "X-DNS-Prefetch-Control",
147
+ value: "on",
148
+ },
149
+ {
150
+ key: "Strict-Transport-Security",
151
+ value: "max-age=63072000; includeSubDomains; preload",
152
+ },
153
+ {
154
+ key: "Permissions-Policy",
155
+ value: "camera=(), microphone=(), geolocation=()",
156
+ },
157
+ ],
158
+ },
129
159
  ],
130
160
  }
131
161
 
@@ -21,16 +21,14 @@
21
21
  "lint:fix": "biome check --write --unsafe --max-diagnostics=200",
22
22
  "prepare": "lefthook install",
23
23
  "start": "next start",
24
- "typecheck": "tsgo --noEmit",
25
- "typecheck:compare": "bun run typecheck:tsc && bun run typecheck",
26
- "typecheck:tsc": "tsc --noEmit --incremental false",
24
+ "typecheck": "tsc --noEmit",
27
25
  "update:packages": "cross-env NPM_CHECK_INSTALLER=bun bunx npm-check -u"
28
26
  },
29
27
  "dependencies": {
30
28
  "class-variance-authority": "^0.7.1",
31
- "next": "^16",
32
- "react": "^19",
33
- "react-dom": "^19",
29
+ "next": "^16.3.0",
30
+ "react": "^19.2.8",
31
+ "react-dom": "^19.2.8",
34
32
  "react-use": "^17.6.0",
35
33
  "tailwind-merge": "^3.5.0",
36
34
  "zod": "^4.3.6"
@@ -38,14 +36,13 @@
38
36
  "devDependencies": {
39
37
  "@biomejs/biome": "^2.4.14",
40
38
  "@csstools/postcss-global-data": "^4.0.0",
41
- "@next/bundle-analyzer": "^16.2.1",
39
+ "@next/bundle-analyzer": "^16.3.0",
42
40
  "@svgr/webpack": "^8.1.0",
43
41
  "@tailwindcss/postcss": "^4.2.2",
44
42
  "@types/bun": "^1.3.11",
45
43
  "@types/node": "^24.13.1",
46
44
  "@types/react": "^19.2.14",
47
45
  "@types/react-dom": "^19.2.3",
48
- "@typescript/native-preview": "^7.0.0-dev.20260323.1",
49
46
  "babel-plugin-react-compiler": "^1.0.0",
50
47
  "cross-env": "^10.1.0",
51
48
  "knip": "^6.16.1",
@@ -53,12 +50,13 @@
53
50
  "postcss-functions": "^4.0.2",
54
51
  "postcss-preset-env": "^11.2.0",
55
52
  "tailwindcss": "^4.2.2",
56
- "typescript": "^5.9.3"
53
+ "typescript": "^7.0.2"
57
54
  },
58
55
  "trustedDependencies": [
59
56
  "@biomejs/biome",
60
57
  "@tailwindcss/oxide",
61
58
  "esbuild",
62
59
  "sharp"
63
- ]
60
+ ],
61
+ "browserslist": "extends @sanity/browserslist-config"
64
62
  }
@@ -0,0 +1,21 @@
1
+ ## Plugins
2
+
3
+ ### 1. `no-anchor-element.grit`
4
+ Enforces using Next.js `<Link>` component instead of HTML `<a>` elements.
5
+
6
+ ### 2. `no-unnecessary-forwardref.grit`
7
+ Checks for unnecessary `forwardRef` usage in React 19 with the compiler.
8
+
9
+ ### 3. `no-relative-parent-imports.grit`
10
+ Forbids relative parent imports (`../`) and encourages alias imports (`@/`).
11
+
12
+ ## Plugin Configuration
13
+
14
+ The plugins are configured in `biome.json`:
15
+ ```json
16
+ "plugins": [
17
+ "./biome-plugins/no-anchor-element.grit",
18
+ "./biome-plugins/no-unnecessary-forwardref.grit",
19
+ "./biome-plugins/no-relative-parent-imports.grit"
20
+ ]
21
+ ```
@@ -0,0 +1,12 @@
1
+ language js;
2
+
3
+ `<a $attrs>$content</a>` as $anchor where {
4
+ !$anchor <: within `if ($condition) { return ($jsx) }` where {
5
+ $condition <: contains or { `isExternal`, `isExternalSSR` }
6
+ },
7
+ register_diagnostic(
8
+ span = $anchor,
9
+ message = "Use custom Link component instead of <a> element. The Link component handles both internal and external links automatically.",
10
+ severity = "error"
11
+ )
12
+ }
@@ -0,0 +1,10 @@
1
+ language js;
2
+
3
+ `import $imports from $source` as $import where {
4
+ $source <: r"['\"]\.\.\/\.\.\/.*['\"]",
5
+ register_diagnostic(
6
+ span = $import,
7
+ message = "Use alias imports (~/dir/) instead of deep relative imports (../../). Single level imports (../) are allowed for colocated files.",
8
+ severity = "error"
9
+ )
10
+ }
@@ -0,0 +1,9 @@
1
+ language js;
2
+
3
+ `forwardRef($func)` as $ref where {
4
+ register_diagnostic(
5
+ span = $ref,
6
+ message = "forwardRef is unnecessary in React 19 with the compiler",
7
+ severity = "warning"
8
+ )
9
+ }
@@ -0,0 +1,40 @@
1
+ # ============================================
2
+ # Basement Next Starter - Environment Variables
3
+ # ============================================
4
+ # bsmnt:if dotenvx
5
+ # This project uses dotenvx (https://dotenvx.com): the committed `.env` holds
6
+ # shared values, with secrets encrypted there. The private keys live in
7
+ # `.env.keys` (gitignored) — restore them with `bun run env:setup`.
8
+ #
9
+ # Add or change a variable in `.env` (not .env.local):
10
+ # - Secret (API key, token, …): bun run env:set KEY value (--preview / --prod)
11
+ # - Trivial / NEXT_PUBLIC_*: bun run env:set KEY value --plain
12
+ # - Bulk-encrypt a file edited by hand: bun run env:encrypt-all (--preview / --prod)
13
+ #
14
+ # Edge runtime (middleware, `runtime = "edge"`): dotenvx can't decrypt there.
15
+ # Set any secret those read directly in Vercel's env vars, not here.
16
+ #
17
+ # This file just documents the available variables; real values live in `.env`.
18
+ # ============================================
19
+ # bsmnt:endif
20
+ # bsmnt:if !dotenvx
21
+ # Copy this file to `.env.local` and fill in the values you need.
22
+ # `.env.local` is gitignored, so real values stay off version control.
23
+ # Only NEXT_PUBLIC_* variables are exposed to the browser.
24
+ # ============================================
25
+ # bsmnt:endif
26
+
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.
30
+ NEXT_PUBLIC_BASE_URL=
31
+
32
+ # Draft Mode
33
+ DRAFT_MODE_TOKEN=
34
+
35
+ # Sanity CMS
36
+ NEXT_PUBLIC_SANITY_PROJECT_ID=
37
+ NEXT_PUBLIC_SANITY_DATASET=production
38
+ NEXT_PUBLIC_SANITY_API_VERSION=2025-02-19
39
+ SANITY_API_READ_TOKEN=
40
+ SANITY_REVALIDATE_SECRET=
@@ -0,0 +1,13 @@
1
+ ## Summary
2
+
3
+ <!-- Brief description of what this PR does -->
4
+
5
+ ## Changes
6
+
7
+ -
8
+
9
+ ## Checklist
10
+
11
+ - [ ] `bun lint` passes
12
+ - [ ] Tested locally in dev mode
13
+ - [ ] No breaking changes (or documented in summary)
@@ -0,0 +1,105 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ branches:
6
+ - main
7
+ push:
8
+ branches:
9
+ - main
10
+
11
+ jobs:
12
+ ci:
13
+ runs-on: ubuntu-latest
14
+
15
+ steps:
16
+ - name: Checkout
17
+ uses: actions/checkout@v4
18
+
19
+ # Node version is read from .nvmrc — the single source of truth.
20
+ - name: Setup Node
21
+ uses: actions/setup-node@v4
22
+ with:
23
+ node-version-file: ".nvmrc"
24
+
25
+ # Bun version is read from package.json's "packageManager" field — the
26
+ # single source of truth. Do NOT pin a version here, or the two can drift.
27
+ - name: Setup Bun
28
+ uses: oven-sh/setup-bun@v2
29
+
30
+ # Cache Bun's global install cache so dependencies aren't re-downloaded
31
+ # on every run. Keyed on the lockfile so it busts when deps change.
32
+ - name: Cache Bun dependencies
33
+ uses: actions/cache@v4
34
+ with:
35
+ path: ~/.bun/install/cache
36
+ key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
37
+ restore-keys: |
38
+ ${{ runner.os }}-bun-
39
+
40
+ - name: Install dependencies
41
+ run: bun install --frozen-lockfile
42
+
43
+ - name: Type check
44
+ run: bun run typecheck
45
+
46
+ - name: Lint
47
+ run: bun run lint
48
+
49
+ # bsmnt:if dotenvx
50
+ # dotenvx needs the private key to decrypt secrets at build time. It's
51
+ # pulled from 1Password via an org-wide service-account token + the op-ref
52
+ # committed in .env. Optional: builds pass without it (only plaintext /
53
+ # NEXT_PUBLIC_* is read), and a per-repo DOTENV_PRIVATE_KEY secret still
54
+ # works as a fallback.
55
+ - name: Install 1Password CLI
56
+ if: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN != '' }}
57
+ uses: 1password/install-cli-action@v1
58
+
59
+ - name: Build
60
+ env:
61
+ OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
62
+ DOTENV_PRIVATE_KEY: ${{ secrets.DOTENV_PRIVATE_KEY }}
63
+ # Sanity project ID and dataset are public (they ship in the client
64
+ # bundle), so they are stored as repository variables. The Sanity read
65
+ # token is secret — add it under Settings → Secrets and variables →
66
+ # Actions. All three are empty (and harmless) until Sanity is set up.
67
+ NEXT_PUBLIC_SANITY_PROJECT_ID: ${{ vars.NEXT_PUBLIC_SANITY_PROJECT_ID }}
68
+ NEXT_PUBLIC_SANITY_DATASET: ${{ vars.NEXT_PUBLIC_SANITY_DATASET }}
69
+ SANITY_API_READ_TOKEN: ${{ secrets.SANITY_API_READ_TOKEN }}
70
+ run: |
71
+ if [ -n "$OP_SERVICE_ACCOUNT_TOKEN" ]; then
72
+ ref="$(grep -m1 '^DOTENV_PRIVATE_KEY_OP_REF=' .env | cut -d= -f2-)"
73
+ if [ -n "$ref" ]; then
74
+ DOTENV_PRIVATE_KEY="$(op read "$ref")"
75
+ echo "::add-mask::$DOTENV_PRIVATE_KEY"
76
+ export DOTENV_PRIVATE_KEY
77
+ fi
78
+ fi
79
+ bun run build
80
+
81
+ # bsmnt:endif
82
+ # bsmnt:if !dotenvx
83
+ - name: Build
84
+ env:
85
+ # Sanity project ID and dataset are public (they ship in the client
86
+ # bundle), so they are stored as repository variables. The Sanity read
87
+ # token is secret — add it under Settings → Secrets and variables →
88
+ # Actions. All three are empty (and harmless) until Sanity is set up.
89
+ NEXT_PUBLIC_SANITY_PROJECT_ID: ${{ vars.NEXT_PUBLIC_SANITY_PROJECT_ID }}
90
+ NEXT_PUBLIC_SANITY_DATASET: ${{ vars.NEXT_PUBLIC_SANITY_DATASET }}
91
+ SANITY_API_READ_TOKEN: ${{ secrets.SANITY_API_READ_TOKEN }}
92
+ run: bun run build
93
+
94
+ # bsmnt:endif
95
+ # Runs your tests if any exist; a fresh project with none passes cleanly.
96
+ - name: Test
97
+ run: |
98
+ if git ls-files '*.test.*' '*.spec.*' '*_test.*' '*_spec.*' | grep -q .; then
99
+ bun test
100
+ else
101
+ echo "No test files found — skipping bun test."
102
+ fi
103
+
104
+ - name: Knip
105
+ run: bun run knip
@@ -0,0 +1,19 @@
1
+ {
2
+ "recommendations": [
3
+ // Formatting & Linting
4
+ "biomejs.biome",
5
+
6
+ // CSS & Styling
7
+ "bradlc.vscode-tailwindcss",
8
+ "csstools.postcss",
9
+
10
+ // Sanity CMS (GROQ syntax + validation)
11
+ "sanity-io.vscode-sanity",
12
+
13
+ // Env (dotenvx — view decrypted .env values inline)
14
+ "dotenv.dotenvx-vscode",
15
+
16
+ // DX Enhancements
17
+ "yoavbls.pretty-ts-errors"
18
+ ]
19
+ }
@@ -0,0 +1,103 @@
1
+ {
2
+ // Language-specific formatters (explicit per-language ensures no fallback to other formatters)
3
+ "[css]": {
4
+ "editor.defaultFormatter": "biomejs.biome"
5
+ },
6
+ "[javascript]": {
7
+ "editor.defaultFormatter": "biomejs.biome"
8
+ },
9
+ "[javascriptreact]": {
10
+ "editor.defaultFormatter": "biomejs.biome"
11
+ },
12
+ "[json]": {
13
+ "editor.defaultFormatter": "biomejs.biome"
14
+ },
15
+ "[jsonc]": {
16
+ "editor.defaultFormatter": "biomejs.biome"
17
+ },
18
+ "[postcss]": {
19
+ "editor.defaultFormatter": "biomejs.biome"
20
+ },
21
+ "[typescript]": {
22
+ "editor.defaultFormatter": "biomejs.biome"
23
+ },
24
+ "[typescriptreact]": {
25
+ "editor.defaultFormatter": "biomejs.biome"
26
+ },
27
+ "colorize.languages": [
28
+ "javascript",
29
+ "typescript",
30
+ "css",
31
+ "scss",
32
+ "less",
33
+ "html",
34
+ "json",
35
+ "yaml",
36
+ "markdown",
37
+ "glsl"
38
+ ],
39
+ "css.lint.unknownAtRules": "ignore",
40
+
41
+ // CSS (Tailwind v4)
42
+ "css.lint.validProperties": ["user-drag"],
43
+ "editor.codeActionsOnSave": {
44
+ "source.fixAll.biome": "always",
45
+ "source.organizeImports.biome": "always"
46
+ },
47
+ "editor.defaultFormatter": "biomejs.biome",
48
+
49
+ // Formatting
50
+ "editor.formatOnSave": true,
51
+ "editor.formatOnSaveMode": "file",
52
+
53
+ // File associations
54
+ "files.associations": {
55
+ "*.css": "css",
56
+ "*.json": "jsonc"
57
+ },
58
+
59
+ // General
60
+ "files.eol": "\n",
61
+ "javascript.suggest.autoImports": true,
62
+ // Disable Prettier to prevent silent formatter conflicts
63
+ "prettier.enable": false,
64
+
65
+ // Search exclusions (performance)
66
+ "search.exclude": {
67
+ "**/.next": true,
68
+ "**/.vercel": true,
69
+ "**/bun.lock": true,
70
+ "**/lib/integrations/sanity/sanity.types.ts": true,
71
+ "**/node_modules": true
72
+ },
73
+ "tailwindCSS.classAttributes": [
74
+ "class",
75
+ "className",
76
+ ".*ClassName",
77
+ ".*Styles"
78
+ ],
79
+ "tailwindCSS.classFunctions": ["cn", "cva", "twMerge", "cx"],
80
+ "tailwindCSS.experimental.classRegex": [
81
+ ["cn\\(([^)]*)\\)", "(?:'|\"|`)([^\"'`]*)(?:'|\"|`)"],
82
+ ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
83
+ ],
84
+ "tailwindCSS.experimental.configFile": "./lib/styles/css/tailwind.css",
85
+ "tailwindCSS.includeLanguages": {
86
+ "typescriptreact": "html"
87
+ },
88
+ "typescript.preferences.importModuleSpecifier": "non-relative",
89
+
90
+ // TypeScript
91
+ "typescript.suggest.autoImports": true,
92
+
93
+ // Editor labels (fixed for .tsx files)
94
+ "workbench.editor.customLabels.patterns": {
95
+ "**/app/**/layout.tsx": "${dirname(1)}/${dirname} <layout>",
96
+ "**/app/**/page.tsx": "${dirname(1)}/${dirname} <page>",
97
+ "**/app/api/**/route.ts": "${dirname(1)}/${dirname} <route>",
98
+ "**/components/**/index.tsx": "${dirname} <component>"
99
+ },
100
+ "workbench.editorAssociations": {
101
+ "*.svg": "default"
102
+ }
103
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "format_on_save": "on",
3
+ "lsp": {
4
+ "vscode-css-language-server": {
5
+ "settings": {
6
+ "css": {
7
+ "lint": {
8
+ "unknownAtRules": "ignore"
9
+ }
10
+ }
11
+ }
12
+ }
13
+ },
14
+ "languages": {
15
+ "JavaScript": {
16
+ "language_servers": ["biome", "..."],
17
+ "formatter": { "language_server": { "name": "biome" } },
18
+ "code_actions_on_format": {
19
+ "source.fixAll.biome": true,
20
+ "source.organizeImports.biome": true
21
+ }
22
+ },
23
+ "TypeScript": {
24
+ "formatter": { "language_server": { "name": "biome" } },
25
+ "code_actions_on_format": {
26
+ "source.fixAll.biome": true,
27
+ "source.organizeImports.biome": true
28
+ }
29
+ },
30
+ "TSX": {
31
+ "formatter": { "language_server": { "name": "biome" } },
32
+ "code_actions_on_format": {
33
+ "source.fixAll.biome": true,
34
+ "source.organizeImports.biome": true
35
+ }
36
+ },
37
+ "CSS": {
38
+ "formatter": { "language_server": { "name": "biome" } }
39
+ },
40
+ "JSON": {
41
+ "formatter": { "language_server": { "name": "biome" } }
42
+ },
43
+ "JSONC": {
44
+ "formatter": { "language_server": { "name": "biome" } }
45
+ }
46
+ }
47
+ }