Skip to content

Browsers and bundlers ​

Effect Prophet runs in browsers as well as Node.js, from the same package and the same import. There is nothing to configure for Vite, webpack or Rspack. esbuild needs one file copied.

Every change is tested in Chromium, Firefox and WebKit (the engine behind Safari) with every bundler below. All three give exactly the same forecasts as Node.js, because they all run the same engine.

How the engine loads ​

The math runs in a WebAssembly binary, prophet_wasm_bg.wasm, which loads the first time a fit or forecast needs it.

  • Node.js reads the binary from disk and compiles it right away, so Effect.runSync works for fitting and forecasting. Country holiday calendars still need Effect.runPromise, because their files load asynchronously.
  • Browsers download and compile the binary once per page, asynchronously, and later calls reuse it. Run your programs with Effect.runPromise or Effect.runFork. Effect.runSync fails on the call that has to wait for the download.

The first call that loads the engine has wasm.cold_start set on its trace, so the download isn't mistaken for a slow fit.

Each country holiday calendar is a separate file that loads the first time a fit or inspectCountryHolidays uses that country. Bundlers turn each one into its own small chunk, so a page downloads only the countries it uses. Prediction from a fitted or saved model never loads a calendar file, because the model carries the calendar it was fitted with.

Sizes ​

Measured with a Vite production build:

FileSizeGzipped
WebAssembly engine263 kB104 kB
One country calendar10 kB3.6 kB
Largest country calendar47 kB12 kB

The country figure is the median of the 250 calendars. Your JavaScript bundle also includes Effect Prophet's code and the parts of Effect it uses.

Vite ​

Works with vite build and the dev server, with no configuration. Vite emits the engine as an asset and each calendar as a chunk.

webpack ​

Works with webpack 5 and no configuration. webpack emits the engine as an asset and each calendar as a chunk.

Rspack ​

Works with Rspack 2 and no configuration, the same way as webpack.

esbuild ​

esbuild bundles the code and, with splitting: true, puts each calendar in its own chunk. It doesn't copy the engine binary, so copy it next to your bundle yourself:

ts
import { copyFile } from "node:fs/promises";
import { build } from "esbuild";

await build({
  entryPoints: ["src/main.ts"],
  bundle: true,
  format: "esm",
  splitting: true,
  outdir: "dist",
});

await copyFile(
  "node_modules/effect-prophet/wasm/prophet_wasm_bg.wasm",
  "dist/prophet_wasm_bg.wasm",
);

Without splitting, every calendar ends up inside your main bundle.

Serving the files ​

Serve .wasm files with the application/wasm content type. Browsers can then compile the engine while it downloads. With another content type it still works, but more slowly, and the browser console shows a warning.

If a file can't be loaded, the operation fails with a typed error rather than throwing: the engine gives a FittingError or PredictionError with reason: "backend-failure" and backendPhase: "load", and a calendar gives a CountryHolidayError with reason: "snapshot-unavailable". See Handling errors.

Inspired by Prophet from Meta. Not affiliated with Meta.