clefbase 2.0.7 → 2.0.9
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.
- package/README.md +748 -55
- package/dist/__tests__/full-feature-validation.test.d.ts +6 -0
- package/dist/__tests__/full-feature-validation.test.d.ts.map +1 -0
- package/dist/__tests__/full-feature-validation.test.js +329 -0
- package/dist/__tests__/full-feature-validation.test.js.map +1 -0
- package/dist/ai.d.ts +294 -9
- package/dist/ai.d.ts.map +1 -1
- package/dist/ai.js +198 -8
- package/dist/ai.js.map +1 -1
- package/dist/app.d.ts +7 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +18 -3
- package/dist/app.js.map +1 -1
- package/dist/auth/index.d.ts +28 -3
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +71 -9
- package/dist/auth/index.js.map +1 -1
- package/dist/cli-src/cli/commands/init.js +30 -0
- package/dist/cli-src/cli/config.js +3 -0
- package/dist/cli.js +32 -1
- package/dist/db/index.d.ts +38 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +55 -1
- package/dist/db/index.js.map +1 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +0 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -3
- package/dist/react/index.js.map +1 -1
- package/dist/storage/index.d.ts +3 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +11 -6
- package/dist/storage/index.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# clefbase
|
|
2
2
|
|
|
3
|
-
Firebase-style SDK and CLI for Clefbase / [Cleforyx](https://cleforyx.com). Database, auth, storage, and hosting in one package.
|
|
3
|
+
Firebase-style SDK and CLI for Clefbase / [Cleforyx](https://cleforyx.com). Database, auth, storage, functions, AI, and hosting in one package.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -12,7 +12,7 @@ npm install clefbase
|
|
|
12
12
|
|
|
13
13
|
## Quick Start
|
|
14
14
|
|
|
15
|
-
### 1 —
|
|
15
|
+
### 1 — Initialize your project
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
npx clefbase init
|
|
@@ -23,15 +23,17 @@ The CLI will ask for your Project ID, API Key, and Admin Secret, let you pick wh
|
|
|
23
23
|
### 2 — Use the SDK
|
|
24
24
|
|
|
25
25
|
```ts
|
|
26
|
-
import { initClefbase, getDatabase, getAuth, getStorage, getHosting } from "clefbase";
|
|
26
|
+
import { initClefbase, getDatabase, getAuth, getStorage, getHosting, getFunctions, getAI } from "clefbase";
|
|
27
27
|
import config from "./clefbase.json";
|
|
28
28
|
|
|
29
29
|
const app = initClefbase(config);
|
|
30
30
|
|
|
31
|
-
const db
|
|
32
|
-
const auth
|
|
33
|
-
const storage
|
|
34
|
-
const hosting
|
|
31
|
+
const db = getDatabase(app);
|
|
32
|
+
const auth = getAuth(app);
|
|
33
|
+
const storage = getStorage(app);
|
|
34
|
+
const hosting = getHosting(app); // requires adminSecret in config
|
|
35
|
+
const functions = getFunctions(app);
|
|
36
|
+
const ai = getAI(app);
|
|
35
37
|
```
|
|
36
38
|
|
|
37
39
|
---
|
|
@@ -46,7 +48,7 @@ const post = await db.collection("posts").add({
|
|
|
46
48
|
published: true,
|
|
47
49
|
views: 0,
|
|
48
50
|
});
|
|
49
|
-
// post.
|
|
51
|
+
// post.id, post._createdAt, post._updatedAt are set by the server
|
|
50
52
|
```
|
|
51
53
|
|
|
52
54
|
### Get a document
|
|
@@ -107,6 +109,27 @@ await comments.add({ text: "Great post!", author: "Bob" });
|
|
|
107
109
|
const all = await comments.getDocs();
|
|
108
110
|
```
|
|
109
111
|
|
|
112
|
+
### Batch operations
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
const batch = db.batch();
|
|
116
|
+
batch.set(db.collection("users").doc("user-1"), { name: "Alice" });
|
|
117
|
+
batch.update(db.collection("users").doc("user-2"), { status: "active" });
|
|
118
|
+
batch.delete(db.collection("logs").doc("log-1"));
|
|
119
|
+
await batch.commit();
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Transactions
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
await db.runTransaction(async (tx) => {
|
|
126
|
+
const balance = await tx.collection("accounts").doc("acc-1").get();
|
|
127
|
+
const newBalance = (balance?.balance ?? 0) - 100;
|
|
128
|
+
tx.update(db.collection("accounts").doc("acc-1"), { balance: newBalance });
|
|
129
|
+
tx.set(db.collection("transactions").doc(), { amount: 100, type: "withdraw" });
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
110
133
|
### Convenience helpers
|
|
111
134
|
|
|
112
135
|
```ts
|
|
@@ -116,10 +139,24 @@ await db.updateDoc("users", "uid-123", { lastSeen: new Date().toISOString() });
|
|
|
116
139
|
await db.deleteDoc("users", "uid-123");
|
|
117
140
|
```
|
|
118
141
|
|
|
142
|
+
### FieldValue helpers
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { FieldValue } from "clefbase";
|
|
146
|
+
|
|
147
|
+
await db.collection("posts").doc("p1").update({
|
|
148
|
+
views: FieldValue.increment(1),
|
|
149
|
+
publishedAt: FieldValue.serverTimestamp(),
|
|
150
|
+
tags: FieldValue.arrayUnion(["new-tag"]),
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
119
154
|
---
|
|
120
155
|
|
|
121
156
|
## Auth
|
|
122
157
|
|
|
158
|
+
### Email / Password
|
|
159
|
+
|
|
123
160
|
```ts
|
|
124
161
|
// Sign up
|
|
125
162
|
const { user, token } = await auth.signUp("alice@example.com", "password123", {
|
|
@@ -127,76 +164,568 @@ const { user, token } = await auth.signUp("alice@example.com", "password123", {
|
|
|
127
164
|
metadata: { role: "member" },
|
|
128
165
|
});
|
|
129
166
|
|
|
130
|
-
// Sign in
|
|
131
|
-
const { user } = await auth.signIn("alice@example.com", "password123");
|
|
167
|
+
// Sign in
|
|
168
|
+
const { user, token } = await auth.signIn("alice@example.com", "password123");
|
|
169
|
+
|
|
170
|
+
// Sign out
|
|
132
171
|
await auth.signOut();
|
|
133
172
|
|
|
134
173
|
// Current user
|
|
135
174
|
const me = auth.currentUser; // AuthUser | null
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Auth state listener
|
|
136
178
|
|
|
137
|
-
|
|
179
|
+
```ts
|
|
138
180
|
const unsubscribe = auth.onAuthStateChanged((user) => {
|
|
139
181
|
if (user) console.log("Signed in as", user.email);
|
|
140
182
|
else console.log("Signed out");
|
|
141
183
|
});
|
|
184
|
+
|
|
185
|
+
// Cleanup
|
|
142
186
|
unsubscribe();
|
|
187
|
+
```
|
|
143
188
|
|
|
189
|
+
### Profile management
|
|
190
|
+
|
|
191
|
+
```ts
|
|
144
192
|
// Update profile
|
|
145
|
-
await auth.updateProfile({
|
|
193
|
+
await auth.updateProfile({
|
|
194
|
+
displayName: "Bob",
|
|
195
|
+
metadata: { theme: "dark" }
|
|
196
|
+
});
|
|
197
|
+
```
|
|
146
198
|
|
|
147
|
-
|
|
199
|
+
### Password management
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
// Change password
|
|
148
203
|
await auth.changePassword("oldPass", "newPass");
|
|
204
|
+
|
|
205
|
+
// Send password reset email
|
|
149
206
|
await auth.sendPasswordResetEmail("alice@example.com");
|
|
207
|
+
|
|
208
|
+
// Confirm reset (call from link in email)
|
|
150
209
|
await auth.confirmPasswordReset(resetToken, "newPassword");
|
|
210
|
+
```
|
|
151
211
|
|
|
152
|
-
|
|
212
|
+
### Email verification
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
// Send verification email
|
|
153
216
|
await auth.sendEmailVerification();
|
|
217
|
+
|
|
218
|
+
// Verify email (call from link in email)
|
|
154
219
|
await auth.verifyEmail("ABC123");
|
|
155
220
|
```
|
|
156
221
|
|
|
222
|
+
### OAuth / Social login (Gateway)
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
// 1. Start sign-in with Google/GitHub/etc via gateway
|
|
226
|
+
await auth.signInWithGateway("google");
|
|
227
|
+
// Redirects to auth.cleforyx.com
|
|
228
|
+
|
|
229
|
+
// 2. Handle callback on every app load (before rendering)
|
|
230
|
+
const result = await auth.handleAuthCallback();
|
|
231
|
+
if (result) {
|
|
232
|
+
console.log("Signed in:", result.user.email);
|
|
233
|
+
setAuthToken(app, result.token);
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
157
237
|
---
|
|
158
238
|
|
|
159
239
|
## Storage
|
|
160
240
|
|
|
241
|
+
### Upload files
|
|
242
|
+
|
|
161
243
|
```ts
|
|
162
|
-
//
|
|
244
|
+
// Node.js
|
|
163
245
|
import fs from "fs";
|
|
164
246
|
const meta = await storage.ref("avatars/user-123.jpg").upload(
|
|
165
247
|
fs.readFileSync("./photo.jpg"),
|
|
166
248
|
{ contentType: "image/jpeg" }
|
|
167
249
|
);
|
|
168
250
|
|
|
169
|
-
//
|
|
170
|
-
const
|
|
251
|
+
// Browser
|
|
252
|
+
const input = document.querySelector("input[type='file']");
|
|
253
|
+
const meta = await storage.ref(`uploads/${input.files[0].name}`).upload(input.files[0]);
|
|
254
|
+
|
|
255
|
+
console.log(meta.id, meta.sizeBytes, meta.md5);
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Download files
|
|
171
259
|
|
|
172
|
-
|
|
260
|
+
```ts
|
|
261
|
+
// Get authenticated download URL
|
|
173
262
|
const url = await storage.ref("avatars/user-123.jpg").getDownloadURL();
|
|
174
263
|
|
|
175
|
-
//
|
|
264
|
+
// Use directly in img tags or fetch
|
|
265
|
+
const response = await fetch(url);
|
|
266
|
+
const blob = await response.blob();
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### File metadata
|
|
270
|
+
|
|
271
|
+
```ts
|
|
176
272
|
const meta = await storage.ref("avatars/user-123.jpg").getMetadata();
|
|
273
|
+
console.log(meta.size, meta.mimeType, meta.uploadedAt);
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Delete files
|
|
177
277
|
|
|
178
|
-
|
|
278
|
+
```ts
|
|
179
279
|
await storage.ref("avatars/user-123.jpg").delete();
|
|
280
|
+
```
|
|
180
281
|
|
|
181
|
-
|
|
282
|
+
### List files
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
// List root files
|
|
182
286
|
const files = await storage.ref("avatars/").list({ limit: 20 });
|
|
183
287
|
|
|
184
|
-
//
|
|
185
|
-
const
|
|
186
|
-
|
|
288
|
+
// List with folder filtering
|
|
289
|
+
const folderFiles = await storage.ref("avatars/2024/").list({ offset: 20 });
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Named buckets
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
const userBucket = storage.bucket("user-uploads");
|
|
296
|
+
|
|
297
|
+
// Upload to bucket
|
|
298
|
+
const file = await userBucket.ref("documents/resume.pdf").upload(buffer, {
|
|
299
|
+
contentType: "application/pdf"
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Download from bucket
|
|
303
|
+
const url = await userBucket.ref("documents/resume.pdf").getDownloadURL();
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Set default bucket
|
|
307
|
+
|
|
308
|
+
```ts
|
|
309
|
+
storage.setDefaultBucket("media");
|
|
310
|
+
// Now storage.ref() uses "media" instead of "default"
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## Functions
|
|
316
|
+
|
|
317
|
+
### Deploy a function
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
await functions.deploy({
|
|
321
|
+
name: "greetUser",
|
|
322
|
+
runtime: "node",
|
|
323
|
+
trigger: { type: "http" },
|
|
324
|
+
source: `export async function handler(ctx) {
|
|
325
|
+
return { greeting: \`Hello, \${ctx.data.name}!\` };
|
|
326
|
+
}`,
|
|
327
|
+
timeoutMs: 30000,
|
|
328
|
+
});
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Deploy from file (Node.js/CLI)
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
import { deployFromFile } from "clefbase";
|
|
335
|
+
|
|
336
|
+
await deployFromFile(functions, {
|
|
337
|
+
name: "analyzeImage",
|
|
338
|
+
runtime: "python",
|
|
339
|
+
trigger: { type: "http" },
|
|
340
|
+
filePath: "./functions/analyze_image.py",
|
|
341
|
+
env: { API_KEY: process.env.API_KEY! },
|
|
342
|
+
});
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Call HTTP-triggered functions
|
|
346
|
+
|
|
347
|
+
```ts
|
|
348
|
+
// One-shot call
|
|
349
|
+
const { data, durationMs } = await functions.call("greetUser", { name: "Alice" });
|
|
350
|
+
|
|
351
|
+
// Typed callable (recommended)
|
|
352
|
+
const greet = httpsCallable<{ name: string }, { greeting: string }>(functions, "greetUser");
|
|
353
|
+
const result = await greet({ name: "Bob" });
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Auth-aware function calls
|
|
357
|
+
|
|
358
|
+
```ts
|
|
359
|
+
import { setAuthToken } from "clefbase";
|
|
360
|
+
|
|
361
|
+
const { token } = await auth.signIn("user@example.com", "password");
|
|
362
|
+
setAuthToken(app, token);
|
|
363
|
+
|
|
364
|
+
// ctx.auth.uid and ctx.auth.email available inside the function
|
|
365
|
+
const { data } = await functions.call("getUserProfile");
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Scheduled functions (Cron)
|
|
369
|
+
|
|
370
|
+
```ts
|
|
371
|
+
await functions.deploy({
|
|
372
|
+
name: "dailyReport",
|
|
373
|
+
runtime: "node",
|
|
374
|
+
trigger: {
|
|
375
|
+
type: "cron",
|
|
376
|
+
cron: "0 9 * * *", // Every day at 9 AM UTC
|
|
377
|
+
},
|
|
378
|
+
source: `export async function handler(ctx) {
|
|
379
|
+
// Generate daily report
|
|
380
|
+
return { status: "completed" };
|
|
381
|
+
}`,
|
|
382
|
+
});
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### List and manage functions
|
|
386
|
+
|
|
387
|
+
```ts
|
|
388
|
+
// List all functions
|
|
389
|
+
const functions = await functions.list();
|
|
390
|
+
|
|
391
|
+
// Get executions / call history
|
|
392
|
+
const executions = await functions.executions("greetUser", 50);
|
|
393
|
+
|
|
394
|
+
// Delete a function
|
|
395
|
+
await functions.delete("greetUser");
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## AI
|
|
401
|
+
|
|
402
|
+
### Text / Code generation
|
|
403
|
+
|
|
404
|
+
```ts
|
|
405
|
+
import { generateText } from "clefbase";
|
|
406
|
+
|
|
407
|
+
const { content, inputTokens, outputTokens } = await ai.text({
|
|
408
|
+
model: "claude-sonnet-4-5",
|
|
409
|
+
prompt: "Write a bubble-sort in Python",
|
|
410
|
+
systemPrompt: "Return only code, no explanation.",
|
|
411
|
+
maxTokens: 512,
|
|
412
|
+
temperature: 0.3,
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
console.log(content);
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Vision / Multimodal text generation
|
|
419
|
+
|
|
420
|
+
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.
|
|
421
|
+
|
|
422
|
+
```ts
|
|
423
|
+
// Single image from URL
|
|
424
|
+
const { content } = await ai.text({
|
|
425
|
+
model: "claude-sonnet-4-5",
|
|
426
|
+
prompt: "What objects are in this image? List them.",
|
|
427
|
+
images: [
|
|
428
|
+
{ source: "https://example.com/photo.jpg" }
|
|
429
|
+
],
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// Multiple images with descriptions
|
|
433
|
+
const { content } = await ai.text({
|
|
434
|
+
model: "gemini-2.5-flash",
|
|
435
|
+
prompt: "Compare these two diagrams and describe the differences",
|
|
436
|
+
images: [
|
|
437
|
+
{
|
|
438
|
+
source: "https://example.com/diagram-1.png",
|
|
439
|
+
description: "Original design"
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
source: "https://example.com/diagram-2.png",
|
|
443
|
+
description: "Updated design"
|
|
444
|
+
},
|
|
445
|
+
],
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// Base64-encoded image (e.g., from canvas, screenshot, etc.)
|
|
449
|
+
const { content } = await ai.text({
|
|
450
|
+
model: "claude-sonnet-4-5",
|
|
451
|
+
prompt: "Extract and read all text from this screenshot",
|
|
452
|
+
images: [
|
|
453
|
+
{
|
|
454
|
+
source: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEA...",
|
|
455
|
+
mediaType: "image/png"
|
|
456
|
+
}
|
|
457
|
+
],
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// Vision with system prompt and history
|
|
461
|
+
const { content } = await ai.text({
|
|
462
|
+
model: "claude-sonnet-4-5",
|
|
463
|
+
prompt: "What color is the car in this image?",
|
|
464
|
+
systemPrompt: "You are a visual expert. Be precise and concise.",
|
|
465
|
+
images: [
|
|
466
|
+
{ source: "https://example.com/car.jpg" }
|
|
467
|
+
],
|
|
468
|
+
history: [
|
|
469
|
+
{ role: "user", content: "Analyze this street scene" },
|
|
470
|
+
{ role: "assistant", content: "I can see a busy urban street..." },
|
|
471
|
+
],
|
|
472
|
+
});
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### File attachments for document and code analysis
|
|
476
|
+
|
|
477
|
+
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.
|
|
478
|
+
|
|
479
|
+
```ts
|
|
480
|
+
// Summarize a PDF from a URL
|
|
481
|
+
const { content } = await ai.text({
|
|
482
|
+
model: "claude-sonnet-4-5",
|
|
483
|
+
prompt: "Summarize this document in 3 key points",
|
|
484
|
+
files: [
|
|
485
|
+
{
|
|
486
|
+
source: "https://example.com/whitepaper.pdf",
|
|
487
|
+
mediaType: "application/pdf",
|
|
488
|
+
filename: "whitepaper.pdf",
|
|
489
|
+
description: "Technical whitepaper"
|
|
490
|
+
}
|
|
491
|
+
],
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
// Extract data from a CSV (base64)
|
|
495
|
+
const csvBase64 = "data:text/csv;base64,bmFtZSxhZ2UsY2l0eSpFdmlzLDM1LExvbmRvbg==";
|
|
496
|
+
const { content } = await ai.text({
|
|
497
|
+
model: "gemini-2.5-flash",
|
|
498
|
+
prompt: "Find all people over 30 and their cities",
|
|
499
|
+
files: [
|
|
500
|
+
{
|
|
501
|
+
source: csvBase64,
|
|
502
|
+
mediaType: "text/csv",
|
|
503
|
+
filename: "users.csv",
|
|
504
|
+
description: "User database export"
|
|
505
|
+
}
|
|
506
|
+
],
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
// Code review and security analysis
|
|
510
|
+
const { content } = await ai.text({
|
|
511
|
+
model: "claude-sonnet-4-5",
|
|
512
|
+
prompt: "Find security vulnerabilities and suggest fixes",
|
|
513
|
+
files: [
|
|
514
|
+
{
|
|
515
|
+
source: "https://example.com/auth-handler.ts",
|
|
516
|
+
mediaType: "text/typescript",
|
|
517
|
+
filename: "auth.ts",
|
|
518
|
+
description: "Authentication handler"
|
|
519
|
+
}
|
|
520
|
+
],
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
// Compare multiple documents
|
|
524
|
+
const { content } = await ai.text({
|
|
525
|
+
model: "claude-sonnet-4-5",
|
|
526
|
+
prompt: "Are these API schemas compatible? List differences.",
|
|
527
|
+
files: [
|
|
528
|
+
{
|
|
529
|
+
source: "data:application/json;base64,eyJwcm9wc...",
|
|
530
|
+
mediaType: "application/json",
|
|
531
|
+
filename: "v1-schema.json",
|
|
532
|
+
description: "Original API schema"
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
source: "data:application/json;base64,eyJwcm9wc...",
|
|
536
|
+
mediaType: "application/json",
|
|
537
|
+
filename: "v2-schema.json",
|
|
538
|
+
description: "New API schema"
|
|
539
|
+
}
|
|
540
|
+
],
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
// Video/audio transcription and analysis
|
|
544
|
+
const { content } = await ai.text({
|
|
545
|
+
model: "claude-sonnet-4-5",
|
|
546
|
+
prompt: "Transcribe and summarize this podcast episode",
|
|
547
|
+
files: [
|
|
548
|
+
{
|
|
549
|
+
source: "https://example.com/podcast.mp3",
|
|
550
|
+
mediaType: "audio/mpeg",
|
|
551
|
+
filename: "episode-42.mp3",
|
|
552
|
+
description: "Weekly podcast episode"
|
|
553
|
+
}
|
|
554
|
+
],
|
|
555
|
+
});
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
### Local AI models (Ollama)
|
|
559
|
+
|
|
560
|
+
```ts
|
|
561
|
+
// If Ollama is configured on your server, use local models
|
|
562
|
+
const { content } = await ai.text({
|
|
563
|
+
model: "ollama:mistral",
|
|
564
|
+
prompt: "Explain quantum computing",
|
|
565
|
+
});
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
### Image generation
|
|
569
|
+
|
|
570
|
+
**Text-to-Image:**
|
|
571
|
+
```ts
|
|
572
|
+
const { files } = await ai.image({
|
|
573
|
+
model: "imagen-4.0-generate-001",
|
|
574
|
+
prompt: "A futuristic city at sunset",
|
|
575
|
+
aspectRatio: "16:9",
|
|
576
|
+
numberOfImages: 2,
|
|
577
|
+
outputFolder: "ai-generated", // saved to project storage
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
// Access generated images
|
|
581
|
+
for (const file of files) {
|
|
582
|
+
console.log(file.fullPath); // e.g. "ai-generated/img_123.png"
|
|
583
|
+
const url = await storage.ref(file.fullPath).getDownloadURL();
|
|
584
|
+
}
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
**Image-to-Image** (style transfer, upscaling, inpainting):
|
|
588
|
+
```ts
|
|
589
|
+
// Style transfer: apply Van Gogh style to a photo
|
|
590
|
+
const { files } = await ai.image({
|
|
591
|
+
model: "imagen-4.0-generate-001",
|
|
592
|
+
prompt: "Repaint in the style of Van Gogh's Starry Night",
|
|
593
|
+
referenceImage: {
|
|
594
|
+
source: "https://example.com/portrait.jpg",
|
|
595
|
+
strength: 0.6, // 0–1: how much to preserve the reference
|
|
596
|
+
},
|
|
597
|
+
outputFolder: "styled",
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
// Upscaling: enhance and detail an existing image
|
|
601
|
+
const { files: upscaled } = await ai.image({
|
|
602
|
+
model: "imagen-4.0-generate-001",
|
|
603
|
+
prompt: "Upscale with enhanced details, make it sharper and more vibrant",
|
|
604
|
+
referenceImage: {
|
|
605
|
+
source: "data:image/jpeg;base64,/9j/4AAQSkZJRg...", // base64 image
|
|
606
|
+
strength: 0.9, // high similarity to original
|
|
607
|
+
},
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
// Inpainting: modify objects in an image
|
|
611
|
+
const { files: inpainted } = await ai.image({
|
|
612
|
+
model: "imagen-4.0-generate-001",
|
|
613
|
+
prompt: "Replace the sky with a dramatic sunset",
|
|
614
|
+
referenceImage: {
|
|
615
|
+
source: "https://example.com/landscape.jpg",
|
|
616
|
+
strength: 0.7,
|
|
617
|
+
},
|
|
618
|
+
});
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
### Video generation (Veo 2)
|
|
622
|
+
|
|
623
|
+
**Text-to-Video:**
|
|
624
|
+
```ts
|
|
625
|
+
// Async operation — waits for completion (1–5 minutes)
|
|
626
|
+
const { status, files } = await ai.video({
|
|
627
|
+
model: "veo-3.1-generate-preview",
|
|
628
|
+
prompt: "A golden retriever on a sunny beach",
|
|
629
|
+
durationSeconds: 5,
|
|
630
|
+
aspectRatio: "16:9",
|
|
631
|
+
outputFolder: "videos",
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
const videoUrl = await storage.ref(files[0].fullPath).getDownloadURL();
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
**Image-to-Video** (animate photos, create camera movements):
|
|
638
|
+
```ts
|
|
639
|
+
// Animate a static photo
|
|
640
|
+
const { files } = await ai.video({
|
|
641
|
+
model: "veo-3.1-generate-preview",
|
|
642
|
+
prompt: "The person starts walking toward the camera with a smile",
|
|
643
|
+
referenceImage: {
|
|
644
|
+
source: "https://example.com/portrait.jpg",
|
|
645
|
+
strength: 0.8, // strong visual consistency
|
|
646
|
+
},
|
|
647
|
+
durationSeconds: 4,
|
|
648
|
+
outputFolder: "animations",
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
// Cinematic camera movement
|
|
652
|
+
const { files: cinematic } = await ai.video({
|
|
653
|
+
model: "veo-3.1-generate-preview",
|
|
654
|
+
prompt: "Smooth dolly-in camera movement, revealing more landscape detail",
|
|
655
|
+
referenceImage: {
|
|
656
|
+
source: "https://example.com/landscape.jpg",
|
|
657
|
+
strength: 0.7,
|
|
658
|
+
},
|
|
659
|
+
durationSeconds: 6,
|
|
660
|
+
aspectRatio: "16:9",
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
// Temporal extension (what happens next?)
|
|
664
|
+
const { files: extended } = await ai.video({
|
|
665
|
+
model: "veo-3.1-generate-preview",
|
|
666
|
+
prompt: "The scene transitions to evening with golden hour lighting",
|
|
667
|
+
referenceImage: {
|
|
668
|
+
source: "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
|
|
669
|
+
strength: 0.6, // more creative freedom
|
|
670
|
+
},
|
|
671
|
+
durationSeconds: 5,
|
|
672
|
+
});
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
### Embeddings
|
|
676
|
+
|
|
677
|
+
```ts
|
|
678
|
+
const { embeddings } = await ai.embedding({
|
|
679
|
+
model: "gemini-embedding-001",
|
|
680
|
+
input: ["apple", "orange", "banana"],
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
// embeddings[0] → vector for "apple"
|
|
684
|
+
// embeddings[1] → vector for "orange"
|
|
685
|
+
// etc.
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
### List available models
|
|
689
|
+
|
|
690
|
+
```ts
|
|
691
|
+
// All models
|
|
692
|
+
const all = await ai.listModels();
|
|
693
|
+
|
|
694
|
+
// Filter by category
|
|
695
|
+
const textModels = await ai.listModels({ category: "text" });
|
|
696
|
+
const imageModels = await ai.listModels({ category: "image" });
|
|
697
|
+
|
|
698
|
+
// Filter by provider
|
|
699
|
+
const googleModels = await ai.listModels({ provider: "google" });
|
|
700
|
+
const anthropicModels = await ai.listModels({ provider: "anthropic" });
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
### Usage statistics
|
|
704
|
+
|
|
705
|
+
```ts
|
|
706
|
+
const stats = await ai.getStats();
|
|
707
|
+
console.log(stats.totalRequests, stats.totalInputTokens, stats.totalMediaGenerated);
|
|
708
|
+
|
|
709
|
+
// Get request log
|
|
710
|
+
const records = await ai.getUsage({ limit: 20 });
|
|
711
|
+
for (const r of records) {
|
|
712
|
+
console.log(r.model, r.status, r.createdAt);
|
|
713
|
+
}
|
|
187
714
|
```
|
|
188
715
|
|
|
189
716
|
---
|
|
190
717
|
|
|
191
718
|
## Hosting
|
|
192
719
|
|
|
193
|
-
### SDK
|
|
720
|
+
### SDK: Deploy and manage sites
|
|
194
721
|
|
|
195
722
|
```ts
|
|
196
723
|
import fs from "fs";
|
|
724
|
+
import { getHosting } from "clefbase";
|
|
725
|
+
|
|
197
726
|
const hosting = getHosting(app);
|
|
198
727
|
|
|
199
|
-
// List sites
|
|
728
|
+
// List all sites
|
|
200
729
|
const sites = await hosting.listSites();
|
|
201
730
|
|
|
202
731
|
// Create a site
|
|
@@ -217,51 +746,102 @@ console.log(`Live at: ${result.url}`);
|
|
|
217
746
|
|
|
218
747
|
// Get active deploy
|
|
219
748
|
const active = await hosting.site(site.id).getActiveDeploy();
|
|
749
|
+
|
|
750
|
+
// List deploy history
|
|
751
|
+
const deploys = await hosting.site(site.id).listDeploys({ limit: 10 });
|
|
220
752
|
```
|
|
221
753
|
|
|
222
|
-
### CLI
|
|
754
|
+
### CLI: Deploy from command line
|
|
223
755
|
|
|
224
756
|
```bash
|
|
225
757
|
# First-time setup (interactive)
|
|
226
758
|
clefbase init
|
|
227
759
|
|
|
228
|
-
# Build then deploy
|
|
760
|
+
# Build then deploy
|
|
229
761
|
npm run build && clefbase deploy
|
|
230
762
|
|
|
231
763
|
# Deploy with options
|
|
232
764
|
clefbase deploy --dir ./build --message "v2 release"
|
|
233
|
-
clefbase deploy --site <siteId> # override
|
|
765
|
+
clefbase deploy --site <siteId> # override linked site
|
|
234
766
|
|
|
235
|
-
# Manage
|
|
236
|
-
clefbase hosting:init # link
|
|
767
|
+
# Manage sites
|
|
768
|
+
clefbase hosting:init # link or create a site
|
|
237
769
|
clefbase hosting:status # show current live deploy
|
|
238
770
|
clefbase hosting:sites # list all sites
|
|
771
|
+
clefbase hosting:list-deploys # show deployment history
|
|
239
772
|
|
|
240
773
|
# Project info
|
|
241
|
-
clefbase info # config
|
|
774
|
+
clefbase info # show config and connectivity
|
|
775
|
+
clefbase --version # show SDK version
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
### Custom domains
|
|
779
|
+
|
|
780
|
+
```ts
|
|
781
|
+
// Add custom domain to site
|
|
782
|
+
await hosting.site(siteId).addCustomDomain("app.example.com");
|
|
783
|
+
|
|
784
|
+
// Get custom domains
|
|
785
|
+
const domains = await hosting.site(siteId).getCustomDomains();
|
|
786
|
+
|
|
787
|
+
// Remove custom domain
|
|
788
|
+
await hosting.site(siteId).removeCustomDomain("app.example.com");
|
|
242
789
|
```
|
|
243
790
|
|
|
244
791
|
---
|
|
245
792
|
|
|
246
|
-
##
|
|
793
|
+
## TypeScript
|
|
247
794
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
795
|
+
### Full type safety
|
|
796
|
+
|
|
797
|
+
```ts
|
|
798
|
+
import type { ClefbaseDocument } from "clefbase";
|
|
799
|
+
|
|
800
|
+
interface Post extends ClefbaseDocument {
|
|
801
|
+
title: string;
|
|
802
|
+
published: boolean;
|
|
803
|
+
views: number;
|
|
804
|
+
tags: string[];
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
const posts = db.collection<Post>("posts");
|
|
808
|
+
const post = await posts.doc("p1").get();
|
|
809
|
+
// post.views is number ✓, post.author doesn't exist ✗
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
### Function types
|
|
813
|
+
|
|
814
|
+
```ts
|
|
815
|
+
import { httpsCallable } from "clefbase";
|
|
816
|
+
|
|
817
|
+
const add = httpsCallable<
|
|
818
|
+
{ a: number; b: number },
|
|
819
|
+
{ sum: number }
|
|
820
|
+
>(functions, "add");
|
|
821
|
+
|
|
822
|
+
const { data } = await add({ a: 3, b: 4 });
|
|
823
|
+
console.log(data.sum); // ✓
|
|
824
|
+
// console.log(data.product); // ✗ TypeScript error
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
### Auth types
|
|
828
|
+
|
|
829
|
+
```ts
|
|
830
|
+
import type { AuthUser } from "clefbase";
|
|
831
|
+
|
|
832
|
+
const user: AuthUser | null = auth.currentUser;
|
|
833
|
+
if (user) {
|
|
834
|
+
console.log(user.uid, user.email, user.displayName);
|
|
835
|
+
}
|
|
836
|
+
```
|
|
259
837
|
|
|
260
838
|
---
|
|
261
839
|
|
|
262
|
-
##
|
|
840
|
+
## Configuration
|
|
263
841
|
|
|
264
|
-
|
|
842
|
+
### clefbase.json
|
|
843
|
+
|
|
844
|
+
Written by `clefbase init`. Never commit this file — add to `.gitignore` automatically.
|
|
265
845
|
|
|
266
846
|
```json
|
|
267
847
|
{
|
|
@@ -272,8 +852,10 @@ Written by `clefbase init`. Never commit this file — it contains your secrets.
|
|
|
272
852
|
"services": {
|
|
273
853
|
"database": true,
|
|
274
854
|
"auth": true,
|
|
275
|
-
"storage":
|
|
276
|
-
"hosting": true
|
|
855
|
+
"storage": true,
|
|
856
|
+
"hosting": true,
|
|
857
|
+
"functions": true,
|
|
858
|
+
"ai": true
|
|
277
859
|
},
|
|
278
860
|
"hosting": {
|
|
279
861
|
"siteId": "355f3976-89dc-...",
|
|
@@ -286,18 +868,129 @@ Written by `clefbase init`. Never commit this file — it contains your secrets.
|
|
|
286
868
|
|
|
287
869
|
---
|
|
288
870
|
|
|
289
|
-
##
|
|
871
|
+
## Error Handling
|
|
290
872
|
|
|
291
|
-
|
|
873
|
+
### Database errors
|
|
292
874
|
|
|
293
875
|
```ts
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
876
|
+
import { ClefbaseError } from "clefbase";
|
|
877
|
+
|
|
878
|
+
try {
|
|
879
|
+
await db.collection("users").doc("id").get();
|
|
880
|
+
} catch (err) {
|
|
881
|
+
if (err instanceof ClefbaseError) {
|
|
882
|
+
console.error(err.message, err.code, err.status);
|
|
883
|
+
}
|
|
298
884
|
}
|
|
885
|
+
```
|
|
299
886
|
|
|
300
|
-
|
|
887
|
+
### Functions errors
|
|
888
|
+
|
|
889
|
+
```ts
|
|
890
|
+
import { FunctionsError } from "clefbase";
|
|
891
|
+
|
|
892
|
+
try {
|
|
893
|
+
await functions.call("myFunction");
|
|
894
|
+
} catch (err) {
|
|
895
|
+
if (err instanceof FunctionsError) {
|
|
896
|
+
console.error(err.message, err.httpStatus);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
### AI errors
|
|
902
|
+
|
|
903
|
+
```ts
|
|
904
|
+
import { AIError } from "clefbase";
|
|
905
|
+
|
|
906
|
+
try {
|
|
907
|
+
await ai.text({ model: "claude-sonnet-4-5", prompt: "Hi" });
|
|
908
|
+
} catch (err) {
|
|
909
|
+
if (err instanceof AIError) {
|
|
910
|
+
console.error(err.message, err.httpStatus);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
```
|
|
914
|
+
|
|
915
|
+
---
|
|
916
|
+
|
|
917
|
+
## Environment variables
|
|
918
|
+
|
|
919
|
+
Create a `.env` file with:
|
|
920
|
+
|
|
921
|
+
```bash
|
|
922
|
+
CLEFORYX_SERVER_URL=https://api.cleforyx.com
|
|
923
|
+
CLEFORYX_PROJECT_ID=your_project_id
|
|
924
|
+
CLEFORYX_API_KEY=your_api_key
|
|
925
|
+
CLEFORYX_ADMIN_SECRET=your_admin_secret
|
|
926
|
+
```
|
|
927
|
+
|
|
928
|
+
Or load into environment and use:
|
|
929
|
+
|
|
930
|
+
```ts
|
|
931
|
+
const app = initClefbase({
|
|
932
|
+
serverUrl: process.env.CLEFORYX_SERVER_URL!,
|
|
933
|
+
projectId: process.env.CLEFORYX_PROJECT_ID!,
|
|
934
|
+
apiKey: process.env.CLEFORYX_API_KEY!,
|
|
935
|
+
adminSecret: process.env.CLEFORYX_ADMIN_SECRET,
|
|
936
|
+
});
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
---
|
|
940
|
+
|
|
941
|
+
## Advanced topics
|
|
942
|
+
|
|
943
|
+
### Custom HTTP headers
|
|
944
|
+
|
|
945
|
+
```ts
|
|
946
|
+
import { HttpClient } from "clefbase";
|
|
947
|
+
|
|
948
|
+
const client = new HttpClient(
|
|
949
|
+
"https://api.cleforyx.com/db",
|
|
950
|
+
{ "x-cfx-key": "your-api-key" }
|
|
951
|
+
);
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
### Offline-first patterns
|
|
955
|
+
|
|
956
|
+
```ts
|
|
957
|
+
// Store auth token locally
|
|
958
|
+
localStorage.setItem("cfx_token", token);
|
|
959
|
+
|
|
960
|
+
// On app load
|
|
961
|
+
const saved = localStorage.getItem("cfx_token");
|
|
962
|
+
if (saved) {
|
|
963
|
+
setAuthToken(app, saved);
|
|
964
|
+
const user = auth.currentUser;
|
|
965
|
+
// Use cached state while syncing in background
|
|
966
|
+
}
|
|
967
|
+
```
|
|
968
|
+
|
|
969
|
+
### Server-side usage (Node.js)
|
|
970
|
+
|
|
971
|
+
All SDK methods work on Node.js:
|
|
972
|
+
|
|
973
|
+
```ts
|
|
974
|
+
import { initClefbase, getDatabase } from "clefbase";
|
|
975
|
+
|
|
976
|
+
const app = initClefbase({
|
|
977
|
+
serverUrl: "https://api.cleforyx.com",
|
|
978
|
+
projectId: "my_project",
|
|
979
|
+
apiKey: process.env.CLEFORYX_API_KEY!,
|
|
980
|
+
});
|
|
981
|
+
|
|
982
|
+
const db = getDatabase(app);
|
|
983
|
+
const users = await db.collection("users").getDocs();
|
|
984
|
+
```
|
|
985
|
+
|
|
986
|
+
---
|
|
987
|
+
|
|
988
|
+
## Support
|
|
989
|
+
|
|
990
|
+
- **Docs**: https://cleforyx.com/docs
|
|
991
|
+
- **GitHub**: https://github.com/cleforyx/clefbase
|
|
992
|
+
- **Issues**: https://github.com/cleforyx/clefbase/issues
|
|
993
|
+
- **Community**: https://discord.gg/cleforyx
|
|
301
994
|
const post = await posts.doc("abc").get(); // Post | null
|
|
302
995
|
```
|
|
303
996
|
|