Status: Phase 2a — In Progress


Chosen Approach: Hono JSX + API HTML Endpoint + CF Cache API

After evaluating multiple approaches (React SSR, Astro, raw HTML strings), we chose Hono JSX on Cloudflare Workers as the SSR engine. This leverages the existing CF Worker infrastructure with zero new services.

flowchart LR
    Browser -->|"GET org.colbin.com/doc/slug"| CFWorker["CF Worker\n(Hono JSX)"]
    CFWorker -->|"Cache HIT"| Browser
    CFWorker -->|"Cache MISS"| API["API Server\n/documents/public/:slug/html"]
    API -->|"crdtState → Y.Doc → PM JSON → HTML"| CFWorker
    CFWorker -->|"Render JSX → Full HTML"| CFCache["CF Cache API\n(s-maxage=300)"]
    CFCache --> Browser
    
    Editor["User edits doc\n(Hocuspocus)"] -->|"onStoreDocument"| CachePurge["Cache Purge Service\n(debounced 60s)"]
    CachePurge -->|"POST /purge_cache"| CFCache

Why Hono JSX?

Criteria

Hono JSX

React SSR

Raw Strings

Bundle size

~14KB

~130KB+

0KB

JSX support

Native

Needs renderToString

None

CF Worker native

Yes

Needs edge adapter

Yes

Template composability

Excellent

Excellent

Poor

Type safety

Full TSX

Full TSX

String only

Streaming SSR

Built-in

Complex setup

Manual

Architecture: Island Architecture

flowchart TD
    subgraph SSR["Server-Side Rendered (CF Worker)"]
        HTML["Full HTML Document\n+ Meta Tags + JSON-LD"]
        Content["Article Content\n(ProseMirror → HTML)"]
        NavBar["Navigation Bar"]
    end
    
    subgraph Client["Client-Side (Lazy)"]
        AuthCheck["Auth Detection\n(~500B inline script)"]
        EditBtn["'Edit' Button\n(shown if authenticated)"]
        Editor["Full Editor Bundle\n(loaded on click)"]
    end
    
    SSR --> AuthCheck
    AuthCheck -->|"has auth cookie"| EditBtn
    EditBtn -->|"user clicks Edit"| Editor

Milestone 1: API HTML Rendering Endpoint

1.1 ProseMirror JSON → HTML Serializer

File: apps/api/src/features/document/utils/renderProsemirrorToHtml.util.ts

Pure string-based recursive serializer (~250 lines). Maps all 28 node types and 11 mark types from packages/block-editor/src/core/schema.ts. Zero DOM dependencies — just string concatenation.

Pipeline:

crdtState (Buffer) → Y.applyUpdate(yDoc) → yDocToProsemirrorJSON(yDoc) → renderProsemirrorJsonToHtml(json) → HTML string

1.2 HTML Renderer Service

File: apps/api/src/features/document/services/htmlRenderer.service.ts

  1. Load document by slug (existing documentService.getDocumentBySlug())

  2. Check accessLevels for PUBLIC scope

  3. Load crdtState → Y.Doc → ProseMirror JSON → HTML

  4. Generate description from first 160 chars of plainText

  5. Return { html, title, description, type, language, updatedAt, org, document }

1.3 Public API Endpoint

GET /documents/public/:slug/html

Milestone 2: Worker Migration to Hono JSX

Routing

Route

Handler

Cache

GET /doc/:docSlug

SSR document page

CF Cache API (300s)

GET /

SSR org homepage

CF Cache API (300s)

GET /* (assets)

Proxy to CF Pages

Unchanged

GET /* (fallback)

Proxy to CF Pages SPA

Existing bootstrap

JSX Templates

SEO Output


Milestone 3: Cache Invalidation

CF Cache Purge (Single File)

sequenceDiagram
    participant User as User (Editor)
    participant WS as Hocuspocus
    participant API as API Server
    participant CF as Cloudflare API
    participant Cache as CF Edge Cache
    
    User->>WS: Edit document
    WS->>API: onStoreDocument (debounced 2s)
    API->>API: cachePurgeService.purgeDocument()
    Note over API: Debounced: max 1 purge/60s/doc
    API->>CF: POST /zones/{id}/purge_cache\n{ files: ["https://org.colbin.com/doc/slug"] }
    CF->>Cache: Purge cached response
    Note over Cache: Next request = cache MISS → fresh SSR

Milestone 4: Progressive Enhancement (Deferred)


Performance Targets

Metric

Target

How

TTFB

< 200ms

CF Cache hit at edge

FCP

< 500ms

Inline CSS, no external requests

SEO Score

90+

Full HTML, meta tags, JSON-LD

Cache freshness

< 60s stale

Purge on edit + stale-while-revalidate


Reference Articles

Topic

Source

Hono JSX Guide

hono.dev/docs/guides/jsx

CF Workers + Hono

developers.cloudflare.com

CF Cache API

developers.cloudflare.com/workers/runtime-apis/cache

CF Cache Purge

developers.cloudflare.com/cache/how-to/purge-cache

CF Instant Purge (<150ms)

blog.cloudflare.com/instant-purge

Islands Architecture

patterns.dev/vanilla/islands-architecture

y-prosemirror

github.com/yjs/y-prosemirror

Mintlify 72M edge-cached pages

mintlify.com/blog/page-speed-improvements