diff --git a/docs/database.md b/docs/database.md index 1eac2244b..dcc1f39ea 100644 --- a/docs/database.md +++ b/docs/database.md @@ -100,6 +100,8 @@ The `listVal()` function creates an observable that emits an object mapped to it The `stateChanges()` function creates an observable that emits each time a change occurs at the reference or query passed. This is useful for tracking the changes in your list. The optional `events` parameter will filter which child events populate the array. +It emits only on child events, which an empty list never produces, so it does not hold the application unstable and a server render does not wait for it. Use `list()` when the render should wait for a list's initial contents. + | | | |-----------------|------------------------------------------------------| | **function** | `stateChanges(ref, options?)` | @@ -110,6 +112,8 @@ The `stateChanges()` function creates an observable that emits each time a chang The `auditTrail()` function creates an observable that emits the entire state trail. This is useful for debugging or replaying the state of a list in your app. The optional `events` parameter will filter which child events populate the array. +Like `stateChanges()`, it waits for child events, so it does not hold the application unstable and a server render does not wait for it. + | | | |-----------------|------------------------------------------------------| | **function** | `auditTrail(ref, options?)` | @@ -120,6 +124,8 @@ The `auditTrail()` function creates an observable that emits the entire state tr The `fromRef()` function creates an observable that emits reference changes. +With `ListenEvent.value` it holds the application unstable until the first value is emitted, so a server render waits for the data. With a child event it does not, because it may never emit. + | | | |-----------------|------------------------------------------| | **function** | `fromRef(ref, event)` | diff --git a/docs/version-22-upgrade.md b/docs/version-22-upgrade.md index 693a4a7bc..e93200fee 100644 --- a/docs/version-22-upgrade.md +++ b/docs/version-22-upgrade.md @@ -1,6 +1,6 @@ # Upgrading to the AngularFire 22 release candidate -AngularFire 22 targets **Angular 22** and the **Firebase JS SDK v12**. Most of the upgrade is handled for you by `ng update`. +AngularFire 22 targets **Angular 22** and the **Firebase JS SDK v12 or v13**. Most of the upgrade is handled for you by `ng update`. ## Run the update @@ -14,8 +14,10 @@ Include `@angular/fire@next` in the same command as Angular. On its own, `ng upd The AngularFire update runs a migration that: -- **Adds** `firebase` **to your** `package.json` **if it is missing**, as `"firebase": "^12.18.0"`, and reinstalls. See the next section for why. -- **Aligns an existing** `firebase` **entry to** `^12.18.0` when it requests a version outside that range. +- **Adds** `firebase` **to your** `package.json` **if it is missing**, and reinstalls. See the next section for why. +- **Aligns an existing** `firebase` **entry** when it requests a version outside `^12.18.0 || ^13.0.0`, the range AngularFire 22 supports. + +Either way, the migration writes `^13.0.0` when firebase 13 is already installed and `^12.18.0` otherwise, so an app on firebase 12 stays on 12 and an app on firebase 13 stays on 13. ## Your app now owns the Firebase SDK @@ -23,7 +25,7 @@ Through AngularFire 21, `@angular/fire` listed `firebase` under `dependencies`, AngularFire 22 lists `firebase` under `peerDependencies` instead. So your app should declare `firebase` in its `package.json` (on Yarn it must, since Yarn installs no peer dependencies). This gives you exactly one copy of `firebase`, and an incompatible version is refused at install time instead of failing at runtime. -`ng update @angular/fire@next` updates an older `firebase` entry in your `package.json` to `^12.18.0` when it requests a version outside that range. If your app doesn't list `firebase` in `package.json` the migration adds `"firebase": "^12.18.0"` and reinstalls. Verify with `npm ls firebase`, which should show exactly one copy. +`ng update @angular/fire@next` updates an older `firebase` entry in your `package.json` when it requests a version outside `^12.18.0 || ^13.0.0`. If your app doesn't list `firebase` in `package.json`, the migration adds it and reinstalls. Verify with `npm ls firebase`, which should show exactly one copy. ### If you install with `npm install` instead of `ng update` @@ -49,14 +51,31 @@ Do not use `--force` or `--legacy-peer-deps` to get past this message. Both inst Yarn doesn't install peer dependencies. Add `firebase` to your own `package.json` as shown above, or run `ng update @angular/fire@next`, which adds it. -An older version installs anyway with a warning (`has incorrect peer dependency "firebase@^12.18.0"` on Yarn 1, `YN0060: INCOMPATIBLE_PEER_DEPENDENCY` on Yarn 2+), so treat that warning as an upgrade step you still have to do. +An older version installs anyway with a warning (`has incorrect peer dependency "firebase@^12.18.0 || ^13.0.0"` on Yarn 1, `YN0060: INCOMPATIBLE_PEER_DEPENDENCY` on Yarn 2+), so treat that warning as an upgrade step you still have to do. ### pnpm -pnpm 8 and later install a missing `firebase` automatically. An older version installs anyway with a warning (`unmet peer firebase@^12.18.0: found 11.x` on pnpm 9 and 10, `Issues with peer dependencies found. Run "pnpm peers check" to list them.` on pnpm 11 and 12), and pnpm prints that warning only once, when it first resolves the tree, never on a later install from the committed lockfile. +pnpm 8 and later install a missing `firebase` automatically. An older version installs anyway with a warning (`unmet peer firebase@"^12.18.0 || ^13.0.0": found 11.x` on pnpm 9 and 10, `Issues with peer dependencies found. Run "pnpm peers check" to list them.` on pnpm 11 and 12), and pnpm prints that warning only once, when it first resolves the tree, never on a later install from the committed lockfile. Set `strict-peer-dependencies=true` in `.npmrc` to make pnpm refuse the mismatch outright. +## Firebase 13 is optional + +AngularFire 22 works with firebase 12 (`^12.18.0`) and firebase 13 (`^13.0.0`). `ng update` keeps the firebase major your app already has, so moving to firebase 13 is a separate step you can take when you are ready: + +```bash +npm install firebase@^13.0.0 +``` + +Before you update, check these firebase 13 changes: + +- **`VertexAIBackend` is removed** from `firebase/ai`, and so from `@angular/fire/ai`. Use `AgentPlatformBackend`, which firebase 12.18 also exports, so you can switch before you upgrade. + - `VertexAIBackend` defaults to the `us-central1` location and `AgentPlatformBackend` to `global`, so replace `new VertexAIBackend()` with `new AgentPlatformBackend('us-central1')` to keep the same location. + - A call that already passes a location keeps it as is. +- **Node.js 24.12 or later.** firebase 13's packages declare `"node": ">=24.12.0"`, so on Node.js 22 npm prints `EBADENGINE` warnings when it installs them. + +For everything else that changed, see the [Firebase JavaScript SDK release notes](https://firebase.google.com/support/release-notes/js). + ## Fewer build-time packages `@angular/fire` no longer installs `@angular-devkit/schematics`, `@angular-devkit/core` or `@schematics/angular`. The Angular CLI supplies them when it runs `ng add` and `ng update`, so nothing changes for an Angular CLI or Nx workspace. @@ -69,6 +88,19 @@ Deploying server-side rendering to Cloud Functions with `ng deploy` (a deploy ta Hosting-only deploys (`ssr` absent or `false`) and Cloud Run deploys (`ssr: 'cloud-run'`) are not deprecated. They no longer print the old "Legacy ng-deploy Firebase is deprecated" message. +## Observable reads deliver as microtasks + +Every Observable read (`collectionData`, `docData`, `authState`, `user`, `objectVal`, `listVal` and the rest) used to hand each value to your subscriber on a timer (`setInterval` with no delay), one event-loop turn later. It now does so at the next microtask checkpoint, which is sooner. The compat `AngularFireAuth` streams change the same way. What arrives, in which order, and when change detection runs do not change. + +What an upgrading app may notice: + +- A test that waits only for microtasks (a few awaited promises, vitest fake timers without advancing, `fakeAsync` with `flushMicrotasks()` alone) now sees a value whose source emits in a microtask, as Auth and a promise-based mock do. + - In a test that fakes timers, a Firestore value still arrives only after the clock is advanced with `tick()` or `vi.advanceTimersByTime()`, because the Firestore SDK hands each snapshot over inside a `setTimeout`. + - A test that already advances the clock, or waits for a real timer, sees the value exactly as before. +- Code that registers its own `setTimeout` when a read emits sees that timer run after the value arrives, not before. +- In a zoneless server render, a value arriving between the app reporting stable and serialization now reaches a `TransferState` entry you write by hand but not the HTML, because the zoneless change-detection pass is itself a later timer. + - Before this change such a value reached neither. AngularFire itself writes nothing into `TransferState`. + ## Other notes - **Node.js 20 is not supported.** Angular 22 requires Node.js `^22.22.3 || ^24.15.0 || >=26.0.0`. Node.js 20 reached end of life on 2026-04-30. diff --git a/docs/zones.md b/docs/zones.md index 99aa0de58..ca1d8df4f 100644 --- a/docs/zones.md +++ b/docs/zones.md @@ -10,6 +10,8 @@ These wrappers ensure Firebase APIs are called outside of the Angular zone. This Observables, Promise-based APIs, and those with callbacks will purposely destabilize your application until a initial value is returned, this ensures that server-side rendering (SSR) and static site generation (SSG/pre-rendering) wait for data before rendering the page's HTML. +Some calls are exceptions. They are still zone wrapped, but they do not hold the application unstable, because their first callback may never come or a render has no reason to wait for them: `onLog`, `onTokenChanged`, `beforeAuthStateChanged`, `onMessage`, `onRegistered`, `onUnregistered`, `logEvent`, `isSupported`, and in the Realtime Database `onChildAdded`, `onChildChanged`, `onChildMoved`, `onChildRemoved`, `off`, `enableLogging`, `stateChanges`, `auditTrail`, and `fromRef` with a child event. To make a server render wait for Realtime Database data, use `onValue`, `object`, `list`, `listVal`, or `fromRef` with `ListenEvent.value`. + ## Consequences of not Zone wrapping When using a Firebase or RxFire API without importing from AngularFire or if AngularFire APIs are used outside of an injection context you _may_ experience instability.