Bundle / foldkit-bundle

Bodies that load on demand: Bundle.lazy

A bundle's declaration is what the parent Schema and the boot need: Model, Message, args, init, and the subscriptions, resources and helpers Foldkit wires at boot. Its update and view can live in a chunk that loads on the first Message inside the bundle:

ts
const Upload = Bundle.lazy(
  {
    name: 'Upload',
    Model: UploadModel,
    Message: UploadMessage,
    init: () => ({ model: { name: '', percent: 0 } }),
    // Shown until the bodies load. Its handlers are what ask for them.
    while: Submodel.defineView<UploadModel, UploadMessage>((model, h) =>
      h.li([h.OnClick(UploadMessage.Progressed({ percent: 0 }))], [model.name]),
    ),
  },
  () => import('./upload.js').then(chunk => chunk.body),
)

The chunk exports a Bundle.Body, the update and the view:

ts
export const body: Bundle.Body<UploadModel, UploadMessage, void, never, never, void> = {
  update: (model, message) => ({ model: { ...model, percent: message.percent } }),
  view: Submodel.defineView<UploadModel, UploadMessage>((model, h) =>
    h.li([], [`${model.name} ${model.percent}%`]),
  ),
}

Until the bodies load, a Message reaching update leaves the Model as it is and returns one Command, which loads them and yields the same Message again, so nothing is lost and the Model ends where an eager bundle's would; the view renders while, or nothing. Upload.load() loads them ahead of time, once, and Upload.isLoaded() says whether they have. A load that fails is forgotten, so the next Message or load() tries again. Placed, extended or preset with with, the bundle stays lazy.

A Message that arrives before the bodies load passes through update twice: once as the no-op that starts the load, and again when the load re-yields it. The bundle's own Model ends where an eager bundle's would. Two things see both passes: a Bundle.mapUpdate wrapper around the bundle, and anything that replays recorded Messages through update after the load, such as Foldkit DevTools time travel, which then applies that Message twice. Load the bodies before the first interaction, as foldkit-ssr does before boot, or with load() when the page is idle, and no Message arrives early. subscriptions, resources and helpers stay in the declaration because Foldkit starts the first two at boot and a helper runs synchronously; none can wait for a chunk. test/lazy.test.ts runs this on the Foldkit runtime. foldkit-ssr loads every lazy bundle in a page's lazy list before it renders or boots, so a server-rendered page never shows while.