@atproto/lex-server 0.0.1 → 0.0.2
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 +6 -0
- package/dist/lex-server.d.ts +7 -5
- package/dist/lex-server.d.ts.map +1 -1
- package/dist/lex-server.js +33 -25
- package/dist/lex-server.js.map +1 -1
- package/dist/nodejs.d.ts +3 -3
- package/dist/nodejs.d.ts.map +1 -1
- package/dist/nodejs.js +49 -19
- package/dist/nodejs.js.map +1 -1
- package/package.json +2 -2
- package/src/lex-server.test.ts +1 -1
- package/src/lex-server.ts +47 -37
- package/src/nodejs.ts +64 -30
- package/dist/example.d.ts +0 -2
- package/dist/example.d.ts.map +0 -1
- package/dist/example.js +0 -36
- package/dist/example.js.map +0 -1
- package/dist/lex-auth-error.d.ts +0 -15
- package/dist/lex-auth-error.d.ts.map +0 -1
- package/dist/lex-auth-error.js +0 -52
- package/dist/lex-auth-error.js.map +0 -1
- package/dist/subscripotion.d.ts +0 -2
- package/dist/subscripotion.d.ts.map +0 -1
- package/dist/subscripotion.js +0 -36
- package/dist/subscripotion.js.map +0 -1
- package/dist/test.d.mts +0 -2
- package/dist/test.d.mts.map +0 -1
- package/dist/test.mjs +0 -52
- package/dist/test.mjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @atproto/lex-server
|
|
2
2
|
|
|
3
|
+
## 0.0.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#4498](https://github.com/bluesky-social/atproto/pull/4498) [`e9f065f`](https://github.com/bluesky-social/atproto/commit/e9f065fce85cd7940617eb611050e6ef65c54c04) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add AbortSignal to context
|
|
8
|
+
|
|
3
9
|
## 0.0.1
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/dist/lex-server.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { InferMethodInput, InferMethodMessage, InferMethodOutput, InferMethodOutputBody, InferMethodOutputEncoding, InferMethodParams, Main, Procedure, Query, Subscription } from '@atproto/lex-schema';
|
|
2
|
+
type Awaitable<T> = T | Promise<T>;
|
|
2
3
|
type LexMethod = Query | Procedure | Subscription;
|
|
3
4
|
export type NetAddr = {
|
|
4
5
|
hostname: string;
|
|
@@ -9,10 +10,10 @@ export type UnixAddr = {
|
|
|
9
10
|
path: string;
|
|
10
11
|
transport: 'unix' | 'unixpacket';
|
|
11
12
|
};
|
|
12
|
-
export type Addr = NetAddr | UnixAddr;
|
|
13
|
-
export type ConnectionInfo = {
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
export type Addr = NetAddr | UnixAddr | undefined;
|
|
14
|
+
export type ConnectionInfo<A extends Addr = Addr> = {
|
|
15
|
+
remoteAddr: A;
|
|
16
|
+
completed: Promise<void>;
|
|
16
17
|
};
|
|
17
18
|
type Handler = (request: Request, connection?: ConnectionInfo) => Promise<Response>;
|
|
18
19
|
export type LexRouterHandlerContext<Method extends LexMethod, Credentials> = {
|
|
@@ -20,6 +21,7 @@ export type LexRouterHandlerContext<Method extends LexMethod, Credentials> = {
|
|
|
20
21
|
input: InferMethodInput<Method, Body>;
|
|
21
22
|
params: InferMethodParams<Method>;
|
|
22
23
|
request: Request;
|
|
24
|
+
signal: AbortSignal;
|
|
23
25
|
connection?: ConnectionInfo;
|
|
24
26
|
};
|
|
25
27
|
type AsOptionalPayloadOptions<T> = T extends undefined | void ? {
|
|
@@ -32,7 +34,7 @@ export type LexRouterHandlerOutput<Method extends Query | Procedure> = Response
|
|
|
32
34
|
encoding?: 'application/json';
|
|
33
35
|
body: InferMethodOutputBody<Method>;
|
|
34
36
|
} : AsOptionalPayloadOptions<InferMethodOutput<Method, BodyInit>>));
|
|
35
|
-
export type LexRouterMethodHandler<Method extends Query | Procedure = Query | Procedure, Credentials = unknown> = (ctx: LexRouterHandlerContext<Method, Credentials>) =>
|
|
37
|
+
export type LexRouterMethodHandler<Method extends Query | Procedure = Query | Procedure, Credentials = unknown> = (ctx: LexRouterHandlerContext<Method, Credentials>) => Awaitable<LexRouterHandlerOutput<Method>>;
|
|
36
38
|
export type LexRouterMethodConfig<Method extends Query | Procedure = Query | Procedure, Credentials = unknown> = {
|
|
37
39
|
handler: LexRouterMethodHandler<Method, Credentials>;
|
|
38
40
|
auth: LexRouterAuth<Method, Credentials>;
|
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,GAAG,KAAK,GAAG,SAAS,GAAG,YAAY,CAAA;AAEjD,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,CAAA;
|
|
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
|
@@ -52,6 +52,7 @@ class LexRouter {
|
|
|
52
52
|
input,
|
|
53
53
|
request,
|
|
54
54
|
connection,
|
|
55
|
+
signal: request.signal,
|
|
55
56
|
});
|
|
56
57
|
if (output instanceof Response) {
|
|
57
58
|
return output;
|
|
@@ -97,57 +98,57 @@ class LexRouter {
|
|
|
97
98
|
},
|
|
98
99
|
});
|
|
99
100
|
}
|
|
101
|
+
if (request.signal.aborted) {
|
|
102
|
+
return Response.json({ error: 'RequestAborted', message: 'The request was aborted' }, { status: 499 });
|
|
103
|
+
}
|
|
100
104
|
try {
|
|
101
105
|
const { response, socket } = upgradeWebSocket(request);
|
|
102
|
-
|
|
103
|
-
|
|
106
|
+
// @NOTE We are using a distinct signal than request.signal because that
|
|
107
|
+
// signal may get aborted before the WebSocket is closed (this is the
|
|
108
|
+
// case with Deno).
|
|
109
|
+
const abortController = new AbortController();
|
|
110
|
+
const { signal } = abortController;
|
|
111
|
+
const abort = () => abortController.abort();
|
|
112
|
+
const onMessage = (event) => {
|
|
113
|
+
const error = new lex_data_1.LexError('InvalidRequest', 'XRPC subscriptions do not accept messages', { cause: event });
|
|
104
114
|
socket.send(encodeErrorFrame(error));
|
|
105
115
|
socket.close(1008, error.error);
|
|
106
|
-
}
|
|
107
|
-
|
|
116
|
+
};
|
|
117
|
+
const onOpen = async () => {
|
|
108
118
|
try {
|
|
109
119
|
const url = new URL(request.url);
|
|
110
120
|
const params = method.parameters.fromURLSearchParams(url.searchParams);
|
|
111
121
|
const credentials = auth
|
|
112
122
|
? await auth({ params, request, connection })
|
|
113
123
|
: undefined;
|
|
114
|
-
|
|
124
|
+
signal.throwIfAborted();
|
|
115
125
|
const iterable = methodHandler({
|
|
116
126
|
credentials,
|
|
117
127
|
params,
|
|
118
128
|
input: undefined,
|
|
119
129
|
request,
|
|
120
130
|
connection,
|
|
131
|
+
signal,
|
|
121
132
|
});
|
|
122
133
|
const iterator = iterable[Symbol.asyncIterator]();
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
await iterator.return();
|
|
129
|
-
}
|
|
130
|
-
catch {
|
|
131
|
-
// Ignore
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
socket.addEventListener('error', abort);
|
|
135
|
-
socket.addEventListener('close', abort);
|
|
136
|
-
}
|
|
137
|
-
while (socket.readyState === 1) {
|
|
134
|
+
signal.addEventListener('abort', async () => {
|
|
135
|
+
// @NOTE will cause the process to crash if this throws
|
|
136
|
+
await iterator.return?.();
|
|
137
|
+
});
|
|
138
|
+
while (!signal.aborted && socket.readyState === 1) {
|
|
138
139
|
const result = await iterator.next();
|
|
139
140
|
if (result.done)
|
|
140
141
|
break;
|
|
141
|
-
// Should not be needed (socket would emit "close" event)
|
|
142
|
-
request.signal.throwIfAborted();
|
|
143
142
|
// @TODO add validation of output based on method.output.schema?
|
|
144
143
|
const data = encodeMessageFrame(method, result.value);
|
|
145
144
|
socket.send(data);
|
|
146
145
|
// Apply backpressure by waiting for the buffered data to drain
|
|
147
146
|
// before generating the next message
|
|
148
|
-
await (0, drain_websocket_js_1.drainWebsocket)(socket,
|
|
147
|
+
await (0, drain_websocket_js_1.drainWebsocket)(socket, signal, this.options);
|
|
148
|
+
}
|
|
149
|
+
if (socket.readyState === 1) {
|
|
150
|
+
socket.close(1000);
|
|
149
151
|
}
|
|
150
|
-
socket.close(1000);
|
|
151
152
|
}
|
|
152
153
|
catch (error) {
|
|
153
154
|
// If the socket is still open, send an error frame before closing
|
|
@@ -165,7 +166,14 @@ class LexRouter {
|
|
|
165
166
|
await onHandlerError({ error, request, method });
|
|
166
167
|
}
|
|
167
168
|
}
|
|
168
|
-
|
|
169
|
+
finally {
|
|
170
|
+
abortController.abort();
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
socket.addEventListener('error', abort);
|
|
174
|
+
socket.addEventListener('close', abort);
|
|
175
|
+
socket.addEventListener('open', onOpen);
|
|
176
|
+
socket.addEventListener('message', onMessage);
|
|
169
177
|
return response;
|
|
170
178
|
}
|
|
171
179
|
catch (error) {
|
package/dist/lex-server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lex-server.js","sourceRoot":"","sources":["../src/lex-server.ts"],"names":[],"mappings":";;;AAAA,gDAA0C;AAC1C,gDAAgF;AAChF,gDAAuD;AACvD,oDAc4B;AAC5B,iEAAyD;AA8GzD,MAAa,SAAS;IAGC;IAFb,QAAQ,GAA6B,IAAI,GAAG,EAAE,CAAA;IAEtD,YAAqB,UAA4B,EAAE;QAA9B,YAAO,GAAP,OAAO,CAAuB;IAAG,CAAC;IAkBvD,GAAG,CACD,EAAW,EACX,MAImC;QAEnC,MAAM,MAAM,GAAG,IAAA,oBAAO,EAAC,EAAE,CAAC,CAAA;QAC1B,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,SAAS,CAAC,UAAU,MAAM,CAAC,IAAI,qBAAqB,CAAC,CAAA;QACjE,CAAC;QACD,MAAM,YAAY,GAChB,OAAO,MAAM,KAAK,UAAU;YAC1B,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE;YACtC,CAAC,CAAC,MAAM,CAAA;QAEZ,MAAM,OAAO,GACX,MAAM,CAAC,IAAI,KAAK,cAAc;YAC5B,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAC3B,MAAM,EACN,YAAY,CAAC,OAAiD,EAC9D,YAAY,CAAC,IAAI,CAClB;YACH,CAAC,CAAC,IAAI,CAAC,kBAAkB,CACrB,MAAM,EACN,YAAY,CAAC,OAA2C,EACxD,YAAY,CAAC,IAAI,CAClB,CAAA;QAEP,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAEvC,OAAO,IAAI,CAAA;IACb,CAAC;IAEO,kBAAkB,CACxB,MAAc,EACd,aAA0D,EAC1D,IAAyC;QAEzC,MAAM,QAAQ,GAAG,CACf,MAAM,CAAC,IAAI,KAAK,WAAW;YACzB,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC;YAChC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CACkC,CAAA;QAElE,OAAO,KAAK,EACV,OAAgB,EAChB,UAA2B,EACR,EAAE;YACrB,wEAAwE;YACxE,cAAc;YACd,IACE,CAAC,MAAM,CAAC,IAAI,KAAK,WAAW,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC;gBAC1D,CAAC,MAAM,CAAC,IAAI,KAAK,OAAO;oBACtB,OAAO,CAAC,MAAM,KAAK,KAAK;oBACxB,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC,EAC5B,CAAC;gBACD,OAAO,QAAQ,CAAC,IAAI,CAClB,EAAE,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,oBAAoB,EAAE,EAC1D,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAA;YACH,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;gBAChC,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,mBAAmB,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;gBAEtE,MAAM,WAAW,GAAG,IAAI;oBACtB,CAAC,CAAC,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;oBAC7C,CAAC,CAAE,SAAyB,CAAA;gBAE9B,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAA;gBAErC,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC;oBACjC,WAAW;oBACX,MAAM;oBACN,KAAK;oBACL,OAAO;oBACP,UAAU;iBACX,CAAC,CAAA;gBAEF,IAAI,MAAM,YAAY,QAAQ,EAAE,CAAC;oBAC/B,OAAO,MAAM,CAAA;gBACf,CAAC;gBAED,gEAAgE;gBAEhE,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;oBAC/D,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAA;gBACrE,CAAC;gBAED,IAAI,MAAM,CAAC,MAAM,EAAE,QAAQ,KAAK,kBAAkB,EAAE,CAAC;oBACnD,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAA,oBAAS,EAAC,MAAM,CAAC,IAAgB,CAAC,EAAE;wBACvD,MAAM,EAAE,GAAG;wBACX,OAAO,EAAE,MAAM,CAAC,OAAO;qBACxB,CAAC,CAAA;gBACJ,CAAC;gBAED,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;gBAC3C,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,MAAM,CAAC,QAAS,CAAC,CAAA;gBAC7C,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAA;YAC5D,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;YACjD,CAAC;QACH,CAAC,CAAA;IACH,CAAC;IAEO,wBAAwB,CAC9B,MAAc,EACd,aAAgE,EAChE,IAAyC;QAEzC,MAAM,EACJ,cAAc,EACd,gBAAgB,GAAI,UAAkB,CAAC,IAAI,EAAE,gBAEhC,GACd,GAAG,IAAI,CAAC,OAAO,CAAA;QAChB,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,MAAM,IAAI,SAAS,CACjB,6HAA6H,CAC9H,CAAA;QACH,CAAC;QAED,OAAO,KAAK,EACV,OAAgB,EAChB,UAA2B,EACR,EAAE;YACrB,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;gBAC7B,OAAO,QAAQ,CAAC,IAAI,CAClB,EAAE,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,oBAAoB,EAAE,EAC1D,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAA;YACH,CAAC;YAED,IACE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,WAAW,EAAE,KAAK,SAAS;gBAC9D,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,KAAK,WAAW,EAC7D,CAAC;gBACD,OAAO,QAAQ,CAAC,IAAI,CAClB;oBACE,KAAK,EAAE,gBAAgB;oBACvB,OAAO,EAAE,sDAAsD;iBAChE,EACD;oBACE,MAAM,EAAE,GAAG;oBACX,OAAO,EAAE;wBACP,UAAU,EAAE,SAAS;wBACrB,OAAO,EAAE,WAAW;qBACrB;iBACF,CACF,CAAA;YACH,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;gBAEtD,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,GAAG,EAAE;oBACtC,MAAM,KAAK,GAAG,IAAI,mBAAQ,CACxB,gBAAgB,EAChB,2CAA2C,CAC5C,CAAA;oBACD,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAA;oBACpC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;gBACjC,CAAC,CAAC,CAAA;gBAEF,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE;oBACzC,IAAI,CAAC;wBACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;wBAChC,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,mBAAmB,CAClD,GAAG,CAAC,YAAY,CACjB,CAAA;wBAED,MAAM,WAAW,GAAgB,IAAI;4BACnC,CAAC,CAAC,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;4BAC7C,CAAC,CAAE,SAAyB,CAAA;wBAE9B,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,CAAA;wBAE/B,MAAM,QAAQ,GAAG,aAAa,CAAC;4BAC7B,WAAW;4BACX,MAAM;4BACN,KAAK,EAAE,SAA2C;4BAClD,OAAO;4BACP,UAAU;yBACX,CAAC,CAAA;wBAEF,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAA;wBAEjD,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;4BACpB,MAAM,KAAK,GAAG,KAAK,IAAI,EAAE;gCACvB,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;gCAC1C,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;gCAC1C,IAAI,CAAC;oCACH,MAAM,QAAQ,CAAC,MAAO,EAAE,CAAA;gCAC1B,CAAC;gCAAC,MAAM,CAAC;oCACP,SAAS;gCACX,CAAC;4BACH,CAAC,CAAA;4BACD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;4BACvC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;wBACzC,CAAC;wBAED,OAAO,MAAM,CAAC,UAAU,KAAK,CAAC,EAAE,CAAC;4BAC/B,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;4BACpC,IAAI,MAAM,CAAC,IAAI;gCAAE,MAAK;4BAEtB,yDAAyD;4BACzD,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,CAAA;4BAE/B,gEAAgE;4BAEhE,MAAM,IAAI,GAAG,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAA;4BAErD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;4BAEjB,+DAA+D;4BAC/D,qCAAqC;4BACrC,MAAM,IAAA,mCAAc,EAAC,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;wBAC5D,CAAC;wBAED,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;oBACpB,CAAC;oBAAC,OAAO,KAAK,EAAE,CAAC;wBACf,kEAAkE;wBAClE,IAAI,MAAM,CAAC,UAAU,KAAK,CAAC,EAAE,CAAC;4BAC5B,MAAM,QAAQ,GACZ,KAAK,YAAY,mBAAQ;gCACvB,CAAC,CAAC,KAAK;gCACP,CAAC,CAAC,IAAI,mBAAQ,CAAC,eAAe,EAAE,4BAA4B,CAAC,CAAA;4BAEjE,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAA;4BAEvC,MAAM,CAAC,KAAK;4BACV,uDAAuD;4BACvD,KAAK,YAAY,mBAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EACvC,QAAQ,CAAC,KAAK,CACf,CAAA;wBACH,CAAC;wBAED,2CAA2C;wBAC3C,IAAI,cAAc,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;4BAC5D,MAAM,cAAc,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAA;wBAClD,CAAC;oBACH,CAAC;gBACH,CAAC,CAAC,CAAA;gBAEF,OAAO,QAAQ,CAAA;YACjB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;YACjD,CAAC;QACH,CAAC,CAAA;IACH,CAAC;IAEO,KAAK,CAAC,WAAW,CACvB,OAAgB,EAChB,MAAiB,EACjB,KAAc;QAEd,2CAA2C;QAC3C,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC,OAAO,CAAA;QACvC,IAAI,cAAc,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;YAC5D,MAAM,cAAc,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAA;QAClD,CAAC;QAED,IAAI,KAAK,YAAY,mBAAQ,EAAE,CAAC;YAC9B,OAAO,KAAK,CAAC,UAAU,EAAE,CAAA;QAC3B,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,CAClB,EAAE,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,4BAA4B,EAAE,EACjE,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAA;IACH,CAAC;IAED,MAAM,GAAY,KAAK,EACrB,OAAgB,EAChB,UAA2B,EACR,EAAE;QACrB,MAAM,IAAI,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAA;QAEvC,MAAM,OAAO,GAAI,IAAI,CAAC,QAAwC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACxE,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;QAEhD,IAAI,CAAC,IAAI,IAAI,CAAC,IAAA,yBAAY,EAAC,IAAI,CAAC,EAAE,CAAC;YACjC,OAAO,QAAQ,CAAC,IAAI,CAClB;gBACE,KAAK,EAAE,gBAAgB;gBACvB,OAAO,EAAE,0BAA0B;aACpC,EACD,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAA;QACH,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,CAClB;YACE,KAAK,EAAE,sBAAsB;YAC7B,OAAO,EAAE,gBAAgB,IAAI,kCAAkC;SAChE,EACD,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAA;IACH,CAAC,CAAA;CACF;AAnUD,8BAmUC;AAED,SAAS,iBAAiB,CAAC,OAAgB;IACzC,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACzC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAA;IAC/C,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC;QAAE,OAAO,IAAI,CAAA;IAC1C,6EAA6E;IAC7E,uDAAuD;IACvD,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAC1B,CAAC;AAED,KAAK,UAAU,iBAAiB,CAE9B,OAAgB;IAEhB,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO;SAChC,GAAG,CAAC,cAAc,CAAC;QACpB,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;SACd,IAAI,EAAE;SACN,WAAW,EAAE,CAAA;IAEhB,MAAM,QAAQ,GACZ,WAAW;QACX,+DAA+D;QAC/D,kCAAkC;QAClC,CAAC,OAAO,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,IAAI;YAClD,CAAC,CAAC,0BAA0B;YAC5B,CAAC,CAAC,SAAS,CAAC,CAAA;IAEhB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,mBAAQ,CAAC,gBAAgB,EAAE,yBAAyB,QAAQ,EAAE,CAAC,CAAA;IAC3E,CAAC;IAED,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,KAAK,kBAAkB,EAAE,CAAC;QAC/C,oBAAoB;QACpB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM;YAC5B,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,IAAA,mBAAQ,EAAC,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YACzD,CAAC,CAAC,IAAA,mBAAQ,EAAC,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC,CAAA;QAClC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAA+B,CAAA;IACxD,CAAC;SAAM,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAS,OAAO,CAAA;QAC1B,OAAO,EAAE,QAAQ,EAAE,IAAI,EAA+B,CAAA;IACxD,CAAC;SAAM,CAAC;QACN,OAAO,SAAsC,CAAA;IAC/C,CAAC;AACH,CAAC;AAED,KAAK,UAAU,aAAa,CAE1B,OAAgB;IAEhB,IACE,OAAO,CAAC,IAAI;QACZ,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;QACnC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,EACrC,CAAC;QACD,MAAM,IAAI,mBAAQ,CAAC,gBAAgB,EAAE,mCAAmC,CAAC,CAAA;IAC3E,CAAC;IAED,OAAO,SAAsC,CAAA;AAC/C,CAAC;AAED,4CAA4C;AAC5C,MAAM,kBAAkB,GAAG,aAAa,CAAC,IAAA,iBAAM,EAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAA;AAE3D,SAAS,gBAAgB,CAAC,KAAe;IACvC,OAAO,IAAA,oBAAS,EAAC,CAAC,kBAAkB,EAAE,IAAA,iBAAM,EAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAA;AAChE,CAAC;AAED,gEAAgE;AAChE,MAAM,4BAA4B,GAAG,aAAa,CAAC,IAAA,iBAAM,EAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAA;AAEpE,SAAS,kBAAkB,CAAC,MAAoB,EAAE,KAAe;IAC/D,IAAI,IAAA,wBAAa,EAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC5D,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAA;QAChC,OAAO,IAAA,oBAAS,EAAC;YACf,IAAA,iBAAM,EAAC;gBACL,EAAE,EAAE,CAAC;gBACL,CAAC;gBACC,sDAAsD;gBACtD,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,MAAM;oBACtC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,IAAI,MAAM;oBACvD,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC;oBAC3B,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;oBACjC,CAAC,CAAC,KAAK;aACZ,CAAC;YACF,IAAA,iBAAM,EAAC,IAAI,CAAC;SACb,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,IAAA,oBAAS,EAAC,CAAC,4BAA4B,EAAE,IAAA,iBAAM,EAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AACjE,CAAC;AAED,SAAS,aAAa,CAAC,MAAmB,EAAE,KAAc;IACxD,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,IAAI,IAAI;QAAE,OAAO,KAAK,CAAA;IAC1D,OAAO,CACL,KAAK,KAAK,MAAM,CAAC,MAAM;QACvB,CAAC,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,CAC1D,CAAA;AACH,CAAC","sourcesContent":["import { encode } from '@atproto/lex-cbor'\nimport { LexError, LexValue, isPlainObject, ui8Concat } from '@atproto/lex-data'\nimport { lexParse, lexToJson } from '@atproto/lex-json'\nimport {\n InferMethodInput,\n InferMethodMessage,\n InferMethodOutput,\n InferMethodOutputBody,\n InferMethodOutputEncoding,\n InferMethodParams,\n Main,\n NsidString,\n Procedure,\n Query,\n Subscription,\n getMain,\n isNsidString,\n} from '@atproto/lex-schema'\nimport { drainWebsocket } from './lib/drain-websocket.js'\n\ntype LexMethod = Query | Procedure | Subscription\n\nexport type NetAddr = {\n hostname: string\n port: number\n transport: 'tcp' | 'udp'\n}\n\nexport type UnixAddr = {\n path: string\n transport: 'unix' | 'unixpacket'\n}\n\nexport type Addr = NetAddr | UnixAddr\n\nexport type ConnectionInfo = {\n localAddr?: Addr\n remoteAddr?: Addr\n}\n\ntype Handler = (\n request: Request,\n connection?: ConnectionInfo,\n) => Promise<Response>\n\nexport type LexRouterHandlerContext<Method extends LexMethod, Credentials> = {\n credentials: Credentials\n input: InferMethodInput<Method, Body>\n params: InferMethodParams<Method>\n request: Request\n connection?: ConnectionInfo\n}\n\ntype AsOptionalPayloadOptions<T> = T extends undefined | void\n ? { encoding?: undefined; body?: undefined }\n : T\n\nexport type LexRouterHandlerOutput<Method extends Query | Procedure> =\n | Response\n | ({\n headers?: HeadersInit\n } & (InferMethodOutputEncoding<Method> extends 'application/json'\n ? {\n // Allow omitting body when output is JSON\n encoding?: 'application/json'\n body: InferMethodOutputBody<Method>\n }\n : AsOptionalPayloadOptions<InferMethodOutput<Method, BodyInit>>))\n\nexport type LexRouterMethodHandler<\n Method extends Query | Procedure = Query | Procedure,\n Credentials = unknown,\n> = (\n ctx: LexRouterHandlerContext<Method, Credentials>,\n) => Promise<LexRouterHandlerOutput<Method>>\n\nexport type LexRouterMethodConfig<\n Method extends Query | Procedure = Query | Procedure,\n Credentials = unknown,\n> = {\n handler: LexRouterMethodHandler<Method, Credentials>\n auth: LexRouterAuth<Method, Credentials>\n}\n\nexport type LexRouterSubscriptionHandler<\n Method extends Subscription = Subscription,\n Credentials = unknown,\n> = (\n ctx: LexRouterHandlerContext<Method, Credentials>,\n) => AsyncIterable<InferMethodMessage<Method>>\n\nexport type LexRouterSubscriptionConfig<\n Method extends Subscription = Subscription,\n Credentials = unknown,\n> = {\n handler: LexRouterSubscriptionHandler<Method, Credentials>\n auth: LexRouterAuth<Method, Credentials>\n}\n\nexport type LexRouterAuthContext<Method extends LexMethod = LexMethod> = {\n params: InferMethodParams<Method>\n request: Request\n connection?: ConnectionInfo\n}\n\nexport type LexRouterAuth<\n Method extends LexMethod = LexMethod,\n Credentials = unknown,\n> = (ctx: LexRouterAuthContext<Method>) => Credentials | Promise<Credentials>\n\nexport type LexErrorHandlerContext = {\n error: unknown\n request: Request\n method: LexMethod\n}\n\nexport type UpgradeWebSocket = (request: Request) => {\n socket: WebSocket\n response: Response\n}\n\nexport type LexRouterOptions = {\n upgradeWebSocket?: UpgradeWebSocket\n onHandlerError?: (ctx: LexErrorHandlerContext) => void | Promise<void>\n highWaterMark?: number\n lowWaterMark?: number\n}\n\nexport class LexRouter {\n private handlers: Map<NsidString, Handler> = new Map()\n\n constructor(readonly options: LexRouterOptions = {}) {}\n\n add<M extends Subscription>(\n ns: Main<M>,\n handler: LexRouterSubscriptionHandler<M, void>,\n ): this\n add<M extends Subscription, Credentials>(\n ns: Main<M>,\n config: LexRouterSubscriptionConfig<M, Credentials>,\n ): this\n add<M extends Query | Procedure>(\n ns: Main<M>,\n handler: LexRouterMethodHandler<M, void>,\n ): this\n add<M extends Query | Procedure, Credentials>(\n ns: Main<M>,\n config: LexRouterMethodConfig<M, Credentials>,\n ): this\n add<M extends LexMethod>(\n ns: Main<M>,\n config:\n | LexRouterSubscriptionHandler<any, any>\n | LexRouterSubscriptionConfig<any, any>\n | LexRouterMethodHandler<any, any>\n | LexRouterMethodConfig<any, any>,\n ) {\n const method = getMain(ns)\n if (this.handlers.has(method.nsid)) {\n throw new TypeError(`Method ${method.nsid} already registered`)\n }\n const methodConfig =\n typeof config === 'function'\n ? { handler: config, auth: undefined }\n : config\n\n const handler: Handler =\n method.type === 'subscription'\n ? this.buildSubscriptionHandler(\n method,\n methodConfig.handler as LexRouterSubscriptionHandler<any, any>,\n methodConfig.auth,\n )\n : this.buildMethodHandler(\n method,\n methodConfig.handler as LexRouterMethodHandler<any, any>,\n methodConfig.auth,\n )\n\n this.handlers.set(method.nsid, handler)\n\n return this\n }\n\n private buildMethodHandler<Method extends Query | Procedure, Credentials>(\n method: Method,\n methodHandler: LexRouterMethodHandler<Method, Credentials>,\n auth?: LexRouterAuth<Method, Credentials>,\n ): Handler {\n const getInput = (\n method.type === 'procedure'\n ? getProcedureInput.bind(method)\n : getQueryInput.bind(method)\n ) as (request: Request) => Promise<InferMethodInput<Method, Body>>\n\n return async (\n request: Request,\n connection?: ConnectionInfo,\n ): Promise<Response> => {\n // @NOTE CORS requests should be handled by a middleware before reaching\n // this point.\n if (\n (method.type === 'procedure' && request.method !== 'POST') ||\n (method.type === 'query' &&\n request.method !== 'GET' &&\n request.method !== 'HEAD')\n ) {\n return Response.json(\n { error: 'InvalidRequest', message: 'Method not allowed' },\n { status: 405 },\n )\n }\n\n try {\n const url = new URL(request.url)\n const params = method.parameters.fromURLSearchParams(url.searchParams)\n\n const credentials = auth\n ? await auth({ params, request, connection })\n : (undefined as Credentials)\n\n const input = await getInput(request)\n\n const output = await methodHandler({\n credentials,\n params,\n input,\n request,\n connection,\n })\n\n if (output instanceof Response) {\n return output\n }\n\n // @TODO add validation of output based on method.output.schema?\n\n if (output.body === undefined && output.encoding === undefined) {\n return new Response(null, { status: 200, headers: output.headers })\n }\n\n if (method.output?.encoding === 'application/json') {\n return Response.json(lexToJson(output.body as LexValue), {\n status: 200,\n headers: output.headers,\n })\n }\n\n const headers = new Headers(output.headers)\n headers.set('content-type', output.encoding!)\n return new Response(output.body, { status: 200, headers })\n } catch (error) {\n return this.handleError(request, method, error)\n }\n }\n }\n\n private buildSubscriptionHandler<Method extends Subscription, Credentials>(\n method: Method,\n methodHandler: LexRouterSubscriptionHandler<Method, Credentials>,\n auth?: LexRouterAuth<Method, Credentials>,\n ): Handler {\n const {\n onHandlerError,\n upgradeWebSocket = (globalThis as any).Deno?.upgradeWebSocket as\n | UpgradeWebSocket\n | undefined,\n } = this.options\n if (!upgradeWebSocket) {\n throw new TypeError(\n 'WebSocket upgrade not supported in this environment. Please provide an upgradeWebSocket option when creating the LexRouter.',\n )\n }\n\n return async (\n request: Request,\n connection?: ConnectionInfo,\n ): Promise<Response> => {\n if (request.method !== 'GET') {\n return Response.json(\n { error: 'InvalidRequest', message: 'Method not allowed' },\n { status: 405 },\n )\n }\n\n if (\n request.headers.get('connection')?.toLowerCase() !== 'upgrade' ||\n request.headers.get('upgrade')?.toLowerCase() !== 'websocket'\n ) {\n return Response.json(\n {\n error: 'InvalidRequest',\n message: 'XRPC subscriptions are only available over WebSocket',\n },\n {\n status: 426,\n headers: {\n Connection: 'Upgrade',\n Upgrade: 'websocket',\n },\n },\n )\n }\n\n try {\n const { response, socket } = upgradeWebSocket(request)\n\n socket.addEventListener('message', () => {\n const error = new LexError(\n 'InvalidRequest',\n 'XRPC subscriptions do not accept messages',\n )\n socket.send(encodeErrorFrame(error))\n socket.close(1008, error.error)\n })\n\n socket.addEventListener('open', async () => {\n try {\n const url = new URL(request.url)\n const params = method.parameters.fromURLSearchParams(\n url.searchParams,\n )\n\n const credentials: Credentials = auth\n ? await auth({ params, request, connection })\n : (undefined as Credentials)\n\n request.signal.throwIfAborted()\n\n const iterable = methodHandler({\n credentials,\n params,\n input: undefined as InferMethodInput<Method, Body>,\n request,\n connection,\n })\n\n const iterator = iterable[Symbol.asyncIterator]()\n\n if (iterator.return) {\n const abort = async () => {\n socket.removeEventListener('error', abort)\n socket.removeEventListener('close', abort)\n try {\n await iterator.return!()\n } catch {\n // Ignore\n }\n }\n socket.addEventListener('error', abort)\n socket.addEventListener('close', abort)\n }\n\n while (socket.readyState === 1) {\n const result = await iterator.next()\n if (result.done) break\n\n // Should not be needed (socket would emit \"close\" event)\n request.signal.throwIfAborted()\n\n // @TODO add validation of output based on method.output.schema?\n\n const data = encodeMessageFrame(method, result.value)\n\n socket.send(data)\n\n // Apply backpressure by waiting for the buffered data to drain\n // before generating the next message\n await drainWebsocket(socket, request.signal, this.options)\n }\n\n socket.close(1000)\n } catch (error) {\n // If the socket is still open, send an error frame before closing\n if (socket.readyState === 1) {\n const lexError =\n error instanceof LexError\n ? error\n : new LexError('InternalError', 'An internal error occurred')\n\n socket.send(encodeErrorFrame(lexError))\n\n socket.close(\n // https://www.rfc-editor.org/rfc/rfc6455#section-7.4.1\n error instanceof LexError ? 1008 : 1011,\n lexError.error,\n )\n }\n\n // Only report unexpected processing errors\n if (onHandlerError && !isAbortReason(request.signal, error)) {\n await onHandlerError({ error, request, method })\n }\n }\n })\n\n return response\n } catch (error) {\n return this.handleError(request, method, error)\n }\n }\n }\n\n private async handleError(\n request: Request,\n method: LexMethod,\n error: unknown,\n ) {\n // Only report unexpected processing errors\n const { onHandlerError } = this.options\n if (onHandlerError && !isAbortReason(request.signal, error)) {\n await onHandlerError({ error, request, method })\n }\n\n if (error instanceof LexError) {\n return error.toResponse()\n }\n\n return Response.json(\n { error: 'InternalError', message: 'An internal error occurred' },\n { status: 500 },\n )\n }\n\n handle: Handler = async (\n request: Request,\n connection?: ConnectionInfo,\n ): Promise<Response> => {\n const nsid = extractMethodNsid(request)\n\n const handler = (this.handlers as Map<string | null, Handler>).get(nsid)\n if (handler) return handler(request, connection)\n\n if (!nsid || !isNsidString(nsid)) {\n return Response.json(\n {\n error: 'InvalidRequest',\n message: 'Invalid XRPC method path',\n },\n { status: 404 },\n )\n }\n\n return Response.json(\n {\n error: 'MethodNotImplemented',\n message: `XRPC method \"${nsid}\" not implemented on this server`,\n },\n { status: 501 },\n )\n }\n}\n\nfunction extractMethodNsid(request: Request): string | null {\n const { pathname } = new URL(request.url)\n if (!pathname.startsWith('/xrpc/')) return null\n if (pathname.includes('/', 6)) return null\n // We don't really need to validate the NSID here, the existence of the route\n // (which is looked up based on an NSID) is sufficient.\n return pathname.slice(6)\n}\n\nasync function getProcedureInput<M extends Procedure>(\n this: M,\n request: Request,\n): Promise<InferMethodInput<M, Body>> {\n const encodingRaw = request.headers\n .get('content-type')\n ?.split(';')[0]\n .trim()\n .toLowerCase()\n\n const encoding =\n encodingRaw ||\n // If the caller did not provide a content-type, but the method\n // expects an input, assume binary\n (request.body != null && this.input.encoding != null\n ? 'application/octet-stream'\n : undefined)\n\n if (!this.input.matchesEncoding(encoding)) {\n throw new LexError('InvalidRequest', `Invalid content-type: ${encoding}`)\n }\n\n if (this.input.encoding === 'application/json') {\n // @TODO limit size?\n const body = this.input.schema\n ? this.input.schema.parse(lexParse(await request.text()))\n : lexParse(await request.text())\n return { encoding, body } as InferMethodInput<M, Body>\n } else if (this.input.encoding) {\n const body: Body = request\n return { encoding, body } as InferMethodInput<M, Body>\n } else {\n return undefined as InferMethodInput<M, Body>\n }\n}\n\nasync function getQueryInput<M extends Query>(\n this: M,\n request: Request,\n): Promise<InferMethodInput<M, Body>> {\n if (\n request.body ||\n request.headers.has('content-type') ||\n request.headers.has('content-length')\n ) {\n throw new LexError('InvalidRequest', 'GET requests must not have a body')\n }\n\n return undefined as InferMethodInput<M, Body>\n}\n\n// Pre-encoded frame header for error frames\nconst ERROR_FRAME_HEADER = /*#__PURE__*/ encode({ op: -1 })\n\nfunction encodeErrorFrame(error: LexError): Uint8Array {\n return ui8Concat([ERROR_FRAME_HEADER, encode(error.toJSON())])\n}\n\n// Pre-encoded frame header for message frames with unknown type\nconst UNKNOWN_MESSAGE_FRAME_HEADER = /*#__PURE__*/ encode({ op: 1 })\n\nfunction encodeMessageFrame(method: Subscription, value: LexValue): Uint8Array {\n if (isPlainObject(value) && typeof value.$type === 'string') {\n const { $type, ...rest } = value\n return ui8Concat([\n encode({\n op: 1,\n t:\n // If $type starts with `nsid#`, strip the NSID prefix\n $type.charCodeAt(0) !== 0x23 && // '#'\n $type.charCodeAt(method.nsid.length) === 0x23 && // '#'\n $type.startsWith(method.nsid)\n ? $type.slice(method.nsid.length)\n : $type,\n }),\n encode(rest),\n ])\n }\n\n return ui8Concat([UNKNOWN_MESSAGE_FRAME_HEADER, encode(value)])\n}\n\nfunction isAbortReason(signal: AbortSignal, error: unknown): boolean {\n if (!signal.aborted || signal.reason == null) return false\n return (\n error === signal.reason ||\n (error instanceof Error && error.cause === signal.reason)\n )\n}\n"]}
|
|
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>\ntype LexMethod = Query | Procedure | Subscription\n\nexport type NetAddr = {\n hostname: string\n port: number\n transport: 'tcp' | 'udp'\n}\n\nexport type UnixAddr = {\n path: string\n transport: 'unix' | 'unixpacket'\n}\n\nexport type Addr = NetAddr | UnixAddr | 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<Method, Credentials>\n}\n\nexport type LexRouterSubscriptionHandler<\n Method extends Subscription = Subscription,\n Credentials = unknown,\n> = (\n ctx: LexRouterHandlerContext<Method, Credentials>,\n) => AsyncIterable<InferMethodMessage<Method>>\n\nexport type LexRouterSubscriptionConfig<\n Method extends Subscription = Subscription,\n Credentials = unknown,\n> = {\n handler: LexRouterSubscriptionHandler<Method, Credentials>\n auth: LexRouterAuth<Method, Credentials>\n}\n\nexport type LexRouterAuthContext<Method extends LexMethod = LexMethod> = {\n params: InferMethodParams<Method>\n request: Request\n connection?: ConnectionInfo\n}\n\nexport type LexRouterAuth<\n Method extends LexMethod = LexMethod,\n Credentials = unknown,\n> = (ctx: LexRouterAuthContext<Method>) => Credentials | Promise<Credentials>\n\nexport type LexErrorHandlerContext = {\n error: unknown\n request: Request\n method: LexMethod\n}\n\nexport type UpgradeWebSocket = (request: Request) => {\n socket: WebSocket\n response: Response\n}\n\nexport type LexRouterOptions = {\n upgradeWebSocket?: UpgradeWebSocket\n onHandlerError?: (ctx: LexErrorHandlerContext) => void | Promise<void>\n highWaterMark?: number\n lowWaterMark?: number\n}\n\nexport class LexRouter {\n private handlers: Map<NsidString, Handler> = new Map()\n\n constructor(readonly options: LexRouterOptions = {}) {}\n\n add<M extends Subscription>(\n ns: Main<M>,\n handler: LexRouterSubscriptionHandler<M, void>,\n ): this\n add<M extends Subscription, Credentials>(\n ns: Main<M>,\n config: LexRouterSubscriptionConfig<M, Credentials>,\n ): this\n add<M extends Query | Procedure>(\n ns: Main<M>,\n handler: LexRouterMethodHandler<M, void>,\n ): this\n add<M extends Query | Procedure, Credentials>(\n ns: Main<M>,\n config: LexRouterMethodConfig<M, Credentials>,\n ): this\n add<M extends LexMethod>(\n ns: Main<M>,\n config:\n | LexRouterSubscriptionHandler<any, any>\n | LexRouterSubscriptionConfig<any, any>\n | LexRouterMethodHandler<any, any>\n | LexRouterMethodConfig<any, any>,\n ) {\n const method = getMain(ns)\n if (this.handlers.has(method.nsid)) {\n throw new TypeError(`Method ${method.nsid} already registered`)\n }\n const methodConfig =\n typeof config === 'function'\n ? { handler: config, auth: undefined }\n : config\n\n const handler: Handler =\n method.type === 'subscription'\n ? this.buildSubscriptionHandler(\n method,\n methodConfig.handler as LexRouterSubscriptionHandler<any, any>,\n methodConfig.auth,\n )\n : this.buildMethodHandler(\n method,\n methodConfig.handler as LexRouterMethodHandler<any, any>,\n methodConfig.auth,\n )\n\n this.handlers.set(method.nsid, handler)\n\n return this\n }\n\n private buildMethodHandler<Method extends Query | Procedure, Credentials>(\n method: Method,\n methodHandler: LexRouterMethodHandler<Method, Credentials>,\n auth?: LexRouterAuth<Method, Credentials>,\n ): Handler {\n const getInput = (\n method.type === 'procedure'\n ? getProcedureInput.bind(method)\n : getQueryInput.bind(method)\n ) as (request: Request) => Promise<InferMethodInput<Method, Body>>\n\n return async (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({ 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<Method, Credentials>,\n ): Handler {\n const {\n onHandlerError,\n upgradeWebSocket = (globalThis as any).Deno?.upgradeWebSocket as\n | UpgradeWebSocket\n | undefined,\n } = this.options\n if (!upgradeWebSocket) {\n throw new TypeError(\n 'WebSocket upgrade not supported in this environment. Please provide an upgradeWebSocket option when creating the LexRouter.',\n )\n }\n\n return async (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({ 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"]}
|
package/dist/nodejs.d.ts
CHANGED
|
@@ -5,13 +5,13 @@ export declare function upgradeWebSocket(request: Request): {
|
|
|
5
5
|
socket: WebSocket;
|
|
6
6
|
};
|
|
7
7
|
export type NetAddr = {
|
|
8
|
+
transport: 'tcp';
|
|
8
9
|
hostname: string;
|
|
9
10
|
port: number;
|
|
10
|
-
transport: 'tcp';
|
|
11
11
|
};
|
|
12
12
|
export type NodeConnectionInfo = {
|
|
13
|
-
|
|
14
|
-
remoteAddr
|
|
13
|
+
completed: Promise<void>;
|
|
14
|
+
remoteAddr: NetAddr | undefined;
|
|
15
15
|
};
|
|
16
16
|
export interface HandlerFunction {
|
|
17
17
|
(req: Request, info: NodeConnectionInfo): Response | Promise<Response>;
|
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;AAmBxC,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG;IAClD,QAAQ,EAAE,QAAQ,CAAA;IAClB,MAAM,EAAE,SAAS,CAAA;CAClB,CAkCA;
|
|
1
|
+
{"version":3,"file":"nodejs.d.ts","sourceRoot":"","sources":["../src/nodejs.ts"],"names":[],"mappings":"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"}
|
package/dist/nodejs.js
CHANGED
|
@@ -48,6 +48,7 @@ function upgradeWebSocket(request) {
|
|
|
48
48
|
});
|
|
49
49
|
return { response, socket };
|
|
50
50
|
}
|
|
51
|
+
const kUpgradeEvent = Symbol.for('@atproto/lex-server:upgrade');
|
|
51
52
|
function handleWebSocketUpgrade(req, response) {
|
|
52
53
|
const ws = response[kResponseWs];
|
|
53
54
|
if (!ws)
|
|
@@ -75,6 +76,7 @@ function handleWebSocketUpgrade(req, response) {
|
|
|
75
76
|
// @TODO find a way to properly "close" the _socket when the server is
|
|
76
77
|
// shutting down (might require replacing http-terminator with a local
|
|
77
78
|
// implementation)
|
|
79
|
+
req.emit(kUpgradeEvent, ws);
|
|
78
80
|
});
|
|
79
81
|
}
|
|
80
82
|
async function sendResponse(req, res, response) {
|
|
@@ -106,18 +108,9 @@ function toRequest(req) {
|
|
|
106
108
|
const url = new URL(req.url ?? '/', `${protocol}://${host}`);
|
|
107
109
|
const headers = toHeaders(req.headers);
|
|
108
110
|
const body = toBody(req);
|
|
109
|
-
const
|
|
110
|
-
const abort = (err) => abortController.abort(err);
|
|
111
|
-
req.on('close', abort);
|
|
112
|
-
req.on('error', abort);
|
|
113
|
-
req.on('end', abort);
|
|
114
|
-
abortController.signal.addEventListener('abort', () => {
|
|
115
|
-
req.off('close', abort);
|
|
116
|
-
req.off('error', abort);
|
|
117
|
-
req.off('end', abort);
|
|
118
|
-
}, { once: true });
|
|
111
|
+
const signal = requestSignal(req);
|
|
119
112
|
return new Request(url, {
|
|
120
|
-
signal
|
|
113
|
+
signal,
|
|
121
114
|
method: req.method,
|
|
122
115
|
headers,
|
|
123
116
|
body,
|
|
@@ -127,6 +120,49 @@ function toRequest(req) {
|
|
|
127
120
|
duplex: body ? 'half' : undefined,
|
|
128
121
|
});
|
|
129
122
|
}
|
|
123
|
+
function requestSignal(req) {
|
|
124
|
+
if (req.destroyed)
|
|
125
|
+
return AbortSignal.abort();
|
|
126
|
+
const abortController = new AbortController();
|
|
127
|
+
const abort = (err) => {
|
|
128
|
+
abortController.abort(err instanceof Error ? err : undefined);
|
|
129
|
+
req.off('close', abort);
|
|
130
|
+
req.off('error', abort);
|
|
131
|
+
req.off('end', abort);
|
|
132
|
+
req.off(kUpgradeEvent, abort);
|
|
133
|
+
};
|
|
134
|
+
req.on('close', abort);
|
|
135
|
+
req.on('error', abort);
|
|
136
|
+
req.on('end', abort);
|
|
137
|
+
req.on(kUpgradeEvent, abort);
|
|
138
|
+
return abortController.signal;
|
|
139
|
+
}
|
|
140
|
+
function requestCompletion(req) {
|
|
141
|
+
if (req.destroyed)
|
|
142
|
+
return Promise.resolve();
|
|
143
|
+
// Unlike the abort signal, we complete the promise only when the request
|
|
144
|
+
// is fully done, accounting for websocket upgrade.
|
|
145
|
+
return new Promise((resolve) => {
|
|
146
|
+
const cleanup = () => {
|
|
147
|
+
req.off('close', done);
|
|
148
|
+
req.off('error', done);
|
|
149
|
+
req.off('end', done);
|
|
150
|
+
req.off(kUpgradeEvent, onUpgrade);
|
|
151
|
+
};
|
|
152
|
+
const onUpgrade = (ws) => {
|
|
153
|
+
cleanup();
|
|
154
|
+
ws.addEventListener('close', () => resolve());
|
|
155
|
+
};
|
|
156
|
+
const done = () => {
|
|
157
|
+
resolve();
|
|
158
|
+
cleanup();
|
|
159
|
+
};
|
|
160
|
+
req.on('close', done);
|
|
161
|
+
req.on('error', done);
|
|
162
|
+
req.on('end', done);
|
|
163
|
+
req.on(kUpgradeEvent, onUpgrade);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
130
166
|
function toHeaders(headers) {
|
|
131
167
|
const result = new Headers();
|
|
132
168
|
for (const [key, value] of Object.entries(headers)) {
|
|
@@ -164,18 +200,12 @@ async function handleRequest(req, res, handlerFn) {
|
|
|
164
200
|
function toConnectionInfo(req) {
|
|
165
201
|
const { socket } = req;
|
|
166
202
|
return {
|
|
167
|
-
|
|
168
|
-
? {
|
|
169
|
-
hostname: socket.localAddress,
|
|
170
|
-
port: socket.localPort,
|
|
171
|
-
transport: 'tcp',
|
|
172
|
-
}
|
|
173
|
-
: undefined,
|
|
203
|
+
completed: requestCompletion(req),
|
|
174
204
|
remoteAddr: socket.remoteAddress != null
|
|
175
205
|
? {
|
|
206
|
+
transport: 'tcp',
|
|
176
207
|
hostname: socket.remoteAddress,
|
|
177
208
|
port: socket.remotePort,
|
|
178
|
-
transport: 'tcp',
|
|
179
209
|
}
|
|
180
210
|
: undefined,
|
|
181
211
|
};
|
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\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}\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\n const abortController = new AbortController()\n const abort = (err?: Error) => abortController.abort(err)\n\n req.on('close', abort)\n req.on('error', abort)\n req.on('end', abort)\n\n abortController.signal.addEventListener(\n 'abort',\n () => {\n req.off('close', abort)\n req.off('error', abort)\n req.off('end', abort)\n },\n { once: true },\n )\n\n return new Request(url, {\n signal: abortController.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 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 hostname: string\n port: number\n transport: 'tcp'\n}\n\nexport type NodeConnectionInfo = {\n localAddr?: NetAddr\n remoteAddr?: NetAddr\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 return {\n localAddr:\n socket.localAddress != null\n ? {\n hostname: socket.localAddress,\n port: socket.localPort!,\n transport: 'tcp',\n }\n : undefined,\n remoteAddr:\n socket.remoteAddress != null\n ? {\n hostname: socket.remoteAddress,\n port: socket.remotePort!,\n transport: 'tcp',\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'\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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/lex-server",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Request router for Atproto Lexicon protocols and schemas",
|
|
6
6
|
"keywords": [
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"tslib": "^2.8.1",
|
|
47
47
|
"ws": "^8.18.3",
|
|
48
48
|
"@atproto/lex-cbor": "0.0.5",
|
|
49
|
-
"@atproto/lex-data": "0.0.5",
|
|
50
49
|
"@atproto/lex-schema": "0.0.6",
|
|
50
|
+
"@atproto/lex-data": "0.0.5",
|
|
51
51
|
"@atproto/lex-json": "0.0.5"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
package/src/lex-server.test.ts
CHANGED
|
@@ -1514,7 +1514,7 @@ describe('Subscription', () => {
|
|
|
1514
1514
|
|
|
1515
1515
|
const router = new LexRouter({ upgradeWebSocket }).add(
|
|
1516
1516
|
io.example.subscribe,
|
|
1517
|
-
async function* ({ params: { message },
|
|
1517
|
+
async function* ({ params: { message }, signal }) {
|
|
1518
1518
|
try {
|
|
1519
1519
|
for (; sentCount < 10; ) {
|
|
1520
1520
|
await scheduler.wait(5, { signal })
|
package/src/lex-server.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
} from '@atproto/lex-schema'
|
|
19
19
|
import { drainWebsocket } from './lib/drain-websocket.js'
|
|
20
20
|
|
|
21
|
+
type Awaitable<T> = T | Promise<T>
|
|
21
22
|
type LexMethod = Query | Procedure | Subscription
|
|
22
23
|
|
|
23
24
|
export type NetAddr = {
|
|
@@ -31,11 +32,11 @@ export type UnixAddr = {
|
|
|
31
32
|
transport: 'unix' | 'unixpacket'
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
export type Addr = NetAddr | UnixAddr
|
|
35
|
+
export type Addr = NetAddr | UnixAddr | undefined
|
|
35
36
|
|
|
36
|
-
export type ConnectionInfo = {
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
export type ConnectionInfo<A extends Addr = Addr> = {
|
|
38
|
+
remoteAddr: A
|
|
39
|
+
completed: Promise<void>
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
type Handler = (
|
|
@@ -48,6 +49,7 @@ export type LexRouterHandlerContext<Method extends LexMethod, Credentials> = {
|
|
|
48
49
|
input: InferMethodInput<Method, Body>
|
|
49
50
|
params: InferMethodParams<Method>
|
|
50
51
|
request: Request
|
|
52
|
+
signal: AbortSignal
|
|
51
53
|
connection?: ConnectionInfo
|
|
52
54
|
}
|
|
53
55
|
|
|
@@ -72,7 +74,7 @@ export type LexRouterMethodHandler<
|
|
|
72
74
|
Credentials = unknown,
|
|
73
75
|
> = (
|
|
74
76
|
ctx: LexRouterHandlerContext<Method, Credentials>,
|
|
75
|
-
) =>
|
|
77
|
+
) => Awaitable<LexRouterHandlerOutput<Method>>
|
|
76
78
|
|
|
77
79
|
export type LexRouterMethodConfig<
|
|
78
80
|
Method extends Query | Procedure = Query | Procedure,
|
|
@@ -193,10 +195,7 @@ export class LexRouter {
|
|
|
193
195
|
: getQueryInput.bind(method)
|
|
194
196
|
) as (request: Request) => Promise<InferMethodInput<Method, Body>>
|
|
195
197
|
|
|
196
|
-
return async (
|
|
197
|
-
request: Request,
|
|
198
|
-
connection?: ConnectionInfo,
|
|
199
|
-
): Promise<Response> => {
|
|
198
|
+
return async (request, connection) => {
|
|
200
199
|
// @NOTE CORS requests should be handled by a middleware before reaching
|
|
201
200
|
// this point.
|
|
202
201
|
if (
|
|
@@ -227,6 +226,7 @@ export class LexRouter {
|
|
|
227
226
|
input,
|
|
228
227
|
request,
|
|
229
228
|
connection,
|
|
229
|
+
signal: request.signal,
|
|
230
230
|
})
|
|
231
231
|
|
|
232
232
|
if (output instanceof Response) {
|
|
@@ -272,10 +272,7 @@ export class LexRouter {
|
|
|
272
272
|
)
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
-
return async (
|
|
276
|
-
request: Request,
|
|
277
|
-
connection?: ConnectionInfo,
|
|
278
|
-
): Promise<Response> => {
|
|
275
|
+
return async (request, connection) => {
|
|
279
276
|
if (request.method !== 'GET') {
|
|
280
277
|
return Response.json(
|
|
281
278
|
{ error: 'InvalidRequest', message: 'Method not allowed' },
|
|
@@ -302,19 +299,34 @@ export class LexRouter {
|
|
|
302
299
|
)
|
|
303
300
|
}
|
|
304
301
|
|
|
302
|
+
if (request.signal.aborted) {
|
|
303
|
+
return Response.json(
|
|
304
|
+
{ error: 'RequestAborted', message: 'The request was aborted' },
|
|
305
|
+
{ status: 499 },
|
|
306
|
+
)
|
|
307
|
+
}
|
|
308
|
+
|
|
305
309
|
try {
|
|
306
310
|
const { response, socket } = upgradeWebSocket(request)
|
|
307
311
|
|
|
308
|
-
|
|
312
|
+
// @NOTE We are using a distinct signal than request.signal because that
|
|
313
|
+
// signal may get aborted before the WebSocket is closed (this is the
|
|
314
|
+
// case with Deno).
|
|
315
|
+
const abortController = new AbortController()
|
|
316
|
+
const { signal } = abortController
|
|
317
|
+
const abort = () => abortController.abort()
|
|
318
|
+
|
|
319
|
+
const onMessage = (event: unknown) => {
|
|
309
320
|
const error = new LexError(
|
|
310
321
|
'InvalidRequest',
|
|
311
322
|
'XRPC subscriptions do not accept messages',
|
|
323
|
+
{ cause: event },
|
|
312
324
|
)
|
|
313
325
|
socket.send(encodeErrorFrame(error))
|
|
314
326
|
socket.close(1008, error.error)
|
|
315
|
-
}
|
|
327
|
+
}
|
|
316
328
|
|
|
317
|
-
|
|
329
|
+
const onOpen = async () => {
|
|
318
330
|
try {
|
|
319
331
|
const url = new URL(request.url)
|
|
320
332
|
const params = method.parameters.fromURLSearchParams(
|
|
@@ -325,7 +337,7 @@ export class LexRouter {
|
|
|
325
337
|
? await auth({ params, request, connection })
|
|
326
338
|
: (undefined as Credentials)
|
|
327
339
|
|
|
328
|
-
|
|
340
|
+
signal.throwIfAborted()
|
|
329
341
|
|
|
330
342
|
const iterable = methodHandler({
|
|
331
343
|
credentials,
|
|
@@ -333,31 +345,20 @@ export class LexRouter {
|
|
|
333
345
|
input: undefined as InferMethodInput<Method, Body>,
|
|
334
346
|
request,
|
|
335
347
|
connection,
|
|
348
|
+
signal,
|
|
336
349
|
})
|
|
337
350
|
|
|
338
351
|
const iterator = iterable[Symbol.asyncIterator]()
|
|
339
352
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
try {
|
|
345
|
-
await iterator.return!()
|
|
346
|
-
} catch {
|
|
347
|
-
// Ignore
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
socket.addEventListener('error', abort)
|
|
351
|
-
socket.addEventListener('close', abort)
|
|
352
|
-
}
|
|
353
|
+
signal.addEventListener('abort', async () => {
|
|
354
|
+
// @NOTE will cause the process to crash if this throws
|
|
355
|
+
await iterator.return?.()
|
|
356
|
+
})
|
|
353
357
|
|
|
354
|
-
while (socket.readyState === 1) {
|
|
358
|
+
while (!signal.aborted && socket.readyState === 1) {
|
|
355
359
|
const result = await iterator.next()
|
|
356
360
|
if (result.done) break
|
|
357
361
|
|
|
358
|
-
// Should not be needed (socket would emit "close" event)
|
|
359
|
-
request.signal.throwIfAborted()
|
|
360
|
-
|
|
361
362
|
// @TODO add validation of output based on method.output.schema?
|
|
362
363
|
|
|
363
364
|
const data = encodeMessageFrame(method, result.value)
|
|
@@ -366,10 +367,12 @@ export class LexRouter {
|
|
|
366
367
|
|
|
367
368
|
// Apply backpressure by waiting for the buffered data to drain
|
|
368
369
|
// before generating the next message
|
|
369
|
-
await drainWebsocket(socket,
|
|
370
|
+
await drainWebsocket(socket, signal, this.options)
|
|
370
371
|
}
|
|
371
372
|
|
|
372
|
-
socket.
|
|
373
|
+
if (socket.readyState === 1) {
|
|
374
|
+
socket.close(1000)
|
|
375
|
+
}
|
|
373
376
|
} catch (error) {
|
|
374
377
|
// If the socket is still open, send an error frame before closing
|
|
375
378
|
if (socket.readyState === 1) {
|
|
@@ -391,8 +394,15 @@ export class LexRouter {
|
|
|
391
394
|
if (onHandlerError && !isAbortReason(request.signal, error)) {
|
|
392
395
|
await onHandlerError({ error, request, method })
|
|
393
396
|
}
|
|
397
|
+
} finally {
|
|
398
|
+
abortController.abort()
|
|
394
399
|
}
|
|
395
|
-
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
socket.addEventListener('error', abort)
|
|
403
|
+
socket.addEventListener('close', abort)
|
|
404
|
+
socket.addEventListener('open', onOpen)
|
|
405
|
+
socket.addEventListener('message', onMessage)
|
|
396
406
|
|
|
397
407
|
return response
|
|
398
408
|
} catch (error) {
|
package/src/nodejs.ts
CHANGED
|
@@ -66,6 +66,8 @@ export function upgradeWebSocket(request: Request): {
|
|
|
66
66
|
return { response, socket }
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
const kUpgradeEvent = Symbol.for('@atproto/lex-server:upgrade')
|
|
70
|
+
|
|
69
71
|
function handleWebSocketUpgrade(
|
|
70
72
|
req: IncomingMessage,
|
|
71
73
|
response: Response,
|
|
@@ -98,6 +100,8 @@ function handleWebSocketUpgrade(
|
|
|
98
100
|
// @TODO find a way to properly "close" the _socket when the server is
|
|
99
101
|
// shutting down (might require replacing http-terminator with a local
|
|
100
102
|
// implementation)
|
|
103
|
+
|
|
104
|
+
req.emit(kUpgradeEvent, ws)
|
|
101
105
|
})
|
|
102
106
|
}
|
|
103
107
|
|
|
@@ -138,26 +142,10 @@ function toRequest(req: IncomingMessage): Request {
|
|
|
138
142
|
const url = new URL(req.url ?? '/', `${protocol}://${host}`)
|
|
139
143
|
const headers = toHeaders(req.headers)
|
|
140
144
|
const body = toBody(req)
|
|
141
|
-
|
|
142
|
-
const abortController = new AbortController()
|
|
143
|
-
const abort = (err?: Error) => abortController.abort(err)
|
|
144
|
-
|
|
145
|
-
req.on('close', abort)
|
|
146
|
-
req.on('error', abort)
|
|
147
|
-
req.on('end', abort)
|
|
148
|
-
|
|
149
|
-
abortController.signal.addEventListener(
|
|
150
|
-
'abort',
|
|
151
|
-
() => {
|
|
152
|
-
req.off('close', abort)
|
|
153
|
-
req.off('error', abort)
|
|
154
|
-
req.off('end', abort)
|
|
155
|
-
},
|
|
156
|
-
{ once: true },
|
|
157
|
-
)
|
|
145
|
+
const signal = requestSignal(req)
|
|
158
146
|
|
|
159
147
|
return new Request(url, {
|
|
160
|
-
signal
|
|
148
|
+
signal,
|
|
161
149
|
method: req.method,
|
|
162
150
|
headers,
|
|
163
151
|
body,
|
|
@@ -168,6 +156,58 @@ function toRequest(req: IncomingMessage): Request {
|
|
|
168
156
|
})
|
|
169
157
|
}
|
|
170
158
|
|
|
159
|
+
function requestSignal(req: IncomingMessage): AbortSignal {
|
|
160
|
+
if (req.destroyed) return AbortSignal.abort()
|
|
161
|
+
|
|
162
|
+
const abortController = new AbortController()
|
|
163
|
+
|
|
164
|
+
const abort = (err?: Error | WebSocket) => {
|
|
165
|
+
abortController.abort(err instanceof Error ? err : undefined)
|
|
166
|
+
|
|
167
|
+
req.off('close', abort)
|
|
168
|
+
req.off('error', abort)
|
|
169
|
+
req.off('end', abort)
|
|
170
|
+
req.off(kUpgradeEvent, abort)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
req.on('close', abort)
|
|
174
|
+
req.on('error', abort)
|
|
175
|
+
req.on('end', abort)
|
|
176
|
+
req.on(kUpgradeEvent, abort)
|
|
177
|
+
|
|
178
|
+
return abortController.signal
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function requestCompletion(req: IncomingMessage): Promise<void> {
|
|
182
|
+
if (req.destroyed) return Promise.resolve()
|
|
183
|
+
|
|
184
|
+
// Unlike the abort signal, we complete the promise only when the request
|
|
185
|
+
// is fully done, accounting for websocket upgrade.
|
|
186
|
+
return new Promise((resolve) => {
|
|
187
|
+
const cleanup = () => {
|
|
188
|
+
req.off('close', done)
|
|
189
|
+
req.off('error', done)
|
|
190
|
+
req.off('end', done)
|
|
191
|
+
req.off(kUpgradeEvent, onUpgrade)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const onUpgrade = (ws: WebSocket) => {
|
|
195
|
+
cleanup()
|
|
196
|
+
ws.addEventListener('close', () => resolve())
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const done = () => {
|
|
200
|
+
resolve()
|
|
201
|
+
cleanup()
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
req.on('close', done)
|
|
205
|
+
req.on('error', done)
|
|
206
|
+
req.on('end', done)
|
|
207
|
+
req.on(kUpgradeEvent, onUpgrade)
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
|
|
171
211
|
function toHeaders(headers: IncomingHttpHeaders): Headers {
|
|
172
212
|
const result = new Headers()
|
|
173
213
|
for (const [key, value] of Object.entries(headers)) {
|
|
@@ -202,14 +242,14 @@ function toBody(req: IncomingMessage): null | ReadableStream<Uint8Array> {
|
|
|
202
242
|
}
|
|
203
243
|
|
|
204
244
|
export type NetAddr = {
|
|
245
|
+
transport: 'tcp'
|
|
205
246
|
hostname: string
|
|
206
247
|
port: number
|
|
207
|
-
transport: 'tcp'
|
|
208
248
|
}
|
|
209
249
|
|
|
210
250
|
export type NodeConnectionInfo = {
|
|
211
|
-
|
|
212
|
-
remoteAddr
|
|
251
|
+
completed: Promise<void>
|
|
252
|
+
remoteAddr: NetAddr | undefined
|
|
213
253
|
}
|
|
214
254
|
|
|
215
255
|
export interface HandlerFunction {
|
|
@@ -233,21 +273,15 @@ async function handleRequest(
|
|
|
233
273
|
|
|
234
274
|
function toConnectionInfo(req: IncomingMessage): NodeConnectionInfo {
|
|
235
275
|
const { socket } = req
|
|
276
|
+
|
|
236
277
|
return {
|
|
237
|
-
|
|
238
|
-
socket.localAddress != null
|
|
239
|
-
? {
|
|
240
|
-
hostname: socket.localAddress,
|
|
241
|
-
port: socket.localPort!,
|
|
242
|
-
transport: 'tcp',
|
|
243
|
-
}
|
|
244
|
-
: undefined,
|
|
278
|
+
completed: requestCompletion(req),
|
|
245
279
|
remoteAddr:
|
|
246
280
|
socket.remoteAddress != null
|
|
247
281
|
? {
|
|
282
|
+
transport: 'tcp',
|
|
248
283
|
hostname: socket.remoteAddress,
|
|
249
284
|
port: socket.remotePort!,
|
|
250
|
-
transport: 'tcp',
|
|
251
285
|
}
|
|
252
286
|
: undefined,
|
|
253
287
|
}
|
package/dist/example.d.ts
DELETED
package/dist/example.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"example.d.ts","sourceRoot":"","sources":["../src/example.ts"],"names":[],"mappings":""}
|
package/dist/example.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/* eslint-disable @typescript-eslint/no-namespace */
|
|
3
|
-
/* eslint-disable n/no-extraneous-import */
|
|
4
|
-
/* eslint-disable import/no-unresolved */
|
|
5
|
-
/* eslint-env node */
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
const lex_1 = require("@atproto/lex");
|
|
8
|
-
const index_js_1 = require("./index.js");
|
|
9
|
-
const nodejs_js_1 = require("./nodejs.js");
|
|
10
|
-
var com;
|
|
11
|
-
(function (com) {
|
|
12
|
-
let example;
|
|
13
|
-
(function (example) {
|
|
14
|
-
let echo;
|
|
15
|
-
(function (echo) {
|
|
16
|
-
echo.nsid = 'com.example.echo';
|
|
17
|
-
echo.message = lex_1.l.typedObject(echo.nsid, 'message', lex_1.l.object({
|
|
18
|
-
message: lex_1.l.string(),
|
|
19
|
-
}));
|
|
20
|
-
echo.main = lex_1.l.subscription(echo.nsid, lex_1.l.params({
|
|
21
|
-
message: lex_1.l.string({ minLength: 1 }),
|
|
22
|
-
interval: lex_1.l.optional(lex_1.l.integer({ minimum: 0, default: 500 })),
|
|
23
|
-
}), lex_1.l.typedUnion([lex_1.l.typedRef(() => echo.message)], false));
|
|
24
|
-
})(echo = example.echo || (example.echo = {}));
|
|
25
|
-
})(example = com.example || (com.example = {}));
|
|
26
|
-
})(com || (com = {}));
|
|
27
|
-
const router = new index_js_1.LexRouter({ upgradeWebSocket: nodejs_js_1.upgradeWebSocket })
|
|
28
|
-
//
|
|
29
|
-
.add(com.example.echo, async function* ({ params: { interval, message } }) {
|
|
30
|
-
while (true) {
|
|
31
|
-
yield com.example.echo.message.$build({ message });
|
|
32
|
-
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
(0, nodejs_js_1.serve)(router, { port: 8080, host: '0.0.0.0' });
|
|
36
|
-
//# sourceMappingURL=example.js.map
|
package/dist/example.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"example.js","sourceRoot":"","sources":["../src/example.ts"],"names":[],"mappings":";AAAA,oDAAoD;AACpD,2CAA2C;AAC3C,yCAAyC;AACzC,qBAAqB;;AAErB,sCAAgC;AAChC,yCAAsC;AACtC,2CAAqD;AAErD,IAAU,GAAG,CAuBZ;AAvBD,WAAU,GAAG;IACX,IAAiB,OAAO,CAqBvB;IArBD,WAAiB,OAAO;QACtB,IAAiB,IAAI,CAmBpB;QAnBD,WAAiB,IAAI;YACN,SAAI,GAAG,kBAAkB,CAAA;YAEzB,YAAO,GAAG,OAAC,CAAC,WAAW,CAClC,KAAA,IAAI,EACJ,SAAS,EACT,OAAC,CAAC,MAAM,CAAC;gBACP,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE;aACpB,CAAC,CACH,CAAA;YAEY,SAAI,GAAG,OAAC,CAAC,YAAY,CAChC,KAAA,IAAI,EACJ,OAAC,CAAC,MAAM,CAAC;gBACP,OAAO,EAAE,OAAC,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;gBACnC,QAAQ,EAAE,OAAC,CAAC,QAAQ,CAAC,OAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;aAC9D,CAAC,EACF,OAAC,CAAC,UAAU,CAAC,CAAC,OAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAA,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,CACjD,CAAA;QACH,CAAC,EAnBgB,IAAI,GAAJ,YAAI,KAAJ,YAAI,QAmBpB;IACH,CAAC,EArBgB,OAAO,GAAP,WAAO,KAAP,WAAO,QAqBvB;AACH,CAAC,EAvBS,GAAG,KAAH,GAAG,QAuBZ;AAED,MAAM,MAAM,GAAG,IAAI,oBAAS,CAAC,EAAE,gBAAgB,EAAhB,4BAAgB,EAAE,CAAC;IAChD,EAAE;KACD,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;IACvE,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAA;QAClD,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAA;IAC/D,CAAC;AACH,CAAC,CAAC,CAAA;AAEJ,IAAA,iBAAK,EAAC,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAA","sourcesContent":["/* eslint-disable @typescript-eslint/no-namespace */\n/* eslint-disable n/no-extraneous-import */\n/* eslint-disable import/no-unresolved */\n/* eslint-env node */\n\nimport { l } from '@atproto/lex'\nimport { LexRouter } from './index.js'\nimport { serve, upgradeWebSocket } from './nodejs.js'\n\nnamespace com {\n export namespace example {\n export namespace echo {\n export const nsid = 'com.example.echo'\n\n export const message = l.typedObject(\n nsid,\n 'message',\n l.object({\n message: l.string(),\n }),\n )\n\n export const main = l.subscription(\n nsid,\n l.params({\n message: l.string({ minLength: 1 }),\n interval: l.optional(l.integer({ minimum: 0, default: 500 })),\n }),\n l.typedUnion([l.typedRef(() => message)], false),\n )\n }\n }\n}\n\nconst router = new LexRouter({ upgradeWebSocket })\n //\n .add(com.example.echo, async function* ({ params: { interval, message } }) {\n while (true) {\n yield com.example.echo.message.$build({ message })\n await new Promise((resolve) => setTimeout(resolve, interval))\n }\n })\n\nserve(router, { port: 8080, host: '0.0.0.0' })\n"]}
|
package/dist/lex-auth-error.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { LexError, LexErrorCode } from '@atproto/lex-data';
|
|
2
|
-
export type WWWAuthenticate = {
|
|
3
|
-
[k: string]: Record<string, string>;
|
|
4
|
-
};
|
|
5
|
-
export declare function formatWWWAuthenticate(wwwAuthenticate: WWWAuthenticate): string;
|
|
6
|
-
export declare class LexServerAuthError<N extends LexErrorCode = LexErrorCode> extends LexError<N> {
|
|
7
|
-
readonly wwwAuthenticate?: WWWAuthenticate | undefined;
|
|
8
|
-
name: string;
|
|
9
|
-
constructor(error: N, message: string, wwwAuthenticate?: WWWAuthenticate | undefined, options?: ErrorOptions);
|
|
10
|
-
get wwwAuthenticateHeader(): string;
|
|
11
|
-
toJSON(): import("@atproto/lex-data").LexErrorData<any>;
|
|
12
|
-
toResponse(): Response;
|
|
13
|
-
static from(cause: LexError, wwwAuthenticate?: WWWAuthenticate): LexServerAuthError;
|
|
14
|
-
}
|
|
15
|
-
//# sourceMappingURL=lex-auth-error.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lex-auth-error.d.ts","sourceRoot":"","sources":["../src/lex-auth-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAE1D,MAAM,MAAM,eAAe,GAAG;IAAE,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAA;AACrE,wBAAgB,qBAAqB,CACnC,eAAe,EAAE,eAAe,GAC/B,MAAM,CAWR;AAED,qBAAa,kBAAkB,CAC7B,CAAC,SAAS,YAAY,GAAG,YAAY,CACrC,SAAQ,QAAQ,CAAC,CAAC,CAAC;IAMjB,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe;IAL5C,IAAI,SAAuB;gBAGzB,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,MAAM,EACN,eAAe,CAAC,EAAE,eAAe,YAAA,EAC1C,OAAO,CAAC,EAAE,YAAY;IAKxB,IAAI,qBAAqB,IAAI,MAAM,CAElC;IAED,MAAM;IAKN,UAAU,IAAI,QAAQ;IAatB,MAAM,CAAC,IAAI,CACT,KAAK,EAAE,QAAQ,EACf,eAAe,CAAC,EAAE,eAAe,GAChC,kBAAkB;CAMtB"}
|
package/dist/lex-auth-error.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LexServerAuthError = void 0;
|
|
4
|
-
exports.formatWWWAuthenticate = formatWWWAuthenticate;
|
|
5
|
-
const lex_data_1 = require("@atproto/lex-data");
|
|
6
|
-
function formatWWWAuthenticate(wwwAuthenticate) {
|
|
7
|
-
return Object.entries(wwwAuthenticate)
|
|
8
|
-
.map(([type, params]) => {
|
|
9
|
-
if (!params)
|
|
10
|
-
return null;
|
|
11
|
-
const paramsEnc = Object.entries(params)
|
|
12
|
-
.filter(([_, val]) => val != null)
|
|
13
|
-
.map(([name, val]) => `${name}=${JSON.stringify(val)}`);
|
|
14
|
-
return paramsEnc?.length ? `${type} ${paramsEnc.join(', ')}` : type;
|
|
15
|
-
})
|
|
16
|
-
.filter(Boolean)
|
|
17
|
-
.join(', ');
|
|
18
|
-
}
|
|
19
|
-
class LexServerAuthError extends lex_data_1.LexError {
|
|
20
|
-
wwwAuthenticate;
|
|
21
|
-
name = 'LexServerAuthError';
|
|
22
|
-
constructor(error, message, wwwAuthenticate, options) {
|
|
23
|
-
super(error, message, options);
|
|
24
|
-
this.wwwAuthenticate = wwwAuthenticate;
|
|
25
|
-
}
|
|
26
|
-
get wwwAuthenticateHeader() {
|
|
27
|
-
return formatWWWAuthenticate(this.wwwAuthenticate ?? {});
|
|
28
|
-
}
|
|
29
|
-
toJSON() {
|
|
30
|
-
const { cause } = this;
|
|
31
|
-
return cause instanceof lex_data_1.LexError ? cause.toJSON() : super.toJSON();
|
|
32
|
-
}
|
|
33
|
-
toResponse() {
|
|
34
|
-
const { wwwAuthenticateHeader } = this;
|
|
35
|
-
const headers = wwwAuthenticateHeader
|
|
36
|
-
? new Headers({
|
|
37
|
-
'WWW-Authenticate': wwwAuthenticateHeader,
|
|
38
|
-
'Access-Control-Expose-Headers': 'WWW-Authenticate', // CORS
|
|
39
|
-
})
|
|
40
|
-
: undefined;
|
|
41
|
-
return Response.json(this.toJSON(), { status: 401, headers });
|
|
42
|
-
}
|
|
43
|
-
static from(cause, wwwAuthenticate) {
|
|
44
|
-
if (cause instanceof LexServerAuthError)
|
|
45
|
-
return cause;
|
|
46
|
-
return new LexServerAuthError(cause.error, cause.message, wwwAuthenticate, {
|
|
47
|
-
cause,
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
exports.LexServerAuthError = LexServerAuthError;
|
|
52
|
-
//# sourceMappingURL=lex-auth-error.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lex-auth-error.js","sourceRoot":"","sources":["../src/lex-auth-error.ts"],"names":[],"mappings":";;;AAGA,sDAaC;AAhBD,gDAA0D;AAG1D,SAAgB,qBAAqB,CACnC,eAAgC;IAEhC,OAAO,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC;SACnC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE;QACtB,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAA;QACxB,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;aACrC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,IAAI,CAAC;aACjC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACzD,OAAO,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;IACrE,CAAC,CAAC;SACD,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,IAAI,CAAC,CAAA;AACf,CAAC;AAED,MAAa,kBAEX,SAAQ,mBAAW;IAMR;IALX,IAAI,GAAG,oBAAoB,CAAA;IAE3B,YACE,KAAQ,EACR,OAAe,EACN,eAAiC,EAC1C,OAAsB;QAEtB,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;QAHrB,oBAAe,GAAf,eAAe,CAAkB;IAI5C,CAAC;IAED,IAAI,qBAAqB;QACvB,OAAO,qBAAqB,CAAC,IAAI,CAAC,eAAe,IAAI,EAAE,CAAC,CAAA;IAC1D,CAAC;IAED,MAAM;QACJ,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAA;QACtB,OAAO,KAAK,YAAY,mBAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAA;IACpE,CAAC;IAED,UAAU;QACR,MAAM,EAAE,qBAAqB,EAAE,GAAG,IAAI,CAAA;QAEtC,MAAM,OAAO,GAAG,qBAAqB;YACnC,CAAC,CAAC,IAAI,OAAO,CAAC;gBACV,kBAAkB,EAAE,qBAAqB;gBACzC,+BAA+B,EAAE,kBAAkB,EAAE,OAAO;aAC7D,CAAC;YACJ,CAAC,CAAC,SAAS,CAAA;QAEb,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAA;IAC/D,CAAC;IAED,MAAM,CAAC,IAAI,CACT,KAAe,EACf,eAAiC;QAEjC,IAAI,KAAK,YAAY,kBAAkB;YAAE,OAAO,KAAK,CAAA;QACrD,OAAO,IAAI,kBAAkB,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,eAAe,EAAE;YACzE,KAAK;SACN,CAAC,CAAA;IACJ,CAAC;CACF;AA7CD,gDA6CC","sourcesContent":["import { LexError, LexErrorCode } from '@atproto/lex-data'\n\nexport type WWWAuthenticate = { [k: string]: Record<string, string> }\nexport function formatWWWAuthenticate(\n wwwAuthenticate: WWWAuthenticate,\n): string {\n return Object.entries(wwwAuthenticate)\n .map(([type, params]) => {\n if (!params) return null\n const paramsEnc = Object.entries(params)\n .filter(([_, val]) => val != null)\n .map(([name, val]) => `${name}=${JSON.stringify(val)}`)\n return paramsEnc?.length ? `${type} ${paramsEnc.join(', ')}` : type\n })\n .filter(Boolean)\n .join(', ')\n}\n\nexport class LexServerAuthError<\n N extends LexErrorCode = LexErrorCode,\n> extends LexError<N> {\n name = 'LexServerAuthError'\n\n constructor(\n error: N,\n message: string,\n readonly wwwAuthenticate?: WWWAuthenticate,\n options?: ErrorOptions,\n ) {\n super(error, message, options)\n }\n\n get wwwAuthenticateHeader(): string {\n return formatWWWAuthenticate(this.wwwAuthenticate ?? {})\n }\n\n toJSON() {\n const { cause } = this\n return cause instanceof LexError ? cause.toJSON() : super.toJSON()\n }\n\n toResponse(): Response {\n const { wwwAuthenticateHeader } = this\n\n const headers = wwwAuthenticateHeader\n ? new Headers({\n 'WWW-Authenticate': wwwAuthenticateHeader,\n 'Access-Control-Expose-Headers': 'WWW-Authenticate', // CORS\n })\n : undefined\n\n return Response.json(this.toJSON(), { status: 401, headers })\n }\n\n static from(\n cause: LexError,\n wwwAuthenticate?: WWWAuthenticate,\n ): LexServerAuthError {\n if (cause instanceof LexServerAuthError) return cause\n return new LexServerAuthError(cause.error, cause.message, wwwAuthenticate, {\n cause,\n })\n }\n}\n"]}
|
package/dist/subscripotion.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"subscripotion.d.ts","sourceRoot":"","sources":["../src/subscripotion.ts"],"names":[],"mappings":""}
|
package/dist/subscripotion.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/* eslint-disable @typescript-eslint/no-namespace */
|
|
3
|
-
/* eslint-disable n/no-extraneous-import */
|
|
4
|
-
/* eslint-disable import/no-unresolved */
|
|
5
|
-
/* eslint-env node */
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
const lex_1 = require("@atproto/lex");
|
|
8
|
-
const index_js_1 = require("./index.js");
|
|
9
|
-
const nodejs_js_1 = require("./nodejs.js");
|
|
10
|
-
var com;
|
|
11
|
-
(function (com) {
|
|
12
|
-
let example;
|
|
13
|
-
(function (example) {
|
|
14
|
-
let echo;
|
|
15
|
-
(function (echo) {
|
|
16
|
-
echo.nsid = 'com.example.echo';
|
|
17
|
-
echo.message = lex_1.l.typedObject(echo.nsid, 'message', lex_1.l.object({
|
|
18
|
-
message: lex_1.l.string(),
|
|
19
|
-
}));
|
|
20
|
-
echo.main = lex_1.l.subscription(echo.nsid, lex_1.l.params({
|
|
21
|
-
message: lex_1.l.string({ minLength: 1 }),
|
|
22
|
-
interval: lex_1.l.optional(lex_1.l.integer({ minimum: 0, default: 500 })),
|
|
23
|
-
}), lex_1.l.typedUnion([lex_1.l.typedRef(() => echo.message)], false));
|
|
24
|
-
})(echo = example.echo || (example.echo = {}));
|
|
25
|
-
})(example = com.example || (com.example = {}));
|
|
26
|
-
})(com || (com = {}));
|
|
27
|
-
const router = new index_js_1.LexRouter({ upgradeWebSocket: nodejs_js_1.upgradeWebSocket })
|
|
28
|
-
//
|
|
29
|
-
.add(com.example.echo, async function* ({ params: { interval, message } }) {
|
|
30
|
-
while (true) {
|
|
31
|
-
yield com.example.echo.message.$build({ message });
|
|
32
|
-
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
(0, nodejs_js_1.serve)(router, { port: 8080, host: '0.0.0.0' });
|
|
36
|
-
//# sourceMappingURL=subscripotion.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"subscripotion.js","sourceRoot":"","sources":["../src/subscripotion.ts"],"names":[],"mappings":";AAAA,oDAAoD;AACpD,2CAA2C;AAC3C,yCAAyC;AACzC,qBAAqB;;AAErB,sCAAgC;AAChC,yCAAsC;AACtC,2CAAqD;AAErD,IAAU,GAAG,CAuBZ;AAvBD,WAAU,GAAG;IACX,IAAiB,OAAO,CAqBvB;IArBD,WAAiB,OAAO;QACtB,IAAiB,IAAI,CAmBpB;QAnBD,WAAiB,IAAI;YACN,SAAI,GAAG,kBAAkB,CAAA;YAEzB,YAAO,GAAG,OAAC,CAAC,WAAW,CAClC,KAAA,IAAI,EACJ,SAAS,EACT,OAAC,CAAC,MAAM,CAAC;gBACP,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE;aACpB,CAAC,CACH,CAAA;YAEY,SAAI,GAAG,OAAC,CAAC,YAAY,CAChC,KAAA,IAAI,EACJ,OAAC,CAAC,MAAM,CAAC;gBACP,OAAO,EAAE,OAAC,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;gBACnC,QAAQ,EAAE,OAAC,CAAC,QAAQ,CAAC,OAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;aAC9D,CAAC,EACF,OAAC,CAAC,UAAU,CAAC,CAAC,OAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAA,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,CACjD,CAAA;QACH,CAAC,EAnBgB,IAAI,GAAJ,YAAI,KAAJ,YAAI,QAmBpB;IACH,CAAC,EArBgB,OAAO,GAAP,WAAO,KAAP,WAAO,QAqBvB;AACH,CAAC,EAvBS,GAAG,KAAH,GAAG,QAuBZ;AAED,MAAM,MAAM,GAAG,IAAI,oBAAS,CAAC,EAAE,gBAAgB,EAAhB,4BAAgB,EAAE,CAAC;IAChD,EAAE;KACD,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;IACvE,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAA;QAClD,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAA;IAC/D,CAAC;AACH,CAAC,CAAC,CAAA;AAEJ,IAAA,iBAAK,EAAC,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAA","sourcesContent":["/* eslint-disable @typescript-eslint/no-namespace */\n/* eslint-disable n/no-extraneous-import */\n/* eslint-disable import/no-unresolved */\n/* eslint-env node */\n\nimport { l } from '@atproto/lex'\nimport { LexRouter } from './index.js'\nimport { serve, upgradeWebSocket } from './nodejs.js'\n\nnamespace com {\n export namespace example {\n export namespace echo {\n export const nsid = 'com.example.echo'\n\n export const message = l.typedObject(\n nsid,\n 'message',\n l.object({\n message: l.string(),\n }),\n )\n\n export const main = l.subscription(\n nsid,\n l.params({\n message: l.string({ minLength: 1 }),\n interval: l.optional(l.integer({ minimum: 0, default: 500 })),\n }),\n l.typedUnion([l.typedRef(() => message)], false),\n )\n }\n }\n}\n\nconst router = new LexRouter({ upgradeWebSocket })\n //\n .add(com.example.echo, async function* ({ params: { interval, message } }) {\n while (true) {\n yield com.example.echo.message.$build({ message })\n await new Promise((resolve) => setTimeout(resolve, interval))\n }\n })\n\nserve(router, { port: 8080, host: '0.0.0.0' })\n"]}
|
package/dist/test.d.mts
DELETED
package/dist/test.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"test.d.mts","sourceRoot":"","sources":["../src/test.mjs"],"names":[],"mappings":""}
|
package/dist/test.mjs
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/* eslint-env node */
|
|
3
|
-
import { l } from '@atproto/lex';
|
|
4
|
-
import { LexRouter } from './lex-server.js';
|
|
5
|
-
import { startServer, upgradeWebSocket } from './nodejs.js';
|
|
6
|
-
const echoMessage = l.typedObject('com.example.echo', 'echoMessage', l.object({
|
|
7
|
-
message: l.string(),
|
|
8
|
-
}));
|
|
9
|
-
const sub = l.subscription('com.example.echo', l.params({
|
|
10
|
-
message: l.string({ minLength: 1 }),
|
|
11
|
-
}), l.typedUnion([
|
|
12
|
-
//
|
|
13
|
-
l.typedRef(() => echoMessage),
|
|
14
|
-
], false));
|
|
15
|
-
const router = new LexRouter({
|
|
16
|
-
upgradeWebSocket,
|
|
17
|
-
onMethodNotFound: () => {
|
|
18
|
-
return new Response('<h1>404 Not Found</h1>', {
|
|
19
|
-
status: 404,
|
|
20
|
-
headers: { 'Content-Type': 'text/html' },
|
|
21
|
-
});
|
|
22
|
-
},
|
|
23
|
-
})
|
|
24
|
-
//
|
|
25
|
-
.add(sub, async function* ({ params: { message } }) {
|
|
26
|
-
try {
|
|
27
|
-
while (true) {
|
|
28
|
-
/** @type {l.TypedObject} */
|
|
29
|
-
const item = {
|
|
30
|
-
$type: 'com.example.echo#ff',
|
|
31
|
-
// @ts-expect-error
|
|
32
|
-
message,
|
|
33
|
-
};
|
|
34
|
-
yield item;
|
|
35
|
-
yield echoMessage.$build({ message });
|
|
36
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
finally {
|
|
40
|
-
console.log('Subscription ended');
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
startServer(router, {
|
|
44
|
-
port: 8080,
|
|
45
|
-
onError: (err) => {
|
|
46
|
-
console.error('Server error:', err);
|
|
47
|
-
},
|
|
48
|
-
}).then((server) => {
|
|
49
|
-
const { port } = /** @type {import('net').AddressInfo} */ (server.address());
|
|
50
|
-
console.log(`Server is running on http://localhost:${port}`);
|
|
51
|
-
});
|
|
52
|
-
//# sourceMappingURL=test.mjs.map
|
package/dist/test.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"test.mjs","sourceRoot":"","sources":["../src/test.mjs"],"names":[],"mappings":";AAAA,qBAAqB;AAErB,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE3D,MAAM,WAAW,GAAG,CAAC,CAAC,WAAW,CAC/B,kBAAkB,EAClB,aAAa,EACb,CAAC,CAAC,MAAM,CAAC;IACP,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;CACpB,CAAC,CACH,CAAA;AAED,MAAM,GAAG,GAAG,CAAC,CAAC,YAAY,CACxB,kBAAkB,EAClB,CAAC,CAAC,MAAM,CAAC;IACP,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;CACpC,CAAC,EACF,CAAC,CAAC,UAAU,CACV;IACE,EAAE;IACF,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC;CAC9B,EACD,KAAK,CACN,CACF,CAAA;AAED,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,gBAAgB;IAChB,gBAAgB,EAAE,GAAG,EAAE;QACrB,OAAO,IAAI,QAAQ,CAAC,wBAAwB,EAAE;YAC5C,MAAM,EAAE,GAAG;YACX,OAAO,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE;SACzC,CAAC,CAAA;IACJ,CAAC;CACF,CAAC;IACA,EAAE;KACD,GAAG,CAAC,GAAG,EAAE,KAAK,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE;IAChD,IAAI,CAAC;QACH,OAAO,IAAI,EAAE,CAAC;YACZ,4BAA4B;YAC5B,MAAM,IAAI,GAAG;gBACX,KAAK,EAAE,qBAAqB;gBAC5B,mBAAmB;gBACnB,OAAO;aACR,CAAA;YAED,MAAM,IAAI,CAAA;YACV,MAAM,WAAW,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAA;YACrC,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAA;QAC1D,CAAC;IACH,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAA;IACnC,CAAC;AACH,CAAC,CAAC,CAAA;AAEJ,WAAW,CAAC,MAAM,EAAE;IAClB,IAAI,EAAE,IAAI;IACV,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QACf,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,GAAG,CAAC,CAAA;IACrC,CAAC;CACF,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;IACjB,MAAM,EAAE,IAAI,EAAE,GAAG,wCAAwC,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAA;IAC5E,OAAO,CAAC,GAAG,CAAC,yCAAyC,IAAI,EAAE,CAAC,CAAA;AAC9D,CAAC,CAAC,CAAA","sourcesContent":["/* eslint-env node */\n\nimport { l } from '@atproto/lex'\nimport { LexRouter } from './lex-server.js'\nimport { startServer, upgradeWebSocket } from './nodejs.js'\n\nconst echoMessage = l.typedObject(\n 'com.example.echo',\n 'echoMessage',\n l.object({\n message: l.string(),\n }),\n)\n\nconst sub = l.subscription(\n 'com.example.echo',\n l.params({\n message: l.string({ minLength: 1 }),\n }),\n l.typedUnion(\n [\n //\n l.typedRef(() => echoMessage),\n ],\n false,\n ),\n)\n\nconst router = new LexRouter({\n upgradeWebSocket,\n onMethodNotFound: () => {\n return new Response('<h1>404 Not Found</h1>', {\n status: 404,\n headers: { 'Content-Type': 'text/html' },\n })\n },\n})\n //\n .add(sub, async function* ({ params: { message } }) {\n try {\n while (true) {\n /** @type {l.TypedObject} */\n const item = {\n $type: 'com.example.echo#ff',\n // @ts-expect-error\n message,\n }\n\n yield item\n yield echoMessage.$build({ message })\n await new Promise((resolve) => setTimeout(resolve, 500))\n }\n } finally {\n console.log('Subscription ended')\n }\n })\n\nstartServer(router, {\n port: 8080,\n onError: (err) => {\n console.error('Server error:', err)\n },\n}).then((server) => {\n const { port } = /** @type {import('net').AddressInfo} */ (server.address())\n console.log(`Server is running on http://localhost:${port}`)\n})\n"]}
|