@stacksjs/realtime 0.70.73 → 0.70.75

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
@@ -18,7 +18,7 @@ export * from 'ts-broadcasting';
18
18
  export { getServer, setServer, createServer, stopServer } from './server-instance';
19
19
  // Stacks-specific exports
20
20
  export { emit, emitToUser, emitToUsers } from './emit';
21
- export { channel as createChannel, Channel as StacksChannel } from './channel';
21
+ export { channel, channel as createChannel, Channel as StacksChannel } from './channel';
22
22
  export { broadcast as dispatchBroadcast, runBroadcast, Broadcast as LegacyBroadcast } from './broadcast';
23
23
  // Backpressure guard for slow consumers (stacksjs/stacks#1877 R-2).
24
24
  // Opt-in via setBackpressureGuard; default is no-op.
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- var O=import.meta.require;export*from"ts-broadcasting";var _=null;function D(G){_=G}function L(){return _}async function N(G){let V=new(await import("ts-broadcasting")).BroadcastServer(G);return await V.start(),D(V),V}async function w(){if(_)await _.stop(),_=null}function F(G,J,Q,V){let Y=L();if(!Y){console.warn("[realtime] Server not initialized, cannot emit event");return}let Z=G;if(V?.presence){if(!G.startsWith("presence-"))Z=`presence-${G}`}else if(V?.private){if(!G.startsWith("private-"))Z=`private-${G}`}let W=V?.exclude?Array.isArray(V.exclude)?V.exclude[0]:V.exclude:void 0;Y.broadcast(Z,J,Q,W)}function M(G,J,Q,V){F(`private-user.${G}`,J,Q,{...V,private:!0})}function b(G,J,Q,V){for(let Y of G)M(Y,J,Q,V)}var y=["private-","presence-"];function T(G){for(let J of y)if(G.startsWith(J))return G.slice(J.length);return G}class E{channelName;constructor(G){this.channelName=G}async private(G,J){let Q=L();if(!Q)throw Error("Broadcast server not initialized");await Q.broadcast(`private-${T(this.channelName)}`,G,J)}async public(G,J){let Q=L();if(!Q)throw Error("Broadcast server not initialized");await Q.broadcast(T(this.channelName),G,J)}async presence(G,J){let Q=L();if(!Q)throw Error("Broadcast server not initialized");await Q.broadcast(`presence-${T(this.channelName)}`,G,J)}async broadcast(G,J,Q="public"){switch(Q){case"private":return this.private(G,J);case"presence":return this.presence(G,J);default:return this.public(G,J)}}}function f(G){return new E(G)}import{log as U}from"@stacksjs/logging";var $=null;function u(G){if(!G){$=null;return}$={channels:G.channels??[],maxPerChannel:G.maxPerChannel??100,ttlMs:G.ttlMs??300000,state:new Map}}function m(){return $}function p(G){if(!$||$.channels.length===0)return!1;for(let J of $.channels){if(J==="*")return!0;if(J===G)return!0;if(J.endsWith(".*")&&G.startsWith(J.slice(0,-1)))return!0}return!1}function A(G,J,Q){if(!$||!p(G))return null;let V=$.state.get(G);if(!V)V={messages:[],nextSeq:1},$.state.set(G,V);let Y={seq:V.nextSeq++,ts:Date.now(),event:J,data:Q};if(V.messages.push(Y),V.messages.length>$.maxPerChannel)V.messages.splice(0,V.messages.length-$.maxPerChannel);return Y.seq}function d(G,J){if(!$)return[];let Q=$.state.get(G);if(!Q)return[];let V=Date.now(),Y=$.ttlMs;while(Q.messages.length>0&&V-Q.messages[0].ts>Y)Q.messages.shift();if(Q.messages.length===0)return[];return Q.messages.filter((Z)=>Z.seq>J)}function g(){if(!$)return;let G=Date.now(),J=$.ttlMs;for(let Q of $.state.values())while(Q.messages.length>0&&G-Q.messages[0].ts>J)Q.messages.shift()}function h(){let G={};if(!$)return G;for(let[J,Q]of $.state)G[J]={count:Q.messages.length,firstSeq:Q.messages[0]?.seq??null,lastSeq:Q.messages[Q.messages.length-1]?.seq??null};return G}var j=null;function v(G){if(!G){j=null;return}j={maxPerSocketBytes:G.maxPerSocketBytes??1048576,onSlow:G.onSlow??((J)=>{U.warn(`[realtime] slow consumer on '${J.channelName}': ${J.backpressure} bytes buffered`)})}}function i(){return j}function l(G,J){if(!j)return;try{let Q=G.channels??G.clients,V=Q&&typeof Q.get==="function"?Q.get(J):null;if(!V||typeof V[Symbol.iterator]!=="function")return;let{maxPerSocketBytes:Y,onSlow:Z}=j;for(let W of V){let R=W&&typeof W==="object"&&"ws"in W?W.ws:W,z=R&&typeof R==="object"&&"backpressure"in R?R.backpressure:null;if(typeof z==="number"&&z>Y)Z({channelName:J,backpressure:z,socket:R})}}catch{}}function o(G,J){try{if(typeof G.hasSubscribers==="function")return Boolean(G.hasSubscribers(J));if(typeof G.subscriberCount==="function")return G.subscriberCount(J)>0;let Q=G.channels??G.clients;if(Q&&typeof Q.get==="function"){let V=Q.get(J),Y=(V&&(V.size??V.length))??null;if(typeof Y==="number")return Y>0}}catch{}return!0}class P{async connect(){}async disconnect(){}subscribe(G,J){U.warn("Broadcast.subscribe() is a client-side operation. Use BroadcastClient instead.")}unsubscribe(G){U.warn("Broadcast.unsubscribe() is a client-side operation. Use BroadcastClient instead.")}broadcast(G,J,Q,V="public"){let Y=L();if(!Y){U.warn("Broadcast server not initialized");return}let Z=G;if(V==="private"&&!G.startsWith("private-"))Z=`private-${G}`;else if(V==="presence"&&!G.startsWith("presence-"))Z=`presence-${G}`;if(!o(Y,Z)){U.debug(`[Broadcast] Skipping '${J}' on '${Z}' \u2014 no subscribers`);return}l(Y,Z),A(Z,J,Q);try{Y.broadcast(Z,J,Q)}catch(W){U.error(`[Broadcast] Failed to broadcast event '${J}' to channel '${Z}':`,W)}}isConnected(){return L()!==null}}async function q(G,J){let{appPath:Q}=await import("@stacksjs/path"),V=awaitPromise.resolve(globalThis.Bun),Y;try{Y=V.globSync([Q("Broadcasts/**/*.ts")],{absolute:!0})}catch(X){throw Error(`Failed to scan broadcast files: ${X instanceof Error?X.message:String(X)}`)}let Z=Y.find((X)=>X.endsWith(`${G}.ts`));if(!Z)throw Error(`Broadcast ${G} not found`);let W;try{W=await import(Z)}catch(X){throw Error(`Failed to import broadcast '${G}': ${X instanceof Error?X.message:String(X)}`)}let R=W.default;if(R.handle){await R.handle(J);return}let z=L();if(!z)throw Error("Broadcast server not initialized");let x=R.broadcastOn?.()||R.channel?.()||[],I=R.broadcastAs?.()||R.event?.()||G,S=R.broadcastWith?.()||R.data?.()||J,k={shouldBroadcast:()=>!0,broadcastOn:()=>x,broadcastAs:()=>I,broadcastWith:()=>S};await z.broadcaster.broadcast(k)}async function c(G,J){if(typeof G!=="string"||G.trim().length===0)throw Error("[realtime] broadcast() requires a non-empty event name");await q(G,J)}import{log as B}from"@stacksjs/logging";var K=null;function s(G){if(K?.timer)clearInterval(K.timer),K.timer=null;if(!G){K=null;return}let J=G.intervalMs??30000,Q=G.maxMissedPongs??2,V=G.onDead??t;K={intervalMs:J,maxMissedPongs:Q,onDead:V,missed:new WeakMap,timer:null},K.timer=setInterval(()=>{if(!K)return;C()},J),K.timer.unref?.()}function r(){return K}function C(){if(!K)return;let G=L();if(!G)return;let J=a(G);for(let Q of J){let V=Q,Y=K.missed.get(Q)??0;if(Y>=K.maxMissedPongs){B.warn(`[realtime] socket missed ${Y} pongs \u2014 declaring dead`);try{K.onDead(Q)}catch(Z){B.warn(`[realtime] heartbeat onDead handler threw: ${Z instanceof Error?Z.message:String(Z)}`)}K.missed.delete(Q);continue}K.missed.set(Q,Y+1);try{if(typeof V.ping==="function")V.ping();else if(typeof V.send==="function")V.send("__stacks_ping__")}catch{}}}function n(G){if(!K)return;K.missed.delete(G)}function a(G){let J=new Set;try{let Q=G.channels??G.clients;if(Q&&typeof Q.values==="function"){for(let V of Q.values())if(V&&typeof V[Symbol.iterator]==="function")for(let Y of V){let Z=Y&&typeof Y==="object"&&"ws"in Y?Y.ws:Y;if(Z&&typeof Z==="object")J.add(Z)}}}catch{}return[...J]}function t(G){let J=G;if(typeof J.close==="function")J.close(1011,"heartbeat timeout")}function e(G){if(G)D(G)}async function GG(G,J,Q){}var H=null;function JG(G){H=G}function QG(){return H}async function VG(G,J){if(!L())return new Response("WebSocket server not initialized",{status:500});if(H)try{let Y=await H(G);if(!Y.ok)return new Response(Y.message??"Unauthorized",{status:Y.status??401});if(J.upgrade(G,Y.data?{data:Y.data}:void 0))return;return new Response("WebSocket upgrade failed",{status:400})}catch(Y){return console.error("[realtime] WebSocket authenticator threw:",Y),new Response("WebSocket auth error",{status:500})}if(J.upgrade(G))return;return new Response("WebSocket upgrade failed",{status:400})}export{GG as storeWebSocketEvent,w as stopServer,JG as setWsAuthenticator,D as setServer,u as setReplayBuffer,s as setHeartbeatConfig,e as setBunSocket,v as setBackpressureGuard,C as runOneTick,q as runBroadcast,d as replaySince,A as recordBroadcast,g as pruneExpired,n as markPong,VG as handleWebSocketRequest,QG as getWsAuthenticator,L as getServer,m as getReplayBuffer,r as getHeartbeatConfig,i as getBackpressureGuard,b as emitToUsers,M as emitToUser,F as emit,c as dispatchBroadcast,h as debugSnapshot,N as createServer,f as createChannel,E as StacksChannel,P as LegacyBroadcast};
2
+ var O=import.meta.require;export*from"ts-broadcasting";var _=null;function D(G){_=G}function L(){return _}async function w(G){let V=new(await import("ts-broadcasting")).BroadcastServer(G);return await V.start(),D(V),V}async function b(){if(_)await _.stop(),_=null}function F(G,J,Q,V){let Y=L();if(!Y){console.warn("[realtime] Server not initialized, cannot emit event");return}let Z=G;if(V?.presence){if(!G.startsWith("presence-"))Z=`presence-${G}`}else if(V?.private){if(!G.startsWith("private-"))Z=`private-${G}`}let W=V?.exclude?Array.isArray(V.exclude)?V.exclude[0]:V.exclude:void 0;Y.broadcast(Z,J,Q,W)}function M(G,J,Q,V){F(`private-user.${G}`,J,Q,{...V,private:!0})}function y(G,J,Q,V){for(let Y of G)M(Y,J,Q,V)}var f=["private-","presence-"];function T(G){for(let J of f)if(G.startsWith(J))return G.slice(J.length);return G}class E{channelName;constructor(G){this.channelName=G}async private(G,J){let Q=L();if(!Q)throw Error("Broadcast server not initialized");await Q.broadcast(`private-${T(this.channelName)}`,G,J)}async public(G,J){let Q=L();if(!Q)throw Error("Broadcast server not initialized");await Q.broadcast(T(this.channelName),G,J)}async presence(G,J){let Q=L();if(!Q)throw Error("Broadcast server not initialized");await Q.broadcast(`presence-${T(this.channelName)}`,G,J)}async broadcast(G,J,Q="public"){switch(Q){case"private":return this.private(G,J);case"presence":return this.presence(G,J);default:return this.public(G,J)}}}function P(G){return new E(G)}import{log as U}from"@stacksjs/logging";var $=null;function u(G){if(!G){$=null;return}$={channels:G.channels??[],maxPerChannel:G.maxPerChannel??100,ttlMs:G.ttlMs??300000,state:new Map}}function m(){return $}function p(G){if(!$||$.channels.length===0)return!1;for(let J of $.channels){if(J==="*")return!0;if(J===G)return!0;if(J.endsWith(".*")&&G.startsWith(J.slice(0,-1)))return!0}return!1}function A(G,J,Q){if(!$||!p(G))return null;let V=$.state.get(G);if(!V)V={messages:[],nextSeq:1},$.state.set(G,V);let Y={seq:V.nextSeq++,ts:Date.now(),event:J,data:Q};if(V.messages.push(Y),V.messages.length>$.maxPerChannel)V.messages.splice(0,V.messages.length-$.maxPerChannel);return Y.seq}function d(G,J){if(!$)return[];let Q=$.state.get(G);if(!Q)return[];let V=Date.now(),Y=$.ttlMs;while(Q.messages.length>0&&V-Q.messages[0].ts>Y)Q.messages.shift();if(Q.messages.length===0)return[];return Q.messages.filter((Z)=>Z.seq>J)}function g(){if(!$)return;let G=Date.now(),J=$.ttlMs;for(let Q of $.state.values())while(Q.messages.length>0&&G-Q.messages[0].ts>J)Q.messages.shift()}function h(){let G={};if(!$)return G;for(let[J,Q]of $.state)G[J]={count:Q.messages.length,firstSeq:Q.messages[0]?.seq??null,lastSeq:Q.messages[Q.messages.length-1]?.seq??null};return G}var j=null;function v(G){if(!G){j=null;return}j={maxPerSocketBytes:G.maxPerSocketBytes??1048576,onSlow:G.onSlow??((J)=>{U.warn(`[realtime] slow consumer on '${J.channelName}': ${J.backpressure} bytes buffered`)})}}function i(){return j}function l(G,J){if(!j)return;try{let Q=G.channels??G.clients,V=Q&&typeof Q.get==="function"?Q.get(J):null;if(!V||typeof V[Symbol.iterator]!=="function")return;let{maxPerSocketBytes:Y,onSlow:Z}=j;for(let W of V){let R=W&&typeof W==="object"&&"ws"in W?W.ws:W,z=R&&typeof R==="object"&&"backpressure"in R?R.backpressure:null;if(typeof z==="number"&&z>Y)Z({channelName:J,backpressure:z,socket:R})}}catch{}}function o(G,J){try{if(typeof G.hasSubscribers==="function")return Boolean(G.hasSubscribers(J));if(typeof G.subscriberCount==="function")return G.subscriberCount(J)>0;let Q=G.channels??G.clients;if(Q&&typeof Q.get==="function"){let V=Q.get(J),Y=(V&&(V.size??V.length))??null;if(typeof Y==="number")return Y>0}}catch{}return!0}class q{async connect(){}async disconnect(){}subscribe(G,J){U.warn("Broadcast.subscribe() is a client-side operation. Use BroadcastClient instead.")}unsubscribe(G){U.warn("Broadcast.unsubscribe() is a client-side operation. Use BroadcastClient instead.")}broadcast(G,J,Q,V="public"){let Y=L();if(!Y){U.warn("Broadcast server not initialized");return}let Z=G;if(V==="private"&&!G.startsWith("private-"))Z=`private-${G}`;else if(V==="presence"&&!G.startsWith("presence-"))Z=`presence-${G}`;if(!o(Y,Z)){U.debug(`[Broadcast] Skipping '${J}' on '${Z}' \u2014 no subscribers`);return}l(Y,Z),A(Z,J,Q);try{Y.broadcast(Z,J,Q)}catch(W){U.error(`[Broadcast] Failed to broadcast event '${J}' to channel '${Z}':`,W)}}isConnected(){return L()!==null}}async function B(G,J){let{appPath:Q}=await import("@stacksjs/path"),V=awaitPromise.resolve(globalThis.Bun),Y;try{Y=V.globSync([Q("Broadcasts/**/*.ts")],{absolute:!0})}catch(X){throw Error(`Failed to scan broadcast files: ${X instanceof Error?X.message:String(X)}`)}let Z=Y.find((X)=>X.endsWith(`${G}.ts`));if(!Z)throw Error(`Broadcast ${G} not found`);let W;try{W=await import(Z)}catch(X){throw Error(`Failed to import broadcast '${G}': ${X instanceof Error?X.message:String(X)}`)}let R=W.default;if(R.handle){await R.handle(J);return}let z=L();if(!z)throw Error("Broadcast server not initialized");let I=R.broadcastOn?.()||R.channel?.()||[],S=R.broadcastAs?.()||R.event?.()||G,k=R.broadcastWith?.()||R.data?.()||J,N={shouldBroadcast:()=>!0,broadcastOn:()=>I,broadcastAs:()=>S,broadcastWith:()=>k};await z.broadcaster.broadcast(N)}async function c(G,J){if(typeof G!=="string"||G.trim().length===0)throw Error("[realtime] broadcast() requires a non-empty event name");await B(G,J)}import{log as C}from"@stacksjs/logging";var K=null;function s(G){if(K?.timer)clearInterval(K.timer),K.timer=null;if(!G){K=null;return}let J=G.intervalMs??30000,Q=G.maxMissedPongs??2,V=G.onDead??t;K={intervalMs:J,maxMissedPongs:Q,onDead:V,missed:new WeakMap,timer:null},K.timer=setInterval(()=>{if(!K)return;x()},J),K.timer.unref?.()}function r(){return K}function x(){if(!K)return;let G=L();if(!G)return;let J=a(G);for(let Q of J){let V=Q,Y=K.missed.get(Q)??0;if(Y>=K.maxMissedPongs){C.warn(`[realtime] socket missed ${Y} pongs \u2014 declaring dead`);try{K.onDead(Q)}catch(Z){C.warn(`[realtime] heartbeat onDead handler threw: ${Z instanceof Error?Z.message:String(Z)}`)}K.missed.delete(Q);continue}K.missed.set(Q,Y+1);try{if(typeof V.ping==="function")V.ping();else if(typeof V.send==="function")V.send("__stacks_ping__")}catch{}}}function n(G){if(!K)return;K.missed.delete(G)}function a(G){let J=new Set;try{let Q=G.channels??G.clients;if(Q&&typeof Q.values==="function"){for(let V of Q.values())if(V&&typeof V[Symbol.iterator]==="function")for(let Y of V){let Z=Y&&typeof Y==="object"&&"ws"in Y?Y.ws:Y;if(Z&&typeof Z==="object")J.add(Z)}}}catch{}return[...J]}function t(G){let J=G;if(typeof J.close==="function")J.close(1011,"heartbeat timeout")}function e(G){if(G)D(G)}async function GG(G,J,Q){}var H=null;function JG(G){H=G}function QG(){return H}async function VG(G,J){if(!L())return new Response("WebSocket server not initialized",{status:500});if(H)try{let Y=await H(G);if(!Y.ok)return new Response(Y.message??"Unauthorized",{status:Y.status??401});if(J.upgrade(G,Y.data?{data:Y.data}:void 0))return;return new Response("WebSocket upgrade failed",{status:400})}catch(Y){return console.error("[realtime] WebSocket authenticator threw:",Y),new Response("WebSocket auth error",{status:500})}if(J.upgrade(G))return;return new Response("WebSocket upgrade failed",{status:400})}export{GG as storeWebSocketEvent,b as stopServer,JG as setWsAuthenticator,D as setServer,u as setReplayBuffer,s as setHeartbeatConfig,e as setBunSocket,v as setBackpressureGuard,x as runOneTick,B as runBroadcast,d as replaySince,A as recordBroadcast,g as pruneExpired,n as markPong,VG as handleWebSocketRequest,QG as getWsAuthenticator,L as getServer,m as getReplayBuffer,r as getHeartbeatConfig,i as getBackpressureGuard,y as emitToUsers,M as emitToUser,F as emit,c as dispatchBroadcast,h as debugSnapshot,w as createServer,P as createChannel,P as channel,E as StacksChannel,q as LegacyBroadcast};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/realtime",
3
3
  "type": "module",
4
- "version": "0.70.73",
4
+ "version": "0.70.75",
5
5
  "description": "The Stacks realtime integration. Built on top of ts-broadcasting.",
6
6
  "author": "Chris Breuer",
7
7
  "contributors": [
@@ -55,6 +55,6 @@
55
55
  "ts-broadcasting": "^0.0.5"
56
56
  },
57
57
  "devDependencies": {
58
- "better-dx": "^0.2.12"
58
+ "better-dx": "^0.2.16"
59
59
  }
60
60
  }