Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

ย 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

dbchart-driver-firebase

The DBChat Firebase driver & bridge โ€” a single Node program that speaks dbchart.bridge v1 over NDJSON stdin/stdout (see dbchart-vscode/docs/driver-bridge-architecture.md), covering Firestore and the Realtime Database.

This app merges two projects into one bridge:

Merged project Role in the driver
fires2rest โ€” Firestore/RTDB REST client reads the live backend
relationalize + rtdb_bridge (Node port) turns the JSON tree into relational tables + SQL

Both are private (not published), so their code is recreated inside this app under src/vendor/ โ€” the driver has zero runtime npm dependencies (only node:* builtins).

        live Firebase                                   export tree                    canonical model
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   read    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  convert   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Firestore  /  RTDB REST  โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ โ”‚ { collection: { key: rec } }โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ โ”‚ relationalize (vendored)  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  client   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜            โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                                                                              โ”‚ envelope
                                                                                              โ–ผ
                                                                                  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                                                                                  โ”‚ adapter: -> DatabaseSchemaโ”‚
                                                                                  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                                                                               โ–ผ
                                        one bridge:  {"protocol":"dbchart.bridge","protocolVersion":1, ...}

Layout

Path Purpose
src/contracts/ protocol constants, frames, manifest, ConnectionConfig, canonical schema, envelope, output validation
src/bridge/ NDJSON transport, method allow-list + dispatch, size caps, stderr logging
src/clients/ fires2rest-backed client factory, credentials, Firestore collection discovery
src/convert/ Converter seam, relationalize-backed converter, the canonical adapter
src/introspect/ live RTDB / Firestore readers โ†’ export tree
src/driver/ the Firebase driver (verbs, connection registry, dialect routing)
src/vendor/ recreations of fires2rest and relationalize/rtdb_bridge
bin/firebase-bridge.mjs the stdio entry the host spawns
bridge.json the manifest the host discovers

Verbs (fixed method set)

Verb Behaviour Gated by
describe / health manifest echo / liveness always
convert params.payload (RTDB export JSON) โ†’ envelope capabilities.convert
connect / disconnect materialize/dispose the client from a ConnectionConfig capabilities.live
introspect read the live backend, then convert capabilities.introspect
query optional row read capabilities.query
subscribe / unsubscribe / cancel not declared โ†’ rejected by the allow-list capabilities.stream (false)

Wire examples

โ†’ {"id":1,"method":"describe"}
โ† {"id":1,"ok":true,"result":{"id":"firebase","dialects":["firebase-rtdb","firestore"], ...}}

โ†’ {"id":2,"method":"convert","params":{"payload":{"users":{"u1":{"name":"A"}}}}}
โ† {"id":2,"ok":true,"result":{"protocol":"dbchart.bridge","protocolVersion":1,
                              "driver":{"id":"firebase","kind":"realtime", ...},
                              "database":{"databaseName":"rtdb_to_sql","tables":[...],"relationships":[...]},
                              "warnings":[]}}

โ†’ {"id":3,"method":"introspect","params":{"connection":{...},"path":"/"}}
โ† {"id":3,"ok":true,"result":{ ...envelope... }}

โ†’ {"id":4,"method":"subscribe"}
โ† {"id":4,"ok":false,"error":{"code":"METHOD_NOT_ALLOWED","message":"..."}}

The canonical adapter

@relationalize/node emits the richer canvas contract; the app consumes the canonical DatabaseSchema from packages/schema/src/database.ts. src/convert/normalize.ts is the single translation point:

relationalize database โ†’ canonical DatabaseSchema
name โ†’ databaseName
tables[].columns[].uiType ?? .type โ†’ columns[].type (free string)
tables[].primaryKey[] โ†’ columns[].primaryKey
tables[].foreignKeys[] โ†’ relationships[]
tree, sql, source, generator โ†’ database.metadata.bridge (the doc's only escape hatch)

Bridges must not invent top-level fields, so the rich artifacts ride inside database.metadata โ€” the webview still only ever sees DatabaseSchema.

Quick start

npm install          # dev tooling only (typescript, tsdown, vitest)
npm run build        # -> self-contained dist/ (no runtime deps)
npm test             # 17 files / 326 tests (281 run, 45 live tests skipped)
npm run typecheck

Run the bridge by hand:

echo '{"id":1,"method":"describe"}' | node bin/firebase-bridge.mjs --once
echo '{"id":1,"method":"convert","params":{"payload":{"users":{"u1":{"name":"A"}}}}}' \
  | node bin/firebase-bridge.mjs --once

Connecting to a live project

connect / introspect take a host-provided ConnectionConfig:

{
  "dialect": "firebase-rtdb",              // or "firestore"
  "connectionId": "my-conn",
  "params": { "databaseURL": "https://my-proj-default-rtdb.firebaseio.com" },
  "credential": { "kind": "service-account", "value": "{...service-account json...}" }
}
Credential kind How it is used
service-account RS256 JWT โ†’ OAuth token (signed with node:crypto), for live projects
bearer sent as Authorization: Bearer โ€ฆ
none + params.emulatorHost / endpoint Firebase emulator / self-hosted

Firestore introspect reads params.collections when given; otherwise it discovers root collections via documents:listCollectionIds. params.depth > 1 pulls sub-collections in as child tables. RTDB introspect reads params.path (default /).

Credentials arrive over stdin only, are never persisted, never logged and never echoed back.

Guardrails (from the architecture doc)

  • Manifest validation before use, plus a protocol-version gate.
  • Method allow-list derived from capabilities โ€” undeclared verbs are refused.
  • Output validation โ€” unknown fields are stripped, invalid nodes are dropped with a BridgeWarning, never a crash (plain functions, no zod).
  • Size caps โ€” outputLimits.maxNodes / maxBytes truncate and emit a truncated warning instead of hanging the host.
  • Fixed warning enum โ€” partial | type-conflict | duplicate-key | truncated | unsupported.

Tests

npm test runs everything with Vitest:

Suite Files Covers
test/ (driver) manifest, normalize, convert, server, driver the bridge contract, the canonical adapter, the verbs, guardrails
test/relationalize/ ported from relationalize/node/test the relationalization algorithm, Schema/DDL, the rtdb_bridge pipeline, the CLI
test/fires2rest/ ported from fires2rest/tests value conversion, query building, RTDB URL/snapshot behaviour, client auth

Two porting changes were needed:

  • the upstream relationalize suites use node:test; they are imported from vitest instead (the node:assert/strict assertions are kept verbatim), and cli.test.ts drives the vendored main() in-process instead of spawning a compiled cli.js.
  • the live fires2rest integration suites keep their describe.skipIf gate, so they skip unless a backend is available:
# emulator
FIRESTORE_EMULATOR_HOST=127.0.0.1:8080 npm run test:live
# or a real project
FIREBASE_PROJECT_ID=... FIREBASE_CLIENT_EMAIL=... FIREBASE_PRIVATE_KEY=... npm run test:live

interop.test.ts (which compares against firebase-admin) is intentionally not ported โ€” it would add an external dependency the driver deliberately does not have.

Packaging

npm run build (tsdown) bundles everything into dist/, so the shipped bridge is self-contained: copy dist/, bin/, bridge.json and any machine with Node โ‰ฅ 20 can run it โ€” no node_modules, no registry access, no Python.

Recreated libraries

src/vendor/fires2rest and src/vendor/relationalize are the driver's own copies of the two private projects (see src/vendor/README.md). The only external dependency they had โ€” jose in fires2rest/auth.ts โ€” was recreated with node:crypto, which is what makes the zero-dependency claim true.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages