@onurege3467/zerohelper 9.1.0 → 9.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/dist/bin/zero.js CHANGED
@@ -101,7 +101,7 @@ program.command('zpack:vacuum')
101
101
  try {
102
102
  const db = index_1.database.createDatabase({
103
103
  adapter: 'zpack',
104
- config: { filePath: file }
104
+ config: { path: file }
105
105
  });
106
106
  await db.vacuum();
107
107
  await db.close();
@@ -16,10 +16,10 @@ class JsonDatabase extends IDatabase_1.IDatabase {
16
16
  this.isWriting = false;
17
17
  this.writeQueue = [];
18
18
  this.saveDebounceTimeout = null;
19
- if (!config || !config.filePath)
20
- throw new Error('Yapılandırma içinde "filePath" belirtilmelidir.');
21
- this.filePath = config.filePath;
22
- this.saveInterval = 500;
19
+ if (!config || !config.path)
20
+ throw new Error('JsonDB: "path" gereklidir.');
21
+ this.filePath = config.path;
22
+ this.saveInterval = config.saveInterval || 500;
23
23
  process.on('exit', () => this.flushSync());
24
24
  this.initPromise = this._load();
25
25
  }
@@ -89,7 +89,7 @@ class JsonDatabase extends IDatabase_1.IDatabase {
89
89
  this.isDirty = false;
90
90
  }
91
91
  catch (error) {
92
- console.error("Veritabanı dosyasına yazılırken hata:", error);
92
+ console.error("JsonDB save error:", error);
93
93
  }
94
94
  }
95
95
  flushSync() {
@@ -128,7 +128,7 @@ class JsonDatabase extends IDatabase_1.IDatabase {
128
128
  return this._queueRequest(() => {
129
129
  let affected = 0;
130
130
  this.db[table].forEach(row => {
131
- if (Object.keys(where).every(k => row[k] === where[k])) {
131
+ if (Object.keys(where).every(k => String(row[k]) === String(where[k]))) {
132
132
  Object.assign(row, data);
133
133
  affected++;
134
134
  }
@@ -144,7 +144,7 @@ class JsonDatabase extends IDatabase_1.IDatabase {
144
144
  await this.ensureTable(table);
145
145
  return this._queueRequest(() => {
146
146
  const initial = this.db[table].length;
147
- this.db[table] = this.db[table].filter(row => !Object.keys(where).every(k => row[k] === where[k]));
147
+ this.db[table] = this.db[table].filter(row => !Object.keys(where).every(k => String(row[k]) === String(where[k])));
148
148
  const affected = initial - this.db[table].length;
149
149
  this.runHooks('afterDelete', table, { affected });
150
150
  return affected;
@@ -155,7 +155,7 @@ class JsonDatabase extends IDatabase_1.IDatabase {
155
155
  return this._execute('select', table, async () => {
156
156
  await this.initPromise;
157
157
  const results = where && Object.keys(where).length > 0
158
- ? (this.db[table] || []).filter(row => Object.keys(where).every(k => row[k] === where[k]))
158
+ ? (this.db[table] || []).filter(row => Object.keys(where).every(k => String(row[k]) === String(where[k])))
159
159
  : (this.db[table] || []);
160
160
  return JSON.parse(JSON.stringify(results));
161
161
  });
@@ -186,7 +186,7 @@ class JsonDatabase extends IDatabase_1.IDatabase {
186
186
  return this._queueRequest(() => {
187
187
  let affected = 0;
188
188
  this.db[table].forEach(row => {
189
- if (Object.keys(where).every(k => row[k] === where[k])) {
189
+ if (Object.keys(where).every(k => String(row[k]) === String(where[k]))) {
190
190
  for (const [f, v] of Object.entries(incs))
191
191
  row[f] = (Number(row[f]) || 0) + v;
192
192
  affected++;
@@ -1,4 +1,5 @@
1
1
  import { IDatabase } from './IDatabase';
2
+ import { MongoDBConfig } from './types';
2
3
  export declare class MongoDBDatabase extends IDatabase {
3
4
  private config;
4
5
  private client;
@@ -6,7 +7,7 @@ export declare class MongoDBDatabase extends IDatabase {
6
7
  private _queue;
7
8
  private _connected;
8
9
  private _connectionPromise;
9
- constructor(config: any);
10
+ constructor(config: MongoDBConfig);
10
11
  private _execute;
11
12
  private _processQueue;
12
13
  insert(collection: string, data: any): Promise<any>;
@@ -10,11 +10,11 @@ class MongoDBDatabase extends IDatabase_1.IDatabase {
10
10
  this._queue = [];
11
11
  this._connected = false;
12
12
  this.config = config;
13
- this.client = new mongodb_1.MongoClient(config.url || config.uri);
13
+ this.client = new mongodb_1.MongoClient(config.url || `mongodb://${config.host || 'localhost'}:${config.port || 27017}`);
14
14
  this._connectionPromise = new Promise(async (resolve, reject) => {
15
15
  try {
16
16
  await this.client.connect();
17
- this.db = this.client.db(config.database || config.dbName);
17
+ this.db = this.client.db(config.database || 'test');
18
18
  this._connected = true;
19
19
  resolve(this.db);
20
20
  this._processQueue();
@@ -65,7 +65,6 @@ class MongoDBDatabase extends IDatabase_1.IDatabase {
65
65
  return this._execute('update', collection, async () => {
66
66
  const db = await this._connectionPromise;
67
67
  const res = await db.collection(collection).updateMany(where, { $set: data });
68
- await this.runHooks('afterUpdate', collection, { affected: res.modifiedCount });
69
68
  return Number(res.modifiedCount);
70
69
  });
71
70
  }
@@ -74,7 +73,6 @@ class MongoDBDatabase extends IDatabase_1.IDatabase {
74
73
  return this._execute('delete', collection, async () => {
75
74
  const db = await this._connectionPromise;
76
75
  const res = await db.collection(collection).deleteMany(where);
77
- await this.runHooks('afterDelete', collection, { affected: res.deletedCount });
78
76
  return res.deletedCount;
79
77
  });
80
78
  }
@@ -7,10 +7,11 @@ export declare class MySQLDatabase extends IDatabase {
7
7
  private _connected;
8
8
  private _connectionPromise;
9
9
  constructor(config: MySQLConfig);
10
- private _queueRequest;
10
+ private _execute;
11
11
  private _processQueue;
12
12
  query(sql: string, params?: any[]): Promise<any>;
13
- ensureTable(table: string, data?: Record<string, any>): Promise<void>;
13
+ private _ensureMissingColumns;
14
+ ensureTable(table: string, data?: any): Promise<void>;
14
15
  insert(table: string, data: Record<string, any>): Promise<number>;
15
16
  update(table: string, data: Record<string, any>, where: Record<string, any>): Promise<number>;
16
17
  delete(table: string, where: Record<string, any>): Promise<number>;
@@ -18,12 +19,11 @@ export declare class MySQLDatabase extends IDatabase {
18
19
  selectOne<T = any>(table: string, where?: Record<string, any> | null): Promise<T | null>;
19
20
  set(table: string, data: Record<string, any>, where: Record<string, any>): Promise<any>;
20
21
  bulkInsert(table: string, dataArray: Record<string, any>[]): Promise<number>;
21
- increment(table: string, increments: Record<string, number>, where: Record<string, any>): Promise<number>;
22
- decrement(table: string, decrements: Record<string, number>, where: Record<string, any>): Promise<number>;
22
+ increment(table: string, incs: Record<string, number>, where: Record<string, any>): Promise<number>;
23
+ decrement(table: string, decs: Record<string, number>, where: Record<string, any>): Promise<number>;
23
24
  close(): Promise<void>;
24
25
  private _getColumnType;
25
26
  private _serializeValue;
26
- private _deserializeValue;
27
27
  private _buildWhereClause;
28
28
  }
29
29
  export default MySQLDatabase;
@@ -16,32 +16,24 @@ class MySQLDatabase extends IDatabase_1.IDatabase {
16
16
  this._connectionPromise = new Promise(async (resolve, reject) => {
17
17
  try {
18
18
  const connection = await promise_1.default.createConnection({
19
- host: config.host,
19
+ host: config.host || 'localhost',
20
20
  port: config.port || 3306,
21
- user: config.user,
21
+ user: config.username,
22
22
  password: config.password,
23
- typeCast: (field, next) => {
24
- if (field.type === 'TINY' && field.length === 1)
25
- return (field.string() === '1');
26
- if (['INT', 'DECIMAL', 'NEWDECIMAL', 'FLOAT', 'DOUBLE'].includes(field.type))
27
- return Number(field.string());
28
- return next();
29
- }
30
23
  });
31
- await connection.query(`CREATE DATABASE IF NOT EXISTS
24
+ await connection.query(`CREATE DATABASE IF NOT EXISTS
32
25
  ${config.database}
33
26
  `);
34
27
  await connection.end();
35
28
  this.pool = promise_1.default.createPool({
36
- host: config.host, port: config.port || 3306, user: config.user, password: config.password, database: config.database,
37
- waitForConnections: true, connectionLimit: config.connectionLimit || 10, queueLimit: 0,
38
- typeCast: (field, next) => {
39
- if (field.type === 'TINY' && field.length === 1)
40
- return (field.string() === '1');
41
- if (['INT', 'DECIMAL', 'NEWDECIMAL', 'FLOAT', 'DOUBLE'].includes(field.type))
42
- return Number(field.string());
43
- return next();
44
- }
29
+ host: config.host || 'localhost',
30
+ port: config.port || 3306,
31
+ user: config.username,
32
+ password: config.password,
33
+ database: config.database,
34
+ waitForConnections: true,
35
+ connectionLimit: config.poolSize || 10,
36
+ queueLimit: 0,
45
37
  });
46
38
  this._connected = true;
47
39
  resolve(this.pool);
@@ -52,18 +44,16 @@ ${config.database}
52
44
  }
53
45
  });
54
46
  }
55
- async _queueRequest(operation, opName, table) {
47
+ async _execute(op, table, fn) {
56
48
  const start = Date.now();
57
49
  const execute = async () => {
58
- const res = await operation();
59
- this.recordMetric(opName, table, Date.now() - start);
50
+ const res = await fn();
51
+ this.recordMetric(op, table, Date.now() - start);
60
52
  return res;
61
53
  };
62
54
  if (this._connected)
63
55
  return execute();
64
- return new Promise((resolve, reject) => {
65
- this._queue.push({ operation: execute, resolve, reject });
66
- });
56
+ return new Promise((resolve, reject) => this._queue.push({ operation: execute, resolve, reject }));
67
57
  }
68
58
  async _processQueue() {
69
59
  if (!this._connected)
@@ -85,122 +75,155 @@ ${config.database}
85
75
  const [rows] = await pool.execute(sql, params);
86
76
  return rows;
87
77
  }
78
+ async _ensureMissingColumns(table, data) {
79
+ const existingColumns = await this.query(`DESCRIBE
80
+ ${table}
81
+ `).catch(() => []);
82
+ const columnNames = existingColumns.map(col => col.Field);
83
+ for (const key of Object.keys(data)) {
84
+ if (key !== '_id' && !columnNames.includes(key)) {
85
+ await this.query(`ALTER TABLE
86
+ ${table}
87
+ ADD COLUMN
88
+ ${key}
89
+ TEXT`);
90
+ }
91
+ }
92
+ }
88
93
  async ensureTable(table, data = {}) {
89
94
  const escapedTable = promise_1.default.escape(table);
90
95
  const tables = await this.query(`SHOW TABLES LIKE ${escapedTable}`);
91
96
  if (tables.length === 0) {
92
- const columnDefinitions = Object.keys(data).map(col => `
93
- ${col}
94
- ${this._getColumnType(data[col])}`);
95
- const columnsPart = columnDefinitions.length > 0 ? ', ' + columnDefinitions.join(", ") : '';
96
- await this.query(`CREATE TABLE
97
+ const defs = Object.keys(data).map(k => `
98
+ ${k}
99
+ TEXT`);
100
+ const columnsPart = defs.length > 0 ? ', ' + defs.join(", ") : '';
101
+ await this.query(`CREATE TABLE
97
102
  ${table}
98
103
  (_id INT PRIMARY KEY AUTO_INCREMENT ${columnsPart}) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`);
99
104
  }
105
+ else {
106
+ await this._ensureMissingColumns(table, data);
107
+ }
100
108
  }
101
109
  async insert(table, data) {
102
110
  await this.runHooks('beforeInsert', table, data);
103
- return this._queueRequest(async () => {
111
+ return this._execute('insert', table, async () => {
104
112
  await this.ensureTable(table, data);
105
113
  const keys = Object.keys(data);
106
- const placeholders = keys.map(() => "?").join(",");
107
- const values = Object.values(data).map(v => this._serializeValue(v));
108
- const sql = `INSERT INTO
114
+ const sql = `INSERT INTO
109
115
  ${table}
110
116
  (${keys.map(k => `
111
117
  ${k}
112
- `).join(",")}) VALUES (${placeholders})`;
113
- const result = await this.query(sql, values);
118
+ `).join(",")}) VALUES (${keys.map(() => '?').join(",")})`;
119
+ const result = await this.query(sql, Object.values(data).map(v => typeof v === 'object' ? JSON.stringify(v) : v));
114
120
  const finalData = { _id: result.insertId, ...data };
115
121
  await this.runHooks('afterInsert', table, finalData);
116
122
  return result.insertId;
117
- }, 'insert', table);
123
+ });
118
124
  }
119
125
  async update(table, data, where) {
120
126
  await this.runHooks('beforeUpdate', table, { data, where });
121
- return this._queueRequest(async () => {
122
- const setString = Object.keys(data).map(k => `
127
+ return this._execute('update', table, async () => {
128
+ await this.ensureTable(table, { ...data, ...where });
129
+ const set = Object.keys(data).map(k => `
123
130
  ${k}
124
131
  = ?`).join(", ");
125
- const { whereClause, values: whereValues } = this._buildWhereClause(where);
126
- const sql = `UPDATE
132
+ const wKeys = Object.keys(where);
133
+ const sql = `UPDATE
127
134
  ${table}
128
- SET ${setString} ${whereClause}`;
129
- const result = await this.query(sql, [...Object.values(data).map(v => this._serializeValue(v)), ...whereValues]);
130
- await this.runHooks('afterUpdate', table, { affected: result.affectedRows });
135
+ SET ${set} WHERE ${wKeys.map(k => `
136
+ ${k}
137
+ = ?`).join(" AND ")}`;
138
+ const result = await this.query(sql, [...Object.values(data).map(v => typeof v === 'object' ? JSON.stringify(v) : v), ...Object.values(where).map(v => typeof v === 'object' ? JSON.stringify(v) : v)]);
131
139
  return result.affectedRows;
132
- }, 'update', table);
140
+ });
133
141
  }
134
142
  async delete(table, where) {
135
143
  await this.runHooks('beforeDelete', table, where);
136
- return this._queueRequest(async () => {
137
- const { whereClause, values } = this._buildWhereClause(where);
138
- const sql = `DELETE FROM
144
+ return this._execute('delete', table, async () => {
145
+ await this.ensureTable(table, where);
146
+ const keys = Object.keys(where);
147
+ const sql = `DELETE FROM
139
148
  ${table}
140
- ${whereClause}`;
141
- const result = await this.query(sql, values);
142
- await this.runHooks('afterDelete', table, { affected: result.affectedRows });
149
+ WHERE ${keys.map(k => `
150
+ ${k}
151
+ = ?`).join(" AND ")}`;
152
+ const result = await this.query(sql, Object.values(where).map(v => typeof v === 'object' ? JSON.stringify(v) : v));
143
153
  return result.affectedRows;
144
- }, 'delete', table);
154
+ });
145
155
  }
146
156
  async select(table, where = null) {
147
- return this._queueRequest(async () => {
148
- const { whereClause, values } = this._buildWhereClause(where || {});
149
- const rows = await this.query(`SELECT * FROM
157
+ return this._execute('select', table, async () => {
158
+ await this.ensureTable(table, where || {});
159
+ const keys = where ? Object.keys(where) : [];
160
+ const sql = `SELECT * FROM
150
161
  ${table}
151
- ${whereClause}`, values);
162
+ ` + (keys.length ? ' WHERE ' + keys.map(k => `
163
+ ${k}
164
+ = ?`).join(" AND ") : '');
165
+ const rows = await this.query(sql, keys.map(k => typeof where[k] === 'object' ? JSON.stringify(where[k]) : where[k]));
152
166
  return rows.map((row) => {
153
- const newRow = {};
154
- for (const key in row)
155
- newRow[key] = this._deserializeValue(row[key]);
156
- return newRow;
167
+ const nr = {};
168
+ for (const k in row) {
169
+ try {
170
+ nr[k] = JSON.parse(row[k]);
171
+ }
172
+ catch {
173
+ nr[k] = row[k];
174
+ }
175
+ }
176
+ return nr;
157
177
  });
158
- }, 'select', table);
178
+ });
159
179
  }
160
180
  async selectOne(table, where = null) {
161
- const results = await this.select(table, where);
162
- return results[0] || null;
181
+ const res = await this.select(table, where);
182
+ return res[0] || null;
163
183
  }
164
184
  async set(table, data, where) {
165
185
  const existing = await this.selectOne(table, where);
166
186
  return existing ? this.update(table, data, where) : this.insert(table, { ...where, ...data });
167
187
  }
168
188
  async bulkInsert(table, dataArray) {
169
- return this._queueRequest(async () => {
170
- if (!dataArray.length)
171
- return 0;
189
+ if (!dataArray.length)
190
+ return 0;
191
+ return this._execute('bulkInsert', table, async () => {
172
192
  await this.ensureTable(table, dataArray[0]);
173
193
  const keys = Object.keys(dataArray[0]);
174
194
  const placeholders = dataArray.map(() => `(${keys.map(() => '?').join(',')})`).join(',');
175
195
  const values = dataArray.flatMap(obj => keys.map(k => this._serializeValue(obj[k])));
176
- const sql = `INSERT INTO
196
+ const sql = `INSERT INTO
177
197
  ${table}
178
198
  (${keys.map(k => `
179
199
  ${k}
180
200
  `).join(",")}) VALUES ${placeholders}`;
181
201
  const result = await this.query(sql, values);
182
202
  return result.affectedRows;
183
- }, 'bulkInsert', table);
203
+ });
184
204
  }
185
- async increment(table, increments, where) {
186
- return this._queueRequest(async () => {
187
- const clauses = Object.keys(increments).map(f => `
205
+ async increment(table, incs, where) {
206
+ return this._execute('increment', table, async () => {
207
+ await this.ensureTable(table, where);
208
+ const set = Object.keys(incs).map(f => `
188
209
  ${f}
189
- =
210
+ =
190
211
  ${f}
191
212
  + ?`).join(', ');
192
- const { whereClause, values } = this._buildWhereClause(where);
193
- const sql = `UPDATE
213
+ const wKeys = Object.keys(where);
214
+ const sql = `UPDATE
194
215
  ${table}
195
- SET ${clauses} ${whereClause}`;
196
- const result = await this.query(sql, [...Object.values(increments), ...values]);
216
+ SET ${set} WHERE ${wKeys.map(k => `
217
+ ${k}
218
+ = ?`).join(" AND ")}`;
219
+ const result = await this.query(sql, [...Object.values(incs), ...Object.values(where).map(v => typeof v === 'object' ? JSON.stringify(v) : v)]);
197
220
  return result.affectedRows;
198
- }, 'increment', table);
221
+ });
199
222
  }
200
- async decrement(table, decrements, where) {
223
+ async decrement(table, decs, where) {
201
224
  const incs = {};
202
- for (const k in decrements)
203
- incs[k] = -decrements[k];
225
+ for (const k in decs)
226
+ incs[k] = -decs[k];
204
227
  return this.increment(table, incs, where);
205
228
  }
206
229
  async close() { if (this.pool)
@@ -214,6 +237,8 @@ ${table}
214
237
  return Number.isInteger(v) ? 'INT' : 'DOUBLE';
215
238
  if (v instanceof Date)
216
239
  return 'DATETIME';
240
+ if (typeof v === 'object')
241
+ return 'JSON';
217
242
  return 'TEXT';
218
243
  }
219
244
  _serializeValue(v) {
@@ -221,24 +246,18 @@ ${table}
221
246
  return v.toISOString().slice(0, 19).replace('T', ' ');
222
247
  return (typeof v === 'object' && v !== null) ? JSON.stringify(v) : v;
223
248
  }
224
- _deserializeValue(v) {
225
- if (typeof v === 'string' && (v.startsWith('{') || v.startsWith('['))) {
226
- try {
227
- return JSON.parse(v);
228
- }
229
- catch {
230
- return v;
231
- }
232
- }
233
- return v;
234
- }
235
249
  _buildWhereClause(where) {
250
+ if (!where)
251
+ return { whereClause: '', values: [] };
236
252
  const keys = Object.keys(where);
237
253
  if (!keys.length)
238
254
  return { whereClause: '', values: [] };
239
- return { whereClause: 'WHERE ' + keys.map(k => `
255
+ return {
256
+ whereClause: 'WHERE ' + keys.map(k => `
240
257
  ${k}
241
- = ?`).join(' AND '), values: Object.values(where).map(v => this._serializeValue(v)) };
258
+ = ?`).join(' AND '),
259
+ values: Object.values(where).map(v => this._serializeValue(v))
260
+ };
242
261
  }
243
262
  }
244
263
  exports.MySQLDatabase = MySQLDatabase;
@@ -10,7 +10,6 @@ export declare class PostgreSQLDatabase extends IDatabase {
10
10
  private _execute;
11
11
  private _processQueue;
12
12
  query(sql: string, params?: any[]): Promise<any>;
13
- private _ensureMissingColumns;
14
13
  ensureTable(table: string, data?: any): Promise<void>;
15
14
  insert(table: string, data: any): Promise<any>;
16
15
  update(table: string, data: any, where: any): Promise<number>;
@@ -24,6 +23,6 @@ export declare class PostgreSQLDatabase extends IDatabase {
24
23
  close(): Promise<void>;
25
24
  private _getColumnType;
26
25
  private _serializeValue;
27
- private _deserializeValue;
26
+ private _buildWhereClause;
28
27
  }
29
28
  export default PostgreSQLDatabase;
@@ -12,7 +12,7 @@ class PostgreSQLDatabase extends IDatabase_1.IDatabase {
12
12
  this.config = config;
13
13
  this._connectionPromise = new Promise(async (resolve, reject) => {
14
14
  try {
15
- const tempPool = new pg_1.Pool({ host: config.host, port: config.port || 5432, user: config.user, password: config.password, database: 'postgres' });
15
+ const tempPool = new pg_1.Pool({ host: config.host || 'localhost', port: config.port || 5432, user: config.username, password: config.password, database: 'postgres' });
16
16
  try {
17
17
  await tempPool.query(`CREATE DATABASE "${config.database}"`);
18
18
  }
@@ -21,7 +21,7 @@ class PostgreSQLDatabase extends IDatabase_1.IDatabase {
21
21
  console.warn(e.message);
22
22
  }
23
23
  await tempPool.end();
24
- this.pool = new pg_1.Pool({ host: config.host, port: config.port || 5432, user: config.user, password: config.password, database: config.database, max: config.connectionLimit || 10 });
24
+ this.pool = new pg_1.Pool({ host: config.host || 'localhost', port: config.port || 5432, user: config.username, password: config.password, database: config.database, max: config.poolSize || 10 });
25
25
  this._connected = true;
26
26
  resolve(this.pool);
27
27
  this._processQueue();
@@ -33,15 +33,11 @@ class PostgreSQLDatabase extends IDatabase_1.IDatabase {
33
33
  }
34
34
  async _execute(op, table, fn) {
35
35
  const start = Date.now();
36
- const execute = async () => {
37
- const res = await fn();
38
- this.recordMetric(op, table, Date.now() - start);
39
- return res;
40
- };
41
- if (this._connected)
42
- return execute();
43
- return new Promise((resolve, reject) => this._queue.push({ operation: execute, resolve, reject }));
36
+ const res = await fn();
37
+ this.recordMetric(op, table, Date.now() - start);
38
+ return res;
44
39
  }
40
+ ;
45
41
  async _processQueue() {
46
42
  if (!this._connected)
47
43
  return;
@@ -62,24 +58,20 @@ class PostgreSQLDatabase extends IDatabase_1.IDatabase {
62
58
  const res = await pool.query(sql, params);
63
59
  return res.rows;
64
60
  }
65
- async _ensureMissingColumns(table, data) {
66
- const existing = await this.query(`SELECT column_name FROM information_schema.columns WHERE table_name = $1 AND table_schema = 'public'`, [table]);
67
- const names = existing.map((c) => c.column_name);
68
- for (const key of Object.keys(data)) {
69
- if (key !== '_id' && !names.includes(key)) {
70
- const type = this._getColumnType(data[key]);
71
- await this.query(`ALTER TABLE "${table}" ADD COLUMN "${key}" ${type}`);
72
- }
73
- }
74
- }
75
61
  async ensureTable(table, data = {}) {
76
62
  const tables = await this.query(`SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_name = $1`, [table]);
77
63
  if (tables.length === 0) {
78
- const defs = Object.keys(data).map(k => `"${k}" ${this._getColumnType(data[k])}`);
64
+ const defs = Object.keys(data).map(k => `"${k}" TEXT`);
79
65
  await this.query(`CREATE TABLE "${table}" ("_id" SERIAL PRIMARY KEY ${defs.length ? ', ' + defs.join(",") : ''})`);
80
66
  }
81
67
  else {
82
- await this._ensureMissingColumns(table, data);
68
+ const existing = await this.query(`SELECT column_name FROM information_schema.columns WHERE table_name = $1 AND table_schema = 'public'`, [table]);
69
+ const names = existing.map((c) => c.column_name);
70
+ for (const key of Object.keys(data)) {
71
+ if (key !== '_id' && !names.includes(key)) {
72
+ await this.query(`ALTER TABLE "${table}" ADD COLUMN "${key}" TEXT`);
73
+ }
74
+ }
83
75
  }
84
76
  }
85
77
  async insert(table, data) {
@@ -99,10 +91,10 @@ class PostgreSQLDatabase extends IDatabase_1.IDatabase {
99
91
  return this._execute('update', table, async () => {
100
92
  await this.ensureTable(table, { ...data, ...where });
101
93
  const set = Object.keys(data).map((k, i) => `"${k}" = $${i + 1}`).join(",");
102
- const wKeys = Object.keys(where);
103
- const sql = `UPDATE "${table}" SET ${set} WHERE ${wKeys.map((k, i) => `"${k}" = $${Object.keys(data).length + i + 1}`).join(" AND ")}`;
94
+ const { whereClause, values: whereValues } = this._buildWhereClause(where);
95
+ const sql = `UPDATE "${table}" SET ${set} ${whereClause}`;
104
96
  const pool = await this._connectionPromise;
105
- const res = await pool.query(sql, [...Object.values(data).map(v => this._serializeValue(v)), ...Object.values(where).map(v => this._serializeValue(v))]);
97
+ const res = await pool.query(sql, [...Object.values(data).map(v => this._serializeValue(v)), ...whereValues]);
106
98
  return res.rowCount ?? 0;
107
99
  });
108
100
  }
@@ -110,23 +102,22 @@ class PostgreSQLDatabase extends IDatabase_1.IDatabase {
110
102
  await this.runHooks('beforeDelete', table, where);
111
103
  return this._execute('delete', table, async () => {
112
104
  await this.ensureTable(table, where);
113
- const keys = Object.keys(where);
114
- const sql = `DELETE FROM "${table}" WHERE ${keys.map((k, i) => `"${k}" = $${i + 1}`).join(" AND ")}`;
105
+ const { whereClause, values } = this._buildWhereClause(where);
106
+ const sql = `DELETE FROM "${table}" ${whereClause}`;
115
107
  const pool = await this._connectionPromise;
116
- const res = await pool.query(sql, Object.values(where).map(v => this._serializeValue(v)));
108
+ const res = await pool.query(sql, values);
117
109
  return res.rowCount ?? 0;
118
110
  });
119
111
  }
120
112
  async select(table, where = null) {
121
113
  return this._execute('select', table, async () => {
122
114
  await this.ensureTable(table, where || {});
123
- const keys = where ? Object.keys(where) : [];
124
- const sql = `SELECT * FROM "${table}"` + (keys.length ? ` WHERE ${keys.map((k, i) => `"${k}" = $${i + 1}`).join(" AND ")}` : '');
125
- const rows = await this.query(sql, Object.values(where || {}).map(v => this._serializeValue(v)));
115
+ const { whereClause, values } = this._buildWhereClause(where);
116
+ const rows = await this.query(`SELECT * FROM "${table}" ${whereClause}`, values);
126
117
  return rows.map((r) => {
127
118
  const nr = {};
128
119
  for (const k in r)
129
- nr[k] = this._deserializeValue(r[k]);
120
+ nr[k] = r[k];
130
121
  return nr;
131
122
  });
132
123
  });
@@ -142,21 +133,18 @@ class PostgreSQLDatabase extends IDatabase_1.IDatabase {
142
133
  async bulkInsert(table, dataArray) {
143
134
  if (!dataArray.length)
144
135
  return 0;
145
- return this._execute('bulkInsert', table, async () => {
146
- await this.ensureTable(table, dataArray[0]);
147
- for (const d of dataArray)
148
- await this.insert(table, d);
149
- return dataArray.length;
150
- });
136
+ for (const d of dataArray)
137
+ await this.insert(table, d);
138
+ return dataArray.length;
151
139
  }
152
140
  async increment(table, incs, where) {
153
141
  return this._execute('increment', table, async () => {
154
142
  await this.ensureTable(table, where);
155
143
  const set = Object.keys(incs).map((f, i) => `"${f}" = "${f}" + $${i + 1}`).join(',');
156
- const wKeys = Object.keys(where);
157
- const sql = `UPDATE "${table}" SET ${set} WHERE ${wKeys.map((k, i) => `"${k}" = $${Object.keys(incs).length + i + 1}`).join(" AND ")}`;
144
+ const { whereClause, values } = this._buildWhereClause(where);
145
+ const sql = `UPDATE "${table}" SET ${set} ${whereClause}`;
158
146
  const pool = await this._connectionPromise;
159
- const res = await pool.query(sql, [...Object.values(incs), ...Object.values(where).map(v => this._serializeValue(v))]);
147
+ const res = await pool.query(sql, [...Object.values(incs), ...values]);
160
148
  return res.rowCount ?? 0;
161
149
  });
162
150
  }
@@ -186,9 +174,17 @@ class PostgreSQLDatabase extends IDatabase_1.IDatabase {
186
174
  return v.toISOString();
187
175
  return (typeof v === 'object' && v !== null) ? JSON.stringify(v) : v;
188
176
  }
189
- _deserializeValue(v) {
190
- // PG automatically handles JSONB, but sometimes manual parsing is safer for consistency
191
- return v;
177
+ _buildWhereClause(where) {
178
+ if (!where)
179
+ return { whereClause: '', values: [] };
180
+ const safeWhere = where;
181
+ const keys = Object.keys(safeWhere);
182
+ if (!keys.length)
183
+ return { whereClause: '', values: [] };
184
+ return {
185
+ whereClause: 'WHERE ' + keys.map((k, i) => `"${k}" = $${i + 1}`).join(' AND '),
186
+ values: keys.map(k => this._serializeValue(safeWhere[k]))
187
+ };
192
188
  }
193
189
  }
194
190
  exports.PostgreSQLDatabase = PostgreSQLDatabase;
@@ -1,11 +1,12 @@
1
1
  import { IDatabase } from './IDatabase';
2
2
  import { RedisClientType } from 'redis';
3
+ import { RedisConfig } from './types';
3
4
  export declare class RedisDatabase extends IDatabase {
4
5
  private config;
5
6
  private client;
6
7
  private isConnecting;
7
8
  private keyPrefix;
8
- constructor(config?: any);
9
+ constructor(config: RedisConfig);
9
10
  private _execute;
10
11
  connect(): Promise<RedisClientType>;
11
12
  private _getKey;
@@ -4,17 +4,11 @@ exports.RedisDatabase = void 0;
4
4
  const IDatabase_1 = require("./IDatabase");
5
5
  const redis_1 = require("redis");
6
6
  class RedisDatabase extends IDatabase_1.IDatabase {
7
- constructor(config = {}) {
7
+ constructor(config) {
8
8
  super();
9
9
  this.client = null;
10
10
  this.isConnecting = false;
11
- this.config = {
12
- host: config.host || '127.0.0.1',
13
- port: config.port || 6379,
14
- password: config.password,
15
- db: config.db || 0,
16
- connectTimeout: config.connectTimeout || 5000,
17
- };
11
+ this.config = config;
18
12
  this.keyPrefix = config.keyPrefix || 'app:';
19
13
  }
20
14
  async _execute(op, table, fn) {
@@ -23,6 +17,7 @@ class RedisDatabase extends IDatabase_1.IDatabase {
23
17
  this.recordMetric(op, table, Date.now() - start);
24
18
  return res;
25
19
  }
20
+ ;
26
21
  async connect() {
27
22
  if (this.client && this.client.isReady)
28
23
  return this.client;
@@ -34,9 +29,10 @@ class RedisDatabase extends IDatabase_1.IDatabase {
34
29
  this.isConnecting = true;
35
30
  try {
36
31
  this.client = (0, redis_1.createClient)({
37
- socket: { host: this.config.host, port: this.config.port, connectTimeout: this.config.connectTimeout },
32
+ url: this.config.url,
33
+ socket: { host: this.config.host || '127.0.0.1', port: this.config.port || 6379 },
38
34
  password: this.config.password,
39
- database: this.config.db,
35
+ database: Number(this.config.database) || 0,
40
36
  });
41
37
  await this.client.connect();
42
38
  return this.client;
@@ -82,8 +78,7 @@ class RedisDatabase extends IDatabase_1.IDatabase {
82
78
  const client = await this.connect();
83
79
  for (const item of existing) {
84
80
  const merged = { ...item, ...data };
85
- const id = String(item._id || item.id);
86
- await client.set(this._getKey(table, id), JSON.stringify(merged));
81
+ await client.set(this._getKey(table, item._id || item.id), JSON.stringify(merged));
87
82
  }
88
83
  return existing.length;
89
84
  });
@@ -110,15 +105,16 @@ class RedisDatabase extends IDatabase_1.IDatabase {
110
105
  return dataArray.length;
111
106
  }
112
107
  async increment(table, incs, where = {}) {
113
- const recs = await this.select(table, where);
114
- const client = await this.connect();
115
- for (const r of recs) {
116
- for (const [f, v] of Object.entries(incs))
117
- r[f] = (Number(r[f]) || 0) + v;
118
- const id = String(r._id || r.id);
119
- await client.set(this._getKey(table, id), JSON.stringify(r));
120
- }
121
- return recs.length;
108
+ return this._execute('increment', table, async () => {
109
+ const recs = await this.select(table, where);
110
+ const client = await this.connect();
111
+ for (const r of recs) {
112
+ for (const [f, v] of Object.entries(incs))
113
+ r[f] = (Number(r[f]) || 0) + v;
114
+ await client.set(this._getKey(table, r._id || r.id), JSON.stringify(r));
115
+ }
116
+ return recs.length;
117
+ });
122
118
  }
123
119
  async decrement(table, decs, where = {}) {
124
120
  const incs = {};
@@ -5,10 +5,6 @@ export declare class SQLiteDatabase extends IDatabase {
5
5
  constructor(config: SQLiteConfig);
6
6
  private _execute;
7
7
  query(sql: string, params?: any[]): Promise<any>;
8
- /**
9
- * SQLite dynamically adds missing columns using ALTER TABLE.
10
- */
11
- private _ensureMissingColumns;
12
8
  ensureTable(table: string, data?: any): Promise<void>;
13
9
  insert(table: string, data: any): Promise<number>;
14
10
  update(table: string, data: any, where: any): Promise<number>;
@@ -20,5 +16,7 @@ export declare class SQLiteDatabase extends IDatabase {
20
16
  increment(table: string, incs: any, where: any): Promise<number>;
21
17
  decrement(table: string, decs: any, where: any): Promise<number>;
22
18
  close(): Promise<void>;
19
+ private _serializeValue;
20
+ private _buildWhereClause;
23
21
  }
24
22
  export default SQLiteDatabase;
@@ -11,12 +11,12 @@ const path_1 = __importDefault(require("path"));
11
11
  class SQLiteDatabase extends IDatabase_1.IDatabase {
12
12
  constructor(config) {
13
13
  super();
14
- if (!config || !config.filename)
15
- throw new Error('SQLite "filename" gereklidir.');
16
- const dir = path_1.default.dirname(config.filename);
14
+ if (!config || !config.path)
15
+ throw new Error('SQLite "path" gereklidir.');
16
+ const dir = path_1.default.dirname(config.path);
17
17
  if (!fs_1.default.existsSync(dir))
18
18
  fs_1.default.mkdirSync(dir, { recursive: true });
19
- this.db = new sqlite3_1.default.Database(config.filename);
19
+ this.db = new sqlite3_1.default.Database(config.path);
20
20
  }
21
21
  async _execute(op, table, fn) {
22
22
  const start = Date.now();
@@ -24,6 +24,7 @@ class SQLiteDatabase extends IDatabase_1.IDatabase {
24
24
  this.recordMetric(op, table, Date.now() - start);
25
25
  return res;
26
26
  }
27
+ ;
27
28
  async query(sql, params = []) {
28
29
  return new Promise((resolve, reject) => {
29
30
  const s = sql.trim().toUpperCase();
@@ -35,22 +36,16 @@ class SQLiteDatabase extends IDatabase_1.IDatabase {
35
36
  }
36
37
  });
37
38
  }
38
- /**
39
- * SQLite dynamically adds missing columns using ALTER TABLE.
40
- */
41
- async _ensureMissingColumns(table, data) {
42
- const info = await this.query(`PRAGMA table_info("${table}")`);
43
- const names = info.map(c => c.name);
44
- for (const key of Object.keys(data)) {
45
- if (key !== '_id' && !names.includes(key)) {
46
- await this.query(`ALTER TABLE "${table}" ADD COLUMN "${key}" TEXT`);
47
- }
48
- }
49
- }
50
39
  async ensureTable(table, data = {}) {
51
40
  try {
52
41
  await this.query(`SELECT 1 FROM "${table}" LIMIT 1`);
53
- await this._ensureMissingColumns(table, data);
42
+ const info = await this.query(`PRAGMA table_info("${table}")`);
43
+ const names = info.map(c => c.name);
44
+ for (const key of Object.keys(data)) {
45
+ if (key !== '_id' && !names.includes(key)) {
46
+ await this.query(`ALTER TABLE "${table}" ADD COLUMN "${key}" TEXT`);
47
+ }
48
+ }
54
49
  }
55
50
  catch {
56
51
  const defs = Object.keys(data).map(k => `"${k}" TEXT`);
@@ -63,7 +58,7 @@ class SQLiteDatabase extends IDatabase_1.IDatabase {
63
58
  await this.ensureTable(table, data);
64
59
  const keys = Object.keys(data);
65
60
  const sql = `INSERT INTO "${table}" (${keys.map(k => `"${k}"`).join(',')}) VALUES (${keys.map(() => '?').join(',')})`;
66
- const res = await this.query(sql, Object.values(data).map(v => JSON.stringify(v)));
61
+ const res = await this.query(sql, Object.values(data).map(v => typeof v === 'object' ? JSON.stringify(v) : v));
67
62
  const finalData = { _id: res.lastID, ...data };
68
63
  await this.runHooks('afterInsert', table, finalData);
69
64
  return res.lastID;
@@ -74,9 +69,9 @@ class SQLiteDatabase extends IDatabase_1.IDatabase {
74
69
  return this._execute('update', table, async () => {
75
70
  await this.ensureTable(table, { ...data, ...where });
76
71
  const set = Object.keys(data).map(k => `"${k}" = ?`).join(',');
77
- const keys = Object.keys(where);
78
- const sql = `UPDATE "${table}" SET ${set} WHERE ${keys.map(k => `"${k}" = ?`).join(' AND ')}`;
79
- const res = await this.query(sql, [...Object.values(data).map(v => JSON.stringify(v)), ...Object.values(where).map(v => JSON.stringify(v))]);
72
+ const { whereClause, values: whereValues } = this._buildWhereClause(where);
73
+ const sql = `UPDATE "${table}" SET ${set} ${whereClause}`;
74
+ const res = await this.query(sql, [...Object.values(data).map(v => typeof v === 'object' ? JSON.stringify(v) : v), ...whereValues]);
80
75
  return res.changes;
81
76
  });
82
77
  }
@@ -84,18 +79,17 @@ class SQLiteDatabase extends IDatabase_1.IDatabase {
84
79
  await this.runHooks('beforeDelete', table, where);
85
80
  return this._execute('delete', table, async () => {
86
81
  await this.ensureTable(table, where);
87
- const keys = Object.keys(where);
88
- const sql = `DELETE FROM "${table}" WHERE ${keys.map(k => `"${k}" = ?`).join(' AND ')}`;
89
- const res = await this.query(sql, Object.values(where).map(v => JSON.stringify(v)));
82
+ const { whereClause, values } = this._buildWhereClause(where);
83
+ const sql = `DELETE FROM "${table}" ${whereClause}`;
84
+ const res = await this.query(sql, values);
90
85
  return res.changes;
91
86
  });
92
87
  }
93
88
  async select(table, where = null) {
94
89
  return this._execute('select', table, async () => {
95
90
  await this.ensureTable(table, where || {});
96
- const keys = where ? Object.keys(where) : [];
97
- const sql = `SELECT * FROM "${table}"` + (keys.length ? ` WHERE ${keys.map(k => `"${k}" = ?`).join(' AND ')}` : '');
98
- const rows = await this.query(sql, keys.map(k => JSON.stringify(where[k])));
91
+ const { whereClause, values } = this._buildWhereClause(where);
92
+ const rows = await this.query(`SELECT * FROM "${table}" ${whereClause}`, values);
99
93
  return rows.map((r) => {
100
94
  const nr = {};
101
95
  for (const k in r) {
@@ -103,20 +97,10 @@ class SQLiteDatabase extends IDatabase_1.IDatabase {
103
97
  nr[k] = JSON.parse(r[k]);
104
98
  }
105
99
  catch {
106
- nr[nr[k] = r[k]];
107
- }
108
- }
109
- const { _id, ...rest } = r; // Handle _id vs id
110
- const finalObj = { _id };
111
- for (const k in rest) {
112
- try {
113
- finalObj[k] = JSON.parse(rest[k]);
114
- }
115
- catch {
116
- finalObj[k] = rest[k];
100
+ nr[k] = r[k];
117
101
  }
118
102
  }
119
- return finalObj;
103
+ return nr;
120
104
  });
121
105
  });
122
106
  }
@@ -138,10 +122,10 @@ class SQLiteDatabase extends IDatabase_1.IDatabase {
138
122
  async increment(table, incs, where) {
139
123
  return this._execute('increment', table, async () => {
140
124
  await this.ensureTable(table, where);
141
- const set = Object.keys(incs).map(f => `"${f}" = CAST("${f}" AS SKIP_CAST) + ?`.replace('SKIP_CAST', 'NUMERIC'));
142
- const keys = Object.keys(where);
143
- const sql = `UPDATE "${table}" SET ${set} WHERE ${keys.map(k => `"${k}" = ?`).join(' AND ')}`;
144
- const res = await this.query(sql, [...Object.values(incs), ...Object.values(where).map(v => JSON.stringify(v))]);
125
+ const set = Object.keys(incs).map(f => `"${f}" = CAST("${f}" AS NUMERIC) + ?`);
126
+ const { whereClause, values } = this._buildWhereClause(where);
127
+ const sql = `UPDATE "${table}" SET ${set} ${whereClause}`;
128
+ const res = await this.query(sql, [...Object.values(incs), ...values]);
145
129
  return res.changes;
146
130
  });
147
131
  }
@@ -152,6 +136,23 @@ class SQLiteDatabase extends IDatabase_1.IDatabase {
152
136
  return this.increment(table, incs, where);
153
137
  }
154
138
  async close() { return new Promise((resolve, reject) => this.db.close(err => err ? reject(err) : resolve())); }
139
+ _serializeValue(v) {
140
+ if (v instanceof Date)
141
+ return v.toISOString().slice(0, 19).replace('T', ' ');
142
+ return (typeof v === 'object' && v !== null) ? JSON.stringify(v) : v;
143
+ }
144
+ _buildWhereClause(where) {
145
+ if (!where)
146
+ return { whereClause: '', values: [] };
147
+ const safeWhere = where;
148
+ const keys = Object.keys(safeWhere);
149
+ if (!keys.length)
150
+ return { whereClause: '', values: [] };
151
+ return {
152
+ whereClause: 'WHERE ' + keys.map(k => `"${k}" = ?`).join(' AND '),
153
+ values: keys.map(k => this._serializeValue(safeWhere[k]))
154
+ };
155
+ }
155
156
  }
156
157
  exports.SQLiteDatabase = SQLiteDatabase;
157
158
  exports.default = SQLiteDatabase;
@@ -1,9 +1,5 @@
1
1
  import { IDatabase } from './IDatabase';
2
- export interface ToonConfig {
3
- filePath: string;
4
- saveInterval?: number;
5
- cache?: any;
6
- }
2
+ import { ToonConfig } from './types';
7
3
  export declare class ToonDatabase extends IDatabase {
8
4
  private filePath;
9
5
  private db;
@@ -17,9 +17,9 @@ class ToonDatabase extends IDatabase_1.IDatabase {
17
17
  this.isWriting = false;
18
18
  this.writeQueue = [];
19
19
  this.saveDebounceTimeout = null;
20
- if (!config || !config.filePath)
21
- throw new Error('ToonDB: "filePath" gereklidir.');
22
- this.filePath = config.filePath;
20
+ if (!config || !config.path)
21
+ throw new Error('ToonDB: "path" gereklidir.');
22
+ this.filePath = config.path;
23
23
  this.saveInterval = config.saveInterval || 500;
24
24
  process.on('exit', () => this.flushSync());
25
25
  this.initPromise = this._load();
@@ -86,8 +86,7 @@ class ToonDatabase extends IDatabase_1.IDatabase {
86
86
  clearTimeout(this.saveDebounceTimeout);
87
87
  this.saveDebounceTimeout = null;
88
88
  try {
89
- const toonContent = (0, toon_1.stringify)(this.db);
90
- await promises_1.default.writeFile(this.filePath, toonContent);
89
+ await promises_1.default.writeFile(this.filePath, (0, toon_1.stringify)(this.db));
91
90
  this.isDirty = false;
92
91
  }
93
92
  catch (error) {
@@ -1,53 +1,38 @@
1
- export interface MySQLConfig {
2
- host: string;
3
- user: string;
4
- password?: string;
5
- database: string;
6
- port?: number;
7
- connectionLimit?: number;
8
- cache?: CacheConfig;
9
- }
10
- export interface SQLiteConfig {
11
- filename: string;
12
- cache?: CacheConfig;
13
- }
14
- export interface MongoDBConfig {
15
- uri: string;
16
- dbName: string;
17
- cache?: CacheConfig;
18
- }
19
- export interface PostgreSQLConfig {
20
- host: string;
21
- user: string;
22
- password?: string;
23
- database: string;
24
- port?: number;
25
- connectionLimit?: number;
1
+ export interface BaseConfig {
26
2
  cache?: CacheConfig;
27
3
  }
28
- export interface RedisConfig {
4
+ export interface NetworkConfig extends BaseConfig {
29
5
  host?: string;
30
6
  port?: number;
7
+ username?: string;
31
8
  password?: string;
32
- db?: number;
9
+ database?: string;
10
+ url?: string;
11
+ poolSize?: number;
12
+ }
13
+ export interface FileConfig extends BaseConfig {
14
+ path: string;
15
+ }
16
+ export interface MySQLConfig extends NetworkConfig {
17
+ }
18
+ export interface PostgreSQLConfig extends NetworkConfig {
19
+ }
20
+ export interface SQLiteConfig extends FileConfig {
21
+ }
22
+ export interface MongoDBConfig extends NetworkConfig {
23
+ }
24
+ export interface RedisConfig extends NetworkConfig {
33
25
  keyPrefix?: string;
34
- cache?: CacheConfig;
35
26
  }
36
- export interface JsonConfig {
37
- filePath: string;
27
+ export interface JsonConfig extends FileConfig {
38
28
  saveInterval?: number;
39
- cache?: CacheConfig;
40
29
  }
41
- export interface ZPackConfig {
42
- filePath: string;
30
+ export interface ZPackConfig extends FileConfig {
43
31
  autoFlush?: boolean;
44
- cache?: CacheConfig;
45
32
  indexFields?: Record<string, string[]>;
46
33
  }
47
- export interface ToonConfig {
48
- filePath: string;
34
+ export interface ToonConfig extends FileConfig {
49
35
  saveInterval?: number;
50
- cache?: CacheConfig;
51
36
  }
52
37
  export interface CacheConfig {
53
38
  type: 'memory' | 'redis';
@@ -44,9 +44,7 @@ export declare class ZPackAdapter extends IDatabase {
44
44
  private rowCache;
45
45
  private secondary;
46
46
  private indexedFields;
47
- constructor(config: ZPackConfig & {
48
- indexFields?: Record<string, string[]>;
49
- });
47
+ constructor(config: ZPackConfig);
50
48
  private _init;
51
49
  ensureTable(table: string): Promise<void>;
52
50
  private _updateSecondaryIndex;
@@ -59,8 +57,8 @@ export declare class ZPackAdapter extends IDatabase {
59
57
  delete(table: string, where: Record<string, any>): Promise<number>;
60
58
  set(table: string, data: Record<string, any>, where: Record<string, any>): Promise<any>;
61
59
  bulkInsert(table: string, dataArray: Record<string, any>[]): Promise<number>;
62
- increment(table: string, increments: Record<string, number>, where?: Record<string, any>): Promise<number>;
63
- decrement(table: string, decrements: Record<string, number>, where?: Record<string, any>): Promise<number>;
60
+ increment(table: string, incs: Record<string, number>, where?: Record<string, any>): Promise<number>;
61
+ decrement(table: string, decs: Record<string, number>, where?: Record<string, any>): Promise<number>;
64
62
  vacuum(): Promise<void>;
65
63
  close(): Promise<void>;
66
64
  }
@@ -133,7 +133,7 @@ class ZPackDatabase {
133
133
  }
134
134
  else {
135
135
  this.index.set(meta.docId, cur);
136
- this.deleted.delete(meta.docId);
136
+ this.deleted.add(meta.docId); // Bugfix: added to deleted, should be removed
137
137
  if (meta.docId >= this._nextId)
138
138
  this._nextId = meta.docId + 1;
139
139
  }
@@ -254,9 +254,7 @@ class ZPackDatabase {
254
254
  const decompressed = zlib_1.default.inflateSync(buf.subarray(6));
255
255
  return { docId, fieldCount: 1, document: JSON.parse(decompressed.toString()) };
256
256
  }
257
- catch (e) {
258
- // Fallback if not compressed or corrupted
259
- }
257
+ catch (e) { }
260
258
  }
261
259
  let p = 6;
262
260
  const end = 2 + payloadSize;
@@ -353,10 +351,7 @@ class ZPackAdapter extends IDatabase_1.IDatabase {
353
351
  this.rowCache = new Map();
354
352
  this.secondary = new Map();
355
353
  this.indexedFields = new Map();
356
- this.db = new ZPackDatabase(config.filePath, {
357
- autoFlush: !!config.autoFlush,
358
- compression: !!config.cache
359
- });
354
+ this.db = new ZPackDatabase(config.path, { autoFlush: !!config.autoFlush, compression: !!config.cache });
360
355
  if (config.indexFields) {
361
356
  for (const [table, fields] of Object.entries(config.indexFields)) {
362
357
  this.indexedFields.set(table, new Set(fields));
@@ -395,9 +390,8 @@ class ZPackAdapter extends IDatabase_1.IDatabase {
395
390
  if (!tableIndex.has(field))
396
391
  tableIndex.set(field, new Map());
397
392
  const fieldMap = tableIndex.get(field);
398
- if (oldData && oldData[field] !== undefined) {
393
+ if (oldData && oldData[field] !== undefined)
399
394
  fieldMap.get(String(oldData[field]))?.delete(logicalId);
400
- }
401
395
  if (data[field] !== undefined) {
402
396
  const newVal = String(data[field]);
403
397
  if (!fieldMap.has(newVal))
@@ -420,6 +414,7 @@ class ZPackAdapter extends IDatabase_1.IDatabase {
420
414
  return Object.entries(where).every(([k, v]) => String(row[k]) === String(v));
421
415
  }
422
416
  async select(table, where = null) {
417
+ const start = Date.now();
423
418
  await this.initPromise;
424
419
  await this.ensureTable(table);
425
420
  if (where && Object.keys(where).length === 1) {
@@ -437,6 +432,7 @@ class ZPackAdapter extends IDatabase_1.IDatabase {
437
432
  results.push(doc);
438
433
  }
439
434
  }
435
+ this.recordMetric('select', table, Date.now() - start);
440
436
  return results;
441
437
  }
442
438
  return [];
@@ -455,6 +451,7 @@ class ZPackAdapter extends IDatabase_1.IDatabase {
455
451
  if (this._matches(row, where))
456
452
  results.push({ ...row });
457
453
  }
454
+ this.recordMetric('select', table, Date.now() - start);
458
455
  return results;
459
456
  }
460
457
  async selectOne(table, where = null) {
@@ -462,23 +459,24 @@ class ZPackAdapter extends IDatabase_1.IDatabase {
462
459
  return res[0] || null;
463
460
  }
464
461
  async insert(table, data) {
462
+ const start = Date.now();
465
463
  await this.initPromise;
466
464
  await this.ensureTable(table);
467
- // Trigger beforeInsert BEFORE coercion
468
465
  await this.runHooks('beforeInsert', table, data);
469
466
  const nextId = (this.tableMaxId.get(table) || 0) + 1;
470
467
  const record = this._coerce(table, data, nextId);
471
468
  const physicalId = await this.db.insert(record);
472
469
  this.tableMaxId.set(table, nextId);
473
470
  this.keyIndex.get(table).set(nextId, physicalId);
474
- // Recover original data structure for cache and afterInsert hook
475
471
  const fullRow = { _id: nextId, ...data };
476
472
  this.rowCache.get(table).set(nextId, fullRow);
477
473
  this._updateSecondaryIndex(table, nextId, fullRow);
478
474
  await this.runHooks('afterInsert', table, fullRow);
475
+ this.recordMetric('insert', table, Date.now() - start);
479
476
  return nextId;
480
477
  }
481
478
  async update(table, data, where) {
479
+ const start = Date.now();
482
480
  const rows = await this.select(table, where);
483
481
  for (const row of rows) {
484
482
  const logicalId = row._id;
@@ -491,9 +489,11 @@ class ZPackAdapter extends IDatabase_1.IDatabase {
491
489
  this._updateSecondaryIndex(table, logicalId, merged, row);
492
490
  await this.runHooks('afterUpdate', table, merged);
493
491
  }
492
+ this.recordMetric('update', table, Date.now() - start);
494
493
  return rows.length;
495
494
  }
496
495
  async delete(table, where) {
496
+ const start = Date.now();
497
497
  const rows = await this.select(table, where);
498
498
  for (const row of rows) {
499
499
  const logicalId = row._id;
@@ -507,36 +507,33 @@ class ZPackAdapter extends IDatabase_1.IDatabase {
507
507
  }
508
508
  await this.runHooks('afterDelete', table, row);
509
509
  }
510
+ this.recordMetric('delete', table, Date.now() - start);
510
511
  return rows.length;
511
512
  }
512
513
  async set(table, data, where) {
513
- const existing = await this.selectOne(table, where);
514
- return existing ? this.update(table, data, where) : this.insert(table, { ...where, ...data });
514
+ const ex = await this.selectOne(table, where);
515
+ return ex ? this.update(table, data, where) : this.insert(table, { ...where, ...data });
515
516
  }
516
517
  async bulkInsert(table, dataArray) {
517
- for (const data of dataArray)
518
- await this.insert(table, data);
518
+ for (const d of dataArray)
519
+ await this.insert(table, d);
519
520
  return dataArray.length;
520
521
  }
521
- async increment(table, increments, where = {}) {
522
+ async increment(table, incs, where = {}) {
522
523
  const rows = await this.select(table, where);
523
524
  for (const row of rows) {
524
525
  const updated = { ...row };
525
- for (const [f, v] of Object.entries(increments))
526
+ for (const [f, v] of Object.entries(incs))
526
527
  updated[f] = (Number(updated[f]) || 0) + v;
527
528
  await this.update(table, updated, { _id: row._id });
528
529
  }
529
530
  return rows.length;
530
531
  }
531
- async decrement(table, decrements, where = {}) {
532
- const rows = await this.select(table, where);
533
- for (const row of rows) {
534
- const updated = { ...row };
535
- for (const [f, v] of Object.entries(decrements))
536
- updated[f] = (Number(updated[f]) || 0) - v;
537
- await this.update(table, updated, { _id: row._id });
538
- }
539
- return rows.length;
532
+ async decrement(table, decs, where = {}) {
533
+ const incs = {};
534
+ for (const k in decs)
535
+ incs[k] = -decs[k];
536
+ return this.increment(table, incs, where);
540
537
  }
541
538
  async vacuum() { await this.db.vacuum(); }
542
539
  async close() { await this.db.close(); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onurege3467/zerohelper",
3
- "version": "9.1.0",
3
+ "version": "9.2.0",
4
4
  "description": "ZeroHelper is a versatile high-performance utility library and database framework for Node.js, fully written in TypeScript.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -76,4 +76,4 @@
76
76
  "ts-jest": "^29.2.5",
77
77
  "typescript": "^5.7.2"
78
78
  }
79
- }
79
+ }