Skip to content

Commit 20bee94

Browse files
Add AI package (#3650)
* Bump to dependency on firebase 11.8.0 (currently a prerelease) * Add new AI entry --------- Co-authored-by: James Daniels <[email protected]>
1 parent ec4e3bd commit 20bee94

12 files changed

+1099
-234
lines changed

package-lock.json

+915-231
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+5-2
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
"@angular/router": "^19.0.0",
5858
"@schematics/angular": "^19.0.0",
5959
"esbuild": "^0.24.0",
60-
"firebase": "^11.2.0",
60+
"firebase": "11.8.0-20250512211235",
6161
"firebase-functions": "^6.1.0",
6262
"fs-extra": "^8.0.1",
6363
"fuzzy": "^0.1.3",
@@ -120,5 +120,8 @@
120120
"typescript": ">=5.5 <5.7",
121121
"yaml": "^2.7.0"
122122
},
123-
"typings": "index.d.ts"
123+
"typings": "index.d.ts",
124+
"overrides": {
125+
"firebase": "11.8.0-20250512211235"
126+
}
124127
}

src/ai/ai.module.ts

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import {
2+
EnvironmentProviders,
3+
InjectionToken,
4+
Injector,
5+
NgModule,
6+
NgZone,
7+
Optional,
8+
makeEnvironmentProviders,
9+
} from '@angular/core';
10+
import { VERSION, ɵAngularFireSchedulers, ɵgetDefaultInstanceOf } from '@angular/fire';
11+
import { FirebaseApp, FirebaseApps } from '@angular/fire/app';
12+
import { AppCheckInstances } from '@angular/fire/app-check';
13+
import { AI as FirebaseAI } from 'firebase/ai';
14+
import { registerVersion } from 'firebase/app';
15+
import { AI, AIInstances, AI_PROVIDER_NAME } from './ai';
16+
17+
export const PROVIDED_AI_INSTANCES = new InjectionToken<AI[]>('angularfire2.ai-instances');
18+
19+
export function defaultAIInstanceFactory(provided: FirebaseAI[]|undefined, defaultApp: FirebaseApp) {
20+
const defaultAI = ɵgetDefaultInstanceOf<FirebaseAI>(AI_PROVIDER_NAME, provided, defaultApp);
21+
return defaultAI && new AI(defaultAI);
22+
}
23+
24+
export function AIInstanceFactory(fn: (injector: Injector) => FirebaseAI) {
25+
return (zone: NgZone, injector: Injector) => {
26+
const ai = zone.runOutsideAngular(() => fn(injector));
27+
return new AI(ai);
28+
};
29+
}
30+
31+
const AI_INSTANCES_PROVIDER = {
32+
provide: AIInstances,
33+
deps: [
34+
[new Optional(), PROVIDED_AI_INSTANCES ],
35+
]
36+
};
37+
38+
const DEFAULT_AI_INSTANCE_PROVIDER = {
39+
provide: AI,
40+
useFactory: defaultAIInstanceFactory,
41+
deps: [
42+
[new Optional(), PROVIDED_AI_INSTANCES ],
43+
FirebaseApp,
44+
]
45+
};
46+
47+
@NgModule({
48+
providers: [
49+
DEFAULT_AI_INSTANCE_PROVIDER,
50+
AI_INSTANCES_PROVIDER,
51+
]
52+
})
53+
export class AIModule {
54+
constructor() {
55+
registerVersion('angularfire', VERSION.full, 'ai');
56+
}
57+
}
58+
59+
export function provideAI(fn: (injector: Injector) => FirebaseAI, ...deps: any[]): EnvironmentProviders {
60+
registerVersion('angularfire', VERSION.full, 'ai');
61+
62+
return makeEnvironmentProviders([
63+
DEFAULT_AI_INSTANCE_PROVIDER,
64+
AI_INSTANCES_PROVIDER,
65+
{
66+
provide: PROVIDED_AI_INSTANCES,
67+
useFactory: AIInstanceFactory(fn),
68+
multi: true,
69+
deps: [
70+
NgZone,
71+
Injector,
72+
ɵAngularFireSchedulers,
73+
FirebaseApps,
74+
[new Optional(), AppCheckInstances ],
75+
...deps,
76+
]
77+
}
78+
]);
79+
}

src/ai/ai.spec.ts

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { TestBed } from '@angular/core/testing';
2+
import { AI, getAI, provideAI } from '@angular/fire/ai';
3+
import { FirebaseApp, getApp, initializeApp, provideFirebaseApp } from '@angular/fire/app';
4+
import { COMMON_CONFIG } from '../test-config';
5+
import { rando } from '../utils';
6+
7+
describe('AI', () => {
8+
let app: FirebaseApp;
9+
let ai: AI;
10+
let providedAI: AI;
11+
let appName: string;
12+
13+
describe('single injection', () => {
14+
15+
beforeEach(() => {
16+
appName = rando();
17+
TestBed.configureTestingModule({
18+
providers: [
19+
provideFirebaseApp(() => initializeApp(COMMON_CONFIG, appName)),
20+
provideAI(() => {
21+
providedAI = getAI(getApp(appName));
22+
return providedAI;
23+
}),
24+
],
25+
});
26+
app = TestBed.inject(FirebaseApp);
27+
ai = TestBed.inject(AI);
28+
});
29+
30+
it('should be injectable', () => {
31+
expect(providedAI).toBeTruthy();
32+
expect(ai).toEqual(providedAI);
33+
expect(ai.app).toEqual(app);
34+
});
35+
36+
});
37+
38+
});

src/ai/ai.ts

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { ɵgetAllInstancesOf } from '@angular/fire';
2+
import { AI as FirebaseAI } from 'firebase/ai';
3+
import { from, timer } from 'rxjs';
4+
import { concatMap, distinct } from 'rxjs/operators';
5+
6+
// see notes in core/firebase.app.module.ts for why we're building the class like this
7+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
8+
export interface AI extends FirebaseAI {}
9+
10+
export class AI {
11+
constructor(ai: FirebaseAI) {
12+
return ai;
13+
}
14+
}
15+
16+
export const AI_PROVIDER_NAME = 'AI';
17+
18+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
19+
export interface AIInstances extends Array<FirebaseAI> {}
20+
21+
export class AIInstances {
22+
constructor() {
23+
return ɵgetAllInstancesOf<FirebaseAI>(AI_PROVIDER_NAME);
24+
}
25+
}
26+
27+
export const AIInstance$ = timer(0, 300).pipe(
28+
concatMap(() => from(ɵgetAllInstancesOf<FirebaseAI>(AI_PROVIDER_NAME))),
29+
distinct(),
30+
);

src/ai/firebase.ts

+14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/ai/ng-package.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3+
"lib": {
4+
"entryFile": "public_api.ts"
5+
}
6+
}

src/ai/package.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"$schema": "../../node_modules/ng-packagr/package.schema.json"
3+
}

src/ai/public_api.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export { AI, AIInstances, AIInstance$ } from './ai';
2+
export { provideAI, AIModule } from './ai.module';
3+
export * from './firebase';

src/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
"@angular/platform-server": { "optional": true }
3737
},
3838
"dependencies": {
39-
"firebase": "^11.2.0",
39+
"firebase": "^11.8.0",
4040
"rxfire": "^6.1.0",
4141
"@angular-devkit/schematics": "^19.0.0",
4242
"@schematics/angular": "^19.0.0",

src/vertexai/firebase.ts

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tools/build.ts

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ ${exportedZoneWrappedFns}
114114
await writeFile(filePath, fileOutput);
115115
};
116116
return Promise.all([
117+
reexport('ai', 'firebase', 'firebase/ai', tsKeys<typeof import('firebase/ai')>()),
117118
reexport('analytics', 'firebase', 'firebase/analytics', tsKeys<typeof import('firebase/analytics')>(), {
118119
isSupported: { blockUntilFirst: false },
119120
logEvent: { blockUntilFirst: false, logLevel: LogLevel.VERBOSE },

0 commit comments

Comments
 (0)