get-db9 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +244 -2
- package/dist/client-B3NUYqOc.d.cts +643 -0
- package/dist/client-B3NUYqOc.d.ts +643 -0
- package/dist/client.cjs +567 -190
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +567 -190
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +575 -191
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +573 -191
- package/dist/index.js.map +1 -1
- package/package.json +8 -5
- package/dist/client-CXv2ZlbR.d.cts +0 -429
- package/dist/client-CXv2ZlbR.d.ts +0 -429
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# get-db9
|
|
2
2
|
|
|
3
|
-
TypeScript SDK for [
|
|
3
|
+
TypeScript SDK for [db9-backend](https://github.com/c4pt0r/db9-backend) — instant PostgreSQL-compatible databases on TiKV.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -31,7 +31,7 @@ const db = await instantDatabase({
|
|
|
31
31
|
|
|
32
32
|
## Db9 Client
|
|
33
33
|
|
|
34
|
-
Full typed client for the API
|
|
34
|
+
Full typed client for the API — databases, SQL, file storage, tokens, migrations, and more.
|
|
35
35
|
|
|
36
36
|
```typescript
|
|
37
37
|
import { createDb9Client } from 'get-db9/client';
|
|
@@ -57,6 +57,167 @@ await client.databases.applyMigration(db.id, {
|
|
|
57
57
|
});
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
+
## File Storage (fs9)
|
|
61
|
+
|
|
62
|
+
Each database includes a built-in file system accessible via **WebSocket**.
|
|
63
|
+
The SDK connects to the fs9 WebSocket server, authenticates, and provides a
|
|
64
|
+
high-level API for file operations.
|
|
65
|
+
|
|
66
|
+
> **Note:** File storage requires a WebSocket implementation. Browsers, Deno,
|
|
67
|
+
> Bun, and Node 21+ have native `WebSocket`. For Node 18–20, install the `ws` package
|
|
68
|
+
> and pass it via the `WebSocket` option.
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { createDb9Client } from 'get-db9/client';
|
|
72
|
+
import WebSocket from 'ws'; // Node 18–20 only
|
|
73
|
+
|
|
74
|
+
const client = createDb9Client({ WebSocket: WebSocket as any });
|
|
75
|
+
const dbId = 'your-database-id';
|
|
76
|
+
|
|
77
|
+
// Write a file (string, ArrayBuffer, or Uint8Array)
|
|
78
|
+
await client.fs.write(dbId, '/data/hello.txt', 'Hello, world!');
|
|
79
|
+
|
|
80
|
+
// Read file as text
|
|
81
|
+
const text = await client.fs.read(dbId, '/data/hello.txt');
|
|
82
|
+
|
|
83
|
+
// Read file as raw bytes (Uint8Array)
|
|
84
|
+
const bytes = await client.fs.readBinary(dbId, '/data/image.png');
|
|
85
|
+
|
|
86
|
+
// List files in a directory
|
|
87
|
+
const files = await client.fs.list(dbId, '/data');
|
|
88
|
+
|
|
89
|
+
// Check if file exists
|
|
90
|
+
const exists = await client.fs.exists(dbId, '/data/hello.txt');
|
|
91
|
+
|
|
92
|
+
// Get file metadata (type, size, mode, mtime)
|
|
93
|
+
const stat = await client.fs.stat(dbId, '/data/hello.txt');
|
|
94
|
+
console.log(stat.type, stat.size); // 'file', 1024
|
|
95
|
+
|
|
96
|
+
// Create directory (recursive)
|
|
97
|
+
await client.fs.mkdir(dbId, '/data/nested/dir');
|
|
98
|
+
|
|
99
|
+
// Append to a file
|
|
100
|
+
await client.fs.append(dbId, '/data/log.txt', 'new line\n');
|
|
101
|
+
|
|
102
|
+
// Rename (move) a file
|
|
103
|
+
await client.fs.rename(dbId, '/data/old.txt', '/data/new.txt');
|
|
104
|
+
|
|
105
|
+
// Delete a file
|
|
106
|
+
await client.fs.remove(dbId, '/data/hello.txt');
|
|
107
|
+
|
|
108
|
+
// Delete a directory recursively
|
|
109
|
+
await client.fs.remove(dbId, '/data/old-dir', { recursive: true });
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Persistent Connection
|
|
113
|
+
|
|
114
|
+
For multiple operations on the same database, use `fs.connect()` to hold a
|
|
115
|
+
single WebSocket connection and avoid reconnecting per-call:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
const fs = await client.fs.connect(dbId);
|
|
119
|
+
try {
|
|
120
|
+
await fs.mkdir('/batch');
|
|
121
|
+
await fs.writeFile('/batch/a.txt', 'file A');
|
|
122
|
+
await fs.writeFile('/batch/b.txt', 'file B');
|
|
123
|
+
const entries = await fs.readdir('/batch');
|
|
124
|
+
console.log(entries); // [{path: '/batch/a.txt', ...}, ...]
|
|
125
|
+
} finally {
|
|
126
|
+
await fs.close();
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Token Management
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
const client = createDb9Client();
|
|
134
|
+
|
|
135
|
+
// Create a named API token
|
|
136
|
+
const token = await client.tokens.create({
|
|
137
|
+
name: 'ci-deploy',
|
|
138
|
+
expires_in_days: 90,
|
|
139
|
+
});
|
|
140
|
+
console.log(token.token); // Use this for CI/CD
|
|
141
|
+
|
|
142
|
+
// List all tokens
|
|
143
|
+
const tokens = await client.tokens.list();
|
|
144
|
+
|
|
145
|
+
// Revoke a token
|
|
146
|
+
await client.tokens.revoke(token.id);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Database Credentials
|
|
150
|
+
|
|
151
|
+
Retrieve stored admin credentials without resetting the password:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
const client = createDb9Client();
|
|
155
|
+
const dbId = 'your-database-id';
|
|
156
|
+
|
|
157
|
+
// Get stored credentials (no password reset)
|
|
158
|
+
const creds = await client.databases.credentials(dbId);
|
|
159
|
+
console.log(creds.admin_user); // admin username
|
|
160
|
+
console.log(creds.admin_password); // current password
|
|
161
|
+
console.log(creds.connection_string); // full connection string
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Device Authorization (CLI flow)
|
|
165
|
+
|
|
166
|
+
Authorize a CLI or device using the OAuth device code flow:
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
const client = createDb9Client();
|
|
170
|
+
|
|
171
|
+
// 1. Start device code flow
|
|
172
|
+
const code = await client.deviceAuth.createDeviceCode();
|
|
173
|
+
console.log(`Visit: ${code.verification_uri}`);
|
|
174
|
+
console.log(`Code: ${code.user_code}`);
|
|
175
|
+
|
|
176
|
+
// 2. Poll for authorization (user approves in browser)
|
|
177
|
+
const poll = async () => {
|
|
178
|
+
while (true) {
|
|
179
|
+
const result = await client.deviceAuth.pollDeviceToken({
|
|
180
|
+
device_code: code.device_code,
|
|
181
|
+
});
|
|
182
|
+
if ('token' in result) {
|
|
183
|
+
return result; // { token, expires_at }
|
|
184
|
+
}
|
|
185
|
+
if (result.error === 'access_denied') {
|
|
186
|
+
throw new Error('User denied authorization');
|
|
187
|
+
}
|
|
188
|
+
if (result.error === 'expired_token') {
|
|
189
|
+
throw new Error('Device code expired');
|
|
190
|
+
}
|
|
191
|
+
// authorization_pending — wait and retry
|
|
192
|
+
await new Promise((r) => setTimeout(r, code.interval * 1000));
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const auth = await poll();
|
|
197
|
+
console.log(auth.token);
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## SQL Error Handling
|
|
201
|
+
|
|
202
|
+
SQL results include structured error details when queries fail:
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
const result = await client.databases.sql(dbId, 'SELECT * FROM nonexistent');
|
|
206
|
+
|
|
207
|
+
if (result.error) {
|
|
208
|
+
// result.error is a SqlErrorDetail object:
|
|
209
|
+
// {
|
|
210
|
+
// message: "relation \"nonexistent\" does not exist",
|
|
211
|
+
// code: "42P01", // PostgreSQL error code
|
|
212
|
+
// detail: "...", // optional
|
|
213
|
+
// hint: "...", // optional
|
|
214
|
+
// position: 15 // optional cursor position
|
|
215
|
+
// }
|
|
216
|
+
console.log(result.error.message);
|
|
217
|
+
console.log(result.error.code);
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
60
221
|
## Configuration
|
|
61
222
|
|
|
62
223
|
### instantDatabase options
|
|
@@ -69,6 +230,9 @@ await client.databases.applyMigration(db.id, {
|
|
|
69
230
|
| `credentialStore` | `CredentialStore` | `FileCredentialStore` | Credential storage |
|
|
70
231
|
| `seed` | `string` | — | SQL to run after creation |
|
|
71
232
|
| `seedFile` | `string` | — | SQL file content to run |
|
|
233
|
+
| `timeout` | `number` | — | Request timeout in ms |
|
|
234
|
+
| `maxRetries` | `number` | `3` (max) | Retry count for failed requests |
|
|
235
|
+
| `retryDelay` | `number` | — | Delay between retries in ms |
|
|
72
236
|
|
|
73
237
|
### Db9 client options
|
|
74
238
|
|
|
@@ -78,6 +242,11 @@ await client.databases.applyMigration(db.id, {
|
|
|
78
242
|
| `token` | `string` | — | Bearer token (optional) |
|
|
79
243
|
| `fetch` | `FetchFn` | `globalThis.fetch` | Custom fetch |
|
|
80
244
|
| `credentialStore` | `CredentialStore` | `FileCredentialStore` | Load/save token |
|
|
245
|
+
| `timeout` | `number` | — | Request timeout in ms |
|
|
246
|
+
| `maxRetries` | `number` | `3` (max) | Retry count for failed requests |
|
|
247
|
+
| `retryDelay` | `number` | — | Delay between retries in ms |
|
|
248
|
+
| `WebSocket` | `WebSocketConstructor` | `globalThis.WebSocket` | WebSocket impl for fs operations |
|
|
249
|
+
| `wsPort` | `number` | `5480` | WebSocket port for fs9 server |
|
|
81
250
|
|
|
82
251
|
## Zero-config client
|
|
83
252
|
|
|
@@ -107,6 +276,8 @@ try {
|
|
|
107
276
|
}
|
|
108
277
|
```
|
|
109
278
|
|
|
279
|
+
> **Note:** 401 errors are automatically retried with a fresh token for anonymous sessions. You typically won't see `Db9AuthError` unless the refresh itself fails.
|
|
280
|
+
|
|
110
281
|
## Credential Storage
|
|
111
282
|
|
|
112
283
|
Credentials are stored in `~/.db9/credentials` (TOML format), shared with the db9 CLI.
|
|
@@ -124,6 +295,77 @@ const customStore = new FileCredentialStore('/path/to/credentials');
|
|
|
124
295
|
const memStore = new MemoryCredentialStore();
|
|
125
296
|
```
|
|
126
297
|
|
|
298
|
+
## API Reference
|
|
299
|
+
|
|
300
|
+
### `client.auth`
|
|
301
|
+
|
|
302
|
+
| Method | Description |
|
|
303
|
+
|--------|-------------|
|
|
304
|
+
| `register(req)` | Create account with email/password |
|
|
305
|
+
| `login(req)` | Login and get bearer token |
|
|
306
|
+
| `me()` | Get current user profile |
|
|
307
|
+
| `anonymousRegister()` | Register anonymously (auto-called) |
|
|
308
|
+
| `anonymousRefresh(req)` | Refresh anonymous token |
|
|
309
|
+
| `getAnonymousSecret()` | Retrieve anonymous secret for token refresh |
|
|
310
|
+
| `ensureAnonymousSecret()` | Ensure anonymous secret is saved to credential store |
|
|
311
|
+
| `claim(req)` | Claim anonymous account with email/password |
|
|
312
|
+
|
|
313
|
+
### `client.tokens`
|
|
314
|
+
|
|
315
|
+
| Method | Description |
|
|
316
|
+
|--------|-------------|
|
|
317
|
+
| `create(req)` | Create a named API token (`{ name?, expires_in_days? }`) |
|
|
318
|
+
| `list()` | List all tokens |
|
|
319
|
+
| `revoke(tokenId)` | Revoke a token by ID |
|
|
320
|
+
|
|
321
|
+
### `client.databases`
|
|
322
|
+
|
|
323
|
+
| Method | Description |
|
|
324
|
+
|--------|-------------|
|
|
325
|
+
| `create(req)` | Create a new database |
|
|
326
|
+
| `list()` | List all databases |
|
|
327
|
+
| `get(id)` | Get database details |
|
|
328
|
+
| `delete(id)` | Delete a database |
|
|
329
|
+
| `resetPassword(id)` | Reset admin password |
|
|
330
|
+
| `credentials(id)` | Get stored admin credentials without resetting |
|
|
331
|
+
| `observability(id)` | Get TPS, latency, connection stats |
|
|
332
|
+
| `sql(id, query)` | Execute SQL query (errors returned as `SqlErrorDetail`) |
|
|
333
|
+
| `sqlFile(id, content)` | Execute SQL from file content |
|
|
334
|
+
| `schema(id)` | Get schema metadata |
|
|
335
|
+
| `dump(id, req?)` | Export schema/data as SQL |
|
|
336
|
+
| `applyMigration(id, req)` | Apply a migration |
|
|
337
|
+
| `listMigrations(id)` | List applied migrations |
|
|
338
|
+
| `branch(id, req)` | Create a database branch |
|
|
339
|
+
| `users.list(id)` | List database users |
|
|
340
|
+
| `users.create(id, req)` | Create database user |
|
|
341
|
+
| `users.delete(id, username)` | Delete database user |
|
|
342
|
+
|
|
343
|
+
### `client.fs`
|
|
344
|
+
|
|
345
|
+
All methods auto-resolve database credentials and connect via WebSocket.
|
|
346
|
+
|
|
347
|
+
| Method | Description |
|
|
348
|
+
|--------|-------------|
|
|
349
|
+
| `connect(dbId)` | Open a persistent `FsClient` WebSocket connection |
|
|
350
|
+
| `read(dbId, path)` | Read file as text (UTF-8) |
|
|
351
|
+
| `readBinary(dbId, path)` | Read file as `Uint8Array` |
|
|
352
|
+
| `write(dbId, path, content)` | Write file (string, ArrayBuffer, or Uint8Array) |
|
|
353
|
+
| `append(dbId, path, content)` | Append to a file, returns bytes written |
|
|
354
|
+
| `list(dbId, path)` | List directory contents (returns `FileInfo[]`) |
|
|
355
|
+
| `stat(dbId, path)` | Get file metadata (`FileInfo`) |
|
|
356
|
+
| `exists(dbId, path)` | Check if file exists (returns boolean) |
|
|
357
|
+
| `mkdir(dbId, path)` | Create directory recursively |
|
|
358
|
+
| `remove(dbId, path, opts?)` | Remove file or directory (`{ recursive?: boolean }`) |
|
|
359
|
+
| `rename(dbId, old, new)` | Rename (move) a file or directory |
|
|
360
|
+
|
|
361
|
+
### `client.deviceAuth`
|
|
362
|
+
|
|
363
|
+
| Method | Description |
|
|
364
|
+
|--------|-------------|
|
|
365
|
+
| `createDeviceCode()` | Start OAuth device code flow |
|
|
366
|
+
| `pollDeviceToken(req)` | Poll for token after user authorizes |
|
|
367
|
+
| `verifyDevice(req)` | Submit device verification with email/password |
|
|
368
|
+
|
|
127
369
|
## Requirements
|
|
128
370
|
|
|
129
371
|
- Node.js >= 18 (native fetch)
|