AWS Lambda Durable Functions
awslabs/agent-plugins
Build resilient, long-running, multi-step applications with AWS Lambda durable functions with automatic state persistence, retry logic, and orchestration for long-running executions.
Comprehensive guide for building serverless applications with Cloudflare Workers.
$ npx skills add einverne/dotfiles --skill cloudflare-workers -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install einverne/dotfiles cloudflare-workers --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/einverne/dotfiles.git skills-src && mkdir -p .claude/skills && cp -r skills-src/claude/skills/cloudflare-workers .claude/skills/cloudflare-workers && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "cloudflare-workers" agent skill from https://github.com/einverne/dotfiles/tree/master/claude/skills/cloudflare-workers into .claude/skills/cloudflare-workers/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cloudflare-workers", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/einverne/dotfiles/tree/master/claude/skills/cloudflare-workersType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add einverne/dotfiles --skill cloudflare-workers -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install einverne/dotfiles cloudflare-workers --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/einverne/dotfiles.git skills-src && mkdir -p .agents/skills && cp -r skills-src/claude/skills/cloudflare-workers .agents/skills/cloudflare-workers && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "cloudflare-workers" agent skill from https://github.com/einverne/dotfiles/tree/master/claude/skills/cloudflare-workers into .agents/skills/cloudflare-workers/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cloudflare-workers", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add einverne/dotfiles --skill cloudflare-workers -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install einverne/dotfiles cloudflare-workers --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/einverne/dotfiles.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/claude/skills/cloudflare-workers .cursor/skills/cloudflare-workers && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "cloudflare-workers" agent skill from https://github.com/einverne/dotfiles/tree/master/claude/skills/cloudflare-workers into .cursor/skills/cloudflare-workers/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cloudflare-workers", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/einverne/dotfiles.git --path claude/skills/cloudflare-workers--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add einverne/dotfiles --skill cloudflare-workers -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install einverne/dotfiles cloudflare-workers --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/einverne/dotfiles.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/claude/skills/cloudflare-workers .gemini/skills/cloudflare-workers && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "cloudflare-workers" agent skill from https://github.com/einverne/dotfiles/tree/master/claude/skills/cloudflare-workers into .gemini/skills/cloudflare-workers/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cloudflare-workers", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install einverne/dotfiles cloudflare-workersInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add einverne/dotfiles --skill cloudflare-workers -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/einverne/dotfiles.git skills-src && mkdir -p .github/skills && cp -r skills-src/claude/skills/cloudflare-workers .github/skills/cloudflare-workers && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "cloudflare-workers" agent skill from https://github.com/einverne/dotfiles/tree/master/claude/skills/cloudflare-workers into .github/skills/cloudflare-workers/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cloudflare-workers", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add einverne/dotfiles --skill cloudflare-workers -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install einverne/dotfiles cloudflare-workers --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/einverne/dotfiles.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/claude/skills/cloudflare-workers .opencode/skills/cloudflare-workers && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "cloudflare-workers" agent skill from https://github.com/einverne/dotfiles/tree/master/claude/skills/cloudflare-workers into .opencode/skills/cloudflare-workers/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cloudflare-workers", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
cloudflare-workersComprehensive guide for building serverless applications with Cloudflare Workers.
Cloudflare Workers is an agent skill from einverne/dotfiles. Comprehensive guide for building serverless applications with Cloudflare Workers. Use when developing Workers, configuring bindings, implementing runtime APIs, testing Workers, using Wrangler CLI, deploying to production, or building edge functions with JavaScript/TypeScript/Python/Rust.
Its SKILL.md is about 8.4k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Backend & APIs, covering Serverless. It works with Cloudflare Workers, JavaScript, Python and Rust. The repository describes itself as: my personal dotfiles managed by dotbot, zinit. The licence is MIT.
4 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit c6c0686. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
wranglernpmnpxcurlFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
developers.cloudflare.comdiscord.cloudflare.comgithub.comcloudflarestatus.comFrom URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
API_KEYCLOUDFLARE_API_TOKENJWT_SECRETWEBHOOK_SECRETFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Cloudflare Workers loads about 8.4k tokens when it runs. Until then it costs about 77 tokens; SKILL.md has 631 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from einverne/dotfiles at commit c6c0686, republished under its MIT licence (© einverne). 631 words, ~8,420 tokens.
.claude/skills/cloudflare-workers/SKILL.md (or your agent's skills folder).Cloudflare Workers is a serverless execution environment that runs JavaScript, TypeScript, Python, and Rust code on Cloudflare's global edge network across 300+ cities worldwide.
Use this skill when:
V8 Isolates: Workers run in lightweight V8 isolates (not containers):
Request Lifecycle:
Key Characteristics:
ES Modules (Recommended):
// Modern syntax with export default
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
return new Response('Hello World!');
}
};Service Worker (Legacy):
// Legacy format
addEventListener('fetch', (event) => {
event.respondWith(new Response('Hello World!'));
});Workers support multiple event types:
Fetch Handler (HTTP requests):
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
return new Response('Hello');
}
};Scheduled Handler (Cron jobs):
export default {
async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext): Promise<void> {
// Runs on schedule
await fetch('https://api.example.com/cleanup');
}
};Queue Handler (Message processing):
export default {
async queue(batch: MessageBatch, env: Env, ctx: ExecutionContext): Promise<void> {
for (const message of batch.messages) {
await processMessage(message.body);
}
}
};Email Handler (Email routing):
export default {
async email(message: ForwardableEmailMessage, env: Env, ctx: ExecutionContext): Promise<void> {
await message.forward('destination@example.com');
}
};Tail Handler (Log aggregation):
export default {
async tail(events: TraceItem[], env: Env, ctx: ExecutionContext): Promise<void> {
// Process logs from other Workers
}
};# Install globally
npm install -g wrangler
# Or use npx
npx wrangler <command>
# Login to Cloudflare
wrangler login
# Check version
wrangler --version# Create new Worker
wrangler init my-worker
# Options: TypeScript, Git, deploy
# Create with template
wrangler init my-worker --template <template-url>
# Development server
wrangler dev # Local mode
wrangler dev --remote # Remote mode (actual edge)
wrangler dev --port 8080 # Custom port
# Deploy
wrangler deploy # Production
wrangler deploy --dry-run # Preview changes
wrangler deploy --env staging # Specific environment
# Logs
wrangler tail # Real-time logs
wrangler tail --format pretty # Formatted output
wrangler tail --status error # Filter by status
# Versions & Rollback
wrangler deployments list
wrangler rollback [version-id]
wrangler versions listname = "my-worker"
main = "src/index.ts"
compatibility_date = "2024-01-01"
compatibility_flags = ["nodejs_compat"]
# Routes
routes = [
{ pattern = "example.com/*", zone_name = "example.com" }
]
# Or use custom domains
# [[routes]]
# pattern = "api.example.com/*"
# custom_domain = true
# Environment variables
[vars]
ENVIRONMENT = "production"
API_VERSION = "v1"
# Cron triggers
[triggers]
crons = ["0 0 * * *"] # Daily at midnight
# Build configuration
[build]
command = "npm run build"
watch_dirs = ["src"]
# Environments
[env.staging]
name = "my-worker-staging"
routes = [
{ pattern = "staging.example.com/*", zone_name = "example.com" }
]
[env.staging.vars]
ENVIRONMENT = "staging"# Add secret
wrangler secret put API_KEY
# Enter value when prompted
# List secrets
wrangler secret list
# Delete secret
wrangler secret delete API_KEY
# Bulk upload
echo "VALUE" | wrangler secret put API_KEYexport default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
// Parse URL
const url = new URL(request.url);
// Request properties
const method = request.method;
const headers = request.headers;
const body = await request.text();
// Subrequest
const response = await fetch('https://api.example.com/data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key: 'value' })
});
const data = await response.json();
// Response
return new Response(JSON.stringify(data), {
status: 200,
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'public, max-age=3600'
}
});
}
};// Read headers
const userAgent = request.headers.get('User-Agent');
const allHeaders = Object.fromEntries(request.headers);
// Set headers
const headers = new Headers();
headers.set('Content-Type', 'application/json');
headers.append('X-Custom-Header', 'value');
// Cloudflare-specific headers
const country = request.cf?.country;
const colo = request.cf?.colo;
const clientIP = request.headers.get('CF-Connecting-IP');
// Response with headers
return new Response(body, { headers });export default {
async fetch(request: Request): Promise<Response> {
const cache = caches.default;
// Check cache
let response = await cache.match(request);
if (!response) {
// Cache miss - fetch from origin
response = await fetch(request);
// Clone and cache response
const cacheResponse = response.clone();
ctx.waitUntil(cache.put(request, cacheResponse));
}
return response;
}
};Cache with custom key:
const cacheKey = new Request(url.toString(), request);
const response = await cache.match(cacheKey);
if (!response) {
response = await fetch(request);
ctx.waitUntil(cache.put(cacheKey, response.clone()));
}Cache with TTL:
const response = await fetch('https://api.example.com/data', {
cf: {
cacheTtl: 3600,
cacheEverything: true,
cacheKey: 'custom-key'
}
});export default {
async fetch(request: Request): Promise<Response> {
const response = await fetch(request);
return new HTMLRewriter()
.on('title', {
element(element) {
element.setInnerContent('New Title');
}
})
.on('a[href]', {
element(element) {
const href = element.getAttribute('href');
element.setAttribute('href', href.replace('http://', 'https://'));
}
})
.on('script', {
element(element) {
element.remove();
}
})
.transform(response);
}
};Text manipulation:
new HTMLRewriter()
.on('p', {
text(text) {
if (text.text.includes('replace-me')) {
text.replace('new-text');
}
}
})
.transform(response);export default {
async fetch(request: Request): Promise<Response> {
const upgradeHeader = request.headers.get('Upgrade');
if (upgradeHeader !== 'websocket') {
return new Response('Expected WebSocket', { status: 426 });
}
const pair = new WebSocketPair();
const [client, server] = Object.values(pair);
// Accept WebSocket connection
server.accept();
// Handle messages
server.addEventListener('message', (event) => {
server.send(`Echo: ${event.data}`);
});
server.addEventListener('close', () => {
console.log('WebSocket closed');
});
return new Response(null, {
status: 101,
webSocket: client
});
}
};// Readable stream
const { readable, writable } = new TransformStream();
const writer = writable.getWriter();
writer.write(new TextEncoder().encode('chunk 1'));
writer.write(new TextEncoder().encode('chunk 2'));
writer.close();
return new Response(readable, {
headers: { 'Content-Type': 'text/plain' }
});Stream transformation:
const response = await fetch('https://example.com/large-file');
const { readable, writable } = new TransformStream({
transform(chunk, controller) {
// Process chunk
controller.enqueue(chunk);
}
});
response.body.pipeTo(writable);
return new Response(readable);export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
// waitUntil: Run tasks after response sent
ctx.waitUntil(
fetch('https://analytics.example.com/log', {
method: 'POST',
body: JSON.stringify({ url: request.url })
})
);
// passThroughOnException: Continue to origin on error
ctx.passThroughOnException();
return new Response('OK');
}
};// Generate hash
const data = new TextEncoder().encode('message');
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
// HMAC signature
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode('secret'),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign', 'verify']
);
const signature = await crypto.subtle.sign('HMAC', key, data);
// Verify
const valid = await crypto.subtle.verify('HMAC', key, signature, data);
// Random values
const randomBytes = crypto.getRandomValues(new Uint8Array(32));
const uuid = crypto.randomUUID();
// Timing-safe comparison
const equal = crypto.timingSafeEqual(buffer1, buffer2);// wrangler.toml
[vars]
API_URL = "https://api.example.com"
// Usage
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const apiUrl = env.API_URL;
return new Response(apiUrl);
}
};# wrangler.toml
[[kv_namespaces]]
binding = "MY_KV"
id = "your-namespace-id"export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Put
await env.MY_KV.put('key', 'value', {
expirationTtl: 3600,
metadata: { userId: '123' }
});
// Get
const value = await env.MY_KV.get('key');
const json = await env.MY_KV.get('key', 'json');
const buffer = await env.MY_KV.get('key', 'arrayBuffer');
const stream = await env.MY_KV.get('key', 'stream');
// Get with metadata
const { value, metadata } = await env.MY_KV.getWithMetadata('key');
// Delete
await env.MY_KV.delete('key');
// List
const list = await env.MY_KV.list({ prefix: 'user:' });
return new Response(value);
}
};# wrangler.toml
[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-bucket"export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Put object
await env.MY_BUCKET.put('file.txt', 'content', {
httpMetadata: {
contentType: 'text/plain',
contentLanguage: 'en-US',
cacheControl: 'public, max-age=3600'
},
customMetadata: {
uploadedBy: 'user123'
}
});
// Get object
const object = await env.MY_BUCKET.get('file.txt');
if (!object) {
return new Response('Not Found', { status: 404 });
}
// Return object
return new Response(object.body, {
headers: {
'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',
'ETag': object.etag
}
});
// Delete object
await env.MY_BUCKET.delete('file.txt');
// List objects
const listed = await env.MY_BUCKET.list({ prefix: 'uploads/' });
// Multipart upload
const multipart = await env.MY_BUCKET.createMultipartUpload('large-file.bin');
const part1 = await multipart.uploadPart(1, chunk1);
const part2 = await multipart.uploadPart(2, chunk2);
await multipart.complete([part1, part2]);
}
};# wrangler.toml
[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "your-database-id"export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Query
const result = await env.DB.prepare(
'SELECT * FROM users WHERE id = ?'
).bind(userId).first();
// Insert
const info = await env.DB.prepare(
'INSERT INTO users (name, email) VALUES (?, ?)'
).bind('Alice', 'alice@example.com').run();
// Batch (atomic)
const results = await env.DB.batch([
env.DB.prepare('UPDATE accounts SET balance = balance - 100 WHERE id = ?').bind(1),
env.DB.prepare('UPDATE accounts SET balance = balance + 100 WHERE id = ?').bind(2)
]);
// All results
const { results: rows } = await env.DB.prepare(
'SELECT * FROM users'
).all();
return new Response(JSON.stringify(result));
}
};# wrangler.toml
[[durable_objects.bindings]]
name = "COUNTER"
class_name = "Counter"
script_name = "my-worker"// Define Durable Object
export class Counter {
state: DurableObjectState;
constructor(state: DurableObjectState, env: Env) {
this.state = state;
}
async fetch(request: Request): Promise<Response> {
let count = (await this.state.storage.get<number>('count')) || 0;
count++;
await this.state.storage.put('count', count);
return new Response(JSON.stringify({ count }));
}
// Alarm handler
async alarm(): Promise<void> {
// Runs at scheduled time
await this.state.storage.deleteAll();
}
}
// Use in Worker
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const id = env.COUNTER.idFromName('global');
const counter = env.COUNTER.get(id);
return counter.fetch(request);
}
};# wrangler.toml
[[queues.producers]]
binding = "MY_QUEUE"
queue = "my-queue"
[[queues.consumers]]
queue = "my-queue"
max_batch_size = 10
max_batch_timeout = 30// Producer
export default {
async fetch(request: Request, env: Env): Promise<Response> {
await env.MY_QUEUE.send({
type: 'email',
to: 'user@example.com'
});
// Batch send
await env.MY_QUEUE.sendBatch([
{ body: { message: 1 } },
{ body: { message: 2 } }
]);
return new Response('Queued');
}
};
// Consumer
export default {
async queue(batch: MessageBatch, env: Env, ctx: ExecutionContext): Promise<void> {
for (const message of batch.messages) {
try {
await processMessage(message.body);
message.ack();
} catch (error) {
message.retry();
}
}
}
};# wrangler.toml
[ai]
binding = "AI"export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Text generation
const response = await env.AI.run('@cf/meta/llama-3-8b-instruct', {
messages: [
{ role: 'user', content: 'What is edge computing?' }
]
});
// Image generation
const imageResponse = await env.AI.run('@cf/stabilityai/stable-diffusion-xl-base-1.0', {
prompt: 'A sunset over mountains'
});
// Embeddings
const embeddings = await env.AI.run('@cf/baai/bge-base-en-v1.5', {
text: ['Hello world', 'Cloudflare Workers']
});
// Speech recognition
const audio = await request.arrayBuffer();
const transcription = await env.AI.run('@cf/openai/whisper', {
audio: [...new Uint8Array(audio)]
});
return new Response(JSON.stringify(response));
}
};# wrangler.toml
[[vectorize]]
binding = "VECTORIZE_INDEX"
index_name = "my-index"export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Insert vectors
await env.VECTORIZE_INDEX.insert([
{
id: '1',
values: [0.1, 0.2, 0.3],
metadata: { text: 'Hello world' }
}
]);
// Query
const results = await env.VECTORIZE_INDEX.query(
[0.1, 0.2, 0.3],
{ topK: 5 }
);
return new Response(JSON.stringify(results));
}
};export default {
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
// Route by path
switch (url.pathname) {
case '/':
return new Response('Home');
case '/about':
return new Response('About');
default:
return new Response('Not Found', { status: 404 });
}
}
};Using Hono framework:
import { Hono } from 'hono';
const app = new Hono();
app.get('/', (c) => c.text('Home'));
app.get('/api/users/:id', async (c) => {
const id = c.req.param('id');
const user = await getUser(id);
return c.json(user);
});
export default app;async function auth(request: Request, env: Env): Promise<Response | null> {
const token = request.headers.get('Authorization');
if (!token) {
return new Response('Unauthorized', { status: 401 });
}
return null; // Continue
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const authResponse = await auth(request, env);
if (authResponse) return authResponse;
// Continue with request
return new Response('Protected content');
}
};export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
try {
const response = await processRequest(request, env);
return response;
} catch (error) {
console.error('Error:', error);
// Log to external service
ctx.waitUntil(
fetch('https://logging.example.com/error', {
method: 'POST',
body: JSON.stringify({
error: error.message,
stack: error.stack,
url: request.url
})
})
);
return new Response('Internal Server Error', { status: 500 });
}
}
};function corsHeaders(origin: string) {
return {
'Access-Control-Allow-Origin': origin,
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400'
};
}
export default {
async fetch(request: Request): Promise<Response> {
const origin = request.headers.get('Origin') || '*';
// Handle preflight
if (request.method === 'OPTIONS') {
return new Response(null, {
headers: corsHeaders(origin)
});
}
// Handle request
const response = await handleRequest(request);
// Add CORS headers
const headers = new Headers(response.headers);
Object.entries(corsHeaders(origin)).forEach(([key, value]) => {
headers.set(key, value);
});
return new Response(response.body, {
status: response.status,
headers
});
}
};async function rateLimit(ip: string, env: Env): Promise<boolean> {
const key = `ratelimit:${ip}`;
const limit = 100;
const window = 60;
const current = await env.MY_KV.get(key);
const count = current ? parseInt(current) : 0;
if (count >= limit) {
return false;
}
await env.MY_KV.put(key, (count + 1).toString(), {
expirationTtl: window
});
return true;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const ip = request.headers.get('CF-Connecting-IP') || 'unknown';
if (!await rateLimit(ip, env)) {
return new Response('Rate limit exceeded', { status: 429 });
}
return new Response('OK');
}
};# Start dev server
wrangler dev
# Test with curl
curl http://localhost:8787
# Remote development (uses actual edge)
wrangler dev --remotenpm install -D vitest @cloudflare/vitest-pool-workersvitest.config.ts:
import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config';
export default defineWorkersConfig({
test: {
poolOptions: {
workers: {
wrangler: { configPath: './wrangler.toml' },
},
},
},
});Test file:
import { env, createExecutionContext, waitOnExecutionContext } from 'cloudflare:test';
import { describe, it, expect } from 'vitest';
import worker from '../src/index';
describe('Worker', () => {
it('responds with Hello World', async () => {
const request = new Request('http://example.com');
const ctx = createExecutionContext();
const response = await worker.fetch(request, env, ctx);
await waitOnExecutionContext(ctx);
expect(await response.text()).toBe('Hello World!');
});
});import { unstable_dev } from 'wrangler';
describe('Worker integration tests', () => {
let worker;
beforeAll(async () => {
worker = await unstable_dev('src/index.ts', {
experimental: { disableExperimentalWarning: true }
});
});
afterAll(async () => {
await worker.stop();
});
it('should return 200', async () => {
const resp = await worker.fetch();
expect(resp.status).toBe(200);
});
});# Deploy to production
wrangler deploy
# Deploy to specific environment
wrangler deploy --env staging
# Preview deployment
wrangler deploy --dry-run# wrangler.toml
name = "my-worker"
[env.staging]
name = "my-worker-staging"
routes = [
{ pattern = "staging.example.com/*", zone_name = "example.com" }
]
[env.production]
name = "my-worker-production"
routes = [
{ pattern = "api.example.com/*", zone_name = "example.com" }
]wrangler deploy --env staging
wrangler deploy --env production# .github/workflows/deploy.yml
name: Deploy Worker
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm install
- run: npm test
- uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}# Deploy to 10% of traffic
wrangler versions deploy --percentage 10
# Promote to 100%
wrangler versions deploy --percentage 100
# Rollback
wrangler rollback# wrangler.toml
name = "my-worker"
main = "src/index.ts"
[assets]
directory = "./public"
binding = "ASSETS"export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Serve static asset
const asset = await env.ASSETS.fetch(request);
if (asset.status === 200) {
return asset;
}
// Fallback to dynamic response
return new Response('Not Found', { status: 404 });
}
};[assets]
directory = "./dist"
binding = "ASSETS"
html_handling = "force-https"
not_found_handling = "single-page-application"export default {
async fetch(request: Request): Promise<Response> {
console.log('Request URL:', request.url);
console.error('Error occurred');
console.warn('Warning message');
// Structured logging
console.log(JSON.stringify({
level: 'info',
message: 'Request processed',
url: request.url,
timestamp: new Date().toISOString()
}));
return new Response('OK');
}
};# Tail logs
wrangler tail
# Filter by status
wrangler tail --status error
# Filter by method
wrangler tail --method POST
# Filter by sampling
wrangler tail --sampling-rate 0.5// Use Analytics Engine binding
export default {
async fetch(request: Request, env: Env): Promise<Response> {
env.ANALYTICS.writeDataPoint({
blobs: ['example'],
doubles: [123.45],
indexes: ['index_value']
});
return new Response('Logged');
}
};import * as Sentry from '@sentry/cloudflare';
export default {
async fetch(request: Request, env: Env): Promise<Response> {
Sentry.init({
dsn: env.SENTRY_DSN,
environment: env.ENVIRONMENT
});
try {
return await handleRequest(request);
} catch (error) {
Sentry.captureException(error);
throw error;
}
}
};// Lazy load large dependencies
export default {
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
if (url.pathname === '/heavy') {
const { processHeavy } = await import('./heavy');
return processHeavy(request);
}
return new Response('OK');
}
};const CACHE_TTL = 3600;
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const cache = caches.default;
const cacheKey = new Request(request.url);
// 1. Check edge cache
let response = await cache.match(cacheKey);
if (response) return response;
// 2. Check KV cache
const kvCached = await env.MY_KV.get(request.url);
if (kvCached) {
response = new Response(kvCached);
ctx.waitUntil(cache.put(cacheKey, response.clone()));
return response;
}
// 3. Fetch from origin
response = await fetch(request);
// 4. Store in both caches
ctx.waitUntil(Promise.all([
cache.put(cacheKey, response.clone()),
env.MY_KV.put(request.url, await response.clone().text(), {
expirationTtl: CACHE_TTL
})
]));
return response;
}
};import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';
const app = new Hono();
app.use('*', cors());
app.use('*', logger());
// Routes
app.get('/api/users', async (c) => {
const users = await c.env.DB.prepare('SELECT * FROM users').all();
return c.json(users.results);
});
app.get('/api/users/:id', async (c) => {
const id = c.req.param('id');
const user = await c.env.DB.prepare(
'SELECT * FROM users WHERE id = ?'
).bind(id).first();
if (!user) {
return c.json({ error: 'Not found' }, 404);
}
return c.json(user);
});
app.post('/api/users', async (c) => {
const body = await c.req.json();
const result = await c.env.DB.prepare(
'INSERT INTO users (name, email) VALUES (?, ?)'
).bind(body.name, body.email).run();
return c.json({ id: result.meta.last_row_id }, 201);
});
export default app;import { sign, verify } from 'hono/jwt';
async function authenticate(request: Request, env: Env): Promise<any> {
const authHeader = request.headers.get('Authorization');
if (!authHeader?.startsWith('Bearer ')) {
throw new Error('Missing token');
}
const token = authHeader.substring(7);
const payload = await verify(token, env.JWT_SECRET);
return payload;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
try {
const user = await authenticate(request, env);
return new Response(`Hello ${user.name}`);
} catch (error) {
return new Response('Unauthorized', { status: 401 });
}
}
};export default {
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
const imageUrl = url.searchParams.get('url');
if (!imageUrl) {
return new Response('Missing url parameter', { status: 400 });
}
const response = await fetch(imageUrl);
return new Response(response.body, {
headers: {
'Content-Type': response.headers.get('Content-Type') || 'image/jpeg',
'Cache-Control': 'public, max-age=86400',
'CDN-Cache-Control': 'public, max-age=31536000'
}
});
}
};export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
if (request.method !== 'POST') {
return new Response('Method not allowed', { status: 405 });
}
// Verify signature
const signature = request.headers.get('X-Hub-Signature-256');
const body = await request.text();
const valid = await verifySignature(body, signature, env.WEBHOOK_SECRET);
if (!valid) {
return new Response('Invalid signature', { status: 401 });
}
// Queue for processing
ctx.waitUntil(
env.WEBHOOK_QUEUE.send(JSON.parse(body))
);
return new Response('OK');
}
};CPU Time Exceeded:
Memory Exceeded:
Script Size Too Large:
wrangler deploy --dry-run --outdir=dist outputBinding Not Found:
CORS Errors:
# Local debugging
wrangler dev --local
# Remote debugging (real edge environment)
wrangler dev --remote
# Inspect bundle
wrangler deploy --dry-run --outdir=dist
# Check logs
wrangler tail --format prettywrangler dev© einverne, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in claude/skills/cloudflare-workers of einverne/dotfiles.
Open the folder on GitHubat commit c6c0686
Cloudflare Workers next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Cloudflare Workers this skilleinverne/dotfiles | 121 | — | ~8.4k | Automated safety check: Pass | MIT | |
| AWS Lambda Durable Functionsawslabs/agent-plugins | 915 | — | ~2.3k | Automated safety check: Pass | Apache-2.0 | |
| Cloudflare Workers Multi Langsecondsky/claude-skills | 227 | — | ~1.3k | Automated safety check: Pass | MIT | |
| jscpd Code Migration Trackerkucherenko/jscpd | 6.4k | — | ~5k | Automated safety check: Pass | MIT | |
| Cross-Language Coding Standardszereight/gitlab-mcp | 2k | 1 repos | ~1.4k | Automated safety check: Pass | MIT | |
| Coding Agentmastra-ai/mastra | 29k | — | ~2.3k | Automated safety check: Pass | Custom licence |
awslabs/agent-plugins
Build resilient, long-running, multi-step applications with AWS Lambda durable functions with automatic state persistence, retry logic, and orchestration for long-running executions.
secondsky/claude-skills
Multi-language Workers development with Rust, Python, and WebAssembly.
kucherenko/jscpd
Measures a code port between languages or frameworks with jscpd's function-level comparison, porting tests before code and tracking what is left unmatched.
zereight/gitlab-mcp
Shared reference for naming, function size, complexity and error handling rules that reviewer agents apply across TypeScript, Python, Go, Rust, Java, C# and Swift.
mastra-ai/mastra
Authoring playbook for building agents that write, edit, review, or refactor code.
supercorp-ai/supercov
Measures test coverage and code quality in a repository with the supercov CLI, and turns what it finds into small, focused tests or fixes.
einverne/dotfiles
Browser automation, debugging, and performance analysis using Puppeteer CLI scripts.
einverne/dotfiles
Comprehensive document creation, editing, and analysis with support for tracked changes, comments, formatting preservation, and text extraction.
einverne/dotfiles
Comprehensive PDF manipulation toolkit for extracting text and tables, creating new PDFs, merging/splitting documents, and handling forms.
einverne/dotfiles
Guide for implementing Google Gemini API audio capabilities - analyze audio with transcription, summarization, and understanding (up to 9.5 hours), plus generate speech with controllable TTS.
einverne/dotfiles
Guide for implementing Google Gemini API image generation - create high-quality images from text prompts using gemini-2.5-flash-image model.
einverne/dotfiles
Guide for implementing Google Gemini API image understanding - analyze images with captioning, classification, visual QA, object detection, segmentation, and multi-image comparison.
Categories
Comprehensive guide for building serverless applications with Cloudflare Workers. Cloudflare Workers is an agent skill from einverne/dotfiles. Comprehensive guide for building serverless applications with Cloudflare Workers.
Cloudflare Workers fits situations like: developing Workers; configuring bindings; implementing runtime APIs; testing Workers.
Run `npx skills add einverne/dotfiles --skill cloudflare-workers -a claude-code`. Or copy the skill folder (claude/skills/cloudflare-workers in einverne/dotfiles) into .claude/skills/cloudflare-workers in your project. Claude Code loads it when a task matches its description.
Run `npx skills add einverne/dotfiles --skill cloudflare-workers -a codex`. Or copy the skill folder (claude/skills/cloudflare-workers in einverne/dotfiles) into .agents/skills/cloudflare-workers in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add einverne/dotfiles --skill cloudflare-workers -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/cloudflare-workers, .gemini/skills/cloudflare-workers, .github/skills/cloudflare-workers and .opencode/skills/cloudflare-workers in your project.
Going by SKILL.md and its folder, Cloudflare Workers needs the command-line tools its instructions call (wrangler, npm, npx and curl) and credentials named API_KEY, CLOUDFLARE_API_TOKEN, JWT_SECRET and WEBHOOK_SECRET. Our summary lists: Node.js; A credential in API_KEY.
SKILL.md names 4 domains. As links in the text: developers.cloudflare.com, discord.cloudflare.com, github.com and cloudflarestatus.com. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Cloudflare Workers is published under the MIT licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.
About 8.4k tokens (SKILL.md is roughly 34k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Cloudflare Workers: AWS Lambda Durable Functions (awslabs/agent-plugins, 915 stars), Cloudflare Workers Multi Lang (secondsky/claude-skills, 227 stars), jscpd Code Migration Tracker (kucherenko/jscpd, 6.4k stars) and Cross-Language Coding Standards (zereight/gitlab-mcp, 2k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
einverne (a GitHub user) maintains it in einverne/dotfiles, which has 121 GitHub stars. The repository holds 39 skills in this directory. The repository was last updated on September 9, 2026.
Source: einverne/dotfiles on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.