@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,257 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vercel-policy-guardrails
|
|
3
|
+
description: |
|
|
4
|
+
Implement Vercel lint rules, policy enforcement, and automated guardrails.
|
|
5
|
+
Use when setting up code quality rules for Vercel integrations, implementing
|
|
6
|
+
pre-commit hooks, or configuring CI policy checks for Vercel best practices.
|
|
7
|
+
Trigger with phrases like "vercel policy", "vercel lint",
|
|
8
|
+
"vercel guardrails", "vercel best practices check", "vercel eslint".
|
|
9
|
+
allowed-tools: Read, Write, Edit, Bash(npx:*)
|
|
10
|
+
version: 1.0.0
|
|
11
|
+
license: MIT
|
|
12
|
+
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Vercel Policy & Guardrails
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Automated policy enforcement and guardrails for Vercel integrations.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- ESLint configured in project
|
|
22
|
+
- Pre-commit hooks infrastructure
|
|
23
|
+
- CI/CD pipeline with policy checks
|
|
24
|
+
- TypeScript for type enforcement
|
|
25
|
+
|
|
26
|
+
## ESLint Rules
|
|
27
|
+
|
|
28
|
+
### Custom Vercel Plugin
|
|
29
|
+
```javascript
|
|
30
|
+
// eslint-plugin-vercel/rules/no-hardcoded-keys.js
|
|
31
|
+
module.exports = {
|
|
32
|
+
meta: {
|
|
33
|
+
type: 'problem',
|
|
34
|
+
docs: {
|
|
35
|
+
description: 'Disallow hardcoded Vercel API keys',
|
|
36
|
+
},
|
|
37
|
+
fixable: 'code',
|
|
38
|
+
},
|
|
39
|
+
create(context) {
|
|
40
|
+
return {
|
|
41
|
+
Literal(node) {
|
|
42
|
+
if (typeof node.value === 'string') {
|
|
43
|
+
if (node.value.match(/^sk_(live|test)_[a-zA-Z0-9]{24,}/)) {
|
|
44
|
+
context.report({
|
|
45
|
+
node,
|
|
46
|
+
message: 'Hardcoded Vercel API key detected',
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### ESLint Configuration
|
|
57
|
+
```javascript
|
|
58
|
+
// .eslintrc.js
|
|
59
|
+
module.exports = {
|
|
60
|
+
plugins: ['vercel'],
|
|
61
|
+
rules: {
|
|
62
|
+
'vercel/no-hardcoded-keys': 'error',
|
|
63
|
+
'vercel/require-error-handling': 'warn',
|
|
64
|
+
'vercel/use-typed-client': 'warn',
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Pre-Commit Hooks
|
|
70
|
+
|
|
71
|
+
```yaml
|
|
72
|
+
# .pre-commit-config.yaml
|
|
73
|
+
repos:
|
|
74
|
+
- repo: local
|
|
75
|
+
hooks:
|
|
76
|
+
- id: vercel-secrets-check
|
|
77
|
+
name: Check for Vercel secrets
|
|
78
|
+
entry: bash -c 'git diff --cached --name-only | xargs grep -l "sk_live_" && exit 1 || exit 0'
|
|
79
|
+
language: system
|
|
80
|
+
pass_filenames: false
|
|
81
|
+
|
|
82
|
+
- id: vercel-config-validate
|
|
83
|
+
name: Validate Vercel configuration
|
|
84
|
+
entry: node scripts/validate-vercel-config.js
|
|
85
|
+
language: node
|
|
86
|
+
files: '\.vercel\.json$'
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## TypeScript Strict Patterns
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// Enforce typed configuration
|
|
93
|
+
interface VercelStrictConfig {
|
|
94
|
+
apiKey: string; // Required
|
|
95
|
+
environment: 'development' | 'staging' | 'production'; // Enum
|
|
96
|
+
timeout: number; // Required number, not optional
|
|
97
|
+
retries: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Disallow any in Vercel code
|
|
101
|
+
// @ts-expect-error - Using any is forbidden
|
|
102
|
+
const client = new Client({ apiKey: any });
|
|
103
|
+
|
|
104
|
+
// Prefer this
|
|
105
|
+
const client = new VercelClient(config satisfies VercelStrictConfig);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Architecture Decision Records
|
|
109
|
+
|
|
110
|
+
### ADR Template
|
|
111
|
+
```markdown
|
|
112
|
+
# ADR-001: Vercel Client Initialization
|
|
113
|
+
|
|
114
|
+
## Status
|
|
115
|
+
Accepted
|
|
116
|
+
|
|
117
|
+
## Context
|
|
118
|
+
We need to decide how to initialize the Vercel client across our application.
|
|
119
|
+
|
|
120
|
+
## Decision
|
|
121
|
+
We will use the singleton pattern with lazy initialization.
|
|
122
|
+
|
|
123
|
+
## Consequences
|
|
124
|
+
- Pro: Single client instance, connection reuse
|
|
125
|
+
- Pro: Easy to mock in tests
|
|
126
|
+
- Con: Global state requires careful lifecycle management
|
|
127
|
+
|
|
128
|
+
## Enforcement
|
|
129
|
+
- ESLint rule: vercel/use-singleton-client
|
|
130
|
+
- CI check: grep for "new VercelClient(" outside allowed files
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Policy-as-Code (OPA)
|
|
134
|
+
|
|
135
|
+
```rego
|
|
136
|
+
# vercel-policy.rego
|
|
137
|
+
package vercel
|
|
138
|
+
|
|
139
|
+
# Deny production API keys in non-production environments
|
|
140
|
+
deny[msg] {
|
|
141
|
+
input.environment != "production"
|
|
142
|
+
startswith(input.apiKey, "sk_live_")
|
|
143
|
+
msg := "Production API keys not allowed in non-production environment"
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
# Require minimum timeout
|
|
147
|
+
deny[msg] {
|
|
148
|
+
input.timeout < 10000
|
|
149
|
+
msg := sprintf("Timeout too low: %d < 10000ms minimum", [input.timeout])
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# Require retry configuration
|
|
153
|
+
deny[msg] {
|
|
154
|
+
not input.retries
|
|
155
|
+
msg := "Retry configuration is required"
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## CI Policy Checks
|
|
160
|
+
|
|
161
|
+
```yaml
|
|
162
|
+
# .github/workflows/vercel-policy.yml
|
|
163
|
+
name: Vercel Policy Check
|
|
164
|
+
|
|
165
|
+
on: [push, pull_request]
|
|
166
|
+
|
|
167
|
+
jobs:
|
|
168
|
+
policy:
|
|
169
|
+
runs-on: ubuntu-latest
|
|
170
|
+
steps:
|
|
171
|
+
- uses: actions/checkout@v4
|
|
172
|
+
|
|
173
|
+
- name: Check for hardcoded secrets
|
|
174
|
+
run: |
|
|
175
|
+
if grep -rE "sk_(live|test)_[a-zA-Z0-9]{24,}" --include="*.ts" --include="*.js" .; then
|
|
176
|
+
echo "ERROR: Hardcoded Vercel keys found"
|
|
177
|
+
exit 1
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
- name: Validate configuration schema
|
|
181
|
+
run: |
|
|
182
|
+
npx ajv validate -s vercel-config.schema.json -d config/vercel/*.json
|
|
183
|
+
|
|
184
|
+
- name: Run ESLint Vercel rules
|
|
185
|
+
run: npx eslint --plugin vercel --rule 'vercel/no-hardcoded-keys: error' src/
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Runtime Guardrails
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// Prevent dangerous operations in production
|
|
192
|
+
const BLOCKED_IN_PROD = ['deleteAll', 'resetData', 'migrateDown'];
|
|
193
|
+
|
|
194
|
+
function guardVercelOperation(operation: string): void {
|
|
195
|
+
const isProd = process.env.NODE_ENV === 'production';
|
|
196
|
+
|
|
197
|
+
if (isProd && BLOCKED_IN_PROD.includes(operation)) {
|
|
198
|
+
throw new Error(`Operation '${operation}' blocked in production`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Rate limit protection
|
|
203
|
+
function guardRateLimits(requestsInWindow: number): void {
|
|
204
|
+
const limit = parseInt(process.env.VERCEL_RATE_LIMIT || '100');
|
|
205
|
+
|
|
206
|
+
if (requestsInWindow > limit * 0.9) {
|
|
207
|
+
console.warn('Approaching Vercel rate limit');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (requestsInWindow >= limit) {
|
|
211
|
+
throw new Error('Vercel rate limit exceeded - request blocked');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Instructions
|
|
217
|
+
|
|
218
|
+
### Step 1: Create ESLint Rules
|
|
219
|
+
Implement custom lint rules for Vercel patterns.
|
|
220
|
+
|
|
221
|
+
### Step 2: Configure Pre-Commit Hooks
|
|
222
|
+
Set up hooks to catch issues before commit.
|
|
223
|
+
|
|
224
|
+
### Step 3: Add CI Policy Checks
|
|
225
|
+
Implement policy-as-code in CI pipeline.
|
|
226
|
+
|
|
227
|
+
### Step 4: Enable Runtime Guardrails
|
|
228
|
+
Add production safeguards for dangerous operations.
|
|
229
|
+
|
|
230
|
+
## Output
|
|
231
|
+
- ESLint plugin with Vercel rules
|
|
232
|
+
- Pre-commit hooks blocking secrets
|
|
233
|
+
- CI policy checks passing
|
|
234
|
+
- Runtime guardrails active
|
|
235
|
+
|
|
236
|
+
## Error Handling
|
|
237
|
+
| Issue | Cause | Solution |
|
|
238
|
+
|-------|-------|----------|
|
|
239
|
+
| ESLint rule not firing | Wrong config | Check plugin registration |
|
|
240
|
+
| Pre-commit skipped | --no-verify | Enforce in CI |
|
|
241
|
+
| Policy false positive | Regex too broad | Narrow pattern match |
|
|
242
|
+
| Guardrail triggered | Actual issue | Fix or whitelist |
|
|
243
|
+
|
|
244
|
+
## Examples
|
|
245
|
+
|
|
246
|
+
### Quick ESLint Check
|
|
247
|
+
```bash
|
|
248
|
+
npx eslint --plugin vercel --rule 'vercel/no-hardcoded-keys: error' src/
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Resources
|
|
252
|
+
- [ESLint Plugin Development](https://eslint.org/docs/latest/extend/plugins)
|
|
253
|
+
- [Pre-commit Framework](https://pre-commit.com/)
|
|
254
|
+
- [Open Policy Agent](https://www.openpolicyagent.org/)
|
|
255
|
+
|
|
256
|
+
## Next Steps
|
|
257
|
+
For architecture blueprints, see `vercel-architecture-variants`.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vercel-prod-checklist
|
|
3
|
+
description: |
|
|
4
|
+
Execute Vercel production deployment checklist and rollback procedures.
|
|
5
|
+
Use when deploying Vercel integrations to production, preparing for launch,
|
|
6
|
+
or implementing go-live procedures.
|
|
7
|
+
Trigger with phrases like "vercel production", "deploy vercel",
|
|
8
|
+
"vercel go-live", "vercel launch checklist".
|
|
9
|
+
allowed-tools: Read, Bash(kubectl:*), Bash(curl:*), Grep
|
|
10
|
+
version: 1.0.0
|
|
11
|
+
license: MIT
|
|
12
|
+
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Vercel Production Checklist
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Complete checklist for deploying Vercel integrations to production.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- Staging environment tested and verified
|
|
22
|
+
- Production API keys available
|
|
23
|
+
- Deployment pipeline configured
|
|
24
|
+
- Monitoring and alerting ready
|
|
25
|
+
|
|
26
|
+
## Instructions
|
|
27
|
+
|
|
28
|
+
### Step 1: Pre-Deployment Configuration
|
|
29
|
+
- [ ] Production API keys in secure vault
|
|
30
|
+
- [ ] Environment variables set in deployment platform
|
|
31
|
+
- [ ] API key scopes are minimal (least privilege)
|
|
32
|
+
- [ ] Webhook endpoints configured with HTTPS
|
|
33
|
+
- [ ] Webhook secrets stored securely
|
|
34
|
+
|
|
35
|
+
### Step 2: Code Quality Verification
|
|
36
|
+
- [ ] All tests passing (`npm test`)
|
|
37
|
+
- [ ] No hardcoded credentials
|
|
38
|
+
- [ ] Error handling covers all Vercel error types
|
|
39
|
+
- [ ] Rate limiting/backoff implemented
|
|
40
|
+
- [ ] Logging is production-appropriate
|
|
41
|
+
|
|
42
|
+
### Step 3: Infrastructure Setup
|
|
43
|
+
- [ ] Health check endpoint includes Vercel connectivity
|
|
44
|
+
- [ ] Monitoring/alerting configured
|
|
45
|
+
- [ ] Circuit breaker pattern implemented
|
|
46
|
+
- [ ] Graceful degradation configured
|
|
47
|
+
|
|
48
|
+
### Step 4: Documentation Requirements
|
|
49
|
+
- [ ] Incident runbook created
|
|
50
|
+
- [ ] Key rotation procedure documented
|
|
51
|
+
- [ ] Rollback procedure documented
|
|
52
|
+
- [ ] On-call escalation path defined
|
|
53
|
+
|
|
54
|
+
### Step 5: Deploy with Gradual Rollout
|
|
55
|
+
```bash
|
|
56
|
+
# Pre-flight checks
|
|
57
|
+
curl -f https://staging.example.com/health
|
|
58
|
+
curl -s https://www.vercel-status.com
|
|
59
|
+
|
|
60
|
+
# Gradual rollout - start with canary (10%)
|
|
61
|
+
kubectl apply -f k8s/production.yaml
|
|
62
|
+
kubectl set image deployment/vercel-integration app=image:new --record
|
|
63
|
+
kubectl rollout pause deployment/vercel-integration
|
|
64
|
+
|
|
65
|
+
# Monitor canary traffic for 10 minutes
|
|
66
|
+
sleep 600
|
|
67
|
+
# Check error rates and latency before continuing
|
|
68
|
+
|
|
69
|
+
# If healthy, continue rollout to 50%
|
|
70
|
+
kubectl rollout resume deployment/vercel-integration
|
|
71
|
+
kubectl rollout pause deployment/vercel-integration
|
|
72
|
+
sleep 300
|
|
73
|
+
|
|
74
|
+
# Complete rollout to 100%
|
|
75
|
+
kubectl rollout resume deployment/vercel-integration
|
|
76
|
+
kubectl rollout status deployment/vercel-integration
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Output
|
|
80
|
+
- Deployed Vercel integration
|
|
81
|
+
- Health checks passing
|
|
82
|
+
- Monitoring active
|
|
83
|
+
- Rollback procedure documented
|
|
84
|
+
|
|
85
|
+
## Error Handling
|
|
86
|
+
| Alert | Condition | Severity |
|
|
87
|
+
|-------|-----------|----------|
|
|
88
|
+
| API Down | 5xx errors > 10/min | P1 |
|
|
89
|
+
| High Latency | p99 > 5000ms | P2 |
|
|
90
|
+
| Rate Limited | 429 errors > 5/min | P2 |
|
|
91
|
+
| Auth Failures | 401/403 errors > 0 | P1 |
|
|
92
|
+
|
|
93
|
+
## Examples
|
|
94
|
+
|
|
95
|
+
### Health Check Implementation
|
|
96
|
+
```typescript
|
|
97
|
+
async function healthCheck(): Promise<{ status: string; vercel: any }> {
|
|
98
|
+
const start = Date.now();
|
|
99
|
+
try {
|
|
100
|
+
await vercelClient.ping();
|
|
101
|
+
return { status: 'healthy', vercel: { connected: true, latencyMs: Date.now() - start } };
|
|
102
|
+
} catch (error) {
|
|
103
|
+
return { status: 'degraded', vercel: { connected: false, latencyMs: Date.now() - start } };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Immediate Rollback
|
|
109
|
+
```bash
|
|
110
|
+
kubectl rollout undo deployment/vercel-integration
|
|
111
|
+
kubectl rollout status deployment/vercel-integration
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Resources
|
|
115
|
+
- [Vercel Status](https://www.vercel-status.com)
|
|
116
|
+
- [Vercel Support](https://vercel.com/docs/support)
|
|
117
|
+
|
|
118
|
+
## Next Steps
|
|
119
|
+
For version upgrades, see `vercel-upgrade-migration`.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vercel-rate-limits
|
|
3
|
+
description: |
|
|
4
|
+
Implement Vercel rate limiting, backoff, and idempotency patterns.
|
|
5
|
+
Use when handling rate limit errors, implementing retry logic,
|
|
6
|
+
or optimizing API request throughput for Vercel.
|
|
7
|
+
Trigger with phrases like "vercel rate limit", "vercel throttling",
|
|
8
|
+
"vercel 429", "vercel retry", "vercel backoff".
|
|
9
|
+
allowed-tools: Read, Write, Edit
|
|
10
|
+
version: 1.0.0
|
|
11
|
+
license: MIT
|
|
12
|
+
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Vercel Rate Limits
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Handle Vercel rate limits gracefully with exponential backoff and idempotency.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
- Vercel SDK installed
|
|
22
|
+
- Understanding of async/await patterns
|
|
23
|
+
- Access to rate limit headers
|
|
24
|
+
|
|
25
|
+
## Instructions
|
|
26
|
+
|
|
27
|
+
### Step 1: Understand Rate Limit Tiers
|
|
28
|
+
|
|
29
|
+
| Tier | Requests/min | Requests/day | Burst |
|
|
30
|
+
|------|-------------|--------------|-------|
|
|
31
|
+
| Hobby | 100 | 100,000 | 10 |
|
|
32
|
+
| Pro | 1,000 | 1,000,000 | 50 |
|
|
33
|
+
| Enterprise | 10,000 | Unlimited | 200 |
|
|
34
|
+
|
|
35
|
+
### Step 2: Implement Exponential Backoff with Jitter
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
async function withExponentialBackoff<T>(
|
|
39
|
+
operation: () => Promise<T>,
|
|
40
|
+
config = { maxRetries: 5, baseDelayMs: 1000, maxDelayMs: 32000, jitterMs: 500 }
|
|
41
|
+
): Promise<T> {
|
|
42
|
+
for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
|
|
43
|
+
try {
|
|
44
|
+
return await operation();
|
|
45
|
+
} catch (error: any) {
|
|
46
|
+
if (attempt === config.maxRetries) throw error;
|
|
47
|
+
const status = error.status || error.response?.status;
|
|
48
|
+
if (status !== 429 && (status < 500 || status >= 600)) throw error;
|
|
49
|
+
|
|
50
|
+
// Exponential delay with jitter to prevent thundering herd
|
|
51
|
+
const exponentialDelay = config.baseDelayMs * Math.pow(2, attempt);
|
|
52
|
+
const jitter = Math.random() * config.jitterMs;
|
|
53
|
+
const delay = Math.min(exponentialDelay + jitter, config.maxDelayMs);
|
|
54
|
+
|
|
55
|
+
console.log(`Rate limited. Retrying in ${delay.toFixed(0)}ms...`);
|
|
56
|
+
await new Promise(r => setTimeout(r, delay));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
throw new Error('Unreachable');
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Step 3: Add Idempotency Keys
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
67
|
+
import crypto from 'crypto';
|
|
68
|
+
|
|
69
|
+
// Generate deterministic key from operation params (for safe retries)
|
|
70
|
+
function generateIdempotencyKey(operation: string, params: Record<string, any>): string {
|
|
71
|
+
const data = JSON.stringify({ operation, params });
|
|
72
|
+
return crypto.createHash('sha256').update(data).digest('hex');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function idempotentRequest<T>(
|
|
76
|
+
client: VercelClient,
|
|
77
|
+
params: Record<string, any>,
|
|
78
|
+
idempotencyKey?: string // Pass existing key for retries
|
|
79
|
+
): Promise<T> {
|
|
80
|
+
// Use provided key (for retries) or generate deterministic key from params
|
|
81
|
+
const key = idempotencyKey || generateIdempotencyKey(params.method || 'POST', params);
|
|
82
|
+
return client.request({
|
|
83
|
+
...params,
|
|
84
|
+
headers: { 'Idempotency-Key': key, ...params.headers },
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Output
|
|
90
|
+
- Reliable API calls with automatic retry
|
|
91
|
+
- Idempotent requests preventing duplicates
|
|
92
|
+
- Rate limit headers properly handled
|
|
93
|
+
|
|
94
|
+
## Error Handling
|
|
95
|
+
| Header | Description | Action |
|
|
96
|
+
|--------|-------------|--------|
|
|
97
|
+
| X-RateLimit-Limit | Max requests | Monitor usage |
|
|
98
|
+
| X-RateLimit-Remaining | Remaining requests | Throttle if low |
|
|
99
|
+
| X-RateLimit-Reset | Reset timestamp | Wait until reset |
|
|
100
|
+
| Retry-After | Seconds to wait | Honor this value |
|
|
101
|
+
|
|
102
|
+
## Examples
|
|
103
|
+
|
|
104
|
+
### Queue-Based Rate Limiting
|
|
105
|
+
```typescript
|
|
106
|
+
import PQueue from 'p-queue';
|
|
107
|
+
|
|
108
|
+
const queue = new PQueue({
|
|
109
|
+
concurrency: 5,
|
|
110
|
+
interval: 1000,
|
|
111
|
+
intervalCap: 10,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
async function queuedRequest<T>(operation: () => Promise<T>): Promise<T> {
|
|
115
|
+
return queue.add(operation);
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Monitor Rate Limit Usage
|
|
120
|
+
```typescript
|
|
121
|
+
class RateLimitMonitor {
|
|
122
|
+
private remaining: number = 60;
|
|
123
|
+
private resetAt: Date = new Date();
|
|
124
|
+
|
|
125
|
+
updateFromHeaders(headers: Headers) {
|
|
126
|
+
this.remaining = parseInt(headers.get('X-RateLimit-Remaining') || '60');
|
|
127
|
+
const resetTimestamp = headers.get('X-RateLimit-Reset');
|
|
128
|
+
if (resetTimestamp) {
|
|
129
|
+
this.resetAt = new Date(parseInt(resetTimestamp) * 1000);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
shouldThrottle(): boolean {
|
|
134
|
+
// Only throttle if low remaining AND reset hasn't happened yet
|
|
135
|
+
return this.remaining < 5 && new Date() < this.resetAt;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
getWaitTime(): number {
|
|
139
|
+
return Math.max(0, this.resetAt.getTime() - Date.now());
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Resources
|
|
145
|
+
- [Vercel Rate Limits](https://vercel.com/docs/rate-limits)
|
|
146
|
+
- [p-queue Documentation](https://github.com/sindresorhus/p-queue)
|
|
147
|
+
|
|
148
|
+
## Next Steps
|
|
149
|
+
For security configuration, see `vercel-security-basics`.
|