Skip to content

Settle script output through one shared runtime policy - #147

Merged
collin-thomas merged 2 commits into
mainfrom
shared-script-output-policy
Nov 12, 2026
Merged

collin-thomas merged 2 commits into
mainfrom
shared-script-output-policy

Conversation

@collin-thomas

Copy link
Copy Markdown
Collaborator

Stacked on #146. The diff here is only the refactor; merge #146 first.

Why

What a server or stream script's result becomes was written three times, once per host, plus a fourth copy in the custom adapter tutorial:

  1. print the value by the shared rule (serverScriptOutput)
  2. strip ANSI escapes unless scripts.stripAnsi is off
  3. apply the component's output scope
  4. refuse a value that is not markup (a failure in dev, an empty slot and one warning per script in production)
  5. apply scripts.onServerScriptError to a failure, and drop a client abort silently

Bascik promises the same bytes from every host. With three copies, a fix to one can miss the others; #146 had to make the same edit in each.

What changed

  • New portable module pkg/src/lib/script-output-policy.ts, exported from @bascik/bascik/runtime:
    • settleScriptOutput(result, yielded, policy) for one settled call or pull
    • scriptJobOutput(invoked, policy) for a whole invocation, including a script that yields
  • runServerScriptJobOutput (Node), and runGraphJobOutput in the Cloudflare and Netlify runtimes, now build a ScriptOutputPolicy and call it. Their own settle functions, Node's refusedReturn and Cloudflare's scopeJobOutput are gone.
  • A policy names only what is the host's own:
Hook Node Cloudflare and Netlify
production from isProdServer / isBuild always true
warn console.warn the handler's log
firstRefusal a Set keyed by where the script starts a WeakSet of jobs
describeFailure the stack mapped to the authored file and line the stack, then the owner the build recorded
createError ServerScriptError plain Error
isSilentFailure a socket reset not set
  • The custom adapter tutorial's job runner shrinks from about 60 lines of policy to one scriptJobOutput call. The runtime reference, package exports page, server internals page and changelog describe the new exports.

No behavior change

Before touching any host I added characterization tests that pin current output byte for byte, ran them green on the old code, and kept them green:

  • Node: the framed, source-mapped report of a failed pull; ServerScriptError for a failed call, a failed pull and a refused value; the dev refusal message with file and line; once-per-script warnings in production; a socket reset during a yield staying silent.
  • Cloudflare and Netlify: the exact log line for a failure with and without an owner, the full production refusal message word for word, and ANSI stripping on and off.

The shared module has 42 tests of its own (printing, refusal in dev and production under each policy, failures, aborts, timeouts, a throwing toString, yielding scripts, portability).

Verification

Local, on this commit's code:

  • typecheck, format, spelling: clean
  • unit:all: pass in every workspace, including the Cloudflare adapter's Node-versus-workerd byte parity suite
  • pkg e2e: static 581, dev 732, HTTP/1.1 706, HTTP/2 706, all pass
  • Cloudflare browser suite: 49 pass
  • linear-time: pass
  • integration: pkg 809 of 809 and docs 25 of 25 pass

Not verified locally:

  • Netlify browser suite: Netlify Dev returns 404 for function routes in a fresh git worktree, including on tests this branch does not touch. Left to CI.
  • skills and shop integration suites were still running when this was opened; neither touches code changed here.

Also in this branch

One line in the pre-push checklist: coverage-only commits can carry [skip ci], and the PR should then be merged with a merge commit so a squash message does not skip the deploy on main.

🤖 Generated with Claude Code

collin-thomas and others added 2 commits November 11, 2026 23:05
What a server or stream script's result becomes (printed, stripped of ANSI
escapes, scoped to its component, refused when it is not markup, reported
under scripts.onServerScriptError) was written three times: in the Node
server, the Cloudflare Worker and the Netlify function, with a fourth copy
in the custom adapter tutorial. Adding yielding scripts meant making the
same edit in each.

It now lives once, in pkg/src/lib/script-output-policy.ts, exported from
@bascik/bascik/runtime as settleScriptOutput and scriptJobOutput. Each host
passes a ScriptOutputPolicy naming only what is its own: where a warning
goes, how a failure is described (Node maps the stack to the authored
file; a function names the owner the build recorded), the error class it
throws, and how it remembers a script's first refused value.

No behavior changes. Characterization tests added to all three hosts
before the change pin the exact bytes of a failure report, a refusal
warning and stripped output, and pass unchanged after it.

Also notes in the pre-push checklist that coverage-only commits can carry
[skip ci].

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The commit before this one described the coverage-only convention and
spelled the marker out in its message, so GitHub ran no workflow for that
push. The pre-push checklist now says not to write it in any other
commit's message.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Site Previews

Built from f3b81d0 on shared-script-output-policy. Preview URLs stay the same for this pull request and show its newest successful build. Their hostname prefix is pr-<number> (or the branch name when there is no pull request), not the hosting provider. None of them is production.

Site Hosting resource Links
Bascik docs (Cloudflare branch preview) bascik / · /components · /deployment/cloudflare
Cloudflare adapter (branch preview) cloudflare-adapter / · /catalog · /catalog-stream
Netlify adapter (branch preview) Netlify: bascik-netlify-adapter / · /catalog · /catalog-stream

Deploy run

@collin-thomas
collin-thomas changed the base branch from pre-release-20261111 to main November 12, 2026 06:42
@collin-thomas
collin-thomas merged commit fd4c70e into main Nov 12, 2026
41 checks passed
@collin-thomas
collin-thomas deleted the shared-script-output-policy branch November 12, 2026 16:30
Sign up for free to 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.

1 participant