nanodb-orm 0.0.3 → 0.0.4

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 (87) hide show
  1. package/README.md +211 -378
  2. package/dist/cli.d.ts +13 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +270 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/constants/index.d.ts +7 -54
  7. package/dist/constants/index.d.ts.map +1 -1
  8. package/dist/constants/index.js +9 -61
  9. package/dist/constants/index.js.map +1 -1
  10. package/dist/core/config.d.ts +3 -13
  11. package/dist/core/config.d.ts.map +1 -1
  12. package/dist/core/config.js +5 -27
  13. package/dist/core/config.js.map +1 -1
  14. package/dist/core/connection.d.ts +10 -31
  15. package/dist/core/connection.d.ts.map +1 -1
  16. package/dist/core/connection.js +28 -78
  17. package/dist/core/connection.js.map +1 -1
  18. package/dist/core/index.d.ts +2 -3
  19. package/dist/core/index.d.ts.map +1 -1
  20. package/dist/core/index.js +3 -18
  21. package/dist/core/index.js.map +1 -1
  22. package/dist/index.d.ts +40 -10
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +76 -31
  25. package/dist/index.js.map +1 -1
  26. package/dist/init.d.ts +79 -22
  27. package/dist/init.d.ts.map +1 -1
  28. package/dist/init.js +246 -48
  29. package/dist/init.js.map +1 -1
  30. package/dist/jest.setup.d.ts +4 -0
  31. package/dist/jest.setup.d.ts.map +1 -0
  32. package/dist/jest.setup.js +40 -0
  33. package/dist/jest.setup.js.map +1 -0
  34. package/dist/types/errors.d.ts +30 -12
  35. package/dist/types/errors.d.ts.map +1 -1
  36. package/dist/types/errors.js +98 -23
  37. package/dist/types/errors.js.map +1 -1
  38. package/dist/types/index.d.ts +130 -4
  39. package/dist/types/index.d.ts.map +1 -1
  40. package/dist/types/index.js +2 -4
  41. package/dist/types/index.js.map +1 -1
  42. package/dist/utils/error-handler.d.ts +6 -31
  43. package/dist/utils/error-handler.d.ts.map +1 -1
  44. package/dist/utils/error-handler.js +24 -81
  45. package/dist/utils/error-handler.js.map +1 -1
  46. package/dist/utils/index.d.ts +1 -3
  47. package/dist/utils/index.d.ts.map +1 -1
  48. package/dist/utils/index.js +4 -6
  49. package/dist/utils/index.js.map +1 -1
  50. package/dist/utils/logger.d.ts +6 -25
  51. package/dist/utils/logger.d.ts.map +1 -1
  52. package/dist/utils/logger.js +20 -38
  53. package/dist/utils/logger.js.map +1 -1
  54. package/dist/utils/migrations.d.ts +16 -90
  55. package/dist/utils/migrations.d.ts.map +1 -1
  56. package/dist/utils/migrations.js +216 -423
  57. package/dist/utils/migrations.js.map +1 -1
  58. package/dist/utils/schema-introspection.d.ts +30 -169
  59. package/dist/utils/schema-introspection.d.ts.map +1 -1
  60. package/dist/utils/schema-introspection.js +128 -462
  61. package/dist/utils/schema-introspection.js.map +1 -1
  62. package/dist/utils/seeds.d.ts +15 -48
  63. package/dist/utils/seeds.d.ts.map +1 -1
  64. package/dist/utils/seeds.js +101 -188
  65. package/dist/utils/seeds.js.map +1 -1
  66. package/dist/utils/sync.d.ts +16 -41
  67. package/dist/utils/sync.d.ts.map +1 -1
  68. package/dist/utils/sync.js +69 -172
  69. package/dist/utils/sync.js.map +1 -1
  70. package/dist/utils/transactions.d.ts +8 -47
  71. package/dist/utils/transactions.d.ts.map +1 -1
  72. package/dist/utils/transactions.js +32 -147
  73. package/dist/utils/transactions.js.map +1 -1
  74. package/package.json +29 -10
  75. package/dist/example.d.ts +0 -67
  76. package/dist/example.d.ts.map +0 -1
  77. package/dist/example.js +0 -86
  78. package/dist/example.js.map +0 -1
  79. package/dist/types/database.d.ts +0 -74
  80. package/dist/types/database.d.ts.map +0 -1
  81. package/dist/types/database.js +0 -6
  82. package/dist/types/database.js.map +0 -1
  83. package/dist/types/types.d.ts +0 -30
  84. package/dist/types/types.d.ts.map +0 -1
  85. package/dist/types/types.js +0 -6
  86. package/dist/types/types.js.map +0 -1
  87. package/llm.txt +0 -336
package/README.md CHANGED
@@ -1,503 +1,336 @@
1
1
  # nanodb-orm
2
2
 
3
- A production-ready, generic database package built on top of Drizzle ORM with automatic migrations, schema introspection, atomic transactions, and support for both local SQLite and remote Turso databases.
3
+ A lightweight, schema-agnostic 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
- - 🚀 **Auto-Migrations**: Automatically creates and updates database schemas from Drizzle table definitions
8
- - 🔍 **Schema Introspection**: Comprehensive schema analysis and validation
9
- - 🌐 **Multi-Database Support**: Works with local SQLite and remote Turso databases
10
- - **Atomic Transactions**: Full transaction support with rollback protection
11
- - 🛡️ **Type Safe**: Full TypeScript support with proper type inference
12
- - 🔒 **Security**: SQL injection protection and input validation
13
- - 🧵 **Thread Safe**: Race condition fixes and concurrent access protection
14
- - 📦 **NPM Package Ready**: Designed to be used as a standalone npm package
15
- - ⚙️ **Configurable**: Flexible migration and seeding options
16
- - 🧪 **Test Ready**: Built-in testing utilities and isolation
17
- - 🔧 **Production Ready**: Enhanced error handling and reliability features
7
+ - **Auto-Migrations** Automatically creates and updates database schemas from Drizzle tables
8
+ - **Schema Introspection** Comprehensive schema analysis and validation
9
+ - **Multi-Database** Works with local SQLite and remote Turso databases
10
+ - **Transactions** Full transaction support with automatic rollback
11
+ - **CLI Tools** Built-in commands including Drizzle Studio integration
12
+ - **Type Safe** Full TypeScript support with proper type inference
13
+ - **Plugin System** Extensible with hooks for audit, validation, transformations
14
+ - **Minimal** ~1K lines of code, zero bloat
18
15
 
19
16
  ## Installation
20
17
 
21
18
  ```bash
22
19
  npm install nanodb-orm
20
+
21
+ # For Drizzle Studio support (optional)
22
+ npm install drizzle-kit --save-dev
23
23
  ```
24
24
 
25
- ## Quick Start
25
+ ## CLI
26
26
 
27
- ### 1. Define Your Models with Drizzle
27
+ nanodb-orm includes a CLI for common database operations:
28
28
 
29
- Create your Drizzle table definitions (e.g., `models/users.ts`):
29
+ ```bash
30
+ # Launch Drizzle Studio (visual database browser)
31
+ npx nanodb studio
30
32
 
31
- ```typescript
32
- // models/users.ts
33
- import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
33
+ # With custom port
34
+ npx nanodb studio --port 3000
34
35
 
35
- export const usersTable = sqliteTable('users', {
36
- id: integer('id').primaryKey({ autoIncrement: true }),
37
- name: text('name').notNull(),
38
- age: integer('age').notNull(),
39
- email: text('email').unique().notNull(),
40
- });
36
+ # With specific database file
37
+ npx nanodb studio --db ./data/myapp.db
41
38
 
42
- export type InsertUser = typeof usersTable.$inferInsert;
43
- export type SelectUser = typeof usersTable.$inferSelect;
39
+ # Other commands
40
+ npx nanodb setup # Initialize schema and seed data
41
+ npx nanodb reset # Drop all tables and recreate
42
+ npx nanodb status # Show database health and stats
43
+ npx nanodb validate # Validate schema against database
44
+ npx nanodb help # Show all commands
44
45
  ```
45
46
 
46
- ```typescript
47
- // models/posts.ts
48
- import { sql } from 'drizzle-orm';
49
- import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
50
- import { usersTable } from './users';
47
+ ### Drizzle Studio
51
48
 
52
- export const postsTable = sqliteTable('posts', {
53
- id: integer('id').primaryKey({ autoIncrement: true }),
54
- title: text('title').notNull(),
55
- content: text('content').notNull(),
56
- userId: integer('user_id')
57
- .notNull()
58
- .references(() => usersTable.id, { onDelete: 'cascade' }),
59
- createdAt: text('created_at')
60
- .default(sql`(datetime('now'))`)
61
- .notNull(),
62
- updatedAt: text('updated_at').$onUpdate(() => new Date().toISOString()),
63
- });
49
+ Launch a visual database browser at `https://local.drizzle.studio`:
64
50
 
65
- export type InsertPost = typeof postsTable.$inferInsert;
66
- export type SelectPost = typeof postsTable.$inferSelect;
51
+ ```bash
52
+ npx nanodb studio
67
53
  ```
68
54
 
55
+ ![Drizzle Studio](https://orm.drizzle.team/images/drizzle-studio.png)
56
+
57
+ ## Quick Start
58
+
59
+ ### 1. Define Your Schema
60
+
69
61
  ```typescript
70
- // models/categories.ts
71
- import { sql } from 'drizzle-orm';
72
- import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
62
+ import { table, integer, text } from 'nanodb-orm';
73
63
 
74
- export const categoriesTable = sqliteTable('categories', {
64
+ const users = table('users', {
75
65
  id: integer('id').primaryKey({ autoIncrement: true }),
76
66
  name: text('name').notNull(),
77
- description: text('description'),
78
- color: text('color').notNull().default('#000000'),
79
- isActive: integer('is_active').notNull().default(1),
80
- createdAt: text('created_at')
81
- .default(sql`(datetime('now'))`)
82
- .notNull(),
67
+ email: text('email').unique().notNull(),
68
+ age: integer('age'),
83
69
  });
84
70
 
85
- export type InsertCategory = typeof categoriesTable.$inferInsert;
86
- export type SelectCategory = typeof categoriesTable.$inferSelect;
87
- ```
71
+ const posts = table('posts', {
72
+ id: integer('id').primaryKey({ autoIncrement: true }),
73
+ title: text('title').notNull(),
74
+ userId: integer('userId').notNull(),
75
+ });
88
76
 
89
- ```typescript
90
- // models/index.ts
91
- export * from './users';
92
- export * from './posts';
93
- export * from './categories';
94
-
95
- // Import tables for aggregation
96
- import { usersTable } from './users';
97
- import { postsTable } from './posts';
98
- import { categoriesTable } from './categories';
99
-
100
- // Export aggregated tables for nanodb-orm
101
- export const tables = {
102
- users: usersTable,
103
- posts: postsTable,
104
- categories: categoriesTable,
105
- } as const;
77
+ const tables = { users, posts };
106
78
  ```
107
79
 
108
- ### 2. Initialize and Setup Database
80
+ ### 2. Create Database
109
81
 
110
82
  ```typescript
111
- import { initializeDatabase, DatabaseSync } from 'nanodb-orm';
112
- import { tables } from './models';
83
+ import { createDatabase } from 'nanodb-orm';
113
84
 
114
- // Initialize the database package with your Drizzle tables
115
- initializeDatabase({
85
+ // One line: creates tables, runs migrations, seeds data, returns db
86
+ const db = await createDatabase({
116
87
  tables,
117
88
  seedData: {
118
- users: [
119
- { name: 'John Doe', age: 30, email: 'john@example.com' },
120
- { name: 'Jane Smith', age: 25, email: 'jane@example.com' }
121
- ],
122
- posts: [
123
- { title: 'Welcome Post', content: 'This is my first post!', userId: 1 },
124
- { title: 'Getting Started', content: 'Here are some tips...', userId: 2 }
125
- ],
126
- categories: [
127
- { name: 'Technology', description: 'Tech-related posts', color: '#3B82F6' },
128
- { name: 'Lifestyle', description: 'Life and personal posts', color: '#10B981' }
129
- ]
130
- }
89
+ users: [{ name: 'Alice', email: 'alice@example.com', age: 28 }],
90
+ },
131
91
  });
132
92
 
133
- // Setup database (creates tables, runs migrations, seeds data)
134
- await DatabaseSync.setup();
93
+ // Store db and use it everywhere - no need for getInstance() or getDb()
94
+ export { db };
135
95
  ```
136
96
 
137
- ### 3. Working with Drizzle Tables
138
-
139
- nanodb-orm works seamlessly with Drizzle ORM table definitions. Here are the key Drizzle column types and methods:
97
+ ### 3. Query with Type Safety
140
98
 
141
- #### Drizzle Column Types
142
- - `integer()` - Integer numbers
143
- - `text()` - Text strings
144
- - `real()` - Floating point numbers
145
- - `blob()` - Binary data
146
-
147
- #### Drizzle Column Methods
148
- - `.primaryKey({ autoIncrement: true })` - Primary key with auto-increment
149
- - `.notNull()` - NOT NULL constraint
150
- - `.unique()` - UNIQUE constraint
151
- - `.default(value)` - Default value
152
- - `.references(table.column)` - Foreign key reference
153
-
154
- #### Example Drizzle Column Definitions
155
99
  ```typescript
156
- // Primary key with auto-increment
157
- id: integer('id').primaryKey({ autoIncrement: true })
158
-
159
- // Required text field
160
- name: text('name').notNull()
100
+ import { eq, gte } from 'nanodb-orm';
161
101
 
162
- // Optional text field with default
163
- color: text('color').notNull().default('#000000')
102
+ // SELECT
103
+ const allUsers = await db.select().from(users);
104
+ const adults = await db.select().from(users).where(gte(users.age, 18));
164
105
 
165
- // Unique email field
166
- email: text('email').unique().notNull()
106
+ // INSERT
107
+ await db.insert(users).values({ name: 'Bob', email: 'bob@example.com' });
167
108
 
168
- // Boolean field (stored as integer)
169
- isActive: integer('is_active').notNull().default(1)
109
+ // UPDATE
110
+ await db.update(users).set({ name: 'Robert' }).where(eq(users.email, 'bob@example.com'));
170
111
 
171
- // Timestamp field with SQL function
172
- createdAt: text('created_at')
173
- .default(sql`(datetime('now'))`)
174
- .notNull()
175
-
176
- // Foreign key reference
177
- userId: integer('user_id')
178
- .notNull()
179
- .references(() => usersTable.id, { onDelete: 'cascade' })
112
+ // DELETE
113
+ await db.delete(users).where(eq(users.email, 'bob@example.com'));
180
114
  ```
181
115
 
182
116
  ## API Reference
183
117
 
184
- ### `initializeDatabase(schemaData: SchemaData)`
118
+ ### `createDatabase(config)`
185
119
 
186
- Initializes the database package with your schema data.
120
+ Create and initialize database. Returns `db` with all utilities attached.
187
121
 
188
122
  ```typescript
189
- interface SchemaData {
190
- tables: Record<string, any>; // Your Drizzle table definitions
191
- seedData?: Record<string, any[]>; // Optional seed data
192
- migrationConfig?: MigrationConfig; // Optional migration configuration
193
- }
194
-
195
- interface MigrationConfig {
196
- preserveData?: boolean; // Preserve existing data (default: true)
197
- autoMigrate?: boolean; // Enable auto-migrations (default: true)
198
- dropTables?: boolean; // Allow dropping tables (default: false)
199
- }
123
+ const db = await createDatabase({
124
+ tables: { users, posts },
125
+ seedData: { users: [...] },
126
+ migrationConfig: {
127
+ preserveData: true, // default: true
128
+ autoMigrate: true, // default: true
129
+ dropTables: false, // default: false
130
+ },
131
+ plugins: [auditPlugin, validationPlugin], // optional
132
+ });
200
133
  ```
201
134
 
202
- ### `DatabaseSync`
203
-
204
- Main database synchronization class.
135
+ ### Database Operations (from `db`)
205
136
 
206
137
  ```typescript
207
- // Setup database (create tables, migrate, seed)
208
- await DatabaseSync.setup();
209
-
210
- // Reset database (drop all tables and recreate)
211
- await DatabaseSync.reset();
212
-
213
- // Check if database is ready
214
- const isReady = await DatabaseSync.isReady();
138
+ // Health & Status
139
+ await db.healthCheck(); // { healthy, tables, totalRecords, ... }
140
+ await db.isReady(); // true/false
141
+ await db.sync(); // Sync with Turso (if remote)
142
+
143
+ // Reset & Seed
144
+ await db.reset(); // Drop all, recreate, reseed
145
+ await db.seed(); // Re-seed data
146
+ await db.clearData(); // Delete all data (keep tables)
215
147
  ```
216
148
 
217
- ### `SchemaIntrospection`
218
-
219
- Comprehensive schema analysis utilities.
149
+ ### Schema Introspection (from `db.schema`)
220
150
 
221
151
  ```typescript
222
- // Get all table names
223
- const tableNames = SchemaIntrospection.getAllTableNames();
224
-
225
- // Get table information
226
- const tableInfo = SchemaIntrospection.getTableInfo('users');
152
+ db.schema.tables(); // ['users', 'posts']
153
+ db.schema.getTable('users'); // { columns, primaryKey, indexes }
154
+ db.schema.getColumns('users'); // ['id', 'name', 'email']
155
+ await db.schema.validate(); // { isValid, missingTables, ... }
156
+ db.schema.stats(); // Full schema statistics
157
+ db.schema.relationships(); // Foreign key relationships
158
+ ```
227
159
 
228
- // Get schema statistics
229
- const stats = SchemaIntrospection.getSchemaStats();
160
+ ### Migrations (from `db.migrations`)
230
161
 
231
- // Validate schema
232
- const validation = await SchemaIntrospection.validateSchema();
162
+ ```typescript
163
+ await db.migrations.run(); // Run pending migrations
164
+ await db.migrations.validate(); // Validate schema vs DB
165
+ await db.migrations.checkTables(); // { users: true, posts: true }
233
166
  ```
234
167
 
235
- ### `DatabaseMigrations`
168
+ ### `transaction(fn)`
236
169
 
237
- Migration management utilities.
170
+ Execute operations atomically.
238
171
 
239
172
  ```typescript
240
- // Initialize schema
241
- await DatabaseMigrations.initializeSchema();
173
+ import { transaction, sql } from 'nanodb-orm';
242
174
 
243
- // Check table existence
244
- const existence = await DatabaseMigrations.checkTableExistence();
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)`);
178
+ return { created: true };
179
+ });
245
180
 
246
- // Validate schema
247
- const validation = await DatabaseMigrations.validateSchema();
181
+ if (result.success) {
182
+ console.log(result.result);
183
+ } else {
184
+ console.log('Rolled back:', result.error?.message);
185
+ }
248
186
  ```
249
187
 
250
- ### `DatabaseSeeds`
188
+ ### `parseDbError(error, context)`
251
189
 
252
- Database seeding utilities.
190
+ Parse SQLite errors into user-friendly messages.
253
191
 
254
192
  ```typescript
255
- // Seed database
256
- await DatabaseSeeds.seedDatabase();
193
+ import { parseDbError } from 'nanodb-orm';
257
194
 
258
- // Check if database has data
259
- const hasData = await DatabaseSeeds.hasData();
260
-
261
- // Clear all data
262
- await DatabaseSeeds.clearAllData();
195
+ try {
196
+ await db.run('INSERT INTO users ...');
197
+ } catch (error) {
198
+ const parsed = parseDbError(error, { table: 'users', operation: 'insert' });
199
+ console.log(parsed.message); // "Column 'email' does not exist"
200
+ }
263
201
  ```
264
202
 
265
- ## Configuration
266
-
267
- ### Environment Variables
268
-
269
- ```bash
270
- # Turso Database (optional)
271
- TURSO_CONNECTION_URL=libsql://your-database.turso.io
272
- TURSO_AUTH_TOKEN=your-auth-token
203
+ ## Plugins
273
204
 
274
- # Force local database (for testing)
275
- FORCE_LOCAL_DB=true
276
- NODE_ENV=test
277
- ```
205
+ Extend nanodb-orm with custom hooks that run automatically on database operations.
278
206
 
279
- ### Migration Configuration
207
+ ### Plugin Interface
280
208
 
281
209
  ```typescript
282
- const migrationConfig = {
283
- preserveData: true, // Always try to preserve existing data
284
- autoMigrate: true, // Automatically handle schema changes
285
- dropTables: false // Don't drop tables by default
210
+ import { NanoPlugin } from 'nanodb-orm';
211
+
212
+ const myPlugin: NanoPlugin = {
213
+ name: 'my-plugin',
214
+
215
+ // Lifecycle
216
+ install: (db) => db, // Modify db instance
217
+ onReady: (db) => {}, // Called after createDatabase
218
+ onError: (err, op, table) => {}, // Called on hook errors
219
+
220
+ // Auto hooks (run automatically)
221
+ beforeInsert: (table, data) => data, // Transform data before insert
222
+ afterInsert: (table, data, result) => {},
223
+ beforeUpdate: (table, data) => data, // Transform data before update
224
+ afterUpdate: (table, data, result) => {},
225
+ beforeDelete: (table, condition) => condition,
226
+ afterDelete: (table, condition, result) => {},
227
+
228
+ // Query hooks (also auto-triggered)
229
+ beforeQuery: (table, fields) => fields,
230
+ afterQuery: (table, fields, result) => {},
286
231
  };
287
-
288
- initializeDatabase({
289
- tables,
290
- migrationConfig
291
- });
292
232
  ```
293
233
 
294
- ## Usage Examples
234
+ ### Example Plugins
295
235
 
296
- ### Basic Setup
236
+ These are **example plugins you can create** - nanodb-orm provides the plugin system, you build the plugins:
297
237
 
298
238
  ```typescript
299
- import { initializeDatabase, DatabaseSync } from 'nanodb-orm';
300
- import { usersTable, postsTable } from './models';
301
-
302
- const tables = {
303
- users: usersTable,
304
- posts: postsTable
239
+ // Example: Audit logging with timing
240
+ const timers = new Map<string, number>();
241
+ const auditPlugin: NanoPlugin = {
242
+ name: 'audit',
243
+ beforeInsert: (table) => { timers.set('op', performance.now()); console.log(`INSERT ${table}`); },
244
+ afterInsert: () => { console.log(` ↳ ${(performance.now() - timers.get('op')!).toFixed(1)}ms`); },
245
+ beforeQuery: (table) => { timers.set('op', performance.now()); console.log(`SELECT ${table}`); },
246
+ afterQuery: (t, _, rows) => { console.log(` ↳ ${rows.length} rows in ${(performance.now() - timers.get('op')!).toFixed(1)}ms`); },
305
247
  };
306
248
 
307
- initializeDatabase({ tables });
308
- await DatabaseSync.setup();
309
- ```
310
-
311
- ### With Seed Data
312
-
313
- ```typescript
314
- const seedData = {
315
- users: [
316
- { name: 'Alice', age: 25, email: 'alice@example.com' },
317
- { name: 'Bob', age: 30, email: 'bob@example.com' }
318
- ],
319
- posts: [
320
- { title: 'Hello World', content: 'My first post', userId: 1 },
321
- { title: 'Second Post', content: 'Another post', userId: 2 }
322
- ]
249
+ // Auto-generate slugs
250
+ const slugPlugin: NanoPlugin = {
251
+ name: 'slug',
252
+ beforeInsert: (table, data) => {
253
+ if (table === 'posts' && data.title && !data.slug) {
254
+ return { ...data, slug: data.title.toLowerCase().replace(/\s+/g, '-') };
255
+ }
256
+ return data;
257
+ },
323
258
  };
324
259
 
325
- initializeDatabase({ tables, seedData });
326
- await DatabaseSync.setup();
327
- ```
328
-
329
- ### Custom Migration Config
330
-
331
- ```typescript
332
- const migrationConfig = {
333
- preserveData: false, // Allow data loss for development
334
- autoMigrate: true, // Enable auto-migrations
335
- dropTables: true // Allow dropping tables
260
+ // Validation
261
+ const validationPlugin: NanoPlugin = {
262
+ name: 'validation',
263
+ beforeInsert: (table, data) => {
264
+ if (table === 'users' && !data.email?.includes('@')) {
265
+ throw new Error('Invalid email format');
266
+ }
267
+ return data;
268
+ },
336
269
  };
337
270
 
338
- initializeDatabase({
339
- tables,
340
- migrationConfig
341
- });
342
- ```
343
-
344
- ### Transaction Support (NEW in v0.0.3)
345
-
346
- ```typescript
347
- import { TransactionManager } from 'nanodb-orm';
348
-
349
- // Atomic operations
350
- await TransactionManager.execute(async (db) => {
351
- await db.run('INSERT INTO users (name, email) VALUES (?, ?)', ['John', 'john@example.com']);
352
- await db.run('INSERT INTO posts (title, content, userId) VALUES (?, ?, ?)', ['Hello', 'World', 1]);
353
- // All operations succeed or all fail
271
+ // Use plugins
272
+ const db = await createDatabase({
273
+ tables,
274
+ plugins: [auditPlugin, slugPlugin, validationPlugin],
354
275
  });
355
276
 
356
- // Batch operations
357
- await TransactionManager.executeBatch([
358
- async (db) => await db.run('INSERT INTO users ...'),
359
- async (db) => await db.run('INSERT INTO posts ...'),
360
- async (db) => await db.run('UPDATE stats ...')
361
- ]);
277
+ // Hooks run automatically
278
+ await db.insert(posts).values({ title: 'My Post' }); // slug auto-generated
279
+ await db.insert(users).values({ email: 'invalid' }); // throws error
362
280
 
363
- // Table recreation with transactions
364
- await TransactionManager.recreateTable('users', async (db) => {
365
- await db.run('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
366
- });
281
+ // Check loaded plugins
282
+ db.plugins.list(); // ['audit', 'slug', 'validation']
367
283
  ```
368
284
 
369
- ### Enhanced Error Handling (NEW in v0.0.3)
370
-
371
- ```typescript
372
- import { ErrorHandler, DatabaseError } from 'nanodb-orm';
373
-
374
- try {
375
- await DatabaseSync.setup();
376
- } catch (error) {
377
- if (error instanceof DatabaseError) {
378
- console.log('Operation:', error.operation);
379
- console.log('Context:', error.message);
380
- console.log('Original Error:', error.originalError);
381
- }
382
- }
383
-
384
- // Non-throwing error handling
385
- const result = ErrorHandler.handleNonThrowingError(
386
- someError,
387
- 'optional-operation',
388
- 'This operation is optional'
389
- );
390
- ```
391
-
392
- ### Thread-Safe Connections (NEW in v0.0.3)
393
-
394
- ```typescript
395
- import { DatabaseConnection } from 'nanodb-orm';
396
-
397
- // NEW: Async connection (recommended)
398
- const db = await DatabaseConnection.getInstance();
399
-
400
- // OLD: Synchronous connection (deprecated but still works)
401
- const db = DatabaseConnection.getInstanceSync();
402
-
403
- // Check connection status
404
- if (DatabaseConnection.isConnected()) {
405
- console.log('Database is connected');
406
- }
407
- ```
408
-
409
- ### Schema Introspection
410
-
411
- ```typescript
412
- import { SchemaIntrospection } from 'nanodb-orm';
413
-
414
- // Get comprehensive schema information
415
- const schemaInfo = SchemaIntrospection.getSchemaStats();
416
- console.log('Total tables:', schemaInfo.totalTables);
417
- console.log('Table details:', schemaInfo.tableDetails);
418
-
419
- // Validate schema integrity
420
- const validation = await SchemaIntrospection.validateSchema();
421
- if (!validation.isValid) {
422
- console.log('Schema issues:', validation.errors);
423
- }
424
- ```
425
-
426
- ## Testing
427
-
428
- The package includes built-in testing utilities:
285
+ ## Configuration
429
286
 
430
- ```typescript
431
- import { DatabaseSync } from 'nanodb-orm';
287
+ ### Environment Variables
432
288
 
433
- describe('My Tests', () => {
434
- beforeEach(async () => {
435
- // Reset database for each test
436
- await DatabaseSync.reset();
437
- });
289
+ ```bash
290
+ # Remote Turso database
291
+ TURSO_CONNECTION_URL=libsql://your-db.turso.io
292
+ TURSO_AUTH_TOKEN=your-token
438
293
 
439
- test('should work with clean database', async () => {
440
- // Your test code here
441
- });
442
- });
294
+ # Force local SQLite (for testing)
295
+ FORCE_LOCAL_DB=true
443
296
  ```
444
297
 
445
- ## Database Support
446
-
447
- ### Local SQLite
448
- - Automatically used when Turso credentials are not available
449
- - Perfect for development and testing
450
- - File-based storage
298
+ ### Database Selection
451
299
 
452
- ### Remote Turso
453
- - Cloud-hosted SQLite database
454
- - Requires `TURSO_CONNECTION_URL` and `TURSO_AUTH_TOKEN`
455
- - Production-ready with global replication
300
+ - **Turso** — Used when `TURSO_CONNECTION_URL` and `TURSO_AUTH_TOKEN` are set
301
+ - **Local SQLite** — Fallback when Turso credentials are missing
302
+ - **Test Mode** — Isolated `test.db` when `NODE_ENV=test`
456
303
 
457
304
  ## Error Handling
458
305
 
459
- The package provides comprehensive error handling:
306
+ Errors are automatically parsed into user-friendly messages:
460
307
 
461
308
  ```typescript
462
- import { DatabaseError } from 'nanodb-orm';
309
+ import { DatabaseError, SchemaError, SeedError, parseDbError } from 'nanodb-orm';
463
310
 
464
311
  try {
465
312
  await DatabaseSync.setup();
466
313
  } catch (error) {
467
314
  if (error instanceof DatabaseError) {
468
- console.log('Database error:', error.message);
469
- console.log('Operation:', error.operation);
315
+ console.log(error.message); // User-friendly message
316
+ console.log(error.operation); // 'seed', 'migration', etc.
317
+ console.log(error.table); // Table name if applicable
318
+ console.log(error.detail); // Additional context
470
319
  }
471
320
  }
472
321
  ```
473
322
 
474
- ## Contributing
323
+ Error output is clean and actionable:
475
324
 
476
- 1. Fork the repository
477
- 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
478
- 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
479
- 4. Push to the branch (`git push origin feature/amazing-feature`)
480
- 5. Open a Pull Request
325
+ ```
326
+ ┌─ nanodb-orm error ─────────────────────────────
327
+ Column "email" does not exist
328
+ Table: users
329
+ Operation: seed
330
+ │ Detail: Row data: {"name":"Alice","email":"alice@example.com"}
331
+ └────────────────────────────────────────────────
332
+ ```
481
333
 
482
334
  ## License
483
335
 
484
336
  MIT © Damilola Alao
485
-
486
- ## Changelog
487
-
488
- ### 0.0.2
489
- - **Enhanced Documentation**: Added comprehensive Drizzle ORM integration examples
490
- - **Real Model Examples**: Updated documentation with actual Drizzle table definitions
491
- - **Schema Validation Fix**: Fixed table existence validation logic for proper health checks
492
- - **LLM Documentation**: Added detailed `llm.txt` file for AI/LLM integration
493
- - **Column Types Guide**: Added complete Drizzle column types and methods documentation
494
- - **Foreign Key Support**: Documented foreign key relationships and cascade deletes
495
- - **Type Safety**: Enhanced TypeScript integration examples with Drizzle's type inference
496
-
497
- ### 0.0.1
498
- - Initial release
499
- - Auto-migration system
500
- - Schema introspection
501
- - Multi-database support
502
- - TypeScript support
503
- - Testing utilities