@atproto/lex-server 0.0.4 → 0.0.6
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 +22 -0
- package/LICENSE.txt +1 -1
- package/README.md +1 -1
- package/dist/lex-server.d.ts +2 -2
- package/dist/lex-server.d.ts.map +1 -1
- package/dist/lex-server.js +14 -10
- package/dist/lex-server.js.map +1 -1
- package/dist/nodejs.d.ts +5 -7
- package/dist/nodejs.d.ts.map +1 -1
- package/dist/nodejs.js +6 -6
- package/dist/nodejs.js.map +1 -1
- package/package.json +7 -7
- package/src/lex-server.test.ts +58 -58
- package/src/lex-server.ts +17 -13
- package/src/nodejs.ts +11 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @atproto/lex-server
|
|
2
2
|
|
|
3
|
+
## 0.0.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#4562](https://github.com/bluesky-social/atproto/pull/4562) [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Rename `handle()` method to `fetch()`
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`99963d0`](https://github.com/bluesky-social/atproto/commit/99963d002a9e030e79aed5fba700e0a68f31e101), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`aaedafc`](https://github.com/bluesky-social/atproto/commit/aaedafc6baef106b85e0954d8474cec21c00c1c2), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`ecf5921`](https://github.com/bluesky-social/atproto/commit/ecf59214d59d9d2530c197c0679d26e76c6a60ef), [`99963d0`](https://github.com/bluesky-social/atproto/commit/99963d002a9e030e79aed5fba700e0a68f31e101), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`99963d0`](https://github.com/bluesky-social/atproto/commit/99963d002a9e030e79aed5fba700e0a68f31e101), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`99963d0`](https://github.com/bluesky-social/atproto/commit/99963d002a9e030e79aed5fba700e0a68f31e101), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b)]:
|
|
10
|
+
- @atproto/lex-schema@0.0.10
|
|
11
|
+
- @atproto/lex-cbor@0.0.9
|
|
12
|
+
- @atproto/lex-json@0.0.9
|
|
13
|
+
- @atproto/lex-data@0.0.9
|
|
14
|
+
|
|
15
|
+
## 0.0.5
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- Updated dependencies [[`dfd4bee`](https://github.com/bluesky-social/atproto/commit/dfd4bee4abbc1a3e53531bb499a6f3169c13ed9e), [`dfd4bee`](https://github.com/bluesky-social/atproto/commit/dfd4bee4abbc1a3e53531bb499a6f3169c13ed9e), [`dfd4bee`](https://github.com/bluesky-social/atproto/commit/dfd4bee4abbc1a3e53531bb499a6f3169c13ed9e), [`dfd4bee`](https://github.com/bluesky-social/atproto/commit/dfd4bee4abbc1a3e53531bb499a6f3169c13ed9e)]:
|
|
20
|
+
- @atproto/lex-data@0.0.8
|
|
21
|
+
- @atproto/lex-cbor@0.0.8
|
|
22
|
+
- @atproto/lex-json@0.0.8
|
|
23
|
+
- @atproto/lex-schema@0.0.9
|
|
24
|
+
|
|
3
25
|
## 0.0.4
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
package/LICENSE.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Dual MIT/Apache-2.0 License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2022-
|
|
3
|
+
Copyright (c) 2022-2026 Bluesky Social PBC, and Contributors
|
|
4
4
|
|
|
5
5
|
Except as otherwise noted in individual files, this software is licensed under the MIT license (<http://opensource.org/licenses/MIT>), or the Apache License, Version 2.0 (<http://www.apache.org/licenses/LICENSE-2.0>).
|
|
6
6
|
|
package/README.md
CHANGED
|
@@ -181,7 +181,7 @@ Handlers receive a context object with the following properties:
|
|
|
181
181
|
type LexRouterHandlerContext<Method, Credentials> = {
|
|
182
182
|
credentials: Credentials // Result of auth function (undefined if no auth)
|
|
183
183
|
input: InferMethodInput<Method> // Parsed request body (procedures only)
|
|
184
|
-
params: InferMethodParams<Method> //
|
|
184
|
+
params: InferMethodParams<Method> // Parsed URL query parameters
|
|
185
185
|
request: Request // Original Web Request object
|
|
186
186
|
connection?: ConnectionInfo // Network connection info
|
|
187
187
|
}
|
package/dist/lex-server.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export type ConnectionInfo<A extends Addr = Addr> = {
|
|
|
15
15
|
remoteAddr: A;
|
|
16
16
|
completed: Promise<void>;
|
|
17
17
|
};
|
|
18
|
-
type
|
|
18
|
+
export type FetchHandler = (request: Request, connection?: ConnectionInfo) => Promise<Response>;
|
|
19
19
|
export type LexRouterHandlerContext<Method extends LexMethod, Credentials> = {
|
|
20
20
|
credentials: Credentials;
|
|
21
21
|
input: InferMethodInput<Method, Body>;
|
|
@@ -78,7 +78,7 @@ export declare class LexRouter {
|
|
|
78
78
|
private buildMethodHandler;
|
|
79
79
|
private buildSubscriptionHandler;
|
|
80
80
|
private handleError;
|
|
81
|
-
|
|
81
|
+
fetch: FetchHandler;
|
|
82
82
|
}
|
|
83
83
|
export {};
|
|
84
84
|
//# sourceMappingURL=lex-server.d.ts.map
|
package/dist/lex-server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lex-server.d.ts","sourceRoot":"","sources":["../src/lex-server.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,qBAAqB,EACrB,yBAAyB,EACzB,iBAAiB,EACjB,IAAI,EAEJ,SAAS,EACT,KAAK,EACL,YAAY,EAGb,MAAM,qBAAqB,CAAA;AAG5B,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;AAClC,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,SAAS,GAAG,YAAY,CAAA;AAExD,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,KAAK,GAAG,KAAK,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,GAAG,YAAY,CAAA;CACjC,CAAA;AAED,MAAM,MAAM,IAAI,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAA;AAEjD,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,IAAI,GAAG,IAAI,IAAI;IAClD,UAAU,EAAE,CAAC,CAAA;IACb,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB,CAAA;AAED,
|
|
1
|
+
{"version":3,"file":"lex-server.d.ts","sourceRoot":"","sources":["../src/lex-server.ts"],"names":[],"mappings":"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"}
|
package/dist/lex-server.js
CHANGED
|
@@ -20,10 +20,10 @@ class LexRouter {
|
|
|
20
20
|
const methodConfig = typeof config === 'function'
|
|
21
21
|
? { handler: config, auth: undefined }
|
|
22
22
|
: config;
|
|
23
|
-
const
|
|
23
|
+
const fetch = method.type === 'subscription'
|
|
24
24
|
? this.buildSubscriptionHandler(method, methodConfig.handler, methodConfig.auth)
|
|
25
25
|
: this.buildMethodHandler(method, methodConfig.handler, methodConfig.auth);
|
|
26
|
-
this.handlers.set(method.nsid,
|
|
26
|
+
this.handlers.set(method.nsid, fetch);
|
|
27
27
|
return this;
|
|
28
28
|
}
|
|
29
29
|
buildMethodHandler(method, methodHandler, auth) {
|
|
@@ -69,7 +69,10 @@ class LexRouter {
|
|
|
69
69
|
}
|
|
70
70
|
const headers = new Headers(output.headers);
|
|
71
71
|
headers.set('content-type', output.encoding);
|
|
72
|
-
return new Response(output.body, {
|
|
72
|
+
return new Response(output.body, {
|
|
73
|
+
status: 200,
|
|
74
|
+
headers,
|
|
75
|
+
});
|
|
73
76
|
}
|
|
74
77
|
catch (error) {
|
|
75
78
|
return this.handleError(request, method, error);
|
|
@@ -192,11 +195,13 @@ class LexRouter {
|
|
|
192
195
|
}
|
|
193
196
|
return Response.json({ error: 'InternalError', message: 'An internal error occurred' }, { status: 500 });
|
|
194
197
|
}
|
|
195
|
-
|
|
198
|
+
fetch = async (request, connection) => {
|
|
196
199
|
const nsid = extractMethodNsid(request);
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
+
const fetch = nsid
|
|
201
|
+
? this.handlers.get(nsid)
|
|
202
|
+
: undefined;
|
|
203
|
+
if (fetch)
|
|
204
|
+
return fetch(request, connection);
|
|
200
205
|
if (!nsid || !(0, lex_schema_1.isNsidString)(nsid)) {
|
|
201
206
|
return Response.json({
|
|
202
207
|
error: 'InvalidRequest',
|
|
@@ -237,9 +242,8 @@ async function getProcedureInput(request) {
|
|
|
237
242
|
}
|
|
238
243
|
if (this.input.encoding === 'application/json') {
|
|
239
244
|
// @TODO limit size?
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
: (0, lex_json_1.lexParse)(await request.text());
|
|
245
|
+
const data = (0, lex_json_1.lexParse)(await request.text());
|
|
246
|
+
const body = this.input.schema ? this.input.schema.parse(data) : data;
|
|
243
247
|
return { encoding, body };
|
|
244
248
|
}
|
|
245
249
|
else if (this.input.encoding) {
|
package/dist/lex-server.js.map
CHANGED
|
@@ -1 +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 Awaitable<T> = T | Promise<T>\nexport type 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 | undefined\n\nexport type ConnectionInfo<A extends Addr = Addr> = {\n remoteAddr: A\n completed: Promise<void>\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 signal: AbortSignal\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) => Awaitable<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<Credentials, Method>\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<Credentials, Method>\n}\n\nexport type LexRouterAuthContext<Method extends LexMethod = LexMethod> = {\n method: Method\n params: InferMethodParams<Method>\n request: Request\n connection?: ConnectionInfo\n}\n\nexport type LexRouterAuth<\n Credentials = unknown,\n Method extends LexMethod = LexMethod,\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, Credentials = unknown>(\n ns: Main<M>,\n config: M extends Subscription\n ?\n | LexRouterSubscriptionHandler<M, Credentials>\n | LexRouterSubscriptionConfig<M, Credentials>\n : M extends Query | Procedure\n ?\n | LexRouterMethodHandler<M, Credentials>\n | LexRouterMethodConfig<M, Credentials>\n : never,\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<Credentials, Method>,\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 (request, connection) => {\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({ method, 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 signal: request.signal,\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<Credentials, Method>,\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 (request, connection) => {\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 if (request.signal.aborted) {\n return Response.json(\n { error: 'RequestAborted', message: 'The request was aborted' },\n { status: 499 },\n )\n }\n\n try {\n const { response, socket } = upgradeWebSocket(request)\n\n // @NOTE We are using a distinct signal than request.signal because that\n // signal may get aborted before the WebSocket is closed (this is the\n // case with Deno).\n const abortController = new AbortController()\n const { signal } = abortController\n const abort = () => abortController.abort()\n\n const onMessage = (event: unknown) => {\n const error = new LexError(\n 'InvalidRequest',\n 'XRPC subscriptions do not accept messages',\n { cause: event },\n )\n socket.send(encodeErrorFrame(error))\n socket.close(1008, error.error)\n }\n\n const onOpen = 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({ method, params, request, connection })\n : (undefined as Credentials)\n\n signal.throwIfAborted()\n\n const iterable = methodHandler({\n credentials,\n params,\n input: undefined as InferMethodInput<Method, Body>,\n request,\n connection,\n signal,\n })\n\n const iterator = iterable[Symbol.asyncIterator]()\n\n signal.addEventListener('abort', async () => {\n // @NOTE will cause the process to crash if this throws\n await iterator.return?.()\n })\n\n while (!signal.aborted && socket.readyState === 1) {\n const result = await iterator.next()\n if (result.done) break\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, signal, this.options)\n }\n\n if (socket.readyState === 1) {\n socket.close(1000)\n }\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 } finally {\n abortController.abort()\n }\n }\n\n socket.addEventListener('error', abort)\n socket.addEventListener('close', abort)\n socket.addEventListener('open', onOpen)\n socket.addEventListener('message', onMessage)\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"]}
|
|
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 Awaitable<T> = T | Promise<T>\nexport type 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 | undefined\n\nexport type ConnectionInfo<A extends Addr = Addr> = {\n remoteAddr: A\n completed: Promise<void>\n}\n\nexport type FetchHandler = (\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 signal: AbortSignal\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) => Awaitable<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<Credentials, Method>\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<Credentials, Method>\n}\n\nexport type LexRouterAuthContext<Method extends LexMethod = LexMethod> = {\n method: Method\n params: InferMethodParams<Method>\n request: Request\n connection?: ConnectionInfo\n}\n\nexport type LexRouterAuth<\n Credentials = unknown,\n Method extends LexMethod = LexMethod,\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, FetchHandler> = 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, Credentials = unknown>(\n ns: Main<M>,\n config: M extends Subscription\n ?\n | LexRouterSubscriptionHandler<M, Credentials>\n | LexRouterSubscriptionConfig<M, Credentials>\n : M extends Query | Procedure\n ?\n | LexRouterMethodHandler<M, Credentials>\n | LexRouterMethodConfig<M, Credentials>\n : never,\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 fetch: FetchHandler =\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, fetch)\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<Credentials, Method>,\n ): FetchHandler {\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 (request, connection) => {\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({ method, 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 signal: request.signal,\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 as BodyInit | null | undefined, {\n status: 200,\n headers,\n })\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<Credentials, Method>,\n ): FetchHandler {\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 (request, connection) => {\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 if (request.signal.aborted) {\n return Response.json(\n { error: 'RequestAborted', message: 'The request was aborted' },\n { status: 499 },\n )\n }\n\n try {\n const { response, socket } = upgradeWebSocket(request)\n\n // @NOTE We are using a distinct signal than request.signal because that\n // signal may get aborted before the WebSocket is closed (this is the\n // case with Deno).\n const abortController = new AbortController()\n const { signal } = abortController\n const abort = () => abortController.abort()\n\n const onMessage = (event: unknown) => {\n const error = new LexError(\n 'InvalidRequest',\n 'XRPC subscriptions do not accept messages',\n { cause: event },\n )\n socket.send(encodeErrorFrame(error))\n socket.close(1008, error.error)\n }\n\n const onOpen = 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({ method, params, request, connection })\n : (undefined as Credentials)\n\n signal.throwIfAborted()\n\n const iterable = methodHandler({\n credentials,\n params,\n input: undefined as InferMethodInput<Method, Body>,\n request,\n connection,\n signal,\n })\n\n const iterator = iterable[Symbol.asyncIterator]()\n\n signal.addEventListener('abort', async () => {\n // @NOTE will cause the process to crash if this throws\n await iterator.return?.()\n })\n\n while (!signal.aborted && socket.readyState === 1) {\n const result = await iterator.next()\n if (result.done) break\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, signal, this.options)\n }\n\n if (socket.readyState === 1) {\n socket.close(1000)\n }\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 } finally {\n abortController.abort()\n }\n }\n\n socket.addEventListener('error', abort)\n socket.addEventListener('close', abort)\n socket.addEventListener('open', onOpen)\n socket.addEventListener('message', onMessage)\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 fetch: FetchHandler = async (\n request: Request,\n connection?: ConnectionInfo,\n ): Promise<Response> => {\n const nsid = extractMethodNsid(request)\n\n const fetch = nsid\n ? (this.handlers as Map<unknown, FetchHandler>).get(nsid)\n : undefined\n if (fetch) return fetch(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 data = lexParse(await request.text())\n const body = this.input.schema ? this.input.schema.parse(data) : data\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"]}
|
package/dist/nodejs.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IncomingMessage, Server as HttpServer, ServerOptions, ServerResponse } from 'node:http';
|
|
2
2
|
import { ListenOptions } from 'node:net';
|
|
3
|
+
import { FetchHandler } from './lex-server.js';
|
|
3
4
|
export declare function upgradeWebSocket(request: Request): {
|
|
4
5
|
response: Response;
|
|
5
6
|
socket: WebSocket;
|
|
@@ -13,13 +14,10 @@ export type NodeConnectionInfo = {
|
|
|
13
14
|
completed: Promise<void>;
|
|
14
15
|
remoteAddr: NetAddr | undefined;
|
|
15
16
|
};
|
|
16
|
-
export interface HandlerFunction {
|
|
17
|
-
(req: Request, info: NodeConnectionInfo): Response | Promise<Response>;
|
|
18
|
-
}
|
|
19
17
|
export interface HandlerObject {
|
|
20
|
-
|
|
18
|
+
fetch: FetchHandler;
|
|
21
19
|
}
|
|
22
|
-
export declare function toRequestListener<Request extends typeof IncomingMessage = typeof IncomingMessage, Response extends typeof ServerResponse<InstanceType<Request>> = typeof ServerResponse>(
|
|
20
|
+
export declare function toRequestListener<Request extends typeof IncomingMessage = typeof IncomingMessage, Response extends typeof ServerResponse<InstanceType<Request>> = typeof ServerResponse>(fetchHandler: FetchHandler): (req: InstanceType<Request>, res: InstanceType<Response> & {
|
|
23
21
|
req: InstanceType<Request>;
|
|
24
22
|
}, next?: (err?: unknown) => void) => void;
|
|
25
23
|
export type CreateServerOptions<Request extends typeof IncomingMessage = typeof IncomingMessage, Response extends typeof ServerResponse<InstanceType<Request>> = typeof ServerResponse> = ServerOptions<Request, Response> & {
|
|
@@ -29,7 +27,7 @@ export interface Server<Request extends typeof IncomingMessage = typeof Incoming
|
|
|
29
27
|
terminate(): Promise<void>;
|
|
30
28
|
[Symbol.asyncDispose](): Promise<void>;
|
|
31
29
|
}
|
|
32
|
-
export declare function createServer<Request extends typeof IncomingMessage = typeof IncomingMessage, Response extends typeof ServerResponse<InstanceType<Request>> = typeof ServerResponse>(handler:
|
|
30
|
+
export declare function createServer<Request extends typeof IncomingMessage = typeof IncomingMessage, Response extends typeof ServerResponse<InstanceType<Request>> = typeof ServerResponse>(handler: FetchHandler | HandlerObject, options?: CreateServerOptions<Request, Response>): Server<Request, Response>;
|
|
33
31
|
export type StartServerOptions<Request extends typeof IncomingMessage = typeof IncomingMessage, Response extends typeof ServerResponse<InstanceType<Request>> = typeof ServerResponse> = ListenOptions & CreateServerOptions<Request, Response>;
|
|
34
|
-
export declare function serve<Request extends typeof IncomingMessage = typeof IncomingMessage, Response extends typeof ServerResponse<InstanceType<Request>> = typeof ServerResponse>(handler:
|
|
32
|
+
export declare function serve<Request extends typeof IncomingMessage = typeof IncomingMessage, Response extends typeof ServerResponse<InstanceType<Request>> = typeof ServerResponse>(handler: FetchHandler | HandlerObject, options?: StartServerOptions<Request, Response>): Promise<Server<Request, Response>>;
|
|
35
33
|
//# sourceMappingURL=nodejs.d.ts.map
|
package/dist/nodejs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodejs.d.ts","sourceRoot":"","sources":["../src/nodejs.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,eAAe,EAEf,MAAM,IAAI,UAAU,EACpB,aAAa,EACb,cAAc,EAEf,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"nodejs.d.ts","sourceRoot":"","sources":["../src/nodejs.ts"],"names":[],"mappings":"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"}
|
package/dist/nodejs.js
CHANGED
|
@@ -191,10 +191,10 @@ function toBody(req) {
|
|
|
191
191
|
}
|
|
192
192
|
return node_stream_1.Readable.toWeb(req);
|
|
193
193
|
}
|
|
194
|
-
async function handleRequest(req, res,
|
|
194
|
+
async function handleRequest(req, res, fetchHandler) {
|
|
195
195
|
const request = toRequest(req);
|
|
196
196
|
const info = toConnectionInfo(req);
|
|
197
|
-
const response = await
|
|
197
|
+
const response = await fetchHandler(request, info);
|
|
198
198
|
await sendResponse(req, res, response);
|
|
199
199
|
}
|
|
200
200
|
function toConnectionInfo(req) {
|
|
@@ -210,9 +210,9 @@ function toConnectionInfo(req) {
|
|
|
210
210
|
: undefined,
|
|
211
211
|
};
|
|
212
212
|
}
|
|
213
|
-
function toRequestListener(
|
|
213
|
+
function toRequestListener(fetchHandler) {
|
|
214
214
|
return ((req, res, next) => {
|
|
215
|
-
handleRequest(req, res,
|
|
215
|
+
handleRequest(req, res, fetchHandler).catch((err) => {
|
|
216
216
|
if (next)
|
|
217
217
|
next(err);
|
|
218
218
|
else {
|
|
@@ -229,8 +229,8 @@ function toRequestListener(handlerFn) {
|
|
|
229
229
|
});
|
|
230
230
|
}
|
|
231
231
|
function createServer(handler, options = {}) {
|
|
232
|
-
const
|
|
233
|
-
const listener = toRequestListener(
|
|
232
|
+
const fetchHandler = typeof handler === 'function' ? handler : handler.fetch.bind(handler);
|
|
233
|
+
const listener = toRequestListener(fetchHandler);
|
|
234
234
|
const server = (0, node_http_1.createServer)(options, listener);
|
|
235
235
|
const terminator = (0, http_terminator_1.createHttpTerminator)({
|
|
236
236
|
server: server,
|
package/dist/nodejs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodejs.js","sourceRoot":"","sources":["../src/nodejs.ts"],"names":[],"mappings":"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","sourcesContent":["import { once } from 'node:events'\nimport {\n IncomingHttpHeaders,\n IncomingMessage,\n RequestListener,\n Server as HttpServer,\n ServerOptions,\n ServerResponse,\n createServer as createHttpServer,\n} from 'node:http'\nimport { ListenOptions } from 'node:net'\nimport { Readable } from 'node:stream'\nimport { pipeline } from 'node:stream/promises'\nimport { createHttpTerminator } from 'http-terminator'\nimport { WebSocket as WebSocketPonyfill, WebSocketServer } from 'ws'\n\n// @ts-expect-error\nSymbol.asyncDispose ??= Symbol.for('Symbol.asyncDispose')\n\nconst kResponseWs = Symbol.for('@atproto/lex-server:WebSocket')\n\nfunction isUpgradeRequest(request: Request, upgrade: string): boolean {\n return (\n request.method === 'GET' &&\n request.headers.get('connection')?.toLowerCase() === 'upgrade' &&\n request.headers.get('upgrade')?.toLowerCase() === upgrade\n )\n}\n\nexport function upgradeWebSocket(request: Request): {\n response: Response\n socket: WebSocket\n} {\n if (!isUpgradeRequest(request, 'websocket')) {\n throw new TypeError('upgradeWebSocket() expects a WebSocket upgrade')\n }\n\n // Placeholder response for WebSocket upgrade. The actual handling will happen\n // through the handleWebSocketUpgrade function. Headers set on the response\n // will be applied during the upgrade.\n const response = new Response(null, { status: 200 })\n\n // The Response constructor does not allow setting status 101, so we\n // define it directly. The purpose of this response is just to signal\n // that an upgrade is needed, and to carry any headers.\n Object.defineProperty(response, 'status', {\n value: 101,\n enumerable: false,\n configurable: false,\n writable: false,\n })\n\n // @ts-expect-error\n const socket: WebSocket = new WebSocketPonyfill(null, undefined, {\n autoPong: true,\n })\n\n // Attach the WebSocket to the response for later retrieval\n Object.defineProperty(response, kResponseWs, {\n value: socket,\n enumerable: false,\n configurable: false,\n writable: false,\n })\n\n return { response, socket }\n}\n\nconst kUpgradeEvent = Symbol.for('@atproto/lex-server:upgrade')\n\nfunction handleWebSocketUpgrade(\n req: IncomingMessage,\n response: Response,\n): void {\n const ws = (response as { [kResponseWs]?: WebSocketPonyfill })[kResponseWs]\n if (!ws) throw new TypeError('Response not created by upgradeWebSocket()')\n\n // Create a one time use WebSocketServer to handle the upgrade\n const wss = new WebSocketServer({\n autoPong: true,\n noServer: true,\n clientTracking: false,\n perMessageDeflate: true,\n // @ts-expect-error\n WebSocket: function () {\n // Return the websocket that was created earlier instead of a new instance\n return ws\n },\n })\n\n // Apply headers that might have been set on the response object during\n // handling. This will be called during wss.handleUpgrade().\n wss.on('headers', (headers) => {\n for (const [name, value] of response.headers) {\n headers.push(`${name}: ${value}`)\n }\n })\n\n wss.handleUpgrade(req, req.socket, Buffer.alloc(0), (_socket) => {\n // @TODO find a way to properly \"close\" the _socket when the server is\n // shutting down (might require replacing http-terminator with a local\n // implementation)\n\n req.emit(kUpgradeEvent, ws)\n })\n}\n\nasync function sendResponse(\n req: IncomingMessage,\n res: ServerResponse,\n response: Response,\n): Promise<void> {\n // Invalid usage\n if (res.headersSent) {\n throw new TypeError('Response has already been sent')\n }\n\n if (response.status === 101) {\n return handleWebSocketUpgrade(req, response)\n }\n\n res.statusCode = response.status\n res.statusMessage = response.statusText\n\n for (const [key, value] of response.headers) {\n res.appendHeader(key, value)\n }\n\n if (response.body != null && req.method !== 'HEAD') {\n const stream = Readable.fromWeb(response.body as any)\n await pipeline(stream, res)\n } else {\n await response.body?.cancel()\n res.end()\n }\n}\n\nfunction toRequest(req: IncomingMessage): Request {\n const host = req.headers.host ?? req.socket?.localAddress ?? 'localhost'\n const isEncrypted = (req.socket as any).encrypted === true\n const protocol = isEncrypted ? 'https' : 'http'\n const url = new URL(req.url ?? '/', `${protocol}://${host}`)\n const headers = toHeaders(req.headers)\n const body = toBody(req)\n const signal = requestSignal(req)\n\n return new Request(url, {\n signal,\n method: req.method,\n headers,\n body,\n referrer: headers.get('referrer') ?? headers.get('referer') ?? undefined,\n redirect: 'manual',\n // @ts-expect-error\n duplex: body ? 'half' : undefined,\n })\n}\n\nfunction requestSignal(req: IncomingMessage): AbortSignal {\n if (req.destroyed) return AbortSignal.abort()\n\n const abortController = new AbortController()\n\n const abort = (err?: Error | WebSocket) => {\n abortController.abort(err instanceof Error ? err : undefined)\n\n req.off('close', abort)\n req.off('error', abort)\n req.off('end', abort)\n req.off(kUpgradeEvent, abort)\n }\n\n req.on('close', abort)\n req.on('error', abort)\n req.on('end', abort)\n req.on(kUpgradeEvent, abort)\n\n return abortController.signal\n}\n\nfunction requestCompletion(req: IncomingMessage): Promise<void> {\n if (req.destroyed) return Promise.resolve()\n\n // Unlike the abort signal, we complete the promise only when the request\n // is fully done, accounting for websocket upgrade.\n return new Promise((resolve) => {\n const cleanup = () => {\n req.off('close', done)\n req.off('error', done)\n req.off('end', done)\n req.off(kUpgradeEvent, onUpgrade)\n }\n\n const onUpgrade = (ws: WebSocket) => {\n cleanup()\n ws.addEventListener('close', () => resolve())\n }\n\n const done = () => {\n resolve()\n cleanup()\n }\n\n req.on('close', done)\n req.on('error', done)\n req.on('end', done)\n req.on(kUpgradeEvent, onUpgrade)\n })\n}\n\nfunction toHeaders(headers: IncomingHttpHeaders): Headers {\n const result = new Headers()\n for (const [key, value] of Object.entries(headers)) {\n if (value === undefined) continue\n if (Array.isArray(value)) {\n for (const v of value) result.append(key, v)\n } else {\n result.set(key, value)\n }\n }\n return result\n}\n\nfunction toBody(req: IncomingMessage): null | ReadableStream<Uint8Array> {\n if (\n req.method === 'GET' ||\n req.method === 'HEAD' ||\n req.method === 'OPTIONS'\n ) {\n return null\n }\n\n if (\n req.headers['content-type'] == null &&\n req.headers['transfer-encoding'] == null &&\n req.headers['content-length'] == null\n ) {\n return null\n }\n\n return Readable.toWeb(req) as ReadableStream<Uint8Array>\n}\n\nexport type NetAddr = {\n transport: 'tcp'\n hostname: string\n port: number\n}\n\nexport type NodeConnectionInfo = {\n completed: Promise<void>\n remoteAddr: NetAddr | undefined\n}\n\nexport interface HandlerFunction {\n (req: Request, info: NodeConnectionInfo): Response | Promise<Response>\n}\n\nexport interface HandlerObject {\n handle: HandlerFunction\n}\n\nasync function handleRequest(\n req: IncomingMessage,\n res: ServerResponse,\n handlerFn: HandlerFunction,\n) {\n const request = toRequest(req)\n const info = toConnectionInfo(req)\n const response = await handlerFn(request, info)\n await sendResponse(req, res, response)\n}\n\nfunction toConnectionInfo(req: IncomingMessage): NodeConnectionInfo {\n const { socket } = req\n\n return {\n completed: requestCompletion(req),\n remoteAddr:\n socket.remoteAddress != null\n ? {\n transport: 'tcp',\n hostname: socket.remoteAddress,\n port: socket.remotePort!,\n }\n : undefined,\n }\n}\n\nexport function toRequestListener<\n Request extends typeof IncomingMessage = typeof IncomingMessage,\n Response extends typeof ServerResponse<\n InstanceType<Request>\n > = typeof ServerResponse,\n>(handlerFn: HandlerFunction) {\n return ((\n req: InstanceType<Request>,\n res: InstanceType<Response> & { req: InstanceType<Request> },\n next?: (err?: unknown) => void,\n ): void => {\n handleRequest(req, res, handlerFn).catch((err) => {\n if (next) next(err)\n else {\n if (!res.headersSent) {\n res.statusCode = 500\n res.setHeader('content-type', 'text/plain; charset=utf-8')\n res.end('Internal Server Error')\n } else if (!res.writableEnded) {\n res.destroy()\n }\n }\n })\n }) satisfies RequestListener<Request, Response>\n}\n\nexport type CreateServerOptions<\n Request extends typeof IncomingMessage = typeof IncomingMessage,\n Response extends typeof ServerResponse<\n InstanceType<Request>\n > = typeof ServerResponse,\n> = ServerOptions<Request, Response> & {\n gracefulTerminationTimeout?: number\n}\n\nexport interface Server<\n Request extends typeof IncomingMessage = typeof IncomingMessage,\n Response extends typeof ServerResponse<\n InstanceType<Request>\n > = typeof ServerResponse,\n> extends HttpServer<Request, Response>,\n AsyncDisposable {\n terminate(): Promise<void>\n [Symbol.asyncDispose](): Promise<void>\n}\n\nexport function createServer<\n Request extends typeof IncomingMessage = typeof IncomingMessage,\n Response extends typeof ServerResponse<\n InstanceType<Request>\n > = typeof ServerResponse,\n>(\n handler: HandlerFunction | HandlerObject,\n options: CreateServerOptions<Request, Response> = {},\n): Server<Request, Response> {\n const handlerFn =\n typeof handler === 'function' ? handler : handler.handle.bind(handler)\n\n const listener = toRequestListener(handlerFn)\n const server = createHttpServer(options, listener)\n\n const terminator = createHttpTerminator({\n server: server as HttpServer,\n gracefulTerminationTimeout: options?.gracefulTerminationTimeout,\n })\n\n const terminate = async function terminate(this: Server<Request, Response>) {\n if (this !== server) {\n throw new TypeError('Server.terminate called with incorrect context')\n }\n // @TODO properly close all active WebSocket connections\n return terminator.terminate()\n }\n\n Object.defineProperty(server, 'terminate', {\n value: terminate,\n enumerable: false,\n configurable: false,\n writable: false,\n })\n\n Object.defineProperty(server, Symbol.asyncDispose, {\n value: terminate,\n enumerable: false,\n configurable: false,\n writable: false,\n })\n\n return server as Server<Request, Response>\n}\n\nexport type StartServerOptions<\n Request extends typeof IncomingMessage = typeof IncomingMessage,\n Response extends typeof ServerResponse<\n InstanceType<Request>\n > = typeof ServerResponse,\n> = ListenOptions & CreateServerOptions<Request, Response>\n\nexport async function serve<\n Request extends typeof IncomingMessage = typeof IncomingMessage,\n Response extends typeof ServerResponse<\n InstanceType<Request>\n > = typeof ServerResponse,\n>(\n handler: HandlerFunction | HandlerObject,\n options?: StartServerOptions<Request, Response>,\n): Promise<Server<Request, Response>> {\n const server = createServer(handler, options)\n server.listen(options)\n await once(server, 'listening')\n return server\n}\n"]}
|
|
1
|
+
{"version":3,"file":"nodejs.js","sourceRoot":"","sources":["../src/nodejs.ts"],"names":[],"mappings":"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","sourcesContent":["import { once } from 'node:events'\nimport {\n IncomingHttpHeaders,\n IncomingMessage,\n RequestListener,\n Server as HttpServer,\n ServerOptions,\n ServerResponse,\n createServer as createHttpServer,\n} from 'node:http'\nimport { ListenOptions } from 'node:net'\nimport { Readable } from 'node:stream'\nimport { pipeline } from 'node:stream/promises'\nimport { createHttpTerminator } from 'http-terminator'\nimport { WebSocket as WebSocketPonyfill, WebSocketServer } from 'ws'\nimport { FetchHandler } from './lex-server.js'\n\n// @ts-expect-error\nSymbol.asyncDispose ??= Symbol.for('Symbol.asyncDispose')\n\nconst kResponseWs = Symbol.for('@atproto/lex-server:WebSocket')\n\nfunction isUpgradeRequest(request: Request, upgrade: string): boolean {\n return (\n request.method === 'GET' &&\n request.headers.get('connection')?.toLowerCase() === 'upgrade' &&\n request.headers.get('upgrade')?.toLowerCase() === upgrade\n )\n}\n\nexport function upgradeWebSocket(request: Request): {\n response: Response\n socket: WebSocket\n} {\n if (!isUpgradeRequest(request, 'websocket')) {\n throw new TypeError('upgradeWebSocket() expects a WebSocket upgrade')\n }\n\n // Placeholder response for WebSocket upgrade. The actual handling will happen\n // through the handleWebSocketUpgrade function. Headers set on the response\n // will be applied during the upgrade.\n const response = new Response(null, { status: 200 })\n\n // The Response constructor does not allow setting status 101, so we\n // define it directly. The purpose of this response is just to signal\n // that an upgrade is needed, and to carry any headers.\n Object.defineProperty(response, 'status', {\n value: 101,\n enumerable: false,\n configurable: false,\n writable: false,\n })\n\n // @ts-expect-error\n const socket: WebSocket = new WebSocketPonyfill(null, undefined, {\n autoPong: true,\n })\n\n // Attach the WebSocket to the response for later retrieval\n Object.defineProperty(response, kResponseWs, {\n value: socket,\n enumerable: false,\n configurable: false,\n writable: false,\n })\n\n return { response, socket }\n}\n\nconst kUpgradeEvent = Symbol.for('@atproto/lex-server:upgrade')\n\nfunction handleWebSocketUpgrade(\n req: IncomingMessage,\n response: Response,\n): void {\n const ws = (response as { [kResponseWs]?: WebSocketPonyfill })[kResponseWs]\n if (!ws) throw new TypeError('Response not created by upgradeWebSocket()')\n\n // Create a one time use WebSocketServer to handle the upgrade\n const wss = new WebSocketServer({\n autoPong: true,\n noServer: true,\n clientTracking: false,\n perMessageDeflate: true,\n // @ts-expect-error\n WebSocket: function () {\n // Return the websocket that was created earlier instead of a new instance\n return ws\n },\n })\n\n // Apply headers that might have been set on the response object during\n // handling. This will be called during wss.handleUpgrade().\n wss.on('headers', (headers) => {\n for (const [name, value] of response.headers) {\n headers.push(`${name}: ${value}`)\n }\n })\n\n wss.handleUpgrade(req, req.socket, Buffer.alloc(0), (_socket) => {\n // @TODO find a way to properly \"close\" the _socket when the server is\n // shutting down (might require replacing http-terminator with a local\n // implementation)\n\n req.emit(kUpgradeEvent, ws)\n })\n}\n\nasync function sendResponse(\n req: IncomingMessage,\n res: ServerResponse,\n response: Response,\n): Promise<void> {\n // Invalid usage\n if (res.headersSent) {\n throw new TypeError('Response has already been sent')\n }\n\n if (response.status === 101) {\n return handleWebSocketUpgrade(req, response)\n }\n\n res.statusCode = response.status\n res.statusMessage = response.statusText\n\n for (const [key, value] of response.headers) {\n res.appendHeader(key, value)\n }\n\n if (response.body != null && req.method !== 'HEAD') {\n const stream = Readable.fromWeb(response.body as any)\n await pipeline(stream, res)\n } else {\n await response.body?.cancel()\n res.end()\n }\n}\n\nfunction toRequest(req: IncomingMessage): Request {\n const host = req.headers.host ?? req.socket?.localAddress ?? 'localhost'\n const isEncrypted = (req.socket as any).encrypted === true\n const protocol = isEncrypted ? 'https' : 'http'\n const url = new URL(req.url ?? '/', `${protocol}://${host}`)\n const headers = toHeaders(req.headers)\n const body = toBody(req)\n const signal = requestSignal(req)\n\n return new Request(url, {\n signal,\n method: req.method,\n headers,\n body,\n referrer: headers.get('referrer') ?? headers.get('referer') ?? undefined,\n redirect: 'manual',\n // @ts-expect-error\n duplex: body ? 'half' : undefined,\n })\n}\n\nfunction requestSignal(req: IncomingMessage): AbortSignal {\n if (req.destroyed) return AbortSignal.abort()\n\n const abortController = new AbortController()\n\n const abort = (err?: Error | WebSocket) => {\n abortController.abort(err instanceof Error ? err : undefined)\n\n req.off('close', abort)\n req.off('error', abort)\n req.off('end', abort)\n req.off(kUpgradeEvent, abort)\n }\n\n req.on('close', abort)\n req.on('error', abort)\n req.on('end', abort)\n req.on(kUpgradeEvent, abort)\n\n return abortController.signal\n}\n\nfunction requestCompletion(req: IncomingMessage): Promise<void> {\n if (req.destroyed) return Promise.resolve()\n\n // Unlike the abort signal, we complete the promise only when the request\n // is fully done, accounting for websocket upgrade.\n return new Promise((resolve) => {\n const cleanup = () => {\n req.off('close', done)\n req.off('error', done)\n req.off('end', done)\n req.off(kUpgradeEvent, onUpgrade)\n }\n\n const onUpgrade = (ws: WebSocket) => {\n cleanup()\n ws.addEventListener('close', () => resolve())\n }\n\n const done = () => {\n resolve()\n cleanup()\n }\n\n req.on('close', done)\n req.on('error', done)\n req.on('end', done)\n req.on(kUpgradeEvent, onUpgrade)\n })\n}\n\nfunction toHeaders(headers: IncomingHttpHeaders): Headers {\n const result = new Headers()\n for (const [key, value] of Object.entries(headers)) {\n if (value === undefined) continue\n if (Array.isArray(value)) {\n for (const v of value) result.append(key, v)\n } else {\n result.set(key, value)\n }\n }\n return result\n}\n\nfunction toBody(req: IncomingMessage): null | ReadableStream<Uint8Array> {\n if (\n req.method === 'GET' ||\n req.method === 'HEAD' ||\n req.method === 'OPTIONS'\n ) {\n return null\n }\n\n if (\n req.headers['content-type'] == null &&\n req.headers['transfer-encoding'] == null &&\n req.headers['content-length'] == null\n ) {\n return null\n }\n\n return Readable.toWeb(req) as ReadableStream<Uint8Array>\n}\n\nexport type NetAddr = {\n transport: 'tcp'\n hostname: string\n port: number\n}\n\nexport type NodeConnectionInfo = {\n completed: Promise<void>\n remoteAddr: NetAddr | undefined\n}\n\nexport interface HandlerObject {\n fetch: FetchHandler\n}\n\nasync function handleRequest(\n req: IncomingMessage,\n res: ServerResponse,\n fetchHandler: FetchHandler,\n) {\n const request = toRequest(req)\n const info = toConnectionInfo(req)\n const response = await fetchHandler(request, info)\n await sendResponse(req, res, response)\n}\n\nfunction toConnectionInfo(req: IncomingMessage): NodeConnectionInfo {\n const { socket } = req\n\n return {\n completed: requestCompletion(req),\n remoteAddr:\n socket.remoteAddress != null\n ? {\n transport: 'tcp',\n hostname: socket.remoteAddress,\n port: socket.remotePort!,\n }\n : undefined,\n }\n}\n\nexport function toRequestListener<\n Request extends typeof IncomingMessage = typeof IncomingMessage,\n Response extends typeof ServerResponse<\n InstanceType<Request>\n > = typeof ServerResponse,\n>(fetchHandler: FetchHandler) {\n return ((\n req: InstanceType<Request>,\n res: InstanceType<Response> & { req: InstanceType<Request> },\n next?: (err?: unknown) => void,\n ): void => {\n handleRequest(req, res, fetchHandler).catch((err) => {\n if (next) next(err)\n else {\n if (!res.headersSent) {\n res.statusCode = 500\n res.setHeader('content-type', 'text/plain; charset=utf-8')\n res.end('Internal Server Error')\n } else if (!res.writableEnded) {\n res.destroy()\n }\n }\n })\n }) satisfies RequestListener<Request, Response>\n}\n\nexport type CreateServerOptions<\n Request extends typeof IncomingMessage = typeof IncomingMessage,\n Response extends typeof ServerResponse<\n InstanceType<Request>\n > = typeof ServerResponse,\n> = ServerOptions<Request, Response> & {\n gracefulTerminationTimeout?: number\n}\n\nexport interface Server<\n Request extends typeof IncomingMessage = typeof IncomingMessage,\n Response extends typeof ServerResponse<\n InstanceType<Request>\n > = typeof ServerResponse,\n> extends HttpServer<Request, Response>,\n AsyncDisposable {\n terminate(): Promise<void>\n [Symbol.asyncDispose](): Promise<void>\n}\n\nexport function createServer<\n Request extends typeof IncomingMessage = typeof IncomingMessage,\n Response extends typeof ServerResponse<\n InstanceType<Request>\n > = typeof ServerResponse,\n>(\n handler: FetchHandler | HandlerObject,\n options: CreateServerOptions<Request, Response> = {},\n): Server<Request, Response> {\n const fetchHandler =\n typeof handler === 'function' ? handler : handler.fetch.bind(handler)\n\n const listener = toRequestListener(fetchHandler)\n const server = createHttpServer(options, listener)\n\n const terminator = createHttpTerminator({\n server: server as HttpServer,\n gracefulTerminationTimeout: options?.gracefulTerminationTimeout,\n })\n\n const terminate = async function terminate(this: Server<Request, Response>) {\n if (this !== server) {\n throw new TypeError('Server.terminate called with incorrect context')\n }\n // @TODO properly close all active WebSocket connections\n return terminator.terminate()\n }\n\n Object.defineProperty(server, 'terminate', {\n value: terminate,\n enumerable: false,\n configurable: false,\n writable: false,\n })\n\n Object.defineProperty(server, Symbol.asyncDispose, {\n value: terminate,\n enumerable: false,\n configurable: false,\n writable: false,\n })\n\n return server as Server<Request, Response>\n}\n\nexport type StartServerOptions<\n Request extends typeof IncomingMessage = typeof IncomingMessage,\n Response extends typeof ServerResponse<\n InstanceType<Request>\n > = typeof ServerResponse,\n> = ListenOptions & CreateServerOptions<Request, Response>\n\nexport async function serve<\n Request extends typeof IncomingMessage = typeof IncomingMessage,\n Response extends typeof ServerResponse<\n InstanceType<Request>\n > = typeof ServerResponse,\n>(\n handler: FetchHandler | HandlerObject,\n options?: StartServerOptions<Request, Response>,\n): Promise<Server<Request, Response>> {\n const server = createServer(handler, options)\n server.listen(options)\n await once(server, 'listening')\n return server\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/lex-server",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Request router for Atproto Lexicon protocols and schemas",
|
|
6
6
|
"keywords": [
|
|
@@ -48,17 +48,17 @@
|
|
|
48
48
|
"@atproto-labs/did-resolver": "0.2.5",
|
|
49
49
|
"@atproto/crypto": "0.4.5",
|
|
50
50
|
"@atproto/did": "0.2.4",
|
|
51
|
-
"@atproto/lex-
|
|
52
|
-
"@atproto/lex-
|
|
53
|
-
"@atproto/lex-json": "0.0.
|
|
54
|
-
"@atproto/lex-schema": "0.0.
|
|
51
|
+
"@atproto/lex-cbor": "0.0.9",
|
|
52
|
+
"@atproto/lex-data": "0.0.9",
|
|
53
|
+
"@atproto/lex-json": "0.0.9",
|
|
54
|
+
"@atproto/lex-schema": "0.0.10"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@types/ws": "^8.18.1",
|
|
58
58
|
"@vitest/coverage-v8": "4.0.16",
|
|
59
59
|
"vitest": "^4.0.16",
|
|
60
|
-
"@atproto/lex": "0.0.
|
|
61
|
-
"@atproto/lex-client": "0.0.
|
|
60
|
+
"@atproto/lex": "0.0.12",
|
|
61
|
+
"@atproto/lex-client": "0.0.10"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
64
|
"build": "tsc --build tsconfig.build.json",
|
package/src/lex-server.test.ts
CHANGED
|
@@ -36,14 +36,14 @@ const io = {
|
|
|
36
36
|
l.payload(
|
|
37
37
|
'application/json',
|
|
38
38
|
l.object({
|
|
39
|
-
cid: l.
|
|
39
|
+
cid: l.cid(),
|
|
40
40
|
bytes: l.bytes(),
|
|
41
41
|
}),
|
|
42
42
|
),
|
|
43
43
|
l.payload(
|
|
44
44
|
'application/json',
|
|
45
45
|
l.object({
|
|
46
|
-
cid: l.
|
|
46
|
+
cid: l.cid(),
|
|
47
47
|
bytes: l.bytes(),
|
|
48
48
|
}),
|
|
49
49
|
),
|
|
@@ -87,7 +87,7 @@ describe('LexRouter', () => {
|
|
|
87
87
|
it('returns MethodNotImplemented when the route is not found', async () => {
|
|
88
88
|
const router = new LexRouter()
|
|
89
89
|
const request = new Request(`https://example.com/xrpc/foo.bar.baz`)
|
|
90
|
-
const response = await router.
|
|
90
|
+
const response = await router.fetch(request)
|
|
91
91
|
expect(response.status).toBe(501)
|
|
92
92
|
expect(await response.json()).toMatchObject({
|
|
93
93
|
error: 'MethodNotImplemented',
|
|
@@ -115,7 +115,7 @@ describe('LexRouter', () => {
|
|
|
115
115
|
},
|
|
116
116
|
}),
|
|
117
117
|
})
|
|
118
|
-
const response = await router.
|
|
118
|
+
const response = await router.fetch(request)
|
|
119
119
|
|
|
120
120
|
const reader = response.body!.getReader()
|
|
121
121
|
const chunks: string[] = []
|
|
@@ -141,7 +141,7 @@ describe('LexRouter', () => {
|
|
|
141
141
|
const request = new Request(
|
|
142
142
|
'https://example.com/xrpc/io.example.paramsToBody?name=Alice&pronouns=she%2Fher&pronouns=they%2Fthem',
|
|
143
143
|
)
|
|
144
|
-
const response = await router.
|
|
144
|
+
const response = await router.fetch(request)
|
|
145
145
|
|
|
146
146
|
expect(response.status).toBe(200)
|
|
147
147
|
expect(await response.json()).toEqual({
|
|
@@ -162,7 +162,7 @@ describe('lex-client integration', () => {
|
|
|
162
162
|
const agent = buildAgent({
|
|
163
163
|
fetch: async (input, init) => {
|
|
164
164
|
const request = new Request(input, init)
|
|
165
|
-
return router.
|
|
165
|
+
return router.fetch(request)
|
|
166
166
|
},
|
|
167
167
|
service: 'https://example.com',
|
|
168
168
|
})
|
|
@@ -180,7 +180,7 @@ describe('lex-client integration', () => {
|
|
|
180
180
|
const agent = buildAgent({
|
|
181
181
|
fetch: async (input, init) => {
|
|
182
182
|
const request = new Request(input, init)
|
|
183
|
-
return router.
|
|
183
|
+
return router.fetch(request)
|
|
184
184
|
},
|
|
185
185
|
service: 'https://example.com',
|
|
186
186
|
})
|
|
@@ -203,7 +203,7 @@ describe('lex-client integration', () => {
|
|
|
203
203
|
const agent = buildAgent({
|
|
204
204
|
fetch: async (input, init) => {
|
|
205
205
|
const request = new Request(input, init)
|
|
206
|
-
return router.
|
|
206
|
+
return router.fetch(request)
|
|
207
207
|
},
|
|
208
208
|
service: 'https://example.com',
|
|
209
209
|
})
|
|
@@ -228,7 +228,7 @@ describe('IPLD values', () => {
|
|
|
228
228
|
const agent = buildAgent({
|
|
229
229
|
fetch: async (input, init) => {
|
|
230
230
|
const request = new Request(input, init)
|
|
231
|
-
return router.
|
|
231
|
+
return router.fetch(request)
|
|
232
232
|
},
|
|
233
233
|
service: 'https://example.com',
|
|
234
234
|
})
|
|
@@ -335,7 +335,7 @@ describe('Authentication', () => {
|
|
|
335
335
|
body: JSON.stringify({ present: false }),
|
|
336
336
|
},
|
|
337
337
|
)
|
|
338
|
-
const response = await router.
|
|
338
|
+
const response = await router.fetch(request)
|
|
339
339
|
|
|
340
340
|
expect(response.status).toBe(400)
|
|
341
341
|
const data = await response.json()
|
|
@@ -359,7 +359,7 @@ describe('Authentication', () => {
|
|
|
359
359
|
body: JSON.stringify({ present: false }),
|
|
360
360
|
},
|
|
361
361
|
)
|
|
362
|
-
const response = await router.
|
|
362
|
+
const response = await router.fetch(request)
|
|
363
363
|
|
|
364
364
|
expect(response.status).toBe(400)
|
|
365
365
|
const data = await response.json()
|
|
@@ -383,7 +383,7 @@ describe('Authentication', () => {
|
|
|
383
383
|
body: JSON.stringify({ present: true }),
|
|
384
384
|
},
|
|
385
385
|
)
|
|
386
|
-
const response = await router.
|
|
386
|
+
const response = await router.fetch(request)
|
|
387
387
|
|
|
388
388
|
expect(response.status).toBe(200)
|
|
389
389
|
const data = await response.json()
|
|
@@ -405,7 +405,7 @@ describe('Authentication', () => {
|
|
|
405
405
|
body: JSON.stringify({ present: true }),
|
|
406
406
|
},
|
|
407
407
|
)
|
|
408
|
-
const response = await router.
|
|
408
|
+
const response = await router.fetch(request)
|
|
409
409
|
|
|
410
410
|
expect(response.status).toBe(400)
|
|
411
411
|
const data = await response.json()
|
|
@@ -461,7 +461,7 @@ describe('Error Handling', () => {
|
|
|
461
461
|
const request = new Request(
|
|
462
462
|
'https://example.com/xrpc/io.example.error?which=foo',
|
|
463
463
|
)
|
|
464
|
-
const response = await router.
|
|
464
|
+
const response = await router.fetch(request)
|
|
465
465
|
|
|
466
466
|
expect(response.status).toBe(400)
|
|
467
467
|
const data = await response.json()
|
|
@@ -487,7 +487,7 @@ describe('Error Handling', () => {
|
|
|
487
487
|
const request = new Request(
|
|
488
488
|
'https://example.com/xrpc/io.example.error?which=bar',
|
|
489
489
|
)
|
|
490
|
-
const response = await router.
|
|
490
|
+
const response = await router.fetch(request)
|
|
491
491
|
|
|
492
492
|
expect(response.status).toBe(400)
|
|
493
493
|
const data = await response.json()
|
|
@@ -507,7 +507,7 @@ describe('Error Handling', () => {
|
|
|
507
507
|
const request = new Request(
|
|
508
508
|
'https://example.com/xrpc/io.example.throwFalsyValue',
|
|
509
509
|
)
|
|
510
|
-
const response = await router.
|
|
510
|
+
const response = await router.fetch(request)
|
|
511
511
|
|
|
512
512
|
expect(response.status).toBe(500)
|
|
513
513
|
const data = await response.json()
|
|
@@ -526,7 +526,7 @@ describe('Error Handling', () => {
|
|
|
526
526
|
const request = new Request('https://example.com/xrpc/io.example.error', {
|
|
527
527
|
method: 'POST',
|
|
528
528
|
})
|
|
529
|
-
const response = await router.
|
|
529
|
+
const response = await router.fetch(request)
|
|
530
530
|
|
|
531
531
|
expect(response.status).toBe(405)
|
|
532
532
|
const data = await response.json()
|
|
@@ -550,7 +550,7 @@ describe('Error Handling', () => {
|
|
|
550
550
|
'https://example.com/xrpc/io.example.procedure',
|
|
551
551
|
{ method: 'GET' },
|
|
552
552
|
)
|
|
553
|
-
const response = await router.
|
|
553
|
+
const response = await router.fetch(request)
|
|
554
554
|
|
|
555
555
|
expect(response.status).toBe(405)
|
|
556
556
|
const data = await response.json()
|
|
@@ -566,7 +566,7 @@ describe('Error Handling', () => {
|
|
|
566
566
|
const request = new Request(
|
|
567
567
|
'https://example.com/xrpc/io.example.doesNotExist',
|
|
568
568
|
)
|
|
569
|
-
const response = await router.
|
|
569
|
+
const response = await router.fetch(request)
|
|
570
570
|
|
|
571
571
|
expect(response.status).toBe(501)
|
|
572
572
|
expect(await response.json()).toMatchObject({
|
|
@@ -591,7 +591,7 @@ describe('Error Handling', () => {
|
|
|
591
591
|
customRouter.add(io.example.error, handler)
|
|
592
592
|
|
|
593
593
|
const request = new Request('https://example.com/xrpc/io.example.error')
|
|
594
|
-
const response = await customRouter.
|
|
594
|
+
const response = await customRouter.fetch(request)
|
|
595
595
|
|
|
596
596
|
expect(onHandlerError).toHaveBeenCalled()
|
|
597
597
|
expect(response.status).toBe(500)
|
|
@@ -613,7 +613,7 @@ describe('Parameters', () => {
|
|
|
613
613
|
int: l.integer({ minimum: 2, maximum: 10 }),
|
|
614
614
|
bool: l.boolean(),
|
|
615
615
|
arr: l.array(l.integer(), { maxLength: 2 }),
|
|
616
|
-
def: l.optional(l.integer(
|
|
616
|
+
def: l.optional(l.withDefault(l.integer(), 0)),
|
|
617
617
|
}),
|
|
618
618
|
l.payload(
|
|
619
619
|
'application/json',
|
|
@@ -647,7 +647,7 @@ describe('Parameters', () => {
|
|
|
647
647
|
const request = new Request(
|
|
648
648
|
'https://example.com/xrpc/io.example.paramTest?str=valid&int=5&bool=true&arr=1&arr=2&def=5',
|
|
649
649
|
)
|
|
650
|
-
const response = await router.
|
|
650
|
+
const response = await router.fetch(request)
|
|
651
651
|
|
|
652
652
|
expect(response.status).toBe(200)
|
|
653
653
|
const data = await response.json()
|
|
@@ -662,7 +662,7 @@ describe('Parameters', () => {
|
|
|
662
662
|
const request = new Request(
|
|
663
663
|
'https://example.com/xrpc/io.example.paramTest?str=valid&int=5&bool=true&arr=3&arr=4',
|
|
664
664
|
)
|
|
665
|
-
const response = await router.
|
|
665
|
+
const response = await router.fetch(request)
|
|
666
666
|
|
|
667
667
|
expect(response.status).toBe(200)
|
|
668
668
|
const data = await response.json()
|
|
@@ -674,7 +674,7 @@ describe('Parameters', () => {
|
|
|
674
674
|
const request = new Request(
|
|
675
675
|
'https://example.com/xrpc/io.example.paramTest?str=10&int=5&bool=true&arr=3&arr=4',
|
|
676
676
|
)
|
|
677
|
-
const response = await router.
|
|
677
|
+
const response = await router.fetch(request)
|
|
678
678
|
|
|
679
679
|
expect(response.status).toBe(200)
|
|
680
680
|
const data = await response.json()
|
|
@@ -688,7 +688,7 @@ describe('Parameters', () => {
|
|
|
688
688
|
const request = new Request(
|
|
689
689
|
'https://example.com/xrpc/io.example.paramTest?str=n&int=5&bool=true&arr=1',
|
|
690
690
|
)
|
|
691
|
-
const response = await router.
|
|
691
|
+
const response = await router.fetch(request)
|
|
692
692
|
|
|
693
693
|
expect(response.status).toBe(400)
|
|
694
694
|
const data = await response.json()
|
|
@@ -699,7 +699,7 @@ describe('Parameters', () => {
|
|
|
699
699
|
const request = new Request(
|
|
700
700
|
'https://example.com/xrpc/io.example.paramTest?str=loooooooooooooong&int=5&bool=true&arr=1',
|
|
701
701
|
)
|
|
702
|
-
const response = await router.
|
|
702
|
+
const response = await router.fetch(request)
|
|
703
703
|
|
|
704
704
|
expect(response.status).toBe(400)
|
|
705
705
|
const data = await response.json()
|
|
@@ -710,7 +710,7 @@ describe('Parameters', () => {
|
|
|
710
710
|
const request = new Request(
|
|
711
711
|
'https://example.com/xrpc/io.example.paramTest?int=5&bool=true&arr=1',
|
|
712
712
|
)
|
|
713
|
-
const response = await router.
|
|
713
|
+
const response = await router.fetch(request)
|
|
714
714
|
|
|
715
715
|
expect(response.status).toBe(400)
|
|
716
716
|
const data = await response.json()
|
|
@@ -721,7 +721,7 @@ describe('Parameters', () => {
|
|
|
721
721
|
const request = new Request(
|
|
722
722
|
'https://example.com/xrpc/io.example.paramTest?str=valid&bool=true&arr=1',
|
|
723
723
|
)
|
|
724
|
-
const response = await router.
|
|
724
|
+
const response = await router.fetch(request)
|
|
725
725
|
|
|
726
726
|
expect(response.status).toBe(400)
|
|
727
727
|
const data = await response.json()
|
|
@@ -732,7 +732,7 @@ describe('Parameters', () => {
|
|
|
732
732
|
const request = new Request(
|
|
733
733
|
'https://example.com/xrpc/io.example.paramTest?str=valid&int=5&arr=1',
|
|
734
734
|
)
|
|
735
|
-
const response = await router.
|
|
735
|
+
const response = await router.fetch(request)
|
|
736
736
|
|
|
737
737
|
expect(response.status).toBe(400)
|
|
738
738
|
const data = await response.json()
|
|
@@ -743,7 +743,7 @@ describe('Parameters', () => {
|
|
|
743
743
|
const request = new Request(
|
|
744
744
|
'https://example.com/xrpc/io.example.paramTest?str=valid&int=-1&bool=true&arr=1',
|
|
745
745
|
)
|
|
746
|
-
const response = await router.
|
|
746
|
+
const response = await router.fetch(request)
|
|
747
747
|
|
|
748
748
|
expect(response.status).toBe(400)
|
|
749
749
|
const data = await response.json()
|
|
@@ -754,7 +754,7 @@ describe('Parameters', () => {
|
|
|
754
754
|
const request = new Request(
|
|
755
755
|
'https://example.com/xrpc/io.example.paramTest?str=valid&int=11&bool=true&arr=1',
|
|
756
756
|
)
|
|
757
|
-
const response = await router.
|
|
757
|
+
const response = await router.fetch(request)
|
|
758
758
|
|
|
759
759
|
expect(response.status).toBe(400)
|
|
760
760
|
const data = await response.json()
|
|
@@ -765,7 +765,7 @@ describe('Parameters', () => {
|
|
|
765
765
|
const request = new Request(
|
|
766
766
|
'https://example.com/xrpc/io.example.paramTest?str=valid&int=5&bool=true',
|
|
767
767
|
)
|
|
768
|
-
const response = await router.
|
|
768
|
+
const response = await router.fetch(request)
|
|
769
769
|
|
|
770
770
|
expect(response.status).toBe(400)
|
|
771
771
|
const data = await response.json()
|
|
@@ -776,10 +776,10 @@ describe('Parameters', () => {
|
|
|
776
776
|
const request = new Request(
|
|
777
777
|
'https://example.com/xrpc/io.example.paramTest?str=valid&int=5&bool=true&arr=1&arr=2&arr=3',
|
|
778
778
|
)
|
|
779
|
-
const response = await router.
|
|
779
|
+
const response = await router.fetch(request)
|
|
780
|
+
const data = await response.json()
|
|
780
781
|
|
|
781
782
|
expect(response.status).toBe(400)
|
|
782
|
-
const data = await response.json()
|
|
783
783
|
expect(data.message).toContain('arr')
|
|
784
784
|
})
|
|
785
785
|
})
|
|
@@ -859,7 +859,7 @@ describe('Procedures', () => {
|
|
|
859
859
|
'https://example.com/xrpc/io.example.pingOne?message=hello%20world',
|
|
860
860
|
{ method: 'POST' },
|
|
861
861
|
)
|
|
862
|
-
const response = await router.
|
|
862
|
+
const response = await router.fetch(request)
|
|
863
863
|
|
|
864
864
|
expect(response.status).toBe(200)
|
|
865
865
|
expect(response.headers.get('content-type')).toBe('text/plain')
|
|
@@ -872,7 +872,7 @@ describe('Procedures', () => {
|
|
|
872
872
|
headers: { 'content-type': 'text/plain' },
|
|
873
873
|
body: 'hello world',
|
|
874
874
|
})
|
|
875
|
-
const response = await router.
|
|
875
|
+
const response = await router.fetch(request)
|
|
876
876
|
|
|
877
877
|
expect(response.status).toBe(200)
|
|
878
878
|
expect(response.headers.get('content-type')).toBe('text/plain')
|
|
@@ -888,7 +888,7 @@ describe('Procedures', () => {
|
|
|
888
888
|
body: new TextEncoder().encode('hello world'),
|
|
889
889
|
},
|
|
890
890
|
)
|
|
891
|
-
const response = await router.
|
|
891
|
+
const response = await router.fetch(request)
|
|
892
892
|
|
|
893
893
|
expect(response.status).toBe(200)
|
|
894
894
|
expect(response.headers.get('content-type')).toBe(
|
|
@@ -907,7 +907,7 @@ describe('Procedures', () => {
|
|
|
907
907
|
body: JSON.stringify({ message: 'hello world' }),
|
|
908
908
|
},
|
|
909
909
|
)
|
|
910
|
-
const response = await router.
|
|
910
|
+
const response = await router.fetch(request)
|
|
911
911
|
|
|
912
912
|
expect(response.status).toBe(200)
|
|
913
913
|
expect(response.headers.get('content-type')).toBe('application/json')
|
|
@@ -971,7 +971,7 @@ describe('Queries', () => {
|
|
|
971
971
|
const request = new Request(
|
|
972
972
|
'https://example.com/xrpc/io.example.pingOne?message=hello%20world',
|
|
973
973
|
)
|
|
974
|
-
const response = await router.
|
|
974
|
+
const response = await router.fetch(request)
|
|
975
975
|
|
|
976
976
|
expect(response.status).toBe(200)
|
|
977
977
|
expect(response.headers.get('content-type')).toBe('text/plain')
|
|
@@ -982,7 +982,7 @@ describe('Queries', () => {
|
|
|
982
982
|
const request = new Request(
|
|
983
983
|
'https://example.com/xrpc/io.example.pingTwo?message=hello%20world',
|
|
984
984
|
)
|
|
985
|
-
const response = await router.
|
|
985
|
+
const response = await router.fetch(request)
|
|
986
986
|
|
|
987
987
|
expect(response.status).toBe(200)
|
|
988
988
|
expect(response.headers.get('content-type')).toBe(
|
|
@@ -996,7 +996,7 @@ describe('Queries', () => {
|
|
|
996
996
|
const request = new Request(
|
|
997
997
|
'https://example.com/xrpc/io.example.pingThree?message=hello%20world',
|
|
998
998
|
)
|
|
999
|
-
const response = await router.
|
|
999
|
+
const response = await router.fetch(request)
|
|
1000
1000
|
|
|
1001
1001
|
expect(response.status).toBe(200)
|
|
1002
1002
|
expect(response.headers.get('content-type')).toBe('application/json')
|
|
@@ -1015,7 +1015,7 @@ describe('Queries', () => {
|
|
|
1015
1015
|
headers: { 'content-type': 'application/json' },
|
|
1016
1016
|
},
|
|
1017
1017
|
)
|
|
1018
|
-
const response = await router.
|
|
1018
|
+
const response = await router.fetch(request)
|
|
1019
1019
|
|
|
1020
1020
|
expect(response.status).toBe(400)
|
|
1021
1021
|
const data = await response.json()
|
|
@@ -1065,7 +1065,7 @@ describe('Responses', () => {
|
|
|
1065
1065
|
const request = new Request(
|
|
1066
1066
|
'https://example.com/xrpc/io.example.readableStream',
|
|
1067
1067
|
)
|
|
1068
|
-
const response = await router.
|
|
1068
|
+
const response = await router.fetch(request)
|
|
1069
1069
|
|
|
1070
1070
|
expect(response.status).toBe(200)
|
|
1071
1071
|
expect(response.headers.get('content-type')).toBe(
|
|
@@ -1111,7 +1111,7 @@ describe('Responses', () => {
|
|
|
1111
1111
|
const request = new Request(
|
|
1112
1112
|
'https://example.com/xrpc/io.example.readableStream?shouldErr=true',
|
|
1113
1113
|
)
|
|
1114
|
-
const response = await router.
|
|
1114
|
+
const response = await router.fetch(request)
|
|
1115
1115
|
|
|
1116
1116
|
expect(response.status).toBe(200)
|
|
1117
1117
|
|
|
@@ -1147,7 +1147,7 @@ describe('Responses', () => {
|
|
|
1147
1147
|
const request = new Request(
|
|
1148
1148
|
'https://example.com/xrpc/io.example.emptyResponse',
|
|
1149
1149
|
)
|
|
1150
|
-
const response = await router.
|
|
1150
|
+
const response = await router.fetch(request)
|
|
1151
1151
|
|
|
1152
1152
|
expect(response.status).toBe(200)
|
|
1153
1153
|
expect(response.body).toBeNull()
|
|
@@ -1165,7 +1165,7 @@ describe('Responses', () => {
|
|
|
1165
1165
|
const request = new Request(
|
|
1166
1166
|
'https://example.com/xrpc/io.example.emptyResponse',
|
|
1167
1167
|
)
|
|
1168
|
-
const response = await router.
|
|
1168
|
+
const response = await router.fetch(request)
|
|
1169
1169
|
|
|
1170
1170
|
expect(response.status).toBe(200)
|
|
1171
1171
|
expect(response.headers.get('x-custom-header')).toBe('value')
|
|
@@ -1201,7 +1201,7 @@ describe('Responses', () => {
|
|
|
1201
1201
|
const request = new Request(
|
|
1202
1202
|
'https://example.com/xrpc/io.example.customResponse?status=201',
|
|
1203
1203
|
)
|
|
1204
|
-
const response = await router.
|
|
1204
|
+
const response = await router.fetch(request)
|
|
1205
1205
|
|
|
1206
1206
|
expect(response.status).toBe(201)
|
|
1207
1207
|
const data = await response.json()
|
|
@@ -1256,7 +1256,7 @@ describe('Body Handling', () => {
|
|
|
1256
1256
|
body: JSON.stringify({ foo: 'hello', bar: 123 }),
|
|
1257
1257
|
},
|
|
1258
1258
|
)
|
|
1259
|
-
const response = await router.
|
|
1259
|
+
const response = await router.fetch(request)
|
|
1260
1260
|
|
|
1261
1261
|
expect(response.status).toBe(200)
|
|
1262
1262
|
const data = await response.json()
|
|
@@ -1273,7 +1273,7 @@ describe('Body Handling', () => {
|
|
|
1273
1273
|
body: JSON.stringify({}),
|
|
1274
1274
|
},
|
|
1275
1275
|
)
|
|
1276
|
-
const response = await router.
|
|
1276
|
+
const response = await router.fetch(request)
|
|
1277
1277
|
|
|
1278
1278
|
expect(response.status).toBe(400)
|
|
1279
1279
|
const data = await response.json()
|
|
@@ -1289,7 +1289,7 @@ describe('Body Handling', () => {
|
|
|
1289
1289
|
body: JSON.stringify({ foo: 123 }),
|
|
1290
1290
|
},
|
|
1291
1291
|
)
|
|
1292
|
-
const response = await router.
|
|
1292
|
+
const response = await router.fetch(request)
|
|
1293
1293
|
|
|
1294
1294
|
expect(response.status).toBe(400)
|
|
1295
1295
|
const data = await response.json()
|
|
@@ -1305,7 +1305,7 @@ describe('Body Handling', () => {
|
|
|
1305
1305
|
body: new Uint8Array([1, 2, 3]),
|
|
1306
1306
|
},
|
|
1307
1307
|
)
|
|
1308
|
-
const response = await router.
|
|
1308
|
+
const response = await router.fetch(request)
|
|
1309
1309
|
|
|
1310
1310
|
expect(response.status).toBe(400)
|
|
1311
1311
|
const data = await response.json()
|
|
@@ -1346,7 +1346,7 @@ describe('Body Handling', () => {
|
|
|
1346
1346
|
body: bytes,
|
|
1347
1347
|
},
|
|
1348
1348
|
)
|
|
1349
|
-
const response = await router.
|
|
1349
|
+
const response = await router.fetch(request)
|
|
1350
1350
|
|
|
1351
1351
|
expect(response.status).toBe(200)
|
|
1352
1352
|
const responseBytes = new Uint8Array(await response.arrayBuffer())
|
|
@@ -1366,7 +1366,7 @@ describe('Body Handling', () => {
|
|
|
1366
1366
|
body: bytes,
|
|
1367
1367
|
},
|
|
1368
1368
|
)
|
|
1369
|
-
const response = await router.
|
|
1369
|
+
const response = await router.fetch(request)
|
|
1370
1370
|
|
|
1371
1371
|
expect(response.status).toBe(200)
|
|
1372
1372
|
const responseBytes = new Uint8Array(await response.arrayBuffer())
|
|
@@ -1392,7 +1392,7 @@ describe('Body Handling', () => {
|
|
|
1392
1392
|
body: stream,
|
|
1393
1393
|
},
|
|
1394
1394
|
)
|
|
1395
|
-
const response = await router.
|
|
1395
|
+
const response = await router.fetch(request)
|
|
1396
1396
|
|
|
1397
1397
|
expect(response.status).toBe(200)
|
|
1398
1398
|
const responseBytes = new Uint8Array(await response.arrayBuffer())
|
|
@@ -1409,7 +1409,7 @@ describe('Body Handling', () => {
|
|
|
1409
1409
|
body: bytes,
|
|
1410
1410
|
},
|
|
1411
1411
|
)
|
|
1412
|
-
const response = await router.
|
|
1412
|
+
const response = await router.fetch(request)
|
|
1413
1413
|
|
|
1414
1414
|
expect(response.status).toBe(200)
|
|
1415
1415
|
})
|
|
@@ -1444,7 +1444,7 @@ describe('Body Handling', () => {
|
|
|
1444
1444
|
},
|
|
1445
1445
|
)
|
|
1446
1446
|
|
|
1447
|
-
const response = await router.
|
|
1447
|
+
const response = await router.fetch(request)
|
|
1448
1448
|
|
|
1449
1449
|
expect(response.status).toBe(400)
|
|
1450
1450
|
const data = await response.json()
|
|
@@ -1481,7 +1481,7 @@ describe('Body Handling', () => {
|
|
|
1481
1481
|
body: new Uint8Array([1, 2, 3]),
|
|
1482
1482
|
},
|
|
1483
1483
|
)
|
|
1484
|
-
const response = await router.
|
|
1484
|
+
const response = await router.fetch(request)
|
|
1485
1485
|
|
|
1486
1486
|
expect(response.status).toBe(200)
|
|
1487
1487
|
const data = await response.json()
|
|
@@ -1497,7 +1497,7 @@ describe('Subscription', () => {
|
|
|
1497
1497
|
subscribe: l.subscription(
|
|
1498
1498
|
'io.example.subscribe',
|
|
1499
1499
|
l.params({
|
|
1500
|
-
message: l.string(
|
|
1500
|
+
message: l.withDefault(l.string(), 'hello'),
|
|
1501
1501
|
}),
|
|
1502
1502
|
l.object({
|
|
1503
1503
|
message: l.string(),
|
package/src/lex-server.ts
CHANGED
|
@@ -39,7 +39,7 @@ export type ConnectionInfo<A extends Addr = Addr> = {
|
|
|
39
39
|
completed: Promise<void>
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
type
|
|
42
|
+
export type FetchHandler = (
|
|
43
43
|
request: Request,
|
|
44
44
|
connection?: ConnectionInfo,
|
|
45
45
|
) => Promise<Response>
|
|
@@ -130,7 +130,7 @@ export type LexRouterOptions = {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
export class LexRouter {
|
|
133
|
-
private handlers: Map<NsidString,
|
|
133
|
+
private handlers: Map<NsidString, FetchHandler> = new Map()
|
|
134
134
|
|
|
135
135
|
constructor(readonly options: LexRouterOptions = {}) {}
|
|
136
136
|
|
|
@@ -179,7 +179,7 @@ export class LexRouter {
|
|
|
179
179
|
? { handler: config, auth: undefined }
|
|
180
180
|
: config
|
|
181
181
|
|
|
182
|
-
const
|
|
182
|
+
const fetch: FetchHandler =
|
|
183
183
|
method.type === 'subscription'
|
|
184
184
|
? this.buildSubscriptionHandler(
|
|
185
185
|
method,
|
|
@@ -192,7 +192,7 @@ export class LexRouter {
|
|
|
192
192
|
methodConfig.auth,
|
|
193
193
|
)
|
|
194
194
|
|
|
195
|
-
this.handlers.set(method.nsid,
|
|
195
|
+
this.handlers.set(method.nsid, fetch)
|
|
196
196
|
|
|
197
197
|
return this
|
|
198
198
|
}
|
|
@@ -201,7 +201,7 @@ export class LexRouter {
|
|
|
201
201
|
method: Method,
|
|
202
202
|
methodHandler: LexRouterMethodHandler<Method, Credentials>,
|
|
203
203
|
auth?: LexRouterAuth<Credentials, Method>,
|
|
204
|
-
):
|
|
204
|
+
): FetchHandler {
|
|
205
205
|
const getInput = (
|
|
206
206
|
method.type === 'procedure'
|
|
207
207
|
? getProcedureInput.bind(method)
|
|
@@ -261,7 +261,10 @@ export class LexRouter {
|
|
|
261
261
|
|
|
262
262
|
const headers = new Headers(output.headers)
|
|
263
263
|
headers.set('content-type', output.encoding!)
|
|
264
|
-
return new Response(output.body
|
|
264
|
+
return new Response(output.body as BodyInit | null | undefined, {
|
|
265
|
+
status: 200,
|
|
266
|
+
headers,
|
|
267
|
+
})
|
|
265
268
|
} catch (error) {
|
|
266
269
|
return this.handleError(request, method, error)
|
|
267
270
|
}
|
|
@@ -272,7 +275,7 @@ export class LexRouter {
|
|
|
272
275
|
method: Method,
|
|
273
276
|
methodHandler: LexRouterSubscriptionHandler<Method, Credentials>,
|
|
274
277
|
auth?: LexRouterAuth<Credentials, Method>,
|
|
275
|
-
):
|
|
278
|
+
): FetchHandler {
|
|
276
279
|
const {
|
|
277
280
|
onHandlerError,
|
|
278
281
|
upgradeWebSocket = (globalThis as any).Deno?.upgradeWebSocket as
|
|
@@ -445,14 +448,16 @@ export class LexRouter {
|
|
|
445
448
|
)
|
|
446
449
|
}
|
|
447
450
|
|
|
448
|
-
|
|
451
|
+
fetch: FetchHandler = async (
|
|
449
452
|
request: Request,
|
|
450
453
|
connection?: ConnectionInfo,
|
|
451
454
|
): Promise<Response> => {
|
|
452
455
|
const nsid = extractMethodNsid(request)
|
|
453
456
|
|
|
454
|
-
const
|
|
455
|
-
|
|
457
|
+
const fetch = nsid
|
|
458
|
+
? (this.handlers as Map<unknown, FetchHandler>).get(nsid)
|
|
459
|
+
: undefined
|
|
460
|
+
if (fetch) return fetch(request, connection)
|
|
456
461
|
|
|
457
462
|
if (!nsid || !isNsidString(nsid)) {
|
|
458
463
|
return Response.json(
|
|
@@ -507,9 +512,8 @@ async function getProcedureInput<M extends Procedure>(
|
|
|
507
512
|
|
|
508
513
|
if (this.input.encoding === 'application/json') {
|
|
509
514
|
// @TODO limit size?
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
: lexParse(await request.text())
|
|
515
|
+
const data = lexParse(await request.text())
|
|
516
|
+
const body = this.input.schema ? this.input.schema.parse(data) : data
|
|
513
517
|
return { encoding, body } as InferMethodInput<M, Body>
|
|
514
518
|
} else if (this.input.encoding) {
|
|
515
519
|
const body: Body = request
|
package/src/nodejs.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { Readable } from 'node:stream'
|
|
|
13
13
|
import { pipeline } from 'node:stream/promises'
|
|
14
14
|
import { createHttpTerminator } from 'http-terminator'
|
|
15
15
|
import { WebSocket as WebSocketPonyfill, WebSocketServer } from 'ws'
|
|
16
|
+
import { FetchHandler } from './lex-server.js'
|
|
16
17
|
|
|
17
18
|
// @ts-expect-error
|
|
18
19
|
Symbol.asyncDispose ??= Symbol.for('Symbol.asyncDispose')
|
|
@@ -252,22 +253,18 @@ export type NodeConnectionInfo = {
|
|
|
252
253
|
remoteAddr: NetAddr | undefined
|
|
253
254
|
}
|
|
254
255
|
|
|
255
|
-
export interface HandlerFunction {
|
|
256
|
-
(req: Request, info: NodeConnectionInfo): Response | Promise<Response>
|
|
257
|
-
}
|
|
258
|
-
|
|
259
256
|
export interface HandlerObject {
|
|
260
|
-
|
|
257
|
+
fetch: FetchHandler
|
|
261
258
|
}
|
|
262
259
|
|
|
263
260
|
async function handleRequest(
|
|
264
261
|
req: IncomingMessage,
|
|
265
262
|
res: ServerResponse,
|
|
266
|
-
|
|
263
|
+
fetchHandler: FetchHandler,
|
|
267
264
|
) {
|
|
268
265
|
const request = toRequest(req)
|
|
269
266
|
const info = toConnectionInfo(req)
|
|
270
|
-
const response = await
|
|
267
|
+
const response = await fetchHandler(request, info)
|
|
271
268
|
await sendResponse(req, res, response)
|
|
272
269
|
}
|
|
273
270
|
|
|
@@ -292,13 +289,13 @@ export function toRequestListener<
|
|
|
292
289
|
Response extends typeof ServerResponse<
|
|
293
290
|
InstanceType<Request>
|
|
294
291
|
> = typeof ServerResponse,
|
|
295
|
-
>(
|
|
292
|
+
>(fetchHandler: FetchHandler) {
|
|
296
293
|
return ((
|
|
297
294
|
req: InstanceType<Request>,
|
|
298
295
|
res: InstanceType<Response> & { req: InstanceType<Request> },
|
|
299
296
|
next?: (err?: unknown) => void,
|
|
300
297
|
): void => {
|
|
301
|
-
handleRequest(req, res,
|
|
298
|
+
handleRequest(req, res, fetchHandler).catch((err) => {
|
|
302
299
|
if (next) next(err)
|
|
303
300
|
else {
|
|
304
301
|
if (!res.headersSent) {
|
|
@@ -339,13 +336,13 @@ export function createServer<
|
|
|
339
336
|
InstanceType<Request>
|
|
340
337
|
> = typeof ServerResponse,
|
|
341
338
|
>(
|
|
342
|
-
handler:
|
|
339
|
+
handler: FetchHandler | HandlerObject,
|
|
343
340
|
options: CreateServerOptions<Request, Response> = {},
|
|
344
341
|
): Server<Request, Response> {
|
|
345
|
-
const
|
|
346
|
-
typeof handler === 'function' ? handler : handler.
|
|
342
|
+
const fetchHandler =
|
|
343
|
+
typeof handler === 'function' ? handler : handler.fetch.bind(handler)
|
|
347
344
|
|
|
348
|
-
const listener = toRequestListener(
|
|
345
|
+
const listener = toRequestListener(fetchHandler)
|
|
349
346
|
const server = createHttpServer(options, listener)
|
|
350
347
|
|
|
351
348
|
const terminator = createHttpTerminator({
|
|
@@ -391,7 +388,7 @@ export async function serve<
|
|
|
391
388
|
InstanceType<Request>
|
|
392
389
|
> = typeof ServerResponse,
|
|
393
390
|
>(
|
|
394
|
-
handler:
|
|
391
|
+
handler: FetchHandler | HandlerObject,
|
|
395
392
|
options?: StartServerOptions<Request, Response>,
|
|
396
393
|
): Promise<Server<Request, Response>> {
|
|
397
394
|
const server = createServer(handler, options)
|