opencode-skills-collection 1.0.186 → 1.0.187

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +5 -1
  2. package/bundled-skills/3d-web-experience/SKILL.md +152 -37
  3. package/bundled-skills/agent-evaluation/SKILL.md +1088 -26
  4. package/bundled-skills/agent-memory-systems/SKILL.md +1037 -25
  5. package/bundled-skills/agent-tool-builder/SKILL.md +668 -16
  6. package/bundled-skills/ai-agents-architect/SKILL.md +271 -31
  7. package/bundled-skills/ai-product/SKILL.md +716 -26
  8. package/bundled-skills/ai-wrapper-product/SKILL.md +450 -44
  9. package/bundled-skills/algolia-search/SKILL.md +867 -15
  10. package/bundled-skills/autonomous-agents/SKILL.md +1033 -26
  11. package/bundled-skills/aws-serverless/SKILL.md +1046 -35
  12. package/bundled-skills/azure-functions/SKILL.md +1318 -19
  13. package/bundled-skills/browser-automation/SKILL.md +1065 -28
  14. package/bundled-skills/browser-extension-builder/SKILL.md +159 -32
  15. package/bundled-skills/bullmq-specialist/SKILL.md +347 -16
  16. package/bundled-skills/clerk-auth/SKILL.md +796 -15
  17. package/bundled-skills/computer-use-agents/SKILL.md +1870 -28
  18. package/bundled-skills/context-window-management/SKILL.md +271 -18
  19. package/bundled-skills/conversation-memory/SKILL.md +453 -24
  20. package/bundled-skills/crewai/SKILL.md +252 -46
  21. package/bundled-skills/discord-bot-architect/SKILL.md +1207 -34
  22. package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
  23. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
  24. package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
  25. package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
  26. package/bundled-skills/docs/users/bundles.md +1 -1
  27. package/bundled-skills/docs/users/claude-code-skills.md +1 -1
  28. package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
  29. package/bundled-skills/docs/users/getting-started.md +1 -1
  30. package/bundled-skills/docs/users/kiro-integration.md +1 -1
  31. package/bundled-skills/docs/users/usage.md +4 -4
  32. package/bundled-skills/docs/users/visual-guide.md +4 -4
  33. package/bundled-skills/email-systems/SKILL.md +646 -26
  34. package/bundled-skills/faf-expert/SKILL.md +221 -0
  35. package/bundled-skills/faf-wizard/SKILL.md +252 -0
  36. package/bundled-skills/file-uploads/SKILL.md +212 -11
  37. package/bundled-skills/firebase/SKILL.md +646 -16
  38. package/bundled-skills/gcp-cloud-run/SKILL.md +1117 -32
  39. package/bundled-skills/graphql/SKILL.md +1026 -27
  40. package/bundled-skills/hubspot-integration/SKILL.md +804 -19
  41. package/bundled-skills/idea-darwin/SKILL.md +120 -0
  42. package/bundled-skills/inngest/SKILL.md +431 -16
  43. package/bundled-skills/interactive-portfolio/SKILL.md +342 -44
  44. package/bundled-skills/langfuse/SKILL.md +296 -41
  45. package/bundled-skills/langgraph/SKILL.md +259 -50
  46. package/bundled-skills/micro-saas-launcher/SKILL.md +343 -44
  47. package/bundled-skills/neon-postgres/SKILL.md +572 -15
  48. package/bundled-skills/nextjs-supabase-auth/SKILL.md +269 -21
  49. package/bundled-skills/notion-template-business/SKILL.md +371 -44
  50. package/bundled-skills/personal-tool-builder/SKILL.md +537 -44
  51. package/bundled-skills/plaid-fintech/SKILL.md +825 -19
  52. package/bundled-skills/prompt-caching/SKILL.md +438 -25
  53. package/bundled-skills/rag-engineer/SKILL.md +271 -29
  54. package/bundled-skills/salesforce-development/SKILL.md +912 -19
  55. package/bundled-skills/satori/SKILL.md +54 -0
  56. package/bundled-skills/scroll-experience/SKILL.md +381 -44
  57. package/bundled-skills/segment-cdp/SKILL.md +817 -19
  58. package/bundled-skills/shopify-apps/SKILL.md +1475 -19
  59. package/bundled-skills/slack-bot-builder/SKILL.md +1162 -28
  60. package/bundled-skills/telegram-bot-builder/SKILL.md +152 -37
  61. package/bundled-skills/telegram-mini-app/SKILL.md +445 -44
  62. package/bundled-skills/trigger-dev/SKILL.md +916 -27
  63. package/bundled-skills/twilio-communications/SKILL.md +1310 -28
  64. package/bundled-skills/upstash-qstash/SKILL.md +898 -27
  65. package/bundled-skills/vercel-deployment/SKILL.md +637 -39
  66. package/bundled-skills/viral-generator-builder/SKILL.md +132 -37
  67. package/bundled-skills/voice-agents/SKILL.md +937 -27
  68. package/bundled-skills/voice-ai-development/SKILL.md +375 -46
  69. package/bundled-skills/workflow-automation/SKILL.md +982 -29
  70. package/bundled-skills/zapier-make-patterns/SKILL.md +772 -27
  71. package/package.json +1 -1
@@ -1,23 +1,38 @@
1
1
  ---
2
2
  name: firebase
3
- description: "You're a developer who has shipped dozens of Firebase projects. You've seen the \"easy\" path lead to security breaches, runaway costs, and impossible migrations. You know Firebase is powerful, but you also know its sharp edges."
3
+ description: Firebase gives you a complete backend in minutes - auth, database,
4
+ storage, functions, hosting. But the ease of setup hides real complexity.
5
+ Security rules are your last line of defense, and they're often wrong.
4
6
  risk: unknown
5
- source: "vibeship-spawner-skills (Apache 2.0)"
6
- date_added: "2026-02-27"
7
+ source: vibeship-spawner-skills (Apache 2.0)
8
+ date_added: 2026-02-27
7
9
  ---
8
10
 
9
11
  # Firebase
10
12
 
11
- You're a developer who has shipped dozens of Firebase projects. You've seen the
12
- "easy" path lead to security breaches, runaway costs, and impossible migrations.
13
- You know Firebase is powerful, but you also know its sharp edges.
13
+ Firebase gives you a complete backend in minutes - auth, database, storage,
14
+ functions, hosting. But the ease of setup hides real complexity. Security rules
15
+ are your last line of defense, and they're often wrong. Firestore queries are
16
+ limited, and you learn this after you've designed your data model.
14
17
 
15
- Your hard-won lessons: The team that skipped security rules got pwned. The team
16
- that designed Firestore like SQL couldn't query their data. The team that
17
- attached listeners to large collections got a $10k bill. You've learned from
18
- all of them.
18
+ This skill covers Firebase Authentication, Firestore, Realtime Database, Cloud
19
+ Functions, Cloud Storage, and Firebase Hosting. Key insight: Firebase is
20
+ optimized for read-heavy, denormalized data. If you're thinking relationally,
21
+ you're thinking wrong.
19
22
 
20
- You advocate for Firebase w
23
+ 2025 lesson: Firestore pricing can surprise you. Reads are cheap until they're
24
+ not. A poorly designed listener can cost more than a dedicated database. Plan
25
+ your data model for your query patterns, not your data relationships.
26
+
27
+ ## Principles
28
+
29
+ - Design data for queries, not relationships
30
+ - Security rules are mandatory, not optional
31
+ - Denormalize aggressively - duplication is cheap, joins are expensive
32
+ - Batch writes and transactions for consistency
33
+ - Use offline persistence wisely - it's not free
34
+ - Cloud Functions for what clients shouldn't do
35
+ - Environment-based config, never hardcode keys in client
21
36
 
22
37
  ## Capabilities
23
38
 
@@ -31,31 +46,646 @@ You advocate for Firebase w
31
46
  - firebase-admin-sdk
32
47
  - firebase-emulators
33
48
 
49
+ ## Scope
50
+
51
+ - general-backend-architecture -> backend
52
+ - payment-processing -> stripe
53
+ - email-sending -> email
54
+ - advanced-auth-flows -> authentication-oauth
55
+ - kubernetes-deployment -> devops
56
+
57
+ ## Tooling
58
+
59
+ ### Core
60
+
61
+ - firebase - When: Client-side SDK Note: Modular SDK - tree-shakeable
62
+ - firebase-admin - When: Server-side / Cloud Functions Note: Full access, bypasses security rules
63
+ - firebase-functions - When: Cloud Functions v2 Note: v2 functions are recommended
64
+
65
+ ### Testing
66
+
67
+ - @firebase/rules-unit-testing - When: Testing security rules Note: Essential - rules bugs are security bugs
68
+ - firebase-tools - When: Emulator suite Note: Local development without hitting production
69
+
70
+ ### Frameworks
71
+
72
+ - reactfire - When: React + Firebase Note: Hooks-based, handles subscriptions
73
+ - vuefire - When: Vue + Firebase Note: Vue-specific bindings
74
+ - angularfire - When: Angular + Firebase Note: Official Angular bindings
75
+
34
76
  ## Patterns
35
77
 
36
78
  ### Modular SDK Import
37
79
 
38
80
  Import only what you need for smaller bundles
39
81
 
82
+ **When to use**: Client-side Firebase usage
83
+
84
+ # MODULAR IMPORTS:
85
+
86
+ """
87
+ Firebase v9+ uses modular SDK. Import only what you need.
88
+ This enables tree-shaking and smaller bundles.
89
+ """
90
+
91
+ // WRONG: v8-compat style (larger bundle)
92
+ import firebase from 'firebase/compat/app';
93
+ import 'firebase/compat/firestore';
94
+ const db = firebase.firestore();
95
+
96
+ // RIGHT: v9+ modular (tree-shakeable)
97
+ import { initializeApp } from 'firebase/app';
98
+ import { getFirestore, collection, doc, getDoc } from 'firebase/firestore';
99
+
100
+ const app = initializeApp(firebaseConfig);
101
+ const db = getFirestore(app);
102
+
103
+ // Get a document
104
+ const docRef = doc(db, 'users', 'userId');
105
+ const docSnap = await getDoc(docRef);
106
+
107
+ if (docSnap.exists()) {
108
+ console.log(docSnap.data());
109
+ }
110
+
111
+ // Query with constraints
112
+ import { query, where, orderBy, limit } from 'firebase/firestore';
113
+
114
+ const q = query(
115
+ collection(db, 'posts'),
116
+ where('published', '==', true),
117
+ orderBy('createdAt', 'desc'),
118
+ limit(10)
119
+ );
120
+
40
121
  ### Security Rules Design
41
122
 
42
123
  Secure your data with proper rules from day one
43
124
 
125
+ **When to use**: Any Firestore database
126
+
127
+ # FIRESTORE SECURITY RULES:
128
+
129
+ """
130
+ Rules are your last line of defense. Every read and write
131
+ goes through them. Get them wrong, and your data is exposed.
132
+ """
133
+
134
+ rules_version = '2';
135
+ service cloud.firestore {
136
+ match /databases/{database}/documents {
137
+
138
+ // Helper functions
139
+ function isSignedIn() {
140
+ return request.auth != null;
141
+ }
142
+
143
+ function isOwner(userId) {
144
+ return request.auth.uid == userId;
145
+ }
146
+
147
+ function isAdmin() {
148
+ return request.auth.token.admin == true;
149
+ }
150
+
151
+ // Users collection
152
+ match /users/{userId} {
153
+ // Anyone can read public profile
154
+ allow read: if true;
155
+
156
+ // Only owner can write their own data
157
+ allow write: if isOwner(userId);
158
+
159
+ // Private subcollection
160
+ match /private/{document=**} {
161
+ allow read, write: if isOwner(userId);
162
+ }
163
+ }
164
+
165
+ // Posts collection
166
+ match /posts/{postId} {
167
+ // Anyone can read published posts
168
+ allow read: if resource.data.published == true
169
+ || isOwner(resource.data.authorId);
170
+
171
+ // Only authenticated users can create
172
+ allow create: if isSignedIn()
173
+ && request.resource.data.authorId == request.auth.uid;
174
+
175
+ // Only author can update/delete
176
+ allow update, delete: if isOwner(resource.data.authorId);
177
+ }
178
+
179
+ // Admin-only collection
180
+ match /admin/{document=**} {
181
+ allow read, write: if isAdmin();
182
+ }
183
+ }
184
+ }
185
+
44
186
  ### Data Modeling for Queries
45
187
 
46
188
  Design Firestore data structure around query patterns
47
189
 
48
- ## Anti-Patterns
190
+ **When to use**: Designing Firestore schema
191
+
192
+ # FIRESTORE DATA MODELING:
193
+
194
+ """
195
+ Firestore is NOT relational. You can't JOIN.
196
+ Design your data for how you'll QUERY it, not how it relates.
197
+ """
198
+
199
+ // WRONG: Normalized (SQL thinking)
200
+ // users/{userId}
201
+ // posts/{postId} with authorId field
202
+ // To get "posts by user" - need to query posts collection
203
+
204
+ // RIGHT: Denormalized for queries
205
+ // users/{userId}/posts/{postId} - subcollection
206
+ // OR
207
+ // posts/{postId} with embedded author data
208
+
209
+ // Document structure for a post
210
+ const post = {
211
+ id: 'post123',
212
+ title: 'My Post',
213
+ content: '...',
214
+
215
+ // Embed frequently-needed author data
216
+ author: {
217
+ id: 'user456',
218
+ name: 'Jane Doe',
219
+ avatarUrl: '...'
220
+ },
221
+
222
+ // Arrays for IN queries (max 30 items for 'in')
223
+ tags: ['javascript', 'firebase'],
224
+
225
+ // Maps for compound queries
226
+ stats: {
227
+ likes: 42,
228
+ comments: 7,
229
+ views: 1000
230
+ },
231
+
232
+ // Timestamps
233
+ createdAt: serverTimestamp(),
234
+ updatedAt: serverTimestamp(),
235
+
236
+ // Booleans for filtering
237
+ published: true,
238
+ featured: false
239
+ };
240
+
241
+ // Query patterns this enables:
242
+ // - Get post with author info: 1 read (no join needed)
243
+ // - Posts by tag: where('tags', 'array-contains', 'javascript')
244
+ // - Featured posts: where('featured', '==', true)
245
+ // - Recent posts: orderBy('createdAt', 'desc')
246
+
247
+ // When author updates their name, update all their posts
248
+ // This is the tradeoff: writes are more complex, reads are fast
249
+
250
+ ### Real-time Listeners
251
+
252
+ Subscribe to data changes with proper cleanup
253
+
254
+ **When to use**: Real-time features
255
+
256
+ # REAL-TIME LISTENERS:
257
+
258
+ """
259
+ onSnapshot creates a persistent connection. Always unsubscribe
260
+ when component unmounts to prevent memory leaks and extra reads.
261
+ """
262
+
263
+ // React hook for real-time document
264
+ function useDocument(path) {
265
+ const [data, setData] = useState(null);
266
+ const [loading, setLoading] = useState(true);
267
+ const [error, setError] = useState(null);
268
+
269
+ useEffect(() => {
270
+ const docRef = doc(db, path);
271
+
272
+ // Subscribe to document
273
+ const unsubscribe = onSnapshot(
274
+ docRef,
275
+ (snapshot) => {
276
+ if (snapshot.exists()) {
277
+ setData({ id: snapshot.id, ...snapshot.data() });
278
+ } else {
279
+ setData(null);
280
+ }
281
+ setLoading(false);
282
+ },
283
+ (err) => {
284
+ setError(err);
285
+ setLoading(false);
286
+ }
287
+ );
288
+
289
+ // Cleanup on unmount
290
+ return () => unsubscribe();
291
+ }, [path]);
292
+
293
+ return { data, loading, error };
294
+ }
295
+
296
+ // Usage
297
+ function UserProfile({ userId }) {
298
+ const { data: user, loading } = useDocument(`users/${userId}`);
299
+
300
+ if (loading) return <Spinner />;
301
+ return <div>{user?.name}</div>;
302
+ }
303
+
304
+ // Collection with query
305
+ function usePosts(limit = 10) {
306
+ const [posts, setPosts] = useState([]);
307
+
308
+ useEffect(() => {
309
+ const q = query(
310
+ collection(db, 'posts'),
311
+ where('published', '==', true),
312
+ orderBy('createdAt', 'desc'),
313
+ limit(limit)
314
+ );
315
+
316
+ const unsubscribe = onSnapshot(q, (snapshot) => {
317
+ const results = snapshot.docs.map(doc => ({
318
+ id: doc.id,
319
+ ...doc.data()
320
+ }));
321
+ setPosts(results);
322
+ });
323
+
324
+ return () => unsubscribe();
325
+ }, [limit]);
326
+
327
+ return posts;
328
+ }
329
+
330
+ ### Cloud Functions Patterns
331
+
332
+ Server-side logic with Cloud Functions v2
333
+
334
+ **When to use**: Backend logic, triggers, scheduled tasks
335
+
336
+ # CLOUD FUNCTIONS V2:
337
+
338
+ """
339
+ Cloud Functions run server-side code triggered by events.
340
+ V2 uses more standard Node.js patterns and better scaling.
341
+ """
342
+
343
+ import { onRequest } from 'firebase-functions/v2/https';
344
+ import { onDocumentCreated } from 'firebase-functions/v2/firestore';
345
+ import { onSchedule } from 'firebase-functions/v2/scheduler';
346
+ import { getFirestore } from 'firebase-admin/firestore';
347
+ import { initializeApp } from 'firebase-admin/app';
348
+
349
+ initializeApp();
350
+ const db = getFirestore();
351
+
352
+ // HTTP function
353
+ export const api = onRequest(
354
+ { cors: true, region: 'us-central1' },
355
+ async (req, res) => {
356
+ // Verify auth token
357
+ const token = req.headers.authorization?.split('Bearer ')[1];
358
+ if (!token) {
359
+ res.status(401).json({ error: 'Unauthorized' });
360
+ return;
361
+ }
49
362
 
50
- ### ❌ No Security Rules
363
+ try {
364
+ const decoded = await getAuth().verifyIdToken(token);
365
+ // Process request with decoded.uid
366
+ res.json({ userId: decoded.uid });
367
+ } catch (error) {
368
+ res.status(401).json({ error: 'Invalid token' });
369
+ }
370
+ }
371
+ );
51
372
 
52
- ### Client-Side Admin Operations
373
+ // Firestore trigger - on document create
374
+ export const onUserCreated = onDocumentCreated(
375
+ 'users/{userId}',
376
+ async (event) => {
377
+ const snapshot = event.data;
378
+ const userId = event.params.userId;
53
379
 
54
- ### Listener on Large Collections
380
+ if (!snapshot) return;
381
+
382
+ const userData = snapshot.data();
383
+
384
+ // Send welcome email, create related documents, etc.
385
+ await db.collection('notifications').add({
386
+ userId,
387
+ type: 'welcome',
388
+ message: `Welcome, ${userData.name}!`,
389
+ createdAt: FieldValue.serverTimestamp()
390
+ });
391
+ }
392
+ );
393
+
394
+ // Scheduled function (every day at midnight)
395
+ export const dailyCleanup = onSchedule(
396
+ { schedule: '0 0 * * *', timeZone: 'UTC' },
397
+ async (event) => {
398
+ const cutoff = new Date();
399
+ cutoff.setDate(cutoff.getDate() - 30);
400
+
401
+ // Delete old documents
402
+ const oldDocs = await db.collection('logs')
403
+ .where('createdAt', '<', cutoff)
404
+ .limit(500)
405
+ .get();
406
+
407
+ const batch = db.batch();
408
+ oldDocs.docs.forEach(doc => batch.delete(doc.ref));
409
+ await batch.commit();
410
+
411
+ console.log(`Deleted ${oldDocs.size} old logs`);
412
+ }
413
+ );
414
+
415
+ ### Batch Operations
416
+
417
+ Atomic writes and transactions for consistency
418
+
419
+ **When to use**: Multiple document updates that must succeed together
420
+
421
+ # BATCH WRITES AND TRANSACTIONS:
422
+
423
+ """
424
+ Batches: Multiple writes that all succeed or all fail.
425
+ Transactions: Read-then-write operations with consistency.
426
+ Max 500 operations per batch/transaction.
427
+ """
428
+
429
+ import {
430
+ writeBatch, runTransaction, doc, getDoc,
431
+ increment, serverTimestamp
432
+ } from 'firebase/firestore';
433
+
434
+ // Batch write - no reads, just writes
435
+ async function createPostWithTags(post, tags) {
436
+ const batch = writeBatch(db);
437
+
438
+ // Create post
439
+ const postRef = doc(collection(db, 'posts'));
440
+ batch.set(postRef, {
441
+ ...post,
442
+ createdAt: serverTimestamp()
443
+ });
444
+
445
+ // Update tag counts
446
+ for (const tag of tags) {
447
+ const tagRef = doc(db, 'tags', tag);
448
+ batch.set(tagRef, {
449
+ count: increment(1),
450
+ lastUsed: serverTimestamp()
451
+ }, { merge: true });
452
+ }
453
+
454
+ await batch.commit();
455
+ return postRef.id;
456
+ }
457
+
458
+ // Transaction - read and write atomically
459
+ async function likePost(postId, userId) {
460
+ return runTransaction(db, async (transaction) => {
461
+ const postRef = doc(db, 'posts', postId);
462
+ const likeRef = doc(db, 'posts', postId, 'likes', userId);
463
+
464
+ const postSnap = await transaction.get(postRef);
465
+ if (!postSnap.exists()) {
466
+ throw new Error('Post not found');
467
+ }
468
+
469
+ const likeSnap = await transaction.get(likeRef);
470
+ if (likeSnap.exists()) {
471
+ throw new Error('Already liked');
472
+ }
473
+
474
+ // Increment like count and add like document
475
+ transaction.update(postRef, {
476
+ likeCount: increment(1)
477
+ });
478
+
479
+ transaction.set(likeRef, {
480
+ userId,
481
+ createdAt: serverTimestamp()
482
+ });
483
+
484
+ return postSnap.data().likeCount + 1;
485
+ });
486
+ }
487
+
488
+ ### Social Login (Google, GitHub, etc.)
489
+
490
+ OAuth provider setup and authentication flows
491
+
492
+ **When to use**: Social login implementation
493
+
494
+ # SOCIAL LOGIN WITH FIREBASE AUTH
495
+
496
+ import {
497
+ getAuth, signInWithPopup, signInWithRedirect,
498
+ GoogleAuthProvider, GithubAuthProvider, OAuthProvider
499
+ } from "firebase/auth";
500
+
501
+ const auth = getAuth();
502
+
503
+ // GOOGLE
504
+ const googleProvider = new GoogleAuthProvider();
505
+ googleProvider.addScope("email");
506
+ googleProvider.setCustomParameters({ prompt: "select_account" });
507
+
508
+ async function signInWithGoogle() {
509
+ try {
510
+ const result = await signInWithPopup(auth, googleProvider);
511
+ return result.user;
512
+ } catch (error) {
513
+ if (error.code === "auth/account-exists-with-different-credential") {
514
+ return handleAccountConflict(error);
515
+ }
516
+ throw error;
517
+ }
518
+ }
519
+
520
+ // GITHUB
521
+ const githubProvider = new GithubAuthProvider();
522
+ githubProvider.addScope("read:user");
523
+
524
+ // APPLE (Required for iOS apps!)
525
+ const appleProvider = new OAuthProvider("apple.com");
526
+ appleProvider.addScope("email");
527
+ appleProvider.addScope("name");
528
+
529
+ ### Popup vs Redirect Auth
530
+
531
+ When to use popup vs redirect for OAuth
532
+
533
+ **When to use**: Choosing authentication flow
534
+
535
+ # Popup: Desktop, SPA (simpler, can be blocked)
536
+ # Redirect: Mobile, iOS Safari (always works)
537
+
538
+ async function signIn(provider) {
539
+ if (/iPhone|iPad|Android/i.test(navigator.userAgent)) {
540
+ return signInWithRedirect(auth, provider);
541
+ }
542
+ try {
543
+ return await signInWithPopup(auth, provider);
544
+ } catch (e) {
545
+ if (e.code === "auth/popup-blocked") {
546
+ return signInWithRedirect(auth, provider);
547
+ }
548
+ throw e;
549
+ }
550
+ }
551
+
552
+ // Check redirect result on page load
553
+ useEffect(() => {
554
+ getRedirectResult(auth).then(r => r && setUser(r.user));
555
+ }, []);
556
+
557
+ ### Account Linking
558
+
559
+ Link multiple providers to one account
560
+
561
+ **When to use**: User has accounts with different providers
562
+
563
+ import { fetchSignInMethodsForEmail, linkWithCredential } from "firebase/auth";
564
+
565
+ async function handleAccountConflict(error) {
566
+ const email = error.customData?.email;
567
+ const pendingCred = OAuthProvider.credentialFromError(error);
568
+ const methods = await fetchSignInMethodsForEmail(auth, email);
569
+
570
+ if (methods.includes("google.com")) {
571
+ alert("Sign in with Google to link accounts");
572
+ const result = await signInWithPopup(auth, new GoogleAuthProvider());
573
+ await linkWithCredential(result.user, pendingCred);
574
+ return result.user;
575
+ }
576
+ }
577
+
578
+ // Link new provider
579
+ await linkWithPopup(auth.currentUser, new GithubAuthProvider());
580
+
581
+ // Unlink provider (keep at least one!)
582
+ await unlink(auth.currentUser, "github.com");
583
+
584
+ ### Auth State Persistence
585
+
586
+ Control session lifetime
587
+
588
+ **When to use**: Managing user sessions
589
+
590
+ import { setPersistence, browserLocalPersistence, browserSessionPersistence } from "firebase/auth";
591
+
592
+ // LOCAL: survives browser close (default)
593
+ // SESSION: cleared on tab close
594
+
595
+ async function signInWithRememberMe(email, pass, remember) {
596
+ await setPersistence(auth, remember ? browserLocalPersistence : browserSessionPersistence);
597
+ return signInWithEmailAndPassword(auth, email, pass);
598
+ }
599
+
600
+ // React auth hook
601
+ function useAuth() {
602
+ const [user, setUser] = useState(null);
603
+ const [loading, setLoading] = useState(true);
604
+ useEffect(() => onAuthStateChanged(auth, u => { setUser(u); setLoading(false); }), []);
605
+ return { user, loading };
606
+ }
607
+
608
+ ### Email Verification and Password Reset
609
+
610
+ Complete email auth flow
611
+
612
+ **When to use**: Email/password authentication
613
+
614
+ import { sendEmailVerification, sendPasswordResetEmail, reauthenticateWithCredential } from "firebase/auth";
615
+
616
+ // Sign up with verification
617
+ async function signUp(email, password) {
618
+ const result = await createUserWithEmailAndPassword(auth, email, password);
619
+ await sendEmailVerification(result.user);
620
+ return result.user;
621
+ }
622
+
623
+ // Password reset
624
+ await sendPasswordResetEmail(auth, email);
625
+
626
+ // Change password (requires recent auth)
627
+ const cred = EmailAuthProvider.credential(user.email, currentPass);
628
+ await reauthenticateWithCredential(user, cred);
629
+ await updatePassword(user, newPass);
630
+
631
+ ### Token Management for APIs
632
+
633
+ Handle ID tokens for backend calls
634
+
635
+ **When to use**: Authenticating with backend APIs
636
+
637
+ import { getIdToken, onIdTokenChanged } from "firebase/auth";
638
+
639
+ // Get token (auto-refreshes if expired)
640
+ const token = await getIdToken(auth.currentUser);
641
+
642
+ // API helper with auto-retry
643
+ async function apiCall(url, opts = {}) {
644
+ const token = await getIdToken(auth.currentUser);
645
+ const res = await fetch(url, {
646
+ ...opts,
647
+ headers: { ...opts.headers, Authorization: "Bearer " + token }
648
+ });
649
+ if (res.status === 401) {
650
+ const newToken = await getIdToken(auth.currentUser, true);
651
+ return fetch(url, { ...opts, headers: { ...opts.headers, Authorization: "Bearer " + newToken }});
652
+ }
653
+ return res;
654
+ }
655
+
656
+ // Sync to cookie for SSR
657
+ onIdTokenChanged(auth, async u => {
658
+ document.cookie = u ? "__session=" + await u.getIdToken() : "__session=; max-age=0";
659
+ });
660
+
661
+ // Check admin claim
662
+ const { claims } = await auth.currentUser.getIdTokenResult();
663
+ const isAdmin = claims.admin === true;
664
+
665
+ ## Collaboration
666
+
667
+ ### Delegation Triggers
668
+
669
+ - user needs complex OAuth flow -> authentication-oauth (Firebase Auth handles basics, complex flows need OAuth skill)
670
+ - user needs payment integration -> stripe (Firebase + Stripe common pattern)
671
+ - user needs email functionality -> email (Firebase doesn't include email - use SendGrid, Resend, etc.)
672
+ - user needs container deployment -> devops (Beyond Firebase Hosting - Kubernetes, Docker)
673
+ - user needs relational data model -> postgres-wizard (Firestore is wrong choice for highly relational data)
674
+ - user needs full-text search -> elasticsearch-search (Firestore doesn't support full-text search - use Algolia/Elastic)
55
675
 
56
676
  ## Related Skills
57
677
 
58
678
  Works well with: `nextjs-app-router`, `react-patterns`, `authentication-oauth`, `stripe`
59
679
 
60
680
  ## When to Use
61
- This skill is applicable to execute the workflow or actions described in the overview.
681
+
682
+ - User mentions or implies: firebase
683
+ - User mentions or implies: firestore
684
+ - User mentions or implies: firebase auth
685
+ - User mentions or implies: cloud functions
686
+ - User mentions or implies: firebase storage
687
+ - User mentions or implies: realtime database
688
+ - User mentions or implies: firebase hosting
689
+ - User mentions or implies: firebase emulator
690
+ - User mentions or implies: security rules
691
+ - User mentions or implies: firebase admin