@aezakmiproject/telemt-sdk-nest 1.0.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 (3) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +200 -0
  3. package/package.json +73 -0
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026 ABRAMOVI4CH
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,200 @@
1
+ # @aezakmiproject/telemt-sdk-nest
2
+
3
+ [![License: ISC](https://img.shields.io/badge/license-ISC-blue.svg)](LICENSE)
4
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D22-brightgreen.svg)](package.json)
5
+
6
+ NestJS module for the [Telemt](https://github.com/telemt/telemt) Control API.
7
+
8
+ Wraps [`@aezakmiproject/telemt-sdk`](https://www.npmjs.com/package/@aezakmiproject/telemt-sdk) in an injectable provider: one configured `TelemtAPI` per registration, resolved through Nest DI, with sync and async registration and an opt-in helper that turns the SDK's no-throw envelope into a thrown exception.
9
+
10
+ Requires **Node.js 18+** (the SDK uses global `fetch` and `AbortSignal.timeout`).
11
+
12
+ This project is an independent open-source client. It is not affiliated with Telegram or the Telemt authors.
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ npm install @aezakmiproject/telemt-sdk-nest
18
+ # or
19
+ pnpm add @aezakmiproject/telemt-sdk-nest
20
+ ```
21
+
22
+ `@nestjs/common` and `@nestjs/core` are expected to be present in the host application.
23
+
24
+ ## Quick start
25
+
26
+ Register the module once, then inject `TelemtService` anywhere.
27
+
28
+ ```ts
29
+ import { Module } from '@nestjs/common';
30
+ import { TelemtModule } from '@aezakmiproject/telemt-sdk-nest';
31
+
32
+ @Module({
33
+ imports: [
34
+ TelemtModule.forRoot({
35
+ apiUrl: 'http://127.0.0.1:9091',
36
+ auth: 'telemt-sdk-dev-token', // exact value of [server.api].auth_header
37
+ }),
38
+ ],
39
+ })
40
+ export class AppModule {}
41
+ ```
42
+
43
+ ```ts
44
+ import { Injectable } from '@nestjs/common';
45
+ import { TelemtService } from '@aezakmiproject/telemt-sdk-nest';
46
+
47
+ @Injectable()
48
+ export class UsersReport {
49
+ constructor(private readonly telemt: TelemtService) {}
50
+
51
+ async listUsernames(): Promise<string[]> {
52
+ const users = await this.telemt.unwrap(this.telemt.users.getAll());
53
+ return users.map((user) => user.username);
54
+ }
55
+ }
56
+ ```
57
+
58
+ `auth` is sent verbatim as the `Authorization` header. Telemt does a constant-time string comparison rather than Bearer/OAuth parsing — do not prefix `Bearer ` unless that prefix is literally part of `auth_header`.
59
+
60
+ ## Async registration
61
+
62
+ Use `forRootAsync` when the options come from config, a secret store, or anything else resolved at boot. Exactly one of `useFactory`, `useClass`, or `useExisting` is required; passing none throws at module-construction time.
63
+
64
+ ### useFactory
65
+
66
+ ```ts
67
+ import { ConfigModule, ConfigService } from '@nestjs/config';
68
+ import { TelemtModule } from '@aezakmiproject/telemt-sdk-nest';
69
+
70
+ TelemtModule.forRootAsync({
71
+ imports: [ConfigModule],
72
+ inject: [ConfigService],
73
+ useFactory: (config: ConfigService) => ({
74
+ apiUrl: config.getOrThrow<string>('TELEMT_API_URL'),
75
+ auth: config.getOrThrow<string>('TELEMT_AUTH'),
76
+ }),
77
+ });
78
+ ```
79
+
80
+ The factory may be `async`; the client is not constructed until it resolves.
81
+
82
+ ### useClass
83
+
84
+ ```ts
85
+ import { Injectable } from '@nestjs/common';
86
+ import {
87
+ TelemtModule,
88
+ type TelemtModuleOptions,
89
+ type TelemtOptionsFactory,
90
+ } from '@aezakmiproject/telemt-sdk-nest';
91
+
92
+ @Injectable()
93
+ export class TelemtConfig implements TelemtOptionsFactory {
94
+ async createTelemtOptions(): Promise<TelemtModuleOptions> {
95
+ return { apiUrl: process.env.TELEMT_API_URL!, auth: await readSecret() };
96
+ }
97
+ }
98
+
99
+ TelemtModule.forRootAsync({ useClass: TelemtConfig });
100
+ ```
101
+
102
+ `useClass` is instantiated by this module, so the class does not need to be provided anywhere else.
103
+
104
+ ### useExisting
105
+
106
+ Reuse a provider the host application already owns, rather than getting a second instance:
107
+
108
+ ```ts
109
+ TelemtModule.forRootAsync({
110
+ imports: [TelemtConfigModule], // must export TelemtConfig
111
+ useExisting: TelemtConfig,
112
+ });
113
+ ```
114
+
115
+ ## `TelemtService`
116
+
117
+ Thin, stateless facade over the underlying `TelemtAPI`. Each getter forwards to the client on every access.
118
+
119
+ | Member | What it covers |
120
+ | --- | --- |
121
+ | `users` | CRUD, enable/disable, rotate secret, reset quota |
122
+ | `config` | Read / merge-patch `config.toml` |
123
+ | `system` | Build info, reload, `waitForReload` |
124
+ | `health` | Liveness and readiness |
125
+ | `stats` | Counters, upstreams, DCs, ME writers |
126
+ | `runtime` | Gates, ME pool/quality, events, TLS fingerprints |
127
+ | `security` | API posture and IP whitelist |
128
+ | `limits` | Effective timeouts / pool / per-user limits |
129
+ | `client` | The raw `TelemtAPI`, for anything not surfaced above |
130
+ | `unwrap(res)` | Returns `data`, or throws `TelemtApiException` |
131
+
132
+ The full method list and request/response types live in the [SDK's API reference](https://github.com/AezakmiProject/telemt-sdk/blob/main/docs/api.md).
133
+
134
+ ## Responses and `unwrap`
135
+
136
+ SDK methods never throw. Every call returns a flat envelope, and transport failures arrive there too, under an `sdk_*` code:
137
+
138
+ ```ts
139
+ interface ISdkResponse<T> {
140
+ isOk: boolean;
141
+ data?: T;
142
+ code?: TelemtErrorCode;
143
+ message?: string;
144
+ revision?: string; // success only — SHA-256 of config.toml
145
+ requestId?: number; // Telemt-side errors only
146
+ }
147
+ ```
148
+
149
+ Checking `isOk` by hand keeps that behaviour:
150
+
151
+ ```ts
152
+ const res = await this.telemt.users.getAll();
153
+ if (!res.isOk) {
154
+ throw new Error(`${res.code}: ${res.message}`);
155
+ }
156
+ // `data` is still `UserInfo[] | undefined` here — see the narrowing note below
157
+ return (res.data ?? []).map((user) => user.username);
158
+ ```
159
+
160
+ `ISdkResponse` is not a discriminated union, so testing `isOk` does **not** narrow `data`; under `strict` you still have to handle the `undefined`. `unwrap` exists to collapse that:
161
+
162
+ ```ts
163
+ const users = await this.telemt.unwrap(this.telemt.users.getAll());
164
+ // users is UserInfo[] — the failure branch has already thrown, and it narrows
165
+ ```
166
+
167
+ It accepts either a response or a promise of one, and on `isOk: false` throws `TelemtApiException`:
168
+
169
+ ```ts
170
+ class TelemtApiException extends Error {
171
+ readonly code?: string; // server or `sdk_*` code
172
+ readonly requestId?: number; // Telemt-side errors only, absent on transport failures
173
+ }
174
+ ```
175
+
176
+ Note that `unwrap` throws whenever `isOk` is false, even if the response also carries partial `data`.
177
+
178
+ A `202` from a user mutation is still `isOk: true` — the write is on disk, but check `UserInfo.in_runtime` (or call `system.reload()`) before treating the user as live.
179
+
180
+ ## Scope
181
+
182
+ Each registration builds its own client, so registering the module in two places gives two independent `TelemtAPI` instances. The module is **not** global: every consuming module must import it, and only `TelemtService` is exported — `TELEMT_CLIENT` and `TELEMT_MODULE_OPTIONS` stay internal to the module.
183
+
184
+ WEB-proxy session control (`api.web`) is not implemented in the SDK, so it is deliberately not surfaced here.
185
+
186
+ ## Development
187
+
188
+ ```bash
189
+ pnpm install
190
+ pnpm test # vitest, unit tests
191
+ pnpm test:cov # with V8 coverage
192
+ pnpm typecheck # tsc, sources + specs
193
+ pnpm build # tsc -> dist/
194
+ ```
195
+
196
+ Tests run on [Vitest](https://vitest.dev) rather than Jest because this project pins `typescript@7` (the native compiler), which no longer ships the JS compiler API `ts-jest` needs. See [docs/decisions.md](docs/decisions.md).
197
+
198
+ ## License
199
+
200
+ [ISC](LICENSE)
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@aezakmiproject/telemt-sdk-nest",
3
+ "version": "1.0.0",
4
+ "description": "NestJS module for the Telemt MTProto proxy Control API: injectable typed client, sync and async registration, no-throw responses, Node 22+",
5
+ "homepage": "https://github.com/AezakmiProject/telemt-sdk-nest#readme",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/AezakmiProject/telemt-sdk-nest.git"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/AezakmiProject/telemt-sdk-nest/issues"
12
+ },
13
+ "main": "dist/index.js",
14
+ "types": "dist/index.d.ts",
15
+ "files": [
16
+ "dist/**",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "keywords": [
21
+ "telemt",
22
+ "telemt-sdk",
23
+ "nestjs",
24
+ "nest",
25
+ "nestjs-module",
26
+ "nestjs-library",
27
+ "nestjs-sdk",
28
+ "mtproto",
29
+ "mtproto-proxy",
30
+ "telegram",
31
+ "telegram-proxy",
32
+ "proxy",
33
+ "proxy-server",
34
+ "typescript",
35
+ "typescript-sdk",
36
+ "sdk",
37
+ "api-client",
38
+ "rest-client",
39
+ "dependency-injection",
40
+ "dynamic-module",
41
+ "node",
42
+ "aezakmi"
43
+ ],
44
+ "author": "ABRAMOVI4CH",
45
+ "license": "ISC",
46
+ "engines": {
47
+ "node": ">=22.12"
48
+ },
49
+ "devDependencies": {
50
+ "@aezakmiproject/telemt-sdk": "^1.2.0",
51
+ "@nestjs/common": "^12.0.1",
52
+ "@nestjs/core": "^12.0.1",
53
+ "@nestjs/testing": "^12.0.1",
54
+ "@types/node": "^26.4.1",
55
+ "@vitest/coverage-v8": "^5.0.0",
56
+ "reflect-metadata": "^0.2.2",
57
+ "rxjs": "^7.8.2",
58
+ "typescript": "^7.0.2",
59
+ "vitest": "^5.0.0"
60
+ },
61
+ "peerDependencies": {
62
+ "@aezakmiproject/telemt-sdk": "^1.2.0",
63
+ "@nestjs/common": "^12.0.0",
64
+ "reflect-metadata": "^0.2.0"
65
+ },
66
+ "scripts": {
67
+ "build": "tsc",
68
+ "test": "vitest run",
69
+ "test:watch": "vitest",
70
+ "test:cov": "vitest run --coverage",
71
+ "typecheck": "tsc -p tsconfig.spec.json"
72
+ }
73
+ }