@zuzjs/flare 0.2.4 → 0.2.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 CHANGED
@@ -1,58 +1,300 @@
1
- # 🔥 ZuzFlare Client
1
+ # ZuzFlare Client
2
2
 
3
- > Official JavaScript/TypeScript client for ZuzFlare Server
3
+ Official JavaScript/TypeScript client for ZuzFlare Server.
4
4
 
5
5
  [![npm version](https://badge.fury.io/js/%40zuzjs%2Fflare.svg)](https://www.npmjs.com/package/@zuzjs/flare)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
- Real-time database client with Firebase-like API for your self-hosted ZuzFlare server.
9
-
10
- ## 📦 Installation
8
+ ## Installation
11
9
 
12
10
  ```bash
13
11
  npm install @zuzjs/flare
14
12
  ```
15
13
 
16
- ## 🚀 Quick Start
14
+ ## Maintainer Rule
17
15
 
18
- ```typescript
19
- import { FlareClient } from '@zuzjs/flare';
16
+ When adding or changing any public SDK API, update this README in the same commit and add a usage example for that API.
20
17
 
21
- const flare = new FlareClient({
22
- endpoint: 'http://localhost:5050',
23
- appId: 'my-app'
24
- });
18
+ ## Quick Start
19
+
20
+ ```ts
21
+ import { connectApp } from '@zuzjs/flare';
25
22
 
26
- flare.connect();
23
+ const app = connectApp({
24
+ endpoint: 'https://flare.zuzcdn.net',
25
+ appId: 'my-app',
26
+ apiKey: 'app-api-key',
27
+ });
27
28
 
28
- // Write data
29
- await flare.collection('users').doc('alice').set({
29
+ await app.collection('users').doc('alice').set({
30
30
  name: 'Alice',
31
- email: 'alice@example.com'
31
+ email: 'alice@example.com',
32
+ });
33
+
34
+ app.collection('users').onSnapshot((snapshot) => {
35
+ console.log('snapshot', snapshot);
36
+ });
37
+ ```
38
+
39
+ ## Public API Usage Examples
40
+
41
+ ### Core Instance
42
+
43
+ ```ts
44
+ import { connectApp, getFlare, disconnectFlare } from '@zuzjs/flare';
45
+
46
+ const app = connectApp({ endpoint: 'https://flare.zuzcdn.net', appId: 'my-app', apiKey: 'ak' });
47
+ const same = getFlare();
48
+ disconnectFlare();
49
+ ```
50
+
51
+ ### Auth Config And State
52
+
53
+ ```ts
54
+ const app = connectApp({ endpoint: 'https://flare.zuzcdn.net', appId: 'my-app', apiKey: 'ak' });
55
+
56
+ await app.ensureCsrfProtection();
57
+ const config = await app.loadAuthConfig();
58
+
59
+ const offConfig = app.onAuthConfigLoaded((next) => {
60
+ console.log('auth config loaded', next.providers);
61
+ });
62
+
63
+ const offState = app.onAuthStateChanged((session) => {
64
+ console.log('auth state', session?.uid);
65
+ });
66
+
67
+ const legacyOffState = app.onAuthStateChange((session) => {
68
+ console.log('legacy listener', session?.uid);
69
+ });
70
+
71
+ console.log('csrf cookie name', app.getCsrfCookieName());
72
+ console.log('csrf token', app.getCsrfToken());
73
+ console.log('current user', app.getCurrentUser());
74
+
75
+ offConfig();
76
+ offState();
77
+ legacyOffState();
78
+ ```
79
+
80
+ ### Email Password Auth
81
+
82
+ ```ts
83
+ const app = connectApp({ endpoint: 'https://flare.zuzcdn.net', appId: 'my-app', apiKey: 'ak' });
84
+
85
+ await app.createUserWithEmail('alice@example.com', 'StrongPassword123!');
86
+ await app.createUserWithEmailAndPassword('bob@example.com', 'StrongPassword123!');
87
+
88
+ await app.signInWithEmail('alice@example.com', 'StrongPassword123!');
89
+ await app.signInWithEmailAndPassword('bob@example.com', 'StrongPassword123!');
90
+
91
+ await app.signInOrCreateWithEmail('carol@example.com', 'StrongPassword123!');
92
+ await app.signInOrCreateWithEmailAndPassword('dave@example.com', 'StrongPassword123!');
93
+
94
+ await app.auth('<access-token>');
95
+ await app.refreshAuthSession();
96
+ await app.signOut();
97
+ ```
98
+
99
+ ### Email Verification And Recovery
100
+
101
+ ```ts
102
+ const app = connectApp({ endpoint: 'https://flare.zuzcdn.net', appId: 'my-app', apiKey: 'ak' });
103
+
104
+ const verifySent = await app.sendEmailVerification('alice@example.com');
105
+ await app.verifyEmailWithCode('alice@example.com', '123456');
106
+ await app.confirmEmailLink('<link-token>', 'alice@example.com');
107
+
108
+ const recoverySent = await app.sendAccountRecovery('alice@example.com');
109
+ await app.recoverAccountWithCode('alice@example.com', '123456', 'NextStrongPassword123!');
110
+ await app.recoverAccountWithToken('<recovery-token>', 'NextStrongPassword123!');
111
+
112
+ console.log(verifySent, recoverySent);
113
+ ```
114
+
115
+ ### OAuth Helpers
116
+
117
+ ```ts
118
+ const app = connectApp({ endpoint: 'https://flare.zuzcdn.net', appId: 'my-app', apiKey: 'ak' });
119
+
120
+ await app.signIn('google');
121
+ await app.signInWithGoogle();
122
+ await app.signInWithGitHub();
123
+ await app.signInWithFacebook();
124
+ await app.signInWithDropbox();
125
+
126
+ const redirectResult = await app.handleSignInRedirect();
127
+ console.log('oauth redirect result', redirectResult);
128
+ ```
129
+
130
+ ### SSR Token
131
+
132
+ ```ts
133
+ const app = connectApp({ endpoint: 'https://flare.zuzcdn.net', appId: 'my-app', apiKey: 'ak' });
134
+
135
+ const ssr = await app.issueSsrToken(120);
136
+ console.log(ssr.token, ssr.expires_in);
137
+ ```
138
+
139
+ ### Push APIs
140
+
141
+ ```ts
142
+ const app = connectApp({ endpoint: 'https://flare.zuzcdn.net', appId: 'my-app', apiKey: 'ak' });
143
+
144
+ await app.setupPushServiceWorker();
145
+ await app.requestPushPermission();
146
+
147
+ const { token } = await app.acquireBrowserPushToken();
148
+ await app.registerPushToken({ token, platform: 'web', topics: ['news'] });
149
+
150
+ await app.enableBrowserPush({ topics: ['marketing'] });
151
+
152
+ await app.sendPushNotification({
153
+ title: 'Hello',
154
+ body: 'Welcome back',
155
+ topic: 'news',
32
156
  });
33
157
 
34
- // Real-time updates
35
- flare.collection('users').onSnapshot((data) => {
36
- console.log('Users:', data);
158
+ await app.unregisterPushToken(token);
159
+ ```
160
+
161
+ ### Direct Query Helpers (Knex-Style)
162
+
163
+ `collection()` query chaining uses object-based logical steps and dedicated operator families:
164
+
165
+ - Logic: `where({...})`, `and({...})`, `or({...})`
166
+ - `in` family: `in`, `andIn`, `orIn`
167
+ - `notIn` family: `notIn`, `andNotIn`, `orNotIn`
168
+ - `arrayContains` family: `arrayContains`, `andArrayContains`, `orArrayContains`
169
+ - `arrayContainsAny` family: `arrayContainsAny`, `andArrayContainsAny`, `orArrayContainsAny`
170
+ - `some` family (array of objects): `some`, `andSome`, `orSome`
171
+ - `like` family: `like`, `andLike`, `orLike`
172
+ - `notLike` family: `notLike`, `andNotLike`, `orNotLike`
173
+ - `exists` family: `exists`, `andExists`, `orExists`
174
+ - `notExists` family: `notExists`, `andNotExists`, `orNotExists`
175
+
176
+ ```ts
177
+ const uid = 'bDEgnSqsEDT5qdDdtOX1';
178
+
179
+ const boards = await app
180
+ .collection('boards')
181
+ .where({ uid })
182
+ .orArrayContains('team', uid)
183
+ .orderBy('createdAt', 'desc')
184
+ .limit(20)
185
+ .get();
186
+
187
+ const sameBoards = await app
188
+ .collection('boards')
189
+ .where({ uid })
190
+ .orArrayContains('team', uid)
191
+ .get();
192
+
193
+ const active = await app
194
+ .collection('tasks')
195
+ .in('status', ['todo', 'doing'])
196
+ .andArrayContainsAny('labels', ['urgent', 'backend'])
197
+ .andLike('title', '%bug%')
198
+ .get();
199
+
200
+ const boardAccess = await app
201
+ .collection('boards')
202
+ .some('team', { uid: 'xyz', role: 1 })
203
+ .get();
204
+ ```
205
+
206
+ ### Template-Based Email APIs
207
+
208
+ ### Security Rules Example (Boards Owner Or Team Member)
209
+
210
+ For a `boards` document shape like:
211
+
212
+ ```json
213
+ {
214
+ "uid": "ownerUid",
215
+ "team": ["memberUid1", "memberUid2"]
216
+ }
217
+ ```
218
+
219
+ Use this DSL to allow read for owner or team member, and allow write only for owner:
220
+
221
+ ```txt
222
+ service cloud.firestore {
223
+ match /databases/{database}/documents {
224
+ function isOwner(ownerUid) {
225
+ return auth != null && auth.uid == ownerUid;
226
+ }
227
+
228
+ function isTeamMember(teamUids) {
229
+ return auth != null && auth.uid in teamUids;
230
+ }
231
+
232
+ match /boards/{boardId} {
233
+ allow read: if isOwner(resourceData.uid) || isTeamMember(resourceData.team);
234
+ allow create, update, delete: if isOwner(resourceData.uid);
235
+ }
236
+ }
237
+ }
238
+ ```
239
+
240
+ Tip: if you set owner uid at create time, prefer checking `requestData.uid` on create and `resourceData.uid` on update/delete.
241
+
242
+ Emails are sent only through app-level templates stored in `_flare_email_templates`.
243
+
244
+ Template placeholders use `{key}` syntax and are replaced from `values`.
245
+
246
+ If template has `includeVerificationLink: true`, server generates a link in `_flare_email_links` and injects:
247
+
248
+ - `{verificationLink}`
249
+ - `{verifyUrl}`
250
+ - `{verificationToken}`
251
+
252
+ ```ts
253
+ const app = connectApp({ endpoint: 'https://flare.zuzcdn.net', appId: 'my-app', apiKey: 'ak' });
254
+
255
+ const sendRes = await app.sendEmail({
256
+ to: 'alice@example.com',
257
+ tag: 'team_invite',
258
+ values: {
259
+ displayName: 'Alice',
260
+ inviterName: 'Bob',
261
+ teamName: 'Product',
262
+ },
263
+ });
264
+
265
+ console.log(sendRes.sent, sendRes.tag, sendRes.verifyUrl);
266
+
267
+ const verifyRes = await app.verifyEmailLink({
268
+ token: '<token-from-link>',
269
+ tag: 'team_invite',
270
+ email: 'alice@example.com',
37
271
  });
272
+
273
+ console.log(verifyRes.verified, verifyRes.tag);
38
274
  ```
39
275
 
40
- ## 📖 Full Documentation
276
+ ## Template Collection Example
41
277
 
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
278
+ Example document in `_flare_email_templates`:
279
+
280
+ ```json
281
+ {
282
+ "tag": "team_invite",
283
+ "enabled": true,
284
+ "subject": "Hi {displayName}, you are invited to {teamName}",
285
+ "text": "Hello {displayName},\n\n{inviterName} invited you.\n\nOpen: {verificationLink}",
286
+ "html": "<p>Hello {displayName}</p><p>{inviterName} invited you.</p><p><a href=\"{verificationLink}\">Accept</a></p>",
287
+ "includeVerificationLink": true,
288
+ "verifyUrl": "https://app.example.com/accept?token=__TOKEN__&appId=__APP_ID__&tag=__TAG__",
289
+ "verificationTtlHours": 72
290
+ }
291
+ ```
49
292
 
50
- ## 🔗 Links
293
+ ## Links
51
294
 
52
- - [Server Package](@zuzjs/flare-server)
53
- - [GitHub](https://github.com/zuzjs/flare-client)
54
- - [Documentation](https://flare.zuz.com.pk)
295
+ - Server package: @zuzjs/flare-server
296
+ - Documentation: https://flare.zuz.com.pk
55
297
 
56
- ## 📄 License
298
+ ## License
57
299
 
58
- MIT © Zuz.js Team
300
+ MIT