clefbase 1.3.6 → 1.3.8
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/dist/cli-src/cli/commands/deploy.js +4 -23
- package/dist/cli.js +4 -20
- package/dist/db/batch.d.ts +62 -0
- package/dist/db/batch.d.ts.map +1 -0
- package/dist/db/batch.js +94 -0
- package/dist/db/batch.js.map +1 -0
- package/dist/db/index.d.ts +121 -2
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +198 -3
- package/dist/db/index.js.map +1 -1
- package/dist/db/transaction.d.ts +96 -0
- package/dist/db/transaction.d.ts.map +1 -0
- package/dist/db/transaction.js +130 -0
- package/dist/db/transaction.js.map +1 -0
- package/dist/field_value.d.ts +93 -0
- package/dist/field_value.d.ts.map +1 -0
- package/dist/field_value.js +161 -0
- package/dist/field_value.js.map +1 -0
- package/dist/index.d.ts +30 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +36 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/db/index.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Database = exports.CollectionReference = exports.Query = exports.DocumentReference = void 0;
|
|
3
|
+
exports.Database = exports.CollectionGroup = exports.CollectionReference = exports.Query = exports.DocumentReference = exports.runTransaction = exports.Transaction = exports.WriteBatch = void 0;
|
|
4
|
+
const batch_1 = require("./batch");
|
|
5
|
+
const transaction_1 = require("./transaction");
|
|
6
|
+
var batch_2 = require("./batch");
|
|
7
|
+
Object.defineProperty(exports, "WriteBatch", { enumerable: true, get: function () { return batch_2.WriteBatch; } });
|
|
8
|
+
var transaction_2 = require("./transaction");
|
|
9
|
+
Object.defineProperty(exports, "Transaction", { enumerable: true, get: function () { return transaction_2.Transaction; } });
|
|
10
|
+
Object.defineProperty(exports, "runTransaction", { enumerable: true, get: function () { return transaction_2.runTransaction; } });
|
|
4
11
|
// ─── DocumentReference ────────────────────────────────────────────────────────
|
|
5
12
|
/**
|
|
6
13
|
* A typed reference to a single document.
|
|
@@ -18,6 +25,10 @@ class DocumentReference {
|
|
|
18
25
|
this.collectionPath = collectionPath;
|
|
19
26
|
this.id = id;
|
|
20
27
|
}
|
|
28
|
+
/** Fully-qualified path string, e.g. "posts/p1" or "posts/p1/comments/c1". */
|
|
29
|
+
get path() {
|
|
30
|
+
return `${this.collectionPath}/${this.id}`;
|
|
31
|
+
}
|
|
21
32
|
/** Fetch this document. Returns null when it does not exist. */
|
|
22
33
|
async get() {
|
|
23
34
|
try {
|
|
@@ -32,6 +43,7 @@ class DocumentReference {
|
|
|
32
43
|
/**
|
|
33
44
|
* Patch this document (merge by default).
|
|
34
45
|
* Pass `{ merge: false }` to fully replace the document.
|
|
46
|
+
* Supports FieldValue sentinels — they are serialized by JSON.stringify.
|
|
35
47
|
*/
|
|
36
48
|
async update(data, opts) {
|
|
37
49
|
const merge = opts?.merge ?? true;
|
|
@@ -59,6 +71,36 @@ class DocumentReference {
|
|
|
59
71
|
subcollection(name) {
|
|
60
72
|
return this.collection(name);
|
|
61
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Subscribe to real-time updates for this document.
|
|
76
|
+
* Returns an unsubscribe function.
|
|
77
|
+
* Requires the server to support SSE at GET /<collection>/<id>/stream.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* const unsub = ref.onSnapshot((doc) => {
|
|
81
|
+
* if (doc) console.log("Updated:", doc);
|
|
82
|
+
* else console.log("Deleted");
|
|
83
|
+
* });
|
|
84
|
+
* // later:
|
|
85
|
+
* unsub();
|
|
86
|
+
*/
|
|
87
|
+
onSnapshot(callback, onError) {
|
|
88
|
+
const url = `${this.http.getBaseUrl()}/${this.collectionPath}/${this.id}/stream`;
|
|
89
|
+
const es = new globalThis.EventSource(url);
|
|
90
|
+
es.onmessage = (event) => {
|
|
91
|
+
try {
|
|
92
|
+
const data = JSON.parse(event.data);
|
|
93
|
+
callback(data);
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
onError?.(err);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
es.onerror = () => {
|
|
100
|
+
onError?.(new Error("DocumentReference.onSnapshot: SSE connection error"));
|
|
101
|
+
};
|
|
102
|
+
return () => es.close();
|
|
103
|
+
}
|
|
62
104
|
}
|
|
63
105
|
exports.DocumentReference = DocumentReference;
|
|
64
106
|
// ─── Query ────────────────────────────────────────────────────────────────────
|
|
@@ -84,12 +126,13 @@ class Query {
|
|
|
84
126
|
/**
|
|
85
127
|
* Filter by field values or operators.
|
|
86
128
|
* Multiple calls are AND-merged.
|
|
129
|
+
* Supports dot-notation for nested fields: `where({ "address.city": "London" })`
|
|
87
130
|
*/
|
|
88
131
|
where(clauses) {
|
|
89
132
|
this._filter = { ...this._filter, ...clauses };
|
|
90
133
|
return this;
|
|
91
134
|
}
|
|
92
|
-
/** Sort by a field. */
|
|
135
|
+
/** Sort by a field. Supports dot-notation: `orderBy("meta.score", "desc")` */
|
|
93
136
|
orderBy(field, dir = "asc") {
|
|
94
137
|
this._sort = { field, dir };
|
|
95
138
|
return this;
|
|
@@ -125,6 +168,69 @@ class Query {
|
|
|
125
168
|
async getDocs() {
|
|
126
169
|
return this.get();
|
|
127
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Count matching documents without fetching their data.
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* const total = await db.collection("orders").where({ status: "pending" }).count();
|
|
176
|
+
*/
|
|
177
|
+
async count() {
|
|
178
|
+
const body = {
|
|
179
|
+
filter: Object.keys(this._filter).length > 0 ? this._filter : undefined,
|
|
180
|
+
limit: 500,
|
|
181
|
+
offset: 0,
|
|
182
|
+
};
|
|
183
|
+
const result = await this.http.post(`/${this.path}/count`, body);
|
|
184
|
+
// Fallback: if server doesn't have /count, use the total from /query
|
|
185
|
+
if ("count" in result)
|
|
186
|
+
return result.count;
|
|
187
|
+
return result.total;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Fetch a single matching document (first result).
|
|
191
|
+
* Returns null when no document matches.
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* const admin = await db.collection("users").where({ role: "admin" }).findOne();
|
|
195
|
+
*/
|
|
196
|
+
async findOne() {
|
|
197
|
+
const prev = this._limit;
|
|
198
|
+
this._limit = 1;
|
|
199
|
+
const docs = await this.get();
|
|
200
|
+
this._limit = prev;
|
|
201
|
+
return docs[0] ?? null;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Iterate over all matching documents in pages, calling `callback` for
|
|
205
|
+
* each page. Useful for large datasets you don't want to load at once.
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* await db.collection("logs")
|
|
209
|
+
* .where({ level: "error" })
|
|
210
|
+
* .orderBy("_createdAt")
|
|
211
|
+
* .forEach(async (page) => {
|
|
212
|
+
* for (const doc of page) await process(doc);
|
|
213
|
+
* }, { pageSize: 100 });
|
|
214
|
+
*/
|
|
215
|
+
async forEach(callback, opts) {
|
|
216
|
+
const pageSize = opts?.pageSize ?? 100;
|
|
217
|
+
let offset = 0;
|
|
218
|
+
while (true) {
|
|
219
|
+
const body = {
|
|
220
|
+
filter: Object.keys(this._filter).length > 0 ? this._filter : undefined,
|
|
221
|
+
sort: this._sort,
|
|
222
|
+
limit: pageSize,
|
|
223
|
+
offset,
|
|
224
|
+
};
|
|
225
|
+
const result = await this.http.post(`/${this.path}/query`, body);
|
|
226
|
+
if (result.data.length === 0)
|
|
227
|
+
break;
|
|
228
|
+
await callback(result.data);
|
|
229
|
+
offset += result.data.length;
|
|
230
|
+
if (result.data.length < pageSize)
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
128
234
|
}
|
|
129
235
|
exports.Query = Query;
|
|
130
236
|
// ─── CollectionReference ──────────────────────────────────────────────────────
|
|
@@ -161,22 +267,68 @@ class CollectionReference extends Query {
|
|
|
161
267
|
}
|
|
162
268
|
}
|
|
163
269
|
exports.CollectionReference = CollectionReference;
|
|
270
|
+
// ─── CollectionGroup ──────────────────────────────────────────────────────────
|
|
271
|
+
/**
|
|
272
|
+
* Query across ALL collections (and subcollections) that share the same name.
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* // Find all comments across every post
|
|
276
|
+
* const allComments = await db
|
|
277
|
+
* .collectionGroup("comments")
|
|
278
|
+
* .where({ approved: true })
|
|
279
|
+
* .orderBy("_createdAt", "desc")
|
|
280
|
+
* .limit(50)
|
|
281
|
+
* .getDocs();
|
|
282
|
+
*/
|
|
283
|
+
class CollectionGroup extends Query {
|
|
284
|
+
constructor(http, collectionId) {
|
|
285
|
+
// The server exposes collection-group queries at a special path
|
|
286
|
+
super(http, `__group__/${collectionId}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
exports.CollectionGroup = CollectionGroup;
|
|
164
290
|
// ─── Database ─────────────────────────────────────────────────────────────────
|
|
165
291
|
/**
|
|
166
292
|
* Top-level database service. Obtain via `getDatabase(app)`.
|
|
167
293
|
*
|
|
168
294
|
* @example
|
|
169
295
|
* const db = getDatabase(app);
|
|
296
|
+
*
|
|
297
|
+
* // Document reads / writes
|
|
170
298
|
* const post = await db.collection("posts").doc("p1").get();
|
|
171
|
-
*
|
|
299
|
+
* await db.collection("posts").doc("p1").update({ views: FieldValue.increment(1) });
|
|
300
|
+
*
|
|
301
|
+
* // Atomic batch write
|
|
302
|
+
* const batch = db.batch();
|
|
303
|
+
* batch.update(db.collection("counters").doc("hits"), { n: FieldValue.increment(1) });
|
|
304
|
+
* batch.delete(db.collection("sessions").doc("expired-id"));
|
|
305
|
+
* await batch.commit();
|
|
306
|
+
*
|
|
307
|
+
* // Transaction (read-then-write)
|
|
308
|
+
* await db.runTransaction(async (tx) => {
|
|
309
|
+
* const doc = await tx.get(db.collection("wallets").doc(id));
|
|
310
|
+
* tx.update(db.collection("wallets").doc(id), { balance: (doc?.balance as number ?? 0) + 10 });
|
|
311
|
+
* });
|
|
172
312
|
*/
|
|
173
313
|
class Database {
|
|
174
314
|
constructor(http) {
|
|
175
315
|
this.http = http;
|
|
176
316
|
}
|
|
317
|
+
// ─── Collection / document accessors ───────────────────────────────────────
|
|
177
318
|
collection(name) {
|
|
178
319
|
return new CollectionReference(this.http, name);
|
|
179
320
|
}
|
|
321
|
+
/**
|
|
322
|
+
* Query across ALL collections that share `collectionId`, regardless of
|
|
323
|
+
* where they are nested in the document tree.
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* const allLikes = await db.collectionGroup("likes").where({ userId: uid }).getDocs();
|
|
327
|
+
*/
|
|
328
|
+
collectionGroup(collectionId) {
|
|
329
|
+
return new CollectionGroup(this.http, collectionId);
|
|
330
|
+
}
|
|
331
|
+
// ─── Convenience document helpers ──────────────────────────────────────────
|
|
180
332
|
async getDoc(collectionName, id) {
|
|
181
333
|
return this.collection(collectionName).doc(id).get();
|
|
182
334
|
}
|
|
@@ -189,6 +341,49 @@ class Database {
|
|
|
189
341
|
async deleteDoc(collectionName, id) {
|
|
190
342
|
return this.collection(collectionName).doc(id).delete();
|
|
191
343
|
}
|
|
344
|
+
/**
|
|
345
|
+
* Fetch a document by its full slash-separated path string.
|
|
346
|
+
*
|
|
347
|
+
* @example
|
|
348
|
+
* const comment = await db.doc("posts/p1/comments/c1");
|
|
349
|
+
*/
|
|
350
|
+
async doc(path) {
|
|
351
|
+
const parts = path.split("/").filter(Boolean);
|
|
352
|
+
if (parts.length < 2 || parts.length % 2 !== 0) {
|
|
353
|
+
throw new Error(`db.doc(): path must have an even number of segments (collection/id), got "${path}"`);
|
|
354
|
+
}
|
|
355
|
+
const id = parts.pop();
|
|
356
|
+
const collectionPath = parts.join("/");
|
|
357
|
+
return new DocumentReference(this.http, collectionPath, id).get();
|
|
358
|
+
}
|
|
359
|
+
// ─── Atomic writes ─────────────────────────────────────────────────────────
|
|
360
|
+
/**
|
|
361
|
+
* Create an atomic WriteBatch. Stage operations with `batch.set()`,
|
|
362
|
+
* `batch.update()`, `batch.delete()`, then call `batch.commit()`.
|
|
363
|
+
*
|
|
364
|
+
* @example
|
|
365
|
+
* const batch = db.batch();
|
|
366
|
+
* batch.set(db.collection("users").doc("u1"), { name: "Alice" });
|
|
367
|
+
* batch.update(db.collection("counters").doc("users"), { total: FieldValue.increment(1) });
|
|
368
|
+
* await batch.commit();
|
|
369
|
+
*/
|
|
370
|
+
batch() {
|
|
371
|
+
return new batch_1.WriteBatch(this.http);
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Execute a transaction — reads are consistent, writes are atomic.
|
|
375
|
+
*
|
|
376
|
+
* @example
|
|
377
|
+
* await db.runTransaction(async (tx) => {
|
|
378
|
+
* const wallet = await tx.get(db.collection("wallets").doc(userId));
|
|
379
|
+
* if ((wallet?.credits as number ?? 0) < cost) throw new Error("Insufficient credits");
|
|
380
|
+
* tx.update(db.collection("wallets").doc(userId), { credits: (wallet!.credits as number) - cost });
|
|
381
|
+
* tx.update(db.collection("items").doc(itemId), { ownerId: userId });
|
|
382
|
+
* });
|
|
383
|
+
*/
|
|
384
|
+
async runTransaction(updateFn) {
|
|
385
|
+
return (0, transaction_1.runTransaction)(this.http, updateFn);
|
|
386
|
+
}
|
|
192
387
|
}
|
|
193
388
|
exports.Database = Database;
|
|
194
389
|
//# sourceMappingURL=index.js.map
|
package/dist/db/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { HttpClient } from "../http";
|
|
2
|
+
import type { ClefbaseDocument } from "../types";
|
|
3
|
+
type TxWriteOp = {
|
|
4
|
+
type: "set" | "update" | "delete";
|
|
5
|
+
collectionPath: string;
|
|
6
|
+
id: string;
|
|
7
|
+
data?: Record<string, unknown>;
|
|
8
|
+
merge?: boolean;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Handle passed to your transaction callback.
|
|
12
|
+
* Accumulates reads and writes, then flushes them in a single atomic request.
|
|
13
|
+
*/
|
|
14
|
+
export declare class Transaction {
|
|
15
|
+
private readonly http;
|
|
16
|
+
/** @internal */
|
|
17
|
+
readonly _reads: {
|
|
18
|
+
collectionPath: string;
|
|
19
|
+
id: string;
|
|
20
|
+
}[];
|
|
21
|
+
/** @internal */
|
|
22
|
+
readonly _writes: TxWriteOp[];
|
|
23
|
+
private _writesStarted;
|
|
24
|
+
constructor(http: HttpClient);
|
|
25
|
+
/**
|
|
26
|
+
* Read a document inside the transaction.
|
|
27
|
+
* Must be called before any write operation.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* const user = await tx.get(db.collection("users").doc("u1"));
|
|
31
|
+
*/
|
|
32
|
+
get<T extends ClefbaseDocument>(ref: {
|
|
33
|
+
collectionPath: string;
|
|
34
|
+
id: string;
|
|
35
|
+
}): Promise<T | null>;
|
|
36
|
+
/**
|
|
37
|
+
* Stage a full overwrite inside the transaction.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* tx.set(db.collection("users").doc("u1"), { name: "Alice", role: "admin" });
|
|
41
|
+
*/
|
|
42
|
+
set<T extends ClefbaseDocument>(ref: {
|
|
43
|
+
collectionPath: string;
|
|
44
|
+
id: string;
|
|
45
|
+
}, data: Omit<T, "_id" | "_createdAt" | "_updatedAt">): this;
|
|
46
|
+
/**
|
|
47
|
+
* Stage a merge-patch update inside the transaction.
|
|
48
|
+
* Supports FieldValue sentinels.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* tx.update(db.collection("posts").doc("p1"), { views: FieldValue.increment(1) });
|
|
52
|
+
*/
|
|
53
|
+
update<T extends ClefbaseDocument>(ref: {
|
|
54
|
+
collectionPath: string;
|
|
55
|
+
id: string;
|
|
56
|
+
}, data: Partial<Omit<T, "_id" | "_createdAt" | "_updatedAt">>): this;
|
|
57
|
+
/**
|
|
58
|
+
* Stage a document deletion inside the transaction.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* tx.delete(db.collection("sessions").doc("s9"));
|
|
62
|
+
*/
|
|
63
|
+
delete(ref: {
|
|
64
|
+
collectionPath: string;
|
|
65
|
+
id: string;
|
|
66
|
+
}): this;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Execute an atomic transaction.
|
|
70
|
+
*
|
|
71
|
+
* Your callback receives a `Transaction` handle. Call `tx.get()` to read
|
|
72
|
+
* documents, then `tx.set()` / `tx.update()` / `tx.delete()` to stage writes.
|
|
73
|
+
* The SDK sends all writes in one atomic request after your callback resolves.
|
|
74
|
+
*
|
|
75
|
+
* The callback may be retried on contention — keep it free of side effects.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* await db.runTransaction(async (tx) => {
|
|
79
|
+
* const counter = await tx.get(db.collection("stats").doc("global"));
|
|
80
|
+
* const current = (counter?.total as number) ?? 0;
|
|
81
|
+
* tx.update(db.collection("stats").doc("global"), { total: current + 1 });
|
|
82
|
+
* });
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* // Transfer credits atomically
|
|
86
|
+
* await db.runTransaction(async (tx) => {
|
|
87
|
+
* const from = await tx.get(db.collection("wallets").doc(fromId));
|
|
88
|
+
* const to = await tx.get(db.collection("wallets").doc(toId));
|
|
89
|
+
* if (!from || (from.credits as number) < amount) throw new Error("Insufficient credits");
|
|
90
|
+
* tx.update(db.collection("wallets").doc(fromId), { credits: (from.credits as number) - amount });
|
|
91
|
+
* tx.update(db.collection("wallets").doc(toId), { credits: ((to?.credits as number) ?? 0) + amount });
|
|
92
|
+
* });
|
|
93
|
+
*/
|
|
94
|
+
export declare function runTransaction<T = void>(http: HttpClient, updateFn: (tx: Transaction) => Promise<T>): Promise<T>;
|
|
95
|
+
export {};
|
|
96
|
+
//# sourceMappingURL=transaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../../src/db/transaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAsBjD,KAAK,SAAS,GAAG;IACf,IAAI,EAAY,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC5C,cAAc,EAAE,MAAM,CAAC;IACvB,EAAE,EAAc,MAAM,CAAC;IACvB,IAAI,CAAC,EAAW,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,KAAK,CAAC,EAAU,OAAO,CAAC;CACzB,CAAC;AAEF;;;GAGG;AACH,qBAAa,WAAW;IAQV,OAAO,CAAC,QAAQ,CAAC,IAAI;IAPjC,gBAAgB;IAChB,QAAQ,CAAC,MAAM,EAAG;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAM;IAChE,gBAAgB;IAChB,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,CAAM;IAEnC,OAAO,CAAC,cAAc,CAAS;gBAEF,IAAI,EAAE,UAAU;IAE7C;;;;;;OAMG;IACG,GAAG,CAAC,CAAC,SAAS,gBAAgB,EAClC,GAAG,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAC1C,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAgBpB;;;;;OAKG;IACH,GAAG,CAAC,CAAC,SAAS,gBAAgB,EAC5B,GAAG,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAC3C,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,KAAK,GAAG,YAAY,GAAG,YAAY,CAAC,GACjD,IAAI;IAYP;;;;;;OAMG;IACH,MAAM,CAAC,CAAC,SAAS,gBAAgB,EAC/B,GAAG,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAC3C,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,GAAG,YAAY,GAAG,YAAY,CAAC,CAAC,GAC1D,IAAI;IAYP;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;CAS1D;AAID;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,cAAc,CAAC,CAAC,GAAG,IAAI,EAC3C,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GACxC,OAAO,CAAC,CAAC,CAAC,CAaZ"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Transaction = void 0;
|
|
4
|
+
exports.runTransaction = runTransaction;
|
|
5
|
+
/**
|
|
6
|
+
* Handle passed to your transaction callback.
|
|
7
|
+
* Accumulates reads and writes, then flushes them in a single atomic request.
|
|
8
|
+
*/
|
|
9
|
+
class Transaction {
|
|
10
|
+
constructor(http) {
|
|
11
|
+
this.http = http;
|
|
12
|
+
/** @internal */
|
|
13
|
+
this._reads = [];
|
|
14
|
+
/** @internal */
|
|
15
|
+
this._writes = [];
|
|
16
|
+
this._writesStarted = false;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Read a document inside the transaction.
|
|
20
|
+
* Must be called before any write operation.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* const user = await tx.get(db.collection("users").doc("u1"));
|
|
24
|
+
*/
|
|
25
|
+
async get(ref) {
|
|
26
|
+
if (this._writesStarted) {
|
|
27
|
+
throw new Error("Transaction: all tx.get() calls must come before any write (set/update/delete).");
|
|
28
|
+
}
|
|
29
|
+
this._reads.push({ collectionPath: ref.collectionPath, id: ref.id });
|
|
30
|
+
try {
|
|
31
|
+
return await this.http.get(`/${ref.collectionPath}/${ref.id}`);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
if (err.status === 404)
|
|
35
|
+
return null;
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Stage a full overwrite inside the transaction.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* tx.set(db.collection("users").doc("u1"), { name: "Alice", role: "admin" });
|
|
44
|
+
*/
|
|
45
|
+
set(ref, data) {
|
|
46
|
+
this._writesStarted = true;
|
|
47
|
+
this._writes.push({
|
|
48
|
+
type: "set",
|
|
49
|
+
collectionPath: ref.collectionPath,
|
|
50
|
+
id: ref.id,
|
|
51
|
+
data: data,
|
|
52
|
+
merge: false,
|
|
53
|
+
});
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Stage a merge-patch update inside the transaction.
|
|
58
|
+
* Supports FieldValue sentinels.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* tx.update(db.collection("posts").doc("p1"), { views: FieldValue.increment(1) });
|
|
62
|
+
*/
|
|
63
|
+
update(ref, data) {
|
|
64
|
+
this._writesStarted = true;
|
|
65
|
+
this._writes.push({
|
|
66
|
+
type: "update",
|
|
67
|
+
collectionPath: ref.collectionPath,
|
|
68
|
+
id: ref.id,
|
|
69
|
+
data: data,
|
|
70
|
+
merge: true,
|
|
71
|
+
});
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Stage a document deletion inside the transaction.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* tx.delete(db.collection("sessions").doc("s9"));
|
|
79
|
+
*/
|
|
80
|
+
delete(ref) {
|
|
81
|
+
this._writesStarted = true;
|
|
82
|
+
this._writes.push({
|
|
83
|
+
type: "delete",
|
|
84
|
+
collectionPath: ref.collectionPath,
|
|
85
|
+
id: ref.id,
|
|
86
|
+
});
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
exports.Transaction = Transaction;
|
|
91
|
+
// ─── runTransaction ───────────────────────────────────────────────────────────
|
|
92
|
+
/**
|
|
93
|
+
* Execute an atomic transaction.
|
|
94
|
+
*
|
|
95
|
+
* Your callback receives a `Transaction` handle. Call `tx.get()` to read
|
|
96
|
+
* documents, then `tx.set()` / `tx.update()` / `tx.delete()` to stage writes.
|
|
97
|
+
* The SDK sends all writes in one atomic request after your callback resolves.
|
|
98
|
+
*
|
|
99
|
+
* The callback may be retried on contention — keep it free of side effects.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* await db.runTransaction(async (tx) => {
|
|
103
|
+
* const counter = await tx.get(db.collection("stats").doc("global"));
|
|
104
|
+
* const current = (counter?.total as number) ?? 0;
|
|
105
|
+
* tx.update(db.collection("stats").doc("global"), { total: current + 1 });
|
|
106
|
+
* });
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* // Transfer credits atomically
|
|
110
|
+
* await db.runTransaction(async (tx) => {
|
|
111
|
+
* const from = await tx.get(db.collection("wallets").doc(fromId));
|
|
112
|
+
* const to = await tx.get(db.collection("wallets").doc(toId));
|
|
113
|
+
* if (!from || (from.credits as number) < amount) throw new Error("Insufficient credits");
|
|
114
|
+
* tx.update(db.collection("wallets").doc(fromId), { credits: (from.credits as number) - amount });
|
|
115
|
+
* tx.update(db.collection("wallets").doc(toId), { credits: ((to?.credits as number) ?? 0) + amount });
|
|
116
|
+
* });
|
|
117
|
+
*/
|
|
118
|
+
async function runTransaction(http, updateFn) {
|
|
119
|
+
const tx = new Transaction(http);
|
|
120
|
+
const result = await updateFn(tx);
|
|
121
|
+
// Flush all writes atomically (reads already happened inline above)
|
|
122
|
+
if (tx._writes.length > 0) {
|
|
123
|
+
await http.post("/transaction", {
|
|
124
|
+
reads: tx._reads,
|
|
125
|
+
writes: tx._writes,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=transaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction.js","sourceRoot":"","sources":["../../src/db/transaction.ts"],"names":[],"mappings":";;;AA8JA,wCAgBC;AA/ID;;;GAGG;AACH,MAAa,WAAW;IAQtB,YAA6B,IAAgB;QAAhB,SAAI,GAAJ,IAAI,CAAY;QAP7C,gBAAgB;QACP,WAAM,GAA8C,EAAE,CAAC;QAChE,gBAAgB;QACP,YAAO,GAAgB,EAAE,CAAC;QAE3B,mBAAc,GAAG,KAAK,CAAC;IAEiB,CAAC;IAEjD;;;;;;OAMG;IACH,KAAK,CAAC,GAAG,CACP,GAA2C;QAE3C,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CACb,iFAAiF,CAClF,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,GAAG,CAAC,cAAc,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;QAErE,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAI,IAAI,GAAG,CAAC,cAAc,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;QACpE,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,IAAK,GAA2B,CAAC,MAAM,KAAK,GAAG;gBAAE,OAAO,IAAI,CAAC;YAC7D,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,GAAG,CACD,GAA2C,EAC3C,IAAkD;QAElD,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC3B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;YAChB,IAAI,EAAY,KAAK;YACrB,cAAc,EAAE,GAAG,CAAC,cAAc;YAClC,EAAE,EAAc,GAAG,CAAC,EAAE;YACtB,IAAI,EAAY,IAA+B;YAC/C,KAAK,EAAW,KAAK;SACtB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CACJ,GAA2C,EAC3C,IAA2D;QAE3D,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC3B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;YAChB,IAAI,EAAY,QAAQ;YACxB,cAAc,EAAE,GAAG,CAAC,cAAc;YAClC,EAAE,EAAc,GAAG,CAAC,EAAE;YACtB,IAAI,EAAY,IAA+B;YAC/C,KAAK,EAAW,IAAI;SACrB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,GAA2C;QAChD,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC3B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;YAChB,IAAI,EAAY,QAAQ;YACxB,cAAc,EAAE,GAAG,CAAC,cAAc;YAClC,EAAE,EAAc,GAAG,CAAC,EAAE;SACvB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AA7FD,kCA6FC;AAED,iFAAiF;AAEjF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACI,KAAK,UAAU,cAAc,CAClC,IAAgB,EAChB,QAAyC;IAEzC,MAAM,EAAE,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,EAAE,CAAC,CAAC;IAElC,oEAAoE;IACpE,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE;YAC9B,KAAK,EAAG,EAAE,CAAC,MAAM;YACjB,MAAM,EAAE,EAAE,CAAC,OAAO;SACnB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
export type FieldValueType = "increment" | "decrement" | "serverTimestamp" | "delete" | "arrayUnion" | "arrayRemove";
|
|
2
|
+
/**
|
|
3
|
+
* Opaque sentinel returned by every FieldValue factory.
|
|
4
|
+
* Never construct this directly — use the static helpers on FieldValue.
|
|
5
|
+
*/
|
|
6
|
+
export declare class FieldValueSentinel {
|
|
7
|
+
readonly _type: FieldValueType;
|
|
8
|
+
readonly _value?: number | unknown[] | undefined;
|
|
9
|
+
/** @internal discriminator so library code can detect sentinels */
|
|
10
|
+
readonly __fieldValue: true;
|
|
11
|
+
/** @internal */
|
|
12
|
+
constructor(_type: FieldValueType, _value?: number | unknown[] | undefined);
|
|
13
|
+
/**
|
|
14
|
+
* Serialize to the wire format the Clefbase server expects.
|
|
15
|
+
* Called automatically by JSON.stringify.
|
|
16
|
+
*/
|
|
17
|
+
toJSON(): object;
|
|
18
|
+
toString(): string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Atomic field-level sentinels for database writes.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* import { FieldValue } from "clefbase";
|
|
25
|
+
*
|
|
26
|
+
* await db.collection("posts").doc("p1").update({
|
|
27
|
+
* views: FieldValue.increment(1),
|
|
28
|
+
* score: FieldValue.decrement(0.5),
|
|
29
|
+
* publishedAt: FieldValue.serverTimestamp(),
|
|
30
|
+
* draftField: FieldValue.deleteField(),
|
|
31
|
+
* tags: FieldValue.arrayUnion("featured", "trending"),
|
|
32
|
+
* oldTags: FieldValue.arrayRemove("draft"),
|
|
33
|
+
* });
|
|
34
|
+
*/
|
|
35
|
+
export declare const FieldValue: {
|
|
36
|
+
/**
|
|
37
|
+
* Atomically increment a numeric field by `delta` (default 1).
|
|
38
|
+
* Creates the field with value `delta` if it does not yet exist.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* await ref.update({ views: FieldValue.increment(1) });
|
|
42
|
+
* await ref.update({ balance: FieldValue.increment(9.99) });
|
|
43
|
+
*/
|
|
44
|
+
readonly increment: (delta?: number) => FieldValueSentinel;
|
|
45
|
+
/**
|
|
46
|
+
* Atomically decrement a numeric field by `delta` (default 1).
|
|
47
|
+
* Sugar for increment(-delta).
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* await ref.update({ lives: FieldValue.decrement(1) });
|
|
51
|
+
*/
|
|
52
|
+
readonly decrement: (delta?: number) => FieldValueSentinel;
|
|
53
|
+
/**
|
|
54
|
+
* Set the field to the server's current timestamp (ISO 8601 string).
|
|
55
|
+
* Avoids clock-skew issues by using the server clock instead of the client.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* await ref.update({ lastSeen: FieldValue.serverTimestamp() });
|
|
59
|
+
*/
|
|
60
|
+
readonly serverTimestamp: () => FieldValueSentinel;
|
|
61
|
+
/**
|
|
62
|
+
* Remove this field from the document entirely.
|
|
63
|
+
* Only valid inside `.update()` — not `.set()`.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* await ref.update({ tempToken: FieldValue.deleteField() });
|
|
67
|
+
*/
|
|
68
|
+
readonly deleteField: () => FieldValueSentinel;
|
|
69
|
+
/**
|
|
70
|
+
* Add one or more items to an array field, ignoring duplicates.
|
|
71
|
+
* Creates the field as a new array if it does not yet exist.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* await ref.update({ roles: FieldValue.arrayUnion("editor", "moderator") });
|
|
75
|
+
*/
|
|
76
|
+
readonly arrayUnion: (...items: unknown[]) => FieldValueSentinel;
|
|
77
|
+
/**
|
|
78
|
+
* Remove one or more items from an array field (all matching occurrences).
|
|
79
|
+
* No-ops silently if the item or field does not exist.
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* await ref.update({ roles: FieldValue.arrayRemove("moderator") });
|
|
83
|
+
*/
|
|
84
|
+
readonly arrayRemove: (...items: unknown[]) => FieldValueSentinel;
|
|
85
|
+
/**
|
|
86
|
+
* Type-guard — returns true if `value` is any FieldValue sentinel.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* if (FieldValue.isSentinel(val)) console.log(val._type);
|
|
90
|
+
*/
|
|
91
|
+
readonly isSentinel: (value: unknown) => value is FieldValueSentinel;
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=field_value.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field_value.d.ts","sourceRoot":"","sources":["../src/field_value.ts"],"names":[],"mappings":"AAqBA,MAAM,MAAM,cAAc,GACtB,WAAW,GACX,WAAW,GACX,iBAAiB,GACjB,QAAQ,GACR,YAAY,GACZ,aAAa,CAAC;AAElB;;;GAGG;AACH,qBAAa,kBAAkB;IAM3B,QAAQ,CAAC,KAAK,EAAE,cAAc;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,EAAE;IANtC,mEAAmE;IACnE,QAAQ,CAAC,YAAY,EAAG,IAAI,CAAU;IAEtC,gBAAgB;gBAEL,KAAK,EAAE,cAAc,EACrB,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,EAAE,YAAA;IAGtC;;;OAGG;IACH,MAAM,IAAI,MAAM;IAiBhB,QAAQ,IAAI,MAAM;CAGnB;AAID;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,UAAU;IACrB;;;;;;;OAOG;iCACc,MAAM,KAAO,kBAAkB;IAOhD;;;;;;OAMG;iCACc,MAAM,KAAO,kBAAkB;IAOhD;;;;;;OAMG;oCACgB,kBAAkB;IAIrC;;;;;;OAMG;gCACY,kBAAkB;IAIjC;;;;;;OAMG;oCACkB,OAAO,EAAE,KAAG,kBAAkB;IAOnD;;;;;;OAMG;qCACmB,OAAO,EAAE,KAAG,kBAAkB;IAOpD;;;;;OAKG;iCACe,OAAO,KAAG,KAAK,IAAI,kBAAkB;CAG/C,CAAC"}
|