@uploadista/event-broadcaster-memory 0.0.3
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 +5 -0
- package/LICENSE +21 -0
- package/README.md +468 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/memory-event-broadcaster.d.ts +14 -0
- package/dist/memory-event-broadcaster.d.ts.map +1 -0
- package/dist/memory-event-broadcaster.js +33 -0
- package/package.json +28 -0
- package/src/index.ts +1 -0
- package/src/memory-event-broadcaster.ts +45 -0
- package/tsconfig.json +14 -0
- package/tsconfig.tsbuildinfo +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 uploadista
|
|
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,468 @@
|
|
|
1
|
+
# @uploadista/event-broadcaster-memory
|
|
2
|
+
|
|
3
|
+
In-memory event broadcaster for Uploadista. Broadcasts events within a single process for development and single-server deployments.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The memory event broadcaster uses JavaScript Maps to distribute events within a single process. Perfect for:
|
|
8
|
+
|
|
9
|
+
- **Development & Testing**: No external services needed
|
|
10
|
+
- **Single-Process Servers**: All instances in same process
|
|
11
|
+
- **WebSocket Servers**: Real-time updates to connected clients
|
|
12
|
+
- **Prototyping**: Quick experimentation with event flows
|
|
13
|
+
|
|
14
|
+
Events are only broadcast to subscribers in the same process. For distributed systems, use [Redis](#see-also) or [Cloudflare Durable Objects](#see-also).
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @uploadista/event-broadcaster-memory
|
|
20
|
+
# or
|
|
21
|
+
pnpm add @uploadista/event-broadcaster-memory
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Prerequisites
|
|
25
|
+
|
|
26
|
+
- Node.js 18+
|
|
27
|
+
- No external services required
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { memoryEventBroadcaster } from "@uploadista/event-broadcaster-memory";
|
|
33
|
+
import { Effect } from "effect";
|
|
34
|
+
|
|
35
|
+
// Use the memory broadcaster layer
|
|
36
|
+
const program = Effect.gen(function* () {
|
|
37
|
+
// Event broadcaster is automatically available
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
Effect.runSync(
|
|
41
|
+
program.pipe(
|
|
42
|
+
Effect.provide(memoryEventBroadcaster),
|
|
43
|
+
// ... other layers
|
|
44
|
+
)
|
|
45
|
+
);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Features
|
|
49
|
+
|
|
50
|
+
- ✅ **Zero Setup**: Works out of the box
|
|
51
|
+
- ✅ **Sub-Millisecond Latency**: In-memory operations (~100μs)
|
|
52
|
+
- ✅ **Synchronous Broadcasting**: Events delivered immediately
|
|
53
|
+
- ✅ **Type Safe**: Full TypeScript support
|
|
54
|
+
- ✅ **Simple API**: Publish/subscribe pattern
|
|
55
|
+
|
|
56
|
+
## API Reference
|
|
57
|
+
|
|
58
|
+
### Main Exports
|
|
59
|
+
|
|
60
|
+
#### `memoryEventBroadcaster: Layer<EventBroadcasterService>`
|
|
61
|
+
|
|
62
|
+
Pre-configured Effect layer providing the `EventBroadcasterService` with in-memory broadcasting.
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { memoryEventBroadcaster } from "@uploadista/event-broadcaster-memory";
|
|
66
|
+
|
|
67
|
+
const layer = memoryEventBroadcaster;
|
|
68
|
+
// Type: Layer<never, never, EventBroadcasterService>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
#### `createMemoryEventBroadcaster(): EventBroadcaster`
|
|
72
|
+
|
|
73
|
+
Factory function to create a new broadcaster instance.
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { createMemoryEventBroadcaster } from "@uploadista/event-broadcaster-memory";
|
|
77
|
+
|
|
78
|
+
const broadcaster = createMemoryEventBroadcaster();
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Available Operations
|
|
82
|
+
|
|
83
|
+
The memory broadcaster implements the `EventBroadcaster` interface:
|
|
84
|
+
|
|
85
|
+
#### `publish(channel: string, message: string): Effect<void>`
|
|
86
|
+
|
|
87
|
+
Broadcast a message to all subscribers on a channel.
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
const program = Effect.gen(function* () {
|
|
91
|
+
yield* broadcaster.publish("uploads:complete", JSON.stringify({
|
|
92
|
+
uploadId: "abc123",
|
|
93
|
+
status: "completed",
|
|
94
|
+
}));
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
#### `subscribe(channel: string, handler: (message: string) => void): Effect<void>`
|
|
99
|
+
|
|
100
|
+
Subscribe to a channel and receive messages.
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
const program = Effect.gen(function* () {
|
|
104
|
+
yield* broadcaster.subscribe("uploads:complete", (message: string) => {
|
|
105
|
+
const event = JSON.parse(message);
|
|
106
|
+
console.log(`Upload complete: ${event.uploadId}`);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### `unsubscribe(channel: string): Effect<void>`
|
|
112
|
+
|
|
113
|
+
Unsubscribe from a channel (removes all handlers).
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
const program = Effect.gen(function* () {
|
|
117
|
+
yield* broadcaster.unsubscribe("uploads:complete");
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Configuration
|
|
122
|
+
|
|
123
|
+
The memory broadcaster requires no configuration:
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { memoryEventBroadcaster } from "@uploadista/event-broadcaster-memory";
|
|
127
|
+
import { uploadServer } from "@uploadista/server";
|
|
128
|
+
import { Effect } from "effect";
|
|
129
|
+
|
|
130
|
+
const program = Effect.gen(function* () {
|
|
131
|
+
const server = yield* uploadServer;
|
|
132
|
+
// Broadcaster is automatically available
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
Effect.runSync(
|
|
136
|
+
program.pipe(
|
|
137
|
+
Effect.provide(memoryEventBroadcaster),
|
|
138
|
+
// ... other layers
|
|
139
|
+
)
|
|
140
|
+
);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Examples
|
|
144
|
+
|
|
145
|
+
### Example 1: Upload Progress Notifications
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
import { createMemoryEventBroadcaster } from "@uploadista/event-broadcaster-memory";
|
|
149
|
+
import { Effect } from "effect";
|
|
150
|
+
|
|
151
|
+
const broadcaster = createMemoryEventBroadcaster();
|
|
152
|
+
|
|
153
|
+
interface UploadEvent {
|
|
154
|
+
uploadId: string;
|
|
155
|
+
status: "started" | "progress" | "completed" | "failed";
|
|
156
|
+
progress?: number;
|
|
157
|
+
error?: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const program = Effect.gen(function* () {
|
|
161
|
+
// Subscribe to upload events
|
|
162
|
+
yield* broadcaster.subscribe("uploads:*", (message: string) => {
|
|
163
|
+
const event: UploadEvent = JSON.parse(message);
|
|
164
|
+
console.log(`[${event.status}] Upload ${event.uploadId}`);
|
|
165
|
+
|
|
166
|
+
if (event.progress !== undefined) {
|
|
167
|
+
console.log(` Progress: ${(event.progress * 100).toFixed(1)}%`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (event.error) {
|
|
171
|
+
console.log(` Error: ${event.error}`);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Simulate upload events
|
|
176
|
+
yield* broadcaster.publish("uploads:*", JSON.stringify({
|
|
177
|
+
uploadId: "upl_123",
|
|
178
|
+
status: "started",
|
|
179
|
+
}));
|
|
180
|
+
|
|
181
|
+
yield* broadcaster.publish("uploads:*", JSON.stringify({
|
|
182
|
+
uploadId: "upl_123",
|
|
183
|
+
status: "progress",
|
|
184
|
+
progress: 0.5,
|
|
185
|
+
}));
|
|
186
|
+
|
|
187
|
+
yield* broadcaster.publish("uploads:*", JSON.stringify({
|
|
188
|
+
uploadId: "upl_123",
|
|
189
|
+
status: "completed",
|
|
190
|
+
}));
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
Effect.runSync(program);
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Example 2: Flow Job Status Updates
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
import { createMemoryEventBroadcaster } from "@uploadista/event-broadcaster-memory";
|
|
200
|
+
import { Effect } from "effect";
|
|
201
|
+
|
|
202
|
+
const broadcaster = createMemoryEventBroadcaster();
|
|
203
|
+
|
|
204
|
+
interface FlowEvent {
|
|
205
|
+
jobId: string;
|
|
206
|
+
stage: "queued" | "processing" | "completed";
|
|
207
|
+
duration?: number;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const trackFlowJob = (jobId: string) =>
|
|
211
|
+
Effect.gen(function* () {
|
|
212
|
+
// Subscribe to job events
|
|
213
|
+
yield* broadcaster.subscribe(`flow:${jobId}`, (message: string) => {
|
|
214
|
+
const event: FlowEvent = JSON.parse(message);
|
|
215
|
+
console.log(`Job ${event.jobId}: ${event.stage}`);
|
|
216
|
+
|
|
217
|
+
if (event.duration) {
|
|
218
|
+
console.log(` Duration: ${event.duration}ms`);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Emit events
|
|
223
|
+
yield* broadcaster.publish(
|
|
224
|
+
`flow:${jobId}`,
|
|
225
|
+
JSON.stringify({ jobId, stage: "queued" })
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
yield* Effect.sleep("1 seconds");
|
|
229
|
+
|
|
230
|
+
yield* broadcaster.publish(
|
|
231
|
+
`flow:${jobId}`,
|
|
232
|
+
JSON.stringify({ jobId, stage: "processing" })
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
yield* Effect.sleep("3 seconds");
|
|
236
|
+
|
|
237
|
+
yield* broadcaster.publish(
|
|
238
|
+
`flow:${jobId}`,
|
|
239
|
+
JSON.stringify({ jobId, stage: "completed", duration: 4000 })
|
|
240
|
+
);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
const program = Effect.gen(function* () {
|
|
244
|
+
yield* trackFlowJob("job_abc123");
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
Effect.runSync(program);
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Example 3: Integration with WebSocket
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
import { createMemoryEventBroadcaster } from "@uploadista/event-broadcaster-memory";
|
|
254
|
+
import { webSocketEventEmitter } from "@uploadista/event-emitter-websocket";
|
|
255
|
+
import { Effect } from "effect";
|
|
256
|
+
|
|
257
|
+
const broadcaster = createMemoryEventBroadcaster();
|
|
258
|
+
|
|
259
|
+
// In your WebSocket handler
|
|
260
|
+
const handleUploadEvent = (
|
|
261
|
+
uploadId: string,
|
|
262
|
+
clientWebSocket: WebSocket
|
|
263
|
+
) =>
|
|
264
|
+
Effect.gen(function* () {
|
|
265
|
+
// Subscribe to upload events
|
|
266
|
+
yield* broadcaster.subscribe(`uploads:${uploadId}`, (message: string) => {
|
|
267
|
+
// Send to WebSocket client
|
|
268
|
+
clientWebSocket.send(message);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Performance Characteristics
|
|
274
|
+
|
|
275
|
+
| Operation | Latency | Throughput |
|
|
276
|
+
|-----------|---------|-----------|
|
|
277
|
+
| publish() | ~100μs | 10,000+ events/sec |
|
|
278
|
+
| subscribe() | ~100μs | N/A |
|
|
279
|
+
| unsubscribe() | ~100μs | N/A |
|
|
280
|
+
|
|
281
|
+
All operations are synchronous and complete in microseconds.
|
|
282
|
+
|
|
283
|
+
## Limitations
|
|
284
|
+
|
|
285
|
+
- **Single Process Only**: No distribution across servers
|
|
286
|
+
- **No Persistence**: Events are lost if not immediately processed
|
|
287
|
+
- **No Pattern Matching**: Cannot use wildcards in channel names
|
|
288
|
+
- **Memory Grows**: Subscribers accumulate in memory
|
|
289
|
+
- **No TTL**: Subscriptions persist until explicitly removed
|
|
290
|
+
|
|
291
|
+
## Use Cases
|
|
292
|
+
|
|
293
|
+
✅ **Perfect For**:
|
|
294
|
+
- Local development
|
|
295
|
+
- Unit/integration testing
|
|
296
|
+
- Single-server deployments
|
|
297
|
+
- Real-time WebSocket updates
|
|
298
|
+
- Prototyping event flows
|
|
299
|
+
|
|
300
|
+
❌ **Not Recommended For**:
|
|
301
|
+
- Distributed systems (use Redis)
|
|
302
|
+
- Event persistence (use database)
|
|
303
|
+
- Pub/Sub patterns with many subscribers
|
|
304
|
+
- High-throughput production systems (>1000 events/sec)
|
|
305
|
+
|
|
306
|
+
## Best Practices
|
|
307
|
+
|
|
308
|
+
### 1. Use Consistent Channel Naming
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
// Good: Hierarchical naming
|
|
312
|
+
"uploads:started"
|
|
313
|
+
"uploads:progress"
|
|
314
|
+
"uploads:completed"
|
|
315
|
+
"flows:abc123:status"
|
|
316
|
+
|
|
317
|
+
// Avoid: Generic names
|
|
318
|
+
"events", "updates", "status"
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### 2. Clean Up Subscriptions
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
const program = Effect.gen(function* () {
|
|
325
|
+
// Subscribe
|
|
326
|
+
yield* broadcaster.subscribe("uploads:*", handler);
|
|
327
|
+
|
|
328
|
+
// Do work...
|
|
329
|
+
|
|
330
|
+
// Clean up when done
|
|
331
|
+
yield* broadcaster.unsubscribe("uploads:*");
|
|
332
|
+
});
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### 3. Handle Synchronous Publishing
|
|
336
|
+
|
|
337
|
+
Events are delivered immediately and synchronously:
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
// This handler runs immediately
|
|
341
|
+
yield* broadcaster.subscribe("channel", (msg) => {
|
|
342
|
+
console.log("Received:", msg);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// This publishes immediately to handler above
|
|
346
|
+
yield* broadcaster.publish("channel", "test");
|
|
347
|
+
// "Received: test" is printed before publish() completes
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
## Deployment
|
|
351
|
+
|
|
352
|
+
### Single-Server Node.js
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
import { memoryEventBroadcaster } from "@uploadista/event-broadcaster-memory";
|
|
356
|
+
import { uploadServer } from "@uploadista/server";
|
|
357
|
+
import { Effect } from "effect";
|
|
358
|
+
|
|
359
|
+
const program = Effect.gen(function* () {
|
|
360
|
+
const server = yield* uploadServer;
|
|
361
|
+
// Use broadcaster for WebSocket updates
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
Effect.runSync(
|
|
365
|
+
program.pipe(
|
|
366
|
+
Effect.provide(memoryEventBroadcaster),
|
|
367
|
+
// ... other layers
|
|
368
|
+
)
|
|
369
|
+
);
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Docker Single Container
|
|
373
|
+
|
|
374
|
+
```dockerfile
|
|
375
|
+
FROM node:18-alpine
|
|
376
|
+
|
|
377
|
+
WORKDIR /app
|
|
378
|
+
COPY . .
|
|
379
|
+
RUN npm ci --omit=dev && npm run build
|
|
380
|
+
|
|
381
|
+
ENV NODE_ENV=production
|
|
382
|
+
CMD ["node", "dist/server.js"]
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
```yaml
|
|
386
|
+
version: "3"
|
|
387
|
+
services:
|
|
388
|
+
app:
|
|
389
|
+
build: .
|
|
390
|
+
ports:
|
|
391
|
+
- "3000:3000"
|
|
392
|
+
# Single instance only
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
## Limitations & Workarounds
|
|
396
|
+
|
|
397
|
+
### Multiple Instances Don't Share Events
|
|
398
|
+
|
|
399
|
+
If you scale to multiple processes, they won't communicate:
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
// Server 1: Publishes to memory broadcaster
|
|
403
|
+
yield* broadcaster.publish("channel", "message1");
|
|
404
|
+
|
|
405
|
+
// Server 2: Will NOT receive message1 (different process)
|
|
406
|
+
// Solution: Use Redis broadcaster instead
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### High Subscriber Count
|
|
410
|
+
|
|
411
|
+
If many subscribers accumulate, unsubscribe to clean up:
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
// Create isolated broadcaster instances
|
|
415
|
+
const createIsolatedBroadcaster = () => createMemoryEventBroadcaster();
|
|
416
|
+
|
|
417
|
+
// Use separate instance per namespace
|
|
418
|
+
const uploadBroadcaster = createIsolatedBroadcaster();
|
|
419
|
+
const flowBroadcaster = createIsolatedBroadcaster();
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## Troubleshooting
|
|
423
|
+
|
|
424
|
+
### Events Not Received
|
|
425
|
+
|
|
426
|
+
Ensure subscribers are registered before publishing:
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
// ❌ Wrong: Subscribe after publish
|
|
430
|
+
yield* broadcaster.publish("channel", "message");
|
|
431
|
+
yield* broadcaster.subscribe("channel", handler); // Won't receive above
|
|
432
|
+
|
|
433
|
+
// ✅ Correct: Subscribe first
|
|
434
|
+
yield* broadcaster.subscribe("channel", handler);
|
|
435
|
+
yield* broadcaster.publish("channel", "message");
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### Memory Leaks
|
|
439
|
+
|
|
440
|
+
Unsubscribe when no longer needed:
|
|
441
|
+
|
|
442
|
+
```typescript
|
|
443
|
+
// ❌ Don't do this in loops
|
|
444
|
+
for (let i = 0; i < 1000; i++) {
|
|
445
|
+
yield* broadcaster.subscribe("channel", handler);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// ✅ Clean up explicitly
|
|
449
|
+
yield* broadcaster.unsubscribe("channel");
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
## Related Packages
|
|
453
|
+
|
|
454
|
+
- [@uploadista/core](../../core) - Core types
|
|
455
|
+
- [@uploadista/event-broadcaster-redis](../event-broadcasters/redis) - Distributed Redis broadcaster
|
|
456
|
+
- [@uploadista/event-emitter-websocket](../event-emitters/websocket) - WebSocket real-time events
|
|
457
|
+
- [@uploadista/server](../../servers/server) - Upload server with events
|
|
458
|
+
- [@uploadista/kv-store-memory](../../kv-stores/memory) - In-memory KV store
|
|
459
|
+
|
|
460
|
+
## License
|
|
461
|
+
|
|
462
|
+
See [LICENSE](../../../LICENSE) in the main repository.
|
|
463
|
+
|
|
464
|
+
## See Also
|
|
465
|
+
|
|
466
|
+
- [EVENT_SYSTEM.md](./EVENT_SYSTEM.md) - Architecture and patterns
|
|
467
|
+
- [Server Setup Guide](../../../SERVER_SETUP.md#events) - Using broadcasters in servers
|
|
468
|
+
- [WebSocket Event Emitter](../event-emitters/websocket/README.md) - Real-time WebSocket events
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./memory-event-broadcaster";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { EventBroadcaster } from "@uploadista/core/types";
|
|
2
|
+
import { EventBroadcasterService } from "@uploadista/core/types";
|
|
3
|
+
import { Layer } from "effect";
|
|
4
|
+
/**
|
|
5
|
+
* In-memory event broadcaster for single-instance deployments.
|
|
6
|
+
* Events are only broadcast within the same process/instance.
|
|
7
|
+
* Use this for development or single-server deployments.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createMemoryEventBroadcaster(): EventBroadcaster;
|
|
10
|
+
/**
|
|
11
|
+
* Layer for in-memory event broadcaster
|
|
12
|
+
*/
|
|
13
|
+
export declare const memoryEventBroadcaster: Layer.Layer<EventBroadcasterService, never, never>;
|
|
14
|
+
//# sourceMappingURL=memory-event-broadcaster.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-event-broadcaster.d.ts","sourceRoot":"","sources":["../src/memory-event-broadcaster.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAU,KAAK,EAAE,MAAM,QAAQ,CAAC;AAEvC;;;;GAIG;AACH,wBAAgB,4BAA4B,IAAI,gBAAgB,CA2B/D;AAED;;GAEG;AACH,eAAO,MAAM,sBAAsB,oDAGlC,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { EventBroadcasterService } from "@uploadista/core/types";
|
|
2
|
+
import { Effect, Layer } from "effect";
|
|
3
|
+
/**
|
|
4
|
+
* In-memory event broadcaster for single-instance deployments.
|
|
5
|
+
* Events are only broadcast within the same process/instance.
|
|
6
|
+
* Use this for development or single-server deployments.
|
|
7
|
+
*/
|
|
8
|
+
export function createMemoryEventBroadcaster() {
|
|
9
|
+
const handlers = new Map();
|
|
10
|
+
return {
|
|
11
|
+
publish: (channel, message) => Effect.sync(() => {
|
|
12
|
+
const channelHandlers = handlers.get(channel);
|
|
13
|
+
if (channelHandlers) {
|
|
14
|
+
for (const handler of channelHandlers) {
|
|
15
|
+
handler(message);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}),
|
|
19
|
+
subscribe: (channel, handler) => Effect.sync(() => {
|
|
20
|
+
if (!handlers.has(channel)) {
|
|
21
|
+
handlers.set(channel, new Set());
|
|
22
|
+
}
|
|
23
|
+
handlers.get(channel)?.add(handler);
|
|
24
|
+
}),
|
|
25
|
+
unsubscribe: (channel) => Effect.sync(() => {
|
|
26
|
+
handlers.delete(channel);
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Layer for in-memory event broadcaster
|
|
32
|
+
*/
|
|
33
|
+
export const memoryEventBroadcaster = Layer.sync(EventBroadcasterService, createMemoryEventBroadcaster);
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@uploadista/event-broadcaster-memory",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.3",
|
|
5
|
+
"description": "Memory event broadcaster for Uploadista",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Uploadista",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"effect": "3.18.4",
|
|
17
|
+
"@uploadista/core": "0.0.3"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@uploadista/typescript-config": "0.0.3"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc -b",
|
|
24
|
+
"format": "biome format --write ./src",
|
|
25
|
+
"lint": "biome lint --write ./src",
|
|
26
|
+
"check": "biome check --write ./src"
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./memory-event-broadcaster";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { EventBroadcaster } from "@uploadista/core/types";
|
|
2
|
+
import { EventBroadcasterService } from "@uploadista/core/types";
|
|
3
|
+
import { Effect, Layer } from "effect";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* In-memory event broadcaster for single-instance deployments.
|
|
7
|
+
* Events are only broadcast within the same process/instance.
|
|
8
|
+
* Use this for development or single-server deployments.
|
|
9
|
+
*/
|
|
10
|
+
export function createMemoryEventBroadcaster(): EventBroadcaster {
|
|
11
|
+
const handlers = new Map<string, Set<(message: string) => void>>();
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
publish: (channel: string, message: string) =>
|
|
15
|
+
Effect.sync(() => {
|
|
16
|
+
const channelHandlers = handlers.get(channel);
|
|
17
|
+
if (channelHandlers) {
|
|
18
|
+
for (const handler of channelHandlers) {
|
|
19
|
+
handler(message);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}),
|
|
23
|
+
|
|
24
|
+
subscribe: (channel: string, handler: (message: string) => void) =>
|
|
25
|
+
Effect.sync(() => {
|
|
26
|
+
if (!handlers.has(channel)) {
|
|
27
|
+
handlers.set(channel, new Set());
|
|
28
|
+
}
|
|
29
|
+
handlers.get(channel)?.add(handler);
|
|
30
|
+
}),
|
|
31
|
+
|
|
32
|
+
unsubscribe: (channel: string) =>
|
|
33
|
+
Effect.sync(() => {
|
|
34
|
+
handlers.delete(channel);
|
|
35
|
+
}),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Layer for in-memory event broadcaster
|
|
41
|
+
*/
|
|
42
|
+
export const memoryEventBroadcaster = Layer.sync(
|
|
43
|
+
EventBroadcasterService,
|
|
44
|
+
createMemoryEventBroadcaster,
|
|
45
|
+
);
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "@uploadista/typescript-config/server.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"baseUrl": "./",
|
|
5
|
+
"paths": {
|
|
6
|
+
"@/*": ["./src/*"]
|
|
7
|
+
},
|
|
8
|
+
"outDir": "./dist",
|
|
9
|
+
"rootDir": "./src",
|
|
10
|
+
"typeRoots": ["../../../../node_modules/@types"],
|
|
11
|
+
"lib": ["ES2022", "WebWorker"]
|
|
12
|
+
},
|
|
13
|
+
"include": ["src"]
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["./src/index.ts","./src/memory-event-broadcaster.ts"],"version":"5.9.3"}
|