@asteby/metacore-websocket 0.5.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/realtime.d.ts +106 -0
- package/dist/realtime.d.ts.map +1 -0
- package/dist/realtime.js +437 -0
- package/dist/realtime.js.map +1 -0
- package/dist/realtime.test.d.ts +2 -0
- package/dist/realtime.test.d.ts.map +1 -0
- package/dist/realtime.test.js +193 -0
- package/dist/realtime.test.js.map +1 -0
- package/dist/types.d.ts +43 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -78,3 +78,72 @@ function MyComponent() {
|
|
|
78
78
|
|
|
79
79
|
- `useWebSocket<TMessage>()` — returns `{ status, isConnected, lastMessage, lastEvent, send, disconnect, reconnect, subscribe }`.
|
|
80
80
|
- `useWebSocketMessage<TMessage>(type, handler)` — typed subscription; auto-unsubscribes on unmount.
|
|
81
|
+
|
|
82
|
+
## Realtime data events (`createRealtimeClient`)
|
|
83
|
+
|
|
84
|
+
The wire protocol behind `host.realtime` / `api.realtime` (contract:
|
|
85
|
+
`RealtimeAPI` in `@asteby/metacore-sdk`). The host bridges the kernel's
|
|
86
|
+
canonical CRUD events into lean, **org-scoped, value-free** `DATA_EVENT`
|
|
87
|
+
frames; addons subscribe by model and refetch what they show. Nothing flows
|
|
88
|
+
until somebody subscribes.
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
client → server {"type":"SUBSCRIBE","models":["SalesOrder","work_orders"]}
|
|
92
|
+
{"type":"UNSUBSCRIBE","models":["SalesOrder"]}
|
|
93
|
+
server → client {"type":"DATA_SUBSCRIBED","payload":{"models":[...]}}
|
|
94
|
+
{"type":"DATA_EVENT","payload":{org_id,addon,model,table,action,id,
|
|
95
|
+
stage_from?,stage_to?,actor_id?,at,fields?,coalesced?}}
|
|
96
|
+
{"type":"DATA_EVENTS_DROPPED","payload":{"dropped":N,"models":[...]}}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
A subscription token matches a frame when it equals (case-insensitively) the
|
|
100
|
+
model key, the table name or `addon.Model`; `"*"` matches everything. The
|
|
101
|
+
server coalesces bursts on the same `(model, id)` (~150 ms) and rate-limits
|
|
102
|
+
each connection; when frames are dropped the client synthesises one `resync`
|
|
103
|
+
event per subscribed model so consumers refetch instead of trusting a stream
|
|
104
|
+
with a hole in it. The same happens after a reconnect.
|
|
105
|
+
|
|
106
|
+
### Own socket (a standalone addon or a non-React host)
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import { createRealtimeClient } from '@asteby/metacore-websocket'
|
|
110
|
+
|
|
111
|
+
// GET /api/realtime/info → { enabled, ws_url, channels: ['org:<id>'], ... }
|
|
112
|
+
const info = await api.get('/realtime/info').then((r) => r.data.data)
|
|
113
|
+
|
|
114
|
+
const realtime = createRealtimeClient({
|
|
115
|
+
wsUrl: info.ws_url,
|
|
116
|
+
channel: info.channels[0],
|
|
117
|
+
getToken: () => useAuthStore.getState().auth.accessToken,
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
const off = realtime.subscribe({ models: ['SalesOrder'] }, (e) => {
|
|
121
|
+
if (e.action === 'resync' || e.id === currentId) refetch()
|
|
122
|
+
})
|
|
123
|
+
// later
|
|
124
|
+
off()
|
|
125
|
+
realtime.close()
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Host socket (what ops does — one socket for chat, presence, calls AND data)
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
import { createRealtimeClient, transportFromHostSocket } from '@asteby/metacore-websocket'
|
|
132
|
+
|
|
133
|
+
const realtime = createRealtimeClient({
|
|
134
|
+
transport: transportFromHostSocket({
|
|
135
|
+
subscribe: ws.subscribe, // (type, handler) => unsubscribe
|
|
136
|
+
send: (frame) => ws.sendMessage(JSON.stringify(frame)),
|
|
137
|
+
isConnected: () => ws.isConnected,
|
|
138
|
+
onConnected: (listener) => ws.onConnected(listener),
|
|
139
|
+
}),
|
|
140
|
+
})
|
|
141
|
+
// hand `realtime` to addons: <Mounted host={{ ..., realtime }} /> or api.realtime
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`RealtimeClient` also exposes `onStatus(listener)`, `subscribedModels()`,
|
|
145
|
+
`serverModels()` (last `DATA_SUBSCRIBED` ack) and `close()`. `subscribe` never
|
|
146
|
+
throws and may be called before the socket is open — the `SUBSCRIBE` frame is
|
|
147
|
+
sent on the next open. React consumers should use `useRealtime` /
|
|
148
|
+
`useRealtimeInvalidate` from `@asteby/metacore-runtime-react` instead of
|
|
149
|
+
calling the client directly.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
export { WebSocketProvider, WebSocketContext } from './provider';
|
|
2
2
|
export { useWebSocket, useWebSocketMessage } from './hooks';
|
|
3
3
|
export { WebSocketStatus } from './types';
|
|
4
|
-
export type { WebSocketMessage, WebSocketContextValue, WebSocketProviderProps, SendPayload, TokenGetter, } from './types';
|
|
4
|
+
export type { WebSocketMessage, WebSocketContextValue, WebSocketProviderProps, SendPayload, TokenGetter, CallKind, CallInvitePayload, CallJoinPayload, CallSignalPayload, CallEndPayload, CallSignalType, } from './types';
|
|
5
|
+
/** Well-known WebSocket message types for realtime A/V calls. */
|
|
6
|
+
export declare const CallMessageType: {
|
|
7
|
+
readonly Invite: "CALL_INVITE";
|
|
8
|
+
readonly Join: "CALL_JOIN";
|
|
9
|
+
readonly Signal: "CALL_SIGNAL";
|
|
10
|
+
readonly End: "CALL_END";
|
|
11
|
+
};
|
|
12
|
+
export type CallMessageType = (typeof CallMessageType)[keyof typeof CallMessageType];
|
|
13
|
+
export { createRealtimeClient, createSocketTransport, transportFromHostSocket, } from './realtime';
|
|
14
|
+
export type { RealtimeClient, RealtimeClientOptions, RealtimeTransport, RealtimeFrame, HostSocketLike, } from './realtime';
|
|
5
15
|
export { createChannelClient } from './channels';
|
|
6
16
|
export type { ChannelClient, ChannelClientOptions, ChannelMessageHandler, ChannelConnectionState, } from './channels';
|
|
7
17
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAChE,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,sBAAsB,EACtB,WAAW,EACX,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAChE,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,sBAAsB,EACtB,WAAW,EACX,WAAW,EACX,QAAQ,EACR,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,cAAc,GACf,MAAM,SAAS,CAAA;AAEhB,iEAAiE;AACjE,eAAO,MAAM,eAAe;;;;;CAKlB,CAAA;AAEV,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,OAAO,eAAe,CAAC,CAAA;AAIpF,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,YAAY,CAAA;AACnB,YAAY,EACV,cAAc,EACd,qBAAqB,EACrB,iBAAiB,EACjB,aAAa,EACb,cAAc,GACf,MAAM,YAAY,CAAA;AAInB,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAChD,YAAY,EACV,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,YAAY,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
export { WebSocketProvider, WebSocketContext } from './provider';
|
|
2
2
|
export { useWebSocket, useWebSocketMessage } from './hooks';
|
|
3
3
|
export { WebSocketStatus } from './types';
|
|
4
|
+
/** Well-known WebSocket message types for realtime A/V calls. */
|
|
5
|
+
export const CallMessageType = {
|
|
6
|
+
Invite: 'CALL_INVITE',
|
|
7
|
+
Join: 'CALL_JOIN',
|
|
8
|
+
Signal: 'CALL_SIGNAL',
|
|
9
|
+
End: 'CALL_END',
|
|
10
|
+
};
|
|
11
|
+
// Realtime data-event client (org-scoped DATA_EVENT protocol behind
|
|
12
|
+
// `host.realtime` / `api.realtime` — see @asteby/metacore-sdk RealtimeAPI).
|
|
13
|
+
export { createRealtimeClient, createSocketTransport, transportFromHostSocket, } from './realtime';
|
|
4
14
|
// Multi-channel imperative client (one socket per logical channel, routed
|
|
5
15
|
// by query params). Complements the single-socket WebSocketProvider.
|
|
6
16
|
export { createChannelClient } from './channels';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAChE,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAChE,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAezC,iEAAiE;AACjE,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,MAAM,EAAE,aAAa;IACrB,IAAI,EAAE,WAAW;IACjB,MAAM,EAAE,aAAa;IACrB,GAAG,EAAE,UAAU;CACP,CAAA;AAIV,oEAAoE;AACpE,4EAA4E;AAC5E,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,YAAY,CAAA;AASnB,0EAA0E;AAC1E,qEAAqE;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Realtime data-event client — the wire protocol behind `host.realtime` /
|
|
3
|
+
* `api.realtime` (see `RealtimeAPI` in `@asteby/metacore-sdk`).
|
|
4
|
+
*
|
|
5
|
+
* Protocol (server = ops hub, `docs/REALTIME.md`):
|
|
6
|
+
*
|
|
7
|
+
* client → server {"type":"SUBSCRIBE","models":["SalesOrder","work_orders"]}
|
|
8
|
+
* {"type":"UNSUBSCRIBE","models":["SalesOrder"]}
|
|
9
|
+
* server → client {"type":"DATA_SUBSCRIBED","payload":{"models":[...]}}
|
|
10
|
+
* {"type":"DATA_EVENT","payload":{org_id,addon,model,table,action,id,...}}
|
|
11
|
+
* {"type":"DATA_EVENTS_DROPPED","payload":{"dropped":N,"models":[...]}}
|
|
12
|
+
*
|
|
13
|
+
* The client ref-counts models across every `subscribe()` call so the server
|
|
14
|
+
* only ever forwards what somebody on this page is listening to, re-sends the
|
|
15
|
+
* whole subscription set on every (re)open, and turns a reconnect or a
|
|
16
|
+
* DROPPED notice into synthetic `resync` events so consumers refetch instead
|
|
17
|
+
* of trusting a stream with a hole in it.
|
|
18
|
+
*
|
|
19
|
+
* Two ways to run it:
|
|
20
|
+
*
|
|
21
|
+
* 1. Own socket — `createRealtimeClient({ wsUrl, getToken, channel })`.
|
|
22
|
+
* Opens `wsUrl?channel=org:<id>&token=<jwt>` with reconnect/backoff.
|
|
23
|
+
* 2. Host socket — `createRealtimeClient({ transport })` where the host
|
|
24
|
+
* adapts its existing authenticated connection (`send`, `onFrame`,
|
|
25
|
+
* `isOpen`, `onOpen`). This is what ops does: one socket for chat,
|
|
26
|
+
* presence, calls AND data events.
|
|
27
|
+
*/
|
|
28
|
+
import type { DataEvent, RealtimeAPI, RealtimeStatus } from '@asteby/metacore-sdk';
|
|
29
|
+
/** Minimal frame shape the client reads off the transport. */
|
|
30
|
+
export interface RealtimeFrame {
|
|
31
|
+
type: string;
|
|
32
|
+
payload?: unknown;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Transport adapter for hosts that already own an authenticated socket. Every
|
|
36
|
+
* method must be safe to call at any time (before open, after close).
|
|
37
|
+
*/
|
|
38
|
+
export interface RealtimeTransport {
|
|
39
|
+
/** Send a JSON-serialisable frame. Return false when the socket is not open. */
|
|
40
|
+
send(frame: unknown): boolean;
|
|
41
|
+
/** Receive every parsed frame. Returns an unsubscribe function. */
|
|
42
|
+
onFrame(handler: (frame: RealtimeFrame) => void): () => void;
|
|
43
|
+
/** True while the socket is OPEN. */
|
|
44
|
+
isOpen(): boolean;
|
|
45
|
+
/** Fires after every successful (re)open. Returns an unsubscribe function. */
|
|
46
|
+
onOpen(handler: () => void): () => void;
|
|
47
|
+
/** Optional status feed — when absent the client derives it from isOpen/onOpen. */
|
|
48
|
+
onStatus?(handler: (status: RealtimeStatus) => void): () => void;
|
|
49
|
+
status?(): RealtimeStatus;
|
|
50
|
+
/** Optional teardown (own-socket transports close the socket). */
|
|
51
|
+
close?(): void;
|
|
52
|
+
}
|
|
53
|
+
export interface RealtimeClientOptions {
|
|
54
|
+
/** Base WS URL (e.g. `wss://panel.example.com/ws`) — own-socket mode. */
|
|
55
|
+
wsUrl?: string;
|
|
56
|
+
/** JWT getter — own-socket mode. `null` postpones the connection. */
|
|
57
|
+
getToken?: () => string | null | undefined;
|
|
58
|
+
/** Channel to join, e.g. `org:<org_id>` (from `GET /api/realtime/info`). */
|
|
59
|
+
channel?: string;
|
|
60
|
+
/** Reuse the host's socket instead of opening one. Wins over `wsUrl`. */
|
|
61
|
+
transport?: RealtimeTransport;
|
|
62
|
+
/** Base reconnect delay in ms (own-socket mode). Default 2000. */
|
|
63
|
+
reconnectInterval?: number;
|
|
64
|
+
/** Max reconnect attempts (own-socket mode). Default Infinity. */
|
|
65
|
+
maxReconnectAttempts?: number;
|
|
66
|
+
/** Tap every delivered event (diagnostics). */
|
|
67
|
+
onEvent?: (event: DataEvent) => void;
|
|
68
|
+
/** Diagnostics logger. Silent by default. */
|
|
69
|
+
log?: (msg: string, data?: unknown) => void;
|
|
70
|
+
}
|
|
71
|
+
export interface RealtimeClient extends RealtimeAPI {
|
|
72
|
+
/** Observe status changes. Returns an unsubscribe function. */
|
|
73
|
+
onStatus(listener: (status: RealtimeStatus) => void): () => void;
|
|
74
|
+
/** Models currently ref-counted on this page (normalised, sorted). */
|
|
75
|
+
subscribedModels(): string[];
|
|
76
|
+
/** Models the server last acknowledged (`DATA_SUBSCRIBED`). */
|
|
77
|
+
serverModels(): string[];
|
|
78
|
+
/** Drop every subscription and, in own-socket mode, close the socket. */
|
|
79
|
+
close(): void;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Build a realtime client. Never throws from `subscribe`; a transport that is
|
|
83
|
+
* not open yet simply defers the SUBSCRIBE frame to the next open.
|
|
84
|
+
*/
|
|
85
|
+
export declare function createRealtimeClient(options: RealtimeClientOptions): RealtimeClient;
|
|
86
|
+
/**
|
|
87
|
+
* Own-socket transport: native WebSocket on `wsUrl?channel=…&token=…` with
|
|
88
|
+
* exponential backoff (capped at 30 s). Exported so hosts can wrap it or
|
|
89
|
+
* reuse it for other channels.
|
|
90
|
+
*/
|
|
91
|
+
export declare function createSocketTransport(options: RealtimeClientOptions): RealtimeTransport;
|
|
92
|
+
/**
|
|
93
|
+
* Adapter for hosts whose socket exposes `subscribe(type, handler)` +
|
|
94
|
+
* `send(payload)` + a live `isConnected` flag (the ops `WebSocketProvider`
|
|
95
|
+
* shape). `onConnected` must invoke its callback with the current flag AND on
|
|
96
|
+
* every change — React hosts typically wire it to an effect on `isConnected`.
|
|
97
|
+
*/
|
|
98
|
+
export interface HostSocketLike {
|
|
99
|
+
subscribe: (type: string, handler: (frame: RealtimeFrame) => void) => () => void;
|
|
100
|
+
send: (payload: unknown) => void;
|
|
101
|
+
isConnected: () => boolean;
|
|
102
|
+
/** Register a listener for connection-state changes. */
|
|
103
|
+
onConnected: (listener: (connected: boolean) => void) => () => void;
|
|
104
|
+
}
|
|
105
|
+
export declare function transportFromHostSocket(host: HostSocketLike): RealtimeTransport;
|
|
106
|
+
//# sourceMappingURL=realtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime.d.ts","sourceRoot":"","sources":["../src/realtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EACV,SAAS,EAET,WAAW,EACX,cAAc,EAEf,MAAM,sBAAsB,CAAA;AAG7B,8DAA8D;AAC9D,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,gFAAgF;IAChF,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAA;IAC7B,mEAAmE;IACnE,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,MAAM,IAAI,CAAA;IAC5D,qCAAqC;IACrC,MAAM,IAAI,OAAO,CAAA;IACjB,8EAA8E;IAC9E,MAAM,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IACvC,mFAAmF;IACnF,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,GAAG,MAAM,IAAI,CAAA;IAChE,MAAM,CAAC,IAAI,cAAc,CAAA;IACzB,kEAAkE;IAClE,KAAK,CAAC,IAAI,IAAI,CAAA;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAC1C,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,yEAAyE;IACzE,SAAS,CAAC,EAAE,iBAAiB,CAAA;IAC7B,kEAAkE;IAClE,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,kEAAkE;IAClE,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,+CAA+C;IAC/C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAA;IACpC,6CAA6C;IAC7C,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;CAC5C;AAED,MAAM,WAAW,cAAe,SAAQ,WAAW;IACjD,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,GAAG,MAAM,IAAI,CAAA;IAChE,sEAAsE;IACtE,gBAAgB,IAAI,MAAM,EAAE,CAAA;IAC5B,+DAA+D;IAC/D,YAAY,IAAI,MAAM,EAAE,CAAA;IACxB,yEAAyE;IACzE,KAAK,IAAI,IAAI,CAAA;CACd;AAeD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,cAAc,CA+KnF;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,iBAAiB,CAmKvF;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,KAAK,MAAM,IAAI,CAAA;IAChF,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAChC,WAAW,EAAE,MAAM,OAAO,CAAA;IAC1B,wDAAwD;IACxD,WAAW,EAAE,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,KAAK,MAAM,IAAI,CAAA;CACpE;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,cAAc,GAAG,iBAAiB,CAiC/E"}
|
package/dist/realtime.js
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Realtime data-event client — the wire protocol behind `host.realtime` /
|
|
3
|
+
* `api.realtime` (see `RealtimeAPI` in `@asteby/metacore-sdk`).
|
|
4
|
+
*
|
|
5
|
+
* Protocol (server = ops hub, `docs/REALTIME.md`):
|
|
6
|
+
*
|
|
7
|
+
* client → server {"type":"SUBSCRIBE","models":["SalesOrder","work_orders"]}
|
|
8
|
+
* {"type":"UNSUBSCRIBE","models":["SalesOrder"]}
|
|
9
|
+
* server → client {"type":"DATA_SUBSCRIBED","payload":{"models":[...]}}
|
|
10
|
+
* {"type":"DATA_EVENT","payload":{org_id,addon,model,table,action,id,...}}
|
|
11
|
+
* {"type":"DATA_EVENTS_DROPPED","payload":{"dropped":N,"models":[...]}}
|
|
12
|
+
*
|
|
13
|
+
* The client ref-counts models across every `subscribe()` call so the server
|
|
14
|
+
* only ever forwards what somebody on this page is listening to, re-sends the
|
|
15
|
+
* whole subscription set on every (re)open, and turns a reconnect or a
|
|
16
|
+
* DROPPED notice into synthetic `resync` events so consumers refetch instead
|
|
17
|
+
* of trusting a stream with a hole in it.
|
|
18
|
+
*
|
|
19
|
+
* Two ways to run it:
|
|
20
|
+
*
|
|
21
|
+
* 1. Own socket — `createRealtimeClient({ wsUrl, getToken, channel })`.
|
|
22
|
+
* Opens `wsUrl?channel=org:<id>&token=<jwt>` with reconnect/backoff.
|
|
23
|
+
* 2. Host socket — `createRealtimeClient({ transport })` where the host
|
|
24
|
+
* adapts its existing authenticated connection (`send`, `onFrame`,
|
|
25
|
+
* `isOpen`, `onOpen`). This is what ops does: one socket for chat,
|
|
26
|
+
* presence, calls AND data events.
|
|
27
|
+
*/
|
|
28
|
+
import { dataEventMatches } from '@asteby/metacore-sdk';
|
|
29
|
+
const DATA_EVENT = 'DATA_EVENT';
|
|
30
|
+
const DATA_SUBSCRIBED = 'DATA_SUBSCRIBED';
|
|
31
|
+
const DATA_EVENTS_DROPPED = 'DATA_EVENTS_DROPPED';
|
|
32
|
+
function normaliseModel(raw) {
|
|
33
|
+
return raw.trim().toLowerCase();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Build a realtime client. Never throws from `subscribe`; a transport that is
|
|
37
|
+
* not open yet simply defers the SUBSCRIBE frame to the next open.
|
|
38
|
+
*/
|
|
39
|
+
export function createRealtimeClient(options) {
|
|
40
|
+
const log = options.log ?? (() => { });
|
|
41
|
+
const transport = options.transport ?? createSocketTransport(options);
|
|
42
|
+
const subscriptions = new Set();
|
|
43
|
+
const modelCounts = new Map();
|
|
44
|
+
let acked = [];
|
|
45
|
+
let everOpened = false;
|
|
46
|
+
let closed = false;
|
|
47
|
+
const statusListeners = new Set();
|
|
48
|
+
const currentStatus = () => {
|
|
49
|
+
if (transport.status)
|
|
50
|
+
return transport.status();
|
|
51
|
+
return transport.isOpen() ? 'open' : closed ? 'closed' : 'connecting';
|
|
52
|
+
};
|
|
53
|
+
const emitStatus = (status) => {
|
|
54
|
+
statusListeners.forEach((l) => {
|
|
55
|
+
try {
|
|
56
|
+
l(status);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
log('status listener threw', err);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
const send = (type, models) => {
|
|
64
|
+
if (models.length === 0)
|
|
65
|
+
return;
|
|
66
|
+
if (!transport.isOpen())
|
|
67
|
+
return;
|
|
68
|
+
transport.send({ type, models });
|
|
69
|
+
};
|
|
70
|
+
const dispatch = (event) => {
|
|
71
|
+
options.onEvent?.(event);
|
|
72
|
+
subscriptions.forEach((sub) => {
|
|
73
|
+
if (!dataEventMatches(sub.opts, event))
|
|
74
|
+
return;
|
|
75
|
+
try {
|
|
76
|
+
sub.handler(event);
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
log('data event handler threw', err);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
/** Synthesises one `resync` per subscribed model so every consumer refetches. */
|
|
84
|
+
const resyncAll = (reason) => {
|
|
85
|
+
const at = new Date().toISOString();
|
|
86
|
+
const models = Array.from(modelCounts.keys());
|
|
87
|
+
if (models.length === 0)
|
|
88
|
+
return;
|
|
89
|
+
log(`resync (${reason})`, models);
|
|
90
|
+
for (const model of models) {
|
|
91
|
+
dispatch({
|
|
92
|
+
org_id: '',
|
|
93
|
+
addon: '',
|
|
94
|
+
model,
|
|
95
|
+
action: 'resync',
|
|
96
|
+
id: '',
|
|
97
|
+
at,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
const offFrame = transport.onFrame((frame) => {
|
|
102
|
+
if (!frame || typeof frame.type !== 'string')
|
|
103
|
+
return;
|
|
104
|
+
switch (frame.type) {
|
|
105
|
+
case DATA_EVENT: {
|
|
106
|
+
const p = frame.payload;
|
|
107
|
+
if (!p || typeof p.model !== 'string' || typeof p.action !== 'string')
|
|
108
|
+
return;
|
|
109
|
+
dispatch({
|
|
110
|
+
org_id: String(p.org_id ?? ''),
|
|
111
|
+
addon: String(p.addon ?? ''),
|
|
112
|
+
model: p.model,
|
|
113
|
+
table: typeof p.table === 'string' ? p.table : undefined,
|
|
114
|
+
action: p.action,
|
|
115
|
+
id: String(p.id ?? ''),
|
|
116
|
+
stage_from: typeof p.stage_from === 'string' ? p.stage_from : undefined,
|
|
117
|
+
stage_to: typeof p.stage_to === 'string' ? p.stage_to : undefined,
|
|
118
|
+
actor_id: typeof p.actor_id === 'string' ? p.actor_id : undefined,
|
|
119
|
+
at: typeof p.at === 'string' ? p.at : new Date().toISOString(),
|
|
120
|
+
fields: Array.isArray(p.fields) ? p.fields : undefined,
|
|
121
|
+
coalesced: typeof p.coalesced === 'number' ? p.coalesced : undefined,
|
|
122
|
+
});
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
case DATA_SUBSCRIBED: {
|
|
126
|
+
const p = frame.payload;
|
|
127
|
+
acked = Array.isArray(p?.models) ? p.models.map(String) : [];
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
case DATA_EVENTS_DROPPED:
|
|
131
|
+
resyncAll('server dropped frames');
|
|
132
|
+
return;
|
|
133
|
+
default:
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
const offOpen = transport.onOpen(() => {
|
|
138
|
+
const models = Array.from(modelCounts.keys());
|
|
139
|
+
send('SUBSCRIBE', models);
|
|
140
|
+
if (everOpened) {
|
|
141
|
+
// Frames emitted during the gap are gone — refetch everything shown.
|
|
142
|
+
resyncAll('reconnect');
|
|
143
|
+
}
|
|
144
|
+
everOpened = true;
|
|
145
|
+
emitStatus('open');
|
|
146
|
+
});
|
|
147
|
+
const offStatus = transport.onStatus
|
|
148
|
+
? transport.onStatus((s) => {
|
|
149
|
+
if (s !== 'open')
|
|
150
|
+
emitStatus(s);
|
|
151
|
+
})
|
|
152
|
+
: () => { };
|
|
153
|
+
const subscribe = (opts, handler) => {
|
|
154
|
+
if (closed)
|
|
155
|
+
return () => { };
|
|
156
|
+
const sub = { opts: { ...opts, models: opts.models ?? [] }, handler };
|
|
157
|
+
subscriptions.add(sub);
|
|
158
|
+
const added = [];
|
|
159
|
+
for (const raw of sub.opts.models ?? []) {
|
|
160
|
+
const m = normaliseModel(raw);
|
|
161
|
+
if (!m)
|
|
162
|
+
continue;
|
|
163
|
+
const n = (modelCounts.get(m) ?? 0) + 1;
|
|
164
|
+
modelCounts.set(m, n);
|
|
165
|
+
if (n === 1)
|
|
166
|
+
added.push(m);
|
|
167
|
+
}
|
|
168
|
+
send('SUBSCRIBE', added);
|
|
169
|
+
let active = true;
|
|
170
|
+
return () => {
|
|
171
|
+
if (!active)
|
|
172
|
+
return;
|
|
173
|
+
active = false;
|
|
174
|
+
subscriptions.delete(sub);
|
|
175
|
+
const removed = [];
|
|
176
|
+
for (const raw of sub.opts.models ?? []) {
|
|
177
|
+
const m = normaliseModel(raw);
|
|
178
|
+
if (!m)
|
|
179
|
+
continue;
|
|
180
|
+
const n = (modelCounts.get(m) ?? 0) - 1;
|
|
181
|
+
if (n <= 0) {
|
|
182
|
+
modelCounts.delete(m);
|
|
183
|
+
removed.push(m);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
modelCounts.set(m, n);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
send('UNSUBSCRIBE', removed);
|
|
190
|
+
};
|
|
191
|
+
};
|
|
192
|
+
return {
|
|
193
|
+
subscribe,
|
|
194
|
+
status: currentStatus,
|
|
195
|
+
onStatus(listener) {
|
|
196
|
+
statusListeners.add(listener);
|
|
197
|
+
return () => {
|
|
198
|
+
statusListeners.delete(listener);
|
|
199
|
+
};
|
|
200
|
+
},
|
|
201
|
+
subscribedModels: () => Array.from(modelCounts.keys()).sort(),
|
|
202
|
+
serverModels: () => acked.slice(),
|
|
203
|
+
close() {
|
|
204
|
+
if (closed)
|
|
205
|
+
return;
|
|
206
|
+
closed = true;
|
|
207
|
+
const models = Array.from(modelCounts.keys());
|
|
208
|
+
send('UNSUBSCRIBE', models);
|
|
209
|
+
subscriptions.clear();
|
|
210
|
+
modelCounts.clear();
|
|
211
|
+
offFrame();
|
|
212
|
+
offOpen();
|
|
213
|
+
offStatus();
|
|
214
|
+
transport.close?.();
|
|
215
|
+
emitStatus('closed');
|
|
216
|
+
statusListeners.clear();
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Own-socket transport: native WebSocket on `wsUrl?channel=…&token=…` with
|
|
222
|
+
* exponential backoff (capped at 30 s). Exported so hosts can wrap it or
|
|
223
|
+
* reuse it for other channels.
|
|
224
|
+
*/
|
|
225
|
+
export function createSocketTransport(options) {
|
|
226
|
+
const frameHandlers = new Set();
|
|
227
|
+
const openHandlers = new Set();
|
|
228
|
+
const statusHandlers = new Set();
|
|
229
|
+
const log = options.log ?? (() => { });
|
|
230
|
+
const baseDelay = options.reconnectInterval ?? 2000;
|
|
231
|
+
const maxAttempts = options.maxReconnectAttempts ?? Number.POSITIVE_INFINITY;
|
|
232
|
+
let ws = null;
|
|
233
|
+
let attempts = 0;
|
|
234
|
+
let timer = null;
|
|
235
|
+
let closed = false;
|
|
236
|
+
let status = 'closed';
|
|
237
|
+
const setStatus = (s) => {
|
|
238
|
+
status = s;
|
|
239
|
+
statusHandlers.forEach((h) => {
|
|
240
|
+
try {
|
|
241
|
+
h(s);
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
log('status handler threw', err);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
};
|
|
248
|
+
const buildUrl = () => {
|
|
249
|
+
if (!options.wsUrl)
|
|
250
|
+
return null;
|
|
251
|
+
const token = options.getToken?.();
|
|
252
|
+
if (options.getToken && !token)
|
|
253
|
+
return null;
|
|
254
|
+
let url;
|
|
255
|
+
try {
|
|
256
|
+
url = new URL(options.wsUrl, typeof window !== 'undefined' ? window.location.href : undefined);
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
if (options.channel)
|
|
262
|
+
url.searchParams.set('channel', options.channel);
|
|
263
|
+
if (token)
|
|
264
|
+
url.searchParams.set('token', token);
|
|
265
|
+
return url.toString();
|
|
266
|
+
};
|
|
267
|
+
const schedule = () => {
|
|
268
|
+
if (closed || attempts >= maxAttempts)
|
|
269
|
+
return;
|
|
270
|
+
const delay = Math.min(baseDelay * 2 ** attempts, 30_000);
|
|
271
|
+
attempts += 1;
|
|
272
|
+
timer = setTimeout(connect, delay);
|
|
273
|
+
};
|
|
274
|
+
const connect = () => {
|
|
275
|
+
if (closed || typeof WebSocket === 'undefined')
|
|
276
|
+
return;
|
|
277
|
+
const url = buildUrl();
|
|
278
|
+
if (!url) {
|
|
279
|
+
// No token yet — poll again with backoff.
|
|
280
|
+
schedule();
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
setStatus('connecting');
|
|
284
|
+
let socket;
|
|
285
|
+
try {
|
|
286
|
+
socket = new WebSocket(url);
|
|
287
|
+
}
|
|
288
|
+
catch (err) {
|
|
289
|
+
log('socket construct failed', err);
|
|
290
|
+
setStatus('closed');
|
|
291
|
+
schedule();
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
ws = socket;
|
|
295
|
+
socket.onopen = () => {
|
|
296
|
+
attempts = 0;
|
|
297
|
+
setStatus('open');
|
|
298
|
+
openHandlers.forEach((h) => {
|
|
299
|
+
try {
|
|
300
|
+
h();
|
|
301
|
+
}
|
|
302
|
+
catch (err) {
|
|
303
|
+
log('open handler threw', err);
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
};
|
|
307
|
+
socket.onmessage = (ev) => {
|
|
308
|
+
if (typeof ev.data !== 'string')
|
|
309
|
+
return;
|
|
310
|
+
let parsed = null;
|
|
311
|
+
try {
|
|
312
|
+
parsed = JSON.parse(ev.data);
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (!parsed || typeof parsed.type !== 'string')
|
|
318
|
+
return;
|
|
319
|
+
frameHandlers.forEach((h) => {
|
|
320
|
+
try {
|
|
321
|
+
h(parsed);
|
|
322
|
+
}
|
|
323
|
+
catch (err) {
|
|
324
|
+
log('frame handler threw', err);
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
};
|
|
328
|
+
socket.onerror = (ev) => log('socket error', ev);
|
|
329
|
+
socket.onclose = (ev) => {
|
|
330
|
+
if (ws === socket)
|
|
331
|
+
ws = null;
|
|
332
|
+
setStatus('closed');
|
|
333
|
+
// 1008 = policy violation (channel/org mismatch) — never retry that.
|
|
334
|
+
if (closed || ev.code === 1008)
|
|
335
|
+
return;
|
|
336
|
+
schedule();
|
|
337
|
+
};
|
|
338
|
+
};
|
|
339
|
+
// Connect lazily on the first listener so building a client is side-effect free.
|
|
340
|
+
const ensureConnected = () => {
|
|
341
|
+
if (!ws && !timer && !closed)
|
|
342
|
+
connect();
|
|
343
|
+
};
|
|
344
|
+
return {
|
|
345
|
+
send(frame) {
|
|
346
|
+
if (!ws || ws.readyState !== WebSocket.OPEN)
|
|
347
|
+
return false;
|
|
348
|
+
try {
|
|
349
|
+
ws.send(typeof frame === 'string' ? frame : JSON.stringify(frame));
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
catch {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
},
|
|
356
|
+
onFrame(handler) {
|
|
357
|
+
frameHandlers.add(handler);
|
|
358
|
+
ensureConnected();
|
|
359
|
+
return () => {
|
|
360
|
+
frameHandlers.delete(handler);
|
|
361
|
+
};
|
|
362
|
+
},
|
|
363
|
+
isOpen: () => !!ws && ws.readyState === WebSocket.OPEN,
|
|
364
|
+
onOpen(handler) {
|
|
365
|
+
openHandlers.add(handler);
|
|
366
|
+
ensureConnected();
|
|
367
|
+
return () => {
|
|
368
|
+
openHandlers.delete(handler);
|
|
369
|
+
};
|
|
370
|
+
},
|
|
371
|
+
onStatus(handler) {
|
|
372
|
+
statusHandlers.add(handler);
|
|
373
|
+
return () => {
|
|
374
|
+
statusHandlers.delete(handler);
|
|
375
|
+
};
|
|
376
|
+
},
|
|
377
|
+
status: () => status,
|
|
378
|
+
close() {
|
|
379
|
+
closed = true;
|
|
380
|
+
if (timer) {
|
|
381
|
+
clearTimeout(timer);
|
|
382
|
+
timer = null;
|
|
383
|
+
}
|
|
384
|
+
const socket = ws;
|
|
385
|
+
ws = null;
|
|
386
|
+
if (socket) {
|
|
387
|
+
socket.onopen = null;
|
|
388
|
+
socket.onmessage = null;
|
|
389
|
+
socket.onerror = null;
|
|
390
|
+
socket.onclose = null;
|
|
391
|
+
try {
|
|
392
|
+
socket.close(1000, 'realtime client closed');
|
|
393
|
+
}
|
|
394
|
+
catch {
|
|
395
|
+
/* ignore */
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
setStatus('closed');
|
|
399
|
+
},
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
export function transportFromHostSocket(host) {
|
|
403
|
+
const statusHandlers = new Set();
|
|
404
|
+
return {
|
|
405
|
+
send(frame) {
|
|
406
|
+
if (!host.isConnected())
|
|
407
|
+
return false;
|
|
408
|
+
host.send(frame);
|
|
409
|
+
return true;
|
|
410
|
+
},
|
|
411
|
+
onFrame(handler) {
|
|
412
|
+
const offs = [DATA_EVENT, DATA_SUBSCRIBED, DATA_EVENTS_DROPPED].map((t) => host.subscribe(t, handler));
|
|
413
|
+
return () => offs.forEach((off) => off());
|
|
414
|
+
},
|
|
415
|
+
isOpen: () => host.isConnected(),
|
|
416
|
+
onOpen(handler) {
|
|
417
|
+
let was = host.isConnected();
|
|
418
|
+
if (was)
|
|
419
|
+
handler();
|
|
420
|
+
return host.onConnected((connected) => {
|
|
421
|
+
if (connected && !was)
|
|
422
|
+
handler();
|
|
423
|
+
was = connected;
|
|
424
|
+
});
|
|
425
|
+
},
|
|
426
|
+
onStatus(handler) {
|
|
427
|
+
statusHandlers.add(handler);
|
|
428
|
+
const off = host.onConnected((connected) => handler(connected ? 'open' : 'connecting'));
|
|
429
|
+
return () => {
|
|
430
|
+
statusHandlers.delete(handler);
|
|
431
|
+
off();
|
|
432
|
+
};
|
|
433
|
+
},
|
|
434
|
+
status: () => (host.isConnected() ? 'open' : 'connecting'),
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
//# sourceMappingURL=realtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime.js","sourceRoot":"","sources":["../src/realtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime.test.d.ts","sourceRoot":"","sources":["../src/realtime.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// createRealtimeClient over a fake transport: subscription ref-counting,
|
|
2
|
+
// (re)subscribe on open, DATA_EVENT dispatch with model/action filters, and
|
|
3
|
+
// the resync synthesised on DROPPED notices / reconnects.
|
|
4
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { createRealtimeClient, transportFromHostSocket } from './realtime';
|
|
6
|
+
function fakeTransport(open = true) {
|
|
7
|
+
const frameHandlers = new Set();
|
|
8
|
+
const openHandlers = new Set();
|
|
9
|
+
const sent = [];
|
|
10
|
+
let isOpen = open;
|
|
11
|
+
const transport = {
|
|
12
|
+
send: (frame) => {
|
|
13
|
+
if (!isOpen)
|
|
14
|
+
return false;
|
|
15
|
+
sent.push(frame);
|
|
16
|
+
return true;
|
|
17
|
+
},
|
|
18
|
+
onFrame: (h) => {
|
|
19
|
+
frameHandlers.add(h);
|
|
20
|
+
return () => frameHandlers.delete(h);
|
|
21
|
+
},
|
|
22
|
+
isOpen: () => isOpen,
|
|
23
|
+
onOpen: (h) => {
|
|
24
|
+
openHandlers.add(h);
|
|
25
|
+
return () => openHandlers.delete(h);
|
|
26
|
+
},
|
|
27
|
+
close: vi.fn(),
|
|
28
|
+
};
|
|
29
|
+
return {
|
|
30
|
+
transport,
|
|
31
|
+
sent,
|
|
32
|
+
open() {
|
|
33
|
+
isOpen = true;
|
|
34
|
+
openHandlers.forEach((h) => h());
|
|
35
|
+
},
|
|
36
|
+
drop() {
|
|
37
|
+
isOpen = false;
|
|
38
|
+
},
|
|
39
|
+
frame(f) {
|
|
40
|
+
frameHandlers.forEach((h) => h(f));
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const event = (over = {}) => ({
|
|
45
|
+
org_id: 'org-1',
|
|
46
|
+
addon: 'pos',
|
|
47
|
+
model: 'SalesOrder',
|
|
48
|
+
table: 'sales_orders',
|
|
49
|
+
action: 'updated',
|
|
50
|
+
id: 'so-1',
|
|
51
|
+
at: '2026-08-31T00:00:00Z',
|
|
52
|
+
fields: ['status'],
|
|
53
|
+
...over,
|
|
54
|
+
});
|
|
55
|
+
describe('createRealtimeClient', () => {
|
|
56
|
+
it('sends SUBSCRIBE once per model and UNSUBSCRIBE when the last listener leaves', () => {
|
|
57
|
+
const t = fakeTransport();
|
|
58
|
+
const client = createRealtimeClient({ transport: t.transport });
|
|
59
|
+
const offA = client.subscribe({ models: ['SalesOrder', 'WorkOrder'] }, () => { });
|
|
60
|
+
const offB = client.subscribe({ models: ['salesorder'] }, () => { });
|
|
61
|
+
expect(t.sent).toEqual([{ type: 'SUBSCRIBE', models: ['salesorder', 'workorder'] }]);
|
|
62
|
+
expect(client.subscribedModels()).toEqual(['salesorder', 'workorder']);
|
|
63
|
+
offA();
|
|
64
|
+
// WorkOrder had a single listener → unsubscribed; SalesOrder still has B.
|
|
65
|
+
expect(t.sent[1]).toEqual({ type: 'UNSUBSCRIBE', models: ['workorder'] });
|
|
66
|
+
offB();
|
|
67
|
+
expect(t.sent[2]).toEqual({ type: 'UNSUBSCRIBE', models: ['salesorder'] });
|
|
68
|
+
expect(client.subscribedModels()).toEqual([]);
|
|
69
|
+
// Idempotent unsubscribe.
|
|
70
|
+
offB();
|
|
71
|
+
expect(t.sent).toHaveLength(3);
|
|
72
|
+
});
|
|
73
|
+
it('defers SUBSCRIBE until the transport opens and re-sends the full set on every open', () => {
|
|
74
|
+
const t = fakeTransport(false);
|
|
75
|
+
const client = createRealtimeClient({ transport: t.transport });
|
|
76
|
+
const handler = vi.fn();
|
|
77
|
+
client.subscribe({ models: ['SalesOrder'] }, handler);
|
|
78
|
+
expect(t.sent).toEqual([]);
|
|
79
|
+
t.open();
|
|
80
|
+
expect(t.sent).toEqual([{ type: 'SUBSCRIBE', models: ['salesorder'] }]);
|
|
81
|
+
expect(client.status()).toBe('open');
|
|
82
|
+
// First open → no resync (nothing was missed).
|
|
83
|
+
expect(handler).not.toHaveBeenCalled();
|
|
84
|
+
t.drop();
|
|
85
|
+
t.open();
|
|
86
|
+
expect(t.sent[1]).toEqual({ type: 'SUBSCRIBE', models: ['salesorder'] });
|
|
87
|
+
// Reconnect → synthetic resync so consumers refetch.
|
|
88
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
89
|
+
expect(handler.mock.calls[0][0]).toMatchObject({ action: 'resync', model: 'salesorder' });
|
|
90
|
+
});
|
|
91
|
+
it('dispatches DATA_EVENT frames only to matching subscriptions', () => {
|
|
92
|
+
const t = fakeTransport();
|
|
93
|
+
const client = createRealtimeClient({ transport: t.transport });
|
|
94
|
+
const byModel = vi.fn();
|
|
95
|
+
const byTable = vi.fn();
|
|
96
|
+
const byQualified = vi.fn();
|
|
97
|
+
const other = vi.fn();
|
|
98
|
+
const onlyDeletes = vi.fn();
|
|
99
|
+
client.subscribe({ models: ['SalesOrder'] }, byModel);
|
|
100
|
+
client.subscribe({ models: ['sales_orders'] }, byTable);
|
|
101
|
+
client.subscribe({ models: ['pos.SalesOrder'] }, byQualified);
|
|
102
|
+
client.subscribe({ models: ['WorkOrder'] }, other);
|
|
103
|
+
client.subscribe({ models: ['SalesOrder'], events: ['deleted'] }, onlyDeletes);
|
|
104
|
+
t.frame({ type: 'DATA_EVENT', payload: event() });
|
|
105
|
+
expect(byModel).toHaveBeenCalledTimes(1);
|
|
106
|
+
expect(byTable).toHaveBeenCalledTimes(1);
|
|
107
|
+
expect(byQualified).toHaveBeenCalledTimes(1);
|
|
108
|
+
expect(other).not.toHaveBeenCalled();
|
|
109
|
+
expect(onlyDeletes).not.toHaveBeenCalled();
|
|
110
|
+
expect(byModel.mock.calls[0][0]).toEqual(event());
|
|
111
|
+
t.frame({ type: 'DATA_EVENT', payload: event({ action: 'deleted', fields: undefined }) });
|
|
112
|
+
expect(onlyDeletes).toHaveBeenCalledTimes(1);
|
|
113
|
+
// Malformed / unrelated frames are ignored.
|
|
114
|
+
t.frame({ type: 'DATA_EVENT', payload: { nope: true } });
|
|
115
|
+
t.frame({ type: 'NOTIFICATION', payload: { title: 'x' } });
|
|
116
|
+
expect(byModel).toHaveBeenCalledTimes(2);
|
|
117
|
+
});
|
|
118
|
+
it('records the server ack and turns DROPPED notices into resync events', () => {
|
|
119
|
+
const t = fakeTransport();
|
|
120
|
+
const client = createRealtimeClient({ transport: t.transport });
|
|
121
|
+
const handler = vi.fn();
|
|
122
|
+
client.subscribe({ models: ['SalesOrder', 'WorkOrder'] }, handler);
|
|
123
|
+
t.frame({ type: 'DATA_SUBSCRIBED', payload: { models: ['salesorder', 'workorder'] } });
|
|
124
|
+
expect(client.serverModels()).toEqual(['salesorder', 'workorder']);
|
|
125
|
+
t.frame({ type: 'DATA_EVENTS_DROPPED', payload: { dropped: 7, models: ['salesorder'] } });
|
|
126
|
+
expect(handler).toHaveBeenCalledTimes(2);
|
|
127
|
+
expect(handler.mock.calls.map((c) => c[0].model).sort()).toEqual(['salesorder', 'workorder']);
|
|
128
|
+
expect(handler.mock.calls.every((c) => c[0].action === 'resync')).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
it('never lets a throwing handler starve the others', () => {
|
|
131
|
+
const t = fakeTransport();
|
|
132
|
+
const log = vi.fn();
|
|
133
|
+
const client = createRealtimeClient({ transport: t.transport, log });
|
|
134
|
+
const bad = vi.fn(() => {
|
|
135
|
+
throw new Error('boom');
|
|
136
|
+
});
|
|
137
|
+
const good = vi.fn();
|
|
138
|
+
client.subscribe({ models: ['SalesOrder'] }, bad);
|
|
139
|
+
client.subscribe({ models: ['SalesOrder'] }, good);
|
|
140
|
+
t.frame({ type: 'DATA_EVENT', payload: event() });
|
|
141
|
+
expect(good).toHaveBeenCalledTimes(1);
|
|
142
|
+
expect(log).toHaveBeenCalled();
|
|
143
|
+
});
|
|
144
|
+
it('close() unsubscribes everything, closes the transport and reports closed', () => {
|
|
145
|
+
const t = fakeTransport();
|
|
146
|
+
const client = createRealtimeClient({ transport: t.transport });
|
|
147
|
+
const status = vi.fn();
|
|
148
|
+
client.onStatus(status);
|
|
149
|
+
client.subscribe({ models: ['SalesOrder'] }, () => { });
|
|
150
|
+
client.close();
|
|
151
|
+
expect(t.sent.at(-1)).toEqual({ type: 'UNSUBSCRIBE', models: ['salesorder'] });
|
|
152
|
+
expect(t.transport.close).toHaveBeenCalled();
|
|
153
|
+
expect(status).toHaveBeenCalledWith('closed');
|
|
154
|
+
expect(client.subscribedModels()).toEqual([]);
|
|
155
|
+
// After close, subscribe is a no-op.
|
|
156
|
+
const off = client.subscribe({ models: ['X'] }, () => { });
|
|
157
|
+
off();
|
|
158
|
+
expect(client.subscribedModels()).toEqual([]);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
describe('transportFromHostSocket', () => {
|
|
162
|
+
it('adapts a host subscribe/send/isConnected surface', () => {
|
|
163
|
+
const subs = new Map();
|
|
164
|
+
const sent = [];
|
|
165
|
+
let connected = false;
|
|
166
|
+
const listeners = new Set();
|
|
167
|
+
const transport = transportFromHostSocket({
|
|
168
|
+
subscribe: (type, handler) => {
|
|
169
|
+
subs.set(type, handler);
|
|
170
|
+
return () => subs.delete(type);
|
|
171
|
+
},
|
|
172
|
+
send: (p) => sent.push(p),
|
|
173
|
+
isConnected: () => connected,
|
|
174
|
+
onConnected: (l) => {
|
|
175
|
+
listeners.add(l);
|
|
176
|
+
return () => listeners.delete(l);
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
const client = createRealtimeClient({ transport });
|
|
180
|
+
const handler = vi.fn();
|
|
181
|
+
client.subscribe({ models: ['SalesOrder'] }, handler);
|
|
182
|
+
expect(sent).toEqual([]); // not connected yet
|
|
183
|
+
expect(client.status()).toBe('connecting');
|
|
184
|
+
connected = true;
|
|
185
|
+
listeners.forEach((l) => l(true));
|
|
186
|
+
expect(sent).toEqual([{ type: 'SUBSCRIBE', models: ['salesorder'] }]);
|
|
187
|
+
expect(client.status()).toBe('open');
|
|
188
|
+
subs.get('DATA_EVENT')({ type: 'DATA_EVENT', payload: event() });
|
|
189
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
190
|
+
expect(subs.has('DATA_EVENTS_DROPPED')).toBe(true);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
//# sourceMappingURL=realtime.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime.test.js","sourceRoot":"","sources":["../src/realtime.test.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -86,4 +86,47 @@ export interface WebSocketContextValue<TMessage extends WebSocketMessage = WebSo
|
|
|
86
86
|
/** Register a handler for a specific message `type`. Returns an unsubscribe fn. */
|
|
87
87
|
subscribe: <T extends TMessage = TMessage>(type: T['type'], handler: (message: T) => void) => () => void;
|
|
88
88
|
}
|
|
89
|
+
/** Audio / video / screen-share call kind. */
|
|
90
|
+
export type CallKind = 'audio' | 'video' | 'screen';
|
|
91
|
+
export type CallSignalType = 'offer' | 'answer' | 'ice';
|
|
92
|
+
/** Host → clients: someone started a call in a channel. */
|
|
93
|
+
export interface CallInvitePayload {
|
|
94
|
+
call_id: string;
|
|
95
|
+
channel_id: string;
|
|
96
|
+
kind: CallKind;
|
|
97
|
+
from_user_id: string;
|
|
98
|
+
from_name?: string;
|
|
99
|
+
}
|
|
100
|
+
/** Peer joined an existing call (mesh renegotiation trigger). */
|
|
101
|
+
export interface CallJoinPayload {
|
|
102
|
+
call_id: string;
|
|
103
|
+
channel_id: string;
|
|
104
|
+
user_id: string;
|
|
105
|
+
name?: string;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* SDP / ICE relay. `to_user_id` is set for directed mesh edges; omit to
|
|
109
|
+
* broadcast to every other participant (rare — prefer directed).
|
|
110
|
+
*/
|
|
111
|
+
export interface CallSignalPayload {
|
|
112
|
+
call_id: string;
|
|
113
|
+
channel_id: string;
|
|
114
|
+
from_user_id: string;
|
|
115
|
+
to_user_id?: string;
|
|
116
|
+
signal_type: CallSignalType;
|
|
117
|
+
sdp?: RTCSessionDescriptionInit | {
|
|
118
|
+
type: string;
|
|
119
|
+
sdp?: string;
|
|
120
|
+
};
|
|
121
|
+
candidate?: RTCIceCandidateInit | null;
|
|
122
|
+
}
|
|
123
|
+
export interface CallEndPayload {
|
|
124
|
+
call_id: string;
|
|
125
|
+
channel_id: string;
|
|
126
|
+
/** Who hung up / ended the room (mesh v1 ends for everyone). */
|
|
127
|
+
ended_by?: string;
|
|
128
|
+
/** @deprecated Prefer ended_by */
|
|
129
|
+
user_id?: string;
|
|
130
|
+
reason?: string;
|
|
131
|
+
}
|
|
89
132
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,eAAe;;;;;CAKlB,CAAA;AAEV,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,OAAO,eAAe,CAAC,CAAA;AAEpF;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,EAAE,QAAQ,GAAG,OAAO;IACjF,IAAI,EAAE,KAAK,CAAA;IACX,OAAO,CAAC,EAAE,QAAQ,CAAA;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAA;AAE9F,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,eAAe,GAAG,IAAI,GAAG,eAAe,GAAG,gBAAgB,CAAA;AAE9F,MAAM,WAAW,sBAAsB,CAAC,QAAQ,SAAS,gBAAgB,GAAG,gBAAgB;IAC1F,4DAA4D;IAC5D,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,WAAW,CAAA;IACtB;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,KAAK,MAAM,GAAG,IAAI,CAAA;IAC3E,iEAAiE;IACjE,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,4DAA4D;IAC5D,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,6DAA6D;IAC7D,gBAAgB,CAAC,EAAE,WAAW,CAAA;IAC9B,kDAAkD;IAClD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAC/B,qCAAqC;IACrC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAA;IACrC,iDAAiD;IACjD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAChC;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,KAAK,IAAI,CAAA;IAC5D,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC7B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC3B;AAED,MAAM,WAAW,qBAAqB,CAAC,QAAQ,SAAS,gBAAgB,GAAG,gBAAgB;IACzF,iCAAiC;IACjC,MAAM,EAAE,eAAe,CAAA;IACvB,qCAAqC;IACrC,WAAW,EAAE,OAAO,CAAA;IACpB,6CAA6C;IAC7C,WAAW,EAAE,QAAQ,GAAG,IAAI,CAAA;IAC5B,wDAAwD;IACxD,SAAS,EAAE,YAAY,GAAG,IAAI,CAAA;IAC9B;;;OAGG;IACH,IAAI,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAA;IACvC,+DAA+D;IAC/D,UAAU,EAAE,MAAM,IAAI,CAAA;IACtB,iDAAiD;IACjD,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,mFAAmF;IACnF,SAAS,EAAE,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ,EACvC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,EACf,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,KAC1B,MAAM,IAAI,CAAA;CAChB"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asteby/metacore-websocket",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Metacore WebSocket provider — auto-reconnect, typed messages, channel subscriptions",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -35,14 +35,17 @@
|
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"react": ">=18",
|
|
38
|
-
"react-dom": ">=18"
|
|
38
|
+
"react-dom": ">=18",
|
|
39
|
+
"@asteby/metacore-sdk": "^3.8.0"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
42
|
"@types/react": "^19.0.0",
|
|
42
43
|
"@types/react-dom": "^19.0.0",
|
|
43
44
|
"react": "^19.2.4",
|
|
44
45
|
"react-dom": "^19.2.4",
|
|
45
|
-
"typescript": "^6.0.0"
|
|
46
|
+
"typescript": "^6.0.0",
|
|
47
|
+
"vitest": "^4.0.0",
|
|
48
|
+
"@asteby/metacore-sdk": "3.8.0"
|
|
46
49
|
},
|
|
47
50
|
"publishConfig": {
|
|
48
51
|
"access": "public"
|
|
@@ -51,6 +54,7 @@
|
|
|
51
54
|
"build": "tsc -p tsconfig.json",
|
|
52
55
|
"dev": "tsc -w -p tsconfig.json",
|
|
53
56
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
57
|
+
"test": "vitest run",
|
|
54
58
|
"clean": "rm -rf dist"
|
|
55
59
|
}
|
|
56
60
|
}
|