@estra/client 0.0.0-stage → 0.1.1

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,221 @@
1
+ import { EstraClientError } from "../transport.js";
2
+ import { getApp } from "./app.js";
3
+ /** Firebase's error shape: `name: "FirebaseError"`, `code: "auth/…"`. */
4
+ export class FirebaseError extends Error {
5
+ code;
6
+ customData;
7
+ constructor(code, message) {
8
+ super(message);
9
+ this.name = "FirebaseError";
10
+ this.code = code;
11
+ }
12
+ }
13
+ const authError = (e) => {
14
+ if (e instanceof FirebaseError)
15
+ throw e;
16
+ if (e instanceof EstraClientError)
17
+ throw new FirebaseError(e.code.startsWith("auth/") ? e.code : `auth/${e.code === "unavailable" ? "network-request-failed" : e.code}`, e.message);
18
+ // fetch rejects with a TypeError when the network or CORS blocks the request.
19
+ if (e instanceof TypeError)
20
+ throw new FirebaseError("auth/network-request-failed", e.message);
21
+ throw e;
22
+ };
23
+ const guard = async (p) => {
24
+ try {
25
+ return await p();
26
+ }
27
+ catch (e) {
28
+ return authError(e);
29
+ }
30
+ };
31
+ export class User {
32
+ providerId = "firebase";
33
+ phoneNumber = null;
34
+ tenantId = null;
35
+ #auth;
36
+ #u;
37
+ constructor(auth, u) {
38
+ this.#auth = auth;
39
+ this.#u = u;
40
+ owners.set(this, auth);
41
+ }
42
+ get uid() { return this.#u.uid; }
43
+ get email() { return this.#u.email; }
44
+ get emailVerified() { return this.#u.emailVerified; }
45
+ get displayName() { return this.#u.displayName; }
46
+ get photoURL() { return this.#u.photoUrl; }
47
+ get isAnonymous() { return this.#u.anonymous; }
48
+ get providerData() {
49
+ return this.#u.providers.map((providerId) => ({ uid: this.#u.uid, providerId, email: this.#u.email, displayName: this.#u.displayName, photoURL: this.#u.photoUrl, phoneNumber: null }));
50
+ }
51
+ get metadata() {
52
+ return { creationTime: new Date(this.#u.createdAt).toUTCString(), lastSignInTime: this.#u.lastSignInAt ? new Date(this.#u.lastSignInAt).toUTCString() : undefined };
53
+ }
54
+ /** @internal */ _update(u) { this.#u = u; }
55
+ async getIdToken(forceRefresh = false) {
56
+ const t = await guard(() => this.#auth._estra.getIdToken(forceRefresh));
57
+ if (!t)
58
+ throw new FirebaseError("auth/user-token-expired", "the user is signed out");
59
+ return t;
60
+ }
61
+ async getIdTokenResult(forceRefresh = false) {
62
+ const token = await this.getIdToken(forceRefresh);
63
+ const claims = JSON.parse(atob(token.split(".")[1].replace(/-/g, "+").replace(/_/g, "/")));
64
+ return { token, claims, authTime: new Date(Number(claims.auth_time ?? claims.iat) * 1000).toUTCString(), issuedAtTime: new Date(Number(claims.iat) * 1000).toUTCString(), expirationTime: new Date(Number(claims.exp) * 1000).toUTCString(), signInProvider: null, signInSecondFactor: null };
65
+ }
66
+ async reload() {
67
+ await this.getIdToken(true);
68
+ }
69
+ async delete() {
70
+ await guard(() => this.#auth._estra.deleteUser());
71
+ }
72
+ toJSON() {
73
+ return { uid: this.uid, email: this.email, emailVerified: this.emailVerified, displayName: this.displayName, photoURL: this.photoURL, isAnonymous: this.isAnonymous };
74
+ }
75
+ }
76
+ export class Auth {
77
+ app;
78
+ name;
79
+ _estra;
80
+ languageCode = null;
81
+ #user = null;
82
+ constructor(app) {
83
+ this.app = app;
84
+ this.name = app.name;
85
+ this._estra = app._estra.auth;
86
+ // Keep one User object per signed-in uid, as Firebase does, and tell live listeners.
87
+ this._estra.onAuthStateChanged((u) => {
88
+ this._wrap(u);
89
+ app._estra.transport.refreshAuth?.();
90
+ });
91
+ }
92
+ get currentUser() {
93
+ return this._wrap(this._estra.currentUser);
94
+ }
95
+ /** @internal */ _wrap(u) {
96
+ if (!u)
97
+ return (this.#user = null);
98
+ if (this.#user?.uid === u.uid)
99
+ this.#user._update(u);
100
+ else
101
+ this.#user = new User(this, u);
102
+ return this.#user;
103
+ }
104
+ authStateReady() {
105
+ return this._estra.ready();
106
+ }
107
+ onAuthStateChanged(next) {
108
+ return onAuthStateChanged(this, next);
109
+ }
110
+ signOut() {
111
+ return signOut(this);
112
+ }
113
+ }
114
+ const instances = new WeakMap();
115
+ export function getAuth(app = getApp()) {
116
+ let a = instances.get(app);
117
+ if (!a)
118
+ instances.set(app, (a = new Auth(app)));
119
+ return a;
120
+ }
121
+ export const initializeAuth = (app, _deps) => getAuth(app);
122
+ // Persistence options are accepted for compatibility; the session lives in localStorage.
123
+ export const browserLocalPersistence = { type: "LOCAL" };
124
+ export const browserSessionPersistence = { type: "SESSION" };
125
+ export const inMemoryPersistence = { type: "NONE" };
126
+ export const indexedDBLocalPersistence = { type: "LOCAL" };
127
+ export const setPersistence = async (_auth, _p) => { };
128
+ export function onAuthStateChanged(auth, next, _error) {
129
+ const cb = typeof next === "function" ? next : (u) => next.next?.(u);
130
+ return auth._estra.onAuthStateChanged((u) => cb(auth._wrap(u)));
131
+ }
132
+ export const onIdTokenChanged = onAuthStateChanged;
133
+ export const beforeAuthStateChanged = (_auth, _cb) => () => { };
134
+ const isNew = (u) => u.lastSignInAt === null || u.lastSignInAt - u.createdAt < 10_000;
135
+ const credential = (auth, u, providerId, isNewUser, operationType = "signIn") => ({ user: auth._wrap(u), providerId, operationType, _isNewUser: isNewUser });
136
+ export const signInWithEmailAndPassword = (auth, email, password) => guard(async () => credential(auth, await auth._estra.signIn(email, password), null, false));
137
+ export const createUserWithEmailAndPassword = (auth, email, password) => guard(async () => credential(auth, await auth._estra.signUp(email, password), null, true));
138
+ export const signInAnonymously = (auth) => guard(async () => credential(auth, await auth._estra.signInAnonymously(), null, true));
139
+ export const signOut = (auth) => guard(() => auth._estra.signOut());
140
+ export const sendPasswordResetEmail = (auth, email, _settings) => guard(async () => void (await auth._estra.sendPasswordReset(email)));
141
+ export const confirmPasswordReset = (auth, oobCode, newPassword) => guard(async () => void (await auth._estra.confirmPasswordReset(oobCode, newPassword)));
142
+ export const sendSignInLinkToEmail = (auth, email, _settings) => guard(async () => void (await auth._estra.sendSignInLink(email)));
143
+ export const signInWithEmailLink = (auth, _email, link) => guard(async () => credential(auth, await auth._estra.signInWithLink(new URL(link).searchParams.get("oobCode") ?? ""), "emailLink", false));
144
+ export const isSignInWithEmailLink = (_auth, link) => {
145
+ try {
146
+ return new URL(link).searchParams.get("mode") === "signIn";
147
+ }
148
+ catch {
149
+ return false;
150
+ }
151
+ };
152
+ export const sendEmailVerification = (user, _settings) => guard(async () => void (await authOf(user)._estra.sendEmailVerification()));
153
+ export const applyActionCode = (auth, oobCode) => guard(() => auth._estra.applyEmailVerification(oobCode));
154
+ const owners = new WeakMap();
155
+ function authOf(user) {
156
+ return owners.get(user) ?? getAuth();
157
+ }
158
+ export const updateProfile = (user, p) => guard(async () => {
159
+ const u = await authOf(user)._estra.updateProfile({ ...(p.displayName !== undefined ? { displayName: p.displayName } : {}), ...(p.photoURL !== undefined ? { photoUrl: p.photoURL } : {}) });
160
+ user._update(u);
161
+ });
162
+ export const updatePassword = (user, newPassword) => guard(async () => void (await authOf(user)._estra.updateProfile({ password: newPassword })));
163
+ export const deleteUser = (user) => user.delete();
164
+ class ProviderBase {
165
+ providerId;
166
+ constructor(id) {
167
+ this.providerId = id;
168
+ }
169
+ // Scopes and custom parameters are fixed server-side (deploy config).
170
+ addScope(_s) {
171
+ return this;
172
+ }
173
+ setCustomParameters(_p) {
174
+ return this;
175
+ }
176
+ }
177
+ export class GoogleAuthProvider extends ProviderBase {
178
+ static PROVIDER_ID = "google.com";
179
+ constructor() {
180
+ super("google.com");
181
+ }
182
+ }
183
+ export class GithubAuthProvider extends ProviderBase {
184
+ static PROVIDER_ID = "github.com";
185
+ constructor() {
186
+ super("github.com");
187
+ }
188
+ }
189
+ export class OAuthProvider extends ProviderBase {
190
+ constructor(id) {
191
+ if (id !== "apple.com" && id !== "google.com" && id !== "github.com")
192
+ throw new FirebaseError("auth/invalid-provider-id", `${id} isn't supported`);
193
+ super(id);
194
+ }
195
+ }
196
+ export const signInWithPopup = (auth, provider, _resolver) => {
197
+ // The popup must open synchronously in the click handler: call before any await.
198
+ const p = auth._estra.signInWithPopup(provider.providerId);
199
+ return guard(async () => {
200
+ const u = await p;
201
+ return credential(auth, u, provider.providerId, isNew(u));
202
+ });
203
+ };
204
+ export const linkWithPopup = (user, provider) => {
205
+ const auth = authOf(user);
206
+ const p = auth._estra.linkWithPopup(provider.providerId);
207
+ return guard(async () => credential(auth, await p, provider.providerId, false, "link"));
208
+ };
209
+ export const reauthenticateWithPopup = (user, provider) => {
210
+ const auth = authOf(user);
211
+ const p = auth._estra.reauthenticateWithPopup(provider.providerId);
212
+ return guard(async () => credential(auth, await p, provider.providerId, false, "reauthenticate"));
213
+ };
214
+ export const signInWithRedirect = (auth, provider) => guard(async () => auth._estra.signInWithRedirect(provider.providerId));
215
+ export const getRedirectResult = (auth) => guard(async () => {
216
+ const u = await auth._estra.getRedirectResult();
217
+ return u ? credential(auth, u, null, isNew(u)) : null;
218
+ });
219
+ export function getAdditionalUserInfo(cred) {
220
+ return cred ? { isNewUser: cred._isNewUser, providerId: cred.providerId, profile: null } : null;
221
+ }
@@ -0,0 +1,18 @@
1
+ import { type Firestore } from "../firestore.js";
2
+ import { type Transport } from "../transport.js";
3
+ import { type FirebaseApp } from "./app.js";
4
+ export * from "../firestore.js";
5
+ /** Settings for an Estra-backed Firestore on any app object (e.g. a real Firebase app). */
6
+ export interface EstraFirestoreSettings {
7
+ estraUrl: string;
8
+ /** The signed-in user's ID token, or null when signed out. */
9
+ getToken: () => Promise<string | null> | string | null;
10
+ /** Called with a listener for sign-in changes, so live listeners switch user at once. */
11
+ onTokenChange?: (cb: () => void) => unknown;
12
+ fetch?: typeof fetch;
13
+ WebSocket?: typeof WebSocket;
14
+ }
15
+ export declare function getFirestore(app?: FirebaseApp | Transport | {
16
+ transport: Transport;
17
+ } | object): Firestore;
18
+ export declare function initializeFirestore(app: object, settings?: Partial<EstraFirestoreSettings> | Record<string, unknown>): Firestore;
@@ -0,0 +1,43 @@
1
+ // Firebase-compatible `firebase/firestore`: Estra's Firestore API, with getFirestore(app) taking
2
+ // the app from ./app.ts (its transport carries the signed-in user's token).
3
+ //
4
+ // Data on Estra, sign-in still on Firebase: alias only firebase/firestore, keep the real
5
+ // firebase/app + firebase/auth, and connect with the Firebase user's ID token:
6
+ // const db = initializeFirestore(app, {
7
+ // estraUrl, getToken: () => auth.currentUser?.getIdToken() ?? null,
8
+ // onTokenChange: (cb) => onIdTokenChanged(auth, cb),
9
+ // });
10
+ // (The server must accept Firebase tokens: defineAuth({ firebaseTokens: { projectId } }).)
11
+ import { getFirestore as fromTransport } from "../firestore.js";
12
+ import { offline } from "../offline.js";
13
+ import { httpTransport } from "../transport.js";
14
+ import { getApp } from "./app.js";
15
+ export * from "../firestore.js";
16
+ const instances = new WeakMap();
17
+ export function getFirestore(app) {
18
+ const key = app ?? getApp();
19
+ let db = instances.get(key);
20
+ if (!db) {
21
+ if ("_estra" in key)
22
+ db = fromTransport(key._estra.transport);
23
+ else if ("getAll" in key || "transport" in key)
24
+ db = fromTransport(key);
25
+ else
26
+ throw new Error("getFirestore: this app has no Estra connection; call initializeFirestore(app, { estraUrl, getToken }) first");
27
+ instances.set(key, db);
28
+ }
29
+ return db;
30
+ }
31
+ export function initializeFirestore(app, settings) {
32
+ const s = (settings ?? {});
33
+ if ("_estra" in app || !s.estraUrl || !s.getToken)
34
+ return getFirestore(app);
35
+ const existing = instances.get(app);
36
+ if (existing)
37
+ return existing;
38
+ const transport = offline(httpTransport({ url: s.estraUrl, getToken: s.getToken, ...(s.fetch ? { fetch: s.fetch } : {}), ...(s.WebSocket ? { WebSocket: s.WebSocket } : {}) }));
39
+ s.onTokenChange?.(() => transport.refreshAuth?.());
40
+ const db = fromTransport(transport);
41
+ instances.set(app, db);
42
+ return db;
43
+ }
@@ -0,0 +1,5 @@
1
+ import { type FirebaseApp } from "./app.js";
2
+ export interface FirebaseStorage {
3
+ readonly app: FirebaseApp;
4
+ }
5
+ export declare const getStorage: (app?: FirebaseApp) => FirebaseStorage;
@@ -0,0 +1,5 @@
1
+ // Firebase-compatible `firebase/storage` stub: getStorage(app) returns a handle. Apps that upload
2
+ // through their own endpoint (presigned R2 URLs) only need it to exist; Estra's storage plugin
3
+ // has its own client in @estra/client/storage.
4
+ import { getApp } from "./app.js";
5
+ export const getStorage = (app = getApp()) => ({ app });
@@ -0,0 +1,256 @@
1
+ import type { DocOut, Filter, Order, Query as QueryAst, Sentinel, Write } from "./_schema/index.js";
2
+ import { EstraClientError, type Transport } from "./transport.js";
3
+ export type DocumentData = Record<string, any>;
4
+ export { EstraClientError as FirestoreError };
5
+ export declare class Timestamp {
6
+ readonly seconds: number;
7
+ readonly nanoseconds: number;
8
+ constructor(seconds: number, nanoseconds: number);
9
+ static now(): Timestamp;
10
+ static fromDate(d: Date): Timestamp;
11
+ static fromMillis(ms: number): Timestamp;
12
+ toMillis(): number;
13
+ toDate(): Date;
14
+ isEqual(o: Timestamp): boolean;
15
+ valueOf(): string;
16
+ toJSON(): {
17
+ seconds: number;
18
+ nanoseconds: number;
19
+ };
20
+ }
21
+ export declare class GeoPoint {
22
+ readonly latitude: number;
23
+ readonly longitude: number;
24
+ constructor(latitude: number, longitude: number);
25
+ isEqual(o: GeoPoint): boolean;
26
+ }
27
+ export declare class FieldValue {
28
+ readonly wire: Sentinel;
29
+ constructor(wire: Sentinel);
30
+ }
31
+ export declare const serverTimestamp: () => FieldValue;
32
+ export declare const increment: (n: number) => FieldValue;
33
+ export declare const arrayUnion: (...values: unknown[]) => FieldValue;
34
+ export declare const arrayRemove: (...values: unknown[]) => FieldValue;
35
+ export declare const deleteField: () => FieldValue;
36
+ export declare class FieldPath {
37
+ readonly segments: string[];
38
+ constructor(...segments: string[]);
39
+ }
40
+ export declare const documentId: () => FieldPath;
41
+ export declare class Firestore {
42
+ readonly type = "firestore";
43
+ readonly transport: Transport;
44
+ constructor(transport: Transport);
45
+ }
46
+ export declare const getFirestore: (t: Transport | {
47
+ transport: Transport;
48
+ }) => Firestore;
49
+ export declare const autoId: () => string;
50
+ export declare class DocumentReference<T = DocumentData> {
51
+ readonly type = "document";
52
+ readonly firestore: Firestore;
53
+ readonly path: string;
54
+ readonly __t?: T;
55
+ constructor(firestore: Firestore, path: string);
56
+ get id(): string;
57
+ get parent(): CollectionReference<T>;
58
+ }
59
+ interface QState {
60
+ from?: string;
61
+ group?: string;
62
+ where: Filter[];
63
+ orderBy: Order[];
64
+ limit?: number;
65
+ limitToLast?: number;
66
+ startAfter?: unknown[];
67
+ startAt?: unknown[];
68
+ endAt?: unknown[];
69
+ endBefore?: unknown[];
70
+ near?: {
71
+ field: string;
72
+ lat: number;
73
+ lng: number;
74
+ radiusMi: number;
75
+ };
76
+ }
77
+ export declare class Query<T = DocumentData> {
78
+ readonly type: "query" | "collection";
79
+ readonly firestore: Firestore;
80
+ readonly state: QState;
81
+ readonly __t?: T;
82
+ constructor(firestore: Firestore, state: QState);
83
+ ast(): QueryAst;
84
+ }
85
+ export declare class CollectionReference<T = DocumentData> extends Query<T> {
86
+ readonly type = "collection";
87
+ readonly path: string;
88
+ constructor(firestore: Firestore, path: string);
89
+ get id(): string;
90
+ get parent(): DocumentReference | null;
91
+ }
92
+ export declare function doc(parent: Firestore | CollectionReference<any> | DocumentReference<any>, path?: string, ...more: string[]): DocumentReference;
93
+ export declare function collection(parent: Firestore | DocumentReference<any> | CollectionReference<any>, path: string, ...more: string[]): CollectionReference;
94
+ export declare const collectionGroup: (db: Firestore, id: string) => Query<DocumentData>;
95
+ type FieldArg = string | FieldPath;
96
+ export declare class QueryConstraint {
97
+ readonly apply: (s: QState) => QState;
98
+ constructor(apply: (s: QState) => QState);
99
+ }
100
+ export declare class QueryFilterConstraint extends QueryConstraint {
101
+ readonly filter: Filter;
102
+ constructor(filter: Filter);
103
+ }
104
+ export type WhereFilterOp = "<" | "<=" | "==" | "!=" | ">=" | ">" | "array-contains" | "in" | "array-contains-any" | "not-in";
105
+ export declare function where(field: FieldArg, op: WhereFilterOp, value: unknown): QueryFilterConstraint;
106
+ export declare const and: (...fs: QueryFilterConstraint[]) => QueryFilterConstraint;
107
+ export declare const or: (...fs: QueryFilterConstraint[]) => QueryFilterConstraint;
108
+ export declare const orderBy: (field: FieldArg, dir?: "asc" | "desc") => QueryConstraint;
109
+ export declare const limit: (n: number) => QueryConstraint;
110
+ /** The last n results in orderBy order (needs an orderBy). */
111
+ export declare const limitToLast: (n: number) => QueryConstraint;
112
+ export declare const startAfter: (...args: unknown[]) => QueryConstraint;
113
+ export declare const startAt: (...args: unknown[]) => QueryConstraint;
114
+ /**
115
+ * Radius search (an Estra addition): documents whose geo field is within `radiusMi` of `center`.
116
+ * Without an orderBy, results sort by distance; each snapshot's `distanceMi` says how far. Works
117
+ * in listeners too (moving documents enter and leave the results).
118
+ */
119
+ export declare const near: (field: FieldArg, center: GeoPoint | {
120
+ lat: number;
121
+ lng: number;
122
+ }, radiusMi: number) => QueryConstraint;
123
+ export declare const endAt: (...args: unknown[]) => QueryConstraint;
124
+ export declare const endBefore: (...args: unknown[]) => QueryConstraint;
125
+ export declare function query<T>(q: Query<T>, ...constraints: QueryConstraint[]): Query<T>;
126
+ export interface SnapshotMetadata {
127
+ hasPendingWrites: boolean;
128
+ fromCache: boolean;
129
+ isEqual(o: SnapshotMetadata): boolean;
130
+ }
131
+ export declare class DocumentSnapshot<T = DocumentData> {
132
+ readonly ref: DocumentReference<T>;
133
+ readonly metadata: SnapshotMetadata;
134
+ readonly _raw: DocOut | null;
135
+ constructor(ref: DocumentReference<T>, raw: DocOut | null, m?: {
136
+ fromCache: boolean;
137
+ hasPendingWrites: boolean;
138
+ });
139
+ get id(): string;
140
+ exists(): boolean;
141
+ /** For `near` queries: miles from the query's center (an Estra addition). */
142
+ get distanceMi(): number | undefined;
143
+ data(): T | undefined;
144
+ get(field: FieldArg): any;
145
+ }
146
+ export declare class QueryDocumentSnapshot<T = DocumentData> extends DocumentSnapshot<T> {
147
+ data(): T;
148
+ }
149
+ export interface DocumentChange<T = DocumentData> {
150
+ type: "added" | "modified" | "removed";
151
+ doc: QueryDocumentSnapshot<T>;
152
+ oldIndex: number;
153
+ newIndex: number;
154
+ }
155
+ export declare class QuerySnapshot<T = DocumentData> {
156
+ readonly query: Query<T>;
157
+ readonly docs: QueryDocumentSnapshot<T>[];
158
+ readonly metadata: SnapshotMetadata;
159
+ private readonly changes;
160
+ constructor(q: Query<T>, docs: QueryDocumentSnapshot<T>[], changes?: DocumentChange<T>[] | null, m?: {
161
+ fromCache: boolean;
162
+ hasPendingWrites: boolean;
163
+ });
164
+ /** Changes since the previous snapshot of a listener (all "added" for a one-off read). */
165
+ docChanges(): DocumentChange<T>[];
166
+ get size(): number;
167
+ get empty(): boolean;
168
+ forEach(cb: (d: QueryDocumentSnapshot<T>) => void): void;
169
+ }
170
+ export declare function getDoc<T>(ref: DocumentReference<T>): Promise<DocumentSnapshot<T>>;
171
+ export declare function getDocs<T>(q: Query<T>): Promise<QuerySnapshot<T>>;
172
+ export declare const waitForPendingWrites: (db: Firestore) => Promise<void>;
173
+ export declare const disableNetwork: (db: Firestore) => Promise<void>;
174
+ export declare const enableNetwork: (db: Firestore) => Promise<void>;
175
+ export declare const clearIndexedDbPersistence: (db: Firestore) => Promise<void>;
176
+ export declare const clearPersistence: (db: Firestore) => Promise<void>;
177
+ export declare function getCountFromServer(q: Query<any>): Promise<{
178
+ data: () => {
179
+ count: number;
180
+ };
181
+ }>;
182
+ type AggSpec = {
183
+ kind: "count";
184
+ } | {
185
+ kind: "sum" | "avg";
186
+ field: string;
187
+ };
188
+ export declare const count: () => AggSpec;
189
+ export declare const sum: (field: string) => AggSpec;
190
+ export declare const average: (field: string) => AggSpec;
191
+ export declare function getAggregateFromServer(q: Query<any>, spec: Record<string, AggSpec>): Promise<{
192
+ data: () => {
193
+ [k: string]: number | null;
194
+ };
195
+ }>;
196
+ /**
197
+ * Live aggregates (an Estra addition; Firestore has none): calls `next` with the current values,
198
+ * then again whenever a matching document changes them. Same rules and caps as getAggregateFromServer.
199
+ * onAggregateSnapshot(query(collection(db, "orders"), where("open", "==", true)), { n: count(), total: sum("amount") }, (s) => show(s.data()))
200
+ */
201
+ export declare function onAggregateSnapshot<S extends Record<string, AggSpec>>(q: Query<any>, spec: S, next: (s: {
202
+ data(): {
203
+ [K in keyof S]: number | null;
204
+ };
205
+ }) => void, error?: (e: EstraClientError) => void): Unsubscribe;
206
+ export interface SetOptions {
207
+ merge?: boolean;
208
+ }
209
+ /**
210
+ * Hot counters: adds `by` to a number field through the server's counter buffer (one commit about
211
+ * every second, however many calls). Resolves with the new value (stored + buffered). Needs
212
+ * `createEstra({ counters })`, and passes the document's update rule per call. Online only.
213
+ */
214
+ export declare function incrementBuffered(ref: DocumentReference<any>, field: string | FieldPath, by?: number): Promise<number | null>;
215
+ export declare const setDoc: (ref: DocumentReference<any>, data: DocumentData, o?: SetOptions) => Promise<void>;
216
+ export declare const updateDoc: (ref: DocumentReference<any>, a: DocumentData | FieldArg, ...rest: unknown[]) => Promise<void>;
217
+ export declare const deleteDoc: (ref: DocumentReference<any>) => Promise<void>;
218
+ export declare function addDoc<T>(coll: CollectionReference<T>, data: DocumentData): Promise<DocumentReference<T>>;
219
+ export declare class WriteBatch {
220
+ readonly db: Firestore;
221
+ readonly writes: Write[];
222
+ constructor(db: Firestore);
223
+ set(ref: DocumentReference<any>, data: DocumentData, o?: SetOptions): this;
224
+ update(ref: DocumentReference<any>, a: DocumentData | FieldArg, ...rest: unknown[]): this;
225
+ delete(ref: DocumentReference<any>): this;
226
+ commit(): Promise<void>;
227
+ }
228
+ export declare const writeBatch: (db: Firestore) => WriteBatch;
229
+ export declare class Transaction {
230
+ readonly db: Firestore;
231
+ readonly reads: Map<string, number>;
232
+ readonly writes: Write[];
233
+ constructor(db: Firestore);
234
+ get<T>(ref: DocumentReference<T>): Promise<DocumentSnapshot<T>>;
235
+ set(ref: DocumentReference<any>, data: DocumentData, o?: SetOptions): this;
236
+ update(ref: DocumentReference<any>, a: DocumentData | FieldArg, ...rest: unknown[]): this;
237
+ delete(ref: DocumentReference<any>): this;
238
+ }
239
+ /** Reads, then writes guarded on the versions read; retried when a read went stale. */
240
+ export declare function runTransaction<R>(db: Firestore, fn: (t: Transaction) => Promise<R>, o?: {
241
+ maxAttempts?: number;
242
+ }): Promise<R>;
243
+ type Observer<S> = {
244
+ next?: (s: S) => void;
245
+ error?: (e: EstraClientError) => void;
246
+ complete?: () => void;
247
+ };
248
+ export type Unsubscribe = () => void;
249
+ export declare function onSnapshot<T>(ref: DocumentReference<T>, next: ((s: DocumentSnapshot<T>) => void) | Observer<DocumentSnapshot<T>>, error?: (e: EstraClientError) => void): Unsubscribe;
250
+ export declare function onSnapshot<T>(q: Query<T>, next: ((s: QuerySnapshot<T>) => void) | Observer<QuerySnapshot<T>>, error?: (e: EstraClientError) => void): Unsubscribe;
251
+ export declare function onSnapshot<T>(ref: DocumentReference<T>, options: {
252
+ includeMetadataChanges?: boolean;
253
+ }, next: (s: DocumentSnapshot<T>) => void, error?: (e: EstraClientError) => void): Unsubscribe;
254
+ export declare function onSnapshot<T>(q: Query<T>, options: {
255
+ includeMetadataChanges?: boolean;
256
+ }, next: (s: QuerySnapshot<T>) => void, error?: (e: EstraClientError) => void): Unsubscribe;