@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 +275 -33
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +176 -9
- package/dist/index.d.ts +176 -9
- package/dist/index.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,58 +1,300 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ZuzFlare Client
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Official JavaScript/TypeScript client for ZuzFlare Server.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@zuzjs/flare)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
## 📦 Installation
|
|
8
|
+
## Installation
|
|
11
9
|
|
|
12
10
|
```bash
|
|
13
11
|
npm install @zuzjs/flare
|
|
14
12
|
```
|
|
15
13
|
|
|
16
|
-
##
|
|
14
|
+
## Maintainer Rule
|
|
17
15
|
|
|
18
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { connectApp } from '@zuzjs/flare';
|
|
25
22
|
|
|
26
|
-
|
|
23
|
+
const app = connectApp({
|
|
24
|
+
endpoint: 'https://flare.zuzcdn.net',
|
|
25
|
+
appId: 'my-app',
|
|
26
|
+
apiKey: 'app-api-key',
|
|
27
|
+
});
|
|
27
28
|
|
|
28
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
##
|
|
276
|
+
## Template Collection Example
|
|
41
277
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
##
|
|
293
|
+
## Links
|
|
51
294
|
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
- [Documentation](https://flare.zuz.com.pk)
|
|
295
|
+
- Server package: @zuzjs/flare-server
|
|
296
|
+
- Documentation: https://flare.zuz.com.pk
|
|
55
297
|
|
|
56
|
-
##
|
|
298
|
+
## License
|
|
57
299
|
|
|
58
|
-
MIT
|
|
300
|
+
MIT
|