bsmnt 0.4.3 → 0.6.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 (266) hide show
  1. package/README.md +1 -0
  2. package/dist/application/add-hooks/index.d.ts.map +1 -1
  3. package/dist/application/add-hooks/index.js +1 -0
  4. package/dist/application/add-hooks/index.js.map +1 -1
  5. package/dist/application/add-integration/index.d.ts +33 -8
  6. package/dist/application/add-integration/index.d.ts.map +1 -1
  7. package/dist/application/add-integration/index.js +221 -90
  8. package/dist/application/add-integration/index.js.map +1 -1
  9. package/dist/application/create-project/build-context.d.ts +3 -1
  10. package/dist/application/create-project/build-context.d.ts.map +1 -1
  11. package/dist/application/create-project/build-context.js +2 -1
  12. package/dist/application/create-project/build-context.js.map +1 -1
  13. package/dist/application/create-project/index.d.ts +3 -1
  14. package/dist/application/create-project/index.d.ts.map +1 -1
  15. package/dist/application/create-project/index.js +4 -2
  16. package/dist/application/create-project/index.js.map +1 -1
  17. package/dist/application/create-project/map-selection-to-registry.js +1 -1
  18. package/dist/application/create-project/map-selection-to-registry.js.map +1 -1
  19. package/dist/application/install-skills/index.js +1 -1
  20. package/dist/application/install-skills/index.js.map +1 -1
  21. package/dist/application/shared/project-detection.d.ts +2 -2
  22. package/dist/application/shared/project-detection.d.ts.map +1 -1
  23. package/dist/application/shared/project-detection.js +2 -2
  24. package/dist/application/shared/project-detection.js.map +1 -1
  25. package/dist/core/create/execute-plan.d.ts +4 -0
  26. package/dist/core/create/execute-plan.d.ts.map +1 -1
  27. package/dist/core/create/execute-plan.js +8 -0
  28. package/dist/core/create/execute-plan.js.map +1 -1
  29. package/dist/core/create/internal-ids.d.ts +4 -1
  30. package/dist/core/create/internal-ids.d.ts.map +1 -1
  31. package/dist/core/create/internal-ids.js +6 -1
  32. package/dist/core/create/internal-ids.js.map +1 -1
  33. package/dist/core/create/types.d.ts +2 -0
  34. package/dist/core/create/types.d.ts.map +1 -1
  35. package/dist/domain/cms.d.ts +0 -7
  36. package/dist/domain/cms.d.ts.map +1 -1
  37. package/dist/domain/cms.js.map +1 -1
  38. package/dist/domain/forms.d.ts +17 -0
  39. package/dist/domain/forms.d.ts.map +1 -0
  40. package/dist/domain/forms.js +13 -0
  41. package/dist/domain/forms.js.map +1 -0
  42. package/dist/domain/integration.d.ts +30 -0
  43. package/dist/domain/integration.d.ts.map +1 -0
  44. package/dist/domain/integration.js +12 -0
  45. package/dist/domain/integration.js.map +1 -0
  46. package/dist/domain/skills.d.ts +58 -0
  47. package/dist/domain/skills.d.ts.map +1 -1
  48. package/dist/domain/skills.js +52 -0
  49. package/dist/domain/skills.js.map +1 -1
  50. package/dist/index.js +114 -38
  51. package/dist/index.js.map +1 -1
  52. package/dist/infrastructure/agents/register-sanity-mcp.js +9 -6
  53. package/dist/infrastructure/agents/register-sanity-mcp.js.map +1 -1
  54. package/dist/infrastructure/create/apply-package-json.d.ts.map +1 -1
  55. package/dist/infrastructure/create/apply-package-json.js +19 -0
  56. package/dist/infrastructure/create/apply-package-json.js.map +1 -1
  57. package/dist/infrastructure/create/executor.d.ts.map +1 -1
  58. package/dist/infrastructure/create/executor.js +5 -0
  59. package/dist/infrastructure/create/executor.js.map +1 -1
  60. package/dist/infrastructure/create/install-dependencies.d.ts.map +1 -1
  61. package/dist/infrastructure/create/install-dependencies.js +4 -0
  62. package/dist/infrastructure/create/install-dependencies.js.map +1 -1
  63. package/dist/infrastructure/create/lifecycle-registry.d.ts.map +1 -1
  64. package/dist/infrastructure/create/lifecycle-registry.js +2 -0
  65. package/dist/infrastructure/create/lifecycle-registry.js.map +1 -1
  66. package/dist/infrastructure/create/merge-registry.d.ts.map +1 -1
  67. package/dist/infrastructure/create/merge-registry.js +8 -0
  68. package/dist/infrastructure/create/merge-registry.js.map +1 -1
  69. package/dist/infrastructure/create/setup-agent.d.ts +2 -0
  70. package/dist/infrastructure/create/setup-agent.d.ts.map +1 -1
  71. package/dist/infrastructure/create/setup-agent.js +32 -8
  72. package/dist/infrastructure/create/setup-agent.js.map +1 -1
  73. package/dist/infrastructure/create/setup-dotenvx.d.ts +41 -0
  74. package/dist/infrastructure/create/setup-dotenvx.d.ts.map +1 -0
  75. package/dist/infrastructure/create/setup-dotenvx.js +502 -0
  76. package/dist/infrastructure/create/setup-dotenvx.js.map +1 -0
  77. package/dist/infrastructure/create/setup-sanity.d.ts +13 -1
  78. package/dist/infrastructure/create/setup-sanity.d.ts.map +1 -1
  79. package/dist/infrastructure/create/setup-sanity.js +74 -31
  80. package/dist/infrastructure/create/setup-sanity.js.map +1 -1
  81. package/dist/infrastructure/integrations/inject-integration.d.ts +3 -3
  82. package/dist/infrastructure/integrations/inject-integration.d.ts.map +1 -1
  83. package/dist/infrastructure/integrations/inject-integration.js +81 -37
  84. package/dist/infrastructure/integrations/inject-integration.js.map +1 -1
  85. package/dist/modules/features/cms/sanity/config.d.ts.map +1 -1
  86. package/dist/modules/features/cms/sanity/config.js +13 -6
  87. package/dist/modules/features/cms/sanity/config.js.map +1 -1
  88. package/dist/modules/features/cms/sanity/index.js +8 -2
  89. package/dist/modules/features/cms/sanity/index.js.map +1 -1
  90. package/dist/modules/features/cms/sanity/mergers/ci-workflow.d.ts +13 -0
  91. package/dist/modules/features/cms/sanity/mergers/ci-workflow.d.ts.map +1 -0
  92. package/dist/modules/features/cms/sanity/mergers/ci-workflow.js +38 -0
  93. package/dist/modules/features/cms/sanity/mergers/ci-workflow.js.map +1 -0
  94. package/dist/modules/features/cms/sanity/mergers/knip.d.ts +12 -0
  95. package/dist/modules/features/cms/sanity/mergers/knip.d.ts.map +1 -0
  96. package/dist/modules/features/cms/sanity/mergers/knip.js +38 -0
  97. package/dist/modules/features/cms/sanity/mergers/knip.js.map +1 -0
  98. package/dist/modules/features/cms/sanity/mergers/layout.d.ts.map +1 -1
  99. package/dist/modules/features/cms/sanity/mergers/layout.js +17 -3
  100. package/dist/modules/features/cms/sanity/mergers/layout.js.map +1 -1
  101. package/dist/modules/features/cms/sanity/mergers/sitemap.d.ts +3 -3
  102. package/dist/modules/features/cms/sanity/mergers/sitemap.d.ts.map +1 -1
  103. package/dist/modules/features/cms/sanity/mergers/sitemap.js +48 -62
  104. package/dist/modules/features/cms/sanity/mergers/sitemap.js.map +1 -1
  105. package/dist/modules/features/cms/sanity-pagebuilder/config.d.ts.map +1 -1
  106. package/dist/modules/features/cms/sanity-pagebuilder/config.js +8 -5
  107. package/dist/modules/features/cms/sanity-pagebuilder/config.js.map +1 -1
  108. package/dist/modules/features/cms/sanity-pagebuilder/index.js +2 -2
  109. package/dist/modules/features/cms/sanity-pagebuilder/index.js.map +1 -1
  110. package/dist/modules/features/env/dotenvx/config.d.ts +13 -0
  111. package/dist/modules/features/env/dotenvx/config.d.ts.map +1 -0
  112. package/dist/modules/features/env/dotenvx/config.js +47 -0
  113. package/dist/modules/features/env/dotenvx/config.js.map +1 -0
  114. package/dist/modules/features/forms/hubspot/config.d.ts +31 -0
  115. package/dist/modules/features/forms/hubspot/config.d.ts.map +1 -0
  116. package/dist/modules/features/forms/hubspot/config.js +25 -0
  117. package/dist/modules/features/forms/hubspot/config.js.map +1 -0
  118. package/dist/paths.d.ts +16 -0
  119. package/dist/paths.d.ts.map +1 -1
  120. package/dist/paths.js +16 -0
  121. package/dist/paths.js.map +1 -1
  122. package/package.json +21 -9
  123. package/src/agent-skills/json-ld/SKILL.md +85 -0
  124. package/src/agent-skills/manage-env/SKILL.md +67 -0
  125. package/src/assets/check-node-version.mjs +22 -0
  126. package/src/modules/features/cms/sanity/files/app/blog/[slug]/page.tsx +48 -21
  127. package/src/modules/features/cms/sanity/files/app/blog/page.tsx +54 -0
  128. package/src/modules/features/cms/sanity/files/app/feed.xml/route.ts +36 -0
  129. package/src/modules/features/cms/sanity/files/components/sanity/structured-data.tsx +83 -0
  130. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/components/disable-draft-mode.tsx +3 -3
  131. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/queries.ts +14 -0
  132. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/sanity.config.ts +19 -3
  133. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/sanity.types.ts +1 -0
  134. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/schemas/article.ts +7 -7
  135. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/schemas/index.ts +36 -1
  136. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/schemas/metadata.ts +12 -1
  137. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/schemas/structured-data-blocks.ts +416 -0
  138. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/schemas/structured-data.ts +100 -0
  139. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/structure.ts +37 -2
  140. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/utils/page-json-ld.ts +208 -0
  141. package/src/modules/features/cms/sanity/files/lib/scripts/setup-ci-secrets.ts +117 -0
  142. package/src/modules/features/cms/sanity/files/lib/utils/rss.ts +96 -0
  143. package/src/modules/features/cms/sanity-pagebuilder/files/.knip.json +8 -0
  144. package/src/modules/features/cms/sanity-pagebuilder/files/app/api/[[...slug]]/route.ts +100 -0
  145. package/src/modules/features/cms/sanity-pagebuilder/files/app/api/draft-mode/disable/route.ts +7 -0
  146. package/src/modules/features/cms/sanity-pagebuilder/files/app/api/draft-mode/enable/route.ts +20 -0
  147. package/src/modules/features/cms/sanity-pagebuilder/files/app/api/revalidate/route.ts +126 -0
  148. package/src/modules/features/cms/sanity-pagebuilder/files/app/feed.xml/route.ts +40 -0
  149. package/src/modules/features/cms/sanity-pagebuilder/files/app/sitemap.md/route.ts +124 -0
  150. package/src/modules/features/cms/sanity-pagebuilder/files/app/studio/[[...tool]]/page.tsx +8 -0
  151. package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/draft-mode-toggle.tsx +27 -0
  152. package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/rich-text.tsx +87 -0
  153. package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/visual-editing.tsx +28 -0
  154. package/src/modules/features/cms/sanity-pagebuilder/files/components/ui/sanity-image/index.tsx +41 -0
  155. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/check-integration.ts +5 -0
  156. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/client.ts +27 -0
  157. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/disable-draft-mode.tsx +23 -0
  158. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/page-builder-input.tsx +39 -0
  159. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/page-category-input.tsx +50 -0
  160. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/same-as-input.tsx +66 -0
  161. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/slug-field.tsx +18 -0
  162. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/slug-input.tsx +14 -0
  163. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/confirm-publish-action.ts +40 -0
  164. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/env.ts +34 -0
  165. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/fetchers/layout.ts +67 -0
  166. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/fetchers/redirects.ts +17 -0
  167. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/icons.ts +79 -0
  168. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/live/index.tsx +88 -0
  169. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/markdown-proxy.config.ts +50 -0
  170. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-builder-config.ts +130 -0
  171. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-category.ts +28 -0
  172. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-tree-pane.tsx +420 -0
  173. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/pinned-pages.ts +54 -0
  174. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/presentation.ts +119 -0
  175. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/queries.ts +407 -0
  176. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/sanity.cli.ts +29 -0
  177. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/sanity.config.ts +113 -0
  178. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/index.ts +4 -0
  179. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/blog-content.ts +89 -0
  180. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/description.ts +29 -0
  181. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/hero.ts +28 -0
  182. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/singleton/content-collection.ts +45 -0
  183. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/content/author.ts +70 -0
  184. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/content/blog-category.ts +55 -0
  185. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/index.ts +124 -0
  186. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/company-data.ts +70 -0
  187. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/footer.ts +79 -0
  188. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/navbar.ts +75 -0
  189. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/redirect.ts +120 -0
  190. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/structured-data.ts +124 -0
  191. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/link.ts +125 -0
  192. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/logo-field.ts +9 -0
  193. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/metadata.ts +79 -0
  194. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/nav-objects.ts +192 -0
  195. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/page-builder.ts +39 -0
  196. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/page-folder.ts +124 -0
  197. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/page.ts +242 -0
  198. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/richText.ts +63 -0
  199. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/structured-data-blocks.ts +436 -0
  200. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/singletons.ts +51 -0
  201. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/structure.ts +330 -0
  202. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/utils/image.ts +8 -0
  203. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/utils/link.ts +140 -0
  204. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/utils/page-builder-markdown.ts +81 -0
  205. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/utils/page-json-ld.ts +208 -0
  206. package/src/modules/features/cms/sanity-pagebuilder/files/lib/scripts/sanity-typegen.ts +45 -0
  207. package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/check-redirect.ts +30 -0
  208. package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/json-ld.tsx +521 -0
  209. package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/metadata.ts +167 -0
  210. package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/rss.ts +96 -0
  211. package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/sitemap.ts +40 -0
  212. package/src/modules/features/cms/sanity-pagebuilder/files/proxy.ts +81 -0
  213. package/src/modules/features/env/dotenvx/files/decrypt-env.mjs +45 -0
  214. package/src/modules/features/env/dotenvx/files/gitleaks.yml +25 -0
  215. package/src/modules/features/env/dotenvx/files/instrumentation.ts +14 -0
  216. package/src/modules/features/env/dotenvx/files/push-keys.mjs +129 -0
  217. package/src/modules/features/env/dotenvx/files/save-key.mjs +108 -0
  218. package/src/modules/features/env/dotenvx/files/setup-env.mjs +67 -0
  219. package/src/modules/features/env/dotenvx/files/vercelignore +4 -0
  220. package/src/modules/features/env/dotenvx/files/write-env.mjs +47 -0
  221. package/src/templates/next-default/.env.example +14 -6
  222. package/src/templates/next-default/.github/workflows/ci.yml +83 -0
  223. package/src/templates/next-default/.vscode/extensions.json +3 -0
  224. package/src/templates/next-default/README.md +53 -0
  225. package/src/templates/next-default/knip.json +14 -0
  226. package/src/templates/next-default/lefthook.yml +5 -0
  227. package/src/templates/next-default/lib/utils/json-ld.tsx +327 -21
  228. package/src/templates/next-default/next.config.ts +6 -0
  229. package/src/templates/next-default/package.json +8 -4
  230. package/src/templates/next-experiments/.env.example +14 -6
  231. package/src/templates/next-experiments/.github/workflows/ci.yml +83 -0
  232. package/src/templates/next-experiments/.vscode/extensions.json +3 -0
  233. package/src/templates/next-experiments/README.md +53 -0
  234. package/src/templates/next-experiments/knip.json +14 -0
  235. package/src/templates/next-experiments/lefthook.yml +30 -0
  236. package/src/templates/next-experiments/lib/utils/json-ld.tsx +327 -21
  237. package/src/templates/next-experiments/next.config.ts +6 -0
  238. package/src/templates/next-experiments/package.json +10 -2
  239. package/src/templates/next-pagebuilder/.env.example +19 -1
  240. package/src/templates/next-pagebuilder/.github/workflows/ci.yml +90 -0
  241. package/src/templates/next-pagebuilder/.vscode/extensions.json +3 -0
  242. package/src/templates/next-pagebuilder/README.md +65 -0
  243. package/src/templates/next-pagebuilder/app/(content)/[[...slug]]/page.tsx +6 -1
  244. package/src/templates/next-pagebuilder/app/(content)/layout.tsx +3 -12
  245. package/src/templates/next-pagebuilder/components/layout/footer/index.tsx +11 -10
  246. package/src/templates/next-pagebuilder/components/layout/header/index.tsx +5 -3
  247. package/src/templates/next-pagebuilder/components/layout/json-ld/index.tsx +38 -26
  248. package/src/templates/next-pagebuilder/components/layout/wrapper/index.tsx +3 -11
  249. package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/index.tsx +1 -1
  250. package/src/templates/next-pagebuilder/components/page-document/index.tsx +38 -0
  251. package/src/templates/next-pagebuilder/knip.json +27 -0
  252. package/src/templates/next-pagebuilder/lefthook.yml +5 -0
  253. package/src/templates/next-pagebuilder/lib/integrations/sanity/sanity.types.ts +36 -2
  254. package/src/templates/next-pagebuilder/lib/scripts/setup-ci-secrets.ts +117 -0
  255. package/src/templates/next-pagebuilder/next.config.ts +6 -0
  256. package/src/templates/next-pagebuilder/package.json +9 -3
  257. package/src/templates/next-webgl/.env.example +14 -6
  258. package/src/templates/next-webgl/.github/workflows/ci.yml +83 -0
  259. package/src/templates/next-webgl/.vscode/extensions.json +3 -0
  260. package/src/templates/next-webgl/README.md +53 -0
  261. package/src/templates/next-webgl/knip.json +13 -0
  262. package/src/templates/next-webgl/lefthook.yml +5 -0
  263. package/src/templates/next-webgl/lib/utils/json-ld.tsx +327 -21
  264. package/src/templates/next-webgl/next.config.ts +6 -0
  265. package/src/templates/next-webgl/package.json +8 -4
  266. package/src/modules/features/cms/sanity/files/lib/utils/json-ld.tsx +0 -244
@@ -0,0 +1,100 @@
1
+ import { defineField, defineType } from "sanity";
2
+
3
+ export const structuredData = defineType({
4
+ name: "structuredData",
5
+ title: "Structured Data",
6
+ type: "document",
7
+ description:
8
+ "Site-wide identity emitted as JSON-LD (schema.org Organization + WebSite) on every page for search engines.",
9
+ fields: [
10
+ defineField({
11
+ name: "name",
12
+ title: "Organization Name",
13
+ type: "string",
14
+ description: "Public name of the organization (shown in rich results).",
15
+ validation: (Rule) => Rule.required(),
16
+ }),
17
+ defineField({
18
+ name: "legalName",
19
+ title: "Legal Name",
20
+ type: "string",
21
+ description:
22
+ "Registered legal entity name, if different from the public name.",
23
+ }),
24
+ defineField({
25
+ name: "logo",
26
+ title: "Logo",
27
+ type: "image",
28
+ description:
29
+ "Square or wide logo used in rich results. Min 112x112px, transparent PNG/SVG recommended.",
30
+ options: { hotspot: true },
31
+ }),
32
+ defineField({
33
+ name: "description",
34
+ title: "Description",
35
+ type: "text",
36
+ rows: 3,
37
+ description: "Short sentence describing the organization.",
38
+ }),
39
+ defineField({
40
+ name: "url",
41
+ title: "Canonical URL",
42
+ type: "url",
43
+ description:
44
+ "Optional override for the canonical site URL. Defaults to the deployed origin.",
45
+ }),
46
+ defineField({
47
+ name: "sameAs",
48
+ title: "Same-As Profiles",
49
+ type: "array",
50
+ description:
51
+ "Official profiles representing this organization (LinkedIn, X, GitHub, Crunchbase, Wikipedia, etc.).",
52
+ of: [{ type: "url" }],
53
+ }),
54
+ defineField({
55
+ name: "contactPoint",
56
+ title: "Contact Point",
57
+ type: "object",
58
+ options: { collapsible: true, collapsed: true },
59
+ fields: [
60
+ defineField({ name: "email", title: "Email", type: "string" }),
61
+ defineField({ name: "telephone", title: "Telephone", type: "string" }),
62
+ defineField({
63
+ name: "contactType",
64
+ title: "Contact Type",
65
+ type: "string",
66
+ description:
67
+ "e.g. 'customer support', 'sales', 'press'. See schema.org/ContactPoint.",
68
+ }),
69
+ ],
70
+ }),
71
+ defineField({
72
+ name: "address",
73
+ title: "Address",
74
+ type: "object",
75
+ options: { collapsible: true, collapsed: true },
76
+ fields: [
77
+ defineField({
78
+ name: "streetAddress",
79
+ title: "Street Address",
80
+ type: "string",
81
+ }),
82
+ defineField({ name: "locality", title: "City", type: "string" }),
83
+ defineField({
84
+ name: "region",
85
+ title: "Region / State",
86
+ type: "string",
87
+ }),
88
+ defineField({
89
+ name: "postalCode",
90
+ title: "Postal Code",
91
+ type: "string",
92
+ }),
93
+ defineField({ name: "country", title: "Country", type: "string" }),
94
+ ],
95
+ }),
96
+ ],
97
+ preview: {
98
+ prepare: () => ({ title: "Structured Data" }),
99
+ },
100
+ });
@@ -1,5 +1,40 @@
1
+ import {
2
+ ComponentIcon,
3
+ DocumentsIcon,
4
+ MenuIcon,
5
+ SchemaIcon,
6
+ ThLargeIcon,
7
+ } from "@sanity/icons";
1
8
  import type { StructureResolver } from "sanity/structure";
2
9
 
3
- // https://www.sanity.io/docs/structure-builder-cheat-sheet
10
+ // Explicit menu: new schema types must be listed here, which keeps plugin
11
+ // types (e.g. media.tag) out of the Studio.
4
12
  export const structure: StructureResolver = (S) =>
5
- S.list().title("Content").items(S.documentTypeListItems());
13
+ S.list()
14
+ .title("Content")
15
+ .items([
16
+ S.documentTypeListItem("article").title("Articles").icon(DocumentsIcon),
17
+ S.documentTypeListItem("example").title("Examples").icon(ComponentIcon),
18
+ S.documentTypeListItem("navigation").title("Navigation").icon(MenuIcon),
19
+ S.divider(),
20
+ // Layout — site-wide singletons.
21
+ S.listItem()
22
+ .title("Layout")
23
+ .id("layout")
24
+ .icon(ThLargeIcon)
25
+ .child(
26
+ S.list()
27
+ .title("Layout")
28
+ .items([
29
+ S.listItem()
30
+ .title("Structured Data")
31
+ .id("structuredData")
32
+ .icon(SchemaIcon)
33
+ .child(
34
+ S.document()
35
+ .schemaType("structuredData")
36
+ .documentId("structuredData"),
37
+ ),
38
+ ]),
39
+ ),
40
+ ]);
@@ -0,0 +1,208 @@
1
+ import { stegaClean } from "next-sanity";
2
+ import type { Thing, WithContext } from "schema-dts";
3
+ import {
4
+ type FaqJsonLdItem,
5
+ generateEventJsonLd,
6
+ generateFaqJsonLd,
7
+ generateItemListJsonLd,
8
+ generateJobPostingJsonLd,
9
+ generateSoftwareApplicationJsonLd,
10
+ generateVideoJsonLd,
11
+ type ItemListEntry,
12
+ } from "@/lib/utils/json-ld";
13
+ import { urlForImage } from "./image";
14
+
15
+ /**
16
+ * Turns `metadata.jsonLd` blocks from Sanity into renderable JSON-LD —
17
+ * stega-cleaned, with image refs resolved to absolute CDN URLs.
18
+ */
19
+
20
+ type SanityImageRef = Parameters<typeof urlForImage>[0];
21
+
22
+ type JsonLdBlock = {
23
+ _type?: string;
24
+ _key?: string;
25
+ [key: string]: unknown;
26
+ };
27
+
28
+ const clean = (value: unknown): string | undefined => {
29
+ const cleaned = stegaClean(value);
30
+ return typeof cleaned === "string" && cleaned.trim() !== ""
31
+ ? cleaned
32
+ : undefined;
33
+ };
34
+
35
+ const resolveImage = (value: unknown): string | undefined => {
36
+ if (!value || typeof value !== "object") return undefined;
37
+ try {
38
+ return urlForImage(value as SanityImageRef).url();
39
+ } catch {
40
+ return undefined;
41
+ }
42
+ };
43
+
44
+ const buildFaq = (block: JsonLdBlock): WithContext<Thing> | null => {
45
+ const questions = Array.isArray(block.questions) ? block.questions : [];
46
+ const items = questions
47
+ .map((entry): FaqJsonLdItem | null => {
48
+ const question = clean((entry as JsonLdBlock)?.question);
49
+ const answer = clean((entry as JsonLdBlock)?.answer);
50
+ return question && answer ? { question, answer } : null;
51
+ })
52
+ .filter((item): item is FaqJsonLdItem => item !== null);
53
+ return items.length ? generateFaqJsonLd(items) : null;
54
+ };
55
+
56
+ const buildJobPosting = (block: JsonLdBlock): WithContext<Thing> | null => {
57
+ const title = clean(block.title);
58
+ const description = clean(block.description);
59
+ const datePosted = clean(block.datePosted);
60
+ if (!title || !description || !datePosted) return null;
61
+ return generateJobPostingJsonLd({
62
+ title,
63
+ description,
64
+ datePosted,
65
+ validThrough: clean(block.validThrough),
66
+ employmentType: clean(block.employmentType),
67
+ organizationName: clean(block.organizationName),
68
+ organizationUrl: clean(block.organizationUrl),
69
+ remote: block.remote === true,
70
+ locality: clean(block.locality),
71
+ region: clean(block.region),
72
+ country: clean(block.country),
73
+ });
74
+ };
75
+
76
+ const buildSoftwareApplication = (
77
+ block: JsonLdBlock,
78
+ ): WithContext<Thing> | null => {
79
+ const name = clean(block.name);
80
+ if (!name) return null;
81
+ const priceValue = stegaClean(block.price);
82
+ const price =
83
+ typeof priceValue === "number" && Number.isFinite(priceValue)
84
+ ? priceValue
85
+ : undefined;
86
+ return generateSoftwareApplicationJsonLd({
87
+ name,
88
+ description: clean(block.description),
89
+ applicationCategory: clean(block.applicationCategory),
90
+ operatingSystem: clean(block.operatingSystem),
91
+ price,
92
+ currency: clean(block.currency),
93
+ url: clean(block.url),
94
+ image: resolveImage(block.image),
95
+ });
96
+ };
97
+
98
+ const buildItemList = (block: JsonLdBlock): WithContext<Thing> | null => {
99
+ const rawItems = Array.isArray(block.items) ? block.items : [];
100
+ const items = rawItems
101
+ .map((entry): ItemListEntry | null => {
102
+ const name = clean((entry as JsonLdBlock)?.name);
103
+ if (!name) return null;
104
+ return { name, url: clean((entry as JsonLdBlock)?.url) };
105
+ })
106
+ .filter((item): item is ItemListEntry => item !== null);
107
+ if (!items.length) return null;
108
+ return generateItemListJsonLd({ name: clean(block.name), items });
109
+ };
110
+
111
+ const buildEvent = (block: JsonLdBlock): WithContext<Thing> | null => {
112
+ const name = clean(block.name);
113
+ const startDate = clean(block.startDate);
114
+ if (!name || !startDate) return null;
115
+ return generateEventJsonLd({
116
+ name,
117
+ startDate,
118
+ endDate: clean(block.endDate),
119
+ description: clean(block.description),
120
+ locationName: clean(block.locationName),
121
+ locationAddress: clean(block.locationAddress),
122
+ url: clean(block.url),
123
+ image: resolveImage(block.image),
124
+ });
125
+ };
126
+
127
+ const buildVideo = (block: JsonLdBlock): WithContext<Thing> | null => {
128
+ const name = clean(block.name);
129
+ const description = clean(block.description);
130
+ const uploadDate = clean(block.uploadDate);
131
+ const thumbnail = resolveImage(block.thumbnail);
132
+ if (!name || !description || !uploadDate || !thumbnail) return null;
133
+ return generateVideoJsonLd({
134
+ name,
135
+ description,
136
+ thumbnail,
137
+ uploadDate,
138
+ contentUrl: clean(block.contentUrl),
139
+ embedUrl: clean(block.embedUrl),
140
+ });
141
+ };
142
+
143
+ const buildCustom = (block: JsonLdBlock): WithContext<Thing> | null => {
144
+ const raw = clean(block.json);
145
+ if (!raw) return null;
146
+ try {
147
+ const parsed = JSON.parse(raw);
148
+ // Skip structurally-incomplete blocks: schema.org requires an @type.
149
+ if (
150
+ typeof parsed !== "object" ||
151
+ parsed === null ||
152
+ Array.isArray(parsed) ||
153
+ !("@type" in parsed)
154
+ ) {
155
+ return null;
156
+ }
157
+ // Default @context so editors can omit the boilerplate (an explicit
158
+ // @context in the pasted object always wins).
159
+ return {
160
+ "@context": "https://schema.org",
161
+ ...parsed,
162
+ } as WithContext<Thing>;
163
+ } catch {
164
+ return null;
165
+ }
166
+ };
167
+
168
+ export type PageJsonLd = { key: string; data: WithContext<Thing> };
169
+
170
+ export function buildPageJsonLd(blocks: unknown): PageJsonLd[] {
171
+ if (!Array.isArray(blocks)) return [];
172
+
173
+ return blocks
174
+ .map((entry, index): PageJsonLd | null => {
175
+ const block = entry as JsonLdBlock;
176
+ let data: WithContext<Thing> | null;
177
+ switch (block._type) {
178
+ case "faqJsonLd":
179
+ data = buildFaq(block);
180
+ break;
181
+ case "eventJsonLd":
182
+ data = buildEvent(block);
183
+ break;
184
+ case "videoJsonLd":
185
+ data = buildVideo(block);
186
+ break;
187
+ case "jobPostingJsonLd":
188
+ data = buildJobPosting(block);
189
+ break;
190
+ case "softwareApplicationJsonLd":
191
+ data = buildSoftwareApplication(block);
192
+ break;
193
+ case "itemListJsonLd":
194
+ data = buildItemList(block);
195
+ break;
196
+ case "customJsonLd":
197
+ data = buildCustom(block);
198
+ break;
199
+ default:
200
+ data = null;
201
+ }
202
+ if (!data) return null;
203
+ const key =
204
+ typeof block._key === "string" ? block._key : `jsonld-${index}`;
205
+ return { key, data };
206
+ })
207
+ .filter((item): item is PageJsonLd => item !== null);
208
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Push CI environment to GitHub Actions for this repository.
3
+ *
4
+ * Reads values from `.env.local` and sets them on the current repo via the
5
+ * GitHub CLI (`gh`), matching the `build` step in `.github/workflows/ci.yml`:
6
+ * the public Sanity project ID and dataset become repository *variables*, and
7
+ * the read token becomes a *secret*.
8
+ *
9
+ * Run once the repo exists on GitHub: `bun run ci:secrets`
10
+ * Requires the GitHub CLI, authenticated (`gh auth login`).
11
+ */
12
+ import { execFileSync } from "node:child_process";
13
+ import { existsSync, readFileSync } from "node:fs";
14
+ import path from "node:path";
15
+
16
+ const ENV_FILE = ".env.local";
17
+
18
+ /** Public config — stored as GitHub Actions repository variables. */
19
+ const VARIABLES = [
20
+ "NEXT_PUBLIC_SANITY_PROJECT_ID",
21
+ "NEXT_PUBLIC_SANITY_DATASET",
22
+ ] as const;
23
+
24
+ /** Sensitive config — stored as GitHub Actions secrets. */
25
+ const SECRETS = ["SANITY_API_READ_TOKEN"] as const;
26
+
27
+ function parseEnvFile(file: string): Record<string, string> {
28
+ const env: Record<string, string> = {};
29
+ if (!existsSync(file)) return env;
30
+
31
+ for (const rawLine of readFileSync(file, "utf8").split("\n")) {
32
+ const line = rawLine.trim();
33
+ if (!line || line.startsWith("#")) continue;
34
+
35
+ const eq = line.indexOf("=");
36
+ if (eq === -1) continue;
37
+
38
+ const key = line.slice(0, eq).trim();
39
+ let value = line.slice(eq + 1).trim();
40
+ if (
41
+ (value.startsWith('"') && value.endsWith('"')) ||
42
+ (value.startsWith("'") && value.endsWith("'"))
43
+ ) {
44
+ value = value.slice(1, -1);
45
+ }
46
+ env[key] = value;
47
+ }
48
+
49
+ return env;
50
+ }
51
+
52
+ function ensureGitHubRepo(): void {
53
+ try {
54
+ execFileSync("gh", ["--version"], { stdio: "pipe" });
55
+ } catch {
56
+ console.error(
57
+ "✖ GitHub CLI (gh) not found. Install it: https://cli.github.com",
58
+ );
59
+ process.exit(1);
60
+ }
61
+
62
+ try {
63
+ execFileSync("gh", ["repo", "view", "--json", "nameWithOwner"], {
64
+ stdio: "pipe",
65
+ });
66
+ } catch {
67
+ console.error("✖ No GitHub repository detected for this directory.");
68
+ console.error(
69
+ " Create and push the repo first (e.g. `gh repo create`), then re-run.",
70
+ );
71
+ process.exit(1);
72
+ }
73
+ }
74
+
75
+ ensureGitHubRepo();
76
+
77
+ const env = parseEnvFile(path.resolve(ENV_FILE));
78
+ let pushed = 0;
79
+ let skipped = 0;
80
+
81
+ for (const name of VARIABLES) {
82
+ const value = env[name] ?? process.env[name];
83
+ if (!value) {
84
+ console.info(`- ${name}: not found in ${ENV_FILE}, skipping`);
85
+ skipped++;
86
+ continue;
87
+ }
88
+ // Public value — safe to pass on the command line.
89
+ execFileSync("gh", ["variable", "set", name, "--body", value], {
90
+ stdio: "pipe",
91
+ });
92
+ console.info(`✓ variable ${name}`);
93
+ pushed++;
94
+ }
95
+
96
+ for (const name of SECRETS) {
97
+ const value = env[name] ?? process.env[name];
98
+ if (!value) {
99
+ console.info(`- ${name}: not found in ${ENV_FILE}, skipping`);
100
+ skipped++;
101
+ continue;
102
+ }
103
+ // Sensitive — pipe via stdin so the token never appears in the process args.
104
+ execFileSync("gh", ["secret", "set", name], { input: value, stdio: "pipe" });
105
+ console.info(`✓ secret ${name}`);
106
+ pushed++;
107
+ }
108
+
109
+ if (pushed === 0) {
110
+ console.info(
111
+ `\nNothing to sync. Add your Sanity values to ${ENV_FILE} (or run Sanity setup) first.`,
112
+ );
113
+ } else {
114
+ console.info(
115
+ `\nDone — ${String(pushed)} value(s) pushed to GitHub Actions${skipped ? `, ${String(skipped)} skipped` : ""}.`,
116
+ );
117
+ }
@@ -0,0 +1,96 @@
1
+ export interface RssFeedItem {
2
+ title: string;
3
+ link: string;
4
+ description?: string | undefined;
5
+ pubDate?: string | undefined;
6
+ authors?: string[] | undefined;
7
+ category?: string | undefined;
8
+ }
9
+
10
+ export interface RssFeedConfig {
11
+ title: string;
12
+ link: string;
13
+ description: string;
14
+ feedUrl: string;
15
+ language?: string;
16
+ items: RssFeedItem[];
17
+ }
18
+
19
+ function escapeXml(str: string): string {
20
+ return str
21
+ .replace(/&/g, "&amp;")
22
+ .replace(/</g, "&lt;")
23
+ .replace(/>/g, "&gt;")
24
+ .replace(/"/g, "&quot;")
25
+ .replace(/'/g, "&apos;");
26
+ }
27
+
28
+ export function generateRssFeed(config: RssFeedConfig): Response {
29
+ const {
30
+ title,
31
+ link,
32
+ description,
33
+ feedUrl,
34
+ language = "en-us",
35
+ items,
36
+ } = config;
37
+
38
+ const itemsXml = items
39
+ .map((item) => {
40
+ const lines = [
41
+ ` <title>${escapeXml(item.title)}</title>`,
42
+ ` <link>${escapeXml(item.link)}</link>`,
43
+ ` <guid isPermaLink="true">${escapeXml(item.link)}</guid>`,
44
+ ];
45
+
46
+ if (item.description) {
47
+ lines.push(
48
+ ` <description>${escapeXml(item.description)}</description>`,
49
+ );
50
+ }
51
+
52
+ const pubDate = item.pubDate ? new Date(item.pubDate) : null;
53
+ if (pubDate && !Number.isNaN(pubDate.getTime())) {
54
+ lines.push(` <pubDate>${pubDate.toUTCString()}</pubDate>`);
55
+ }
56
+
57
+ for (const author of item.authors ?? []) {
58
+ lines.push(` <dc:creator>${escapeXml(author)}</dc:creator>`);
59
+ }
60
+
61
+ if (item.category) {
62
+ lines.push(` <category>${escapeXml(item.category)}</category>`);
63
+ }
64
+
65
+ return ` <item>\n${lines.join("\n")}\n </item>`;
66
+ })
67
+ .join("\n");
68
+
69
+ const xml = `<?xml version="1.0" encoding="UTF-8"?>
70
+ <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom">
71
+ <channel>
72
+ <title>${escapeXml(title)}</title>
73
+ <link>${escapeXml(link)}</link>
74
+ <description>${escapeXml(description)}</description>
75
+ <language>${language}</language>
76
+ <atom:link href="${escapeXml(feedUrl)}" rel="self" type="application/rss+xml" />
77
+ ${itemsXml}
78
+ </channel>
79
+ </rss>`;
80
+
81
+ return new Response(xml, {
82
+ headers: {
83
+ "Content-Type": "application/rss+xml",
84
+ },
85
+ });
86
+ }
87
+
88
+ export function rssErrorResponse(): Response {
89
+ return new Response(
90
+ '<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>Error</title></channel></rss>',
91
+ {
92
+ status: 500,
93
+ headers: { "Content-Type": "application/xml" },
94
+ },
95
+ );
96
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "https://unpkg.com/knip@6/schema.json",
3
+ "entry": [
4
+ "lib/integrations/sanity/sanity.cli.ts",
5
+ "lib/integrations/sanity/sanity.config.ts"
6
+ ],
7
+ "ignore": ["lib/integrations/sanity/sanity.types.ts"]
8
+ }
@@ -0,0 +1,100 @@
1
+ import { NextResponse } from "next/server";
2
+ import { isSanityConfigured } from "@/lib/integrations/check-integration";
3
+ import { sanityFetch } from "@/lib/integrations/sanity/live";
4
+ import { PAGE_QUERY } from "@/lib/integrations/sanity/queries";
5
+ import type { PAGE_QUERY_RESULT } from "@/lib/integrations/sanity/sanity.types";
6
+ import { renderPageBuilderMarkdown } from "@/lib/integrations/sanity/utils/page-builder-markdown";
7
+ import { getBaseUrl } from "@/lib/utils/base-url";
8
+
9
+ type PageResult = NonNullable<PAGE_QUERY_RESULT>;
10
+
11
+ const getSlugPath = (slugParts?: string[]) => {
12
+ if (!slugParts?.length) return null;
13
+
14
+ const lastSegment = slugParts.at(-1);
15
+
16
+ if (!lastSegment?.endsWith(".md")) return null;
17
+
18
+ const pathSegments = [...slugParts];
19
+ pathSegments[pathSegments.length - 1] = lastSegment.slice(0, -3);
20
+
21
+ const slug = pathSegments.filter(Boolean).join("/");
22
+ if (!slug) return null;
23
+
24
+ return slug === "index" ? null : slug;
25
+ };
26
+
27
+ export async function GET(
28
+ _request: Request,
29
+ props: { params: Promise<{ slug?: string[] }> },
30
+ ) {
31
+ const { slug: rawSlugParts } = await props.params;
32
+ const slug = getSlugPath(rawSlugParts);
33
+
34
+ if (
35
+ rawSlugParts?.length &&
36
+ slug === null &&
37
+ rawSlugParts.join("/") !== "index.md"
38
+ ) {
39
+ return new NextResponse(null, { status: 404 });
40
+ }
41
+
42
+ if (!rawSlugParts?.length) {
43
+ return new NextResponse(null, { status: 404 });
44
+ }
45
+
46
+ if (!isSanityConfigured()) {
47
+ return new NextResponse("# 404 Not Found\n\nCMS not configured.", {
48
+ status: 404,
49
+ headers: { "Content-Type": "text/markdown; charset=utf-8" },
50
+ });
51
+ }
52
+
53
+ const { data } = await sanityFetch({
54
+ query: PAGE_QUERY,
55
+ params: { slug },
56
+ });
57
+
58
+ const page = data as PageResult | null;
59
+
60
+ const baseUrl = getBaseUrl();
61
+
62
+ if (!page) {
63
+ return new NextResponse("# 404 Not Found\n\nPage not found.", {
64
+ status: 404,
65
+ headers: { "Content-Type": "text/markdown; charset=utf-8" },
66
+ });
67
+ }
68
+
69
+ const body = renderPageBuilderMarkdown(page.pageBuilder);
70
+ const canonicalPath = slug ? `/${slug}` : "/";
71
+
72
+ return new NextResponse(
73
+ [
74
+ `# ${page.title}`,
75
+ "",
76
+ page._updatedAt
77
+ ? `**Updated:** ${new Date(page._updatedAt).toLocaleDateString()}`
78
+ : null,
79
+ "",
80
+ `Canonical URL: ${baseUrl}${canonicalPath}`,
81
+ "",
82
+ "---",
83
+ "",
84
+ body || "No page builder content yet.",
85
+ "",
86
+ "---",
87
+ "",
88
+ `[View all content](${baseUrl}/sitemap.md)`,
89
+ ]
90
+ .filter((part): part is string => part !== null)
91
+ .join("\n"),
92
+ {
93
+ headers: {
94
+ "Content-Type": "text/markdown; charset=utf-8",
95
+ Vary: "Accept",
96
+ "X-Content-Type-Options": "nosniff",
97
+ },
98
+ },
99
+ );
100
+ }
@@ -0,0 +1,7 @@
1
+ import { draftMode } from "next/headers";
2
+ import { type NextRequest, NextResponse } from "next/server";
3
+
4
+ export async function GET(request: NextRequest) {
5
+ (await draftMode()).disable();
6
+ return NextResponse.redirect(new URL("/", request.url));
7
+ }
@@ -0,0 +1,20 @@
1
+ import { NextResponse } from "next/server";
2
+ import { defineEnableDraftMode } from "next-sanity/draft-mode";
3
+ import { isSanityConfigured } from "@/lib/integrations/check-integration";
4
+ import { client } from "@/lib/integrations/sanity/client";
5
+ import { sanityToken } from "@/lib/integrations/sanity/env";
6
+
7
+ const draftModeHandler =
8
+ isSanityConfigured() && client
9
+ ? defineEnableDraftMode({
10
+ client: client.withConfig({ token: sanityToken }),
11
+ })
12
+ : {
13
+ GET: () =>
14
+ NextResponse.json(
15
+ { error: "Sanity is not configured" },
16
+ { status: 503 },
17
+ ),
18
+ };
19
+
20
+ export const { GET } = draftModeHandler;