@folpe/loom 0.1.0 → 0.2.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.
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: cli-development
3
+ description: "CLI tool development patterns for Node.js with Commander.js, terminal UX, and npm distribution. Use when building command-line tools."
4
+ allowed-tools: "Read, Write, Edit, Glob, Grep"
5
+ ---
6
+
7
+ # CLI Development Patterns
8
+
9
+ ## Project Structure
10
+
11
+ ```
12
+ src/
13
+ index.ts # Entry point — program definition and parse()
14
+ commands/ # One file per command/subcommand
15
+ init.ts
16
+ build.ts
17
+ list.ts
18
+ lib/ # Shared utilities
19
+ config.ts # Config loading and validation
20
+ output.ts # Formatting and printing helpers
21
+ errors.ts # Custom error classes
22
+ ```
23
+
24
+ ## Commander.js Setup
25
+
26
+ - Define the program with metadata from `package.json`:
27
+ ```ts
28
+ const program = new Command()
29
+ .name('mytool')
30
+ .description('What this tool does')
31
+ .version(version)
32
+ ```
33
+ - One file per subcommand — register with `program.addCommand()`.
34
+ - Use `.argument()` for required positional args, `.option()` for flags:
35
+ ```ts
36
+ program
37
+ .command('init')
38
+ .description('Initialize a new project')
39
+ .argument('<name>', 'project name')
40
+ .option('-t, --template <template>', 'template to use', 'default')
41
+ .action(async (name, options) => { /* ... */ })
42
+ ```
43
+ - Always provide `--help` descriptions for all arguments and options.
44
+
45
+ ## Exit Codes
46
+
47
+ - `0` — success
48
+ - `1` — general error (runtime failure, unhandled exception)
49
+ - `2` — usage error (invalid arguments, missing required flags)
50
+ - Exit explicitly: `process.exit(code)` after cleanup.
51
+ - Catch unhandled errors at the top level:
52
+ ```ts
53
+ program.parseAsync().catch((error) => {
54
+ console.error(error.message)
55
+ process.exit(1)
56
+ })
57
+ ```
58
+
59
+ ## Output Conventions
60
+
61
+ - Use `stdout` for actual output (data, results, formatted tables).
62
+ - Use `stderr` for progress, logging, warnings, and errors.
63
+ - Support `--json` flag for machine-readable output on data commands.
64
+ - Support `--quiet` or `--silent` flag to suppress non-essential output.
65
+ - Use colors sparingly — respect `NO_COLOR` environment variable:
66
+ ```ts
67
+ const useColor = !process.env.NO_COLOR && process.stdout.isTTY
68
+ ```
69
+
70
+ ## Terminal UX
71
+
72
+ - Show a spinner for long operations (use `ora` or `nanospinner`).
73
+ - Use progress bars for multi-step or percentage-based operations.
74
+ - Confirm destructive actions with a prompt (use `@inquirer/prompts`):
75
+ ```ts
76
+ const confirmed = await confirm({ message: 'Delete all files?' })
77
+ if (!confirmed) process.exit(0)
78
+ ```
79
+ - Use tables for structured data display (use `cli-table3` or `columnify`).
80
+ - Truncate long output with `--limit` option or pipe to `less`.
81
+
82
+ ## Input
83
+
84
+ - Accept input from stdin for piping:
85
+ ```ts
86
+ if (!process.stdin.isTTY) {
87
+ const input = await readStdin()
88
+ }
89
+ ```
90
+ - Support both `--flag value` and `--flag=value` syntax (Commander.js handles this).
91
+ - Use environment variables as fallback for configuration: `MYTOOL_TOKEN`, `MYTOOL_CONFIG`.
92
+
93
+ ## Error Messages
94
+
95
+ - Include what went wrong, why, and how to fix it:
96
+ ```
97
+ Error: Config file not found at ./config.yaml
98
+ Run `mytool init` to create a default configuration.
99
+ ```
100
+ - Use chalk/picocolors for error formatting:
101
+ - Red for errors
102
+ - Yellow for warnings
103
+ - Dim for secondary info
104
+ - Never show raw stack traces to users. Log them with `--verbose` flag.
105
+
106
+ ## Configuration
107
+
108
+ - Support config file (`.mytoolrc`, `mytool.config.ts`, or field in `package.json`).
109
+ - Use `cosmiconfig` or manual lookup for config file discovery.
110
+ - Validate config with Zod schema — fail fast with clear error on invalid config.
111
+ - Allow CLI flags to override config file values.
112
+
113
+ ## Bundling & Distribution
114
+
115
+ - Bundle with `tsup` for a single distributable file:
116
+ ```ts
117
+ // tsup.config.ts
118
+ export default defineConfig({
119
+ entry: ['src/index.ts'],
120
+ format: ['esm'],
121
+ target: 'node20',
122
+ clean: true,
123
+ banner: { js: '#!/usr/bin/env node' },
124
+ })
125
+ ```
126
+ - Set `"bin"` in `package.json` pointing to the built file.
127
+ - Set `"type": "module"` for ESM.
128
+ - Test the built binary locally before publishing: `node dist/index.js`.
129
+
130
+ ## Testing
131
+
132
+ - Test commands by invoking them programmatically, not by spawning processes:
133
+ ```ts
134
+ import { program } from '../src/index'
135
+ program.parse(['node', 'test', 'init', 'my-project'])
136
+ ```
137
+ - Test output by capturing stdout/stderr.
138
+ - Test error cases: missing args, invalid flags, missing config.
139
+ - Test stdin piping with mock readable streams.
@@ -0,0 +1,85 @@
1
+ ---
2
+ name: react-native-patterns
3
+ description: "React Native and Expo best practices for navigation, styling, performance, and native features. Use when building mobile apps with Expo. Inspired by callstackincubator/agent-skills and expo/skills."
4
+ allowed-tools: "Read, Write, Edit, Glob, Grep"
5
+ ---
6
+
7
+ # React Native & Expo Patterns
8
+
9
+ ## Expo Router
10
+
11
+ - Use file-based routing in the `app/` directory exclusively.
12
+ - Never co-locate components, types, or utilities in the `app/` directory — keep them in `src/`.
13
+ - Always ensure a route matches `/` (may be inside a group route).
14
+ - Use layout routes (`_layout.tsx`) for shared navigation UI (tabs, stacks, drawers).
15
+ - Remove old route files immediately when restructuring navigation.
16
+ - Use kebab-case for file names: `comment-card.tsx`.
17
+
18
+ ## Styling with NativeWind
19
+
20
+ - Use NativeWind (Tailwind for React Native) as the primary styling method.
21
+ - Use `className` prop like web Tailwind — NativeWind compiles to StyleSheet.
22
+ - Use `cn()` helper for conditional classes.
23
+ - Prefer `useWindowDimensions` over `Dimensions.get()` for responsive layouts.
24
+ - Use flexbox exclusively for layout — avoid absolute positioning except for overlays.
25
+
26
+ ## Running the App
27
+
28
+ - **Always try Expo Go first** before creating custom builds (`npx expo start`).
29
+ - Only use `npx expo run:ios/android` when custom native modules are required.
30
+ - Expo Go supports: all `expo-*` packages, Expo Router, Reanimated, Gesture Handler.
31
+
32
+ ## Components
33
+
34
+ - Use functional components only — never class components.
35
+ - Use `expo-image` instead of React Native's `Image` — better caching and performance.
36
+ - Use `expo-image` with `source="sf:name"` for SF Symbols icons — not `@expo/vector-icons`.
37
+ - Use `react-native-safe-area-context` — never React Native's built-in `SafeAreaView`.
38
+ - Use `<ScrollView contentInsetAdjustmentBehavior="automatic" />` instead of `<SafeAreaView>` for root scrolling.
39
+ - Use `process.env.EXPO_OS` instead of `Platform.OS`.
40
+ - Use `React.use` instead of `React.useContext`.
41
+
42
+ ## Navigation
43
+
44
+ - Use Expo Router's typed routes for type-safe navigation.
45
+ - Handle deep linking and universal links from project start.
46
+ - Use native tabs (`NativeTabs`) for iOS tab navigation when possible.
47
+ - Preload frequently accessed screens for faster navigation.
48
+
49
+ ## Performance
50
+
51
+ - **Profile before optimizing** — measure with React DevTools and Xcode Instruments.
52
+ - Replace `ScrollView` with `FlashList` (not `FlatList`) for large lists.
53
+ - Use React Compiler for automatic memoization when available.
54
+ - Avoid barrel exports — import directly from source files to reduce bundle size.
55
+ - Keep JS thread work minimal — offload to native with Turbo Modules when needed.
56
+ - Target 60fps — never block the JS thread with heavy computation.
57
+ - Use `useDeferredValue` for expensive computations during rendering.
58
+
59
+ ## Animations
60
+
61
+ - Use `react-native-reanimated` for performant animations on the UI thread.
62
+ - Animate only `transform` and `opacity` — avoid animating layout properties.
63
+ - Use shared values and worklets for gesture-driven animations.
64
+ - Respect `prefers-reduced-motion` accessibility setting.
65
+
66
+ ## Offline & Storage
67
+
68
+ - Use `@react-native-async-storage/async-storage` or `react-native-mmkv` for local key-value storage.
69
+ - Use `expo-sqlite` for structured offline data.
70
+ - Implement optimistic updates for network operations.
71
+ - Cache critical data locally for offline-first experience.
72
+
73
+ ## Push Notifications
74
+
75
+ - Use `expo-notifications` for push notification setup.
76
+ - Request permission at a contextual moment — never on first launch.
77
+ - Handle notification responses (tap, dismiss) in the app root.
78
+ - Store push tokens in your backend database, linked to user profiles.
79
+
80
+ ## App Distribution
81
+
82
+ - Use EAS Build for creating production builds.
83
+ - Use EAS Submit for app store submissions.
84
+ - Configure `app.json` / `app.config.ts` thoroughly: icons, splash, permissions, version.
85
+ - Test on both iOS and Android — never assume platform parity.
@@ -0,0 +1,98 @@
1
+ ---
2
+ name: seo-optimization
3
+ description: "SEO best practices for structured data, meta tags, Core Web Vitals, and indexing. Use when building landing pages, marketing sites, or public-facing content."
4
+ allowed-tools: "Read, Write, Edit, Glob, Grep"
5
+ ---
6
+
7
+ # SEO Optimization
8
+
9
+ ## Meta Tags
10
+
11
+ - Every page must have unique `title` and `description` meta tags:
12
+ ```ts
13
+ export const metadata: Metadata = {
14
+ title: 'Product Name — Clear Value Proposition',
15
+ description: 'Compelling 150-160 char description with primary keyword and call to action.',
16
+ }
17
+ ```
18
+ - Title format: `Page Title — Brand Name` (50-60 characters).
19
+ - Description: 150-160 characters, include primary keyword, end with CTA or benefit.
20
+ - Use `generateMetadata()` for dynamic routes to generate unique meta per page.
21
+
22
+ ## Open Graph & Social
23
+
24
+ - Include Open Graph tags for social sharing:
25
+ ```ts
26
+ openGraph: {
27
+ title: 'Page Title',
28
+ description: 'Social description',
29
+ images: [{ url: '/og-image.png', width: 1200, height: 630 }],
30
+ type: 'website',
31
+ }
32
+ ```
33
+ - Create a dedicated OG image for each important page (1200x630px).
34
+ - Add Twitter card meta: `twitter: { card: 'summary_large_image' }`.
35
+
36
+ ## Structured Data (JSON-LD)
37
+
38
+ - Add JSON-LD structured data for rich search results:
39
+ ```tsx
40
+ <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify({
41
+ '@context': 'https://schema.org',
42
+ '@type': 'Product',
43
+ name: product.name,
44
+ description: product.description,
45
+ offers: { '@type': 'Offer', price: product.price, priceCurrency: 'EUR' }
46
+ }) }} />
47
+ ```
48
+ - Use appropriate schema types: `Organization`, `Product`, `Article`, `FAQPage`, `BreadcrumbList`.
49
+ - Validate with Google's Rich Results Test.
50
+
51
+ ## Semantic HTML
52
+
53
+ - Use proper heading hierarchy: one `<h1>` per page, then `<h2>`, `<h3>`, etc.
54
+ - Use semantic elements: `<main>`, `<article>`, `<section>`, `<nav>`, `<aside>`, `<footer>`.
55
+ - Add `alt` text to all images — descriptive, not keyword-stuffed.
56
+ - Use `<a>` with descriptive anchor text — avoid "click here".
57
+
58
+ ## Performance (Core Web Vitals)
59
+
60
+ - Target scores: LCP < 2.5s, INP < 200ms, CLS < 0.1.
61
+ - Optimize images with `next/image`: proper sizing, modern formats (WebP/AVIF), lazy loading.
62
+ - Minimize render-blocking resources: defer non-critical CSS/JS.
63
+ - Use `next/font` to prevent FOUT/FOIT.
64
+ - Preload critical assets: hero images, above-the-fold fonts.
65
+
66
+ ## Technical SEO
67
+
68
+ - Generate `sitemap.xml` dynamically:
69
+ ```ts
70
+ // src/app/sitemap.ts
71
+ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
72
+ return [
73
+ { url: 'https://example.com', lastModified: new Date(), changeFrequency: 'weekly', priority: 1 },
74
+ ]
75
+ }
76
+ ```
77
+ - Create `robots.txt`:
78
+ ```ts
79
+ // src/app/robots.ts
80
+ export default function robots(): MetadataRoute.Robots {
81
+ return { rules: { userAgent: '*', allow: '/' }, sitemap: 'https://example.com/sitemap.xml' }
82
+ }
83
+ ```
84
+ - Use canonical URLs to prevent duplicate content issues.
85
+ - Implement proper redirects (301) for moved pages — never soft 404s.
86
+
87
+ ## Content
88
+
89
+ - Place primary keyword in the first 100 words of page content.
90
+ - Use internal links between related pages to distribute page authority.
91
+ - Add breadcrumb navigation with `BreadcrumbList` schema.
92
+ - Ensure all pages are reachable within 3 clicks from the homepage.
93
+
94
+ ## Internationalization
95
+
96
+ - Use `hreflang` tags for multi-language sites.
97
+ - Use `lang` attribute on `<html>` element.
98
+ - Create dedicated URLs per language: `/en/about`, `/fr/about`.
@@ -0,0 +1,92 @@
1
+ ---
2
+ name: shadcn-ui
3
+ description: "ShadCN UI component patterns, composition, and accessibility guidelines. Use when building interfaces with ShadCN components. Inspired by ibelick/ui-skills baseline-ui."
4
+ allowed-tools: "Read, Write, Edit, Glob, Grep"
5
+ ---
6
+
7
+ # ShadCN UI Patterns
8
+
9
+ ## Installation & Setup
10
+
11
+ - Install components individually with `npx shadcn@latest add <component>` — never install all at once.
12
+ - Components are copied to `src/components/ui/` — they are yours to customize.
13
+ - Configure `components.json` for path aliases and styling preferences.
14
+ - Use `cn()` from `src/lib/utils` for class merging (clsx + tailwind-merge).
15
+
16
+ ## Component Usage
17
+
18
+ - **Always use ShadCN primitives** before building custom components:
19
+ - `Button` for all clickable actions (with appropriate `variant` and `size`)
20
+ - `Card` for content containers
21
+ - `Dialog` for modal overlays
22
+ - `Sheet` for slide-out panels
23
+ - `DropdownMenu` for action menus
24
+ - `Select` for option picking
25
+ - `Table` for data display
26
+ - `Form` + `Input` + `Label` for forms
27
+ - `Badge` for tags and status indicators
28
+ - `Tabs` for content switching
29
+ - `Toast` / `Sonner` for notifications
30
+ - Never rebuild keyboard or focus behavior by hand — use the component primitives.
31
+ - Never mix primitive systems (Radix, Headless UI, React Aria) within the same surface.
32
+
33
+ ## Forms
34
+
35
+ - Use `react-hook-form` + `zod` with ShadCN's `Form` component:
36
+ ```tsx
37
+ const form = useForm<z.infer<typeof schema>>({
38
+ resolver: zodResolver(schema),
39
+ defaultValues: { name: '' },
40
+ })
41
+ ```
42
+ - Stack form fields with `space-y-4`.
43
+ - Use `grid gap-4 md:grid-cols-2` for side-by-side fields on desktop.
44
+ - Show validation errors inline with `FormMessage`.
45
+ - Use `FormDescription` for helper text below fields.
46
+
47
+ ## Data Tables
48
+
49
+ - Use ShadCN `DataTable` pattern with `@tanstack/react-table`:
50
+ - Define columns with type-safe `ColumnDef`.
51
+ - Support sorting, filtering, and pagination.
52
+ - Add row actions via `DropdownMenu`.
53
+ - Use `tabular-nums` on numeric columns for alignment.
54
+ - Add loading skeletons with ShadCN `Skeleton` for async data.
55
+
56
+ ## Dialogs & Alerts
57
+
58
+ - Use `Dialog` for informational or form-based modals.
59
+ - Use `AlertDialog` for destructive or irreversible actions — never a plain `Dialog`.
60
+ - Keep dialog content focused — one primary action per dialog.
61
+ - Always provide a way to dismiss (close button, escape key, outside click).
62
+
63
+ ## Theming
64
+
65
+ - Use CSS variables from the ShadCN theme system: `bg-background`, `text-foreground`, `bg-card`, `text-muted-foreground`, `bg-primary`, etc.
66
+ - Never hardcode hex colors — always use semantic tokens.
67
+ - Support dark mode via the `dark` class on `<html>` — CSS variables handle the switch.
68
+ - Limit accent color usage to one per view.
69
+
70
+ ## Accessibility
71
+
72
+ - All icon-only buttons must have `aria-label`.
73
+ - Use `sr-only` class for screen-reader-only text.
74
+ - Ensure all interactive elements have visible focus states (ShadCN handles this by default).
75
+ - Use `role` and `aria-*` attributes when composing custom components.
76
+ - Never block paste in `input` or `textarea` elements.
77
+
78
+ ## Animation
79
+
80
+ - Never add animation unless explicitly requested.
81
+ - Use `transition-colors` for hover/focus state changes.
82
+ - Keep interaction feedback under 200ms.
83
+ - Avoid animating layout properties (`width`, `height`, `margin`, `padding`) — use `transform` and `opacity` only.
84
+ - Respect `prefers-reduced-motion` media query.
85
+
86
+ ## Anti-Patterns to Avoid
87
+
88
+ - Never use `h-screen` — use `h-dvh` for correct mobile viewport.
89
+ - Never use arbitrary `z-index` values — use a fixed scale.
90
+ - Never use gradients or glow effects unless explicitly requested.
91
+ - Never use custom easing curves unless explicitly requested.
92
+ - Empty states must have one clear next action.
@@ -0,0 +1,88 @@
1
+ ---
2
+ name: stripe-integration
3
+ description: "Stripe payment integration patterns for checkout, subscriptions, and webhooks. Use when building e-commerce, SaaS billing, or any payment flow."
4
+ allowed-tools: "Read, Write, Edit, Glob, Grep"
5
+ ---
6
+
7
+ # Stripe Integration
8
+
9
+ ## Setup
10
+
11
+ - Install `stripe` (server) and `@stripe/stripe-js` + `@stripe/react-stripe-js` (client).
12
+ - Store keys in environment variables:
13
+ - `STRIPE_SECRET_KEY` — server-side only, never expose to client
14
+ - `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` — safe for client-side
15
+ - `STRIPE_WEBHOOK_SECRET` — for verifying webhook signatures
16
+ - Create a Stripe instance in `src/lib/stripe.ts`:
17
+ ```ts
18
+ import Stripe from 'stripe'
19
+ export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
20
+ ```
21
+
22
+ ## Checkout
23
+
24
+ - **Always use Stripe Checkout or Payment Elements** — never collect card details directly.
25
+ - Create a Checkout Session server-side, redirect client-side:
26
+ ```ts
27
+ const session = await stripe.checkout.sessions.create({
28
+ mode: 'payment', // or 'subscription'
29
+ line_items: [{ price: priceId, quantity: 1 }],
30
+ success_url: `${origin}/success?session_id={CHECKOUT_SESSION_ID}`,
31
+ cancel_url: `${origin}/cart`,
32
+ metadata: { userId, orderId },
33
+ })
34
+ ```
35
+ - Use `metadata` to link Stripe objects back to your database records.
36
+ - For subscriptions, use `mode: 'subscription'` with recurring prices.
37
+
38
+ ## Webhooks
39
+
40
+ - Create a webhook endpoint at `/api/webhooks/stripe`:
41
+ ```ts
42
+ const sig = request.headers.get('stripe-signature')!
43
+ const event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!)
44
+ ```
45
+ - Always verify webhook signatures. Never trust unverified payloads.
46
+ - Handle these critical events:
47
+ - `checkout.session.completed` — fulfill the order
48
+ - `invoice.payment_succeeded` — extend subscription
49
+ - `invoice.payment_failed` — notify user, retry logic
50
+ - `customer.subscription.deleted` — revoke access
51
+ - Make webhook handlers idempotent — the same event may arrive multiple times.
52
+ - Return 200 quickly. Process heavy logic asynchronously if needed.
53
+
54
+ ## Products & Prices
55
+
56
+ - Define products and prices in Stripe Dashboard for simplicity.
57
+ - Use `price` IDs (not `product` IDs) when creating Checkout Sessions.
58
+ - For multiple pricing tiers, create one product with multiple prices (monthly/yearly).
59
+ - Sync product/price data to your database via webhooks or a scheduled job.
60
+
61
+ ## Customer Portal
62
+
63
+ - Use Stripe Customer Portal for subscription management (upgrade, downgrade, cancel, update payment method).
64
+ - Create a portal session and redirect:
65
+ ```ts
66
+ const portalSession = await stripe.billingPortal.sessions.create({
67
+ customer: customerId,
68
+ return_url: `${origin}/account`,
69
+ })
70
+ ```
71
+
72
+ ## Security
73
+
74
+ - Never log or store raw card numbers, CVV, or full card details.
75
+ - Use Stripe's PCI-compliant elements for all card input.
76
+ - Validate amounts and currencies server-side before creating charges.
77
+ - Use `idempotencyKey` for critical operations to prevent duplicate charges.
78
+ - Restrict Stripe API key permissions in production (read-only where possible).
79
+
80
+ ## Testing
81
+
82
+ - Use Stripe test mode keys during development.
83
+ - Use test card numbers: `4242424242424242` (success), `4000000000000002` (decline).
84
+ - Use Stripe CLI to forward webhooks to localhost:
85
+ ```bash
86
+ stripe listen --forward-to localhost:3000/api/webhooks/stripe
87
+ ```
88
+ - Test all webhook event types, especially failure scenarios.
@@ -0,0 +1,93 @@
1
+ ---
2
+ name: supabase-patterns
3
+ description: "Supabase best practices for auth, database, RLS, and real-time. Use when working with Supabase in any project. Inspired by supabase/agent-skills."
4
+ allowed-tools: "Read, Write, Edit, Glob, Grep"
5
+ ---
6
+
7
+ # Supabase Patterns
8
+
9
+ ## Authentication
10
+
11
+ - Use `@supabase/ssr` for Next.js server-side auth — never use `@supabase/auth-helpers-nextjs` (deprecated).
12
+ - Create two client helpers:
13
+ - `createClient()` in `src/lib/supabase/client.ts` for Client Components
14
+ - `createServerClient()` in `src/lib/supabase/server.ts` for Server Components / Actions
15
+ - Always validate session server-side before granting access. Never trust `supabase.auth.getUser()` from the client alone.
16
+ - Use middleware (`middleware.ts`) to refresh the session on every request:
17
+ ```ts
18
+ const { data: { user } } = await supabase.auth.getUser()
19
+ if (!user && protectedRoutes.includes(request.nextUrl.pathname)) {
20
+ return NextResponse.redirect(new URL('/login', request.url))
21
+ }
22
+ ```
23
+ - Support multiple auth strategies: email/password, OAuth (Google, GitHub), magic link.
24
+ - Store user metadata in a separate `profiles` table linked by `auth.users.id`.
25
+
26
+ ## Row Level Security (RLS)
27
+
28
+ - **Enable RLS on every table** — no exceptions, even internal tables.
29
+ - Write policies that reference `auth.uid()` directly:
30
+ ```sql
31
+ CREATE POLICY "Users read own data"
32
+ ON profiles FOR SELECT
33
+ USING (auth.uid() = user_id);
34
+ ```
35
+ - Avoid `security definer` functions unless absolutely necessary. Prefer RLS policies.
36
+ - Test policies explicitly: try accessing data as different users/roles.
37
+ - Use `auth.jwt() ->> 'role'` for role-based access if implementing RBAC.
38
+
39
+ ## Database Schema
40
+
41
+ - Use `uuid` for primary keys, generated by `gen_random_uuid()`.
42
+ - Add `created_at` and `updated_at` timestamps to every table:
43
+ ```sql
44
+ created_at timestamptz DEFAULT now() NOT NULL,
45
+ updated_at timestamptz DEFAULT now() NOT NULL
46
+ ```
47
+ - Create an `updated_at` trigger using `moddatetime` extension.
48
+ - Use foreign key constraints for referential integrity.
49
+ - Add indexes on columns used in WHERE clauses and JOIN conditions.
50
+
51
+ ## Query Patterns
52
+
53
+ - Use the Supabase client query builder for simple CRUD:
54
+ ```ts
55
+ const { data, error } = await supabase
56
+ .from('posts')
57
+ .select('*, author:profiles(name, avatar_url)')
58
+ .eq('published', true)
59
+ .order('created_at', { ascending: false })
60
+ .limit(20)
61
+ ```
62
+ - Handle errors explicitly — never ignore `error`:
63
+ ```ts
64
+ if (error) throw new Error(error.message)
65
+ ```
66
+ - Use `.single()` when expecting exactly one row. Use `.maybeSingle()` when the row may not exist.
67
+ - Use server-side Supabase client for mutations in Server Actions.
68
+
69
+ ## Real-time
70
+
71
+ - Use Supabase Realtime only for features that genuinely need live updates (chat, notifications, collaboration).
72
+ - Subscribe in Client Components and clean up on unmount:
73
+ ```ts
74
+ useEffect(() => {
75
+ const channel = supabase.channel('room').on('postgres_changes', { event: '*', schema: 'public', table: 'messages' }, handler).subscribe()
76
+ return () => { supabase.removeChannel(channel) }
77
+ }, [])
78
+ ```
79
+ - Prefer polling or revalidation for data that changes infrequently.
80
+
81
+ ## Storage
82
+
83
+ - Use Supabase Storage for file uploads (images, documents).
84
+ - Create separate buckets for public vs private files.
85
+ - Set RLS policies on storage buckets.
86
+ - Generate signed URLs for private files with short expiration times.
87
+
88
+ ## Performance
89
+
90
+ - Use connection pooling (Supavisor) in production — never connect directly at scale.
91
+ - Add `LIMIT` to all queries. Never fetch unbounded result sets.
92
+ - Use `select('column1, column2')` instead of `select('*')` for large tables.
93
+ - Create partial indexes for frequently filtered subsets of data.