@memberjunction/server 5.46.0 → 5.48.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/dist/context.js +37 -1
- package/dist/context.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -1
- package/dist/index.js.map +1 -1
- package/dist/realtimeProxy/RealtimeProxyServer.d.ts +48 -0
- package/dist/realtimeProxy/RealtimeProxyServer.d.ts.map +1 -0
- package/dist/realtimeProxy/RealtimeProxyServer.js +180 -0
- package/dist/realtimeProxy/RealtimeProxyServer.js.map +1 -0
- package/dist/resolvers/TestQuerySQLResolver.d.ts +1 -0
- package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
- package/dist/resolvers/TestQuerySQLResolver.js +5 -0
- package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
- package/package.json +88 -88
- package/src/__tests__/RealtimeProxyServer.test.ts +70 -0
- package/src/context.ts +46 -1
- package/src/index.ts +26 -1
- package/src/realtimeProxy/RealtimeProxyServer.ts +196 -0
- package/src/resolvers/TestQuerySQLResolver.ts +4 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { WebSocket as WsClient, WebSocketServer } from 'ws';
|
|
2
|
+
import { BaseSingleton, ShutdownRegistry } from '@memberjunction/global';
|
|
3
|
+
import { RealtimeProxyRegistry, REALTIME_PROXY_PATH } from '@memberjunction/ai';
|
|
4
|
+
/**
|
|
5
|
+
* MJAPI's realtime websocket **proxy** — the transport half of the self-hosted realtime provider story.
|
|
6
|
+
*
|
|
7
|
+
* A provider driver (e.g. `HuggingFaceRealtime`) mints a one-time ticket into the shared
|
|
8
|
+
* {@link RealtimeProxyRegistry} pointing at an INTERNAL realtime endpoint, and hands the browser a
|
|
9
|
+
* `wss://<mjapi-public>{REALTIME_PROXY_PATH}?ticket=<id>` URL. The browser opens its socket HERE; this
|
|
10
|
+
* proxy consumes the ticket, opens the authenticated upstream leg (injecting any auth server-side), and
|
|
11
|
+
* pumps frames transparently in both directions. The internal endpoint + auth never reach the browser,
|
|
12
|
+
* and the internal box needs no browser-facing ingress — MJAPI stays the single ingress point.
|
|
13
|
+
*
|
|
14
|
+
* Deliberately provider-agnostic: it does no protocol translation (the same-keyed client driver owns the
|
|
15
|
+
* wire vocabulary) and knows nothing about any specific provider — it is a pure authenticated byte tunnel,
|
|
16
|
+
* reusable by any future self-hosted realtime provider that mints a ticket.
|
|
17
|
+
*
|
|
18
|
+
* A {@link BaseSingleton} + {@link IShutdownable}: it tracks live tunnels and closes them on graceful
|
|
19
|
+
* shutdown (drained by MJServer's `ShutdownRegistry` before `httpServer.close()`).
|
|
20
|
+
*/
|
|
21
|
+
export class RealtimeProxyServer extends BaseSingleton {
|
|
22
|
+
constructor() {
|
|
23
|
+
super();
|
|
24
|
+
this.ShutdownName = 'RealtimeProxyServer';
|
|
25
|
+
/** `noServer` so THIS server never binds its own upgrade listener — MJServer routes upgrades to it by path. */
|
|
26
|
+
this.wss = new WebSocketServer({ noServer: true });
|
|
27
|
+
/** Live browser↔upstream tunnels, tracked for shutdown teardown. */
|
|
28
|
+
this.tunnels = new Set();
|
|
29
|
+
this.registered = false;
|
|
30
|
+
}
|
|
31
|
+
/** Process-wide singleton accessor. */
|
|
32
|
+
static get Instance() {
|
|
33
|
+
return super.getInstance();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Routes an HTTP `upgrade` for {@link REALTIME_PROXY_PATH} to the proxy. Returns `true` when it OWNS
|
|
37
|
+
* (handled/rejected) the request, `false` when the path is not the proxy's — so the caller leaves the
|
|
38
|
+
* socket for the GraphQL websocket server's own upgrade listener. NEVER destroys a socket it doesn't own.
|
|
39
|
+
*/
|
|
40
|
+
TryHandleUpgrade(request, socket, head) {
|
|
41
|
+
const url = RealtimeProxyServer.parseUrl(request.url);
|
|
42
|
+
if (!url || url.pathname !== REALTIME_PROXY_PATH) {
|
|
43
|
+
return false; // not ours — leave it for the GraphQL WS server
|
|
44
|
+
}
|
|
45
|
+
this.ensureRegistered();
|
|
46
|
+
const ticketId = url.searchParams.get('ticket') ?? '';
|
|
47
|
+
const entry = RealtimeProxyRegistry.Instance.Consume(ticketId);
|
|
48
|
+
if (!entry) {
|
|
49
|
+
RealtimeProxyServer.rejectUpgrade(socket, 401, 'Unauthorized');
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
this.wss.handleUpgrade(request, socket, head, (browserWs) => this.openTunnel(browserWs, entry));
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
/** Opens the upstream leg and wires a bidirectional pump between the browser socket and it. */
|
|
56
|
+
openTunnel(browserWs, entry) {
|
|
57
|
+
const tunnel = new RealtimeProxyTunnel(browserWs, entry, () => this.tunnels.delete(tunnel));
|
|
58
|
+
this.tunnels.add(tunnel);
|
|
59
|
+
tunnel.Start();
|
|
60
|
+
}
|
|
61
|
+
/** Registers for graceful-shutdown draining exactly once (lazily, on first real use). */
|
|
62
|
+
ensureRegistered() {
|
|
63
|
+
if (!this.registered) {
|
|
64
|
+
ShutdownRegistry.Instance.Register(this);
|
|
65
|
+
this.registered = true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** Closes every live tunnel and the proxy server. Idempotent; never throws. */
|
|
69
|
+
Shutdown() {
|
|
70
|
+
for (const tunnel of [...this.tunnels]) {
|
|
71
|
+
tunnel.Close();
|
|
72
|
+
}
|
|
73
|
+
this.tunnels.clear();
|
|
74
|
+
try {
|
|
75
|
+
this.wss.close();
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
/* already closing */
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** Parses `request.url` (a path+query) into a URL, or `null` when absent/unparseable. */
|
|
82
|
+
static parseUrl(rawUrl) {
|
|
83
|
+
if (!rawUrl) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
return new URL(rawUrl, 'http://internal'); // base is only for parsing path+query
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** Writes a minimal HTTP error response and destroys the socket (used for a rejected upgrade). */
|
|
94
|
+
static rejectUpgrade(socket, code, reason) {
|
|
95
|
+
try {
|
|
96
|
+
socket.write(`HTTP/1.1 ${code} ${reason}\r\nConnection: close\r\n\r\n`);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
/* socket already gone */
|
|
100
|
+
}
|
|
101
|
+
socket.destroy();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* One live browser↔upstream tunnel. Buffers browser→upstream frames until the upstream socket is open,
|
|
106
|
+
* then pumps both directions byte-transparently (text and binary alike). Closing either side closes the other.
|
|
107
|
+
*/
|
|
108
|
+
class RealtimeProxyTunnel {
|
|
109
|
+
constructor(browser, entry, onClosed) {
|
|
110
|
+
this.browser = browser;
|
|
111
|
+
this.entry = entry;
|
|
112
|
+
this.onClosed = onClosed;
|
|
113
|
+
this.upstream = null;
|
|
114
|
+
/** Frames the browser sent before the upstream opened; flushed in order once it's ready. */
|
|
115
|
+
this.pending = [];
|
|
116
|
+
this.closed = false;
|
|
117
|
+
}
|
|
118
|
+
/** Opens the upstream socket and wires both legs. */
|
|
119
|
+
Start() {
|
|
120
|
+
const headers = this.entry.UpstreamAuthHeader ? { Authorization: this.entry.UpstreamAuthHeader } : undefined;
|
|
121
|
+
const upstream = new WsClient(this.entry.UpstreamUrl, { headers });
|
|
122
|
+
this.upstream = upstream;
|
|
123
|
+
upstream.on('open', () => this.flushPending());
|
|
124
|
+
upstream.on('message', (data, isBinary) => this.forward(this.browser, data, isBinary));
|
|
125
|
+
upstream.on('close', () => this.Close());
|
|
126
|
+
upstream.on('error', () => this.Close());
|
|
127
|
+
this.browser.on('message', (data, isBinary) => this.fromBrowser(data, isBinary));
|
|
128
|
+
this.browser.on('close', () => this.Close());
|
|
129
|
+
this.browser.on('error', () => this.Close());
|
|
130
|
+
}
|
|
131
|
+
/** Browser→upstream: forward immediately when the upstream is open, else queue until it is. */
|
|
132
|
+
fromBrowser(data, isBinary) {
|
|
133
|
+
if (this.upstream && this.upstream.readyState === WsClient.OPEN) {
|
|
134
|
+
this.forward(this.upstream, data, isBinary);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
this.pending.push({ data, isBinary });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/** Drains any frames queued before the upstream opened. */
|
|
141
|
+
flushPending() {
|
|
142
|
+
if (!this.upstream) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
for (const frame of this.pending) {
|
|
146
|
+
this.forward(this.upstream, frame.data, frame.isBinary);
|
|
147
|
+
}
|
|
148
|
+
this.pending.length = 0;
|
|
149
|
+
}
|
|
150
|
+
/** Sends one frame on a socket, preserving the text/binary distinction; failures close the tunnel. */
|
|
151
|
+
forward(target, data, isBinary) {
|
|
152
|
+
if (target.readyState !== WsClient.OPEN) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
target.send(data, { binary: isBinary });
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
this.Close();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/** Closes both legs and detaches the tunnel from the server. Idempotent. */
|
|
163
|
+
Close() {
|
|
164
|
+
if (this.closed) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
this.closed = true;
|
|
168
|
+
this.pending.length = 0;
|
|
169
|
+
for (const sock of [this.browser, this.upstream]) {
|
|
170
|
+
try {
|
|
171
|
+
sock?.close();
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
/* already closing */
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
this.onClosed();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=RealtimeProxyServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RealtimeProxyServer.js","sourceRoot":"","sources":["../../src/realtimeProxy/RealtimeProxyServer.ts"],"names":[],"mappings":"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"}
|
|
@@ -34,6 +34,7 @@ export declare class TestQuerySQLResult {
|
|
|
34
34
|
ExecutionTime: number;
|
|
35
35
|
ErrorMessage?: string;
|
|
36
36
|
AppliedParameters?: string;
|
|
37
|
+
RenderedSQL?: string;
|
|
37
38
|
}
|
|
38
39
|
/**
|
|
39
40
|
* Resolver for testing transient (unsaved) query SQL with full composition + Nunjucks template processing.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TestQuerySQLResolver.d.ts","sourceRoot":"","sources":["../../src/resolvers/TestQuerySQLResolver.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D;;;GAGG;AACH,qBACa,wBAAwB;IAEjC,IAAI,EAAE,MAAM,CAAC;IAGb,YAAY,EAAE,MAAM,CAAC;IAGrB,GAAG,EAAE,MAAM,CAAC;IAGZ,YAAY,CAAC,EAAE,OAAO,CAAC;IAGvB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAGpC,YAAY,CAAC,EAAE,wBAAwB,EAAE,CAAC;CAC7C;AAED;;;;GAIG;AACH,qBACa,iBAAiB;IAE1B,GAAG,EAAE,MAAM,CAAC;IAGZ,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAGpC,YAAY,CAAC,EAAE,OAAO,CAAC;IAGvB,YAAY,CAAC,EAAE,wBAAwB,EAAE,CAAC;IAG1C,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBACa,kBAAkB;IAE3B,OAAO,EAAE,OAAO,CAAC;IAGjB,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,QAAQ,EAAE,MAAM,CAAC;IAGjB,aAAa,EAAE,MAAM,CAAC;IAGtB,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"TestQuerySQLResolver.d.ts","sourceRoot":"","sources":["../../src/resolvers/TestQuerySQLResolver.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D;;;GAGG;AACH,qBACa,wBAAwB;IAEjC,IAAI,EAAE,MAAM,CAAC;IAGb,YAAY,EAAE,MAAM,CAAC;IAGrB,GAAG,EAAE,MAAM,CAAC;IAGZ,YAAY,CAAC,EAAE,OAAO,CAAC;IAGvB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAGpC,YAAY,CAAC,EAAE,wBAAwB,EAAE,CAAC;CAC7C;AAED;;;;GAIG;AACH,qBACa,iBAAiB;IAE1B,GAAG,EAAE,MAAM,CAAC;IAGZ,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAGpC,YAAY,CAAC,EAAE,OAAO,CAAC;IAGvB,YAAY,CAAC,EAAE,wBAAwB,EAAE,CAAC;IAG1C,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBACa,kBAAkB;IAE3B,OAAO,EAAE,OAAO,CAAC;IAGjB,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,QAAQ,EAAE,MAAM,CAAC;IAGjB,aAAa,EAAE,MAAM,CAAC;IAGtB,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAG3B,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;GAYG;AACH,qBACa,oBAAqB,SAAQ,YAAY;IAI5C,YAAY,CACyB,KAAK,EAAE,iBAAiB,EACxD,OAAO,EAAE,UAAU,GAC3B,OAAO,CAAC,kBAAkB,CAAC;CAgDjC"}
|
|
@@ -109,6 +109,10 @@ __decorate([
|
|
|
109
109
|
Field(() => String, { nullable: true, description: 'JSON-stringified applied parameters including defaults' }),
|
|
110
110
|
__metadata("design:type", String)
|
|
111
111
|
], TestQuerySQLResult.prototype, "AppliedParameters", void 0);
|
|
112
|
+
__decorate([
|
|
113
|
+
Field(() => String, { nullable: true, description: 'The fully rendered SQL that was executed against the database. On error, reveals transformations (composition, templates, MaxRows wrapping) that may have caused the failure.' }),
|
|
114
|
+
__metadata("design:type", String)
|
|
115
|
+
], TestQuerySQLResult.prototype, "RenderedSQL", void 0);
|
|
112
116
|
TestQuerySQLResult = __decorate([
|
|
113
117
|
ObjectType()
|
|
114
118
|
], TestQuerySQLResult);
|
|
@@ -157,6 +161,7 @@ let TestQuerySQLResolver = class TestQuerySQLResolver extends ResolverBase {
|
|
|
157
161
|
ExecutionTime: result.ExecutionTime,
|
|
158
162
|
ErrorMessage: result.ErrorMessage || undefined,
|
|
159
163
|
AppliedParameters: result.AppliedParameters ? JSON.stringify(result.AppliedParameters) : undefined,
|
|
164
|
+
RenderedSQL: result.RenderedSQL || undefined,
|
|
160
165
|
};
|
|
161
166
|
}
|
|
162
167
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TestQuerySQLResolver.js","sourceRoot":"","sources":["../../src/resolvers/TestQuerySQLResolver.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC5F,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAqB,QAAQ,EAAE,SAAS,EAAsB,MAAM,sBAAsB,CAAC;AAE5G,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D;;;GAGG;AAEI,IAAM,wBAAwB,GAA9B,MAAM,wBAAwB;CAkBpC,CAAA;AAhBG;IADC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,mDAAmD,EAAE,CAAC;;sDAC7E;AAGb;IADC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,kFAAkF,EAAE,CAAC;;8DACpG;AAGrB;IADC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,iCAAiC,EAAE,CAAC;;qDAC5D;AAGZ;IADC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uDAAuD,EAAE,CAAC;;8DACxF;AAGvB;IADC,KAAK,CAAC,GAAG,EAAE,CAAC,iBAAiB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,sDAAsD,EAAE,CAAC;;4DACpF;AAGpC;IADC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,wBAAwB,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,iCAAiC,EAAE,CAAC;;8DAClE;AAjBjC,wBAAwB;IADpC,SAAS,EAAE;GACC,wBAAwB,CAkBpC;;AAED;;;;GAIG;AAEI,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;CAe7B,CAAA;AAbG;IADC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,kEAAkE,EAAE,CAAC;;8CAC7F;AAGZ;IADC,KAAK,CAAC,GAAG,EAAE,CAAC,iBAAiB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,qDAAqD,EAAE,CAAC;;qDACnF;AAGpC;IADC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,kDAAkD,EAAE,CAAC;;uDACnF;AAGvB;IADC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,wBAAwB,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,sDAAsD,EAAE,CAAC;;uDACvF;AAG1C;IADC,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,WAAW,EAAE,mCAAmC,EAAE,CAAC;;kDACzF;AAdR,iBAAiB;IAD7B,SAAS,EAAE;GACC,iBAAiB,CAe7B;;AAED;;GAEG;AAEI,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;
|
|
1
|
+
{"version":3,"file":"TestQuerySQLResolver.js","sourceRoot":"","sources":["../../src/resolvers/TestQuerySQLResolver.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/server",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.48.0",
|
|
4
4
|
"description": "MemberJunction: This project provides API access via GraphQL to the common data store.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -27,93 +27,93 @@
|
|
|
27
27
|
"@as-integrations/express5": "^1.0.0",
|
|
28
28
|
"@graphql-tools/schema": "latest",
|
|
29
29
|
"@graphql-tools/utils": "^11.0.0",
|
|
30
|
-
"@memberjunction/actions": "5.
|
|
31
|
-
"@memberjunction/actions-apollo": "5.
|
|
32
|
-
"@memberjunction/actions-base": "5.
|
|
33
|
-
"@memberjunction/actions-bizapps-accounting": "5.
|
|
34
|
-
"@memberjunction/actions-bizapps-crm": "5.
|
|
35
|
-
"@memberjunction/actions-bizapps-formbuilders": "5.
|
|
36
|
-
"@memberjunction/actions-bizapps-lms": "5.
|
|
37
|
-
"@memberjunction/actions-bizapps-social": "5.
|
|
38
|
-
"@memberjunction/ai": "5.
|
|
39
|
-
"@memberjunction/ai-agent-manager": "5.
|
|
40
|
-
"@memberjunction/ai-agent-manager-actions": "5.
|
|
41
|
-
"@memberjunction/ai-agents": "5.
|
|
42
|
-
"@memberjunction/ai-bridge-base": "5.
|
|
43
|
-
"@memberjunction/ai-bridge-ringcentral": "5.
|
|
44
|
-
"@memberjunction/ai-bridge-server": "5.
|
|
45
|
-
"@memberjunction/ai-bridge-teams": "5.
|
|
46
|
-
"@memberjunction/ai-bridge-twilio": "5.
|
|
47
|
-
"@memberjunction/ai-bridge-vonage": "5.
|
|
48
|
-
"@memberjunction/ai-core-plus": "5.
|
|
49
|
-
"@memberjunction/ai-engine-base": "5.
|
|
50
|
-
"@memberjunction/ai-mcp-client": "5.
|
|
51
|
-
"@memberjunction/ai-prompts": "5.
|
|
52
|
-
"@memberjunction/ai-provider-bundle": "5.
|
|
53
|
-
"@memberjunction/ai-vector-sync": "5.
|
|
54
|
-
"@memberjunction/ai-vectordb": "5.
|
|
55
|
-
"@memberjunction/ai-vectors-pinecone": "5.
|
|
56
|
-
"@memberjunction/aiengine": "5.
|
|
57
|
-
"@memberjunction/api-keys": "5.
|
|
58
|
-
"@memberjunction/auth-providers": "5.
|
|
59
|
-
"@memberjunction/clustering-engine": "5.
|
|
60
|
-
"@memberjunction/codegen-lib": "5.
|
|
61
|
-
"@memberjunction/communication-engine": "5.
|
|
62
|
-
"@memberjunction/communication-ms-graph": "5.
|
|
63
|
-
"@memberjunction/notifications": "5.
|
|
64
|
-
"@memberjunction/communication-sendgrid": "5.
|
|
65
|
-
"@memberjunction/communication-types": "5.
|
|
66
|
-
"@memberjunction/component-registry-client-sdk": "5.
|
|
67
|
-
"@memberjunction/computer-use": "5.
|
|
68
|
-
"@memberjunction/computer-use-engine": "5.
|
|
69
|
-
"@memberjunction/config": "5.
|
|
70
|
-
"@memberjunction/core": "5.
|
|
71
|
-
"@memberjunction/core-actions": "5.
|
|
72
|
-
"@memberjunction/core-entities": "5.
|
|
73
|
-
"@memberjunction/core-entities-server": "5.
|
|
74
|
-
"@memberjunction/credentials": "5.
|
|
75
|
-
"@memberjunction/data-context": "5.
|
|
76
|
-
"@memberjunction/data-context-server": "5.
|
|
77
|
-
"@memberjunction/doc-utils": "5.
|
|
78
|
-
"@memberjunction/encryption": "5.
|
|
79
|
-
"@memberjunction/entity-communications-base": "5.
|
|
80
|
-
"@memberjunction/entity-communications-server": "5.
|
|
81
|
-
"@memberjunction/esignature": "5.
|
|
82
|
-
"@memberjunction/external-change-detection": "5.
|
|
83
|
-
"@memberjunction/generic-database-provider": "5.
|
|
84
|
-
"@memberjunction/global": "5.
|
|
85
|
-
"@memberjunction/graphql-dataprovider": "5.
|
|
86
|
-
"@memberjunction/integration-engine": "5.
|
|
87
|
-
"@memberjunction/integration-progress-artifacts": "5.
|
|
88
|
-
"@memberjunction/integration-schema-builder": "5.
|
|
89
|
-
"@memberjunction/interactive-component-types": "5.
|
|
90
|
-
"@memberjunction/lists": "5.
|
|
91
|
-
"@memberjunction/lists-base": "5.
|
|
92
|
-
"@memberjunction/livekit-room-server": "5.
|
|
93
|
-
"@memberjunction/postgresql-dataprovider": "5.
|
|
94
|
-
"@memberjunction/queue": "5.
|
|
95
|
-
"@memberjunction/record-comparison": "5.
|
|
96
|
-
"@memberjunction/redis-provider": "5.
|
|
97
|
-
"@memberjunction/remote-browser-base": "5.
|
|
98
|
-
"@memberjunction/remote-browser-cdp": "5.
|
|
99
|
-
"@memberjunction/remote-browser-selfhost": "5.
|
|
100
|
-
"@memberjunction/remote-browser-server": "5.
|
|
101
|
-
"@memberjunction/scheduling-actions": "5.
|
|
102
|
-
"@memberjunction/scheduling-base-types": "5.
|
|
103
|
-
"@memberjunction/scheduling-engine": "5.
|
|
104
|
-
"@memberjunction/scheduling-engine-base": "5.
|
|
105
|
-
"@memberjunction/schema-engine": "5.
|
|
106
|
-
"@memberjunction/search-engine": "5.
|
|
107
|
-
"@memberjunction/server-extensions-core": "5.
|
|
108
|
-
"@memberjunction/sql-dialect": "5.
|
|
109
|
-
"@memberjunction/sqlserver-dataprovider": "5.
|
|
110
|
-
"@memberjunction/storage": "5.
|
|
111
|
-
"@memberjunction/tag-engine": "5.
|
|
112
|
-
"@memberjunction/tag-engine-base": "5.
|
|
113
|
-
"@memberjunction/templates": "5.
|
|
114
|
-
"@memberjunction/testing-engine": "5.
|
|
115
|
-
"@memberjunction/testing-engine-base": "5.
|
|
116
|
-
"@memberjunction/version-history": "5.
|
|
30
|
+
"@memberjunction/actions": "5.48.0",
|
|
31
|
+
"@memberjunction/actions-apollo": "5.48.0",
|
|
32
|
+
"@memberjunction/actions-base": "5.48.0",
|
|
33
|
+
"@memberjunction/actions-bizapps-accounting": "5.48.0",
|
|
34
|
+
"@memberjunction/actions-bizapps-crm": "5.48.0",
|
|
35
|
+
"@memberjunction/actions-bizapps-formbuilders": "5.48.0",
|
|
36
|
+
"@memberjunction/actions-bizapps-lms": "5.48.0",
|
|
37
|
+
"@memberjunction/actions-bizapps-social": "5.48.0",
|
|
38
|
+
"@memberjunction/ai": "5.48.0",
|
|
39
|
+
"@memberjunction/ai-agent-manager": "5.48.0",
|
|
40
|
+
"@memberjunction/ai-agent-manager-actions": "5.48.0",
|
|
41
|
+
"@memberjunction/ai-agents": "5.48.0",
|
|
42
|
+
"@memberjunction/ai-bridge-base": "5.48.0",
|
|
43
|
+
"@memberjunction/ai-bridge-ringcentral": "5.48.0",
|
|
44
|
+
"@memberjunction/ai-bridge-server": "5.48.0",
|
|
45
|
+
"@memberjunction/ai-bridge-teams": "5.48.0",
|
|
46
|
+
"@memberjunction/ai-bridge-twilio": "5.48.0",
|
|
47
|
+
"@memberjunction/ai-bridge-vonage": "5.48.0",
|
|
48
|
+
"@memberjunction/ai-core-plus": "5.48.0",
|
|
49
|
+
"@memberjunction/ai-engine-base": "5.48.0",
|
|
50
|
+
"@memberjunction/ai-mcp-client": "5.48.0",
|
|
51
|
+
"@memberjunction/ai-prompts": "5.48.0",
|
|
52
|
+
"@memberjunction/ai-provider-bundle": "5.48.0",
|
|
53
|
+
"@memberjunction/ai-vector-sync": "5.48.0",
|
|
54
|
+
"@memberjunction/ai-vectordb": "5.48.0",
|
|
55
|
+
"@memberjunction/ai-vectors-pinecone": "5.48.0",
|
|
56
|
+
"@memberjunction/aiengine": "5.48.0",
|
|
57
|
+
"@memberjunction/api-keys": "5.48.0",
|
|
58
|
+
"@memberjunction/auth-providers": "5.48.0",
|
|
59
|
+
"@memberjunction/clustering-engine": "5.48.0",
|
|
60
|
+
"@memberjunction/codegen-lib": "5.48.0",
|
|
61
|
+
"@memberjunction/communication-engine": "5.48.0",
|
|
62
|
+
"@memberjunction/communication-ms-graph": "5.48.0",
|
|
63
|
+
"@memberjunction/notifications": "5.48.0",
|
|
64
|
+
"@memberjunction/communication-sendgrid": "5.48.0",
|
|
65
|
+
"@memberjunction/communication-types": "5.48.0",
|
|
66
|
+
"@memberjunction/component-registry-client-sdk": "5.48.0",
|
|
67
|
+
"@memberjunction/computer-use": "5.48.0",
|
|
68
|
+
"@memberjunction/computer-use-engine": "5.48.0",
|
|
69
|
+
"@memberjunction/config": "5.48.0",
|
|
70
|
+
"@memberjunction/core": "5.48.0",
|
|
71
|
+
"@memberjunction/core-actions": "5.48.0",
|
|
72
|
+
"@memberjunction/core-entities": "5.48.0",
|
|
73
|
+
"@memberjunction/core-entities-server": "5.48.0",
|
|
74
|
+
"@memberjunction/credentials": "5.48.0",
|
|
75
|
+
"@memberjunction/data-context": "5.48.0",
|
|
76
|
+
"@memberjunction/data-context-server": "5.48.0",
|
|
77
|
+
"@memberjunction/doc-utils": "5.48.0",
|
|
78
|
+
"@memberjunction/encryption": "5.48.0",
|
|
79
|
+
"@memberjunction/entity-communications-base": "5.48.0",
|
|
80
|
+
"@memberjunction/entity-communications-server": "5.48.0",
|
|
81
|
+
"@memberjunction/esignature": "5.48.0",
|
|
82
|
+
"@memberjunction/external-change-detection": "5.48.0",
|
|
83
|
+
"@memberjunction/generic-database-provider": "5.48.0",
|
|
84
|
+
"@memberjunction/global": "5.48.0",
|
|
85
|
+
"@memberjunction/graphql-dataprovider": "5.48.0",
|
|
86
|
+
"@memberjunction/integration-engine": "5.48.0",
|
|
87
|
+
"@memberjunction/integration-progress-artifacts": "5.48.0",
|
|
88
|
+
"@memberjunction/integration-schema-builder": "5.48.0",
|
|
89
|
+
"@memberjunction/interactive-component-types": "5.48.0",
|
|
90
|
+
"@memberjunction/lists": "5.48.0",
|
|
91
|
+
"@memberjunction/lists-base": "5.48.0",
|
|
92
|
+
"@memberjunction/livekit-room-server": "5.48.0",
|
|
93
|
+
"@memberjunction/postgresql-dataprovider": "5.48.0",
|
|
94
|
+
"@memberjunction/queue": "5.48.0",
|
|
95
|
+
"@memberjunction/record-comparison": "5.48.0",
|
|
96
|
+
"@memberjunction/redis-provider": "5.48.0",
|
|
97
|
+
"@memberjunction/remote-browser-base": "5.48.0",
|
|
98
|
+
"@memberjunction/remote-browser-cdp": "5.48.0",
|
|
99
|
+
"@memberjunction/remote-browser-selfhost": "5.48.0",
|
|
100
|
+
"@memberjunction/remote-browser-server": "5.48.0",
|
|
101
|
+
"@memberjunction/scheduling-actions": "5.48.0",
|
|
102
|
+
"@memberjunction/scheduling-base-types": "5.48.0",
|
|
103
|
+
"@memberjunction/scheduling-engine": "5.48.0",
|
|
104
|
+
"@memberjunction/scheduling-engine-base": "5.48.0",
|
|
105
|
+
"@memberjunction/schema-engine": "5.48.0",
|
|
106
|
+
"@memberjunction/search-engine": "5.48.0",
|
|
107
|
+
"@memberjunction/server-extensions-core": "5.48.0",
|
|
108
|
+
"@memberjunction/sql-dialect": "5.48.0",
|
|
109
|
+
"@memberjunction/sqlserver-dataprovider": "5.48.0",
|
|
110
|
+
"@memberjunction/storage": "5.48.0",
|
|
111
|
+
"@memberjunction/tag-engine": "5.48.0",
|
|
112
|
+
"@memberjunction/tag-engine-base": "5.48.0",
|
|
113
|
+
"@memberjunction/templates": "5.48.0",
|
|
114
|
+
"@memberjunction/testing-engine": "5.48.0",
|
|
115
|
+
"@memberjunction/testing-engine-base": "5.48.0",
|
|
116
|
+
"@memberjunction/version-history": "5.48.0",
|
|
117
117
|
"@octokit/auth-app": "^7.1.5",
|
|
118
118
|
"@octokit/rest": "^21.1.1",
|
|
119
119
|
"@types/compression": "^1.8.1",
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import type { IncomingMessage } from 'node:http';
|
|
3
|
+
import type { Duplex } from 'node:stream';
|
|
4
|
+
import { RealtimeProxyRegistry, REALTIME_PROXY_PATH } from '@memberjunction/ai';
|
|
5
|
+
import { RealtimeProxyServer } from '../realtimeProxy/RealtimeProxyServer';
|
|
6
|
+
|
|
7
|
+
/** A fake upgrade socket capturing whether it was written to / destroyed (the rejection path). */
|
|
8
|
+
class FakeSocket {
|
|
9
|
+
public Written: string[] = [];
|
|
10
|
+
public Destroyed = false;
|
|
11
|
+
public write(data: string): boolean {
|
|
12
|
+
this.Written.push(data);
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
public destroy(): void {
|
|
16
|
+
this.Destroyed = true;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function req(url: string): IncomingMessage {
|
|
21
|
+
return { url } as unknown as IncomingMessage;
|
|
22
|
+
}
|
|
23
|
+
function sock(fake: FakeSocket): Duplex {
|
|
24
|
+
return fake as unknown as Duplex;
|
|
25
|
+
}
|
|
26
|
+
const HEAD = Buffer.alloc(0);
|
|
27
|
+
|
|
28
|
+
describe('RealtimeProxyServer.TryHandleUpgrade', () => {
|
|
29
|
+
let proxy: RealtimeProxyServer;
|
|
30
|
+
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
proxy = RealtimeProxyServer.Instance;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('does NOT claim a non-proxy path (leaves the socket for the graphql-ws server)', () => {
|
|
36
|
+
const fake = new FakeSocket();
|
|
37
|
+
const owned = proxy.TryHandleUpgrade(req('/graphql'), sock(fake), HEAD);
|
|
38
|
+
expect(owned).toBe(false);
|
|
39
|
+
expect(fake.Destroyed).toBe(false);
|
|
40
|
+
expect(fake.Written).toHaveLength(0);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('claims the proxy path but rejects a MISSING ticket with 401 + destroy', () => {
|
|
44
|
+
const fake = new FakeSocket();
|
|
45
|
+
const owned = proxy.TryHandleUpgrade(req(REALTIME_PROXY_PATH), sock(fake), HEAD);
|
|
46
|
+
expect(owned).toBe(true);
|
|
47
|
+
expect(fake.Written[0]).toContain('401');
|
|
48
|
+
expect(fake.Destroyed).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('rejects an UNKNOWN ticket with 401 + destroy', () => {
|
|
52
|
+
const fake = new FakeSocket();
|
|
53
|
+
const owned = proxy.TryHandleUpgrade(req(`${REALTIME_PROXY_PATH}?ticket=does-not-exist`), sock(fake), HEAD);
|
|
54
|
+
expect(owned).toBe(true);
|
|
55
|
+
expect(fake.Written[0]).toContain('401');
|
|
56
|
+
expect(fake.Destroyed).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('consumes a valid ticket exactly once (single-use)', () => {
|
|
60
|
+
// A valid ticket exists in the shared registry until consumed. We assert single-use by consuming
|
|
61
|
+
// it here first (proving it was live), then confirming the proxy would 401 a second attempt.
|
|
62
|
+
const ticket = RealtimeProxyRegistry.Instance.Issue({ UpstreamUrl: 'ws://hf.internal/v1/realtime', TTLSeconds: 60 });
|
|
63
|
+
expect(RealtimeProxyRegistry.Instance.Consume(ticket.ID)).not.toBeNull();
|
|
64
|
+
|
|
65
|
+
const fake = new FakeSocket();
|
|
66
|
+
const owned = proxy.TryHandleUpgrade(req(`${REALTIME_PROXY_PATH}?ticket=${ticket.ID}`), sock(fake), HEAD);
|
|
67
|
+
expect(owned).toBe(true);
|
|
68
|
+
expect(fake.Destroyed).toBe(true); // already consumed → treated as invalid
|
|
69
|
+
});
|
|
70
|
+
});
|
package/src/context.ts
CHANGED
|
@@ -705,7 +705,12 @@ async function createPerRequestProviders(
|
|
|
705
705
|
{ provider: p, type: 'Read-Write' }
|
|
706
706
|
];
|
|
707
707
|
|
|
708
|
-
if (
|
|
708
|
+
if (isPostgres) {
|
|
709
|
+
const rp = await tryCreateReadOnlyPostgresProvider();
|
|
710
|
+
if (rp) {
|
|
711
|
+
providers.push({ provider: rp, type: 'Read-Only' });
|
|
712
|
+
}
|
|
713
|
+
} else {
|
|
709
714
|
const rp = await tryCreateReadOnlyProvider(dataSources);
|
|
710
715
|
if (rp) {
|
|
711
716
|
providers.push({ provider: rp, type: 'Read-Only' });
|
|
@@ -748,6 +753,46 @@ async function createPostgresProvider(): Promise<DatabaseProviderBase> {
|
|
|
748
753
|
return pgProvider;
|
|
749
754
|
}
|
|
750
755
|
|
|
756
|
+
/**
|
|
757
|
+
* Attempts to create a read-only PostgreSQL provider using DB_READ_ONLY_USERNAME/PASSWORD.
|
|
758
|
+
* Shares the connection pool from the primary provider. Returns null if no read-only credentials are configured.
|
|
759
|
+
*/
|
|
760
|
+
async function tryCreateReadOnlyPostgresProvider(): Promise<DatabaseProviderBase | null> {
|
|
761
|
+
const roUser = process.env.PG_READ_ONLY_USERNAME || process.env.DB_READ_ONLY_USERNAME;
|
|
762
|
+
const roPass = process.env.PG_READ_ONLY_PASSWORD || process.env.DB_READ_ONLY_PASSWORD;
|
|
763
|
+
if (!roUser || !roPass) {
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
try {
|
|
768
|
+
const { PostgreSQLDataProvider, PostgreSQLProviderConfigData } = await import('@memberjunction/postgresql-dataprovider');
|
|
769
|
+
const pgHost = process.env.PG_HOST || process.env.DB_HOST || 'localhost';
|
|
770
|
+
const pgPort = parseInt(process.env.PG_PORT || process.env.DB_PORT || '5432', 10);
|
|
771
|
+
const pgDatabase = process.env.PG_DATABASE || process.env.DB_DATABASE || '';
|
|
772
|
+
|
|
773
|
+
const roProvider = new PostgreSQLDataProvider();
|
|
774
|
+
const roConfig = new PostgreSQLProviderConfigData(
|
|
775
|
+
{ Host: pgHost, Port: pgPort, Database: pgDatabase, User: roUser, Password: roPass },
|
|
776
|
+
mj_core_schema,
|
|
777
|
+
0,
|
|
778
|
+
undefined,
|
|
779
|
+
undefined,
|
|
780
|
+
false,
|
|
781
|
+
);
|
|
782
|
+
|
|
783
|
+
const primaryProvider = Metadata.Provider as unknown as { DatabaseConnection?: import('pg').Pool }; // global-provider-ok: bootstrap (share primary provider's PG pool with the read-only provider)
|
|
784
|
+
if (primaryProvider?.DatabaseConnection) {
|
|
785
|
+
await roProvider.ConfigWithSharedPool(roConfig, primaryProvider.DatabaseConnection);
|
|
786
|
+
} else {
|
|
787
|
+
await roProvider.Config(roConfig);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
return roProvider;
|
|
791
|
+
} catch (_err) {
|
|
792
|
+
return null;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
751
796
|
/**
|
|
752
797
|
* Attempts to create a read-only SQL Server provider.
|
|
753
798
|
* Returns null if no read-only data source is available.
|
package/src/index.ts
CHANGED
|
@@ -26,6 +26,7 @@ import { BuildSchemaOptions, buildSchemaSync, GraphQLTimestamp, PubSubEngine } f
|
|
|
26
26
|
import { PubSub } from 'graphql-subscriptions';
|
|
27
27
|
import sql from 'mssql';
|
|
28
28
|
import { WebSocketServer } from 'ws';
|
|
29
|
+
import { RealtimeProxyServer } from './realtimeProxy/RealtimeProxyServer.js';
|
|
29
30
|
import buildApolloServer from './apolloServer/index.js';
|
|
30
31
|
import { configInfo, configFilePath, dbDatabase, dbHost, dbPort, dbUsername, graphqlPort, graphqlRootPath, mj_core_schema, websiteRunFromPackage, RESTApiOptions } from './config.js';
|
|
31
32
|
import { default as jwt } from 'jsonwebtoken';
|
|
@@ -857,7 +858,31 @@ export const serve = async (resolverPaths: Array<string>, app: Application = cre
|
|
|
857
858
|
|
|
858
859
|
const httpServer = createServer(app);
|
|
859
860
|
|
|
860
|
-
|
|
861
|
+
// `noServer` so we own the HTTP `upgrade` routing ourselves (below) and can dispatch by path between
|
|
862
|
+
// the graphql-ws server and the realtime proxy. (In `{ server, path }` mode, ws destroys any socket whose
|
|
863
|
+
// path doesn't match, which would kill the proxy's `/realtime-proxy` upgrades — hence the explicit router.)
|
|
864
|
+
const webSocketServer = new WebSocketServer({ noServer: true });
|
|
865
|
+
|
|
866
|
+
// Single upgrade router: the realtime proxy claims its own path first (an authenticated byte-tunnel that
|
|
867
|
+
// lets self-hosted realtime providers — e.g. HuggingFace speech-to-speech — run the shipped client-direct
|
|
868
|
+
// audio topology without the internal endpoint ever being exposed to the browser); everything on the
|
|
869
|
+
// graphql path goes to graphql-ws; anything else is rejected. Must be registered before httpServer.listen().
|
|
870
|
+
httpServer.on('upgrade', (request, socket, head) => {
|
|
871
|
+
if (RealtimeProxyServer.Instance.TryHandleUpgrade(request, socket, head as Buffer)) {
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
let pathname = graphqlRootPath;
|
|
875
|
+
try {
|
|
876
|
+
pathname = new URL(request.url ?? graphqlRootPath, 'http://internal').pathname;
|
|
877
|
+
} catch {
|
|
878
|
+
/* unparseable — fall through to the graphql-path check, which will reject it */
|
|
879
|
+
}
|
|
880
|
+
if (pathname === graphqlRootPath) {
|
|
881
|
+
webSocketServer.handleUpgrade(request, socket, head, (ws) => webSocketServer.emit('connection', ws, request));
|
|
882
|
+
} else {
|
|
883
|
+
socket.destroy();
|
|
884
|
+
}
|
|
885
|
+
});
|
|
861
886
|
|
|
862
887
|
// Track per-connection expiry timers so we can clean them up on close
|
|
863
888
|
const expiryTimers = new WeakMap<object, ReturnType<typeof setTimeout>>();
|