mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-03-21 18:11:27 -07:00
update skills
This commit is contained in:
200
.agents/skills/cloudflare-deploy/references/tail-workers/api.md
Normal file
200
.agents/skills/cloudflare-deploy/references/tail-workers/api.md
Normal file
@@ -0,0 +1,200 @@
|
||||
# Tail Workers API Reference
|
||||
|
||||
## Handler Signature
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async tail(
|
||||
events: TraceItem[],
|
||||
env: Env,
|
||||
ctx: ExecutionContext
|
||||
): Promise<void> {
|
||||
// Process events
|
||||
}
|
||||
} satisfies ExportedHandler<Env>;
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `events`: Array of `TraceItem` objects (one per producer invocation)
|
||||
- `env`: Bindings (KV, D1, R2, env vars, etc.)
|
||||
- `ctx`: Context with `waitUntil()` for async work
|
||||
|
||||
**CRITICAL:** Tail handlers don't return values. Use `ctx.waitUntil()` for async operations.
|
||||
|
||||
## TraceItem Type
|
||||
|
||||
```typescript
|
||||
interface TraceItem {
|
||||
scriptName: string; // Producer Worker name
|
||||
eventTimestamp: number; // Epoch milliseconds
|
||||
outcome: 'ok' | 'exception' | 'exceededCpu' | 'exceededMemory'
|
||||
| 'canceled' | 'scriptNotFound' | 'responseStreamDisconnected' | 'unknown';
|
||||
|
||||
event?: {
|
||||
request?: {
|
||||
url: string; // Redacted by default
|
||||
method: string;
|
||||
headers: Record<string, string>; // Sensitive headers redacted
|
||||
cf?: IncomingRequestCfProperties;
|
||||
getUnredacted(): TraceRequest; // Bypass redaction (use carefully)
|
||||
};
|
||||
response?: {
|
||||
status: number;
|
||||
};
|
||||
};
|
||||
|
||||
logs: Array<{
|
||||
timestamp: number; // Epoch milliseconds
|
||||
level: 'debug' | 'info' | 'log' | 'warn' | 'error';
|
||||
message: unknown[]; // Args passed to console function
|
||||
}>;
|
||||
|
||||
exceptions: Array<{
|
||||
timestamp: number; // Epoch milliseconds
|
||||
name: string; // Error type (Error, TypeError, etc.)
|
||||
message: string; // Error description
|
||||
}>;
|
||||
|
||||
diagnosticsChannelEvents: Array<{
|
||||
channel: string;
|
||||
message: unknown;
|
||||
timestamp: number; // Epoch milliseconds
|
||||
}>;
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** Official SDK uses `TraceItem`, not `TailItem`. Use `@cloudflare/workers-types` for accurate types.
|
||||
|
||||
## Timestamp Handling
|
||||
|
||||
All timestamps are **epoch milliseconds**, not seconds:
|
||||
|
||||
```typescript
|
||||
// ✅ CORRECT - use directly with Date
|
||||
const date = new Date(event.eventTimestamp);
|
||||
|
||||
// ❌ WRONG - don't multiply by 1000
|
||||
const date = new Date(event.eventTimestamp * 1000);
|
||||
```
|
||||
|
||||
## Automatic Redaction
|
||||
|
||||
By default, sensitive data is redacted from `TraceRequest`:
|
||||
|
||||
### Header Redaction
|
||||
|
||||
Headers containing these substrings (case-insensitive):
|
||||
- `auth`, `key`, `secret`, `token`, `jwt`
|
||||
- `cookie`, `set-cookie`
|
||||
|
||||
Redacted values show as `"REDACTED"`.
|
||||
|
||||
### URL Redaction
|
||||
|
||||
- **Hex IDs:** 32+ hex digits → `"REDACTED"`
|
||||
- **Base-64 IDs:** 21+ chars with 2+ upper, 2+ lower, 2+ digits → `"REDACTED"`
|
||||
|
||||
## Bypassing Redaction
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async tail(events, env, ctx) {
|
||||
for (const event of events) {
|
||||
// ⚠️ Use with extreme caution
|
||||
const unredacted = event.event?.request?.getUnredacted();
|
||||
// unredacted.url and unredacted.headers contain raw values
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**Best practices:**
|
||||
- Only call `getUnredacted()` when absolutely necessary
|
||||
- Never log unredacted sensitive data
|
||||
- Implement additional filtering before external transmission
|
||||
- Use environment variables for API keys, never hardcode
|
||||
|
||||
## Type-Safe Handler
|
||||
|
||||
```typescript
|
||||
interface Env {
|
||||
LOGS_KV: KVNamespace;
|
||||
ANALYTICS: AnalyticsEngineDataset;
|
||||
LOG_ENDPOINT: string;
|
||||
API_TOKEN: string;
|
||||
}
|
||||
|
||||
export default {
|
||||
async tail(
|
||||
events: TraceItem[],
|
||||
env: Env,
|
||||
ctx: ExecutionContext
|
||||
): Promise<void> {
|
||||
const payload = events.map(event => ({
|
||||
script: event.scriptName,
|
||||
timestamp: event.eventTimestamp,
|
||||
outcome: event.outcome,
|
||||
url: event.event?.request?.url,
|
||||
status: event.event?.response?.status,
|
||||
}));
|
||||
|
||||
ctx.waitUntil(
|
||||
fetch(env.LOG_ENDPOINT, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
);
|
||||
}
|
||||
} satisfies ExportedHandler<Env>;
|
||||
```
|
||||
|
||||
## Outcome vs HTTP Status
|
||||
|
||||
**IMPORTANT:** `outcome` is script execution status, NOT HTTP status.
|
||||
|
||||
- Worker returns 500 → `outcome='ok'` if script completed successfully
|
||||
- Uncaught exception → `outcome='exception'` regardless of HTTP status
|
||||
- CPU limit exceeded → `outcome='exceededCpu'`
|
||||
|
||||
```typescript
|
||||
// ✅ Check outcome for script execution status
|
||||
if (event.outcome === 'exception') {
|
||||
// Script threw uncaught exception
|
||||
}
|
||||
|
||||
// ✅ Check HTTP status separately
|
||||
if (event.event?.response?.status === 500) {
|
||||
// HTTP 500 returned (script may have handled error)
|
||||
}
|
||||
```
|
||||
|
||||
## Serialization Considerations
|
||||
|
||||
`log.message` is `unknown[]` and may contain non-serializable objects:
|
||||
|
||||
```typescript
|
||||
// ❌ May fail with circular references or BigInt
|
||||
JSON.stringify(events);
|
||||
|
||||
// ✅ Safe serialization
|
||||
const safePayload = events.map(event => ({
|
||||
...event,
|
||||
logs: event.logs.map(log => ({
|
||||
...log,
|
||||
message: log.message.map(m => {
|
||||
try {
|
||||
return JSON.parse(JSON.stringify(m));
|
||||
} catch {
|
||||
return String(m);
|
||||
}
|
||||
})
|
||||
}))
|
||||
}));
|
||||
```
|
||||
|
||||
**Common serialization issues:**
|
||||
- Circular references in logged objects
|
||||
- `BigInt` values (not JSON-serializable)
|
||||
- Functions or symbols in console.log arguments
|
||||
- Large objects exceeding body size limits
|
||||
Reference in New Issue
Block a user