@doviui/dev-db 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -0
- package/README.md +532 -0
- package/index.ts +39 -0
- package/package.json +49 -0
- package/src/field-builder.ts +188 -0
- package/src/generator.ts +360 -0
- package/src/schema-builder.ts +213 -0
- package/src/types.ts +107 -0
- package/src/validator.ts +231 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2025 Calvin Kimani
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
# dev-db
|
|
2
|
+
|
|
3
|
+
**TypeScript-first mock database generator for rapid application development**
|
|
4
|
+
|
|
5
|
+
dev-db eliminates the friction of setting up databases during development. Define your data model with a type-safe schema, generate realistic mock data instantly, and iterate faster without database infrastructure overhead.
|
|
6
|
+
|
|
7
|
+
## Why dev-db?
|
|
8
|
+
|
|
9
|
+
**For Frontend Developers**: Build and test UI components with realistic data without waiting for backend APIs. Generate hundreds of records in seconds and work independently.
|
|
10
|
+
|
|
11
|
+
**For Backend Developers**: Prototype schemas, test business logic, and validate data models before committing to a database. Catch schema errors early with built-in validation.
|
|
12
|
+
|
|
13
|
+
**For Teams**: Share reproducible datasets across development, testing, and CI/CD environments using seeds. Onboard new developers instantly with pre-generated data.
|
|
14
|
+
|
|
15
|
+
## Key Features
|
|
16
|
+
|
|
17
|
+
- **Type-Safe Schema Definition** - Fluent TypeScript API with full IntelliSense support
|
|
18
|
+
- **Automatic Relationship Resolution** - Foreign keys handled intelligently with topological sorting
|
|
19
|
+
- **Production-Quality Mock Data** - Powered by Faker.js for realistic, diverse datasets
|
|
20
|
+
- **Built-In Validation** - Detect circular dependencies, missing tables, and constraint conflicts before generation
|
|
21
|
+
- **Reproducible Datasets** - Seed-based generation for consistent data across environments
|
|
22
|
+
- **Zero Configuration** - Works out of the box, no database setup required
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
bun add @doviui/dev-db
|
|
28
|
+
# or
|
|
29
|
+
npm install @doviui/dev-db
|
|
30
|
+
# or
|
|
31
|
+
yarn add @doviui/dev-db
|
|
32
|
+
# or
|
|
33
|
+
pnpm add @doviui/dev-db
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
37
|
+
|
|
38
|
+
### Step 1: Define Your Schema
|
|
39
|
+
|
|
40
|
+
Create schema files using the fluent TypeScript API:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
// schemas/user.schema.ts
|
|
44
|
+
import { t } from '@doviui/dev-db'
|
|
45
|
+
|
|
46
|
+
export default {
|
|
47
|
+
User: {
|
|
48
|
+
$count: 100, // Generate 100 users
|
|
49
|
+
|
|
50
|
+
id: t.bigserial().primaryKey(),
|
|
51
|
+
username: t.varchar(50).unique().notNull().generate('internet.userName'),
|
|
52
|
+
email: t.varchar(255).unique().notNull().generate('internet.email'),
|
|
53
|
+
full_name: t.text().generate('person.fullName'),
|
|
54
|
+
age: t.integer().min(18).max(90),
|
|
55
|
+
is_active: t.boolean().default(true),
|
|
56
|
+
created_at: t.timestamptz().default('now')
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// schemas/post.schema.ts
|
|
63
|
+
import { t } from '@doviui/dev-db'
|
|
64
|
+
|
|
65
|
+
export default {
|
|
66
|
+
Post: {
|
|
67
|
+
$count: 500, // Generate 500 posts
|
|
68
|
+
|
|
69
|
+
id: t.bigserial().primaryKey(),
|
|
70
|
+
user_id: t.foreignKey('User', 'id').notNull(),
|
|
71
|
+
title: t.varchar(200).generate('lorem.sentence'),
|
|
72
|
+
content: t.text().generate('lorem.paragraphs'),
|
|
73
|
+
status: t.varchar(20).enum(['draft', 'published', 'archived']).default('draft'),
|
|
74
|
+
view_count: t.integer().default(0),
|
|
75
|
+
created_at: t.timestamptz().default('now')
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Step 2: Generate Your Data
|
|
81
|
+
|
|
82
|
+
#### Using the CLI directly
|
|
83
|
+
|
|
84
|
+
Run the CLI to generate JSON files:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Generate from a directory of schemas
|
|
88
|
+
bunx @doviui/dev-db generate ./schemas -o ./mock-data
|
|
89
|
+
|
|
90
|
+
# Generate from a single file
|
|
91
|
+
bunx @doviui/dev-db generate ./schemas/user.schema.ts -o ./data
|
|
92
|
+
|
|
93
|
+
# Use a seed for reproducible data
|
|
94
|
+
bunx @doviui/dev-db generate ./schemas --seed 42
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### Adding to package.json scripts
|
|
98
|
+
|
|
99
|
+
Add generation to your project's scripts:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"scripts": {
|
|
104
|
+
"generate:data": "bunx @doviui/dev-db generate ./schemas -o ./mock-data",
|
|
105
|
+
"generate:data:seed": "bunx @doviui/dev-db generate ./schemas -o ./mock-data --seed 42"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Then run with:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
bun run generate:data
|
|
114
|
+
# or with a seed
|
|
115
|
+
bun run generate:data:seed
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Step 3: Consume Your Data
|
|
119
|
+
|
|
120
|
+
Import the generated JSON and use it in your application:
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
// server.ts
|
|
124
|
+
import users from './mock-data/User.json'
|
|
125
|
+
import posts from './mock-data/Post.json'
|
|
126
|
+
|
|
127
|
+
// Simple query helpers
|
|
128
|
+
function getUserById(id: number) {
|
|
129
|
+
return users.find(u => u.id === id)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function getPostsByUserId(userId: number) {
|
|
133
|
+
return posts.filter(p => p.user_id === userId)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Use in your API
|
|
137
|
+
app.get('/users/:id', (req, res) => {
|
|
138
|
+
const user = getUserById(parseInt(req.params.id))
|
|
139
|
+
if (!user) return res.status(404).json({ error: 'User not found' })
|
|
140
|
+
|
|
141
|
+
const userPosts = getPostsByUserId(user.id)
|
|
142
|
+
res.json({ ...user, posts: userPosts })
|
|
143
|
+
})
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## API Reference
|
|
147
|
+
|
|
148
|
+
### Data Types
|
|
149
|
+
|
|
150
|
+
dev-db supports a comprehensive set of SQL-inspired data types:
|
|
151
|
+
|
|
152
|
+
#### Numeric Types
|
|
153
|
+
```typescript
|
|
154
|
+
t.bigint() // Big integer
|
|
155
|
+
t.bigserial() // Auto-incrementing big integer
|
|
156
|
+
t.integer() // Standard integer
|
|
157
|
+
t.smallint() // Small integer
|
|
158
|
+
t.serial() // Auto-incrementing integer
|
|
159
|
+
t.decimal(10, 2) // Decimal with precision and scale
|
|
160
|
+
t.numeric(10, 2) // Alias for decimal
|
|
161
|
+
t.real() // Floating point
|
|
162
|
+
t.double() // Double precision float
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
#### String Types
|
|
166
|
+
```typescript
|
|
167
|
+
t.varchar(255) // Variable-length string
|
|
168
|
+
t.char(10) // Fixed-length string
|
|
169
|
+
t.text() // Unlimited text
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
#### Date/Time Types
|
|
173
|
+
```typescript
|
|
174
|
+
t.date() // Date only
|
|
175
|
+
t.time() // Time only
|
|
176
|
+
t.timestamp() // Date and time
|
|
177
|
+
t.timestamptz() // Date and time with timezone
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
#### Other Types
|
|
181
|
+
```typescript
|
|
182
|
+
t.boolean() // True/false
|
|
183
|
+
t.uuid() // UUID v4
|
|
184
|
+
t.json() // JSON object
|
|
185
|
+
t.jsonb() // JSON binary
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
#### Relationships
|
|
189
|
+
```typescript
|
|
190
|
+
t.foreignKey('TableName', 'column') // Foreign key reference
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Field Modifiers
|
|
194
|
+
|
|
195
|
+
Chain modifiers to configure field behavior and constraints:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
// Constraints
|
|
199
|
+
.primaryKey() // Mark as primary key
|
|
200
|
+
.unique() // Enforce uniqueness
|
|
201
|
+
.notNull() // Cannot be null
|
|
202
|
+
.nullable() // Can be null (default)
|
|
203
|
+
|
|
204
|
+
// Defaults
|
|
205
|
+
.default(value) // Set default value
|
|
206
|
+
.default('now') // Special: current timestamp
|
|
207
|
+
|
|
208
|
+
// Ranges (for numeric types)
|
|
209
|
+
.min(18) // Minimum value
|
|
210
|
+
.max(90) // Maximum value
|
|
211
|
+
|
|
212
|
+
// Enums
|
|
213
|
+
.enum(['draft', 'published', 'archived'])
|
|
214
|
+
|
|
215
|
+
// Custom generation
|
|
216
|
+
.generate('internet.email') // Use Faker.js method
|
|
217
|
+
.generate(() => Math.random() * 100) // Custom function
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Advanced Usage
|
|
221
|
+
|
|
222
|
+
### Multi-Table Schemas
|
|
223
|
+
|
|
224
|
+
Define multiple related tables in a single file for better organization:
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
// schemas/social.schema.ts
|
|
228
|
+
import { t } from '@doviui/dev-db'
|
|
229
|
+
|
|
230
|
+
export default {
|
|
231
|
+
User: {
|
|
232
|
+
$count: 100,
|
|
233
|
+
id: t.bigserial().primaryKey(),
|
|
234
|
+
username: t.varchar(50).unique()
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
Post: {
|
|
238
|
+
$count: 500,
|
|
239
|
+
id: t.bigserial().primaryKey(),
|
|
240
|
+
user_id: t.foreignKey('User', 'id'),
|
|
241
|
+
title: t.varchar(200)
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
Comment: {
|
|
245
|
+
$count: 2000,
|
|
246
|
+
id: t.uuid().primaryKey(),
|
|
247
|
+
post_id: t.foreignKey('Post', 'id'),
|
|
248
|
+
user_id: t.foreignKey('User', 'id'),
|
|
249
|
+
content: t.text()
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Schema Validation
|
|
255
|
+
|
|
256
|
+
dev-db validates schemas before generation to catch errors early:
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
// ❌ This will fail validation
|
|
260
|
+
export default {
|
|
261
|
+
Post: {
|
|
262
|
+
id: t.bigserial().primaryKey(),
|
|
263
|
+
user_id: t.foreignKey('User', 'id') // Error: User table doesn't exist!
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**Output:**
|
|
269
|
+
```
|
|
270
|
+
🔍 Validating schemas...
|
|
271
|
+
|
|
272
|
+
❌ Schema validation failed:
|
|
273
|
+
|
|
274
|
+
• Post.user_id: Foreign key references non-existent table 'User'
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Custom Data Generators
|
|
278
|
+
|
|
279
|
+
Leverage any [Faker.js](https://fakerjs.dev/) method for realistic data generation:
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
t.varchar(100).generate('company.name')
|
|
283
|
+
t.varchar(50).generate('location.city')
|
|
284
|
+
t.integer().generate('number.int', { min: 1000, max: 9999 })
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Or write custom functions:
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
t.varchar(20).generate(() => {
|
|
291
|
+
const colors = ['red', 'blue', 'green', 'yellow']
|
|
292
|
+
return colors[Math.floor(Math.random() * colors.length)]
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
t.jsonb().generate((faker) => ({
|
|
296
|
+
preferences: {
|
|
297
|
+
theme: faker.helpers.arrayElement(['light', 'dark']),
|
|
298
|
+
language: faker.helpers.arrayElement(['en', 'es', 'fr'])
|
|
299
|
+
},
|
|
300
|
+
lastLogin: faker.date.recent().toISOString()
|
|
301
|
+
}))
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Command Line Interface
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
bunx @doviui/dev-db generate <schemas> [options]
|
|
308
|
+
|
|
309
|
+
Arguments:
|
|
310
|
+
schemas Path to schema file or directory
|
|
311
|
+
|
|
312
|
+
Options:
|
|
313
|
+
-o, --output <dir> Output directory (default: "./mock-data")
|
|
314
|
+
-s, --seed <number> Random seed for reproducibility
|
|
315
|
+
-h, --help Display help
|
|
316
|
+
-v, --version Display version
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Programmatic API
|
|
320
|
+
|
|
321
|
+
For more control, create a custom generation script:
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
// scripts/generate-data.ts
|
|
325
|
+
import { MockDataGenerator, SchemaValidator } from '@doviui/dev-db'
|
|
326
|
+
import { schema } from './schemas/index'
|
|
327
|
+
|
|
328
|
+
// Validate schema
|
|
329
|
+
const validator = new SchemaValidator()
|
|
330
|
+
const errors = validator.validate(schema)
|
|
331
|
+
|
|
332
|
+
if (errors.length > 0) {
|
|
333
|
+
console.error('❌ Schema validation failed:')
|
|
334
|
+
errors.forEach(err => console.error(` • ${err.message}`))
|
|
335
|
+
process.exit(1)
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Generate data
|
|
339
|
+
const generator = new MockDataGenerator(schema, {
|
|
340
|
+
outputDir: './mock-data',
|
|
341
|
+
seed: process.env.SEED ? parseInt(process.env.SEED) : undefined
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
await generator.generate()
|
|
345
|
+
console.log('✓ Mock data generated successfully!')
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
Add to package.json:
|
|
349
|
+
|
|
350
|
+
```json
|
|
351
|
+
{
|
|
352
|
+
"scripts": {
|
|
353
|
+
"generate:data": "bun run scripts/generate-data.ts"
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Then run with:
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
bun run generate:data
|
|
362
|
+
# or with a custom seed
|
|
363
|
+
SEED=42 bun run generate:data
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## Real-World Examples
|
|
367
|
+
|
|
368
|
+
### E-Commerce Platform
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
// schemas/ecommerce.schema.ts
|
|
372
|
+
import { t } from '@doviui/dev-db'
|
|
373
|
+
|
|
374
|
+
export default {
|
|
375
|
+
Customer: {
|
|
376
|
+
$count: 200,
|
|
377
|
+
id: t.uuid().primaryKey(),
|
|
378
|
+
email: t.varchar(255).unique().generate('internet.email'),
|
|
379
|
+
first_name: t.varchar(50).generate('person.firstName'),
|
|
380
|
+
last_name: t.varchar(50).generate('person.lastName'),
|
|
381
|
+
phone: t.varchar(20).generate('phone.number'),
|
|
382
|
+
created_at: t.timestamptz().default('now')
|
|
383
|
+
},
|
|
384
|
+
|
|
385
|
+
Product: {
|
|
386
|
+
$count: 100,
|
|
387
|
+
id: t.bigserial().primaryKey(),
|
|
388
|
+
name: t.varchar(200).generate('commerce.productName'),
|
|
389
|
+
description: t.text().generate('commerce.productDescription'),
|
|
390
|
+
price: t.decimal(10, 2).min(5).max(5000),
|
|
391
|
+
stock: t.integer().min(0).max(1000),
|
|
392
|
+
category: t.varchar(50).enum(['electronics', 'clothing', 'home', 'books'])
|
|
393
|
+
},
|
|
394
|
+
|
|
395
|
+
Order: {
|
|
396
|
+
$count: 500,
|
|
397
|
+
id: t.bigserial().primaryKey(),
|
|
398
|
+
customer_id: t.foreignKey('Customer', 'id').notNull(),
|
|
399
|
+
status: t.varchar(20).enum(['pending', 'processing', 'shipped', 'delivered']),
|
|
400
|
+
total: t.decimal(10, 2).min(10).max(10000),
|
|
401
|
+
created_at: t.timestamptz().default('now')
|
|
402
|
+
},
|
|
403
|
+
|
|
404
|
+
OrderItem: {
|
|
405
|
+
$count: 1500,
|
|
406
|
+
id: t.bigserial().primaryKey(),
|
|
407
|
+
order_id: t.foreignKey('Order', 'id').notNull(),
|
|
408
|
+
product_id: t.foreignKey('Product', 'id').notNull(),
|
|
409
|
+
quantity: t.integer().min(1).max(10),
|
|
410
|
+
price: t.decimal(10, 2)
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Blog Platform
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
// schemas/blog.schema.ts
|
|
419
|
+
import { t } from '@doviui/dev-db'
|
|
420
|
+
|
|
421
|
+
export default {
|
|
422
|
+
Author: {
|
|
423
|
+
$count: 50,
|
|
424
|
+
id: t.uuid().primaryKey(),
|
|
425
|
+
username: t.varchar(50).unique().generate('internet.userName'),
|
|
426
|
+
email: t.varchar(255).unique().generate('internet.email'),
|
|
427
|
+
bio: t.text().generate('lorem.paragraph'),
|
|
428
|
+
avatar_url: t.varchar(500).generate('image.avatar')
|
|
429
|
+
},
|
|
430
|
+
|
|
431
|
+
Article: {
|
|
432
|
+
$count: 300,
|
|
433
|
+
id: t.bigserial().primaryKey(),
|
|
434
|
+
author_id: t.foreignKey('Author', 'id'),
|
|
435
|
+
title: t.varchar(200).generate('lorem.sentence'),
|
|
436
|
+
slug: t.varchar(200).unique().generate('lorem.slug'),
|
|
437
|
+
content: t.text().generate('lorem.paragraphs', 5),
|
|
438
|
+
excerpt: t.varchar(500).generate('lorem.paragraph'),
|
|
439
|
+
published: t.boolean(),
|
|
440
|
+
published_at: t.timestamptz().nullable(),
|
|
441
|
+
created_at: t.timestamptz().default('now')
|
|
442
|
+
},
|
|
443
|
+
|
|
444
|
+
Tag: {
|
|
445
|
+
$count: 30,
|
|
446
|
+
id: t.serial().primaryKey(),
|
|
447
|
+
name: t.varchar(50).unique().generate('lorem.word')
|
|
448
|
+
},
|
|
449
|
+
|
|
450
|
+
ArticleTag: {
|
|
451
|
+
$count: 800,
|
|
452
|
+
article_id: t.foreignKey('Article', 'id'),
|
|
453
|
+
tag_id: t.foreignKey('Tag', 'id')
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Best Practices
|
|
459
|
+
|
|
460
|
+
### Schema Design
|
|
461
|
+
|
|
462
|
+
**Define independent tables first** - Structure your schemas so tables without foreign keys are defined before those with dependencies. This simplifies validation and generation.
|
|
463
|
+
|
|
464
|
+
**Use realistic record counts** - Set `$count` values that reflect production ratios. For example, if users typically have 5 posts, generate 100 users and 500 posts.
|
|
465
|
+
|
|
466
|
+
**Leverage domain-specific generators** - Use Faker.js methods that match your domain (e.g., `company.name` for business data, `person.firstName` for user data) to generate realistic datasets.
|
|
467
|
+
|
|
468
|
+
### Development Workflow
|
|
469
|
+
|
|
470
|
+
**Validate frequently** - Run generation regularly during schema development to catch errors early. The validator provides immediate feedback on structural issues.
|
|
471
|
+
|
|
472
|
+
**Use seeds for reproducibility** - In test environments and CI/CD, use the `--seed` option to generate identical datasets across runs. This ensures consistent test results.
|
|
473
|
+
|
|
474
|
+
**Organize by domain** - Group related tables in single schema files (e.g., `auth.schema.ts`, `orders.schema.ts`) for better maintainability and clearer relationships.
|
|
475
|
+
|
|
476
|
+
## Troubleshooting
|
|
477
|
+
|
|
478
|
+
### Foreign Key Validation Errors
|
|
479
|
+
|
|
480
|
+
**Problem**: `Foreign key references non-existent table 'TableName'`
|
|
481
|
+
|
|
482
|
+
**Solution**: Ensure all referenced tables are defined in your schema before tables that reference them:
|
|
483
|
+
|
|
484
|
+
```typescript
|
|
485
|
+
// ✅ Correct - User defined before Post
|
|
486
|
+
export default {
|
|
487
|
+
User: {
|
|
488
|
+
id: t.bigserial().primaryKey(),
|
|
489
|
+
// ... other fields
|
|
490
|
+
},
|
|
491
|
+
Post: {
|
|
492
|
+
id: t.bigserial().primaryKey(),
|
|
493
|
+
user_id: t.foreignKey('User', 'id') // User exists in schema
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### Unique Constraint Errors
|
|
499
|
+
|
|
500
|
+
**Problem**: `Could not generate unique value after 1000 attempts`
|
|
501
|
+
|
|
502
|
+
**Solution**: This occurs when unique constraints are too restrictive for the number of records. Consider these options:
|
|
503
|
+
|
|
504
|
+
- **Increase variety**: Use a more diverse Faker.js generator that produces more unique values
|
|
505
|
+
- **Reduce record count**: Lower the `$count` if you don't need that many records
|
|
506
|
+
- **Remove constraint**: If uniqueness isn't critical for your use case, remove the `.unique()` modifier
|
|
507
|
+
|
|
508
|
+
### Circular Dependency Errors
|
|
509
|
+
|
|
510
|
+
**Problem**: `Circular dependency detected involving table: TableName`
|
|
511
|
+
|
|
512
|
+
**Solution**: dev-db cannot resolve circular foreign key relationships. Restructure your schema using one of these approaches:
|
|
513
|
+
|
|
514
|
+
- **Make relationships optional**: Use `.nullable()` on one of the foreign keys
|
|
515
|
+
- **Introduce a junction table**: Break the circular dependency with an intermediary table
|
|
516
|
+
- **Reconsider the model**: Circular dependencies often indicate a modeling issue
|
|
517
|
+
|
|
518
|
+
## Contributing
|
|
519
|
+
|
|
520
|
+
We welcome contributions! Whether you're fixing bugs, improving documentation, or proposing new features, your input helps make dev-db better for everyone.
|
|
521
|
+
|
|
522
|
+
Please read our [Contributing Guide](CONTRIBUTING.md) to get started.
|
|
523
|
+
|
|
524
|
+
## License
|
|
525
|
+
|
|
526
|
+
MIT License - see [LICENSE](LICENSE) for details
|
|
527
|
+
|
|
528
|
+
## Acknowledgments
|
|
529
|
+
|
|
530
|
+
dev-db is built with:
|
|
531
|
+
- [Faker.js](https://fakerjs.dev/) - High-quality fake data generation
|
|
532
|
+
- [Bun](https://bun.sh/) - Fast JavaScript runtime and toolkit
|
package/index.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @doviui/dev-db - Generate realistic mock JSON databases from TypeScript schemas
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { t, MockDataGenerator, SchemaValidator } from '@doviui/dev-db';
|
|
9
|
+
*
|
|
10
|
+
* // Define schema
|
|
11
|
+
* const schema = {
|
|
12
|
+
* User: {
|
|
13
|
+
* $count: 100,
|
|
14
|
+
* id: t.bigserial().primaryKey(),
|
|
15
|
+
* email: t.varchar(255).unique().notNull().generate('internet.email'),
|
|
16
|
+
* age: t.integer().min(18).max(100)
|
|
17
|
+
* }
|
|
18
|
+
* };
|
|
19
|
+
*
|
|
20
|
+
* // Validate schema
|
|
21
|
+
* const validator = new SchemaValidator();
|
|
22
|
+
* const errors = validator.validate(schema);
|
|
23
|
+
*
|
|
24
|
+
* // Generate data
|
|
25
|
+
* const generator = new MockDataGenerator(schema, {
|
|
26
|
+
* outputDir: './mock-data',
|
|
27
|
+
* seed: 42
|
|
28
|
+
* });
|
|
29
|
+
* await generator.generate();
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
// Re-export public API
|
|
34
|
+
export { t } from './src/schema-builder';
|
|
35
|
+
export { MockDataGenerator } from './src/generator';
|
|
36
|
+
export { SchemaValidator } from './src/validator';
|
|
37
|
+
export type { Schema, TableConfig, FieldConfig, Generator } from './src/types';
|
|
38
|
+
export type { ValidationError } from './src/validator';
|
|
39
|
+
export type { GeneratorOptions } from './src/generator';
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@doviui/dev-db",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TypeScript-first mock database generator for rapid application development",
|
|
5
|
+
"main": "index.ts",
|
|
6
|
+
"module": "index.ts",
|
|
7
|
+
"types": "index.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./index.ts",
|
|
12
|
+
"types": "./index.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"index.ts",
|
|
17
|
+
"src/**/*.ts",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"keywords": [
|
|
22
|
+
"mock",
|
|
23
|
+
"database",
|
|
24
|
+
"generator",
|
|
25
|
+
"faker",
|
|
26
|
+
"typescript",
|
|
27
|
+
"testing",
|
|
28
|
+
"development"
|
|
29
|
+
],
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/calvin-kimani/dev-db.git"
|
|
34
|
+
},
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/calvin-kimani/dev-db/issues"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/calvin-kimani/dev-db#readme",
|
|
39
|
+
"author": "Calvin Kimani",
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/bun": "latest"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"typescript": "^5"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@faker-js/faker": "^10.1.0"
|
|
48
|
+
}
|
|
49
|
+
}
|