@mantiq/realtime 0.0.1
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 +19 -0
- package/package.json +56 -0
- package/src/RealtimeServiceProvider.ts +71 -0
- package/src/broadcast/BunBroadcaster.ts +24 -0
- package/src/channels/ChannelManager.ts +309 -0
- package/src/contracts/Channel.ts +40 -0
- package/src/contracts/RealtimeConfig.ts +72 -0
- package/src/errors/RealtimeError.ts +6 -0
- package/src/helpers/realtime.ts +42 -0
- package/src/index.ts +48 -0
- package/src/protocol/Protocol.ts +138 -0
- package/src/server/ConnectionManager.ts +192 -0
- package/src/server/WebSocketServer.ts +159 -0
- package/src/sse/SSEManager.ts +228 -0
- package/src/testing/RealtimeFake.ts +137 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory fake for testing realtime broadcasting.
|
|
3
|
+
*
|
|
4
|
+
* Records all broadcasts and provides assertion methods.
|
|
5
|
+
* Does not require a WebSocket server or real connections.
|
|
6
|
+
*
|
|
7
|
+
* ```typescript
|
|
8
|
+
* const fake = new RealtimeFake()
|
|
9
|
+
*
|
|
10
|
+
* fake.broadcast('orders.1', 'OrderShipped', { orderId: 1 })
|
|
11
|
+
*
|
|
12
|
+
* fake.assertBroadcast('OrderShipped')
|
|
13
|
+
* fake.assertBroadcastOn('orders.1', 'OrderShipped')
|
|
14
|
+
* fake.assertBroadcastCount('OrderShipped', 1)
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
interface BroadcastRecord {
|
|
19
|
+
channel: string
|
|
20
|
+
event: string
|
|
21
|
+
data: Record<string, any>
|
|
22
|
+
timestamp: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface SubscriptionRecord {
|
|
26
|
+
channel: string
|
|
27
|
+
userId?: string | number
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class RealtimeFake {
|
|
31
|
+
private readonly broadcasts: BroadcastRecord[] = []
|
|
32
|
+
private readonly subscriptions: SubscriptionRecord[] = []
|
|
33
|
+
|
|
34
|
+
// ── Broadcasting ──────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Record a broadcast (mimics ChannelManager.broadcast).
|
|
38
|
+
*/
|
|
39
|
+
broadcast(channel: string, event: string, data: Record<string, any>): void {
|
|
40
|
+
this.broadcasts.push({ channel, event, data, timestamp: Date.now() })
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ── Subscriptions ─────────────────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Record a subscription.
|
|
47
|
+
*/
|
|
48
|
+
subscribe(channel: string, userId?: string | number): void {
|
|
49
|
+
this.subscriptions.push({ channel, userId })
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ── Broadcast Assertions ──────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
assertBroadcast(eventName: string, predicate?: (data: Record<string, any>) => boolean): void {
|
|
55
|
+
const matched = this.broadcasts.filter(
|
|
56
|
+
(b) => b.event === eventName && (!predicate || predicate(b.data)),
|
|
57
|
+
)
|
|
58
|
+
if (matched.length === 0) {
|
|
59
|
+
throw new Error(`Expected [${eventName}] to be broadcast, but it was not.`)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
assertBroadcastOn(channel: string, eventName: string): void {
|
|
64
|
+
const matched = this.broadcasts.filter(
|
|
65
|
+
(b) => b.event === eventName && b.channel === channel,
|
|
66
|
+
)
|
|
67
|
+
if (matched.length === 0) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
`Expected [${eventName}] to be broadcast on channel "${channel}", but it was not.`,
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
assertBroadcastCount(eventName: string, count: number): void {
|
|
75
|
+
const matched = this.broadcasts.filter((b) => b.event === eventName)
|
|
76
|
+
if (matched.length !== count) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`Expected [${eventName}] to be broadcast ${count} time(s), but it was broadcast ${matched.length} time(s).`,
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
assertNotBroadcast(eventName: string): void {
|
|
84
|
+
const matched = this.broadcasts.filter((b) => b.event === eventName)
|
|
85
|
+
if (matched.length > 0) {
|
|
86
|
+
throw new Error(`Unexpected [${eventName}] was broadcast.`)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
assertNothingBroadcast(): void {
|
|
91
|
+
if (this.broadcasts.length > 0) {
|
|
92
|
+
const names = [...new Set(this.broadcasts.map((b) => b.event))]
|
|
93
|
+
throw new Error(
|
|
94
|
+
`Expected no broadcasts, but the following were broadcast: ${names.join(', ')}`,
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ── Subscription Assertions ───────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
assertSubscribed(channel: string, userId?: string | number): void {
|
|
102
|
+
const matched = this.subscriptions.filter(
|
|
103
|
+
(s) => s.channel === channel && (userId === undefined || s.userId === userId),
|
|
104
|
+
)
|
|
105
|
+
if (matched.length === 0) {
|
|
106
|
+
const msg = userId !== undefined
|
|
107
|
+
? `Expected user [${userId}] to be subscribed to "${channel}", but they were not.`
|
|
108
|
+
: `Expected a subscription to "${channel}", but none found.`
|
|
109
|
+
throw new Error(msg)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
assertNotSubscribed(channel: string): void {
|
|
114
|
+
const matched = this.subscriptions.filter((s) => s.channel === channel)
|
|
115
|
+
if (matched.length > 0) {
|
|
116
|
+
throw new Error(`Unexpected subscription to "${channel}" found.`)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ── Query ─────────────────────────────────────────────────────────────
|
|
121
|
+
|
|
122
|
+
allBroadcasts(): BroadcastRecord[] {
|
|
123
|
+
return [...this.broadcasts]
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
allSubscriptions(): SubscriptionRecord[] {
|
|
127
|
+
return [...this.subscriptions]
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Clear all recorded data.
|
|
132
|
+
*/
|
|
133
|
+
reset(): void {
|
|
134
|
+
this.broadcasts.length = 0
|
|
135
|
+
this.subscriptions.length = 0
|
|
136
|
+
}
|
|
137
|
+
}
|