@atproto/xrpc-server 0.4.2 → 0.4.4-next.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/CHANGELOG.md +18 -0
- package/LICENSE.txt +1 -1
- package/dist/auth.d.ts +3 -2
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +124 -0
- package/dist/auth.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -53377
- package/dist/index.js.map +1 -7
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +7 -0
- package/dist/logger.js.map +1 -0
- package/dist/rate-limiter.d.ts +2 -1
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +166 -0
- package/dist/rate-limiter.js.map +1 -0
- package/dist/server.d.ts +6 -5
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +472 -0
- package/dist/server.js.map +1 -0
- package/dist/stream/frames.d.ts +2 -1
- package/dist/stream/frames.d.ts.map +1 -0
- package/dist/stream/frames.js +141 -0
- package/dist/stream/frames.js.map +1 -0
- package/dist/stream/index.d.ts +1 -0
- package/dist/stream/index.d.ts.map +1 -0
- package/dist/stream/index.js +22 -0
- package/dist/stream/index.js.map +1 -0
- package/dist/stream/logger.d.ts +1 -0
- package/dist/stream/logger.d.ts.map +1 -0
- package/dist/stream/logger.js +7 -0
- package/dist/stream/logger.js.map +1 -0
- package/dist/stream/server.d.ts +3 -1
- package/dist/stream/server.d.ts.map +1 -0
- package/dist/stream/server.js +70 -0
- package/dist/stream/server.js.map +1 -0
- package/dist/stream/stream.d.ts +1 -0
- package/dist/stream/stream.d.ts.map +1 -0
- package/dist/stream/stream.js +44 -0
- package/dist/stream/stream.js.map +1 -0
- package/dist/stream/subscription.d.ts +2 -0
- package/dist/stream/subscription.d.ts.map +1 -0
- package/dist/stream/subscription.js +80 -0
- package/dist/stream/subscription.js.map +1 -0
- package/dist/stream/types.d.ts +5 -4
- package/dist/stream/types.d.ts.map +1 -0
- package/dist/stream/types.js +47 -0
- package/dist/stream/types.js.map +1 -0
- package/dist/stream/websocket-keepalive.d.ts +2 -0
- package/dist/stream/websocket-keepalive.d.ts.map +1 -0
- package/dist/stream/websocket-keepalive.js +160 -0
- package/dist/stream/websocket-keepalive.js.map +1 -0
- package/dist/types.d.ts +54 -34
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +163 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +3 -2
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +263 -0
- package/dist/util.js.map +1 -0
- package/jest.config.js +4 -3
- package/package.json +10 -11
- package/src/rate-limiter.ts +3 -0
- package/src/server.ts +53 -14
- package/src/stream/frames.ts +1 -1
- package/src/stream/websocket-keepalive.ts +2 -1
- package/src/types.ts +22 -10
- package/src/util.ts +3 -3
- package/tests/bodies.test.ts +4 -4
- package/tests/errors.test.ts +1 -1
- package/tsconfig.build.json +6 -2
- package/tsconfig.json +3 -11
- package/tsconfig.tests.json +7 -0
- package/babel.config.js +0 -1
- package/build.js +0 -14
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./types"), exports);
|
|
18
|
+
__exportStar(require("./frames"), exports);
|
|
19
|
+
__exportStar(require("./stream"), exports);
|
|
20
|
+
__exportStar(require("./subscription"), exports);
|
|
21
|
+
__exportStar(require("./server"), exports);
|
|
22
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/stream/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAuB;AACvB,2CAAwB;AACxB,2CAAwB;AACxB,iDAA8B;AAC9B,2CAAwB"}
|
package/dist/stream/logger.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/stream/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEjD,eAAO,MAAM,MAAM,EAAE,UAAU,CAAC,OAAO,eAAe,CACtB,CAAA;AAEhC,eAAe,MAAM,CAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.logger = void 0;
|
|
4
|
+
const common_1 = require("@atproto/common");
|
|
5
|
+
exports.logger = (0, common_1.subsystemLogger)('xrpc-stream');
|
|
6
|
+
exports.default = exports.logger;
|
|
7
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/stream/logger.ts"],"names":[],"mappings":";;;AAAA,4CAAiD;AAEpC,QAAA,MAAM,GACjB,IAAA,wBAAe,EAAC,aAAa,CAAC,CAAA;AAEhC,kBAAe,cAAM,CAAA"}
|
package/dist/stream/server.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
2
3
|
import { IncomingMessage } from 'http';
|
|
3
4
|
import { WebSocketServer, ServerOptions, WebSocket } from 'ws';
|
|
4
5
|
import { Frame } from './frames';
|
|
@@ -8,4 +9,5 @@ export declare class XrpcStreamServer {
|
|
|
8
9
|
handler: Handler;
|
|
9
10
|
});
|
|
10
11
|
}
|
|
11
|
-
export
|
|
12
|
+
export type Handler = (req: IncomingMessage, signal: AbortSignal, socket: WebSocket, server: XrpcStreamServer) => AsyncIterable<Frame>;
|
|
13
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/stream/server.ts"],"names":[],"mappings":";;AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AACtC,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,IAAI,CAAA;AAC9D,OAAO,EAAc,KAAK,EAAE,MAAM,UAAU,CAAA;AAI5C,qBAAa,gBAAgB;IAC3B,GAAG,EAAE,eAAe,CAAA;gBACR,IAAI,EAAE,aAAa,GAAG;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE;CAqCvD;AAED,MAAM,MAAM,OAAO,GAAG,CACpB,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,gBAAgB,KACrB,aAAa,CAAC,KAAK,CAAC,CAAA"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.XrpcStreamServer = void 0;
|
|
7
|
+
const ws_1 = require("ws");
|
|
8
|
+
const frames_1 = require("./frames");
|
|
9
|
+
const logger_1 = __importDefault(require("./logger"));
|
|
10
|
+
const types_1 = require("./types");
|
|
11
|
+
class XrpcStreamServer {
|
|
12
|
+
constructor(opts) {
|
|
13
|
+
Object.defineProperty(this, "wss", {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
configurable: true,
|
|
16
|
+
writable: true,
|
|
17
|
+
value: void 0
|
|
18
|
+
});
|
|
19
|
+
const { handler, ...serverOpts } = opts;
|
|
20
|
+
this.wss = new ws_1.WebSocketServer(serverOpts);
|
|
21
|
+
this.wss.on('connection', async (socket, req) => {
|
|
22
|
+
socket.on('error', (err) => logger_1.default.error(err, 'websocket error'));
|
|
23
|
+
try {
|
|
24
|
+
const ac = new AbortController();
|
|
25
|
+
const iterator = unwrapIterator(handler(req, ac.signal, socket, this));
|
|
26
|
+
socket.once('close', () => {
|
|
27
|
+
iterator.return?.();
|
|
28
|
+
ac.abort();
|
|
29
|
+
});
|
|
30
|
+
const safeFrames = wrapIterator(iterator);
|
|
31
|
+
for await (const frame of safeFrames) {
|
|
32
|
+
await new Promise((res, rej) => {
|
|
33
|
+
socket.send(frame.toBytes(), { binary: true }, (err) => {
|
|
34
|
+
// @TODO this callback may give more aggressive on backpressure than
|
|
35
|
+
// we ultimately want, but trying it out for the time being.
|
|
36
|
+
if (err)
|
|
37
|
+
return rej(err);
|
|
38
|
+
res(undefined);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
if (frame instanceof frames_1.ErrorFrame) {
|
|
42
|
+
throw new types_1.DisconnectError(types_1.CloseCode.Policy, frame.body.error);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
if (err instanceof types_1.DisconnectError) {
|
|
48
|
+
return socket.close(err.wsCode, err.xrpcCode);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
logger_1.default.error(err, 'websocket server error');
|
|
52
|
+
return socket.terminate();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
socket.close(types_1.CloseCode.Normal);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.XrpcStreamServer = XrpcStreamServer;
|
|
60
|
+
function unwrapIterator(iterable) {
|
|
61
|
+
return iterable[Symbol.asyncIterator]();
|
|
62
|
+
}
|
|
63
|
+
function wrapIterator(iterator) {
|
|
64
|
+
return {
|
|
65
|
+
[Symbol.asyncIterator]() {
|
|
66
|
+
return iterator;
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/stream/server.ts"],"names":[],"mappings":";;;;;;AACA,2BAA8D;AAC9D,qCAA4C;AAC5C,sDAA6B;AAC7B,mCAAoD;AAEpD,MAAa,gBAAgB;IAE3B,YAAY,IAA0C;QADtD;;;;;WAAoB;QAElB,MAAM,EAAE,OAAO,EAAE,GAAG,UAAU,EAAE,GAAG,IAAI,CAAA;QACvC,IAAI,CAAC,GAAG,GAAG,IAAI,oBAAe,CAAC,UAAU,CAAC,CAAA;QAC1C,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;YAC9C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAM,CAAC,KAAK,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC,CAAA;YACjE,IAAI,CAAC;gBACH,MAAM,EAAE,GAAG,IAAI,eAAe,EAAE,CAAA;gBAChC,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAA;gBACtE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;oBACxB,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAA;oBACnB,EAAE,CAAC,KAAK,EAAE,CAAA;gBACZ,CAAC,CAAC,CAAA;gBACF,MAAM,UAAU,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAA;gBACzC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;oBACrC,MAAM,IAAI,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;wBAC7B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE;4BACrD,oEAAoE;4BACpE,4DAA4D;4BAC5D,IAAI,GAAG;gCAAE,OAAO,GAAG,CAAC,GAAG,CAAC,CAAA;4BACxB,GAAG,CAAC,SAAS,CAAC,CAAA;wBAChB,CAAC,CAAC,CAAA;oBACJ,CAAC,CAAC,CAAA;oBACF,IAAI,KAAK,YAAY,mBAAU,EAAE,CAAC;wBAChC,MAAM,IAAI,uBAAe,CAAC,iBAAS,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;oBAC/D,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,GAAG,YAAY,uBAAe,EAAE,CAAC;oBACnC,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAA;gBAC/C,CAAC;qBAAM,CAAC;oBACN,gBAAM,CAAC,KAAK,CAAC,GAAG,EAAE,wBAAwB,CAAC,CAAA;oBAC3C,OAAO,MAAM,CAAC,SAAS,EAAE,CAAA;gBAC3B,CAAC;YACH,CAAC;YACD,MAAM,CAAC,KAAK,CAAC,iBAAS,CAAC,MAAM,CAAC,CAAA;QAChC,CAAC,CAAC,CAAA;IACJ,CAAC;CACF;AAvCD,4CAuCC;AASD,SAAS,cAAc,CAAI,QAA0B;IACnD,OAAO,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAA;AACzC,CAAC;AAED,SAAS,YAAY,CAAI,QAA0B;IACjD,OAAO;QACL,CAAC,MAAM,CAAC,aAAa,CAAC;YACpB,OAAO,QAAQ,CAAA;QACjB,CAAC;KACF,CAAA;AACH,CAAC"}
|
package/dist/stream/stream.d.ts
CHANGED
|
@@ -6,3 +6,4 @@ export declare function streamByteChunks(ws: WebSocket, options?: DuplexOptions)
|
|
|
6
6
|
export declare function byFrame(ws: WebSocket, options?: DuplexOptions): AsyncGenerator<MessageFrame<unknown> | import("./frames").ErrorFrame<string>, void, unknown>;
|
|
7
7
|
export declare function byMessage(ws: WebSocket, options?: DuplexOptions): AsyncGenerator<MessageFrame<unknown>, void, unknown>;
|
|
8
8
|
export declare function ensureChunkIsMessage(chunk: Uint8Array): MessageFrame<unknown>;
|
|
9
|
+
//# sourceMappingURL=stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/stream/stream.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAA;AACtC,OAAO,EAAyB,SAAS,EAAE,MAAM,IAAI,CAAA;AACrD,OAAO,EAAS,YAAY,EAAE,MAAM,UAAU,CAAA;AAE9C,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,aAAa,2BAKtE;AAED,wBAAuB,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,aAAa,gGAKpE;AAED,wBAAuB,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,aAAa,wDAMtE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,GAAG,YAAY,CAAC,OAAO,CAAC,CAW7E"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ensureChunkIsMessage = exports.byMessage = exports.byFrame = exports.streamByteChunks = void 0;
|
|
4
|
+
const xrpc_1 = require("@atproto/xrpc");
|
|
5
|
+
const ws_1 = require("ws");
|
|
6
|
+
const frames_1 = require("./frames");
|
|
7
|
+
function streamByteChunks(ws, options) {
|
|
8
|
+
return (0, ws_1.createWebSocketStream)(ws, {
|
|
9
|
+
...options,
|
|
10
|
+
readableObjectMode: true, // Ensures frame bytes don't get buffered/combined together
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
exports.streamByteChunks = streamByteChunks;
|
|
14
|
+
async function* byFrame(ws, options) {
|
|
15
|
+
const wsStream = streamByteChunks(ws, options);
|
|
16
|
+
for await (const chunk of wsStream) {
|
|
17
|
+
yield frames_1.Frame.fromBytes(chunk);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.byFrame = byFrame;
|
|
21
|
+
async function* byMessage(ws, options) {
|
|
22
|
+
const wsStream = streamByteChunks(ws, options);
|
|
23
|
+
for await (const chunk of wsStream) {
|
|
24
|
+
const msg = ensureChunkIsMessage(chunk);
|
|
25
|
+
yield msg;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.byMessage = byMessage;
|
|
29
|
+
function ensureChunkIsMessage(chunk) {
|
|
30
|
+
const frame = frames_1.Frame.fromBytes(chunk);
|
|
31
|
+
if (frame.isMessage()) {
|
|
32
|
+
return frame;
|
|
33
|
+
}
|
|
34
|
+
else if (frame.isError()) {
|
|
35
|
+
// @TODO work -1 error code into XRPCError
|
|
36
|
+
// @ts-ignore
|
|
37
|
+
throw new xrpc_1.XRPCError(-1, frame.code, frame.message);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
throw new xrpc_1.XRPCError(xrpc_1.ResponseType.Unknown, undefined, 'Unknown frame type');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.ensureChunkIsMessage = ensureChunkIsMessage;
|
|
44
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/stream/stream.ts"],"names":[],"mappings":";;;AAAA,wCAAuD;AAEvD,2BAAqD;AACrD,qCAA8C;AAE9C,SAAgB,gBAAgB,CAAC,EAAa,EAAE,OAAuB;IACrE,OAAO,IAAA,0BAAqB,EAAC,EAAE,EAAE;QAC/B,GAAG,OAAO;QACV,kBAAkB,EAAE,IAAI,EAAE,2DAA2D;KACtF,CAAC,CAAA;AACJ,CAAC;AALD,4CAKC;AAEM,KAAK,SAAS,CAAC,CAAC,OAAO,CAAC,EAAa,EAAE,OAAuB;IACnE,MAAM,QAAQ,GAAG,gBAAgB,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;IAC9C,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QACnC,MAAM,cAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IAC9B,CAAC;AACH,CAAC;AALD,0BAKC;AAEM,KAAK,SAAS,CAAC,CAAC,SAAS,CAAC,EAAa,EAAE,OAAuB;IACrE,MAAM,QAAQ,GAAG,gBAAgB,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;IAC9C,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAA;QACvC,MAAM,GAAG,CAAA;IACX,CAAC;AACH,CAAC;AAND,8BAMC;AAED,SAAgB,oBAAoB,CAAC,KAAiB;IACpD,MAAM,KAAK,GAAG,cAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IACpC,IAAI,KAAK,CAAC,SAAS,EAAE,EAAE,CAAC;QACtB,OAAO,KAAK,CAAA;IACd,CAAC;SAAM,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QAC3B,0CAA0C;QAC1C,aAAa;QACb,MAAM,IAAI,gBAAS,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;IACpD,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,gBAAS,CAAC,mBAAY,CAAC,OAAO,EAAE,SAAS,EAAE,oBAAoB,CAAC,CAAA;IAC5E,CAAC;AACH,CAAC;AAXD,oDAWC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import { ClientOptions } from 'ws';
|
|
2
3
|
export declare class Subscription<T = unknown> {
|
|
3
4
|
opts: ClientOptions & {
|
|
@@ -23,3 +24,4 @@ export declare class Subscription<T = unknown> {
|
|
|
23
24
|
[Symbol.asyncIterator](): AsyncGenerator<T>;
|
|
24
25
|
}
|
|
25
26
|
export default Subscription;
|
|
27
|
+
//# sourceMappingURL=subscription.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscription.d.ts","sourceRoot":"","sources":["../../src/stream/subscription.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,IAAI,CAAA;AAIlC,qBAAa,YAAY,CAAC,CAAC,GAAG,OAAO;IAE1B,IAAI,EAAE,aAAa,GAAG;QAC3B,OAAO,EAAE,MAAM,CAAA;QACf,MAAM,EAAE,MAAM,CAAA;QACd,mBAAmB,CAAC,EAAE,MAAM,CAAA;QAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAA;QAC5B,MAAM,CAAC,EAAE,WAAW,CAAA;QACpB,QAAQ,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,CAAC,GAAG,SAAS,CAAA;QACzC,gBAAgB,CAAC,EAAE,CACjB,KAAK,EAAE,OAAO,EACd,CAAC,EAAE,MAAM,EACT,YAAY,EAAE,OAAO,KAClB,IAAI,CAAA;QACT,SAAS,CAAC,EAAE,MACR,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACvB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,GAC5C,SAAS,CAAA;KACd;gBAhBM,IAAI,EAAE,aAAa,GAAG;QAC3B,OAAO,EAAE,MAAM,CAAA;QACf,MAAM,EAAE,MAAM,CAAA;QACd,mBAAmB,CAAC,EAAE,MAAM,CAAA;QAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAA;QAC5B,MAAM,CAAC,EAAE,WAAW,CAAA;QACpB,QAAQ,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,CAAC,GAAG,SAAS,CAAA;QACzC,gBAAgB,CAAC,EAAE,CACjB,KAAK,EAAE,OAAO,EACd,CAAC,EAAE,MAAM,EACT,YAAY,EAAE,OAAO,KAClB,IAAI,CAAA;QACT,SAAS,CAAC,EAAE,MACR,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACvB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,GAC5C,SAAS,CAAA;KACd;IAGI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,cAAc,CAAC,CAAC,CAAC;CAsBnD;AAED,eAAe,YAAY,CAAA"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Subscription = void 0;
|
|
4
|
+
const websocket_keepalive_1 = require("./websocket-keepalive");
|
|
5
|
+
const stream_1 = require("./stream");
|
|
6
|
+
class Subscription {
|
|
7
|
+
constructor(opts) {
|
|
8
|
+
Object.defineProperty(this, "opts", {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
configurable: true,
|
|
11
|
+
writable: true,
|
|
12
|
+
value: opts
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
async *[Symbol.asyncIterator]() {
|
|
16
|
+
const ws = new websocket_keepalive_1.WebSocketKeepAlive({
|
|
17
|
+
...this.opts,
|
|
18
|
+
getUrl: async () => {
|
|
19
|
+
const params = (await this.opts.getParams?.()) ?? {};
|
|
20
|
+
const query = encodeQueryParams(params);
|
|
21
|
+
return `${this.opts.service}/xrpc/${this.opts.method}?${query}`;
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
for await (const chunk of ws) {
|
|
25
|
+
const message = await (0, stream_1.ensureChunkIsMessage)(chunk);
|
|
26
|
+
const t = message.header.t;
|
|
27
|
+
const clone = message.body !== undefined ? { ...message.body } : undefined;
|
|
28
|
+
if (clone !== undefined && t !== undefined) {
|
|
29
|
+
clone['$type'] = t.startsWith('#') ? this.opts.method + t : t;
|
|
30
|
+
}
|
|
31
|
+
const result = this.opts.validate(clone);
|
|
32
|
+
if (result !== undefined) {
|
|
33
|
+
yield result;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.Subscription = Subscription;
|
|
39
|
+
exports.default = Subscription;
|
|
40
|
+
function encodeQueryParams(obj) {
|
|
41
|
+
const params = new URLSearchParams();
|
|
42
|
+
Object.entries(obj).forEach(([key, value]) => {
|
|
43
|
+
const encoded = encodeQueryParam(value);
|
|
44
|
+
if (Array.isArray(encoded)) {
|
|
45
|
+
encoded.forEach((enc) => params.append(key, enc));
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
params.set(key, encoded);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
return params.toString();
|
|
52
|
+
}
|
|
53
|
+
// Adapted from xrpc, but without any lex-specific knowledge
|
|
54
|
+
function encodeQueryParam(value) {
|
|
55
|
+
if (typeof value === 'string') {
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
if (typeof value === 'number') {
|
|
59
|
+
return value.toString();
|
|
60
|
+
}
|
|
61
|
+
if (typeof value === 'boolean') {
|
|
62
|
+
return value ? 'true' : 'false';
|
|
63
|
+
}
|
|
64
|
+
if (typeof value === 'undefined') {
|
|
65
|
+
return '';
|
|
66
|
+
}
|
|
67
|
+
if (typeof value === 'object') {
|
|
68
|
+
if (value instanceof Date) {
|
|
69
|
+
return value.toISOString();
|
|
70
|
+
}
|
|
71
|
+
else if (Array.isArray(value)) {
|
|
72
|
+
return value.flatMap(encodeQueryParam);
|
|
73
|
+
}
|
|
74
|
+
else if (!value) {
|
|
75
|
+
return '';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
throw new Error(`Cannot encode ${typeof value}s into query params`);
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=subscription.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscription.js","sourceRoot":"","sources":["../../src/stream/subscription.ts"],"names":[],"mappings":"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"}
|
package/dist/stream/types.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export declare const messageFrameHeader: z.ZodObject<{
|
|
|
13
13
|
op: FrameType.Message;
|
|
14
14
|
t?: string | undefined;
|
|
15
15
|
}>;
|
|
16
|
-
export
|
|
16
|
+
export type MessageFrameHeader = z.infer<typeof messageFrameHeader>;
|
|
17
17
|
export declare const errorFrameHeader: z.ZodObject<{
|
|
18
18
|
op: z.ZodLiteral<FrameType.Error>;
|
|
19
19
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -31,8 +31,8 @@ export declare const errorFrameBody: z.ZodObject<{
|
|
|
31
31
|
error: string;
|
|
32
32
|
message?: string | undefined;
|
|
33
33
|
}>;
|
|
34
|
-
export
|
|
35
|
-
export
|
|
34
|
+
export type ErrorFrameHeader = z.infer<typeof errorFrameHeader>;
|
|
35
|
+
export type ErrorFrameBody<T extends string = string> = {
|
|
36
36
|
error: T;
|
|
37
37
|
} & z.infer<typeof errorFrameBody>;
|
|
38
38
|
export declare const frameHeader: z.ZodUnion<[z.ZodObject<{
|
|
@@ -51,7 +51,7 @@ export declare const frameHeader: z.ZodUnion<[z.ZodObject<{
|
|
|
51
51
|
}, {
|
|
52
52
|
op: FrameType.Error;
|
|
53
53
|
}>]>;
|
|
54
|
-
export
|
|
54
|
+
export type FrameHeader = z.infer<typeof frameHeader>;
|
|
55
55
|
export declare class DisconnectError extends Error {
|
|
56
56
|
wsCode: CloseCode;
|
|
57
57
|
xrpcCode?: string | undefined;
|
|
@@ -62,3 +62,4 @@ export declare enum CloseCode {
|
|
|
62
62
|
Abnormal = 1006,
|
|
63
63
|
Policy = 1008
|
|
64
64
|
}
|
|
65
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/stream/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,oBAAY,SAAS;IACnB,OAAO,IAAI;IACX,KAAK,KAAK;CACX;AAED,eAAO,MAAM,kBAAkB;;;;;;;;;EAG7B,CAAA;AACF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAA;AAEnE,eAAO,MAAM,gBAAgB;;;;;;EAE3B,CAAA;AACF,eAAO,MAAM,cAAc;;;;;;;;;EAGzB,CAAA;AACF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAA;AAC/D,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI;IAAE,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG,CAAC,CAAC,KAAK,CAC5E,OAAO,cAAc,CACtB,CAAA;AAED,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;IAAkD,CAAA;AAC1E,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAA;AAErD,qBAAa,eAAgB,SAAQ,KAAK;IAE/B,MAAM,EAAE,SAAS;IACjB,QAAQ,CAAC;gBADT,MAAM,GAAE,SAA4B,EACpC,QAAQ,CAAC,oBAAQ;CAI3B;AAGD,oBAAY,SAAS;IACnB,MAAM,OAAO;IACb,QAAQ,OAAO;IACf,MAAM,OAAO;CACd"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CloseCode = exports.DisconnectError = exports.frameHeader = exports.errorFrameBody = exports.errorFrameHeader = exports.messageFrameHeader = exports.FrameType = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
var FrameType;
|
|
6
|
+
(function (FrameType) {
|
|
7
|
+
FrameType[FrameType["Message"] = 1] = "Message";
|
|
8
|
+
FrameType[FrameType["Error"] = -1] = "Error";
|
|
9
|
+
})(FrameType || (exports.FrameType = FrameType = {}));
|
|
10
|
+
exports.messageFrameHeader = zod_1.z.object({
|
|
11
|
+
op: zod_1.z.literal(FrameType.Message), // Frame op
|
|
12
|
+
t: zod_1.z.string().optional(), // Message body type discriminator
|
|
13
|
+
});
|
|
14
|
+
exports.errorFrameHeader = zod_1.z.object({
|
|
15
|
+
op: zod_1.z.literal(FrameType.Error),
|
|
16
|
+
});
|
|
17
|
+
exports.errorFrameBody = zod_1.z.object({
|
|
18
|
+
error: zod_1.z.string(), // Error code
|
|
19
|
+
message: zod_1.z.string().optional(), // Error message
|
|
20
|
+
});
|
|
21
|
+
exports.frameHeader = zod_1.z.union([exports.messageFrameHeader, exports.errorFrameHeader]);
|
|
22
|
+
class DisconnectError extends Error {
|
|
23
|
+
constructor(wsCode = CloseCode.Policy, xrpcCode) {
|
|
24
|
+
super();
|
|
25
|
+
Object.defineProperty(this, "wsCode", {
|
|
26
|
+
enumerable: true,
|
|
27
|
+
configurable: true,
|
|
28
|
+
writable: true,
|
|
29
|
+
value: wsCode
|
|
30
|
+
});
|
|
31
|
+
Object.defineProperty(this, "xrpcCode", {
|
|
32
|
+
enumerable: true,
|
|
33
|
+
configurable: true,
|
|
34
|
+
writable: true,
|
|
35
|
+
value: xrpcCode
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.DisconnectError = DisconnectError;
|
|
40
|
+
// https://www.rfc-editor.org/rfc/rfc6455#section-7.4.1
|
|
41
|
+
var CloseCode;
|
|
42
|
+
(function (CloseCode) {
|
|
43
|
+
CloseCode[CloseCode["Normal"] = 1000] = "Normal";
|
|
44
|
+
CloseCode[CloseCode["Abnormal"] = 1006] = "Abnormal";
|
|
45
|
+
CloseCode[CloseCode["Policy"] = 1008] = "Policy";
|
|
46
|
+
})(CloseCode || (exports.CloseCode = CloseCode = {}));
|
|
47
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/stream/types.ts"],"names":[],"mappings":";;;AAAA,6BAAuB;AAEvB,IAAY,SAGX;AAHD,WAAY,SAAS;IACnB,+CAAW,CAAA;IACX,4CAAU,CAAA;AACZ,CAAC,EAHW,SAAS,yBAAT,SAAS,QAGpB;AAEY,QAAA,kBAAkB,GAAG,OAAC,CAAC,MAAM,CAAC;IACzC,EAAE,EAAE,OAAC,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,WAAW;IAC7C,CAAC,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,kCAAkC;CAC7D,CAAC,CAAA;AAGW,QAAA,gBAAgB,GAAG,OAAC,CAAC,MAAM,CAAC;IACvC,EAAE,EAAE,OAAC,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC;CAC/B,CAAC,CAAA;AACW,QAAA,cAAc,GAAG,OAAC,CAAC,MAAM,CAAC;IACrC,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,EAAE,aAAa;IAChC,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,gBAAgB;CACjD,CAAC,CAAA;AAMW,QAAA,WAAW,GAAG,OAAC,CAAC,KAAK,CAAC,CAAC,0BAAkB,EAAE,wBAAgB,CAAC,CAAC,CAAA;AAG1E,MAAa,eAAgB,SAAQ,KAAK;IACxC,YACS,SAAoB,SAAS,CAAC,MAAM,EACpC,QAAiB;QAExB,KAAK,EAAE,CAAA;QAHP;;;;mBAAO,MAAM;WAA8B;QAC3C;;;;mBAAO,QAAQ;WAAS;IAG1B,CAAC;CACF;AAPD,0CAOC;AAED,uDAAuD;AACvD,IAAY,SAIX;AAJD,WAAY,SAAS;IACnB,gDAAa,CAAA;IACb,oDAAe,CAAA;IACf,gDAAa,CAAA;AACf,CAAC,EAJW,SAAS,yBAAT,SAAS,QAIpB"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import { WebSocket, ClientOptions } from 'ws';
|
|
2
3
|
export declare class WebSocketKeepAlive {
|
|
3
4
|
opts: ClientOptions & {
|
|
@@ -21,3 +22,4 @@ export declare class WebSocketKeepAlive {
|
|
|
21
22
|
startHeartbeat(ws: WebSocket): void;
|
|
22
23
|
}
|
|
23
24
|
export default WebSocketKeepAlive;
|
|
25
|
+
//# sourceMappingURL=websocket-keepalive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"websocket-keepalive.d.ts","sourceRoot":"","sources":["../../src/stream/websocket-keepalive.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,IAAI,CAAA;AAI7C,qBAAa,kBAAkB;IAMpB,IAAI,EAAE,aAAa,GAAG;QAC3B,MAAM,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;QAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAA;QAC5B,MAAM,CAAC,EAAE,WAAW,CAAA;QACpB,mBAAmB,CAAC,EAAE,MAAM,CAAA;QAC5B,gBAAgB,CAAC,EAAE,CACjB,KAAK,EAAE,OAAO,EACd,CAAC,EAAE,MAAM,EACT,YAAY,EAAE,OAAO,KAClB,IAAI,CAAA;KACV;IAfI,EAAE,EAAE,SAAS,GAAG,IAAI,CAAO;IAC3B,YAAY,UAAO;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAO;gBAG9B,IAAI,EAAE,aAAa,GAAG;QAC3B,MAAM,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;QAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAA;QAC5B,MAAM,CAAC,EAAE,WAAW,CAAA;QACpB,mBAAmB,CAAC,EAAE,MAAM,CAAA;QAC5B,gBAAgB,CAAC,EAAE,CACjB,KAAK,EAAE,OAAO,EACd,CAAC,EAAE,MAAM,EACT,YAAY,EAAE,OAAO,KAClB,IAAI,CAAA;KACV;IAGI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,cAAc,CAAC,UAAU,CAAC;IAwD3D,cAAc,CAAC,EAAE,EAAE,SAAS;CA4B7B;AAED,eAAe,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WebSocketKeepAlive = void 0;
|
|
4
|
+
const common_1 = require("@atproto/common");
|
|
5
|
+
const ws_1 = require("ws");
|
|
6
|
+
const stream_1 = require("./stream");
|
|
7
|
+
const types_1 = require("./types");
|
|
8
|
+
class WebSocketKeepAlive {
|
|
9
|
+
constructor(opts) {
|
|
10
|
+
Object.defineProperty(this, "opts", {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
writable: true,
|
|
14
|
+
value: opts
|
|
15
|
+
});
|
|
16
|
+
Object.defineProperty(this, "ws", {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
configurable: true,
|
|
19
|
+
writable: true,
|
|
20
|
+
value: null
|
|
21
|
+
});
|
|
22
|
+
Object.defineProperty(this, "initialSetup", {
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
writable: true,
|
|
26
|
+
value: true
|
|
27
|
+
});
|
|
28
|
+
Object.defineProperty(this, "reconnects", {
|
|
29
|
+
enumerable: true,
|
|
30
|
+
configurable: true,
|
|
31
|
+
writable: true,
|
|
32
|
+
value: null
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async *[Symbol.asyncIterator]() {
|
|
36
|
+
const maxReconnectMs = 1000 * (this.opts.maxReconnectSeconds ?? 64);
|
|
37
|
+
while (true) {
|
|
38
|
+
if (this.reconnects !== null) {
|
|
39
|
+
const duration = this.initialSetup
|
|
40
|
+
? Math.min(1000, maxReconnectMs)
|
|
41
|
+
: backoffMs(this.reconnects++, maxReconnectMs);
|
|
42
|
+
await (0, common_1.wait)(duration);
|
|
43
|
+
}
|
|
44
|
+
const url = await this.opts.getUrl();
|
|
45
|
+
this.ws = new ws_1.WebSocket(url, this.opts);
|
|
46
|
+
const ac = new AbortController();
|
|
47
|
+
if (this.opts.signal) {
|
|
48
|
+
forwardSignal(this.opts.signal, ac);
|
|
49
|
+
}
|
|
50
|
+
this.ws.once('open', () => {
|
|
51
|
+
this.initialSetup = false;
|
|
52
|
+
this.reconnects = 0;
|
|
53
|
+
if (this.ws) {
|
|
54
|
+
this.startHeartbeat(this.ws);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
this.ws.once('close', (code, reason) => {
|
|
58
|
+
if (code === types_1.CloseCode.Abnormal) {
|
|
59
|
+
// Forward into an error to distinguish from a clean close
|
|
60
|
+
ac.abort(new AbnormalCloseError(`Abnormal ws close: ${reason.toString()}`));
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
try {
|
|
64
|
+
const wsStream = (0, stream_1.streamByteChunks)(this.ws, { signal: ac.signal });
|
|
65
|
+
for await (const chunk of wsStream) {
|
|
66
|
+
yield chunk;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (_err) {
|
|
70
|
+
const err = _err?.['code'] === 'ABORT_ERR' ? _err['cause'] : _err;
|
|
71
|
+
if (err instanceof types_1.DisconnectError) {
|
|
72
|
+
// We cleanly end the connection
|
|
73
|
+
this.ws?.close(err.wsCode);
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
this.ws?.close(); // No-ops if already closed or closing
|
|
77
|
+
if (isReconnectable(err)) {
|
|
78
|
+
this.reconnects ?? (this.reconnects = 0); // Never reconnect with a null
|
|
79
|
+
this.opts.onReconnectError?.(err, this.reconnects, this.initialSetup);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
break; // Other side cleanly ended stream and disconnected
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
startHeartbeat(ws) {
|
|
90
|
+
let isAlive = true;
|
|
91
|
+
let heartbeatInterval = null;
|
|
92
|
+
const checkAlive = () => {
|
|
93
|
+
if (!isAlive) {
|
|
94
|
+
return ws.terminate();
|
|
95
|
+
}
|
|
96
|
+
isAlive = false; // expect websocket to no longer be alive unless we receive a "pong" within the interval
|
|
97
|
+
ws.ping();
|
|
98
|
+
};
|
|
99
|
+
checkAlive();
|
|
100
|
+
heartbeatInterval = setInterval(checkAlive, this.opts.heartbeatIntervalMs ?? 10 * common_1.SECOND);
|
|
101
|
+
ws.on('pong', () => {
|
|
102
|
+
isAlive = true;
|
|
103
|
+
});
|
|
104
|
+
ws.once('close', () => {
|
|
105
|
+
if (heartbeatInterval) {
|
|
106
|
+
clearInterval(heartbeatInterval);
|
|
107
|
+
heartbeatInterval = null;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
exports.WebSocketKeepAlive = WebSocketKeepAlive;
|
|
113
|
+
exports.default = WebSocketKeepAlive;
|
|
114
|
+
class AbnormalCloseError extends Error {
|
|
115
|
+
constructor() {
|
|
116
|
+
super(...arguments);
|
|
117
|
+
Object.defineProperty(this, "code", {
|
|
118
|
+
enumerable: true,
|
|
119
|
+
configurable: true,
|
|
120
|
+
writable: true,
|
|
121
|
+
value: 'EWSABNORMALCLOSE'
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function isReconnectable(err) {
|
|
126
|
+
// Network errors are reconnectable.
|
|
127
|
+
// AuthenticationRequired and InvalidRequest XRPCErrors are not reconnectable.
|
|
128
|
+
// @TODO method-specific XRPCErrors may be reconnectable, need to consider. Receiving
|
|
129
|
+
// an invalid message is not current reconnectable, but the user can decide to skip them.
|
|
130
|
+
if (!err || typeof err['code'] !== 'string')
|
|
131
|
+
return false;
|
|
132
|
+
return networkErrorCodes.includes(err['code']);
|
|
133
|
+
}
|
|
134
|
+
const networkErrorCodes = [
|
|
135
|
+
'EWSABNORMALCLOSE',
|
|
136
|
+
'ECONNRESET',
|
|
137
|
+
'ECONNREFUSED',
|
|
138
|
+
'ECONNABORTED',
|
|
139
|
+
'EPIPE',
|
|
140
|
+
'ETIMEDOUT',
|
|
141
|
+
'ECANCELED',
|
|
142
|
+
];
|
|
143
|
+
function backoffMs(n, maxMs) {
|
|
144
|
+
const baseSec = Math.pow(2, n); // 1, 2, 4, ...
|
|
145
|
+
const randSec = Math.random() - 0.5; // Random jitter between -.5 and .5 seconds
|
|
146
|
+
const ms = 1000 * (baseSec + randSec);
|
|
147
|
+
return Math.min(ms, maxMs);
|
|
148
|
+
}
|
|
149
|
+
function forwardSignal(signal, ac) {
|
|
150
|
+
if (signal.aborted) {
|
|
151
|
+
return ac.abort(signal.reason);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
signal.addEventListener('abort', () => ac.abort(signal.reason), {
|
|
155
|
+
// @ts-ignore https://github.com/DefinitelyTyped/DefinitelyTyped/pull/68625
|
|
156
|
+
signal: ac.signal,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=websocket-keepalive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"websocket-keepalive.js","sourceRoot":"","sources":["../../src/stream/websocket-keepalive.ts"],"names":[],"mappings":"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"}
|