@onurege3467/zerohelper 6.0.2 → 6.2.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/.snapshots/config.json +151 -0
- package/.snapshots/readme.md +11 -0
- package/.snapshots/sponsors.md +44 -0
- package/database/mysql.js +210 -18
- package/database/sqlite.js +113 -6
- package/package.json +1 -1
- package/test.js +5 -4
- package/data/test_db.json +0 -3
- package/data/test_db.sqlite +0 -0
- package/data/test_db_cached.sqlite +0 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
{
|
|
2
|
+
"excluded_patterns": [
|
|
3
|
+
".git",
|
|
4
|
+
".gitignore",
|
|
5
|
+
"gradle",
|
|
6
|
+
"gradlew",
|
|
7
|
+
"gradlew.*",
|
|
8
|
+
"node_modules",
|
|
9
|
+
".snapshots",
|
|
10
|
+
".idea",
|
|
11
|
+
".vscode",
|
|
12
|
+
"*.log",
|
|
13
|
+
"*.tmp",
|
|
14
|
+
"target",
|
|
15
|
+
"dist",
|
|
16
|
+
"build",
|
|
17
|
+
".DS_Store",
|
|
18
|
+
"*.bak",
|
|
19
|
+
"*.swp",
|
|
20
|
+
"*.swo",
|
|
21
|
+
"*.lock",
|
|
22
|
+
"*.iml",
|
|
23
|
+
"coverage",
|
|
24
|
+
"*.min.js",
|
|
25
|
+
"*.min.css",
|
|
26
|
+
"__pycache__",
|
|
27
|
+
".marketing",
|
|
28
|
+
".env",
|
|
29
|
+
".env.*",
|
|
30
|
+
"*.jpg",
|
|
31
|
+
"*.jpeg",
|
|
32
|
+
"*.png",
|
|
33
|
+
"*.gif",
|
|
34
|
+
"*.bmp",
|
|
35
|
+
"*.tiff",
|
|
36
|
+
"*.ico",
|
|
37
|
+
"*.svg",
|
|
38
|
+
"*.webp",
|
|
39
|
+
"*.psd",
|
|
40
|
+
"*.ai",
|
|
41
|
+
"*.eps",
|
|
42
|
+
"*.indd",
|
|
43
|
+
"*.raw",
|
|
44
|
+
"*.cr2",
|
|
45
|
+
"*.nef",
|
|
46
|
+
"*.mp4",
|
|
47
|
+
"*.mov",
|
|
48
|
+
"*.avi",
|
|
49
|
+
"*.wmv",
|
|
50
|
+
"*.flv",
|
|
51
|
+
"*.mkv",
|
|
52
|
+
"*.webm",
|
|
53
|
+
"*.m4v",
|
|
54
|
+
"*.wfp",
|
|
55
|
+
"*.prproj",
|
|
56
|
+
"*.aep",
|
|
57
|
+
"*.psb",
|
|
58
|
+
"*.xcf",
|
|
59
|
+
"*.sketch",
|
|
60
|
+
"*.fig",
|
|
61
|
+
"*.xd",
|
|
62
|
+
"*.db",
|
|
63
|
+
"*.sqlite",
|
|
64
|
+
"*.sqlite3",
|
|
65
|
+
"*.mdb",
|
|
66
|
+
"*.accdb",
|
|
67
|
+
"*.frm",
|
|
68
|
+
"*.myd",
|
|
69
|
+
"*.myi",
|
|
70
|
+
"*.ibd",
|
|
71
|
+
"*.dbf",
|
|
72
|
+
"*.rdb",
|
|
73
|
+
"*.aof",
|
|
74
|
+
"*.pdb",
|
|
75
|
+
"*.sdb",
|
|
76
|
+
"*.s3db",
|
|
77
|
+
"*.ddb",
|
|
78
|
+
"*.db-shm",
|
|
79
|
+
"*.db-wal",
|
|
80
|
+
"*.sqlitedb",
|
|
81
|
+
"*.sql.gz",
|
|
82
|
+
"*.bak.sql",
|
|
83
|
+
"dump.sql",
|
|
84
|
+
"dump.rdb",
|
|
85
|
+
"*.vsix",
|
|
86
|
+
"*.jar",
|
|
87
|
+
"*.war",
|
|
88
|
+
"*.ear",
|
|
89
|
+
"*.zip",
|
|
90
|
+
"*.tar",
|
|
91
|
+
"*.tar.gz",
|
|
92
|
+
"*.tgz",
|
|
93
|
+
"*.rar",
|
|
94
|
+
"*.7z",
|
|
95
|
+
"*.exe",
|
|
96
|
+
"*.dll",
|
|
97
|
+
"*.so",
|
|
98
|
+
"*.dylib",
|
|
99
|
+
"*.app",
|
|
100
|
+
"*.dmg",
|
|
101
|
+
"*.iso",
|
|
102
|
+
"*.msi",
|
|
103
|
+
"*.deb",
|
|
104
|
+
"*.rpm",
|
|
105
|
+
"*.apk",
|
|
106
|
+
"*.aab",
|
|
107
|
+
"*.ipa",
|
|
108
|
+
"*.pkg",
|
|
109
|
+
"*.nupkg",
|
|
110
|
+
"*.snap",
|
|
111
|
+
"*.whl",
|
|
112
|
+
"*.gem",
|
|
113
|
+
"*.pyc",
|
|
114
|
+
"*.pyo",
|
|
115
|
+
"*.pyd",
|
|
116
|
+
"*.class",
|
|
117
|
+
"*.o",
|
|
118
|
+
"*.obj",
|
|
119
|
+
"*.lib",
|
|
120
|
+
"*.a",
|
|
121
|
+
"*.map",
|
|
122
|
+
".npmrc"
|
|
123
|
+
],
|
|
124
|
+
"default": {
|
|
125
|
+
"default_prompt": "Enter your prompt here",
|
|
126
|
+
"default_include_all_files": false,
|
|
127
|
+
"default_include_entire_project_structure": true
|
|
128
|
+
},
|
|
129
|
+
"included_patterns": [
|
|
130
|
+
"build.gradle",
|
|
131
|
+
"settings.gradle",
|
|
132
|
+
"gradle.properties",
|
|
133
|
+
"pom.xml",
|
|
134
|
+
"Makefile",
|
|
135
|
+
"CMakeLists.txt",
|
|
136
|
+
"package.json",
|
|
137
|
+
"requirements.txt",
|
|
138
|
+
"Pipfile",
|
|
139
|
+
"Gemfile",
|
|
140
|
+
"composer.json",
|
|
141
|
+
".editorconfig",
|
|
142
|
+
".eslintrc.json",
|
|
143
|
+
".eslintrc.js",
|
|
144
|
+
".prettierrc",
|
|
145
|
+
".babelrc",
|
|
146
|
+
".dockerignore",
|
|
147
|
+
".gitattributes",
|
|
148
|
+
".stylelintrc",
|
|
149
|
+
".npmrc"
|
|
150
|
+
]
|
|
151
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Snapshots Directory
|
|
2
|
+
|
|
3
|
+
This directory contains snapshots of your code for AI interactions. Each snapshot is a markdown file that includes relevant code context and project structure information.
|
|
4
|
+
|
|
5
|
+
## What's included in snapshots?
|
|
6
|
+
- Selected code files and their contents
|
|
7
|
+
- Project structure (if enabled)
|
|
8
|
+
- Your prompt/question for the AI
|
|
9
|
+
|
|
10
|
+
## Configuration
|
|
11
|
+
You can customize snapshot behavior in `config.json`.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Thank you for using Snapshots for AI
|
|
2
|
+
|
|
3
|
+
Thanks for using Snapshots for AI. We hope this tool has helped you solve a problem or two.
|
|
4
|
+
|
|
5
|
+
If you would like to support our work, please help us by considering the following offers and requests:
|
|
6
|
+
|
|
7
|
+
## Ways to Support
|
|
8
|
+
|
|
9
|
+
### Join the GBTI Network!!! 🙏🙏🙏
|
|
10
|
+
The GBTI Network is a community of developers who are passionate about open source and community-driven development. Members enjoy access to exclussive tools, resources, a private MineCraft server, a listing in our members directory, co-op opportunities and more.
|
|
11
|
+
|
|
12
|
+
- Support our work by becoming a [GBTI Network member](https://gbti.network/membership/).
|
|
13
|
+
|
|
14
|
+
### Try out BugHerd 🐛
|
|
15
|
+
BugHerd is a visual feedback and bug-tracking tool designed to streamline website development by enabling users to pin feedback directly onto web pages. This approach facilitates clear communication among clients, designers, developers, and project managers.
|
|
16
|
+
|
|
17
|
+
- Start your free trial with [BugHerd](https://partners.bugherd.com/55z6c8az8rvr) today.
|
|
18
|
+
|
|
19
|
+
### Hire Developers from Codeable 👥
|
|
20
|
+
Codeable connects you with top-tier professionals skilled in frameworks and technologies such as Laravel, React, Django, Node, Vue.js, Angular, Ruby on Rails, and Node.js. Don't let the WordPress focus discourage you. Codeable experts do it all.
|
|
21
|
+
|
|
22
|
+
- Visit [Codeable](https://www.codeable.io/developers/?ref=z8h3e) to hire your next team member.
|
|
23
|
+
|
|
24
|
+
### Lead positive reviews on our marketplace listing ⭐⭐⭐⭐⭐
|
|
25
|
+
- Rate us on [VSCode marketplace](https://marketplace.visualstudio.com/items?itemName=GBTI.snapshots-for-ai)
|
|
26
|
+
- Review us on [Cursor marketplace](https://open-vsx.org/extension/GBTI/snapshots-for-ai)
|
|
27
|
+
|
|
28
|
+
### Star Our GitHub Repository ⭐
|
|
29
|
+
- Star and watch our [repository](https://github.com/gbti-network/vscode-snapshots-for-ai)
|
|
30
|
+
|
|
31
|
+
### 📡 Stay Connected
|
|
32
|
+
Follow us on your favorite platforms for updates, news, and community discussions:
|
|
33
|
+
- **[Twitter/X](https://twitter.com/gbti_network)**
|
|
34
|
+
- **[GitHub](https://github.com/gbti-network)**
|
|
35
|
+
- **[YouTube](https://www.youtube.com/channel/UCh4FjB6r4oWQW-QFiwqv-UA)**
|
|
36
|
+
- **[Dev.to](https://dev.to/gbti)**
|
|
37
|
+
- **[Daily.dev](https://dly.to/zfCriM6JfRF)**
|
|
38
|
+
- **[Hashnode](https://gbti.hashnode.dev/)**
|
|
39
|
+
- **[Discord Community](https://gbti.network)**
|
|
40
|
+
- **[Reddit Community](https://www.reddit.com/r/GBTI_network)**
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
Thank you for supporting open source software! 🙏
|
package/database/mysql.js
CHANGED
|
@@ -20,6 +20,15 @@ class MySQLDatabase extends IDatabase{
|
|
|
20
20
|
port: config.port || 3306,
|
|
21
21
|
user: config.user,
|
|
22
22
|
password: config.password,
|
|
23
|
+
typeCast: function (field, next) {
|
|
24
|
+
if (field.type === 'TINY' && field.length === 1) {
|
|
25
|
+
return (field.string() === '1'); // Convert tinyint(1) to boolean
|
|
26
|
+
}
|
|
27
|
+
if (field.type === 'INT' || field.type === 'DECIMAL' || field.type === 'NEWDECIMAL' || field.type === 'FLOAT' || field.type === 'DOUBLE') {
|
|
28
|
+
return Number(field.string()); // Convert numeric types to Number
|
|
29
|
+
}
|
|
30
|
+
return next();
|
|
31
|
+
}
|
|
23
32
|
});
|
|
24
33
|
|
|
25
34
|
await connection.query(`CREATE DATABASE IF NOT EXISTS \`${config.database}\``);
|
|
@@ -34,6 +43,15 @@ class MySQLDatabase extends IDatabase{
|
|
|
34
43
|
waitForConnections: true,
|
|
35
44
|
connectionLimit: config.connectionLimit || 10,
|
|
36
45
|
queueLimit: 0,
|
|
46
|
+
typeCast: function (field, next) {
|
|
47
|
+
if (field.type === 'TINY' && field.length === 1) {
|
|
48
|
+
return (field.string() === '1'); // Convert tinyint(1) to boolean
|
|
49
|
+
}
|
|
50
|
+
if (field.type === 'INT' || field.type === 'DECIMAL' || field.type === 'NEWDECIMAL' || field.type === 'FLOAT' || field.type === 'DOUBLE') {
|
|
51
|
+
return Number(field.string()); // Convert numeric types to Number
|
|
52
|
+
}
|
|
53
|
+
return next();
|
|
54
|
+
}
|
|
37
55
|
});
|
|
38
56
|
|
|
39
57
|
this._connected = true;
|
|
@@ -46,6 +64,111 @@ class MySQLDatabase extends IDatabase{
|
|
|
46
64
|
});
|
|
47
65
|
}
|
|
48
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Değerin türüne göre MySQL column türünü otomatik belirler
|
|
69
|
+
*/
|
|
70
|
+
_getColumnType(value) {
|
|
71
|
+
if (value === null || value === undefined) {
|
|
72
|
+
return 'TEXT'; // Null değerler için varsayılan
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (typeof value === 'boolean') {
|
|
76
|
+
return 'BOOLEAN';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (typeof value === 'number') {
|
|
80
|
+
if (Number.isInteger(value)) {
|
|
81
|
+
// Integer range kontrolü
|
|
82
|
+
if (value >= -128 && value <= 127) {
|
|
83
|
+
return 'TINYINT';
|
|
84
|
+
} else if (value >= -32768 && value <= 32767) {
|
|
85
|
+
return 'SMALLINT';
|
|
86
|
+
} else if (value >= -2147483648 && value <= 2147483647) {
|
|
87
|
+
return 'INT';
|
|
88
|
+
} else {
|
|
89
|
+
return 'BIGINT';
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
// Float/Double için
|
|
93
|
+
return 'DOUBLE';
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (typeof value === 'string') {
|
|
98
|
+
const length = value.length;
|
|
99
|
+
if (length <= 255) {
|
|
100
|
+
return 'VARCHAR(255)';
|
|
101
|
+
} else if (length <= 65535) {
|
|
102
|
+
return 'TEXT';
|
|
103
|
+
} else if (length <= 16777215) {
|
|
104
|
+
return 'MEDIUMTEXT';
|
|
105
|
+
} else {
|
|
106
|
+
return 'LONGTEXT';
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (typeof value === 'object') {
|
|
111
|
+
// Array ve Object'ler JSON olarak saklanır
|
|
112
|
+
const jsonString = JSON.stringify(value);
|
|
113
|
+
const length = jsonString.length;
|
|
114
|
+
if (length <= 65535) {
|
|
115
|
+
return 'JSON';
|
|
116
|
+
} else {
|
|
117
|
+
return 'LONGTEXT';
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (value instanceof Date) {
|
|
122
|
+
return 'DATETIME';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Varsayılan
|
|
126
|
+
return 'TEXT';
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Birden fazla değere göre en uygun column türünü belirler
|
|
131
|
+
*/
|
|
132
|
+
_getBestColumnType(values) {
|
|
133
|
+
const types = values.map(val => this._getColumnType(val));
|
|
134
|
+
const uniqueTypes = [...new Set(types)];
|
|
135
|
+
|
|
136
|
+
// Eğer hepsi aynı türse, o türü kullan
|
|
137
|
+
if (uniqueTypes.length === 1) {
|
|
138
|
+
return uniqueTypes[0];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Mixed türler için öncelik sırası
|
|
142
|
+
const typePriority = {
|
|
143
|
+
'LONGTEXT': 10,
|
|
144
|
+
'MEDIUMTEXT': 9,
|
|
145
|
+
'TEXT': 8,
|
|
146
|
+
'JSON': 7,
|
|
147
|
+
'VARCHAR(255)': 6,
|
|
148
|
+
'DATETIME': 5,
|
|
149
|
+
'DOUBLE': 4,
|
|
150
|
+
'BIGINT': 3,
|
|
151
|
+
'INT': 2,
|
|
152
|
+
'SMALLINT': 1,
|
|
153
|
+
'TINYINT': 0,
|
|
154
|
+
'BOOLEAN': -1
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// En yüksek öncelikli türü seç
|
|
158
|
+
let bestType = uniqueTypes[0];
|
|
159
|
+
let bestPriority = typePriority[bestType] || 0;
|
|
160
|
+
|
|
161
|
+
for (const type of uniqueTypes) {
|
|
162
|
+
const priority = typePriority[type] || 0;
|
|
163
|
+
if (priority > bestPriority) {
|
|
164
|
+
bestType = type;
|
|
165
|
+
bestPriority = priority;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return bestType;
|
|
170
|
+
}
|
|
171
|
+
|
|
49
172
|
async _queueRequest(operation) {
|
|
50
173
|
if (this._connected) {
|
|
51
174
|
return operation();
|
|
@@ -80,12 +203,25 @@ class MySQLDatabase extends IDatabase{
|
|
|
80
203
|
|
|
81
204
|
async ensureTable(table, data = {}) {
|
|
82
205
|
return this._queueRequest(async () => {
|
|
83
|
-
|
|
84
|
-
|
|
85
206
|
const escapedTable = mysql.escape(table);
|
|
86
207
|
const tables = await this.query(`SHOW TABLES LIKE ${escapedTable}`);
|
|
87
208
|
if (tables.length === 0) {
|
|
88
|
-
const columnDefinitions = Object.keys(data).map(col =>
|
|
209
|
+
const columnDefinitions = Object.keys(data).map(col => {
|
|
210
|
+
const columnType = this._getColumnType(data[col]);
|
|
211
|
+
return `\`${col}\` ${columnType}`;
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
let columnsPart = '';
|
|
215
|
+
if (columnDefinitions.length > 0) {
|
|
216
|
+
columnsPart = ', ' + columnDefinitions.join(", ");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const createTableSQL = `
|
|
220
|
+
CREATE TABLE \`${table}\` (
|
|
221
|
+
_id INT PRIMARY KEY AUTO_INCREMENT
|
|
222
|
+
${columnsPart}
|
|
223
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
|
224
|
+
`;
|
|
89
225
|
await this.query(createTableSQL);
|
|
90
226
|
}
|
|
91
227
|
});
|
|
@@ -99,15 +235,24 @@ class MySQLDatabase extends IDatabase{
|
|
|
99
235
|
if (!existingColumns) throw new Error(`Table ${table} does not exist.`);
|
|
100
236
|
|
|
101
237
|
const existingNames = existingColumns.map(col => col.Field);
|
|
102
|
-
|
|
238
|
+
const primaryKeyColumn = existingColumns.find(col => col.Key === 'PRI' && col.Extra.includes('auto_increment'));
|
|
239
|
+
|
|
240
|
+
const insertData = { ...copy };
|
|
241
|
+
// Remove the auto-incrementing primary key from insertData if it's present
|
|
242
|
+
if (primaryKeyColumn && insertData[primaryKeyColumn.Field] !== undefined) {
|
|
243
|
+
delete insertData[primaryKeyColumn.Field];
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
for (const key of Object.keys(insertData)) {
|
|
103
247
|
if (!existingNames.includes(key)) {
|
|
104
|
-
|
|
248
|
+
const columnType = this._getColumnType(insertData[key]);
|
|
249
|
+
await this.query(`ALTER TABLE \`${table}\` ADD COLUMN \`${key}\` ${columnType}`);
|
|
105
250
|
}
|
|
106
251
|
}
|
|
107
252
|
|
|
108
|
-
const keys = Object.keys(
|
|
253
|
+
const keys = Object.keys(insertData);
|
|
109
254
|
const placeholders = keys.map(() => "?").join(",");
|
|
110
|
-
const values = Object.values(
|
|
255
|
+
const values = Object.values(insertData).map(value => this._serializeValue(value));
|
|
111
256
|
const sql = `INSERT INTO \`${table}\` (${keys.map(k => `\`${k}\``).join(",")}) VALUES (${placeholders})`;
|
|
112
257
|
|
|
113
258
|
const result = await this.query(sql, values);
|
|
@@ -124,9 +269,10 @@ class MySQLDatabase extends IDatabase{
|
|
|
124
269
|
const existingColumnNames = existingColumns.map(col => col.Field);
|
|
125
270
|
for (const key of Object.keys(data)) {
|
|
126
271
|
if (!existingColumnNames.includes(key)) {
|
|
127
|
-
const
|
|
272
|
+
const columnType = this._getColumnType(data[key]);
|
|
273
|
+
const alterSQL = `ALTER TABLE \`${table}\` ADD COLUMN \`${key}\` ${columnType}`;
|
|
128
274
|
await this.query(alterSQL);
|
|
129
|
-
console.log(`Added missing column '${key}' to table '${table}'`);
|
|
275
|
+
console.log(`Added missing column '${key}' to table '${table}' with type ${columnType}`);
|
|
130
276
|
}
|
|
131
277
|
}
|
|
132
278
|
const setString = Object.keys(data).map(k => `\`${k}\` = ?`).join(", ");
|
|
@@ -160,7 +306,13 @@ class MySQLDatabase extends IDatabase{
|
|
|
160
306
|
params = Object.values(where);
|
|
161
307
|
}
|
|
162
308
|
|
|
163
|
-
return await this.query(sql, params)
|
|
309
|
+
return (await this.query(sql, params)).map(row => {
|
|
310
|
+
const newRow = {};
|
|
311
|
+
for (const key in row) {
|
|
312
|
+
newRow[key] = this._deserializeValue(row[key]);
|
|
313
|
+
}
|
|
314
|
+
return newRow;
|
|
315
|
+
});
|
|
164
316
|
});
|
|
165
317
|
}
|
|
166
318
|
|
|
@@ -173,9 +325,10 @@ class MySQLDatabase extends IDatabase{
|
|
|
173
325
|
const existingColumnNames = existingColumns.map(col => col.Field);
|
|
174
326
|
for (const key of Object.keys(data)) {
|
|
175
327
|
if (!existingColumnNames.includes(key)) {
|
|
176
|
-
const
|
|
328
|
+
const columnType = this._getColumnType(data[key]);
|
|
329
|
+
const alterSQL = `ALTER TABLE \`${table}\` ADD COLUMN \`${key}\` ${columnType}`;
|
|
177
330
|
await this.query(alterSQL);
|
|
178
|
-
console.log(`Added missing column '${key}' to table '${table}'`);
|
|
331
|
+
console.log(`Added missing column '${key}' to table '${table}' with type ${columnType}`);
|
|
179
332
|
}
|
|
180
333
|
}
|
|
181
334
|
const existing = await this.select(table, where);
|
|
@@ -190,7 +343,14 @@ class MySQLDatabase extends IDatabase{
|
|
|
190
343
|
async selectOne(table, where = null) {
|
|
191
344
|
return this._queueRequest(async () => {
|
|
192
345
|
const results = await this.select(table, where);
|
|
193
|
-
|
|
346
|
+
if (results[0]) {
|
|
347
|
+
const newResult = {};
|
|
348
|
+
for (const key in results[0]) {
|
|
349
|
+
newResult[key] = this._deserializeValue(results[0][key]);
|
|
350
|
+
}
|
|
351
|
+
return newResult;
|
|
352
|
+
}
|
|
353
|
+
return null;
|
|
194
354
|
});
|
|
195
355
|
}
|
|
196
356
|
|
|
@@ -214,9 +374,10 @@ class MySQLDatabase extends IDatabase{
|
|
|
214
374
|
const existingColumnNames = existingColumns.map(col => col.Field);
|
|
215
375
|
for (const key of Object.keys(data)) {
|
|
216
376
|
if (!existingColumnNames.includes(key)) {
|
|
217
|
-
const
|
|
377
|
+
const columnType = this._getColumnType(data[key]);
|
|
378
|
+
const alterSQL = `ALTER TABLE \`${table}\` ADD COLUMN \`${key}\` ${columnType}`;
|
|
218
379
|
await this.query(alterSQL);
|
|
219
|
-
console.log(`Added missing column '${key}' to table '${table}'`);
|
|
380
|
+
console.log(`Added missing column '${key}' to table '${table}' with type ${columnType}`);
|
|
220
381
|
}
|
|
221
382
|
}
|
|
222
383
|
const setString = Object.keys(data).map(k => `\`${k}\` = ?`).join(", ");
|
|
@@ -236,15 +397,18 @@ class MySQLDatabase extends IDatabase{
|
|
|
236
397
|
const existingColumnNames = existingColumns.map(col => col.Field);
|
|
237
398
|
const keys = Object.keys(dataArray[0]);
|
|
238
399
|
|
|
239
|
-
// Eksik kolonları
|
|
400
|
+
// Eksik kolonları kontrol et ve ekle
|
|
240
401
|
for (const key of keys) {
|
|
241
402
|
if (!existingColumnNames.includes(key)) {
|
|
242
|
-
|
|
403
|
+
// Tüm değerleri kontrol ederek en uygun türü belirle
|
|
404
|
+
const columnValues = dataArray.map(obj => obj[key]).filter(val => val !== undefined && val !== null);
|
|
405
|
+
const columnType = columnValues.length > 0 ? this._getBestColumnType(columnValues) : 'TEXT';
|
|
406
|
+
await this.query(`ALTER TABLE \`${table}\` ADD COLUMN \`${key}\` ${columnType}`);
|
|
243
407
|
}
|
|
244
408
|
}
|
|
245
409
|
|
|
246
410
|
const placeholders = dataArray.map(() => `(${keys.map(() => '?').join(',')})`).join(',');
|
|
247
|
-
const values = dataArray.flatMap(obj => keys.map(k => obj[k]));
|
|
411
|
+
const values = dataArray.flatMap(obj => keys.map(k => this._serializeValue(obj[k])));
|
|
248
412
|
const sql = `INSERT INTO \`${table}\` (${keys.map(k => `\`${k}\``).join(",")}) VALUES ${placeholders}`;
|
|
249
413
|
|
|
250
414
|
const result = await this.query(sql, values);
|
|
@@ -255,6 +419,34 @@ class MySQLDatabase extends IDatabase{
|
|
|
255
419
|
async close() {
|
|
256
420
|
if (this.pool) await this.pool.end();
|
|
257
421
|
}
|
|
422
|
+
|
|
423
|
+
// Helper to serialize values for storage
|
|
424
|
+
_serializeValue(value) {
|
|
425
|
+
if (value instanceof Date) {
|
|
426
|
+
return value.toISOString().slice(0, 19).replace('T', ' '); // MySQL DATETIME format
|
|
427
|
+
}
|
|
428
|
+
if (Array.isArray(value) || (typeof value === 'object' && value !== null)) {
|
|
429
|
+
return JSON.stringify(value);
|
|
430
|
+
}
|
|
431
|
+
return value;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Helper to deserialize values after retrieval
|
|
435
|
+
_deserializeValue(value) {
|
|
436
|
+
try {
|
|
437
|
+
// Attempt to parse only if it looks like a JSON string (e.g., starts with [ or {)
|
|
438
|
+
if (typeof value === 'string' && (value.startsWith('[') || value.startsWith('{'))) {
|
|
439
|
+
const parsed = JSON.parse(value);
|
|
440
|
+
// Ensure it was actually an object or array, not just a string that happened to be valid JSON
|
|
441
|
+
if (typeof parsed === 'object' && parsed !== null) {
|
|
442
|
+
return parsed;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
} catch (e) {
|
|
446
|
+
// Not a valid JSON string, return original value
|
|
447
|
+
}
|
|
448
|
+
return value;
|
|
449
|
+
}
|
|
258
450
|
}
|
|
259
451
|
|
|
260
452
|
module.exports = MySQLDatabase;
|
package/database/sqlite.js
CHANGED
|
@@ -33,6 +33,93 @@ class SQLiteDatabase extends IDatabase{
|
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Değerin türünü otomatik olarak tespit eder ve SQLite türünü döndürür.
|
|
38
|
+
* @param {any} value - Kontrol edilecek değer
|
|
39
|
+
* @returns {string} SQLite veri türü
|
|
40
|
+
*/
|
|
41
|
+
_detectColumnType(value) {
|
|
42
|
+
if (value === null || value === undefined) {
|
|
43
|
+
return 'TEXT'; // Varsayılan olarak TEXT
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Boolean kontrolü
|
|
47
|
+
if (typeof value === 'boolean') {
|
|
48
|
+
return 'BOOLEAN';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Number kontrolü
|
|
52
|
+
if (typeof value === 'number') {
|
|
53
|
+
// Tam sayı mı ondalık mı kontrol et
|
|
54
|
+
if (Number.isInteger(value)) {
|
|
55
|
+
return 'INTEGER';
|
|
56
|
+
} else {
|
|
57
|
+
return 'REAL';
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Date kontrolü
|
|
62
|
+
if (value instanceof Date) {
|
|
63
|
+
return 'DATETIME';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// String kontrolü - sayısal string'leri kontrol et
|
|
67
|
+
if (typeof value === 'string') {
|
|
68
|
+
// Boş string kontrolü
|
|
69
|
+
if (value.trim() === '') {
|
|
70
|
+
return 'TEXT';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Tam sayı string kontrolü
|
|
74
|
+
if (/^-?\d+$/.test(value.trim())) {
|
|
75
|
+
return 'INTEGER';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Ondalık sayı string kontrolü
|
|
79
|
+
if (/^-?\d+\.\d+$/.test(value.trim())) {
|
|
80
|
+
return 'REAL';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Boolean string kontrolü
|
|
84
|
+
const lowerValue = value.toLowerCase().trim();
|
|
85
|
+
if (lowerValue === 'true' || lowerValue === 'false') {
|
|
86
|
+
return 'BOOLEAN';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ISO date string kontrolü
|
|
90
|
+
if (value.match(/^\d{4}-\d{2}-\d{2}/) || value.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/)) {
|
|
91
|
+
return 'DATETIME';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Varsayılan olarak TEXT
|
|
96
|
+
return 'TEXT';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Birden fazla değerden en uygun sütun türünü belirler.
|
|
101
|
+
* @param {Array} values - Değer dizisi
|
|
102
|
+
* @returns {string} En uygun SQLite veri türü
|
|
103
|
+
*/
|
|
104
|
+
_determineBestColumnType(values) {
|
|
105
|
+
const types = values.map(v => this._detectColumnType(v));
|
|
106
|
+
|
|
107
|
+
// Tür öncelik sırası: INTEGER < REAL < BOOLEAN < DATETIME < TEXT
|
|
108
|
+
const typePriority = {
|
|
109
|
+
'INTEGER': 1,
|
|
110
|
+
'REAL': 2,
|
|
111
|
+
'BOOLEAN': 3,
|
|
112
|
+
'DATETIME': 4,
|
|
113
|
+
'TEXT': 5
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// En yüksek öncelikli türü seç
|
|
117
|
+
const maxPriority = Math.max(...types.map(t => typePriority[t] || 5));
|
|
118
|
+
const bestType = Object.keys(typePriority).find(t => typePriority[t] === maxPriority);
|
|
119
|
+
|
|
120
|
+
return bestType || 'TEXT';
|
|
121
|
+
}
|
|
122
|
+
|
|
36
123
|
/**
|
|
37
124
|
* SQL sorgusu çalıştırır. Promise döndürür.
|
|
38
125
|
* @param {string} sql - Çalıştırılacak SQL sorgusu.
|
|
@@ -62,13 +149,26 @@ class SQLiteDatabase extends IDatabase{
|
|
|
62
149
|
* @param {object} data - Tablo oluşturulurken sütunları belirlemek için örnek veri.
|
|
63
150
|
*/
|
|
64
151
|
async ensureTable(table, data = {}) {
|
|
65
|
-
|
|
66
|
-
|
|
67
152
|
try {
|
|
68
153
|
await this.query(`SELECT 1 FROM \`${table}\` LIMIT 1`);
|
|
69
154
|
} catch (error) {
|
|
70
155
|
if (error.message.includes('no such table')) {
|
|
71
|
-
const columnDefinitions = Object.keys(data).map(col =>
|
|
156
|
+
const columnDefinitions = Object.keys(data).map(col => {
|
|
157
|
+
const columnType = this._detectColumnType(data[col]);
|
|
158
|
+
return `\`${col}\` ${columnType}`;
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
let columnsPart = '';
|
|
162
|
+
if (columnDefinitions.length > 0) {
|
|
163
|
+
columnsPart = ', ' + columnDefinitions.join(", ");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const createTableSQL = `
|
|
167
|
+
CREATE TABLE \`${table}\` (
|
|
168
|
+
_id INTEGER PRIMARY KEY AUTOINCREMENT
|
|
169
|
+
${columnsPart}
|
|
170
|
+
)
|
|
171
|
+
`;
|
|
72
172
|
await this.query(createTableSQL);
|
|
73
173
|
} else {
|
|
74
174
|
throw error;
|
|
@@ -86,7 +186,8 @@ class SQLiteDatabase extends IDatabase{
|
|
|
86
186
|
|
|
87
187
|
for (const key of Object.keys(data)) {
|
|
88
188
|
if (!existingNames.includes(key)) {
|
|
89
|
-
|
|
189
|
+
const columnType = this._detectColumnType(data[key]);
|
|
190
|
+
await this.query(`ALTER TABLE \`${table}\` ADD COLUMN \`${key}\` ${columnType}`);
|
|
90
191
|
}
|
|
91
192
|
}
|
|
92
193
|
}
|
|
@@ -211,8 +312,14 @@ class SQLiteDatabase extends IDatabase{
|
|
|
211
312
|
*/
|
|
212
313
|
async bulkInsert(table, dataArray) {
|
|
213
314
|
if (!Array.isArray(dataArray) || dataArray.length === 0) return 0;
|
|
315
|
+
|
|
316
|
+
// İlk elemanı tablo oluşturmak için kullan
|
|
214
317
|
await this.ensureTable(table, dataArray[0]);
|
|
215
|
-
|
|
318
|
+
|
|
319
|
+
// Tüm elemanların sütunlarını kontrol et
|
|
320
|
+
for (const item of dataArray) {
|
|
321
|
+
await this._ensureColumns(table, item);
|
|
322
|
+
}
|
|
216
323
|
|
|
217
324
|
const keys = Object.keys(dataArray[0]);
|
|
218
325
|
const placeholders = keys.map(() => '?').join(',');
|
|
@@ -244,4 +351,4 @@ class SQLiteDatabase extends IDatabase{
|
|
|
244
351
|
}
|
|
245
352
|
}
|
|
246
353
|
|
|
247
|
-
module.exports = SQLiteDatabase;
|
|
354
|
+
module.exports = SQLiteDatabase;
|
package/package.json
CHANGED
package/test.js
CHANGED
|
@@ -20,7 +20,7 @@ const mysqlConfig = {
|
|
|
20
20
|
const mongoConfig = {
|
|
21
21
|
adapter: 'mongodb',
|
|
22
22
|
config: {
|
|
23
|
-
url: process.env.MONGODB_URL || '
|
|
23
|
+
url: process.env.MONGODB_URL || 'MONGODB_URL',
|
|
24
24
|
database: process.env.MONGODB_DATABASE || 'test_mongo_db_zerohelper',
|
|
25
25
|
cache: {
|
|
26
26
|
max: 100,
|
|
@@ -56,6 +56,7 @@ const cachedSqliteConfig = {
|
|
|
56
56
|
},
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
+
// Absurd configurations for testing edge cases
|
|
59
60
|
async function runDatabaseTests() {
|
|
60
61
|
console.log('Starting database tests...');
|
|
61
62
|
|
|
@@ -75,7 +76,7 @@ async function runDatabaseTests() {
|
|
|
75
76
|
console.log(`${name} database connected.`);
|
|
76
77
|
|
|
77
78
|
// Test Insert
|
|
78
|
-
const insertResult = await db.insert('users', { name: 'Test User', email: `test_${name.toLowerCase()}@example.com`,
|
|
79
|
+
const insertResult = await db.insert('users', { name: 'Test User', email: `test_${name.toLowerCase()}@example.com`, int_age: 30, bool_active: true });
|
|
79
80
|
console.log(`${name} Insert:`, insertResult);
|
|
80
81
|
|
|
81
82
|
// Test SelectOne
|
|
@@ -84,7 +85,7 @@ async function runDatabaseTests() {
|
|
|
84
85
|
|
|
85
86
|
// Test Update
|
|
86
87
|
if (user) {
|
|
87
|
-
const updateResult = await db.update('users', {
|
|
88
|
+
const updateResult = await db.update('users', { int_age: 31 }, { name: 'Test User' });
|
|
88
89
|
console.log(`${name} Update:`, updateResult);
|
|
89
90
|
}
|
|
90
91
|
|
|
@@ -158,6 +159,7 @@ async function runDatabaseTests() {
|
|
|
158
159
|
}
|
|
159
160
|
}
|
|
160
161
|
|
|
162
|
+
|
|
161
163
|
console.log('\nAll database tests completed.');
|
|
162
164
|
}
|
|
163
165
|
|
|
@@ -238,7 +240,6 @@ function runHelperFunctionTests() {
|
|
|
238
240
|
|
|
239
241
|
async function main() {
|
|
240
242
|
await runDatabaseTests();
|
|
241
|
-
runHelperFunctionTests();
|
|
242
243
|
}
|
|
243
244
|
|
|
244
245
|
main();
|
package/data/test_db.json
DELETED
package/data/test_db.sqlite
DELETED
|
Binary file
|
|
Binary file
|