From b66a993b351a06f52b8d4ee37e1ecafea5e1833b Mon Sep 17 00:00:00 2001 From: Samraaj Bath Date: Mon, 6 Jul 2026 10:27:53 -0700 Subject: [PATCH] docs(SERVICE): correct event-stream contract and document preview.html MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audited docs against the fork-salvage API surface (PR #17). SERVICE.md fixes: - Event stream: events are the compiler's granular {t, ...} log stream plus service phases, not a fixed capture/generate/verify enum. Corrected the "{seq, t, ...payload}" claim — returned bodies carry `t` but NOT `seq`. Documented that `after` is a seq cursor (DB filters seq > N) and that the in-memory backend ignores `after` and returns the full list each poll. - preview.html: new Preview section — the flat, self-contained generate-time artifact (file-map path `preview.html`, manifest `preview_html`, always present), fetched via /v1/clones/:id/files/preview.html. Documented its arrival on the `generated` event, relative `public/assets/...` resolution through the files route, the authenticated-iframe caveat + proxy recommendation, and the original→preview.html→deployed staging. Stated plainly that it is static/runtime-free: frozen animations, no interactions. - Added the `preview` option and clarified app-preview (built export) vs the flat preview.html. DEPLOY.md migration list (0000/0001/0002) verified accurate — no change. README.md / docs/README.md make no stale API claims — no change. Co-Authored-By: Claude Fable 5 --- docs/SERVICE.md | 77 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 10 deletions(-) diff --git a/docs/SERVICE.md b/docs/SERVICE.md index 634667e..46c7636 100644 --- a/docs/SERVICE.md +++ b/docs/SERVICE.md @@ -31,12 +31,27 @@ Two run modes, same HTTP surface: **worker** process consumes the queue, runs the clone, stores artifacts, and the client polls to completion. This is the production mode. -In both modes the job emits structured progress events (stage transitions such as -`capture`, `generate`, `verify`) as it runs. Poll `GET /v1/clones/:id/events?after=N` -with the last sequence number you have seen; each event is `{seq, t, ...payload}`. -In production mode events persist in the `job_events` table (migration -`0002_productive_wallflower.sql`), so they survive worker restarts and remain -readable after completion. +In both modes the job emits structured progress events as it runs — the compiler's +granular log stream (`{ event: "captured", ... }`, `{ event: "generated", ... }`, +`{ event: "build_start", ... }`, etc.) plus service phases (`{ event: "clone_done" }`, +`{ event: "clone_error", error }`). Every event carries a millisecond wall-clock +timestamp `t`; the rest of each object is the payload the emitter logged, so field sets +vary by event. There is no fixed enum of event types — treat unknown `event` strings as +opaque progress and drive UI off the coarse job `status` from `GET /v1/clones/:id`. + +Poll `GET /v1/clones/:id/events?after=N` → `{ jobId, events: [{ t, ... }] }`. `after` is a +**sequence cursor** (a 1-based event count, not a timestamp): pass the number of events +you have already consumed to get only newer ones. Caveats: + +- The returned event bodies do **not** include the `seq` value, so track the cursor + yourself as a running count of events received. +- The in-memory backend **ignores `after`** and returns the full list on every poll — so + in that mode you always get all events and must de-duplicate by count. The DB backend + honors `after` (`seq > N`). + +In production (DB) mode events persist in the `job_events` table (migration +`0002_productive_wallflower.sql`) with a per-job `seq`, so they survive worker restarts +and remain readable after completion. ## Local development @@ -92,10 +107,10 @@ POST /v1/signup/request { email } → 202 {message} (s POST /v1/signup/verify { token } → 201 {apiKey,message} (consume email token) GET /v1/clones → list (metadata) GET /v1/clones/:id → status + metadata (fileCount, totalBytes, capture, timings) -GET /v1/clones/:id/events?after=N → structured progress events since seq N (poll while running) +GET /v1/clones/:id/events?after=N → { jobId, events } — progress events after cursor N (poll while running) GET /v1/clones/:id/result → the eager CloneResult (text files inline; binaries by URL) +GET /v1/clones/:id/files/* → stream one generated file (e.g. .../files/preview.html — see Preview) GET /v1/clones/:id/app-preview/* → serve the clone's built static export when present (404 until built) -GET /v1/clones/:id/files/* → stream one file GET /v1/clones/:id/bundle?format=tgz|zip → the whole app as one archive (302 → S3 when configured) DELETE /v1/clones/:id → purge artifacts GET /healthz → { ok: true } (unauthenticated) @@ -116,8 +131,10 @@ one-time email link, and `POST /v1/signup/verify` consumes the token, stores only the API key's SHA-256 hash in Postgres, stores the verified email in the key label for attribution, and returns the raw key once. -Normal product `options` are `{ mode?: "single" | "multi", styling?: "tailwind" | "css", framework?: "next" | "vite" }`. -`mode` defaults to `"single"`, `styling` defaults to `"tailwind"`, and `framework` defaults to `"next"`. Operational options +Normal product `options` are `{ mode?: "single" | "multi", styling?: "tailwind" | "css", framework?: "next" | "vite", preview?: boolean }`. +`mode` defaults to `"single"`, `styling` defaults to `"tailwind"`, and `framework` defaults to `"next"`. `preview` +controls the browsable built export served at `/v1/clones/:id/app-preview/` (see **Preview** below); it is on by +default for single-page clones, so pass `preview:true` to force it for a multi-page clone. Operational options remain `{ verify?, asyncVerify?, maxRoutes?, maxCollection?, captureConcurrency?, validationConcurrency?, viewportConcurrency?, noCache? }`; `noCache` is service-level and bypasses the cache. Deprecated aliases (`multiPage`, `humanizeMode`) and dev-only escape hatches are still accepted for compatibility, but are not part of the normal product surface. @@ -132,6 +149,46 @@ attaches the verify report afterward while the worker still has the run artifact current async QA path; a post-hoc verify endpoint would require persisting full capture artifacts, not just the generated app bundle. +## Preview + +Every clone emits a flat, self-contained **`preview.html`** at generate time — it is a +regular entry in the result file map (path `preview.html`, at the app root), and the +manifest records it as `preview_html: "preview.html"` (always present). It exists as soon +as the compiler's `generate` step finishes emitting the file map — well before any Next +build or deploy — so a consumer can show the cloned page within seconds. Its arrival is +visible in the event stream as a `{ event: "generated", ... }` event. + +Fetch it through the ordinary files route: + +```bash +curl -sS -H "authorization: Bearer $DITTO_API_KEY" \ + "$DITTO_API_URL/v1/clones/$JOB_ID/files/preview.html" -o preview.html +``` + +**What it is — and isn't.** `preview.html` is a single HTML file with inline CSS and **no +JavaScript** (no runtime scripts). It renders the captured page statically: animations are +frozen at their captured start frame, Lottie/`