@atcute/xrpc-server 0.1.2 → 0.1.3
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/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 +1 -0
- package/dist/main/index.d.ts.map +1 -1
- package/dist/main/index.js +1 -0
- package/dist/main/index.js.map +1 -1
- package/dist/main/router.d.ts +12 -3
- package/dist/main/router.d.ts.map +1 -1
- package/dist/main/router.js +87 -7
- 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/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 +11 -0
- 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/lib/auth/jwt.ts +16 -5
- package/lib/main/index.ts +2 -0
- package/lib/main/router.ts +131 -10
- 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/websocket-mock.ts +111 -0
- package/lib/main/xrpc-error.ts +20 -0
- package/package.json +6 -4
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
|
|
3
|
+
import type { Promisable } from '../../types/misc.js';
|
|
4
|
+
import type { XRPCRouter } from '../router.js';
|
|
5
|
+
import type { WebSocketAdapter, WebSocketConnection } from '../types/websocket.js';
|
|
6
|
+
import { EventEmitter } from './event-emitter.js';
|
|
7
|
+
|
|
8
|
+
interface WebSocketHandlerContext {
|
|
9
|
+
handler: ((ws: WebSocketConnection) => Promisable<void>) | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface SubscriptionClient extends Disposable {
|
|
13
|
+
events: EventEmitter<{
|
|
14
|
+
message: [data: Uint8Array];
|
|
15
|
+
close: [event: { code: number; reason: string; wasClean: boolean }];
|
|
16
|
+
}>;
|
|
17
|
+
dispose(): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface SubscriptionMock {
|
|
21
|
+
subscribe(url: string): Promise<SubscriptionClient>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class MockWebSocketAdapter implements WebSocketAdapter {
|
|
25
|
+
#context = new AsyncLocalStorage<WebSocketHandlerContext>();
|
|
26
|
+
|
|
27
|
+
upgrade(
|
|
28
|
+
_request: Request,
|
|
29
|
+
handler: (ws: WebSocketConnection) => Promisable<void>,
|
|
30
|
+
): Promisable<Response | undefined> {
|
|
31
|
+
const ctx = this.#context.getStore();
|
|
32
|
+
if (!ctx) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
ctx.handler = handler;
|
|
37
|
+
|
|
38
|
+
return new Response(null);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
attach(router: XRPCRouter): SubscriptionMock {
|
|
42
|
+
return {
|
|
43
|
+
subscribe: async (url) => {
|
|
44
|
+
const ctx: WebSocketHandlerContext = {
|
|
45
|
+
handler: null,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
await this.#context.run(ctx, async () => {
|
|
49
|
+
const urlp = new URL(url, 'http://localhost');
|
|
50
|
+
const request = new Request(urlp, {
|
|
51
|
+
headers: {
|
|
52
|
+
upgrade: 'websocket',
|
|
53
|
+
connection: 'upgrade',
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const response = await router.fetch(request);
|
|
58
|
+
|
|
59
|
+
return response;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (!ctx.handler) {
|
|
63
|
+
throw new Error(`WebSocket upgrade succeeded but no handler was set`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const events = new EventEmitter<{
|
|
67
|
+
message: [data: Uint8Array];
|
|
68
|
+
close: [event: { code: number; reason: string; wasClean: boolean }];
|
|
69
|
+
}>();
|
|
70
|
+
|
|
71
|
+
const controller = new AbortController();
|
|
72
|
+
const signal = controller.signal;
|
|
73
|
+
|
|
74
|
+
const connection: WebSocketConnection = {
|
|
75
|
+
signal: signal,
|
|
76
|
+
send(data) {
|
|
77
|
+
events.emit('message', data);
|
|
78
|
+
},
|
|
79
|
+
close(code = 1000, reason = '') {
|
|
80
|
+
if (!signal.aborted) {
|
|
81
|
+
events.emit('close', { code, reason, wasClean: true });
|
|
82
|
+
controller.abort();
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
{
|
|
88
|
+
const handler = ctx.handler;
|
|
89
|
+
setTimeout(() => {
|
|
90
|
+
handler(connection);
|
|
91
|
+
}, 1);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const client: SubscriptionClient = {
|
|
95
|
+
events,
|
|
96
|
+
dispose() {
|
|
97
|
+
if (!signal.aborted) {
|
|
98
|
+
events.emit('close', { code: 1000, reason: '', wasClean: true });
|
|
99
|
+
controller.abort();
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
[Symbol.dispose]() {
|
|
103
|
+
this.dispose();
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
return client;
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
package/lib/main/xrpc-error.ts
CHANGED
|
@@ -78,3 +78,23 @@ export class UpstreamTimeoutError extends XRPCError {
|
|
|
78
78
|
super({ status, error, description });
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
|
+
|
|
82
|
+
export interface XRPCSubscriptionErrorOptions {
|
|
83
|
+
closeCode?: number;
|
|
84
|
+
error: string;
|
|
85
|
+
description?: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export class XRPCSubscriptionError extends Error {
|
|
89
|
+
readonly closeCode: number;
|
|
90
|
+
readonly error: string;
|
|
91
|
+
readonly description?: string;
|
|
92
|
+
|
|
93
|
+
constructor({ closeCode = 1008, error, description }: XRPCSubscriptionErrorOptions) {
|
|
94
|
+
super(`Subscription error: ${error}${description ? ` - ${description}` : ''}`);
|
|
95
|
+
|
|
96
|
+
this.closeCode = closeCode;
|
|
97
|
+
this.error = error;
|
|
98
|
+
this.description = description;
|
|
99
|
+
}
|
|
100
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@atcute/xrpc-server",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3",
|
|
5
5
|
"description": "a small web framework for handling XRPC operations",
|
|
6
6
|
"license": "0BSD",
|
|
7
7
|
"repository": {
|
|
@@ -22,19 +22,21 @@
|
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@badrap/valita": "^0.4.6",
|
|
24
24
|
"nanoid": "^5.1.5",
|
|
25
|
+
"@atcute/cbor": "^2.2.7",
|
|
26
|
+
"@atcute/identity-resolver": "^1.1.4",
|
|
25
27
|
"@atcute/crypto": "^2.2.5",
|
|
26
28
|
"@atcute/identity": "^1.1.1",
|
|
27
29
|
"@atcute/lexicons": "^1.2.2",
|
|
28
|
-
"@atcute/identity-resolver": "^1.1.4",
|
|
29
30
|
"@atcute/multibase": "^1.1.6",
|
|
30
31
|
"@atcute/uint8array": "^1.0.5"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
34
|
"@atcute/xrpc-server": "file:",
|
|
35
|
+
"@types/node": "^24.9.1",
|
|
34
36
|
"@vitest/coverage-v8": "^3.2.4",
|
|
35
37
|
"vitest": "^3.2.4",
|
|
36
|
-
"@atcute/
|
|
37
|
-
"@atcute/
|
|
38
|
+
"@atcute/bluesky": "^3.2.8",
|
|
39
|
+
"@atcute/atproto": "^3.1.8"
|
|
38
40
|
},
|
|
39
41
|
"scripts": {
|
|
40
42
|
"build": "tsc --project tsconfig.build.json",
|