@objectstack/service-realtime 4.0.2 → 4.0.4
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/.turbo/turbo-build.log +6 -6
- package/CHANGELOG.md +15 -0
- package/README.md +438 -0
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/package.json +5 -5
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @objectstack/service-realtime@4.0.
|
|
2
|
+
> @objectstack/service-realtime@4.0.4 build /home/runner/work/framework/framework/packages/services/service-realtime
|
|
3
3
|
> tsup --config ../../../tsup.config.ts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mESM[39m Build start
|
|
12
12
|
[34mCJS[39m Build start
|
|
13
|
-
[32mCJS[39m [1mdist/index.cjs [22m[32m6.64 KB[39m
|
|
14
|
-
[32mCJS[39m [1mdist/index.cjs.map [22m[32m13.93 KB[39m
|
|
15
|
-
[32mCJS[39m ⚡️ Build success in 53ms
|
|
16
13
|
[32mESM[39m [1mdist/index.js [22m[32m5.40 KB[39m
|
|
17
14
|
[32mESM[39m [1mdist/index.js.map [22m[32m13.42 KB[39m
|
|
18
|
-
[32mESM[39m ⚡️ Build success in
|
|
15
|
+
[32mESM[39m ⚡️ Build success in 133ms
|
|
16
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m6.64 KB[39m
|
|
17
|
+
[32mCJS[39m [1mdist/index.cjs.map [22m[32m13.93 KB[39m
|
|
18
|
+
[32mCJS[39m ⚡️ Build success in 137ms
|
|
19
19
|
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 13258ms
|
|
21
21
|
[32mDTS[39m [1mdist/index.d.ts [22m[32m99.42 KB[39m
|
|
22
22
|
[32mDTS[39m [1mdist/index.d.cts [22m[32m99.42 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @objectstack/service-realtime
|
|
2
2
|
|
|
3
|
+
## 4.0.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [326b66b]
|
|
8
|
+
- @objectstack/spec@4.0.4
|
|
9
|
+
- @objectstack/core@4.0.4
|
|
10
|
+
|
|
11
|
+
## 4.0.3
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- @objectstack/spec@4.0.3
|
|
16
|
+
- @objectstack/core@4.0.3
|
|
17
|
+
|
|
3
18
|
## 4.0.2
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/README.md
ADDED
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
# @objectstack/service-realtime
|
|
2
|
+
|
|
3
|
+
Realtime Service for ObjectStack — implements `IRealtimeService` with WebSocket and in-memory pub/sub.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **WebSocket Support**: Real-time bidirectional communication
|
|
8
|
+
- **Pub/Sub Pattern**: Subscribe to channels and receive updates
|
|
9
|
+
- **Room-Based Architecture**: Organize connections into rooms
|
|
10
|
+
- **Presence Tracking**: Track online users and their status
|
|
11
|
+
- **Message Broadcasting**: Send messages to all connections or specific rooms
|
|
12
|
+
- **Event Streaming**: Stream database changes and system events
|
|
13
|
+
- **Auto-Reconnection**: Client auto-reconnects on connection loss
|
|
14
|
+
- **Type-Safe**: Full TypeScript support for events and messages
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add @objectstack/service-realtime
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Basic Usage
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { defineStack } from '@objectstack/spec';
|
|
26
|
+
import { ServiceRealtime } from '@objectstack/service-realtime';
|
|
27
|
+
|
|
28
|
+
const stack = defineStack({
|
|
29
|
+
services: [
|
|
30
|
+
ServiceRealtime.configure({
|
|
31
|
+
port: 3001,
|
|
32
|
+
path: '/ws',
|
|
33
|
+
}),
|
|
34
|
+
],
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Configuration
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
interface RealtimeServiceConfig {
|
|
42
|
+
/** WebSocket server port (default: 3001) */
|
|
43
|
+
port?: number;
|
|
44
|
+
|
|
45
|
+
/** WebSocket path (default: '/ws') */
|
|
46
|
+
path?: string;
|
|
47
|
+
|
|
48
|
+
/** Enable CORS (default: true) */
|
|
49
|
+
cors?: boolean;
|
|
50
|
+
|
|
51
|
+
/** Maximum connections per user (default: 10) */
|
|
52
|
+
maxConnectionsPerUser?: number;
|
|
53
|
+
|
|
54
|
+
/** Ping interval in ms (default: 30000) */
|
|
55
|
+
pingInterval?: number;
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Service API (Server-Side)
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// Get realtime service
|
|
63
|
+
const realtime = kernel.getService<IRealtimeService>('realtime');
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Broadcasting
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// Broadcast to all connected clients
|
|
70
|
+
await realtime.broadcast({
|
|
71
|
+
event: 'notification',
|
|
72
|
+
data: { message: 'System update in 5 minutes' },
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Broadcast to specific room
|
|
76
|
+
await realtime.broadcastToRoom('opportunity:123', {
|
|
77
|
+
event: 'record_updated',
|
|
78
|
+
data: { recordId: '123', field: 'stage', value: 'closed_won' },
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Broadcast to specific user
|
|
82
|
+
await realtime.broadcastToUser('user:456', {
|
|
83
|
+
event: 'mention',
|
|
84
|
+
data: { commentId: 'comment:789', mentionedBy: 'user:123' },
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Channel Management
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
// Join a channel (room)
|
|
92
|
+
await realtime.join(connectionId, 'opportunity:123');
|
|
93
|
+
|
|
94
|
+
// Leave a channel
|
|
95
|
+
await realtime.leave(connectionId, 'opportunity:123');
|
|
96
|
+
|
|
97
|
+
// Get all connections in a channel
|
|
98
|
+
const connections = await realtime.getChannelConnections('opportunity:123');
|
|
99
|
+
|
|
100
|
+
// Get all channels for a connection
|
|
101
|
+
const channels = await realtime.getConnectionChannels(connectionId);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Presence
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
// Set user presence
|
|
108
|
+
await realtime.setPresence('user:456', {
|
|
109
|
+
status: 'online',
|
|
110
|
+
currentPage: '/opportunity/123',
|
|
111
|
+
lastActive: new Date(),
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Get user presence
|
|
115
|
+
const presence = await realtime.getPresence('user:456');
|
|
116
|
+
|
|
117
|
+
// Get all online users
|
|
118
|
+
const onlineUsers = await realtime.getOnlineUsers();
|
|
119
|
+
|
|
120
|
+
// Get users in a specific channel
|
|
121
|
+
const channelUsers = await realtime.getChannelPresence('opportunity:123');
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Client-Side Usage
|
|
125
|
+
|
|
126
|
+
### React Hook
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import { useRealtime } from '@objectstack/client-react';
|
|
130
|
+
|
|
131
|
+
function OpportunityDetails({ id }: { id: string }) {
|
|
132
|
+
const { subscribe, send, isConnected } = useRealtime();
|
|
133
|
+
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
// Subscribe to record updates
|
|
136
|
+
const unsubscribe = subscribe(`opportunity:${id}`, (event) => {
|
|
137
|
+
if (event.type === 'record_updated') {
|
|
138
|
+
console.log('Record updated:', event.data);
|
|
139
|
+
// Update UI
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
return unsubscribe;
|
|
144
|
+
}, [id]);
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<div>
|
|
148
|
+
{isConnected ? '🟢 Connected' : '🔴 Disconnected'}
|
|
149
|
+
</div>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### JavaScript Client
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
import { RealtimeClient } from '@objectstack/client';
|
|
158
|
+
|
|
159
|
+
const client = new RealtimeClient({
|
|
160
|
+
url: 'ws://localhost:3001/ws',
|
|
161
|
+
auth: {
|
|
162
|
+
token: 'your-auth-token',
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Connect
|
|
167
|
+
await client.connect();
|
|
168
|
+
|
|
169
|
+
// Subscribe to a channel
|
|
170
|
+
client.subscribe('opportunity:123', (event) => {
|
|
171
|
+
console.log('Received event:', event);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Send a message
|
|
175
|
+
client.send('typing', {
|
|
176
|
+
recordId: '123',
|
|
177
|
+
userId: 'user:456',
|
|
178
|
+
isTyping: true,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Disconnect
|
|
182
|
+
await client.disconnect();
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Advanced Features
|
|
186
|
+
|
|
187
|
+
### Event Streaming
|
|
188
|
+
|
|
189
|
+
Stream database changes in real-time:
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
// Server-side: Stream record changes
|
|
193
|
+
realtime.streamRecordChanges('opportunity', {
|
|
194
|
+
onInsert: async (record) => {
|
|
195
|
+
await realtime.broadcast({
|
|
196
|
+
event: 'record_created',
|
|
197
|
+
data: { object: 'opportunity', record },
|
|
198
|
+
});
|
|
199
|
+
},
|
|
200
|
+
onUpdate: async (record, changes) => {
|
|
201
|
+
await realtime.broadcastToRoom(`opportunity:${record.id}`, {
|
|
202
|
+
event: 'record_updated',
|
|
203
|
+
data: { recordId: record.id, changes },
|
|
204
|
+
});
|
|
205
|
+
},
|
|
206
|
+
onDelete: async (recordId) => {
|
|
207
|
+
await realtime.broadcast({
|
|
208
|
+
event: 'record_deleted',
|
|
209
|
+
data: { object: 'opportunity', recordId },
|
|
210
|
+
});
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Private Channels
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
// Server-side: Authorize private channel access
|
|
219
|
+
realtime.authorizeChannel = async (userId, channel) => {
|
|
220
|
+
if (channel.startsWith('user:')) {
|
|
221
|
+
// Only allow users to join their own private channel
|
|
222
|
+
return channel === `user:${userId}`;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (channel.startsWith('opportunity:')) {
|
|
226
|
+
// Check if user has access to the opportunity
|
|
227
|
+
const opportunityId = channel.split(':')[1];
|
|
228
|
+
return await hasAccess(userId, 'opportunity', opportunityId);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return false;
|
|
232
|
+
};
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Typing Indicators
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
// Client sends typing event
|
|
239
|
+
client.send('typing', {
|
|
240
|
+
recordId: '123',
|
|
241
|
+
userId: 'user:456',
|
|
242
|
+
isTyping: true,
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// Server broadcasts to room
|
|
246
|
+
realtime.on('typing', async (connectionId, data) => {
|
|
247
|
+
await realtime.broadcastToRoom(`opportunity:${data.recordId}`, {
|
|
248
|
+
event: 'user_typing',
|
|
249
|
+
data: { userId: data.userId, isTyping: data.isTyping },
|
|
250
|
+
}, { exclude: [connectionId] }); // Don't send back to sender
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// Other clients receive typing notification
|
|
254
|
+
client.subscribe('opportunity:123', (event) => {
|
|
255
|
+
if (event.type === 'user_typing') {
|
|
256
|
+
showTypingIndicator(event.data.userId, event.data.isTyping);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Live Cursor Tracking
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
// Client sends cursor position
|
|
265
|
+
client.send('cursor', {
|
|
266
|
+
recordId: '123',
|
|
267
|
+
x: 450,
|
|
268
|
+
y: 200,
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Server broadcasts to room
|
|
272
|
+
realtime.on('cursor', async (connectionId, data) => {
|
|
273
|
+
const user = await getConnectionUser(connectionId);
|
|
274
|
+
|
|
275
|
+
await realtime.broadcastToRoom(`opportunity:${data.recordId}`, {
|
|
276
|
+
event: 'cursor_moved',
|
|
277
|
+
data: {
|
|
278
|
+
userId: user.id,
|
|
279
|
+
userName: user.name,
|
|
280
|
+
x: data.x,
|
|
281
|
+
y: data.y,
|
|
282
|
+
},
|
|
283
|
+
}, { exclude: [connectionId] });
|
|
284
|
+
});
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Collaborative Editing
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
// Operational Transform (OT) for collaborative editing
|
|
291
|
+
client.send('edit', {
|
|
292
|
+
documentId: '123',
|
|
293
|
+
operation: {
|
|
294
|
+
type: 'insert',
|
|
295
|
+
position: 42,
|
|
296
|
+
text: 'Hello',
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
realtime.on('edit', async (connectionId, data) => {
|
|
301
|
+
// Apply operation transform
|
|
302
|
+
const transformedOp = await applyOT(data.operation);
|
|
303
|
+
|
|
304
|
+
// Broadcast to all editors
|
|
305
|
+
await realtime.broadcastToRoom(`document:${data.documentId}`, {
|
|
306
|
+
event: 'operation',
|
|
307
|
+
data: transformedOp,
|
|
308
|
+
}, { exclude: [connectionId] });
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Integration with ObjectStack Features
|
|
313
|
+
|
|
314
|
+
### Feed Updates
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
// When a comment is added
|
|
318
|
+
feed.on('comment_added', async (comment) => {
|
|
319
|
+
await realtime.broadcastToRoom(`${comment.object}:${comment.recordId}`, {
|
|
320
|
+
event: 'feed_update',
|
|
321
|
+
data: { type: 'comment', comment },
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Workflow Status
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
// When a flow step completes
|
|
330
|
+
automation.on('step_completed', async (execution) => {
|
|
331
|
+
await realtime.broadcastToUser(execution.userId, {
|
|
332
|
+
event: 'flow_progress',
|
|
333
|
+
data: {
|
|
334
|
+
flowId: execution.flowId,
|
|
335
|
+
step: execution.currentStep,
|
|
336
|
+
progress: execution.progress,
|
|
337
|
+
},
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Analytics Dashboard
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
// Stream real-time metrics
|
|
346
|
+
setInterval(async () => {
|
|
347
|
+
const metrics = await analytics.getCurrentMetrics();
|
|
348
|
+
|
|
349
|
+
await realtime.broadcastToRoom('dashboard:sales', {
|
|
350
|
+
event: 'metrics_update',
|
|
351
|
+
data: metrics,
|
|
352
|
+
});
|
|
353
|
+
}, 5000); // Every 5 seconds
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
## Connection Events
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
// Server-side event handlers
|
|
360
|
+
realtime.on('connection', async (connectionId, userId) => {
|
|
361
|
+
console.log(`User ${userId} connected (${connectionId})`);
|
|
362
|
+
|
|
363
|
+
// Set initial presence
|
|
364
|
+
await realtime.setPresence(userId, { status: 'online' });
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
realtime.on('disconnection', async (connectionId, userId) => {
|
|
368
|
+
console.log(`User ${userId} disconnected`);
|
|
369
|
+
|
|
370
|
+
// Update presence
|
|
371
|
+
await realtime.setPresence(userId, {
|
|
372
|
+
status: 'offline',
|
|
373
|
+
lastSeen: new Date(),
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
realtime.on('error', async (connectionId, error) => {
|
|
378
|
+
console.error(`Connection error:`, error);
|
|
379
|
+
});
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## Best Practices
|
|
383
|
+
|
|
384
|
+
1. **Channel Organization**: Use namespaced channels (e.g., `object:recordId`)
|
|
385
|
+
2. **Authorization**: Always verify channel access before joining
|
|
386
|
+
3. **Message Size**: Keep messages small (< 10KB)
|
|
387
|
+
4. **Rate Limiting**: Limit message frequency per connection
|
|
388
|
+
5. **Cleanup**: Remove disconnected users from channels
|
|
389
|
+
6. **Heartbeat**: Implement ping/pong for connection health
|
|
390
|
+
7. **Compression**: Enable WebSocket compression for large messages
|
|
391
|
+
|
|
392
|
+
## Performance Considerations
|
|
393
|
+
|
|
394
|
+
- **Scaling**: Use Redis adapter for multi-server deployments
|
|
395
|
+
- **Connection Pooling**: Limit concurrent connections per user
|
|
396
|
+
- **Channel Limits**: Limit channels per connection
|
|
397
|
+
- **Message Batching**: Batch frequent updates to reduce traffic
|
|
398
|
+
- **Binary Protocol**: Use binary for large data transfers
|
|
399
|
+
|
|
400
|
+
## REST API Endpoints
|
|
401
|
+
|
|
402
|
+
```
|
|
403
|
+
POST /api/v1/realtime/broadcast # Broadcast to all
|
|
404
|
+
POST /api/v1/realtime/broadcast/room/:room # Broadcast to room
|
|
405
|
+
POST /api/v1/realtime/broadcast/user/:userId # Broadcast to user
|
|
406
|
+
GET /api/v1/realtime/presence # Get online users
|
|
407
|
+
GET /api/v1/realtime/presence/:userId # Get user presence
|
|
408
|
+
GET /api/v1/realtime/channels/:channel # Get channel connections
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## Contract Implementation
|
|
412
|
+
|
|
413
|
+
Implements `IRealtimeService` from `@objectstack/spec/contracts`:
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
interface IRealtimeService {
|
|
417
|
+
broadcast(message: Message): Promise<void>;
|
|
418
|
+
broadcastToRoom(room: string, message: Message): Promise<void>;
|
|
419
|
+
broadcastToUser(userId: string, message: Message): Promise<void>;
|
|
420
|
+
join(connectionId: string, channel: string): Promise<void>;
|
|
421
|
+
leave(connectionId: string, channel: string): Promise<void>;
|
|
422
|
+
setPresence(userId: string, presence: PresenceData): Promise<void>;
|
|
423
|
+
getPresence(userId: string): Promise<PresenceData | null>;
|
|
424
|
+
getOnlineUsers(): Promise<string[]>;
|
|
425
|
+
on(event: string, handler: EventHandler): void;
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
## License
|
|
430
|
+
|
|
431
|
+
Apache-2.0
|
|
432
|
+
|
|
433
|
+
## See Also
|
|
434
|
+
|
|
435
|
+
- [WebSocket API Documentation](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API)
|
|
436
|
+
- [@objectstack/client](../../client/)
|
|
437
|
+
- [@objectstack/client-react](../../client-react/)
|
|
438
|
+
- [Realtime Features Guide](/content/docs/guides/realtime/)
|
package/dist/index.d.cts
CHANGED
|
@@ -427,7 +427,7 @@ declare const SysPresence: Omit<{
|
|
|
427
427
|
actions?: {
|
|
428
428
|
name: string;
|
|
429
429
|
label: string;
|
|
430
|
-
type: "url" | "
|
|
430
|
+
type: "url" | "flow" | "api" | "script" | "modal";
|
|
431
431
|
refreshAfter: boolean;
|
|
432
432
|
objectName?: string | undefined;
|
|
433
433
|
icon?: string | undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -427,7 +427,7 @@ declare const SysPresence: Omit<{
|
|
|
427
427
|
actions?: {
|
|
428
428
|
name: string;
|
|
429
429
|
label: string;
|
|
430
|
-
type: "url" | "
|
|
430
|
+
type: "url" | "flow" | "api" | "script" | "modal";
|
|
431
431
|
refreshAfter: boolean;
|
|
432
432
|
objectName?: string | undefined;
|
|
433
433
|
icon?: string | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/service-realtime",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.4",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Realtime Service for ObjectStack — implements IRealtimeService with WebSocket and in-memory pub/sub",
|
|
6
6
|
"type": "module",
|
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@objectstack/core": "4.0.
|
|
18
|
-
"@objectstack/spec": "4.0.
|
|
17
|
+
"@objectstack/core": "4.0.4",
|
|
18
|
+
"@objectstack/spec": "4.0.4"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@types/node": "^25.
|
|
21
|
+
"@types/node": "^25.6.0",
|
|
22
22
|
"typescript": "^6.0.2",
|
|
23
|
-
"vitest": "^4.1.
|
|
23
|
+
"vitest": "^4.1.4"
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build": "tsup --config ../../../tsup.config.ts",
|