Skip to content

Commit 8b24f71

Browse files
authored
Merge branch 'main' into fix-dataconnect-codegen-injection
2 parents 9970536 + 88c7b80 commit 8b24f71

20 files changed

Lines changed: 2476 additions & 14 deletions

README.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ export class AppComponent {
7474

7575
[Upgrading from v6.0? Check out our guide.](docs/version-7-upgrade.md)
7676

77+
[Upgrading from AngularFire 20? See the v21 upgrade guide.](docs/version-21-upgrade.md)
78+
7779
### Sample app
7880

7981
The [`sample`](sample) folder contains a kitchen sink application that demonstrates use of the "modular" API, in a zoneless server-rendered application, with all the bells and whistles.
@@ -144,43 +146,49 @@ import { } from '@angular/fire/storage';
144146
<tr>
145147
<td>
146148

149+
#### [Data Connect](docs/data-connect.md#data-connect)
150+
```ts
151+
import { } from '@angular/fire/data-connect';
152+
```
153+
</td>
154+
<td>
155+
147156
#### [Performance Monitoring](docs/performance.md#performance-monitoring)
148157
```ts
149158
import { } from '@angular/fire/performance';
150159
```
151160
</td>
161+
</tr>
162+
<tr>
152163
<td>
153164

154165
#### [Realtime Database](docs/database.md#realtime-database)
155166
```ts
156167
import { } from '@angular/fire/database';
157168
```
158169
</td>
159-
</tr>
160-
<tr>
161170
<td>
162171

163172
#### [Remote Config](docs/remote-config.md#remote-config)
164173
```ts
165174
import { } from '@angular/fire/remote-config';
166175
```
167176
</td>
177+
</tr>
178+
<tr>
168179
<td>
169180

170181
#### [App Check](docs/app-check.md#app-check)
171182
```ts
172183
import { } from '@angular/fire/app-check';
173184
```
174185
</td>
175-
</tr>
176-
<tr>
177186
<td>
178187

179188
#### [AI Logic](docs/ai.md#ai-logic)
180189
```ts
181190
import { } from '@angular/fire/ai';
182191
```
183192
</td>
184-
185193
</tr>
186194
</table>

docs/ai.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Firebase AI Logic gives you access to the latest generative AI models from Googl
88

99
[Learn more](https://firebase.google.com/docs/ai-logic)
1010

11+
> Firebase AI Logic was previously called **Vertex AI in Firebase**. If you are upgrading from AngularFire 20, the module moved from `@angular/fire/vertexai` to `@angular/fire/ai` and most symbols were renamed (`provideVertexAI` to `provideAI`, `VertexAI` to `AI`). One is not a rename: plain `getAI()` uses the Gemini Developer API backend, so the old `getVertexAI()` maps to `getAI(app, { backend: new VertexAIBackend() })`. Running `ng update @angular/fire` rewrites all of this for you and keeps your app on the Vertex AI backend. See the [AngularFire 20 to 21 upgrade guide](./version-21-upgrade.md).
12+
1113
## Dependency Injection
1214

1315
As a prerequisite, ensure that `AngularFire` has been added to your project via

docs/data-connect.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<small>
2+
<a href="https://github.com/angular/angularfire">AngularFire</a> &#10097; <a href="../README.md#developer-guide">Developer Guide</a> &#10097; Data Connect
3+
</small>
4+
5+
# Data Connect
6+
7+
Firebase Data Connect (now known as "Firebase SQL Connect" in the Firebase documentation) is a backend service that pairs a Cloud SQL for PostgreSQL database with GraphQL, generating type-safe SDKs to query and mutate your data.
8+
9+
[Learn more](https://firebase.google.com/docs/data-connect)
10+
11+
## Dependency Injection
12+
13+
As a prerequisite, ensure that `AngularFire` has been added to your project via
14+
```bash
15+
ng add @angular/fire
16+
```
17+
18+
Provide a Data Connect instance in the application's `app.config.ts`. `getDataConnect` takes a connector config that identifies your service, connector, and location; this is generated for you when you set up Data Connect and is also exported from your generated SDK:
19+
20+
```ts
21+
import { provideFirebaseApp, initializeApp } from '@angular/fire/app';
22+
import { provideDataConnect, getDataConnect } from '@angular/fire/data-connect';
23+
24+
const connectorConfig = {
25+
connector: 'my-connector',
26+
service: 'my-service',
27+
location: 'us-central1',
28+
};
29+
30+
export const appConfig: ApplicationConfig = {
31+
providers: [
32+
provideFirebaseApp(() => initializeApp({ ... })),
33+
provideDataConnect(() => getDataConnect(connectorConfig)),
34+
...
35+
],
36+
...,
37+
}
38+
```
39+
40+
Next inject `DataConnect` into your component:
41+
42+
```typescript
43+
import { Component, inject } from '@angular/core';
44+
import { DataConnect } from '@angular/fire/data-connect';
45+
46+
@Component({ ... })
47+
export class MyComponent {
48+
private dataConnect = inject(DataConnect);
49+
...
50+
}
51+
```
52+
53+
## Firebase API
54+
55+
AngularFire wraps the Firebase JS SDK to ensure proper functionality in Angular, while providing the same API.
56+
57+
Update the imports from `import { ... } from 'firebase/data-connect'` to `import { ... } from '@angular/fire/data-connect'` and follow the official documentation.
58+
59+
[Getting Started](https://firebase.google.com/docs/data-connect/quickstart)

docs/version-21-upgrade.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Upgrading to AngularFire 21
2+
3+
AngularFire 21 targets **Angular 21** and the **Firebase JS SDK v12**. Most of the upgrade is handled for you by `ng update`.
4+
5+
## Run the update
6+
7+
```bash
8+
ng update @angular/core @angular/cli # move your app to Angular 21 first
9+
ng update @angular/fire # then AngularFire 21
10+
```
11+
12+
`ng update @angular/fire` runs a migration that:
13+
14+
- **Aligns your `firebase` dependency to `^12.4.0`.** AngularFire 21 requires Firebase JS SDK 12. If your app still requested `firebase` 11, npm would install both 11 and 12 side by side, and the two copies reject each other's objects at runtime. The migration updates the dependency and reinstalls so you end up with a single copy. Verify with `npm ls firebase`.
15+
- **Rewrites Vertex AI imports to AI Logic** (see below).
16+
17+
## Vertex AI is now Firebase AI Logic
18+
19+
The Vertex AI module has been renamed to Firebase AI Logic. The `@angular/fire/vertexai` entry point (and the older `@angular/fire/vertexai-preview`) are removed in favor of `@angular/fire/ai`:
20+
21+
| Before (`@angular/fire/vertexai`) | After (`@angular/fire/ai`) |
22+
|---|---|
23+
| `getVertexAI(app?, { location? })` | `getAI(app, { backend: new VertexAIBackend(location?) })` |
24+
| `provideVertexAI` | `provideAI` |
25+
| `VertexAI` | `AI` |
26+
| `VertexAIError` | `AIError` |
27+
| `VertexAIErrorCode` | `AIErrorCode` |
28+
| `VertexAIModel` | `AIModel` |
29+
| `VertexAIInstances` | `AIInstances` |
30+
| `vertexAIInstance$` | `AIInstance$` |
31+
| `VertexAIModule` | `AIModule` |
32+
33+
**`getVertexAI` is not a plain rename.** `getAI` already existed alongside it, and a plain `getAI()` call talks to the Gemini Developer API backend, not to Vertex AI. The equivalent of `getVertexAI()` is `getAI(app, { backend: new VertexAIBackend() })`, which is what the migration writes, so your app keeps calling the Vertex AI backend it was configured, enabled, and billed for. A `location` option moves into the `VertexAIBackend` constructor.
34+
35+
`ng update @angular/fire` rewrites these imports and identifiers for you and logs every `getVertexAI` call it rewrites. Code it cannot rewrite safely (for example when the options are not a literal `{ location }` object or that literal references other rewritten symbols, when the function itself is handed around as a value, when a local declaration in the file reuses an imported symbol's name, or when the file already binds `getAI` or `VertexAIBackend` from a source other than AI Logic) is left in place with a warning. The import path itself still moves to the new entry point, so the leftover code fails to compile there, and nothing changes backends silently. A file where a named `getVertexAI` import has any use that cannot be rewritten keeps every use of its named `getVertexAI` imports in place (namespace-style `ns.getVertexAI(...)` calls are judged per call), and each skipped call is logged. `export * from '@angular/fire/vertexai'` is also left alone (rewriting it would silently rename your re-exported public symbols), so replace it with named re-exports by hand. `VertexAIOptions` was removed rather than renamed (the new `AIOptions` takes a `backend` instead of a `location`), so imports of it are left and warned about. Migrate those sites using the table above. `getGenerativeModel` and `getImagenModel` keep their names.
36+
37+
Imports straight from the Firebase SDK (`firebase/vertexai`, gone in SDK 12) are rewritten to `firebase/ai` under the same rules. The rewrite parses your sources with the `typescript` package (an optional peer dependency of `@angular/fire`). Every Angular workspace already has it, but if the migration warns that it could not be resolved, install `typescript` and re-run. See [ai.md](./ai.md) for current usage.
38+
39+
## Other notes
40+
41+
- **Angular 21 is required.** AngularFire 21 peers `@angular/* ^21.0.0` and does not support Angular 22 (a future AngularFire 22 will).
42+
- The obsolete `@angular/platform-browser-dynamic` peer dependency was removed. No action is needed.

src/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,13 @@
3232
"@angular/platform-browser": "^21.0.0",
3333
"@angular/platform-server": "^21.0.0",
3434
"rxjs": "~7.8.0",
35-
"firebase-tools": "^14.0.0 || ^15.0.0"
35+
"firebase-tools": "^14.0.0 || ^15.0.0",
36+
"typescript": ">=5.8 <6.0"
3637
},
3738
"peerDependenciesMeta": {
3839
"firebase-tools": { "optional": true },
39-
"@angular/platform-server": { "optional": true }
40+
"@angular/platform-server": { "optional": true },
41+
"typescript": { "optional": true }
4042
},
4143
"dependencies": {
4244
"firebase": "^12.4.0",

src/schematics/migration.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
},
99
"migration-v21": {
1010
"version": "21.0.0",
11-
"description": "Align the workspace's firebase dependency with the range @angular/fire 21 requires, so the install cannot contain two copies of the firebase SDK",
11+
"description": "Align the workspace's firebase dependency with the range @angular/fire 21 requires, and rewrite Vertex AI imports to Firebase AI Logic (getVertexAI callers keep the Vertex AI backend)",
1212
"factory": "./update/v21#ngUpdate"
1313
},
1414
"ng-post-upgate": {

src/schematics/setup/index.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
createFirestoreStarterFiles,
2222
setDefaultProjectInFirebaseRc,
2323
} from './firebaseConfigs';
24-
import { appPrompt, featuresPrompt, projectPrompt, userPrompt } from './prompts';
24+
import { appPrompt, featuresPrompt, featuresPromptMessage, projectPrompt, userPrompt } from './prompts';
2525

2626
// FirebaseOptions keys — apps.sdkconfig responses include management-API extras that initializeApp() rejects.
2727
const firebaseOptionsKeys = [
@@ -67,7 +67,14 @@ export const ngAddSetupProject = (
6767

6868
const features = await featuresPrompt();
6969

70-
if (features.length > 0) {
70+
if (features.length === 0) {
71+
context.logger.warn(
72+
'No features were selected, so there is nothing to set up. ' +
73+
`At the "${featuresPromptMessage}" prompt, use the arrow keys to move, ` +
74+
'press Space to select each feature you want, then Enter to confirm. ' +
75+
'Re-run ng add @angular/fire to try again.'
76+
);
77+
} else {
7178

7279
const firebaseTools = await getFirebaseTools();
7380

src/schematics/setup/prompts.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,14 @@ type Prompt = <K extends string, U= unknown>(questions: { name: K, source: (...a
7979
const autocomplete: Prompt = (questions) => inquirer.prompt(questions);
8080

8181

82+
export const featuresPromptMessage = 'What features would you like to setup?';
83+
8284
export const featuresPrompt = async (): Promise<FEATURES[]> => {
8385
const { features } = await inquirer.prompt({
8486
type: 'checkbox',
8587
name: 'features',
8688
choices: featureOptions,
87-
message: 'What features would you like to setup?',
89+
message: featuresPromptMessage,
8890
default: [],
8991
}) as { features: FEATURES[] };
9092
return features;

src/schematics/update/v21/index.jasmine.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { logging } from '@angular-devkit/core';
22
import { HostTree, SchematicContext } from '@angular-devkit/schematics';
3+
import * as typescript from 'typescript';
34
import { firebaseVersionRange } from '../../common.js';
45
import { ngUpdate } from './index.js';
56
import 'jasmine';
@@ -40,4 +41,45 @@ describe('migration-v21 ngUpdate', () => {
4041
expect(addTask).not.toHaveBeenCalled();
4142
});
4243

44+
it('keeps the firebase alignment when the rewrite throws', () => {
45+
const logger = new logging.Logger('test');
46+
const warn = spyOn(logger, 'warn');
47+
const addTask = jasmine.createSpy('addTask');
48+
const context = { logger, addTask } as unknown as SchematicContext;
49+
const tree = treeWithFirebase('^11.0.0');
50+
tree.create('angular.json', JSON.stringify({
51+
projects: { app: { root: '', sourceRoot: 'src' } },
52+
}));
53+
tree.create('src/app/foo.ts', `import { getVertexAI } from '@angular/fire/vertexai';`);
54+
const throwingCompiler = {
55+
ScriptTarget: typescript.ScriptTarget,
56+
createSourceFile: () => { throw new Error('boom'); },
57+
} as unknown as typeof typescript;
58+
59+
ngUpdate({ compiler: throwingCompiler })(tree, context);
60+
61+
// The rewrite failure costs only the rewrite, never the firebase alignment.
62+
const written = JSON.parse(tree.readText('package.json'));
63+
expect(written.dependencies.firebase).toBe(firebaseVersionRange);
64+
expect(addTask).toHaveBeenCalledTimes(1);
65+
expect(warn.calls.allArgs().map(callArgs => String(callArgs[0])).join('\n'))
66+
.toContain('Skipped the Vertex AI -> AI Logic source rewrite');
67+
});
68+
69+
it('runs the Vertex AI rewrite and the alignment through one ngUpdate call', () => {
70+
const { context, addTask } = contextWithTaskSpy();
71+
const tree = treeWithFirebase('^11.0.0');
72+
tree.create('angular.json', JSON.stringify({
73+
projects: { app: { root: '', sourceRoot: 'src' } },
74+
}));
75+
tree.create('src/app/foo.ts', `import { getVertexAI } from '@angular/fire/vertexai';`);
76+
77+
ngUpdate({ compiler: typescript })(tree, context);
78+
79+
expect(tree.readText('src/app/foo.ts')).toBe(`import { getAI } from '@angular/fire/ai';`);
80+
const written = JSON.parse(tree.readText('package.json'));
81+
expect(written.dependencies.firebase).toBe(firebaseVersionRange);
82+
expect(addTask).toHaveBeenCalledTimes(1);
83+
});
84+
4385
});

src/schematics/update/v21/index.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,31 @@
11
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
2-
// The explicit index.js subpath keeps this importable from the ESM jasmine run; the bare
2+
// The explicit index.js subpath keeps this importable from the ESM jasmine run. The bare
33
// /tasks directory specifier only resolves under CommonJS.
44
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks/index.js';
5+
import type * as ts from 'typescript';
56
import { alignFirebaseVersion } from '../../common.js';
7+
import { rewriteVertexAIToAI } from './vertexai-to-ai/index.js';
68

79
// ng update re-runs this migration on rc-to-stable transitions (the CLI clamps the migration
810
// range's upper bound to the release version), so it must stay a no-op when nothing changes.
9-
export const ngUpdate = (): Rule => (
11+
export const ngUpdate = (options?: { compiler?: typeof ts }): Rule => (
1012
host: Tree,
1113
context: SchematicContext
1214
) => {
15+
// Align firebase before anything else: it is the one step users cannot do without, so no
16+
// failure below may cost it. This step changes dependencies, so only it schedules an install.
1317
if (alignFirebaseVersion(host, context)) {
1418
context.addTask(new NodePackageInstallTask());
1519
}
20+
// Rewrite Vertex AI imports to AI Logic (source-only edits, no dependency change). Guarded so
21+
// an unexpected rewrite failure costs only the rewrite, never the alignment above.
22+
try {
23+
rewriteVertexAIToAI(host, context, options?.compiler);
24+
} catch (error) {
25+
context.logger.warn(
26+
`Skipped the Vertex AI -> AI Logic source rewrite: ${error}. ` +
27+
'Any remaining @angular/fire/vertexai imports need a manual migration - see the v21 upgrade guide (docs/version-21-upgrade.md).'
28+
);
29+
}
1630
return host;
1731
};

0 commit comments

Comments
 (0)