@aetherframework/database 1.0.9 → 1.1.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.
Files changed (44) hide show
  1. package/package.json +1 -2
  2. package/src/DatabaseManager.js +0 -565
  3. package/src/core/ConnectionManager.js +0 -351
  4. package/src/core/DatabaseFactory.js +0 -188
  5. package/src/core/MongoQueryBuilder.js +0 -576
  6. package/src/core/PluginManager.js +0 -968
  7. package/src/core/QueryBuilder.js +0 -4398
  8. package/src/core/TransactionManager.js +0 -40
  9. package/src/drivers/clickhouse-driver.js +0 -272
  10. package/src/drivers/index.js +0 -273
  11. package/src/drivers/mongodb-driver.js +0 -87
  12. package/src/drivers/mssql-driver.js +0 -117
  13. package/src/drivers/mysql-driver.js +0 -169
  14. package/src/drivers/oracle-driver.js +0 -101
  15. package/src/drivers/postgres-driver.js +0 -234
  16. package/src/drivers/redis-driver.js +0 -52
  17. package/src/drivers/sqlite-driver.js +0 -67
  18. package/src/middleware/connection-pool.js +0 -455
  19. package/src/middleware/performance-monitor.js +0 -652
  20. package/src/middleware/query-cache.js +0 -500
  21. package/src/middleware/query-logger.js +0 -262
  22. package/src/plugins/AuditPlugin.js +0 -447
  23. package/src/plugins/BasePlugin.js +0 -418
  24. package/src/plugins/BatchOperationPlugin.js +0 -165
  25. package/src/plugins/CachePlugin.js +0 -407
  26. package/src/plugins/CtePlugin.js +0 -523
  27. package/src/plugins/DistributedPlugin.js +0 -543
  28. package/src/plugins/EncryptionPlugin.js +0 -211
  29. package/src/plugins/FullTextSearchPlugin.js +0 -164
  30. package/src/plugins/GeospatialPlugin.js +0 -219
  31. package/src/plugins/GraphQLPlugin.js +0 -162
  32. package/src/plugins/HookPlugin.js +0 -211
  33. package/src/plugins/JsonPlugin.js +0 -366
  34. package/src/plugins/OptimisticLockPlugin.js +0 -374
  35. package/src/plugins/PerformancePlugin.js +0 -175
  36. package/src/plugins/ResiliencePlugin.js +0 -114
  37. package/src/plugins/ShardingPlugin.js +0 -227
  38. package/src/plugins/SoftDeletePlugin.js +0 -258
  39. package/src/plugins/SyncPlugin.js +0 -373
  40. package/src/plugins/VersioningPlugin.js +0 -314
  41. package/src/plugins/WindowFunctionPlugin.js +0 -343
  42. package/src/utils/config-loader.js +0 -632
  43. package/src/utils/error-handler.js +0 -724
  44. package/src/utils/migration-runner.js +0 -1066
@@ -1,373 +0,0 @@
1
- /**
2
- * @license MIT
3
- * Copyright (c) 2026-present AetherFramework Contributors.
4
- * SPDX-License-Identifier: MIT
5
- * @module @aetherframework/database/plugin/SyncPlugin
6
- */
7
- import { BasePlugin } from './BasePlugin.js';
8
-
9
- /**
10
- * Sync Plugin - Provides cross-database data synchronization functionality
11
- * Supports bidirectional sync, conflict resolution, and batch processing
12
- */
13
- export class SyncPlugin extends BasePlugin {
14
- constructor(queryBuilder) {
15
- super(queryBuilder);
16
- this.syncConfig = {
17
- conflictStrategy: 'update',
18
- batchSize: 1000,
19
- keyField: 'id',
20
- timestampField: 'updated_at',
21
- onConflict: null,
22
- onProgress: null
23
- };
24
- }
25
-
26
- _registerMethods() {
27
- // Register sync methods to QueryBuilder
28
- this.queryBuilder.syncTo = this.syncTo.bind(this);
29
- this.queryBuilder.syncBidirectional = this.syncBidirectional.bind(this);
30
- this.queryBuilder.setSyncConfig = this.setSyncConfig.bind(this);
31
- this.queryBuilder.migrateData = this.migrateData.bind(this);
32
- }
33
-
34
- /**
35
- * Set synchronization configuration
36
- * @param {Object} config - Sync configuration
37
- * @returns {QueryBuilder} Query builder instance
38
- */
39
- setSyncConfig(config = {}) {
40
- this.syncConfig = {
41
- ...this.syncConfig,
42
- ...config
43
- };
44
- return this.queryBuilder;
45
- }
46
-
47
- /**
48
- * Sync data to another database
49
- * @param {Object} targetDb - Target database connection
50
- * @param {string} targetTable - Target table name
51
- * @param {Object} options - Sync options
52
- * @returns {Promise<number>} Number of synced records
53
- */
54
- async syncTo(targetDb, targetTable, options = {}) {
55
- const config = {
56
- ...this.syncConfig,
57
- ...options
58
- };
59
-
60
- let offset = 0;
61
- let totalSynced = 0;
62
- let totalRecords = 0;
63
-
64
- // Get total record count
65
- const countResult = await this.queryBuilder.clone()
66
- .selectRaw("COUNT(*) as total")
67
- .first();
68
- totalRecords = parseInt(countResult.total);
69
- while (true) {
70
- // Get batch of data
71
- const sourceData = await this.queryBuilder.clone()
72
- .limit(config.batchSize)
73
- .offset(offset)
74
- .get();
75
-
76
- if (!sourceData || sourceData.length === 0) {
77
- break;
78
- }
79
-
80
- // Sync each record
81
- for (const row of sourceData) {
82
- try {
83
- const targetQuery = new this.queryBuilder.constructor(
84
- targetTable,
85
- targetDb,
86
- this.queryBuilder.dialect
87
- );
88
-
89
- // Build conflict resolution
90
- if (config.conflictStrategy === 'update') {
91
- const updateData = { ...row };
92
- delete updateData[config.keyField];
93
-
94
- await targetQuery
95
- .where(config.keyField, '=', row[config.keyField])
96
- .update(updateData)
97
- .execute();
98
- } else if (config.conflictStrategy === 'ignore') {
99
- try {
100
- await targetQuery.insert(row).execute();
101
- } catch (error) {
102
- // Ignore duplicate errors
103
- if (
104
- !error.message.includes('duplicate') &&
105
- !error.message.includes('unique')
106
- ) {
107
- throw error;
108
- }
109
- }
110
- } else {
111
- // error strategy - just insert, will throw on conflict
112
- await targetQuery.insert(row).execute();
113
- }
114
-
115
- totalSynced++;
116
- } catch (error) {
117
- if (config.onConflict) {
118
- await config.onConflict(row, error);
119
- } else {
120
- throw error;
121
- }
122
- }
123
- }
124
-
125
- offset += config.batchSize;
126
-
127
- // Progress callback
128
- if (config.onProgress) {
129
- config.onProgress({
130
- totalRecords,
131
- synced: totalSynced,
132
- percentage: Math.round((offset / totalRecords) * 100)
133
- });
134
- }
135
-
136
- }
137
-
138
- return totalSynced;
139
- }
140
-
141
- /**
142
- * Two-way sync between databases
143
- * @param {Object} targetDb - Target database connection
144
- * @param {string} targetTable - Target table name
145
- * @param {Object} options - Sync options
146
- * @returns {Promise<Object>} Sync results
147
- */
148
- async syncBidirectional(targetDb, targetTable, options = {}) {
149
- const config = {
150
- ...this.syncConfig,
151
- ...options
152
- };
153
-
154
- const results = {
155
- sourceToTarget: 0,
156
- targetToSource: 0,
157
- conflicts: 0,
158
- errors: []
159
- };
160
-
161
- // Sync from source to target
162
- try {
163
- results.sourceToTarget = await this.syncTo(targetDb, targetTable, {
164
- conflictStrategy: config.conflictStrategy,
165
- keyField: config.keyField,
166
- batchSize: config.batchSize,
167
- onConflict: async (row, error) => {
168
- if (config.conflictStrategy === 'newer_wins') {
169
- try {
170
- // Compare timestamps
171
- const targetQuery = new this.queryBuilder.constructor(
172
- targetTable,
173
- targetDb,
174
- this.queryBuilder.dialect
175
- );
176
- const targetRow = await targetQuery
177
- .where(config.keyField, '=', row[config.keyField])
178
- .first();
179
-
180
- if (
181
- targetRow &&
182
- row[config.timestampField] > targetRow[config.timestampField]
183
- ) {
184
- // Source is newer, update target
185
- const updateData = { ...row };
186
- delete updateData[config.keyField];
187
-
188
- await targetQuery
189
- .where(config.keyField, '=', row[config.keyField])
190
- .update(updateData)
191
- .execute();
192
- results.conflicts++;
193
- }
194
- } catch (compareError) {
195
- results.errors.push(
196
- `Conflict resolution failed: ${compareError.message}`
197
- );
198
- }
199
- }
200
- }
201
- });
202
- } catch (error) {
203
- results.errors.push(`Source to target sync failed: ${error.message}`);
204
- }
205
-
206
- // Sync from target to source
207
- try {
208
- const targetQuery = new this.queryBuilder.constructor(targetTable, targetDb, this.queryBuilder.dialect);
209
- results.targetToSource = await targetQuery.syncTo(
210
- this.queryBuilder.connection,
211
- this.queryBuilder.tableName,
212
- {
213
- conflictStrategy: config.conflictStrategy,
214
- keyField: config.keyField,
215
- batchSize: config.batchSize,
216
- onConflict: async (row, error) => {
217
- if (config.conflictStrategy === 'newer_wins') {
218
- try {
219
- // Compare timestamps
220
- const sourceRow = await this.queryBuilder.clone()
221
- .where(config.keyField, '=', row[config.keyField])
222
- .first();
223
-
224
- if (
225
- sourceRow &&
226
- row[config.timestampField] > sourceRow[config.timestampField]
227
- ) {
228
- // Target is newer, update source
229
- const updateData = { ...row };
230
- delete updateData[config.keyField];
231
-
232
- await this.queryBuilder.clone()
233
- .where(config.keyField, '=', row[config.keyField])
234
- .update(updateData)
235
- .execute();
236
- results.conflicts++;
237
- }
238
- } catch (compareError) {
239
- results.errors.push(
240
- `Conflict resolution failed: ${compareError.message}`
241
- );
242
- }
243
- }
244
- }
245
- }
246
- );
247
- } catch (error) {
248
- results.errors.push(`Target to source sync failed: ${error.message}`);
249
- }
250
-
251
- return results;
252
- }
253
-
254
- /**
255
- * Migrate data from source to target
256
- * @param {string} sourceTable - Source table name
257
- * @param {Object} mapping - Field mapping
258
- * @param {Object} options - Migration options
259
- * @returns {Promise<number>} Number of migrated records
260
- */
261
- async migrateData(sourceTable, mapping, options = {}) {
262
- const {
263
- batchSize = 1000,
264
- transform = null,
265
- validate = null,
266
- onProgress = null
267
- } = options;
268
-
269
- let offset = 0;
270
- let totalMigrated = 0;
271
- let totalProcessed = 0;
272
-
273
- // Get total record count
274
- const countResult = await this.queryBuilder.connection.query(
275
- `SELECT COUNT(*) as total FROM ${sourceTable}`
276
- );
277
- const totalRecords = parseInt(countResult.rows.total);
278
-
279
-
280
-
281
- while (true) {
282
- // Get batch of data
283
- const sourceData = await this.queryBuilder.connection.query(
284
- `SELECT * FROM ${sourceTable} LIMIT ? OFFSET ?`,
285
- [batchSize, offset]
286
- );
287
-
288
- if (!sourceData.rows || sourceData.rows.length === 0) {
289
- break;
290
- }
291
-
292
- // Transform data
293
- const transformedData = sourceData.rows.map((row) => {
294
- let newRow = {};
295
-
296
- // Apply field mapping
297
- Object.entries(mapping).forEach(([sourceField, targetField]) => {
298
- if (typeof targetField === 'function') {
299
- newRow[sourceField] = targetField(row);
300
- } else {
301
- newRow[targetField] = row[sourceField];
302
- }
303
- });
304
-
305
- // Apply transformation if provided
306
- if (transform) {
307
- newRow = transform(newRow, row);
308
- }
309
-
310
- return newRow;
311
- });
312
-
313
- // Validate data if validator provided
314
- if (validate) {
315
- const validationErrors = [];
316
- transformedData.forEach((row, index) => {
317
- const error = validate(row);
318
- if (error) {
319
- validationErrors.push({ index, error, row });
320
- }
321
- });
322
-
323
- if (validationErrors.length > 0) {
324
- throw new Error(
325
- `Validation failed for ${validationErrors.length} records`
326
- );
327
- }
328
- }
329
-
330
- // Insert batch
331
- const result = await this.queryBuilder.clone().insert(transformedData).execute();
332
-
333
- totalMigrated += transformedData.length;
334
- totalProcessed += sourceData.rows.length;
335
- offset += batchSize;
336
-
337
- // Progress callback
338
- if (onProgress) {
339
- onProgress({
340
- totalRecords,
341
- processed: totalProcessed,
342
- migrated: totalMigrated,
343
- batchSize: transformedData.length,
344
- percentage: Math.round((totalProcessed / totalRecords) * 100)
345
- });
346
- }
347
-
348
- }
349
-
350
-
351
- return totalMigrated;
352
- }
353
-
354
- /**
355
- * Get plugin metadata
356
- * @returns {Object} Plugin metadata
357
- */
358
- getMetadata() {
359
- return {
360
- name: 'SyncPlugin',
361
- version: '1.0.0',
362
- description: 'Cross-database data synchronization and migration',
363
- features: [
364
- 'Bidirectional synchronization',
365
- 'Conflict resolution strategies',
366
- 'Batch processing',
367
- 'Data migration',
368
- 'Progress tracking'
369
- ],
370
- strategies: ['update', 'ignore', 'error', 'newer_wins']
371
- };
372
- }
373
- }
@@ -1,314 +0,0 @@
1
- /**
2
- * @license MIT
3
- * Copyright (c) 2026-present AetherFramework Contributors.
4
- * SPDX-License-Identifier: MIT
5
- * @module @aetherframework/database/plugin/VersioningPlugin
6
- */
7
- import { BasePlugin } from './BasePlugin.js';
8
-
9
- /**
10
- * Versioning Plugin - Provides optimistic locking and data versioning functionality.
11
- * Prevents concurrent update conflicts and optionally maintains a version history table.
12
- */
13
- export class VersioningPlugin extends BasePlugin {
14
- constructor(queryBuilder) {
15
- super(queryBuilder);
16
- this.versionColumn = 'version';
17
- this.versionHistoryEnabled = false;
18
- this.versionHistoryTable = null;
19
- this.keyField = 'id';
20
- }
21
-
22
- _registerMethods() {
23
- // Register versioning methods to QueryBuilder
24
- this.queryBuilder.withOptimisticLock = this.withOptimisticLock.bind(this);
25
- this.queryBuilder.updateWithVersion = this.updateWithVersion.bind(this);
26
- this.queryBuilder.createVersionHistory = this.createVersionHistory.bind(this);
27
- this.queryBuilder.getVersionHistory = this.getVersionHistory.bind(this);
28
- this.queryBuilder.restoreFromVersion = this.restoreFromVersion.bind(this);
29
- this.queryBuilder.getCurrentVersion = this.getCurrentVersion.bind(this);
30
- this.queryBuilder.checkVersion = this.checkVersion.bind(this);
31
-
32
- // Override the update method to integrate version checking
33
- this._wrapUpdateMethod();
34
- }
35
-
36
- /**
37
- * Enable optimistic locking for the table.
38
- * @param {string} column - The name of the version column (default: 'version').
39
- * @returns {QueryBuilder} The QueryBuilder instance for chaining.
40
- */
41
- withOptimisticLock(column = 'version') {
42
- this.versionColumn = column;
43
- this.queryBuilder.query.optimisticLock = true;
44
- return this.queryBuilder;
45
- }
46
-
47
- /**
48
- * Perform an update with optimistic lock check.
49
- * @param {Object} data - The data to update.
50
- * @param {number} expectedVersion - The expected current version of the record.
51
- * @param {Object} options - Additional options (e.g., changedBy, changeReason).
52
- * @returns {Promise<Object>} The result of the update operation, including new version.
53
- */
54
- async updateWithVersion(data, expectedVersion, options = {}) {
55
- if (!this.versionColumn) {
56
- throw new Error('Optimistic lock is not enabled. Call withOptimisticLock() first.');
57
- }
58
-
59
- // Fetch the current record to check version and optionally log history
60
- const originalData = await this.queryBuilder.clone().first();
61
- if (!originalData) {
62
- throw new Error('Record not found');
63
- }
64
-
65
- if (originalData[this.versionColumn] !== expectedVersion) {
66
- throw new Error(
67
- `Optimistic lock failed: Record was modified by another transaction. ` +
68
- `Expected version: ${expectedVersion}, Actual version: ${originalData[this.versionColumn]}`
69
- );
70
- }
71
-
72
- // Increment the version in the update data
73
- const updateData = { ...data };
74
- updateData[this.versionColumn] = expectedVersion + 1;
75
-
76
- // Add version condition to WHERE clause
77
- this.queryBuilder.where(this.versionColumn, '=', expectedVersion);
78
-
79
- // Execute the update
80
- const result = await this.queryBuilder.update(updateData).execute();
81
-
82
- if (result.affectedRows === 0) {
83
- throw new Error('Optimistic lock failed: Record was modified by another transaction during update.');
84
- }
85
-
86
- // Record version history if enabled
87
- if (this.versionHistoryEnabled && this.versionHistoryTable) {
88
- await this._recordVersionHistory(originalData, updateData, options);
89
- }
90
-
91
- return {
92
- ...result,
93
- newVersion: updateData[this.versionColumn],
94
- previousVersion: expectedVersion
95
- };
96
- }
97
-
98
- /**
99
- * Create a version history table and enable history tracking.
100
- * @param {string} historyTable - Name of the history table (defaults to `${mainTable}_history`).
101
- * @param {string} keyField - The primary key field name (default: 'id').
102
- * @returns {Promise<QueryBuilder>} The QueryBuilder instance.
103
- */
104
- async createVersionHistory(historyTable = null, keyField = 'id') {
105
- const tableName = historyTable || `${this.queryBuilder.tableName}_history`;
106
- this.versionHistoryEnabled = true;
107
- this.versionHistoryTable = tableName;
108
- this.keyField = keyField;
109
-
110
- // SQL to create the history table (example for MySQL/PostgreSQL syntax)
111
- const createTableSQL = `
112
- CREATE TABLE IF NOT EXISTS ${tableName} (
113
- history_id BIGINT AUTO_INCREMENT PRIMARY KEY,
114
- ${keyField} BIGINT NOT NULL,
115
- version INT NOT NULL,
116
- data JSON NOT NULL,
117
- changed_by VARCHAR(255),
118
- changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
119
- change_reason TEXT,
120
- INDEX idx_${keyField}_version (${keyField}, version),
121
- INDEX idx_changed_at (changed_at)
122
- )
123
- `;
124
-
125
- try {
126
- await this.queryBuilder.connection.query(createTableSQL);
127
- } catch (error) {
128
- console.error(`Failed to create version history table: ${error.message}`);
129
- throw error;
130
- }
131
-
132
- // Attach a hook to automatically record history on updates
133
- this.queryBuilder.addHook('beforeUpdate', async (data, builder) => {
134
- if (builder.query.optimisticLock) {
135
- await this._recordVersionHistoryOnUpdate(data, builder);
136
- }
137
- });
138
-
139
- return this.queryBuilder;
140
- }
141
-
142
- /**
143
- * Retrieve the version history for a specific record.
144
- * @param {number|string} recordId - The ID of the record.
145
- * @param {Object} options - Query options (limit, offset, order).
146
- * @returns {Promise<Array>} Array of historical versions.
147
- */
148
- async getVersionHistory(recordId, options = {}) {
149
- if (!this.versionHistoryEnabled || !this.versionHistoryTable) {
150
- throw new Error('Version history is not enabled. Call createVersionHistory() first.');
151
- }
152
-
153
- const { limit = 100, offset = 0, orderBy = 'changed_at', orderDirection = 'DESC' } = options;
154
-
155
- const historyQuery = new this.queryBuilder.constructor(
156
- this.versionHistoryTable,
157
- this.queryBuilder.connection,
158
- this.queryBuilder.dialect
159
- );
160
-
161
- return historyQuery
162
- .where(this.keyField, '=', recordId)
163
- .orderBy(orderBy, orderDirection)
164
- .limit(limit)
165
- .offset(offset)
166
- .get();
167
- }
168
-
169
- /**
170
- * Restore a record to a specific historical version.
171
- * @param {number} historyId - The ID of the history record to restore from.
172
- * @param {Object} options - Restore options (restoredBy, restoreReason).
173
- * @returns {Promise<Object>} The result of the restore operation.
174
- */
175
- async restoreFromVersion(historyId, options = {}) {
176
- if (!this.versionHistoryEnabled || !this.versionHistoryTable) {
177
- throw new Error('Version history is not enabled. Call createVersionHistory() first.');
178
- }
179
-
180
- // Fetch the historical record
181
- const historyQuery = new this.queryBuilder.constructor(
182
- this.versionHistoryTable,
183
- this.queryBuilder.connection,
184
- this.queryBuilder.dialect
185
- );
186
- const historyRecord = await historyQuery.where('history_id', '=', historyId).first();
187
-
188
- if (!historyRecord) {
189
- throw new Error(`Version history record with ID ${historyId} not found.`);
190
- }
191
-
192
- // Parse the historical data
193
- const historicalData = typeof historyRecord.data === 'string'
194
- ? JSON.parse(historyRecord.data)
195
- : historyRecord.data;
196
-
197
- // Prepare update data, restoring the version and adding audit info
198
- const updateData = {
199
- ...historicalData,
200
- [this.versionColumn]: historyRecord.version,
201
- restored_from_version: historyId,
202
- restored_at: new Date(),
203
- restored_by: options.restoredBy || null,
204
- restore_reason: options.restoreReason || null
205
- };
206
-
207
- // Remove history-specific fields before update
208
- delete updateData.history_id;
209
- delete updateData.changed_at;
210
- delete updateData.changed_by;
211
-
212
- // Perform the restore update
213
- return this.queryBuilder
214
- .where(this.keyField, '=', historyRecord[this.keyField])
215
- .update(updateData)
216
- .execute();
217
- }
218
-
219
- /**
220
- * Get the current version of a record.
221
- * @param {number|string} recordId - The record ID.
222
- * @returns {Promise<number|null>} The current version number, or null if not found.
223
- */
224
- async getCurrentVersion(recordId) {
225
- const record = await this.queryBuilder
226
- .clone()
227
- .where(this.keyField, '=', recordId)
228
- .select(this.versionColumn)
229
- .first();
230
- return record ? record[this.versionColumn] : null;
231
- }
232
-
233
- /**
234
- * Check if a record's current version matches the expected version.
235
- * @param {number|string} recordId - The record ID.
236
- * @param {number} expectedVersion - The version to check against.
237
- * @returns {Promise<boolean>} True if versions match.
238
- */
239
- async checkVersion(recordId, expectedVersion) {
240
- const currentVersion = await this.getCurrentVersion(recordId);
241
- return currentVersion === expectedVersion;
242
- }
243
-
244
- /**
245
- * Wraps the original update method to inject version checking logic.
246
- * @private
247
- */
248
- _wrapUpdateMethod() {
249
- const originalUpdate = this.queryBuilder.update;
250
- this.queryBuilder.update = function(data) {
251
- // If optimistic locking is enabled and a version is provided in the data
252
- if (this.query.optimisticLock && data && data[this.versionColumn] !== undefined) {
253
- const expectedVersion = data[this.versionColumn];
254
- // Remove version from update data as it will be incremented
255
- delete data[this.versionColumn];
256
- // Add version check to WHERE clause
257
- this.where(this.versionColumn, '=', expectedVersion);
258
- // Increment version for the update
259
- data[this.versionColumn] = expectedVersion + 1;
260
- }
261
- return originalUpdate.call(this, data);
262
- }.bind(this.queryBuilder);
263
- }
264
-
265
- /**
266
- * Records a version history entry during an update operation.
267
- * @private
268
- */
269
- async _recordVersionHistoryOnUpdate(data, builder) {
270
- if (!this.versionHistoryEnabled || !this.versionHistoryTable) return;
271
-
272
- const current = await builder.clone().first();
273
- if (current && current[this.versionColumn] !== undefined) {
274
- await this._recordVersionHistory(current, data, {
275
- changed_by: data.changed_by,
276
- change_reason: data.change_reason
277
- });
278
- }
279
- }
280
-
281
- /**
282
- * Inserts a record into the version history table.
283
- * @private
284
- */
285
- async _recordVersionHistory(originalData, newData, options = {}) {
286
- const historyData = {
287
- [this.keyField]: originalData[this.keyField],
288
- version: originalData[this.versionColumn],
289
- data: JSON.stringify(originalData),
290
- changed_by: options.changed_by || null,
291
- change_reason: options.change_reason || null
292
- };
293
-
294
- const historyQuery = new this.queryBuilder.constructor(
295
- this.versionHistoryTable,
296
- this.queryBuilder.connection,
297
- this.queryBuilder.dialect
298
- );
299
- await historyQuery.insert(historyData);
300
- }
301
-
302
- /**
303
- * Returns the plugin's configuration.
304
- * @returns {Object} The configuration object.
305
- */
306
- getConfig() {
307
- return {
308
- versionColumn: this.versionColumn,
309
- versionHistoryEnabled: this.versionHistoryEnabled,
310
- versionHistoryTable: this.versionHistoryTable,
311
- keyField: this.keyField
312
- };
313
- }
314
- }