@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 CHANGED
@@ -1,17 +1,32 @@
1
1
  # @toonstore/torm
2
2
 
3
- > ToonStore ORM Client for Node.js & TypeScript
3
+ > ToonStoreDB ORM Client for Node.js & TypeScript
4
4
 
5
- A Mongoose-style ORM client for ToonStore, providing type-safe models, validation, and query building.
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
- - βœ… **Validation** - Built-in validators (email, URL, min/max, patterns)
11
- - βœ… **Query Builder** - Fluent API for filtering and sorting
12
- - βœ… **Schema Definition** - Define your data models
13
- - βœ… **Async/Await** - Promise-based API
14
- - βœ… **Direct Redis Connection** - No server required, connects directly to Redis/ToonStore
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
- Make sure Redis is running:
45
+ **ToonStoreDB must be running:**
46
+
29
47
  ```bash
30
- redis-server
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
- ### Basic Usage
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
- // 1. Create client (connects directly to Redis)
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: 'redis://localhost:6379'
79
+ // url: 'toonstoredb://localhost:6379'
45
80
  });
46
81
 
47
- // 2. Define model with schema
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
- // 3. Create document
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
- // 4. Find documents
138
+ // Find all users
84
139
  const allUsers = await User.find();
85
- const specificUser = await User.findById('user:1');
86
140
 
87
- // 5. Query with filters
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
- .filter('age', 'gte', 18)
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
- // 6. Update document
95
- await User.update('user:1', { age: 31 });
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
- // 7. Delete document
98
- await User.delete('user:1');
182
+ ### Step 5: Launch TORM Studio (Optional)
99
183
 
100
- // 8. Count documents
101
- const count = await User.count();
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) - Redis host (default: `localhost`)
116
- - `port` (number) - Redis port (default: `6379`)
117
- - `url` (string) - Redis connection URL (e.g., `redis://localhost:6379`)
118
- - All standard `ioredis` options are supported
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 Redis connection health
124
- - `disconnect()` - Close Redis connection
125
- - `isConnected()` - Check if connected to Redis
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
- .filter(field, operator, value) // Add filter
154
- .where(field, value) // Shorthand for .filter(field, 'eq', value)
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
- .filter('status', 'eq', 'active')
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 adults
272
- const adults = await User.query()
273
- .filter('age', 'gte', 18)
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
- author_id: string;
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
- author_id: {
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 ToonStore...',
319
- author_id: 'user:1',
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 published = await Post.query()
326
- .filter('published', 'eq', true)
327
- .sort('created_at', 'desc')
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 authorPosts = await Post.query()
333
- .filter('author_id', 'eq', 'user:1')
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 Products
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 product
372
- await Product.create({
373
- id: 'product:1',
374
- name: 'Laptop',
375
- price: 999.99,
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
- .filter('stock', 'gt', 0)
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 expensive = await Product.query()
389
- .filter('price', 'gte', 1000)
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
- ## πŸ› οΈ Development
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
- ## πŸ™ Credits
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
- - Inspired by [Mongoose](https://mongoosejs.com/)
426
- - Built for [ToonStore](https://github.com/toon-format/toon)
1157
+ For questions and support, please open an issue on GitHub.