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.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
|
|
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():
|
|
1057
|
+
getReplicatorStats(): Promise<ReplicatorStats>;
|
|
1035
1058
|
getReplicatorLogs(filters?: any): Promise<any[]>;
|
|
1036
|
-
|
|
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.
|
|
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
|