mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-03-20 06:11:27 -07:00
update skills
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
# Cloudflare Workers Playground Skill Reference
|
||||
|
||||
## Overview
|
||||
|
||||
Cloudflare Workers Playground is a browser-based sandbox for instantly experimenting with, testing, and deploying Cloudflare Workers without authentication or setup. This skill provides patterns, APIs, and best practices specifically for Workers Playground development.
|
||||
|
||||
**URL:** [workers.cloudflare.com/playground](https://workers.cloudflare.com/playground)
|
||||
|
||||
## ⚠️ Playground Constraints
|
||||
|
||||
**Playground is NOT production-equivalent:**
|
||||
- ✅ Real Workers runtime, instant testing, shareable URLs
|
||||
- ❌ No TypeScript (JavaScript only)
|
||||
- ❌ No bindings (KV, D1, R2, Durable Objects)
|
||||
- ❌ No environment variables or secrets
|
||||
- ❌ ES modules only (no Service Worker format)
|
||||
- ⚠️ Safari broken (use Chrome/Firefox)
|
||||
|
||||
**For production:** Use `wrangler` CLI. Playground is for rapid prototyping.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Minimal Worker:
|
||||
|
||||
```javascript
|
||||
export default {
|
||||
async fetch(request, env, ctx) {
|
||||
return new Response('Hello World');
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
JSON API:
|
||||
|
||||
```javascript
|
||||
export default {
|
||||
async fetch(request, env, ctx) {
|
||||
const data = { message: 'Hello', timestamp: Date.now() };
|
||||
return Response.json(data);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Proxy with modification:
|
||||
|
||||
```javascript
|
||||
export default {
|
||||
async fetch(request, env, ctx) {
|
||||
const response = await fetch('https://example.com');
|
||||
const modified = new Response(response.body, response);
|
||||
modified.headers.set('X-Custom-Header', 'added-by-worker');
|
||||
return modified;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Import from CDN:
|
||||
|
||||
```javascript
|
||||
import { Hono } from 'https://esm.sh/hono@3';
|
||||
|
||||
export default {
|
||||
async fetch(request) {
|
||||
const app = new Hono();
|
||||
app.get('/', (c) => c.text('Hello Hono!'));
|
||||
return app.fetch(request);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Reading Order
|
||||
|
||||
1. **[configuration.md](configuration.md)** - Start here: playground setup, constraints, deployment
|
||||
2. **[api.md](api.md)** - Core APIs: Request, Response, ExecutionContext, fetch, Cache
|
||||
3. **[patterns.md](patterns.md)** - Common use cases: routing, proxying, A/B testing, multi-module code
|
||||
4. **[gotchas.md](gotchas.md)** - Troubleshooting: errors, browser issues, limits, best practices
|
||||
|
||||
## In This Reference
|
||||
|
||||
- **[configuration.md](configuration.md)** - Setup, deployment, configuration
|
||||
- **[api.md](api.md)** - API endpoints, methods, interfaces
|
||||
- **[patterns.md](patterns.md)** - Common patterns, use cases, examples
|
||||
- **[gotchas.md](gotchas.md)** - Troubleshooting, best practices, limitations
|
||||
|
||||
## Key Features
|
||||
|
||||
**No Setup Required:**
|
||||
- Open URL and start coding
|
||||
- No CLI, no account, no config files
|
||||
- Code executes in real Cloudflare Workers runtime
|
||||
|
||||
**Instant Preview:**
|
||||
- Live preview pane with browser tab or HTTP tester
|
||||
- Auto-reload on code changes
|
||||
- DevTools integration (right-click → Inspect)
|
||||
|
||||
**Share & Deploy:**
|
||||
- Copy Link generates permanent shareable URL
|
||||
- Deploy button publishes to production in ~30 seconds
|
||||
- Get `*.workers.dev` subdomain immediately
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
- **API development:** Test endpoints before wrangler setup
|
||||
- **Learning Workers:** Experiment with APIs without local environment
|
||||
- **Prototyping:** Quick POCs for edge logic
|
||||
- **Sharing examples:** Generate shareable links for bug reports or demos
|
||||
- **Framework testing:** Import from CDN (Hono, itty-router, etc.)
|
||||
|
||||
## Limitations vs Production
|
||||
|
||||
| Feature | Playground | Production (wrangler) |
|
||||
|---------|------------|----------------------|
|
||||
| Language | JavaScript only | JS + TypeScript |
|
||||
| Bindings | None | KV, D1, R2, DO, AI, etc. |
|
||||
| Environment vars | None | Full support |
|
||||
| Module format | ES only | ES + Service Worker |
|
||||
| CPU time | 10ms (Free plan) | 10ms Free / 50ms Paid |
|
||||
| Custom domains | No | Yes |
|
||||
| Analytics | No | Yes |
|
||||
|
||||
## See Also
|
||||
|
||||
- [Cloudflare Workers Docs](https://developers.cloudflare.com/workers/)
|
||||
- [Workers Examples](https://developers.cloudflare.com/workers/examples/)
|
||||
- [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/)
|
||||
- [Workers API Reference](https://developers.cloudflare.com/workers/runtime-apis/)
|
||||
@@ -0,0 +1,101 @@
|
||||
# Workers Playground API
|
||||
|
||||
## Handler
|
||||
|
||||
```javascript
|
||||
export default {
|
||||
async fetch(request, env, ctx) {
|
||||
// request: Request, env: {} (empty in playground), ctx: ExecutionContext
|
||||
return new Response('Hello');
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Request
|
||||
|
||||
```javascript
|
||||
const method = request.method; // "GET", "POST"
|
||||
const url = new URL(request.url); // Parse URL
|
||||
const headers = request.headers; // Headers object
|
||||
const body = await request.json(); // Read body (consumes stream)
|
||||
const clone = request.clone(); // Clone before reading body
|
||||
|
||||
// Query params
|
||||
url.searchParams.get('page'); // Single value
|
||||
url.searchParams.getAll('tag'); // Array
|
||||
|
||||
// Cloudflare metadata
|
||||
request.cf.country; // "US"
|
||||
request.cf.colo; // "SFO"
|
||||
```
|
||||
|
||||
## Response
|
||||
|
||||
```javascript
|
||||
// Text
|
||||
return new Response('Hello', { status: 200 });
|
||||
|
||||
// JSON
|
||||
return Response.json({ data }, { status: 200, headers: {...} });
|
||||
|
||||
// Redirect
|
||||
return Response.redirect('/new-path', 301);
|
||||
|
||||
// Modify existing
|
||||
const modified = new Response(response.body, response);
|
||||
modified.headers.set('X-Custom', 'value');
|
||||
```
|
||||
|
||||
## ExecutionContext
|
||||
|
||||
```javascript
|
||||
// Background work (after response sent)
|
||||
ctx.waitUntil(fetch('https://logs.example.com', { method: 'POST', body: '...' }));
|
||||
return new Response('OK'); // Returns immediately
|
||||
```
|
||||
|
||||
## Fetch
|
||||
|
||||
```javascript
|
||||
const response = await fetch('https://api.example.com');
|
||||
const data = await response.json();
|
||||
|
||||
// With options
|
||||
await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name: 'Alice' })
|
||||
});
|
||||
```
|
||||
|
||||
## Cache
|
||||
|
||||
```javascript
|
||||
const cache = caches.default;
|
||||
|
||||
// Check cache
|
||||
let response = await cache.match(request);
|
||||
if (!response) {
|
||||
response = await fetch(origin);
|
||||
await cache.put(request, response.clone()); // Clone before put!
|
||||
}
|
||||
return response;
|
||||
```
|
||||
|
||||
## Crypto
|
||||
|
||||
```javascript
|
||||
crypto.randomUUID(); // UUID v4
|
||||
crypto.getRandomValues(new Uint8Array(16));
|
||||
|
||||
// SHA-256 hash
|
||||
const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(data));
|
||||
```
|
||||
|
||||
## Limits (Playground = Free Plan)
|
||||
|
||||
| Resource | Limit |
|
||||
|----------|-------|
|
||||
| CPU time | 10ms |
|
||||
| Subrequests | 50 |
|
||||
| Memory | 128 MB |
|
||||
@@ -0,0 +1,163 @@
|
||||
# Configuration
|
||||
|
||||
## Getting Started
|
||||
|
||||
Navigate to [workers.cloudflare.com/playground](https://workers.cloudflare.com/playground)
|
||||
|
||||
- **No account required** for testing
|
||||
- **No CLI or local setup** needed
|
||||
- Code executes in real Cloudflare Workers runtime
|
||||
- Share code via URL (never expires)
|
||||
|
||||
## Playground Constraints
|
||||
|
||||
⚠️ **Important Limitations**
|
||||
|
||||
| Constraint | Playground | Production Workers |
|
||||
|------------|------------|-------------------|
|
||||
| **Module Format** | ES modules only | ES modules or Service Worker |
|
||||
| **TypeScript** | Not supported (JS only) | Supported via build step |
|
||||
| **Bindings** | Not available | KV, D1, R2, Durable Objects, etc. |
|
||||
| **wrangler.toml** | Not used | Required for config |
|
||||
| **Environment Variables** | Not available | Full support |
|
||||
| **Secrets** | Not available | Full support |
|
||||
| **Custom Domains** | Not available | Full support |
|
||||
|
||||
**Playground is for rapid prototyping only.** For production apps, use `wrangler` CLI.
|
||||
|
||||
## Code Editor
|
||||
|
||||
### Syntax Requirements
|
||||
|
||||
Must export default object with `fetch` handler:
|
||||
|
||||
```javascript
|
||||
export default {
|
||||
async fetch(request, env, ctx) {
|
||||
return new Response('Hello World');
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- Must use ES modules (`export default`)
|
||||
- `fetch` method receives `(request, env, ctx)`
|
||||
- Must return `Response` object
|
||||
- TypeScript not supported (use plain JavaScript)
|
||||
|
||||
### Multi-Module Code
|
||||
|
||||
Import from external URLs or inline modules:
|
||||
|
||||
```javascript
|
||||
// Import from CDN
|
||||
import { Hono } from 'https://esm.sh/hono@3';
|
||||
|
||||
// Or paste library code and import relatively
|
||||
// (See patterns.md for multi-module examples)
|
||||
|
||||
export default {
|
||||
async fetch(request) {
|
||||
const app = new Hono();
|
||||
app.get('/', (c) => c.text('Hello'));
|
||||
return app.fetch(request);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Preview Panel
|
||||
|
||||
### Browser Tab
|
||||
|
||||
Default interactive preview with address bar:
|
||||
- Enter custom URL paths
|
||||
- Automatic reload on code changes
|
||||
- DevTools available (right-click → Inspect)
|
||||
|
||||
### HTTP Test Panel
|
||||
|
||||
Switch to **HTTP** tab for raw HTTP testing:
|
||||
- Change HTTP method (GET, POST, PUT, DELETE, PATCH, etc.)
|
||||
- Add/edit request headers
|
||||
- Modify request body (JSON, form data, text)
|
||||
- View response headers and body
|
||||
- Test different content types
|
||||
|
||||
Example HTTP test:
|
||||
```
|
||||
Method: POST
|
||||
URL: /api/users
|
||||
Headers:
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer token123
|
||||
Body:
|
||||
{
|
||||
"name": "Alice",
|
||||
"email": "alice@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
## Sharing Code
|
||||
|
||||
**Copy Link** button generates shareable URL:
|
||||
- Code embedded in URL fragment
|
||||
- Links never expire
|
||||
- No account required
|
||||
- Can be bookmarked for later
|
||||
|
||||
Example: `https://workers.cloudflare.com/playground#abc123...`
|
||||
|
||||
## Deploying from Playground
|
||||
|
||||
Click **Deploy** button to move code to production:
|
||||
|
||||
1. **Log in** to Cloudflare account (creates free account if needed)
|
||||
2. **Review** Worker name and code
|
||||
3. **Deploy** to global network (takes ~30 seconds)
|
||||
4. **Get URL**: Deployed to `<name>.workers.dev` subdomain
|
||||
5. **Manage** from dashboard: add bindings, custom domains, analytics
|
||||
|
||||
**After deploy:**
|
||||
- Code runs on Cloudflare's global network (300+ cities)
|
||||
- Can add KV, D1, R2, Durable Objects bindings
|
||||
- Configure custom domains and routes
|
||||
- View analytics and logs
|
||||
- Set environment variables and secrets
|
||||
|
||||
**Note:** Deployed Workers are production-ready but start on Free plan (100k requests/day).
|
||||
|
||||
## Browser Compatibility
|
||||
|
||||
| Browser | Status | Notes |
|
||||
|---------|--------|-------|
|
||||
| Chrome/Edge | ✅ Full support | Recommended |
|
||||
| Firefox | ✅ Full support | Works well |
|
||||
| Safari | ⚠️ Broken | Preview fails with "PreviewRequestFailed" |
|
||||
|
||||
**Safari users:** Use Chrome, Firefox, or Edge for Workers Playground.
|
||||
|
||||
## DevTools Integration
|
||||
|
||||
1. **Open preview** in browser tab
|
||||
2. **Right-click** → Inspect Element
|
||||
3. **Console tab** shows Worker logs:
|
||||
- `console.log()` output
|
||||
- Uncaught errors
|
||||
- Network requests (subrequests)
|
||||
|
||||
**Note:** DevTools show client-side console, not Worker execution logs. For production logging, use Logpush or Tail Workers.
|
||||
|
||||
## Limits in Playground
|
||||
|
||||
Same as production Free plan:
|
||||
|
||||
| Resource | Limit | Notes |
|
||||
|----------|-------|-------|
|
||||
| CPU time | 10ms | Per request |
|
||||
| Memory | 128 MB | Per request |
|
||||
| Script size | 1 MB | After compression |
|
||||
| Subrequests | 50 | Outbound fetch calls |
|
||||
| Request size | 100 MB | Incoming |
|
||||
| Response size | Unlimited | Outgoing (streamed) |
|
||||
|
||||
**Exceeding CPU time** throws error immediately. Optimize hot paths or upgrade to Paid plan (50ms CPU).
|
||||
@@ -0,0 +1,88 @@
|
||||
# Workers Playground Gotchas
|
||||
|
||||
## Platform Limitations
|
||||
|
||||
| Limitation | Impact | Workaround |
|
||||
|------------|--------|------------|
|
||||
| Safari broken | Preview fails | Use Chrome/Firefox/Edge |
|
||||
| TypeScript unsupported | TS syntax errors | Write plain JS or use JSDoc |
|
||||
| No bindings | `env` always `{}` | Mock data or use external APIs |
|
||||
| No env vars | Can't access secrets | Hardcode for testing |
|
||||
|
||||
## Common Runtime Errors
|
||||
|
||||
### "Response body already read"
|
||||
|
||||
```javascript
|
||||
// ❌ Body consumed twice
|
||||
const body = await request.text();
|
||||
await fetch(url, { body: request.body }); // Error!
|
||||
|
||||
// ✅ Clone first
|
||||
const clone = request.clone();
|
||||
const body = await request.text();
|
||||
await fetch(url, { body: clone.body });
|
||||
```
|
||||
|
||||
### "Worker exceeded CPU time"
|
||||
|
||||
**Limit:** 10ms (free), 50ms (paid)
|
||||
|
||||
```javascript
|
||||
// ✅ Move slow work to background
|
||||
ctx.waitUntil(fetch('https://analytics.example.com', {...}));
|
||||
return new Response('OK'); // Return immediately
|
||||
```
|
||||
|
||||
### "Too many subrequests"
|
||||
|
||||
**Limit:** 50 (free), 1000 (paid)
|
||||
|
||||
```javascript
|
||||
// ❌ 100 individual fetches
|
||||
// ✅ Batch into single API call
|
||||
await fetch('https://api.example.com/batch', {
|
||||
body: JSON.stringify({ ids: [...] })
|
||||
});
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
```javascript
|
||||
// Clone before caching
|
||||
await cache.put(request, response.clone());
|
||||
return response;
|
||||
|
||||
// Validate input early
|
||||
if (request.method !== 'POST') return new Response('', { status: 405 });
|
||||
|
||||
// Handle errors
|
||||
try { ... } catch (e) {
|
||||
return Response.json({ error: e.message }, { status: 500 });
|
||||
}
|
||||
```
|
||||
|
||||
## Limits
|
||||
|
||||
| Resource | Free | Paid |
|
||||
|----------|------|------|
|
||||
| CPU time | 10ms | 50ms |
|
||||
| Memory | 128 MB | 128 MB |
|
||||
| Subrequests | 50 | 1000 |
|
||||
|
||||
## Browser Support
|
||||
|
||||
| Browser | Status |
|
||||
|---------|--------|
|
||||
| Chrome | ✅ Recommended |
|
||||
| Firefox | ✅ Works |
|
||||
| Edge | ✅ Works |
|
||||
| Safari | ❌ Broken |
|
||||
|
||||
## Debugging
|
||||
|
||||
```javascript
|
||||
console.log('URL:', request.url); // View in browser DevTools Console
|
||||
```
|
||||
|
||||
**Note:** `console.log` works in playground. For production, use Logpush or Tail Workers.
|
||||
@@ -0,0 +1,132 @@
|
||||
# Workers Playground Patterns
|
||||
|
||||
## JSON API
|
||||
|
||||
```javascript
|
||||
export default {
|
||||
async fetch(request) {
|
||||
const url = new URL(request.url);
|
||||
if (url.pathname === '/api/hello') return Response.json({ message: 'Hello' });
|
||||
if (url.pathname === '/api/echo' && request.method === 'POST') {
|
||||
return Response.json({ received: await request.json() });
|
||||
}
|
||||
return Response.json({ error: 'Not found' }, { status: 404 });
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Router Pattern
|
||||
|
||||
```javascript
|
||||
const routes = {
|
||||
'/': () => new Response('Home'),
|
||||
'/api/users': () => Response.json([{ id: 1, name: 'Alice' }])
|
||||
};
|
||||
|
||||
export default {
|
||||
async fetch(request) {
|
||||
const handler = routes[new URL(request.url).pathname];
|
||||
return handler ? handler() : new Response('Not Found', { status: 404 });
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Proxy Pattern
|
||||
|
||||
```javascript
|
||||
export default {
|
||||
async fetch(request) {
|
||||
const url = new URL(request.url);
|
||||
url.hostname = 'api.example.com';
|
||||
return fetch(url.toString(), {
|
||||
method: request.method, headers: request.headers, body: request.body
|
||||
});
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## CORS Handling
|
||||
|
||||
```javascript
|
||||
export default {
|
||||
async fetch(request) {
|
||||
if (request.method === 'OPTIONS') {
|
||||
return new Response(null, {
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
|
||||
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
|
||||
}
|
||||
});
|
||||
}
|
||||
const response = await fetch('https://api.example.com', request);
|
||||
const modified = new Response(response.body, response);
|
||||
modified.headers.set('Access-Control-Allow-Origin', '*');
|
||||
return modified;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Caching
|
||||
|
||||
```javascript
|
||||
export default {
|
||||
async fetch(request) {
|
||||
if (request.method !== 'GET') return fetch(request);
|
||||
const cache = caches.default;
|
||||
let response = await cache.match(request);
|
||||
if (!response) {
|
||||
response = await fetch('https://api.example.com');
|
||||
if (response.status === 200) await cache.put(request, response.clone());
|
||||
}
|
||||
return response;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Hono Framework
|
||||
|
||||
```javascript
|
||||
import { Hono } from 'https://esm.sh/hono@3';
|
||||
const app = new Hono();
|
||||
app.get('/', (c) => c.text('Hello'));
|
||||
app.get('/api/users/:id', (c) => c.json({ id: c.req.param('id') }));
|
||||
app.notFound((c) => c.json({ error: 'Not found' }, 404));
|
||||
export default app;
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```javascript
|
||||
export default {
|
||||
async fetch(request) {
|
||||
const auth = request.headers.get('Authorization');
|
||||
if (!auth?.startsWith('Bearer ')) {
|
||||
return Response.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
const token = auth.substring(7);
|
||||
if (token !== 'secret-token') {
|
||||
return Response.json({ error: 'Invalid token' }, { status: 403 });
|
||||
}
|
||||
return Response.json({ message: 'Authenticated' });
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```javascript
|
||||
export default {
|
||||
async fetch(request) {
|
||||
try {
|
||||
const response = await fetch('https://api.example.com');
|
||||
if (!response.ok) throw new Error(`API returned ${response.status}`);
|
||||
return response;
|
||||
} catch (error) {
|
||||
return Response.json({ error: error.message }, { status: 500 });
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**Note:** In-memory state (Maps, variables) resets on Worker cold start. Use Durable Objects or KV for persistence.
|
||||
Reference in New Issue
Block a user