@zuzjs/flare 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/index.cjs +3 -0
- package/dist/index.d.cts +379 -0
- package/dist/index.d.ts +379 -0
- package/dist/index.js +2 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zuz.js Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# 🔥 ZuzFlare Client
|
|
2
|
+
|
|
3
|
+
> Official JavaScript/TypeScript client for ZuzFlare Server
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@zuzjs/flare)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
Real-time database client with Firebase-like API for your self-hosted ZuzFlare server.
|
|
9
|
+
|
|
10
|
+
## 📦 Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @zuzjs/flare
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## 🚀 Quick Start
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { FlareClient } from '@zuzjs/flare';
|
|
20
|
+
|
|
21
|
+
const flare = new FlareClient({
|
|
22
|
+
endpoint: 'http://localhost:5050',
|
|
23
|
+
appId: 'my-app'
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
flare.connect();
|
|
27
|
+
|
|
28
|
+
// Write data
|
|
29
|
+
await flare.collection('users').doc('alice').set({
|
|
30
|
+
name: 'Alice',
|
|
31
|
+
email: 'alice@example.com'
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Real-time updates
|
|
35
|
+
flare.collection('users').onSnapshot((data) => {
|
|
36
|
+
console.log('Users:', data);
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 📖 Full Documentation
|
|
41
|
+
|
|
42
|
+
See [complete documentation](https://flare.zuz.com.pk) for:
|
|
43
|
+
- API Reference
|
|
44
|
+
- Usage Examples
|
|
45
|
+
- TypeScript Types
|
|
46
|
+
- React/Vue/Svelte Integration
|
|
47
|
+
- Authentication
|
|
48
|
+
- Offline Support
|
|
49
|
+
|
|
50
|
+
## 🔗 Links
|
|
51
|
+
|
|
52
|
+
- [Server Package](@zuzjs/flare-server)
|
|
53
|
+
- [GitHub](https://github.com/zuzjs/flare-client)
|
|
54
|
+
- [Documentation](https://flare.zuz.com.pk)
|
|
55
|
+
|
|
56
|
+
## 📄 License
|
|
57
|
+
|
|
58
|
+
MIT © Zuz.js Team
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
'use strict';Object.defineProperty(exports,'__esModule',{value:true});var core=require('@zuzjs/core'),auth=require('@zuzjs/auth');/* ZuzFlare Client */
|
|
2
|
+
var C=(c=>(c.SUBSCRIBE="subscribe",c.UNSUBSCRIBE="unsubscribe",c.WRITE="write",c.DELETE="delete",c.AUTH="auth",c.PING="ping",c.OFFLINE_SYNC="offline_sync",c.CALL="call",c))(C||{}),v=(c=>(c.SNAPSHOT="snapshot",c.CHANGE="change",c.ERROR="error",c.ACK="ack",c.PONG="pong",c.AUTH_OK="auth_ok",c.OFFLINE_ACK="offline_ack",c.CALL_RESPONSE="call_response",c))(v||{});function S(r){let e=[];for(let[t,i]of Object.entries(r))if(typeof i=="string"){let o=i.match(/^(>=|<=|!=|>|<|==)\s*(.+)$/);if(o){let[,n,s]=o;e.push({field:t,op:n,value:w(s.trim())});}else e.push({field:t,op:"==",value:i});}else Array.isArray(i)?e.push({field:t,op:"in",value:i}):e.push({field:t,op:"==",value:i});return e}function w(r){if(!isNaN(Number(r)))return Number(r);if(r==="true")return true;if(r==="false")return false;if(r==="null")return null;if(r!=="undefined")return r}var a=class{constructor(e,t,i){this.client=e;this.collection=t;this.legacyId=i;}whereCondition;updateData;setData;deleteOp=false;promise;where(e){return this.whereCondition=e,this}update(e){return this.updateData=e,this}set(e){return this.setData=e,this}delete(){return this.deleteOp=true,this}getDocId(){if(this.legacyId)return this.legacyId;if(this.whereCondition&&this.whereCondition.id){let e=this.whereCondition.id;if(typeof e=="string")return e}throw new Error('Document ID not specified. Use .where({ id: "..." }) or doc(collection, id)')}async execute(){return this._execute()}async _execute(){let e=this.getDocId();if(this.deleteOp){await this.client.send("delete",{collection:this.collection,docId:e});return}if(this.updateData){await this.client.send("write",{collection:this.collection,docId:e,data:this.updateData,merge:true});return}if(this.setData){await this.client.send("write",{collection:this.collection,docId:e,data:this.setData,merge:false});return}return this.get()}then(e,t){return this.promise||(this.promise=this._execute()),this.promise.then(e,t)}async get(){let e=this.getDocId(),t=core.uuid2(18);return new Promise((i,o)=>{let n=this.client.subscribe(t,this.collection,e,void 0,s=>{s.type==="snapshot"&&(n(),i(s.data));});setTimeout(()=>{n(),o(new Error("Document fetch timeout"));},1e4);})}onSnapshot(e){let t=this.getDocId(),i=core.uuid2(18);return this.client.subscribe(i,this.collection,t,void 0,e)}},h=class{constructor(e,t,i){this.client=e;this.collection=t;this.id=i;}async get(){return new a(this.client,this.collection,this.id).get()}async set(e){await this.client.send("write",{collection:this.collection,docId:this.id,data:e,merge:false});}async update(e){await this.client.send("write",{collection:this.collection,docId:this.id,data:e,merge:true});}async delete(){await this.client.send("delete",{collection:this.collection,docId:this.id});}onSnapshot(e){let t=core.uuid2(18);return this.client.subscribe(t,this.collection,this.id,void 0,e)}},f=class r{constructor(e,t){this.client=e;this.collection=t;}queries=[];promise;doc(e){return new h(this.client,this.collection,e)}where(e){let t=new r(this.client,this.collection),i=S(e);return t.queries=[...this.queries,...i],t}async get(){return this._execute()}async _execute(){let e=core.uuid2(18);return new Promise((t,i)=>{let o=this.queries.length>0?this.queries:void 0,n=this.client.subscribe(e,this.collection,void 0,o,s=>{s.type==="snapshot"&&(n(),t(Array.isArray(s.data)?s.data:[s.data]));});setTimeout(()=>{n(),i(new Error("Collection fetch timeout"));},1e4);})}then(e,t){return this.promise||(this.promise=this._execute()),this.promise.then(e,t)}onSnapshot(e){let t=core.uuid2(18),i=this.queries.length>0?this.queries:void 0;return this.client.subscribe(t,this.collection,void 0,i,e)}async add(e){let t=core.uuid2(18),i=this.doc(t);return await i.set(e),i}update(e){return new a(this.client,this.collection).update(e)}delete(){return new a(this.client,this.collection).delete()}};async function R(r){let e=r.replace(/-----BEGIN PUBLIC KEY-----/,"").replace(/-----END PUBLIC KEY-----/,"").replace(/\s+/g,""),t=typeof atob<"u"?atob(e):Buffer.from(e,"base64").toString("binary"),i=new Uint8Array(t.length);for(let n=0;n<t.length;n++)i[n]=t.charCodeAt(n);return (globalThis.crypto??(await import('crypto')).webcrypto).subtle.importKey("spki",i.buffer,{name:"RSA-OAEP",hash:"SHA-256"},false,["encrypt"])}async function I(r,e){let t=await R(e),i=new TextEncoder().encode(JSON.stringify(r)),n=await(globalThis.crypto??(await import('crypto')).webcrypto).subtle.encrypt({name:"RSA-OAEP"},t,i),s=typeof btoa<"u"?btoa(String.fromCharCode(...new Uint8Array(n))):Buffer.from(n).toString("base64");return JSON.stringify({enc:"rsa",data:s})}var g=class{socket=null;reconnectInterval;maxReconnectDelay;isConnected=false;shouldReconnect=true;options;messageQueue=[];heartbeatInterval=null;connectionTimeout=null;constructor(e){this.options=e,this.reconnectInterval=e.reconnectDelay||2,this.maxReconnectDelay=e.maxReconnectDelay||60,this.log("Transport initialized",e.url);}connect(){if(this.socket){this.log("Socket already exists, skipping connection");return}this.log("Connecting to",this.options.url),this.socket=new WebSocket(this.options.url),this.connectionTimeout=setTimeout(()=>{this.isConnected||(this.log("Connection timeout"),this.socket?.close(),this.handleReconnect());},1e4),this.socket.onopen=()=>{this.connectionTimeout&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=null),this.isConnected=true,this.reconnectInterval=this.options.reconnectDelay||2,this.log("Connected to server"),this.options.onOpen?.(),this.startHeartbeat(),this.flushQueue();},this.socket.onmessage=e=>{try{let t=JSON.parse(e.data);this.options.onMessage(t);}catch(t){this.log("Parse error",t),this.options.onError?.(t);}},this.socket.onerror=e=>{this.log("WebSocket error",e),this.options.onError?.(new Error("WebSocket error"));},this.socket.onclose=e=>{this.connectionTimeout&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=null),this.isConnected=false,this.socket=null,this.stopHeartbeat(),this.log("Connection closed",e.code,e.reason),this.options.onClose?.(),e.code!==1e3&&this.shouldReconnect&&this.options.autoReconnect&&this.handleReconnect();};}handleReconnect(){let e=this.reconnectInterval*1e3;this.log(`Reconnecting in ${this.reconnectInterval}s...`),setTimeout(()=>{this.reconnectInterval=Math.min(this.reconnectInterval*2,this.maxReconnectDelay),this.connect();},e);}startHeartbeat(){this.heartbeatInterval=setInterval(()=>{this.isConnected&&this.send({type:"ping",id:Date.now().toString(),ts:Date.now()});},3e4);}stopHeartbeat(){this.heartbeatInterval&&(clearInterval(this.heartbeatInterval),this.heartbeatInterval=null);}flushQueue(){for(this.log("Flushing message queue",this.messageQueue.length);this.messageQueue.length>0;){let e=this.messageQueue.shift();e&&this.send(e);}}send(e){if(this.socket&&this.socket.readyState===WebSocket.OPEN){let t=i=>{try{this.socket.send(i),this.log("Sent message",e);}catch(o){this.log("Send error",o),this.messageQueue.push(e);}};this.options.publicKey?I(e,this.options.publicKey).then(t).catch(i=>{this.log("RSA encrypt error \u2014 sending plaintext",i),t(JSON.stringify(e));}):t(JSON.stringify(e));}else this.log("Socket not ready, queueing message"),this.messageQueue.push(e);}disconnect(){this.shouldReconnect=false,this.stopHeartbeat(),this.socket&&(this.socket.close(1e3,"Client disconnect"),this.socket=null),this.isConnected=false,this.log("Disconnected");}get connected(){return this.isConnected}log(...e){this.options.debug&&console.log("[FlareTransport]",...e);}};var m=class{transport;config;pendingAcks=new Map;subscriptions=new Map;offlineQueue=[];currentState="disconnected";connectionListeners=[];errorListeners=[];authToken;userId;isDebug=false;constructor(e){this.config={autoReconnect:true,reconnectDelay:2,maxReconnectDelay:60,debug:false,connectionTimeout:1e4,...e},this.isDebug=this.config.debug||false;let{hostname:t,port:i,protocol:o}=new URL(this.config.endpoint),n=o==="https:",c=`${n?"wss":"ws"}://${t}:${i||(n?"443":"80")}/?appId=${this.config.appId}${this.config.apiKey?`&apiKey=${this.config.apiKey}`:""}`;this.transport=new g({url:c,publicKey:this.config.publicKey,autoReconnect:this.config.autoReconnect,reconnectDelay:this.config.reconnectDelay,maxReconnectDelay:this.config.maxReconnectDelay,onMessage:y=>this.handleIncoming(y),onOpen:()=>this.onConnected(),onClose:()=>this.onDisconnected(),onError:y=>this.handleTransportError(y),debug:this.isDebug}),this.log("FlareClient initialized",e);}connect(){this.setState("connecting"),this.transport.connect();}disconnect(){this.transport.disconnect(),this.setState("disconnected");}collection(e){return new f(this,e)}doc(e,t){return t!==void 0?new h(this,e,t):new a(this,e)}async auth(e){let t=await this.send("auth",{token:e});if(t.type==="auth_ok")return this.authToken=e,this.userId=t.uid,this.log("Authentication successful",t.uid),{uid:t.uid,token:e};throw new Error("Authentication failed")}signOut(){this.authToken=void 0,this.userId=void 0,this.log("Signed out");}get currentUser(){return this.userId}get connectionState(){return this.currentState}get isConnected(){return this.currentState==="connected"}onConnectionStateChange(e){return this.connectionListeners.push(e),()=>{this.connectionListeners=this.connectionListeners.filter(t=>t!==e);}}onError(e){return this.errorListeners.push(e),()=>{this.errorListeners=this.errorListeners.filter(t=>t!==e);}}async ping(){let e=Date.now();return await this.send("ping",{}),Date.now()-e}async call(e,t={}){let i=await this.send("call",{topic:e,payload:t});if(!i.success)throw new Error(i.error??`CALL "${e}" failed`);return i.result}async syncOffline(){if(this.offlineQueue.length===0)return;this.log("Syncing offline operations",this.offlineQueue.length);let e=[...this.offlineQueue];this.offlineQueue.length=0;let t=await this.send("offline_sync",{operations:e});t.conflicts&&t.conflicts.length>0&&(this.log("Offline sync conflicts",t.conflicts),t.conflicts.forEach(i=>{let o=e.find(n=>n.id===i.operationId);o&&this.offlineQueue.push(o);}));}handleTransportError(e){this.log("Transport error",e),this.errorListeners.forEach(t=>{try{t(e);}catch(i){this.log("Error listener error",i);}});}onConnected(){this.setState("connected"),this.log("Connected to FlareServer"),this.offlineQueue.length>0&&this.syncOffline().catch(e=>{this.log("Offline sync failed",e);});}onDisconnected(){this.currentState!=="disconnected"&&this.setState("reconnecting"),this.log("Disconnected from FlareServer");}setState(e){this.currentState!==e&&(this.currentState=e,this.log("Connection state changed",e),this.connectionListeners.forEach(t=>{try{t(e);}catch(i){this.log("Connection listener error",i);}}));}handleIncoming(e){if(this.log("Received message",e.type,e),e.type==="ack"||e.type==="pong"||e.type==="auth_ok"||e.type==="call_response"){let t=this.pendingAcks.get(e.correlationId||e.id);t&&(t(e),this.pendingAcks.delete(e.correlationId||e.id));return}if(e.type==="error"){this.log("Server error",e.code,e.message);let t=new Error(`[${e.code}] ${e.message}`);if(this.errorListeners.forEach(i=>{try{i(t);}catch(o){this.log("Error listener error",o);}}),e.correlationId){let i=this.pendingAcks.get(e.correlationId);i&&(i(e),this.pendingAcks.delete(e.correlationId));}return}if(e.type==="snapshot"||e.type==="change"){let t=this.subscriptions.get(e.subscriptionId);if(t){let i={subscriptionId:e.subscriptionId,collection:e.collection,docId:e.docId,data:e.data,type:e.type==="snapshot"?"snapshot":"change",operation:e.operation};try{t(i);}catch(o){this.log("Subscription callback error",o);}}}}async send(e,t){return new Promise((i,o)=>{let n=core.uuid2(18),s={id:n,type:e,ts:Date.now(),...t};this.pendingAcks.set(n,d=>{d.type==="error"?o(new Error(`[${d.code}] ${d.message}`)):i(d);}),this.isConnected?this.transport.send(s):(this.log("Queueing message for offline",s),this.offlineQueue.push(s),o(new Error("Not connected - message queued"))),setTimeout(()=>{this.pendingAcks.has(n)&&(this.pendingAcks.delete(n),o(new Error("Request timeout")));},this.config.connectionTimeout);})}subscribe(e,t,i,o,n){return this.log("Creating subscription",e,t,i),this.subscriptions.set(e,n),this.send("subscribe",{collection:t,docId:i,query:o}).then(s=>{s.subscriptionId&&(this.subscriptions.delete(e),this.subscriptions.set(s.subscriptionId,n));}).catch(s=>{this.log("Subscription failed",s),this.subscriptions.delete(e);}),()=>{this.log("Unsubscribing",e),this.subscriptions.delete(e),this.isConnected&&this.send("unsubscribe",{subscriptionId:e}).catch(s=>this.log("Unsubscribe failed",s));}}log(...e){this.isDebug&&console.log("[FlareClient]",...e);}},b=m;var T=class extends Error{constructor(t,i,o){super(t);this.code=i;this.cause=o;this.name="ZuzFlareError";}};var l=null,q=r=>(l||(l=new b(r),l.connect()),l),$=()=>l,G=()=>{l&&(l.disconnect(),l=null);};var J=b;
|
|
3
|
+
Object.defineProperty(exports,"AuthGuard",{enumerable:true,get:function(){return auth.AuthGuard}});Object.defineProperty(exports,"Dropbox",{enumerable:true,get:function(){return auth.Dropbox}});Object.defineProperty(exports,"Google",{enumerable:true,get:function(){return auth.Google}});exports.CollectionReference=f;exports.DocumentQueryBuilder=a;exports.DocumentReference=h;exports.FlareAction=C;exports.FlareError=T;exports.FlareEvent=v;exports.connectApp=q;exports.default=J;exports.disconnectFlare=G;exports.getFlare=$;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
export { AuthGuard, AuthToken, Dropbox, Google, NormalizedProfile, OAuthProvider, ProviderId } from '@zuzjs/auth';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Client Configuration
|
|
5
|
+
*/
|
|
6
|
+
interface FlareConfig {
|
|
7
|
+
/** WebSocket endpoint URL (e.g., 'http://localhost:5050' or 'wss://api.example.com') */
|
|
8
|
+
endpoint: string;
|
|
9
|
+
/** Application/Project ID */
|
|
10
|
+
appId: string;
|
|
11
|
+
/** API Key for authentication (optional) */
|
|
12
|
+
apiKey?: string;
|
|
13
|
+
/**
|
|
14
|
+
* RSA public key (PEM) returned by `flare app create`.
|
|
15
|
+
* When provided, all outgoing messages are wrapped in an RSA-OAEP encrypted
|
|
16
|
+
* envelope: `{ enc: "rsa", data: "<base64>" }` before being sent over the wire.
|
|
17
|
+
*/
|
|
18
|
+
publicKey?: string;
|
|
19
|
+
/** Enable automatic reconnection (default: true) */
|
|
20
|
+
autoReconnect?: boolean;
|
|
21
|
+
/** Initial reconnect delay in seconds (default: 2) */
|
|
22
|
+
reconnectDelay?: number;
|
|
23
|
+
/** Maximum reconnect delay in seconds (default: 60) */
|
|
24
|
+
maxReconnectDelay?: number;
|
|
25
|
+
/** Enable debug logging (default: false) */
|
|
26
|
+
debug?: boolean;
|
|
27
|
+
/** Connection timeout in milliseconds (default: 10000) */
|
|
28
|
+
connectionTimeout?: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Query configuration (NEW v0.2: Object-based query syntax)
|
|
32
|
+
*/
|
|
33
|
+
interface QueryConfig {
|
|
34
|
+
field: string;
|
|
35
|
+
op: QueryOperator;
|
|
36
|
+
value: unknown;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Where condition (NEW v0.2: Object-based syntax)
|
|
40
|
+
* Example: { age: ">= 25", role: "admin" }
|
|
41
|
+
*/
|
|
42
|
+
type WhereCondition = Record<string, string | number | boolean | any[]>;
|
|
43
|
+
type QueryOperator = "==" | "!=" | "<" | "<=" | ">" | ">=" | "in" | "not-in" | "array-contains";
|
|
44
|
+
/**
|
|
45
|
+
* Subscription callback
|
|
46
|
+
*/
|
|
47
|
+
type SubscriptionCallback<T = any> = (data: SubscriptionData<T>) => void;
|
|
48
|
+
/**
|
|
49
|
+
* Subscription data received from server
|
|
50
|
+
*/
|
|
51
|
+
interface SubscriptionData<T = any> {
|
|
52
|
+
subscriptionId: string;
|
|
53
|
+
collection: string;
|
|
54
|
+
docId?: string;
|
|
55
|
+
data: T | T[];
|
|
56
|
+
type: 'snapshot' | 'change';
|
|
57
|
+
operation?: 'insert' | 'update' | 'delete' | 'replace';
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Document reference
|
|
61
|
+
*/
|
|
62
|
+
interface DocumentSnapshot<T = any> {
|
|
63
|
+
id: string;
|
|
64
|
+
data: T | null;
|
|
65
|
+
exists: boolean;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Collection query result
|
|
69
|
+
*/
|
|
70
|
+
interface QuerySnapshot<T = any> {
|
|
71
|
+
docs: DocumentSnapshot<T>[];
|
|
72
|
+
size: number;
|
|
73
|
+
empty: boolean;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Offline operation
|
|
77
|
+
*/
|
|
78
|
+
interface OfflineOperation {
|
|
79
|
+
id: string;
|
|
80
|
+
type: 'write' | 'delete';
|
|
81
|
+
collection: string;
|
|
82
|
+
docId: string;
|
|
83
|
+
data?: Record<string, unknown>;
|
|
84
|
+
merge?: boolean;
|
|
85
|
+
clientTs: number;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Authentication result
|
|
89
|
+
*/
|
|
90
|
+
interface AuthResult {
|
|
91
|
+
uid: string;
|
|
92
|
+
token?: string;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Connection state
|
|
96
|
+
*/
|
|
97
|
+
type ConnectionState = 'connecting' | 'connected' | 'disconnected' | 'reconnecting' | 'error';
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Query builder for document operations (ORM-style)
|
|
101
|
+
* Supports: doc('users').update({...}).where({ id: 'alice' })
|
|
102
|
+
*
|
|
103
|
+
* This class is thenable - you can await it directly without .execute() or .get()
|
|
104
|
+
* @example
|
|
105
|
+
* await doc('users').where({ id: 'alice' })
|
|
106
|
+
* await doc('users').update({ name: 'Alice' }).where({ id: 'alice' })
|
|
107
|
+
*/
|
|
108
|
+
declare class DocumentQueryBuilder<T = any> implements PromiseLike<T | null | void> {
|
|
109
|
+
private client;
|
|
110
|
+
private collection;
|
|
111
|
+
private legacyId?;
|
|
112
|
+
private whereCondition?;
|
|
113
|
+
private updateData?;
|
|
114
|
+
private setData?;
|
|
115
|
+
private deleteOp;
|
|
116
|
+
private promise?;
|
|
117
|
+
constructor(client: FlareClient, collection: string, legacyId?: string | undefined);
|
|
118
|
+
/**
|
|
119
|
+
* Set where condition
|
|
120
|
+
*/
|
|
121
|
+
where(condition: WhereCondition): this;
|
|
122
|
+
/**
|
|
123
|
+
* Set update data (for update operations)
|
|
124
|
+
*/
|
|
125
|
+
update(data: Partial<T>): this;
|
|
126
|
+
/**
|
|
127
|
+
* Set data (for set operations)
|
|
128
|
+
*/
|
|
129
|
+
set(data: Partial<T>): this;
|
|
130
|
+
/**
|
|
131
|
+
* Mark for deletion
|
|
132
|
+
*/
|
|
133
|
+
delete(): this;
|
|
134
|
+
/**
|
|
135
|
+
* Get the document ID from where condition or legacy id
|
|
136
|
+
*/
|
|
137
|
+
private getDocId;
|
|
138
|
+
/**
|
|
139
|
+
* Execute the query
|
|
140
|
+
* @deprecated Use await directly instead of .execute()
|
|
141
|
+
*/
|
|
142
|
+
execute(): Promise<T | null | void>;
|
|
143
|
+
/**
|
|
144
|
+
* Internal execute method
|
|
145
|
+
*/
|
|
146
|
+
private _execute;
|
|
147
|
+
/**
|
|
148
|
+
* Make this class thenable so it can be awaited directly
|
|
149
|
+
*/
|
|
150
|
+
then<TResult1 = T | null | void, TResult2 = never>(onfulfilled?: ((value: T | null | void) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): PromiseLike<TResult1 | TResult2>;
|
|
151
|
+
/**
|
|
152
|
+
* Get the document data once
|
|
153
|
+
*/
|
|
154
|
+
get(): Promise<T | null>;
|
|
155
|
+
/**
|
|
156
|
+
* Subscribe to real-time updates
|
|
157
|
+
*/
|
|
158
|
+
onSnapshot(callback: SubscriptionCallback<T>): () => void;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Legacy document reference (for backward compatibility)
|
|
162
|
+
*/
|
|
163
|
+
declare class DocumentReference<T = any> {
|
|
164
|
+
private client;
|
|
165
|
+
readonly collection: string;
|
|
166
|
+
readonly id: string;
|
|
167
|
+
constructor(client: FlareClient, collection: string, id: string);
|
|
168
|
+
get(): Promise<T | null>;
|
|
169
|
+
set(data: Partial<T>): Promise<void>;
|
|
170
|
+
update(data: Partial<T>): Promise<void>;
|
|
171
|
+
delete(): Promise<void>;
|
|
172
|
+
onSnapshot(callback: SubscriptionCallback<T>): () => void;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Collection reference with ORM-style query builder
|
|
176
|
+
*
|
|
177
|
+
* This class is thenable - you can await it directly without .get()
|
|
178
|
+
* @example
|
|
179
|
+
* await collection('users').where({ age: '>= 25' })
|
|
180
|
+
*/
|
|
181
|
+
declare class CollectionReference<T = any> implements PromiseLike<T[]> {
|
|
182
|
+
private client;
|
|
183
|
+
readonly collection: string;
|
|
184
|
+
private queries;
|
|
185
|
+
private promise?;
|
|
186
|
+
constructor(client: FlareClient, collection: string);
|
|
187
|
+
/**
|
|
188
|
+
* Get a document reference (legacy API)
|
|
189
|
+
*/
|
|
190
|
+
doc(id: string): DocumentReference<T>;
|
|
191
|
+
/**
|
|
192
|
+
* Add a where filter (NEW ORM-style API)
|
|
193
|
+
* @example .where({ age: ">= 25", role: "admin" })
|
|
194
|
+
*/
|
|
195
|
+
where(condition: WhereCondition): CollectionReference<T>;
|
|
196
|
+
/**
|
|
197
|
+
* Get all documents once
|
|
198
|
+
* @deprecated Use await directly instead of .get()
|
|
199
|
+
*/
|
|
200
|
+
get(): Promise<T[]>;
|
|
201
|
+
/**
|
|
202
|
+
* Internal execute method
|
|
203
|
+
*/
|
|
204
|
+
private _execute;
|
|
205
|
+
/**
|
|
206
|
+
* Make this class thenable so it can be awaited directly
|
|
207
|
+
*/
|
|
208
|
+
then<TResult1 = T[], TResult2 = never>(onfulfilled?: ((value: T[]) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): PromiseLike<TResult1 | TResult2>;
|
|
209
|
+
/**
|
|
210
|
+
* Subscribe to real-time updates
|
|
211
|
+
*/
|
|
212
|
+
onSnapshot(callback: SubscriptionCallback<T[]>): () => void;
|
|
213
|
+
/**
|
|
214
|
+
* Add a new document with auto-generated ID
|
|
215
|
+
*/
|
|
216
|
+
add(data: Partial<T>): Promise<DocumentReference<T>>;
|
|
217
|
+
/**
|
|
218
|
+
* Update documents matching the where condition
|
|
219
|
+
*/
|
|
220
|
+
update(data: Partial<T>): DocumentQueryBuilder<T>;
|
|
221
|
+
/**
|
|
222
|
+
* Delete documents matching the where condition
|
|
223
|
+
*/
|
|
224
|
+
delete(): DocumentQueryBuilder<T>;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** Client Request */
|
|
228
|
+
declare enum FlareAction {
|
|
229
|
+
SUBSCRIBE = "subscribe",
|
|
230
|
+
UNSUBSCRIBE = "unsubscribe",
|
|
231
|
+
WRITE = "write",
|
|
232
|
+
DELETE = "delete",
|
|
233
|
+
AUTH = "auth",
|
|
234
|
+
PING = "ping",
|
|
235
|
+
OFFLINE_SYNC = "offline_sync",
|
|
236
|
+
/** General-purpose RPC / topic call */
|
|
237
|
+
CALL = "call"
|
|
238
|
+
}
|
|
239
|
+
/** Server Response */
|
|
240
|
+
declare enum FlareEvent {
|
|
241
|
+
SNAPSHOT = "snapshot",
|
|
242
|
+
CHANGE = "change",
|
|
243
|
+
ERROR = "error",
|
|
244
|
+
ACK = "ack",
|
|
245
|
+
PONG = "pong",
|
|
246
|
+
AUTH_OK = "auth_ok",
|
|
247
|
+
OFFLINE_ACK = "offline_ack",
|
|
248
|
+
/** Response to a CALL */
|
|
249
|
+
CALL_RESPONSE = "call_response"
|
|
250
|
+
}
|
|
251
|
+
interface BaseMessage {
|
|
252
|
+
id: string;
|
|
253
|
+
type: FlareAction | FlareEvent;
|
|
254
|
+
ts: number;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
type ConnectionListener = (state: ConnectionState) => void;
|
|
258
|
+
type ErrorListener = (error: Error) => void;
|
|
259
|
+
declare class FlareClient {
|
|
260
|
+
private transport;
|
|
261
|
+
private readonly config;
|
|
262
|
+
private readonly pendingAcks;
|
|
263
|
+
private readonly subscriptions;
|
|
264
|
+
private readonly offlineQueue;
|
|
265
|
+
private currentState;
|
|
266
|
+
private connectionListeners;
|
|
267
|
+
private errorListeners;
|
|
268
|
+
private authToken?;
|
|
269
|
+
private userId?;
|
|
270
|
+
private isDebug;
|
|
271
|
+
constructor(config: FlareConfig);
|
|
272
|
+
/**
|
|
273
|
+
* Connect to the server
|
|
274
|
+
*/
|
|
275
|
+
connect(): void;
|
|
276
|
+
/**
|
|
277
|
+
* Disconnect from the server
|
|
278
|
+
*/
|
|
279
|
+
disconnect(): void;
|
|
280
|
+
/**
|
|
281
|
+
* Get a collection reference
|
|
282
|
+
*/
|
|
283
|
+
collection<T = any>(name: string): CollectionReference<T>;
|
|
284
|
+
/**
|
|
285
|
+
* Get a document query builder (NEW ORM-style API)
|
|
286
|
+
* @example flare.doc('users').update({...}).where({ id: 'alice' })
|
|
287
|
+
*/
|
|
288
|
+
doc<T = any>(collection: string): DocumentQueryBuilder<T>;
|
|
289
|
+
/**
|
|
290
|
+
* Get a document reference (Legacy API - deprecated)
|
|
291
|
+
* @deprecated Use doc(collection).where({ id: '...' }) instead
|
|
292
|
+
*/
|
|
293
|
+
doc<T = any>(collection: string, id: string): DocumentReference<T>;
|
|
294
|
+
/**
|
|
295
|
+
* Authenticate with a token
|
|
296
|
+
*/
|
|
297
|
+
auth(token: string): Promise<AuthResult>;
|
|
298
|
+
/**
|
|
299
|
+
* Sign out
|
|
300
|
+
*/
|
|
301
|
+
signOut(): void;
|
|
302
|
+
/**
|
|
303
|
+
* Get current user ID
|
|
304
|
+
*/
|
|
305
|
+
get currentUser(): string | undefined;
|
|
306
|
+
/**
|
|
307
|
+
* Get connection state
|
|
308
|
+
*/
|
|
309
|
+
get connectionState(): ConnectionState;
|
|
310
|
+
/**
|
|
311
|
+
* Check if connected
|
|
312
|
+
*/
|
|
313
|
+
get isConnected(): boolean;
|
|
314
|
+
/**
|
|
315
|
+
* Listen to connection state changes
|
|
316
|
+
*/
|
|
317
|
+
onConnectionStateChange(listener: ConnectionListener): () => void;
|
|
318
|
+
/**
|
|
319
|
+
* Listen to errors
|
|
320
|
+
*/
|
|
321
|
+
onError(callback: ErrorListener): () => void;
|
|
322
|
+
/**
|
|
323
|
+
* Ping the server
|
|
324
|
+
*/
|
|
325
|
+
ping(): Promise<number>;
|
|
326
|
+
/**
|
|
327
|
+
* Invoke a server-side CALL handler by topic and await its response.
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* const result = await flare.call("agent:backup", { name: "db-daily" });
|
|
331
|
+
* console.log(result.jobId);
|
|
332
|
+
*
|
|
333
|
+
* @param topic The topic registered with `flare.registerCallHandler(topic, fn)` on the server.
|
|
334
|
+
* @param payload Arbitrary JSON payload forwarded to the handler.
|
|
335
|
+
* @returns The object returned by the server handler.
|
|
336
|
+
* @throws If the server returns `success: false` or the request times out.
|
|
337
|
+
*/
|
|
338
|
+
call<T = Record<string, unknown>>(topic: string, payload?: Record<string, unknown>): Promise<T>;
|
|
339
|
+
/**
|
|
340
|
+
* Sync offline operations
|
|
341
|
+
*/
|
|
342
|
+
syncOffline(): Promise<void>;
|
|
343
|
+
private handleTransportError;
|
|
344
|
+
private onConnected;
|
|
345
|
+
private onDisconnected;
|
|
346
|
+
private setState;
|
|
347
|
+
private handleIncoming;
|
|
348
|
+
/**
|
|
349
|
+
* Internal: Send message to server
|
|
350
|
+
*/
|
|
351
|
+
send(type: FlareAction, payload: any): Promise<any>;
|
|
352
|
+
/**
|
|
353
|
+
* Internal: Create a subscription
|
|
354
|
+
*/
|
|
355
|
+
subscribe(subId: string, collection: string, docId: string | undefined, query: QueryConfig | undefined, callback: SubscriptionCallback): () => void;
|
|
356
|
+
private log;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
declare class FlareError extends Error {
|
|
360
|
+
readonly code: string;
|
|
361
|
+
readonly cause?: unknown | undefined;
|
|
362
|
+
constructor(message: string, code: string, cause?: unknown | undefined);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Initialize and connect to FlareServer
|
|
367
|
+
* Returns a singleton instance
|
|
368
|
+
*/
|
|
369
|
+
declare const connectApp: (config: FlareConfig) => FlareClient;
|
|
370
|
+
/**
|
|
371
|
+
* Get the current Flare instance
|
|
372
|
+
*/
|
|
373
|
+
declare const getFlare: () => FlareClient | null;
|
|
374
|
+
/**
|
|
375
|
+
* Disconnect and reset the instance
|
|
376
|
+
*/
|
|
377
|
+
declare const disconnectFlare: () => void;
|
|
378
|
+
|
|
379
|
+
export { type AuthResult, type BaseMessage, CollectionReference, type ConnectionState, DocumentQueryBuilder, DocumentReference, type DocumentSnapshot, FlareAction, type FlareConfig, FlareError, FlareEvent, type OfflineOperation, type QueryConfig, type QueryOperator, type QuerySnapshot, type SubscriptionCallback, type SubscriptionData, type WhereCondition, connectApp, FlareClient as default, disconnectFlare, getFlare };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
export { AuthGuard, AuthToken, Dropbox, Google, NormalizedProfile, OAuthProvider, ProviderId } from '@zuzjs/auth';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Client Configuration
|
|
5
|
+
*/
|
|
6
|
+
interface FlareConfig {
|
|
7
|
+
/** WebSocket endpoint URL (e.g., 'http://localhost:5050' or 'wss://api.example.com') */
|
|
8
|
+
endpoint: string;
|
|
9
|
+
/** Application/Project ID */
|
|
10
|
+
appId: string;
|
|
11
|
+
/** API Key for authentication (optional) */
|
|
12
|
+
apiKey?: string;
|
|
13
|
+
/**
|
|
14
|
+
* RSA public key (PEM) returned by `flare app create`.
|
|
15
|
+
* When provided, all outgoing messages are wrapped in an RSA-OAEP encrypted
|
|
16
|
+
* envelope: `{ enc: "rsa", data: "<base64>" }` before being sent over the wire.
|
|
17
|
+
*/
|
|
18
|
+
publicKey?: string;
|
|
19
|
+
/** Enable automatic reconnection (default: true) */
|
|
20
|
+
autoReconnect?: boolean;
|
|
21
|
+
/** Initial reconnect delay in seconds (default: 2) */
|
|
22
|
+
reconnectDelay?: number;
|
|
23
|
+
/** Maximum reconnect delay in seconds (default: 60) */
|
|
24
|
+
maxReconnectDelay?: number;
|
|
25
|
+
/** Enable debug logging (default: false) */
|
|
26
|
+
debug?: boolean;
|
|
27
|
+
/** Connection timeout in milliseconds (default: 10000) */
|
|
28
|
+
connectionTimeout?: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Query configuration (NEW v0.2: Object-based query syntax)
|
|
32
|
+
*/
|
|
33
|
+
interface QueryConfig {
|
|
34
|
+
field: string;
|
|
35
|
+
op: QueryOperator;
|
|
36
|
+
value: unknown;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Where condition (NEW v0.2: Object-based syntax)
|
|
40
|
+
* Example: { age: ">= 25", role: "admin" }
|
|
41
|
+
*/
|
|
42
|
+
type WhereCondition = Record<string, string | number | boolean | any[]>;
|
|
43
|
+
type QueryOperator = "==" | "!=" | "<" | "<=" | ">" | ">=" | "in" | "not-in" | "array-contains";
|
|
44
|
+
/**
|
|
45
|
+
* Subscription callback
|
|
46
|
+
*/
|
|
47
|
+
type SubscriptionCallback<T = any> = (data: SubscriptionData<T>) => void;
|
|
48
|
+
/**
|
|
49
|
+
* Subscription data received from server
|
|
50
|
+
*/
|
|
51
|
+
interface SubscriptionData<T = any> {
|
|
52
|
+
subscriptionId: string;
|
|
53
|
+
collection: string;
|
|
54
|
+
docId?: string;
|
|
55
|
+
data: T | T[];
|
|
56
|
+
type: 'snapshot' | 'change';
|
|
57
|
+
operation?: 'insert' | 'update' | 'delete' | 'replace';
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Document reference
|
|
61
|
+
*/
|
|
62
|
+
interface DocumentSnapshot<T = any> {
|
|
63
|
+
id: string;
|
|
64
|
+
data: T | null;
|
|
65
|
+
exists: boolean;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Collection query result
|
|
69
|
+
*/
|
|
70
|
+
interface QuerySnapshot<T = any> {
|
|
71
|
+
docs: DocumentSnapshot<T>[];
|
|
72
|
+
size: number;
|
|
73
|
+
empty: boolean;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Offline operation
|
|
77
|
+
*/
|
|
78
|
+
interface OfflineOperation {
|
|
79
|
+
id: string;
|
|
80
|
+
type: 'write' | 'delete';
|
|
81
|
+
collection: string;
|
|
82
|
+
docId: string;
|
|
83
|
+
data?: Record<string, unknown>;
|
|
84
|
+
merge?: boolean;
|
|
85
|
+
clientTs: number;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Authentication result
|
|
89
|
+
*/
|
|
90
|
+
interface AuthResult {
|
|
91
|
+
uid: string;
|
|
92
|
+
token?: string;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Connection state
|
|
96
|
+
*/
|
|
97
|
+
type ConnectionState = 'connecting' | 'connected' | 'disconnected' | 'reconnecting' | 'error';
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Query builder for document operations (ORM-style)
|
|
101
|
+
* Supports: doc('users').update({...}).where({ id: 'alice' })
|
|
102
|
+
*
|
|
103
|
+
* This class is thenable - you can await it directly without .execute() or .get()
|
|
104
|
+
* @example
|
|
105
|
+
* await doc('users').where({ id: 'alice' })
|
|
106
|
+
* await doc('users').update({ name: 'Alice' }).where({ id: 'alice' })
|
|
107
|
+
*/
|
|
108
|
+
declare class DocumentQueryBuilder<T = any> implements PromiseLike<T | null | void> {
|
|
109
|
+
private client;
|
|
110
|
+
private collection;
|
|
111
|
+
private legacyId?;
|
|
112
|
+
private whereCondition?;
|
|
113
|
+
private updateData?;
|
|
114
|
+
private setData?;
|
|
115
|
+
private deleteOp;
|
|
116
|
+
private promise?;
|
|
117
|
+
constructor(client: FlareClient, collection: string, legacyId?: string | undefined);
|
|
118
|
+
/**
|
|
119
|
+
* Set where condition
|
|
120
|
+
*/
|
|
121
|
+
where(condition: WhereCondition): this;
|
|
122
|
+
/**
|
|
123
|
+
* Set update data (for update operations)
|
|
124
|
+
*/
|
|
125
|
+
update(data: Partial<T>): this;
|
|
126
|
+
/**
|
|
127
|
+
* Set data (for set operations)
|
|
128
|
+
*/
|
|
129
|
+
set(data: Partial<T>): this;
|
|
130
|
+
/**
|
|
131
|
+
* Mark for deletion
|
|
132
|
+
*/
|
|
133
|
+
delete(): this;
|
|
134
|
+
/**
|
|
135
|
+
* Get the document ID from where condition or legacy id
|
|
136
|
+
*/
|
|
137
|
+
private getDocId;
|
|
138
|
+
/**
|
|
139
|
+
* Execute the query
|
|
140
|
+
* @deprecated Use await directly instead of .execute()
|
|
141
|
+
*/
|
|
142
|
+
execute(): Promise<T | null | void>;
|
|
143
|
+
/**
|
|
144
|
+
* Internal execute method
|
|
145
|
+
*/
|
|
146
|
+
private _execute;
|
|
147
|
+
/**
|
|
148
|
+
* Make this class thenable so it can be awaited directly
|
|
149
|
+
*/
|
|
150
|
+
then<TResult1 = T | null | void, TResult2 = never>(onfulfilled?: ((value: T | null | void) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): PromiseLike<TResult1 | TResult2>;
|
|
151
|
+
/**
|
|
152
|
+
* Get the document data once
|
|
153
|
+
*/
|
|
154
|
+
get(): Promise<T | null>;
|
|
155
|
+
/**
|
|
156
|
+
* Subscribe to real-time updates
|
|
157
|
+
*/
|
|
158
|
+
onSnapshot(callback: SubscriptionCallback<T>): () => void;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Legacy document reference (for backward compatibility)
|
|
162
|
+
*/
|
|
163
|
+
declare class DocumentReference<T = any> {
|
|
164
|
+
private client;
|
|
165
|
+
readonly collection: string;
|
|
166
|
+
readonly id: string;
|
|
167
|
+
constructor(client: FlareClient, collection: string, id: string);
|
|
168
|
+
get(): Promise<T | null>;
|
|
169
|
+
set(data: Partial<T>): Promise<void>;
|
|
170
|
+
update(data: Partial<T>): Promise<void>;
|
|
171
|
+
delete(): Promise<void>;
|
|
172
|
+
onSnapshot(callback: SubscriptionCallback<T>): () => void;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Collection reference with ORM-style query builder
|
|
176
|
+
*
|
|
177
|
+
* This class is thenable - you can await it directly without .get()
|
|
178
|
+
* @example
|
|
179
|
+
* await collection('users').where({ age: '>= 25' })
|
|
180
|
+
*/
|
|
181
|
+
declare class CollectionReference<T = any> implements PromiseLike<T[]> {
|
|
182
|
+
private client;
|
|
183
|
+
readonly collection: string;
|
|
184
|
+
private queries;
|
|
185
|
+
private promise?;
|
|
186
|
+
constructor(client: FlareClient, collection: string);
|
|
187
|
+
/**
|
|
188
|
+
* Get a document reference (legacy API)
|
|
189
|
+
*/
|
|
190
|
+
doc(id: string): DocumentReference<T>;
|
|
191
|
+
/**
|
|
192
|
+
* Add a where filter (NEW ORM-style API)
|
|
193
|
+
* @example .where({ age: ">= 25", role: "admin" })
|
|
194
|
+
*/
|
|
195
|
+
where(condition: WhereCondition): CollectionReference<T>;
|
|
196
|
+
/**
|
|
197
|
+
* Get all documents once
|
|
198
|
+
* @deprecated Use await directly instead of .get()
|
|
199
|
+
*/
|
|
200
|
+
get(): Promise<T[]>;
|
|
201
|
+
/**
|
|
202
|
+
* Internal execute method
|
|
203
|
+
*/
|
|
204
|
+
private _execute;
|
|
205
|
+
/**
|
|
206
|
+
* Make this class thenable so it can be awaited directly
|
|
207
|
+
*/
|
|
208
|
+
then<TResult1 = T[], TResult2 = never>(onfulfilled?: ((value: T[]) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): PromiseLike<TResult1 | TResult2>;
|
|
209
|
+
/**
|
|
210
|
+
* Subscribe to real-time updates
|
|
211
|
+
*/
|
|
212
|
+
onSnapshot(callback: SubscriptionCallback<T[]>): () => void;
|
|
213
|
+
/**
|
|
214
|
+
* Add a new document with auto-generated ID
|
|
215
|
+
*/
|
|
216
|
+
add(data: Partial<T>): Promise<DocumentReference<T>>;
|
|
217
|
+
/**
|
|
218
|
+
* Update documents matching the where condition
|
|
219
|
+
*/
|
|
220
|
+
update(data: Partial<T>): DocumentQueryBuilder<T>;
|
|
221
|
+
/**
|
|
222
|
+
* Delete documents matching the where condition
|
|
223
|
+
*/
|
|
224
|
+
delete(): DocumentQueryBuilder<T>;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** Client Request */
|
|
228
|
+
declare enum FlareAction {
|
|
229
|
+
SUBSCRIBE = "subscribe",
|
|
230
|
+
UNSUBSCRIBE = "unsubscribe",
|
|
231
|
+
WRITE = "write",
|
|
232
|
+
DELETE = "delete",
|
|
233
|
+
AUTH = "auth",
|
|
234
|
+
PING = "ping",
|
|
235
|
+
OFFLINE_SYNC = "offline_sync",
|
|
236
|
+
/** General-purpose RPC / topic call */
|
|
237
|
+
CALL = "call"
|
|
238
|
+
}
|
|
239
|
+
/** Server Response */
|
|
240
|
+
declare enum FlareEvent {
|
|
241
|
+
SNAPSHOT = "snapshot",
|
|
242
|
+
CHANGE = "change",
|
|
243
|
+
ERROR = "error",
|
|
244
|
+
ACK = "ack",
|
|
245
|
+
PONG = "pong",
|
|
246
|
+
AUTH_OK = "auth_ok",
|
|
247
|
+
OFFLINE_ACK = "offline_ack",
|
|
248
|
+
/** Response to a CALL */
|
|
249
|
+
CALL_RESPONSE = "call_response"
|
|
250
|
+
}
|
|
251
|
+
interface BaseMessage {
|
|
252
|
+
id: string;
|
|
253
|
+
type: FlareAction | FlareEvent;
|
|
254
|
+
ts: number;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
type ConnectionListener = (state: ConnectionState) => void;
|
|
258
|
+
type ErrorListener = (error: Error) => void;
|
|
259
|
+
declare class FlareClient {
|
|
260
|
+
private transport;
|
|
261
|
+
private readonly config;
|
|
262
|
+
private readonly pendingAcks;
|
|
263
|
+
private readonly subscriptions;
|
|
264
|
+
private readonly offlineQueue;
|
|
265
|
+
private currentState;
|
|
266
|
+
private connectionListeners;
|
|
267
|
+
private errorListeners;
|
|
268
|
+
private authToken?;
|
|
269
|
+
private userId?;
|
|
270
|
+
private isDebug;
|
|
271
|
+
constructor(config: FlareConfig);
|
|
272
|
+
/**
|
|
273
|
+
* Connect to the server
|
|
274
|
+
*/
|
|
275
|
+
connect(): void;
|
|
276
|
+
/**
|
|
277
|
+
* Disconnect from the server
|
|
278
|
+
*/
|
|
279
|
+
disconnect(): void;
|
|
280
|
+
/**
|
|
281
|
+
* Get a collection reference
|
|
282
|
+
*/
|
|
283
|
+
collection<T = any>(name: string): CollectionReference<T>;
|
|
284
|
+
/**
|
|
285
|
+
* Get a document query builder (NEW ORM-style API)
|
|
286
|
+
* @example flare.doc('users').update({...}).where({ id: 'alice' })
|
|
287
|
+
*/
|
|
288
|
+
doc<T = any>(collection: string): DocumentQueryBuilder<T>;
|
|
289
|
+
/**
|
|
290
|
+
* Get a document reference (Legacy API - deprecated)
|
|
291
|
+
* @deprecated Use doc(collection).where({ id: '...' }) instead
|
|
292
|
+
*/
|
|
293
|
+
doc<T = any>(collection: string, id: string): DocumentReference<T>;
|
|
294
|
+
/**
|
|
295
|
+
* Authenticate with a token
|
|
296
|
+
*/
|
|
297
|
+
auth(token: string): Promise<AuthResult>;
|
|
298
|
+
/**
|
|
299
|
+
* Sign out
|
|
300
|
+
*/
|
|
301
|
+
signOut(): void;
|
|
302
|
+
/**
|
|
303
|
+
* Get current user ID
|
|
304
|
+
*/
|
|
305
|
+
get currentUser(): string | undefined;
|
|
306
|
+
/**
|
|
307
|
+
* Get connection state
|
|
308
|
+
*/
|
|
309
|
+
get connectionState(): ConnectionState;
|
|
310
|
+
/**
|
|
311
|
+
* Check if connected
|
|
312
|
+
*/
|
|
313
|
+
get isConnected(): boolean;
|
|
314
|
+
/**
|
|
315
|
+
* Listen to connection state changes
|
|
316
|
+
*/
|
|
317
|
+
onConnectionStateChange(listener: ConnectionListener): () => void;
|
|
318
|
+
/**
|
|
319
|
+
* Listen to errors
|
|
320
|
+
*/
|
|
321
|
+
onError(callback: ErrorListener): () => void;
|
|
322
|
+
/**
|
|
323
|
+
* Ping the server
|
|
324
|
+
*/
|
|
325
|
+
ping(): Promise<number>;
|
|
326
|
+
/**
|
|
327
|
+
* Invoke a server-side CALL handler by topic and await its response.
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* const result = await flare.call("agent:backup", { name: "db-daily" });
|
|
331
|
+
* console.log(result.jobId);
|
|
332
|
+
*
|
|
333
|
+
* @param topic The topic registered with `flare.registerCallHandler(topic, fn)` on the server.
|
|
334
|
+
* @param payload Arbitrary JSON payload forwarded to the handler.
|
|
335
|
+
* @returns The object returned by the server handler.
|
|
336
|
+
* @throws If the server returns `success: false` or the request times out.
|
|
337
|
+
*/
|
|
338
|
+
call<T = Record<string, unknown>>(topic: string, payload?: Record<string, unknown>): Promise<T>;
|
|
339
|
+
/**
|
|
340
|
+
* Sync offline operations
|
|
341
|
+
*/
|
|
342
|
+
syncOffline(): Promise<void>;
|
|
343
|
+
private handleTransportError;
|
|
344
|
+
private onConnected;
|
|
345
|
+
private onDisconnected;
|
|
346
|
+
private setState;
|
|
347
|
+
private handleIncoming;
|
|
348
|
+
/**
|
|
349
|
+
* Internal: Send message to server
|
|
350
|
+
*/
|
|
351
|
+
send(type: FlareAction, payload: any): Promise<any>;
|
|
352
|
+
/**
|
|
353
|
+
* Internal: Create a subscription
|
|
354
|
+
*/
|
|
355
|
+
subscribe(subId: string, collection: string, docId: string | undefined, query: QueryConfig | undefined, callback: SubscriptionCallback): () => void;
|
|
356
|
+
private log;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
declare class FlareError extends Error {
|
|
360
|
+
readonly code: string;
|
|
361
|
+
readonly cause?: unknown | undefined;
|
|
362
|
+
constructor(message: string, code: string, cause?: unknown | undefined);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Initialize and connect to FlareServer
|
|
367
|
+
* Returns a singleton instance
|
|
368
|
+
*/
|
|
369
|
+
declare const connectApp: (config: FlareConfig) => FlareClient;
|
|
370
|
+
/**
|
|
371
|
+
* Get the current Flare instance
|
|
372
|
+
*/
|
|
373
|
+
declare const getFlare: () => FlareClient | null;
|
|
374
|
+
/**
|
|
375
|
+
* Disconnect and reset the instance
|
|
376
|
+
*/
|
|
377
|
+
declare const disconnectFlare: () => void;
|
|
378
|
+
|
|
379
|
+
export { type AuthResult, type BaseMessage, CollectionReference, type ConnectionState, DocumentQueryBuilder, DocumentReference, type DocumentSnapshot, FlareAction, type FlareConfig, FlareError, FlareEvent, type OfflineOperation, type QueryConfig, type QueryOperator, type QuerySnapshot, type SubscriptionCallback, type SubscriptionData, type WhereCondition, connectApp, FlareClient as default, disconnectFlare, getFlare };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {uuid2}from'@zuzjs/core';export{AuthGuard,Dropbox,Google}from'@zuzjs/auth';var v=(c=>(c.SUBSCRIBE="subscribe",c.UNSUBSCRIBE="unsubscribe",c.WRITE="write",c.DELETE="delete",c.AUTH="auth",c.PING="ping",c.OFFLINE_SYNC="offline_sync",c.CALL="call",c))(v||{}),S=(c=>(c.SNAPSHOT="snapshot",c.CHANGE="change",c.ERROR="error",c.ACK="ack",c.PONG="pong",c.AUTH_OK="auth_ok",c.OFFLINE_ACK="offline_ack",c.CALL_RESPONSE="call_response",c))(S||{});function w(r){let e=[];for(let[t,i]of Object.entries(r))if(typeof i=="string"){let o=i.match(/^(>=|<=|!=|>|<|==)\s*(.+)$/);if(o){let[,n,s]=o;e.push({field:t,op:n,value:R(s.trim())});}else e.push({field:t,op:"==",value:i});}else Array.isArray(i)?e.push({field:t,op:"in",value:i}):e.push({field:t,op:"==",value:i});return e}function R(r){if(!isNaN(Number(r)))return Number(r);if(r==="true")return true;if(r==="false")return false;if(r==="null")return null;if(r!=="undefined")return r}var a=class{constructor(e,t,i){this.client=e;this.collection=t;this.legacyId=i;}whereCondition;updateData;setData;deleteOp=false;promise;where(e){return this.whereCondition=e,this}update(e){return this.updateData=e,this}set(e){return this.setData=e,this}delete(){return this.deleteOp=true,this}getDocId(){if(this.legacyId)return this.legacyId;if(this.whereCondition&&this.whereCondition.id){let e=this.whereCondition.id;if(typeof e=="string")return e}throw new Error('Document ID not specified. Use .where({ id: "..." }) or doc(collection, id)')}async execute(){return this._execute()}async _execute(){let e=this.getDocId();if(this.deleteOp){await this.client.send("delete",{collection:this.collection,docId:e});return}if(this.updateData){await this.client.send("write",{collection:this.collection,docId:e,data:this.updateData,merge:true});return}if(this.setData){await this.client.send("write",{collection:this.collection,docId:e,data:this.setData,merge:false});return}return this.get()}then(e,t){return this.promise||(this.promise=this._execute()),this.promise.then(e,t)}async get(){let e=this.getDocId(),t=uuid2(18);return new Promise((i,o)=>{let n=this.client.subscribe(t,this.collection,e,void 0,s=>{s.type==="snapshot"&&(n(),i(s.data));});setTimeout(()=>{n(),o(new Error("Document fetch timeout"));},1e4);})}onSnapshot(e){let t=this.getDocId(),i=uuid2(18);return this.client.subscribe(i,this.collection,t,void 0,e)}},h=class{constructor(e,t,i){this.client=e;this.collection=t;this.id=i;}async get(){return new a(this.client,this.collection,this.id).get()}async set(e){await this.client.send("write",{collection:this.collection,docId:this.id,data:e,merge:false});}async update(e){await this.client.send("write",{collection:this.collection,docId:this.id,data:e,merge:true});}async delete(){await this.client.send("delete",{collection:this.collection,docId:this.id});}onSnapshot(e){let t=uuid2(18);return this.client.subscribe(t,this.collection,this.id,void 0,e)}},g=class r{constructor(e,t){this.client=e;this.collection=t;}queries=[];promise;doc(e){return new h(this.client,this.collection,e)}where(e){let t=new r(this.client,this.collection),i=w(e);return t.queries=[...this.queries,...i],t}async get(){return this._execute()}async _execute(){let e=uuid2(18);return new Promise((t,i)=>{let o=this.queries.length>0?this.queries:void 0,n=this.client.subscribe(e,this.collection,void 0,o,s=>{s.type==="snapshot"&&(n(),t(Array.isArray(s.data)?s.data:[s.data]));});setTimeout(()=>{n(),i(new Error("Collection fetch timeout"));},1e4);})}then(e,t){return this.promise||(this.promise=this._execute()),this.promise.then(e,t)}onSnapshot(e){let t=uuid2(18),i=this.queries.length>0?this.queries:void 0;return this.client.subscribe(t,this.collection,void 0,i,e)}async add(e){let t=uuid2(18),i=this.doc(t);return await i.set(e),i}update(e){return new a(this.client,this.collection).update(e)}delete(){return new a(this.client,this.collection).delete()}};async function I(r){let e=r.replace(/-----BEGIN PUBLIC KEY-----/,"").replace(/-----END PUBLIC KEY-----/,"").replace(/\s+/g,""),t=typeof atob<"u"?atob(e):Buffer.from(e,"base64").toString("binary"),i=new Uint8Array(t.length);for(let n=0;n<t.length;n++)i[n]=t.charCodeAt(n);return (globalThis.crypto??(await import('crypto')).webcrypto).subtle.importKey("spki",i.buffer,{name:"RSA-OAEP",hash:"SHA-256"},false,["encrypt"])}async function E(r,e){let t=await I(e),i=new TextEncoder().encode(JSON.stringify(r)),n=await(globalThis.crypto??(await import('crypto')).webcrypto).subtle.encrypt({name:"RSA-OAEP"},t,i),s=typeof btoa<"u"?btoa(String.fromCharCode(...new Uint8Array(n))):Buffer.from(n).toString("base64");return JSON.stringify({enc:"rsa",data:s})}var y=class{socket=null;reconnectInterval;maxReconnectDelay;isConnected=false;shouldReconnect=true;options;messageQueue=[];heartbeatInterval=null;connectionTimeout=null;constructor(e){this.options=e,this.reconnectInterval=e.reconnectDelay||2,this.maxReconnectDelay=e.maxReconnectDelay||60,this.log("Transport initialized",e.url);}connect(){if(this.socket){this.log("Socket already exists, skipping connection");return}this.log("Connecting to",this.options.url),this.socket=new WebSocket(this.options.url),this.connectionTimeout=setTimeout(()=>{this.isConnected||(this.log("Connection timeout"),this.socket?.close(),this.handleReconnect());},1e4),this.socket.onopen=()=>{this.connectionTimeout&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=null),this.isConnected=true,this.reconnectInterval=this.options.reconnectDelay||2,this.log("Connected to server"),this.options.onOpen?.(),this.startHeartbeat(),this.flushQueue();},this.socket.onmessage=e=>{try{let t=JSON.parse(e.data);this.options.onMessage(t);}catch(t){this.log("Parse error",t),this.options.onError?.(t);}},this.socket.onerror=e=>{this.log("WebSocket error",e),this.options.onError?.(new Error("WebSocket error"));},this.socket.onclose=e=>{this.connectionTimeout&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=null),this.isConnected=false,this.socket=null,this.stopHeartbeat(),this.log("Connection closed",e.code,e.reason),this.options.onClose?.(),e.code!==1e3&&this.shouldReconnect&&this.options.autoReconnect&&this.handleReconnect();};}handleReconnect(){let e=this.reconnectInterval*1e3;this.log(`Reconnecting in ${this.reconnectInterval}s...`),setTimeout(()=>{this.reconnectInterval=Math.min(this.reconnectInterval*2,this.maxReconnectDelay),this.connect();},e);}startHeartbeat(){this.heartbeatInterval=setInterval(()=>{this.isConnected&&this.send({type:"ping",id:Date.now().toString(),ts:Date.now()});},3e4);}stopHeartbeat(){this.heartbeatInterval&&(clearInterval(this.heartbeatInterval),this.heartbeatInterval=null);}flushQueue(){for(this.log("Flushing message queue",this.messageQueue.length);this.messageQueue.length>0;){let e=this.messageQueue.shift();e&&this.send(e);}}send(e){if(this.socket&&this.socket.readyState===WebSocket.OPEN){let t=i=>{try{this.socket.send(i),this.log("Sent message",e);}catch(o){this.log("Send error",o),this.messageQueue.push(e);}};this.options.publicKey?E(e,this.options.publicKey).then(t).catch(i=>{this.log("RSA encrypt error \u2014 sending plaintext",i),t(JSON.stringify(e));}):t(JSON.stringify(e));}else this.log("Socket not ready, queueing message"),this.messageQueue.push(e);}disconnect(){this.shouldReconnect=false,this.stopHeartbeat(),this.socket&&(this.socket.close(1e3,"Client disconnect"),this.socket=null),this.isConnected=false,this.log("Disconnected");}get connected(){return this.isConnected}log(...e){this.options.debug&&console.log("[FlareTransport]",...e);}};var b=class{transport;config;pendingAcks=new Map;subscriptions=new Map;offlineQueue=[];currentState="disconnected";connectionListeners=[];errorListeners=[];authToken;userId;isDebug=false;constructor(e){this.config={autoReconnect:true,reconnectDelay:2,maxReconnectDelay:60,debug:false,connectionTimeout:1e4,...e},this.isDebug=this.config.debug||false;let{hostname:t,port:i,protocol:o}=new URL(this.config.endpoint),n=o==="https:",c=`${n?"wss":"ws"}://${t}:${i||(n?"443":"80")}/?appId=${this.config.appId}${this.config.apiKey?`&apiKey=${this.config.apiKey}`:""}`;this.transport=new y({url:c,publicKey:this.config.publicKey,autoReconnect:this.config.autoReconnect,reconnectDelay:this.config.reconnectDelay,maxReconnectDelay:this.config.maxReconnectDelay,onMessage:m=>this.handleIncoming(m),onOpen:()=>this.onConnected(),onClose:()=>this.onDisconnected(),onError:m=>this.handleTransportError(m),debug:this.isDebug}),this.log("FlareClient initialized",e);}connect(){this.setState("connecting"),this.transport.connect();}disconnect(){this.transport.disconnect(),this.setState("disconnected");}collection(e){return new g(this,e)}doc(e,t){return t!==void 0?new h(this,e,t):new a(this,e)}async auth(e){let t=await this.send("auth",{token:e});if(t.type==="auth_ok")return this.authToken=e,this.userId=t.uid,this.log("Authentication successful",t.uid),{uid:t.uid,token:e};throw new Error("Authentication failed")}signOut(){this.authToken=void 0,this.userId=void 0,this.log("Signed out");}get currentUser(){return this.userId}get connectionState(){return this.currentState}get isConnected(){return this.currentState==="connected"}onConnectionStateChange(e){return this.connectionListeners.push(e),()=>{this.connectionListeners=this.connectionListeners.filter(t=>t!==e);}}onError(e){return this.errorListeners.push(e),()=>{this.errorListeners=this.errorListeners.filter(t=>t!==e);}}async ping(){let e=Date.now();return await this.send("ping",{}),Date.now()-e}async call(e,t={}){let i=await this.send("call",{topic:e,payload:t});if(!i.success)throw new Error(i.error??`CALL "${e}" failed`);return i.result}async syncOffline(){if(this.offlineQueue.length===0)return;this.log("Syncing offline operations",this.offlineQueue.length);let e=[...this.offlineQueue];this.offlineQueue.length=0;let t=await this.send("offline_sync",{operations:e});t.conflicts&&t.conflicts.length>0&&(this.log("Offline sync conflicts",t.conflicts),t.conflicts.forEach(i=>{let o=e.find(n=>n.id===i.operationId);o&&this.offlineQueue.push(o);}));}handleTransportError(e){this.log("Transport error",e),this.errorListeners.forEach(t=>{try{t(e);}catch(i){this.log("Error listener error",i);}});}onConnected(){this.setState("connected"),this.log("Connected to FlareServer"),this.offlineQueue.length>0&&this.syncOffline().catch(e=>{this.log("Offline sync failed",e);});}onDisconnected(){this.currentState!=="disconnected"&&this.setState("reconnecting"),this.log("Disconnected from FlareServer");}setState(e){this.currentState!==e&&(this.currentState=e,this.log("Connection state changed",e),this.connectionListeners.forEach(t=>{try{t(e);}catch(i){this.log("Connection listener error",i);}}));}handleIncoming(e){if(this.log("Received message",e.type,e),e.type==="ack"||e.type==="pong"||e.type==="auth_ok"||e.type==="call_response"){let t=this.pendingAcks.get(e.correlationId||e.id);t&&(t(e),this.pendingAcks.delete(e.correlationId||e.id));return}if(e.type==="error"){this.log("Server error",e.code,e.message);let t=new Error(`[${e.code}] ${e.message}`);if(this.errorListeners.forEach(i=>{try{i(t);}catch(o){this.log("Error listener error",o);}}),e.correlationId){let i=this.pendingAcks.get(e.correlationId);i&&(i(e),this.pendingAcks.delete(e.correlationId));}return}if(e.type==="snapshot"||e.type==="change"){let t=this.subscriptions.get(e.subscriptionId);if(t){let i={subscriptionId:e.subscriptionId,collection:e.collection,docId:e.docId,data:e.data,type:e.type==="snapshot"?"snapshot":"change",operation:e.operation};try{t(i);}catch(o){this.log("Subscription callback error",o);}}}}async send(e,t){return new Promise((i,o)=>{let n=uuid2(18),s={id:n,type:e,ts:Date.now(),...t};this.pendingAcks.set(n,d=>{d.type==="error"?o(new Error(`[${d.code}] ${d.message}`)):i(d);}),this.isConnected?this.transport.send(s):(this.log("Queueing message for offline",s),this.offlineQueue.push(s),o(new Error("Not connected - message queued"))),setTimeout(()=>{this.pendingAcks.has(n)&&(this.pendingAcks.delete(n),o(new Error("Request timeout")));},this.config.connectionTimeout);})}subscribe(e,t,i,o,n){return this.log("Creating subscription",e,t,i),this.subscriptions.set(e,n),this.send("subscribe",{collection:t,docId:i,query:o}).then(s=>{s.subscriptionId&&(this.subscriptions.delete(e),this.subscriptions.set(s.subscriptionId,n));}).catch(s=>{this.log("Subscription failed",s),this.subscriptions.delete(e);}),()=>{this.log("Unsubscribing",e),this.subscriptions.delete(e),this.isConnected&&this.send("unsubscribe",{subscriptionId:e}).catch(s=>this.log("Unsubscribe failed",s));}}log(...e){this.isDebug&&console.log("[FlareClient]",...e);}},T=b;var C=class extends Error{constructor(t,i,o){super(t);this.code=i;this.cause=o;this.name="ZuzFlareError";}};var l=null,$=r=>(l||(l=new T(r),l.connect()),l),G=()=>l,J=()=>{l&&(l.disconnect(),l=null);};var M=T;
|
|
2
|
+
export{g as CollectionReference,a as DocumentQueryBuilder,h as DocumentReference,v as FlareAction,C as FlareError,S as FlareEvent,$ as connectApp,M as default,J as disconnectFlare,G as getFlare};
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zuzjs/flare",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"core",
|
|
6
|
+
"zuz",
|
|
7
|
+
"zuz.js",
|
|
8
|
+
"zuz flare",
|
|
9
|
+
"zuz flare client",
|
|
10
|
+
"zuzjs"
|
|
11
|
+
],
|
|
12
|
+
"description": "Official JavaScript/TypeScript client for ZuzFlare Server - Self-hosted real-time database",
|
|
13
|
+
"author": "Zuz.js Team <support@zuz.com.pk>",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/zuzjs/flare-client.git"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/zuzjs/flare-client/issues"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://flare.zedgon.io",
|
|
24
|
+
"main": "./dist/index.cjs",
|
|
25
|
+
"module": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js",
|
|
31
|
+
"require": "./dist/index.cjs"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.17.0"
|
|
39
|
+
},
|
|
40
|
+
"sideEffects": [
|
|
41
|
+
"reflect-metadata"
|
|
42
|
+
],
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@zuzjs/auth": "^0.1.7",
|
|
45
|
+
"@zuzjs/core": "^0.3.8"
|
|
46
|
+
}
|
|
47
|
+
}
|