@toonstore/torm 0.2.0 β 0.4.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 +809 -78
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +549 -0
- package/dist/cli.js.map +1 -0
- package/dist/migrations.d.ts +62 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +275 -0
- package/dist/migrations.js.map +1 -0
- package/dist/studio-server.d.ts +24 -0
- package/dist/studio-server.d.ts.map +1 -0
- package/dist/studio-server.js +586 -0
- package/dist/studio-server.js.map +1 -0
- package/package.json +12 -4
- package/studio/index.html +663 -0
package/README.md
CHANGED
|
@@ -1,17 +1,32 @@
|
|
|
1
1
|
# @toonstore/torm
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> ToonStoreDB ORM Client for Node.js & TypeScript
|
|
4
4
|
|
|
5
|
-
A Mongoose-style ORM client for
|
|
5
|
+
A Mongoose-style ORM client for ToonStoreDB, providing type-safe models, validation, and query building.
|
|
6
|
+
|
|
7
|
+
> **What is ToonStoreDB?** ToonStoreDB is a blazingly fast embedded database with a built-in caching layer. This SDK lets you work with ToonStoreDB using a familiar Mongoose-like API.
|
|
8
|
+
|
|
9
|
+
## π― Quick Summary
|
|
10
|
+
|
|
11
|
+
**What is this?** A TypeScript/JavaScript ORM library (like Mongoose for MongoDB or Drizzle for PostgreSQL) that lets you easily work with ToonStoreDB.
|
|
12
|
+
|
|
13
|
+
**What do I need?**
|
|
14
|
+
- β
ToonStoreDB server running (the database)
|
|
15
|
+
- β
This npm package (`@toonstore/torm`)
|
|
16
|
+
- β
Node.js/TypeScript project
|
|
17
|
+
|
|
18
|
+
**What is TORM Studio?** An optional visual management tool (like phpMyAdmin or MongoDB Compass) for managing your ToonStoreDB data - NOT required for the SDK to work.
|
|
6
19
|
|
|
7
20
|
## β¨ Features
|
|
8
21
|
|
|
9
|
-
- β
**Type-Safe** - Full TypeScript support
|
|
10
|
-
- β
**
|
|
11
|
-
- β
**
|
|
12
|
-
- β
**Schema
|
|
13
|
-
- β
**
|
|
14
|
-
- β
**
|
|
22
|
+
- β
**Type-Safe** - Full TypeScript support with generics
|
|
23
|
+
- β
**Direct Database Connection** - Connects directly to ToonStoreDB
|
|
24
|
+
- β
**Mongoose-Like API** - Familiar API for easy adoption
|
|
25
|
+
- β
**Schema Validation** - Built-in validators (email, URL, min/max, patterns, custom)
|
|
26
|
+
- β
**Query Builder** - Fluent API for filtering, sorting, and pagination
|
|
27
|
+
- β
**Async/Await** - Modern promise-based API
|
|
28
|
+
- β
**Auto-Generated IDs** - Automatic document ID generation with timestamps
|
|
29
|
+
- β
**Zero Configuration** - Works out of the box with sensible defaults
|
|
15
30
|
|
|
16
31
|
## π¦ Installation
|
|
17
32
|
|
|
@@ -19,39 +34,70 @@ A Mongoose-style ORM client for ToonStore, providing type-safe models, validatio
|
|
|
19
34
|
npm install @toonstore/torm
|
|
20
35
|
# or
|
|
21
36
|
yarn add @toonstore/torm
|
|
37
|
+
# or
|
|
38
|
+
pnpm add @toonstore/torm
|
|
22
39
|
```
|
|
23
40
|
|
|
24
41
|
## π Quick Start
|
|
25
42
|
|
|
26
43
|
### Prerequisites
|
|
27
44
|
|
|
28
|
-
|
|
45
|
+
**ToonStoreDB must be running:**
|
|
46
|
+
|
|
29
47
|
```bash
|
|
30
|
-
|
|
48
|
+
# Start ToonStoreDB
|
|
49
|
+
./toonstoredb
|
|
31
50
|
# Default: localhost:6379
|
|
51
|
+
|
|
52
|
+
# Or using Docker
|
|
53
|
+
docker run -d -p 6379:6379 samso9th/toonstore:latest
|
|
54
|
+
|
|
55
|
+
# Or install from releases
|
|
56
|
+
# Download from: https://github.com/kalama-tech/toonstoredb/releases
|
|
32
57
|
```
|
|
33
58
|
|
|
34
|
-
|
|
59
|
+
> **Note:** ToonStoreDB is a high-performance database with Redis-compatible protocol. The SDK connects directly to your ToonStoreDB instance.
|
|
60
|
+
|
|
61
|
+
### Installation
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm install @toonstore/torm
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Step 1: Connect to ToonStoreDB
|
|
68
|
+
|
|
69
|
+
Create a new file (e.g., `app.ts`):
|
|
35
70
|
|
|
36
71
|
```typescript
|
|
37
72
|
import { TormClient } from '@toonstore/torm';
|
|
38
73
|
|
|
39
|
-
//
|
|
74
|
+
// Connect to ToonStoreDB
|
|
40
75
|
const torm = new TormClient({
|
|
41
76
|
host: 'localhost',
|
|
42
77
|
port: 6379,
|
|
43
78
|
// Or use connection URL:
|
|
44
|
-
// url: '
|
|
79
|
+
// url: 'toonstoredb://localhost:6379'
|
|
45
80
|
});
|
|
46
81
|
|
|
47
|
-
//
|
|
82
|
+
// Check connection
|
|
83
|
+
const health = await torm.health();
|
|
84
|
+
console.log('Database health:', health);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Step 2: Define Your Models
|
|
88
|
+
|
|
89
|
+
Define TypeScript interfaces and create models with validation:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// Define your data structure
|
|
48
93
|
interface User {
|
|
49
|
-
id: string;
|
|
50
94
|
name: string;
|
|
51
95
|
email: string;
|
|
52
96
|
age: number;
|
|
97
|
+
website?: string;
|
|
53
98
|
}
|
|
54
99
|
|
|
100
|
+
// Create model with schema validation
|
|
55
101
|
const User = torm.model<User>('User', {
|
|
56
102
|
name: {
|
|
57
103
|
type: 'string',
|
|
@@ -70,35 +116,378 @@ const User = torm.model<User>('User', {
|
|
|
70
116
|
min: 13,
|
|
71
117
|
max: 120,
|
|
72
118
|
},
|
|
119
|
+
website: {
|
|
120
|
+
type: 'string',
|
|
121
|
+
url: true,
|
|
122
|
+
},
|
|
73
123
|
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Step 3: Perform CRUD Operations
|
|
74
127
|
|
|
75
|
-
|
|
128
|
+
```typescript
|
|
129
|
+
// Create a user (ID auto-generated)
|
|
76
130
|
const user = await User.create({
|
|
77
|
-
id: 'user:1',
|
|
78
131
|
name: 'Alice',
|
|
79
132
|
email: 'alice@example.com',
|
|
80
133
|
age: 30,
|
|
134
|
+
website: 'https://alice.dev',
|
|
81
135
|
});
|
|
136
|
+
console.log('Created user:', user._id);
|
|
82
137
|
|
|
83
|
-
//
|
|
138
|
+
// Find all users
|
|
84
139
|
const allUsers = await User.find();
|
|
85
|
-
const specificUser = await User.findById('user:1');
|
|
86
140
|
|
|
87
|
-
//
|
|
141
|
+
// Find user by ID
|
|
142
|
+
const foundUser = await User.findById(user._id);
|
|
143
|
+
|
|
144
|
+
// Find one user by criteria
|
|
145
|
+
const alice = await User.findOne({ email: 'alice@example.com' });
|
|
146
|
+
|
|
147
|
+
// Update user
|
|
148
|
+
const updated = await User.update(user._id, { age: 31 });
|
|
149
|
+
|
|
150
|
+
// Delete user
|
|
151
|
+
await User.delete(user._id);
|
|
152
|
+
|
|
153
|
+
// Count users
|
|
154
|
+
const count = await User.count();
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Step 4: Query with Filters
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
// Find users 18 and older
|
|
88
161
|
const adults = await User.query()
|
|
89
|
-
.
|
|
162
|
+
.where('age', 'gte', 18)
|
|
90
163
|
.sort('name', 'asc')
|
|
164
|
+
.exec();
|
|
165
|
+
|
|
166
|
+
// Pagination
|
|
167
|
+
const page1 = await User.query()
|
|
168
|
+
.sort('_createdAt', 'desc')
|
|
91
169
|
.limit(10)
|
|
170
|
+
.skip(0)
|
|
92
171
|
.exec();
|
|
93
172
|
|
|
94
|
-
//
|
|
95
|
-
await User.
|
|
173
|
+
// Complex queries
|
|
174
|
+
const result = await User.query()
|
|
175
|
+
.where('age', 'gte', 18)
|
|
176
|
+
.where('age', 'lte', 65)
|
|
177
|
+
.sort('name', 'asc')
|
|
178
|
+
.limit(20)
|
|
179
|
+
.exec();
|
|
180
|
+
```
|
|
96
181
|
|
|
97
|
-
|
|
98
|
-
await User.delete('user:1');
|
|
182
|
+
### Step 5: Launch TORM Studio (Optional)
|
|
99
183
|
|
|
100
|
-
|
|
101
|
-
|
|
184
|
+
Visual database manager for browsing and editing data:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
# First time - auto-generates config
|
|
188
|
+
npx torm studio
|
|
189
|
+
|
|
190
|
+
# Edit torm.config.ts with your database credentials
|
|
191
|
+
# Then run again:
|
|
192
|
+
npx torm studio
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Opens at http://localhost:4983 with a visual interface to browse collections, edit records, and view stats.
|
|
196
|
+
|
|
197
|
+
## π οΈ CLI Commands
|
|
198
|
+
|
|
199
|
+
TORM includes a powerful CLI for database management and development workflows:
|
|
200
|
+
|
|
201
|
+
### TORM Studio
|
|
202
|
+
|
|
203
|
+
Launch the visual database manager (bundled with the SDK):
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
# Start TORM Studio
|
|
207
|
+
npx torm studio
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**First Run:** If no `torm.config.ts` exists, it will be auto-generated. Edit it with your database credentials, then run `npx torm studio` again.
|
|
211
|
+
|
|
212
|
+
**Access:** http://localhost:4983
|
|
213
|
+
|
|
214
|
+
**Configuration:**
|
|
215
|
+
|
|
216
|
+
You can use either TypeScript or JavaScript config files:
|
|
217
|
+
|
|
218
|
+
**Option 1: TypeScript Config (Recommended)**
|
|
219
|
+
|
|
220
|
+
Create `torm.config.ts` in your project root:
|
|
221
|
+
```typescript
|
|
222
|
+
export default {
|
|
223
|
+
dbCredentials: {
|
|
224
|
+
host: 'localhost', // or cloud host
|
|
225
|
+
port: 6379,
|
|
226
|
+
// For cloud/auth:
|
|
227
|
+
// password: 'your-password',
|
|
228
|
+
// url: 'redis://user:pass@host:port',
|
|
229
|
+
},
|
|
230
|
+
studio: {
|
|
231
|
+
port: 4983, // default
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
For TypeScript config files, install `tsx` (recommended) or `ts-node`:
|
|
237
|
+
```bash
|
|
238
|
+
npm install -D tsx
|
|
239
|
+
# or
|
|
240
|
+
npm install -D ts-node
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**Option 2: JavaScript Config**
|
|
244
|
+
|
|
245
|
+
Create `torm.config.js` in your project root:
|
|
246
|
+
```javascript
|
|
247
|
+
module.exports = {
|
|
248
|
+
dbCredentials: {
|
|
249
|
+
host: 'localhost',
|
|
250
|
+
port: 6379,
|
|
251
|
+
},
|
|
252
|
+
studio: {
|
|
253
|
+
port: 4983,
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
No additional dependencies needed for `.js` config files.
|
|
259
|
+
|
|
260
|
+
**Features:**
|
|
261
|
+
- π Visual database browser - browse collections and documents
|
|
262
|
+
- βοΈ Create, read, update, delete records with UI
|
|
263
|
+
- π Search and filter data
|
|
264
|
+
- π Real-time database statistics
|
|
265
|
+
- π¨ Modern dark-themed interface
|
|
266
|
+
- β
No separate installation - fully bundled with npm package!
|
|
267
|
+
- π Works with local or cloud-hosted ToonStoreDB
|
|
268
|
+
|
|
269
|
+
**Custom Port:**
|
|
270
|
+
```bash
|
|
271
|
+
npx torm studio --port 8080
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Migrations
|
|
275
|
+
|
|
276
|
+
Create and run data-shape migrations for your schemaless database:
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
# Create a new migration
|
|
280
|
+
npx torm migrate:create add_age_field
|
|
281
|
+
|
|
282
|
+
# Run pending migrations
|
|
283
|
+
npx torm migrate:up
|
|
284
|
+
|
|
285
|
+
# Run specific number of migrations
|
|
286
|
+
npx torm migrate:up 1
|
|
287
|
+
|
|
288
|
+
# Rollback last migration
|
|
289
|
+
npx torm migrate:down
|
|
290
|
+
|
|
291
|
+
# Rollback multiple migrations
|
|
292
|
+
npx torm migrate:down 2
|
|
293
|
+
|
|
294
|
+
# Check migration status
|
|
295
|
+
npx torm migrate:status
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
**How Migrations Work with ToonStoreDB:**
|
|
299
|
+
|
|
300
|
+
ToonStoreDB is schemaless at the storage layer, but your application code assumes a particular data shape. Migrations help you evolve that shape over time.
|
|
301
|
+
|
|
302
|
+
**Example Workflow:**
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
// Step 1: Create a migration file
|
|
306
|
+
// $ npx torm migrate:create add_age_to_users
|
|
307
|
+
// Creates: migrations/20260105_add_age_to_users.ts
|
|
308
|
+
|
|
309
|
+
// migrations/20260105_add_age_to_users.ts
|
|
310
|
+
export default {
|
|
311
|
+
async up(client) {
|
|
312
|
+
// Add 'age' field to all existing users with a default value
|
|
313
|
+
const pattern = 'toonstore:User:*';
|
|
314
|
+
const keys = await client.keys(pattern);
|
|
315
|
+
|
|
316
|
+
for (const key of keys) {
|
|
317
|
+
const data = JSON.parse(await client.get(key));
|
|
318
|
+
if (!data.age) {
|
|
319
|
+
data.age = 18; // Default value
|
|
320
|
+
await client.set(key, JSON.stringify(data));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
console.log(`Updated ${keys.length} users with age field`);
|
|
325
|
+
},
|
|
326
|
+
|
|
327
|
+
async down(client) {
|
|
328
|
+
// Remove 'age' field from all users
|
|
329
|
+
const pattern = 'toonstore:User:*';
|
|
330
|
+
const keys = await client.keys(pattern);
|
|
331
|
+
|
|
332
|
+
for (const key of keys) {
|
|
333
|
+
const data = JSON.parse(await client.get(key));
|
|
334
|
+
delete data.age;
|
|
335
|
+
await client.set(key, JSON.stringify(data));
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
console.log(`Removed age field from ${keys.length} users`);
|
|
339
|
+
},
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
// Step 2: Run migration
|
|
343
|
+
// $ npx torm migrate:up
|
|
344
|
+
// Applies: 20260105_add_age_to_users
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
**Common Migration Patterns:**
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
// 1. Add field with default value
|
|
351
|
+
export default {
|
|
352
|
+
async up(client) {
|
|
353
|
+
const keys = await client.keys('toonstore:User:*');
|
|
354
|
+
for (const key of keys) {
|
|
355
|
+
const user = JSON.parse(await client.get(key));
|
|
356
|
+
user.verified = user.verified ?? false;
|
|
357
|
+
await client.set(key, JSON.stringify(user));
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
async down(client) {
|
|
361
|
+
const keys = await client.keys('toonstore:User:*');
|
|
362
|
+
for (const key of keys) {
|
|
363
|
+
const user = JSON.parse(await client.get(key));
|
|
364
|
+
delete user.verified;
|
|
365
|
+
await client.set(key, JSON.stringify(user));
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
// 2. Rename field
|
|
371
|
+
export default {
|
|
372
|
+
async up(client) {
|
|
373
|
+
const keys = await client.keys('toonstore:Post:*');
|
|
374
|
+
for (const key of keys) {
|
|
375
|
+
const post = JSON.parse(await client.get(key));
|
|
376
|
+
if (post.author_id) {
|
|
377
|
+
post.authorId = post.author_id;
|
|
378
|
+
delete post.author_id;
|
|
379
|
+
await client.set(key, JSON.stringify(post));
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
async down(client) {
|
|
384
|
+
const keys = await client.keys('toonstore:Post:*');
|
|
385
|
+
for (const key of keys) {
|
|
386
|
+
const post = JSON.parse(await client.get(key));
|
|
387
|
+
if (post.authorId) {
|
|
388
|
+
post.author_id = post.authorId;
|
|
389
|
+
delete post.authorId;
|
|
390
|
+
await client.set(key, JSON.stringify(post));
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
// 3. Data transformation
|
|
397
|
+
export default {
|
|
398
|
+
async up(client) {
|
|
399
|
+
const keys = await client.keys('toonstore:User:*');
|
|
400
|
+
for (const key of keys) {
|
|
401
|
+
const user = JSON.parse(await client.get(key));
|
|
402
|
+
// Split full name into first and last
|
|
403
|
+
if (user.name && !user.firstName) {
|
|
404
|
+
const [firstName, ...lastName] = user.name.split(' ');
|
|
405
|
+
user.firstName = firstName;
|
|
406
|
+
user.lastName = lastName.join(' ');
|
|
407
|
+
delete user.name;
|
|
408
|
+
await client.set(key, JSON.stringify(user));
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
async down(client) {
|
|
413
|
+
const keys = await client.keys('toonstore:User:*');
|
|
414
|
+
for (const key of keys) {
|
|
415
|
+
const user = JSON.parse(await client.get(key));
|
|
416
|
+
if (user.firstName) {
|
|
417
|
+
user.name = `${user.firstName} ${user.lastName || ''}`.trim();
|
|
418
|
+
delete user.firstName;
|
|
419
|
+
delete user.lastName;
|
|
420
|
+
await client.set(key, JSON.stringify(user));
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
},
|
|
424
|
+
};
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
**Migration Tracking:**
|
|
428
|
+
|
|
429
|
+
TORM tracks which migrations have run using a special key:
|
|
430
|
+
```
|
|
431
|
+
toonstore:_migrations -> ["20260105_add_age_to_users", "20260106_rename_author_field"]
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
**Best Practices:**
|
|
435
|
+
|
|
436
|
+
1. **Never modify old migrations** - create new ones to fix issues
|
|
437
|
+
2. **Always provide `down()` for rollback** - test it works
|
|
438
|
+
3. **Use transactions when available** - ensure atomic changes
|
|
439
|
+
4. **Test on copy of production data** - before running in prod
|
|
440
|
+
5. **Version control migrations** - commit with code changes
|
|
441
|
+
|
|
442
|
+
### Type Generation (Coming Soon)
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
# Generate TypeScript types from your schema
|
|
446
|
+
npx torm generate
|
|
447
|
+
|
|
448
|
+
# Watch mode for development
|
|
449
|
+
npx torm generate --watch
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
**How Type Generation Will Work:**
|
|
453
|
+
|
|
454
|
+
TORM will introspect your database and generate TypeScript interfaces:
|
|
455
|
+
|
|
456
|
+
```bash
|
|
457
|
+
# Scan your database and generate types
|
|
458
|
+
$ npx torm generate
|
|
459
|
+
|
|
460
|
+
# Output: src/generated/torm-types.ts
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
**Generated Types:**
|
|
464
|
+
```typescript
|
|
465
|
+
// src/generated/torm-types.ts (auto-generated)
|
|
466
|
+
export interface User {
|
|
467
|
+
_id: string;
|
|
468
|
+
name: string;
|
|
469
|
+
email: string;
|
|
470
|
+
age: number;
|
|
471
|
+
website?: string;
|
|
472
|
+
_createdAt: string;
|
|
473
|
+
_updatedAt: string;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export interface Post {
|
|
477
|
+
_id: string;
|
|
478
|
+
title: string;
|
|
479
|
+
content: string;
|
|
480
|
+
authorId: string;
|
|
481
|
+
published: boolean;
|
|
482
|
+
_createdAt: string;
|
|
483
|
+
_updatedAt: string;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Use generated types
|
|
487
|
+
import { User, Post } from './generated/torm-types';
|
|
488
|
+
|
|
489
|
+
const User = torm.model<User>('User');
|
|
490
|
+
const Post = torm.model<Post>('Post');
|
|
102
491
|
```
|
|
103
492
|
|
|
104
493
|
## π API Documentation
|
|
@@ -112,35 +501,45 @@ const torm = new TormClient(options?: TormClientOptions);
|
|
|
112
501
|
```
|
|
113
502
|
|
|
114
503
|
**Options:**
|
|
115
|
-
- `host` (string) -
|
|
116
|
-
- `port` (number) -
|
|
117
|
-
- `url` (string) -
|
|
118
|
-
- All standard
|
|
504
|
+
- `host` (string) - ToonStoreDB host (default: `localhost`)
|
|
505
|
+
- `port` (number) - ToonStoreDB port (default: `6379`)
|
|
506
|
+
- `url` (string) - ToonStoreDB connection URL (e.g., `toonstoredb://localhost:6379`)
|
|
507
|
+
- All standard connection options are supported
|
|
119
508
|
|
|
120
509
|
#### Methods
|
|
121
510
|
|
|
122
511
|
- `model<T>(name, schema?, options?)` - Create a model
|
|
123
|
-
- `health()` - Check
|
|
124
|
-
- `disconnect()` - Close
|
|
125
|
-
- `isConnected()` - Check if connected to
|
|
512
|
+
- `health()` - Check database connection health
|
|
513
|
+
- `disconnect()` - Close database connection
|
|
514
|
+
- `isConnected()` - Check if connected to database
|
|
515
|
+
- `getClient()` - Get underlying database client instance
|
|
126
516
|
|
|
127
517
|
### Model
|
|
128
518
|
|
|
519
|
+
All documents automatically include:
|
|
520
|
+
- `_id` - Auto-generated unique ID
|
|
521
|
+
- `_createdAt` - ISO timestamp of creation
|
|
522
|
+
- `_updatedAt` - ISO timestamp of last update
|
|
523
|
+
|
|
129
524
|
#### CRUD Operations
|
|
130
525
|
|
|
131
526
|
```typescript
|
|
132
|
-
// Create
|
|
527
|
+
// Create (ID auto-generated)
|
|
133
528
|
const doc = await Model.create(data);
|
|
529
|
+
// Returns: { ...data, _id, _createdAt, _updatedAt }
|
|
134
530
|
|
|
135
531
|
// Read
|
|
136
532
|
const all = await Model.find();
|
|
137
533
|
const one = await Model.findById(id);
|
|
534
|
+
const match = await Model.findOne({ email: 'user@example.com' });
|
|
138
535
|
|
|
139
536
|
// Update
|
|
140
537
|
const updated = await Model.update(id, data);
|
|
141
538
|
|
|
142
539
|
// Delete
|
|
143
540
|
const deleted = await Model.delete(id);
|
|
541
|
+
await Model.deleteMany(); // Delete all
|
|
542
|
+
await Model.deleteMany({ status: 'inactive' }); // Delete matching
|
|
144
543
|
|
|
145
544
|
// Count
|
|
146
545
|
const count = await Model.count();
|
|
@@ -150,16 +549,16 @@ const count = await Model.count();
|
|
|
150
549
|
|
|
151
550
|
```typescript
|
|
152
551
|
const results = await Model.query()
|
|
153
|
-
.
|
|
154
|
-
.
|
|
155
|
-
.sort(field, order) // Sort results
|
|
552
|
+
.where(field, operator, value) // Add filter
|
|
553
|
+
.equals(field, value) // Shorthand for .where(field, 'eq', value)
|
|
554
|
+
.sort(field, order) // Sort results ('asc' | 'desc')
|
|
156
555
|
.limit(n) // Limit results
|
|
157
|
-
.skip(n) // Skip results
|
|
556
|
+
.skip(n) // Skip results (pagination)
|
|
158
557
|
.exec(); // Execute query
|
|
159
558
|
|
|
160
559
|
// Count with filters
|
|
161
560
|
const count = await Model.query()
|
|
162
|
-
.
|
|
561
|
+
.where('status', 'eq', 'active')
|
|
163
562
|
.count();
|
|
164
563
|
```
|
|
165
564
|
|
|
@@ -219,16 +618,26 @@ age: { type: 'number', min: 0, max: 120 }
|
|
|
219
618
|
// Pattern matching
|
|
220
619
|
phone: { type: 'string', pattern: /^\d{10}$/ }
|
|
221
620
|
|
|
222
|
-
// Custom validation
|
|
621
|
+
// Custom validation (sync or async)
|
|
223
622
|
username: {
|
|
224
623
|
type: 'string',
|
|
225
624
|
validate: (value) => /^[a-z0-9_]+$/.test(value),
|
|
226
625
|
}
|
|
626
|
+
|
|
627
|
+
// Async custom validation
|
|
628
|
+
email: {
|
|
629
|
+
type: 'string',
|
|
630
|
+
validate: async (value) => {
|
|
631
|
+
// Check if email is already taken
|
|
632
|
+
const existing = await User.findOne({ email: value });
|
|
633
|
+
return !existing;
|
|
634
|
+
},
|
|
635
|
+
}
|
|
227
636
|
```
|
|
228
637
|
|
|
229
638
|
## π Examples
|
|
230
639
|
|
|
231
|
-
### Example 1: User Management
|
|
640
|
+
### Example 1: User Management System
|
|
232
641
|
|
|
233
642
|
```typescript
|
|
234
643
|
import { TormClient } from '@toonstore/torm';
|
|
@@ -236,10 +645,10 @@ import { TormClient } from '@toonstore/torm';
|
|
|
236
645
|
const torm = new TormClient();
|
|
237
646
|
|
|
238
647
|
interface User {
|
|
239
|
-
id: string;
|
|
240
648
|
username: string;
|
|
241
649
|
email: string;
|
|
242
650
|
age: number;
|
|
651
|
+
role: 'admin' | 'user';
|
|
243
652
|
}
|
|
244
653
|
|
|
245
654
|
const User = torm.model<User>('User', {
|
|
@@ -258,33 +667,43 @@ const User = torm.model<User>('User', {
|
|
|
258
667
|
type: 'number',
|
|
259
668
|
min: 18,
|
|
260
669
|
},
|
|
670
|
+
role: {
|
|
671
|
+
type: 'string',
|
|
672
|
+
required: true,
|
|
673
|
+
},
|
|
261
674
|
});
|
|
262
675
|
|
|
263
676
|
// Create users
|
|
264
|
-
await User.create({
|
|
265
|
-
id: 'user:1',
|
|
677
|
+
const alice = await User.create({
|
|
266
678
|
username: 'alice',
|
|
267
679
|
email: 'alice@example.com',
|
|
268
680
|
age: 30,
|
|
681
|
+
role: 'admin',
|
|
269
682
|
});
|
|
270
683
|
|
|
271
|
-
// Find
|
|
272
|
-
const
|
|
273
|
-
.
|
|
684
|
+
// Find by role
|
|
685
|
+
const admins = await User.query()
|
|
686
|
+
.where('role', 'eq', 'admin')
|
|
274
687
|
.sort('username', 'asc')
|
|
275
688
|
.exec();
|
|
689
|
+
|
|
690
|
+
// Find adults sorted by age
|
|
691
|
+
const adults = await User.query()
|
|
692
|
+
.where('age', 'gte', 18)
|
|
693
|
+
.sort('age', 'desc')
|
|
694
|
+
.exec();
|
|
276
695
|
```
|
|
277
696
|
|
|
278
|
-
### Example 2: Blog Posts
|
|
697
|
+
### Example 2: Blog Posts with Tags
|
|
279
698
|
|
|
280
699
|
```typescript
|
|
281
700
|
interface Post {
|
|
282
|
-
id: string;
|
|
283
701
|
title: string;
|
|
284
702
|
content: string;
|
|
285
|
-
|
|
703
|
+
authorId: string;
|
|
286
704
|
published: boolean;
|
|
287
705
|
tags: string[];
|
|
706
|
+
views: number;
|
|
288
707
|
}
|
|
289
708
|
|
|
290
709
|
const Post = torm.model<Post>('Post', {
|
|
@@ -299,7 +718,7 @@ const Post = torm.model<Post>('Post', {
|
|
|
299
718
|
required: true,
|
|
300
719
|
minLength: 10,
|
|
301
720
|
},
|
|
302
|
-
|
|
721
|
+
authorId: {
|
|
303
722
|
type: 'string',
|
|
304
723
|
required: true,
|
|
305
724
|
},
|
|
@@ -309,41 +728,48 @@ const Post = torm.model<Post>('Post', {
|
|
|
309
728
|
tags: {
|
|
310
729
|
type: 'array',
|
|
311
730
|
},
|
|
731
|
+
views: {
|
|
732
|
+
type: 'number',
|
|
733
|
+
min: 0,
|
|
734
|
+
},
|
|
312
735
|
});
|
|
313
736
|
|
|
314
737
|
// Create post
|
|
315
|
-
await Post.create({
|
|
316
|
-
id: 'post:1',
|
|
738
|
+
const post = await Post.create({
|
|
317
739
|
title: 'Getting Started with TORM',
|
|
318
|
-
content: 'TORM is an amazing ORM for
|
|
319
|
-
|
|
740
|
+
content: 'TORM is an amazing ORM for ToonStoreDB with type-safe models and validation...',
|
|
741
|
+
authorId: alice._id,
|
|
320
742
|
published: true,
|
|
321
|
-
tags: ['orm', 'database', 'toonstore'],
|
|
743
|
+
tags: ['orm', 'database', 'toonstore', 'redis'],
|
|
744
|
+
views: 0,
|
|
322
745
|
});
|
|
323
746
|
|
|
324
747
|
// Find published posts
|
|
325
|
-
const
|
|
326
|
-
.
|
|
327
|
-
.sort('
|
|
748
|
+
const publishedPosts = await Post.query()
|
|
749
|
+
.where('published', 'eq', true)
|
|
750
|
+
.sort('_createdAt', 'desc')
|
|
328
751
|
.limit(10)
|
|
329
752
|
.exec();
|
|
330
753
|
|
|
331
754
|
// Find posts by author
|
|
332
|
-
const
|
|
333
|
-
.
|
|
755
|
+
const alicePosts = await Post.query()
|
|
756
|
+
.where('authorId', 'eq', alice._id)
|
|
334
757
|
.exec();
|
|
758
|
+
|
|
759
|
+
// Update view count
|
|
760
|
+
await Post.update(post._id, { views: post.views + 1 });
|
|
335
761
|
```
|
|
336
762
|
|
|
337
|
-
### Example 3: E-Commerce
|
|
763
|
+
### Example 3: E-Commerce Product Catalog
|
|
338
764
|
|
|
339
765
|
```typescript
|
|
340
766
|
interface Product {
|
|
341
|
-
id: string;
|
|
342
767
|
name: string;
|
|
343
768
|
price: number;
|
|
344
769
|
stock: number;
|
|
345
770
|
sku: string;
|
|
346
771
|
category: string;
|
|
772
|
+
description?: string;
|
|
347
773
|
}
|
|
348
774
|
|
|
349
775
|
const Product = torm.model<Product>('Product', {
|
|
@@ -368,31 +794,97 @@ const Product = torm.model<Product>('Product', {
|
|
|
368
794
|
},
|
|
369
795
|
});
|
|
370
796
|
|
|
371
|
-
// Create
|
|
372
|
-
await Product.create({
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
stock: 50,
|
|
797
|
+
// Create products
|
|
798
|
+
const laptop = await Product.create({
|
|
799
|
+
name: 'Gaming Laptop',
|
|
800
|
+
price: 1299.99,
|
|
801
|
+
stock: 15,
|
|
377
802
|
sku: 'LAP-12345',
|
|
378
803
|
category: 'electronics',
|
|
804
|
+
description: 'High-performance gaming laptop with RTX 4070',
|
|
379
805
|
});
|
|
380
806
|
|
|
381
807
|
// Find products in stock
|
|
382
808
|
const inStock = await Product.query()
|
|
383
|
-
.
|
|
809
|
+
.where('stock', 'gt', 0)
|
|
384
810
|
.sort('price', 'asc')
|
|
385
811
|
.exec();
|
|
386
812
|
|
|
813
|
+
// Find by category with pagination
|
|
814
|
+
const electronics = await Product.query()
|
|
815
|
+
.where('category', 'eq', 'electronics')
|
|
816
|
+
.sort('name', 'asc')
|
|
817
|
+
.skip(0)
|
|
818
|
+
.limit(20)
|
|
819
|
+
.exec();
|
|
820
|
+
|
|
387
821
|
// Find expensive products
|
|
388
|
-
const
|
|
389
|
-
.
|
|
822
|
+
const premium = await Product.query()
|
|
823
|
+
.where('price', 'gte', 1000)
|
|
824
|
+
.sort('price', 'desc')
|
|
825
|
+
.exec();
|
|
826
|
+
|
|
827
|
+
// Low stock alert
|
|
828
|
+
const lowStock = await Product.query()
|
|
829
|
+
.where('stock', 'lt', 10)
|
|
390
830
|
.exec();
|
|
391
831
|
```
|
|
392
832
|
|
|
833
|
+
## π Migration from v0.1.x
|
|
834
|
+
|
|
835
|
+
Version 0.2.0 introduces breaking changes:
|
|
836
|
+
|
|
837
|
+
### What Changed
|
|
838
|
+
- **No more HTTP server required** - SDK now connects directly to ToonStoreDB
|
|
839
|
+
- **Auto-generated IDs** - No need to provide `id` field, use `_id` instead
|
|
840
|
+
- **New connection options** - Use `host`, `port`, or `url` for ToonStoreDB connection
|
|
841
|
+
- **TORM Studio bundled** - Visual database manager included (run `npx torm studio`)
|
|
842
|
+
|
|
843
|
+
### Migration Steps
|
|
844
|
+
|
|
845
|
+
**Old (v0.1.x):**
|
|
846
|
+
```typescript
|
|
847
|
+
// Required TORM server via HTTP
|
|
848
|
+
const torm = new TormClient({
|
|
849
|
+
baseURL: 'http://localhost:3001',
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
const user = await User.create({
|
|
853
|
+
id: 'user:1', // Manual ID
|
|
854
|
+
name: 'Alice',
|
|
855
|
+
});
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
**New (v0.2.x):**
|
|
859
|
+
```typescript
|
|
860
|
+
// Direct ToonStoreDB connection
|
|
861
|
+
const torm = new TormClient({
|
|
862
|
+
host: 'localhost',
|
|
863
|
+
port: 6379,
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
const user = await User.create({
|
|
867
|
+
name: 'Alice', // No id needed
|
|
868
|
+
});
|
|
869
|
+
console.log(user._id); // Auto-generated: "1736076000000-abc123xyz"
|
|
870
|
+
|
|
871
|
+
// Launch Studio
|
|
872
|
+
// $ npx torm studio
|
|
873
|
+
// Opens http://localhost:4983
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
**Steps:**
|
|
877
|
+
1. Update connection config (remove `baseURL`, add `host`/`port`)
|
|
878
|
+
2. Remove manual `id` fields from `create()` calls
|
|
879
|
+
3. Use `_id` to access document IDs
|
|
880
|
+
4. (Optional) Create `torm.config.ts` for Studio
|
|
881
|
+
|
|
393
882
|
## π§ͺ Running Examples
|
|
394
883
|
|
|
395
884
|
```bash
|
|
885
|
+
# Make sure ToonStoreDB is running
|
|
886
|
+
./toonstoredb
|
|
887
|
+
|
|
396
888
|
# Install dependencies
|
|
397
889
|
npm install
|
|
398
890
|
|
|
@@ -403,7 +895,7 @@ npm run build
|
|
|
403
895
|
npm run example
|
|
404
896
|
```
|
|
405
897
|
|
|
406
|
-
##
|
|
898
|
+
## ποΈ Development
|
|
407
899
|
|
|
408
900
|
```bash
|
|
409
901
|
# Install dependencies
|
|
@@ -412,15 +904,254 @@ npm install
|
|
|
412
904
|
# Build TypeScript
|
|
413
905
|
npm run build
|
|
414
906
|
|
|
415
|
-
# Watch mode (auto-rebuild)
|
|
907
|
+
# Watch mode (auto-rebuild on changes)
|
|
416
908
|
npm run dev
|
|
909
|
+
|
|
910
|
+
# Run tests
|
|
911
|
+
npm test
|
|
912
|
+
|
|
913
|
+
# Start Studio
|
|
914
|
+
npm run studio
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
## π― Complete Example
|
|
918
|
+
|
|
919
|
+
Here's a complete example building a blog application:
|
|
920
|
+
|
|
921
|
+
```typescript
|
|
922
|
+
import { TormClient } from '@toonstore/torm';
|
|
923
|
+
|
|
924
|
+
// 1. Connect to database
|
|
925
|
+
const torm = new TormClient({
|
|
926
|
+
host: 'localhost',
|
|
927
|
+
port: 6379,
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
// 2. Define models
|
|
931
|
+
interface User {
|
|
932
|
+
username: string;
|
|
933
|
+
email: string;
|
|
934
|
+
bio?: string;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
interface Post {
|
|
938
|
+
title: string;
|
|
939
|
+
content: string;
|
|
940
|
+
authorId: string;
|
|
941
|
+
published: boolean;
|
|
942
|
+
tags: string[];
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
const User = torm.model<User>('User', {
|
|
946
|
+
username: {
|
|
947
|
+
type: 'string',
|
|
948
|
+
required: true,
|
|
949
|
+
minLength: 3,
|
|
950
|
+
pattern: /^[a-zA-Z0-9_]+$/,
|
|
951
|
+
},
|
|
952
|
+
email: {
|
|
953
|
+
type: 'string',
|
|
954
|
+
required: true,
|
|
955
|
+
email: true,
|
|
956
|
+
},
|
|
957
|
+
bio: {
|
|
958
|
+
type: 'string',
|
|
959
|
+
maxLength: 500,
|
|
960
|
+
},
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
const Post = torm.model<Post>('Post', {
|
|
964
|
+
title: {
|
|
965
|
+
type: 'string',
|
|
966
|
+
required: true,
|
|
967
|
+
minLength: 5,
|
|
968
|
+
maxLength: 200,
|
|
969
|
+
},
|
|
970
|
+
content: {
|
|
971
|
+
type: 'string',
|
|
972
|
+
required: true,
|
|
973
|
+
minLength: 10,
|
|
974
|
+
},
|
|
975
|
+
authorId: {
|
|
976
|
+
type: 'string',
|
|
977
|
+
required: true,
|
|
978
|
+
},
|
|
979
|
+
published: {
|
|
980
|
+
type: 'boolean',
|
|
981
|
+
},
|
|
982
|
+
tags: {
|
|
983
|
+
type: 'array',
|
|
984
|
+
},
|
|
985
|
+
});
|
|
986
|
+
|
|
987
|
+
// 3. Use the models
|
|
988
|
+
async function main() {
|
|
989
|
+
// Create a user
|
|
990
|
+
const user = await User.create({
|
|
991
|
+
username: 'alice',
|
|
992
|
+
email: 'alice@example.com',
|
|
993
|
+
bio: 'Love coding and coffee β',
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
// Create posts
|
|
997
|
+
await Post.create({
|
|
998
|
+
title: 'Getting Started with ToonStoreDB',
|
|
999
|
+
content: 'ToonStoreDB is a blazingly fast database...',
|
|
1000
|
+
authorId: user._id,
|
|
1001
|
+
published: true,
|
|
1002
|
+
tags: ['database', 'toonstoredb', 'tutorial'],
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
await Post.create({
|
|
1006
|
+
title: 'Building a Blog with TORM',
|
|
1007
|
+
content: 'TORM makes it easy to build applications...',
|
|
1008
|
+
authorId: user._id,
|
|
1009
|
+
published: false,
|
|
1010
|
+
tags: ['torm', 'tutorial'],
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
// Query published posts
|
|
1014
|
+
const publishedPosts = await Post.query()
|
|
1015
|
+
.where('published', 'eq', true)
|
|
1016
|
+
.where('authorId', 'eq', user._id)
|
|
1017
|
+
.sort('_createdAt', 'desc')
|
|
1018
|
+
.exec();
|
|
1019
|
+
|
|
1020
|
+
console.log('Published posts:', publishedPosts.length);
|
|
1021
|
+
|
|
1022
|
+
// Get user's post count
|
|
1023
|
+
const postCount = await Post.query()
|
|
1024
|
+
.where('authorId', 'eq', user._id)
|
|
1025
|
+
.count();
|
|
1026
|
+
|
|
1027
|
+
console.log(`${user.username} has ${postCount} posts`);
|
|
1028
|
+
|
|
1029
|
+
// Clean up
|
|
1030
|
+
await torm.disconnect();
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
main().catch(console.error);
|
|
417
1034
|
```
|
|
418
1035
|
|
|
1036
|
+
## π Why TORM?
|
|
1037
|
+
|
|
1038
|
+
- **Standalone SDK** - No TORM server needed, just ToonStoreDB
|
|
1039
|
+
- **Type Safety** - Full TypeScript support with generics
|
|
1040
|
+
- **Familiar API** - If you know Mongoose, you know TORM
|
|
1041
|
+
- **High Performance** - Leverages ToonStoreDB's blazing fast speed (5.28M ops/sec)
|
|
1042
|
+
- **Validation Built-in** - Comprehensive validation without extra libraries
|
|
1043
|
+
- **Modern Async/Await** - Clean, readable promise-based code
|
|
1044
|
+
|
|
1045
|
+
## π’ Architecture
|
|
1046
|
+
|
|
1047
|
+
```
|
|
1048
|
+
Your Node.js App β @toonstore/torm SDK β ToonStoreDB
|
|
1049
|
+
```
|
|
1050
|
+
|
|
1051
|
+
**What you need to run:**
|
|
1052
|
+
- β
ToonStoreDB server (the database)
|
|
1053
|
+
- β
Your Node.js app with this SDK
|
|
1054
|
+
|
|
1055
|
+
**What you DON'T need:**
|
|
1056
|
+
- β TORM Server (that's only for TORM Studio UI - a separate optional visual management tool)
|
|
1057
|
+
- β Any HTTP server or API layer
|
|
1058
|
+
- β Redis installation (ToonStoreDB is the database, it just uses Redis-compatible protocol internally)
|
|
1059
|
+
|
|
1060
|
+
## πΊοΈ Roadmap
|
|
1061
|
+
|
|
1062
|
+
**SDK Features:**
|
|
1063
|
+
- [ ] Relationships (references between models)
|
|
1064
|
+
- [ ] Hooks (pre/post save, update, delete)
|
|
1065
|
+
- [ ] Indexes and search optimization
|
|
1066
|
+
- [ ] Transactions support
|
|
1067
|
+
- [ ] Aggregation pipelines
|
|
1068
|
+
- [ ] Virtual fields
|
|
1069
|
+
- [ ] Plugins system
|
|
1070
|
+
|
|
1071
|
+
**CLI Tools:**
|
|
1072
|
+
- [x] TORM Studio (bundled Node.js server) β
|
|
1073
|
+
- [x] Schema migrations (data-shape migrations for schemaless DB) β
|
|
1074
|
+
- [x] `torm migrate:create` - Create migration file
|
|
1075
|
+
- [x] `torm migrate:up` - Run pending migrations
|
|
1076
|
+
- [x] `torm migrate:down` - Rollback last migration
|
|
1077
|
+
- [x] `torm migrate:status` - Check migration status
|
|
1078
|
+
- [ ] Type generation from existing data
|
|
1079
|
+
- [ ] Database seeding
|
|
1080
|
+
- [ ] Data export/import tools
|
|
1081
|
+
|
|
1082
|
+
**Studio Enhancements:**
|
|
1083
|
+
- [ ] Custom domain (local.torm.studio via DNS) - no hosts file needed
|
|
1084
|
+
- [ ] Advanced query builder UI
|
|
1085
|
+
- [ ] Export/import data
|
|
1086
|
+
- [ ] Real-time collaboration
|
|
1087
|
+
- [ ] Schema visualization
|
|
1088
|
+
|
|
1089
|
+
**Migration System (Planned):**
|
|
1090
|
+
|
|
1091
|
+
Unlike SQL databases, ToonStoreDB is schemalessβbut you still need **data-shape migrations** for application-level schema evolution:
|
|
1092
|
+
|
|
1093
|
+
```bash
|
|
1094
|
+
# Create migration for data transformation
|
|
1095
|
+
torm migrate:create add_age_field
|
|
1096
|
+
|
|
1097
|
+
# Run pending migrations
|
|
1098
|
+
torm migrate:up
|
|
1099
|
+
|
|
1100
|
+
# Rollback last migration
|
|
1101
|
+
torm migrate:down
|
|
1102
|
+
|
|
1103
|
+
# Check what's been applied
|
|
1104
|
+
torm migrate:status
|
|
1105
|
+
```
|
|
1106
|
+
|
|
1107
|
+
**Example Migration:**
|
|
1108
|
+
```typescript
|
|
1109
|
+
// migrations/20260105_add_age_field.ts
|
|
1110
|
+
export default {
|
|
1111
|
+
async up(client) {
|
|
1112
|
+
// Add 'age' to all users with default value
|
|
1113
|
+
const keys = await client.keys('toonstore:User:*');
|
|
1114
|
+
for (const key of keys) {
|
|
1115
|
+
const user = JSON.parse(await client.get(key));
|
|
1116
|
+
user.age = user.age ?? 18; // Default age
|
|
1117
|
+
await client.set(key, JSON.stringify(user));
|
|
1118
|
+
}
|
|
1119
|
+
},
|
|
1120
|
+
async down(client) {
|
|
1121
|
+
// Remove 'age' from all users
|
|
1122
|
+
const keys = await client.keys('toonstore:User:*');
|
|
1123
|
+
for (const key of keys) {
|
|
1124
|
+
const user = JSON.parse(await client.get(key));
|
|
1125
|
+
delete user.age;
|
|
1126
|
+
await client.set(key, JSON.stringify(user));
|
|
1127
|
+
}
|
|
1128
|
+
},
|
|
1129
|
+
};
|
|
1130
|
+
```
|
|
1131
|
+
|
|
1132
|
+
**Common Use Cases:**
|
|
1133
|
+
- Add/remove fields with defaults
|
|
1134
|
+
- Rename fields (`author_id` β `authorId`)
|
|
1135
|
+
- Transform data (split name into first/last)
|
|
1136
|
+
- Back-fill missing values
|
|
1137
|
+
- Data cleanup and normalization
|
|
1138
|
+
|
|
1139
|
+
## π€ Contributing
|
|
1140
|
+
|
|
1141
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
1142
|
+
|
|
419
1143
|
## π License
|
|
420
1144
|
|
|
421
1145
|
MIT
|
|
422
1146
|
|
|
423
|
-
##
|
|
1147
|
+
## π Links
|
|
1148
|
+
|
|
1149
|
+
- [ToonStoreDB](https://github.com/toonstore/toonstoredb) - The blazingly fast database
|
|
1150
|
+
- [TORM](https://github.com/toonstore/torm) - Multi-language ORM with Studio
|
|
1151
|
+
- [TOON Format](https://github.com/toon-format/toon) - The data format specification
|
|
1152
|
+
- [NPM Package](https://www.npmjs.com/package/@toonstore/torm)
|
|
1153
|
+
- [TORM Studio Guide](https://github.com/toonstore/torm/blob/main/docs/STUDIO.md)
|
|
1154
|
+
|
|
1155
|
+
## π¬ Support
|
|
424
1156
|
|
|
425
|
-
|
|
426
|
-
- Built for [ToonStore](https://github.com/toon-format/toon)
|
|
1157
|
+
For questions and support, please open an issue on GitHub.
|