') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Add Cloudflare Workers world implementation by ataylorme · Pull Request #1372 · vercel/workflow · GitHub
Skip to content

Add Cloudflare Workers world implementation - #1372

Open
ataylorme wants to merge 2 commits into
vercel:mainfrom
taylor-tech-ventures:claude/cloudflare-world-sdk-9kmfr
Open

Add Cloudflare Workers world implementation#1372
ataylorme wants to merge 2 commits into
vercel:mainfrom
taylor-tech-ventures:claude/cloudflare-world-sdk-9kmfr

Conversation

@ataylorme

Copy link
Copy Markdown

Summary

This PR introduces a complete Cloudflare Workers world implementation for the Workflow DevKit, enabling workflow execution on Cloudflare's serverless platform using Durable Objects for state management, D1 for cross-run queries, and Cloudflare Queues for message dispatch.

Key Changes

  • WorkflowRunDO (Durable Object): A comprehensive per-run state machine that handles:

    • Event processing and validation with full state transition logic
    • Run, step, hook, and wait entity management
    • Stream chunk storage and retrieval for large data handling
    • Legacy spec version compatibility
    • Idempotent event handling and terminal state enforcement
  • Storage Layer: Implements the Storage interface with:

    • Run listing via D1 index with fallback to Durable Objects for full data
    • Event creation and retrieval routed through per-run DOs
    • Hook and step management with cross-run query support via D1
  • Queue Integration:

    • Message envelope format for Cloudflare Queues
    • Queue handler registration and batch processing
    • Integration with local world executor for handler execution
  • D1 Index Schema: Lightweight global index tables for:

    • Workflow runs (with filtering by name/status)
    • Workflow hooks (with token-based lookup)
    • Automatic index updates on run mutations
  • Streaming Support: Stream chunk storage in Durable Object storage with:

    • Multi-chunk write operations
    • Stream closure and metadata tracking
    • Byte-range read support
  • Configuration & Utilities:

    • Type-safe config interface for Cloudflare bindings
    • Durable Object stub management and fetch helpers
    • ISO date serialization utilities

Notable Implementation Details

  • Event processing logic ported from world-postgres with full validation including version compatibility checks, terminal state enforcement, and idempotent operations
  • Hybrid storage model: DO storage for per-run data, D1 for global queries with intelligent fallback
  • Monotonic ULID generation for deterministic ordering
  • Comprehensive error handling with proper HTTP status codes (404, 409, 410, 425)
  • Data filtering support (resolveData parameter) to exclude input/output when not needed

https://claude.ai/code/session_019jYBQA1pNTDjK4LGt8qFKA

Implements a Workflow DevKit world backend using Cloudflare primitives:
- Durable Objects for per-run state (events, steps, hooks, waits, streams)
- D1 SQLite for cross-run index queries (runs.list, hooks.getByToken)
- Cloudflare Queues for reliable message dispatch
https://claude.ai/code/session_019jYBQA1pNTDjK4LGt8qFKA
@ataylorme
ataylorme requested a review from a team as a code ownerMarch 13, 2026 15:37
@changeset-bot

changeset-botBot commented Mar 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f2ebe0e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
@workflow/world-cloudflarePatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Mar 13, 2026

Copy link
Copy Markdown
Contributor

@claude is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

@socket-security

socket-securityBot commented Mar 13, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

DiffPackageSupply Chain
Security
VulnerabilityQualityMaintenanceLicense
Addednpm/​@​cloudflare/​workers-types@​4.20250312.0100100100100100

View full report

Comment threadpackages/world-cloudflare/src/streamer.ts Outdated
Comment threadpackages/world-cloudflare/src/storage.ts Outdated
- Fix run_created with null runId: generate runId client-side so routing
key and stored runId map to the same DO instance; temp random keys routed
to a different instance making run data permanently unretrievable
- Fix listStreamsByRunId: prefix returned names with `runId:` to match
the format expected by readFromStream
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@VaguelySerious

Copy link
Copy Markdown
Member

Hi @ataylorme

We love seeing World contributions. The approach we're taking with community Worlds is that we'd like them to be published as third-party Worlds, which we then add/advertise on our list of Worlds. In order to get on this list, all you need is:

  • Publish your World as an npm package
  • Make a PR to our docs and the world-manifest.json file in this repo adding yourself

Once that's done, we'll verify whether the package works, is deployable/redeployable, and passes at least basic workflow e2e tests. If that looks good, we'll merge the PR! If your package gets significant community attention, we may consider moving to first-party support inside the workflow repository.

@ataylorme

Copy link
Copy Markdown
Author
  • Make a PR to our docs and the world-manifest.json file in this repo adding yourself

Done in #1477

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@ataylorme@VaguelySerious@claude