Surface / foldkit-surface

Diagnostics

Diagnostics is the one channel every Foldkit Plus package reports into what it met and kept going past: a fallback, a refusal it answered generically, a failure it swallowed, a check run in development. This package owns the channel, not the reports: each package declares its own variants on the base Diagnostic (_tag, source, level, message) and lists them in its README.

A report goes to the Diagnostics.Reporter in the Effect context, which by default is Effect's log at the diagnostic's level (an error reaches console.error, or stderr on a server). Replace it to send reports elsewhere, and collect them in a test:

ts
import { Effect, Layer } from 'effect'
import { Diagnostics, type Diagnostic } from 'foldkit-surface'

declare const send: (diagnostic: Diagnostic) => Effect.Effect<void>

// In a browser runtime's `resources`: log as before, and send.
const reporting = Diagnostics.layer(diagnostic =>
  Effect.andThen(Diagnostics.log(diagnostic), send(diagnostic)),
)

// On a server, where no Mount runs, the reporter alone is enough.
const onServer = Layer.succeed(Diagnostics.Reporter, Diagnostics.log)

// In a test: provide `report`, then assert on `reported`.
const { report, reported } = Diagnostics.collect()
const inTest = Layer.succeed(Diagnostics.Reporter, report)

Diagnostics.report(diagnostic) reports one and never fails: a reporter that breaks is logged with what it was given.

Mounts. Foldkit runs a Mount in the runtime's own context, not its resources, so a reporter there reaches Commands and Subscriptions but no Mount. A Mount reports with Diagnostics.dispatch(element, diagnostic) instead: a DiagnosticEvent at its element, which Diagnostics.layer(reporter) claims for reporter while the runtime holds its resources. Nothing claims it on a page without that layer, and it goes to the reporter in context, the log. Two limits: Foldkit builds resources at the first Command or Subscription (before init with Flags), so a Mount that fails earlier is logged; and with two runtimes in one document, the first to build its resources claims both runtimes' events. Only events foldkit-surface built are claimed, so a script dispatching a foldkit-diagnostic event cannot reach your reporter.

Production. The default is the same in every build: this package cannot tell a production build from a development one in every bundler. A page that should not show warnings (which ask a developer to change something) chooses Diagnostics.quiet, which logs errors and drops warnings, where it knows its mode: Diagnostics.layer(import.meta.env.DEV ? Diagnostics.log : Diagnostics.quiet). In development, foldkit-mixins' DevOverlay lists what was reported on the page.