@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,233 @@
1
+ ---
2
+ name: astro-patterns
3
+ description: Astro patterns for islands architecture, content collections, SSG/SSR modes, integrations, and view transitions.
4
+ ---
5
+
6
+ # Astro Patterns
7
+
8
+ ## When to Use
9
+ Use Astro for content-heavy websites, documentation sites, blogs, marketing pages, and any project where most pages are static with selective interactivity. Astro's islands architecture ships zero JavaScript by default, hydrating only the interactive components you specify. Choose Astro when page load performance and SEO are top priorities.
10
+
11
+ ## How It Works
12
+
13
+ ### Project Structure and Configuration
14
+
15
+ ```typescript
16
+ // astro.config.mjs
17
+ import { defineConfig } from 'astro/config';
18
+ import react from '@astrojs/react';
19
+ import tailwind from '@astrojs/tailwind';
20
+ import mdx from '@astrojs/mdx';
21
+ import sitemap from '@astrojs/sitemap';
22
+ import vercel from '@astrojs/vercel';
23
+
24
+ export default defineConfig({
25
+ site: 'https://example.com',
26
+ output: 'hybrid', // static by default, opt-in to SSR per page
27
+ adapter: vercel(),
28
+ integrations: [
29
+ react(),
30
+ tailwind({ applyBaseStyles: false }),
31
+ mdx(),
32
+ sitemap({ filter: (page) => !page.includes('/admin/') }),
33
+ ],
34
+ image: { service: { entrypoint: 'astro/assets/services/sharp' } },
35
+ vite: { build: { assetsInlineLimit: 4096 } },
36
+ });
37
+ ```
38
+
39
+ ### Islands Architecture (Partial Hydration)
40
+
41
+ ```astro
42
+ ---
43
+ // src/pages/index.astro
44
+ import Layout from '../layouts/Base.astro';
45
+ import Hero from '../components/Hero.astro'; // Static — no JS shipped
46
+ import SearchBar from '../components/SearchBar'; // React — hydrated on load
47
+ import Newsletter from '../components/Newsletter'; // React — hydrated on visible
48
+ import Analytics from '../components/Analytics'; // React — hydrated on idle
49
+ ---
50
+
51
+ <Layout title="Home">
52
+ <Hero headline="Welcome" subline="Fast by default" />
53
+
54
+ <!-- Hydrate immediately — critical interactive component -->
55
+ <SearchBar client:load placeholder="Search docs..." />
56
+
57
+ <!-- Hydrate when scrolled into view — below the fold -->
58
+ <Newsletter client:visible />
59
+
60
+ <!-- Hydrate when browser is idle — non-critical -->
61
+ <Analytics client:idle />
62
+
63
+ <!-- Hydrate only on specific media query -->
64
+ <MobileMenu client:media="(max-width: 768px)" />
65
+ </Layout>
66
+ ```
67
+
68
+ ### Content Collections
69
+
70
+ ```typescript
71
+ // src/content/config.ts
72
+ import { defineCollection, z, reference } from 'astro:content';
73
+
74
+ const blog = defineCollection({
75
+ type: 'content',
76
+ schema: ({ image }) => z.object({
77
+ title: z.string().max(100),
78
+ description: z.string().max(200),
79
+ pubDate: z.coerce.date(),
80
+ updatedDate: z.coerce.date().optional(),
81
+ author: reference('authors'),
82
+ tags: z.array(z.string()).default([]),
83
+ cover: image().optional(),
84
+ draft: z.boolean().default(false),
85
+ }),
86
+ });
87
+
88
+ const authors = defineCollection({
89
+ type: 'data',
90
+ schema: z.object({
91
+ name: z.string(),
92
+ avatar: z.string().url(),
93
+ bio: z.string(),
94
+ social: z.object({
95
+ twitter: z.string().optional(),
96
+ github: z.string().optional(),
97
+ }),
98
+ }),
99
+ });
100
+
101
+ export const collections = { blog, authors };
102
+ ```
103
+
104
+ ```astro
105
+ ---
106
+ // src/pages/blog/[...slug].astro
107
+ import { getCollection } from 'astro:content';
108
+ import BlogPost from '../../layouts/BlogPost.astro';
109
+
110
+ export async function getStaticPaths() {
111
+ const posts = await getCollection('blog', ({ data }) => !data.draft);
112
+ return posts.map((post) => ({
113
+ params: { slug: post.slug },
114
+ props: { post },
115
+ }));
116
+ }
117
+
118
+ const { post } = Astro.props;
119
+ const { Content, headings } = await post.render();
120
+ ---
121
+
122
+ <BlogPost frontmatter={post.data} headings={headings}>
123
+ <Content />
124
+ </BlogPost>
125
+ ```
126
+
127
+ ### SSR with Server Endpoints
128
+
129
+ ```typescript
130
+ // src/pages/api/search.ts
131
+ import type { APIRoute } from 'astro';
132
+ import { getCollection } from 'astro:content';
133
+
134
+ export const prerender = false; // opt this route into SSR
135
+
136
+ export const GET: APIRoute = async ({ url }) => {
137
+ const query = url.searchParams.get('q')?.toLowerCase() ?? '';
138
+ if (query.length < 2) {
139
+ return new Response(JSON.stringify({ results: [] }), {
140
+ headers: { 'Content-Type': 'application/json' },
141
+ });
142
+ }
143
+
144
+ const posts = await getCollection('blog');
145
+ const results = posts
146
+ .filter((p) =>
147
+ p.data.title.toLowerCase().includes(query) ||
148
+ p.data.description.toLowerCase().includes(query)
149
+ )
150
+ .slice(0, 10)
151
+ .map((p) => ({ title: p.data.title, slug: p.slug, description: p.data.description }));
152
+
153
+ return new Response(JSON.stringify({ results }), {
154
+ headers: { 'Content-Type': 'application/json', 'Cache-Control': 'public, max-age=60' },
155
+ });
156
+ };
157
+ ```
158
+
159
+ ### View Transitions
160
+
161
+ ```astro
162
+ ---
163
+ // src/layouts/Base.astro
164
+ import { ViewTransitions } from 'astro:transitions';
165
+ ---
166
+
167
+ <html lang="en">
168
+ <head>
169
+ <meta charset="utf-8" />
170
+ <ViewTransitions />
171
+ </head>
172
+ <body>
173
+ <header transition:persist>
174
+ <nav><!-- persists across page navigations --></nav>
175
+ </header>
176
+
177
+ <main transition:animate="slide">
178
+ <slot />
179
+ </main>
180
+ </body>
181
+ </html>
182
+ ```
183
+
184
+ ### Custom Integration
185
+
186
+ ```typescript
187
+ // integrations/reading-time.ts
188
+ import type { AstroIntegration } from 'astro';
189
+ import { toString } from 'mdast-util-to-string';
190
+ import getReadingTime from 'reading-time';
191
+
192
+ export function readingTime(): AstroIntegration {
193
+ return {
194
+ name: 'reading-time',
195
+ hooks: {
196
+ 'astro:config:setup': ({ updateConfig }) => {
197
+ updateConfig({
198
+ markdown: {
199
+ remarkPlugins: [
200
+ () => (tree: any, file: any) => {
201
+ const text = toString(tree);
202
+ const { minutes } = getReadingTime(text);
203
+ file.data.astro.frontmatter.readingTime = Math.ceil(minutes);
204
+ },
205
+ ],
206
+ },
207
+ });
208
+ },
209
+ },
210
+ };
211
+ }
212
+ ```
213
+
214
+ ## Examples
215
+
216
+ | Directive | When It Hydrates | Use Case |
217
+ |-----------|-----------------|----------|
218
+ | `client:load` | Immediately on page load | Search bar, auth widget |
219
+ | `client:idle` | After page is idle | Analytics, chat widget |
220
+ | `client:visible` | When scrolled into viewport | Below-fold interactive content |
221
+ | `client:media="(max-width: 768px)"` | On matching media query | Mobile-only components |
222
+ | `client:only="react"` | Client-only, never SSR | Components using browser APIs |
223
+ | No directive | Never (static HTML) | Headers, footers, content |
224
+
225
+ ## Checklist
226
+ - [ ] Static components have no `client:*` directive (zero JS by default)
227
+ - [ ] Interactive islands use the least aggressive hydration directive needed
228
+ - [ ] Content collections have Zod schemas with validation in `config.ts`
229
+ - [ ] `getStaticPaths` filters out draft content in production
230
+ - [ ] SSR routes explicitly set `export const prerender = false`
231
+ - [ ] View transitions enabled with `transition:persist` on shared elements
232
+ - [ ] Images use `astro:assets` for automatic optimization
233
+ - [ ] Sitemap integration configured to exclude private routes
@@ -0,0 +1,248 @@
1
+ ---
2
+ name: auth-patterns
3
+ description: Implement authentication and authorization — JWT, refresh tokens, RBAC, OAuth2 PKCE, and API keys.
4
+ ---
5
+
6
+ # Auth Patterns
7
+
8
+ ## When to Use
9
+
10
+ Apply these patterns whenever your application has users, API consumers, or any
11
+ protected resource. Authentication (who are you?) and authorization (what can
12
+ you do?) are distinct concerns — implement both. Getting auth wrong means data
13
+ breaches, account takeovers, and compliance failures.
14
+
15
+ ## How It Works
16
+
17
+ ### 1. JWT Access Tokens
18
+
19
+ Short-lived tokens (15 minutes) for API authentication. Stateless verification
20
+ with no database lookup per request.
21
+
22
+ ```typescript
23
+ import jwt from 'jsonwebtoken';
24
+
25
+ interface TokenPayload {
26
+ sub: string; // user ID
27
+ role: string;
28
+ permissions: string[];
29
+ }
30
+
31
+ function signAccessToken(user: User): string {
32
+ const payload: TokenPayload = {
33
+ sub: user.id,
34
+ role: user.role,
35
+ permissions: user.permissions,
36
+ };
37
+ return jwt.sign(payload, process.env.JWT_SECRET!, { expiresIn: '15m' });
38
+ }
39
+
40
+ function verifyAccessToken(token: string): TokenPayload {
41
+ return jwt.verify(token, process.env.JWT_SECRET!) as TokenPayload;
42
+ }
43
+ ```
44
+
45
+ Never put sensitive data (email, password hash, PII) in the JWT payload — it's
46
+ base64 encoded, not encrypted.
47
+
48
+ ### 2. Refresh Tokens
49
+
50
+ Long-lived (7-30 days), stored in the database, rotated on each use.
51
+
52
+ ```typescript
53
+ async function refreshTokens(refreshToken: string) {
54
+ const stored = await db.refreshToken.findUnique({
55
+ where: { token: hashToken(refreshToken) },
56
+ include: { user: true },
57
+ });
58
+
59
+ if (!stored || stored.expiresAt < new Date()) {
60
+ throw new AuthError('Invalid refresh token');
61
+ }
62
+
63
+ // Rotate: delete old, create new
64
+ await db.refreshToken.delete({ where: { id: stored.id } });
65
+
66
+ const newRefreshToken = generateSecureToken();
67
+ await db.refreshToken.create({
68
+ data: {
69
+ token: hashToken(newRefreshToken),
70
+ userId: stored.userId,
71
+ expiresAt: addDays(new Date(), 7),
72
+ },
73
+ });
74
+
75
+ const accessToken = signAccessToken(stored.user);
76
+ return { accessToken, refreshToken: newRefreshToken };
77
+ }
78
+ ```
79
+
80
+ Store refresh tokens as hashed values. If the database leaks, raw tokens remain
81
+ unknown.
82
+
83
+ ### 3. RBAC — Role-Based Access Control
84
+
85
+ Define roles and permissions, check at the middleware level.
86
+
87
+ ```typescript
88
+ const ROLE_PERMISSIONS: Record<string, string[]> = {
89
+ admin: ['users:read', 'users:write', 'billing:manage', 'settings:write'],
90
+ editor: ['users:read', 'content:write', 'content:publish'],
91
+ viewer: ['users:read', 'content:read'],
92
+ };
93
+
94
+ function requirePermission(...required: string[]) {
95
+ return (req: Request, res: Response, next: NextFunction) => {
96
+ const userPermissions = req.user.permissions;
97
+ const hasAll = required.every((p) => userPermissions.includes(p));
98
+
99
+ if (!hasAll) {
100
+ return res.status(403).json({
101
+ error: { code: 'FORBIDDEN', message: 'Insufficient permissions' },
102
+ });
103
+ }
104
+ next();
105
+ };
106
+ }
107
+
108
+ // Usage
109
+ router.delete('/users/:id', requirePermission('users:write'), deleteUser);
110
+ router.get('/users', requirePermission('users:read'), listUsers);
111
+ ```
112
+
113
+ ### 4. Session Management
114
+
115
+ For server-rendered apps, use secure HTTP-only cookies.
116
+
117
+ ```typescript
118
+ app.use(session({
119
+ store: new RedisStore({ client: redisClient }),
120
+ secret: process.env.SESSION_SECRET!,
121
+ resave: false,
122
+ saveUninitialized: false,
123
+ cookie: {
124
+ httpOnly: true, // JavaScript can't read it
125
+ secure: true, // HTTPS only
126
+ sameSite: 'lax', // CSRF protection
127
+ maxAge: 24 * 60 * 60 * 1000, // 24 hours
128
+ },
129
+ }));
130
+ ```
131
+
132
+ ### 5. OAuth2 PKCE Flow
133
+
134
+ For SPAs and mobile apps that can't securely store client secrets.
135
+
136
+ ```typescript
137
+ // Step 1: Generate code verifier and challenge
138
+ function generatePKCE() {
139
+ const verifier = crypto.randomBytes(32).toString('base64url');
140
+ const challenge = crypto
141
+ .createHash('sha256')
142
+ .update(verifier)
143
+ .digest('base64url');
144
+ return { verifier, challenge };
145
+ }
146
+
147
+ // Step 2: Redirect to authorization server
148
+ const { verifier, challenge } = generatePKCE();
149
+ sessionStorage.setItem('pkce_verifier', verifier);
150
+
151
+ const authUrl = new URL('https://provider.com/authorize');
152
+ authUrl.searchParams.set('response_type', 'code');
153
+ authUrl.searchParams.set('client_id', CLIENT_ID);
154
+ authUrl.searchParams.set('redirect_uri', REDIRECT_URI);
155
+ authUrl.searchParams.set('code_challenge', challenge);
156
+ authUrl.searchParams.set('code_challenge_method', 'S256');
157
+ authUrl.searchParams.set('scope', 'openid profile email');
158
+
159
+ // Step 3: Exchange code for tokens (callback handler)
160
+ async function handleCallback(code: string) {
161
+ const verifier = sessionStorage.getItem('pkce_verifier');
162
+ const response = await fetch('https://provider.com/token', {
163
+ method: 'POST',
164
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
165
+ body: new URLSearchParams({
166
+ grant_type: 'authorization_code',
167
+ code,
168
+ redirect_uri: REDIRECT_URI,
169
+ client_id: CLIENT_ID,
170
+ code_verifier: verifier!,
171
+ }),
172
+ });
173
+ return response.json(); // { access_token, refresh_token, id_token }
174
+ }
175
+ ```
176
+
177
+ ### 6. API Keys
178
+
179
+ For server-to-server authentication. Simpler than OAuth, appropriate when the
180
+ caller is a trusted backend.
181
+
182
+ ```typescript
183
+ // Generation
184
+ function generateApiKey(): { key: string; hash: string } {
185
+ const prefix = 'bs_live_';
186
+ const secret = crypto.randomBytes(24).toString('base64url');
187
+ const key = `${prefix}${secret}`;
188
+ const hash = crypto.createHash('sha256').update(key).digest('hex');
189
+ return { key, hash }; // show key once, store hash
190
+ }
191
+
192
+ // Middleware
193
+ function authenticateApiKey(req: Request, res: Response, next: NextFunction) {
194
+ const key = req.headers['x-api-key'] as string;
195
+ if (!key) return res.status(401).json({ error: 'API key required' });
196
+
197
+ const hash = crypto.createHash('sha256').update(key).digest('hex');
198
+ const record = await db.apiKey.findUnique({ where: { hash } });
199
+
200
+ if (!record || record.revokedAt) {
201
+ return res.status(401).json({ error: 'Invalid API key' });
202
+ }
203
+
204
+ req.apiKeyOwner = record.userId;
205
+ next();
206
+ }
207
+ ```
208
+
209
+ ### 7. Authentication Middleware
210
+
211
+ ```typescript
212
+ function authenticate(req: Request, res: Response, next: NextFunction) {
213
+ const header = req.headers.authorization;
214
+ if (!header?.startsWith('Bearer ')) {
215
+ return res.status(401).json({ error: { code: 'UNAUTHENTICATED' } });
216
+ }
217
+
218
+ try {
219
+ const token = header.slice(7);
220
+ req.user = verifyAccessToken(token);
221
+ next();
222
+ } catch {
223
+ return res.status(401).json({ error: { code: 'TOKEN_EXPIRED' } });
224
+ }
225
+ }
226
+ ```
227
+
228
+ ## Examples
229
+
230
+ | Scenario | Pattern | Token lifetime |
231
+ |----------|---------|---------------|
232
+ | SPA calling own API | JWT access + refresh | 15min / 7 days |
233
+ | SSR web app | HTTP-only session cookie | 24 hours |
234
+ | Mobile app, third-party login | OAuth2 PKCE | Provider-dependent |
235
+ | Backend-to-backend | API key | Until revoked |
236
+ | Microservice mesh | mTLS or JWT with service accounts | 1 hour |
237
+
238
+ ## Checklist
239
+
240
+ - [ ] Access tokens expire in 15 minutes or less
241
+ - [ ] Refresh tokens are hashed in the database and rotated on use
242
+ - [ ] Passwords are hashed with bcrypt/argon2 (cost factor >= 10)
243
+ - [ ] RBAC permissions are checked in middleware, not scattered in handlers
244
+ - [ ] Cookies use `httpOnly`, `secure`, and `sameSite` flags
245
+ - [ ] OAuth2 flows use PKCE, never implicit grant
246
+ - [ ] API keys are prefixed (`bs_live_`, `bs_test_`) for identification
247
+ - [ ] Failed login attempts are rate-limited (5 per minute per IP)
248
+ - [ ] Token secrets and session secrets come from environment variables, never source code
@@ -0,0 +1,171 @@
1
+ ---
2
+ name: aws-patterns
3
+ description: AWS patterns for S3, Lambda, SQS, DynamoDB, CloudFront, and IAM best practices.
4
+ ---
5
+
6
+ # AWS Patterns
7
+
8
+ ## When to Use
9
+ Apply when architecting or operating workloads on AWS. Covers the most common building blocks: S3 for storage, Lambda for compute, SQS for decoupling, DynamoDB for NoSQL, CloudFront for CDN, and IAM for least-privilege access. Use this skill when designing new services, reviewing infrastructure, or optimizing AWS costs.
10
+
11
+ ## How It Works
12
+
13
+ ### S3 -- Object Storage
14
+
15
+ ```typescript
16
+ import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";
17
+ import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
18
+
19
+ const s3 = new S3Client({ region: "us-east-1" });
20
+
21
+ // Upload with server-side encryption
22
+ await s3.send(new PutObjectCommand({
23
+ Bucket: "my-app-uploads",
24
+ Key: `users/${userId}/avatar.png`,
25
+ Body: fileBuffer,
26
+ ContentType: "image/png",
27
+ ServerSideEncryption: "AES256",
28
+ }));
29
+
30
+ // Presigned URL for client-side upload (bypass your server)
31
+ const presignedUrl = await getSignedUrl(s3, new PutObjectCommand({
32
+ Bucket: "my-app-uploads",
33
+ Key: `users/${userId}/avatar.png`,
34
+ ContentType: "image/png",
35
+ }), { expiresIn: 300 });
36
+ ```
37
+
38
+ Rules: enable versioning, lifecycle policies to Glacier after 90 days, block all public access, use presigned URLs.
39
+
40
+ ### Lambda -- Stateless Functions
41
+
42
+ ```typescript
43
+ import { SQSEvent, Context } from "aws-lambda";
44
+
45
+ export const handler = async (event: SQSEvent, context: Context) => {
46
+ const failures: { itemIdentifier: string }[] = [];
47
+
48
+ for (const record of event.Records) {
49
+ try {
50
+ const order = JSON.parse(record.body);
51
+ await processOrder(order);
52
+ } catch {
53
+ failures.push({ itemIdentifier: record.messageId });
54
+ }
55
+ }
56
+ return { batchItemFailures: failures };
57
+ };
58
+ ```
59
+
60
+ Best practices: keep handler thin, set `reservedConcurrency`, use ARM64 (Graviton) for 20% savings, set timeout to 2x p99.
61
+
62
+ ### SQS -- Decouple Services
63
+
64
+ ```typescript
65
+ import { SQSClient, SendMessageCommand } from "@aws-sdk/client-sqs";
66
+
67
+ const sqs = new SQSClient({ region: "us-east-1" });
68
+
69
+ await sqs.send(new SendMessageCommand({
70
+ QueueUrl: process.env.ORDER_QUEUE_URL,
71
+ MessageBody: JSON.stringify({ orderId, action: "fulfill" }),
72
+ MessageGroupId: orderId,
73
+ MessageDeduplicationId: `${orderId}-fulfill`,
74
+ }));
75
+ ```
76
+
77
+ Use Standard queues for throughput, FIFO for ordering. Always configure a Dead Letter Queue with `maxReceiveCount: 3`.
78
+
79
+ ### DynamoDB -- Single-Table Design
80
+
81
+ ```typescript
82
+ // PK = entity type + ID, SK = relationship
83
+ await dynamo.put({
84
+ TableName: "AppTable",
85
+ Item: {
86
+ PK: `USER#${userId}`,
87
+ SK: `ORDER#${orderId}`,
88
+ GSI1PK: `ORDER#${orderId}`,
89
+ GSI1SK: `USER#${userId}`,
90
+ type: "order",
91
+ total: 4599,
92
+ status: "pending",
93
+ createdAt: new Date().toISOString(),
94
+ },
95
+ });
96
+
97
+ // Query all orders for a user
98
+ const orders = await dynamo.query({
99
+ TableName: "AppTable",
100
+ KeyConditionExpression: "PK = :pk AND begins_with(SK, :sk)",
101
+ ExpressionAttributeValues: { ":pk": `USER#${userId}`, ":sk": "ORDER#" },
102
+ });
103
+ ```
104
+
105
+ Design access patterns first, then the table. Use GSIs for inverse lookups. Avoid scans.
106
+
107
+ ### CloudFront -- CDN and Edge
108
+
109
+ ```yaml
110
+ Origins:
111
+ - Id: s3-static
112
+ DomainName: my-app-static.s3.amazonaws.com
113
+ S3OriginConfig:
114
+ OriginAccessIdentity: origin-access-identity/cloudfront/EXXXXX
115
+ - Id: api-origin
116
+ DomainName: api.my-app.com
117
+ CustomOriginConfig:
118
+ OriginProtocolPolicy: https-only
119
+ CacheBehaviors:
120
+ - PathPattern: /api/*
121
+ TargetOriginId: api-origin
122
+ CachePolicyId: 4135ea2d-... # CachingDisabled
123
+ - PathPattern: /*
124
+ TargetOriginId: s3-static
125
+ CachePolicyId: 658327ea-... # CachingOptimized
126
+ ```
127
+
128
+ ### IAM -- Least Privilege
129
+
130
+ ```json
131
+ {
132
+ "Version": "2012-10-17",
133
+ "Statement": [{
134
+ "Effect": "Allow",
135
+ "Action": ["s3:GetObject", "s3:PutObject"],
136
+ "Resource": "arn:aws:s3:::my-app-uploads/users/${aws:PrincipalTag/userId}/*"
137
+ }]
138
+ }
139
+ ```
140
+
141
+ Never use `*` for actions or resources in production. Use IAM Roles for Lambda/EC2/ECS, not access keys.
142
+
143
+ ### Cost Optimization
144
+
145
+ | Technique | Savings | Effort |
146
+ |-----------|---------|--------|
147
+ | Graviton (ARM) Lambda/EC2 | 20-40% | Low |
148
+ | S3 Intelligent-Tiering | 30-70% cold data | Low |
149
+ | Reserved Instances / Savings Plans | 30-60% steady-state | Medium |
150
+ | Right-size Lambda memory | 10-50% | Low |
151
+ | Spot Instances for batch | 60-90% | Medium |
152
+
153
+ ## Examples
154
+
155
+ | Service | Pattern | Key Consideration |
156
+ |---------|---------|-------------------|
157
+ | S3 | Presigned uploads | Never make buckets public |
158
+ | Lambda | SQS trigger + partial batch | Return batchItemFailures |
159
+ | SQS | FIFO + DLQ | Monitor oldest message age |
160
+ | DynamoDB | Single-table design | Access patterns first |
161
+ | CloudFront | Static + API origins | Disable cache for API |
162
+
163
+ ## Checklist
164
+ - [ ] S3 buckets have versioning, encryption, public access blocked
165
+ - [ ] Lambda functions have reserved concurrency and DLQ
166
+ - [ ] SQS queues have DLQs with `maxReceiveCount` set
167
+ - [ ] DynamoDB access patterns documented before table design
168
+ - [ ] CloudFront serves static assets; API bypasses cache
169
+ - [ ] IAM policies follow least privilege -- no `*` actions
170
+ - [ ] CloudWatch alarms on Lambda errors, SQS age, DynamoDB throttles
171
+ - [ ] Infrastructure defined in IaC (CDK, Terraform, or SAM)