tetherdb 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +277 -0
- package/bin/tetherdb.js +7 -0
- package/dist/cli/args.d.cts +20 -0
- package/dist/cli/args.d.ts +20 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/backend.d.cts +15 -0
- package/dist/cli/backend.d.ts +15 -0
- package/dist/cli/backend.d.ts.map +1 -0
- package/dist/cli/cli.d.cts +8 -0
- package/dist/cli/cli.d.ts +8 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/cli/commands/apps.d.cts +9 -0
- package/dist/cli/commands/apps.d.ts +9 -0
- package/dist/cli/commands/apps.d.ts.map +1 -0
- package/dist/cli/commands/help.d.cts +5 -0
- package/dist/cli/commands/help.d.ts +5 -0
- package/dist/cli/commands/help.d.ts.map +1 -0
- package/dist/cli/commands/index.d.cts +8 -0
- package/dist/cli/commands/index.d.ts +8 -0
- package/dist/cli/commands/index.d.ts.map +1 -0
- package/dist/cli/commands/maintenance.d.cts +9 -0
- package/dist/cli/commands/maintenance.d.ts +9 -0
- package/dist/cli/commands/maintenance.d.ts.map +1 -0
- package/dist/cli/commands/serve.d.cts +14 -0
- package/dist/cli/commands/serve.d.ts +14 -0
- package/dist/cli/commands/serve.d.ts.map +1 -0
- package/dist/cli/commands/status.d.cts +9 -0
- package/dist/cli/commands/status.d.ts +9 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/tables.d.cts +9 -0
- package/dist/cli/commands/tables.d.ts +9 -0
- package/dist/cli/commands/tables.d.ts.map +1 -0
- package/dist/cli/commands/users.d.cts +9 -0
- package/dist/cli/commands/users.d.ts +9 -0
- package/dist/cli/commands/users.d.ts.map +1 -0
- package/dist/cli/index.cjs +3999 -0
- package/dist/cli/index.cjs.map +1 -0
- package/dist/cli/index.d.cts +7 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +3958 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/client/auth.d.cts +155 -0
- package/dist/client/auth.d.ts +155 -0
- package/dist/client/auth.d.ts.map +1 -0
- package/dist/client/client.d.cts +122 -0
- package/dist/client/client.d.ts +122 -0
- package/dist/client/client.d.ts.map +1 -0
- package/dist/client/errors.d.cts +39 -0
- package/dist/client/errors.d.ts +39 -0
- package/dist/client/errors.d.ts.map +1 -0
- package/dist/client/index.cjs +1742 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.cts +12 -0
- package/dist/client/index.d.ts +12 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +1707 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/shared/event.d.cts +29 -0
- package/dist/client/shared/event.d.ts +29 -0
- package/dist/client/shared/event.d.ts.map +1 -0
- package/dist/client/shared/id.d.cts +5 -0
- package/dist/client/shared/id.d.ts +5 -0
- package/dist/client/shared/id.d.ts.map +1 -0
- package/dist/client/storage.d.cts +183 -0
- package/dist/client/storage.d.ts +183 -0
- package/dist/client/storage.d.ts.map +1 -0
- package/dist/client/sync.d.cts +126 -0
- package/dist/client/sync.d.ts +126 -0
- package/dist/client/sync.d.ts.map +1 -0
- package/dist/client/table.d.cts +146 -0
- package/dist/client/table.d.ts +146 -0
- package/dist/client/table.d.ts.map +1 -0
- package/dist/index.cjs +1742 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1707 -0
- package/dist/index.js.map +1 -0
- package/dist/server/crypto.d.cts +55 -0
- package/dist/server/crypto.d.ts +55 -0
- package/dist/server/crypto.d.ts.map +1 -0
- package/dist/server/errors.d.cts +39 -0
- package/dist/server/errors.d.ts +39 -0
- package/dist/server/errors.d.ts.map +1 -0
- package/dist/server/index.cjs +3621 -0
- package/dist/server/index.cjs.map +1 -0
- package/dist/server/index.d.cts +10 -0
- package/dist/server/index.d.ts +10 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +3569 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/lock.d.cts +56 -0
- package/dist/server/lock.d.ts +56 -0
- package/dist/server/lock.d.ts.map +1 -0
- package/dist/server/rate-limiter.d.cts +81 -0
- package/dist/server/rate-limiter.d.ts +81 -0
- package/dist/server/rate-limiter.d.ts.map +1 -0
- package/dist/server/server.d.cts +206 -0
- package/dist/server/server.d.ts +206 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/storage/app.d.cts +70 -0
- package/dist/server/storage/app.d.ts +70 -0
- package/dist/server/storage/app.d.ts.map +1 -0
- package/dist/server/storage/base/app.d.cts +42 -0
- package/dist/server/storage/base/app.d.ts +42 -0
- package/dist/server/storage/base/app.d.ts.map +1 -0
- package/dist/server/storage/base/index.d.cts +5 -0
- package/dist/server/storage/base/index.d.ts +5 -0
- package/dist/server/storage/base/index.d.ts.map +1 -0
- package/dist/server/storage/base/storage.d.cts +40 -0
- package/dist/server/storage/base/storage.d.ts +40 -0
- package/dist/server/storage/base/storage.d.ts.map +1 -0
- package/dist/server/storage/base/table.d.cts +32 -0
- package/dist/server/storage/base/table.d.ts +32 -0
- package/dist/server/storage/base/table.d.ts.map +1 -0
- package/dist/server/storage/base/user.d.cts +31 -0
- package/dist/server/storage/base/user.d.ts +31 -0
- package/dist/server/storage/base/user.d.ts.map +1 -0
- package/dist/server/storage/file/app.d.cts +50 -0
- package/dist/server/storage/file/app.d.ts +50 -0
- package/dist/server/storage/file/app.d.ts.map +1 -0
- package/dist/server/storage/file/index.d.cts +5 -0
- package/dist/server/storage/file/index.d.ts +5 -0
- package/dist/server/storage/file/index.d.ts.map +1 -0
- package/dist/server/storage/file/storage.d.cts +67 -0
- package/dist/server/storage/file/storage.d.ts +67 -0
- package/dist/server/storage/file/storage.d.ts.map +1 -0
- package/dist/server/storage/file/table.d.cts +14 -0
- package/dist/server/storage/file/table.d.ts +14 -0
- package/dist/server/storage/file/table.d.ts.map +1 -0
- package/dist/server/storage/file/user.d.cts +14 -0
- package/dist/server/storage/file/user.d.ts +14 -0
- package/dist/server/storage/file/user.d.ts.map +1 -0
- package/dist/server/storage/index.d.cts +14 -0
- package/dist/server/storage/index.d.ts +14 -0
- package/dist/server/storage/index.d.ts.map +1 -0
- package/dist/server/storage/memory/app.d.cts +29 -0
- package/dist/server/storage/memory/app.d.ts +29 -0
- package/dist/server/storage/memory/app.d.ts.map +1 -0
- package/dist/server/storage/memory/index.d.cts +5 -0
- package/dist/server/storage/memory/index.d.ts +5 -0
- package/dist/server/storage/memory/index.d.ts.map +1 -0
- package/dist/server/storage/memory/storage.d.cts +47 -0
- package/dist/server/storage/memory/storage.d.ts +47 -0
- package/dist/server/storage/memory/storage.d.ts.map +1 -0
- package/dist/server/storage/memory/table.d.cts +17 -0
- package/dist/server/storage/memory/table.d.ts +17 -0
- package/dist/server/storage/memory/table.d.ts.map +1 -0
- package/dist/server/storage/memory/user.d.cts +21 -0
- package/dist/server/storage/memory/user.d.ts +21 -0
- package/dist/server/storage/memory/user.d.ts.map +1 -0
- package/dist/server/storage/sqlite/app.d.cts +31 -0
- package/dist/server/storage/sqlite/app.d.ts +31 -0
- package/dist/server/storage/sqlite/app.d.ts.map +1 -0
- package/dist/server/storage/sqlite/index.d.cts +5 -0
- package/dist/server/storage/sqlite/index.d.ts +5 -0
- package/dist/server/storage/sqlite/index.d.ts.map +1 -0
- package/dist/server/storage/sqlite/storage.d.cts +92 -0
- package/dist/server/storage/sqlite/storage.d.ts +92 -0
- package/dist/server/storage/sqlite/storage.d.ts.map +1 -0
- package/dist/server/storage/sqlite/table.d.cts +15 -0
- package/dist/server/storage/sqlite/table.d.ts +15 -0
- package/dist/server/storage/sqlite/table.d.ts.map +1 -0
- package/dist/server/storage/sqlite/user.d.cts +14 -0
- package/dist/server/storage/sqlite/user.d.ts +14 -0
- package/dist/server/storage/sqlite/user.d.ts.map +1 -0
- package/dist/server/storage/storage.d.cts +152 -0
- package/dist/server/storage/storage.d.ts +152 -0
- package/dist/server/storage/storage.d.ts.map +1 -0
- package/dist/server/storage/table.d.cts +45 -0
- package/dist/server/storage/table.d.ts +45 -0
- package/dist/server/storage/table.d.ts.map +1 -0
- package/dist/server/storage/user.d.cts +45 -0
- package/dist/server/storage/user.d.ts +45 -0
- package/dist/server/storage/user.d.ts.map +1 -0
- package/dist/server/sync.d.cts +67 -0
- package/dist/server/sync.d.ts +67 -0
- package/dist/server/sync.d.ts.map +1 -0
- package/dist/server/validate.d.cts +113 -0
- package/dist/server/validate.d.ts +113 -0
- package/dist/server/validate.d.ts.map +1 -0
- package/dist/shared/clock.d.cts +22 -0
- package/dist/shared/clock.d.ts +22 -0
- package/dist/shared/clock.d.ts.map +1 -0
- package/dist/shared/path.d.cts +12 -0
- package/dist/shared/path.d.ts +12 -0
- package/dist/shared/path.d.ts.map +1 -0
- package/dist/shared/types.d.cts +234 -0
- package/dist/shared/types.d.ts +234 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/package.json +133 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lukas Renggli
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# ⚡ TetherDB
|
|
2
|
+
|
|
3
|
+
**Lightweight, local-first TypeScript database with reactive IndexedDB storage and real-time two-way WebSocket sync.**
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/tetherdb)
|
|
6
|
+
[](https://github.com/renggli/typescript-tetherdb/actions)
|
|
7
|
+
[](https://codecov.io/gh/renggli/typescript-tetherdb)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
|
|
11
|
+
*Open-source under the [MIT License](LICENSE).*
|
|
12
|
+
|
|
13
|
+
[Features](#features) •
|
|
14
|
+
[Installation](#installation) •
|
|
15
|
+
[Quick Start](#quick-start) •
|
|
16
|
+
[Framework Integration](#framework-integration) •
|
|
17
|
+
[CLI & Server](#cli--server-administration) •
|
|
18
|
+
[Production Deployment](#production-deployment) •
|
|
19
|
+
[Example App](#example-application)
|
|
20
|
+
|
|
21
|
+
## Features
|
|
22
|
+
|
|
23
|
+
- **📱 Offline-First & Local-First**: Mutations apply immediately to browser IndexedDB with zero latency and queue in an atomic outbox for background synchronization.
|
|
24
|
+
- **🔄 Real-Time Two-Way Sync**: Fast, bi-directional WebSocket sync with delta diff catch-up on reconnect, adaptive full snapshots, and automatic live broadcast to active sessions.
|
|
25
|
+
- **⚖️ Deterministic Conflict Resolution**: Last-Write-Wins (LWW) conflict handling powered by monotonically increasing logical clocks and deterministic tie-breaking.
|
|
26
|
+
- **🏢 Multi-Tenant & Multi-App**: Host multiple independent web applications on a single domain and server instance. Data and WebSocket streams are strictly isolated by `appId` and user account.
|
|
27
|
+
- **🚀 Seamless Offline-to-Cloud Onboarding**: Start using the local database immediately without an account; attach live cloud sync on demand with a single `client.login()` or `client.register()` call.
|
|
28
|
+
- **🗄️ Pluggable Server Storage**: Built-in persistence engines for SQLite (`SqliteStorage`), sharded filesystem directories (`FileStorage`), and ephemeral testing (`MemoryStorage`).
|
|
29
|
+
- **🔐 Built-in Authentication**: Password hashing using scrypt with salt and HMAC-signed session tokens with automatic session persistence and recovery.
|
|
30
|
+
- **⚡ Batch-by-Default Performance**: High-throughput atomic mutations (`putAll`, `deleteAll`, `getAll`) and coalesced WebSocket frame transmission.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
Install from npm:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install tetherdb
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Or install directly from GitHub:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install github:renggli/typescript-tetherdb
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
### 1. Launch a Server (Zero-Config)
|
|
49
|
+
|
|
50
|
+
You can launch a standalone TetherDB server in seconds using the CLI:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Start server with persistent SQLite storage on port 8080
|
|
54
|
+
npx tetherdb --sqlite=./data --port=8080
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or embed it programmatically in your Node.js backend:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { SqliteStorage, startServer } from 'tetherdb/server';
|
|
61
|
+
|
|
62
|
+
const running = await startServer({
|
|
63
|
+
port: 8080,
|
|
64
|
+
storage: new SqliteStorage({ baseDir: './data' }),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
console.log(`TetherDB server listening on http://${running.host}:${running.port}`);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 2. Client Setup: Instant Local-First Storage
|
|
71
|
+
|
|
72
|
+
TetherDB works out of the box in the browser. Writes are instant and persist offline:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { TetherClient } from 'tetherdb/client';
|
|
76
|
+
|
|
77
|
+
interface Todo {
|
|
78
|
+
title: string;
|
|
79
|
+
completed: boolean;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 1. Initialize client with server endpoint
|
|
83
|
+
const client = new TetherClient('my-todo-app', {
|
|
84
|
+
url: 'http://localhost:8080',
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// 2. Obtain a typed table
|
|
88
|
+
const todos = client.table<Todo>('todos');
|
|
89
|
+
|
|
90
|
+
// 3. React to local and remote data changes
|
|
91
|
+
const unsubscribe = todos.onChange.register((events) => {
|
|
92
|
+
for (const { op, id, data, isRemote } of events) {
|
|
93
|
+
console.log(`Table event [${op}] on ${id} (remote: ${isRemote}):`, data);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// 4. Instant local writes (offline-ready)
|
|
98
|
+
await todos.put('task-1', {
|
|
99
|
+
title: 'Try TetherDB',
|
|
100
|
+
completed: false,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Batch operations execute in a single atomic transaction
|
|
104
|
+
await todos.putAll([
|
|
105
|
+
{ id: 'task-2', data: { title: 'Add offline support', completed: true } },
|
|
106
|
+
{ id: 'task-3', data: { title: 'Sync with cloud', completed: false } },
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
// Read data
|
|
110
|
+
const item = await todos.get('task-1');
|
|
111
|
+
const allItems = await todos.getAll();
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 3. Attach Live Cloud Sync & Authentication
|
|
115
|
+
|
|
116
|
+
Connect your local data to the cloud whenever the user registers or signs in:
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
// Register or login to initiate real-time synchronization
|
|
120
|
+
await client.register({
|
|
121
|
+
username: 'alice',
|
|
122
|
+
password: 'secure-password',
|
|
123
|
+
remember: true, // Persists session token across browser reloads
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Monitor live connection and sync status
|
|
127
|
+
client.onSyncStatusChange.register((status) => {
|
|
128
|
+
// 'disconnected' | 'connecting' | 'connected' | 'syncing' | 'synced'
|
|
129
|
+
console.log('Sync status:', status);
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Framework Integration
|
|
134
|
+
|
|
135
|
+
### React Hook Example
|
|
136
|
+
|
|
137
|
+
Bind any TetherDB table to component state with automatic real-time updates:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { useEffect, useState } from 'react';
|
|
141
|
+
import type { Table } from 'tetherdb/client';
|
|
142
|
+
|
|
143
|
+
export function useTable<T>(table: Table<T>): T[] {
|
|
144
|
+
const [items, setItems] = useState<T[]>([]);
|
|
145
|
+
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
// 1. Initial local load
|
|
148
|
+
table.getAll().then(setItems);
|
|
149
|
+
|
|
150
|
+
// 2. Subscribe to live changes (local writes & remote sync broadcasts)
|
|
151
|
+
const unsubscribe = table.onChange.register(() => {
|
|
152
|
+
table.getAll().then(setItems);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
return unsubscribe;
|
|
156
|
+
}, [table]);
|
|
157
|
+
|
|
158
|
+
return items;
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## CLI & Server Administration
|
|
163
|
+
|
|
164
|
+
TetherDB includes a full CLI suite for running servers and managing databases:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Start standalone server with SQLite persistence
|
|
168
|
+
npx tetherdb --sqlite=./data --port=8080
|
|
169
|
+
|
|
170
|
+
# Start with filesystem storage
|
|
171
|
+
npx tetherdb --file=./data --port=8080
|
|
172
|
+
|
|
173
|
+
# Manage apps, tables, and users
|
|
174
|
+
npx tetherdb apps list --sqlite=./data
|
|
175
|
+
npx tetherdb apps add my-todo-app --sqlite=./data
|
|
176
|
+
npx tetherdb tables add my-todo-app todos --sqlite=./data
|
|
177
|
+
npx tetherdb users add alice secret --sqlite=./data
|
|
178
|
+
|
|
179
|
+
# Run database maintenance & compaction
|
|
180
|
+
npx tetherdb maintenance checkpoint --sqlite=./data
|
|
181
|
+
npx tetherdb maintenance vacuum --sqlite=./data
|
|
182
|
+
npx tetherdb maintenance prune my-todo-app 1000 --sqlite=./data
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## HTTP & WebSocket Endpoints
|
|
186
|
+
|
|
187
|
+
| Method | Endpoint | Description | Authentication |
|
|
188
|
+
| :--- | :--- | :--- | :--- |
|
|
189
|
+
| `GET` | `/health` | Server uptime and health probe | Public |
|
|
190
|
+
| `GET` | `/ready` | Storage backend readiness check | Public |
|
|
191
|
+
| `GET` | `/metrics` | Connected clients and application metrics | Public |
|
|
192
|
+
| `POST` | `/auth/register` | Create a new user account | Public |
|
|
193
|
+
| `POST` | `/auth/login` | Log in and receive a signed session token | Public |
|
|
194
|
+
| `WS` | `/sync` | Bi-directional WebSocket synchronization stream | Token handshake |
|
|
195
|
+
|
|
196
|
+
## Production Deployment
|
|
197
|
+
|
|
198
|
+
### Storage Engines
|
|
199
|
+
|
|
200
|
+
TetherDB supports pluggable server storage backends:
|
|
201
|
+
|
|
202
|
+
| Engine | CLI Flag | Pros | Cons |
|
|
203
|
+
| :--- | :--- | :--- | :--- |
|
|
204
|
+
| **SQLite** (`SqliteStorage`) | `--sqlite=<dir>` | • High throughput & ACID safety<br>• WAL mode concurrency<br>• Built-in compaction (`vacuum`, `prune`) | • Single-node filesystem binding |
|
|
205
|
+
| **Filesystem** (`FileStorage`) | `--file=<dir>` | • Human-readable JSON structure<br>• Zero binary dependencies<br>• Direct inspection & simple backup | • I/O overhead on large tables<br>• Lower concurrent write throughput |
|
|
206
|
+
| **In-Memory** (`MemoryStorage`) | `--memory` | • Zero disk I/O, ultra-fast<br>• Zero configuration | • Ephemeral (data lost on restart) |
|
|
207
|
+
|
|
208
|
+
#### When to Use Which
|
|
209
|
+
|
|
210
|
+
- **SQLite (`--sqlite`) — Best for Production (Recommended)**: Ideal for multi-user and high-concurrency apps requiring fast transactional persistence and operational maintenance tools (`vacuum`, `checkpoint`, `prune`).
|
|
211
|
+
- **Filesystem (`--file`) — Best for Lightweight / Embedded**: Ideal for low-traffic apps, resource-constrained environments, or setups where direct inspection and editing of JSON files is desired.
|
|
212
|
+
- **In-Memory (`--memory`) — Best for Testing & CI/CD**: Ideal for automated unit/integration test suites, ephemeral pipelines, and rapid local prototyping.
|
|
213
|
+
|
|
214
|
+
### Reverse Proxy & SSL Termination
|
|
215
|
+
|
|
216
|
+
For production, run TetherDB behind a reverse proxy (such as Caddy or Nginx) to handle SSL/TLS and proxy WebSocket connections:
|
|
217
|
+
|
|
218
|
+
#### Caddy
|
|
219
|
+
|
|
220
|
+
```caddy
|
|
221
|
+
api.example.com {
|
|
222
|
+
reverse_proxy localhost:8080
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
#### Nginx
|
|
227
|
+
|
|
228
|
+
```nginx
|
|
229
|
+
server {
|
|
230
|
+
listen 443 ssl http2;
|
|
231
|
+
server_name api.example.com;
|
|
232
|
+
|
|
233
|
+
ssl_certificate /path/to/cert.pem;
|
|
234
|
+
ssl_certificate_key /path/to/key.pem;
|
|
235
|
+
|
|
236
|
+
location / {
|
|
237
|
+
proxy_pass http://127.0.0.1:8080;
|
|
238
|
+
proxy_http_version 1.1;
|
|
239
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
240
|
+
proxy_set_header Connection "upgrade";
|
|
241
|
+
proxy_set_header Host $host;
|
|
242
|
+
proxy_buffering off;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Example Application
|
|
248
|
+
|
|
249
|
+
Check out the included real-time multi-client collaborative Todo app in [`examples/todo/`](examples/todo):
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
# Build packages and start example app
|
|
253
|
+
npm run build
|
|
254
|
+
npm run example:todo
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Open `http://localhost:3000` in multiple browser windows or simulate offline mode in DevTools to see seamless local-first persistence and instant background synchronization.
|
|
258
|
+
|
|
259
|
+
## Development & Testing
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
# Format code & lint fix
|
|
263
|
+
npm run format
|
|
264
|
+
npm run lint
|
|
265
|
+
|
|
266
|
+
# Type check
|
|
267
|
+
npm run typecheck
|
|
268
|
+
|
|
269
|
+
# Run unit and integration tests
|
|
270
|
+
npm test
|
|
271
|
+
|
|
272
|
+
# Run tests with coverage
|
|
273
|
+
npm run test:coverage
|
|
274
|
+
|
|
275
|
+
# Build bundle & type definitions
|
|
276
|
+
npm run build
|
|
277
|
+
```
|
package/bin/tetherdb.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { BackendType } from './backend.cjs';
|
|
2
|
+
/**
|
|
3
|
+
* Parsed CLI arguments and configuration options.
|
|
4
|
+
*/
|
|
5
|
+
export interface ParsedCliArgs {
|
|
6
|
+
command: string;
|
|
7
|
+
positionalArgs: string[];
|
|
8
|
+
port: number;
|
|
9
|
+
host: string;
|
|
10
|
+
backend: BackendType;
|
|
11
|
+
dir: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Parses raw command-line arguments into structured options.
|
|
15
|
+
*
|
|
16
|
+
* @param args - Command line arguments.
|
|
17
|
+
* @returns Parsed CLI configuration.
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseCliArgs(args: string[]): ParsedCliArgs;
|
|
20
|
+
//# sourceMappingURL=args.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { BackendType } from './backend.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parsed CLI arguments and configuration options.
|
|
4
|
+
*/
|
|
5
|
+
export interface ParsedCliArgs {
|
|
6
|
+
command: string;
|
|
7
|
+
positionalArgs: string[];
|
|
8
|
+
port: number;
|
|
9
|
+
host: string;
|
|
10
|
+
backend: BackendType;
|
|
11
|
+
dir: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Parses raw command-line arguments into structured options.
|
|
15
|
+
*
|
|
16
|
+
* @param args - Command line arguments.
|
|
17
|
+
* @returns Parsed CLI configuration.
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseCliArgs(args: string[]): ParsedCliArgs;
|
|
20
|
+
//# sourceMappingURL=args.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,WAAW,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAsC1D"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Storage, type StorageOptions } from '../server/index.cjs';
|
|
2
|
+
/**
|
|
3
|
+
* Backend persistence type for TetherDB server.
|
|
4
|
+
*/
|
|
5
|
+
export type BackendType = 'memory' | 'file' | 'sqlite';
|
|
6
|
+
/**
|
|
7
|
+
* Instantiates the matching Storage implementation for a given backend type and directory.
|
|
8
|
+
*
|
|
9
|
+
* @param backend - Target backend ('memory', 'file', or 'sqlite'). Defaults to 'memory'.
|
|
10
|
+
* @param baseDir - Directory path for file and sqlite backends. Defaults to '.data'.
|
|
11
|
+
* @param options - Optional storage configuration and limits.
|
|
12
|
+
* @returns Instantiated `Storage` engine.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createBackend(backend?: BackendType, baseDir?: string, options?: StorageOptions): Storage;
|
|
15
|
+
//# sourceMappingURL=backend.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Storage, type StorageOptions } from '../server/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Backend persistence type for TetherDB server.
|
|
4
|
+
*/
|
|
5
|
+
export type BackendType = 'memory' | 'file' | 'sqlite';
|
|
6
|
+
/**
|
|
7
|
+
* Instantiates the matching Storage implementation for a given backend type and directory.
|
|
8
|
+
*
|
|
9
|
+
* @param backend - Target backend ('memory', 'file', or 'sqlite'). Defaults to 'memory'.
|
|
10
|
+
* @param baseDir - Directory path for file and sqlite backends. Defaults to '.data'.
|
|
11
|
+
* @param options - Optional storage configuration and limits.
|
|
12
|
+
* @returns Instantiated `Storage` engine.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createBackend(backend?: BackendType, baseDir?: string, options?: StorageOptions): Storage;
|
|
15
|
+
//# sourceMappingURL=backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/cli/backend.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,OAAO,EACZ,KAAK,cAAc,EAGpB,MAAM,oBAAoB,CAAC;AAE5B;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEvD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,OAAO,GAAE,WAAsB,EAC/B,OAAO,SAAU,EACjB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAeT"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli/cli.ts"],"names":[],"mappings":";AAoBA;;;;GAIG;AACH,wBAAsB,MAAM,CAC1B,IAAI,GAAE,MAAM,EAA0B,GACrC,OAAO,CAAC,IAAI,CAAC,CAyCf"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Storage } from '../../server/index.cjs';
|
|
2
|
+
/**
|
|
3
|
+
* Handles the 'apps' command family (list, add, rm).
|
|
4
|
+
*
|
|
5
|
+
* @param storage - Instantiated Storage engine.
|
|
6
|
+
* @param args - Positional arguments: `[command, action, appId]`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function handleAppsCommand(storage: Storage, [, action, appId]: string[]): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=apps.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Storage } from '../../server/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Handles the 'apps' command family (list, add, rm).
|
|
4
|
+
*
|
|
5
|
+
* @param storage - Instantiated Storage engine.
|
|
6
|
+
* @param args - Positional arguments: `[command, action, appId]`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function handleAppsCommand(storage: Storage, [, action, appId]: string[]): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=apps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/apps.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAGb,MAAM,uBAAuB,CAAC;AAE/B;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,OAAO,EAChB,CAAC,EAAE,MAAe,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GACnC,OAAO,CAAC,IAAI,CAAC,CAwCf"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/help.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,SAAS,IAAI,IAAI,CAkChC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { handleAppsCommand } from './apps.cjs';
|
|
2
|
+
export { printHelp } from './help.cjs';
|
|
3
|
+
export { handleMaintenanceCommand } from './maintenance.cjs';
|
|
4
|
+
export { handleServeCommand } from './serve.cjs';
|
|
5
|
+
export { handleStatusCommand } from './status.cjs';
|
|
6
|
+
export { handleTablesCommand } from './tables.cjs';
|
|
7
|
+
export { handleUsersCommand } from './users.cjs';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { handleAppsCommand } from './apps.js';
|
|
2
|
+
export { printHelp } from './help.js';
|
|
3
|
+
export { handleMaintenanceCommand } from './maintenance.js';
|
|
4
|
+
export { handleServeCommand } from './serve.js';
|
|
5
|
+
export { handleStatusCommand } from './status.js';
|
|
6
|
+
export { handleTablesCommand } from './tables.js';
|
|
7
|
+
export { handleUsersCommand } from './users.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Storage } from '../../server/index.cjs';
|
|
2
|
+
/**
|
|
3
|
+
* Handles the 'maintenance' command to execute maintenance routines.
|
|
4
|
+
*
|
|
5
|
+
* @param storage - Instantiated Storage engine.
|
|
6
|
+
* @param positionalArgs - Positional CLI arguments (e.g. ['maintenance', 'checkpoint', 'my-app']).
|
|
7
|
+
*/
|
|
8
|
+
export declare function handleMaintenanceCommand(storage: Storage, positionalArgs: string[]): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=maintenance.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Storage } from '../../server/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Handles the 'maintenance' command to execute maintenance routines.
|
|
4
|
+
*
|
|
5
|
+
* @param storage - Instantiated Storage engine.
|
|
6
|
+
* @param positionalArgs - Positional CLI arguments (e.g. ['maintenance', 'checkpoint', 'my-app']).
|
|
7
|
+
*/
|
|
8
|
+
export declare function handleMaintenanceCommand(storage: Storage, positionalArgs: string[]): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=maintenance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"maintenance.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/maintenance.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAGb,MAAM,uBAAuB,CAAC;AAE/B;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,MAAM,EAAE,GACvB,OAAO,CAAC,IAAI,CAAC,CAsCf"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type RunningServer, type Storage } from '../../server/index.cjs';
|
|
2
|
+
import type { BackendType } from '../backend.cjs';
|
|
3
|
+
/**
|
|
4
|
+
* Handles the 'serve' command to launch the HTTP and WebSocket synchronization server.
|
|
5
|
+
*
|
|
6
|
+
* @param storage - Instantiated Storage engine.
|
|
7
|
+
* @param backend - Storage backend type ('memory', 'file', or 'sqlite').
|
|
8
|
+
* @param dir - Data directory for file-based backends.
|
|
9
|
+
* @param port - HTTP port to bind.
|
|
10
|
+
* @param host - Network interface to bind.
|
|
11
|
+
* @returns Handle to the running server.
|
|
12
|
+
*/
|
|
13
|
+
export declare function handleServeCommand(storage: Storage, backend: BackendType, dir: string, port: number, host: string): Promise<RunningServer>;
|
|
14
|
+
//# sourceMappingURL=serve.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type RunningServer, type Storage } from '../../server/index.js';
|
|
2
|
+
import type { BackendType } from '../backend.js';
|
|
3
|
+
/**
|
|
4
|
+
* Handles the 'serve' command to launch the HTTP and WebSocket synchronization server.
|
|
5
|
+
*
|
|
6
|
+
* @param storage - Instantiated Storage engine.
|
|
7
|
+
* @param backend - Storage backend type ('memory', 'file', or 'sqlite').
|
|
8
|
+
* @param dir - Data directory for file-based backends.
|
|
9
|
+
* @param port - HTTP port to bind.
|
|
10
|
+
* @param host - Network interface to bind.
|
|
11
|
+
* @returns Handle to the running server.
|
|
12
|
+
*/
|
|
13
|
+
export declare function handleServeCommand(storage: Storage, backend: BackendType, dir: string, port: number, host: string): Promise<RunningServer>;
|
|
14
|
+
//# sourceMappingURL=serve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/serve.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,OAAO,EAEb,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,aAAa,CAAC,CA8BxB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Storage } from '../../server/index.cjs';
|
|
2
|
+
/**
|
|
3
|
+
* Handles the 'status' command to display storage backend statistics.
|
|
4
|
+
*
|
|
5
|
+
* @param storage - Instantiated Storage engine.
|
|
6
|
+
* @param positionalArgs - Positional CLI arguments (e.g. ['status', 'my-app']).
|
|
7
|
+
*/
|
|
8
|
+
export declare function handleStatusCommand(storage: Storage, positionalArgs: string[]): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Storage } from '../../server/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Handles the 'status' command to display storage backend statistics.
|
|
4
|
+
*
|
|
5
|
+
* @param storage - Instantiated Storage engine.
|
|
6
|
+
* @param positionalArgs - Positional CLI arguments (e.g. ['status', 'my-app']).
|
|
7
|
+
*/
|
|
8
|
+
export declare function handleStatusCommand(storage: Storage, positionalArgs: string[]): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAGrD;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,MAAM,EAAE,GACvB,OAAO,CAAC,IAAI,CAAC,CAiCf"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Storage } from '../../server/index.cjs';
|
|
2
|
+
/**
|
|
3
|
+
* Handles the 'tables' command family (list, add, rm, <appid>).
|
|
4
|
+
*
|
|
5
|
+
* @param storage - Instantiated Storage engine.
|
|
6
|
+
* @param args - Positional arguments: `[command, action, appId, ...tableNames]`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function handleTablesCommand(storage: Storage, args: string[]): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=tables.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Storage } from '../../server/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Handles the 'tables' command family (list, add, rm, <appid>).
|
|
4
|
+
*
|
|
5
|
+
* @param storage - Instantiated Storage engine.
|
|
6
|
+
* @param args - Positional arguments: `[command, action, appId, ...tableNames]`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function handleTablesCommand(storage: Storage, args: string[]): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=tables.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/tables.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAGb,MAAM,uBAAuB,CAAC;AAE/B;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,IAAI,CAAC,CAuEf"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Storage } from '../../server/index.cjs';
|
|
2
|
+
/**
|
|
3
|
+
* Handles the 'users' command family (list, add, rm).
|
|
4
|
+
*
|
|
5
|
+
* @param storage - Instantiated Storage engine.
|
|
6
|
+
* @param args - Positional arguments: `[command, action, arg1, arg2]`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function handleUsersCommand(storage: Storage, [, action, arg1, arg2]: string[]): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=users.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Storage } from '../../server/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Handles the 'users' command family (list, add, rm).
|
|
4
|
+
*
|
|
5
|
+
* @param storage - Instantiated Storage engine.
|
|
6
|
+
* @param args - Positional arguments: `[command, action, arg1, arg2]`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function handleUsersCommand(storage: Storage, [, action, arg1, arg2]: string[]): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=users.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/users.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAGb,MAAM,uBAAuB,CAAC;AAE/B;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,OAAO,EAChB,CAAC,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,GACxC,OAAO,CAAC,IAAI,CAAC,CA0Cf"}
|