pi-cloudflare 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +67 -0
- package/bin/setup.mjs +103 -0
- package/dist/auth-flow.d.ts +15 -0
- package/dist/auth-flow.d.ts.map +1 -0
- package/dist/auth-flow.js +90 -0
- package/dist/auth-flow.js.map +1 -0
- package/dist/client.d.ts +18 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +47 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +10 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +109 -0
- package/dist/index.js.map +1 -0
- package/dist/oauth.d.ts +44 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +135 -0
- package/dist/oauth.js.map +1 -0
- package/dist/proxy.d.ts +29 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +39 -0
- package/dist/proxy.js.map +1 -0
- package/dist/servers.d.ts +19 -0
- package/dist/servers.d.ts.map +1 -0
- package/dist/servers.js +40 -0
- package/dist/servers.js.map +1 -0
- package/dist/token-store.d.ts +23 -0
- package/dist/token-store.d.ts.map +1 -0
- package/dist/token-store.js +47 -0
- package/dist/token-store.js.map +1 -0
- package/package.json +67 -0
- package/skills/agents-sdk/SKILL.md +221 -0
- package/skills/agents-sdk/references/browse-the-web.md +63 -0
- package/skills/agents-sdk/references/callable.md +92 -0
- package/skills/agents-sdk/references/client-sdk.md +110 -0
- package/skills/agents-sdk/references/codemode.md +110 -0
- package/skills/agents-sdk/references/configuration.md +72 -0
- package/skills/agents-sdk/references/durable-execution.md +51 -0
- package/skills/agents-sdk/references/email.md +146 -0
- package/skills/agents-sdk/references/human-in-the-loop.md +67 -0
- package/skills/agents-sdk/references/mcp.md +188 -0
- package/skills/agents-sdk/references/observability.md +44 -0
- package/skills/agents-sdk/references/queue-retries.md +79 -0
- package/skills/agents-sdk/references/routing.md +75 -0
- package/skills/agents-sdk/references/server-driven-messages.md +63 -0
- package/skills/agents-sdk/references/state-scheduling.md +171 -0
- package/skills/agents-sdk/references/streaming-chat.md +198 -0
- package/skills/agents-sdk/references/think.md +112 -0
- package/skills/agents-sdk/references/voice.md +68 -0
- package/skills/agents-sdk/references/webhooks-push.md +86 -0
- package/skills/agents-sdk/references/workflows.md +132 -0
- package/skills/cloudflare/SKILL.md +248 -0
- package/skills/cloudflare/references/agents-sdk/README.md +89 -0
- package/skills/cloudflare/references/agents-sdk/api.md +190 -0
- package/skills/cloudflare/references/agents-sdk/configuration.md +182 -0
- package/skills/cloudflare/references/agents-sdk/gotchas.md +158 -0
- package/skills/cloudflare/references/agents-sdk/patterns.md +192 -0
- package/skills/cloudflare/references/ai-gateway/README.md +175 -0
- package/skills/cloudflare/references/ai-gateway/configuration.md +111 -0
- package/skills/cloudflare/references/ai-gateway/dynamic-routing.md +82 -0
- package/skills/cloudflare/references/ai-gateway/features.md +96 -0
- package/skills/cloudflare/references/ai-gateway/sdk-integration.md +114 -0
- package/skills/cloudflare/references/ai-gateway/troubleshooting.md +88 -0
- package/skills/cloudflare/references/ai-search/README.md +138 -0
- package/skills/cloudflare/references/ai-search/api.md +87 -0
- package/skills/cloudflare/references/ai-search/configuration.md +88 -0
- package/skills/cloudflare/references/ai-search/gotchas.md +81 -0
- package/skills/cloudflare/references/ai-search/patterns.md +85 -0
- package/skills/cloudflare/references/analytics-engine/README.md +94 -0
- package/skills/cloudflare/references/analytics-engine/api.md +112 -0
- package/skills/cloudflare/references/analytics-engine/configuration.md +112 -0
- package/skills/cloudflare/references/analytics-engine/gotchas.md +85 -0
- package/skills/cloudflare/references/analytics-engine/patterns.md +83 -0
- package/skills/cloudflare/references/api/README.md +66 -0
- package/skills/cloudflare/references/api/api.md +204 -0
- package/skills/cloudflare/references/api/configuration.md +160 -0
- package/skills/cloudflare/references/api/gotchas.md +225 -0
- package/skills/cloudflare/references/api/patterns.md +204 -0
- package/skills/cloudflare/references/api-shield/README.md +44 -0
- package/skills/cloudflare/references/api-shield/api.md +141 -0
- package/skills/cloudflare/references/api-shield/configuration.md +192 -0
- package/skills/cloudflare/references/api-shield/gotchas.md +125 -0
- package/skills/cloudflare/references/api-shield/patterns.md +180 -0
- package/skills/cloudflare/references/argo-smart-routing/README.md +90 -0
- package/skills/cloudflare/references/argo-smart-routing/api.md +240 -0
- package/skills/cloudflare/references/argo-smart-routing/configuration.md +197 -0
- package/skills/cloudflare/references/argo-smart-routing/gotchas.md +111 -0
- package/skills/cloudflare/references/argo-smart-routing/patterns.md +104 -0
- package/skills/cloudflare/references/artifacts/README.md +79 -0
- package/skills/cloudflare/references/artifacts/api.md +128 -0
- package/skills/cloudflare/references/artifacts/configuration.md +92 -0
- package/skills/cloudflare/references/bindings/README.md +122 -0
- package/skills/cloudflare/references/bindings/api.md +203 -0
- package/skills/cloudflare/references/bindings/configuration.md +188 -0
- package/skills/cloudflare/references/bindings/gotchas.md +208 -0
- package/skills/cloudflare/references/bindings/patterns.md +200 -0
- package/skills/cloudflare/references/bot-management/README.md +94 -0
- package/skills/cloudflare/references/bot-management/api.md +169 -0
- package/skills/cloudflare/references/bot-management/configuration.md +163 -0
- package/skills/cloudflare/references/bot-management/gotchas.md +114 -0
- package/skills/cloudflare/references/bot-management/patterns.md +182 -0
- package/skills/cloudflare/references/browser-rendering/README.md +78 -0
- package/skills/cloudflare/references/browser-rendering/api.md +108 -0
- package/skills/cloudflare/references/browser-rendering/configuration.md +78 -0
- package/skills/cloudflare/references/browser-rendering/gotchas.md +88 -0
- package/skills/cloudflare/references/browser-rendering/patterns.md +91 -0
- package/skills/cloudflare/references/c3/README.md +111 -0
- package/skills/cloudflare/references/c3/api.md +71 -0
- package/skills/cloudflare/references/c3/configuration.md +81 -0
- package/skills/cloudflare/references/c3/gotchas.md +92 -0
- package/skills/cloudflare/references/c3/patterns.md +82 -0
- package/skills/cloudflare/references/cache-reserve/README.md +147 -0
- package/skills/cloudflare/references/cache-reserve/api.md +194 -0
- package/skills/cloudflare/references/cache-reserve/configuration.md +169 -0
- package/skills/cloudflare/references/cache-reserve/gotchas.md +132 -0
- package/skills/cloudflare/references/cache-reserve/patterns.md +197 -0
- package/skills/cloudflare/references/containers/README.md +85 -0
- package/skills/cloudflare/references/containers/api.md +187 -0
- package/skills/cloudflare/references/containers/configuration.md +188 -0
- package/skills/cloudflare/references/containers/gotchas.md +178 -0
- package/skills/cloudflare/references/containers/patterns.md +202 -0
- package/skills/cloudflare/references/cron-triggers/README.md +99 -0
- package/skills/cloudflare/references/cron-triggers/api.md +196 -0
- package/skills/cloudflare/references/cron-triggers/configuration.md +180 -0
- package/skills/cloudflare/references/cron-triggers/gotchas.md +199 -0
- package/skills/cloudflare/references/cron-triggers/patterns.md +190 -0
- package/skills/cloudflare/references/d1/README.md +133 -0
- package/skills/cloudflare/references/d1/api.md +196 -0
- package/skills/cloudflare/references/d1/configuration.md +191 -0
- package/skills/cloudflare/references/d1/gotchas.md +98 -0
- package/skills/cloudflare/references/d1/patterns.md +189 -0
- package/skills/cloudflare/references/ddos/README.md +41 -0
- package/skills/cloudflare/references/ddos/api.md +164 -0
- package/skills/cloudflare/references/ddos/configuration.md +93 -0
- package/skills/cloudflare/references/ddos/gotchas.md +107 -0
- package/skills/cloudflare/references/ddos/patterns.md +174 -0
- package/skills/cloudflare/references/do-storage/README.md +75 -0
- package/skills/cloudflare/references/do-storage/api.md +102 -0
- package/skills/cloudflare/references/do-storage/configuration.md +112 -0
- package/skills/cloudflare/references/do-storage/gotchas.md +150 -0
- package/skills/cloudflare/references/do-storage/patterns.md +194 -0
- package/skills/cloudflare/references/do-storage/testing.md +183 -0
- package/skills/cloudflare/references/durable-objects/README.md +185 -0
- package/skills/cloudflare/references/durable-objects/api.md +187 -0
- package/skills/cloudflare/references/durable-objects/configuration.md +160 -0
- package/skills/cloudflare/references/durable-objects/gotchas.md +197 -0
- package/skills/cloudflare/references/durable-objects/patterns.md +201 -0
- package/skills/cloudflare/references/email-routing/README.md +89 -0
- package/skills/cloudflare/references/email-routing/api.md +195 -0
- package/skills/cloudflare/references/email-routing/configuration.md +186 -0
- package/skills/cloudflare/references/email-routing/gotchas.md +196 -0
- package/skills/cloudflare/references/email-routing/patterns.md +229 -0
- package/skills/cloudflare/references/email-workers/README.md +151 -0
- package/skills/cloudflare/references/email-workers/api.md +237 -0
- package/skills/cloudflare/references/email-workers/configuration.md +112 -0
- package/skills/cloudflare/references/email-workers/gotchas.md +125 -0
- package/skills/cloudflare/references/email-workers/patterns.md +102 -0
- package/skills/cloudflare/references/flagship/README.md +59 -0
- package/skills/cloudflare/references/flagship/api.md +390 -0
- package/skills/cloudflare/references/flagship/configuration.md +202 -0
- package/skills/cloudflare/references/flagship/gotchas.md +178 -0
- package/skills/cloudflare/references/flagship/patterns.md +469 -0
- package/skills/cloudflare/references/graphql-api/README.md +147 -0
- package/skills/cloudflare/references/graphql-api/api.md +175 -0
- package/skills/cloudflare/references/graphql-api/configuration.md +118 -0
- package/skills/cloudflare/references/graphql-api/gotchas.md +110 -0
- package/skills/cloudflare/references/graphql-api/patterns.md +225 -0
- package/skills/cloudflare/references/hyperdrive/README.md +82 -0
- package/skills/cloudflare/references/hyperdrive/api.md +143 -0
- package/skills/cloudflare/references/hyperdrive/configuration.md +159 -0
- package/skills/cloudflare/references/hyperdrive/gotchas.md +77 -0
- package/skills/cloudflare/references/hyperdrive/patterns.md +190 -0
- package/skills/cloudflare/references/images/README.md +61 -0
- package/skills/cloudflare/references/images/api.md +96 -0
- package/skills/cloudflare/references/images/configuration.md +211 -0
- package/skills/cloudflare/references/images/gotchas.md +99 -0
- package/skills/cloudflare/references/images/patterns.md +115 -0
- package/skills/cloudflare/references/kv/README.md +89 -0
- package/skills/cloudflare/references/kv/api.md +160 -0
- package/skills/cloudflare/references/kv/configuration.md +144 -0
- package/skills/cloudflare/references/kv/gotchas.md +131 -0
- package/skills/cloudflare/references/kv/patterns.md +196 -0
- package/skills/cloudflare/references/miniflare/README.md +105 -0
- package/skills/cloudflare/references/miniflare/api.md +187 -0
- package/skills/cloudflare/references/miniflare/configuration.md +173 -0
- package/skills/cloudflare/references/miniflare/gotchas.md +160 -0
- package/skills/cloudflare/references/miniflare/patterns.md +181 -0
- package/skills/cloudflare/references/network-interconnect/README.md +99 -0
- package/skills/cloudflare/references/network-interconnect/api.md +199 -0
- package/skills/cloudflare/references/network-interconnect/configuration.md +114 -0
- package/skills/cloudflare/references/network-interconnect/gotchas.md +165 -0
- package/skills/cloudflare/references/network-interconnect/patterns.md +166 -0
- package/skills/cloudflare/references/observability/README.md +88 -0
- package/skills/cloudflare/references/observability/api.md +164 -0
- package/skills/cloudflare/references/observability/configuration.md +169 -0
- package/skills/cloudflare/references/observability/gotchas.md +115 -0
- package/skills/cloudflare/references/observability/patterns.md +105 -0
- package/skills/cloudflare/references/pages/README.md +88 -0
- package/skills/cloudflare/references/pages/api.md +204 -0
- package/skills/cloudflare/references/pages/configuration.md +201 -0
- package/skills/cloudflare/references/pages/gotchas.md +203 -0
- package/skills/cloudflare/references/pages/patterns.md +204 -0
- package/skills/cloudflare/references/pages-functions/README.md +98 -0
- package/skills/cloudflare/references/pages-functions/api.md +143 -0
- package/skills/cloudflare/references/pages-functions/configuration.md +122 -0
- package/skills/cloudflare/references/pages-functions/gotchas.md +94 -0
- package/skills/cloudflare/references/pages-functions/patterns.md +137 -0
- package/skills/cloudflare/references/pipelines/README.md +90 -0
- package/skills/cloudflare/references/pipelines/api.md +124 -0
- package/skills/cloudflare/references/pipelines/configuration.md +155 -0
- package/skills/cloudflare/references/pipelines/gotchas.md +58 -0
- package/skills/cloudflare/references/pipelines/patterns.md +130 -0
- package/skills/cloudflare/references/pulumi/README.md +100 -0
- package/skills/cloudflare/references/pulumi/api.md +200 -0
- package/skills/cloudflare/references/pulumi/configuration.md +198 -0
- package/skills/cloudflare/references/pulumi/gotchas.md +181 -0
- package/skills/cloudflare/references/pulumi/patterns.md +191 -0
- package/skills/cloudflare/references/queues/README.md +96 -0
- package/skills/cloudflare/references/queues/api.md +206 -0
- package/skills/cloudflare/references/queues/configuration.md +144 -0
- package/skills/cloudflare/references/queues/gotchas.md +206 -0
- package/skills/cloudflare/references/queues/patterns.md +220 -0
- package/skills/cloudflare/references/r2/README.md +95 -0
- package/skills/cloudflare/references/r2/api.md +200 -0
- package/skills/cloudflare/references/r2/configuration.md +165 -0
- package/skills/cloudflare/references/r2/gotchas.md +190 -0
- package/skills/cloudflare/references/r2/patterns.md +193 -0
- package/skills/cloudflare/references/r2-data-catalog/README.md +75 -0
- package/skills/cloudflare/references/r2-data-catalog/api.md +122 -0
- package/skills/cloudflare/references/r2-data-catalog/configuration.md +98 -0
- package/skills/cloudflare/references/r2-data-catalog/gotchas.md +55 -0
- package/skills/cloudflare/references/r2-data-catalog/patterns.md +122 -0
- package/skills/cloudflare/references/r2-sql/README.md +64 -0
- package/skills/cloudflare/references/r2-sql/api.md +121 -0
- package/skills/cloudflare/references/r2-sql/configuration.md +50 -0
- package/skills/cloudflare/references/r2-sql/gotchas.md +39 -0
- package/skills/cloudflare/references/r2-sql/patterns.md +118 -0
- package/skills/cloudflare/references/realtime-sfu/README.md +65 -0
- package/skills/cloudflare/references/realtime-sfu/api.md +158 -0
- package/skills/cloudflare/references/realtime-sfu/configuration.md +137 -0
- package/skills/cloudflare/references/realtime-sfu/gotchas.md +133 -0
- package/skills/cloudflare/references/realtime-sfu/patterns.md +174 -0
- package/skills/cloudflare/references/realtimekit/README.md +113 -0
- package/skills/cloudflare/references/realtimekit/api.md +212 -0
- package/skills/cloudflare/references/realtimekit/configuration.md +203 -0
- package/skills/cloudflare/references/realtimekit/gotchas.md +169 -0
- package/skills/cloudflare/references/realtimekit/patterns.md +223 -0
- package/skills/cloudflare/references/sandbox/README.md +96 -0
- package/skills/cloudflare/references/sandbox/api.md +198 -0
- package/skills/cloudflare/references/sandbox/configuration.md +143 -0
- package/skills/cloudflare/references/sandbox/gotchas.md +194 -0
- package/skills/cloudflare/references/sandbox/patterns.md +201 -0
- package/skills/cloudflare/references/secrets-store/README.md +74 -0
- package/skills/cloudflare/references/secrets-store/api.md +200 -0
- package/skills/cloudflare/references/secrets-store/configuration.md +185 -0
- package/skills/cloudflare/references/secrets-store/gotchas.md +97 -0
- package/skills/cloudflare/references/secrets-store/patterns.md +207 -0
- package/skills/cloudflare/references/smart-placement/README.md +138 -0
- package/skills/cloudflare/references/smart-placement/api.md +183 -0
- package/skills/cloudflare/references/smart-placement/configuration.md +196 -0
- package/skills/cloudflare/references/smart-placement/gotchas.md +174 -0
- package/skills/cloudflare/references/smart-placement/patterns.md +183 -0
- package/skills/cloudflare/references/snippets/README.md +68 -0
- package/skills/cloudflare/references/snippets/api.md +198 -0
- package/skills/cloudflare/references/snippets/configuration.md +227 -0
- package/skills/cloudflare/references/snippets/gotchas.md +86 -0
- package/skills/cloudflare/references/snippets/patterns.md +135 -0
- package/skills/cloudflare/references/spectrum/README.md +52 -0
- package/skills/cloudflare/references/spectrum/api.md +181 -0
- package/skills/cloudflare/references/spectrum/configuration.md +194 -0
- package/skills/cloudflare/references/spectrum/gotchas.md +145 -0
- package/skills/cloudflare/references/spectrum/patterns.md +196 -0
- package/skills/cloudflare/references/static-assets/README.md +65 -0
- package/skills/cloudflare/references/static-assets/api.md +199 -0
- package/skills/cloudflare/references/static-assets/configuration.md +186 -0
- package/skills/cloudflare/references/static-assets/gotchas.md +162 -0
- package/skills/cloudflare/references/static-assets/patterns.md +189 -0
- package/skills/cloudflare/references/stream/README.md +114 -0
- package/skills/cloudflare/references/stream/api-live.md +195 -0
- package/skills/cloudflare/references/stream/api.md +199 -0
- package/skills/cloudflare/references/stream/configuration.md +141 -0
- package/skills/cloudflare/references/stream/gotchas.md +130 -0
- package/skills/cloudflare/references/stream/patterns.md +184 -0
- package/skills/cloudflare/references/tail-workers/README.md +89 -0
- package/skills/cloudflare/references/tail-workers/api.md +200 -0
- package/skills/cloudflare/references/tail-workers/configuration.md +176 -0
- package/skills/cloudflare/references/tail-workers/gotchas.md +192 -0
- package/skills/cloudflare/references/tail-workers/patterns.md +180 -0
- package/skills/cloudflare/references/terraform/README.md +102 -0
- package/skills/cloudflare/references/terraform/api.md +178 -0
- package/skills/cloudflare/references/terraform/configuration.md +197 -0
- package/skills/cloudflare/references/terraform/gotchas.md +150 -0
- package/skills/cloudflare/references/terraform/patterns.md +174 -0
- package/skills/cloudflare/references/tunnel/README.md +129 -0
- package/skills/cloudflare/references/tunnel/api.md +193 -0
- package/skills/cloudflare/references/tunnel/configuration.md +157 -0
- package/skills/cloudflare/references/tunnel/gotchas.md +147 -0
- package/skills/cloudflare/references/tunnel/networking.md +168 -0
- package/skills/cloudflare/references/tunnel/patterns.md +192 -0
- package/skills/cloudflare/references/turn/README.md +82 -0
- package/skills/cloudflare/references/turn/api.md +239 -0
- package/skills/cloudflare/references/turn/configuration.md +179 -0
- package/skills/cloudflare/references/turn/gotchas.md +231 -0
- package/skills/cloudflare/references/turn/patterns.md +213 -0
- package/skills/cloudflare/references/turnstile/README.md +99 -0
- package/skills/cloudflare/references/turnstile/api.md +240 -0
- package/skills/cloudflare/references/turnstile/configuration.md +222 -0
- package/skills/cloudflare/references/turnstile/gotchas.md +218 -0
- package/skills/cloudflare/references/turnstile/patterns.md +193 -0
- package/skills/cloudflare/references/vectorize/README.md +133 -0
- package/skills/cloudflare/references/vectorize/api.md +88 -0
- package/skills/cloudflare/references/vectorize/configuration.md +88 -0
- package/skills/cloudflare/references/vectorize/gotchas.md +76 -0
- package/skills/cloudflare/references/vectorize/patterns.md +90 -0
- package/skills/cloudflare/references/waf/README.md +113 -0
- package/skills/cloudflare/references/waf/api.md +202 -0
- package/skills/cloudflare/references/waf/configuration.md +203 -0
- package/skills/cloudflare/references/waf/gotchas.md +204 -0
- package/skills/cloudflare/references/waf/patterns.md +197 -0
- package/skills/cloudflare/references/web-analytics/README.md +141 -0
- package/skills/cloudflare/references/web-analytics/configuration.md +76 -0
- package/skills/cloudflare/references/web-analytics/gotchas.md +82 -0
- package/skills/cloudflare/references/web-analytics/integration.md +60 -0
- package/skills/cloudflare/references/web-analytics/patterns.md +91 -0
- package/skills/cloudflare/references/workerd/README.md +78 -0
- package/skills/cloudflare/references/workerd/api.md +185 -0
- package/skills/cloudflare/references/workerd/configuration.md +183 -0
- package/skills/cloudflare/references/workerd/gotchas.md +139 -0
- package/skills/cloudflare/references/workerd/patterns.md +192 -0
- package/skills/cloudflare/references/workers/README.md +108 -0
- package/skills/cloudflare/references/workers/api.md +195 -0
- package/skills/cloudflare/references/workers/configuration.md +185 -0
- package/skills/cloudflare/references/workers/frameworks.md +197 -0
- package/skills/cloudflare/references/workers/gotchas.md +137 -0
- package/skills/cloudflare/references/workers/patterns.md +198 -0
- package/skills/cloudflare/references/workers-ai/README.md +197 -0
- package/skills/cloudflare/references/workers-ai/api.md +112 -0
- package/skills/cloudflare/references/workers-ai/configuration.md +97 -0
- package/skills/cloudflare/references/workers-ai/gotchas.md +114 -0
- package/skills/cloudflare/references/workers-ai/patterns.md +120 -0
- package/skills/cloudflare/references/workers-for-platforms/README.md +89 -0
- package/skills/cloudflare/references/workers-for-platforms/api.md +196 -0
- package/skills/cloudflare/references/workers-for-platforms/configuration.md +167 -0
- package/skills/cloudflare/references/workers-for-platforms/gotchas.md +134 -0
- package/skills/cloudflare/references/workers-for-platforms/patterns.md +188 -0
- package/skills/cloudflare/references/workers-playground/README.md +127 -0
- package/skills/cloudflare/references/workers-playground/api.md +101 -0
- package/skills/cloudflare/references/workers-playground/configuration.md +163 -0
- package/skills/cloudflare/references/workers-playground/gotchas.md +88 -0
- package/skills/cloudflare/references/workers-playground/patterns.md +132 -0
- package/skills/cloudflare/references/workers-vpc/README.md +127 -0
- package/skills/cloudflare/references/workers-vpc/api.md +202 -0
- package/skills/cloudflare/references/workers-vpc/configuration.md +147 -0
- package/skills/cloudflare/references/workers-vpc/gotchas.md +167 -0
- package/skills/cloudflare/references/workers-vpc/patterns.md +209 -0
- package/skills/cloudflare/references/workflows/README.md +77 -0
- package/skills/cloudflare/references/workflows/api.md +218 -0
- package/skills/cloudflare/references/workflows/configuration.md +152 -0
- package/skills/cloudflare/references/workflows/gotchas.md +90 -0
- package/skills/cloudflare/references/workflows/patterns.md +175 -0
- package/skills/cloudflare/references/wrangler/README.md +135 -0
- package/skills/cloudflare/references/wrangler/api.md +188 -0
- package/skills/cloudflare/references/wrangler/configuration.md +197 -0
- package/skills/cloudflare/references/wrangler/gotchas.md +197 -0
- package/skills/cloudflare/references/wrangler/patterns.md +209 -0
- package/skills/cloudflare/references/zaraz/IMPLEMENTATION_SUMMARY.md +121 -0
- package/skills/cloudflare/references/zaraz/README.md +111 -0
- package/skills/cloudflare/references/zaraz/api.md +112 -0
- package/skills/cloudflare/references/zaraz/configuration.md +90 -0
- package/skills/cloudflare/references/zaraz/gotchas.md +81 -0
- package/skills/cloudflare/references/zaraz/patterns.md +74 -0
- package/skills/cloudflare-email-service/SKILL.md +103 -0
- package/skills/cloudflare-email-service/references/cli-and-mcp.md +125 -0
- package/skills/cloudflare-email-service/references/deliverability.md +285 -0
- package/skills/cloudflare-email-service/references/rest-api.md +184 -0
- package/skills/cloudflare-email-service/references/routing.md +199 -0
- package/skills/cloudflare-email-service/references/sending.md +252 -0
- package/skills/cloudflare-one/SKILL.md +176 -0
- package/skills/cloudflare-one-migrations/SKILL.md +110 -0
- package/skills/durable-objects/SKILL.md +186 -0
- package/skills/durable-objects/references/rules.md +295 -0
- package/skills/durable-objects/references/testing.md +264 -0
- package/skills/durable-objects/references/workers.md +346 -0
- package/skills/sandbox-migrate-to-next/SKILL.md +185 -0
- package/skills/sandbox-next/SKILL.md +91 -0
- package/skills/sandbox-next/references/api-quick-ref.md +94 -0
- package/skills/sandbox-next/references/examples.md +15 -0
- package/skills/sandbox-stable/SKILL.md +110 -0
- package/skills/turnstile-spin/README.md +51 -0
- package/skills/turnstile-spin/SKILL.md +330 -0
- package/skills/turnstile-spin/references/astro.md +199 -0
- package/skills/turnstile-spin/references/hugo.md +114 -0
- package/skills/turnstile-spin/references/nextjs-app.md +261 -0
- package/skills/turnstile-spin/references/nextjs-pages.md +82 -0
- package/skills/turnstile-spin/references/sveltekit.md +191 -0
- package/skills/turnstile-spin/references/vanilla-html.md +157 -0
- package/skills/turnstile-spin/scripts/auth-probe.sh +285 -0
- package/skills/turnstile-spin/scripts/persist-skill.sh +114 -0
- package/skills/turnstile-spin/scripts/validate.sh +137 -0
- package/skills/turnstile-spin/scripts/widget-create.sh +121 -0
- package/skills/turnstile-spin/tests/validation.md +62 -0
- package/skills/workers-best-practices/SKILL.md +127 -0
- package/skills/workers-best-practices/references/review.md +174 -0
- package/skills/workers-best-practices/references/rules.md +467 -0
- package/skills/wrangler/SKILL.md +922 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Astro
|
|
2
|
+
|
|
3
|
+
For Astro projects. The widget renders in a page; siteverify lives in an Astro Action, an API route, or a Pages Function. Astro frontmatter reads the sitekey from env at build time; the secret stays server-only.
|
|
4
|
+
|
|
5
|
+
```astro title="src/pages/signup.astro"
|
|
6
|
+
---
|
|
7
|
+
const SITEKEY = import.meta.env.PUBLIC_TURNSTILE_SITEKEY;
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
<html>
|
|
11
|
+
<head>
|
|
12
|
+
<script
|
|
13
|
+
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
|
|
14
|
+
async
|
|
15
|
+
defer
|
|
16
|
+
></script>
|
|
17
|
+
</head>
|
|
18
|
+
<body>
|
|
19
|
+
<form action="/api/signup" method="POST">
|
|
20
|
+
<input name="email" type="email" required />
|
|
21
|
+
<div
|
|
22
|
+
class="cf-turnstile"
|
|
23
|
+
data-sitekey={SITEKEY}
|
|
24
|
+
data-action="signup"
|
|
25
|
+
/>
|
|
26
|
+
<button type="submit">Sign up</button>
|
|
27
|
+
</form>
|
|
28
|
+
</body>
|
|
29
|
+
</html>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
In your `.env`:
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
PUBLIC_TURNSTILE_SITEKEY=YOUR_SITEKEY
|
|
36
|
+
TURNSTILE_SECRET=YOUR_SECRET
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The `PUBLIC_` prefix is mandatory for client-exposed variables in Astro. The secret has **no** prefix; it stays server-only.
|
|
40
|
+
|
|
41
|
+
## API route (canonical siteverify)
|
|
42
|
+
|
|
43
|
+
```ts title="src/pages/api/signup.ts"
|
|
44
|
+
import type { APIRoute } from "astro";
|
|
45
|
+
|
|
46
|
+
const expectedHostnames = new Set(
|
|
47
|
+
(import.meta.env.TURNSTILE_HOSTNAMES ?? "")
|
|
48
|
+
.split(",")
|
|
49
|
+
.map((h) => h.trim())
|
|
50
|
+
.filter(Boolean),
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
export const POST: APIRoute = async ({ request, clientAddress }) => {
|
|
54
|
+
const form = await request.formData();
|
|
55
|
+
const token = form.get("cf-turnstile-response");
|
|
56
|
+
if (typeof token !== "string" || expectedHostnames.size === 0) {
|
|
57
|
+
return new Response("forbidden", { status: 403 });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const verify = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
|
|
61
|
+
method: "POST",
|
|
62
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
63
|
+
body: new URLSearchParams({
|
|
64
|
+
secret: import.meta.env.TURNSTILE_SECRET,
|
|
65
|
+
response: token,
|
|
66
|
+
remoteip: clientAddress,
|
|
67
|
+
}),
|
|
68
|
+
});
|
|
69
|
+
const result = await verify.json();
|
|
70
|
+
if (
|
|
71
|
+
verify.ok !== true ||
|
|
72
|
+
result.success !== true ||
|
|
73
|
+
result.action !== "signup" ||
|
|
74
|
+
!expectedHostnames.has(result.hostname)
|
|
75
|
+
) {
|
|
76
|
+
return new Response("forbidden", { status: 403 });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// process signup
|
|
80
|
+
return Response.json({ ok: true });
|
|
81
|
+
};
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Variant: Astro Actions
|
|
85
|
+
|
|
86
|
+
If the project uses Astro Actions, call siteverify from the action:
|
|
87
|
+
|
|
88
|
+
```ts title="src/actions/index.ts"
|
|
89
|
+
import { defineAction } from "astro:actions";
|
|
90
|
+
import { z } from "astro:schema";
|
|
91
|
+
|
|
92
|
+
const expectedHostnames = new Set(
|
|
93
|
+
(import.meta.env.TURNSTILE_HOSTNAMES ?? "")
|
|
94
|
+
.split(",")
|
|
95
|
+
.map((h) => h.trim())
|
|
96
|
+
.filter(Boolean),
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
export const server = {
|
|
100
|
+
signup: defineAction({
|
|
101
|
+
accept: "form",
|
|
102
|
+
input: z.object({
|
|
103
|
+
email: z.string().email(),
|
|
104
|
+
"cf-turnstile-response": z.string(),
|
|
105
|
+
}),
|
|
106
|
+
handler: async (input, ctx) => {
|
|
107
|
+
if (expectedHostnames.size === 0) throw new Error("Verification failed");
|
|
108
|
+
const verify = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
|
|
109
|
+
method: "POST",
|
|
110
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
111
|
+
body: new URLSearchParams({
|
|
112
|
+
secret: import.meta.env.TURNSTILE_SECRET,
|
|
113
|
+
response: input["cf-turnstile-response"],
|
|
114
|
+
remoteip: ctx.clientAddress,
|
|
115
|
+
}),
|
|
116
|
+
});
|
|
117
|
+
const result = await verify.json();
|
|
118
|
+
if (
|
|
119
|
+
verify.ok !== true ||
|
|
120
|
+
result.success !== true ||
|
|
121
|
+
result.action !== "signup" ||
|
|
122
|
+
!expectedHostnames.has(result.hostname)
|
|
123
|
+
) {
|
|
124
|
+
throw new Error("Verification failed");
|
|
125
|
+
}
|
|
126
|
+
// process signup
|
|
127
|
+
},
|
|
128
|
+
}),
|
|
129
|
+
};
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`signup` is the stable action for this surface. Preserve an existing custom migration action and compare the returned action to the same value. Siteverify is mandatory for every widget mode, including pre-clearance. Set `TURNSTILE_HOSTNAMES` to the deployment-specific frontend hostnames; a production value must not include `localhost` or `127.0.0.1`.
|
|
133
|
+
|
|
134
|
+
For a client-side Astro Action, replace the native form and script with an explicit widget. Retain this surface's widget ID and reset it in `finally` after every same-page request completion:
|
|
135
|
+
|
|
136
|
+
```astro
|
|
137
|
+
<form id="signup-action-form">
|
|
138
|
+
<input name="email" type="email" required />
|
|
139
|
+
<div id="signup-action-turnstile" data-sitekey={SITEKEY}></div>
|
|
140
|
+
<button type="submit">Sign up</button>
|
|
141
|
+
</form>
|
|
142
|
+
<script>
|
|
143
|
+
import { actions } from "astro:actions";
|
|
144
|
+
|
|
145
|
+
type TurnstileApi = {
|
|
146
|
+
render: (
|
|
147
|
+
container: HTMLElement,
|
|
148
|
+
options: { sitekey: string; action: string },
|
|
149
|
+
) => string;
|
|
150
|
+
reset: (widgetId: string) => void;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const turnstileWindow = window as Window & { turnstile?: TurnstileApi };
|
|
154
|
+
const form = document.getElementById("signup-action-form") as HTMLFormElement;
|
|
155
|
+
const container = document.getElementById("signup-action-turnstile") as HTMLElement;
|
|
156
|
+
let signupActionWidgetId: string | undefined;
|
|
157
|
+
|
|
158
|
+
const renderWidget = () => {
|
|
159
|
+
if (!turnstileWindow.turnstile) return;
|
|
160
|
+
signupActionWidgetId = turnstileWindow.turnstile.render(container, {
|
|
161
|
+
sitekey: container.dataset.sitekey!,
|
|
162
|
+
action: "signup",
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
if (turnstileWindow.turnstile) {
|
|
167
|
+
renderWidget();
|
|
168
|
+
} else {
|
|
169
|
+
const script = document.createElement("script");
|
|
170
|
+
script.src =
|
|
171
|
+
"https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
|
|
172
|
+
script.async = true;
|
|
173
|
+
script.addEventListener("load", renderWidget, { once: true });
|
|
174
|
+
document.head.appendChild(script);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
form.addEventListener("submit", async (event) => {
|
|
178
|
+
event.preventDefault();
|
|
179
|
+
try {
|
|
180
|
+
const { error } = await actions.signup(new FormData(form));
|
|
181
|
+
if (error) throw error;
|
|
182
|
+
// proceed
|
|
183
|
+
} catch {
|
|
184
|
+
// surface the error
|
|
185
|
+
} finally {
|
|
186
|
+
if (signupActionWidgetId !== undefined) {
|
|
187
|
+
turnstileWindow.turnstile?.reset(signupActionWidgetId);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
</script>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Substitutions
|
|
195
|
+
|
|
196
|
+
| Placeholder | Replace with |
|
|
197
|
+
| ------------------- | -------------------------------------------------------------------- |
|
|
198
|
+
| `YOUR_SITEKEY` | The widget site key from Step 8 |
|
|
199
|
+
| `YOUR_SECRET` | The secret captured in Step 8. Stays in env, never inlined. |
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Hugo
|
|
2
|
+
|
|
3
|
+
For Hugo static sites. The widget renders on any page that includes the partial; siteverify happens at whatever backend handles your form submissions (a Cloudflare Pages Function, a Worker, an external API, or a form host with a server-side hook).
|
|
4
|
+
|
|
5
|
+
```html title="layouts/partials/turnstile.html"
|
|
6
|
+
<script
|
|
7
|
+
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
|
|
8
|
+
async
|
|
9
|
+
defer
|
|
10
|
+
></script>
|
|
11
|
+
|
|
12
|
+
<form action="{{ .Site.Params.turnstileFormEndpoint }}" method="POST">
|
|
13
|
+
<input name="email" type="email" required />
|
|
14
|
+
<div
|
|
15
|
+
class="cf-turnstile"
|
|
16
|
+
data-sitekey="{{ .Site.Params.turnstileSitekey }}"
|
|
17
|
+
data-action="subscribe"
|
|
18
|
+
></div>
|
|
19
|
+
<button type="submit">Subscribe</button>
|
|
20
|
+
</form>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Add the params to your site config:
|
|
24
|
+
|
|
25
|
+
```toml title="hugo.toml"
|
|
26
|
+
[params]
|
|
27
|
+
turnstileSitekey = "YOUR_SITEKEY"
|
|
28
|
+
turnstileFormEndpoint = "/api/subscribe" # path to your existing form handler
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Reference the partial from any layout or content file:
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
{{ partial "turnstile.html" . }}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Backend (where siteverify lives)
|
|
38
|
+
|
|
39
|
+
Hugo doesn't host server-side code, so the form endpoint must live elsewhere. Two common setups:
|
|
40
|
+
|
|
41
|
+
**Cloudflare Pages Function** (`functions/api/subscribe.js`):
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
export async function onRequestPost({ request, env }) {
|
|
45
|
+
const form = await request.formData();
|
|
46
|
+
const token = form.get("cf-turnstile-response");
|
|
47
|
+
|
|
48
|
+
const expectedHostnames = new Set(
|
|
49
|
+
(env.TURNSTILE_HOSTNAMES ?? "")
|
|
50
|
+
.split(",")
|
|
51
|
+
.map((h) => h.trim())
|
|
52
|
+
.filter(Boolean),
|
|
53
|
+
);
|
|
54
|
+
if (expectedHostnames.size === 0) {
|
|
55
|
+
return new Response("forbidden", { status: 403 });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const r = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
|
|
59
|
+
method: "POST",
|
|
60
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
61
|
+
body: new URLSearchParams({
|
|
62
|
+
secret: env.TURNSTILE_SECRET,
|
|
63
|
+
response: token,
|
|
64
|
+
remoteip: request.headers.get("CF-Connecting-IP"),
|
|
65
|
+
}),
|
|
66
|
+
});
|
|
67
|
+
const result = await r.json();
|
|
68
|
+
if (
|
|
69
|
+
r.ok !== true ||
|
|
70
|
+
result.success !== true ||
|
|
71
|
+
result.action !== "subscribe" ||
|
|
72
|
+
!expectedHostnames.has(result.hostname)
|
|
73
|
+
) {
|
|
74
|
+
return new Response("forbidden", { status: 403 });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// process subscribe
|
|
78
|
+
return new Response("ok");
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`subscribe` is the stable action for this surface. Preserve an existing custom migration action and compare the returned action to the same value. Siteverify is mandatory for every widget mode, including pre-clearance. Set `TURNSTILE_HOSTNAMES` to the deployment-specific frontend hostnames; a production value must not include `localhost` or `127.0.0.1`.
|
|
83
|
+
|
|
84
|
+
After the user approves a canonical absolute `WRANGLER_BIN` outside the project, set the secret with `(set +x; printf '%s' "$WIDGET_SECRET" | "$WRANGLER_BIN" pages secret put TURNSTILE_SECRET)` (or use the dashboard's Pages → your project → Settings → Environment variables → Add secret).
|
|
85
|
+
|
|
86
|
+
**External backend**: any Node/Ruby/Python/Go handler can do the same call. See the [vanilla-html reference](./vanilla-html.md) for non-Cloudflare-specific snippets.
|
|
87
|
+
|
|
88
|
+
## Variant: shortcode for content files
|
|
89
|
+
|
|
90
|
+
If you want to drop the widget into Markdown content (not just layouts), create a shortcode:
|
|
91
|
+
|
|
92
|
+
```html title="layouts/shortcodes/turnstile-form.html"
|
|
93
|
+
{{ partial "turnstile.html" . }}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Use in content:
|
|
97
|
+
|
|
98
|
+
```markdown title="content/contact.md"
|
|
99
|
+
---
|
|
100
|
+
title: Contact
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
Contact us:
|
|
104
|
+
|
|
105
|
+
{{< turnstile-form >}}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Substitutions
|
|
109
|
+
|
|
110
|
+
| Placeholder | Replace with |
|
|
111
|
+
| ------------------------ | -------------------------------------------------------------------- |
|
|
112
|
+
| `YOUR_SITEKEY` | The widget site key from Step 8 |
|
|
113
|
+
| `turnstileFormEndpoint` | The path or URL to your form handler (Pages Function, Worker, etc.) |
|
|
114
|
+
| `TURNSTILE_SECRET` | Env-var name in your backend. Value is the secret captured in Step 8.|
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# Next.js (App Router)
|
|
2
|
+
|
|
3
|
+
For `app/`-directory Next.js projects. The widget needs to run on the client, so the page or component must be `"use client"`. The siteverify call lives server-side, either in a Server Action or an API route.
|
|
4
|
+
|
|
5
|
+
```tsx title="app/signup/page.tsx"
|
|
6
|
+
"use client";
|
|
7
|
+
import Script from "next/script";
|
|
8
|
+
import { type FormEvent, useRef, useState } from "react";
|
|
9
|
+
|
|
10
|
+
type TurnstileWidgetId = string;
|
|
11
|
+
type TurnstileApi = {
|
|
12
|
+
render: (
|
|
13
|
+
container: HTMLElement,
|
|
14
|
+
options: {
|
|
15
|
+
sitekey: string;
|
|
16
|
+
action: string;
|
|
17
|
+
callback: (token: string) => void;
|
|
18
|
+
},
|
|
19
|
+
) => TurnstileWidgetId;
|
|
20
|
+
reset: (widgetId: TurnstileWidgetId) => void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
declare global {
|
|
24
|
+
interface Window {
|
|
25
|
+
turnstile: TurnstileApi;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default function SignupPage() {
|
|
30
|
+
const turnstileContainer = useRef<HTMLDivElement>(null);
|
|
31
|
+
const signupWidgetId = useRef<TurnstileWidgetId | null>(null);
|
|
32
|
+
const [token, setToken] = useState("");
|
|
33
|
+
|
|
34
|
+
function renderTurnstile() {
|
|
35
|
+
if (!turnstileContainer.current || signupWidgetId.current !== null) return;
|
|
36
|
+
signupWidgetId.current = window.turnstile.render(turnstileContainer.current, {
|
|
37
|
+
sitekey: "YOUR_SITEKEY",
|
|
38
|
+
action: "signup",
|
|
39
|
+
callback: setToken,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function handleSubmit(e: FormEvent<HTMLFormElement>) {
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
try {
|
|
46
|
+
const res = await fetch("/api/signup", {
|
|
47
|
+
method: "POST",
|
|
48
|
+
headers: { "Content-Type": "application/json" },
|
|
49
|
+
body: JSON.stringify({ token }),
|
|
50
|
+
});
|
|
51
|
+
const data = await res.json();
|
|
52
|
+
if (!res.ok || data.ok !== true) throw new Error("Submission failed");
|
|
53
|
+
// proceed
|
|
54
|
+
} catch {
|
|
55
|
+
// surface the error
|
|
56
|
+
} finally {
|
|
57
|
+
if (signupWidgetId.current !== null) {
|
|
58
|
+
window.turnstile.reset(signupWidgetId.current);
|
|
59
|
+
setToken("");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<>
|
|
66
|
+
<Script
|
|
67
|
+
src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"
|
|
68
|
+
strategy="afterInteractive"
|
|
69
|
+
onReady={renderTurnstile}
|
|
70
|
+
/>
|
|
71
|
+
<form onSubmit={handleSubmit}>
|
|
72
|
+
<input name="email" type="email" required />
|
|
73
|
+
<div ref={turnstileContainer} />
|
|
74
|
+
<button type="submit" disabled={!token}>
|
|
75
|
+
Sign up
|
|
76
|
+
</button>
|
|
77
|
+
</form>
|
|
78
|
+
</>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Explicit rendering returns the widget ID for this surface. The `finally` block resets that ID after network, JSON, validation, server, or successful same-page completion.
|
|
84
|
+
|
|
85
|
+
API route (canonical siteverify):
|
|
86
|
+
|
|
87
|
+
```ts title="app/api/signup/route.ts"
|
|
88
|
+
const expectedHostnames = new Set(
|
|
89
|
+
(process.env.TURNSTILE_HOSTNAMES ?? "")
|
|
90
|
+
.split(",")
|
|
91
|
+
.map((h) => h.trim())
|
|
92
|
+
.filter(Boolean),
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
export async function POST(req: Request) {
|
|
96
|
+
const { token } = await req.json();
|
|
97
|
+
const remoteip = req.headers.get("x-forwarded-for") ?? undefined;
|
|
98
|
+
|
|
99
|
+
if (expectedHostnames.size === 0) {
|
|
100
|
+
return new Response("forbidden", { status: 403 });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const r = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
|
|
104
|
+
method: "POST",
|
|
105
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
106
|
+
body: new URLSearchParams({
|
|
107
|
+
secret: process.env.TURNSTILE_SECRET!,
|
|
108
|
+
response: token,
|
|
109
|
+
...(remoteip ? { remoteip } : {}),
|
|
110
|
+
}),
|
|
111
|
+
});
|
|
112
|
+
const result = await r.json();
|
|
113
|
+
if (
|
|
114
|
+
r.ok !== true ||
|
|
115
|
+
result.success !== true ||
|
|
116
|
+
result.action !== "signup" ||
|
|
117
|
+
!expectedHostnames.has(result.hostname)
|
|
118
|
+
) {
|
|
119
|
+
return new Response("forbidden", { status: 403 });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// existing signup logic runs here
|
|
123
|
+
return Response.json({ ok: true });
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`signup` is the stable action for this surface. Preserve an existing custom migration action and compare the returned action to the same value. Siteverify is mandatory for every widget mode, including pre-clearance. Set `TURNSTILE_HOSTNAMES` to the deployment-specific frontend hostnames; a production value must not include `localhost` or `127.0.0.1`.
|
|
128
|
+
|
|
129
|
+
## Variant: Server Action
|
|
130
|
+
|
|
131
|
+
If you are using Server Actions, do the siteverify call from the action itself. The widget still goes in a client component, but the verify call moves server-side:
|
|
132
|
+
|
|
133
|
+
```tsx title="app/signup/actions.ts"
|
|
134
|
+
"use server";
|
|
135
|
+
import { headers } from "next/headers";
|
|
136
|
+
|
|
137
|
+
export type SignupState = { ok?: true; error?: string } | null;
|
|
138
|
+
|
|
139
|
+
const expectedHostnames = new Set(
|
|
140
|
+
(process.env.TURNSTILE_HOSTNAMES ?? "")
|
|
141
|
+
.split(",")
|
|
142
|
+
.map((h) => h.trim())
|
|
143
|
+
.filter(Boolean),
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
export async function submitSignup(
|
|
147
|
+
_previousState: SignupState,
|
|
148
|
+
formData: FormData,
|
|
149
|
+
): Promise<SignupState> {
|
|
150
|
+
const token = formData.get("cf-turnstile-response");
|
|
151
|
+
if (typeof token !== "string") return { error: "Verification failed" };
|
|
152
|
+
if (expectedHostnames.size === 0) return { error: "Verification failed" };
|
|
153
|
+
const remoteip = (await headers()).get("x-forwarded-for") ?? undefined;
|
|
154
|
+
|
|
155
|
+
const r = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
|
|
156
|
+
method: "POST",
|
|
157
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
158
|
+
body: new URLSearchParams({
|
|
159
|
+
secret: process.env.TURNSTILE_SECRET!,
|
|
160
|
+
response: token,
|
|
161
|
+
...(remoteip ? { remoteip } : {}),
|
|
162
|
+
}),
|
|
163
|
+
});
|
|
164
|
+
const result = await r.json();
|
|
165
|
+
if (
|
|
166
|
+
r.ok !== true ||
|
|
167
|
+
result.success !== true ||
|
|
168
|
+
result.action !== "signup" ||
|
|
169
|
+
!expectedHostnames.has(result.hostname)
|
|
170
|
+
) {
|
|
171
|
+
return { error: "Verification failed" };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// process signup
|
|
175
|
+
return { ok: true };
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```tsx title="app/signup/page.tsx (server-action variant)"
|
|
180
|
+
"use client";
|
|
181
|
+
import Script from "next/script";
|
|
182
|
+
import { useActionState, useRef, useState } from "react";
|
|
183
|
+
import { submitSignup, type SignupState } from "./actions";
|
|
184
|
+
|
|
185
|
+
type TurnstileWidgetId = string;
|
|
186
|
+
type TurnstileApi = {
|
|
187
|
+
render: (
|
|
188
|
+
container: HTMLElement,
|
|
189
|
+
options: {
|
|
190
|
+
sitekey: string;
|
|
191
|
+
action: string;
|
|
192
|
+
callback: (token: string) => void;
|
|
193
|
+
},
|
|
194
|
+
) => TurnstileWidgetId;
|
|
195
|
+
reset: (widgetId: TurnstileWidgetId) => void;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
declare global {
|
|
199
|
+
interface Window {
|
|
200
|
+
turnstile: TurnstileApi;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export default function SignupPage() {
|
|
205
|
+
const turnstileContainer = useRef<HTMLDivElement>(null);
|
|
206
|
+
const signupActionWidgetId = useRef<TurnstileWidgetId | null>(null);
|
|
207
|
+
const [token, setToken] = useState("");
|
|
208
|
+
const [state, action, pending] = useActionState(
|
|
209
|
+
async (previousState: SignupState, formData: FormData) => {
|
|
210
|
+
try {
|
|
211
|
+
return await submitSignup(previousState, formData);
|
|
212
|
+
} finally {
|
|
213
|
+
if (signupActionWidgetId.current !== null) {
|
|
214
|
+
window.turnstile.reset(signupActionWidgetId.current);
|
|
215
|
+
setToken("");
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
null,
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
function renderTurnstile() {
|
|
223
|
+
if (!turnstileContainer.current || signupActionWidgetId.current !== null) return;
|
|
224
|
+
signupActionWidgetId.current = window.turnstile.render(
|
|
225
|
+
turnstileContainer.current,
|
|
226
|
+
{
|
|
227
|
+
sitekey: "YOUR_SITEKEY",
|
|
228
|
+
action: "signup",
|
|
229
|
+
callback: setToken,
|
|
230
|
+
},
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return (
|
|
235
|
+
<>
|
|
236
|
+
<Script
|
|
237
|
+
src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"
|
|
238
|
+
strategy="afterInteractive"
|
|
239
|
+
onReady={renderTurnstile}
|
|
240
|
+
/>
|
|
241
|
+
<form action={action}>
|
|
242
|
+
<input name="email" type="email" required />
|
|
243
|
+
<div ref={turnstileContainer} />
|
|
244
|
+
{state?.error && <p role="alert">{state.error}</p>}
|
|
245
|
+
<button type="submit" disabled={!token || pending}>
|
|
246
|
+
Sign up
|
|
247
|
+
</button>
|
|
248
|
+
</form>
|
|
249
|
+
</>
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Server Actions can return state without navigating. This wrapper accepts `previousState` before `FormData` and resets the retained widget ID in `finally`, including validation, server, and network failures.
|
|
255
|
+
|
|
256
|
+
## Substitutions
|
|
257
|
+
|
|
258
|
+
| Placeholder | Replace with |
|
|
259
|
+
| ------------------- | -------------------------------------------------------------------- |
|
|
260
|
+
| `YOUR_SITEKEY` | The widget site key from Step 8 |
|
|
261
|
+
| `TURNSTILE_SECRET` | Env-var name. Value is the secret captured in Step 8, kept off disk. |
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Next.js (Pages Router)
|
|
2
|
+
|
|
3
|
+
For older Next.js projects using `pages/` rather than `app/`. The widget renders client-side; siteverify lives in the API route.
|
|
4
|
+
|
|
5
|
+
```tsx title="pages/signup.tsx"
|
|
6
|
+
import Script from "next/script";
|
|
7
|
+
|
|
8
|
+
export default function SignupPage() {
|
|
9
|
+
return (
|
|
10
|
+
<>
|
|
11
|
+
<Script src="https://challenges.cloudflare.com/turnstile/v0/api.js" />
|
|
12
|
+
<form action="/api/signup" method="POST">
|
|
13
|
+
<input name="email" type="email" required />
|
|
14
|
+
<div
|
|
15
|
+
className="cf-turnstile"
|
|
16
|
+
data-sitekey="YOUR_SITEKEY"
|
|
17
|
+
data-action="signup"
|
|
18
|
+
/>
|
|
19
|
+
<button type="submit">Sign up</button>
|
|
20
|
+
</form>
|
|
21
|
+
</>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
This native form navigates to the API response, so it does not need client-side reset code.
|
|
27
|
+
|
|
28
|
+
API route (canonical siteverify):
|
|
29
|
+
|
|
30
|
+
```ts title="pages/api/signup.ts"
|
|
31
|
+
import type { NextApiRequest, NextApiResponse } from "next";
|
|
32
|
+
|
|
33
|
+
const expectedHostnames = new Set(
|
|
34
|
+
(process.env.TURNSTILE_HOSTNAMES ?? "")
|
|
35
|
+
.split(",")
|
|
36
|
+
.map((h) => h.trim())
|
|
37
|
+
.filter(Boolean),
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
export default async function handler(
|
|
41
|
+
req: NextApiRequest,
|
|
42
|
+
res: NextApiResponse,
|
|
43
|
+
) {
|
|
44
|
+
const token = req.body["cf-turnstile-response"] ?? req.body.token;
|
|
45
|
+
if (expectedHostnames.size === 0) {
|
|
46
|
+
return res.status(403).json({ error: "Verification failed" });
|
|
47
|
+
}
|
|
48
|
+
const remoteip =
|
|
49
|
+
(req.headers["x-forwarded-for"] as string | undefined)?.split(",")[0] ??
|
|
50
|
+
req.socket.remoteAddress;
|
|
51
|
+
|
|
52
|
+
const verify = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
|
|
53
|
+
method: "POST",
|
|
54
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
55
|
+
body: new URLSearchParams({
|
|
56
|
+
secret: process.env.TURNSTILE_SECRET!,
|
|
57
|
+
response: token,
|
|
58
|
+
...(remoteip ? { remoteip } : {}),
|
|
59
|
+
}),
|
|
60
|
+
});
|
|
61
|
+
const result = await verify.json();
|
|
62
|
+
if (
|
|
63
|
+
verify.ok !== true ||
|
|
64
|
+
result.success !== true ||
|
|
65
|
+
result.action !== "signup" ||
|
|
66
|
+
!expectedHostnames.has(result.hostname)
|
|
67
|
+
) {
|
|
68
|
+
return res.status(403).json({ error: "Verification failed" });
|
|
69
|
+
}
|
|
70
|
+
// process signup
|
|
71
|
+
return res.json({ ok: true });
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`signup` is the stable action for this surface. Preserve an existing custom migration action and compare the returned action to the same value. Siteverify is mandatory for every widget mode, including pre-clearance. Set `TURNSTILE_HOSTNAMES` to the deployment-specific frontend hostnames; a production value must not include `localhost` or `127.0.0.1`.
|
|
76
|
+
|
|
77
|
+
## Substitutions
|
|
78
|
+
|
|
79
|
+
| Placeholder | Replace with |
|
|
80
|
+
| ------------------- | -------------------------------------------------------------------- |
|
|
81
|
+
| `YOUR_SITEKEY` | The widget site key from Step 8 |
|
|
82
|
+
| `TURNSTILE_SECRET` | Env-var name. Value is the secret captured in Step 8, kept off disk. |
|