@atcute/xrpc-server 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +206 -28
- package/dist/auth/jwt-creator.d.ts.map +1 -1
- package/dist/auth/jwt-creator.js.map +1 -1
- package/dist/auth/jwt-verifier.d.ts.map +1 -1
- package/dist/auth/jwt-verifier.js.map +1 -1
- package/dist/auth/jwt.d.ts +10 -15
- package/dist/auth/jwt.d.ts.map +1 -1
- package/dist/auth/jwt.js.map +1 -1
- package/dist/main/index.d.ts +2 -0
- package/dist/main/index.d.ts.map +1 -1
- package/dist/main/index.js +2 -0
- package/dist/main/index.js.map +1 -1
- package/dist/main/response.js.map +1 -1
- package/dist/main/router.d.ts +15 -5
- package/dist/main/router.d.ts.map +1 -1
- package/dist/main/router.js +103 -16
- package/dist/main/router.js.map +1 -1
- package/dist/main/types/operation.d.ts +16 -1
- package/dist/main/types/operation.d.ts.map +1 -1
- package/dist/main/types/websocket.d.ts +10 -0
- package/dist/main/types/websocket.d.ts.map +1 -0
- package/dist/main/types/websocket.js +2 -0
- package/dist/main/types/websocket.js.map +1 -0
- package/dist/main/utils/event-emitter.d.ts +37 -0
- package/dist/main/utils/event-emitter.d.ts.map +1 -0
- package/dist/main/utils/event-emitter.js +96 -0
- package/dist/main/utils/event-emitter.js.map +1 -0
- package/dist/main/utils/frames.d.ts +5 -0
- package/dist/main/utils/frames.d.ts.map +1 -0
- package/dist/main/utils/frames.js +45 -0
- package/dist/main/utils/frames.js.map +1 -0
- package/dist/main/utils/middlewares.d.ts.map +1 -1
- package/dist/main/utils/middlewares.js.map +1 -1
- package/dist/main/utils/namespaced.d.ts +5 -0
- package/dist/main/utils/namespaced.d.ts.map +1 -0
- package/dist/main/utils/namespaced.js +4 -0
- package/dist/main/utils/namespaced.js.map +1 -0
- package/dist/main/utils/request-input.d.ts +1 -1
- package/dist/main/utils/request-input.d.ts.map +1 -1
- package/dist/main/utils/request-input.js.map +1 -1
- package/dist/main/utils/request-params.d.ts +1 -1
- package/dist/main/utils/request-params.d.ts.map +1 -1
- package/dist/main/utils/request-params.js.map +1 -1
- package/dist/main/utils/response.d.ts +1 -1
- package/dist/main/utils/response.d.ts.map +1 -1
- package/dist/main/utils/response.js.map +1 -1
- package/dist/main/utils/websocket-mock.d.ts +24 -0
- package/dist/main/utils/websocket-mock.d.ts.map +1 -0
- package/dist/main/utils/websocket-mock.js +71 -0
- package/dist/main/utils/websocket-mock.js.map +1 -0
- package/dist/main/xrpc-error.d.ts +12 -1
- package/dist/main/xrpc-error.d.ts.map +1 -1
- package/dist/main/xrpc-error.js +11 -0
- package/dist/main/xrpc-error.js.map +1 -1
- package/dist/main/xrpc-handler.d.ts +23 -0
- package/dist/main/xrpc-handler.d.ts.map +1 -0
- package/dist/main/xrpc-handler.js +19 -0
- package/dist/main/xrpc-handler.js.map +1 -0
- package/dist/middlewares/cors.d.ts.map +1 -1
- package/dist/middlewares/cors.js.map +1 -1
- package/lib/auth/jwt.ts +16 -5
- package/lib/main/index.ts +3 -0
- package/lib/main/router.ts +158 -23
- package/lib/main/types/operation.ts +33 -0
- package/lib/main/types/websocket.ts +14 -0
- package/lib/main/utils/event-emitter.ts +116 -0
- package/lib/main/utils/frames.ts +71 -0
- package/lib/main/utils/namespaced.ts +5 -0
- package/lib/main/utils/websocket-mock.ts +111 -0
- package/lib/main/xrpc-error.ts +20 -0
- package/lib/main/xrpc-handler.ts +54 -0
- package/package.json +17 -12
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Promisable } from '../../types/misc.js';
|
|
2
|
+
import type { XRPCRouter } from '../router.js';
|
|
3
|
+
import type { WebSocketAdapter, WebSocketConnection } from '../types/websocket.js';
|
|
4
|
+
import { EventEmitter } from './event-emitter.js';
|
|
5
|
+
export interface SubscriptionClient extends Disposable {
|
|
6
|
+
events: EventEmitter<{
|
|
7
|
+
message: [data: Uint8Array];
|
|
8
|
+
close: [event: {
|
|
9
|
+
code: number;
|
|
10
|
+
reason: string;
|
|
11
|
+
wasClean: boolean;
|
|
12
|
+
}];
|
|
13
|
+
}>;
|
|
14
|
+
dispose(): void;
|
|
15
|
+
}
|
|
16
|
+
export interface SubscriptionMock {
|
|
17
|
+
subscribe(url: string): Promise<SubscriptionClient>;
|
|
18
|
+
}
|
|
19
|
+
export declare class MockWebSocketAdapter implements WebSocketAdapter {
|
|
20
|
+
#private;
|
|
21
|
+
upgrade(_request: Request, handler: (ws: WebSocketConnection) => Promisable<void>): Promisable<Response | undefined>;
|
|
22
|
+
attach(router: XRPCRouter): SubscriptionMock;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=websocket-mock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"websocket-mock.d.ts","sourceRoot":"","sources":["../../../lib/main/utils/websocket-mock.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACnF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAMlD,MAAM,WAAW,kBAAmB,SAAQ,UAAU;IACrD,MAAM,EAAE,YAAY,CAAC;QACpB,OAAO,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QAC5B,KAAK,EAAE,CAAC,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,OAAO,CAAA;SAAE,CAAC,CAAC;KACpE,CAAC,CAAC;IACH,OAAO,IAAI,IAAI,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAChC,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACpD;AAED,qBAAa,oBAAqB,YAAW,gBAAgB;;IAG5D,OAAO,CACN,QAAQ,EAAE,OAAO,EACjB,OAAO,EAAE,CAAC,EAAE,EAAE,mBAAmB,KAAK,UAAU,CAAC,IAAI,CAAC,GACpD,UAAU,CAAC,QAAQ,GAAG,SAAS,CAAC,CASlC;IAED,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,gBAAgB,CAqE3C;CACD"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { EventEmitter } from './event-emitter.js';
|
|
3
|
+
export class MockWebSocketAdapter {
|
|
4
|
+
#context = new AsyncLocalStorage();
|
|
5
|
+
upgrade(_request, handler) {
|
|
6
|
+
const ctx = this.#context.getStore();
|
|
7
|
+
if (!ctx) {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
ctx.handler = handler;
|
|
11
|
+
return new Response(null);
|
|
12
|
+
}
|
|
13
|
+
attach(router) {
|
|
14
|
+
return {
|
|
15
|
+
subscribe: async (url) => {
|
|
16
|
+
const ctx = {
|
|
17
|
+
handler: null,
|
|
18
|
+
};
|
|
19
|
+
await this.#context.run(ctx, async () => {
|
|
20
|
+
const urlp = new URL(url, 'http://localhost');
|
|
21
|
+
const request = new Request(urlp, {
|
|
22
|
+
headers: {
|
|
23
|
+
upgrade: 'websocket',
|
|
24
|
+
connection: 'upgrade',
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
const response = await router.fetch(request);
|
|
28
|
+
return response;
|
|
29
|
+
});
|
|
30
|
+
if (!ctx.handler) {
|
|
31
|
+
throw new Error(`WebSocket upgrade succeeded but no handler was set`);
|
|
32
|
+
}
|
|
33
|
+
const events = new EventEmitter();
|
|
34
|
+
const controller = new AbortController();
|
|
35
|
+
const signal = controller.signal;
|
|
36
|
+
const connection = {
|
|
37
|
+
signal: signal,
|
|
38
|
+
send(data) {
|
|
39
|
+
events.emit('message', data);
|
|
40
|
+
},
|
|
41
|
+
close(code = 1000, reason = '') {
|
|
42
|
+
if (!signal.aborted) {
|
|
43
|
+
events.emit('close', { code, reason, wasClean: true });
|
|
44
|
+
controller.abort();
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
{
|
|
49
|
+
const handler = ctx.handler;
|
|
50
|
+
setTimeout(() => {
|
|
51
|
+
handler(connection);
|
|
52
|
+
}, 1);
|
|
53
|
+
}
|
|
54
|
+
const client = {
|
|
55
|
+
events,
|
|
56
|
+
dispose() {
|
|
57
|
+
if (!signal.aborted) {
|
|
58
|
+
events.emit('close', { code: 1000, reason: '', wasClean: true });
|
|
59
|
+
controller.abort();
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
[Symbol.dispose]() {
|
|
63
|
+
this.dispose();
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
return client;
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=websocket-mock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"websocket-mock.js","sourceRoot":"","sources":["../../../lib/main/utils/websocket-mock.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,7 +17,7 @@ export declare class InvalidRequestError extends XRPCError {
|
|
|
17
17
|
constructor({ status, error, description }?: Partial<XRPCErrorOptions>);
|
|
18
18
|
}
|
|
19
19
|
export declare class AuthRequiredError extends XRPCError {
|
|
20
|
-
constructor({ status, error, description
|
|
20
|
+
constructor({ status, error, description }?: Partial<XRPCErrorOptions>);
|
|
21
21
|
}
|
|
22
22
|
export declare class ForbiddenError extends XRPCError {
|
|
23
23
|
constructor({ status, error, description }?: Partial<XRPCErrorOptions>);
|
|
@@ -37,4 +37,15 @@ export declare class NotEnoughResourcesError extends XRPCError {
|
|
|
37
37
|
export declare class UpstreamTimeoutError extends XRPCError {
|
|
38
38
|
constructor({ status, error, description }?: Partial<XRPCErrorOptions>);
|
|
39
39
|
}
|
|
40
|
+
export interface XRPCSubscriptionErrorOptions {
|
|
41
|
+
closeCode?: number;
|
|
42
|
+
error: string;
|
|
43
|
+
description?: string;
|
|
44
|
+
}
|
|
45
|
+
export declare class XRPCSubscriptionError extends Error {
|
|
46
|
+
readonly closeCode: number;
|
|
47
|
+
readonly error: string;
|
|
48
|
+
readonly description?: string;
|
|
49
|
+
constructor({ closeCode, error, description }: XRPCSubscriptionErrorOptions);
|
|
50
|
+
}
|
|
40
51
|
//# sourceMappingURL=xrpc-error.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xrpc-error.d.ts","sourceRoot":"","sources":["../../lib/main/xrpc-error.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,SAAU,SAAQ,KAAK;IACnC,sBAAsB;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,iBAAiB;IACjB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,oBAAoB;IACpB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"xrpc-error.d.ts","sourceRoot":"","sources":["../../lib/main/xrpc-error.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,SAAU,SAAQ,KAAK;IACnC,sBAAsB;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,iBAAiB;IACjB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,oBAAoB;IACpB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAE9B,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,gBAAgB,EAO3D;IAED,UAAU,IAAI,QAAQ,CAErB;CACD;AAED,qBAAa,mBAAoB,SAAQ,SAAS;IACjD,YAAY,EAAE,MAAY,EAAE,KAAwB,EAAE,WAAW,EAAE,GAAE,OAAO,CAAC,gBAAgB,CAAM,EAElG;CACD;AAED,qBAAa,iBAAkB,SAAQ,SAAS;IAC/C,YAAY,EACX,MAAY,EACZ,KAAgC,EAChC,WAAW,EACX,GAAE,OAAO,CAAC,gBAAgB,CAAM,EAEhC;CACD;AAED,qBAAa,cAAe,SAAQ,SAAS;IAC5C,YAAY,EAAE,MAAY,EAAE,KAAmB,EAAE,WAAW,EAAE,GAAE,OAAO,CAAC,gBAAgB,CAAM,EAE7F;CACD;AAED,qBAAa,sBAAuB,SAAQ,SAAS;IACpD,YAAY,EAAE,MAAY,EAAE,KAA2B,EAAE,WAAW,EAAE,GAAE,OAAO,CAAC,gBAAgB,CAAM,EAErG;CACD;AAED,qBAAa,mBAAoB,SAAQ,SAAS;IACjD,YAAY,EAAE,MAAY,EAAE,KAA6B,EAAE,WAAW,EAAE,GAAE,OAAO,CAAC,gBAAgB,CAAM,EAEvG;CACD;AAED,qBAAa,oBAAqB,SAAQ,SAAS;IAClD,YAAY,EAAE,MAAY,EAAE,KAAyB,EAAE,WAAW,EAAE,GAAE,OAAO,CAAC,gBAAgB,CAAM,EAEnG;CACD;AAED,qBAAa,uBAAwB,SAAQ,SAAS;IACrD,YAAY,EAAE,MAAY,EAAE,KAA4B,EAAE,WAAW,EAAE,GAAE,OAAO,CAAC,gBAAgB,CAAM,EAEtG;CACD;AAED,qBAAa,oBAAqB,SAAQ,SAAS;IAClD,YAAY,EAAE,MAAY,EAAE,KAAyB,EAAE,WAAW,EAAE,GAAE,OAAO,CAAC,gBAAgB,CAAM,EAEnG;CACD;AAED,MAAM,WAAW,4BAA4B;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAE9B,YAAY,EAAE,SAAgB,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,4BAA4B,EAMjF;CACD"}
|
package/dist/main/xrpc-error.js
CHANGED
|
@@ -55,4 +55,15 @@ export class UpstreamTimeoutError extends XRPCError {
|
|
|
55
55
|
super({ status, error, description });
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
+
export class XRPCSubscriptionError extends Error {
|
|
59
|
+
closeCode;
|
|
60
|
+
error;
|
|
61
|
+
description;
|
|
62
|
+
constructor({ closeCode = 1008, error, description }) {
|
|
63
|
+
super(`Subscription error: ${error}${description ? ` - ${description}` : ''}`);
|
|
64
|
+
this.closeCode = closeCode;
|
|
65
|
+
this.error = error;
|
|
66
|
+
this.description = description;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
58
69
|
//# sourceMappingURL=xrpc-error.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xrpc-error.js","sourceRoot":"","sources":["../../lib/main/xrpc-error.ts"],"names":[],"mappings":"AAMA,MAAM,OAAO,SAAU,SAAQ,KAAK;IACnC,sBAAsB;IACb,MAAM,CAAS;IAExB,iBAAiB;IACR,KAAK,CAAS;IACvB,oBAAoB;IACX,WAAW,CAAU;IAE9B,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAoB;
|
|
1
|
+
{"version":3,"file":"xrpc-error.js","sourceRoot":"","sources":["../../lib/main/xrpc-error.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { XRPCProcedureMetadata, XRPCQueryMetadata } from '@atcute/lexicons/validations';
|
|
2
|
+
import { type XRPCRouterOptions } from './router.js';
|
|
3
|
+
import type { ProcedureConfig, QueryConfig } from './types/operation.js';
|
|
4
|
+
import { type Namespaced } from './utils/namespaced.js';
|
|
5
|
+
type XrpcHandlerRouterOptions = Pick<XRPCRouterOptions, 'middlewares' | 'handleNotFound' | 'handleException'>;
|
|
6
|
+
export type XrpcQueryHandlerOptions<TQuery extends XRPCQueryMetadata> = {
|
|
7
|
+
lxm: TQuery | Namespaced<TQuery>;
|
|
8
|
+
routerOptions?: XrpcHandlerRouterOptions;
|
|
9
|
+
} & QueryConfig<TQuery>;
|
|
10
|
+
export type XrpcProcedureHandlerOptions<TProcedure extends XRPCProcedureMetadata> = {
|
|
11
|
+
lxm: TProcedure | Namespaced<TProcedure>;
|
|
12
|
+
routerOptions?: XrpcHandlerRouterOptions;
|
|
13
|
+
} & ProcedureConfig<TProcedure>;
|
|
14
|
+
export type XrpcHandlerOptions = XrpcQueryHandlerOptions<XRPCQueryMetadata> | XrpcProcedureHandlerOptions<XRPCProcedureMetadata>;
|
|
15
|
+
/**
|
|
16
|
+
* create a fetch handler for a single xrpc query or procedure.
|
|
17
|
+
* requests are expected at `/xrpc/<nsid>`.
|
|
18
|
+
* subscriptions are not supported.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createXrpcHandler<TQuery extends XRPCQueryMetadata>(options: XrpcQueryHandlerOptions<TQuery>): (request: Request) => Promise<Response>;
|
|
21
|
+
export declare function createXrpcHandler<TProcedure extends XRPCProcedureMetadata>(options: XrpcProcedureHandlerOptions<TProcedure>): (request: Request) => Promise<Response>;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=xrpc-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xrpc-handler.d.ts","sourceRoot":"","sources":["../../lib/main/xrpc-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAE7F,OAAO,EAAc,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEnE,KAAK,wBAAwB,GAAG,IAAI,CAAC,iBAAiB,EAAE,aAAa,GAAG,gBAAgB,GAAG,iBAAiB,CAAC,CAAC;AAE9G,MAAM,MAAM,uBAAuB,CAAC,MAAM,SAAS,iBAAiB,IAAI;IACvE,GAAG,EAAE,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IACjC,aAAa,CAAC,EAAE,wBAAwB,CAAC;CACzC,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;AAExB,MAAM,MAAM,2BAA2B,CAAC,UAAU,SAAS,qBAAqB,IAC/E;IACC,GAAG,EAAE,UAAU,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IACzC,aAAa,CAAC,EAAE,wBAAwB,CAAC;CACzC,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;AAEjC,MAAM,MAAM,kBAAkB,GAC3B,uBAAuB,CAAC,iBAAiB,CAAC,GAC1C,2BAA2B,CAAC,qBAAqB,CAAC,CAAC;AAEtD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,SAAS,iBAAiB,EACjE,OAAO,EAAE,uBAAuB,CAAC,MAAM,CAAC,GACtC,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAC3C,wBAAgB,iBAAiB,CAAC,UAAU,SAAS,qBAAqB,EACzE,OAAO,EAAE,2BAA2B,CAAC,UAAU,CAAC,GAC9C,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { XRPCRouter } from './router.js';
|
|
2
|
+
import { unwrapLxm } from './utils/namespaced.js';
|
|
3
|
+
export function createXrpcHandler(options) {
|
|
4
|
+
const { lxm, handler, routerOptions } = options;
|
|
5
|
+
const router = new XRPCRouter(routerOptions);
|
|
6
|
+
const schema = unwrapLxm(lxm);
|
|
7
|
+
switch (schema.type) {
|
|
8
|
+
case 'xrpc_query': {
|
|
9
|
+
router.addQuery(schema, { handler: handler });
|
|
10
|
+
break;
|
|
11
|
+
}
|
|
12
|
+
case 'xrpc_procedure': {
|
|
13
|
+
router.addProcedure(schema, { handler: handler });
|
|
14
|
+
break;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return router.fetch;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=xrpc-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xrpc-handler.js","sourceRoot":"","sources":["../../lib/main/xrpc-handler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAA0B,MAAM,aAAa,CAAC;AAEjE,OAAO,EAAE,SAAS,EAAmB,MAAM,uBAAuB,CAAC;AA8BnE,MAAM,UAAU,iBAAiB,CAAC,OAA2B,EAA2C;IACvG,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC;IAEhD,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,aAAa,CAAC,CAAC;IAE7C,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAE9B,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACrB,KAAK,YAAY,EAAE,CAAC;YACnB,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,OAAoD,EAAE,CAAC,CAAC;YAC3F,MAAM;QACP,CAAC;QACD,KAAK,gBAAgB,EAAE,CAAC;YACvB,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,OAA4D,EAAE,CAAC,CAAC;YACvG,MAAM;QACP,CAAC;IACF,CAAC;IAED,OAAO,MAAM,CAAC,KAAK,CAAC;AAAA,CACpB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../lib/middlewares/cors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,MAAM,WAAW,WAAW;IAC3B,iDAAiD;IACjD,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,kCAAkC;IAClC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAsBD,eAAO,MAAM,IAAI,
|
|
1
|
+
{"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../lib/middlewares/cors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,MAAM,WAAW,WAAW;IAC3B,iDAAiD;IACjD,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,kCAAkC;IAClC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAsBD,eAAO,MAAM,IAAI,4CAsChB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cors.js","sourceRoot":"","sources":["../../lib/middlewares/cors.ts"],"names":[],"mappings":"AASA,MAAM,uBAAuB,GAAG;IAC/B,YAAY;IACZ,kBAAkB;IAElB,iBAAiB;IACjB,kBAAkB;IAClB,qBAAqB;IACrB,iBAAiB;CACjB,CAAC;AAEF,MAAM,uBAAuB,GAAG;IAC/B,cAAc;IAEd,eAAe;IACf,MAAM;IAEN,yBAAyB;IACzB,eAAe;CACf,CAAC;AAEF,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"cors.js","sourceRoot":"","sources":["../../lib/middlewares/cors.ts"],"names":[],"mappings":"AASA,MAAM,uBAAuB,GAAG;IAC/B,YAAY;IACZ,kBAAkB;IAElB,iBAAiB;IACjB,kBAAkB;IAClB,qBAAqB;IACrB,iBAAiB;CACjB,CAAC;AAEF,MAAM,uBAAuB,GAAG;IAC/B,cAAc;IAEd,eAAe;IACf,MAAM;IAEN,yBAAyB;IACzB,eAAe;CACf,CAAC;AAEF,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,OAAO,GAAgB,EAAE,EAAmB,EAAE,CAAC;IACnE,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAChC,IAAI,GAAG,CAAC,CAAC,GAAG,uBAAuB,EAAE,GAAG,CAAC,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CACrG,CAAC,IAAI,EAAE,CAAC;IAET,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAChC,IAAI,GAAG,CAAC,CAAC,GAAG,uBAAuB,EAAE,GAAG,CAAC,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CACrG;SACC,IAAI,EAAE;SACN,IAAI,CAAC,GAAG,CAAC,CAAC;IAEZ,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC;QAEpD,4BAA4B;QAC5B,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAClC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;YAC9B,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,OAAO,CAAC,CAAC;YAC/C,OAAO,CAAC,GAAG,CAAC,6BAA6B,EAAE,MAAM,CAAC,CAAC;YAEnD,IAAI,cAAc,EAAE,CAAC;gBACpB,OAAO,CAAC,GAAG,CAAC,8BAA8B,EAAE,cAAc,CAAC,CAAC;YAC7D,CAAC;YAED,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;QAC9D,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC;QAErC,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAE/E,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,6BAA6B,EAAE,MAAM,CAAC,CAAC;QAC5D,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,+BAA+B,EAAE,MAAM,CAAC,CAAC;QAClE,CAAC;QAED,OAAO,QAAQ,CAAC;IAAA,CAChB,CAAC;AAAA,CACF,CAAC"}
|
package/lib/auth/jwt.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as v from '@badrap/valita';
|
|
2
2
|
|
|
3
|
+
import type { Did, Nsid } from '@atcute/lexicons';
|
|
3
4
|
import { isDid, isNsid } from '@atcute/lexicons/syntax';
|
|
4
5
|
import { fromBase64Url } from '@atcute/multibase';
|
|
5
6
|
import { decodeUtf8From, encodeUtf8 } from '@atcute/uint8array';
|
|
@@ -13,14 +14,26 @@ const nsidString = v.string().assert(isNsid, `must be an nsid`);
|
|
|
13
14
|
|
|
14
15
|
const integer = v.number().assert((input) => input >= 0 && Number.isSafeInteger(input), `must be an integer`);
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
export interface JwtHeader {
|
|
18
|
+
typ?: string;
|
|
19
|
+
alg: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const jwtHeader: v.Type<JwtHeader> = v.object({
|
|
17
23
|
typ: v.string().optional(),
|
|
18
24
|
alg: v.string(),
|
|
19
25
|
});
|
|
20
26
|
|
|
21
|
-
export interface
|
|
27
|
+
export interface JwtPayload {
|
|
28
|
+
iss: Did;
|
|
29
|
+
aud: Did;
|
|
30
|
+
exp: number;
|
|
31
|
+
iat?: number;
|
|
32
|
+
lxm?: Nsid;
|
|
33
|
+
jti?: string;
|
|
34
|
+
}
|
|
22
35
|
|
|
23
|
-
const jwtPayload = v
|
|
36
|
+
const jwtPayload: v.Type<JwtPayload> = v
|
|
24
37
|
.object({
|
|
25
38
|
/** issuer */
|
|
26
39
|
iss: didString,
|
|
@@ -40,8 +53,6 @@ const jwtPayload = v
|
|
|
40
53
|
path: ['exp'],
|
|
41
54
|
});
|
|
42
55
|
|
|
43
|
-
export interface JwtPayload extends v.Infer<typeof jwtPayload> {}
|
|
44
|
-
|
|
45
56
|
export interface ParsedJwt {
|
|
46
57
|
header: JwtHeader;
|
|
47
58
|
payload: JwtPayload;
|
package/lib/main/index.ts
CHANGED
package/lib/main/router.ts
CHANGED
|
@@ -1,14 +1,27 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
safeParse,
|
|
3
|
+
type XRPCProcedureMetadata,
|
|
4
|
+
type XRPCQueryMetadata,
|
|
5
|
+
type XRPCSubscriptionMetadata,
|
|
6
|
+
} from '@atcute/lexicons/validations';
|
|
2
7
|
|
|
3
8
|
import type { Literal, Promisable } from '../types/misc.js';
|
|
4
9
|
|
|
5
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
ProcedureConfig,
|
|
12
|
+
QueryConfig,
|
|
13
|
+
SubscriptionConfig,
|
|
14
|
+
UnknownOperationContext,
|
|
15
|
+
UnknownSubscriptionContext,
|
|
16
|
+
} from './types/operation.js';
|
|
17
|
+
import type { WebSocketAdapter } from './types/websocket.js';
|
|
18
|
+
import { encodeErrorFrame, encodeMessageFrame, extractMessageType, omitMessageType } from './utils/frames.js';
|
|
6
19
|
import { createAsyncMiddlewareRunner, type Middleware } from './utils/middlewares.js';
|
|
20
|
+
import { unwrapLxm, type Namespaced } from './utils/namespaced.js';
|
|
7
21
|
import { constructMimeValidator } from './utils/request-input.js';
|
|
8
22
|
import { constructParamsHandler } from './utils/request-params.js';
|
|
9
23
|
import { invalidRequest, validationError } from './utils/response.js';
|
|
10
|
-
|
|
11
|
-
import { XRPCError } from './xrpc-error.js';
|
|
24
|
+
import { XRPCError, XRPCSubscriptionError } from './xrpc-error.js';
|
|
12
25
|
|
|
13
26
|
type InternalRequestContext = {
|
|
14
27
|
url: URL;
|
|
@@ -26,6 +39,7 @@ export type FetchMiddleware = Middleware<[request: Request], Promise<Response>>;
|
|
|
26
39
|
|
|
27
40
|
export type NotFoundHandler = (request: Request) => Promisable<Response>;
|
|
28
41
|
export type ExceptionHandler = (error: unknown, request: Request) => Promisable<Response>;
|
|
42
|
+
export type SubscriptionExceptionHandler = (error: unknown, request: Request) => void;
|
|
29
43
|
|
|
30
44
|
export const defaultExceptionHandler: ExceptionHandler = (error: unknown) => {
|
|
31
45
|
if (error instanceof XRPCError) {
|
|
@@ -46,16 +60,24 @@ export const defaultNotFoundHandler: NotFoundHandler = () => {
|
|
|
46
60
|
return new Response('Not Found', { status: 404 });
|
|
47
61
|
};
|
|
48
62
|
|
|
63
|
+
export const defaultSubscriptionExceptionHandler: SubscriptionExceptionHandler = (error: unknown) => {
|
|
64
|
+
throw error;
|
|
65
|
+
};
|
|
66
|
+
|
|
49
67
|
export interface XRPCRouterOptions {
|
|
50
68
|
middlewares?: FetchMiddleware[];
|
|
51
69
|
handleNotFound?: NotFoundHandler;
|
|
52
70
|
handleException?: ExceptionHandler;
|
|
71
|
+
handleSubscriptionException?: SubscriptionExceptionHandler;
|
|
72
|
+
websocket?: WebSocketAdapter;
|
|
53
73
|
}
|
|
54
74
|
|
|
55
75
|
export class XRPCRouter {
|
|
56
76
|
#handlers: Record<string, InternalRouteData> = {};
|
|
57
77
|
#handleNotFound: NotFoundHandler;
|
|
58
78
|
#handleException: ExceptionHandler;
|
|
79
|
+
#handleSubscriptionException: SubscriptionExceptionHandler;
|
|
80
|
+
#websocket?: WebSocketAdapter;
|
|
59
81
|
|
|
60
82
|
fetch: (request: Request) => Promise<Response>;
|
|
61
83
|
|
|
@@ -63,12 +85,16 @@ export class XRPCRouter {
|
|
|
63
85
|
middlewares = [],
|
|
64
86
|
handleException = defaultExceptionHandler,
|
|
65
87
|
handleNotFound = defaultNotFoundHandler,
|
|
88
|
+
handleSubscriptionException = defaultSubscriptionExceptionHandler,
|
|
89
|
+
websocket,
|
|
66
90
|
}: XRPCRouterOptions = {}) {
|
|
67
91
|
const runner = createAsyncMiddlewareRunner([...middlewares, (request) => this.#dispatch(request)]);
|
|
68
92
|
|
|
69
93
|
this.fetch = (request) => runner(request);
|
|
70
94
|
this.#handleException = handleException;
|
|
71
95
|
this.#handleNotFound = handleNotFound;
|
|
96
|
+
this.#handleSubscriptionException = handleSubscriptionException;
|
|
97
|
+
this.#websocket = websocket;
|
|
72
98
|
}
|
|
73
99
|
|
|
74
100
|
async #dispatch(request: Request): Promise<Response> {
|
|
@@ -80,8 +106,8 @@ export class XRPCRouter {
|
|
|
80
106
|
}
|
|
81
107
|
|
|
82
108
|
const nsid = pathname.slice('/xrpc/'.length);
|
|
83
|
-
const route = this.#handlers[nsid];
|
|
84
109
|
|
|
110
|
+
const route = this.#handlers[nsid];
|
|
85
111
|
if (route === undefined) {
|
|
86
112
|
return this.#handleNotFound(request);
|
|
87
113
|
}
|
|
@@ -105,28 +131,41 @@ export class XRPCRouter {
|
|
|
105
131
|
}
|
|
106
132
|
}
|
|
107
133
|
|
|
108
|
-
|
|
134
|
+
/** @deprecated use `addQuery` and `addProcedure` instead */
|
|
135
|
+
add<TQuery extends XRPCQueryMetadata>(query: TQuery | Namespaced<TQuery>, config: QueryConfig<TQuery>): void;
|
|
109
136
|
add<TProcedure extends XRPCProcedureMetadata>(
|
|
110
|
-
procedure: TProcedure
|
|
137
|
+
procedure: TProcedure | Namespaced<TProcedure>,
|
|
111
138
|
config: ProcedureConfig<TProcedure>,
|
|
112
139
|
): void;
|
|
113
|
-
add(
|
|
114
|
-
|
|
140
|
+
add(
|
|
141
|
+
operation:
|
|
142
|
+
| XRPCQueryMetadata
|
|
143
|
+
| XRPCProcedureMetadata
|
|
144
|
+
| Namespaced<XRPCQueryMetadata | XRPCProcedureMetadata>,
|
|
145
|
+
config: any,
|
|
146
|
+
): void {
|
|
147
|
+
const schema = unwrapLxm(operation);
|
|
148
|
+
|
|
149
|
+
switch (schema.type) {
|
|
115
150
|
case 'xrpc_query': {
|
|
116
|
-
return this
|
|
151
|
+
return this.addQuery(schema, config);
|
|
117
152
|
}
|
|
118
153
|
case 'xrpc_procedure': {
|
|
119
|
-
return this
|
|
154
|
+
return this.addProcedure(schema, config);
|
|
120
155
|
}
|
|
121
156
|
}
|
|
122
157
|
}
|
|
123
158
|
|
|
124
|
-
|
|
125
|
-
|
|
159
|
+
addQuery<TQuery extends XRPCQueryMetadata, TConfig extends QueryConfig<TQuery>>(
|
|
160
|
+
query: TQuery | Namespaced<TQuery>,
|
|
161
|
+
config: TConfig,
|
|
162
|
+
): void {
|
|
163
|
+
const querySchema = unwrapLxm(query);
|
|
164
|
+
const handleParams = querySchema.params ? constructParamsHandler(querySchema.params) : null;
|
|
126
165
|
|
|
127
166
|
const handler = config.handler;
|
|
128
167
|
|
|
129
|
-
this.#handlers[
|
|
168
|
+
this.#handlers[querySchema.nsid] = {
|
|
130
169
|
method: 'GET',
|
|
131
170
|
handler: async ({ request, url }) => {
|
|
132
171
|
let params: Record<string, Literal | Literal[]>;
|
|
@@ -158,19 +197,20 @@ export class XRPCRouter {
|
|
|
158
197
|
};
|
|
159
198
|
}
|
|
160
199
|
|
|
161
|
-
|
|
162
|
-
procedure: TProcedure
|
|
163
|
-
config:
|
|
200
|
+
addProcedure<TProcedure extends XRPCProcedureMetadata, TConfig extends ProcedureConfig<TProcedure>>(
|
|
201
|
+
procedure: TProcedure | Namespaced<TProcedure>,
|
|
202
|
+
config: TConfig,
|
|
164
203
|
): void {
|
|
165
|
-
const
|
|
166
|
-
const
|
|
204
|
+
const procedureSchema = unwrapLxm(procedure);
|
|
205
|
+
const handleParams = procedureSchema.params ? constructParamsHandler(procedureSchema.params) : null;
|
|
206
|
+
const validateInputType = procedureSchema.input ? constructMimeValidator(procedureSchema.input) : null;
|
|
167
207
|
|
|
168
|
-
const requiresInput =
|
|
169
|
-
const inputSchema =
|
|
208
|
+
const requiresInput = procedureSchema.input !== null;
|
|
209
|
+
const inputSchema = procedureSchema.input?.type === 'lex' ? procedureSchema.input.schema : null;
|
|
170
210
|
|
|
171
211
|
const handler = config.handler;
|
|
172
212
|
|
|
173
|
-
this.#handlers[
|
|
213
|
+
this.#handlers[procedureSchema.nsid] = {
|
|
174
214
|
method: 'POST',
|
|
175
215
|
handler: async ({ request, url }) => {
|
|
176
216
|
let params: Record<string, Literal | Literal[]>;
|
|
@@ -203,7 +243,7 @@ export class XRPCRouter {
|
|
|
203
243
|
let raw: any;
|
|
204
244
|
try {
|
|
205
245
|
raw = await request.json();
|
|
206
|
-
} catch
|
|
246
|
+
} catch {
|
|
207
247
|
return invalidRequest(`invalid request body (failed to parse json)`);
|
|
208
248
|
}
|
|
209
249
|
|
|
@@ -236,4 +276,99 @@ export class XRPCRouter {
|
|
|
236
276
|
},
|
|
237
277
|
};
|
|
238
278
|
}
|
|
279
|
+
|
|
280
|
+
addSubscription<
|
|
281
|
+
TSubscription extends XRPCSubscriptionMetadata,
|
|
282
|
+
TConfig extends SubscriptionConfig<TSubscription>,
|
|
283
|
+
>(subscription: TSubscription | Namespaced<TSubscription>, config: TConfig): void {
|
|
284
|
+
const websocket = this.#websocket;
|
|
285
|
+
if (websocket === undefined) {
|
|
286
|
+
throw new Error(`WebSocket adapter not configured`);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const subscriptionSchema = unwrapLxm(subscription);
|
|
290
|
+
const nsid = subscriptionSchema.nsid;
|
|
291
|
+
|
|
292
|
+
const handleParams = subscriptionSchema.params
|
|
293
|
+
? constructParamsHandler(subscriptionSchema.params)
|
|
294
|
+
: null;
|
|
295
|
+
const handler = config.handler;
|
|
296
|
+
|
|
297
|
+
this.#handlers[nsid] = {
|
|
298
|
+
method: 'GET',
|
|
299
|
+
handler: async ({ request, url }) => {
|
|
300
|
+
{
|
|
301
|
+
const con = request.headers.get('connection');
|
|
302
|
+
const req = request.headers.get('upgrade');
|
|
303
|
+
if (
|
|
304
|
+
con === null ||
|
|
305
|
+
req === null ||
|
|
306
|
+
con.toLowerCase() !== 'upgrade' ||
|
|
307
|
+
req.toLowerCase() !== 'websocket'
|
|
308
|
+
) {
|
|
309
|
+
return invalidRequest(`invalid WebSocket upgrade`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
let params: Record<string, Literal | Literal[]>;
|
|
314
|
+
|
|
315
|
+
if (handleParams !== null) {
|
|
316
|
+
const result = handleParams(url.searchParams);
|
|
317
|
+
if (!result.ok) {
|
|
318
|
+
return validationError('params', result);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
params = result.value;
|
|
322
|
+
} else {
|
|
323
|
+
params = {};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const upgrade = await websocket.upgrade(request, async (ws) => {
|
|
327
|
+
const signal = ws.signal;
|
|
328
|
+
|
|
329
|
+
const context: UnknownSubscriptionContext = {
|
|
330
|
+
request: request,
|
|
331
|
+
params: params,
|
|
332
|
+
signal: signal,
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
for await (const message of handler(context)) {
|
|
337
|
+
if (signal.aborted) {
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const type = extractMessageType(message, nsid);
|
|
342
|
+
const body = omitMessageType(message);
|
|
343
|
+
|
|
344
|
+
const frame = encodeMessageFrame(body, type);
|
|
345
|
+
await ws.send(frame);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
ws.close(1000);
|
|
349
|
+
} catch (err) {
|
|
350
|
+
if (err instanceof XRPCSubscriptionError) {
|
|
351
|
+
const frame = encodeErrorFrame(err.error, err.description);
|
|
352
|
+
|
|
353
|
+
try {
|
|
354
|
+
await ws.send(frame);
|
|
355
|
+
} catch {}
|
|
356
|
+
|
|
357
|
+
ws.close(err.closeCode, err.error);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
ws.close(1011, `internal server error`);
|
|
362
|
+
this.#handleSubscriptionException(err, request);
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
if (upgrade !== undefined) {
|
|
367
|
+
return upgrade;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return invalidRequest(`WebSocket upgrade failed`);
|
|
371
|
+
},
|
|
372
|
+
};
|
|
373
|
+
}
|
|
239
374
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
InferOutput,
|
|
3
3
|
ObjectSchema,
|
|
4
|
+
VariantSchema,
|
|
4
5
|
XRPCBlobBodyParam,
|
|
5
6
|
XRPCLexBodyParam,
|
|
6
7
|
XRPCProcedureMetadata,
|
|
7
8
|
XRPCQueryMetadata,
|
|
9
|
+
XRPCSubscriptionMetadata,
|
|
8
10
|
} from '@atcute/lexicons/validations';
|
|
9
11
|
|
|
10
12
|
import type { Literal, Promisable } from '../../types/misc.js';
|
|
@@ -85,3 +87,34 @@ export type ProcedureHandler<TProcedure extends XRPCProcedureMetadata> = (
|
|
|
85
87
|
export type ProcedureConfig<TProcedure extends XRPCProcedureMetadata = XRPCProcedureMetadata> = {
|
|
86
88
|
handler: ProcedureHandler<TProcedure>;
|
|
87
89
|
};
|
|
90
|
+
|
|
91
|
+
// #region Subscription
|
|
92
|
+
|
|
93
|
+
export interface UnknownSubscriptionContext {
|
|
94
|
+
request: Request;
|
|
95
|
+
signal: AbortSignal;
|
|
96
|
+
params: Record<string, Literal | Literal[]>;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export type SubscriptionContext<TSubscription extends XRPCSubscriptionMetadata> = {
|
|
100
|
+
request: Request;
|
|
101
|
+
signal: AbortSignal;
|
|
102
|
+
} & (TSubscription['params'] extends ObjectSchema
|
|
103
|
+
? {
|
|
104
|
+
params: InferOutput<TSubscription['params']>;
|
|
105
|
+
}
|
|
106
|
+
: {
|
|
107
|
+
// params
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
export type SubscriptionHandler<TSubscription extends XRPCSubscriptionMetadata> = (
|
|
111
|
+
context: SubscriptionContext<TSubscription>,
|
|
112
|
+
) => AsyncIterable<
|
|
113
|
+
TSubscription['message'] extends ObjectSchema | VariantSchema<any>
|
|
114
|
+
? InferOutput<TSubscription['message']>
|
|
115
|
+
: never
|
|
116
|
+
>;
|
|
117
|
+
|
|
118
|
+
export type SubscriptionConfig<TSubscription extends XRPCSubscriptionMetadata = XRPCSubscriptionMetadata> = {
|
|
119
|
+
handler: SubscriptionHandler<TSubscription>;
|
|
120
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Promisable } from '../../types/misc.js';
|
|
2
|
+
|
|
3
|
+
export interface WebSocketConnection {
|
|
4
|
+
signal: AbortSignal;
|
|
5
|
+
send(data: Uint8Array): void | Promise<void>;
|
|
6
|
+
close(code?: number, reason?: string): void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface WebSocketAdapter {
|
|
10
|
+
upgrade(
|
|
11
|
+
request: Request,
|
|
12
|
+
handler: (ws: WebSocketConnection) => Promisable<void>,
|
|
13
|
+
): Promisable<Response | undefined>;
|
|
14
|
+
}
|