@raisindb/client 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/README.md +734 -0
- package/dist/index.d.mts +6936 -0
- package/dist/index.d.ts +6936 -0
- package/dist/index.js +10966 -0
- package/dist/index.mjs +10836 -0
- package/package.json +76 -0
package/README.md
ADDED
|
@@ -0,0 +1,734 @@
|
|
|
1
|
+
# RaisinDB JavaScript/TypeScript Client SDK
|
|
2
|
+
|
|
3
|
+
A universal WebSocket client for RaisinDB that works seamlessly in browser, Node.js, and serverless environments.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Universal Compatibility**: Works in browser, Node.js, and serverless environments
|
|
8
|
+
- **TypeScript First**: Full TypeScript support with comprehensive type definitions
|
|
9
|
+
- **Auto-Reconnection**: Automatic reconnection with exponential backoff
|
|
10
|
+
- **Event Subscriptions**: Real-time event streaming with filtered subscriptions
|
|
11
|
+
- **MessagePack Protocol**: Efficient binary protocol using MessagePack
|
|
12
|
+
- **JWT Authentication**: Built-in authentication and token management
|
|
13
|
+
- **Identity Auth**: Email/password login, registration, session management
|
|
14
|
+
- **Auth State Changes**: Firebase/Supabase-style `onAuthStateChange()` listener
|
|
15
|
+
- **User Home Subscriptions**: Auto-subscribe to user profile changes
|
|
16
|
+
- **SQL Queries**: Template literal support for safe SQL queries
|
|
17
|
+
- **Type-Safe**: Full type safety for all operations
|
|
18
|
+
- **Framework Agnostic**: Works with React, Vue, Svelte, Angular, or vanilla JS
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @raisindb/client
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
For Node.js environments, also install the WebSocket library:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install ws
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { RaisinClient } from '@raisindb/client';
|
|
36
|
+
|
|
37
|
+
// Create client
|
|
38
|
+
const client = new RaisinClient('raisin://localhost:8080/sys/default');
|
|
39
|
+
|
|
40
|
+
// Connect and authenticate
|
|
41
|
+
await client.connect();
|
|
42
|
+
await client.authenticate({
|
|
43
|
+
username: 'admin',
|
|
44
|
+
password: 'password'
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Get database and workspace
|
|
48
|
+
const db = client.database('my_repo');
|
|
49
|
+
const ws = db.workspace('content');
|
|
50
|
+
|
|
51
|
+
// Create a node
|
|
52
|
+
const node = await ws.nodes().create({
|
|
53
|
+
type: 'Page',
|
|
54
|
+
path: '/home',
|
|
55
|
+
properties: {
|
|
56
|
+
title: 'Home Page',
|
|
57
|
+
description: 'Welcome to our site'
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
console.log('Created node:', node);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Usage Examples
|
|
65
|
+
|
|
66
|
+
### Admin Authentication
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { RaisinClient, ConnectionState } from '@raisindb/client';
|
|
70
|
+
|
|
71
|
+
const client = new RaisinClient('raisin://localhost:8080/sys/default', {
|
|
72
|
+
connection: {
|
|
73
|
+
autoReconnect: true,
|
|
74
|
+
heartbeatInterval: 30000,
|
|
75
|
+
},
|
|
76
|
+
requestTimeout: 30000,
|
|
77
|
+
defaultBranch: 'main',
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Listen to connection state changes (public API)
|
|
81
|
+
const unsubscribe = client.onConnectionStateChange((state) => {
|
|
82
|
+
console.log('Connection state:', state);
|
|
83
|
+
if (state === ConnectionState.Disconnected) {
|
|
84
|
+
showOfflineIndicator();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Connect and authenticate (admin user)
|
|
89
|
+
await client.connect();
|
|
90
|
+
await client.authenticate({ username: 'admin', password: 'password' });
|
|
91
|
+
|
|
92
|
+
console.log('Authenticated:', client.isAuthenticated());
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Identity Authentication (Email/Password)
|
|
96
|
+
|
|
97
|
+
For user-facing apps with email/password authentication:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { RaisinClient, LocalStorageTokenStorage } from '@raisindb/client';
|
|
101
|
+
|
|
102
|
+
// Create client with persistent token storage
|
|
103
|
+
const client = new RaisinClient('ws://localhost:8081/sys/default/myrepo', {
|
|
104
|
+
tokenStorage: new LocalStorageTokenStorage('myapp'),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Login with email/password
|
|
108
|
+
const user = await client.loginWithEmail('user@example.com', 'password', 'myrepo');
|
|
109
|
+
console.log('Logged in as:', user.email);
|
|
110
|
+
console.log('User home path:', user.userHome); // e.g., '/users/internal/john-at-example-com'
|
|
111
|
+
|
|
112
|
+
// Register new user
|
|
113
|
+
const newUser = await client.registerWithEmail(
|
|
114
|
+
'newuser@example.com',
|
|
115
|
+
'password123',
|
|
116
|
+
'myrepo',
|
|
117
|
+
'John Doe' // display name
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// Logout (reconnects as anonymous by default)
|
|
121
|
+
await client.logout();
|
|
122
|
+
|
|
123
|
+
// Or fully disconnect
|
|
124
|
+
await client.logout({ disconnect: true });
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Session Restoration
|
|
128
|
+
|
|
129
|
+
Restore user session on app startup:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
const client = new RaisinClient('ws://localhost:8081/sys/default/myrepo', {
|
|
133
|
+
tokenStorage: new LocalStorageTokenStorage('myapp'),
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Initialize session from stored tokens
|
|
137
|
+
const user = await client.initSession('myrepo');
|
|
138
|
+
if (user) {
|
|
139
|
+
console.log('Session restored for:', user.email);
|
|
140
|
+
} else {
|
|
141
|
+
// No stored session - redirect to login
|
|
142
|
+
redirectToLogin();
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Auth State Changes (Firebase/Supabase Pattern)
|
|
147
|
+
|
|
148
|
+
Listen reactively to authentication events:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { RaisinClient, AuthEvent } from '@raisindb/client';
|
|
152
|
+
|
|
153
|
+
// Subscribe to auth state changes
|
|
154
|
+
const unsubscribe = client.onAuthStateChange(({ event, session }) => {
|
|
155
|
+
switch (event) {
|
|
156
|
+
case 'SIGNED_IN':
|
|
157
|
+
console.log('User signed in:', session.user?.email);
|
|
158
|
+
break;
|
|
159
|
+
case 'SIGNED_OUT':
|
|
160
|
+
console.log('User signed out');
|
|
161
|
+
redirectToLogin();
|
|
162
|
+
break;
|
|
163
|
+
case 'TOKEN_REFRESHED':
|
|
164
|
+
console.log('Token refreshed');
|
|
165
|
+
break;
|
|
166
|
+
case 'SESSION_EXPIRED':
|
|
167
|
+
console.log('Session expired');
|
|
168
|
+
break;
|
|
169
|
+
case 'USER_UPDATED':
|
|
170
|
+
console.log('User profile updated:', session.user);
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Later, to stop listening:
|
|
176
|
+
unsubscribe();
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### User Home Changes
|
|
180
|
+
|
|
181
|
+
Listen for real-time updates to the user's profile node:
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
// Auto-subscribes to user's home node when logged in
|
|
185
|
+
const unsubscribe = client.onUserChange(({ node, changeType }) => {
|
|
186
|
+
console.log('User home updated:', changeType);
|
|
187
|
+
console.log('New properties:', node.properties);
|
|
188
|
+
// Update UI with new avatar, displayName, preferences, etc.
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Helper Methods
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
// Get current session (sync)
|
|
196
|
+
const session = client.getSession();
|
|
197
|
+
if (session) {
|
|
198
|
+
console.log('User:', session.user?.email);
|
|
199
|
+
console.log('Token:', session.accessToken);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Get current user (alias for getStoredUser)
|
|
203
|
+
const user = client.getUser();
|
|
204
|
+
|
|
205
|
+
// Check if user has stored token
|
|
206
|
+
if (client.hasStoredToken()) {
|
|
207
|
+
await client.initSession('myrepo');
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Node Operations
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
const db = client.database('my_repo');
|
|
215
|
+
const ws = db.workspace('content');
|
|
216
|
+
const nodes = ws.nodes();
|
|
217
|
+
|
|
218
|
+
// Create a node
|
|
219
|
+
const page = await nodes.create({
|
|
220
|
+
type: 'Page',
|
|
221
|
+
path: '/blog/my-post',
|
|
222
|
+
properties: {
|
|
223
|
+
title: 'My First Post',
|
|
224
|
+
author: 'John Doe',
|
|
225
|
+
published: true,
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Get a node by ID
|
|
230
|
+
const node = await nodes.get(page.id);
|
|
231
|
+
|
|
232
|
+
// Get a node by path
|
|
233
|
+
const nodeByPath = await nodes.getByPath('/blog/my-post');
|
|
234
|
+
|
|
235
|
+
// Update a node
|
|
236
|
+
const updated = await nodes.update(page.id, {
|
|
237
|
+
properties: {
|
|
238
|
+
title: 'My Updated Post',
|
|
239
|
+
updatedAt: new Date().toISOString(),
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Query nodes by type
|
|
244
|
+
const pages = await nodes.queryByType('Page', 10);
|
|
245
|
+
|
|
246
|
+
// Query nodes by property
|
|
247
|
+
const publishedPages = await nodes.queryByProperty('published', true);
|
|
248
|
+
|
|
249
|
+
// Get children of a node
|
|
250
|
+
const children = await nodes.getChildren(page.id);
|
|
251
|
+
|
|
252
|
+
// Delete a node
|
|
253
|
+
await nodes.delete(page.id);
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### SQL Queries
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
const db = client.database('my_repo');
|
|
260
|
+
|
|
261
|
+
// Template literal queries (automatically parameterized)
|
|
262
|
+
const nodeType = 'Page';
|
|
263
|
+
const results = await db.sql`
|
|
264
|
+
SELECT * FROM nodes
|
|
265
|
+
WHERE node_type = ${nodeType}
|
|
266
|
+
ORDER BY created_at DESC
|
|
267
|
+
LIMIT 10
|
|
268
|
+
`;
|
|
269
|
+
|
|
270
|
+
console.log('Columns:', results.columns);
|
|
271
|
+
console.log('Rows:', results.rows);
|
|
272
|
+
console.log('Row count:', results.row_count);
|
|
273
|
+
|
|
274
|
+
// Raw SQL with explicit parameters
|
|
275
|
+
const sqlQuery = db.getSqlQuery();
|
|
276
|
+
const results2 = await sqlQuery.execute(
|
|
277
|
+
'SELECT * FROM nodes WHERE node_type = $1 AND created_at > $2',
|
|
278
|
+
['Page', '2024-01-01']
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
// Raw SQL without parameters (use with caution)
|
|
282
|
+
const results3 = await sqlQuery.raw('SELECT COUNT(*) FROM nodes');
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Event Subscriptions
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
const db = client.database('my_repo');
|
|
289
|
+
const ws = db.workspace('content');
|
|
290
|
+
const events = ws.events();
|
|
291
|
+
|
|
292
|
+
// Subscribe to all events in workspace
|
|
293
|
+
const subscription1 = await events.subscribe({}, (event) => {
|
|
294
|
+
console.log('Event:', event.event_type);
|
|
295
|
+
console.log('Payload:', event.payload);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// Subscribe to specific event types
|
|
299
|
+
const subscription2 = await events.subscribeToTypes(
|
|
300
|
+
['node:created', 'node:updated'],
|
|
301
|
+
(event) => {
|
|
302
|
+
console.log('Node changed:', event.payload);
|
|
303
|
+
}
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
// Subscribe to events for a specific path pattern
|
|
307
|
+
const subscription3 = await events.subscribeToPath('/blog/*', (event) => {
|
|
308
|
+
console.log('Blog event:', event);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// Subscribe to events for a specific node type
|
|
312
|
+
const subscription4 = await events.subscribeToNodeType('Page', (event) => {
|
|
313
|
+
console.log('Page event:', event);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// Unsubscribe
|
|
317
|
+
await subscription1.unsubscribe();
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Workspace Management
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
const db = client.database('my_repo');
|
|
324
|
+
const workspaces = db.workspaces();
|
|
325
|
+
|
|
326
|
+
// Create a workspace
|
|
327
|
+
const workspace = await workspaces.create({
|
|
328
|
+
name: 'blog',
|
|
329
|
+
description: 'Blog content workspace',
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// Get workspace metadata
|
|
333
|
+
const ws = await workspaces.get('blog');
|
|
334
|
+
console.log('Workspace:', ws);
|
|
335
|
+
|
|
336
|
+
// List all workspaces
|
|
337
|
+
const allWorkspaces = await workspaces.list();
|
|
338
|
+
|
|
339
|
+
// Update workspace
|
|
340
|
+
const updated = await workspaces.update('blog', {
|
|
341
|
+
description: 'Updated description',
|
|
342
|
+
allowed_node_types: ['Page', 'Post', 'Image'],
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// Delete workspace
|
|
346
|
+
await workspaces.delete('blog');
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Working with Multiple Branches
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
const client = new RaisinClient('raisin://localhost:8080/sys/default');
|
|
353
|
+
await client.connect();
|
|
354
|
+
await client.authenticate({ username: 'admin', password: 'password' });
|
|
355
|
+
|
|
356
|
+
// Work on main branch
|
|
357
|
+
const db = client.database('my_repo');
|
|
358
|
+
const mainWs = db.workspace('content');
|
|
359
|
+
const node = await mainWs.nodes().create({
|
|
360
|
+
type: 'Page',
|
|
361
|
+
path: '/test',
|
|
362
|
+
properties: { title: 'Test' },
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// Switch to feature branch
|
|
366
|
+
client.setBranch('feature/new-design');
|
|
367
|
+
|
|
368
|
+
// Operations now run on feature branch
|
|
369
|
+
const featureWs = db.workspace('content');
|
|
370
|
+
const featureNode = await featureWs.nodes().get(node.id);
|
|
371
|
+
|
|
372
|
+
// Switch back to main
|
|
373
|
+
client.setBranch('main');
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Error Handling
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
import { RaisinClient } from '@raisindb/client';
|
|
380
|
+
|
|
381
|
+
try {
|
|
382
|
+
const client = new RaisinClient('raisin://localhost:8080/sys/default');
|
|
383
|
+
await client.connect();
|
|
384
|
+
|
|
385
|
+
try {
|
|
386
|
+
await client.authenticate({
|
|
387
|
+
username: 'admin',
|
|
388
|
+
password: 'wrong-password'
|
|
389
|
+
});
|
|
390
|
+
} catch (error) {
|
|
391
|
+
console.error('Authentication failed:', error.message);
|
|
392
|
+
console.error('Error code:', error.code);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const db = client.database('my_repo');
|
|
396
|
+
const ws = db.workspace('content');
|
|
397
|
+
|
|
398
|
+
try {
|
|
399
|
+
const node = await ws.nodes().get('non-existent-id');
|
|
400
|
+
if (!node) {
|
|
401
|
+
console.log('Node not found');
|
|
402
|
+
}
|
|
403
|
+
} catch (error) {
|
|
404
|
+
console.error('Error fetching node:', error);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
} catch (error) {
|
|
408
|
+
console.error('Connection error:', error);
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Custom Token Storage
|
|
413
|
+
|
|
414
|
+
By default, tokens are stored in memory. You can implement custom storage:
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
import { TokenStorage } from '@raisindb/client';
|
|
418
|
+
|
|
419
|
+
class LocalStorageTokenStorage implements TokenStorage {
|
|
420
|
+
getAccessToken(): string | null {
|
|
421
|
+
return localStorage.getItem('access_token');
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
setAccessToken(token: string): void {
|
|
425
|
+
localStorage.setItem('access_token', token);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
getRefreshToken(): string | null {
|
|
429
|
+
return localStorage.getItem('refresh_token');
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
setRefreshToken(token: string): void {
|
|
433
|
+
localStorage.setItem('refresh_token', token);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
clear(): void {
|
|
437
|
+
localStorage.removeItem('access_token');
|
|
438
|
+
localStorage.removeItem('refresh_token');
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const client = new RaisinClient('raisin://localhost:8080/sys/default', {
|
|
443
|
+
tokenStorage: new LocalStorageTokenStorage(),
|
|
444
|
+
});
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
## Framework Integration
|
|
448
|
+
|
|
449
|
+
### React
|
|
450
|
+
|
|
451
|
+
```tsx
|
|
452
|
+
import { useEffect, useState } from 'react';
|
|
453
|
+
import { RaisinClient, LocalStorageTokenStorage, IdentityUser } from '@raisindb/client';
|
|
454
|
+
|
|
455
|
+
const client = new RaisinClient('ws://localhost:8081/sys/default/myrepo', {
|
|
456
|
+
tokenStorage: new LocalStorageTokenStorage('myapp'),
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
function App() {
|
|
460
|
+
const [user, setUser] = useState<IdentityUser | null>(null);
|
|
461
|
+
const [loading, setLoading] = useState(true);
|
|
462
|
+
|
|
463
|
+
useEffect(() => {
|
|
464
|
+
// Initialize session on mount
|
|
465
|
+
client.initSession('myrepo').then(setUser).finally(() => setLoading(false));
|
|
466
|
+
|
|
467
|
+
// Listen to auth state changes
|
|
468
|
+
const unsubscribe = client.onAuthStateChange(({ event, session }) => {
|
|
469
|
+
setUser(session.user);
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
return unsubscribe;
|
|
473
|
+
}, []);
|
|
474
|
+
|
|
475
|
+
if (loading) return <div>Loading...</div>;
|
|
476
|
+
if (!user) return <LoginPage />;
|
|
477
|
+
return <Dashboard user={user} />;
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### SvelteKit
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
// lib/raisin.ts
|
|
485
|
+
import { RaisinClient, LocalStorageTokenStorage } from '@raisindb/client';
|
|
486
|
+
import { writable } from 'svelte/store';
|
|
487
|
+
|
|
488
|
+
export const client = new RaisinClient('ws://localhost:8081/sys/default/myrepo', {
|
|
489
|
+
tokenStorage: new LocalStorageTokenStorage('myapp'),
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
export const user = writable<IdentityUser | null>(null);
|
|
493
|
+
|
|
494
|
+
// Subscribe to auth changes
|
|
495
|
+
client.onAuthStateChange(({ session }) => {
|
|
496
|
+
user.set(session.user);
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
// lib/stores/auth.ts
|
|
500
|
+
export async function initSession() {
|
|
501
|
+
return client.initSession('myrepo');
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
export async function login(email: string, password: string) {
|
|
505
|
+
return client.loginWithEmail(email, password, 'myrepo');
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
export async function logout() {
|
|
509
|
+
return client.logout();
|
|
510
|
+
}
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
### Vue 3
|
|
514
|
+
|
|
515
|
+
```typescript
|
|
516
|
+
// composables/useAuth.ts
|
|
517
|
+
import { ref, onMounted, onUnmounted } from 'vue';
|
|
518
|
+
import { RaisinClient, LocalStorageTokenStorage, IdentityUser } from '@raisindb/client';
|
|
519
|
+
|
|
520
|
+
const client = new RaisinClient('ws://localhost:8081/sys/default/myrepo', {
|
|
521
|
+
tokenStorage: new LocalStorageTokenStorage('myapp'),
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
export function useAuth() {
|
|
525
|
+
const user = ref<IdentityUser | null>(null);
|
|
526
|
+
const loading = ref(true);
|
|
527
|
+
let unsubscribe: (() => void) | null = null;
|
|
528
|
+
|
|
529
|
+
onMounted(async () => {
|
|
530
|
+
user.value = await client.initSession('myrepo');
|
|
531
|
+
loading.value = false;
|
|
532
|
+
|
|
533
|
+
unsubscribe = client.onAuthStateChange(({ session }) => {
|
|
534
|
+
user.value = session.user;
|
|
535
|
+
});
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
onUnmounted(() => unsubscribe?.());
|
|
539
|
+
|
|
540
|
+
return {
|
|
541
|
+
user,
|
|
542
|
+
loading,
|
|
543
|
+
login: (email: string, password: string) => client.loginWithEmail(email, password, 'myrepo'),
|
|
544
|
+
logout: () => client.logout(),
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
### Server-Side Rendering (SSR)
|
|
550
|
+
|
|
551
|
+
For SSR environments (Next.js, Nuxt, SvelteKit server routes), use the HTTP client:
|
|
552
|
+
|
|
553
|
+
```typescript
|
|
554
|
+
import { RaisinClient } from '@raisindb/client';
|
|
555
|
+
|
|
556
|
+
// Create HTTP-only client for SSR
|
|
557
|
+
const client = RaisinClient.forSSR('http://localhost:8080', {
|
|
558
|
+
tenantId: 'default',
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
// Authenticate (admin or JWT)
|
|
562
|
+
await client.authenticate({ username: 'admin', password: 'admin' });
|
|
563
|
+
|
|
564
|
+
// Execute queries
|
|
565
|
+
const result = await client.executeSql('myrepo', 'SELECT * FROM nodes LIMIT 10');
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
## API Reference
|
|
569
|
+
|
|
570
|
+
### RaisinClient
|
|
571
|
+
|
|
572
|
+
Main client class for connecting to RaisinDB.
|
|
573
|
+
|
|
574
|
+
#### Constructor
|
|
575
|
+
|
|
576
|
+
```typescript
|
|
577
|
+
new RaisinClient(url: string, options?: ClientOptions)
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
**Options:**
|
|
581
|
+
- `connection`: Connection options (auto-reconnect, heartbeat, etc.)
|
|
582
|
+
- `tokenStorage`: Custom token storage implementation
|
|
583
|
+
- `requestTimeout`: Request timeout in milliseconds (default: 30000)
|
|
584
|
+
- `tenantId`: Tenant ID (extracted from URL if not provided)
|
|
585
|
+
- `defaultBranch`: Default branch name (default: "main")
|
|
586
|
+
|
|
587
|
+
#### Methods
|
|
588
|
+
|
|
589
|
+
**Connection & Admin Auth:**
|
|
590
|
+
- `connect(): Promise<void>` - Connect to the server
|
|
591
|
+
- `authenticate(credentials: Credentials): Promise<void>` - Authenticate with credentials
|
|
592
|
+
- `disconnect(): void` - Disconnect from the server
|
|
593
|
+
- `isConnected(): boolean` - Check if connected
|
|
594
|
+
- `isAuthenticated(): boolean` - Check if authenticated
|
|
595
|
+
|
|
596
|
+
**Identity Auth (Email/Password):**
|
|
597
|
+
- `loginWithEmail(email, password, repository): Promise<IdentityUser>` - Login with email
|
|
598
|
+
- `registerWithEmail(email, password, repository, displayName?): Promise<IdentityUser>` - Register
|
|
599
|
+
- `logout(options?): Promise<void>` - Logout (options: `{ disconnect?, reconnect? }`)
|
|
600
|
+
- `initSession(repository): Promise<IdentityUser | null>` - Restore session from storage
|
|
601
|
+
- `refreshToken(): Promise<IdentityUser | null>` - Refresh access token
|
|
602
|
+
|
|
603
|
+
**Auth State Listeners:**
|
|
604
|
+
- `onAuthStateChange(callback): () => void` - Listen for auth events (`SIGNED_IN`, `SIGNED_OUT`, etc.)
|
|
605
|
+
- `onConnectionStateChange(callback): () => void` - Listen for connection state changes
|
|
606
|
+
- `onUserChange(callback): () => void` - Listen for user home node changes
|
|
607
|
+
|
|
608
|
+
**Session Helpers:**
|
|
609
|
+
- `getSession(): { user, accessToken } | null` - Get current session (sync)
|
|
610
|
+
- `getUser(): IdentityUser | null` - Get current user (alias)
|
|
611
|
+
- `getStoredUser(): IdentityUser | null` - Get stored user from localStorage
|
|
612
|
+
- `hasStoredToken(): boolean` - Check if token exists in storage
|
|
613
|
+
- `getCurrentUser(): CurrentUser | null` - Get full current user with roles/node
|
|
614
|
+
- `fetchUserNode(repository): Promise<UserNode | null>` - Fetch user node via SQL
|
|
615
|
+
|
|
616
|
+
**Database & Branches:**
|
|
617
|
+
- `database(name: string): Database` - Get database interface
|
|
618
|
+
- `setBranch(branch: string): void` - Set branch for subsequent requests
|
|
619
|
+
- `getBranch(): string` - Get current branch
|
|
620
|
+
|
|
621
|
+
### Database
|
|
622
|
+
|
|
623
|
+
Database/repository interface.
|
|
624
|
+
|
|
625
|
+
#### Methods
|
|
626
|
+
|
|
627
|
+
- `workspace(name: string): WorkspaceClient` - Get workspace client
|
|
628
|
+
- `workspaces(): WorkspaceManager` - Get workspace management operations
|
|
629
|
+
- `sql(strings, ...values): Promise<SqlResult>` - Execute SQL query with template literals
|
|
630
|
+
- `executeSql(query: string, params?: unknown[]): Promise<SqlResult>` - Execute raw SQL
|
|
631
|
+
|
|
632
|
+
### WorkspaceClient
|
|
633
|
+
|
|
634
|
+
Workspace operations interface.
|
|
635
|
+
|
|
636
|
+
#### Methods
|
|
637
|
+
|
|
638
|
+
- `nodes(): NodeOperations` - Get node operations
|
|
639
|
+
- `events(): EventSubscriptions` - Get event subscriptions
|
|
640
|
+
|
|
641
|
+
### NodeOperations
|
|
642
|
+
|
|
643
|
+
Node CRUD operations.
|
|
644
|
+
|
|
645
|
+
#### Methods
|
|
646
|
+
|
|
647
|
+
- `create(options: NodeCreateOptions): Promise<Node>` - Create a node
|
|
648
|
+
- `update(id: string, options: NodeUpdateOptions): Promise<Node>` - Update a node
|
|
649
|
+
- `delete(id: string): Promise<boolean>` - Delete a node
|
|
650
|
+
- `get(id: string): Promise<Node | null>` - Get a node by ID
|
|
651
|
+
- `query(options: NodeQueryOptions): Promise<Node[]>` - Query nodes
|
|
652
|
+
- `getByPath(path: string): Promise<Node | null>` - Get a node by path
|
|
653
|
+
- `queryByProperty(name: string, value: PropertyValue, limit?: number): Promise<Node[]>` - Query by property
|
|
654
|
+
- `queryByType(nodeType: string, limit?: number): Promise<Node[]>` - Query by type
|
|
655
|
+
- `getChildren(parentId: string, limit?: number): Promise<Node[]>` - Get children
|
|
656
|
+
|
|
657
|
+
### EventSubscriptions
|
|
658
|
+
|
|
659
|
+
Event subscription interface.
|
|
660
|
+
|
|
661
|
+
#### Methods
|
|
662
|
+
|
|
663
|
+
- `subscribe(filters: SubscriptionFilters, callback: EventCallback): Promise<Subscription>` - Subscribe to events
|
|
664
|
+
- `subscribeToTypes(eventTypes: string[], callback: EventCallback): Promise<Subscription>` - Subscribe to specific types
|
|
665
|
+
- `subscribeToPath(path: string, callback: EventCallback): Promise<Subscription>` - Subscribe to path
|
|
666
|
+
- `subscribeToNodeType(nodeType: string, callback: EventCallback): Promise<Subscription>` - Subscribe to node type
|
|
667
|
+
|
|
668
|
+
## Environment Support
|
|
669
|
+
|
|
670
|
+
### Browser
|
|
671
|
+
|
|
672
|
+
Works out of the box with native WebSocket support:
|
|
673
|
+
|
|
674
|
+
```typescript
|
|
675
|
+
import { RaisinClient } from '@raisindb/client';
|
|
676
|
+
|
|
677
|
+
const client = new RaisinClient('raisin://localhost:8080/sys/default');
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
### Node.js
|
|
681
|
+
|
|
682
|
+
Requires the `ws` package:
|
|
683
|
+
|
|
684
|
+
```bash
|
|
685
|
+
npm install ws
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
```typescript
|
|
689
|
+
import { RaisinClient } from '@raisindb/client';
|
|
690
|
+
|
|
691
|
+
const client = new RaisinClient('raisin://localhost:8080/sys/default');
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
### TypeScript
|
|
695
|
+
|
|
696
|
+
Full TypeScript support with type definitions included:
|
|
697
|
+
|
|
698
|
+
```typescript
|
|
699
|
+
import { RaisinClient, Node, NodeCreateOptions } from '@raisindb/client';
|
|
700
|
+
|
|
701
|
+
const options: NodeCreateOptions = {
|
|
702
|
+
type: 'Page',
|
|
703
|
+
path: '/home',
|
|
704
|
+
properties: {
|
|
705
|
+
title: 'Home',
|
|
706
|
+
},
|
|
707
|
+
};
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
## Building
|
|
711
|
+
|
|
712
|
+
```bash
|
|
713
|
+
# Install dependencies
|
|
714
|
+
npm install
|
|
715
|
+
|
|
716
|
+
# Build
|
|
717
|
+
npm run build
|
|
718
|
+
|
|
719
|
+
# Watch mode
|
|
720
|
+
npm run dev
|
|
721
|
+
|
|
722
|
+
# Type check
|
|
723
|
+
npm run typecheck
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
## License
|
|
727
|
+
|
|
728
|
+
BSL-1.1 - See LICENSE file for details.
|
|
729
|
+
|
|
730
|
+
## Links
|
|
731
|
+
|
|
732
|
+
- [RaisinDB Documentation](https://raisindb.com/docs)
|
|
733
|
+
- [GitHub Repository](https://github.com/maravilla-labs/raisindb)
|
|
734
|
+
- [Issue Tracker](https://github.com/maravilla-labs/raisindb/issues)
|