PluginBench
Skill
Pass
Audit score 90

extension-to-functions-codebase

firebase/agent-skills

Convert Firebase Extensions into standalone Cloud Functions codebases or publishable npm packages with V2 trigger upgrades.

What is extension-to-functions-codebase?

Migrates a Firebase Extension into either a local Cloud Functions codebase for app integration or a reusable npm package. Handles V1-to-V2 trigger modernization, declarative IAM/API requirements, and lifecycle hooks using native SDK features.

  • Converts extension.yaml resources to V2 Cloud Functions (Firestore, Tasks, HTTP)
  • Declares IAM roles and Google APIs using requiresRole() and requiresAPI()
  • Migrates extension parameters to firebase-functions/params definitions
  • Converts lifecycle events (onInstall, onUpdate) to SDK lifecycle hooks
  • Generates publishable npm packages with proper exports maps and peer dependencies
  • Applies Destructuring Compatibility Shim for legacy handler patterns

How to install extension-to-functions-codebase

npx skills add https://github.com/firebase/agent-skills --skill extension-to-functions-codebase
Prerequisites
  • Installed Firebase Extension or extension source code (extension.yaml)
  • Node.js >=22 for target package compatibility
  • firebase-functions >=6.0.0 SDK installed locally
  • Existing Cloud Functions project structure or intent to create one
Claude Code
Cursor
Windsurf
Cline

How to use extension-to-functions-codebase

  1. 1.Review extension.yaml to inventory params, APIs, roles, lifecycle events, and resources
  2. 2.Configure package.json with name, node engine (>=22), peerDependencies, and exports map
  3. 3.Upgrade all V1 triggers to V2 equivalents (onDocumentWritten, onTaskDispatched, onRequest)
  4. 4.Convert extension parameters to defineString/defineInt/defineBoolean/defineSecret calls
  5. 5.Map lifecycle events to afterFirstDeploy and afterRedeploy hooks in src/index.ts
  6. 6.Apply Destructuring Compatibility Shim for handlers expecting (change, context) or (snapshot, context)
  7. 7.Generate README.md with installation, re-export snippet, and configuration reference
  8. 8.Deploy as functions/ codebase or publish as npm package (do not execute npm publish)

Use cases

Good for
  • Migrate an installed Firebase Extension into your app's functions/ codebase for deployment
  • Convert a custom extension into a reusable, open-source npm package for team sharing
  • Upgrade V1 Firestore/Tasks triggers to V2 with concurrent execution support
  • Establish declarative security by replacing manual gcloud IAM scripts with SDK declarations
  • Modernize extension lifecycle events to afterFirstDeploy and afterRedeploy hooks
Who it's for
  • Firebase developers maintaining or extending custom extensions
  • Teams publishing reusable Cloud Functions packages to npm
  • Backend engineers upgrading from V1 to V2 Cloud Functions
  • DevOps engineers standardizing declarative infrastructure-as-code patterns

extension-to-functions-codebase FAQ

What's the difference between Target A (local codebase) and Target B (npm package)?

Target A outputs code under functions/src/ for direct app integration and deployment via firebase deploy. Target B creates a reusable npm package with exports map and peerDependencies for consumers to install and re-export functions.

Why should I never call .value() at module load scope?

Calling .value() at top-level scope blocks function initialization and violates V2 best practices. Instead, initialize parameters inside onInit() or lazy getters to ensure they load only when needed.

How do I preserve V1 single-concurrency pricing in V2?

Set cpu: "gcf_gen1" in your function configuration. V2 defaults to concurrent execution (up to 80 requests), which changes pricing; this flag restores V1 behavior.

What is the Destructuring Compatibility Shim?

A pattern that wraps V2 handlers to accept legacy V1 signatures like (change, context) or (snapshot, context), allowing old extension code to run on V2 triggers without rewriting every handler.

Should I run npm publish after generating the package?

No. The skill generates a publishable npm package structure, but you must review, test, and manually publish it when ready. Never auto-publish.

Full instructions (SKILL.md)

Source of truth, from firebase/agent-skills.


name: extension-to-functions-codebase description: Skill for converting an installed Firebase Extension (or extension source) into a standalone Cloud Functions for Firebase codebase or publishable npm package, including V1 to V2 trigger upgrades, lifecycle hooks, and declarative security metadata: category: Serverless

Extension to Functions Codebase & npm Package Migration

Overview

Migrates a Firebase Extension into either:

  1. A local Cloud Functions codebase (functions/src/ for app integration).
  2. A publishable npm package (reusable open-source package exporting V2 functions).

Leverages native Cloud Functions features (declarative IAM, Parameterized Config, SDK Lifecycle Hooks) and modernizes 1st Gen triggers to 2nd Gen using the Destructuring Compatibility Shim.


Target Migration Workflows

  • Target A: Local Functions Codebase (End-User App Integration)

    • Output: Code under functions/src/. Config in .env.
    • Deployment: firebase deploy --only functions.
  • Target B: Publishable npm Package / Shareable Package

    • Output: Reusable npm package exporting V2 functions.
    • Configuration: package.json specifying exports map, engines: { "node": ">=22" }, and peerDependencies: { "firebase-functions": ">=6.0.0" }.
    • Usage: Consumers install package and re-export functions in index.ts (export * from "<package-name>").

Core Rules & Constraints

1. Declarative IAM & APIs (Zero-Local-Overhead)

Use native SDK declarations instead of manual gcloud scripts or console instructions:

  • Use requiresRole("roles/...") for required GCP IAM permissions.
  • Use requiresAPI("service.googleapis.com", "Description") for Google APIs.

2. Global Parameter Access Restriction

  • Never call .value() at top-level module load scope.
  • Initialize global SDK instances inside onInit() or lazy getters:
    import { defineString } from "firebase-functions/params";
    import { onInit } from "firebase-functions/v2";
    
    const dataset = defineString("DATASET_ID");
    let client: BigQuery;
    
    onInit(() => {
      client = new BigQuery({ datasetId: dataset.value() });
    });
    

3. V2 Concurrency & Cost Parity

V2 enables concurrency (up to 80 requests). To preserve V1 single-concurrency pricing, set cpu: "gcf_gen1".


Step-by-Step Migration Execution

Step 1: Inventory Extension Resources

  1. extension.yaml:
    • params → defineString, defineInt, defineBoolean, defineSecret.
    • apis → requiresAPI(...).
    • roles → requiresRole(...).
    • lifecycleEvents → afterFirstDeploy & afterRedeploy.
    • resources → Upgrade 1st Gen triggers to 2nd Gen (onDocumentWritten, onTaskDispatched, onRequest).
  2. Files & Scripts: Preserve devDependencies, test framework (jest), and test scripts.

Step 2: Configure package.json

  • Set name: "<package-name>", engines: { "node": ">=22" }.
  • Set peerDependencies:
    "peerDependencies": {
      "firebase-admin": "^11.0.0 || ^12.0.0",
      "firebase-functions": ">=6.0.0"
    }
    
  • Configure exports map targeting ESM/CommonJS and TypeScript declarations (lib/index.js, lib/index.d.ts).

Step 3: Upgrade Triggers from V1 to V2

  • Firestore: Use onDocumentWritten from firebase-functions/v2/firestore.
  • Tasks: Use onTaskDispatched from firebase-functions/v2/tasks. Remove EXT_INSTANCE_ID when enqueueing tasks.
  • HTTP: Use onRequest from firebase-functions/v2/https.
  • Apply Destructuring Compatibility Shim ({ change, context }, { snapshot, context }) where legacy 1st Gen handlers expect (change, context).

Step 4: Convert Lifecycle Events

Map extension lifecycle events to SDK lifecycle hooks in src/index.ts:

  • onInstall → afterFirstDeploy({ task: { function: "initTask" } })
  • onUpdate / onConfigure → afterRedeploy({ task: { function: "setupTask" } })

Step 5: Package README & Export Instructions

Generate README.md containing:

  1. Installation instructions (npm install).
  2. Re-export snippet (export * from "<package-name>").
  3. Parameterized Configuration .env reference table.
  4. What Changed (Extension vs Package) comparison table.

Reminder: NEVER execute npm publish.