bsmnt 0.4.2 → 0.5.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 (188) hide show
  1. package/dist/application/add-integration/index.d.ts +24 -6
  2. package/dist/application/add-integration/index.d.ts.map +1 -1
  3. package/dist/application/add-integration/index.js +130 -82
  4. package/dist/application/add-integration/index.js.map +1 -1
  5. package/dist/application/create-project/map-selection-to-registry.js +1 -1
  6. package/dist/application/create-project/map-selection-to-registry.js.map +1 -1
  7. package/dist/application/shared/project-detection.d.ts +2 -2
  8. package/dist/application/shared/project-detection.d.ts.map +1 -1
  9. package/dist/application/shared/project-detection.js +2 -2
  10. package/dist/application/shared/project-detection.js.map +1 -1
  11. package/dist/core/create/internal-ids.d.ts +4 -1
  12. package/dist/core/create/internal-ids.d.ts.map +1 -1
  13. package/dist/core/create/internal-ids.js +6 -1
  14. package/dist/core/create/internal-ids.js.map +1 -1
  15. package/dist/domain/cms.d.ts +0 -7
  16. package/dist/domain/cms.d.ts.map +1 -1
  17. package/dist/domain/cms.js.map +1 -1
  18. package/dist/domain/forms.d.ts +17 -0
  19. package/dist/domain/forms.d.ts.map +1 -0
  20. package/dist/domain/forms.js +13 -0
  21. package/dist/domain/forms.js.map +1 -0
  22. package/dist/domain/integration.d.ts +30 -0
  23. package/dist/domain/integration.d.ts.map +1 -0
  24. package/dist/domain/integration.js +12 -0
  25. package/dist/domain/integration.js.map +1 -0
  26. package/dist/domain/skills.d.ts +32 -0
  27. package/dist/domain/skills.d.ts.map +1 -1
  28. package/dist/domain/skills.js +39 -0
  29. package/dist/domain/skills.js.map +1 -1
  30. package/dist/index.js +95 -37
  31. package/dist/index.js.map +1 -1
  32. package/dist/infrastructure/agents/register-sanity-mcp.js +9 -6
  33. package/dist/infrastructure/agents/register-sanity-mcp.js.map +1 -1
  34. package/dist/infrastructure/create/apply-package-json.d.ts.map +1 -1
  35. package/dist/infrastructure/create/apply-package-json.js +19 -0
  36. package/dist/infrastructure/create/apply-package-json.js.map +1 -1
  37. package/dist/infrastructure/create/install-dependencies.d.ts.map +1 -1
  38. package/dist/infrastructure/create/install-dependencies.js +4 -0
  39. package/dist/infrastructure/create/install-dependencies.js.map +1 -1
  40. package/dist/infrastructure/create/merge-registry.d.ts.map +1 -1
  41. package/dist/infrastructure/create/merge-registry.js +8 -0
  42. package/dist/infrastructure/create/merge-registry.js.map +1 -1
  43. package/dist/infrastructure/create/setup-agent.d.ts.map +1 -1
  44. package/dist/infrastructure/create/setup-agent.js +29 -6
  45. package/dist/infrastructure/create/setup-agent.js.map +1 -1
  46. package/dist/infrastructure/create/setup-sanity.d.ts.map +1 -1
  47. package/dist/infrastructure/create/setup-sanity.js +3 -0
  48. package/dist/infrastructure/create/setup-sanity.js.map +1 -1
  49. package/dist/infrastructure/integrations/inject-integration.d.ts +3 -3
  50. package/dist/infrastructure/integrations/inject-integration.d.ts.map +1 -1
  51. package/dist/infrastructure/integrations/inject-integration.js +81 -37
  52. package/dist/infrastructure/integrations/inject-integration.js.map +1 -1
  53. package/dist/modules/features/cms/sanity/config.d.ts.map +1 -1
  54. package/dist/modules/features/cms/sanity/config.js +11 -5
  55. package/dist/modules/features/cms/sanity/config.js.map +1 -1
  56. package/dist/modules/features/cms/sanity/index.js +8 -2
  57. package/dist/modules/features/cms/sanity/index.js.map +1 -1
  58. package/dist/modules/features/cms/sanity/mergers/ci-workflow.d.ts +13 -0
  59. package/dist/modules/features/cms/sanity/mergers/ci-workflow.d.ts.map +1 -0
  60. package/dist/modules/features/cms/sanity/mergers/ci-workflow.js +38 -0
  61. package/dist/modules/features/cms/sanity/mergers/ci-workflow.js.map +1 -0
  62. package/dist/modules/features/cms/sanity/mergers/knip.d.ts +12 -0
  63. package/dist/modules/features/cms/sanity/mergers/knip.d.ts.map +1 -0
  64. package/dist/modules/features/cms/sanity/mergers/knip.js +38 -0
  65. package/dist/modules/features/cms/sanity/mergers/knip.js.map +1 -0
  66. package/dist/modules/features/cms/sanity/mergers/layout.d.ts.map +1 -1
  67. package/dist/modules/features/cms/sanity/mergers/layout.js +7 -3
  68. package/dist/modules/features/cms/sanity/mergers/layout.js.map +1 -1
  69. package/dist/modules/features/cms/sanity/mergers/sitemap.d.ts +3 -3
  70. package/dist/modules/features/cms/sanity/mergers/sitemap.d.ts.map +1 -1
  71. package/dist/modules/features/cms/sanity/mergers/sitemap.js +48 -62
  72. package/dist/modules/features/cms/sanity/mergers/sitemap.js.map +1 -1
  73. package/dist/modules/features/cms/sanity-pagebuilder/config.d.ts.map +1 -1
  74. package/dist/modules/features/cms/sanity-pagebuilder/config.js +8 -5
  75. package/dist/modules/features/cms/sanity-pagebuilder/config.js.map +1 -1
  76. package/dist/modules/features/cms/sanity-pagebuilder/index.js +2 -2
  77. package/dist/modules/features/cms/sanity-pagebuilder/index.js.map +1 -1
  78. package/dist/modules/features/forms/hubspot/config.d.ts +31 -0
  79. package/dist/modules/features/forms/hubspot/config.d.ts.map +1 -0
  80. package/dist/modules/features/forms/hubspot/config.js +25 -0
  81. package/dist/modules/features/forms/hubspot/config.js.map +1 -0
  82. package/dist/paths.d.ts +8 -0
  83. package/dist/paths.d.ts.map +1 -1
  84. package/dist/paths.js +8 -0
  85. package/dist/paths.js.map +1 -1
  86. package/index.js +2 -2
  87. package/package.json +17 -9
  88. package/src/assets/check-node-version.mjs +22 -0
  89. package/src/modules/features/cms/sanity/files/app/blog/page.tsx +54 -0
  90. package/src/modules/features/cms/sanity/files/app/feed.xml/route.ts +36 -0
  91. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/components/disable-draft-mode.tsx +3 -3
  92. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/schemas/article.ts +7 -7
  93. package/src/modules/features/cms/sanity/files/lib/scripts/setup-ci-secrets.ts +117 -0
  94. package/src/modules/features/cms/sanity/files/lib/utils/rss.ts +96 -0
  95. package/src/modules/features/cms/sanity-pagebuilder/files/.knip.json +8 -0
  96. package/src/modules/features/cms/sanity-pagebuilder/files/app/api/[[...slug]]/route.ts +100 -0
  97. package/src/modules/features/cms/sanity-pagebuilder/files/app/api/draft-mode/disable/route.ts +7 -0
  98. package/src/modules/features/cms/sanity-pagebuilder/files/app/api/draft-mode/enable/route.ts +20 -0
  99. package/src/modules/features/cms/sanity-pagebuilder/files/app/api/revalidate/route.ts +126 -0
  100. package/src/modules/features/cms/sanity-pagebuilder/files/app/feed.xml/route.ts +40 -0
  101. package/src/modules/features/cms/sanity-pagebuilder/files/app/sitemap.md/route.ts +124 -0
  102. package/src/modules/features/cms/sanity-pagebuilder/files/app/studio/[[...tool]]/page.tsx +8 -0
  103. package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/draft-mode-toggle.tsx +27 -0
  104. package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/rich-text.tsx +87 -0
  105. package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/visual-editing.tsx +28 -0
  106. package/src/modules/features/cms/sanity-pagebuilder/files/components/ui/sanity-image/index.tsx +41 -0
  107. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/check-integration.ts +5 -0
  108. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/client.ts +27 -0
  109. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/disable-draft-mode.tsx +23 -0
  110. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/page-builder-input.tsx +39 -0
  111. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/page-category-input.tsx +50 -0
  112. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/slug-field.tsx +18 -0
  113. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/slug-input.tsx +14 -0
  114. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/confirm-publish-action.ts +40 -0
  115. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/env.ts +34 -0
  116. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/fetchers/layout.ts +48 -0
  117. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/fetchers/redirects.ts +17 -0
  118. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/icons.ts +62 -0
  119. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/live/index.tsx +88 -0
  120. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/markdown-proxy.config.ts +50 -0
  121. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-builder-config.ts +130 -0
  122. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-category.ts +28 -0
  123. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-tree-pane.tsx +420 -0
  124. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/pinned-pages.ts +54 -0
  125. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/presentation.ts +119 -0
  126. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/queries.ts +385 -0
  127. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/sanity.cli.ts +29 -0
  128. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/sanity.config.ts +113 -0
  129. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/index.ts +4 -0
  130. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/blog-content.ts +89 -0
  131. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/description.ts +29 -0
  132. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/hero.ts +28 -0
  133. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/singleton/content-collection.ts +45 -0
  134. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/content/author.ts +70 -0
  135. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/content/blog-category.ts +55 -0
  136. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/index.ts +96 -0
  137. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/company-data.ts +62 -0
  138. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/footer.ts +79 -0
  139. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/navbar.ts +75 -0
  140. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/redirect.ts +120 -0
  141. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/link.ts +125 -0
  142. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/logo-field.ts +9 -0
  143. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/metadata.ts +68 -0
  144. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/nav-objects.ts +192 -0
  145. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/page-builder.ts +39 -0
  146. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/page-folder.ts +124 -0
  147. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/page.ts +242 -0
  148. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/richText.ts +63 -0
  149. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/singletons.ts +39 -0
  150. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/structure.ts +330 -0
  151. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/utils/image.ts +8 -0
  152. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/utils/link.ts +140 -0
  153. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/utils/page-builder-markdown.ts +81 -0
  154. package/src/modules/features/cms/sanity-pagebuilder/files/lib/scripts/sanity-typegen.ts +45 -0
  155. package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/check-redirect.ts +30 -0
  156. package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/json-ld.tsx +212 -0
  157. package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/metadata.ts +167 -0
  158. package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/rss.ts +96 -0
  159. package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/sitemap.ts +40 -0
  160. package/src/modules/features/cms/sanity-pagebuilder/files/proxy.ts +81 -0
  161. package/src/templates/next-default/.github/workflows/ci.yml +62 -0
  162. package/src/templates/next-default/README.md +22 -0
  163. package/src/templates/next-default/knip.json +14 -0
  164. package/src/templates/next-default/lefthook.yml +5 -0
  165. package/src/templates/next-default/package.json +8 -4
  166. package/src/templates/next-experiments/.github/workflows/ci.yml +62 -0
  167. package/src/templates/next-experiments/README.md +22 -0
  168. package/src/templates/next-experiments/knip.json +14 -0
  169. package/src/templates/next-experiments/lefthook.yml +30 -0
  170. package/src/templates/next-experiments/package.json +10 -2
  171. package/src/templates/next-pagebuilder/.github/workflows/ci.yml +70 -0
  172. package/src/templates/next-pagebuilder/README.md +34 -0
  173. package/src/templates/next-pagebuilder/app/(content)/[[...slug]]/page.tsx +6 -1
  174. package/src/templates/next-pagebuilder/app/(content)/layout.tsx +3 -12
  175. package/src/templates/next-pagebuilder/components/layout/footer/index.tsx +11 -10
  176. package/src/templates/next-pagebuilder/components/layout/header/index.tsx +5 -3
  177. package/src/templates/next-pagebuilder/components/layout/json-ld/index.tsx +9 -10
  178. package/src/templates/next-pagebuilder/components/layout/wrapper/index.tsx +3 -11
  179. package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/index.tsx +1 -1
  180. package/src/templates/next-pagebuilder/knip.json +27 -0
  181. package/src/templates/next-pagebuilder/lefthook.yml +5 -0
  182. package/src/templates/next-pagebuilder/lib/scripts/setup-ci-secrets.ts +117 -0
  183. package/src/templates/next-pagebuilder/package.json +9 -3
  184. package/src/templates/next-webgl/.github/workflows/ci.yml +62 -0
  185. package/src/templates/next-webgl/README.md +22 -0
  186. package/src/templates/next-webgl/knip.json +13 -0
  187. package/src/templates/next-webgl/lefthook.yml +5 -0
  188. package/src/templates/next-webgl/package.json +8 -4
@@ -1,21 +1,12 @@
1
1
  import { JsonLd } from "@/components/layout/json-ld"
2
2
  import { Wrapper } from "@/components/layout/wrapper"
3
3
  import { SanityVisualEditing } from "@/components/sanity/visual-editing"
4
- import { getSiteLayoutData } from "@/lib/integrations/sanity/fetchers/layout"
5
-
6
- const Layout = async ({ children }: { children: React.ReactNode }) => {
7
- const layoutData = await getSiteLayoutData()
8
4
 
5
+ const Layout = ({ children }: { children: React.ReactNode }) => {
9
6
  return (
10
7
  <>
11
- <JsonLd navbar={layoutData.navbar} companyData={layoutData.companyData} />
12
- <Wrapper
13
- navbar={layoutData.navbar}
14
- footer={layoutData.footer}
15
- companyData={layoutData.companyData}
16
- >
17
- {children}
18
- </Wrapper>
8
+ <JsonLd />
9
+ <Wrapper>{children}</Wrapper>
19
10
  <SanityVisualEditing />
20
11
  </>
21
12
  )
@@ -1,21 +1,22 @@
1
1
  import { Link } from "@/components/ui/link"
2
2
  import { SanityImage } from "@/components/ui/sanity-image"
3
- import type {
4
- CompanyData,
5
- FooterData,
3
+ import {
4
+ type CompanyData,
5
+ type FooterData,
6
+ getCompanyData,
7
+ getFooterData,
6
8
  } from "@/lib/integrations/sanity/fetchers/layout"
7
9
 
8
10
  type FooterLinkGroup = NonNullable<FooterData["links"]>[number]
9
11
  type FooterLinkItem = NonNullable<FooterLinkGroup["items"]>[number]
10
12
  type SocialLink = NonNullable<CompanyData["socialLinks"]>[number]
11
13
 
12
- export const Footer = ({
13
- footer,
14
- companyData,
15
- }: {
16
- footer: FooterData | null
17
- companyData: CompanyData | null
18
- }) => {
14
+ export const Footer = async () => {
15
+ const [footer, companyData] = await Promise.all([
16
+ getFooterData(),
17
+ getCompanyData(),
18
+ ])
19
+
19
20
  const links = footer?.links ?? []
20
21
 
21
22
  return (
@@ -1,6 +1,8 @@
1
- import type { NavbarData } from "@/lib/integrations/sanity/fetchers/layout"
1
+ import { getNavbarData } from "@/lib/integrations/sanity/fetchers/layout"
2
2
  import { HeaderClient } from "./header-client"
3
3
 
4
- export const Header = ({ data }: { data: NavbarData | null }) => {
5
- return <HeaderClient data={data} />
4
+ export const Header = async () => {
5
+ const navbar = await getNavbarData()
6
+
7
+ return <HeaderClient data={navbar} />
6
8
  }
@@ -1,7 +1,7 @@
1
1
  import { stegaClean } from "next-sanity"
2
- import type {
3
- CompanyData,
4
- NavbarData,
2
+ import {
3
+ getCompanyData,
4
+ getNavbarData,
5
5
  } from "@/lib/integrations/sanity/fetchers/layout"
6
6
  import {
7
7
  generateOrganizationJsonLd,
@@ -12,13 +12,12 @@ import {
12
12
  const APP_DESCRIPTION =
13
13
  "Basement is the AI-native platform for audit and advisory firms. Automate engagement workflows using AI agents trusted by 50% of top 100 firms."
14
14
 
15
- export const JsonLd = ({
16
- navbar,
17
- companyData,
18
- }: {
19
- navbar: NavbarData | null
20
- companyData: CompanyData | null
21
- }) => {
15
+ export const JsonLd = async () => {
16
+ const [navbar, companyData] = await Promise.all([
17
+ getNavbarData(),
18
+ getCompanyData(),
19
+ ])
20
+
22
21
  const sameAs = companyData?.socialLinks
23
22
  ?.map((link) => stegaClean(link.url))
24
23
  .filter((url): url is string => Boolean(url))
@@ -1,13 +1,8 @@
1
1
  import Link from "next/link"
2
2
  import { Footer } from "@/components/layout/footer"
3
3
  import { Header } from "@/components/layout/header"
4
- import type { SiteLayoutData } from "@/lib/integrations/sanity/fetchers/layout"
5
4
  import { cn } from "@/lib/styles/cn"
6
5
 
7
- interface WrapperProps
8
- extends React.HTMLAttributes<HTMLDivElement>,
9
- SiteLayoutData {}
10
-
11
6
  const SkipToMainContent = () => (
12
7
  <Link
13
8
  href="#main-content"
@@ -20,14 +15,11 @@ const SkipToMainContent = () => (
20
15
  export const Wrapper = ({
21
16
  children,
22
17
  className,
23
- navbar,
24
- footer,
25
- companyData,
26
18
  ...props
27
- }: WrapperProps) => (
19
+ }: React.HTMLAttributes<HTMLDivElement>) => (
28
20
  <>
29
21
  <SkipToMainContent />
30
- <Header data={navbar} />
22
+ <Header />
31
23
  <main
32
24
  id="main-content"
33
25
  className={cn("relative flex grow flex-col", className)}
@@ -35,6 +27,6 @@ export const Wrapper = ({
35
27
  >
36
28
  {children}
37
29
  </main>
38
- <Footer footer={footer} companyData={companyData} />
30
+ <Footer />
39
31
  </>
40
32
  )
@@ -190,7 +190,7 @@ export async function ContentCollection({
190
190
  params: {
191
191
  category: canonicalCategory,
192
192
  offset: fetchOffset,
193
- limit: fetchLimit,
193
+ end: fetchOffset + fetchLimit,
194
194
  },
195
195
  tags: ["blog-articles"],
196
196
  })
@@ -0,0 +1,27 @@
1
+ {
2
+ "$schema": "https://unpkg.com/knip@6/schema.json",
3
+ "entry": [
4
+ "lib/integrations/sanity/**/*.{ts,tsx}",
5
+ "lib/scripts/**/*.{ts,tsx}",
6
+ "lib/utils/**/*.{ts,tsx}",
7
+ "components/**/*.{ts,tsx}",
8
+ "app/actions/**/*.{ts,tsx}"
9
+ ],
10
+ "ignoreDependencies": [
11
+ "zod",
12
+ "react-use",
13
+ "@portabletext/types",
14
+ "@portabletext/react",
15
+ "@portabletext/markdown",
16
+ "@sanity/vision",
17
+ "@sanity/visual-editing",
18
+ "@sanity/ui",
19
+ "@svgr/webpack",
20
+ "postcss-functions",
21
+ "clsx",
22
+ "rxjs"
23
+ ],
24
+ "ignoreBinaries": [
25
+ "gh"
26
+ ]
27
+ }
@@ -14,6 +14,11 @@ pre-commit:
14
14
  run: bunx tsgo --noEmit --incremental false
15
15
  stage_fixed: false
16
16
 
17
+ pre-push:
18
+ commands:
19
+ knip:
20
+ run: bun run knip
21
+
17
22
  post-merge:
18
23
  commands:
19
24
  install-deps:
@@ -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
+ }
@@ -3,14 +3,19 @@
3
3
  "description": "Basement Next.js starter template",
4
4
  "version": "0.1.0",
5
5
  "private": true,
6
+ "packageManager": "bun@1.3.14",
7
+ "engines": {
8
+ "node": ">=24"
9
+ },
6
10
  "scripts": {
7
11
  "analyze": "cross-env ANALYZE=true bun run build",
8
12
  "analyze:experimental": "next experimental-analyze",
9
13
  "build": "next build",
14
+ "ci:secrets": "bun ./lib/scripts/setup-ci-secrets.ts",
10
15
  "dev": "next dev",
11
16
  "format": "biome format --write .",
12
- "knip": "bunx knip",
13
- "knip:fix": "bunx knip --fix",
17
+ "knip": "knip",
18
+ "knip:fix": "knip --fix",
14
19
  "lighthouse": "bunx @unlighthouse/cli --site http://localhost:3000",
15
20
  "lint": "biome lint --max-diagnostics=200",
16
21
  "lint:fix": "biome check --write --unsafe --max-diagnostics=200",
@@ -37,12 +42,13 @@
37
42
  "@svgr/webpack": "^8.1.0",
38
43
  "@tailwindcss/postcss": "^4.2.2",
39
44
  "@types/bun": "^1.3.11",
40
- "@types/node": "^25.5.0",
45
+ "@types/node": "^24.13.1",
41
46
  "@types/react": "^19.2.14",
42
47
  "@types/react-dom": "^19.2.3",
43
48
  "@typescript/native-preview": "^7.0.0-dev.20260323.1",
44
49
  "babel-plugin-react-compiler": "^1.0.0",
45
50
  "cross-env": "^10.1.0",
51
+ "knip": "^6.16.1",
46
52
  "lefthook": "^1.13.0",
47
53
  "postcss-functions": "^4.0.2",
48
54
  "postcss-preset-env": "^11.2.0",
@@ -0,0 +1,62 @@
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:compare
45
+
46
+ - name: Lint
47
+ run: bun run lint
48
+
49
+ - name: Build
50
+ run: bun run build
51
+
52
+ # Runs your tests if any exist; a fresh project with none passes cleanly.
53
+ - name: Test
54
+ run: |
55
+ if git ls-files '*.test.*' '*.spec.*' '*_test.*' '*_spec.*' | grep -q .; then
56
+ bun test
57
+ else
58
+ echo "No test files found — skipping bun test."
59
+ fi
60
+
61
+ - name: Knip
62
+ run: bun run knip
@@ -49,3 +49,25 @@ lib/
49
49
  - Edit `lib/styles/tokens.css` for theme variables, breakpoints, and custom Tailwind utilities.
50
50
  - Edit `lib/styles/global.css` for reset rules and app-wide global styles.
51
51
  - `lib/styles/fonts.ts` handles font variables.
52
+
53
+ ## Continuous Integration
54
+
55
+ A GitHub Actions workflow (`.github/workflows/ci.yml`) runs on every pull request to `main` and on pushes to `main`. It runs type checking, linting, the production build, tests, and `knip`.
56
+
57
+ The workflow takes its runtime versions from your manifests, so there's a single source of truth and nothing to keep in sync by hand:
58
+
59
+ - **Node** comes from `.nvmrc`.
60
+ - **Bun** comes from the `packageManager` field in `package.json`.
61
+
62
+ It also caches Bun's install cache between runs, and the test step is a no-op until you add real test files.
63
+
64
+ ### Branch protection
65
+
66
+ To block merges when CI fails, enable branch protection on `main`:
67
+
68
+ 1. Go to **Settings → Branches → Add branch ruleset** (or **Add rule**).
69
+ 2. Target the `main` branch.
70
+ 3. Enable **Require status checks to pass before merging** and select the **CI** check.
71
+ 4. (Recommended) Enable **Require a pull request before merging**.
72
+
73
+ Pull requests then cannot be merged until the CI workflow passes.
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "https://unpkg.com/knip@6/schema.json",
3
+ "entry": [
4
+ "lib/scripts/**/*.{ts,tsx}",
5
+ "lib/hooks/**/*.{ts,tsx}",
6
+ "lib/utils/**/*.{ts,tsx}",
7
+ "components/**/*.{ts,tsx}"
8
+ ],
9
+ "ignoreDependencies": [
10
+ "zod",
11
+ "@svgr/webpack"
12
+ ]
13
+ }
@@ -14,6 +14,11 @@ pre-commit:
14
14
  run: bunx tsgo --noEmit --incremental false
15
15
  stage_fixed: false
16
16
 
17
+ pre-push:
18
+ commands:
19
+ knip:
20
+ run: bun run knip
21
+
17
22
  post-merge:
18
23
  commands:
19
24
  install-deps:
@@ -3,7 +3,10 @@
3
3
  "description": "Basement Next.js starter template",
4
4
  "version": "0.1.0",
5
5
  "private": true,
6
- "packageManager": "bun@1.3.5",
6
+ "packageManager": "bun@1.3.14",
7
+ "engines": {
8
+ "node": ">=24"
9
+ },
7
10
  "scripts": {
8
11
  "analyze": "cross-env ANALYZE=true bun run build",
9
12
  "analyze:experimental": "next experimental-analyze",
@@ -12,8 +15,8 @@
12
15
  "dev:https": "bun ./lib/scripts/dev.ts --https",
13
16
  "dev:inspect": "bun ./lib/scripts/dev.ts --inspect",
14
17
  "format": "biome format --write .",
15
- "knip": "bunx knip",
16
- "knip:fix": "bunx knip --fix",
18
+ "knip": "knip",
19
+ "knip:fix": "knip --fix",
17
20
  "lighthouse": "bunx @unlighthouse/cli --site http://localhost:3000",
18
21
  "lint": "biome lint --max-diagnostics=200",
19
22
  "lint:fix": "biome check --write --unsafe --max-diagnostics=200",
@@ -42,13 +45,14 @@
42
45
  "@svgr/webpack": "^8.1.0",
43
46
  "@tailwindcss/postcss": "^4",
44
47
  "@types/bun": "^1.3.6",
45
- "@types/node": "^20.19.30",
48
+ "@types/node": "^24.13.1",
46
49
  "@types/react": "^19.2.9",
47
50
  "@types/react-dom": "^19.2.3",
48
51
  "@types/three": "^0.182.0",
49
52
  "@typescript/native-preview": "^7.0.0-dev.20260122.3",
50
53
  "babel-plugin-react-compiler": "^1.0.0",
51
54
  "cross-env": "^10.1.0",
55
+ "knip": "^6.16.1",
52
56
  "lefthook": "^1.13.0",
53
57
  "postcss-preset-env": "^10.6.1",
54
58
  "schema-dts": "^2.0.0",