npm.io
0.0.5 • Published 6d ago

@playfast/reform-resource

Licence
MIT
Version
0.0.5
Deps
0
Size
14 kB
Vulns
0
Weekly
357

@playfast/reform-resource

Async resources for Reform.

Reform renders synchronously, so it builds a scene's whole layer graph with runSync. A layer whose acquire does async work (open a connection, fetch config, Effect.sleep) breaks that build — reform now reports it as AsyncSceneLayer and points you here.

A Resource is the sanctioned home for that async work:

  • its live layer builds synchronously, seeding a pending value;
  • it forks the load under the scene scope (so acquireRelease finalizers release when the scene unmounts);
  • when the load settles it flips a reactive store to ready (or failed), which re-renders any composition that read the resource — exactly like a state/calc.

Unlike AsyncCalc, a Resource has no reactive inputs and no query driver: it is a one-shot, scoped load.

Usage

import { Resource } from '@playfast/reform-resource'
import { Effect, Schema as S } from 'effect'

// Define — output schema shapes the ready value; add `error` for a failed arm.
class Config extends Resource.make('Config', { output: S.Struct({ url: S.String }) }) {}

// Wire — `acquire` may be scoped (acquire/release tied to the scene scope).
const ConfigLive = Resource.live(
  Config,
  Effect.acquireRelease(openConfig, closeConfig),
)

// Read inside a composition.
const view = Effect.gen(function* () {
  const config = yield* Config
  return config.isReady ? render(config.value) : spinner()
})

The value is a union discriminated by isReady:

type AsyncResource<A, E = never> =
  | { _tag: 'AsyncResource'; isReady: false; failed: false }       // pending
  | { _tag: 'AsyncResource'; isReady: true;  value: A }            // ready
  | { _tag: 'AsyncResource'; isReady: false; failed: true; error: E } // only when Enever

The public API is re-exported from the package index.

Keywords