clefbase
v2.1.5
Published
Firebase-style SDK and CLI for Clefbase — database, auth, storage, hosting, and functions
Downloads
3,502
Maintainers
Readme
clefbase
Firebase-style SDK and CLI for Clefbase / Cleforyx. Database, auth, storage, functions, AI, and hosting in one package.
Install
npm install clefbaseQuick Start
1 — Initialize your project
npx clefbase initThe CLI will ask for your Project ID, API Key, and Admin Secret, let you pick which services you need, and optionally set up hosting. It writes a clefbase.json config file (automatically added to .gitignore) and a .env.example.
2 — Use the SDK
import { initClefbase, getDatabase, getAuth, getStorage, getHosting, getFunctions, getAI } from "clefbase";
import config from "./clefbase.json";
const app = initClefbase(config);
const db = getDatabase(app);
const auth = getAuth(app);
const storage = getStorage(app);
const hosting = getHosting(app); // requires adminSecret in config
const functions = getFunctions(app);
const ai = getAI(app);Database
Add a document
const post = await db.collection("posts").add({
title: "Hello World",
published: true,
views: 0,
});
// post.id, post._createdAt, post._updatedAt are set by the serverGet a document
const user = await db.collection("users").doc("uid-123").get();
// returns null if not foundUpdate a document
// Merge (default) — only changes the fields you pass
await db.collection("users").doc("uid-123").update({ name: "Alice" });
// Full overwrite
await db.collection("users").doc("uid-123").set({ name: "Alice", age: 30 });Delete a document
await db.collection("users").doc("uid-123").delete();Query a collection
const results = await db.collection("posts")
.where({ published: true, views: { $gt: 100 } })
.orderBy("_createdAt", "desc")
.limit(10)
.query(); // returns { data, total, limit, offset }
// Or just the array:
const posts = await db.collection("posts")
.where({ published: true })
.getDocs(); // alias: .get()Filter operators
| Operator | Meaning |
|---|---|
| { $gt: n } | greater than |
| { $gte: n } | greater than or equal |
| { $lt: n } | less than |
| { $lte: n } | less than or equal |
| { $ne: val } | not equal |
| { $contains: "str" } | string contains (case-insensitive) |
Subcollections
// posts → [postId] → comments
const comments = db.collection("posts").doc("post-abc").collection("comments");
await comments.add({ text: "Great post!", author: "Bob" });
const all = await comments.getDocs();Batch operations
const batch = db.batch();
batch.set(db.collection("users").doc("user-1"), { name: "Alice" });
batch.update(db.collection("users").doc("user-2"), { status: "active" });
batch.delete(db.collection("logs").doc("log-1"));
await batch.commit();Transactions
await db.runTransaction(async (tx) => {
const balance = await tx.collection("accounts").doc("acc-1").get();
const newBalance = (balance?.balance ?? 0) - 100;
tx.update(db.collection("accounts").doc("acc-1"), { balance: newBalance });
tx.set(db.collection("transactions").doc(), { amount: 100, type: "withdraw" });
});Convenience helpers
await db.getDoc("users", "uid-123");
await db.addDoc("logs", { action: "login" });
await db.updateDoc("users", "uid-123", { lastSeen: new Date().toISOString() });
await db.deleteDoc("users", "uid-123");FieldValue helpers
import { FieldValue } from "clefbase";
await db.collection("posts").doc("p1").update({
views: FieldValue.increment(1),
publishedAt: FieldValue.serverTimestamp(),
tags: FieldValue.arrayUnion(["new-tag"]),
});Auth
Email / Password
// Sign up
const { user, token } = await auth.signUp("[email protected]", "password123", {
displayName: "Alice",
metadata: { role: "member" },
});
// Sign in
const { user, token } = await auth.signIn("[email protected]", "password123");
// Sign out
await auth.signOut();
// Current user
const me = auth.currentUser; // AuthUser | nullAuth state listener
const unsubscribe = auth.onAuthStateChanged((user) => {
if (user) console.log("Signed in as", user.email);
else console.log("Signed out");
});
// Cleanup
unsubscribe();Profile management
// Update profile
await auth.updateProfile({
displayName: "Bob",
metadata: { theme: "dark" }
});Password management
// Change password
await auth.changePassword("oldPass", "newPass");
// Send password reset email
await auth.sendPasswordResetEmail("[email protected]");
// Confirm reset (call from link in email)
await auth.confirmPasswordReset(resetToken, "newPassword");Email verification
// Send verification email
await auth.sendEmailVerification();
// Verify email (call from link in email)
await auth.verifyEmail("ABC123");OAuth / Social login (Gateway)
// 1. Start sign-in with Google/GitHub/etc via gateway
await auth.signInWithGateway("google");
// Redirects to auth.cleforyx.com
// 2. Handle callback on every app load (before rendering)
const result = await auth.handleAuthCallback();
if (result) {
console.log("Signed in:", result.user.email);
setAuthToken(app, result.token);
}Storage
Upload files
// Node.js
import fs from "fs";
const meta = await storage.ref("avatars/user-123.jpg").upload(
fs.readFileSync("./photo.jpg"),
{ contentType: "image/jpeg" }
);
// Browser
const input = document.querySelector("input[type='file']");
const meta = await storage.ref(`uploads/${input.files[0].name}`).upload(input.files[0]);
console.log(meta.id, meta.sizeBytes, meta.md5);Download files
// Get authenticated download URL
const url = await storage.ref("avatars/user-123.jpg").getDownloadURL();
// Use directly in img tags or fetch
const response = await fetch(url);
const blob = await response.blob();File metadata
const meta = await storage.ref("avatars/user-123.jpg").getMetadata();
console.log(meta.size, meta.mimeType, meta.uploadedAt);Delete files
await storage.ref("avatars/user-123.jpg").delete();List files
// List root files
const files = await storage.ref("avatars/").list({ limit: 20 });
// List with folder filtering
const folderFiles = await storage.ref("avatars/2024/").list({ offset: 20 });Named buckets
const userBucket = storage.bucket("user-uploads");
// Upload to bucket
const file = await userBucket.ref("documents/resume.pdf").upload(buffer, {
contentType: "application/pdf"
});
// Download from bucket
const url = await userBucket.ref("documents/resume.pdf").getDownloadURL();Set default bucket
storage.setDefaultBucket("media");
// Now storage.ref() uses "media" instead of "default"Functions
Deploy a function
await functions.deploy({
name: "greetUser",
runtime: "node",
trigger: { type: "http" },
source: `export async function handler(ctx) {
return { greeting: \`Hello, \${ctx.data.name}!\` };
}`,
timeoutMs: 30000,
});Deploy from file (Node.js/CLI)
import { deployFromFile } from "clefbase";
await deployFromFile(functions, {
name: "analyzeImage",
runtime: "python",
trigger: { type: "http" },
filePath: "./functions/analyze_image.py",
env: { API_KEY: process.env.API_KEY! },
});Call HTTP-triggered functions
// One-shot call
const { data, durationMs } = await functions.call("greetUser", { name: "Alice" });
// Typed callable (recommended)
const greet = httpsCallable<{ name: string }, { greeting: string }>(functions, "greetUser");
const result = await greet({ name: "Bob" });Auth-aware function calls
import { setAuthToken } from "clefbase";
const { token } = await auth.signIn("[email protected]", "password");
setAuthToken(app, token);
// ctx.auth.uid and ctx.auth.email available inside the function
const { data } = await functions.call("getUserProfile");Scheduled functions (Cron)
await functions.deploy({
name: "dailyReport",
runtime: "node",
trigger: {
type: "cron",
cron: "0 9 * * *", // Every day at 9 AM UTC
},
source: `export async function handler(ctx) {
// Generate daily report
return { status: "completed" };
}`,
});List and manage functions
// List all functions
const functions = await functions.list();
// Get executions / call history
const executions = await functions.executions("greetUser", 50);
// Delete a function
await functions.delete("greetUser");AI
Text / Code generation
import { generateText } from "clefbase";
const { content, inputTokens, outputTokens } = await ai.text({
model: "claude-sonnet-4-5",
prompt: "Write a bubble-sort in Python",
systemPrompt: "Return only code, no explanation.",
maxTokens: 512,
temperature: 0.3,
});
console.log(content);Vision / Multimodal text generation
Send images (from URLs or base64) to text generation models for analysis, object detection, OCR, or image description. Supported by Claude 3+, Gemini 1.5+, and other vision-capable models.
// Single image from URL
const { content } = await ai.text({
model: "claude-sonnet-4-5",
prompt: "What objects are in this image? List them.",
images: [
{ source: "https://example.com/photo.jpg" }
],
});
// Multiple images with descriptions
const { content } = await ai.text({
model: "gemini-2.5-flash",
prompt: "Compare these two diagrams and describe the differences",
images: [
{
source: "https://example.com/diagram-1.png",
description: "Original design"
},
{
source: "https://example.com/diagram-2.png",
description: "Updated design"
},
],
});
// Base64-encoded image (e.g., from canvas, screenshot, etc.)
const { content } = await ai.text({
model: "claude-sonnet-4-5",
prompt: "Extract and read all text from this screenshot",
images: [
{
source: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEA...",
mediaType: "image/png"
}
],
});
// Vision with system prompt and history
const { content } = await ai.text({
model: "claude-sonnet-4-5",
prompt: "What color is the car in this image?",
systemPrompt: "You are a visual expert. Be precise and concise.",
images: [
{ source: "https://example.com/car.jpg" }
],
history: [
{ role: "user", content: "Analyze this street scene" },
{ role: "assistant", content: "I can see a busy urban street..." },
],
});File attachments for document and code analysis
Pass documents, code files, spreadsheets, and other files to text models for analysis, summarization, and extraction. Supports PDFs, Word docs, spreadsheets, code files, JSON, CSV, video/audio transcription, and more.
// Summarize a PDF from a URL
const { content } = await ai.text({
model: "claude-sonnet-4-5",
prompt: "Summarize this document in 3 key points",
files: [
{
source: "https://example.com/whitepaper.pdf",
mediaType: "application/pdf",
filename: "whitepaper.pdf",
description: "Technical whitepaper"
}
],
});
// Extract data from a CSV (base64)
const csvBase64 = "data:text/csv;base64,bmFtZSxhZ2UsY2l0eSpFdmlzLDM1LExvbmRvbg==";
const { content } = await ai.text({
model: "gemini-2.5-flash",
prompt: "Find all people over 30 and their cities",
files: [
{
source: csvBase64,
mediaType: "text/csv",
filename: "users.csv",
description: "User database export"
}
],
});
// Code review and security analysis
const { content } = await ai.text({
model: "claude-sonnet-4-5",
prompt: "Find security vulnerabilities and suggest fixes",
files: [
{
source: "https://example.com/auth-handler.ts",
mediaType: "text/typescript",
filename: "auth.ts",
description: "Authentication handler"
}
],
});
// Compare multiple documents
const { content } = await ai.text({
model: "claude-sonnet-4-5",
prompt: "Are these API schemas compatible? List differences.",
files: [
{
source: "data:application/json;base64,eyJwcm9wc...",
mediaType: "application/json",
filename: "v1-schema.json",
description: "Original API schema"
},
{
source: "data:application/json;base64,eyJwcm9wc...",
mediaType: "application/json",
filename: "v2-schema.json",
description: "New API schema"
}
],
});
// Video/audio transcription and analysis
const { content } = await ai.text({
model: "claude-sonnet-4-5",
prompt: "Transcribe and summarize this podcast episode",
files: [
{
source: "https://example.com/podcast.mp3",
mediaType: "audio/mpeg",
filename: "episode-42.mp3",
description: "Weekly podcast episode"
}
],
});Local AI models (Ollama)
// If Ollama is configured on your server, use local models
const { content } = await ai.text({
model: "ollama:mistral",
prompt: "Explain quantum computing",
});Image generation
Text-to-Image:
const { files } = await ai.image({
model: "imagen-4.0-generate-001",
prompt: "A futuristic city at sunset",
aspectRatio: "16:9",
numberOfImages: 2,
outputFolder: "ai-generated", // saved to project storage
});
// Access generated images
for (const file of files) {
console.log(file.fullPath); // e.g. "ai-generated/img_123.png"
const url = await storage.ref(file.fullPath).getDownloadURL();
}Image-to-Image (style transfer, upscaling, inpainting):
// Style transfer: apply Van Gogh style to a photo
const { files } = await ai.image({
model: "imagen-4.0-generate-001",
prompt: "Repaint in the style of Van Gogh's Starry Night",
referenceImage: {
source: "https://example.com/portrait.jpg",
strength: 0.6, // 0–1: how much to preserve the reference
},
outputFolder: "styled",
});
// Upscaling: enhance and detail an existing image
const { files: upscaled } = await ai.image({
model: "imagen-4.0-generate-001",
prompt: "Upscale with enhanced details, make it sharper and more vibrant",
referenceImage: {
source: "data:image/jpeg;base64,/9j/4AAQSkZJRg...", // base64 image
strength: 0.9, // high similarity to original
},
});
// Inpainting: modify objects in an image
const { files: inpainted } = await ai.image({
model: "imagen-4.0-generate-001",
prompt: "Replace the sky with a dramatic sunset",
referenceImage: {
source: "https://example.com/landscape.jpg",
strength: 0.7,
},
});Video generation (Veo 2)
Text-to-Video:
// Async operation — waits for completion (1–5 minutes)
const { status, files } = await ai.video({
model: "veo-3.1-generate-preview",
prompt: "A golden retriever on a sunny beach",
durationSeconds: 5,
aspectRatio: "16:9",
outputFolder: "videos",
});
const videoUrl = await storage.ref(files[0].fullPath).getDownloadURL();Image-to-Video (animate photos, create camera movements):
// Animate a static photo
const { files } = await ai.video({
model: "veo-3.1-generate-preview",
prompt: "The person starts walking toward the camera with a smile",
referenceImage: {
source: "https://example.com/portrait.jpg",
strength: 0.8, // strong visual consistency
},
durationSeconds: 4,
outputFolder: "animations",
});
// Cinematic camera movement
const { files: cinematic } = await ai.video({
model: "veo-3.1-generate-preview",
prompt: "Smooth dolly-in camera movement, revealing more landscape detail",
referenceImage: {
source: "https://example.com/landscape.jpg",
strength: 0.7,
},
durationSeconds: 6,
aspectRatio: "16:9",
});
// Temporal extension (what happens next?)
const { files: extended } = await ai.video({
model: "veo-3.1-generate-preview",
prompt: "The scene transitions to evening with golden hour lighting",
referenceImage: {
source: "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
strength: 0.6, // more creative freedom
},
durationSeconds: 5,
});Embeddings
const { embeddings } = await ai.embedding({
model: "gemini-embedding-001",
input: ["apple", "orange", "banana"],
});
// embeddings[0] → vector for "apple"
// embeddings[1] → vector for "orange"
// etc.List available models
// All models
const all = await ai.listModels();
// Filter by category
const textModels = await ai.listModels({ category: "text" });
const imageModels = await ai.listModels({ category: "image" });
// Filter by provider
const googleModels = await ai.listModels({ provider: "google" });
const anthropicModels = await ai.listModels({ provider: "anthropic" });Usage statistics
const stats = await ai.getStats();
console.log(stats.totalRequests, stats.totalInputTokens, stats.totalMediaGenerated);
// Get request log
const records = await ai.getUsage({ limit: 20 });
for (const r of records) {
console.log(r.model, r.status, r.createdAt);
}Hosting
SDK: Deploy and manage sites
import fs from "fs";
import { getHosting } from "clefbase";
const hosting = getHosting(app);
// List all sites
const sites = await hosting.listSites();
// Create a site
const site = await hosting.createSite("my-app", "My awesome app");
// Deploy files
const result = await hosting.site(site.id).deployFiles({
"index.html": fs.readFileSync("dist/index.html"),
"app.js": fs.readFileSync("dist/app.js"),
"styles.css": fs.readFileSync("dist/styles.css"),
}, {
entrypoint: "index.html",
message: "v1.2.0 release",
onProgress: (done, total) => console.log(`${done}/${total} files`),
});
console.log(`Live at: ${result.url}`);
// Get active deploy
const active = await hosting.site(site.id).getActiveDeploy();
// List deploy history
const deploys = await hosting.site(site.id).listDeploys({ limit: 10 });CLI: Deploy from command line
# First-time setup (interactive)
clefbase init
# Build then deploy
npm run build && clefbase deploy
# Deploy with options
clefbase deploy --dir ./build --message "v2 release"
clefbase deploy --site <siteId> # override linked site
# Manage sites
clefbase hosting:init # link or create a site
clefbase hosting:status # show current live deploy
clefbase hosting:sites # list all sites
clefbase hosting:list-deploys # show deployment history
# Project info
clefbase info # show config and connectivity
clefbase --version # show SDK versionCustom domains
// Add custom domain to site
await hosting.site(siteId).addCustomDomain("app.example.com");
// Get custom domains
const domains = await hosting.site(siteId).getCustomDomains();
// Remove custom domain
await hosting.site(siteId).removeCustomDomain("app.example.com");TypeScript
Full type safety
import type { ClefbaseDocument } from "clefbase";
interface Post extends ClefbaseDocument {
title: string;
published: boolean;
views: number;
tags: string[];
}
const posts = db.collection<Post>("posts");
const post = await posts.doc("p1").get();
// post.views is number ✓, post.author doesn't exist ✗Function types
import { httpsCallable } from "clefbase";
const add = httpsCallable<
{ a: number; b: number },
{ sum: number }
>(functions, "add");
const { data } = await add({ a: 3, b: 4 });
console.log(data.sum); // ✓
// console.log(data.product); // ✗ TypeScript errorAuth types
import type { AuthUser } from "clefbase";
const user: AuthUser | null = auth.currentUser;
if (user) {
console.log(user.uid, user.email, user.displayName);
}Configuration
clefbase.json
Written by clefbase init. Never commit this file — add to .gitignore automatically.
{
"serverUrl": "https://api.cleforyx.com",
"projectId": "my_project_abc123",
"apiKey": "cfx_...",
"adminSecret": "...",
"services": {
"database": true,
"auth": true,
"storage": true,
"hosting": true,
"functions": true,
"ai": true
},
"hosting": {
"siteId": "355f3976-89dc-...",
"siteName": "my-app",
"distDir": "dist",
"entrypoint": "index.html"
}
}Error Handling
Database errors
import { ClefbaseError } from "clefbase";
try {
await db.collection("users").doc("id").get();
} catch (err) {
if (err instanceof ClefbaseError) {
console.error(err.message, err.code, err.status);
}
}Functions errors
import { FunctionsError } from "clefbase";
try {
await functions.call("myFunction");
} catch (err) {
if (err instanceof FunctionsError) {
console.error(err.message, err.httpStatus);
}
}AI errors
import { AIError } from "clefbase";
try {
await ai.text({ model: "claude-sonnet-4-5", prompt: "Hi" });
} catch (err) {
if (err instanceof AIError) {
console.error(err.message, err.httpStatus);
}
}Environment variables
Create a .env file with:
CLEFORYX_SERVER_URL=https://api.cleforyx.com
CLEFORYX_PROJECT_ID=your_project_id
CLEFORYX_API_KEY=your_api_key
CLEFORYX_ADMIN_SECRET=your_admin_secretOr load into environment and use:
const app = initClefbase({
serverUrl: process.env.CLEFORYX_SERVER_URL!,
projectId: process.env.CLEFORYX_PROJECT_ID!,
apiKey: process.env.CLEFORYX_API_KEY!,
adminSecret: process.env.CLEFORYX_ADMIN_SECRET,
});Advanced topics
Custom HTTP headers
import { HttpClient } from "clefbase";
const client = new HttpClient(
"https://api.cleforyx.com/db",
{ "x-cfx-key": "your-api-key" }
);Offline-first patterns
// Store auth token locally
localStorage.setItem("cfx_token", token);
// On app load
const saved = localStorage.getItem("cfx_token");
if (saved) {
setAuthToken(app, saved);
const user = auth.currentUser;
// Use cached state while syncing in background
}Server-side usage (Node.js)
All SDK methods work on Node.js:
import { initClefbase, getDatabase } from "clefbase";
const app = initClefbase({
serverUrl: "https://api.cleforyx.com",
projectId: "my_project",
apiKey: process.env.CLEFORYX_API_KEY!,
});
const db = getDatabase(app);
const users = await db.collection("users").getDocs();Support
- Docs: https://cleforyx.com/docs
- GitHub: https://github.com/cleforyx/clefbase
- Issues: https://github.com/cleforyx/clefbase/issues
- Community: https://discord.gg/cleforyx const post = await posts.doc("abc").get(); // Post | null
---
## Error handling
```ts
import { ClefbaseError } from "clefbase";
try {
await auth.signIn("[email protected]", "wrong");
} catch (err) {
if (err instanceof ClefbaseError) {
console.log(err.message); // "Invalid credentials"
console.log(err.status); // 401
}
}Multiple apps
const defaultApp = initClefbase({ serverUrl, projectId, apiKey });
const adminApp = initClefbase({ serverUrl, projectId, apiKey: adminKey, adminSecret }, "admin");
const db = getDatabase(defaultApp);
const hosting = getHosting(adminApp);