Skip to content

Improve saga dispatch concurrency and cleanup - #33

Open
misak113 wants to merge 2 commits into
masterfrom
mz/aperol-v3
Open

Improve saga dispatch concurrency and cleanup#33
misak113 wants to merge 2 commits into
masterfrom
mz/aperol-v3

Conversation

@misak113

@misak113 misak113 commented Jul 16, 2026

Copy link
Copy Markdown
Owner

Summary

  • Port saga routing and concurrency changes from signageOS Box MR 3682 into maintainable Aperol TypeScript.
  • Route updater construction by actionTypes while delivering every action to every reducer, with duplicate route declarations deduplicated.
  • Run matching leaf sagas concurrently, wait for every sibling to settle, report every failure, and preserve nested action error ownership.
  • Add update-completion callbacks, robust non-Error rejection normalization, safe callback handling, and deterministic observable/subscription cleanup.
  • Keep action completion promises compatible with frozen, reused, and legacy actions.

Compatibility

  • Custom sagas must declare handled actionTypes; use ANY_ACTION for wildcard updater routing.
  • Reducers continue receiving every action regardless of updater routing.
  • README and changelog document public behavior and onUpdateComplete contract.

Validation

  • npm run lint
  • npm test — 33 passing
  • npm run build

Source

  • Initial port: GitLab commit a304d1d
  • Follow-up synchronization: GitLab commit 60d6b52

misak113 and others added 2 commits July 16, 2026 14:00
Index combined sagas by handled action type, run matching leaf sagas independently, and track action completion without mutating frozen actions. Clean up finished iterators and observable subscriptions while preserving legacy action promise compatibility.

Ported from signageOS Box MR 3682 commit a304d1d005f846f00261b402aff0a912653ba128.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Port follow-up behavior from Box MR 3682, including updater routing, reducer delivery, fan-out settlement, completion callbacks, error normalization, and lifecycle cleanup.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors Aperol’s saga execution model to route updater construction by declared actionTypes, run matching leaf sagas concurrently, improve error propagation/reporting, and make observable subscription cleanup deterministic. It also introduces a WeakMap-backed action completion promise API to support frozen/reused actions, and updates documentation + tests accordingly.

Changes:

  • Add actionTypes declarations to sagas and introduce ANY_ACTION / saga-group matching to route only relevant updaters while reducers still receive every action.
  • Execute matching leaf sagas concurrently, wait for all siblings to settle, and normalize/report updater rejections (including nested-action failure ownership).
  • Replace direct action.__promise mutation with a WeakMap-backed ActionPromise helper (while keeping legacy __promise compatibility), plus improved observable subscription lifecycle handling.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/unit/sumModelMock.ts Adds actionTypes declarations to test sagas for updater routing.
tests/unit/createModelSaga.spec.ts Expands unit coverage for concurrency, action promise behavior, rejection normalization, callbacks, and subscription cleanup.
tests/unit/combineSagas.spec.ts Adds tests for required actionTypes, reducer-vs-updater routing behavior, wildcard routing, and deduped routes.
tests/unit/ActionPromise.spec.ts New unit tests for WeakMap-backed action promise storage and legacy compatibility.
tests/integration/Profiler/profiler.spec.ts Updates profiler integration expectations to include sagaKey and nested saga keys.
tests/integration/index.spec.ts Verifies new public exports (ANY_ACTION, getActionPromise) are available from the package entrypoint.
src/SagaGroup.ts Introduces saga-group matching (MATCH_SAGAS), leaf saga identification, and ANY_ACTION.
src/Profiler/profiler.ts Adds saga-key-aware profiler warnings and reduces profiling overhead when disabled.
src/ISaga.ts Extends saga contract to require actionTypes for updater routing.
src/index.ts Exports ANY_ACTION and getActionPromise publicly.
src/createModelSaga.ts Implements concurrent leaf saga execution, normalized error handling, completion callbacks, and deterministic observable subscription cleanup.
src/combineSagas.ts Builds actionType->saga routing index, supports nested groups, preserves reducer identity, and keeps a flattened updater fallback.
src/ActionPromise.ts Adds WeakMap-backed promise storage with best-effort legacy __promise attachment.
README.md Documents required actionTypes, wildcard usage, and onUpdateComplete contract.
CHANGELOG.md Records new concurrency/routing behavior and related fixes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/createModelSaga.ts
Comment on lines +285 to +290
const matched: IMatchedSaga[] = [];
if (isSagaGroup<TModel>(saga)) {
saga[MATCH_SAGAS](model, action, '', matched);
} else if (saga.actionTypes.indexOf(action.type) !== -1 || saga.actionTypes.indexOf(ANY_ACTION) !== -1) {
matched.push({ key: '', iterator: saga.updater(model, action) });
}
return action.type === ObservableSubscribed;
}

describe('Application.craeteModelSaga', function () {
@misak113

Copy link
Copy Markdown
Owner Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants