@girardmedia/bootspring 3.3.2 → 3.4.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 (171) hide show
  1. package/assets/agents/accessibility-auditor.md +39 -0
  2. package/assets/agents/api-designer.md +40 -0
  3. package/assets/agents/auth-implementer.md +64 -0
  4. package/assets/agents/bug-hunter.md +42 -0
  5. package/assets/agents/bundle-analyzer.md +40 -0
  6. package/assets/agents/cache-optimizer.md +55 -0
  7. package/assets/agents/changelog-writer.md +55 -0
  8. package/assets/agents/ci-cd-builder.md +40 -0
  9. package/assets/agents/code-explainer.md +39 -0
  10. package/assets/agents/code-reviewer.md +39 -0
  11. package/assets/agents/cost-optimizer.md +57 -0
  12. package/assets/agents/cron-scheduler.md +51 -0
  13. package/assets/agents/data-seeder.md +56 -0
  14. package/assets/agents/database-architect.md +40 -0
  15. package/assets/agents/dependency-updater.md +40 -0
  16. package/assets/agents/deploy-checker.md +40 -0
  17. package/assets/agents/docker-optimizer.md +40 -0
  18. package/assets/agents/documentation-writer.md +40 -0
  19. package/assets/agents/email-builder.md +55 -0
  20. package/assets/agents/env-setup.md +40 -0
  21. package/assets/agents/error-handler.md +40 -0
  22. package/assets/agents/eslint-fixer.md +46 -0
  23. package/assets/agents/feature-flagger.md +69 -0
  24. package/assets/agents/git-detective.md +39 -0
  25. package/assets/agents/graphql-builder.md +60 -0
  26. package/assets/agents/incident-responder.md +59 -0
  27. package/assets/agents/log-analyzer.md +39 -0
  28. package/assets/agents/migration-planner.md +41 -0
  29. package/assets/agents/monorepo-navigator.md +39 -0
  30. package/assets/agents/nextjs-expert.md +57 -0
  31. package/assets/agents/notification-builder.md +56 -0
  32. package/assets/agents/onboarding-guide.md +39 -0
  33. package/assets/agents/performance-profiler.md +40 -0
  34. package/assets/agents/prisma-expert.md +57 -0
  35. package/assets/agents/rate-limiter.md +58 -0
  36. package/assets/agents/react-expert.md +58 -0
  37. package/assets/agents/refactorer.md +42 -0
  38. package/assets/agents/regex-builder.md +46 -0
  39. package/assets/agents/release-manager.md +40 -0
  40. package/assets/agents/s3-manager.md +58 -0
  41. package/assets/agents/schema-validator.md +40 -0
  42. package/assets/agents/search-builder.md +62 -0
  43. package/assets/agents/security-auditor.md +39 -0
  44. package/assets/agents/sitemap-generator.md +53 -0
  45. package/assets/agents/stripe-integrator.md +59 -0
  46. package/assets/agents/tailwind-expert.md +55 -0
  47. package/assets/agents/tech-debt-tracker.md +39 -0
  48. package/assets/agents/test-writer.md +42 -0
  49. package/assets/agents/type-fixer.md +45 -0
  50. package/assets/agents/webhook-builder.md +54 -0
  51. package/assets/rules/cpp.md +53 -0
  52. package/assets/rules/css.md +52 -0
  53. package/assets/rules/go.md +50 -0
  54. package/assets/rules/html.md +52 -0
  55. package/assets/rules/java.md +51 -0
  56. package/assets/rules/kotlin.md +50 -0
  57. package/assets/rules/php.md +51 -0
  58. package/assets/rules/python.md +51 -0
  59. package/assets/rules/ruby.md +51 -0
  60. package/assets/rules/rust.md +49 -0
  61. package/assets/rules/shell.md +52 -0
  62. package/assets/rules/sql.md +49 -0
  63. package/assets/rules/swift.md +50 -0
  64. package/assets/rules/typescript.md +52 -0
  65. package/assets/rules/yaml-json.md +51 -0
  66. package/assets/skills/accessibility.md +210 -0
  67. package/assets/skills/agent-patterns.md +387 -0
  68. package/assets/skills/ai-integration.md +263 -0
  69. package/assets/skills/animation-patterns.md +224 -0
  70. package/assets/skills/api-design.md +218 -0
  71. package/assets/skills/api-gateway.md +341 -0
  72. package/assets/skills/api-versioning.md +226 -0
  73. package/assets/skills/astro-patterns.md +233 -0
  74. package/assets/skills/auth-patterns.md +248 -0
  75. package/assets/skills/aws-patterns.md +171 -0
  76. package/assets/skills/background-jobs.md +162 -0
  77. package/assets/skills/browser-extensions.md +309 -0
  78. package/assets/skills/caching-patterns.md +253 -0
  79. package/assets/skills/ci-cd.md +251 -0
  80. package/assets/skills/cli-development.md +296 -0
  81. package/assets/skills/code-review.md +185 -0
  82. package/assets/skills/cron-patterns.md +327 -0
  83. package/assets/skills/data-fetching.md +231 -0
  84. package/assets/skills/database-migrations.md +346 -0
  85. package/assets/skills/database-patterns.md +219 -0
  86. package/assets/skills/debugging.md +281 -0
  87. package/assets/skills/design-system.md +289 -0
  88. package/assets/skills/django-patterns.md +182 -0
  89. package/assets/skills/docker-patterns.md +235 -0
  90. package/assets/skills/e2e-testing.md +287 -0
  91. package/assets/skills/edge-computing.md +268 -0
  92. package/assets/skills/electron-patterns.md +266 -0
  93. package/assets/skills/email-templates.md +206 -0
  94. package/assets/skills/error-handling.md +265 -0
  95. package/assets/skills/event-driven.md +232 -0
  96. package/assets/skills/express-patterns.md +239 -0
  97. package/assets/skills/fastapi-patterns.md +198 -0
  98. package/assets/skills/feature-flags.md +212 -0
  99. package/assets/skills/figma-to-code.md +298 -0
  100. package/assets/skills/file-upload.md +228 -0
  101. package/assets/skills/forms-patterns.md +264 -0
  102. package/assets/skills/gcp-patterns.md +189 -0
  103. package/assets/skills/git-workflow.md +187 -0
  104. package/assets/skills/golang-patterns.md +185 -0
  105. package/assets/skills/graphql-patterns.md +244 -0
  106. package/assets/skills/i18n-patterns.md +172 -0
  107. package/assets/skills/image-processing.md +350 -0
  108. package/assets/skills/java-springboot.md +226 -0
  109. package/assets/skills/kotlin-patterns.md +207 -0
  110. package/assets/skills/kubernetes-patterns.md +326 -0
  111. package/assets/skills/laravel-patterns.md +261 -0
  112. package/assets/skills/llm-fine-tuning.md +335 -0
  113. package/assets/skills/load-testing.md +303 -0
  114. package/assets/skills/logging-observability.md +228 -0
  115. package/assets/skills/markdown-processing.md +318 -0
  116. package/assets/skills/mcp-server-patterns.md +292 -0
  117. package/assets/skills/microservices.md +272 -0
  118. package/assets/skills/migration-patterns.md +239 -0
  119. package/assets/skills/mongodb-patterns.md +189 -0
  120. package/assets/skills/monorepo-patterns.md +287 -0
  121. package/assets/skills/nextjs-app-router.md +237 -0
  122. package/assets/skills/notification-patterns.md +348 -0
  123. package/assets/skills/oauth-patterns.md +246 -0
  124. package/assets/skills/payment-integration.md +222 -0
  125. package/assets/skills/pdf-generation.md +307 -0
  126. package/assets/skills/performance-optimization.md +277 -0
  127. package/assets/skills/php-patterns.md +210 -0
  128. package/assets/skills/prisma-patterns.md +241 -0
  129. package/assets/skills/prompt-engineering.md +193 -0
  130. package/assets/skills/pwa-patterns.md +247 -0
  131. package/assets/skills/python-patterns.md +158 -0
  132. package/assets/skills/python-testing.md +172 -0
  133. package/assets/skills/queue-patterns.md +295 -0
  134. package/assets/skills/rag-patterns.md +159 -0
  135. package/assets/skills/rate-limiting.md +319 -0
  136. package/assets/skills/react-components.md +201 -0
  137. package/assets/skills/react-native-patterns.md +299 -0
  138. package/assets/skills/real-time-patterns.md +181 -0
  139. package/assets/skills/redis-patterns.md +188 -0
  140. package/assets/skills/refactoring.md +218 -0
  141. package/assets/skills/regex-patterns.md +191 -0
  142. package/assets/skills/remix-patterns.md +262 -0
  143. package/assets/skills/responsive-design.md +199 -0
  144. package/assets/skills/ruby-rails-patterns.md +178 -0
  145. package/assets/skills/rust-patterns.md +211 -0
  146. package/assets/skills/search-patterns.md +227 -0
  147. package/assets/skills/security-hardening.md +237 -0
  148. package/assets/skills/seo-patterns.md +179 -0
  149. package/assets/skills/serverless-patterns.md +223 -0
  150. package/assets/skills/sql-optimization.md +154 -0
  151. package/assets/skills/state-management.md +254 -0
  152. package/assets/skills/storybook-patterns.md +330 -0
  153. package/assets/skills/svelte-patterns.md +258 -0
  154. package/assets/skills/swift-patterns.md +227 -0
  155. package/assets/skills/tailwind-patterns.md +272 -0
  156. package/assets/skills/tdd-workflow.md +199 -0
  157. package/assets/skills/terraform-patterns.md +270 -0
  158. package/assets/skills/testing-react.md +240 -0
  159. package/assets/skills/testing-vitest.md +232 -0
  160. package/assets/skills/typescript-strict.md +159 -0
  161. package/assets/skills/video-processing.md +340 -0
  162. package/assets/skills/vue-patterns.md +247 -0
  163. package/assets/skills/web-workers.md +327 -0
  164. package/assets/skills/webhooks-patterns.md +283 -0
  165. package/assets/skills/websocket-patterns.md +306 -0
  166. package/dist/cli/index.js +941 -958
  167. package/dist/core/index.d.ts +341 -11
  168. package/dist/core.js +58 -95
  169. package/dist/mcp/index.d.ts +33 -1
  170. package/dist/mcp-server.js +177 -255
  171. package/package.json +4 -1
@@ -0,0 +1,223 @@
1
+ ---
2
+ name: serverless-patterns
3
+ description: Serverless patterns for cold starts, function composition, event triggers, DynamoDB, and step functions.
4
+ ---
5
+
6
+ # Serverless Patterns
7
+
8
+ ## When to Use
9
+
10
+ Apply these patterns when building on AWS Lambda, Azure Functions, or Google Cloud
11
+ Functions. Use this skill for minimizing cold starts, composing functions into
12
+ workflows, handling event triggers from queues and streams, designing DynamoDB
13
+ tables, and orchestrating multi-step processes with Step Functions.
14
+
15
+ ## How It Works
16
+
17
+ ### Cold Start Mitigation
18
+
19
+ Cold starts add latency on first invocation. Minimize them by keeping bundles
20
+ small, initializing outside the handler, and using provisioned concurrency for
21
+ latency-sensitive paths.
22
+
23
+ ```typescript
24
+ // Initialize OUTSIDE the handler — runs once per container
25
+ import { DynamoDBClient } from '@aws-sdk/client-dynamodb'
26
+ import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb'
27
+
28
+ const client = new DynamoDBClient({})
29
+ const ddb = DynamoDBDocumentClient.from(client)
30
+
31
+ // Lazy initialization for rarely-used resources
32
+ let s3Client: S3Client | null = null
33
+ function getS3() {
34
+ if (!s3Client) s3Client = new S3Client({})
35
+ return s3Client
36
+ }
37
+
38
+ // The handler itself should be fast
39
+ export async function handler(event: APIGatewayProxyEvent) {
40
+ const body = JSON.parse(event.body ?? '{}')
41
+ const result = await processRequest(ddb, body)
42
+ return {
43
+ statusCode: 200,
44
+ headers: { 'Content-Type': 'application/json' },
45
+ body: JSON.stringify(result),
46
+ }
47
+ }
48
+ ```
49
+
50
+ **Bundle size tips:**
51
+ - Use `esbuild` or `tsup` to tree-shake and bundle
52
+ - Import only needed clients: `@aws-sdk/client-dynamodb` not `aws-sdk`
53
+ - Keep the deployment package under 5MB zipped
54
+ - Exclude dev dependencies and test files
55
+
56
+ ### Event Triggers
57
+
58
+ Lambda functions respond to events from SQS, SNS, S3, EventBridge, DynamoDB
59
+ Streams, and API Gateway. Handle each event type with its own handler pattern.
60
+
61
+ ```typescript
62
+ // SQS batch handler with partial failure reporting
63
+ import type { SQSEvent, SQSBatchResponse } from 'aws-lambda'
64
+
65
+ export async function handler(event: SQSEvent): Promise<SQSBatchResponse> {
66
+ const failures: string[] = []
67
+
68
+ await Promise.allSettled(
69
+ event.Records.map(async (record) => {
70
+ try {
71
+ const body = JSON.parse(record.body)
72
+ await processMessage(body)
73
+ } catch (error) {
74
+ console.error(`Failed to process ${record.messageId}:`, error)
75
+ failures.push(record.messageId)
76
+ }
77
+ }),
78
+ )
79
+
80
+ return {
81
+ batchItemFailures: failures.map(id => ({ itemIdentifier: id })),
82
+ }
83
+ }
84
+
85
+ // S3 trigger
86
+ export async function handleUpload(event: S3Event) {
87
+ for (const record of event.Records) {
88
+ const bucket = record.s3.bucket.name
89
+ const key = decodeURIComponent(record.s3.object.key)
90
+ await processFile(bucket, key)
91
+ }
92
+ }
93
+ ```
94
+
95
+ ### DynamoDB Single-Table Design
96
+
97
+ Model multiple entity types in one table using composite keys. Use GSIs for
98
+ access patterns. Avoid scans; every query should hit an index.
99
+
100
+ ```typescript
101
+ // Single table: PK and SK encode entity type and ID
102
+ // Entity | PK | SK | GSI1PK | GSI1SK
103
+ // User | USER#123 | PROFILE | EMAIL#a@b.com | USER#123
104
+ // Order | USER#123 | ORDER#456 | ORDER#456 | STATUS#active
105
+ // OrderItem | ORDER#456 | ITEM#789 | — | —
106
+
107
+ interface TableItem {
108
+ PK: string
109
+ SK: string
110
+ GSI1PK?: string
111
+ GSI1SK?: string
112
+ type: string // discriminator
113
+ [key: string]: unknown
114
+ }
115
+
116
+ // Get user by ID
117
+ const user = await ddb.get({ TableName: TABLE, Key: { PK: `USER#${id}`, SK: 'PROFILE' } })
118
+
119
+ // Get all orders for user
120
+ const orders = await ddb.query({
121
+ TableName: TABLE,
122
+ KeyConditionExpression: 'PK = :pk AND begins_with(SK, :sk)',
123
+ ExpressionAttributeValues: { ':pk': `USER#${userId}`, ':sk': 'ORDER#' },
124
+ })
125
+
126
+ // Get order by order ID (via GSI)
127
+ const order = await ddb.query({
128
+ TableName: TABLE,
129
+ IndexName: 'GSI1',
130
+ KeyConditionExpression: 'GSI1PK = :pk',
131
+ ExpressionAttributeValues: { ':pk': `ORDER#${orderId}` },
132
+ })
133
+ ```
134
+
135
+ ### Step Functions for Orchestration
136
+
137
+ Use AWS Step Functions to orchestrate multi-step workflows. Define states as a
138
+ state machine. Handle retries, parallelism, and error handling declaratively.
139
+
140
+ ```json
141
+ {
142
+ "StartAt": "ValidateInput",
143
+ "States": {
144
+ "ValidateInput": {
145
+ "Type": "Task",
146
+ "Resource": "arn:aws:lambda:us-east-1:123:function:validate",
147
+ "Next": "ProcessParallel",
148
+ "Catch": [{ "ErrorEquals": ["ValidationError"], "Next": "HandleError" }]
149
+ },
150
+ "ProcessParallel": {
151
+ "Type": "Parallel",
152
+ "Branches": [
153
+ { "StartAt": "GenerateReport", "States": { "GenerateReport": { "Type": "Task", "Resource": "arn:aws:lambda:...:report", "End": true } } },
154
+ { "StartAt": "SendNotification", "States": { "SendNotification": { "Type": "Task", "Resource": "arn:aws:lambda:...:notify", "End": true } } }
155
+ ],
156
+ "Next": "Complete"
157
+ },
158
+ "Complete": { "Type": "Succeed" },
159
+ "HandleError": { "Type": "Fail", "Error": "ProcessingFailed" }
160
+ }
161
+ }
162
+ ```
163
+
164
+ ### Function Composition Patterns
165
+
166
+ Use SNS/SQS for fan-out, EventBridge for event routing, and Step Functions for
167
+ orchestration. Avoid direct Lambda-to-Lambda invocation.
168
+
169
+ ```
170
+ API Gateway -> Lambda (validate) -> SQS -> Lambda (process) -> DynamoDB
171
+ \-> Lambda (audit) -> CloudWatch
172
+
173
+ EventBridge Rule: source=orders, detail-type=OrderCreated
174
+ -> Target 1: Lambda (send-confirmation)
175
+ -> Target 2: Lambda (update-analytics)
176
+ -> Target 3: SQS (inventory-queue)
177
+ ```
178
+
179
+ ### Environment and Configuration
180
+
181
+ Use SSM Parameter Store or Secrets Manager for configuration. Cache values
182
+ in the Lambda container to avoid repeated API calls.
183
+
184
+ ```typescript
185
+ import { SSMClient, GetParameterCommand } from '@aws-sdk/client-ssm'
186
+
187
+ const ssm = new SSMClient({})
188
+ const paramCache = new Map<string, { value: string; expiry: number }>()
189
+
190
+ async function getParam(name: string, ttl = 300_000): Promise<string> {
191
+ const cached = paramCache.get(name)
192
+ if (cached && cached.expiry > Date.now()) return cached.value
193
+
194
+ const result = await ssm.send(new GetParameterCommand({ Name: name, WithDecryption: true }))
195
+ const value = result.Parameter!.Value!
196
+ paramCache.set(name, { value, expiry: Date.now() + ttl })
197
+ return value
198
+ }
199
+ ```
200
+
201
+ ## Examples
202
+
203
+ **Pattern: Idempotent handler with DynamoDB conditional write**
204
+ ```typescript
205
+ await ddb.put({
206
+ TableName: TABLE,
207
+ Item: { PK: `IDEM#${idempotencyKey}`, result, TTL: Math.floor(Date.now() / 1000) + 86400 },
208
+ ConditionExpression: 'attribute_not_exists(PK)',
209
+ })
210
+ ```
211
+
212
+ ## Checklist
213
+
214
+ - [ ] SDK clients initialized outside handler (reused across invocations)
215
+ - [ ] Bundle size under 5MB zipped; tree-shaken with esbuild
216
+ - [ ] Provisioned concurrency on latency-sensitive functions
217
+ - [ ] SQS handlers return `batchItemFailures` for partial failure
218
+ - [ ] DynamoDB single-table design with composite keys and GSIs
219
+ - [ ] No table scans; every access pattern uses a key or index
220
+ - [ ] Step Functions for multi-step orchestration (not Lambda-to-Lambda calls)
221
+ - [ ] SSM/Secrets Manager for config, cached in-container with TTL
222
+ - [ ] Idempotency keys on all mutating operations
223
+ - [ ] Dead letter queues on all SQS queues and async Lambda invocations
@@ -0,0 +1,154 @@
1
+ ---
2
+ name: sql-optimization
3
+ description: SQL optimization patterns with EXPLAIN ANALYZE, index strategies, query rewriting, CTEs, and window functions.
4
+ ---
5
+
6
+ # SQL Optimization
7
+
8
+ ## When to Use
9
+ Apply when queries are slow, tables are growing, or you are designing a new schema. SQL optimization starts with measuring (EXPLAIN ANALYZE), then choosing the right index, then restructuring the query. Fix the query first before adding application-level caching.
10
+
11
+ ## How It Works
12
+
13
+ ### EXPLAIN ANALYZE -- Always Measure First
14
+
15
+ Never guess. Run EXPLAIN ANALYZE to see actual execution time and row counts:
16
+
17
+ ```sql
18
+ EXPLAIN ANALYZE
19
+ SELECT o.id, o.total, u.email
20
+ FROM orders o
21
+ JOIN users u ON u.id = o.user_id
22
+ WHERE o.status = 'pending'
23
+ AND o.created_at > NOW() - INTERVAL '7 days'
24
+ ORDER BY o.created_at DESC
25
+ LIMIT 20;
26
+ ```
27
+
28
+ Key things to look for:
29
+ - **Seq Scan** on large tables -- needs an index
30
+ - **Rows** actual vs estimated -- stale statistics need `ANALYZE`
31
+ - **Sort** with high cost -- add an index matching the ORDER BY
32
+ - **Nested Loop** with high row count -- consider hash or merge join
33
+
34
+ ### Index Strategies
35
+
36
+ ```sql
37
+ -- Composite index: leftmost columns must match WHERE clause
38
+ CREATE INDEX idx_orders_status_created ON orders (status, created_at DESC);
39
+
40
+ -- Partial index: only index rows you actually query
41
+ CREATE INDEX idx_orders_pending ON orders (created_at DESC)
42
+ WHERE status = 'pending';
43
+
44
+ -- Covering index: includes all selected columns, avoids table lookup
45
+ CREATE INDEX idx_orders_cover ON orders (status, created_at DESC)
46
+ INCLUDE (total, user_id);
47
+
48
+ -- Expression index: index computed values
49
+ CREATE INDEX idx_users_lower_email ON users (LOWER(email));
50
+
51
+ -- Always use CONCURRENTLY in production
52
+ CREATE INDEX CONCURRENTLY idx_orders_user ON orders (user_id);
53
+ ```
54
+
55
+ Index rules of thumb:
56
+ - Columns in WHERE, JOIN ON, ORDER BY are index candidates
57
+ - High-cardinality columns first in composite indexes
58
+ - Partial indexes when you filter on a constant value
59
+ - Do not index columns that change frequently
60
+
61
+ ### CTEs -- Readable Subquery Composition
62
+
63
+ ```sql
64
+ WITH monthly_revenue AS (
65
+ SELECT DATE_TRUNC('month', created_at) AS month,
66
+ SUM(total) AS revenue
67
+ FROM orders
68
+ WHERE status = 'paid'
69
+ GROUP BY 1
70
+ ),
71
+ growth AS (
72
+ SELECT month,
73
+ revenue,
74
+ LAG(revenue) OVER (ORDER BY month) AS prev_revenue
75
+ FROM monthly_revenue
76
+ )
77
+ SELECT month,
78
+ revenue,
79
+ ROUND((revenue - prev_revenue) / NULLIF(prev_revenue, 0) * 100, 1) AS growth_pct
80
+ FROM growth
81
+ WHERE prev_revenue IS NOT NULL
82
+ ORDER BY month;
83
+ ```
84
+
85
+ In PostgreSQL 12+, CTEs are inlined by default. Use `MATERIALIZED` only when you need to force a single evaluation.
86
+
87
+ ### Window Functions -- Avoid Self-Joins
88
+
89
+ ```sql
90
+ -- Rank users by order count
91
+ SELECT user_id,
92
+ COUNT(*) AS order_count,
93
+ RANK() OVER (ORDER BY COUNT(*) DESC) AS rank
94
+ FROM orders
95
+ GROUP BY user_id;
96
+
97
+ -- Running total
98
+ SELECT id, amount,
99
+ SUM(amount) OVER (ORDER BY created_at ROWS UNBOUNDED PRECEDING) AS running_total
100
+ FROM transactions;
101
+
102
+ -- Get previous and next values without self-join
103
+ SELECT id, amount,
104
+ LAG(amount) OVER (ORDER BY created_at) AS prev_amount,
105
+ LEAD(amount) OVER (ORDER BY created_at) AS next_amount
106
+ FROM transactions;
107
+ ```
108
+
109
+ ### Partitioning -- Scale Large Tables
110
+
111
+ ```sql
112
+ CREATE TABLE events (
113
+ id BIGSERIAL,
114
+ event_type TEXT NOT NULL,
115
+ payload JSONB,
116
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
117
+ ) PARTITION BY RANGE (created_at);
118
+
119
+ CREATE TABLE events_2026_01 PARTITION OF events
120
+ FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
121
+ CREATE TABLE events_2026_02 PARTITION OF events
122
+ FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
123
+ ```
124
+
125
+ Always include the partition key in WHERE clauses for partition pruning.
126
+
127
+ ### Common Anti-Patterns
128
+
129
+ | Anti-Pattern | Fix |
130
+ |-------------|-----|
131
+ | `SELECT *` | List only needed columns |
132
+ | `WHERE function(col) = value` | Use expression index or rewrite |
133
+ | `LIKE '%term%'` | Use `pg_trgm` GIN index or full-text search |
134
+ | `NOT IN (subquery)` with NULLs | Use `NOT EXISTS` instead |
135
+ | Missing `LIMIT` on dashboards | Always paginate (keyset preferred) |
136
+ | `ORDER BY RANDOM()` | Use `TABLESAMPLE` or precomputed column |
137
+
138
+ ## Examples
139
+
140
+ | Scenario | Before | After |
141
+ |----------|--------|-------|
142
+ | Filter + sort | Seq Scan + Sort (2.3s) | Index Scan (4ms) |
143
+ | Aggregate + rank | Self-join (800ms) | Window function (50ms) |
144
+ | Time-range on 500M rows | Full scan (45s) | Partition pruning (200ms) |
145
+
146
+ ## Checklist
147
+ - [ ] `EXPLAIN ANALYZE` run on every query touching > 10K rows
148
+ - [ ] All foreign keys have indexes
149
+ - [ ] Composite indexes match common WHERE + ORDER BY patterns
150
+ - [ ] Partial indexes exist for status-filtered queries
151
+ - [ ] No `SELECT *` in application code
152
+ - [ ] Window functions replace self-joins and correlated subqueries
153
+ - [ ] Tables over 100M rows are partitioned
154
+ - [ ] `CREATE INDEX` always uses `CONCURRENTLY` in production
@@ -0,0 +1,254 @@
1
+ ---
2
+ name: state-management
3
+ description: Manage React state effectively — Zustand for client, TanStack Query for server, URL state, and optimistic updates.
4
+ ---
5
+
6
+ # State Management
7
+
8
+ ## When to Use
9
+
10
+ Choose the right state tool for the job. Most React apps misuse global state
11
+ stores for data that belongs in the URL, the server, or a local component.
12
+ The rule: keep state as close as possible to where it's used, and use the
13
+ simplest tool that works.
14
+
15
+ ## How It Works
16
+
17
+ ### 1. Decision Framework
18
+
19
+ | State type | Where it lives | Tool |
20
+ |-----------|----------------|------|
21
+ | Component UI (open/closed, hover) | Component `useState` | React |
22
+ | Form input | Component `useState` or form library | React / react-hook-form |
23
+ | URL state (filters, pagination, tabs) | URL search params | `useSearchParams` |
24
+ | Client-only global (theme, sidebar) | Global store | Zustand |
25
+ | Server data (users, orders) | Server cache | TanStack Query |
26
+ | Auth session | Context + server | Context + cookies |
27
+
28
+ ### 2. Zustand for Client State
29
+
30
+ Minimal, no boilerplate, works outside React components.
31
+
32
+ ```typescript
33
+ import { create } from 'zustand';
34
+ import { persist } from 'zustand/middleware';
35
+
36
+ interface UIStore {
37
+ sidebarOpen: boolean;
38
+ toggleSidebar: () => void;
39
+ theme: 'light' | 'dark';
40
+ setTheme: (theme: 'light' | 'dark') => void;
41
+ }
42
+
43
+ export const useUIStore = create<UIStore>()(
44
+ persist(
45
+ (set) => ({
46
+ sidebarOpen: true,
47
+ toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
48
+ theme: 'light',
49
+ setTheme: (theme) => set({ theme }),
50
+ }),
51
+ { name: 'ui-store' }, // persists to localStorage
52
+ ),
53
+ );
54
+ ```
55
+
56
+ Usage in components:
57
+
58
+ ```tsx
59
+ function Sidebar() {
60
+ const open = useUIStore((s) => s.sidebarOpen); // only re-renders on this slice
61
+ if (!open) return null;
62
+ return <nav>...</nav>;
63
+ }
64
+
65
+ function ThemeToggle() {
66
+ const [theme, setTheme] = useUIStore((s) => [s.theme, s.setTheme]);
67
+ return (
68
+ <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
69
+ {theme === 'light' ? 'Dark mode' : 'Light mode'}
70
+ </button>
71
+ );
72
+ }
73
+ ```
74
+
75
+ Select individual fields to avoid unnecessary re-renders.
76
+
77
+ ### 3. React Context — When Zustand is Overkill
78
+
79
+ For state scoped to a subtree, not the whole app.
80
+
81
+ ```tsx
82
+ interface ModalContextValue {
83
+ isOpen: boolean;
84
+ open: (content: ReactNode) => void;
85
+ close: () => void;
86
+ content: ReactNode | null;
87
+ }
88
+
89
+ const ModalContext = createContext<ModalContextValue | null>(null);
90
+
91
+ export function ModalProvider({ children }: { children: ReactNode }) {
92
+ const [state, setState] = useState<{ isOpen: boolean; content: ReactNode | null }>({
93
+ isOpen: false,
94
+ content: null,
95
+ });
96
+
97
+ const value = useMemo(() => ({
98
+ ...state,
99
+ open: (content: ReactNode) => setState({ isOpen: true, content }),
100
+ close: () => setState({ isOpen: false, content: null }),
101
+ }), [state]);
102
+
103
+ return (
104
+ <ModalContext.Provider value={value}>
105
+ {children}
106
+ {state.isOpen && <ModalOverlay>{state.content}</ModalOverlay>}
107
+ </ModalContext.Provider>
108
+ );
109
+ }
110
+
111
+ export function useModal() {
112
+ const ctx = useContext(ModalContext);
113
+ if (!ctx) throw new Error('useModal must be used within ModalProvider');
114
+ return ctx;
115
+ }
116
+ ```
117
+
118
+ ### 4. URL State for Shareable UI
119
+
120
+ Filters, pagination, search, and tabs should live in the URL so users can
121
+ bookmark and share.
122
+
123
+ ```tsx
124
+ import { useSearchParams } from 'react-router-dom';
125
+
126
+ function OrderList() {
127
+ const [params, setParams] = useSearchParams();
128
+ const status = params.get('status') ?? 'all';
129
+ const page = Number(params.get('page') ?? '1');
130
+ const search = params.get('q') ?? '';
131
+
132
+ function setFilter(key: string, value: string) {
133
+ setParams((prev) => {
134
+ const next = new URLSearchParams(prev);
135
+ if (value) next.set(key, value);
136
+ else next.delete(key);
137
+ next.set('page', '1'); // reset to page 1 on filter change
138
+ return next;
139
+ });
140
+ }
141
+
142
+ return (
143
+ <>
144
+ <FilterBar
145
+ status={status}
146
+ onStatusChange={(v) => setFilter('status', v)}
147
+ search={search}
148
+ onSearchChange={(v) => setFilter('q', v)}
149
+ />
150
+ <OrderTable status={status} page={page} search={search} />
151
+ <Pagination page={page} onChange={(p) => setFilter('page', String(p))} />
152
+ </>
153
+ );
154
+ }
155
+ ```
156
+
157
+ ### 5. TanStack Query for Server State
158
+
159
+ Server data is not client state. It has its own lifecycle: loading, fresh,
160
+ stale, error, background refresh.
161
+
162
+ ```typescript
163
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
164
+
165
+ // Fetch
166
+ function useOrders(filters: OrderFilters) {
167
+ return useQuery({
168
+ queryKey: ['orders', filters],
169
+ queryFn: () => api.orders.list(filters),
170
+ staleTime: 30_000, // fresh for 30s
171
+ gcTime: 5 * 60_000, // keep in cache 5min
172
+ placeholderData: (prev) => prev, // show old data while refetching
173
+ });
174
+ }
175
+
176
+ // Mutate with cache invalidation
177
+ function useDeleteOrder() {
178
+ const queryClient = useQueryClient();
179
+
180
+ return useMutation({
181
+ mutationFn: (id: string) => api.orders.delete(id),
182
+ onSuccess: () => {
183
+ queryClient.invalidateQueries({ queryKey: ['orders'] });
184
+ },
185
+ });
186
+ }
187
+ ```
188
+
189
+ ### 6. Optimistic Updates
190
+
191
+ Update the UI immediately, roll back if the server rejects.
192
+
193
+ ```typescript
194
+ function useToggleFavorite() {
195
+ const queryClient = useQueryClient();
196
+
197
+ return useMutation({
198
+ mutationFn: (id: string) => api.favorites.toggle(id),
199
+ onMutate: async (id) => {
200
+ await queryClient.cancelQueries({ queryKey: ['favorites'] });
201
+
202
+ const previous = queryClient.getQueryData<string[]>(['favorites']);
203
+
204
+ queryClient.setQueryData<string[]>(['favorites'], (old = []) =>
205
+ old.includes(id) ? old.filter((x) => x !== id) : [...old, id],
206
+ );
207
+
208
+ return { previous };
209
+ },
210
+ onError: (_err, _id, context) => {
211
+ // Roll back on failure
212
+ queryClient.setQueryData(['favorites'], context?.previous);
213
+ },
214
+ onSettled: () => {
215
+ queryClient.invalidateQueries({ queryKey: ['favorites'] });
216
+ },
217
+ });
218
+ }
219
+ ```
220
+
221
+ ### 7. Derived State — Compute, Don't Store
222
+
223
+ ```tsx
224
+ // Bad — derived state stored separately
225
+ const [items, setItems] = useState<Item[]>([]);
226
+ const [total, setTotal] = useState(0);
227
+ // total drifts if you forget to update it
228
+
229
+ // Good — compute from source
230
+ const [items, setItems] = useState<Item[]>([]);
231
+ const total = useMemo(() => items.reduce((sum, i) => sum + i.price, 0), [items]);
232
+ ```
233
+
234
+ ## Examples
235
+
236
+ | Scenario | Approach |
237
+ |----------|----------|
238
+ | Dark mode toggle | Zustand with `persist` middleware |
239
+ | Data table with filters | URL search params + TanStack Query |
240
+ | Shopping cart | Zustand (client state with persistence) |
241
+ | User profile data | TanStack Query (`useQuery`) |
242
+ | Like button | Optimistic mutation (`useMutation` + `onMutate`) |
243
+ | Modal open/close | Local `useState` or Context |
244
+
245
+ ## Checklist
246
+
247
+ - [ ] Server data uses TanStack Query, not `useState` + `useEffect`
248
+ - [ ] Filters, pagination, and search live in URL params
249
+ - [ ] Zustand selectors pick individual fields to minimize re-renders
250
+ - [ ] No derived state is stored — it's computed with `useMemo`
251
+ - [ ] Optimistic updates include rollback logic in `onError`
252
+ - [ ] Context is used for subtree state (modals, forms), Zustand for app-wide
253
+ - [ ] `staleTime` is configured per query to avoid unnecessary refetches
254
+ - [ ] Loading, error, and empty states are handled for every server query