@shadow-js/realtime 1.0.4 → 1.0.5
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/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/dist/dev/client/index.d.ts +1 -1
- package/dist/dev/cloudflare/adapter.js +1 -1
- package/dist/dev/cloudflare/object.js +1 -1
- package/dist/dev/cloudflare.d.ts +1 -1
- package/dist/dev/hooks.d.ts +1 -1
- package/dist/dev/index.d.ts +1 -1
- package/dist/dev/server/hub.d.ts +2 -2
- package/dist/dev/server/hub.js +1 -1
- package/dist/dev/server/index.d.ts +3 -3
- package/dist/dev/server/index.js +2 -2
- package/dist/dev/testing.d.ts +2 -2
- package/dist/prod/cloudflare/adapter.js +2 -2
- package/dist/prod/cloudflare/object.js +1 -1
- package/dist/prod/server/hub.js +1 -1
- package/dist/prod/server/index.js +1 -1
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -106,7 +106,7 @@ Root, `@shadow-js/realtime`: the wire protocol and shared types.
|
|
|
106
106
|
|
|
107
107
|
## Production error codes
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
For the errors in the table below, a production build keeps a short code instead of the development explanation; other errors keep their message in every build. Search this table for the code. In the explanation, `…` stands for a value the development message names. A code followed by a word names the option, feature or member at fault.
|
|
110
110
|
|
|
111
111
|
| Code | Development explanation |
|
|
112
112
|
| --- | --- |
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ErrorCode, RealtimeError } from "../errors.js";
|
|
2
2
|
import { Json, Member } from "../protocol.js";
|
|
3
|
-
import { AnyMessage, Channel, ChannelDef, ChannelOptions, ChannelPresence, ChannelSchema, ChannelStatus, GapInfo, MessageMeta, PresenceOf, PublishAck, PublishOptions, Realtime, RealtimeOptions, RequestData, RequestReply, RequestsOf, Status } from "./types.js";
|
|
4
3
|
import { createRealtime } from "./core.js";
|
|
4
|
+
import { AnyMessage, Channel, ChannelDef, ChannelOptions, ChannelPresence, ChannelSchema, ChannelStatus, GapInfo, MessageMeta, PresenceOf, PublishAck, PublishOptions, Realtime, RealtimeOptions, RequestData, RequestReply, RequestsOf, Status } from "./types.js";
|
|
5
5
|
export { type AnyMessage, type Channel, type ChannelDef, type ChannelOptions, type ChannelPresence, type ChannelSchema, type ChannelStatus, type ErrorCode, type GapInfo, type Json, type Member, type MessageMeta, type PresenceOf, type PublishAck, type PublishOptions, type Realtime, RealtimeError, type RealtimeOptions, type RequestData, type RequestReply, type RequestsOf, type Status, createRealtime };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RealtimeError } from "../errors.js";
|
|
2
|
-
import { isWholeNumber, parseMember, parseStored } from "./wire.js";
|
|
3
2
|
import { ChannelLink } from "./link.js";
|
|
3
|
+
import { isWholeNumber, parseMember, parseStored } from "./wire.js";
|
|
4
4
|
// src/cloudflare/adapter.ts
|
|
5
5
|
const ORIGIN = "https://realtime.internal";
|
|
6
6
|
const PAGE = 512;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { RealtimeError } from "../errors.js";
|
|
2
2
|
import { isChannelName } from "../protocol.js";
|
|
3
|
-
import { isOperation, parseRequest } from "./wire.js";
|
|
4
3
|
import { ChannelCore } from "./core.js";
|
|
4
|
+
import { isOperation, parseRequest } from "./wire.js";
|
|
5
5
|
// src/cloudflare/object.ts
|
|
6
6
|
function workersPlatform() {
|
|
7
7
|
return {
|
package/dist/dev/cloudflare.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ChannelNamespace, ChannelObjectState, ChannelRequestInit, ChannelResponse, ChannelSocket, ChannelStorage, ChannelStub, ClientSocket, StorageListOptions, StorageTransaction } from "./cloudflare/types.js";
|
|
2
1
|
import { CloudflareRealtimeOptions, cloudflareAdapter } from "./cloudflare/adapter.js";
|
|
3
2
|
import { RealtimeChannelObject } from "./cloudflare/object.js";
|
|
3
|
+
import { ChannelNamespace, ChannelObjectState, ChannelRequestInit, ChannelResponse, ChannelSocket, ChannelStorage, ChannelStub, ClientSocket, StorageListOptions, StorageTransaction } from "./cloudflare/types.js";
|
|
4
4
|
export { type ChannelNamespace, type ChannelObjectState, type ChannelRequestInit, type ChannelResponse, type ChannelSocket, type ChannelStorage, type ChannelStub, type ClientSocket, type CloudflareRealtimeOptions, RealtimeChannelObject, type StorageListOptions, type StorageTransaction, cloudflareAdapter };
|
package/dist/dev/hooks.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Member } from "./protocol.js";
|
|
2
1
|
import { AnyMessage, Channel, ChannelDef, ChannelOptions, ChannelSchema, ChannelStatus, MessageMeta, PresenceOf, Realtime, RequestData, RequestReply, RequestsOf, Status } from "./client/types.js";
|
|
2
|
+
import { Member } from "./protocol.js";
|
|
3
3
|
import { Accessor } from "@shadow-js/core";
|
|
4
4
|
/**
|
|
5
5
|
* What {@link useChannel} returns: the reactive state of a channel and the calls on it.
|
package/dist/dev/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
+
import { Adapter, HistoryResult, PresenceRef, PublishOptions, PublishResult, Unsubscribe } from "./adapter.js";
|
|
1
2
|
import { ErrorCode, RealtimeError } from "./errors.js";
|
|
2
3
|
import { ClientFrame, DecodedClientFrame, Json, Member, PROTOCOL_VERSION, PresenceEvent, ServerFrame, StoredEntry, StoredMessage, decodeClientFrame, decodeServerFrame, isChannelName, isEventName } from "./protocol.js";
|
|
3
|
-
import { Adapter, HistoryResult, PresenceRef, PublishOptions, PublishResult, Unsubscribe } from "./adapter.js";
|
|
4
4
|
export { type Adapter, type ClientFrame, type DecodedClientFrame, type ErrorCode, type HistoryResult, type Json, type Member, PROTOCOL_VERSION, type PresenceEvent, type PresenceRef, type PublishOptions, type PublishResult, RealtimeError, type ServerFrame, type StoredEntry, type StoredMessage, type Unsubscribe, decodeClientFrame, decodeServerFrame, isChannelName, isEventName };
|
package/dist/dev/server/hub.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Json } from "../protocol.js";
|
|
2
1
|
import { Adapter, PublishResult } from "../adapter.js";
|
|
3
|
-
import {
|
|
2
|
+
import { Json } from "../protocol.js";
|
|
4
3
|
import { HubLimits, TokenBucket } from "./limits.js";
|
|
4
|
+
import { ChannelRule, Identity, RuleContext } from "./rules.js";
|
|
5
5
|
/**
|
|
6
6
|
* What a hub needs from a connection's transport. A WebSocket, an SSE stream and an in-memory test client all fit.
|
|
7
7
|
* @since 1.0.0
|
package/dist/dev/server/hub.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { RealtimeError } from "../errors.js";
|
|
2
|
+
import { memoryAdapter } from "../memory.js";
|
|
2
3
|
import { byteLength, decodeClientFrame, isChannelName, isEventName, isIdempotencyKey } from "../protocol.js";
|
|
3
4
|
import { TokenBucket, WindowCounter, resolveLimits } from "./limits.js";
|
|
4
|
-
import { memoryAdapter } from "../memory.js";
|
|
5
5
|
import { compileRule, matchRule } from "./rules.js";
|
|
6
6
|
// src/server/hub.ts
|
|
7
7
|
const REPLAY_PAGE = 500;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Adapter } from "../adapter.js";
|
|
2
1
|
import { ChannelRule, Identity, Permission, RequestContext, RequestHandler, RuleContext } from "./rules.js";
|
|
3
|
-
import { DEFAULT_HUB_LIMITS, HubLimits } from "./limits.js";
|
|
4
|
-
import { ConnectOptions, ErrorContext, HubConnection, HubCore, HubOptions, HubStats, RevokeTarget, Transport } from "./hub.js";
|
|
5
2
|
import { AuthenticateResult, HubRouteOptions, RequestLike, SseLike, WebSocketLike, WebSocketRoute } from "./http.js";
|
|
3
|
+
import { ConnectOptions, ErrorContext, HubConnection, HubCore, HubOptions, HubStats, RevokeTarget, Transport } from "./hub.js";
|
|
4
|
+
import { Adapter } from "../adapter.js";
|
|
6
5
|
import { MemoryAdapterOptions, MemoryErrorContext, memoryAdapter } from "../memory.js";
|
|
6
|
+
import { DEFAULT_HUB_LIMITS, HubLimits } from "./limits.js";
|
|
7
7
|
/**
|
|
8
8
|
* The server object of `@shadow-js/realtime`: connections, channel rules, limits and an adapter, with the routes that
|
|
9
9
|
* connect it to a transport.
|
package/dist/dev/server/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { DEFAULT_HUB_LIMITS } from "./limits.js";
|
|
2
1
|
import { postRoute, sseRoute, websocketRoute } from "./http.js";
|
|
3
|
-
import { memoryAdapter } from "../memory.js";
|
|
4
2
|
import { createHubCore } from "./hub.js";
|
|
3
|
+
import { memoryAdapter } from "../memory.js";
|
|
4
|
+
import { DEFAULT_HUB_LIMITS } from "./limits.js";
|
|
5
5
|
// src/server/index.ts
|
|
6
6
|
/**
|
|
7
7
|
* Creates the hub: the server side of `@shadow-js/realtime`. It owns connections and channel rules (default deny), enforces the
|
package/dist/dev/testing.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ServerFrame } from "./protocol.js";
|
|
2
1
|
import { Adapter } from "./adapter.js";
|
|
3
|
-
import {
|
|
2
|
+
import { ServerFrame } from "./protocol.js";
|
|
4
3
|
import { Hub } from "./server/index.js";
|
|
4
|
+
import { Identity } from "./server/rules.js";
|
|
5
5
|
/**
|
|
6
6
|
* A connection to a hub with no network: frames go in as objects and come out decoded. For tests of an app's channel rules and
|
|
7
7
|
* request handlers.
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{RealtimeError as e}from"../errors.js";import{
|
|
2
|
-
`)))throw TypeError(`cloudflareAdapter: prefix must be 1..256 characters without a newline`);let _=u(
|
|
1
|
+
import{RealtimeError as e}from"../errors.js";import{ChannelLink as t}from"./link.js";import{isWholeNumber as n,parseMember as r,parseStored as i}from"./wire.js";const a=`https://realtime.internal`,o=/* @__PURE__ */ new Set([`too_large`,`bad_channel`,`bad_frame`,`limit`,`conflict`]);function s(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function c(t,n){return new e(`unavailable`,t,n===void 0?void 0:{cause:n})}function l(e,t,r=!1){if(!n(e)||r&&e<1)throw TypeError(`${t} must be a whole number`);return e}function u(e,t,n){return e===void 0?t:l(e,n)}const d=e=>s(e)&&n(e.seq)&&e.seq>=1&&typeof e.duplicate==`boolean`?{seq:e.seq,duplicate:e.duplicate}:void 0,f=e=>{if(!s(e)||!Array.isArray(e.messages))return;let{head:t,oldest:r,more:a}=e;if(!n(t)||!n(r)||typeof a!=`boolean`)return;let o=[];for(let t of e.messages){let e=i(t);if(e===void 0)return;o.push(e)}return{messages:o,head:t,oldest:r,more:a}},p=e=>{if(!s(e)||!Array.isArray(e.members))return;let t=[];for(let n of e.members){let e=r(n);if(e===void 0)return;t.push(e)}return t},m=e=>s(e)&&typeof e.removed==`boolean`?e.removed:void 0,h=e=>{if(s(e)&&Array.isArray(e.missing))return e.missing.every(e=>typeof e==`string`)?e.missing:void 0},g=e=>s(e)?!0:void 0;function _(n){let{namespace:r}=n;if(typeof r?.idFromName!=`function`||typeof r.get!=`function`)throw TypeError(`cloudflareAdapter: namespace must be a Durable Object namespace binding`);let i=n.prefix;if(i!==void 0&&(i.length===0||i.length>256||i.includes(`
|
|
2
|
+
`)))throw TypeError(`cloudflareAdapter: prefix must be 1..256 characters without a newline`);let _=u(n.requestTimeoutMs,1e4,`requestTimeoutMs`),v=u(n.connectTimeoutMs,1e4,`connectTimeoutMs`),y=u(n.retries,2,`retries`),b=u(n.reconnect?.minDelayMs,250,`reconnect.minDelayMs`),x=u(n.reconnect?.maxDelayMs,1e4,`reconnect.maxDelayMs`),S=u(n.keepAliveMs,3e4,`keepAliveMs`),C=u(n.presenceGraceMs,2e3,`presenceGraceMs`),w=u(n.idleMs,0,`idleMs`),T=l(n.maxQueued??1e3,`maxQueued`,!0);if(_===0||v===0)throw TypeError(`cloudflareAdapter: timeouts must be above 0`);let E=n.onError??((e,t)=>console.error(`[realtime] ${t.where} ${t.channel}`,e)),D=!1,O=/* @__PURE__ */ new Map,k=/* @__PURE__ */ new Set,A=/* @__PURE__ */ new Set,j=/* @__PURE__ */ new Set;function M(e){let t=i===void 0?e:`${i}\n${e}`,a=r.idFromName(t);return n.locationHint===void 0?r.get(a):r.get(a,{locationHint:n.locationHint})}function N(e,t){let n=setTimeout(()=>{k.delete(r),t()},e);n.unref?.();let r=()=>{clearTimeout(n),k.delete(r)};return k.add(r),r}function P(e,t){let n=setInterval(t,e);n.unref?.();let r=()=>{clearInterval(n),k.delete(r)};return k.add(r),r}function F(e){return new Promise((t,n)=>{let r=e=>{i(),j.delete(r),n(e)},i=N(e,()=>{j.delete(r),t()});j.add(r)})}function I(){if(D)throw new e(`closed`,`the adapter is closed`)}async function L(t,n,r,i,l){for(let u=0;;u+=1){I();let d=new AbortController;A.add(d);let f=setTimeout(()=>d.abort(),_);try{let l=await M(t).fetch(`${a}/v1/${n}`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({channel:t,...r}),signal:d.signal}),u=await l.text(),f;try{f=JSON.parse(u)}catch{throw c(`the channel object answered ${l.status} with a body that is not JSON`)}if(l.status===200){let e=i(f);if(e===void 0)throw c(`the channel object answered an unexpected shape`);return e}let p=s(f)&&s(f.error)?f.error:void 0,m=p?.code;throw typeof m==`string`&&o.has(m)?new e(m,String(p?.message??m)):c(`the channel object answered ${l.status}`)}catch(t){if(D)throw new e(`closed`,`the adapter is closed`,{cause:t});if(t instanceof e&&t.code!==`unavailable`)throw t;if(l&&u<y){await F(Math.min(50*3**u,1e3));continue}throw t instanceof e?t:c(`the channel object is unreachable`,t)}finally{clearTimeout(f),A.delete(d)}}}let R={reconnectMinMs:b,reconnectMaxMs:Math.max(b,x),keepAliveMs:S,connectTimeoutMs:v,maxQueued:T,now:Date.now,random:Math.random,async open(e,t){let n=`c=${encodeURIComponent(e)}${t===void 0?``:`&after=${t}`}`,r=await M(e).fetch(`${a}/v1/ws?${n}`,{headers:{upgrade:`websocket`}}),i=r.webSocket;if(r.status!==101||i==null)throw c(`the channel object refused the subscribe socket (${r.status})`);return i},after:N,every:P,report:(e,t,n)=>E(e,{where:t,channel:n}),forget(e){O.get(e.channel)===e&&O.delete(e.channel)}};function z(e){let n=O.get(e);return n===void 0&&(n=new t(e,R),O.set(e,n)),n}async function B(t,n){I();let r=z(t),i=n(r);try{await r.whenReady(v)}catch(t){throw r.remove(i),D?new e(`closed`,`the adapter is closed`,{cause:t}):t instanceof e?t:c(`could not subscribe`,t)}return async()=>{r.remove(i)}}let V=e=>({event:e.event,data:e.data,...e.publisher===void 0?{}:{publisher:e.publisher},...e.user===void 0?{}:{user:e.user}});return{publish(e,t,n){return L(e,`publish`,{entry:V(t),options:{history:n.history,historyTtlMs:n.historyTtlMs,idempotencyTtlMs:n.idempotencyTtlMs,...n.key===void 0?{}:{key:n.key}},...w>0?{idleMs:w}:{}},d,n.key!==void 0)},subscribe(e,t){return B(e,e=>e.addMessages(t))},async history(e,t,n){let r=(t,n)=>L(e,`history`,{after:t,limit:n},f,!0),i=await r(t,n<=0?0:Math.min(n,512)),{oldest:a}=i,o=[...i.messages];for(;i.more&&o.length<n;){let e=o[o.length-1];if(e===void 0)break;i=await r(e.seq,Math.min(n-o.length,512)),o.push(...i.messages)}return{messages:o,head:i.head,oldest:a}},async presenceSet(e,t,n){await L(e,`presence-set`,{member:t,ttlMs:n,graceMs:C},g,!0)},presenceRemove(e,t){return L(e,`presence-remove`,{id:t},m,!1)},presenceList(e){return L(e,`presence-list`,{},p,!0)},async presenceTouch(e,t){let n=/* @__PURE__ */ new Map;for(let t of e){let e=n.get(t.channel);e===void 0?n.set(t.channel,[t.id]):e.push(t.id)}return(await Promise.all([...n].map(async([e,n])=>(await L(e,`presence-touch`,{ids:n,ttlMs:t},h,!0)).map(t=>({channel:e,id:t}))))).flat()},presenceReap(e){return L(e,`presence-reap`,{},p,!1)},presenceSubscribe(e,t){return B(e,e=>e.addPresence(t))},async close(){if(!D){D=!0;for(let e of A)e.abort();for(let t of[...j])t(new e(`closed`,`the adapter is closed`));for(let t of[...O.values()])t.dispose(new e(`closed`,`the adapter is closed`));O.clear();for(let e of[...k])e()}}}}export{_ as cloudflareAdapter};
|
|
3
3
|
//# sourceMappingURL=adapter.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{RealtimeError as e}from"../errors.js";import{isChannelName as t}from"../protocol.js";import{
|
|
1
|
+
import{RealtimeError as e}from"../errors.js";import{isChannelName as t}from"../protocol.js";import{ChannelCore as n}from"./core.js";import{isOperation as r,parseRequest as i}from"./wire.js";function a(){return{createPair(){let t=Reflect.get(globalThis,`WebSocketPair`);if(typeof t!=`function`)throw new e(`unavailable`,`WebSocketPair is missing: this is not a Worker`);let n=Reflect.construct(t,[]),r=Reflect.get(Object(n),`0`),i=Reflect.get(Object(n),`1`);if(typeof i!=`object`||!i||typeof Reflect.get(i,`send`)!=`function`||typeof Reflect.get(i,`close`)!=`function`)throw TypeError(`WebSocketPair returned an unexpected socket`);return{client:r,server:i}},upgrade(e){return new Response(null,{status:101,webSocket:e})},autoResponse(){let e=Reflect.get(globalThis,`WebSocketRequestResponsePair`);return typeof e==`function`?Reflect.construct(e,[`ping`,`pong`]):void 0}}}const o={"content-type":`application/json`};function s(e,t=200){return new Response(JSON.stringify(e),{status:t,headers:o})}function c(e,t,n){return s({error:{code:e,message:t}},n)}const l={bad_frame:400,bad_channel:400,too_large:413,limit:429,conflict:409};function u(e){return e>=1e3&&e<5e3&&e!==1005&&e!==1006&&e!==1015?e:1e3}function d(e){let t=e.searchParams.get(`after`);if(t!==null)return/^\d{1,16}$/.test(t)?Number(t):`bad`}var f=class{#e;#t;#n;constructor(e,t,r){this.#e=e,this.#n=r??a(),this.#t=new n({storage:e.storage}),this.#t.adopt(e.getWebSockets());let i=this.#n.autoResponse();i!==void 0&&e.setWebSocketAutoResponse?.(i)}async fetch(t){let n=new URL(t.url);if(n.pathname===`/v1/ws`)return this.#r(t,n);let a=/^\/v(\d+)\/([a-z-]+)$/.exec(n.pathname);if(a?.[1]!==`1`||!r(a[2]??``))return c(`bad_frame`,`unknown operation`,404);let o=a[2]??``;if(!r(o))return c(`bad_frame`,`unknown operation`,404);if(t.method!==`POST`)return c(`bad_frame`,`use POST`,405);if(Number(t.headers.get(`content-length`)??0)>262144)return c(`too_large`,`the body is too large`,413);let u=await t.text();if(u.length>262144)return c(`too_large`,`the body is too large`,413);let d;try{d=JSON.parse(u)}catch{return c(`bad_frame`,`the body is not JSON`,400)}let f=i(o,d);if(!f.ok)return c(f.code,f.message,l[f.code]??400);let p=f.request;try{switch(p.op){case`publish`:return s(await this.#t.publish(p.channel,p.entry,p.options,p.idleMs));case`history`:return s(await this.#t.history(p.channel,p.after,p.limit));case`presence-set`:return await this.#t.presenceSet(p.channel,p.member,p.ttlMs,p.graceMs),s({});case`presence-remove`:return s({removed:await this.#t.presenceRemove(p.channel,p.id)});case`presence-list`:return s({members:await this.#t.presenceList(p.channel)});case`presence-touch`:return s({missing:await this.#t.presenceTouch(p.channel,p.ids,p.ttlMs)});case`presence-reap`:return s({members:await this.#t.presenceReap(p.channel)})}}catch(t){return t instanceof e?c(t.code,t.message,l[t.code]??500):(console.error(`[realtime] channel object`,t),c(`unavailable`,`the channel object failed`,500))}}async#r(n,r){if(n.headers.get(`upgrade`)?.toLowerCase()!==`websocket`)return c(`bad_frame`,`expected a WebSocket upgrade`,426);let i=r.searchParams.get(`c`);if(!t(i))return c(`bad_channel`,`bad channel name`,400);let a=d(r);if(a===`bad`)return c(`bad_frame`,`bad after`,400);let{client:o,server:s}=this.#n.createPair();this.#e.acceptWebSocket(s);try{await this.#t.attach(s,i,a)}catch(t){try{s.close(1011,`attach failed`)}catch{}return t instanceof e?c(t.code,t.message,l[t.code]??500):(console.error(`[realtime] channel object`,t),c(`unavailable`,`the channel object failed`,500))}return this.#n.upgrade(o)}webSocketMessage(e,t){if(t===`ping`)try{e.send(`pong`)}catch{this.#t.detach(e)}}webSocketClose(e,t,n){this.#t.detach(e);try{e.close(u(t),n.slice(0,100))}catch{}}webSocketError(e){this.#t.detach(e);try{e.close(1011,`error`)}catch{}}async alarm(){await this.#t.alarm()}};export{f as RealtimeChannelObject};
|
|
2
2
|
//# sourceMappingURL=object.js.map
|
package/dist/prod/server/hub.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{RealtimeError as e}from"../errors.js";import{byteLength as t,decodeClientFrame as n,isChannelName as r,isEventName as i,isIdempotencyKey as a}from"../protocol.js";import{TokenBucket as o,WindowCounter as s,resolveLimits as c}from"./limits.js";import{memoryAdapter as l}from"../memory.js";import{compileRule as u,matchRule as d}from"./rules.js";function ee(){let e=crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16));return btoa(String.fromCharCode(...e)).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``)}function f(e){return JSON.stringify(e)}function p(e,t,n){return`{"t":"msg","c":${JSON.stringify(e)},"s":${t.seq},"e":${JSON.stringify(t.event)},"d":${t.data}`+(t.publisher===void 0?``:`,"p":${JSON.stringify(t.publisher)}`)+(t.user===void 0?``:`,"u":${JSON.stringify(t.user)}`)+(n?`,"r":1}`:`}`)}function m(m={}){let h=c(m.limits),g=m.now??Date.now,te=m.adapter===void 0,_=m.adapter??l({now:g,onError:(e,t)=>D(e,{where:`memory.${t.where}`,channel:t.channel})}),v=[],y=/* @__PURE__ */ new Map,b=/* @__PURE__ */ new Map,x=/* @__PURE__ */ new Set,S=/* @__PURE__ */ new Map,C,w=!1,T=!1,E={published:0,delivered:0,refused:0,slowConsumers:0},D=(e,t)=>{if(m.onError!==void 0)try{m.onError(e,t)}catch{}else console.error(`[realtime] ${t.where}`,e)};function O(e){for(let t of v){let n=d(t,e);if(n!==void 0)return{rule:t.rule,params:n}}}async function k(t,n,r,i,a){let o=t[n];if(o===void 0||o===!1)return!1;if(o===!0)return!0;try{return await o({channel:r,params:i,identity:a,op:n})===!0}catch(t){throw D(t,{where:`rule.${n}`,channel:r}),new e(`unavailable`)}}function A(e,t){if(!e.closed){try{if((e.transport.buffered?.()??0)>h.maxBufferedBytes){E.slowConsumers+=1,P(e,1013,``);return}if(e.transport.send(t))return}catch(t){D(t,{where:`transport.send`,connection:e.id}),j(e,`transport.close`,()=>e.transport.close(1011,``))}Z(e)}}function j(e,t,n){try{n()}catch(n){D(n,{where:t,connection:e.id})}}function M(e,t){A(e,f(t))}function N(e,t,n,r,i){E.refused+=1,M(e,{t:`err`,...t===void 0?{}:{n:t},...r===void 0?{}:{c:r},code:n,...i===void 0||i===``?{}:{msg:i}})}function P(e,t,n){e.closed||(j(e,`transport.send`,()=>e.transport.send(f({t:`bye`,code:t,why:n}))),j(e,`transport.close`,()=>e.transport.close(t,n)),Z(e))}function F(e){let t=b.get(e);return(t===void 0||t.closing)&&(t={name:e,subs:/* @__PURE__ */ new Set,bucket:new o(h.channelPublishesPerSecond,h.channelPublishesPerSecond,g())},b.set(e,t)),t}async function I(e,t,n){if(e[t]===void 0){let r=n();e[t]=r,r.catch(()=>{e[t]=void 0})}await e[t]}function L(e,t){for(let n of e.subs)n.active&&(n.pumping?n.buffer.length>=1e4?V(n,`gap`):n.buffer.push(t):t.seq===n.seq+1?R(n,t,!1):t.seq>n.seq+1?(n.buffer.push(t),z(n,void 0)):t.at>n.at&&V(n,`reset`))}function R(e,t,n){if(e.seq=t.seq,e.at=Math.max(e.at,t.at),!e.echo&&t.publisher===e.conn.id)return;if(E.delivered+=1,n){A(e.conn,p(e.channel.name,t,!0));return}let r=e.channel.frame;(r===void 0||r.seq!==t.seq)&&(r=e.channel.frame={seq:t.seq,text:p(e.channel.name,t,!1)}),A(e.conn,r.text)}async function z(e,t){if(!(e.pumping&&t===void 0)){e.pumping=!0;try{for(;e.active;){e.buffer=e.buffer.filter(t=>t.seq>e.seq);let n=e.buffer[0];if(n!==void 0&&n.seq===e.seq+1){R(e,n,!1),e.buffer.shift();continue}let r=n===void 0?t??0:n.seq-1;if(e.seq>=r)break;let i=await _.history(e.channel.name,e.seq,Math.min(500,r-e.seq));if(!e.active)return;let a=i.messages[0];if(a===void 0||a.seq!==e.seq+1){if(!e.gapOk){V(e,`gap`);return}e.seq=(a?.seq??r+1)-1}else for(let t of i.messages){if(t.seq>r)break;R(e,t,!0)}}}catch(t){D(t,{where:`pump`,channel:e.channel.name,connection:e.conn.id}),e.active&&V(e,`unavailable`)}finally{e.pumping=!1}e.active&&e.buffer.length>0&&z(e,void 0)}}function ne(e,t){for(let n of e.subs)n.joined&&t.op!==`leave`&&t.member.id===n.conn.id&&t.member.v>n.version&&(n.version=t.member.v,n.state=t.member.s),n.active&&(n.joined||n.watch)&&(n.queued===void 0?B(n,t):n.queued.push(t))}function B(e,t){M(e.conn,{t:`pres`,c:t.channel,op:t.op,m:t.member})}function V(e,t){e.active&&(e.active=!1,e.conn.subs.delete(e.channel.name),e.channel.subs.delete(e),t!==void 0&&!e.conn.closed&&M(e.conn,{t:`unsubbed`,c:e.channel.name,...t===``?{}:{why:t}}),U(e))}let H=/* @__PURE__ */ new Set;function U(e){let t=W(e);return H.add(t),t.finally(()=>H.delete(t)),t}async function W(e){let t=e.channel;try{await e.joining?.catch(()=>void 0),e.joined&&(e.joined=!1,await _.presenceRemove(t.name,e.conn.id))}catch(n){D(n,{where:`presenceRemove`,channel:t.name,connection:e.conn.id})}if(t.subs.size>0||t.closing)return;t.closing=!0,b.get(t.name)===t&&b.delete(t.name);let n=[t.feed,t.pfeed].filter(e=>e!==void 0);await Promise.all(n.map(async e=>{try{await(await e)()}catch(e){D(e,{where:`unsubscribe`,channel:t.name})}}))}function G(e){return e.identity.user===void 0?`unauthorized`:`forbidden`}async function K(n,r){let{c:i,n:a}=r;if(n.subs.has(i))return N(n,a,`conflict`,i);if(n.subs.size>=h.maxChannelsPerConnection)return N(n,a,`limit`,i);let o=O(i);if(o===void 0)return N(n,a,G(n),i);let s=r.hasPresence||r.watch===!0,c={conn:n,channel:i};x.add(c);try{if(!await k(o.rule,`subscribe`,i,o.params,n.identity)||s&&!await k(o.rule,`presence`,i,o.params,n.identity)||!x.has(c))return N(n,a,G(n),i)}finally{x.delete(c)}if(r.hasPresence&&t(JSON.stringify(r.presence??null))>h.maxStateBytes)return N(n,a,`too_large`,i);if(n.closed)return;let l=F(i);if(l.subs.size>=h.maxSubscribersPerChannel)return N(n,a,`limit`,i);let u={conn:n,channel:l,...o,echo:r.echo!==!1,watch:r.watch===!0,version:0,state:null,seq:0,at:g(),pumping:!0,buffer:[],queued:s?[]:void 0,active:!0};n.subs.set(i,u),l.subs.add(u);try{await I(l,`feed`,()=>_.subscribe(l.name,e=>L(l,e))),s&&(await I(l,`pfeed`,()=>_.presenceSubscribe(l.name,e=>ne(l,e))),Q());let e=await _.history(i,2**53-1,0);if(!u.active)return;let t=e.head,o=r.after,c=!1,d=!1;if(o===void 0?u.seq=t:o>t?(d=!0,u.seq=t):(u.seq=o,c=e.oldest===0?t>o:e.oldest>o+1,u.gapOk=c),M(n,{t:`subbed`,n:a,c:i,head:t,...c?{gap:!0}:{},...d?{reset:!0}:{}}),r.hasPresence&&(u.joining=re(u,r.presence??null),await u.joining),s&&await ie(u),!u.active)return;await z(u,t),u.gapOk=!1}catch(t){u.active&&(V(u),D(t,{where:`subscribe`,channel:i,connection:n.id}),N(n,a,t instanceof e?t.code:`unavailable`,i))}}function re(t,n){let r=t.channel.name,i=(S.get(r)??Promise.resolve()).then(async()=>{if(t.active){if((await _.presenceList(r)).length>=h.maxPresenceMembers)throw new e(`limit`);t.version=1,t.state=n,t.joined=!0,Q(),await _.presenceSet(r,q(t),h.presenceTtlMs)}}),a=()=>{S.get(r)===o&&S.delete(r)},o=i.then(a,a);return S.set(r,o),i}function q(e){let{user:t}=e.conn.identity;return{id:e.conn.id,...t===void 0?{}:{u:t},s:e.state,v:e.version}}async function ie(e){let t=await _.presenceList(e.channel.name);if(!e.active)return;M(e.conn,{t:`pres`,c:e.channel.name,op:`sync`,m:t});let n=e.queued??[];e.queued=void 0;let r=new Map(t.map(e=>[e.id,e.v]));for(let t of n){let n=r.get(t.member.id);(t.op===`leave`?n===void 0:n!==void 0&&n>=t.member.v)||B(e,t)}}async function ae(e,n){let r=e.subs.get(n.c);if(r===void 0||!r.joined)return N(e,void 0,`not_subscribed`,n.c);if(t(JSON.stringify(n.s))>h.maxStateBytes)return N(e,void 0,`too_large`,n.c);if(!e.publishBucket.take(g()))return J(e,void 0,n.c);r.version+=1,r.state=n.s;try{await _.presenceSet(n.c,q(r),h.presenceTtlMs)}catch(t){D(t,{where:`presenceSet`,channel:n.c,connection:e.id}),N(e,void 0,`unavailable`,n.c)}}function J(e,t,n){N(e,t,`rate_limited`,n),e.limited.hit(g())&&P(e,1008,``)}async function oe(e,t){let{c:n,n:r}=t,i=O(n);if(i===void 0||!await k(i.rule,`publish`,n,i.params,e.identity))return N(e,r,G(e),n);if(t.d.length>h.maxMessageBytes)return N(e,r,`too_large`,n);if(!e.publishBucket.take(g())||b.get(n)?.bucket.take(g())===!1)return J(e,r,n);try{let a=await _.publish(n,{event:t.e,data:t.d,publisher:e.id,...e.identity.user===void 0?{}:{user:e.identity.user}},{history:i.rule.history??h.history,historyTtlMs:h.historyTtlMs,idempotencyTtlMs:h.idempotencyTtlMs,...t.k===void 0?{}:{key:`${t.k}/${e.identity.user===void 0?`a`:`u:${e.identity.user}`}`}});E.published+=+!a.duplicate,r!==void 0&&M(e,{t:`ack`,n:r,s:a.seq,...a.duplicate?{dup:!0}:{}})}catch(t){D(t,{where:`publish`,channel:n,connection:e.id}),N(e,r,`unavailable`,n)}}async function se(n,r){let{c:i,n:a}=r,o=n.subs.get(i);if(o===void 0&&n.stateless){let e=O(i);e!==void 0&&await k(e.rule,`subscribe`,i,e.params,n.identity)&&(o=e)}if(o===void 0)return N(n,a,G(n)===`unauthorized`&&n.stateless?`unauthorized`:`not_subscribed`,i);let s=o,c=s.rule.handlers?.[r.e];if(c===void 0)return N(n,a,`no_handler`,i);if(n.pending.size>=h.maxPendingRequests)return N(n,a,`limit`,i);if(!n.publishBucket.take(g()))return J(n,a,i);let l=new AbortController;n.pending.add(l);let u=Math.min(r.ms??h.requestTimeoutMs,h.requestTimeoutMs),d=setTimeout(()=>l.abort(new e(`timeout`)),u);(async()=>{try{let e=Promise.resolve(c({channel:i,params:s.params,identity:n.identity,event:r.e,data:r.d,signal:l.signal})),o=new Promise((e,t)=>{l.signal.addEventListener(`abort`,()=>t(l.signal.reason),{once:!0})}),u=await Promise.race([e,o])??null,d=JSON.stringify(u);if(d===void 0||t(d)>h.maxMessageBytes)return N(n,a,`too_large`,i);A(n,`{"t":"rep","n":${a},"d":${d}}`)}catch(t){t instanceof e?N(n,a,t.code,i,t.code===`timeout`||t.code===`closed`?void 0:t.message):(D(t,{where:`request`,channel:i,connection:n.id}),N(n,a,`handler_error`,i))}finally{clearTimeout(d),n.pending.delete(l),X(n)}})()}function Y(e,t,n,r){N(e,t,n,void 0,r),e.invalid.hit(g())&&P(e,1008,``)}async function ce(t,r){let i=n(r,h.maxMessageBytes);if(!i.ok){Y(t,i.n,i.code,i.why);return}let a=i.frame,o=`n`in a?a.n:void 0;if((a.t===`sub`||a.t===`unsub`)&&!t.ctl.take(g())&&a.t===`sub`)return J(t,o,a.c);try{switch(a.t){case`hello`:return;case`ping`:M(t,{t:`pong`,...a.n===void 0?{}:{n:a.n}});return;case`sub`:return await K(t,a);case`unsub`:{let e=t.subs.get(a.c);if(e===void 0)return N(t,a.n,`not_subscribed`,a.c);V(e);return}case`pub`:return await oe(t,a);case`req`:return await se(t,a);case`pres`:return await ae(t,a)}}catch(n){let r=`c`in a?a.c:void 0;n instanceof e?N(t,o,n.code,r):(D(n,{where:`frame`,connection:t.id,...r===void 0?{}:{channel:r}}),N(t,o,`unavailable`,r))}}async function le(t){if(!t.running){t.running=!0;try{for(let n=t.queue.shift();n!==void 0&&!t.closed;n=t.queue.shift())try{await ce(t,n)}catch(n){n instanceof e?N(t,void 0,n.code):(D(n,{where:`frame`,connection:t.id}),N(t,void 0,`unavailable`))}}finally{t.running=!1,X(t)}}}function X(e){if(!(e.running||e.queue.length>0||e.pending.size>0))for(let t of e.waiters.splice(0))t()}async function Z(t){if(t.closed)return t.done;t.closed=!0,y.get(t.id)===t&&y.delete(t.id);for(let n of t.pending)n.abort(new e(`closed`));t.pending.clear(),t.queue.length=0;for(let e of t.waiters.splice(0))e();let n=[...t.subs.values()];for(let e of n)e.active=!1,e.channel.subs.delete(e);t.subs.clear(),await Promise.allSettled(n.map(e=>U(e))),t.release?.()}function Q(){if(C!==void 0||w)return;let e=setInterval(()=>void ue(),h.presenceHeartbeatMs);e.unref?.(),C=e}async function ue(){if(!T){T=!0;try{let e=[],t=/* @__PURE__ */ new Set;for(let n of y.values())for(let r of n.subs.values())r.joined&&e.push({channel:r.channel.name,id:n.id}),(r.joined||r.watch)&&t.add(r.channel.name);if(e.length===0&&t.size===0){clearInterval(C),C=void 0;return}try{let n=await _.presenceTouch(e,h.presenceTtlMs);for(let e of n){let t=y.get(e.id)?.subs.get(e.channel);t!==void 0&&t.joined&&await _.presenceSet(e.channel,q(t),h.presenceTtlMs)}for(let e of t)await _.presenceReap(e)}catch(e){D(e,{where:`presenceHeartbeat`})}}finally{T=!1}}}for(let[e,t]of Object.entries(m.channels??{}))v.push(u(e,t));let $={limits:h,adapter:_,report:D,match:O,allowed:k,channel(e,t){return v.push(u(e,t)),$},connect(n,r={},i={}){if(w)throw new e(`closed`);let a,c=new Promise(e=>{a=e}),l=i.actingFor===void 0?void 0:y.get(i.actingFor),u=l!==void 0&&r.user!==void 0&&l.identity.user===r.user,d={id:u?i.actingFor:ee(),identity:r,transport:n,subs:/* @__PURE__ */ new Map,queue:[],publishBucket:i.publishBucket??new o(h.publishesPerSecond,h.publishBurst,g()),ctl:new o(h.publishesPerSecond,Math.max(h.publishBurst,h.maxChannelsPerConnection),g()),stateless:i.stateless===!0,waiters:[],invalid:new s(10,6e4),limited:new s(50,1e4),pending:/* @__PURE__ */ new Set,release:a,done:c};return u||y.set(d.id,d),M(d,{t:`ready`,v:1,id:d.id,hb:h.heartbeatMs,max:h.maxMessageBytes}),{id:d.id,identity:r,done:c,receive(e){d.closed||(t(e)>h.maxMessageBytes?Y(d,void 0,`too_large`):d.queue.length>=h.maxQueuedFrames?P(d,1013,``):(d.queue.push(e),le(d)))},closed(){Z(d)},settled(){return new Promise(e=>{d.waiters.push(e),X(d)})}}},async publish(n,o,s,c={}){if(!r(n))throw new e(`bad_channel`);if(!i(o)||c.key!==void 0&&!a(c.key))throw new e(`bad_frame`);let l;try{l=JSON.stringify(s===void 0?null:s)}catch(e){throw TypeError(`REALTIME_DATA`,{cause:e})}if(l===void 0)throw TypeError(`REALTIME_DATA`);if(t(l)>h.maxMessageBytes)throw new e(`too_large`);let u=O(n),d=await _.publish(n,{event:o,data:l},{history:u?.rule.history??h.history,historyTtlMs:h.historyTtlMs,idempotencyTtlMs:h.idempotencyTtlMs,...c.key===void 0?{}:{key:`${c.key}/s`}});return E.published+=+!d.duplicate,d},async setMemberState(n,r,i,a){if(n.user===void 0)throw new e(`unauthorized`);let o=O(r);if(o===void 0||!await k(o.rule,`presence`,r,o.params,n))throw new e(`forbidden`);if(t(JSON.stringify(a))>h.maxStateBytes)throw new e(`too_large`);let s=(await _.presenceList(r)).find(e=>e.id===i);if(s===void 0||s.u!==n.user)throw new e(`forbidden`);let c=y.get(i)?.subs.get(r),l=Math.max(s.v,c?.version??0)+1;c?.joined===!0&&(c.version=l,c.state=a),await _.presenceSet(r,{id:s.id,u:s.u,s:a,v:l},h.presenceTtlMs)},revoke(e){if(e.user===void 0&&e.connection===void 0&&e.channel===void 0)throw TypeError(`REALTIME_REVOKE`);let t=(t,n)=>(e.connection===void 0||t.id===e.connection)&&(e.user===void 0||t.identity.user===e.user)&&(e.channel===void 0||n===e.channel),n=0;for(let e of x)t(e.conn,e.channel)&&(x.delete(e),n+=1);for(let e of[...y.values()])for(let r of[...e.subs.values()])t(e,r.channel.name)&&(V(r,`revoked`),n+=1);return n},stats(){let e=0;for(let t of y.values())e+=t.subs.size;return{connections:y.size,channels:b.size,subscriptions:e,...E}},async close(){if(w)return;w=!0,C!==void 0&&clearInterval(C),C=void 0;let e=[...y.values()];for(let t of e)P(t,1001,``);await Promise.all(e.map(e=>e.done)),await Promise.allSettled([...H]),te&&await _.close()}};return $}export{m as createHubCore};
|
|
1
|
+
import{RealtimeError as e}from"../errors.js";import{memoryAdapter as t}from"../memory.js";import{byteLength as n,decodeClientFrame as r,isChannelName as i,isEventName as a,isIdempotencyKey as o}from"../protocol.js";import{TokenBucket as s,WindowCounter as c,resolveLimits as l}from"./limits.js";import{compileRule as u,matchRule as d}from"./rules.js";function ee(){let e=crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16));return btoa(String.fromCharCode(...e)).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``)}function f(e){return JSON.stringify(e)}function p(e,t,n){return`{"t":"msg","c":${JSON.stringify(e)},"s":${t.seq},"e":${JSON.stringify(t.event)},"d":${t.data}`+(t.publisher===void 0?``:`,"p":${JSON.stringify(t.publisher)}`)+(t.user===void 0?``:`,"u":${JSON.stringify(t.user)}`)+(n?`,"r":1}`:`}`)}function m(m={}){let h=l(m.limits),g=m.now??Date.now,te=m.adapter===void 0,_=m.adapter??t({now:g,onError:(e,t)=>D(e,{where:`memory.${t.where}`,channel:t.channel})}),v=[],y=/* @__PURE__ */ new Map,b=/* @__PURE__ */ new Map,x=/* @__PURE__ */ new Set,S=/* @__PURE__ */ new Map,C,w=!1,T=!1,E={published:0,delivered:0,refused:0,slowConsumers:0},D=(e,t)=>{if(m.onError!==void 0)try{m.onError(e,t)}catch{}else console.error(`[realtime] ${t.where}`,e)};function O(e){for(let t of v){let n=d(t,e);if(n!==void 0)return{rule:t.rule,params:n}}}async function k(t,n,r,i,a){let o=t[n];if(o===void 0||o===!1)return!1;if(o===!0)return!0;try{return await o({channel:r,params:i,identity:a,op:n})===!0}catch(t){throw D(t,{where:`rule.${n}`,channel:r}),new e(`unavailable`)}}function A(e,t){if(!e.closed){try{if((e.transport.buffered?.()??0)>h.maxBufferedBytes){E.slowConsumers+=1,P(e,1013,``);return}if(e.transport.send(t))return}catch(t){D(t,{where:`transport.send`,connection:e.id}),j(e,`transport.close`,()=>e.transport.close(1011,``))}Z(e)}}function j(e,t,n){try{n()}catch(n){D(n,{where:t,connection:e.id})}}function M(e,t){A(e,f(t))}function N(e,t,n,r,i){E.refused+=1,M(e,{t:`err`,...t===void 0?{}:{n:t},...r===void 0?{}:{c:r},code:n,...i===void 0||i===``?{}:{msg:i}})}function P(e,t,n){e.closed||(j(e,`transport.send`,()=>e.transport.send(f({t:`bye`,code:t,why:n}))),j(e,`transport.close`,()=>e.transport.close(t,n)),Z(e))}function F(e){let t=b.get(e);return(t===void 0||t.closing)&&(t={name:e,subs:/* @__PURE__ */ new Set,bucket:new s(h.channelPublishesPerSecond,h.channelPublishesPerSecond,g())},b.set(e,t)),t}async function I(e,t,n){if(e[t]===void 0){let r=n();e[t]=r,r.catch(()=>{e[t]=void 0})}await e[t]}function L(e,t){for(let n of e.subs)n.active&&(n.pumping?n.buffer.length>=1e4?V(n,`gap`):n.buffer.push(t):t.seq===n.seq+1?R(n,t,!1):t.seq>n.seq+1?(n.buffer.push(t),z(n,void 0)):t.at>n.at&&V(n,`reset`))}function R(e,t,n){if(e.seq=t.seq,e.at=Math.max(e.at,t.at),!e.echo&&t.publisher===e.conn.id)return;if(E.delivered+=1,n){A(e.conn,p(e.channel.name,t,!0));return}let r=e.channel.frame;(r===void 0||r.seq!==t.seq)&&(r=e.channel.frame={seq:t.seq,text:p(e.channel.name,t,!1)}),A(e.conn,r.text)}async function z(e,t){if(!(e.pumping&&t===void 0)){e.pumping=!0;try{for(;e.active;){e.buffer=e.buffer.filter(t=>t.seq>e.seq);let n=e.buffer[0];if(n!==void 0&&n.seq===e.seq+1){R(e,n,!1),e.buffer.shift();continue}let r=n===void 0?t??0:n.seq-1;if(e.seq>=r)break;let i=await _.history(e.channel.name,e.seq,Math.min(500,r-e.seq));if(!e.active)return;let a=i.messages[0];if(a===void 0||a.seq!==e.seq+1){if(!e.gapOk){V(e,`gap`);return}e.seq=(a?.seq??r+1)-1}else for(let t of i.messages){if(t.seq>r)break;R(e,t,!0)}}}catch(t){D(t,{where:`pump`,channel:e.channel.name,connection:e.conn.id}),e.active&&V(e,`unavailable`)}finally{e.pumping=!1}e.active&&e.buffer.length>0&&z(e,void 0)}}function ne(e,t){for(let n of e.subs)n.joined&&t.op!==`leave`&&t.member.id===n.conn.id&&t.member.v>n.version&&(n.version=t.member.v,n.state=t.member.s),n.active&&(n.joined||n.watch)&&(n.queued===void 0?B(n,t):n.queued.push(t))}function B(e,t){M(e.conn,{t:`pres`,c:t.channel,op:t.op,m:t.member})}function V(e,t){e.active&&(e.active=!1,e.conn.subs.delete(e.channel.name),e.channel.subs.delete(e),t!==void 0&&!e.conn.closed&&M(e.conn,{t:`unsubbed`,c:e.channel.name,...t===``?{}:{why:t}}),U(e))}let H=/* @__PURE__ */ new Set;function U(e){let t=W(e);return H.add(t),t.finally(()=>H.delete(t)),t}async function W(e){let t=e.channel;try{await e.joining?.catch(()=>void 0),e.joined&&(e.joined=!1,await _.presenceRemove(t.name,e.conn.id))}catch(n){D(n,{where:`presenceRemove`,channel:t.name,connection:e.conn.id})}if(t.subs.size>0||t.closing)return;t.closing=!0,b.get(t.name)===t&&b.delete(t.name);let n=[t.feed,t.pfeed].filter(e=>e!==void 0);await Promise.all(n.map(async e=>{try{await(await e)()}catch(e){D(e,{where:`unsubscribe`,channel:t.name})}}))}function G(e){return e.identity.user===void 0?`unauthorized`:`forbidden`}async function K(t,r){let{c:i,n:a}=r;if(t.subs.has(i))return N(t,a,`conflict`,i);if(t.subs.size>=h.maxChannelsPerConnection)return N(t,a,`limit`,i);let o=O(i);if(o===void 0)return N(t,a,G(t),i);let s=r.hasPresence||r.watch===!0,c={conn:t,channel:i};x.add(c);try{if(!await k(o.rule,`subscribe`,i,o.params,t.identity)||s&&!await k(o.rule,`presence`,i,o.params,t.identity)||!x.has(c))return N(t,a,G(t),i)}finally{x.delete(c)}if(r.hasPresence&&n(JSON.stringify(r.presence??null))>h.maxStateBytes)return N(t,a,`too_large`,i);if(t.closed)return;let l=F(i);if(l.subs.size>=h.maxSubscribersPerChannel)return N(t,a,`limit`,i);let u={conn:t,channel:l,...o,echo:r.echo!==!1,watch:r.watch===!0,version:0,state:null,seq:0,at:g(),pumping:!0,buffer:[],queued:s?[]:void 0,active:!0};t.subs.set(i,u),l.subs.add(u);try{await I(l,`feed`,()=>_.subscribe(l.name,e=>L(l,e))),s&&(await I(l,`pfeed`,()=>_.presenceSubscribe(l.name,e=>ne(l,e))),Q());let e=await _.history(i,2**53-1,0);if(!u.active)return;let n=e.head,o=r.after,c=!1,d=!1;if(o===void 0?u.seq=n:o>n?(d=!0,u.seq=n):(u.seq=o,c=e.oldest===0?n>o:e.oldest>o+1,u.gapOk=c),M(t,{t:`subbed`,n:a,c:i,head:n,...c?{gap:!0}:{},...d?{reset:!0}:{}}),r.hasPresence&&(u.joining=re(u,r.presence??null),await u.joining),s&&await ie(u),!u.active)return;await z(u,n),u.gapOk=!1}catch(n){u.active&&(V(u),D(n,{where:`subscribe`,channel:i,connection:t.id}),N(t,a,n instanceof e?n.code:`unavailable`,i))}}function re(t,n){let r=t.channel.name,i=(S.get(r)??Promise.resolve()).then(async()=>{if(t.active){if((await _.presenceList(r)).length>=h.maxPresenceMembers)throw new e(`limit`);t.version=1,t.state=n,t.joined=!0,Q(),await _.presenceSet(r,q(t),h.presenceTtlMs)}}),a=()=>{S.get(r)===o&&S.delete(r)},o=i.then(a,a);return S.set(r,o),i}function q(e){let{user:t}=e.conn.identity;return{id:e.conn.id,...t===void 0?{}:{u:t},s:e.state,v:e.version}}async function ie(e){let t=await _.presenceList(e.channel.name);if(!e.active)return;M(e.conn,{t:`pres`,c:e.channel.name,op:`sync`,m:t});let n=e.queued??[];e.queued=void 0;let r=new Map(t.map(e=>[e.id,e.v]));for(let t of n){let n=r.get(t.member.id);(t.op===`leave`?n===void 0:n!==void 0&&n>=t.member.v)||B(e,t)}}async function ae(e,t){let r=e.subs.get(t.c);if(r===void 0||!r.joined)return N(e,void 0,`not_subscribed`,t.c);if(n(JSON.stringify(t.s))>h.maxStateBytes)return N(e,void 0,`too_large`,t.c);if(!e.publishBucket.take(g()))return J(e,void 0,t.c);r.version+=1,r.state=t.s;try{await _.presenceSet(t.c,q(r),h.presenceTtlMs)}catch(n){D(n,{where:`presenceSet`,channel:t.c,connection:e.id}),N(e,void 0,`unavailable`,t.c)}}function J(e,t,n){N(e,t,`rate_limited`,n),e.limited.hit(g())&&P(e,1008,``)}async function oe(e,t){let{c:n,n:r}=t,i=O(n);if(i===void 0||!await k(i.rule,`publish`,n,i.params,e.identity))return N(e,r,G(e),n);if(t.d.length>h.maxMessageBytes)return N(e,r,`too_large`,n);if(!e.publishBucket.take(g())||b.get(n)?.bucket.take(g())===!1)return J(e,r,n);try{let a=await _.publish(n,{event:t.e,data:t.d,publisher:e.id,...e.identity.user===void 0?{}:{user:e.identity.user}},{history:i.rule.history??h.history,historyTtlMs:h.historyTtlMs,idempotencyTtlMs:h.idempotencyTtlMs,...t.k===void 0?{}:{key:`${t.k}/${e.identity.user===void 0?`a`:`u:${e.identity.user}`}`}});E.published+=+!a.duplicate,r!==void 0&&M(e,{t:`ack`,n:r,s:a.seq,...a.duplicate?{dup:!0}:{}})}catch(t){D(t,{where:`publish`,channel:n,connection:e.id}),N(e,r,`unavailable`,n)}}async function se(t,r){let{c:i,n:a}=r,o=t.subs.get(i);if(o===void 0&&t.stateless){let e=O(i);e!==void 0&&await k(e.rule,`subscribe`,i,e.params,t.identity)&&(o=e)}if(o===void 0)return N(t,a,G(t)===`unauthorized`&&t.stateless?`unauthorized`:`not_subscribed`,i);let s=o,c=s.rule.handlers?.[r.e];if(c===void 0)return N(t,a,`no_handler`,i);if(t.pending.size>=h.maxPendingRequests)return N(t,a,`limit`,i);if(!t.publishBucket.take(g()))return J(t,a,i);let l=new AbortController;t.pending.add(l);let u=Math.min(r.ms??h.requestTimeoutMs,h.requestTimeoutMs),d=setTimeout(()=>l.abort(new e(`timeout`)),u);(async()=>{try{let e=Promise.resolve(c({channel:i,params:s.params,identity:t.identity,event:r.e,data:r.d,signal:l.signal})),o=new Promise((e,t)=>{l.signal.addEventListener(`abort`,()=>t(l.signal.reason),{once:!0})}),u=await Promise.race([e,o])??null,d=JSON.stringify(u);if(d===void 0||n(d)>h.maxMessageBytes)return N(t,a,`too_large`,i);A(t,`{"t":"rep","n":${a},"d":${d}}`)}catch(n){n instanceof e?N(t,a,n.code,i,n.code===`timeout`||n.code===`closed`?void 0:n.message):(D(n,{where:`request`,channel:i,connection:t.id}),N(t,a,`handler_error`,i))}finally{clearTimeout(d),t.pending.delete(l),X(t)}})()}function Y(e,t,n,r){N(e,t,n,void 0,r),e.invalid.hit(g())&&P(e,1008,``)}async function ce(t,n){let i=r(n,h.maxMessageBytes);if(!i.ok){Y(t,i.n,i.code,i.why);return}let a=i.frame,o=`n`in a?a.n:void 0;if((a.t===`sub`||a.t===`unsub`)&&!t.ctl.take(g())&&a.t===`sub`)return J(t,o,a.c);try{switch(a.t){case`hello`:return;case`ping`:M(t,{t:`pong`,...a.n===void 0?{}:{n:a.n}});return;case`sub`:return await K(t,a);case`unsub`:{let e=t.subs.get(a.c);if(e===void 0)return N(t,a.n,`not_subscribed`,a.c);V(e);return}case`pub`:return await oe(t,a);case`req`:return await se(t,a);case`pres`:return await ae(t,a)}}catch(n){let r=`c`in a?a.c:void 0;n instanceof e?N(t,o,n.code,r):(D(n,{where:`frame`,connection:t.id,...r===void 0?{}:{channel:r}}),N(t,o,`unavailable`,r))}}async function le(t){if(!t.running){t.running=!0;try{for(let n=t.queue.shift();n!==void 0&&!t.closed;n=t.queue.shift())try{await ce(t,n)}catch(n){n instanceof e?N(t,void 0,n.code):(D(n,{where:`frame`,connection:t.id}),N(t,void 0,`unavailable`))}}finally{t.running=!1,X(t)}}}function X(e){if(!(e.running||e.queue.length>0||e.pending.size>0))for(let t of e.waiters.splice(0))t()}async function Z(t){if(t.closed)return t.done;t.closed=!0,y.get(t.id)===t&&y.delete(t.id);for(let n of t.pending)n.abort(new e(`closed`));t.pending.clear(),t.queue.length=0;for(let e of t.waiters.splice(0))e();let n=[...t.subs.values()];for(let e of n)e.active=!1,e.channel.subs.delete(e);t.subs.clear(),await Promise.allSettled(n.map(e=>U(e))),t.release?.()}function Q(){if(C!==void 0||w)return;let e=setInterval(()=>void ue(),h.presenceHeartbeatMs);e.unref?.(),C=e}async function ue(){if(!T){T=!0;try{let e=[],t=/* @__PURE__ */ new Set;for(let n of y.values())for(let r of n.subs.values())r.joined&&e.push({channel:r.channel.name,id:n.id}),(r.joined||r.watch)&&t.add(r.channel.name);if(e.length===0&&t.size===0){clearInterval(C),C=void 0;return}try{let n=await _.presenceTouch(e,h.presenceTtlMs);for(let e of n){let t=y.get(e.id)?.subs.get(e.channel);t!==void 0&&t.joined&&await _.presenceSet(e.channel,q(t),h.presenceTtlMs)}for(let e of t)await _.presenceReap(e)}catch(e){D(e,{where:`presenceHeartbeat`})}}finally{T=!1}}}for(let[e,t]of Object.entries(m.channels??{}))v.push(u(e,t));let $={limits:h,adapter:_,report:D,match:O,allowed:k,channel(e,t){return v.push(u(e,t)),$},connect(t,r={},i={}){if(w)throw new e(`closed`);let a,o=new Promise(e=>{a=e}),l=i.actingFor===void 0?void 0:y.get(i.actingFor),u=l!==void 0&&r.user!==void 0&&l.identity.user===r.user,d={id:u?i.actingFor:ee(),identity:r,transport:t,subs:/* @__PURE__ */ new Map,queue:[],publishBucket:i.publishBucket??new s(h.publishesPerSecond,h.publishBurst,g()),ctl:new s(h.publishesPerSecond,Math.max(h.publishBurst,h.maxChannelsPerConnection),g()),stateless:i.stateless===!0,waiters:[],invalid:new c(10,6e4),limited:new c(50,1e4),pending:/* @__PURE__ */ new Set,release:a,done:o};return u||y.set(d.id,d),M(d,{t:`ready`,v:1,id:d.id,hb:h.heartbeatMs,max:h.maxMessageBytes}),{id:d.id,identity:r,done:o,receive(e){d.closed||(n(e)>h.maxMessageBytes?Y(d,void 0,`too_large`):d.queue.length>=h.maxQueuedFrames?P(d,1013,``):(d.queue.push(e),le(d)))},closed(){Z(d)},settled(){return new Promise(e=>{d.waiters.push(e),X(d)})}}},async publish(t,r,s,c={}){if(!i(t))throw new e(`bad_channel`);if(!a(r)||c.key!==void 0&&!o(c.key))throw new e(`bad_frame`);let l;try{l=JSON.stringify(s===void 0?null:s)}catch(e){throw TypeError(`REALTIME_DATA`,{cause:e})}if(l===void 0)throw TypeError(`REALTIME_DATA`);if(n(l)>h.maxMessageBytes)throw new e(`too_large`);let u=O(t),d=await _.publish(t,{event:r,data:l},{history:u?.rule.history??h.history,historyTtlMs:h.historyTtlMs,idempotencyTtlMs:h.idempotencyTtlMs,...c.key===void 0?{}:{key:`${c.key}/s`}});return E.published+=+!d.duplicate,d},async setMemberState(t,r,i,a){if(t.user===void 0)throw new e(`unauthorized`);let o=O(r);if(o===void 0||!await k(o.rule,`presence`,r,o.params,t))throw new e(`forbidden`);if(n(JSON.stringify(a))>h.maxStateBytes)throw new e(`too_large`);let s=(await _.presenceList(r)).find(e=>e.id===i);if(s===void 0||s.u!==t.user)throw new e(`forbidden`);let c=y.get(i)?.subs.get(r),l=Math.max(s.v,c?.version??0)+1;c?.joined===!0&&(c.version=l,c.state=a),await _.presenceSet(r,{id:s.id,u:s.u,s:a,v:l},h.presenceTtlMs)},revoke(e){if(e.user===void 0&&e.connection===void 0&&e.channel===void 0)throw TypeError(`REALTIME_REVOKE`);let t=(t,n)=>(e.connection===void 0||t.id===e.connection)&&(e.user===void 0||t.identity.user===e.user)&&(e.channel===void 0||n===e.channel),n=0;for(let e of x)t(e.conn,e.channel)&&(x.delete(e),n+=1);for(let e of[...y.values()])for(let r of[...e.subs.values()])t(e,r.channel.name)&&(V(r,`revoked`),n+=1);return n},stats(){let e=0;for(let t of y.values())e+=t.subs.size;return{connections:y.size,channels:b.size,subscriptions:e,...E}},async close(){if(w)return;w=!0,C!==void 0&&clearInterval(C),C=void 0;let e=[...y.values()];for(let t of e)P(t,1001,``);await Promise.all(e.map(e=>e.done)),await Promise.allSettled([...H]),te&&await _.close()}};return $}export{m as createHubCore};
|
|
2
2
|
//# sourceMappingURL=hub.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{postRoute as e,sseRoute as t,websocketRoute as n}from"./http.js";import{createHubCore as r}from"./hub.js";import{memoryAdapter as i}from"../memory.js";import{DEFAULT_HUB_LIMITS as a}from"./limits.js";function o(i={}){let a=r(i),o=Object.assign(a,{channel(e,t){return a.channel(e,t),o},websocket:e=>n(a,e),sse:e=>t(a,e),post:t=>e(a,t)});return o}export{a as DEFAULT_HUB_LIMITS,o as createHub,i as memoryAdapter};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shadow-js/realtime",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Typed channels, presence and resumable delivery over WebSocket or server-sent events",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -77,21 +77,21 @@
|
|
|
77
77
|
"url": "https://github.com/shadowjs-dev/realtime/issues"
|
|
78
78
|
},
|
|
79
79
|
"peerDependencies": {
|
|
80
|
-
"@shadow-js/core": "^1.0.
|
|
80
|
+
"@shadow-js/core": "^1.0.5"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
83
|
"@biomejs/biome": "2.5.15",
|
|
84
84
|
"@resvg/resvg-wasm": "2.6.2",
|
|
85
|
-
"@shadow-js/core": "^1.0.
|
|
86
|
-
"@shadow-js/dusk": "^1.0.
|
|
85
|
+
"@shadow-js/core": "^1.0.5",
|
|
86
|
+
"@shadow-js/dusk": "^1.0.5",
|
|
87
87
|
"@shadow-js/i18n": "*",
|
|
88
88
|
"@shadow-js/router": "*",
|
|
89
|
-
"@shadow-js/testing": "^1.0.
|
|
89
|
+
"@shadow-js/testing": "^1.0.5",
|
|
90
90
|
"@testing-library/dom": "10.4.2",
|
|
91
91
|
"@types/node": "^24.0.0",
|
|
92
92
|
"esbuild": "0.27.0",
|
|
93
93
|
"fast-check": "^4.10.2",
|
|
94
|
-
"happy-dom": "
|
|
94
|
+
"happy-dom": "21.0.4",
|
|
95
95
|
"satori": "0.43.0",
|
|
96
96
|
"sharp": "0.35.5",
|
|
97
97
|
"subset-font": "2.9.0",
|