@girardmedia/bootspring 1.2.0 → 2.0.3
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.
- package/README.md +107 -14
- package/bin/bootspring.js +166 -27
- package/cli/agent.js +189 -17
- package/cli/analyze.js +499 -0
- package/cli/audit.js +557 -0
- package/cli/auth.js +495 -38
- package/cli/billing.js +302 -0
- package/cli/build.js +695 -0
- package/cli/business.js +109 -26
- package/cli/checkpoint-utils.js +168 -0
- package/cli/checkpoint.js +639 -0
- package/cli/cloud-sync.js +447 -0
- package/cli/content.js +198 -0
- package/cli/context.js +1 -1
- package/cli/deploy.js +543 -0
- package/cli/fundraise.js +112 -50
- package/cli/github-cmd.js +435 -0
- package/cli/health.js +477 -0
- package/cli/init.js +84 -13
- package/cli/legal.js +107 -95
- package/cli/log.js +2 -2
- package/cli/loop.js +976 -73
- package/cli/manager.js +711 -0
- package/cli/metrics.js +480 -0
- package/cli/monitor.js +812 -0
- package/cli/onboard.js +521 -0
- package/cli/orchestrator.js +12 -24
- package/cli/prd.js +594 -0
- package/cli/preseed-start.js +1483 -0
- package/cli/preseed.js +2302 -0
- package/cli/project.js +436 -0
- package/cli/quality.js +233 -0
- package/cli/security.js +913 -0
- package/cli/seed.js +1441 -5
- package/cli/skill.js +273 -211
- package/cli/suggest.js +989 -0
- package/cli/switch.js +453 -0
- package/cli/visualize.js +527 -0
- package/cli/watch.js +769 -0
- package/cli/workspace.js +607 -0
- package/core/analyze-workflow.js +1134 -0
- package/core/api-client.js +535 -22
- package/core/audit-workflow.js +1350 -0
- package/core/build-orchestrator.js +480 -0
- package/core/build-state.js +577 -0
- package/core/checkpoint-engine.js +408 -0
- package/core/config.js +1109 -26
- package/core/context-loader.js +21 -1
- package/core/deploy-workflow.js +836 -0
- package/core/entitlements.js +93 -22
- package/core/github-sync.js +610 -0
- package/core/index.js +8 -1
- package/core/ingest.js +1111 -0
- package/core/metrics-engine.js +768 -0
- package/core/onboard-workflow.js +1007 -0
- package/core/preseed-workflow.js +934 -0
- package/core/preseed.js +1617 -0
- package/core/project-context.js +325 -0
- package/core/project-state.js +694 -0
- package/core/r2-sync.js +583 -0
- package/core/scaffold.js +525 -7
- package/core/session.js +258 -0
- package/core/task-extractor.js +758 -0
- package/core/telemetry.js +28 -6
- package/core/tier-enforcement.js +737 -0
- package/core/utils.js +38 -14
- package/generators/questionnaire.js +15 -12
- package/generators/sections/ai.js +7 -7
- package/generators/sections/content.js +300 -0
- package/generators/sections/index.js +3 -0
- package/generators/sections/plugins.js +7 -6
- package/generators/templates/build-planning.template.js +596 -0
- package/generators/templates/content.template.js +819 -0
- package/generators/templates/index.js +2 -1
- package/hooks/git-autopilot.js +1250 -0
- package/hooks/index.js +9 -0
- package/intelligence/agent-collab.js +2057 -0
- package/intelligence/auto-suggest.js +634 -0
- package/intelligence/content-gen.js +1589 -0
- package/intelligence/cross-project.js +1647 -0
- package/intelligence/index.js +184 -0
- package/intelligence/learning/insights.json +517 -7
- package/intelligence/learning/pattern-learner.js +1008 -14
- package/intelligence/memory/decision-tracker.js +1431 -31
- package/intelligence/memory/decisions.jsonl +0 -0
- package/intelligence/orchestrator.js +2896 -1
- package/intelligence/prd.js +92 -1
- package/intelligence/recommendation-weights.json +14 -2
- package/intelligence/recommendations.js +463 -9
- package/intelligence/workflow-composer.js +1451 -0
- package/marketplace/index.d.ts +324 -0
- package/marketplace/index.js +1921 -0
- package/mcp/contracts/mcp-contract.v1.json +342 -4
- package/mcp/registry.js +680 -3
- package/mcp/response-formatter.js +23 -0
- package/mcp/tools/assist-tool.js +78 -4
- package/mcp/tools/autopilot-tool.js +408 -0
- package/mcp/tools/content-tool.js +571 -0
- package/mcp/tools/dashboard-tool.js +251 -5
- package/mcp/tools/mvp-tool.js +344 -0
- package/mcp/tools/plugin-tool.js +23 -1
- package/mcp/tools/prd-tool.js +579 -0
- package/mcp/tools/seed-tool.js +447 -0
- package/mcp/tools/skill-tool.js +43 -14
- package/mcp/tools/suggest-tool.js +147 -0
- package/package.json +15 -6
- package/agents/README.md +0 -93
- package/agents/ai-integration-expert/context.md +0 -386
- package/agents/api-expert/context.md +0 -416
- package/agents/architecture-expert/context.md +0 -454
- package/agents/auth-expert/context.md +0 -399
- package/agents/backend-expert/context.md +0 -483
- package/agents/business-strategy-expert/context.md +0 -180
- package/agents/code-review-expert/context.md +0 -365
- package/agents/competitive-analysis-expert/context.md +0 -239
- package/agents/data-modeling-expert/context.md +0 -352
- package/agents/database-expert/context.md +0 -250
- package/agents/devops-expert/context.md +0 -446
- package/agents/email-expert/context.md +0 -379
- package/agents/financial-expert/context.md +0 -213
- package/agents/frontend-expert/context.md +0 -364
- package/agents/fundraising-expert/context.md +0 -257
- package/agents/growth-expert/context.md +0 -249
- package/agents/index.js +0 -140
- package/agents/investor-relations-expert/context.md +0 -266
- package/agents/legal-expert/context.md +0 -284
- package/agents/marketing-expert/context.md +0 -236
- package/agents/monitoring-expert/context.md +0 -362
- package/agents/operations-expert/context.md +0 -279
- package/agents/partnerships-expert/context.md +0 -286
- package/agents/payment-expert/context.md +0 -340
- package/agents/performance-expert/context.md +0 -377
- package/agents/private-equity-expert/context.md +0 -246
- package/agents/railway-expert/context.md +0 -284
- package/agents/research-expert/context.md +0 -245
- package/agents/sales-expert/context.md +0 -241
- package/agents/security-expert/context.md +0 -343
- package/agents/testing-expert/context.md +0 -414
- package/agents/ui-ux-expert/context.md +0 -448
- package/agents/vercel-expert/context.md +0 -426
- package/skills/index.js +0 -787
- package/skills/patterns/README.md +0 -163
- package/skills/patterns/ai/agents.md +0 -281
- package/skills/patterns/ai/claude.md +0 -138
- package/skills/patterns/ai/embeddings.md +0 -150
- package/skills/patterns/ai/rag.md +0 -266
- package/skills/patterns/ai/streaming.md +0 -170
- package/skills/patterns/ai/structured-output.md +0 -162
- package/skills/patterns/ai/tools.md +0 -154
- package/skills/patterns/analytics/tracking.md +0 -220
- package/skills/patterns/api/errors.md +0 -296
- package/skills/patterns/api/graphql.md +0 -440
- package/skills/patterns/api/middleware.md +0 -279
- package/skills/patterns/api/openapi.md +0 -285
- package/skills/patterns/api/rate-limiting.md +0 -231
- package/skills/patterns/api/route-handler.md +0 -217
- package/skills/patterns/api/server-action.md +0 -249
- package/skills/patterns/api/versioning.md +0 -443
- package/skills/patterns/api/webhooks.md +0 -247
- package/skills/patterns/auth/clerk.md +0 -132
- package/skills/patterns/auth/mfa.md +0 -313
- package/skills/patterns/auth/nextauth.md +0 -140
- package/skills/patterns/auth/oauth.md +0 -237
- package/skills/patterns/auth/rbac.md +0 -152
- package/skills/patterns/auth/session-management.md +0 -367
- package/skills/patterns/auth/session.md +0 -120
- package/skills/patterns/database/audit.md +0 -177
- package/skills/patterns/database/migrations.md +0 -177
- package/skills/patterns/database/pagination.md +0 -230
- package/skills/patterns/database/pooling.md +0 -357
- package/skills/patterns/database/prisma.md +0 -180
- package/skills/patterns/database/relations.md +0 -187
- package/skills/patterns/database/seeding.md +0 -246
- package/skills/patterns/database/soft-delete.md +0 -153
- package/skills/patterns/database/transactions.md +0 -162
- package/skills/patterns/deployment/ci-cd.md +0 -231
- package/skills/patterns/deployment/docker.md +0 -188
- package/skills/patterns/deployment/monitoring.md +0 -387
- package/skills/patterns/deployment/vercel.md +0 -160
- package/skills/patterns/email/resend.md +0 -143
- package/skills/patterns/email/templates.md +0 -245
- package/skills/patterns/email/transactional.md +0 -503
- package/skills/patterns/email/verification.md +0 -176
- package/skills/patterns/files/download.md +0 -243
- package/skills/patterns/files/upload.md +0 -239
- package/skills/patterns/i18n/nextintl.md +0 -188
- package/skills/patterns/logging/structured.md +0 -292
- package/skills/patterns/notifications/email-queue.md +0 -248
- package/skills/patterns/notifications/push.md +0 -279
- package/skills/patterns/payments/checkout.md +0 -303
- package/skills/patterns/payments/invoices.md +0 -287
- package/skills/patterns/payments/portal.md +0 -245
- package/skills/patterns/payments/stripe.md +0 -272
- package/skills/patterns/payments/subscriptions.md +0 -300
- package/skills/patterns/payments/usage.md +0 -279
- package/skills/patterns/performance/caching.md +0 -276
- package/skills/patterns/performance/code-splitting.md +0 -233
- package/skills/patterns/performance/edge.md +0 -254
- package/skills/patterns/performance/isr.md +0 -266
- package/skills/patterns/performance/lazy-loading.md +0 -281
- package/skills/patterns/realtime/sse.md +0 -327
- package/skills/patterns/realtime/websockets.md +0 -336
- package/skills/patterns/search/filtering.md +0 -329
- package/skills/patterns/search/fulltext.md +0 -260
- package/skills/patterns/security/audit-logging.md +0 -444
- package/skills/patterns/security/csrf.md +0 -234
- package/skills/patterns/security/headers.md +0 -252
- package/skills/patterns/security/sanitization.md +0 -258
- package/skills/patterns/security/secrets.md +0 -261
- package/skills/patterns/security/validation.md +0 -268
- package/skills/patterns/security/xss.md +0 -229
- package/skills/patterns/seo/metadata.md +0 -252
- package/skills/patterns/state/context.md +0 -349
- package/skills/patterns/state/react-query.md +0 -313
- package/skills/patterns/state/url-state.md +0 -482
- package/skills/patterns/state/zustand.md +0 -262
- package/skills/patterns/testing/api.md +0 -259
- package/skills/patterns/testing/component.md +0 -233
- package/skills/patterns/testing/coverage.md +0 -207
- package/skills/patterns/testing/fixtures.md +0 -225
- package/skills/patterns/testing/integration.md +0 -436
- package/skills/patterns/testing/mocking.md +0 -177
- package/skills/patterns/testing/playwright.md +0 -162
- package/skills/patterns/testing/snapshot.md +0 -175
- package/skills/patterns/testing/vitest.md +0 -307
- package/skills/patterns/ui/accordions.md +0 -395
- package/skills/patterns/ui/cards.md +0 -299
- package/skills/patterns/ui/dropdowns.md +0 -476
- package/skills/patterns/ui/empty-states.md +0 -320
- package/skills/patterns/ui/forms.md +0 -405
- package/skills/patterns/ui/inputs.md +0 -319
- package/skills/patterns/ui/layouts.md +0 -282
- package/skills/patterns/ui/loading.md +0 -291
- package/skills/patterns/ui/modals.md +0 -338
- package/skills/patterns/ui/navigation.md +0 -374
- package/skills/patterns/ui/tables.md +0 -407
- package/skills/patterns/ui/toasts.md +0 -300
- package/skills/patterns/ui/tooltips.md +0 -396
- package/skills/patterns/utils/dates.md +0 -435
- package/skills/patterns/utils/errors.md +0 -451
- package/skills/patterns/utils/formatting.md +0 -345
- package/skills/patterns/utils/validation.md +0 -434
- package/templates/bootspring.config.js +0 -83
- package/templates/business/business-model-canvas.md +0 -246
- package/templates/business/business-plan.md +0 -266
- package/templates/business/competitive-analysis.md +0 -312
- package/templates/fundraising/data-room-checklist.md +0 -300
- package/templates/fundraising/investor-research.md +0 -243
- package/templates/fundraising/pitch-deck-outline.md +0 -253
- package/templates/legal/gdpr-checklist.md +0 -339
- package/templates/legal/privacy-policy.md +0 -285
- package/templates/legal/terms-of-service.md +0 -222
- package/templates/mcp.json +0 -9
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
# Code Splitting Patterns
|
|
2
|
-
|
|
3
|
-
Patterns for optimizing bundle size with code splitting.
|
|
4
|
-
|
|
5
|
-
## Route-Based Splitting
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
// Next.js does this automatically with the App Router
|
|
9
|
-
// Each route segment is a separate chunk
|
|
10
|
-
|
|
11
|
-
// app/
|
|
12
|
-
// ├── page.tsx -> main chunk
|
|
13
|
-
// ├── dashboard/
|
|
14
|
-
// │ └── page.tsx -> dashboard chunk
|
|
15
|
-
// ├── settings/
|
|
16
|
-
// │ └── page.tsx -> settings chunk
|
|
17
|
-
// └── admin/
|
|
18
|
-
// └── page.tsx -> admin chunk
|
|
19
|
-
|
|
20
|
-
// Route groups don't affect bundling
|
|
21
|
-
// app/(marketing)/page.tsx -> same chunk as app/page.tsx
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Component-Level Splitting
|
|
25
|
-
|
|
26
|
-
```tsx
|
|
27
|
-
// Split heavy components
|
|
28
|
-
import dynamic from 'next/dynamic'
|
|
29
|
-
|
|
30
|
-
// Editor only loads when needed
|
|
31
|
-
const RichTextEditor = dynamic(
|
|
32
|
-
() => import('@/components/RichTextEditor'),
|
|
33
|
-
{
|
|
34
|
-
loading: () => <EditorSkeleton />,
|
|
35
|
-
ssr: false // Client-only
|
|
36
|
-
}
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
// Chart library only loads on dashboard
|
|
40
|
-
const Charts = dynamic(() => import('@/components/Charts'))
|
|
41
|
-
|
|
42
|
-
// Code highlighter only loads in docs
|
|
43
|
-
const CodeBlock = dynamic(
|
|
44
|
-
() => import('@/components/CodeBlock').then(mod => mod.CodeBlock)
|
|
45
|
-
)
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## Library Splitting
|
|
49
|
-
|
|
50
|
-
```tsx
|
|
51
|
-
// Split heavy libraries
|
|
52
|
-
// DON'T import the whole library at top level
|
|
53
|
-
// import { format, parse, addDays, subDays, ... } from 'date-fns'
|
|
54
|
-
|
|
55
|
-
// DO import specific functions
|
|
56
|
-
import { format } from 'date-fns/format'
|
|
57
|
-
import { parseISO } from 'date-fns/parseISO'
|
|
58
|
-
|
|
59
|
-
// Or dynamic import for occasional use
|
|
60
|
-
async function formatDate(date: Date) {
|
|
61
|
-
const { format } = await import('date-fns')
|
|
62
|
-
return format(date, 'PPP')
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Lodash - use per-function imports
|
|
66
|
-
import debounce from 'lodash/debounce'
|
|
67
|
-
// NOT: import { debounce } from 'lodash'
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## Conditional Imports
|
|
71
|
-
|
|
72
|
-
```tsx
|
|
73
|
-
// Load features based on user role
|
|
74
|
-
'use client'
|
|
75
|
-
|
|
76
|
-
import dynamic from 'next/dynamic'
|
|
77
|
-
import { useSession } from 'next-auth/react'
|
|
78
|
-
|
|
79
|
-
const AdminPanel = dynamic(() => import('@/components/AdminPanel'))
|
|
80
|
-
const ModeratorTools = dynamic(() => import('@/components/ModeratorTools'))
|
|
81
|
-
|
|
82
|
-
export function Dashboard() {
|
|
83
|
-
const { data: session } = useSession()
|
|
84
|
-
|
|
85
|
-
return (
|
|
86
|
-
<div>
|
|
87
|
-
<MainContent />
|
|
88
|
-
|
|
89
|
-
{session?.user.role === 'ADMIN' && <AdminPanel />}
|
|
90
|
-
{session?.user.role === 'MODERATOR' && <ModeratorTools />}
|
|
91
|
-
</div>
|
|
92
|
-
)
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## Bundle Analysis
|
|
97
|
-
|
|
98
|
-
```typescript
|
|
99
|
-
// next.config.js
|
|
100
|
-
const withBundleAnalyzer = require('@next/bundle-analyzer')({
|
|
101
|
-
enabled: process.env.ANALYZE === 'true'
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
module.exports = withBundleAnalyzer({
|
|
105
|
-
// ... other config
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
// Run analysis
|
|
109
|
-
// ANALYZE=true npm run build
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
## Shared Chunks
|
|
113
|
-
|
|
114
|
-
```typescript
|
|
115
|
-
// next.config.js
|
|
116
|
-
module.exports = {
|
|
117
|
-
experimental: {
|
|
118
|
-
optimizePackageImports: [
|
|
119
|
-
'lucide-react',
|
|
120
|
-
'@radix-ui/react-icons',
|
|
121
|
-
'date-fns',
|
|
122
|
-
'lodash'
|
|
123
|
-
]
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// This automatically tree-shakes these packages
|
|
128
|
-
// import { Search, Menu, X } from 'lucide-react'
|
|
129
|
-
// Only the used icons are bundled
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
## Parallel Route Loading
|
|
133
|
-
|
|
134
|
-
```tsx
|
|
135
|
-
// app/dashboard/layout.tsx
|
|
136
|
-
// Load multiple route segments in parallel
|
|
137
|
-
|
|
138
|
-
export default function DashboardLayout({
|
|
139
|
-
children,
|
|
140
|
-
analytics,
|
|
141
|
-
activity
|
|
142
|
-
}: {
|
|
143
|
-
children: React.ReactNode
|
|
144
|
-
analytics: React.ReactNode
|
|
145
|
-
activity: React.ReactNode
|
|
146
|
-
}) {
|
|
147
|
-
return (
|
|
148
|
-
<div className="grid grid-cols-3 gap-4">
|
|
149
|
-
<div className="col-span-2">{children}</div>
|
|
150
|
-
<div className="space-y-4">
|
|
151
|
-
{analytics}
|
|
152
|
-
{activity}
|
|
153
|
-
</div>
|
|
154
|
-
</div>
|
|
155
|
-
)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// app/dashboard/@analytics/page.tsx
|
|
159
|
-
// app/dashboard/@activity/page.tsx
|
|
160
|
-
// These load in parallel, each as separate chunks
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
## Module Federation (Micro-frontends)
|
|
164
|
-
|
|
165
|
-
```typescript
|
|
166
|
-
// next.config.js
|
|
167
|
-
const { NextFederationPlugin } = require('@module-federation/nextjs-mf')
|
|
168
|
-
|
|
169
|
-
module.exports = {
|
|
170
|
-
webpack(config, options) {
|
|
171
|
-
config.plugins.push(
|
|
172
|
-
new NextFederationPlugin({
|
|
173
|
-
name: 'host',
|
|
174
|
-
remotes: {
|
|
175
|
-
checkout: 'checkout@https://checkout.example.com/remoteEntry.js',
|
|
176
|
-
analytics: 'analytics@https://analytics.example.com/remoteEntry.js'
|
|
177
|
-
},
|
|
178
|
-
shared: {
|
|
179
|
-
react: { singleton: true },
|
|
180
|
-
'react-dom': { singleton: true }
|
|
181
|
-
}
|
|
182
|
-
})
|
|
183
|
-
)
|
|
184
|
-
return config
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Usage
|
|
189
|
-
const RemoteCheckout = dynamic(
|
|
190
|
-
() => import('checkout/CheckoutForm'),
|
|
191
|
-
{ ssr: false }
|
|
192
|
-
)
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
## Preloading Critical Chunks
|
|
196
|
-
|
|
197
|
-
```tsx
|
|
198
|
-
// Preload chunks that will likely be needed
|
|
199
|
-
import Link from 'next/link'
|
|
200
|
-
|
|
201
|
-
export function Navigation() {
|
|
202
|
-
return (
|
|
203
|
-
<nav>
|
|
204
|
-
{/* Prefetch on hover/focus */}
|
|
205
|
-
<Link href="/dashboard" prefetch={true}>
|
|
206
|
-
Dashboard
|
|
207
|
-
</Link>
|
|
208
|
-
|
|
209
|
-
{/* Disable prefetch for rarely visited */}
|
|
210
|
-
<Link href="/settings" prefetch={false}>
|
|
211
|
-
Settings
|
|
212
|
-
</Link>
|
|
213
|
-
</nav>
|
|
214
|
-
)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Manual preloading
|
|
218
|
-
import dynamic from 'next/dynamic'
|
|
219
|
-
|
|
220
|
-
const HeavyModal = dynamic(() => import('@/components/HeavyModal'))
|
|
221
|
-
|
|
222
|
-
// Preload when user shows intent
|
|
223
|
-
function handleMouseEnter() {
|
|
224
|
-
import('@/components/HeavyModal')
|
|
225
|
-
}
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
## When to Use
|
|
229
|
-
|
|
230
|
-
- Large libraries
|
|
231
|
-
- Admin-only features
|
|
232
|
-
- Below-the-fold content
|
|
233
|
-
- Micro-frontend architectures
|
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
# Edge Runtime Patterns
|
|
2
|
-
|
|
3
|
-
Patterns for Edge functions and middleware.
|
|
4
|
-
|
|
5
|
-
## Edge API Route
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
// app/api/geo/route.ts
|
|
9
|
-
export const runtime = 'edge'
|
|
10
|
-
|
|
11
|
-
export async function GET(request: Request) {
|
|
12
|
-
// Access geo data from edge
|
|
13
|
-
const country = request.headers.get('x-vercel-ip-country') ?? 'US'
|
|
14
|
-
const city = request.headers.get('x-vercel-ip-city') ?? 'Unknown'
|
|
15
|
-
const region = request.headers.get('x-vercel-ip-country-region')
|
|
16
|
-
|
|
17
|
-
return Response.json({
|
|
18
|
-
country,
|
|
19
|
-
city,
|
|
20
|
-
region,
|
|
21
|
-
timestamp: Date.now()
|
|
22
|
-
})
|
|
23
|
-
}
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Edge Middleware
|
|
27
|
-
|
|
28
|
-
```typescript
|
|
29
|
-
// middleware.ts
|
|
30
|
-
import { NextResponse } from 'next/server'
|
|
31
|
-
import type { NextRequest } from 'next/server'
|
|
32
|
-
|
|
33
|
-
export const config = {
|
|
34
|
-
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)']
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function middleware(request: NextRequest) {
|
|
38
|
-
const response = NextResponse.next()
|
|
39
|
-
|
|
40
|
-
// Geo-based routing
|
|
41
|
-
const country = request.geo?.country ?? 'US'
|
|
42
|
-
|
|
43
|
-
if (country === 'DE' && !request.nextUrl.pathname.startsWith('/de')) {
|
|
44
|
-
return NextResponse.redirect(new URL('/de' + request.nextUrl.pathname, request.url))
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// A/B testing
|
|
48
|
-
const bucket = request.cookies.get('ab-bucket')?.value ?? Math.random() > 0.5 ? 'a' : 'b'
|
|
49
|
-
|
|
50
|
-
if (!request.cookies.get('ab-bucket')) {
|
|
51
|
-
response.cookies.set('ab-bucket', bucket)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
response.headers.set('x-ab-bucket', bucket)
|
|
55
|
-
|
|
56
|
-
return response
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
## Edge Authentication
|
|
61
|
-
|
|
62
|
-
```typescript
|
|
63
|
-
// middleware.ts
|
|
64
|
-
import { jwtVerify } from 'jose'
|
|
65
|
-
import { NextResponse } from 'next/server'
|
|
66
|
-
import type { NextRequest } from 'next/server'
|
|
67
|
-
|
|
68
|
-
const JWT_SECRET = new TextEncoder().encode(process.env.JWT_SECRET)
|
|
69
|
-
|
|
70
|
-
export async function middleware(request: NextRequest) {
|
|
71
|
-
// Get token from cookie or header
|
|
72
|
-
const token = request.cookies.get('token')?.value ??
|
|
73
|
-
request.headers.get('authorization')?.replace('Bearer ', '')
|
|
74
|
-
|
|
75
|
-
if (!token) {
|
|
76
|
-
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
const { payload } = await jwtVerify(token, JWT_SECRET)
|
|
81
|
-
|
|
82
|
-
// Add user info to headers
|
|
83
|
-
const response = NextResponse.next()
|
|
84
|
-
response.headers.set('x-user-id', payload.sub as string)
|
|
85
|
-
return response
|
|
86
|
-
} catch {
|
|
87
|
-
return NextResponse.json({ error: 'Invalid token' }, { status: 401 })
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export const config = {
|
|
92
|
-
matcher: '/api/protected/:path*'
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## Edge Rate Limiting
|
|
97
|
-
|
|
98
|
-
```typescript
|
|
99
|
-
// middleware.ts
|
|
100
|
-
import { NextResponse } from 'next/server'
|
|
101
|
-
import type { NextRequest } from 'next/server'
|
|
102
|
-
|
|
103
|
-
// Using Vercel KV at edge
|
|
104
|
-
import { kv } from '@vercel/kv'
|
|
105
|
-
|
|
106
|
-
export async function middleware(request: NextRequest) {
|
|
107
|
-
const ip = request.ip ?? 'unknown'
|
|
108
|
-
const key = `rate-limit:${ip}`
|
|
109
|
-
|
|
110
|
-
const [count] = await kv
|
|
111
|
-
.multi()
|
|
112
|
-
.incr(key)
|
|
113
|
-
.expire(key, 60)
|
|
114
|
-
.exec()
|
|
115
|
-
|
|
116
|
-
if ((count as number) > 100) {
|
|
117
|
-
return NextResponse.json(
|
|
118
|
-
{ error: 'Too many requests' },
|
|
119
|
-
{
|
|
120
|
-
status: 429,
|
|
121
|
-
headers: { 'Retry-After': '60' }
|
|
122
|
-
}
|
|
123
|
-
)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return NextResponse.next()
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export const config = {
|
|
130
|
-
matcher: '/api/:path*'
|
|
131
|
-
}
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
## Edge Caching
|
|
135
|
-
|
|
136
|
-
```typescript
|
|
137
|
-
// app/api/cached/route.ts
|
|
138
|
-
export const runtime = 'edge'
|
|
139
|
-
|
|
140
|
-
export async function GET(request: Request) {
|
|
141
|
-
const { searchParams } = new URL(request.url)
|
|
142
|
-
const id = searchParams.get('id')
|
|
143
|
-
|
|
144
|
-
// Cache at edge
|
|
145
|
-
const data = await fetch(`https://api.example.com/data/${id}`, {
|
|
146
|
-
next: { revalidate: 60 }
|
|
147
|
-
}).then(r => r.json())
|
|
148
|
-
|
|
149
|
-
return Response.json(data, {
|
|
150
|
-
headers: {
|
|
151
|
-
'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=300'
|
|
152
|
-
}
|
|
153
|
-
})
|
|
154
|
-
}
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
## Edge Feature Flags
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
// middleware.ts
|
|
161
|
-
import { NextResponse } from 'next/server'
|
|
162
|
-
import type { NextRequest } from 'next/server'
|
|
163
|
-
|
|
164
|
-
const FEATURE_FLAGS = {
|
|
165
|
-
newCheckout: { enabled: true, percentage: 50 },
|
|
166
|
-
darkMode: { enabled: true, percentage: 100 },
|
|
167
|
-
betaFeatures: { enabled: false, percentage: 0 }
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
export function middleware(request: NextRequest) {
|
|
171
|
-
const response = NextResponse.next()
|
|
172
|
-
|
|
173
|
-
// Get or create user bucket
|
|
174
|
-
let userId = request.cookies.get('user-id')?.value
|
|
175
|
-
|
|
176
|
-
if (!userId) {
|
|
177
|
-
userId = crypto.randomUUID()
|
|
178
|
-
response.cookies.set('user-id', userId, {
|
|
179
|
-
httpOnly: true,
|
|
180
|
-
secure: true,
|
|
181
|
-
sameSite: 'lax'
|
|
182
|
-
})
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Determine feature flags for user
|
|
186
|
-
const flags: Record<string, boolean> = {}
|
|
187
|
-
|
|
188
|
-
for (const [flag, config] of Object.entries(FEATURE_FLAGS)) {
|
|
189
|
-
if (!config.enabled) {
|
|
190
|
-
flags[flag] = false
|
|
191
|
-
continue
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Consistent bucketing based on user ID + flag name
|
|
195
|
-
const hash = simpleHash(userId + flag)
|
|
196
|
-
flags[flag] = (hash % 100) < config.percentage
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Pass flags via headers
|
|
200
|
-
response.headers.set('x-feature-flags', JSON.stringify(flags))
|
|
201
|
-
|
|
202
|
-
return response
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
function simpleHash(str: string): number {
|
|
206
|
-
let hash = 0
|
|
207
|
-
for (let i = 0; i < str.length; i++) {
|
|
208
|
-
hash = ((hash << 5) - hash) + str.charCodeAt(i)
|
|
209
|
-
hash = hash & hash
|
|
210
|
-
}
|
|
211
|
-
return Math.abs(hash)
|
|
212
|
-
}
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
## Edge Redirects
|
|
216
|
-
|
|
217
|
-
```typescript
|
|
218
|
-
// middleware.ts
|
|
219
|
-
import { NextResponse } from 'next/server'
|
|
220
|
-
import type { NextRequest } from 'next/server'
|
|
221
|
-
|
|
222
|
-
const REDIRECTS: Record<string, string> = {
|
|
223
|
-
'/old-page': '/new-page',
|
|
224
|
-
'/blog/old-post': '/articles/new-post',
|
|
225
|
-
'/products/discontinued': '/'
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
export function middleware(request: NextRequest) {
|
|
229
|
-
const path = request.nextUrl.pathname
|
|
230
|
-
|
|
231
|
-
// Check redirects
|
|
232
|
-
const redirect = REDIRECTS[path]
|
|
233
|
-
if (redirect) {
|
|
234
|
-
return NextResponse.redirect(new URL(redirect, request.url), 301)
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Trailing slash handling
|
|
238
|
-
if (path !== '/' && path.endsWith('/')) {
|
|
239
|
-
return NextResponse.redirect(
|
|
240
|
-
new URL(path.slice(0, -1), request.url),
|
|
241
|
-
308
|
|
242
|
-
)
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return NextResponse.next()
|
|
246
|
-
}
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
## When to Use
|
|
250
|
-
|
|
251
|
-
- Geo-routing
|
|
252
|
-
- A/B testing
|
|
253
|
-
- Authentication checks
|
|
254
|
-
- Rate limiting
|