@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,227 @@
1
+ ---
2
+ name: search-patterns
3
+ description: Search implementation patterns with full-text search, Elasticsearch, Meilisearch, faceted search, and autocomplete.
4
+ ---
5
+
6
+ # Search Patterns
7
+
8
+ ## When to Use
9
+ Apply when users need to find content in your application -- products, articles, users, or documents. Good search is the difference between "I cannot find anything" and "it reads my mind." Start with Postgres full-text search, graduate to Elasticsearch or Meilisearch when you need fuzzy matching, facets, or scale beyond 1M documents.
10
+
11
+ ## How It Works
12
+
13
+ ### Postgres Full-Text Search -- Start Here
14
+
15
+ Built into Postgres, no extra infrastructure:
16
+
17
+ ```sql
18
+ -- Add a tsvector column for search
19
+ ALTER TABLE products ADD COLUMN search_vector tsvector;
20
+
21
+ -- Populate from name and description with weighted fields
22
+ UPDATE products SET search_vector =
23
+ setweight(to_tsvector('english', coalesce(name, '')), 'A') ||
24
+ setweight(to_tsvector('english', coalesce(description, '')), 'B') ||
25
+ setweight(to_tsvector('english', coalesce(category, '')), 'C');
26
+
27
+ -- GIN index for fast lookups
28
+ CREATE INDEX idx_products_search ON products USING GIN (search_vector);
29
+
30
+ -- Auto-update with trigger
31
+ CREATE FUNCTION products_search_trigger() RETURNS trigger AS $$
32
+ BEGIN
33
+ NEW.search_vector :=
34
+ setweight(to_tsvector('english', coalesce(NEW.name, '')), 'A') ||
35
+ setweight(to_tsvector('english', coalesce(NEW.description, '')), 'B') ||
36
+ setweight(to_tsvector('english', coalesce(NEW.category, '')), 'C');
37
+ RETURN NEW;
38
+ END $$ LANGUAGE plpgsql;
39
+
40
+ CREATE TRIGGER products_search_update
41
+ BEFORE INSERT OR UPDATE ON products
42
+ FOR EACH ROW EXECUTE FUNCTION products_search_trigger();
43
+ ```
44
+
45
+ ```typescript
46
+ async function searchProducts(query: string, limit = 20) {
47
+ const result = await db.query(`
48
+ SELECT id, name, description,
49
+ ts_rank(search_vector, websearch_to_tsquery('english', $1)) AS rank
50
+ FROM products
51
+ WHERE search_vector @@ websearch_to_tsquery('english', $1)
52
+ ORDER BY rank DESC
53
+ LIMIT $2
54
+ `, [query, limit]);
55
+ return result.rows;
56
+ }
57
+ ```
58
+
59
+ ### Meilisearch -- Fast and Simple
60
+
61
+ Easier to run than Elasticsearch, great for most use cases:
62
+
63
+ ```typescript
64
+ import { MeiliSearch } from "meilisearch";
65
+
66
+ const client = new MeiliSearch({ host: "http://localhost:7700", apiKey: "masterKey" });
67
+ const index = client.index("products");
68
+
69
+ // Index documents
70
+ await index.addDocuments(products);
71
+
72
+ // Configure searchable and filterable attributes
73
+ await index.updateSettings({
74
+ searchableAttributes: ["name", "description", "category"],
75
+ filterableAttributes: ["category", "price", "inStock"],
76
+ sortableAttributes: ["price", "createdAt"],
77
+ typoTolerance: { enabled: true, minWordSizeForTypos: { oneTypo: 4, twoTypos: 8 } },
78
+ });
79
+
80
+ // Search with filters and facets
81
+ const results = await index.search("wireless keyboard", {
82
+ filter: ["category = electronics", "price < 100", "inStock = true"],
83
+ facets: ["category"],
84
+ sort: ["price:asc"],
85
+ limit: 20,
86
+ });
87
+ // results.hits, results.facetDistribution, results.estimatedTotalHits
88
+ ```
89
+
90
+ ### Elasticsearch -- Full-Featured
91
+
92
+ Use when you need complex aggregations or operate at > 10M documents:
93
+
94
+ ```typescript
95
+ import { Client } from "@elastic/elasticsearch";
96
+
97
+ const elastic = new Client({ node: "http://localhost:9200" });
98
+
99
+ // Search with boosting, fuzzy matching, and highlighting
100
+ async function search(query: string, filters: SearchFilters) {
101
+ const result = await elastic.search({
102
+ index: "products",
103
+ body: {
104
+ query: {
105
+ bool: {
106
+ must: {
107
+ multi_match: {
108
+ query,
109
+ fields: ["name^3", "description", "tags^2"],
110
+ fuzziness: "AUTO",
111
+ type: "best_fields",
112
+ },
113
+ },
114
+ filter: [
115
+ ...(filters.category ? [{ term: { category: filters.category } }] : []),
116
+ ...(filters.maxPrice ? [{ range: { price: { lte: filters.maxPrice } } }] : []),
117
+ ],
118
+ },
119
+ },
120
+ highlight: { fields: { name: {}, description: {} } },
121
+ aggs: {
122
+ categories: { terms: { field: "category.keyword", size: 20 } },
123
+ price_ranges: {
124
+ range: {
125
+ field: "price",
126
+ ranges: [
127
+ { key: "under-25", to: 25 },
128
+ { key: "25-50", from: 25, to: 50 },
129
+ { key: "50-100", from: 50, to: 100 },
130
+ { key: "over-100", from: 100 },
131
+ ],
132
+ },
133
+ },
134
+ },
135
+ },
136
+ });
137
+ return result;
138
+ }
139
+ ```
140
+
141
+ ### Autocomplete / Typeahead
142
+
143
+ Show suggestions as the user types. Must respond in under 100ms:
144
+
145
+ ```typescript
146
+ // Simple prefix matching in Postgres (good for < 100K items)
147
+ async function autocomplete(prefix: string, limit = 8) {
148
+ return db.query(
149
+ "SELECT DISTINCT name FROM products WHERE name ILIKE $1 ORDER BY popularity DESC LIMIT $2",
150
+ [`${prefix}%`, limit]
151
+ );
152
+ }
153
+
154
+ // Frontend -- debounce input, abort previous request
155
+ function useAutocomplete(query: string) {
156
+ const [suggestions, setSuggestions] = useState<string[]>([]);
157
+
158
+ useEffect(() => {
159
+ if (query.length < 2) { setSuggestions([]); return; }
160
+ const controller = new AbortController();
161
+ const timer = setTimeout(async () => {
162
+ const res = await fetch(`/api/autocomplete?q=${encodeURIComponent(query)}`, {
163
+ signal: controller.signal,
164
+ });
165
+ setSuggestions(await res.json());
166
+ }, 150);
167
+ return () => { clearTimeout(timer); controller.abort(); };
168
+ }, [query]);
169
+
170
+ return suggestions;
171
+ }
172
+ ```
173
+
174
+ ### Fuzzy Matching
175
+
176
+ Handle typos and misspellings:
177
+
178
+ ```sql
179
+ -- Postgres pg_trgm extension for trigram similarity
180
+ CREATE EXTENSION IF NOT EXISTS pg_trgm;
181
+ CREATE INDEX idx_products_name_trgm ON products USING GIN (name gin_trgm_ops);
182
+
183
+ -- Find "laptop" even if user types "laptpo"
184
+ SELECT name, similarity(name, 'laptpo') AS sim
185
+ FROM products
186
+ WHERE name % 'laptpo'
187
+ ORDER BY sim DESC
188
+ LIMIT 10;
189
+ ```
190
+
191
+ ### Faceted Search
192
+
193
+ Let users filter results by categories, price ranges, and attributes:
194
+
195
+ ```typescript
196
+ // Response shape for faceted search
197
+ interface SearchResponse {
198
+ hits: Product[];
199
+ total: number;
200
+ facets: {
201
+ categories: { value: string; count: number }[];
202
+ priceRanges: { label: string; min: number; max: number; count: number }[];
203
+ };
204
+ query: string;
205
+ took: number;
206
+ }
207
+ ```
208
+
209
+ ## Examples
210
+
211
+ | Scale | Tool | When |
212
+ |-------|------|------|
213
+ | < 100K docs | Postgres `tsvector` + GIN | Already have Postgres |
214
+ | 100K-10M, easy ops | Meilisearch | Need fuzzy + facets, simple setup |
215
+ | 10M+, complex queries | Elasticsearch | Need aggregations, custom scoring |
216
+ | Semantic search | pgvector, Pinecone | Meaning-based retrieval |
217
+ | Code search | `pg_trgm` | Substring and regex matching |
218
+
219
+ ## Checklist
220
+ - [ ] Search query debounced on frontend (150-300ms)
221
+ - [ ] Autocomplete returns results in under 100ms
222
+ - [ ] Fuzzy matching handles common typos
223
+ - [ ] Search fields weighted by importance (name > description)
224
+ - [ ] Results include highlighted matching snippets
225
+ - [ ] Faceted filters show counts and update dynamically
226
+ - [ ] Empty and no-results states show helpful suggestions
227
+ - [ ] Search index kept in sync with source database
@@ -0,0 +1,237 @@
1
+ ---
2
+ name: security-hardening
3
+ description: Harden web applications — CSP, CORS, rate limiting, input validation, injection prevention, and secrets management.
4
+ ---
5
+
6
+ # Security Hardening
7
+
8
+ ## When to Use
9
+
10
+ Apply these patterns to every web application and API from the start. Security
11
+ is not a feature you add later — it's a set of constraints that prevent
12
+ catastrophic failures. Each pattern here blocks a specific, common attack vector.
13
+
14
+ ## How It Works
15
+
16
+ ### 1. Content Security Policy (CSP)
17
+
18
+ Prevent XSS by controlling which resources the browser can load.
19
+
20
+ ```typescript
21
+ import helmet from 'helmet';
22
+
23
+ app.use(helmet({
24
+ contentSecurityPolicy: {
25
+ directives: {
26
+ defaultSrc: ["'self'"],
27
+ scriptSrc: ["'self'", "'nonce-{{NONCE}}'"], // no 'unsafe-inline'
28
+ styleSrc: ["'self'", "'unsafe-inline'"], // inline styles often needed
29
+ imgSrc: ["'self'", "data:", "https://cdn.example.com"],
30
+ connectSrc: ["'self'", "https://api.example.com"],
31
+ fontSrc: ["'self'", "https://fonts.gstatic.com"],
32
+ objectSrc: ["'none'"],
33
+ frameAncestors: ["'none'"],
34
+ },
35
+ },
36
+ }));
37
+ ```
38
+
39
+ Generate a unique nonce per request for inline scripts:
40
+
41
+ ```typescript
42
+ app.use((req, res, next) => {
43
+ res.locals.nonce = crypto.randomBytes(16).toString('base64');
44
+ next();
45
+ });
46
+ ```
47
+
48
+ ### 2. CORS Configuration
49
+
50
+ Only allow origins you explicitly trust.
51
+
52
+ ```typescript
53
+ import cors from 'cors';
54
+
55
+ app.use(cors({
56
+ origin: (origin, callback) => {
57
+ const allowed = ['https://app.example.com', 'https://admin.example.com'];
58
+ if (!origin || allowed.includes(origin)) {
59
+ callback(null, true);
60
+ } else {
61
+ callback(new Error('Not allowed by CORS'));
62
+ }
63
+ },
64
+ methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
65
+ allowedHeaders: ['Content-Type', 'Authorization'],
66
+ credentials: true,
67
+ maxAge: 86400, // preflight cache: 24 hours
68
+ }));
69
+ ```
70
+
71
+ Never use `origin: '*'` with `credentials: true` — browsers block it anyway,
72
+ and trying to work around it opens you to credential theft.
73
+
74
+ ### 3. Rate Limiting
75
+
76
+ Prevent brute force, DDoS, and abuse.
77
+
78
+ ```typescript
79
+ import rateLimit from 'express-rate-limit';
80
+
81
+ // Global: 100 requests per minute per IP
82
+ app.use(rateLimit({
83
+ windowMs: 60 * 1000,
84
+ max: 100,
85
+ standardHeaders: true,
86
+ legacyHeaders: false,
87
+ message: { error: { code: 'RATE_LIMITED', message: 'Too many requests' } },
88
+ }));
89
+
90
+ // Strict: 5 login attempts per 15 minutes
91
+ app.use('/api/auth/login', rateLimit({
92
+ windowMs: 15 * 60 * 1000,
93
+ max: 5,
94
+ skipSuccessfulRequests: true,
95
+ }));
96
+
97
+ // API keys: higher limits
98
+ app.use('/api/v1', rateLimit({
99
+ windowMs: 60 * 1000,
100
+ max: 1000,
101
+ keyGenerator: (req) => req.headers['x-api-key'] as string || req.ip,
102
+ }));
103
+ ```
104
+
105
+ For production, use a Redis-backed store so limits work across server instances.
106
+
107
+ ### 4. Input Validation
108
+
109
+ Validate and sanitize every input at the boundary. Use Zod for schema
110
+ validation.
111
+
112
+ ```typescript
113
+ import { z } from 'zod';
114
+
115
+ const CreateUserSchema = z.object({
116
+ name: z.string().min(1).max(100).trim(),
117
+ email: z.string().email().max(255).toLowerCase(),
118
+ age: z.number().int().min(13).max(150),
119
+ bio: z.string().max(2000).optional(),
120
+ });
121
+
122
+ function validateBody<T>(schema: z.ZodSchema<T>) {
123
+ return (req: Request, res: Response, next: NextFunction) => {
124
+ const result = schema.safeParse(req.body);
125
+ if (!result.success) {
126
+ return res.status(400).json({
127
+ error: {
128
+ code: 'VALIDATION_ERROR',
129
+ details: result.error.flatten().fieldErrors,
130
+ },
131
+ });
132
+ }
133
+ req.body = result.data; // replace with validated data
134
+ next();
135
+ };
136
+ }
137
+
138
+ router.post('/users', validateBody(CreateUserSchema), createUser);
139
+ ```
140
+
141
+ ### 5. SQL Injection Prevention
142
+
143
+ Use parameterized queries. Never concatenate user input into SQL.
144
+
145
+ ```typescript
146
+ // DANGEROUS — SQL injection
147
+ const result = await db.query(`SELECT * FROM users WHERE id = '${userId}'`);
148
+
149
+ // SAFE — parameterized query
150
+ const result = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
151
+
152
+ // SAFE — Prisma (parameterized by default)
153
+ const user = await prisma.user.findUnique({ where: { id: userId } });
154
+
155
+ // SAFE — tagged template (Prisma raw)
156
+ const result = await prisma.$queryRaw`SELECT * FROM users WHERE id = ${userId}`;
157
+ ```
158
+
159
+ ### 6. Security Headers
160
+
161
+ ```typescript
162
+ app.use(helmet({
163
+ hsts: { maxAge: 31536000, includeSubDomains: true, preload: true },
164
+ referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
165
+ noSniff: true, // X-Content-Type-Options: nosniff
166
+ frameguard: { action: 'deny' }, // X-Frame-Options: DENY
167
+ }));
168
+
169
+ // Permissions Policy — disable unused browser features
170
+ app.use((req, res, next) => {
171
+ res.setHeader('Permissions-Policy',
172
+ 'camera=(), microphone=(), geolocation=(), payment=()');
173
+ next();
174
+ });
175
+ ```
176
+
177
+ ### 7. Secrets Management
178
+
179
+ ```typescript
180
+ // Load from environment, never from code
181
+ const config = {
182
+ dbUrl: requireEnv('DATABASE_URL'),
183
+ jwtSecret: requireEnv('JWT_SECRET'),
184
+ apiKey: requireEnv('STRIPE_API_KEY'),
185
+ };
186
+
187
+ function requireEnv(name: string): string {
188
+ const value = process.env[name];
189
+ if (!value) throw new Error(`Missing required env var: ${name}`);
190
+ return value;
191
+ }
192
+ ```
193
+
194
+ Rules:
195
+ - `.env` is in `.gitignore` — always
196
+ - Use `.env.example` with placeholder values for documentation
197
+ - Production secrets come from the deployment platform (Vercel, AWS SSM, Vault)
198
+ - Rotate secrets quarterly; immediately after any exposure
199
+ - Never log secrets — redact in structured logging
200
+
201
+ ### 8. Request Body Limits
202
+
203
+ Prevent denial of service via large payloads.
204
+
205
+ ```typescript
206
+ app.use(express.json({ limit: '1mb' }));
207
+ app.use(express.urlencoded({ extended: true, limit: '1mb' }));
208
+
209
+ // Stricter limits on specific routes
210
+ app.post('/api/auth/login', express.json({ limit: '10kb' }), loginHandler);
211
+
212
+ // File upload routes get larger limits
213
+ app.post('/api/uploads', express.raw({ limit: '50mb' }), uploadHandler);
214
+ ```
215
+
216
+ ## Examples
217
+
218
+ | Attack | Defense | Implementation |
219
+ |--------|---------|---------------|
220
+ | XSS | CSP + input sanitization | helmet CSP + Zod validation |
221
+ | CSRF | SameSite cookies + CORS | `sameSite: 'lax'` + origin whitelist |
222
+ | Brute force | Rate limiting | express-rate-limit + Redis store |
223
+ | SQL injection | Parameterized queries | Prisma / `$1` placeholders |
224
+ | Credential theft | HTTPS + secure headers | HSTS + httpOnly cookies |
225
+ | Secret exposure | Environment variables | `.env` in `.gitignore` + requireEnv |
226
+
227
+ ## Checklist
228
+
229
+ - [ ] CSP is configured with no `unsafe-inline` for scripts
230
+ - [ ] CORS whitelist contains only known origins — no wildcards with credentials
231
+ - [ ] Rate limiting is enabled globally and stricter on auth endpoints
232
+ - [ ] All user input is validated with Zod at the route boundary
233
+ - [ ] All SQL uses parameterized queries or an ORM — zero string concatenation
234
+ - [ ] Helmet sets HSTS, noSniff, frameguard, and referrer policy
235
+ - [ ] `.env` is in `.gitignore` and secrets never appear in logs or responses
236
+ - [ ] Request body size limits are set per route
237
+ - [ ] Dependencies are audited (`npm audit`) and updated monthly
@@ -0,0 +1,179 @@
1
+ ---
2
+ name: seo-patterns
3
+ description: SEO patterns for meta tags, JSON-LD structured data, sitemaps, Core Web Vitals, and SSR/SSG rendering strategies.
4
+ ---
5
+
6
+ # SEO Patterns
7
+
8
+ ## When to Use
9
+ Apply these patterns to any public-facing web application. SEO is not an afterthought -- it is a core requirement for content sites, e-commerce, SaaS landing pages, and documentation. This skill covers technical SEO: meta tags, structured data (JSON-LD), sitemaps, Core Web Vitals optimization, and rendering strategy selection (SSR vs. SSG vs. ISR).
10
+
11
+ ## How It Works
12
+
13
+ ### Meta Tags -- The Foundation
14
+
15
+ ```tsx
16
+ // Next.js App Router metadata API
17
+ export async function generateMetadata({ params }: Props): Promise<Metadata> {
18
+ const product = await getProduct(params.slug);
19
+ return {
20
+ title: `${product.name} | MyStore`,
21
+ description: product.summary.slice(0, 155),
22
+ alternates: { canonical: `https://mystore.com/products/${params.slug}` },
23
+ openGraph: {
24
+ title: product.name,
25
+ description: product.summary,
26
+ images: [{ url: product.image, width: 1200, height: 630, alt: product.name }],
27
+ type: "website",
28
+ },
29
+ twitter: { card: "summary_large_image", title: product.name, images: [product.image] },
30
+ robots: { index: product.isPublished, follow: true },
31
+ };
32
+ }
33
+ ```
34
+
35
+ Every page needs a unique `<title>` (50-60 chars) and `<meta description>` (120-155 chars). Set canonical URL to prevent duplicate content. Use `noindex` for admin pages and search results.
36
+
37
+ ### Structured Data (JSON-LD)
38
+
39
+ ```tsx
40
+ function ProductJsonLd({ product }: { product: Product }) {
41
+ const jsonLd = {
42
+ "@context": "https://schema.org",
43
+ "@type": "Product",
44
+ name: product.name,
45
+ image: product.images,
46
+ description: product.description,
47
+ sku: product.sku,
48
+ brand: { "@type": "Brand", name: product.brand },
49
+ offers: {
50
+ "@type": "Offer",
51
+ priceCurrency: "USD",
52
+ price: product.price,
53
+ availability: product.inStock
54
+ ? "https://schema.org/InStock"
55
+ : "https://schema.org/OutOfStock",
56
+ },
57
+ aggregateRating: product.rating ? {
58
+ "@type": "AggregateRating",
59
+ ratingValue: product.rating.average,
60
+ reviewCount: product.rating.count,
61
+ } : undefined,
62
+ };
63
+ return (
64
+ <script
65
+ type="application/ld+json"
66
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
67
+ />
68
+ );
69
+ }
70
+ ```
71
+
72
+ Common schema types: `Article`, `Product`, `FAQPage`, `BreadcrumbList`, `Organization`, `LocalBusiness`.
73
+
74
+ ### Sitemap Generation
75
+
76
+ ```typescript
77
+ // Next.js App Router sitemap.ts
78
+ import { MetadataRoute } from "next";
79
+
80
+ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
81
+ const products = await db.products.findMany({
82
+ where: { isPublished: true },
83
+ select: { slug: true, updatedAt: true },
84
+ });
85
+
86
+ return [
87
+ { url: "https://mystore.com", lastModified: new Date(), changeFrequency: "daily", priority: 1 },
88
+ { url: "https://mystore.com/about", lastModified: new Date(), changeFrequency: "monthly", priority: 0.5 },
89
+ ...products.map((p) => ({
90
+ url: `https://mystore.com/products/${p.slug}`,
91
+ lastModified: p.updatedAt,
92
+ changeFrequency: "weekly" as const,
93
+ priority: 0.8,
94
+ })),
95
+ ];
96
+ }
97
+ ```
98
+
99
+ For large sites (>50K URLs), split into sitemap index files.
100
+
101
+ ### Core Web Vitals
102
+
103
+ | Metric | Target | Optimization |
104
+ |--------|--------|-------------|
105
+ | LCP | < 2.5s | Preload hero image, use `<Image priority>`, optimize server response |
106
+ | INP | < 200ms | Avoid long tasks, use `startTransition`, debounce inputs |
107
+ | CLS | < 0.1 | Set explicit width/height on images, avoid injecting content above fold |
108
+
109
+ ```tsx
110
+ // Preload critical hero image
111
+ <link rel="preload" as="image" href="/hero.webp" fetchPriority="high" />
112
+
113
+ // Next.js Image with priority
114
+ <Image src={product.image} alt={product.name} width={800} height={600}
115
+ priority sizes="(max-width: 768px) 100vw, 50vw" />
116
+
117
+ // Font preloading to prevent CLS
118
+ <link rel="preload" href="/fonts/inter-var.woff2" as="font"
119
+ type="font/woff2" crossOrigin="anonymous" />
120
+ ```
121
+
122
+ ### Rendering Strategy Selection
123
+
124
+ | Strategy | When to Use | SEO Impact |
125
+ |----------|-------------|------------|
126
+ | SSG | Content changes rarely (blog, docs) | Best -- instant, fully crawlable |
127
+ | ISR | Content changes periodically | Great -- static + fresh |
128
+ | SSR | Content changes per request | Good -- crawlable but slower |
129
+ | CSR | Behind auth, dashboards | Poor -- use SSR for public pages |
130
+
131
+ ```typescript
132
+ // Next.js ISR -- revalidate every 60 seconds
133
+ export const revalidate = 60;
134
+
135
+ export default async function ProductPage({ params }: Props) {
136
+ const product = await getProduct(params.slug);
137
+ return <ProductView product={product} />;
138
+ }
139
+ ```
140
+
141
+ ### Heading Hierarchy and Internal Linking
142
+
143
+ ```html
144
+ <!-- One H1 per page, logical heading hierarchy -->
145
+ <h1>Running Shoes</h1>
146
+ <h2>Men's Running Shoes</h2>
147
+ <h3>Trail Running</h3>
148
+ <h3>Road Running</h3>
149
+ <h2>Women's Running Shoes</h2>
150
+
151
+ <!-- Breadcrumbs with semantic markup -->
152
+ <nav aria-label="Breadcrumb">
153
+ <ol>
154
+ <li><a href="/">Home</a></li>
155
+ <li><a href="/shoes">Shoes</a></li>
156
+ <li aria-current="page">Running Shoes</li>
157
+ </ol>
158
+ </nav>
159
+ ```
160
+
161
+ ## Examples
162
+
163
+ | Pattern | Impact | Effort |
164
+ |---------|--------|--------|
165
+ | Unique title + description | High -- click-through rate | Low |
166
+ | JSON-LD structured data | High -- rich snippets | Medium |
167
+ | Sitemap | Medium -- crawl discovery | Low |
168
+ | Core Web Vitals | High -- ranking signal | Medium-High |
169
+ | SSG/ISR | High -- speed + crawlability | Medium |
170
+
171
+ ## Checklist
172
+ - [ ] Every public page has a unique title (50-60 chars) and description (120-155 chars)
173
+ - [ ] Canonical URLs set on all pages to prevent duplicate content
174
+ - [ ] JSON-LD structured data on product, article, FAQ, and organization pages
175
+ - [ ] Sitemap generated dynamically and submitted to Google Search Console
176
+ - [ ] robots.txt blocks admin, API, and auth routes
177
+ - [ ] Images have explicit width/height, alt text, and use WebP/AVIF formats
178
+ - [ ] LCP < 2.5s, INP < 200ms, CLS < 0.1 (measured with Lighthouse)
179
+ - [ ] Public content uses SSG or ISR, not client-side rendering