clefbase 1.3.7 → 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.js +1 -1
- 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/cli.js
CHANGED
|
@@ -34523,7 +34523,7 @@ async function runSitesList(cwd = process.cwd()) {
|
|
|
34523
34523
|
}
|
|
34524
34524
|
|
|
34525
34525
|
// package.json
|
|
34526
|
-
var version = "1.3.
|
|
34526
|
+
var version = "1.3.8";
|
|
34527
34527
|
|
|
34528
34528
|
// src/cli/index.ts
|
|
34529
34529
|
var program2 = new Command();
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { HttpClient } from "../http";
|
|
2
|
+
import type { ClefbaseDocument } from "../types";
|
|
3
|
+
/**
|
|
4
|
+
* Atomic multi-document writer.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* const batch = db.batch();
|
|
8
|
+
*
|
|
9
|
+
* // set — full overwrite (no merge)
|
|
10
|
+
* batch.set(db.collection("users").doc("u1"), { name: "Alice", role: "admin" });
|
|
11
|
+
*
|
|
12
|
+
* // update — merge patch (same as doc.update())
|
|
13
|
+
* batch.update(db.collection("posts").doc("p1"), { views: FieldValue.increment(1) });
|
|
14
|
+
*
|
|
15
|
+
* // delete a document
|
|
16
|
+
* batch.delete(db.collection("sessions").doc("s9"));
|
|
17
|
+
*
|
|
18
|
+
* await batch.commit();
|
|
19
|
+
*/
|
|
20
|
+
export declare class WriteBatch {
|
|
21
|
+
private readonly http;
|
|
22
|
+
private readonly _ops;
|
|
23
|
+
private _committed;
|
|
24
|
+
constructor(http: HttpClient);
|
|
25
|
+
/** Number of operations staged so far. */
|
|
26
|
+
get size(): number;
|
|
27
|
+
private _assertNotCommitted;
|
|
28
|
+
/**
|
|
29
|
+
* Stage a full overwrite for the given document reference.
|
|
30
|
+
* Equivalent to `ref.set(data)`.
|
|
31
|
+
*/
|
|
32
|
+
set<T extends ClefbaseDocument>(ref: {
|
|
33
|
+
collectionPath: string;
|
|
34
|
+
id: string;
|
|
35
|
+
}, data: Omit<T, "_id" | "_createdAt" | "_updatedAt">): this;
|
|
36
|
+
/**
|
|
37
|
+
* Stage a merge-patch update for the given document reference.
|
|
38
|
+
* Equivalent to `ref.update(data)`.
|
|
39
|
+
* Supports FieldValue sentinels.
|
|
40
|
+
*/
|
|
41
|
+
update<T extends ClefbaseDocument>(ref: {
|
|
42
|
+
collectionPath: string;
|
|
43
|
+
id: string;
|
|
44
|
+
}, data: Partial<Omit<T, "_id" | "_createdAt" | "_updatedAt">>): this;
|
|
45
|
+
/**
|
|
46
|
+
* Stage a document deletion.
|
|
47
|
+
* Equivalent to `ref.delete()`.
|
|
48
|
+
*/
|
|
49
|
+
delete(ref: {
|
|
50
|
+
collectionPath: string;
|
|
51
|
+
id: string;
|
|
52
|
+
}): this;
|
|
53
|
+
/**
|
|
54
|
+
* Execute all staged operations atomically.
|
|
55
|
+
* After commit() the batch is sealed — calling commit() again throws.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* await batch.commit();
|
|
59
|
+
*/
|
|
60
|
+
commit(): Promise<void>;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=batch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../../src/db/batch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AA6BjD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,UAAU;IAIT,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiB;IACtC,OAAO,CAAC,UAAU,CAAS;gBAEE,IAAI,EAAE,UAAU;IAE7C,0CAA0C;IAC1C,IAAI,IAAI,IAAI,MAAM,CAA6B;IAE/C,OAAO,CAAC,mBAAmB;IAM3B;;;OAGG;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;;;;OAIG;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;;;OAGG;IACH,MAAM,CAAC,GAAG,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAUzD;;;;;;OAMG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ9B"}
|
package/dist/db/batch.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WriteBatch = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Atomic multi-document writer.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* const batch = db.batch();
|
|
9
|
+
*
|
|
10
|
+
* // set — full overwrite (no merge)
|
|
11
|
+
* batch.set(db.collection("users").doc("u1"), { name: "Alice", role: "admin" });
|
|
12
|
+
*
|
|
13
|
+
* // update — merge patch (same as doc.update())
|
|
14
|
+
* batch.update(db.collection("posts").doc("p1"), { views: FieldValue.increment(1) });
|
|
15
|
+
*
|
|
16
|
+
* // delete a document
|
|
17
|
+
* batch.delete(db.collection("sessions").doc("s9"));
|
|
18
|
+
*
|
|
19
|
+
* await batch.commit();
|
|
20
|
+
*/
|
|
21
|
+
class WriteBatch {
|
|
22
|
+
constructor(http) {
|
|
23
|
+
this.http = http;
|
|
24
|
+
this._ops = [];
|
|
25
|
+
this._committed = false;
|
|
26
|
+
}
|
|
27
|
+
/** Number of operations staged so far. */
|
|
28
|
+
get size() { return this._ops.length; }
|
|
29
|
+
_assertNotCommitted() {
|
|
30
|
+
if (this._committed) {
|
|
31
|
+
throw new Error("WriteBatch: cannot add operations after commit()");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Stage a full overwrite for the given document reference.
|
|
36
|
+
* Equivalent to `ref.set(data)`.
|
|
37
|
+
*/
|
|
38
|
+
set(ref, data) {
|
|
39
|
+
this._assertNotCommitted();
|
|
40
|
+
this._ops.push({
|
|
41
|
+
type: "set",
|
|
42
|
+
collectionPath: ref.collectionPath,
|
|
43
|
+
id: ref.id,
|
|
44
|
+
data: data,
|
|
45
|
+
merge: false,
|
|
46
|
+
});
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Stage a merge-patch update for the given document reference.
|
|
51
|
+
* Equivalent to `ref.update(data)`.
|
|
52
|
+
* Supports FieldValue sentinels.
|
|
53
|
+
*/
|
|
54
|
+
update(ref, data) {
|
|
55
|
+
this._assertNotCommitted();
|
|
56
|
+
this._ops.push({
|
|
57
|
+
type: "update",
|
|
58
|
+
collectionPath: ref.collectionPath,
|
|
59
|
+
id: ref.id,
|
|
60
|
+
data: data,
|
|
61
|
+
merge: true,
|
|
62
|
+
});
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Stage a document deletion.
|
|
67
|
+
* Equivalent to `ref.delete()`.
|
|
68
|
+
*/
|
|
69
|
+
delete(ref) {
|
|
70
|
+
this._assertNotCommitted();
|
|
71
|
+
this._ops.push({
|
|
72
|
+
type: "delete",
|
|
73
|
+
collectionPath: ref.collectionPath,
|
|
74
|
+
id: ref.id,
|
|
75
|
+
});
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Execute all staged operations atomically.
|
|
80
|
+
* After commit() the batch is sealed — calling commit() again throws.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* await batch.commit();
|
|
84
|
+
*/
|
|
85
|
+
async commit() {
|
|
86
|
+
this._assertNotCommitted();
|
|
87
|
+
this._committed = true;
|
|
88
|
+
if (this._ops.length === 0)
|
|
89
|
+
return;
|
|
90
|
+
await this.http.post("/batch", { operations: this._ops });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
exports.WriteBatch = WriteBatch;
|
|
94
|
+
//# sourceMappingURL=batch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch.js","sourceRoot":"","sources":["../../src/db/batch.ts"],"names":[],"mappings":";;;AA8BA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAa,UAAU;IAIrB,YAA6B,IAAgB;QAAhB,SAAI,GAAJ,IAAI,CAAY;QAH5B,SAAI,GAAc,EAAE,CAAC;QAC9B,eAAU,GAAG,KAAK,CAAC;IAEqB,CAAC;IAEjD,0CAA0C;IAC1C,IAAI,IAAI,KAAa,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAEvC,mBAAmB;QACzB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,GAAG,CACD,GAA2C,EAC3C,IAAkD;QAElD,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;YACb,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;;;;OAIG;IACH,MAAM,CACJ,GAA2C,EAC3C,IAA2D;QAE3D,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;YACb,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;;;OAGG;IACH,MAAM,CAAC,GAA2C;QAChD,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;YACb,IAAI,EAAY,QAAQ;YACxB,cAAc,EAAE,GAAG,CAAC,cAAc;YAClC,EAAE,EAAc,GAAG,CAAC,EAAE;SACvB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,MAAM;QACV,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QAEvB,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEnC,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC;CACF;AAnFD,gCAmFC"}
|
package/dist/db/index.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { HttpClient } from "../http";
|
|
2
|
+
import { WriteBatch } from "./batch";
|
|
3
|
+
import { Transaction } from "./transaction";
|
|
2
4
|
import type { ClefbaseDocument, QueryResult, WhereClause } from "../types";
|
|
3
5
|
export type { WhereClause, WhereValue } from "../types";
|
|
6
|
+
export { WriteBatch } from "./batch";
|
|
7
|
+
export { Transaction, runTransaction } from "./transaction";
|
|
4
8
|
/**
|
|
5
9
|
* A typed reference to a single document.
|
|
6
10
|
*
|
|
@@ -16,11 +20,14 @@ export declare class DocumentReference<T extends ClefbaseDocument = ClefbaseDocu
|
|
|
16
20
|
readonly collectionPath: string;
|
|
17
21
|
readonly id: string;
|
|
18
22
|
constructor(http: HttpClient, collectionPath: string, id: string);
|
|
23
|
+
/** Fully-qualified path string, e.g. "posts/p1" or "posts/p1/comments/c1". */
|
|
24
|
+
get path(): string;
|
|
19
25
|
/** Fetch this document. Returns null when it does not exist. */
|
|
20
26
|
get(): Promise<T | null>;
|
|
21
27
|
/**
|
|
22
28
|
* Patch this document (merge by default).
|
|
23
29
|
* Pass `{ merge: false }` to fully replace the document.
|
|
30
|
+
* Supports FieldValue sentinels — they are serialized by JSON.stringify.
|
|
24
31
|
*/
|
|
25
32
|
update(data: Partial<Omit<T, "_id" | "_createdAt" | "_updatedAt">>, opts?: {
|
|
26
33
|
merge?: boolean;
|
|
@@ -39,6 +46,20 @@ export declare class DocumentReference<T extends ClefbaseDocument = ClefbaseDocu
|
|
|
39
46
|
collection<S extends ClefbaseDocument = ClefbaseDocument>(name: string): CollectionReference<S>;
|
|
40
47
|
/** @deprecated Use .collection() — kept for backwards compat */
|
|
41
48
|
subcollection<S extends ClefbaseDocument = ClefbaseDocument>(name: string): CollectionReference<S>;
|
|
49
|
+
/**
|
|
50
|
+
* Subscribe to real-time updates for this document.
|
|
51
|
+
* Returns an unsubscribe function.
|
|
52
|
+
* Requires the server to support SSE at GET /<collection>/<id>/stream.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* const unsub = ref.onSnapshot((doc) => {
|
|
56
|
+
* if (doc) console.log("Updated:", doc);
|
|
57
|
+
* else console.log("Deleted");
|
|
58
|
+
* });
|
|
59
|
+
* // later:
|
|
60
|
+
* unsub();
|
|
61
|
+
*/
|
|
62
|
+
onSnapshot(callback: (doc: T | null) => void, onError?: (err: Error) => void): () => void;
|
|
42
63
|
}
|
|
43
64
|
/**
|
|
44
65
|
* A chainable query builder.
|
|
@@ -65,9 +86,10 @@ export declare class Query<T extends ClefbaseDocument = ClefbaseDocument> {
|
|
|
65
86
|
/**
|
|
66
87
|
* Filter by field values or operators.
|
|
67
88
|
* Multiple calls are AND-merged.
|
|
89
|
+
* Supports dot-notation for nested fields: `where({ "address.city": "London" })`
|
|
68
90
|
*/
|
|
69
91
|
where(clauses: WhereClause): this;
|
|
70
|
-
/** Sort by a field. */
|
|
92
|
+
/** Sort by a field. Supports dot-notation: `orderBy("meta.score", "desc")` */
|
|
71
93
|
orderBy(field: string, dir?: "asc" | "desc"): this;
|
|
72
94
|
/** Cap results (server max: 500). */
|
|
73
95
|
limit(n: number): this;
|
|
@@ -82,6 +104,36 @@ export declare class Query<T extends ClefbaseDocument = ClefbaseDocument> {
|
|
|
82
104
|
get(): Promise<T[]>;
|
|
83
105
|
/** @alias get() */
|
|
84
106
|
getDocs(): Promise<T[]>;
|
|
107
|
+
/**
|
|
108
|
+
* Count matching documents without fetching their data.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* const total = await db.collection("orders").where({ status: "pending" }).count();
|
|
112
|
+
*/
|
|
113
|
+
count(): Promise<number>;
|
|
114
|
+
/**
|
|
115
|
+
* Fetch a single matching document (first result).
|
|
116
|
+
* Returns null when no document matches.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* const admin = await db.collection("users").where({ role: "admin" }).findOne();
|
|
120
|
+
*/
|
|
121
|
+
findOne(): Promise<T | null>;
|
|
122
|
+
/**
|
|
123
|
+
* Iterate over all matching documents in pages, calling `callback` for
|
|
124
|
+
* each page. Useful for large datasets you don't want to load at once.
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* await db.collection("logs")
|
|
128
|
+
* .where({ level: "error" })
|
|
129
|
+
* .orderBy("_createdAt")
|
|
130
|
+
* .forEach(async (page) => {
|
|
131
|
+
* for (const doc of page) await process(doc);
|
|
132
|
+
* }, { pageSize: 100 });
|
|
133
|
+
*/
|
|
134
|
+
forEach(callback: (docs: T[]) => Promise<void> | void, opts?: {
|
|
135
|
+
pageSize?: number;
|
|
136
|
+
}): Promise<void>;
|
|
85
137
|
}
|
|
86
138
|
/**
|
|
87
139
|
* A reference to a collection (or subcollection path).
|
|
@@ -107,23 +159,90 @@ export declare class CollectionReference<T extends ClefbaseDocument = ClefbaseDo
|
|
|
107
159
|
offset?: number;
|
|
108
160
|
}): Promise<QueryResult<T>>;
|
|
109
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* Query across ALL collections (and subcollections) that share the same name.
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* // Find all comments across every post
|
|
167
|
+
* const allComments = await db
|
|
168
|
+
* .collectionGroup("comments")
|
|
169
|
+
* .where({ approved: true })
|
|
170
|
+
* .orderBy("_createdAt", "desc")
|
|
171
|
+
* .limit(50)
|
|
172
|
+
* .getDocs();
|
|
173
|
+
*/
|
|
174
|
+
export declare class CollectionGroup<T extends ClefbaseDocument = ClefbaseDocument> extends Query<T> {
|
|
175
|
+
constructor(http: HttpClient, collectionId: string);
|
|
176
|
+
}
|
|
110
177
|
/**
|
|
111
178
|
* Top-level database service. Obtain via `getDatabase(app)`.
|
|
112
179
|
*
|
|
113
180
|
* @example
|
|
114
181
|
* const db = getDatabase(app);
|
|
182
|
+
*
|
|
183
|
+
* // Document reads / writes
|
|
115
184
|
* const post = await db.collection("posts").doc("p1").get();
|
|
116
|
-
*
|
|
185
|
+
* await db.collection("posts").doc("p1").update({ views: FieldValue.increment(1) });
|
|
186
|
+
*
|
|
187
|
+
* // Atomic batch write
|
|
188
|
+
* const batch = db.batch();
|
|
189
|
+
* batch.update(db.collection("counters").doc("hits"), { n: FieldValue.increment(1) });
|
|
190
|
+
* batch.delete(db.collection("sessions").doc("expired-id"));
|
|
191
|
+
* await batch.commit();
|
|
192
|
+
*
|
|
193
|
+
* // Transaction (read-then-write)
|
|
194
|
+
* await db.runTransaction(async (tx) => {
|
|
195
|
+
* const doc = await tx.get(db.collection("wallets").doc(id));
|
|
196
|
+
* tx.update(db.collection("wallets").doc(id), { balance: (doc?.balance as number ?? 0) + 10 });
|
|
197
|
+
* });
|
|
117
198
|
*/
|
|
118
199
|
export declare class Database {
|
|
119
200
|
private readonly http;
|
|
120
201
|
constructor(http: HttpClient);
|
|
121
202
|
collection<T extends ClefbaseDocument = ClefbaseDocument>(name: string): CollectionReference<T>;
|
|
203
|
+
/**
|
|
204
|
+
* Query across ALL collections that share `collectionId`, regardless of
|
|
205
|
+
* where they are nested in the document tree.
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* const allLikes = await db.collectionGroup("likes").where({ userId: uid }).getDocs();
|
|
209
|
+
*/
|
|
210
|
+
collectionGroup<T extends ClefbaseDocument = ClefbaseDocument>(collectionId: string): CollectionGroup<T>;
|
|
122
211
|
getDoc<T extends ClefbaseDocument = ClefbaseDocument>(collectionName: string, id: string): Promise<T | null>;
|
|
123
212
|
addDoc<T extends ClefbaseDocument = ClefbaseDocument>(collectionName: string, data: Omit<T, "_id" | "_createdAt" | "_updatedAt">): Promise<T>;
|
|
124
213
|
updateDoc<T extends ClefbaseDocument = ClefbaseDocument>(collectionName: string, id: string, data: Partial<Omit<T, "_id" | "_createdAt" | "_updatedAt">>, opts?: {
|
|
125
214
|
merge?: boolean;
|
|
126
215
|
}): Promise<T>;
|
|
127
216
|
deleteDoc(collectionName: string, id: string): Promise<void>;
|
|
217
|
+
/**
|
|
218
|
+
* Fetch a document by its full slash-separated path string.
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* const comment = await db.doc("posts/p1/comments/c1");
|
|
222
|
+
*/
|
|
223
|
+
doc<T extends ClefbaseDocument = ClefbaseDocument>(path: string): Promise<T | null>;
|
|
224
|
+
/**
|
|
225
|
+
* Create an atomic WriteBatch. Stage operations with `batch.set()`,
|
|
226
|
+
* `batch.update()`, `batch.delete()`, then call `batch.commit()`.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* const batch = db.batch();
|
|
230
|
+
* batch.set(db.collection("users").doc("u1"), { name: "Alice" });
|
|
231
|
+
* batch.update(db.collection("counters").doc("users"), { total: FieldValue.increment(1) });
|
|
232
|
+
* await batch.commit();
|
|
233
|
+
*/
|
|
234
|
+
batch(): WriteBatch;
|
|
235
|
+
/**
|
|
236
|
+
* Execute a transaction — reads are consistent, writes are atomic.
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* await db.runTransaction(async (tx) => {
|
|
240
|
+
* const wallet = await tx.get(db.collection("wallets").doc(userId));
|
|
241
|
+
* if ((wallet?.credits as number ?? 0) < cost) throw new Error("Insufficient credits");
|
|
242
|
+
* tx.update(db.collection("wallets").doc(userId), { credits: (wallet!.credits as number) - cost });
|
|
243
|
+
* tx.update(db.collection("items").doc(itemId), { ownerId: userId });
|
|
244
|
+
* });
|
|
245
|
+
*/
|
|
246
|
+
runTransaction<T = void>(updateFn: (tx: Transaction) => Promise<T>): Promise<T>;
|
|
128
247
|
}
|
|
129
248
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/db/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EACV,gBAAgB,EAEhB,WAAW,EACX,WAAW,EACZ,MAAM,UAAU,CAAC;AAElB,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"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"}
|
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
|