@intentsolutionsio/vercel-pack 1.0.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.
- package/.claude-plugin/plugin.json +17 -0
- package/000-docs/001-BL-LICN-license.txt +3 -0
- package/LICENSE +21 -0
- package/README.md +69 -0
- package/package.json +43 -0
- package/skills/vercel-advanced-troubleshooting/SKILL.md +261 -0
- package/skills/vercel-architecture-variants/SKILL.md +284 -0
- package/skills/vercel-ci-integration/SKILL.md +124 -0
- package/skills/vercel-common-errors/SKILL.md +109 -0
- package/skills/vercel-cost-tuning/SKILL.md +201 -0
- package/skills/vercel-data-handling/SKILL.md +220 -0
- package/skills/vercel-debug-bundle/SKILL.md +111 -0
- package/skills/vercel-deploy-integration/SKILL.md +209 -0
- package/skills/vercel-deploy-preview/SKILL.md +71 -0
- package/skills/vercel-edge-functions/SKILL.md +73 -0
- package/skills/vercel-enterprise-rbac/SKILL.md +222 -0
- package/skills/vercel-hello-world/SKILL.md +96 -0
- package/skills/vercel-incident-runbook/SKILL.md +203 -0
- package/skills/vercel-install-auth/SKILL.md +90 -0
- package/skills/vercel-known-pitfalls/SKILL.md +334 -0
- package/skills/vercel-load-scale/SKILL.md +274 -0
- package/skills/vercel-local-dev-loop/SKILL.md +117 -0
- package/skills/vercel-migration-deep-dive/SKILL.md +244 -0
- package/skills/vercel-multi-env-setup/SKILL.md +222 -0
- package/skills/vercel-observability/SKILL.md +250 -0
- package/skills/vercel-performance-tuning/SKILL.md +214 -0
- package/skills/vercel-policy-guardrails/SKILL.md +257 -0
- package/skills/vercel-prod-checklist/SKILL.md +119 -0
- package/skills/vercel-rate-limits/SKILL.md +149 -0
- package/skills/vercel-reference-architecture/SKILL.md +238 -0
- package/skills/vercel-reliability-patterns/SKILL.md +290 -0
- package/skills/vercel-sdk-patterns/SKILL.md +147 -0
- package/skills/vercel-security-basics/SKILL.md +140 -0
- package/skills/vercel-upgrade-migration/SKILL.md +112 -0
- package/skills/vercel-webhooks-events/SKILL.md +199 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vercel-security-basics
|
|
3
|
+
description: |
|
|
4
|
+
Apply Vercel security best practices for secrets and access control.
|
|
5
|
+
Use when securing API keys, implementing least privilege access,
|
|
6
|
+
or auditing Vercel security configuration.
|
|
7
|
+
Trigger with phrases like "vercel security", "vercel secrets",
|
|
8
|
+
"secure vercel", "vercel API key security".
|
|
9
|
+
allowed-tools: Read, Write, Grep
|
|
10
|
+
version: 1.0.0
|
|
11
|
+
license: MIT
|
|
12
|
+
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Vercel Security Basics
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Security best practices for Vercel API keys, tokens, and access control.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- Vercel SDK installed
|
|
22
|
+
- Understanding of environment variables
|
|
23
|
+
- Access to Vercel dashboard
|
|
24
|
+
|
|
25
|
+
## Instructions
|
|
26
|
+
|
|
27
|
+
### Step 1: Configure Environment Variables
|
|
28
|
+
```bash
|
|
29
|
+
# .env (NEVER commit to git)
|
|
30
|
+
VERCEL_API_KEY=sk_live_***
|
|
31
|
+
VERCEL_SECRET=***
|
|
32
|
+
|
|
33
|
+
# .gitignore
|
|
34
|
+
.env
|
|
35
|
+
.env.local
|
|
36
|
+
.env.*.local
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Step 2: Implement Secret Rotation
|
|
40
|
+
```bash
|
|
41
|
+
# 1. Generate new key in Vercel dashboard
|
|
42
|
+
# 2. Update environment variable
|
|
43
|
+
export VERCEL_API_KEY="new_key_here"
|
|
44
|
+
|
|
45
|
+
# 3. Verify new key works
|
|
46
|
+
curl -H "Authorization: Bearer ${VERCEL_API_KEY}" \
|
|
47
|
+
https://api.vercel.com/health
|
|
48
|
+
|
|
49
|
+
# 4. Revoke old key in dashboard
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Step 3: Apply Least Privilege
|
|
53
|
+
| Environment | Recommended Scopes |
|
|
54
|
+
|-------------|-------------------|
|
|
55
|
+
| Development | `read, deploy` |
|
|
56
|
+
| Staging | `read, write, deploy` |
|
|
57
|
+
| Production | `read, write, deploy, domains` |
|
|
58
|
+
|
|
59
|
+
## Output
|
|
60
|
+
- Secure API key storage
|
|
61
|
+
- Environment-specific access controls
|
|
62
|
+
- Audit logging enabled
|
|
63
|
+
|
|
64
|
+
## Error Handling
|
|
65
|
+
| Security Issue | Detection | Mitigation |
|
|
66
|
+
|----------------|-----------|------------|
|
|
67
|
+
| Exposed API key | Git scanning | Rotate immediately |
|
|
68
|
+
| Excessive scopes | Audit logs | Reduce permissions |
|
|
69
|
+
| Missing rotation | Key age check | Schedule rotation |
|
|
70
|
+
|
|
71
|
+
## Examples
|
|
72
|
+
|
|
73
|
+
### Service Account Pattern
|
|
74
|
+
```typescript
|
|
75
|
+
const clients = {
|
|
76
|
+
reader: new VercelClient({
|
|
77
|
+
apiKey: process.env.VERCEL_READ_KEY,
|
|
78
|
+
}),
|
|
79
|
+
writer: new VercelClient({
|
|
80
|
+
apiKey: process.env.VERCEL_WRITE_KEY,
|
|
81
|
+
}),
|
|
82
|
+
};
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Webhook Signature Verification
|
|
86
|
+
```typescript
|
|
87
|
+
import crypto from 'crypto';
|
|
88
|
+
|
|
89
|
+
function verifyWebhookSignature(
|
|
90
|
+
payload: string, signature: string, secret: string
|
|
91
|
+
): boolean {
|
|
92
|
+
const expected = crypto.createHmac('sha256', secret).update(payload).digest('hex');
|
|
93
|
+
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Security Checklist
|
|
98
|
+
- [ ] API keys in environment variables
|
|
99
|
+
- [ ] `.env` files in `.gitignore`
|
|
100
|
+
- [ ] Different keys for dev/staging/prod
|
|
101
|
+
- [ ] Minimal scopes per environment
|
|
102
|
+
- [ ] Webhook signatures validated
|
|
103
|
+
- [ ] Audit logging enabled
|
|
104
|
+
|
|
105
|
+
### Audit Logging
|
|
106
|
+
```typescript
|
|
107
|
+
interface AuditEntry {
|
|
108
|
+
timestamp: Date;
|
|
109
|
+
action: string;
|
|
110
|
+
userId: string;
|
|
111
|
+
resource: string;
|
|
112
|
+
result: 'success' | 'failure';
|
|
113
|
+
metadata?: Record<string, any>;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function auditLog(entry: Omit<AuditEntry, 'timestamp'>): Promise<void> {
|
|
117
|
+
const log: AuditEntry = { ...entry, timestamp: new Date() };
|
|
118
|
+
|
|
119
|
+
// Log to Vercel analytics
|
|
120
|
+
await vercelClient.track('audit', log);
|
|
121
|
+
|
|
122
|
+
// Also log locally for compliance
|
|
123
|
+
console.log('[AUDIT]', JSON.stringify(log));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Usage
|
|
127
|
+
await auditLog({
|
|
128
|
+
action: 'vercel.api.call',
|
|
129
|
+
userId: currentUser.id,
|
|
130
|
+
resource: '/v1/resource',
|
|
131
|
+
result: 'success',
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Resources
|
|
136
|
+
- [Vercel Security Guide](https://vercel.com/docs/security)
|
|
137
|
+
- [Vercel API Scopes](https://vercel.com/docs/scopes)
|
|
138
|
+
|
|
139
|
+
## Next Steps
|
|
140
|
+
For production deployment, see `vercel-prod-checklist`.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vercel-upgrade-migration
|
|
3
|
+
description: |
|
|
4
|
+
Analyze, plan, and execute Vercel SDK upgrades with breaking change detection.
|
|
5
|
+
Use when upgrading Vercel SDK versions, detecting deprecations,
|
|
6
|
+
or migrating to new API versions.
|
|
7
|
+
Trigger with phrases like "upgrade vercel", "vercel migration",
|
|
8
|
+
"vercel breaking changes", "update vercel SDK", "analyze vercel version".
|
|
9
|
+
allowed-tools: Read, Write, Edit, Bash(npm:*), Bash(git:*)
|
|
10
|
+
version: 1.0.0
|
|
11
|
+
license: MIT
|
|
12
|
+
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Vercel Upgrade & Migration
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Guide for upgrading Vercel SDK versions and handling breaking changes.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- Current Vercel SDK installed
|
|
22
|
+
- Git for version control
|
|
23
|
+
- Test suite available
|
|
24
|
+
- Staging environment
|
|
25
|
+
|
|
26
|
+
## Instructions
|
|
27
|
+
|
|
28
|
+
### Step 1: Check Current Version
|
|
29
|
+
```bash
|
|
30
|
+
npm list vercel
|
|
31
|
+
npm view vercel version
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Step 2: Review Changelog
|
|
35
|
+
```bash
|
|
36
|
+
open https://github.com/vercel/vercel/releases
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Step 3: Create Upgrade Branch
|
|
40
|
+
```bash
|
|
41
|
+
git checkout -b upgrade/vercel-sdk-vX.Y.Z
|
|
42
|
+
npm install vercel@latest
|
|
43
|
+
npm test
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Step 4: Handle Breaking Changes
|
|
47
|
+
Update import statements, configuration, and method signatures as needed.
|
|
48
|
+
|
|
49
|
+
## Output
|
|
50
|
+
- Updated SDK version
|
|
51
|
+
- Fixed breaking changes
|
|
52
|
+
- Passing test suite
|
|
53
|
+
- Documented rollback procedure
|
|
54
|
+
|
|
55
|
+
## Error Handling
|
|
56
|
+
| SDK Version | API Version | Node.js | Breaking Changes |
|
|
57
|
+
|-------------|-------------|---------|------------------|
|
|
58
|
+
| 3.x | 2024-01 | 18+ | Major refactor |
|
|
59
|
+
| 2.x | 2023-06 | 16+ | Auth changes |
|
|
60
|
+
| 1.x | 2022-01 | 14+ | Initial release |
|
|
61
|
+
|
|
62
|
+
## Examples
|
|
63
|
+
|
|
64
|
+
### Import Changes
|
|
65
|
+
```typescript
|
|
66
|
+
// Before (v1.x)
|
|
67
|
+
import { Client } from 'vercel';
|
|
68
|
+
|
|
69
|
+
// After (v2.x)
|
|
70
|
+
import { VercelClient } from 'vercel';
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Configuration Changes
|
|
74
|
+
```typescript
|
|
75
|
+
// Before (v1.x)
|
|
76
|
+
const client = new Client({ key: 'xxx' });
|
|
77
|
+
|
|
78
|
+
// After (v2.x)
|
|
79
|
+
const client = new VercelClient({
|
|
80
|
+
apiKey: 'xxx',
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Rollback Procedure
|
|
85
|
+
```bash
|
|
86
|
+
npm install vercel@1.x.x --save-exact
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Deprecation Handling
|
|
90
|
+
```typescript
|
|
91
|
+
// Monitor for deprecation warnings in development
|
|
92
|
+
if (process.env.NODE_ENV === 'development') {
|
|
93
|
+
process.on('warning', (warning) => {
|
|
94
|
+
if (warning.name === 'DeprecationWarning') {
|
|
95
|
+
console.warn('[Vercel]', warning.message);
|
|
96
|
+
// Log to tracking system for proactive updates
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Common deprecation patterns to watch for:
|
|
102
|
+
// - Renamed methods: client.oldMethod() -> client.newMethod()
|
|
103
|
+
// - Changed parameters: { key: 'x' } -> { apiKey: 'x' }
|
|
104
|
+
// - Removed features: Check release notes before upgrading
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Resources
|
|
108
|
+
- [Vercel Changelog](https://github.com/vercel/vercel/releases)
|
|
109
|
+
- [Vercel Migration Guide](https://vercel.com/docs/migration)
|
|
110
|
+
|
|
111
|
+
## Next Steps
|
|
112
|
+
For CI integration during upgrades, see `vercel-ci-integration`.
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vercel-webhooks-events
|
|
3
|
+
description: |
|
|
4
|
+
Implement Vercel webhook signature validation and event handling.
|
|
5
|
+
Use when setting up webhook endpoints, implementing signature verification,
|
|
6
|
+
or handling Vercel event notifications securely.
|
|
7
|
+
Trigger with phrases like "vercel webhook", "vercel events",
|
|
8
|
+
"vercel webhook signature", "handle vercel events", "vercel notifications".
|
|
9
|
+
allowed-tools: Read, Write, Edit, Bash(curl:*)
|
|
10
|
+
version: 1.0.0
|
|
11
|
+
license: MIT
|
|
12
|
+
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Vercel Webhooks & Events
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Securely handle Vercel webhooks with signature validation and replay protection.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- Vercel webhook secret configured
|
|
22
|
+
- HTTPS endpoint accessible from internet
|
|
23
|
+
- Understanding of cryptographic signatures
|
|
24
|
+
- Redis or database for idempotency (optional)
|
|
25
|
+
|
|
26
|
+
## Webhook Endpoint Setup
|
|
27
|
+
|
|
28
|
+
### Express.js
|
|
29
|
+
```typescript
|
|
30
|
+
import express from 'express';
|
|
31
|
+
import crypto from 'crypto';
|
|
32
|
+
|
|
33
|
+
const app = express();
|
|
34
|
+
|
|
35
|
+
// IMPORTANT: Raw body needed for signature verification
|
|
36
|
+
app.post('/webhooks/vercel',
|
|
37
|
+
express.raw({ type: 'application/json' }),
|
|
38
|
+
async (req, res) => {
|
|
39
|
+
const signature = req.headers['x-vercel-signature'] as string;
|
|
40
|
+
const timestamp = req.headers['x-vercel-timestamp'] as string;
|
|
41
|
+
|
|
42
|
+
if (!verifyVercelSignature(req.body, signature, timestamp)) {
|
|
43
|
+
return res.status(401).json({ error: 'Invalid signature' });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const event = JSON.parse(req.body.toString());
|
|
47
|
+
await handleVercelEvent(event);
|
|
48
|
+
|
|
49
|
+
res.status(200).json({ received: true });
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Signature Verification
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
function verifyVercelSignature(
|
|
58
|
+
payload: Buffer,
|
|
59
|
+
signature: string,
|
|
60
|
+
timestamp: string
|
|
61
|
+
): boolean {
|
|
62
|
+
const secret = process.env.VERCEL_WEBHOOK_SECRET!;
|
|
63
|
+
|
|
64
|
+
// Reject old timestamps (replay attack protection)
|
|
65
|
+
const timestampAge = Date.now() - parseInt(timestamp) * 1000;
|
|
66
|
+
if (timestampAge > 300000) { // 5 minutes
|
|
67
|
+
console.error('Webhook timestamp too old');
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Compute expected signature
|
|
72
|
+
const signedPayload = `${timestamp}.${payload.toString()}`;
|
|
73
|
+
const expectedSignature = crypto
|
|
74
|
+
.createHmac('sha256', secret)
|
|
75
|
+
.update(signedPayload)
|
|
76
|
+
.digest('hex');
|
|
77
|
+
|
|
78
|
+
// Timing-safe comparison
|
|
79
|
+
return crypto.timingSafeEqual(
|
|
80
|
+
Buffer.from(signature),
|
|
81
|
+
Buffer.from(expectedSignature)
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Event Handler Pattern
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
type VercelEventType = 'resource.created' | 'resource.updated' | 'resource.deleted';
|
|
90
|
+
|
|
91
|
+
interface VercelEvent {
|
|
92
|
+
id: string;
|
|
93
|
+
type: VercelEventType;
|
|
94
|
+
data: Record<string, any>;
|
|
95
|
+
created: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const eventHandlers: Record<VercelEventType, (data: any) => Promise<void>> = {
|
|
99
|
+
'resource.created': async (data) => { /* handle */ },
|
|
100
|
+
'resource.updated': async (data) => { /* handle */ },
|
|
101
|
+
'resource.deleted': async (data) => { /* handle */ }
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
async function handleVercelEvent(event: VercelEvent): Promise<void> {
|
|
105
|
+
const handler = eventHandlers[event.type];
|
|
106
|
+
|
|
107
|
+
if (!handler) {
|
|
108
|
+
console.log(`Unhandled event type: ${event.type}`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
await handler(event.data);
|
|
114
|
+
console.log(`Processed ${event.type}: ${event.id}`);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error(`Failed to process ${event.type}: ${event.id}`, error);
|
|
117
|
+
throw error; // Rethrow to trigger retry
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Idempotency Handling
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import { Redis } from 'ioredis';
|
|
126
|
+
|
|
127
|
+
const redis = new Redis(process.env.REDIS_URL);
|
|
128
|
+
|
|
129
|
+
async function isEventProcessed(eventId: string): Promise<boolean> {
|
|
130
|
+
const key = `vercel:event:${eventId}`;
|
|
131
|
+
const exists = await redis.exists(key);
|
|
132
|
+
return exists === 1;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function markEventProcessed(eventId: string): Promise<void> {
|
|
136
|
+
const key = `vercel:event:${eventId}`;
|
|
137
|
+
await redis.set(key, '1', 'EX', 86400 * 7); // 7 days TTL
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Webhook Testing
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# Use Vercel CLI to send test events
|
|
145
|
+
vercel dev
|
|
146
|
+
|
|
147
|
+
# Or use webhook.site for debugging
|
|
148
|
+
curl -X POST https://webhook.site/your-uuid \
|
|
149
|
+
-H "Content-Type: application/json" \
|
|
150
|
+
-d '{"type": "resource.created", "data": {}}'
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Instructions
|
|
154
|
+
|
|
155
|
+
### Step 1: Register Webhook Endpoint
|
|
156
|
+
Configure your webhook URL in the Vercel dashboard.
|
|
157
|
+
|
|
158
|
+
### Step 2: Implement Signature Verification
|
|
159
|
+
Use the signature verification code to validate incoming webhooks.
|
|
160
|
+
|
|
161
|
+
### Step 3: Handle Events
|
|
162
|
+
Implement handlers for each event type your application needs.
|
|
163
|
+
|
|
164
|
+
### Step 4: Add Idempotency
|
|
165
|
+
Prevent duplicate processing with event ID tracking.
|
|
166
|
+
|
|
167
|
+
## Output
|
|
168
|
+
- Secure webhook endpoint
|
|
169
|
+
- Signature validation enabled
|
|
170
|
+
- Event handlers implemented
|
|
171
|
+
- Replay attack protection active
|
|
172
|
+
|
|
173
|
+
## Error Handling
|
|
174
|
+
| Issue | Cause | Solution |
|
|
175
|
+
|-------|-------|----------|
|
|
176
|
+
| Invalid signature | Wrong secret | Verify webhook secret |
|
|
177
|
+
| Timestamp rejected | Clock drift | Check server time sync |
|
|
178
|
+
| Duplicate events | Missing idempotency | Implement event ID tracking |
|
|
179
|
+
| Handler timeout | Slow processing | Use async queue |
|
|
180
|
+
|
|
181
|
+
## Examples
|
|
182
|
+
|
|
183
|
+
### Testing Webhooks Locally
|
|
184
|
+
```bash
|
|
185
|
+
# Use ngrok to expose local server
|
|
186
|
+
ngrok http 3000
|
|
187
|
+
|
|
188
|
+
# Send test webhook
|
|
189
|
+
curl -X POST https://your-ngrok-url/webhooks/vercel \
|
|
190
|
+
-H "Content-Type: application/json" \
|
|
191
|
+
-d '{"type": "test", "data": {}}'
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Resources
|
|
195
|
+
- [Vercel Webhooks Guide](https://vercel.com/docs/webhooks)
|
|
196
|
+
- [Webhook Security Best Practices](https://vercel.com/docs/webhooks/security)
|
|
197
|
+
|
|
198
|
+
## Next Steps
|
|
199
|
+
For performance optimization, see `vercel-performance-tuning`.
|