bsmnt 0.0.2 → 0.1.1

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 (346) hide show
  1. package/.github/workflows/release.yml +2 -0
  2. package/CHANGELOG.md +31 -0
  3. package/CLAUDE.md +42 -24
  4. package/README.md +33 -12
  5. package/biome.json +1 -0
  6. package/bun.lock +2 -1
  7. package/docs/architecture.drawio +250 -0
  8. package/docs/architecture.mermaid +85 -0
  9. package/package.json +42 -42
  10. package/{bin → packages/cli/bin}/index.js +28 -31
  11. package/packages/cli/package.json +16 -0
  12. package/{src → packages/cli/src}/commands/add-integration.js +22 -37
  13. package/{src → packages/cli/src}/commands/create.js +125 -131
  14. package/packages/create-basement-app/integrations/sanity/config.js +46 -0
  15. package/{src → packages/create-basement-app/integrations/sanity}/mergers/check-integration-merger.js +1 -1
  16. package/{src → packages/create-basement-app/integrations/sanity}/mergers/layout-merger.js +1 -1
  17. package/{src → packages/create-basement-app/integrations/sanity}/mergers/sitemap-merger.js +1 -1
  18. package/packages/create-basement-app/package.json +10 -0
  19. package/packages/create-basement-app/src/configs/animations.js +28 -0
  20. package/packages/create-basement-app/src/index.js +15 -0
  21. package/packages/create-basement-app/src/mergers/check-integration-merger.js +105 -0
  22. package/{src → packages/create-basement-app/src}/mergers/config.js +5 -33
  23. package/{src → packages/create-basement-app/src}/mergers/index.js +89 -98
  24. package/packages/create-basement-app/src/mergers/layout-merger.js +223 -0
  25. package/packages/create-basement-app/src/mergers/sitemap-merger.js +121 -0
  26. package/packages/create-basement-app/template-hooks/config.js +38 -0
  27. package/packages/create-basement-app/templates/default/.biome/plugins/README.md +21 -0
  28. package/packages/create-basement-app/templates/default/.biome/plugins/no-anchor-element.grit +12 -0
  29. package/packages/create-basement-app/templates/default/.biome/plugins/no-relative-parent-imports.grit +10 -0
  30. package/packages/create-basement-app/templates/default/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
  31. package/packages/create-basement-app/templates/default/.cursor/rules/README.md +184 -0
  32. package/packages/create-basement-app/templates/default/.cursor/rules/architecture.mdc +437 -0
  33. package/packages/create-basement-app/templates/default/.cursor/rules/components.mdc +436 -0
  34. package/packages/create-basement-app/templates/default/.cursor/rules/integrations.mdc +447 -0
  35. package/packages/create-basement-app/templates/default/.cursor/rules/main.mdc +278 -0
  36. package/packages/create-basement-app/templates/default/.cursor/rules/styling.mdc +433 -0
  37. package/packages/create-basement-app/templates/default/.editorconfig +40 -0
  38. package/packages/create-basement-app/templates/default/.env.example +81 -0
  39. package/packages/create-basement-app/templates/default/.gitattributes +19 -0
  40. package/packages/create-basement-app/templates/default/.github/PULL_REQUEST_TEMPLATE.md +14 -0
  41. package/packages/create-basement-app/templates/default/.github/workflows/lighthouse-to-slack.yml +136 -0
  42. package/packages/create-basement-app/templates/default/.vscode/extensions.json +20 -0
  43. package/packages/create-basement-app/templates/default/.vscode/settings.json +105 -0
  44. package/packages/create-basement-app/templates/default/README.md +221 -0
  45. package/packages/create-basement-app/templates/default/_gitignore +67 -0
  46. package/packages/create-basement-app/templates/default/app/favicon.ico +0 -0
  47. package/packages/create-basement-app/templates/default/app/layout.tsx +104 -0
  48. package/packages/create-basement-app/templates/default/app/page.tsx +275 -0
  49. package/packages/create-basement-app/templates/default/app/robots.ts +15 -0
  50. package/packages/create-basement-app/templates/default/app/sitemap.ts +16 -0
  51. package/packages/create-basement-app/templates/default/biome.json +250 -0
  52. package/packages/create-basement-app/templates/default/components/basement.svg +1 -0
  53. package/packages/create-basement-app/templates/default/components/layout/footer/index.tsx +27 -0
  54. package/packages/create-basement-app/templates/default/components/layout/header/index.tsx +11 -0
  55. package/packages/create-basement-app/templates/default/components/layout/theme/index.tsx +66 -0
  56. package/packages/create-basement-app/templates/default/components/layout/wrapper/index.tsx +65 -0
  57. package/packages/create-basement-app/templates/default/components/ui/README.md +77 -0
  58. package/packages/create-basement-app/templates/default/components/ui/image/README.md +37 -0
  59. package/packages/create-basement-app/templates/default/components/ui/image/index.tsx +224 -0
  60. package/packages/create-basement-app/templates/default/components/ui/link/index.tsx +146 -0
  61. package/packages/create-basement-app/templates/default/lib/README.md +33 -0
  62. package/packages/create-basement-app/templates/default/lib/hooks/index.ts +12 -0
  63. package/packages/create-basement-app/templates/default/lib/hooks/use-device-detection.ts +81 -0
  64. package/packages/create-basement-app/templates/default/lib/hooks/use-media-breakpoint.ts +15 -0
  65. package/packages/create-basement-app/templates/default/lib/hooks/use-prefetch.ts +74 -0
  66. package/packages/create-basement-app/templates/default/lib/scripts/dev.ts +52 -0
  67. package/packages/create-basement-app/templates/default/lib/scripts/generate-component.ts +322 -0
  68. package/packages/create-basement-app/templates/default/lib/scripts/generate-page.ts +193 -0
  69. package/packages/create-basement-app/templates/default/lib/scripts/generate.ts +79 -0
  70. package/packages/create-basement-app/templates/default/lib/scripts/utils.ts +246 -0
  71. package/packages/create-basement-app/templates/default/lib/store/app.ts +11 -0
  72. package/packages/create-basement-app/templates/default/lib/store/index.ts +11 -0
  73. package/packages/create-basement-app/templates/default/lib/styles/README.md +64 -0
  74. package/packages/create-basement-app/templates/default/lib/styles/cn.ts +7 -0
  75. package/packages/create-basement-app/templates/default/lib/styles/colors.ts +63 -0
  76. package/packages/create-basement-app/templates/default/lib/styles/config.ts +34 -0
  77. package/packages/create-basement-app/templates/default/lib/styles/css/global.css +85 -0
  78. package/packages/create-basement-app/templates/default/lib/styles/css/index.css +6 -0
  79. package/packages/create-basement-app/templates/default/lib/styles/css/reset.css +166 -0
  80. package/packages/create-basement-app/templates/default/lib/styles/css/root.css +68 -0
  81. package/packages/create-basement-app/templates/default/lib/styles/css/tailwind.css +132 -0
  82. package/packages/create-basement-app/templates/default/lib/styles/easings.ts +21 -0
  83. package/packages/create-basement-app/templates/default/lib/styles/fonts.ts +28 -0
  84. package/packages/create-basement-app/templates/default/lib/styles/index.ts +12 -0
  85. package/packages/create-basement-app/templates/default/lib/styles/layout.mjs +27 -0
  86. package/packages/create-basement-app/templates/default/lib/styles/scripts/README.md +29 -0
  87. package/packages/create-basement-app/templates/default/lib/styles/scripts/generate-root.ts +57 -0
  88. package/packages/create-basement-app/templates/default/lib/styles/scripts/generate-tailwind.ts +162 -0
  89. package/packages/create-basement-app/templates/default/lib/styles/scripts/postcss-functions.mjs +168 -0
  90. package/packages/create-basement-app/templates/default/lib/styles/scripts/setup-styles.ts +24 -0
  91. package/packages/create-basement-app/templates/default/lib/styles/scripts/utils.ts +20 -0
  92. package/packages/create-basement-app/templates/default/lib/styles/typography.ts +36 -0
  93. package/packages/create-basement-app/templates/default/lib/utils/README.md +40 -0
  94. package/packages/create-basement-app/templates/default/lib/utils/css.d.ts +21 -0
  95. package/packages/create-basement-app/templates/default/lib/utils/easings.ts +240 -0
  96. package/packages/create-basement-app/templates/default/lib/utils/fetch.ts +84 -0
  97. package/packages/create-basement-app/templates/default/lib/utils/math.test.ts +221 -0
  98. package/packages/create-basement-app/templates/default/lib/utils/math.ts +236 -0
  99. package/packages/create-basement-app/templates/default/lib/utils/metadata.ts +126 -0
  100. package/packages/create-basement-app/templates/default/lib/utils/strings.test.ts +166 -0
  101. package/packages/create-basement-app/templates/default/lib/utils/strings.ts +246 -0
  102. package/packages/create-basement-app/templates/default/lib/utils/types.d.ts +15 -0
  103. package/packages/create-basement-app/templates/default/lib/utils/viewport.test.ts +256 -0
  104. package/packages/create-basement-app/templates/default/lib/utils/viewport.ts +193 -0
  105. package/packages/create-basement-app/templates/default/next.config.ts +142 -0
  106. package/packages/create-basement-app/templates/default/package.json +62 -0
  107. package/packages/create-basement-app/templates/default/postcss.config.mjs +42 -0
  108. package/packages/create-basement-app/templates/default/public/fonts/geist/Geist-Mono.woff2 +0 -0
  109. package/packages/create-basement-app/templates/default/tsconfig.json +43 -0
  110. package/packages/create-basement-app/templates/experiment/.biome/plugins/README.md +21 -0
  111. package/packages/create-basement-app/templates/experiment/.biome/plugins/no-anchor-element.grit +12 -0
  112. package/packages/create-basement-app/templates/experiment/.biome/plugins/no-relative-parent-imports.grit +10 -0
  113. package/packages/create-basement-app/templates/experiment/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
  114. package/packages/create-basement-app/templates/experiment/.cursor/rules/README.md +184 -0
  115. package/packages/create-basement-app/templates/experiment/.cursor/rules/architecture.mdc +437 -0
  116. package/packages/create-basement-app/templates/experiment/.cursor/rules/components.mdc +436 -0
  117. package/packages/create-basement-app/templates/experiment/.cursor/rules/integrations.mdc +447 -0
  118. package/packages/create-basement-app/templates/experiment/.cursor/rules/main.mdc +278 -0
  119. package/packages/create-basement-app/templates/experiment/.cursor/rules/styling.mdc +433 -0
  120. package/packages/create-basement-app/templates/experiment/.editorconfig +40 -0
  121. package/packages/create-basement-app/templates/experiment/.env.example +81 -0
  122. package/packages/create-basement-app/templates/experiment/.gitattributes +19 -0
  123. package/packages/create-basement-app/templates/experiment/.github/PULL_REQUEST_TEMPLATE.md +14 -0
  124. package/packages/create-basement-app/templates/experiment/.github/workflows/lighthouse-to-slack.yml +136 -0
  125. package/packages/create-basement-app/templates/experiment/.vscode/extensions.json +20 -0
  126. package/packages/create-basement-app/templates/experiment/.vscode/settings.json +105 -0
  127. package/packages/create-basement-app/templates/experiment/README.md +221 -0
  128. package/packages/create-basement-app/templates/experiment/_gitignore +67 -0
  129. package/packages/create-basement-app/templates/experiment/app/favicon.ico +0 -0
  130. package/packages/create-basement-app/templates/experiment/app/layout.tsx +104 -0
  131. package/packages/create-basement-app/templates/experiment/app/page.tsx +275 -0
  132. package/packages/create-basement-app/templates/experiment/app/robots.ts +15 -0
  133. package/packages/create-basement-app/templates/experiment/app/sitemap.ts +16 -0
  134. package/packages/create-basement-app/templates/experiment/biome.json +250 -0
  135. package/packages/create-basement-app/templates/experiment/components/basement.svg +1 -0
  136. package/packages/create-basement-app/templates/experiment/components/layout/footer/index.tsx +27 -0
  137. package/packages/create-basement-app/templates/experiment/components/layout/header/index.tsx +58 -0
  138. package/packages/create-basement-app/templates/experiment/components/layout/navigation-menu.tsx +127 -0
  139. package/packages/create-basement-app/templates/experiment/components/layout/theme/index.tsx +66 -0
  140. package/packages/create-basement-app/templates/experiment/components/layout/wrapper/index.tsx +65 -0
  141. package/packages/create-basement-app/templates/experiment/components/ui/README.md +77 -0
  142. package/packages/create-basement-app/templates/experiment/components/ui/image/README.md +37 -0
  143. package/packages/create-basement-app/templates/experiment/components/ui/image/index.tsx +224 -0
  144. package/packages/create-basement-app/templates/experiment/components/ui/link/index.tsx +146 -0
  145. package/packages/create-basement-app/templates/experiment/lib/README.md +33 -0
  146. package/packages/create-basement-app/templates/experiment/lib/constants.ts +12 -0
  147. package/packages/create-basement-app/templates/experiment/lib/hooks/index.ts +12 -0
  148. package/packages/create-basement-app/templates/experiment/lib/hooks/use-device-detection.ts +81 -0
  149. package/packages/create-basement-app/templates/experiment/lib/hooks/use-media-breakpoint.ts +15 -0
  150. package/packages/create-basement-app/templates/experiment/lib/hooks/use-prefetch.ts +74 -0
  151. package/packages/create-basement-app/templates/experiment/lib/integrations/.gitkeep +0 -0
  152. package/packages/create-basement-app/templates/experiment/lib/scripts/dev.ts +52 -0
  153. package/packages/create-basement-app/templates/experiment/lib/scripts/generate-component.ts +322 -0
  154. package/packages/create-basement-app/templates/experiment/lib/scripts/generate-page.ts +193 -0
  155. package/packages/create-basement-app/templates/experiment/lib/scripts/generate.ts +79 -0
  156. package/packages/create-basement-app/templates/experiment/lib/scripts/utils.ts +246 -0
  157. package/packages/create-basement-app/templates/experiment/lib/store/app.ts +11 -0
  158. package/packages/create-basement-app/templates/experiment/lib/store/index.ts +11 -0
  159. package/packages/create-basement-app/templates/experiment/lib/styles/README.md +64 -0
  160. package/packages/create-basement-app/templates/experiment/lib/styles/cn.ts +7 -0
  161. package/packages/create-basement-app/templates/experiment/lib/styles/colors.ts +63 -0
  162. package/packages/create-basement-app/templates/experiment/lib/styles/config.ts +34 -0
  163. package/packages/create-basement-app/templates/experiment/lib/styles/css/global.css +85 -0
  164. package/packages/create-basement-app/templates/experiment/lib/styles/css/index.css +6 -0
  165. package/packages/create-basement-app/templates/experiment/lib/styles/css/reset.css +166 -0
  166. package/packages/create-basement-app/templates/experiment/lib/styles/css/root.css +68 -0
  167. package/packages/create-basement-app/templates/experiment/lib/styles/css/tailwind.css +132 -0
  168. package/packages/create-basement-app/templates/experiment/lib/styles/easings.ts +21 -0
  169. package/packages/create-basement-app/templates/experiment/lib/styles/fonts.ts +28 -0
  170. package/packages/create-basement-app/templates/experiment/lib/styles/index.ts +12 -0
  171. package/packages/create-basement-app/templates/experiment/lib/styles/layout.mjs +27 -0
  172. package/packages/create-basement-app/templates/experiment/lib/styles/scripts/README.md +29 -0
  173. package/packages/create-basement-app/templates/experiment/lib/styles/scripts/generate-root.ts +57 -0
  174. package/packages/create-basement-app/templates/experiment/lib/styles/scripts/generate-tailwind.ts +162 -0
  175. package/packages/create-basement-app/templates/experiment/lib/styles/scripts/postcss-functions.mjs +168 -0
  176. package/packages/create-basement-app/templates/experiment/lib/styles/scripts/setup-styles.ts +24 -0
  177. package/packages/create-basement-app/templates/experiment/lib/styles/scripts/utils.ts +20 -0
  178. package/packages/create-basement-app/templates/experiment/lib/styles/typography.ts +36 -0
  179. package/packages/create-basement-app/templates/experiment/lib/utils/README.md +40 -0
  180. package/packages/create-basement-app/templates/experiment/lib/utils/css.d.ts +21 -0
  181. package/packages/create-basement-app/templates/experiment/lib/utils/easings.ts +240 -0
  182. package/packages/create-basement-app/templates/experiment/lib/utils/fetch.ts +84 -0
  183. package/packages/create-basement-app/templates/experiment/lib/utils/math.test.ts +221 -0
  184. package/packages/create-basement-app/templates/experiment/lib/utils/math.ts +236 -0
  185. package/packages/create-basement-app/templates/experiment/lib/utils/metadata.ts +126 -0
  186. package/packages/create-basement-app/templates/experiment/lib/utils/strings.test.ts +166 -0
  187. package/packages/create-basement-app/templates/experiment/lib/utils/strings.ts +246 -0
  188. package/packages/create-basement-app/templates/experiment/lib/utils/types.d.ts +15 -0
  189. package/packages/create-basement-app/templates/experiment/lib/utils/viewport.test.ts +256 -0
  190. package/packages/create-basement-app/templates/experiment/lib/utils/viewport.ts +193 -0
  191. package/packages/create-basement-app/templates/experiment/next.config.ts +142 -0
  192. package/packages/create-basement-app/templates/experiment/package.json +69 -0
  193. package/packages/create-basement-app/templates/experiment/postcss.config.mjs +42 -0
  194. package/packages/create-basement-app/templates/experiment/public/fonts/geist/Geist-Mono.woff2 +0 -0
  195. package/packages/create-basement-app/templates/experiment/tsconfig.json +43 -0
  196. package/packages/create-basement-app/templates/webgl/.biome/plugins/README.md +21 -0
  197. package/packages/create-basement-app/templates/webgl/.biome/plugins/no-anchor-element.grit +12 -0
  198. package/packages/create-basement-app/templates/webgl/.biome/plugins/no-relative-parent-imports.grit +10 -0
  199. package/packages/create-basement-app/templates/webgl/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
  200. package/packages/create-basement-app/templates/webgl/.cursor/rules/README.md +184 -0
  201. package/packages/create-basement-app/templates/webgl/.cursor/rules/architecture.mdc +437 -0
  202. package/packages/create-basement-app/templates/webgl/.cursor/rules/components.mdc +436 -0
  203. package/packages/create-basement-app/templates/webgl/.cursor/rules/integrations.mdc +447 -0
  204. package/packages/create-basement-app/templates/webgl/.cursor/rules/main.mdc +278 -0
  205. package/packages/create-basement-app/templates/webgl/.cursor/rules/styling.mdc +433 -0
  206. package/packages/create-basement-app/templates/webgl/.editorconfig +40 -0
  207. package/packages/create-basement-app/templates/webgl/.env.example +81 -0
  208. package/packages/create-basement-app/templates/webgl/.gitattributes +19 -0
  209. package/packages/create-basement-app/templates/webgl/.github/PULL_REQUEST_TEMPLATE.md +14 -0
  210. package/packages/create-basement-app/templates/webgl/.github/workflows/lighthouse-to-slack.yml +136 -0
  211. package/packages/create-basement-app/templates/webgl/.vscode/extensions.json +20 -0
  212. package/packages/create-basement-app/templates/webgl/.vscode/settings.json +105 -0
  213. package/packages/create-basement-app/templates/webgl/README.md +221 -0
  214. package/packages/create-basement-app/templates/webgl/_gitignore +67 -0
  215. package/packages/create-basement-app/templates/webgl/app/favicon.ico +0 -0
  216. package/packages/create-basement-app/templates/webgl/app/layout.tsx +104 -0
  217. package/packages/create-basement-app/templates/webgl/app/page.tsx +10 -0
  218. package/packages/create-basement-app/templates/webgl/app/robots.ts +15 -0
  219. package/packages/create-basement-app/templates/webgl/app/sitemap.ts +16 -0
  220. package/packages/create-basement-app/templates/webgl/biome.json +250 -0
  221. package/packages/create-basement-app/templates/webgl/components/basement.svg +1 -0
  222. package/packages/create-basement-app/templates/webgl/components/layout/footer/index.tsx +27 -0
  223. package/packages/create-basement-app/templates/webgl/components/layout/header/index.tsx +11 -0
  224. package/packages/create-basement-app/templates/webgl/components/layout/theme/index.tsx +66 -0
  225. package/packages/create-basement-app/templates/webgl/components/layout/wrapper/index.tsx +65 -0
  226. package/packages/create-basement-app/templates/webgl/components/ui/README.md +77 -0
  227. package/packages/create-basement-app/templates/webgl/components/ui/image/README.md +37 -0
  228. package/packages/create-basement-app/templates/webgl/components/ui/image/index.tsx +224 -0
  229. package/packages/create-basement-app/templates/webgl/components/ui/link/index.tsx +146 -0
  230. package/packages/create-basement-app/templates/webgl/components/webgl/canvas/dynamic.tsx +34 -0
  231. package/packages/create-basement-app/templates/webgl/components/webgl/canvas/index.tsx +38 -0
  232. package/packages/create-basement-app/templates/webgl/components/webgl/components/scene/index.tsx +29 -0
  233. package/packages/create-basement-app/templates/webgl/lib/README.md +33 -0
  234. package/packages/create-basement-app/templates/webgl/lib/hooks/index.ts +12 -0
  235. package/packages/create-basement-app/templates/webgl/lib/hooks/use-device-detection.ts +81 -0
  236. package/packages/create-basement-app/templates/webgl/lib/hooks/use-media-breakpoint.ts +15 -0
  237. package/packages/create-basement-app/templates/webgl/lib/hooks/use-prefetch.ts +74 -0
  238. package/packages/create-basement-app/templates/webgl/lib/integrations/.gitkeep +0 -0
  239. package/packages/create-basement-app/templates/webgl/lib/renderer.ts +7 -0
  240. package/packages/create-basement-app/templates/webgl/lib/scripts/dev.ts +52 -0
  241. package/packages/create-basement-app/templates/webgl/lib/scripts/generate-component.ts +322 -0
  242. package/packages/create-basement-app/templates/webgl/lib/scripts/generate-page.ts +193 -0
  243. package/packages/create-basement-app/templates/webgl/lib/scripts/generate.ts +79 -0
  244. package/packages/create-basement-app/templates/webgl/lib/scripts/utils.ts +246 -0
  245. package/packages/create-basement-app/templates/webgl/lib/store/app.ts +11 -0
  246. package/packages/create-basement-app/templates/webgl/lib/store/index.ts +11 -0
  247. package/packages/create-basement-app/templates/webgl/lib/styles/README.md +64 -0
  248. package/packages/create-basement-app/templates/webgl/lib/styles/cn.ts +7 -0
  249. package/packages/create-basement-app/templates/webgl/lib/styles/colors.ts +63 -0
  250. package/packages/create-basement-app/templates/webgl/lib/styles/config.ts +34 -0
  251. package/packages/create-basement-app/templates/webgl/lib/styles/css/global.css +85 -0
  252. package/packages/create-basement-app/templates/webgl/lib/styles/css/index.css +6 -0
  253. package/packages/create-basement-app/templates/webgl/lib/styles/css/reset.css +166 -0
  254. package/packages/create-basement-app/templates/webgl/lib/styles/css/root.css +68 -0
  255. package/packages/create-basement-app/templates/webgl/lib/styles/css/tailwind.css +132 -0
  256. package/packages/create-basement-app/templates/webgl/lib/styles/easings.ts +21 -0
  257. package/packages/create-basement-app/templates/webgl/lib/styles/fonts.ts +28 -0
  258. package/packages/create-basement-app/templates/webgl/lib/styles/index.ts +12 -0
  259. package/packages/create-basement-app/templates/webgl/lib/styles/layout.mjs +27 -0
  260. package/packages/create-basement-app/templates/webgl/lib/styles/scripts/README.md +29 -0
  261. package/packages/create-basement-app/templates/webgl/lib/styles/scripts/generate-root.ts +57 -0
  262. package/packages/create-basement-app/templates/webgl/lib/styles/scripts/generate-tailwind.ts +162 -0
  263. package/packages/create-basement-app/templates/webgl/lib/styles/scripts/postcss-functions.mjs +168 -0
  264. package/packages/create-basement-app/templates/webgl/lib/styles/scripts/setup-styles.ts +24 -0
  265. package/packages/create-basement-app/templates/webgl/lib/styles/scripts/utils.ts +20 -0
  266. package/packages/create-basement-app/templates/webgl/lib/styles/typography.ts +36 -0
  267. package/packages/create-basement-app/templates/webgl/lib/utils/README.md +40 -0
  268. package/packages/create-basement-app/templates/webgl/lib/utils/css.d.ts +21 -0
  269. package/packages/create-basement-app/templates/webgl/lib/utils/easings.ts +240 -0
  270. package/packages/create-basement-app/templates/webgl/lib/utils/fetch.ts +84 -0
  271. package/packages/create-basement-app/templates/webgl/lib/utils/math.test.ts +221 -0
  272. package/packages/create-basement-app/templates/webgl/lib/utils/math.ts +236 -0
  273. package/packages/create-basement-app/templates/webgl/lib/utils/metadata.ts +126 -0
  274. package/packages/create-basement-app/templates/webgl/lib/utils/strings.test.ts +166 -0
  275. package/packages/create-basement-app/templates/webgl/lib/utils/strings.ts +246 -0
  276. package/packages/create-basement-app/templates/webgl/lib/utils/types.d.ts +15 -0
  277. package/packages/create-basement-app/templates/webgl/lib/utils/viewport.test.ts +256 -0
  278. package/packages/create-basement-app/templates/webgl/lib/utils/viewport.ts +193 -0
  279. package/packages/create-basement-app/templates/webgl/next.config.ts +142 -0
  280. package/packages/create-basement-app/templates/webgl/package.json +68 -0
  281. package/packages/create-basement-app/templates/webgl/postcss.config.mjs +42 -0
  282. package/packages/create-basement-app/templates/webgl/public/fonts/geist/Geist-Mono.woff2 +0 -0
  283. package/packages/create-basement-app/templates/webgl/tsconfig.json +43 -0
  284. package/tasks/.last-branch +1 -0
  285. package/tasks/CLAUDE.md +104 -0
  286. package/tasks/archive/2026-02-09-next-starter-dynamic-layers/prd.json +153 -0
  287. package/tasks/archive/2026-02-09-next-starter-dynamic-layers/progress.txt +115 -0
  288. package/tasks/prd-project-restructure.md +375 -0
  289. package/tasks/prd.json +227 -91
  290. package/tasks/progress.txt +281 -87
  291. package/tasks/ralph.sh +113 -0
  292. package/integrations/basehub/README.md +0 -3
  293. package/layers/experiment/components/layout/header/index.tsx +0 -58
  294. package/layers/experiment/components/layout/navigation-menu.tsx +0 -127
  295. package/layers/experiment/lib/constants.ts +0 -12
  296. package/layers/webgl/app/page.tsx +0 -10
  297. package/layers/webgl/components/webgl/canvas/dynamic.tsx +0 -34
  298. package/layers/webgl/components/webgl/canvas/index.tsx +0 -43
  299. package/layers/webgl/components/webgl/components/scene/index.tsx +0 -21
  300. package/src/mergers/next-config-merger.js +0 -63
  301. /package/{src → packages/cli/src}/commands/setup-sanity.js +0 -0
  302. /package/{src → packages/cli/src}/commands/worktree.js +0 -0
  303. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/api/draft-mode/disable/route.ts +0 -0
  304. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/api/draft-mode/enable/route.ts +0 -0
  305. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/api/revalidate/route.ts +0 -0
  306. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/layout.tsx +0 -0
  307. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/sitemap.ts +0 -0
  308. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/studio/[[...tool]]/page.tsx +0 -0
  309. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/studio/layout.tsx +0 -0
  310. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/components/ui/sanity-image/index.tsx +0 -0
  311. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/README.md +0 -0
  312. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/check-integration.ts +0 -0
  313. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/README.md +0 -0
  314. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/client.ts +0 -0
  315. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/components/disable-draft-mode.tsx +0 -0
  316. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/components/rich-text.tsx +0 -0
  317. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/env.ts +0 -0
  318. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/live/index.tsx +0 -0
  319. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/queries.ts +0 -0
  320. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/sanity.cli.ts +0 -0
  321. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/sanity.config.ts +0 -0
  322. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/sanity.types.ts +0 -0
  323. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schema.json +0 -0
  324. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/article.ts +0 -0
  325. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/example.ts +0 -0
  326. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/index.ts +0 -0
  327. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/link.ts +0 -0
  328. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/metadata.ts +0 -0
  329. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/navigation.ts +0 -0
  330. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/page.ts +0 -0
  331. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/richText.ts +0 -0
  332. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/structure.ts +0 -0
  333. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/utils/image.ts +0 -0
  334. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/utils/link.ts +0 -0
  335. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/scripts/copy-sanity-mcp.ts +0 -0
  336. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/scripts/generate-page.ts +0 -0
  337. /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/utils/metadata.ts +0 -0
  338. /package/{plugins → packages/create-basement-app/plugins}/README.md +0 -0
  339. /package/{plugins → packages/create-basement-app/plugins}/no-anchor-element.grit +0 -0
  340. /package/{plugins → packages/create-basement-app/plugins}/no-relative-parent-imports.grit +0 -0
  341. /package/{plugins → packages/create-basement-app/plugins}/no-unnecessary-forwardref.grit +0 -0
  342. /package/{template-hooks → packages/create-basement-app/template-hooks}/use-battery.ts +0 -0
  343. /package/{template-hooks → packages/create-basement-app/template-hooks}/use-device-perf.ts +0 -0
  344. /package/{template-hooks → packages/create-basement-app/template-hooks}/use-intersection-observer.ts +0 -0
  345. /package/{template-hooks → packages/create-basement-app/template-hooks}/use-media.ts +0 -0
  346. /package/{layers/webgpu → packages/create-basement-app/templates/default/lib/integrations}/.gitkeep +0 -0
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Math Utilities
3
+ *
4
+ * Pure mathematical functions with no side effects.
5
+ * These are the building blocks for animations, layouts, and data transformations.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { clamp, lerp, mapRange } from '@/utils/math'
10
+ *
11
+ * // Constrain a value
12
+ * const bounded = clamp(0, value, 100)
13
+ *
14
+ * // Interpolate between values
15
+ * const mid = lerp(0, 100, 0.5) // 50
16
+ *
17
+ * // Map scroll position to opacity
18
+ * const opacity = mapRange(0, 500, scrollY, 0, 1, true)
19
+ * ```
20
+ */
21
+
22
+ /**
23
+ * Constrains a value between a minimum and maximum.
24
+ *
25
+ * @param min - Lower bound
26
+ * @param input - Value to constrain
27
+ * @param max - Upper bound
28
+ * @returns The clamped value
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * clamp(0, -5, 100) // 0
33
+ * clamp(0, 50, 100) // 50
34
+ * clamp(0, 150, 100) // 100
35
+ * ```
36
+ */
37
+ export function clamp(min: number, input: number, max: number): number {
38
+ return Math.max(min, Math.min(input, max))
39
+ }
40
+
41
+ /**
42
+ * Linear interpolation between two values.
43
+ *
44
+ * @param start - Starting value
45
+ * @param end - Ending value
46
+ * @param amount - Interpolation factor (0 = start, 1 = end)
47
+ * @returns The interpolated value
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * lerp(0, 100, 0) // 0
52
+ * lerp(0, 100, 0.5) // 50
53
+ * lerp(0, 100, 1) // 100
54
+ * lerp(0, 100, 0.25) // 25
55
+ * ```
56
+ */
57
+ export function lerp(start: number, end: number, amount: number): number {
58
+ return (1 - amount) * start + amount * end
59
+ }
60
+
61
+ /**
62
+ * Maps a value from one range to another.
63
+ *
64
+ * @param inMin - Input range minimum
65
+ * @param inMax - Input range maximum
66
+ * @param input - Value to map
67
+ * @param outMin - Output range minimum
68
+ * @param outMax - Output range maximum
69
+ * @param shouldClamp - Whether to clamp output to range (default: false)
70
+ * @returns The mapped value
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * // Map scroll (0-1000) to opacity (0-1)
75
+ * mapRange(0, 1000, 500, 0, 1) // 0.5
76
+ *
77
+ * // Map with clamping (won't exceed bounds)
78
+ * mapRange(0, 100, 150, 0, 1, true) // 1
79
+ *
80
+ * // Inverted ranges work too
81
+ * mapRange(0, 100, 50, 1, 0) // 0.5
82
+ * ```
83
+ */
84
+ export function mapRange(
85
+ inMin: number,
86
+ inMax: number,
87
+ input: number,
88
+ outMin: number,
89
+ outMax: number,
90
+ shouldClamp = false
91
+ ): number {
92
+ const result =
93
+ ((input - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin
94
+
95
+ if (!shouldClamp) return result
96
+
97
+ const isInverted = outMin > outMax
98
+ return isInverted
99
+ ? clamp(outMax, result, outMin)
100
+ : clamp(outMin, result, outMax)
101
+ }
102
+
103
+ /**
104
+ * Truncates a number to a specified number of decimal places.
105
+ *
106
+ * @param value - Number to truncate
107
+ * @param decimals - Number of decimal places
108
+ * @returns The truncated number
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * truncate(3.14159, 2) // 3.14
113
+ * truncate(3.14159, 0) // 3
114
+ * ```
115
+ */
116
+ export function truncate(value: number, decimals: number): number {
117
+ return Number.parseFloat(value.toFixed(decimals))
118
+ }
119
+
120
+ /**
121
+ * True modulo operation (handles negative numbers correctly).
122
+ *
123
+ * JavaScript's % operator is remainder, not modulo.
124
+ * This function returns a value in [0, d) for positive divisors.
125
+ *
126
+ * @param n - Dividend
127
+ * @param d - Divisor (must be positive)
128
+ * @returns The modulo result
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * // JavaScript remainder vs true modulo
133
+ * -1 % 3 // -1 (remainder)
134
+ * modulo(-1, 3) // 2 (modulo)
135
+ *
136
+ * // Useful for wrapping array indices
137
+ * modulo(index - 1, array.length)
138
+ * ```
139
+ */
140
+ export function modulo(n: number, d: number): number {
141
+ if (d === 0) return n
142
+ if (d < 0) return Number.NaN
143
+ return ((n % d) + d) % d
144
+ }
145
+
146
+ /**
147
+ * Rounds a number to the nearest multiple.
148
+ *
149
+ * @param value - Number to round
150
+ * @param multiple - Multiple to round to
151
+ * @returns The rounded value
152
+ *
153
+ * @example
154
+ * ```ts
155
+ * roundTo(23, 10) // 20
156
+ * roundTo(27, 10) // 30
157
+ * roundTo(0.23, 0.1) // 0.2
158
+ * ```
159
+ */
160
+ export function roundTo(value: number, multiple: number): number {
161
+ return Math.round(value / multiple) * multiple
162
+ }
163
+
164
+ /**
165
+ * Converts degrees to radians.
166
+ *
167
+ * @param degrees - Angle in degrees
168
+ * @returns Angle in radians
169
+ *
170
+ * @example
171
+ * ```ts
172
+ * degToRad(180) // Math.PI
173
+ * degToRad(90) // Math.PI / 2
174
+ * ```
175
+ */
176
+ export function degToRad(degrees: number): number {
177
+ return (degrees * Math.PI) / 180
178
+ }
179
+
180
+ /**
181
+ * Converts radians to degrees.
182
+ *
183
+ * @param radians - Angle in radians
184
+ * @returns Angle in degrees
185
+ *
186
+ * @example
187
+ * ```ts
188
+ * radToDeg(Math.PI) // 180
189
+ * radToDeg(Math.PI / 2) // 90
190
+ * ```
191
+ */
192
+ export function radToDeg(radians: number): number {
193
+ return (radians * 180) / Math.PI
194
+ }
195
+
196
+ /**
197
+ * Calculates the distance between two 2D points.
198
+ *
199
+ * @param x1 - First point X
200
+ * @param y1 - First point Y
201
+ * @param x2 - Second point X
202
+ * @param y2 - Second point Y
203
+ * @returns The distance
204
+ *
205
+ * @example
206
+ * ```ts
207
+ * distance(0, 0, 3, 4) // 5
208
+ * ```
209
+ */
210
+ export function distance(
211
+ x1: number,
212
+ y1: number,
213
+ x2: number,
214
+ y2: number
215
+ ): number {
216
+ return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
217
+ }
218
+
219
+ /**
220
+ * Normalizes a value to a 0-1 range.
221
+ *
222
+ * @param min - Range minimum
223
+ * @param max - Range maximum
224
+ * @param value - Value to normalize
225
+ * @returns Normalized value (0-1)
226
+ *
227
+ * @example
228
+ * ```ts
229
+ * normalize(0, 100, 50) // 0.5
230
+ * normalize(0, 100, 0) // 0
231
+ * normalize(0, 100, 100) // 1
232
+ * ```
233
+ */
234
+ export function normalize(min: number, max: number, value: number): number {
235
+ return (value - min) / (max - min)
236
+ }
@@ -0,0 +1,126 @@
1
+ import type { Metadata } from "next"
2
+
3
+ /**
4
+ * Metadata Generation Utilities
5
+ *
6
+ * Helpers to generate consistent metadata across pages,
7
+ * reducing duplication and ensuring SEO best practices.
8
+ */
9
+
10
+ interface GenerateMetadataOptions {
11
+ title?: string
12
+ description?: string
13
+ keywords?: string[]
14
+ image?: {
15
+ url?: string
16
+ width?: number
17
+ height?: number
18
+ alt?: string
19
+ }
20
+ url?: string
21
+ siteName?: string
22
+ noIndex?: boolean
23
+ type?: "website" | "article"
24
+ publishedTime?: string
25
+ modifiedTime?: string
26
+ authors?: string[]
27
+ }
28
+
29
+ const APP_BASE_URL =
30
+ process.env.NEXT_PUBLIC_BASE_URL ?? "https://localhost:3000"
31
+
32
+ /**
33
+ * Generate complete metadata object for pages
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * export async function generateMetadata({ params }) {
38
+ * const page = await fetchPage(params.slug)
39
+ *
40
+ * return generatePageMetadata({
41
+ * title: page.metadata?.title || page.title,
42
+ * description: page.metadata?.description,
43
+ * image: { url: page.metadata?.image?.asset?.url },
44
+ * url: `/page/${params.slug}`,
45
+ * noIndex: page.metadata?.noIndex,
46
+ * })
47
+ * }
48
+ * ```
49
+ */
50
+ export function generatePageMetadata(
51
+ options: GenerateMetadataOptions
52
+ ): Metadata {
53
+ const {
54
+ title,
55
+ description,
56
+ keywords,
57
+ image,
58
+ url,
59
+ siteName = "Satūs",
60
+ noIndex = false,
61
+ type = "website",
62
+ publishedTime,
63
+ modifiedTime,
64
+ authors,
65
+ } = options
66
+
67
+ const fullUrl = url ? `${APP_BASE_URL}${url}` : APP_BASE_URL
68
+ const imageUrl = image?.url || "/opengraph-image.jpg"
69
+ const imageWidth = image?.width || 1200
70
+ const imageHeight = image?.height || 630
71
+ const imageAlt = image?.alt || title || siteName
72
+
73
+ const metadata: Metadata = {
74
+ metadataBase: new URL(APP_BASE_URL),
75
+ title,
76
+ description,
77
+ keywords,
78
+ alternates: {
79
+ canonical: url || "/",
80
+ },
81
+ openGraph: {
82
+ title,
83
+ description,
84
+ url: fullUrl,
85
+ siteName,
86
+ locale: "en_US",
87
+ type,
88
+ images: [
89
+ {
90
+ url: imageUrl,
91
+ width: imageWidth,
92
+ height: imageHeight,
93
+ alt: imageAlt,
94
+ },
95
+ ],
96
+ ...(publishedTime && { publishedTime }),
97
+ ...(modifiedTime && { modifiedTime }),
98
+ ...(authors && { authors }),
99
+ },
100
+ twitter: {
101
+ card: "summary_large_image",
102
+ title,
103
+ description,
104
+ images: [
105
+ {
106
+ url: imageUrl,
107
+ width: imageWidth,
108
+ height: imageHeight,
109
+ alt: imageAlt,
110
+ },
111
+ ],
112
+ },
113
+ other: {
114
+ "fb:app_id": process.env.NEXT_PUBLIC_FACEBOOK_APP_ID || "",
115
+ },
116
+ }
117
+
118
+ if (noIndex) {
119
+ metadata.robots = {
120
+ index: false,
121
+ follow: false,
122
+ }
123
+ }
124
+
125
+ return metadata
126
+ }
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Unit tests for string utilities
3
+ *
4
+ * Run with: bun test lib/utils/strings.test.ts
5
+ */
6
+
7
+ import { describe, expect, it } from "bun:test"
8
+ import {
9
+ capitalizeFirstLetter,
10
+ convertToCamelCase,
11
+ isEmptyArray,
12
+ isEmptyObject,
13
+ numberWithCommas,
14
+ slugify,
15
+ twoDigits,
16
+ } from "./strings"
17
+
18
+ describe("slugify", () => {
19
+ it("should convert text to URL-friendly format", () => {
20
+ expect(slugify("Hello World")).toBe("hello-world")
21
+ expect(slugify("Hello World!")).toBe("hello-world")
22
+ })
23
+
24
+ it("should handle special characters", () => {
25
+ expect(slugify("Café & Restaurant")).toBe("cafe-restaurant")
26
+ expect(slugify("Rock & Roll")).toBe("rock-roll")
27
+ })
28
+
29
+ it("should handle multiple spaces and dashes", () => {
30
+ expect(slugify("Hello World")).toBe("hello-world")
31
+ expect(slugify("Hello--World")).toBe("hello-world")
32
+ expect(slugify("Hello - World")).toBe("hello-world")
33
+ })
34
+
35
+ it("should trim whitespace", () => {
36
+ expect(slugify(" Hello World ")).toBe("hello-world")
37
+ })
38
+
39
+ it("should handle numbers", () => {
40
+ expect(slugify("Product 123")).toBe("product-123")
41
+ expect(slugify("2024 Review")).toBe("2024-review")
42
+ })
43
+
44
+ it("should handle objects with toString", () => {
45
+ const obj = { toString: () => "Custom Object" }
46
+ expect(slugify(obj)).toBe("custom-object")
47
+ })
48
+ })
49
+
50
+ describe("convertToCamelCase", () => {
51
+ it("should convert first character to lowercase", () => {
52
+ expect(convertToCamelCase("HelloWorld")).toBe("helloWorld")
53
+ expect(convertToCamelCase("MyComponent")).toBe("myComponent")
54
+ })
55
+
56
+ it("should handle already camelCase strings", () => {
57
+ expect(convertToCamelCase("helloWorld")).toBe("helloWorld")
58
+ })
59
+
60
+ it("should handle single characters", () => {
61
+ expect(convertToCamelCase("A")).toBe("a")
62
+ expect(convertToCamelCase("a")).toBe("a")
63
+ })
64
+
65
+ it("should handle empty strings", () => {
66
+ expect(convertToCamelCase("")).toBe("")
67
+ })
68
+ })
69
+
70
+ describe("capitalizeFirstLetter", () => {
71
+ it("should capitalize the first letter", () => {
72
+ expect(capitalizeFirstLetter("hello")).toBe("Hello")
73
+ expect(capitalizeFirstLetter("world")).toBe("World")
74
+ })
75
+
76
+ it("should handle already capitalized strings", () => {
77
+ expect(capitalizeFirstLetter("Hello")).toBe("Hello")
78
+ })
79
+
80
+ it("should handle single characters", () => {
81
+ expect(capitalizeFirstLetter("a")).toBe("A")
82
+ expect(capitalizeFirstLetter("A")).toBe("A")
83
+ })
84
+
85
+ it("should handle empty strings", () => {
86
+ expect(capitalizeFirstLetter("")).toBe("")
87
+ })
88
+ })
89
+
90
+ describe("twoDigits", () => {
91
+ it("should pad single digit numbers with zero", () => {
92
+ expect(twoDigits(0)).toBe("00")
93
+ expect(twoDigits(5)).toBe("05")
94
+ expect(twoDigits(9)).toBe("09")
95
+ })
96
+
97
+ it("should not pad two-digit numbers", () => {
98
+ expect(twoDigits(10)).toBe("10")
99
+ expect(twoDigits(23)).toBe("23")
100
+ expect(twoDigits(99)).toBe("99")
101
+ })
102
+
103
+ it("should handle numbers greater than 99", () => {
104
+ expect(twoDigits(100)).toBe("100")
105
+ expect(twoDigits(999)).toBe("999")
106
+ })
107
+ })
108
+
109
+ describe("numberWithCommas", () => {
110
+ it("should add commas as thousands separators", () => {
111
+ expect(numberWithCommas(1000)).toBe("1,000")
112
+ expect(numberWithCommas(1234)).toBe("1,234")
113
+ expect(numberWithCommas(1234567)).toBe("1,234,567")
114
+ })
115
+
116
+ it("should not add commas to numbers less than 1000", () => {
117
+ expect(numberWithCommas(999)).toBe("999")
118
+ expect(numberWithCommas(1)).toBe("1")
119
+ expect(numberWithCommas(0)).toBe("0")
120
+ })
121
+
122
+ it("should handle objects with toString", () => {
123
+ const obj = { toString: () => "1234567" }
124
+ expect(numberWithCommas(obj)).toBe("1,234,567")
125
+ })
126
+ })
127
+
128
+ describe("isEmptyObject", () => {
129
+ it("should return true for empty objects", () => {
130
+ expect(isEmptyObject({})).toBe(true)
131
+ })
132
+
133
+ it("should return false for non-empty objects", () => {
134
+ expect(isEmptyObject({ a: 1 })).toBe(false)
135
+ expect(isEmptyObject({ key: "value" })).toBe(false)
136
+ })
137
+
138
+ it("should return true for null/undefined", () => {
139
+ expect(isEmptyObject(null as unknown as Record<string, unknown>)).toBe(true)
140
+ expect(isEmptyObject(undefined as unknown as Record<string, unknown>)).toBe(
141
+ true
142
+ )
143
+ })
144
+ })
145
+
146
+ describe("isEmptyArray", () => {
147
+ it("should return true for empty arrays", () => {
148
+ expect(isEmptyArray([])).toBe(true)
149
+ })
150
+
151
+ it("should return false for non-empty arrays", () => {
152
+ expect(isEmptyArray([1, 2, 3])).toBe(false)
153
+ expect(isEmptyArray(["a"])).toBe(false)
154
+ })
155
+
156
+ it("should return false for non-arrays (strings)", () => {
157
+ // Note: strings are not arrays, so Array.isArray returns false
158
+ // The function returns false for non-arrays that have a truthy value
159
+ expect(isEmptyArray("test")).toBe(false)
160
+ })
161
+
162
+ it("should return true for null/undefined", () => {
163
+ expect(isEmptyArray(null as unknown as unknown[])).toBe(true)
164
+ expect(isEmptyArray(undefined as unknown as unknown[])).toBe(true)
165
+ })
166
+ })