Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
97 changes: 89 additions & 8 deletions docs/rxjs-next/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,73 @@ been removed, and public declarations use explicit `unique symbol` types.
D-037. D-048 records the version, duplicate-copy, realm, and collision
consequences of that split.

## Proposed all-pipeable beta experiment

D-060 proposes exposing ordinary pipeable functions from the `rxjs` root while
retaining exact Symbols during beta. After two bounded review slices, the
maintainer approved expanding the experiment across the current catalog:

- `rx(input, ...functions)` converts `input` through the active platform
`Observable.from` and applies unary functions from left to right;
- the ambient `ObservableInput` union names the platform conversion boundary;
public `UnaryFunction<In, Out>` and `OperatorFunction<In, Out>` type aliases
describe composition without a redundant mono-type alias;
- 91 source-bound functions are root exports; six dual static/source
capabilities use `*With` operator names, while 12 static forms retain their
ordinary names;
- `operate` creates through the source's D-037 `[create]` protocol and catches
synchronous connection failures, while `subscribeToSource` continues to own
signal propagation and notification-callback safety;
- all current Symbol modules, imports, installations, and tests remain intact.

The first maintainer review selected a shared internal callback for behavior
implemented by both public forms. Pipeable `map` and exact-Symbol `[map]` now
apply the same `mapOperator(project)` callback through `operate`; neither public
surface delegates through the other. The second slice applies the same pattern
to `takeOperator(count)`.

The post-review slices also add terminal shapes. RxJS `toArray()` is an
`OperatorFunction<In, In[]>` that emits one array on source completion instead
of returning the Promise produced by the platform string method. The optional
`subscribe(observer)` terminal returns a minimal AbortSignal-backed
`Subscription` with `unsubscribe()` and a live `closed` getter; it is not an
RxJS 7 Subscription tree. The four async-iteration terminals return their
exact `AsyncGenerator` types, so `rx(source, iterateEachValue())` crosses out of
the Observable graph without result coercion.

The complete additive layout has `rxjs/pipeable`, `rxjs/static`, and
`rxjs/symbol` barrels plus per-capability paths. Existing `rxjs/map`-style
paths still export their Symbols; switching those paths remains a separate
breaking decision. `rxjs/rx`, `rxjs/to-array`, `rxjs/subscribe`, and the six
new `*With` names also have direct paths.

The broad facade is generated from the ambient instance/static Symbol
signatures, preserving overloads, type guards, tuple results, and explicit
`this` receiver restrictions. `map` and `take` keep their reviewed shared
`operate` callbacks. Other generated functions delegate to the corresponding
exact-Symbol implementation as a branch-by-abstraction seam. A freshness gate
reconciles 91 operators, 12 statics, and all 98 exported Symbols including the
package-private D-037 construction protocol.

That seam changes root import behavior: the root re-exports load the shared
side-effectful Symbol modules, so the prior root-core-only installation
contract no longer holds on this branch. Focused deep imports remain available,
and final acceptance requires explicit bundle-retention evidence or a further
split between implementation and Symbol installation.

The initial `rx` declaration uses explicit overloads through nine
transformations and an `unknown` fallback after that point. This makes the
type-safety horizon and overload-resolution cost visible. The alternative of
recursive variadic-tuple typing remains a proposal until representative
TypeScript projects measure checking time and diagnostic quality.

This experiment does not change Observable lifecycle semantics. Pipeable
results use the same construction protocol, active-producer sharing,
AbortSignal cancellation, and source-subscription safety as the corresponding
platform-layer Symbol design. See
`packages/rxjs/docs/PIPEABLE_EXPERIMENT.md` for the detailed comparison and
open review gates.

## Current API inventory

This inventory documents what exists in source, not a supported public API.
Expand Down Expand Up @@ -970,8 +1037,11 @@ and none of these contracts creates an RxJS 7 compatibility claim. See
`@rxjs/observable-polyfill`.
- Every public `rxjs` source entry reaches the conditional initializer before
reading or extending `Observable`.
- The root source exports the approved non-operator core. Each public source
subpath has one ESM runtime and declaration export.
- The P6.12 experiment exports the complete functional catalog from the root
alongside the approved non-operator core. Because most generated facades
currently share their exact-Symbol modules, evaluating that root installs
those exact keys. Each public source subpath still has one ESM runtime and
declaration export.
- The polyfill's ambient declarations are emitted from its package entry.
- All four release packages build one ESM output without self-links or source
specs in the packed artifact. Browser, Webpack, `import`, and Node
Expand All @@ -987,21 +1057,32 @@ The published runtime map has three products:
| Package | Accepted responsibility |
| --------------------------- | -------------------------------------------------------------------------------------------------- |
| `@rxjs/observable-polyfill` | Independently publishable conditional fallback and owner of the base ambient platform declarations |
| `rxjs` | Symbol extensions plus intentional non-operator RxJS Next classes and values |
| `rxjs` | Symbol extensions, experimental functional facades, and intentional RxJS Next classes and values |
| `@rxjs/test` | Implementation-neutral test harness that consumes an already initialized realm |

`@rxjs/observable` has no target role and is removed. No runtime
compatibility package replaces it.

`rxjs` declares a runtime dependency on `@rxjs/observable-polyfill`. Every
public root or subpath import first evaluates the conditional initializer. The
root exports non-operator core values—cold, Subject, connectable, notification,
and public-error primitives—without installing the operator and factory
catalog. An operator or factory subpath installs only its exported exact Symbol
capability and internal kernel dependencies.
accepted pre-P6.12 root exported non-operator core values—cold, Subject,
connectable, notification, and public-error primitives—without installing the
operator and factory catalog. The P6.12 branch deliberately replaces that
property for review: root functional re-exports share most exact-Symbol
implementation modules and therefore install the catalog as they evaluate.
Focused exact-Symbol subpaths retain their established behavior, while focused
`rxjs/pipeable/*` and `rxjs/static/*` paths bound the requested functional
graph.

This is not yet accepted release architecture. The production Webpack fixture
emits 64,874 bytes against its 22,000-byte ceiling with the experimental root.
That expected failure quantifies the root-retention risk; it is not permission
to raise the release budget. P6.12 remains review-gated on either separating
shared implementations from installation or explicitly accepting a different
root contract and its measured cost.

The polyfill package owns the ambient TypeScript declarations for
`Observable`, `Subscriber`, `ObservableValue`, and `EventTarget.when`.
`Observable`, `Subscriber`, `ObservableInput`, and `EventTarget.when`.
Individual `rxjs` entry points augment those base declarations only with the
Symbols they export. `@rxjs/test` imports the public `ColdObservable` entry;
that entry preserves an existing constructor or conditionally initializes the
Expand Down
73 changes: 49 additions & 24 deletions docs/rxjs-next/COMPATIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

RxJS Next reuses the behavioral knowledge in RxJS 7 tests where the represented
expectations remain meaningful. It does not ship a separate runtime package
that emulates RxJS 7 imports, `Subscription`, pipeable operators, schedulers, or
deprecated aliases.
that emulates RxJS 7, schedulers, or deprecated aliases. The accepted baseline
uses exact Symbols; D-060 now proposes a real RxJS Next pipeable surface in the
same package during beta. That proposal is a new public contract, not an RxJS 7
runtime-emulation package.

Useful APIs such as `ColdObservable`, Subjects, and the Symbol-keyed `pipe` may
remain in `rxjs` as intentional RxJS Next capabilities with their own explicit
Expand Down Expand Up @@ -55,8 +57,8 @@ The table describes architectural defaults, not every edge case.
| Teardown registration | Producer may return teardown logic; subscriptions aggregate finalizers | Producer calls `subscriber.addTeardown()` | Rewrite custom producers rather than relying on returned teardown functions |
| Teardown order | RxJS 7 subscription finalizers follow RxJS aggregation semantics | The platform specification closes teardown callbacks in reverse insertion order | Treat order-sensitive teardown as a semantic migration |
| Error reporting | RxJS configuration and host error reporting rules | Platform exception reporting and Web IDL callback behavior | Audit unhandled, late, and observer-callback errors |
| Operators | Mostly standalone pipeable functions returning Observables | Platform string methods plus exact RxJS Symbol extensions | Migrate imports and invocation shape; verify lifecycle-sensitive behavior |
| Pipe | `pipe(...)`, `source.pipe(...)`, and `OperatorFunction` types | Exact Symbol-keyed `pipe` may remain as a Next API | Do not assume RxJS 7 pipeable functions or types exist |
| Operators | Mostly standalone pipeable functions returning Observables | Exact Symbols plus D-060's proposed root pipeable surface | Familiar syntax does not remove lifecycle and overload review |
| Pipe | `pipe(...)`, `source.pipe(...)`, and `OperatorFunction` types | Exact Symbol-keyed `pipe`; proposed `rx` and new Next composition types | Do not assume RxJS 7 imports, overloads, or lifecycle behavior |
| Subjects | Subject family with established RxJS 7 semantics | Intentional Next Subject APIs with directly documented contracts | Verify late-observer, replay, terminal, and cancellation semantics |
| Scheduling | Scheduler arguments and scheduler classes affect many APIs | Host APIs and `@rxjs/test`; no general public RxJS scheduler abstraction | Remove scheduler arguments and review timing-sensitive code |
| Input conversion | Broad `ObservableInput` ecosystem and interop protocols | Platform `Observable.from` conversion order and categories | Audit custom subscribables and legacy interop |
Expand Down Expand Up @@ -102,21 +104,25 @@ helpers, and other APIs when they are useful on their own terms. They must:
shared platform lifecycle;
- keep unsupported RxJS 7 behavior visible in migration guidance.

They do not provide an RxJS 7 `Subscription` facade, pipeable-operator import
surface, scheduler system, deprecated aliases, or compatibility package.
The accepted APIs do not provide an RxJS 7 `Subscription` facade, scheduler
system, deprecated aliases, or compatibility package. D-060's pipeable pilot
adds new root functions and familiar composition types on their own RxJS Next
contracts; the full catalog, deep-import layout, and lite `subscribe` facade
remain unimplemented review items.

D-050 stabilizes `ColdObservable`, `PerSubscriptionSubjectBase`, the Subject
family, and the Symbol-keyed `pipe` as this intentional Next surface.

| Intentional API | Public form | Own contract |
| ---------------------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `ColdObservable` | Root and `rxjs/cold-observable` class | One producer and compatibility Subscriber per direct `subscribe()` call |
| `Subject` | Root and `rxjs/subject` class | Hot live fanout plus a non-mutating `asObservable()` platform view |
| `AsyncSubject` | Root and `rxjs/async-subject` class | Hot final-value-on-completion fanout with retained terminal behavior |
| Behavior subject | Root and `rxjs/behavior-subject` lowercase factory | Hot current-value delivery for every direct observer |
| Replay subject | Root and `rxjs/replay-subject` lowercase factory | Hot size/host-time-bounded replay followed by live fanout |
| `PerSubscriptionSubjectBase` | Root and explicit advanced-base subpath | Protected per-direct-observer setup hook for specialized hot Subject implementations |
| Exact Symbol-keyed `pipe` | `rxjs/pipe` static and instance Symbol | Typed one-to-seven-step composition; no `.pipe`, pipeable imports, or `OperatorFunction` claim |
| Intentional API | Public form | Own contract |
| ---------------------------- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `ColdObservable` | Root and `rxjs/cold-observable` class | One producer and compatibility Subscriber per direct `subscribe()` call |
| `Subject` | Root and `rxjs/subject` class | Hot live fanout plus a non-mutating `asObservable()` platform view |
| `AsyncSubject` | Root and `rxjs/async-subject` class | Hot final-value-on-completion fanout with retained terminal behavior |
| Behavior subject | Root and `rxjs/behavior-subject` lowercase factory | Hot current-value delivery for every direct observer |
| Replay subject | Root and `rxjs/replay-subject` lowercase factory | Hot size/host-time-bounded replay followed by live fanout |
| `PerSubscriptionSubjectBase` | Root and explicit advanced-base subpath | Protected per-direct-observer setup hook for specialized hot Subject implementations |
| Exact Symbol-keyed `pipe` | `rxjs/pipe` static and instance Symbol | Typed one-to-seven-step composition; no `.pipe`, pipeable imports, or `OperatorFunction` claim |
| Pipeable pilot | Root `rx`, `map`, `filter`, `take`, `toArray`, `subscribe`, and composition types | New Next contracts with a nine-transformation `rx` horizon and a minimal AbortSignal-backed subscription handle |

`PerSubscriptionSubjectBase` is hot: its Subject producer exists as soon as the
instance is constructed. Its distinction from `Subject` is that it inherits
Expand Down Expand Up @@ -227,11 +233,28 @@ support:
- explicit selection when the pipeline moves between shared platform and
producer-per-subscription Next semantics.

The exact Symbol-addressed `[pipe]` is an intentional Next API. It does not
promise RxJS 7 `OperatorFunction` types, standalone pipeable imports, or the
string-named `.pipe`. Skills may transform an old pipeline to imported Symbols
and `[pipe]` or to direct Symbol composition, then flag lifecycle-sensitive
segments for review.
The exact Symbol-addressed `[pipe]` remains an intentional Next API. D-060's
parallel experiment now adds root `rx`, 91 source-bound functions, 12 static
functions, Observable-returning `toArray`, `UnaryFunction`, and
`OperatorFunction`. `OperatorFunction<T, T>` replaces the redundant
`MonoTypeOperatorFunction` alias. Six dual static/source capabilities use
`*With` names for their pipeable forms. These are new Next contracts and do
not imply that RxJS 7 scheduler overloads, broad `ObservableInput`, or full
`Subscription` behavior are restored. The optional `subscribe` terminal
exposes only `unsubscribe()` and a live `closed` getter backed by one
AbortSignal.

The four async-iteration functions deliberately return `AsyncGenerator`
instead of Observable. The complete additive import layout exposes
`rxjs/pipeable`, `rxjs/static`, and `rxjs/symbol` barrels and focused paths;
established `rxjs/map`-style paths continue to export Symbols until a separate
breaking package-layout decision is accepted.

The initial `rx` overloads preserve exact types through nine transformations
and return `unknown` for longer chains. Migration tooling must not hide that
boundary with `any`. It may split a longer pipeline into typed segments, but it
must retain lifecycle-sensitive review and cannot treat familiar pipeable
syntax as proof of RxJS 7 semantic compatibility.

## Testing and migration-evidence boundary

Expand Down Expand Up @@ -400,10 +423,12 @@ whether the RxJS form delegates, which additional functionality it supplies,
and which behavior or types intentionally differ. It must also prove that
installing the Symbol does not alter the platform method.

The P2.4 `map` pilot is the first recorded overlap. `observable[map](project,
thisArg?)` owns an RxJS projection index and optional callback receiver,
constructs through the RxJS `[create]` protocol, and participates in the
platform layer's shared activation lifecycle. It does not delegate to
The P2.4 `map` pilot is the first recorded overlap. `observable[map](project)`
owns an RxJS projection index, constructs through the RxJS `[create]` protocol,
and participates in the platform layer's shared activation lifecycle. D-059
removes the inherited RxJS 7 callback-receiver argument from this and every
other RxJS Next callback API; migration uses a closure or
`Function.prototype.bind`. The Symbol form does not delegate to
`observable.map(project)`, and installing it leaves that platform-owned string
method unchanged. Focused and native/fallback kernel tests cover both the
additional Symbol behavior and non-interference.
Expand Down
Loading