@atproto/lex-server 0.0.1

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.
Files changed (61) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE.txt +7 -0
  3. package/README.md +598 -0
  4. package/dist/errors.d.ts +13 -0
  5. package/dist/errors.d.ts.map +1 -0
  6. package/dist/errors.js +39 -0
  7. package/dist/errors.js.map +1 -0
  8. package/dist/example.d.ts +2 -0
  9. package/dist/example.d.ts.map +1 -0
  10. package/dist/example.js +36 -0
  11. package/dist/example.js.map +1 -0
  12. package/dist/index.d.ts +4 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +9 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/lex-auth-error.d.ts +15 -0
  17. package/dist/lex-auth-error.d.ts.map +1 -0
  18. package/dist/lex-auth-error.js +52 -0
  19. package/dist/lex-auth-error.js.map +1 -0
  20. package/dist/lex-server.d.ts +80 -0
  21. package/dist/lex-server.d.ts.map +1 -0
  22. package/dist/lex-server.js +285 -0
  23. package/dist/lex-server.js.map +1 -0
  24. package/dist/lib/drain-websocket.d.ts +6 -0
  25. package/dist/lib/drain-websocket.d.ts.map +1 -0
  26. package/dist/lib/drain-websocket.js +16 -0
  27. package/dist/lib/drain-websocket.js.map +1 -0
  28. package/dist/lib/sleep.d.ts +2 -0
  29. package/dist/lib/sleep.d.ts.map +1 -0
  30. package/dist/lib/sleep.js +22 -0
  31. package/dist/lib/sleep.js.map +1 -0
  32. package/dist/lib/www-authenticate.d.ts +7 -0
  33. package/dist/lib/www-authenticate.d.ts.map +1 -0
  34. package/dist/lib/www-authenticate.js +22 -0
  35. package/dist/lib/www-authenticate.js.map +1 -0
  36. package/dist/nodejs.d.ts +35 -0
  37. package/dist/nodejs.d.ts.map +1 -0
  38. package/dist/nodejs.js +236 -0
  39. package/dist/nodejs.js.map +1 -0
  40. package/dist/subscripotion.d.ts +2 -0
  41. package/dist/subscripotion.d.ts.map +1 -0
  42. package/dist/subscripotion.js +36 -0
  43. package/dist/subscripotion.js.map +1 -0
  44. package/dist/test.d.mts +2 -0
  45. package/dist/test.d.mts.map +1 -0
  46. package/dist/test.mjs +52 -0
  47. package/dist/test.mjs.map +1 -0
  48. package/nodejs.js +5 -0
  49. package/package.json +64 -0
  50. package/src/errors.ts +54 -0
  51. package/src/index.ts +8 -0
  52. package/src/lex-server.test.ts +1621 -0
  53. package/src/lex-server.ts +551 -0
  54. package/src/lib/drain-websocket.ts +23 -0
  55. package/src/lib/sleep.ts +25 -0
  56. package/src/lib/www-authenticate.ts +26 -0
  57. package/src/nodejs.test.ts +107 -0
  58. package/src/nodejs.ts +367 -0
  59. package/tsconfig.build.json +12 -0
  60. package/tsconfig.json +8 -0
  61. package/tsconfig.tests.json +9 -0
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ /* eslint-disable @typescript-eslint/no-namespace */
3
+ /* eslint-disable n/no-extraneous-import */
4
+ /* eslint-disable import/no-unresolved */
5
+ /* eslint-env node */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const lex_1 = require("@atproto/lex");
8
+ const index_js_1 = require("./index.js");
9
+ const nodejs_js_1 = require("./nodejs.js");
10
+ var com;
11
+ (function (com) {
12
+ let example;
13
+ (function (example) {
14
+ let echo;
15
+ (function (echo) {
16
+ echo.nsid = 'com.example.echo';
17
+ echo.message = lex_1.l.typedObject(echo.nsid, 'message', lex_1.l.object({
18
+ message: lex_1.l.string(),
19
+ }));
20
+ echo.main = lex_1.l.subscription(echo.nsid, lex_1.l.params({
21
+ message: lex_1.l.string({ minLength: 1 }),
22
+ interval: lex_1.l.optional(lex_1.l.integer({ minimum: 0, default: 500 })),
23
+ }), lex_1.l.typedUnion([lex_1.l.typedRef(() => echo.message)], false));
24
+ })(echo = example.echo || (example.echo = {}));
25
+ })(example = com.example || (com.example = {}));
26
+ })(com || (com = {}));
27
+ const router = new index_js_1.LexRouter({ upgradeWebSocket: nodejs_js_1.upgradeWebSocket })
28
+ //
29
+ .add(com.example.echo, async function* ({ params: { interval, message } }) {
30
+ while (true) {
31
+ yield com.example.echo.message.$build({ message });
32
+ await new Promise((resolve) => setTimeout(resolve, interval));
33
+ }
34
+ });
35
+ (0, nodejs_js_1.serve)(router, { port: 8080, host: '0.0.0.0' });
36
+ //# sourceMappingURL=example.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"example.js","sourceRoot":"","sources":["../src/example.ts"],"names":[],"mappings":";AAAA,oDAAoD;AACpD,2CAA2C;AAC3C,yCAAyC;AACzC,qBAAqB;;AAErB,sCAAgC;AAChC,yCAAsC;AACtC,2CAAqD;AAErD,IAAU,GAAG,CAuBZ;AAvBD,WAAU,GAAG;IACX,IAAiB,OAAO,CAqBvB;IArBD,WAAiB,OAAO;QACtB,IAAiB,IAAI,CAmBpB;QAnBD,WAAiB,IAAI;YACN,SAAI,GAAG,kBAAkB,CAAA;YAEzB,YAAO,GAAG,OAAC,CAAC,WAAW,CAClC,KAAA,IAAI,EACJ,SAAS,EACT,OAAC,CAAC,MAAM,CAAC;gBACP,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE;aACpB,CAAC,CACH,CAAA;YAEY,SAAI,GAAG,OAAC,CAAC,YAAY,CAChC,KAAA,IAAI,EACJ,OAAC,CAAC,MAAM,CAAC;gBACP,OAAO,EAAE,OAAC,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;gBACnC,QAAQ,EAAE,OAAC,CAAC,QAAQ,CAAC,OAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;aAC9D,CAAC,EACF,OAAC,CAAC,UAAU,CAAC,CAAC,OAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAA,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,CACjD,CAAA;QACH,CAAC,EAnBgB,IAAI,GAAJ,YAAI,KAAJ,YAAI,QAmBpB;IACH,CAAC,EArBgB,OAAO,GAAP,WAAO,KAAP,WAAO,QAqBvB;AACH,CAAC,EAvBS,GAAG,KAAH,GAAG,QAuBZ;AAED,MAAM,MAAM,GAAG,IAAI,oBAAS,CAAC,EAAE,gBAAgB,EAAhB,4BAAgB,EAAE,CAAC;IAChD,EAAE;KACD,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;IACvE,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAA;QAClD,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAA;IAC/D,CAAC;AACH,CAAC,CAAC,CAAA;AAEJ,IAAA,iBAAK,EAAC,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAA","sourcesContent":["/* eslint-disable @typescript-eslint/no-namespace */\n/* eslint-disable n/no-extraneous-import */\n/* eslint-disable import/no-unresolved */\n/* eslint-env node */\n\nimport { l } from '@atproto/lex'\nimport { LexRouter } from './index.js'\nimport { serve, upgradeWebSocket } from './nodejs.js'\n\nnamespace com {\n export namespace example {\n export namespace echo {\n export const nsid = 'com.example.echo'\n\n export const message = l.typedObject(\n nsid,\n 'message',\n l.object({\n message: l.string(),\n }),\n )\n\n export const main = l.subscription(\n nsid,\n l.params({\n message: l.string({ minLength: 1 }),\n interval: l.optional(l.integer({ minimum: 0, default: 500 })),\n }),\n l.typedUnion([l.typedRef(() => message)], false),\n )\n }\n }\n}\n\nconst router = new LexRouter({ upgradeWebSocket })\n //\n .add(com.example.echo, async function* ({ params: { interval, message } }) {\n while (true) {\n yield com.example.echo.message.$build({ message })\n await new Promise((resolve) => setTimeout(resolve, interval))\n }\n })\n\nserve(router, { port: 8080, host: '0.0.0.0' })\n"]}
@@ -0,0 +1,4 @@
1
+ export { LexError, type LexErrorCode, type LexErrorData, } from '@atproto/lex-data';
2
+ export * from './errors.js';
3
+ export * from './lex-server.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,KAAK,YAAY,EACjB,KAAK,YAAY,GAClB,MAAM,mBAAmB,CAAA;AAE1B,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LexError = void 0;
4
+ const tslib_1 = require("tslib");
5
+ var lex_data_1 = require("@atproto/lex-data");
6
+ Object.defineProperty(exports, "LexError", { enumerable: true, get: function () { return lex_data_1.LexError; } });
7
+ tslib_1.__exportStar(require("./errors.js"), exports);
8
+ tslib_1.__exportStar(require("./lex-server.js"), exports);
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;AAAA,8CAI0B;AAHxB,oGAAA,QAAQ,OAAA;AAKV,sDAA2B;AAC3B,0DAA+B","sourcesContent":["export {\n LexError,\n type LexErrorCode,\n type LexErrorData,\n} from '@atproto/lex-data'\n\nexport * from './errors.js'\nexport * from './lex-server.js'\n"]}
@@ -0,0 +1,15 @@
1
+ import { LexError, LexErrorCode } from '@atproto/lex-data';
2
+ export type WWWAuthenticate = {
3
+ [k: string]: Record<string, string>;
4
+ };
5
+ export declare function formatWWWAuthenticate(wwwAuthenticate: WWWAuthenticate): string;
6
+ export declare class LexServerAuthError<N extends LexErrorCode = LexErrorCode> extends LexError<N> {
7
+ readonly wwwAuthenticate?: WWWAuthenticate | undefined;
8
+ name: string;
9
+ constructor(error: N, message: string, wwwAuthenticate?: WWWAuthenticate | undefined, options?: ErrorOptions);
10
+ get wwwAuthenticateHeader(): string;
11
+ toJSON(): import("@atproto/lex-data").LexErrorData<any>;
12
+ toResponse(): Response;
13
+ static from(cause: LexError, wwwAuthenticate?: WWWAuthenticate): LexServerAuthError;
14
+ }
15
+ //# sourceMappingURL=lex-auth-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lex-auth-error.d.ts","sourceRoot":"","sources":["../src/lex-auth-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAE1D,MAAM,MAAM,eAAe,GAAG;IAAE,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAA;AACrE,wBAAgB,qBAAqB,CACnC,eAAe,EAAE,eAAe,GAC/B,MAAM,CAWR;AAED,qBAAa,kBAAkB,CAC7B,CAAC,SAAS,YAAY,GAAG,YAAY,CACrC,SAAQ,QAAQ,CAAC,CAAC,CAAC;IAMjB,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe;IAL5C,IAAI,SAAuB;gBAGzB,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,MAAM,EACN,eAAe,CAAC,EAAE,eAAe,YAAA,EAC1C,OAAO,CAAC,EAAE,YAAY;IAKxB,IAAI,qBAAqB,IAAI,MAAM,CAElC;IAED,MAAM;IAKN,UAAU,IAAI,QAAQ;IAatB,MAAM,CAAC,IAAI,CACT,KAAK,EAAE,QAAQ,EACf,eAAe,CAAC,EAAE,eAAe,GAChC,kBAAkB;CAMtB"}
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LexServerAuthError = void 0;
4
+ exports.formatWWWAuthenticate = formatWWWAuthenticate;
5
+ const lex_data_1 = require("@atproto/lex-data");
6
+ function formatWWWAuthenticate(wwwAuthenticate) {
7
+ return Object.entries(wwwAuthenticate)
8
+ .map(([type, params]) => {
9
+ if (!params)
10
+ return null;
11
+ const paramsEnc = Object.entries(params)
12
+ .filter(([_, val]) => val != null)
13
+ .map(([name, val]) => `${name}=${JSON.stringify(val)}`);
14
+ return paramsEnc?.length ? `${type} ${paramsEnc.join(', ')}` : type;
15
+ })
16
+ .filter(Boolean)
17
+ .join(', ');
18
+ }
19
+ class LexServerAuthError extends lex_data_1.LexError {
20
+ wwwAuthenticate;
21
+ name = 'LexServerAuthError';
22
+ constructor(error, message, wwwAuthenticate, options) {
23
+ super(error, message, options);
24
+ this.wwwAuthenticate = wwwAuthenticate;
25
+ }
26
+ get wwwAuthenticateHeader() {
27
+ return formatWWWAuthenticate(this.wwwAuthenticate ?? {});
28
+ }
29
+ toJSON() {
30
+ const { cause } = this;
31
+ return cause instanceof lex_data_1.LexError ? cause.toJSON() : super.toJSON();
32
+ }
33
+ toResponse() {
34
+ const { wwwAuthenticateHeader } = this;
35
+ const headers = wwwAuthenticateHeader
36
+ ? new Headers({
37
+ 'WWW-Authenticate': wwwAuthenticateHeader,
38
+ 'Access-Control-Expose-Headers': 'WWW-Authenticate', // CORS
39
+ })
40
+ : undefined;
41
+ return Response.json(this.toJSON(), { status: 401, headers });
42
+ }
43
+ static from(cause, wwwAuthenticate) {
44
+ if (cause instanceof LexServerAuthError)
45
+ return cause;
46
+ return new LexServerAuthError(cause.error, cause.message, wwwAuthenticate, {
47
+ cause,
48
+ });
49
+ }
50
+ }
51
+ exports.LexServerAuthError = LexServerAuthError;
52
+ //# sourceMappingURL=lex-auth-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lex-auth-error.js","sourceRoot":"","sources":["../src/lex-auth-error.ts"],"names":[],"mappings":";;;AAGA,sDAaC;AAhBD,gDAA0D;AAG1D,SAAgB,qBAAqB,CACnC,eAAgC;IAEhC,OAAO,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC;SACnC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE;QACtB,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAA;QACxB,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;aACrC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,IAAI,CAAC;aACjC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACzD,OAAO,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;IACrE,CAAC,CAAC;SACD,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,IAAI,CAAC,CAAA;AACf,CAAC;AAED,MAAa,kBAEX,SAAQ,mBAAW;IAMR;IALX,IAAI,GAAG,oBAAoB,CAAA;IAE3B,YACE,KAAQ,EACR,OAAe,EACN,eAAiC,EAC1C,OAAsB;QAEtB,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;QAHrB,oBAAe,GAAf,eAAe,CAAkB;IAI5C,CAAC;IAED,IAAI,qBAAqB;QACvB,OAAO,qBAAqB,CAAC,IAAI,CAAC,eAAe,IAAI,EAAE,CAAC,CAAA;IAC1D,CAAC;IAED,MAAM;QACJ,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAA;QACtB,OAAO,KAAK,YAAY,mBAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAA;IACpE,CAAC;IAED,UAAU;QACR,MAAM,EAAE,qBAAqB,EAAE,GAAG,IAAI,CAAA;QAEtC,MAAM,OAAO,GAAG,qBAAqB;YACnC,CAAC,CAAC,IAAI,OAAO,CAAC;gBACV,kBAAkB,EAAE,qBAAqB;gBACzC,+BAA+B,EAAE,kBAAkB,EAAE,OAAO;aAC7D,CAAC;YACJ,CAAC,CAAC,SAAS,CAAA;QAEb,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAA;IAC/D,CAAC;IAED,MAAM,CAAC,IAAI,CACT,KAAe,EACf,eAAiC;QAEjC,IAAI,KAAK,YAAY,kBAAkB;YAAE,OAAO,KAAK,CAAA;QACrD,OAAO,IAAI,kBAAkB,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,eAAe,EAAE;YACzE,KAAK;SACN,CAAC,CAAA;IACJ,CAAC;CACF;AA7CD,gDA6CC","sourcesContent":["import { LexError, LexErrorCode } from '@atproto/lex-data'\n\nexport type WWWAuthenticate = { [k: string]: Record<string, string> }\nexport function formatWWWAuthenticate(\n wwwAuthenticate: WWWAuthenticate,\n): string {\n return Object.entries(wwwAuthenticate)\n .map(([type, params]) => {\n if (!params) return null\n const paramsEnc = Object.entries(params)\n .filter(([_, val]) => val != null)\n .map(([name, val]) => `${name}=${JSON.stringify(val)}`)\n return paramsEnc?.length ? `${type} ${paramsEnc.join(', ')}` : type\n })\n .filter(Boolean)\n .join(', ')\n}\n\nexport class LexServerAuthError<\n N extends LexErrorCode = LexErrorCode,\n> extends LexError<N> {\n name = 'LexServerAuthError'\n\n constructor(\n error: N,\n message: string,\n readonly wwwAuthenticate?: WWWAuthenticate,\n options?: ErrorOptions,\n ) {\n super(error, message, options)\n }\n\n get wwwAuthenticateHeader(): string {\n return formatWWWAuthenticate(this.wwwAuthenticate ?? {})\n }\n\n toJSON() {\n const { cause } = this\n return cause instanceof LexError ? cause.toJSON() : super.toJSON()\n }\n\n toResponse(): Response {\n const { wwwAuthenticateHeader } = this\n\n const headers = wwwAuthenticateHeader\n ? new Headers({\n 'WWW-Authenticate': wwwAuthenticateHeader,\n 'Access-Control-Expose-Headers': 'WWW-Authenticate', // CORS\n })\n : undefined\n\n return Response.json(this.toJSON(), { status: 401, headers })\n }\n\n static from(\n cause: LexError,\n wwwAuthenticate?: WWWAuthenticate,\n ): LexServerAuthError {\n if (cause instanceof LexServerAuthError) return cause\n return new LexServerAuthError(cause.error, cause.message, wwwAuthenticate, {\n cause,\n })\n }\n}\n"]}
@@ -0,0 +1,80 @@
1
+ import { InferMethodInput, InferMethodMessage, InferMethodOutput, InferMethodOutputBody, InferMethodOutputEncoding, InferMethodParams, Main, Procedure, Query, Subscription } from '@atproto/lex-schema';
2
+ type LexMethod = Query | Procedure | Subscription;
3
+ export type NetAddr = {
4
+ hostname: string;
5
+ port: number;
6
+ transport: 'tcp' | 'udp';
7
+ };
8
+ export type UnixAddr = {
9
+ path: string;
10
+ transport: 'unix' | 'unixpacket';
11
+ };
12
+ export type Addr = NetAddr | UnixAddr;
13
+ export type ConnectionInfo = {
14
+ localAddr?: Addr;
15
+ remoteAddr?: Addr;
16
+ };
17
+ type Handler = (request: Request, connection?: ConnectionInfo) => Promise<Response>;
18
+ export type LexRouterHandlerContext<Method extends LexMethod, Credentials> = {
19
+ credentials: Credentials;
20
+ input: InferMethodInput<Method, Body>;
21
+ params: InferMethodParams<Method>;
22
+ request: Request;
23
+ connection?: ConnectionInfo;
24
+ };
25
+ type AsOptionalPayloadOptions<T> = T extends undefined | void ? {
26
+ encoding?: undefined;
27
+ body?: undefined;
28
+ } : T;
29
+ export type LexRouterHandlerOutput<Method extends Query | Procedure> = Response | ({
30
+ headers?: HeadersInit;
31
+ } & (InferMethodOutputEncoding<Method> extends 'application/json' ? {
32
+ encoding?: 'application/json';
33
+ body: InferMethodOutputBody<Method>;
34
+ } : AsOptionalPayloadOptions<InferMethodOutput<Method, BodyInit>>));
35
+ export type LexRouterMethodHandler<Method extends Query | Procedure = Query | Procedure, Credentials = unknown> = (ctx: LexRouterHandlerContext<Method, Credentials>) => Promise<LexRouterHandlerOutput<Method>>;
36
+ export type LexRouterMethodConfig<Method extends Query | Procedure = Query | Procedure, Credentials = unknown> = {
37
+ handler: LexRouterMethodHandler<Method, Credentials>;
38
+ auth: LexRouterAuth<Method, Credentials>;
39
+ };
40
+ export type LexRouterSubscriptionHandler<Method extends Subscription = Subscription, Credentials = unknown> = (ctx: LexRouterHandlerContext<Method, Credentials>) => AsyncIterable<InferMethodMessage<Method>>;
41
+ export type LexRouterSubscriptionConfig<Method extends Subscription = Subscription, Credentials = unknown> = {
42
+ handler: LexRouterSubscriptionHandler<Method, Credentials>;
43
+ auth: LexRouterAuth<Method, Credentials>;
44
+ };
45
+ export type LexRouterAuthContext<Method extends LexMethod = LexMethod> = {
46
+ params: InferMethodParams<Method>;
47
+ request: Request;
48
+ connection?: ConnectionInfo;
49
+ };
50
+ export type LexRouterAuth<Method extends LexMethod = LexMethod, Credentials = unknown> = (ctx: LexRouterAuthContext<Method>) => Credentials | Promise<Credentials>;
51
+ export type LexErrorHandlerContext = {
52
+ error: unknown;
53
+ request: Request;
54
+ method: LexMethod;
55
+ };
56
+ export type UpgradeWebSocket = (request: Request) => {
57
+ socket: WebSocket;
58
+ response: Response;
59
+ };
60
+ export type LexRouterOptions = {
61
+ upgradeWebSocket?: UpgradeWebSocket;
62
+ onHandlerError?: (ctx: LexErrorHandlerContext) => void | Promise<void>;
63
+ highWaterMark?: number;
64
+ lowWaterMark?: number;
65
+ };
66
+ export declare class LexRouter {
67
+ readonly options: LexRouterOptions;
68
+ private handlers;
69
+ constructor(options?: LexRouterOptions);
70
+ add<M extends Subscription>(ns: Main<M>, handler: LexRouterSubscriptionHandler<M, void>): this;
71
+ add<M extends Subscription, Credentials>(ns: Main<M>, config: LexRouterSubscriptionConfig<M, Credentials>): this;
72
+ add<M extends Query | Procedure>(ns: Main<M>, handler: LexRouterMethodHandler<M, void>): this;
73
+ add<M extends Query | Procedure, Credentials>(ns: Main<M>, config: LexRouterMethodConfig<M, Credentials>): this;
74
+ private buildMethodHandler;
75
+ private buildSubscriptionHandler;
76
+ private handleError;
77
+ handle: Handler;
78
+ }
79
+ export {};
80
+ //# sourceMappingURL=lex-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lex-server.d.ts","sourceRoot":"","sources":["../src/lex-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,285 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LexRouter = void 0;
4
+ const lex_cbor_1 = require("@atproto/lex-cbor");
5
+ const lex_data_1 = require("@atproto/lex-data");
6
+ const lex_json_1 = require("@atproto/lex-json");
7
+ const lex_schema_1 = require("@atproto/lex-schema");
8
+ const drain_websocket_js_1 = require("./lib/drain-websocket.js");
9
+ class LexRouter {
10
+ options;
11
+ handlers = new Map();
12
+ constructor(options = {}) {
13
+ this.options = options;
14
+ }
15
+ add(ns, config) {
16
+ const method = (0, lex_schema_1.getMain)(ns);
17
+ if (this.handlers.has(method.nsid)) {
18
+ throw new TypeError(`Method ${method.nsid} already registered`);
19
+ }
20
+ const methodConfig = typeof config === 'function'
21
+ ? { handler: config, auth: undefined }
22
+ : config;
23
+ const handler = method.type === 'subscription'
24
+ ? this.buildSubscriptionHandler(method, methodConfig.handler, methodConfig.auth)
25
+ : this.buildMethodHandler(method, methodConfig.handler, methodConfig.auth);
26
+ this.handlers.set(method.nsid, handler);
27
+ return this;
28
+ }
29
+ buildMethodHandler(method, methodHandler, auth) {
30
+ const getInput = (method.type === 'procedure'
31
+ ? getProcedureInput.bind(method)
32
+ : getQueryInput.bind(method));
33
+ return async (request, connection) => {
34
+ // @NOTE CORS requests should be handled by a middleware before reaching
35
+ // this point.
36
+ if ((method.type === 'procedure' && request.method !== 'POST') ||
37
+ (method.type === 'query' &&
38
+ request.method !== 'GET' &&
39
+ request.method !== 'HEAD')) {
40
+ return Response.json({ error: 'InvalidRequest', message: 'Method not allowed' }, { status: 405 });
41
+ }
42
+ try {
43
+ const url = new URL(request.url);
44
+ const params = method.parameters.fromURLSearchParams(url.searchParams);
45
+ const credentials = auth
46
+ ? await auth({ params, request, connection })
47
+ : undefined;
48
+ const input = await getInput(request);
49
+ const output = await methodHandler({
50
+ credentials,
51
+ params,
52
+ input,
53
+ request,
54
+ connection,
55
+ });
56
+ if (output instanceof Response) {
57
+ return output;
58
+ }
59
+ // @TODO add validation of output based on method.output.schema?
60
+ if (output.body === undefined && output.encoding === undefined) {
61
+ return new Response(null, { status: 200, headers: output.headers });
62
+ }
63
+ if (method.output?.encoding === 'application/json') {
64
+ return Response.json((0, lex_json_1.lexToJson)(output.body), {
65
+ status: 200,
66
+ headers: output.headers,
67
+ });
68
+ }
69
+ const headers = new Headers(output.headers);
70
+ headers.set('content-type', output.encoding);
71
+ return new Response(output.body, { status: 200, headers });
72
+ }
73
+ catch (error) {
74
+ return this.handleError(request, method, error);
75
+ }
76
+ };
77
+ }
78
+ buildSubscriptionHandler(method, methodHandler, auth) {
79
+ const { onHandlerError, upgradeWebSocket = globalThis.Deno?.upgradeWebSocket, } = this.options;
80
+ if (!upgradeWebSocket) {
81
+ throw new TypeError('WebSocket upgrade not supported in this environment. Please provide an upgradeWebSocket option when creating the LexRouter.');
82
+ }
83
+ return async (request, connection) => {
84
+ if (request.method !== 'GET') {
85
+ return Response.json({ error: 'InvalidRequest', message: 'Method not allowed' }, { status: 405 });
86
+ }
87
+ if (request.headers.get('connection')?.toLowerCase() !== 'upgrade' ||
88
+ request.headers.get('upgrade')?.toLowerCase() !== 'websocket') {
89
+ return Response.json({
90
+ error: 'InvalidRequest',
91
+ message: 'XRPC subscriptions are only available over WebSocket',
92
+ }, {
93
+ status: 426,
94
+ headers: {
95
+ Connection: 'Upgrade',
96
+ Upgrade: 'websocket',
97
+ },
98
+ });
99
+ }
100
+ try {
101
+ const { response, socket } = upgradeWebSocket(request);
102
+ socket.addEventListener('message', () => {
103
+ const error = new lex_data_1.LexError('InvalidRequest', 'XRPC subscriptions do not accept messages');
104
+ socket.send(encodeErrorFrame(error));
105
+ socket.close(1008, error.error);
106
+ });
107
+ socket.addEventListener('open', async () => {
108
+ try {
109
+ const url = new URL(request.url);
110
+ const params = method.parameters.fromURLSearchParams(url.searchParams);
111
+ const credentials = auth
112
+ ? await auth({ params, request, connection })
113
+ : undefined;
114
+ request.signal.throwIfAborted();
115
+ const iterable = methodHandler({
116
+ credentials,
117
+ params,
118
+ input: undefined,
119
+ request,
120
+ connection,
121
+ });
122
+ const iterator = iterable[Symbol.asyncIterator]();
123
+ if (iterator.return) {
124
+ const abort = async () => {
125
+ socket.removeEventListener('error', abort);
126
+ socket.removeEventListener('close', abort);
127
+ try {
128
+ await iterator.return();
129
+ }
130
+ catch {
131
+ // Ignore
132
+ }
133
+ };
134
+ socket.addEventListener('error', abort);
135
+ socket.addEventListener('close', abort);
136
+ }
137
+ while (socket.readyState === 1) {
138
+ const result = await iterator.next();
139
+ if (result.done)
140
+ break;
141
+ // Should not be needed (socket would emit "close" event)
142
+ request.signal.throwIfAborted();
143
+ // @TODO add validation of output based on method.output.schema?
144
+ const data = encodeMessageFrame(method, result.value);
145
+ socket.send(data);
146
+ // Apply backpressure by waiting for the buffered data to drain
147
+ // before generating the next message
148
+ await (0, drain_websocket_js_1.drainWebsocket)(socket, request.signal, this.options);
149
+ }
150
+ socket.close(1000);
151
+ }
152
+ catch (error) {
153
+ // If the socket is still open, send an error frame before closing
154
+ if (socket.readyState === 1) {
155
+ const lexError = error instanceof lex_data_1.LexError
156
+ ? error
157
+ : new lex_data_1.LexError('InternalError', 'An internal error occurred');
158
+ socket.send(encodeErrorFrame(lexError));
159
+ socket.close(
160
+ // https://www.rfc-editor.org/rfc/rfc6455#section-7.4.1
161
+ error instanceof lex_data_1.LexError ? 1008 : 1011, lexError.error);
162
+ }
163
+ // Only report unexpected processing errors
164
+ if (onHandlerError && !isAbortReason(request.signal, error)) {
165
+ await onHandlerError({ error, request, method });
166
+ }
167
+ }
168
+ });
169
+ return response;
170
+ }
171
+ catch (error) {
172
+ return this.handleError(request, method, error);
173
+ }
174
+ };
175
+ }
176
+ async handleError(request, method, error) {
177
+ // Only report unexpected processing errors
178
+ const { onHandlerError } = this.options;
179
+ if (onHandlerError && !isAbortReason(request.signal, error)) {
180
+ await onHandlerError({ error, request, method });
181
+ }
182
+ if (error instanceof lex_data_1.LexError) {
183
+ return error.toResponse();
184
+ }
185
+ return Response.json({ error: 'InternalError', message: 'An internal error occurred' }, { status: 500 });
186
+ }
187
+ handle = async (request, connection) => {
188
+ const nsid = extractMethodNsid(request);
189
+ const handler = this.handlers.get(nsid);
190
+ if (handler)
191
+ return handler(request, connection);
192
+ if (!nsid || !(0, lex_schema_1.isNsidString)(nsid)) {
193
+ return Response.json({
194
+ error: 'InvalidRequest',
195
+ message: 'Invalid XRPC method path',
196
+ }, { status: 404 });
197
+ }
198
+ return Response.json({
199
+ error: 'MethodNotImplemented',
200
+ message: `XRPC method "${nsid}" not implemented on this server`,
201
+ }, { status: 501 });
202
+ };
203
+ }
204
+ exports.LexRouter = LexRouter;
205
+ function extractMethodNsid(request) {
206
+ const { pathname } = new URL(request.url);
207
+ if (!pathname.startsWith('/xrpc/'))
208
+ return null;
209
+ if (pathname.includes('/', 6))
210
+ return null;
211
+ // We don't really need to validate the NSID here, the existence of the route
212
+ // (which is looked up based on an NSID) is sufficient.
213
+ return pathname.slice(6);
214
+ }
215
+ async function getProcedureInput(request) {
216
+ const encodingRaw = request.headers
217
+ .get('content-type')
218
+ ?.split(';')[0]
219
+ .trim()
220
+ .toLowerCase();
221
+ const encoding = encodingRaw ||
222
+ // If the caller did not provide a content-type, but the method
223
+ // expects an input, assume binary
224
+ (request.body != null && this.input.encoding != null
225
+ ? 'application/octet-stream'
226
+ : undefined);
227
+ if (!this.input.matchesEncoding(encoding)) {
228
+ throw new lex_data_1.LexError('InvalidRequest', `Invalid content-type: ${encoding}`);
229
+ }
230
+ if (this.input.encoding === 'application/json') {
231
+ // @TODO limit size?
232
+ const body = this.input.schema
233
+ ? this.input.schema.parse((0, lex_json_1.lexParse)(await request.text()))
234
+ : (0, lex_json_1.lexParse)(await request.text());
235
+ return { encoding, body };
236
+ }
237
+ else if (this.input.encoding) {
238
+ const body = request;
239
+ return { encoding, body };
240
+ }
241
+ else {
242
+ return undefined;
243
+ }
244
+ }
245
+ async function getQueryInput(request) {
246
+ if (request.body ||
247
+ request.headers.has('content-type') ||
248
+ request.headers.has('content-length')) {
249
+ throw new lex_data_1.LexError('InvalidRequest', 'GET requests must not have a body');
250
+ }
251
+ return undefined;
252
+ }
253
+ // Pre-encoded frame header for error frames
254
+ const ERROR_FRAME_HEADER = /*#__PURE__*/ (0, lex_cbor_1.encode)({ op: -1 });
255
+ function encodeErrorFrame(error) {
256
+ return (0, lex_data_1.ui8Concat)([ERROR_FRAME_HEADER, (0, lex_cbor_1.encode)(error.toJSON())]);
257
+ }
258
+ // Pre-encoded frame header for message frames with unknown type
259
+ const UNKNOWN_MESSAGE_FRAME_HEADER = /*#__PURE__*/ (0, lex_cbor_1.encode)({ op: 1 });
260
+ function encodeMessageFrame(method, value) {
261
+ if ((0, lex_data_1.isPlainObject)(value) && typeof value.$type === 'string') {
262
+ const { $type, ...rest } = value;
263
+ return (0, lex_data_1.ui8Concat)([
264
+ (0, lex_cbor_1.encode)({
265
+ op: 1,
266
+ t:
267
+ // If $type starts with `nsid#`, strip the NSID prefix
268
+ $type.charCodeAt(0) !== 0x23 && // '#'
269
+ $type.charCodeAt(method.nsid.length) === 0x23 && // '#'
270
+ $type.startsWith(method.nsid)
271
+ ? $type.slice(method.nsid.length)
272
+ : $type,
273
+ }),
274
+ (0, lex_cbor_1.encode)(rest),
275
+ ]);
276
+ }
277
+ return (0, lex_data_1.ui8Concat)([UNKNOWN_MESSAGE_FRAME_HEADER, (0, lex_cbor_1.encode)(value)]);
278
+ }
279
+ function isAbortReason(signal, error) {
280
+ if (!signal.aborted || signal.reason == null)
281
+ return false;
282
+ return (error === signal.reason ||
283
+ (error instanceof Error && error.cause === signal.reason));
284
+ }
285
+ //# sourceMappingURL=lex-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lex-server.js","sourceRoot":"","sources":["../src/lex-server.ts"],"names":[],"mappings":"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","sourcesContent":["import { encode } from '@atproto/lex-cbor'\nimport { LexError, LexValue, isPlainObject, ui8Concat } from '@atproto/lex-data'\nimport { lexParse, lexToJson } from '@atproto/lex-json'\nimport {\n InferMethodInput,\n InferMethodMessage,\n InferMethodOutput,\n InferMethodOutputBody,\n InferMethodOutputEncoding,\n InferMethodParams,\n Main,\n NsidString,\n Procedure,\n Query,\n Subscription,\n getMain,\n isNsidString,\n} from '@atproto/lex-schema'\nimport { drainWebsocket } from './lib/drain-websocket.js'\n\ntype LexMethod = Query | Procedure | Subscription\n\nexport type NetAddr = {\n hostname: string\n port: number\n transport: 'tcp' | 'udp'\n}\n\nexport type UnixAddr = {\n path: string\n transport: 'unix' | 'unixpacket'\n}\n\nexport type Addr = NetAddr | UnixAddr\n\nexport type ConnectionInfo = {\n localAddr?: Addr\n remoteAddr?: Addr\n}\n\ntype Handler = (\n request: Request,\n connection?: ConnectionInfo,\n) => Promise<Response>\n\nexport type LexRouterHandlerContext<Method extends LexMethod, Credentials> = {\n credentials: Credentials\n input: InferMethodInput<Method, Body>\n params: InferMethodParams<Method>\n request: Request\n connection?: ConnectionInfo\n}\n\ntype AsOptionalPayloadOptions<T> = T extends undefined | void\n ? { encoding?: undefined; body?: undefined }\n : T\n\nexport type LexRouterHandlerOutput<Method extends Query | Procedure> =\n | Response\n | ({\n headers?: HeadersInit\n } & (InferMethodOutputEncoding<Method> extends 'application/json'\n ? {\n // Allow omitting body when output is JSON\n encoding?: 'application/json'\n body: InferMethodOutputBody<Method>\n }\n : AsOptionalPayloadOptions<InferMethodOutput<Method, BodyInit>>))\n\nexport type LexRouterMethodHandler<\n Method extends Query | Procedure = Query | Procedure,\n Credentials = unknown,\n> = (\n ctx: LexRouterHandlerContext<Method, Credentials>,\n) => Promise<LexRouterHandlerOutput<Method>>\n\nexport type LexRouterMethodConfig<\n Method extends Query | Procedure = Query | Procedure,\n Credentials = unknown,\n> = {\n handler: LexRouterMethodHandler<Method, Credentials>\n auth: LexRouterAuth<Method, Credentials>\n}\n\nexport type LexRouterSubscriptionHandler<\n Method extends Subscription = Subscription,\n Credentials = unknown,\n> = (\n ctx: LexRouterHandlerContext<Method, Credentials>,\n) => AsyncIterable<InferMethodMessage<Method>>\n\nexport type LexRouterSubscriptionConfig<\n Method extends Subscription = Subscription,\n Credentials = unknown,\n> = {\n handler: LexRouterSubscriptionHandler<Method, Credentials>\n auth: LexRouterAuth<Method, Credentials>\n}\n\nexport type LexRouterAuthContext<Method extends LexMethod = LexMethod> = {\n params: InferMethodParams<Method>\n request: Request\n connection?: ConnectionInfo\n}\n\nexport type LexRouterAuth<\n Method extends LexMethod = LexMethod,\n Credentials = unknown,\n> = (ctx: LexRouterAuthContext<Method>) => Credentials | Promise<Credentials>\n\nexport type LexErrorHandlerContext = {\n error: unknown\n request: Request\n method: LexMethod\n}\n\nexport type UpgradeWebSocket = (request: Request) => {\n socket: WebSocket\n response: Response\n}\n\nexport type LexRouterOptions = {\n upgradeWebSocket?: UpgradeWebSocket\n onHandlerError?: (ctx: LexErrorHandlerContext) => void | Promise<void>\n highWaterMark?: number\n lowWaterMark?: number\n}\n\nexport class LexRouter {\n private handlers: Map<NsidString, Handler> = new Map()\n\n constructor(readonly options: LexRouterOptions = {}) {}\n\n add<M extends Subscription>(\n ns: Main<M>,\n handler: LexRouterSubscriptionHandler<M, void>,\n ): this\n add<M extends Subscription, Credentials>(\n ns: Main<M>,\n config: LexRouterSubscriptionConfig<M, Credentials>,\n ): this\n add<M extends Query | Procedure>(\n ns: Main<M>,\n handler: LexRouterMethodHandler<M, void>,\n ): this\n add<M extends Query | Procedure, Credentials>(\n ns: Main<M>,\n config: LexRouterMethodConfig<M, Credentials>,\n ): this\n add<M extends LexMethod>(\n ns: Main<M>,\n config:\n | LexRouterSubscriptionHandler<any, any>\n | LexRouterSubscriptionConfig<any, any>\n | LexRouterMethodHandler<any, any>\n | LexRouterMethodConfig<any, any>,\n ) {\n const method = getMain(ns)\n if (this.handlers.has(method.nsid)) {\n throw new TypeError(`Method ${method.nsid} already registered`)\n }\n const methodConfig =\n typeof config === 'function'\n ? { handler: config, auth: undefined }\n : config\n\n const handler: Handler =\n method.type === 'subscription'\n ? this.buildSubscriptionHandler(\n method,\n methodConfig.handler as LexRouterSubscriptionHandler<any, any>,\n methodConfig.auth,\n )\n : this.buildMethodHandler(\n method,\n methodConfig.handler as LexRouterMethodHandler<any, any>,\n methodConfig.auth,\n )\n\n this.handlers.set(method.nsid, handler)\n\n return this\n }\n\n private buildMethodHandler<Method extends Query | Procedure, Credentials>(\n method: Method,\n methodHandler: LexRouterMethodHandler<Method, Credentials>,\n auth?: LexRouterAuth<Method, Credentials>,\n ): Handler {\n const getInput = (\n method.type === 'procedure'\n ? getProcedureInput.bind(method)\n : getQueryInput.bind(method)\n ) as (request: Request) => Promise<InferMethodInput<Method, Body>>\n\n return async (\n request: Request,\n connection?: ConnectionInfo,\n ): Promise<Response> => {\n // @NOTE CORS requests should be handled by a middleware before reaching\n // this point.\n if (\n (method.type === 'procedure' && request.method !== 'POST') ||\n (method.type === 'query' &&\n request.method !== 'GET' &&\n request.method !== 'HEAD')\n ) {\n return Response.json(\n { error: 'InvalidRequest', message: 'Method not allowed' },\n { status: 405 },\n )\n }\n\n try {\n const url = new URL(request.url)\n const params = method.parameters.fromURLSearchParams(url.searchParams)\n\n const credentials = auth\n ? await auth({ params, request, connection })\n : (undefined as Credentials)\n\n const input = await getInput(request)\n\n const output = await methodHandler({\n credentials,\n params,\n input,\n request,\n connection,\n })\n\n if (output instanceof Response) {\n return output\n }\n\n // @TODO add validation of output based on method.output.schema?\n\n if (output.body === undefined && output.encoding === undefined) {\n return new Response(null, { status: 200, headers: output.headers })\n }\n\n if (method.output?.encoding === 'application/json') {\n return Response.json(lexToJson(output.body as LexValue), {\n status: 200,\n headers: output.headers,\n })\n }\n\n const headers = new Headers(output.headers)\n headers.set('content-type', output.encoding!)\n return new Response(output.body, { status: 200, headers })\n } catch (error) {\n return this.handleError(request, method, error)\n }\n }\n }\n\n private buildSubscriptionHandler<Method extends Subscription, Credentials>(\n method: Method,\n methodHandler: LexRouterSubscriptionHandler<Method, Credentials>,\n auth?: LexRouterAuth<Method, Credentials>,\n ): Handler {\n const {\n onHandlerError,\n upgradeWebSocket = (globalThis as any).Deno?.upgradeWebSocket as\n | UpgradeWebSocket\n | undefined,\n } = this.options\n if (!upgradeWebSocket) {\n throw new TypeError(\n 'WebSocket upgrade not supported in this environment. Please provide an upgradeWebSocket option when creating the LexRouter.',\n )\n }\n\n return async (\n request: Request,\n connection?: ConnectionInfo,\n ): Promise<Response> => {\n if (request.method !== 'GET') {\n return Response.json(\n { error: 'InvalidRequest', message: 'Method not allowed' },\n { status: 405 },\n )\n }\n\n if (\n request.headers.get('connection')?.toLowerCase() !== 'upgrade' ||\n request.headers.get('upgrade')?.toLowerCase() !== 'websocket'\n ) {\n return Response.json(\n {\n error: 'InvalidRequest',\n message: 'XRPC subscriptions are only available over WebSocket',\n },\n {\n status: 426,\n headers: {\n Connection: 'Upgrade',\n Upgrade: 'websocket',\n },\n },\n )\n }\n\n try {\n const { response, socket } = upgradeWebSocket(request)\n\n socket.addEventListener('message', () => {\n const error = new LexError(\n 'InvalidRequest',\n 'XRPC subscriptions do not accept messages',\n )\n socket.send(encodeErrorFrame(error))\n socket.close(1008, error.error)\n })\n\n socket.addEventListener('open', async () => {\n try {\n const url = new URL(request.url)\n const params = method.parameters.fromURLSearchParams(\n url.searchParams,\n )\n\n const credentials: Credentials = auth\n ? await auth({ params, request, connection })\n : (undefined as Credentials)\n\n request.signal.throwIfAborted()\n\n const iterable = methodHandler({\n credentials,\n params,\n input: undefined as InferMethodInput<Method, Body>,\n request,\n connection,\n })\n\n const iterator = iterable[Symbol.asyncIterator]()\n\n if (iterator.return) {\n const abort = async () => {\n socket.removeEventListener('error', abort)\n socket.removeEventListener('close', abort)\n try {\n await iterator.return!()\n } catch {\n // Ignore\n }\n }\n socket.addEventListener('error', abort)\n socket.addEventListener('close', abort)\n }\n\n while (socket.readyState === 1) {\n const result = await iterator.next()\n if (result.done) break\n\n // Should not be needed (socket would emit \"close\" event)\n request.signal.throwIfAborted()\n\n // @TODO add validation of output based on method.output.schema?\n\n const data = encodeMessageFrame(method, result.value)\n\n socket.send(data)\n\n // Apply backpressure by waiting for the buffered data to drain\n // before generating the next message\n await drainWebsocket(socket, request.signal, this.options)\n }\n\n socket.close(1000)\n } catch (error) {\n // If the socket is still open, send an error frame before closing\n if (socket.readyState === 1) {\n const lexError =\n error instanceof LexError\n ? error\n : new LexError('InternalError', 'An internal error occurred')\n\n socket.send(encodeErrorFrame(lexError))\n\n socket.close(\n // https://www.rfc-editor.org/rfc/rfc6455#section-7.4.1\n error instanceof LexError ? 1008 : 1011,\n lexError.error,\n )\n }\n\n // Only report unexpected processing errors\n if (onHandlerError && !isAbortReason(request.signal, error)) {\n await onHandlerError({ error, request, method })\n }\n }\n })\n\n return response\n } catch (error) {\n return this.handleError(request, method, error)\n }\n }\n }\n\n private async handleError(\n request: Request,\n method: LexMethod,\n error: unknown,\n ) {\n // Only report unexpected processing errors\n const { onHandlerError } = this.options\n if (onHandlerError && !isAbortReason(request.signal, error)) {\n await onHandlerError({ error, request, method })\n }\n\n if (error instanceof LexError) {\n return error.toResponse()\n }\n\n return Response.json(\n { error: 'InternalError', message: 'An internal error occurred' },\n { status: 500 },\n )\n }\n\n handle: Handler = async (\n request: Request,\n connection?: ConnectionInfo,\n ): Promise<Response> => {\n const nsid = extractMethodNsid(request)\n\n const handler = (this.handlers as Map<string | null, Handler>).get(nsid)\n if (handler) return handler(request, connection)\n\n if (!nsid || !isNsidString(nsid)) {\n return Response.json(\n {\n error: 'InvalidRequest',\n message: 'Invalid XRPC method path',\n },\n { status: 404 },\n )\n }\n\n return Response.json(\n {\n error: 'MethodNotImplemented',\n message: `XRPC method \"${nsid}\" not implemented on this server`,\n },\n { status: 501 },\n )\n }\n}\n\nfunction extractMethodNsid(request: Request): string | null {\n const { pathname } = new URL(request.url)\n if (!pathname.startsWith('/xrpc/')) return null\n if (pathname.includes('/', 6)) return null\n // We don't really need to validate the NSID here, the existence of the route\n // (which is looked up based on an NSID) is sufficient.\n return pathname.slice(6)\n}\n\nasync function getProcedureInput<M extends Procedure>(\n this: M,\n request: Request,\n): Promise<InferMethodInput<M, Body>> {\n const encodingRaw = request.headers\n .get('content-type')\n ?.split(';')[0]\n .trim()\n .toLowerCase()\n\n const encoding =\n encodingRaw ||\n // If the caller did not provide a content-type, but the method\n // expects an input, assume binary\n (request.body != null && this.input.encoding != null\n ? 'application/octet-stream'\n : undefined)\n\n if (!this.input.matchesEncoding(encoding)) {\n throw new LexError('InvalidRequest', `Invalid content-type: ${encoding}`)\n }\n\n if (this.input.encoding === 'application/json') {\n // @TODO limit size?\n const body = this.input.schema\n ? this.input.schema.parse(lexParse(await request.text()))\n : lexParse(await request.text())\n return { encoding, body } as InferMethodInput<M, Body>\n } else if (this.input.encoding) {\n const body: Body = request\n return { encoding, body } as InferMethodInput<M, Body>\n } else {\n return undefined as InferMethodInput<M, Body>\n }\n}\n\nasync function getQueryInput<M extends Query>(\n this: M,\n request: Request,\n): Promise<InferMethodInput<M, Body>> {\n if (\n request.body ||\n request.headers.has('content-type') ||\n request.headers.has('content-length')\n ) {\n throw new LexError('InvalidRequest', 'GET requests must not have a body')\n }\n\n return undefined as InferMethodInput<M, Body>\n}\n\n// Pre-encoded frame header for error frames\nconst ERROR_FRAME_HEADER = /*#__PURE__*/ encode({ op: -1 })\n\nfunction encodeErrorFrame(error: LexError): Uint8Array {\n return ui8Concat([ERROR_FRAME_HEADER, encode(error.toJSON())])\n}\n\n// Pre-encoded frame header for message frames with unknown type\nconst UNKNOWN_MESSAGE_FRAME_HEADER = /*#__PURE__*/ encode({ op: 1 })\n\nfunction encodeMessageFrame(method: Subscription, value: LexValue): Uint8Array {\n if (isPlainObject(value) && typeof value.$type === 'string') {\n const { $type, ...rest } = value\n return ui8Concat([\n encode({\n op: 1,\n t:\n // If $type starts with `nsid#`, strip the NSID prefix\n $type.charCodeAt(0) !== 0x23 && // '#'\n $type.charCodeAt(method.nsid.length) === 0x23 && // '#'\n $type.startsWith(method.nsid)\n ? $type.slice(method.nsid.length)\n : $type,\n }),\n encode(rest),\n ])\n }\n\n return ui8Concat([UNKNOWN_MESSAGE_FRAME_HEADER, encode(value)])\n}\n\nfunction isAbortReason(signal: AbortSignal, error: unknown): boolean {\n if (!signal.aborted || signal.reason == null) return false\n return (\n error === signal.reason ||\n (error instanceof Error && error.cause === signal.reason)\n )\n}\n"]}
@@ -0,0 +1,6 @@
1
+ export declare function drainWebsocket(socket: WebSocket, signal: AbortSignal, { highWaterMark, // 250 KB
2
+ lowWaterMark, }?: {
3
+ highWaterMark?: number;
4
+ lowWaterMark?: number;
5
+ }): Promise<void>;
6
+ //# sourceMappingURL=drain-websocket.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drain-websocket.d.ts","sourceRoot":"","sources":["../../src/lib/drain-websocket.ts"],"names":[],"mappings":"AAEA,wBAAsB,cAAc,CAClC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,WAAW,EACnB,EACE,aAAuB,EAAE,SAAS;AAClC,YAAqB,GACtB,GAAE;IACD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,YAAY,CAAC,EAAE,MAAM,CAAA;CACjB,GACL,OAAO,CAAC,IAAI,CAAC,CAUf"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.drainWebsocket = drainWebsocket;
4
+ const sleep_js_1 = require("./sleep.js");
5
+ async function drainWebsocket(socket, signal, { highWaterMark = 250_000, // 250 KB
6
+ lowWaterMark = 50_000, // 50 KB
7
+ } = {}) {
8
+ if (socket.bufferedAmount > highWaterMark) {
9
+ while (socket.readyState === 1 &&
10
+ socket.bufferedAmount !== 0 &&
11
+ socket.bufferedAmount > lowWaterMark) {
12
+ await (0, sleep_js_1.abortableSleep)(10, signal);
13
+ }
14
+ }
15
+ }
16
+ //# sourceMappingURL=drain-websocket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drain-websocket.js","sourceRoot":"","sources":["../../src/lib/drain-websocket.ts"],"names":[],"mappings":";;AAEA,wCAoBC;AAtBD,yCAA2C;AAEpC,KAAK,UAAU,cAAc,CAClC,MAAiB,EACjB,MAAmB,EACnB,EACE,aAAa,GAAG,OAAO,EAAE,SAAS;AAClC,YAAY,GAAG,MAAM,EAAE,QAAQ;KAI7B,EAAE;IAEN,IAAI,MAAM,CAAC,cAAc,GAAG,aAAa,EAAE,CAAC;QAC1C,OACE,MAAM,CAAC,UAAU,KAAK,CAAC;YACvB,MAAM,CAAC,cAAc,KAAK,CAAC;YAC3B,MAAM,CAAC,cAAc,GAAG,YAAY,EACpC,CAAC;YACD,MAAM,IAAA,yBAAc,EAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QAClC,CAAC;IACH,CAAC;AACH,CAAC","sourcesContent":["import { abortableSleep } from './sleep.js'\n\nexport async function drainWebsocket(\n socket: WebSocket,\n signal: AbortSignal,\n {\n highWaterMark = 250_000, // 250 KB\n lowWaterMark = 50_000, // 50 KB\n }: {\n highWaterMark?: number\n lowWaterMark?: number\n } = {},\n): Promise<void> {\n if (socket.bufferedAmount > highWaterMark) {\n while (\n socket.readyState === 1 &&\n socket.bufferedAmount !== 0 &&\n socket.bufferedAmount > lowWaterMark\n ) {\n await abortableSleep(10, signal)\n }\n }\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export declare function abortableSleep(ms: number, signal: AbortSignal): Promise<void>;
2
+ //# sourceMappingURL=sleep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sleep.d.ts","sourceRoot":"","sources":["../../src/lib/sleep.ts"],"names":[],"mappings":"AAAA,wBAAsB,cAAc,CAClC,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,IAAI,CAAC,CAqBf"}