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 +249 -1
- package/dist/s3db.cjs.js.map +1 -1
- package/dist/s3db.d.ts +70 -3
- package/dist/s3db.es.js +249 -2
- package/dist/s3db.es.js.map +1 -1
- package/package.json +1 -1
- package/src/plugins/audit.plugin.js +4 -2
- package/src/plugins/backup.plugin.js +3 -1
- package/src/plugins/queue-consumer.plugin.js +4 -2
- package/src/s3db.d.ts +70 -3
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.
|
|
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;
|