nanodb-orm 0.0.4 → 0.0.6

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.
Files changed (46) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +438 -40
  3. package/dist/cli.d.ts +84 -1
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +227 -149
  6. package/dist/cli.js.map +1 -1
  7. package/dist/core/connection.d.ts +6 -0
  8. package/dist/core/connection.d.ts.map +1 -1
  9. package/dist/core/connection.js +17 -3
  10. package/dist/core/connection.js.map +1 -1
  11. package/dist/index.d.ts +206 -13
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +222 -50
  14. package/dist/index.js.map +1 -1
  15. package/dist/init.d.ts +114 -62
  16. package/dist/init.d.ts.map +1 -1
  17. package/dist/init.js +79 -44
  18. package/dist/init.js.map +1 -1
  19. package/dist/types/index.d.ts +176 -38
  20. package/dist/types/index.d.ts.map +1 -1
  21. package/dist/types/index.js +3 -0
  22. package/dist/types/index.js.map +1 -1
  23. package/dist/utils/index.d.ts +1 -1
  24. package/dist/utils/index.d.ts.map +1 -1
  25. package/dist/utils/index.js +2 -1
  26. package/dist/utils/index.js.map +1 -1
  27. package/dist/utils/migrations.d.ts +2 -2
  28. package/dist/utils/migrations.d.ts.map +1 -1
  29. package/dist/utils/migrations.js +167 -56
  30. package/dist/utils/migrations.js.map +1 -1
  31. package/dist/utils/schema-introspection.d.ts +4 -4
  32. package/dist/utils/schema-introspection.d.ts.map +1 -1
  33. package/dist/utils/schema-introspection.js +0 -3
  34. package/dist/utils/schema-introspection.js.map +1 -1
  35. package/dist/utils/seeds.d.ts +2 -2
  36. package/dist/utils/seeds.d.ts.map +1 -1
  37. package/dist/utils/seeds.js +10 -1
  38. package/dist/utils/seeds.js.map +1 -1
  39. package/dist/utils/sync.d.ts.map +1 -1
  40. package/dist/utils/sync.js +11 -2
  41. package/dist/utils/sync.js.map +1 -1
  42. package/dist/utils/transactions.d.ts +85 -2
  43. package/dist/utils/transactions.d.ts.map +1 -1
  44. package/dist/utils/transactions.js +160 -8
  45. package/dist/utils/transactions.js.map +1 -1
  46. package/package.json +2 -2
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 Damilola Alao
3
+ Copyright (c) 2024 Easy-Deploy-Dev
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,15 +1,15 @@
1
1
  # nanodb-orm
2
2
 
3
- A lightweight, schema-agnostic ORM wrapper for Drizzle ORM with automatic migrations, schema introspection, CLI tools, and support for SQLite/Turso databases.
3
+ A lightweight ORM wrapper for Drizzle ORM with automatic migrations, schema introspection, CLI tools, and support for SQLite/Turso databases.
4
4
 
5
5
  ## Features
6
6
 
7
+ - **TypeScript First** — Full type inference from schema to queries
7
8
  - **Auto-Migrations** — Automatically creates and updates database schemas from Drizzle tables
8
9
  - **Schema Introspection** — Comprehensive schema analysis and validation
9
10
  - **Multi-Database** — Works with local SQLite and remote Turso databases
10
11
  - **Transactions** — Full transaction support with automatic rollback
11
12
  - **CLI Tools** — Built-in commands including Drizzle Studio integration
12
- - **Type Safe** — Full TypeScript support with proper type inference
13
13
  - **Plugin System** — Extensible with hooks for audit, validation, transformations
14
14
  - **Minimal** — ~1K lines of code, zero bloat
15
15
 
@@ -54,65 +54,136 @@ npx nanodb studio
54
54
 
55
55
  ![Drizzle Studio](https://orm.drizzle.team/images/drizzle-studio.png)
56
56
 
57
+ ## Import Styles
58
+
59
+ ```typescript
60
+ // Default import (recommended)
61
+ import nanodb from 'nanodb-orm';
62
+
63
+ const users = nanodb.schema.table('users', { ... });
64
+ const db = await nanodb.createDatabase({ tables: { users } });
65
+ await db.select().from(users).where(nanodb.query.eq(users.id, 1));
66
+ ```
67
+
68
+ ```typescript
69
+ // Named imports
70
+ import { createDatabase, schema, query } from 'nanodb-orm';
71
+ ```
72
+
73
+ ```typescript
74
+ // Individual imports (tree-shakeable)
75
+ import { createDatabase, table, integer, text, eq } from 'nanodb-orm';
76
+ ```
77
+
57
78
  ## Quick Start
58
79
 
59
80
  ### 1. Define Your Schema
60
81
 
61
82
  ```typescript
62
- import { table, integer, text } from 'nanodb-orm';
83
+ import nanodb from 'nanodb-orm';
63
84
 
64
- const users = table('users', {
65
- id: integer('id').primaryKey({ autoIncrement: true }),
66
- name: text('name').notNull(),
67
- email: text('email').unique().notNull(),
68
- age: integer('age'),
85
+ const users = nanodb.schema.table('users', {
86
+ id: nanodb.schema.integer('id').primaryKey({ autoIncrement: true }),
87
+ name: nanodb.schema.text('name').notNull(),
88
+ email: nanodb.schema.text('email').unique().notNull(),
89
+ age: nanodb.schema.integer('age'),
69
90
  });
70
91
 
71
- const posts = table('posts', {
72
- id: integer('id').primaryKey({ autoIncrement: true }),
73
- title: text('title').notNull(),
74
- userId: integer('userId').notNull(),
92
+ const posts = nanodb.schema.table('posts', {
93
+ id: nanodb.schema.integer('id').primaryKey({ autoIncrement: true }),
94
+ title: nanodb.schema.text('title').notNull(),
95
+ userId: nanodb.schema.integer('userId').notNull(),
75
96
  });
76
-
77
- const tables = { users, posts };
78
97
  ```
79
98
 
80
99
  ### 2. Create Database
81
100
 
82
101
  ```typescript
83
- import { createDatabase } from 'nanodb-orm';
84
-
85
- // One line: creates tables, runs migrations, seeds data, returns db
86
- const db = await createDatabase({
87
- tables,
102
+ const db = await nanodb.createDatabase({
103
+ tables: { users, posts },
88
104
  seedData: {
89
105
  users: [{ name: 'Alice', email: 'alice@example.com', age: 28 }],
90
106
  },
91
107
  });
92
108
 
93
- // Store db and use it everywhere - no need for getInstance() or getDb()
94
109
  export { db };
95
110
  ```
96
111
 
97
- ### 3. Query with Type Safety
112
+ ### 3. Query Your Data
98
113
 
99
114
  ```typescript
100
- import { eq, gte } from 'nanodb-orm';
101
-
102
115
  // SELECT
103
116
  const allUsers = await db.select().from(users);
104
- const adults = await db.select().from(users).where(gte(users.age, 18));
117
+ const adults = await db.select().from(users).where(nanodb.query.gte(users.age, 18));
105
118
 
106
119
  // INSERT
107
120
  await db.insert(users).values({ name: 'Bob', email: 'bob@example.com' });
108
121
 
109
122
  // UPDATE
110
- await db.update(users).set({ name: 'Robert' }).where(eq(users.email, 'bob@example.com'));
123
+ await db.update(users).set({ name: 'Robert' }).where(nanodb.query.eq(users.email, 'bob@example.com'));
111
124
 
112
125
  // DELETE
113
- await db.delete(users).where(eq(users.email, 'bob@example.com'));
126
+ await db.delete(users).where(nanodb.query.eq(users.email, 'bob@example.com'));
127
+ ```
128
+
129
+ ## Type Inference
130
+
131
+ nanodb-orm provides full type inference from your schema:
132
+
133
+ ```typescript
134
+ import {
135
+ createDatabase,
136
+ table,
137
+ integer,
138
+ text,
139
+ type SelectModel,
140
+ type InsertModel,
141
+ } from 'nanodb-orm';
142
+
143
+ const users = table('users', {
144
+ id: integer('id').primaryKey({ autoIncrement: true }),
145
+ name: text('name').notNull(),
146
+ email: text('email').notNull(),
147
+ age: integer('age'),
148
+ });
149
+
150
+ // Infer types directly from your table definitions
151
+ type User = SelectModel<typeof users>;
152
+ // { id: number; name: string; email: string; age: number | null }
153
+
154
+ type NewUser = InsertModel<typeof users>;
155
+ // { id?: number; name: string; email: string; age?: number | null }
156
+
157
+ // The database is fully typed
158
+ const db = await createDatabase({ tables: { users } });
159
+
160
+ // All operations are type-safe
161
+ const allUsers: User[] = await db.select().from(users);
162
+
163
+ // Seed data is type-checked at compile time
164
+ const db2 = await createDatabase({
165
+ tables: { users },
166
+ seedData: {
167
+ users: [
168
+ { name: 'Alice', email: 'alice@example.com' }, // ✓ Valid
169
+ // { name: 123 }, // ✗ TypeScript error!
170
+ ],
171
+ },
172
+ });
114
173
  ```
115
174
 
175
+ ### Available Type Utilities
176
+
177
+ | Type | Description |
178
+ |------|-------------|
179
+ | `SelectModel<T>` | Infer the row type (SELECT result) from a table |
180
+ | `InsertModel<T>` | Infer the insert type from a table (optional auto-generated columns) |
181
+ | `SchemaModels<S>` | Extract all row types from a schema object |
182
+ | `SchemaInsertModels<S>` | Extract all insert types from a schema |
183
+ | `NanoDatabase<S>` | The typed database instance |
184
+ | `Schema` | Type for schema objects |
185
+ | `AnyTable` | Type constraint for Drizzle tables |
186
+
116
187
  ## API Reference
117
188
 
118
189
  ### `createDatabase(config)`
@@ -122,7 +193,7 @@ Create and initialize database. Returns `db` with all utilities attached.
122
193
  ```typescript
123
194
  const db = await createDatabase({
124
195
  tables: { users, posts },
125
- seedData: { users: [...] },
196
+ seedData: { users: [...] }, // Type-checked against schema
126
197
  migrationConfig: {
127
198
  preserveData: true, // default: true
128
199
  autoMigrate: true, // default: true
@@ -149,7 +220,7 @@ await db.clearData(); // Delete all data (keep tables)
149
220
  ### Schema Introspection (from `db.schema`)
150
221
 
151
222
  ```typescript
152
- db.schema.tables(); // ['users', 'posts']
223
+ db.schema.tables(); // ['users', 'posts'] - typed as (keyof Schema)[]
153
224
  db.schema.getTable('users'); // { columns, primaryKey, indexes }
154
225
  db.schema.getColumns('users'); // ['id', 'name', 'email']
155
226
  await db.schema.validate(); // { isValid, missingTables, ... }
@@ -165,24 +236,89 @@ await db.migrations.validate(); // Validate schema vs DB
165
236
  await db.migrations.checkTables(); // { users: true, posts: true }
166
237
  ```
167
238
 
168
- ### `transaction(fn)`
239
+ ### Data-Preserving Auto Migrations
240
+
241
+ nanodb-orm automatically migrates your schema while **preserving existing data**. When you change your schema (add/remove columns, change types), the migration:
169
242
 
170
- Execute operations atomically.
243
+ 1. Creates a temporary table with the new schema
244
+ 2. Copies data from matching columns
245
+ 3. Drops the old table
246
+ 4. Renames the temp table
171
247
 
172
248
  ```typescript
173
- import { transaction, sql } from 'nanodb-orm';
249
+ const db = await createDatabase({
250
+ tables: { users, posts },
251
+ migrationConfig: {
252
+ autoMigrate: true, // Enable automatic migrations (default: true)
253
+ preserveData: true, // Preserve existing data during migration (default: true)
254
+ dropTables: false, // Allow destructive drop & recreate (default: false)
255
+ },
256
+ });
257
+ ```
258
+
259
+ #### Migration Config Options
260
+
261
+ | Option | Default | Description |
262
+ |--------|---------|-------------|
263
+ | `autoMigrate` | `true` | Master switch - enables/disables automatic schema changes |
264
+ | `preserveData` | `true` | Copy existing data to new schema during migration |
265
+ | `dropTables` | `false` | Allow destructive operations (bypasses data preservation) |
266
+
267
+ #### How Column Changes Work
268
+
269
+ ```
270
+ Adding a column:
271
+ OLD: (id, name) → NEW: (id, name, email)
272
+ ✅ All rows preserved, 'email' starts as NULL/default
273
+
274
+ Removing a column:
275
+ OLD: (id, name, oldField) → NEW: (id, name)
276
+ ⚠️ Rows preserved, but 'oldField' data is LOST
277
+
278
+ Renaming a column:
279
+ OLD: (id, username) → NEW: (id, name)
280
+ ⚠️ Treated as remove + add, 'username' data is LOST
281
+ ```
282
+
283
+ #### Manual Migration
284
+
285
+ ```typescript
286
+ import { migrateTablePreservingData } from 'nanodb-orm';
287
+
288
+ const result = await migrateTablePreservingData(
289
+ 'users',
290
+ 'CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)',
291
+ ['id', 'name'], // old columns
292
+ ['id', 'name', 'email'] // new columns
293
+ );
294
+ // { rowsMigrated: 150, columnsPreserved: ['id', 'name'] }
295
+ ```
174
296
 
175
- const result = await transaction(async (tx) => {
176
- await tx.run(sql`INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com')`);
177
- await tx.run(sql`INSERT INTO posts (title, userId) VALUES ('Hello', 1)`);
297
+ ### `transaction(fn)` / `batch(statements)`
298
+
299
+ Execute operations atomically. Uses Drizzle's native transaction when available (better for Turso).
300
+
301
+ ```typescript
302
+ import nanodb from 'nanodb-orm';
303
+
304
+ // Transaction with custom logic
305
+ const result = await nanodb.transaction(async (tx) => {
306
+ await tx.run(nanodb.query.sql`INSERT INTO users (name) VALUES ('Alice')`);
307
+ await tx.run(nanodb.query.sql`INSERT INTO posts (title, userId) VALUES ('Hello', 1)`);
178
308
  return { created: true };
179
309
  });
180
310
 
181
311
  if (result.success) {
182
- console.log(result.result);
312
+ console.log(result.result); // { created: true }
183
313
  } else {
184
314
  console.log('Rolled back:', result.error?.message);
185
315
  }
316
+
317
+ // Batch multiple statements (simpler for bulk operations)
318
+ const batchResult = await nanodb.batch([
319
+ nanodb.query.sql`INSERT INTO users (name) VALUES ('Bob')`,
320
+ nanodb.query.sql`INSERT INTO users (name) VALUES ('Carol')`,
321
+ ]);
186
322
  ```
187
323
 
188
324
  ### `parseDbError(error, context)`
@@ -193,10 +329,10 @@ Parse SQLite errors into user-friendly messages.
193
329
  import { parseDbError } from 'nanodb-orm';
194
330
 
195
331
  try {
196
- await db.run('INSERT INTO users ...');
332
+ await db.insert(users).values({ email: 'duplicate@example.com' });
197
333
  } catch (error) {
198
334
  const parsed = parseDbError(error, { table: 'users', operation: 'insert' });
199
- console.log(parsed.message); // "Column 'email' does not exist"
335
+ console.log(parsed.message); // "Duplicate value for unique column 'email'"
200
336
  }
201
337
  ```
202
338
 
@@ -282,6 +418,192 @@ await db.insert(users).values({ email: 'invalid' }); // throws error
282
418
  db.plugins.list(); // ['audit', 'slug', 'validation']
283
419
  ```
284
420
 
421
+ ## Best Practices
422
+
423
+ ### Recommended Project Structure
424
+
425
+ ```
426
+ db/
427
+ ├── schema.ts # Table definitions
428
+ ├── index.ts # Database instance export
429
+ ├── types.ts # Type aliases (SelectModel, InsertModel)
430
+ ├── plugins.ts # Custom plugins
431
+ └── seeds.ts # Seed data
432
+ ```
433
+
434
+ ### 1. Schema Order Matters
435
+
436
+ Define parent tables before children for correct seeding order:
437
+
438
+ ```typescript
439
+ // db/schema.ts
440
+ import nanodb from 'nanodb-orm';
441
+
442
+ // Parent tables first (no foreign keys)
443
+ export const users = nanodb.schema.table('users', {
444
+ id: nanodb.schema.integer('id').primaryKey({ autoIncrement: true }),
445
+ name: nanodb.schema.text('name').notNull(),
446
+ email: nanodb.schema.text('email').notNull().unique(),
447
+ });
448
+
449
+ export const categories = nanodb.schema.table('categories', {
450
+ id: nanodb.schema.integer('id').primaryKey({ autoIncrement: true }),
451
+ name: nanodb.schema.text('name').notNull(),
452
+ });
453
+
454
+ // Child tables after (have foreign keys)
455
+ export const posts = nanodb.schema.table('posts', {
456
+ id: nanodb.schema.integer('id').primaryKey({ autoIncrement: true }),
457
+ title: nanodb.schema.text('title').notNull(),
458
+ userId: nanodb.schema.integer('userId').notNull(), // FK to users
459
+ categoryId: nanodb.schema.integer('categoryId'), // FK to categories
460
+ });
461
+
462
+ // Order: parents → children
463
+ export const schema = { users, categories, posts };
464
+ ```
465
+
466
+ ### 2. Single Database Instance
467
+
468
+ ```typescript
469
+ // db/index.ts
470
+ import nanodb from 'nanodb-orm';
471
+ import { schema } from './schema';
472
+ import { seedData } from './seeds';
473
+
474
+ export const db = await nanodb.createDatabase({ tables: schema, seedData });
475
+ ```
476
+
477
+ ```typescript
478
+ // anywhere.ts
479
+ import { db } from './db';
480
+ import { users } from './db/schema';
481
+
482
+ const allUsers = await db.select().from(users);
483
+ ```
484
+
485
+ ### 3. Use Type Inference
486
+
487
+ ```typescript
488
+ // db/types.ts
489
+ import { type SelectModel, type InsertModel } from 'nanodb-orm';
490
+ import { users, posts } from './schema';
491
+
492
+ export type User = SelectModel<typeof users>;
493
+ export type NewUser = InsertModel<typeof users>;
494
+ export type Post = SelectModel<typeof posts>;
495
+
496
+ // Usage
497
+ async function createUser(data: NewUser): Promise<void> {
498
+ await db.insert(users).values(data); // TypeScript enforces shape
499
+ }
500
+ ```
501
+
502
+ ### 4. Prefer Grouped Imports
503
+
504
+ ```typescript
505
+ // ✅ Clean - default import
506
+ import nanodb from 'nanodb-orm';
507
+
508
+ const users = nanodb.schema.table('users', { ... });
509
+ await db.select().from(users).where(nanodb.query.eq(users.id, 1));
510
+
511
+ // ✅ Also good - grouped imports
512
+ import { schema, query, errors } from 'nanodb-orm';
513
+
514
+ // ❌ Avoid - many individual imports
515
+ import { table, integer, text, eq, gte, and, sql, count, ... } from 'nanodb-orm';
516
+ ```
517
+
518
+ ### 5. Handle Errors Gracefully
519
+
520
+ ```typescript
521
+ import { parseDbError, DatabaseError } from 'nanodb-orm';
522
+
523
+ try {
524
+ await db.insert(users).values({ email: 'duplicate@test.com' });
525
+ } catch (error) {
526
+ if (error instanceof DatabaseError) {
527
+ // Already formatted with context
528
+ console.log(error.message); // "UNIQUE constraint failed: users.email"
529
+ console.log(error.table); // "users"
530
+ } else {
531
+ const parsed = parseDbError(error, { table: 'users' });
532
+ console.log(parsed.format());
533
+ }
534
+ }
535
+ ```
536
+
537
+ ### 6. Use Transactions for Atomic Operations
538
+
539
+ ```typescript
540
+ import nanodb from 'nanodb-orm';
541
+
542
+ const result = await nanodb.transaction(async (tx) => {
543
+ await tx.run(nanodb.query.sql`INSERT INTO users (name) VALUES ('Alice')`);
544
+ await tx.run(nanodb.query.sql`INSERT INTO posts (title, userId) VALUES ('Hello', 1)`);
545
+ return { inserted: 2 };
546
+ });
547
+
548
+ if (!result.success) {
549
+ console.log('Rolled back:', result.error?.message);
550
+ }
551
+ ```
552
+
553
+ ### 7. Validate on Startup (Production)
554
+
555
+ ```typescript
556
+ import nanodb from 'nanodb-orm';
557
+
558
+ const db = await nanodb.createDatabase({ tables: schema });
559
+
560
+ if (process.env.NODE_ENV === 'production') {
561
+ const validation = await db.schema.validate();
562
+ if (!validation.isValid) {
563
+ throw new Error(`Schema mismatch: ${validation.missingTables.join(', ')}`);
564
+ }
565
+
566
+ const health = await db.healthCheck();
567
+ if (!health.healthy) {
568
+ console.warn('Database issues:', health.errors);
569
+ }
570
+ }
571
+ ```
572
+
573
+ ### 8. Keep Plugins Simple
574
+
575
+ ```typescript
576
+ // Good: focused, single responsibility
577
+ const timestampPlugin: NanoPlugin = {
578
+ name: 'timestamps',
579
+ beforeInsert: (_table, data) => ({
580
+ ...data,
581
+ createdAt: new Date().toISOString(),
582
+ }),
583
+ beforeUpdate: (_table, data) => ({
584
+ ...data,
585
+ updatedAt: new Date().toISOString(),
586
+ }),
587
+ };
588
+
589
+ // Avoid: complex business logic in hooks
590
+ ```
591
+
592
+ ### 9. Environment Configuration
593
+
594
+ ```bash
595
+ # .env
596
+ TURSO_CONNECTION_URL=libsql://your-db.turso.io
597
+ TURSO_AUTH_TOKEN=your-token
598
+ FORCE_LOCAL_DB=true # Use local SQLite
599
+ DATABASE_PATH=./data/app.db # Custom DB path
600
+ ```
601
+
602
+ nanodb-orm auto-detects the right database:
603
+ - **Turso**: when `TURSO_*` vars are set
604
+ - **Local**: when `FORCE_LOCAL_DB=true` or no Turso config
605
+ - **Test**: isolated `test.db` when `NODE_ENV=test`
606
+
285
607
  ## Configuration
286
608
 
287
609
  ### Environment Variables
@@ -291,14 +613,18 @@ db.plugins.list(); // ['audit', 'slug', 'validation']
291
613
  TURSO_CONNECTION_URL=libsql://your-db.turso.io
292
614
  TURSO_AUTH_TOKEN=your-token
293
615
 
294
- # Force local SQLite (for testing)
616
+ # Force local SQLite
295
617
  FORCE_LOCAL_DB=true
618
+
619
+ # Custom database path (works with FORCE_LOCAL_DB or as fallback)
620
+ DATABASE_PATH=./data/myapp.db
296
621
  ```
297
622
 
298
623
  ### Database Selection
299
624
 
300
625
  - **Turso** — Used when `TURSO_CONNECTION_URL` and `TURSO_AUTH_TOKEN` are set
301
- - **Local SQLite** — Fallback when Turso credentials are missing
626
+ - **Local SQLite** — Used when `FORCE_LOCAL_DB=true` or Turso credentials missing
627
+ - **Custom Path** — Set `DATABASE_PATH=./path/to/db.sqlite` for custom location
302
628
  - **Test Mode** — Isolated `test.db` when `NODE_ENV=test`
303
629
 
304
630
  ## Error Handling
@@ -331,6 +657,78 @@ Error output is clean and actionable:
331
657
  └────────────────────────────────────────────────
332
658
  ```
333
659
 
660
+ ## Exports
661
+
662
+ ```typescript
663
+ // Default export (recommended)
664
+ import nanodb from 'nanodb-orm';
665
+
666
+ nanodb.createDatabase // Main entry point
667
+ nanodb.transaction // Atomic operations
668
+ nanodb.schema // .table, .integer, .text, .real, .blob
669
+ nanodb.query // .eq, .gte, .and, .or, .sql, .count, ...
670
+ nanodb.errors // .DatabaseError, .parse
671
+ nanodb.cli // .studio, .setup, .reset, .status, .validate
672
+
673
+ // Types (named imports)
674
+ import { type SelectModel, type InsertModel, type NanoPlugin } from 'nanodb-orm';
675
+ ```
676
+
677
+ ## nanodb-orm vs Drizzle + Turso (Direct)
678
+
679
+ | Feature | nanodb-orm | Drizzle + Turso |
680
+ |---------|------------|-----------------|
681
+ | **Setup** | One-liner: `createDatabase({ tables })` | Manual: create client, drizzle, manage connection |
682
+ | **Migrations** | Automatic on startup | Manual: `drizzle-kit push/migrate` |
683
+ | **Seeding** | Built-in with `seedData` | Write seed scripts |
684
+ | **Type Safety** | ✅ Full (same as Drizzle) | ✅ Full |
685
+ | **Query API** | ✅ Same as Drizzle | ✅ Native Drizzle |
686
+ | **Plugins/Hooks** | ✅ beforeInsert, afterQuery, etc. | ❌ None |
687
+ | **Schema Introspection** | ✅ `db.schema.tables()` | ❌ Manual |
688
+ | **Health Checks** | ✅ `db.healthCheck()` | ❌ Manual |
689
+ | **CLI** | `npx nanodb studio/status/validate` | `npx drizzle-kit studio` only |
690
+ | **Error Parsing** | User-friendly messages | Raw SQLite errors |
691
+ | **Connection** | Auto-detects Turso vs local | Manual configuration |
692
+
693
+ ### When to Use What
694
+
695
+ | Use Case | Recommendation |
696
+ |----------|----------------|
697
+ | Quick prototyping | **nanodb-orm** |
698
+ | Need plugins/hooks | **nanodb-orm** |
699
+ | Want auto-migrations | **nanodb-orm** |
700
+ | New SQLite/Turso project | **nanodb-orm** |
701
+ | Maximum control | Drizzle directly |
702
+ | Complex migration strategies | Drizzle + drizzle-kit |
703
+ | Existing Drizzle project | Keep Drizzle |
704
+
705
+ ### Comparison
706
+
707
+ ```typescript
708
+ // nanodb-orm: 5 lines
709
+ import nanodb from 'nanodb-orm';
710
+
711
+ const users = nanodb.schema.table('users', { id: nanodb.schema.integer('id').primaryKey() });
712
+ const db = await nanodb.createDatabase({ tables: { users }, seedData: { users: [{ id: 1 }] } });
713
+ // Ready - tables created, seeded
714
+ ```
715
+
716
+ ```typescript
717
+ // Drizzle + Turso: More setup
718
+ import { drizzle } from 'drizzle-orm/libsql';
719
+ import { createClient } from '@libsql/client';
720
+ import { sqliteTable, integer } from 'drizzle-orm/sqlite-core';
721
+ import { migrate } from 'drizzle-orm/libsql/migrator';
722
+
723
+ const users = sqliteTable('users', { id: integer('id').primaryKey() });
724
+ const client = createClient({ url: process.env.TURSO_CONNECTION_URL!, authToken: process.env.TURSO_AUTH_TOKEN! });
725
+ const db = drizzle(client);
726
+ await migrate(db, { migrationsFolder: './drizzle' });
727
+ await db.insert(users).values([{ id: 1 }]);
728
+ ```
729
+
730
+ **nanodb-orm is a convenience layer** — it uses Drizzle under the hood and passes through all queries unchanged. You get Drizzle's full type safety plus automatic setup, plugins, and utilities.
731
+
334
732
  ## License
335
733
 
336
- MIT © Damilola Alao
734
+ MIT © Easy-Deploy-Dev
package/dist/cli.d.ts CHANGED
@@ -9,5 +9,88 @@
9
9
  * status - Show database health and stats
10
10
  * validate - Validate schema against database
11
11
  */
12
- export {};
12
+ import { type ChildProcess } from 'child_process';
13
+ export interface StudioOptions {
14
+ /** Database file path (default: auto-detected or 'database.db') */
15
+ dbPath?: string | undefined;
16
+ /** Port for Drizzle Studio (default: 4983) */
17
+ port?: number | undefined;
18
+ /** Suppress console output */
19
+ silent?: boolean | undefined;
20
+ }
21
+ export interface StatusResult {
22
+ healthy: boolean;
23
+ schemaValid: boolean;
24
+ totalTables: number;
25
+ totalRecords: number;
26
+ tables: Record<string, number>;
27
+ errors: string[];
28
+ }
29
+ export interface ValidationResult {
30
+ isValid: boolean;
31
+ missingTables: string[];
32
+ extraTables: string[];
33
+ errors: string[];
34
+ }
35
+ /**
36
+ * Launch Drizzle Studio for visual database browsing.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * import { launchStudio } from 'nanodb-orm';
41
+ *
42
+ * // Launch with defaults
43
+ * await launchStudio();
44
+ *
45
+ * // Launch with options
46
+ * await launchStudio({ dbPath: './data/app.db', port: 3000 });
47
+ * ```
48
+ */
49
+ export declare function launchStudio(options?: StudioOptions): Promise<ChildProcess>;
50
+ /**
51
+ * Run database setup (initialize schema and seed data).
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * import { runSetup } from 'nanodb-orm';
56
+ * await runSetup();
57
+ * ```
58
+ */
59
+ export declare function runSetup(silent?: boolean): Promise<void>;
60
+ /**
61
+ * Reset database (drop all tables and recreate with seed data).
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * import { runReset } from 'nanodb-orm';
66
+ * await runReset();
67
+ * ```
68
+ */
69
+ export declare function runReset(silent?: boolean): Promise<void>;
70
+ /**
71
+ * Get database status and health information.
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * import { getStatus } from 'nanodb-orm';
76
+ *
77
+ * const status = await getStatus();
78
+ * console.log(status.healthy, status.totalRecords);
79
+ * ```
80
+ */
81
+ export declare function getStatus(silent?: boolean): Promise<StatusResult>;
82
+ /**
83
+ * Validate schema against database.
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * import { runValidate } from 'nanodb-orm';
88
+ *
89
+ * const result = await runValidate();
90
+ * if (!result.isValid) {
91
+ * console.log('Missing tables:', result.missingTables);
92
+ * }
93
+ * ```
94
+ */
95
+ export declare function runValidate(silent?: boolean): Promise<ValidationResult>;
13
96
  //# sourceMappingURL=cli.d.ts.map
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG;AAEH,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAQzD,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,8BAA8B;IAC9B,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,YAAY,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CA8CrF;AAED;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAAC,MAAM,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAO5D;AAED;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAAC,MAAM,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAO5D;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,SAAS,CAAC,MAAM,UAAQ,GAAG,OAAO,CAAC,YAAY,CAAC,CAsCrE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,CAAC,MAAM,UAAQ,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAsC3E"}