Skip to content

Flux Migration ​

3.0 ​

Flux 3.0 removes package-specific adapter subpaths in favor of two dependency-free structural bridges. Flux owns push-stream behavior; state, event buses, transport, and presence remain owned by their source packages.

Replace snapshot adapters with fromStore() ​

fromStore() requires both snapshot and subscription operations. It emits the current snapshot, closes subscription-time update gaps, and reads a new snapshot after every notification.

diff
- import { fromSignal } from '@vielzeug/flux/ripple';
+ import { fromStore } from '@vielzeug/flux';

- const updates = fromSignal(signal);
+ const updates = fromStore({
+   getSnapshot: () => signal.peek(),
+   subscribe: (listener) => signal.subscribe(listener),
+ });

Use the same shape for Sourcerer query handles and presence state:

ts
const updates = fromStore({
  getSnapshot: () => query.snapshot,
  subscribe: (listener) => query.subscribe(listener),
});

Replace event adapters with fromSubscribe() ​

fromSubscribe() receives a value-listener registration function rather than a snapshot object:

diff
- import { fromBus } from '@vielzeug/flux/herald';
+ import { fromSubscribe } from '@vielzeug/flux';

- const messages = fromBus(bus, 'message');
+ const messages = fromSubscribe<Message>((listener) => bus.on('message', listener));

Pulse events use the same form:

ts
const messages = fromSubscribe<Message>((listener) => pulse.on('message', listener));

Pulse presence already implements the structural store contract:

ts
const presence = fromStore(room.presence);

Replace sink-side adapters explicitly ​

toSignal() and toBus() are removed. Subscribe at the state or event boundary that owns writes:

ts
using subscription = source.subscribe({
  error: reportError,
  next: (value) => {
    target.value = value;
  },
});
ts
using subscription = source.subscribe({
  error: reportError,
  next: (value) => bus.emit('message', value),
});

Removed entry points ​

The following subpaths are removed:

  • @vielzeug/flux/courier
  • @vielzeug/flux/herald
  • @vielzeug/flux/pulse
  • @vielzeug/flux/ripple

Courier query state moved to Sourcerer, so adapt a query handle with fromStore() rather than restoring fromQuery().

Recheck runtime boundaries ​

  • Timer-backed durations range from 0 through 2,147,483,647 milliseconds.
  • first(), last(), and toArray() preserve custom abort reasons.
  • ValueOptions<T>.defaultValue must match the stream value type.
  • Async-iterator overflow rejects with FluxCapacityError; invalid policies throw RangeError, and external cancellation rejects with signal.reason.
  • takeUntil() accepts cross-realm AbortSignals.
  • Rejected async-iterator teardown is reported through the platform error channel.
  • Channel disposal releases replay and pending values.
  • Error names and NodeNext declaration specifiers remain stable in published artifacts.

2.0 ​

Flux 2.0 redesigned streams around returned teardowns, pipe(), explicit buffers, channels, and terminal consumer names.

Replace subscription cleanup ​

Retain the Subscription returned by subscribe() and call unsubscribe(), or own it with using and [Symbol.dispose]().

Compose with pipe() ​

Build stream transformations through pipe() and current creation, transformation, filtering, combination, and terminal operators.

Make buffering explicit ​

Configure buffering and overflow behavior on flattening, collection, and async-iteration boundaries. Review every source that can outpace its consumer.