s3db.js 7.3.5 → 7.3.6
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 +1285 -157
- package/dist/s3db.cjs.js +296 -83
- package/dist/s3db.cjs.min.js +1 -1
- package/dist/s3db.es.js +296 -83
- package/dist/s3db.es.min.js +1 -1
- package/dist/s3db.iife.js +296 -83
- package/dist/s3db.iife.min.js +1 -1
- package/mcp/server.js +1410 -0
- package/package.json +7 -1
- package/src/plugins/cache/filesystem-cache.class.js +9 -0
- package/src/plugins/replicator.plugin.js +130 -46
- package/src/plugins/replicators/bigquery-replicator.class.js +31 -5
- package/src/plugins/replicators/postgres-replicator.class.js +17 -2
- package/src/plugins/replicators/s3db-replicator.class.js +172 -68
- package/src/plugins/replicators/sqs-replicator.class.js +13 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "s3db.js",
|
|
3
|
-
"version": "7.3.
|
|
3
|
+
"version": "7.3.6",
|
|
4
4
|
"description": "Use AWS S3, the world's most reliable document storage, as a database with this ORM.",
|
|
5
5
|
"main": "dist/s3db.cjs.js",
|
|
6
6
|
"module": "dist/s3db.es.js",
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
"jsdelivr": "dist/s3db.iife.min.js",
|
|
11
11
|
"author": "@stone/martech",
|
|
12
12
|
"license": "UNLICENSED",
|
|
13
|
+
"bin": {
|
|
14
|
+
"s3db-mcp": "./mcp/server.js"
|
|
15
|
+
},
|
|
13
16
|
"repository": {
|
|
14
17
|
"type": "git",
|
|
15
18
|
"url": "git+https://github.com/forattini-dev/s3db.js.git"
|
|
@@ -26,6 +29,9 @@
|
|
|
26
29
|
"type": "module",
|
|
27
30
|
"sideEffects": false,
|
|
28
31
|
"imports": {
|
|
32
|
+
"#mcp/*": "./mcp/*",
|
|
33
|
+
"#dist/*": "./dist/*",
|
|
34
|
+
"#examples/*": "./examples/*",
|
|
29
35
|
"#src/*": "./src/*",
|
|
30
36
|
"#tests/*": "./tests/*"
|
|
31
37
|
},
|
|
@@ -428,6 +428,15 @@ export class FilesystemCache extends Cache {
|
|
|
428
428
|
|
|
429
429
|
async _clear(prefix) {
|
|
430
430
|
try {
|
|
431
|
+
// Check if directory exists before trying to read it
|
|
432
|
+
if (!await this._fileExists(this.directory)) {
|
|
433
|
+
// Directory doesn't exist, nothing to clear
|
|
434
|
+
if (this.enableStats) {
|
|
435
|
+
this.stats.clears++;
|
|
436
|
+
}
|
|
437
|
+
return true;
|
|
438
|
+
}
|
|
439
|
+
|
|
431
440
|
const files = await readdir(this.directory);
|
|
432
441
|
const cacheFiles = files.filter(file => {
|
|
433
442
|
if (!file.startsWith(this.prefix)) return false;
|
|
@@ -61,19 +61,13 @@ function normalizeResourceName(name) {
|
|
|
61
61
|
* 2. Map: source resource → destination resource name:
|
|
62
62
|
* resources: { users: 'people' }
|
|
63
63
|
*
|
|
64
|
-
* 3. Map: source resource →
|
|
65
|
-
* resources: { users:
|
|
64
|
+
* 3. Map: source resource → { resource, transform }:
|
|
65
|
+
* resources: { users: { resource: 'people', transform: fn } }
|
|
66
66
|
*
|
|
67
|
-
* 4. Map: source resource →
|
|
68
|
-
* resources: { users: { resource: 'people', transformer: fn } }
|
|
69
|
-
*
|
|
70
|
-
* 5. Map: source resource → array of objects (multi-destination):
|
|
71
|
-
* resources: { users: [ { resource: 'people', transformer: fn } ] }
|
|
72
|
-
*
|
|
73
|
-
* 6. Map: source resource → function (transformer only):
|
|
67
|
+
* 4. Map: source resource → function (transformer only):
|
|
74
68
|
* resources: { users: (el) => ({ ...el, fullName: el.name }) }
|
|
75
69
|
*
|
|
76
|
-
*
|
|
70
|
+
* The transform function is optional and applies to data before replication.
|
|
77
71
|
*
|
|
78
72
|
* === Example Plugin Configurations ===
|
|
79
73
|
*
|
|
@@ -93,10 +87,10 @@ function normalizeResourceName(name) {
|
|
|
93
87
|
* ]
|
|
94
88
|
* });
|
|
95
89
|
*
|
|
96
|
-
* // Advanced mapping with
|
|
90
|
+
* // Advanced mapping with transform
|
|
97
91
|
* new ReplicatorPlugin({
|
|
98
92
|
* replicators: [
|
|
99
|
-
* { driver: 's3db', client: dbB, config: { resources: { users:
|
|
93
|
+
* { driver: 's3db', client: dbB, config: { resources: { users: { resource: 'people', transform: (el) => ({ ...el, fullName: el.name }) } } } }
|
|
100
94
|
* ]
|
|
101
95
|
* });
|
|
102
96
|
*
|
|
@@ -177,27 +171,42 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
177
171
|
}
|
|
178
172
|
|
|
179
173
|
resource.on('insert', async (data) => {
|
|
180
|
-
|
|
174
|
+
const [ok, error] = await tryFn(async () => {
|
|
181
175
|
const completeData = { ...data, createdAt: new Date().toISOString() };
|
|
182
176
|
await plugin.processReplicatorEvent('insert', resource.name, completeData.id, completeData);
|
|
183
|
-
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
if (!ok) {
|
|
180
|
+
if (this.config.verbose) {
|
|
181
|
+
console.warn(`[ReplicatorPlugin] Insert event failed for resource ${resource.name}: ${error.message}`);
|
|
182
|
+
}
|
|
184
183
|
this.emit('error', { operation: 'insert', error: error.message, resource: resource.name });
|
|
185
184
|
}
|
|
186
185
|
});
|
|
187
186
|
|
|
188
187
|
resource.on('update', async (data, beforeData) => {
|
|
189
|
-
|
|
188
|
+
const [ok, error] = await tryFn(async () => {
|
|
190
189
|
const completeData = { ...data, updatedAt: new Date().toISOString() };
|
|
191
190
|
await plugin.processReplicatorEvent('update', resource.name, completeData.id, completeData, beforeData);
|
|
192
|
-
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
if (!ok) {
|
|
194
|
+
if (this.config.verbose) {
|
|
195
|
+
console.warn(`[ReplicatorPlugin] Update event failed for resource ${resource.name}: ${error.message}`);
|
|
196
|
+
}
|
|
193
197
|
this.emit('error', { operation: 'update', error: error.message, resource: resource.name });
|
|
194
198
|
}
|
|
195
199
|
});
|
|
196
200
|
|
|
197
201
|
resource.on('delete', async (data) => {
|
|
198
|
-
|
|
202
|
+
const [ok, error] = await tryFn(async () => {
|
|
199
203
|
await plugin.processReplicatorEvent('delete', resource.name, data.id, data);
|
|
200
|
-
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
if (!ok) {
|
|
207
|
+
if (this.config.verbose) {
|
|
208
|
+
console.warn(`[ReplicatorPlugin] Delete event failed for resource ${resource.name}: ${error.message}`);
|
|
209
|
+
}
|
|
201
210
|
this.emit('error', { operation: 'delete', error: error.message, resource: resource.name });
|
|
202
211
|
}
|
|
203
212
|
});
|
|
@@ -219,14 +228,19 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
219
228
|
async setup(database) {
|
|
220
229
|
this.database = database;
|
|
221
230
|
|
|
222
|
-
|
|
231
|
+
const [initOk, initError] = await tryFn(async () => {
|
|
223
232
|
await this.initializeReplicators(database);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
if (!initOk) {
|
|
236
|
+
if (this.config.verbose) {
|
|
237
|
+
console.warn(`[ReplicatorPlugin] Replicator initialization failed: ${initError.message}`);
|
|
238
|
+
}
|
|
239
|
+
this.emit('error', { operation: 'setup', error: initError.message });
|
|
240
|
+
throw initError;
|
|
227
241
|
}
|
|
228
242
|
|
|
229
|
-
|
|
243
|
+
const [logOk, logError] = await tryFn(async () => {
|
|
230
244
|
if (this.config.replicatorLogResource) {
|
|
231
245
|
const logRes = await database.createResource({
|
|
232
246
|
name: this.config.replicatorLogResource,
|
|
@@ -243,8 +257,16 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
243
257
|
}
|
|
244
258
|
});
|
|
245
259
|
}
|
|
246
|
-
}
|
|
247
|
-
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
if (!logOk) {
|
|
263
|
+
if (this.config.verbose) {
|
|
264
|
+
console.warn(`[ReplicatorPlugin] Failed to create log resource ${this.config.replicatorLogResource}: ${logError.message}`);
|
|
265
|
+
}
|
|
266
|
+
this.emit('replicator_log_resource_creation_error', {
|
|
267
|
+
resourceName: this.config.replicatorLogResource,
|
|
268
|
+
error: logError.message
|
|
269
|
+
});
|
|
248
270
|
}
|
|
249
271
|
|
|
250
272
|
await this.uploadMetadataFile(database);
|
|
@@ -307,15 +329,24 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
307
329
|
async retryWithBackoff(operation, maxRetries = 3) {
|
|
308
330
|
let lastError;
|
|
309
331
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
332
|
+
const [ok, error] = await tryFn(operation);
|
|
333
|
+
|
|
334
|
+
if (ok) {
|
|
335
|
+
return ok;
|
|
336
|
+
} else {
|
|
313
337
|
lastError = error;
|
|
338
|
+
if (this.config.verbose) {
|
|
339
|
+
console.warn(`[ReplicatorPlugin] Retry attempt ${attempt}/${maxRetries} failed: ${error.message}`);
|
|
340
|
+
}
|
|
341
|
+
|
|
314
342
|
if (attempt === maxRetries) {
|
|
315
343
|
throw error;
|
|
316
344
|
}
|
|
317
345
|
// Simple backoff: wait 1s, 2s, 4s...
|
|
318
346
|
const delay = Math.pow(2, attempt - 1) * 1000;
|
|
347
|
+
if (this.config.verbose) {
|
|
348
|
+
console.warn(`[ReplicatorPlugin] Waiting ${delay}ms before retry...`);
|
|
349
|
+
}
|
|
319
350
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
320
351
|
}
|
|
321
352
|
}
|
|
@@ -323,7 +354,7 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
323
354
|
}
|
|
324
355
|
|
|
325
356
|
async logError(replicator, resourceName, operation, recordId, data, error) {
|
|
326
|
-
|
|
357
|
+
const [ok, logError] = await tryFn(async () => {
|
|
327
358
|
const logResourceName = this.config.replicatorLogResource;
|
|
328
359
|
if (this.database && this.database.resources && this.database.resources[logResourceName]) {
|
|
329
360
|
const logResource = this.database.resources[logResourceName];
|
|
@@ -338,8 +369,20 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
338
369
|
status: 'error'
|
|
339
370
|
});
|
|
340
371
|
}
|
|
341
|
-
}
|
|
342
|
-
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
if (!ok) {
|
|
375
|
+
if (this.config.verbose) {
|
|
376
|
+
console.warn(`[ReplicatorPlugin] Failed to log error for ${resourceName}: ${logError.message}`);
|
|
377
|
+
}
|
|
378
|
+
this.emit('replicator_log_error', {
|
|
379
|
+
replicator: replicator.name || replicator.id,
|
|
380
|
+
resourceName,
|
|
381
|
+
operation,
|
|
382
|
+
recordId,
|
|
383
|
+
originalError: error.message,
|
|
384
|
+
logError: logError.message
|
|
385
|
+
});
|
|
343
386
|
}
|
|
344
387
|
}
|
|
345
388
|
|
|
@@ -356,7 +399,7 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
356
399
|
}
|
|
357
400
|
|
|
358
401
|
const promises = applicableReplicators.map(async (replicator) => {
|
|
359
|
-
|
|
402
|
+
const [ok, error, result] = await tryFn(async () => {
|
|
360
403
|
const result = await this.retryWithBackoff(
|
|
361
404
|
() => replicator.replicate(resourceName, operation, data, recordId, beforeData),
|
|
362
405
|
this.config.maxRetries
|
|
@@ -372,7 +415,15 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
372
415
|
});
|
|
373
416
|
|
|
374
417
|
return result;
|
|
375
|
-
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
if (ok) {
|
|
421
|
+
return result;
|
|
422
|
+
} else {
|
|
423
|
+
if (this.config.verbose) {
|
|
424
|
+
console.warn(`[ReplicatorPlugin] Replication failed for ${replicator.name || replicator.id} on ${resourceName}: ${error.message}`);
|
|
425
|
+
}
|
|
426
|
+
|
|
376
427
|
this.emit('replicator_error', {
|
|
377
428
|
replicator: replicator.name || replicator.id,
|
|
378
429
|
resourceName,
|
|
@@ -403,12 +454,16 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
403
454
|
}
|
|
404
455
|
|
|
405
456
|
const promises = applicableReplicators.map(async (replicator) => {
|
|
406
|
-
|
|
457
|
+
const [wrapperOk, wrapperError] = await tryFn(async () => {
|
|
407
458
|
const [ok, err, result] = await tryFn(() =>
|
|
408
459
|
replicator.replicate(item.resourceName, item.operation, item.data, item.recordId, item.beforeData)
|
|
409
460
|
);
|
|
410
461
|
|
|
411
462
|
if (!ok) {
|
|
463
|
+
if (this.config.verbose) {
|
|
464
|
+
console.warn(`[ReplicatorPlugin] Replicator item processing failed for ${replicator.name || replicator.id} on ${item.resourceName}: ${err.message}`);
|
|
465
|
+
}
|
|
466
|
+
|
|
412
467
|
this.emit('replicator_error', {
|
|
413
468
|
replicator: replicator.name || replicator.id,
|
|
414
469
|
resourceName: item.resourceName,
|
|
@@ -434,20 +489,28 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
434
489
|
});
|
|
435
490
|
|
|
436
491
|
return { success: true, result };
|
|
437
|
-
}
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
if (wrapperOk) {
|
|
495
|
+
return wrapperOk;
|
|
496
|
+
} else {
|
|
497
|
+
if (this.config.verbose) {
|
|
498
|
+
console.warn(`[ReplicatorPlugin] Wrapper processing failed for ${replicator.name || replicator.id} on ${item.resourceName}: ${wrapperError.message}`);
|
|
499
|
+
}
|
|
500
|
+
|
|
438
501
|
this.emit('replicator_error', {
|
|
439
502
|
replicator: replicator.name || replicator.id,
|
|
440
503
|
resourceName: item.resourceName,
|
|
441
504
|
operation: item.operation,
|
|
442
505
|
recordId: item.recordId,
|
|
443
|
-
error:
|
|
506
|
+
error: wrapperError.message
|
|
444
507
|
});
|
|
445
508
|
|
|
446
509
|
if (this.config.logErrors && this.database) {
|
|
447
|
-
await this.logError(replicator, item.resourceName, item.operation, item.recordId, item.data,
|
|
510
|
+
await this.logError(replicator, item.resourceName, item.operation, item.recordId, item.data, wrapperError);
|
|
448
511
|
}
|
|
449
512
|
|
|
450
|
-
return { success: false, error:
|
|
513
|
+
return { success: false, error: wrapperError.message };
|
|
451
514
|
}
|
|
452
515
|
});
|
|
453
516
|
|
|
@@ -474,9 +537,14 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
474
537
|
timestamp: typeof item.timestamp === 'number' ? item.timestamp : Date.now(),
|
|
475
538
|
createdAt: item.createdAt || new Date().toISOString().slice(0, 10),
|
|
476
539
|
};
|
|
477
|
-
|
|
540
|
+
const [ok, err] = await tryFn(async () => {
|
|
478
541
|
await logRes.insert(logItem);
|
|
479
|
-
}
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
if (!ok) {
|
|
545
|
+
if (this.config.verbose) {
|
|
546
|
+
console.warn(`[ReplicatorPlugin] Failed to log replicator item: ${err.message}`);
|
|
547
|
+
}
|
|
480
548
|
this.emit('replicator.log.failed', { error: err, item });
|
|
481
549
|
}
|
|
482
550
|
}
|
|
@@ -611,15 +679,24 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
611
679
|
}
|
|
612
680
|
|
|
613
681
|
async cleanup() {
|
|
614
|
-
|
|
682
|
+
const [ok, error] = await tryFn(async () => {
|
|
615
683
|
if (this.replicators && this.replicators.length > 0) {
|
|
616
684
|
const cleanupPromises = this.replicators.map(async (replicator) => {
|
|
617
|
-
|
|
685
|
+
const [replicatorOk, replicatorError] = await tryFn(async () => {
|
|
618
686
|
if (replicator && typeof replicator.cleanup === 'function') {
|
|
619
687
|
await replicator.cleanup();
|
|
620
688
|
}
|
|
621
|
-
}
|
|
622
|
-
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
if (!replicatorOk) {
|
|
692
|
+
if (this.config.verbose) {
|
|
693
|
+
console.warn(`[ReplicatorPlugin] Failed to cleanup replicator ${replicator.name || replicator.id}: ${replicatorError.message}`);
|
|
694
|
+
}
|
|
695
|
+
this.emit('replicator_cleanup_error', {
|
|
696
|
+
replicator: replicator.name || replicator.id || 'unknown',
|
|
697
|
+
driver: replicator.driver || 'unknown',
|
|
698
|
+
error: replicatorError.message
|
|
699
|
+
});
|
|
623
700
|
}
|
|
624
701
|
});
|
|
625
702
|
|
|
@@ -631,8 +708,15 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
631
708
|
this.eventListenersInstalled.clear();
|
|
632
709
|
|
|
633
710
|
this.removeAllListeners();
|
|
634
|
-
}
|
|
635
|
-
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
if (!ok) {
|
|
714
|
+
if (this.config.verbose) {
|
|
715
|
+
console.warn(`[ReplicatorPlugin] Failed to cleanup plugin: ${error.message}`);
|
|
716
|
+
}
|
|
717
|
+
this.emit('replicator_plugin_cleanup_error', {
|
|
718
|
+
error: error.message
|
|
719
|
+
});
|
|
636
720
|
}
|
|
637
721
|
}
|
|
638
722
|
}
|
|
@@ -115,6 +115,9 @@ class BigqueryReplicator extends BaseReplicator {
|
|
|
115
115
|
await super.initialize(database);
|
|
116
116
|
const [ok, err, sdk] = await tryFn(() => import('@google-cloud/bigquery'));
|
|
117
117
|
if (!ok) {
|
|
118
|
+
if (this.config.verbose) {
|
|
119
|
+
console.warn(`[BigqueryReplicator] Failed to import BigQuery SDK: ${err.message}`);
|
|
120
|
+
}
|
|
118
121
|
this.emit('initialization_error', { replicator: this.name, error: err.message });
|
|
119
122
|
throw err;
|
|
120
123
|
}
|
|
@@ -206,21 +209,32 @@ class BigqueryReplicator extends BaseReplicator {
|
|
|
206
209
|
let lastError = null;
|
|
207
210
|
|
|
208
211
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
209
|
-
|
|
212
|
+
const [ok, error] = await tryFn(async () => {
|
|
210
213
|
const [updateJob] = await this.bigqueryClient.createQueryJob({
|
|
211
214
|
query,
|
|
212
215
|
params,
|
|
213
216
|
location: this.location
|
|
214
217
|
});
|
|
215
218
|
await updateJob.getQueryResults();
|
|
216
|
-
|
|
219
|
+
return [updateJob];
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
if (ok) {
|
|
223
|
+
job = ok;
|
|
217
224
|
break;
|
|
218
|
-
}
|
|
225
|
+
} else {
|
|
219
226
|
lastError = error;
|
|
220
227
|
|
|
228
|
+
if (this.config.verbose) {
|
|
229
|
+
console.warn(`[BigqueryReplicator] Update attempt ${attempt} failed: ${error.message}`);
|
|
230
|
+
}
|
|
231
|
+
|
|
221
232
|
// If it's streaming buffer error and not the last attempt
|
|
222
233
|
if (error?.message?.includes('streaming buffer') && attempt < maxRetries) {
|
|
223
234
|
const delaySeconds = 30;
|
|
235
|
+
if (this.config.verbose) {
|
|
236
|
+
console.warn(`[BigqueryReplicator] Retrying in ${delaySeconds} seconds due to streaming buffer issue`);
|
|
237
|
+
}
|
|
224
238
|
await new Promise(resolve => setTimeout(resolve, delaySeconds * 1000));
|
|
225
239
|
continue;
|
|
226
240
|
}
|
|
@@ -298,6 +312,9 @@ class BigqueryReplicator extends BaseReplicator {
|
|
|
298
312
|
|
|
299
313
|
if (ok) return result;
|
|
300
314
|
|
|
315
|
+
if (this.config.verbose) {
|
|
316
|
+
console.warn(`[BigqueryReplicator] Replication failed for ${resourceName}: ${err.message}`);
|
|
317
|
+
}
|
|
301
318
|
this.emit('replicator_error', {
|
|
302
319
|
replicator: this.name,
|
|
303
320
|
resourceName,
|
|
@@ -321,8 +338,14 @@ class BigqueryReplicator extends BaseReplicator {
|
|
|
321
338
|
record.id,
|
|
322
339
|
record.beforeData
|
|
323
340
|
));
|
|
324
|
-
if (ok)
|
|
325
|
-
|
|
341
|
+
if (ok) {
|
|
342
|
+
results.push(res);
|
|
343
|
+
} else {
|
|
344
|
+
if (this.config.verbose) {
|
|
345
|
+
console.warn(`[BigqueryReplicator] Batch replication failed for record ${record.id}: ${err.message}`);
|
|
346
|
+
}
|
|
347
|
+
errors.push({ id: record.id, error: err.message });
|
|
348
|
+
}
|
|
326
349
|
}
|
|
327
350
|
|
|
328
351
|
return {
|
|
@@ -340,6 +363,9 @@ class BigqueryReplicator extends BaseReplicator {
|
|
|
340
363
|
return true;
|
|
341
364
|
});
|
|
342
365
|
if (ok) return true;
|
|
366
|
+
if (this.config.verbose) {
|
|
367
|
+
console.warn(`[BigqueryReplicator] Connection test failed: ${err.message}`);
|
|
368
|
+
}
|
|
343
369
|
this.emit('connection_error', { replicator: this.name, error: err.message });
|
|
344
370
|
return false;
|
|
345
371
|
}
|
|
@@ -113,6 +113,9 @@ class PostgresReplicator extends BaseReplicator {
|
|
|
113
113
|
await super.initialize(database);
|
|
114
114
|
const [ok, err, sdk] = await tryFn(() => import('pg'));
|
|
115
115
|
if (!ok) {
|
|
116
|
+
if (this.config.verbose) {
|
|
117
|
+
console.warn(`[PostgresReplicator] Failed to import pg SDK: ${err.message}`);
|
|
118
|
+
}
|
|
116
119
|
this.emit('initialization_error', {
|
|
117
120
|
replicator: this.name,
|
|
118
121
|
error: err.message
|
|
@@ -276,6 +279,9 @@ class PostgresReplicator extends BaseReplicator {
|
|
|
276
279
|
};
|
|
277
280
|
});
|
|
278
281
|
if (ok) return result;
|
|
282
|
+
if (this.config.verbose) {
|
|
283
|
+
console.warn(`[PostgresReplicator] Replication failed for ${resourceName}: ${err.message}`);
|
|
284
|
+
}
|
|
279
285
|
this.emit('replicator_error', {
|
|
280
286
|
replicator: this.name,
|
|
281
287
|
resourceName,
|
|
@@ -298,8 +304,14 @@ class PostgresReplicator extends BaseReplicator {
|
|
|
298
304
|
record.id,
|
|
299
305
|
record.beforeData
|
|
300
306
|
));
|
|
301
|
-
if (ok)
|
|
302
|
-
|
|
307
|
+
if (ok) {
|
|
308
|
+
results.push(res);
|
|
309
|
+
} else {
|
|
310
|
+
if (this.config.verbose) {
|
|
311
|
+
console.warn(`[PostgresReplicator] Batch replication failed for record ${record.id}: ${err.message}`);
|
|
312
|
+
}
|
|
313
|
+
errors.push({ id: record.id, error: err.message });
|
|
314
|
+
}
|
|
303
315
|
}
|
|
304
316
|
|
|
305
317
|
return {
|
|
@@ -316,6 +328,9 @@ class PostgresReplicator extends BaseReplicator {
|
|
|
316
328
|
return true;
|
|
317
329
|
});
|
|
318
330
|
if (ok) return true;
|
|
331
|
+
if (this.config.verbose) {
|
|
332
|
+
console.warn(`[PostgresReplicator] Connection test failed: ${err.message}`);
|
|
333
|
+
}
|
|
319
334
|
this.emit('connection_error', { replicator: this.name, error: err.message });
|
|
320
335
|
return false;
|
|
321
336
|
}
|