noormme 1.0.0 → 1.0.2

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 (137) hide show
  1. package/LICENSE +89 -21
  2. package/README.md +195 -578
  3. package/dist/cjs/cli/commands/analyze.js +4 -1
  4. package/dist/cjs/cli/commands/generate.js +48 -8
  5. package/dist/cjs/cli/commands/init.js +54 -14
  6. package/dist/cjs/cli/commands/inspect.js +10 -3
  7. package/dist/cjs/cli/commands/migrate.js +38 -2
  8. package/dist/cjs/cli/commands/optimize.js +4 -1
  9. package/dist/cjs/cli/commands/status.js +41 -5
  10. package/dist/cjs/cli/commands/watch.js +4 -1
  11. package/dist/cjs/cli/index.js +4 -1
  12. package/dist/cjs/dialect/database-introspector.js +16 -21
  13. package/dist/cjs/dialect/sqlite/sqlite-introspector.js +13 -24
  14. package/dist/cjs/dynamic/dynamic.d.ts +20 -0
  15. package/dist/cjs/dynamic/dynamic.js +25 -0
  16. package/dist/cjs/edge-runtime/edge-config.d.ts +125 -0
  17. package/dist/cjs/edge-runtime/edge-config.js +323 -0
  18. package/dist/cjs/errors/NoormError.d.ts +27 -4
  19. package/dist/cjs/errors/NoormError.js +142 -21
  20. package/dist/cjs/logging/logger.d.ts +7 -2
  21. package/dist/cjs/logging/logger.js +21 -7
  22. package/dist/cjs/noormme.d.ts +12 -8
  23. package/dist/cjs/noormme.js +133 -61
  24. package/dist/cjs/operation-node/column-node.js +4 -0
  25. package/dist/cjs/operation-node/identifier-node.js +4 -0
  26. package/dist/cjs/operation-node/table-node.js +7 -0
  27. package/dist/cjs/parser/reference-parser.js +5 -0
  28. package/dist/cjs/parser/table-parser.js +2 -0
  29. package/dist/cjs/performance/index.d.ts +44 -0
  30. package/dist/cjs/performance/index.js +64 -0
  31. package/dist/cjs/performance/query-optimizer.d.ts +134 -0
  32. package/dist/cjs/performance/query-optimizer.js +391 -0
  33. package/dist/cjs/performance/services/cache-service.d.ts +177 -0
  34. package/dist/cjs/performance/services/cache-service.js +415 -0
  35. package/dist/cjs/performance/services/connection-factory.d.ts +198 -0
  36. package/dist/cjs/performance/services/connection-factory.js +498 -0
  37. package/dist/cjs/performance/services/metrics-collector.d.ts +162 -0
  38. package/dist/cjs/performance/services/metrics-collector.js +406 -0
  39. package/dist/cjs/performance/utils/query-parser.d.ts +123 -0
  40. package/dist/cjs/performance/utils/query-parser.js +295 -0
  41. package/dist/cjs/raw-builder/sql.d.ts +73 -26
  42. package/dist/cjs/raw-builder/sql.js +9 -0
  43. package/dist/cjs/repository/repository-factory.d.ts +10 -42
  44. package/dist/cjs/repository/repository-factory.js +276 -394
  45. package/dist/cjs/schema/core/coordinators/schema-discovery.coordinator.js +6 -4
  46. package/dist/cjs/schema/core/factories/discovery-factory.js +5 -5
  47. package/dist/cjs/schema/core/utils/name-generator.js +34 -2
  48. package/dist/cjs/schema/core/utils/type-mapper.d.ts +19 -14
  49. package/dist/cjs/schema/core/utils/type-mapper.js +4 -7
  50. package/dist/cjs/schema/dialects/sqlite/discovery/sqlite-constraint-discovery.js +3 -2
  51. package/dist/cjs/schema/dialects/sqlite/sqlite-discovery.coordinator.d.ts +2 -0
  52. package/dist/cjs/schema/dialects/sqlite/sqlite-discovery.coordinator.js +19 -5
  53. package/dist/cjs/schema/test/dialect-capabilities.test.js +6 -0
  54. package/dist/cjs/schema/test/error-handling.test.js +52 -33
  55. package/dist/cjs/schema/test/integration.test.js +51 -5
  56. package/dist/cjs/schema/test/sqlite-discovery-coordinator.test.js +8 -4
  57. package/dist/cjs/sqlite-migration/index.js +35 -2
  58. package/dist/cjs/testing/test-utils.d.ts +5 -0
  59. package/dist/cjs/testing/test-utils.js +66 -6
  60. package/dist/cjs/types/index.d.ts +78 -13
  61. package/dist/cjs/types/index.js +23 -0
  62. package/dist/cjs/types/type-generator.d.ts +8 -0
  63. package/dist/cjs/types/type-generator.js +86 -17
  64. package/dist/cjs/util/safe-sql-helpers.d.ts +124 -0
  65. package/dist/cjs/util/safe-sql-helpers.js +221 -0
  66. package/dist/cjs/util/security-validator.d.ts +44 -0
  67. package/dist/cjs/util/security-validator.js +256 -0
  68. package/dist/cjs/util/security.d.ts +60 -0
  69. package/dist/cjs/util/security.js +137 -0
  70. package/dist/cjs/watch/schema-watcher.js +26 -7
  71. package/dist/esm/cli/commands/generate.js +10 -6
  72. package/dist/esm/cli/commands/init.js +15 -11
  73. package/dist/esm/cli/commands/inspect.js +6 -2
  74. package/dist/esm/cli/commands/status.js +3 -3
  75. package/dist/esm/dialect/database-introspector.js +16 -21
  76. package/dist/esm/dialect/sqlite/sqlite-introspector.js +13 -24
  77. package/dist/esm/dynamic/dynamic.d.ts +20 -0
  78. package/dist/esm/dynamic/dynamic.js +25 -0
  79. package/dist/esm/edge-runtime/edge-config.d.ts +125 -0
  80. package/dist/esm/edge-runtime/edge-config.js +281 -0
  81. package/dist/esm/errors/NoormError.d.ts +27 -4
  82. package/dist/esm/errors/NoormError.js +134 -18
  83. package/dist/esm/logging/logger.d.ts +7 -2
  84. package/dist/esm/logging/logger.js +21 -7
  85. package/dist/esm/noormme.d.ts +12 -8
  86. package/dist/esm/noormme.js +130 -61
  87. package/dist/esm/operation-node/column-node.js +4 -0
  88. package/dist/esm/operation-node/identifier-node.js +4 -0
  89. package/dist/esm/operation-node/table-node.js +7 -0
  90. package/dist/esm/parser/reference-parser.js +5 -0
  91. package/dist/esm/parser/table-parser.js +2 -0
  92. package/dist/esm/performance/index.d.ts +44 -0
  93. package/dist/esm/performance/index.js +48 -0
  94. package/dist/esm/performance/query-optimizer.d.ts +134 -0
  95. package/dist/esm/performance/query-optimizer.js +387 -0
  96. package/dist/esm/performance/services/cache-service.d.ts +177 -0
  97. package/dist/esm/performance/services/cache-service.js +410 -0
  98. package/dist/esm/performance/services/connection-factory.d.ts +198 -0
  99. package/dist/esm/performance/services/connection-factory.js +493 -0
  100. package/dist/esm/performance/services/metrics-collector.d.ts +162 -0
  101. package/dist/esm/performance/services/metrics-collector.js +402 -0
  102. package/dist/esm/performance/utils/query-parser.d.ts +123 -0
  103. package/dist/esm/performance/utils/query-parser.js +292 -0
  104. package/dist/esm/raw-builder/sql.d.ts +73 -26
  105. package/dist/esm/raw-builder/sql.js +9 -0
  106. package/dist/esm/repository/repository-factory.d.ts +10 -42
  107. package/dist/esm/repository/repository-factory.js +277 -395
  108. package/dist/esm/schema/core/coordinators/schema-discovery.coordinator.js +6 -4
  109. package/dist/esm/schema/core/factories/discovery-factory.js +5 -5
  110. package/dist/esm/schema/core/utils/name-generator.js +34 -2
  111. package/dist/esm/schema/core/utils/type-mapper.d.ts +19 -14
  112. package/dist/esm/schema/core/utils/type-mapper.js +4 -7
  113. package/dist/esm/schema/dialects/sqlite/discovery/sqlite-constraint-discovery.js +3 -2
  114. package/dist/esm/schema/dialects/sqlite/sqlite-discovery.coordinator.d.ts +2 -0
  115. package/dist/esm/schema/dialects/sqlite/sqlite-discovery.coordinator.js +19 -5
  116. package/dist/esm/schema/test/dialect-capabilities.test.js +6 -0
  117. package/dist/esm/schema/test/error-handling.test.js +52 -33
  118. package/dist/esm/schema/test/integration.test.js +18 -5
  119. package/dist/esm/schema/test/sqlite-discovery-coordinator.test.js +8 -4
  120. package/dist/esm/testing/test-utils.d.ts +5 -0
  121. package/dist/esm/testing/test-utils.js +66 -6
  122. package/dist/esm/types/index.d.ts +78 -13
  123. package/dist/esm/types/index.js +22 -1
  124. package/dist/esm/types/type-generator.d.ts +8 -0
  125. package/dist/esm/types/type-generator.js +86 -17
  126. package/dist/esm/util/safe-sql-helpers.d.ts +124 -0
  127. package/dist/esm/util/safe-sql-helpers.js +209 -0
  128. package/dist/esm/util/security-validator.d.ts +44 -0
  129. package/dist/esm/util/security-validator.js +246 -0
  130. package/dist/esm/util/security.d.ts +60 -0
  131. package/dist/esm/util/security.js +114 -0
  132. package/dist/esm/watch/schema-watcher.js +26 -7
  133. package/package.json +1 -1
  134. package/dist/cjs/performance/query-analyzer.d.ts +0 -89
  135. package/dist/cjs/performance/query-analyzer.js +0 -263
  136. package/dist/esm/performance/query-analyzer.d.ts +0 -89
  137. package/dist/esm/performance/query-analyzer.js +0 -260
@@ -23,7 +23,7 @@ async function createTestDatabase(config = {}) {
23
23
  password: ''
24
24
  },
25
25
  logging: {
26
- enabled: false // Disable logging in tests by default
26
+ enabled: process.env.TEST_DEBUG === 'true' // Enable with TEST_DEBUG=true
27
27
  }
28
28
  };
29
29
  break;
@@ -95,14 +95,62 @@ async function setupTestSchema(db) {
95
95
  .column('user_id')
96
96
  .execute();
97
97
  // Initialize NOORMME to discover the schema
98
- await db.initialize();
98
+ // Temporarily enable warnings to see discovery errors
99
+ const originalWarn = console.warn;
100
+ let discoveryError = null;
101
+ console.warn = (...args) => {
102
+ if (args[0]?.includes?.('Schema discovery failed')) {
103
+ discoveryError = args;
104
+ console.error('[DISCOVERY ERROR]', ...args);
105
+ }
106
+ originalWarn(...args);
107
+ };
108
+ try {
109
+ await db.initialize();
110
+ }
111
+ catch (error) {
112
+ console.error('NOORMME initialization error:', error);
113
+ throw new Error(`Test setup failed during initialization: ${error}`);
114
+ }
115
+ finally {
116
+ console.warn = originalWarn;
117
+ }
118
+ // Verify schema was discovered
119
+ const schemaInfo = await db.getSchemaInfo();
120
+ if (schemaInfo.tables.length === 0) {
121
+ // Try to manually check if tables exist
122
+ let actualTables = [];
123
+ try {
124
+ actualTables = await kysely.selectFrom('sqlite_master')
125
+ .select(['name', 'type'])
126
+ .where('type', '=', 'table')
127
+ .where('name', 'not like', 'sqlite_%')
128
+ .execute();
129
+ }
130
+ catch (e) {
131
+ console.error('[DEBUG] Failed to query sqlite_master:', e);
132
+ }
133
+ throw new Error(`Test setup failed: Schema discovery returned no tables but ${actualTables.length} tables exist in database (${actualTables.map((t) => t.name).join(', ')}). ` +
134
+ `Discovery error: ${discoveryError ? discoveryError.join(' ') : 'Unknown'}`);
135
+ }
136
+ // Verify expected tables exist
137
+ const tableNames = schemaInfo.tables.map(t => t.name);
138
+ const expectedTables = ['users', 'posts', 'comments'];
139
+ const missingTables = expectedTables.filter(t => !tableNames.includes(t));
140
+ if (missingTables.length > 0) {
141
+ throw new Error(`Test setup failed: Missing tables: ${missingTables.join(', ')}. ` +
142
+ `Found tables: ${tableNames.join(', ')}. ` +
143
+ `Expected: ${expectedTables.join(', ')}`);
144
+ }
99
145
  }
100
146
  /**
101
147
  * Clean up test database
102
148
  */
103
149
  async function cleanupTestDatabase(db) {
104
- const kysely = db.getKysely();
150
+ if (!db)
151
+ return;
105
152
  try {
153
+ const kysely = db.getKysely();
106
154
  // Drop tables in reverse order to handle foreign keys
107
155
  await kysely.schema.dropTable('comments').ifExists().execute();
108
156
  await kysely.schema.dropTable('posts').ifExists().execute();
@@ -111,16 +159,29 @@ async function cleanupTestDatabase(db) {
111
159
  catch (error) {
112
160
  // Ignore errors during cleanup
113
161
  }
114
- await db.close();
162
+ try {
163
+ await db.close();
164
+ }
165
+ catch (error) {
166
+ // Ignore close errors
167
+ }
115
168
  }
116
169
  /**
117
170
  * Test data factory
118
171
  */
119
172
  class TestDataFactory {
120
173
  db;
174
+ static emailCounter = 0;
121
175
  constructor(db) {
122
176
  this.db = db;
123
177
  }
178
+ /**
179
+ * Generate a unique email address
180
+ */
181
+ generateUniqueEmail() {
182
+ TestDataFactory.emailCounter++;
183
+ return `test${Date.now()}_${TestDataFactory.emailCounter}_${Math.random().toString(36).substring(7)}@example.com`;
184
+ }
124
185
  /**
125
186
  * Create a test user
126
187
  */
@@ -128,7 +189,7 @@ class TestDataFactory {
128
189
  const userRepo = this.db.getRepository('users');
129
190
  const userData = {
130
191
  name: 'Test User',
131
- email: `test${Date.now()}@example.com`,
192
+ email: this.generateUniqueEmail(),
132
193
  age: 25,
133
194
  active: true,
134
195
  ...overrides
@@ -143,7 +204,6 @@ class TestDataFactory {
143
204
  for (let i = 0; i < count; i++) {
144
205
  const user = await this.createUser({
145
206
  name: `Test User ${i + 1}`,
146
- email: `test${Date.now()}_${i}@example.com`,
147
207
  ...overrides
148
208
  });
149
209
  users.push(user);
@@ -2,19 +2,22 @@
2
2
  * Core type definitions for NOORMME
3
3
  */
4
4
  export interface NOORMConfig {
5
- dialect: 'sqlite';
5
+ dialect: 'sqlite' | 'postgresql' | 'mysql';
6
6
  connection: ConnectionConfig;
7
7
  introspection?: IntrospectionConfig;
8
8
  cache?: CacheConfig;
9
9
  logging?: LoggingConfig;
10
10
  performance?: PerformanceConfig;
11
+ automation?: AutomationConfig;
12
+ optimization?: OptimizationConfig;
13
+ sqlite?: SQLiteConfig;
11
14
  }
12
15
  export interface ConnectionConfig {
13
- host: string;
14
- port: number;
16
+ host?: string;
17
+ port?: number;
15
18
  database: string;
16
- username: string;
17
- password: string;
19
+ username?: string;
20
+ password?: string;
18
21
  ssl?: boolean | object;
19
22
  pool?: PoolConfig;
20
23
  }
@@ -42,6 +45,36 @@ export interface PerformanceConfig {
42
45
  enableQueryOptimization?: boolean;
43
46
  enableBatchLoading?: boolean;
44
47
  maxBatchSize?: number;
48
+ enableCaching?: boolean;
49
+ maxCacheSize?: number;
50
+ enableBatchOperations?: boolean;
51
+ slowQueryThreshold?: number;
52
+ }
53
+ export interface AutomationConfig {
54
+ enableAutoOptimization?: boolean;
55
+ enableIndexRecommendations?: boolean;
56
+ enableQueryAnalysis?: boolean;
57
+ enableMigrationGeneration?: boolean;
58
+ enablePerformanceMonitoring?: boolean;
59
+ enableSchemaWatcher?: boolean;
60
+ }
61
+ export interface OptimizationConfig {
62
+ enableWALMode?: boolean;
63
+ enableForeignKeys?: boolean;
64
+ cacheSize?: number;
65
+ synchronous?: 'OFF' | 'NORMAL' | 'FULL' | 'EXTRA';
66
+ tempStore?: 'DEFAULT' | 'FILE' | 'MEMORY';
67
+ autoVacuumMode?: 'NONE' | 'FULL' | 'INCREMENTAL';
68
+ journalMode?: 'DELETE' | 'TRUNCATE' | 'PERSIST' | 'MEMORY' | 'WAL' | 'OFF';
69
+ }
70
+ export interface SQLiteConfig {
71
+ enableWALMode?: boolean;
72
+ enableForeignKeys?: boolean;
73
+ cacheSize?: number;
74
+ synchronous?: 'OFF' | 'NORMAL' | 'FULL' | 'EXTRA';
75
+ tempStore?: 'DEFAULT' | 'FILE' | 'MEMORY';
76
+ autoVacuumMode?: 'NONE' | 'FULL' | 'INCREMENTAL';
77
+ journalMode?: 'DELETE' | 'TRUNCATE' | 'PERSIST' | 'MEMORY' | 'WAL' | 'OFF';
45
78
  }
46
79
  export interface SchemaInfo {
47
80
  tables: TableInfo[];
@@ -60,7 +93,7 @@ export interface ColumnInfo {
60
93
  name: string;
61
94
  type: string;
62
95
  nullable: boolean;
63
- defaultValue?: any;
96
+ defaultValue?: unknown;
64
97
  isPrimaryKey: boolean;
65
98
  isAutoIncrement: boolean;
66
99
  maxLength?: number;
@@ -111,15 +144,16 @@ export interface EntityType {
111
144
  selectType: string;
112
145
  }
113
146
  export interface Repository<T> {
114
- findById(id: any): Promise<T | null>;
147
+ objects: any;
148
+ findById(id: string | number): Promise<T | null>;
115
149
  findAll(): Promise<T[]>;
116
150
  create(data: Partial<T>): Promise<T>;
117
151
  update(entity: T): Promise<T>;
118
- delete(id: any): Promise<boolean>;
119
- findWithRelations(id: any, relations: string[]): Promise<T | null>;
152
+ delete(id: string | number): Promise<boolean>;
153
+ findWithRelations(id: string | number, relations: string[]): Promise<T | null>;
120
154
  loadRelationships(entities: T[], relations: string[]): Promise<void>;
121
155
  count(): Promise<number>;
122
- exists(id: any): Promise<boolean>;
156
+ exists(id: string | number): Promise<boolean>;
123
157
  paginate(options: {
124
158
  page: number;
125
159
  limit: number;
@@ -139,17 +173,48 @@ export interface Repository<T> {
139
173
  hasPrev: boolean;
140
174
  };
141
175
  }>;
142
- withCount(id: any, relationships: string[]): Promise<T & Record<string, number>>;
143
- [key: string]: any;
176
+ withCount(id: string | number, relationships: string[]): Promise<T & Record<string, number>>;
177
+ [key: string]: unknown;
144
178
  }
145
179
  export interface SchemaChange {
146
180
  type: 'table_added' | 'table_removed' | 'column_added' | 'column_removed' | 'column_modified';
147
181
  table: string;
148
182
  column?: string;
149
- details?: any;
183
+ details?: unknown;
150
184
  }
151
185
  export interface RefreshResult {
152
186
  schemaInfo: SchemaInfo;
153
187
  changes: SchemaChange[];
154
188
  typesRegenerated: boolean;
155
189
  }
190
+ export interface ConnectionPoolConfig {
191
+ min?: number;
192
+ max?: number;
193
+ idleTimeout?: number;
194
+ acquireTimeout?: number;
195
+ }
196
+ export interface QueryCacheConfig {
197
+ enabled: boolean;
198
+ ttl: number;
199
+ maxSize: number;
200
+ }
201
+ export interface BatchConfig {
202
+ maxBatchSize: number;
203
+ batchTimeout: number;
204
+ }
205
+ export interface OptimizationRecommendation {
206
+ type: 'index' | 'query' | 'schema' | 'performance';
207
+ priority: 'low' | 'medium' | 'high';
208
+ description: string;
209
+ suggestion: string;
210
+ estimatedImpact: string;
211
+ }
212
+ export interface BaseRepository<T> {
213
+ findAll(): Promise<T[]>;
214
+ findById(id: string | number): Promise<T | null>;
215
+ create(data: Partial<T>): Promise<T>;
216
+ update(id: string | number, data: Partial<T>): Promise<T>;
217
+ delete(id: string | number): Promise<boolean>;
218
+ count(): Promise<number>;
219
+ }
220
+ export declare function validateNOORMConfig(config: NOORMConfig): void;
@@ -3,3 +3,26 @@
3
3
  * Core type definitions for NOORMME
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.validateNOORMConfig = validateNOORMConfig;
7
+ // Configuration validation function type
8
+ function validateNOORMConfig(config) {
9
+ if (!config.dialect) {
10
+ throw new Error('Dialect is required');
11
+ }
12
+ if (!config.connection?.database) {
13
+ throw new Error('Database path is required');
14
+ }
15
+ // Validate dialect-specific requirements
16
+ if (config.dialect === 'sqlite') {
17
+ if (!config.connection.database.endsWith('.db') && !config.connection.database.endsWith('.sqlite')) {
18
+ console.warn('SQLite database path should typically end with .db or .sqlite');
19
+ }
20
+ }
21
+ // Validate performance settings
22
+ if (config.performance?.maxBatchSize && config.performance.maxBatchSize <= 0) {
23
+ throw new Error('maxBatchSize must be greater than 0');
24
+ }
25
+ if (config.performance?.maxCacheSize && config.performance.maxCacheSize <= 0) {
26
+ throw new Error('maxCacheSize must be greater than 0');
27
+ }
28
+ }
@@ -41,6 +41,14 @@ export declare class TypeGenerator {
41
41
  * Get relationships for a specific table
42
42
  */
43
43
  private getRelationshipsForTable;
44
+ /**
45
+ * Generate relationship interfaces
46
+ */
47
+ private generateRelationshipInterfaces;
48
+ /**
49
+ * Convert string to camelCase
50
+ */
51
+ private toCamelCase;
44
52
  /**
45
53
  * Get relationship type name
46
54
  */
@@ -18,7 +18,7 @@ class TypeGenerator {
18
18
  let types = '';
19
19
  // Generate entity types for each table
20
20
  for (const table of schemaInfo.tables) {
21
- const entity = this.generateEntityType(table);
21
+ const entity = this.generateEntityType(table, schemaInfo.relationships);
22
22
  entities.push(entity);
23
23
  interfaces += entity.interface + '\n\n';
24
24
  types += entity.insertType + '\n\n';
@@ -36,11 +36,11 @@ class TypeGenerator {
36
36
  /**
37
37
  * Generate entity type for a table
38
38
  */
39
- generateEntityType(table) {
39
+ generateEntityType(table, relationships) {
40
40
  const entityName = this.toPascalCase(table.name);
41
41
  const tableName = table.name;
42
42
  // Generate main entity interface
43
- const interfaceCode = this.generateEntityInterface(table, entityName);
43
+ const interfaceCode = this.generateEntityInterface(table, entityName, relationships);
44
44
  // Generate insert type (all columns except auto-generated ones)
45
45
  const insertType = this.generateInsertType(table, entityName);
46
46
  // Generate update type (all columns optional except primary key)
@@ -59,7 +59,7 @@ class TypeGenerator {
59
59
  /**
60
60
  * Generate main entity interface
61
61
  */
62
- generateEntityInterface(table, entityName) {
62
+ generateEntityInterface(table, entityName, relationships) {
63
63
  let interfaceCode = `export interface ${entityName} {\n`;
64
64
  // Add primary key columns first
65
65
  for (const column of table.columns) {
@@ -78,8 +78,8 @@ class TypeGenerator {
78
78
  }
79
79
  }
80
80
  // Add relationship properties
81
- const relationships = this.getRelationshipsForTable(table.name);
82
- for (const rel of relationships) {
81
+ const tableRelationships = this.getRelationshipsForTable(table.name, relationships);
82
+ for (const rel of tableRelationships) {
83
83
  const relType = this.getRelationshipType(rel);
84
84
  interfaceCode += ` ${rel.name}?: ${relType}\n`;
85
85
  }
@@ -189,8 +189,8 @@ class TypeGenerator {
189
189
  'timestamp': 'Date',
190
190
  'timestamptz': 'Date',
191
191
  'time': 'Date',
192
- 'json': 'any',
193
- 'jsonb': 'any',
192
+ 'json': 'Record<string, unknown>',
193
+ 'jsonb': 'Record<string, unknown>',
194
194
  'uuid': 'string',
195
195
  // MySQL specific types
196
196
  'longtext': 'string',
@@ -202,8 +202,11 @@ class TypeGenerator {
202
202
  'double': 'number',
203
203
  'bool': 'boolean',
204
204
  'datetime': 'Date',
205
- // SQLite specific types
205
+ // SQLite specific types (enhanced)
206
206
  'blob': 'Buffer',
207
+ 'int2': 'number',
208
+ 'int8': 'number',
209
+ 'clob': 'string',
207
210
  // MSSQL specific types
208
211
  'nvarchar': 'string',
209
212
  'nchar': 'string',
@@ -214,23 +217,89 @@ class TypeGenerator {
214
217
  };
215
218
  // Try exact match first
216
219
  if (typeMapping[column.type.toLowerCase()]) {
217
- return typeMapping[column.type.toLowerCase()];
220
+ let mappedType = typeMapping[column.type.toLowerCase()];
221
+ // Handle nullable columns
222
+ if (column.nullable && mappedType !== 'unknown') {
223
+ mappedType = `${mappedType} | null`;
224
+ }
225
+ return mappedType;
218
226
  }
219
227
  // Handle parameterized types (e.g., varchar(255), decimal(10,2))
220
228
  const baseType = column.type.toLowerCase().split('(')[0];
221
229
  if (typeMapping[baseType]) {
222
- return typeMapping[baseType];
230
+ let mappedType = typeMapping[baseType];
231
+ // Handle nullable columns
232
+ if (column.nullable && mappedType !== 'unknown') {
233
+ mappedType = `${mappedType} | null`;
234
+ }
235
+ return mappedType;
223
236
  }
224
- // Default to any for unknown types
225
- return 'any';
237
+ // Default to unknown for unknown types
238
+ return column.nullable ? 'unknown | null' : 'unknown';
226
239
  }
227
240
  /**
228
241
  * Get relationships for a specific table
229
242
  */
230
- getRelationshipsForTable(tableName) {
231
- // This would be passed from the schema discovery
232
- // For now, return empty array
233
- return [];
243
+ getRelationshipsForTable(tableName, relationships) {
244
+ return relationships.filter(rel => rel.fromTable === tableName || rel.toTable === tableName);
245
+ }
246
+ /**
247
+ * Generate relationship interfaces
248
+ */
249
+ generateRelationshipInterfaces(relationships) {
250
+ if (relationships.length === 0) {
251
+ return '';
252
+ }
253
+ let relationshipInterfaces = '// Relationship Types\n\n';
254
+ // Group relationships by type
255
+ const groupedRelationships = relationships.reduce((acc, rel) => {
256
+ const key = `${rel.fromTable}_${rel.toTable}_${rel.type}`;
257
+ if (!acc[key]) {
258
+ acc[key] = [];
259
+ }
260
+ acc[key].push(rel);
261
+ return acc;
262
+ }, {});
263
+ for (const [key, rels] of Object.entries(groupedRelationships)) {
264
+ const rel = rels[0]; // Use first relationship as template
265
+ const fromEntity = this.toPascalCase(rel.fromTable);
266
+ const toEntity = this.toPascalCase(rel.toTable);
267
+ switch (rel.type) {
268
+ case 'one-to-many':
269
+ relationshipInterfaces += `export interface ${fromEntity}With${toEntity}s extends ${fromEntity} {\n`;
270
+ relationshipInterfaces += ` ${this.toCamelCase(rel.toTable)}s: ${toEntity}[]\n`;
271
+ relationshipInterfaces += `}\n\n`;
272
+ relationshipInterfaces += `export interface ${toEntity}With${fromEntity} extends ${toEntity} {\n`;
273
+ relationshipInterfaces += ` ${this.toCamelCase(rel.fromTable)}: ${fromEntity}\n`;
274
+ relationshipInterfaces += `}\n\n`;
275
+ break;
276
+ case 'many-to-one':
277
+ relationshipInterfaces += `export interface ${fromEntity}With${toEntity} extends ${fromEntity} {\n`;
278
+ relationshipInterfaces += ` ${this.toCamelCase(rel.toTable)}: ${toEntity}\n`;
279
+ relationshipInterfaces += `}\n\n`;
280
+ relationshipInterfaces += `export interface ${toEntity}With${fromEntity}s extends ${toEntity} {\n`;
281
+ relationshipInterfaces += ` ${this.toCamelCase(rel.fromTable)}s: ${fromEntity}[]\n`;
282
+ relationshipInterfaces += `}\n\n`;
283
+ break;
284
+ case 'many-to-many':
285
+ relationshipInterfaces += `export interface ${fromEntity}With${toEntity}s extends ${fromEntity} {\n`;
286
+ relationshipInterfaces += ` ${this.toCamelCase(rel.toTable)}s: ${toEntity}[]\n`;
287
+ relationshipInterfaces += `}\n\n`;
288
+ relationshipInterfaces += `export interface ${toEntity}With${fromEntity}s extends ${toEntity} {\n`;
289
+ relationshipInterfaces += ` ${this.toCamelCase(rel.fromTable)}s: ${fromEntity}[]\n`;
290
+ relationshipInterfaces += `}\n\n`;
291
+ break;
292
+ }
293
+ }
294
+ return relationshipInterfaces;
295
+ }
296
+ /**
297
+ * Convert string to camelCase
298
+ */
299
+ toCamelCase(str) {
300
+ return str.replace(/([-_][a-z])/gi, ($1) => {
301
+ return $1.toUpperCase().replace('-', '').replace('_', '');
302
+ });
234
303
  }
235
304
  /**
236
305
  * Get relationship type name
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Safe SQL helper utilities
3
+ * Provides secure alternatives to dangerous sql.raw() and sql.lit() methods
4
+ */
5
+ import { RawBuilder } from '../raw-builder/raw-builder.js';
6
+ /**
7
+ * Safely creates a SQL ORDER BY direction clause
8
+ * Only allows 'ASC' or 'DESC'
9
+ */
10
+ export declare function safeOrderDirection(direction: string): RawBuilder<unknown>;
11
+ /**
12
+ * Safely creates a SQL LIMIT clause
13
+ * Validates that the limit is a positive integer
14
+ */
15
+ export declare function safeLimit(limit: number | string): RawBuilder<unknown>;
16
+ /**
17
+ * Safely creates a SQL OFFSET clause
18
+ * Validates that the offset is a non-negative integer
19
+ */
20
+ export declare function safeOffset(offset: number | string): RawBuilder<unknown>;
21
+ /**
22
+ * Safely creates SQL keywords from a whitelist
23
+ * Useful for database-specific syntax that must be hardcoded
24
+ */
25
+ export declare function safeKeyword(keyword: string, allowedKeywords: string[]): RawBuilder<unknown>;
26
+ /**
27
+ * Type-safe enum for common SQL keywords
28
+ */
29
+ export declare const SafeSQLKeywords: {
30
+ /**
31
+ * Lock modes for SELECT statements
32
+ */
33
+ readonly LockMode: {
34
+ readonly FOR_UPDATE: "FOR UPDATE";
35
+ readonly FOR_SHARE: "FOR SHARE";
36
+ readonly FOR_NO_KEY_UPDATE: "FOR NO KEY UPDATE";
37
+ readonly FOR_KEY_SHARE: "FOR KEY SHARE";
38
+ };
39
+ /**
40
+ * Join types
41
+ */
42
+ readonly JoinType: {
43
+ readonly INNER: "INNER";
44
+ readonly LEFT: "LEFT";
45
+ readonly RIGHT: "RIGHT";
46
+ readonly FULL: "FULL";
47
+ readonly CROSS: "CROSS";
48
+ };
49
+ /**
50
+ * Set operations
51
+ */
52
+ readonly SetOperation: {
53
+ readonly UNION: "UNION";
54
+ readonly UNION_ALL: "UNION ALL";
55
+ readonly INTERSECT: "INTERSECT";
56
+ readonly EXCEPT: "EXCEPT";
57
+ };
58
+ /**
59
+ * SQLite-specific pragmas (use with extreme caution)
60
+ */
61
+ readonly SafePragma: {
62
+ readonly OPTIMIZE: "OPTIMIZE";
63
+ readonly WAL_CHECKPOINT: "WAL_CHECKPOINT";
64
+ };
65
+ };
66
+ /**
67
+ * Safely creates a lock mode clause for SELECT statements
68
+ */
69
+ export declare function safeLockMode(mode: keyof typeof SafeSQLKeywords.LockMode): RawBuilder<unknown>;
70
+ /**
71
+ * Type-safe ORDER BY builder
72
+ * Validates column names and direction
73
+ */
74
+ export interface SafeOrderBy {
75
+ column: string;
76
+ direction?: 'ASC' | 'DESC';
77
+ }
78
+ /**
79
+ * Creates a safe ORDER BY clause from validated columns
80
+ */
81
+ export declare function safeOrderBy(orderBy: SafeOrderBy[], allowedColumns: string[]): RawBuilder<unknown>;
82
+ /**
83
+ * Validates and creates a safe boolean value for SQL
84
+ */
85
+ export declare function safeBoolean(value: boolean | string | number): RawBuilder<boolean>;
86
+ /**
87
+ * Creates a safe CASE statement with validated conditions
88
+ */
89
+ export interface SafeCaseWhen<T> {
90
+ condition: RawBuilder<boolean>;
91
+ result: T;
92
+ }
93
+ export declare function safeCaseStatement<T>(cases: SafeCaseWhen<T>[], elseResult: T): RawBuilder<T>;
94
+ /**
95
+ * Example usage and best practices
96
+ */
97
+ export declare const SafeSQLExamples: {
98
+ /**
99
+ * Example: Safe pagination with user input
100
+ */
101
+ safePagination: () => {
102
+ limit: RawBuilder<unknown>;
103
+ offset: RawBuilder<unknown>;
104
+ };
105
+ /**
106
+ * Example: Safe sorting with user input
107
+ */
108
+ safeSorting: (userSortColumn: string, userDirection: string) => {
109
+ column: RawBuilder<unknown>;
110
+ direction: RawBuilder<unknown>;
111
+ };
112
+ /**
113
+ * Example: Safe lock mode for transactions
114
+ */
115
+ safeTransactionLock: () => RawBuilder<unknown>;
116
+ };
117
+ /**
118
+ * Helper to validate that a value is from a specific enum
119
+ */
120
+ export declare function validateEnum<T extends string>(value: string, enumValues: readonly T[], fieldName?: string): T;
121
+ /**
122
+ * Helper to validate numeric ranges
123
+ */
124
+ export declare function validateNumericRange(value: number | string, min: number, max: number, fieldName?: string): number;