edmaxlabs-core 2.5.6 โ 2.6.7
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 +337 -194
- package/dist/index.cjs +706 -338
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +72 -54
- package/dist/index.d.ts +72 -54
- package/dist/index.mjs +706 -338
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -3,302 +3,445 @@
|
|
|
3
3
|
[](https://badge.fury.io/js/edmaxlabs-core)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
`edmaxlabs-core` is the official SDK for EdmaxLabs services
|
|
6
|
+
`edmaxlabs-core` is the official framework-agnostic SDK for EdmaxLabs services. It supports database access, realtime listeners, offline persistence, authentication, storage, and cloud functions.
|
|
7
7
|
|
|
8
|
-
##
|
|
9
|
-
|
|
10
|
-
- **Authentication** - Secure user authentication
|
|
11
|
-
- **Database** - Real-time database with offline support
|
|
12
|
-
- **Storage** - File upload/download
|
|
13
|
-
- **Cloud Functions** - Serverless function execution
|
|
14
|
-
- **Framework Agnostic** - Works with React, Vue, Angular, vanilla JS, Node.js
|
|
15
|
-
- **Offline Support** - Automatic sync when online
|
|
16
|
-
- **TypeScript** - Full TypeScript support
|
|
17
|
-
|
|
18
|
-
## ๐ฆ Installation
|
|
8
|
+
## Installation
|
|
19
9
|
|
|
20
10
|
```bash
|
|
21
11
|
npm install edmaxlabs-core
|
|
22
12
|
```
|
|
23
13
|
|
|
24
|
-
##
|
|
25
|
-
|
|
26
|
-
### Basic Usage
|
|
14
|
+
## Create an App
|
|
27
15
|
|
|
28
|
-
```
|
|
16
|
+
```ts
|
|
29
17
|
import EdmaxLabs from "edmaxlabs-core";
|
|
30
18
|
|
|
31
19
|
const app = new EdmaxLabs({
|
|
32
20
|
token: "your-api-token",
|
|
33
21
|
project: "your-project-id",
|
|
34
|
-
persistence: true,
|
|
22
|
+
persistence: true,
|
|
23
|
+
app_name: "my-web-app",
|
|
35
24
|
});
|
|
36
|
-
|
|
37
|
-
// Get data
|
|
38
|
-
const messages = await app.getDatabase.collection("messages").get();
|
|
39
|
-
console.log(messages);
|
|
40
25
|
```
|
|
41
26
|
|
|
42
|
-
|
|
27
|
+
Config options:
|
|
28
|
+
|
|
29
|
+
- `token`: required API token
|
|
30
|
+
- `project`: required project id
|
|
31
|
+
- `persistence`: enable offline persistence and sync
|
|
32
|
+
- `app_name`: optional cache namespace for the local persistence layer
|
|
33
|
+
- `default_bucket`: optional default storage bucket
|
|
34
|
+
|
|
35
|
+
## React Quick Start
|
|
43
36
|
|
|
44
37
|
```tsx
|
|
38
|
+
import { useEffect, useState } from "react";
|
|
45
39
|
import EdmaxLabs from "edmaxlabs-core";
|
|
46
40
|
|
|
47
41
|
const app = new EdmaxLabs({
|
|
48
|
-
token: "your-token",
|
|
42
|
+
token: "your-api-token",
|
|
49
43
|
project: "your-project-id",
|
|
50
44
|
persistence: true,
|
|
45
|
+
app_name: "chat-app",
|
|
51
46
|
});
|
|
52
47
|
|
|
53
|
-
function
|
|
54
|
-
const [messages, setMessages] = useState([]);
|
|
48
|
+
export function MessagesList() {
|
|
49
|
+
const [messages, setMessages] = useState<any[]>([]);
|
|
55
50
|
const [loading, setLoading] = useState(true);
|
|
56
51
|
|
|
57
52
|
useEffect(() => {
|
|
58
|
-
const unsubscribe = app.getDatabase
|
|
59
|
-
(
|
|
60
|
-
|
|
53
|
+
const unsubscribe = app.getDatabase
|
|
54
|
+
.collection("messages")
|
|
55
|
+
.onSnapshot((snapshots) => {
|
|
56
|
+
setMessages(snapshots.map((doc) => doc.data));
|
|
61
57
|
setLoading(false);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return unsubscribe;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return unsubscribe;
|
|
65
61
|
}, []);
|
|
66
62
|
|
|
67
|
-
if (loading) return <
|
|
63
|
+
if (loading) return <p>Loading...</p>;
|
|
68
64
|
|
|
69
65
|
return (
|
|
70
|
-
<
|
|
71
|
-
{messages.map(
|
|
72
|
-
<
|
|
66
|
+
<ul>
|
|
67
|
+
{messages.map((message) => (
|
|
68
|
+
<li key={message.id}>{message.text}</li>
|
|
73
69
|
))}
|
|
74
|
-
</
|
|
70
|
+
</ul>
|
|
75
71
|
);
|
|
76
72
|
}
|
|
77
73
|
```
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
74
|
+
|
|
75
|
+
## Realtime
|
|
76
|
+
|
|
77
|
+
### `onSnapshot`
|
|
78
|
+
|
|
79
|
+
`onSnapshot` is still fully supported. For collections and queries it returns the full current result set every time something changes.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
const unsubscribe = app.getDatabase
|
|
83
|
+
.collection("messages")
|
|
84
|
+
.onSnapshot((snapshots, change, changedDocId) => {
|
|
85
|
+
console.log("change:", change);
|
|
86
|
+
console.log("changedDocId:", changedDocId);
|
|
87
|
+
console.log("docs:", snapshots.map((doc) => doc.data));
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
unsubscribe();
|
|
85
91
|
```
|
|
86
92
|
|
|
87
|
-
|
|
93
|
+
Document listeners work the same way:
|
|
88
94
|
|
|
89
|
-
```
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
});
|
|
95
|
+
```ts
|
|
96
|
+
const unsubscribe = app.getDatabase
|
|
97
|
+
.collection("messages")
|
|
98
|
+
.doc("message-1")
|
|
99
|
+
.onSnapshot((snapshot, change) => {
|
|
100
|
+
console.log("change:", change);
|
|
101
|
+
console.log("doc:", snapshot?.data ?? null);
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### `onChildListener`
|
|
106
|
+
|
|
107
|
+
Yes, `onChildListener` is now available.
|
|
108
|
+
|
|
109
|
+
Use it when you want Firestore-style granular callbacks instead of diffing the full array yourself.
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
const unsubscribe = app.getDatabase
|
|
113
|
+
.collection("messages")
|
|
114
|
+
.onChildListener({
|
|
115
|
+
onChildAdded(snapshot, context) {
|
|
116
|
+
console.log("added:", snapshot.data);
|
|
117
|
+
console.log("index:", context.newIndex);
|
|
118
|
+
console.log("fromCache:", context.fromCache);
|
|
119
|
+
},
|
|
120
|
+
onChildUpdated(snapshot, context) {
|
|
121
|
+
console.log("updated:", snapshot.data);
|
|
122
|
+
console.log("oldIndex:", context.oldIndex);
|
|
123
|
+
console.log("newIndex:", context.newIndex);
|
|
124
|
+
console.log("previous:", context.previousDoc?.data ?? null);
|
|
125
|
+
},
|
|
126
|
+
onChildRemoved(snapshot, context) {
|
|
127
|
+
console.log("removed:", snapshot.data);
|
|
128
|
+
console.log("oldIndex:", context.oldIndex);
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
unsubscribe();
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
It also works on queries:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
const unsubscribe = app.getDatabase
|
|
139
|
+
.collection("messages")
|
|
140
|
+
.query
|
|
141
|
+
.where({ key: "roomId", op: "==", value: "general" })
|
|
142
|
+
.onChildListener({
|
|
143
|
+
onChildAdded(snapshot) {
|
|
144
|
+
console.log("room message:", snapshot.data);
|
|
145
|
+
},
|
|
146
|
+
});
|
|
97
147
|
```
|
|
98
148
|
|
|
99
|
-
|
|
149
|
+
Child listener callback context includes:
|
|
150
|
+
|
|
151
|
+
- `snapshots`: the current full query result
|
|
152
|
+
- `source`: `"initial" | "insert" | "update" | "delete"`
|
|
153
|
+
- `changedDocId`: the document that triggered the emission
|
|
154
|
+
- `fromCache`: whether the emission came from local cache
|
|
155
|
+
- `oldIndex`: previous index in the result set
|
|
156
|
+
- `newIndex`: next index in the result set
|
|
157
|
+
- `previousDoc`: previous version of the document when available
|
|
158
|
+
|
|
159
|
+
## Collections and Documents
|
|
160
|
+
|
|
161
|
+
### Get all documents
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
const users = await app.getDatabase.collection("users").get();
|
|
165
|
+
|
|
166
|
+
users.forEach((user) => {
|
|
167
|
+
console.log(user.id, user.data);
|
|
168
|
+
});
|
|
169
|
+
```
|
|
100
170
|
|
|
101
|
-
###
|
|
171
|
+
### Get one document
|
|
102
172
|
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
const collection = app.getDatabase.collection("users");
|
|
173
|
+
```ts
|
|
174
|
+
const user = await app.getDatabase.collection("users").doc("user-1").get();
|
|
106
175
|
|
|
107
|
-
|
|
108
|
-
|
|
176
|
+
if (user) {
|
|
177
|
+
console.log(user.data);
|
|
178
|
+
}
|
|
179
|
+
```
|
|
109
180
|
|
|
110
|
-
|
|
111
|
-
const user = await collection.doc("user-id").get();
|
|
181
|
+
### Create with generated id
|
|
112
182
|
|
|
113
|
-
|
|
114
|
-
await collection
|
|
115
|
-
|
|
116
|
-
|
|
183
|
+
```ts
|
|
184
|
+
const created = await app.getDatabase.collection("messages").add({
|
|
185
|
+
text: "Hello world",
|
|
186
|
+
roomId: "general",
|
|
187
|
+
createdAt: Date.now(),
|
|
117
188
|
});
|
|
189
|
+
```
|
|
118
190
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
191
|
+
### Set a known id
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
await app.getDatabase.collection("users").doc("user-1").set({
|
|
195
|
+
name: "Jane",
|
|
196
|
+
email: "jane@example.com",
|
|
122
197
|
});
|
|
198
|
+
```
|
|
123
199
|
|
|
124
|
-
|
|
125
|
-
await collection.doc("user-id").delete();
|
|
200
|
+
### Update a document
|
|
126
201
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
202
|
+
```ts
|
|
203
|
+
await app.getDatabase.collection("users").doc("user-1").update({
|
|
204
|
+
lastLoginAt: Date.now(),
|
|
130
205
|
});
|
|
131
206
|
```
|
|
132
207
|
|
|
133
|
-
###
|
|
134
|
-
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
const unsubscribeDoc = app.getDatabase.collection("users").doc("user-id").onSnapshot(
|
|
138
|
-
(snapshot, change) => {
|
|
139
|
-
console.log("Document:", snapshot?.data());
|
|
140
|
-
}
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
// Collection listener
|
|
144
|
-
const unsubscribeCollection = app.getDatabase.collection("messages").onSnapshot(
|
|
145
|
-
(documents, change) => {
|
|
146
|
-
console.log("Messages:", documents.map(doc => doc.data()));
|
|
147
|
-
}
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
// Cleanup when done
|
|
151
|
-
unsubscribeDoc();
|
|
152
|
-
unsubscribeCollection();
|
|
208
|
+
### Delete a document
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
await app.getDatabase.collection("users").doc("user-1").delete();
|
|
153
212
|
```
|
|
154
213
|
|
|
155
|
-
|
|
214
|
+
## Queries
|
|
156
215
|
|
|
157
|
-
|
|
158
|
-
// Upload file
|
|
159
|
-
const ref = app.getStorage.ref("avatars/user-id.jpg");
|
|
160
|
-
await ref.put(file);
|
|
216
|
+
Build queries with `collection.query.where(...)`.
|
|
161
217
|
|
|
162
|
-
|
|
163
|
-
const
|
|
218
|
+
```ts
|
|
219
|
+
const activeUsers = await app.getDatabase
|
|
220
|
+
.collection("users")
|
|
221
|
+
.query
|
|
222
|
+
.where({ key: "status", op: "==", value: "active" })
|
|
223
|
+
.where({ key: "age", op: ">=", value: 18 })
|
|
224
|
+
.get();
|
|
225
|
+
```
|
|
164
226
|
|
|
165
|
-
|
|
166
|
-
|
|
227
|
+
Supported operators:
|
|
228
|
+
|
|
229
|
+
- `==`
|
|
230
|
+
- `===`
|
|
231
|
+
- `!=`
|
|
232
|
+
- `<`
|
|
233
|
+
- `<=`
|
|
234
|
+
- `>`
|
|
235
|
+
- `>=`
|
|
236
|
+
- `in`
|
|
237
|
+
- `nin`
|
|
238
|
+
- `contains`
|
|
239
|
+
|
|
240
|
+
Query snapshots are supported too:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
const unsubscribe = app.getDatabase
|
|
244
|
+
.collection("users")
|
|
245
|
+
.query
|
|
246
|
+
.where({ key: "status", op: "==", value: "active" })
|
|
247
|
+
.onSnapshot((snapshots) => {
|
|
248
|
+
console.log("active users:", snapshots.map((doc) => doc.data));
|
|
249
|
+
});
|
|
167
250
|
```
|
|
168
251
|
|
|
169
|
-
|
|
252
|
+
## Offline Persistence
|
|
170
253
|
|
|
171
|
-
|
|
172
|
-
// Get current user
|
|
173
|
-
const user = app.getAuthentication.currentUser;
|
|
254
|
+
Enable it with `persistence: true`.
|
|
174
255
|
|
|
175
|
-
|
|
176
|
-
const
|
|
177
|
-
|
|
256
|
+
```ts
|
|
257
|
+
const app = new EdmaxLabs({
|
|
258
|
+
token: "your-api-token",
|
|
259
|
+
project: "your-project-id",
|
|
260
|
+
persistence: true,
|
|
178
261
|
});
|
|
179
262
|
```
|
|
180
263
|
|
|
181
|
-
|
|
264
|
+
What you get:
|
|
182
265
|
|
|
183
|
-
|
|
266
|
+
- local cached reads
|
|
267
|
+
- optimistic local writes
|
|
268
|
+
- background sync when the network returns
|
|
269
|
+
- realtime listeners backed by the local cache first
|
|
184
270
|
|
|
185
|
-
|
|
271
|
+
Useful helpers:
|
|
186
272
|
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
useEffect(() => {
|
|
190
|
-
const unsubscribe = app.getDatabase.collection("items").onSnapshot(callback);
|
|
191
|
-
return unsubscribe; // โ
Critical: prevent memory leaks
|
|
192
|
-
}, []);
|
|
193
|
-
}
|
|
194
|
-
```
|
|
273
|
+
```ts
|
|
274
|
+
app.isOfflineEnabled;
|
|
195
275
|
|
|
196
|
-
|
|
276
|
+
await app.sync();
|
|
277
|
+
await app.retrySync();
|
|
197
278
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
279
|
+
const failed = await app.getFailedMutations();
|
|
280
|
+
console.log(failed);
|
|
281
|
+
|
|
282
|
+
const usage = await app.getStorageUsage();
|
|
283
|
+
console.log(usage);
|
|
284
|
+
|
|
285
|
+
await app.clearCache();
|
|
286
|
+
|
|
287
|
+
const offline = app.offline();
|
|
288
|
+
console.log(offline.enabled);
|
|
289
|
+
console.log(offline.getListenerCount());
|
|
207
290
|
```
|
|
208
291
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
// Handle storage full
|
|
217
|
-
alert("Storage is full. Please clear some data.");
|
|
218
|
-
} else if (error.message.includes("Network")) {
|
|
219
|
-
// Handle offline
|
|
220
|
-
console.log("Will sync when online");
|
|
221
|
-
}
|
|
222
|
-
}
|
|
292
|
+
## Authentication
|
|
293
|
+
|
|
294
|
+
### Current user
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
const user = app.getAuthentication.currentUser();
|
|
298
|
+
console.log(user);
|
|
223
299
|
```
|
|
224
300
|
|
|
225
|
-
###
|
|
301
|
+
### Auth state listener
|
|
302
|
+
|
|
303
|
+
```ts
|
|
304
|
+
const unsubscribe = app.getAuthentication.authState({
|
|
305
|
+
onChange(user) {
|
|
306
|
+
console.log("signed in:", user);
|
|
307
|
+
},
|
|
308
|
+
onSignOut() {
|
|
309
|
+
console.log("signed out");
|
|
310
|
+
},
|
|
311
|
+
onDeleted() {
|
|
312
|
+
console.log("user deleted");
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
```
|
|
226
316
|
|
|
227
|
-
|
|
317
|
+
### Create account
|
|
228
318
|
|
|
229
|
-
```
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
319
|
+
```ts
|
|
320
|
+
const user = await app.getAuthentication.createUserWithEmailAndPassword({
|
|
321
|
+
email: "jane@example.com",
|
|
322
|
+
password: "secret123",
|
|
323
|
+
});
|
|
234
324
|
```
|
|
235
325
|
|
|
236
|
-
###
|
|
326
|
+
### Sign in
|
|
327
|
+
|
|
328
|
+
```ts
|
|
329
|
+
const user = await app.getAuthentication.signInWithEmailAndPassword({
|
|
330
|
+
email: "jane@example.com",
|
|
331
|
+
password: "secret123",
|
|
332
|
+
});
|
|
333
|
+
```
|
|
237
334
|
|
|
238
|
-
|
|
335
|
+
### Sign out
|
|
239
336
|
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
await doc.set(null); // Error: data cannot be null
|
|
243
|
-
await doc.set({ id: "123" }); // Error: 'id' is reserved
|
|
244
|
-
await doc.set([]); // Error: data cannot be an array
|
|
337
|
+
```ts
|
|
338
|
+
await app.getAuthentication.signOut();
|
|
245
339
|
```
|
|
246
340
|
|
|
247
|
-
##
|
|
341
|
+
## Storage
|
|
248
342
|
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
|
|
343
|
+
```ts
|
|
344
|
+
const uploaded = await app.getStorage.upload(file);
|
|
345
|
+
console.log(uploaded);
|
|
346
|
+
|
|
347
|
+
const fileMeta = await app.getStorage.get("file-id");
|
|
348
|
+
const fileInfo = await app.getStorage.getMeta("file-id");
|
|
252
349
|
|
|
253
|
-
|
|
254
|
-
|
|
350
|
+
await app.getStorage.delete("file-id");
|
|
351
|
+
```
|
|
255
352
|
|
|
256
|
-
|
|
257
|
-
|
|
353
|
+
## Cloud Functions
|
|
354
|
+
|
|
355
|
+
```ts
|
|
356
|
+
const result = await app.getFunctions.call("sendWelcomeEmail", {
|
|
357
|
+
userId: "user-1",
|
|
358
|
+
});
|
|
258
359
|
|
|
259
|
-
|
|
260
|
-
npm run lint
|
|
360
|
+
console.log(result);
|
|
261
361
|
```
|
|
262
362
|
|
|
263
|
-
##
|
|
363
|
+
## Batch Writes
|
|
364
|
+
|
|
365
|
+
```ts
|
|
366
|
+
const db = app.getDatabase;
|
|
367
|
+
const batch = db.batch();
|
|
368
|
+
|
|
369
|
+
batch.set(db.collection("users").doc("u1"), {
|
|
370
|
+
name: "Jane",
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
batch.update(db.collection("users").doc("u2"), {
|
|
374
|
+
online: true,
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
batch.delete(db.collection("users").doc("u3"));
|
|
378
|
+
|
|
379
|
+
await batch.commit();
|
|
380
|
+
```
|
|
264
381
|
|
|
265
|
-
|
|
266
|
-
- [Migration Guide](https://edmaxlabs.com/docs/migration)
|
|
267
|
-
- [Best Practices](https://edmaxlabs.com/docs/best-practices)
|
|
382
|
+
## Important Notes
|
|
268
383
|
|
|
269
|
-
|
|
384
|
+
- Always unsubscribe realtime listeners in React `useEffect` cleanup.
|
|
385
|
+
- `onSnapshot` returns full results for collections and queries.
|
|
386
|
+
- `onChildListener` gives you granular callbacks like Firestore.
|
|
387
|
+
- With persistence enabled, listeners can emit cached data first and then refresh with synced data.
|
|
388
|
+
- Query results while offline reflect the latest local cache available on that device.
|
|
270
389
|
|
|
271
|
-
|
|
390
|
+
## Minimal React Pattern
|
|
272
391
|
|
|
273
|
-
|
|
392
|
+
```tsx
|
|
393
|
+
useEffect(() => {
|
|
394
|
+
const unsubscribe = app.getDatabase
|
|
395
|
+
.collection("messages")
|
|
396
|
+
.onChildListener({
|
|
397
|
+
onChildAdded(snapshot) {
|
|
398
|
+
console.log("new message", snapshot.data);
|
|
399
|
+
},
|
|
400
|
+
onChildUpdated(snapshot) {
|
|
401
|
+
console.log("edited message", snapshot.data);
|
|
402
|
+
},
|
|
403
|
+
onChildRemoved(snapshot) {
|
|
404
|
+
console.log("deleted message", snapshot.id);
|
|
405
|
+
},
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
return unsubscribe;
|
|
409
|
+
}, []);
|
|
410
|
+
```
|
|
274
411
|
|
|
275
|
-
|
|
276
|
-
- [https://edmaxlabs.com/docs/database](https://edmaxlabs.com/docs/database)
|
|
277
|
-
---
|
|
412
|
+
## Manual SDK Testing
|
|
278
413
|
|
|
279
|
-
|
|
414
|
+
There is now a manual browser test harness inside this repo.
|
|
280
415
|
|
|
281
|
-
|
|
282
|
-
* Backend developers (Node.js)
|
|
283
|
-
* Teams building real-time apps or SaaS platforms
|
|
416
|
+
Build the SDK:
|
|
284
417
|
|
|
285
|
-
|
|
418
|
+
```bash
|
|
419
|
+
npm run manual:test
|
|
420
|
+
```
|
|
286
421
|
|
|
287
|
-
|
|
422
|
+
Start the local static server:
|
|
288
423
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
424
|
+
```bash
|
|
425
|
+
npm run manual:test:serve
|
|
426
|
+
```
|
|
292
427
|
|
|
293
|
-
|
|
428
|
+
Then open:
|
|
294
429
|
|
|
295
|
-
|
|
430
|
+
```text
|
|
431
|
+
http://localhost:4173/manual-tests/index.html
|
|
432
|
+
```
|
|
296
433
|
|
|
297
|
-
|
|
298
|
-
* **edmaxlabs-core** โ Runtime SDK for applications
|
|
434
|
+
What you can verify there:
|
|
299
435
|
|
|
300
|
-
|
|
436
|
+
- `collection.onSnapshot(...)`
|
|
437
|
+
- `query.onSnapshot(...)`
|
|
438
|
+
- `document.onSnapshot(...)`
|
|
439
|
+
- `collection.onChildListener(...)`
|
|
440
|
+
- create, set, update, delete flows
|
|
441
|
+
- persistence-enabled behavior in the browser
|
|
442
|
+
- offline/online transitions using devtools network controls
|
|
443
|
+
- multiple active listeners at once
|
|
301
444
|
|
|
302
|
-
##
|
|
445
|
+
## License
|
|
303
446
|
|
|
304
|
-
|
|
447
|
+
MIT
|