Why js-message

Normalize once. Move messages anywhere.

Use one small boundary primitive when your application needs a predictable convention—not another transport framework, schema engine, or dependency tree.

js-message works with bundlers and without a bundler. Bundlers resolve bare imports normally; native browser ESM uses a standard import map with no build or transpilation step.

The practical case

Less boundary code to own.

A message can be complete, partial, incoming as JSON, or already an object. js-message copies the fields that exist and stays out of the payload.

Optional by design

No dummy payloads

{ type: 'ping' } is a valid signal. Missing fields remain undefined; applications choose whether they are required.

One Node source

Import or require

Node 22.13+ loads the same synchronous Message.js constructor through ESM and CommonJS. There is no duplicate build to drift.

Recovery as data

Inspect malformed input

A failed parse becomes a normal type: 'error' message with the original response and useful error details.

Small on purpose

Zero runtime dependencies

No transpiler, generated runtime, framework binding, install hook, timer, listener, or hidden network request.

Use it when

Your transports need one convention.

  • WebSockets, workers, IPC, fetch, or storage exchange JavaScript messages.
  • Node services mix ESM and CommonJS during a migration.
  • Some signals carry payloads and others do not.
  • You want parse failures preserved without repeating recovery code.

Choose something else when

You need enforcement or transport.

  • Use a schema validator when fields and payload types must be rejected.
  • Use a protocol client when you need delivery, retries, authentication, or persistence.
  • Use native objects directly when one boundary never needs normalization or JSON recovery.
Small enough to understand. Fast enough to disappear.

Review the measured path, then run the real module before choosing it.