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.cjs.js CHANGED
@@ -5902,6 +5902,253 @@ class MetricsPlugin extends Plugin {
5902
5902
  }
5903
5903
  }
5904
5904
 
5905
+ class SqsConsumer {
5906
+ constructor({ queueUrl, onMessage, onError, poolingInterval = 5e3, maxMessages = 10, region = "us-east-1", credentials, endpoint, driver = "sqs" }) {
5907
+ this.driver = driver;
5908
+ this.queueUrl = queueUrl;
5909
+ this.onMessage = onMessage;
5910
+ this.onError = onError;
5911
+ this.poolingInterval = poolingInterval;
5912
+ this.maxMessages = maxMessages;
5913
+ this.region = region;
5914
+ this.credentials = credentials;
5915
+ this.endpoint = endpoint;
5916
+ this.sqs = null;
5917
+ this._stopped = false;
5918
+ this._timer = null;
5919
+ this._pollPromise = null;
5920
+ this._pollResolve = null;
5921
+ this._SQSClient = null;
5922
+ this._ReceiveMessageCommand = null;
5923
+ this._DeleteMessageCommand = null;
5924
+ }
5925
+ async start() {
5926
+ const [ok, err, sdk] = await tryFn(() => import('@aws-sdk/client-sqs'));
5927
+ if (!ok) throw new Error("SqsConsumer: @aws-sdk/client-sqs is not installed. Please install it to use the SQS consumer.");
5928
+ const { SQSClient, ReceiveMessageCommand, DeleteMessageCommand } = sdk;
5929
+ this._SQSClient = SQSClient;
5930
+ this._ReceiveMessageCommand = ReceiveMessageCommand;
5931
+ this._DeleteMessageCommand = DeleteMessageCommand;
5932
+ this.sqs = new SQSClient({ region: this.region, credentials: this.credentials, endpoint: this.endpoint });
5933
+ this._stopped = false;
5934
+ this._pollPromise = new Promise((resolve) => {
5935
+ this._pollResolve = resolve;
5936
+ });
5937
+ this._poll();
5938
+ }
5939
+ async stop() {
5940
+ this._stopped = true;
5941
+ if (this._timer) {
5942
+ clearTimeout(this._timer);
5943
+ this._timer = null;
5944
+ }
5945
+ if (this._pollResolve) {
5946
+ this._pollResolve();
5947
+ }
5948
+ }
5949
+ async _poll() {
5950
+ if (this._stopped) {
5951
+ if (this._pollResolve) this._pollResolve();
5952
+ return;
5953
+ }
5954
+ const [ok, err, result] = await tryFn(async () => {
5955
+ const cmd = new this._ReceiveMessageCommand({
5956
+ QueueUrl: this.queueUrl,
5957
+ MaxNumberOfMessages: this.maxMessages,
5958
+ WaitTimeSeconds: 10,
5959
+ MessageAttributeNames: ["All"]
5960
+ });
5961
+ const { Messages } = await this.sqs.send(cmd);
5962
+ if (Messages && Messages.length > 0) {
5963
+ for (const msg of Messages) {
5964
+ const [okMsg, errMsg] = await tryFn(async () => {
5965
+ const parsedMsg = this._parseMessage(msg);
5966
+ await this.onMessage(parsedMsg, msg);
5967
+ await this.sqs.send(new this._DeleteMessageCommand({
5968
+ QueueUrl: this.queueUrl,
5969
+ ReceiptHandle: msg.ReceiptHandle
5970
+ }));
5971
+ });
5972
+ if (!okMsg && this.onError) {
5973
+ this.onError(errMsg, msg);
5974
+ }
5975
+ }
5976
+ }
5977
+ });
5978
+ if (!ok && this.onError) {
5979
+ this.onError(err);
5980
+ }
5981
+ this._timer = setTimeout(() => this._poll(), this.poolingInterval);
5982
+ }
5983
+ _parseMessage(msg) {
5984
+ let body;
5985
+ const [ok, err, parsed] = tryFn(() => JSON.parse(msg.Body));
5986
+ body = ok ? parsed : msg.Body;
5987
+ const attributes = {};
5988
+ if (msg.MessageAttributes) {
5989
+ for (const [k, v] of Object.entries(msg.MessageAttributes)) {
5990
+ attributes[k] = v.StringValue;
5991
+ }
5992
+ }
5993
+ return { $body: body, $attributes: attributes, $raw: msg };
5994
+ }
5995
+ }
5996
+
5997
+ class RabbitMqConsumer {
5998
+ constructor({ amqpUrl, queue, prefetch = 10, reconnectInterval = 2e3, onMessage, onError, driver = "rabbitmq" }) {
5999
+ this.amqpUrl = amqpUrl;
6000
+ this.queue = queue;
6001
+ this.prefetch = prefetch;
6002
+ this.reconnectInterval = reconnectInterval;
6003
+ this.onMessage = onMessage;
6004
+ this.onError = onError;
6005
+ this.driver = driver;
6006
+ this.connection = null;
6007
+ this.channel = null;
6008
+ this._stopped = false;
6009
+ }
6010
+ async start() {
6011
+ this._stopped = false;
6012
+ await this._connect();
6013
+ }
6014
+ async stop() {
6015
+ this._stopped = true;
6016
+ if (this.channel) await this.channel.close();
6017
+ if (this.connection) await this.connection.close();
6018
+ }
6019
+ async _connect() {
6020
+ const [ok, err] = await tryFn(async () => {
6021
+ const amqp = (await import('amqplib')).default;
6022
+ this.connection = await amqp.connect(this.amqpUrl);
6023
+ this.channel = await this.connection.createChannel();
6024
+ await this.channel.assertQueue(this.queue, { durable: true });
6025
+ this.channel.prefetch(this.prefetch);
6026
+ this.channel.consume(this.queue, async (msg) => {
6027
+ if (msg !== null) {
6028
+ const [okMsg, errMsg] = await tryFn(async () => {
6029
+ const content = JSON.parse(msg.content.toString());
6030
+ await this.onMessage({ $body: content, $raw: msg });
6031
+ this.channel.ack(msg);
6032
+ });
6033
+ if (!okMsg) {
6034
+ if (this.onError) this.onError(errMsg, msg);
6035
+ this.channel.nack(msg, false, false);
6036
+ }
6037
+ }
6038
+ });
6039
+ });
6040
+ if (!ok) {
6041
+ if (this.onError) this.onError(err);
6042
+ if (!this._stopped) {
6043
+ setTimeout(() => this._connect(), this.reconnectInterval);
6044
+ }
6045
+ }
6046
+ }
6047
+ }
6048
+
6049
+ const CONSUMER_DRIVERS = {
6050
+ sqs: SqsConsumer,
6051
+ rabbitmq: RabbitMqConsumer
6052
+ // kafka: KafkaConsumer, // futuro
6053
+ };
6054
+ function createConsumer(driver, config) {
6055
+ const ConsumerClass = CONSUMER_DRIVERS[driver];
6056
+ if (!ConsumerClass) {
6057
+ throw new Error(`Unknown consumer driver: ${driver}. Available: ${Object.keys(CONSUMER_DRIVERS).join(", ")}`);
6058
+ }
6059
+ return new ConsumerClass(config);
6060
+ }
6061
+
6062
+ class QueueConsumerPlugin {
6063
+ constructor(options = {}) {
6064
+ this.options = options;
6065
+ this.driversConfig = Array.isArray(options.consumers) ? options.consumers : [];
6066
+ this.consumers = [];
6067
+ }
6068
+ async setup(database) {
6069
+ this.database = database;
6070
+ for (const driverDef of this.driversConfig) {
6071
+ const { driver, config: driverConfig = {}, consumers: consumerDefs = [] } = driverDef;
6072
+ if (consumerDefs.length === 0 && driverDef.resources) {
6073
+ const { resources, driver: defDriver, config: nestedConfig, ...directConfig } = driverDef;
6074
+ const resourceList = Array.isArray(resources) ? resources : [resources];
6075
+ const flatConfig = nestedConfig ? { ...directConfig, ...nestedConfig } : directConfig;
6076
+ for (const resource of resourceList) {
6077
+ const consumer = createConsumer(driver, {
6078
+ ...flatConfig,
6079
+ onMessage: (msg) => this._handleMessage(msg, resource),
6080
+ onError: (err, raw) => this._handleError(err, raw, resource)
6081
+ });
6082
+ await consumer.start();
6083
+ this.consumers.push(consumer);
6084
+ }
6085
+ } else {
6086
+ for (const consumerDef of consumerDefs) {
6087
+ const { resources, ...consumerConfig } = consumerDef;
6088
+ const resourceList = Array.isArray(resources) ? resources : [resources];
6089
+ for (const resource of resourceList) {
6090
+ const mergedConfig = { ...driverConfig, ...consumerConfig };
6091
+ const consumer = createConsumer(driver, {
6092
+ ...mergedConfig,
6093
+ onMessage: (msg) => this._handleMessage(msg, resource),
6094
+ onError: (err, raw) => this._handleError(err, raw, resource)
6095
+ });
6096
+ await consumer.start();
6097
+ this.consumers.push(consumer);
6098
+ }
6099
+ }
6100
+ }
6101
+ }
6102
+ }
6103
+ async stop() {
6104
+ if (!Array.isArray(this.consumers)) this.consumers = [];
6105
+ for (const consumer of this.consumers) {
6106
+ if (consumer && typeof consumer.stop === "function") {
6107
+ await consumer.stop();
6108
+ }
6109
+ }
6110
+ this.consumers = [];
6111
+ }
6112
+ async _handleMessage(msg, configuredResource) {
6113
+ this.options;
6114
+ let body = msg.$body || msg;
6115
+ if (body.$body && !body.resource && !body.action && !body.data) {
6116
+ body = body.$body;
6117
+ }
6118
+ let resource = body.resource || msg.resource;
6119
+ let action = body.action || msg.action;
6120
+ let data = body.data || msg.data;
6121
+ if (!resource) {
6122
+ throw new Error("QueueConsumerPlugin: resource not found in message");
6123
+ }
6124
+ if (!action) {
6125
+ throw new Error("QueueConsumerPlugin: action not found in message");
6126
+ }
6127
+ const resourceObj = this.database.resources[resource];
6128
+ if (!resourceObj) throw new Error(`QueueConsumerPlugin: resource '${resource}' not found`);
6129
+ let result;
6130
+ const [ok, err, res] = await tryFn(async () => {
6131
+ if (action === "insert") {
6132
+ result = await resourceObj.insert(data);
6133
+ } else if (action === "update") {
6134
+ const { id: updateId, ...updateAttributes } = data;
6135
+ result = await resourceObj.update(updateId, updateAttributes);
6136
+ } else if (action === "delete") {
6137
+ result = await resourceObj.delete(data.id);
6138
+ } else {
6139
+ throw new Error(`QueueConsumerPlugin: unsupported action '${action}'`);
6140
+ }
6141
+ return result;
6142
+ });
6143
+ if (!ok) {
6144
+ throw err;
6145
+ }
6146
+ return res;
6147
+ }
6148
+ _handleError(err, raw, resourceName) {
6149
+ }
6150
+ }
6151
+
5905
6152
  class BaseReplicator extends EventEmitter {
5906
6153
  constructor(config = {}) {
5907
6154
  super();
@@ -10858,7 +11105,7 @@ class Database extends EventEmitter {
10858
11105
  this.id = idGenerator(7);
10859
11106
  this.version = "1";
10860
11107
  this.s3dbVersion = (() => {
10861
- const [ok, err, version] = tryFn(() => true ? "10.0.1" : "latest");
11108
+ const [ok, err, version] = tryFn(() => true ? "10.0.3" : "latest");
10862
11109
  return ok ? version : "latest";
10863
11110
  })();
10864
11111
  this.resources = {};
@@ -14597,6 +14844,7 @@ exports.PartitionError = PartitionError;
14597
14844
  exports.PermissionError = PermissionError;
14598
14845
  exports.Plugin = Plugin;
14599
14846
  exports.PluginObject = PluginObject;
14847
+ exports.QueueConsumerPlugin = QueueConsumerPlugin;
14600
14848
  exports.ReplicatorPlugin = ReplicatorPlugin;
14601
14849
  exports.Resource = Resource;
14602
14850
  exports.ResourceError = ResourceError;