@zuzjs/flare-admin 0.1.4 → 0.1.6
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/README.md +312 -138
- package/dist/index.cjs +4 -4
- package/dist/index.d.cts +578 -47
- package/dist/index.d.ts +94 -11
- package/dist/index.js +3 -3
- package/dist/lib/grpc.d.ts +11 -0
- package/dist/lib/http.d.ts +3 -0
- package/dist/lib/notifications.d.ts +1 -1
- package/dist/lib/storage.d.ts +98 -0
- package/dist/realtime/Connection.d.ts +14 -0
- package/dist/realtime/LiveCollection.d.ts +3 -1
- package/dist/realtime/WsConnection.d.ts +11 -1
- package/dist/serverTimestamp.d.ts +13 -0
- package/dist/types/index.d.ts +357 -0
- package/package.json +4 -1
- package/proto/admin.proto +129 -0
- package/proto/app.proto +69 -0
- package/proto/auth.proto +70 -0
- package/proto/flare.proto +11 -0
- package/proto/query.proto +109 -0
package/dist/index.d.ts
CHANGED
|
@@ -33,24 +33,27 @@
|
|
|
33
33
|
* .get();
|
|
34
34
|
*
|
|
35
35
|
* // Real-time subscriptions over WebSocket
|
|
36
|
-
* const unsub = admin.
|
|
36
|
+
* const unsub = admin.live()
|
|
37
37
|
* .collection("orders")
|
|
38
38
|
* .where({ status: "pending" })
|
|
39
39
|
* .orderBy("createdAt", "desc")
|
|
40
40
|
* .onSnapshot((snap) => console.log(snap));
|
|
41
41
|
*/
|
|
42
|
-
export
|
|
42
|
+
export { AdminStorageSignedAction } from "./types";
|
|
43
|
+
export type { AdminConnectionState, AdminConnectionStateListener, AdminCopyObjectInput, AdminDeleteObjectsInput, AdminDocAddedCallback, AdminDocChangedCallback, AdminDocDeletedCallback, AdminDocUpdatedCallback, AdminDownloadObjectInput, AdminDownloadObjectResult, AdminGetObjectInput, AdminGetObjectResult, AdminGetObjectUrlInput, AdminHeadObjectInput, AdminHeadObjectsInput, AdminListObjectsInput, AdminListObjectsResult, AdminPushSendInput, AdminPushSendResult, AdminPushToken, AdminPutObjectInput, AdminPutObjectResult, AdminSnapshotCallback, AdminSnapshotData, AdminStorageAwsConfig, AdminStorageBucket, AdminStorageBucketInput, AdminStorageDeleteInput, AdminStorageDownloadInput, AdminStorageObjectMeta, AdminStorageObjectResult, AdminStorageRulesHistoryResult, AdminStorageRulesPolicy, AdminStorageServer, AdminStorageServerInput, AdminStorageServerPatchInput, AdminStorageSignedUrlInput, AdminStorageSignedUrlResult, AdminStorageUploadInput, AdminSubscriptionError, AdminSubscriptionErrorCallback, AdminSubscriptionHandle, AggregateFunction, AggregateSpec, AnyFilter, CreateCustomTokenOptions, CursorValue, FlareAdminAuth, FlareAdminConfig, FlareAdminDb, FlareAdminNotifications, FlareAdminStorage, GroupByClause, HavingClause, JoinClause, OrderByClause, OrFilter, QueryOperator, StructuredQuery, VectorSearchClause, WhereCondition, WhereFilter } from "./types";
|
|
43
44
|
export { AdminCollectionReference } from "./db/Collection";
|
|
44
45
|
export { AdminDocumentReference } from "./db/Document";
|
|
45
46
|
export { FlareAdminDbService } from "./db/index";
|
|
46
47
|
export { FlareAdminAuthService } from "./lib/auth";
|
|
47
48
|
export { FlareAdminNotificationsService } from "./lib/notifications";
|
|
49
|
+
export { FlareAdminStorageS3 } from "./lib/storage";
|
|
48
50
|
export { FlareAdminConnection } from "./realtime/Connection";
|
|
49
51
|
export { AdminLiveCollectionReference } from "./realtime/LiveCollection";
|
|
50
52
|
export { AdminLiveDocumentReference } from "./realtime/LiveDocument";
|
|
51
53
|
export { FlareAdminWsConnection } from "./realtime/WsConnection";
|
|
54
|
+
import { FlareAdminStorageS3 } from "./lib/storage";
|
|
52
55
|
import { FlareAdminConnection } from "./realtime/Connection";
|
|
53
|
-
import { FlareAdminAuth, FlareAdminConfig, FlareAdminDb, FlareAdminNotifications } from "./types";
|
|
56
|
+
import { AdminCopyObjectInput, AdminDeleteObjectsInput, AdminDownloadObjectInput, AdminDownloadObjectResult, AdminGetObjectInput, AdminGetObjectResult, AdminGetObjectUrlInput, AdminHeadObjectInput, AdminHeadObjectsInput, AdminListObjectsInput, AdminListObjectsResult, AdminPutObjectInput, AdminPutObjectResult, AdminStorageBucket, AdminStorageBucketInput, AdminStorageObjectMeta, AdminStorageSignedUrlInput, AdminStorageSignedUrlResult, FlareAdminAuth, FlareAdminConfig, FlareAdminDb, FlareAdminNotifications } from "./types";
|
|
54
57
|
/**
|
|
55
58
|
* A FlareAdmin application instance.
|
|
56
59
|
* Create one with `connectApp()` and keep it as a module-level singleton.
|
|
@@ -61,6 +64,8 @@ export declare class FlareAdminApp {
|
|
|
61
64
|
private _db?;
|
|
62
65
|
private _conn?;
|
|
63
66
|
private _notifications?;
|
|
67
|
+
private _storage?;
|
|
68
|
+
private _storageS3?;
|
|
64
69
|
/**
|
|
65
70
|
* Access the auth service.
|
|
66
71
|
*
|
|
@@ -95,7 +100,7 @@ export declare class FlareAdminApp {
|
|
|
95
100
|
* `groupBy`, `having`, `Join`, `select`, `distinctField`, `vectorSearch`.
|
|
96
101
|
*
|
|
97
102
|
* @example
|
|
98
|
-
* const unsub = admin.
|
|
103
|
+
* const unsub = admin.live()
|
|
99
104
|
* .collection("orders")
|
|
100
105
|
* .where({ status: "pending" })
|
|
101
106
|
* .orderBy("createdAt", "desc")
|
|
@@ -105,19 +110,86 @@ export declare class FlareAdminApp {
|
|
|
105
110
|
* else console.log(snap.operation, snap.data);
|
|
106
111
|
* });
|
|
107
112
|
*
|
|
108
|
-
* const unsub2 = admin.
|
|
113
|
+
* const unsub2 = admin.live()
|
|
109
114
|
* .collection("users").doc("alice")
|
|
110
115
|
* .onSnapshot((snap) => console.log(snap.data));
|
|
111
116
|
*
|
|
112
117
|
* unsub();
|
|
113
118
|
* unsub2();
|
|
114
|
-
* admin.
|
|
119
|
+
* admin.live().disconnect();
|
|
115
120
|
*/
|
|
116
|
-
|
|
121
|
+
live(): FlareAdminConnection;
|
|
117
122
|
/**
|
|
118
123
|
* Access push notification management APIs.
|
|
119
124
|
*/
|
|
120
125
|
notifications(): FlareAdminNotifications;
|
|
126
|
+
private storageService;
|
|
127
|
+
storage(): FlareAdminStorageS3;
|
|
128
|
+
/**
|
|
129
|
+
* S3-compatible storage service. Works with bucket names; no server IDs needed.
|
|
130
|
+
* Bucket is auto-created on putObject() if it doesn't exist.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* await admin.s3().putObject({ bucket: 'avatars', key: 'alice.png', body: buffer });
|
|
134
|
+
* const { contentBase64 } = await admin.s3().getObject({ bucket: 'avatars', key: 'alice.png' });
|
|
135
|
+
*/
|
|
136
|
+
s3(): FlareAdminStorageS3;
|
|
137
|
+
createSignedUrl(input: AdminStorageSignedUrlInput): Promise<AdminStorageSignedUrlResult>;
|
|
138
|
+
createBucket(name: string, options?: AdminStorageBucketInput): Promise<AdminStorageBucket>;
|
|
139
|
+
listBuckets(): Promise<AdminStorageBucket[]>;
|
|
140
|
+
deleteBucket(name: string): Promise<{
|
|
141
|
+
ok: boolean;
|
|
142
|
+
removedObjects: number;
|
|
143
|
+
}>;
|
|
144
|
+
deleteBuckets(names: string[]): Promise<{
|
|
145
|
+
ok: boolean;
|
|
146
|
+
deleted: string[];
|
|
147
|
+
errors: Record<string, string>;
|
|
148
|
+
}>;
|
|
149
|
+
getBucketLocation(name: string): Promise<{
|
|
150
|
+
bucket: string;
|
|
151
|
+
kind: string;
|
|
152
|
+
region?: string;
|
|
153
|
+
endpoint?: string;
|
|
154
|
+
}>;
|
|
155
|
+
putObject(input: AdminPutObjectInput): Promise<AdminPutObjectResult>;
|
|
156
|
+
getObject(input: AdminGetObjectInput): Promise<AdminGetObjectResult>;
|
|
157
|
+
getObjectUrl(input: AdminGetObjectUrlInput): Promise<string>;
|
|
158
|
+
downloadObject(input: AdminDownloadObjectInput): Promise<AdminDownloadObjectResult>;
|
|
159
|
+
headObject(input: AdminHeadObjectInput): Promise<AdminStorageObjectMeta>;
|
|
160
|
+
headObjects(input: AdminHeadObjectsInput): Promise<AdminStorageObjectMeta[]>;
|
|
161
|
+
listObjects(input: AdminListObjectsInput): Promise<AdminListObjectsResult>;
|
|
162
|
+
copyObject(input: AdminCopyObjectInput): Promise<{
|
|
163
|
+
ok: boolean;
|
|
164
|
+
}>;
|
|
165
|
+
copyObjects(inputs: AdminCopyObjectInput[]): Promise<{
|
|
166
|
+
ok: boolean;
|
|
167
|
+
errors: Record<string, string>;
|
|
168
|
+
}>;
|
|
169
|
+
deleteObjects(input: AdminDeleteObjectsInput): Promise<{
|
|
170
|
+
ok: boolean;
|
|
171
|
+
deleted: string[];
|
|
172
|
+
errors: Record<string, string>;
|
|
173
|
+
}>;
|
|
174
|
+
/**
|
|
175
|
+
* Subscribe to WebSocket connection state changes.
|
|
176
|
+
* Initializes the live connection if not already open.
|
|
177
|
+
* Returns an unsubscribe function.
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* const unsub = admin.onConnectionStateChange((state) => {
|
|
181
|
+
* console.log("connection:", state);
|
|
182
|
+
* // state: 'connecting' | 'connected' | 'disconnected' | 'reconnecting' | 'error'
|
|
183
|
+
* });
|
|
184
|
+
* // Later:
|
|
185
|
+
* unsub();
|
|
186
|
+
*/
|
|
187
|
+
onConnectionStateChange(listener: (state: import("./types").AdminConnectionState) => void): () => void;
|
|
188
|
+
/**
|
|
189
|
+
* Disconnect realtime resources tied to this app instance.
|
|
190
|
+
* Safe to call multiple times.
|
|
191
|
+
*/
|
|
192
|
+
disconnect(): void;
|
|
121
193
|
}
|
|
122
194
|
/**
|
|
123
195
|
* Initialize a FlareAdmin app instance.
|
|
@@ -139,6 +211,15 @@ export declare function connectApp(config: FlareAdminConfig, name?: string): Fla
|
|
|
139
211
|
* @throws If the app has not been initialized yet.
|
|
140
212
|
*/
|
|
141
213
|
export declare function getApp(name?: string): FlareAdminApp;
|
|
214
|
+
/**
|
|
215
|
+
* Disconnect and remove an initialized app by name.
|
|
216
|
+
* Returns true when an app existed and was removed.
|
|
217
|
+
*/
|
|
218
|
+
export declare function disconnectApp(name?: string): boolean;
|
|
219
|
+
/**
|
|
220
|
+
* Disconnect and clear all initialized apps.
|
|
221
|
+
*/
|
|
222
|
+
export declare function disconnectAllApps(): void;
|
|
142
223
|
/**
|
|
143
224
|
* Get the auth service from the default app.
|
|
144
225
|
* Equivalent to `getApp().auth()`.
|
|
@@ -159,17 +240,19 @@ export declare function auth(name?: string): FlareAdminAuth;
|
|
|
159
240
|
export declare function db(name?: string): FlareAdminDb;
|
|
160
241
|
/**
|
|
161
242
|
* Get the real-time WebSocket connection from the default app.
|
|
162
|
-
* Equivalent to `getApp().
|
|
243
|
+
* Equivalent to `getApp().live()`.
|
|
163
244
|
*
|
|
164
245
|
* @example
|
|
165
|
-
* import {
|
|
166
|
-
* const unsub =
|
|
246
|
+
* import { live } from "@zuzjs/flare-admin";
|
|
247
|
+
* const unsub = live().collection("users")
|
|
167
248
|
* .where({ role: "admin" })
|
|
168
249
|
* .onSnapshot((snap) => console.log(snap));
|
|
169
250
|
*/
|
|
170
|
-
export declare function
|
|
251
|
+
export declare function live(name?: string): FlareAdminConnection;
|
|
171
252
|
/**
|
|
172
253
|
* Get the notifications service from the default app.
|
|
173
254
|
* Equivalent to `getApp().notifications()`.
|
|
174
255
|
*/
|
|
175
256
|
export declare function notifications(name?: string): FlareAdminNotifications;
|
|
257
|
+
export declare function storage(name?: string): FlareAdminStorageS3;
|
|
258
|
+
export * from "./serverTimestamp";
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import L from'ws';function k(o){let e=[];for(let[n,r]of Object.entries(o))if(typeof r=="string"){let t=r.match(/^(>=|<=|!=|>|<|==)\s*(.+)$/);if(t){let[,i,s]=t;e.push({field:n,op:i,value:I(s.trim())});}else e.push({field:n,op:"==",value:r});}else Array.isArray(r)?e.push({field:n,op:"in",value:r}):e.push({field:n,op:"==",value:r});return e}function I(o){return isNaN(Number(o))?o==="true"?true:o==="false"?false:o==="null"?null:o:Number(o)}function F(){return Math.random().toString(36).slice(2,12)+Date.now().toString(36)}function $(o,e){let n=o.dataMapper;if(!n||typeof n!="object")return null;let r=n[e];return typeof r=="function"?r:null}function q(o,e,n){let r=$(o,e);if(!r||n==null||typeof n!="object")return n;try{return r(n)}catch{return n}}function E(o,e,n){if(!e||typeof e!="object"||!Array.isArray(n)||n.length===0)return e;let r=e;for(let t of n){let i=String(t?.as??"").trim();if(!i)continue;let s=Array.isArray(t?.joins)?t.joins:[],a=r[i],c=a;Array.isArray(a)?c=a.map(l=>E(o,l,s)):a&&typeof a=="object"&&(c=E(o,a,s)),c=Array.isArray(c)?c.map(l=>q(o,i,l)):q(o,i,c),c!==a&&(r===e&&(r={...r}),r[i]=c);}return r}function f(o,e,n,r){let t=Array.isArray(r?.joins)?r.joins:[],i=s=>{let a=E(o,s,t);return q(o,e,a)};return Array.isArray(n)?n.map(s=>i(s)):i(n)}var g=class{constructor(e,n,r){this.cfg=e;this.collection=n;this.id=r;}get baseUrl(){return `${this.cfg.serverUrl}/admin/db/${this.cfg.appId}/${this.collection}/${this.id}`}get headers(){return {"Content-Type":"application/json",Authorization:`Bearer ${this.cfg.adminKey}`}}async request(e,n){let r;try{r=await fetch(this.baseUrl,{method:e,headers:this.headers,...n!==void 0?{body:JSON.stringify(n)}:{}});}catch(i){let s=i instanceof Error?i.message:String(i);throw new Error(`[flare-admin] Network error (${e} ${this.baseUrl}): ${s}`)}let t=await r.json().catch(()=>({}));if(!r.ok)throw new Error(`[flare-admin] ${e} ${this.baseUrl} failed (HTTP ${r.status}): `+(t.error??"Unknown error"));return t}async get(){let e;try{e=await fetch(this.baseUrl,{headers:this.headers});}catch(t){let i=t instanceof Error?t.message:String(t);throw new Error(`[flare-admin] Network error (GET ${this.baseUrl}): ${i}`)}if(e.status===404)return null;let n=await e.json().catch(()=>({}));if(!e.ok)throw new Error(`[flare-admin] GET ${this.baseUrl} failed (HTTP ${e.status}): `+(n.error??"Unknown error"));return f(this.cfg,this.collection,n.data)??null}async set(e){await this.request("PUT",e);}async update(e){await this.request("PATCH",e);}async delete(){await this.request("DELETE");}parent(){return new m(this.cfg,this.collection)}};var m=class o{constructor(e,n){this.cfg=e;this.name=n;this.sq={};this.opts={allowSensitiveAuthUserFields:true};}get baseUrl(){return `${this.cfg.serverUrl}/admin/db/${this.cfg.appId}/${this.name}`}get headers(){return {"Content-Type":"application/json",Authorization:`Bearer ${this.cfg.adminKey}`}}clone(e){let n=new o(this.cfg,this.name);return n.sq={...this.sq,...e},n.opts={...this.opts},n}allowSensitiveAuthUserFields(e=true){let n=this.clone({});return n.opts.allowSensitiveAuthUserFields=!!e,n}normalizeFilterValue(e,n){return e==="in"||e==="not-in"||e==="array-contains-any"?Array.isArray(n)?n:[n]:n}toQueryFilters(e){return k(e).map(r=>typeof r=="object"&&r!=null&&"field"in r&&"op"in r?{...r,value:this.normalizeFilterValue(r.op,r.value)}:r)}appendAndFilters(e){return this.clone({where:[...this.sq.where??[],...e]})}appendOrFilters(e){let n=[...this.sq.where??[]];if(n.length===0)return this.clone({where:[{or:e}]});let r=n[0];if(n.length===1&&typeof r=="object"&&r!=null&&"or"in r)return this.clone({where:[{or:[...r.or,...e]}]});let i=n.length===1?n[0]:{and:n};return this.clone({where:[{or:[i,...e]}]})}appendFilters(e,n){return n==="or"?this.appendOrFilters(e):this.appendAndFilters(e)}appendOperatorFilter(e,n,r,t){return this.appendFilters([{field:e,op:n,value:this.normalizeFilterValue(n,r)}],t)}where(e){return this.appendFilters(this.toQueryFilters(e),"and")}and(e){return this.appendFilters(this.toQueryFilters(e),"and")}or(e){return this.appendFilters(this.toQueryFilters(e),"or")}in(e,n){return this.appendOperatorFilter(e,"in",n,"and")}andIn(e,n){return this.appendOperatorFilter(e,"in",n,"and")}orIn(e,n){return this.appendOperatorFilter(e,"in",n,"or")}notIn(e,n){return this.appendOperatorFilter(e,"not-in",n,"and")}andNotIn(e,n){return this.appendOperatorFilter(e,"not-in",n,"and")}orNotIn(e,n){return this.appendOperatorFilter(e,"not-in",n,"or")}arrayContains(e,n){return this.appendOperatorFilter(e,"array-contains",n,"and")}andArrayContains(e,n){return this.appendOperatorFilter(e,"array-contains",n,"and")}orArrayContains(e,n){return this.appendOperatorFilter(e,"array-contains",n,"or")}arrayContainsAny(e,n){return this.appendOperatorFilter(e,"array-contains-any",n,"and")}andArrayContainsAny(e,n){return this.appendOperatorFilter(e,"array-contains-any",n,"and")}orArrayContainsAny(e,n){return this.appendOperatorFilter(e,"array-contains-any",n,"or")}some(e,n){return this.appendOperatorFilter(e,"elem-match",n,"and")}andSome(e,n){return this.appendOperatorFilter(e,"elem-match",n,"and")}orSome(e,n){return this.appendOperatorFilter(e,"elem-match",n,"or")}like(e,n){return this.appendOperatorFilter(e,"like",n,"and")}andLike(e,n){return this.appendOperatorFilter(e,"like",n,"and")}orLike(e,n){return this.appendOperatorFilter(e,"like",n,"or")}notLike(e,n){return this.appendOperatorFilter(e,"not-like",n,"and")}andNotLike(e,n){return this.appendOperatorFilter(e,"not-like",n,"and")}orNotLike(e,n){return this.appendOperatorFilter(e,"not-like",n,"or")}exists(e){return this.appendOperatorFilter(e,"exists",true,"and")}andExists(e){return this.appendOperatorFilter(e,"exists",true,"and")}orExists(e){return this.appendOperatorFilter(e,"exists",true,"or")}notExists(e){return this.appendOperatorFilter(e,"not-exists",true,"and")}andNotExists(e){return this.appendOperatorFilter(e,"not-exists",true,"and")}orNotExists(e){return this.appendOperatorFilter(e,"not-exists",true,"or")}latest(){return this.clone({orderBy:[...this.sq.orderBy??[],{field:"_seq",dir:"desc"}]})}newest(){return this.latest()}oldest(){return this.clone({orderBy:[...this.sq.orderBy??[],{field:"_seq",dir:"asc"}]})}orderBy(e,n="asc"){return this.clone({orderBy:[...this.sq.orderBy??[],{field:e,dir:n}]})}limit(e){return this.clone({limit:e})}offset(e){return this.clone({offset:e})}startAt(...e){return this.clone({startAt:{values:e}})}startAfter(...e){return this.clone({startAfter:{values:e}})}endAt(...e){return this.clone({endAt:{values:e}})}endBefore(...e){return this.clone({endBefore:{values:e}})}aggregate(...e){return this.clone({aggregate:[...this.sq.aggregate??[],...e]})}count(e="count"){return this.aggregate({fn:"count",alias:e})}sum(e,n){return this.aggregate({fn:"sum",field:e,alias:n??`sum_${e}`})}avg(e,n){return this.aggregate({fn:"avg",field:e,alias:n??`avg_${e}`})}min(e,n){return this.aggregate({fn:"min",field:e,alias:n??`min_${e}`})}max(e,n){return this.aggregate({fn:"max",field:e,alias:n??`max_${e}`})}distinct(e,n){return this.aggregate({fn:"distinct",field:e,alias:n??`distinct_${e}`})}groupBy(...e){return this.clone({groupBy:{fields:e}})}having(e,n,r){return this.clone({having:[...this.sq.having??[],{field:e,op:n,value:r}]})}buildStructuredJoin(e,n){let r={from:String(e??""),localField:String(n?.source??""),foreignField:String(n?.target??""),as:String(n?.as??""),single:n?.single};return Array.isArray(n?.where)&&(r.where=n.where),Array.isArray(n?.orderBy)&&(r.orderBy=n.orderBy),typeof n?.limit=="number"&&(r.limit=n.limit),typeof n?.offset=="number"&&(r.offset=n.offset),Array.isArray(n?.select)&&(r.select=n.select),Array.isArray(n?.joins)&&(r.joins=n.joins.map(t=>this.buildStructuredJoin(String(t?.collection??""),t))),r}cloneStructuredJoin(e){let n={...e};return Array.isArray(e.where)&&(n.where=e.where.map(r=>({...r}))),Array.isArray(e.orderBy)&&(n.orderBy=e.orderBy.map(r=>({...r}))),Array.isArray(e.select)&&(n.select=[...e.select]),Array.isArray(e.joins)&&(n.joins=e.joins.map(r=>this.cloneStructuredJoin(r))),n}appendNestedJoinByAlias(e,n,r){for(let t of e){if(t.as===n)return t.joins=[...t.joins??[],r],true;if(Array.isArray(t.joins)&&this.appendNestedJoinByAlias(t.joins,n,r))return true}return false}parseRelationRef(e){let r=String(e??"").trim().match(/^([A-Za-z0-9_.]+)\s*->\s*([A-Za-z0-9_]+)\.([A-Za-z0-9_.]+)(?:\s+as\s+([A-Za-z0-9_]+))?$/i);if(!r)throw new Error(`Invalid relation format: "${e}". Expected "source.path->collection.target"`);return {source:r[1],collection:r[2],target:r[3],alias:r[4]}}join(e,n){let r=this.buildStructuredJoin(e,n);return this.clone({joins:[...this.sq.joins??[],r]})}joinNested(e,n,r){let t=String(e??"").trim();if(!t)throw new Error("joinNested requires parentAlias");let i=(this.sq.joins??[]).map(a=>this.cloneStructuredJoin(a));if(i.length===0)throw new Error(`joinNested parent alias "${t}" not found`);let s=this.buildStructuredJoin(n,r);if(!this.appendNestedJoinByAlias(i,t,s))throw new Error(`joinNested parent alias "${t}" not found`);return this.clone({joins:i})}Join(e,n){return this.join(e,n)}JoinNested(e,n,r){return this.joinNested(e,n,r)}withRelation(e,n={}){let r=this.parseRelationRef(e);return this.join(r.collection,{...n,source:r.source,target:r.target,as:n.as??r.alias??r.collection})}select(...e){return this.clone({select:e})}distinctField(e){return this.clone({distinctField:e})}vectorSearch(e){return this.clone({vectorSearch:e})}getRawQuery(){let e={...this.sq};return Array.isArray(this.sq.where)&&(e.where=this.sq.where.map(n=>({...n}))),Array.isArray(this.sq.orderBy)&&(e.orderBy=this.sq.orderBy.map(n=>({...n}))),Array.isArray(this.sq.aggregate)&&(e.aggregate=this.sq.aggregate.map(n=>({...n}))),Array.isArray(this.sq.having)&&(e.having=this.sq.having.map(n=>({...n}))),Array.isArray(this.sq.select)&&(e.select=[...this.sq.select]),Array.isArray(this.sq.joins)&&(e.joins=this.sq.joins.map(n=>this.cloneStructuredJoin(n))),this.sq.groupBy?.fields&&(e.groupBy={fields:[...this.sq.groupBy.fields]}),this.sq.startAt?.values&&(e.startAt={values:[...this.sq.startAt.values]}),this.sq.startAfter?.values&&(e.startAfter={values:[...this.sq.startAfter.values]}),this.sq.endAt?.values&&(e.endAt={values:[...this.sq.endAt.values]}),this.sq.endBefore?.values&&(e.endBefore={values:[...this.sq.endBefore.values]}),{collection:this.name,query:e}}async get(){let e=new URL(this.baseUrl);e.searchParams.set("query",JSON.stringify(this.sq)),e.searchParams.set("allowSensitiveAuthUserFields",this.opts.allowSensitiveAuthUserFields?"1":"0");let n;try{n=await fetch(e.toString(),{headers:this.headers});}catch(i){let s=i instanceof Error?i.message:String(i);throw new Error(`[flare-admin] Network error (GET ${this.baseUrl}): ${s}`)}let r=await n.json().catch(()=>({}));if(!n.ok)throw new Error(`[flare-admin] GET ${this.baseUrl} failed (HTTP ${n.status}): ${r.error??"Unknown error"}`);return f(this.cfg,this.name,r.data,this.sq)??[]}async first(){let e=await this.get();return e.length>0?e[0]:null}async last(){let e=await this.get();return e.length>0?e[e.length-1]:null}async add(e){let n;try{n=await fetch(this.baseUrl,{method:"POST",headers:this.headers,body:JSON.stringify(e)});}catch(t){let i=t instanceof Error?t.message:String(t);throw new Error(`[flare-admin] Network error (POST ${this.baseUrl}): ${i}`)}let r=await n.json().catch(()=>({}));if(!n.ok)throw new Error(`[flare-admin] POST ${this.baseUrl} failed (HTTP ${n.status}): ${r.error??"Unknown error"}`);return new g(this.cfg,this.name,r.id)}async deleteMany(){let e;try{e=await fetch(this.baseUrl,{method:"DELETE",headers:this.headers,body:JSON.stringify({where:this.sq.where??[]})});}catch(r){let t=r instanceof Error?r.message:String(r);throw new Error(`[flare-admin] Network error (DELETE ${this.baseUrl}): ${t}`)}let n=await e.json().catch(()=>({}));if(!e.ok)throw new Error(`[flare-admin] DELETE ${this.baseUrl} failed (HTTP ${e.status}): ${n.error??"Unknown error"}`);return n.deleted??0}doc(e){return new g(this.cfg,this.name,e)}};var A=class{constructor(e){this.cfg=e;}collection(e){return new m(this.cfg,e)}};var y=class{constructor(e){this.cfg=e;}async createCustomToken(e,n={}){let r=`${this.cfg.serverUrl.replace(/\/$/,"")}/admin/token`,t=JSON.stringify({appId:this.cfg.appId,uid:String(e),role:n.role??"user",claims:n.claims??{},ttl:n.ttl??this.cfg.defaultTtl}),i;try{i=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.cfg.adminKey}`},body:t});}catch(a){let c=a instanceof Error?a.message:String(a);throw new Error(`[flare-admin] Could not reach FlareServer at "${r}": ${c}
|
|
2
|
-
Make sure FLARE_URL is set correctly and the server is running.`)}let
|
|
3
|
-
Make sure FLARE_URL is set correctly and the server is running.`)}let s;try{s=await i.json();}catch{throw new Error(`[flare-admin] Server returned non-JSON response (status ${i.status})`)}if(!i.ok)throw new Error(`[flare-admin] getTicket failed (HTTP ${i.status}): `+(s.error??"Unknown error"));let a=s.ticket;if(!a||typeof a.ticket!="string")throw new Error('[flare-admin] Server response missing "ticket" object');return {ticket:String(a.ticket),tag:String(a.tag??""),uuid:String(a.uuid??""),expires_at:String(a.expires_at??""),one_time:!!(a.one_time??true),uid:String(a.uid??String(e)),role:String(a.role??n.role??"user"),ip:String(a.ip??"unknown")}}};var w=class{constructor(e){this.cfg=e;}get baseUrl(){return `${this.cfg.serverUrl.replace(/\/$/,"")}/admin/notify/${this.cfg.appId}`}get headers(){return {"Content-Type":"application/json",Authorization:`Bearer ${this.cfg.adminKey}`}}async send(e){let n;try{n=await fetch(`${this.baseUrl}/send`,{method:"POST",headers:this.headers,body:JSON.stringify(e??{})});}catch(t){let i=t instanceof Error?t.message:String(t);throw new Error(`[flare-admin] Network error (POST ${this.baseUrl}/send): ${i}`)}let r=await n.json().catch(()=>({}));if(!n.ok)throw new Error(`[flare-admin] Notification send failed (HTTP ${n.status}): `+(r.error??"Unknown error"));return {sent:!!r.sent,appId:String(r.appId??this.cfg.appId),targetCount:Number(r.targetCount??0),successCount:Number(r.successCount??0),failureCount:Number(r.failureCount??0),invalidatedTokenCount:Number(r.invalidatedTokenCount??0),dryRun:!!r.dryRun}}async tokens(){let e;try{e=await fetch(`${this.baseUrl}/tokens`,{method:"GET",headers:this.headers});}catch(r){let t=r instanceof Error?r.message:String(r);throw new Error(`[flare-admin] Network error (GET ${this.baseUrl}/tokens): ${t}`)}let n=await e.json().catch(()=>({}));if(!e.ok)throw new Error(`[flare-admin] Tokens fetch failed (HTTP ${e.status}): `+(n.error??"Unknown error"));return {appId:String(n.appId??this.cfg.appId),hasPushGateway:!!n.hasPushGateway,total:Number(n.total??0),tokens:Array.isArray(n.tokens)?n.tokens:[]}}};var C=class{constructor(e){this.cfg=e;this.ws=null;this.pendingAcks=new Map;this.subscriptions=new Map;this.activeSubscriptions=new Map;this.subscriptionErrorHandlers=new Map;this.subscriptionPermissionHandlers=new Map;this.subscriptionLastErrors=new Map;this.connected=false;this.shouldReconnect=true;this.reconnectDelay=2e3;let n=e.serverUrl.replace(/^http/,"ws");this.wsUrl=`${n}/?appId=${encodeURIComponent(e.appId)}&adminKey=${encodeURIComponent(e.adminKey)}`,this._readyPromise=new Promise(r=>{this._readyResolve=r;}),this._connect();}ready(){return this._readyPromise}disconnect(){this.shouldReconnect=false,this.ws?.close(1e3,"Admin disconnect"),this.ws=null,this.connected=false;}send(e,n){return new Promise((r,t)=>{let i=F(),s={id:i,type:e,ts:Date.now(),...n},a=setTimeout(()=>{this.pendingAcks.delete(i),t(new Error(`[flare-admin] WS request timeout (${e})`));},1e4);this.pendingAcks.set(i,l=>{clearTimeout(a),l.type==="error"?t(new Error(`[flare-admin] Server error: ${l.message}`)):r(l);});let c=()=>this.ws?.send(JSON.stringify(s));this.connected&&this.ws?.readyState===L.OPEN?c():this.ready().then(c).catch(t);})}subscribe(e,n,r,t,i={}){let s=F(),a={baseId:s,liveId:s,collection:e,docId:n,structuredQuery:r,callback:t,options:i};this.activeSubscriptions.set(s,a),this.subscriptionErrorHandlers.set(s,new Set),this.subscriptionPermissionHandlers.set(s,new Set),this.activateSubscription(a);let c=()=>{let p=this.activeSubscriptions.get(s)?.liveId??s;this.activeSubscriptions.delete(s),this.subscriptions.delete(s),this.subscriptions.delete(p),this.subscriptionErrorHandlers.delete(s),this.subscriptionPermissionHandlers.delete(s),this.subscriptionLastErrors.delete(s),this.connected&&this.send("unsubscribe",{subscriptionId:p}).catch(()=>{});},l=c;return l.unsubscribe=c,l.onError=h=>{this.subscriptionErrorHandlers.get(s)?.add(h);let p=this.subscriptionLastErrors.get(s);if(p)try{h(p);}catch{}return l},l.onPermissionDenied=h=>{this.subscriptionPermissionHandlers.get(s)?.add(h);let p=this.subscriptionLastErrors.get(s);if(p?.permissionDenied)try{h(p);}catch{}return l},l.catch=h=>l.onError(h),l}activateSubscription(e){let n=()=>{this.subscriptions.set(e.liveId,e.callback);let r={collection:e.collection};e.docId&&(r.docId=e.docId),e.structuredQuery&&(r.query=e.structuredQuery),e.options.skipSnapshot&&(r.skipSnapshot=true),this.send("subscribe",r).then(t=>{let i=t.subscriptionId;if(i&&i!==e.liveId){let s=this.subscriptions.get(e.liveId);s&&(this.subscriptions.delete(e.liveId),this.subscriptions.set(i,s),e.liveId=i);}}).catch(()=>{this.subscriptions.delete(e.liveId),this.emitSubscriptionError(e.baseId,this.toSubscriptionError(new Error("Subscribe failed")));});};this.connected?n():this.ready().then(n).catch(r=>{this.emitSubscriptionError(e.baseId,this.toSubscriptionError(r));});}async replayActiveSubscriptions(){if(!this.connected)return;let e=Array.from(this.activeSubscriptions.values());for(let n of e){if(!this.activeSubscriptions.has(n.baseId))continue;let r=n.liveId;this.subscriptions.delete(r),n.liveId=n.baseId,r&&r!==n.baseId&&await this.send("unsubscribe",{subscriptionId:r}).catch(()=>{}),this.activateSubscription(n);}}toSubscriptionError(e){let n=e instanceof Error?e.message:String(e??"Unknown subscription error"),r=n.match(/^\[([^\]]+)\]\s*(.*)$/),t=r?.[1],i=(r?.[2]??n).trim()||n,s=t==="PERMISSION_DENIED"||n.includes("PERMISSION_DENIED");return {code:t,message:i,permissionDenied:s,raw:e}}emitSubscriptionError(e,n){this.subscriptionLastErrors.set(e,n);let r=this.subscriptionErrorHandlers.get(e);if(r)for(let t of r)try{t(n);}catch{}if(n.permissionDenied){let t=this.subscriptionPermissionHandlers.get(e);if(t)for(let i of t)try{i(n);}catch{}}}_connect(){this._readyPromise=new Promise(e=>{this._readyResolve=e;}),this.ws=new L(this.wsUrl),this.ws.on("message",e=>{let n;try{n=JSON.parse(e.toString());}catch{return}this._handle(n);}),this.ws.on("close",e=>{this.connected=false,this.shouldReconnect&&e!==1e3&&setTimeout(()=>{this.reconnectDelay=Math.min(this.reconnectDelay*2,3e4),this._connect();},this.reconnectDelay);}),this.ws.on("error",()=>{});}_handle(e){let n=e.type;if(n==="auth_ok"){this.connected||(this.connected=true,this.reconnectDelay=2e3,this._readyResolve(),this.replayActiveSubscriptions().catch(()=>{}));return}if(n==="ack"||n==="pong"||n==="call_response"){let r=e.correlationId??e.id,t=this.pendingAcks.get(r);t&&(t(e),this.pendingAcks.delete(r));return}if(n==="error"){let r=e.correlationId;if(r){let t=this.pendingAcks.get(r);t&&(t(e),this.pendingAcks.delete(r));let i=Array.from(this.activeSubscriptions.values()).find(s=>s.liveId===r||s.baseId===r);if(i){let s=this.toSubscriptionError(new Error(`[${String(e.code??"ERROR")}] ${String(e.message??"Unknown error")}`));this.emitSubscriptionError(i.baseId,s);}}return}if(n==="snapshot"||n==="change"){let r=e.subscriptionId,t=this.subscriptions.get(r);if(t){let i=Array.from(this.activeSubscriptions.values()).find(c=>c.liveId===r),s=String(e.collection??i?.collection??""),a=n==="change"&&e.operation==="delete"?null:f(this.cfg,s,e.data,i?.structuredQuery);t({subscriptionId:r,collection:s,docId:e.docId,data:a,type:n==="snapshot"?"snapshot":"change",operation:e.operation});}}}};var b=class{constructor(e,n,r){this.conn=e;this.collection=n;this.id=r;}onSnapshot(e){let n=()=>{};return n=this.conn.subscribe(this.collection,this.id,void 0,r=>{r.type==="snapshot"&&(e(r),n());}),n}};var S=class o{constructor(e,n){this.conn=e;this.name=n;this.sq={};}clone(e){let n=new o(this.conn,this.name);return n.sq={...this.sq,...e},n}normalizeFilterValue(e,n){return e==="in"||e==="not-in"||e==="array-contains-any"?Array.isArray(n)?n:[n]:n}toQueryFilters(e){return k(e).map(r=>typeof r=="object"&&r!=null&&"field"in r&&"op"in r?{...r,value:this.normalizeFilterValue(r.op,r.value)}:r)}appendAndFilters(e){return this.clone({where:[...this.sq.where??[],...e]})}appendOrFilters(e){let n=[...this.sq.where??[]];if(n.length===0)return this.clone({where:[{or:e}]});let r=n[0];if(n.length===1&&typeof r=="object"&&r!=null&&"or"in r)return this.clone({where:[{or:[...r.or,...e]}]});let i=n.length===1?n[0]:{and:n};return this.clone({where:[{or:[i,...e]}]})}appendFilters(e,n){return n==="or"?this.appendOrFilters(e):this.appendAndFilters(e)}appendOperatorFilter(e,n,r,t){return this.appendFilters([{field:e,op:n,value:this.normalizeFilterValue(n,r)}],t)}where(e){return this.appendFilters(this.toQueryFilters(e),"and")}and(e){return this.appendFilters(this.toQueryFilters(e),"and")}or(e){return this.appendFilters(this.toQueryFilters(e),"or")}in(e,n){return this.appendOperatorFilter(e,"in",n,"and")}andIn(e,n){return this.appendOperatorFilter(e,"in",n,"and")}orIn(e,n){return this.appendOperatorFilter(e,"in",n,"or")}notIn(e,n){return this.appendOperatorFilter(e,"not-in",n,"and")}andNotIn(e,n){return this.appendOperatorFilter(e,"not-in",n,"and")}orNotIn(e,n){return this.appendOperatorFilter(e,"not-in",n,"or")}arrayContains(e,n){return this.appendOperatorFilter(e,"array-contains",n,"and")}andArrayContains(e,n){return this.appendOperatorFilter(e,"array-contains",n,"and")}orArrayContains(e,n){return this.appendOperatorFilter(e,"array-contains",n,"or")}arrayContainsAny(e,n){return this.appendOperatorFilter(e,"array-contains-any",n,"and")}andArrayContainsAny(e,n){return this.appendOperatorFilter(e,"array-contains-any",n,"and")}orArrayContainsAny(e,n){return this.appendOperatorFilter(e,"array-contains-any",n,"or")}some(e,n){return this.appendOperatorFilter(e,"elem-match",n,"and")}andSome(e,n){return this.appendOperatorFilter(e,"elem-match",n,"and")}orSome(e,n){return this.appendOperatorFilter(e,"elem-match",n,"or")}like(e,n){return this.appendOperatorFilter(e,"like",n,"and")}andLike(e,n){return this.appendOperatorFilter(e,"like",n,"and")}orLike(e,n){return this.appendOperatorFilter(e,"like",n,"or")}notLike(e,n){return this.appendOperatorFilter(e,"not-like",n,"and")}andNotLike(e,n){return this.appendOperatorFilter(e,"not-like",n,"and")}orNotLike(e,n){return this.appendOperatorFilter(e,"not-like",n,"or")}exists(e){return this.appendOperatorFilter(e,"exists",true,"and")}andExists(e){return this.appendOperatorFilter(e,"exists",true,"and")}orExists(e){return this.appendOperatorFilter(e,"exists",true,"or")}notExists(e){return this.appendOperatorFilter(e,"not-exists",true,"and")}andNotExists(e){return this.appendOperatorFilter(e,"not-exists",true,"and")}orNotExists(e){return this.appendOperatorFilter(e,"not-exists",true,"or")}latest(){return this.clone({orderBy:[...this.sq.orderBy??[],{field:"_seq",dir:"desc"}]})}newest(){return this.latest()}oldest(){return this.clone({orderBy:[...this.sq.orderBy??[],{field:"_seq",dir:"asc"}]})}orderBy(e,n="asc"){return this.clone({orderBy:[...this.sq.orderBy??[],{field:e,dir:n}]})}limit(e){return this.clone({limit:e})}offset(e){return this.clone({offset:e})}startAt(...e){return this.clone({startAt:{values:e}})}startAfter(...e){return this.clone({startAfter:{values:e}})}endAt(...e){return this.clone({endAt:{values:e}})}endBefore(...e){return this.clone({endBefore:{values:e}})}aggregate(...e){return this.clone({aggregate:[...this.sq.aggregate??[],...e]})}count(e="count"){return this.aggregate({fn:"count",alias:e})}sum(e,n){return this.aggregate({fn:"sum",field:e,alias:n??`sum_${e}`})}avg(e,n){return this.aggregate({fn:"avg",field:e,alias:n??`avg_${e}`})}min(e,n){return this.aggregate({fn:"min",field:e,alias:n??`min_${e}`})}max(e,n){return this.aggregate({fn:"max",field:e,alias:n??`max_${e}`})}distinct(e,n){return this.aggregate({fn:"distinct",field:e,alias:n??`distinct_${e}`})}groupBy(...e){return this.clone({groupBy:{fields:e}})}having(e,n,r){return this.clone({having:[...this.sq.having??[],{field:e,op:n,value:r}]})}buildStructuredJoin(e,n){let r={from:String(e??""),localField:String(n?.source??""),foreignField:String(n?.target??""),as:String(n?.as??""),single:n?.single};return Array.isArray(n?.where)&&(r.where=n.where),Array.isArray(n?.orderBy)&&(r.orderBy=n.orderBy),typeof n?.limit=="number"&&(r.limit=n.limit),typeof n?.offset=="number"&&(r.offset=n.offset),Array.isArray(n?.select)&&(r.select=n.select),Array.isArray(n?.joins)&&(r.joins=n.joins.map(t=>this.buildStructuredJoin(String(t?.collection??""),t))),r}cloneStructuredJoin(e){let n={...e};return Array.isArray(e.where)&&(n.where=e.where.map(r=>({...r}))),Array.isArray(e.orderBy)&&(n.orderBy=e.orderBy.map(r=>({...r}))),Array.isArray(e.select)&&(n.select=[...e.select]),Array.isArray(e.joins)&&(n.joins=e.joins.map(r=>this.cloneStructuredJoin(r))),n}appendNestedJoinByAlias(e,n,r){for(let t of e){if(t.as===n)return t.joins=[...t.joins??[],r],true;if(Array.isArray(t.joins)&&this.appendNestedJoinByAlias(t.joins,n,r))return true}return false}parseRelationRef(e){let r=String(e??"").trim().match(/^([A-Za-z0-9_.]+)\s*->\s*([A-Za-z0-9_]+)\.([A-Za-z0-9_.]+)(?:\s+as\s+([A-Za-z0-9_]+))?$/i);if(!r)throw new Error(`Invalid relation format: "${e}". Expected "source.path->collection.target"`);return {source:r[1],collection:r[2],target:r[3],alias:r[4]}}join(e,n){let r=this.buildStructuredJoin(e,n);return this.clone({joins:[...this.sq.joins??[],r]})}joinNested(e,n,r){let t=String(e??"").trim();if(!t)throw new Error("joinNested requires parentAlias");let i=(this.sq.joins??[]).map(a=>this.cloneStructuredJoin(a));if(i.length===0)throw new Error(`joinNested parent alias "${t}" not found`);let s=this.buildStructuredJoin(n,r);if(!this.appendNestedJoinByAlias(i,t,s))throw new Error(`joinNested parent alias "${t}" not found`);return this.clone({joins:i})}Join(e,n){return this.join(e,n)}JoinNested(e,n,r){return this.joinNested(e,n,r)}withRelation(e,n={}){let r=this.parseRelationRef(e);return this.join(r.collection,{...n,source:r.source,target:r.target,as:n.as??r.alias??r.collection})}select(...e){return this.clone({select:e})}distinctField(e){return this.clone({distinctField:e})}vectorSearch(e){return this.clone({vectorSearch:e})}doc(e){return new b(this.conn,this.name,e)}getRawQuery(){let e={...this.sq};return Array.isArray(this.sq.where)&&(e.where=this.sq.where.map(n=>({...n}))),Array.isArray(this.sq.orderBy)&&(e.orderBy=this.sq.orderBy.map(n=>({...n}))),Array.isArray(this.sq.aggregate)&&(e.aggregate=this.sq.aggregate.map(n=>({...n}))),Array.isArray(this.sq.having)&&(e.having=this.sq.having.map(n=>({...n}))),Array.isArray(this.sq.select)&&(e.select=[...this.sq.select]),Array.isArray(this.sq.joins)&&(e.joins=this.sq.joins.map(n=>this.cloneStructuredJoin(n))),this.sq.groupBy?.fields&&(e.groupBy={fields:[...this.sq.groupBy.fields]}),this.sq.startAt?.values&&(e.startAt={values:[...this.sq.startAt.values]}),this.sq.startAfter?.values&&(e.startAfter={values:[...this.sq.startAfter.values]}),this.sq.endAt?.values&&(e.endAt={values:[...this.sq.endAt.values]}),this.sq.endBefore?.values&&(e.endBefore={values:[...this.sq.endBefore.values]}),{collection:this.name,query:e}}onSnapshot(e){let n=this._buildSq(),r=()=>{};return r=this.conn.subscribe(this.name,void 0,n,t=>{t.type==="snapshot"&&(e(t),r());}),r}onDocAdded(e){let n=this._buildSq();return this.conn.subscribe(this.name,void 0,n,r=>{r.type==="change"&&r.operation==="insert"&&r.data!=null&&e(r.data,r.docId);},{skipSnapshot:true})}onDocUpdated(e){let n=this._buildSq();return this.conn.subscribe(this.name,void 0,n,r=>{r.type==="change"&&(r.operation==="update"||r.operation==="replace")&&r.data!=null&&e(r.data,r.docId);},{skipSnapshot:true})}onDocDeleted(e){let n=this._buildSq();return this.conn.subscribe(this.name,void 0,n,r=>{r.type==="change"&&r.operation==="delete"&&e(r.docId);},{skipSnapshot:true})}onDocChanged(e){let n=this._buildSq();return this.conn.subscribe(this.name,void 0,n,r=>{r.type==="change"&&e(r.data??null,r.docId,r.operation);},{skipSnapshot:true})}_buildSq(){let e={};return this.sq.where&&this.sq.where.length>0&&(e.where=this.sq.where),this.sq.orderBy&&this.sq.orderBy.length>0&&(e.orderBy=this.sq.orderBy),this.sq.limit!==void 0&&(e.limit=this.sq.limit),this.sq.offset!==void 0&&(e.offset=this.sq.offset),this.sq.startAt&&(e.startAt=this.sq.startAt),this.sq.startAfter&&(e.startAfter=this.sq.startAfter),this.sq.endAt&&(e.endAt=this.sq.endAt),this.sq.endBefore&&(e.endBefore=this.sq.endBefore),this.sq.aggregate&&this.sq.aggregate.length>0&&(e.aggregate=this.sq.aggregate),this.sq.groupBy&&(e.groupBy=this.sq.groupBy),this.sq.having&&this.sq.having.length>0&&(e.having=this.sq.having),this.sq.joins&&this.sq.joins.length>0&&(e.joins=this.sq.joins),this.sq.vectorSearch&&(e.vectorSearch=this.sq.vectorSearch),this.sq.select&&this.sq.select.length>0&&(e.select=this.sq.select),this.sq.distinctField&&(e.distinctField=this.sq.distinctField),e}};var v=class{constructor(e){this._ws=new C(e);}collection(e){return new S(this._ws,e)}ready(){return this._ws.ready()}disconnect(){this._ws.disconnect();}};var O=class{constructor(e){this.cfg={defaultTtl:"24h",...e,serverUrl:e.serverUrl.replace(/\/$/,""),dataMapper:e.dataMapper??{}};}auth(){return this._auth??(this._auth=new y(this.cfg))}db(){return this._db??(this._db=new A(this.cfg))}connection(){return this._conn??(this._conn=new v(this.cfg))}notifications(){return this._notifications??(this._notifications=new w(this.cfg))}},R=new Map;function we(o,e="[DEFAULT]"){if(R.has(e))return R.get(e);let n=new O(o);return R.set(e,n),n}function T(o="[DEFAULT]"){let e=R.get(o);if(!e)throw new Error(`[flare-admin] No app named "${o}" found. Call connectApp() before getApp().`);return e}function Ce(o="[DEFAULT]"){return T(o).auth()}function be(o="[DEFAULT]"){return T(o).db()}function Se(o="[DEFAULT]"){return T(o).connection()}function ve(o="[DEFAULT]"){return T(o).notifications()}export{m as AdminCollectionReference,g as AdminDocumentReference,S as AdminLiveCollectionReference,b as AdminLiveDocumentReference,O as FlareAdminApp,y as FlareAdminAuthService,v as FlareAdminConnection,A as FlareAdminDbService,w as FlareAdminNotificationsService,C as FlareAdminWsConnection,Ce as auth,we as connectApp,Se as connection,be as db,T as getApp,ve as notifications};
|
|
1
|
+
import ve from'fs';import I from'path';import {fileURLToPath}from'url';import be from'ws';var Z=(i=>(i.Upload="upload",i.Download="download",i.Delete="delete",i.Edit="edit",i))(Z||{});function G(r){let e=[];for(let[t,n]of Object.entries(r))if(typeof n=="string"){let i=n.match(/^(>=|<=|!=|>|<|==)\s*(.+)$/);if(i){let[,s,o]=i;e.push({field:t,op:s,value:we(o.trim())});}else e.push({field:t,op:"==",value:n});}else Array.isArray(n)?e.push({field:t,op:"in",value:n}):e.push({field:t,op:"==",value:n});return e}function we(r){return isNaN(Number(r))?r==="true"?true:r==="false"?false:r==="null"?null:r:Number(r)}function X(){return Math.random().toString(36).slice(2,12)+Date.now().toString(36)}function Ce(r,e){let t=r.dataMapper;if(!t||typeof t!="object")return null;let n=t[e];return typeof n=="function"?n:null}function Y(r,e,t){let n=Ce(r,e);if(!n||t==null||typeof t!="object")return t;try{return n(t)}catch{return t}}function ee(r,e,t){if(!e||typeof e!="object"||!Array.isArray(t)||t.length===0)return e;let n=e;for(let i of t){let s=String(i?.as??"").trim();if(!s)continue;let o=Array.isArray(i?.joins)?i.joins:[],a=n[s],d=a;Array.isArray(a)?d=a.map(c=>ee(r,c,o)):a&&typeof a=="object"&&(d=ee(r,a,o)),d=Array.isArray(d)?d.map(c=>Y(r,s,c)):Y(r,s,d),d!==a&&(n===e&&(n={...n}),n[s]=d);}return n}function v(r,e,t,n){let i=Array.isArray(n?.joins)?n.joins:[],s=o=>{let a=ee(r,o,i);return Y(r,e,a)};return Array.isArray(t)?t.map(o=>s(o)):s(t)}var Re=()=>typeof process<"u"&&!!process.versions?.node,f=r=>{if(r.transport==="http")return false;let e=!!(r.grpcUrl&&r.grpcUrl.trim().length>0),t=Re();if(r.transport==="grpc"){if(!e)throw new Error("[flare-admin][grpc] transport=grpc requires grpcUrl");if(!t)throw new Error("[flare-admin][grpc] transport=grpc requires Node.js runtime");return true}return e&&t},j=r=>{try{return JSON.stringify(r??{})}catch{return "{}"}},x=r=>r?.field&&r?.op?{queryFilter:{field:String(r.field),op:String(r.op),valueJson:j(r.value)}}:Array.isArray(r?.or)?{orFilter:{or:r.or.map(e=>x(e))}}:Array.isArray(r?.and)?{andFilter:{and:r.and.map(e=>x(e))}}:{queryFilter:{field:"",op:"==",valueJson:"null"}},Q=r=>{if(!(!r||!Array.isArray(r.values)))return {valuesJson:r.values.map(e=>j(e))}},ce=r=>({from:r.from,localField:r.localField,foreignField:r.foreignField,as:r.as,single:!!r.single,where:Array.isArray(r.where)?r.where.map(x):[],orderBy:Array.isArray(r.orderBy)?r.orderBy.map(e=>({field:e.field,dir:e.dir??"asc"})):[],limit:r.limit??0,offset:r.offset??0,select:Array.isArray(r.select)?r.select:[],joins:Array.isArray(r.joins)?r.joins.map(ce):[]}),Oe=r=>({where:Array.isArray(r.where)?r.where.map(x):[],orderBy:Array.isArray(r.orderBy)?r.orderBy.map(e=>({field:e.field,dir:e.dir??"asc"})):[],limit:r.limit??0,offset:r.offset??0,startAt:Q(r.startAt),startAfter:Q(r.startAfter),endAt:Q(r.endAt),endBefore:Q(r.endBefore),aggregate:Array.isArray(r.aggregate)?r.aggregate.map(e=>({fn:e.fn,field:e.field??"",alias:e.alias??""})):[],groupBy:r.groupBy?{fields:r.groupBy.fields??[]}:void 0,having:Array.isArray(r.having)?r.having.map(e=>({field:e.field,op:e.op,value:e.value})):[],joins:Array.isArray(r.joins)?r.joins.map(ce):[],select:Array.isArray(r.select)?r.select:[],distinctField:r.distinctField??""});async function S(r){let e=await import('@grpc/grpc-js'),t=await import('@grpc/proto-loader'),n=I.dirname(fileURLToPath(import.meta.url)),i=[I.resolve(n,"../../proto"),I.resolve(n,"../proto"),I.resolve(process.cwd(),"proto")],s=i.find(m=>ve.existsSync(I.join(m,"flare.proto")))??i[0],o=I.join(s,"flare.proto"),a=await t.load(o,{keepCase:false,longs:String,enums:String,defaults:true,oneofs:true,includeDirs:[s]}),c=e.loadPackageDefinition(a).flare;return new c.AdminService(r,e.credentials.createInsecure())}function k(r,e,t){return new Promise((n,i)=>{r[e](t,(s,o)=>{if(s)return i(s);n(o??{});});})}function w(r,e){if(r.error)throw new Error(`[flare-admin][grpc] ${e} failed: ${r.errorDescription||r.error}`)}async function de(r,e,t){if(!f(r))return null;let n=await S(r.grpcUrl),i=await k(n,"AdminQuery",{app_id:r.appId,admin_key:r.adminKey,collection:e,query:Oe(t)});w(i,"admin_query");try{let s=JSON.parse(i.dataJson||"[]");return Array.isArray(s)?s:[]}catch{return []}}async function le(r,e,t){if(!f(r))return null;let n=await S(r.grpcUrl),i=await k(n,"CreateCustomToken",{app_id:r.appId,admin_key:r.adminKey,uid:e,role:t.role??"user",claims_json:j(t.claims??{}),ttl:t.ttl??r.defaultTtl});if(w(i,"create_custom_token"),!i.token)throw new Error("[flare-admin][grpc] create_custom_token returned empty token");return String(i.token)}async function ue(r,e,t){if(!f(r))return null;let n=await S(r.grpcUrl),i=await k(n,"CreateAuthTicket",{app_id:r.appId,admin_key:r.adminKey,uid:e,role:t.role??"user",email:t.email??"",sid:t.sid??"",tag:t.tag??"",ttl_seconds:t.ttlSeconds??0,ip:t.ip??""});return w(i,"create_auth_ticket"),{ticket:String(i.ticket??""),tag:String(i.tag??""),uuid:String(i.uuid??""),expires_at:String(i.expiresAt??""),one_time:!!(i.oneTime??true),uid:String(i.uid??e),role:String(i.role??t.role??"user"),ip:String(i.ip??"unknown")}}async function me(r,e,t){if(!f(r))return;let n=await S(r.grpcUrl),i=await k(n,"GetDocument",{app_id:r.appId,admin_key:r.adminKey,collection:e,doc_id:t});if(w(i,"get_document"),!i.found)return null;try{return JSON.parse(i.dataJson||"{}")}catch{return {}}}async function pe(r,e,t){if(!f(r))return null;let n=await S(r.grpcUrl),i=await k(n,"CreateDocument",{app_id:r.appId,admin_key:r.adminKey,collection:e,data_json:j(t)});return w(i,"create_document"),String(i.id??"")}async function ge(r,e,t,n){if(!f(r))return null;let i=await S(r.grpcUrl),s=await k(i,"ReplaceDocument",{app_id:r.appId,admin_key:r.adminKey,collection:e,doc_id:t,data_json:j(n)});return w(s,"replace_document"),true}async function he(r,e,t,n){if(!f(r))return null;let i=await S(r.grpcUrl),s=await k(i,"UpdateDocument",{app_id:r.appId,admin_key:r.adminKey,collection:e,doc_id:t,data_json:j(n)});return w(s,"update_document"),true}async function fe(r,e,t){if(!f(r))return null;let n=await S(r.grpcUrl),i=await k(n,"DeleteDocument",{app_id:r.appId,admin_key:r.adminKey,collection:e,doc_id:t});return w(i,"delete_document"),!!i.deleted}async function Ae(r,e,t){if(!f(r))return null;let n=await S(r.grpcUrl),i=await k(n,"DeleteMany",{app_id:r.appId,admin_key:r.adminKey,collection:e,where:(t??[]).map(x)});return w(i,"delete_many"),Number(i.deleted??0)}var ye=r=>r.replace(/\/$/,""),Ie=r=>{let e=String(r.httpBase??"").trim();return ye(e||r.serverUrl)},A=(r,e)=>{let t=Ie(r),n=e.startsWith("/")?e:`/${e}`;return `${t}${n}`};var R=class{constructor(e,t,n){this.cfg=e;this.collection=t;this.id=n;}get baseUrl(){return A(this.cfg,`/admin/db/${this.cfg.appId}/${this.collection}/${this.id}`)}get headers(){return {"Content-Type":"application/json",Authorization:`Bearer ${this.cfg.adminKey}`}}async request(e,t){let n;try{n=await fetch(this.baseUrl,{method:e,headers:this.headers,...t!==void 0?{body:JSON.stringify(t)}:{}});}catch(s){let o=s instanceof Error?s.message:String(s);throw new Error(`[flare-admin] Network error (${e} ${this.baseUrl}): ${o}`)}let i=await n.json().catch(()=>({}));if(!n.ok)throw new Error(`[flare-admin] ${e} ${this.baseUrl} failed (HTTP ${n.status}): `+(i.error??"Unknown error"));return i}async get(){let e=await me(this.cfg,this.collection,this.id);if(e!==void 0)return e===null?null:v(this.cfg,this.collection,e)??null;let t;try{t=await fetch(this.baseUrl,{headers:this.headers});}catch(s){let o=s instanceof Error?s.message:String(s);throw new Error(`[flare-admin] Network error (GET ${this.baseUrl}): ${o}`)}if(t.status===404)return null;let n=await t.json().catch(()=>({}));if(!t.ok)throw new Error(`[flare-admin] GET ${this.baseUrl} failed (HTTP ${t.status}): `+(n.error??"Unknown error"));return v(this.cfg,this.collection,n.data)??null}async set(e){await ge(this.cfg,this.collection,this.id,e)===null&&await this.request("PUT",e);}async update(e){await he(this.cfg,this.collection,this.id,e)===null&&await this.request("PATCH",e);}async delete(){await fe(this.cfg,this.collection,this.id)===null&&await this.request("DELETE");}parent(){return new O(this.cfg,this.collection)}};var O=class r{constructor(e,t){this.cfg=e;this.name=t;this.sq={};this.opts={allowSensitiveAuthUserFields:true};}get baseUrl(){return A(this.cfg,`/admin/db/${this.cfg.appId}/${this.name}`)}get headers(){return {"Content-Type":"application/json",Authorization:`Bearer ${this.cfg.adminKey}`}}clone(e){let t=new r(this.cfg,this.name);return t.sq={...this.sq,...e},t.opts={...this.opts},t}allowSensitiveAuthUserFields(e=true){let t=this.clone({});return t.opts.allowSensitiveAuthUserFields=!!e,t}normalizeFilterValue(e,t){return e==="in"||e==="not-in"||e==="array-contains-any"?Array.isArray(t)?t:[t]:t}toQueryFilters(e){return G(e).map(n=>typeof n=="object"&&n!=null&&"field"in n&&"op"in n?{...n,value:this.normalizeFilterValue(n.op,n.value)}:n)}appendAndFilters(e){return this.clone({where:[...this.sq.where??[],...e]})}appendOrFilters(e){let t=[...this.sq.where??[]];if(t.length===0)return this.clone({where:[{or:e}]});let n=t[0];if(t.length===1&&typeof n=="object"&&n!=null&&"or"in n)return this.clone({where:[{or:[...n.or,...e]}]});let s=t.length===1?t[0]:{and:t};return this.clone({where:[{or:[s,...e]}]})}appendFilters(e,t){return t==="or"?this.appendOrFilters(e):this.appendAndFilters(e)}appendOperatorFilter(e,t,n,i){return this.appendFilters([{field:e,op:t,value:this.normalizeFilterValue(t,n)}],i)}where(e){return this.appendFilters(this.toQueryFilters(e),"and")}and(e){return this.appendFilters(this.toQueryFilters(e),"and")}or(e){return this.appendFilters(this.toQueryFilters(e),"or")}in(e,t){return this.appendOperatorFilter(e,"in",t,"and")}andIn(e,t){return this.appendOperatorFilter(e,"in",t,"and")}orIn(e,t){return this.appendOperatorFilter(e,"in",t,"or")}notIn(e,t){return this.appendOperatorFilter(e,"not-in",t,"and")}andNotIn(e,t){return this.appendOperatorFilter(e,"not-in",t,"and")}orNotIn(e,t){return this.appendOperatorFilter(e,"not-in",t,"or")}arrayContains(e,t){return this.appendOperatorFilter(e,"array-contains",t,"and")}andArrayContains(e,t){return this.appendOperatorFilter(e,"array-contains",t,"and")}orArrayContains(e,t){return this.appendOperatorFilter(e,"array-contains",t,"or")}arrayContainsAny(e,t){return this.appendOperatorFilter(e,"array-contains-any",t,"and")}andArrayContainsAny(e,t){return this.appendOperatorFilter(e,"array-contains-any",t,"and")}orArrayContainsAny(e,t){return this.appendOperatorFilter(e,"array-contains-any",t,"or")}some(e,t){return this.appendOperatorFilter(e,"elem-match",t,"and")}andSome(e,t){return this.appendOperatorFilter(e,"elem-match",t,"and")}orSome(e,t){return this.appendOperatorFilter(e,"elem-match",t,"or")}like(e,t){return this.appendOperatorFilter(e,"like",t,"and")}andLike(e,t){return this.appendOperatorFilter(e,"like",t,"and")}orLike(e,t){return this.appendOperatorFilter(e,"like",t,"or")}notLike(e,t){return this.appendOperatorFilter(e,"not-like",t,"and")}andNotLike(e,t){return this.appendOperatorFilter(e,"not-like",t,"and")}orNotLike(e,t){return this.appendOperatorFilter(e,"not-like",t,"or")}exists(e){return this.appendOperatorFilter(e,"exists",true,"and")}andExists(e){return this.appendOperatorFilter(e,"exists",true,"and")}orExists(e){return this.appendOperatorFilter(e,"exists",true,"or")}notExists(e){return this.appendOperatorFilter(e,"not-exists",true,"and")}andNotExists(e){return this.appendOperatorFilter(e,"not-exists",true,"and")}orNotExists(e){return this.appendOperatorFilter(e,"not-exists",true,"or")}latest(){return this.clone({orderBy:[...this.sq.orderBy??[],{field:"_seq",dir:"desc"}]})}newest(){return this.latest()}oldest(){return this.clone({orderBy:[...this.sq.orderBy??[],{field:"_seq",dir:"asc"}]})}orderBy(e,t="asc"){return this.clone({orderBy:[...this.sq.orderBy??[],{field:e,dir:t}]})}limit(e){return this.clone({limit:e})}offset(e){return this.clone({offset:e})}startAt(...e){return this.clone({startAt:{values:e}})}startAfter(...e){return this.clone({startAfter:{values:e}})}endAt(...e){return this.clone({endAt:{values:e}})}endBefore(...e){return this.clone({endBefore:{values:e}})}aggregate(...e){return this.clone({aggregate:[...this.sq.aggregate??[],...e]})}count(e="count"){return this.aggregate({fn:"count",alias:e})}sum(e,t){return this.aggregate({fn:"sum",field:e,alias:t??`sum_${e}`})}avg(e,t){return this.aggregate({fn:"avg",field:e,alias:t??`avg_${e}`})}min(e,t){return this.aggregate({fn:"min",field:e,alias:t??`min_${e}`})}max(e,t){return this.aggregate({fn:"max",field:e,alias:t??`max_${e}`})}distinct(e,t){return this.aggregate({fn:"distinct",field:e,alias:t??`distinct_${e}`})}groupBy(...e){return this.clone({groupBy:{fields:e}})}having(e,t,n){return this.clone({having:[...this.sq.having??[],{field:e,op:t,value:n}]})}buildStructuredJoin(e,t){let n={from:String(e??""),localField:String(t?.source??""),foreignField:String(t?.target??""),as:String(t?.as??""),single:t?.single};return Array.isArray(t?.where)&&(n.where=t.where),Array.isArray(t?.orderBy)&&(n.orderBy=t.orderBy),typeof t?.limit=="number"&&(n.limit=t.limit),typeof t?.offset=="number"&&(n.offset=t.offset),Array.isArray(t?.select)&&(n.select=t.select),Array.isArray(t?.joins)&&(n.joins=t.joins.map(i=>this.buildStructuredJoin(String(i?.collection??""),i))),n}cloneStructuredJoin(e){let t={...e};return Array.isArray(e.where)&&(t.where=e.where.map(n=>({...n}))),Array.isArray(e.orderBy)&&(t.orderBy=e.orderBy.map(n=>({...n}))),Array.isArray(e.select)&&(t.select=[...e.select]),Array.isArray(e.joins)&&(t.joins=e.joins.map(n=>this.cloneStructuredJoin(n))),t}appendNestedJoinByAlias(e,t,n){for(let i of e){if(i.as===t)return i.joins=[...i.joins??[],n],true;if(Array.isArray(i.joins)&&this.appendNestedJoinByAlias(i.joins,t,n))return true}return false}parseRelationRef(e){let n=String(e??"").trim().match(/^([A-Za-z0-9_.]+)\s*->\s*([A-Za-z0-9_]+)\.([A-Za-z0-9_.]+)(?:\s+as\s+([A-Za-z0-9_]+))?$/i);if(!n)throw new Error(`Invalid relation format: "${e}". Expected "source.path->collection.target"`);return {source:n[1],collection:n[2],target:n[3],alias:n[4]}}join(e,t){let n=this.buildStructuredJoin(e,t);return this.clone({joins:[...this.sq.joins??[],n]})}joinNested(e,t,n){let i=String(e??"").trim();if(!i)throw new Error("joinNested requires parentAlias");let s=(this.sq.joins??[]).map(a=>this.cloneStructuredJoin(a));if(s.length===0)throw new Error(`joinNested parent alias "${i}" not found`);let o=this.buildStructuredJoin(t,n);if(!this.appendNestedJoinByAlias(s,i,o))throw new Error(`joinNested parent alias "${i}" not found`);return this.clone({joins:s})}Join(e,t){return this.join(e,t)}JoinNested(e,t,n){return this.joinNested(e,t,n)}withRelation(e,t={}){let n=this.parseRelationRef(e);return this.join(n.collection,{...t,source:n.source,target:n.target,as:t.as??n.alias??n.collection})}select(...e){return this.clone({select:e})}distinctField(e){return this.clone({distinctField:e})}vectorSearch(e){return this.clone({vectorSearch:e})}getRawQuery(){let e={...this.sq};return Array.isArray(this.sq.where)&&(e.where=this.sq.where.map(t=>({...t}))),Array.isArray(this.sq.orderBy)&&(e.orderBy=this.sq.orderBy.map(t=>({...t}))),Array.isArray(this.sq.aggregate)&&(e.aggregate=this.sq.aggregate.map(t=>({...t}))),Array.isArray(this.sq.having)&&(e.having=this.sq.having.map(t=>({...t}))),Array.isArray(this.sq.select)&&(e.select=[...this.sq.select]),Array.isArray(this.sq.joins)&&(e.joins=this.sq.joins.map(t=>this.cloneStructuredJoin(t))),this.sq.groupBy?.fields&&(e.groupBy={fields:[...this.sq.groupBy.fields]}),this.sq.startAt?.values&&(e.startAt={values:[...this.sq.startAt.values]}),this.sq.startAfter?.values&&(e.startAfter={values:[...this.sq.startAfter.values]}),this.sq.endAt?.values&&(e.endAt={values:[...this.sq.endAt.values]}),this.sq.endBefore?.values&&(e.endBefore={values:[...this.sq.endBefore.values]}),{collection:this.name,query:e}}async get(){let e=await de(this.cfg,this.name,this.sq);if(e)return v(this.cfg,this.name,e,this.sq)??[];let t=new URL(this.baseUrl);t.searchParams.set("query",JSON.stringify(this.sq)),t.searchParams.set("allowSensitiveAuthUserFields",this.opts.allowSensitiveAuthUserFields?"1":"0");let n;try{n=await fetch(t.toString(),{headers:this.headers});}catch(o){let a=o instanceof Error?o.message:String(o);throw new Error(`[flare-admin] Network error (GET ${this.baseUrl}): ${a}`)}let i=await n.json().catch(()=>({}));if(!n.ok)throw new Error(`[flare-admin] GET ${this.baseUrl} failed (HTTP ${n.status}): ${i.error??"Unknown error"}`);return v(this.cfg,this.name,i.data,this.sq)??[]}async first(){let e=await this.get();return e.length>0?e[0]:null}async last(){let e=await this.get();return e.length>0?e[e.length-1]:null}async add(e){let t=await pe(this.cfg,this.name,e);if(t!==null)return new R(this.cfg,this.name,t);let n;try{n=await fetch(this.baseUrl,{method:"POST",headers:this.headers,body:JSON.stringify(e)});}catch(s){let o=s instanceof Error?s.message:String(s);throw new Error(`[flare-admin] Network error (POST ${this.baseUrl}): ${o}`)}let i=await n.json().catch(()=>({}));if(!n.ok)throw new Error(`[flare-admin] POST ${this.baseUrl} failed (HTTP ${n.status}): ${i.error??"Unknown error"}`);return new R(this.cfg,this.name,i.id)}async deleteMany(){let e=await Ae(this.cfg,this.name,this.sq.where??[]);if(e!==null)return e;let t;try{t=await fetch(this.baseUrl,{method:"DELETE",headers:this.headers,body:JSON.stringify({where:this.sq.where??[]})});}catch(i){let s=i instanceof Error?i.message:String(i);throw new Error(`[flare-admin] Network error (DELETE ${this.baseUrl}): ${s}`)}let n=await t.json().catch(()=>({}));if(!t.ok)throw new Error(`[flare-admin] DELETE ${this.baseUrl} failed (HTTP ${t.status}): ${n.error??"Unknown error"}`);return n.deleted??0}doc(e){return new R(this.cfg,this.name,e)}};var B=class{constructor(e){this.cfg=e;}collection(e){return new O(this.cfg,e)}};var _=class{constructor(e){this.cfg=e;}async createCustomToken(e,t={}){if(f(this.cfg)){let a=await le(this.cfg,String(e),t);if(!a)throw new Error("[flare-admin][grpc] create_custom_token returned empty token");return a}let n=A(this.cfg,"/admin/token"),i=JSON.stringify({appId:this.cfg.appId,uid:String(e),role:t.role??"user",claims:t.claims??{},ttl:t.ttl??this.cfg.defaultTtl}),s;try{s=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.cfg.adminKey}`},body:i});}catch(a){let d=a instanceof Error?a.message:String(a);throw new Error(`[flare-admin] Could not reach FlareServer at "${n}": ${d}
|
|
2
|
+
Make sure FLARE_URL is set correctly and the server is running.`)}let o;try{o=await s.json();}catch{throw new Error(`[flare-admin] Server returned non-JSON response (status ${s.status})`)}if(!s.ok)throw new Error(`[flare-admin] createCustomToken failed (HTTP ${s.status}): `+(o.error??"Unknown error"));if(typeof o.token!="string")throw new Error('[flare-admin] Server response missing "token" field');return o.token}async getTicket(e,t={}){if(f(this.cfg)){let d=await ue(this.cfg,String(e),t);if(!d)throw new Error("[flare-admin][grpc] create_auth_ticket returned empty payload");return d}let n=A(this.cfg,"/admin/ticket"),i=JSON.stringify({appId:this.cfg.appId,uid:String(e),role:t.role??"user",email:t.email,sid:t.sid,tag:t.tag,ttlSeconds:t.ttlSeconds,ip:t.ip}),s;try{s=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.cfg.adminKey}`},body:i});}catch(d){let c=d instanceof Error?d.message:String(d);throw new Error(`[flare-admin] Could not reach FlareServer at "${n}": ${c}
|
|
3
|
+
Make sure FLARE_URL is set correctly and the server is running.`)}let o;try{o=await s.json();}catch{throw new Error(`[flare-admin] Server returned non-JSON response (status ${s.status})`)}if(!s.ok)throw new Error(`[flare-admin] getTicket failed (HTTP ${s.status}): `+(o.error??"Unknown error"));let a=o.ticket;if(!a||typeof a.ticket!="string")throw new Error('[flare-admin] Server response missing "ticket" object');return {ticket:String(a.ticket),tag:String(a.tag??""),uuid:String(a.uuid??""),expires_at:String(a.expires_at??""),one_time:!!(a.one_time??true),uid:String(a.uid??String(e)),role:String(a.role??t.role??"user"),ip:String(a.ip??"unknown")}}};var E=class{constructor(e){this.cfg=e;}get baseUrl(){return A(this.cfg,`/admin/notify/${this.cfg.appId}`)}get headers(){return {"Content-Type":"application/json",Authorization:`Bearer ${this.cfg.adminKey}`}}async send(e){let t;try{t=await fetch(`${this.baseUrl}/send`,{method:"POST",headers:this.headers,body:JSON.stringify(e??{})});}catch(i){let s=i instanceof Error?i.message:String(i);throw new Error(`[flare-admin] Network error (POST ${this.baseUrl}/send): ${s}`)}let n=await t.json().catch(()=>({}));if(!t.ok)throw new Error(`[flare-admin] Notification send failed (HTTP ${t.status}): `+(n.error??"Unknown error"));return {sent:!!n.sent,appId:String(n.appId??this.cfg.appId),targetCount:Number(n.targetCount??0),successCount:Number(n.successCount??0),failureCount:Number(n.failureCount??0),invalidatedTokenCount:Number(n.invalidatedTokenCount??0),dryRun:!!n.dryRun}}async tokens(){let e;try{e=await fetch(`${this.baseUrl}/tokens`,{method:"GET",headers:this.headers});}catch(n){let i=n instanceof Error?n.message:String(n);throw new Error(`[flare-admin] Network error (GET ${this.baseUrl}/tokens): ${i}`)}let t=await e.json().catch(()=>({}));if(!e.ok)throw new Error(`[flare-admin] Tokens fetch failed (HTTP ${e.status}): `+(t.error??"Unknown error"));return {appId:String(t.appId??this.cfg.appId),hasPushGateway:!!t.hasPushGateway,total:Number(t.total??0),tokens:Array.isArray(t.tokens)?t.tokens:[]}}};var z=class{constructor(e){this.cfg=e;}base(e){return A(this.cfg,`/admin/storage/${encodeURIComponent(this.cfg.appId)}${e}`)}get headers(){return {"Content-Type":"application/json",Authorization:`Bearer ${this.cfg.adminKey}`}}async jsonRequest(e,t,n){let i,s=this.base(t);try{i=await fetch(s,{method:e,headers:this.headers,...n!==void 0?{body:JSON.stringify(n)}:{}});}catch(a){let d=a instanceof Error?a.message:String(a);throw new Error(`[flare-admin] Network error (${e} ${s}): ${d}`)}let o=await i.json().catch(()=>({}));if(!i.ok)throw new Error(`[flare-admin] ${e} ${s} failed (HTTP ${i.status}): `+(o.error??"Unknown error"));return o}async request(e,t,n){return this.jsonRequest(e,t,n)}async servers(){let e=await this.jsonRequest("GET","/servers");return Array.isArray(e.servers)?e.servers:[]}async createServer(e){let t=await this.jsonRequest("POST","/servers",e??{});return {ok:!!(t.ok??true),serverId:String(t.serverId??"")}}async patchServer(e,t){let n=String(e??"").trim();if(!n)throw new Error("storage server id is required");let i=await this.jsonRequest("POST",`/servers/${encodeURIComponent(n)}`,t??{});return {ok:!!(i.ok??true),serverId:String(i.serverId??n)}}async deleteServer(e){let t=String(e??"").trim();if(!t)throw new Error("storage server id is required");let n=await this.jsonRequest("DELETE",`/servers/${encodeURIComponent(t)}`);return {ok:!!(n.ok??true),serverId:String(n.serverId??t),removedObjects:Number(n.removedObjects??0)}}async awsConfig(e){let t=String(e??"").trim();if(!t)throw new Error("storage server id is required");return (await this.jsonRequest("GET",`/servers/${encodeURIComponent(t)}/aws`)).aws??{}}async uploadObject(e){return await this.jsonRequest("POST","/object/upload",e??{})}async downloadObject(e){return await this.jsonRequest("POST","/object/download",e??{})}async deleteObject(e){return await this.jsonRequest("POST","/object/delete",e??{})}async createSignedUrl(e){return await this.jsonRequest("POST","/signed-url",e??{})}async setRules(e){let t=await this.jsonRequest("POST","/rules",{...e?.rules?{rules:e.rules}:{},...typeof e?.rulesDsl=="string"?{rulesDsl:e.rulesDsl}:{},...e?.rulesHistoryPolicy?{rulesHistoryPolicy:e.rulesHistoryPolicy}:{}});return {id:String(t.id??this.cfg.appId)}}async validateRules(e){let t=await this.jsonRequest("POST","/rules/validate",{rulesDsl:e});return {valid:!!t.valid,diagnostics:Array.isArray(t.diagnostics)?t.diagnostics:[],rulesCount:Number(t.rulesCount??0)}}async rulesHistory(){let e=await this.jsonRequest("GET","/rules/history");return {history:Array.isArray(e.history)?e.history:[],policy:e.policy??{maxEntries:30,maxAgeDays:365},restoreEvents:Array.isArray(e.restoreEvents)?e.restoreEvents:[]}}async restoreRules(e){let t=String(e??"").trim();if(!t)throw new Error("historyId is required");let n=await this.jsonRequest("POST","/rules/restore",{historyId:t});return {id:String(n.id??this.cfg.appId),rulesText:String(n.rulesText??"")}}};function te(r){if(typeof Buffer<"u")return Buffer.from(r).toString("base64");let e="";for(let t=0;t<r.length;t++)e+=String.fromCharCode(r[t]);return btoa(e)}async function je(r){return r===void 0?"":typeof r=="string"?te(new TextEncoder().encode(r)):Buffer&&Buffer.isBuffer(r)?r.toString("base64"):r instanceof Uint8Array?te(r):r instanceof ArrayBuffer?te(new Uint8Array(r)):""}function Fe(r){return Buffer?Buffer.from(String(r)).toString("base64"):btoa(String(r))}function Pe(r){try{let e=Buffer?Buffer.from(r,"base64").toString():atob(r),t=parseInt(e,10);return isNaN(t)?0:t}catch{return 0}}function xe(r){return (String(r??"").split("/").pop()??"download").replace(/["\\\r\n]/g,"")||"download"}var D=class{constructor(e){this._bucketCache=new Map;this._bucketListLoaded=false;this._bucketListPromise=null;this._svc=e;}async _ensureBuckets(){if(!this._bucketListLoaded)return this._bucketListPromise||(this._bucketListPromise=this._loadBuckets().then(()=>{this._bucketListLoaded=true;}).catch(e=>{throw this._bucketListPromise=null,e}).finally(()=>{this._bucketListPromise=null;})),this._bucketListPromise}async _loadBuckets(){let e=await this.listBuckets();for(let t of e)this._bucketCache.set(t.bucket,t.id);}_invalidateCache(){this._bucketCache.clear(),this._bucketListLoaded=false,this._bucketListPromise=null;}async _resolveBucketId(e,t){let n=this._bucketCache.get(e);if(n)return n;await this._ensureBuckets();let i=this._bucketCache.get(e);if(i)return i;if(t)return (await this.createBucket(e)).id;throw new Error(`Bucket "${e}" not found. Create it first with createBucket("${e}").`)}async createBucket(e,t={}){let n=e.trim();if(!n)throw new Error("bucket name is required");await this._ensureBuckets();let i=this._bucketCache.get(n);if(i){let d=(await this.listBuckets()).find(c=>c.id===i);if(d)return d}let s;try{s=await this._svc.createServer({name:n,kind:t.kind??"managed",bucket:n,prefix:t.prefix,region:t.region,endpoint:t.endpoint,accessKey:t.accessKey,secretKey:t.secretKey,dataDir:t.dataDir,forcePathStyle:t.forcePathStyle});}catch(a){if((a instanceof Error?a.message:String(a)).includes("bucket_conflict")){this._invalidateCache(),await this._ensureBuckets();let c=this._bucketCache.get(n);if(c){let u=(await this.listBuckets()).find(b=>b.id===c);if(u)return u}}throw a}let o={id:s.serverId,name:n,bucket:n,kind:t.kind??"managed",prefix:t.prefix};return this._bucketCache.set(n,o.id),o}async listBuckets(){let t=(await this._svc.servers()).map(n=>({id:n.id,name:n.name,bucket:n.bucket,kind:n.kind,region:n.region,endpoint:n.endpoint,prefix:n.prefix,frozen:n.frozen,readOnly:n.readOnly,createdAt:n.createdAt,updatedAt:n.updatedAt}));this._bucketCache.clear();for(let n of t)this._bucketCache.set(n.bucket,n.id);return this._bucketListLoaded=true,t}async deleteBucket(e){let t=await this._resolveBucketId(e,false),n=await this._svc.deleteServer(t);return this._bucketCache.delete(e),{ok:n.ok,removedObjects:n.removedObjects}}async deleteBuckets(e){let t=[],n={};return await Promise.all(e.map(async i=>{try{await this.deleteBucket(i),t.push(i);}catch(s){n[i]=s instanceof Error?s.message:String(s);}})),{ok:Object.keys(n).length===0,deleted:t,errors:n}}async getBucketLocation(e){let n=(await this.listBuckets()).find(i=>i.bucket===e||i.name===e);if(!n)throw new Error(`Bucket "${e}" not found`);return {bucket:n.bucket,kind:n.kind,region:n.region,endpoint:n.endpoint}}async putObject(e){let t=await this._resolveBucketId(e.bucket,true);if(e.contentBase64!==void 0){let c=await this._svc.uploadObject({serverId:t,path:e.key,contentBase64:e.contentBase64,contentType:e.contentType,encrypt:e.encrypt});return {ok:c.ok,bucket:e.bucket,key:String(c.path??e.key),size:Number(c.size??0),encrypted:!!c.encrypted}}let n=4*1024*1024,i=e.base64MaxBytes??n,s;if(typeof e.body=="string"?s=new TextEncoder().encode(e.body).length:(e.body instanceof Uint8Array||e.body instanceof ArrayBuffer||Buffer&&Buffer.isBuffer(e.body))&&(s=e.body.byteLength),s!==void 0&&s>i){let c=await this.createSignedUrl({bucket:e.bucket,key:e.key,action:"upload",expiresInSeconds:300,sizeBytes:s,contentType:e.contentType,encrypt:e.encrypt}),m;typeof e.body=="string"?m=new TextEncoder().encode(e.body):m=e.body;let u=await fetch(c.url,{method:c.method??"PUT",headers:{...e.contentType?{"Content-Type":e.contentType}:{}},body:m});if(!u.ok){let b=await u.text().catch(()=>"");throw new Error(`[flare-admin] Signed-URL upload failed (HTTP ${u.status}): ${b}`)}return {ok:true,bucket:e.bucket,key:e.key,size:s??0,encrypted:!!(e.encrypt??true)}}let a=await je(e.body),d=await this._svc.uploadObject({serverId:t,path:e.key,contentBase64:a,contentType:e.contentType,encrypt:e.encrypt});return {ok:d.ok,bucket:e.bucket,key:String(d.path??e.key),size:Number(d.size??0),encrypted:!!d.encrypted}}async getObject(e){let t=await this._resolveBucketId(e.bucket,false),n=await this._svc.downloadObject({serverId:t,path:e.key,decrypt:e.decrypt});return {ok:n.ok,bucket:e.bucket,key:String(n.path??e.key),contentBase64:String(n.contentBase64??""),contentType:String(n.contentType??"application/octet-stream"),size:Number(n.size??0),encrypted:!!n.encrypted}}async getObjectUrl(e){return (await this.createSignedUrl({bucket:e.bucket,key:e.key,action:"download",decrypt:e.decrypt,expiresInSeconds:e.expiresInSeconds,forceDownload:e.forceDownload,allowedOrigins:e.allowedOrigins,embedOnly:e.embedOnly})).url}async downloadObject(e){let t=e.forceDownload??true,n=await this.getObjectUrl({...e,forceDownload:t}),i=e.filename??xe(e.key),s=globalThis.document;if(!s)return {ok:true,url:n,filename:i,triggered:false};let o=s.createElement("a");return o.href=n,e.openInNewTab?o.target="_blank":o.download=i,o.rel="noopener noreferrer",s.body?.appendChild(o),o.click(),o.remove(),{ok:true,url:n,filename:i,triggered:true}}async headObject(e){let t=await this._resolveBucketId(e.bucket,false),n=await this._svc.request("POST","/object/head",{serverId:t,path:e.key});return {bucket:e.bucket,key:String(n.path??e.key),size:Number(n.size??0),contentType:String(n.contentType??"application/octet-stream"),encrypted:!!n.encrypted,createdAt:n.createdAt,updatedAt:n.updatedAt}}async headObjects(e){return Promise.all(e.keys.map(t=>this.headObject({bucket:e.bucket,key:t})))}async listObjects(e){let t=await this._resolveBucketId(e.bucket,false),n=e.cursor?Pe(e.cursor):0,i=Math.max(1,Math.min(e.limit??100,1e3)),s=await this._svc.request("POST","/object/list",{serverId:t,prefix:e.prefix,limit:i,skip:n}),o=Array.isArray(s.objects)?s.objects:[],a=!!s.hasMore,d=Number(s.count??o.length);return {bucket:e.bucket,objects:o.map(c=>({bucket:e.bucket,key:String(c.path??c.key??""),size:Number(c.size??0),contentType:String(c.contentType??"application/octet-stream"),encrypted:!!c.encrypted,createdAt:c.createdAt,updatedAt:c.updatedAt})),count:d,hasMore:a,cursor:a?Fe(n+o.length):void 0}}async copyObject(e){let[t,n]=await Promise.all([this._resolveBucketId(e.sourceBucket,false),this._resolveBucketId(e.destBucket,true)]);return {ok:!!((await this._svc.request("POST","/object/copy",{serverId:t,path:e.sourceKey,destServerId:n,destPath:e.destKey})).ok??true)}}async copyObjects(e){let t={};return await Promise.all(e.map(async n=>{try{await this.copyObject(n);}catch(i){t[`${n.sourceBucket}/${n.sourceKey}`]=i instanceof Error?i.message:String(i);}})),{ok:Object.keys(t).length===0,errors:t}}async deleteObject(e){return this._svc.deleteObject(e)}async deleteObjects(e){let t=await this._resolveBucketId(e.bucket,false),n=await this._svc.request("POST","/object/delete-many",{serverId:t,paths:e.keys});return {ok:!!(n.ok??true),deleted:Array.isArray(n.deleted)?n.deleted:e.keys,errors:n.errors??{}}}async createSignedUrl(e){let t=await this._resolveBucketId(e.bucket,false);return await this._svc.request("POST","/signed-url",{serverId:t,path:e.key,action:e.action,expiresInSeconds:e.expiresInSeconds,sizeBytes:e.sizeBytes,contentType:e.contentType,encrypt:e.encrypt,decrypt:e.decrypt,forceDownload:e.forceDownload,allowedOrigins:e.allowedOrigins,embedOnly:e.embedOnly})}};var q=class{constructor(e,t,n){this.conn=e;this.collection=t;this.id=n;}onSnapshot(e){let t=()=>{};return t=this.conn.subscribe(this.collection,this.id,void 0,n=>{n.type==="snapshot"&&(e(n),t());}),t}};var L=class r{constructor(e,t){this.conn=e;this.name=t;this.sq={};}clone(e){let t=new r(this.conn,this.name);return t.sq={...this.sq,...e},t}normalizeFilterValue(e,t){return e==="in"||e==="not-in"||e==="array-contains-any"?Array.isArray(t)?t:[t]:t}toQueryFilters(e){return G(e).map(n=>typeof n=="object"&&n!=null&&"field"in n&&"op"in n?{...n,value:this.normalizeFilterValue(n.op,n.value)}:n)}appendAndFilters(e){return this.clone({where:[...this.sq.where??[],...e]})}appendOrFilters(e){let t=[...this.sq.where??[]];if(t.length===0)return this.clone({where:[{or:e}]});let n=t[0];if(t.length===1&&typeof n=="object"&&n!=null&&"or"in n)return this.clone({where:[{or:[...n.or,...e]}]});let s=t.length===1?t[0]:{and:t};return this.clone({where:[{or:[s,...e]}]})}appendFilters(e,t){return t==="or"?this.appendOrFilters(e):this.appendAndFilters(e)}appendOperatorFilter(e,t,n,i){return this.appendFilters([{field:e,op:t,value:this.normalizeFilterValue(t,n)}],i)}where(e){return this.appendFilters(this.toQueryFilters(e),"and")}and(e){return this.appendFilters(this.toQueryFilters(e),"and")}or(e){return this.appendFilters(this.toQueryFilters(e),"or")}in(e,t){return this.appendOperatorFilter(e,"in",t,"and")}andIn(e,t){return this.appendOperatorFilter(e,"in",t,"and")}orIn(e,t){return this.appendOperatorFilter(e,"in",t,"or")}notIn(e,t){return this.appendOperatorFilter(e,"not-in",t,"and")}andNotIn(e,t){return this.appendOperatorFilter(e,"not-in",t,"and")}orNotIn(e,t){return this.appendOperatorFilter(e,"not-in",t,"or")}arrayContains(e,t){return this.appendOperatorFilter(e,"array-contains",t,"and")}andArrayContains(e,t){return this.appendOperatorFilter(e,"array-contains",t,"and")}orArrayContains(e,t){return this.appendOperatorFilter(e,"array-contains",t,"or")}arrayContainsAny(e,t){return this.appendOperatorFilter(e,"array-contains-any",t,"and")}andArrayContainsAny(e,t){return this.appendOperatorFilter(e,"array-contains-any",t,"and")}orArrayContainsAny(e,t){return this.appendOperatorFilter(e,"array-contains-any",t,"or")}some(e,t){return this.appendOperatorFilter(e,"elem-match",t,"and")}andSome(e,t){return this.appendOperatorFilter(e,"elem-match",t,"and")}orSome(e,t){return this.appendOperatorFilter(e,"elem-match",t,"or")}like(e,t){return this.appendOperatorFilter(e,"like",t,"and")}andLike(e,t){return this.appendOperatorFilter(e,"like",t,"and")}orLike(e,t){return this.appendOperatorFilter(e,"like",t,"or")}notLike(e,t){return this.appendOperatorFilter(e,"not-like",t,"and")}andNotLike(e,t){return this.appendOperatorFilter(e,"not-like",t,"and")}orNotLike(e,t){return this.appendOperatorFilter(e,"not-like",t,"or")}exists(e){return this.appendOperatorFilter(e,"exists",true,"and")}andExists(e){return this.appendOperatorFilter(e,"exists",true,"and")}orExists(e){return this.appendOperatorFilter(e,"exists",true,"or")}notExists(e){return this.appendOperatorFilter(e,"not-exists",true,"and")}andNotExists(e){return this.appendOperatorFilter(e,"not-exists",true,"and")}orNotExists(e){return this.appendOperatorFilter(e,"not-exists",true,"or")}latest(){return this.clone({orderBy:[...this.sq.orderBy??[],{field:"_seq",dir:"desc"}]})}newest(){return this.latest()}oldest(){return this.clone({orderBy:[...this.sq.orderBy??[],{field:"_seq",dir:"asc"}]})}orderBy(e,t="asc"){return this.clone({orderBy:[...this.sq.orderBy??[],{field:e,dir:t}]})}limit(e){return this.clone({limit:e})}offset(e){return this.clone({offset:e})}startAt(...e){return this.clone({startAt:{values:e}})}startAfter(...e){return this.clone({startAfter:{values:e}})}endAt(...e){return this.clone({endAt:{values:e}})}endBefore(...e){return this.clone({endBefore:{values:e}})}aggregate(...e){return this.clone({aggregate:[...this.sq.aggregate??[],...e]})}count(e="count"){return this.aggregate({fn:"count",alias:e})}sum(e,t){return this.aggregate({fn:"sum",field:e,alias:t??`sum_${e}`})}avg(e,t){return this.aggregate({fn:"avg",field:e,alias:t??`avg_${e}`})}min(e,t){return this.aggregate({fn:"min",field:e,alias:t??`min_${e}`})}max(e,t){return this.aggregate({fn:"max",field:e,alias:t??`max_${e}`})}distinct(e,t){return this.aggregate({fn:"distinct",field:e,alias:t??`distinct_${e}`})}groupBy(...e){return this.clone({groupBy:{fields:e}})}having(e,t,n){return this.clone({having:[...this.sq.having??[],{field:e,op:t,value:n}]})}buildStructuredJoin(e,t){let n={from:String(e??""),localField:String(t?.source??""),foreignField:String(t?.target??""),as:String(t?.as??""),single:t?.single};return Array.isArray(t?.where)&&(n.where=t.where),Array.isArray(t?.orderBy)&&(n.orderBy=t.orderBy),typeof t?.limit=="number"&&(n.limit=t.limit),typeof t?.offset=="number"&&(n.offset=t.offset),Array.isArray(t?.select)&&(n.select=t.select),Array.isArray(t?.joins)&&(n.joins=t.joins.map(i=>this.buildStructuredJoin(String(i?.collection??""),i))),n}cloneStructuredJoin(e){let t={...e};return Array.isArray(e.where)&&(t.where=e.where.map(n=>({...n}))),Array.isArray(e.orderBy)&&(t.orderBy=e.orderBy.map(n=>({...n}))),Array.isArray(e.select)&&(t.select=[...e.select]),Array.isArray(e.joins)&&(t.joins=e.joins.map(n=>this.cloneStructuredJoin(n))),t}appendNestedJoinByAlias(e,t,n){for(let i of e){if(i.as===t)return i.joins=[...i.joins??[],n],true;if(Array.isArray(i.joins)&&this.appendNestedJoinByAlias(i.joins,t,n))return true}return false}parseRelationRef(e){let n=String(e??"").trim().match(/^([A-Za-z0-9_.]+)\s*->\s*([A-Za-z0-9_]+)\.([A-Za-z0-9_.]+)(?:\s+as\s+([A-Za-z0-9_]+))?$/i);if(!n)throw new Error(`Invalid relation format: "${e}". Expected "source.path->collection.target"`);return {source:n[1],collection:n[2],target:n[3],alias:n[4]}}join(e,t){let n=this.buildStructuredJoin(e,t);return this.clone({joins:[...this.sq.joins??[],n]})}joinNested(e,t,n){let i=String(e??"").trim();if(!i)throw new Error("joinNested requires parentAlias");let s=(this.sq.joins??[]).map(a=>this.cloneStructuredJoin(a));if(s.length===0)throw new Error(`joinNested parent alias "${i}" not found`);let o=this.buildStructuredJoin(t,n);if(!this.appendNestedJoinByAlias(s,i,o))throw new Error(`joinNested parent alias "${i}" not found`);return this.clone({joins:s})}Join(e,t){return this.join(e,t)}JoinNested(e,t,n){return this.joinNested(e,t,n)}withRelation(e,t={}){let n=this.parseRelationRef(e);return this.join(n.collection,{...t,source:n.source,target:n.target,as:t.as??n.alias??n.collection})}select(...e){return this.clone({select:e})}distinctField(e){return this.clone({distinctField:e})}vectorSearch(e){return this.clone({vectorSearch:e})}doc(e){return new q(this.conn,this.name,e)}getRawQuery(){let e={...this.sq};return Array.isArray(this.sq.where)&&(e.where=this.sq.where.map(t=>({...t}))),Array.isArray(this.sq.orderBy)&&(e.orderBy=this.sq.orderBy.map(t=>({...t}))),Array.isArray(this.sq.aggregate)&&(e.aggregate=this.sq.aggregate.map(t=>({...t}))),Array.isArray(this.sq.having)&&(e.having=this.sq.having.map(t=>({...t}))),Array.isArray(this.sq.select)&&(e.select=[...this.sq.select]),Array.isArray(this.sq.joins)&&(e.joins=this.sq.joins.map(t=>this.cloneStructuredJoin(t))),this.sq.groupBy?.fields&&(e.groupBy={fields:[...this.sq.groupBy.fields]}),this.sq.startAt?.values&&(e.startAt={values:[...this.sq.startAt.values]}),this.sq.startAfter?.values&&(e.startAfter={values:[...this.sq.startAfter.values]}),this.sq.endAt?.values&&(e.endAt={values:[...this.sq.endAt.values]}),this.sq.endBefore?.values&&(e.endBefore={values:[...this.sq.endBefore.values]}),{collection:this.name,query:e}}onSnapshot(e){let t=this._buildSq(),n=()=>{};return n=this.conn.subscribe(this.name,void 0,t,i=>{i.type==="snapshot"&&(e(i),n());}),n}stream(e={}){let t=this._buildSq(),n=new Set,i=[],s=new Map,o=Math.max(0,Number(e.flushMs??24)),a=Math.max(1,Number(e.maxBatchSize??200)),d=e.insertAt??"end",c=typeof e.maxDocs=="number"&&e.maxDocs>0?Math.floor(e.maxDocs):void 0,m=String(e.idField??"id"),u=[],b=false,F=false,$=0,P,H=()=>{s.clear();for(let l=0;l<u.length;l+=1){let p=re(u[l]);p&&s.set(p,l);}},re=(l,p)=>{if(p)return p;if(l==null)return;if(typeof e.getId=="function"){let C=e.getId(l);if(typeof C=="string"&&C.length>0)return C}let y=l?.[m]??l?._id??l?.docId;if(typeof y=="string"&&y.length>0)return y},ie=()=>{c==null||u.length<=c||(u=u.slice(0,c));},se=()=>{typeof e.sort=="function"&&(u=u.slice().sort(e.sort));},oe=(l,p)=>{$+=1;let y=u.slice(),C={reason:l,batchSize:p,version:$,ready:F};for(let M of Array.from(n))try{M(y,C);}catch{}},K=()=>{P!=null&&(clearTimeout(P),P=void 0);},Se=l=>{if(l.operation==="delete"){let M=s.get(l.docId??"");if(M==null)return;u.splice(M,1),H();return}let p=l.data;if(p==null)return;let y=re(p,l.docId);if(!y)return;let C=s.get(y);if(typeof C=="number"){u[C]=p;return}d==="start"?u.unshift(p):u.push(p),ie(),H();},ae=()=>{if(b||i.length===0)return;K();let l=i.splice(0,i.length);for(let p of l)Se(p);se(),H(),oe("change-batch",l.length);},ke=()=>{b||P!=null||(P=setTimeout(ae,o));},W=this.conn.subscribe(this.name,void 0,t,l=>{if(!b){if(l.type==="snapshot"){u=Array.isArray(l.data)?[...l.data]:[],F=true,K(),i.length=0,se(),ie(),H(),oe("snapshot",0);return}if(i.push({docId:l.docId,operation:l.operation,data:l.data??null}),i.length>=a){ae();return}ke();}}),V={subscribe(l,p=true){if(n.add(l),p){let y={reason:F?"change-batch":"snapshot",batchSize:0,version:$,ready:F};try{l(u.slice(),y);}catch{}}return ()=>{n.delete(l);}},getSnapshot(){return u.slice()},isReady(){return F},getVersion(){return $},close:()=>{b||(b=true,K(),i.length=0,n.clear(),W());},onError(l){return W.onError(l),V},onPermissionDenied(l){return W.onPermissionDenied(l),V}};return V}asStore(e={}){let t=this.stream(e);return {subscribe:n=>t.subscribe(()=>{n();},false),getSnapshot:()=>t.getSnapshot(),getServerSnapshot:()=>[],stream:t,destroy:()=>{t.close();}}}onDocAdded(e){let t=this._buildSq();return this.conn.subscribe(this.name,void 0,t,n=>{n.type==="change"&&n.operation==="insert"&&n.data!=null&&e(n.data,n.docId);},{skipSnapshot:true})}onDocUpdated(e){let t=this._buildSq();return this.conn.subscribe(this.name,void 0,t,n=>{n.type==="change"&&(n.operation==="update"||n.operation==="replace")&&n.data!=null&&e(n.data,n.docId);},{skipSnapshot:true})}onDocDeleted(e){let t=this._buildSq();return this.conn.subscribe(this.name,void 0,t,n=>{n.type==="change"&&n.operation==="delete"&&e(n.docId);},{skipSnapshot:true})}onDocChanged(e){let t=this._buildSq();return this.conn.subscribe(this.name,void 0,t,n=>{n.type==="change"&&e(n.data??null,n.docId,n.operation);},{skipSnapshot:true})}_buildSq(){let e={};return this.sq.where&&this.sq.where.length>0&&(e.where=this.sq.where),this.sq.orderBy&&this.sq.orderBy.length>0&&(e.orderBy=this.sq.orderBy),this.sq.limit!==void 0&&(e.limit=this.sq.limit),this.sq.offset!==void 0&&(e.offset=this.sq.offset),this.sq.startAt&&(e.startAt=this.sq.startAt),this.sq.startAfter&&(e.startAfter=this.sq.startAfter),this.sq.endAt&&(e.endAt=this.sq.endAt),this.sq.endBefore&&(e.endBefore=this.sq.endBefore),this.sq.aggregate&&this.sq.aggregate.length>0&&(e.aggregate=this.sq.aggregate),this.sq.groupBy&&(e.groupBy=this.sq.groupBy),this.sq.having&&this.sq.having.length>0&&(e.having=this.sq.having),this.sq.joins&&this.sq.joins.length>0&&(e.joins=this.sq.joins),this.sq.vectorSearch&&(e.vectorSearch=this.sq.vectorSearch),this.sq.select&&this.sq.select.length>0&&(e.select=this.sq.select),this.sq.distinctField&&(e.distinctField=this.sq.distinctField),e}};var U=class{constructor(e){this.cfg=e;this.ws=null;this.pendingAcks=new Map;this.subscriptions=new Map;this.activeSubscriptions=new Map;this.subscriptionErrorHandlers=new Map;this.subscriptionPermissionHandlers=new Map;this.subscriptionLastErrors=new Map;this.connected=false;this.shouldReconnect=true;this.reconnectDelay=2e3;this._state="disconnected";this._stateListeners=[];let t=e.serverUrl.replace(/^http/,"ws");this.wsUrl=`${t}/?appId=${encodeURIComponent(e.appId)}&adminKey=${encodeURIComponent(e.adminKey)}`,this._readyPromise=new Promise(n=>{this._readyResolve=n;}),this._setState("connecting"),this._connect();}ready(){return this._readyPromise}get connectionState(){return this._state}onConnectionStateChange(e){return this._stateListeners.push(e),()=>{this._stateListeners=this._stateListeners.filter(t=>t!==e);}}disconnect(){this.shouldReconnect=false,this.ws?.close(1e3,"Admin disconnect"),this.ws=null,this.connected=false,this._setState("disconnected");}send(e,t){return new Promise((n,i)=>{let s=X(),o={id:s,type:e,ts:Date.now(),...t},a=setTimeout(()=>{this.pendingAcks.delete(s),i(new Error(`[flare-admin] WS request timeout (${e})`));},1e4);this.pendingAcks.set(s,c=>{clearTimeout(a),c.type==="error"?i(new Error(`[flare-admin] Server error: ${c.message}`)):n(c);});let d=()=>this.ws?.send(JSON.stringify(o));this.connected&&this.ws?.readyState===be.OPEN?d():this.ready().then(d).catch(i);})}subscribe(e,t,n,i,s={}){let o=X(),a={baseId:o,liveId:o,collection:e,docId:t,structuredQuery:n,callback:i,options:s};this.activeSubscriptions.set(o,a),this.subscriptionErrorHandlers.set(o,new Set),this.subscriptionPermissionHandlers.set(o,new Set),this.activateSubscription(a);let d=()=>{let u=this.activeSubscriptions.get(o)?.liveId??o;this.activeSubscriptions.delete(o),this.subscriptions.delete(o),this.subscriptions.delete(u),this.subscriptionErrorHandlers.delete(o),this.subscriptionPermissionHandlers.delete(o),this.subscriptionLastErrors.delete(o),this.connected&&this.send("unsubscribe",{subscriptionId:u}).catch(()=>{});},c=d;return c.unsubscribe=d,c.onError=m=>{this.subscriptionErrorHandlers.get(o)?.add(m);let u=this.subscriptionLastErrors.get(o);if(u)try{m(u);}catch{}return c},c.onPermissionDenied=m=>{this.subscriptionPermissionHandlers.get(o)?.add(m);let u=this.subscriptionLastErrors.get(o);if(u?.permissionDenied)try{m(u);}catch{}return c},c.catch=m=>c.onError(m),c}activateSubscription(e){let t=()=>{this.subscriptions.set(e.liveId,e.callback);let n={collection:e.collection};e.docId&&(n.docId=e.docId),e.structuredQuery&&(n.query=e.structuredQuery),e.options.skipSnapshot&&(n.skipSnapshot=true),this.send("subscribe",n).then(i=>{let s=i.subscriptionId;if(s&&s!==e.liveId){let o=this.subscriptions.get(e.liveId);o&&(this.subscriptions.delete(e.liveId),this.subscriptions.set(s,o),e.liveId=s);}}).catch(()=>{this.subscriptions.delete(e.liveId),this.emitSubscriptionError(e.baseId,this.toSubscriptionError(new Error("Subscribe failed")));});};this.connected?t():this.ready().then(t).catch(n=>{this.emitSubscriptionError(e.baseId,this.toSubscriptionError(n));});}async replayActiveSubscriptions(){if(!this.connected)return;let e=Array.from(this.activeSubscriptions.values());for(let t of e){if(!this.activeSubscriptions.has(t.baseId))continue;let n=t.liveId;this.subscriptions.delete(n),t.liveId=t.baseId,n&&n!==t.baseId&&await this.send("unsubscribe",{subscriptionId:n}).catch(()=>{}),this.activateSubscription(t);}}toSubscriptionError(e){let t=e instanceof Error?e.message:String(e??"Unknown subscription error"),n=t.match(/^\[([^\]]+)\]\s*(.*)$/),i=n?.[1],s=(n?.[2]??t).trim()||t,o=i==="PERMISSION_DENIED"||t.includes("PERMISSION_DENIED");return {code:i,message:s,permissionDenied:o,raw:e}}emitSubscriptionError(e,t){this.subscriptionLastErrors.set(e,t);let n=this.subscriptionErrorHandlers.get(e);if(n)for(let i of n)try{i(t);}catch{}if(t.permissionDenied){let i=this.subscriptionPermissionHandlers.get(e);if(i)for(let s of i)try{s(t);}catch{}}}_setState(e){if(this._state!==e){this._state=e;for(let t of this._stateListeners.slice())try{t(e);}catch{}}}_connect(){this._readyPromise=new Promise(e=>{this._readyResolve=e;}),this.ws=new be(this.wsUrl),this.ws.on("message",e=>{let t;try{t=JSON.parse(e.toString());}catch{return}this._handle(t);}),this.ws.on("close",e=>{this.connected=false,this.shouldReconnect&&e!==1e3?(this._setState("reconnecting"),setTimeout(()=>{this.reconnectDelay=Math.min(this.reconnectDelay*2,3e4),this._connect();},this.reconnectDelay)):this._setState("disconnected");}),this.ws.on("error",()=>{this._setState("error");});}_handle(e){let t=e.type;if(t==="auth_ok"){this.connected||(this.connected=true,this.reconnectDelay=2e3,this._setState("connected"),this._readyResolve(),this.replayActiveSubscriptions().catch(()=>{}));return}if(t==="ack"||t==="pong"||t==="call_response"){let n=e.correlationId??e.id,i=this.pendingAcks.get(n);i&&(i(e),this.pendingAcks.delete(n));return}if(t==="error"){let n=e.correlationId;if(n){let i=this.pendingAcks.get(n);i&&(i(e),this.pendingAcks.delete(n));let s=Array.from(this.activeSubscriptions.values()).find(o=>o.liveId===n||o.baseId===n);if(s){let o=this.toSubscriptionError(new Error(`[${String(e.code??"ERROR")}] ${String(e.message??"Unknown error")}`));this.emitSubscriptionError(s.baseId,o);}}return}if(t==="snapshot"||t==="change"){let n=e.subscriptionId,i=this.subscriptions.get(n);if(i){let s=Array.from(this.activeSubscriptions.values()).find(d=>d.liveId===n),o=String(e.collection??s?.collection??""),a=t==="change"&&e.operation==="delete"?null:v(this.cfg,o,e.data,s?.structuredQuery);i({subscriptionId:n,collection:o,docId:e.docId,data:a,type:t==="snapshot"?"snapshot":"change",operation:e.operation});}}}};var N=class{constructor(e){this._ws=new U(e);}collection(e){return new L(this._ws,e)}ready(){return this._ws.ready()}get connectionState(){return this._ws.connectionState}onConnectionStateChange(e){return this._ws.onConnectionStateChange(e)}disconnect(){this._ws.disconnect();}};var un="ServerTimeStamp",mn={$serverTimestamp:true};var ne=class{constructor(e){this.cfg={defaultTtl:"24h",transport:"auto",...e,serverUrl:e.serverUrl.replace(/\/$/,""),httpBase:e.httpBase?.replace(/\/$/,"")??"",grpcUrl:e.grpcUrl??"",dataMapper:e.dataMapper??{}};}auth(){return this._auth??(this._auth=new _(this.cfg))}db(){return this._db??(this._db=new B(this.cfg))}live(){return this._conn??(this._conn=new N(this.cfg))}notifications(){return this._notifications??(this._notifications=new E(this.cfg))}storageService(){return this._storage??(this._storage=new z(this.cfg))}storage(){return this._storageS3||(this._storageS3=new D(this.storageService())),this._storageS3}s3(){return this.storage()}async createSignedUrl(e){return await this.s3().createSignedUrl(e)}createBucket(e,t){return this.s3().createBucket(e,t)}listBuckets(){return this.s3().listBuckets()}deleteBucket(e){return this.s3().deleteBucket(e)}deleteBuckets(e){return this.s3().deleteBuckets(e)}getBucketLocation(e){return this.s3().getBucketLocation(e)}putObject(e){return this.s3().putObject(e)}getObject(e){return this.s3().getObject(e)}getObjectUrl(e){return this.s3().getObjectUrl(e)}downloadObject(e){return this.s3().downloadObject(e)}headObject(e){return this.s3().headObject(e)}headObjects(e){return this.s3().headObjects(e)}listObjects(e){return this.s3().listObjects(e)}copyObject(e){return this.s3().copyObject(e)}copyObjects(e){return this.s3().copyObjects(e)}deleteObjects(e){return this.s3().deleteObjects(e)}onConnectionStateChange(e){return this.live().onConnectionStateChange(e)}disconnect(){this._conn?.disconnect(),this._conn=void 0;}},T=new Map;function Sn(r,e="[DEFAULT]"){if(T.has(e))return T.get(e);let t=new ne(r);return T.set(e,t),t}function J(r="[DEFAULT]"){let e=T.get(r);if(!e)throw new Error(`[flare-admin] No app named "${r}" found. Call connectApp() before getApp().`);return e}function kn(r="[DEFAULT]"){let e=T.get(r);return e?(e.disconnect(),T.delete(r),true):false}function wn(){for(let r of T.values())r.disconnect();T.clear();}function Cn(r="[DEFAULT]"){return J(r).auth()}function vn(r="[DEFAULT]"){return J(r).db()}function Tn(r="[DEFAULT]"){return J(r).live()}function Rn(r="[DEFAULT]"){return J(r).notifications()}function On(r="[DEFAULT]"){return J(r).storage()}export{O as AdminCollectionReference,R as AdminDocumentReference,L as AdminLiveCollectionReference,q as AdminLiveDocumentReference,Z as AdminStorageSignedAction,ne as FlareAdminApp,_ as FlareAdminAuthService,N as FlareAdminConnection,B as FlareAdminDbService,E as FlareAdminNotificationsService,D as FlareAdminStorageS3,U as FlareAdminWsConnection,un as ServerTimeStamp,mn as ServerTimeStampField,Cn as auth,Sn as connectApp,vn as db,wn as disconnectAllApps,kn as disconnectApp,J as getApp,Tn as live,Rn as notifications,On as storage};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AdminAuthTicket, AnyFilter, CreateAuthTicketOptions, CreateCustomTokenOptions, FlareAdminConfig, StructuredQuery } from "../types";
|
|
2
|
+
export declare const shouldUseGrpcTransport: (cfg: Required<FlareAdminConfig>) => boolean;
|
|
3
|
+
export declare function runGrpcAdminQuery<T = Record<string, unknown>>(cfg: Required<FlareAdminConfig>, collection: string, query: StructuredQuery): Promise<T[] | null>;
|
|
4
|
+
export declare function runGrpcCreateCustomToken(cfg: Required<FlareAdminConfig>, uid: string, opts: CreateCustomTokenOptions): Promise<string | null>;
|
|
5
|
+
export declare function runGrpcCreateAuthTicket(cfg: Required<FlareAdminConfig>, uid: string, opts: CreateAuthTicketOptions): Promise<AdminAuthTicket | null>;
|
|
6
|
+
export declare function runGrpcGetDocument<T = Record<string, unknown>>(cfg: Required<FlareAdminConfig>, collection: string, id: string): Promise<T | null | undefined>;
|
|
7
|
+
export declare function runGrpcCreateDocument(cfg: Required<FlareAdminConfig>, collection: string, data: unknown): Promise<string | null>;
|
|
8
|
+
export declare function runGrpcReplaceDocument(cfg: Required<FlareAdminConfig>, collection: string, id: string, data: unknown): Promise<boolean | null>;
|
|
9
|
+
export declare function runGrpcUpdateDocument(cfg: Required<FlareAdminConfig>, collection: string, id: string, data: unknown): Promise<boolean | null>;
|
|
10
|
+
export declare function runGrpcDeleteDocument(cfg: Required<FlareAdminConfig>, collection: string, id: string): Promise<boolean | null>;
|
|
11
|
+
export declare function runGrpcDeleteMany(cfg: Required<FlareAdminConfig>, collection: string, where: AnyFilter[]): Promise<number | null>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AdminPushSendInput, AdminPushSendResult, FlareAdminConfig, FlareAdminNotifications
|
|
1
|
+
import { AdminPushSendInput, AdminPushSendResult, AdminPushToken, FlareAdminConfig, FlareAdminNotifications } from "../types";
|
|
2
2
|
export declare class FlareAdminNotificationsService implements FlareAdminNotifications {
|
|
3
3
|
private readonly cfg;
|
|
4
4
|
constructor(cfg: Required<FlareAdminConfig>);
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { AdminCopyObjectInput, AdminDeleteObjectsInput, AdminDownloadObjectInput, AdminDownloadObjectResult, AdminGetObjectInput, AdminGetObjectResult, AdminGetObjectUrlInput, AdminHeadObjectInput, AdminHeadObjectsInput, AdminListObjectsInput, AdminListObjectsResult, AdminPutObjectInput, AdminPutObjectResult, AdminStorageAwsConfig, AdminStorageBucket, AdminStorageBucketInput, AdminStorageDeleteInput, AdminStorageDownloadInput, AdminStorageObjectMeta, AdminStorageObjectResult, AdminStorageRulesHistoryResult, AdminStorageRulesPolicy, AdminStorageServer, AdminStorageServerInput, AdminStorageServerPatchInput, AdminStorageSignedUrlInput, AdminStorageSignedUrlResult, AdminStorageUploadInput, FlareAdminConfig } from "../types";
|
|
2
|
+
export declare class FlareAdminStorageService {
|
|
3
|
+
private readonly cfg;
|
|
4
|
+
constructor(cfg: Required<FlareAdminConfig>);
|
|
5
|
+
private base;
|
|
6
|
+
private get headers();
|
|
7
|
+
protected jsonRequest<T>(method: string, path: string, body?: unknown): Promise<T>;
|
|
8
|
+
request<T>(method: string, path: string, body?: unknown): Promise<T>;
|
|
9
|
+
servers(): Promise<AdminStorageServer[]>;
|
|
10
|
+
createServer(input: AdminStorageServerInput): Promise<{
|
|
11
|
+
ok: boolean;
|
|
12
|
+
serverId: string;
|
|
13
|
+
}>;
|
|
14
|
+
patchServer(serverId: string, input: AdminStorageServerPatchInput): Promise<{
|
|
15
|
+
ok: boolean;
|
|
16
|
+
serverId: string;
|
|
17
|
+
}>;
|
|
18
|
+
deleteServer(serverId: string): Promise<{
|
|
19
|
+
ok: boolean;
|
|
20
|
+
serverId: string;
|
|
21
|
+
removedObjects: number;
|
|
22
|
+
}>;
|
|
23
|
+
awsConfig(serverId: string): Promise<AdminStorageAwsConfig>;
|
|
24
|
+
uploadObject(input: AdminStorageUploadInput): Promise<AdminStorageObjectResult>;
|
|
25
|
+
downloadObject(input: AdminStorageDownloadInput): Promise<AdminStorageObjectResult>;
|
|
26
|
+
deleteObject(input: AdminStorageDeleteInput): Promise<AdminStorageObjectResult>;
|
|
27
|
+
createSignedUrl(input: AdminStorageSignedUrlInput): Promise<AdminStorageSignedUrlResult>;
|
|
28
|
+
setRules(input: {
|
|
29
|
+
rules?: Record<string, unknown>;
|
|
30
|
+
rulesDsl?: string;
|
|
31
|
+
rulesHistoryPolicy?: AdminStorageRulesPolicy;
|
|
32
|
+
}): Promise<{
|
|
33
|
+
id: string;
|
|
34
|
+
}>;
|
|
35
|
+
validateRules(rulesDsl: string): Promise<{
|
|
36
|
+
valid: boolean;
|
|
37
|
+
diagnostics: unknown[];
|
|
38
|
+
rulesCount: number;
|
|
39
|
+
}>;
|
|
40
|
+
rulesHistory(): Promise<AdminStorageRulesHistoryResult>;
|
|
41
|
+
restoreRules(historyId: string): Promise<{
|
|
42
|
+
id: string;
|
|
43
|
+
rulesText: string;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* S3-compatible admin storage service.
|
|
48
|
+
* Works with bucket names; resolves server IDs internally.
|
|
49
|
+
*/
|
|
50
|
+
export declare class FlareAdminStorageS3 {
|
|
51
|
+
private readonly _svc;
|
|
52
|
+
private readonly _bucketCache;
|
|
53
|
+
private _bucketListLoaded;
|
|
54
|
+
private _bucketListPromise;
|
|
55
|
+
constructor(svc: FlareAdminStorageService);
|
|
56
|
+
private _ensureBuckets;
|
|
57
|
+
private _loadBuckets;
|
|
58
|
+
private _invalidateCache;
|
|
59
|
+
private _resolveBucketId;
|
|
60
|
+
createBucket(name: string, options?: AdminStorageBucketInput): Promise<AdminStorageBucket>;
|
|
61
|
+
listBuckets(): Promise<AdminStorageBucket[]>;
|
|
62
|
+
deleteBucket(name: string): Promise<{
|
|
63
|
+
ok: boolean;
|
|
64
|
+
removedObjects: number;
|
|
65
|
+
}>;
|
|
66
|
+
deleteBuckets(names: string[]): Promise<{
|
|
67
|
+
ok: boolean;
|
|
68
|
+
deleted: string[];
|
|
69
|
+
errors: Record<string, string>;
|
|
70
|
+
}>;
|
|
71
|
+
getBucketLocation(name: string): Promise<{
|
|
72
|
+
bucket: string;
|
|
73
|
+
kind: string;
|
|
74
|
+
region?: string;
|
|
75
|
+
endpoint?: string;
|
|
76
|
+
}>;
|
|
77
|
+
putObject(input: AdminPutObjectInput): Promise<AdminPutObjectResult>;
|
|
78
|
+
getObject(input: AdminGetObjectInput): Promise<AdminGetObjectResult>;
|
|
79
|
+
getObjectUrl(input: AdminGetObjectUrlInput): Promise<string>;
|
|
80
|
+
downloadObject(input: AdminDownloadObjectInput): Promise<AdminDownloadObjectResult>;
|
|
81
|
+
headObject(input: AdminHeadObjectInput): Promise<AdminStorageObjectMeta>;
|
|
82
|
+
headObjects(input: AdminHeadObjectsInput): Promise<AdminStorageObjectMeta[]>;
|
|
83
|
+
listObjects(input: AdminListObjectsInput): Promise<AdminListObjectsResult>;
|
|
84
|
+
copyObject(input: AdminCopyObjectInput): Promise<{
|
|
85
|
+
ok: boolean;
|
|
86
|
+
}>;
|
|
87
|
+
copyObjects(inputs: AdminCopyObjectInput[]): Promise<{
|
|
88
|
+
ok: boolean;
|
|
89
|
+
errors: Record<string, string>;
|
|
90
|
+
}>;
|
|
91
|
+
deleteObject(input: AdminStorageDeleteInput): Promise<AdminStorageObjectResult>;
|
|
92
|
+
deleteObjects(input: AdminDeleteObjectsInput): Promise<{
|
|
93
|
+
ok: boolean;
|
|
94
|
+
deleted: string[];
|
|
95
|
+
errors: Record<string, string>;
|
|
96
|
+
}>;
|
|
97
|
+
createSignedUrl(input: AdminStorageSignedUrlInput): Promise<AdminStorageSignedUrlResult>;
|
|
98
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AdminConnectionState, AdminConnectionStateListener } from "../types";
|
|
1
2
|
import { AdminLiveCollectionReference } from "./LiveCollection";
|
|
2
3
|
/**
|
|
3
4
|
* The real-time connection service returned by `admin.connection()`.
|
|
@@ -28,6 +29,19 @@ export declare class FlareAdminConnection {
|
|
|
28
29
|
collection<T = Record<string, unknown>>(name: string): AdminLiveCollectionReference<T>;
|
|
29
30
|
/** Wait until the WebSocket is open and authenticated. */
|
|
30
31
|
ready(): Promise<void>;
|
|
32
|
+
/** Current connection state. */
|
|
33
|
+
get connectionState(): AdminConnectionState;
|
|
34
|
+
/**
|
|
35
|
+
* Subscribe to connection state changes.
|
|
36
|
+
* Returns an unsubscribe function.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* const unsub = admin.live().onConnectionStateChange((state) => {
|
|
40
|
+
* console.log("connection:", state);
|
|
41
|
+
* });
|
|
42
|
+
* // state: 'connecting' | 'connected' | 'disconnected' | 'reconnecting' | 'error'
|
|
43
|
+
*/
|
|
44
|
+
onConnectionStateChange(listener: AdminConnectionStateListener): () => void;
|
|
31
45
|
/** Close the WebSocket connection permanently (no reconnect). */
|
|
32
46
|
disconnect(): void;
|
|
33
47
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WhereCondition, HavingClause, JoinClause, AggregateSpec, VectorSearchClause, StructuredQuery, AdminDocAddedCallback, AdminDocChangedCallback, AdminDocDeletedCallback, AdminDocUpdatedCallback, AdminSnapshotCallback, AdminSubscribeOptions, AdminSubscriptionHandle } from "../types";
|
|
1
|
+
import { AdminCollectionExternalStore, AdminCollectionStream, AdminCollectionStreamOptions, WhereCondition, HavingClause, JoinClause, AggregateSpec, VectorSearchClause, StructuredQuery, AdminDocAddedCallback, AdminDocChangedCallback, AdminDocDeletedCallback, AdminDocUpdatedCallback, AdminSnapshotCallback, AdminSubscribeOptions, AdminSubscriptionHandle } from "../types";
|
|
2
2
|
import { AdminLiveDocumentReference } from "./LiveDocument";
|
|
3
3
|
type AdminRealtimeSubscriber = {
|
|
4
4
|
subscribe: (collection: string, docId: string | undefined, structuredQuery: StructuredQuery | undefined, callback: AdminSnapshotCallback, options?: AdminSubscribeOptions) => AdminSubscriptionHandle;
|
|
@@ -84,6 +84,8 @@ export declare class AdminLiveCollectionReference<T = Record<string, unknown>> {
|
|
|
84
84
|
query: StructuredQuery;
|
|
85
85
|
};
|
|
86
86
|
onSnapshot(callback: AdminSnapshotCallback<T[]>): () => void;
|
|
87
|
+
stream(options?: AdminCollectionStreamOptions<T>): AdminCollectionStream<T>;
|
|
88
|
+
asStore(options?: AdminCollectionStreamOptions<T>): AdminCollectionExternalStore<T>;
|
|
87
89
|
onDocAdded(callback: AdminDocAddedCallback<T>): () => void;
|
|
88
90
|
onDocUpdated(callback: AdminDocUpdatedCallback<T>): () => void;
|
|
89
91
|
onDocDeleted(callback: AdminDocDeletedCallback): () => void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AdminConnectionState, AdminConnectionStateListener, AdminSnapshotCallback, AdminSubscribeOptions, AdminSubscriptionHandle, FlareAdminConfig, StructuredQuery } from "../types";
|
|
2
2
|
/**
|
|
3
3
|
* Manages a persistent admin WebSocket connection to FlareServer.
|
|
4
4
|
* Passes `adminKey` as a query param — the server immediately elevates the
|
|
@@ -16,12 +16,21 @@ export declare class FlareAdminWsConnection {
|
|
|
16
16
|
private connected;
|
|
17
17
|
private shouldReconnect;
|
|
18
18
|
private reconnectDelay;
|
|
19
|
+
private _state;
|
|
20
|
+
private _stateListeners;
|
|
19
21
|
private readonly wsUrl;
|
|
20
22
|
private _readyResolve;
|
|
21
23
|
private _readyPromise;
|
|
22
24
|
constructor(cfg: Required<FlareAdminConfig>);
|
|
23
25
|
/** Resolves once the WS is open and AUTH_OK is received from the server. */
|
|
24
26
|
ready(): Promise<void>;
|
|
27
|
+
/** Current connection state. */
|
|
28
|
+
get connectionState(): AdminConnectionState;
|
|
29
|
+
/**
|
|
30
|
+
* Subscribe to connection state changes.
|
|
31
|
+
* Returns an unsubscribe function.
|
|
32
|
+
*/
|
|
33
|
+
onConnectionStateChange(listener: AdminConnectionStateListener): () => void;
|
|
25
34
|
/** Close the connection permanently (no reconnect). */
|
|
26
35
|
disconnect(): void;
|
|
27
36
|
/**
|
|
@@ -38,6 +47,7 @@ export declare class FlareAdminWsConnection {
|
|
|
38
47
|
private replayActiveSubscriptions;
|
|
39
48
|
private toSubscriptionError;
|
|
40
49
|
private emitSubscriptionError;
|
|
50
|
+
private _setState;
|
|
41
51
|
private _connect;
|
|
42
52
|
private _handle;
|
|
43
53
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentinel value that asks flare-node to write the current server timestamp.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* await admin.db().collection("posts").doc(id).update({ updatedAt: ServerTimeStamp })
|
|
6
|
+
*/
|
|
7
|
+
export declare const ServerTimeStamp: "ServerTimeStamp";
|
|
8
|
+
/**
|
|
9
|
+
* Object form of the same sentinel for payloads that prefer structured values.
|
|
10
|
+
*/
|
|
11
|
+
export declare const ServerTimeStampField: {
|
|
12
|
+
readonly $serverTimestamp: true;
|
|
13
|
+
};
|