s3db.js 10.0.1 → 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.
package/dist/s3db.d.ts CHANGED
@@ -335,7 +335,14 @@ declare module 's3db.js' {
335
335
 
336
336
  /** Replicator Plugin config */
337
337
  export interface ReplicatorPluginConfig extends PluginConfig {
338
- replicators?: ReplicatorConfig[];
338
+ replicators: ReplicatorConfig[];
339
+ persistReplicatorLog?: boolean;
340
+ replicatorLogResource?: string;
341
+ logErrors?: boolean;
342
+ batchSize?: number;
343
+ maxRetries?: number;
344
+ timeout?: number;
345
+ verbose?: boolean;
339
346
  }
340
347
 
341
348
  // ============================================================================
@@ -1027,16 +1034,76 @@ declare module 's3db.js' {
1027
1034
  getConsumerLogs(filters?: any): Promise<any[]>;
1028
1035
  }
1029
1036
 
1037
+ /** Replicator stats information */
1038
+ export interface ReplicatorStats {
1039
+ replicators: Array<{
1040
+ id: string;
1041
+ driver: string;
1042
+ config: any;
1043
+ status: any;
1044
+ }>;
1045
+ stats: {
1046
+ totalReplications: number;
1047
+ totalErrors: number;
1048
+ lastSync: string | null;
1049
+ };
1050
+ lastSync: string | null;
1051
+ }
1052
+
1030
1053
  /** Replicator Plugin */
1031
1054
  export class ReplicatorPlugin extends Plugin {
1032
1055
  constructor(config?: ReplicatorPluginConfig);
1033
1056
  replicate(operation: string, resourceName: string, data: any, oldData?: any): Promise<void>;
1034
- getReplicatorStats(): any;
1057
+ getReplicatorStats(): Promise<ReplicatorStats>;
1035
1058
  getReplicatorLogs(filters?: any): Promise<any[]>;
1036
- retryFailedReplications(): Promise<void>;
1059
+ retryFailedReplicators(): Promise<{ retried: number }>;
1037
1060
  syncAllData(targetName: string): Promise<void>;
1038
1061
  }
1039
1062
 
1063
+ /** Backup Plugin */
1064
+ export class BackupPlugin extends Plugin {
1065
+ constructor(config?: any);
1066
+ backup(options?: any): Promise<any>;
1067
+ restore(options?: any): Promise<any>;
1068
+ listBackups(): Promise<any[]>;
1069
+ deleteBackup(backupId: string): Promise<void>;
1070
+ }
1071
+
1072
+ /** Eventual Consistency Plugin */
1073
+ export class EventualConsistencyPlugin extends Plugin {
1074
+ constructor(config?: any);
1075
+ setup(database: Database): Promise<void>;
1076
+ createTransaction(resourceName: string): any;
1077
+ }
1078
+
1079
+ /** Scheduler Plugin */
1080
+ export class SchedulerPlugin extends Plugin {
1081
+ constructor(config?: any);
1082
+ schedule(name: string, schedule: string, handler: Function): void;
1083
+ unschedule(name: string): void;
1084
+ listSchedules(): any[];
1085
+ getScheduleStatus(name: string): any;
1086
+ }
1087
+
1088
+ /** State Machine Plugin */
1089
+ export class StateMachinePlugin extends Plugin {
1090
+ constructor(config?: any);
1091
+ defineMachine(config: any): void;
1092
+ transition(options: { machineId: string; entityId: string; event: string; context?: any }): Promise<any>;
1093
+ getCurrentState(machineId: string, entityId: string): Promise<any>;
1094
+ getTransitionHistory(machineId: string, entityId: string, options?: any): Promise<any[]>;
1095
+ }
1096
+
1097
+ /** S3 Queue Plugin */
1098
+ export class S3QueuePlugin extends Plugin {
1099
+ constructor(config?: any);
1100
+ enqueue(queueName: string, item: any): Promise<void>;
1101
+ dequeue(queueName: string): Promise<any>;
1102
+ peek(queueName: string): Promise<any>;
1103
+ getQueueLength(queueName: string): Promise<number>;
1104
+ clearQueue(queueName: string): Promise<void>;
1105
+ }
1106
+
1040
1107
  // ============================================================================
1041
1108
  // REPLICATOR CLASSES
1042
1109
  // ============================================================================
package/dist/s3db.es.js CHANGED
@@ -5898,6 +5898,253 @@ class MetricsPlugin extends Plugin {
5898
5898
  }
5899
5899
  }
5900
5900
 
5901
+ class SqsConsumer {
5902
+ constructor({ queueUrl, onMessage, onError, poolingInterval = 5e3, maxMessages = 10, region = "us-east-1", credentials, endpoint, driver = "sqs" }) {
5903
+ this.driver = driver;
5904
+ this.queueUrl = queueUrl;
5905
+ this.onMessage = onMessage;
5906
+ this.onError = onError;
5907
+ this.poolingInterval = poolingInterval;
5908
+ this.maxMessages = maxMessages;
5909
+ this.region = region;
5910
+ this.credentials = credentials;
5911
+ this.endpoint = endpoint;
5912
+ this.sqs = null;
5913
+ this._stopped = false;
5914
+ this._timer = null;
5915
+ this._pollPromise = null;
5916
+ this._pollResolve = null;
5917
+ this._SQSClient = null;
5918
+ this._ReceiveMessageCommand = null;
5919
+ this._DeleteMessageCommand = null;
5920
+ }
5921
+ async start() {
5922
+ const [ok, err, sdk] = await tryFn(() => import('@aws-sdk/client-sqs'));
5923
+ if (!ok) throw new Error("SqsConsumer: @aws-sdk/client-sqs is not installed. Please install it to use the SQS consumer.");
5924
+ const { SQSClient, ReceiveMessageCommand, DeleteMessageCommand } = sdk;
5925
+ this._SQSClient = SQSClient;
5926
+ this._ReceiveMessageCommand = ReceiveMessageCommand;
5927
+ this._DeleteMessageCommand = DeleteMessageCommand;
5928
+ this.sqs = new SQSClient({ region: this.region, credentials: this.credentials, endpoint: this.endpoint });
5929
+ this._stopped = false;
5930
+ this._pollPromise = new Promise((resolve) => {
5931
+ this._pollResolve = resolve;
5932
+ });
5933
+ this._poll();
5934
+ }
5935
+ async stop() {
5936
+ this._stopped = true;
5937
+ if (this._timer) {
5938
+ clearTimeout(this._timer);
5939
+ this._timer = null;
5940
+ }
5941
+ if (this._pollResolve) {
5942
+ this._pollResolve();
5943
+ }
5944
+ }
5945
+ async _poll() {
5946
+ if (this._stopped) {
5947
+ if (this._pollResolve) this._pollResolve();
5948
+ return;
5949
+ }
5950
+ const [ok, err, result] = await tryFn(async () => {
5951
+ const cmd = new this._ReceiveMessageCommand({
5952
+ QueueUrl: this.queueUrl,
5953
+ MaxNumberOfMessages: this.maxMessages,
5954
+ WaitTimeSeconds: 10,
5955
+ MessageAttributeNames: ["All"]
5956
+ });
5957
+ const { Messages } = await this.sqs.send(cmd);
5958
+ if (Messages && Messages.length > 0) {
5959
+ for (const msg of Messages) {
5960
+ const [okMsg, errMsg] = await tryFn(async () => {
5961
+ const parsedMsg = this._parseMessage(msg);
5962
+ await this.onMessage(parsedMsg, msg);
5963
+ await this.sqs.send(new this._DeleteMessageCommand({
5964
+ QueueUrl: this.queueUrl,
5965
+ ReceiptHandle: msg.ReceiptHandle
5966
+ }));
5967
+ });
5968
+ if (!okMsg && this.onError) {
5969
+ this.onError(errMsg, msg);
5970
+ }
5971
+ }
5972
+ }
5973
+ });
5974
+ if (!ok && this.onError) {
5975
+ this.onError(err);
5976
+ }
5977
+ this._timer = setTimeout(() => this._poll(), this.poolingInterval);
5978
+ }
5979
+ _parseMessage(msg) {
5980
+ let body;
5981
+ const [ok, err, parsed] = tryFn(() => JSON.parse(msg.Body));
5982
+ body = ok ? parsed : msg.Body;
5983
+ const attributes = {};
5984
+ if (msg.MessageAttributes) {
5985
+ for (const [k, v] of Object.entries(msg.MessageAttributes)) {
5986
+ attributes[k] = v.StringValue;
5987
+ }
5988
+ }
5989
+ return { $body: body, $attributes: attributes, $raw: msg };
5990
+ }
5991
+ }
5992
+
5993
+ class RabbitMqConsumer {
5994
+ constructor({ amqpUrl, queue, prefetch = 10, reconnectInterval = 2e3, onMessage, onError, driver = "rabbitmq" }) {
5995
+ this.amqpUrl = amqpUrl;
5996
+ this.queue = queue;
5997
+ this.prefetch = prefetch;
5998
+ this.reconnectInterval = reconnectInterval;
5999
+ this.onMessage = onMessage;
6000
+ this.onError = onError;
6001
+ this.driver = driver;
6002
+ this.connection = null;
6003
+ this.channel = null;
6004
+ this._stopped = false;
6005
+ }
6006
+ async start() {
6007
+ this._stopped = false;
6008
+ await this._connect();
6009
+ }
6010
+ async stop() {
6011
+ this._stopped = true;
6012
+ if (this.channel) await this.channel.close();
6013
+ if (this.connection) await this.connection.close();
6014
+ }
6015
+ async _connect() {
6016
+ const [ok, err] = await tryFn(async () => {
6017
+ const amqp = (await import('amqplib')).default;
6018
+ this.connection = await amqp.connect(this.amqpUrl);
6019
+ this.channel = await this.connection.createChannel();
6020
+ await this.channel.assertQueue(this.queue, { durable: true });
6021
+ this.channel.prefetch(this.prefetch);
6022
+ this.channel.consume(this.queue, async (msg) => {
6023
+ if (msg !== null) {
6024
+ const [okMsg, errMsg] = await tryFn(async () => {
6025
+ const content = JSON.parse(msg.content.toString());
6026
+ await this.onMessage({ $body: content, $raw: msg });
6027
+ this.channel.ack(msg);
6028
+ });
6029
+ if (!okMsg) {
6030
+ if (this.onError) this.onError(errMsg, msg);
6031
+ this.channel.nack(msg, false, false);
6032
+ }
6033
+ }
6034
+ });
6035
+ });
6036
+ if (!ok) {
6037
+ if (this.onError) this.onError(err);
6038
+ if (!this._stopped) {
6039
+ setTimeout(() => this._connect(), this.reconnectInterval);
6040
+ }
6041
+ }
6042
+ }
6043
+ }
6044
+
6045
+ const CONSUMER_DRIVERS = {
6046
+ sqs: SqsConsumer,
6047
+ rabbitmq: RabbitMqConsumer
6048
+ // kafka: KafkaConsumer, // futuro
6049
+ };
6050
+ function createConsumer(driver, config) {
6051
+ const ConsumerClass = CONSUMER_DRIVERS[driver];
6052
+ if (!ConsumerClass) {
6053
+ throw new Error(`Unknown consumer driver: ${driver}. Available: ${Object.keys(CONSUMER_DRIVERS).join(", ")}`);
6054
+ }
6055
+ return new ConsumerClass(config);
6056
+ }
6057
+
6058
+ class QueueConsumerPlugin {
6059
+ constructor(options = {}) {
6060
+ this.options = options;
6061
+ this.driversConfig = Array.isArray(options.consumers) ? options.consumers : [];
6062
+ this.consumers = [];
6063
+ }
6064
+ async setup(database) {
6065
+ this.database = database;
6066
+ for (const driverDef of this.driversConfig) {
6067
+ const { driver, config: driverConfig = {}, consumers: consumerDefs = [] } = driverDef;
6068
+ if (consumerDefs.length === 0 && driverDef.resources) {
6069
+ const { resources, driver: defDriver, config: nestedConfig, ...directConfig } = driverDef;
6070
+ const resourceList = Array.isArray(resources) ? resources : [resources];
6071
+ const flatConfig = nestedConfig ? { ...directConfig, ...nestedConfig } : directConfig;
6072
+ for (const resource of resourceList) {
6073
+ const consumer = createConsumer(driver, {
6074
+ ...flatConfig,
6075
+ onMessage: (msg) => this._handleMessage(msg, resource),
6076
+ onError: (err, raw) => this._handleError(err, raw, resource)
6077
+ });
6078
+ await consumer.start();
6079
+ this.consumers.push(consumer);
6080
+ }
6081
+ } else {
6082
+ for (const consumerDef of consumerDefs) {
6083
+ const { resources, ...consumerConfig } = consumerDef;
6084
+ const resourceList = Array.isArray(resources) ? resources : [resources];
6085
+ for (const resource of resourceList) {
6086
+ const mergedConfig = { ...driverConfig, ...consumerConfig };
6087
+ const consumer = createConsumer(driver, {
6088
+ ...mergedConfig,
6089
+ onMessage: (msg) => this._handleMessage(msg, resource),
6090
+ onError: (err, raw) => this._handleError(err, raw, resource)
6091
+ });
6092
+ await consumer.start();
6093
+ this.consumers.push(consumer);
6094
+ }
6095
+ }
6096
+ }
6097
+ }
6098
+ }
6099
+ async stop() {
6100
+ if (!Array.isArray(this.consumers)) this.consumers = [];
6101
+ for (const consumer of this.consumers) {
6102
+ if (consumer && typeof consumer.stop === "function") {
6103
+ await consumer.stop();
6104
+ }
6105
+ }
6106
+ this.consumers = [];
6107
+ }
6108
+ async _handleMessage(msg, configuredResource) {
6109
+ this.options;
6110
+ let body = msg.$body || msg;
6111
+ if (body.$body && !body.resource && !body.action && !body.data) {
6112
+ body = body.$body;
6113
+ }
6114
+ let resource = body.resource || msg.resource;
6115
+ let action = body.action || msg.action;
6116
+ let data = body.data || msg.data;
6117
+ if (!resource) {
6118
+ throw new Error("QueueConsumerPlugin: resource not found in message");
6119
+ }
6120
+ if (!action) {
6121
+ throw new Error("QueueConsumerPlugin: action not found in message");
6122
+ }
6123
+ const resourceObj = this.database.resources[resource];
6124
+ if (!resourceObj) throw new Error(`QueueConsumerPlugin: resource '${resource}' not found`);
6125
+ let result;
6126
+ const [ok, err, res] = await tryFn(async () => {
6127
+ if (action === "insert") {
6128
+ result = await resourceObj.insert(data);
6129
+ } else if (action === "update") {
6130
+ const { id: updateId, ...updateAttributes } = data;
6131
+ result = await resourceObj.update(updateId, updateAttributes);
6132
+ } else if (action === "delete") {
6133
+ result = await resourceObj.delete(data.id);
6134
+ } else {
6135
+ throw new Error(`QueueConsumerPlugin: unsupported action '${action}'`);
6136
+ }
6137
+ return result;
6138
+ });
6139
+ if (!ok) {
6140
+ throw err;
6141
+ }
6142
+ return res;
6143
+ }
6144
+ _handleError(err, raw, resourceName) {
6145
+ }
6146
+ }
6147
+
5901
6148
  class BaseReplicator extends EventEmitter {
5902
6149
  constructor(config = {}) {
5903
6150
  super();
@@ -10854,7 +11101,7 @@ class Database extends EventEmitter {
10854
11101
  this.id = idGenerator(7);
10855
11102
  this.version = "1";
10856
11103
  this.s3dbVersion = (() => {
10857
- const [ok, err, version] = tryFn(() => true ? "10.0.1" : "latest");
11104
+ const [ok, err, version] = tryFn(() => true ? "10.0.3" : "latest");
10858
11105
  return ok ? version : "latest";
10859
11106
  })();
10860
11107
  this.resources = {};
@@ -14565,5 +14812,5 @@ class StateMachinePlugin extends Plugin {
14565
14812
  }
14566
14813
  }
14567
14814
 
14568
- export { AVAILABLE_BEHAVIORS, AuditPlugin, AuthenticationError, BackupPlugin, BaseError, CachePlugin, Client, ConnectionString, ConnectionStringError, CostsPlugin, CryptoError, DEFAULT_BEHAVIOR, Database, DatabaseError, EncryptionError, ErrorMap, EventualConsistencyPlugin, FullTextPlugin, InvalidResourceItem, MetricsPlugin, MissingMetadata, NoSuchBucket, NoSuchKey, NotFound, PartitionError, PermissionError, Plugin, PluginObject, ReplicatorPlugin, Resource, ResourceError, ResourceIdsPageReader, ResourceIdsReader, ResourceNotFound, ResourceReader, ResourceWriter, S3QueuePlugin, Database as S3db, S3dbError, SchedulerPlugin, Schema, SchemaError, StateMachinePlugin, UnknownError, ValidationError, Validator, behaviors, calculateAttributeNamesSize, calculateAttributeSizes, calculateEffectiveLimit, calculateSystemOverhead, calculateTotalSize, calculateUTF8Bytes, clearUTF8Cache, clearUTF8Memo, clearUTF8Memory, decode, decodeDecimal, decrypt, S3db as default, encode, encodeDecimal, encrypt, getBehavior, getSizeBreakdown, idGenerator, mapAwsError, md5, passwordGenerator, sha256, streamToString, transformValue, tryFn, tryFnSync };
14815
+ export { AVAILABLE_BEHAVIORS, AuditPlugin, AuthenticationError, BackupPlugin, BaseError, CachePlugin, Client, ConnectionString, ConnectionStringError, CostsPlugin, CryptoError, DEFAULT_BEHAVIOR, Database, DatabaseError, EncryptionError, ErrorMap, EventualConsistencyPlugin, FullTextPlugin, InvalidResourceItem, MetricsPlugin, MissingMetadata, NoSuchBucket, NoSuchKey, NotFound, PartitionError, PermissionError, Plugin, PluginObject, QueueConsumerPlugin, ReplicatorPlugin, Resource, ResourceError, ResourceIdsPageReader, ResourceIdsReader, ResourceNotFound, ResourceReader, ResourceWriter, S3QueuePlugin, Database as S3db, S3dbError, SchedulerPlugin, Schema, SchemaError, StateMachinePlugin, UnknownError, ValidationError, Validator, behaviors, calculateAttributeNamesSize, calculateAttributeSizes, calculateEffectiveLimit, calculateSystemOverhead, calculateTotalSize, calculateUTF8Bytes, clearUTF8Cache, clearUTF8Memo, clearUTF8Memory, decode, decodeDecimal, decrypt, S3db as default, encode, encodeDecimal, encrypt, getBehavior, getSizeBreakdown, idGenerator, mapAwsError, md5, passwordGenerator, sha256, streamToString, transformValue, tryFn, tryFnSync };
14569
14816
  //# sourceMappingURL=s3db.es.js.map