@sovaria/sdk 0.0.0-stage → 0.4.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.
- package/LICENSE +21 -0
- package/README.md +179 -2
- package/dist/client.d.ts +146 -0
- package/dist/client.js +583 -0
- package/dist/collections.d.ts +189 -0
- package/dist/collections.js +467 -0
- package/dist/complete-api.d.ts +47 -0
- package/dist/complete-api.js +64 -0
- package/dist/crashes.d.ts +79 -0
- package/dist/crashes.js +176 -0
- package/dist/crypto.d.ts +37 -0
- package/dist/crypto.js +189 -0
- package/dist/errors.d.ts +40 -0
- package/dist/errors.js +246 -0
- package/dist/firestore.d.ts +176 -0
- package/dist/firestore.js +337 -0
- package/dist/generated.d.ts +1227 -0
- package/dist/generated.js +1054 -0
- package/dist/index.d.ts +2046 -0
- package/dist/index.js +2287 -0
- package/dist/offline.d.ts +90 -0
- package/dist/offline.js +528 -0
- package/dist/types.d.ts +102 -0
- package/dist/types.js +1 -0
- package/package.json +74 -4
package/dist/errors.js
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Errors thrown by the SDK.
|
|
3
|
+
*
|
|
4
|
+
* `message` is written for the people using your app ("You don't have permission to do that."),
|
|
5
|
+
* so `catch (e) { show(e.message) }` never leaks policy names, table names or SQL to end users.
|
|
6
|
+
* The server's own text stays available for developers in `technicalMessage` / `detail`,
|
|
7
|
+
* and `code` is a stable value to branch on.
|
|
8
|
+
*/
|
|
9
|
+
const FRIENDLY = {
|
|
10
|
+
NETWORK_ERROR: "Can't reach the server. Check your connection and try again.",
|
|
11
|
+
UNAUTHENTICATED: 'Your session has ended. Please sign in again.',
|
|
12
|
+
PERMISSION_DENIED: "You don't have permission to do that.",
|
|
13
|
+
NOT_FOUND: "We couldn't find what you were looking for.",
|
|
14
|
+
CONFLICT: 'That already exists or was changed by someone else.',
|
|
15
|
+
INVALID_INPUT: 'Some of the information entered is not valid.',
|
|
16
|
+
PAYLOAD_TOO_LARGE: 'That file or request is too large.',
|
|
17
|
+
UNSUPPORTED_TYPE: "That file type isn't allowed here.",
|
|
18
|
+
RATE_LIMITED: 'Too many requests. Please wait a moment and try again.',
|
|
19
|
+
PLAN_LIMIT_REACHED: "This project has reached its plan limit.",
|
|
20
|
+
PLAN_FEATURE_REQUIRED: "This feature isn't available on the current plan.",
|
|
21
|
+
PLAN_QUOTA_EXHAUSTED: "This project's usage quota is used up for now.",
|
|
22
|
+
SERVICE_UNAVAILABLE: 'The service is busy right now. Please try again shortly.',
|
|
23
|
+
SERVER_ERROR: 'Something went wrong on our side. Please try again.',
|
|
24
|
+
BAD_REQUEST: "That request couldn't be completed.",
|
|
25
|
+
CONFIG_ERROR: 'The app is not configured correctly.',
|
|
26
|
+
UNKNOWN: 'Something went wrong. Please try again.',
|
|
27
|
+
};
|
|
28
|
+
const PLAN_CODES = new Set(['PLAN_LIMIT_REACHED', 'PLAN_FEATURE_REQUIRED', 'PLAN_QUOTA_EXHAUSTED']);
|
|
29
|
+
function serverText(detail) {
|
|
30
|
+
if (detail == null)
|
|
31
|
+
return '';
|
|
32
|
+
if (typeof detail === 'string')
|
|
33
|
+
return detail;
|
|
34
|
+
if (Array.isArray(detail))
|
|
35
|
+
return detail.map(serverText).filter(Boolean).join('; ');
|
|
36
|
+
if (typeof detail === 'object') {
|
|
37
|
+
const d = detail;
|
|
38
|
+
for (const key of ['detail', 'message', 'msg', 'error', 'error_description']) {
|
|
39
|
+
if (d[key] != null) {
|
|
40
|
+
const text = serverText(d[key]);
|
|
41
|
+
if (text)
|
|
42
|
+
return text;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
return JSON.stringify(detail);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return String(detail);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return String(detail);
|
|
53
|
+
}
|
|
54
|
+
function explicitCode(detail) {
|
|
55
|
+
if (!detail || typeof detail !== 'object' || Array.isArray(detail))
|
|
56
|
+
return undefined;
|
|
57
|
+
const d = detail;
|
|
58
|
+
if (typeof d.code === 'string')
|
|
59
|
+
return d.code;
|
|
60
|
+
for (const key of ['detail', 'error']) {
|
|
61
|
+
const inner = explicitCode(d[key]);
|
|
62
|
+
if (inner)
|
|
63
|
+
return inner;
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
function planMessage(detail) {
|
|
68
|
+
if (!detail || typeof detail !== 'object' || Array.isArray(detail))
|
|
69
|
+
return undefined;
|
|
70
|
+
const d = detail;
|
|
71
|
+
if (typeof d.code === 'string' && PLAN_CODES.has(d.code) && typeof d.message === 'string')
|
|
72
|
+
return d.message;
|
|
73
|
+
for (const key of ['detail', 'error']) {
|
|
74
|
+
const inner = planMessage(d[key]);
|
|
75
|
+
if (inner)
|
|
76
|
+
return inner;
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
/** FastAPI/pydantic validation errors: [{loc: [..., "field"], msg}] → "title: field required". */
|
|
81
|
+
function validationMessage(detail) {
|
|
82
|
+
const items = Array.isArray(detail)
|
|
83
|
+
? detail
|
|
84
|
+
: detail && typeof detail === 'object' && Array.isArray(detail.detail)
|
|
85
|
+
? (detail.detail)
|
|
86
|
+
: undefined;
|
|
87
|
+
if (!items?.length)
|
|
88
|
+
return undefined;
|
|
89
|
+
const first = items[0];
|
|
90
|
+
if (typeof first?.msg !== 'string')
|
|
91
|
+
return undefined;
|
|
92
|
+
const field = Array.isArray(first.loc) ? first.loc.filter((p) => typeof p === 'string' && p !== 'body').pop() : undefined;
|
|
93
|
+
const raw = first.msg.replace(/^Value error, /, '');
|
|
94
|
+
const plain = humanizeValidation(raw);
|
|
95
|
+
const name = field ? capitalize(String(field).replace(/_/g, ' ')) : '';
|
|
96
|
+
if (plain)
|
|
97
|
+
return name ? `${name} ${plain}` : capitalize(plain);
|
|
98
|
+
return name ? `${name}: ${raw}` : capitalize(raw);
|
|
99
|
+
}
|
|
100
|
+
function capitalize(text) {
|
|
101
|
+
return text ? text.charAt(0).toUpperCase() + text.slice(1) : text;
|
|
102
|
+
}
|
|
103
|
+
/** pydantic wording → plain English, phrased to follow the field name ("Email is required."). */
|
|
104
|
+
function humanizeValidation(msg) {
|
|
105
|
+
const rules = [
|
|
106
|
+
[/^Field required$/i, () => 'is required.'],
|
|
107
|
+
[/^String should have at least (\d+) characters?$/i, (m) => `must be at least ${m[1]} characters.`],
|
|
108
|
+
[/^String should have at most (\d+) characters?$/i, (m) => `must be at most ${m[1]} characters.`],
|
|
109
|
+
[/^Input should be greater than or equal to (\S+)$/i, (m) => `must be ${m[1]} or more.`],
|
|
110
|
+
[/^Input should be less than or equal to (\S+)$/i, (m) => `must be ${m[1]} or less.`],
|
|
111
|
+
[/^Input should be a valid (\w+)/i, (m) => `must be a valid ${m[1]}.`],
|
|
112
|
+
[/not a valid email address/i, () => 'is not a valid email address.'],
|
|
113
|
+
[/^String should match pattern/i, () => 'is not in the right format.'],
|
|
114
|
+
];
|
|
115
|
+
for (const [re, fmt] of rules) {
|
|
116
|
+
const m = msg.match(re);
|
|
117
|
+
if (m)
|
|
118
|
+
return fmt(m);
|
|
119
|
+
}
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
/** Postgres constraint text that is safe and useful to show (no table or policy names). */
|
|
123
|
+
function databaseMessage(text) {
|
|
124
|
+
const t = text.toLowerCase();
|
|
125
|
+
if (t.includes('row-level security') || t.includes('guard denied') || t.includes('permission denied')) {
|
|
126
|
+
return { code: 'PERMISSION_DENIED', message: FRIENDLY.PERMISSION_DENIED };
|
|
127
|
+
}
|
|
128
|
+
if (t.includes('duplicate key') || t.includes('unique constraint')) {
|
|
129
|
+
return { code: 'CONFLICT', message: 'That already exists.' };
|
|
130
|
+
}
|
|
131
|
+
if (t.includes('violates foreign key')) {
|
|
132
|
+
return { code: 'CONFLICT', message: 'That refers to something that no longer exists, or is still in use.' };
|
|
133
|
+
}
|
|
134
|
+
if (t.includes('violates check constraint') || t.includes('violates not-null') || t.includes('invalid input syntax')) {
|
|
135
|
+
return { code: 'INVALID_INPUT', message: FRIENDLY.INVALID_INPUT };
|
|
136
|
+
}
|
|
137
|
+
if (t.includes('value too long'))
|
|
138
|
+
return { code: 'INVALID_INPUT', message: 'One of the values is too long.' };
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
function classify(status, detail) {
|
|
142
|
+
const raw = explicitCode(detail);
|
|
143
|
+
if (raw && PLAN_CODES.has(raw)) {
|
|
144
|
+
return { code: raw, message: planMessage(detail) ?? FRIENDLY[raw] };
|
|
145
|
+
}
|
|
146
|
+
const text = serverText(detail);
|
|
147
|
+
const db = databaseMessage(text);
|
|
148
|
+
if (status === 0)
|
|
149
|
+
return { code: 'NETWORK_ERROR', message: FRIENDLY.NETWORK_ERROR };
|
|
150
|
+
if (status === 401)
|
|
151
|
+
return { code: 'UNAUTHENTICATED', message: FRIENDLY.UNAUTHENTICATED };
|
|
152
|
+
if (status === 403)
|
|
153
|
+
return { code: 'PERMISSION_DENIED', message: FRIENDLY.PERMISSION_DENIED };
|
|
154
|
+
if (db)
|
|
155
|
+
return db;
|
|
156
|
+
if (status === 404)
|
|
157
|
+
return { code: 'NOT_FOUND', message: FRIENDLY.NOT_FOUND };
|
|
158
|
+
if (status === 409)
|
|
159
|
+
return { code: 'CONFLICT', message: FRIENDLY.CONFLICT };
|
|
160
|
+
if (status === 413)
|
|
161
|
+
return { code: 'PAYLOAD_TOO_LARGE', message: FRIENDLY.PAYLOAD_TOO_LARGE };
|
|
162
|
+
if (status === 415)
|
|
163
|
+
return { code: 'UNSUPPORTED_TYPE', message: FRIENDLY.UNSUPPORTED_TYPE };
|
|
164
|
+
if (status === 422)
|
|
165
|
+
return { code: 'INVALID_INPUT', message: validationMessage(detail) ?? FRIENDLY.INVALID_INPUT };
|
|
166
|
+
if (status === 429)
|
|
167
|
+
return { code: 'RATE_LIMITED', message: FRIENDLY.RATE_LIMITED };
|
|
168
|
+
if (status === 502 || status === 503 || status === 504) {
|
|
169
|
+
return { code: 'SERVICE_UNAVAILABLE', message: FRIENDLY.SERVICE_UNAVAILABLE };
|
|
170
|
+
}
|
|
171
|
+
if (status >= 500)
|
|
172
|
+
return { code: 'SERVER_ERROR', message: FRIENDLY.SERVER_ERROR };
|
|
173
|
+
if (status === 400)
|
|
174
|
+
return { code: 'BAD_REQUEST', message: FRIENDLY.BAD_REQUEST };
|
|
175
|
+
return { code: 'UNKNOWN', message: FRIENDLY.UNKNOWN };
|
|
176
|
+
}
|
|
177
|
+
export class SovariaError extends Error {
|
|
178
|
+
/** HTTP status (0 when the request never got a response). */
|
|
179
|
+
statusCode;
|
|
180
|
+
/** The server's response body (or the SDK's own explanation). */
|
|
181
|
+
detail;
|
|
182
|
+
/** Stable category to branch on. */
|
|
183
|
+
code;
|
|
184
|
+
/** The server code when one was sent (e.g. PLAN_LIMIT_REACHED), else `code`. */
|
|
185
|
+
serverCode;
|
|
186
|
+
/** `HTTP <status>: <server text>` — for logs and developers, not for end users. */
|
|
187
|
+
technicalMessage;
|
|
188
|
+
/**
|
|
189
|
+
* The request's trace id (W3C traceparent). In the console, Logs › Request traces (or
|
|
190
|
+
* `sov.api.observability…` / `sov logs why <id>`) says which check refused the request and why.
|
|
191
|
+
*/
|
|
192
|
+
traceId;
|
|
193
|
+
constructor(statusCode, detail, code) {
|
|
194
|
+
const classified = code
|
|
195
|
+
? { code, message: code === 'CONFIG_ERROR' && typeof detail === 'string' ? detail : FRIENDLY[code] }
|
|
196
|
+
: classify(statusCode, detail);
|
|
197
|
+
super(classified.message);
|
|
198
|
+
this.name = 'SovariaError';
|
|
199
|
+
this.statusCode = statusCode;
|
|
200
|
+
this.detail = detail;
|
|
201
|
+
this.code = classified.code;
|
|
202
|
+
this.serverCode = explicitCode(detail) ?? classified.code;
|
|
203
|
+
this.technicalMessage = `HTTP ${statusCode}: ${serverText(detail)}`;
|
|
204
|
+
}
|
|
205
|
+
/** Attach the request's trace id (kept in `technicalMessage` for logs). */
|
|
206
|
+
withTraceId(traceId) {
|
|
207
|
+
if (traceId) {
|
|
208
|
+
this.traceId = traceId;
|
|
209
|
+
this.technicalMessage = `${this.technicalMessage} (trace ${traceId})`;
|
|
210
|
+
}
|
|
211
|
+
return this;
|
|
212
|
+
}
|
|
213
|
+
/** A mistake in how the SDK is set up or called; the message is written for the developer. */
|
|
214
|
+
static config(message, statusCode = 400) {
|
|
215
|
+
return new SovariaError(statusCode, message, 'CONFIG_ERROR');
|
|
216
|
+
}
|
|
217
|
+
/** The message to show your users (same as `message`). */
|
|
218
|
+
get userMessage() {
|
|
219
|
+
return this.message;
|
|
220
|
+
}
|
|
221
|
+
toString() {
|
|
222
|
+
return `SovariaError [${this.code}] ${this.message} (${this.technicalMessage})`;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/** A friendly message for any thrown value: SovariaError text as-is, anything else generic. */
|
|
226
|
+
export function userMessageFor(error, fallback = FRIENDLY.UNKNOWN) {
|
|
227
|
+
if (error instanceof SovariaError)
|
|
228
|
+
return error.message;
|
|
229
|
+
if (error instanceof TypeError && /fetch|network/i.test(error.message))
|
|
230
|
+
return FRIENDLY.NETWORK_ERROR;
|
|
231
|
+
return fallback;
|
|
232
|
+
}
|
|
233
|
+
/** A W3C trace id (32 hex) for one logical request; every retry of it shares the id. */
|
|
234
|
+
export function newTraceId() {
|
|
235
|
+
const bytes = new Uint8Array(16);
|
|
236
|
+
globalThis.crypto?.getRandomValues?.(bytes);
|
|
237
|
+
if (bytes.every((b) => b === 0))
|
|
238
|
+
for (let i = 0; i < 16; i += 1)
|
|
239
|
+
bytes[i] = Math.floor(Math.random() * 256);
|
|
240
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
|
|
241
|
+
}
|
|
242
|
+
/** `traceparent` for one attempt: the request's trace id and a fresh span id. */
|
|
243
|
+
export function traceparentFor(traceId) {
|
|
244
|
+
const span = newTraceId().slice(0, 16);
|
|
245
|
+
return `00-${traceId}-${span}-01`;
|
|
246
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { type DocumentSnapshot as SovDocumentSnapshot, DocumentReference as SovDocumentReference, Query as SovQuery, type QuerySnapshot as SovQuerySnapshot, Transaction as SovTransaction, WriteBatch as SovWriteBatch, type WhereOp } from './collections.js';
|
|
2
|
+
export type DocumentData = Record<string, any>;
|
|
3
|
+
export type WhereFilterOp = WhereOp;
|
|
4
|
+
export type OrderByDirection = 'asc' | 'desc';
|
|
5
|
+
export type Unsubscribe = () => void;
|
|
6
|
+
export interface SetOptions {
|
|
7
|
+
merge?: boolean;
|
|
8
|
+
mergeFields?: string[];
|
|
9
|
+
}
|
|
10
|
+
/** What getFirestore needs from a Sovaria client (any `SovariaSDK`). */
|
|
11
|
+
import { type OfflineStorage } from './offline.js';
|
|
12
|
+
export interface SovariaLike {
|
|
13
|
+
collection<T = any>(path: string): SovQuery<T>;
|
|
14
|
+
doc<T = any>(path: string): SovDocumentReference<T>;
|
|
15
|
+
batch(): SovWriteBatch;
|
|
16
|
+
runTransaction<R>(fn: (tx: SovTransaction) => Promise<R>, maxAttempts?: number): Promise<R>;
|
|
17
|
+
}
|
|
18
|
+
export declare class Firestore {
|
|
19
|
+
readonly sov: SovariaLike;
|
|
20
|
+
readonly type = "firestore";
|
|
21
|
+
constructor(sov: SovariaLike);
|
|
22
|
+
}
|
|
23
|
+
/** The Firestore handle for a Sovaria client. */
|
|
24
|
+
export declare function getFirestore(sov: SovariaLike): Firestore;
|
|
25
|
+
export type LocalCache = {
|
|
26
|
+
kind: 'persistent' | 'memory';
|
|
27
|
+
storage?: OfflineStorage;
|
|
28
|
+
};
|
|
29
|
+
/** Firebase's `persistentLocalCache()`: IndexedDB in browsers (memory where there is none). */
|
|
30
|
+
export declare function persistentLocalCache(options?: {
|
|
31
|
+
storage?: OfflineStorage;
|
|
32
|
+
}): LocalCache;
|
|
33
|
+
/** Firebase's `memoryLocalCache()`: offline cache for this page/process only. */
|
|
34
|
+
export declare function memoryLocalCache(): LocalCache;
|
|
35
|
+
/** `initializeFirestore(sov, { localCache: persistentLocalCache() })` turns on offline mode, like Firebase. */
|
|
36
|
+
export declare function initializeFirestore(sov: SovariaLike, settings?: {
|
|
37
|
+
localCache?: LocalCache;
|
|
38
|
+
}): Firestore;
|
|
39
|
+
/** Firebase's (older) way to turn on the persistent cache. */
|
|
40
|
+
export declare function enableIndexedDbPersistence(db: Firestore): Promise<void>;
|
|
41
|
+
export declare class DocumentReference<T = DocumentData> {
|
|
42
|
+
readonly firestore: Firestore;
|
|
43
|
+
readonly _ref: SovDocumentReference<T>;
|
|
44
|
+
readonly type = "document";
|
|
45
|
+
constructor(firestore: Firestore, _ref: SovDocumentReference<T>);
|
|
46
|
+
get id(): string;
|
|
47
|
+
get path(): string;
|
|
48
|
+
get parent(): CollectionReference<T>;
|
|
49
|
+
}
|
|
50
|
+
export declare class Query<T = DocumentData> {
|
|
51
|
+
readonly firestore: Firestore;
|
|
52
|
+
readonly _query: SovQuery<T>;
|
|
53
|
+
readonly type: 'query' | 'collection';
|
|
54
|
+
constructor(firestore: Firestore, _query: SovQuery<T>);
|
|
55
|
+
}
|
|
56
|
+
export declare class CollectionReference<T = DocumentData> extends Query<T> {
|
|
57
|
+
readonly type: "collection";
|
|
58
|
+
get id(): string;
|
|
59
|
+
get path(): string;
|
|
60
|
+
}
|
|
61
|
+
type Parent = Firestore | CollectionReference<any> | DocumentReference<any>;
|
|
62
|
+
/** `collection(db, 'users')`, `collection(db, 'users', uid, 'posts')`, `collection(docRef, 'posts')`. */
|
|
63
|
+
export declare function collection<T = DocumentData>(parent: Parent, path: string, ...segments: string[]): CollectionReference<T>;
|
|
64
|
+
/** `doc(db, 'users', uid)`, `doc(db, 'users/uid')`, `doc(collectionRef)` (new id), `doc(collectionRef, id)`. */
|
|
65
|
+
export declare function doc<T = DocumentData>(parent: Parent, path?: string, ...segments: string[]): DocumentReference<T>;
|
|
66
|
+
/** `where(documentId(), '==', id)` filters on the document id. */
|
|
67
|
+
export declare function documentId(): string;
|
|
68
|
+
export type QueryConstraint = (q: SovQuery<any>) => SovQuery<any>;
|
|
69
|
+
export declare function where(field: string, op: WhereFilterOp, value: unknown): QueryConstraint;
|
|
70
|
+
export declare function orderBy(field: string, direction?: OrderByDirection): QueryConstraint;
|
|
71
|
+
export declare function limit(n: number): QueryConstraint;
|
|
72
|
+
export declare function limitToLast(n: number): QueryConstraint;
|
|
73
|
+
export declare function startAt(...values: unknown[]): QueryConstraint;
|
|
74
|
+
export declare function startAfter(...values: unknown[]): QueryConstraint;
|
|
75
|
+
export declare function endAt(...values: unknown[]): QueryConstraint;
|
|
76
|
+
export declare function endBefore(...values: unknown[]): QueryConstraint;
|
|
77
|
+
export declare function query<T = DocumentData>(base: Query<T>, ...constraints: QueryConstraint[]): Query<T>;
|
|
78
|
+
export declare class DocumentSnapshot<T = DocumentData> {
|
|
79
|
+
readonly ref: DocumentReference<T>;
|
|
80
|
+
readonly _snap: SovDocumentSnapshot<T>;
|
|
81
|
+
constructor(ref: DocumentReference<T>, _snap: SovDocumentSnapshot<T>);
|
|
82
|
+
get id(): string;
|
|
83
|
+
exists(): boolean;
|
|
84
|
+
data(): T | undefined;
|
|
85
|
+
/** A field by dotted path (`get('address.city')`). */
|
|
86
|
+
get(field: string): any;
|
|
87
|
+
/** Offline mode (enableIndexedDbPersistence / persistentLocalCache): from the cache, or a write not yet synced. */
|
|
88
|
+
get metadata(): {
|
|
89
|
+
hasPendingWrites: boolean;
|
|
90
|
+
fromCache: boolean;
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
export declare class QueryDocumentSnapshot<T = DocumentData> extends DocumentSnapshot<T> {
|
|
94
|
+
data(): T;
|
|
95
|
+
}
|
|
96
|
+
export interface DocumentChange<T = DocumentData> {
|
|
97
|
+
type: 'added' | 'modified' | 'removed';
|
|
98
|
+
doc: QueryDocumentSnapshot<T>;
|
|
99
|
+
oldIndex: number;
|
|
100
|
+
newIndex: number;
|
|
101
|
+
}
|
|
102
|
+
export declare class QuerySnapshot<T = DocumentData> {
|
|
103
|
+
readonly query: Query<T>;
|
|
104
|
+
readonly docs: QueryDocumentSnapshot<T>[];
|
|
105
|
+
private readonly changes;
|
|
106
|
+
constructor(query: Query<T>, raw: SovQuerySnapshot<T>);
|
|
107
|
+
get size(): number;
|
|
108
|
+
get empty(): boolean;
|
|
109
|
+
forEach(callback: (doc: QueryDocumentSnapshot<T>) => void): void;
|
|
110
|
+
docChanges(): DocumentChange<T>[];
|
|
111
|
+
/** Offline mode: answered from the cache, or holding writes not yet synced. */
|
|
112
|
+
get metadata(): {
|
|
113
|
+
hasPendingWrites: boolean;
|
|
114
|
+
fromCache: boolean;
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
export declare function getDoc<T = DocumentData>(ref: DocumentReference<T>): Promise<DocumentSnapshot<T>>;
|
|
118
|
+
export declare function getDocs<T = DocumentData>(q: Query<T>): Promise<QuerySnapshot<T>>;
|
|
119
|
+
export declare function getCountFromServer(q: Query<any>): Promise<{
|
|
120
|
+
data: () => {
|
|
121
|
+
count: number;
|
|
122
|
+
};
|
|
123
|
+
}>;
|
|
124
|
+
type Observer<S> = {
|
|
125
|
+
next?: (snap: S) => void;
|
|
126
|
+
error?: (error: unknown) => void;
|
|
127
|
+
};
|
|
128
|
+
/** Live document or query. Returns its unsubscribe function right away, like Firebase. */
|
|
129
|
+
export declare function onSnapshot<T = DocumentData>(target: DocumentReference<T> | Query<T>, onNextOrObserver: ((snap: any) => void) | Observer<any>, onError?: (error: unknown) => void): Unsubscribe;
|
|
130
|
+
/** A point in time. Stored as an ISO-8601 string in Sovaria. */
|
|
131
|
+
export declare class Timestamp {
|
|
132
|
+
readonly seconds: number;
|
|
133
|
+
readonly nanoseconds: number;
|
|
134
|
+
constructor(seconds: number, nanoseconds: number);
|
|
135
|
+
static now(): Timestamp;
|
|
136
|
+
static fromDate(date: Date): Timestamp;
|
|
137
|
+
static fromMillis(ms: number): Timestamp;
|
|
138
|
+
/** Read back a stored timestamp (an ISO string) as a Timestamp. */
|
|
139
|
+
static fromIso(value: string): Timestamp;
|
|
140
|
+
toMillis(): number;
|
|
141
|
+
toDate(): Date;
|
|
142
|
+
toISOString(): string;
|
|
143
|
+
isEqual(other: Timestamp): boolean;
|
|
144
|
+
}
|
|
145
|
+
export declare function serverTimestamp(): import("./collections.js").FieldTransform;
|
|
146
|
+
export declare function increment(n: number): import("./collections.js").FieldTransform;
|
|
147
|
+
export declare function arrayUnion(...elements: unknown[]): import("./collections.js").FieldTransform;
|
|
148
|
+
export declare function arrayRemove(...elements: unknown[]): import("./collections.js").FieldTransform;
|
|
149
|
+
export declare function deleteField(): import("./collections.js").FieldTransform;
|
|
150
|
+
export declare function setDoc<T = DocumentData>(ref: DocumentReference<T>, data: Partial<T> & DocumentData, options?: SetOptions): Promise<void>;
|
|
151
|
+
export declare function addDoc<T = DocumentData>(ref: CollectionReference<T>, data: Partial<T> & DocumentData): Promise<DocumentReference<T>>;
|
|
152
|
+
/** `updateDoc(ref, {a: 1})` or `updateDoc(ref, 'a', 1, 'b.c', 2)`. */
|
|
153
|
+
export declare function updateDoc(ref: DocumentReference<any>, dataOrField: string | DocumentData, ...rest: unknown[]): Promise<void>;
|
|
154
|
+
export declare function deleteDoc(ref: DocumentReference<any>): Promise<void>;
|
|
155
|
+
export declare class WriteBatch {
|
|
156
|
+
private readonly batch;
|
|
157
|
+
constructor(batch: SovWriteBatch);
|
|
158
|
+
set(ref: DocumentReference<any>, data: DocumentData, options?: SetOptions): WriteBatch;
|
|
159
|
+
update(ref: DocumentReference<any>, dataOrField: string | DocumentData, ...rest: unknown[]): WriteBatch;
|
|
160
|
+
delete(ref: DocumentReference<any>): WriteBatch;
|
|
161
|
+
commit(): Promise<void>;
|
|
162
|
+
}
|
|
163
|
+
export declare function writeBatch(db: Firestore): WriteBatch;
|
|
164
|
+
export declare class Transaction {
|
|
165
|
+
private readonly tx;
|
|
166
|
+
constructor(tx: SovTransaction);
|
|
167
|
+
get<T = DocumentData>(ref: DocumentReference<T>): Promise<DocumentSnapshot<T>>;
|
|
168
|
+
set(ref: DocumentReference<any>, data: DocumentData, options?: SetOptions): Transaction;
|
|
169
|
+
update(ref: DocumentReference<any>, dataOrField: string | DocumentData, ...rest: unknown[]): Transaction;
|
|
170
|
+
delete(ref: DocumentReference<any>): Transaction;
|
|
171
|
+
}
|
|
172
|
+
/** Reads then writes atomically; retried when a document it read changed meanwhile (default 5 attempts). */
|
|
173
|
+
export declare function runTransaction<R>(db: Firestore, fn: (tx: Transaction) => Promise<R>, options?: {
|
|
174
|
+
maxAttempts?: number;
|
|
175
|
+
}): Promise<R>;
|
|
176
|
+
export {};
|