gramobase 1.0.7 → 1.0.9
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/README.md +36 -7
- package/dist/bin/gramobase.cjs +102 -19
- package/dist/bin/gramobase.cjs.map +1 -1
- package/dist/bin/gramobase.js +101 -20
- package/dist/bin/gramobase.js.map +1 -1
- package/dist/react/index.cjs +102 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +24 -0
- package/dist/react/index.d.ts +24 -0
- package/dist/react/index.js +99 -0
- package/dist/react/index.js.map +1 -0
- package/package.json +47 -25
package/README.md
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
[](https://github.com/besaoct/gramobase/actions)
|
|
5
5
|
[](https://github.com/besaoct/gramobase/blob/main/LICENSE)
|
|
6
6
|
[](https://github.com/besaoct/gramobase/actions)
|
|
7
|
+
[](https://github.com/besaoct/gramobase/actions)
|
|
7
8
|
[](https://github.com/besaoct/gramobase/pulls)
|
|
8
9
|
|
|
9
10
|
**Telegram as a free, infinite, production-grade backend database.**
|
|
@@ -38,6 +39,7 @@ const user = await users.findOne({ name: { $eq: 'Aarav' } });
|
|
|
38
39
|
| Auth | ✓ built-in | ✓ | ✓ |
|
|
39
40
|
| File storage | **2GB per file** | 1GB total | 1GB total |
|
|
40
41
|
| Realtime | ✓ SSE/webhook | ✓ | ✓ |
|
|
42
|
+
| Frontend UX | ✓ React Optimistic UI Hooks | ✓ | ✓ |
|
|
41
43
|
| Infra needed | **None** | Firebase project | Supabase project |
|
|
42
44
|
| Cost | **$0 forever** | Free tier | Free tier |
|
|
43
45
|
|
|
@@ -51,7 +53,7 @@ npm install gramobase
|
|
|
51
53
|
|
|
52
54
|
### Running Tests
|
|
53
55
|
|
|
54
|
-
To run the suite of
|
|
56
|
+
To run the suite of 40 unit tests checking the ORM, caching, queue/worker pooling, and authentication:
|
|
55
57
|
|
|
56
58
|
```bash
|
|
57
59
|
npm run test
|
|
@@ -63,13 +65,13 @@ npm run test
|
|
|
63
65
|
npx gramobase init
|
|
64
66
|
```
|
|
65
67
|
|
|
66
|
-
This walks you through
|
|
68
|
+
This interactive wizard walks you through setting up your backend.
|
|
69
|
+
It features **Auto-Detect** technology and **Anti-Flood** scaling: it will ask you how many Bot Tokens you want to configure (for 30 req/s scaling per bot). Provide your tokens, and leave the Channel ID blank! By sending a message in your channel, `gramobase` will automatically fetch your hidden Telegram Channel ID for you and generate your `.env` and `gramobase.config.ts`.
|
|
67
70
|
|
|
68
71
|
**Prerequisites:**
|
|
69
72
|
1. Create a bot via [@BotFather](https://t.me/BotFather) on Telegram — takes 30 seconds
|
|
70
73
|
2. Create a private Telegram channel
|
|
71
74
|
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
75
|
|
|
74
76
|
---
|
|
75
77
|
|
|
@@ -184,6 +186,33 @@ const es = new EventSource('/stream');
|
|
|
184
186
|
es.onmessage = (e) => console.log(JSON.parse(e.data));
|
|
185
187
|
```
|
|
186
188
|
|
|
189
|
+
### React Hooks (Frontend Optimistic UI)
|
|
190
|
+
|
|
191
|
+
`gramobase` provides a built-in, zero-dependency React module to power your frontends with instantaneous, production-grade Optimistic UI and per-action loading states.
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
import { useGramoQuery, useGramoMutation } from 'gramobase/react';
|
|
195
|
+
|
|
196
|
+
// Automatically manages data, loading, and error states!
|
|
197
|
+
const { data: todos, isLoading } = useGramoQuery('/api/todos');
|
|
198
|
+
|
|
199
|
+
// Automatically handles Optimistic UI rollbacks on failure!
|
|
200
|
+
const toggleTodo = useGramoMutation('/api/todos', 'PATCH', {
|
|
201
|
+
onMutate: (variables) => {
|
|
202
|
+
// Instantly update UI (Optimistic Update)
|
|
203
|
+
},
|
|
204
|
+
onError: () => {
|
|
205
|
+
// Automatically rolls back if Telegram API fails
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
return (
|
|
210
|
+
<button onClick={() => toggleTodo.mutate({ id: 1 })} disabled={toggleTodo.isLoading}>
|
|
211
|
+
{toggleTodo.isLoading ? 'Saving...' : 'Complete Task'}
|
|
212
|
+
</button>
|
|
213
|
+
)
|
|
214
|
+
```
|
|
215
|
+
|
|
187
216
|
### Migrations
|
|
188
217
|
|
|
189
218
|
```ts
|
|
@@ -216,11 +245,11 @@ await db.migrate(migrations);
|
|
|
216
245
|
// Pass multiple bot tokens — gramobase round-robins and backs off per token
|
|
217
246
|
const db = await createClient({
|
|
218
247
|
botToken: [
|
|
219
|
-
process.env.
|
|
220
|
-
process.env.
|
|
221
|
-
process.env.
|
|
248
|
+
process.env.GRAMOBASE_BOT_TOKEN_1!,
|
|
249
|
+
process.env.GRAMOBASE_BOT_TOKEN_2!,
|
|
250
|
+
process.env.GRAMOBASE_BOT_TOKEN_3!,
|
|
222
251
|
],
|
|
223
|
-
channelId: process.env.
|
|
252
|
+
channelId: process.env.GRAMOBASE_CHANNEL_ID!,
|
|
224
253
|
}).connect();
|
|
225
254
|
|
|
226
255
|
// 3 tokens × 30 req/s = effectively 90 writes/s sustained
|
package/dist/bin/gramobase.cjs
CHANGED
|
@@ -4,50 +4,133 @@
|
|
|
4
4
|
var commander = require('commander');
|
|
5
5
|
var chalk = require('chalk');
|
|
6
6
|
var ora = require('ora');
|
|
7
|
-
var readline = require('readline');
|
|
8
7
|
var fs = require('fs');
|
|
9
8
|
var path = require('path');
|
|
9
|
+
var inquirer = require('inquirer');
|
|
10
10
|
|
|
11
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
11
12
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
13
|
|
|
13
14
|
var chalk__default = /*#__PURE__*/_interopDefault(chalk);
|
|
14
15
|
var ora__default = /*#__PURE__*/_interopDefault(ora);
|
|
16
|
+
var inquirer__default = /*#__PURE__*/_interopDefault(inquirer);
|
|
15
17
|
|
|
16
18
|
// gramobase — Telegram as your free, infinite backend
|
|
17
19
|
|
|
18
|
-
var pkg = { version: "0.
|
|
20
|
+
var pkg = { version: "0.0.0" };
|
|
21
|
+
try {
|
|
22
|
+
const pkgUrl = new URL("../../package.json", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('gramobase.cjs', document.baseURI).href)));
|
|
23
|
+
pkg = JSON.parse(fs.readFileSync(pkgUrl, "utf-8"));
|
|
24
|
+
} catch (e) {
|
|
25
|
+
try {
|
|
26
|
+
const pkgUrl2 = new URL("../package.json", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('gramobase.cjs', document.baseURI).href)));
|
|
27
|
+
pkg = JSON.parse(fs.readFileSync(pkgUrl2, "utf-8"));
|
|
28
|
+
} catch (e2) {
|
|
29
|
+
}
|
|
30
|
+
}
|
|
19
31
|
var program = new commander.Command();
|
|
20
32
|
program.name("gramobase").description(chalk__default.default.cyan("Telegram as your free, infinite backend database")).version(pkg.version);
|
|
21
33
|
program.command("init").description("Initialize a new gramobase project").option("--yes", "Skip prompts, use defaults").action(async (opts) => {
|
|
22
34
|
console.log("\n" + chalk__default.default.bold.cyan(" gramobase") + chalk__default.default.gray(" \u2014 Telegram backend\n"));
|
|
23
|
-
let
|
|
35
|
+
let botTokens = [];
|
|
24
36
|
let channelId = "";
|
|
25
37
|
let encryptionKey = "";
|
|
26
38
|
if (!opts.yes) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
console.log(chalk__default.default.cyan.bold("\n Step 1: Bot Tokens (Anti-flood rotation)"));
|
|
40
|
+
console.log(chalk__default.default.gray(" You can use multiple bot tokens to increase your rate limit (30 req/s per bot)."));
|
|
41
|
+
const { numBotsStr } = await inquirer__default.default.prompt([{
|
|
42
|
+
type: "input",
|
|
43
|
+
name: "numBotsStr",
|
|
44
|
+
message: chalk__default.default.white("How many bot tokens do you want to add? (Default: 1):")
|
|
45
|
+
}]);
|
|
46
|
+
const numBots = Math.max(1, parseInt(numBotsStr, 10) || 1);
|
|
47
|
+
console.log(chalk__default.default.gray(" Create your bots by messaging @BotFather on Telegram and copy the HTTP API tokens."));
|
|
48
|
+
for (let i = 1; i <= numBots; i++) {
|
|
49
|
+
const { token } = await inquirer__default.default.prompt([{
|
|
50
|
+
type: "password",
|
|
51
|
+
name: "token",
|
|
52
|
+
message: chalk__default.default.white(`Bot token ${i}:`),
|
|
53
|
+
mask: chalk__default.default.red("*")
|
|
54
|
+
}]);
|
|
55
|
+
botTokens.push(token.trim());
|
|
56
|
+
}
|
|
57
|
+
console.log(chalk__default.default.cyan.bold("\n Step 2: Channel ID"));
|
|
58
|
+
console.log(chalk__default.default.gray(" You can enter your Channel ID manually (e.g. -100123456789)"));
|
|
59
|
+
console.log(chalk__default.default.gray(" OR leave it blank to auto-detect it."));
|
|
60
|
+
const { channelIdInput } = await inquirer__default.default.prompt([{
|
|
61
|
+
type: "input",
|
|
62
|
+
name: "channelIdInput",
|
|
63
|
+
message: chalk__default.default.white("Channel ID (Press Enter to auto-detect):")
|
|
64
|
+
}]);
|
|
65
|
+
channelId = channelIdInput.trim();
|
|
66
|
+
if (!channelId) {
|
|
67
|
+
console.log(chalk__default.default.yellow("\n [Auto-Detect Mode]"));
|
|
68
|
+
console.log(chalk__default.default.gray(` 1. Create a private Telegram channel.`));
|
|
69
|
+
console.log(chalk__default.default.gray(` 2. Add your bot as an Administrator with full permissions.`));
|
|
70
|
+
console.log(chalk__default.default.gray(` 3. Send any message in the channel (e.g. "hello").
|
|
71
|
+
`));
|
|
72
|
+
const spinner2 = ora__default.default("Waiting for a message in your channel...").start();
|
|
73
|
+
let detected = false;
|
|
74
|
+
let offset = 0;
|
|
75
|
+
const pollToken = botTokens[0] || "";
|
|
76
|
+
while (!detected) {
|
|
77
|
+
try {
|
|
78
|
+
const res = await fetch(`https://api.telegram.org/bot${pollToken}/getUpdates?offset=${offset}&timeout=2`);
|
|
79
|
+
const json = await res.json();
|
|
80
|
+
if (json.ok && json.result.length > 0) {
|
|
81
|
+
for (const update of json.result) {
|
|
82
|
+
offset = update.update_id + 1;
|
|
83
|
+
if (update.channel_post && update.channel_post.chat) {
|
|
84
|
+
channelId = update.channel_post.chat.id.toString();
|
|
85
|
+
const title = update.channel_post.chat.title || "Unknown Channel";
|
|
86
|
+
spinner2.succeed(chalk__default.default.green(`Found channel: ${title} (${channelId})`));
|
|
87
|
+
detected = true;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} else if (!json.ok) {
|
|
92
|
+
spinner2.fail(chalk__default.default.red("Invalid Bot Token or Telegram API error."));
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
} catch (e) {
|
|
96
|
+
}
|
|
97
|
+
if (!detected) {
|
|
98
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
console.log(chalk__default.default.cyan.bold("\n Step 3: Security (Optional)"));
|
|
103
|
+
const { encryptionKeyInput } = await inquirer__default.default.prompt([{
|
|
104
|
+
type: "input",
|
|
105
|
+
name: "encryptionKeyInput",
|
|
106
|
+
message: chalk__default.default.white("Encryption key (optional, press enter to skip):")
|
|
107
|
+
}]);
|
|
108
|
+
encryptionKey = encryptionKeyInput;
|
|
34
109
|
}
|
|
35
110
|
const spinner = ora__default.default("Setting up gramobase...").start();
|
|
36
|
-
const safeToken = botToken.trim();
|
|
37
|
-
const safeChannelId = channelId.trim();
|
|
38
|
-
const safeKey = encryptionKey.trim();
|
|
39
111
|
const cwd = process.cwd();
|
|
40
112
|
const envPath = path.join(cwd, ".env");
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
`
|
|
44
|
-
|
|
45
|
-
|
|
113
|
+
const envContentLines = [];
|
|
114
|
+
if (botTokens.length === 1) {
|
|
115
|
+
envContentLines.push(`GRAMOBASE_BOT_TOKEN=${botTokens[0]}`);
|
|
116
|
+
} else {
|
|
117
|
+
botTokens.forEach((token, i) => {
|
|
118
|
+
envContentLines.push(`GRAMOBASE_BOT_TOKEN_${i + 1}=${token}`);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
const safeChannelId = channelId.trim();
|
|
122
|
+
const safeKey = encryptionKey.trim();
|
|
123
|
+
envContentLines.push(`GRAMOBASE_CHANNEL_ID=${safeChannelId}`);
|
|
124
|
+
envContentLines.push(safeKey ? `GRAMOBASE_ENCRYPTION_KEY=${safeKey}` : "# GRAMOBASE_ENCRYPTION_KEY=");
|
|
125
|
+
const envContent = envContentLines.join("\n");
|
|
46
126
|
fs.writeFileSync(envPath, envContent + "\n");
|
|
127
|
+
const botTokenConfigStr = botTokens.length === 1 ? `process.env.GRAMOBASE_BOT_TOKEN!` : `[
|
|
128
|
+
${botTokens.map((_, i) => ` process.env.GRAMOBASE_BOT_TOKEN_${i + 1}!,`).join("\n")}
|
|
129
|
+
]`;
|
|
47
130
|
const configContent = `import { GramoBaseConfig } from 'gramobase';
|
|
48
131
|
|
|
49
132
|
const config: GramoBaseConfig = {
|
|
50
|
-
botToken:
|
|
133
|
+
botToken: ${botTokenConfigStr},
|
|
51
134
|
channelId: process.env.GRAMOBASE_CHANNEL_ID!,
|
|
52
135
|
// encryptionKey: process.env.GRAMOBASE_ENCRYPTION_KEY,
|
|
53
136
|
cacheMaxBytes: 64 * 1024 * 1024, // 64MB hot cache
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../bin/gramobase.ts"],"names":["Command","chalk","createInterface","ora","join","writeFileSync","existsSync","mkdirSync"],"mappings":"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","file":"gramobase.cjs","sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { createInterface } from 'readline';\nimport { writeFileSync, existsSync, mkdirSync } from 'fs';\nimport { join, resolve, basename } from 'path';\n\nconst pkg = { version: '0.1.0' };\n\nconst program = new Command();\n\nprogram\n .name('gramobase')\n .description(chalk.cyan('Telegram as your free, infinite backend database'))\n .version(pkg.version);\n\n// ─── gramobase init ──────────────────────────────────────────────────────────\n\nprogram\n .command('init')\n .description('Initialize a new gramobase project')\n .option('--yes', 'Skip prompts, use defaults')\n .action(async (opts) => {\n console.log('\\n' + chalk.bold.cyan(' gramobase') + chalk.gray(' — Telegram backend\\n'));\n\n let botToken = '';\n let channelId = '';\n let encryptionKey = '';\n\n if (!opts.yes) {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n const ask = (q: string) => new Promise<string>((res) => rl.question(q, res));\n\n console.log(chalk.gray(' Get a bot token from @BotFather on Telegram\\n'));\n botToken = await ask(chalk.white(' Bot token: '));\n channelId = await ask(chalk.white(' Channel ID (e.g. -100123456789): '));\n encryptionKey = await ask(chalk.white(' Encryption key (optional, press enter to skip): '));\n rl.close();\n }\n\n const spinner = ora('Setting up gramobase...').start();\n\n // Sanitize inputs — only use basename of any path-like values\n const safeToken = botToken.trim();\n const safeChannelId = channelId.trim();\n const safeKey = encryptionKey.trim();\n\n // Create .env — never write tokens to paths derived from user input\n const cwd = process.cwd();\n const envPath = join(cwd, '.env');\n const envContent = [\n `GRAMOBASE_BOT_TOKEN=${safeToken}`,\n `GRAMOBASE_CHANNEL_ID=${safeChannelId}`,\n safeKey ? `GRAMOBASE_ENCRYPTION_KEY=${safeKey}` : '# GRAMOBASE_ENCRYPTION_KEY=',\n ].join('\\n');\n\n writeFileSync(envPath, envContent + '\\n');\n\n // Create gramobase.config.ts\n const configContent = `import { GramoBaseConfig } from 'gramobase';\n\nconst config: GramoBaseConfig = {\n botToken: process.env.GRAMOBASE_BOT_TOKEN!,\n channelId: process.env.GRAMOBASE_CHANNEL_ID!,\n // encryptionKey: process.env.GRAMOBASE_ENCRYPTION_KEY,\n cacheMaxBytes: 64 * 1024 * 1024, // 64MB hot cache\n cacheTtlMs: 60_000,\n concurrency: 25,\n debug: process.env.NODE_ENV === 'development',\n};\n\nexport default config;\n`;\n\n writeFileSync(join(cwd, 'gramobase.config.ts'), configContent);\n\n // Create migrations folder — path is hardcoded, not from user input\n const migrationsDir = join(cwd, 'gramobase', 'migrations');\n if (!existsSync(migrationsDir)) {\n mkdirSync(migrationsDir, { recursive: true });\n }\n\n spinner.succeed(chalk.green('gramobase initialized!'));\n\n console.log(`\n ${chalk.bold('Files created:')}\n ${chalk.gray('├─')} .env\n ${chalk.gray('└─')} gramobase.config.ts\n ${chalk.gray('└─')} gramobase/migrations/\n\n ${chalk.bold('Next steps:')}\n ${chalk.cyan('1.')} Add your bot token and channel ID to .env\n ${chalk.cyan('2.')} Run ${chalk.bold('gramobase migrate')} to initialize the database\n ${chalk.cyan('3.')} Import and use: ${chalk.gray(\"import { createClient } from 'gramobase'\")}\n`);\n });\n\n// ─── gramobase migrate ───────────────────────────────────────────────────────\n\nprogram\n .command('migrate')\n .description('Run pending migrations')\n .option('--rollback <steps>', 'Rollback N migration steps', '0')\n .option('--status', 'Show migration status')\n .action(async (opts) => {\n const spinner = ora('Loading migrations...').start();\n try {\n const configPath = join(process.cwd(), 'gramobase.config.ts');\n\n spinner.text = 'Connecting...';\n spinner.succeed('Migration runner ready (run in your project after build)');\n } catch (e: any) {\n spinner.fail(chalk.red('Failed: ' + (e instanceof Error ? e.message : 'Unknown error')));\n }\n });\n\n// ─── gramobase status ────────────────────────────────────────────────────────\n\nprogram\n .command('status')\n .description('Show database and connection status')\n .action(async () => {\n const spinner = ora('Checking status...').start();\n try {\n const token = process.env['GRAMOBASE_BOT_TOKEN'];\n const channelId = process.env['GRAMOBASE_CHANNEL_ID'];\n\n if (!token || !channelId) {\n spinner.fail('.env not found — run gramobase init first');\n return;\n }\n\n // Ping Telegram Bot API — token is from env, not user input in this context\n const res = await fetch(`https://api.telegram.org/bot${encodeURIComponent(token)}/getMe`);\n const json = await res.json() as any;\n\n if (json.ok) {\n spinner.succeed(chalk.green('Connected'));\n console.log(`\n ${chalk.bold('Bot:')} ${chalk.cyan('@' + json.result.username)} (${json.result.first_name})\n ${chalk.bold('Channel:')} ${chalk.cyan(channelId)}\n ${chalk.bold('Status:')} ${chalk.green('● Online')}\n`);\n } else {\n spinner.fail(chalk.red('Bot API error — check your token'));\n }\n } catch (e: any) {\n spinner.fail(chalk.red('Connection failed'));\n }\n });\n\n// ─── gramobase generate ──────────────────────────────────────────────────────\n\nprogram\n .command('generate <name>')\n .description('Generate a typed collection schema')\n .option('--fields <fields>', 'Comma-separated fields (e.g. name:string,age:number)')\n .action((name: string, opts) => {\n // Sanitize name — only allow alphanumeric + underscore/hyphen\n const safeName = name.replace(/[^a-zA-Z0-9_-]/g, '');\n if (!safeName || safeName !== name) {\n console.error(chalk.red(' Error: Schema name must contain only letters, numbers, underscores, and hyphens'));\n process.exit(1);\n }\n\n const fields: string[] = opts.fields\n ? (opts.fields as string).split(',').map((f: string) => f.trim())\n : ['name:string'];\n\n const schemaFields = fields.map((f: string) => {\n const [fname, ftype] = f.split(':');\n // Sanitize field name\n const safeFname = (fname ?? 'field').replace(/[^a-zA-Z0-9_]/g, '');\n const zodType =\n ftype === 'number' ? 'z.number()' :\n ftype === 'boolean' ? 'z.boolean()' :\n ftype === 'date' ? 'z.string()' :\n 'z.string()';\n return ` ${safeFname}: ${zodType},`;\n }).join('\\n');\n\n const output = `import { z } from 'zod';\nimport { createClient } from 'gramobase';\n\nexport const ${safeName}Schema = z.object({\n${schemaFields}\n});\n\nexport type ${capitalize(safeName)} = z.infer<typeof ${safeName}Schema>;\n\n// Usage:\n// const db = createClient(config);\n// const ${safeName}s = db.collection('${safeName}s', { schema: ${safeName}Schema });\n// await ${safeName}s.insertOne({ ${fields.map((f: string) => (f.split(':')[0] ?? 'field').replace(/[^a-zA-Z0-9_]/g, '') + ': ...' ).join(', ')} });\n`;\n\n // Path is constructed from sanitized name only — no user-controlled path traversal\n const dir = join(process.cwd(), 'gramobase');\n const outPath = join(dir, `${safeName}.schema.ts`);\n\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n writeFileSync(outPath, output);\n\n console.log(`\\n ${chalk.green('✓')} Generated ${chalk.cyan(`gramobase/${safeName}.schema.ts`)}\\n`);\n });\n\n// ─── gramobase studio ────────────────────────────────────────────────────────\n\nprogram\n .command('studio')\n .description('Open the gramobase browser studio UI')\n .option('--port <port>', 'Port to listen on', '4242')\n .action((opts) => {\n // Validate port is numeric and in valid range\n const port = parseInt(opts.port as string, 10);\n if (isNaN(port) || port < 1 || port > 65535) {\n console.error(chalk.red(' Error: Invalid port number'));\n process.exit(1);\n }\n console.log(`\\n ${chalk.bold.cyan('gramobase studio')}\\n`);\n console.log(` ${chalk.gray('Open')} ${chalk.cyan(`http://localhost:${port}`)} ${chalk.gray('in your browser')}\\n`);\n console.log(chalk.yellow(' Studio UI coming in v0.2.0 — contribute at github.com/yourusername/gramobase\\n'));\n });\n\nfunction capitalize(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\nprogram.parse();\n"]}
|
|
1
|
+
{"version":3,"sources":["../../bin/gramobase.ts"],"names":["readFileSync","Command","chalk","inquirer","spinner","ora","join","writeFileSync","existsSync","mkdirSync"],"mappings":"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","file":"gramobase.cjs","sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { writeFileSync, existsSync, mkdirSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport inquirer from 'inquirer';\n\nlet pkg = { version: '0.0.0' };\ntry {\n const pkgUrl = new URL('../../package.json', import.meta.url);\n pkg = JSON.parse(readFileSync(pkgUrl, 'utf-8'));\n} catch (e) {\n try {\n const pkgUrl2 = new URL('../package.json', import.meta.url);\n pkg = JSON.parse(readFileSync(pkgUrl2, 'utf-8'));\n } catch (e2) {}\n}\n\nconst program = new Command();\n\nprogram\n .name('gramobase')\n .description(chalk.cyan('Telegram as your free, infinite backend database'))\n .version(pkg.version);\n\n// ─── gramobase init ──────────────────────────────────────────────────────────\n\nprogram\n .command('init')\n .description('Initialize a new gramobase project')\n .option('--yes', 'Skip prompts, use defaults')\n .action(async (opts) => {\n console.log('\\n' + chalk.bold.cyan(' gramobase') + chalk.gray(' — Telegram backend\\n'));\n\n let botTokens: string[] = [];\n let channelId = '';\n let encryptionKey = '';\n\n if (!opts.yes) {\n console.log(chalk.cyan.bold('\\n Step 1: Bot Tokens (Anti-flood rotation)'));\n console.log(chalk.gray(' You can use multiple bot tokens to increase your rate limit (30 req/s per bot).'));\n \n const { numBotsStr } = await inquirer.prompt([{\n type: 'input',\n name: 'numBotsStr',\n message: chalk.white('How many bot tokens do you want to add? (Default: 1):')\n }]);\n const numBots = Math.max(1, parseInt(numBotsStr, 10) || 1);\n\n console.log(chalk.gray(' Create your bots by messaging @BotFather on Telegram and copy the HTTP API tokens.'));\n for (let i = 1; i <= numBots; i++) {\n const { token } = await inquirer.prompt([{\n type: 'password',\n name: 'token',\n message: chalk.white(`Bot token ${i}:`),\n mask: chalk.red('*')\n }]);\n botTokens.push(token.trim());\n }\n\n console.log(chalk.cyan.bold('\\n Step 2: Channel ID'));\n console.log(chalk.gray(' You can enter your Channel ID manually (e.g. -100123456789)'));\n console.log(chalk.gray(' OR leave it blank to auto-detect it.'));\n \n const { channelIdInput } = await inquirer.prompt([{\n type: 'input',\n name: 'channelIdInput',\n message: chalk.white('Channel ID (Press Enter to auto-detect):')\n }]);\n \n channelId = channelIdInput.trim();\n\n if (!channelId) {\n console.log(chalk.yellow('\\n [Auto-Detect Mode]'));\n console.log(chalk.gray(` 1. Create a private Telegram channel.`));\n console.log(chalk.gray(` 2. Add your bot as an Administrator with full permissions.`));\n console.log(chalk.gray(` 3. Send any message in the channel (e.g. \"hello\").\\n`));\n \n const spinner = ora('Waiting for a message in your channel...').start();\n \n let detected = false;\n let offset = 0;\n // Use the first token to poll\n const pollToken = botTokens[0] || '';\n \n while (!detected) {\n try {\n const res = await fetch(`https://api.telegram.org/bot${pollToken}/getUpdates?offset=${offset}&timeout=2`);\n const json: any = await res.json();\n \n if (json.ok && json.result.length > 0) {\n for (const update of json.result) {\n offset = update.update_id + 1;\n if (update.channel_post && update.channel_post.chat) {\n channelId = update.channel_post.chat.id.toString();\n const title = update.channel_post.chat.title || 'Unknown Channel';\n spinner.succeed(chalk.green(`Found channel: ${title} (${channelId})`));\n detected = true;\n break;\n }\n }\n } else if (!json.ok) {\n spinner.fail(chalk.red('Invalid Bot Token or Telegram API error.'));\n process.exit(1);\n }\n } catch (e) {\n // Ignore fetch errors and continue polling\n }\n \n if (!detected) {\n await new Promise((resolve) => setTimeout(resolve, 2000));\n }\n }\n }\n\n console.log(chalk.cyan.bold('\\n Step 3: Security (Optional)'));\n const { encryptionKeyInput } = await inquirer.prompt([{\n type: 'input',\n name: 'encryptionKeyInput',\n message: chalk.white('Encryption key (optional, press enter to skip):')\n }]);\n encryptionKey = encryptionKeyInput;\n }\n\n const spinner = ora('Setting up gramobase...').start();\n\n // Create .env — never write tokens to paths derived from user input\n const cwd = process.cwd();\n const envPath = join(cwd, '.env');\n const envContentLines = [];\n \n if (botTokens.length === 1) {\n envContentLines.push(`GRAMOBASE_BOT_TOKEN=${botTokens[0]}`);\n } else {\n botTokens.forEach((token, i) => {\n envContentLines.push(`GRAMOBASE_BOT_TOKEN_${i + 1}=${token}`);\n });\n }\n \n const safeChannelId = channelId.trim();\n const safeKey = encryptionKey.trim();\n \n envContentLines.push(`GRAMOBASE_CHANNEL_ID=${safeChannelId}`);\n envContentLines.push(safeKey ? `GRAMOBASE_ENCRYPTION_KEY=${safeKey}` : '# GRAMOBASE_ENCRYPTION_KEY=');\n \n const envContent = envContentLines.join('\\n');\n writeFileSync(envPath, envContent + '\\n');\n\n const botTokenConfigStr = botTokens.length === 1\n ? `process.env.GRAMOBASE_BOT_TOKEN!`\n : `[\\n${botTokens.map((_, i) => ` process.env.GRAMOBASE_BOT_TOKEN_${i + 1}!,`).join('\\n')}\\n ]`;\n\n // Create gramobase.config.ts\n const configContent = `import { GramoBaseConfig } from 'gramobase';\n\nconst config: GramoBaseConfig = {\n botToken: ${botTokenConfigStr},\n channelId: process.env.GRAMOBASE_CHANNEL_ID!,\n // encryptionKey: process.env.GRAMOBASE_ENCRYPTION_KEY,\n cacheMaxBytes: 64 * 1024 * 1024, // 64MB hot cache\n cacheTtlMs: 60_000,\n concurrency: 25,\n debug: process.env.NODE_ENV === 'development',\n};\n\nexport default config;\n`;\n\n writeFileSync(join(cwd, 'gramobase.config.ts'), configContent);\n\n // Create migrations folder — path is hardcoded, not from user input\n const migrationsDir = join(cwd, 'gramobase', 'migrations');\n if (!existsSync(migrationsDir)) {\n mkdirSync(migrationsDir, { recursive: true });\n }\n\n spinner.succeed(chalk.green('gramobase initialized!'));\n\n console.log(`\n ${chalk.bold('Files created:')}\n ${chalk.gray('├─')} .env\n ${chalk.gray('└─')} gramobase.config.ts\n ${chalk.gray('└─')} gramobase/migrations/\n\n ${chalk.bold('Next steps:')}\n ${chalk.cyan('1.')} Add your bot token and channel ID to .env\n ${chalk.cyan('2.')} Run ${chalk.bold('gramobase migrate')} to initialize the database\n ${chalk.cyan('3.')} Import and use: ${chalk.gray(\"import { createClient } from 'gramobase'\")}\n`);\n });\n\n// ─── gramobase migrate ───────────────────────────────────────────────────────\n\nprogram\n .command('migrate')\n .description('Run pending migrations')\n .option('--rollback <steps>', 'Rollback N migration steps', '0')\n .option('--status', 'Show migration status')\n .action(async (opts) => {\n const spinner = ora('Loading migrations...').start();\n try {\n const configPath = join(process.cwd(), 'gramobase.config.ts');\n\n spinner.text = 'Connecting...';\n spinner.succeed('Migration runner ready (run in your project after build)');\n } catch (e: any) {\n spinner.fail(chalk.red('Failed: ' + (e instanceof Error ? e.message : 'Unknown error')));\n }\n });\n\n// ─── gramobase status ────────────────────────────────────────────────────────\n\nprogram\n .command('status')\n .description('Show database and connection status')\n .action(async () => {\n const spinner = ora('Checking status...').start();\n try {\n const token = process.env['GRAMOBASE_BOT_TOKEN'];\n const channelId = process.env['GRAMOBASE_CHANNEL_ID'];\n\n if (!token || !channelId) {\n spinner.fail('.env not found — run gramobase init first');\n return;\n }\n\n // Ping Telegram Bot API — token is from env, not user input in this context\n const res = await fetch(`https://api.telegram.org/bot${encodeURIComponent(token)}/getMe`);\n const json = await res.json() as any;\n\n if (json.ok) {\n spinner.succeed(chalk.green('Connected'));\n console.log(`\n ${chalk.bold('Bot:')} ${chalk.cyan('@' + json.result.username)} (${json.result.first_name})\n ${chalk.bold('Channel:')} ${chalk.cyan(channelId)}\n ${chalk.bold('Status:')} ${chalk.green('● Online')}\n`);\n } else {\n spinner.fail(chalk.red('Bot API error — check your token'));\n }\n } catch (e: any) {\n spinner.fail(chalk.red('Connection failed'));\n }\n });\n\n// ─── gramobase generate ──────────────────────────────────────────────────────\n\nprogram\n .command('generate <name>')\n .description('Generate a typed collection schema')\n .option('--fields <fields>', 'Comma-separated fields (e.g. name:string,age:number)')\n .action((name: string, opts) => {\n // Sanitize name — only allow alphanumeric + underscore/hyphen\n const safeName = name.replace(/[^a-zA-Z0-9_-]/g, '');\n if (!safeName || safeName !== name) {\n console.error(chalk.red(' Error: Schema name must contain only letters, numbers, underscores, and hyphens'));\n process.exit(1);\n }\n\n const fields: string[] = opts.fields\n ? (opts.fields as string).split(',').map((f: string) => f.trim())\n : ['name:string'];\n\n const schemaFields = fields.map((f: string) => {\n const [fname, ftype] = f.split(':');\n // Sanitize field name\n const safeFname = (fname ?? 'field').replace(/[^a-zA-Z0-9_]/g, '');\n const zodType =\n ftype === 'number' ? 'z.number()' :\n ftype === 'boolean' ? 'z.boolean()' :\n ftype === 'date' ? 'z.string()' :\n 'z.string()';\n return ` ${safeFname}: ${zodType},`;\n }).join('\\n');\n\n const output = `import { z } from 'zod';\nimport { createClient } from 'gramobase';\n\nexport const ${safeName}Schema = z.object({\n${schemaFields}\n});\n\nexport type ${capitalize(safeName)} = z.infer<typeof ${safeName}Schema>;\n\n// Usage:\n// const db = createClient(config);\n// const ${safeName}s = db.collection('${safeName}s', { schema: ${safeName}Schema });\n// await ${safeName}s.insertOne({ ${fields.map((f: string) => (f.split(':')[0] ?? 'field').replace(/[^a-zA-Z0-9_]/g, '') + ': ...' ).join(', ')} });\n`;\n\n // Path is constructed from sanitized name only — no user-controlled path traversal\n const dir = join(process.cwd(), 'gramobase');\n const outPath = join(dir, `${safeName}.schema.ts`);\n\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n writeFileSync(outPath, output);\n\n console.log(`\\n ${chalk.green('✓')} Generated ${chalk.cyan(`gramobase/${safeName}.schema.ts`)}\\n`);\n });\n\n// ─── gramobase studio ────────────────────────────────────────────────────────\n\nprogram\n .command('studio')\n .description('Open the gramobase browser studio UI')\n .option('--port <port>', 'Port to listen on', '4242')\n .action((opts) => {\n // Validate port is numeric and in valid range\n const port = parseInt(opts.port as string, 10);\n if (isNaN(port) || port < 1 || port > 65535) {\n console.error(chalk.red(' Error: Invalid port number'));\n process.exit(1);\n }\n console.log(`\\n ${chalk.bold.cyan('gramobase studio')}\\n`);\n console.log(` ${chalk.gray('Open')} ${chalk.cyan(`http://localhost:${port}`)} ${chalk.gray('in your browser')}\\n`);\n console.log(chalk.yellow(' Studio UI coming in v0.2.0 — contribute at github.com/yourusername/gramobase\\n'));\n });\n\nfunction capitalize(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\nprogram.parse();\n"]}
|
package/dist/bin/gramobase.js
CHANGED
|
@@ -2,45 +2,126 @@
|
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import ora from 'ora';
|
|
5
|
-
import {
|
|
6
|
-
import { writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
5
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
7
6
|
import { join } from 'path';
|
|
7
|
+
import inquirer from 'inquirer';
|
|
8
8
|
|
|
9
9
|
// gramobase — Telegram as your free, infinite backend
|
|
10
10
|
|
|
11
|
-
var pkg = { version: "0.
|
|
11
|
+
var pkg = { version: "0.0.0" };
|
|
12
|
+
try {
|
|
13
|
+
const pkgUrl = new URL("../../package.json", import.meta.url);
|
|
14
|
+
pkg = JSON.parse(readFileSync(pkgUrl, "utf-8"));
|
|
15
|
+
} catch (e) {
|
|
16
|
+
try {
|
|
17
|
+
const pkgUrl2 = new URL("../package.json", import.meta.url);
|
|
18
|
+
pkg = JSON.parse(readFileSync(pkgUrl2, "utf-8"));
|
|
19
|
+
} catch (e2) {
|
|
20
|
+
}
|
|
21
|
+
}
|
|
12
22
|
var program = new Command();
|
|
13
23
|
program.name("gramobase").description(chalk.cyan("Telegram as your free, infinite backend database")).version(pkg.version);
|
|
14
24
|
program.command("init").description("Initialize a new gramobase project").option("--yes", "Skip prompts, use defaults").action(async (opts) => {
|
|
15
25
|
console.log("\n" + chalk.bold.cyan(" gramobase") + chalk.gray(" \u2014 Telegram backend\n"));
|
|
16
|
-
let
|
|
26
|
+
let botTokens = [];
|
|
17
27
|
let channelId = "";
|
|
18
28
|
let encryptionKey = "";
|
|
19
29
|
if (!opts.yes) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
console.log(chalk.cyan.bold("\n Step 1: Bot Tokens (Anti-flood rotation)"));
|
|
31
|
+
console.log(chalk.gray(" You can use multiple bot tokens to increase your rate limit (30 req/s per bot)."));
|
|
32
|
+
const { numBotsStr } = await inquirer.prompt([{
|
|
33
|
+
type: "input",
|
|
34
|
+
name: "numBotsStr",
|
|
35
|
+
message: chalk.white("How many bot tokens do you want to add? (Default: 1):")
|
|
36
|
+
}]);
|
|
37
|
+
const numBots = Math.max(1, parseInt(numBotsStr, 10) || 1);
|
|
38
|
+
console.log(chalk.gray(" Create your bots by messaging @BotFather on Telegram and copy the HTTP API tokens."));
|
|
39
|
+
for (let i = 1; i <= numBots; i++) {
|
|
40
|
+
const { token } = await inquirer.prompt([{
|
|
41
|
+
type: "password",
|
|
42
|
+
name: "token",
|
|
43
|
+
message: chalk.white(`Bot token ${i}:`),
|
|
44
|
+
mask: chalk.red("*")
|
|
45
|
+
}]);
|
|
46
|
+
botTokens.push(token.trim());
|
|
47
|
+
}
|
|
48
|
+
console.log(chalk.cyan.bold("\n Step 2: Channel ID"));
|
|
49
|
+
console.log(chalk.gray(" You can enter your Channel ID manually (e.g. -100123456789)"));
|
|
50
|
+
console.log(chalk.gray(" OR leave it blank to auto-detect it."));
|
|
51
|
+
const { channelIdInput } = await inquirer.prompt([{
|
|
52
|
+
type: "input",
|
|
53
|
+
name: "channelIdInput",
|
|
54
|
+
message: chalk.white("Channel ID (Press Enter to auto-detect):")
|
|
55
|
+
}]);
|
|
56
|
+
channelId = channelIdInput.trim();
|
|
57
|
+
if (!channelId) {
|
|
58
|
+
console.log(chalk.yellow("\n [Auto-Detect Mode]"));
|
|
59
|
+
console.log(chalk.gray(` 1. Create a private Telegram channel.`));
|
|
60
|
+
console.log(chalk.gray(` 2. Add your bot as an Administrator with full permissions.`));
|
|
61
|
+
console.log(chalk.gray(` 3. Send any message in the channel (e.g. "hello").
|
|
62
|
+
`));
|
|
63
|
+
const spinner2 = ora("Waiting for a message in your channel...").start();
|
|
64
|
+
let detected = false;
|
|
65
|
+
let offset = 0;
|
|
66
|
+
const pollToken = botTokens[0] || "";
|
|
67
|
+
while (!detected) {
|
|
68
|
+
try {
|
|
69
|
+
const res = await fetch(`https://api.telegram.org/bot${pollToken}/getUpdates?offset=${offset}&timeout=2`);
|
|
70
|
+
const json = await res.json();
|
|
71
|
+
if (json.ok && json.result.length > 0) {
|
|
72
|
+
for (const update of json.result) {
|
|
73
|
+
offset = update.update_id + 1;
|
|
74
|
+
if (update.channel_post && update.channel_post.chat) {
|
|
75
|
+
channelId = update.channel_post.chat.id.toString();
|
|
76
|
+
const title = update.channel_post.chat.title || "Unknown Channel";
|
|
77
|
+
spinner2.succeed(chalk.green(`Found channel: ${title} (${channelId})`));
|
|
78
|
+
detected = true;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} else if (!json.ok) {
|
|
83
|
+
spinner2.fail(chalk.red("Invalid Bot Token or Telegram API error."));
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
} catch (e) {
|
|
87
|
+
}
|
|
88
|
+
if (!detected) {
|
|
89
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
console.log(chalk.cyan.bold("\n Step 3: Security (Optional)"));
|
|
94
|
+
const { encryptionKeyInput } = await inquirer.prompt([{
|
|
95
|
+
type: "input",
|
|
96
|
+
name: "encryptionKeyInput",
|
|
97
|
+
message: chalk.white("Encryption key (optional, press enter to skip):")
|
|
98
|
+
}]);
|
|
99
|
+
encryptionKey = encryptionKeyInput;
|
|
27
100
|
}
|
|
28
101
|
const spinner = ora("Setting up gramobase...").start();
|
|
29
|
-
const safeToken = botToken.trim();
|
|
30
|
-
const safeChannelId = channelId.trim();
|
|
31
|
-
const safeKey = encryptionKey.trim();
|
|
32
102
|
const cwd = process.cwd();
|
|
33
103
|
const envPath = join(cwd, ".env");
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
`
|
|
37
|
-
|
|
38
|
-
|
|
104
|
+
const envContentLines = [];
|
|
105
|
+
if (botTokens.length === 1) {
|
|
106
|
+
envContentLines.push(`GRAMOBASE_BOT_TOKEN=${botTokens[0]}`);
|
|
107
|
+
} else {
|
|
108
|
+
botTokens.forEach((token, i) => {
|
|
109
|
+
envContentLines.push(`GRAMOBASE_BOT_TOKEN_${i + 1}=${token}`);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
const safeChannelId = channelId.trim();
|
|
113
|
+
const safeKey = encryptionKey.trim();
|
|
114
|
+
envContentLines.push(`GRAMOBASE_CHANNEL_ID=${safeChannelId}`);
|
|
115
|
+
envContentLines.push(safeKey ? `GRAMOBASE_ENCRYPTION_KEY=${safeKey}` : "# GRAMOBASE_ENCRYPTION_KEY=");
|
|
116
|
+
const envContent = envContentLines.join("\n");
|
|
39
117
|
writeFileSync(envPath, envContent + "\n");
|
|
118
|
+
const botTokenConfigStr = botTokens.length === 1 ? `process.env.GRAMOBASE_BOT_TOKEN!` : `[
|
|
119
|
+
${botTokens.map((_, i) => ` process.env.GRAMOBASE_BOT_TOKEN_${i + 1}!,`).join("\n")}
|
|
120
|
+
]`;
|
|
40
121
|
const configContent = `import { GramoBaseConfig } from 'gramobase';
|
|
41
122
|
|
|
42
123
|
const config: GramoBaseConfig = {
|
|
43
|
-
botToken:
|
|
124
|
+
botToken: ${botTokenConfigStr},
|
|
44
125
|
channelId: process.env.GRAMOBASE_CHANNEL_ID!,
|
|
45
126
|
// encryptionKey: process.env.GRAMOBASE_ENCRYPTION_KEY,
|
|
46
127
|
cacheMaxBytes: 64 * 1024 * 1024, // 64MB hot cache
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../bin/gramobase.ts"],"names":[],"mappings":";;;;;;;;;;AAQA,IAAM,GAAA,GAAM,EAAE,OAAA,EAAS,OAAA,EAAQ;AAE/B,IAAM,OAAA,GAAU,IAAI,OAAA,EAAQ;AAE5B,OAAA,CACG,IAAA,CAAK,WAAW,CAAA,CAChB,WAAA,CAAY,KAAA,CAAM,IAAA,CAAK,kDAAkD,CAAC,CAAA,CAC1E,OAAA,CAAQ,GAAA,CAAI,OAAO,CAAA;AAItB,OAAA,CACG,OAAA,CAAQ,MAAM,CAAA,CACd,WAAA,CAAY,oCAAoC,CAAA,CAChD,MAAA,CAAO,OAAA,EAAS,4BAA4B,CAAA,CAC5C,MAAA,CAAO,OAAO,IAAA,KAAS;AACtB,EAAA,OAAA,CAAQ,GAAA,CAAI,IAAA,GAAO,KAAA,CAAM,IAAA,CAAK,IAAA,CAAK,aAAa,CAAA,GAAI,KAAA,CAAM,IAAA,CAAK,4BAAuB,CAAC,CAAA;AAEvF,EAAA,IAAI,QAAA,GAAW,EAAA;AACf,EAAA,IAAI,SAAA,GAAY,EAAA;AAChB,EAAA,IAAI,aAAA,GAAgB,EAAA;AAEpB,EAAA,IAAI,CAAC,KAAK,GAAA,EAAK;AACb,IAAA,MAAM,EAAA,GAAK,gBAAgB,EAAE,KAAA,EAAO,QAAQ,KAAA,EAAO,MAAA,EAAQ,OAAA,CAAQ,MAAA,EAAQ,CAAA;AAC3E,IAAA,MAAM,GAAA,GAAM,CAAC,CAAA,KAAc,IAAI,OAAA,CAAgB,CAAC,GAAA,KAAQ,EAAA,CAAG,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA;AAE3E,IAAA,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,IAAA,CAAK,iDAAiD,CAAC,CAAA;AACzE,IAAA,QAAA,GAAW,MAAM,GAAA,CAAI,KAAA,CAAM,KAAA,CAAM,eAAe,CAAC,CAAA;AACjD,IAAA,SAAA,GAAY,MAAM,GAAA,CAAI,KAAA,CAAM,KAAA,CAAM,qCAAqC,CAAC,CAAA;AACxE,IAAA,aAAA,GAAgB,MAAM,GAAA,CAAI,KAAA,CAAM,KAAA,CAAM,oDAAoD,CAAC,CAAA;AAC3F,IAAA,EAAA,CAAG,KAAA,EAAM;AAAA,EACX;AAEA,EAAA,MAAM,OAAA,GAAU,GAAA,CAAI,yBAAyB,CAAA,CAAE,KAAA,EAAM;AAGrD,EAAA,MAAM,SAAA,GAAY,SAAS,IAAA,EAAK;AAChC,EAAA,MAAM,aAAA,GAAgB,UAAU,IAAA,EAAK;AACrC,EAAA,MAAM,OAAA,GAAU,cAAc,IAAA,EAAK;AAGnC,EAAA,MAAM,GAAA,GAAM,QAAQ,GAAA,EAAI;AACxB,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,GAAA,EAAK,MAAM,CAAA;AAChC,EAAA,MAAM,UAAA,GAAa;AAAA,IACjB,uBAAuB,SAAS,CAAA,CAAA;AAAA,IAChC,wBAAwB,aAAa,CAAA,CAAA;AAAA,IACrC,OAAA,GAAU,CAAA,yBAAA,EAA4B,OAAO,CAAA,CAAA,GAAK;AAAA,GACpD,CAAE,KAAK,IAAI,CAAA;AAEX,EAAA,aAAA,CAAc,OAAA,EAAS,aAAa,IAAI,CAAA;AAGxC,EAAA,MAAM,aAAA,GAAgB,CAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA;AAetB,EAAA,aAAA,CAAc,IAAA,CAAK,GAAA,EAAK,qBAAqB,CAAA,EAAG,aAAa,CAAA;AAG7D,EAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,GAAA,EAAK,WAAA,EAAa,YAAY,CAAA;AACzD,EAAA,IAAI,CAAC,UAAA,CAAW,aAAa,CAAA,EAAG;AAC9B,IAAA,SAAA,CAAU,aAAA,EAAe,EAAE,SAAA,EAAW,IAAA,EAAM,CAAA;AAAA,EAC9C;AAEA,EAAA,OAAA,CAAQ,OAAA,CAAQ,KAAA,CAAM,KAAA,CAAM,wBAAwB,CAAC,CAAA;AAErD,EAAA,OAAA,CAAQ,GAAA,CAAI;AAAA,EAAA,EACZ,KAAA,CAAM,IAAA,CAAK,gBAAgB,CAAC;AAAA,EAAA,EAC5B,KAAA,CAAM,IAAA,CAAK,cAAI,CAAC,CAAA;AAAA,EAAA,EAChB,KAAA,CAAM,IAAA,CAAK,cAAI,CAAC,CAAA;AAAA,EAAA,EAChB,KAAA,CAAM,IAAA,CAAK,cAAI,CAAC,CAAA;;AAAA,EAAA,EAEhB,KAAA,CAAM,IAAA,CAAK,aAAa,CAAC;AAAA,EAAA,EACzB,KAAA,CAAM,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,EAAA,EAChB,KAAA,CAAM,KAAK,IAAI,CAAC,QAAQ,KAAA,CAAM,IAAA,CAAK,mBAAmB,CAAC,CAAA;AAAA,EAAA,EACvD,KAAA,CAAM,KAAK,IAAI,CAAC,oBAAoB,KAAA,CAAM,IAAA,CAAK,0CAA0C,CAAC;AAAA,CAC7F,CAAA;AACC,CAAC,CAAA;AAIH,OAAA,CACG,QAAQ,SAAS,CAAA,CACjB,WAAA,CAAY,wBAAwB,EACpC,MAAA,CAAO,oBAAA,EAAsB,4BAAA,EAA8B,GAAG,EAC9D,MAAA,CAAO,UAAA,EAAY,uBAAuB,CAAA,CAC1C,MAAA,CAAO,OAAO,IAAA,KAAS;AACtB,EAAA,MAAM,OAAA,GAAU,GAAA,CAAI,uBAAuB,CAAA,CAAE,KAAA,EAAM;AACnD,EAAA,IAAI;AACF,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,OAAA,CAAQ,GAAA,IAAO,qBAAqB,CAAA;AAE5D,IAAA,OAAA,CAAQ,IAAA,GAAO,eAAA;AACf,IAAA,OAAA,CAAQ,QAAQ,0DAA0D,CAAA;AAAA,EAC5E,SAAS,CAAA,EAAQ;AACf,IAAA,OAAA,CAAQ,IAAA,CAAK,MAAM,GAAA,CAAI,UAAA,IAAc,aAAa,KAAA,GAAQ,CAAA,CAAE,OAAA,GAAU,eAAA,CAAgB,CAAC,CAAA;AAAA,EACzF;AACF,CAAC,CAAA;AAIH,OAAA,CACG,QAAQ,QAAQ,CAAA,CAChB,YAAY,qCAAqC,CAAA,CACjD,OAAO,YAAY;AAClB,EAAA,MAAM,OAAA,GAAU,GAAA,CAAI,oBAAoB,CAAA,CAAE,KAAA,EAAM;AAChD,EAAA,IAAI;AACF,IAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,qBAAqB,CAAA;AAC/C,IAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,GAAA,CAAI,sBAAsB,CAAA;AAEpD,IAAA,IAAI,CAAC,KAAA,IAAS,CAAC,SAAA,EAAW;AACxB,MAAA,OAAA,CAAQ,KAAK,gDAA2C,CAAA;AACxD,MAAA;AAAA,IACF;AAGA,IAAA,MAAM,MAAM,MAAM,KAAA,CAAM,+BAA+B,kBAAA,CAAmB,KAAK,CAAC,CAAA,MAAA,CAAQ,CAAA;AACxF,IAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAE5B,IAAA,IAAI,KAAK,EAAA,EAAI;AACX,MAAA,OAAA,CAAQ,OAAA,CAAQ,KAAA,CAAM,KAAA,CAAM,WAAW,CAAC,CAAA;AACxC,MAAA,OAAA,CAAQ,GAAA,CAAI;AAAA,EAAA,EAChB,KAAA,CAAM,IAAA,CAAK,MAAM,CAAC,QAAQ,KAAA,CAAM,IAAA,CAAK,GAAA,GAAM,IAAA,CAAK,OAAO,QAAQ,CAAC,CAAA,EAAA,EAAK,IAAA,CAAK,OAAO,UAAU,CAAA;AAAA,EAAA,EAC3F,KAAA,CAAM,KAAK,UAAU,CAAC,IAAI,KAAA,CAAM,IAAA,CAAK,SAAS,CAAC;AAAA,EAAA,EAC/C,KAAA,CAAM,KAAK,SAAS,CAAC,KAAK,KAAA,CAAM,KAAA,CAAM,eAAU,CAAC;AAAA,CACpD,CAAA;AAAA,IACK,CAAA,MAAO;AACL,MAAA,OAAA,CAAQ,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,uCAAkC,CAAC,CAAA;AAAA,IAC5D;AAAA,EACF,SAAS,CAAA,EAAQ;AACf,IAAA,OAAA,CAAQ,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,mBAAmB,CAAC,CAAA;AAAA,EAC7C;AACF,CAAC,CAAA;AAIH,OAAA,CACG,OAAA,CAAQ,iBAAiB,CAAA,CACzB,WAAA,CAAY,oCAAoC,CAAA,CAChD,MAAA,CAAO,mBAAA,EAAqB,sDAAsD,CAAA,CAClF,MAAA,CAAO,CAAC,MAAc,IAAA,KAAS;AAE9B,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,OAAA,CAAQ,iBAAA,EAAmB,EAAE,CAAA;AACnD,EAAA,IAAI,CAAC,QAAA,IAAY,QAAA,KAAa,IAAA,EAAM;AAClC,IAAA,OAAA,CAAQ,KAAA,CAAM,KAAA,CAAM,GAAA,CAAI,mFAAmF,CAAC,CAAA;AAC5G,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAChB;AAEA,EAAA,MAAM,SAAmB,IAAA,CAAK,MAAA,GACzB,IAAA,CAAK,MAAA,CAAkB,MAAM,GAAG,CAAA,CAAE,GAAA,CAAI,CAAC,MAAc,CAAA,CAAE,IAAA,EAAM,CAAA,GAC9D,CAAC,aAAa,CAAA;AAElB,EAAA,MAAM,YAAA,GAAe,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAc;AAC7C,IAAA,MAAM,CAAC,KAAA,EAAO,KAAK,CAAA,GAAI,CAAA,CAAE,MAAM,GAAG,CAAA;AAElC,IAAA,MAAM,SAAA,GAAA,CAAa,KAAA,IAAS,OAAA,EAAS,OAAA,CAAQ,kBAAkB,EAAE,CAAA;AACjE,IAAA,MAAM,OAAA,GACJ,UAAU,QAAA,GAAW,YAAA,GACrB,UAAU,SAAA,GAAY,aAAA,GACtB,KAAA,KAAU,MAAA,GAAS,YAAA,GACnB,YAAA;AACF,IAAA,OAAO,CAAA,EAAA,EAAK,SAAS,CAAA,EAAA,EAAK,OAAO,CAAA,CAAA,CAAA;AAAA,EACnC,CAAC,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA;AAEZ,EAAA,MAAM,MAAA,GAAS,CAAA;AAAA;;AAAA,aAAA,EAGJ,QAAQ,CAAA;AAAA,EACrB,YAAY;AAAA;;AAAA,YAAA,EAGA,UAAA,CAAW,QAAQ,CAAC,CAAA,kBAAA,EAAqB,QAAQ,CAAA;;AAAA;AAAA;AAAA,SAAA,EAIpD,QAAQ,CAAA,mBAAA,EAAsB,QAAQ,CAAA,cAAA,EAAiB,QAAQ,CAAA;AAAA,SAAA,EAC/D,QAAQ,iBAAiB,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAA,CAAe,CAAA,CAAE,MAAM,GAAG,CAAA,CAAE,CAAC,CAAA,IAAK,OAAA,EAAS,QAAQ,gBAAA,EAAkB,EAAE,IAAI,OAAQ,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,CAAA;AAI3I,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,OAAA,CAAQ,GAAA,IAAO,WAAW,CAAA;AAC3C,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,GAAA,EAAK,CAAA,EAAG,QAAQ,CAAA,UAAA,CAAY,CAAA;AAEjD,EAAA,IAAI,CAAC,WAAW,GAAG,CAAA,YAAa,GAAA,EAAK,EAAE,SAAA,EAAW,IAAA,EAAM,CAAA;AACxD,EAAA,aAAA,CAAc,SAAS,MAAM,CAAA;AAE7B,EAAA,OAAA,CAAQ,GAAA,CAAI;AAAA,EAAA,EAAO,KAAA,CAAM,KAAA,CAAM,QAAG,CAAC,CAAA,WAAA,EAAc,MAAM,IAAA,CAAK,CAAA,UAAA,EAAa,QAAQ,CAAA,UAAA,CAAY,CAAC;AAAA,CAAI,CAAA;AACpG,CAAC,CAAA;AAIH,OAAA,CACG,OAAA,CAAQ,QAAQ,CAAA,CAChB,WAAA,CAAY,sCAAsC,CAAA,CAClD,MAAA,CAAO,eAAA,EAAiB,mBAAA,EAAqB,MAAM,CAAA,CACnD,MAAA,CAAO,CAAC,IAAA,KAAS;AAEhB,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,IAAA,CAAK,IAAA,EAAgB,EAAE,CAAA;AAC7C,EAAA,IAAI,MAAM,IAAI,CAAA,IAAK,IAAA,GAAO,CAAA,IAAK,OAAO,KAAA,EAAO;AAC3C,IAAA,OAAA,CAAQ,KAAA,CAAM,KAAA,CAAM,GAAA,CAAI,8BAA8B,CAAC,CAAA;AACvD,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAChB;AACA,EAAA,OAAA,CAAQ,GAAA,CAAI;AAAA,EAAA,EAAO,KAAA,CAAM,IAAA,CAAK,IAAA,CAAK,kBAAkB,CAAC;AAAA,CAAI,CAAA;AAC1D,EAAA,OAAA,CAAQ,IAAI,CAAA,EAAA,EAAK,KAAA,CAAM,IAAA,CAAK,MAAM,CAAC,CAAA,CAAA,EAAI,KAAA,CAAM,IAAA,CAAK,CAAA,iBAAA,EAAoB,IAAI,CAAA,CAAE,CAAC,IAAI,KAAA,CAAM,IAAA,CAAK,iBAAiB,CAAC;AAAA,CAAI,CAAA;AAClH,EAAA,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,MAAA,CAAO,uFAAkF,CAAC,CAAA;AAC9G,CAAC,CAAA;AAEH,SAAS,WAAW,CAAA,EAAmB;AACrC,EAAA,OAAO,CAAA,CAAE,OAAO,CAAC,CAAA,CAAE,aAAY,GAAI,CAAA,CAAE,MAAM,CAAC,CAAA;AAC9C;AAEA,OAAA,CAAQ,KAAA,EAAM","file":"gramobase.js","sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { createInterface } from 'readline';\nimport { writeFileSync, existsSync, mkdirSync } from 'fs';\nimport { join, resolve, basename } from 'path';\n\nconst pkg = { version: '0.1.0' };\n\nconst program = new Command();\n\nprogram\n .name('gramobase')\n .description(chalk.cyan('Telegram as your free, infinite backend database'))\n .version(pkg.version);\n\n// ─── gramobase init ──────────────────────────────────────────────────────────\n\nprogram\n .command('init')\n .description('Initialize a new gramobase project')\n .option('--yes', 'Skip prompts, use defaults')\n .action(async (opts) => {\n console.log('\\n' + chalk.bold.cyan(' gramobase') + chalk.gray(' — Telegram backend\\n'));\n\n let botToken = '';\n let channelId = '';\n let encryptionKey = '';\n\n if (!opts.yes) {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n const ask = (q: string) => new Promise<string>((res) => rl.question(q, res));\n\n console.log(chalk.gray(' Get a bot token from @BotFather on Telegram\\n'));\n botToken = await ask(chalk.white(' Bot token: '));\n channelId = await ask(chalk.white(' Channel ID (e.g. -100123456789): '));\n encryptionKey = await ask(chalk.white(' Encryption key (optional, press enter to skip): '));\n rl.close();\n }\n\n const spinner = ora('Setting up gramobase...').start();\n\n // Sanitize inputs — only use basename of any path-like values\n const safeToken = botToken.trim();\n const safeChannelId = channelId.trim();\n const safeKey = encryptionKey.trim();\n\n // Create .env — never write tokens to paths derived from user input\n const cwd = process.cwd();\n const envPath = join(cwd, '.env');\n const envContent = [\n `GRAMOBASE_BOT_TOKEN=${safeToken}`,\n `GRAMOBASE_CHANNEL_ID=${safeChannelId}`,\n safeKey ? `GRAMOBASE_ENCRYPTION_KEY=${safeKey}` : '# GRAMOBASE_ENCRYPTION_KEY=',\n ].join('\\n');\n\n writeFileSync(envPath, envContent + '\\n');\n\n // Create gramobase.config.ts\n const configContent = `import { GramoBaseConfig } from 'gramobase';\n\nconst config: GramoBaseConfig = {\n botToken: process.env.GRAMOBASE_BOT_TOKEN!,\n channelId: process.env.GRAMOBASE_CHANNEL_ID!,\n // encryptionKey: process.env.GRAMOBASE_ENCRYPTION_KEY,\n cacheMaxBytes: 64 * 1024 * 1024, // 64MB hot cache\n cacheTtlMs: 60_000,\n concurrency: 25,\n debug: process.env.NODE_ENV === 'development',\n};\n\nexport default config;\n`;\n\n writeFileSync(join(cwd, 'gramobase.config.ts'), configContent);\n\n // Create migrations folder — path is hardcoded, not from user input\n const migrationsDir = join(cwd, 'gramobase', 'migrations');\n if (!existsSync(migrationsDir)) {\n mkdirSync(migrationsDir, { recursive: true });\n }\n\n spinner.succeed(chalk.green('gramobase initialized!'));\n\n console.log(`\n ${chalk.bold('Files created:')}\n ${chalk.gray('├─')} .env\n ${chalk.gray('└─')} gramobase.config.ts\n ${chalk.gray('└─')} gramobase/migrations/\n\n ${chalk.bold('Next steps:')}\n ${chalk.cyan('1.')} Add your bot token and channel ID to .env\n ${chalk.cyan('2.')} Run ${chalk.bold('gramobase migrate')} to initialize the database\n ${chalk.cyan('3.')} Import and use: ${chalk.gray(\"import { createClient } from 'gramobase'\")}\n`);\n });\n\n// ─── gramobase migrate ───────────────────────────────────────────────────────\n\nprogram\n .command('migrate')\n .description('Run pending migrations')\n .option('--rollback <steps>', 'Rollback N migration steps', '0')\n .option('--status', 'Show migration status')\n .action(async (opts) => {\n const spinner = ora('Loading migrations...').start();\n try {\n const configPath = join(process.cwd(), 'gramobase.config.ts');\n\n spinner.text = 'Connecting...';\n spinner.succeed('Migration runner ready (run in your project after build)');\n } catch (e: any) {\n spinner.fail(chalk.red('Failed: ' + (e instanceof Error ? e.message : 'Unknown error')));\n }\n });\n\n// ─── gramobase status ────────────────────────────────────────────────────────\n\nprogram\n .command('status')\n .description('Show database and connection status')\n .action(async () => {\n const spinner = ora('Checking status...').start();\n try {\n const token = process.env['GRAMOBASE_BOT_TOKEN'];\n const channelId = process.env['GRAMOBASE_CHANNEL_ID'];\n\n if (!token || !channelId) {\n spinner.fail('.env not found — run gramobase init first');\n return;\n }\n\n // Ping Telegram Bot API — token is from env, not user input in this context\n const res = await fetch(`https://api.telegram.org/bot${encodeURIComponent(token)}/getMe`);\n const json = await res.json() as any;\n\n if (json.ok) {\n spinner.succeed(chalk.green('Connected'));\n console.log(`\n ${chalk.bold('Bot:')} ${chalk.cyan('@' + json.result.username)} (${json.result.first_name})\n ${chalk.bold('Channel:')} ${chalk.cyan(channelId)}\n ${chalk.bold('Status:')} ${chalk.green('● Online')}\n`);\n } else {\n spinner.fail(chalk.red('Bot API error — check your token'));\n }\n } catch (e: any) {\n spinner.fail(chalk.red('Connection failed'));\n }\n });\n\n// ─── gramobase generate ──────────────────────────────────────────────────────\n\nprogram\n .command('generate <name>')\n .description('Generate a typed collection schema')\n .option('--fields <fields>', 'Comma-separated fields (e.g. name:string,age:number)')\n .action((name: string, opts) => {\n // Sanitize name — only allow alphanumeric + underscore/hyphen\n const safeName = name.replace(/[^a-zA-Z0-9_-]/g, '');\n if (!safeName || safeName !== name) {\n console.error(chalk.red(' Error: Schema name must contain only letters, numbers, underscores, and hyphens'));\n process.exit(1);\n }\n\n const fields: string[] = opts.fields\n ? (opts.fields as string).split(',').map((f: string) => f.trim())\n : ['name:string'];\n\n const schemaFields = fields.map((f: string) => {\n const [fname, ftype] = f.split(':');\n // Sanitize field name\n const safeFname = (fname ?? 'field').replace(/[^a-zA-Z0-9_]/g, '');\n const zodType =\n ftype === 'number' ? 'z.number()' :\n ftype === 'boolean' ? 'z.boolean()' :\n ftype === 'date' ? 'z.string()' :\n 'z.string()';\n return ` ${safeFname}: ${zodType},`;\n }).join('\\n');\n\n const output = `import { z } from 'zod';\nimport { createClient } from 'gramobase';\n\nexport const ${safeName}Schema = z.object({\n${schemaFields}\n});\n\nexport type ${capitalize(safeName)} = z.infer<typeof ${safeName}Schema>;\n\n// Usage:\n// const db = createClient(config);\n// const ${safeName}s = db.collection('${safeName}s', { schema: ${safeName}Schema });\n// await ${safeName}s.insertOne({ ${fields.map((f: string) => (f.split(':')[0] ?? 'field').replace(/[^a-zA-Z0-9_]/g, '') + ': ...' ).join(', ')} });\n`;\n\n // Path is constructed from sanitized name only — no user-controlled path traversal\n const dir = join(process.cwd(), 'gramobase');\n const outPath = join(dir, `${safeName}.schema.ts`);\n\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n writeFileSync(outPath, output);\n\n console.log(`\\n ${chalk.green('✓')} Generated ${chalk.cyan(`gramobase/${safeName}.schema.ts`)}\\n`);\n });\n\n// ─── gramobase studio ────────────────────────────────────────────────────────\n\nprogram\n .command('studio')\n .description('Open the gramobase browser studio UI')\n .option('--port <port>', 'Port to listen on', '4242')\n .action((opts) => {\n // Validate port is numeric and in valid range\n const port = parseInt(opts.port as string, 10);\n if (isNaN(port) || port < 1 || port > 65535) {\n console.error(chalk.red(' Error: Invalid port number'));\n process.exit(1);\n }\n console.log(`\\n ${chalk.bold.cyan('gramobase studio')}\\n`);\n console.log(` ${chalk.gray('Open')} ${chalk.cyan(`http://localhost:${port}`)} ${chalk.gray('in your browser')}\\n`);\n console.log(chalk.yellow(' Studio UI coming in v0.2.0 — contribute at github.com/yourusername/gramobase\\n'));\n });\n\nfunction capitalize(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\nprogram.parse();\n"]}
|
|
1
|
+
{"version":3,"sources":["../../bin/gramobase.ts"],"names":["spinner"],"mappings":"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","file":"gramobase.js","sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { writeFileSync, existsSync, mkdirSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport inquirer from 'inquirer';\n\nlet pkg = { version: '0.0.0' };\ntry {\n const pkgUrl = new URL('../../package.json', import.meta.url);\n pkg = JSON.parse(readFileSync(pkgUrl, 'utf-8'));\n} catch (e) {\n try {\n const pkgUrl2 = new URL('../package.json', import.meta.url);\n pkg = JSON.parse(readFileSync(pkgUrl2, 'utf-8'));\n } catch (e2) {}\n}\n\nconst program = new Command();\n\nprogram\n .name('gramobase')\n .description(chalk.cyan('Telegram as your free, infinite backend database'))\n .version(pkg.version);\n\n// ─── gramobase init ──────────────────────────────────────────────────────────\n\nprogram\n .command('init')\n .description('Initialize a new gramobase project')\n .option('--yes', 'Skip prompts, use defaults')\n .action(async (opts) => {\n console.log('\\n' + chalk.bold.cyan(' gramobase') + chalk.gray(' — Telegram backend\\n'));\n\n let botTokens: string[] = [];\n let channelId = '';\n let encryptionKey = '';\n\n if (!opts.yes) {\n console.log(chalk.cyan.bold('\\n Step 1: Bot Tokens (Anti-flood rotation)'));\n console.log(chalk.gray(' You can use multiple bot tokens to increase your rate limit (30 req/s per bot).'));\n \n const { numBotsStr } = await inquirer.prompt([{\n type: 'input',\n name: 'numBotsStr',\n message: chalk.white('How many bot tokens do you want to add? (Default: 1):')\n }]);\n const numBots = Math.max(1, parseInt(numBotsStr, 10) || 1);\n\n console.log(chalk.gray(' Create your bots by messaging @BotFather on Telegram and copy the HTTP API tokens.'));\n for (let i = 1; i <= numBots; i++) {\n const { token } = await inquirer.prompt([{\n type: 'password',\n name: 'token',\n message: chalk.white(`Bot token ${i}:`),\n mask: chalk.red('*')\n }]);\n botTokens.push(token.trim());\n }\n\n console.log(chalk.cyan.bold('\\n Step 2: Channel ID'));\n console.log(chalk.gray(' You can enter your Channel ID manually (e.g. -100123456789)'));\n console.log(chalk.gray(' OR leave it blank to auto-detect it.'));\n \n const { channelIdInput } = await inquirer.prompt([{\n type: 'input',\n name: 'channelIdInput',\n message: chalk.white('Channel ID (Press Enter to auto-detect):')\n }]);\n \n channelId = channelIdInput.trim();\n\n if (!channelId) {\n console.log(chalk.yellow('\\n [Auto-Detect Mode]'));\n console.log(chalk.gray(` 1. Create a private Telegram channel.`));\n console.log(chalk.gray(` 2. Add your bot as an Administrator with full permissions.`));\n console.log(chalk.gray(` 3. Send any message in the channel (e.g. \"hello\").\\n`));\n \n const spinner = ora('Waiting for a message in your channel...').start();\n \n let detected = false;\n let offset = 0;\n // Use the first token to poll\n const pollToken = botTokens[0] || '';\n \n while (!detected) {\n try {\n const res = await fetch(`https://api.telegram.org/bot${pollToken}/getUpdates?offset=${offset}&timeout=2`);\n const json: any = await res.json();\n \n if (json.ok && json.result.length > 0) {\n for (const update of json.result) {\n offset = update.update_id + 1;\n if (update.channel_post && update.channel_post.chat) {\n channelId = update.channel_post.chat.id.toString();\n const title = update.channel_post.chat.title || 'Unknown Channel';\n spinner.succeed(chalk.green(`Found channel: ${title} (${channelId})`));\n detected = true;\n break;\n }\n }\n } else if (!json.ok) {\n spinner.fail(chalk.red('Invalid Bot Token or Telegram API error.'));\n process.exit(1);\n }\n } catch (e) {\n // Ignore fetch errors and continue polling\n }\n \n if (!detected) {\n await new Promise((resolve) => setTimeout(resolve, 2000));\n }\n }\n }\n\n console.log(chalk.cyan.bold('\\n Step 3: Security (Optional)'));\n const { encryptionKeyInput } = await inquirer.prompt([{\n type: 'input',\n name: 'encryptionKeyInput',\n message: chalk.white('Encryption key (optional, press enter to skip):')\n }]);\n encryptionKey = encryptionKeyInput;\n }\n\n const spinner = ora('Setting up gramobase...').start();\n\n // Create .env — never write tokens to paths derived from user input\n const cwd = process.cwd();\n const envPath = join(cwd, '.env');\n const envContentLines = [];\n \n if (botTokens.length === 1) {\n envContentLines.push(`GRAMOBASE_BOT_TOKEN=${botTokens[0]}`);\n } else {\n botTokens.forEach((token, i) => {\n envContentLines.push(`GRAMOBASE_BOT_TOKEN_${i + 1}=${token}`);\n });\n }\n \n const safeChannelId = channelId.trim();\n const safeKey = encryptionKey.trim();\n \n envContentLines.push(`GRAMOBASE_CHANNEL_ID=${safeChannelId}`);\n envContentLines.push(safeKey ? `GRAMOBASE_ENCRYPTION_KEY=${safeKey}` : '# GRAMOBASE_ENCRYPTION_KEY=');\n \n const envContent = envContentLines.join('\\n');\n writeFileSync(envPath, envContent + '\\n');\n\n const botTokenConfigStr = botTokens.length === 1\n ? `process.env.GRAMOBASE_BOT_TOKEN!`\n : `[\\n${botTokens.map((_, i) => ` process.env.GRAMOBASE_BOT_TOKEN_${i + 1}!,`).join('\\n')}\\n ]`;\n\n // Create gramobase.config.ts\n const configContent = `import { GramoBaseConfig } from 'gramobase';\n\nconst config: GramoBaseConfig = {\n botToken: ${botTokenConfigStr},\n channelId: process.env.GRAMOBASE_CHANNEL_ID!,\n // encryptionKey: process.env.GRAMOBASE_ENCRYPTION_KEY,\n cacheMaxBytes: 64 * 1024 * 1024, // 64MB hot cache\n cacheTtlMs: 60_000,\n concurrency: 25,\n debug: process.env.NODE_ENV === 'development',\n};\n\nexport default config;\n`;\n\n writeFileSync(join(cwd, 'gramobase.config.ts'), configContent);\n\n // Create migrations folder — path is hardcoded, not from user input\n const migrationsDir = join(cwd, 'gramobase', 'migrations');\n if (!existsSync(migrationsDir)) {\n mkdirSync(migrationsDir, { recursive: true });\n }\n\n spinner.succeed(chalk.green('gramobase initialized!'));\n\n console.log(`\n ${chalk.bold('Files created:')}\n ${chalk.gray('├─')} .env\n ${chalk.gray('└─')} gramobase.config.ts\n ${chalk.gray('└─')} gramobase/migrations/\n\n ${chalk.bold('Next steps:')}\n ${chalk.cyan('1.')} Add your bot token and channel ID to .env\n ${chalk.cyan('2.')} Run ${chalk.bold('gramobase migrate')} to initialize the database\n ${chalk.cyan('3.')} Import and use: ${chalk.gray(\"import { createClient } from 'gramobase'\")}\n`);\n });\n\n// ─── gramobase migrate ───────────────────────────────────────────────────────\n\nprogram\n .command('migrate')\n .description('Run pending migrations')\n .option('--rollback <steps>', 'Rollback N migration steps', '0')\n .option('--status', 'Show migration status')\n .action(async (opts) => {\n const spinner = ora('Loading migrations...').start();\n try {\n const configPath = join(process.cwd(), 'gramobase.config.ts');\n\n spinner.text = 'Connecting...';\n spinner.succeed('Migration runner ready (run in your project after build)');\n } catch (e: any) {\n spinner.fail(chalk.red('Failed: ' + (e instanceof Error ? e.message : 'Unknown error')));\n }\n });\n\n// ─── gramobase status ────────────────────────────────────────────────────────\n\nprogram\n .command('status')\n .description('Show database and connection status')\n .action(async () => {\n const spinner = ora('Checking status...').start();\n try {\n const token = process.env['GRAMOBASE_BOT_TOKEN'];\n const channelId = process.env['GRAMOBASE_CHANNEL_ID'];\n\n if (!token || !channelId) {\n spinner.fail('.env not found — run gramobase init first');\n return;\n }\n\n // Ping Telegram Bot API — token is from env, not user input in this context\n const res = await fetch(`https://api.telegram.org/bot${encodeURIComponent(token)}/getMe`);\n const json = await res.json() as any;\n\n if (json.ok) {\n spinner.succeed(chalk.green('Connected'));\n console.log(`\n ${chalk.bold('Bot:')} ${chalk.cyan('@' + json.result.username)} (${json.result.first_name})\n ${chalk.bold('Channel:')} ${chalk.cyan(channelId)}\n ${chalk.bold('Status:')} ${chalk.green('● Online')}\n`);\n } else {\n spinner.fail(chalk.red('Bot API error — check your token'));\n }\n } catch (e: any) {\n spinner.fail(chalk.red('Connection failed'));\n }\n });\n\n// ─── gramobase generate ──────────────────────────────────────────────────────\n\nprogram\n .command('generate <name>')\n .description('Generate a typed collection schema')\n .option('--fields <fields>', 'Comma-separated fields (e.g. name:string,age:number)')\n .action((name: string, opts) => {\n // Sanitize name — only allow alphanumeric + underscore/hyphen\n const safeName = name.replace(/[^a-zA-Z0-9_-]/g, '');\n if (!safeName || safeName !== name) {\n console.error(chalk.red(' Error: Schema name must contain only letters, numbers, underscores, and hyphens'));\n process.exit(1);\n }\n\n const fields: string[] = opts.fields\n ? (opts.fields as string).split(',').map((f: string) => f.trim())\n : ['name:string'];\n\n const schemaFields = fields.map((f: string) => {\n const [fname, ftype] = f.split(':');\n // Sanitize field name\n const safeFname = (fname ?? 'field').replace(/[^a-zA-Z0-9_]/g, '');\n const zodType =\n ftype === 'number' ? 'z.number()' :\n ftype === 'boolean' ? 'z.boolean()' :\n ftype === 'date' ? 'z.string()' :\n 'z.string()';\n return ` ${safeFname}: ${zodType},`;\n }).join('\\n');\n\n const output = `import { z } from 'zod';\nimport { createClient } from 'gramobase';\n\nexport const ${safeName}Schema = z.object({\n${schemaFields}\n});\n\nexport type ${capitalize(safeName)} = z.infer<typeof ${safeName}Schema>;\n\n// Usage:\n// const db = createClient(config);\n// const ${safeName}s = db.collection('${safeName}s', { schema: ${safeName}Schema });\n// await ${safeName}s.insertOne({ ${fields.map((f: string) => (f.split(':')[0] ?? 'field').replace(/[^a-zA-Z0-9_]/g, '') + ': ...' ).join(', ')} });\n`;\n\n // Path is constructed from sanitized name only — no user-controlled path traversal\n const dir = join(process.cwd(), 'gramobase');\n const outPath = join(dir, `${safeName}.schema.ts`);\n\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n writeFileSync(outPath, output);\n\n console.log(`\\n ${chalk.green('✓')} Generated ${chalk.cyan(`gramobase/${safeName}.schema.ts`)}\\n`);\n });\n\n// ─── gramobase studio ────────────────────────────────────────────────────────\n\nprogram\n .command('studio')\n .description('Open the gramobase browser studio UI')\n .option('--port <port>', 'Port to listen on', '4242')\n .action((opts) => {\n // Validate port is numeric and in valid range\n const port = parseInt(opts.port as string, 10);\n if (isNaN(port) || port < 1 || port > 65535) {\n console.error(chalk.red(' Error: Invalid port number'));\n process.exit(1);\n }\n console.log(`\\n ${chalk.bold.cyan('gramobase studio')}\\n`);\n console.log(` ${chalk.gray('Open')} ${chalk.cyan(`http://localhost:${port}`)} ${chalk.gray('in your browser')}\\n`);\n console.log(chalk.yellow(' Studio UI coming in v0.2.0 — contribute at github.com/yourusername/gramobase\\n'));\n });\n\nfunction capitalize(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\nprogram.parse();\n"]}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
|
|
5
|
+
// gramobase — Telegram as your free, infinite backend
|
|
6
|
+
|
|
7
|
+
function useGramoQuery(endpoint, options) {
|
|
8
|
+
const [data, setData] = react.useState(options?.initialData ?? null);
|
|
9
|
+
const [isLoading, setIsLoading] = react.useState(!options?.initialData);
|
|
10
|
+
const [error, setError] = react.useState(null);
|
|
11
|
+
const fetcher = react.useCallback(async () => {
|
|
12
|
+
try {
|
|
13
|
+
setIsLoading(true);
|
|
14
|
+
const res = await fetch(endpoint);
|
|
15
|
+
const json = await res.json();
|
|
16
|
+
if (!res.ok) {
|
|
17
|
+
throw new Error(json.error || "Failed to fetch data");
|
|
18
|
+
}
|
|
19
|
+
setData(json);
|
|
20
|
+
setError(null);
|
|
21
|
+
} catch (err) {
|
|
22
|
+
setError(err);
|
|
23
|
+
setData(null);
|
|
24
|
+
} finally {
|
|
25
|
+
setIsLoading(false);
|
|
26
|
+
}
|
|
27
|
+
}, [endpoint]);
|
|
28
|
+
react.useEffect(() => {
|
|
29
|
+
fetcher();
|
|
30
|
+
}, [fetcher]);
|
|
31
|
+
const mutate = react.useCallback((newData) => {
|
|
32
|
+
setData((prev) => {
|
|
33
|
+
if (typeof newData === "function") {
|
|
34
|
+
return newData(prev);
|
|
35
|
+
}
|
|
36
|
+
return newData;
|
|
37
|
+
});
|
|
38
|
+
}, []);
|
|
39
|
+
return { data, isLoading, error, mutate, refetch: fetcher };
|
|
40
|
+
}
|
|
41
|
+
function useGramoMutation(endpoint, method = "POST", options) {
|
|
42
|
+
const [isLoading, setIsLoading] = react.useState(false);
|
|
43
|
+
const [error, setError] = react.useState(null);
|
|
44
|
+
const mutate = async (variables) => {
|
|
45
|
+
setIsLoading(true);
|
|
46
|
+
setError(null);
|
|
47
|
+
let context;
|
|
48
|
+
if (options?.onMutate) {
|
|
49
|
+
try {
|
|
50
|
+
context = await options.onMutate(variables);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
console.error("onMutate failed", err);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const url = typeof endpoint === "function" ? endpoint(variables) : endpoint;
|
|
57
|
+
const res = await fetch(url, {
|
|
58
|
+
method,
|
|
59
|
+
headers: { "Content-Type": "application/json" },
|
|
60
|
+
...method !== "DELETE" ? { body: JSON.stringify(variables) } : {}
|
|
61
|
+
});
|
|
62
|
+
let data = null;
|
|
63
|
+
if (res.status !== 204) {
|
|
64
|
+
const text = await res.text();
|
|
65
|
+
if (text) {
|
|
66
|
+
try {
|
|
67
|
+
data = JSON.parse(text);
|
|
68
|
+
} catch (e) {
|
|
69
|
+
data = text;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (!res.ok) {
|
|
74
|
+
throw new Error(data?.error || `Request failed with status ${res.status}`);
|
|
75
|
+
}
|
|
76
|
+
if (options?.onSuccess) {
|
|
77
|
+
options.onSuccess(data, variables, context);
|
|
78
|
+
}
|
|
79
|
+
if (options?.onSettled) {
|
|
80
|
+
options.onSettled(data, void 0, variables, context);
|
|
81
|
+
}
|
|
82
|
+
return data;
|
|
83
|
+
} catch (err) {
|
|
84
|
+
setError(err);
|
|
85
|
+
if (options?.onError) {
|
|
86
|
+
options.onError(err, variables, context);
|
|
87
|
+
}
|
|
88
|
+
if (options?.onSettled) {
|
|
89
|
+
options.onSettled(void 0, err, variables, context);
|
|
90
|
+
}
|
|
91
|
+
throw err;
|
|
92
|
+
} finally {
|
|
93
|
+
setIsLoading(false);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
return { mutate, isLoading, error };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
exports.useGramoMutation = useGramoMutation;
|
|
100
|
+
exports.useGramoQuery = useGramoQuery;
|
|
101
|
+
//# sourceMappingURL=index.cjs.map
|
|
102
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/react/useGramoQuery.ts","../../src/react/useGramoMutation.ts"],"names":["useState","useCallback","useEffect"],"mappings":"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","file":"index.cjs","sourcesContent":["import { useState, useEffect, useCallback } from 'react';\n\nexport type QueryOptions<T> = {\n initialData?: T;\n};\n\nexport function useGramoQuery<T = any>(endpoint: string, options?: QueryOptions<T>) {\n const [data, setData] = useState<T | null>(options?.initialData ?? null);\n const [isLoading, setIsLoading] = useState<boolean>(!options?.initialData);\n const [error, setError] = useState<Error | null>(null);\n\n const fetcher = useCallback(async () => {\n try {\n setIsLoading(true);\n const res = await fetch(endpoint);\n const json = await res.json() as any;\n \n if (!res.ok) {\n throw new Error(json.error || 'Failed to fetch data');\n }\n \n setData(json);\n setError(null);\n } catch (err: any) {\n setError(err);\n setData(null);\n } finally {\n setIsLoading(false);\n }\n }, [endpoint]);\n\n useEffect(() => {\n fetcher();\n }, [fetcher]);\n\n const mutate = useCallback((newData: T | ((prev: T | null) => T)) => {\n setData((prev: T | null) => {\n if (typeof newData === 'function') {\n return (newData as Function)(prev);\n }\n return newData;\n });\n }, []);\n\n return { data, isLoading, error, mutate, refetch: fetcher };\n}\n","import { useState } from 'react';\n\nexport type MutationOptions<TData, TVariables, TContext> = {\n onMutate?: (variables: TVariables) => Promise<TContext | void> | TContext | void;\n onSuccess?: (data: TData, variables: TVariables, context?: TContext) => void;\n onError?: (error: Error, variables: TVariables, context?: TContext) => void;\n onSettled?: (data?: TData, error?: Error, variables?: TVariables, context?: TContext) => void;\n};\n\nexport function useGramoMutation<TData = any, TVariables = any, TContext = any>(\n endpoint: string | ((vars: TVariables) => string),\n method: 'POST' | 'PATCH' | 'PUT' | 'DELETE' = 'POST',\n options?: MutationOptions<TData, TVariables, TContext>\n) {\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const mutate = async (variables: TVariables) => {\n setIsLoading(true);\n setError(null);\n\n let context: TContext | undefined;\n \n if (options?.onMutate) {\n try {\n context = (await options.onMutate(variables)) as TContext;\n } catch (err) {\n console.error('onMutate failed', err);\n }\n }\n\n try {\n const url = typeof endpoint === 'function' ? endpoint(variables) : endpoint;\n \n const res = await fetch(url, {\n method,\n headers: { 'Content-Type': 'application/json' },\n ...(method !== 'DELETE' ? { body: JSON.stringify(variables) } : {}),\n });\n\n let data: any = null;\n if (res.status !== 204) {\n const text = await res.text();\n if (text) {\n try {\n data = JSON.parse(text);\n } catch(e) {\n data = text;\n }\n }\n }\n\n if (!res.ok) {\n throw new Error(data?.error || `Request failed with status ${res.status}`);\n }\n\n if (options?.onSuccess) {\n options.onSuccess(data, variables, context);\n }\n \n if (options?.onSettled) {\n options.onSettled(data, undefined, variables, context);\n }\n \n return data;\n } catch (err: any) {\n setError(err);\n if (options?.onError) {\n options.onError(err, variables, context);\n }\n if (options?.onSettled) {\n options.onSettled(undefined, err, variables, context);\n }\n throw err;\n } finally {\n setIsLoading(false);\n }\n };\n\n return { mutate, isLoading, error };\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type QueryOptions<T> = {
|
|
2
|
+
initialData?: T;
|
|
3
|
+
};
|
|
4
|
+
declare function useGramoQuery<T = any>(endpoint: string, options?: QueryOptions<T>): {
|
|
5
|
+
data: T | null;
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
error: Error | null;
|
|
8
|
+
mutate: (newData: T | ((prev: T | null) => T)) => void;
|
|
9
|
+
refetch: () => Promise<void>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type MutationOptions<TData, TVariables, TContext> = {
|
|
13
|
+
onMutate?: (variables: TVariables) => Promise<TContext | void> | TContext | void;
|
|
14
|
+
onSuccess?: (data: TData, variables: TVariables, context?: TContext) => void;
|
|
15
|
+
onError?: (error: Error, variables: TVariables, context?: TContext) => void;
|
|
16
|
+
onSettled?: (data?: TData, error?: Error, variables?: TVariables, context?: TContext) => void;
|
|
17
|
+
};
|
|
18
|
+
declare function useGramoMutation<TData = any, TVariables = any, TContext = any>(endpoint: string | ((vars: TVariables) => string), method?: 'POST' | 'PATCH' | 'PUT' | 'DELETE', options?: MutationOptions<TData, TVariables, TContext>): {
|
|
19
|
+
mutate: (variables: TVariables) => Promise<any>;
|
|
20
|
+
isLoading: boolean;
|
|
21
|
+
error: Error | null;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export { type MutationOptions, type QueryOptions, useGramoMutation, useGramoQuery };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type QueryOptions<T> = {
|
|
2
|
+
initialData?: T;
|
|
3
|
+
};
|
|
4
|
+
declare function useGramoQuery<T = any>(endpoint: string, options?: QueryOptions<T>): {
|
|
5
|
+
data: T | null;
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
error: Error | null;
|
|
8
|
+
mutate: (newData: T | ((prev: T | null) => T)) => void;
|
|
9
|
+
refetch: () => Promise<void>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type MutationOptions<TData, TVariables, TContext> = {
|
|
13
|
+
onMutate?: (variables: TVariables) => Promise<TContext | void> | TContext | void;
|
|
14
|
+
onSuccess?: (data: TData, variables: TVariables, context?: TContext) => void;
|
|
15
|
+
onError?: (error: Error, variables: TVariables, context?: TContext) => void;
|
|
16
|
+
onSettled?: (data?: TData, error?: Error, variables?: TVariables, context?: TContext) => void;
|
|
17
|
+
};
|
|
18
|
+
declare function useGramoMutation<TData = any, TVariables = any, TContext = any>(endpoint: string | ((vars: TVariables) => string), method?: 'POST' | 'PATCH' | 'PUT' | 'DELETE', options?: MutationOptions<TData, TVariables, TContext>): {
|
|
19
|
+
mutate: (variables: TVariables) => Promise<any>;
|
|
20
|
+
isLoading: boolean;
|
|
21
|
+
error: Error | null;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export { type MutationOptions, type QueryOptions, useGramoMutation, useGramoQuery };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
// gramobase — Telegram as your free, infinite backend
|
|
4
|
+
|
|
5
|
+
function useGramoQuery(endpoint, options) {
|
|
6
|
+
const [data, setData] = useState(options?.initialData ?? null);
|
|
7
|
+
const [isLoading, setIsLoading] = useState(!options?.initialData);
|
|
8
|
+
const [error, setError] = useState(null);
|
|
9
|
+
const fetcher = useCallback(async () => {
|
|
10
|
+
try {
|
|
11
|
+
setIsLoading(true);
|
|
12
|
+
const res = await fetch(endpoint);
|
|
13
|
+
const json = await res.json();
|
|
14
|
+
if (!res.ok) {
|
|
15
|
+
throw new Error(json.error || "Failed to fetch data");
|
|
16
|
+
}
|
|
17
|
+
setData(json);
|
|
18
|
+
setError(null);
|
|
19
|
+
} catch (err) {
|
|
20
|
+
setError(err);
|
|
21
|
+
setData(null);
|
|
22
|
+
} finally {
|
|
23
|
+
setIsLoading(false);
|
|
24
|
+
}
|
|
25
|
+
}, [endpoint]);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
fetcher();
|
|
28
|
+
}, [fetcher]);
|
|
29
|
+
const mutate = useCallback((newData) => {
|
|
30
|
+
setData((prev) => {
|
|
31
|
+
if (typeof newData === "function") {
|
|
32
|
+
return newData(prev);
|
|
33
|
+
}
|
|
34
|
+
return newData;
|
|
35
|
+
});
|
|
36
|
+
}, []);
|
|
37
|
+
return { data, isLoading, error, mutate, refetch: fetcher };
|
|
38
|
+
}
|
|
39
|
+
function useGramoMutation(endpoint, method = "POST", options) {
|
|
40
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
41
|
+
const [error, setError] = useState(null);
|
|
42
|
+
const mutate = async (variables) => {
|
|
43
|
+
setIsLoading(true);
|
|
44
|
+
setError(null);
|
|
45
|
+
let context;
|
|
46
|
+
if (options?.onMutate) {
|
|
47
|
+
try {
|
|
48
|
+
context = await options.onMutate(variables);
|
|
49
|
+
} catch (err) {
|
|
50
|
+
console.error("onMutate failed", err);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const url = typeof endpoint === "function" ? endpoint(variables) : endpoint;
|
|
55
|
+
const res = await fetch(url, {
|
|
56
|
+
method,
|
|
57
|
+
headers: { "Content-Type": "application/json" },
|
|
58
|
+
...method !== "DELETE" ? { body: JSON.stringify(variables) } : {}
|
|
59
|
+
});
|
|
60
|
+
let data = null;
|
|
61
|
+
if (res.status !== 204) {
|
|
62
|
+
const text = await res.text();
|
|
63
|
+
if (text) {
|
|
64
|
+
try {
|
|
65
|
+
data = JSON.parse(text);
|
|
66
|
+
} catch (e) {
|
|
67
|
+
data = text;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (!res.ok) {
|
|
72
|
+
throw new Error(data?.error || `Request failed with status ${res.status}`);
|
|
73
|
+
}
|
|
74
|
+
if (options?.onSuccess) {
|
|
75
|
+
options.onSuccess(data, variables, context);
|
|
76
|
+
}
|
|
77
|
+
if (options?.onSettled) {
|
|
78
|
+
options.onSettled(data, void 0, variables, context);
|
|
79
|
+
}
|
|
80
|
+
return data;
|
|
81
|
+
} catch (err) {
|
|
82
|
+
setError(err);
|
|
83
|
+
if (options?.onError) {
|
|
84
|
+
options.onError(err, variables, context);
|
|
85
|
+
}
|
|
86
|
+
if (options?.onSettled) {
|
|
87
|
+
options.onSettled(void 0, err, variables, context);
|
|
88
|
+
}
|
|
89
|
+
throw err;
|
|
90
|
+
} finally {
|
|
91
|
+
setIsLoading(false);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
return { mutate, isLoading, error };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export { useGramoMutation, useGramoQuery };
|
|
98
|
+
//# sourceMappingURL=index.js.map
|
|
99
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/react/useGramoQuery.ts","../../src/react/useGramoMutation.ts"],"names":["useState"],"mappings":"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","file":"index.js","sourcesContent":["import { useState, useEffect, useCallback } from 'react';\n\nexport type QueryOptions<T> = {\n initialData?: T;\n};\n\nexport function useGramoQuery<T = any>(endpoint: string, options?: QueryOptions<T>) {\n const [data, setData] = useState<T | null>(options?.initialData ?? null);\n const [isLoading, setIsLoading] = useState<boolean>(!options?.initialData);\n const [error, setError] = useState<Error | null>(null);\n\n const fetcher = useCallback(async () => {\n try {\n setIsLoading(true);\n const res = await fetch(endpoint);\n const json = await res.json() as any;\n \n if (!res.ok) {\n throw new Error(json.error || 'Failed to fetch data');\n }\n \n setData(json);\n setError(null);\n } catch (err: any) {\n setError(err);\n setData(null);\n } finally {\n setIsLoading(false);\n }\n }, [endpoint]);\n\n useEffect(() => {\n fetcher();\n }, [fetcher]);\n\n const mutate = useCallback((newData: T | ((prev: T | null) => T)) => {\n setData((prev: T | null) => {\n if (typeof newData === 'function') {\n return (newData as Function)(prev);\n }\n return newData;\n });\n }, []);\n\n return { data, isLoading, error, mutate, refetch: fetcher };\n}\n","import { useState } from 'react';\n\nexport type MutationOptions<TData, TVariables, TContext> = {\n onMutate?: (variables: TVariables) => Promise<TContext | void> | TContext | void;\n onSuccess?: (data: TData, variables: TVariables, context?: TContext) => void;\n onError?: (error: Error, variables: TVariables, context?: TContext) => void;\n onSettled?: (data?: TData, error?: Error, variables?: TVariables, context?: TContext) => void;\n};\n\nexport function useGramoMutation<TData = any, TVariables = any, TContext = any>(\n endpoint: string | ((vars: TVariables) => string),\n method: 'POST' | 'PATCH' | 'PUT' | 'DELETE' = 'POST',\n options?: MutationOptions<TData, TVariables, TContext>\n) {\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const mutate = async (variables: TVariables) => {\n setIsLoading(true);\n setError(null);\n\n let context: TContext | undefined;\n \n if (options?.onMutate) {\n try {\n context = (await options.onMutate(variables)) as TContext;\n } catch (err) {\n console.error('onMutate failed', err);\n }\n }\n\n try {\n const url = typeof endpoint === 'function' ? endpoint(variables) : endpoint;\n \n const res = await fetch(url, {\n method,\n headers: { 'Content-Type': 'application/json' },\n ...(method !== 'DELETE' ? { body: JSON.stringify(variables) } : {}),\n });\n\n let data: any = null;\n if (res.status !== 204) {\n const text = await res.text();\n if (text) {\n try {\n data = JSON.parse(text);\n } catch(e) {\n data = text;\n }\n }\n }\n\n if (!res.ok) {\n throw new Error(data?.error || `Request failed with status ${res.status}`);\n }\n\n if (options?.onSuccess) {\n options.onSuccess(data, variables, context);\n }\n \n if (options?.onSettled) {\n options.onSettled(data, undefined, variables, context);\n }\n \n return data;\n } catch (err: any) {\n setError(err);\n if (options?.onError) {\n options.onError(err, variables, context);\n }\n if (options?.onSettled) {\n options.onSettled(undefined, err, variables, context);\n }\n throw err;\n } finally {\n setIsLoading(false);\n }\n };\n\n return { mutate, isLoading, error };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,26 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gramobase",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "Telegram as a free, infinite, production-grade backend database with a MongoDB-like ORM, hot-caching, JWT auth, WAL crash-recovery, and SSE realtime streams.",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "dist/index.
|
|
7
|
-
"module": "dist/index.
|
|
6
|
+
"main": "dist/index.cjs",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
|
-
"import": "./dist/index.
|
|
13
|
-
"require": "./dist/index.
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
14
|
},
|
|
15
15
|
"./auth": {
|
|
16
16
|
"types": "./dist/auth/index.d.ts",
|
|
17
|
-
"import": "./dist/auth/index.
|
|
18
|
-
"require": "./dist/auth/index.
|
|
17
|
+
"import": "./dist/auth/index.js",
|
|
18
|
+
"require": "./dist/auth/index.cjs"
|
|
19
19
|
},
|
|
20
20
|
"./migrations": {
|
|
21
21
|
"types": "./dist/migrations/index.d.ts",
|
|
22
|
-
"import": "./dist/migrations/index.
|
|
23
|
-
"require": "./dist/migrations/index.
|
|
22
|
+
"import": "./dist/migrations/index.js",
|
|
23
|
+
"require": "./dist/migrations/index.cjs"
|
|
24
|
+
},
|
|
25
|
+
"./react": {
|
|
26
|
+
"types": "./dist/react/index.d.ts",
|
|
27
|
+
"import": "./dist/react/index.js",
|
|
28
|
+
"require": "./dist/react/index.cjs"
|
|
24
29
|
}
|
|
25
30
|
},
|
|
26
31
|
"bin": {
|
|
@@ -44,32 +49,49 @@
|
|
|
44
49
|
],
|
|
45
50
|
"license": "MIT",
|
|
46
51
|
"dependencies": {
|
|
47
|
-
"zod": "^3.22.0",
|
|
48
|
-
"node-telegram-bot-api": "^0.66.0",
|
|
49
|
-
"jsonwebtoken": "^9.0.0",
|
|
50
52
|
"bcryptjs": "^2.4.3",
|
|
51
|
-
"
|
|
52
|
-
"commander": "^12.0.0",
|
|
53
|
+
"bloom-filters": "^3.0.1",
|
|
53
54
|
"chalk": "^5.3.0",
|
|
54
|
-
"
|
|
55
|
-
"
|
|
55
|
+
"commander": "^12.0.0",
|
|
56
|
+
"crypto-js": "^4.2.0",
|
|
56
57
|
"dotenv": "^16.4.0",
|
|
57
58
|
"eventemitter3": "^5.0.1",
|
|
59
|
+
"inquirer": "^9.2.0",
|
|
60
|
+
"jsonwebtoken": "^9.0.0",
|
|
61
|
+
"kleur": "^4.1.5",
|
|
62
|
+
"lru-cache": "^10.2.0",
|
|
63
|
+
"node-telegram-bot-api": "^0.66.0",
|
|
64
|
+
"ora": "^8.0.1",
|
|
58
65
|
"p-queue": "^8.0.1",
|
|
59
66
|
"p-retry": "^6.2.0",
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
-
|
|
67
|
+
"zod": "^3.22.0"
|
|
68
|
+
},
|
|
69
|
+
"peerDependencies": {
|
|
70
|
+
"react": ">=18.0.0",
|
|
71
|
+
"react-dom": ">=18.0.0"
|
|
72
|
+
},
|
|
73
|
+
"peerDependenciesMeta": {
|
|
74
|
+
"react": {
|
|
75
|
+
"optional": true
|
|
76
|
+
},
|
|
77
|
+
"react-dom": {
|
|
78
|
+
"optional": true
|
|
79
|
+
}
|
|
63
80
|
},
|
|
64
81
|
"devDependencies": {
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
82
|
+
"@types/bcryptjs": "^2.4.6",
|
|
83
|
+
"@types/crypto-js": "^4.2.2",
|
|
84
|
+
"@types/inquirer": "^9.0.9",
|
|
85
|
+
"@types/jsonwebtoken": "^9.0.0",
|
|
68
86
|
"@types/node": "^20.0.0",
|
|
69
87
|
"@types/node-telegram-bot-api": "^0.64.0",
|
|
70
|
-
"@types/
|
|
71
|
-
"@types/
|
|
72
|
-
"
|
|
88
|
+
"@types/react": "^19.2.17",
|
|
89
|
+
"@types/react-dom": "^19.2.3",
|
|
90
|
+
"react": "^19.2.7",
|
|
91
|
+
"react-dom": "^19.2.7",
|
|
92
|
+
"tsup": "^8.0.0",
|
|
93
|
+
"typescript": "^5.4.0",
|
|
94
|
+
"vitest": "^1.4.0"
|
|
73
95
|
},
|
|
74
96
|
"engines": {
|
|
75
97
|
"node": ">=24.0.0"
|