@stacksjs/realtime 0.75.236 → 0.75.238

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/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { BroadcastStreamOptions } from './stream';
1
+ export type { BroadcastStreamMetadata, BroadcastStreamOptions } from './stream';
2
2
  export type { EmitOptions } from './emit';
3
3
  export type { BroadcastInstance } from './broadcast';
4
4
  export type { BufferedMessage, ReplayBufferConfig } from './replay-buffer';
package/dist/stream.d.ts CHANGED
@@ -1,9 +1,16 @@
1
1
  /** Authenticated SSE transport for the same engine used by emit(), channels and model broadcasts. */
2
2
  export declare function createBroadcastResponse(options: BroadcastStreamOptions): Promise<Response>;
3
+ export declare interface BroadcastStreamMetadata {
4
+ readonly channel: string
5
+ readonly event: string
6
+ readonly data: unknown
7
+ }
3
8
  export declare interface BroadcastStreamOptions {
4
9
  channels: readonly string[]
5
10
  authorize: () => boolean | Promise<boolean>
6
11
  signal?: AbortSignal
7
12
  heartbeatMs?: number
8
13
  maxPending?: number
14
+ onEnqueue?: (chunk: Uint8Array, broadcast: BroadcastStreamMetadata | null) => void
15
+ onClose?: () => void
9
16
  }
package/dist/stream.js CHANGED
@@ -1,7 +1,7 @@
1
- import{AsyncResource}from"node:async_hooks";import{getServer}from"./server-instance";export async function createBroadcastResponse(options){if(!options.channels.length||typeof options.authorize!=="function")throw Error("Broadcast streams require channels and authorization");const authorize=AsyncResource.bind(options.authorize,void 0,options);if(!await authorize())return new Response("Unauthorized",{status:401});const server=getServer();if(!server)throw Error("Broadcast server not initialized");const channels=new Set(options.channels),encoder=new TextEncoder,maxPending=options.maxPending??64;if(!Number.isSafeInteger(maxPending)||maxPending<1||maxPending>1024||options.heartbeatMs!==void 0&&(!Number.isFinite(options.heartbeatMs)||options.heartbeatMs<1))throw Error("Invalid broadcast stream configuration");let cleanup=()=>{};const stream=new ReadableStream({start(controller){let closed=!1,pending=0,chain=Promise.resolve(),timer,remove=()=>{};function close(){if(closed)return;closed=!0;remove();clearInterval(timer);options.signal?.removeEventListener("abort",close);try{controller.close()}catch{}}function enqueue(frame){if(closed)return;if((controller.desiredSize??0)<=0){close();return}controller.enqueue(encoder.encode(frame))}function deliver(frame){if(closed)return;if(++pending>maxPending){close();return}chain=chain.then(async()=>{if(closed)return;if(!await authorize()){close();return}enqueue(frame)}).catch(close).finally(()=>{pending--})}cleanup=close;remove=server.addBroadcastHook(({channel,event,data})=>{if(channels.has(channel))deliver(`data: ${JSON.stringify({channel,event,data})}
1
+ import{AsyncResource}from"node:async_hooks";import{getServer}from"./server-instance";export async function createBroadcastResponse(options){if(!options.channels.length||typeof options.authorize!=="function")throw Error("Broadcast streams require channels and authorization");const authorize=AsyncResource.bind(options.authorize,void 0,options),enqueueObserver=options.onEnqueue?AsyncResource.bind(options.onEnqueue,void 0,options):void 0,closeObserver=options.onClose?AsyncResource.bind(options.onClose,void 0,options):void 0;if(!await authorize())return new Response("Unauthorized",{status:401});const server=getServer();if(!server)throw Error("Broadcast server not initialized");const channels=new Set(options.channels),encoder=new TextEncoder,maxPending=options.maxPending??64;if(!Number.isSafeInteger(maxPending)||maxPending<1||maxPending>1024||options.heartbeatMs!==void 0&&(!Number.isFinite(options.heartbeatMs)||options.heartbeatMs<1))throw Error("Invalid broadcast stream configuration");let cleanup=()=>{};const stream=new ReadableStream({start(controller){let closed=!1,pending=0,chain=Promise.resolve(),timer,remove=()=>{};function close(){if(closed)return;closed=!0;remove();clearInterval(timer);options.signal?.removeEventListener("abort",close);try{controller.close()}catch{}try{const result=closeObserver?.();if(result!==void 0)Promise.resolve(result).catch(()=>{})}catch{}}function enqueue(frame,broadcast=null){if(closed)return;if((controller.desiredSize??0)<=0){close();return}const chunk=encoder.encode(frame);controller.enqueue(chunk);try{const result=enqueueObserver?.(chunk,broadcast);if(result!==void 0)Promise.resolve(result).catch(close)}catch{close()}}function deliver(frame,broadcast=null){if(closed)return;if(++pending>maxPending){close();return}chain=chain.then(async()=>{if(closed)return;if(!await authorize()){close();return}enqueue(frame,broadcast)}).catch(close).finally(()=>{pending--})}cleanup=close;remove=server.addBroadcastHook(({channel,event,data})=>{if(channels.has(channel))deliver(`data: ${JSON.stringify({channel,event,data})}
2
2
 
3
- `)});enqueue(`: connected
3
+ `,{channel,event,data})});enqueue(`: connected
4
4
 
5
- `);timer=setInterval(()=>deliver(`: heartbeat
5
+ `);if(!closed){timer=setInterval(()=>deliver(`: heartbeat
6
6
 
7
- `),options.heartbeatMs??15000);options.signal?.addEventListener("abort",close,{once:!0});if(options.signal?.aborted)close()},cancel(){cleanup()}},{highWaterMark:maxPending});return new Response(stream,{headers:{"Content-Type":"text/event-stream","Cache-Control":"no-store","X-Accel-Buffering":"no"}})}
7
+ `),options.heartbeatMs??15000);options.signal?.addEventListener("abort",close,{once:!0});if(options.signal?.aborted)close()}},cancel(){cleanup()}},{highWaterMark:maxPending});return new Response(stream,{headers:{"Content-Type":"text/event-stream","Cache-Control":"no-store","X-Accel-Buffering":"no"}})}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/realtime",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.75.236",
5
+ "version": "0.75.238",
6
6
  "description": "The Stacks realtime integration. Built on top of ts-broadcasting.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -58,8 +58,8 @@
58
58
  "prepublishOnly": "bun run build"
59
59
  },
60
60
  "dependencies": {
61
- "@stacksjs/logging": "0.75.236",
62
- "@stacksjs/path": "0.75.236",
61
+ "@stacksjs/logging": "0.75.238",
62
+ "@stacksjs/path": "0.75.238",
63
63
  "ts-broadcasting": "^0.0.11"
64
64
  },
65
65
  "devDependencies": {