@rivium/sync-node 0.1.0
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 +440 -0
- package/dist/index.d.mts +337 -0
- package/dist/index.d.ts +337 -0
- package/dist/index.js +888 -0
- package/dist/index.mjs +845 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Rivium
|
|
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,440 @@
|
|
|
1
|
+
# @rivium/sync-node
|
|
2
|
+
|
|
3
|
+
Official Node.js SDK for RiviumSync Realtime Database. Designed for server-side applications with admin-level access.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Admin-level access** - Bypasses security rules by default
|
|
8
|
+
- **Full CRUD operations** - Create, read, update, delete documents
|
|
9
|
+
- **Query support** - Filters, sorting, pagination
|
|
10
|
+
- **Batch operations** - Atomic writes across multiple documents
|
|
11
|
+
- **Optional realtime** - MQTT-based subscriptions when needed
|
|
12
|
+
- **TypeScript** - Full type definitions included
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# npm
|
|
18
|
+
npm install @rivium/sync-node
|
|
19
|
+
|
|
20
|
+
# yarn
|
|
21
|
+
yarn add @rivium/sync-node
|
|
22
|
+
|
|
23
|
+
# pnpm
|
|
24
|
+
pnpm add @rivium/sync-node
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { RiviumSyncAdmin } from '@rivium/sync-node';
|
|
31
|
+
|
|
32
|
+
// Initialize with your Project API Key and Server Secret
|
|
33
|
+
const riviumSync = new RiviumSyncAdmin({
|
|
34
|
+
apiKey: process.env.RIVIUM_SYNC_API_KEY, // nl_live_xxx or nl_test_xxx
|
|
35
|
+
serverSecret: process.env.RIVIUM_SYNC_SERVER_SECRET, // nl_srv_xxx - Required for server-side operations
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Get a database reference (database must be created via dashboard first)
|
|
39
|
+
const db = riviumSync.database('your-database-id');
|
|
40
|
+
|
|
41
|
+
// Get a collection reference
|
|
42
|
+
const users = db.collection('users');
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
> **Note:** Both `apiKey` and `serverSecret` are required for all server-side SDK operations. You can find these credentials in your [AuthLeap Dashboard](https://console.authleap.com) when you create a project. Database creation and deletion is managed via the dashboard, not via SDK.
|
|
46
|
+
|
|
47
|
+
## CRUD Operations
|
|
48
|
+
|
|
49
|
+
### Create a Document
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// Add with auto-generated ID
|
|
53
|
+
const newUser = await users.add({
|
|
54
|
+
name: 'John Doe',
|
|
55
|
+
email: 'john@example.com',
|
|
56
|
+
age: 28,
|
|
57
|
+
createdAt: new Date().toISOString(),
|
|
58
|
+
});
|
|
59
|
+
console.log('Created user with ID:', newUser.id);
|
|
60
|
+
|
|
61
|
+
// Set with specific ID
|
|
62
|
+
await users.doc('user-123').set({
|
|
63
|
+
name: 'Jane Doe',
|
|
64
|
+
email: 'jane@example.com',
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Read Documents
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
// Get a single document
|
|
72
|
+
const user = await users.get('user-123');
|
|
73
|
+
if (user) {
|
|
74
|
+
console.log('User name:', user.data.name);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Check if document exists
|
|
78
|
+
const exists = await users.doc('user-123').exists();
|
|
79
|
+
|
|
80
|
+
// Get all documents
|
|
81
|
+
const allUsers = await users.getAll();
|
|
82
|
+
console.log('Total users:', allUsers.length);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Update Documents
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// Partial update (merge)
|
|
89
|
+
await users.doc('user-123').update({
|
|
90
|
+
age: 29,
|
|
91
|
+
updatedAt: new Date().toISOString(),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Full replace
|
|
95
|
+
await users.doc('user-123').set({
|
|
96
|
+
name: 'John Updated',
|
|
97
|
+
email: 'john.new@example.com',
|
|
98
|
+
age: 29,
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Delete Documents
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
await users.doc('user-123').delete();
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Querying
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// Build queries with fluent API
|
|
112
|
+
const adults = await users
|
|
113
|
+
.where('age', '>=', 18)
|
|
114
|
+
.where('status', '==', 'active')
|
|
115
|
+
.orderBy('createdAt', 'desc')
|
|
116
|
+
.limit(20)
|
|
117
|
+
.get();
|
|
118
|
+
|
|
119
|
+
// Get first result only
|
|
120
|
+
const firstUser = await users
|
|
121
|
+
.where('email', '==', 'john@example.com')
|
|
122
|
+
.query()
|
|
123
|
+
.getFirst();
|
|
124
|
+
|
|
125
|
+
// Count matching documents
|
|
126
|
+
const activeCount = await users
|
|
127
|
+
.where('status', '==', 'active')
|
|
128
|
+
.query()
|
|
129
|
+
.count();
|
|
130
|
+
|
|
131
|
+
// Pagination
|
|
132
|
+
const page1 = await users.orderBy('name').limit(10).get();
|
|
133
|
+
const page2 = await users.orderBy('name').limit(10).offset(10).get();
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Available Query Operators
|
|
137
|
+
|
|
138
|
+
| Operator | Description |
|
|
139
|
+
|----------|-------------|
|
|
140
|
+
| `==` | Equal |
|
|
141
|
+
| `!=` | Not equal |
|
|
142
|
+
| `<` | Less than |
|
|
143
|
+
| `<=` | Less than or equal |
|
|
144
|
+
| `>` | Greater than |
|
|
145
|
+
| `>=` | Greater than or equal |
|
|
146
|
+
| `in` | Value in array |
|
|
147
|
+
| `not-in` | Value not in array |
|
|
148
|
+
| `array-contains` | Array contains value |
|
|
149
|
+
|
|
150
|
+
## Batch Operations
|
|
151
|
+
|
|
152
|
+
Execute multiple writes atomically:
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
const batch = riviumSync.batch();
|
|
156
|
+
|
|
157
|
+
// Add operations to the batch
|
|
158
|
+
batch.set(users.doc('user1'), { name: 'User 1', status: 'active' });
|
|
159
|
+
batch.update(users.doc('user2'), { lastSeen: new Date().toISOString() });
|
|
160
|
+
batch.delete(users.doc('user3'));
|
|
161
|
+
|
|
162
|
+
// Commit all operations
|
|
163
|
+
await batch.commit();
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Realtime Updates (Optional)
|
|
167
|
+
|
|
168
|
+
Enable realtime subscriptions for server-side event processing:
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
const riviumSync = new RiviumSyncAdmin({
|
|
172
|
+
apiKey: process.env.RIVIUM_SYNC_API_KEY!,
|
|
173
|
+
serverSecret: process.env.RIVIUM_SYNC_SERVER_SECRET!,
|
|
174
|
+
enableRealtime: true, // Enable MQTT connection
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Listen to a single document
|
|
178
|
+
const unsubscribe = users.doc('user-123').onSnapshot((user) => {
|
|
179
|
+
if (user) {
|
|
180
|
+
console.log('User updated:', user.data);
|
|
181
|
+
} else {
|
|
182
|
+
console.log('User was deleted');
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Listen to a collection
|
|
187
|
+
const unsubscribeAll = users.onSnapshot((allUsers) => {
|
|
188
|
+
console.log('Users changed, count:', allUsers.length);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Listen to query results
|
|
192
|
+
const unsubscribeQuery = users
|
|
193
|
+
.where('status', '==', 'online')
|
|
194
|
+
.onSnapshot((onlineUsers) => {
|
|
195
|
+
console.log('Online users:', onlineUsers.length);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Stop listening when done
|
|
199
|
+
unsubscribe();
|
|
200
|
+
unsubscribeAll();
|
|
201
|
+
unsubscribeQuery();
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Configuration Options
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
const riviumSync = new RiviumSyncAdmin({
|
|
208
|
+
// Required
|
|
209
|
+
apiKey: 'nl_live_xxxxxxxxxxxxxxxxxxxxx', // Required - from AuthLeap Dashboard
|
|
210
|
+
serverSecret: 'nl_srv_xxxxxxxxxxxxxxxxxxxxx', // Required - from AuthLeap Dashboard
|
|
211
|
+
|
|
212
|
+
// Optional
|
|
213
|
+
enableRealtime: false, // Enable MQTT subscriptions
|
|
214
|
+
logLevel: RiviumSyncLogLevel.ERROR, // Logging level
|
|
215
|
+
timeout: 30000, // Request timeout in ms
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Credentials
|
|
220
|
+
|
|
221
|
+
| Credential | Format | Description |
|
|
222
|
+
|------------|--------|-------------|
|
|
223
|
+
| **API Key** | `nl_live_xxx` or `nl_test_xxx` | Used for client-side SDKs and server-side SDKs |
|
|
224
|
+
| **Server Secret** | `nl_srv_xxx` | **Required** for server-side operations. Never expose in client-side code. |
|
|
225
|
+
|
|
226
|
+
Both credentials are generated when you create a project in the [AuthLeap Dashboard](https://console.authleap.com). Store them securely and never commit them to version control.
|
|
227
|
+
|
|
228
|
+
### Log Levels
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
import { RiviumSyncLogLevel } from '@rivium/sync-node';
|
|
232
|
+
|
|
233
|
+
RiviumSyncLogLevel.NONE // No logs
|
|
234
|
+
RiviumSyncLogLevel.ERROR // Only errors
|
|
235
|
+
RiviumSyncLogLevel.WARNING // Errors and warnings
|
|
236
|
+
RiviumSyncLogLevel.INFO // General info
|
|
237
|
+
RiviumSyncLogLevel.DEBUG // Debug info
|
|
238
|
+
RiviumSyncLogLevel.VERBOSE // Everything
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## TypeScript Support
|
|
242
|
+
|
|
243
|
+
Full TypeScript support with generics:
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
interface User {
|
|
247
|
+
name: string;
|
|
248
|
+
email: string;
|
|
249
|
+
age: number;
|
|
250
|
+
status: 'active' | 'inactive';
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const users = db.collection<User>('users');
|
|
254
|
+
|
|
255
|
+
// All operations are now typed
|
|
256
|
+
const newUser = await users.add({
|
|
257
|
+
name: 'John',
|
|
258
|
+
email: 'john@example.com',
|
|
259
|
+
age: 28,
|
|
260
|
+
status: 'active',
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Type inference works
|
|
264
|
+
const user = await users.get('user-123');
|
|
265
|
+
if (user) {
|
|
266
|
+
console.log(user.data.name); // string
|
|
267
|
+
console.log(user.data.age); // number
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Error Handling
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
import { RiviumSyncError, RiviumSyncErrorCode } from '@rivium/sync-node';
|
|
275
|
+
|
|
276
|
+
try {
|
|
277
|
+
await users.get('nonexistent-id');
|
|
278
|
+
} catch (error) {
|
|
279
|
+
if (error instanceof RiviumSyncError) {
|
|
280
|
+
console.error('Error code:', error.code);
|
|
281
|
+
console.error('Message:', error.message);
|
|
282
|
+
console.error('Details:', error.details);
|
|
283
|
+
|
|
284
|
+
if (error.code === RiviumSyncErrorCode.DOCUMENT_NOT_FOUND) {
|
|
285
|
+
// Handle not found
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Use Cases
|
|
292
|
+
|
|
293
|
+
### Backend API Server
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
// Express.js example
|
|
297
|
+
import express from 'express';
|
|
298
|
+
import { RiviumSyncAdmin } from '@rivium/sync-node';
|
|
299
|
+
|
|
300
|
+
const app = express();
|
|
301
|
+
const riviumSync = new RiviumSyncAdmin({
|
|
302
|
+
apiKey: process.env.RIVIUM_SYNC_API_KEY!,
|
|
303
|
+
serverSecret: process.env.RIVIUM_SYNC_SERVER_SECRET!,
|
|
304
|
+
});
|
|
305
|
+
const db = riviumSync.database('my-database');
|
|
306
|
+
|
|
307
|
+
app.get('/api/users', async (req, res) => {
|
|
308
|
+
const users = await db.collection('users').getAll();
|
|
309
|
+
res.json(users);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
app.post('/api/users', async (req, res) => {
|
|
313
|
+
const user = await db.collection('users').add(req.body);
|
|
314
|
+
res.json(user);
|
|
315
|
+
});
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Serverless Functions
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
// AWS Lambda example
|
|
322
|
+
import { RiviumSyncAdmin } from '@rivium/sync-node';
|
|
323
|
+
|
|
324
|
+
const riviumSync = new RiviumSyncAdmin({
|
|
325
|
+
apiKey: process.env.RIVIUM_SYNC_API_KEY!,
|
|
326
|
+
serverSecret: process.env.RIVIUM_SYNC_SERVER_SECRET!,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
export async function handler(event) {
|
|
330
|
+
const db = riviumSync.database('my-database');
|
|
331
|
+
const users = await db.collection('users')
|
|
332
|
+
.where('status', '==', 'active')
|
|
333
|
+
.get();
|
|
334
|
+
|
|
335
|
+
return {
|
|
336
|
+
statusCode: 200,
|
|
337
|
+
body: JSON.stringify(users),
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Data Migration Script
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
import { RiviumSyncAdmin } from '@rivium/sync-node';
|
|
346
|
+
|
|
347
|
+
const riviumSync = new RiviumSyncAdmin({
|
|
348
|
+
apiKey: process.env.RIVIUM_SYNC_API_KEY!,
|
|
349
|
+
serverSecret: process.env.RIVIUM_SYNC_SERVER_SECRET!,
|
|
350
|
+
});
|
|
351
|
+
const db = riviumSync.database('my-database');
|
|
352
|
+
|
|
353
|
+
async function migrate() {
|
|
354
|
+
const users = await db.collection('users').getAll();
|
|
355
|
+
|
|
356
|
+
const batch = riviumSync.batch();
|
|
357
|
+
for (const user of users) {
|
|
358
|
+
// Add migration logic
|
|
359
|
+
batch.update(db.collection('users').doc(user.id), {
|
|
360
|
+
migratedAt: new Date().toISOString(),
|
|
361
|
+
version: 2,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
await batch.commit();
|
|
366
|
+
console.log('Migration complete!');
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
migrate();
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## API Reference
|
|
373
|
+
|
|
374
|
+
### RiviumSyncAdmin
|
|
375
|
+
|
|
376
|
+
| Method | Description |
|
|
377
|
+
|--------|-------------|
|
|
378
|
+
| `database(id)` | Get a database reference |
|
|
379
|
+
| `batch()` | Create a write batch |
|
|
380
|
+
| `disconnect()` | Disconnect from realtime |
|
|
381
|
+
| `setLogLevel(level)` | Change log level |
|
|
382
|
+
|
|
383
|
+
### SyncDatabase
|
|
384
|
+
|
|
385
|
+
| Method | Description |
|
|
386
|
+
|--------|-------------|
|
|
387
|
+
| `collection<T>(id)` | Get a typed collection reference |
|
|
388
|
+
|
|
389
|
+
### SyncCollection
|
|
390
|
+
|
|
391
|
+
| Method | Description |
|
|
392
|
+
|--------|-------------|
|
|
393
|
+
| `doc(id)` | Get a document reference |
|
|
394
|
+
| `add(data)` | Create document with auto ID |
|
|
395
|
+
| `get(id)` | Get a single document |
|
|
396
|
+
| `getAll(options?)` | Get all documents |
|
|
397
|
+
| `where(field, op, value)` | Start a query |
|
|
398
|
+
| `orderBy(field, direction?)` | Start a sorted query |
|
|
399
|
+
| `limit(count)` | Start a limited query |
|
|
400
|
+
| `query()` | Get query builder |
|
|
401
|
+
| `onSnapshot(callback, options?)` | Listen to changes |
|
|
402
|
+
|
|
403
|
+
### SyncDocumentRef
|
|
404
|
+
|
|
405
|
+
| Method | Description |
|
|
406
|
+
|--------|-------------|
|
|
407
|
+
| `get()` | Get document data |
|
|
408
|
+
| `exists()` | Check if document exists |
|
|
409
|
+
| `set(data)` | Set document (overwrite) |
|
|
410
|
+
| `update(data)` | Update document (merge) |
|
|
411
|
+
| `delete()` | Delete document |
|
|
412
|
+
| `onSnapshot(callback)` | Listen to changes |
|
|
413
|
+
|
|
414
|
+
### SyncQuery
|
|
415
|
+
|
|
416
|
+
| Method | Description |
|
|
417
|
+
|--------|-------------|
|
|
418
|
+
| `where(field, op, value)` | Add filter |
|
|
419
|
+
| `orderBy(field, direction?)` | Set ordering |
|
|
420
|
+
| `limit(count)` | Limit results |
|
|
421
|
+
| `offset(count)` | Skip results |
|
|
422
|
+
| `startAfter(count)` | Alias for offset |
|
|
423
|
+
| `get()` | Execute query |
|
|
424
|
+
| `getFirst()` | Get first result |
|
|
425
|
+
| `count()` | Count results |
|
|
426
|
+
| `onSnapshot(callback)` | Listen to query |
|
|
427
|
+
|
|
428
|
+
### WriteBatch
|
|
429
|
+
|
|
430
|
+
| Method | Description |
|
|
431
|
+
|--------|-------------|
|
|
432
|
+
| `set(docRef, data)` | Add set operation |
|
|
433
|
+
| `update(docRef, data)` | Add update operation |
|
|
434
|
+
| `delete(docRef)` | Add delete operation |
|
|
435
|
+
| `commit()` | Execute all operations |
|
|
436
|
+
| `size` | Number of pending operations |
|
|
437
|
+
|
|
438
|
+
## License
|
|
439
|
+
|
|
440
|
+
MIT
|