From ba5f41c0860f8e44ff1bd599b7326682469064e6 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Sat, 26 Sep 2026 09:23:31 +0000 Subject: [PATCH 1/2] Add RFC: WarpDrive: Deprecating Subscription-Based Request State --- ...cating-subscription-based-request-state.md | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 text/0000-deprecating-subscription-based-request-state.md diff --git a/text/0000-deprecating-subscription-based-request-state.md b/text/0000-deprecating-subscription-based-request-state.md new file mode 100644 index 0000000000..abff1ca1e7 --- /dev/null +++ b/text/0000-deprecating-subscription-based-request-state.md @@ -0,0 +1,158 @@ +--- +stage: proposed +start-date: 2026-09-26T00:00:00.000Z +release-date: +release-versions: +teams: + - data + - learning +prs: + accepted: +project-link: +suite: +--- + +# WarpDrive: Deprecating Subscription-Based Request State + +## Summary + +The imperative request-state API — `store.getRequestStateService()` and the subscription and +lookup methods it exposes (`subscribeForRecord`, `getPendingRequestsForRecord`, +`getLastRequestForRecord`) — is deprecated in favor of the signals-backed reactive request state +introduced by [RFC 0001, Transactional Notification Delivery and Reactive Request +State](./0001-warp-drive-transactional-notifications.md). Consumers read request state and +autotrack it instead of registering callbacks. Removal from publishing is targeted for 6.0. + +This deprecation is deliberately **separate from, and sequenced after, RFC 0001**. RFC 0001 +proposes the replacement; this RFC deprecates the old surface, and its deprecation flag is only +enabled once that replacement has shipped and reached the Recommended stage — the point at which +the reactive API is the one we steer people toward. Until then this RFC documents the intent and +the migration, but nothing is deprecated. + +## Motivation + +RFC 0001 rebuilds request state as lazily-created, signals-backed data written inside the same +transaction that applies a response's cache changes. Reactive consumers read that state directly +and autotrack it; there is no callback to register and no flush to coordinate against. That makes +the subscription API redundant, and redundant in a way that is actively worse for correctness: + +- **Subscriptions observe a different timeline than the data they describe.** The subscription + API fires on promise hops, stitched to the notification flush by a one-shot internal callback. + RFC 0001 removes that coupling; keeping the subscription API would mean maintaining the exact + data/request-state skew RFC 0001 exists to eliminate. +- **Callbacks are strictly more error-prone than autotracking.** A subscriber must be unsubscribed + to avoid leaks and must reconcile its own view of "is this request still current." Reading + signals-backed state has neither hazard: consumers read the current value and the framework + invalidates them. +- **One surface, one contract.** Leaving two ways to observe request state — one reactive, one + callback-based, with different timing — is the kind of accidental multiplicity the reactive + layer is trying to retire. + +## Detailed design + +### What is deprecated + +The following become deprecated once the flag below is enabled: + +- `store.getRequestStateService()` and the service instance it returns. +- That service's `subscribeForRecord(identifier, callback)`. +- Its imperative lookups `getPendingRequestsForRecord(identifier)` and + `getLastRequestForRecord(identifier)`. + +### Deprecation flag + +- **id**: `warp-drive:deprecate-request-state-subscriptions` +- **since**: the first 5.x minor in which RFC 0001's reactive request state is **Recommended** + (not merely shipped). This ordering is a hard precondition — see below. +- **until**: `6.0` +- Gated by a build-config flag following the existing `ENABLE_LEGACY_*` pattern in + `deprecations.ts`, so apps can opt into the removed-code build once migrated. + +### Sequencing constraint + +The flag must not be enabled before the reactive replacement is Recommended. Enabling a +deprecation whose replacement is not yet the recommended path would warn users toward an API that +is still stabilizing. Concretely, this RFC may be Accepted independently, but the PR that flips +`since` from a placeholder to a concrete version, and enables the warning, lands only after RFC +0001's reactive request state has reached Recommended. This is the general rule captured in the +[Writing and Implementing RFCs](/skills/contributors/writing-and-implementing-rfcs.md) skill: +deprecations follow the features that replace them. + +### Migration + +The mapping is mechanical for the common cases: + +```ts +// Before: imperative subscription +const requests = store.getRequestStateService(); +const token = requests.subscribeForRecord(identifier, (state) => { + if (state.type === 'mutation' && state.state === 'fulfilled') { + // react to save completion + } +}); +// ...later +// (consumer is responsible for teardown) +``` + +```ts +// After: read signals-backed request state and autotrack it +import { getRequestState } from '@warp-drive/core/reactive'; // exact import per RFC 0001 + +const state = getRequestState(identifier); +// read state.status / state.isMutation / state.error in a tracked context; +// the framework invalidates readers when it changes. No token, no teardown. +``` + +The precise public read API is being settled in RFC 0001 (an open question there); this RFC's +migration guidance will name the final import and shape once RFC 0001 resolves it, before this +deprecation's warning is enabled. + +### Ecosystem implications + +- **Addon ecosystem**: addons that consume `getRequestStateService` are the affected surface. + They migrate to the reactive read API on the same timeline. +- **Ember Inspector / DevTools**: any request-state panel that reads via the subscription API + moves to the reactive source; behavior for users is unchanged. +- **Lint rules**: an `eslint-plugin-warp-drive` rule flagging `getRequestStateService` usage, + with an autofix to the reactive API where the callback body maps cleanly, is a possible + follow-up but is not required by this RFC. +- Blueprints, Engines, SSR, IDE support: no changes. + +## How we teach this + +- A deprecation guide entry for `warp-drive:deprecate-request-state-subscriptions` with the + before/after above and the teardown-hazard explanation, cross-linked from RFC 0001's teaching + guide (which introduces the reactive request state this points at). +- The API docs for `getRequestStateService` and its methods gain a deprecation notice pointing at + the reactive read API. +- No guide reorganization: the reactive request-state guide is introduced by RFC 0001; this entry + only adds the "migrating off the old subscription API" section to it. + +## Drawbacks + +- **Churn for an intimate-but-used API.** `getRequestStateService` is public and has real + consumers despite its low profile; the deprecation window and migration carry a maintenance + cost until 6.0. +- **Two-RFC coordination.** Splitting this out of RFC 0001 (per review feedback) means the + deprecation's concrete `since` cannot be fixed until RFC 0001 is Recommended, so this RFC + carries a placeholder version longer than a self-contained deprecation would. + +## Alternatives + +- **Keep the subscription API indefinitely as a thin shim over the reactive state.** RFC 0001 + already describes a compatibility shim (an internal reactive subscriber replaying transitions + to old callbacks) to avoid a hard break. This RFC's position is that the shim is a *migration + aid with an end date*, not a permanent second API; keeping it forever preserves the + dual-surface problem this deprecation exists to remove. +- **Fold this back into RFC 0001.** Rejected: it couples the deprecation timeline to the feature + and mixes two independent decisions in one proposal — the specific concern raised in review of + emberjs/rfcs#1236. + +## Unresolved questions + +1. The concrete `since` version, which cannot be fixed until RFC 0001's reactive request state + reaches Recommended. +2. Whether an `eslint-plugin-warp-drive` autofix rule ships alongside the deprecation or as a + later convenience. +3. The final import path and read-API shape referenced in the migration examples, inherited from + RFC 0001's corresponding open question. From eb1d7b93f31d6b9af44e528906c25baf5ff1d282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=A4=96=20WarpDrive=20RFC=20Sync?= <332362369+warpdrive-bot@users.noreply.github.com> Date: Sat, 26 Sep 2026 09:23:33 +0000 Subject: [PATCH 2/2] RFC #1245: fill prs.accepted and rename file with PR number --- ...d => 1245-deprecating-subscription-based-request-state.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename text/{0000-deprecating-subscription-based-request-state.md => 1245-deprecating-subscription-based-request-state.md} (98%) diff --git a/text/0000-deprecating-subscription-based-request-state.md b/text/1245-deprecating-subscription-based-request-state.md similarity index 98% rename from text/0000-deprecating-subscription-based-request-state.md rename to text/1245-deprecating-subscription-based-request-state.md index abff1ca1e7..16fd57f84d 100644 --- a/text/0000-deprecating-subscription-based-request-state.md +++ b/text/1245-deprecating-subscription-based-request-state.md @@ -7,12 +7,12 @@ teams: - data - learning prs: - accepted: + accepted: https://github.com/emberjs/rfcs/pull/1245 project-link: suite: --- -# WarpDrive: Deprecating Subscription-Based Request State +# Deprecating Subscription-Based Request State ## Summary