bsmnt 0.0.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 (98) hide show
  1. package/.changeset/2026-02-11-test-patch-bump.md +5 -0
  2. package/.changeset/README.md +10 -0
  3. package/.changeset/config.json +16 -0
  4. package/.cursor/rules/README.md +184 -0
  5. package/.cursor/rules/architecture.mdc +437 -0
  6. package/.cursor/rules/components.mdc +436 -0
  7. package/.cursor/rules/integrations.mdc +447 -0
  8. package/.cursor/rules/main.mdc +278 -0
  9. package/.cursor/rules/styling.mdc +433 -0
  10. package/.github/PULL_REQUEST_TEMPLATE.md +14 -0
  11. package/.github/workflows/.gitkeep +0 -0
  12. package/.github/workflows/ci.yml +37 -0
  13. package/.github/workflows/release.yml +54 -0
  14. package/.tldr/cache/call_graph.json +7 -0
  15. package/.tldr/languages.json +6 -0
  16. package/.tldr/status +1 -0
  17. package/.tldrignore +84 -0
  18. package/.vscode/extensions.json +20 -0
  19. package/.vscode/settings.json +98 -0
  20. package/CHANGELOG.md +13 -0
  21. package/CLAUDE.md +138 -0
  22. package/README.md +176 -0
  23. package/bin/index.js +262 -0
  24. package/biome.json +44 -0
  25. package/bun.lock +496 -0
  26. package/changelog/04-02-26.md +86 -0
  27. package/changelog/05-02-26.md +101 -0
  28. package/changelog/09-02-26.md +83 -0
  29. package/docs/fix-studio-hydration.md +46 -0
  30. package/docs/plans/2026-01-29-sanity-smart-merge-design.md +196 -0
  31. package/docs/plans/2026-01-29-sanity-smart-merge-implementation.md +695 -0
  32. package/docs/sanity-setup-steps.md +199 -0
  33. package/integrations/basehub/README.md +3 -0
  34. package/integrations/sanity/app/api/draft-mode/disable/route.ts +7 -0
  35. package/integrations/sanity/app/api/draft-mode/enable/route.ts +21 -0
  36. package/integrations/sanity/app/api/revalidate/route.ts +37 -0
  37. package/integrations/sanity/app/layout.tsx +111 -0
  38. package/integrations/sanity/app/sitemap.ts +80 -0
  39. package/integrations/sanity/app/studio/[[...tool]]/page.tsx +8 -0
  40. package/integrations/sanity/app/studio/layout.tsx +7 -0
  41. package/integrations/sanity/components/ui/sanity-image/index.tsx +37 -0
  42. package/integrations/sanity/lib/integrations/README.md +58 -0
  43. package/integrations/sanity/lib/integrations/check-integration.ts +62 -0
  44. package/integrations/sanity/lib/integrations/sanity/README.md +144 -0
  45. package/integrations/sanity/lib/integrations/sanity/client.ts +30 -0
  46. package/integrations/sanity/lib/integrations/sanity/components/disable-draft-mode.tsx +29 -0
  47. package/integrations/sanity/lib/integrations/sanity/components/rich-text.tsx +73 -0
  48. package/integrations/sanity/lib/integrations/sanity/env.ts +38 -0
  49. package/integrations/sanity/lib/integrations/sanity/live/index.tsx +34 -0
  50. package/integrations/sanity/lib/integrations/sanity/queries.ts +99 -0
  51. package/integrations/sanity/lib/integrations/sanity/sanity.cli.ts +20 -0
  52. package/integrations/sanity/lib/integrations/sanity/sanity.config.ts +94 -0
  53. package/integrations/sanity/lib/integrations/sanity/sanity.types.ts +337 -0
  54. package/integrations/sanity/lib/integrations/sanity/schema.json +1850 -0
  55. package/integrations/sanity/lib/integrations/sanity/schemas/article.ts +132 -0
  56. package/integrations/sanity/lib/integrations/sanity/schemas/example.ts +203 -0
  57. package/integrations/sanity/lib/integrations/sanity/schemas/index.ts +37 -0
  58. package/integrations/sanity/lib/integrations/sanity/schemas/link.ts +127 -0
  59. package/integrations/sanity/lib/integrations/sanity/schemas/metadata.ts +68 -0
  60. package/integrations/sanity/lib/integrations/sanity/schemas/navigation.ts +39 -0
  61. package/integrations/sanity/lib/integrations/sanity/schemas/page.ts +77 -0
  62. package/integrations/sanity/lib/integrations/sanity/schemas/richText.ts +59 -0
  63. package/integrations/sanity/lib/integrations/sanity/structure.ts +5 -0
  64. package/integrations/sanity/lib/integrations/sanity/utils/image.ts +11 -0
  65. package/integrations/sanity/lib/integrations/sanity/utils/link.ts +61 -0
  66. package/integrations/sanity/lib/scripts/copy-sanity-mcp.ts +23 -0
  67. package/integrations/sanity/lib/scripts/generate-page.ts +310 -0
  68. package/integrations/sanity/lib/utils/metadata.ts +190 -0
  69. package/layers/experiment/components/layout/header/index.tsx +58 -0
  70. package/layers/experiment/components/layout/navigation-menu.tsx +127 -0
  71. package/layers/experiment/lib/constants.ts +12 -0
  72. package/layers/webgl/app/page.tsx +10 -0
  73. package/layers/webgl/components/webgl/canvas/dynamic.tsx +34 -0
  74. package/layers/webgl/components/webgl/canvas/index.tsx +43 -0
  75. package/layers/webgl/components/webgl/components/scene/index.tsx +21 -0
  76. package/layers/webgpu/.gitkeep +0 -0
  77. package/package.json +44 -0
  78. package/plugins/README.md +21 -0
  79. package/plugins/no-anchor-element.grit +11 -0
  80. package/plugins/no-relative-parent-imports.grit +6 -0
  81. package/plugins/no-unnecessary-forwardref.grit +5 -0
  82. package/src/commands/add-integration.js +325 -0
  83. package/src/commands/create.js +415 -0
  84. package/src/commands/setup-sanity.js +426 -0
  85. package/src/commands/worktree.js +805 -0
  86. package/src/mergers/check-integration-merger.js +105 -0
  87. package/src/mergers/config.js +137 -0
  88. package/src/mergers/index.js +355 -0
  89. package/src/mergers/layout-merger.js +223 -0
  90. package/src/mergers/next-config-merger.js +63 -0
  91. package/src/mergers/sitemap-merger.js +121 -0
  92. package/tasks/prd-next-starter-dynamic-layers.md +184 -0
  93. package/tasks/prd.json +153 -0
  94. package/tasks/progress.txt +115 -0
  95. package/template-hooks/use-battery.ts +126 -0
  96. package/template-hooks/use-device-perf.ts +184 -0
  97. package/template-hooks/use-intersection-observer.ts +32 -0
  98. package/template-hooks/use-media.ts +33 -0
@@ -0,0 +1,447 @@
1
+ ---
2
+ alwaysApply: true
3
+ ---
4
+ ---
5
+ description: Third-party integration guidelines (Sanity, Shopify, etc.)
6
+ globs: *.tsx, *.jsx, *.css, *.js, *.ts
7
+ ---
8
+
9
+ # Third-Party Integration Guidelines
10
+
11
+ ## Sanity CMS Integration
12
+
13
+ ### Configuration & Setup
14
+ Use CDN for performance with stega for visual editing. Store credentials securely in environment variables. All Sanity files are organized in `/lib/integrations/sanity/` directory.
15
+
16
+ ```typescript
17
+ // In lib/integrations/sanity/client.ts
18
+ export const client = createClient({
19
+ projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
20
+ dataset: process.env.NEXT_PUBLIC_SANITY_DATASET,
21
+ apiVersion: '2024-03-15',
22
+ useCdn: true, // Use CDN for better performance
23
+ token: process.env.SANITY_API_WRITE_TOKEN, // Write token for editing
24
+ stega: {
25
+ studioUrl: process.env.NEXT_PUBLIC_SANITY_STUDIO_URL || '/studio',
26
+ },
27
+ })
28
+ ```
29
+
30
+ ### Schema Management
31
+
32
+ #### Content Modelling
33
+ - Unless explicitly modelling web pages or app views, create content models for what things are, not what they look like in a front-end
34
+ - For example, consider the `status` of an element instead of its `color`
35
+
36
+ #### Basic Schema Types
37
+ - ALWAYS use the `defineType`, `defineField`, and `defineArrayMember` helper functions
38
+ - ALWAYS write schema types to their own files and export a named `const` that matches the filename
39
+ - ONLY use a `name` attribute in fields unless the `title` needs to be something other than a title-case version of the `name`
40
+ - ANY `string` field type with an `options.list` array with fewer than 5 options must use `options.layout: "radio"`
41
+ - ANY `image` field must include `options.hotspot: true`
42
+ - INCLUDE brief, useful `description` values if the intention of a field is not obvious
43
+ - INCLUDE `rule.warning()` for fields that would benefit from being a certain length
44
+ - INCLUDE brief, useful validation errors in `rule.required().error('<Message>')` that signal why the field must be correct before publishing is allowed
45
+ - AVOID `boolean` fields, write a `string` field with an `options.list` configuration
46
+ - NEVER write single `reference` type fields, always write an `array` of references
47
+ - CONSIDER the order of fields, from most important and relevant first, to least often used last
48
+
49
+ ```ts
50
+ // ./src/schemaTypes/lessonType.ts
51
+ import {defineField, defineType} from 'sanity'
52
+
53
+ export const lessonType = defineType({
54
+ name: 'lesson',
55
+ title: 'Lesson',
56
+ type: 'document',
57
+ fields: [
58
+ defineField({
59
+ name: 'title',
60
+ type: 'string',
61
+ }),
62
+ defineField({
63
+ name: 'categories',
64
+ type: 'array',
65
+ of: [defineArrayMember({type: 'reference', to: {type: 'category'}})],
66
+ }),
67
+ ],
68
+ })
69
+ ```
70
+
71
+ #### Schema Type with Custom Input Components
72
+ If a schema type has input components, they should be colocated with the schema type file. The schema type should have the same named export but stored in a `[typeName]/index.ts` file:
73
+
74
+ ```ts
75
+ // ./src/schemaTypes/seoType/index.ts
76
+ import {defineField, defineType} from 'sanity'
77
+ import seoInput from './seoInput'
78
+
79
+ export const seoType = defineType({
80
+ name: 'seo',
81
+ title: 'SEO',
82
+ type: 'object',
83
+ components: { input: seoInput }
84
+ // ...
85
+ })
86
+ ```
87
+
88
+ #### No Anonymous Reusable Schema Types
89
+ ANY schema type that benefits from being reused in multiple document types should be registered as its own custom schema type.
90
+
91
+ ```ts
92
+ // ./src/schemaTypes/blockContentType.ts
93
+ import {defineField, defineType} from 'sanity'
94
+
95
+ export const blockContentType = defineType({
96
+ name: 'blockContent',
97
+ title: 'Block content',
98
+ type: 'array',
99
+ of: [defineField({name: 'block',type: 'block'})],
100
+ })
101
+ ```
102
+
103
+ #### Decorating Schema Types
104
+ Every `document` and `object` schema type should:
105
+
106
+ - Have an `icon` property from `@sanity/icons`
107
+ - Have a customized `preview` property that shows rich contextual details about the document
108
+ - Use `groups` when the schema type has more than a few fields to collate related fields and only show the most important group by default. These `groups` should use the icon property as well.
109
+ - Use `fieldsets` with `options: {columns: 2}` if related fields could be grouped visually together, such as `startDate` and `endDate`
110
+
111
+ ### Visual Editing
112
+ Always add `data-sanity` attributes for visual editing. Use SanityContextProvider for document access. Implement proper draft mode handling. Import from `/lib/integrations/sanity` directory.
113
+
114
+ ```typescript
115
+ import { RichText } from '@/integrations/sanity/components/rich-text'
116
+
117
+ export function MyComponent() {
118
+ const { document } = useSanityContext()
119
+
120
+ return (
121
+ <div data-sanity={document._id}>
122
+ <h1 data-sanity="title">{document.title}</h1>
123
+ <div data-sanity="content">
124
+ <RichText content={document.content} />
125
+ </div>
126
+ </div>
127
+ )
128
+ }
129
+ ```
130
+
131
+ ### Data Fetching
132
+ Use proper perspective for draft vs published content. Implement caching strategies for performance. Handle errors gracefully with try-catch. Import from `/lib/integrations/sanity` directory. Use `@/utils/metadata` helpers for SEO optimization. All `sanityFetch` calls automatically use `cacheSignal()` for request cleanup.
133
+
134
+ ```typescript
135
+ // In lib/integrations/sanity/queries/index.ts
136
+ import { sanityFetch } from '@/integrations/sanity/live'
137
+ import { generateSanityMetadata } from '@/utils/metadata'
138
+
139
+ // Use sanityFetch (automatically includes cacheSignal)
140
+ export async function fetchSanityPage(slug: string, isDraftMode = false) {
141
+ const { data, error } = await sanityFetch({
142
+ query: pageQuery,
143
+ params: { slug },
144
+ isDraftMode
145
+ })
146
+
147
+ if (error) {
148
+ console.error('fetchSanityPage error:', error)
149
+ return { data: null, error }
150
+ }
151
+
152
+ return { data, error: null }
153
+ }
154
+
155
+ // In page.tsx for SEO
156
+ export async function generateMetadata({ params }) {
157
+ const { data } = await fetchSanityPage(params.slug)
158
+ return generateSanityMetadata(data)
159
+ }
160
+ ```
161
+
162
+ **Cache Components Notes:**
163
+ - Draft mode automatically uses `cache: 'no-store'` ✅
164
+ - Published content uses ISR with revalidation ✅
165
+ - All queries use `cacheSignal()` for automatic cleanup ✅
166
+ - Wrap in Suspense boundaries for proper loading states ✅
167
+
168
+ ### GROQ Queries
169
+
170
+ - ALWAYS use SCREAMING_SNAKE_CASE for variable names, for example POSTS_QUERY
171
+ - ALWAYS write queries to their own variables, never as a parameter in a function
172
+ - ALWAYS import the `defineQuery` function to wrap query strings from the `groq` or `next-sanity` package
173
+ - ALWAYS write every required attribute in a projection when writing a query
174
+ - ALWAYS put each segment of a filter, and each attribute on its own line
175
+ - ALWAYS use parameters for variables in a query
176
+ - NEVER insert dynamic values using string interpolation
177
+
178
+ ```ts
179
+ // In lib/integrations/sanity/queries/index.ts
180
+ import { groq } from 'next-sanity'
181
+
182
+ export const pageQuery = groq`
183
+ *[_type == "page" && slug.current == $slug][0] {
184
+ _id,
185
+ title,
186
+ slug,
187
+ content,
188
+ "imageUrl": image.asset->url,
189
+ _updatedAt
190
+ }
191
+ `
192
+
193
+ // Good query example
194
+ import {defineQuery} from 'groq'
195
+
196
+ export const POST_QUERY = defineQuery(`*[
197
+ _type == "post"
198
+ && slug.current == $slug
199
+ ][0]{
200
+ _id,
201
+ title,
202
+ image,
203
+ author->{
204
+ _id,
205
+ name
206
+ }
207
+ }`)
208
+ ```
209
+
210
+ ### Performance Optimization
211
+ Use ISR for static content with revalidation. Implement proper cache invalidation via webhooks. Optimize images with proper sizing. Use consolidated imports for better tree-shaking. Use metadata helpers for consistent SEO.
212
+
213
+ ```typescript
214
+ import { urlForImage } from '@/integrations/sanity/utils/image'
215
+ import { generateSanityMetadata } from '@/utils/metadata'
216
+
217
+ // Use ISR for published content
218
+ export const revalidate = 3600
219
+
220
+ // Optimize images
221
+ <SanityImage image={document.image} maxWidth={1200} />
222
+
223
+ // Generate SEO metadata
224
+ export async function generateMetadata({ params }) {
225
+ const page = await fetchSanityPage(params.slug)
226
+ return generateSanityMetadata(page, {
227
+ title: page.seo?.title || page.title,
228
+ description: page.seo?.description,
229
+ image: page.seo?.image || page.image,
230
+ noIndex: page.seo?.noIndex,
231
+ })
232
+ }
233
+ ```
234
+
235
+ ### Project Structure
236
+ All Sanity files are organized in `/lib/integrations/sanity/` directory
237
+
238
+ ```
239
+ lib/integrations/sanity/
240
+ ├── sanity.cli.ts # CLI configuration
241
+ ├── sanity.config.ts # Studio configuration
242
+ ├── env.ts # Environment variables
243
+ ├── structure.ts # Studio structure
244
+ ├── client.ts # Sanity client
245
+ ├── fetch.ts # Data fetching with cacheSignal
246
+ ├── index.ts # Main exports
247
+ ├── README.md # Documentation
248
+ ├── queries/
249
+ │ └── index.ts # GROQ queries
250
+ ├── schemaTypes/ # Content type definitions
251
+ │ ├── documents/ # Document types (article, page, etc.)
252
+ │ ├── objects/ # Object types (link, metadata, richText)
253
+ │ ├── singletons/ # Singleton types (navigation)
254
+ │ └── index.ts
255
+ ├── components/ # React components
256
+ │ ├── context.tsx # React context
257
+ │ ├── rich-text.tsx # Rich text component
258
+ │ └── disable-draft-mode.tsx
259
+ └── utils/
260
+ ├── image.ts # Image utilities
261
+ └── link.ts # Link utilities
262
+ ```
263
+
264
+ ### Writing Sanity Content for Importing
265
+
266
+ When asked to write content:
267
+
268
+ - ONLY use the existing schema types registered in the Studio configuration
269
+ - ALWAYS write content as an `.ndjson` file at the root of the project
270
+ - NEVER write a script to write the file, just write the file
271
+ - IMPORT `.ndjson` files using the CLI command `npx sanity dataset import <filename.ndjson>`
272
+ - NEVER include a `.` in the `_id` field of a document unless you need it to be private
273
+ - NEVER include image references because you don't know what image documents exist
274
+ - ALWAYS write images in this format below, replacing the document ID value to generate the same placeholder image
275
+
276
+ ```JSON
277
+ {"_type":"image","_sanityAsset":"image@https://picsum.photos/seed/[[REPLACE_WITH_DOCUMENT_ID]]/1920/1080"}
278
+ ```
279
+
280
+ ### TypeScript Generation
281
+
282
+ #### For the Studio
283
+ ALWAYS re-run schema extraction after making schema file changes with `npx sanity@latest schema extract`
284
+
285
+ #### For Monorepos
286
+ ALWAYS use a simple pnpm workspace configuration to place the studio in `apps/studio`
287
+
288
+ ```
289
+ your-project/
290
+ └── apps/
291
+ ├── studio/ -> Sanity Studio
292
+ └── web/ -> Front-end
293
+ ```
294
+
295
+ - ALWAYS extract the schema to the web folder with `npx sanity@latest schema extract --path=../<front-end-folder>/sanity/extract.json`
296
+ - ALWAYS generate types with `npx sanity@latest typegen generate` after every GROQ query change
297
+ - ALWAYS create a TypeGen configuration file called `sanity-typegen.json` at the root of the front-end code-base
298
+
299
+ ```json
300
+ {
301
+ "path": "./**/*.{ts,tsx,js,jsx}",
302
+ "schema": "./<front-end-folder>/sanity/extract.json",
303
+ "generates": "./<web-folder>/sanity/types.ts"
304
+ }
305
+ ```
306
+
307
+ #### For the Front-end
308
+ ONLY write Types for document types and query responses if you cannot generate them with Sanity TypeGen
309
+
310
+ ### Project Settings and Data
311
+ ALWAYS check if there is a way to interact with a project via the CLI before writing custom scripts `npx sanity --help`
312
+
313
+ ---
314
+
315
+ ## Shopify Integration
316
+
317
+ ### API Configuration
318
+ Use GraphQL for queries. Store credentials securely.
319
+
320
+ ```typescript
321
+ const shopifyClient = createShopifyClient({
322
+ domain: process.env.SHOPIFY_DOMAIN,
323
+ storefrontAccessToken: process.env.SHOPIFY_STOREFRONT_TOKEN
324
+ })
325
+ ```
326
+
327
+ ### Product Management
328
+ Use fragments for reusable queries. Implement proper error handling.
329
+
330
+ ```typescript
331
+ import { PRODUCT_FRAGMENT } from '@/lib/integrations/shopify/fragments/product'
332
+ ```
333
+
334
+ ### Cart Operations
335
+ Use mutations for cart operations. Maintain cart state with Zustand.
336
+
337
+ ```typescript
338
+ import { ADD_TO_CART_MUTATION } from '@/lib/integrations/shopify/mutations/cart'
339
+ ```
340
+
341
+ ---
342
+
343
+ ## General Integration Best Practices
344
+
345
+ ### Environment Variables
346
+ - Never commit API keys
347
+ - Use `.env.local` for development
348
+ - Document required variables in `.env.example`
349
+ - Validate environment variables are set before use
350
+
351
+ ```typescript
352
+ // Check required env vars at module load
353
+ const requiredEnvVars = ['NEXT_PUBLIC_API_KEY', 'API_SECRET'] as const
354
+ for (const envVar of requiredEnvVars) {
355
+ if (!process.env[envVar]) {
356
+ throw new Error(`Missing required environment variable: ${envVar}`)
357
+ }
358
+ }
359
+ ```
360
+
361
+ ### API Resilience
362
+ **Always use `fetchWithTimeout` for external API calls**
363
+
364
+ - Standard timeouts: 8-10 seconds for most integrations
365
+ - Implement proper error handling and retry logic
366
+ - Use `cacheSignal()` for automatic request cleanup (React 19+)
367
+
368
+ ```typescript
369
+ import { fetchWithTimeout } from '@/utils/fetch'
370
+ import { cacheSignal } from 'react'
371
+
372
+ // With cacheSignal for automatic cleanup
373
+ const signal = cacheSignal()
374
+ const response = await fetchWithTimeout(url, {
375
+ timeout: 10000,
376
+ signal: signal as AbortSignal,
377
+ ...options
378
+ })
379
+ ```
380
+
381
+ ### Error Handling
382
+ Implement timeout handling for all API calls. Provide user-friendly error messages. Log errors for debugging with context.
383
+
384
+ ```typescript
385
+ try {
386
+ const response = await fetchWithTimeout(url, options, 10000)
387
+ return { data: await response.json(), error: null }
388
+ } catch (error) {
389
+ console.error('API call failed:', error)
390
+ return { data: null, error: error.message }
391
+ }
392
+ ```
393
+
394
+ ### Type Safety
395
+ Generate TypeScript types from APIs when possible. Use proper validation (e.g., Zod schemas).
396
+
397
+ ```typescript
398
+ import * as z from "zod";
399
+
400
+ const ProductSchema = z.object({
401
+ id: z.string(),
402
+ title: z.string(),
403
+ price: z.number()
404
+ })
405
+ ```
406
+
407
+ ### Performance
408
+ - Cache API responses appropriately (except user-specific data)
409
+ - Use ISR (Incremental Static Regeneration) for dynamic content
410
+ - Implement proper loading states with Suspense boundaries
411
+ - Use `cacheSignal()` for automatic request cleanup
412
+
413
+ **⚠️ Cache Components Gotchas:**
414
+ - **User-specific data**: Never cache (carts, accounts, private content)
415
+ - **Real-time data**: Use `cache: 'no-store'` for live feeds
416
+ - **Suspense boundaries**: Required for cached data fetching
417
+ - **Cache invalidation**: Use `revalidateTag()` or `revalidatePath()` in webhooks
418
+ - **Testing**: Test with hard refresh AND navigation (different cache layers)
419
+
420
+ ### Security
421
+ - Validate all user inputs
422
+ - Use server-side API calls for sensitive operations
423
+ - Implement rate limiting where necessary
424
+
425
+ ### Integration Management
426
+ - Check integration usage with `@/lib/integrations/check-integration`
427
+ - Keep only active integrations to optimize bundle size
428
+ - Remove unused integration directories to reduce bundle size
429
+
430
+ ## Webhook Handling
431
+
432
+ ### Verification
433
+ Always verify webhook signatures. Use proper authentication.
434
+
435
+ ```typescript
436
+ export async function verifyWebhookSignature(
437
+ payload: string,
438
+ signature: string
439
+ ): Promise<boolean> {
440
+ // verification logic
441
+ }
442
+ ```
443
+
444
+ ### Processing
445
+ Process webhooks asynchronously. Implement idempotency. Return 200 status quickly.
446
+
447
+ Last updated: 2025-12-18