nanodb-orm 0.0.1
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 +21 -0
- package/README.md +288 -0
- package/dist/constants/index.d.ts +59 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +67 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/core/config.d.ts +17 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +40 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/connection.d.ts +29 -0
- package/dist/core/connection.d.ts.map +1 -0
- package/dist/core/connection.js +77 -0
- package/dist/core/connection.js.map +1 -0
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +26 -0
- package/dist/core/index.js.map +1 -0
- package/dist/example.d.ts +67 -0
- package/dist/example.d.ts.map +1 -0
- package/dist/example.js +86 -0
- package/dist/example.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +28 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +63 -0
- package/dist/init.js.map +1 -0
- package/dist/types/errors.d.ts +23 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +46 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +23 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/types.d.ts +30 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/types.js +6 -0
- package/dist/types/types.js.map +1 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +18 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +26 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +41 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/migrations.d.ts +94 -0
- package/dist/utils/migrations.d.ts.map +1 -0
- package/dist/utils/migrations.js +440 -0
- package/dist/utils/migrations.js.map +1 -0
- package/dist/utils/schema-introspection.d.ts +183 -0
- package/dist/utils/schema-introspection.d.ts.map +1 -0
- package/dist/utils/schema-introspection.js +501 -0
- package/dist/utils/schema-introspection.js.map +1 -0
- package/dist/utils/seeds.d.ts +51 -0
- package/dist/utils/seeds.d.ts.map +1 -0
- package/dist/utils/seeds.js +209 -0
- package/dist/utils/seeds.js.map +1 -0
- package/dist/utils/sync.d.ts +57 -0
- package/dist/utils/sync.d.ts.map +1 -0
- package/dist/utils/sync.js +221 -0
- package/dist/utils/sync.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema introspection utilities
|
|
3
|
+
* Dynamically works with schema changes without hardcoding table names
|
|
4
|
+
* Schema.ts is the single source of truth for all database operations
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Schema introspection utilities for dynamic schema handling
|
|
8
|
+
* Accepts schema data as parameters to work as an npm package
|
|
9
|
+
*/
|
|
10
|
+
export declare class SchemaIntrospection {
|
|
11
|
+
private static tables;
|
|
12
|
+
private static tableNames;
|
|
13
|
+
/**
|
|
14
|
+
* Initialize the introspection utility with schema data
|
|
15
|
+
*/
|
|
16
|
+
static initialize(schemaData: {
|
|
17
|
+
tables: Record<string, any>;
|
|
18
|
+
tableNames: string[];
|
|
19
|
+
}): void;
|
|
20
|
+
/**
|
|
21
|
+
* Get all table names from the current schema
|
|
22
|
+
*/
|
|
23
|
+
static getAllTableNames(): string[];
|
|
24
|
+
/**
|
|
25
|
+
* Get all tables from the current schema
|
|
26
|
+
*/
|
|
27
|
+
static getAllTables(): {
|
|
28
|
+
[x: string]: any;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Get a specific table by name
|
|
32
|
+
*/
|
|
33
|
+
static getTable(tableName: string): any;
|
|
34
|
+
/**
|
|
35
|
+
* Check if a table exists in the schema
|
|
36
|
+
*/
|
|
37
|
+
static hasTable(tableName: string): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Get table count in schema
|
|
40
|
+
*/
|
|
41
|
+
static getTableCount(): number;
|
|
42
|
+
/**
|
|
43
|
+
* Get table information for a specific table
|
|
44
|
+
*/
|
|
45
|
+
static getTableInfo(tableName: string): {
|
|
46
|
+
name: string;
|
|
47
|
+
exists: boolean;
|
|
48
|
+
columns: string[];
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Get all table information
|
|
52
|
+
*/
|
|
53
|
+
static getAllTableInfo(): {
|
|
54
|
+
name: string;
|
|
55
|
+
exists: boolean;
|
|
56
|
+
columns: string[];
|
|
57
|
+
}[];
|
|
58
|
+
/**
|
|
59
|
+
* Generate CREATE TABLE statement for a table
|
|
60
|
+
* This is truly derived from the schema.ts source of truth - no hardcoded values!
|
|
61
|
+
*
|
|
62
|
+
* This method uses a generic approach that works with any table structure.
|
|
63
|
+
* It analyzes the table definition from schema.ts and generates appropriate SQL.
|
|
64
|
+
*/
|
|
65
|
+
static generateCreateTableStatement(tableName: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* Get table column information from schema.ts
|
|
68
|
+
* This extracts column names directly from the Drizzle table definition
|
|
69
|
+
* Completely generic - works with any table structure defined in schema.ts
|
|
70
|
+
*/
|
|
71
|
+
static getTableColumns(tableName: string): string[];
|
|
72
|
+
/**
|
|
73
|
+
* Get columns from the actual schema.ts file
|
|
74
|
+
* This approach uses Drizzle's internal table structure more effectively
|
|
75
|
+
*/
|
|
76
|
+
private static getColumnsFromSchemaFile;
|
|
77
|
+
/**
|
|
78
|
+
* Get table metadata from schema.ts
|
|
79
|
+
*/
|
|
80
|
+
static getTableMetadata(tableName: string): {
|
|
81
|
+
name: string;
|
|
82
|
+
columns: string[];
|
|
83
|
+
primaryKey: string[];
|
|
84
|
+
foreignKeys: {
|
|
85
|
+
column: string;
|
|
86
|
+
references: string;
|
|
87
|
+
table: string;
|
|
88
|
+
}[];
|
|
89
|
+
indexes: string[];
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Get primary key column(s) from schema.ts
|
|
93
|
+
* Works with any table structure - no hardcoded table names!
|
|
94
|
+
*/
|
|
95
|
+
static getPrimaryKey(tableName: string): string[];
|
|
96
|
+
/**
|
|
97
|
+
* Get foreign key relationships from schema.ts
|
|
98
|
+
* Works with any table structure - no hardcoded table names!
|
|
99
|
+
*/
|
|
100
|
+
static getForeignKeys(tableName: string): Array<{
|
|
101
|
+
column: string;
|
|
102
|
+
references: string;
|
|
103
|
+
table: string;
|
|
104
|
+
}>;
|
|
105
|
+
/**
|
|
106
|
+
* Get indexes from schema.ts
|
|
107
|
+
* Works with any table structure - no hardcoded table names!
|
|
108
|
+
*/
|
|
109
|
+
static getIndexes(tableName: string): string[];
|
|
110
|
+
/**
|
|
111
|
+
* Check if database schema matches the current schema definition
|
|
112
|
+
*/
|
|
113
|
+
static validateSchema(): Promise<{
|
|
114
|
+
isValid: boolean;
|
|
115
|
+
missingTables: string[];
|
|
116
|
+
extraTables: string[];
|
|
117
|
+
errors: string[];
|
|
118
|
+
}>;
|
|
119
|
+
/**
|
|
120
|
+
* Get comprehensive schema statistics derived from schema.ts
|
|
121
|
+
*/
|
|
122
|
+
static getSchemaStats(): {
|
|
123
|
+
totalTables: number;
|
|
124
|
+
tableNames: string[];
|
|
125
|
+
tables: string[];
|
|
126
|
+
tableDetails: {
|
|
127
|
+
name: string;
|
|
128
|
+
columns: string[];
|
|
129
|
+
primaryKey: string[];
|
|
130
|
+
foreignKeys: {
|
|
131
|
+
column: string;
|
|
132
|
+
references: string;
|
|
133
|
+
table: string;
|
|
134
|
+
}[];
|
|
135
|
+
indexes: string[];
|
|
136
|
+
}[];
|
|
137
|
+
lastUpdated: string;
|
|
138
|
+
sourceOfTruth: string;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Get table relationships derived from schema.ts
|
|
142
|
+
*/
|
|
143
|
+
static getTableRelationships(): {
|
|
144
|
+
fromTable: string;
|
|
145
|
+
fromColumn: string;
|
|
146
|
+
toTable: string;
|
|
147
|
+
toColumn: string;
|
|
148
|
+
}[];
|
|
149
|
+
/**
|
|
150
|
+
* Get schema validation rules derived from schema.ts
|
|
151
|
+
* Works with any table structure - no hardcoded table names!
|
|
152
|
+
*/
|
|
153
|
+
static getSchemaValidationRules(): {
|
|
154
|
+
table: string;
|
|
155
|
+
column: string;
|
|
156
|
+
rule: string;
|
|
157
|
+
value?: any;
|
|
158
|
+
}[];
|
|
159
|
+
/**
|
|
160
|
+
* Get schema export for documentation or migration purposes
|
|
161
|
+
*/
|
|
162
|
+
static exportSchemaDefinition(): {
|
|
163
|
+
tables: any;
|
|
164
|
+
relationships: {
|
|
165
|
+
fromTable: string;
|
|
166
|
+
fromColumn: string;
|
|
167
|
+
toTable: string;
|
|
168
|
+
toColumn: string;
|
|
169
|
+
}[];
|
|
170
|
+
validationRules: {
|
|
171
|
+
table: string;
|
|
172
|
+
column: string;
|
|
173
|
+
rule: string;
|
|
174
|
+
value?: any;
|
|
175
|
+
}[];
|
|
176
|
+
metadata: {
|
|
177
|
+
totalTables: number;
|
|
178
|
+
exportedAt: string;
|
|
179
|
+
sourceFile: string;
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=schema-introspection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-introspection.d.ts","sourceRoot":"","sources":["../../utils/schema-introspection.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH;;;GAGG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,CAA2B;IAChD,OAAO,CAAC,MAAM,CAAC,UAAU,CAAgB;IAEzC;;OAEG;IACH,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE;QAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC5B,UAAU,EAAE,MAAM,EAAE,CAAC;KACtB,GAAG,IAAI;IAIR;;OAEG;IACH,MAAM,CAAC,gBAAgB,IAAI,MAAM,EAAE;IAInC;;OAEG;IACH,MAAM,CAAC,YAAY;;;IAInB;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM;IASjC;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAI3C;;OAEG;IACH,MAAM,CAAC,aAAa,IAAI,MAAM;IAI9B;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM;;;;;IAcrC;;OAEG;IACH,MAAM,CAAC,eAAe;;;;;IAKtB;;;;;;OAMG;IACH,MAAM,CAAC,4BAA4B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAwD9D;;;;OAIG;IACH,MAAM,CAAC,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;IAsCnD;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAwFvC;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,MAAM;;;;;oBAyCiB,MAAM;wBAAc,MAAM;mBAAS,MAAM;;;;IA7BnG;;;OAGG;IACH,MAAM,CAAC,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;IAqBjD;;;OAGG;IACH,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IA6BtG;;;OAGG;IACH,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;IAwB9C;;OAEG;WACU,cAAc,IAAI,OAAO,CAAC;QACrC,OAAO,EAAE,OAAO,CAAC;QACjB,aAAa,EAAE,MAAM,EAAE,CAAC;QACxB,WAAW,EAAE,MAAM,EAAE,CAAC;QACtB,MAAM,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;IAiDF;;OAEG;IACH,MAAM,CAAC,cAAc;;;;;;;;;wBArHqC,MAAM;4BAAc,MAAM;uBAAS,MAAM;;;;;;;IA4InG;;OAEG;IACH,MAAM,CAAC,qBAAqB;mBAEb,MAAM;oBACL,MAAM;iBACT,MAAM;kBACL,MAAM;;IAoBpB;;;OAGG;IACH,MAAM,CAAC,wBAAwB;eAEpB,MAAM;gBACL,MAAM;cACR,MAAM;gBACJ,GAAG;;IA4Cf;;OAEG;IACH,MAAM,CAAC,sBAAsB;gBAGX,GAAG;;uBAlFN,MAAM;wBACL,MAAM;qBACT,MAAM;sBACL,MAAM;;;mBA0BT,MAAM;oBACL,MAAM;kBACR,MAAM;oBACJ,GAAG;;;;;;;;CAyEhB"}
|
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Schema introspection utilities
|
|
4
|
+
* Dynamically works with schema changes without hardcoding table names
|
|
5
|
+
* Schema.ts is the single source of truth for all database operations
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.SchemaIntrospection = void 0;
|
|
9
|
+
const drizzle_orm_1 = require("drizzle-orm");
|
|
10
|
+
const connection_1 = require("../core/connection");
|
|
11
|
+
const logger_1 = require("./logger");
|
|
12
|
+
const errors_1 = require("../types/errors");
|
|
13
|
+
/**
|
|
14
|
+
* Schema introspection utilities for dynamic schema handling
|
|
15
|
+
* Accepts schema data as parameters to work as an npm package
|
|
16
|
+
*/
|
|
17
|
+
class SchemaIntrospection {
|
|
18
|
+
/**
|
|
19
|
+
* Initialize the introspection utility with schema data
|
|
20
|
+
*/
|
|
21
|
+
static initialize(schemaData) {
|
|
22
|
+
this.tables = schemaData.tables;
|
|
23
|
+
this.tableNames = schemaData.tableNames;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get all table names from the current schema
|
|
27
|
+
*/
|
|
28
|
+
static getAllTableNames() {
|
|
29
|
+
return [...this.tableNames];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get all tables from the current schema
|
|
33
|
+
*/
|
|
34
|
+
static getAllTables() {
|
|
35
|
+
return { ...this.tables };
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get a specific table by name
|
|
39
|
+
*/
|
|
40
|
+
static getTable(tableName) {
|
|
41
|
+
const allTables = this.getAllTables();
|
|
42
|
+
const table = allTables[tableName];
|
|
43
|
+
if (!table) {
|
|
44
|
+
throw new errors_1.SchemaError(`Table ${tableName} not found in schema.ts`);
|
|
45
|
+
}
|
|
46
|
+
return table;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Check if a table exists in the schema
|
|
50
|
+
*/
|
|
51
|
+
static hasTable(tableName) {
|
|
52
|
+
return tableName in this.getAllTables();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get table count in schema
|
|
56
|
+
*/
|
|
57
|
+
static getTableCount() {
|
|
58
|
+
return this.getAllTableNames().length;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get table information for a specific table
|
|
62
|
+
*/
|
|
63
|
+
static getTableInfo(tableName) {
|
|
64
|
+
const table = this.getTable(tableName);
|
|
65
|
+
if (!table) {
|
|
66
|
+
throw new errors_1.SchemaError(`Table ${tableName} not found in schema`);
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
name: tableName,
|
|
70
|
+
exists: true,
|
|
71
|
+
columns: Object.keys(table._.columns),
|
|
72
|
+
// Add more table metadata as needed
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get all table information
|
|
77
|
+
*/
|
|
78
|
+
static getAllTableInfo() {
|
|
79
|
+
const tableNames = this.getAllTableNames();
|
|
80
|
+
return tableNames.map(name => this.getTableInfo(name));
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Generate CREATE TABLE statement for a table
|
|
84
|
+
* This is truly derived from the schema.ts source of truth - no hardcoded values!
|
|
85
|
+
*
|
|
86
|
+
* This method uses a generic approach that works with any table structure.
|
|
87
|
+
* It analyzes the table definition from schema.ts and generates appropriate SQL.
|
|
88
|
+
*/
|
|
89
|
+
static generateCreateTableStatement(tableName) {
|
|
90
|
+
// Get the table from schema.ts (single source of truth)
|
|
91
|
+
const table = this.getTable(tableName);
|
|
92
|
+
// Get table metadata dynamically from schema.ts
|
|
93
|
+
const columns = this.getTableColumns(tableName);
|
|
94
|
+
const primaryKey = this.getPrimaryKey(tableName);
|
|
95
|
+
const foreignKeys = this.getForeignKeys(tableName);
|
|
96
|
+
// Build column definitions dynamically
|
|
97
|
+
const columnDefinitions = [];
|
|
98
|
+
for (const column of columns) {
|
|
99
|
+
let columnDef = ` ${column}`;
|
|
100
|
+
// Determine data type and constraints based on column name patterns
|
|
101
|
+
// This is a generic approach that works with any table structure
|
|
102
|
+
if (column === 'id' || primaryKey.includes(column)) {
|
|
103
|
+
columnDef += ' INTEGER PRIMARY KEY';
|
|
104
|
+
}
|
|
105
|
+
else if (column.includes('_id')) {
|
|
106
|
+
columnDef += ' INTEGER NOT NULL';
|
|
107
|
+
}
|
|
108
|
+
else if (column.includes('_at') || column === 'created_at' || column === 'updated_at') {
|
|
109
|
+
if (column === 'created_at') {
|
|
110
|
+
columnDef += ' TEXT DEFAULT (CURRENT_TIMESTAMP) NOT NULL';
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
columnDef += ' INTEGER';
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else if (column === 'email') {
|
|
117
|
+
columnDef += ' TEXT UNIQUE NOT NULL';
|
|
118
|
+
}
|
|
119
|
+
else if (column === 'age' || column.includes('count') || column.includes('number')) {
|
|
120
|
+
columnDef += ' INTEGER NOT NULL';
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
columnDef += ' TEXT NOT NULL';
|
|
124
|
+
}
|
|
125
|
+
columnDefinitions.push(columnDef);
|
|
126
|
+
}
|
|
127
|
+
// Build foreign key constraints dynamically
|
|
128
|
+
const foreignKeyConstraints = [];
|
|
129
|
+
for (const fk of foreignKeys) {
|
|
130
|
+
foreignKeyConstraints.push(` FOREIGN KEY (${fk.column}) REFERENCES ${fk.table}(${fk.references}) ON DELETE CASCADE`);
|
|
131
|
+
}
|
|
132
|
+
// Combine all parts
|
|
133
|
+
const allDefinitions = [...columnDefinitions, ...foreignKeyConstraints];
|
|
134
|
+
return `
|
|
135
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
136
|
+
${allDefinitions.join(',\n')}
|
|
137
|
+
)
|
|
138
|
+
`;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get table column information from schema.ts
|
|
142
|
+
* This extracts column names directly from the Drizzle table definition
|
|
143
|
+
* Completely generic - works with any table structure defined in schema.ts
|
|
144
|
+
*/
|
|
145
|
+
static getTableColumns(tableName) {
|
|
146
|
+
const table = this.getTable(tableName);
|
|
147
|
+
// Extract column names from the Drizzle table definition
|
|
148
|
+
try {
|
|
149
|
+
// Access the Drizzle table's column definitions
|
|
150
|
+
const tableObj = table;
|
|
151
|
+
// Try different ways to access column information
|
|
152
|
+
if (tableObj._?.columns) {
|
|
153
|
+
return Object.keys(tableObj._.columns);
|
|
154
|
+
}
|
|
155
|
+
// Alternative access pattern
|
|
156
|
+
if (tableObj.columns) {
|
|
157
|
+
return Object.keys(tableObj.columns);
|
|
158
|
+
}
|
|
159
|
+
// Another possible access pattern
|
|
160
|
+
if (tableObj._?.name && tableObj._?.columns) {
|
|
161
|
+
return Object.keys(tableObj._.columns);
|
|
162
|
+
}
|
|
163
|
+
// If we can't access columns directly, this is a limitation of Drizzle introspection
|
|
164
|
+
// In production, you would use Drizzle's migration system for full dynamic support
|
|
165
|
+
logger_1.logger.debug(`Could not extract columns for table ${tableName} from Drizzle table object`);
|
|
166
|
+
// For now, we'll use a fallback that reads from the actual schema.ts file
|
|
167
|
+
// This is a more robust approach that works with any table structure
|
|
168
|
+
return this.getColumnsFromSchemaFile(tableName);
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
logger_1.logger.error(`Error extracting columns for table ${tableName}:`, error);
|
|
172
|
+
// Fallback to reading from schema.ts file
|
|
173
|
+
return this.getColumnsFromSchemaFile(tableName);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Get columns from the actual schema.ts file
|
|
178
|
+
* This approach uses Drizzle's internal table structure more effectively
|
|
179
|
+
*/
|
|
180
|
+
static getColumnsFromSchemaFile(tableName) {
|
|
181
|
+
try {
|
|
182
|
+
logger_1.logger.info(`Reading column information from schema.ts for table ${tableName}`);
|
|
183
|
+
// Get the table object from schema.ts
|
|
184
|
+
const table = this.getTable(tableName);
|
|
185
|
+
// Try multiple approaches to extract column information from Drizzle table object
|
|
186
|
+
const tableObj = table;
|
|
187
|
+
// Approach 1: Try to access columns through the table's internal structure
|
|
188
|
+
if (tableObj._?.columns) {
|
|
189
|
+
const columns = Object.keys(tableObj._.columns);
|
|
190
|
+
logger_1.logger.info(`Successfully extracted ${columns.length} columns for table ${tableName}: ${columns.join(', ')}`);
|
|
191
|
+
return columns;
|
|
192
|
+
}
|
|
193
|
+
// Approach 2: Try alternative column access pattern
|
|
194
|
+
if (tableObj.columns) {
|
|
195
|
+
const columns = Object.keys(tableObj.columns);
|
|
196
|
+
logger_1.logger.info(`Successfully extracted ${columns.length} columns for table ${tableName}: ${columns.join(', ')}`);
|
|
197
|
+
return columns;
|
|
198
|
+
}
|
|
199
|
+
// Approach 3: Try to access through the table's schema property
|
|
200
|
+
if (tableObj._?.schema) {
|
|
201
|
+
const columns = Object.keys(tableObj._.schema);
|
|
202
|
+
logger_1.logger.info(`Successfully extracted ${columns.length} columns for table ${tableName}: ${columns.join(', ')}`);
|
|
203
|
+
return columns;
|
|
204
|
+
}
|
|
205
|
+
// Approach 4: Try to get column names from the table's column definitions
|
|
206
|
+
if (tableObj._?.name && tableObj._?.columns) {
|
|
207
|
+
const columns = Object.keys(tableObj._.columns);
|
|
208
|
+
logger_1.logger.info(`Successfully extracted ${columns.length} columns for table ${tableName}: ${columns.join(', ')}`);
|
|
209
|
+
return columns;
|
|
210
|
+
}
|
|
211
|
+
// If all approaches fail, we need to use a different strategy
|
|
212
|
+
// Let's try to use Drizzle's introspection capabilities
|
|
213
|
+
logger_1.logger.debug(`All direct column extraction approaches failed for table ${tableName}, using fallback method`);
|
|
214
|
+
// The table object itself contains the column names as direct properties
|
|
215
|
+
// These are the actual column names from schema.ts
|
|
216
|
+
const allKeys = Object.keys(tableObj);
|
|
217
|
+
logger_1.logger.info(`Available keys on table object: ${allKeys.join(', ')}`);
|
|
218
|
+
// Filter out internal Drizzle properties and keep only column names
|
|
219
|
+
const columnNames = allKeys.filter(key => {
|
|
220
|
+
// Skip internal Drizzle properties
|
|
221
|
+
if (key.startsWith('_') || key === 'table' || key === 'schema') {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
// Check if this looks like a column (not a method or internal property)
|
|
225
|
+
const value = tableObj[key];
|
|
226
|
+
if (typeof value === 'function' || value === null || value === undefined) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
// If it's an object, check if it has column-like properties
|
|
230
|
+
if (typeof value === 'object') {
|
|
231
|
+
// This is likely a Drizzle column object, so the key is the column name
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
return true;
|
|
235
|
+
});
|
|
236
|
+
if (columnNames.length > 0) {
|
|
237
|
+
logger_1.logger.info(`Successfully extracted ${columnNames.length} columns for table ${tableName}: ${columnNames.join(', ')}`);
|
|
238
|
+
return columnNames;
|
|
239
|
+
}
|
|
240
|
+
// If we still can't find columns, throw an error
|
|
241
|
+
throw new errors_1.SchemaError(`Cannot extract column information for table ${tableName} from schema.ts. ` +
|
|
242
|
+
`This table exists in schema.ts but the column information cannot be determined dynamically. ` +
|
|
243
|
+
`Please ensure your schema.ts table definition is properly structured and accessible.`);
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
logger_1.logger.error(`Error reading columns from schema file for table ${tableName}:`, error);
|
|
247
|
+
throw new errors_1.SchemaError(`Failed to get column information for table ${tableName}: ${error.message}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Get table metadata from schema.ts
|
|
252
|
+
*/
|
|
253
|
+
static getTableMetadata(tableName) {
|
|
254
|
+
const table = this.getTable(tableName);
|
|
255
|
+
return {
|
|
256
|
+
name: tableName,
|
|
257
|
+
columns: this.getTableColumns(tableName),
|
|
258
|
+
primaryKey: this.getPrimaryKey(tableName),
|
|
259
|
+
foreignKeys: this.getForeignKeys(tableName),
|
|
260
|
+
indexes: this.getIndexes(tableName),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Get primary key column(s) from schema.ts
|
|
265
|
+
* Works with any table structure - no hardcoded table names!
|
|
266
|
+
*/
|
|
267
|
+
static getPrimaryKey(tableName) {
|
|
268
|
+
const table = this.getTable(tableName);
|
|
269
|
+
// Extract primary key columns from the table definition
|
|
270
|
+
try {
|
|
271
|
+
const columns = table._?.columns;
|
|
272
|
+
if (columns) {
|
|
273
|
+
return Object.keys(columns).filter(colName => {
|
|
274
|
+
const column = columns[colName];
|
|
275
|
+
return column.primary;
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
logger_1.logger.warn(`Could not extract primary key for table ${tableName} from Drizzle table object`);
|
|
281
|
+
}
|
|
282
|
+
// Generic fallback: assume 'id' is the primary key (common pattern)
|
|
283
|
+
// This works for most tables but could be enhanced
|
|
284
|
+
return ['id'];
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Get foreign key relationships from schema.ts
|
|
288
|
+
* Works with any table structure - no hardcoded table names!
|
|
289
|
+
*/
|
|
290
|
+
static getForeignKeys(tableName) {
|
|
291
|
+
const table = this.getTable(tableName);
|
|
292
|
+
// Extract foreign key information from the table definition
|
|
293
|
+
const foreignKeys = [];
|
|
294
|
+
try {
|
|
295
|
+
// Try to extract foreign key information from Drizzle table object
|
|
296
|
+
const columns = table._?.columns;
|
|
297
|
+
if (columns) {
|
|
298
|
+
for (const [colName, column] of Object.entries(columns)) {
|
|
299
|
+
const col = column;
|
|
300
|
+
if (col.references) {
|
|
301
|
+
// Extract foreign key information
|
|
302
|
+
foreignKeys.push({
|
|
303
|
+
column: colName,
|
|
304
|
+
references: col.references.column || 'id',
|
|
305
|
+
table: col.references.table || 'unknown'
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
logger_1.logger.warn(`Could not extract foreign keys for table ${tableName} from Drizzle table object`);
|
|
313
|
+
}
|
|
314
|
+
return foreignKeys;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Get indexes from schema.ts
|
|
318
|
+
* Works with any table structure - no hardcoded table names!
|
|
319
|
+
*/
|
|
320
|
+
static getIndexes(tableName) {
|
|
321
|
+
const table = this.getTable(tableName);
|
|
322
|
+
// Extract index information from the table definition
|
|
323
|
+
const indexes = [];
|
|
324
|
+
try {
|
|
325
|
+
// Try to extract index information from Drizzle table object
|
|
326
|
+
const columns = table._?.columns;
|
|
327
|
+
if (columns) {
|
|
328
|
+
for (const [colName, column] of Object.entries(columns)) {
|
|
329
|
+
const col = column;
|
|
330
|
+
if (col.unique || col.index) {
|
|
331
|
+
indexes.push(colName);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
logger_1.logger.warn(`Could not extract indexes for table ${tableName} from Drizzle table object`);
|
|
338
|
+
}
|
|
339
|
+
return indexes;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Check if database schema matches the current schema definition
|
|
343
|
+
*/
|
|
344
|
+
static async validateSchema() {
|
|
345
|
+
try {
|
|
346
|
+
const db = connection_1.DatabaseConnection.getInstance();
|
|
347
|
+
const expectedTables = this.getAllTableNames();
|
|
348
|
+
const missingTables = [];
|
|
349
|
+
const extraTables = [];
|
|
350
|
+
const errors = [];
|
|
351
|
+
// Get tables from database
|
|
352
|
+
const result = await db.run((0, drizzle_orm_1.sql) `
|
|
353
|
+
SELECT name FROM sqlite_master
|
|
354
|
+
WHERE type='table' AND name NOT LIKE 'sqlite_%'
|
|
355
|
+
`);
|
|
356
|
+
const dbTables = result.rows?.map((row) => row.name) || [];
|
|
357
|
+
// Check for missing tables
|
|
358
|
+
for (const expectedTable of expectedTables) {
|
|
359
|
+
if (!dbTables.includes(expectedTable)) {
|
|
360
|
+
missingTables.push(expectedTable);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
// Check for extra tables
|
|
364
|
+
for (const dbTable of dbTables) {
|
|
365
|
+
if (!expectedTables.includes(dbTable)) {
|
|
366
|
+
extraTables.push(dbTable);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
const isValid = missingTables.length === 0 && errors.length === 0;
|
|
370
|
+
return {
|
|
371
|
+
isValid,
|
|
372
|
+
missingTables,
|
|
373
|
+
extraTables,
|
|
374
|
+
errors,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
catch (error) {
|
|
378
|
+
logger_1.logger.error('Schema validation failed:', error);
|
|
379
|
+
return {
|
|
380
|
+
isValid: false,
|
|
381
|
+
missingTables: [],
|
|
382
|
+
extraTables: [],
|
|
383
|
+
errors: [error.message],
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Get comprehensive schema statistics derived from schema.ts
|
|
389
|
+
*/
|
|
390
|
+
static getSchemaStats() {
|
|
391
|
+
const tableNames = this.getAllTableNames();
|
|
392
|
+
const tables = this.getAllTables();
|
|
393
|
+
// Get detailed information for each table
|
|
394
|
+
const tableDetails = tableNames.map(tableName => ({
|
|
395
|
+
name: tableName,
|
|
396
|
+
columns: this.getTableColumns(tableName),
|
|
397
|
+
primaryKey: this.getPrimaryKey(tableName),
|
|
398
|
+
foreignKeys: this.getForeignKeys(tableName),
|
|
399
|
+
indexes: this.getIndexes(tableName),
|
|
400
|
+
}));
|
|
401
|
+
return {
|
|
402
|
+
totalTables: tableNames.length,
|
|
403
|
+
tableNames,
|
|
404
|
+
tables: Object.keys(tables),
|
|
405
|
+
tableDetails,
|
|
406
|
+
lastUpdated: new Date().toISOString(),
|
|
407
|
+
sourceOfTruth: 'schema.ts',
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Get table relationships derived from schema.ts
|
|
412
|
+
*/
|
|
413
|
+
static getTableRelationships() {
|
|
414
|
+
const relationships = [];
|
|
415
|
+
const tableNames = this.getAllTableNames();
|
|
416
|
+
for (const tableName of tableNames) {
|
|
417
|
+
const foreignKeys = this.getForeignKeys(tableName);
|
|
418
|
+
for (const fk of foreignKeys) {
|
|
419
|
+
relationships.push({
|
|
420
|
+
fromTable: tableName,
|
|
421
|
+
fromColumn: fk.column,
|
|
422
|
+
toTable: fk.table,
|
|
423
|
+
toColumn: fk.references,
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return relationships;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Get schema validation rules derived from schema.ts
|
|
431
|
+
* Works with any table structure - no hardcoded table names!
|
|
432
|
+
*/
|
|
433
|
+
static getSchemaValidationRules() {
|
|
434
|
+
const rules = [];
|
|
435
|
+
const tableNames = this.getAllTableNames();
|
|
436
|
+
for (const tableName of tableNames) {
|
|
437
|
+
const columns = this.getTableColumns(tableName);
|
|
438
|
+
const foreignKeys = this.getForeignKeys(tableName);
|
|
439
|
+
const indexes = this.getIndexes(tableName);
|
|
440
|
+
for (const column of columns) {
|
|
441
|
+
// Add validation rules based on column patterns and metadata
|
|
442
|
+
if (indexes.includes(column)) {
|
|
443
|
+
rules.push({
|
|
444
|
+
table: tableName,
|
|
445
|
+
column,
|
|
446
|
+
rule: 'unique',
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
if (column.includes('_id')) {
|
|
450
|
+
rules.push({
|
|
451
|
+
table: tableName,
|
|
452
|
+
column,
|
|
453
|
+
rule: 'not_null',
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
// Check if this column is a foreign key
|
|
457
|
+
const fk = foreignKeys.find(f => f.column === column);
|
|
458
|
+
if (fk) {
|
|
459
|
+
rules.push({
|
|
460
|
+
table: tableName,
|
|
461
|
+
column,
|
|
462
|
+
rule: 'foreign_key',
|
|
463
|
+
value: `${fk.table}.${fk.references}`,
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return rules;
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Get schema export for documentation or migration purposes
|
|
472
|
+
*/
|
|
473
|
+
static exportSchemaDefinition() {
|
|
474
|
+
const tableNames = this.getAllTableNames();
|
|
475
|
+
const schemaDefinition = {
|
|
476
|
+
tables: {},
|
|
477
|
+
relationships: this.getTableRelationships(),
|
|
478
|
+
validationRules: this.getSchemaValidationRules(),
|
|
479
|
+
metadata: {
|
|
480
|
+
totalTables: tableNames.length,
|
|
481
|
+
exportedAt: new Date().toISOString(),
|
|
482
|
+
sourceFile: 'schema.ts',
|
|
483
|
+
},
|
|
484
|
+
};
|
|
485
|
+
// Add table definitions
|
|
486
|
+
for (const tableName of tableNames) {
|
|
487
|
+
schemaDefinition.tables[tableName] = {
|
|
488
|
+
columns: this.getTableColumns(tableName),
|
|
489
|
+
primaryKey: this.getPrimaryKey(tableName),
|
|
490
|
+
foreignKeys: this.getForeignKeys(tableName),
|
|
491
|
+
indexes: this.getIndexes(tableName),
|
|
492
|
+
createStatement: this.generateCreateTableStatement(tableName),
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
return schemaDefinition;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
exports.SchemaIntrospection = SchemaIntrospection;
|
|
499
|
+
SchemaIntrospection.tables = {};
|
|
500
|
+
SchemaIntrospection.tableNames = [];
|
|
501
|
+
//# sourceMappingURL=schema-introspection.js.map
|