opencodekit 0.15.3 → 0.15.5
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/dist/index.js +1 -1
- package/dist/template/.opencode/AGENTS.md +209 -77
- package/dist/template/.opencode/agent/plan.md +4 -0
- package/dist/template/.opencode/command/cloudflare.md +70 -0
- package/dist/template/.opencode/package.json +1 -1
- package/dist/template/.opencode/skill/cloudflare/SKILL.md +233 -0
- package/dist/template/.opencode/skill/cloudflare/references/agents-sdk/README.md +35 -0
- package/dist/template/.opencode/skill/cloudflare/references/agents-sdk/api.md +100 -0
- package/dist/template/.opencode/skill/cloudflare/references/agents-sdk/configuration.md +99 -0
- package/dist/template/.opencode/skill/cloudflare/references/agents-sdk/gotchas.md +59 -0
- package/dist/template/.opencode/skill/cloudflare/references/agents-sdk/patterns.md +89 -0
- package/dist/template/.opencode/skill/cloudflare/references/ai-gateway/README.md +695 -0
- package/dist/template/.opencode/skill/cloudflare/references/ai-search/README.md +14 -0
- package/dist/template/.opencode/skill/cloudflare/references/ai-search/api.md +38 -0
- package/dist/template/.opencode/skill/cloudflare/references/ai-search/configuration.md +52 -0
- package/dist/template/.opencode/skill/cloudflare/references/ai-search/gotchas.md +41 -0
- package/dist/template/.opencode/skill/cloudflare/references/ai-search/patterns.md +45 -0
- package/dist/template/.opencode/skill/cloudflare/references/analytics-engine/README.md +14 -0
- package/dist/template/.opencode/skill/cloudflare/references/analytics-engine/api.md +27 -0
- package/dist/template/.opencode/skill/cloudflare/references/analytics-engine/configuration.md +45 -0
- package/dist/template/.opencode/skill/cloudflare/references/analytics-engine/gotchas.md +3 -0
- package/dist/template/.opencode/skill/cloudflare/references/analytics-engine/patterns.md +36 -0
- package/dist/template/.opencode/skill/cloudflare/references/api/README.md +21 -0
- package/dist/template/.opencode/skill/cloudflare/references/api/api.md +31 -0
- package/dist/template/.opencode/skill/cloudflare/references/api/configuration.md +20 -0
- package/dist/template/.opencode/skill/cloudflare/references/api/gotchas.md +28 -0
- package/dist/template/.opencode/skill/cloudflare/references/api/patterns.md +47 -0
- package/dist/template/.opencode/skill/cloudflare/references/api-shield/README.md +20 -0
- package/dist/template/.opencode/skill/cloudflare/references/api-shield/api.md +78 -0
- package/dist/template/.opencode/skill/cloudflare/references/api-shield/configuration.md +128 -0
- package/dist/template/.opencode/skill/cloudflare/references/api-shield/gotchas.md +51 -0
- package/dist/template/.opencode/skill/cloudflare/references/api-shield/patterns.md +145 -0
- package/dist/template/.opencode/skill/cloudflare/references/argo-smart-routing/README.md +16 -0
- package/dist/template/.opencode/skill/cloudflare/references/argo-smart-routing/api.md +50 -0
- package/dist/template/.opencode/skill/cloudflare/references/argo-smart-routing/configuration.md +53 -0
- package/dist/template/.opencode/skill/cloudflare/references/argo-smart-routing/gotchas.md +16 -0
- package/dist/template/.opencode/skill/cloudflare/references/argo-smart-routing/patterns.md +45 -0
- package/dist/template/.opencode/skill/cloudflare/references/bindings/README.md +14 -0
- package/dist/template/.opencode/skill/cloudflare/references/bindings/api.md +3 -0
- package/dist/template/.opencode/skill/cloudflare/references/bindings/configuration.md +58 -0
- package/dist/template/.opencode/skill/cloudflare/references/bindings/gotchas.md +35 -0
- package/dist/template/.opencode/skill/cloudflare/references/bindings/patterns.md +37 -0
- package/dist/template/.opencode/skill/cloudflare/references/bot-management/README.md +71 -0
- package/dist/template/.opencode/skill/cloudflare/references/bot-management/api.md +168 -0
- package/dist/template/.opencode/skill/cloudflare/references/bot-management/configuration.md +114 -0
- package/dist/template/.opencode/skill/cloudflare/references/bot-management/gotchas.md +99 -0
- package/dist/template/.opencode/skill/cloudflare/references/bot-management/patterns.md +125 -0
- package/dist/template/.opencode/skill/cloudflare/references/browser-rendering/README.md +16 -0
- package/dist/template/.opencode/skill/cloudflare/references/browser-rendering/api.md +54 -0
- package/dist/template/.opencode/skill/cloudflare/references/browser-rendering/configuration.md +47 -0
- package/dist/template/.opencode/skill/cloudflare/references/browser-rendering/gotchas.md +29 -0
- package/dist/template/.opencode/skill/cloudflare/references/browser-rendering/patterns.md +29 -0
- package/dist/template/.opencode/skill/cloudflare/references/c3/README.md +264 -0
- package/dist/template/.opencode/skill/cloudflare/references/cache-reserve/README.md +93 -0
- package/dist/template/.opencode/skill/cloudflare/references/cache-reserve/api.md +176 -0
- package/dist/template/.opencode/skill/cloudflare/references/cache-reserve/configuration.md +164 -0
- package/dist/template/.opencode/skill/cloudflare/references/cache-reserve/gotchas.md +203 -0
- package/dist/template/.opencode/skill/cloudflare/references/cache-reserve/patterns.md +180 -0
- package/dist/template/.opencode/skill/cloudflare/references/containers/README.md +16 -0
- package/dist/template/.opencode/skill/cloudflare/references/containers/api.md +43 -0
- package/dist/template/.opencode/skill/cloudflare/references/containers/configuration.md +56 -0
- package/dist/template/.opencode/skill/cloudflare/references/containers/gotchas.md +21 -0
- package/dist/template/.opencode/skill/cloudflare/references/containers/patterns.md +40 -0
- package/dist/template/.opencode/skill/cloudflare/references/cron-triggers/README.md +85 -0
- package/dist/template/.opencode/skill/cloudflare/references/cron-triggers/api.md +198 -0
- package/dist/template/.opencode/skill/cloudflare/references/cron-triggers/configuration.md +151 -0
- package/dist/template/.opencode/skill/cloudflare/references/cron-triggers/gotchas.md +129 -0
- package/dist/template/.opencode/skill/cloudflare/references/cron-triggers/patterns.md +122 -0
- package/dist/template/.opencode/skill/cloudflare/references/d1/README.md +92 -0
- package/dist/template/.opencode/skill/cloudflare/references/d1/api.md +141 -0
- package/dist/template/.opencode/skill/cloudflare/references/d1/configuration.md +127 -0
- package/dist/template/.opencode/skill/cloudflare/references/d1/gotchas.md +70 -0
- package/dist/template/.opencode/skill/cloudflare/references/d1/patterns.md +144 -0
- package/dist/template/.opencode/skill/cloudflare/references/ddos/README.md +34 -0
- package/dist/template/.opencode/skill/cloudflare/references/ddos/api.md +136 -0
- package/dist/template/.opencode/skill/cloudflare/references/ddos/configuration.md +67 -0
- package/dist/template/.opencode/skill/cloudflare/references/ddos/gotchas.md +114 -0
- package/dist/template/.opencode/skill/cloudflare/references/ddos/patterns.md +158 -0
- package/dist/template/.opencode/skill/cloudflare/references/do-storage/README.md +62 -0
- package/dist/template/.opencode/skill/cloudflare/references/do-storage/api.md +89 -0
- package/dist/template/.opencode/skill/cloudflare/references/do-storage/configuration.md +116 -0
- package/dist/template/.opencode/skill/cloudflare/references/do-storage/gotchas.md +93 -0
- package/dist/template/.opencode/skill/cloudflare/references/do-storage/patterns.md +112 -0
- package/dist/template/.opencode/skill/cloudflare/references/durable-objects/README.md +125 -0
- package/dist/template/.opencode/skill/cloudflare/references/durable-objects/api.md +152 -0
- package/dist/template/.opencode/skill/cloudflare/references/durable-objects/configuration.md +148 -0
- package/dist/template/.opencode/skill/cloudflare/references/durable-objects/gotchas.md +158 -0
- package/dist/template/.opencode/skill/cloudflare/references/durable-objects/patterns.md +255 -0
- package/dist/template/.opencode/skill/cloudflare/references/email-routing/README.md +18 -0
- package/dist/template/.opencode/skill/cloudflare/references/email-routing/api.md +46 -0
- package/dist/template/.opencode/skill/cloudflare/references/email-routing/configuration.md +63 -0
- package/dist/template/.opencode/skill/cloudflare/references/email-routing/gotchas.md +16 -0
- package/dist/template/.opencode/skill/cloudflare/references/email-routing/patterns.md +46 -0
- package/dist/template/.opencode/skill/cloudflare/references/email-workers/README.md +598 -0
- package/dist/template/.opencode/skill/cloudflare/references/hyperdrive/README.md +62 -0
- package/dist/template/.opencode/skill/cloudflare/references/hyperdrive/api.md +137 -0
- package/dist/template/.opencode/skill/cloudflare/references/hyperdrive/configuration.md +133 -0
- package/dist/template/.opencode/skill/cloudflare/references/hyperdrive/gotchas.md +184 -0
- package/dist/template/.opencode/skill/cloudflare/references/hyperdrive/patterns.md +176 -0
- package/dist/template/.opencode/skill/cloudflare/references/images/README.md +14 -0
- package/dist/template/.opencode/skill/cloudflare/references/images/api.md +3 -0
- package/dist/template/.opencode/skill/cloudflare/references/images/configuration.md +45 -0
- package/dist/template/.opencode/skill/cloudflare/references/images/gotchas.md +23 -0
- package/dist/template/.opencode/skill/cloudflare/references/images/patterns.md +31 -0
- package/dist/template/.opencode/skill/cloudflare/references/kv/README.md +60 -0
- package/dist/template/.opencode/skill/cloudflare/references/kv/api.md +114 -0
- package/dist/template/.opencode/skill/cloudflare/references/kv/configuration.md +92 -0
- package/dist/template/.opencode/skill/cloudflare/references/kv/gotchas.md +117 -0
- package/dist/template/.opencode/skill/cloudflare/references/kv/patterns.md +139 -0
- package/dist/template/.opencode/skill/cloudflare/references/miniflare/README.md +64 -0
- package/dist/template/.opencode/skill/cloudflare/references/miniflare/api.md +144 -0
- package/dist/template/.opencode/skill/cloudflare/references/miniflare/configuration.md +203 -0
- package/dist/template/.opencode/skill/cloudflare/references/miniflare/gotchas.md +187 -0
- package/dist/template/.opencode/skill/cloudflare/references/miniflare/patterns.md +211 -0
- package/dist/template/.opencode/skill/cloudflare/references/network-interconnect/README.md +60 -0
- package/dist/template/.opencode/skill/cloudflare/references/network-interconnect/api.md +240 -0
- package/dist/template/.opencode/skill/cloudflare/references/network-interconnect/configuration.md +127 -0
- package/dist/template/.opencode/skill/cloudflare/references/network-interconnect/gotchas.md +171 -0
- package/dist/template/.opencode/skill/cloudflare/references/network-interconnect/patterns.md +171 -0
- package/dist/template/.opencode/skill/cloudflare/references/observability/README.md +18 -0
- package/dist/template/.opencode/skill/cloudflare/references/observability/api.md +51 -0
- package/dist/template/.opencode/skill/cloudflare/references/observability/configuration.md +60 -0
- package/dist/template/.opencode/skill/cloudflare/references/observability/gotchas.md +36 -0
- package/dist/template/.opencode/skill/cloudflare/references/observability/patterns.md +42 -0
- package/dist/template/.opencode/skill/cloudflare/references/pages/README.md +76 -0
- package/dist/template/.opencode/skill/cloudflare/references/pages/api.md +200 -0
- package/dist/template/.opencode/skill/cloudflare/references/pages/configuration.md +228 -0
- package/dist/template/.opencode/skill/cloudflare/references/pages/gotchas.md +161 -0
- package/dist/template/.opencode/skill/cloudflare/references/pages/patterns.md +145 -0
- package/dist/template/.opencode/skill/cloudflare/references/pages-functions/README.md +57 -0
- package/dist/template/.opencode/skill/cloudflare/references/pages-functions/api.md +201 -0
- package/dist/template/.opencode/skill/cloudflare/references/pages-functions/configuration.md +159 -0
- package/dist/template/.opencode/skill/cloudflare/references/pages-functions/gotchas.md +151 -0
- package/dist/template/.opencode/skill/cloudflare/references/pages-functions/patterns.md +190 -0
- package/dist/template/.opencode/skill/cloudflare/references/pipelines/README.md +664 -0
- package/dist/template/.opencode/skill/cloudflare/references/pulumi/README.md +107 -0
- package/dist/template/.opencode/skill/cloudflare/references/pulumi/api.md +194 -0
- package/dist/template/.opencode/skill/cloudflare/references/pulumi/configuration.md +216 -0
- package/dist/template/.opencode/skill/cloudflare/references/pulumi/gotchas.md +223 -0
- package/dist/template/.opencode/skill/cloudflare/references/pulumi/patterns.md +139 -0
- package/dist/template/.opencode/skill/cloudflare/references/queues/README.md +69 -0
- package/dist/template/.opencode/skill/cloudflare/references/queues/api.md +138 -0
- package/dist/template/.opencode/skill/cloudflare/references/queues/configuration.md +125 -0
- package/dist/template/.opencode/skill/cloudflare/references/queues/gotchas.md +112 -0
- package/dist/template/.opencode/skill/cloudflare/references/queues/patterns.md +155 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2/README.md +61 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2/api.md +127 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2/configuration.md +76 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2/gotchas.md +94 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2/patterns.md +127 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2-data-catalog/README.md +18 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2-data-catalog/api.md +29 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2-data-catalog/configuration.md +39 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2-data-catalog/gotchas.md +20 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2-data-catalog/patterns.md +46 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2-sql/README.md +512 -0
- package/dist/template/.opencode/skill/cloudflare/references/realtime-sfu/README.md +21 -0
- package/dist/template/.opencode/skill/cloudflare/references/realtime-sfu/api.md +135 -0
- package/dist/template/.opencode/skill/cloudflare/references/realtime-sfu/configuration.md +63 -0
- package/dist/template/.opencode/skill/cloudflare/references/realtime-sfu/gotchas.md +75 -0
- package/dist/template/.opencode/skill/cloudflare/references/realtime-sfu/patterns.md +102 -0
- package/dist/template/.opencode/skill/cloudflare/references/realtimekit/README.md +81 -0
- package/dist/template/.opencode/skill/cloudflare/references/realtimekit/api.md +164 -0
- package/dist/template/.opencode/skill/cloudflare/references/realtimekit/configuration.md +147 -0
- package/dist/template/.opencode/skill/cloudflare/references/realtimekit/gotchas.md +172 -0
- package/dist/template/.opencode/skill/cloudflare/references/realtimekit/patterns.md +155 -0
- package/dist/template/.opencode/skill/cloudflare/references/sandbox/README.md +90 -0
- package/dist/template/.opencode/skill/cloudflare/references/sandbox/api.md +178 -0
- package/dist/template/.opencode/skill/cloudflare/references/sandbox/configuration.md +131 -0
- package/dist/template/.opencode/skill/cloudflare/references/sandbox/gotchas.md +156 -0
- package/dist/template/.opencode/skill/cloudflare/references/sandbox/patterns.md +203 -0
- package/dist/template/.opencode/skill/cloudflare/references/secrets-store/README.md +58 -0
- package/dist/template/.opencode/skill/cloudflare/references/secrets-store/api.md +182 -0
- package/dist/template/.opencode/skill/cloudflare/references/secrets-store/configuration.md +140 -0
- package/dist/template/.opencode/skill/cloudflare/references/secrets-store/gotchas.md +129 -0
- package/dist/template/.opencode/skill/cloudflare/references/secrets-store/patterns.md +218 -0
- package/dist/template/.opencode/skill/cloudflare/references/smart-placement/README.md +91 -0
- package/dist/template/.opencode/skill/cloudflare/references/smart-placement/api.md +139 -0
- package/dist/template/.opencode/skill/cloudflare/references/smart-placement/configuration.md +129 -0
- package/dist/template/.opencode/skill/cloudflare/references/smart-placement/gotchas.md +87 -0
- package/dist/template/.opencode/skill/cloudflare/references/smart-placement/patterns.md +135 -0
- package/dist/template/.opencode/skill/cloudflare/references/snippets/README.md +15 -0
- package/dist/template/.opencode/skill/cloudflare/references/snippets/api.md +47 -0
- package/dist/template/.opencode/skill/cloudflare/references/snippets/configuration.md +33 -0
- package/dist/template/.opencode/skill/cloudflare/references/snippets/gotchas.md +21 -0
- package/dist/template/.opencode/skill/cloudflare/references/snippets/patterns.md +34 -0
- package/dist/template/.opencode/skill/cloudflare/references/spectrum/README.md +16 -0
- package/dist/template/.opencode/skill/cloudflare/references/spectrum/api.md +24 -0
- package/dist/template/.opencode/skill/cloudflare/references/spectrum/configuration.md +43 -0
- package/dist/template/.opencode/skill/cloudflare/references/spectrum/gotchas.md +42 -0
- package/dist/template/.opencode/skill/cloudflare/references/spectrum/patterns.md +40 -0
- package/dist/template/.opencode/skill/cloudflare/references/static-assets/README.md +14 -0
- package/dist/template/.opencode/skill/cloudflare/references/static-assets/api.md +3 -0
- package/dist/template/.opencode/skill/cloudflare/references/static-assets/configuration.md +47 -0
- package/dist/template/.opencode/skill/cloudflare/references/static-assets/gotchas.md +44 -0
- package/dist/template/.opencode/skill/cloudflare/references/static-assets/patterns.md +42 -0
- package/dist/template/.opencode/skill/cloudflare/references/stream/README.md +103 -0
- package/dist/template/.opencode/skill/cloudflare/references/stream/api.md +204 -0
- package/dist/template/.opencode/skill/cloudflare/references/stream/configuration.md +127 -0
- package/dist/template/.opencode/skill/cloudflare/references/stream/gotchas.md +131 -0
- package/dist/template/.opencode/skill/cloudflare/references/stream/patterns.md +152 -0
- package/dist/template/.opencode/skill/cloudflare/references/tail-workers/README.md +640 -0
- package/dist/template/.opencode/skill/cloudflare/references/terraform/README.md +76 -0
- package/dist/template/.opencode/skill/cloudflare/references/terraform/api.md +159 -0
- package/dist/template/.opencode/skill/cloudflare/references/terraform/configuration.md +156 -0
- package/dist/template/.opencode/skill/cloudflare/references/terraform/gotchas.md +207 -0
- package/dist/template/.opencode/skill/cloudflare/references/terraform/patterns.md +135 -0
- package/dist/template/.opencode/skill/cloudflare/references/tunnel/README.md +82 -0
- package/dist/template/.opencode/skill/cloudflare/references/tunnel/api.md +105 -0
- package/dist/template/.opencode/skill/cloudflare/references/tunnel/configuration.md +113 -0
- package/dist/template/.opencode/skill/cloudflare/references/tunnel/gotchas.md +115 -0
- package/dist/template/.opencode/skill/cloudflare/references/tunnel/patterns.md +157 -0
- package/dist/template/.opencode/skill/cloudflare/references/turn/README.md +699 -0
- package/dist/template/.opencode/skill/cloudflare/references/turnstile/README.md +14 -0
- package/dist/template/.opencode/skill/cloudflare/references/turnstile/api.md +3 -0
- package/dist/template/.opencode/skill/cloudflare/references/turnstile/configuration.md +19 -0
- package/dist/template/.opencode/skill/cloudflare/references/turnstile/gotchas.md +27 -0
- package/dist/template/.opencode/skill/cloudflare/references/turnstile/patterns.md +41 -0
- package/dist/template/.opencode/skill/cloudflare/references/vectorize/README.md +682 -0
- package/dist/template/.opencode/skill/cloudflare/references/waf/README.md +14 -0
- package/dist/template/.opencode/skill/cloudflare/references/waf/api.md +3 -0
- package/dist/template/.opencode/skill/cloudflare/references/waf/configuration.md +44 -0
- package/dist/template/.opencode/skill/cloudflare/references/waf/gotchas.md +24 -0
- package/dist/template/.opencode/skill/cloudflare/references/waf/patterns.md +29 -0
- package/dist/template/.opencode/skill/cloudflare/references/web-analytics/README.md +19 -0
- package/dist/template/.opencode/skill/cloudflare/references/web-analytics/api.md +52 -0
- package/dist/template/.opencode/skill/cloudflare/references/web-analytics/configuration.md +31 -0
- package/dist/template/.opencode/skill/cloudflare/references/web-analytics/gotchas.md +28 -0
- package/dist/template/.opencode/skill/cloudflare/references/web-analytics/patterns.md +52 -0
- package/dist/template/.opencode/skill/cloudflare/references/workerd/README.md +47 -0
- package/dist/template/.opencode/skill/cloudflare/references/workerd/api.md +199 -0
- package/dist/template/.opencode/skill/cloudflare/references/workerd/configuration.md +185 -0
- package/dist/template/.opencode/skill/cloudflare/references/workerd/gotchas.md +203 -0
- package/dist/template/.opencode/skill/cloudflare/references/workerd/patterns.md +216 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers/README.md +96 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers/api.md +137 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers/configuration.md +147 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers/gotchas.md +99 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers/patterns.md +149 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-ai/README.md +116 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-for-platforms/README.md +48 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-for-platforms/api.md +169 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-for-platforms/configuration.md +136 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-for-platforms/gotchas.md +130 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-for-platforms/patterns.md +170 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-playground/README.md +16 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-playground/api.md +20 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-playground/configuration.md +3 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-playground/gotchas.md +35 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-playground/patterns.md +42 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-vpc/README.md +579 -0
- package/dist/template/.opencode/skill/cloudflare/references/workflows/README.md +62 -0
- package/dist/template/.opencode/skill/cloudflare/references/workflows/api.md +125 -0
- package/dist/template/.opencode/skill/cloudflare/references/workflows/configuration.md +177 -0
- package/dist/template/.opencode/skill/cloudflare/references/workflows/gotchas.md +136 -0
- package/dist/template/.opencode/skill/cloudflare/references/workflows/patterns.md +132 -0
- package/dist/template/.opencode/skill/cloudflare/references/wrangler/README.md +90 -0
- package/dist/template/.opencode/skill/cloudflare/references/wrangler/api.md +140 -0
- package/dist/template/.opencode/skill/cloudflare/references/wrangler/configuration.md +128 -0
- package/dist/template/.opencode/skill/cloudflare/references/wrangler/gotchas.md +93 -0
- package/dist/template/.opencode/skill/cloudflare/references/wrangler/patterns.md +150 -0
- package/dist/template/.opencode/skill/cloudflare/references/zaraz/README.md +360 -0
- package/dist/template/.opencode/skill/react-best-practices/AGENTS.md +2410 -0
- package/dist/template/.opencode/skill/react-best-practices/README.md +123 -0
- package/dist/template/.opencode/skill/react-best-practices/SKILL.md +125 -0
- package/dist/template/.opencode/skill/react-best-practices/metadata.json +15 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/_sections.md +46 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/_template.md +28 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/advanced-use-latest.md +49 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/async-api-routes.md +38 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/async-defer-await.md +80 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/async-dependencies.md +36 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/async-parallel.md +28 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/bundle-conditional.md +31 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/bundle-preload.md +50 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/client-event-listeners.md +74 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-cache-storage.md +70 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-early-exit.md +50 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-index-maps.md +37 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-length-check-first.md +49 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rendering-activity.md +26 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rerender-memo.md +44 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rerender-transitions.md +40 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/server-cache-lru.md +41 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/server-cache-react.md +76 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/server-serialization.md +38 -0
- package/dist/template/.opencode/skill/supabase/SKILL.md +120 -0
- package/dist/template/.opencode/skill/supabase/mcp.json +27 -0
- package/dist/template/.opencode/skill/vercel-deploy-claimable/SKILL.md +112 -0
- package/dist/template/.opencode/skill/vercel-deploy-claimable/scripts/deploy.sh +249 -0
- package/dist/template/.opencode/skill/web-design-guidelines/SKILL.md +39 -0
- package/package.json +1 -1
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
# Cloudflare Email Workers Skill
|
|
2
|
+
|
|
3
|
+
Expert guidance for building, configuring, and deploying Cloudflare Email Workers.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Email Workers let you programmatically process incoming emails using Cloudflare Workers runtime. Use them to build custom email routing logic, spam filters, auto-responders, ticket systems, notification handlers, and more.
|
|
8
|
+
|
|
9
|
+
## Core Architecture
|
|
10
|
+
|
|
11
|
+
### Event Handler (ES Modules)
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
export default {
|
|
15
|
+
async email(message, env, ctx) {
|
|
16
|
+
// Process email
|
|
17
|
+
await message.forward("destination@example.com");
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Event Handler (Service Worker - Deprecated)
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
addEventListener("email", async (event) => {
|
|
26
|
+
await event.message.forward("destination@example.com");
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Use ES modules format for all new projects.**
|
|
31
|
+
|
|
32
|
+
## ForwardableEmailMessage API
|
|
33
|
+
|
|
34
|
+
### Properties
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
interface ForwardableEmailMessage {
|
|
38
|
+
readonly from: string; // Envelope From
|
|
39
|
+
readonly to: string; // Envelope To
|
|
40
|
+
readonly headers: Headers; // Message headers
|
|
41
|
+
readonly raw: ReadableStream; // Raw message stream
|
|
42
|
+
readonly rawSize: number; // Message size in bytes
|
|
43
|
+
|
|
44
|
+
setReject(reason: string): void;
|
|
45
|
+
forward(rcptTo: string, headers?: Headers): Promise<void>;
|
|
46
|
+
reply(message: EmailMessage): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Key Methods
|
|
51
|
+
|
|
52
|
+
- **`setReject(reason)`**: Reject with permanent SMTP error
|
|
53
|
+
- **`forward(rcptTo, headers?)`**: Forward to verified destination (only `X-*` headers allowed)
|
|
54
|
+
- **`reply(message)`**: Reply to sender with new EmailMessage
|
|
55
|
+
|
|
56
|
+
### EmailMessage for Sending
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
interface EmailMessage {
|
|
60
|
+
readonly from: string;
|
|
61
|
+
readonly to: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Usage
|
|
65
|
+
import { EmailMessage } from "cloudflare:email";
|
|
66
|
+
const msg = new EmailMessage(from, to, rawMimeContent);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Common Patterns
|
|
70
|
+
|
|
71
|
+
### 1. Allowlist
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
export default {
|
|
75
|
+
async email(message, env, ctx) {
|
|
76
|
+
const allowList = ["friend@example.com", "coworker@example.com"];
|
|
77
|
+
if (!allowList.includes(message.from)) {
|
|
78
|
+
message.setReject("Address not allowed");
|
|
79
|
+
} else {
|
|
80
|
+
await message.forward("inbox@corp.example.com");
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 2. Blocklist
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
export default {
|
|
90
|
+
async email(message, env, ctx) {
|
|
91
|
+
const blockList = ["spam@example.com", "badactor@example.com"];
|
|
92
|
+
if (blockList.includes(message.from)) {
|
|
93
|
+
message.setReject("Blocked sender");
|
|
94
|
+
} else {
|
|
95
|
+
await message.forward("inbox@corp.example.com");
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 3. Parse Email with postal-mime
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import * as PostalMime from 'postal-mime';
|
|
105
|
+
|
|
106
|
+
export default {
|
|
107
|
+
async email(message, env, ctx) {
|
|
108
|
+
const parser = new PostalMime.default();
|
|
109
|
+
const rawEmail = new Response(message.raw);
|
|
110
|
+
const email = await parser.parse(await rawEmail.arrayBuffer());
|
|
111
|
+
|
|
112
|
+
// email contains: headers, from, to, subject, html, text, attachments
|
|
113
|
+
console.log(email.subject, email.from);
|
|
114
|
+
|
|
115
|
+
await message.forward("inbox@example.com");
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 4. Auto-Reply
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import { EmailMessage } from "cloudflare:email";
|
|
124
|
+
import { createMimeMessage } from 'mimetext';
|
|
125
|
+
|
|
126
|
+
export default {
|
|
127
|
+
async email(message, env, ctx) {
|
|
128
|
+
const msg = createMimeMessage();
|
|
129
|
+
msg.setSender({ name: 'Support Team', addr: 'support@example.com' });
|
|
130
|
+
msg.setRecipient(message.from);
|
|
131
|
+
msg.setHeader('In-Reply-To', message.headers.get('Message-ID'));
|
|
132
|
+
msg.setSubject('Re: Your inquiry');
|
|
133
|
+
msg.addMessage({
|
|
134
|
+
contentType: 'text/plain',
|
|
135
|
+
data: 'Thank you for contacting us. We will respond within 24 hours.',
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const replyMessage = new EmailMessage(
|
|
139
|
+
'support@example.com',
|
|
140
|
+
message.from,
|
|
141
|
+
msg.asRaw()
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
await message.reply(replyMessage);
|
|
145
|
+
await message.forward("team@example.com");
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### 5. Conditional Routing by Subject
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
export default {
|
|
154
|
+
async email(message, env, ctx) {
|
|
155
|
+
const subject = message.headers.get('Subject') || '';
|
|
156
|
+
|
|
157
|
+
if (subject.toLowerCase().includes('billing')) {
|
|
158
|
+
await message.forward("billing@example.com");
|
|
159
|
+
} else if (subject.toLowerCase().includes('support')) {
|
|
160
|
+
await message.forward("support@example.com");
|
|
161
|
+
} else {
|
|
162
|
+
await message.forward("general@example.com");
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 6. Store Email in KV/R2
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
import * as PostalMime from 'postal-mime';
|
|
172
|
+
|
|
173
|
+
export default {
|
|
174
|
+
async email(message, env, ctx) {
|
|
175
|
+
const parser = new PostalMime.default();
|
|
176
|
+
const rawEmail = new Response(message.raw);
|
|
177
|
+
const email = await parser.parse(await rawEmail.arrayBuffer());
|
|
178
|
+
|
|
179
|
+
// Store in KV
|
|
180
|
+
const key = `email:${Date.now()}:${message.from}`;
|
|
181
|
+
await env.EMAIL_ARCHIVE.put(key, JSON.stringify({
|
|
182
|
+
from: email.from,
|
|
183
|
+
subject: email.subject,
|
|
184
|
+
receivedAt: new Date().toISOString(),
|
|
185
|
+
}));
|
|
186
|
+
|
|
187
|
+
await message.forward("inbox@example.com");
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### 7. Webhook Notification
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
export default {
|
|
196
|
+
async email(message, env, ctx) {
|
|
197
|
+
const subject = message.headers.get('Subject');
|
|
198
|
+
|
|
199
|
+
// Notify via webhook
|
|
200
|
+
ctx.waitUntil(
|
|
201
|
+
fetch('https://hooks.slack.com/services/YOUR/WEBHOOK/URL', {
|
|
202
|
+
method: 'POST',
|
|
203
|
+
headers: { 'Content-Type': 'application/json' },
|
|
204
|
+
body: JSON.stringify({
|
|
205
|
+
text: `New email from ${message.from}: ${subject}`,
|
|
206
|
+
}),
|
|
207
|
+
})
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
await message.forward("inbox@example.com");
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### 8. Size-Based Filtering
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
export default {
|
|
219
|
+
async email(message, env, ctx) {
|
|
220
|
+
const MAX_SIZE = 10 * 1024 * 1024; // 10 MB
|
|
221
|
+
|
|
222
|
+
if (message.rawSize > MAX_SIZE) {
|
|
223
|
+
message.setReject("Message too large");
|
|
224
|
+
} else {
|
|
225
|
+
await message.forward("inbox@example.com");
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Wrangler Configuration
|
|
232
|
+
|
|
233
|
+
### Minimal Config (Local Dev)
|
|
234
|
+
|
|
235
|
+
```jsonc
|
|
236
|
+
// wrangler.jsonc
|
|
237
|
+
{
|
|
238
|
+
"send_email": [
|
|
239
|
+
{
|
|
240
|
+
"name": "EMAIL"
|
|
241
|
+
}
|
|
242
|
+
]
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
```toml
|
|
247
|
+
# wrangler.toml
|
|
248
|
+
[[send_email]]
|
|
249
|
+
name = "EMAIL"
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Full Production Config
|
|
253
|
+
|
|
254
|
+
```toml
|
|
255
|
+
name = "email-worker"
|
|
256
|
+
main = "src/index.ts"
|
|
257
|
+
compatibility_date = "2024-01-01"
|
|
258
|
+
|
|
259
|
+
# Email binding
|
|
260
|
+
[[send_email]]
|
|
261
|
+
name = "EMAIL"
|
|
262
|
+
|
|
263
|
+
# Add KV for email archival
|
|
264
|
+
[[kv_namespaces]]
|
|
265
|
+
binding = "EMAIL_ARCHIVE"
|
|
266
|
+
id = "your-kv-namespace-id"
|
|
267
|
+
|
|
268
|
+
# Add secrets for API keys
|
|
269
|
+
[vars]
|
|
270
|
+
WEBHOOK_URL = "https://example.com/webhook"
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Local Development
|
|
274
|
+
|
|
275
|
+
### Test Receiving Email
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
npx wrangler dev
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
curl --request POST 'http://localhost:8787/cdn-cgi/handler/email' \
|
|
283
|
+
--url-query 'from=sender@example.com' \
|
|
284
|
+
--url-query 'to=recipient@example.com' \
|
|
285
|
+
--header 'Content-Type: application/json' \
|
|
286
|
+
--data-raw 'From: sender@example.com
|
|
287
|
+
To: recipient@example.com
|
|
288
|
+
Subject: Test Email
|
|
289
|
+
|
|
290
|
+
Hello world'
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Test Sending Email
|
|
294
|
+
|
|
295
|
+
Wrangler writes sent emails to local `.eml` files:
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
import { EmailMessage } from "cloudflare:email";
|
|
299
|
+
import { createMimeMessage } from 'mimetext';
|
|
300
|
+
|
|
301
|
+
export default {
|
|
302
|
+
async fetch(request, env, ctx) {
|
|
303
|
+
const msg = createMimeMessage();
|
|
304
|
+
msg.setSender({ name: 'Test', addr: 'sender@example.com' });
|
|
305
|
+
msg.setRecipient('recipient@example.com');
|
|
306
|
+
msg.setSubject('Test from Worker');
|
|
307
|
+
msg.addMessage({
|
|
308
|
+
contentType: 'text/plain',
|
|
309
|
+
data: 'Hello from Email Worker',
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
const message = new EmailMessage(
|
|
313
|
+
'sender@example.com',
|
|
314
|
+
'recipient@example.com',
|
|
315
|
+
msg.asRaw()
|
|
316
|
+
);
|
|
317
|
+
await env.EMAIL.send(message);
|
|
318
|
+
|
|
319
|
+
return Response.json({ ok: true });
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Visit `http://localhost:8787/` to trigger. Check terminal for `.eml` file path.
|
|
325
|
+
|
|
326
|
+
## Deployment
|
|
327
|
+
|
|
328
|
+
### Prerequisites
|
|
329
|
+
|
|
330
|
+
1. Enable Email Routing in Cloudflare dashboard
|
|
331
|
+
2. Add verified destination address
|
|
332
|
+
3. Configure wrangler.toml
|
|
333
|
+
|
|
334
|
+
### Deploy
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
npx wrangler deploy
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Bind to Route
|
|
341
|
+
|
|
342
|
+
In Cloudflare dashboard:
|
|
343
|
+
1. Go to Email Routing → Email Workers
|
|
344
|
+
2. Create route (e.g., `hello@yourdomain.com`)
|
|
345
|
+
3. Bind route to your deployed Worker
|
|
346
|
+
|
|
347
|
+
## Limits
|
|
348
|
+
|
|
349
|
+
| Limit | Value |
|
|
350
|
+
|-------|-------|
|
|
351
|
+
| Max message size | 25 MiB |
|
|
352
|
+
| Max rules | 200 |
|
|
353
|
+
| Max destination addresses | 200 |
|
|
354
|
+
| Workers CPU (free tier) | Limited (upgrade for more) |
|
|
355
|
+
|
|
356
|
+
### CPU Limit Errors
|
|
357
|
+
|
|
358
|
+
Monitor with `wrangler tail`:
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
npx wrangler tail
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Look for `EXCEEDED_CPU` errors. Consider:
|
|
365
|
+
- Upgrading to Workers Paid plan
|
|
366
|
+
- Optimizing email parsing logic
|
|
367
|
+
- Using `ctx.waitUntil()` for non-critical operations
|
|
368
|
+
|
|
369
|
+
## Best Practices
|
|
370
|
+
|
|
371
|
+
### 1. Use Verified Destinations Only
|
|
372
|
+
|
|
373
|
+
`forward()` only works with verified destination addresses in your Cloudflare account.
|
|
374
|
+
|
|
375
|
+
### 2. Handle Large Emails
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
export default {
|
|
379
|
+
async email(message, env, ctx) {
|
|
380
|
+
if (message.rawSize > 20 * 1024 * 1024) {
|
|
381
|
+
// Don't parse huge emails synchronously
|
|
382
|
+
ctx.waitUntil(processLargeEmail(message, env));
|
|
383
|
+
await message.forward("inbox@example.com");
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Normal processing
|
|
388
|
+
},
|
|
389
|
+
};
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### 3. Use ctx.waitUntil for Async Operations
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
export default {
|
|
396
|
+
async email(message, env, ctx) {
|
|
397
|
+
// Forward immediately
|
|
398
|
+
await message.forward("inbox@example.com");
|
|
399
|
+
|
|
400
|
+
// Non-blocking operations
|
|
401
|
+
ctx.waitUntil(
|
|
402
|
+
Promise.all([
|
|
403
|
+
logToAnalytics(message),
|
|
404
|
+
notifySlack(message),
|
|
405
|
+
updateDatabase(message),
|
|
406
|
+
])
|
|
407
|
+
);
|
|
408
|
+
},
|
|
409
|
+
};
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### 4. Add Custom Headers When Forwarding
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
export default {
|
|
416
|
+
async email(message, env, ctx) {
|
|
417
|
+
const customHeaders = new Headers();
|
|
418
|
+
customHeaders.set('X-Processed-By', 'Email-Worker');
|
|
419
|
+
customHeaders.set('X-Original-To', message.to);
|
|
420
|
+
|
|
421
|
+
await message.forward("inbox@example.com", customHeaders);
|
|
422
|
+
},
|
|
423
|
+
};
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### 5. Parse Headers Safely
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
export default {
|
|
430
|
+
async email(message, env, ctx) {
|
|
431
|
+
const subject = message.headers.get('Subject') || '(no subject)';
|
|
432
|
+
const messageId = message.headers.get('Message-ID') || '';
|
|
433
|
+
|
|
434
|
+
// Avoid throwing on missing headers
|
|
435
|
+
},
|
|
436
|
+
};
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### 6. Type Safety
|
|
440
|
+
|
|
441
|
+
```typescript
|
|
442
|
+
interface Env {
|
|
443
|
+
EMAIL: SendEmail;
|
|
444
|
+
EMAIL_ARCHIVE: KVNamespace;
|
|
445
|
+
WEBHOOK_URL: string;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export default {
|
|
449
|
+
async email(message: ForwardableEmailMessage, env: Env, ctx: ExecutionContext) {
|
|
450
|
+
// Fully typed
|
|
451
|
+
},
|
|
452
|
+
};
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
## Common Use Cases
|
|
456
|
+
|
|
457
|
+
1. **Spam/Allowlist Filtering**: Block/allow senders
|
|
458
|
+
2. **Auto-Responders**: Reply with canned responses
|
|
459
|
+
3. **Ticket Creation**: Parse email, create support ticket
|
|
460
|
+
4. **Email Archival**: Store emails in KV/R2/D1
|
|
461
|
+
5. **Notification Routing**: Forward to Slack/Discord/webhooks
|
|
462
|
+
6. **Size Filtering**: Reject oversized attachments
|
|
463
|
+
7. **Domain Routing**: Route by sender domain
|
|
464
|
+
8. **Subject-Based Routing**: Route by keywords in subject
|
|
465
|
+
9. **Attachment Handling**: Extract and store attachments
|
|
466
|
+
10. **Email Analytics**: Track email metrics
|
|
467
|
+
|
|
468
|
+
## Dependencies
|
|
469
|
+
|
|
470
|
+
### Recommended npm Packages
|
|
471
|
+
|
|
472
|
+
```json
|
|
473
|
+
{
|
|
474
|
+
"dependencies": {
|
|
475
|
+
"postal-mime": "^2.3.3", // Parse incoming emails
|
|
476
|
+
"mimetext": "^4.0.0" // Compose outgoing emails
|
|
477
|
+
},
|
|
478
|
+
"devDependencies": {
|
|
479
|
+
"@cloudflare/workers-types": "^4.0.0",
|
|
480
|
+
"wrangler": "^3.0.0"
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
## Troubleshooting
|
|
486
|
+
|
|
487
|
+
### Email Not Forwarding
|
|
488
|
+
|
|
489
|
+
- Verify destination address in Cloudflare dashboard
|
|
490
|
+
- Check Email Routing is enabled
|
|
491
|
+
- Verify route binding in dashboard
|
|
492
|
+
- Check `wrangler tail` for errors
|
|
493
|
+
|
|
494
|
+
### CPU Limit Errors
|
|
495
|
+
|
|
496
|
+
- Upgrade to Workers Paid plan
|
|
497
|
+
- Use `ctx.waitUntil()` for heavy operations
|
|
498
|
+
- Avoid synchronous parsing of large emails
|
|
499
|
+
|
|
500
|
+
### Local Dev Not Working
|
|
501
|
+
|
|
502
|
+
- Ensure `send_email` binding in wrangler config
|
|
503
|
+
- Use correct curl format with `--data-raw`
|
|
504
|
+
- Check wrangler version (`npx wrangler --version`)
|
|
505
|
+
|
|
506
|
+
## Advanced Patterns
|
|
507
|
+
|
|
508
|
+
### Multi-Tenant Email Processing
|
|
509
|
+
|
|
510
|
+
```typescript
|
|
511
|
+
export default {
|
|
512
|
+
async email(message, env, ctx) {
|
|
513
|
+
const [localPart, domain] = message.to.split('@');
|
|
514
|
+
|
|
515
|
+
// Route based on subdomain or local part
|
|
516
|
+
const tenantId = extractTenantId(localPart);
|
|
517
|
+
const config = await env.TENANT_CONFIG.get(tenantId, 'json');
|
|
518
|
+
|
|
519
|
+
if (config?.forwardTo) {
|
|
520
|
+
await message.forward(config.forwardTo);
|
|
521
|
+
} else {
|
|
522
|
+
message.setReject("Unknown recipient");
|
|
523
|
+
}
|
|
524
|
+
},
|
|
525
|
+
};
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
### Attachment Extraction
|
|
529
|
+
|
|
530
|
+
```typescript
|
|
531
|
+
import * as PostalMime from 'postal-mime';
|
|
532
|
+
|
|
533
|
+
export default {
|
|
534
|
+
async email(message, env, ctx) {
|
|
535
|
+
const parser = new PostalMime.default();
|
|
536
|
+
const rawEmail = new Response(message.raw);
|
|
537
|
+
const email = await parser.parse(await rawEmail.arrayBuffer());
|
|
538
|
+
|
|
539
|
+
// Process attachments
|
|
540
|
+
for (const attachment of email.attachments) {
|
|
541
|
+
const key = `attachments/${Date.now()}-${attachment.filename}`;
|
|
542
|
+
ctx.waitUntil(
|
|
543
|
+
env.ATTACHMENTS.put(key, attachment.content, {
|
|
544
|
+
metadata: {
|
|
545
|
+
contentType: attachment.mimeType,
|
|
546
|
+
from: email.from.address,
|
|
547
|
+
},
|
|
548
|
+
})
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
await message.forward("inbox@example.com");
|
|
553
|
+
},
|
|
554
|
+
};
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### Conditional Auto-Reply with Rate Limiting
|
|
558
|
+
|
|
559
|
+
```typescript
|
|
560
|
+
import { EmailMessage } from "cloudflare:email";
|
|
561
|
+
import { createMimeMessage } from 'mimetext';
|
|
562
|
+
|
|
563
|
+
export default {
|
|
564
|
+
async email(message, env, ctx) {
|
|
565
|
+
const rateKey = `rate:${message.from}`;
|
|
566
|
+
const lastReply = await env.RATE_LIMIT.get(rateKey);
|
|
567
|
+
|
|
568
|
+
if (!lastReply) {
|
|
569
|
+
// Send auto-reply
|
|
570
|
+
const msg = createMimeMessage();
|
|
571
|
+
msg.setSender({ name: 'Auto Reply', addr: 'noreply@example.com' });
|
|
572
|
+
msg.setRecipient(message.from);
|
|
573
|
+
msg.setSubject('Received your message');
|
|
574
|
+
msg.addMessage({
|
|
575
|
+
contentType: 'text/plain',
|
|
576
|
+
data: 'Thank you for contacting us.',
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
const reply = new EmailMessage('noreply@example.com', message.from, msg.asRaw());
|
|
580
|
+
await message.reply(reply);
|
|
581
|
+
|
|
582
|
+
// Rate limit: 1 reply per hour
|
|
583
|
+
ctx.waitUntil(
|
|
584
|
+
env.RATE_LIMIT.put(rateKey, Date.now().toString(), { expirationTtl: 3600 })
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
await message.forward("inbox@example.com");
|
|
589
|
+
},
|
|
590
|
+
};
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
## Related Documentation
|
|
594
|
+
|
|
595
|
+
- [Email Routing Setup](https://developers.cloudflare.com/email-routing/get-started/enable-email-routing/)
|
|
596
|
+
- [Workers Platform](https://developers.cloudflare.com/workers/)
|
|
597
|
+
- [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/)
|
|
598
|
+
- [Workers Limits](https://developers.cloudflare.com/workers/platform/limits/)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Hyperdrive
|
|
2
|
+
|
|
3
|
+
Accelerates database queries from Workers via connection pooling, edge setup, query caching.
|
|
4
|
+
|
|
5
|
+
## Key Features
|
|
6
|
+
|
|
7
|
+
- **Connection Pooling**: Persistent connections eliminate TCP/TLS/auth handshakes (~7 round-trips)
|
|
8
|
+
- **Edge Setup**: Connection negotiation at edge, pooling near origin
|
|
9
|
+
- **Query Caching**: Auto-cache non-mutating queries (default 60s TTL)
|
|
10
|
+
- **Support**: PostgreSQL, MySQL + compatibles (CockroachDB, Timescale, PlanetScale, Neon, Supabase)
|
|
11
|
+
|
|
12
|
+
## Architecture
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
Worker → Edge (setup) → Pool (near DB) → Origin
|
|
16
|
+
↓ cached reads
|
|
17
|
+
Cache
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Create config
|
|
24
|
+
npx wrangler hyperdrive create my-db \
|
|
25
|
+
--connection-string="postgres://user:pass@host:5432/db"
|
|
26
|
+
|
|
27
|
+
# wrangler.jsonc
|
|
28
|
+
{
|
|
29
|
+
"compatibility_flags": ["nodejs_compat"],
|
|
30
|
+
"hyperdrive": [{"binding": "HYPERDRIVE", "id": "<ID>"}]
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { Client } from "pg";
|
|
36
|
+
|
|
37
|
+
export default {
|
|
38
|
+
async fetch(req: Request, env: Env): Promise<Response> {
|
|
39
|
+
const client = new Client({
|
|
40
|
+
connectionString: env.HYPERDRIVE.connectionString,
|
|
41
|
+
});
|
|
42
|
+
await client.connect();
|
|
43
|
+
const result = await client.query("SELECT * FROM users WHERE id = $1", [123]);
|
|
44
|
+
await client.end();
|
|
45
|
+
return Response.json(result.rows);
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## When to Use
|
|
51
|
+
|
|
52
|
+
✅ Global access to single-region DBs, high read ratios, popular queries, connection-heavy loads
|
|
53
|
+
❌ Write-heavy, real-time data (<1s), single-region apps close to DB
|
|
54
|
+
|
|
55
|
+
## See Also
|
|
56
|
+
|
|
57
|
+
- [configuration.md](./configuration.md) - Setup, wrangler config
|
|
58
|
+
- [api.md](./api.md) - Binding APIs, query patterns
|
|
59
|
+
- [patterns.md](./patterns.md) - Use cases, ORMs
|
|
60
|
+
- [gotchas.md](./gotchas.md) - Limits, troubleshooting
|
|
61
|
+
|
|
62
|
+
[Docs](https://developers.cloudflare.com/hyperdrive/) | [Discord #hyperdrive](https://discord.cloudflare.com)
|