s3db.js 13.1.0 → 13.2.1
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/README.md +9 -9
- package/dist/s3db.cjs.js +1079 -271
- package/dist/s3db.cjs.js.map +1 -1
- package/dist/s3db.es.js +1079 -271
- package/dist/s3db.es.js.map +1 -1
- package/package.json +2 -1
- package/src/clients/memory-client.class.js +16 -16
- package/src/clients/s3-client.class.js +17 -17
- package/src/concerns/error-classifier.js +204 -0
- package/src/database.class.js +9 -9
- package/src/plugins/backup.plugin.js +8 -8
- package/src/plugins/cache.plugin.js +3 -3
- package/src/plugins/concerns/plugin-dependencies.js +12 -0
- package/src/plugins/geo.plugin.js +2 -2
- package/src/plugins/ml.plugin.js +337 -137
- package/src/plugins/relation.plugin.js +1 -1
- package/src/plugins/replicator.plugin.js +16 -16
- package/src/plugins/s3-queue.plugin.js +5 -5
- package/src/plugins/scheduler.plugin.js +7 -7
- package/src/plugins/state-machine.errors.js +9 -1
- package/src/plugins/state-machine.plugin.js +603 -16
- package/src/plugins/ttl.plugin.js +4 -4
- package/src/plugins/vector.plugin.js +10 -10
- package/src/resource.class.js +58 -40
package/dist/s3db.cjs.js
CHANGED
|
@@ -2567,6 +2567,18 @@ const PLUGIN_DEPENDENCIES = {
|
|
|
2567
2567
|
npmUrl: "https://www.npmjs.com/package/@hono/swagger-ui"
|
|
2568
2568
|
}
|
|
2569
2569
|
}
|
|
2570
|
+
},
|
|
2571
|
+
"ml-plugin": {
|
|
2572
|
+
name: "ML Plugin",
|
|
2573
|
+
docsUrl: "https://github.com/forattini-dev/s3db.js/blob/main/docs/plugins/ml-plugin.md",
|
|
2574
|
+
dependencies: {
|
|
2575
|
+
"@tensorflow/tfjs-node": {
|
|
2576
|
+
version: "^4.0.0",
|
|
2577
|
+
description: "TensorFlow.js for Node.js with native bindings",
|
|
2578
|
+
installCommand: "pnpm add @tensorflow/tfjs-node",
|
|
2579
|
+
npmUrl: "https://www.npmjs.com/package/@tensorflow/tfjs-node"
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2570
2582
|
}
|
|
2571
2583
|
};
|
|
2572
2584
|
function isVersionCompatible(actual, required) {
|
|
@@ -6616,7 +6628,7 @@ class BackupPlugin extends Plugin {
|
|
|
6616
6628
|
const storageInfo = this.driver.getStorageInfo();
|
|
6617
6629
|
console.log(`[BackupPlugin] Initialized with driver: ${storageInfo.type}`);
|
|
6618
6630
|
}
|
|
6619
|
-
this.emit("initialized", {
|
|
6631
|
+
this.emit("db:plugin:initialized", {
|
|
6620
6632
|
driver: this.driver.getType(),
|
|
6621
6633
|
config: this.driver.getStorageInfo()
|
|
6622
6634
|
});
|
|
@@ -6664,7 +6676,7 @@ class BackupPlugin extends Plugin {
|
|
|
6664
6676
|
if (this.config.onBackupStart) {
|
|
6665
6677
|
await this._executeHook(this.config.onBackupStart, type, { backupId });
|
|
6666
6678
|
}
|
|
6667
|
-
this.emit("
|
|
6679
|
+
this.emit("plg:backup:start", { id: backupId, type });
|
|
6668
6680
|
const metadata = await this._createBackupMetadata(backupId, type);
|
|
6669
6681
|
const tempBackupDir = path$1.join(this.config.tempDir, backupId);
|
|
6670
6682
|
await promises.mkdir(tempBackupDir, { recursive: true });
|
|
@@ -6697,7 +6709,7 @@ class BackupPlugin extends Plugin {
|
|
|
6697
6709
|
const stats = { backupId, type, size: totalSize, duration, driverInfo: uploadResult };
|
|
6698
6710
|
await this._executeHook(this.config.onBackupComplete, type, stats);
|
|
6699
6711
|
}
|
|
6700
|
-
this.emit("
|
|
6712
|
+
this.emit("plg:backup:complete", {
|
|
6701
6713
|
id: backupId,
|
|
6702
6714
|
type,
|
|
6703
6715
|
size: totalSize,
|
|
@@ -6725,7 +6737,7 @@ class BackupPlugin extends Plugin {
|
|
|
6725
6737
|
error: error.message,
|
|
6726
6738
|
duration: Date.now() - startTime
|
|
6727
6739
|
});
|
|
6728
|
-
this.emit("
|
|
6740
|
+
this.emit("plg:backup:error", { id: backupId, type, error: error.message });
|
|
6729
6741
|
throw error;
|
|
6730
6742
|
} finally {
|
|
6731
6743
|
this.activeBackups.delete(backupId);
|
|
@@ -6946,7 +6958,7 @@ class BackupPlugin extends Plugin {
|
|
|
6946
6958
|
if (this.config.onRestoreStart) {
|
|
6947
6959
|
await this._executeHook(this.config.onRestoreStart, backupId, options);
|
|
6948
6960
|
}
|
|
6949
|
-
this.emit("
|
|
6961
|
+
this.emit("plg:backup:restore-start", { id: backupId, options });
|
|
6950
6962
|
const backup = await this.getBackupStatus(backupId);
|
|
6951
6963
|
if (!backup) {
|
|
6952
6964
|
throw new Error(`Backup '${backupId}' not found`);
|
|
@@ -6969,7 +6981,7 @@ class BackupPlugin extends Plugin {
|
|
|
6969
6981
|
if (this.config.onRestoreComplete) {
|
|
6970
6982
|
await this._executeHook(this.config.onRestoreComplete, backupId, { restored: restoredResources });
|
|
6971
6983
|
}
|
|
6972
|
-
this.emit("
|
|
6984
|
+
this.emit("plg:backup:restore-complete", {
|
|
6973
6985
|
id: backupId,
|
|
6974
6986
|
restored: restoredResources
|
|
6975
6987
|
});
|
|
@@ -6984,7 +6996,7 @@ class BackupPlugin extends Plugin {
|
|
|
6984
6996
|
if (this.config.onRestoreError) {
|
|
6985
6997
|
await this._executeHook(this.config.onRestoreError, backupId, { error });
|
|
6986
6998
|
}
|
|
6987
|
-
this.emit("
|
|
6999
|
+
this.emit("plg:backup:restore-error", { id: backupId, error: error.message });
|
|
6988
7000
|
throw error;
|
|
6989
7001
|
}
|
|
6990
7002
|
}
|
|
@@ -7234,7 +7246,7 @@ class BackupPlugin extends Plugin {
|
|
|
7234
7246
|
}
|
|
7235
7247
|
async stop() {
|
|
7236
7248
|
for (const backupId of this.activeBackups) {
|
|
7237
|
-
this.emit("
|
|
7249
|
+
this.emit("plg:backup:cancelled", { id: backupId });
|
|
7238
7250
|
}
|
|
7239
7251
|
this.activeBackups.clear();
|
|
7240
7252
|
if (this.driver) {
|
|
@@ -8758,7 +8770,7 @@ class CachePlugin extends Plugin {
|
|
|
8758
8770
|
const specificKey = await this.generateCacheKey(resource, method, { id: data.id });
|
|
8759
8771
|
const [ok2, err2] = await this.clearCacheWithRetry(resource.cache, specificKey);
|
|
8760
8772
|
if (!ok2) {
|
|
8761
|
-
this.emit("
|
|
8773
|
+
this.emit("plg:cache:clear-error", {
|
|
8762
8774
|
resource: resource.name,
|
|
8763
8775
|
method,
|
|
8764
8776
|
id: data.id,
|
|
@@ -8776,7 +8788,7 @@ class CachePlugin extends Plugin {
|
|
|
8776
8788
|
const partitionKeyPrefix = path$1.join(keyPrefix, `partition=${partitionName}`);
|
|
8777
8789
|
const [ok2, err2] = await this.clearCacheWithRetry(resource.cache, partitionKeyPrefix);
|
|
8778
8790
|
if (!ok2) {
|
|
8779
|
-
this.emit("
|
|
8791
|
+
this.emit("plg:cache:clear-error", {
|
|
8780
8792
|
resource: resource.name,
|
|
8781
8793
|
partition: partitionName,
|
|
8782
8794
|
error: err2.message
|
|
@@ -8791,7 +8803,7 @@ class CachePlugin extends Plugin {
|
|
|
8791
8803
|
}
|
|
8792
8804
|
const [ok, err] = await this.clearCacheWithRetry(resource.cache, keyPrefix);
|
|
8793
8805
|
if (!ok) {
|
|
8794
|
-
this.emit("
|
|
8806
|
+
this.emit("plg:cache:clear-error", {
|
|
8795
8807
|
resource: resource.name,
|
|
8796
8808
|
type: "broad",
|
|
8797
8809
|
error: err.message
|
|
@@ -12691,7 +12703,7 @@ class GeoPlugin extends Plugin {
|
|
|
12691
12703
|
if (this.verbose) {
|
|
12692
12704
|
console.log(`[GeoPlugin] Installed with ${Object.keys(this.resources).length} resources`);
|
|
12693
12705
|
}
|
|
12694
|
-
this.emit("installed", {
|
|
12706
|
+
this.emit("db:plugin:installed", {
|
|
12695
12707
|
plugin: "GeoPlugin",
|
|
12696
12708
|
resources: Object.keys(this.resources)
|
|
12697
12709
|
});
|
|
@@ -13258,7 +13270,7 @@ class GeoPlugin extends Plugin {
|
|
|
13258
13270
|
if (this.verbose) {
|
|
13259
13271
|
console.log("[GeoPlugin] Uninstalled");
|
|
13260
13272
|
}
|
|
13261
|
-
this.emit("uninstalled", {
|
|
13273
|
+
this.emit("db:plugin:uninstalled", {
|
|
13262
13274
|
plugin: "GeoPlugin"
|
|
13263
13275
|
});
|
|
13264
13276
|
await super.uninstall();
|
|
@@ -15260,7 +15272,7 @@ class MLPlugin extends Plugin {
|
|
|
15260
15272
|
enableVersioning: options.enableVersioning !== false
|
|
15261
15273
|
// Default true
|
|
15262
15274
|
};
|
|
15263
|
-
requirePluginDependency("
|
|
15275
|
+
requirePluginDependency("ml-plugin");
|
|
15264
15276
|
this.models = {};
|
|
15265
15277
|
this.modelVersions = /* @__PURE__ */ new Map();
|
|
15266
15278
|
this.modelCache = /* @__PURE__ */ new Map();
|
|
@@ -15298,7 +15310,7 @@ class MLPlugin extends Plugin {
|
|
|
15298
15310
|
if (this.config.verbose) {
|
|
15299
15311
|
console.log(`[MLPlugin] Installed with ${Object.keys(this.models).length} models`);
|
|
15300
15312
|
}
|
|
15301
|
-
this.emit("installed", {
|
|
15313
|
+
this.emit("db:plugin:installed", {
|
|
15302
15314
|
plugin: "MLPlugin",
|
|
15303
15315
|
models: Object.keys(this.models)
|
|
15304
15316
|
});
|
|
@@ -15649,6 +15661,22 @@ class MLPlugin extends Plugin {
|
|
|
15649
15661
|
}
|
|
15650
15662
|
data = allData;
|
|
15651
15663
|
}
|
|
15664
|
+
if (modelConfig.filter && typeof modelConfig.filter === "function") {
|
|
15665
|
+
if (this.config.verbose) {
|
|
15666
|
+
console.log(`[MLPlugin] Applying custom filter function...`);
|
|
15667
|
+
}
|
|
15668
|
+
const originalLength = data.length;
|
|
15669
|
+
data = data.filter(modelConfig.filter);
|
|
15670
|
+
if (this.config.verbose) {
|
|
15671
|
+
console.log(`[MLPlugin] Filter reduced dataset from ${originalLength} to ${data.length} samples`);
|
|
15672
|
+
}
|
|
15673
|
+
}
|
|
15674
|
+
if (modelConfig.map && typeof modelConfig.map === "function") {
|
|
15675
|
+
if (this.config.verbose) {
|
|
15676
|
+
console.log(`[MLPlugin] Applying custom map function...`);
|
|
15677
|
+
}
|
|
15678
|
+
data = data.map(modelConfig.map);
|
|
15679
|
+
}
|
|
15652
15680
|
if (!data || data.length < this.config.minTrainingSamples) {
|
|
15653
15681
|
throw new TrainingError(
|
|
15654
15682
|
`Insufficient training data: ${data?.length || 0} samples (minimum: ${this.config.minTrainingSamples})`,
|
|
@@ -15671,7 +15699,7 @@ class MLPlugin extends Plugin {
|
|
|
15671
15699
|
if (this.config.verbose) {
|
|
15672
15700
|
console.log(`[MLPlugin] Training completed for "${modelName}":`, result);
|
|
15673
15701
|
}
|
|
15674
|
-
this.emit("
|
|
15702
|
+
this.emit("plg:ml:model-trained", {
|
|
15675
15703
|
modelName,
|
|
15676
15704
|
type: modelConfig.type,
|
|
15677
15705
|
result
|
|
@@ -15707,7 +15735,7 @@ class MLPlugin extends Plugin {
|
|
|
15707
15735
|
try {
|
|
15708
15736
|
const result = await model.predict(input);
|
|
15709
15737
|
this.stats.totalPredictions++;
|
|
15710
|
-
this.emit("prediction", {
|
|
15738
|
+
this.emit("plg:ml:prediction", {
|
|
15711
15739
|
modelName,
|
|
15712
15740
|
input,
|
|
15713
15741
|
result
|
|
@@ -15822,7 +15850,11 @@ class MLPlugin extends Plugin {
|
|
|
15822
15850
|
async _initializeVersioning(modelName) {
|
|
15823
15851
|
try {
|
|
15824
15852
|
const storage = this.getStorage();
|
|
15825
|
-
const
|
|
15853
|
+
const modelConfig = this.config.models[modelName];
|
|
15854
|
+
const resourceName = modelConfig.resource;
|
|
15855
|
+
const [ok, err, versionInfo] = await tryFn(
|
|
15856
|
+
() => storage.get(storage.getPluginKey(resourceName, "metadata", modelName, "versions"))
|
|
15857
|
+
);
|
|
15826
15858
|
if (ok && versionInfo) {
|
|
15827
15859
|
this.modelVersions.set(modelName, {
|
|
15828
15860
|
currentVersion: versionInfo.currentVersion || 1,
|
|
@@ -15861,16 +15893,22 @@ class MLPlugin extends Plugin {
|
|
|
15861
15893
|
async _updateVersionInfo(modelName, version) {
|
|
15862
15894
|
try {
|
|
15863
15895
|
const storage = this.getStorage();
|
|
15896
|
+
const modelConfig = this.config.models[modelName];
|
|
15897
|
+
const resourceName = modelConfig.resource;
|
|
15864
15898
|
const versionInfo = this.modelVersions.get(modelName) || { currentVersion: 1, latestVersion: 0 };
|
|
15865
15899
|
versionInfo.latestVersion = Math.max(versionInfo.latestVersion, version);
|
|
15866
15900
|
versionInfo.currentVersion = version;
|
|
15867
15901
|
this.modelVersions.set(modelName, versionInfo);
|
|
15868
|
-
await storage.
|
|
15869
|
-
modelName,
|
|
15870
|
-
|
|
15871
|
-
|
|
15872
|
-
|
|
15873
|
-
|
|
15902
|
+
await storage.set(
|
|
15903
|
+
storage.getPluginKey(resourceName, "metadata", modelName, "versions"),
|
|
15904
|
+
{
|
|
15905
|
+
modelName,
|
|
15906
|
+
currentVersion: versionInfo.currentVersion,
|
|
15907
|
+
latestVersion: versionInfo.latestVersion,
|
|
15908
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
15909
|
+
},
|
|
15910
|
+
{ behavior: "body-overflow" }
|
|
15911
|
+
);
|
|
15874
15912
|
if (this.config.verbose) {
|
|
15875
15913
|
console.log(`[MLPlugin] Updated version info for "${modelName}": current=v${versionInfo.currentVersion}, latest=v${versionInfo.latestVersion}`);
|
|
15876
15914
|
}
|
|
@@ -15885,6 +15923,8 @@ class MLPlugin extends Plugin {
|
|
|
15885
15923
|
async _saveModel(modelName) {
|
|
15886
15924
|
try {
|
|
15887
15925
|
const storage = this.getStorage();
|
|
15926
|
+
const modelConfig = this.config.models[modelName];
|
|
15927
|
+
const resourceName = modelConfig.resource;
|
|
15888
15928
|
const exportedModel = await this.models[modelName].export();
|
|
15889
15929
|
if (!exportedModel) {
|
|
15890
15930
|
if (this.config.verbose) {
|
|
@@ -15896,37 +15936,52 @@ class MLPlugin extends Plugin {
|
|
|
15896
15936
|
if (enableVersioning) {
|
|
15897
15937
|
const version = this._getNextVersion(modelName);
|
|
15898
15938
|
const modelStats = this.models[modelName].getStats();
|
|
15899
|
-
await storage.
|
|
15900
|
-
modelName,
|
|
15901
|
-
|
|
15902
|
-
|
|
15903
|
-
|
|
15904
|
-
|
|
15905
|
-
|
|
15906
|
-
|
|
15907
|
-
|
|
15908
|
-
|
|
15909
|
-
|
|
15910
|
-
|
|
15939
|
+
await storage.set(
|
|
15940
|
+
storage.getPluginKey(resourceName, "models", modelName, `v${version}`),
|
|
15941
|
+
{
|
|
15942
|
+
modelName,
|
|
15943
|
+
version,
|
|
15944
|
+
type: "model",
|
|
15945
|
+
modelData: exportedModel,
|
|
15946
|
+
// TensorFlow.js model object (will go to body)
|
|
15947
|
+
metrics: {
|
|
15948
|
+
loss: modelStats.loss,
|
|
15949
|
+
accuracy: modelStats.accuracy,
|
|
15950
|
+
samples: modelStats.samples
|
|
15951
|
+
},
|
|
15952
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
15953
|
+
},
|
|
15954
|
+
{ behavior: "body-only" }
|
|
15955
|
+
// Large binary data goes to S3 body
|
|
15956
|
+
);
|
|
15911
15957
|
await this._updateVersionInfo(modelName, version);
|
|
15912
|
-
await storage.
|
|
15913
|
-
modelName,
|
|
15914
|
-
|
|
15915
|
-
|
|
15916
|
-
|
|
15917
|
-
|
|
15958
|
+
await storage.set(
|
|
15959
|
+
storage.getPluginKey(resourceName, "metadata", modelName, "active"),
|
|
15960
|
+
{
|
|
15961
|
+
modelName,
|
|
15962
|
+
version,
|
|
15963
|
+
type: "reference",
|
|
15964
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
15965
|
+
},
|
|
15966
|
+
{ behavior: "body-overflow" }
|
|
15967
|
+
// Small metadata
|
|
15968
|
+
);
|
|
15918
15969
|
if (this.config.verbose) {
|
|
15919
|
-
console.log(`[MLPlugin] Saved model "${modelName}" v${version} to
|
|
15970
|
+
console.log(`[MLPlugin] Saved model "${modelName}" v${version} to S3 (resource=${resourceName}/plugin=ml/models/${modelName}/v${version})`);
|
|
15920
15971
|
}
|
|
15921
15972
|
} else {
|
|
15922
|
-
await storage.
|
|
15923
|
-
modelName,
|
|
15924
|
-
|
|
15925
|
-
|
|
15926
|
-
|
|
15927
|
-
|
|
15973
|
+
await storage.set(
|
|
15974
|
+
storage.getPluginKey(resourceName, "models", modelName, "latest"),
|
|
15975
|
+
{
|
|
15976
|
+
modelName,
|
|
15977
|
+
type: "model",
|
|
15978
|
+
modelData: exportedModel,
|
|
15979
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
15980
|
+
},
|
|
15981
|
+
{ behavior: "body-only" }
|
|
15982
|
+
);
|
|
15928
15983
|
if (this.config.verbose) {
|
|
15929
|
-
console.log(`[MLPlugin] Saved model "${modelName}" to
|
|
15984
|
+
console.log(`[MLPlugin] Saved model "${modelName}" to S3 (resource=${resourceName}/plugin=ml/models/${modelName}/latest)`);
|
|
15930
15985
|
}
|
|
15931
15986
|
}
|
|
15932
15987
|
} catch (error) {
|
|
@@ -15934,7 +15989,7 @@ class MLPlugin extends Plugin {
|
|
|
15934
15989
|
}
|
|
15935
15990
|
}
|
|
15936
15991
|
/**
|
|
15937
|
-
* Save intermediate training data to plugin storage (incremental)
|
|
15992
|
+
* Save intermediate training data to plugin storage (incremental - only new samples)
|
|
15938
15993
|
* @private
|
|
15939
15994
|
*/
|
|
15940
15995
|
async _saveTrainingData(modelName, rawData) {
|
|
@@ -15942,64 +15997,106 @@ class MLPlugin extends Plugin {
|
|
|
15942
15997
|
const storage = this.getStorage();
|
|
15943
15998
|
const model = this.models[modelName];
|
|
15944
15999
|
const modelConfig = this.config.models[modelName];
|
|
16000
|
+
const resourceName = modelConfig.resource;
|
|
15945
16001
|
const modelStats = model.getStats();
|
|
15946
16002
|
const enableVersioning = this.config.enableVersioning;
|
|
15947
|
-
const
|
|
15948
|
-
|
|
15949
|
-
|
|
15950
|
-
|
|
15951
|
-
|
|
15952
|
-
|
|
15953
|
-
|
|
15954
|
-
|
|
15955
|
-
|
|
15956
|
-
|
|
15957
|
-
|
|
15958
|
-
|
|
15959
|
-
target: item[modelConfig.target]
|
|
15960
|
-
};
|
|
15961
|
-
}),
|
|
15962
|
-
metrics: {
|
|
15963
|
-
loss: modelStats.loss,
|
|
15964
|
-
accuracy: modelStats.accuracy,
|
|
15965
|
-
r2: modelStats.r2
|
|
15966
|
-
},
|
|
15967
|
-
trainedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
15968
|
-
};
|
|
16003
|
+
const processedData = rawData.map((item) => {
|
|
16004
|
+
const features = {};
|
|
16005
|
+
modelConfig.features.forEach((feature) => {
|
|
16006
|
+
features[feature] = item[feature];
|
|
16007
|
+
});
|
|
16008
|
+
return {
|
|
16009
|
+
id: item.id || `${Date.now()}_${Math.random()}`,
|
|
16010
|
+
// Use record ID or generate
|
|
16011
|
+
features,
|
|
16012
|
+
target: item[modelConfig.target]
|
|
16013
|
+
};
|
|
16014
|
+
});
|
|
15969
16015
|
if (enableVersioning) {
|
|
15970
|
-
const
|
|
16016
|
+
const version = this._getNextVersion(modelName);
|
|
16017
|
+
const [ok, err, existing] = await tryFn(
|
|
16018
|
+
() => storage.get(storage.getPluginKey(resourceName, "training", "history", modelName))
|
|
16019
|
+
);
|
|
15971
16020
|
let history = [];
|
|
16021
|
+
let previousSampleIds = /* @__PURE__ */ new Set();
|
|
15972
16022
|
if (ok && existing && existing.history) {
|
|
15973
|
-
|
|
15974
|
-
|
|
15975
|
-
|
|
15976
|
-
|
|
15977
|
-
|
|
16023
|
+
history = existing.history;
|
|
16024
|
+
history.forEach((entry) => {
|
|
16025
|
+
if (entry.sampleIds) {
|
|
16026
|
+
entry.sampleIds.forEach((id) => previousSampleIds.add(id));
|
|
16027
|
+
}
|
|
16028
|
+
});
|
|
15978
16029
|
}
|
|
15979
|
-
|
|
15980
|
-
|
|
15981
|
-
|
|
15982
|
-
|
|
15983
|
-
|
|
15984
|
-
|
|
15985
|
-
|
|
15986
|
-
|
|
15987
|
-
|
|
16030
|
+
const currentSampleIds = new Set(processedData.map((d) => d.id));
|
|
16031
|
+
const newSamples = processedData.filter((d) => !previousSampleIds.has(d.id));
|
|
16032
|
+
const newSampleIds = newSamples.map((d) => d.id);
|
|
16033
|
+
if (newSamples.length > 0) {
|
|
16034
|
+
await storage.set(
|
|
16035
|
+
storage.getPluginKey(resourceName, "training", "data", modelName, `v${version}`),
|
|
16036
|
+
{
|
|
16037
|
+
modelName,
|
|
16038
|
+
version,
|
|
16039
|
+
samples: newSamples,
|
|
16040
|
+
// Only new samples
|
|
16041
|
+
features: modelConfig.features,
|
|
16042
|
+
target: modelConfig.target,
|
|
16043
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
16044
|
+
},
|
|
16045
|
+
{ behavior: "body-only" }
|
|
16046
|
+
// Dataset goes to S3 body
|
|
16047
|
+
);
|
|
16048
|
+
}
|
|
16049
|
+
const historyEntry = {
|
|
16050
|
+
version,
|
|
16051
|
+
totalSamples: processedData.length,
|
|
16052
|
+
// Total cumulative
|
|
16053
|
+
newSamples: newSamples.length,
|
|
16054
|
+
// Only new in this version
|
|
16055
|
+
sampleIds: Array.from(currentSampleIds),
|
|
16056
|
+
// All IDs for this version
|
|
16057
|
+
newSampleIds,
|
|
16058
|
+
// IDs of new samples
|
|
16059
|
+
storageKey: newSamples.length > 0 ? `training/data/${modelName}/v${version}` : null,
|
|
16060
|
+
metrics: {
|
|
16061
|
+
loss: modelStats.loss,
|
|
16062
|
+
accuracy: modelStats.accuracy,
|
|
16063
|
+
r2: modelStats.r2
|
|
16064
|
+
},
|
|
16065
|
+
trainedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
16066
|
+
};
|
|
16067
|
+
history.push(historyEntry);
|
|
16068
|
+
await storage.set(
|
|
16069
|
+
storage.getPluginKey(resourceName, "training", "history", modelName),
|
|
16070
|
+
{
|
|
16071
|
+
modelName,
|
|
16072
|
+
type: "training_history",
|
|
16073
|
+
totalTrainings: history.length,
|
|
16074
|
+
latestVersion: version,
|
|
16075
|
+
history,
|
|
16076
|
+
// Array of metadata entries (not full data)
|
|
16077
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
16078
|
+
},
|
|
16079
|
+
{ behavior: "body-overflow" }
|
|
16080
|
+
// History metadata
|
|
16081
|
+
);
|
|
15988
16082
|
if (this.config.verbose) {
|
|
15989
|
-
console.log(`[MLPlugin]
|
|
16083
|
+
console.log(`[MLPlugin] Saved training data for "${modelName}" v${version}: ${newSamples.length} new samples (total: ${processedData.length}, storage: resource=${resourceName}/plugin=ml/training/data/${modelName}/v${version})`);
|
|
15990
16084
|
}
|
|
15991
16085
|
} else {
|
|
15992
|
-
await storage.
|
|
15993
|
-
modelName,
|
|
15994
|
-
|
|
15995
|
-
|
|
15996
|
-
|
|
15997
|
-
|
|
15998
|
-
|
|
15999
|
-
|
|
16000
|
-
|
|
16086
|
+
await storage.set(
|
|
16087
|
+
storage.getPluginKey(resourceName, "training", "data", modelName, "latest"),
|
|
16088
|
+
{
|
|
16089
|
+
modelName,
|
|
16090
|
+
type: "training_data",
|
|
16091
|
+
samples: processedData,
|
|
16092
|
+
features: modelConfig.features,
|
|
16093
|
+
target: modelConfig.target,
|
|
16094
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
16095
|
+
},
|
|
16096
|
+
{ behavior: "body-only" }
|
|
16097
|
+
);
|
|
16001
16098
|
if (this.config.verbose) {
|
|
16002
|
-
console.log(`[MLPlugin] Saved training data for "${modelName}" (${
|
|
16099
|
+
console.log(`[MLPlugin] Saved training data for "${modelName}" (${processedData.length} samples) to S3 (resource=${resourceName}/plugin=ml/training/data/${modelName}/latest)`);
|
|
16003
16100
|
}
|
|
16004
16101
|
}
|
|
16005
16102
|
} catch (error) {
|
|
@@ -16013,17 +16110,22 @@ class MLPlugin extends Plugin {
|
|
|
16013
16110
|
async _loadModel(modelName) {
|
|
16014
16111
|
try {
|
|
16015
16112
|
const storage = this.getStorage();
|
|
16113
|
+
const modelConfig = this.config.models[modelName];
|
|
16114
|
+
const resourceName = modelConfig.resource;
|
|
16016
16115
|
const enableVersioning = this.config.enableVersioning;
|
|
16017
16116
|
if (enableVersioning) {
|
|
16018
|
-
const [okRef, errRef, activeRef] = await tryFn(
|
|
16117
|
+
const [okRef, errRef, activeRef] = await tryFn(
|
|
16118
|
+
() => storage.get(storage.getPluginKey(resourceName, "metadata", modelName, "active"))
|
|
16119
|
+
);
|
|
16019
16120
|
if (okRef && activeRef && activeRef.version) {
|
|
16020
16121
|
const version = activeRef.version;
|
|
16021
|
-
const [ok, err, versionData] = await tryFn(
|
|
16022
|
-
|
|
16023
|
-
|
|
16024
|
-
|
|
16122
|
+
const [ok, err, versionData] = await tryFn(
|
|
16123
|
+
() => storage.get(storage.getPluginKey(resourceName, "models", modelName, `v${version}`))
|
|
16124
|
+
);
|
|
16125
|
+
if (ok && versionData && versionData.modelData) {
|
|
16126
|
+
await this.models[modelName].import(versionData.modelData);
|
|
16025
16127
|
if (this.config.verbose) {
|
|
16026
|
-
console.log(`[MLPlugin] Loaded model "${modelName}" v${version} (active) from
|
|
16128
|
+
console.log(`[MLPlugin] Loaded model "${modelName}" v${version} (active) from S3 (resource=${resourceName}/plugin=ml/models/${modelName}/v${version})`);
|
|
16027
16129
|
}
|
|
16028
16130
|
return;
|
|
16029
16131
|
}
|
|
@@ -16031,12 +16133,13 @@ class MLPlugin extends Plugin {
|
|
|
16031
16133
|
const versionInfo = this.modelVersions.get(modelName);
|
|
16032
16134
|
if (versionInfo && versionInfo.latestVersion > 0) {
|
|
16033
16135
|
const version = versionInfo.latestVersion;
|
|
16034
|
-
const [ok, err, versionData] = await tryFn(
|
|
16035
|
-
|
|
16036
|
-
|
|
16037
|
-
|
|
16136
|
+
const [ok, err, versionData] = await tryFn(
|
|
16137
|
+
() => storage.get(storage.getPluginKey(resourceName, "models", modelName, `v${version}`))
|
|
16138
|
+
);
|
|
16139
|
+
if (ok && versionData && versionData.modelData) {
|
|
16140
|
+
await this.models[modelName].import(versionData.modelData);
|
|
16038
16141
|
if (this.config.verbose) {
|
|
16039
|
-
console.log(`[MLPlugin] Loaded model "${modelName}" v${version} (latest) from
|
|
16142
|
+
console.log(`[MLPlugin] Loaded model "${modelName}" v${version} (latest) from S3`);
|
|
16040
16143
|
}
|
|
16041
16144
|
return;
|
|
16042
16145
|
}
|
|
@@ -16045,17 +16148,18 @@ class MLPlugin extends Plugin {
|
|
|
16045
16148
|
console.log(`[MLPlugin] No saved model versions found for "${modelName}"`);
|
|
16046
16149
|
}
|
|
16047
16150
|
} else {
|
|
16048
|
-
const [ok, err, record] = await tryFn(
|
|
16049
|
-
|
|
16151
|
+
const [ok, err, record] = await tryFn(
|
|
16152
|
+
() => storage.get(storage.getPluginKey(resourceName, "models", modelName, "latest"))
|
|
16153
|
+
);
|
|
16154
|
+
if (!ok || !record || !record.modelData) {
|
|
16050
16155
|
if (this.config.verbose) {
|
|
16051
16156
|
console.log(`[MLPlugin] No saved model found for "${modelName}"`);
|
|
16052
16157
|
}
|
|
16053
16158
|
return;
|
|
16054
16159
|
}
|
|
16055
|
-
|
|
16056
|
-
await this.models[modelName].import(modelData);
|
|
16160
|
+
await this.models[modelName].import(record.modelData);
|
|
16057
16161
|
if (this.config.verbose) {
|
|
16058
|
-
console.log(`[MLPlugin] Loaded model "${modelName}" from
|
|
16162
|
+
console.log(`[MLPlugin] Loaded model "${modelName}" from S3 (resource=${resourceName}/plugin=ml/models/${modelName}/latest)`);
|
|
16059
16163
|
}
|
|
16060
16164
|
}
|
|
16061
16165
|
} catch (error) {
|
|
@@ -16063,27 +16167,68 @@ class MLPlugin extends Plugin {
|
|
|
16063
16167
|
}
|
|
16064
16168
|
}
|
|
16065
16169
|
/**
|
|
16066
|
-
* Load training data from plugin storage
|
|
16170
|
+
* Load training data from plugin storage (reconstructs specific version from incremental data)
|
|
16067
16171
|
* @param {string} modelName - Model name
|
|
16172
|
+
* @param {number} version - Version number (optional, defaults to latest)
|
|
16068
16173
|
* @returns {Object|null} Training data or null if not found
|
|
16069
16174
|
*/
|
|
16070
|
-
async getTrainingData(modelName) {
|
|
16175
|
+
async getTrainingData(modelName, version = null) {
|
|
16071
16176
|
try {
|
|
16072
16177
|
const storage = this.getStorage();
|
|
16073
|
-
const
|
|
16074
|
-
|
|
16178
|
+
const modelConfig = this.config.models[modelName];
|
|
16179
|
+
const resourceName = modelConfig.resource;
|
|
16180
|
+
const enableVersioning = this.config.enableVersioning;
|
|
16181
|
+
if (!enableVersioning) {
|
|
16182
|
+
const [ok, err, record] = await tryFn(
|
|
16183
|
+
() => storage.get(storage.getPluginKey(resourceName, "training", "data", modelName, "latest"))
|
|
16184
|
+
);
|
|
16185
|
+
if (!ok || !record) {
|
|
16186
|
+
if (this.config.verbose) {
|
|
16187
|
+
console.log(`[MLPlugin] No saved training data found for "${modelName}"`);
|
|
16188
|
+
}
|
|
16189
|
+
return null;
|
|
16190
|
+
}
|
|
16191
|
+
return {
|
|
16192
|
+
modelName: record.modelName,
|
|
16193
|
+
samples: record.samples,
|
|
16194
|
+
features: record.features,
|
|
16195
|
+
target: record.target,
|
|
16196
|
+
data: record.samples,
|
|
16197
|
+
savedAt: record.savedAt
|
|
16198
|
+
};
|
|
16199
|
+
}
|
|
16200
|
+
const [okHistory, errHistory, historyData] = await tryFn(
|
|
16201
|
+
() => storage.get(storage.getPluginKey(resourceName, "training", "history", modelName))
|
|
16202
|
+
);
|
|
16203
|
+
if (!okHistory || !historyData || !historyData.history) {
|
|
16075
16204
|
if (this.config.verbose) {
|
|
16076
|
-
console.log(`[MLPlugin] No
|
|
16205
|
+
console.log(`[MLPlugin] No training history found for "${modelName}"`);
|
|
16077
16206
|
}
|
|
16078
16207
|
return null;
|
|
16079
16208
|
}
|
|
16209
|
+
const targetVersion = version || historyData.latestVersion;
|
|
16210
|
+
const reconstructedSamples = [];
|
|
16211
|
+
for (const entry of historyData.history) {
|
|
16212
|
+
if (entry.version > targetVersion) break;
|
|
16213
|
+
if (entry.storageKey && entry.newSamples > 0) {
|
|
16214
|
+
const [ok, err, versionData] = await tryFn(
|
|
16215
|
+
() => storage.get(storage.getPluginKey(resourceName, "training", "data", modelName, `v${entry.version}`))
|
|
16216
|
+
);
|
|
16217
|
+
if (ok && versionData && versionData.samples) {
|
|
16218
|
+
reconstructedSamples.push(...versionData.samples);
|
|
16219
|
+
}
|
|
16220
|
+
}
|
|
16221
|
+
}
|
|
16222
|
+
const targetEntry = historyData.history.find((e) => e.version === targetVersion);
|
|
16080
16223
|
return {
|
|
16081
|
-
modelName
|
|
16082
|
-
|
|
16083
|
-
|
|
16084
|
-
|
|
16085
|
-
|
|
16086
|
-
|
|
16224
|
+
modelName,
|
|
16225
|
+
version: targetVersion,
|
|
16226
|
+
samples: reconstructedSamples,
|
|
16227
|
+
totalSamples: reconstructedSamples.length,
|
|
16228
|
+
features: modelConfig.features,
|
|
16229
|
+
target: modelConfig.target,
|
|
16230
|
+
metrics: targetEntry?.metrics,
|
|
16231
|
+
savedAt: targetEntry?.trainedAt
|
|
16087
16232
|
};
|
|
16088
16233
|
} catch (error) {
|
|
16089
16234
|
console.error(`[MLPlugin] Failed to load training data for "${modelName}":`, error.message);
|
|
@@ -16091,15 +16236,29 @@ class MLPlugin extends Plugin {
|
|
|
16091
16236
|
}
|
|
16092
16237
|
}
|
|
16093
16238
|
/**
|
|
16094
|
-
* Delete model from plugin storage
|
|
16239
|
+
* Delete model from plugin storage (all versions)
|
|
16095
16240
|
* @private
|
|
16096
16241
|
*/
|
|
16097
16242
|
async _deleteModel(modelName) {
|
|
16098
16243
|
try {
|
|
16099
16244
|
const storage = this.getStorage();
|
|
16100
|
-
|
|
16245
|
+
const modelConfig = this.config.models[modelName];
|
|
16246
|
+
const resourceName = modelConfig.resource;
|
|
16247
|
+
const enableVersioning = this.config.enableVersioning;
|
|
16248
|
+
if (enableVersioning) {
|
|
16249
|
+
const versionInfo = this.modelVersions.get(modelName);
|
|
16250
|
+
if (versionInfo && versionInfo.latestVersion > 0) {
|
|
16251
|
+
for (let v = 1; v <= versionInfo.latestVersion; v++) {
|
|
16252
|
+
await storage.delete(storage.getPluginKey(resourceName, "models", modelName, `v${v}`));
|
|
16253
|
+
}
|
|
16254
|
+
}
|
|
16255
|
+
await storage.delete(storage.getPluginKey(resourceName, "metadata", modelName, "active"));
|
|
16256
|
+
await storage.delete(storage.getPluginKey(resourceName, "metadata", modelName, "versions"));
|
|
16257
|
+
} else {
|
|
16258
|
+
await storage.delete(storage.getPluginKey(resourceName, "models", modelName, "latest"));
|
|
16259
|
+
}
|
|
16101
16260
|
if (this.config.verbose) {
|
|
16102
|
-
console.log(`[MLPlugin] Deleted model "${modelName}" from plugin
|
|
16261
|
+
console.log(`[MLPlugin] Deleted model "${modelName}" from S3 (resource=${resourceName}/plugin=ml/models/${modelName}/)`);
|
|
16103
16262
|
}
|
|
16104
16263
|
} catch (error) {
|
|
16105
16264
|
if (this.config.verbose) {
|
|
@@ -16108,15 +16267,32 @@ class MLPlugin extends Plugin {
|
|
|
16108
16267
|
}
|
|
16109
16268
|
}
|
|
16110
16269
|
/**
|
|
16111
|
-
* Delete training data from plugin storage
|
|
16270
|
+
* Delete training data from plugin storage (all versions)
|
|
16112
16271
|
* @private
|
|
16113
16272
|
*/
|
|
16114
16273
|
async _deleteTrainingData(modelName) {
|
|
16115
16274
|
try {
|
|
16116
16275
|
const storage = this.getStorage();
|
|
16117
|
-
|
|
16276
|
+
const modelConfig = this.config.models[modelName];
|
|
16277
|
+
const resourceName = modelConfig.resource;
|
|
16278
|
+
const enableVersioning = this.config.enableVersioning;
|
|
16279
|
+
if (enableVersioning) {
|
|
16280
|
+
const [ok, err, historyData] = await tryFn(
|
|
16281
|
+
() => storage.get(storage.getPluginKey(resourceName, "training", "history", modelName))
|
|
16282
|
+
);
|
|
16283
|
+
if (ok && historyData && historyData.history) {
|
|
16284
|
+
for (const entry of historyData.history) {
|
|
16285
|
+
if (entry.storageKey) {
|
|
16286
|
+
await storage.delete(storage.getPluginKey(resourceName, "training", "data", modelName, `v${entry.version}`));
|
|
16287
|
+
}
|
|
16288
|
+
}
|
|
16289
|
+
}
|
|
16290
|
+
await storage.delete(storage.getPluginKey(resourceName, "training", "history", modelName));
|
|
16291
|
+
} else {
|
|
16292
|
+
await storage.delete(storage.getPluginKey(resourceName, "training", "data", modelName, "latest"));
|
|
16293
|
+
}
|
|
16118
16294
|
if (this.config.verbose) {
|
|
16119
|
-
console.log(`[MLPlugin] Deleted training data for "${modelName}" from plugin
|
|
16295
|
+
console.log(`[MLPlugin] Deleted training data for "${modelName}" from S3 (resource=${resourceName}/plugin=ml/training/)`);
|
|
16120
16296
|
}
|
|
16121
16297
|
} catch (error) {
|
|
16122
16298
|
if (this.config.verbose) {
|
|
@@ -16135,17 +16311,18 @@ class MLPlugin extends Plugin {
|
|
|
16135
16311
|
}
|
|
16136
16312
|
try {
|
|
16137
16313
|
const storage = this.getStorage();
|
|
16314
|
+
const modelConfig = this.config.models[modelName];
|
|
16315
|
+
const resourceName = modelConfig.resource;
|
|
16138
16316
|
const versionInfo = this.modelVersions.get(modelName) || { latestVersion: 0 };
|
|
16139
16317
|
const versions = [];
|
|
16140
16318
|
for (let v = 1; v <= versionInfo.latestVersion; v++) {
|
|
16141
|
-
const [ok, err, versionData] = await tryFn(() => storage.get(`
|
|
16319
|
+
const [ok, err, versionData] = await tryFn(() => storage.get(storage.getPluginKey(resourceName, "models", modelName, `v${v}`)));
|
|
16142
16320
|
if (ok && versionData) {
|
|
16143
|
-
const metrics = versionData.metrics ? JSON.parse(versionData.metrics) : {};
|
|
16144
16321
|
versions.push({
|
|
16145
16322
|
version: v,
|
|
16146
16323
|
savedAt: versionData.savedAt,
|
|
16147
16324
|
isCurrent: v === versionInfo.currentVersion,
|
|
16148
|
-
metrics
|
|
16325
|
+
metrics: versionData.metrics
|
|
16149
16326
|
});
|
|
16150
16327
|
}
|
|
16151
16328
|
}
|
|
@@ -16169,12 +16346,16 @@ class MLPlugin extends Plugin {
|
|
|
16169
16346
|
}
|
|
16170
16347
|
try {
|
|
16171
16348
|
const storage = this.getStorage();
|
|
16172
|
-
const
|
|
16349
|
+
const modelConfig = this.config.models[modelName];
|
|
16350
|
+
const resourceName = modelConfig.resource;
|
|
16351
|
+
const [ok, err, versionData] = await tryFn(() => storage.get(storage.getPluginKey(resourceName, "models", modelName, `v${version}`)));
|
|
16173
16352
|
if (!ok || !versionData) {
|
|
16174
16353
|
throw new MLError(`Version ${version} not found for model "${modelName}"`, { modelName, version });
|
|
16175
16354
|
}
|
|
16176
|
-
|
|
16177
|
-
|
|
16355
|
+
if (!versionData.modelData) {
|
|
16356
|
+
throw new MLError(`Model data not found in version ${version}`, { modelName, version });
|
|
16357
|
+
}
|
|
16358
|
+
await this.models[modelName].import(versionData.modelData);
|
|
16178
16359
|
const versionInfo = this.modelVersions.get(modelName);
|
|
16179
16360
|
if (versionInfo) {
|
|
16180
16361
|
versionInfo.currentVersion = version;
|
|
@@ -16202,10 +16383,12 @@ class MLPlugin extends Plugin {
|
|
|
16202
16383
|
if (!this.config.enableVersioning) {
|
|
16203
16384
|
throw new MLError("Versioning is not enabled", { modelName });
|
|
16204
16385
|
}
|
|
16386
|
+
const modelConfig = this.config.models[modelName];
|
|
16387
|
+
const resourceName = modelConfig.resource;
|
|
16205
16388
|
await this.loadModelVersion(modelName, version);
|
|
16206
16389
|
await this._updateVersionInfo(modelName, version);
|
|
16207
16390
|
const storage = this.getStorage();
|
|
16208
|
-
await storage.
|
|
16391
|
+
await storage.set(storage.getPluginKey(resourceName, "metadata", modelName, "active"), {
|
|
16209
16392
|
modelName,
|
|
16210
16393
|
version,
|
|
16211
16394
|
type: "reference",
|
|
@@ -16227,7 +16410,9 @@ class MLPlugin extends Plugin {
|
|
|
16227
16410
|
}
|
|
16228
16411
|
try {
|
|
16229
16412
|
const storage = this.getStorage();
|
|
16230
|
-
const
|
|
16413
|
+
const modelConfig = this.config.models[modelName];
|
|
16414
|
+
const resourceName = modelConfig.resource;
|
|
16415
|
+
const [ok, err, historyData] = await tryFn(() => storage.get(storage.getPluginKey(resourceName, "training", "history", modelName)));
|
|
16231
16416
|
if (!ok || !historyData) {
|
|
16232
16417
|
return null;
|
|
16233
16418
|
}
|
|
@@ -16256,8 +16441,10 @@ class MLPlugin extends Plugin {
|
|
|
16256
16441
|
}
|
|
16257
16442
|
try {
|
|
16258
16443
|
const storage = this.getStorage();
|
|
16259
|
-
const
|
|
16260
|
-
const
|
|
16444
|
+
const modelConfig = this.config.models[modelName];
|
|
16445
|
+
const resourceName = modelConfig.resource;
|
|
16446
|
+
const [ok1, err1, v1Data] = await tryFn(() => storage.get(storage.getPluginKey(resourceName, "models", modelName, `v${version1}`)));
|
|
16447
|
+
const [ok2, err2, v2Data] = await tryFn(() => storage.get(storage.getPluginKey(resourceName, "models", modelName, `v${version2}`)));
|
|
16261
16448
|
if (!ok1 || !v1Data) {
|
|
16262
16449
|
throw new MLError(`Version ${version1} not found`, { modelName, version: version1 });
|
|
16263
16450
|
}
|
|
@@ -16713,7 +16900,7 @@ class RelationPlugin extends Plugin {
|
|
|
16713
16900
|
if (this.verbose) {
|
|
16714
16901
|
console.log(`[RelationPlugin] Installed with ${Object.keys(this.relations).length} resources`);
|
|
16715
16902
|
}
|
|
16716
|
-
this.emit("installed", {
|
|
16903
|
+
this.emit("db:plugin:installed", {
|
|
16717
16904
|
plugin: "RelationPlugin",
|
|
16718
16905
|
resources: Object.keys(this.relations)
|
|
16719
16906
|
});
|
|
@@ -20288,7 +20475,7 @@ class S3Client extends EventEmitter {
|
|
|
20288
20475
|
return client;
|
|
20289
20476
|
}
|
|
20290
20477
|
async sendCommand(command) {
|
|
20291
|
-
this.emit("
|
|
20478
|
+
this.emit("cl:request", command.constructor.name, command.input);
|
|
20292
20479
|
const [ok, err, response] = await tryFn(() => this.client.send(command));
|
|
20293
20480
|
if (!ok) {
|
|
20294
20481
|
const bucket = this.config.bucket;
|
|
@@ -20300,7 +20487,7 @@ class S3Client extends EventEmitter {
|
|
|
20300
20487
|
commandInput: command.input
|
|
20301
20488
|
});
|
|
20302
20489
|
}
|
|
20303
|
-
this.emit("
|
|
20490
|
+
this.emit("cl:response", command.constructor.name, response, command.input);
|
|
20304
20491
|
return response;
|
|
20305
20492
|
}
|
|
20306
20493
|
async putObject({ key, metadata, contentType, body, contentEncoding, contentLength, ifMatch }) {
|
|
@@ -20325,7 +20512,7 @@ class S3Client extends EventEmitter {
|
|
|
20325
20512
|
if (contentLength !== void 0) options.ContentLength = contentLength;
|
|
20326
20513
|
if (ifMatch !== void 0) options.IfMatch = ifMatch;
|
|
20327
20514
|
const [ok, err, response] = await tryFn(() => this.sendCommand(new clientS3.PutObjectCommand(options)));
|
|
20328
|
-
this.emit("
|
|
20515
|
+
this.emit("cl:PutObject", err || response, { key, metadata, contentType, body, contentEncoding, contentLength });
|
|
20329
20516
|
if (!ok) {
|
|
20330
20517
|
throw mapAwsError(err, {
|
|
20331
20518
|
bucket: this.config.bucket,
|
|
@@ -20353,7 +20540,7 @@ class S3Client extends EventEmitter {
|
|
|
20353
20540
|
}
|
|
20354
20541
|
return res;
|
|
20355
20542
|
});
|
|
20356
|
-
this.emit("
|
|
20543
|
+
this.emit("cl:GetObject", err || response, { key });
|
|
20357
20544
|
if (!ok) {
|
|
20358
20545
|
throw mapAwsError(err, {
|
|
20359
20546
|
bucket: this.config.bucket,
|
|
@@ -20381,7 +20568,7 @@ class S3Client extends EventEmitter {
|
|
|
20381
20568
|
}
|
|
20382
20569
|
return res;
|
|
20383
20570
|
});
|
|
20384
|
-
this.emit("
|
|
20571
|
+
this.emit("cl:HeadObject", err || response, { key });
|
|
20385
20572
|
if (!ok) {
|
|
20386
20573
|
throw mapAwsError(err, {
|
|
20387
20574
|
bucket: this.config.bucket,
|
|
@@ -20414,7 +20601,7 @@ class S3Client extends EventEmitter {
|
|
|
20414
20601
|
options.ContentType = contentType;
|
|
20415
20602
|
}
|
|
20416
20603
|
const [ok, err, response] = await tryFn(() => this.sendCommand(new clientS3.CopyObjectCommand(options)));
|
|
20417
|
-
this.emit("
|
|
20604
|
+
this.emit("cl:CopyObject", err || response, { from, to, metadataDirective });
|
|
20418
20605
|
if (!ok) {
|
|
20419
20606
|
throw mapAwsError(err, {
|
|
20420
20607
|
bucket: this.config.bucket,
|
|
@@ -20439,7 +20626,7 @@ class S3Client extends EventEmitter {
|
|
|
20439
20626
|
Key: keyPrefix ? path$1.join(keyPrefix, key) : key
|
|
20440
20627
|
};
|
|
20441
20628
|
const [ok, err, response] = await tryFn(() => this.sendCommand(new clientS3.DeleteObjectCommand(options)));
|
|
20442
|
-
this.emit("
|
|
20629
|
+
this.emit("cl:DeleteObject", err || response, { key });
|
|
20443
20630
|
if (!ok) {
|
|
20444
20631
|
throw mapAwsError(err, {
|
|
20445
20632
|
bucket: this.config.bucket,
|
|
@@ -20479,7 +20666,7 @@ class S3Client extends EventEmitter {
|
|
|
20479
20666
|
deleted: results,
|
|
20480
20667
|
notFound: errors
|
|
20481
20668
|
};
|
|
20482
|
-
this.emit("
|
|
20669
|
+
this.emit("cl:DeleteObjects", report, keys);
|
|
20483
20670
|
return report;
|
|
20484
20671
|
}
|
|
20485
20672
|
/**
|
|
@@ -20509,7 +20696,7 @@ class S3Client extends EventEmitter {
|
|
|
20509
20696
|
const deleteResponse = await this.client.send(deleteCommand);
|
|
20510
20697
|
const deletedCount = deleteResponse.Deleted ? deleteResponse.Deleted.length : 0;
|
|
20511
20698
|
totalDeleted += deletedCount;
|
|
20512
|
-
this.emit("
|
|
20699
|
+
this.emit("cl:DeleteAll", {
|
|
20513
20700
|
prefix,
|
|
20514
20701
|
batch: deletedCount,
|
|
20515
20702
|
total: totalDeleted
|
|
@@ -20517,7 +20704,7 @@ class S3Client extends EventEmitter {
|
|
|
20517
20704
|
}
|
|
20518
20705
|
continuationToken = listResponse.IsTruncated ? listResponse.NextContinuationToken : void 0;
|
|
20519
20706
|
} while (continuationToken);
|
|
20520
|
-
this.emit("
|
|
20707
|
+
this.emit("cl:DeleteAllComplete", {
|
|
20521
20708
|
prefix,
|
|
20522
20709
|
totalDeleted
|
|
20523
20710
|
});
|
|
@@ -20548,7 +20735,7 @@ class S3Client extends EventEmitter {
|
|
|
20548
20735
|
if (!ok) {
|
|
20549
20736
|
throw new UnknownError("Unknown error in listObjects", { prefix, bucket: this.config.bucket, original: err });
|
|
20550
20737
|
}
|
|
20551
|
-
this.emit("
|
|
20738
|
+
this.emit("cl:ListObjects", response, options);
|
|
20552
20739
|
return response;
|
|
20553
20740
|
}
|
|
20554
20741
|
async count({ prefix } = {}) {
|
|
@@ -20565,7 +20752,7 @@ class S3Client extends EventEmitter {
|
|
|
20565
20752
|
truncated = response.IsTruncated || false;
|
|
20566
20753
|
continuationToken = response.NextContinuationToken;
|
|
20567
20754
|
}
|
|
20568
|
-
this.emit("
|
|
20755
|
+
this.emit("cl:Count", count, { prefix });
|
|
20569
20756
|
return count;
|
|
20570
20757
|
}
|
|
20571
20758
|
async getAllKeys({ prefix } = {}) {
|
|
@@ -20587,7 +20774,7 @@ class S3Client extends EventEmitter {
|
|
|
20587
20774
|
if (this.config.keyPrefix) {
|
|
20588
20775
|
keys = keys.map((x) => x.replace(this.config.keyPrefix, "")).map((x) => x.startsWith("/") ? x.replace(`/`, "") : x);
|
|
20589
20776
|
}
|
|
20590
|
-
this.emit("
|
|
20777
|
+
this.emit("cl:GetAllKeys", keys, { prefix });
|
|
20591
20778
|
return keys;
|
|
20592
20779
|
}
|
|
20593
20780
|
async getContinuationTokenAfterOffset(params = {}) {
|
|
@@ -20616,7 +20803,7 @@ class S3Client extends EventEmitter {
|
|
|
20616
20803
|
break;
|
|
20617
20804
|
}
|
|
20618
20805
|
}
|
|
20619
|
-
this.emit("
|
|
20806
|
+
this.emit("cl:GetContinuationTokenAfterOffset", continuationToken || null, params);
|
|
20620
20807
|
return continuationToken || null;
|
|
20621
20808
|
}
|
|
20622
20809
|
async getKeysPage(params = {}) {
|
|
@@ -20634,7 +20821,7 @@ class S3Client extends EventEmitter {
|
|
|
20634
20821
|
offset
|
|
20635
20822
|
});
|
|
20636
20823
|
if (!continuationToken) {
|
|
20637
|
-
this.emit("
|
|
20824
|
+
this.emit("cl:GetKeysPage", [], params);
|
|
20638
20825
|
return [];
|
|
20639
20826
|
}
|
|
20640
20827
|
}
|
|
@@ -20657,7 +20844,7 @@ class S3Client extends EventEmitter {
|
|
|
20657
20844
|
if (this.config.keyPrefix) {
|
|
20658
20845
|
keys = keys.map((x) => x.replace(this.config.keyPrefix, "")).map((x) => x.startsWith("/") ? x.replace(`/`, "") : x);
|
|
20659
20846
|
}
|
|
20660
|
-
this.emit("
|
|
20847
|
+
this.emit("cl:GetKeysPage", keys, params);
|
|
20661
20848
|
return keys;
|
|
20662
20849
|
}
|
|
20663
20850
|
async moveAllObjects({ prefixFrom, prefixTo }) {
|
|
@@ -20675,7 +20862,7 @@ class S3Client extends EventEmitter {
|
|
|
20675
20862
|
}
|
|
20676
20863
|
return to;
|
|
20677
20864
|
});
|
|
20678
|
-
this.emit("
|
|
20865
|
+
this.emit("cl:MoveAllObjects", { results, errors }, { prefixFrom, prefixTo });
|
|
20679
20866
|
if (errors.length > 0) {
|
|
20680
20867
|
throw new UnknownError("Some objects could not be moved", {
|
|
20681
20868
|
bucket: this.config.bucket,
|
|
@@ -23530,6 +23717,20 @@ ${errorDetails}`,
|
|
|
23530
23717
|
if (typeof body === "object") return Buffer.byteLength(JSON.stringify(body), "utf8");
|
|
23531
23718
|
return Buffer.byteLength(String(body), "utf8");
|
|
23532
23719
|
}
|
|
23720
|
+
/**
|
|
23721
|
+
* Emit standardized events with optional ID-specific variant
|
|
23722
|
+
*
|
|
23723
|
+
* @private
|
|
23724
|
+
* @param {string} event - Event name
|
|
23725
|
+
* @param {Object} payload - Event payload
|
|
23726
|
+
* @param {string} [id] - Optional ID for ID-specific events
|
|
23727
|
+
*/
|
|
23728
|
+
_emitStandardized(event, payload, id = null) {
|
|
23729
|
+
this.emit(event, payload);
|
|
23730
|
+
if (id) {
|
|
23731
|
+
this.emit(`${event}:${id}`, payload);
|
|
23732
|
+
}
|
|
23733
|
+
}
|
|
23533
23734
|
/**
|
|
23534
23735
|
* Insert a new resource object
|
|
23535
23736
|
* @param {Object} attributes - Resource attributes
|
|
@@ -23670,11 +23871,11 @@ ${errorDetails}`,
|
|
|
23670
23871
|
for (const hook of nonPartitionHooks) {
|
|
23671
23872
|
finalResult = await hook(finalResult);
|
|
23672
23873
|
}
|
|
23673
|
-
this.
|
|
23874
|
+
this._emitStandardized("inserted", finalResult, finalResult?.id || insertedObject?.id);
|
|
23674
23875
|
return finalResult;
|
|
23675
23876
|
} else {
|
|
23676
23877
|
const finalResult = await this.executeHooks("afterInsert", insertedObject);
|
|
23677
|
-
this.
|
|
23878
|
+
this._emitStandardized("inserted", finalResult, finalResult?.id || insertedObject?.id);
|
|
23678
23879
|
return finalResult;
|
|
23679
23880
|
}
|
|
23680
23881
|
}
|
|
@@ -23738,7 +23939,7 @@ ${errorDetails}`,
|
|
|
23738
23939
|
data = await this.applyVersionMapping(data, objectVersion, this.version);
|
|
23739
23940
|
}
|
|
23740
23941
|
data = await this.executeHooks("afterGet", data);
|
|
23741
|
-
this.
|
|
23942
|
+
this._emitWithDeprecation("get", "fetched", data, data.id);
|
|
23742
23943
|
const value = data;
|
|
23743
23944
|
return value;
|
|
23744
23945
|
}
|
|
@@ -23989,19 +24190,19 @@ ${errorDetails}`,
|
|
|
23989
24190
|
for (const hook of nonPartitionHooks) {
|
|
23990
24191
|
finalResult = await hook(finalResult);
|
|
23991
24192
|
}
|
|
23992
|
-
this.
|
|
24193
|
+
this._emitStandardized("updated", {
|
|
23993
24194
|
...updatedData,
|
|
23994
24195
|
$before: { ...originalData },
|
|
23995
24196
|
$after: { ...finalResult }
|
|
23996
|
-
});
|
|
24197
|
+
}, updatedData.id);
|
|
23997
24198
|
return finalResult;
|
|
23998
24199
|
} else {
|
|
23999
24200
|
const finalResult = await this.executeHooks("afterUpdate", updatedData);
|
|
24000
|
-
this.
|
|
24201
|
+
this._emitStandardized("updated", {
|
|
24001
24202
|
...updatedData,
|
|
24002
24203
|
$before: { ...originalData },
|
|
24003
24204
|
$after: { ...finalResult }
|
|
24004
|
-
});
|
|
24205
|
+
}, updatedData.id);
|
|
24005
24206
|
return finalResult;
|
|
24006
24207
|
}
|
|
24007
24208
|
}
|
|
@@ -24400,11 +24601,11 @@ ${errorDetails}`,
|
|
|
24400
24601
|
for (const hook of nonPartitionHooks) {
|
|
24401
24602
|
finalResult = await hook(finalResult);
|
|
24402
24603
|
}
|
|
24403
|
-
this.
|
|
24604
|
+
this._emitStandardized("updated", {
|
|
24404
24605
|
...updatedData,
|
|
24405
24606
|
$before: { ...originalData },
|
|
24406
24607
|
$after: { ...finalResult }
|
|
24407
|
-
});
|
|
24608
|
+
}, updatedData.id);
|
|
24408
24609
|
return {
|
|
24409
24610
|
success: true,
|
|
24410
24611
|
data: finalResult,
|
|
@@ -24413,11 +24614,11 @@ ${errorDetails}`,
|
|
|
24413
24614
|
} else {
|
|
24414
24615
|
await this.handlePartitionReferenceUpdates(oldData, newData);
|
|
24415
24616
|
const finalResult = await this.executeHooks("afterUpdate", updatedData);
|
|
24416
|
-
this.
|
|
24617
|
+
this._emitStandardized("updated", {
|
|
24417
24618
|
...updatedData,
|
|
24418
24619
|
$before: { ...originalData },
|
|
24419
24620
|
$after: { ...finalResult }
|
|
24420
|
-
});
|
|
24621
|
+
}, updatedData.id);
|
|
24421
24622
|
return {
|
|
24422
24623
|
success: true,
|
|
24423
24624
|
data: finalResult,
|
|
@@ -24448,11 +24649,11 @@ ${errorDetails}`,
|
|
|
24448
24649
|
await this.executeHooks("beforeDelete", objectData);
|
|
24449
24650
|
const key = this.getResourceKey(id);
|
|
24450
24651
|
const [ok2, err2, response] = await tryFn(() => this.client.deleteObject(key));
|
|
24451
|
-
this.
|
|
24652
|
+
this._emitWithDeprecation("delete", "deleted", {
|
|
24452
24653
|
...objectData,
|
|
24453
24654
|
$before: { ...objectData },
|
|
24454
24655
|
$after: null
|
|
24455
|
-
});
|
|
24656
|
+
}, id);
|
|
24456
24657
|
if (deleteError) {
|
|
24457
24658
|
throw mapAwsError(deleteError, {
|
|
24458
24659
|
bucket: this.client.config.bucket,
|
|
@@ -24576,7 +24777,7 @@ ${errorDetails}`,
|
|
|
24576
24777
|
}
|
|
24577
24778
|
const count = await this.client.count({ prefix });
|
|
24578
24779
|
await this.executeHooks("afterCount", { count, partition, partitionValues });
|
|
24579
|
-
this.
|
|
24780
|
+
this._emitWithDeprecation("count", "counted", count);
|
|
24580
24781
|
return count;
|
|
24581
24782
|
}
|
|
24582
24783
|
/**
|
|
@@ -24599,7 +24800,7 @@ ${errorDetails}`,
|
|
|
24599
24800
|
const result = await this.insert(attributes);
|
|
24600
24801
|
return result;
|
|
24601
24802
|
});
|
|
24602
|
-
this.
|
|
24803
|
+
this._emitWithDeprecation("insertMany", "inserted-many", objects.length);
|
|
24603
24804
|
return results;
|
|
24604
24805
|
}
|
|
24605
24806
|
/**
|
|
@@ -24634,7 +24835,7 @@ ${errorDetails}`,
|
|
|
24634
24835
|
return response;
|
|
24635
24836
|
});
|
|
24636
24837
|
await this.executeHooks("afterDeleteMany", { ids, results });
|
|
24637
|
-
this.
|
|
24838
|
+
this._emitWithDeprecation("deleteMany", "deleted-many", ids.length);
|
|
24638
24839
|
return results;
|
|
24639
24840
|
}
|
|
24640
24841
|
async deleteAll() {
|
|
@@ -24643,7 +24844,7 @@ ${errorDetails}`,
|
|
|
24643
24844
|
}
|
|
24644
24845
|
const prefix = `resource=${this.name}/data`;
|
|
24645
24846
|
const deletedCount = await this.client.deleteAll({ prefix });
|
|
24646
|
-
this.
|
|
24847
|
+
this._emitWithDeprecation("deleteAll", "deleted-all", {
|
|
24647
24848
|
version: this.version,
|
|
24648
24849
|
prefix,
|
|
24649
24850
|
deletedCount
|
|
@@ -24660,7 +24861,7 @@ ${errorDetails}`,
|
|
|
24660
24861
|
}
|
|
24661
24862
|
const prefix = `resource=${this.name}`;
|
|
24662
24863
|
const deletedCount = await this.client.deleteAll({ prefix });
|
|
24663
|
-
this.
|
|
24864
|
+
this._emitWithDeprecation("deleteAllData", "deleted-all-data", {
|
|
24664
24865
|
resource: this.name,
|
|
24665
24866
|
prefix,
|
|
24666
24867
|
deletedCount
|
|
@@ -24730,7 +24931,7 @@ ${errorDetails}`,
|
|
|
24730
24931
|
const idPart = parts.find((part) => part.startsWith("id="));
|
|
24731
24932
|
return idPart ? idPart.replace("id=", "") : null;
|
|
24732
24933
|
}).filter(Boolean);
|
|
24733
|
-
this.
|
|
24934
|
+
this._emitWithDeprecation("listIds", "listed-ids", ids.length);
|
|
24734
24935
|
return ids;
|
|
24735
24936
|
}
|
|
24736
24937
|
/**
|
|
@@ -24772,12 +24973,12 @@ ${errorDetails}`,
|
|
|
24772
24973
|
const [ok, err, ids] = await tryFn(() => this.listIds({ limit, offset }));
|
|
24773
24974
|
if (!ok) throw err;
|
|
24774
24975
|
const results = await this.processListResults(ids, "main");
|
|
24775
|
-
this.
|
|
24976
|
+
this._emitWithDeprecation("list", "listed", { count: results.length, errors: 0 });
|
|
24776
24977
|
return results;
|
|
24777
24978
|
}
|
|
24778
24979
|
async listPartition({ partition, partitionValues, limit, offset = 0 }) {
|
|
24779
24980
|
if (!this.config.partitions?.[partition]) {
|
|
24780
|
-
this.
|
|
24981
|
+
this._emitWithDeprecation("list", "listed", { partition, partitionValues, count: 0, errors: 0 });
|
|
24781
24982
|
return [];
|
|
24782
24983
|
}
|
|
24783
24984
|
const partitionDef = this.config.partitions[partition];
|
|
@@ -24787,7 +24988,7 @@ ${errorDetails}`,
|
|
|
24787
24988
|
const ids = this.extractIdsFromKeys(keys).slice(offset);
|
|
24788
24989
|
const filteredIds = limit ? ids.slice(0, limit) : ids;
|
|
24789
24990
|
const results = await this.processPartitionResults(filteredIds, partition, partitionDef, keys);
|
|
24790
|
-
this.
|
|
24991
|
+
this._emitWithDeprecation("list", "listed", { partition, partitionValues, count: results.length, errors: 0 });
|
|
24791
24992
|
return results;
|
|
24792
24993
|
}
|
|
24793
24994
|
/**
|
|
@@ -24832,7 +25033,7 @@ ${errorDetails}`,
|
|
|
24832
25033
|
}
|
|
24833
25034
|
return this.handleResourceError(err, id, context);
|
|
24834
25035
|
});
|
|
24835
|
-
this.
|
|
25036
|
+
this._emitWithDeprecation("list", "listed", { count: results.length, errors: 0 });
|
|
24836
25037
|
return results;
|
|
24837
25038
|
}
|
|
24838
25039
|
/**
|
|
@@ -24895,10 +25096,10 @@ ${errorDetails}`,
|
|
|
24895
25096
|
*/
|
|
24896
25097
|
handleListError(error, { partition, partitionValues }) {
|
|
24897
25098
|
if (error.message.includes("Partition '") && error.message.includes("' not found")) {
|
|
24898
|
-
this.
|
|
25099
|
+
this._emitWithDeprecation("list", "listed", { partition, partitionValues, count: 0, errors: 1 });
|
|
24899
25100
|
return [];
|
|
24900
25101
|
}
|
|
24901
|
-
this.
|
|
25102
|
+
this._emitWithDeprecation("list", "listed", { partition, partitionValues, count: 0, errors: 1 });
|
|
24902
25103
|
return [];
|
|
24903
25104
|
}
|
|
24904
25105
|
/**
|
|
@@ -24931,7 +25132,7 @@ ${errorDetails}`,
|
|
|
24931
25132
|
throw err;
|
|
24932
25133
|
});
|
|
24933
25134
|
const finalResults = await this.executeHooks("afterGetMany", results);
|
|
24934
|
-
this.
|
|
25135
|
+
this._emitWithDeprecation("getMany", "fetched-many", ids.length);
|
|
24935
25136
|
return finalResults;
|
|
24936
25137
|
}
|
|
24937
25138
|
/**
|
|
@@ -25017,7 +25218,7 @@ ${errorDetails}`,
|
|
|
25017
25218
|
hasTotalItems: totalItems !== null
|
|
25018
25219
|
}
|
|
25019
25220
|
};
|
|
25020
|
-
this.
|
|
25221
|
+
this._emitWithDeprecation("page", "paginated", result2);
|
|
25021
25222
|
return result2;
|
|
25022
25223
|
});
|
|
25023
25224
|
if (ok) return result;
|
|
@@ -25087,7 +25288,7 @@ ${errorDetails}`,
|
|
|
25087
25288
|
contentType
|
|
25088
25289
|
}));
|
|
25089
25290
|
if (!ok2) throw err2;
|
|
25090
|
-
this.
|
|
25291
|
+
this._emitWithDeprecation("setContent", "content-set", { id, contentType, contentLength: buffer.length }, id);
|
|
25091
25292
|
return updatedData;
|
|
25092
25293
|
}
|
|
25093
25294
|
/**
|
|
@@ -25116,7 +25317,7 @@ ${errorDetails}`,
|
|
|
25116
25317
|
}
|
|
25117
25318
|
const buffer = Buffer.from(await response.Body.transformToByteArray());
|
|
25118
25319
|
const contentType = response.ContentType || null;
|
|
25119
|
-
this.
|
|
25320
|
+
this._emitWithDeprecation("content", "content-fetched", { id, contentLength: buffer.length, contentType }, id);
|
|
25120
25321
|
return {
|
|
25121
25322
|
buffer,
|
|
25122
25323
|
contentType
|
|
@@ -25148,7 +25349,7 @@ ${errorDetails}`,
|
|
|
25148
25349
|
metadata: existingMetadata
|
|
25149
25350
|
}));
|
|
25150
25351
|
if (!ok2) throw err2;
|
|
25151
|
-
this.
|
|
25352
|
+
this._emitWithDeprecation("deleteContent", "content-deleted", id, id);
|
|
25152
25353
|
return response;
|
|
25153
25354
|
}
|
|
25154
25355
|
/**
|
|
@@ -25460,7 +25661,7 @@ ${errorDetails}`,
|
|
|
25460
25661
|
const data = await this.get(id);
|
|
25461
25662
|
data._partition = partitionName;
|
|
25462
25663
|
data._partitionValues = partitionValues;
|
|
25463
|
-
this.
|
|
25664
|
+
this._emitWithDeprecation("getFromPartition", "partition-fetched", data, data.id);
|
|
25464
25665
|
return data;
|
|
25465
25666
|
}
|
|
25466
25667
|
/**
|
|
@@ -25869,7 +26070,7 @@ class Database extends EventEmitter {
|
|
|
25869
26070
|
})();
|
|
25870
26071
|
this.version = "1";
|
|
25871
26072
|
this.s3dbVersion = (() => {
|
|
25872
|
-
const [ok, err, version] = tryFn(() => true ? "13.1
|
|
26073
|
+
const [ok, err, version] = tryFn(() => true ? "13.2.1" : "latest");
|
|
25873
26074
|
return ok ? version : "latest";
|
|
25874
26075
|
})();
|
|
25875
26076
|
this._resourcesMap = {};
|
|
@@ -26039,12 +26240,12 @@ class Database extends EventEmitter {
|
|
|
26039
26240
|
}
|
|
26040
26241
|
}
|
|
26041
26242
|
if (definitionChanges.length > 0) {
|
|
26042
|
-
this.emit("
|
|
26243
|
+
this.emit("db:resource-definitions-changed", {
|
|
26043
26244
|
changes: definitionChanges,
|
|
26044
26245
|
metadata: this.savedMetadata
|
|
26045
26246
|
});
|
|
26046
26247
|
}
|
|
26047
|
-
this.emit("connected", /* @__PURE__ */ new Date());
|
|
26248
|
+
this.emit("db:connected", /* @__PURE__ */ new Date());
|
|
26048
26249
|
}
|
|
26049
26250
|
/**
|
|
26050
26251
|
* Detect changes in resource definitions compared to saved metadata
|
|
@@ -26258,7 +26459,7 @@ class Database extends EventEmitter {
|
|
|
26258
26459
|
if (index > -1) {
|
|
26259
26460
|
this.pluginList.splice(index, 1);
|
|
26260
26461
|
}
|
|
26261
|
-
this.emit("plugin
|
|
26462
|
+
this.emit("db:plugin:uninstalled", { name: pluginName, plugin });
|
|
26262
26463
|
}
|
|
26263
26464
|
async uploadMetadataFile() {
|
|
26264
26465
|
const metadata = {
|
|
@@ -26317,7 +26518,7 @@ class Database extends EventEmitter {
|
|
|
26317
26518
|
contentType: "application/json"
|
|
26318
26519
|
});
|
|
26319
26520
|
this.savedMetadata = metadata;
|
|
26320
|
-
this.emit("
|
|
26521
|
+
this.emit("db:metadata-uploaded", metadata);
|
|
26321
26522
|
}
|
|
26322
26523
|
blankMetadataStructure() {
|
|
26323
26524
|
return {
|
|
@@ -26574,7 +26775,7 @@ class Database extends EventEmitter {
|
|
|
26574
26775
|
body: JSON.stringify(metadata, null, 2),
|
|
26575
26776
|
contentType: "application/json"
|
|
26576
26777
|
});
|
|
26577
|
-
this.emit("
|
|
26778
|
+
this.emit("db:metadata-healed", { healingLog, metadata });
|
|
26578
26779
|
if (this.verbose) {
|
|
26579
26780
|
console.warn("S3DB: Successfully uploaded healed metadata");
|
|
26580
26781
|
}
|
|
@@ -26714,7 +26915,7 @@ class Database extends EventEmitter {
|
|
|
26714
26915
|
if (!existingVersionData || existingVersionData.hash !== newHash) {
|
|
26715
26916
|
await this.uploadMetadataFile();
|
|
26716
26917
|
}
|
|
26717
|
-
this.emit("
|
|
26918
|
+
this.emit("db:resource:updated", name);
|
|
26718
26919
|
return existingResource;
|
|
26719
26920
|
}
|
|
26720
26921
|
const existingMetadata = this.savedMetadata?.resources?.[name];
|
|
@@ -26751,7 +26952,7 @@ class Database extends EventEmitter {
|
|
|
26751
26952
|
this._applyMiddlewares(resource, middlewares);
|
|
26752
26953
|
}
|
|
26753
26954
|
await this.uploadMetadataFile();
|
|
26754
|
-
this.emit("
|
|
26955
|
+
this.emit("db:resource:created", name);
|
|
26755
26956
|
return resource;
|
|
26756
26957
|
}
|
|
26757
26958
|
/**
|
|
@@ -26915,7 +27116,7 @@ class Database extends EventEmitter {
|
|
|
26915
27116
|
if (this.client && typeof this.client.removeAllListeners === "function") {
|
|
26916
27117
|
this.client.removeAllListeners();
|
|
26917
27118
|
}
|
|
26918
|
-
await this.emit("disconnected", /* @__PURE__ */ new Date());
|
|
27119
|
+
await this.emit("db:disconnected", /* @__PURE__ */ new Date());
|
|
26919
27120
|
this.removeAllListeners();
|
|
26920
27121
|
if (this._exitListener && typeof process !== "undefined") {
|
|
26921
27122
|
process.off("exit", this._exitListener);
|
|
@@ -27027,7 +27228,7 @@ class Database extends EventEmitter {
|
|
|
27027
27228
|
for (const hook of hooks) {
|
|
27028
27229
|
const [ok, error] = await tryFn(() => hook({ database: this, ...context }));
|
|
27029
27230
|
if (!ok) {
|
|
27030
|
-
this.emit("
|
|
27231
|
+
this.emit("db:hook-error", { event, error, context });
|
|
27031
27232
|
if (this.strictHooks) {
|
|
27032
27233
|
throw new DatabaseError(`Hook execution failed for event '${event}': ${error.message}`, {
|
|
27033
27234
|
event,
|
|
@@ -28603,7 +28804,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28603
28804
|
if (this.config.verbose) {
|
|
28604
28805
|
console.warn(`[ReplicatorPlugin] Insert event failed for resource ${resource.name}: ${error.message}`);
|
|
28605
28806
|
}
|
|
28606
|
-
this.emit("error", { operation: "insert", error: error.message, resource: resource.name });
|
|
28807
|
+
this.emit("plg:replicator:error", { operation: "insert", error: error.message, resource: resource.name });
|
|
28607
28808
|
}
|
|
28608
28809
|
};
|
|
28609
28810
|
const updateHandler = async (data, beforeData) => {
|
|
@@ -28616,7 +28817,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28616
28817
|
if (this.config.verbose) {
|
|
28617
28818
|
console.warn(`[ReplicatorPlugin] Update event failed for resource ${resource.name}: ${error.message}`);
|
|
28618
28819
|
}
|
|
28619
|
-
this.emit("error", { operation: "update", error: error.message, resource: resource.name });
|
|
28820
|
+
this.emit("plg:replicator:error", { operation: "update", error: error.message, resource: resource.name });
|
|
28620
28821
|
}
|
|
28621
28822
|
};
|
|
28622
28823
|
const deleteHandler = async (data) => {
|
|
@@ -28627,7 +28828,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28627
28828
|
if (this.config.verbose) {
|
|
28628
28829
|
console.warn(`[ReplicatorPlugin] Delete event failed for resource ${resource.name}: ${error.message}`);
|
|
28629
28830
|
}
|
|
28630
|
-
this.emit("error", { operation: "delete", error: error.message, resource: resource.name });
|
|
28831
|
+
this.emit("plg:replicator:error", { operation: "delete", error: error.message, resource: resource.name });
|
|
28631
28832
|
}
|
|
28632
28833
|
};
|
|
28633
28834
|
this.eventHandlers.set(resource.name, {
|
|
@@ -28748,7 +28949,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28748
28949
|
if (this.config.verbose) {
|
|
28749
28950
|
console.warn(`[ReplicatorPlugin] Failed to log error for ${resourceName}: ${logError.message}`);
|
|
28750
28951
|
}
|
|
28751
|
-
this.emit("
|
|
28952
|
+
this.emit("plg:replicator:log-error", {
|
|
28752
28953
|
replicator: replicator.name || replicator.id,
|
|
28753
28954
|
resourceName,
|
|
28754
28955
|
operation,
|
|
@@ -28773,7 +28974,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28773
28974
|
() => replicator.replicate(resourceName, operation, data, recordId, beforeData),
|
|
28774
28975
|
this.config.maxRetries
|
|
28775
28976
|
);
|
|
28776
|
-
this.emit("replicated", {
|
|
28977
|
+
this.emit("plg:replicator:replicated", {
|
|
28777
28978
|
replicator: replicator.name || replicator.id,
|
|
28778
28979
|
resourceName,
|
|
28779
28980
|
operation,
|
|
@@ -28789,7 +28990,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28789
28990
|
if (this.config.verbose) {
|
|
28790
28991
|
console.warn(`[ReplicatorPlugin] Replication failed for ${replicator.name || replicator.id} on ${resourceName}: ${error.message}`);
|
|
28791
28992
|
}
|
|
28792
|
-
this.emit("
|
|
28993
|
+
this.emit("plg:replicator:error", {
|
|
28793
28994
|
replicator: replicator.name || replicator.id,
|
|
28794
28995
|
resourceName,
|
|
28795
28996
|
operation,
|
|
@@ -28821,7 +29022,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28821
29022
|
if (this.config.verbose) {
|
|
28822
29023
|
console.warn(`[ReplicatorPlugin] Replicator item processing failed for ${replicator.name || replicator.id} on ${item.resourceName}: ${err.message}`);
|
|
28823
29024
|
}
|
|
28824
|
-
this.emit("
|
|
29025
|
+
this.emit("plg:replicator:error", {
|
|
28825
29026
|
replicator: replicator.name || replicator.id,
|
|
28826
29027
|
resourceName: item.resourceName,
|
|
28827
29028
|
operation: item.operation,
|
|
@@ -28833,7 +29034,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28833
29034
|
}
|
|
28834
29035
|
return { success: false, error: err.message };
|
|
28835
29036
|
}
|
|
28836
|
-
this.emit("replicated", {
|
|
29037
|
+
this.emit("plg:replicator:replicated", {
|
|
28837
29038
|
replicator: replicator.name || replicator.id,
|
|
28838
29039
|
resourceName: item.resourceName,
|
|
28839
29040
|
operation: item.operation,
|
|
@@ -28849,7 +29050,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28849
29050
|
if (this.config.verbose) {
|
|
28850
29051
|
console.warn(`[ReplicatorPlugin] Wrapper processing failed for ${replicator.name || replicator.id} on ${item.resourceName}: ${wrapperError.message}`);
|
|
28851
29052
|
}
|
|
28852
|
-
this.emit("
|
|
29053
|
+
this.emit("plg:replicator:error", {
|
|
28853
29054
|
replicator: replicator.name || replicator.id,
|
|
28854
29055
|
resourceName: item.resourceName,
|
|
28855
29056
|
operation: item.operation,
|
|
@@ -28867,7 +29068,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28867
29068
|
async logReplicator(item) {
|
|
28868
29069
|
const logRes = this.replicatorLog || this.database.resources[normalizeResourceName(this.config.replicatorLogResource)];
|
|
28869
29070
|
if (!logRes) {
|
|
28870
|
-
this.emit("replicator
|
|
29071
|
+
this.emit("plg:replicator:log-failed", { error: "replicator log resource not found", item });
|
|
28871
29072
|
return;
|
|
28872
29073
|
}
|
|
28873
29074
|
const logItem = {
|
|
@@ -28885,7 +29086,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28885
29086
|
if (this.config.verbose) {
|
|
28886
29087
|
console.warn(`[ReplicatorPlugin] Failed to log replicator item: ${err.message}`);
|
|
28887
29088
|
}
|
|
28888
|
-
this.emit("replicator
|
|
29089
|
+
this.emit("plg:replicator:log-failed", { error: err, item });
|
|
28889
29090
|
}
|
|
28890
29091
|
}
|
|
28891
29092
|
async updateReplicatorLog(logId, updates) {
|
|
@@ -28897,7 +29098,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28897
29098
|
});
|
|
28898
29099
|
});
|
|
28899
29100
|
if (!ok) {
|
|
28900
|
-
this.emit("replicator
|
|
29101
|
+
this.emit("plg:replicator:update-log-failed", { error: err.message, logId, updates });
|
|
28901
29102
|
}
|
|
28902
29103
|
}
|
|
28903
29104
|
// Utility methods
|
|
@@ -28981,7 +29182,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28981
29182
|
for (const resourceName in this.database.resources) {
|
|
28982
29183
|
if (normalizeResourceName(resourceName) === normalizeResourceName("plg_replicator_logs")) continue;
|
|
28983
29184
|
if (replicator.shouldReplicateResource(resourceName)) {
|
|
28984
|
-
this.emit("replicator
|
|
29185
|
+
this.emit("plg:replicator:sync-resource", { resourceName, replicatorId });
|
|
28985
29186
|
const resource = this.database.resources[resourceName];
|
|
28986
29187
|
let offset = 0;
|
|
28987
29188
|
const pageSize = this.config.batchSize || 100;
|
|
@@ -28997,7 +29198,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28997
29198
|
}
|
|
28998
29199
|
}
|
|
28999
29200
|
}
|
|
29000
|
-
this.emit("replicator
|
|
29201
|
+
this.emit("plg:replicator:sync-completed", { replicatorId, stats: this.stats });
|
|
29001
29202
|
}
|
|
29002
29203
|
async stop() {
|
|
29003
29204
|
const [ok, error] = await tryFn(async () => {
|
|
@@ -29012,7 +29213,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
29012
29213
|
if (this.config.verbose) {
|
|
29013
29214
|
console.warn(`[ReplicatorPlugin] Failed to stop replicator ${replicator.name || replicator.id}: ${replicatorError.message}`);
|
|
29014
29215
|
}
|
|
29015
|
-
this.emit("
|
|
29216
|
+
this.emit("plg:replicator:stop-error", {
|
|
29016
29217
|
replicator: replicator.name || replicator.id || "unknown",
|
|
29017
29218
|
driver: replicator.driver || "unknown",
|
|
29018
29219
|
error: replicatorError.message
|
|
@@ -29043,7 +29244,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
29043
29244
|
if (this.config.verbose) {
|
|
29044
29245
|
console.warn(`[ReplicatorPlugin] Failed to stop plugin: ${error.message}`);
|
|
29045
29246
|
}
|
|
29046
|
-
this.emit("
|
|
29247
|
+
this.emit("plg:replicator:plugin-stop-error", {
|
|
29047
29248
|
error: error.message
|
|
29048
29249
|
});
|
|
29049
29250
|
}
|
|
@@ -29200,7 +29401,7 @@ class S3QueuePlugin extends Plugin {
|
|
|
29200
29401
|
if (this.config.verbose) {
|
|
29201
29402
|
console.log(`[S3QueuePlugin] Started ${concurrency} workers`);
|
|
29202
29403
|
}
|
|
29203
|
-
this.emit("workers
|
|
29404
|
+
this.emit("plg:s3-queue:workers-started", { concurrency, workerId: this.workerId });
|
|
29204
29405
|
}
|
|
29205
29406
|
async stopProcessing() {
|
|
29206
29407
|
if (!this.isRunning) return;
|
|
@@ -29215,7 +29416,7 @@ class S3QueuePlugin extends Plugin {
|
|
|
29215
29416
|
if (this.config.verbose) {
|
|
29216
29417
|
console.log("[S3QueuePlugin] Stopped all workers");
|
|
29217
29418
|
}
|
|
29218
|
-
this.emit("workers
|
|
29419
|
+
this.emit("plg:s3-queue:workers-stopped", { workerId: this.workerId });
|
|
29219
29420
|
}
|
|
29220
29421
|
createWorker(handler, workerIndex) {
|
|
29221
29422
|
return (async () => {
|
|
@@ -29383,7 +29584,7 @@ class S3QueuePlugin extends Plugin {
|
|
|
29383
29584
|
});
|
|
29384
29585
|
await this.completeMessage(message.queueId, result);
|
|
29385
29586
|
const duration = Date.now() - startTime;
|
|
29386
|
-
this.emit("message
|
|
29587
|
+
this.emit("plg:s3-queue:message-completed", {
|
|
29387
29588
|
queueId: message.queueId,
|
|
29388
29589
|
originalId: message.record.id,
|
|
29389
29590
|
duration,
|
|
@@ -29396,7 +29597,7 @@ class S3QueuePlugin extends Plugin {
|
|
|
29396
29597
|
const shouldRetry = message.attempts < message.maxAttempts;
|
|
29397
29598
|
if (shouldRetry) {
|
|
29398
29599
|
await this.retryMessage(message.queueId, message.attempts, error.message);
|
|
29399
|
-
this.emit("message
|
|
29600
|
+
this.emit("plg:s3-queue:message-retry", {
|
|
29400
29601
|
queueId: message.queueId,
|
|
29401
29602
|
originalId: message.record.id,
|
|
29402
29603
|
attempts: message.attempts,
|
|
@@ -29404,7 +29605,7 @@ class S3QueuePlugin extends Plugin {
|
|
|
29404
29605
|
});
|
|
29405
29606
|
} else {
|
|
29406
29607
|
await this.moveToDeadLetter(message.queueId, message.record, error.message);
|
|
29407
|
-
this.emit("message
|
|
29608
|
+
this.emit("plg:s3-queue:message-dead", {
|
|
29408
29609
|
queueId: message.queueId,
|
|
29409
29610
|
originalId: message.record.id,
|
|
29410
29611
|
error: error.message
|
|
@@ -29636,7 +29837,7 @@ class SchedulerPlugin extends Plugin {
|
|
|
29636
29837
|
});
|
|
29637
29838
|
}
|
|
29638
29839
|
await this._startScheduling();
|
|
29639
|
-
this.emit("initialized", { jobs: this.jobs.size });
|
|
29840
|
+
this.emit("db:plugin:initialized", { jobs: this.jobs.size });
|
|
29640
29841
|
}
|
|
29641
29842
|
async _createJobHistoryResource() {
|
|
29642
29843
|
const [ok] = await tryFn(() => this.database.createResource({
|
|
@@ -29774,7 +29975,7 @@ class SchedulerPlugin extends Plugin {
|
|
|
29774
29975
|
if (this.config.onJobStart) {
|
|
29775
29976
|
await this._executeHook(this.config.onJobStart, jobName, context);
|
|
29776
29977
|
}
|
|
29777
|
-
this.emit("
|
|
29978
|
+
this.emit("plg:scheduler:job-start", { jobName, executionId, startTime });
|
|
29778
29979
|
let attempt = 0;
|
|
29779
29980
|
let lastError = null;
|
|
29780
29981
|
let result = null;
|
|
@@ -29841,7 +30042,7 @@ class SchedulerPlugin extends Plugin {
|
|
|
29841
30042
|
} else if (status !== "success" && this.config.onJobError) {
|
|
29842
30043
|
await this._executeHook(this.config.onJobError, jobName, lastError, attempt);
|
|
29843
30044
|
}
|
|
29844
|
-
this.emit("
|
|
30045
|
+
this.emit("plg:scheduler:job-complete", {
|
|
29845
30046
|
jobName,
|
|
29846
30047
|
executionId,
|
|
29847
30048
|
status,
|
|
@@ -29927,7 +30128,7 @@ class SchedulerPlugin extends Plugin {
|
|
|
29927
30128
|
}
|
|
29928
30129
|
job.enabled = true;
|
|
29929
30130
|
this._scheduleNextExecution(jobName);
|
|
29930
|
-
this.emit("
|
|
30131
|
+
this.emit("plg:scheduler:job-enabled", { jobName });
|
|
29931
30132
|
}
|
|
29932
30133
|
/**
|
|
29933
30134
|
* Disable a job
|
|
@@ -29948,7 +30149,7 @@ class SchedulerPlugin extends Plugin {
|
|
|
29948
30149
|
clearTimeout(timer);
|
|
29949
30150
|
this.timers.delete(jobName);
|
|
29950
30151
|
}
|
|
29951
|
-
this.emit("
|
|
30152
|
+
this.emit("plg:scheduler:job-disabled", { jobName });
|
|
29952
30153
|
}
|
|
29953
30154
|
/**
|
|
29954
30155
|
* Get job status and statistics
|
|
@@ -30086,7 +30287,7 @@ class SchedulerPlugin extends Plugin {
|
|
|
30086
30287
|
if (job.enabled) {
|
|
30087
30288
|
this._scheduleNextExecution(jobName);
|
|
30088
30289
|
}
|
|
30089
|
-
this.emit("
|
|
30290
|
+
this.emit("plg:scheduler:job-added", { jobName });
|
|
30090
30291
|
}
|
|
30091
30292
|
/**
|
|
30092
30293
|
* Remove a job
|
|
@@ -30109,7 +30310,7 @@ class SchedulerPlugin extends Plugin {
|
|
|
30109
30310
|
this.jobs.delete(jobName);
|
|
30110
30311
|
this.statistics.delete(jobName);
|
|
30111
30312
|
this.activeJobs.delete(jobName);
|
|
30112
|
-
this.emit("
|
|
30313
|
+
this.emit("plg:scheduler:job-removed", { jobName });
|
|
30113
30314
|
}
|
|
30114
30315
|
/**
|
|
30115
30316
|
* Get plugin instance by name (for job actions that need other plugins)
|
|
@@ -30150,9 +30351,14 @@ class SchedulerPlugin extends Plugin {
|
|
|
30150
30351
|
}
|
|
30151
30352
|
}
|
|
30152
30353
|
|
|
30354
|
+
var scheduler_plugin = /*#__PURE__*/Object.freeze({
|
|
30355
|
+
__proto__: null,
|
|
30356
|
+
SchedulerPlugin: SchedulerPlugin
|
|
30357
|
+
});
|
|
30358
|
+
|
|
30153
30359
|
class StateMachineError extends S3dbError {
|
|
30154
30360
|
constructor(message, details = {}) {
|
|
30155
|
-
const { currentState, targetState, resourceName, operation = "unknown", ...rest } = details;
|
|
30361
|
+
const { currentState, targetState, resourceName, operation = "unknown", retriable, ...rest } = details;
|
|
30156
30362
|
let description = details.description;
|
|
30157
30363
|
if (!description) {
|
|
30158
30364
|
description = `
|
|
@@ -30177,6 +30383,158 @@ Docs: https://github.com/forattini-dev/s3db.js/blob/main/docs/plugins/state-mach
|
|
|
30177
30383
|
`.trim();
|
|
30178
30384
|
}
|
|
30179
30385
|
super(message, { ...rest, currentState, targetState, resourceName, operation, description });
|
|
30386
|
+
if (retriable !== void 0) {
|
|
30387
|
+
this.retriable = retriable;
|
|
30388
|
+
}
|
|
30389
|
+
}
|
|
30390
|
+
}
|
|
30391
|
+
|
|
30392
|
+
const RETRIABLE = "RETRIABLE";
|
|
30393
|
+
const NON_RETRIABLE = "NON_RETRIABLE";
|
|
30394
|
+
const RETRIABLE_NETWORK_CODES = /* @__PURE__ */ new Set([
|
|
30395
|
+
"ECONNREFUSED",
|
|
30396
|
+
"ETIMEDOUT",
|
|
30397
|
+
"ECONNRESET",
|
|
30398
|
+
"EPIPE",
|
|
30399
|
+
"ENOTFOUND",
|
|
30400
|
+
"NetworkError",
|
|
30401
|
+
"NETWORK_ERROR",
|
|
30402
|
+
"TimeoutError",
|
|
30403
|
+
"TIMEOUT"
|
|
30404
|
+
]);
|
|
30405
|
+
const RETRIABLE_AWS_CODES = /* @__PURE__ */ new Set([
|
|
30406
|
+
"ThrottlingException",
|
|
30407
|
+
"TooManyRequestsException",
|
|
30408
|
+
"RequestLimitExceeded",
|
|
30409
|
+
"ProvisionedThroughputExceededException",
|
|
30410
|
+
"RequestThrottledException",
|
|
30411
|
+
"SlowDown",
|
|
30412
|
+
"ServiceUnavailable"
|
|
30413
|
+
]);
|
|
30414
|
+
const RETRIABLE_AWS_CONFLICTS = /* @__PURE__ */ new Set([
|
|
30415
|
+
"ConditionalCheckFailedException",
|
|
30416
|
+
"TransactionConflictException"
|
|
30417
|
+
]);
|
|
30418
|
+
const RETRIABLE_STATUS_CODES = /* @__PURE__ */ new Set([
|
|
30419
|
+
429,
|
|
30420
|
+
// Too Many Requests
|
|
30421
|
+
500,
|
|
30422
|
+
// Internal Server Error
|
|
30423
|
+
502,
|
|
30424
|
+
// Bad Gateway
|
|
30425
|
+
503,
|
|
30426
|
+
// Service Unavailable
|
|
30427
|
+
504,
|
|
30428
|
+
// Gateway Timeout
|
|
30429
|
+
507,
|
|
30430
|
+
// Insufficient Storage
|
|
30431
|
+
509
|
|
30432
|
+
// Bandwidth Limit Exceeded
|
|
30433
|
+
]);
|
|
30434
|
+
const NON_RETRIABLE_ERROR_NAMES = /* @__PURE__ */ new Set([
|
|
30435
|
+
"ValidationError",
|
|
30436
|
+
"StateMachineError",
|
|
30437
|
+
"SchemaError",
|
|
30438
|
+
"AuthenticationError",
|
|
30439
|
+
"PermissionError",
|
|
30440
|
+
"BusinessLogicError",
|
|
30441
|
+
"InvalidStateTransition"
|
|
30442
|
+
]);
|
|
30443
|
+
const NON_RETRIABLE_STATUS_CODES = /* @__PURE__ */ new Set([
|
|
30444
|
+
400,
|
|
30445
|
+
// Bad Request
|
|
30446
|
+
401,
|
|
30447
|
+
// Unauthorized
|
|
30448
|
+
403,
|
|
30449
|
+
// Forbidden
|
|
30450
|
+
404,
|
|
30451
|
+
// Not Found
|
|
30452
|
+
405,
|
|
30453
|
+
// Method Not Allowed
|
|
30454
|
+
406,
|
|
30455
|
+
// Not Acceptable
|
|
30456
|
+
409,
|
|
30457
|
+
// Conflict
|
|
30458
|
+
410,
|
|
30459
|
+
// Gone
|
|
30460
|
+
422
|
|
30461
|
+
// Unprocessable Entity
|
|
30462
|
+
]);
|
|
30463
|
+
class ErrorClassifier {
|
|
30464
|
+
/**
|
|
30465
|
+
* Classify an error as RETRIABLE or NON_RETRIABLE
|
|
30466
|
+
*
|
|
30467
|
+
* @param {Error} error - The error to classify
|
|
30468
|
+
* @param {Object} options - Classification options
|
|
30469
|
+
* @param {Array<string>} options.retryableErrors - Custom retriable error names/codes
|
|
30470
|
+
* @param {Array<string>} options.nonRetriableErrors - Custom non-retriable error names/codes
|
|
30471
|
+
* @returns {string} 'RETRIABLE' or 'NON_RETRIABLE'
|
|
30472
|
+
*/
|
|
30473
|
+
static classify(error, options = {}) {
|
|
30474
|
+
if (!error) return NON_RETRIABLE;
|
|
30475
|
+
const {
|
|
30476
|
+
retryableErrors = [],
|
|
30477
|
+
nonRetriableErrors = []
|
|
30478
|
+
} = options;
|
|
30479
|
+
if (retryableErrors.length > 0) {
|
|
30480
|
+
const isCustomRetriable = retryableErrors.some(
|
|
30481
|
+
(errType) => error.code === errType || error.name === errType || error.message?.includes(errType)
|
|
30482
|
+
);
|
|
30483
|
+
if (isCustomRetriable) return RETRIABLE;
|
|
30484
|
+
}
|
|
30485
|
+
if (nonRetriableErrors.length > 0) {
|
|
30486
|
+
const isCustomNonRetriable = nonRetriableErrors.some(
|
|
30487
|
+
(errType) => error.code === errType || error.name === errType || error.message?.includes(errType)
|
|
30488
|
+
);
|
|
30489
|
+
if (isCustomNonRetriable) return NON_RETRIABLE;
|
|
30490
|
+
}
|
|
30491
|
+
if (error.retriable === false) return NON_RETRIABLE;
|
|
30492
|
+
if (error.retriable === true) return RETRIABLE;
|
|
30493
|
+
if (NON_RETRIABLE_ERROR_NAMES.has(error.name)) {
|
|
30494
|
+
return NON_RETRIABLE;
|
|
30495
|
+
}
|
|
30496
|
+
if (error.statusCode && NON_RETRIABLE_STATUS_CODES.has(error.statusCode)) {
|
|
30497
|
+
return NON_RETRIABLE;
|
|
30498
|
+
}
|
|
30499
|
+
if (error.code && RETRIABLE_NETWORK_CODES.has(error.code)) {
|
|
30500
|
+
return RETRIABLE;
|
|
30501
|
+
}
|
|
30502
|
+
if (error.code && RETRIABLE_AWS_CODES.has(error.code)) {
|
|
30503
|
+
return RETRIABLE;
|
|
30504
|
+
}
|
|
30505
|
+
if (error.code && RETRIABLE_AWS_CONFLICTS.has(error.code)) {
|
|
30506
|
+
return RETRIABLE;
|
|
30507
|
+
}
|
|
30508
|
+
if (error.statusCode && RETRIABLE_STATUS_CODES.has(error.statusCode)) {
|
|
30509
|
+
return RETRIABLE;
|
|
30510
|
+
}
|
|
30511
|
+
if (error.message && typeof error.message === "string") {
|
|
30512
|
+
const lowerMessage = error.message.toLowerCase();
|
|
30513
|
+
if (lowerMessage.includes("timeout") || lowerMessage.includes("timed out") || lowerMessage.includes("network") || lowerMessage.includes("connection")) {
|
|
30514
|
+
return RETRIABLE;
|
|
30515
|
+
}
|
|
30516
|
+
}
|
|
30517
|
+
return RETRIABLE;
|
|
30518
|
+
}
|
|
30519
|
+
/**
|
|
30520
|
+
* Check if an error is retriable
|
|
30521
|
+
*
|
|
30522
|
+
* @param {Error} error - The error to check
|
|
30523
|
+
* @param {Object} options - Classification options
|
|
30524
|
+
* @returns {boolean} true if retriable
|
|
30525
|
+
*/
|
|
30526
|
+
static isRetriable(error, options = {}) {
|
|
30527
|
+
return this.classify(error, options) === RETRIABLE;
|
|
30528
|
+
}
|
|
30529
|
+
/**
|
|
30530
|
+
* Check if an error is non-retriable
|
|
30531
|
+
*
|
|
30532
|
+
* @param {Error} error - The error to check
|
|
30533
|
+
* @param {Object} options - Classification options
|
|
30534
|
+
* @returns {boolean} true if non-retriable
|
|
30535
|
+
*/
|
|
30536
|
+
static isNonRetriable(error, options = {}) {
|
|
30537
|
+
return this.classify(error, options) === NON_RETRIABLE;
|
|
30180
30538
|
}
|
|
30181
30539
|
}
|
|
30182
30540
|
|
|
@@ -30197,11 +30555,23 @@ class StateMachinePlugin extends Plugin {
|
|
|
30197
30555
|
workerId: options.workerId || "default",
|
|
30198
30556
|
lockTimeout: options.lockTimeout || 1e3,
|
|
30199
30557
|
// Wait up to 1s for lock
|
|
30200
|
-
lockTTL: options.lockTTL || 5
|
|
30558
|
+
lockTTL: options.lockTTL || 5,
|
|
30201
30559
|
// Lock expires after 5s (prevent deadlock)
|
|
30560
|
+
// Global retry configuration for action execution
|
|
30561
|
+
retryConfig: options.retryConfig || null,
|
|
30562
|
+
// Trigger system configuration
|
|
30563
|
+
enableScheduler: options.enableScheduler || false,
|
|
30564
|
+
schedulerConfig: options.schedulerConfig || {},
|
|
30565
|
+
enableDateTriggers: options.enableDateTriggers !== false,
|
|
30566
|
+
enableFunctionTriggers: options.enableFunctionTriggers !== false,
|
|
30567
|
+
enableEventTriggers: options.enableEventTriggers !== false,
|
|
30568
|
+
triggerCheckInterval: options.triggerCheckInterval || 6e4
|
|
30569
|
+
// Check triggers every 60s by default
|
|
30202
30570
|
};
|
|
30203
30571
|
this.database = null;
|
|
30204
30572
|
this.machines = /* @__PURE__ */ new Map();
|
|
30573
|
+
this.triggerIntervals = [];
|
|
30574
|
+
this.schedulerPlugin = null;
|
|
30205
30575
|
this._validateConfiguration();
|
|
30206
30576
|
}
|
|
30207
30577
|
_validateConfiguration() {
|
|
@@ -30250,7 +30620,8 @@ class StateMachinePlugin extends Plugin {
|
|
|
30250
30620
|
// entityId -> currentState
|
|
30251
30621
|
});
|
|
30252
30622
|
}
|
|
30253
|
-
|
|
30623
|
+
await this._setupTriggers();
|
|
30624
|
+
this.emit("db:plugin:initialized", { machines: Array.from(this.machines.keys()) });
|
|
30254
30625
|
}
|
|
30255
30626
|
async _createStateResources() {
|
|
30256
30627
|
const [logOk] = await tryFn(() => this.database.createResource({
|
|
@@ -30281,6 +30652,8 @@ class StateMachinePlugin extends Plugin {
|
|
|
30281
30652
|
currentState: "string|required",
|
|
30282
30653
|
context: "json|default:{}",
|
|
30283
30654
|
lastTransition: "string|default:null",
|
|
30655
|
+
triggerCounts: "json|default:{}",
|
|
30656
|
+
// Track trigger execution counts
|
|
30284
30657
|
updatedAt: "string|required"
|
|
30285
30658
|
},
|
|
30286
30659
|
behavior: "body-overflow"
|
|
@@ -30344,7 +30717,7 @@ class StateMachinePlugin extends Plugin {
|
|
|
30344
30717
|
if (targetStateConfig && targetStateConfig.entry) {
|
|
30345
30718
|
await this._executeAction(targetStateConfig.entry, context, event, machineId, entityId);
|
|
30346
30719
|
}
|
|
30347
|
-
this.emit("transition", {
|
|
30720
|
+
this.emit("plg:state-machine:transition", {
|
|
30348
30721
|
machineId,
|
|
30349
30722
|
entityId,
|
|
30350
30723
|
from: currentState,
|
|
@@ -30370,14 +30743,97 @@ class StateMachinePlugin extends Plugin {
|
|
|
30370
30743
|
}
|
|
30371
30744
|
return;
|
|
30372
30745
|
}
|
|
30373
|
-
const
|
|
30374
|
-
|
|
30375
|
-
|
|
30376
|
-
|
|
30377
|
-
|
|
30378
|
-
|
|
30746
|
+
const machine = this.machines.get(machineId);
|
|
30747
|
+
const currentState = await this.getState(machineId, entityId);
|
|
30748
|
+
const stateConfig = machine?.config?.states?.[currentState];
|
|
30749
|
+
const retryConfig = {
|
|
30750
|
+
...this.config.retryConfig || {},
|
|
30751
|
+
...machine?.config?.retryConfig || {},
|
|
30752
|
+
...stateConfig?.retryConfig || {}
|
|
30753
|
+
};
|
|
30754
|
+
const maxAttempts = retryConfig.maxAttempts ?? 0;
|
|
30755
|
+
const retryEnabled = maxAttempts > 0;
|
|
30756
|
+
let attempt = 0;
|
|
30757
|
+
while (attempt <= maxAttempts) {
|
|
30758
|
+
try {
|
|
30759
|
+
const result = await action(context, event, { database: this.database, machineId, entityId });
|
|
30760
|
+
if (attempt > 0) {
|
|
30761
|
+
this.emit("plg:state-machine:action-retry-success", {
|
|
30762
|
+
machineId,
|
|
30763
|
+
entityId,
|
|
30764
|
+
action: actionName,
|
|
30765
|
+
attempts: attempt + 1,
|
|
30766
|
+
state: currentState
|
|
30767
|
+
});
|
|
30768
|
+
if (this.config.verbose) {
|
|
30769
|
+
console.log(`[StateMachinePlugin] Action '${actionName}' succeeded after ${attempt + 1} attempts`);
|
|
30770
|
+
}
|
|
30771
|
+
}
|
|
30772
|
+
return result;
|
|
30773
|
+
} catch (error) {
|
|
30774
|
+
if (!retryEnabled) {
|
|
30775
|
+
if (this.config.verbose) {
|
|
30776
|
+
console.error(`[StateMachinePlugin] Action '${actionName}' failed:`, error.message);
|
|
30777
|
+
}
|
|
30778
|
+
this.emit("plg:state-machine:action-error", { actionName, error: error.message, machineId, entityId });
|
|
30779
|
+
return;
|
|
30780
|
+
}
|
|
30781
|
+
const classification = ErrorClassifier.classify(error, {
|
|
30782
|
+
retryableErrors: retryConfig.retryableErrors,
|
|
30783
|
+
nonRetriableErrors: retryConfig.nonRetriableErrors
|
|
30784
|
+
});
|
|
30785
|
+
if (classification === "NON_RETRIABLE") {
|
|
30786
|
+
this.emit("plg:state-machine:action-error-non-retriable", {
|
|
30787
|
+
machineId,
|
|
30788
|
+
entityId,
|
|
30789
|
+
action: actionName,
|
|
30790
|
+
error: error.message,
|
|
30791
|
+
state: currentState
|
|
30792
|
+
});
|
|
30793
|
+
if (this.config.verbose) {
|
|
30794
|
+
console.error(`[StateMachinePlugin] Action '${actionName}' failed with non-retriable error:`, error.message);
|
|
30795
|
+
}
|
|
30796
|
+
throw error;
|
|
30797
|
+
}
|
|
30798
|
+
if (attempt >= maxAttempts) {
|
|
30799
|
+
this.emit("plg:state-machine:action-retry-exhausted", {
|
|
30800
|
+
machineId,
|
|
30801
|
+
entityId,
|
|
30802
|
+
action: actionName,
|
|
30803
|
+
attempts: attempt + 1,
|
|
30804
|
+
error: error.message,
|
|
30805
|
+
state: currentState
|
|
30806
|
+
});
|
|
30807
|
+
if (this.config.verbose) {
|
|
30808
|
+
console.error(`[StateMachinePlugin] Action '${actionName}' failed after ${attempt + 1} attempts:`, error.message);
|
|
30809
|
+
}
|
|
30810
|
+
throw error;
|
|
30811
|
+
}
|
|
30812
|
+
attempt++;
|
|
30813
|
+
const delay = this._calculateBackoff(attempt, retryConfig);
|
|
30814
|
+
if (retryConfig.onRetry) {
|
|
30815
|
+
try {
|
|
30816
|
+
await retryConfig.onRetry(attempt, error, context);
|
|
30817
|
+
} catch (hookError) {
|
|
30818
|
+
if (this.config.verbose) {
|
|
30819
|
+
console.warn(`[StateMachinePlugin] onRetry hook failed:`, hookError.message);
|
|
30820
|
+
}
|
|
30821
|
+
}
|
|
30822
|
+
}
|
|
30823
|
+
this.emit("plg:state-machine:action-retry-attempt", {
|
|
30824
|
+
machineId,
|
|
30825
|
+
entityId,
|
|
30826
|
+
action: actionName,
|
|
30827
|
+
attempt,
|
|
30828
|
+
delay,
|
|
30829
|
+
error: error.message,
|
|
30830
|
+
state: currentState
|
|
30831
|
+
});
|
|
30832
|
+
if (this.config.verbose) {
|
|
30833
|
+
console.warn(`[StateMachinePlugin] Action '${actionName}' failed (attempt ${attempt + 1}/${maxAttempts + 1}), retrying in ${delay}ms:`, error.message);
|
|
30834
|
+
}
|
|
30835
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
30379
30836
|
}
|
|
30380
|
-
this.emit("action_error", { actionName, error: error.message, machineId, entityId });
|
|
30381
30837
|
}
|
|
30382
30838
|
}
|
|
30383
30839
|
async _transition(machineId, entityId, fromState, toState, event, context) {
|
|
@@ -30475,6 +30931,27 @@ class StateMachinePlugin extends Plugin {
|
|
|
30475
30931
|
console.warn(`[StateMachinePlugin] Failed to release lock '${lockName}':`, err.message);
|
|
30476
30932
|
}
|
|
30477
30933
|
}
|
|
30934
|
+
/**
|
|
30935
|
+
* Calculate backoff delay for retry attempts
|
|
30936
|
+
* @private
|
|
30937
|
+
*/
|
|
30938
|
+
_calculateBackoff(attempt, retryConfig) {
|
|
30939
|
+
const {
|
|
30940
|
+
backoffStrategy = "exponential",
|
|
30941
|
+
baseDelay = 1e3,
|
|
30942
|
+
maxDelay = 3e4
|
|
30943
|
+
} = retryConfig || {};
|
|
30944
|
+
let delay;
|
|
30945
|
+
if (backoffStrategy === "exponential") {
|
|
30946
|
+
delay = Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay);
|
|
30947
|
+
} else if (backoffStrategy === "linear") {
|
|
30948
|
+
delay = Math.min(baseDelay * attempt, maxDelay);
|
|
30949
|
+
} else {
|
|
30950
|
+
delay = baseDelay;
|
|
30951
|
+
}
|
|
30952
|
+
const jitter = delay * 0.2 * (Math.random() - 0.5);
|
|
30953
|
+
return Math.round(delay + jitter);
|
|
30954
|
+
}
|
|
30478
30955
|
/**
|
|
30479
30956
|
* Get current state for an entity
|
|
30480
30957
|
*/
|
|
@@ -30604,7 +31081,7 @@ class StateMachinePlugin extends Plugin {
|
|
|
30604
31081
|
if (initialStateConfig && initialStateConfig.entry) {
|
|
30605
31082
|
await this._executeAction(initialStateConfig.entry, context, "INIT", machineId, entityId);
|
|
30606
31083
|
}
|
|
30607
|
-
this.emit("
|
|
31084
|
+
this.emit("plg:state-machine:entity-initialized", { machineId, entityId, initialState });
|
|
30608
31085
|
return initialState;
|
|
30609
31086
|
}
|
|
30610
31087
|
/**
|
|
@@ -30661,12 +31138,343 @@ class StateMachinePlugin extends Plugin {
|
|
|
30661
31138
|
`;
|
|
30662
31139
|
return dot;
|
|
30663
31140
|
}
|
|
31141
|
+
/**
|
|
31142
|
+
* Get all entities currently in a specific state
|
|
31143
|
+
* @private
|
|
31144
|
+
*/
|
|
31145
|
+
async _getEntitiesInState(machineId, stateName) {
|
|
31146
|
+
if (!this.config.persistTransitions) {
|
|
31147
|
+
const machine = this.machines.get(machineId);
|
|
31148
|
+
if (!machine) return [];
|
|
31149
|
+
const entities = [];
|
|
31150
|
+
for (const [entityId, currentState] of machine.currentStates) {
|
|
31151
|
+
if (currentState === stateName) {
|
|
31152
|
+
entities.push({ entityId, currentState, context: {}, triggerCounts: {} });
|
|
31153
|
+
}
|
|
31154
|
+
}
|
|
31155
|
+
return entities;
|
|
31156
|
+
}
|
|
31157
|
+
const [ok, err, records] = await tryFn(
|
|
31158
|
+
() => this.database.resources[this.config.stateResource].query({
|
|
31159
|
+
machineId,
|
|
31160
|
+
currentState: stateName
|
|
31161
|
+
})
|
|
31162
|
+
);
|
|
31163
|
+
if (!ok) {
|
|
31164
|
+
if (this.config.verbose) {
|
|
31165
|
+
console.warn(`[StateMachinePlugin] Failed to query entities in state '${stateName}':`, err.message);
|
|
31166
|
+
}
|
|
31167
|
+
return [];
|
|
31168
|
+
}
|
|
31169
|
+
return records || [];
|
|
31170
|
+
}
|
|
31171
|
+
/**
|
|
31172
|
+
* Increment trigger execution count for an entity
|
|
31173
|
+
* @private
|
|
31174
|
+
*/
|
|
31175
|
+
async _incrementTriggerCount(machineId, entityId, triggerName) {
|
|
31176
|
+
if (!this.config.persistTransitions) {
|
|
31177
|
+
return;
|
|
31178
|
+
}
|
|
31179
|
+
const stateId = `${machineId}_${entityId}`;
|
|
31180
|
+
const [ok, err, stateRecord] = await tryFn(
|
|
31181
|
+
() => this.database.resources[this.config.stateResource].get(stateId)
|
|
31182
|
+
);
|
|
31183
|
+
if (ok && stateRecord) {
|
|
31184
|
+
const triggerCounts = stateRecord.triggerCounts || {};
|
|
31185
|
+
triggerCounts[triggerName] = (triggerCounts[triggerName] || 0) + 1;
|
|
31186
|
+
await tryFn(
|
|
31187
|
+
() => this.database.resources[this.config.stateResource].patch(stateId, { triggerCounts })
|
|
31188
|
+
);
|
|
31189
|
+
}
|
|
31190
|
+
}
|
|
31191
|
+
/**
|
|
31192
|
+
* Setup trigger system for all state machines
|
|
31193
|
+
* @private
|
|
31194
|
+
*/
|
|
31195
|
+
async _setupTriggers() {
|
|
31196
|
+
if (!this.config.enableScheduler && !this.config.enableDateTriggers && !this.config.enableFunctionTriggers && !this.config.enableEventTriggers) {
|
|
31197
|
+
return;
|
|
31198
|
+
}
|
|
31199
|
+
const cronJobs = {};
|
|
31200
|
+
for (const [machineId, machineData] of this.machines) {
|
|
31201
|
+
const machineConfig = machineData.config;
|
|
31202
|
+
for (const [stateName, stateConfig] of Object.entries(machineConfig.states)) {
|
|
31203
|
+
const triggers = stateConfig.triggers || [];
|
|
31204
|
+
for (let i = 0; i < triggers.length; i++) {
|
|
31205
|
+
const trigger = triggers[i];
|
|
31206
|
+
const triggerName = `${trigger.action}_${i}`;
|
|
31207
|
+
if (trigger.type === "cron" && this.config.enableScheduler) {
|
|
31208
|
+
const jobName = `${machineId}_${stateName}_${triggerName}`;
|
|
31209
|
+
cronJobs[jobName] = await this._createCronJob(machineId, stateName, trigger, triggerName);
|
|
31210
|
+
} else if (trigger.type === "date" && this.config.enableDateTriggers) {
|
|
31211
|
+
await this._setupDateTrigger(machineId, stateName, trigger, triggerName);
|
|
31212
|
+
} else if (trigger.type === "function" && this.config.enableFunctionTriggers) {
|
|
31213
|
+
await this._setupFunctionTrigger(machineId, stateName, trigger, triggerName);
|
|
31214
|
+
} else if (trigger.type === "event" && this.config.enableEventTriggers) {
|
|
31215
|
+
await this._setupEventTrigger(machineId, stateName, trigger, triggerName);
|
|
31216
|
+
}
|
|
31217
|
+
}
|
|
31218
|
+
}
|
|
31219
|
+
}
|
|
31220
|
+
if (Object.keys(cronJobs).length > 0 && this.config.enableScheduler) {
|
|
31221
|
+
const { SchedulerPlugin } = await Promise.resolve().then(function () { return scheduler_plugin; });
|
|
31222
|
+
this.schedulerPlugin = new SchedulerPlugin({
|
|
31223
|
+
jobs: cronJobs,
|
|
31224
|
+
persistJobs: false,
|
|
31225
|
+
// Don't persist trigger jobs
|
|
31226
|
+
verbose: this.config.verbose,
|
|
31227
|
+
...this.config.schedulerConfig
|
|
31228
|
+
});
|
|
31229
|
+
await this.database.usePlugin(this.schedulerPlugin);
|
|
31230
|
+
if (this.config.verbose) {
|
|
31231
|
+
console.log(`[StateMachinePlugin] Installed SchedulerPlugin with ${Object.keys(cronJobs).length} cron triggers`);
|
|
31232
|
+
}
|
|
31233
|
+
}
|
|
31234
|
+
}
|
|
31235
|
+
/**
|
|
31236
|
+
* Create a SchedulerPlugin job for a cron trigger
|
|
31237
|
+
* @private
|
|
31238
|
+
*/
|
|
31239
|
+
async _createCronJob(machineId, stateName, trigger, triggerName) {
|
|
31240
|
+
return {
|
|
31241
|
+
schedule: trigger.schedule,
|
|
31242
|
+
description: `Trigger '${triggerName}' for ${machineId}.${stateName}`,
|
|
31243
|
+
action: async (database, context) => {
|
|
31244
|
+
const entities = await this._getEntitiesInState(machineId, stateName);
|
|
31245
|
+
let executedCount = 0;
|
|
31246
|
+
for (const entity of entities) {
|
|
31247
|
+
try {
|
|
31248
|
+
if (trigger.condition) {
|
|
31249
|
+
const shouldTrigger = await trigger.condition(entity.context, entity.entityId);
|
|
31250
|
+
if (!shouldTrigger) continue;
|
|
31251
|
+
}
|
|
31252
|
+
if (trigger.maxTriggers !== void 0) {
|
|
31253
|
+
const triggerCount = entity.triggerCounts?.[triggerName] || 0;
|
|
31254
|
+
if (triggerCount >= trigger.maxTriggers) {
|
|
31255
|
+
if (trigger.onMaxTriggersReached) {
|
|
31256
|
+
await this.send(machineId, entity.entityId, trigger.onMaxTriggersReached, entity.context);
|
|
31257
|
+
}
|
|
31258
|
+
continue;
|
|
31259
|
+
}
|
|
31260
|
+
}
|
|
31261
|
+
const result = await this._executeAction(
|
|
31262
|
+
trigger.action,
|
|
31263
|
+
entity.context,
|
|
31264
|
+
"TRIGGER",
|
|
31265
|
+
machineId,
|
|
31266
|
+
entity.entityId
|
|
31267
|
+
);
|
|
31268
|
+
await this._incrementTriggerCount(machineId, entity.entityId, triggerName);
|
|
31269
|
+
executedCount++;
|
|
31270
|
+
if (trigger.eventOnSuccess) {
|
|
31271
|
+
await this.send(machineId, entity.entityId, trigger.eventOnSuccess, {
|
|
31272
|
+
...entity.context,
|
|
31273
|
+
triggerResult: result
|
|
31274
|
+
});
|
|
31275
|
+
} else if (trigger.event) {
|
|
31276
|
+
await this.send(machineId, entity.entityId, trigger.event, {
|
|
31277
|
+
...entity.context,
|
|
31278
|
+
triggerResult: result
|
|
31279
|
+
});
|
|
31280
|
+
}
|
|
31281
|
+
this.emit("plg:state-machine:trigger-executed", {
|
|
31282
|
+
machineId,
|
|
31283
|
+
entityId: entity.entityId,
|
|
31284
|
+
state: stateName,
|
|
31285
|
+
trigger: triggerName,
|
|
31286
|
+
type: "cron"
|
|
31287
|
+
});
|
|
31288
|
+
} catch (error) {
|
|
31289
|
+
if (trigger.event) {
|
|
31290
|
+
await tryFn(() => this.send(machineId, entity.entityId, trigger.event, {
|
|
31291
|
+
...entity.context,
|
|
31292
|
+
triggerError: error.message
|
|
31293
|
+
}));
|
|
31294
|
+
}
|
|
31295
|
+
if (this.config.verbose) {
|
|
31296
|
+
console.error(`[StateMachinePlugin] Trigger '${triggerName}' failed for entity ${entity.entityId}:`, error.message);
|
|
31297
|
+
}
|
|
31298
|
+
}
|
|
31299
|
+
}
|
|
31300
|
+
return { processed: entities.length, executed: executedCount };
|
|
31301
|
+
}
|
|
31302
|
+
};
|
|
31303
|
+
}
|
|
31304
|
+
/**
|
|
31305
|
+
* Setup a date-based trigger
|
|
31306
|
+
* @private
|
|
31307
|
+
*/
|
|
31308
|
+
async _setupDateTrigger(machineId, stateName, trigger, triggerName) {
|
|
31309
|
+
const checkInterval = setInterval(async () => {
|
|
31310
|
+
const entities = await this._getEntitiesInState(machineId, stateName);
|
|
31311
|
+
for (const entity of entities) {
|
|
31312
|
+
try {
|
|
31313
|
+
const triggerDateValue = entity.context?.[trigger.field];
|
|
31314
|
+
if (!triggerDateValue) continue;
|
|
31315
|
+
const triggerDate = new Date(triggerDateValue);
|
|
31316
|
+
const now = /* @__PURE__ */ new Date();
|
|
31317
|
+
if (now >= triggerDate) {
|
|
31318
|
+
if (trigger.maxTriggers !== void 0) {
|
|
31319
|
+
const triggerCount = entity.triggerCounts?.[triggerName] || 0;
|
|
31320
|
+
if (triggerCount >= trigger.maxTriggers) {
|
|
31321
|
+
if (trigger.onMaxTriggersReached) {
|
|
31322
|
+
await this.send(machineId, entity.entityId, trigger.onMaxTriggersReached, entity.context);
|
|
31323
|
+
}
|
|
31324
|
+
continue;
|
|
31325
|
+
}
|
|
31326
|
+
}
|
|
31327
|
+
const result = await this._executeAction(trigger.action, entity.context, "TRIGGER", machineId, entity.entityId);
|
|
31328
|
+
await this._incrementTriggerCount(machineId, entity.entityId, triggerName);
|
|
31329
|
+
if (trigger.event) {
|
|
31330
|
+
await this.send(machineId, entity.entityId, trigger.event, {
|
|
31331
|
+
...entity.context,
|
|
31332
|
+
triggerResult: result
|
|
31333
|
+
});
|
|
31334
|
+
}
|
|
31335
|
+
this.emit("plg:state-machine:trigger-executed", {
|
|
31336
|
+
machineId,
|
|
31337
|
+
entityId: entity.entityId,
|
|
31338
|
+
state: stateName,
|
|
31339
|
+
trigger: triggerName,
|
|
31340
|
+
type: "date"
|
|
31341
|
+
});
|
|
31342
|
+
}
|
|
31343
|
+
} catch (error) {
|
|
31344
|
+
if (this.config.verbose) {
|
|
31345
|
+
console.error(`[StateMachinePlugin] Date trigger '${triggerName}' failed:`, error.message);
|
|
31346
|
+
}
|
|
31347
|
+
}
|
|
31348
|
+
}
|
|
31349
|
+
}, this.config.triggerCheckInterval);
|
|
31350
|
+
this.triggerIntervals.push(checkInterval);
|
|
31351
|
+
}
|
|
31352
|
+
/**
|
|
31353
|
+
* Setup a function-based trigger
|
|
31354
|
+
* @private
|
|
31355
|
+
*/
|
|
31356
|
+
async _setupFunctionTrigger(machineId, stateName, trigger, triggerName) {
|
|
31357
|
+
const interval = trigger.interval || this.config.triggerCheckInterval;
|
|
31358
|
+
const checkInterval = setInterval(async () => {
|
|
31359
|
+
const entities = await this._getEntitiesInState(machineId, stateName);
|
|
31360
|
+
for (const entity of entities) {
|
|
31361
|
+
try {
|
|
31362
|
+
if (trigger.maxTriggers !== void 0) {
|
|
31363
|
+
const triggerCount = entity.triggerCounts?.[triggerName] || 0;
|
|
31364
|
+
if (triggerCount >= trigger.maxTriggers) {
|
|
31365
|
+
if (trigger.onMaxTriggersReached) {
|
|
31366
|
+
await this.send(machineId, entity.entityId, trigger.onMaxTriggersReached, entity.context);
|
|
31367
|
+
}
|
|
31368
|
+
continue;
|
|
31369
|
+
}
|
|
31370
|
+
}
|
|
31371
|
+
const shouldTrigger = await trigger.condition(entity.context, entity.entityId);
|
|
31372
|
+
if (shouldTrigger) {
|
|
31373
|
+
const result = await this._executeAction(trigger.action, entity.context, "TRIGGER", machineId, entity.entityId);
|
|
31374
|
+
await this._incrementTriggerCount(machineId, entity.entityId, triggerName);
|
|
31375
|
+
if (trigger.event) {
|
|
31376
|
+
await this.send(machineId, entity.entityId, trigger.event, {
|
|
31377
|
+
...entity.context,
|
|
31378
|
+
triggerResult: result
|
|
31379
|
+
});
|
|
31380
|
+
}
|
|
31381
|
+
this.emit("plg:state-machine:trigger-executed", {
|
|
31382
|
+
machineId,
|
|
31383
|
+
entityId: entity.entityId,
|
|
31384
|
+
state: stateName,
|
|
31385
|
+
trigger: triggerName,
|
|
31386
|
+
type: "function"
|
|
31387
|
+
});
|
|
31388
|
+
}
|
|
31389
|
+
} catch (error) {
|
|
31390
|
+
if (this.config.verbose) {
|
|
31391
|
+
console.error(`[StateMachinePlugin] Function trigger '${triggerName}' failed:`, error.message);
|
|
31392
|
+
}
|
|
31393
|
+
}
|
|
31394
|
+
}
|
|
31395
|
+
}, interval);
|
|
31396
|
+
this.triggerIntervals.push(checkInterval);
|
|
31397
|
+
}
|
|
31398
|
+
/**
|
|
31399
|
+
* Setup an event-based trigger
|
|
31400
|
+
* @private
|
|
31401
|
+
*/
|
|
31402
|
+
async _setupEventTrigger(machineId, stateName, trigger, triggerName) {
|
|
31403
|
+
const eventName = trigger.event;
|
|
31404
|
+
const eventHandler = async (eventData) => {
|
|
31405
|
+
const entities = await this._getEntitiesInState(machineId, stateName);
|
|
31406
|
+
for (const entity of entities) {
|
|
31407
|
+
try {
|
|
31408
|
+
if (trigger.condition) {
|
|
31409
|
+
const shouldTrigger = await trigger.condition(entity.context, entity.entityId, eventData);
|
|
31410
|
+
if (!shouldTrigger) continue;
|
|
31411
|
+
}
|
|
31412
|
+
if (trigger.maxTriggers !== void 0) {
|
|
31413
|
+
const triggerCount = entity.triggerCounts?.[triggerName] || 0;
|
|
31414
|
+
if (triggerCount >= trigger.maxTriggers) {
|
|
31415
|
+
if (trigger.onMaxTriggersReached) {
|
|
31416
|
+
await this.send(machineId, entity.entityId, trigger.onMaxTriggersReached, entity.context);
|
|
31417
|
+
}
|
|
31418
|
+
continue;
|
|
31419
|
+
}
|
|
31420
|
+
}
|
|
31421
|
+
const result = await this._executeAction(
|
|
31422
|
+
trigger.action,
|
|
31423
|
+
{ ...entity.context, eventData },
|
|
31424
|
+
"TRIGGER",
|
|
31425
|
+
machineId,
|
|
31426
|
+
entity.entityId
|
|
31427
|
+
);
|
|
31428
|
+
await this._incrementTriggerCount(machineId, entity.entityId, triggerName);
|
|
31429
|
+
if (trigger.sendEvent) {
|
|
31430
|
+
await this.send(machineId, entity.entityId, trigger.sendEvent, {
|
|
31431
|
+
...entity.context,
|
|
31432
|
+
triggerResult: result,
|
|
31433
|
+
eventData
|
|
31434
|
+
});
|
|
31435
|
+
}
|
|
31436
|
+
this.emit("plg:state-machine:trigger-executed", {
|
|
31437
|
+
machineId,
|
|
31438
|
+
entityId: entity.entityId,
|
|
31439
|
+
state: stateName,
|
|
31440
|
+
trigger: triggerName,
|
|
31441
|
+
type: "event",
|
|
31442
|
+
eventName
|
|
31443
|
+
});
|
|
31444
|
+
} catch (error) {
|
|
31445
|
+
if (this.config.verbose) {
|
|
31446
|
+
console.error(`[StateMachinePlugin] Event trigger '${triggerName}' failed:`, error.message);
|
|
31447
|
+
}
|
|
31448
|
+
}
|
|
31449
|
+
}
|
|
31450
|
+
};
|
|
31451
|
+
if (eventName.startsWith("db:")) {
|
|
31452
|
+
const dbEventName = eventName.substring(3);
|
|
31453
|
+
this.database.on(dbEventName, eventHandler);
|
|
31454
|
+
if (this.config.verbose) {
|
|
31455
|
+
console.log(`[StateMachinePlugin] Listening to database event '${dbEventName}' for trigger '${triggerName}'`);
|
|
31456
|
+
}
|
|
31457
|
+
} else {
|
|
31458
|
+
this.on(eventName, eventHandler);
|
|
31459
|
+
if (this.config.verbose) {
|
|
31460
|
+
console.log(`[StateMachinePlugin] Listening to plugin event '${eventName}' for trigger '${triggerName}'`);
|
|
31461
|
+
}
|
|
31462
|
+
}
|
|
31463
|
+
}
|
|
30664
31464
|
async start() {
|
|
30665
31465
|
if (this.config.verbose) {
|
|
30666
31466
|
console.log(`[StateMachinePlugin] Started with ${this.machines.size} state machines`);
|
|
30667
31467
|
}
|
|
30668
31468
|
}
|
|
30669
31469
|
async stop() {
|
|
31470
|
+
for (const interval of this.triggerIntervals) {
|
|
31471
|
+
clearInterval(interval);
|
|
31472
|
+
}
|
|
31473
|
+
this.triggerIntervals = [];
|
|
31474
|
+
if (this.schedulerPlugin) {
|
|
31475
|
+
await this.schedulerPlugin.stop();
|
|
31476
|
+
this.schedulerPlugin = null;
|
|
31477
|
+
}
|
|
30670
31478
|
this.machines.clear();
|
|
30671
31479
|
this.removeAllListeners();
|
|
30672
31480
|
}
|
|
@@ -40938,7 +41746,7 @@ class TTLPlugin extends Plugin {
|
|
|
40938
41746
|
if (this.verbose) {
|
|
40939
41747
|
console.log(`[TTLPlugin] Installed with ${Object.keys(this.resources).length} resources`);
|
|
40940
41748
|
}
|
|
40941
|
-
this.emit("installed", {
|
|
41749
|
+
this.emit("db:plugin:installed", {
|
|
40942
41750
|
plugin: "TTLPlugin",
|
|
40943
41751
|
resources: Object.keys(this.resources)
|
|
40944
41752
|
});
|
|
@@ -41175,7 +41983,7 @@ class TTLPlugin extends Plugin {
|
|
|
41175
41983
|
}
|
|
41176
41984
|
this.stats.lastScanAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
41177
41985
|
this.stats.lastScanDuration = Date.now() - startTime;
|
|
41178
|
-
this.emit("
|
|
41986
|
+
this.emit("plg:ttl:scan-completed", {
|
|
41179
41987
|
granularity,
|
|
41180
41988
|
duration: this.stats.lastScanDuration,
|
|
41181
41989
|
cohorts
|
|
@@ -41183,7 +41991,7 @@ class TTLPlugin extends Plugin {
|
|
|
41183
41991
|
} catch (error) {
|
|
41184
41992
|
console.error(`[TTLPlugin] Error in ${granularity} cleanup:`, error);
|
|
41185
41993
|
this.stats.totalErrors++;
|
|
41186
|
-
this.emit("
|
|
41994
|
+
this.emit("plg:ttl:cleanup-error", { granularity, error });
|
|
41187
41995
|
}
|
|
41188
41996
|
}
|
|
41189
41997
|
/**
|
|
@@ -41231,7 +42039,7 @@ class TTLPlugin extends Plugin {
|
|
|
41231
42039
|
}
|
|
41232
42040
|
await this.expirationIndex.delete(entry.id);
|
|
41233
42041
|
this.stats.totalExpired++;
|
|
41234
|
-
this.emit("
|
|
42042
|
+
this.emit("plg:ttl:record-expired", { resource: entry.resourceName, record });
|
|
41235
42043
|
} catch (error) {
|
|
41236
42044
|
console.error(`[TTLPlugin] Error processing expired entry:`, error);
|
|
41237
42045
|
this.stats.totalErrors++;
|
|
@@ -41702,15 +42510,15 @@ class VectorPlugin extends Plugin {
|
|
|
41702
42510
|
this._throttleState = /* @__PURE__ */ new Map();
|
|
41703
42511
|
}
|
|
41704
42512
|
async onInstall() {
|
|
41705
|
-
this.emit("installed", { plugin: "VectorPlugin" });
|
|
42513
|
+
this.emit("db:plugin:installed", { plugin: "VectorPlugin" });
|
|
41706
42514
|
this.validateVectorStorage();
|
|
41707
42515
|
this.installResourceMethods();
|
|
41708
42516
|
}
|
|
41709
42517
|
async onStart() {
|
|
41710
|
-
this.emit("started", { plugin: "VectorPlugin" });
|
|
42518
|
+
this.emit("db:plugin:started", { plugin: "VectorPlugin" });
|
|
41711
42519
|
}
|
|
41712
42520
|
async onStop() {
|
|
41713
|
-
this.emit("stopped", { plugin: "VectorPlugin" });
|
|
42521
|
+
this.emit("db:plugin:stopped", { plugin: "VectorPlugin" });
|
|
41714
42522
|
}
|
|
41715
42523
|
async onUninstall(options) {
|
|
41716
42524
|
for (const resource of Object.values(this.database.resources)) {
|
|
@@ -41721,7 +42529,7 @@ class VectorPlugin extends Plugin {
|
|
|
41721
42529
|
delete resource.findSimilar;
|
|
41722
42530
|
delete resource.distance;
|
|
41723
42531
|
}
|
|
41724
|
-
this.emit("uninstalled", { plugin: "VectorPlugin" });
|
|
42532
|
+
this.emit("db:plugin:uninstalled", { plugin: "VectorPlugin" });
|
|
41725
42533
|
}
|
|
41726
42534
|
/**
|
|
41727
42535
|
* Validate vector storage configuration for all resources
|
|
@@ -41750,10 +42558,10 @@ class VectorPlugin extends Plugin {
|
|
|
41750
42558
|
currentBehavior: resource.behavior || "default",
|
|
41751
42559
|
recommendation: "body-overflow"
|
|
41752
42560
|
};
|
|
41753
|
-
this.emit("vector:storage-warning", warning);
|
|
42561
|
+
this.emit("plg:vector:storage-warning", warning);
|
|
41754
42562
|
if (this.config.autoFixBehavior) {
|
|
41755
42563
|
resource.behavior = "body-overflow";
|
|
41756
|
-
this.emit("vector:behavior-fixed", {
|
|
42564
|
+
this.emit("plg:vector:behavior-fixed", {
|
|
41757
42565
|
resource: resource.name,
|
|
41758
42566
|
newBehavior: "body-overflow"
|
|
41759
42567
|
});
|
|
@@ -41785,7 +42593,7 @@ class VectorPlugin extends Plugin {
|
|
|
41785
42593
|
const partitionName = `byHas${this.capitalize(vectorField.name.replace(/\./g, "_"))}`;
|
|
41786
42594
|
const trackingFieldName = `_has${this.capitalize(vectorField.name.replace(/\./g, "_"))}`;
|
|
41787
42595
|
if (resource.config.partitions && resource.config.partitions[partitionName]) {
|
|
41788
|
-
this.emit("vector:partition-exists", {
|
|
42596
|
+
this.emit("plg:vector:partition-exists", {
|
|
41789
42597
|
resource: resource.name,
|
|
41790
42598
|
vectorField: vectorField.name,
|
|
41791
42599
|
partition: partitionName,
|
|
@@ -41808,7 +42616,7 @@ class VectorPlugin extends Plugin {
|
|
|
41808
42616
|
default: false
|
|
41809
42617
|
}, "VectorPlugin");
|
|
41810
42618
|
}
|
|
41811
|
-
this.emit("vector:partition-created", {
|
|
42619
|
+
this.emit("plg:vector:partition-created", {
|
|
41812
42620
|
resource: resource.name,
|
|
41813
42621
|
vectorField: vectorField.name,
|
|
41814
42622
|
partition: partitionName,
|
|
@@ -41883,7 +42691,7 @@ class VectorPlugin extends Plugin {
|
|
|
41883
42691
|
}
|
|
41884
42692
|
return updates;
|
|
41885
42693
|
});
|
|
41886
|
-
this.emit("vector:hooks-installed", {
|
|
42694
|
+
this.emit("plg:vector:hooks-installed", {
|
|
41887
42695
|
resource: resource.name,
|
|
41888
42696
|
vectorField,
|
|
41889
42697
|
trackingField,
|
|
@@ -41992,7 +42800,7 @@ class VectorPlugin extends Plugin {
|
|
|
41992
42800
|
const vectorField = this._findEmbeddingField(resource.schema.attributes);
|
|
41993
42801
|
this._vectorFieldCache.set(resource.name, vectorField);
|
|
41994
42802
|
if (vectorField && this.config.emitEvents) {
|
|
41995
|
-
this.emit("vector:field-detected", {
|
|
42803
|
+
this.emit("plg:vector:field-detected", {
|
|
41996
42804
|
resource: resource.name,
|
|
41997
42805
|
vectorField,
|
|
41998
42806
|
timestamp: Date.now()
|
|
@@ -42916,7 +43724,7 @@ class MemoryClient extends EventEmitter {
|
|
|
42916
43724
|
async sendCommand(command) {
|
|
42917
43725
|
const commandName = command.constructor.name;
|
|
42918
43726
|
const input = command.input || {};
|
|
42919
|
-
this.emit("
|
|
43727
|
+
this.emit("cl:request", commandName, input);
|
|
42920
43728
|
let response;
|
|
42921
43729
|
try {
|
|
42922
43730
|
switch (commandName) {
|
|
@@ -42944,7 +43752,7 @@ class MemoryClient extends EventEmitter {
|
|
|
42944
43752
|
default:
|
|
42945
43753
|
throw new Error(`Unsupported command: ${commandName}`);
|
|
42946
43754
|
}
|
|
42947
|
-
this.emit("
|
|
43755
|
+
this.emit("cl:response", commandName, response, input);
|
|
42948
43756
|
return response;
|
|
42949
43757
|
} catch (error) {
|
|
42950
43758
|
const mappedError = mapAwsError(error, {
|
|
@@ -43055,7 +43863,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43055
43863
|
contentLength,
|
|
43056
43864
|
ifMatch
|
|
43057
43865
|
});
|
|
43058
|
-
this.emit("
|
|
43866
|
+
this.emit("cl:PutObject", null, { key, metadata, contentType, body, contentEncoding, contentLength });
|
|
43059
43867
|
return response;
|
|
43060
43868
|
}
|
|
43061
43869
|
/**
|
|
@@ -43070,7 +43878,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43070
43878
|
decodedMetadata[k] = metadataDecode(v);
|
|
43071
43879
|
}
|
|
43072
43880
|
}
|
|
43073
|
-
this.emit("
|
|
43881
|
+
this.emit("cl:GetObject", null, { key });
|
|
43074
43882
|
return {
|
|
43075
43883
|
...response,
|
|
43076
43884
|
Metadata: decodedMetadata
|
|
@@ -43088,7 +43896,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43088
43896
|
decodedMetadata[k] = metadataDecode(v);
|
|
43089
43897
|
}
|
|
43090
43898
|
}
|
|
43091
|
-
this.emit("
|
|
43899
|
+
this.emit("cl:HeadObject", null, { key });
|
|
43092
43900
|
return {
|
|
43093
43901
|
...response,
|
|
43094
43902
|
Metadata: decodedMetadata
|
|
@@ -43113,7 +43921,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43113
43921
|
metadataDirective,
|
|
43114
43922
|
contentType
|
|
43115
43923
|
});
|
|
43116
|
-
this.emit("
|
|
43924
|
+
this.emit("cl:CopyObject", null, { from, to, metadata, metadataDirective });
|
|
43117
43925
|
return response;
|
|
43118
43926
|
}
|
|
43119
43927
|
/**
|
|
@@ -43129,7 +43937,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43129
43937
|
async deleteObject(key) {
|
|
43130
43938
|
const fullKey = this.keyPrefix ? path$1.join(this.keyPrefix, key) : key;
|
|
43131
43939
|
const response = await this.storage.delete(fullKey);
|
|
43132
|
-
this.emit("
|
|
43940
|
+
this.emit("cl:DeleteObject", null, { key });
|
|
43133
43941
|
return response;
|
|
43134
43942
|
}
|
|
43135
43943
|
/**
|
|
@@ -43162,7 +43970,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43162
43970
|
maxKeys,
|
|
43163
43971
|
continuationToken
|
|
43164
43972
|
});
|
|
43165
|
-
this.emit("
|
|
43973
|
+
this.emit("cl:ListObjects", null, { prefix, count: response.Contents.length });
|
|
43166
43974
|
return response;
|
|
43167
43975
|
}
|
|
43168
43976
|
/**
|
|
@@ -43202,7 +44010,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43202
44010
|
if (this.keyPrefix) {
|
|
43203
44011
|
keys = keys.map((x) => x.replace(this.keyPrefix, "")).map((x) => x.startsWith("/") ? x.replace("/", "") : x);
|
|
43204
44012
|
}
|
|
43205
|
-
this.emit("
|
|
44013
|
+
this.emit("cl:GetKeysPage", keys, params);
|
|
43206
44014
|
return keys;
|
|
43207
44015
|
}
|
|
43208
44016
|
/**
|
|
@@ -43219,7 +44027,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43219
44027
|
if (this.keyPrefix) {
|
|
43220
44028
|
keys = keys.map((x) => x.replace(this.keyPrefix, "")).map((x) => x.startsWith("/") ? x.replace("/", "") : x);
|
|
43221
44029
|
}
|
|
43222
|
-
this.emit("
|
|
44030
|
+
this.emit("cl:GetAllKeys", keys, { prefix });
|
|
43223
44031
|
return keys;
|
|
43224
44032
|
}
|
|
43225
44033
|
/**
|
|
@@ -43228,7 +44036,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43228
44036
|
async count({ prefix = "" } = {}) {
|
|
43229
44037
|
const keys = await this.getAllKeys({ prefix });
|
|
43230
44038
|
const count = keys.length;
|
|
43231
|
-
this.emit("
|
|
44039
|
+
this.emit("cl:Count", count, { prefix });
|
|
43232
44040
|
return count;
|
|
43233
44041
|
}
|
|
43234
44042
|
/**
|
|
@@ -43240,13 +44048,13 @@ class MemoryClient extends EventEmitter {
|
|
|
43240
44048
|
if (keys.length > 0) {
|
|
43241
44049
|
const result = await this.deleteObjects(keys);
|
|
43242
44050
|
totalDeleted = result.Deleted.length;
|
|
43243
|
-
this.emit("
|
|
44051
|
+
this.emit("cl:DeleteAll", {
|
|
43244
44052
|
prefix,
|
|
43245
44053
|
batch: totalDeleted,
|
|
43246
44054
|
total: totalDeleted
|
|
43247
44055
|
});
|
|
43248
44056
|
}
|
|
43249
|
-
this.emit("
|
|
44057
|
+
this.emit("cl:DeleteAllComplete", {
|
|
43250
44058
|
prefix,
|
|
43251
44059
|
totalDeleted
|
|
43252
44060
|
});
|
|
@@ -43259,11 +44067,11 @@ class MemoryClient extends EventEmitter {
|
|
|
43259
44067
|
if (offset === 0) return null;
|
|
43260
44068
|
const keys = await this.getAllKeys({ prefix });
|
|
43261
44069
|
if (offset >= keys.length) {
|
|
43262
|
-
this.emit("
|
|
44070
|
+
this.emit("cl:GetContinuationTokenAfterOffset", null, { prefix, offset });
|
|
43263
44071
|
return null;
|
|
43264
44072
|
}
|
|
43265
44073
|
const token = keys[offset];
|
|
43266
|
-
this.emit("
|
|
44074
|
+
this.emit("cl:GetContinuationTokenAfterOffset", token, { prefix, offset });
|
|
43267
44075
|
return token;
|
|
43268
44076
|
}
|
|
43269
44077
|
/**
|
|
@@ -43293,7 +44101,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43293
44101
|
});
|
|
43294
44102
|
}
|
|
43295
44103
|
}
|
|
43296
|
-
this.emit("
|
|
44104
|
+
this.emit("cl:MoveAllObjects", { results, errors }, { prefixFrom, prefixTo });
|
|
43297
44105
|
if (errors.length > 0) {
|
|
43298
44106
|
const error = new Error("Some objects could not be moved");
|
|
43299
44107
|
error.context = {
|