@leaven-graphql/ws 0.2.1 → 0.2.3
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 +42 -3
- package/dist/handler.d.ts +22 -1
- package/dist/handler.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +152 -31
- package/dist/logger.d.ts +20 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/manager.d.ts +24 -1
- package/dist/manager.d.ts.map +1 -1
- package/dist/protocol.d.ts +19 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/pubsub.d.ts +4 -0
- package/dist/pubsub.d.ts.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -283,6 +283,11 @@ interface WebSocketHandlerConfig<TContext = unknown> {
|
|
|
283
283
|
keepAliveInterval?: number;
|
|
284
284
|
/** Forwarded to the SubscriptionManager (minus `schema`) */
|
|
285
285
|
subscriptionManager?: Omit<SubscriptionManagerConfig, 'schema'>;
|
|
286
|
+
/**
|
|
287
|
+
* Destination for diagnostics such as failing hooks (default `console`).
|
|
288
|
+
* Passed on to the SubscriptionManager unless it sets its own.
|
|
289
|
+
*/
|
|
290
|
+
logger?: WebSocketLogger;
|
|
286
291
|
/** Return false to reject the connection with 4403 */
|
|
287
292
|
onConnect?: (
|
|
288
293
|
socket: ServerWebSocket<WebSocketContext>,
|
|
@@ -313,7 +318,22 @@ interface WebSocketContext {
|
|
|
313
318
|
Close codes follow the protocol: `4400` invalid message, `4401` unauthorized
|
|
314
319
|
(operation before `connection_init`), `4403` rejected by `onConnect`, `4408`
|
|
315
320
|
init timeout, `4409` duplicate subscription id, `4429` repeated
|
|
316
|
-
`connection_init
|
|
321
|
+
`connection_init` (including a second one sent while `onConnect` for the
|
|
322
|
+
first is still running).
|
|
323
|
+
|
|
324
|
+
An operation is tracked from the moment its `subscribe` frame arrives. A
|
|
325
|
+
client `complete` for an operation that is still setting up cancels it, and
|
|
326
|
+
an operation still setting up when the socket closes is abandoned: in both
|
|
327
|
+
cases nothing is registered and any source stream it obtained is returned.
|
|
328
|
+
Operations still setting up count toward `maxSubscriptionsPerConnection`.
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
interface WebSocketLogger {
|
|
332
|
+
error(...args: unknown[]): void;
|
|
333
|
+
/** Optional; warnings go to `error` without it */
|
|
334
|
+
warn?(...args: unknown[]): void;
|
|
335
|
+
}
|
|
336
|
+
```
|
|
317
337
|
|
|
318
338
|
### SubscriptionManager
|
|
319
339
|
|
|
@@ -334,6 +354,9 @@ class SubscriptionManager {
|
|
|
334
354
|
onError: (errors: readonly { message: string }[]) => void
|
|
335
355
|
): Promise<Subscription>;
|
|
336
356
|
|
|
357
|
+
/** Parse with the executor's limits (maxTokens, maxDepth) */
|
|
358
|
+
parse(query: string): Promise<DocumentNode>;
|
|
359
|
+
|
|
337
360
|
/** Run a query or mutation on the same executor */
|
|
338
361
|
execute<TContext = unknown>(
|
|
339
362
|
request: GraphQLRequest,
|
|
@@ -348,6 +371,8 @@ class SubscriptionManager {
|
|
|
348
371
|
|
|
349
372
|
readonly subscriptionCount: number;
|
|
350
373
|
readonly connectionCount: number;
|
|
374
|
+
/** maxSubscriptionsPerConnection */
|
|
375
|
+
readonly subscriptionLimit: number;
|
|
351
376
|
}
|
|
352
377
|
|
|
353
378
|
function createSubscriptionManager(config: SubscriptionManagerConfig): SubscriptionManager;
|
|
@@ -365,6 +390,8 @@ interface SubscriptionManagerConfig {
|
|
|
365
390
|
* `introspection: false`, `maxDepth` or `maxComplexity`.
|
|
366
391
|
*/
|
|
367
392
|
executor?: LeavenExecutor | Omit<ExecutorConfig, 'schema'>;
|
|
393
|
+
/** Destination for timeout and callback diagnostics (default `console`) */
|
|
394
|
+
logger?: WebSocketLogger;
|
|
368
395
|
}
|
|
369
396
|
|
|
370
397
|
interface Subscription {
|
|
@@ -380,7 +407,9 @@ interface Subscription {
|
|
|
380
407
|
|
|
381
408
|
Subscription ids are scoped to a connection: `graphql-ws` clients number their
|
|
382
409
|
operations per connection (starting at `"1"`), so every lookup and teardown
|
|
383
|
-
takes **both** the connection id and the subscription id.
|
|
410
|
+
takes **both** the connection id and the subscription id. `subscribe` throws
|
|
411
|
+
when the id is already registered on that connection rather than overwriting
|
|
412
|
+
it.
|
|
384
413
|
|
|
385
414
|
### PubSub
|
|
386
415
|
|
|
@@ -409,6 +438,8 @@ interface PubSubConfig {
|
|
|
409
438
|
* events instead of an unbounded queue. Unlimited by default.
|
|
410
439
|
*/
|
|
411
440
|
maxQueueSize?: number;
|
|
441
|
+
/** Destination for subscriber callback errors (default `console`) */
|
|
442
|
+
logger?: WebSocketLogger;
|
|
412
443
|
}
|
|
413
444
|
```
|
|
414
445
|
|
|
@@ -435,6 +466,10 @@ function createErrorMessage(
|
|
|
435
466
|
function createCompleteMessage(id: string): CompleteMessage;
|
|
436
467
|
function createPongMessage(payload?: Record<string, unknown>): PongMessage;
|
|
437
468
|
|
|
469
|
+
/** `/^[A-Za-z0-9_.:-]{1,64}$/`: the accepted shape of a client operation id */
|
|
470
|
+
const OPERATION_ID_PATTERN: RegExp;
|
|
471
|
+
function isValidOperationId(id: unknown): id is string;
|
|
472
|
+
|
|
438
473
|
interface ParseMessageOptions {
|
|
439
474
|
/**
|
|
440
475
|
* Require a string `id` on the types that carry one (`subscribe`, `next`,
|
|
@@ -463,7 +498,11 @@ const message = parseMessage(data, { requireId: false });
|
|
|
463
498
|
|
|
464
499
|
`parseMessage` throws on invalid JSON, on JSON that is not an object, on a
|
|
465
500
|
missing or unknown `type`, and — unless `requireId` is `false` — on a missing
|
|
466
|
-
`id
|
|
501
|
+
`id`, or on a `subscribe`/`complete` id that does not match
|
|
502
|
+
`OPERATION_ID_PATTERN` (`Invalid message`). The server closes the socket with
|
|
503
|
+
4400 in every one of those cases. Ids are client-chosen and reach close
|
|
504
|
+
reasons and log lines, so the allow-list keeps control characters and
|
|
505
|
+
oversized strings out of both; UUIDs, counters and `prefix:n` ids all pass.
|
|
467
506
|
|
|
468
507
|
### Message Types
|
|
469
508
|
|
package/dist/handler.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { type GraphQLSchema } from 'graphql';
|
|
|
8
8
|
import type { ServerWebSocket } from 'bun';
|
|
9
9
|
import type { GraphQLRequest } from '@leaven-graphql/core';
|
|
10
10
|
import { type SubscriptionManagerConfig } from './manager';
|
|
11
|
+
import type { WebSocketLogger } from './logger';
|
|
11
12
|
/**
|
|
12
13
|
* WebSocket context data
|
|
13
14
|
*/
|
|
@@ -39,6 +40,12 @@ export interface WebSocketHandlerConfig<TContext = unknown> {
|
|
|
39
40
|
keepAliveInterval?: number;
|
|
40
41
|
/** Subscription manager config */
|
|
41
42
|
subscriptionManager?: Omit<SubscriptionManagerConfig, 'schema'>;
|
|
43
|
+
/**
|
|
44
|
+
* Where diagnostics (failing hooks and callbacks) are logged. Defaults to
|
|
45
|
+
* `console`. Also passed to the subscription manager unless
|
|
46
|
+
* `subscriptionManager.logger` is set.
|
|
47
|
+
*/
|
|
48
|
+
logger?: WebSocketLogger;
|
|
42
49
|
/** Called when a connection is initialized */
|
|
43
50
|
onConnect?: (socket: ServerWebSocket<WebSocketContext>, params?: Record<string, unknown>) => boolean | Promise<boolean>;
|
|
44
51
|
/** Called when a connection is closed */
|
|
@@ -61,9 +68,19 @@ export declare class WebSocketHandler<TContext = unknown> {
|
|
|
61
68
|
private readonly onDisconnect?;
|
|
62
69
|
private readonly onSubscribe?;
|
|
63
70
|
private readonly onComplete?;
|
|
71
|
+
private readonly logger;
|
|
64
72
|
private readonly initTimeouts;
|
|
65
73
|
private readonly keepAliveTimers;
|
|
74
|
+
private readonly states;
|
|
66
75
|
constructor(config: WebSocketHandlerConfig<TContext>);
|
|
76
|
+
/**
|
|
77
|
+
* Lifecycle state for a socket, created on first use.
|
|
78
|
+
*/
|
|
79
|
+
private stateOf;
|
|
80
|
+
/**
|
|
81
|
+
* Stop and forget a connection's keep-alive timer, if it has one.
|
|
82
|
+
*/
|
|
83
|
+
private clearKeepAlive;
|
|
67
84
|
/**
|
|
68
85
|
* Generate a unique connection ID
|
|
69
86
|
*/
|
|
@@ -104,7 +121,11 @@ export declare class WebSocketHandler<TContext = unknown> {
|
|
|
104
121
|
* A document graphql-js cannot resolve to a single operation (no operations
|
|
105
122
|
* at all, or several with no `operationName`) is reported as a subscription
|
|
106
123
|
* so the executor produces the precise error rather than this method
|
|
107
|
-
* guessing at one.
|
|
124
|
+
* guessing at one. The same goes for a document that fails to parse.
|
|
125
|
+
*
|
|
126
|
+
* Parsing goes through the executor, so the configured `maxTokens` and
|
|
127
|
+
* depth limits apply here too; a plain `parse()` would lex an arbitrarily
|
|
128
|
+
* large document before the executor's limits ever ran.
|
|
108
129
|
*/
|
|
109
130
|
private isSubscriptionOperation;
|
|
110
131
|
/**
|
package/dist/handler.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export type { WebSocketHandlerConfig, WebSocketContext } from './handler';
|
|
|
9
9
|
export { SubscriptionManager, createSubscriptionManager } from './manager';
|
|
10
10
|
export type { SubscriptionManagerConfig, Subscription, SubscriptionStatus } from './manager';
|
|
11
11
|
export { PubSub, createPubSub } from './pubsub';
|
|
12
|
+
export type { WebSocketLogger } from './logger';
|
|
12
13
|
export type { PubSubConfig, PubSubEngine, SubscribeFn, PublishFn } from './pubsub';
|
|
13
|
-
export { MessageType, parseMessage, formatMessage, createConnectionAck, createNextMessage, createErrorMessage, createCompleteMessage, createPongMessage, type ConnectionInitMessage, type ConnectionAckMessage, type SubscribeMessage, type NextMessage, type ErrorMessage, type CompleteMessage, type PingMessage, type PongMessage, type ParseMessageOptions, } from './protocol';
|
|
14
|
+
export { MessageType, parseMessage, formatMessage, createConnectionAck, createNextMessage, createErrorMessage, createCompleteMessage, createPongMessage, isValidOperationId, OPERATION_ID_PATTERN, type ConnectionInitMessage, type ConnectionAckMessage, type SubscribeMessage, type NextMessage, type ErrorMessage, type CompleteMessage, type PingMessage, type PongMessage, type ParseMessageOptions, } from './protocol';
|
|
14
15
|
//# 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;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACrE,YAAY,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE1E,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AAC3E,YAAY,EAAE,yBAAyB,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE7F,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAChD,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAEnF,OAAO,EACL,WAAW,EACX,YAAY,EACZ,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,mBAAmB,GACzB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACrE,YAAY,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE1E,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AAC3E,YAAY,EAAE,yBAAyB,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE7F,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAChD,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAChD,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAEnF,OAAO,EACL,WAAW,EACX,YAAY,EACZ,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,EACpB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,mBAAmB,GACzB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,22 +1,41 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// src/handler.ts
|
|
3
|
-
import { OperationTypeNode, getOperationAST
|
|
3
|
+
import { OperationTypeNode, getOperationAST } from "graphql";
|
|
4
4
|
|
|
5
5
|
// src/manager.ts
|
|
6
6
|
import {
|
|
7
|
-
LeavenExecutor
|
|
7
|
+
LeavenExecutor,
|
|
8
|
+
escapeLog
|
|
8
9
|
} from "@leaven-graphql/core";
|
|
9
10
|
|
|
11
|
+
// src/logger.ts
|
|
12
|
+
function logWarning(logger, ...args) {
|
|
13
|
+
if (logger.warn) {
|
|
14
|
+
logger.warn(...args);
|
|
15
|
+
} else {
|
|
16
|
+
logger.error(...args);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/manager.ts
|
|
10
21
|
class SubscriptionManager {
|
|
11
22
|
executor;
|
|
12
23
|
connections;
|
|
13
24
|
maxSubscriptionsPerConnection;
|
|
14
25
|
subscriptionTimeout;
|
|
26
|
+
logger;
|
|
15
27
|
constructor(config) {
|
|
16
28
|
this.executor = config.executor instanceof LeavenExecutor ? config.executor : new LeavenExecutor({ ...config.executor, schema: config.schema });
|
|
17
29
|
this.connections = new Map;
|
|
18
30
|
this.maxSubscriptionsPerConnection = config.maxSubscriptionsPerConnection ?? 100;
|
|
19
31
|
this.subscriptionTimeout = config.subscriptionTimeout ?? 0;
|
|
32
|
+
this.logger = config.logger ?? console;
|
|
33
|
+
}
|
|
34
|
+
get subscriptionLimit() {
|
|
35
|
+
return this.maxSubscriptionsPerConnection;
|
|
36
|
+
}
|
|
37
|
+
parse(query) {
|
|
38
|
+
return this.executor.parse(query);
|
|
20
39
|
}
|
|
21
40
|
async execute(request, context) {
|
|
22
41
|
const { response } = await this.executor.execute(request, context);
|
|
@@ -27,6 +46,9 @@ class SubscriptionManager {
|
|
|
27
46
|
if (existing && existing.size >= this.maxSubscriptionsPerConnection) {
|
|
28
47
|
throw new Error("Maximum subscriptions per connection reached");
|
|
29
48
|
}
|
|
49
|
+
if (existing?.has(subscriptionId)) {
|
|
50
|
+
throw new Error(`Subscription ${escapeLog(subscriptionId)} already exists`);
|
|
51
|
+
}
|
|
30
52
|
const subscription = {
|
|
31
53
|
id: subscriptionId,
|
|
32
54
|
connectionId,
|
|
@@ -42,6 +64,13 @@ class SubscriptionManager {
|
|
|
42
64
|
connectionSubs.set(subscriptionId, subscription);
|
|
43
65
|
try {
|
|
44
66
|
const result = await this.executor.subscribe(request, context);
|
|
67
|
+
if (!this.isCurrent(subscription)) {
|
|
68
|
+
subscription.status = "completed";
|
|
69
|
+
if (Symbol.asyncIterator in Object(result)) {
|
|
70
|
+
result.return?.()?.catch(() => {});
|
|
71
|
+
}
|
|
72
|
+
return subscription;
|
|
73
|
+
}
|
|
45
74
|
if (!(Symbol.asyncIterator in Object(result))) {
|
|
46
75
|
subscription.status = "error";
|
|
47
76
|
const errors = result.errors ?? [{ message: "Subscription failed" }];
|
|
@@ -57,15 +86,17 @@ class SubscriptionManager {
|
|
|
57
86
|
timeoutId = setTimeout(() => {
|
|
58
87
|
try {
|
|
59
88
|
const timedOut = this.getSubscription(connectionId, subscriptionId);
|
|
60
|
-
if (timedOut && timedOut.status === "active") {
|
|
89
|
+
if (timedOut === subscription && timedOut.status === "active") {
|
|
61
90
|
timedOut.status = "completed";
|
|
62
|
-
|
|
91
|
+
logWarning(this.logger, `Subscription ${escapeLog(subscriptionId)} timed out after ${this.subscriptionTimeout}ms`);
|
|
63
92
|
onComplete();
|
|
64
93
|
}
|
|
65
94
|
} catch (error) {
|
|
66
|
-
|
|
95
|
+
this.logger.error(`Error completing timed-out subscription ${escapeLog(subscriptionId)}:`, error);
|
|
67
96
|
} finally {
|
|
68
|
-
this.
|
|
97
|
+
if (this.isCurrent(subscription)) {
|
|
98
|
+
this.unsubscribe(connectionId, subscriptionId);
|
|
99
|
+
}
|
|
69
100
|
}
|
|
70
101
|
}, this.subscriptionTimeout);
|
|
71
102
|
}
|
|
@@ -74,9 +105,13 @@ class SubscriptionManager {
|
|
|
74
105
|
clearTimeout(timeoutId);
|
|
75
106
|
}
|
|
76
107
|
};
|
|
77
|
-
this.consumeIterator(
|
|
108
|
+
this.consumeIterator(subscription, iterator, onNext, onComplete, onError);
|
|
78
109
|
return subscription;
|
|
79
110
|
} catch (error) {
|
|
111
|
+
if (!this.isCurrent(subscription)) {
|
|
112
|
+
subscription.status = "completed";
|
|
113
|
+
return subscription;
|
|
114
|
+
}
|
|
80
115
|
subscription.status = "error";
|
|
81
116
|
const message = error instanceof Error ? error.message : "Subscription failed";
|
|
82
117
|
onError([{ message }]);
|
|
@@ -84,24 +119,25 @@ class SubscriptionManager {
|
|
|
84
119
|
return subscription;
|
|
85
120
|
}
|
|
86
121
|
}
|
|
87
|
-
|
|
122
|
+
isCurrent(subscription) {
|
|
123
|
+
return this.getSubscription(subscription.connectionId, subscription.id) === subscription;
|
|
124
|
+
}
|
|
125
|
+
async consumeIterator(subscription, iterator, onNext, onComplete, onError) {
|
|
126
|
+
const { connectionId, id: subscriptionId } = subscription;
|
|
88
127
|
try {
|
|
89
128
|
for await (const result of iterator) {
|
|
90
|
-
|
|
91
|
-
if (!subscription || subscription.status !== "active") {
|
|
129
|
+
if (!this.isCurrent(subscription) || subscription.status !== "active") {
|
|
92
130
|
break;
|
|
93
131
|
}
|
|
94
132
|
onNext(result);
|
|
95
133
|
}
|
|
96
|
-
|
|
97
|
-
if (subscription) {
|
|
134
|
+
if (this.isCurrent(subscription)) {
|
|
98
135
|
subscription.status = "completed";
|
|
99
136
|
onComplete();
|
|
100
137
|
this.unsubscribe(connectionId, subscriptionId);
|
|
101
138
|
}
|
|
102
139
|
} catch (error) {
|
|
103
|
-
|
|
104
|
-
if (subscription) {
|
|
140
|
+
if (this.isCurrent(subscription)) {
|
|
105
141
|
subscription.status = "error";
|
|
106
142
|
const message = error instanceof Error ? error.message : "Subscription error";
|
|
107
143
|
onError([{ message }]);
|
|
@@ -166,6 +202,7 @@ function createSubscriptionManager(config) {
|
|
|
166
202
|
}
|
|
167
203
|
|
|
168
204
|
// src/protocol.ts
|
|
205
|
+
import { escapeLog as escapeLog2 } from "@leaven-graphql/core";
|
|
169
206
|
var MessageType;
|
|
170
207
|
((MessageType) => {
|
|
171
208
|
MessageType["ConnectionInit"] = "connection_init";
|
|
@@ -184,6 +221,14 @@ var ID_REQUIRED_TYPES = new Set([
|
|
|
184
221
|
"error" /* Error */,
|
|
185
222
|
"complete" /* Complete */
|
|
186
223
|
]);
|
|
224
|
+
var CLIENT_ID_TYPES = new Set([
|
|
225
|
+
"subscribe" /* Subscribe */,
|
|
226
|
+
"complete" /* Complete */
|
|
227
|
+
]);
|
|
228
|
+
var OPERATION_ID_PATTERN = /^[A-Za-z0-9_.:-]{1,64}$/;
|
|
229
|
+
function isValidOperationId(id) {
|
|
230
|
+
return typeof id === "string" && OPERATION_ID_PATTERN.test(id);
|
|
231
|
+
}
|
|
187
232
|
function parseMessage(data, options = {}) {
|
|
188
233
|
const requireId = options.requireId ?? true;
|
|
189
234
|
const text = typeof data === "string" ? data : data.toString("utf-8");
|
|
@@ -197,11 +242,14 @@ function parseMessage(data, options = {}) {
|
|
|
197
242
|
throw new Error("Message must have a type");
|
|
198
243
|
}
|
|
199
244
|
if (typeof message.type !== "string" || !VALID_MESSAGE_TYPES.has(message.type)) {
|
|
200
|
-
throw new Error(`Invalid message type: ${
|
|
245
|
+
throw new Error(`Invalid message type: ${escapeLog2(message.type, 12)}`);
|
|
201
246
|
}
|
|
202
247
|
if (requireId && ID_REQUIRED_TYPES.has(message.type) && typeof message.id !== "string") {
|
|
203
248
|
throw new Error(`Message of type "${message.type}" must have a string id`);
|
|
204
249
|
}
|
|
250
|
+
if (requireId && CLIENT_ID_TYPES.has(message.type) && !isValidOperationId(message.id)) {
|
|
251
|
+
throw new Error("Invalid message");
|
|
252
|
+
}
|
|
205
253
|
return message;
|
|
206
254
|
} catch (error) {
|
|
207
255
|
if (error instanceof SyntaxError) {
|
|
@@ -260,15 +308,19 @@ class WebSocketHandler {
|
|
|
260
308
|
onDisconnect;
|
|
261
309
|
onSubscribe;
|
|
262
310
|
onComplete;
|
|
311
|
+
logger;
|
|
263
312
|
initTimeouts;
|
|
264
313
|
keepAliveTimers;
|
|
314
|
+
states;
|
|
265
315
|
constructor(config) {
|
|
266
316
|
this.schema = config.schema;
|
|
267
317
|
this.contextFactory = config.context;
|
|
268
318
|
this.connectionInitTimeout = config.connectionInitTimeout ?? 3000;
|
|
269
319
|
this.keepAliveInterval = config.keepAliveInterval ?? 12000;
|
|
320
|
+
this.logger = config.logger ?? console;
|
|
270
321
|
this.subscriptionManager = new SubscriptionManager({
|
|
271
322
|
schema: config.schema,
|
|
323
|
+
logger: this.logger,
|
|
272
324
|
...config.subscriptionManager
|
|
273
325
|
});
|
|
274
326
|
this.onConnect = config.onConnect;
|
|
@@ -277,6 +329,22 @@ class WebSocketHandler {
|
|
|
277
329
|
this.onComplete = config.onComplete;
|
|
278
330
|
this.initTimeouts = new Map;
|
|
279
331
|
this.keepAliveTimers = new Map;
|
|
332
|
+
this.states = new WeakMap;
|
|
333
|
+
}
|
|
334
|
+
stateOf(socket) {
|
|
335
|
+
let state = this.states.get(socket);
|
|
336
|
+
if (!state) {
|
|
337
|
+
state = { initializing: false, closed: false, pending: new Map };
|
|
338
|
+
this.states.set(socket, state);
|
|
339
|
+
}
|
|
340
|
+
return state;
|
|
341
|
+
}
|
|
342
|
+
clearKeepAlive(connectionId) {
|
|
343
|
+
const timer = this.keepAliveTimers.get(connectionId);
|
|
344
|
+
if (timer) {
|
|
345
|
+
clearInterval(timer);
|
|
346
|
+
this.keepAliveTimers.delete(connectionId);
|
|
347
|
+
}
|
|
280
348
|
}
|
|
281
349
|
generateConnectionId() {
|
|
282
350
|
return crypto.randomUUID();
|
|
@@ -286,11 +354,11 @@ class WebSocketHandler {
|
|
|
286
354
|
const result = invoke();
|
|
287
355
|
if (result instanceof Promise) {
|
|
288
356
|
result.catch((error) => {
|
|
289
|
-
|
|
357
|
+
this.logger.error(`${name} failed:`, error);
|
|
290
358
|
});
|
|
291
359
|
}
|
|
292
360
|
} catch (error) {
|
|
293
|
-
|
|
361
|
+
this.logger.error(`${name} failed:`, error);
|
|
294
362
|
}
|
|
295
363
|
}
|
|
296
364
|
handleOpen(socket) {
|
|
@@ -300,6 +368,7 @@ class WebSocketHandler {
|
|
|
300
368
|
initialized: false,
|
|
301
369
|
subscriptions: new Set
|
|
302
370
|
};
|
|
371
|
+
this.states.set(socket, { initializing: false, closed: false, pending: new Map });
|
|
303
372
|
const timeout = setTimeout(() => {
|
|
304
373
|
if (!socket.data.initialized) {
|
|
305
374
|
socket.close(4408, "Connection initialization timeout");
|
|
@@ -344,10 +413,12 @@ class WebSocketHandler {
|
|
|
344
413
|
}
|
|
345
414
|
}
|
|
346
415
|
async handleConnectionInit(socket, params) {
|
|
347
|
-
|
|
416
|
+
const state = this.stateOf(socket);
|
|
417
|
+
if (state.initializing || socket.data.initialized) {
|
|
348
418
|
socket.close(4429, "Too many initialisation requests");
|
|
349
419
|
return;
|
|
350
420
|
}
|
|
421
|
+
state.initializing = true;
|
|
351
422
|
const timeout = this.initTimeouts.get(socket.data.connectionId);
|
|
352
423
|
if (timeout) {
|
|
353
424
|
clearTimeout(timeout);
|
|
@@ -355,6 +426,9 @@ class WebSocketHandler {
|
|
|
355
426
|
}
|
|
356
427
|
if (this.onConnect) {
|
|
357
428
|
const allowed = await this.onConnect(socket, params);
|
|
429
|
+
if (state.closed) {
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
358
432
|
if (!allowed) {
|
|
359
433
|
socket.close(4403, "Forbidden");
|
|
360
434
|
return;
|
|
@@ -364,6 +438,7 @@ class WebSocketHandler {
|
|
|
364
438
|
socket.data.initialized = true;
|
|
365
439
|
socket.send(formatMessage(createConnectionAck()));
|
|
366
440
|
if (this.keepAliveInterval > 0) {
|
|
441
|
+
this.clearKeepAlive(socket.data.connectionId);
|
|
367
442
|
const timer = setInterval(() => {
|
|
368
443
|
socket.send(formatMessage({ type: "ping" /* Ping */ }));
|
|
369
444
|
}, this.keepAliveInterval);
|
|
@@ -372,31 +447,54 @@ class WebSocketHandler {
|
|
|
372
447
|
}
|
|
373
448
|
async handleSubscribe(socket, message) {
|
|
374
449
|
const { id, payload } = message;
|
|
450
|
+
const state = this.stateOf(socket);
|
|
375
451
|
if (socket.data.subscriptions.has(id)) {
|
|
376
452
|
socket.close(4409, `Subscriber for ${id} already exists`);
|
|
377
453
|
return;
|
|
378
454
|
}
|
|
455
|
+
if (socket.data.subscriptions.size >= this.subscriptionManager.subscriptionLimit) {
|
|
456
|
+
socket.send(formatMessage(createErrorMessage(id, [{ message: "Maximum subscriptions per connection reached" }])));
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
379
459
|
socket.data.subscriptions.add(id);
|
|
460
|
+
const token = { cancelled: false };
|
|
461
|
+
state.pending.set(id, token);
|
|
462
|
+
const settle = () => {
|
|
463
|
+
if (state.pending.get(id) === token) {
|
|
464
|
+
state.pending.delete(id);
|
|
465
|
+
}
|
|
466
|
+
};
|
|
380
467
|
const request = {
|
|
381
468
|
query: payload.query,
|
|
382
469
|
operationName: payload.operationName,
|
|
383
470
|
variables: payload.variables,
|
|
384
471
|
extensions: payload.extensions
|
|
385
472
|
};
|
|
473
|
+
const abandoned = () => state.closed || token.cancelled;
|
|
386
474
|
try {
|
|
387
475
|
await this.onSubscribe?.(socket, id, request);
|
|
476
|
+
if (abandoned())
|
|
477
|
+
return;
|
|
388
478
|
let context;
|
|
389
479
|
if (this.contextFactory) {
|
|
390
480
|
context = await this.contextFactory(socket, request);
|
|
481
|
+
if (abandoned())
|
|
482
|
+
return;
|
|
391
483
|
}
|
|
392
|
-
|
|
484
|
+
const isSubscription = await this.isSubscriptionOperation(request);
|
|
485
|
+
if (abandoned())
|
|
486
|
+
return;
|
|
487
|
+
if (!isSubscription) {
|
|
393
488
|
const response = await this.subscriptionManager.execute(request, context);
|
|
489
|
+
if (abandoned())
|
|
490
|
+
return;
|
|
394
491
|
if (response.data === undefined && response.errors && response.errors.length > 0) {
|
|
395
492
|
socket.send(formatMessage(createErrorMessage(id, response.errors)));
|
|
396
493
|
} else {
|
|
397
494
|
socket.send(formatMessage(createNextMessage(id, response.data, response.errors)));
|
|
398
495
|
socket.send(formatMessage(createCompleteMessage(id)));
|
|
399
496
|
}
|
|
497
|
+
settle();
|
|
400
498
|
socket.data.subscriptions.delete(id);
|
|
401
499
|
this.invokeHook("onComplete hook", () => this.onComplete?.(socket, id));
|
|
402
500
|
return;
|
|
@@ -412,35 +510,52 @@ class WebSocketHandler {
|
|
|
412
510
|
socket.data.subscriptions.delete(id);
|
|
413
511
|
this.invokeHook("onComplete hook", () => this.onComplete?.(socket, id));
|
|
414
512
|
});
|
|
513
|
+
settle();
|
|
415
514
|
} catch (error) {
|
|
515
|
+
if (abandoned())
|
|
516
|
+
return;
|
|
517
|
+
settle();
|
|
416
518
|
const errorText = error instanceof Error ? error.message : "Subscription failed";
|
|
417
519
|
socket.send(formatMessage(createErrorMessage(id, [{ message: errorText }])));
|
|
418
520
|
socket.data.subscriptions.delete(id);
|
|
419
521
|
this.invokeHook("onComplete hook", () => this.onComplete?.(socket, id));
|
|
420
522
|
}
|
|
421
523
|
}
|
|
422
|
-
isSubscriptionOperation(request) {
|
|
423
|
-
|
|
524
|
+
async isSubscriptionOperation(request) {
|
|
525
|
+
let document;
|
|
526
|
+
try {
|
|
527
|
+
document = await this.subscriptionManager.parse(request.query);
|
|
528
|
+
} catch {
|
|
529
|
+
return true;
|
|
530
|
+
}
|
|
424
531
|
const operation = getOperationAST(document, request.operationName ?? null);
|
|
425
532
|
return !operation || operation.operation === OperationTypeNode.SUBSCRIPTION;
|
|
426
533
|
}
|
|
427
534
|
handleComplete(socket, subscriptionId) {
|
|
535
|
+
const state = this.stateOf(socket);
|
|
536
|
+
const pending = state.pending.get(subscriptionId);
|
|
537
|
+
if (pending) {
|
|
538
|
+
pending.cancelled = true;
|
|
539
|
+
state.pending.delete(subscriptionId);
|
|
540
|
+
}
|
|
428
541
|
socket.data.subscriptions.delete(subscriptionId);
|
|
429
542
|
this.subscriptionManager.unsubscribe(socket.data.connectionId, subscriptionId);
|
|
430
543
|
this.invokeHook("onComplete hook", () => this.onComplete?.(socket, subscriptionId));
|
|
431
544
|
}
|
|
432
545
|
async handleClose(socket) {
|
|
433
546
|
const { connectionId } = socket.data;
|
|
547
|
+
const state = this.stateOf(socket);
|
|
548
|
+
state.closed = true;
|
|
549
|
+
for (const pending of state.pending.values()) {
|
|
550
|
+
pending.cancelled = true;
|
|
551
|
+
}
|
|
552
|
+
state.pending.clear();
|
|
434
553
|
const initTimeout = this.initTimeouts.get(connectionId);
|
|
435
554
|
if (initTimeout) {
|
|
436
555
|
clearTimeout(initTimeout);
|
|
437
556
|
this.initTimeouts.delete(connectionId);
|
|
438
557
|
}
|
|
439
|
-
|
|
440
|
-
if (keepAliveTimer) {
|
|
441
|
-
clearInterval(keepAliveTimer);
|
|
442
|
-
this.keepAliveTimers.delete(connectionId);
|
|
443
|
-
}
|
|
558
|
+
this.clearKeepAlive(connectionId);
|
|
444
559
|
this.subscriptionManager.unsubscribeConnection(connectionId);
|
|
445
560
|
socket.data.subscriptions.clear();
|
|
446
561
|
await this.onDisconnect?.(socket);
|
|
@@ -450,12 +565,12 @@ class WebSocketHandler {
|
|
|
450
565
|
open: (socket) => this.handleOpen(socket),
|
|
451
566
|
message: (socket, message) => {
|
|
452
567
|
this.handleMessage(socket, message).catch((error) => {
|
|
453
|
-
|
|
568
|
+
this.logger.error("WebSocket message handling failed:", error);
|
|
454
569
|
});
|
|
455
570
|
},
|
|
456
571
|
close: (socket) => {
|
|
457
572
|
this.handleClose(socket).catch((error) => {
|
|
458
|
-
|
|
573
|
+
this.logger.error("onDisconnect failed:", error);
|
|
459
574
|
});
|
|
460
575
|
}
|
|
461
576
|
};
|
|
@@ -465,12 +580,15 @@ function createWebSocketHandler(config) {
|
|
|
465
580
|
return new WebSocketHandler(config);
|
|
466
581
|
}
|
|
467
582
|
// src/pubsub.ts
|
|
583
|
+
import { escapeLog as escapeLog3 } from "@leaven-graphql/core";
|
|
584
|
+
|
|
468
585
|
class PubSub {
|
|
469
586
|
subscribers;
|
|
470
587
|
wildcardPatterns;
|
|
471
588
|
maxSubscribersPerTopic;
|
|
472
589
|
wildcards;
|
|
473
590
|
maxQueueSize;
|
|
591
|
+
logger;
|
|
474
592
|
nextId;
|
|
475
593
|
constructor(config = {}) {
|
|
476
594
|
this.subscribers = new Map;
|
|
@@ -478,6 +596,7 @@ class PubSub {
|
|
|
478
596
|
this.maxSubscribersPerTopic = config.maxSubscribersPerTopic ?? 1e4;
|
|
479
597
|
this.wildcards = config.wildcards ?? false;
|
|
480
598
|
this.maxQueueSize = config.maxQueueSize ?? Infinity;
|
|
599
|
+
this.logger = config.logger ?? console;
|
|
481
600
|
this.nextId = 0;
|
|
482
601
|
}
|
|
483
602
|
subscribe(topic, callback) {
|
|
@@ -493,7 +612,7 @@ class PubSub {
|
|
|
493
612
|
}
|
|
494
613
|
}
|
|
495
614
|
if (topicSubscribers.size >= this.maxSubscribersPerTopic) {
|
|
496
|
-
throw new Error(`Maximum subscribers reached for topic: ${topic}`);
|
|
615
|
+
throw new Error(`Maximum subscribers reached for topic: ${escapeLog3(topic)}`);
|
|
497
616
|
}
|
|
498
617
|
const subscriber = {
|
|
499
618
|
id: String(this.nextId++),
|
|
@@ -519,7 +638,7 @@ class PubSub {
|
|
|
519
638
|
try {
|
|
520
639
|
subscriber.callback(payload);
|
|
521
640
|
} catch (error) {
|
|
522
|
-
|
|
641
|
+
this.logger.error("Error in subscriber callback:", error);
|
|
523
642
|
}
|
|
524
643
|
}
|
|
525
644
|
}
|
|
@@ -542,7 +661,7 @@ class PubSub {
|
|
|
542
661
|
try {
|
|
543
662
|
subscriber.callback(payload);
|
|
544
663
|
} catch (error) {
|
|
545
|
-
|
|
664
|
+
this.logger.error("Error in wildcard subscriber callback:", error);
|
|
546
665
|
}
|
|
547
666
|
}
|
|
548
667
|
}
|
|
@@ -645,6 +764,7 @@ function createPubSub(config) {
|
|
|
645
764
|
}
|
|
646
765
|
export {
|
|
647
766
|
MessageType,
|
|
767
|
+
OPERATION_ID_PATTERN,
|
|
648
768
|
PubSub,
|
|
649
769
|
SubscriptionManager,
|
|
650
770
|
WebSocketHandler,
|
|
@@ -657,5 +777,6 @@ export {
|
|
|
657
777
|
createSubscriptionManager,
|
|
658
778
|
createWebSocketHandler,
|
|
659
779
|
formatMessage,
|
|
780
|
+
isValidOperationId,
|
|
660
781
|
parseMessage
|
|
661
782
|
};
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @leaven-graphql/ws - Logger interface
|
|
3
|
+
*
|
|
4
|
+
* Copyright 2026 Joseph Quinn
|
|
5
|
+
* Licensed under the Apache License, Version 2.0
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Destination for the diagnostics the WebSocket transport emits: failing
|
|
9
|
+
* hooks, subscriber callbacks and subscription timeouts. Defaults to
|
|
10
|
+
* `console`. `warn` is optional; without it, warnings go to `error`.
|
|
11
|
+
*/
|
|
12
|
+
export interface WebSocketLogger {
|
|
13
|
+
error(...args: unknown[]): void;
|
|
14
|
+
warn?(...args: unknown[]): void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Emit a warning, falling back to `error` when the logger has no `warn`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function logWarning(logger: WebSocketLogger, ...args: unknown[]): void;
|
|
20
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChC,IAAI,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CACjC;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAM5E"}
|
package/dist/manager.d.ts
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Copyright 2026 Joseph Quinn
|
|
5
5
|
* Licensed under the Apache License, Version 2.0
|
|
6
6
|
*/
|
|
7
|
-
import type { GraphQLSchema } from 'graphql';
|
|
7
|
+
import type { DocumentNode, GraphQLSchema } from 'graphql';
|
|
8
8
|
import { LeavenExecutor, type ExecutorConfig, type GraphQLRequest, type GraphQLResponse } from '@leaven-graphql/core';
|
|
9
|
+
import { type WebSocketLogger } from './logger';
|
|
9
10
|
/**
|
|
10
11
|
* Subscription status
|
|
11
12
|
*/
|
|
@@ -54,6 +55,11 @@ export interface SubscriptionManagerConfig {
|
|
|
54
55
|
* Defaults to a new executor configured with `schema` alone.
|
|
55
56
|
*/
|
|
56
57
|
executor?: LeavenExecutor | Omit<ExecutorConfig, 'schema'>;
|
|
58
|
+
/**
|
|
59
|
+
* Where diagnostics (subscription timeouts, failing completion callbacks)
|
|
60
|
+
* are logged. Defaults to `console`.
|
|
61
|
+
*/
|
|
62
|
+
logger?: WebSocketLogger;
|
|
57
63
|
}
|
|
58
64
|
/**
|
|
59
65
|
* Manages GraphQL subscriptions
|
|
@@ -73,7 +79,18 @@ export declare class SubscriptionManager {
|
|
|
73
79
|
private readonly connections;
|
|
74
80
|
private readonly maxSubscriptionsPerConnection;
|
|
75
81
|
private readonly subscriptionTimeout;
|
|
82
|
+
private readonly logger;
|
|
76
83
|
constructor(config: SubscriptionManagerConfig);
|
|
84
|
+
/**
|
|
85
|
+
* Maximum number of subscriptions (pending or active) one connection may
|
|
86
|
+
* hold.
|
|
87
|
+
*/
|
|
88
|
+
get subscriptionLimit(): number;
|
|
89
|
+
/**
|
|
90
|
+
* Parse a query under the managed executor's parser limits.
|
|
91
|
+
* See {@link LeavenExecutor.parse}.
|
|
92
|
+
*/
|
|
93
|
+
parse(query: string): Promise<DocumentNode>;
|
|
77
94
|
/**
|
|
78
95
|
* Execute a single-result operation (query or mutation) on the same
|
|
79
96
|
* executor that serves subscriptions, so both transports honour the same
|
|
@@ -86,6 +103,12 @@ export declare class SubscriptionManager {
|
|
|
86
103
|
subscribe<TContext = unknown>(connectionId: string, subscriptionId: string, request: GraphQLRequest, context: TContext, onNext: (result: GraphQLResponse) => void, onComplete: () => void, onError: (errors: readonly {
|
|
87
104
|
message: string;
|
|
88
105
|
}[]) => void): Promise<Subscription>;
|
|
106
|
+
/**
|
|
107
|
+
* Whether this exact subscription object is still the one registered under
|
|
108
|
+
* its connection and id. An id lookup alone is not enough: after a
|
|
109
|
+
* teardown the id can be registered again by a NEWER subscription.
|
|
110
|
+
*/
|
|
111
|
+
private isCurrent;
|
|
89
112
|
/**
|
|
90
113
|
* Consume an async iterator and call callbacks
|
|
91
114
|
*/
|
package/dist/manager.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"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"}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -97,6 +97,22 @@ export interface CompleteMessage {
|
|
|
97
97
|
* All message types
|
|
98
98
|
*/
|
|
99
99
|
export type Message = ConnectionInitMessage | ConnectionAckMessage | PingMessage | PongMessage | SubscribeMessage | NextMessage | ErrorMessage | CompleteMessage;
|
|
100
|
+
/**
|
|
101
|
+
* Accepted shape of a client-chosen operation id: 1-64 characters from
|
|
102
|
+
* `[A-Za-z0-9_.:-]`.
|
|
103
|
+
*
|
|
104
|
+
* Ids are picked by the client and end up as map keys, in close reasons and
|
|
105
|
+
* in log lines. The graphql-ws spec only says "unique string", so in
|
|
106
|
+
* principle a client could send newlines, ANSI escapes or a megabyte of
|
|
107
|
+
* text; this allow-list covers UUIDs, counters and the `prefix:n` styles
|
|
108
|
+
* real clients use, and nothing that can forge a log line.
|
|
109
|
+
*/
|
|
110
|
+
export declare const OPERATION_ID_PATTERN: RegExp;
|
|
111
|
+
/**
|
|
112
|
+
* Whether a value is an acceptable client-chosen operation id.
|
|
113
|
+
* See {@link OPERATION_ID_PATTERN}.
|
|
114
|
+
*/
|
|
115
|
+
export declare function isValidOperationId(id: unknown): id is string;
|
|
100
116
|
/**
|
|
101
117
|
* Options for {@link parseMessage}
|
|
102
118
|
*/
|
|
@@ -113,6 +129,9 @@ export interface ParseMessageOptions {
|
|
|
113
129
|
* lenient peer may omit the id on `next`/`error`, and a parse throw there is
|
|
114
130
|
* far more disruptive (it tears down the whole connection) than tolerating
|
|
115
131
|
* the frame.
|
|
132
|
+
*
|
|
133
|
+
* When `true`, the id of a `subscribe` or `complete` frame must also match
|
|
134
|
+
* {@link OPERATION_ID_PATTERN}; anything else throws `Invalid message`.
|
|
116
135
|
*/
|
|
117
136
|
requireId?: boolean;
|
|
118
137
|
}
|
package/dist/protocol.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
|
package/dist/pubsub.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Copyright 2026 Joseph Quinn
|
|
5
5
|
* Licensed under the Apache License, Version 2.0
|
|
6
6
|
*/
|
|
7
|
+
import type { WebSocketLogger } from './logger';
|
|
7
8
|
/**
|
|
8
9
|
* Subscribe function type
|
|
9
10
|
*/
|
|
@@ -35,6 +36,8 @@ export interface PubSubConfig {
|
|
|
35
36
|
* without bound. Unlimited by default.
|
|
36
37
|
*/
|
|
37
38
|
maxQueueSize?: number;
|
|
39
|
+
/** Where subscriber callback errors are logged. Defaults to `console`. */
|
|
40
|
+
logger?: WebSocketLogger;
|
|
38
41
|
}
|
|
39
42
|
/**
|
|
40
43
|
* In-memory PubSub implementation
|
|
@@ -46,6 +49,7 @@ export declare class PubSub implements PubSubEngine {
|
|
|
46
49
|
private readonly maxSubscribersPerTopic;
|
|
47
50
|
private readonly wildcards;
|
|
48
51
|
private readonly maxQueueSize;
|
|
52
|
+
private readonly logger;
|
|
49
53
|
private nextId;
|
|
50
54
|
constructor(config?: PubSubConfig);
|
|
51
55
|
/**
|
package/dist/pubsub.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pubsub.d.ts","sourceRoot":"","sources":["../src/pubsub.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"pubsub.d.ts","sourceRoot":"","sources":["../src/pubsub.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,GAAG,OAAO,IAAI,CACrC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,KAC3B,MAAM,IAAI,CAAC;AAEhB;;GAEG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,IAAI,CAAC;AAEzE;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,WAAW,CAAC;IACvB,OAAO,EAAE,SAAS,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,oCAAoC;IACpC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,6BAA6B;IAC7B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAUD;;GAEG;AACH,qBAAa,MAAO,YAAW,YAAY;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+B;IAC3D,wEAAwE;IACxE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAwB;IACzD,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAS;IAChD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,GAAE,YAAiB;IAUrC;;OAEG;IACI,SAAS,CAAC,CAAC,GAAG,OAAO,EAC1B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,GAC7B,MAAM,IAAI;IA+Cb;;OAEG;IACI,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI;IAmB5D;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA2B1B;;OAEG;IACH,OAAO,CAAC,eAAe;IAwBvB;;OAEG;IACI,aAAa,CAAC,CAAC,GAAG,OAAO,EAC9B,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GACvB,qBAAqB,CAAC,CAAC,CAAC;IAmF3B;;OAEG;IACI,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIhD;;OAEG;IACI,SAAS,IAAI,MAAM,EAAE;IAI5B;;OAEG;IACI,KAAK,IAAI,IAAI;CAIrB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,CAE1D"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leaven-graphql/ws",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "WebSocket subscriptions support for Leaven GraphQL",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"directory": "packages/ws"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
+
"@leaven-graphql/core": "0.2.3",
|
|
35
36
|
"@leaven-graphql/context": "0.2.1",
|
|
36
|
-
"@leaven-graphql/errors": "0.2.1"
|
|
37
|
-
"@leaven-graphql/core": "0.2.1"
|
|
37
|
+
"@leaven-graphql/errors": "0.2.1"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/bun": "^1.3.14",
|