First working framework release

Firebase apps with a clear shape.

Firescope is a convention-first framework for Firebase. It handles function exports, admin SDK scope, generated Firebase config, local env loading, emulator wiring, project connection, rules files, and deploy commands.

Quick start npx firescope@latest init my-app
1 file per function export
0 runtime Firestore abstraction tax
Node 20+ Firebase Functions v2
src/functions/users/created.ts
import { firestore } from "firescope/functions"
import { data } from "../../db.js"

export default firestore
  .document("users/{userId}")
  .onCreate(async ({ event, scope }) => {
    await data.collection("audit", scope.db).add({
      type: "user.created",
      userId: event.params.userId,
      createdAt: new Date().toISOString(),
    })
  })
$ npm run dev
OK Built 4 functions into .firescope/functions
OK Firebase emulators started
What it gives you

The Firebase workflow you wanted to start with.

Firescope keeps Firebase visible. It removes repetitive setup, not the underlying platform.

fn

File-based functions

Drop files into src/functions. Firescope discovers them and generates Firebase-compatible exports.

db

Typed Firestore

Define collection models once and get typed collections, docs, and collection groups in TypeScript.

cfg

Generated config

Builds firebase.json, .firebaserc, and the deployable Functions package.

env

Predictable env files

Loads .env, local files, and mode-specific files with clear override behavior.

emu

Emulator workflow

npm run dev builds your app and starts Firebase emulators with local project wiring.

ship

Deploy command

firescope deploy builds the generated output and hands it to Firebase deploy.

The API

Small wrappers, real Firebase underneath.

The public API is intentionally thin. You still write Firebase functions, just with a cleaner context and fewer exports to manage.

HTTP function src/functions/hello.ts

import { http } from "firescope/functions"
import { data } from "../db.js"

export default http(async ({ scope, res }) => {
  const snapshot = await data
    .collection("users", scope.db)
    .limit(10)
    .get()

  res.json({
    users: snapshot.docs.map((doc) => ({
      id: doc.id,
      ...doc.data(),
    })),
  })
})

Typed schema src/db.ts

import { defineFirestoreSchema } from "firescope"

export type AppDb = {
  users: {
    displayName: string
    createdAt: string
  }
  audit: {
    type: "user.created"
    userId: string
    createdAt: string
  }
}

export const data = defineFirestoreSchema<AppDb>()
Workflow

From empty folder to emulator loop.

Firescope's CLI owns the boring path: scaffold, build, run locally, connect when ready, and deploy.

1
Scaffoldfirescope init creates config, functions, public files, and a typed db helper.
2
Developnpm run dev uses a demo Firebase project and starts the emulator loop.
3
Connectnpm run connect selects a real Firebase project when you are ready to deploy.
app shape
my-app/
firescope.config.ts
src/
db.ts # typed Firestore schema
functions/
hello.ts # exports hello
users/created.ts # exports usersCreated
_shared/audit.ts # ignored helper
public/
.env.local

generated on build
firebase.json
.firebaserc
.firescope/functions/lib/index.js
Generated output

Clean source. Firebase-ready deploys.

Your source app stays organized. Firebase receives a generated Functions package built for deployment.

firescope build
$ npx firescope build
OK Built 4 functions into .firescope/functions

.firescope/functions/
  package.json        # Firebase runtime dependencies
  src/index.ts        # generated exports
  lib/index.js        # bundled app and Firescope helpers

firebase.json         # functions, hosting, emulators
firestore.rules       # safe generated default when missing
storage.rules         # safe generated default when missing
.firebaserc           # selected project
Ready

Build Firebase apps without rebuilding the same setup.

Start with one command, keep Firebase's APIs, and let Firescope handle the framework-shaped glue.

Get Firescope