Skip to content

Releases: Smoren/transferum-ts

Sequence Guard, ordered execution, ConditionTransfer cleanup

Choose a tag to compare

@Smoren Smoren released this 24 Jul 21:56

Sequence Guard for AsyncConvertTransfer and AsyncConditionTransfer

  • Bug fix (issue #7): Race condition on _state.value when maxConcurrency > 1. Parallel async operations shared a single ProxyReference<T>, so a faster operation could overwrite or clear _state.value before a slower one emitted — subscribers received stale or undefined values, and emissions arrived out of order.
  • Sequence Guard: When maxConcurrency > 1, each operation receives a monotonic sequence number. Results are placed into an internal PendingResultQueue and emitted to subscribers strictly in data-arrival order. A faster result waits in the queue until all preceding operations have emitted. When maxConcurrency <= 1, the guard is inactive (zero overhead, backward-compatible).
  • AsyncConditionTransfer.shouldEmit now receives the operation's local data: T instead of the shared _state.value, eliminating cross-operation data leakage. The _state is written only at emission time inside the guarded _drain().
  • AsyncConditionTransfer.shouldEmit signature narrowed from (currentState: T | undefined) => Promise<boolean> | boolean to (data: T) => Promise<boolean> | booleanundefined is no longer possible at runtime. Backward-compatible via contravariance (a function accepting T | undefined is assignable to a parameter expecting T).
  • AsyncConvertTransfer and AsyncConditionTransfer destroy() now clears the pending result queue.
  • New helper classes (src/helpers.ts): PendingResultQueue<T> (ordered result queue with nextSeq()/submit()/drain()/clear()) and OrderedExecutor (sequential async task executor with submit()/reset()).

Ordered execution for AsyncSinkTransfer and AsyncWriteTransfer

  • New config option ordered?: boolean added to AsyncSinkTransferConfig and AsyncWriteTransferConfig (default: false, backward-compatible). When enabled, callback / flow.write() invocations are executed sequentially in data-arrival order via an internal OrderedExecutor (promise chain). The chain catches errors per-task so a throwing task does not block subsequent tasks; the error is still propagated to the asyncPush caller.
  • destroy() on both transfers now resets the executor.

ConditionTransfer cleanup

  • trigger() method removed from ConditionTransfer. It was not part of any implemented interface (isTriggerable was never set to true), and its existence created a race condition: shouldEmit could receive undefined from _state.value after _state.clear() in a prior push(). The push() method now inlines the shouldEmit check directly with the incoming data: T, making undefined impossible.
  • ConditionTransferConfig.shouldEmit signature narrowed from (currentState: T | undefined) => boolean to (data: T) => booleanundefined is no longer possible at runtime. Backward-compatible via contravariance.
  • README: Removed !== undefined guards from shouldEmit examples for both ConditionTransfer and AsyncConditionTransfer.

Infrastructure

  • API docs generator: typedoc added as devDependency; npm run docs script generates API reference to docs/api.
  • GitHub Actions: deploy-docs.yml workflow added — generates and deploys API docs to GitHub Pages on push to master.
  • .gitignore: docs directory added.
  • interfaces.ts: Unused OutputTransfer import removed.

Tests

  • Sequence Guard tests: Added race-condition regression tests for AsyncConvertTransfer (287 tests) and AsyncConditionTransfer (459 tests) — covering parallel ordered emission, stale-result prevention, failed-operation queue advancement, and destroy() queue cleanup.
  • Ordered execution tests: Added tests for AsyncSinkTransfer (264 tests) and AsyncWriteTransfer (284 tests) covering ordered callback/write execution, error isolation, and destroy() reset.
  • Helper tests: Added PendingResultQueue (237 tests) and OrderedExecutor (355 tests) test suites.
  • ConditionTransfer tests: Removed 3 tests that used trigger() manually; renamed 2 tests from "trigger" to "shouldEmit" naming; removed !== undefined guards from shouldEmit predicates.
  • Coverage: Maintained 100% test coverage (statements, branches, functions, lines) across all 11 source files. Total tests: 2,086.

DisplaceTransfer added

Choose a tag to compare

@Smoren Smoren released this 19 Jul 01:17

DisplaceTransfer — switch-map transfer with onDisplace callback

  • New transfer: DisplaceTransfer<TInput, TOutput, TInner> — for each input value, creates a new inner async-pushable + subscribable transfer via a factory function, pushes the value into it via asyncPush(), and forwards the inner's emissions to outer subscribers. On each new push(), the previous inner is unsubscribed and destroyed — only the latest inner's emissions pass through. RxJS equivalent: switchMap.
  • New factory: createDisplaceTransfer<TInput, TOutput, TInner>().

Documentation

  • README — DisplaceTransfer section: New subsection with capabilities, configuration (including onDisplace), RxJS equivalent, code example, and onDisplace usage example with a custom FetchTransfer (abort pattern).
  • README — Comparison tables: DisplaceTransfer added to Transfer Comparison Table, Transformation category, operator-equivalent coverage (switchMapDisplaceTransfer), and Gate/Flow control row.
  • README — Debounced search example: Updated to use DisplaceTransfer instead of AsyncConvertTransfer directly, demonstrating switch-map semantics.
  • JSDoc: DisplaceTransfer class and createDisplaceTransfer factory fully documented with mechanics, error handling, configuration, and use cases.

Tests

  • DisplaceTransfer test suite: 37 tests covering capability flags, basic push & subscribe, displace behavior (cancel previous inner), destroy (dispose inner, clean up subscriptions, idempotent), error handling (factory error with/without onError, previous inner kept active on factory error), real-world scenarios (fetch, WebSocket, readFile, search-as-you-type), async inner transfers (AsyncConvertTransfer, AsyncConditionTransfer), and onDisplace (called with previous inner, not called on first push/destroy, called before destroy, rapid push, custom cleanup, backward-compatible without callback, exception rethrown but inner still destroyed).
  • Factory tests: 8 tests for createDisplaceTransfer (type, push, displace, destroy, error handling, async inner).
  • Use-case tests: Updated debounced search test to use DisplaceTransfer; added displacement scenario test (slow query cancelled by fast query).
  • Coverage: Maintained 100% test coverage (statements, branches, functions, lines) across all 11 source files. Total tests: 2,029.

BridgeSelector and BridgeMultiSelector: syncWithChildren feature added

Choose a tag to compare

@Smoren Smoren released this 18 Jul 18:04

syncWithChildren for BridgeSelector and BridgeMultiSelector

  • New optional config field: syncWithChildren?: boolean added to BridgeSelectorConfig and BridgeMultiSelectorConfig (default false).
  • BridgeSelector: when enabled, the selector subscribes to onStateChange() of all child bridges. External activation of a child bridge switches selection to it (select()). External deactivation of the selected bridge deactivates the selector (deactivate()).
  • BridgeMultiSelector: when enabled, the selector subscribes to onStateChange() of all child bridges. External activation of a child bridge adds it to the selection (check()). External deactivation of a selected child bridge removes it from the selection (uncheck()).
  • Feedback loop prevention: an internal _syncing guard suppresses child state-change notifications while the selector is performing its own activate() / deactivate() / select() / check() / uncheck(), preventing recursive re-entry.
  • Cleanup: destroy() unsubscribes from all child state-change subscriptions before destroying owned bridges.
  • Backward-compatible: syncWithChildren defaults to false — existing code behaves exactly as before.

Tests

  • Added 11 tests for BridgeSelector syncWithChildren (external activation/deactivation, feedback loop prevention on activate/deactivate/select, disabled mode, no-op cases, inactive selector, destroy unsubscribe, onStateChange notification).
  • Added 14 tests for BridgeMultiSelector syncWithChildren (external activation/deactivation, feedback loop prevention on activate/deactivate/select/check/uncheck, disabled mode, no-op cases, inactive selector, destroy unsubscribe, onStateChange notification, multiple external activations accumulation).
  • Coverage: Maintained 100% test coverage (statements, branches, functions, lines) across all 11 source files. Total tests: 1,982.

Documentation updated

Choose a tag to compare

@Smoren Smoren released this 16 Jul 12:48

Documentation

  • README — Comparison section: Removed "Transferum vs Callbag" and "Transferum vs AsyncIterator" subsections, their table of contents entries, their columns from the Quick Comparison Table, and their entries from "When to Consider Alternatives." Neither Callbag (a spec, not a library) nor AsyncIterator (a built-in pull-only protocol) represents a realistic alternative for library selection.

Tests

  • No test changes. 100% coverage maintained. Total tests: 1,957.

Documentation improved

Choose a tag to compare

@Smoren Smoren released this 15 Jul 23:22

Documentation

  • README — Tagline: Replaced "A reactive data processing pipeline system for TypeScript" with "A language for describing interactions between components" — describes the essence, not an application.
  • README — Graph diagram: Added ASCII graph diagram illustrating transfers as nodes and bridges as edges, with the caption "This is a graph."
  • README — Architectural Invariants: Integrated the central philosophical formulation: "Behavior can be described as a composition of independent capabilities that simultaneously determine the type, the implementation, and the rules of interaction."
  • README — Audit corrections, verbosity reduction.
  • package.json: Description aligned with the new tagline.

Code style

  • src/transfers.ts: Transfer class definitions reordered for logical grouping — ChannelTransfer, StoredChannelTransfer, PushStoredChannelTransfer moved adjacent to PushChannelTransfer. No functional changes.
  • src/factories.ts: createPushStoredChannelTransfer factory moved adjacent to createPushChannelTransfer. Multi-line function signatures collapsed to single lines, trailing commas added for consistency. No functional changes.

Tests

  • No test changes. 100% coverage maintained. Total tests: 1,957.

Backpressure support added for some async transfers

Choose a tag to compare

@Smoren Smoren released this 15 Jul 20:57

Backpressure for async transfers

  • New shared config: BackpressureConfig<T> (maxConcurrency?, bufferSize?, onBufferOverflow?) added to configs.ts.
  • Four async transfers gained backpressure support: AsyncSinkTransfer, AsyncWriteTransfer, AsyncConvertTransfer, AsyncConditionTransfer. Their asyncPush() methods now route data through _process() / _dequeue() — limiting concurrent async operations, queuing excess data in an internal buffer, and invoking onBufferOverflow (or silently dropping) when both concurrency and buffer are full.
  • Backward-compatible: All backpressure options default to Infinity — existing code behaves exactly as before (unlimited parallel processing, no buffering).
  • destroy() on all four transfers now clears the internal buffer, discarding queued items.

Documentation

  • README — Backpressure section: New subsection under Async Transfers documenting BackpressureConfig<T> options, mechanics, and a usage example.
  • README — Key Benefits: Added "Built-in backpressure" row.
  • README — Async Transfer Comparison Table: Added "BP" (Backpressure) column.
  • README — Transfer descriptions: Added backpressure notes to AsyncSinkTransfer, AsyncWriteTransfer, AsyncConvertTransfer, AsyncConditionTransfer.
  • README — Configurations: Updated async configs table with maxConcurrency?, bufferSize?, onBufferOverflow? fields and BackpressureConfig<T> reference.

Tests

  • Backpressure tests: Added 8 backpressure test scenarios for each of the 4 async transfers (32 tests total): maxConcurrency sequential/parallel/default, bufferSize with/without onBufferOverflow, error-frees-slot, destroy() clears buffer.
  • Coverage: Maintained 100% test coverage (statements, branches, functions, lines) across all 11 source files. Total tests: 1,957.

Added project logo

Choose a tag to compare

@Smoren Smoren released this 12 Jul 16:43

Documentation

  • README: Added project logo.
  • README — Monitoring & Alerts: Rewritten example to use OutputPipelineBuilder with ConditionTransferThrottleTransferConvertTransfer (instead of manual PushChannelTransfer + subscribe). Updated imports and accompanying text.

Example tests

  • Domain-specific tests: Synced "Monitoring & Alerts" test with the updated README example — pipeline-based with concrete throttle assertion (1 alert out of ~5 polls).

Documentation fixed

Choose a tag to compare

@Smoren Smoren released this 12 Jul 06:33

Documentation

  • README: Examples fixed and improved.

Example tests

  • Use-cases tests: synced with README.
  • Domain-specific tests:: synced with README.
  • Coverage: Maintained 100% test coverage. Total tests: 1,925.

First stable release

Choose a tag to compare

@Smoren Smoren released this 11 Jul 16:16

Stable release

First stable release. The public API is now frozen — no breaking changes are planned within the v1.x line.

  • Codebase Stability: v1.0.0 (as well as v0.3.1) contains absolutely no changes to the src/ directory. The API surface is officially confirmed as stable.
  • API Stability Marker: v0.3.0 finalized the error handling model (ErrorHandler<TSource>, handleError(), per-stage handlers, fail-safe polling).

Documentation

  • Comparison Tables: Clarified operator counts by distinguishing pure operators (~10, stateless transforms) from flow-control transfers with explicit lifecycle.
  • API Mapping: Added an "Operator-equivalent coverage" row mapping RxJS operators to Transferum transfers (debounceTimeDebounceTransfer, filterConditionTransfer, mergeMergeTransfer, shareSplitTransfer, takeUntilGateTransfer, delayDelayedPushChannelTransfer).
  • Architecture Highlights: Updated the "Quick Comparison Table" with a "Flow-control as nodes" row, highlighting Transferum's transfer-based architecture versus operator-only alternatives.
  • Alternative Recommendations: The "When to Consider Alternatives" section for RxJS now lists specific uncovered operators (combineLatest, zip, withLatestFrom, bufferCount, windowTime, retryWhen) instead of using a generic operator-count argument.
  • Code Examples:
    • Expanded the "Debounced search" code comparison to include realistic error handling (catchError / onError) and empty-result suppression (second ConditionTransfer / filter).
    • Added a new code comparison: "Conditional routing with runtime switching" showcasing BridgeMultiSelector versus manual RxJS subscription management.

Tests

  • Test Suite Alignment: Replaced the "Debounced user input with async validation" test with "Debounced search with error handling and empty-result suppression" to perfectly match the updated README examples, covering API failure recovery and empty-result filtering.
  • Fixtures: Added the SearchResult type to test fixtures.
  • Coverage: Maintained 100% test coverage (statements, branches, functions, lines) across all 11 source files with a total of 1,927 tests.

Iterative pre-release

Choose a tag to compare

@Smoren Smoren released this 11 Jul 10:33

Documentation

  • README and keywords updated.