@zuzjs/flare 0.2.13 → 0.2.15

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,1322 @@
1
+ import { AuthToken } from '@zuzjs/auth';
2
+
3
+ /**
4
+ * Client Configuration
5
+ */
6
+ interface FlareConfig {
7
+ /** Base URL for the Flare API. */
8
+ endpoint: string;
9
+ /**
10
+ * Optional HTTP base URL for auth API calls.
11
+ * When set, all auth HTTP calls go through this base instead of calling
12
+ * Flare directly. Use this to route calls through a Next.js proxy so CSRF
13
+ * is handled entirely server-side.
14
+ * Example: '/api/flare' (relative, browser resolves against current origin)
15
+ */
16
+ httpBase?: string;
17
+ /**
18
+ * WebSocket path used for realtime transport.
19
+ * Defaults to '/' for backward compatibility.
20
+ */
21
+ wsPath?: string;
22
+ /** Unique identifier for the application. */
23
+ appId: string;
24
+ /** API key for the application. */
25
+ apiKey?: string;
26
+ /**
27
+ * Request content type for credential auth endpoints (/auth/token, /auth/register).
28
+ * Defaults to OAuth-compatible form encoding.
29
+ */
30
+ authRequestContentType?: "application/x-www-form-urlencoded" | "application/json";
31
+ /**
32
+ * Controls how onAuthStateChanged initializes auth in browser runtime.
33
+ * - `refresh` (default): attempt /auth/refresh once in httpBase mode.
34
+ * - `none`: skip automatic refresh bootstrap; listeners receive current in-memory state only.
35
+ */
36
+ authBootstrapMode?: "refresh" | "none";
37
+ /** Public key for the application. */
38
+ publicKey?: string;
39
+ /** Whether to automatically reconnect on connection loss. */
40
+ autoReconnect?: boolean;
41
+ /** Delay between reconnection attempts in milliseconds. */
42
+ reconnectDelay?: number;
43
+ /** Maximum delay between reconnection attempts in milliseconds. */
44
+ maxReconnectDelay?: number;
45
+ /** Enable or disable debug mode. */
46
+ debug?: boolean;
47
+ /** Enable or disable request timing. */
48
+ requestTiming?: boolean;
49
+ /** Connection timeout in milliseconds. */
50
+ connectionTimeout?: number;
51
+ /** Enable automatic push notification registration on supported platforms. */
52
+ pushNotifications?: boolean;
53
+ /**
54
+ * Optional per-collection mapper registry for shaping inbound data.
55
+ *
56
+ * Keys can be:
57
+ * - base collection names (e.g. "boards")
58
+ * - join aliases (`join(..., { as: "team" })` => "team")
59
+ */
60
+ dataMapper?: DataMapperRegistry;
61
+ }
62
+ type DataMapperFn<TRow = any, TMapped = any> = (row: TRow) => TMapped;
63
+ type DataMapperRegistry = Record<string, DataMapperFn<any, any>>;
64
+ type FlareAuthProviderId = "credentials" | "anonymous" | "google" | "facebook" | "github" | "dropbox" | "apple" | "twitter";
65
+ interface FlareAuthProviderPublicConfig {
66
+ enabled: boolean;
67
+ clientId?: string;
68
+ scopes?: string[];
69
+ }
70
+ interface FlareAuthConfig {
71
+ appId: string;
72
+ enabled: boolean;
73
+ needsEmailVerification?: boolean;
74
+ autoSendVerificationEmail?: boolean;
75
+ redirectUri?: string;
76
+ csrfToken?: string;
77
+ cookie?: {
78
+ accessTokenName?: string;
79
+ refreshTokenName?: string;
80
+ csrfTokenName?: string;
81
+ domain?: string;
82
+ path?: string;
83
+ secure?: boolean;
84
+ sameSite?: "Lax" | "Strict" | "None";
85
+ accessTokenMaxAge?: number;
86
+ refreshTokenMaxAge?: number;
87
+ csrfTokenMaxAge?: number;
88
+ };
89
+ providers: Record<FlareAuthProviderId, FlareAuthProviderPublicConfig>;
90
+ }
91
+ interface FlareAuthSession {
92
+ uid: string;
93
+ accessToken: string;
94
+ refreshToken: string | null;
95
+ provider?: string;
96
+ email?: string | null;
97
+ emailVerified?: boolean;
98
+ }
99
+ interface FlareAuthUser {
100
+ uid: string;
101
+ email: string;
102
+ email_verified: string;
103
+ [x: string]: any;
104
+ }
105
+ interface FlareAuthHydrationInput {
106
+ uid?: string | null;
107
+ id?: string | null;
108
+ accessToken?: string | null;
109
+ refreshToken?: string | null;
110
+ ticket?: string | null;
111
+ provider?: string;
112
+ email?: string | null;
113
+ emailVerified?: boolean;
114
+ email_verified?: boolean;
115
+ profile?: Partial<FlareAuthUser> | null;
116
+ }
117
+ interface FlareAuthHydrationOptions {
118
+ source?: string;
119
+ markBootstrapAttempted?: boolean;
120
+ syncSocket?: boolean;
121
+ }
122
+ interface RegisterPushTokenInput {
123
+ token: string;
124
+ platform?: string;
125
+ deviceId?: string;
126
+ topics?: string[];
127
+ authAppId?: string;
128
+ }
129
+ interface BrowserPushTokenOptions {
130
+ /** Service worker registration used for PushManager subscription. */
131
+ serviceWorkerRegistration?: ServiceWorkerRegistration;
132
+ /** Existing PushSubscription to reuse instead of subscribing again. */
133
+ subscription?: PushSubscription;
134
+ /** Public VAPID key used when creating a new PushSubscription. */
135
+ applicationServerKey?: string;
136
+ /** When true, unsubscribe old subscriptions before creating a new one. */
137
+ forceResubscribe?: boolean;
138
+ }
139
+ interface BrowserPushRegistrationOptions extends BrowserPushTokenOptions {
140
+ /** Optional explicit platform label. Defaults to "web". */
141
+ platform?: string;
142
+ deviceId?: string;
143
+ topics?: string[];
144
+ authAppId?: string;
145
+ }
146
+ interface SendPushNotificationInput {
147
+ title?: string;
148
+ body?: string;
149
+ image?: string;
150
+ data?: Record<string, unknown>;
151
+ tokens?: string[];
152
+ uid?: string;
153
+ topic?: string;
154
+ priority?: "normal" | "high";
155
+ ttlSeconds?: number;
156
+ dryRun?: boolean;
157
+ authAppId?: string;
158
+ }
159
+ interface PushSendResult {
160
+ sent: boolean;
161
+ appId: string;
162
+ targetCount: number;
163
+ successCount: number;
164
+ failureCount: number;
165
+ invalidatedTokenCount: number;
166
+ dryRun: boolean;
167
+ }
168
+ interface SendEmailInput {
169
+ to: string | string[];
170
+ tag: string;
171
+ values?: Record<string, unknown>;
172
+ authAppId?: string;
173
+ }
174
+ interface EmailSendResult {
175
+ sent: boolean;
176
+ appId: string;
177
+ tag: string;
178
+ recipientCount: number;
179
+ acceptedCount: number;
180
+ rejectedCount: number;
181
+ includeVerificationLink?: boolean;
182
+ linkId?: string;
183
+ verifyUrl?: string;
184
+ messageId?: string;
185
+ }
186
+ interface VerifyEmailLinkInput {
187
+ token: string;
188
+ tag?: string;
189
+ email?: string;
190
+ authAppId?: string;
191
+ }
192
+ interface EmailLinkVerifyResult {
193
+ verified: boolean;
194
+ alreadyVerified: boolean;
195
+ appId: string;
196
+ linkId: string;
197
+ email: string;
198
+ tag: string;
199
+ verifiedAt?: string;
200
+ acceptedByUid?: string;
201
+ }
202
+ type AuthStateListener = (session: FlareAuthSession & FlareAuthUser | null) => void;
203
+ type AuthConfigListener = (conf: FlareAuthConfig) => void;
204
+ interface SubscribeOptions {
205
+ skipSnapshot?: boolean;
206
+ }
207
+ type QueryOperator = "==" | "!=" | "<" | "<=" | ">" | ">=" | "in" | "not-in" | "array-contains" | "array-contains-any" | "elem-match" | "like" | "not-like" | "contains" | "exists" | "not-exists";
208
+ interface QueryConfig {
209
+ field: string;
210
+ op: QueryOperator;
211
+ value: unknown;
212
+ }
213
+ /** OR group */
214
+ interface OrFilter {
215
+ or: AnyFilter[];
216
+ }
217
+ /** AND group */
218
+ interface AndFilter {
219
+ and: AnyFilter[];
220
+ }
221
+ type AnyFilter = QueryConfig | OrFilter | AndFilter;
222
+ type WhereCondition = Record<string, string | number | boolean | any[]>;
223
+ interface OrderByClause {
224
+ field: string;
225
+ dir?: "asc" | "desc";
226
+ }
227
+ interface GroupByClause {
228
+ fields: string[];
229
+ }
230
+ interface HavingClause {
231
+ field: string;
232
+ op: "==" | "!=" | "<" | "<=" | ">" | ">=";
233
+ value: number;
234
+ }
235
+ interface CursorValue {
236
+ values: unknown[];
237
+ }
238
+ type AggregateFunction = "count" | "sum" | "avg" | "min" | "max" | "distinct";
239
+ interface AggregateSpec {
240
+ fn: AggregateFunction;
241
+ field?: string;
242
+ alias?: string;
243
+ }
244
+ /**
245
+ * Join definition used by CollectionReference.Join().
246
+ *
247
+ * Example:
248
+ * Join("tasks", { source: "id", target: "boardId", as: "tasks" })
249
+ */
250
+ interface JoinQueryPattern {
251
+ where?: AnyFilter[];
252
+ orderBy?: OrderByClause[];
253
+ limit?: number;
254
+ offset?: number;
255
+ startAt?: CursorValue;
256
+ startAfter?: CursorValue;
257
+ endAt?: CursorValue;
258
+ endBefore?: CursorValue;
259
+ aggregate?: AggregateSpec[];
260
+ groupBy?: GroupByClause;
261
+ having?: HavingClause[];
262
+ vectorSearch?: VectorSearchClause;
263
+ select?: string[];
264
+ distinctField?: string;
265
+ }
266
+ interface NestedJoinClause extends JoinQueryPattern {
267
+ /** Joined collection name for this nested join. */
268
+ collection: string;
269
+ /** Field from the parent join result. */
270
+ source: string;
271
+ /** Field from this nested collection to match source. */
272
+ target: string;
273
+ /** Alias where nested rows are attached in each parent join row. */
274
+ as: string;
275
+ /** If true, expect at most one joined row. */
276
+ single?: boolean;
277
+ /** Recursive nested joins. */
278
+ joins?: NestedJoinClause[];
279
+ }
280
+ interface JoinClause extends JoinQueryPattern {
281
+ /** Field from the base collection (the collection you started the query on). */
282
+ source: string;
283
+ /** Field from the joined collection that should match source. */
284
+ target: string;
285
+ /** Alias where joined rows will be attached in each result object. */
286
+ as: string;
287
+ /** If true, expect at most one joined row (object instead of array on server side). */
288
+ single?: boolean;
289
+ /** Optional nested joins under this join. */
290
+ joins?: NestedJoinClause[];
291
+ }
292
+ /** Internal wire-ready join shape sent to server query engine. */
293
+ interface StructuredJoinClause extends JoinQueryPattern {
294
+ from: string;
295
+ localField: string;
296
+ foreignField: string;
297
+ as: string;
298
+ single?: boolean;
299
+ joins?: StructuredJoinClause[];
300
+ }
301
+ interface VectorSearchClause {
302
+ field: string;
303
+ vector: number[];
304
+ k: number;
305
+ metric?: "cosine" | "euclidean" | "dotProduct";
306
+ minScore?: number;
307
+ }
308
+ /** Full structured query (document query + SQL-style feature set) */
309
+ interface StructuredQuery {
310
+ where?: AnyFilter[];
311
+ orderBy?: OrderByClause[];
312
+ limit?: number;
313
+ offset?: number;
314
+ startAt?: CursorValue;
315
+ startAfter?: CursorValue;
316
+ endAt?: CursorValue;
317
+ endBefore?: CursorValue;
318
+ aggregate?: AggregateSpec[];
319
+ groupBy?: GroupByClause;
320
+ having?: HavingClause[];
321
+ joins?: StructuredJoinClause[];
322
+ vectorSearch?: VectorSearchClause;
323
+ select?: string[];
324
+ distinctField?: string;
325
+ }
326
+ type QueryPresetSpec<Params extends Record<string, unknown> = Record<string, unknown>, Row = any> = {
327
+ params: Params;
328
+ row: Row;
329
+ };
330
+ type QueryPresetMap = Record<string, QueryPresetSpec<any, any>>;
331
+ type QueryPresetParams<TSpec> = TSpec extends QueryPresetSpec<infer Params, any> ? Params : Record<string, unknown>;
332
+ type QueryPresetRow<TSpec> = TSpec extends QueryPresetSpec<any, infer Row> ? Row : any;
333
+ type ChangeOperation = 'insert' | 'update' | 'replace' | 'delete';
334
+ /**
335
+ * Fired once when the subscription is first established.
336
+ * `data` is always an array — the full matching collection snapshot.
337
+ */
338
+ interface SnapshotEvent<T = any> {
339
+ type: 'snapshot';
340
+ subscriptionId: string;
341
+ collection: string;
342
+ data: T[];
343
+ }
344
+ /**
345
+ * Fired on every subsequent document mutation that matches the subscription query.
346
+ * `data` is the single affected document (null on delete).
347
+ */
348
+ interface ChangeEvent<T = any> {
349
+ type: 'change';
350
+ subscriptionId: string;
351
+ collection: string;
352
+ docId: string;
353
+ operation: ChangeOperation;
354
+ data: T | null;
355
+ }
356
+ /** Discriminated union — narrow on `event.type` to get the right shape. */
357
+ type SubscriptionData<T = any> = SnapshotEvent<T> | ChangeEvent<T>;
358
+ type SubscriptionCallback<T = any> = (data: SubscriptionData<T>) => void;
359
+ interface SubscriptionError {
360
+ code?: string;
361
+ message: string;
362
+ permissionDenied: boolean;
363
+ raw?: unknown;
364
+ }
365
+ type SubscriptionErrorCallback = (error: SubscriptionError) => void;
366
+ interface SubscriptionHandle {
367
+ (): void;
368
+ unsubscribe: () => void;
369
+ onError: (callback: SubscriptionErrorCallback) => SubscriptionHandle;
370
+ onPermissionDenied: (callback: SubscriptionErrorCallback) => SubscriptionHandle;
371
+ catch: (callback: SubscriptionErrorCallback) => SubscriptionHandle;
372
+ }
373
+ type DocAddedCallback<T = any> = (data: T, docId: string) => void;
374
+ type DocUpdatedCallback<T = any> = (data: T, docId: string) => void;
375
+ type DocDeletedCallback<T = any> = (docId: string) => void;
376
+ type DocChangedCallback<T = any> = (data: T | null, docId: string, operation: ChangeOperation) => void;
377
+ type StreamFlushReason = 'snapshot' | 'change-batch';
378
+ interface CollectionStreamOptions<T = any> {
379
+ /** Delay before a queued burst is flushed to listeners. */
380
+ flushMs?: number;
381
+ /** Flush immediately when queued changes reach this count. */
382
+ maxBatchSize?: number;
383
+ /** Field used to identify docs inside snapshots when getId is not provided. */
384
+ idField?: keyof T & string;
385
+ /** Custom identifier extractor for snapshot rows. */
386
+ getId?: (doc: T) => string | undefined;
387
+ /** Where newly inserted docs should be placed when they were not in snapshot. */
388
+ insertAt?: 'start' | 'end';
389
+ /** Optional cap to keep only the newest N docs in local stream state. */
390
+ maxDocs?: number;
391
+ /** Optional local sort run after flush. */
392
+ sort?: (a: T, b: T) => number;
393
+ }
394
+ interface CollectionStreamMeta {
395
+ reason: StreamFlushReason;
396
+ batchSize: number;
397
+ version: number;
398
+ ready: boolean;
399
+ }
400
+ type CollectionStreamListener<T = any> = (rows: readonly T[], meta: CollectionStreamMeta) => void;
401
+ interface CollectionStream<T = any> {
402
+ /** Subscribe to stream updates (call unsubscribe to stop). */
403
+ subscribe: (listener: CollectionStreamListener<T>, emitCurrent?: boolean) => () => void;
404
+ /** Returns the latest immutable snapshot of rows. */
405
+ getSnapshot: () => readonly T[];
406
+ /** Returns true after the initial snapshot has been received. */
407
+ isReady: () => boolean;
408
+ /** Monotonic version incremented on each flush. */
409
+ getVersion: () => number;
410
+ /** Stop the underlying realtime subscription and cleanup timers/listeners. */
411
+ close: () => void;
412
+ /** Attach subscription-level error handler. */
413
+ onError: (callback: SubscriptionErrorCallback) => CollectionStream<T>;
414
+ /** Attach permission-denied handler. */
415
+ onPermissionDenied: (callback: SubscriptionErrorCallback) => CollectionStream<T>;
416
+ }
417
+ interface CollectionExternalStore<T = any> {
418
+ /** Standard external-store subscribe signature used by UI store hooks. */
419
+ subscribe: (onStoreChange: () => void) => () => void;
420
+ /** Returns current immutable rows snapshot. */
421
+ getSnapshot: () => readonly T[];
422
+ /** Server snapshot fallback for SSR-safe store hooks. */
423
+ getServerSnapshot: () => readonly T[];
424
+ /** Access to underlying realtime stream for advanced handlers. */
425
+ stream: CollectionStream<T>;
426
+ /** Stops realtime stream and detaches listeners. */
427
+ destroy: () => void;
428
+ }
429
+ interface DocumentSnapshot<T = any> {
430
+ id: string;
431
+ data: T | null;
432
+ exists: boolean;
433
+ }
434
+ interface QuerySnapshot<T = any> {
435
+ docs: DocumentSnapshot<T>[];
436
+ size: number;
437
+ empty: boolean;
438
+ }
439
+ interface OfflineOperation {
440
+ id: string;
441
+ type: 'write' | 'delete';
442
+ collection: string;
443
+ docId: string;
444
+ data?: Record<string, unknown>;
445
+ merge?: boolean;
446
+ clientTs: number;
447
+ }
448
+ interface AuthResult {
449
+ uid: string;
450
+ token?: string;
451
+ }
452
+ type ConnectionState = 'connecting' | 'connected' | 'disconnected' | 'reconnecting' | 'error';
453
+ interface AuthWithPendingVerificationResult {
454
+ verificationRequired: true;
455
+ created: true;
456
+ emailSent: boolean;
457
+ preview?: {
458
+ code: string;
459
+ link: string;
460
+ };
461
+ }
462
+ interface AuthWithTokenResult extends AuthResult {
463
+ accessToken: string;
464
+ refreshToken: string | null;
465
+ authToken: AuthToken;
466
+ created: boolean;
467
+ }
468
+ interface PresenceMember {
469
+ uid: string;
470
+ socketId: string;
471
+ room: string;
472
+ meta?: Record<string, unknown>;
473
+ joinedAt: number;
474
+ lastSeen: number;
475
+ }
476
+ type PresenceCallback = (members: PresenceMember[]) => void;
477
+ type PresenceJoinCallback = (member: PresenceMember) => void;
478
+ type PresenceLeaveCallback = (uid: string) => void;
479
+ /** Fields marked as vector will be auto-embedded before write */
480
+ type VectorFieldConfig = {
481
+ /** Dimensions of the vector (e.g. 1536 for OpenAI ada-002) */
482
+ dimensions: number;
483
+ /** Optional custom embedding function; defaults to client-configured embedder */
484
+ embed?: (text: string) => Promise<number[]>;
485
+ };
486
+ type RulePermission = "create" | "read" | "update" | "delete";
487
+ interface FlareRule {
488
+ id: string;
489
+ name: string;
490
+ auth: "any" | "guest" | "auth";
491
+ collection: string;
492
+ document?: string;
493
+ condition?: string;
494
+ permissions: RulePermission[];
495
+ }
496
+ interface SecurityRuleEntry {
497
+ ".read"?: string;
498
+ ".write"?: string;
499
+ ".create"?: string;
500
+ ".update"?: string;
501
+ ".delete"?: string;
502
+ }
503
+ type SecurityRulesMap = Record<string, SecurityRuleEntry>;
504
+ declare const flareRulesToSecurityMap: (rules: FlareRule[]) => SecurityRulesMap;
505
+ declare const securityMapToFlareRules: (rules: SecurityRulesMap) => FlareRule[];
506
+
507
+ /**
508
+ * Parse ORM-style where condition: { age: ">= 25", role: "admin" }
509
+ * Returns array of QueryConfig objects
510
+ */
511
+ declare function parseWhereCondition(condition: WhereCondition): QueryConfig[];
512
+ /**
513
+ * Parse string value to appropriate type
514
+ */
515
+ declare function parseValue(val: string): any;
516
+ /**
517
+ * Query builder for document operations (ORM-style)
518
+ * Supports: doc('users').update({...}).where({ id: 'alice' })
519
+ *
520
+ * This class is thenable - you can await it directly without .execute() or .get()
521
+ * @example
522
+ * await doc('users').where({ id: 'alice' })
523
+ * await doc('users').update({ name: 'Alice' }).where({ id: 'alice' })
524
+ */
525
+ declare class DocumentQueryBuilder<T = any> implements PromiseLike<T | null | void> {
526
+ private client;
527
+ private collection;
528
+ private docIdFromRef?;
529
+ private whereCondition?;
530
+ private updateData?;
531
+ private setData?;
532
+ private deleteOp;
533
+ private promise?;
534
+ constructor(client: FlareClient<any>, collection: string, docIdFromRef?: string | undefined);
535
+ /**
536
+ * Set where condition
537
+ */
538
+ where(condition: WhereCondition): this;
539
+ /**
540
+ * Set update data (for update operations)
541
+ */
542
+ update(data: Partial<T>): this;
543
+ /**
544
+ * Set data (for set operations)
545
+ */
546
+ set(data: Partial<T>): this;
547
+ /**
548
+ * Mark for deletion
549
+ */
550
+ delete(): this;
551
+ /**
552
+ * Get the document ID from doc() reference or where condition.
553
+ */
554
+ private getDocId;
555
+ /**
556
+ * Execute the query
557
+ * @deprecated Use await directly instead of .execute()
558
+ */
559
+ execute(): Promise<T | null | void>;
560
+ /**
561
+ * Internal execute method
562
+ */
563
+ private _execute;
564
+ /**
565
+ * Make this class thenable so it can be awaited directly
566
+ */
567
+ then<TResult1 = T | null | void, TResult2 = never>(onfulfilled?: ((value: T | null | void) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): PromiseLike<TResult1 | TResult2>;
568
+ /**
569
+ * Get the document data once
570
+ */
571
+ get(): Promise<T | null>;
572
+ /**
573
+ * Subscribe to real-time updates
574
+ */
575
+ onSnapshot(callback: SubscriptionCallback<T>): () => void;
576
+ }
577
+
578
+ /** Document reference */
579
+ declare class DocumentReference<T = any> {
580
+ private client;
581
+ readonly collection: string;
582
+ readonly id: string;
583
+ constructor(client: FlareClient<any>, collection: string, id: string);
584
+ get(): Promise<T | null>;
585
+ set(data: Partial<T>): Promise<void>;
586
+ update(data: Partial<T>): Promise<void>;
587
+ delete(): Promise<void>;
588
+ onSnapshot(callback: SubscriptionCallback<T>): () => void;
589
+ /**
590
+ * Fires when this document is updated / replaced.
591
+ */
592
+ onDocUpdated(callback: DocUpdatedCallback<T>): () => void;
593
+ /**
594
+ * Fires when this document is deleted.
595
+ */
596
+ onDocDeleted(callback: DocDeletedCallback<T>): () => void;
597
+ /**
598
+ * Fires on any change to this document (update / delete).
599
+ * `data` is null on deletes.
600
+ */
601
+ onDocChanged(callback: DocChangedCallback<T>): () => void;
602
+ }
603
+
604
+ type CollectionPresetMethods<TPresetMap extends QueryPresetMap> = {
605
+ [K in keyof TPresetMap & string]: (params: QueryPresetParams<TPresetMap[K]>) => CollectionQuery<QueryPresetRow<TPresetMap[K]>, TPresetMap>;
606
+ };
607
+ type CollectionQuery<T = any, TPresetMap extends QueryPresetMap = {}> = CollectionReference<T, TPresetMap> & CollectionPresetMethods<TPresetMap>;
608
+ declare class CollectionReference<T = any, TPresetMap extends QueryPresetMap = {}> implements PromiseLike<T[]> {
609
+ private client;
610
+ readonly collection: string;
611
+ private sq;
612
+ private promise?;
613
+ constructor(client: FlareClient<TPresetMap>, collection: string);
614
+ doc(id: string): DocumentReference<T>;
615
+ private clone;
616
+ private normalizeFilterValue;
617
+ private normalizeFilter;
618
+ private toQueryFilters;
619
+ private appendOperatorFilter;
620
+ private appendAndFilters;
621
+ private toOrNode;
622
+ private toAndNode;
623
+ private isLeafFilter;
624
+ private isIdentityGuard;
625
+ private splitIdentityGuards;
626
+ private appendOrFilters;
627
+ private appendFilters;
628
+ with<Name extends keyof TPresetMap & string>(name: Name, params: QueryPresetParams<TPresetMap[Name]>): CollectionQuery<QueryPresetRow<TPresetMap[Name]>, TPresetMap>;
629
+ with(name: string, params?: Record<string, unknown>): CollectionQuery<T, TPresetMap>;
630
+ /** ORM shorthand: .where({ age: ">= 25", role: "admin" }) */
631
+ where(condition: WhereCondition): CollectionQuery<T, TPresetMap>;
632
+ and(condition: WhereCondition): CollectionQuery<T, TPresetMap>;
633
+ or(condition: WhereCondition): CollectionQuery<T, TPresetMap>;
634
+ in(field: string, values: unknown[] | unknown): CollectionQuery<T, TPresetMap>;
635
+ andIn(field: string, values: unknown[] | unknown): CollectionQuery<T, TPresetMap>;
636
+ orIn(field: string, values: unknown[] | unknown): CollectionQuery<T, TPresetMap>;
637
+ notIn(field: string, values: unknown[] | unknown): CollectionQuery<T, TPresetMap>;
638
+ andNotIn(field: string, values: unknown[] | unknown): CollectionQuery<T, TPresetMap>;
639
+ orNotIn(field: string, values: unknown[] | unknown): CollectionQuery<T, TPresetMap>;
640
+ arrayContains(field: string, value: unknown): CollectionQuery<T, TPresetMap>;
641
+ andArrayContains(field: string, value: unknown): CollectionQuery<T, TPresetMap>;
642
+ orArrayContains(field: string, value: unknown): CollectionQuery<T, TPresetMap>;
643
+ arrayContainsAny(field: string, values: unknown[] | unknown): CollectionQuery<T, TPresetMap>;
644
+ andArrayContainsAny(field: string, values: unknown[] | unknown): CollectionQuery<T, TPresetMap>;
645
+ orArrayContainsAny(field: string, values: unknown[] | unknown): CollectionQuery<T, TPresetMap>;
646
+ some(field: string, condition: Record<string, unknown>): CollectionQuery<T, TPresetMap>;
647
+ andSome(field: string, condition: Record<string, unknown>): CollectionQuery<T, TPresetMap>;
648
+ orSome(field: string, condition: Record<string, unknown>): CollectionQuery<T, TPresetMap>;
649
+ like(field: string, value: string): CollectionQuery<T, TPresetMap>;
650
+ andLike(field: string, value: string): CollectionQuery<T, TPresetMap>;
651
+ orLike(field: string, value: string): CollectionQuery<T, TPresetMap>;
652
+ notLike(field: string, value: string): CollectionQuery<T, TPresetMap>;
653
+ andNotLike(field: string, value: string): CollectionQuery<T, TPresetMap>;
654
+ orNotLike(field: string, value: string): CollectionQuery<T, TPresetMap>;
655
+ exists(field: string): CollectionQuery<T, TPresetMap>;
656
+ andExists(field: string): CollectionQuery<T, TPresetMap>;
657
+ orExists(field: string): CollectionQuery<T, TPresetMap>;
658
+ notExists(field: string): CollectionQuery<T, TPresetMap>;
659
+ andNotExists(field: string): CollectionQuery<T, TPresetMap>;
660
+ orNotExists(field: string): CollectionQuery<T, TPresetMap>;
661
+ /** Get items starting from the most recently created (descending sequence) */
662
+ latest(): CollectionQuery<T, TPresetMap>;
663
+ /** Get items starting from the most recently created (descending sequence) */
664
+ newest(): CollectionQuery<T, TPresetMap>;
665
+ /** Get items starting from the first ever created (ascending sequence) */
666
+ oldest(): CollectionQuery<T, TPresetMap>;
667
+ orderBy(field: string, dir?: "asc" | "desc"): CollectionQuery<T, TPresetMap>;
668
+ limit(n: number): CollectionQuery<T, TPresetMap>;
669
+ offset(n: number): CollectionQuery<T, TPresetMap>;
670
+ startAt(...values: unknown[]): CollectionQuery<T, TPresetMap>;
671
+ startAfter(...values: unknown[]): CollectionQuery<T, TPresetMap>;
672
+ endAt(...values: unknown[]): CollectionQuery<T, TPresetMap>;
673
+ endBefore(...values: unknown[]): CollectionQuery<T, TPresetMap>;
674
+ aggregate(...specs: AggregateSpec[]): CollectionQuery<T, TPresetMap>;
675
+ count(alias?: string): CollectionQuery<T, TPresetMap>;
676
+ sum(field: string, alias?: string): CollectionQuery<T, TPresetMap>;
677
+ avg(field: string, alias?: string): CollectionQuery<T, TPresetMap>;
678
+ min(field: string, alias?: string): CollectionQuery<T, TPresetMap>;
679
+ max(field: string, alias?: string): CollectionQuery<T, TPresetMap>;
680
+ distinct(field: string, alias?: string): CollectionQuery<T, TPresetMap>;
681
+ groupBy(...fields: string[]): CollectionQuery<T, TPresetMap>;
682
+ having(field: string, op: HavingClause['op'], value: number): CollectionQuery<T, TPresetMap>;
683
+ private buildStructuredJoin;
684
+ private cloneStructuredJoin;
685
+ private appendNestedJoinByAlias;
686
+ private parseRelationRef;
687
+ /**
688
+ * Join another collection into this query.
689
+ *
690
+ * @param collectionName Joined collection name.
691
+ * @param j Join mapping clause.
692
+ * @example
693
+ * flare.collection("boards")
694
+ * .join("tasks", { source: "id", target: "boardId", as: "tasks" })
695
+ * .get();
696
+ */
697
+ join(collectionName: string, j: JoinClause): CollectionQuery<T, TPresetMap>;
698
+ /**
699
+ * Append a nested join under an existing join alias.
700
+ * Example:
701
+ * .join("lists", { source: "id", target: "boardId", as: "lists" })
702
+ * .joinNested("lists", "cards", { source: "id", target: "listId", as: "cards" })
703
+ */
704
+ joinNested(parentAlias: string, collectionName: string, j: JoinClause): CollectionQuery<T, TPresetMap>;
705
+ Join(collectionName: string, j: JoinClause): CollectionQuery<T, TPresetMap>;
706
+ JoinNested(parentAlias: string, collectionName: string, j: JoinClause): CollectionQuery<T, TPresetMap>;
707
+ /**
708
+ * SQL-like relation shorthand.
709
+ * Example: .withRelation("team.uid->users.id", { as: "teamMembers" })
710
+ */
711
+ withRelation(relation: string, options?: (Omit<JoinClause, 'source' | 'target' | 'as'> & {
712
+ as?: string;
713
+ })): CollectionQuery<T, TPresetMap>;
714
+ select(...fields: string[]): CollectionQuery<T, TPresetMap>;
715
+ /** Returns unique values for a single field */
716
+ distinctField(field: string): CollectionQuery<T, TPresetMap>;
717
+ /**
718
+ * KNN nearest-neighbour search (requires Atlas vector index).
719
+ * @example
720
+ * col.vectorSearch({ field: "embedding", vector: [...1536 numbers...], k: 10 })
721
+ */
722
+ vectorSearch(opts: VectorSearchClause): CollectionQuery<T, TPresetMap>;
723
+ getRawQuery(): {
724
+ collection: string;
725
+ query: StructuredQuery;
726
+ };
727
+ get(): Promise<T[]>;
728
+ first(): Promise<T | null>;
729
+ last(): Promise<T | null>;
730
+ private _isStructured;
731
+ private _execute;
732
+ private _executeQuery;
733
+ private _executeSubscribe;
734
+ then<TResult1 = T[], TResult2 = never>(onfulfilled?: ((value: T[]) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): PromiseLike<TResult1 | TResult2>;
735
+ /**
736
+ * Subscribe to real-time updates.
737
+ * The full StructuredQuery (including orderBy, where, limit, offset) is sent
738
+ * to the server so the initial snapshot respects all constraints.
739
+ * Individual change events are then sorted / filtered client-side to keep
740
+ * the live result consistent.
741
+ */
742
+ onSnapshot(callback: SubscriptionCallback<T[]>): SubscriptionHandle;
743
+ /**
744
+ * High-throughput stream wrapper for bursty collections (chat, feeds, logs).
745
+ * It keeps local state and flushes change bursts in batches to reduce UI churn.
746
+ */
747
+ stream(options?: CollectionStreamOptions<T>): CollectionStream<T>;
748
+ /**
749
+ * Framework-agnostic external-store bridge.
750
+ * Compatible with UI hooks expecting subscribe/getSnapshot signatures.
751
+ */
752
+ asStore(options?: CollectionStreamOptions<T>): CollectionExternalStore<T>;
753
+ onDocAdded(callback: DocAddedCallback<T>): () => void;
754
+ onDocUpdated(callback: DocUpdatedCallback<T>): () => void;
755
+ onDocDeleted(callback: DocDeletedCallback<T>): () => void;
756
+ onDocChanged(callback: DocChangedCallback<T>): () => void;
757
+ add(data: Partial<T>): Promise<DocumentReference<T>>;
758
+ update(data: Partial<T>): DocumentQueryBuilder<T>;
759
+ delete(): DocumentQueryBuilder<T>;
760
+ }
761
+
762
+ /** Client Request */
763
+ declare enum FlareAction {
764
+ SUBSCRIBE = "subscribe",
765
+ UNSUBSCRIBE = "unsubscribe",
766
+ WRITE = "write",
767
+ DELETE = "delete",
768
+ AUTH = "auth",
769
+ PING = "ping",
770
+ OFFLINE_SYNC = "offline_sync",
771
+ CALL = "call",
772
+ /** One-shot rich query (no real-time subscription) */
773
+ QUERY = "query",
774
+ /** Presence */
775
+ PRESENCE_JOIN = "presence_join",
776
+ PRESENCE_LEAVE = "presence_leave",
777
+ PRESENCE_HEARTBEAT = "presence_heartbeat"
778
+ }
779
+ /** Server Response */
780
+ declare enum FlareEvent {
781
+ SNAPSHOT = "snapshot",
782
+ CHANGE = "change",
783
+ ERROR = "error",
784
+ ACK = "ack",
785
+ PONG = "pong",
786
+ AUTH_OK = "auth_ok",
787
+ OFFLINE_ACK = "offline_ack",
788
+ CALL_RESPONSE = "call_response",
789
+ QUERY_RESULT = "query_result",
790
+ PRESENCE_STATE = "presence_state",
791
+ PRESENCE_JOIN = "presence_join",
792
+ PRESENCE_LEAVE = "presence_leave"
793
+ }
794
+ interface BaseMessage {
795
+ id: string;
796
+ type: FlareAction | FlareEvent;
797
+ ts: number;
798
+ }
799
+ interface SubscribeMessage extends BaseMessage {
800
+ type: FlareAction.SUBSCRIBE;
801
+ collection: string;
802
+ docId?: string;
803
+ query?: Record<string, unknown>;
804
+ skipSnapshot?: boolean;
805
+ resumeToken?: string;
806
+ }
807
+
808
+ type TransportOptions = {
809
+ url: string;
810
+ onMessage: (data: any) => void;
811
+ onOpen?: () => void;
812
+ onClose?: () => void;
813
+ onError?: (error: Error) => void;
814
+ autoReconnect?: boolean;
815
+ reconnectDelay?: number;
816
+ maxReconnectDelay?: number;
817
+ debug?: boolean;
818
+ /** RSA public key (PEM). When set, all outgoing messages are RSA-OAEP encrypted. */
819
+ publicKey?: string;
820
+ };
821
+
822
+ declare class FlareTransport {
823
+ private socket;
824
+ private reconnectInterval;
825
+ private maxReconnectDelay;
826
+ private isConnected;
827
+ private shouldReconnect;
828
+ private options;
829
+ private messageQueue;
830
+ private heartbeatInterval;
831
+ private connectionTimeout;
832
+ constructor(options: TransportOptions);
833
+ connect(): void;
834
+ private handleReconnect;
835
+ private startHeartbeat;
836
+ private stopHeartbeat;
837
+ private flushQueue;
838
+ send(message: object): void;
839
+ disconnect(): void;
840
+ get connected(): boolean;
841
+ private log;
842
+ }
843
+
844
+ type ConnectionListener = (state: ConnectionState) => void;
845
+ type ErrorListener = (error: Error) => void;
846
+ type HttpResponseSnapshot = {
847
+ status: number;
848
+ headers: Record<string, string>;
849
+ data: any;
850
+ };
851
+ type ActiveSubscription = {
852
+ baseId: string;
853
+ liveId: string;
854
+ collection: string;
855
+ docId?: string;
856
+ query?: StructuredQuery;
857
+ callback: SubscriptionCallback;
858
+ options: SubscribeOptions;
859
+ };
860
+ type QueryPresetHandler<Params extends Record<string, unknown> = Record<string, unknown>, Row = any> = (ref: CollectionQuery<any, any>, params: Params) => CollectionQuery<Row, any>;
861
+ /** Embedder function registered by the user */
862
+ type EmbedFn = (text: string) => Promise<number[]>;
863
+ declare class FlareBase<TPresetMap extends QueryPresetMap = {}> {
864
+ protected transport: FlareTransport;
865
+ protected readonly config: FlareConfig;
866
+ protected readonly pendingAcks: Map<string, (value: any) => void>;
867
+ protected readonly subscriptions: Map<string, SubscriptionCallback>;
868
+ protected readonly activeSubscriptions: Map<string, ActiveSubscription>;
869
+ protected readonly queryPresets: Map<string, QueryPresetHandler<any, any>>;
870
+ protected readonly subscriptionErrorHandlers: Map<string, Set<SubscriptionErrorCallback>>;
871
+ protected readonly subscriptionPermissionHandlers: Map<string, Set<SubscriptionErrorCallback>>;
872
+ protected readonly subscriptionLastErrors: Map<string, SubscriptionError>;
873
+ protected readonly offlineQueue: any[];
874
+ protected currentState: ConnectionState;
875
+ protected connectionListeners: ConnectionListener[];
876
+ protected errorListeners: ErrorListener[];
877
+ protected isDebug: boolean;
878
+ protected socketAuthUid: string;
879
+ protected pendingSubscriptionReplay: boolean;
880
+ protected subscriptionReplayPromise: Promise<void>;
881
+ protected requestTraceSeq: number;
882
+ protected requestTimingEnabled: boolean;
883
+ protected httpInFlight: Map<string, Promise<HttpResponseSnapshot>>;
884
+ protected httpResponseCache: Map<string, HttpResponseSnapshot>;
885
+ protected readonly maxHttpCacheEntries = 200;
886
+ protected presenceCallbacks: Map<string, PresenceCallback[]>;
887
+ protected presenceJoinCbs: Map<string, PresenceJoinCallback[]>;
888
+ protected presenceLeaveCbs: Map<string, PresenceLeaveCallback[]>;
889
+ protected presenceHeartbeatTimer?: ReturnType<typeof setInterval>;
890
+ protected embedder?: EmbedFn;
891
+ protected vectorSchema: Map<string, Map<string, VectorFieldConfig>>;
892
+ protected throwFetchFlareError(payload: unknown, fallbackMessage: string, fallbackCode: string): never;
893
+ protected nowMs(): number;
894
+ protected normalizeHeaders(headers?: HeadersInit): Record<string, string>;
895
+ protected redactHeaders(headers: Record<string, string>): Record<string, string>;
896
+ protected stableStringify(value: unknown): string;
897
+ protected buildHttpCacheKey(method: string, url: string, headers: Record<string, string>, body: unknown, credentials?: RequestCredentials): string;
898
+ protected shouldCacheResponse(method: string, url: string): boolean;
899
+ protected rememberHttpResponse(key: string, value: HttpResponseSnapshot): void;
900
+ protected createTimedFetchTrace(snapshot: HttpResponseSnapshot, requestId: number, startedAtMs: number, method: string, url: string, networkMs: number): {
901
+ response: {
902
+ status: number;
903
+ ok: boolean;
904
+ headers: {
905
+ get: (name: string) => string | null;
906
+ };
907
+ json: () => Promise<any>;
908
+ };
909
+ requestId: number;
910
+ startedAtMs: number;
911
+ networkMs: number;
912
+ method: string;
913
+ url: string;
914
+ };
915
+ protected logHttpTiming(...args: any[]): void;
916
+ protected mergeHeaders(base: HeadersInit | undefined, extra: Record<string, string>): HeadersInit;
917
+ protected toWireField(field: string): string;
918
+ protected fromWireField(field: string): string;
919
+ protected normalizeOutboundData(value: unknown): unknown;
920
+ protected normalizeInboundData(value: unknown): unknown;
921
+ private getDataMapper;
922
+ private runMapper;
923
+ private applyJoinAliasMappers;
924
+ mapInboundResult(collection: string, payload: unknown, query?: StructuredQuery): unknown;
925
+ protected normalizeOutboundAnyFilter(filter: Record<string, unknown>): Record<string, unknown>;
926
+ protected normalizeOutboundQuery(query: unknown): unknown;
927
+ protected timedFetch(label: string, input: string, init?: RequestInit): Promise<{
928
+ response: {
929
+ status: number;
930
+ ok: boolean;
931
+ headers: {
932
+ get: (name: string) => string | null;
933
+ };
934
+ json: () => Promise<any>;
935
+ };
936
+ requestId: number;
937
+ startedAtMs: number;
938
+ networkMs: number;
939
+ method: string;
940
+ url: string;
941
+ }>;
942
+ protected parseJsonWithTiming(label: string, trace: {
943
+ requestId: number;
944
+ startedAtMs: number;
945
+ response: {
946
+ status: number;
947
+ ok: boolean;
948
+ headers: {
949
+ get: (name: string) => string | null;
950
+ };
951
+ json: () => Promise<any>;
952
+ };
953
+ networkMs: number;
954
+ method: string;
955
+ url: string;
956
+ }): Promise<any>;
957
+ protected getHttpBase(): string;
958
+ protected log(...args: any[]): void;
959
+ constructor(config: FlareConfig);
960
+ connect(): void;
961
+ disconnect(): void;
962
+ get connectionState(): ConnectionState;
963
+ get isConnected(): boolean;
964
+ onConnectionStateChange(listener: ConnectionListener): () => void;
965
+ onError(callback: ErrorListener): () => void;
966
+ collection<T = any>(name: string): CollectionQuery<T, TPresetMap>;
967
+ registerQueryPreset<Name extends string, Params extends Record<string, unknown>, Row = any>(name: Name, handler: QueryPresetHandler<Params, Row>): this & FlareBase<TPresetMap & Record<Name, QueryPresetSpec<Params, Row>>>;
968
+ registerQueryPresets<TRegistry extends Record<string, QueryPresetHandler<any, any>>>(presets: TRegistry): this & FlareBase<TPresetMap & {
969
+ [K in keyof TRegistry]: TRegistry[K] extends QueryPresetHandler<infer Params, infer Row> ? QueryPresetSpec<Params, Row> : QueryPresetSpec<Record<string, unknown>, any>;
970
+ }>;
971
+ hasQueryPreset(name: string): boolean;
972
+ applyQueryPreset<Name extends keyof TPresetMap & string>(ref: CollectionReference<any, TPresetMap>, name: Name, params: QueryPresetParams<TPresetMap[Name]>): CollectionQuery<QueryPresetRow<TPresetMap[Name]>, TPresetMap>;
973
+ applyQueryPreset<T = any>(ref: CollectionQuery<T, TPresetMap>, name: string, params?: Record<string, unknown>): CollectionQuery<T, TPresetMap>;
974
+ doc<T = any>(collection: string): DocumentQueryBuilder<T>;
975
+ doc<T = any>(collection: string, id: string): DocumentReference<T>;
976
+ ping(): Promise<number>;
977
+ call<T = Record<string, unknown>>(topic: string, payload?: Record<string, unknown>): Promise<T>;
978
+ query<T = Record<string, unknown>>(collection: string, q?: StructuredQuery): Promise<T[]>;
979
+ setEmbedder(fn: EmbedFn): void;
980
+ markVectorField(collection: string, field: string, config?: VectorFieldConfig): void;
981
+ embedVectorFields(collection: string, data: Record<string, unknown>): Promise<Record<string, unknown>>;
982
+ joinPresence(room: string, meta?: Record<string, unknown>): Promise<() => void>;
983
+ leavePresence(room: string): Promise<void>;
984
+ onPresenceState(room: string, cb: PresenceCallback): () => void;
985
+ onPresenceJoin(room: string, cb: PresenceJoinCallback): () => void;
986
+ onPresenceLeave(room: string, cb: PresenceLeaveCallback): () => void;
987
+ private _startPresenceHeartbeat;
988
+ private _stopPresenceHeartbeat;
989
+ syncOffline(): Promise<void>;
990
+ protected beforeActivateSubscription(_entry: ActiveSubscription): Promise<void>;
991
+ protected activateSubscription(entry: ActiveSubscription): Promise<void>;
992
+ protected toSubscriptionError(err: unknown): SubscriptionError;
993
+ protected emitSubscriptionError(baseId: string, error: SubscriptionError): void;
994
+ protected replayActiveSubscriptions(): Promise<void>;
995
+ subscribe(subId: string, collection: string, docId: string | undefined, query: StructuredQuery | undefined, callback: SubscriptionCallback, options?: SubscribeOptions): SubscriptionHandle;
996
+ send(type: FlareAction, payload: any): Promise<any>;
997
+ private handleTransportError;
998
+ protected onConnected(): void;
999
+ protected onDisconnected(): void;
1000
+ protected setState(state: ConnectionState): void;
1001
+ protected handleIncoming(msg: any): void;
1002
+ }
1003
+
1004
+ declare class FlareAuth<TPresetMap extends QueryPresetMap = {}> extends FlareBase<TPresetMap> {
1005
+ static AUTH_TRACE_STORAGE_KEY: string;
1006
+ protected pushServiceWorkerInitPromise?: Promise<ServiceWorkerRegistration | null>;
1007
+ /** Current authentication configuration */
1008
+ protected userId?: string;
1009
+ protected authToken?: string;
1010
+ protected authTicket?: string;
1011
+ protected authConfig?: FlareAuthConfig;
1012
+ protected authStateListeners: AuthStateListener[];
1013
+ protected authConfigListeners: AuthConfigListener[];
1014
+ protected authSession: FlareAuthSession | null;
1015
+ protected currentProfile: FlareAuthUser | undefined;
1016
+ protected authBootstrapAttempted: boolean;
1017
+ protected authBootstrapPromise?: Promise<void>;
1018
+ protected socketAuthSyncPromise?: Promise<void>;
1019
+ /** In-memory CSRF token extracted from the `x-flare-csrf` response header */
1020
+ protected csrfToken?: string;
1021
+ protected csrfBootstrapAttempted: boolean;
1022
+ protected csrfInitPromise?: Promise<void>;
1023
+ protected isAuthTraceEnabled(): boolean;
1024
+ protected traceAuth(event: string, details?: Record<string, unknown>): void;
1025
+ setAuthTrace(enabled: boolean, persist?: boolean): void;
1026
+ protected fetchAuthMe(token?: string): Promise<{
1027
+ id?: string;
1028
+ email?: string | null;
1029
+ email_verified?: boolean;
1030
+ }>;
1031
+ isBootstrapAttempted(): boolean;
1032
+ hydrateAuthState(input: FlareAuthHydrationInput | null, options?: FlareAuthHydrationOptions): Promise<void>;
1033
+ loadAuthConfig(): Promise<FlareAuthConfig>;
1034
+ onAuthConfigLoaded(listener: AuthConfigListener): () => void;
1035
+ onAuthStateChanged(listener: AuthStateListener): () => void;
1036
+ getCurrentUser(): FlareAuthUser | undefined;
1037
+ getAuthTicket(): string | undefined;
1038
+ consumeAuthTicket(): string | undefined;
1039
+ private getDefaultCsrfCookieName;
1040
+ /**
1041
+ * Extract CSRF token from a server response.
1042
+ * The server now sends it ONLY as the `x-flare-csrf` response header
1043
+ * (not in the JSON body). We still support the body field as a fallback
1044
+ * for older server versions.
1045
+ */
1046
+ protected extractCsrfToken(json: unknown, response?: {
1047
+ headers: {
1048
+ get: (name: string) => string | null;
1049
+ };
1050
+ }): string | undefined;
1051
+ getCsrfHeaders(): Record<string, string>;
1052
+ getCsrfCookieName(): string;
1053
+ /**
1054
+ * Read the CSRF token from a browser-readable cookie (set by the server as
1055
+ * a non-HttpOnly fallback) or fall back to the in-memory value captured
1056
+ * from the response header.
1057
+ *
1058
+ * Priority:
1059
+ * 1. Non-HttpOnly cookie on the current domain (browser only)
1060
+ * 2. In-memory value from `x-flare-csrf` response header
1061
+ */
1062
+ getCsrfToken(): string | null;
1063
+ ensureCsrfProtection(): Promise<void>;
1064
+ protected setAuthSession(session: FlareAuthSession | null, source?: string): void;
1065
+ protected emitAuthState(): void;
1066
+ protected setProfile(profile: FlareAuthUser): void;
1067
+ protected ensureSessionForSocketAuth(reason: string): Promise<boolean>;
1068
+ refreshAuthSession(refresh_token?: string): Promise<FlareAuthSession | null>;
1069
+ protected updateSocketIdentity(uid?: string, forceReplay?: boolean): Promise<void>;
1070
+ protected waitUntilConnected(): Promise<void>;
1071
+ protected syncSocketAuth(accessToken?: string | null): Promise<void>;
1072
+ protected beforeActivateSubscription(_entry: any): Promise<void>;
1073
+ /**
1074
+ * Fetch a fresh one-time WebSocket ticket from the server.
1075
+ * Called on reconnect when neither an access token nor a cached ticket exists.
1076
+ * Requires `config.ticketRefreshUrl` to be configured.
1077
+ */
1078
+ protected fetchFreshTicket(): Promise<string | null>;
1079
+ protected onConnected(): void;
1080
+ private toUint8ArrayFromBase64Url;
1081
+ private encodePushTokenFromSubscription;
1082
+ private fetchPushSetupConfig;
1083
+ setupPushServiceWorker(): Promise<ServiceWorkerRegistration | null>;
1084
+ requestPushPermission(): Promise<NotificationPermission>;
1085
+ acquireBrowserPushToken(options?: BrowserPushTokenOptions): Promise<{
1086
+ token: string;
1087
+ subscription: PushSubscription;
1088
+ }>;
1089
+ enableBrowserPush(options?: BrowserPushRegistrationOptions): Promise<{
1090
+ registered: boolean;
1091
+ appId: string;
1092
+ uid: string;
1093
+ token: string;
1094
+ platform?: string;
1095
+ subscription: PushSubscription;
1096
+ }>;
1097
+ registerPushToken(input: RegisterPushTokenInput): Promise<{
1098
+ registered: boolean;
1099
+ appId: string;
1100
+ uid: string;
1101
+ token: string;
1102
+ platform?: string;
1103
+ }>;
1104
+ unregisterPushToken(token: string, authAppId?: string): Promise<{
1105
+ unregistered: boolean;
1106
+ appId: string;
1107
+ token: string;
1108
+ removed: boolean;
1109
+ }>;
1110
+ sendPushNotification(input: SendPushNotificationInput): Promise<PushSendResult>;
1111
+ auth(token: string): Promise<AuthResult>;
1112
+ private getAuthRequestContentType;
1113
+ private buildAuthRequestBody;
1114
+ protected requestEmailPasswordToken(email: string, password: string, scope?: string[]): Promise<AuthToken & {
1115
+ kind: string;
1116
+ }>;
1117
+ signInWithEmailAndPassword(email: string, password: string, options?: {
1118
+ scope?: string[];
1119
+ createIfMissing?: boolean;
1120
+ }): Promise<AuthResult & {
1121
+ kind?: string;
1122
+ accessToken: string;
1123
+ refreshToken: string | null;
1124
+ authToken: AuthToken;
1125
+ created?: boolean;
1126
+ }>;
1127
+ signInWithEmail(email: string, password: string, options?: {
1128
+ scope?: string[];
1129
+ createIfMissing?: boolean;
1130
+ }): Promise<AuthResult & {
1131
+ kind?: string;
1132
+ accessToken: string;
1133
+ refreshToken: string | null;
1134
+ authToken: AuthToken;
1135
+ created?: boolean;
1136
+ }>;
1137
+ createUserWithEmail(email: string, password: string, options?: {
1138
+ scope?: string[];
1139
+ additionalParams?: Record<string, string>;
1140
+ signInIfAllowed?: boolean;
1141
+ }): Promise<{
1142
+ kind?: string;
1143
+ verificationRequired: true;
1144
+ verification_required: true;
1145
+ emailSent: boolean;
1146
+ email_sent: boolean;
1147
+ message?: string;
1148
+ preview?: {
1149
+ code: string;
1150
+ link: string;
1151
+ };
1152
+ } | (AuthResult & {
1153
+ kind?: string;
1154
+ accessToken: string;
1155
+ access_token: string;
1156
+ token_type: string;
1157
+ expires_in: number;
1158
+ refreshToken: string | null;
1159
+ refresh_token: string | null;
1160
+ scope: string;
1161
+ expires_at: string | number;
1162
+ sid: string;
1163
+ authToken: AuthToken;
1164
+ auth_token: AuthToken;
1165
+ verificationRequired: false;
1166
+ verification_required: false;
1167
+ emailSent: boolean;
1168
+ email_sent: boolean;
1169
+ preview?: {
1170
+ code: string;
1171
+ link: string;
1172
+ };
1173
+ })>;
1174
+ createUserWithEmailAndPassword(email: string, password: string, options?: {
1175
+ scope?: string[];
1176
+ additionalParams?: Record<string, string>;
1177
+ signInIfAllowed?: boolean;
1178
+ }): Promise<{
1179
+ kind?: string;
1180
+ verificationRequired: true;
1181
+ verification_required: true;
1182
+ emailSent: boolean;
1183
+ email_sent: boolean;
1184
+ message?: string;
1185
+ preview?: {
1186
+ code: string;
1187
+ link: string;
1188
+ };
1189
+ } | (AuthResult & {
1190
+ kind?: string;
1191
+ accessToken: string;
1192
+ access_token: string;
1193
+ token_type: string;
1194
+ expires_in: number;
1195
+ refreshToken: string | null;
1196
+ refresh_token: string | null;
1197
+ scope: string;
1198
+ expires_at: string | number;
1199
+ sid: string;
1200
+ authToken: AuthToken;
1201
+ auth_token: AuthToken;
1202
+ verificationRequired: false;
1203
+ verification_required: false;
1204
+ emailSent: boolean;
1205
+ email_sent: boolean;
1206
+ preview?: {
1207
+ code: string;
1208
+ link: string;
1209
+ };
1210
+ })>;
1211
+ signInOrCreateWithEmail(email: string, password: string, options?: {
1212
+ scope?: string[];
1213
+ additionalParams?: Record<string, string>;
1214
+ }): Promise<{
1215
+ kind?: string;
1216
+ verificationRequired: true;
1217
+ created: true;
1218
+ emailSent: boolean;
1219
+ preview?: {
1220
+ code: string;
1221
+ link: string;
1222
+ };
1223
+ } | (AuthResult & {
1224
+ accessToken: string;
1225
+ refreshToken: string | null;
1226
+ authToken: AuthToken;
1227
+ created: boolean;
1228
+ })>;
1229
+ signInOrCreateWithEmailAndPassword(email: string, password: string, options?: {
1230
+ scope?: string[];
1231
+ additionalParams?: Record<string, string>;
1232
+ }): Promise<{
1233
+ kind?: string;
1234
+ verificationRequired: true;
1235
+ created: true;
1236
+ emailSent: boolean;
1237
+ preview?: {
1238
+ code: string;
1239
+ link: string;
1240
+ };
1241
+ } | (AuthResult & {
1242
+ accessToken: string;
1243
+ refreshToken: string | null;
1244
+ authToken: AuthToken;
1245
+ created: boolean;
1246
+ })>;
1247
+ sendEmailVerification(email: string): Promise<{
1248
+ sent: boolean;
1249
+ emailSent: boolean;
1250
+ preview?: {
1251
+ code: string;
1252
+ link: string;
1253
+ };
1254
+ }>;
1255
+ verifyEmailWithCode(email: string, code: string): Promise<{
1256
+ verified: boolean;
1257
+ email: string;
1258
+ }>;
1259
+ confirmEmailLink(token: string, email: string): Promise<{
1260
+ verified: boolean;
1261
+ email: string;
1262
+ }>;
1263
+ sendAccountRecovery(email: string): Promise<{
1264
+ kind?: string;
1265
+ sent: boolean;
1266
+ emailSent?: boolean;
1267
+ preview?: {
1268
+ code: string;
1269
+ token: string;
1270
+ };
1271
+ }>;
1272
+ recoverAccountWithCode(email: string, code: string, newPassword: string): Promise<{
1273
+ recovered: boolean;
1274
+ email: string;
1275
+ sessionsRevoked?: number;
1276
+ }>;
1277
+ recoverAccountWithToken(token: string, newPassword: string): Promise<{
1278
+ recovered: boolean;
1279
+ email: string;
1280
+ sessionsRevoked?: number;
1281
+ }>;
1282
+ signOut(): Promise<void>;
1283
+ protected registerWithEmail(email: string, password: string, options?: {
1284
+ scope?: string[];
1285
+ additionalParams?: Record<string, string>;
1286
+ signInIfAllowed?: boolean;
1287
+ }): Promise<Record<string, any>>;
1288
+ }
1289
+
1290
+ /**
1291
+ * Client/index.ts ─ entry point
1292
+ *
1293
+ * FlareClient is the public-facing class. All logic lives in:
1294
+ * - Client/base.ts → transport, subscriptions, presence, vector, offline
1295
+ * - Client/auth.ts → CSRF capture, all auth & session methods
1296
+ *
1297
+ * CSRF Protection (SSR-Only by Default)
1298
+ * ────────────────────────────────────
1299
+ * FlareClient does NOT automatically fetch CSRF on construction. Instead:
1300
+ *
1301
+ * 1. SSR (Next.js): Middleware fetches /auth/config once, sets CSRF as HttpOnly
1302
+ * cookie on response. Methods automatically use this cookie (no extra calls).
1303
+ *
1304
+ * 2. Browser-only (SPA): Explicitly call client.ensureCsrfProtection() before
1305
+ * mutations to fetch /auth/config and cache CSRF token in memory.
1306
+ *
1307
+ * Why? Eliminates redundant /auth/config calls in SSR, where every method used
1308
+ * to fetch it again internally. Now CSRF bootstrapping happens once (in middleware),
1309
+ * and auth methods just use getCsrfHeaders() which returns the cached token
1310
+ * (if available) or empty object (relying on HttpOnly cookie validation).
1311
+ *
1312
+ * This file wires FlareAuth together and leaves CSRF bootstrapping to the user.
1313
+ */
1314
+
1315
+ declare class FlareClient<TPresetMap extends QueryPresetMap = {}> extends FlareAuth<TPresetMap> {
1316
+ private autoPushRegisteredIdentity?;
1317
+ constructor(config: FlareConfig);
1318
+ private enableAutoPushNotificationsAfterAuth;
1319
+ autoEnablePushNotifications(): Promise<void>;
1320
+ }
1321
+
1322
+ export { type OrFilter as $, type AggregateFunction as A, type BaseMessage as B, type ChangeEvent as C, type DataMapperFn as D, type DocUpdatedCallback as E, type FlareConfig as F, DocumentQueryBuilder as G, DocumentReference as H, type DocumentSnapshot as I, type EmailLinkVerifyResult as J, type EmailSendResult as K, FlareAction as L, type FlareAuthConfig as M, type FlareAuthHydrationInput as N, type FlareAuthHydrationOptions as O, type FlareAuthProviderId as P, type FlareAuthProviderPublicConfig as Q, type FlareAuthSession as R, type FlareAuthUser as S, FlareEvent as T, type FlareRule as U, type GroupByClause as V, type HavingClause as W, type JoinClause as X, type JoinQueryPattern as Y, type NestedJoinClause as Z, type OfflineOperation as _, FlareClient as a, type OrderByClause as a0, type PresenceCallback as a1, type PresenceJoinCallback as a2, type PresenceLeaveCallback as a3, type PresenceMember as a4, type PushSendResult as a5, type QueryConfig as a6, type QueryOperator as a7, type QueryPresetMap as a8, type QueryPresetParams as a9, parseWhereCondition as aA, securityMapToFlareRules as aB, type QueryPresetRow as aa, type QueryPresetSpec as ab, type QuerySnapshot as ac, type RegisterPushTokenInput as ad, type RulePermission as ae, type SecurityRuleEntry as af, type SecurityRulesMap as ag, type SendEmailInput as ah, type SendPushNotificationInput as ai, type SnapshotEvent as aj, type StreamFlushReason as ak, type StructuredJoinClause as al, type StructuredQuery as am, type SubscribeMessage as an, type SubscribeOptions as ao, type SubscriptionCallback as ap, type SubscriptionData as aq, type SubscriptionError as ar, type SubscriptionErrorCallback as as, type SubscriptionHandle as at, type VectorFieldConfig as au, type VectorSearchClause as av, type VerifyEmailLinkInput as aw, type WhereCondition as ax, flareRulesToSecurityMap as ay, parseValue as az, type AggregateSpec as b, type AndFilter as c, type AnyFilter as d, type AuthConfigListener as e, type AuthResult as f, type AuthStateListener as g, type AuthWithPendingVerificationResult as h, type AuthWithTokenResult as i, type BrowserPushRegistrationOptions as j, type BrowserPushTokenOptions as k, type ChangeOperation as l, type CollectionExternalStore as m, type CollectionPresetMethods as n, type CollectionQuery as o, CollectionReference as p, type CollectionStream as q, type CollectionStreamListener as r, type CollectionStreamMeta as s, type CollectionStreamOptions as t, type ConnectionState as u, type CursorValue as v, type DataMapperRegistry as w, type DocAddedCallback as x, type DocChangedCallback as y, type DocDeletedCallback as z };