clefbase 1.0.0

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.
@@ -0,0 +1,271 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Auth = void 0;
4
+ /**
5
+ * Stores the session in memory and optionally in localStorage (browser).
6
+ * On Node.js localStorage is not available so only in-memory is used.
7
+ */
8
+ class SessionStore {
9
+ constructor() {
10
+ this.session = null;
11
+ this.STORAGE_KEY = "cfb_session";
12
+ }
13
+ get ls() {
14
+ try {
15
+ return typeof localStorage !== "undefined" ? localStorage : null;
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ }
21
+ save(s) {
22
+ this.session = s;
23
+ try {
24
+ this.ls?.setItem(this.STORAGE_KEY, JSON.stringify(s));
25
+ }
26
+ catch { /* quota exceeded or private mode */ }
27
+ }
28
+ load() {
29
+ if (this.session)
30
+ return this.session;
31
+ try {
32
+ const raw = this.ls?.getItem(this.STORAGE_KEY);
33
+ if (raw) {
34
+ const parsed = JSON.parse(raw);
35
+ if (new Date(parsed.expiresAt) > new Date()) {
36
+ this.session = parsed;
37
+ return this.session;
38
+ }
39
+ // Expired — clean up
40
+ this.ls?.removeItem(this.STORAGE_KEY);
41
+ }
42
+ }
43
+ catch { /* corrupted data */ }
44
+ return null;
45
+ }
46
+ clear() {
47
+ this.session = null;
48
+ try {
49
+ this.ls?.removeItem(this.STORAGE_KEY);
50
+ }
51
+ catch { /* ignore */ }
52
+ }
53
+ }
54
+ /**
55
+ * Authentication service. Obtain via `getAuth(app)`.
56
+ *
57
+ * @example
58
+ * import { initClefbase, getAuth } from "clefbase";
59
+ * const app = initClefbase({ serverUrl, projectId, apiKey });
60
+ * const auth = getAuth(app);
61
+ *
62
+ * const { user } = await auth.signUp("alice@example.com", "secret123");
63
+ * const { user } = await auth.signIn("alice@example.com", "secret123");
64
+ * console.log(auth.currentUser);
65
+ * await auth.signOut();
66
+ *
67
+ * const unsub = auth.onAuthStateChanged((u) => console.log(u));
68
+ * unsub(); // stop listening
69
+ */
70
+ class Auth {
71
+ constructor(http) {
72
+ this.http = http;
73
+ this.store = new SessionStore();
74
+ this.listeners = [];
75
+ // Attempt to restore a persisted session on startup
76
+ this.store.load();
77
+ }
78
+ // ─── Internal ──────────────────────────────────────────────────────────────
79
+ notify(user) {
80
+ for (const cb of this.listeners) {
81
+ try {
82
+ cb(user);
83
+ }
84
+ catch { /* don't let a listener crash the SDK */ }
85
+ }
86
+ }
87
+ handleResult(result) {
88
+ this.store.save({
89
+ token: result.token,
90
+ expiresAt: result.session.expiresAt,
91
+ user: result.user,
92
+ });
93
+ this.notify(result.user);
94
+ return result;
95
+ }
96
+ /** @internal Used by Storage to attach the auth token to upload requests. */
97
+ getAuthHeaders() {
98
+ const session = this.store.load();
99
+ return session ? { Authorization: `Bearer ${session.token}` } : {};
100
+ }
101
+ // ─── Public API ─────────────────────────────────────────────────────────────
102
+ /** The currently signed-in user, or null if no session exists. */
103
+ get currentUser() {
104
+ return this.store.load()?.user ?? null;
105
+ }
106
+ /** The raw bearer token for the active session, or null. */
107
+ get currentToken() {
108
+ return this.store.load()?.token ?? null;
109
+ }
110
+ /**
111
+ * Create a new account with email + password.
112
+ *
113
+ * @example
114
+ * const { user } = await auth.signUp("alice@example.com", "pass123", {
115
+ * displayName: "Alice",
116
+ * metadata: { role: "admin" },
117
+ * });
118
+ */
119
+ async signUp(email, password, profile) {
120
+ const result = await this.http.post("/auth/signup", {
121
+ email,
122
+ password,
123
+ ...profile,
124
+ });
125
+ return this.handleResult(result);
126
+ }
127
+ /**
128
+ * Sign in with email + password.
129
+ *
130
+ * @example
131
+ * const { user, token } = await auth.signIn("alice@example.com", "pass123");
132
+ */
133
+ async signIn(email, password) {
134
+ const result = await this.http.post("/auth/signin", {
135
+ email,
136
+ password,
137
+ });
138
+ return this.handleResult(result);
139
+ }
140
+ /**
141
+ * Sign out the current user and clear the local session.
142
+ */
143
+ async signOut() {
144
+ const token = this.currentToken;
145
+ if (token) {
146
+ try {
147
+ await this.http.post("/auth/signout", undefined, {
148
+ Authorization: `Bearer ${token}`,
149
+ });
150
+ }
151
+ catch {
152
+ // Always clear locally even if the server call fails
153
+ }
154
+ }
155
+ this.store.clear();
156
+ this.notify(null);
157
+ }
158
+ /**
159
+ * Re-fetch the current user's profile from the server and update the cache.
160
+ * Useful after an admin changes the user's data server-side.
161
+ */
162
+ async refreshCurrentUser() {
163
+ const token = this.currentToken;
164
+ if (!token)
165
+ return null;
166
+ try {
167
+ const user = await this.http.get("/auth/me", {
168
+ Authorization: `Bearer ${token}`,
169
+ });
170
+ const session = this.store.load();
171
+ if (session)
172
+ this.store.save({ ...session, user });
173
+ this.notify(user);
174
+ return user;
175
+ }
176
+ catch {
177
+ return null;
178
+ }
179
+ }
180
+ /**
181
+ * Update the signed-in user's profile.
182
+ *
183
+ * @example
184
+ * await auth.updateProfile({ displayName: "Bob", metadata: { theme: "dark" } });
185
+ */
186
+ async updateProfile(updates) {
187
+ const token = this.currentToken;
188
+ if (!token)
189
+ throw new Error("No user is currently signed in.");
190
+ const user = await this.http.patch("/auth/me", updates, {
191
+ Authorization: `Bearer ${token}`,
192
+ });
193
+ const session = this.store.load();
194
+ if (session)
195
+ this.store.save({ ...session, user });
196
+ this.notify(user);
197
+ return user;
198
+ }
199
+ /**
200
+ * Change the signed-in user's password.
201
+ * All other active sessions are invalidated on the server.
202
+ */
203
+ async changePassword(currentPassword, newPassword) {
204
+ const token = this.currentToken;
205
+ if (!token)
206
+ throw new Error("No user is currently signed in.");
207
+ await this.http.post("/auth/change-password", { currentPassword, newPassword }, { Authorization: `Bearer ${token}` });
208
+ }
209
+ /**
210
+ * Request a password-reset email. Safe to call even if the address
211
+ * is not registered (server returns the same response either way).
212
+ */
213
+ async sendPasswordResetEmail(email) {
214
+ await this.http.post("/auth/forgot-password", { email });
215
+ }
216
+ /**
217
+ * Complete a password reset with the token from the reset email.
218
+ */
219
+ async confirmPasswordReset(resetToken, newPassword) {
220
+ await this.http.post("/auth/reset-password", {
221
+ token: resetToken,
222
+ newPassword,
223
+ });
224
+ }
225
+ /**
226
+ * Send an email verification code to the currently signed-in user.
227
+ */
228
+ async sendEmailVerification() {
229
+ const token = this.currentToken;
230
+ if (!token)
231
+ throw new Error("No user is currently signed in.");
232
+ await this.http.post("/auth/send-verification", undefined, {
233
+ Authorization: `Bearer ${token}`,
234
+ });
235
+ }
236
+ /**
237
+ * Verify the current user's email with the code they received.
238
+ *
239
+ * @example
240
+ * const user = await auth.verifyEmail("ABC123");
241
+ */
242
+ async verifyEmail(code) {
243
+ return this.http.post("/auth/verify-email", { code });
244
+ }
245
+ /**
246
+ * Subscribe to auth state changes.
247
+ * The callback fires immediately with the current user, then on every change.
248
+ * Returns an unsubscribe function.
249
+ *
250
+ * @example
251
+ * const unsubscribe = auth.onAuthStateChanged((user) => {
252
+ * if (user) console.log("signed in:", user.email);
253
+ * else console.log("signed out");
254
+ * });
255
+ * // Later:
256
+ * unsubscribe();
257
+ */
258
+ onAuthStateChanged(callback) {
259
+ this.listeners.push(callback);
260
+ // Emit the current state immediately
261
+ try {
262
+ callback(this.currentUser);
263
+ }
264
+ catch { /* don't crash on listener error */ }
265
+ return () => {
266
+ this.listeners = this.listeners.filter((cb) => cb !== callback);
267
+ };
268
+ }
269
+ }
270
+ exports.Auth = Auth;
271
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":";;;AAWA;;;GAGG;AACH,MAAM,YAAY;IAAlB;QACU,YAAO,GAA4B,IAAI,CAAC;QAC/B,gBAAW,GAAG,aAAa,CAAC;IAwC/C,CAAC;IAtCC,IAAY,EAAE;QACZ,IAAI,CAAC;YACH,OAAO,OAAO,YAAY,KAAK,WAAW,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC;QACnE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,IAAI,CAAC,CAAmB;QACtB,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;QACjB,IAAI,CAAC;YACH,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;QACxD,CAAC;QAAC,MAAM,CAAC,CAAC,oCAAoC,CAAC,CAAC;IAClD,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC;QACtC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC/C,IAAI,GAAG,EAAE,CAAC;gBACR,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAqB,CAAC;gBACnD,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC;oBAC5C,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;oBACtB,OAAO,IAAI,CAAC,OAAO,CAAC;gBACtB,CAAC;gBACD,qBAAqB;gBACrB,IAAI,CAAC,EAAE,EAAE,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAC,oBAAoB,CAAC,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC;YACH,IAAI,CAAC,EAAE,EAAE,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IAC1B,CAAC;CACF;AAMD;;;;;;;;;;;;;;;GAeG;AACH,MAAa,IAAI;IAIf,YAA6B,IAAgB;QAAhB,SAAI,GAAJ,IAAI,CAAY;QAH5B,UAAK,GAAG,IAAI,YAAY,EAAE,CAAC;QACpC,cAAS,GAAwB,EAAE,CAAC;QAG1C,oDAAoD;QACpD,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IACpB,CAAC;IAED,8EAA8E;IAEtE,MAAM,CAAC,IAAqB;QAClC,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAChC,IAAI,CAAC;gBAAC,EAAE,CAAC,IAAI,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,wCAAwC,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IAEO,YAAY,CAAC,MAAkB;QACrC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YACd,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,SAAS;YACnC,IAAI,EAAE,MAAM,CAAC,IAAI;SAClB,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACzB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,6EAA6E;IAC7E,cAAc;QACZ,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAClC,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,UAAU,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACrE,CAAC;IAED,+EAA+E;IAE/E,kEAAkE;IAClE,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,IAAI,IAAI,IAAI,CAAC;IACzC,CAAC;IAED,4DAA4D;IAC5D,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,KAAK,IAAI,IAAI,CAAC;IAC1C,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,MAAM,CACV,KAAa,EACb,QAAgB,EAChB,OAIC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAa,cAAc,EAAE;YAC9D,KAAK;YACL,QAAQ;YACR,GAAG,OAAO;SACX,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,QAAgB;QAC1C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAa,cAAc,EAAE;YAC9D,KAAK;YACL,QAAQ;SACT,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO;QACX,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC;QAChC,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,SAAS,EAAE;oBAC/C,aAAa,EAAE,UAAU,KAAK,EAAE;iBACjC,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACP,qDAAqD;YACvD,CAAC;QACH,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACnB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,kBAAkB;QACtB,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC;QAChC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAW,UAAU,EAAE;gBACrD,aAAa,EAAE,UAAU,KAAK,EAAE;aACjC,CAAC,CAAC;YACH,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;YAClC,IAAI,OAAO;gBAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACnD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAClB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,aAAa,CAAC,OAInB;QACC,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC;QAChC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QAC/D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAW,UAAU,EAAE,OAAO,EAAE;YAChE,aAAa,EAAE,UAAU,KAAK,EAAE;SACjC,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAClC,IAAI,OAAO;YAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACnD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,cAAc,CAClB,eAAuB,EACvB,WAAmB;QAEnB,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC;QAChC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QAC/D,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAClB,uBAAuB,EACvB,EAAE,eAAe,EAAE,WAAW,EAAE,EAChC,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE,CACrC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,sBAAsB,CAAC,KAAa;QACxC,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,oBAAoB,CACxB,UAAkB,EAClB,WAAmB;QAEnB,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAAE;YAC3C,KAAK,EAAE,UAAU;YACjB,WAAW;SACZ,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,qBAAqB;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC;QAChC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QAC/D,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,yBAAyB,EAAE,SAAS,EAAE;YACzD,aAAa,EAAE,UAAU,KAAK,EAAE;SACjC,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,WAAW,CAAC,IAAY;QAC5B,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAW,oBAAoB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAClE,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,kBAAkB,CAAC,QAA2B;QAC5C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC9B,qCAAqC;QACrC,IAAI,CAAC;YAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,mCAAmC,CAAC,CAAC;QACjF,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC;QAClE,CAAC,CAAC;IACJ,CAAC;CACF;AAjOD,oBAiOC"}
@@ -0,0 +1,165 @@
1
+ import { HttpClient } from "../http";
2
+ import type { ClefbaseDocument, QueryResult, WhereClause } from "../types";
3
+ export type { WhereClause, WhereValue } from "../types";
4
+ /**
5
+ * A typed reference to a single document.
6
+ *
7
+ * @example
8
+ * const ref = db.collection("users").doc("abc123");
9
+ * const user = await ref.get(); // T | null
10
+ * await ref.update({ name: "Alice" }); // merge patch
11
+ * await ref.set({ name: "Alice", age: 30 }); // full overwrite
12
+ * await ref.delete();
13
+ *
14
+ * // Subcollection
15
+ * const comments = ref.subcollection("comments");
16
+ */
17
+ export declare class DocumentReference<T extends ClefbaseDocument = ClefbaseDocument> {
18
+ private readonly http;
19
+ /** Full collection path, e.g. "users" or "users/uid/posts" */
20
+ readonly collectionPath: string;
21
+ readonly id: string;
22
+ constructor(http: HttpClient,
23
+ /** Full collection path, e.g. "users" or "users/uid/posts" */
24
+ collectionPath: string, id: string);
25
+ /** Fetch this document. Returns null when it does not exist. */
26
+ get(): Promise<T | null>;
27
+ /**
28
+ * Patch this document.
29
+ * By default fields are merged; pass `{ merge: false }` to fully replace.
30
+ *
31
+ * @example
32
+ * await ref.update({ score: 42 });
33
+ * await ref.update({ score: 42 }, { merge: false }); // replaces whole doc
34
+ */
35
+ update(data: Partial<Omit<T, "_id" | "_createdAt" | "_updatedAt">>, opts?: {
36
+ merge?: boolean;
37
+ }): Promise<T>;
38
+ /**
39
+ * Overwrite this document entirely (no merge).
40
+ *
41
+ * @example
42
+ * await ref.set({ name: "Bob", age: 25 });
43
+ */
44
+ set(data: Omit<T, "_id" | "_createdAt" | "_updatedAt">): Promise<T>;
45
+ /** Delete this document (and all its subcollections on the server). */
46
+ delete(): Promise<void>;
47
+ /**
48
+ * Access a subcollection nested under this document.
49
+ *
50
+ * @example
51
+ * const comments = db.collection("posts").doc("p1").subcollection("comments");
52
+ * await comments.add({ text: "Great!" });
53
+ */
54
+ subcollection<S extends ClefbaseDocument = ClefbaseDocument>(name: string): CollectionReference<S>;
55
+ }
56
+ /**
57
+ * A chainable query builder.
58
+ *
59
+ * @example
60
+ * const docs = await db.collection("orders")
61
+ * .where({ status: "pending", total: { $gt: 50 } })
62
+ * .orderBy("_createdAt", "desc")
63
+ * .limit(20)
64
+ * .offset(0)
65
+ * .getDocs();
66
+ */
67
+ export declare class Query<T extends ClefbaseDocument = ClefbaseDocument> {
68
+ protected readonly http: HttpClient;
69
+ protected readonly path: string;
70
+ protected _filter: Record<string, unknown>;
71
+ protected _sort?: {
72
+ field: string;
73
+ dir: "asc" | "desc";
74
+ };
75
+ protected _limit: number;
76
+ protected _offset: number;
77
+ constructor(http: HttpClient, path: string);
78
+ /**
79
+ * Filter documents by field values or operators.
80
+ * Multiple calls are merged together (AND logic).
81
+ *
82
+ * @example
83
+ * .where({ published: true })
84
+ * .where({ views: { $gte: 100 } })
85
+ */
86
+ where(clauses: WhereClause): this;
87
+ /** Sort by a field. Default direction is ascending. */
88
+ orderBy(field: string, dir?: "asc" | "desc"): this;
89
+ /** Cap results at n (server maximum: 500). */
90
+ limit(n: number): this;
91
+ /** Skip the first n results (for pagination). */
92
+ offset(n: number): this;
93
+ /** Execute and return the full paginated result object. */
94
+ query(): Promise<QueryResult<T>>;
95
+ /** Execute and return just the document array. */
96
+ getDocs(): Promise<T[]>;
97
+ }
98
+ /**
99
+ * A reference to a collection (or subcollection path).
100
+ *
101
+ * @example
102
+ * const col = db.collection("users");
103
+ *
104
+ * // Add
105
+ * const user = await col.add({ name: "Alice", age: 30 });
106
+ *
107
+ * // List (no filter)
108
+ * const { data, total } = await col.list({ limit: 20 });
109
+ *
110
+ * // Query (with filter / sort)
111
+ * const adults = await col.where({ age: { $gte: 18 } }).orderBy("name").getDocs();
112
+ */
113
+ export declare class CollectionReference<T extends ClefbaseDocument = ClefbaseDocument> extends Query<T> {
114
+ constructor(http: HttpClient, path: string);
115
+ /** Return a DocumentReference for a given ID without fetching. */
116
+ doc(id: string): DocumentReference<T>;
117
+ /** Add a new document with a server-generated ID. */
118
+ add(data: Omit<T, "_id" | "_createdAt" | "_updatedAt">): Promise<T>;
119
+ /**
120
+ * Fetch documents without any query filter — useful for simple pagination.
121
+ * For filtering/sorting use the inherited `.where()` / `.orderBy()` chain.
122
+ *
123
+ * @example
124
+ * const { data, total } = await col.list({ limit: 10, offset: 20 });
125
+ */
126
+ list(opts?: {
127
+ limit?: number;
128
+ offset?: number;
129
+ }): Promise<QueryResult<T>>;
130
+ }
131
+ /**
132
+ * Top-level database service.
133
+ * Obtain via `getDatabase(app)`.
134
+ *
135
+ * @example
136
+ * import { initClefbase, getDatabase } from "clefbase";
137
+ * const app = initClefbase({ serverUrl, projectId, apiKey });
138
+ * const db = getDatabase(app);
139
+ *
140
+ * // Reference-based API
141
+ * const post = await db.collection("posts").doc("p1").get();
142
+ *
143
+ * // Convenience helpers
144
+ * const user = await db.getDoc("users", "uid-123");
145
+ * const newDoc = await db.addDoc("logs", { action: "login" });
146
+ * await db.updateDoc("users", "uid-123", { lastSeen: new Date().toISOString() });
147
+ * await db.deleteDoc("users", "uid-123");
148
+ */
149
+ export declare class Database {
150
+ private readonly http;
151
+ constructor(http: HttpClient);
152
+ /** Return a CollectionReference for a top-level collection. */
153
+ collection<T extends ClefbaseDocument = ClefbaseDocument>(name: string): CollectionReference<T>;
154
+ /** Fetch a single document by collection name and ID. Returns null if not found. */
155
+ getDoc<T extends ClefbaseDocument = ClefbaseDocument>(collectionName: string, id: string): Promise<T | null>;
156
+ /** Add a document to a collection and return it with server-set fields. */
157
+ addDoc<T extends ClefbaseDocument = ClefbaseDocument>(collectionName: string, data: Omit<T, "_id" | "_createdAt" | "_updatedAt">): Promise<T>;
158
+ /** Patch a document. Merges by default; pass `{ merge: false }` to overwrite. */
159
+ updateDoc<T extends ClefbaseDocument = ClefbaseDocument>(collectionName: string, id: string, data: Partial<Omit<T, "_id" | "_createdAt" | "_updatedAt">>, opts?: {
160
+ merge?: boolean;
161
+ }): Promise<T>;
162
+ /** Delete a document. */
163
+ deleteDoc(collectionName: string, id: string): Promise<void>;
164
+ }
165
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,230 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Database = exports.CollectionReference = exports.Query = exports.DocumentReference = void 0;
4
+ // ─── DocumentReference ────────────────────────────────────────────────────────
5
+ /**
6
+ * A typed reference to a single document.
7
+ *
8
+ * @example
9
+ * const ref = db.collection("users").doc("abc123");
10
+ * const user = await ref.get(); // T | null
11
+ * await ref.update({ name: "Alice" }); // merge patch
12
+ * await ref.set({ name: "Alice", age: 30 }); // full overwrite
13
+ * await ref.delete();
14
+ *
15
+ * // Subcollection
16
+ * const comments = ref.subcollection("comments");
17
+ */
18
+ class DocumentReference {
19
+ constructor(http,
20
+ /** Full collection path, e.g. "users" or "users/uid/posts" */
21
+ collectionPath, id) {
22
+ this.http = http;
23
+ this.collectionPath = collectionPath;
24
+ this.id = id;
25
+ }
26
+ /** Fetch this document. Returns null when it does not exist. */
27
+ async get() {
28
+ try {
29
+ return await this.http.get(`/${this.collectionPath}/${this.id}`);
30
+ }
31
+ catch (err) {
32
+ const status = err.status;
33
+ if (status === 404)
34
+ return null;
35
+ throw err;
36
+ }
37
+ }
38
+ /**
39
+ * Patch this document.
40
+ * By default fields are merged; pass `{ merge: false }` to fully replace.
41
+ *
42
+ * @example
43
+ * await ref.update({ score: 42 });
44
+ * await ref.update({ score: 42 }, { merge: false }); // replaces whole doc
45
+ */
46
+ async update(data, opts) {
47
+ const merge = opts?.merge ?? true;
48
+ return this.http.put(`/${this.collectionPath}/${this.id}?merge=${String(merge)}`, data);
49
+ }
50
+ /**
51
+ * Overwrite this document entirely (no merge).
52
+ *
53
+ * @example
54
+ * await ref.set({ name: "Bob", age: 25 });
55
+ */
56
+ async set(data) {
57
+ return this.http.put(`/${this.collectionPath}/${this.id}?merge=false`, data);
58
+ }
59
+ /** Delete this document (and all its subcollections on the server). */
60
+ async delete() {
61
+ await this.http.delete(`/${this.collectionPath}/${this.id}`);
62
+ }
63
+ /**
64
+ * Access a subcollection nested under this document.
65
+ *
66
+ * @example
67
+ * const comments = db.collection("posts").doc("p1").subcollection("comments");
68
+ * await comments.add({ text: "Great!" });
69
+ */
70
+ subcollection(name) {
71
+ return new CollectionReference(this.http, `${this.collectionPath}/${this.id}/${name}`);
72
+ }
73
+ }
74
+ exports.DocumentReference = DocumentReference;
75
+ // ─── Query ────────────────────────────────────────────────────────────────────
76
+ /**
77
+ * A chainable query builder.
78
+ *
79
+ * @example
80
+ * const docs = await db.collection("orders")
81
+ * .where({ status: "pending", total: { $gt: 50 } })
82
+ * .orderBy("_createdAt", "desc")
83
+ * .limit(20)
84
+ * .offset(0)
85
+ * .getDocs();
86
+ */
87
+ class Query {
88
+ constructor(http, path) {
89
+ this.http = http;
90
+ this.path = path;
91
+ this._filter = {};
92
+ this._limit = 50;
93
+ this._offset = 0;
94
+ }
95
+ /**
96
+ * Filter documents by field values or operators.
97
+ * Multiple calls are merged together (AND logic).
98
+ *
99
+ * @example
100
+ * .where({ published: true })
101
+ * .where({ views: { $gte: 100 } })
102
+ */
103
+ where(clauses) {
104
+ this._filter = { ...this._filter, ...clauses };
105
+ return this;
106
+ }
107
+ /** Sort by a field. Default direction is ascending. */
108
+ orderBy(field, dir = "asc") {
109
+ this._sort = { field, dir };
110
+ return this;
111
+ }
112
+ /** Cap results at n (server maximum: 500). */
113
+ limit(n) {
114
+ this._limit = Math.min(n, 500);
115
+ return this;
116
+ }
117
+ /** Skip the first n results (for pagination). */
118
+ offset(n) {
119
+ this._offset = n;
120
+ return this;
121
+ }
122
+ /** Execute and return the full paginated result object. */
123
+ async query() {
124
+ const body = {
125
+ filter: Object.keys(this._filter).length > 0 ? this._filter : undefined,
126
+ sort: this._sort,
127
+ limit: this._limit,
128
+ offset: this._offset,
129
+ };
130
+ return this.http.post(`/${this.path}/query`, body);
131
+ }
132
+ /** Execute and return just the document array. */
133
+ async getDocs() {
134
+ const result = await this.query();
135
+ return result.data;
136
+ }
137
+ }
138
+ exports.Query = Query;
139
+ // ─── CollectionReference ──────────────────────────────────────────────────────
140
+ /**
141
+ * A reference to a collection (or subcollection path).
142
+ *
143
+ * @example
144
+ * const col = db.collection("users");
145
+ *
146
+ * // Add
147
+ * const user = await col.add({ name: "Alice", age: 30 });
148
+ *
149
+ * // List (no filter)
150
+ * const { data, total } = await col.list({ limit: 20 });
151
+ *
152
+ * // Query (with filter / sort)
153
+ * const adults = await col.where({ age: { $gte: 18 } }).orderBy("name").getDocs();
154
+ */
155
+ class CollectionReference extends Query {
156
+ constructor(http, path) {
157
+ super(http, path);
158
+ }
159
+ /** Return a DocumentReference for a given ID without fetching. */
160
+ doc(id) {
161
+ return new DocumentReference(this.http, this.path, id);
162
+ }
163
+ /** Add a new document with a server-generated ID. */
164
+ async add(data) {
165
+ return this.http.post(`/${this.path}`, data);
166
+ }
167
+ /**
168
+ * Fetch documents without any query filter — useful for simple pagination.
169
+ * For filtering/sorting use the inherited `.where()` / `.orderBy()` chain.
170
+ *
171
+ * @example
172
+ * const { data, total } = await col.list({ limit: 10, offset: 20 });
173
+ */
174
+ async list(opts) {
175
+ const limit = Math.min(opts?.limit ?? 50, 500);
176
+ const offset = opts?.offset ?? 0;
177
+ const qs = new URLSearchParams({
178
+ limit: String(limit),
179
+ offset: String(offset),
180
+ });
181
+ return this.http.get(`/${this.path}?${qs}`);
182
+ }
183
+ }
184
+ exports.CollectionReference = CollectionReference;
185
+ // ─── Database ─────────────────────────────────────────────────────────────────
186
+ /**
187
+ * Top-level database service.
188
+ * Obtain via `getDatabase(app)`.
189
+ *
190
+ * @example
191
+ * import { initClefbase, getDatabase } from "clefbase";
192
+ * const app = initClefbase({ serverUrl, projectId, apiKey });
193
+ * const db = getDatabase(app);
194
+ *
195
+ * // Reference-based API
196
+ * const post = await db.collection("posts").doc("p1").get();
197
+ *
198
+ * // Convenience helpers
199
+ * const user = await db.getDoc("users", "uid-123");
200
+ * const newDoc = await db.addDoc("logs", { action: "login" });
201
+ * await db.updateDoc("users", "uid-123", { lastSeen: new Date().toISOString() });
202
+ * await db.deleteDoc("users", "uid-123");
203
+ */
204
+ class Database {
205
+ constructor(http) {
206
+ this.http = http;
207
+ }
208
+ /** Return a CollectionReference for a top-level collection. */
209
+ collection(name) {
210
+ return new CollectionReference(this.http, name);
211
+ }
212
+ /** Fetch a single document by collection name and ID. Returns null if not found. */
213
+ async getDoc(collectionName, id) {
214
+ return this.collection(collectionName).doc(id).get();
215
+ }
216
+ /** Add a document to a collection and return it with server-set fields. */
217
+ async addDoc(collectionName, data) {
218
+ return this.collection(collectionName).add(data);
219
+ }
220
+ /** Patch a document. Merges by default; pass `{ merge: false }` to overwrite. */
221
+ async updateDoc(collectionName, id, data, opts) {
222
+ return this.collection(collectionName).doc(id).update(data, opts);
223
+ }
224
+ /** Delete a document. */
225
+ async deleteDoc(collectionName, id) {
226
+ return this.collection(collectionName).doc(id).delete();
227
+ }
228
+ }
229
+ exports.Database = Database;
230
+ //# sourceMappingURL=index.js.map