Summary
createChannelFromSelector (src/utils/sagas/selector-channel-effects.ts, @augmentcode/themis@0.2.7) emits the selector's initial value synchronously inside the eventChannel subscriber, before any saga has taken from the channel:
return eventChannel((emitter) => {
...
const unsubscribe = reduxStore.subscribe(emitCurrentValue);
emitCurrentValue(); // synchronous, no taker registered yet
return unsubscribe;
});
eventChannel defaults to buffers.none(), so with no taker attached that first emitter(...) call is dropped. takeEveryFromSelector / takeLatestFromSelector / takeLeadingFromSelector therefore only observe changes after channel creation and never the value the store already held. The JSDoc / skill docs describe the first emission as arriving with prevPayload null, which suggests the initial delivery is intended.
Impact
For a selector whose value is already correct at saga start and never changes, the worker never runs at all. A saga written as "run once on the current value, then on changes" silently skips initialization — nothing throws, no type or lint fires.
Seen in cloudlands-fe: a presence saga watching a window-backend id that starts as and hydrates to the same value never seeds its principal lookup (intent-hq/intent#5008).
Proposed fix
Give the channel a buffer so the initial emission is retained until the first take, e.g. eventChannel(subscribe, buffers.sliding(1)). (Alternative: defer emitCurrentValue() until the first taker attaches.)
Regression test: a takeEveryFromSelector worker over a selector whose value never changes must run exactly once with the initial value and prevPayload == null.
Interim
cloudlands-fe carries a pnpm patch applying the buffers.sliding(1) fix to dist/utils/sagas/selector-channel-effects.js; it will be retired once a Themis release ships this.
Summary
createChannelFromSelector(src/utils/sagas/selector-channel-effects.ts,@augmentcode/themis@0.2.7) emits the selector's initial value synchronously inside theeventChannelsubscriber, before any saga hastaken from the channel:eventChanneldefaults tobuffers.none(), so with no taker attached that firstemitter(...)call is dropped.takeEveryFromSelector/takeLatestFromSelector/takeLeadingFromSelectortherefore only observe changes after channel creation and never the value the store already held. The JSDoc / skill docs describe the first emission as arriving withprevPayloadnull, which suggests the initial delivery is intended.Impact
For a selector whose value is already correct at saga start and never changes, the worker never runs at all. A saga written as "run once on the current value, then on changes" silently skips initialization — nothing throws, no type or lint fires.
Seen in cloudlands-fe: a presence saga watching a window-backend id that starts as and hydrates to the same value never seeds its principal lookup (intent-hq/intent#5008).
Proposed fix
Give the channel a buffer so the initial emission is retained until the first
take, e.g.eventChannel(subscribe, buffers.sliding(1)). (Alternative: deferemitCurrentValue()until the first taker attaches.)Regression test: a
takeEveryFromSelectorworker over a selector whose value never changes must run exactly once with the initial value andprevPayload == null.Interim
cloudlands-fe carries a
pnpm patchapplying thebuffers.sliding(1)fix todist/utils/sagas/selector-channel-effects.js; it will be retired once a Themis release ships this.