s3db.js 6.2.0 → 7.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PLUGINS.md +2724 -0
- package/README.md +372 -469
- package/UNLICENSE +24 -0
- package/dist/s3db.cjs.js +12105 -19396
- package/dist/s3db.cjs.min.js +1 -1
- package/dist/s3db.d.ts +373 -72
- package/dist/s3db.es.js +12090 -19393
- package/dist/s3db.es.min.js +1 -1
- package/dist/s3db.iife.js +12103 -19398
- package/dist/s3db.iife.min.js +1 -1
- package/package.json +44 -38
- package/src/behaviors/body-only.js +110 -0
- package/src/behaviors/body-overflow.js +153 -0
- package/src/behaviors/enforce-limits.js +195 -0
- package/src/behaviors/index.js +39 -0
- package/src/behaviors/truncate-data.js +204 -0
- package/src/behaviors/user-managed.js +147 -0
- package/src/client.class.js +515 -0
- package/src/concerns/base62.js +61 -0
- package/src/concerns/calculator.js +204 -0
- package/src/concerns/crypto.js +159 -0
- package/src/concerns/id.js +8 -0
- package/src/concerns/index.js +5 -0
- package/src/concerns/try-fn.js +151 -0
- package/src/connection-string.class.js +75 -0
- package/src/database.class.js +599 -0
- package/src/errors.js +261 -0
- package/src/index.js +17 -0
- package/src/plugins/audit.plugin.js +442 -0
- package/src/plugins/cache/cache.class.js +53 -0
- package/src/plugins/cache/index.js +6 -0
- package/src/plugins/cache/memory-cache.class.js +164 -0
- package/src/plugins/cache/s3-cache.class.js +189 -0
- package/src/plugins/cache.plugin.js +275 -0
- package/src/plugins/consumers/index.js +24 -0
- package/src/plugins/consumers/rabbitmq-consumer.js +56 -0
- package/src/plugins/consumers/sqs-consumer.js +102 -0
- package/src/plugins/costs.plugin.js +81 -0
- package/src/plugins/fulltext.plugin.js +473 -0
- package/src/plugins/index.js +12 -0
- package/src/plugins/metrics.plugin.js +603 -0
- package/src/plugins/plugin.class.js +210 -0
- package/src/plugins/plugin.obj.js +13 -0
- package/src/plugins/queue-consumer.plugin.js +134 -0
- package/src/plugins/replicator.plugin.js +769 -0
- package/src/plugins/replicators/base-replicator.class.js +85 -0
- package/src/plugins/replicators/bigquery-replicator.class.js +328 -0
- package/src/plugins/replicators/index.js +44 -0
- package/src/plugins/replicators/postgres-replicator.class.js +427 -0
- package/src/plugins/replicators/s3db-replicator.class.js +352 -0
- package/src/plugins/replicators/sqs-replicator.class.js +427 -0
- package/src/resource.class.js +2626 -0
- package/src/s3db.d.ts +1263 -0
- package/src/schema.class.js +706 -0
- package/src/stream/index.js +16 -0
- package/src/stream/resource-ids-page-reader.class.js +10 -0
- package/src/stream/resource-ids-reader.class.js +63 -0
- package/src/stream/resource-reader.class.js +81 -0
- package/src/stream/resource-writer.class.js +92 -0
- package/src/validator.class.js +97 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import EventEmitter from 'events';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base class for all replicator drivers
|
|
5
|
+
* Defines the interface that all replicators must implement
|
|
6
|
+
*/
|
|
7
|
+
export class BaseReplicator extends EventEmitter {
|
|
8
|
+
constructor(config = {}) {
|
|
9
|
+
super();
|
|
10
|
+
this.config = config;
|
|
11
|
+
this.name = this.constructor.name;
|
|
12
|
+
this.enabled = config.enabled !== false; // Default to enabled unless explicitly disabled
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Initialize the replicator
|
|
17
|
+
* @param {Object} database - The s3db database instance
|
|
18
|
+
* @returns {Promise<void>}
|
|
19
|
+
*/
|
|
20
|
+
async initialize(database) {
|
|
21
|
+
this.database = database;
|
|
22
|
+
this.emit('initialized', { replicator: this.name });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Replicate data to the target
|
|
27
|
+
* @param {string} resourceName - Name of the resource being replicated
|
|
28
|
+
* @param {string} operation - Operation type (insert, update, delete)
|
|
29
|
+
* @param {Object} data - The data to replicate
|
|
30
|
+
* @param {string} id - Record ID
|
|
31
|
+
* @returns {Promise<Object>} replicator result
|
|
32
|
+
*/
|
|
33
|
+
async replicate(resourceName, operation, data, id) {
|
|
34
|
+
throw new Error(`replicate() method must be implemented by ${this.name}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Replicate multiple records in batch
|
|
39
|
+
* @param {string} resourceName - Name of the resource being replicated
|
|
40
|
+
* @param {Array} records - Array of records to replicate
|
|
41
|
+
* @returns {Promise<Object>} Batch replicator result
|
|
42
|
+
*/
|
|
43
|
+
async replicateBatch(resourceName, records) {
|
|
44
|
+
throw new Error(`replicateBatch() method must be implemented by ${this.name}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Test the connection to the target
|
|
49
|
+
* @returns {Promise<boolean>} True if connection is successful
|
|
50
|
+
*/
|
|
51
|
+
async testConnection() {
|
|
52
|
+
throw new Error(`testConnection() method must be implemented by ${this.name}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get replicator status and statistics
|
|
57
|
+
* @returns {Promise<Object>} Status information
|
|
58
|
+
*/
|
|
59
|
+
async getStatus() {
|
|
60
|
+
return {
|
|
61
|
+
name: this.name,
|
|
62
|
+
// Removed: enabled: this.enabled,
|
|
63
|
+
config: this.config,
|
|
64
|
+
connected: false
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Cleanup resources
|
|
70
|
+
* @returns {Promise<void>}
|
|
71
|
+
*/
|
|
72
|
+
async cleanup() {
|
|
73
|
+
this.emit('cleanup', { replicator: this.name });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Validate replicator configuration
|
|
78
|
+
* @returns {Object} Validation result
|
|
79
|
+
*/
|
|
80
|
+
validateConfig() {
|
|
81
|
+
return { isValid: true, errors: [] };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export default BaseReplicator;
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import BaseReplicator from './base-replicator.class.js';
|
|
2
|
+
import tryFn from "../../concerns/try-fn.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* BigQuery Replicator
|
|
6
|
+
*
|
|
7
|
+
* Replicates data to Google BigQuery tables, supporting per-resource table mapping and action filtering.
|
|
8
|
+
*
|
|
9
|
+
* ⚠️ REQUIRED DEPENDENCY: You must install the Google Cloud BigQuery SDK to use this replicator:
|
|
10
|
+
*
|
|
11
|
+
* ```bash
|
|
12
|
+
* npm install @google-cloud/bigquery
|
|
13
|
+
* # or
|
|
14
|
+
* yarn add @google-cloud/bigquery
|
|
15
|
+
* # or
|
|
16
|
+
* pnpm add @google-cloud/bigquery
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @config {Object} config - Configuration object for the replicator
|
|
20
|
+
* @config {string} config.projectId - (Required) Google Cloud project ID
|
|
21
|
+
* @config {string} config.datasetId - (Required) BigQuery dataset ID
|
|
22
|
+
* @config {Object} [config.credentials] - (Optional) Google service account credentials object (JSON). If omitted, uses default credentials.
|
|
23
|
+
* @config {string} [config.location='US'] - (Optional) BigQuery dataset location/region
|
|
24
|
+
* @config {string} [config.logTable] - (Optional) Table name for operation logging. If omitted, no logging is performed.
|
|
25
|
+
* @config {Object} resources - Resource configuration mapping
|
|
26
|
+
* @config {Object|string} resources[resourceName] - Resource configuration
|
|
27
|
+
* @config {string} resources[resourceName].table - Table name for this resource
|
|
28
|
+
* @config {Array} resources[resourceName].actions - Array of actions to replicate (insert, update, delete)
|
|
29
|
+
* @config {string} resources[resourceName] - Short form: just the table name (equivalent to { actions: ['insert'], table: tableName })
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* new BigqueryReplicator({
|
|
33
|
+
* projectId: 'my-gcp-project',
|
|
34
|
+
* datasetId: 'analytics',
|
|
35
|
+
* location: 'US',
|
|
36
|
+
* credentials: require('./gcp-service-account.json'),
|
|
37
|
+
* logTable: 's3db_replicator_log'
|
|
38
|
+
* }, {
|
|
39
|
+
* users: [
|
|
40
|
+
* { actions: ['insert', 'update', 'delete'], table: 'users_table' },
|
|
41
|
+
* ],
|
|
42
|
+
* urls: [
|
|
43
|
+
* { actions: ['insert'], table: 'urls_table' },
|
|
44
|
+
* { actions: ['insert'], table: 'urls_table_v2' },
|
|
45
|
+
* ],
|
|
46
|
+
* clicks: 'clicks_table' // equivalent to { actions: ['insert'], table: 'clicks_table' }
|
|
47
|
+
* })
|
|
48
|
+
*
|
|
49
|
+
* Notes:
|
|
50
|
+
* - The target tables must exist and have columns matching the resource attributes (id is required as primary key)
|
|
51
|
+
* - The log table must have columns: resource_name, operation, record_id, data, timestamp, source
|
|
52
|
+
* - Uses @google-cloud/bigquery SDK
|
|
53
|
+
*/
|
|
54
|
+
class BigqueryReplicator extends BaseReplicator {
|
|
55
|
+
constructor(config = {}, resources = {}) {
|
|
56
|
+
super(config);
|
|
57
|
+
this.projectId = config.projectId;
|
|
58
|
+
this.datasetId = config.datasetId;
|
|
59
|
+
this.bigqueryClient = null;
|
|
60
|
+
this.credentials = config.credentials;
|
|
61
|
+
this.location = config.location || 'US';
|
|
62
|
+
this.logTable = config.logTable;
|
|
63
|
+
|
|
64
|
+
// Parse resources configuration
|
|
65
|
+
this.resources = this.parseResourcesConfig(resources);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
parseResourcesConfig(resources) {
|
|
69
|
+
const parsed = {};
|
|
70
|
+
|
|
71
|
+
for (const [resourceName, config] of Object.entries(resources)) {
|
|
72
|
+
if (typeof config === 'string') {
|
|
73
|
+
// Short form: just table name
|
|
74
|
+
parsed[resourceName] = [{
|
|
75
|
+
table: config,
|
|
76
|
+
actions: ['insert']
|
|
77
|
+
}];
|
|
78
|
+
} else if (Array.isArray(config)) {
|
|
79
|
+
// Array form: multiple table mappings
|
|
80
|
+
parsed[resourceName] = config.map(item => {
|
|
81
|
+
if (typeof item === 'string') {
|
|
82
|
+
return { table: item, actions: ['insert'] };
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
table: item.table,
|
|
86
|
+
actions: item.actions || ['insert']
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
} else if (typeof config === 'object') {
|
|
90
|
+
// Single object form
|
|
91
|
+
parsed[resourceName] = [{
|
|
92
|
+
table: config.table,
|
|
93
|
+
actions: config.actions || ['insert']
|
|
94
|
+
}];
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return parsed;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
validateConfig() {
|
|
102
|
+
const errors = [];
|
|
103
|
+
if (!this.projectId) errors.push('projectId is required');
|
|
104
|
+
if (!this.datasetId) errors.push('datasetId is required');
|
|
105
|
+
if (Object.keys(this.resources).length === 0) errors.push('At least one resource must be configured');
|
|
106
|
+
|
|
107
|
+
// Validate resource configurations
|
|
108
|
+
for (const [resourceName, tables] of Object.entries(this.resources)) {
|
|
109
|
+
for (const tableConfig of tables) {
|
|
110
|
+
if (!tableConfig.table) {
|
|
111
|
+
errors.push(`Table name is required for resource '${resourceName}'`);
|
|
112
|
+
}
|
|
113
|
+
if (!Array.isArray(tableConfig.actions) || tableConfig.actions.length === 0) {
|
|
114
|
+
errors.push(`Actions array is required for resource '${resourceName}'`);
|
|
115
|
+
}
|
|
116
|
+
const validActions = ['insert', 'update', 'delete'];
|
|
117
|
+
const invalidActions = tableConfig.actions.filter(action => !validActions.includes(action));
|
|
118
|
+
if (invalidActions.length > 0) {
|
|
119
|
+
errors.push(`Invalid actions for resource '${resourceName}': ${invalidActions.join(', ')}. Valid actions: ${validActions.join(', ')}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return { isValid: errors.length === 0, errors };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async initialize(database) {
|
|
128
|
+
await super.initialize(database);
|
|
129
|
+
const [ok, err, sdk] = await tryFn(() => import('@google-cloud/bigquery'));
|
|
130
|
+
if (!ok) {
|
|
131
|
+
this.emit('initialization_error', { replicator: this.name, error: err.message });
|
|
132
|
+
throw err;
|
|
133
|
+
}
|
|
134
|
+
const { BigQuery } = sdk;
|
|
135
|
+
this.bigqueryClient = new BigQuery({
|
|
136
|
+
projectId: this.projectId,
|
|
137
|
+
credentials: this.credentials,
|
|
138
|
+
location: this.location
|
|
139
|
+
});
|
|
140
|
+
this.emit('initialized', {
|
|
141
|
+
replicator: this.name,
|
|
142
|
+
projectId: this.projectId,
|
|
143
|
+
datasetId: this.datasetId,
|
|
144
|
+
resources: Object.keys(this.resources)
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
shouldReplicateResource(resourceName) {
|
|
149
|
+
return this.resources.hasOwnProperty(resourceName);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
shouldReplicateAction(resourceName, operation) {
|
|
153
|
+
if (!this.resources[resourceName]) return false;
|
|
154
|
+
|
|
155
|
+
return this.resources[resourceName].some(tableConfig =>
|
|
156
|
+
tableConfig.actions.includes(operation)
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
getTablesForResource(resourceName, operation) {
|
|
161
|
+
if (!this.resources[resourceName]) return [];
|
|
162
|
+
|
|
163
|
+
return this.resources[resourceName]
|
|
164
|
+
.filter(tableConfig => tableConfig.actions.includes(operation))
|
|
165
|
+
.map(tableConfig => tableConfig.table);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async replicate(resourceName, operation, data, id, beforeData = null) {
|
|
169
|
+
|
|
170
|
+
if (!this.enabled || !this.shouldReplicateResource(resourceName)) {
|
|
171
|
+
return { skipped: true, reason: 'resource_not_included' };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (!this.shouldReplicateAction(resourceName, operation)) {
|
|
175
|
+
return { skipped: true, reason: 'action_not_included' };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const tables = this.getTablesForResource(resourceName, operation);
|
|
179
|
+
if (tables.length === 0) {
|
|
180
|
+
return { skipped: true, reason: 'no_tables_for_action' };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const results = [];
|
|
184
|
+
const errors = [];
|
|
185
|
+
|
|
186
|
+
const [ok, err, result] = await tryFn(async () => {
|
|
187
|
+
const dataset = this.bigqueryClient.dataset(this.datasetId);
|
|
188
|
+
// Replicate to all applicable tables
|
|
189
|
+
for (const tableId of tables) {
|
|
190
|
+
const [okTable, errTable] = await tryFn(async () => {
|
|
191
|
+
const table = dataset.table(tableId);
|
|
192
|
+
let job;
|
|
193
|
+
if (operation === 'insert') {
|
|
194
|
+
const row = { ...data };
|
|
195
|
+
job = await table.insert([row]);
|
|
196
|
+
} else if (operation === 'update') {
|
|
197
|
+
const keys = Object.keys(data).filter(k => k !== 'id');
|
|
198
|
+
const setClause = keys.map(k => `${k}=@${k}`).join(', ');
|
|
199
|
+
const params = { id };
|
|
200
|
+
keys.forEach(k => { params[k] = data[k]; });
|
|
201
|
+
const query = `UPDATE \`${this.projectId}.${this.datasetId}.${tableId}\` SET ${setClause} WHERE id=@id`;
|
|
202
|
+
const [updateJob] = await this.bigqueryClient.createQueryJob({
|
|
203
|
+
query,
|
|
204
|
+
params
|
|
205
|
+
});
|
|
206
|
+
await updateJob.getQueryResults();
|
|
207
|
+
job = [updateJob];
|
|
208
|
+
} else if (operation === 'delete') {
|
|
209
|
+
const query = `DELETE FROM \`${this.projectId}.${this.datasetId}.${tableId}\` WHERE id=@id`;
|
|
210
|
+
const [deleteJob] = await this.bigqueryClient.createQueryJob({
|
|
211
|
+
query,
|
|
212
|
+
params: { id }
|
|
213
|
+
});
|
|
214
|
+
await deleteJob.getQueryResults();
|
|
215
|
+
job = [deleteJob];
|
|
216
|
+
} else {
|
|
217
|
+
throw new Error(`Unsupported operation: ${operation}`);
|
|
218
|
+
}
|
|
219
|
+
results.push({
|
|
220
|
+
table: tableId,
|
|
221
|
+
success: true,
|
|
222
|
+
jobId: job[0]?.id
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
if (!okTable) {
|
|
226
|
+
errors.push({
|
|
227
|
+
table: tableId,
|
|
228
|
+
error: errTable.message
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// Log operation if logTable is configured
|
|
233
|
+
if (this.logTable) {
|
|
234
|
+
const [okLog, errLog] = await tryFn(async () => {
|
|
235
|
+
const logTable = dataset.table(this.logTable);
|
|
236
|
+
await logTable.insert([{
|
|
237
|
+
resource_name: resourceName,
|
|
238
|
+
operation,
|
|
239
|
+
record_id: id,
|
|
240
|
+
data: JSON.stringify(data),
|
|
241
|
+
timestamp: new Date().toISOString(),
|
|
242
|
+
source: 's3db-replicator'
|
|
243
|
+
}]);
|
|
244
|
+
});
|
|
245
|
+
if (!okLog) {
|
|
246
|
+
// Don't fail the main operation if logging fails
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const success = errors.length === 0;
|
|
250
|
+
this.emit('replicated', {
|
|
251
|
+
replicator: this.name,
|
|
252
|
+
resourceName,
|
|
253
|
+
operation,
|
|
254
|
+
id,
|
|
255
|
+
tables,
|
|
256
|
+
results,
|
|
257
|
+
errors,
|
|
258
|
+
success
|
|
259
|
+
});
|
|
260
|
+
return {
|
|
261
|
+
success,
|
|
262
|
+
results,
|
|
263
|
+
errors,
|
|
264
|
+
tables
|
|
265
|
+
};
|
|
266
|
+
});
|
|
267
|
+
if (ok) return result;
|
|
268
|
+
this.emit('replicator_error', {
|
|
269
|
+
replicator: this.name,
|
|
270
|
+
resourceName,
|
|
271
|
+
operation,
|
|
272
|
+
id,
|
|
273
|
+
error: err.message
|
|
274
|
+
});
|
|
275
|
+
return { success: false, error: err.message };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async replicateBatch(resourceName, records) {
|
|
279
|
+
const results = [];
|
|
280
|
+
const errors = [];
|
|
281
|
+
|
|
282
|
+
for (const record of records) {
|
|
283
|
+
const [ok, err, res] = await tryFn(() => this.replicate(
|
|
284
|
+
resourceName,
|
|
285
|
+
record.operation,
|
|
286
|
+
record.data,
|
|
287
|
+
record.id,
|
|
288
|
+
record.beforeData
|
|
289
|
+
));
|
|
290
|
+
if (ok) results.push(res);
|
|
291
|
+
else errors.push({ id: record.id, error: err.message });
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
success: errors.length === 0,
|
|
296
|
+
results,
|
|
297
|
+
errors
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async testConnection() {
|
|
302
|
+
const [ok, err] = await tryFn(async () => {
|
|
303
|
+
if (!this.bigqueryClient) await this.initialize();
|
|
304
|
+
const dataset = this.bigqueryClient.dataset(this.datasetId);
|
|
305
|
+
await dataset.getMetadata();
|
|
306
|
+
return true;
|
|
307
|
+
});
|
|
308
|
+
if (ok) return true;
|
|
309
|
+
this.emit('connection_error', { replicator: this.name, error: err.message });
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async cleanup() {
|
|
314
|
+
// BigQuery SDK doesn't need cleanup
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
getStatus() {
|
|
318
|
+
return {
|
|
319
|
+
...super.getStatus(),
|
|
320
|
+
projectId: this.projectId,
|
|
321
|
+
datasetId: this.datasetId,
|
|
322
|
+
resources: this.resources,
|
|
323
|
+
logTable: this.logTable
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export default BigqueryReplicator;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import BaseReplicator from './base-replicator.class.js';
|
|
2
|
+
import BigqueryReplicator from './bigquery-replicator.class.js';
|
|
3
|
+
import PostgresReplicator from './postgres-replicator.class.js';
|
|
4
|
+
import S3dbReplicator from './s3db-replicator.class.js';
|
|
5
|
+
import SqsReplicator from './sqs-replicator.class.js';
|
|
6
|
+
|
|
7
|
+
export { BaseReplicator, BigqueryReplicator, PostgresReplicator, S3dbReplicator, SqsReplicator };
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Available replicator drivers
|
|
11
|
+
*/
|
|
12
|
+
export const REPLICATOR_DRIVERS = {
|
|
13
|
+
s3db: S3dbReplicator,
|
|
14
|
+
sqs: SqsReplicator,
|
|
15
|
+
bigquery: BigqueryReplicator,
|
|
16
|
+
postgres: PostgresReplicator
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Create a replicator instance based on driver type
|
|
21
|
+
* @param {string} driver - Driver type (s3db, sqs, bigquery, postgres)
|
|
22
|
+
* @param {Object} config - Replicator configuration
|
|
23
|
+
* @returns {BaseReplicator} Replicator instance
|
|
24
|
+
*/
|
|
25
|
+
export function createReplicator(driver, config = {}, resources = [], client = null) {
|
|
26
|
+
const ReplicatorClass = REPLICATOR_DRIVERS[driver];
|
|
27
|
+
|
|
28
|
+
if (!ReplicatorClass) {
|
|
29
|
+
throw new Error(`Unknown replicator driver: ${driver}. Available drivers: ${Object.keys(REPLICATOR_DRIVERS).join(', ')}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return new ReplicatorClass(config, resources, client);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Validate replicator configuration
|
|
37
|
+
* @param {string} driver - Driver type
|
|
38
|
+
* @param {Object} config - Configuration to validate
|
|
39
|
+
* @returns {Object} Validation result
|
|
40
|
+
*/
|
|
41
|
+
export function validateReplicatorConfig(driver, config, resources = [], client = null) {
|
|
42
|
+
const replicator = createReplicator(driver, config, resources, client);
|
|
43
|
+
return replicator.validateConfig();
|
|
44
|
+
}
|