@zuzjs/flare 0.2.1 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +3 -3
- package/dist/index.d.cts +1041 -164
- package/dist/index.d.ts +1041 -164
- package/dist/index.js +2 -2
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,80 +1,251 @@
|
|
|
1
|
-
|
|
1
|
+
import { AuthToken, AuthGuard, ProviderId } from '@zuzjs/auth';
|
|
2
|
+
export { Anonymous, Apple, AuthGuard, AuthToken, CreateUserWithEmailAndPasswordInput, Credentials, Dropbox, Facebook, GitHub, Google, NormalizedProfile, OAuthProvider, ProviderId, Providers, SignInAnonymouslyInput, SignInWithEmailAndPasswordInput, Twitter, setupProvider } from '@zuzjs/auth';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Client Configuration
|
|
5
6
|
*/
|
|
6
7
|
interface FlareConfig {
|
|
7
|
-
/** WebSocket endpoint URL (e.g., 'http://localhost:5050' or 'wss://api.example.com') */
|
|
8
8
|
endpoint: string;
|
|
9
|
-
/** Application/Project ID */
|
|
10
|
-
appId: string;
|
|
11
|
-
/** API Key for authentication (optional) */
|
|
12
|
-
apiKey?: string;
|
|
13
9
|
/**
|
|
14
|
-
*
|
|
15
|
-
* When
|
|
16
|
-
*
|
|
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)
|
|
17
15
|
*/
|
|
16
|
+
httpBase?: string;
|
|
17
|
+
appId: string;
|
|
18
|
+
apiKey?: string;
|
|
18
19
|
publicKey?: string;
|
|
19
|
-
/** Enable automatic reconnection (default: true) */
|
|
20
20
|
autoReconnect?: boolean;
|
|
21
|
-
/** Initial reconnect delay in seconds (default: 2) */
|
|
22
21
|
reconnectDelay?: number;
|
|
23
|
-
/** Maximum reconnect delay in seconds (default: 60) */
|
|
24
22
|
maxReconnectDelay?: number;
|
|
25
|
-
/** Enable debug logging (default: false) */
|
|
26
23
|
debug?: boolean;
|
|
27
|
-
|
|
24
|
+
requestTiming?: boolean;
|
|
28
25
|
connectionTimeout?: number;
|
|
29
26
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
type FlareAuthProviderId = "credentials" | "anonymous" | "google" | "facebook" | "github" | "dropbox" | "apple" | "twitter";
|
|
28
|
+
interface FlareAuthProviderPublicConfig {
|
|
29
|
+
enabled: boolean;
|
|
30
|
+
clientId?: string;
|
|
31
|
+
scopes?: string[];
|
|
32
|
+
}
|
|
33
|
+
interface FlareAuthConfig {
|
|
34
|
+
appId: string;
|
|
35
|
+
enabled: boolean;
|
|
36
|
+
needsEmailVerification?: boolean;
|
|
37
|
+
autoSendVerificationEmail?: boolean;
|
|
38
|
+
redirectUri?: string;
|
|
39
|
+
csrfToken?: string;
|
|
40
|
+
cookie?: {
|
|
41
|
+
accessTokenName?: string;
|
|
42
|
+
refreshTokenName?: string;
|
|
43
|
+
csrfTokenName?: string;
|
|
44
|
+
domain?: string;
|
|
45
|
+
path?: string;
|
|
46
|
+
secure?: boolean;
|
|
47
|
+
sameSite?: "Lax" | "Strict" | "None";
|
|
48
|
+
accessTokenMaxAge?: number;
|
|
49
|
+
refreshTokenMaxAge?: number;
|
|
50
|
+
csrfTokenMaxAge?: number;
|
|
51
|
+
};
|
|
52
|
+
providers: Record<FlareAuthProviderId, FlareAuthProviderPublicConfig>;
|
|
53
|
+
}
|
|
54
|
+
interface FlareAuthSession {
|
|
55
|
+
uid: string;
|
|
56
|
+
accessToken: string;
|
|
57
|
+
refreshToken: string | null;
|
|
58
|
+
provider?: string;
|
|
59
|
+
email?: string | null;
|
|
60
|
+
emailVerified?: boolean;
|
|
61
|
+
}
|
|
62
|
+
interface FlareAuthUser {
|
|
63
|
+
uid: string;
|
|
64
|
+
email: string;
|
|
65
|
+
email_verified: string;
|
|
66
|
+
[x: string]: any;
|
|
67
|
+
}
|
|
68
|
+
type AuthStateListener = (session: FlareAuthSession & FlareAuthUser | null) => void;
|
|
69
|
+
type AuthConfigListener = (conf: FlareAuthConfig) => void;
|
|
70
|
+
interface SubscribeOptions {
|
|
71
|
+
skipSnapshot?: boolean;
|
|
72
|
+
}
|
|
73
|
+
type QueryOperator = "==" | "!=" | "<" | "<=" | ">" | ">=" | "in" | "not-in" | "array-contains" | "array-contains-any" | "like" | "not-like" | "contains" | "exists" | "not-exists";
|
|
33
74
|
interface QueryConfig {
|
|
34
75
|
field: string;
|
|
35
76
|
op: QueryOperator;
|
|
36
77
|
value: unknown;
|
|
37
78
|
}
|
|
38
|
-
/**
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
79
|
+
/** OR group */
|
|
80
|
+
interface OrFilter {
|
|
81
|
+
or: QueryConfig[];
|
|
82
|
+
}
|
|
83
|
+
type AnyFilter = QueryConfig | OrFilter;
|
|
42
84
|
type WhereCondition = Record<string, string | number | boolean | any[]>;
|
|
43
|
-
|
|
85
|
+
interface OrderByClause {
|
|
86
|
+
field: string;
|
|
87
|
+
dir?: "asc" | "desc";
|
|
88
|
+
}
|
|
89
|
+
interface GroupByClause {
|
|
90
|
+
fields: string[];
|
|
91
|
+
}
|
|
92
|
+
interface HavingClause {
|
|
93
|
+
field: string;
|
|
94
|
+
op: "==" | "!=" | "<" | "<=" | ">" | ">=";
|
|
95
|
+
value: number;
|
|
96
|
+
}
|
|
97
|
+
interface CursorValue {
|
|
98
|
+
values: unknown[];
|
|
99
|
+
}
|
|
100
|
+
type AggregateFunction = "count" | "sum" | "avg" | "min" | "max" | "distinct";
|
|
101
|
+
interface AggregateSpec {
|
|
102
|
+
fn: AggregateFunction;
|
|
103
|
+
field?: string;
|
|
104
|
+
alias?: string;
|
|
105
|
+
}
|
|
44
106
|
/**
|
|
45
|
-
*
|
|
107
|
+
* Join definition used by CollectionReference.Join().
|
|
108
|
+
*
|
|
109
|
+
* Example:
|
|
110
|
+
* Join("tasks", { source: "id", target: "boardId", as: "tasks" })
|
|
46
111
|
*/
|
|
47
|
-
|
|
112
|
+
interface JoinQueryPattern {
|
|
113
|
+
where?: AnyFilter[];
|
|
114
|
+
orderBy?: OrderByClause[];
|
|
115
|
+
limit?: number;
|
|
116
|
+
offset?: number;
|
|
117
|
+
startAt?: CursorValue;
|
|
118
|
+
startAfter?: CursorValue;
|
|
119
|
+
endAt?: CursorValue;
|
|
120
|
+
endBefore?: CursorValue;
|
|
121
|
+
aggregate?: AggregateSpec[];
|
|
122
|
+
groupBy?: GroupByClause;
|
|
123
|
+
having?: HavingClause[];
|
|
124
|
+
vectorSearch?: VectorSearchClause;
|
|
125
|
+
select?: string[];
|
|
126
|
+
distinctField?: string;
|
|
127
|
+
}
|
|
128
|
+
interface NestedJoinClause extends JoinQueryPattern {
|
|
129
|
+
/** Joined collection name for this nested join. */
|
|
130
|
+
collection: string;
|
|
131
|
+
/** Field from the parent join result. */
|
|
132
|
+
source: string;
|
|
133
|
+
/** Field from this nested collection to match source. */
|
|
134
|
+
target: string;
|
|
135
|
+
/** Alias where nested rows are attached in each parent join row. */
|
|
136
|
+
as: string;
|
|
137
|
+
/** If true, expect at most one joined row. */
|
|
138
|
+
single?: boolean;
|
|
139
|
+
/** Recursive nested joins. */
|
|
140
|
+
joins?: NestedJoinClause[];
|
|
141
|
+
}
|
|
142
|
+
interface JoinClause extends JoinQueryPattern {
|
|
143
|
+
/** Field from the base collection (the collection you started the query on). */
|
|
144
|
+
source: string;
|
|
145
|
+
/** Field from the joined collection that should match source. */
|
|
146
|
+
target: string;
|
|
147
|
+
/** Alias where joined rows will be attached in each result object. */
|
|
148
|
+
as: string;
|
|
149
|
+
/** If true, expect at most one joined row (object instead of array on server side). */
|
|
150
|
+
single?: boolean;
|
|
151
|
+
/** Optional nested joins under this join. */
|
|
152
|
+
joins?: NestedJoinClause[];
|
|
153
|
+
}
|
|
154
|
+
/** Internal wire-ready join shape sent to server query engine. */
|
|
155
|
+
interface StructuredJoinClause extends JoinQueryPattern {
|
|
156
|
+
from: string;
|
|
157
|
+
localField: string;
|
|
158
|
+
foreignField: string;
|
|
159
|
+
as: string;
|
|
160
|
+
single?: boolean;
|
|
161
|
+
joins?: StructuredJoinClause[];
|
|
162
|
+
}
|
|
163
|
+
interface VectorSearchClause {
|
|
164
|
+
field: string;
|
|
165
|
+
vector: number[];
|
|
166
|
+
k: number;
|
|
167
|
+
metric?: "cosine" | "euclidean" | "dotProduct";
|
|
168
|
+
minScore?: number;
|
|
169
|
+
}
|
|
170
|
+
/** Full structured query (Firestore + SQL feature set) */
|
|
171
|
+
interface StructuredQuery {
|
|
172
|
+
where?: AnyFilter[];
|
|
173
|
+
orderBy?: OrderByClause[];
|
|
174
|
+
limit?: number;
|
|
175
|
+
offset?: number;
|
|
176
|
+
startAt?: CursorValue;
|
|
177
|
+
startAfter?: CursorValue;
|
|
178
|
+
endAt?: CursorValue;
|
|
179
|
+
endBefore?: CursorValue;
|
|
180
|
+
aggregate?: AggregateSpec[];
|
|
181
|
+
groupBy?: GroupByClause;
|
|
182
|
+
having?: HavingClause[];
|
|
183
|
+
joins?: StructuredJoinClause[];
|
|
184
|
+
vectorSearch?: VectorSearchClause;
|
|
185
|
+
select?: string[];
|
|
186
|
+
distinctField?: string;
|
|
187
|
+
}
|
|
188
|
+
type QueryPresetSpec<Params extends Record<string, unknown> = Record<string, unknown>, Row = any> = {
|
|
189
|
+
params: Params;
|
|
190
|
+
row: Row;
|
|
191
|
+
};
|
|
192
|
+
type QueryPresetMap = Record<string, QueryPresetSpec<any, any>>;
|
|
193
|
+
type QueryPresetParams<TSpec> = TSpec extends QueryPresetSpec<infer Params, any> ? Params : Record<string, unknown>;
|
|
194
|
+
type QueryPresetRow<TSpec> = TSpec extends QueryPresetSpec<any, infer Row> ? Row : any;
|
|
195
|
+
type ChangeOperation = 'insert' | 'update' | 'replace' | 'delete';
|
|
48
196
|
/**
|
|
49
|
-
*
|
|
197
|
+
* Fired once when the subscription is first established.
|
|
198
|
+
* `data` is always an array — the full matching collection snapshot.
|
|
50
199
|
*/
|
|
51
|
-
interface
|
|
200
|
+
interface SnapshotEvent<T = any> {
|
|
201
|
+
type: 'snapshot';
|
|
52
202
|
subscriptionId: string;
|
|
53
203
|
collection: string;
|
|
54
|
-
|
|
55
|
-
data: T | T[];
|
|
56
|
-
type: 'snapshot' | 'change';
|
|
57
|
-
operation?: 'insert' | 'update' | 'delete' | 'replace';
|
|
204
|
+
data: T[];
|
|
58
205
|
}
|
|
59
206
|
/**
|
|
60
|
-
*
|
|
207
|
+
* Fired on every subsequent document mutation that matches the subscription query.
|
|
208
|
+
* `data` is the single affected document (null on delete).
|
|
61
209
|
*/
|
|
210
|
+
interface ChangeEvent<T = any> {
|
|
211
|
+
type: 'change';
|
|
212
|
+
subscriptionId: string;
|
|
213
|
+
collection: string;
|
|
214
|
+
docId: string;
|
|
215
|
+
operation: ChangeOperation;
|
|
216
|
+
data: T | null;
|
|
217
|
+
}
|
|
218
|
+
/** Discriminated union — narrow on `event.type` to get the right shape. */
|
|
219
|
+
type SubscriptionData<T = any> = SnapshotEvent<T> | ChangeEvent<T>;
|
|
220
|
+
type SubscriptionCallback<T = any> = (data: SubscriptionData<T>) => void;
|
|
221
|
+
interface SubscriptionError {
|
|
222
|
+
code?: string;
|
|
223
|
+
message: string;
|
|
224
|
+
permissionDenied: boolean;
|
|
225
|
+
raw?: unknown;
|
|
226
|
+
}
|
|
227
|
+
type SubscriptionErrorCallback = (error: SubscriptionError) => void;
|
|
228
|
+
interface SubscriptionHandle {
|
|
229
|
+
(): void;
|
|
230
|
+
unsubscribe: () => void;
|
|
231
|
+
onError: (callback: SubscriptionErrorCallback) => SubscriptionHandle;
|
|
232
|
+
onPermissionDenied: (callback: SubscriptionErrorCallback) => SubscriptionHandle;
|
|
233
|
+
catch: (callback: SubscriptionErrorCallback) => SubscriptionHandle;
|
|
234
|
+
}
|
|
235
|
+
type DocAddedCallback<T = any> = (data: T, docId: string) => void;
|
|
236
|
+
type DocUpdatedCallback<T = any> = (data: T, docId: string) => void;
|
|
237
|
+
type DocDeletedCallback<T = any> = (docId: string) => void;
|
|
238
|
+
type DocChangedCallback<T = any> = (data: T | null, docId: string, operation: ChangeOperation) => void;
|
|
62
239
|
interface DocumentSnapshot<T = any> {
|
|
63
240
|
id: string;
|
|
64
241
|
data: T | null;
|
|
65
242
|
exists: boolean;
|
|
66
243
|
}
|
|
67
|
-
/**
|
|
68
|
-
* Collection query result
|
|
69
|
-
*/
|
|
70
244
|
interface QuerySnapshot<T = any> {
|
|
71
245
|
docs: DocumentSnapshot<T>[];
|
|
72
246
|
size: number;
|
|
73
247
|
empty: boolean;
|
|
74
248
|
}
|
|
75
|
-
/**
|
|
76
|
-
* Offline operation
|
|
77
|
-
*/
|
|
78
249
|
interface OfflineOperation {
|
|
79
250
|
id: string;
|
|
80
251
|
type: 'write' | 'delete';
|
|
@@ -84,18 +255,74 @@ interface OfflineOperation {
|
|
|
84
255
|
merge?: boolean;
|
|
85
256
|
clientTs: number;
|
|
86
257
|
}
|
|
87
|
-
/**
|
|
88
|
-
* Authentication result
|
|
89
|
-
*/
|
|
90
258
|
interface AuthResult {
|
|
91
259
|
uid: string;
|
|
92
260
|
token?: string;
|
|
93
261
|
}
|
|
94
|
-
/**
|
|
95
|
-
* Connection state
|
|
96
|
-
*/
|
|
97
262
|
type ConnectionState = 'connecting' | 'connected' | 'disconnected' | 'reconnecting' | 'error';
|
|
263
|
+
interface AuthWithPendingVerificationResult {
|
|
264
|
+
verificationRequired: true;
|
|
265
|
+
created: true;
|
|
266
|
+
emailSent: boolean;
|
|
267
|
+
preview?: {
|
|
268
|
+
code: string;
|
|
269
|
+
link: string;
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
interface AuthWithTokenResult extends AuthResult {
|
|
273
|
+
accessToken: string;
|
|
274
|
+
refreshToken: string | null;
|
|
275
|
+
authToken: AuthToken;
|
|
276
|
+
created: boolean;
|
|
277
|
+
}
|
|
278
|
+
interface PresenceMember {
|
|
279
|
+
uid: string;
|
|
280
|
+
socketId: string;
|
|
281
|
+
room: string;
|
|
282
|
+
meta?: Record<string, unknown>;
|
|
283
|
+
joinedAt: number;
|
|
284
|
+
lastSeen: number;
|
|
285
|
+
}
|
|
286
|
+
type PresenceCallback = (members: PresenceMember[]) => void;
|
|
287
|
+
type PresenceJoinCallback = (member: PresenceMember) => void;
|
|
288
|
+
type PresenceLeaveCallback = (uid: string) => void;
|
|
289
|
+
/** Fields marked as vector will be auto-embedded before write */
|
|
290
|
+
type VectorFieldConfig = {
|
|
291
|
+
/** Dimensions of the vector (e.g. 1536 for OpenAI ada-002) */
|
|
292
|
+
dimensions: number;
|
|
293
|
+
/** Optional custom embedding function; defaults to client-configured embedder */
|
|
294
|
+
embed?: (text: string) => Promise<number[]>;
|
|
295
|
+
};
|
|
296
|
+
type RulePermission = "create" | "read" | "update" | "delete";
|
|
297
|
+
interface FlareRule {
|
|
298
|
+
id: string;
|
|
299
|
+
name: string;
|
|
300
|
+
auth: "any" | "guest" | "auth";
|
|
301
|
+
collection: string;
|
|
302
|
+
document?: string;
|
|
303
|
+
condition?: string;
|
|
304
|
+
permissions: RulePermission[];
|
|
305
|
+
}
|
|
306
|
+
interface SecurityRuleEntry {
|
|
307
|
+
".read"?: string;
|
|
308
|
+
".write"?: string;
|
|
309
|
+
".create"?: string;
|
|
310
|
+
".update"?: string;
|
|
311
|
+
".delete"?: string;
|
|
312
|
+
}
|
|
313
|
+
type SecurityRulesMap = Record<string, SecurityRuleEntry>;
|
|
314
|
+
declare const flareRulesToSecurityMap: (rules: FlareRule[]) => SecurityRulesMap;
|
|
315
|
+
declare const securityMapToFlareRules: (rules: SecurityRulesMap) => FlareRule[];
|
|
98
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Parse ORM-style where condition: { age: ">= 25", role: "admin" }
|
|
319
|
+
* Returns array of QueryConfig objects
|
|
320
|
+
*/
|
|
321
|
+
declare function parseWhereCondition(condition: WhereCondition): QueryConfig[];
|
|
322
|
+
/**
|
|
323
|
+
* Parse string value to appropriate type
|
|
324
|
+
*/
|
|
325
|
+
declare function parseValue(val: string): any;
|
|
99
326
|
/**
|
|
100
327
|
* Query builder for document operations (ORM-style)
|
|
101
328
|
* Supports: doc('users').update({...}).where({ id: 'alice' })
|
|
@@ -114,7 +341,7 @@ declare class DocumentQueryBuilder<T = any> implements PromiseLike<T | null | vo
|
|
|
114
341
|
private setData?;
|
|
115
342
|
private deleteOp;
|
|
116
343
|
private promise?;
|
|
117
|
-
constructor(client: FlareClient
|
|
344
|
+
constructor(client: FlareClient<any>, collection: string, legacyId?: string | undefined);
|
|
118
345
|
/**
|
|
119
346
|
* Set where condition
|
|
120
347
|
*/
|
|
@@ -157,6 +384,7 @@ declare class DocumentQueryBuilder<T = any> implements PromiseLike<T | null | vo
|
|
|
157
384
|
*/
|
|
158
385
|
onSnapshot(callback: SubscriptionCallback<T>): () => void;
|
|
159
386
|
}
|
|
387
|
+
|
|
160
388
|
/**
|
|
161
389
|
* Legacy document reference (for backward compatibility)
|
|
162
390
|
*/
|
|
@@ -164,63 +392,124 @@ declare class DocumentReference<T = any> {
|
|
|
164
392
|
private client;
|
|
165
393
|
readonly collection: string;
|
|
166
394
|
readonly id: string;
|
|
167
|
-
constructor(client: FlareClient
|
|
395
|
+
constructor(client: FlareClient<any>, collection: string, id: string);
|
|
168
396
|
get(): Promise<T | null>;
|
|
169
397
|
set(data: Partial<T>): Promise<void>;
|
|
170
398
|
update(data: Partial<T>): Promise<void>;
|
|
171
399
|
delete(): Promise<void>;
|
|
172
400
|
onSnapshot(callback: SubscriptionCallback<T>): () => void;
|
|
401
|
+
/**
|
|
402
|
+
* Fires when this document is updated / replaced.
|
|
403
|
+
* Aliases: onDocModified, onDocChange
|
|
404
|
+
*/
|
|
405
|
+
onDocUpdated(callback: DocUpdatedCallback<T>): () => void;
|
|
406
|
+
/** Alias for onDocUpdated */
|
|
407
|
+
onDocModified(callback: DocUpdatedCallback<T>): () => void;
|
|
408
|
+
/** Alias for onDocUpdated */
|
|
409
|
+
onDocChange(callback: DocUpdatedCallback<T>): () => void;
|
|
410
|
+
/**
|
|
411
|
+
* Fires when this document is deleted.
|
|
412
|
+
*/
|
|
413
|
+
onDocDeleted(callback: DocDeletedCallback<T>): () => void;
|
|
414
|
+
/**
|
|
415
|
+
* Fires on any change to this document (update / delete).
|
|
416
|
+
* `data` is null on deletes.
|
|
417
|
+
*/
|
|
418
|
+
onDocChanged(callback: DocChangedCallback<T>): () => void;
|
|
173
419
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
*/
|
|
181
|
-
declare class CollectionReference<T = any> implements PromiseLike<T[]> {
|
|
420
|
+
|
|
421
|
+
type CollectionPresetMethods<TPresetMap extends QueryPresetMap> = {
|
|
422
|
+
[K in keyof TPresetMap & string]: (params: QueryPresetParams<TPresetMap[K]>) => CollectionQuery<QueryPresetRow<TPresetMap[K]>, TPresetMap>;
|
|
423
|
+
};
|
|
424
|
+
type CollectionQuery<T = any, TPresetMap extends QueryPresetMap = {}> = CollectionReference<T, TPresetMap> & CollectionPresetMethods<TPresetMap>;
|
|
425
|
+
declare class CollectionReference<T = any, TPresetMap extends QueryPresetMap = {}> implements PromiseLike<T[]> {
|
|
182
426
|
private client;
|
|
183
427
|
readonly collection: string;
|
|
184
|
-
private
|
|
428
|
+
private sq;
|
|
185
429
|
private promise?;
|
|
186
|
-
constructor(client: FlareClient
|
|
187
|
-
/**
|
|
188
|
-
* Get a document reference (legacy API)
|
|
189
|
-
*/
|
|
430
|
+
constructor(client: FlareClient<TPresetMap>, collection: string);
|
|
190
431
|
doc(id: string): DocumentReference<T>;
|
|
432
|
+
private clone;
|
|
433
|
+
with<Name extends keyof TPresetMap & string>(name: Name, params: QueryPresetParams<TPresetMap[Name]>): CollectionQuery<QueryPresetRow<TPresetMap[Name]>, TPresetMap>;
|
|
434
|
+
with(name: string, params?: Record<string, unknown>): CollectionQuery<T, TPresetMap>;
|
|
435
|
+
/** ORM shorthand: .where({ age: ">= 25", role: "admin" }) */
|
|
436
|
+
where(condition: WhereCondition): CollectionQuery<T, TPresetMap>;
|
|
437
|
+
/** Explicit field/op/value */
|
|
438
|
+
where(field: string, op: QueryConfig['op'], value: unknown): CollectionQuery<T, TPresetMap>;
|
|
439
|
+
/** OR group: .orWhere([{ field:"status", op:"==", value:"active" }, ...]) */
|
|
440
|
+
orWhere(filters: QueryConfig[]): CollectionQuery<T, TPresetMap>;
|
|
441
|
+
/** Get items starting from the most recently created (descending sequence) */
|
|
442
|
+
latest(): CollectionQuery<T, TPresetMap>;
|
|
443
|
+
/** Get items starting from the first ever created (ascending sequence) */
|
|
444
|
+
oldest(): CollectionQuery<T, TPresetMap>;
|
|
445
|
+
orderBy(field: string, dir?: "asc" | "desc"): CollectionQuery<T, TPresetMap>;
|
|
446
|
+
limit(n: number): CollectionQuery<T, TPresetMap>;
|
|
447
|
+
offset(n: number): CollectionQuery<T, TPresetMap>;
|
|
448
|
+
startAt(...values: unknown[]): CollectionQuery<T, TPresetMap>;
|
|
449
|
+
startAfter(...values: unknown[]): CollectionQuery<T, TPresetMap>;
|
|
450
|
+
endAt(...values: unknown[]): CollectionQuery<T, TPresetMap>;
|
|
451
|
+
endBefore(...values: unknown[]): CollectionQuery<T, TPresetMap>;
|
|
452
|
+
aggregate(...specs: AggregateSpec[]): CollectionQuery<T, TPresetMap>;
|
|
453
|
+
count(alias?: string): CollectionQuery<T, TPresetMap>;
|
|
454
|
+
sum(field: string, alias?: string): CollectionQuery<T, TPresetMap>;
|
|
455
|
+
avg(field: string, alias?: string): CollectionQuery<T, TPresetMap>;
|
|
456
|
+
min(field: string, alias?: string): CollectionQuery<T, TPresetMap>;
|
|
457
|
+
max(field: string, alias?: string): CollectionQuery<T, TPresetMap>;
|
|
458
|
+
distinct(field: string, alias?: string): CollectionQuery<T, TPresetMap>;
|
|
459
|
+
groupBy(...fields: string[]): CollectionQuery<T, TPresetMap>;
|
|
460
|
+
having(field: string, op: HavingClause['op'], value: number): CollectionQuery<T, TPresetMap>;
|
|
461
|
+
private buildStructuredJoin;
|
|
191
462
|
/**
|
|
192
|
-
*
|
|
193
|
-
*
|
|
463
|
+
* Join another collection into this query.
|
|
464
|
+
*
|
|
465
|
+
* @param collectionName Joined collection name.
|
|
466
|
+
* @param j Join mapping clause.
|
|
467
|
+
* @example
|
|
468
|
+
* flare.collection("boards")
|
|
469
|
+
* .Join("tasks", { source: "id", target: "boardId", as: "tasks" })
|
|
470
|
+
* .get();
|
|
194
471
|
*/
|
|
195
|
-
|
|
472
|
+
Join(collectionName: string, j: JoinClause): CollectionQuery<T, TPresetMap>;
|
|
196
473
|
/**
|
|
197
|
-
*
|
|
198
|
-
*
|
|
474
|
+
* Legacy join signature.
|
|
475
|
+
* Prefer Join(collectionName, clause) for better readability.
|
|
199
476
|
*/
|
|
200
|
-
|
|
477
|
+
join(collectionName: string, j: JoinClause): CollectionQuery<T, TPresetMap>;
|
|
478
|
+
join(j: JoinClause & {
|
|
479
|
+
from?: string;
|
|
480
|
+
collection?: string;
|
|
481
|
+
}): CollectionQuery<T, TPresetMap>;
|
|
482
|
+
select(...fields: string[]): CollectionQuery<T, TPresetMap>;
|
|
483
|
+
/** Returns unique values for a single field */
|
|
484
|
+
distinctField(field: string): CollectionQuery<T, TPresetMap>;
|
|
201
485
|
/**
|
|
202
|
-
*
|
|
486
|
+
* KNN nearest-neighbour search (requires Atlas vector index).
|
|
487
|
+
* @example
|
|
488
|
+
* col.vectorSearch({ field: "embedding", vector: [...1536 numbers...], k: 10 })
|
|
203
489
|
*/
|
|
490
|
+
vectorSearch(opts: VectorSearchClause): CollectionQuery<T, TPresetMap>;
|
|
491
|
+
get(): Promise<T[]>;
|
|
492
|
+
private _isStructured;
|
|
204
493
|
private _execute;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
*/
|
|
494
|
+
private _executeQuery;
|
|
495
|
+
private _executeSubscribe;
|
|
208
496
|
then<TResult1 = T[], TResult2 = never>(onfulfilled?: ((value: T[]) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): PromiseLike<TResult1 | TResult2>;
|
|
209
497
|
/**
|
|
210
|
-
* Subscribe to real-time updates
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
*
|
|
498
|
+
* Subscribe to real-time updates.
|
|
499
|
+
* The full StructuredQuery (including orderBy, where, limit, offset) is sent
|
|
500
|
+
* to the server so the initial snapshot respects all constraints.
|
|
501
|
+
* Individual change events are then sorted / filtered client-side to keep
|
|
502
|
+
* the live result consistent.
|
|
215
503
|
*/
|
|
504
|
+
onSnapshot(callback: SubscriptionCallback<T[]>): SubscriptionHandle;
|
|
505
|
+
onDocAdded(callback: DocAddedCallback<T>): () => void;
|
|
506
|
+
onDocUpdated(callback: DocUpdatedCallback<T>): () => void;
|
|
507
|
+
onDocModified(callback: DocUpdatedCallback<T>): () => void;
|
|
508
|
+
onDocChange(callback: DocUpdatedCallback<T>): () => void;
|
|
509
|
+
onDocDeleted(callback: DocDeletedCallback<T>): () => void;
|
|
510
|
+
onDocChanged(callback: DocChangedCallback<T>): () => void;
|
|
216
511
|
add(data: Partial<T>): Promise<DocumentReference<T>>;
|
|
217
|
-
/**
|
|
218
|
-
* Update documents matching the where condition
|
|
219
|
-
*/
|
|
220
512
|
update(data: Partial<T>): DocumentQueryBuilder<T>;
|
|
221
|
-
/**
|
|
222
|
-
* Delete documents matching the where condition
|
|
223
|
-
*/
|
|
224
513
|
delete(): DocumentQueryBuilder<T>;
|
|
225
514
|
}
|
|
226
515
|
|
|
@@ -233,8 +522,13 @@ declare enum FlareAction {
|
|
|
233
522
|
AUTH = "auth",
|
|
234
523
|
PING = "ping",
|
|
235
524
|
OFFLINE_SYNC = "offline_sync",
|
|
236
|
-
|
|
237
|
-
|
|
525
|
+
CALL = "call",
|
|
526
|
+
/** One-shot rich query (no real-time subscription) */
|
|
527
|
+
QUERY = "query",
|
|
528
|
+
/** Presence */
|
|
529
|
+
PRESENCE_JOIN = "presence_join",
|
|
530
|
+
PRESENCE_LEAVE = "presence_leave",
|
|
531
|
+
PRESENCE_HEARTBEAT = "presence_heartbeat"
|
|
238
532
|
}
|
|
239
533
|
/** Server Response */
|
|
240
534
|
declare enum FlareEvent {
|
|
@@ -245,116 +539,640 @@ declare enum FlareEvent {
|
|
|
245
539
|
PONG = "pong",
|
|
246
540
|
AUTH_OK = "auth_ok",
|
|
247
541
|
OFFLINE_ACK = "offline_ack",
|
|
248
|
-
|
|
249
|
-
|
|
542
|
+
CALL_RESPONSE = "call_response",
|
|
543
|
+
QUERY_RESULT = "query_result",
|
|
544
|
+
PRESENCE_STATE = "presence_state",
|
|
545
|
+
PRESENCE_JOIN = "presence_join",
|
|
546
|
+
PRESENCE_LEAVE = "presence_leave"
|
|
250
547
|
}
|
|
251
548
|
interface BaseMessage {
|
|
252
549
|
id: string;
|
|
253
550
|
type: FlareAction | FlareEvent;
|
|
254
551
|
ts: number;
|
|
255
552
|
}
|
|
553
|
+
interface SubscribeMessage extends BaseMessage {
|
|
554
|
+
type: FlareAction.SUBSCRIBE;
|
|
555
|
+
collection: string;
|
|
556
|
+
docId?: string;
|
|
557
|
+
query?: Record<string, unknown>;
|
|
558
|
+
skipSnapshot?: boolean;
|
|
559
|
+
resumeToken?: string;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
type TransportOptions = {
|
|
563
|
+
url: string;
|
|
564
|
+
onMessage: (data: any) => void;
|
|
565
|
+
onOpen?: () => void;
|
|
566
|
+
onClose?: () => void;
|
|
567
|
+
onError?: (error: Error) => void;
|
|
568
|
+
autoReconnect?: boolean;
|
|
569
|
+
reconnectDelay?: number;
|
|
570
|
+
maxReconnectDelay?: number;
|
|
571
|
+
debug?: boolean;
|
|
572
|
+
/** RSA public key (PEM). When set, all outgoing messages are RSA-OAEP encrypted. */
|
|
573
|
+
publicKey?: string;
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
declare class FlareTransport {
|
|
577
|
+
private socket;
|
|
578
|
+
private reconnectInterval;
|
|
579
|
+
private maxReconnectDelay;
|
|
580
|
+
private isConnected;
|
|
581
|
+
private shouldReconnect;
|
|
582
|
+
private options;
|
|
583
|
+
private messageQueue;
|
|
584
|
+
private heartbeatInterval;
|
|
585
|
+
private connectionTimeout;
|
|
586
|
+
constructor(options: TransportOptions);
|
|
587
|
+
connect(): void;
|
|
588
|
+
private handleReconnect;
|
|
589
|
+
private startHeartbeat;
|
|
590
|
+
private stopHeartbeat;
|
|
591
|
+
private flushQueue;
|
|
592
|
+
send(message: object): void;
|
|
593
|
+
disconnect(): void;
|
|
594
|
+
get connected(): boolean;
|
|
595
|
+
private log;
|
|
596
|
+
}
|
|
256
597
|
|
|
257
598
|
type ConnectionListener = (state: ConnectionState) => void;
|
|
258
599
|
type ErrorListener = (error: Error) => void;
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
600
|
+
type HttpResponseSnapshot = {
|
|
601
|
+
status: number;
|
|
602
|
+
headers: Record<string, string>;
|
|
603
|
+
data: any;
|
|
604
|
+
};
|
|
605
|
+
type ActiveSubscription = {
|
|
606
|
+
baseId: string;
|
|
607
|
+
liveId: string;
|
|
608
|
+
collection: string;
|
|
609
|
+
docId?: string;
|
|
610
|
+
query?: QueryConfig | StructuredQuery;
|
|
611
|
+
callback: SubscriptionCallback;
|
|
612
|
+
options: SubscribeOptions;
|
|
613
|
+
};
|
|
614
|
+
type QueryPresetHandler<Params extends Record<string, unknown> = Record<string, unknown>, Row = any> = (ref: CollectionQuery<any, any>, params: Params) => CollectionQuery<Row, any>;
|
|
615
|
+
/** Embedder function registered by the user */
|
|
616
|
+
type EmbedFn = (text: string) => Promise<number[]>;
|
|
617
|
+
declare class FlareBase<TPresetMap extends QueryPresetMap = {}> {
|
|
618
|
+
protected transport: FlareTransport;
|
|
619
|
+
protected readonly config: FlareConfig;
|
|
620
|
+
protected readonly pendingAcks: Map<string, (value: any) => void>;
|
|
621
|
+
protected readonly subscriptions: Map<string, SubscriptionCallback>;
|
|
622
|
+
protected readonly activeSubscriptions: Map<string, ActiveSubscription>;
|
|
623
|
+
protected readonly queryPresets: Map<string, QueryPresetHandler<any, any>>;
|
|
624
|
+
protected readonly subscriptionErrorHandlers: Map<string, Set<SubscriptionErrorCallback>>;
|
|
625
|
+
protected readonly subscriptionPermissionHandlers: Map<string, Set<SubscriptionErrorCallback>>;
|
|
626
|
+
protected readonly subscriptionLastErrors: Map<string, SubscriptionError>;
|
|
627
|
+
protected readonly offlineQueue: any[];
|
|
628
|
+
protected currentState: ConnectionState;
|
|
629
|
+
protected connectionListeners: ConnectionListener[];
|
|
630
|
+
protected errorListeners: ErrorListener[];
|
|
631
|
+
protected isDebug: boolean;
|
|
632
|
+
protected socketAuthUid: string;
|
|
633
|
+
protected pendingSubscriptionReplay: boolean;
|
|
634
|
+
protected subscriptionReplayPromise: Promise<void>;
|
|
635
|
+
protected requestTraceSeq: number;
|
|
636
|
+
protected requestTimingEnabled: boolean;
|
|
637
|
+
protected httpInFlight: Map<string, Promise<HttpResponseSnapshot>>;
|
|
638
|
+
protected httpResponseCache: Map<string, HttpResponseSnapshot>;
|
|
639
|
+
protected readonly maxHttpCacheEntries = 200;
|
|
640
|
+
protected presenceCallbacks: Map<string, PresenceCallback[]>;
|
|
641
|
+
protected presenceJoinCbs: Map<string, PresenceJoinCallback[]>;
|
|
642
|
+
protected presenceLeaveCbs: Map<string, PresenceLeaveCallback[]>;
|
|
643
|
+
protected presenceHeartbeatTimer?: ReturnType<typeof setInterval>;
|
|
644
|
+
protected embedder?: EmbedFn;
|
|
645
|
+
protected vectorSchema: Map<string, Map<string, VectorFieldConfig>>;
|
|
646
|
+
protected throwFetchFlareError(payload: unknown, fallbackMessage: string, fallbackCode: string): never;
|
|
647
|
+
protected nowMs(): number;
|
|
648
|
+
protected normalizeHeaders(headers?: HeadersInit): Record<string, string>;
|
|
649
|
+
protected redactHeaders(headers: Record<string, string>): Record<string, string>;
|
|
650
|
+
protected stableStringify(value: unknown): string;
|
|
651
|
+
protected buildHttpCacheKey(method: string, url: string, headers: Record<string, string>, body: unknown, credentials?: RequestCredentials): string;
|
|
652
|
+
protected shouldCacheResponse(method: string, url: string): boolean;
|
|
653
|
+
protected rememberHttpResponse(key: string, value: HttpResponseSnapshot): void;
|
|
654
|
+
protected createTimedFetchTrace(snapshot: HttpResponseSnapshot, requestId: number, startedAtMs: number, method: string, url: string, networkMs: number): {
|
|
655
|
+
response: {
|
|
656
|
+
status: number;
|
|
657
|
+
ok: boolean;
|
|
658
|
+
headers: {
|
|
659
|
+
get: (name: string) => string | null;
|
|
660
|
+
};
|
|
661
|
+
json: () => Promise<any>;
|
|
662
|
+
};
|
|
663
|
+
requestId: number;
|
|
664
|
+
startedAtMs: number;
|
|
665
|
+
networkMs: number;
|
|
666
|
+
method: string;
|
|
667
|
+
url: string;
|
|
668
|
+
};
|
|
669
|
+
protected logHttpTiming(...args: any[]): void;
|
|
670
|
+
protected mergeHeaders(base: HeadersInit | undefined, extra: Record<string, string>): HeadersInit;
|
|
671
|
+
protected toWireField(field: string): string;
|
|
672
|
+
protected fromWireField(field: string): string;
|
|
673
|
+
protected normalizeOutboundData(value: unknown): unknown;
|
|
674
|
+
protected normalizeInboundData(value: unknown): unknown;
|
|
675
|
+
protected normalizeOutboundAnyFilter(filter: Record<string, unknown>): Record<string, unknown>;
|
|
676
|
+
protected normalizeOutboundQuery(query: unknown): unknown;
|
|
677
|
+
protected timedFetch(label: string, input: string, init?: RequestInit): Promise<{
|
|
678
|
+
response: {
|
|
679
|
+
status: number;
|
|
680
|
+
ok: boolean;
|
|
681
|
+
headers: {
|
|
682
|
+
get: (name: string) => string | null;
|
|
683
|
+
};
|
|
684
|
+
json: () => Promise<any>;
|
|
685
|
+
};
|
|
686
|
+
requestId: number;
|
|
687
|
+
startedAtMs: number;
|
|
688
|
+
networkMs: number;
|
|
689
|
+
method: string;
|
|
690
|
+
url: string;
|
|
691
|
+
}>;
|
|
692
|
+
protected parseJsonWithTiming(label: string, trace: {
|
|
693
|
+
requestId: number;
|
|
694
|
+
startedAtMs: number;
|
|
695
|
+
response: {
|
|
696
|
+
status: number;
|
|
697
|
+
ok: boolean;
|
|
698
|
+
headers: {
|
|
699
|
+
get: (name: string) => string | null;
|
|
700
|
+
};
|
|
701
|
+
json: () => Promise<any>;
|
|
702
|
+
};
|
|
703
|
+
networkMs: number;
|
|
704
|
+
method: string;
|
|
705
|
+
url: string;
|
|
706
|
+
}): Promise<any>;
|
|
707
|
+
protected getHttpBase(): string;
|
|
708
|
+
protected log(...args: any[]): void;
|
|
271
709
|
constructor(config: FlareConfig);
|
|
272
|
-
/**
|
|
273
|
-
* Connect to the server
|
|
274
|
-
*/
|
|
275
710
|
connect(): void;
|
|
276
|
-
/**
|
|
277
|
-
* Disconnect from the server
|
|
278
|
-
*/
|
|
279
711
|
disconnect(): void;
|
|
280
|
-
/**
|
|
281
|
-
* Get a collection reference
|
|
282
|
-
*/
|
|
283
|
-
collection<T = any>(name: string): CollectionReference<T>;
|
|
284
|
-
/**
|
|
285
|
-
* Get a document query builder (NEW ORM-style API)
|
|
286
|
-
* @example flare.doc('users').update({...}).where({ id: 'alice' })
|
|
287
|
-
*/
|
|
288
|
-
doc<T = any>(collection: string): DocumentQueryBuilder<T>;
|
|
289
|
-
/**
|
|
290
|
-
* Get a document reference (Legacy API - deprecated)
|
|
291
|
-
* @deprecated Use doc(collection).where({ id: '...' }) instead
|
|
292
|
-
*/
|
|
293
|
-
doc<T = any>(collection: string, id: string): DocumentReference<T>;
|
|
294
|
-
/**
|
|
295
|
-
* Authenticate with a token
|
|
296
|
-
*/
|
|
297
|
-
auth(token: string): Promise<AuthResult>;
|
|
298
|
-
/**
|
|
299
|
-
* Sign out
|
|
300
|
-
*/
|
|
301
|
-
signOut(): void;
|
|
302
|
-
/**
|
|
303
|
-
* Get current user ID
|
|
304
|
-
*/
|
|
305
|
-
get currentUser(): string | undefined;
|
|
306
|
-
/**
|
|
307
|
-
* Get connection state
|
|
308
|
-
*/
|
|
309
712
|
get connectionState(): ConnectionState;
|
|
310
|
-
/**
|
|
311
|
-
* Check if connected
|
|
312
|
-
*/
|
|
313
713
|
get isConnected(): boolean;
|
|
314
|
-
/**
|
|
315
|
-
* Listen to connection state changes
|
|
316
|
-
*/
|
|
317
714
|
onConnectionStateChange(listener: ConnectionListener): () => void;
|
|
715
|
+
onError(callback: ErrorListener): () => void;
|
|
716
|
+
collection<T = any>(name: string): CollectionQuery<T, TPresetMap>;
|
|
717
|
+
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>>>;
|
|
718
|
+
registerQueryPresets<TRegistry extends Record<string, QueryPresetHandler<any, any>>>(presets: TRegistry): this & FlareBase<TPresetMap & {
|
|
719
|
+
[K in keyof TRegistry]: TRegistry[K] extends QueryPresetHandler<infer Params, infer Row> ? QueryPresetSpec<Params, Row> : QueryPresetSpec<Record<string, unknown>, any>;
|
|
720
|
+
}>;
|
|
721
|
+
hasQueryPreset(name: string): boolean;
|
|
722
|
+
applyQueryPreset<Name extends keyof TPresetMap & string>(ref: CollectionReference<any, TPresetMap>, name: Name, params: QueryPresetParams<TPresetMap[Name]>): CollectionQuery<QueryPresetRow<TPresetMap[Name]>, TPresetMap>;
|
|
723
|
+
applyQueryPreset<T = any>(ref: CollectionQuery<T, TPresetMap>, name: string, params?: Record<string, unknown>): CollectionQuery<T, TPresetMap>;
|
|
724
|
+
doc<T = any>(collection: string): DocumentQueryBuilder<T>;
|
|
725
|
+
doc<T = any>(collection: string, id: string): DocumentReference<T>;
|
|
726
|
+
ping(): Promise<number>;
|
|
727
|
+
call<T = Record<string, unknown>>(topic: string, payload?: Record<string, unknown>): Promise<T>;
|
|
728
|
+
query<T = Record<string, unknown>>(collection: string, q?: StructuredQuery): Promise<T[]>;
|
|
729
|
+
setEmbedder(fn: EmbedFn): void;
|
|
730
|
+
markVectorField(collection: string, field: string, config?: VectorFieldConfig): void;
|
|
731
|
+
embedVectorFields(collection: string, data: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
732
|
+
joinPresence(room: string, meta?: Record<string, unknown>): Promise<() => void>;
|
|
733
|
+
leavePresence(room: string): Promise<void>;
|
|
734
|
+
onPresenceState(room: string, cb: PresenceCallback): () => void;
|
|
735
|
+
onPresenceJoin(room: string, cb: PresenceJoinCallback): () => void;
|
|
736
|
+
onPresenceLeave(room: string, cb: PresenceLeaveCallback): () => void;
|
|
737
|
+
private _startPresenceHeartbeat;
|
|
738
|
+
private _stopPresenceHeartbeat;
|
|
739
|
+
syncOffline(): Promise<void>;
|
|
740
|
+
protected beforeActivateSubscription(_entry: ActiveSubscription): Promise<void>;
|
|
741
|
+
protected activateSubscription(entry: ActiveSubscription): Promise<void>;
|
|
742
|
+
protected toSubscriptionError(err: unknown): SubscriptionError;
|
|
743
|
+
protected emitSubscriptionError(baseId: string, error: SubscriptionError): void;
|
|
744
|
+
protected replayActiveSubscriptions(): Promise<void>;
|
|
745
|
+
subscribe(subId: string, collection: string, docId: string | undefined, query: QueryConfig | StructuredQuery | undefined, callback: SubscriptionCallback, options?: SubscribeOptions): SubscriptionHandle;
|
|
746
|
+
send(type: FlareAction, payload: any): Promise<any>;
|
|
747
|
+
private handleTransportError;
|
|
748
|
+
protected onConnected(): void;
|
|
749
|
+
protected onDisconnected(): void;
|
|
750
|
+
protected setState(state: ConnectionState): void;
|
|
751
|
+
protected handleIncoming(msg: any): void;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* FlareAuth extends FlareBase with all authentication and CSRF logic.
|
|
756
|
+
*
|
|
757
|
+
* CSRF strategy
|
|
758
|
+
* ─────────────
|
|
759
|
+
* The server sets an HttpOnly cookie on /auth/config responses. The raw
|
|
760
|
+
* cookie value is NOT readable by JS (HttpOnly), but the server also echoes
|
|
761
|
+
* the token in the `x-flare-csrf` response header so the client can send
|
|
762
|
+
* it back as `x-flare-csrf` on every mutating request.
|
|
763
|
+
*
|
|
764
|
+
* Two environments are supported:
|
|
765
|
+
* 1. Browser – header is read from the /auth/config fetch; stored in
|
|
766
|
+
* `this.csrfToken` (in-memory only, never written to a
|
|
767
|
+
* readable cookie by the client).
|
|
768
|
+
* 2. Next.js SSR – use the standalone `createCsrfProxy()` handler
|
|
769
|
+
* (see Client/proxy.ts) which captures the Set-Cookie header
|
|
770
|
+
* from Flare server and sets it on the client domain too, so
|
|
771
|
+
* the browser owns two HttpOnly cookies: one from the Flare
|
|
772
|
+
* domain and one from the Next.js domain.
|
|
773
|
+
*/
|
|
774
|
+
declare class FlareAuth<TPresetMap extends QueryPresetMap = {}> extends FlareBase<TPresetMap> {
|
|
775
|
+
protected authToken?: string;
|
|
776
|
+
protected userId?: string;
|
|
777
|
+
protected authGuard?: AuthGuard;
|
|
778
|
+
protected authConfig?: FlareAuthConfig;
|
|
779
|
+
/** In-memory CSRF token extracted from the `x-flare-csrf` response header */
|
|
780
|
+
protected csrfToken?: string;
|
|
781
|
+
protected csrfInitPromise?: Promise<void>;
|
|
782
|
+
protected csrfBootstrapAttempted: boolean;
|
|
783
|
+
protected socketAuthSyncPromise?: Promise<void>;
|
|
784
|
+
protected authSession: FlareAuthSession | null;
|
|
785
|
+
protected authStateListeners: AuthStateListener[];
|
|
786
|
+
protected authConfigListeners: AuthConfigListener[];
|
|
787
|
+
protected currentProfile: FlareAuthUser | undefined;
|
|
788
|
+
private getDefaultCsrfCookieName;
|
|
789
|
+
getCsrfCookieName(): string;
|
|
318
790
|
/**
|
|
319
|
-
*
|
|
791
|
+
* Read the CSRF token from a browser-readable cookie (set by the server as
|
|
792
|
+
* a non-HttpOnly fallback) or fall back to the in-memory value captured
|
|
793
|
+
* from the response header.
|
|
794
|
+
*
|
|
795
|
+
* Priority:
|
|
796
|
+
* 1. Non-HttpOnly cookie on the current domain (browser only)
|
|
797
|
+
* 2. In-memory value from `x-flare-csrf` response header
|
|
320
798
|
*/
|
|
321
|
-
|
|
799
|
+
getCsrfToken(): string | null;
|
|
800
|
+
private getCookieValue;
|
|
322
801
|
/**
|
|
323
|
-
*
|
|
802
|
+
* Extract CSRF token from a server response.
|
|
803
|
+
* The server now sends it ONLY as the `x-flare-csrf` response header
|
|
804
|
+
* (not in the JSON body). We still support the body field as a fallback
|
|
805
|
+
* for older server versions.
|
|
324
806
|
*/
|
|
325
|
-
|
|
807
|
+
protected extractCsrfToken(json: unknown, response?: {
|
|
808
|
+
headers: {
|
|
809
|
+
get: (name: string) => string | null;
|
|
810
|
+
};
|
|
811
|
+
}): string | undefined;
|
|
812
|
+
getCsrfHeaders(): Record<string, string>;
|
|
326
813
|
/**
|
|
327
|
-
*
|
|
814
|
+
* Inject CSRF token directly (used by SSR middleware).
|
|
815
|
+
* This allows the server to bootstrap CSRF once and inject it into the client,
|
|
816
|
+
* preventing redundant /auth/config calls.
|
|
328
817
|
*
|
|
329
818
|
* @example
|
|
330
|
-
*
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
* @returns The object returned by the server handler.
|
|
336
|
-
* @throws If the server returns `success: false` or the request times out.
|
|
337
|
-
*/
|
|
338
|
-
call<T = Record<string, unknown>>(topic: string, payload?: Record<string, unknown>): Promise<T>;
|
|
339
|
-
/**
|
|
340
|
-
* Sync offline operations
|
|
819
|
+
* // In Next.js middleware
|
|
820
|
+
* const csrf = extractCsrfFromRequest(request, appId);
|
|
821
|
+
* if (csrf) {
|
|
822
|
+
* flareClient.setCsrfToken(csrf);
|
|
823
|
+
* }
|
|
341
824
|
*/
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
825
|
+
setCsrfToken(token: string): void;
|
|
826
|
+
ensureCsrfProtection(): Promise<void>;
|
|
827
|
+
loadAuthConfig(): Promise<FlareAuthConfig>;
|
|
828
|
+
protected fetchAuthConfig(): Promise<FlareAuthConfig>;
|
|
829
|
+
onAuthConfigLoaded(listener: AuthConfigListener): () => void;
|
|
830
|
+
protected setProfile(profile: FlareAuthUser): void;
|
|
831
|
+
protected setAuthSession(session: FlareAuthSession | null): void;
|
|
832
|
+
onAuthStateChanged(listener: AuthStateListener): () => void;
|
|
833
|
+
onAuthStateChange(listener: AuthStateListener): () => void;
|
|
834
|
+
get currentUser(): FlareAuthUser | undefined;
|
|
835
|
+
getCurrentUser(): FlareAuthUser | undefined;
|
|
836
|
+
protected syncSocketAuth(accessToken?: string | null): Promise<void>;
|
|
837
|
+
protected updateSocketIdentity(uid?: string, forceReplay?: boolean): Promise<void>;
|
|
838
|
+
protected beforeActivateSubscription(_entry: any): Promise<void>;
|
|
839
|
+
protected onConnected(): void;
|
|
840
|
+
protected handleIncoming(msg: any): void;
|
|
841
|
+
auth(token: string): Promise<AuthResult>;
|
|
842
|
+
signInWithEmailAndPassword(email: string, password: string, options?: {
|
|
843
|
+
scope?: string[];
|
|
844
|
+
createIfMissing?: boolean;
|
|
845
|
+
}): Promise<AuthResult & {
|
|
846
|
+
kind?: string;
|
|
847
|
+
accessToken: string;
|
|
848
|
+
refreshToken: string | null;
|
|
849
|
+
authToken: AuthToken;
|
|
850
|
+
created?: boolean;
|
|
851
|
+
}>;
|
|
852
|
+
signInWithEmail(email: string, password: string, options?: {
|
|
853
|
+
scope?: string[];
|
|
854
|
+
createIfMissing?: boolean;
|
|
855
|
+
}): Promise<AuthResult & {
|
|
856
|
+
kind?: string;
|
|
857
|
+
accessToken: string;
|
|
858
|
+
refreshToken: string | null;
|
|
859
|
+
authToken: AuthToken;
|
|
860
|
+
created?: boolean;
|
|
861
|
+
}>;
|
|
862
|
+
createUserWithEmail(email: string, password: string, options?: {
|
|
863
|
+
scope?: string[];
|
|
864
|
+
additionalParams?: Record<string, string>;
|
|
865
|
+
signInIfAllowed?: boolean;
|
|
866
|
+
}): Promise<{
|
|
867
|
+
kind?: string;
|
|
868
|
+
verificationRequired: true;
|
|
869
|
+
emailSent: boolean;
|
|
870
|
+
preview?: {
|
|
871
|
+
code: string;
|
|
872
|
+
link: string;
|
|
873
|
+
};
|
|
874
|
+
} | (AuthResult & {
|
|
875
|
+
kind?: string;
|
|
876
|
+
accessToken: string;
|
|
877
|
+
refreshToken: string | null;
|
|
878
|
+
authToken: AuthToken;
|
|
879
|
+
verificationRequired?: false;
|
|
880
|
+
emailSent?: boolean;
|
|
881
|
+
preview?: {
|
|
882
|
+
code: string;
|
|
883
|
+
link: string;
|
|
884
|
+
};
|
|
885
|
+
})>;
|
|
886
|
+
createUserWithEmailAndPassword(email: string, password: string, options?: {
|
|
887
|
+
scope?: string[];
|
|
888
|
+
additionalParams?: Record<string, string>;
|
|
889
|
+
signInIfAllowed?: boolean;
|
|
890
|
+
}): Promise<{
|
|
891
|
+
kind?: string;
|
|
892
|
+
verificationRequired: true;
|
|
893
|
+
emailSent: boolean;
|
|
894
|
+
preview?: {
|
|
895
|
+
code: string;
|
|
896
|
+
link: string;
|
|
897
|
+
};
|
|
898
|
+
} | (AuthResult & {
|
|
899
|
+
kind?: string;
|
|
900
|
+
accessToken: string;
|
|
901
|
+
refreshToken: string | null;
|
|
902
|
+
authToken: AuthToken;
|
|
903
|
+
verificationRequired?: false;
|
|
904
|
+
emailSent?: boolean;
|
|
905
|
+
preview?: {
|
|
906
|
+
code: string;
|
|
907
|
+
link: string;
|
|
908
|
+
};
|
|
909
|
+
})>;
|
|
910
|
+
signInOrCreateWithEmail(email: string, password: string, options?: {
|
|
911
|
+
scope?: string[];
|
|
912
|
+
additionalParams?: Record<string, string>;
|
|
913
|
+
}): Promise<{
|
|
914
|
+
kind?: string;
|
|
915
|
+
verificationRequired: true;
|
|
916
|
+
created: true;
|
|
917
|
+
emailSent: boolean;
|
|
918
|
+
preview?: {
|
|
919
|
+
code: string;
|
|
920
|
+
link: string;
|
|
921
|
+
};
|
|
922
|
+
} | (AuthResult & {
|
|
923
|
+
accessToken: string;
|
|
924
|
+
refreshToken: string | null;
|
|
925
|
+
authToken: AuthToken;
|
|
926
|
+
created: boolean;
|
|
927
|
+
})>;
|
|
928
|
+
signInOrCreateWithEmailAndPassword(email: string, password: string, options?: {
|
|
929
|
+
scope?: string[];
|
|
930
|
+
additionalParams?: Record<string, string>;
|
|
931
|
+
}): Promise<{
|
|
932
|
+
kind?: string;
|
|
933
|
+
verificationRequired: true;
|
|
934
|
+
created: true;
|
|
935
|
+
emailSent: boolean;
|
|
936
|
+
preview?: {
|
|
937
|
+
code: string;
|
|
938
|
+
link: string;
|
|
939
|
+
};
|
|
940
|
+
} | (AuthResult & {
|
|
941
|
+
accessToken: string;
|
|
942
|
+
refreshToken: string | null;
|
|
943
|
+
authToken: AuthToken;
|
|
944
|
+
created: boolean;
|
|
945
|
+
})>;
|
|
946
|
+
sendEmailVerification(email: string): Promise<{
|
|
947
|
+
sent: boolean;
|
|
948
|
+
emailSent: boolean;
|
|
949
|
+
preview?: {
|
|
950
|
+
code: string;
|
|
951
|
+
link: string;
|
|
952
|
+
};
|
|
953
|
+
}>;
|
|
954
|
+
verifyEmailWithCode(email: string, code: string): Promise<{
|
|
955
|
+
verified: boolean;
|
|
956
|
+
email: string;
|
|
957
|
+
}>;
|
|
958
|
+
confirmEmailLink(token: string, email: string): Promise<{
|
|
959
|
+
verified: boolean;
|
|
960
|
+
email: string;
|
|
961
|
+
}>;
|
|
962
|
+
sendAccountRecovery(email: string): Promise<{
|
|
963
|
+
sent: boolean;
|
|
964
|
+
emailSent?: boolean;
|
|
965
|
+
preview?: {
|
|
966
|
+
code: string;
|
|
967
|
+
token: string;
|
|
968
|
+
};
|
|
969
|
+
}>;
|
|
970
|
+
recoverAccountWithCode(email: string, code: string, newPassword: string): Promise<{
|
|
971
|
+
recovered: boolean;
|
|
972
|
+
email: string;
|
|
973
|
+
sessionsRevoked?: number;
|
|
974
|
+
}>;
|
|
975
|
+
recoverAccountWithToken(token: string, newPassword: string): Promise<{
|
|
976
|
+
recovered: boolean;
|
|
977
|
+
email: string;
|
|
978
|
+
sessionsRevoked?: number;
|
|
979
|
+
}>;
|
|
980
|
+
signIn(providerId: ProviderId, options?: {
|
|
981
|
+
returnTo?: string;
|
|
982
|
+
metaTag?: string;
|
|
983
|
+
}): Promise<any>;
|
|
984
|
+
signIn(authGuard: Pick<AuthGuard, 'signIn'>, providerId: ProviderId, options?: {
|
|
985
|
+
returnTo?: string;
|
|
986
|
+
metaTag?: string;
|
|
987
|
+
}): Promise<any>;
|
|
988
|
+
signInWithGoogle(options?: {
|
|
989
|
+
returnTo?: string;
|
|
990
|
+
metaTag?: string;
|
|
991
|
+
}): Promise<any>;
|
|
992
|
+
signInWithGitHub(options?: {
|
|
993
|
+
returnTo?: string;
|
|
994
|
+
metaTag?: string;
|
|
995
|
+
}): Promise<any>;
|
|
996
|
+
signInWithFacebook(options?: {
|
|
997
|
+
returnTo?: string;
|
|
998
|
+
metaTag?: string;
|
|
999
|
+
}): Promise<any>;
|
|
1000
|
+
signInWithDropbox(options?: {
|
|
1001
|
+
returnTo?: string;
|
|
1002
|
+
metaTag?: string;
|
|
1003
|
+
}): Promise<any>;
|
|
1004
|
+
handleSignInRedirect(autoRedirect?: boolean): Promise<(AuthResult & {
|
|
1005
|
+
authToken: AuthToken;
|
|
1006
|
+
provider?: ProviderId;
|
|
1007
|
+
}) | null>;
|
|
1008
|
+
handleSignInRedirect(authGuard: Pick<AuthGuard, 'handleRedirect'>, autoRedirect?: boolean): Promise<(AuthResult & {
|
|
1009
|
+
authToken: AuthToken;
|
|
1010
|
+
provider?: ProviderId;
|
|
1011
|
+
}) | null>;
|
|
1012
|
+
private exchangeProviderToken;
|
|
1013
|
+
protected getAuthGuard(): Promise<AuthGuard>;
|
|
1014
|
+
refreshAuthSession(refresh_token?: string): Promise<FlareAuthSession | null>;
|
|
1015
|
+
issueSsrToken(ttlSeconds?: number): Promise<{
|
|
1016
|
+
token: string;
|
|
1017
|
+
token_type: string;
|
|
1018
|
+
expires_in: number;
|
|
1019
|
+
uid: string;
|
|
1020
|
+
role: string;
|
|
1021
|
+
email?: string;
|
|
1022
|
+
}>;
|
|
1023
|
+
signOut(): Promise<void>;
|
|
1024
|
+
protected registerWithEmail(email: string, password: string, options?: {
|
|
1025
|
+
scope?: string[];
|
|
1026
|
+
additionalParams?: Record<string, string>;
|
|
1027
|
+
signInIfAllowed?: boolean;
|
|
1028
|
+
}): Promise<Record<string, any>>;
|
|
1029
|
+
protected requestEmailPasswordToken(email: string, password: string, scope?: string[]): Promise<AuthToken & {
|
|
1030
|
+
kind: string;
|
|
1031
|
+
}>;
|
|
1032
|
+
protected fetchAuthMe(token: string): Promise<{
|
|
1033
|
+
id?: string;
|
|
1034
|
+
email?: string | null;
|
|
1035
|
+
email_verified?: boolean;
|
|
1036
|
+
}>;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
/**
|
|
1040
|
+
* Client/index.ts ─ entry point
|
|
1041
|
+
*
|
|
1042
|
+
* FlareClient is the public-facing class. All logic lives in:
|
|
1043
|
+
* - Client/base.ts → transport, subscriptions, presence, vector, offline
|
|
1044
|
+
* - Client/auth.ts → CSRF capture, all auth & session methods
|
|
1045
|
+
*
|
|
1046
|
+
* CSRF Protection (SSR-Only by Default)
|
|
1047
|
+
* ────────────────────────────────────
|
|
1048
|
+
* FlareClient does NOT automatically fetch CSRF on construction. Instead:
|
|
1049
|
+
*
|
|
1050
|
+
* 1. SSR (Next.js): Middleware fetches /auth/config once, sets CSRF as HttpOnly
|
|
1051
|
+
* cookie on response. Methods automatically use this cookie (no extra calls).
|
|
1052
|
+
*
|
|
1053
|
+
* 2. Browser-only (SPA): Explicitly call client.ensureCsrfProtection() before
|
|
1054
|
+
* mutations to fetch /auth/config and cache CSRF token in memory.
|
|
1055
|
+
*
|
|
1056
|
+
* Why? Eliminates redundant /auth/config calls in SSR, where every method used
|
|
1057
|
+
* to fetch it again internally. Now CSRF bootstrapping happens once (in middleware),
|
|
1058
|
+
* and auth methods just use getCsrfHeaders() which returns the cached token
|
|
1059
|
+
* (if available) or empty object (relying on HttpOnly cookie validation).
|
|
1060
|
+
*
|
|
1061
|
+
* This file wires FlareAuth together and leaves CSRF bootstrapping to the user.
|
|
1062
|
+
*/
|
|
1063
|
+
|
|
1064
|
+
declare class FlareClient<TPresetMap extends QueryPresetMap = {}> extends FlareAuth<TPresetMap> {
|
|
1065
|
+
constructor(config: FlareConfig);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* Client/proxy.ts ─ Next.js SSR CSRF proxy helper
|
|
1070
|
+
*
|
|
1071
|
+
* Why this exists
|
|
1072
|
+
* ───────────────
|
|
1073
|
+
* The Flare server now sets the CSRF token exclusively as an HttpOnly cookie
|
|
1074
|
+
* on its own domain (e.g. api.flare.example.com) and also echoes it in the
|
|
1075
|
+
* `x-flare-csrf` response header so the browser client can capture it in-
|
|
1076
|
+
* memory and send it back as a request header.
|
|
1077
|
+
*
|
|
1078
|
+
* When your Next.js app runs server-side rendering (SSR / Route Handlers /
|
|
1079
|
+
* Server Actions) it operates on a *different* domain (e.g. app.example.com).
|
|
1080
|
+
* The browser's HttpOnly Flare cookie is scoped to the Flare domain and is
|
|
1081
|
+
* therefore NOT automatically forwarded by the browser to your Next.js
|
|
1082
|
+
* server-side fetch calls.
|
|
1083
|
+
*
|
|
1084
|
+
* Solution — two-cookie strategy
|
|
1085
|
+
* ────────────────────────────────
|
|
1086
|
+
* 1. Browser → Flare domain : Flare sets `__flare_csrf_<appId>` as HttpOnly
|
|
1087
|
+
* on the Flare domain. Browser also reads the
|
|
1088
|
+
* token from the `x-flare-csrf` header and keeps
|
|
1089
|
+
* it in-memory for direct API calls.
|
|
1090
|
+
*
|
|
1091
|
+
* 2. Browser → Next.js domain : Mount `createCsrfProxy()` at a route such as
|
|
1092
|
+
* `/api/flare/csrf`. On first browser load call
|
|
1093
|
+
* this route; it hits Flare's /auth/config,
|
|
1094
|
+
* reads the `x-flare-csrf` header, and sets
|
|
1095
|
+
* it as an HttpOnly cookie on the *Next.js*
|
|
1096
|
+
* domain too (`__flare_csrf_<appId>`).
|
|
1097
|
+
* All subsequent SSR fetch calls from the
|
|
1098
|
+
* Next.js server can then read this cookie from
|
|
1099
|
+
* the incoming request and forward it as the
|
|
1100
|
+
* `x-flare-csrf` header to Flare.
|
|
1101
|
+
*
|
|
1102
|
+
* Usage (Next.js App Router)
|
|
1103
|
+
* ─────────────────────────────
|
|
1104
|
+
* // app/api/flare/csrf/route.ts
|
|
1105
|
+
* import { createCsrfProxy } from "@zuzjs/flare-client/proxy";
|
|
1106
|
+
* export const GET = createCsrfProxy({ endpoint: "https://api.flare.example.com", appId: "my-app" });
|
|
1107
|
+
*
|
|
1108
|
+
* // In any Server Component / Route Handler:
|
|
1109
|
+
* import { extractCsrfFromRequest, buildFlareHeaders } from "@zuzjs/flare-client/proxy";
|
|
1110
|
+
* const csrf = extractCsrfFromRequest(request, "my-app");
|
|
1111
|
+
* const res = await withGet(`${FLARE}/auth/whatever`, { headers: buildFlareHeaders(csrf), ignoreKind: true });
|
|
1112
|
+
*
|
|
1113
|
+
* Usage (Next.js Pages Router)
|
|
1114
|
+
* ──────────────────────────────
|
|
1115
|
+
* // pages/api/flare/csrf.ts
|
|
1116
|
+
* import { createCsrfProxyHandler } from "@zuzjs/flare-client/proxy";
|
|
1117
|
+
* export default createCsrfProxyHandler({ endpoint: "...", appId: "my-app" });
|
|
1118
|
+
*/
|
|
1119
|
+
interface CsrfProxyConfig {
|
|
1120
|
+
/** Base URL of the Flare server, e.g. "https://api.flare.example.com" */
|
|
1121
|
+
endpoint: string;
|
|
1122
|
+
/** App ID passed to Flare's /auth/config */
|
|
1123
|
+
appId: string;
|
|
1124
|
+
/** Optional Flare API key */
|
|
1125
|
+
apiKey?: string;
|
|
348
1126
|
/**
|
|
349
|
-
*
|
|
1127
|
+
* Name of the proxy cookie written on the Next.js domain.
|
|
1128
|
+
* Defaults to `__flare_csrf_<appId>`.
|
|
350
1129
|
*/
|
|
351
|
-
|
|
1130
|
+
proxyCookieName?: string;
|
|
352
1131
|
/**
|
|
353
|
-
*
|
|
1132
|
+
* Max-Age for the proxy cookie in seconds.
|
|
1133
|
+
* Defaults to 3600 (1 hour).
|
|
354
1134
|
*/
|
|
355
|
-
|
|
356
|
-
private log;
|
|
1135
|
+
proxyCookieMaxAge?: number;
|
|
357
1136
|
}
|
|
1137
|
+
/**
|
|
1138
|
+
* Creates a Next.js App Router `GET` handler that:
|
|
1139
|
+
* 1. Calls Flare's /auth/config and captures the `x-flare-csrf` header.
|
|
1140
|
+
* 2. Sets it as `__flare_csrf_<appId>` HttpOnly cookie on the current domain.
|
|
1141
|
+
* 3. Returns the token in JSON for the browser to store in-memory as well.
|
|
1142
|
+
*
|
|
1143
|
+
* Mount at: `app/api/flare/csrf/route.ts`
|
|
1144
|
+
*/
|
|
1145
|
+
declare function createCsrfProxy(config: CsrfProxyConfig): (_request: Request) => Promise<Response>;
|
|
1146
|
+
/**
|
|
1147
|
+
* Creates a Next.js Pages Router API handler (`pages/api/flare/csrf.ts`).
|
|
1148
|
+
* Same behaviour as `createCsrfProxy()` but uses the Node.js req/res API.
|
|
1149
|
+
*/
|
|
1150
|
+
declare function createCsrfProxyHandler(config: CsrfProxyConfig): (req: any, res: any) => Promise<void>;
|
|
1151
|
+
/**
|
|
1152
|
+
* Extract the proxied CSRF token from an incoming Next.js request's cookies.
|
|
1153
|
+
*
|
|
1154
|
+
* Call this inside Server Components, Route Handlers, or `getServerSideProps`
|
|
1155
|
+
* to retrieve the token that was set by `createCsrfProxy`.
|
|
1156
|
+
*
|
|
1157
|
+
* @example
|
|
1158
|
+
* // App Router Route Handler
|
|
1159
|
+
* const csrf = extractCsrfFromRequest(request, "my-app");
|
|
1160
|
+
*/
|
|
1161
|
+
declare function extractCsrfFromRequest(request: Request | {
|
|
1162
|
+
cookies: Record<string, string> | {
|
|
1163
|
+
get(name: string): {
|
|
1164
|
+
value: string;
|
|
1165
|
+
} | undefined;
|
|
1166
|
+
};
|
|
1167
|
+
}, appId: string, proxyCookieName?: string): string | null;
|
|
1168
|
+
/**
|
|
1169
|
+
* Build the headers object to attach to a server-side fetch call to Flare,
|
|
1170
|
+
* forwarding the CSRF token and (optionally) the Authorization bearer token.
|
|
1171
|
+
*/
|
|
1172
|
+
declare function buildFlareHeaders(csrfToken: string | null, options?: {
|
|
1173
|
+
accessToken?: string;
|
|
1174
|
+
apiKey?: string;
|
|
1175
|
+
}): Record<string, string>;
|
|
358
1176
|
|
|
359
1177
|
declare class FlareError extends Error {
|
|
360
1178
|
readonly code: string;
|
|
@@ -362,6 +1180,65 @@ declare class FlareError extends Error {
|
|
|
362
1180
|
constructor(message: string, code: string, cause?: unknown | undefined);
|
|
363
1181
|
}
|
|
364
1182
|
|
|
1183
|
+
declare enum FlareErrors {
|
|
1184
|
+
authEmailNotVerified = "auth/email-not-verified",
|
|
1185
|
+
authEmailAlreadyVerified = "auth/email-already-verified",
|
|
1186
|
+
authInvalidToken = "auth/invalid-token",
|
|
1187
|
+
authUserDisabled = "auth/user-disabled",
|
|
1188
|
+
authUserNotFound = "auth/user-not-found",
|
|
1189
|
+
authWrongPassword = "auth/wrong-password",
|
|
1190
|
+
authEmailAlreadyInUse = "auth/email-already-in-use",
|
|
1191
|
+
authInvalidEmail = "auth/invalid-email",
|
|
1192
|
+
authWeakPassword = "auth/weak-password",
|
|
1193
|
+
authTooManyRequests = "auth/too-many-requests",
|
|
1194
|
+
authInternalError = "auth/internal-error"
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
declare enum FlareResponseCodes {
|
|
1198
|
+
health = "health",
|
|
1199
|
+
authConfig = "auth_config",
|
|
1200
|
+
authRegistration = "auth/registration",
|
|
1201
|
+
authRegistrationVerificationRequired = "auth/registration-verification-required",
|
|
1202
|
+
authSession = "auth/session",
|
|
1203
|
+
authExchange = "auth/exchange",
|
|
1204
|
+
authLogout = "auth/logout",
|
|
1205
|
+
authSsrBridge = "auth/ssr_bridge",
|
|
1206
|
+
authSsrVerify = "auth/ssr_verify",
|
|
1207
|
+
accountRecovery = "account/recovery",
|
|
1208
|
+
emailVerification = "email/verification",
|
|
1209
|
+
verificationDispatch = "verification/dispatch",
|
|
1210
|
+
authProfile = "auth/profile",
|
|
1211
|
+
adminToken = "admin/token",
|
|
1212
|
+
documentDelete = "document/delete",
|
|
1213
|
+
documentsDelete = "documents/delete",
|
|
1214
|
+
documents = "documents",
|
|
1215
|
+
document = "document",
|
|
1216
|
+
documentCreate = "document/create",
|
|
1217
|
+
documentUpdate = "document/update",
|
|
1218
|
+
oauthProviderResponse = "oauth_provider_response",
|
|
1219
|
+
success = "success",
|
|
1220
|
+
response = "response"
|
|
1221
|
+
}
|
|
1222
|
+
interface AuthConfigResponse {
|
|
1223
|
+
kind: string;
|
|
1224
|
+
appId: string;
|
|
1225
|
+
enabled: boolean;
|
|
1226
|
+
csrfToken?: string;
|
|
1227
|
+
cookie: {
|
|
1228
|
+
accessTokenName: string;
|
|
1229
|
+
refreshTokenName: string;
|
|
1230
|
+
csrfTokenName: string;
|
|
1231
|
+
path: string;
|
|
1232
|
+
secure: boolean;
|
|
1233
|
+
sameSite: 'Strict' | 'Lax' | 'None';
|
|
1234
|
+
accessTokenMaxAge: number;
|
|
1235
|
+
refreshTokenMaxAge: number;
|
|
1236
|
+
csrfTokenMaxAge: number;
|
|
1237
|
+
};
|
|
1238
|
+
providers: Record<string, any>;
|
|
1239
|
+
ssr: Record<string, any>;
|
|
1240
|
+
}
|
|
1241
|
+
|
|
365
1242
|
/**
|
|
366
1243
|
* Initialize and connect to FlareServer
|
|
367
1244
|
* Returns a singleton instance
|
|
@@ -376,4 +1253,4 @@ declare const getFlare: () => FlareClient | null;
|
|
|
376
1253
|
*/
|
|
377
1254
|
declare const disconnectFlare: () => void;
|
|
378
1255
|
|
|
379
|
-
export { type AuthResult, type BaseMessage, CollectionReference, type ConnectionState, DocumentQueryBuilder, DocumentReference, type DocumentSnapshot, FlareAction, type FlareConfig, FlareError, FlareEvent, type OfflineOperation, type QueryConfig, type QueryOperator, type QuerySnapshot, type SubscriptionCallback, type SubscriptionData, type WhereCondition, connectApp, FlareClient as default, disconnectFlare, getFlare };
|
|
1256
|
+
export { type AggregateFunction, type AggregateSpec, type AnyFilter, type AuthConfigListener, type AuthConfigResponse, type AuthResult, type AuthStateListener, type AuthWithPendingVerificationResult, type AuthWithTokenResult, type BaseMessage, type ChangeEvent, type ChangeOperation, type CollectionPresetMethods, type CollectionQuery, CollectionReference, type ConnectionState, type CsrfProxyConfig, type CursorValue, type DocAddedCallback, type DocChangedCallback, type DocDeletedCallback, type DocUpdatedCallback, DocumentQueryBuilder, DocumentReference, type DocumentSnapshot, FlareAction, type FlareAuthConfig, type FlareAuthProviderId, type FlareAuthProviderPublicConfig, type FlareAuthSession, type FlareAuthUser, type FlareConfig, FlareError, FlareErrors, FlareEvent, FlareResponseCodes, type FlareRule, type GroupByClause, type HavingClause, type JoinClause, type JoinQueryPattern, type NestedJoinClause, type OfflineOperation, type OrFilter, type OrderByClause, type PresenceCallback, type PresenceJoinCallback, type PresenceLeaveCallback, type PresenceMember, type QueryConfig, type QueryOperator, type QueryPresetMap, type QueryPresetParams, type QueryPresetRow, type QueryPresetSpec, type QuerySnapshot, type RulePermission, type SecurityRuleEntry, type SecurityRulesMap, type SnapshotEvent, type StructuredJoinClause, type StructuredQuery, type SubscribeMessage, type SubscribeOptions, type SubscriptionCallback, type SubscriptionData, type SubscriptionError, type SubscriptionErrorCallback, type SubscriptionHandle, type VectorFieldConfig, type VectorSearchClause, type WhereCondition, buildFlareHeaders, connectApp, createCsrfProxy, createCsrfProxyHandler, FlareClient as default, disconnectFlare, extractCsrfFromRequest, flareRulesToSecurityMap, getFlare, parseValue, parseWhereCondition, securityMapToFlareRules };
|