s3db.js 10.0.0 → 10.0.3

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.
@@ -18,7 +18,7 @@ export class FullTextPlugin extends Plugin {
18
18
 
19
19
  // Create index resource if it doesn't exist
20
20
  const [ok, err, indexResource] = await tryFn(() => database.createResource({
21
- name: 'fulltext_indexes',
21
+ name: 'plg_fulltext_indexes',
22
22
  attributes: {
23
23
  id: 'string|required',
24
24
  resourceName: 'string|required',
@@ -96,7 +96,7 @@ export class FullTextPlugin extends Plugin {
96
96
  installDatabaseHooks() {
97
97
  // Use the new database hooks system for automatic resource discovery
98
98
  this.database.addHook('afterCreateResource', (resource) => {
99
- if (resource.name !== 'fulltext_indexes') {
99
+ if (resource.name !== 'plg_fulltext_indexes') {
100
100
  this.installResourceHooks(resource);
101
101
  }
102
102
  });
@@ -115,7 +115,7 @@ export class FullTextPlugin extends Plugin {
115
115
  this.database.plugins.fulltext = this;
116
116
 
117
117
  for (const resource of Object.values(this.database.resources)) {
118
- if (resource.name === 'fulltext_indexes') continue;
118
+ if (resource.name === 'plg_fulltext_indexes') continue;
119
119
 
120
120
  this.installResourceHooks(resource);
121
121
  }
@@ -126,7 +126,7 @@ export class FullTextPlugin extends Plugin {
126
126
  this.database._previousCreateResourceForFullText = this.database.createResource;
127
127
  this.database.createResource = async function (...args) {
128
128
  const resource = await this._previousCreateResourceForFullText(...args);
129
- if (this.plugins?.fulltext && resource.name !== 'fulltext_indexes') {
129
+ if (this.plugins?.fulltext && resource.name !== 'plg_fulltext_indexes') {
130
130
  this.plugins.fulltext.installResourceHooks(resource);
131
131
  }
132
132
  return resource;
@@ -136,7 +136,7 @@ export class FullTextPlugin extends Plugin {
136
136
 
137
137
  // Ensure all existing resources have hooks (even if created before plugin setup)
138
138
  for (const resource of Object.values(this.database.resources)) {
139
- if (resource.name !== 'fulltext_indexes') {
139
+ if (resource.name !== 'plg_fulltext_indexes') {
140
140
  this.installResourceHooks(resource);
141
141
  }
142
142
  }
@@ -460,7 +460,7 @@ export class FullTextPlugin extends Plugin {
460
460
  }
461
461
 
462
462
  async _rebuildAllIndexesInternal() {
463
- const resourceNames = Object.keys(this.database.resources).filter(name => name !== 'fulltext_indexes');
463
+ const resourceNames = Object.keys(this.database.resources).filter(name => name !== 'plg_fulltext_indexes');
464
464
 
465
465
  // Process resources sequentially to avoid overwhelming the system
466
466
  for (const resourceName of resourceNames) {
@@ -12,5 +12,6 @@ export * from './fulltext.plugin.js'
12
12
  export * from './metrics.plugin.js'
13
13
  export * from './queue-consumer.plugin.js'
14
14
  export * from './replicator.plugin.js'
15
+ export * from './s3-queue.plugin.js'
15
16
  export * from './scheduler.plugin.js'
16
17
  export * from './state-machine.plugin.js'
@@ -37,7 +37,7 @@ export class MetricsPlugin extends Plugin {
37
37
 
38
38
  const [ok, err] = await tryFn(async () => {
39
39
  const [ok1, err1, metricsResource] = await tryFn(() => database.createResource({
40
- name: 'metrics',
40
+ name: 'plg_metrics',
41
41
  attributes: {
42
42
  id: 'string|required',
43
43
  type: 'string|required', // 'operation', 'error', 'performance'
@@ -51,10 +51,10 @@ export class MetricsPlugin extends Plugin {
51
51
  metadata: 'json'
52
52
  }
53
53
  }));
54
- this.metricsResource = ok1 ? metricsResource : database.resources.metrics;
54
+ this.metricsResource = ok1 ? metricsResource : database.resources.plg_metrics;
55
55
 
56
56
  const [ok2, err2, errorsResource] = await tryFn(() => database.createResource({
57
- name: 'error_logs',
57
+ name: 'plg_error_logs',
58
58
  attributes: {
59
59
  id: 'string|required',
60
60
  resourceName: 'string|required',
@@ -64,10 +64,10 @@ export class MetricsPlugin extends Plugin {
64
64
  metadata: 'json'
65
65
  }
66
66
  }));
67
- this.errorsResource = ok2 ? errorsResource : database.resources.error_logs;
67
+ this.errorsResource = ok2 ? errorsResource : database.resources.plg_error_logs;
68
68
 
69
69
  const [ok3, err3, performanceResource] = await tryFn(() => database.createResource({
70
- name: 'performance_logs',
70
+ name: 'plg_performance_logs',
71
71
  attributes: {
72
72
  id: 'string|required',
73
73
  resourceName: 'string|required',
@@ -77,13 +77,13 @@ export class MetricsPlugin extends Plugin {
77
77
  metadata: 'json'
78
78
  }
79
79
  }));
80
- this.performanceResource = ok3 ? performanceResource : database.resources.performance_logs;
80
+ this.performanceResource = ok3 ? performanceResource : database.resources.plg_performance_logs;
81
81
  });
82
82
  if (!ok) {
83
83
  // Resources might already exist
84
- this.metricsResource = database.resources.metrics;
85
- this.errorsResource = database.resources.error_logs;
86
- this.performanceResource = database.resources.performance_logs;
84
+ this.metricsResource = database.resources.plg_metrics;
85
+ this.errorsResource = database.resources.plg_error_logs;
86
+ this.performanceResource = database.resources.plg_performance_logs;
87
87
  }
88
88
 
89
89
  // Use database hooks for automatic resource discovery
@@ -116,7 +116,7 @@ export class MetricsPlugin extends Plugin {
116
116
  installDatabaseHooks() {
117
117
  // Use the new database hooks system for automatic resource discovery
118
118
  this.database.addHook('afterCreateResource', (resource) => {
119
- if (resource.name !== 'metrics' && resource.name !== 'error_logs' && resource.name !== 'performance_logs') {
119
+ if (resource.name !== 'plg_metrics' && resource.name !== 'plg_error_logs' && resource.name !== 'plg_performance_logs') {
120
120
  this.installResourceHooks(resource);
121
121
  }
122
122
  });
@@ -130,10 +130,10 @@ export class MetricsPlugin extends Plugin {
130
130
  installMetricsHooks() {
131
131
  // Only hook into non-metrics resources
132
132
  for (const resource of Object.values(this.database.resources)) {
133
- if (['metrics', 'error_logs', 'performance_logs'].includes(resource.name)) {
133
+ if (['plg_metrics', 'plg_error_logs', 'plg_performance_logs'].includes(resource.name)) {
134
134
  continue; // Skip metrics resources to avoid recursion
135
135
  }
136
-
136
+
137
137
  this.installResourceHooks(resource);
138
138
  }
139
139
 
@@ -141,7 +141,7 @@ export class MetricsPlugin extends Plugin {
141
141
  this.database._createResource = this.database.createResource;
142
142
  this.database.createResource = async function (...args) {
143
143
  const resource = await this._createResource(...args);
144
- if (this.plugins?.metrics && !['metrics', 'error_logs', 'performance_logs'].includes(resource.name)) {
144
+ if (this.plugins?.metrics && !['plg_metrics', 'plg_error_logs', 'plg_performance_logs'].includes(resource.name)) {
145
145
  this.plugins.metrics.installResourceHooks(resource);
146
146
  }
147
147
  return resource;
@@ -20,7 +20,7 @@ import tryFn from "../concerns/try-fn.js";
20
20
  // reconnectInterval: 2000,
21
21
  // });
22
22
 
23
- export default class QueueConsumerPlugin {
23
+ export class QueueConsumerPlugin {
24
24
  constructor(options = {}) {
25
25
  this.options = options;
26
26
  // New pattern: consumers = [{ driver, config, consumers: [{ queueUrl, resources, ... }] }]
@@ -131,4 +131,6 @@ export default class QueueConsumerPlugin {
131
131
 
132
132
  _handleError(err, raw, resourceName) {
133
133
  }
134
- }
134
+ }
135
+
136
+ export default QueueConsumerPlugin;
@@ -17,7 +17,7 @@ function normalizeResourceName(name) {
17
17
  * If true, the plugin will persist all replicator events to a log resource.
18
18
  * If false, no replicator log resource is created or used.
19
19
  *
20
- * - replicatorLogResource (string, default: 'replicator_logs')
20
+ * - replicatorLogResource (string, default: 'plg_replicator_logs')
21
21
  * The name of the resource used to store replicator logs.
22
22
  *
23
23
  * === replicator Log Resource Structure ===
@@ -132,24 +132,24 @@ export class ReplicatorPlugin extends Plugin {
132
132
  replicators: options.replicators || [],
133
133
  logErrors: options.logErrors !== false,
134
134
  replicatorLogResource: options.replicatorLogResource || 'replicator_log',
135
+ persistReplicatorLog: options.persistReplicatorLog || false,
135
136
  enabled: options.enabled !== false,
136
137
  batchSize: options.batchSize || 100,
137
138
  maxRetries: options.maxRetries || 3,
138
139
  timeout: options.timeout || 30000,
139
- verbose: options.verbose || false,
140
- ...options
140
+ verbose: options.verbose || false
141
141
  };
142
-
142
+
143
143
  this.replicators = [];
144
144
  this.database = null;
145
145
  this.eventListenersInstalled = new Set();
146
- }
147
-
148
- /**
149
- * Decompress data if it was compressed
150
- */
151
- async decompressData(data) {
152
- return data;
146
+ this.eventHandlers = new Map(); // Map<resourceName, {insert, update, delete}>
147
+ this.stats = {
148
+ totalReplications: 0,
149
+ totalErrors: 0,
150
+ lastSync: null
151
+ };
152
+ this._afterCreateResourceHook = null;
153
153
  }
154
154
 
155
155
  // Helper to filter out internal S3DB fields
@@ -172,54 +172,67 @@ export class ReplicatorPlugin extends Plugin {
172
172
  }
173
173
 
174
174
  installEventListeners(resource, database, plugin) {
175
- if (!resource || this.eventListenersInstalled.has(resource.name) ||
175
+ if (!resource || this.eventListenersInstalled.has(resource.name) ||
176
176
  resource.name === this.config.replicatorLogResource) {
177
177
  return;
178
178
  }
179
179
 
180
- resource.on('insert', async (data) => {
180
+ // Create handler functions and save references for later removal
181
+ const insertHandler = async (data) => {
181
182
  const [ok, error] = await tryFn(async () => {
182
183
  const completeData = { ...data, createdAt: new Date().toISOString() };
183
184
  await plugin.processReplicatorEvent('insert', resource.name, completeData.id, completeData);
184
185
  });
185
-
186
+
186
187
  if (!ok) {
187
188
  if (this.config.verbose) {
188
189
  console.warn(`[ReplicatorPlugin] Insert event failed for resource ${resource.name}: ${error.message}`);
189
190
  }
190
191
  this.emit('error', { operation: 'insert', error: error.message, resource: resource.name });
191
192
  }
192
- });
193
+ };
193
194
 
194
- resource.on('update', async (data, beforeData) => {
195
+ const updateHandler = async (data, beforeData) => {
195
196
  const [ok, error] = await tryFn(async () => {
196
197
  // For updates, we need to get the complete updated record, not just the changed fields
197
198
  const completeData = await plugin.getCompleteData(resource, data);
198
199
  const dataWithTimestamp = { ...completeData, updatedAt: new Date().toISOString() };
199
200
  await plugin.processReplicatorEvent('update', resource.name, completeData.id, dataWithTimestamp, beforeData);
200
201
  });
201
-
202
+
202
203
  if (!ok) {
203
204
  if (this.config.verbose) {
204
205
  console.warn(`[ReplicatorPlugin] Update event failed for resource ${resource.name}: ${error.message}`);
205
206
  }
206
207
  this.emit('error', { operation: 'update', error: error.message, resource: resource.name });
207
208
  }
208
- });
209
+ };
209
210
 
210
- resource.on('delete', async (data) => {
211
+ const deleteHandler = async (data) => {
211
212
  const [ok, error] = await tryFn(async () => {
212
213
  await plugin.processReplicatorEvent('delete', resource.name, data.id, data);
213
214
  });
214
-
215
+
215
216
  if (!ok) {
216
217
  if (this.config.verbose) {
217
218
  console.warn(`[ReplicatorPlugin] Delete event failed for resource ${resource.name}: ${error.message}`);
218
219
  }
219
220
  this.emit('error', { operation: 'delete', error: error.message, resource: resource.name });
220
221
  }
222
+ };
223
+
224
+ // Save handler references
225
+ this.eventHandlers.set(resource.name, {
226
+ insert: insertHandler,
227
+ update: updateHandler,
228
+ delete: deleteHandler
221
229
  });
222
230
 
231
+ // Attach listeners
232
+ resource.on('insert', insertHandler);
233
+ resource.on('update', updateHandler);
234
+ resource.on('delete', deleteHandler);
235
+
223
236
  this.eventListenersInstalled.add(resource.name);
224
237
  }
225
238
 
@@ -229,7 +242,7 @@ export class ReplicatorPlugin extends Plugin {
229
242
  // Create replicator log resource if enabled
230
243
  if (this.config.persistReplicatorLog) {
231
244
  const [ok, err, logResource] = await tryFn(() => database.createResource({
232
- name: this.config.replicatorLogResource || 'replicator_logs',
245
+ name: this.config.replicatorLogResource || 'plg_replicator_logs',
233
246
  attributes: {
234
247
  id: 'string|required',
235
248
  resource: 'string|required',
@@ -244,7 +257,7 @@ export class ReplicatorPlugin extends Plugin {
244
257
  if (ok) {
245
258
  this.replicatorLogResource = logResource;
246
259
  } else {
247
- this.replicatorLogResource = database.resources[this.config.replicatorLogResource || 'replicator_logs'];
260
+ this.replicatorLogResource = database.resources[this.config.replicatorLogResource || 'plg_replicator_logs'];
248
261
  }
249
262
  }
250
263
 
@@ -256,7 +269,7 @@ export class ReplicatorPlugin extends Plugin {
256
269
 
257
270
  // Install event listeners for existing resources
258
271
  for (const resource of Object.values(database.resources)) {
259
- if (resource.name !== (this.config.replicatorLogResource || 'replicator_logs')) {
272
+ if (resource.name !== (this.config.replicatorLogResource || 'plg_replicator_logs')) {
260
273
  this.installEventListeners(resource, database, this);
261
274
  }
262
275
  }
@@ -279,17 +292,22 @@ export class ReplicatorPlugin extends Plugin {
279
292
  }
280
293
 
281
294
  installDatabaseHooks() {
282
- // Use the new database hooks system for automatic resource discovery
283
- this.database.addHook('afterCreateResource', (resource) => {
284
- if (resource.name !== (this.config.replicatorLogResource || 'replicator_logs')) {
295
+ // Store hook reference for later removal
296
+ this._afterCreateResourceHook = (resource) => {
297
+ if (resource.name !== (this.config.replicatorLogResource || 'plg_replicator_logs')) {
285
298
  this.installEventListeners(resource, this.database, this);
286
299
  }
287
- });
300
+ };
301
+
302
+ this.database.addHook('afterCreateResource', this._afterCreateResourceHook);
288
303
  }
289
304
 
290
305
  removeDatabaseHooks() {
291
- // Remove the hook we added
292
- this.database.removeHook('afterCreateResource', this.installEventListeners.bind(this));
306
+ // Remove the hook we added using stored reference
307
+ if (this._afterCreateResourceHook) {
308
+ this.database.removeHook('afterCreateResource', this._afterCreateResourceHook);
309
+ this._afterCreateResourceHook = null;
310
+ }
293
311
  }
294
312
 
295
313
  createReplicator(driver, config, resources, client) {
@@ -324,16 +342,16 @@ export class ReplicatorPlugin extends Plugin {
324
342
  async retryWithBackoff(operation, maxRetries = 3) {
325
343
  let lastError;
326
344
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
327
- const [ok, error] = await tryFn(operation);
328
-
345
+ const [ok, error, result] = await tryFn(operation);
346
+
329
347
  if (ok) {
330
- return ok;
348
+ return result;
331
349
  } else {
332
350
  lastError = error;
333
351
  if (this.config.verbose) {
334
352
  console.warn(`[ReplicatorPlugin] Retry attempt ${attempt}/${maxRetries} failed: ${error.message}`);
335
353
  }
336
-
354
+
337
355
  if (attempt === maxRetries) {
338
356
  throw error;
339
357
  }
@@ -438,7 +456,7 @@ export class ReplicatorPlugin extends Plugin {
438
456
  return Promise.allSettled(promises);
439
457
  }
440
458
 
441
- async processreplicatorItem(item) {
459
+ async processReplicatorItem(item) {
442
460
  const applicableReplicators = this.replicators.filter(replicator => {
443
461
  const should = replicator.shouldReplicateResource && replicator.shouldReplicateResource(item.resourceName, item.operation);
444
462
  return should;
@@ -512,14 +530,10 @@ export class ReplicatorPlugin extends Plugin {
512
530
  return Promise.allSettled(promises);
513
531
  }
514
532
 
515
- async logreplicator(item) {
516
- // Always use the saved reference
533
+ async logReplicator(item) {
534
+ // Always use the saved reference
517
535
  const logRes = this.replicatorLog || this.database.resources[normalizeResourceName(this.config.replicatorLogResource)];
518
536
  if (!logRes) {
519
- if (this.database) {
520
- if (this.database.options && this.database.options.connectionString) {
521
- }
522
- }
523
537
  this.emit('replicator.log.failed', { error: 'replicator log resource not found', item });
524
538
  return;
525
539
  }
@@ -544,7 +558,7 @@ export class ReplicatorPlugin extends Plugin {
544
558
  }
545
559
  }
546
560
 
547
- async updatereplicatorLog(logId, updates) {
561
+ async updateReplicatorLog(logId, updates) {
548
562
  if (!this.replicatorLog) return;
549
563
 
550
564
  const [ok, err] = await tryFn(async () => {
@@ -559,7 +573,7 @@ export class ReplicatorPlugin extends Plugin {
559
573
  }
560
574
 
561
575
  // Utility methods
562
- async getreplicatorStats() {
576
+ async getReplicatorStats() {
563
577
  const replicatorStats = await Promise.all(
564
578
  this.replicators.map(async (replicator) => {
565
579
  const status = await replicator.getStatus();
@@ -574,16 +588,12 @@ export class ReplicatorPlugin extends Plugin {
574
588
 
575
589
  return {
576
590
  replicators: replicatorStats,
577
- queue: {
578
- length: this.queue.length,
579
- isProcessing: this.isProcessing
580
- },
581
591
  stats: this.stats,
582
592
  lastSync: this.stats.lastSync
583
593
  };
584
594
  }
585
595
 
586
- async getreplicatorLogs(options = {}) {
596
+ async getReplicatorLogs(options = {}) {
587
597
  if (!this.replicatorLog) {
588
598
  return [];
589
599
  }
@@ -596,43 +606,42 @@ export class ReplicatorPlugin extends Plugin {
596
606
  offset = 0
597
607
  } = options;
598
608
 
599
- let query = {};
600
-
609
+ const filter = {};
610
+
601
611
  if (resourceName) {
602
- query.resourceName = resourceName;
612
+ filter.resourceName = resourceName;
603
613
  }
604
-
614
+
605
615
  if (operation) {
606
- query.operation = operation;
616
+ filter.operation = operation;
607
617
  }
608
-
618
+
609
619
  if (status) {
610
- query.status = status;
620
+ filter.status = status;
611
621
  }
612
622
 
613
- const logs = await this.replicatorLog.list(query);
614
-
615
- // Apply pagination
616
- return logs.slice(offset, offset + limit);
623
+ const logs = await this.replicatorLog.query(filter, { limit, offset });
624
+
625
+ return logs || [];
617
626
  }
618
627
 
619
- async retryFailedreplicators() {
628
+ async retryFailedReplicators() {
620
629
  if (!this.replicatorLog) {
621
630
  return { retried: 0 };
622
631
  }
623
632
 
624
- const failedLogs = await this.replicatorLog.list({
633
+ const failedLogs = await this.replicatorLog.query({
625
634
  status: 'failed'
626
635
  });
627
636
 
628
637
  let retried = 0;
629
-
630
- for (const log of failedLogs) {
638
+
639
+ for (const log of failedLogs || []) {
631
640
  const [ok, err] = await tryFn(async () => {
632
641
  // Re-queue the replicator
633
642
  await this.processReplicatorEvent(
634
- log.resourceName,
635
643
  log.operation,
644
+ log.resourceName,
636
645
  log.recordId,
637
646
  log.data
638
647
  );
@@ -656,16 +665,30 @@ export class ReplicatorPlugin extends Plugin {
656
665
  this.stats.lastSync = new Date().toISOString();
657
666
 
658
667
  for (const resourceName in this.database.resources) {
659
- if (normalizeResourceName(resourceName) === normalizeResourceName('replicator_logs')) continue;
668
+ if (normalizeResourceName(resourceName) === normalizeResourceName('plg_replicator_logs')) continue;
660
669
 
661
670
  if (replicator.shouldReplicateResource(resourceName)) {
662
671
  this.emit('replicator.sync.resource', { resourceName, replicatorId });
663
-
672
+
664
673
  const resource = this.database.resources[resourceName];
665
- const allRecords = await resource.getAll();
666
-
667
- for (const record of allRecords) {
668
- await replicator.replicate(resourceName, 'insert', record, record.id);
674
+
675
+ // Use pagination to avoid memory issues
676
+ let offset = 0;
677
+ const pageSize = this.config.batchSize || 100;
678
+
679
+ while (true) {
680
+ const [ok, err, page] = await tryFn(() => resource.page({ offset, size: pageSize }));
681
+
682
+ if (!ok || !page) break;
683
+
684
+ const records = Array.isArray(page) ? page : (page.items || []);
685
+ if (records.length === 0) break;
686
+
687
+ for (const record of records) {
688
+ await replicator.replicate(resourceName, 'insert', record, record.id);
689
+ }
690
+
691
+ offset += pageSize;
669
692
  }
670
693
  }
671
694
  }
@@ -698,10 +721,25 @@ export class ReplicatorPlugin extends Plugin {
698
721
  await Promise.allSettled(cleanupPromises);
699
722
  }
700
723
 
724
+ // Remove event listeners from resources to prevent memory leaks
725
+ if (this.database && this.database.resources) {
726
+ for (const resourceName of this.eventListenersInstalled) {
727
+ const resource = this.database.resources[resourceName];
728
+ const handlers = this.eventHandlers.get(resourceName);
729
+
730
+ if (resource && handlers) {
731
+ resource.off('insert', handlers.insert);
732
+ resource.off('update', handlers.update);
733
+ resource.off('delete', handlers.delete);
734
+ }
735
+ }
736
+ }
737
+
701
738
  this.replicators = [];
702
739
  this.database = null;
703
740
  this.eventListenersInstalled.clear();
704
-
741
+ this.eventHandlers.clear();
742
+
705
743
  this.removeAllListeners();
706
744
  });
707
745
 
@@ -328,17 +328,21 @@ class S3dbReplicator extends BaseReplicator {
328
328
  }
329
329
 
330
330
  _getDestResourceObj(resource) {
331
- const available = Object.keys(this.client.resources || {});
331
+ const db = this.targetDatabase || this.client;
332
+ const available = Object.keys(db.resources || {});
332
333
  const norm = normalizeResourceName(resource);
333
334
  const found = available.find(r => normalizeResourceName(r) === norm);
334
335
  if (!found) {
335
336
  throw new Error(`[S3dbReplicator] Destination resource not found: ${resource}. Available: ${available.join(', ')}`);
336
337
  }
337
- return this.client.resources[found];
338
+ return db.resources[found];
338
339
  }
339
340
 
340
341
  async replicateBatch(resourceName, records) {
341
- if (!this.enabled || !this.shouldReplicateResource(resourceName)) {
342
+ if (this.enabled === false) {
343
+ return { skipped: true, reason: 'replicator_disabled' };
344
+ }
345
+ if (!this.shouldReplicateResource(resourceName)) {
342
346
  return { skipped: true, reason: 'resource_not_included' };
343
347
  }
344
348
 
@@ -32,11 +32,13 @@ class SqsReplicator extends BaseReplicator {
32
32
  this.client = client;
33
33
  this.queueUrl = config.queueUrl;
34
34
  this.queues = config.queues || {};
35
- this.defaultQueue = config.defaultQueue || config.defaultQueueUrl || config.queueUrlDefault;
35
+ // Support legacy names but prefer defaultQueue
36
+ this.defaultQueue = config.defaultQueue || config.defaultQueueUrl || config.queueUrlDefault || null;
36
37
  this.region = config.region || 'us-east-1';
37
38
  this.sqsClient = client || null;
38
39
  this.messageGroupId = config.messageGroupId;
39
40
  this.deduplicationId = config.deduplicationId;
41
+ this.resourceQueueMap = config.resourceQueueMap || null;
40
42
 
41
43
  // Normalize resources to object format
42
44
  if (Array.isArray(resources)) {
@@ -188,7 +190,10 @@ class SqsReplicator extends BaseReplicator {
188
190
  }
189
191
 
190
192
  async replicate(resource, operation, data, id, beforeData = null) {
191
- if (!this.enabled || !this.shouldReplicateResource(resource)) {
193
+ if (this.enabled === false) {
194
+ return { skipped: true, reason: 'replicator_disabled' };
195
+ }
196
+ if (!this.shouldReplicateResource(resource)) {
192
197
  return { skipped: true, reason: 'resource_not_included' };
193
198
  }
194
199
  const [ok, err, result] = await tryFn(async () => {
@@ -234,7 +239,10 @@ class SqsReplicator extends BaseReplicator {
234
239
  }
235
240
 
236
241
  async replicateBatch(resource, records) {
237
- if (!this.enabled || !this.shouldReplicateResource(resource)) {
242
+ if (this.enabled === false) {
243
+ return { skipped: true, reason: 'replicator_disabled' };
244
+ }
245
+ if (!this.shouldReplicateResource(resource)) {
238
246
  return { skipped: true, reason: 'resource_not_included' };
239
247
  }
240
248
  const [ok, err, result] = await tryFn(async () => {