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,66 @@
1
+ import { Badge, Card, Flex, Stack, Text } from "@sanity/ui";
2
+ import { useEffect, useState } from "react";
3
+ import { type ArrayOfPrimitivesInputProps, useClient } from "sanity";
4
+ import { apiVersion } from "../env";
5
+
6
+ type CompanyProfile = { name?: string | null; url?: string | null };
7
+
8
+ // Draft-first so editors see unpublished toggle changes.
9
+ export const INCLUDED_COMPANY_PROFILES_QUERY = `coalesce(
10
+ *[_id == "drafts.companyData"][0],
11
+ *[_id == "companyData"][0]
12
+ ).socialLinks[includeInStructuredData == true]{ name, url }`;
13
+
14
+ // Shows which Company Data social links are merged into sameAs automatically.
15
+ export function SameAsInput(props: ArrayOfPrimitivesInputProps) {
16
+ const { renderDefault } = props;
17
+ const client = useClient({ apiVersion });
18
+ const [included, setIncluded] = useState<CompanyProfile[]>([]);
19
+
20
+ useEffect(() => {
21
+ let active = true;
22
+ client
23
+ .fetch<CompanyProfile[] | null>(INCLUDED_COMPANY_PROFILES_QUERY)
24
+ .then((rows) => {
25
+ if (active) setIncluded(rows ?? []);
26
+ })
27
+ .catch(() => {
28
+ // Best-effort preview — never block editing.
29
+ });
30
+ return () => {
31
+ active = false;
32
+ };
33
+ }, [client]);
34
+
35
+ return (
36
+ <Stack space={3}>
37
+ <Card padding={3} radius={2} border tone="transparent">
38
+ <Stack space={3}>
39
+ {included.length > 0 ? (
40
+ <>
41
+ <Text size={1} muted>
42
+ Included from Company Data (toggle off there to remove):
43
+ </Text>
44
+ {included.map((profile) => (
45
+ <Flex key={profile.url} gap={2} align="center">
46
+ <Badge fontSize={0} mode="outline">
47
+ {profile.name ?? "Profile"}
48
+ </Badge>
49
+ <Text size={1} muted textOverflow="ellipsis">
50
+ {profile.url}
51
+ </Text>
52
+ </Flex>
53
+ ))}
54
+ </>
55
+ ) : (
56
+ <Text size={1} muted>
57
+ No Company Data social links are included yet — enable "Include in
58
+ Structured Data" on a social link to add it here.
59
+ </Text>
60
+ )}
61
+ </Stack>
62
+ </Card>
63
+ {renderDefault(props)}
64
+ </Stack>
65
+ );
66
+ }
@@ -0,0 +1,18 @@
1
+ import { type FieldProps, useFormValue } from "sanity";
2
+ import { getPinnedPageById } from "@/lib/integrations/sanity/pinned-pages";
3
+
4
+ const DEFAULT_DESCRIPTION = "URL path without leading slash.";
5
+ const PINNED_DESCRIPTION =
6
+ "URL path without leading slash. Disabled on 📌 pinned pages.";
7
+
8
+ // Swaps the slug description to a locked-state note on pinned pages.
9
+ // (Sanity descriptions are plain strings, hence the 📌 character.)
10
+ export function SlugField(props: FieldProps) {
11
+ const documentId = useFormValue(["_id"]) as string | undefined;
12
+ const isPinned = getPinnedPageById(documentId) !== null;
13
+
14
+ return props.renderDefault({
15
+ ...props,
16
+ description: isPinned ? PINNED_DESCRIPTION : DEFAULT_DESCRIPTION,
17
+ });
18
+ }
@@ -0,0 +1,14 @@
1
+ import type { SlugInputProps } from "sanity";
2
+
3
+ // Adds a not-allowed cursor to the slug field when it's read-only (pinned pages).
4
+ export function SlugInput(props: SlugInputProps) {
5
+ if (!props.readOnly) {
6
+ return props.renderDefault(props);
7
+ }
8
+
9
+ return (
10
+ <div style={{ cursor: "not-allowed" }}>
11
+ <div style={{ pointerEvents: "none" }}>{props.renderDefault(props)}</div>
12
+ </div>
13
+ );
14
+ }
@@ -0,0 +1,40 @@
1
+ import { useState } from "react";
2
+ import type {
3
+ DocumentActionComponent,
4
+ DocumentActionDialogProps,
5
+ DocumentActionProps,
6
+ } from "sanity";
7
+
8
+ export function createConfirmPublishAction(
9
+ originalPublishAction: DocumentActionComponent,
10
+ ): DocumentActionComponent {
11
+ const ConfirmPublishAction = (props: DocumentActionProps) => {
12
+ const original = originalPublishAction(props);
13
+ const [dialogOpen, setDialogOpen] = useState(false);
14
+
15
+ if (!original) return null;
16
+
17
+ const { dialog: _originalDialog, ...originalWithoutDialog } = original;
18
+ const dialog: DocumentActionDialogProps | false = dialogOpen
19
+ ? {
20
+ type: "confirm",
21
+ tone: "critical",
22
+ message:
23
+ "This will make these changes live on the production site immediately. Continue?",
24
+ onCancel: () => setDialogOpen(false),
25
+ onConfirm: () => {
26
+ setDialogOpen(false);
27
+ original.onHandle?.();
28
+ },
29
+ }
30
+ : false;
31
+
32
+ return {
33
+ ...originalWithoutDialog,
34
+ onHandle: () => setDialogOpen(true),
35
+ dialog,
36
+ };
37
+ };
38
+
39
+ return ConfirmPublishAction;
40
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Sanity Environment Configuration
3
+ *
4
+ * All values have safe defaults to allow the app to build and run
5
+ * without Sanity configured. Use isSanityConfigured() to check
6
+ * if Sanity is properly set up before making API calls.
7
+ */
8
+
9
+ /** Sanity API version - defaults to latest stable */
10
+ export const apiVersion =
11
+ process.env.NEXT_PUBLIC_SANITY_API_VERSION || "2024-03-15";
12
+
13
+ /** Sanity dataset name - defaults to 'production' */
14
+ export const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET || "production";
15
+
16
+ /** Sanity project ID - empty string if not configured */
17
+ export const projectId =
18
+ process.env.NEXT_PUBLIC_SANITY_PROJECT_ID ||
19
+ process.env.SANITY_STUDIO_PROJECT_ID ||
20
+ "";
21
+
22
+ /** Sanity Studio URL for visual editing */
23
+ export const studioUrl =
24
+ process.env.NODE_ENV === "development"
25
+ ? "http://localhost:3000/studio"
26
+ : `${process.env.NEXT_PUBLIC_BASE_URL || ""}/studio`;
27
+
28
+ export const sanityToken = process.env.SANITY_API_READ_TOKEN || "";
29
+
30
+ /** Preview URL for draft mode */
31
+ export const previewURL =
32
+ process.env.NODE_ENV === "development"
33
+ ? "http://localhost:3000"
34
+ : process.env.NEXT_PUBLIC_BASE_URL || "";
@@ -0,0 +1,67 @@
1
+ import { cache } from "react";
2
+ import { fetchSanity } from "@/lib/integrations/sanity/live";
3
+ import {
4
+ COMPANY_DATA_QUERY,
5
+ FOOTER_QUERY,
6
+ NAVBAR_QUERY,
7
+ STRUCTURED_DATA_QUERY,
8
+ } from "@/lib/integrations/sanity/queries";
9
+ import type {
10
+ COMPANY_DATA_QUERY_RESULT,
11
+ FOOTER_QUERY_RESULT,
12
+ NAVBAR_QUERY_RESULT,
13
+ STRUCTURED_DATA_QUERY_RESULT,
14
+ } from "@/lib/integrations/sanity/sanity.types";
15
+
16
+ export type NavbarData = NonNullable<NAVBAR_QUERY_RESULT>;
17
+ export type FooterData = NonNullable<FOOTER_QUERY_RESULT>;
18
+ export type CompanyData = NonNullable<COMPANY_DATA_QUERY_RESULT>;
19
+ export type StructuredData = NonNullable<STRUCTURED_DATA_QUERY_RESULT>;
20
+
21
+ // Singleton document types that, when published, should bust the layout cache.
22
+ export const SANITY_LAYOUT_TAGS = [
23
+ "navbar",
24
+ "footer",
25
+ "companyData",
26
+ "structuredData",
27
+ ];
28
+
29
+ // Wrapped in React cache so components sharing a singleton (e.g. Footer + JsonLd
30
+ // both reading companyData) only hit Sanity once per request.
31
+ export const getNavbarData = cache(async (): Promise<NavbarData | null> => {
32
+ const { data } = await fetchSanity<NavbarData>({
33
+ query: NAVBAR_QUERY,
34
+ tags: ["navbar"],
35
+ });
36
+
37
+ return data ?? null;
38
+ });
39
+
40
+ export const getFooterData = cache(async (): Promise<FooterData | null> => {
41
+ const { data } = await fetchSanity<FooterData>({
42
+ query: FOOTER_QUERY,
43
+ tags: ["footer"],
44
+ });
45
+
46
+ return data ?? null;
47
+ });
48
+
49
+ export const getCompanyData = cache(async (): Promise<CompanyData | null> => {
50
+ const { data } = await fetchSanity<CompanyData>({
51
+ query: COMPANY_DATA_QUERY,
52
+ tags: ["companyData"],
53
+ });
54
+
55
+ return data ?? null;
56
+ });
57
+
58
+ export const getStructuredData = cache(
59
+ async (): Promise<StructuredData | null> => {
60
+ const { data } = await fetchSanity<StructuredData>({
61
+ query: STRUCTURED_DATA_QUERY,
62
+ tags: ["structuredData"],
63
+ });
64
+
65
+ return data ?? null;
66
+ },
67
+ );
@@ -0,0 +1,17 @@
1
+ import { fetchSanity } from "@/lib/integrations/sanity/live";
2
+ import { REDIRECTS_QUERY } from "@/lib/integrations/sanity/queries";
3
+ import type { REDIRECTS_QUERY_RESULT } from "@/lib/integrations/sanity/sanity.types";
4
+
5
+ export type RedirectEntry = REDIRECTS_QUERY_RESULT[number];
6
+
7
+ /** Cache tag invalidated by the revalidate webhook on publish. */
8
+ export const SANITY_REDIRECTS_TAG = "redirect";
9
+
10
+ export const getRedirects = async (): Promise<REDIRECTS_QUERY_RESULT> => {
11
+ const { data } = await fetchSanity<REDIRECTS_QUERY_RESULT>({
12
+ query: REDIRECTS_QUERY,
13
+ tags: [SANITY_REDIRECTS_TAG],
14
+ });
15
+
16
+ return data ?? [];
17
+ };
@@ -0,0 +1,79 @@
1
+ import type { Icon, IconProps } from "@phosphor-icons/react";
2
+ import {
3
+ AppWindowIcon,
4
+ ArrowBendUpRightIcon,
5
+ ArticleNyTimesIcon,
6
+ BracketsCurlyIcon,
7
+ BriefcaseIcon,
8
+ BrowsersIcon,
9
+ BuildingsIcon,
10
+ CalendarBlankIcon,
11
+ CardsIcon,
12
+ FileTextIcon,
13
+ FolderOpenIcon,
14
+ FootprintsIcon,
15
+ LayoutIcon,
16
+ ListBulletsIcon,
17
+ ListIcon,
18
+ PackageIcon,
19
+ PushPinIcon,
20
+ QuestionIcon,
21
+ RocketIcon,
22
+ ShapesIcon,
23
+ ShareNetworkIcon,
24
+ TagIcon,
25
+ TextAlignLeftIcon,
26
+ TreeStructureIcon,
27
+ UserCircleIcon,
28
+ VideoCameraIcon,
29
+ } from "@phosphor-icons/react/dist/ssr";
30
+ import { createElement, forwardRef } from "react";
31
+
32
+ export type SanityStudioIcon = Icon;
33
+
34
+ const asDuotoneIcon = (IconComponent: Icon): SanityStudioIcon => {
35
+ const DuotoneIcon = forwardRef<SVGSVGElement, IconProps>((props, ref) =>
36
+ createElement(IconComponent, { ...props, ref, weight: "duotone" }),
37
+ );
38
+
39
+ DuotoneIcon.displayName = `${IconComponent.displayName ?? "Phosphor"}Duotone`;
40
+
41
+ return DuotoneIcon;
42
+ };
43
+
44
+ export const pageBuilderCardIcon = asDuotoneIcon(PackageIcon);
45
+ export const pageBuilderCardListIcon = asDuotoneIcon(ListIcon);
46
+
47
+ export const authorIcon = asDuotoneIcon(UserCircleIcon);
48
+
49
+ export const pageListIcon = asDuotoneIcon(ListBulletsIcon);
50
+
51
+ export const blogCategoryIcon = asDuotoneIcon(TagIcon);
52
+
53
+ export const componentsFolderIcon = asDuotoneIcon(ShapesIcon);
54
+
55
+ export const companyDataIcon = asDuotoneIcon(BuildingsIcon);
56
+ export const socialLinksIcon = asDuotoneIcon(ShareNetworkIcon);
57
+ export const navbarIcon = asDuotoneIcon(ListIcon);
58
+ export const footerIcon = asDuotoneIcon(FootprintsIcon);
59
+ export const redirectIcon = asDuotoneIcon(ArrowBendUpRightIcon);
60
+ export const layoutFolderIcon = asDuotoneIcon(LayoutIcon);
61
+ export const structuredDataIcon = asDuotoneIcon(TreeStructureIcon);
62
+
63
+ export const faqJsonLdIcon = asDuotoneIcon(QuestionIcon);
64
+ export const eventJsonLdIcon = asDuotoneIcon(CalendarBlankIcon);
65
+ export const videoJsonLdIcon = asDuotoneIcon(VideoCameraIcon);
66
+ export const jobPostingJsonLdIcon = asDuotoneIcon(BriefcaseIcon);
67
+ export const softwareApplicationJsonLdIcon = asDuotoneIcon(AppWindowIcon);
68
+ export const itemListJsonLdIcon = asDuotoneIcon(CardsIcon);
69
+ export const customJsonLdIcon = asDuotoneIcon(BracketsCurlyIcon);
70
+
71
+ export const heroIcon = asDuotoneIcon(RocketIcon);
72
+ export const descriptionIcon = asDuotoneIcon(TextAlignLeftIcon);
73
+ export const contentIcon = asDuotoneIcon(ArticleNyTimesIcon);
74
+
75
+ export const pageIcon = asDuotoneIcon(FileTextIcon);
76
+ export const pinIcon = asDuotoneIcon(PushPinIcon);
77
+ export const pagesFolderIcon = asDuotoneIcon(BrowsersIcon);
78
+ export const pageFolderIcon = asDuotoneIcon(FolderOpenIcon);
79
+ export const contentFolderIcon = asDuotoneIcon(FolderOpenIcon);
@@ -0,0 +1,88 @@
1
+ import { NextResponse } from "next/server";
2
+ import type { QueryParams } from "next-sanity";
3
+ import { defineLive } from "next-sanity/live";
4
+ import { isSanityConfigured } from "@/lib/integrations/check-integration";
5
+ import { client } from "../client";
6
+ import { sanityToken } from "../env";
7
+
8
+ const isConfigured = isSanityConfigured() && client;
9
+
10
+ const liveExports =
11
+ isConfigured && client
12
+ ? defineLive({
13
+ client,
14
+ browserToken: sanityToken ?? false,
15
+ serverToken: sanityToken ?? false,
16
+ })
17
+ : null;
18
+
19
+ export const sanityFetch =
20
+ liveExports?.sanityFetch ?? (async () => ({ data: null }));
21
+
22
+ export const SanityLive = liveExports?.SanityLive ?? (() => null);
23
+
24
+ type SanityFetchOptions = {
25
+ query: string;
26
+ params?: QueryParams;
27
+ tags?: string[];
28
+ stega?: boolean;
29
+ perspective?: "published" | "drafts" | "previewDrafts";
30
+ };
31
+
32
+ export async function fetchSanity<T = unknown>({
33
+ query,
34
+ params = {},
35
+ tags = [],
36
+ stega,
37
+ perspective,
38
+ }: SanityFetchOptions): Promise<{ data: T | null }> {
39
+ const { data } = await sanityFetch({
40
+ query,
41
+ params,
42
+ tags,
43
+ ...(typeof stega === "boolean" ? { stega } : {}),
44
+ ...(perspective ? { perspective } : {}),
45
+ });
46
+
47
+ return { data: (data as T | null) ?? null };
48
+ }
49
+
50
+ export const SanityFetch = async <T,>(
51
+ slug: string,
52
+ query: string,
53
+ label = "Content",
54
+ ): Promise<T | NextResponse> => {
55
+ if (!client) {
56
+ return new NextResponse(`# 503 Unavailable\n\nCMS client unavailable.`, {
57
+ status: 503,
58
+ headers: { "Content-Type": "text/markdown; charset=utf-8" },
59
+ });
60
+ }
61
+
62
+ try {
63
+ const { data } = await sanityFetch({
64
+ query,
65
+ params: { slug },
66
+ });
67
+
68
+ const result = data as T | null;
69
+
70
+ if (!result) {
71
+ return new NextResponse(`# 404 Not Found\n\nPage not found.`, {
72
+ status: 404,
73
+ headers: { "Content-Type": "text/markdown; charset=utf-8" },
74
+ });
75
+ }
76
+
77
+ return result;
78
+ } catch (error) {
79
+ console.error(`Error fetching ${label.toLowerCase()}:`, error);
80
+ return new NextResponse(
81
+ `# 500 Error\n\nFailed to fetch ${label.toLowerCase()}.`,
82
+ {
83
+ status: 500,
84
+ headers: { "Content-Type": "text/markdown; charset=utf-8" },
85
+ },
86
+ );
87
+ }
88
+ };
@@ -0,0 +1,50 @@
1
+ export interface MarkdownRoute {
2
+ /** Regex to match the public-facing URL */
3
+ regex: RegExp;
4
+ /** API route path template (under /api/) */
5
+ apiPath: string;
6
+ /** Public markdown URL template (for Link headers) */
7
+ publicPath: string;
8
+ }
9
+
10
+ /**
11
+ * Routes that serve markdown versions for AI agents.
12
+ * All pages are backed by the shared `page` document type and resolved
13
+ * through the catch-all route handlers.
14
+ *
15
+ * How it works:
16
+ * - `regex`: match the public-facing URL (e.g. /about.md or /about)
17
+ * - `apiPath`: the internal API route that serves the markdown (e.g. /api/about.md)
18
+ * - Proxy rewrites .md URLs and Accept: text/markdown requests to the API route
19
+ * - Route handlers live under /api/ so page.tsx can coexist at the public path
20
+ */
21
+
22
+ /** .md extension routes -- rewrite to API endpoints */
23
+ export const mdExtensionRoutes: MarkdownRoute[] = [
24
+ {
25
+ regex: /^\/(.+)\.md$/,
26
+ apiPath: "/api/[slug].md",
27
+ publicPath: "/[slug].md",
28
+ },
29
+ ];
30
+
31
+ /** Accept: text/markdown routes -- content negotiation for HTML pages */
32
+ export const acceptHeaderRoutes: MarkdownRoute[] = [
33
+ {
34
+ regex: /^\/(.+)$/,
35
+ apiPath: "/api/[slug].md",
36
+ publicPath: "/[slug].md",
37
+ },
38
+ ];
39
+
40
+ /**
41
+ * Paths the proxy should never process.
42
+ * Studio, static files, Next.js internals.
43
+ */
44
+ export const ignoredPaths = [
45
+ "/studio",
46
+ "/api/",
47
+ "/_next/",
48
+ "/favicon.ico",
49
+ "/sitemap.md",
50
+ ];
@@ -0,0 +1,130 @@
1
+ import { contentIcon, pageIcon, type SanityStudioIcon } from "./icons";
2
+ import { singletonComponents } from "./singletons";
3
+
4
+ export type PageBuilderDocumentType =
5
+ | "hero"
6
+ | "description"
7
+ | "blogContent"
8
+ | (typeof singletonComponents)[number]["schemaType"];
9
+
10
+ type PageBuilderReferenceMember = {
11
+ documentType: PageBuilderDocumentType;
12
+ name: string;
13
+ title: string;
14
+ documentId?: string;
15
+ groups: PageBuilderGroupName[];
16
+ };
17
+
18
+ export type PageBuilderGroupName = "generic" | "blogEntry";
19
+
20
+ export const pageBuilderPageTypes: Array<{
21
+ title: string;
22
+ value: PageBuilderGroupName;
23
+ }> = [
24
+ { title: "Generic", value: "generic" },
25
+ { title: "Blog Entry", value: "blogEntry" },
26
+ ];
27
+
28
+ const pageCategoryIcons: Record<PageBuilderGroupName, SanityStudioIcon> = {
29
+ generic: pageIcon,
30
+ blogEntry: contentIcon,
31
+ };
32
+
33
+ export const getPageCategoryIcon = (
34
+ pageType: PageBuilderGroupName | undefined | null,
35
+ ) => pageCategoryIcons[pageType ?? "generic"];
36
+
37
+ const reusablePageBuilderReferenceMembers: PageBuilderReferenceMember[] = [
38
+ {
39
+ documentType: "hero",
40
+ name: "heroReference",
41
+ title: "Heading",
42
+ groups: ["generic"],
43
+ },
44
+ {
45
+ documentType: "description",
46
+ name: "descriptionReference",
47
+ title: "Description",
48
+ groups: ["generic"],
49
+ },
50
+ {
51
+ documentType: "blogContent",
52
+ name: "blogContentReference",
53
+ title: "Blog Content",
54
+ groups: ["blogEntry"],
55
+ },
56
+ ];
57
+
58
+ const singletonPageBuilderReferenceMembers: PageBuilderReferenceMember[] =
59
+ singletonComponents.map((component) => ({
60
+ documentType: component.schemaType,
61
+ name: `${component.schemaType}Reference`,
62
+ title: component.title,
63
+ documentId: component.documentId,
64
+ groups: ["generic"],
65
+ }));
66
+
67
+ export const pageBuilderReferenceMembers: PageBuilderReferenceMember[] = [
68
+ ...reusablePageBuilderReferenceMembers,
69
+ ...singletonPageBuilderReferenceMembers,
70
+ ];
71
+
72
+ export const getPageBuilderReferenceMember = (name: string) =>
73
+ pageBuilderReferenceMembers.find((member) => member.name === name);
74
+
75
+ export const getPageBuilderReferenceMemberByDocumentType = (
76
+ documentType: PageBuilderDocumentType,
77
+ ) =>
78
+ pageBuilderReferenceMembers.find(
79
+ (member) => member.documentType === documentType,
80
+ );
81
+
82
+ const isPageBuilderGroupVisible = (
83
+ pageType: PageBuilderGroupName | undefined,
84
+ group: PageBuilderGroupName,
85
+ ) => (pageType ?? "generic") === group;
86
+
87
+ export const getVisiblePageBuilderReferenceMembers = (
88
+ pageType: PageBuilderGroupName | undefined,
89
+ ) =>
90
+ pageBuilderReferenceMembers.filter((member) =>
91
+ member.groups.some((group) => isPageBuilderGroupVisible(pageType, group)),
92
+ );
93
+
94
+ export const getVisiblePageBuilderInsertMenuGroups = (
95
+ pageType: PageBuilderGroupName | undefined,
96
+ ) =>
97
+ pageBuilderInsertMenuGroups.filter(
98
+ (group) => group.name === (pageType ?? "generic"),
99
+ );
100
+
101
+ export const getInvalidPageBuilderDocumentTypes = (
102
+ pageType: PageBuilderGroupName | undefined,
103
+ documentTypes: PageBuilderDocumentType[],
104
+ ) =>
105
+ documentTypes.filter((documentType) => {
106
+ const member = getPageBuilderReferenceMemberByDocumentType(documentType);
107
+
108
+ if (!member) return false;
109
+
110
+ return !member.groups.some((group) =>
111
+ isPageBuilderGroupVisible(pageType, group),
112
+ );
113
+ });
114
+
115
+ export const pageBuilderInsertMenuGroups = [
116
+ {
117
+ name: "generic",
118
+ title: "Generic",
119
+ of: pageBuilderReferenceMembers
120
+ .filter((member) => member.groups.includes("generic"))
121
+ .map((member) => member.name),
122
+ },
123
+ {
124
+ name: "blogEntry",
125
+ title: "Blog Entry",
126
+ of: pageBuilderReferenceMembers
127
+ .filter((member) => member.groups.includes("blogEntry"))
128
+ .map((member) => member.name),
129
+ },
130
+ ];
@@ -0,0 +1,28 @@
1
+ import type { PageBuilderGroupName } from "@/lib/integrations/sanity/page-builder-config";
2
+
3
+ type PageCategoryParams = {
4
+ slug?: string | null;
5
+ folder?: string | null;
6
+ };
7
+
8
+ export const isEmptyPageSlug = (slug?: string | null) =>
9
+ slug == null || slug === "" || slug === "/";
10
+
11
+ export const getGeneratedPageCategory = ({
12
+ slug,
13
+ folder,
14
+ }: PageCategoryParams): PageBuilderGroupName => {
15
+ if (!isEmptyPageSlug(slug) && folder === "blog") return "blogEntry";
16
+
17
+ return "generic";
18
+ };
19
+
20
+ export const pageCategoryExpression = `
21
+ select(
22
+ defined(slug.current) &&
23
+ slug.current != "" &&
24
+ slug.current != "/" &&
25
+ pageFolder->slug.current == "blog" => "blogEntry",
26
+ "generic"
27
+ )
28
+ `;