@mt-tl/server 0.1.0
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/LICENSE +21 -0
- package/README.md +50 -0
- package/dist/auth/handshake.d.ts +35 -0
- package/dist/auth/handshake.d.ts.map +1 -0
- package/dist/auth/handshake.js +208 -0
- package/dist/auth/handshake.js.map +1 -0
- package/dist/auth/nonce-store.d.ts +22 -0
- package/dist/auth/nonce-store.d.ts.map +1 -0
- package/dist/auth/nonce-store.js +23 -0
- package/dist/auth/nonce-store.js.map +1 -0
- package/dist/bootstrap.d.ts +36 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +82 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/config.d.ts +103 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -0
- package/dist/core/context.d.ts +57 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +27 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/errors.d.ts +33 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +47 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +5 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/rpc.d.ts +83 -0
- package/dist/core/rpc.d.ts.map +1 -0
- package/dist/core/rpc.js +102 -0
- package/dist/core/rpc.js.map +1 -0
- package/dist/core/updates.d.ts +56 -0
- package/dist/core/updates.d.ts.map +1 -0
- package/dist/core/updates.js +34 -0
- package/dist/core/updates.js.map +1 -0
- package/dist/create-server.d.ts +89 -0
- package/dist/create-server.d.ts.map +1 -0
- package/dist/create-server.js +109 -0
- package/dist/create-server.js.map +1 -0
- package/dist/crypto/aes-ige.d.ts +3 -0
- package/dist/crypto/aes-ige.d.ts.map +1 -0
- package/dist/crypto/aes-ige.js +55 -0
- package/dist/crypto/aes-ige.js.map +1 -0
- package/dist/crypto/dh.d.ts +21 -0
- package/dist/crypto/dh.d.ts.map +1 -0
- package/dist/crypto/dh.js +99 -0
- package/dist/crypto/dh.js.map +1 -0
- package/dist/crypto/hashes.d.ts +6 -0
- package/dist/crypto/hashes.d.ts.map +1 -0
- package/dist/crypto/hashes.js +14 -0
- package/dist/crypto/hashes.js.map +1 -0
- package/dist/crypto/msg-key.d.ts +15 -0
- package/dist/crypto/msg-key.d.ts.map +1 -0
- package/dist/crypto/msg-key.js +24 -0
- package/dist/crypto/msg-key.js.map +1 -0
- package/dist/crypto/rsa.d.ts +27 -0
- package/dist/crypto/rsa.d.ts.map +1 -0
- package/dist/crypto/rsa.js +50 -0
- package/dist/crypto/rsa.js.map +1 -0
- package/dist/dispatch/dispatcher.d.ts +72 -0
- package/dist/dispatch/dispatcher.d.ts.map +1 -0
- package/dist/dispatch/dispatcher.js +503 -0
- package/dist/dispatch/dispatcher.js.map +1 -0
- package/dist/dispatch/forwarders/in-process.d.ts +12 -0
- package/dist/dispatch/forwarders/in-process.d.ts.map +1 -0
- package/dist/dispatch/forwarders/in-process.js +15 -0
- package/dist/dispatch/forwarders/in-process.js.map +1 -0
- package/dist/dispatch/forwarders/print.d.ts +14 -0
- package/dist/dispatch/forwarders/print.d.ts.map +1 -0
- package/dist/dispatch/forwarders/print.js +23 -0
- package/dist/dispatch/forwarders/print.js.map +1 -0
- package/dist/dispatch/rpc-forwarder.d.ts +14 -0
- package/dist/dispatch/rpc-forwarder.d.ts.map +1 -0
- package/dist/dispatch/rpc-forwarder.js +2 -0
- package/dist/dispatch/rpc-forwarder.js.map +1 -0
- package/dist/dispatch/types.d.ts +32 -0
- package/dist/dispatch/types.d.ts.map +1 -0
- package/dist/dispatch/types.js +23 -0
- package/dist/dispatch/types.js.map +1 -0
- package/dist/gateway.d.ts +57 -0
- package/dist/gateway.d.ts.map +1 -0
- package/dist/gateway.js +150 -0
- package/dist/gateway.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/lib.d.ts +12 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +13 -0
- package/dist/lib.js.map +1 -0
- package/dist/server/message-pipeline.d.ts +57 -0
- package/dist/server/message-pipeline.d.ts.map +1 -0
- package/dist/server/message-pipeline.js +199 -0
- package/dist/server/message-pipeline.js.map +1 -0
- package/dist/session/inbound-tracker.d.ts +118 -0
- package/dist/session/inbound-tracker.d.ts.map +1 -0
- package/dist/session/inbound-tracker.js +170 -0
- package/dist/session/inbound-tracker.js.map +1 -0
- package/dist/session/message-id.d.ts +19 -0
- package/dist/session/message-id.d.ts.map +1 -0
- package/dist/session/message-id.js +30 -0
- package/dist/session/message-id.js.map +1 -0
- package/dist/session/salts.d.ts +51 -0
- package/dist/session/salts.d.ts.map +1 -0
- package/dist/session/salts.js +132 -0
- package/dist/session/salts.js.map +1 -0
- package/dist/session/session-manager.d.ts +18 -0
- package/dist/session/session-manager.d.ts.map +1 -0
- package/dist/session/session-manager.js +43 -0
- package/dist/session/session-manager.js.map +1 -0
- package/dist/storage/index.d.ts +14 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +16 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/memory.d.ts +3 -0
- package/dist/storage/memory.d.ts.map +1 -0
- package/dist/storage/memory.js +91 -0
- package/dist/storage/memory.js.map +1 -0
- package/dist/storage/mongo.d.ts +3 -0
- package/dist/storage/mongo.d.ts.map +1 -0
- package/dist/storage/mongo.js +175 -0
- package/dist/storage/mongo.js.map +1 -0
- package/dist/storage/types.d.ts +85 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +3 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/testkit.d.ts +11 -0
- package/dist/testkit.d.ts.map +1 -0
- package/dist/testkit.js +17 -0
- package/dist/testkit.js.map +1 -0
- package/dist/tl/codec.d.ts +37 -0
- package/dist/tl/codec.d.ts.map +1 -0
- package/dist/tl/codec.js +297 -0
- package/dist/tl/codec.js.map +1 -0
- package/dist/tl/layered-registry.d.ts +29 -0
- package/dist/tl/layered-registry.d.ts.map +1 -0
- package/dist/tl/layered-registry.js +118 -0
- package/dist/tl/layered-registry.js.map +1 -0
- package/dist/tl/protocol.d.ts +126 -0
- package/dist/tl/protocol.d.ts.map +1 -0
- package/dist/tl/protocol.js +22 -0
- package/dist/tl/protocol.js.map +1 -0
- package/dist/tl/reader.d.ts +30 -0
- package/dist/tl/reader.d.ts.map +1 -0
- package/dist/tl/reader.js +87 -0
- package/dist/tl/reader.js.map +1 -0
- package/dist/tl/registry.d.ts +39 -0
- package/dist/tl/registry.d.ts.map +1 -0
- package/dist/tl/registry.js +52 -0
- package/dist/tl/registry.js.map +1 -0
- package/dist/tl/writer.d.ts +24 -0
- package/dist/tl/writer.d.ts.map +1 -0
- package/dist/tl/writer.js +95 -0
- package/dist/tl/writer.js.map +1 -0
- package/dist/transport/connection-registry.d.ts +31 -0
- package/dist/transport/connection-registry.d.ts.map +1 -0
- package/dist/transport/connection-registry.js +84 -0
- package/dist/transport/connection-registry.js.map +1 -0
- package/dist/transport/connection.d.ts +62 -0
- package/dist/transport/connection.d.ts.map +1 -0
- package/dist/transport/connection.js +77 -0
- package/dist/transport/connection.js.map +1 -0
- package/dist/transport/framing.d.ts +39 -0
- package/dist/transport/framing.d.ts.map +1 -0
- package/dist/transport/framing.js +212 -0
- package/dist/transport/framing.js.map +1 -0
- package/dist/transport/proxy-protocol.d.ts +23 -0
- package/dist/transport/proxy-protocol.d.ts.map +1 -0
- package/dist/transport/proxy-protocol.js +108 -0
- package/dist/transport/proxy-protocol.js.map +1 -0
- package/dist/transport/server-common.d.ts +27 -0
- package/dist/transport/server-common.d.ts.map +1 -0
- package/dist/transport/server-common.js +27 -0
- package/dist/transport/server-common.js.map +1 -0
- package/dist/transport/tcp-server.d.ts +26 -0
- package/dist/transport/tcp-server.d.ts.map +1 -0
- package/dist/transport/tcp-server.js +91 -0
- package/dist/transport/tcp-server.js.map +1 -0
- package/dist/transport/ws-server.d.ts +19 -0
- package/dist/transport/ws-server.d.ts.map +1 -0
- package/dist/transport/ws-server.js +78 -0
- package/dist/transport/ws-server.js.map +1 -0
- package/dist/update-publisher.d.ts +34 -0
- package/dist/update-publisher.d.ts.map +1 -0
- package/dist/update-publisher.js +29 -0
- package/dist/update-publisher.js.map +1 -0
- package/dist/updates/mongo-update-log.d.ts +13 -0
- package/dist/updates/mongo-update-log.d.ts.map +1 -0
- package/dist/updates/mongo-update-log.js +39 -0
- package/dist/updates/mongo-update-log.js.map +1 -0
- package/dist/updates/presence-binder.d.ts +29 -0
- package/dist/updates/presence-binder.d.ts.map +1 -0
- package/dist/updates/presence-binder.js +36 -0
- package/dist/updates/presence-binder.js.map +1 -0
- package/dist/updates/presence.d.ts +31 -0
- package/dist/updates/presence.d.ts.map +1 -0
- package/dist/updates/presence.js +44 -0
- package/dist/updates/presence.js.map +1 -0
- package/dist/updates/push.d.ts +25 -0
- package/dist/updates/push.d.ts.map +1 -0
- package/dist/updates/push.js +72 -0
- package/dist/updates/push.js.map +1 -0
- package/dist/updates/redis-bus.d.ts +45 -0
- package/dist/updates/redis-bus.d.ts.map +1 -0
- package/dist/updates/redis-bus.js +59 -0
- package/dist/updates/redis-bus.js.map +1 -0
- package/dist/updates/redis-presence.d.ts +43 -0
- package/dist/updates/redis-presence.d.ts.map +1 -0
- package/dist/updates/redis-presence.js +65 -0
- package/dist/updates/redis-presence.js.map +1 -0
- package/dist/updates/render.d.ts +16 -0
- package/dist/updates/render.d.ts.map +1 -0
- package/dist/updates/render.js +46 -0
- package/dist/updates/render.js.map +1 -0
- package/dist/updates/router.d.ts +27 -0
- package/dist/updates/router.d.ts.map +1 -0
- package/dist/updates/router.js +36 -0
- package/dist/updates/router.js.map +1 -0
- package/dist/updates/types.d.ts +23 -0
- package/dist/updates/types.d.ts.map +1 -0
- package/dist/updates/types.js +2 -0
- package/dist/updates/types.js.map +1 -0
- package/dist/updates/update-bus.d.ts +29 -0
- package/dist/updates/update-bus.d.ts.map +1 -0
- package/dist/updates/update-bus.js +24 -0
- package/dist/updates/update-bus.js.map +1 -0
- package/dist/util/bytes.d.ts +12 -0
- package/dist/util/bytes.d.ts.map +1 -0
- package/dist/util/bytes.js +46 -0
- package/dist/util/bytes.js.map +1 -0
- package/package.json +84 -0
- package/src/auth/handshake.ts +262 -0
- package/src/auth/nonce-store.ts +39 -0
- package/src/bootstrap.ts +114 -0
- package/src/config.ts +103 -0
- package/src/core/context.ts +94 -0
- package/src/core/errors.ts +52 -0
- package/src/core/index.ts +4 -0
- package/src/core/rpc.ts +165 -0
- package/src/core/updates.ts +69 -0
- package/src/create-server.ts +181 -0
- package/src/crypto/aes-ige.ts +57 -0
- package/src/crypto/dh.ts +101 -0
- package/src/crypto/hashes.ts +17 -0
- package/src/crypto/msg-key.ts +29 -0
- package/src/crypto/rsa.ts +70 -0
- package/src/dispatch/dispatcher.ts +586 -0
- package/src/dispatch/forwarders/in-process.ts +14 -0
- package/src/dispatch/forwarders/print.ts +22 -0
- package/src/dispatch/rpc-forwarder.ts +15 -0
- package/src/dispatch/types.ts +60 -0
- package/src/gateway.ts +214 -0
- package/src/index.ts +53 -0
- package/src/lib.ts +24 -0
- package/src/server/message-pipeline.ts +256 -0
- package/src/session/inbound-tracker.ts +221 -0
- package/src/session/message-id.ts +43 -0
- package/src/session/salts.ts +162 -0
- package/src/session/session-manager.ts +66 -0
- package/src/storage/index.ts +26 -0
- package/src/storage/memory.ts +101 -0
- package/src/storage/mongo.ts +215 -0
- package/src/storage/types.ts +92 -0
- package/src/testkit.ts +19 -0
- package/src/tl/codec.ts +292 -0
- package/src/tl/layered-registry.ts +132 -0
- package/src/tl/protocol.ts +146 -0
- package/src/tl/reader.ts +99 -0
- package/src/tl/registry.ts +78 -0
- package/src/tl/writer.ts +104 -0
- package/src/transport/connection-registry.ts +91 -0
- package/src/transport/connection.ts +113 -0
- package/src/transport/framing.ts +223 -0
- package/src/transport/proxy-protocol.ts +109 -0
- package/src/transport/server-common.ts +49 -0
- package/src/transport/tcp-server.ts +102 -0
- package/src/transport/ws-server.ts +94 -0
- package/src/update-publisher.ts +47 -0
- package/src/updates/mongo-update-log.ts +49 -0
- package/src/updates/presence-binder.ts +51 -0
- package/src/updates/presence.ts +61 -0
- package/src/updates/push.ts +90 -0
- package/src/updates/redis-bus.ts +86 -0
- package/src/updates/redis-presence.ts +87 -0
- package/src/updates/render.ts +53 -0
- package/src/updates/router.ts +52 -0
- package/src/updates/types.ts +24 -0
- package/src/updates/update-bus.ts +49 -0
- package/src/util/bytes.ts +49 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type Logger, type RpcContext, type SessionEffect } from '@mt-tl/tl';
|
|
2
|
+
import type { UpdateEmitter } from './updates.js';
|
|
3
|
+
/**
|
|
4
|
+
* Handler-facing context: the request data plus session effects, server-push,
|
|
5
|
+
* and a per-request value bag. Business dependencies are NOT here — a handler
|
|
6
|
+
* (or a plugin) closes over its service (Style A DI), so `ctx` carries only
|
|
7
|
+
* request-scoped + cross-cutting concerns. Handlers stay thin: call the service,
|
|
8
|
+
* shape the result, optionally `login()`/`push()`.
|
|
9
|
+
*/
|
|
10
|
+
export interface HandlerCtx {
|
|
11
|
+
/** Raw request context forwarded by the gateway (sessionId, authKeyId, ip, …). */
|
|
12
|
+
readonly request: RpcContext;
|
|
13
|
+
/** The bound **subject** — your app's internal user id (opaque string, e.g. a
|
|
14
|
+
* uuid), shareable across your services. `undefined` if the auth key is
|
|
15
|
+
* anonymous; on an `auth: true` method it is guaranteed present, so
|
|
16
|
+
* `ctx.subject!` is safe there. Distinct from any wire `user_id:int` your TL
|
|
17
|
+
* schema exposes — map between them in your app (see the demo `users` module). */
|
|
18
|
+
readonly subject: string | undefined;
|
|
19
|
+
/** The TL layer this request came in on (the client's negotiated layer).
|
|
20
|
+
* Read-only — layer negotiation is the protocol's job. Branch on it when an
|
|
21
|
+
* old client needs a different response shape. */
|
|
22
|
+
readonly layer: number;
|
|
23
|
+
/** Per-request logger (a child bound with method/subject) — log with the same
|
|
24
|
+
* style as the framework so app and engine lines interleave coherently. */
|
|
25
|
+
readonly log: Logger;
|
|
26
|
+
/** Low-level update emitter behind {@link push}; prefer `ctx.push(subject, update)`. */
|
|
27
|
+
readonly updates: UpdateEmitter;
|
|
28
|
+
/** Collected session effects (applied by the gateway). */
|
|
29
|
+
readonly effects: readonly SessionEffect[];
|
|
30
|
+
/** Bind the auth key to a `subject` — your internal user id (device login). */
|
|
31
|
+
login(subject: string): void;
|
|
32
|
+
/** Unbind the auth key (logout). */
|
|
33
|
+
logout(): void;
|
|
34
|
+
/** Revoke the auth key entirely. */
|
|
35
|
+
revoke(): void;
|
|
36
|
+
/** Push a TL update to a `subject` (delivered via the update bus to whatever node holds them). */
|
|
37
|
+
push(subject: string, update: unknown): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Push to a specific auth key — including an anonymous (not-logged-in)
|
|
40
|
+
* connection, e.g. to deliver API to a client before it registers. Pass
|
|
41
|
+
* `ctx.request.authKeyId` for the current connection. No pts (anonymous
|
|
42
|
+
* connections have no durable update state).
|
|
43
|
+
*/
|
|
44
|
+
pushToAuthKey(authKeyId: string, update: unknown): Promise<void>;
|
|
45
|
+
/** Stash a value for the handler (e.g. data a pre-handler already fetched). */
|
|
46
|
+
set(key: string, value: unknown): void;
|
|
47
|
+
/** Read a value stashed earlier in this request. */
|
|
48
|
+
get<T = unknown>(key: string): T | undefined;
|
|
49
|
+
/** @deprecated use {@link login} */
|
|
50
|
+
bindUser(subject: string): void;
|
|
51
|
+
/** @deprecated use {@link logout} */
|
|
52
|
+
unbindUser(): void;
|
|
53
|
+
/** @deprecated use {@link revoke} */
|
|
54
|
+
revokeKey(): void;
|
|
55
|
+
}
|
|
56
|
+
export declare function createHandlerCtx(request: RpcContext, updates: UpdateEmitter, log?: Logger): HandlerCtx;
|
|
57
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,MAAM,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,WAAW,CAAA;AACxF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAEjD;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACvB,kFAAkF;IAClF,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAA;IAC5B;;;;sFAIkF;IAClF,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IACpC;;sDAEkD;IAClD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB;+EAC2E;IAC3E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,wFAAwF;IACxF,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAA;IAC/B,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAA;IAG1C,+EAA+E;IAC/E,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,oCAAoC;IACpC,MAAM,IAAI,IAAI,CAAA;IACd,oCAAoC;IACpC,MAAM,IAAI,IAAI,CAAA;IAGd,kGAAkG;IAClG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACrD;;;;;OAKG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAGhE,+EAA+E;IAC/E,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;IACtC,oDAAoD;IACpD,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAA;IAG5C,oCAAoC;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,qCAAqC;IACrC,UAAU,IAAI,IAAI,CAAA;IAClB,qCAAqC;IACrC,SAAS,IAAI,IAAI,CAAA;CACpB;AAED,wBAAgB,gBAAgB,CAC5B,OAAO,EAAE,UAAU,EACnB,OAAO,EAAE,aAAa,EACtB,GAAG,GAAE,MAAmB,GACzB,UAAU,CAwBZ"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { noopLogger } from '@mt-tl/tl';
|
|
2
|
+
export function createHandlerCtx(request, updates, log = noopLogger) {
|
|
3
|
+
const effects = [];
|
|
4
|
+
const bag = new Map();
|
|
5
|
+
const login = (subject) => void effects.push({ type: 'bindUser', subject });
|
|
6
|
+
const logout = () => void effects.push({ type: 'unbindUser' });
|
|
7
|
+
const revoke = () => void effects.push({ type: 'revokeKey' });
|
|
8
|
+
return {
|
|
9
|
+
request,
|
|
10
|
+
subject: request.subject,
|
|
11
|
+
layer: request.apiLayer,
|
|
12
|
+
log,
|
|
13
|
+
updates,
|
|
14
|
+
effects,
|
|
15
|
+
login,
|
|
16
|
+
logout,
|
|
17
|
+
revoke,
|
|
18
|
+
push: (subject, update) => updates.emit(subject, update),
|
|
19
|
+
pushToAuthKey: (authKeyId, update) => updates.emitToAuthKey(authKeyId, update),
|
|
20
|
+
set: (key, value) => void bag.set(key, value),
|
|
21
|
+
get: (key) => bag.get(key),
|
|
22
|
+
bindUser: login,
|
|
23
|
+
unbindUser: logout,
|
|
24
|
+
revokeKey: revoke,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAoD,MAAM,WAAW,CAAA;AAiExF,MAAM,UAAU,gBAAgB,CAC5B,OAAmB,EACnB,OAAsB,EACtB,MAAc,UAAU;IAExB,MAAM,OAAO,GAAoB,EAAE,CAAA;IACnC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAmB,CAAA;IACtC,MAAM,KAAK,GAAG,CAAC,OAAe,EAAE,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAA;IACnF,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAA;IAC9D,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAA;IAC7D,OAAO;QACH,OAAO;QACP,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,KAAK,EAAE,OAAO,CAAC,QAAQ;QACvB,GAAG;QACH,OAAO;QACP,OAAO;QACP,KAAK;QACL,MAAM;QACN,MAAM;QACN,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,MAAe,CAAC;QACjE,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,SAAS,EAAE,MAAe,CAAC;QACvF,GAAG,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC;QAC7C,GAAG,EAAE,CAAI,GAAW,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAkB;QACtD,QAAQ,EAAE,KAAK;QACf,UAAU,EAAE,MAAM;QAClB,SAAS,EAAE,MAAM;KACpB,CAAA;AACL,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Business errors. `code`/`message` map straight to the gateway's `rpc_error`
|
|
3
|
+
* (Telegram-style: 400/401/404/420/500…). Throw these from handlers; anything
|
|
4
|
+
* else becomes a 500 INTERNAL.
|
|
5
|
+
*/
|
|
6
|
+
export declare class AppError extends Error {
|
|
7
|
+
readonly code: number;
|
|
8
|
+
constructor(code: number, message: string);
|
|
9
|
+
}
|
|
10
|
+
/** Invalid input or a violated precondition → `rpc_error 400`. */
|
|
11
|
+
export declare class BadRequestError extends AppError {
|
|
12
|
+
constructor(message?: string);
|
|
13
|
+
}
|
|
14
|
+
/** Method requires an authorized user but the auth key is anonymous → `rpc_error 401`. */
|
|
15
|
+
export declare class AuthRequiredError extends AppError {
|
|
16
|
+
constructor(message?: string);
|
|
17
|
+
}
|
|
18
|
+
/** The requested entity does not exist → `rpc_error 404`. */
|
|
19
|
+
export declare class NotFoundError extends AppError {
|
|
20
|
+
constructor(message?: string);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Rate-limited → `rpc_error 420 FLOOD_WAIT_<seconds>`. Clients read the number
|
|
24
|
+
* off the message and retry after that many seconds.
|
|
25
|
+
*/
|
|
26
|
+
export declare class FloodWaitError extends AppError {
|
|
27
|
+
constructor(seconds: number);
|
|
28
|
+
}
|
|
29
|
+
/** An unexpected server-side failure → `rpc_error 500`. */
|
|
30
|
+
export declare class InternalError extends AppError {
|
|
31
|
+
constructor(message?: string);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAE3B,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM;CAKtB;AAED,kEAAkE;AAClE,qBAAa,eAAgB,SAAQ,QAAQ;gBAC7B,OAAO,SAAgB;CAGtC;AAED,0FAA0F;AAC1F,qBAAa,iBAAkB,SAAQ,QAAQ;gBAC/B,OAAO,SAA0B;CAGhD;AAED,6DAA6D;AAC7D,qBAAa,aAAc,SAAQ,QAAQ;gBAC3B,OAAO,SAAc;CAGpC;AAED;;;GAGG;AACH,qBAAa,cAAe,SAAQ,QAAQ;gBAC5B,OAAO,EAAE,MAAM;CAG9B;AAED,2DAA2D;AAC3D,qBAAa,aAAc,SAAQ,QAAQ;gBAC3B,OAAO,SAAa;CAGnC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Business errors. `code`/`message` map straight to the gateway's `rpc_error`
|
|
3
|
+
* (Telegram-style: 400/401/404/420/500…). Throw these from handlers; anything
|
|
4
|
+
* else becomes a 500 INTERNAL.
|
|
5
|
+
*/
|
|
6
|
+
export class AppError extends Error {
|
|
7
|
+
code;
|
|
8
|
+
constructor(code, message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.name = new.target.name;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** Invalid input or a violated precondition → `rpc_error 400`. */
|
|
15
|
+
export class BadRequestError extends AppError {
|
|
16
|
+
constructor(message = 'BAD_REQUEST') {
|
|
17
|
+
super(400, message);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** Method requires an authorized user but the auth key is anonymous → `rpc_error 401`. */
|
|
21
|
+
export class AuthRequiredError extends AppError {
|
|
22
|
+
constructor(message = 'AUTH_KEY_UNREGISTERED') {
|
|
23
|
+
super(401, message);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** The requested entity does not exist → `rpc_error 404`. */
|
|
27
|
+
export class NotFoundError extends AppError {
|
|
28
|
+
constructor(message = 'NOT_FOUND') {
|
|
29
|
+
super(404, message);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Rate-limited → `rpc_error 420 FLOOD_WAIT_<seconds>`. Clients read the number
|
|
34
|
+
* off the message and retry after that many seconds.
|
|
35
|
+
*/
|
|
36
|
+
export class FloodWaitError extends AppError {
|
|
37
|
+
constructor(seconds) {
|
|
38
|
+
super(420, `FLOOD_WAIT_${seconds}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** An unexpected server-side failure → `rpc_error 500`. */
|
|
42
|
+
export class InternalError extends AppError {
|
|
43
|
+
constructor(message = 'INTERNAL') {
|
|
44
|
+
super(500, message);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IAElB;IADb,YACa,IAAY,EACrB,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAA;QAHL,SAAI,GAAJ,IAAI,CAAQ;QAIrB,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAA;IAC/B,CAAC;CACJ;AAED,kEAAkE;AAClE,MAAM,OAAO,eAAgB,SAAQ,QAAQ;IACzC,YAAY,OAAO,GAAG,aAAa;QAC/B,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IACvB,CAAC;CACJ;AAED,0FAA0F;AAC1F,MAAM,OAAO,iBAAkB,SAAQ,QAAQ;IAC3C,YAAY,OAAO,GAAG,uBAAuB;QACzC,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IACvB,CAAC;CACJ;AAED,6DAA6D;AAC7D,MAAM,OAAO,aAAc,SAAQ,QAAQ;IACvC,YAAY,OAAO,GAAG,WAAW;QAC7B,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IACvB,CAAC;CACJ;AAED;;;GAGG;AACH,MAAM,OAAO,cAAe,SAAQ,QAAQ;IACxC,YAAY,OAAe;QACvB,KAAK,CAAC,GAAG,EAAE,cAAc,OAAO,EAAE,CAAC,CAAA;IACvC,CAAC;CACJ;AAED,2DAA2D;AAC3D,MAAM,OAAO,aAAc,SAAQ,QAAQ;IACvC,YAAY,OAAO,GAAG,UAAU;QAC5B,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IACvB,CAAC;CACJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { Logger, RpcRequest, RpcResponse } from '@mt-tl/tl';
|
|
2
|
+
import { type HandlerCtx } from './context.js';
|
|
3
|
+
import type { UpdateEmitter } from './updates.js';
|
|
4
|
+
/**
|
|
5
|
+
* Shape of one TL method's I/O. An app's generated `RpcMethods` (one entry per
|
|
6
|
+
* method, `{ params, result }`) structurally matches `RpcMethodMap` — the
|
|
7
|
+
* framework is generic over it, not bound to any specific schema.
|
|
8
|
+
*/
|
|
9
|
+
export interface RpcMethodSpec {
|
|
10
|
+
params: unknown;
|
|
11
|
+
result: unknown;
|
|
12
|
+
}
|
|
13
|
+
export type RpcMethodMap = Record<string, RpcMethodSpec>;
|
|
14
|
+
/** A typed handler for one method `M` of the app's method map `RM`. */
|
|
15
|
+
export type RpcHandlerOf<RM extends Record<keyof RM, RpcMethodSpec>, M extends keyof RM> = (params: RM[M]['params'], ctx: HandlerCtx) => Promise<RM[M]['result']>;
|
|
16
|
+
/**
|
|
17
|
+
* A reusable pre-handler. Runs before the handler with the same `ctx`; throw an
|
|
18
|
+
* `AppError` to reject (→ `rpc_error`), or `ctx.set(...)` to pass data forward.
|
|
19
|
+
* Method-agnostic, so `params` is `unknown` (narrow if a hook needs them).
|
|
20
|
+
*/
|
|
21
|
+
export type Hook = (params: unknown, ctx: HandlerCtx) => Promise<void> | void;
|
|
22
|
+
/** Identity helper for authoring a reusable hook. */
|
|
23
|
+
export declare function defineHook(fn: Hook): Hook;
|
|
24
|
+
export type RpcEntryOf<RM extends Record<keyof RM, RpcMethodSpec>, M extends keyof RM> = RpcHandlerOf<RM, M> | {
|
|
25
|
+
auth?: boolean;
|
|
26
|
+
preHandlers?: Hook[];
|
|
27
|
+
handler: RpcHandlerOf<RM, M>;
|
|
28
|
+
};
|
|
29
|
+
/** A fully-typed module of RPC handlers, keyed by the app's method names. */
|
|
30
|
+
export type RpcModuleOf<RM extends Record<keyof RM, RpcMethodSpec>> = {
|
|
31
|
+
[M in keyof RM]?: RpcEntryOf<RM, M>;
|
|
32
|
+
};
|
|
33
|
+
type LooseHandler = (params: never, ctx: HandlerCtx) => Promise<unknown>;
|
|
34
|
+
type LooseEntry = LooseHandler | {
|
|
35
|
+
auth?: boolean;
|
|
36
|
+
preHandlers?: Hook[];
|
|
37
|
+
handler: LooseHandler;
|
|
38
|
+
};
|
|
39
|
+
/** Erased module shape — what module factories expose and the registry consumes. */
|
|
40
|
+
export type RpcModule = Record<string, LooseEntry | undefined>;
|
|
41
|
+
/**
|
|
42
|
+
* Creates a `defineRpc` typed against the app's generated `RpcMethods`. Use once
|
|
43
|
+
* per app: `export const { defineRpc } = createRpc<RpcMethods>()`, then write
|
|
44
|
+
* modules with full param/result inference per method.
|
|
45
|
+
*/
|
|
46
|
+
export declare function createRpc<RM extends Record<keyof RM, RpcMethodSpec>>(): {
|
|
47
|
+
defineRpc(mod: RpcModuleOf<RM>): RpcModuleOf<RM>;
|
|
48
|
+
};
|
|
49
|
+
/** Untyped authoring helper — for fixtures/demos with no generated types. */
|
|
50
|
+
export declare function defineRpc(mod: RpcModule): RpcModule;
|
|
51
|
+
interface Route {
|
|
52
|
+
handler: (params: unknown, ctx: HandlerCtx) => Promise<unknown>;
|
|
53
|
+
auth: boolean;
|
|
54
|
+
preHandlers: Hook[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The method table behind a server. `createServer().method(...)` adds to one for
|
|
58
|
+
* you; you rarely touch it directly (pass your own via `createServer(config, {
|
|
59
|
+
* registry })` only for advanced wiring or tests).
|
|
60
|
+
*/
|
|
61
|
+
export declare class RpcRegistry {
|
|
62
|
+
private routes;
|
|
63
|
+
/** Register a module of handlers (later entries override earlier same-named ones). */
|
|
64
|
+
add(mod: RpcModule): this;
|
|
65
|
+
get(method: string): Route | undefined;
|
|
66
|
+
/** Registered method names. */
|
|
67
|
+
methods(): string[];
|
|
68
|
+
get size(): number;
|
|
69
|
+
}
|
|
70
|
+
/** Process-wide dependencies {@link dispatchRpc} threads into each handler's `ctx`. */
|
|
71
|
+
export interface DispatchDeps {
|
|
72
|
+
updates: UpdateEmitter;
|
|
73
|
+
/** Observability sink; a per-request child becomes `ctx.log`. Defaults to no-op. */
|
|
74
|
+
logger?: Logger;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Routes a forwarded request to its handler and returns the gateway envelope.
|
|
78
|
+
* 404 for unknown methods, 401 for auth-required methods on an anonymous key,
|
|
79
|
+
* AppError → its code, anything else → 500. Effects accompany result or error.
|
|
80
|
+
*/
|
|
81
|
+
export declare function dispatchRpc(registry: RpcRegistry, request: RpcRequest, deps: DispatchDeps): Promise<RpcResponse>;
|
|
82
|
+
export {};
|
|
83
|
+
//# sourceMappingURL=rpc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc.d.ts","sourceRoot":"","sources":["../../src/core/rpc.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,EAAW,MAAM,WAAW,CAAA;AAEzE,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAA;AAChE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAEjD;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,OAAO,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;CAClB;AACD,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;AAExD,uEAAuE;AACvE,MAAM,MAAM,YAAY,CAAC,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,EAAE,aAAa,CAAC,EAAE,CAAC,SAAS,MAAM,EAAE,IAAI,CACvF,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EACvB,GAAG,EAAE,UAAU,KACd,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAA;AAE7B;;;;GAIG;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;AAE7E,qDAAqD;AACrD,wBAAgB,UAAU,CAAC,EAAE,EAAE,IAAI,GAAG,IAAI,CAEzC;AAED,MAAM,MAAM,UAAU,CAAC,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,EAAE,aAAa,CAAC,EAAE,CAAC,SAAS,MAAM,EAAE,IAC/E,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC,GACnB;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,IAAI,EAAE,CAAC;IAAC,OAAO,EAAE,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;CAAE,CAAA;AAE5E,6EAA6E;AAC7E,MAAM,MAAM,WAAW,CAAC,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,EAAE,aAAa,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC;CAAE,CAAA;AAG7G,KAAK,YAAY,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,UAAU,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;AACxE,KAAK,UAAU,GAAG,YAAY,GAAG;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,IAAI,EAAE,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,CAAA;AAChG,oFAAoF;AACpF,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC,CAAA;AAE9D;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,EAAE,aAAa,CAAC;mBAE7C,WAAW,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,EAAE,CAAC;EAIvD;AAED,6EAA6E;AAC7E,wBAAgB,SAAS,CAAC,GAAG,EAAE,SAAS,GAAG,SAAS,CAEnD;AAED,UAAU,KAAK;IACX,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC/D,IAAI,EAAE,OAAO,CAAA;IACb,WAAW,EAAE,IAAI,EAAE,CAAA;CACtB;AAED;;;;GAIG;AACH,qBAAa,WAAW;IACpB,OAAO,CAAC,MAAM,CAA2B;IAEzC,sFAAsF;IACtF,GAAG,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI;IAgBzB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS;IAItC,+BAA+B;IAC/B,OAAO,IAAI,MAAM,EAAE;IAInB,IAAI,IAAI,IAAI,MAAM,CAEjB;CACJ;AAED,uFAAuF;AACvF,MAAM,WAAW,YAAY;IACzB,OAAO,EAAE,aAAa,CAAA;IACtB,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAC7B,QAAQ,EAAE,WAAW,EACrB,OAAO,EAAE,UAAU,EACnB,IAAI,EAAE,YAAY,GACnB,OAAO,CAAC,WAAW,CAAC,CAiCtB"}
|
package/dist/core/rpc.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { fromJson, toJson, noopLogger } from '@mt-tl/tl';
|
|
2
|
+
import { AppError } from './errors.js';
|
|
3
|
+
import { createHandlerCtx } from './context.js';
|
|
4
|
+
/** Identity helper for authoring a reusable hook. */
|
|
5
|
+
export function defineHook(fn) {
|
|
6
|
+
return fn;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Creates a `defineRpc` typed against the app's generated `RpcMethods`. Use once
|
|
10
|
+
* per app: `export const { defineRpc } = createRpc<RpcMethods>()`, then write
|
|
11
|
+
* modules with full param/result inference per method.
|
|
12
|
+
*/
|
|
13
|
+
export function createRpc() {
|
|
14
|
+
return {
|
|
15
|
+
defineRpc(mod) {
|
|
16
|
+
return mod;
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** Untyped authoring helper — for fixtures/demos with no generated types. */
|
|
21
|
+
export function defineRpc(mod) {
|
|
22
|
+
return mod;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The method table behind a server. `createServer().method(...)` adds to one for
|
|
26
|
+
* you; you rarely touch it directly (pass your own via `createServer(config, {
|
|
27
|
+
* registry })` only for advanced wiring or tests).
|
|
28
|
+
*/
|
|
29
|
+
export class RpcRegistry {
|
|
30
|
+
routes = new Map();
|
|
31
|
+
/** Register a module of handlers (later entries override earlier same-named ones). */
|
|
32
|
+
add(mod) {
|
|
33
|
+
for (const [name, entry] of Object.entries(mod)) {
|
|
34
|
+
if (!entry)
|
|
35
|
+
continue;
|
|
36
|
+
const route = typeof entry === 'function'
|
|
37
|
+
? { handler: entry, auth: true, preHandlers: [] }
|
|
38
|
+
: {
|
|
39
|
+
handler: entry.handler,
|
|
40
|
+
auth: entry.auth ?? true,
|
|
41
|
+
preHandlers: entry.preHandlers ?? [],
|
|
42
|
+
};
|
|
43
|
+
this.routes.set(name, route);
|
|
44
|
+
}
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
get(method) {
|
|
48
|
+
return this.routes.get(method);
|
|
49
|
+
}
|
|
50
|
+
/** Registered method names. */
|
|
51
|
+
methods() {
|
|
52
|
+
return [...this.routes.keys()];
|
|
53
|
+
}
|
|
54
|
+
get size() {
|
|
55
|
+
return this.routes.size;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Routes a forwarded request to its handler and returns the gateway envelope.
|
|
60
|
+
* 404 for unknown methods, 401 for auth-required methods on an anonymous key,
|
|
61
|
+
* AppError → its code, anything else → 500. Effects accompany result or error.
|
|
62
|
+
*/
|
|
63
|
+
export async function dispatchRpc(registry, request, deps) {
|
|
64
|
+
const route = registry.get(request.method);
|
|
65
|
+
if (!route)
|
|
66
|
+
return { error: { code: 404, message: 'METHOD_NOT_FOUND' } };
|
|
67
|
+
if (route.auth && request.context.subject === undefined) {
|
|
68
|
+
return { error: { code: 401, message: 'AUTH_KEY_UNREGISTERED' } };
|
|
69
|
+
}
|
|
70
|
+
// Bind the full request identity onto every handler line: reqId (the client's
|
|
71
|
+
// msg_id) ties the context to one request, plus authKeyId/sessionId/subject.
|
|
72
|
+
const log = (deps.logger ?? noopLogger).child({
|
|
73
|
+
reqId: request.id,
|
|
74
|
+
method: request.method,
|
|
75
|
+
subject: request.context.subject,
|
|
76
|
+
authKeyId: request.context.authKeyId,
|
|
77
|
+
sessionId: request.context.sessionId,
|
|
78
|
+
});
|
|
79
|
+
const ctx = createHandlerCtx(request.context, deps.updates, log);
|
|
80
|
+
try {
|
|
81
|
+
const params = fromJson(request.params);
|
|
82
|
+
for (const hook of route.preHandlers)
|
|
83
|
+
await hook(params, ctx);
|
|
84
|
+
const result = await route.handler(params, ctx);
|
|
85
|
+
return { result: toJson(result), effects: effectsOf(ctx) };
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
if (err instanceof AppError) {
|
|
89
|
+
// Expected business rejection (→ rpc_error with the app's code).
|
|
90
|
+
log.debug('handler.reject', { code: err.code, error: err.message });
|
|
91
|
+
return { error: { code: err.code, message: err.message }, effects: effectsOf(ctx) };
|
|
92
|
+
}
|
|
93
|
+
// Unexpected throw — a real bug. Log it with the stack (errorStack-gated) so
|
|
94
|
+
// the failed request is traceable; the client only sees a generic 500.
|
|
95
|
+
log.error('handler.fail', { err });
|
|
96
|
+
return { error: { code: 500, message: 'INTERNAL' }, effects: effectsOf(ctx) };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function effectsOf(ctx) {
|
|
100
|
+
return ctx.effects.length ? [...ctx.effects] : undefined;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=rpc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../src/core/rpc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { JsonValue } from '@mt-tl/tl';
|
|
2
|
+
/**
|
|
3
|
+
* Durable per-subject update log: assigns the next `pts` and persists the update.
|
|
4
|
+
* `updates.getDifference` reads from here. The gateway never sees pts — it only
|
|
5
|
+
* delivers live updates best-effort; correctness is this log + getDifference.
|
|
6
|
+
* Keyed by `subject` (your internal user id), like the rest of the push path.
|
|
7
|
+
*/
|
|
8
|
+
export interface UpdateLog {
|
|
9
|
+
append(subject: string, update: JsonValue): Promise<{
|
|
10
|
+
pts: number;
|
|
11
|
+
}>;
|
|
12
|
+
/** Updates with pts in (sincePts, +inf], for getDifference. */
|
|
13
|
+
since(subject: string, sincePts: number): Promise<Array<{
|
|
14
|
+
pts: number;
|
|
15
|
+
update: JsonValue;
|
|
16
|
+
}>>;
|
|
17
|
+
currentPts(subject: string): Promise<number>;
|
|
18
|
+
}
|
|
19
|
+
/** Publishes a routed live update onto the update bus (in-memory, or Redis pub/sub in prod). */
|
|
20
|
+
export type UpdatePublish = (msg: {
|
|
21
|
+
subject?: string;
|
|
22
|
+
authKeyId?: string;
|
|
23
|
+
update: JsonValue;
|
|
24
|
+
pts?: number;
|
|
25
|
+
}) => Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Emits a server update. `emit(subject, …)` is the common path: append to the
|
|
28
|
+
* durable log (assigns pts) then publish — this backs `ctx.push`. `emitToAuthKey`
|
|
29
|
+
* targets a specific (possibly anonymous) connection by auth key, with no pts
|
|
30
|
+
* (anonymous connections have no durable update state).
|
|
31
|
+
*/
|
|
32
|
+
export interface UpdateEmitter {
|
|
33
|
+
emit(subject: string, update: JsonValue): Promise<void>;
|
|
34
|
+
emitToAuthKey(authKeyId: string, update: JsonValue): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
/** The default {@link UpdateEmitter}: log (for pts) then publish to the bus. */
|
|
37
|
+
export declare class LoggingUpdateEmitter implements UpdateEmitter {
|
|
38
|
+
private readonly log;
|
|
39
|
+
private readonly publish;
|
|
40
|
+
constructor(log: UpdateLog, publish: UpdatePublish);
|
|
41
|
+
emit(subject: string, update: JsonValue): Promise<void>;
|
|
42
|
+
emitToAuthKey(authKeyId: string, update: JsonValue): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
/** In-memory update log (single process / tests). Use a Mongo impl in prod. */
|
|
45
|
+
export declare class InMemoryUpdateLog implements UpdateLog {
|
|
46
|
+
private bySubject;
|
|
47
|
+
append(subject: string, update: JsonValue): Promise<{
|
|
48
|
+
pts: number;
|
|
49
|
+
}>;
|
|
50
|
+
since(subject: string, sincePts: number): Promise<Array<{
|
|
51
|
+
pts: number;
|
|
52
|
+
update: JsonValue;
|
|
53
|
+
}>>;
|
|
54
|
+
currentPts(subject: string): Promise<number>;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=updates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updates.d.ts","sourceRoot":"","sources":["../../src/core/updates.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAE1C;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACtB,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACpE,+DAA+D;IAC/D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,SAAS,CAAA;KAAE,CAAC,CAAC,CAAA;IAC5F,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CAC/C;AAED,gGAAgG;AAChG,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,SAAS,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAA;CACf,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAEnB;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC1B,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACvD,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACrE;AAED,gFAAgF;AAChF,qBAAa,oBAAqB,YAAW,aAAa;IAElD,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBADP,GAAG,EAAE,SAAS,EACd,OAAO,EAAE,aAAa;IAGrC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvD,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAG3E;AAED,+EAA+E;AAC/E,qBAAa,iBAAkB,YAAW,SAAS;IAC/C,OAAO,CAAC,SAAS,CAA+D;IAE1E,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAOpE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,SAAS,CAAA;KAAE,CAAC,CAAC;IAG5F,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAGrD"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** The default {@link UpdateEmitter}: log (for pts) then publish to the bus. */
|
|
2
|
+
export class LoggingUpdateEmitter {
|
|
3
|
+
log;
|
|
4
|
+
publish;
|
|
5
|
+
constructor(log, publish) {
|
|
6
|
+
this.log = log;
|
|
7
|
+
this.publish = publish;
|
|
8
|
+
}
|
|
9
|
+
async emit(subject, update) {
|
|
10
|
+
const { pts } = await this.log.append(subject, update);
|
|
11
|
+
await this.publish({ subject, update, pts });
|
|
12
|
+
}
|
|
13
|
+
async emitToAuthKey(authKeyId, update) {
|
|
14
|
+
await this.publish({ authKeyId, update });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** In-memory update log (single process / tests). Use a Mongo impl in prod. */
|
|
18
|
+
export class InMemoryUpdateLog {
|
|
19
|
+
bySubject = new Map();
|
|
20
|
+
async append(subject, update) {
|
|
21
|
+
const list = this.bySubject.get(subject) ?? [];
|
|
22
|
+
const pts = (list.at(-1)?.pts ?? 0) + 1;
|
|
23
|
+
list.push({ pts, update });
|
|
24
|
+
this.bySubject.set(subject, list);
|
|
25
|
+
return { pts };
|
|
26
|
+
}
|
|
27
|
+
async since(subject, sincePts) {
|
|
28
|
+
return (this.bySubject.get(subject) ?? []).filter(e => e.pts > sincePts);
|
|
29
|
+
}
|
|
30
|
+
async currentPts(subject) {
|
|
31
|
+
return this.bySubject.get(subject)?.at(-1)?.pts ?? 0;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=updates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updates.js","sourceRoot":"","sources":["../../src/core/updates.ts"],"names":[],"mappings":"AAkCA,gFAAgF;AAChF,MAAM,OAAO,oBAAoB;IAER;IACA;IAFrB,YACqB,GAAc,EACd,OAAsB;QADtB,QAAG,GAAH,GAAG,CAAW;QACd,YAAO,GAAP,OAAO,CAAe;IACxC,CAAC;IAEJ,KAAK,CAAC,IAAI,CAAC,OAAe,EAAE,MAAiB;QACzC,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QACtD,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IAChD,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,SAAiB,EAAE,MAAiB;QACpD,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAA;IAC7C,CAAC;CACJ;AAED,+EAA+E;AAC/E,MAAM,OAAO,iBAAiB;IAClB,SAAS,GAAG,IAAI,GAAG,EAAqD,CAAA;IAEhF,KAAK,CAAC,MAAM,CAAC,OAAe,EAAE,MAAiB;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;QAC9C,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;QACvC,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAA;QAC1B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;QACjC,OAAO,EAAE,GAAG,EAAE,CAAA;IAClB,CAAC;IACD,KAAK,CAAC,KAAK,CAAC,OAAe,EAAE,QAAgB;QACzC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,QAAQ,CAAC,CAAA;IAC5E,CAAC;IACD,KAAK,CAAC,UAAU,CAAC,OAAe;QAC5B,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAAA;IACxD,CAAC;CACJ"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { KeyObject } from 'node:crypto';
|
|
2
|
+
import { type MTProtoConfig } from './lib.js';
|
|
3
|
+
import { type Logger, type MigrationRegistry, type RpcRequest, type RpcResponse } from '@mt-tl/tl';
|
|
4
|
+
import { RpcRegistry, type Hook, type HandlerCtx, type RpcMethodSpec } from './core/index.js';
|
|
5
|
+
/** Per-method options. */
|
|
6
|
+
export interface MethodOpts {
|
|
7
|
+
/** Require an authorized auth key (a bound `subject`). Defaults to `true`. */
|
|
8
|
+
auth?: boolean;
|
|
9
|
+
/** Reusable pre-handlers, run in order before the handler (see `defineHook`). */
|
|
10
|
+
preHandlers?: Hook[];
|
|
11
|
+
}
|
|
12
|
+
/** A handler for method `M` of the app's generated method map `RM`. */
|
|
13
|
+
export type MethodHandler<RM, M extends keyof RM> = RM[M] extends RpcMethodSpec ? (params: RM[M]['params'], ctx: HandlerCtx) => Promise<RM[M]['result']> : never;
|
|
14
|
+
/** A plugin: registers routes on the server, given its declared dependencies. */
|
|
15
|
+
export type Plugin<RM, D = void> = (app: MtprotoServer<RM>, deps: D) => void;
|
|
16
|
+
/**
|
|
17
|
+
* The server instance (Fastify-style): register routes and listen. It wraps the
|
|
18
|
+
* MTProto engine + the in-process handler dispatch — there is no broker and no
|
|
19
|
+
* separate "worker" to wire. Generic over the app's generated `RpcMethods` so
|
|
20
|
+
* `.method()` infers `params`/`result` (and the method name) per method.
|
|
21
|
+
*/
|
|
22
|
+
export interface MtprotoServer<RM = Record<string, RpcMethodSpec>> {
|
|
23
|
+
/** Register a route. `auth` defaults to true. */
|
|
24
|
+
method<M extends keyof RM>(name: M, handler: MethodHandler<RM, M>): this;
|
|
25
|
+
method<M extends keyof RM>(name: M, opts: MethodOpts, handler: MethodHandler<RM, M>): this;
|
|
26
|
+
/** Run a plugin, passing its dependencies by value (Style-A DI; omit for `void` deps). */
|
|
27
|
+
register<D = void>(plugin: Plugin<RM, D>, deps?: D): this;
|
|
28
|
+
/** Open the configured transports (and the in-process push loop if enabled). */
|
|
29
|
+
listen(): Promise<void>;
|
|
30
|
+
close(): Promise<void>;
|
|
31
|
+
/** Dispatch a request against the registry without a socket — for tests. */
|
|
32
|
+
inject(req: RpcRequest): Promise<RpcResponse>;
|
|
33
|
+
/** The server's root logger — use it (or `ctx.log` in a handler) for a unified log style. */
|
|
34
|
+
readonly log: Logger;
|
|
35
|
+
/** Registered method names. */
|
|
36
|
+
readonly methods: string[];
|
|
37
|
+
/** Bound WebSocket port after {@link listen} (resolves `wsPort: 0`); else `undefined`. */
|
|
38
|
+
readonly wsPort: number | undefined;
|
|
39
|
+
/** Bound raw-TCP port after {@link listen} (resolves `tcpPort: 0`); else `undefined`. */
|
|
40
|
+
readonly tcpPort: number | undefined;
|
|
41
|
+
/** The server's RSA public key after {@link listen}; clients encrypt the handshake with it. */
|
|
42
|
+
readonly publicKey: KeyObject | undefined;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Creates an MTProto server (Fastify-style). Pass the {@link MTProtoConfig},
|
|
46
|
+
* register routes with `.method()` / plugins with `.register()`, then
|
|
47
|
+
* `await app.listen()`. The framework owns the whole protocol — transport,
|
|
48
|
+
* handshake, crypto, sessions, TL (de)serialization, layered encoding,
|
|
49
|
+
* server-push — you write methods.
|
|
50
|
+
*
|
|
51
|
+
* Type it with your generated `RpcMethods` (`createServer<RpcMethods>(config)`)
|
|
52
|
+
* so every route's name, `params`, and `result` are checked.
|
|
53
|
+
*
|
|
54
|
+
* @param config - the server configuration (your app builds it from env).
|
|
55
|
+
* @param opts.registry - adopt an existing {@link RpcRegistry} instead of a fresh one (advanced/tests).
|
|
56
|
+
* @param opts.migrations - per-layer migration ladders applied on input/output.
|
|
57
|
+
* @param opts.logger - structured logger; defaults to `createLogger({ name: config.nodeId })`
|
|
58
|
+
* (env-configured via `LOG_LEVEL`/`LOG_FORMAT`). Exposed as `app.log`; handlers get `ctx.log`.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* const app = createServer<RpcMethods>(config)
|
|
63
|
+
* app.method('help.getConfig', { auth: false }, async () => ({ _: 'config' }))
|
|
64
|
+
* await app.listen() // opens the WS + raw-TCP carriers
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare function createServer<RM = Record<string, RpcMethodSpec>>(config: MTProtoConfig, opts?: {
|
|
68
|
+
registry?: RpcRegistry;
|
|
69
|
+
migrations?: MigrationRegistry;
|
|
70
|
+
logger?: Logger;
|
|
71
|
+
}): MtprotoServer<RM>;
|
|
72
|
+
/**
|
|
73
|
+
* Authoring helper for a typed plugin — a function that registers a group of
|
|
74
|
+
* related routes, taking its dependencies by value (Style-A DI, like
|
|
75
|
+
* `fastify.register`). Pin it to your `RpcMethods` once (see your app's
|
|
76
|
+
* `framework.ts`) so routes inside infer their types.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```ts
|
|
80
|
+
* export const walletsPlugin = definePlugin<RpcMethods, { wallets: WalletService }>(
|
|
81
|
+
* (app, { wallets }) => {
|
|
82
|
+
* app.method('wallets.getBalance', async (_p, ctx) => wallets.balanceOf(ctx.subject!))
|
|
83
|
+
* },
|
|
84
|
+
* )
|
|
85
|
+
* app.register(walletsPlugin, { wallets: new WalletService() })
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export declare function definePlugin<RM = Record<string, RpcMethodSpec>, D = void>(fn: Plugin<RM, D>): Plugin<RM, D>;
|
|
89
|
+
//# sourceMappingURL=create-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-server.d.ts","sourceRoot":"","sources":["../src/create-server.ts"],"names":[],"mappings":"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"}
|