@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,43 @@
|
|
|
1
|
+
import type { Presence } from './presence.js';
|
|
2
|
+
/** Minimal subset of ioredis used by {@link RedisPresence} (for testability). */
|
|
3
|
+
export interface RedisLike {
|
|
4
|
+
zadd(key: string, score: number, member: string): Promise<unknown>;
|
|
5
|
+
zrem(key: string, member: string): Promise<unknown>;
|
|
6
|
+
zrangebyscore(key: string, min: number | string, max: number | string): Promise<string[]>;
|
|
7
|
+
zremrangebyscore(key: string, min: number | string, max: number | string): Promise<unknown>;
|
|
8
|
+
pexpire(key: string, ms: number): Promise<unknown>;
|
|
9
|
+
}
|
|
10
|
+
export interface RedisPresenceOptions {
|
|
11
|
+
ttlMs?: number;
|
|
12
|
+
now?: () => number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Redis-backed presence using a per-subject sorted set `presence:{subject}` with
|
|
16
|
+
* member = nodeId and score = expiry epoch (now + ttl). This gives per-member
|
|
17
|
+
* TTL: a node's entry is considered live while its score is in the future and
|
|
18
|
+
* is refreshed by a heartbeat; stale entries are pruned on lookup. Eventually
|
|
19
|
+
* consistent by design (a crashed node's entry just expires).
|
|
20
|
+
*/
|
|
21
|
+
export declare class RedisPresence implements Presence {
|
|
22
|
+
private readonly redis;
|
|
23
|
+
private readonly opts;
|
|
24
|
+
constructor(redis: RedisLike, opts?: RedisPresenceOptions);
|
|
25
|
+
private ttl;
|
|
26
|
+
private now;
|
|
27
|
+
private key;
|
|
28
|
+
private authKey;
|
|
29
|
+
private addKey;
|
|
30
|
+
private lookupKey;
|
|
31
|
+
add(subject: string, nodeId: string): Promise<void>;
|
|
32
|
+
remove(subject: string, nodeId: string): Promise<void>;
|
|
33
|
+
lookup(subject: string): Promise<string[]>;
|
|
34
|
+
addAuthKey(authKeyId: string, nodeId: string): Promise<void>;
|
|
35
|
+
removeAuthKey(authKeyId: string, nodeId: string): Promise<void>;
|
|
36
|
+
lookupAuthKey(authKeyId: string): Promise<string[]>;
|
|
37
|
+
}
|
|
38
|
+
export interface RedisPresenceHandle {
|
|
39
|
+
presence: RedisPresence;
|
|
40
|
+
close: () => Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
export declare function createRedisPresence(url: string, ttlMs: number): RedisPresenceHandle;
|
|
43
|
+
//# sourceMappingURL=redis-presence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-presence.d.ts","sourceRoot":"","sources":["../../src/updates/redis-presence.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAE7C,iFAAiF;AACjF,MAAM,WAAW,SAAS;IACtB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAClE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACnD,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IACzF,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC3F,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACrD;AAED,MAAM,WAAW,oBAAoB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACrB;AAED;;;;;;GAMG;AACH,qBAAa,aAAc,YAAW,QAAQ;IAEtC,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBADJ,KAAK,EAAE,SAAS,EAChB,IAAI,GAAE,oBAAyB;IAGpD,OAAO,CAAC,GAAG;IAGX,OAAO,CAAC,GAAG;IAGX,OAAO,CAAC,GAAG;IAGX,OAAO,CAAC,OAAO;YAID,MAAM;YAIN,SAAS;IAMjB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAGnD,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAGtD,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAG1C,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAG5D,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAG/D,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAG5D;AAED,MAAM,WAAW,mBAAmB;IAChC,QAAQ,EAAE,aAAa,CAAA;IACvB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7B;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,mBAAmB,CAQnF"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import Redis from 'ioredis';
|
|
2
|
+
/**
|
|
3
|
+
* Redis-backed presence using a per-subject sorted set `presence:{subject}` with
|
|
4
|
+
* member = nodeId and score = expiry epoch (now + ttl). This gives per-member
|
|
5
|
+
* TTL: a node's entry is considered live while its score is in the future and
|
|
6
|
+
* is refreshed by a heartbeat; stale entries are pruned on lookup. Eventually
|
|
7
|
+
* consistent by design (a crashed node's entry just expires).
|
|
8
|
+
*/
|
|
9
|
+
export class RedisPresence {
|
|
10
|
+
redis;
|
|
11
|
+
opts;
|
|
12
|
+
constructor(redis, opts = {}) {
|
|
13
|
+
this.redis = redis;
|
|
14
|
+
this.opts = opts;
|
|
15
|
+
}
|
|
16
|
+
ttl() {
|
|
17
|
+
return this.opts.ttlMs ?? 60_000;
|
|
18
|
+
}
|
|
19
|
+
now() {
|
|
20
|
+
return (this.opts.now ?? Date.now)();
|
|
21
|
+
}
|
|
22
|
+
key(subject) {
|
|
23
|
+
return `presence:${subject}`;
|
|
24
|
+
}
|
|
25
|
+
authKey(authKeyId) {
|
|
26
|
+
return `presence:a:${authKeyId}`;
|
|
27
|
+
}
|
|
28
|
+
async addKey(key, nodeId) {
|
|
29
|
+
await this.redis.zadd(key, this.now() + this.ttl(), nodeId);
|
|
30
|
+
await this.redis.pexpire(key, this.ttl() * 2);
|
|
31
|
+
}
|
|
32
|
+
async lookupKey(key) {
|
|
33
|
+
const now = this.now();
|
|
34
|
+
await this.redis.zremrangebyscore(key, 0, now); // drop expired members
|
|
35
|
+
return this.redis.zrangebyscore(key, now, '+inf'); // live members
|
|
36
|
+
}
|
|
37
|
+
async add(subject, nodeId) {
|
|
38
|
+
await this.addKey(this.key(subject), nodeId);
|
|
39
|
+
}
|
|
40
|
+
async remove(subject, nodeId) {
|
|
41
|
+
await this.redis.zrem(this.key(subject), nodeId);
|
|
42
|
+
}
|
|
43
|
+
async lookup(subject) {
|
|
44
|
+
return this.lookupKey(this.key(subject));
|
|
45
|
+
}
|
|
46
|
+
async addAuthKey(authKeyId, nodeId) {
|
|
47
|
+
await this.addKey(this.authKey(authKeyId), nodeId);
|
|
48
|
+
}
|
|
49
|
+
async removeAuthKey(authKeyId, nodeId) {
|
|
50
|
+
await this.redis.zrem(this.authKey(authKeyId), nodeId);
|
|
51
|
+
}
|
|
52
|
+
async lookupAuthKey(authKeyId) {
|
|
53
|
+
return this.lookupKey(this.authKey(authKeyId));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export function createRedisPresence(url, ttlMs) {
|
|
57
|
+
const client = new Redis(url, { lazyConnect: false });
|
|
58
|
+
return {
|
|
59
|
+
presence: new RedisPresence(client, { ttlMs }),
|
|
60
|
+
close: async () => {
|
|
61
|
+
await client.quit();
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=redis-presence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-presence.js","sourceRoot":"","sources":["../../src/updates/redis-presence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { LayeredRegistry } from '../tl/layered-registry.js';
|
|
2
|
+
import type { TlObject } from '@mt-tl/tl';
|
|
3
|
+
/**
|
|
4
|
+
* Renders a server update for a specific client layer.
|
|
5
|
+
*
|
|
6
|
+
* - Representable at the layer → unchanged.
|
|
7
|
+
* - Not representable but pts-bearing → `updateUnsupported{pts, pts_count}`
|
|
8
|
+
* (preserves the pts accounting so the client resyncs via getDifference).
|
|
9
|
+
* - Not representable and ephemeral (no pts) → dropped (returns null).
|
|
10
|
+
*
|
|
11
|
+
* Recurses into the common containers (`updateShort`, `updates`,
|
|
12
|
+
* `updatesCombined`) so a single unrepresentable inner update doesn't sink the
|
|
13
|
+
* whole batch.
|
|
14
|
+
*/
|
|
15
|
+
export declare function renderUpdateForLayer(update: TlObject, layer: number, layered: LayeredRegistry): TlObject | null;
|
|
16
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/updates/render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAChE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAEzC;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAChC,MAAM,EAAE,QAAQ,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,eAAe,GACzB,QAAQ,GAAG,IAAI,CAmBjB"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renders a server update for a specific client layer.
|
|
3
|
+
*
|
|
4
|
+
* - Representable at the layer → unchanged.
|
|
5
|
+
* - Not representable but pts-bearing → `updateUnsupported{pts, pts_count}`
|
|
6
|
+
* (preserves the pts accounting so the client resyncs via getDifference).
|
|
7
|
+
* - Not representable and ephemeral (no pts) → dropped (returns null).
|
|
8
|
+
*
|
|
9
|
+
* Recurses into the common containers (`updateShort`, `updates`,
|
|
10
|
+
* `updatesCombined`) so a single unrepresentable inner update doesn't sink the
|
|
11
|
+
* whole batch.
|
|
12
|
+
*/
|
|
13
|
+
export function renderUpdateForLayer(update, layer, layered) {
|
|
14
|
+
if (layered.representable(update, layer))
|
|
15
|
+
return update;
|
|
16
|
+
switch (update._) {
|
|
17
|
+
case 'updateShort': {
|
|
18
|
+
const inner = renderLeaf(update.update, layer, layered);
|
|
19
|
+
return inner ? { ...update, update: inner } : null;
|
|
20
|
+
}
|
|
21
|
+
case 'updates':
|
|
22
|
+
case 'updatesCombined': {
|
|
23
|
+
const list = Array.isArray(update.updates) ? update.updates : [];
|
|
24
|
+
const rendered = list
|
|
25
|
+
.map(u => renderLeaf(u, layer, layered))
|
|
26
|
+
.filter((u) => u !== null);
|
|
27
|
+
return { ...update, updates: rendered };
|
|
28
|
+
}
|
|
29
|
+
default:
|
|
30
|
+
return renderLeaf(update, layer, layered);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function renderLeaf(update, layer, layered) {
|
|
34
|
+
if (layered.representable(update, layer))
|
|
35
|
+
return update;
|
|
36
|
+
const pts = numberField(update, 'pts');
|
|
37
|
+
if (pts !== undefined) {
|
|
38
|
+
return { _: 'updateUnsupported', pts, pts_count: numberField(update, 'pts_count') ?? 0 };
|
|
39
|
+
}
|
|
40
|
+
return null; // ephemeral, no pts — safe to drop
|
|
41
|
+
}
|
|
42
|
+
function numberField(obj, key) {
|
|
43
|
+
const v = obj[key];
|
|
44
|
+
return typeof v === 'number' ? v : undefined;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/updates/render.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,oBAAoB,CAChC,MAAgB,EAChB,KAAa,EACb,OAAwB;IAExB,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC;QAAE,OAAO,MAAM,CAAA;IAEvD,QAAQ,MAAM,CAAC,CAAC,EAAE,CAAC;QACf,KAAK,aAAa,CAAC,CAAC,CAAC;YACjB,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,MAAkB,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;YACnE,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;QACtD,CAAC;QACD,KAAK,SAAS,CAAC;QACf,KAAK,iBAAiB,CAAC,CAAC,CAAC;YACrB,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAE,MAAM,CAAC,OAAsB,CAAC,CAAC,CAAC,EAAE,CAAA;YAChF,MAAM,QAAQ,GAAG,IAAI;iBAChB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;iBACvC,MAAM,CAAC,CAAC,CAAC,EAAiB,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAA;YAC7C,OAAO,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAA;QAC3C,CAAC;QACD;YACI,OAAO,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;IACjD,CAAC;AACL,CAAC;AAED,SAAS,UAAU,CAAC,MAAgB,EAAE,KAAa,EAAE,OAAwB;IACzE,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC;QAAE,OAAO,MAAM,CAAA;IACvD,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IACtC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACpB,OAAO,EAAE,CAAC,EAAE,mBAAmB,EAAE,GAAG,EAAE,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,IAAI,CAAC,EAAE,CAAA;IAC5F,CAAC;IACD,OAAO,IAAI,CAAA,CAAC,mCAAmC;AACnD,CAAC;AAED,SAAS,WAAW,CAAC,GAAa,EAAE,GAAW;IAC3C,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAA;IAClB,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;AAChD,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { JsonValue } from '@mt-tl/tl';
|
|
2
|
+
import type { Presence } from './presence.js';
|
|
3
|
+
import type { UpdateBus } from './update-bus.js';
|
|
4
|
+
import type { UpdateMessage } from './types.js';
|
|
5
|
+
export interface RouterOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Anti-DDoS valve: return false to drop/coalesce an update under load.
|
|
8
|
+
* Safe to drop — clients recover via pts/getDifference. Default: deliver all.
|
|
9
|
+
*/
|
|
10
|
+
shouldDeliver?: (subject: string, update: JsonValue) => boolean;
|
|
11
|
+
onError?: (err: unknown, msg: UpdateMessage) => void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Presence-aware Update Router (standalone service, shard by subject in prod).
|
|
15
|
+
* Consumes worker updates, looks up which nodes hold the subject, and delivers
|
|
16
|
+
* only to those nodes — so 8 idle nodes never receive an update for a subject on
|
|
17
|
+
* node 1. This is the single place to throttle/coalesce per subject.
|
|
18
|
+
*/
|
|
19
|
+
export declare class UpdateRouter {
|
|
20
|
+
private readonly bus;
|
|
21
|
+
private readonly presence;
|
|
22
|
+
private readonly opts;
|
|
23
|
+
constructor(bus: UpdateBus, presence: Presence, opts?: RouterOptions);
|
|
24
|
+
start(): void;
|
|
25
|
+
private route;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/updates/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,MAAM,WAAW,aAAa;IAC1B;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,KAAK,OAAO,CAAA;IAC/D,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,aAAa,KAAK,IAAI,CAAA;CACvD;AAED;;;;;GAKG;AACH,qBAAa,YAAY;IAEjB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAFJ,GAAG,EAAE,SAAS,EACd,QAAQ,EAAE,QAAQ,EAClB,IAAI,GAAE,aAAkB;IAG7C,KAAK,IAAI,IAAI;YAMC,KAAK;CAkBtB"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presence-aware Update Router (standalone service, shard by subject in prod).
|
|
3
|
+
* Consumes worker updates, looks up which nodes hold the subject, and delivers
|
|
4
|
+
* only to those nodes — so 8 idle nodes never receive an update for a subject on
|
|
5
|
+
* node 1. This is the single place to throttle/coalesce per subject.
|
|
6
|
+
*/
|
|
7
|
+
export class UpdateRouter {
|
|
8
|
+
bus;
|
|
9
|
+
presence;
|
|
10
|
+
opts;
|
|
11
|
+
constructor(bus, presence, opts = {}) {
|
|
12
|
+
this.bus = bus;
|
|
13
|
+
this.presence = presence;
|
|
14
|
+
this.opts = opts;
|
|
15
|
+
}
|
|
16
|
+
start() {
|
|
17
|
+
this.bus.subscribeUpdates(msg => {
|
|
18
|
+
void this.route(msg).catch(err => this.opts.onError?.(err, msg));
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async route(msg) {
|
|
22
|
+
// Auth-key-addressed delivery (anonymous connection); skips the per-user valve.
|
|
23
|
+
if (msg.authKeyId !== undefined) {
|
|
24
|
+
const nodes = await this.presence.lookupAuthKey(msg.authKeyId);
|
|
25
|
+
await Promise.all(nodes.map(nodeId => this.bus.publishToNode(nodeId, { authKeyId: msg.authKeyId, update: msg.update })));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (msg.subject === undefined)
|
|
29
|
+
return;
|
|
30
|
+
if (this.opts.shouldDeliver && !this.opts.shouldDeliver(msg.subject, msg.update))
|
|
31
|
+
return;
|
|
32
|
+
const nodes = await this.presence.lookup(msg.subject);
|
|
33
|
+
await Promise.all(nodes.map(nodeId => this.bus.publishToNode(nodeId, { subject: msg.subject, update: msg.update })));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/updates/router.ts"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH,MAAM,OAAO,YAAY;IAEA;IACA;IACA;IAHrB,YACqB,GAAc,EACd,QAAkB,EAClB,OAAsB,EAAE;QAFxB,QAAG,GAAH,GAAG,CAAW;QACd,aAAQ,GAAR,QAAQ,CAAU;QAClB,SAAI,GAAJ,IAAI,CAAoB;IAC1C,CAAC;IAEJ,KAAK;QACD,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE;YAC5B,KAAK,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;QACpE,CAAC,CAAC,CAAA;IACN,CAAC;IAEO,KAAK,CAAC,KAAK,CAAC,GAAkB;QAClC,gFAAgF;QAChF,IAAI,GAAG,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YAC9B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;YAC9D,MAAM,OAAO,CAAC,GAAG,CACb,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CACf,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CACnF,CACJ,CAAA;YACD,OAAM;QACV,CAAC;QACD,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS;YAAE,OAAM;QACrC,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC;YAAE,OAAM;QACxF,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QACrD,MAAM,OAAO,CAAC,GAAG,CACb,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CACpG,CAAA;IACL,CAAC;CACJ"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { JsonValue } from '@mt-tl/tl';
|
|
2
|
+
/**
|
|
3
|
+
* An update to deliver, addressed to exactly one target: a bound `subject` (the
|
|
4
|
+
* common case, pts-logged) OR an `authKeyId` (a specific, possibly anonymous,
|
|
5
|
+
* connection — e.g. pushing API to a not-yet-registered client; no pts). Set one.
|
|
6
|
+
*/
|
|
7
|
+
export interface UpdateMessage {
|
|
8
|
+
/** The subject (internal user id) to deliver to. */
|
|
9
|
+
subject?: string;
|
|
10
|
+
/** Decimal auth-key id, to address a specific (possibly anonymous) connection. */
|
|
11
|
+
authKeyId?: string;
|
|
12
|
+
/** A TL update object as tagged JSON ({ _: name, ... }). */
|
|
13
|
+
update: JsonValue;
|
|
14
|
+
/** Permanent-update sequence number (for getDifference recovery); opaque here. */
|
|
15
|
+
pts?: number;
|
|
16
|
+
}
|
|
17
|
+
/** A routed delivery from the Update Router to a specific gateway node. */
|
|
18
|
+
export interface NodeDelivery {
|
|
19
|
+
subject?: string;
|
|
20
|
+
authKeyId?: string;
|
|
21
|
+
update: JsonValue;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/updates/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAE1C;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC1B,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4DAA4D;IAC5D,MAAM,EAAE,SAAS,CAAA;IACjB,kFAAkF;IAClF,GAAG,CAAC,EAAE,MAAM,CAAA;CACf;AAED,2EAA2E;AAC3E,MAAM,WAAW,YAAY;IACzB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,SAAS,CAAA;CACpB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/updates/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { NodeDelivery, UpdateMessage } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Message bus connecting publishers -> Update Router -> server nodes. Two impls:
|
|
4
|
+
* an in-memory one (single process) and a Redis pub/sub one (multi-instance:
|
|
5
|
+
* an `updates.in` channel for emissions and per-node `updates.node.{id}` channels
|
|
6
|
+
* for routed deliveries).
|
|
7
|
+
*/
|
|
8
|
+
export interface UpdateBus {
|
|
9
|
+
/** Publisher side: emit an update for a user. Fire-and-forget. */
|
|
10
|
+
publishUpdate(msg: UpdateMessage): Promise<void>;
|
|
11
|
+
/** Router side: receive all emitted updates. */
|
|
12
|
+
subscribeUpdates(handler: (msg: UpdateMessage) => void): void;
|
|
13
|
+
/** Router side: deliver a routed update to a specific node. */
|
|
14
|
+
publishToNode(nodeId: string, msg: NodeDelivery): Promise<void>;
|
|
15
|
+
/** Node side: receive deliveries addressed to this node. */
|
|
16
|
+
subscribeNode(nodeId: string, handler: (msg: NodeDelivery) => void): void;
|
|
17
|
+
close(): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
/** In-memory bus (single process / tests). */
|
|
20
|
+
export declare class InMemoryUpdateBus implements UpdateBus {
|
|
21
|
+
private emitter;
|
|
22
|
+
constructor();
|
|
23
|
+
publishUpdate(msg: UpdateMessage): Promise<void>;
|
|
24
|
+
subscribeUpdates(handler: (msg: UpdateMessage) => void): void;
|
|
25
|
+
publishToNode(nodeId: string, msg: NodeDelivery): Promise<void>;
|
|
26
|
+
subscribeNode(nodeId: string, handler: (msg: NodeDelivery) => void): void;
|
|
27
|
+
close(): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=update-bus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-bus.d.ts","sourceRoot":"","sources":["../../src/updates/update-bus.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE7D;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACtB,kEAAkE;IAClE,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAChD,gDAAgD;IAChD,gBAAgB,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,GAAG,IAAI,CAAA;IAC7D,+DAA+D;IAC/D,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/D,4DAA4D;IAC5D,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,GAAG,IAAI,CAAA;IACzE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB;AAED,8CAA8C;AAC9C,qBAAa,iBAAkB,YAAW,SAAS;IAC/C,OAAO,CAAC,OAAO,CAAqB;;IAM9B,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD,gBAAgB,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,GAAG,IAAI;IAIvD,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrE,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,GAAG,IAAI;IAInE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG/B"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
/** In-memory bus (single process / tests). */
|
|
3
|
+
export class InMemoryUpdateBus {
|
|
4
|
+
emitter = new EventEmitter();
|
|
5
|
+
constructor() {
|
|
6
|
+
this.emitter.setMaxListeners(0);
|
|
7
|
+
}
|
|
8
|
+
async publishUpdate(msg) {
|
|
9
|
+
this.emitter.emit('update', msg);
|
|
10
|
+
}
|
|
11
|
+
subscribeUpdates(handler) {
|
|
12
|
+
this.emitter.on('update', handler);
|
|
13
|
+
}
|
|
14
|
+
async publishToNode(nodeId, msg) {
|
|
15
|
+
this.emitter.emit(`node:${nodeId}`, msg);
|
|
16
|
+
}
|
|
17
|
+
subscribeNode(nodeId, handler) {
|
|
18
|
+
this.emitter.on(`node:${nodeId}`, handler);
|
|
19
|
+
}
|
|
20
|
+
async close() {
|
|
21
|
+
this.emitter.removeAllListeners();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=update-bus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-bus.js","sourceRoot":"","sources":["../../src/updates/update-bus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAqB1C,8CAA8C;AAC9C,MAAM,OAAO,iBAAiB;IAClB,OAAO,GAAG,IAAI,YAAY,EAAE,CAAA;IAEpC;QACI,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAA;IACnC,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,GAAkB;QAClC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAA;IACpC,CAAC;IAED,gBAAgB,CAAC,OAAqC;QAClD,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IACtC,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,MAAc,EAAE,GAAiB;QACjD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,MAAM,EAAE,EAAE,GAAG,CAAC,CAAA;IAC5C,CAAC;IAED,aAAa,CAAC,MAAc,EAAE,OAAoC;QAC9D,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,MAAM,EAAE,EAAE,OAAO,CAAC,CAAA;IAC9C,CAAC;IAED,KAAK,CAAC,KAAK;QACP,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAA;IACrC,CAAC;CACJ"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure-JS BigInt <-> Buffer helpers (little/big endian).
|
|
3
|
+
* Replaces the native `bigint-buffer` dependency so the gateway has no
|
|
4
|
+
* node-gyp build step. Values are treated as unsigned; this matches the
|
|
5
|
+
* existing server's `toBigIntLE`/`toBufferLE` usage for MTProto longs/ids.
|
|
6
|
+
*/
|
|
7
|
+
export declare function toBigIntLE(buf: Buffer): bigint;
|
|
8
|
+
export declare function toBigIntBE(buf: Buffer): bigint;
|
|
9
|
+
export declare function toBufferLE(value: bigint, length: number): Buffer;
|
|
10
|
+
export declare function toBufferBE(value: bigint, length: number): Buffer;
|
|
11
|
+
export declare function xorBuffers(a: Buffer, b: Buffer): Buffer;
|
|
12
|
+
//# sourceMappingURL=bytes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bytes.d.ts","sourceRoot":"","sources":["../../src/util/bytes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAM9C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAM9C;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAQhE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAQhE;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAKvD"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure-JS BigInt <-> Buffer helpers (little/big endian).
|
|
3
|
+
* Replaces the native `bigint-buffer` dependency so the gateway has no
|
|
4
|
+
* node-gyp build step. Values are treated as unsigned; this matches the
|
|
5
|
+
* existing server's `toBigIntLE`/`toBufferLE` usage for MTProto longs/ids.
|
|
6
|
+
*/
|
|
7
|
+
export function toBigIntLE(buf) {
|
|
8
|
+
let result = 0n;
|
|
9
|
+
for (let i = buf.length - 1; i >= 0; i--) {
|
|
10
|
+
result = (result << 8n) | BigInt(buf[i]);
|
|
11
|
+
}
|
|
12
|
+
return result;
|
|
13
|
+
}
|
|
14
|
+
export function toBigIntBE(buf) {
|
|
15
|
+
let result = 0n;
|
|
16
|
+
for (let i = 0; i < buf.length; i++) {
|
|
17
|
+
result = (result << 8n) | BigInt(buf[i]);
|
|
18
|
+
}
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
export function toBufferLE(value, length) {
|
|
22
|
+
const buf = Buffer.alloc(length);
|
|
23
|
+
let v = value;
|
|
24
|
+
for (let i = 0; i < length; i++) {
|
|
25
|
+
buf[i] = Number(v & 0xffn);
|
|
26
|
+
v >>= 8n;
|
|
27
|
+
}
|
|
28
|
+
return buf;
|
|
29
|
+
}
|
|
30
|
+
export function toBufferBE(value, length) {
|
|
31
|
+
const buf = Buffer.alloc(length);
|
|
32
|
+
let v = value;
|
|
33
|
+
for (let i = length - 1; i >= 0; i--) {
|
|
34
|
+
buf[i] = Number(v & 0xffn);
|
|
35
|
+
v >>= 8n;
|
|
36
|
+
}
|
|
37
|
+
return buf;
|
|
38
|
+
}
|
|
39
|
+
export function xorBuffers(a, b) {
|
|
40
|
+
const len = Math.min(a.length, b.length);
|
|
41
|
+
const res = Buffer.allocUnsafe(len);
|
|
42
|
+
for (let i = 0; i < len; i++)
|
|
43
|
+
res[i] = a[i] ^ b[i];
|
|
44
|
+
return res;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=bytes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bytes.js","sourceRoot":"","sources":["../../src/util/bytes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,UAAU,UAAU,CAAC,GAAW;IAClC,IAAI,MAAM,GAAG,EAAE,CAAA;IACf,KAAK,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC,CAAA;IAC7C,CAAC;IACD,OAAO,MAAM,CAAA;AACjB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAW;IAClC,IAAI,MAAM,GAAG,EAAE,CAAA;IACf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,MAAM,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC,CAAA;IAC7C,CAAC;IACD,OAAO,MAAM,CAAA;AACjB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa,EAAE,MAAc;IACpD,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IAChC,IAAI,CAAC,GAAG,KAAK,CAAA;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9B,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,CAAA;QAC1B,CAAC,KAAK,EAAE,CAAA;IACZ,CAAC;IACD,OAAO,GAAG,CAAA;AACd,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa,EAAE,MAAc;IACpD,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IAChC,IAAI,CAAC,GAAG,KAAK,CAAA;IACb,KAAK,IAAI,CAAC,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,CAAA;QAC1B,CAAC,KAAK,EAAE,CAAA;IACZ,CAAC;IACD,OAAO,GAAG,CAAA;AACd,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,CAAS,EAAE,CAAS;IAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAA;IACxC,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;IACnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE;QAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAE,CAAA;IACpD,OAAO,GAAG,CAAA;AACd,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mt-tl/server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "An MTProto 2.0 server framework (Fastify-style): createServer, routes, plugins, hooks, server-push. Ships the full protocol engine — WebSocket/raw-TCP transport, handshake, AES-IGE crypto, sessions, TL (de)serialization, layered encoding — plus the handler layer, in one install.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Joe Beretta",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/joeberetta/mt-tl.git",
|
|
11
|
+
"directory": "packages/server"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/joeberetta/mt-tl/tree/master/packages/server#readme",
|
|
14
|
+
"bugs": "https://github.com/joeberetta/mt-tl/issues",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"mtproto",
|
|
17
|
+
"telegram",
|
|
18
|
+
"server",
|
|
19
|
+
"framework",
|
|
20
|
+
"websocket",
|
|
21
|
+
"tcp",
|
|
22
|
+
"protocol",
|
|
23
|
+
"tl"
|
|
24
|
+
],
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=20"
|
|
27
|
+
},
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"import": "./dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./testkit": {
|
|
34
|
+
"types": "./dist/testkit.d.ts",
|
|
35
|
+
"import": "./dist/testkit.js"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist",
|
|
40
|
+
"src"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
44
|
+
"clean": "rm -rf dist",
|
|
45
|
+
"prepack": "yarn run build",
|
|
46
|
+
"typecheck": "tsc --noEmit",
|
|
47
|
+
"test": "vitest run"
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public",
|
|
51
|
+
"exports": {
|
|
52
|
+
".": {
|
|
53
|
+
"types": "./dist/index.d.ts",
|
|
54
|
+
"import": "./dist/index.js"
|
|
55
|
+
},
|
|
56
|
+
"./testkit": {
|
|
57
|
+
"types": "./dist/testkit.d.ts",
|
|
58
|
+
"import": "./dist/testkit.js"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"main": "./dist/index.js",
|
|
62
|
+
"module": "./dist/index.js",
|
|
63
|
+
"types": "./dist/index.d.ts"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"@mt-tl/tl": "0.1.0",
|
|
67
|
+
"crc-32": "^1.2.2",
|
|
68
|
+
"ioredis": "^5.4.1",
|
|
69
|
+
"mongodb": "^6.12.0",
|
|
70
|
+
"ws": "^8.18.0"
|
|
71
|
+
},
|
|
72
|
+
"devDependencies": {
|
|
73
|
+
"@mt-tl/testing": "0.1.0",
|
|
74
|
+
"@types/node": "^20.14.0",
|
|
75
|
+
"@types/ws": "^8.5.12",
|
|
76
|
+
"demo-eos-seed-app": "0.1.0",
|
|
77
|
+
"tsx": "^4.19.2",
|
|
78
|
+
"typescript": "^5.6.3",
|
|
79
|
+
"vitest": "^2.1.8"
|
|
80
|
+
},
|
|
81
|
+
"main": "./dist/index.js",
|
|
82
|
+
"module": "./dist/index.js",
|
|
83
|
+
"types": "./dist/index.d.ts"
|
|
84
|
+
}
|