@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.
Files changed (293) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -0
  3. package/dist/auth/handshake.d.ts +35 -0
  4. package/dist/auth/handshake.d.ts.map +1 -0
  5. package/dist/auth/handshake.js +208 -0
  6. package/dist/auth/handshake.js.map +1 -0
  7. package/dist/auth/nonce-store.d.ts +22 -0
  8. package/dist/auth/nonce-store.d.ts.map +1 -0
  9. package/dist/auth/nonce-store.js +23 -0
  10. package/dist/auth/nonce-store.js.map +1 -0
  11. package/dist/bootstrap.d.ts +36 -0
  12. package/dist/bootstrap.d.ts.map +1 -0
  13. package/dist/bootstrap.js +82 -0
  14. package/dist/bootstrap.js.map +1 -0
  15. package/dist/config.d.ts +103 -0
  16. package/dist/config.d.ts.map +1 -0
  17. package/dist/config.js +2 -0
  18. package/dist/config.js.map +1 -0
  19. package/dist/core/context.d.ts +57 -0
  20. package/dist/core/context.d.ts.map +1 -0
  21. package/dist/core/context.js +27 -0
  22. package/dist/core/context.js.map +1 -0
  23. package/dist/core/errors.d.ts +33 -0
  24. package/dist/core/errors.d.ts.map +1 -0
  25. package/dist/core/errors.js +47 -0
  26. package/dist/core/errors.js.map +1 -0
  27. package/dist/core/index.d.ts +5 -0
  28. package/dist/core/index.d.ts.map +1 -0
  29. package/dist/core/index.js +5 -0
  30. package/dist/core/index.js.map +1 -0
  31. package/dist/core/rpc.d.ts +83 -0
  32. package/dist/core/rpc.d.ts.map +1 -0
  33. package/dist/core/rpc.js +102 -0
  34. package/dist/core/rpc.js.map +1 -0
  35. package/dist/core/updates.d.ts +56 -0
  36. package/dist/core/updates.d.ts.map +1 -0
  37. package/dist/core/updates.js +34 -0
  38. package/dist/core/updates.js.map +1 -0
  39. package/dist/create-server.d.ts +89 -0
  40. package/dist/create-server.d.ts.map +1 -0
  41. package/dist/create-server.js +109 -0
  42. package/dist/create-server.js.map +1 -0
  43. package/dist/crypto/aes-ige.d.ts +3 -0
  44. package/dist/crypto/aes-ige.d.ts.map +1 -0
  45. package/dist/crypto/aes-ige.js +55 -0
  46. package/dist/crypto/aes-ige.js.map +1 -0
  47. package/dist/crypto/dh.d.ts +21 -0
  48. package/dist/crypto/dh.d.ts.map +1 -0
  49. package/dist/crypto/dh.js +99 -0
  50. package/dist/crypto/dh.js.map +1 -0
  51. package/dist/crypto/hashes.d.ts +6 -0
  52. package/dist/crypto/hashes.d.ts.map +1 -0
  53. package/dist/crypto/hashes.js +14 -0
  54. package/dist/crypto/hashes.js.map +1 -0
  55. package/dist/crypto/msg-key.d.ts +15 -0
  56. package/dist/crypto/msg-key.d.ts.map +1 -0
  57. package/dist/crypto/msg-key.js +24 -0
  58. package/dist/crypto/msg-key.js.map +1 -0
  59. package/dist/crypto/rsa.d.ts +27 -0
  60. package/dist/crypto/rsa.d.ts.map +1 -0
  61. package/dist/crypto/rsa.js +50 -0
  62. package/dist/crypto/rsa.js.map +1 -0
  63. package/dist/dispatch/dispatcher.d.ts +72 -0
  64. package/dist/dispatch/dispatcher.d.ts.map +1 -0
  65. package/dist/dispatch/dispatcher.js +503 -0
  66. package/dist/dispatch/dispatcher.js.map +1 -0
  67. package/dist/dispatch/forwarders/in-process.d.ts +12 -0
  68. package/dist/dispatch/forwarders/in-process.d.ts.map +1 -0
  69. package/dist/dispatch/forwarders/in-process.js +15 -0
  70. package/dist/dispatch/forwarders/in-process.js.map +1 -0
  71. package/dist/dispatch/forwarders/print.d.ts +14 -0
  72. package/dist/dispatch/forwarders/print.d.ts.map +1 -0
  73. package/dist/dispatch/forwarders/print.js +23 -0
  74. package/dist/dispatch/forwarders/print.js.map +1 -0
  75. package/dist/dispatch/rpc-forwarder.d.ts +14 -0
  76. package/dist/dispatch/rpc-forwarder.d.ts.map +1 -0
  77. package/dist/dispatch/rpc-forwarder.js +2 -0
  78. package/dist/dispatch/rpc-forwarder.js.map +1 -0
  79. package/dist/dispatch/types.d.ts +32 -0
  80. package/dist/dispatch/types.d.ts.map +1 -0
  81. package/dist/dispatch/types.js +23 -0
  82. package/dist/dispatch/types.js.map +1 -0
  83. package/dist/gateway.d.ts +57 -0
  84. package/dist/gateway.d.ts.map +1 -0
  85. package/dist/gateway.js +150 -0
  86. package/dist/gateway.js.map +1 -0
  87. package/dist/index.d.ts +7 -0
  88. package/dist/index.d.ts.map +1 -0
  89. package/dist/index.js +25 -0
  90. package/dist/index.js.map +1 -0
  91. package/dist/lib.d.ts +12 -0
  92. package/dist/lib.d.ts.map +1 -0
  93. package/dist/lib.js +13 -0
  94. package/dist/lib.js.map +1 -0
  95. package/dist/server/message-pipeline.d.ts +57 -0
  96. package/dist/server/message-pipeline.d.ts.map +1 -0
  97. package/dist/server/message-pipeline.js +199 -0
  98. package/dist/server/message-pipeline.js.map +1 -0
  99. package/dist/session/inbound-tracker.d.ts +118 -0
  100. package/dist/session/inbound-tracker.d.ts.map +1 -0
  101. package/dist/session/inbound-tracker.js +170 -0
  102. package/dist/session/inbound-tracker.js.map +1 -0
  103. package/dist/session/message-id.d.ts +19 -0
  104. package/dist/session/message-id.d.ts.map +1 -0
  105. package/dist/session/message-id.js +30 -0
  106. package/dist/session/message-id.js.map +1 -0
  107. package/dist/session/salts.d.ts +51 -0
  108. package/dist/session/salts.d.ts.map +1 -0
  109. package/dist/session/salts.js +132 -0
  110. package/dist/session/salts.js.map +1 -0
  111. package/dist/session/session-manager.d.ts +18 -0
  112. package/dist/session/session-manager.d.ts.map +1 -0
  113. package/dist/session/session-manager.js +43 -0
  114. package/dist/session/session-manager.js.map +1 -0
  115. package/dist/storage/index.d.ts +14 -0
  116. package/dist/storage/index.d.ts.map +1 -0
  117. package/dist/storage/index.js +16 -0
  118. package/dist/storage/index.js.map +1 -0
  119. package/dist/storage/memory.d.ts +3 -0
  120. package/dist/storage/memory.d.ts.map +1 -0
  121. package/dist/storage/memory.js +91 -0
  122. package/dist/storage/memory.js.map +1 -0
  123. package/dist/storage/mongo.d.ts +3 -0
  124. package/dist/storage/mongo.d.ts.map +1 -0
  125. package/dist/storage/mongo.js +175 -0
  126. package/dist/storage/mongo.js.map +1 -0
  127. package/dist/storage/types.d.ts +85 -0
  128. package/dist/storage/types.d.ts.map +1 -0
  129. package/dist/storage/types.js +3 -0
  130. package/dist/storage/types.js.map +1 -0
  131. package/dist/testkit.d.ts +11 -0
  132. package/dist/testkit.d.ts.map +1 -0
  133. package/dist/testkit.js +17 -0
  134. package/dist/testkit.js.map +1 -0
  135. package/dist/tl/codec.d.ts +37 -0
  136. package/dist/tl/codec.d.ts.map +1 -0
  137. package/dist/tl/codec.js +297 -0
  138. package/dist/tl/codec.js.map +1 -0
  139. package/dist/tl/layered-registry.d.ts +29 -0
  140. package/dist/tl/layered-registry.d.ts.map +1 -0
  141. package/dist/tl/layered-registry.js +118 -0
  142. package/dist/tl/layered-registry.js.map +1 -0
  143. package/dist/tl/protocol.d.ts +126 -0
  144. package/dist/tl/protocol.d.ts.map +1 -0
  145. package/dist/tl/protocol.js +22 -0
  146. package/dist/tl/protocol.js.map +1 -0
  147. package/dist/tl/reader.d.ts +30 -0
  148. package/dist/tl/reader.d.ts.map +1 -0
  149. package/dist/tl/reader.js +87 -0
  150. package/dist/tl/reader.js.map +1 -0
  151. package/dist/tl/registry.d.ts +39 -0
  152. package/dist/tl/registry.d.ts.map +1 -0
  153. package/dist/tl/registry.js +52 -0
  154. package/dist/tl/registry.js.map +1 -0
  155. package/dist/tl/writer.d.ts +24 -0
  156. package/dist/tl/writer.d.ts.map +1 -0
  157. package/dist/tl/writer.js +95 -0
  158. package/dist/tl/writer.js.map +1 -0
  159. package/dist/transport/connection-registry.d.ts +31 -0
  160. package/dist/transport/connection-registry.d.ts.map +1 -0
  161. package/dist/transport/connection-registry.js +84 -0
  162. package/dist/transport/connection-registry.js.map +1 -0
  163. package/dist/transport/connection.d.ts +62 -0
  164. package/dist/transport/connection.d.ts.map +1 -0
  165. package/dist/transport/connection.js +77 -0
  166. package/dist/transport/connection.js.map +1 -0
  167. package/dist/transport/framing.d.ts +39 -0
  168. package/dist/transport/framing.d.ts.map +1 -0
  169. package/dist/transport/framing.js +212 -0
  170. package/dist/transport/framing.js.map +1 -0
  171. package/dist/transport/proxy-protocol.d.ts +23 -0
  172. package/dist/transport/proxy-protocol.d.ts.map +1 -0
  173. package/dist/transport/proxy-protocol.js +108 -0
  174. package/dist/transport/proxy-protocol.js.map +1 -0
  175. package/dist/transport/server-common.d.ts +27 -0
  176. package/dist/transport/server-common.d.ts.map +1 -0
  177. package/dist/transport/server-common.js +27 -0
  178. package/dist/transport/server-common.js.map +1 -0
  179. package/dist/transport/tcp-server.d.ts +26 -0
  180. package/dist/transport/tcp-server.d.ts.map +1 -0
  181. package/dist/transport/tcp-server.js +91 -0
  182. package/dist/transport/tcp-server.js.map +1 -0
  183. package/dist/transport/ws-server.d.ts +19 -0
  184. package/dist/transport/ws-server.d.ts.map +1 -0
  185. package/dist/transport/ws-server.js +78 -0
  186. package/dist/transport/ws-server.js.map +1 -0
  187. package/dist/update-publisher.d.ts +34 -0
  188. package/dist/update-publisher.d.ts.map +1 -0
  189. package/dist/update-publisher.js +29 -0
  190. package/dist/update-publisher.js.map +1 -0
  191. package/dist/updates/mongo-update-log.d.ts +13 -0
  192. package/dist/updates/mongo-update-log.d.ts.map +1 -0
  193. package/dist/updates/mongo-update-log.js +39 -0
  194. package/dist/updates/mongo-update-log.js.map +1 -0
  195. package/dist/updates/presence-binder.d.ts +29 -0
  196. package/dist/updates/presence-binder.d.ts.map +1 -0
  197. package/dist/updates/presence-binder.js +36 -0
  198. package/dist/updates/presence-binder.js.map +1 -0
  199. package/dist/updates/presence.d.ts +31 -0
  200. package/dist/updates/presence.d.ts.map +1 -0
  201. package/dist/updates/presence.js +44 -0
  202. package/dist/updates/presence.js.map +1 -0
  203. package/dist/updates/push.d.ts +25 -0
  204. package/dist/updates/push.d.ts.map +1 -0
  205. package/dist/updates/push.js +72 -0
  206. package/dist/updates/push.js.map +1 -0
  207. package/dist/updates/redis-bus.d.ts +45 -0
  208. package/dist/updates/redis-bus.d.ts.map +1 -0
  209. package/dist/updates/redis-bus.js +59 -0
  210. package/dist/updates/redis-bus.js.map +1 -0
  211. package/dist/updates/redis-presence.d.ts +43 -0
  212. package/dist/updates/redis-presence.d.ts.map +1 -0
  213. package/dist/updates/redis-presence.js +65 -0
  214. package/dist/updates/redis-presence.js.map +1 -0
  215. package/dist/updates/render.d.ts +16 -0
  216. package/dist/updates/render.d.ts.map +1 -0
  217. package/dist/updates/render.js +46 -0
  218. package/dist/updates/render.js.map +1 -0
  219. package/dist/updates/router.d.ts +27 -0
  220. package/dist/updates/router.d.ts.map +1 -0
  221. package/dist/updates/router.js +36 -0
  222. package/dist/updates/router.js.map +1 -0
  223. package/dist/updates/types.d.ts +23 -0
  224. package/dist/updates/types.d.ts.map +1 -0
  225. package/dist/updates/types.js +2 -0
  226. package/dist/updates/types.js.map +1 -0
  227. package/dist/updates/update-bus.d.ts +29 -0
  228. package/dist/updates/update-bus.d.ts.map +1 -0
  229. package/dist/updates/update-bus.js +24 -0
  230. package/dist/updates/update-bus.js.map +1 -0
  231. package/dist/util/bytes.d.ts +12 -0
  232. package/dist/util/bytes.d.ts.map +1 -0
  233. package/dist/util/bytes.js +46 -0
  234. package/dist/util/bytes.js.map +1 -0
  235. package/package.json +84 -0
  236. package/src/auth/handshake.ts +262 -0
  237. package/src/auth/nonce-store.ts +39 -0
  238. package/src/bootstrap.ts +114 -0
  239. package/src/config.ts +103 -0
  240. package/src/core/context.ts +94 -0
  241. package/src/core/errors.ts +52 -0
  242. package/src/core/index.ts +4 -0
  243. package/src/core/rpc.ts +165 -0
  244. package/src/core/updates.ts +69 -0
  245. package/src/create-server.ts +181 -0
  246. package/src/crypto/aes-ige.ts +57 -0
  247. package/src/crypto/dh.ts +101 -0
  248. package/src/crypto/hashes.ts +17 -0
  249. package/src/crypto/msg-key.ts +29 -0
  250. package/src/crypto/rsa.ts +70 -0
  251. package/src/dispatch/dispatcher.ts +586 -0
  252. package/src/dispatch/forwarders/in-process.ts +14 -0
  253. package/src/dispatch/forwarders/print.ts +22 -0
  254. package/src/dispatch/rpc-forwarder.ts +15 -0
  255. package/src/dispatch/types.ts +60 -0
  256. package/src/gateway.ts +214 -0
  257. package/src/index.ts +53 -0
  258. package/src/lib.ts +24 -0
  259. package/src/server/message-pipeline.ts +256 -0
  260. package/src/session/inbound-tracker.ts +221 -0
  261. package/src/session/message-id.ts +43 -0
  262. package/src/session/salts.ts +162 -0
  263. package/src/session/session-manager.ts +66 -0
  264. package/src/storage/index.ts +26 -0
  265. package/src/storage/memory.ts +101 -0
  266. package/src/storage/mongo.ts +215 -0
  267. package/src/storage/types.ts +92 -0
  268. package/src/testkit.ts +19 -0
  269. package/src/tl/codec.ts +292 -0
  270. package/src/tl/layered-registry.ts +132 -0
  271. package/src/tl/protocol.ts +146 -0
  272. package/src/tl/reader.ts +99 -0
  273. package/src/tl/registry.ts +78 -0
  274. package/src/tl/writer.ts +104 -0
  275. package/src/transport/connection-registry.ts +91 -0
  276. package/src/transport/connection.ts +113 -0
  277. package/src/transport/framing.ts +223 -0
  278. package/src/transport/proxy-protocol.ts +109 -0
  279. package/src/transport/server-common.ts +49 -0
  280. package/src/transport/tcp-server.ts +102 -0
  281. package/src/transport/ws-server.ts +94 -0
  282. package/src/update-publisher.ts +47 -0
  283. package/src/updates/mongo-update-log.ts +49 -0
  284. package/src/updates/presence-binder.ts +51 -0
  285. package/src/updates/presence.ts +61 -0
  286. package/src/updates/push.ts +90 -0
  287. package/src/updates/redis-bus.ts +86 -0
  288. package/src/updates/redis-presence.ts +87 -0
  289. package/src/updates/render.ts +53 -0
  290. package/src/updates/router.ts +52 -0
  291. package/src/updates/types.ts +24 -0
  292. package/src/updates/update-bus.ts +49 -0
  293. package/src/util/bytes.ts +49 -0
@@ -0,0 +1,132 @@
1
+ import { existsSync, readdirSync, readFileSync } from 'node:fs'
2
+ import { join } from 'node:path'
3
+ import type { TlDef } from '@mt-tl/tl'
4
+ import { parseType } from '@mt-tl/tl'
5
+ import type { TlValue } from '@mt-tl/tl'
6
+
7
+ interface RawDef {
8
+ id: string
9
+ predicate?: string
10
+ method?: string
11
+ params: Array<{ name: string; type: string }>
12
+ type: string
13
+ }
14
+ interface RawSchema {
15
+ constructors: RawDef[]
16
+ methods: RawDef[]
17
+ }
18
+
19
+ interface LayerSchema {
20
+ byName: Map<string, TlDef>
21
+ }
22
+
23
+ /**
24
+ * Per-layer schema snapshots, used to encode a result/update with the
25
+ * constructor id and fields valid for a client's negotiated layer (decoding
26
+ * stays global by id). Also answers whether a value is representable at a layer
27
+ * — a pushed update whose type doesn't exist there must be substituted/dropped.
28
+ */
29
+ export class LayeredRegistry {
30
+ private layers = new Map<number, LayerSchema>()
31
+ private sortedLayers: number[] = []
32
+
33
+ addLayer(layer: number, defs: TlDef[]): void {
34
+ const byName = new Map<string, TlDef>()
35
+ for (const def of defs) if (!byName.has(def.name)) byName.set(def.name, def)
36
+ this.layers.set(layer, { byName })
37
+ this.sortedLayers = [...this.layers.keys()].sort((a, b) => a - b)
38
+ }
39
+
40
+ hasLayers(): boolean {
41
+ return this.sortedLayers.length > 0
42
+ }
43
+
44
+ /** Every distinct def across all layers (by id) — for the decode-union registry. */
45
+ allDefs(): TlDef[] {
46
+ const seen = new Set<string>()
47
+ const out: TlDef[] = []
48
+ for (const { byName } of this.layers.values()) {
49
+ for (const def of byName.values()) {
50
+ if (seen.has(def.id)) continue
51
+ seen.add(def.id)
52
+ out.push(def)
53
+ }
54
+ }
55
+ return out
56
+ }
57
+
58
+ layerNumbers(): number[] {
59
+ return [...this.sortedLayers]
60
+ }
61
+
62
+ /** Largest available layer <= requested (or the smallest if requested is below all). */
63
+ resolveLayer(requested: number): number | undefined {
64
+ if (!this.sortedLayers.length) return undefined
65
+ let best: number | undefined
66
+ for (const layer of this.sortedLayers) {
67
+ if (layer <= requested) best = layer
68
+ else break
69
+ }
70
+ return best ?? this.sortedLayers[0]
71
+ }
72
+
73
+ resolve(name: string, layer: number): TlDef | undefined {
74
+ const l = this.resolveLayer(layer)
75
+ if (l === undefined) return undefined
76
+ return this.layers.get(l)?.byName.get(name)
77
+ }
78
+
79
+ /** True iff every constructed type in the value tree exists at `layer`. */
80
+ representable(value: TlValue, layer: number): boolean {
81
+ if (value === null || value === undefined) return true
82
+ if (Array.isArray(value)) return value.every(v => this.representable(v, layer))
83
+ if (Buffer.isBuffer(value)) return true
84
+ if (typeof value === 'object') {
85
+ const obj = value as Record<string, unknown>
86
+ if (typeof obj._ === 'string') {
87
+ if (!this.resolve(obj._, layer)) return false
88
+ for (const [k, v] of Object.entries(obj)) {
89
+ if (k !== '_' && !this.representable(v as TlValue, layer)) return false
90
+ }
91
+ }
92
+ return true
93
+ }
94
+ return true // primitives, bigint, boolean, string, number
95
+ }
96
+ }
97
+
98
+ function rawToDef(raw: RawDef, kind: 'constructor' | 'method'): TlDef {
99
+ const id = raw.id.toLowerCase().padStart(8, '0')
100
+ return {
101
+ id,
102
+ idNum: parseInt(id, 16) >>> 0,
103
+ name: (raw.predicate ?? raw.method)!,
104
+ kind,
105
+ params: raw.params.map(p => ({ name: p.name, raw: p.type, type: parseType(p.type) })),
106
+ type: raw.type,
107
+ isProtocol: false,
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Loads `scheme_<layer>.json` snapshots from a directory into a
113
+ * {@link LayeredRegistry}. A missing directory yields an empty registry
114
+ * (layered encoding then disabled — the gateway falls back to single-schema).
115
+ */
116
+ export function loadLayeredRegistry(dir: string): LayeredRegistry {
117
+ const registry = new LayeredRegistry()
118
+ if (!existsSync(dir)) return registry
119
+
120
+ for (const file of readdirSync(dir)) {
121
+ const m = file.match(/_(\d+)\.json$/)
122
+ if (!m) continue
123
+ const layer = Number(m[1])
124
+ const raw = JSON.parse(readFileSync(join(dir, file), 'utf-8')) as RawSchema
125
+ const defs = [
126
+ ...raw.constructors.map(c => rawToDef(c, 'constructor')),
127
+ ...(raw.methods ?? []).map(c => rawToDef(c, 'method')),
128
+ ]
129
+ registry.addLayer(layer, defs)
130
+ }
131
+ return registry
132
+ }
@@ -0,0 +1,146 @@
1
+ import type { TlObject } from '@mt-tl/tl'
2
+
3
+ /**
4
+ * Typed views over the immutable MTProto protocol/service constructors.
5
+ *
6
+ * These are the ~30 types the gateway reads/writes directly during the
7
+ * handshake, session setup and service-message handling. They are still
8
+ * encoded/decoded by the generic {@link TlCodec} (the constructors live in the
9
+ * registry); these interfaces just give the gateway code type-safety when it
10
+ * builds or inspects them. `_` is the constructor predicate.
11
+ */
12
+
13
+ // --- auth key exchange ------------------------------------------------------
14
+
15
+ export interface ResPQ extends TlObject {
16
+ _: 'resPQ'
17
+ nonce: Buffer // int128
18
+ server_nonce: Buffer // int128
19
+ pq: Buffer // bytes
20
+ server_public_key_fingerprints: bigint[] // Vector<long>
21
+ }
22
+
23
+ export interface PQInnerData extends TlObject {
24
+ _: 'p_q_inner_data' | 'p_q_inner_data_dc' | 'p_q_inner_data_temp' | 'p_q_inner_data_temp_dc'
25
+ pq: Buffer
26
+ p: Buffer
27
+ q: Buffer
28
+ nonce: Buffer
29
+ server_nonce: Buffer
30
+ new_nonce: Buffer // int256
31
+ dc?: number
32
+ expires_in?: number
33
+ }
34
+
35
+ export interface ServerDHParamsOk extends TlObject {
36
+ _: 'server_DH_params_ok'
37
+ nonce: Buffer
38
+ server_nonce: Buffer
39
+ encrypted_answer: Buffer
40
+ }
41
+
42
+ export interface ServerDHParamsFail extends TlObject {
43
+ _: 'server_DH_params_fail'
44
+ nonce: Buffer
45
+ server_nonce: Buffer
46
+ new_nonce_hash: Buffer
47
+ }
48
+
49
+ export interface ServerDHInnerData extends TlObject {
50
+ _: 'server_DH_inner_data'
51
+ nonce: Buffer
52
+ server_nonce: Buffer
53
+ g: number
54
+ dh_prime: Buffer
55
+ g_a: Buffer
56
+ server_time: number
57
+ }
58
+
59
+ export interface ClientDHInnerData extends TlObject {
60
+ _: 'client_DH_inner_data'
61
+ nonce: Buffer
62
+ server_nonce: Buffer
63
+ retry_id: bigint
64
+ g_b: Buffer
65
+ }
66
+
67
+ export interface DhGenOk extends TlObject {
68
+ _: 'dh_gen_ok'
69
+ nonce: Buffer
70
+ server_nonce: Buffer
71
+ new_nonce_hash1: Buffer
72
+ }
73
+
74
+ // --- session / service ------------------------------------------------------
75
+
76
+ export interface NewSessionCreated extends TlObject {
77
+ _: 'new_session_created'
78
+ first_msg_id: bigint
79
+ unique_id: bigint
80
+ server_salt: bigint
81
+ }
82
+
83
+ export interface RpcResult extends TlObject {
84
+ _: 'rpc_result'
85
+ req_msg_id: bigint
86
+ result: TlObject | boolean
87
+ }
88
+
89
+ export interface RpcError extends TlObject {
90
+ _: 'rpc_error'
91
+ error_code: number
92
+ error_message: string
93
+ }
94
+
95
+ export interface Pong extends TlObject {
96
+ _: 'pong'
97
+ msg_id: bigint
98
+ ping_id: bigint
99
+ }
100
+
101
+ export interface MsgsAck extends TlObject {
102
+ _: 'msgs_ack'
103
+ msg_ids: bigint[]
104
+ }
105
+
106
+ export interface BadServerSalt extends TlObject {
107
+ _: 'bad_server_salt'
108
+ bad_msg_id: bigint
109
+ bad_msg_seqno: number
110
+ error_code: number
111
+ new_server_salt: bigint
112
+ }
113
+
114
+ export interface BadMsgNotification extends TlObject {
115
+ _: 'bad_msg_notification'
116
+ bad_msg_id: bigint
117
+ bad_msg_seqno: number
118
+ error_code: number
119
+ }
120
+
121
+ export interface MsgContainer extends TlObject {
122
+ _: 'msg_container'
123
+ messages: TlObject[]
124
+ }
125
+
126
+ /** Well-known protocol constructor predicates, for branching in the pipeline. */
127
+ export const Predicate = {
128
+ req_pq: 'req_pq',
129
+ req_pq_multi: 'req_pq_multi',
130
+ req_DH_params: 'req_DH_params',
131
+ set_client_DH_params: 'set_client_DH_params',
132
+ ping: 'ping',
133
+ ping_delay_disconnect: 'ping_delay_disconnect',
134
+ msgs_ack: 'msgs_ack',
135
+ msgs_state_req: 'msgs_state_req',
136
+ rpc_drop_answer: 'rpc_drop_answer',
137
+ destroy_session: 'destroy_session',
138
+ get_future_salts: 'get_future_salts',
139
+ http_wait: 'http_wait',
140
+ msg_container: 'msg_container',
141
+ gzip_packed: 'gzip_packed',
142
+ invokeWithLayer: 'invokeWithLayer',
143
+ invokeWithoutUpdates: 'invokeWithoutUpdates',
144
+ invokeAfterMsg: 'invokeAfterMsg',
145
+ initConnection: 'initConnection',
146
+ } as const
@@ -0,0 +1,99 @@
1
+ import { toBigIntLE } from '../util/bytes.js'
2
+
3
+ export const BOOL_TRUE_ID = 0x997275b5
4
+ export const BOOL_FALSE_ID = 0xbc799737
5
+ export const VECTOR_ID = 0x1cb5c415
6
+
7
+ /**
8
+ * Low-level reader for the TL wire format. Exposes only primitive reads; the
9
+ * codec drives constructor-id dispatch, vectors and flags. Ported from the
10
+ * existing `protocolBuffer.js` (same little-endian / padding rules).
11
+ */
12
+ export class TlReader {
13
+ private pos = 0
14
+
15
+ constructor(private readonly buf: Buffer) {}
16
+
17
+ get position(): number {
18
+ return this.pos
19
+ }
20
+
21
+ get remaining(): number {
22
+ return this.buf.length - this.pos
23
+ }
24
+
25
+ private expect(len: number): void {
26
+ if (this.buf.length < this.pos + len) {
27
+ throw new Error(`TL read out of bounds: need ${len}, have ${this.remaining}`)
28
+ }
29
+ }
30
+
31
+ read(len: number): Buffer {
32
+ this.expect(len)
33
+ const out = this.buf.subarray(this.pos, this.pos + len)
34
+ this.pos += len
35
+ return out
36
+ }
37
+
38
+ skip(len: number): void {
39
+ this.expect(len)
40
+ this.pos += len
41
+ }
42
+
43
+ readUInt8(): number {
44
+ this.expect(1)
45
+ return this.buf.readUInt8(this.pos++)
46
+ }
47
+
48
+ readUInt32(): number {
49
+ this.expect(4)
50
+ const v = this.buf.readUInt32LE(this.pos)
51
+ this.pos += 4
52
+ return v
53
+ }
54
+
55
+ readInt32(): number {
56
+ this.expect(4)
57
+ const v = this.buf.readInt32LE(this.pos)
58
+ this.pos += 4
59
+ return v
60
+ }
61
+
62
+ /** TL `long` — 8 bytes LE, read as an unsigned bigint (matches existing server). */
63
+ readLong(): bigint {
64
+ return toBigIntLE(this.read(8))
65
+ }
66
+
67
+ readDouble(): number {
68
+ this.expect(8)
69
+ const v = this.buf.readDoubleLE(this.pos)
70
+ this.pos += 8
71
+ return v
72
+ }
73
+
74
+ readInt128(): Buffer {
75
+ return Buffer.from(this.read(16))
76
+ }
77
+
78
+ readInt256(): Buffer {
79
+ return Buffer.from(this.read(32))
80
+ }
81
+
82
+ /** TL `bytes`/`string` — length-prefixed with 4-byte alignment padding. */
83
+ readBytes(): Buffer {
84
+ let extra = 1
85
+ let len = this.readUInt8()
86
+ if (len >= 254) {
87
+ len = this.readUInt8() + this.readUInt8() * 256 + this.readUInt8() * 65536
88
+ extra = 4
89
+ }
90
+ const out = Buffer.from(this.read(len))
91
+ const rem = (len + extra) % 4
92
+ if (rem > 0) this.skip(4 - rem)
93
+ return out
94
+ }
95
+
96
+ readString(): string {
97
+ return this.readBytes().toString('utf-8')
98
+ }
99
+ }
@@ -0,0 +1,78 @@
1
+ import type { TlDef } from '@mt-tl/tl'
2
+ import { parseSchemaDir } from '@mt-tl/tl'
3
+
4
+ /**
5
+ * Resolves TL definitions both ways: by wire constructor id (for decoding) and
6
+ * by name (for encoding a result the backend returned as `{ _: name, ... }`).
7
+ *
8
+ * Hand-written protocol/service classes register a {@link ProtocolCodec} for
9
+ * their id so the pipeline can read/write them with typed code; everything else
10
+ * falls through to the generic, IR-driven codec.
11
+ */
12
+ export interface ProtocolCodec {
13
+ id: string
14
+ name: string
15
+ read(reader: import('./reader.js').TlReader): unknown
16
+ write(writer: import('./writer.js').TlWriter, value: unknown): void
17
+ }
18
+
19
+ export class TlRegistry {
20
+ private byId = new Map<number, TlDef>()
21
+ private byName = new Map<string, TlDef>()
22
+ private protocolCodecs = new Map<number, ProtocolCodec>()
23
+
24
+ register(def: TlDef): void {
25
+ if (!this.byId.has(def.idNum)) this.byId.set(def.idNum, def)
26
+ if (!this.byName.has(def.name)) this.byName.set(def.name, def)
27
+ }
28
+
29
+ registerProtocolCodec(codec: ProtocolCodec): void {
30
+ this.protocolCodecs.set(parseInt(codec.id, 16) >>> 0, codec)
31
+ }
32
+
33
+ getById(idNum: number): TlDef | undefined {
34
+ return this.byId.get(idNum >>> 0)
35
+ }
36
+
37
+ getByName(name: string): TlDef | undefined {
38
+ return this.byName.get(name)
39
+ }
40
+
41
+ getProtocolCodec(idNum: number): ProtocolCodec | undefined {
42
+ return this.protocolCodecs.get(idNum >>> 0)
43
+ }
44
+
45
+ get size(): number {
46
+ return this.byId.size
47
+ }
48
+ }
49
+
50
+ export interface LoadSchemaResult {
51
+ registry: TlRegistry
52
+ constructors: number
53
+ methods: number
54
+ crcMismatches: number
55
+ }
56
+
57
+ /**
58
+ * Loads + merges one or more `.tl` schema directories into a single registry.
59
+ * The gateway merges the framework's protocol schema with the app's business
60
+ * schema — pass protocol first (earlier dirs win a name/id clash).
61
+ */
62
+ export function loadSchema(dirs: string | string[]): LoadSchemaResult {
63
+ const list = Array.isArray(dirs) ? dirs : [dirs]
64
+ const registry = new TlRegistry()
65
+ let constructors = 0
66
+ let methods = 0
67
+ let crcMismatches = 0
68
+ for (const dir of list) {
69
+ const parsed = parseSchemaDir(dir)
70
+ crcMismatches += parsed.crcMismatches.length
71
+ for (const def of parsed.defs) {
72
+ registry.register(def)
73
+ if (def.kind === 'method') methods++
74
+ else constructors++
75
+ }
76
+ }
77
+ return { registry, constructors, methods, crcMismatches }
78
+ }
@@ -0,0 +1,104 @@
1
+ import { toBufferLE } from '../util/bytes.js'
2
+ import { BOOL_FALSE_ID, BOOL_TRUE_ID } from './reader.js'
3
+
4
+ /**
5
+ * Low-level writer for the TL wire format. Grows its backing buffer in 1 KiB
6
+ * steps. Ported from the existing `protocolBuffer.js`.
7
+ */
8
+ export class TlWriter {
9
+ private buf: Buffer
10
+ private pos = 0
11
+ private readonly step = 1024
12
+
13
+ constructor(preallocate = 1024) {
14
+ this.buf = Buffer.allocUnsafe(preallocate)
15
+ }
16
+
17
+ private alloc(len: number): void {
18
+ if (this.buf.length >= this.pos + len) return
19
+ let target = this.buf.length + this.step
20
+ while (target < this.pos + len) target += this.step
21
+ const next = Buffer.allocUnsafe(target)
22
+ this.buf.copy(next, 0, 0, this.pos)
23
+ this.buf = next
24
+ }
25
+
26
+ writeRaw(val: Buffer, len = val.length): this {
27
+ this.alloc(len)
28
+ val.copy(this.buf, this.pos, 0, len)
29
+ this.pos += len
30
+ return this
31
+ }
32
+
33
+ writeUInt8(val: number): this {
34
+ this.alloc(1)
35
+ this.buf.writeUInt8(val & 0xff, this.pos++)
36
+ return this
37
+ }
38
+
39
+ writeUInt32(val: number): this {
40
+ this.alloc(4)
41
+ this.buf.writeUInt32LE(val >>> 0, this.pos)
42
+ this.pos += 4
43
+ return this
44
+ }
45
+
46
+ writeInt32(val: number): this {
47
+ this.alloc(4)
48
+ this.buf.writeInt32LE(val | 0, this.pos)
49
+ this.pos += 4
50
+ return this
51
+ }
52
+
53
+ writeLong(val: bigint): this {
54
+ return this.writeRaw(toBufferLE(BigInt(val), 8), 8)
55
+ }
56
+
57
+ writeDouble(val: number): this {
58
+ this.alloc(8)
59
+ this.buf.writeDoubleLE(val, this.pos)
60
+ this.pos += 8
61
+ return this
62
+ }
63
+
64
+ /** 16/32 raw bytes for int128/int256 (left-padded if a short buffer is given). */
65
+ writeFixed(val: Buffer, len: number): this {
66
+ if (val.length === len) return this.writeRaw(val, len)
67
+ const padded = Buffer.alloc(len)
68
+ val.copy(padded, len - val.length)
69
+ return this.writeRaw(padded, len)
70
+ }
71
+
72
+ writeBytes(val: Buffer): this {
73
+ let len: number
74
+ if (val.length < 254) {
75
+ this.writeUInt8(val.length)
76
+ len = 1
77
+ } else {
78
+ // 0xfe marker + 3-byte LE length
79
+ this.writeUInt8(254)
80
+ this.writeUInt32(val.length)
81
+ this.pos--
82
+ len = 4
83
+ }
84
+ this.writeRaw(val)
85
+ len += val.length
86
+ while (len % 4 !== 0) {
87
+ this.writeUInt8(0)
88
+ len++
89
+ }
90
+ return this
91
+ }
92
+
93
+ writeString(val: string): this {
94
+ return this.writeBytes(val ? Buffer.from(val, 'utf-8') : Buffer.alloc(0))
95
+ }
96
+
97
+ writeBool(val: boolean): this {
98
+ return this.writeUInt32(val ? BOOL_TRUE_ID : BOOL_FALSE_ID)
99
+ }
100
+
101
+ toBuffer(): Buffer {
102
+ return Buffer.from(this.buf.subarray(0, this.pos))
103
+ }
104
+ }
@@ -0,0 +1,91 @@
1
+ import type { Connection } from './connection.js'
2
+
3
+ /**
4
+ * Per-node map of authorized subject -> live connections, used to deliver
5
+ * server-pushed updates to the right sockets. A subject (internal user id) may
6
+ * have several connections (multiple devices/sessions) on one node.
7
+ */
8
+ export class ConnectionRegistry {
9
+ private bySubject = new Map<string, Set<Connection>>()
10
+ private connSubject = new Map<Connection, string>()
11
+ private byAuthKey = new Map<string, Set<Connection>>()
12
+ private connAuthKey = new Map<Connection, string>()
13
+
14
+ register(subject: string, conn: Connection): void {
15
+ const prev = this.connSubject.get(conn)
16
+ if (prev !== undefined && prev !== subject) removeFrom(this.bySubject, prev, conn)
17
+ this.connSubject.set(conn, subject)
18
+ addTo(this.bySubject, subject, conn)
19
+ }
20
+
21
+ /** Index a connection by its auth key, for delivery before/without a subject. */
22
+ registerAuthKey(authKeyId: string, conn: Connection): void {
23
+ if (this.connAuthKey.get(conn) === authKeyId) return
24
+ this.connAuthKey.set(conn, authKeyId)
25
+ addTo(this.byAuthKey, authKeyId, conn)
26
+ }
27
+
28
+ /** Remove a connection from both indexes; returns the keys it was held under. */
29
+ unregister(conn: Connection): { subject?: string; authKeyId?: string } {
30
+ const subject = this.connSubject.get(conn)
31
+ if (subject !== undefined) {
32
+ this.connSubject.delete(conn)
33
+ removeFrom(this.bySubject, subject, conn)
34
+ }
35
+ const authKeyId = this.connAuthKey.get(conn)
36
+ if (authKeyId !== undefined) {
37
+ this.connAuthKey.delete(conn)
38
+ removeFrom(this.byAuthKey, authKeyId, conn)
39
+ }
40
+ return { subject, authKeyId }
41
+ }
42
+
43
+ subjectOf(conn: Connection): string | undefined {
44
+ return this.connSubject.get(conn)
45
+ }
46
+
47
+ hasSubject(subject: string): boolean {
48
+ return this.bySubject.has(subject)
49
+ }
50
+ hasAuthKey(authKeyId: string): boolean {
51
+ return this.byAuthKey.has(authKeyId)
52
+ }
53
+
54
+ getBySubject(subject: string): Connection[] {
55
+ const set = this.bySubject.get(subject)
56
+ return set ? [...set] : []
57
+ }
58
+ getByAuthKey(authKeyId: string): Connection[] {
59
+ const set = this.byAuthKey.get(authKeyId)
60
+ return set ? [...set] : []
61
+ }
62
+
63
+ /** All subjects with at least one local connection (for presence heartbeat). */
64
+ subjects(): string[] {
65
+ return [...this.bySubject.keys()]
66
+ }
67
+ /** All auth keys with at least one local connection (for presence heartbeat). */
68
+ authKeys(): string[] {
69
+ return [...this.byAuthKey.keys()]
70
+ }
71
+
72
+ get size(): number {
73
+ return this.connSubject.size
74
+ }
75
+ }
76
+
77
+ function addTo<K>(map: Map<K, Set<Connection>>, key: K, conn: Connection): void {
78
+ let set = map.get(key)
79
+ if (!set) {
80
+ set = new Set()
81
+ map.set(key, set)
82
+ }
83
+ set.add(conn)
84
+ }
85
+
86
+ function removeFrom<K>(map: Map<K, Set<Connection>>, key: K, conn: Connection): void {
87
+ const set = map.get(key)
88
+ if (!set) return
89
+ set.delete(conn)
90
+ if (set.size === 0) map.delete(key)
91
+ }