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,193 @@
|
|
|
1
|
+
# R2 Patterns & Best Practices
|
|
2
|
+
|
|
3
|
+
## Streaming Large Files
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
const object = await env.MY_BUCKET.get(key);
|
|
7
|
+
if (!object) return new Response('Not found', { status: 404 });
|
|
8
|
+
|
|
9
|
+
const headers = new Headers();
|
|
10
|
+
object.writeHttpMetadata(headers);
|
|
11
|
+
headers.set('etag', object.httpEtag);
|
|
12
|
+
|
|
13
|
+
return new Response(object.body, { headers });
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Conditional GET (304 Not Modified)
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
const ifNoneMatch = request.headers.get('if-none-match');
|
|
20
|
+
const object = await env.MY_BUCKET.get(key, {
|
|
21
|
+
onlyIf: { etagDoesNotMatch: ifNoneMatch?.replace(/"/g, '') || '' }
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (!object) return new Response('Not found', { status: 404 });
|
|
25
|
+
if (!object.body) return new Response(null, { status: 304, headers: { 'etag': object.httpEtag } });
|
|
26
|
+
|
|
27
|
+
return new Response(object.body, { headers: { 'etag': object.httpEtag } });
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Upload with Validation
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
const key = url.pathname.slice(1);
|
|
34
|
+
if (!key || key.includes('..')) return new Response('Invalid key', { status: 400 });
|
|
35
|
+
|
|
36
|
+
const object = await env.MY_BUCKET.put(key, request.body, {
|
|
37
|
+
httpMetadata: { contentType: request.headers.get('content-type') || 'application/octet-stream' },
|
|
38
|
+
customMetadata: { uploadedAt: new Date().toISOString(), ip: request.headers.get('cf-connecting-ip') || 'unknown' }
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return Response.json({ key: object.key, size: object.size, etag: object.httpEtag });
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Multipart with Progress
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
const PART_SIZE = 5 * 1024 * 1024; // 5MB
|
|
48
|
+
const partCount = Math.ceil(file.size / PART_SIZE);
|
|
49
|
+
const multipart = await env.MY_BUCKET.createMultipartUpload(key, { httpMetadata: { contentType: file.type } });
|
|
50
|
+
|
|
51
|
+
const uploadedParts: R2UploadedPart[] = [];
|
|
52
|
+
try {
|
|
53
|
+
for (let i = 0; i < partCount; i++) {
|
|
54
|
+
const start = i * PART_SIZE;
|
|
55
|
+
const part = await multipart.uploadPart(i + 1, file.slice(start, start + PART_SIZE));
|
|
56
|
+
uploadedParts.push(part);
|
|
57
|
+
onProgress?.(Math.round(((i + 1) / partCount) * 100));
|
|
58
|
+
}
|
|
59
|
+
return await multipart.complete(uploadedParts);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
await multipart.abort();
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Batch Delete
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
async function deletePrefix(prefix: string, env: Env) {
|
|
70
|
+
let cursor: string | undefined;
|
|
71
|
+
let truncated = true;
|
|
72
|
+
|
|
73
|
+
while (truncated) {
|
|
74
|
+
const listed = await env.MY_BUCKET.list({ prefix, limit: 1000, cursor });
|
|
75
|
+
if (listed.objects.length > 0) {
|
|
76
|
+
await env.MY_BUCKET.delete(listed.objects.map(o => o.key));
|
|
77
|
+
}
|
|
78
|
+
truncated = listed.truncated;
|
|
79
|
+
cursor = listed.cursor;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Checksum Validation & Storage Transitions
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// Upload with checksum
|
|
88
|
+
const hash = await crypto.subtle.digest('SHA-256', data);
|
|
89
|
+
await env.MY_BUCKET.put(key, data, { sha256: hash });
|
|
90
|
+
|
|
91
|
+
// Transition storage class (requires S3 SDK)
|
|
92
|
+
import { S3Client, CopyObjectCommand } from '@aws-sdk/client-s3';
|
|
93
|
+
await s3.send(new CopyObjectCommand({
|
|
94
|
+
Bucket: 'my-bucket', Key: key,
|
|
95
|
+
CopySource: `/my-bucket/${key}`,
|
|
96
|
+
StorageClass: 'STANDARD_IA'
|
|
97
|
+
}));
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Client-Side Uploads (Presigned URLs)
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { S3Client } from '@aws-sdk/client-s3';
|
|
104
|
+
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
105
|
+
import { PutObjectCommand } from '@aws-sdk/client-s3';
|
|
106
|
+
|
|
107
|
+
// Worker: Generate presigned upload URL
|
|
108
|
+
const s3 = new S3Client({
|
|
109
|
+
region: 'auto',
|
|
110
|
+
endpoint: `https://${env.ACCOUNT_ID}.r2.cloudflarestorage.com`,
|
|
111
|
+
credentials: { accessKeyId: env.R2_ACCESS_KEY_ID, secretAccessKey: env.R2_SECRET_ACCESS_KEY }
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const url = await getSignedUrl(s3, new PutObjectCommand({ Bucket: 'my-bucket', Key: key }), { expiresIn: 3600 });
|
|
115
|
+
return Response.json({ uploadUrl: url });
|
|
116
|
+
|
|
117
|
+
// Client: Upload directly
|
|
118
|
+
const { uploadUrl } = await fetch('/api/upload-url').then(r => r.json());
|
|
119
|
+
await fetch(uploadUrl, { method: 'PUT', body: file });
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Caching with Cache API
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
export default {
|
|
126
|
+
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
|
|
127
|
+
const cache = caches.default;
|
|
128
|
+
const url = new URL(request.url);
|
|
129
|
+
const cacheKey = new Request(url.toString(), request);
|
|
130
|
+
|
|
131
|
+
// Check cache first
|
|
132
|
+
let response = await cache.match(cacheKey);
|
|
133
|
+
if (response) return response;
|
|
134
|
+
|
|
135
|
+
// Fetch from R2
|
|
136
|
+
const key = url.pathname.slice(1);
|
|
137
|
+
const object = await env.MY_BUCKET.get(key);
|
|
138
|
+
if (!object) return new Response('Not found', { status: 404 });
|
|
139
|
+
|
|
140
|
+
const headers = new Headers();
|
|
141
|
+
object.writeHttpMetadata(headers);
|
|
142
|
+
headers.set('etag', object.httpEtag);
|
|
143
|
+
headers.set('cache-control', 'public, max-age=31536000, immutable');
|
|
144
|
+
|
|
145
|
+
response = new Response(object.body, { headers });
|
|
146
|
+
|
|
147
|
+
// Cache for subsequent requests
|
|
148
|
+
ctx.waitUntil(cache.put(cacheKey, response.clone()));
|
|
149
|
+
|
|
150
|
+
return response;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Public Bucket with Custom Domain
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
export default {
|
|
159
|
+
async fetch(request: Request, env: Env): Promise<Response> {
|
|
160
|
+
// CORS preflight
|
|
161
|
+
if (request.method === 'OPTIONS') {
|
|
162
|
+
return new Response(null, {
|
|
163
|
+
headers: {
|
|
164
|
+
'access-control-allow-origin': '*',
|
|
165
|
+
'access-control-allow-methods': 'GET, HEAD',
|
|
166
|
+
'access-control-max-age': '86400'
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const key = new URL(request.url).pathname.slice(1);
|
|
172
|
+
if (!key) return Response.redirect('/index.html', 302);
|
|
173
|
+
|
|
174
|
+
const object = await env.MY_BUCKET.get(key);
|
|
175
|
+
if (!object) return new Response('Not found', { status: 404 });
|
|
176
|
+
|
|
177
|
+
const headers = new Headers();
|
|
178
|
+
object.writeHttpMetadata(headers);
|
|
179
|
+
headers.set('etag', object.httpEtag);
|
|
180
|
+
headers.set('access-control-allow-origin', '*');
|
|
181
|
+
headers.set('cache-control', 'public, max-age=31536000, immutable');
|
|
182
|
+
|
|
183
|
+
return new Response(object.body, { headers });
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## r2.dev Public URLs
|
|
189
|
+
|
|
190
|
+
Enable r2.dev in dashboard for simple public access: `https://pub-${hashId}.r2.dev/${key}`
|
|
191
|
+
Or add custom domain via dashboard: `https://files.example.com/${key}`
|
|
192
|
+
|
|
193
|
+
**Limitations:** No auth, bucket-level CORS, no cache override.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Cloudflare R2 Data Catalog
|
|
2
|
+
|
|
3
|
+
Managed Apache Iceberg REST catalog built into R2 buckets. No catalog servers to run.
|
|
4
|
+
|
|
5
|
+
## Documentation
|
|
6
|
+
|
|
7
|
+
This reference is a fast-start with verified connection details and code. For limits, maintenance settings, engine config examples, and pricing, **retrieve the live docs** — use the Cloudflare MCP `docs` tool if available, otherwise `webfetch` the URL. Docs are source of truth over this file.
|
|
8
|
+
|
|
9
|
+
| Topic | URL |
|
|
10
|
+
|-------|-----|
|
|
11
|
+
| Overview / get started | `https://developers.cloudflare.com/r2/data-catalog/get-started/` |
|
|
12
|
+
| Manage catalogs (enable, tokens) | `https://developers.cloudflare.com/r2/data-catalog/manage-catalogs/` |
|
|
13
|
+
| Engine config examples | `https://developers.cloudflare.com/r2/data-catalog/config-examples/` (`pyiceberg/`, `spark-python/`, `spark-scala/`, `duckdb/`, `snowflake/`, `trino/`, `starrocks/`) |
|
|
14
|
+
| Table maintenance (compaction, snapshots) | `https://developers.cloudflare.com/r2/data-catalog/table-maintenance/` |
|
|
15
|
+
| Deleting data | `https://developers.cloudflare.com/r2/data-catalog/deleting-data/` |
|
|
16
|
+
| Metrics (GraphQL) | `https://developers.cloudflare.com/r2/data-catalog/observability/metrics/` |
|
|
17
|
+
| Pricing | `https://developers.cloudflare.com/r2/data-catalog/platform/pricing/` |
|
|
18
|
+
| Iceberg spec | `https://iceberg.apache.org/spec/` |
|
|
19
|
+
|
|
20
|
+
## Connection Values
|
|
21
|
+
|
|
22
|
+
Use the exact **Catalog URI** and **Warehouse** printed by `npx wrangler r2 bucket catalog enable <bucket>` (also shown in the dashboard). They follow these formats:
|
|
23
|
+
|
|
24
|
+
| Value | Format | Example |
|
|
25
|
+
|-------|--------|---------|
|
|
26
|
+
| Catalog URI | `https://catalog.cloudflarestorage.com/{ACCOUNT_ID}/{BUCKET}` | `https://catalog.cloudflarestorage.com/4482a1.../live-data` |
|
|
27
|
+
| Warehouse | `{ACCOUNT_ID}_{BUCKET}` (hyphens preserved) | `4482a1..._live-data` |
|
|
28
|
+
| Token | R2 API token (Admin R&W on Storage + R&W on Data Catalog) | `cfut_...` |
|
|
29
|
+
|
|
30
|
+
The Iceberg `/config` route needs `?warehouse={WAREHOUSE}`.
|
|
31
|
+
|
|
32
|
+
## Architecture
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
Engines (PyIceberg, PySpark, Trino, Snowflake, DuckDB, R2 SQL)
|
|
36
|
+
│ Iceberg REST API (Bearer token)
|
|
37
|
+
▼
|
|
38
|
+
R2 Data Catalog ── namespace/table metadata, snapshots, txn coordination
|
|
39
|
+
│ vended S3 credentials
|
|
40
|
+
▼
|
|
41
|
+
R2 Bucket ── Parquet data files + Iceberg metadata
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- **Warehouse** — top-level catalog grouping (`{ACCOUNT_ID}_{BUCKET}`)
|
|
45
|
+
- **Namespace** — schema/database; nested namespaces supported
|
|
46
|
+
- **Table** — Iceberg table (schema, partition spec, snapshots)
|
|
47
|
+
- **Vended credentials** — temp S3 creds the catalog hands engines (`X-Iceberg-Access-Delegation: vended-credentials`)
|
|
48
|
+
|
|
49
|
+
## When to Use
|
|
50
|
+
|
|
51
|
+
**Use for:** log/analytics data lakes, BI pipelines, time-series/event data, multi-cloud or multi-engine analytics needing ACID + schema evolution on object storage.
|
|
52
|
+
|
|
53
|
+
**Don't use for:** OLTP (use D1/a database), sub-second point lookups, tiny datasets (<1 GB), or unstructured blobs (store directly in R2).
|
|
54
|
+
|
|
55
|
+
## Two APIs — Don't Confuse Them
|
|
56
|
+
|
|
57
|
+
| API | Base | Use for |
|
|
58
|
+
|-----|------|---------|
|
|
59
|
+
| **Iceberg REST catalog** | `https://catalog.cloudflarestorage.com/{ACCOUNT_ID}/{BUCKET}` | Table reads/writes via PyIceberg, PySpark, Trino, etc. |
|
|
60
|
+
| **Control-plane REST API** | `https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/r2-catalog/{BUCKET}` | Enable/disable, maintenance config, list namespaces/tables, get-table |
|
|
61
|
+
|
|
62
|
+
**Status:** Open beta. Available to all R2 subscribers; verify pricing/billing status in docs.
|
|
63
|
+
|
|
64
|
+
## Reading Order
|
|
65
|
+
|
|
66
|
+
1. [configuration.md](configuration.md) — enable catalog, tokens, maintenance, client connection
|
|
67
|
+
2. [api.md](api.md) — control-plane REST (incl. get-table), PyIceberg client, maintenance
|
|
68
|
+
3. [patterns.md](patterns.md) — PyIceberg + PySpark templates, partitioning, external engines
|
|
69
|
+
4. [gotchas.md](gotchas.md) — auth errors, maintenance behavior, troubleshooting
|
|
70
|
+
|
|
71
|
+
## See Also
|
|
72
|
+
|
|
73
|
+
- [pipelines](../pipelines/) — stream events into Iceberg tables
|
|
74
|
+
- [r2-sql](../r2-sql/) — serverless SQL over these tables
|
|
75
|
+
- [r2](../r2/) — underlying object storage
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# R2 Data Catalog API Reference
|
|
2
|
+
|
|
3
|
+
Two APIs: the **control-plane REST API** (Cloudflare-specific) and the **Iceberg REST catalog API** (standard, used via PyIceberg/PySpark). For PyIceberg method details pull `https://py.iceberg.apache.org/`; for engine configs see `https://developers.cloudflare.com/r2/data-catalog/config-examples/`.
|
|
4
|
+
|
|
5
|
+
## Control-Plane REST API
|
|
6
|
+
|
|
7
|
+
Base: `https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/r2-catalog/{BUCKET}`
|
|
8
|
+
Auth: `Authorization: Bearer $API_TOKEN`
|
|
9
|
+
|
|
10
|
+
| Operation | Method | Path |
|
|
11
|
+
|-----------|--------|------|
|
|
12
|
+
| Get catalog details | GET | `/r2-catalog/{bucket}` |
|
|
13
|
+
| Enable / disable | POST | `/r2-catalog/{bucket}/enable` · `/disable` |
|
|
14
|
+
| Store compaction credential | POST | `/r2-catalog/{bucket}/credential` |
|
|
15
|
+
| List namespaces | GET | `/namespaces` |
|
|
16
|
+
| List tables | GET | `/namespaces/{ns}/tables` |
|
|
17
|
+
| **Get table metadata** | GET | `/namespaces/{ns}/tables/{table}` |
|
|
18
|
+
| Get/update maintenance config | GET/POST | `/maintenance-configs` and `/namespaces/{ns}/tables/{table}/maintenance-configs` |
|
|
19
|
+
|
|
20
|
+
List endpoints accept `?return_uuids=true`, `?return_details=true`, `?parent={ns}`, and pagination. **Nested namespaces use `%1F` (Unit Separator)**, not `/` or `.`: `/namespaces/parent%1Fchild/tables`.
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Catalog details (status, maintenance_config, credential_status)
|
|
24
|
+
curl -s "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/r2-catalog/$BUCKET" \
|
|
25
|
+
-H "Authorization: Bearer $API_TOKEN"
|
|
26
|
+
|
|
27
|
+
# Store token for compaction (pure-API setups)
|
|
28
|
+
curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/r2-catalog/$BUCKET/credential" \
|
|
29
|
+
-H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/json" \
|
|
30
|
+
-d '{"token": "'$API_TOKEN'"}'
|
|
31
|
+
|
|
32
|
+
# Update maintenance config (all fields optional; table-level overrides catalog-level)
|
|
33
|
+
curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/r2-catalog/$BUCKET/maintenance-configs" \
|
|
34
|
+
-H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/json" \
|
|
35
|
+
-d '{"compaction": {"state": "enabled", "target_size_mb": "256"},
|
|
36
|
+
"snapshot_expiration": {"state": "enabled", "min_snapshots_to_keep": 10, "max_snapshot_age": "7d"}}'
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Get Table (metadata introspection)
|
|
40
|
+
|
|
41
|
+
`GET /namespaces/{ns}/tables/{table}` returns schema, partition spec, sort order, and snapshot info — like Iceberg "load table" but on the control plane, with snapshots pruned to the most recent 10. (Newer than the published API docs.)
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
curl -s "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/r2-catalog/$BUCKET/namespaces/live/tables/earthquakes" \
|
|
45
|
+
-H "Authorization: Bearer $API_TOKEN"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{"result": {
|
|
50
|
+
"identifier": {"namespace": ["live"], "name": "earthquakes"},
|
|
51
|
+
"table_uuid": "019edccf-3ac8-73e3-...",
|
|
52
|
+
"metadata_location": "s3://live-data/__r2_data_catalog/.../metadata/01225-....metadata.json",
|
|
53
|
+
"total_snapshots": 1225,
|
|
54
|
+
"returned_snapshots": 10,
|
|
55
|
+
"metadata": { /* standard Iceberg TableMetadata: schemas, partition-specs, sort-orders,
|
|
56
|
+
properties, current-snapshot-id, snapshots (≤10), snapshot-log, refs */ }
|
|
57
|
+
}, "success": true}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
| Field | Description |
|
|
61
|
+
|-------|-------------|
|
|
62
|
+
| `identifier` | `{namespace: [...], name}` |
|
|
63
|
+
| `table_uuid` | Iceberg table UUID |
|
|
64
|
+
| `metadata_location` | R2 path to current metadata file |
|
|
65
|
+
| `total_snapshots` | Total before pruning |
|
|
66
|
+
| `returned_snapshots` | Count in `metadata.snapshots` (max 10) |
|
|
67
|
+
| `metadata` | Standard [Iceberg TableMetadata](https://iceberg.apache.org/spec/#table-metadata-fields), arrays pruned to 10 |
|
|
68
|
+
|
|
69
|
+
### Error Format
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{"success": false, "errors": [{"code": 10000, "message": "Authentication error"}]}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Standard HTTP codes (401 auth, 403 perms, 404 not enabled/found, 409 conflict).
|
|
76
|
+
|
|
77
|
+
## Iceberg REST Catalog API (via PyIceberg)
|
|
78
|
+
|
|
79
|
+
Standard [Iceberg REST Catalog](https://github.com/apache/iceberg/blob/main/open-api/rest-catalog-open-api.yaml). Base: `https://catalog.cloudflarestorage.com/{ACCOUNT_ID}/{BUCKET}`. The `/config` route needs `?warehouse={WAREHOUSE}`.
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from pyiceberg.catalog.rest import RestCatalog
|
|
83
|
+
catalog = RestCatalog(name="r2", warehouse=WAREHOUSE, uri=CATALOG_URI, token=TOKEN)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Common operations (see PyIceberg docs for full signatures):
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
catalog.create_namespace_if_not_exists("logs")
|
|
90
|
+
catalog.list_tables("logs")
|
|
91
|
+
table = catalog.create_table(("logs", "events"), schema=schema) # pyiceberg.schema.Schema
|
|
92
|
+
table = catalog.load_table(("logs", "events"))
|
|
93
|
+
table.append(pyarrow_table) # also .overwrite(...)
|
|
94
|
+
table.scan(row_filter="id > 100").to_pandas()
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Schema evolution (add nullable columns; widen types only):
|
|
98
|
+
```python
|
|
99
|
+
with table.update_schema() as u:
|
|
100
|
+
u.add_column("user_id", LongType(), doc="User ID")
|
|
101
|
+
u.rename_column("msg", "message")
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Time-travel:
|
|
105
|
+
```python
|
|
106
|
+
table.scan(snapshot_id=table.snapshots()[-2].snapshot_id)
|
|
107
|
+
table.scan(as_of_timestamp=ms_epoch)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Manual Maintenance (PySpark)
|
|
111
|
+
|
|
112
|
+
Prefer automatic maintenance (control-plane API/wrangler). For manual control or very large tables, use Spark procedures (`rewrite_data_files`, `rewrite_manifests`, `expire_snapshots`, `remove_orphan_files`). See `https://developers.cloudflare.com/r2/data-catalog/table-maintenance/`.
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
spark.sql("CALL r2dc.system.rewrite_data_files(table => 'ns.tbl')")
|
|
116
|
+
# Orphan removal REQUIRES S3 credentials (vended creds fail with NoAuthWithAWSException)
|
|
117
|
+
spark.sql("CALL r2dc.system.remove_orphan_files(table => 'ns.tbl', older_than => TIMESTAMP '2026-02-28 00:00:00')")
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## See Also
|
|
121
|
+
|
|
122
|
+
- [configuration.md](configuration.md) · [patterns.md](patterns.md) · [gotchas.md](gotchas.md)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# R2 Data Catalog Configuration
|
|
2
|
+
|
|
3
|
+
Enable the catalog, create tokens, turn on automatic maintenance, connect clients. For exhaustive token/permission options and maintenance settings, pull `https://developers.cloudflare.com/r2/data-catalog/manage-catalogs/` and `.../table-maintenance/`.
|
|
4
|
+
|
|
5
|
+
## Step 1: Create Bucket + Enable Catalog
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx wrangler r2 bucket create my-bucket
|
|
9
|
+
npx wrangler r2 bucket catalog enable my-bucket
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
`enable` outputs the two values used everywhere:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
Warehouse: 4482a1cd43bf5197657ae1d8636c414a_my-bucket # {ACCOUNT_ID}_{BUCKET}
|
|
16
|
+
Catalog URI: https://catalog.cloudflarestorage.com/4482a1cd43bf5197657ae1d8636c414a/my-bucket
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Enabling creates `__r2_data_catalog/` metadata in the bucket; existing objects are untouched.
|
|
20
|
+
|
|
21
|
+
## Step 2: Create an API Token
|
|
22
|
+
|
|
23
|
+
Dashboard → **R2** → **Manage R2 API tokens** → **Create API token**.
|
|
24
|
+
|
|
25
|
+
**Simplest:** one token with **R2 Storage Admin Read & Write** + **R2 Data Catalog Read & Write**, scoped to your bucket(s). Add **R2 SQL Read** if you also query. This token works for the Iceberg REST API, control-plane API, R2 SQL, and GraphQL Analytics. Token creation also yields S3 Access Key ID / Secret (needed only for Spark orphan-file removal).
|
|
26
|
+
|
|
27
|
+
> Open-beta limitation: R2 Storage **Admin Write is required even for read-only data access**. See the manage-catalogs doc for the current permission matrix.
|
|
28
|
+
|
|
29
|
+
## Step 3: Enable Automatic Maintenance (Recommended)
|
|
30
|
+
|
|
31
|
+
R2 Data Catalog runs compaction and snapshot expiration for you.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Compaction — merges small files (target size MB; default 128)
|
|
35
|
+
npx wrangler r2 bucket catalog compaction enable my-bucket \
|
|
36
|
+
--target-size 128 --token $API_TOKEN
|
|
37
|
+
|
|
38
|
+
# Snapshot expiration — removes old snapshots AND their unreferenced data files
|
|
39
|
+
npx wrangler r2 bucket catalog snapshot-expiration enable my-bucket \
|
|
40
|
+
--token $API_TOKEN --older-than-days 7 --retain-last 10
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Compaction needs a **stored credential** to access files. `compaction enable` (and the dashboard wizard) stores it automatically; pure-API setups must call `/credential` (see [api.md](api.md)).
|
|
44
|
+
|
|
45
|
+
> Compaction triggers **hourly** with **no hard throughput cap** (the former 2 GB/hour limit was lifted). Snapshot expiration deletes unreferenced data files automatically (since April 2026) — manual orphan cleanup is rarely needed. For target-size guidance per workload, see the table-maintenance doc.
|
|
46
|
+
|
|
47
|
+
## Step 4: Verify
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npx wrangler r2 bucket catalog status my-bucket
|
|
51
|
+
# or control-plane API:
|
|
52
|
+
curl -s "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/r2-catalog/$BUCKET" \
|
|
53
|
+
-H "Authorization: Bearer $API_TOKEN"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Expect `"status": "active"`, `compaction.state: "enabled"`, `credential_status: "present"`.
|
|
57
|
+
|
|
58
|
+
## Client Connection
|
|
59
|
+
|
|
60
|
+
### PyIceberg
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
import os
|
|
64
|
+
from pyiceberg.catalog.rest import RestCatalog
|
|
65
|
+
|
|
66
|
+
catalog = RestCatalog(
|
|
67
|
+
name="r2_catalog",
|
|
68
|
+
warehouse=os.environ["R2_WAREHOUSE"], # {ACCOUNT_ID}_{BUCKET}
|
|
69
|
+
uri=os.environ["R2_CATALOG_URI"], # https://catalog.cloudflarestorage.com/{ACCOUNT_ID}/{BUCKET}
|
|
70
|
+
token=os.environ["R2_TOKEN"],
|
|
71
|
+
)
|
|
72
|
+
print(catalog.list_namespaces()) # connection test
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### PySpark / DuckDB / Trino / Snowflake
|
|
76
|
+
|
|
77
|
+
Full, current engine configs live at `https://developers.cloudflare.com/r2/data-catalog/config-examples/`. A verified PySpark session template is in [patterns.md](patterns.md#pyspark-session) (needs Iceberg 1.6.1 and `X-Iceberg-Access-Delegation: vended-credentials`).
|
|
78
|
+
|
|
79
|
+
## Environment Variables Pattern
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# .env (never commit)
|
|
83
|
+
R2_CATALOG_URI=https://catalog.cloudflarestorage.com/<ACCOUNT_ID>/<BUCKET>
|
|
84
|
+
R2_WAREHOUSE=<ACCOUNT_ID>_<BUCKET>
|
|
85
|
+
R2_TOKEN=<api-token>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Disable Catalog
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npx wrangler r2 bucket catalog disable my-bucket
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Preserves data and metadata; tables become inaccessible via the catalog until re-enabled.
|
|
95
|
+
|
|
96
|
+
## See Also
|
|
97
|
+
|
|
98
|
+
- [api.md](api.md) — control-plane + PyIceberg API · [gotchas.md](gotchas.md) — auth & maintenance troubleshooting
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# R2 Data Catalog Gotchas
|
|
2
|
+
|
|
3
|
+
Common failure modes and operational behavior. For limits, recommendations, and supported settings, pull `https://developers.cloudflare.com/r2/data-catalog/` and `.../table-maintenance/`.
|
|
4
|
+
|
|
5
|
+
## Connection / Auth
|
|
6
|
+
|
|
7
|
+
- **Catalog URI / warehouse mismatch (most common).** Copy both values exactly from `wrangler r2 bucket catalog enable` (Catalog URI `https://catalog.cloudflarestorage.com/{ACCOUNT_ID}/{BUCKET}`, warehouse `{ACCOUNT_ID}_{BUCKET}`). Mismatched values fail to connect.
|
|
8
|
+
- **401 Unauthorized** — token lacks Data Catalog R&W. Test with `catalog.list_namespaces()`.
|
|
9
|
+
- **403 on data files** — token lacks R2 Storage. Open beta requires **Admin Read & Write on R2 Storage even for read-only** data access.
|
|
10
|
+
- **`/config` "Warehouse name missing in query param"** — the Iceberg `/v1/config` route needs `?warehouse={ACCOUNT_ID}_{BUCKET}`. PyIceberg/PySpark add it automatically when you set `warehouse=`.
|
|
11
|
+
|
|
12
|
+
## Maintenance Behavior (updated)
|
|
13
|
+
|
|
14
|
+
- **No throughput cap on compaction.** The former 2 GB/hour/table limit is **lifted** — compaction triggers hourly and processes the backlog with no hard cap. Large small-file backlogs still take multiple hourly cycles.
|
|
15
|
+
- **Snapshot expiration deletes data files** (since April 2026), not just metadata. Manual `remove_orphan_files` is rarely needed.
|
|
16
|
+
- **Compaction requires a stored credential.** `wrangler ... compaction enable` and the dashboard wizard store it automatically; pure-API setups must POST `/credential`.
|
|
17
|
+
- Compaction is **Parquet-only**.
|
|
18
|
+
|
|
19
|
+
## Tables & Schema
|
|
20
|
+
|
|
21
|
+
- `TableAlreadyExistsError` / `NamespaceAlreadyExistsError` → use `create_*_if_not_exists` / load existing.
|
|
22
|
+
- `422 Validation` on schema update → only add nullable columns and widen types (int→long, float→double).
|
|
23
|
+
- `TypeError: Cannot cast` on append → PyArrow type ≠ Iceberg schema; cast to int64 (Iceberg default); check `table.schema()`.
|
|
24
|
+
|
|
25
|
+
## Concurrency
|
|
26
|
+
|
|
27
|
+
- `CommitFailedException` → optimistic-locking conflict; retry with backoff (see [patterns.md](patterns.md#concurrent-writes-with-retry-pyiceberg)).
|
|
28
|
+
- Stale metadata after external writes → reload: `table = catalog.load_table(("ns","tbl"))`.
|
|
29
|
+
|
|
30
|
+
## PySpark / Iceberg
|
|
31
|
+
|
|
32
|
+
| Issue | Fix |
|
|
33
|
+
|-------|-----|
|
|
34
|
+
| Catalog auth fails | Add header `X-Iceberg-Access-Delegation: vended-credentials` |
|
|
35
|
+
| `NoAuthWithAWSException` on orphan removal | Supply S3 access/secret keys (vended creds don't work here) |
|
|
36
|
+
| Version mismatch | Use Iceberg `1.6.1` |
|
|
37
|
+
| Slow first run (~30–60s) | JAR download; cached after |
|
|
38
|
+
| Remote signing errors | Set `s3.remote-signing-enabled=false` |
|
|
39
|
+
|
|
40
|
+
## Nested Namespaces
|
|
41
|
+
|
|
42
|
+
Control-plane URL separator for nested namespaces is **`%1F`** (Unit Separator), not `/` or `.`: `/namespaces/parent%1Fchild/tables`.
|
|
43
|
+
|
|
44
|
+
## Debug Checklist
|
|
45
|
+
|
|
46
|
+
1. `npx wrangler r2 bucket catalog status <bucket>` — enabled?
|
|
47
|
+
2. Token has R2 Storage (Admin R&W) + R2 Data Catalog (R&W)?
|
|
48
|
+
3. `catalog.list_namespaces()` succeeds?
|
|
49
|
+
4. Catalog URI = `catalog.cloudflarestorage.com/{ACCOUNT_ID}/{BUCKET}`, warehouse = `{ACCOUNT_ID}_{BUCKET}`?
|
|
50
|
+
5. Namespace created before `create_table`?
|
|
51
|
+
6. Compaction enabled + `credential_status: present`?
|
|
52
|
+
|
|
53
|
+
## See Also
|
|
54
|
+
|
|
55
|
+
- [configuration.md](configuration.md) · [api.md](api.md) · [patterns.md](patterns.md)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# R2 Data Catalog Patterns
|
|
2
|
+
|
|
3
|
+
Code templates with PyIceberg (lightweight, no JVM) and PySpark (full Iceberg ecosystem). For per-engine config (DuckDB, Trino, Snowflake, StarRocks) and partitioning/maintenance best practices, pull `https://developers.cloudflare.com/r2/data-catalog/config-examples/` and `.../table-maintenance/`.
|
|
4
|
+
|
|
5
|
+
| Need | Tool |
|
|
6
|
+
|------|------|
|
|
7
|
+
| Catalog ops, append/scan, small-medium loads | PyIceberg |
|
|
8
|
+
| Batch ETL, INSERT INTO SELECT, DELETE/MERGE, write-back, >1 TB maintenance | PySpark |
|
|
9
|
+
| Pure SQL analytics (no writes) | [R2 SQL](../r2-sql/) |
|
|
10
|
+
|
|
11
|
+
## PyIceberg: Connect, Create, Load
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
import os, pyarrow as pa
|
|
15
|
+
from pyiceberg.catalog.rest import RestCatalog
|
|
16
|
+
|
|
17
|
+
catalog = RestCatalog(
|
|
18
|
+
name="r2",
|
|
19
|
+
warehouse=os.environ["R2_WAREHOUSE"], # {ACCOUNT_ID}_{BUCKET}
|
|
20
|
+
uri=os.environ["R2_CATALOG_URI"], # https://catalog.cloudflarestorage.com/{ACCOUNT_ID}/{BUCKET}
|
|
21
|
+
token=os.environ["R2_TOKEN"],
|
|
22
|
+
)
|
|
23
|
+
catalog.create_namespace_if_not_exists("analytics")
|
|
24
|
+
|
|
25
|
+
schema = pa.schema([("id", pa.int64()), ("name", pa.string()), ("amount", pa.float64())])
|
|
26
|
+
table = catalog.create_table(("analytics", "events"), schema=schema)
|
|
27
|
+
table.append(pa.table({"id": [1, 2], "name": ["a", "b"], "amount": [80.0, 92.5]}))
|
|
28
|
+
print(table.scan().to_arrow().to_pandas())
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## PyIceberg: Partitioned Time-Series Table
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from pyiceberg.schema import Schema
|
|
35
|
+
from pyiceberg.types import NestedField, TimestampType, StringType
|
|
36
|
+
from pyiceberg.partitioning import PartitionSpec, PartitionField
|
|
37
|
+
from pyiceberg.transforms import DayTransform
|
|
38
|
+
|
|
39
|
+
schema = Schema(
|
|
40
|
+
NestedField(1, "timestamp", TimestampType(), required=True),
|
|
41
|
+
NestedField(2, "level", StringType(), required=True),
|
|
42
|
+
NestedField(3, "message", StringType(), required=False),
|
|
43
|
+
)
|
|
44
|
+
spec = PartitionSpec(PartitionField(source_id=1, field_id=1000, transform=DayTransform(), name="day"))
|
|
45
|
+
table = catalog.create_table(("logs", "app_logs"), schema=schema, partition_spec=spec)
|
|
46
|
+
errors = table.scan(row_filter="level = 'ERROR'").to_pandas() # partition pruning
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## PySpark Session
|
|
50
|
+
|
|
51
|
+
Verified template — requires Iceberg **1.6.1** and vended credentials. S3 keys are only needed for orphan-file removal. (If this drifts, cross-check `config-examples/spark-python/`.)
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from pyspark.sql import SparkSession
|
|
55
|
+
|
|
56
|
+
spark = SparkSession.builder \
|
|
57
|
+
.appName("R2DataCatalog") \
|
|
58
|
+
.config('spark.jars.packages',
|
|
59
|
+
'org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.6.1,'
|
|
60
|
+
'org.apache.iceberg:iceberg-aws-bundle:1.6.1,'
|
|
61
|
+
'org.apache.hadoop:hadoop-aws:3.3.4,'
|
|
62
|
+
'com.amazonaws:aws-java-sdk-bundle:1.12.262') \
|
|
63
|
+
.config("spark.sql.extensions", "org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions") \
|
|
64
|
+
.config("spark.sql.catalog.r2dc", "org.apache.iceberg.spark.SparkCatalog") \
|
|
65
|
+
.config("spark.sql.catalog.r2dc.type", "rest") \
|
|
66
|
+
.config("spark.sql.catalog.r2dc.uri", CATALOG_URI) \
|
|
67
|
+
.config("spark.sql.catalog.r2dc.warehouse", WAREHOUSE) \
|
|
68
|
+
.config("spark.sql.catalog.r2dc.token", TOKEN) \
|
|
69
|
+
.config("spark.sql.catalog.r2dc.header.X-Iceberg-Access-Delegation", "vended-credentials") \
|
|
70
|
+
.config("spark.sql.catalog.r2dc.s3.remote-signing-enabled", "false") \
|
|
71
|
+
.config("spark.sql.defaultCatalog", "r2dc") \
|
|
72
|
+
.config("spark.hadoop.fs.s3a.access.key", S3_ACCESS_KEY) \
|
|
73
|
+
.config("spark.hadoop.fs.s3a.secret.key", S3_SECRET_KEY) \
|
|
74
|
+
.config("spark.hadoop.fs.s3a.endpoint", S3_ENDPOINT) \
|
|
75
|
+
.config("spark.hadoop.fs.s3a.path.style.access", "true") \
|
|
76
|
+
.getOrCreate()
|
|
77
|
+
spark.sql("USE r2dc")
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
> `X-Iceberg-Access-Delegation: vended-credentials` is required; `s3.remote-signing-enabled` must be `false`. First startup ~30–60s for JAR downloads (cached after).
|
|
81
|
+
|
|
82
|
+
## PySpark: Batch ETL
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
spark.sql("""
|
|
86
|
+
CREATE TABLE IF NOT EXISTS my_ns.events (
|
|
87
|
+
__ingest_ts TIMESTAMP, event_id STRING, category STRING, amount DOUBLE
|
|
88
|
+
) PARTITIONED BY (days(__ingest_ts))
|
|
89
|
+
""")
|
|
90
|
+
|
|
91
|
+
spark.read.option("header","true").csv("data.csv").writeTo("my_ns.events").append()
|
|
92
|
+
spark.read.parquet("data.parquet").writeTo("my_ns.events").append()
|
|
93
|
+
spark.sql("INSERT INTO my_ns.target SELECT col1, col2 FROM my_ns.source WHERE col1 > 0")
|
|
94
|
+
spark.sql("DELETE FROM my_ns.events WHERE amount < 0")
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
> Partition large tables (`PARTITIONED BY (days(__ingest_ts))`). Unpartitioned works for small datasets (<1000 files) but degrades at scale.
|
|
98
|
+
|
|
99
|
+
## Concurrent Writes with Retry (PyIceberg)
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from pyiceberg.exceptions import CommitFailedException
|
|
103
|
+
import time
|
|
104
|
+
|
|
105
|
+
def append_with_retry(table, data, max_retries=3):
|
|
106
|
+
for attempt in range(max_retries):
|
|
107
|
+
try:
|
|
108
|
+
table.append(data); return
|
|
109
|
+
except CommitFailedException:
|
|
110
|
+
if attempt == max_retries - 1: raise
|
|
111
|
+
time.sleep(2 ** attempt)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Optimistic locking: concurrent commits to the same table may conflict; different-partition writes are safe.
|
|
115
|
+
|
|
116
|
+
## Connecting Any Iceberg Engine
|
|
117
|
+
|
|
118
|
+
Engines connect with the Iceberg REST catalog config — Catalog URI `https://catalog.cloudflarestorage.com/{ACCOUNT_ID}/{BUCKET}`, warehouse `{ACCOUNT_ID}_{BUCKET}`, your token, and header `X-Iceberg-Access-Delegation: vended-credentials`. Copy-paste configs per engine: `config-examples/`.
|
|
119
|
+
|
|
120
|
+
## See Also
|
|
121
|
+
|
|
122
|
+
- [api.md](api.md) · [gotchas.md](gotchas.md) · [pipelines/patterns.md](../pipelines/patterns.md) · [r2-sql/patterns.md](../r2-sql/patterns.md)
|