gramobase 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.
@@ -0,0 +1,132 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in the
6
+ **gramobase** community a harassment-free experience for everyone, regardless of
7
+ age, body size, visible or invisible disability, ethnicity, sex characteristics,
8
+ gender identity and expression, level of experience, education, socio-economic
9
+ status, nationality, personal appearance, race, caste, color, religion, or
10
+ sexual identity and orientation.
11
+
12
+ We pledge to act and interact in ways that contribute to an open, welcoming,
13
+ diverse, inclusive, and healthy community.
14
+
15
+ ---
16
+
17
+ ## Our Standards
18
+
19
+ **Examples of behavior that contributes to a positive environment:**
20
+
21
+ - Using welcoming and inclusive language
22
+ - Being respectful of differing viewpoints and experiences
23
+ - Gracefully accepting constructive criticism
24
+ - Focusing on what is best for the community
25
+ - Showing empathy towards other community members
26
+ - Giving credit where credit is due
27
+
28
+ **Examples of unacceptable behavior:**
29
+
30
+ - The use of sexualized language or imagery, and sexual attention or advances of any kind
31
+ - Trolling, insulting or derogatory comments, and personal or political attacks
32
+ - Public or private harassment
33
+ - Publishing others' private information, such as a physical or email address, without explicit permission
34
+ - Deliberate intimidation, stalking, or following
35
+ - Other conduct which could reasonably be considered inappropriate in a professional setting
36
+
37
+ ---
38
+
39
+ ## Enforcement Responsibilities
40
+
41
+ Community leaders are responsible for clarifying and enforcing our standards of
42
+ acceptable behavior and will take appropriate and fair corrective action in
43
+ response to any behavior that they deem inappropriate, threatening, offensive,
44
+ or harmful.
45
+
46
+ Community leaders have the right and responsibility to remove, edit, or reject
47
+ comments, commits, code, wiki edits, issues, and other contributions that are
48
+ not aligned to this Code of Conduct, and will communicate reasons for moderation
49
+ decisions when appropriate.
50
+
51
+ ---
52
+
53
+ ## Scope
54
+
55
+ This Code of Conduct applies within all community spaces — including the GitHub
56
+ repository, issue tracker, pull requests, discussions, and any other official
57
+ communication channels — and also applies when an individual is officially
58
+ representing the community in public spaces.
59
+
60
+ ---
61
+
62
+ ## Reporting
63
+
64
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
65
+ reported by opening a **private** GitHub issue or contacting the maintainers
66
+ directly. All complaints will be reviewed and investigated promptly and fairly.
67
+
68
+ All community leaders are obligated to respect the privacy and security of the
69
+ reporter of any incident.
70
+
71
+ ---
72
+
73
+ ## Enforcement Guidelines
74
+
75
+ Community leaders will follow these guidelines in determining the consequences
76
+ for any action they deem in violation of this Code of Conduct:
77
+
78
+ ### 1. Correction
79
+
80
+ **Impact:** Use of inappropriate language or other behavior deemed unprofessional.
81
+
82
+ **Consequence:** A private, written warning from community leaders, providing
83
+ clarity around the nature of the violation and an explanation of why the
84
+ behavior was inappropriate. A public apology may be requested.
85
+
86
+ ### 2. Warning
87
+
88
+ **Impact:** A violation through a single incident or series of actions.
89
+
90
+ **Consequence:** A warning with consequences for continued behavior. No
91
+ interaction with the people involved — including unsolicited interaction with
92
+ those enforcing the Code of Conduct — for a specified period of time. This
93
+ includes avoiding interactions in community spaces as well as external channels
94
+ like social media. Violating these terms may lead to a temporary or permanent
95
+ ban.
96
+
97
+ ### 3. Temporary Ban
98
+
99
+ **Impact:** A serious violation of community standards, including sustained
100
+ inappropriate behavior.
101
+
102
+ **Consequence:** A temporary ban from any sort of interaction or public
103
+ communication with the community for a specified period of time. No public or
104
+ private interaction with the people involved — including unsolicited interaction
105
+ with those enforcing the Code of Conduct — is allowed during this period.
106
+ Violating these terms may lead to a permanent ban.
107
+
108
+ ### 4. Permanent Ban
109
+
110
+ **Impact:** Demonstrating a pattern of violation of community standards,
111
+ including sustained inappropriate behavior, harassment of an individual, or
112
+ aggression toward or disparagement of classes of individuals.
113
+
114
+ **Consequence:** A permanent ban from any sort of public interaction within the
115
+ community.
116
+
117
+ ---
118
+
119
+ ## Attribution
120
+
121
+ This Code of Conduct is adapted from the
122
+ [Contributor Covenant](https://www.contributor-covenant.org), version 2.1,
123
+ available at
124
+ [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html).
125
+
126
+ Community Impact Guidelines were inspired by
127
+ [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
128
+
129
+ For answers to common questions about this code of conduct, see the FAQ at
130
+ [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq).
131
+ Translations are available at
132
+ [https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations).
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 gramobase contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,314 @@
1
+ # gramobase
2
+
3
+ [![Build Status](https://img.shields.io/github/actions/workflow/status/besaoct/gramobase/build.yml?branch=main&style=flat-square)](https://github.com/besaoct/gramobase/actions)
4
+ [![NPM Version](https://img.shields.io/npm/v/gramobase?color=blue&style=flat-square)](https://www.npmjs.com/package/gramobase)
5
+ [![License](https://img.shields.io/github/license/besaoct/gramobase?style=flat-square)](https://github.com/besaoct/gramobase/blob/main/LICENSE)
6
+ [![Tests Status](https://img.shields.io/badge/tests-33%20passed-brightgreen?style=flat-square)](https://github.com/besaoct/gramobase/actions)
7
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com)
8
+
9
+ **Telegram as a free, infinite, production-grade backend database.**
10
+
11
+ Every Telegram channel is a collection. Every message is a document. Zero infrastructure needed — all you need is a free Telegram account.
12
+
13
+ ```ts
14
+ import { createClient } from 'gramobase';
15
+ import { z } from 'zod';
16
+
17
+ const db = await createClient({
18
+ botToken: process.env.BOT_TOKEN!,
19
+ channelId: process.env.CHANNEL_ID!,
20
+ }).connect();
21
+
22
+ const users = db.collection('users', {
23
+ schema: z.object({ name: z.string(), email: z.string().email() }),
24
+ });
25
+
26
+ await users.insertOne({ name: 'Aarav', email: 'aarav@example.com' });
27
+ const user = await users.findOne({ name: { $eq: 'Aarav' } });
28
+ ```
29
+
30
+ ---
31
+
32
+ ## Why gramobase?
33
+
34
+ | Feature | gramobase | Firebase free | Supabase free |
35
+ |---|---|---|---|
36
+ | Storage | **Unlimited** | 1GB | 500MB |
37
+ | Reads/writes | 30/s per bot, scales with bot count | 50K/day | 500MB bandwidth |
38
+ | Auth | ✓ built-in | ✓ | ✓ |
39
+ | File storage | **2GB per file** | 1GB total | 1GB total |
40
+ | Realtime | ✓ SSE/webhook | ✓ | ✓ |
41
+ | Infra needed | **None** | Firebase project | Supabase project |
42
+ | Cost | **$0 forever** | Free tier | Free tier |
43
+
44
+ ---
45
+
46
+ ## Installation
47
+
48
+ ```bash
49
+ npm install gramobase
50
+ ```
51
+
52
+ ### Running Tests
53
+
54
+ To run the suite of 33 unit tests checking the ORM, caching, queue/worker pooling, and authentication:
55
+
56
+ ```bash
57
+ npm run test
58
+ ```
59
+
60
+ ### Setup
61
+
62
+ ```bash
63
+ npx gramobase init
64
+ ```
65
+
66
+ This walks you through entering your bot token and channel ID, creates `.env` and `gramobase.config.ts`.
67
+
68
+ **Prerequisites:**
69
+ 1. Create a bot via [@BotFather](https://t.me/BotFather) on Telegram — takes 30 seconds
70
+ 2. Create a private Telegram channel
71
+ 3. Add your bot as an **Administrator** with full permissions to the channel
72
+ 4. Get your channel ID (forward a message to @userinfobot)
73
+
74
+ ---
75
+
76
+ ## Core API
77
+
78
+ ### Collections (MongoDB-like ORM)
79
+
80
+ ```ts
81
+ const PostSchema = z.object({
82
+ title: z.string(),
83
+ body: z.string(),
84
+ views: z.number().default(0),
85
+ tags: z.array(z.string()).default([]),
86
+ published: z.boolean().default(false),
87
+ });
88
+
89
+ const posts = db.collection('posts', {
90
+ schema: PostSchema,
91
+ indexes: ['title'], // bloom filter index
92
+ encrypt: true, // AES-256 field-level encryption
93
+ });
94
+
95
+ // Insert
96
+ const post = await posts.insertOne({ title: 'Hello', body: 'World' });
97
+ await posts.insertMany([...]);
98
+
99
+ // Find
100
+ const all = await posts.find();
101
+ const published = await posts.find({ filter: { published: { $eq: true } } });
102
+ const recent = await posts.find({
103
+ filter: { views: { $gte: 100 } },
104
+ sort: { views: -1 },
105
+ limit: 10,
106
+ skip: 0,
107
+ });
108
+
109
+ // Operators: $eq $ne $gt $gte $lt $lte $in $nin $regex $exists $and $or $not
110
+
111
+ // Update
112
+ await posts.findByIdAndUpdate(post._id, {
113
+ $set: { published: true },
114
+ $inc: { views: 1 },
115
+ $push: { tags: 'featured' },
116
+ });
117
+ await posts.updateMany({ published: { $eq: false } }, { $set: { published: true } });
118
+
119
+ // Delete
120
+ await posts.deleteById(post._id);
121
+ await posts.deleteMany({ views: { $eq: 0 } });
122
+ await posts.count({ published: { $eq: true } });
123
+ ```
124
+
125
+ ### Authentication
126
+
127
+ ```ts
128
+ const auth = db.createAuth({
129
+ jwtSecret: process.env.JWT_SECRET!,
130
+ jwtExpiresIn: '7d',
131
+ bcryptRounds: 12,
132
+ });
133
+
134
+ const { user, session } = await auth.register('user@example.com', 'password', ['user']);
135
+ const { session: s2 } = await auth.login('user@example.com', 'password');
136
+
137
+ const verified = auth.verifyToken(s2.token);
138
+ auth.requireRole(verified, 'admin'); // throws if not admin
139
+ auth.requireAnyRole(verified, ['mod', 'admin']);
140
+
141
+ await auth.changePassword(user._id, 'old', 'new');
142
+ await auth.updateRoles(user._id, ['user', 'pro']);
143
+
144
+ // Express middleware
145
+ app.use('/api', auth.middleware());
146
+ app.post('/admin', auth.middleware(), auth.requireRoleMiddleware('admin'), handler);
147
+ ```
148
+
149
+ ### File Storage
150
+
151
+ ```ts
152
+ // Upload any file — images, PDFs, videos up to 2GB (via MTProto)
153
+ const file = await db.uploadFile(buffer, {
154
+ fileName: 'profile.jpg',
155
+ mimeType: 'image/jpeg',
156
+ metadata: { userId: '123' },
157
+ });
158
+
159
+ console.log(file.fileId); // stable Telegram file reference
160
+ console.log(file.url); // CDN-served download URL
161
+ ```
162
+
163
+ ### Realtime
164
+
165
+ ```ts
166
+ // Subscribe to collection events
167
+ const unsub = db.realtime.onInsert('orders', (order) => {
168
+ console.log('New order:', order);
169
+ });
170
+
171
+ db.realtime.onUpdate('products', (id, changes, doc) => {
172
+ console.log('Updated:', id, changes);
173
+ });
174
+
175
+ db.realtime.onDelete('posts', (id) => {
176
+ console.log('Deleted:', id);
177
+ });
178
+
179
+ // Server-Sent Events for browser clients
180
+ app.get('/stream', db.realtime.sseHandler('orders'));
181
+
182
+ // Frontend:
183
+ const es = new EventSource('/stream');
184
+ es.onmessage = (e) => console.log(JSON.parse(e.data));
185
+ ```
186
+
187
+ ### Migrations
188
+
189
+ ```ts
190
+ const migrations = [
191
+ {
192
+ version: 1,
193
+ name: 'add-slug-field',
194
+ async up(db) {
195
+ const posts = db.collection('posts', { schema: PostSchema });
196
+ const all = await posts.find();
197
+ for (const post of all) {
198
+ await posts.findByIdAndUpdate(post._id, {
199
+ $set: { slug: post.title.toLowerCase().replace(/ /g, '-') },
200
+ });
201
+ }
202
+ },
203
+ async down(db) {
204
+ await db.collection('posts', { schema: PostSchema })
205
+ .updateMany({}, { $unset: { slug: '' } });
206
+ },
207
+ },
208
+ ];
209
+
210
+ await db.migrate(migrations);
211
+ ```
212
+
213
+ ### Anti-flood bot pool
214
+
215
+ ```ts
216
+ // Pass multiple bot tokens — gramobase round-robins and backs off per token
217
+ const db = await createClient({
218
+ botToken: [
219
+ process.env.BOT_TOKEN_1!,
220
+ process.env.BOT_TOKEN_2!,
221
+ process.env.BOT_TOKEN_3!,
222
+ ],
223
+ channelId: process.env.CHANNEL_ID!,
224
+ }).connect();
225
+
226
+ // 3 tokens × 30 req/s = effectively 90 writes/s sustained
227
+ ```
228
+
229
+ ---
230
+
231
+ ## Architecture
232
+
233
+ ```
234
+ Developer API (ORM, Auth, Files, Realtime)
235
+
236
+ Hot Cache (LRU, 64MB+, O(1) reads)
237
+
238
+ State Manager (reactive, optimistic writes)
239
+
240
+ Write-Ahead Log (crash recovery, sequence IDs)
241
+
242
+ Registry (distributed lease, heartbeat, single writer)
243
+
244
+ Bot Worker Pool (round-robin, 429 backoff, retry)
245
+
246
+ Telegram Bot API ─────────────────────────────────┐
247
+ │ │
248
+ Private Channel File Storage Realtime
249
+ (messages = docs, (sendDocument, (webhook +
250
+ pinned = index) file_id refs) SSE bridge)
251
+ ```
252
+
253
+ ### Storage model
254
+
255
+ - Each collection maps to a private Telegram channel (or shares one via namespaced message tags)
256
+ - A **pinned index message** stores `{ id → msgId }` for O(1) lookups
257
+ - The **Write-Ahead Log** channel stores operation logs for crash recovery
258
+ - A **registry message** acts as a distributed write lock across processes
259
+
260
+ ### Limits
261
+
262
+ | Limit | Value |
263
+ |---|---|
264
+ | Telegram rate limit | 30 req/s per bot token (scales with pool size) |
265
+ | Message size | 4096 bytes per message (large docs auto-chunked) |
266
+ | File size (Bot API) | 50MB send, 20MB receive |
267
+ | File size (MTProto/TDLib) | 2GB |
268
+ | Channel message history | Unlimited |
269
+ | Cost | $0 |
270
+
271
+ ---
272
+
273
+ ## CLI
274
+
275
+ ```bash
276
+ npx gramobase init # interactive setup wizard
277
+ npx gramobase status # check bot + channel connectivity
278
+ npx gramobase migrate # run pending migrations
279
+ npx gramobase migrate --rollback 1 # rollback last migration
280
+ npx gramobase migrate --status # show migration history
281
+ npx gramobase generate post --fields "title:string,views:number"
282
+ npx gramobase studio # open browser UI (v0.2)
283
+ ```
284
+
285
+ ---
286
+
287
+ ## Configuration
288
+
289
+ ```ts
290
+ const db = createClient({
291
+ botToken: string | string[], // single token or pool
292
+ channelId: string, // main storage channel
293
+ walChannelId?: string, // separate WAL channel (optional)
294
+ indexChannelId?: string, // separate index channel (optional)
295
+ encryptionKey?: string, // AES-256 key for encryption at rest
296
+ cacheMaxBytes?: number, // default 64MB
297
+ cacheTtlMs?: number, // default 60s
298
+ concurrency?: number, // max concurrent requests per token, default 25
299
+ webhookUrl?: string, // enables webhook mode for realtime
300
+ debug?: boolean,
301
+ });
302
+ ```
303
+
304
+ ---
305
+
306
+ ## Disclaimer
307
+
308
+ gramobase is designed for prototypes, hobby projects, and small-to-medium applications. It is not a replacement for PostgreSQL or MongoDB in high-traffic production systems. Data lives on Telegram's infrastructure — do not store sensitive PII without encryption.
309
+
310
+ ---
311
+
312
+ ## License
313
+
314
+ MIT
@@ -0,0 +1,201 @@
1
+ import { z } from 'zod';
2
+ import TelegramBot from 'node-telegram-bot-api';
3
+ import EventEmitter from 'eventemitter3';
4
+
5
+ interface GramoBaseDocument {
6
+ _id: string;
7
+ _collection: string;
8
+ _msgId: number;
9
+ _createdAt: string;
10
+ _updatedAt: string;
11
+ [key: string]: unknown;
12
+ }
13
+ type WithId<T> = T & GramoBaseDocument;
14
+ interface CollectionConfig<T extends z.ZodType> {
15
+ schema: T;
16
+ /** Channel override — uses the default if omitted */
17
+ channelId?: string | undefined;
18
+ /** Bloom-filter field list for fast-miss short-circuit */
19
+ indexes?: string[] | undefined;
20
+ /** Encrypt all documents in this collection with AES-256 */
21
+ encrypt?: boolean | undefined;
22
+ /** TTL seconds — documents are automatically expired after this many seconds */
23
+ ttl?: number | undefined;
24
+ }
25
+ type ElemOf<T> = T extends (infer U)[] ? U : T;
26
+ type ComparisonOperator<T> = {
27
+ $eq?: T | ElemOf<T> | undefined;
28
+ $ne?: T | ElemOf<T> | undefined;
29
+ $gt?: T | ElemOf<T> | number | undefined;
30
+ $gte?: T | ElemOf<T> | number | undefined;
31
+ $lt?: T | ElemOf<T> | number | undefined;
32
+ $lte?: T | ElemOf<T> | number | undefined;
33
+ $in?: (T | ElemOf<T>)[] | undefined;
34
+ $nin?: (T | ElemOf<T>)[] | undefined;
35
+ $exists?: boolean | undefined;
36
+ $regex?: RegExp | string | undefined;
37
+ };
38
+ type Filter<T> = {
39
+ [K in keyof T]?: T[K] | ElemOf<T[K]> | ComparisonOperator<T[K]> | undefined;
40
+ } | {
41
+ $and?: Filter<T>[] | undefined;
42
+ $or?: Filter<T>[] | undefined;
43
+ $not?: Filter<T> | undefined;
44
+ };
45
+ interface FindOptions<T> {
46
+ filter?: Filter<T> | undefined;
47
+ sort?: Partial<Record<keyof T | string, 1 | -1>> | undefined;
48
+ limit?: number | undefined;
49
+ skip?: number | undefined;
50
+ projection?: Partial<Record<keyof T | string, 1 | 0>> | undefined;
51
+ useCache?: boolean | undefined;
52
+ }
53
+ interface UpdateOperators<T> {
54
+ $set?: (Partial<T> & Record<string, unknown>) | undefined;
55
+ $unset?: Partial<Record<keyof T | string, '' | true>> | undefined;
56
+ $inc?: Partial<Record<keyof T | string, number>> | undefined;
57
+ $push?: Partial<Record<keyof T | string, unknown>> | undefined;
58
+ }
59
+ type WalOpType = 'INSERT' | 'UPDATE' | 'DELETE';
60
+ interface WalEntry {
61
+ seq: number;
62
+ op: WalOpType;
63
+ collection: string;
64
+ id: string;
65
+ data?: unknown;
66
+ timestamp: string;
67
+ checksum: string;
68
+ }
69
+ interface Lease {
70
+ instanceId: string;
71
+ acquiredAt: number;
72
+ expiresAt: number;
73
+ heartbeatInterval: ReturnType<typeof setInterval> | null;
74
+ }
75
+ interface User {
76
+ _id: string;
77
+ email: string;
78
+ passwordHash: string;
79
+ roles: string[];
80
+ metadata?: Record<string, unknown> | undefined;
81
+ createdAt: string;
82
+ updatedAt: string;
83
+ }
84
+ interface Session {
85
+ userId: string;
86
+ roles: string[];
87
+ expiresAt: number;
88
+ token: string;
89
+ }
90
+ interface AuthConfig {
91
+ jwtSecret: string;
92
+ jwtExpiresIn?: string | undefined;
93
+ bcryptRounds?: number | undefined;
94
+ onSignIn?: ((user: User) => Promise<void>) | undefined;
95
+ onSignOut?: ((userId: string) => Promise<void>) | undefined;
96
+ }
97
+ interface UploadOptions {
98
+ fileName?: string | undefined;
99
+ mimeType?: string | undefined;
100
+ metadata?: Record<string, unknown> | undefined;
101
+ }
102
+ interface FileRecord {
103
+ _id: string;
104
+ fileId: string;
105
+ fileName: string;
106
+ mimeType: string;
107
+ sizeBytes: number;
108
+ url?: string | undefined;
109
+ uploadedAt: string;
110
+ metadata?: Record<string, unknown> | undefined;
111
+ }
112
+ type GramoBaseEvent = {
113
+ type: 'insert';
114
+ collection: string;
115
+ doc: unknown;
116
+ } | {
117
+ type: 'update';
118
+ collection: string;
119
+ id: string;
120
+ changes: unknown;
121
+ doc: unknown;
122
+ } | {
123
+ type: 'delete';
124
+ collection: string;
125
+ id: string;
126
+ } | {
127
+ type: 'worker:rotate';
128
+ tokenIndex: number;
129
+ } | {
130
+ type: 'wal:flush';
131
+ entries: number;
132
+ };
133
+ interface Migration {
134
+ version: number;
135
+ name: string;
136
+ up(db: unknown): Promise<void>;
137
+ down(db: unknown): Promise<void>;
138
+ }
139
+ interface GramoBaseConfig {
140
+ /** Bot token or array of tokens for pool rotation */
141
+ botToken: string | string[];
142
+ /** Primary storage channel ID */
143
+ channelId: string;
144
+ /** Optional separate channel for WAL entries */
145
+ walChannelId?: string | undefined;
146
+ /** Optional separate channel for collection indexes */
147
+ indexChannelId?: string | undefined;
148
+ /** AES-256 encryption key for data at rest */
149
+ encryptionKey?: string | undefined;
150
+ /** LRU cache byte limit (default: 64MB) */
151
+ cacheMaxBytes?: number | undefined;
152
+ /** LRU cache TTL in milliseconds (default: 60s) */
153
+ cacheTtlMs?: number | undefined;
154
+ /** Max concurrent requests per bot token (default: 25) */
155
+ concurrency?: number | undefined;
156
+ /** Webhook URL for realtime events (optional — falls back to polling) */
157
+ webhookUrl?: string | undefined;
158
+ /** Enable verbose debug logging */
159
+ debug?: boolean | undefined;
160
+ }
161
+
162
+ interface WorkerStats {
163
+ tokenIndex: number;
164
+ requestCount: number;
165
+ errorCount: number;
166
+ lastUsed: number;
167
+ rateLimitHits: number;
168
+ }
169
+ /**
170
+ * BotWorkerPool manages a round-robin pool of Telegram bot tokens.
171
+ * Each token gets its own PQueue limited to 25 concurrent requests
172
+ * (safe under Telegram's 30 req/s flood limit with headroom).
173
+ * On 429 responses, the worker is cooled down and the next token takes over.
174
+ */
175
+ declare class BotWorkerPool extends EventEmitter {
176
+ private bots;
177
+ private queues;
178
+ private stats;
179
+ private currentIndex;
180
+ private debug;
181
+ constructor(tokens: string[], concurrency?: number, debug?: boolean);
182
+ /**
183
+ * Execute a Telegram API call through the pool with automatic retry
184
+ * and token rotation on rate limits.
185
+ */
186
+ execute<T>(fn: (bot: TelegramBot) => Promise<T>, priority?: number): Promise<T>;
187
+ /**
188
+ * Round-robin with recency bias — prefer the worker that was least recently used.
189
+ */
190
+ private pickWorker;
191
+ private isFloodError;
192
+ private isRetryableError;
193
+ private extractRetryAfter;
194
+ getBot(index?: number): TelegramBot;
195
+ getStats(): WorkerStats[];
196
+ getQueueSizes(): number[];
197
+ private sleep;
198
+ destroy(): Promise<void>;
199
+ }
200
+
201
+ export { type AuthConfig as A, BotWorkerPool as B, type CollectionConfig as C, type FileRecord as F, type GramoBaseEvent as G, type Lease as L, type Migration as M, type Session as S, type UploadOptions as U, type WorkerStats as W, type GramoBaseConfig as a, type ComparisonOperator as b, type Filter as c, type FindOptions as d, type GramoBaseDocument as e, type UpdateOperators as f, type User as g, type WalEntry as h, type WalOpType as i, type WithId as j };