@stacksjs/realtime 0.70.87 → 0.70.88
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/package.json +2 -2
- package/dist/broadcast.d.ts +0 -62
- package/dist/channel.d.ts +0 -24
- package/dist/emit.d.ts +0 -37
- package/dist/heartbeat.d.ts +0 -32
- package/dist/index.d.ts +0 -37
- package/dist/index.js +0 -2
- package/dist/replay-buffer.d.ts +0 -83
- package/dist/server-instance.d.ts +0 -17
- package/dist/ws.d.ts +0 -44
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/realtime",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.70.
|
|
5
|
+
"version": "0.70.88",
|
|
6
6
|
"description": "The Stacks realtime integration. Built on top of ts-broadcasting.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"prepublishOnly": "bun run build"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"ts-broadcasting": "^0.0.
|
|
56
|
+
"ts-broadcasting": "^0.0.8"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"better-dx": "^0.2.16"
|
package/dist/broadcast.d.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import type { ChannelType } from 'ts-broadcasting';
|
|
2
|
-
/**
|
|
3
|
-
* Install (or clear) the backpressure guard. Pass `null` to disable.
|
|
4
|
-
*/
|
|
5
|
-
export declare function setBackpressureGuard(cfg: BackpressureGuardConfig | null): void;
|
|
6
|
-
/**
|
|
7
|
-
* Read the currently-installed guard config (useful for tests).
|
|
8
|
-
*/
|
|
9
|
-
export declare function getBackpressureGuard(): Required<BackpressureGuardConfig> | null;
|
|
10
|
-
/**
|
|
11
|
-
* Run a broadcast from a broadcast file
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* await runBroadcast('OrderCreated', { orderId: 123 })
|
|
15
|
-
*/
|
|
16
|
-
export declare function runBroadcast(name: string, payload?: any): Promise<void>;
|
|
17
|
-
/**
|
|
18
|
-
* Alias for runBroadcast.
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* await broadcast('OrderCreated', { orderId: 123 })
|
|
22
|
-
*/
|
|
23
|
-
export declare function broadcast(name: string, payload?: any): Promise<void>;
|
|
24
|
-
/**
|
|
25
|
-
* Backpressure guard config (stacksjs/stacks#1877 R-2). The default
|
|
26
|
-
* threshold is 1 MiB of buffered-bytes per socket — above this, the
|
|
27
|
-
* configured `onSlow` callback fires once per offending socket per
|
|
28
|
-
* broadcast. Apps install via `setBackpressureGuard({...})`; the
|
|
29
|
-
* default is "no guard" for backwards-compat, so existing callers
|
|
30
|
-
* see no behavior change until they opt in.
|
|
31
|
-
*
|
|
32
|
-
* Why opt-in: the underlying ts-broadcasting `server.broadcast()` is
|
|
33
|
-
* synchronous and we can't inject between message-serialize and
|
|
34
|
-
* socket-write. The best we can do at the Stacks layer is detect
|
|
35
|
-
* slow consumers AROUND the broadcast call and let the app decide
|
|
36
|
-
* what to do (close socket, drop client from channel, scale up).
|
|
37
|
-
*/
|
|
38
|
-
export declare interface BackpressureGuardConfig {
|
|
39
|
-
maxPerSocketBytes?: number
|
|
40
|
-
onSlow?: (info: { channelName: string, backpressure: number, socket: unknown }) => void
|
|
41
|
-
}
|
|
42
|
-
export declare interface BroadcastInstance {
|
|
43
|
-
channel?: () => string | string[]
|
|
44
|
-
broadcastOn?: () => string | string[]
|
|
45
|
-
event?: () => string
|
|
46
|
-
broadcastAs?: () => string
|
|
47
|
-
data?: () => any
|
|
48
|
-
broadcastWith?: () => any
|
|
49
|
-
handle?: (payload?: any) => Promise<void> | void
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Stacks Broadcast class for backward compatibility
|
|
53
|
-
* Wraps ts-broadcasting's BroadcastServer
|
|
54
|
-
*/
|
|
55
|
-
export declare class Broadcast {
|
|
56
|
-
connect(): Promise<void>;
|
|
57
|
-
disconnect(): Promise<void>;
|
|
58
|
-
subscribe(channel: string, callback: (data: any) => void): void;
|
|
59
|
-
unsubscribe(channel: string): void;
|
|
60
|
-
broadcast(channel: string, event: string, data?: any, type?: ChannelType): void;
|
|
61
|
-
isConnected(): boolean;
|
|
62
|
-
}
|
package/dist/channel.d.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { ChannelType } from 'ts-broadcasting';
|
|
2
|
-
/**
|
|
3
|
-
* Create a new channel instance
|
|
4
|
-
*
|
|
5
|
-
* @example
|
|
6
|
-
* // Broadcast to a public channel
|
|
7
|
-
* await channel('orders').public('created', { id: 1 })
|
|
8
|
-
*
|
|
9
|
-
* // Broadcast to a private channel
|
|
10
|
-
* await channel('orders.123').private('updated', { status: 'shipped' })
|
|
11
|
-
*
|
|
12
|
-
* // Broadcast to a presence channel
|
|
13
|
-
* await channel('chat.room.1').presence('message', { text: 'Hello' })
|
|
14
|
-
*/
|
|
15
|
-
export declare function channel(name: string): Channel;
|
|
16
|
-
/**
|
|
17
|
-
* Stacks Channel class for backward compatibility
|
|
18
|
-
* Provides a fluent API for broadcasting to channels
|
|
19
|
-
*/
|
|
20
|
-
export declare class Channel {
|
|
21
|
-
constructor(channel: string);
|
|
22
|
-
presence(event: string, data?: any): Promise<void>;
|
|
23
|
-
broadcast(event: string, data?: any, type?: ChannelType): Promise<void>;
|
|
24
|
-
}
|
package/dist/emit.d.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Emit an event to a channel
|
|
3
|
-
*
|
|
4
|
-
* @example
|
|
5
|
-
* // Simple emit to public channel
|
|
6
|
-
* emit('orders', 'created', { id: 1, total: 99.99 })
|
|
7
|
-
*
|
|
8
|
-
* // Emit to private channel
|
|
9
|
-
* emit('orders.123', 'updated', { status: 'shipped' }, { private: true })
|
|
10
|
-
*
|
|
11
|
-
* // Emit to presence channel
|
|
12
|
-
* emit('chat.room.1', 'message', { text: 'Hello' }, { presence: true })
|
|
13
|
-
*
|
|
14
|
-
* // Exclude specific users
|
|
15
|
-
* emit('chat.room.1', 'message', { text: 'Hello' }, { exclude: 'user-123' })
|
|
16
|
-
*/
|
|
17
|
-
export declare function emit<T = unknown>(channel: string, event: string, data?: T, options?: EmitOptions): void;
|
|
18
|
-
/**
|
|
19
|
-
* Emit an event to a specific user
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* emitToUser('user-123', 'notification', { message: 'You have a new order!' })
|
|
23
|
-
*/
|
|
24
|
-
export declare function emitToUser<T = unknown>(userId: string | number, event: string, data?: T, options?: Omit<EmitOptions, 'private'>): void;
|
|
25
|
-
/**
|
|
26
|
-
* Emit an event to multiple users
|
|
27
|
-
*
|
|
28
|
-
* @example
|
|
29
|
-
* emitToUsers(['user-1', 'user-2'], 'announcement', { message: 'Server maintenance!' })
|
|
30
|
-
*/
|
|
31
|
-
export declare function emitToUsers<T = unknown>(userIds: (string | number)[], event: string, data?: T, options?: Omit<EmitOptions, 'private'>): void;
|
|
32
|
-
export declare interface EmitOptions {
|
|
33
|
-
private?: boolean
|
|
34
|
-
presence?: boolean
|
|
35
|
-
exclude?: string | string[]
|
|
36
|
-
driver?: string
|
|
37
|
-
}
|
package/dist/heartbeat.d.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Install (or replace) the heartbeat config. Pass `null` to stop
|
|
3
|
-
* the heartbeat loop. Safe to call multiple times — the previous
|
|
4
|
-
* timer is cleared before the new one starts.
|
|
5
|
-
*/
|
|
6
|
-
export declare function setHeartbeatConfig(cfg: HeartbeatConfig | null): void;
|
|
7
|
-
/** Read the current config — useful for tests. */
|
|
8
|
-
export declare function getHeartbeatConfig(): Readonly<HeartbeatState> | null;
|
|
9
|
-
/**
|
|
10
|
-
* Manually fire a single heartbeat tick. Exposed for tests; in
|
|
11
|
-
* production it's invoked by the internal interval.
|
|
12
|
-
*/
|
|
13
|
-
export declare function runOneTick(): void;
|
|
14
|
-
/**
|
|
15
|
-
* Called from the server's pong handler (or message handler when
|
|
16
|
-
* fallback `__stacks_ping__` text frames are in use). Resets the
|
|
17
|
-
* missed-pong counter for the given socket so it doesn't get
|
|
18
|
-
* declared dead.
|
|
19
|
-
*/
|
|
20
|
-
export declare function markPong(socket: object): void;
|
|
21
|
-
export declare interface HeartbeatConfig {
|
|
22
|
-
intervalMs?: number
|
|
23
|
-
maxMissedPongs?: number
|
|
24
|
-
onDead?: (socket: unknown) => void
|
|
25
|
-
}
|
|
26
|
-
declare interface HeartbeatState {
|
|
27
|
-
intervalMs: number
|
|
28
|
-
maxMissedPongs: number
|
|
29
|
-
onDead: (socket: unknown) => void
|
|
30
|
-
missed: WeakMap<object, number>
|
|
31
|
-
timer: ReturnType<typeof setInterval> | null
|
|
32
|
-
}
|
package/dist/index.d.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export type { EmitOptions } from './emit';
|
|
2
|
-
export type { BroadcastInstance } from './broadcast';
|
|
3
|
-
export type { BackpressureGuardConfig } from './broadcast';
|
|
4
|
-
export type { HeartbeatConfig } from './heartbeat';
|
|
5
|
-
export type { BufferedMessage, ReplayBufferConfig } from './replay-buffer';
|
|
6
|
-
export type { WsAuthenticator, WsAuthResult } from './ws';
|
|
7
|
-
/**
|
|
8
|
-
* Stacks Realtime Module
|
|
9
|
-
*
|
|
10
|
-
* This module provides real-time broadcasting capabilities for Stacks applications.
|
|
11
|
-
* It's built on top of ts-broadcasting and provides a familiar Laravel-like API.
|
|
12
|
-
*/
|
|
13
|
-
// Re-export everything from ts-broadcasting
|
|
14
|
-
export * from 'ts-broadcasting';
|
|
15
|
-
// Note: all exports are already provided by `export * from 'ts-broadcasting'` above.
|
|
16
|
-
// Aliases are provided below for convenience.
|
|
17
|
-
// Server instance management
|
|
18
|
-
export { getServer, setServer, createServer, stopServer } from './server-instance';
|
|
19
|
-
// Stacks-specific exports
|
|
20
|
-
export { emit, emitToUser, emitToUsers } from './emit';
|
|
21
|
-
export { channel, channel as createChannel, Channel as StacksChannel } from './channel';
|
|
22
|
-
export { broadcast as dispatchBroadcast, runBroadcast, Broadcast as LegacyBroadcast } from './broadcast';
|
|
23
|
-
// Backpressure guard for slow consumers (stacksjs/stacks#1877 R-2).
|
|
24
|
-
// Opt-in via setBackpressureGuard; default is no-op.
|
|
25
|
-
export { setBackpressureGuard, getBackpressureGuard } from './broadcast';
|
|
26
|
-
// Heartbeat ping/pong for detecting half-closed sockets
|
|
27
|
-
// (stacksjs/stacks#1877 R-5). Opt-in via setHeartbeatConfig.
|
|
28
|
-
export { getHeartbeatConfig, markPong, runOneTick, setHeartbeatConfig } from './heartbeat';
|
|
29
|
-
// At-least-once replay buffer for reconnect (stacksjs/stacks#1877 R-3).
|
|
30
|
-
// Opt-in via setReplayBuffer. Apps wire `replaySince(channel, seq)`
|
|
31
|
-
// into their reconnect handler to re-send missed messages.
|
|
32
|
-
export { debugSnapshot, getReplayBuffer, pruneExpired, recordBroadcast, replaySince, setReplayBuffer } from './replay-buffer';
|
|
33
|
-
export { setBunSocket, handleWebSocketRequest, storeWebSocketEvent } from './ws';
|
|
34
|
-
// WebSocket authenticator wiring (stacksjs/stacks#1877 R-1). Install
|
|
35
|
-
// once at server boot to require a valid token / cookie at the
|
|
36
|
-
// handshake boundary — without it, the upgrade proceeds unauthed.
|
|
37
|
-
export { setWsAuthenticator, getWsAuthenticator } from './ws';
|
package/dist/index.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
export*from"ts-broadcasting";export{l as storeWebSocketEvent,C as stopServer,p as setWsAuthenticator,A as setServer,m as setReplayBuffer,W as setHeartbeatConfig,f as setBunSocket,Q as setBackpressureGuard,V as runOneTick,N as runBroadcast,k as replaySince,$ as recordBroadcast,_ as pruneExpired,U as markPong,g as handleWebSocketRequest,w as getWsAuthenticator,z as getServer,Z as getReplayBuffer,T as getHeartbeatConfig,R as getBackpressureGuard,G as emitToUsers,F as emitToUser,E as emit,M as dispatchBroadcast,Y as debugSnapshot,B as createServer,J as createChannel,I as channel,K as StacksChannel,O as LegacyBroadcast};
|
package/dist/replay-buffer.d.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Install (or replace) the replay-buffer config. Pass `null` to disable
|
|
3
|
-
* and drop all buffered state. Safe to call multiple times.
|
|
4
|
-
*/
|
|
5
|
-
export declare function setReplayBuffer(cfg: ReplayBufferConfig | null): void;
|
|
6
|
-
/** Read the current config — useful for tests. */
|
|
7
|
-
export declare function getReplayBuffer(): Readonly<BufferRegistry> | null;
|
|
8
|
-
/**
|
|
9
|
-
* Called by the broadcast wrapper for every outbound message on a
|
|
10
|
-
* matched channel. Records the message and assigns a monotonic seq.
|
|
11
|
-
* Returns the seq for the caller to optionally include in the
|
|
12
|
-
* outbound payload — clients store the latest seq locally and send
|
|
13
|
-
* it back on reconnect via `replaySince`.
|
|
14
|
-
*/
|
|
15
|
-
export declare function recordBroadcast(channel: string, event: string, data: unknown): number | null;
|
|
16
|
-
/**
|
|
17
|
-
* Replay every buffered message on `channel` with `seq > sinceSeq`.
|
|
18
|
-
* Stale entries (older than `ttlMs`) are evicted on the way through
|
|
19
|
-
* so callers don't see them. Returns the array of messages the
|
|
20
|
-
* caller should re-send to the reconnecting client.
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* ```ts
|
|
24
|
-
* // Inside the reconnect handler:
|
|
25
|
-
* const missed = replaySince('orders', lastSeenSeq)
|
|
26
|
-
* for (const msg of missed) {
|
|
27
|
-
* socket.send(JSON.stringify({ event: msg.event, data: msg.data, seq: msg.seq }))
|
|
28
|
-
* }
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
export declare function replaySince(channel: string, sinceSeq: number): BufferedMessage[];
|
|
32
|
-
/**
|
|
33
|
-
* Drop expired entries across every tracked channel. Called by apps
|
|
34
|
-
* that want eager memory reclaim — the default lazy-on-read path is
|
|
35
|
-
* adequate for most workloads.
|
|
36
|
-
*/
|
|
37
|
-
export declare function pruneExpired(): void;
|
|
38
|
-
/**
|
|
39
|
-
* Snapshot the buffer state — debugging only. Don't depend on this
|
|
40
|
-
* shape in production code; the internals may change.
|
|
41
|
-
*/
|
|
42
|
-
export declare function debugSnapshot(): Record<string, { count: number, firstSeq: number | null, lastSeq: number | null }>;
|
|
43
|
-
/**
|
|
44
|
-
* Per-channel message replay buffer (stacksjs/stacks#1877 R-3).
|
|
45
|
-
*
|
|
46
|
-
* Background: ts-broadcasting delivers messages at-most-once — a client
|
|
47
|
-
* that drops between two broadcasts loses everything in flight. For
|
|
48
|
-
* channels where the app needs every message (chat, presence, order
|
|
49
|
-
* updates), reconnect-after-network-blip becomes a silent data loss.
|
|
50
|
-
*
|
|
51
|
-
* Fix: opt-in per-channel ring buffer that retains the most-recent N
|
|
52
|
-
* messages with monotonic sequence IDs. On reconnect, the client sends
|
|
53
|
-
* its last-seen seq; the server replays everything stored after that
|
|
54
|
-
* point. Apps install via `setReplayBuffer({ channels, maxPerChannel,
|
|
55
|
-
* ttlMs })`. Buffer is in-process — for cross-instance replay, route
|
|
56
|
-
* through a shared store (Redis Streams, Postgres LISTEN/NOTIFY, etc.).
|
|
57
|
-
*
|
|
58
|
-
* Memory shape: `Map<channel, RingBuffer<BufferedMessage>>`. Bounded by
|
|
59
|
-
* `maxPerChannel` (default 100) so a chatty channel can't OOM the
|
|
60
|
-
* server. Entries past `ttlMs` are evicted lazily on read — apps that
|
|
61
|
-
* want eager eviction can call `pruneExpired()` from their own timer.
|
|
62
|
-
*/
|
|
63
|
-
export declare interface ReplayBufferConfig {
|
|
64
|
-
channels?: string[]
|
|
65
|
-
maxPerChannel?: number
|
|
66
|
-
ttlMs?: number
|
|
67
|
-
}
|
|
68
|
-
export declare interface BufferedMessage {
|
|
69
|
-
seq: number
|
|
70
|
-
ts: number
|
|
71
|
-
event: string
|
|
72
|
-
data: unknown
|
|
73
|
-
}
|
|
74
|
-
declare interface ChannelState {
|
|
75
|
-
messages: BufferedMessage[]
|
|
76
|
-
nextSeq: number
|
|
77
|
-
}
|
|
78
|
-
declare interface BufferRegistry {
|
|
79
|
-
channels: string[]
|
|
80
|
-
maxPerChannel: number
|
|
81
|
-
ttlMs: number
|
|
82
|
-
state: Map<string, ChannelState>
|
|
83
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { BroadcastServer, ServerConfig } from 'ts-broadcasting';
|
|
2
|
-
/**
|
|
3
|
-
* Set the global broadcast server instance
|
|
4
|
-
*/
|
|
5
|
-
export declare function setServer(server: BroadcastServer): void;
|
|
6
|
-
/**
|
|
7
|
-
* Get the global broadcast server instance
|
|
8
|
-
*/
|
|
9
|
-
export declare function getServer(): BroadcastServer | null;
|
|
10
|
-
/**
|
|
11
|
-
* Create and start a new broadcast server
|
|
12
|
-
*/
|
|
13
|
-
export declare function createServer(config: ServerConfig): Promise<BroadcastServer>;
|
|
14
|
-
/**
|
|
15
|
-
* Stop the current broadcast server
|
|
16
|
-
*/
|
|
17
|
-
export declare function stopServer(): Promise<void>;
|
package/dist/ws.d.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { Server } from 'bun';
|
|
2
|
-
import type { BroadcastServer } from 'ts-broadcasting';
|
|
3
|
-
/**
|
|
4
|
-
* Set the broadcast server instance
|
|
5
|
-
* @deprecated Use setServer from './server-instance' instead
|
|
6
|
-
*/
|
|
7
|
-
export declare function setBunSocket(server: BroadcastServer | null): void;
|
|
8
|
-
/**
|
|
9
|
-
* Store WebSocket event in the database
|
|
10
|
-
* Note: This function is now a no-op. WebSocket events are tracked internally by ts-broadcasting.
|
|
11
|
-
*/
|
|
12
|
-
export declare function storeWebSocketEvent(_type: 'disconnection' | 'error' | 'success', _socket: string, _details: string): Promise<void>;
|
|
13
|
-
/**
|
|
14
|
-
* Install (or clear) the global WebSocket authenticator. Called once
|
|
15
|
-
* at server boot; pass `null` to disable auth (the unauthed default).
|
|
16
|
-
*/
|
|
17
|
-
export declare function setWsAuthenticator(fn: WsAuthenticator | null): void;
|
|
18
|
-
/** Read the currently-installed authenticator. Useful for tests. */
|
|
19
|
-
export declare function getWsAuthenticator(): WsAuthenticator | null;
|
|
20
|
-
/**
|
|
21
|
-
* Handle WebSocket request upgrade. If an authenticator is installed
|
|
22
|
-
* (see `setWsAuthenticator`), it runs FIRST and a 401 is returned on
|
|
23
|
-
* failure (stacksjs/stacks#1877 R-1). Without an authenticator the
|
|
24
|
-
* upgrade proceeds for backwards-compat — the function still works
|
|
25
|
-
* the same way it did before.
|
|
26
|
-
*/
|
|
27
|
-
export declare function handleWebSocketRequest(req: Request, server: Server<any>): Promise<Response | undefined>;
|
|
28
|
-
/**
|
|
29
|
-
* Optional authenticator invoked at WebSocket handshake time.
|
|
30
|
-
*
|
|
31
|
-
* Apps install one via `setWsAuthenticator(fn)` to require a valid
|
|
32
|
-
* token / cookie / signed query param BEFORE the upgrade goes through
|
|
33
|
-
* (stacksjs/stacks#1877 R-1). Without an authenticator, the upgrade
|
|
34
|
-
* proceeds as before — useful for local-dev / public-broadcast apps,
|
|
35
|
-
* but production apps should always install one.
|
|
36
|
-
*
|
|
37
|
-
* The returned `data` is attached to the upgraded socket as `ws.data`
|
|
38
|
-
* so per-message authorization can read it back without re-parsing
|
|
39
|
-
* the auth token on every frame.
|
|
40
|
-
*/
|
|
41
|
-
export type WsAuthenticator = (req: Request) => Promise<WsAuthResult> | WsAuthResult;
|
|
42
|
-
/** Result returned from a `WsAuthenticator`. */
|
|
43
|
-
export type WsAuthResult = | { ok: true, data?: Record<string, unknown> }
|
|
44
|
-
| { ok: false, status?: number, message?: string }
|