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.es.js
CHANGED
|
@@ -2544,6 +2544,18 @@ const PLUGIN_DEPENDENCIES = {
|
|
|
2544
2544
|
npmUrl: "https://www.npmjs.com/package/@hono/swagger-ui"
|
|
2545
2545
|
}
|
|
2546
2546
|
}
|
|
2547
|
+
},
|
|
2548
|
+
"ml-plugin": {
|
|
2549
|
+
name: "ML Plugin",
|
|
2550
|
+
docsUrl: "https://github.com/forattini-dev/s3db.js/blob/main/docs/plugins/ml-plugin.md",
|
|
2551
|
+
dependencies: {
|
|
2552
|
+
"@tensorflow/tfjs-node": {
|
|
2553
|
+
version: "^4.0.0",
|
|
2554
|
+
description: "TensorFlow.js for Node.js with native bindings",
|
|
2555
|
+
installCommand: "pnpm add @tensorflow/tfjs-node",
|
|
2556
|
+
npmUrl: "https://www.npmjs.com/package/@tensorflow/tfjs-node"
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2547
2559
|
}
|
|
2548
2560
|
};
|
|
2549
2561
|
function isVersionCompatible(actual, required) {
|
|
@@ -6593,7 +6605,7 @@ class BackupPlugin extends Plugin {
|
|
|
6593
6605
|
const storageInfo = this.driver.getStorageInfo();
|
|
6594
6606
|
console.log(`[BackupPlugin] Initialized with driver: ${storageInfo.type}`);
|
|
6595
6607
|
}
|
|
6596
|
-
this.emit("initialized", {
|
|
6608
|
+
this.emit("db:plugin:initialized", {
|
|
6597
6609
|
driver: this.driver.getType(),
|
|
6598
6610
|
config: this.driver.getStorageInfo()
|
|
6599
6611
|
});
|
|
@@ -6641,7 +6653,7 @@ class BackupPlugin extends Plugin {
|
|
|
6641
6653
|
if (this.config.onBackupStart) {
|
|
6642
6654
|
await this._executeHook(this.config.onBackupStart, type, { backupId });
|
|
6643
6655
|
}
|
|
6644
|
-
this.emit("
|
|
6656
|
+
this.emit("plg:backup:start", { id: backupId, type });
|
|
6645
6657
|
const metadata = await this._createBackupMetadata(backupId, type);
|
|
6646
6658
|
const tempBackupDir = path$1.join(this.config.tempDir, backupId);
|
|
6647
6659
|
await mkdir(tempBackupDir, { recursive: true });
|
|
@@ -6674,7 +6686,7 @@ class BackupPlugin extends Plugin {
|
|
|
6674
6686
|
const stats = { backupId, type, size: totalSize, duration, driverInfo: uploadResult };
|
|
6675
6687
|
await this._executeHook(this.config.onBackupComplete, type, stats);
|
|
6676
6688
|
}
|
|
6677
|
-
this.emit("
|
|
6689
|
+
this.emit("plg:backup:complete", {
|
|
6678
6690
|
id: backupId,
|
|
6679
6691
|
type,
|
|
6680
6692
|
size: totalSize,
|
|
@@ -6702,7 +6714,7 @@ class BackupPlugin extends Plugin {
|
|
|
6702
6714
|
error: error.message,
|
|
6703
6715
|
duration: Date.now() - startTime
|
|
6704
6716
|
});
|
|
6705
|
-
this.emit("
|
|
6717
|
+
this.emit("plg:backup:error", { id: backupId, type, error: error.message });
|
|
6706
6718
|
throw error;
|
|
6707
6719
|
} finally {
|
|
6708
6720
|
this.activeBackups.delete(backupId);
|
|
@@ -6923,7 +6935,7 @@ class BackupPlugin extends Plugin {
|
|
|
6923
6935
|
if (this.config.onRestoreStart) {
|
|
6924
6936
|
await this._executeHook(this.config.onRestoreStart, backupId, options);
|
|
6925
6937
|
}
|
|
6926
|
-
this.emit("
|
|
6938
|
+
this.emit("plg:backup:restore-start", { id: backupId, options });
|
|
6927
6939
|
const backup = await this.getBackupStatus(backupId);
|
|
6928
6940
|
if (!backup) {
|
|
6929
6941
|
throw new Error(`Backup '${backupId}' not found`);
|
|
@@ -6946,7 +6958,7 @@ class BackupPlugin extends Plugin {
|
|
|
6946
6958
|
if (this.config.onRestoreComplete) {
|
|
6947
6959
|
await this._executeHook(this.config.onRestoreComplete, backupId, { restored: restoredResources });
|
|
6948
6960
|
}
|
|
6949
|
-
this.emit("
|
|
6961
|
+
this.emit("plg:backup:restore-complete", {
|
|
6950
6962
|
id: backupId,
|
|
6951
6963
|
restored: restoredResources
|
|
6952
6964
|
});
|
|
@@ -6961,7 +6973,7 @@ class BackupPlugin extends Plugin {
|
|
|
6961
6973
|
if (this.config.onRestoreError) {
|
|
6962
6974
|
await this._executeHook(this.config.onRestoreError, backupId, { error });
|
|
6963
6975
|
}
|
|
6964
|
-
this.emit("
|
|
6976
|
+
this.emit("plg:backup:restore-error", { id: backupId, error: error.message });
|
|
6965
6977
|
throw error;
|
|
6966
6978
|
}
|
|
6967
6979
|
}
|
|
@@ -7211,7 +7223,7 @@ class BackupPlugin extends Plugin {
|
|
|
7211
7223
|
}
|
|
7212
7224
|
async stop() {
|
|
7213
7225
|
for (const backupId of this.activeBackups) {
|
|
7214
|
-
this.emit("
|
|
7226
|
+
this.emit("plg:backup:cancelled", { id: backupId });
|
|
7215
7227
|
}
|
|
7216
7228
|
this.activeBackups.clear();
|
|
7217
7229
|
if (this.driver) {
|
|
@@ -8735,7 +8747,7 @@ class CachePlugin extends Plugin {
|
|
|
8735
8747
|
const specificKey = await this.generateCacheKey(resource, method, { id: data.id });
|
|
8736
8748
|
const [ok2, err2] = await this.clearCacheWithRetry(resource.cache, specificKey);
|
|
8737
8749
|
if (!ok2) {
|
|
8738
|
-
this.emit("
|
|
8750
|
+
this.emit("plg:cache:clear-error", {
|
|
8739
8751
|
resource: resource.name,
|
|
8740
8752
|
method,
|
|
8741
8753
|
id: data.id,
|
|
@@ -8753,7 +8765,7 @@ class CachePlugin extends Plugin {
|
|
|
8753
8765
|
const partitionKeyPrefix = join(keyPrefix, `partition=${partitionName}`);
|
|
8754
8766
|
const [ok2, err2] = await this.clearCacheWithRetry(resource.cache, partitionKeyPrefix);
|
|
8755
8767
|
if (!ok2) {
|
|
8756
|
-
this.emit("
|
|
8768
|
+
this.emit("plg:cache:clear-error", {
|
|
8757
8769
|
resource: resource.name,
|
|
8758
8770
|
partition: partitionName,
|
|
8759
8771
|
error: err2.message
|
|
@@ -8768,7 +8780,7 @@ class CachePlugin extends Plugin {
|
|
|
8768
8780
|
}
|
|
8769
8781
|
const [ok, err] = await this.clearCacheWithRetry(resource.cache, keyPrefix);
|
|
8770
8782
|
if (!ok) {
|
|
8771
|
-
this.emit("
|
|
8783
|
+
this.emit("plg:cache:clear-error", {
|
|
8772
8784
|
resource: resource.name,
|
|
8773
8785
|
type: "broad",
|
|
8774
8786
|
error: err.message
|
|
@@ -12668,7 +12680,7 @@ class GeoPlugin extends Plugin {
|
|
|
12668
12680
|
if (this.verbose) {
|
|
12669
12681
|
console.log(`[GeoPlugin] Installed with ${Object.keys(this.resources).length} resources`);
|
|
12670
12682
|
}
|
|
12671
|
-
this.emit("installed", {
|
|
12683
|
+
this.emit("db:plugin:installed", {
|
|
12672
12684
|
plugin: "GeoPlugin",
|
|
12673
12685
|
resources: Object.keys(this.resources)
|
|
12674
12686
|
});
|
|
@@ -13235,7 +13247,7 @@ class GeoPlugin extends Plugin {
|
|
|
13235
13247
|
if (this.verbose) {
|
|
13236
13248
|
console.log("[GeoPlugin] Uninstalled");
|
|
13237
13249
|
}
|
|
13238
|
-
this.emit("uninstalled", {
|
|
13250
|
+
this.emit("db:plugin:uninstalled", {
|
|
13239
13251
|
plugin: "GeoPlugin"
|
|
13240
13252
|
});
|
|
13241
13253
|
await super.uninstall();
|
|
@@ -15237,7 +15249,7 @@ class MLPlugin extends Plugin {
|
|
|
15237
15249
|
enableVersioning: options.enableVersioning !== false
|
|
15238
15250
|
// Default true
|
|
15239
15251
|
};
|
|
15240
|
-
requirePluginDependency("
|
|
15252
|
+
requirePluginDependency("ml-plugin");
|
|
15241
15253
|
this.models = {};
|
|
15242
15254
|
this.modelVersions = /* @__PURE__ */ new Map();
|
|
15243
15255
|
this.modelCache = /* @__PURE__ */ new Map();
|
|
@@ -15275,7 +15287,7 @@ class MLPlugin extends Plugin {
|
|
|
15275
15287
|
if (this.config.verbose) {
|
|
15276
15288
|
console.log(`[MLPlugin] Installed with ${Object.keys(this.models).length} models`);
|
|
15277
15289
|
}
|
|
15278
|
-
this.emit("installed", {
|
|
15290
|
+
this.emit("db:plugin:installed", {
|
|
15279
15291
|
plugin: "MLPlugin",
|
|
15280
15292
|
models: Object.keys(this.models)
|
|
15281
15293
|
});
|
|
@@ -15626,6 +15638,22 @@ class MLPlugin extends Plugin {
|
|
|
15626
15638
|
}
|
|
15627
15639
|
data = allData;
|
|
15628
15640
|
}
|
|
15641
|
+
if (modelConfig.filter && typeof modelConfig.filter === "function") {
|
|
15642
|
+
if (this.config.verbose) {
|
|
15643
|
+
console.log(`[MLPlugin] Applying custom filter function...`);
|
|
15644
|
+
}
|
|
15645
|
+
const originalLength = data.length;
|
|
15646
|
+
data = data.filter(modelConfig.filter);
|
|
15647
|
+
if (this.config.verbose) {
|
|
15648
|
+
console.log(`[MLPlugin] Filter reduced dataset from ${originalLength} to ${data.length} samples`);
|
|
15649
|
+
}
|
|
15650
|
+
}
|
|
15651
|
+
if (modelConfig.map && typeof modelConfig.map === "function") {
|
|
15652
|
+
if (this.config.verbose) {
|
|
15653
|
+
console.log(`[MLPlugin] Applying custom map function...`);
|
|
15654
|
+
}
|
|
15655
|
+
data = data.map(modelConfig.map);
|
|
15656
|
+
}
|
|
15629
15657
|
if (!data || data.length < this.config.minTrainingSamples) {
|
|
15630
15658
|
throw new TrainingError(
|
|
15631
15659
|
`Insufficient training data: ${data?.length || 0} samples (minimum: ${this.config.minTrainingSamples})`,
|
|
@@ -15648,7 +15676,7 @@ class MLPlugin extends Plugin {
|
|
|
15648
15676
|
if (this.config.verbose) {
|
|
15649
15677
|
console.log(`[MLPlugin] Training completed for "${modelName}":`, result);
|
|
15650
15678
|
}
|
|
15651
|
-
this.emit("
|
|
15679
|
+
this.emit("plg:ml:model-trained", {
|
|
15652
15680
|
modelName,
|
|
15653
15681
|
type: modelConfig.type,
|
|
15654
15682
|
result
|
|
@@ -15684,7 +15712,7 @@ class MLPlugin extends Plugin {
|
|
|
15684
15712
|
try {
|
|
15685
15713
|
const result = await model.predict(input);
|
|
15686
15714
|
this.stats.totalPredictions++;
|
|
15687
|
-
this.emit("prediction", {
|
|
15715
|
+
this.emit("plg:ml:prediction", {
|
|
15688
15716
|
modelName,
|
|
15689
15717
|
input,
|
|
15690
15718
|
result
|
|
@@ -15799,7 +15827,11 @@ class MLPlugin extends Plugin {
|
|
|
15799
15827
|
async _initializeVersioning(modelName) {
|
|
15800
15828
|
try {
|
|
15801
15829
|
const storage = this.getStorage();
|
|
15802
|
-
const
|
|
15830
|
+
const modelConfig = this.config.models[modelName];
|
|
15831
|
+
const resourceName = modelConfig.resource;
|
|
15832
|
+
const [ok, err, versionInfo] = await tryFn(
|
|
15833
|
+
() => storage.get(storage.getPluginKey(resourceName, "metadata", modelName, "versions"))
|
|
15834
|
+
);
|
|
15803
15835
|
if (ok && versionInfo) {
|
|
15804
15836
|
this.modelVersions.set(modelName, {
|
|
15805
15837
|
currentVersion: versionInfo.currentVersion || 1,
|
|
@@ -15838,16 +15870,22 @@ class MLPlugin extends Plugin {
|
|
|
15838
15870
|
async _updateVersionInfo(modelName, version) {
|
|
15839
15871
|
try {
|
|
15840
15872
|
const storage = this.getStorage();
|
|
15873
|
+
const modelConfig = this.config.models[modelName];
|
|
15874
|
+
const resourceName = modelConfig.resource;
|
|
15841
15875
|
const versionInfo = this.modelVersions.get(modelName) || { currentVersion: 1, latestVersion: 0 };
|
|
15842
15876
|
versionInfo.latestVersion = Math.max(versionInfo.latestVersion, version);
|
|
15843
15877
|
versionInfo.currentVersion = version;
|
|
15844
15878
|
this.modelVersions.set(modelName, versionInfo);
|
|
15845
|
-
await storage.
|
|
15846
|
-
modelName,
|
|
15847
|
-
|
|
15848
|
-
|
|
15849
|
-
|
|
15850
|
-
|
|
15879
|
+
await storage.set(
|
|
15880
|
+
storage.getPluginKey(resourceName, "metadata", modelName, "versions"),
|
|
15881
|
+
{
|
|
15882
|
+
modelName,
|
|
15883
|
+
currentVersion: versionInfo.currentVersion,
|
|
15884
|
+
latestVersion: versionInfo.latestVersion,
|
|
15885
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
15886
|
+
},
|
|
15887
|
+
{ behavior: "body-overflow" }
|
|
15888
|
+
);
|
|
15851
15889
|
if (this.config.verbose) {
|
|
15852
15890
|
console.log(`[MLPlugin] Updated version info for "${modelName}": current=v${versionInfo.currentVersion}, latest=v${versionInfo.latestVersion}`);
|
|
15853
15891
|
}
|
|
@@ -15862,6 +15900,8 @@ class MLPlugin extends Plugin {
|
|
|
15862
15900
|
async _saveModel(modelName) {
|
|
15863
15901
|
try {
|
|
15864
15902
|
const storage = this.getStorage();
|
|
15903
|
+
const modelConfig = this.config.models[modelName];
|
|
15904
|
+
const resourceName = modelConfig.resource;
|
|
15865
15905
|
const exportedModel = await this.models[modelName].export();
|
|
15866
15906
|
if (!exportedModel) {
|
|
15867
15907
|
if (this.config.verbose) {
|
|
@@ -15873,37 +15913,52 @@ class MLPlugin extends Plugin {
|
|
|
15873
15913
|
if (enableVersioning) {
|
|
15874
15914
|
const version = this._getNextVersion(modelName);
|
|
15875
15915
|
const modelStats = this.models[modelName].getStats();
|
|
15876
|
-
await storage.
|
|
15877
|
-
modelName,
|
|
15878
|
-
|
|
15879
|
-
|
|
15880
|
-
|
|
15881
|
-
|
|
15882
|
-
|
|
15883
|
-
|
|
15884
|
-
|
|
15885
|
-
|
|
15886
|
-
|
|
15887
|
-
|
|
15916
|
+
await storage.set(
|
|
15917
|
+
storage.getPluginKey(resourceName, "models", modelName, `v${version}`),
|
|
15918
|
+
{
|
|
15919
|
+
modelName,
|
|
15920
|
+
version,
|
|
15921
|
+
type: "model",
|
|
15922
|
+
modelData: exportedModel,
|
|
15923
|
+
// TensorFlow.js model object (will go to body)
|
|
15924
|
+
metrics: {
|
|
15925
|
+
loss: modelStats.loss,
|
|
15926
|
+
accuracy: modelStats.accuracy,
|
|
15927
|
+
samples: modelStats.samples
|
|
15928
|
+
},
|
|
15929
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
15930
|
+
},
|
|
15931
|
+
{ behavior: "body-only" }
|
|
15932
|
+
// Large binary data goes to S3 body
|
|
15933
|
+
);
|
|
15888
15934
|
await this._updateVersionInfo(modelName, version);
|
|
15889
|
-
await storage.
|
|
15890
|
-
modelName,
|
|
15891
|
-
|
|
15892
|
-
|
|
15893
|
-
|
|
15894
|
-
|
|
15935
|
+
await storage.set(
|
|
15936
|
+
storage.getPluginKey(resourceName, "metadata", modelName, "active"),
|
|
15937
|
+
{
|
|
15938
|
+
modelName,
|
|
15939
|
+
version,
|
|
15940
|
+
type: "reference",
|
|
15941
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
15942
|
+
},
|
|
15943
|
+
{ behavior: "body-overflow" }
|
|
15944
|
+
// Small metadata
|
|
15945
|
+
);
|
|
15895
15946
|
if (this.config.verbose) {
|
|
15896
|
-
console.log(`[MLPlugin] Saved model "${modelName}" v${version} to
|
|
15947
|
+
console.log(`[MLPlugin] Saved model "${modelName}" v${version} to S3 (resource=${resourceName}/plugin=ml/models/${modelName}/v${version})`);
|
|
15897
15948
|
}
|
|
15898
15949
|
} else {
|
|
15899
|
-
await storage.
|
|
15900
|
-
modelName,
|
|
15901
|
-
|
|
15902
|
-
|
|
15903
|
-
|
|
15904
|
-
|
|
15950
|
+
await storage.set(
|
|
15951
|
+
storage.getPluginKey(resourceName, "models", modelName, "latest"),
|
|
15952
|
+
{
|
|
15953
|
+
modelName,
|
|
15954
|
+
type: "model",
|
|
15955
|
+
modelData: exportedModel,
|
|
15956
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
15957
|
+
},
|
|
15958
|
+
{ behavior: "body-only" }
|
|
15959
|
+
);
|
|
15905
15960
|
if (this.config.verbose) {
|
|
15906
|
-
console.log(`[MLPlugin] Saved model "${modelName}" to
|
|
15961
|
+
console.log(`[MLPlugin] Saved model "${modelName}" to S3 (resource=${resourceName}/plugin=ml/models/${modelName}/latest)`);
|
|
15907
15962
|
}
|
|
15908
15963
|
}
|
|
15909
15964
|
} catch (error) {
|
|
@@ -15911,7 +15966,7 @@ class MLPlugin extends Plugin {
|
|
|
15911
15966
|
}
|
|
15912
15967
|
}
|
|
15913
15968
|
/**
|
|
15914
|
-
* Save intermediate training data to plugin storage (incremental)
|
|
15969
|
+
* Save intermediate training data to plugin storage (incremental - only new samples)
|
|
15915
15970
|
* @private
|
|
15916
15971
|
*/
|
|
15917
15972
|
async _saveTrainingData(modelName, rawData) {
|
|
@@ -15919,64 +15974,106 @@ class MLPlugin extends Plugin {
|
|
|
15919
15974
|
const storage = this.getStorage();
|
|
15920
15975
|
const model = this.models[modelName];
|
|
15921
15976
|
const modelConfig = this.config.models[modelName];
|
|
15977
|
+
const resourceName = modelConfig.resource;
|
|
15922
15978
|
const modelStats = model.getStats();
|
|
15923
15979
|
const enableVersioning = this.config.enableVersioning;
|
|
15924
|
-
const
|
|
15925
|
-
|
|
15926
|
-
|
|
15927
|
-
|
|
15928
|
-
|
|
15929
|
-
|
|
15930
|
-
|
|
15931
|
-
|
|
15932
|
-
|
|
15933
|
-
|
|
15934
|
-
|
|
15935
|
-
|
|
15936
|
-
target: item[modelConfig.target]
|
|
15937
|
-
};
|
|
15938
|
-
}),
|
|
15939
|
-
metrics: {
|
|
15940
|
-
loss: modelStats.loss,
|
|
15941
|
-
accuracy: modelStats.accuracy,
|
|
15942
|
-
r2: modelStats.r2
|
|
15943
|
-
},
|
|
15944
|
-
trainedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
15945
|
-
};
|
|
15980
|
+
const processedData = rawData.map((item) => {
|
|
15981
|
+
const features = {};
|
|
15982
|
+
modelConfig.features.forEach((feature) => {
|
|
15983
|
+
features[feature] = item[feature];
|
|
15984
|
+
});
|
|
15985
|
+
return {
|
|
15986
|
+
id: item.id || `${Date.now()}_${Math.random()}`,
|
|
15987
|
+
// Use record ID or generate
|
|
15988
|
+
features,
|
|
15989
|
+
target: item[modelConfig.target]
|
|
15990
|
+
};
|
|
15991
|
+
});
|
|
15946
15992
|
if (enableVersioning) {
|
|
15947
|
-
const
|
|
15993
|
+
const version = this._getNextVersion(modelName);
|
|
15994
|
+
const [ok, err, existing] = await tryFn(
|
|
15995
|
+
() => storage.get(storage.getPluginKey(resourceName, "training", "history", modelName))
|
|
15996
|
+
);
|
|
15948
15997
|
let history = [];
|
|
15998
|
+
let previousSampleIds = /* @__PURE__ */ new Set();
|
|
15949
15999
|
if (ok && existing && existing.history) {
|
|
15950
|
-
|
|
15951
|
-
|
|
15952
|
-
|
|
15953
|
-
|
|
15954
|
-
|
|
16000
|
+
history = existing.history;
|
|
16001
|
+
history.forEach((entry) => {
|
|
16002
|
+
if (entry.sampleIds) {
|
|
16003
|
+
entry.sampleIds.forEach((id) => previousSampleIds.add(id));
|
|
16004
|
+
}
|
|
16005
|
+
});
|
|
15955
16006
|
}
|
|
15956
|
-
|
|
15957
|
-
|
|
15958
|
-
|
|
15959
|
-
|
|
15960
|
-
|
|
15961
|
-
|
|
15962
|
-
|
|
15963
|
-
|
|
15964
|
-
|
|
16007
|
+
const currentSampleIds = new Set(processedData.map((d) => d.id));
|
|
16008
|
+
const newSamples = processedData.filter((d) => !previousSampleIds.has(d.id));
|
|
16009
|
+
const newSampleIds = newSamples.map((d) => d.id);
|
|
16010
|
+
if (newSamples.length > 0) {
|
|
16011
|
+
await storage.set(
|
|
16012
|
+
storage.getPluginKey(resourceName, "training", "data", modelName, `v${version}`),
|
|
16013
|
+
{
|
|
16014
|
+
modelName,
|
|
16015
|
+
version,
|
|
16016
|
+
samples: newSamples,
|
|
16017
|
+
// Only new samples
|
|
16018
|
+
features: modelConfig.features,
|
|
16019
|
+
target: modelConfig.target,
|
|
16020
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
16021
|
+
},
|
|
16022
|
+
{ behavior: "body-only" }
|
|
16023
|
+
// Dataset goes to S3 body
|
|
16024
|
+
);
|
|
16025
|
+
}
|
|
16026
|
+
const historyEntry = {
|
|
16027
|
+
version,
|
|
16028
|
+
totalSamples: processedData.length,
|
|
16029
|
+
// Total cumulative
|
|
16030
|
+
newSamples: newSamples.length,
|
|
16031
|
+
// Only new in this version
|
|
16032
|
+
sampleIds: Array.from(currentSampleIds),
|
|
16033
|
+
// All IDs for this version
|
|
16034
|
+
newSampleIds,
|
|
16035
|
+
// IDs of new samples
|
|
16036
|
+
storageKey: newSamples.length > 0 ? `training/data/${modelName}/v${version}` : null,
|
|
16037
|
+
metrics: {
|
|
16038
|
+
loss: modelStats.loss,
|
|
16039
|
+
accuracy: modelStats.accuracy,
|
|
16040
|
+
r2: modelStats.r2
|
|
16041
|
+
},
|
|
16042
|
+
trainedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
16043
|
+
};
|
|
16044
|
+
history.push(historyEntry);
|
|
16045
|
+
await storage.set(
|
|
16046
|
+
storage.getPluginKey(resourceName, "training", "history", modelName),
|
|
16047
|
+
{
|
|
16048
|
+
modelName,
|
|
16049
|
+
type: "training_history",
|
|
16050
|
+
totalTrainings: history.length,
|
|
16051
|
+
latestVersion: version,
|
|
16052
|
+
history,
|
|
16053
|
+
// Array of metadata entries (not full data)
|
|
16054
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
16055
|
+
},
|
|
16056
|
+
{ behavior: "body-overflow" }
|
|
16057
|
+
// History metadata
|
|
16058
|
+
);
|
|
15965
16059
|
if (this.config.verbose) {
|
|
15966
|
-
console.log(`[MLPlugin]
|
|
16060
|
+
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})`);
|
|
15967
16061
|
}
|
|
15968
16062
|
} else {
|
|
15969
|
-
await storage.
|
|
15970
|
-
modelName,
|
|
15971
|
-
|
|
15972
|
-
|
|
15973
|
-
|
|
15974
|
-
|
|
15975
|
-
|
|
15976
|
-
|
|
15977
|
-
|
|
16063
|
+
await storage.set(
|
|
16064
|
+
storage.getPluginKey(resourceName, "training", "data", modelName, "latest"),
|
|
16065
|
+
{
|
|
16066
|
+
modelName,
|
|
16067
|
+
type: "training_data",
|
|
16068
|
+
samples: processedData,
|
|
16069
|
+
features: modelConfig.features,
|
|
16070
|
+
target: modelConfig.target,
|
|
16071
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
16072
|
+
},
|
|
16073
|
+
{ behavior: "body-only" }
|
|
16074
|
+
);
|
|
15978
16075
|
if (this.config.verbose) {
|
|
15979
|
-
console.log(`[MLPlugin] Saved training data for "${modelName}" (${
|
|
16076
|
+
console.log(`[MLPlugin] Saved training data for "${modelName}" (${processedData.length} samples) to S3 (resource=${resourceName}/plugin=ml/training/data/${modelName}/latest)`);
|
|
15980
16077
|
}
|
|
15981
16078
|
}
|
|
15982
16079
|
} catch (error) {
|
|
@@ -15990,17 +16087,22 @@ class MLPlugin extends Plugin {
|
|
|
15990
16087
|
async _loadModel(modelName) {
|
|
15991
16088
|
try {
|
|
15992
16089
|
const storage = this.getStorage();
|
|
16090
|
+
const modelConfig = this.config.models[modelName];
|
|
16091
|
+
const resourceName = modelConfig.resource;
|
|
15993
16092
|
const enableVersioning = this.config.enableVersioning;
|
|
15994
16093
|
if (enableVersioning) {
|
|
15995
|
-
const [okRef, errRef, activeRef] = await tryFn(
|
|
16094
|
+
const [okRef, errRef, activeRef] = await tryFn(
|
|
16095
|
+
() => storage.get(storage.getPluginKey(resourceName, "metadata", modelName, "active"))
|
|
16096
|
+
);
|
|
15996
16097
|
if (okRef && activeRef && activeRef.version) {
|
|
15997
16098
|
const version = activeRef.version;
|
|
15998
|
-
const [ok, err, versionData] = await tryFn(
|
|
15999
|
-
|
|
16000
|
-
|
|
16001
|
-
|
|
16099
|
+
const [ok, err, versionData] = await tryFn(
|
|
16100
|
+
() => storage.get(storage.getPluginKey(resourceName, "models", modelName, `v${version}`))
|
|
16101
|
+
);
|
|
16102
|
+
if (ok && versionData && versionData.modelData) {
|
|
16103
|
+
await this.models[modelName].import(versionData.modelData);
|
|
16002
16104
|
if (this.config.verbose) {
|
|
16003
|
-
console.log(`[MLPlugin] Loaded model "${modelName}" v${version} (active) from
|
|
16105
|
+
console.log(`[MLPlugin] Loaded model "${modelName}" v${version} (active) from S3 (resource=${resourceName}/plugin=ml/models/${modelName}/v${version})`);
|
|
16004
16106
|
}
|
|
16005
16107
|
return;
|
|
16006
16108
|
}
|
|
@@ -16008,12 +16110,13 @@ class MLPlugin extends Plugin {
|
|
|
16008
16110
|
const versionInfo = this.modelVersions.get(modelName);
|
|
16009
16111
|
if (versionInfo && versionInfo.latestVersion > 0) {
|
|
16010
16112
|
const version = versionInfo.latestVersion;
|
|
16011
|
-
const [ok, err, versionData] = await tryFn(
|
|
16012
|
-
|
|
16013
|
-
|
|
16014
|
-
|
|
16113
|
+
const [ok, err, versionData] = await tryFn(
|
|
16114
|
+
() => storage.get(storage.getPluginKey(resourceName, "models", modelName, `v${version}`))
|
|
16115
|
+
);
|
|
16116
|
+
if (ok && versionData && versionData.modelData) {
|
|
16117
|
+
await this.models[modelName].import(versionData.modelData);
|
|
16015
16118
|
if (this.config.verbose) {
|
|
16016
|
-
console.log(`[MLPlugin] Loaded model "${modelName}" v${version} (latest) from
|
|
16119
|
+
console.log(`[MLPlugin] Loaded model "${modelName}" v${version} (latest) from S3`);
|
|
16017
16120
|
}
|
|
16018
16121
|
return;
|
|
16019
16122
|
}
|
|
@@ -16022,17 +16125,18 @@ class MLPlugin extends Plugin {
|
|
|
16022
16125
|
console.log(`[MLPlugin] No saved model versions found for "${modelName}"`);
|
|
16023
16126
|
}
|
|
16024
16127
|
} else {
|
|
16025
|
-
const [ok, err, record] = await tryFn(
|
|
16026
|
-
|
|
16128
|
+
const [ok, err, record] = await tryFn(
|
|
16129
|
+
() => storage.get(storage.getPluginKey(resourceName, "models", modelName, "latest"))
|
|
16130
|
+
);
|
|
16131
|
+
if (!ok || !record || !record.modelData) {
|
|
16027
16132
|
if (this.config.verbose) {
|
|
16028
16133
|
console.log(`[MLPlugin] No saved model found for "${modelName}"`);
|
|
16029
16134
|
}
|
|
16030
16135
|
return;
|
|
16031
16136
|
}
|
|
16032
|
-
|
|
16033
|
-
await this.models[modelName].import(modelData);
|
|
16137
|
+
await this.models[modelName].import(record.modelData);
|
|
16034
16138
|
if (this.config.verbose) {
|
|
16035
|
-
console.log(`[MLPlugin] Loaded model "${modelName}" from
|
|
16139
|
+
console.log(`[MLPlugin] Loaded model "${modelName}" from S3 (resource=${resourceName}/plugin=ml/models/${modelName}/latest)`);
|
|
16036
16140
|
}
|
|
16037
16141
|
}
|
|
16038
16142
|
} catch (error) {
|
|
@@ -16040,27 +16144,68 @@ class MLPlugin extends Plugin {
|
|
|
16040
16144
|
}
|
|
16041
16145
|
}
|
|
16042
16146
|
/**
|
|
16043
|
-
* Load training data from plugin storage
|
|
16147
|
+
* Load training data from plugin storage (reconstructs specific version from incremental data)
|
|
16044
16148
|
* @param {string} modelName - Model name
|
|
16149
|
+
* @param {number} version - Version number (optional, defaults to latest)
|
|
16045
16150
|
* @returns {Object|null} Training data or null if not found
|
|
16046
16151
|
*/
|
|
16047
|
-
async getTrainingData(modelName) {
|
|
16152
|
+
async getTrainingData(modelName, version = null) {
|
|
16048
16153
|
try {
|
|
16049
16154
|
const storage = this.getStorage();
|
|
16050
|
-
const
|
|
16051
|
-
|
|
16155
|
+
const modelConfig = this.config.models[modelName];
|
|
16156
|
+
const resourceName = modelConfig.resource;
|
|
16157
|
+
const enableVersioning = this.config.enableVersioning;
|
|
16158
|
+
if (!enableVersioning) {
|
|
16159
|
+
const [ok, err, record] = await tryFn(
|
|
16160
|
+
() => storage.get(storage.getPluginKey(resourceName, "training", "data", modelName, "latest"))
|
|
16161
|
+
);
|
|
16162
|
+
if (!ok || !record) {
|
|
16163
|
+
if (this.config.verbose) {
|
|
16164
|
+
console.log(`[MLPlugin] No saved training data found for "${modelName}"`);
|
|
16165
|
+
}
|
|
16166
|
+
return null;
|
|
16167
|
+
}
|
|
16168
|
+
return {
|
|
16169
|
+
modelName: record.modelName,
|
|
16170
|
+
samples: record.samples,
|
|
16171
|
+
features: record.features,
|
|
16172
|
+
target: record.target,
|
|
16173
|
+
data: record.samples,
|
|
16174
|
+
savedAt: record.savedAt
|
|
16175
|
+
};
|
|
16176
|
+
}
|
|
16177
|
+
const [okHistory, errHistory, historyData] = await tryFn(
|
|
16178
|
+
() => storage.get(storage.getPluginKey(resourceName, "training", "history", modelName))
|
|
16179
|
+
);
|
|
16180
|
+
if (!okHistory || !historyData || !historyData.history) {
|
|
16052
16181
|
if (this.config.verbose) {
|
|
16053
|
-
console.log(`[MLPlugin] No
|
|
16182
|
+
console.log(`[MLPlugin] No training history found for "${modelName}"`);
|
|
16054
16183
|
}
|
|
16055
16184
|
return null;
|
|
16056
16185
|
}
|
|
16186
|
+
const targetVersion = version || historyData.latestVersion;
|
|
16187
|
+
const reconstructedSamples = [];
|
|
16188
|
+
for (const entry of historyData.history) {
|
|
16189
|
+
if (entry.version > targetVersion) break;
|
|
16190
|
+
if (entry.storageKey && entry.newSamples > 0) {
|
|
16191
|
+
const [ok, err, versionData] = await tryFn(
|
|
16192
|
+
() => storage.get(storage.getPluginKey(resourceName, "training", "data", modelName, `v${entry.version}`))
|
|
16193
|
+
);
|
|
16194
|
+
if (ok && versionData && versionData.samples) {
|
|
16195
|
+
reconstructedSamples.push(...versionData.samples);
|
|
16196
|
+
}
|
|
16197
|
+
}
|
|
16198
|
+
}
|
|
16199
|
+
const targetEntry = historyData.history.find((e) => e.version === targetVersion);
|
|
16057
16200
|
return {
|
|
16058
|
-
modelName
|
|
16059
|
-
|
|
16060
|
-
|
|
16061
|
-
|
|
16062
|
-
|
|
16063
|
-
|
|
16201
|
+
modelName,
|
|
16202
|
+
version: targetVersion,
|
|
16203
|
+
samples: reconstructedSamples,
|
|
16204
|
+
totalSamples: reconstructedSamples.length,
|
|
16205
|
+
features: modelConfig.features,
|
|
16206
|
+
target: modelConfig.target,
|
|
16207
|
+
metrics: targetEntry?.metrics,
|
|
16208
|
+
savedAt: targetEntry?.trainedAt
|
|
16064
16209
|
};
|
|
16065
16210
|
} catch (error) {
|
|
16066
16211
|
console.error(`[MLPlugin] Failed to load training data for "${modelName}":`, error.message);
|
|
@@ -16068,15 +16213,29 @@ class MLPlugin extends Plugin {
|
|
|
16068
16213
|
}
|
|
16069
16214
|
}
|
|
16070
16215
|
/**
|
|
16071
|
-
* Delete model from plugin storage
|
|
16216
|
+
* Delete model from plugin storage (all versions)
|
|
16072
16217
|
* @private
|
|
16073
16218
|
*/
|
|
16074
16219
|
async _deleteModel(modelName) {
|
|
16075
16220
|
try {
|
|
16076
16221
|
const storage = this.getStorage();
|
|
16077
|
-
|
|
16222
|
+
const modelConfig = this.config.models[modelName];
|
|
16223
|
+
const resourceName = modelConfig.resource;
|
|
16224
|
+
const enableVersioning = this.config.enableVersioning;
|
|
16225
|
+
if (enableVersioning) {
|
|
16226
|
+
const versionInfo = this.modelVersions.get(modelName);
|
|
16227
|
+
if (versionInfo && versionInfo.latestVersion > 0) {
|
|
16228
|
+
for (let v = 1; v <= versionInfo.latestVersion; v++) {
|
|
16229
|
+
await storage.delete(storage.getPluginKey(resourceName, "models", modelName, `v${v}`));
|
|
16230
|
+
}
|
|
16231
|
+
}
|
|
16232
|
+
await storage.delete(storage.getPluginKey(resourceName, "metadata", modelName, "active"));
|
|
16233
|
+
await storage.delete(storage.getPluginKey(resourceName, "metadata", modelName, "versions"));
|
|
16234
|
+
} else {
|
|
16235
|
+
await storage.delete(storage.getPluginKey(resourceName, "models", modelName, "latest"));
|
|
16236
|
+
}
|
|
16078
16237
|
if (this.config.verbose) {
|
|
16079
|
-
console.log(`[MLPlugin] Deleted model "${modelName}" from plugin
|
|
16238
|
+
console.log(`[MLPlugin] Deleted model "${modelName}" from S3 (resource=${resourceName}/plugin=ml/models/${modelName}/)`);
|
|
16080
16239
|
}
|
|
16081
16240
|
} catch (error) {
|
|
16082
16241
|
if (this.config.verbose) {
|
|
@@ -16085,15 +16244,32 @@ class MLPlugin extends Plugin {
|
|
|
16085
16244
|
}
|
|
16086
16245
|
}
|
|
16087
16246
|
/**
|
|
16088
|
-
* Delete training data from plugin storage
|
|
16247
|
+
* Delete training data from plugin storage (all versions)
|
|
16089
16248
|
* @private
|
|
16090
16249
|
*/
|
|
16091
16250
|
async _deleteTrainingData(modelName) {
|
|
16092
16251
|
try {
|
|
16093
16252
|
const storage = this.getStorage();
|
|
16094
|
-
|
|
16253
|
+
const modelConfig = this.config.models[modelName];
|
|
16254
|
+
const resourceName = modelConfig.resource;
|
|
16255
|
+
const enableVersioning = this.config.enableVersioning;
|
|
16256
|
+
if (enableVersioning) {
|
|
16257
|
+
const [ok, err, historyData] = await tryFn(
|
|
16258
|
+
() => storage.get(storage.getPluginKey(resourceName, "training", "history", modelName))
|
|
16259
|
+
);
|
|
16260
|
+
if (ok && historyData && historyData.history) {
|
|
16261
|
+
for (const entry of historyData.history) {
|
|
16262
|
+
if (entry.storageKey) {
|
|
16263
|
+
await storage.delete(storage.getPluginKey(resourceName, "training", "data", modelName, `v${entry.version}`));
|
|
16264
|
+
}
|
|
16265
|
+
}
|
|
16266
|
+
}
|
|
16267
|
+
await storage.delete(storage.getPluginKey(resourceName, "training", "history", modelName));
|
|
16268
|
+
} else {
|
|
16269
|
+
await storage.delete(storage.getPluginKey(resourceName, "training", "data", modelName, "latest"));
|
|
16270
|
+
}
|
|
16095
16271
|
if (this.config.verbose) {
|
|
16096
|
-
console.log(`[MLPlugin] Deleted training data for "${modelName}" from plugin
|
|
16272
|
+
console.log(`[MLPlugin] Deleted training data for "${modelName}" from S3 (resource=${resourceName}/plugin=ml/training/)`);
|
|
16097
16273
|
}
|
|
16098
16274
|
} catch (error) {
|
|
16099
16275
|
if (this.config.verbose) {
|
|
@@ -16112,17 +16288,18 @@ class MLPlugin extends Plugin {
|
|
|
16112
16288
|
}
|
|
16113
16289
|
try {
|
|
16114
16290
|
const storage = this.getStorage();
|
|
16291
|
+
const modelConfig = this.config.models[modelName];
|
|
16292
|
+
const resourceName = modelConfig.resource;
|
|
16115
16293
|
const versionInfo = this.modelVersions.get(modelName) || { latestVersion: 0 };
|
|
16116
16294
|
const versions = [];
|
|
16117
16295
|
for (let v = 1; v <= versionInfo.latestVersion; v++) {
|
|
16118
|
-
const [ok, err, versionData] = await tryFn(() => storage.get(`
|
|
16296
|
+
const [ok, err, versionData] = await tryFn(() => storage.get(storage.getPluginKey(resourceName, "models", modelName, `v${v}`)));
|
|
16119
16297
|
if (ok && versionData) {
|
|
16120
|
-
const metrics = versionData.metrics ? JSON.parse(versionData.metrics) : {};
|
|
16121
16298
|
versions.push({
|
|
16122
16299
|
version: v,
|
|
16123
16300
|
savedAt: versionData.savedAt,
|
|
16124
16301
|
isCurrent: v === versionInfo.currentVersion,
|
|
16125
|
-
metrics
|
|
16302
|
+
metrics: versionData.metrics
|
|
16126
16303
|
});
|
|
16127
16304
|
}
|
|
16128
16305
|
}
|
|
@@ -16146,12 +16323,16 @@ class MLPlugin extends Plugin {
|
|
|
16146
16323
|
}
|
|
16147
16324
|
try {
|
|
16148
16325
|
const storage = this.getStorage();
|
|
16149
|
-
const
|
|
16326
|
+
const modelConfig = this.config.models[modelName];
|
|
16327
|
+
const resourceName = modelConfig.resource;
|
|
16328
|
+
const [ok, err, versionData] = await tryFn(() => storage.get(storage.getPluginKey(resourceName, "models", modelName, `v${version}`)));
|
|
16150
16329
|
if (!ok || !versionData) {
|
|
16151
16330
|
throw new MLError(`Version ${version} not found for model "${modelName}"`, { modelName, version });
|
|
16152
16331
|
}
|
|
16153
|
-
|
|
16154
|
-
|
|
16332
|
+
if (!versionData.modelData) {
|
|
16333
|
+
throw new MLError(`Model data not found in version ${version}`, { modelName, version });
|
|
16334
|
+
}
|
|
16335
|
+
await this.models[modelName].import(versionData.modelData);
|
|
16155
16336
|
const versionInfo = this.modelVersions.get(modelName);
|
|
16156
16337
|
if (versionInfo) {
|
|
16157
16338
|
versionInfo.currentVersion = version;
|
|
@@ -16179,10 +16360,12 @@ class MLPlugin extends Plugin {
|
|
|
16179
16360
|
if (!this.config.enableVersioning) {
|
|
16180
16361
|
throw new MLError("Versioning is not enabled", { modelName });
|
|
16181
16362
|
}
|
|
16363
|
+
const modelConfig = this.config.models[modelName];
|
|
16364
|
+
const resourceName = modelConfig.resource;
|
|
16182
16365
|
await this.loadModelVersion(modelName, version);
|
|
16183
16366
|
await this._updateVersionInfo(modelName, version);
|
|
16184
16367
|
const storage = this.getStorage();
|
|
16185
|
-
await storage.
|
|
16368
|
+
await storage.set(storage.getPluginKey(resourceName, "metadata", modelName, "active"), {
|
|
16186
16369
|
modelName,
|
|
16187
16370
|
version,
|
|
16188
16371
|
type: "reference",
|
|
@@ -16204,7 +16387,9 @@ class MLPlugin extends Plugin {
|
|
|
16204
16387
|
}
|
|
16205
16388
|
try {
|
|
16206
16389
|
const storage = this.getStorage();
|
|
16207
|
-
const
|
|
16390
|
+
const modelConfig = this.config.models[modelName];
|
|
16391
|
+
const resourceName = modelConfig.resource;
|
|
16392
|
+
const [ok, err, historyData] = await tryFn(() => storage.get(storage.getPluginKey(resourceName, "training", "history", modelName)));
|
|
16208
16393
|
if (!ok || !historyData) {
|
|
16209
16394
|
return null;
|
|
16210
16395
|
}
|
|
@@ -16233,8 +16418,10 @@ class MLPlugin extends Plugin {
|
|
|
16233
16418
|
}
|
|
16234
16419
|
try {
|
|
16235
16420
|
const storage = this.getStorage();
|
|
16236
|
-
const
|
|
16237
|
-
const
|
|
16421
|
+
const modelConfig = this.config.models[modelName];
|
|
16422
|
+
const resourceName = modelConfig.resource;
|
|
16423
|
+
const [ok1, err1, v1Data] = await tryFn(() => storage.get(storage.getPluginKey(resourceName, "models", modelName, `v${version1}`)));
|
|
16424
|
+
const [ok2, err2, v2Data] = await tryFn(() => storage.get(storage.getPluginKey(resourceName, "models", modelName, `v${version2}`)));
|
|
16238
16425
|
if (!ok1 || !v1Data) {
|
|
16239
16426
|
throw new MLError(`Version ${version1} not found`, { modelName, version: version1 });
|
|
16240
16427
|
}
|
|
@@ -16690,7 +16877,7 @@ class RelationPlugin extends Plugin {
|
|
|
16690
16877
|
if (this.verbose) {
|
|
16691
16878
|
console.log(`[RelationPlugin] Installed with ${Object.keys(this.relations).length} resources`);
|
|
16692
16879
|
}
|
|
16693
|
-
this.emit("installed", {
|
|
16880
|
+
this.emit("db:plugin:installed", {
|
|
16694
16881
|
plugin: "RelationPlugin",
|
|
16695
16882
|
resources: Object.keys(this.relations)
|
|
16696
16883
|
});
|
|
@@ -20265,7 +20452,7 @@ class S3Client extends EventEmitter {
|
|
|
20265
20452
|
return client;
|
|
20266
20453
|
}
|
|
20267
20454
|
async sendCommand(command) {
|
|
20268
|
-
this.emit("
|
|
20455
|
+
this.emit("cl:request", command.constructor.name, command.input);
|
|
20269
20456
|
const [ok, err, response] = await tryFn(() => this.client.send(command));
|
|
20270
20457
|
if (!ok) {
|
|
20271
20458
|
const bucket = this.config.bucket;
|
|
@@ -20277,7 +20464,7 @@ class S3Client extends EventEmitter {
|
|
|
20277
20464
|
commandInput: command.input
|
|
20278
20465
|
});
|
|
20279
20466
|
}
|
|
20280
|
-
this.emit("
|
|
20467
|
+
this.emit("cl:response", command.constructor.name, response, command.input);
|
|
20281
20468
|
return response;
|
|
20282
20469
|
}
|
|
20283
20470
|
async putObject({ key, metadata, contentType, body, contentEncoding, contentLength, ifMatch }) {
|
|
@@ -20302,7 +20489,7 @@ class S3Client extends EventEmitter {
|
|
|
20302
20489
|
if (contentLength !== void 0) options.ContentLength = contentLength;
|
|
20303
20490
|
if (ifMatch !== void 0) options.IfMatch = ifMatch;
|
|
20304
20491
|
const [ok, err, response] = await tryFn(() => this.sendCommand(new PutObjectCommand(options)));
|
|
20305
|
-
this.emit("
|
|
20492
|
+
this.emit("cl:PutObject", err || response, { key, metadata, contentType, body, contentEncoding, contentLength });
|
|
20306
20493
|
if (!ok) {
|
|
20307
20494
|
throw mapAwsError(err, {
|
|
20308
20495
|
bucket: this.config.bucket,
|
|
@@ -20330,7 +20517,7 @@ class S3Client extends EventEmitter {
|
|
|
20330
20517
|
}
|
|
20331
20518
|
return res;
|
|
20332
20519
|
});
|
|
20333
|
-
this.emit("
|
|
20520
|
+
this.emit("cl:GetObject", err || response, { key });
|
|
20334
20521
|
if (!ok) {
|
|
20335
20522
|
throw mapAwsError(err, {
|
|
20336
20523
|
bucket: this.config.bucket,
|
|
@@ -20358,7 +20545,7 @@ class S3Client extends EventEmitter {
|
|
|
20358
20545
|
}
|
|
20359
20546
|
return res;
|
|
20360
20547
|
});
|
|
20361
|
-
this.emit("
|
|
20548
|
+
this.emit("cl:HeadObject", err || response, { key });
|
|
20362
20549
|
if (!ok) {
|
|
20363
20550
|
throw mapAwsError(err, {
|
|
20364
20551
|
bucket: this.config.bucket,
|
|
@@ -20391,7 +20578,7 @@ class S3Client extends EventEmitter {
|
|
|
20391
20578
|
options.ContentType = contentType;
|
|
20392
20579
|
}
|
|
20393
20580
|
const [ok, err, response] = await tryFn(() => this.sendCommand(new CopyObjectCommand(options)));
|
|
20394
|
-
this.emit("
|
|
20581
|
+
this.emit("cl:CopyObject", err || response, { from, to, metadataDirective });
|
|
20395
20582
|
if (!ok) {
|
|
20396
20583
|
throw mapAwsError(err, {
|
|
20397
20584
|
bucket: this.config.bucket,
|
|
@@ -20416,7 +20603,7 @@ class S3Client extends EventEmitter {
|
|
|
20416
20603
|
Key: keyPrefix ? path$1.join(keyPrefix, key) : key
|
|
20417
20604
|
};
|
|
20418
20605
|
const [ok, err, response] = await tryFn(() => this.sendCommand(new DeleteObjectCommand(options)));
|
|
20419
|
-
this.emit("
|
|
20606
|
+
this.emit("cl:DeleteObject", err || response, { key });
|
|
20420
20607
|
if (!ok) {
|
|
20421
20608
|
throw mapAwsError(err, {
|
|
20422
20609
|
bucket: this.config.bucket,
|
|
@@ -20456,7 +20643,7 @@ class S3Client extends EventEmitter {
|
|
|
20456
20643
|
deleted: results,
|
|
20457
20644
|
notFound: errors
|
|
20458
20645
|
};
|
|
20459
|
-
this.emit("
|
|
20646
|
+
this.emit("cl:DeleteObjects", report, keys);
|
|
20460
20647
|
return report;
|
|
20461
20648
|
}
|
|
20462
20649
|
/**
|
|
@@ -20486,7 +20673,7 @@ class S3Client extends EventEmitter {
|
|
|
20486
20673
|
const deleteResponse = await this.client.send(deleteCommand);
|
|
20487
20674
|
const deletedCount = deleteResponse.Deleted ? deleteResponse.Deleted.length : 0;
|
|
20488
20675
|
totalDeleted += deletedCount;
|
|
20489
|
-
this.emit("
|
|
20676
|
+
this.emit("cl:DeleteAll", {
|
|
20490
20677
|
prefix,
|
|
20491
20678
|
batch: deletedCount,
|
|
20492
20679
|
total: totalDeleted
|
|
@@ -20494,7 +20681,7 @@ class S3Client extends EventEmitter {
|
|
|
20494
20681
|
}
|
|
20495
20682
|
continuationToken = listResponse.IsTruncated ? listResponse.NextContinuationToken : void 0;
|
|
20496
20683
|
} while (continuationToken);
|
|
20497
|
-
this.emit("
|
|
20684
|
+
this.emit("cl:DeleteAllComplete", {
|
|
20498
20685
|
prefix,
|
|
20499
20686
|
totalDeleted
|
|
20500
20687
|
});
|
|
@@ -20525,7 +20712,7 @@ class S3Client extends EventEmitter {
|
|
|
20525
20712
|
if (!ok) {
|
|
20526
20713
|
throw new UnknownError("Unknown error in listObjects", { prefix, bucket: this.config.bucket, original: err });
|
|
20527
20714
|
}
|
|
20528
|
-
this.emit("
|
|
20715
|
+
this.emit("cl:ListObjects", response, options);
|
|
20529
20716
|
return response;
|
|
20530
20717
|
}
|
|
20531
20718
|
async count({ prefix } = {}) {
|
|
@@ -20542,7 +20729,7 @@ class S3Client extends EventEmitter {
|
|
|
20542
20729
|
truncated = response.IsTruncated || false;
|
|
20543
20730
|
continuationToken = response.NextContinuationToken;
|
|
20544
20731
|
}
|
|
20545
|
-
this.emit("
|
|
20732
|
+
this.emit("cl:Count", count, { prefix });
|
|
20546
20733
|
return count;
|
|
20547
20734
|
}
|
|
20548
20735
|
async getAllKeys({ prefix } = {}) {
|
|
@@ -20564,7 +20751,7 @@ class S3Client extends EventEmitter {
|
|
|
20564
20751
|
if (this.config.keyPrefix) {
|
|
20565
20752
|
keys = keys.map((x) => x.replace(this.config.keyPrefix, "")).map((x) => x.startsWith("/") ? x.replace(`/`, "") : x);
|
|
20566
20753
|
}
|
|
20567
|
-
this.emit("
|
|
20754
|
+
this.emit("cl:GetAllKeys", keys, { prefix });
|
|
20568
20755
|
return keys;
|
|
20569
20756
|
}
|
|
20570
20757
|
async getContinuationTokenAfterOffset(params = {}) {
|
|
@@ -20593,7 +20780,7 @@ class S3Client extends EventEmitter {
|
|
|
20593
20780
|
break;
|
|
20594
20781
|
}
|
|
20595
20782
|
}
|
|
20596
|
-
this.emit("
|
|
20783
|
+
this.emit("cl:GetContinuationTokenAfterOffset", continuationToken || null, params);
|
|
20597
20784
|
return continuationToken || null;
|
|
20598
20785
|
}
|
|
20599
20786
|
async getKeysPage(params = {}) {
|
|
@@ -20611,7 +20798,7 @@ class S3Client extends EventEmitter {
|
|
|
20611
20798
|
offset
|
|
20612
20799
|
});
|
|
20613
20800
|
if (!continuationToken) {
|
|
20614
|
-
this.emit("
|
|
20801
|
+
this.emit("cl:GetKeysPage", [], params);
|
|
20615
20802
|
return [];
|
|
20616
20803
|
}
|
|
20617
20804
|
}
|
|
@@ -20634,7 +20821,7 @@ class S3Client extends EventEmitter {
|
|
|
20634
20821
|
if (this.config.keyPrefix) {
|
|
20635
20822
|
keys = keys.map((x) => x.replace(this.config.keyPrefix, "")).map((x) => x.startsWith("/") ? x.replace(`/`, "") : x);
|
|
20636
20823
|
}
|
|
20637
|
-
this.emit("
|
|
20824
|
+
this.emit("cl:GetKeysPage", keys, params);
|
|
20638
20825
|
return keys;
|
|
20639
20826
|
}
|
|
20640
20827
|
async moveAllObjects({ prefixFrom, prefixTo }) {
|
|
@@ -20652,7 +20839,7 @@ class S3Client extends EventEmitter {
|
|
|
20652
20839
|
}
|
|
20653
20840
|
return to;
|
|
20654
20841
|
});
|
|
20655
|
-
this.emit("
|
|
20842
|
+
this.emit("cl:MoveAllObjects", { results, errors }, { prefixFrom, prefixTo });
|
|
20656
20843
|
if (errors.length > 0) {
|
|
20657
20844
|
throw new UnknownError("Some objects could not be moved", {
|
|
20658
20845
|
bucket: this.config.bucket,
|
|
@@ -23507,6 +23694,20 @@ ${errorDetails}`,
|
|
|
23507
23694
|
if (typeof body === "object") return Buffer.byteLength(JSON.stringify(body), "utf8");
|
|
23508
23695
|
return Buffer.byteLength(String(body), "utf8");
|
|
23509
23696
|
}
|
|
23697
|
+
/**
|
|
23698
|
+
* Emit standardized events with optional ID-specific variant
|
|
23699
|
+
*
|
|
23700
|
+
* @private
|
|
23701
|
+
* @param {string} event - Event name
|
|
23702
|
+
* @param {Object} payload - Event payload
|
|
23703
|
+
* @param {string} [id] - Optional ID for ID-specific events
|
|
23704
|
+
*/
|
|
23705
|
+
_emitStandardized(event, payload, id = null) {
|
|
23706
|
+
this.emit(event, payload);
|
|
23707
|
+
if (id) {
|
|
23708
|
+
this.emit(`${event}:${id}`, payload);
|
|
23709
|
+
}
|
|
23710
|
+
}
|
|
23510
23711
|
/**
|
|
23511
23712
|
* Insert a new resource object
|
|
23512
23713
|
* @param {Object} attributes - Resource attributes
|
|
@@ -23647,11 +23848,11 @@ ${errorDetails}`,
|
|
|
23647
23848
|
for (const hook of nonPartitionHooks) {
|
|
23648
23849
|
finalResult = await hook(finalResult);
|
|
23649
23850
|
}
|
|
23650
|
-
this.
|
|
23851
|
+
this._emitStandardized("inserted", finalResult, finalResult?.id || insertedObject?.id);
|
|
23651
23852
|
return finalResult;
|
|
23652
23853
|
} else {
|
|
23653
23854
|
const finalResult = await this.executeHooks("afterInsert", insertedObject);
|
|
23654
|
-
this.
|
|
23855
|
+
this._emitStandardized("inserted", finalResult, finalResult?.id || insertedObject?.id);
|
|
23655
23856
|
return finalResult;
|
|
23656
23857
|
}
|
|
23657
23858
|
}
|
|
@@ -23715,7 +23916,7 @@ ${errorDetails}`,
|
|
|
23715
23916
|
data = await this.applyVersionMapping(data, objectVersion, this.version);
|
|
23716
23917
|
}
|
|
23717
23918
|
data = await this.executeHooks("afterGet", data);
|
|
23718
|
-
this.
|
|
23919
|
+
this._emitWithDeprecation("get", "fetched", data, data.id);
|
|
23719
23920
|
const value = data;
|
|
23720
23921
|
return value;
|
|
23721
23922
|
}
|
|
@@ -23966,19 +24167,19 @@ ${errorDetails}`,
|
|
|
23966
24167
|
for (const hook of nonPartitionHooks) {
|
|
23967
24168
|
finalResult = await hook(finalResult);
|
|
23968
24169
|
}
|
|
23969
|
-
this.
|
|
24170
|
+
this._emitStandardized("updated", {
|
|
23970
24171
|
...updatedData,
|
|
23971
24172
|
$before: { ...originalData },
|
|
23972
24173
|
$after: { ...finalResult }
|
|
23973
|
-
});
|
|
24174
|
+
}, updatedData.id);
|
|
23974
24175
|
return finalResult;
|
|
23975
24176
|
} else {
|
|
23976
24177
|
const finalResult = await this.executeHooks("afterUpdate", updatedData);
|
|
23977
|
-
this.
|
|
24178
|
+
this._emitStandardized("updated", {
|
|
23978
24179
|
...updatedData,
|
|
23979
24180
|
$before: { ...originalData },
|
|
23980
24181
|
$after: { ...finalResult }
|
|
23981
|
-
});
|
|
24182
|
+
}, updatedData.id);
|
|
23982
24183
|
return finalResult;
|
|
23983
24184
|
}
|
|
23984
24185
|
}
|
|
@@ -24377,11 +24578,11 @@ ${errorDetails}`,
|
|
|
24377
24578
|
for (const hook of nonPartitionHooks) {
|
|
24378
24579
|
finalResult = await hook(finalResult);
|
|
24379
24580
|
}
|
|
24380
|
-
this.
|
|
24581
|
+
this._emitStandardized("updated", {
|
|
24381
24582
|
...updatedData,
|
|
24382
24583
|
$before: { ...originalData },
|
|
24383
24584
|
$after: { ...finalResult }
|
|
24384
|
-
});
|
|
24585
|
+
}, updatedData.id);
|
|
24385
24586
|
return {
|
|
24386
24587
|
success: true,
|
|
24387
24588
|
data: finalResult,
|
|
@@ -24390,11 +24591,11 @@ ${errorDetails}`,
|
|
|
24390
24591
|
} else {
|
|
24391
24592
|
await this.handlePartitionReferenceUpdates(oldData, newData);
|
|
24392
24593
|
const finalResult = await this.executeHooks("afterUpdate", updatedData);
|
|
24393
|
-
this.
|
|
24594
|
+
this._emitStandardized("updated", {
|
|
24394
24595
|
...updatedData,
|
|
24395
24596
|
$before: { ...originalData },
|
|
24396
24597
|
$after: { ...finalResult }
|
|
24397
|
-
});
|
|
24598
|
+
}, updatedData.id);
|
|
24398
24599
|
return {
|
|
24399
24600
|
success: true,
|
|
24400
24601
|
data: finalResult,
|
|
@@ -24425,11 +24626,11 @@ ${errorDetails}`,
|
|
|
24425
24626
|
await this.executeHooks("beforeDelete", objectData);
|
|
24426
24627
|
const key = this.getResourceKey(id);
|
|
24427
24628
|
const [ok2, err2, response] = await tryFn(() => this.client.deleteObject(key));
|
|
24428
|
-
this.
|
|
24629
|
+
this._emitWithDeprecation("delete", "deleted", {
|
|
24429
24630
|
...objectData,
|
|
24430
24631
|
$before: { ...objectData },
|
|
24431
24632
|
$after: null
|
|
24432
|
-
});
|
|
24633
|
+
}, id);
|
|
24433
24634
|
if (deleteError) {
|
|
24434
24635
|
throw mapAwsError(deleteError, {
|
|
24435
24636
|
bucket: this.client.config.bucket,
|
|
@@ -24553,7 +24754,7 @@ ${errorDetails}`,
|
|
|
24553
24754
|
}
|
|
24554
24755
|
const count = await this.client.count({ prefix });
|
|
24555
24756
|
await this.executeHooks("afterCount", { count, partition, partitionValues });
|
|
24556
|
-
this.
|
|
24757
|
+
this._emitWithDeprecation("count", "counted", count);
|
|
24557
24758
|
return count;
|
|
24558
24759
|
}
|
|
24559
24760
|
/**
|
|
@@ -24576,7 +24777,7 @@ ${errorDetails}`,
|
|
|
24576
24777
|
const result = await this.insert(attributes);
|
|
24577
24778
|
return result;
|
|
24578
24779
|
});
|
|
24579
|
-
this.
|
|
24780
|
+
this._emitWithDeprecation("insertMany", "inserted-many", objects.length);
|
|
24580
24781
|
return results;
|
|
24581
24782
|
}
|
|
24582
24783
|
/**
|
|
@@ -24611,7 +24812,7 @@ ${errorDetails}`,
|
|
|
24611
24812
|
return response;
|
|
24612
24813
|
});
|
|
24613
24814
|
await this.executeHooks("afterDeleteMany", { ids, results });
|
|
24614
|
-
this.
|
|
24815
|
+
this._emitWithDeprecation("deleteMany", "deleted-many", ids.length);
|
|
24615
24816
|
return results;
|
|
24616
24817
|
}
|
|
24617
24818
|
async deleteAll() {
|
|
@@ -24620,7 +24821,7 @@ ${errorDetails}`,
|
|
|
24620
24821
|
}
|
|
24621
24822
|
const prefix = `resource=${this.name}/data`;
|
|
24622
24823
|
const deletedCount = await this.client.deleteAll({ prefix });
|
|
24623
|
-
this.
|
|
24824
|
+
this._emitWithDeprecation("deleteAll", "deleted-all", {
|
|
24624
24825
|
version: this.version,
|
|
24625
24826
|
prefix,
|
|
24626
24827
|
deletedCount
|
|
@@ -24637,7 +24838,7 @@ ${errorDetails}`,
|
|
|
24637
24838
|
}
|
|
24638
24839
|
const prefix = `resource=${this.name}`;
|
|
24639
24840
|
const deletedCount = await this.client.deleteAll({ prefix });
|
|
24640
|
-
this.
|
|
24841
|
+
this._emitWithDeprecation("deleteAllData", "deleted-all-data", {
|
|
24641
24842
|
resource: this.name,
|
|
24642
24843
|
prefix,
|
|
24643
24844
|
deletedCount
|
|
@@ -24707,7 +24908,7 @@ ${errorDetails}`,
|
|
|
24707
24908
|
const idPart = parts.find((part) => part.startsWith("id="));
|
|
24708
24909
|
return idPart ? idPart.replace("id=", "") : null;
|
|
24709
24910
|
}).filter(Boolean);
|
|
24710
|
-
this.
|
|
24911
|
+
this._emitWithDeprecation("listIds", "listed-ids", ids.length);
|
|
24711
24912
|
return ids;
|
|
24712
24913
|
}
|
|
24713
24914
|
/**
|
|
@@ -24749,12 +24950,12 @@ ${errorDetails}`,
|
|
|
24749
24950
|
const [ok, err, ids] = await tryFn(() => this.listIds({ limit, offset }));
|
|
24750
24951
|
if (!ok) throw err;
|
|
24751
24952
|
const results = await this.processListResults(ids, "main");
|
|
24752
|
-
this.
|
|
24953
|
+
this._emitWithDeprecation("list", "listed", { count: results.length, errors: 0 });
|
|
24753
24954
|
return results;
|
|
24754
24955
|
}
|
|
24755
24956
|
async listPartition({ partition, partitionValues, limit, offset = 0 }) {
|
|
24756
24957
|
if (!this.config.partitions?.[partition]) {
|
|
24757
|
-
this.
|
|
24958
|
+
this._emitWithDeprecation("list", "listed", { partition, partitionValues, count: 0, errors: 0 });
|
|
24758
24959
|
return [];
|
|
24759
24960
|
}
|
|
24760
24961
|
const partitionDef = this.config.partitions[partition];
|
|
@@ -24764,7 +24965,7 @@ ${errorDetails}`,
|
|
|
24764
24965
|
const ids = this.extractIdsFromKeys(keys).slice(offset);
|
|
24765
24966
|
const filteredIds = limit ? ids.slice(0, limit) : ids;
|
|
24766
24967
|
const results = await this.processPartitionResults(filteredIds, partition, partitionDef, keys);
|
|
24767
|
-
this.
|
|
24968
|
+
this._emitWithDeprecation("list", "listed", { partition, partitionValues, count: results.length, errors: 0 });
|
|
24768
24969
|
return results;
|
|
24769
24970
|
}
|
|
24770
24971
|
/**
|
|
@@ -24809,7 +25010,7 @@ ${errorDetails}`,
|
|
|
24809
25010
|
}
|
|
24810
25011
|
return this.handleResourceError(err, id, context);
|
|
24811
25012
|
});
|
|
24812
|
-
this.
|
|
25013
|
+
this._emitWithDeprecation("list", "listed", { count: results.length, errors: 0 });
|
|
24813
25014
|
return results;
|
|
24814
25015
|
}
|
|
24815
25016
|
/**
|
|
@@ -24872,10 +25073,10 @@ ${errorDetails}`,
|
|
|
24872
25073
|
*/
|
|
24873
25074
|
handleListError(error, { partition, partitionValues }) {
|
|
24874
25075
|
if (error.message.includes("Partition '") && error.message.includes("' not found")) {
|
|
24875
|
-
this.
|
|
25076
|
+
this._emitWithDeprecation("list", "listed", { partition, partitionValues, count: 0, errors: 1 });
|
|
24876
25077
|
return [];
|
|
24877
25078
|
}
|
|
24878
|
-
this.
|
|
25079
|
+
this._emitWithDeprecation("list", "listed", { partition, partitionValues, count: 0, errors: 1 });
|
|
24879
25080
|
return [];
|
|
24880
25081
|
}
|
|
24881
25082
|
/**
|
|
@@ -24908,7 +25109,7 @@ ${errorDetails}`,
|
|
|
24908
25109
|
throw err;
|
|
24909
25110
|
});
|
|
24910
25111
|
const finalResults = await this.executeHooks("afterGetMany", results);
|
|
24911
|
-
this.
|
|
25112
|
+
this._emitWithDeprecation("getMany", "fetched-many", ids.length);
|
|
24912
25113
|
return finalResults;
|
|
24913
25114
|
}
|
|
24914
25115
|
/**
|
|
@@ -24994,7 +25195,7 @@ ${errorDetails}`,
|
|
|
24994
25195
|
hasTotalItems: totalItems !== null
|
|
24995
25196
|
}
|
|
24996
25197
|
};
|
|
24997
|
-
this.
|
|
25198
|
+
this._emitWithDeprecation("page", "paginated", result2);
|
|
24998
25199
|
return result2;
|
|
24999
25200
|
});
|
|
25000
25201
|
if (ok) return result;
|
|
@@ -25064,7 +25265,7 @@ ${errorDetails}`,
|
|
|
25064
25265
|
contentType
|
|
25065
25266
|
}));
|
|
25066
25267
|
if (!ok2) throw err2;
|
|
25067
|
-
this.
|
|
25268
|
+
this._emitWithDeprecation("setContent", "content-set", { id, contentType, contentLength: buffer.length }, id);
|
|
25068
25269
|
return updatedData;
|
|
25069
25270
|
}
|
|
25070
25271
|
/**
|
|
@@ -25093,7 +25294,7 @@ ${errorDetails}`,
|
|
|
25093
25294
|
}
|
|
25094
25295
|
const buffer = Buffer.from(await response.Body.transformToByteArray());
|
|
25095
25296
|
const contentType = response.ContentType || null;
|
|
25096
|
-
this.
|
|
25297
|
+
this._emitWithDeprecation("content", "content-fetched", { id, contentLength: buffer.length, contentType }, id);
|
|
25097
25298
|
return {
|
|
25098
25299
|
buffer,
|
|
25099
25300
|
contentType
|
|
@@ -25125,7 +25326,7 @@ ${errorDetails}`,
|
|
|
25125
25326
|
metadata: existingMetadata
|
|
25126
25327
|
}));
|
|
25127
25328
|
if (!ok2) throw err2;
|
|
25128
|
-
this.
|
|
25329
|
+
this._emitWithDeprecation("deleteContent", "content-deleted", id, id);
|
|
25129
25330
|
return response;
|
|
25130
25331
|
}
|
|
25131
25332
|
/**
|
|
@@ -25437,7 +25638,7 @@ ${errorDetails}`,
|
|
|
25437
25638
|
const data = await this.get(id);
|
|
25438
25639
|
data._partition = partitionName;
|
|
25439
25640
|
data._partitionValues = partitionValues;
|
|
25440
|
-
this.
|
|
25641
|
+
this._emitWithDeprecation("getFromPartition", "partition-fetched", data, data.id);
|
|
25441
25642
|
return data;
|
|
25442
25643
|
}
|
|
25443
25644
|
/**
|
|
@@ -25846,7 +26047,7 @@ class Database extends EventEmitter {
|
|
|
25846
26047
|
})();
|
|
25847
26048
|
this.version = "1";
|
|
25848
26049
|
this.s3dbVersion = (() => {
|
|
25849
|
-
const [ok, err, version] = tryFn(() => true ? "13.1
|
|
26050
|
+
const [ok, err, version] = tryFn(() => true ? "13.2.1" : "latest");
|
|
25850
26051
|
return ok ? version : "latest";
|
|
25851
26052
|
})();
|
|
25852
26053
|
this._resourcesMap = {};
|
|
@@ -26016,12 +26217,12 @@ class Database extends EventEmitter {
|
|
|
26016
26217
|
}
|
|
26017
26218
|
}
|
|
26018
26219
|
if (definitionChanges.length > 0) {
|
|
26019
|
-
this.emit("
|
|
26220
|
+
this.emit("db:resource-definitions-changed", {
|
|
26020
26221
|
changes: definitionChanges,
|
|
26021
26222
|
metadata: this.savedMetadata
|
|
26022
26223
|
});
|
|
26023
26224
|
}
|
|
26024
|
-
this.emit("connected", /* @__PURE__ */ new Date());
|
|
26225
|
+
this.emit("db:connected", /* @__PURE__ */ new Date());
|
|
26025
26226
|
}
|
|
26026
26227
|
/**
|
|
26027
26228
|
* Detect changes in resource definitions compared to saved metadata
|
|
@@ -26235,7 +26436,7 @@ class Database extends EventEmitter {
|
|
|
26235
26436
|
if (index > -1) {
|
|
26236
26437
|
this.pluginList.splice(index, 1);
|
|
26237
26438
|
}
|
|
26238
|
-
this.emit("plugin
|
|
26439
|
+
this.emit("db:plugin:uninstalled", { name: pluginName, plugin });
|
|
26239
26440
|
}
|
|
26240
26441
|
async uploadMetadataFile() {
|
|
26241
26442
|
const metadata = {
|
|
@@ -26294,7 +26495,7 @@ class Database extends EventEmitter {
|
|
|
26294
26495
|
contentType: "application/json"
|
|
26295
26496
|
});
|
|
26296
26497
|
this.savedMetadata = metadata;
|
|
26297
|
-
this.emit("
|
|
26498
|
+
this.emit("db:metadata-uploaded", metadata);
|
|
26298
26499
|
}
|
|
26299
26500
|
blankMetadataStructure() {
|
|
26300
26501
|
return {
|
|
@@ -26551,7 +26752,7 @@ class Database extends EventEmitter {
|
|
|
26551
26752
|
body: JSON.stringify(metadata, null, 2),
|
|
26552
26753
|
contentType: "application/json"
|
|
26553
26754
|
});
|
|
26554
|
-
this.emit("
|
|
26755
|
+
this.emit("db:metadata-healed", { healingLog, metadata });
|
|
26555
26756
|
if (this.verbose) {
|
|
26556
26757
|
console.warn("S3DB: Successfully uploaded healed metadata");
|
|
26557
26758
|
}
|
|
@@ -26691,7 +26892,7 @@ class Database extends EventEmitter {
|
|
|
26691
26892
|
if (!existingVersionData || existingVersionData.hash !== newHash) {
|
|
26692
26893
|
await this.uploadMetadataFile();
|
|
26693
26894
|
}
|
|
26694
|
-
this.emit("
|
|
26895
|
+
this.emit("db:resource:updated", name);
|
|
26695
26896
|
return existingResource;
|
|
26696
26897
|
}
|
|
26697
26898
|
const existingMetadata = this.savedMetadata?.resources?.[name];
|
|
@@ -26728,7 +26929,7 @@ class Database extends EventEmitter {
|
|
|
26728
26929
|
this._applyMiddlewares(resource, middlewares);
|
|
26729
26930
|
}
|
|
26730
26931
|
await this.uploadMetadataFile();
|
|
26731
|
-
this.emit("
|
|
26932
|
+
this.emit("db:resource:created", name);
|
|
26732
26933
|
return resource;
|
|
26733
26934
|
}
|
|
26734
26935
|
/**
|
|
@@ -26892,7 +27093,7 @@ class Database extends EventEmitter {
|
|
|
26892
27093
|
if (this.client && typeof this.client.removeAllListeners === "function") {
|
|
26893
27094
|
this.client.removeAllListeners();
|
|
26894
27095
|
}
|
|
26895
|
-
await this.emit("disconnected", /* @__PURE__ */ new Date());
|
|
27096
|
+
await this.emit("db:disconnected", /* @__PURE__ */ new Date());
|
|
26896
27097
|
this.removeAllListeners();
|
|
26897
27098
|
if (this._exitListener && typeof process !== "undefined") {
|
|
26898
27099
|
process.off("exit", this._exitListener);
|
|
@@ -27004,7 +27205,7 @@ class Database extends EventEmitter {
|
|
|
27004
27205
|
for (const hook of hooks) {
|
|
27005
27206
|
const [ok, error] = await tryFn(() => hook({ database: this, ...context }));
|
|
27006
27207
|
if (!ok) {
|
|
27007
|
-
this.emit("
|
|
27208
|
+
this.emit("db:hook-error", { event, error, context });
|
|
27008
27209
|
if (this.strictHooks) {
|
|
27009
27210
|
throw new DatabaseError(`Hook execution failed for event '${event}': ${error.message}`, {
|
|
27010
27211
|
event,
|
|
@@ -28580,7 +28781,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28580
28781
|
if (this.config.verbose) {
|
|
28581
28782
|
console.warn(`[ReplicatorPlugin] Insert event failed for resource ${resource.name}: ${error.message}`);
|
|
28582
28783
|
}
|
|
28583
|
-
this.emit("error", { operation: "insert", error: error.message, resource: resource.name });
|
|
28784
|
+
this.emit("plg:replicator:error", { operation: "insert", error: error.message, resource: resource.name });
|
|
28584
28785
|
}
|
|
28585
28786
|
};
|
|
28586
28787
|
const updateHandler = async (data, beforeData) => {
|
|
@@ -28593,7 +28794,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28593
28794
|
if (this.config.verbose) {
|
|
28594
28795
|
console.warn(`[ReplicatorPlugin] Update event failed for resource ${resource.name}: ${error.message}`);
|
|
28595
28796
|
}
|
|
28596
|
-
this.emit("error", { operation: "update", error: error.message, resource: resource.name });
|
|
28797
|
+
this.emit("plg:replicator:error", { operation: "update", error: error.message, resource: resource.name });
|
|
28597
28798
|
}
|
|
28598
28799
|
};
|
|
28599
28800
|
const deleteHandler = async (data) => {
|
|
@@ -28604,7 +28805,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28604
28805
|
if (this.config.verbose) {
|
|
28605
28806
|
console.warn(`[ReplicatorPlugin] Delete event failed for resource ${resource.name}: ${error.message}`);
|
|
28606
28807
|
}
|
|
28607
|
-
this.emit("error", { operation: "delete", error: error.message, resource: resource.name });
|
|
28808
|
+
this.emit("plg:replicator:error", { operation: "delete", error: error.message, resource: resource.name });
|
|
28608
28809
|
}
|
|
28609
28810
|
};
|
|
28610
28811
|
this.eventHandlers.set(resource.name, {
|
|
@@ -28725,7 +28926,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28725
28926
|
if (this.config.verbose) {
|
|
28726
28927
|
console.warn(`[ReplicatorPlugin] Failed to log error for ${resourceName}: ${logError.message}`);
|
|
28727
28928
|
}
|
|
28728
|
-
this.emit("
|
|
28929
|
+
this.emit("plg:replicator:log-error", {
|
|
28729
28930
|
replicator: replicator.name || replicator.id,
|
|
28730
28931
|
resourceName,
|
|
28731
28932
|
operation,
|
|
@@ -28750,7 +28951,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28750
28951
|
() => replicator.replicate(resourceName, operation, data, recordId, beforeData),
|
|
28751
28952
|
this.config.maxRetries
|
|
28752
28953
|
);
|
|
28753
|
-
this.emit("replicated", {
|
|
28954
|
+
this.emit("plg:replicator:replicated", {
|
|
28754
28955
|
replicator: replicator.name || replicator.id,
|
|
28755
28956
|
resourceName,
|
|
28756
28957
|
operation,
|
|
@@ -28766,7 +28967,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28766
28967
|
if (this.config.verbose) {
|
|
28767
28968
|
console.warn(`[ReplicatorPlugin] Replication failed for ${replicator.name || replicator.id} on ${resourceName}: ${error.message}`);
|
|
28768
28969
|
}
|
|
28769
|
-
this.emit("
|
|
28970
|
+
this.emit("plg:replicator:error", {
|
|
28770
28971
|
replicator: replicator.name || replicator.id,
|
|
28771
28972
|
resourceName,
|
|
28772
28973
|
operation,
|
|
@@ -28798,7 +28999,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28798
28999
|
if (this.config.verbose) {
|
|
28799
29000
|
console.warn(`[ReplicatorPlugin] Replicator item processing failed for ${replicator.name || replicator.id} on ${item.resourceName}: ${err.message}`);
|
|
28800
29001
|
}
|
|
28801
|
-
this.emit("
|
|
29002
|
+
this.emit("plg:replicator:error", {
|
|
28802
29003
|
replicator: replicator.name || replicator.id,
|
|
28803
29004
|
resourceName: item.resourceName,
|
|
28804
29005
|
operation: item.operation,
|
|
@@ -28810,7 +29011,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28810
29011
|
}
|
|
28811
29012
|
return { success: false, error: err.message };
|
|
28812
29013
|
}
|
|
28813
|
-
this.emit("replicated", {
|
|
29014
|
+
this.emit("plg:replicator:replicated", {
|
|
28814
29015
|
replicator: replicator.name || replicator.id,
|
|
28815
29016
|
resourceName: item.resourceName,
|
|
28816
29017
|
operation: item.operation,
|
|
@@ -28826,7 +29027,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28826
29027
|
if (this.config.verbose) {
|
|
28827
29028
|
console.warn(`[ReplicatorPlugin] Wrapper processing failed for ${replicator.name || replicator.id} on ${item.resourceName}: ${wrapperError.message}`);
|
|
28828
29029
|
}
|
|
28829
|
-
this.emit("
|
|
29030
|
+
this.emit("plg:replicator:error", {
|
|
28830
29031
|
replicator: replicator.name || replicator.id,
|
|
28831
29032
|
resourceName: item.resourceName,
|
|
28832
29033
|
operation: item.operation,
|
|
@@ -28844,7 +29045,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28844
29045
|
async logReplicator(item) {
|
|
28845
29046
|
const logRes = this.replicatorLog || this.database.resources[normalizeResourceName(this.config.replicatorLogResource)];
|
|
28846
29047
|
if (!logRes) {
|
|
28847
|
-
this.emit("replicator
|
|
29048
|
+
this.emit("plg:replicator:log-failed", { error: "replicator log resource not found", item });
|
|
28848
29049
|
return;
|
|
28849
29050
|
}
|
|
28850
29051
|
const logItem = {
|
|
@@ -28862,7 +29063,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28862
29063
|
if (this.config.verbose) {
|
|
28863
29064
|
console.warn(`[ReplicatorPlugin] Failed to log replicator item: ${err.message}`);
|
|
28864
29065
|
}
|
|
28865
|
-
this.emit("replicator
|
|
29066
|
+
this.emit("plg:replicator:log-failed", { error: err, item });
|
|
28866
29067
|
}
|
|
28867
29068
|
}
|
|
28868
29069
|
async updateReplicatorLog(logId, updates) {
|
|
@@ -28874,7 +29075,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28874
29075
|
});
|
|
28875
29076
|
});
|
|
28876
29077
|
if (!ok) {
|
|
28877
|
-
this.emit("replicator
|
|
29078
|
+
this.emit("plg:replicator:update-log-failed", { error: err.message, logId, updates });
|
|
28878
29079
|
}
|
|
28879
29080
|
}
|
|
28880
29081
|
// Utility methods
|
|
@@ -28958,7 +29159,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28958
29159
|
for (const resourceName in this.database.resources) {
|
|
28959
29160
|
if (normalizeResourceName(resourceName) === normalizeResourceName("plg_replicator_logs")) continue;
|
|
28960
29161
|
if (replicator.shouldReplicateResource(resourceName)) {
|
|
28961
|
-
this.emit("replicator
|
|
29162
|
+
this.emit("plg:replicator:sync-resource", { resourceName, replicatorId });
|
|
28962
29163
|
const resource = this.database.resources[resourceName];
|
|
28963
29164
|
let offset = 0;
|
|
28964
29165
|
const pageSize = this.config.batchSize || 100;
|
|
@@ -28974,7 +29175,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28974
29175
|
}
|
|
28975
29176
|
}
|
|
28976
29177
|
}
|
|
28977
|
-
this.emit("replicator
|
|
29178
|
+
this.emit("plg:replicator:sync-completed", { replicatorId, stats: this.stats });
|
|
28978
29179
|
}
|
|
28979
29180
|
async stop() {
|
|
28980
29181
|
const [ok, error] = await tryFn(async () => {
|
|
@@ -28989,7 +29190,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28989
29190
|
if (this.config.verbose) {
|
|
28990
29191
|
console.warn(`[ReplicatorPlugin] Failed to stop replicator ${replicator.name || replicator.id}: ${replicatorError.message}`);
|
|
28991
29192
|
}
|
|
28992
|
-
this.emit("
|
|
29193
|
+
this.emit("plg:replicator:stop-error", {
|
|
28993
29194
|
replicator: replicator.name || replicator.id || "unknown",
|
|
28994
29195
|
driver: replicator.driver || "unknown",
|
|
28995
29196
|
error: replicatorError.message
|
|
@@ -29020,7 +29221,7 @@ class ReplicatorPlugin extends Plugin {
|
|
|
29020
29221
|
if (this.config.verbose) {
|
|
29021
29222
|
console.warn(`[ReplicatorPlugin] Failed to stop plugin: ${error.message}`);
|
|
29022
29223
|
}
|
|
29023
|
-
this.emit("
|
|
29224
|
+
this.emit("plg:replicator:plugin-stop-error", {
|
|
29024
29225
|
error: error.message
|
|
29025
29226
|
});
|
|
29026
29227
|
}
|
|
@@ -29177,7 +29378,7 @@ class S3QueuePlugin extends Plugin {
|
|
|
29177
29378
|
if (this.config.verbose) {
|
|
29178
29379
|
console.log(`[S3QueuePlugin] Started ${concurrency} workers`);
|
|
29179
29380
|
}
|
|
29180
|
-
this.emit("workers
|
|
29381
|
+
this.emit("plg:s3-queue:workers-started", { concurrency, workerId: this.workerId });
|
|
29181
29382
|
}
|
|
29182
29383
|
async stopProcessing() {
|
|
29183
29384
|
if (!this.isRunning) return;
|
|
@@ -29192,7 +29393,7 @@ class S3QueuePlugin extends Plugin {
|
|
|
29192
29393
|
if (this.config.verbose) {
|
|
29193
29394
|
console.log("[S3QueuePlugin] Stopped all workers");
|
|
29194
29395
|
}
|
|
29195
|
-
this.emit("workers
|
|
29396
|
+
this.emit("plg:s3-queue:workers-stopped", { workerId: this.workerId });
|
|
29196
29397
|
}
|
|
29197
29398
|
createWorker(handler, workerIndex) {
|
|
29198
29399
|
return (async () => {
|
|
@@ -29360,7 +29561,7 @@ class S3QueuePlugin extends Plugin {
|
|
|
29360
29561
|
});
|
|
29361
29562
|
await this.completeMessage(message.queueId, result);
|
|
29362
29563
|
const duration = Date.now() - startTime;
|
|
29363
|
-
this.emit("message
|
|
29564
|
+
this.emit("plg:s3-queue:message-completed", {
|
|
29364
29565
|
queueId: message.queueId,
|
|
29365
29566
|
originalId: message.record.id,
|
|
29366
29567
|
duration,
|
|
@@ -29373,7 +29574,7 @@ class S3QueuePlugin extends Plugin {
|
|
|
29373
29574
|
const shouldRetry = message.attempts < message.maxAttempts;
|
|
29374
29575
|
if (shouldRetry) {
|
|
29375
29576
|
await this.retryMessage(message.queueId, message.attempts, error.message);
|
|
29376
|
-
this.emit("message
|
|
29577
|
+
this.emit("plg:s3-queue:message-retry", {
|
|
29377
29578
|
queueId: message.queueId,
|
|
29378
29579
|
originalId: message.record.id,
|
|
29379
29580
|
attempts: message.attempts,
|
|
@@ -29381,7 +29582,7 @@ class S3QueuePlugin extends Plugin {
|
|
|
29381
29582
|
});
|
|
29382
29583
|
} else {
|
|
29383
29584
|
await this.moveToDeadLetter(message.queueId, message.record, error.message);
|
|
29384
|
-
this.emit("message
|
|
29585
|
+
this.emit("plg:s3-queue:message-dead", {
|
|
29385
29586
|
queueId: message.queueId,
|
|
29386
29587
|
originalId: message.record.id,
|
|
29387
29588
|
error: error.message
|
|
@@ -29613,7 +29814,7 @@ class SchedulerPlugin extends Plugin {
|
|
|
29613
29814
|
});
|
|
29614
29815
|
}
|
|
29615
29816
|
await this._startScheduling();
|
|
29616
|
-
this.emit("initialized", { jobs: this.jobs.size });
|
|
29817
|
+
this.emit("db:plugin:initialized", { jobs: this.jobs.size });
|
|
29617
29818
|
}
|
|
29618
29819
|
async _createJobHistoryResource() {
|
|
29619
29820
|
const [ok] = await tryFn(() => this.database.createResource({
|
|
@@ -29751,7 +29952,7 @@ class SchedulerPlugin extends Plugin {
|
|
|
29751
29952
|
if (this.config.onJobStart) {
|
|
29752
29953
|
await this._executeHook(this.config.onJobStart, jobName, context);
|
|
29753
29954
|
}
|
|
29754
|
-
this.emit("
|
|
29955
|
+
this.emit("plg:scheduler:job-start", { jobName, executionId, startTime });
|
|
29755
29956
|
let attempt = 0;
|
|
29756
29957
|
let lastError = null;
|
|
29757
29958
|
let result = null;
|
|
@@ -29818,7 +30019,7 @@ class SchedulerPlugin extends Plugin {
|
|
|
29818
30019
|
} else if (status !== "success" && this.config.onJobError) {
|
|
29819
30020
|
await this._executeHook(this.config.onJobError, jobName, lastError, attempt);
|
|
29820
30021
|
}
|
|
29821
|
-
this.emit("
|
|
30022
|
+
this.emit("plg:scheduler:job-complete", {
|
|
29822
30023
|
jobName,
|
|
29823
30024
|
executionId,
|
|
29824
30025
|
status,
|
|
@@ -29904,7 +30105,7 @@ class SchedulerPlugin extends Plugin {
|
|
|
29904
30105
|
}
|
|
29905
30106
|
job.enabled = true;
|
|
29906
30107
|
this._scheduleNextExecution(jobName);
|
|
29907
|
-
this.emit("
|
|
30108
|
+
this.emit("plg:scheduler:job-enabled", { jobName });
|
|
29908
30109
|
}
|
|
29909
30110
|
/**
|
|
29910
30111
|
* Disable a job
|
|
@@ -29925,7 +30126,7 @@ class SchedulerPlugin extends Plugin {
|
|
|
29925
30126
|
clearTimeout(timer);
|
|
29926
30127
|
this.timers.delete(jobName);
|
|
29927
30128
|
}
|
|
29928
|
-
this.emit("
|
|
30129
|
+
this.emit("plg:scheduler:job-disabled", { jobName });
|
|
29929
30130
|
}
|
|
29930
30131
|
/**
|
|
29931
30132
|
* Get job status and statistics
|
|
@@ -30063,7 +30264,7 @@ class SchedulerPlugin extends Plugin {
|
|
|
30063
30264
|
if (job.enabled) {
|
|
30064
30265
|
this._scheduleNextExecution(jobName);
|
|
30065
30266
|
}
|
|
30066
|
-
this.emit("
|
|
30267
|
+
this.emit("plg:scheduler:job-added", { jobName });
|
|
30067
30268
|
}
|
|
30068
30269
|
/**
|
|
30069
30270
|
* Remove a job
|
|
@@ -30086,7 +30287,7 @@ class SchedulerPlugin extends Plugin {
|
|
|
30086
30287
|
this.jobs.delete(jobName);
|
|
30087
30288
|
this.statistics.delete(jobName);
|
|
30088
30289
|
this.activeJobs.delete(jobName);
|
|
30089
|
-
this.emit("
|
|
30290
|
+
this.emit("plg:scheduler:job-removed", { jobName });
|
|
30090
30291
|
}
|
|
30091
30292
|
/**
|
|
30092
30293
|
* Get plugin instance by name (for job actions that need other plugins)
|
|
@@ -30127,9 +30328,14 @@ class SchedulerPlugin extends Plugin {
|
|
|
30127
30328
|
}
|
|
30128
30329
|
}
|
|
30129
30330
|
|
|
30331
|
+
var scheduler_plugin = /*#__PURE__*/Object.freeze({
|
|
30332
|
+
__proto__: null,
|
|
30333
|
+
SchedulerPlugin: SchedulerPlugin
|
|
30334
|
+
});
|
|
30335
|
+
|
|
30130
30336
|
class StateMachineError extends S3dbError {
|
|
30131
30337
|
constructor(message, details = {}) {
|
|
30132
|
-
const { currentState, targetState, resourceName, operation = "unknown", ...rest } = details;
|
|
30338
|
+
const { currentState, targetState, resourceName, operation = "unknown", retriable, ...rest } = details;
|
|
30133
30339
|
let description = details.description;
|
|
30134
30340
|
if (!description) {
|
|
30135
30341
|
description = `
|
|
@@ -30154,6 +30360,158 @@ Docs: https://github.com/forattini-dev/s3db.js/blob/main/docs/plugins/state-mach
|
|
|
30154
30360
|
`.trim();
|
|
30155
30361
|
}
|
|
30156
30362
|
super(message, { ...rest, currentState, targetState, resourceName, operation, description });
|
|
30363
|
+
if (retriable !== void 0) {
|
|
30364
|
+
this.retriable = retriable;
|
|
30365
|
+
}
|
|
30366
|
+
}
|
|
30367
|
+
}
|
|
30368
|
+
|
|
30369
|
+
const RETRIABLE = "RETRIABLE";
|
|
30370
|
+
const NON_RETRIABLE = "NON_RETRIABLE";
|
|
30371
|
+
const RETRIABLE_NETWORK_CODES = /* @__PURE__ */ new Set([
|
|
30372
|
+
"ECONNREFUSED",
|
|
30373
|
+
"ETIMEDOUT",
|
|
30374
|
+
"ECONNRESET",
|
|
30375
|
+
"EPIPE",
|
|
30376
|
+
"ENOTFOUND",
|
|
30377
|
+
"NetworkError",
|
|
30378
|
+
"NETWORK_ERROR",
|
|
30379
|
+
"TimeoutError",
|
|
30380
|
+
"TIMEOUT"
|
|
30381
|
+
]);
|
|
30382
|
+
const RETRIABLE_AWS_CODES = /* @__PURE__ */ new Set([
|
|
30383
|
+
"ThrottlingException",
|
|
30384
|
+
"TooManyRequestsException",
|
|
30385
|
+
"RequestLimitExceeded",
|
|
30386
|
+
"ProvisionedThroughputExceededException",
|
|
30387
|
+
"RequestThrottledException",
|
|
30388
|
+
"SlowDown",
|
|
30389
|
+
"ServiceUnavailable"
|
|
30390
|
+
]);
|
|
30391
|
+
const RETRIABLE_AWS_CONFLICTS = /* @__PURE__ */ new Set([
|
|
30392
|
+
"ConditionalCheckFailedException",
|
|
30393
|
+
"TransactionConflictException"
|
|
30394
|
+
]);
|
|
30395
|
+
const RETRIABLE_STATUS_CODES = /* @__PURE__ */ new Set([
|
|
30396
|
+
429,
|
|
30397
|
+
// Too Many Requests
|
|
30398
|
+
500,
|
|
30399
|
+
// Internal Server Error
|
|
30400
|
+
502,
|
|
30401
|
+
// Bad Gateway
|
|
30402
|
+
503,
|
|
30403
|
+
// Service Unavailable
|
|
30404
|
+
504,
|
|
30405
|
+
// Gateway Timeout
|
|
30406
|
+
507,
|
|
30407
|
+
// Insufficient Storage
|
|
30408
|
+
509
|
|
30409
|
+
// Bandwidth Limit Exceeded
|
|
30410
|
+
]);
|
|
30411
|
+
const NON_RETRIABLE_ERROR_NAMES = /* @__PURE__ */ new Set([
|
|
30412
|
+
"ValidationError",
|
|
30413
|
+
"StateMachineError",
|
|
30414
|
+
"SchemaError",
|
|
30415
|
+
"AuthenticationError",
|
|
30416
|
+
"PermissionError",
|
|
30417
|
+
"BusinessLogicError",
|
|
30418
|
+
"InvalidStateTransition"
|
|
30419
|
+
]);
|
|
30420
|
+
const NON_RETRIABLE_STATUS_CODES = /* @__PURE__ */ new Set([
|
|
30421
|
+
400,
|
|
30422
|
+
// Bad Request
|
|
30423
|
+
401,
|
|
30424
|
+
// Unauthorized
|
|
30425
|
+
403,
|
|
30426
|
+
// Forbidden
|
|
30427
|
+
404,
|
|
30428
|
+
// Not Found
|
|
30429
|
+
405,
|
|
30430
|
+
// Method Not Allowed
|
|
30431
|
+
406,
|
|
30432
|
+
// Not Acceptable
|
|
30433
|
+
409,
|
|
30434
|
+
// Conflict
|
|
30435
|
+
410,
|
|
30436
|
+
// Gone
|
|
30437
|
+
422
|
|
30438
|
+
// Unprocessable Entity
|
|
30439
|
+
]);
|
|
30440
|
+
class ErrorClassifier {
|
|
30441
|
+
/**
|
|
30442
|
+
* Classify an error as RETRIABLE or NON_RETRIABLE
|
|
30443
|
+
*
|
|
30444
|
+
* @param {Error} error - The error to classify
|
|
30445
|
+
* @param {Object} options - Classification options
|
|
30446
|
+
* @param {Array<string>} options.retryableErrors - Custom retriable error names/codes
|
|
30447
|
+
* @param {Array<string>} options.nonRetriableErrors - Custom non-retriable error names/codes
|
|
30448
|
+
* @returns {string} 'RETRIABLE' or 'NON_RETRIABLE'
|
|
30449
|
+
*/
|
|
30450
|
+
static classify(error, options = {}) {
|
|
30451
|
+
if (!error) return NON_RETRIABLE;
|
|
30452
|
+
const {
|
|
30453
|
+
retryableErrors = [],
|
|
30454
|
+
nonRetriableErrors = []
|
|
30455
|
+
} = options;
|
|
30456
|
+
if (retryableErrors.length > 0) {
|
|
30457
|
+
const isCustomRetriable = retryableErrors.some(
|
|
30458
|
+
(errType) => error.code === errType || error.name === errType || error.message?.includes(errType)
|
|
30459
|
+
);
|
|
30460
|
+
if (isCustomRetriable) return RETRIABLE;
|
|
30461
|
+
}
|
|
30462
|
+
if (nonRetriableErrors.length > 0) {
|
|
30463
|
+
const isCustomNonRetriable = nonRetriableErrors.some(
|
|
30464
|
+
(errType) => error.code === errType || error.name === errType || error.message?.includes(errType)
|
|
30465
|
+
);
|
|
30466
|
+
if (isCustomNonRetriable) return NON_RETRIABLE;
|
|
30467
|
+
}
|
|
30468
|
+
if (error.retriable === false) return NON_RETRIABLE;
|
|
30469
|
+
if (error.retriable === true) return RETRIABLE;
|
|
30470
|
+
if (NON_RETRIABLE_ERROR_NAMES.has(error.name)) {
|
|
30471
|
+
return NON_RETRIABLE;
|
|
30472
|
+
}
|
|
30473
|
+
if (error.statusCode && NON_RETRIABLE_STATUS_CODES.has(error.statusCode)) {
|
|
30474
|
+
return NON_RETRIABLE;
|
|
30475
|
+
}
|
|
30476
|
+
if (error.code && RETRIABLE_NETWORK_CODES.has(error.code)) {
|
|
30477
|
+
return RETRIABLE;
|
|
30478
|
+
}
|
|
30479
|
+
if (error.code && RETRIABLE_AWS_CODES.has(error.code)) {
|
|
30480
|
+
return RETRIABLE;
|
|
30481
|
+
}
|
|
30482
|
+
if (error.code && RETRIABLE_AWS_CONFLICTS.has(error.code)) {
|
|
30483
|
+
return RETRIABLE;
|
|
30484
|
+
}
|
|
30485
|
+
if (error.statusCode && RETRIABLE_STATUS_CODES.has(error.statusCode)) {
|
|
30486
|
+
return RETRIABLE;
|
|
30487
|
+
}
|
|
30488
|
+
if (error.message && typeof error.message === "string") {
|
|
30489
|
+
const lowerMessage = error.message.toLowerCase();
|
|
30490
|
+
if (lowerMessage.includes("timeout") || lowerMessage.includes("timed out") || lowerMessage.includes("network") || lowerMessage.includes("connection")) {
|
|
30491
|
+
return RETRIABLE;
|
|
30492
|
+
}
|
|
30493
|
+
}
|
|
30494
|
+
return RETRIABLE;
|
|
30495
|
+
}
|
|
30496
|
+
/**
|
|
30497
|
+
* Check if an error is retriable
|
|
30498
|
+
*
|
|
30499
|
+
* @param {Error} error - The error to check
|
|
30500
|
+
* @param {Object} options - Classification options
|
|
30501
|
+
* @returns {boolean} true if retriable
|
|
30502
|
+
*/
|
|
30503
|
+
static isRetriable(error, options = {}) {
|
|
30504
|
+
return this.classify(error, options) === RETRIABLE;
|
|
30505
|
+
}
|
|
30506
|
+
/**
|
|
30507
|
+
* Check if an error is non-retriable
|
|
30508
|
+
*
|
|
30509
|
+
* @param {Error} error - The error to check
|
|
30510
|
+
* @param {Object} options - Classification options
|
|
30511
|
+
* @returns {boolean} true if non-retriable
|
|
30512
|
+
*/
|
|
30513
|
+
static isNonRetriable(error, options = {}) {
|
|
30514
|
+
return this.classify(error, options) === NON_RETRIABLE;
|
|
30157
30515
|
}
|
|
30158
30516
|
}
|
|
30159
30517
|
|
|
@@ -30174,11 +30532,23 @@ class StateMachinePlugin extends Plugin {
|
|
|
30174
30532
|
workerId: options.workerId || "default",
|
|
30175
30533
|
lockTimeout: options.lockTimeout || 1e3,
|
|
30176
30534
|
// Wait up to 1s for lock
|
|
30177
|
-
lockTTL: options.lockTTL || 5
|
|
30535
|
+
lockTTL: options.lockTTL || 5,
|
|
30178
30536
|
// Lock expires after 5s (prevent deadlock)
|
|
30537
|
+
// Global retry configuration for action execution
|
|
30538
|
+
retryConfig: options.retryConfig || null,
|
|
30539
|
+
// Trigger system configuration
|
|
30540
|
+
enableScheduler: options.enableScheduler || false,
|
|
30541
|
+
schedulerConfig: options.schedulerConfig || {},
|
|
30542
|
+
enableDateTriggers: options.enableDateTriggers !== false,
|
|
30543
|
+
enableFunctionTriggers: options.enableFunctionTriggers !== false,
|
|
30544
|
+
enableEventTriggers: options.enableEventTriggers !== false,
|
|
30545
|
+
triggerCheckInterval: options.triggerCheckInterval || 6e4
|
|
30546
|
+
// Check triggers every 60s by default
|
|
30179
30547
|
};
|
|
30180
30548
|
this.database = null;
|
|
30181
30549
|
this.machines = /* @__PURE__ */ new Map();
|
|
30550
|
+
this.triggerIntervals = [];
|
|
30551
|
+
this.schedulerPlugin = null;
|
|
30182
30552
|
this._validateConfiguration();
|
|
30183
30553
|
}
|
|
30184
30554
|
_validateConfiguration() {
|
|
@@ -30227,7 +30597,8 @@ class StateMachinePlugin extends Plugin {
|
|
|
30227
30597
|
// entityId -> currentState
|
|
30228
30598
|
});
|
|
30229
30599
|
}
|
|
30230
|
-
|
|
30600
|
+
await this._setupTriggers();
|
|
30601
|
+
this.emit("db:plugin:initialized", { machines: Array.from(this.machines.keys()) });
|
|
30231
30602
|
}
|
|
30232
30603
|
async _createStateResources() {
|
|
30233
30604
|
const [logOk] = await tryFn(() => this.database.createResource({
|
|
@@ -30258,6 +30629,8 @@ class StateMachinePlugin extends Plugin {
|
|
|
30258
30629
|
currentState: "string|required",
|
|
30259
30630
|
context: "json|default:{}",
|
|
30260
30631
|
lastTransition: "string|default:null",
|
|
30632
|
+
triggerCounts: "json|default:{}",
|
|
30633
|
+
// Track trigger execution counts
|
|
30261
30634
|
updatedAt: "string|required"
|
|
30262
30635
|
},
|
|
30263
30636
|
behavior: "body-overflow"
|
|
@@ -30321,7 +30694,7 @@ class StateMachinePlugin extends Plugin {
|
|
|
30321
30694
|
if (targetStateConfig && targetStateConfig.entry) {
|
|
30322
30695
|
await this._executeAction(targetStateConfig.entry, context, event, machineId, entityId);
|
|
30323
30696
|
}
|
|
30324
|
-
this.emit("transition", {
|
|
30697
|
+
this.emit("plg:state-machine:transition", {
|
|
30325
30698
|
machineId,
|
|
30326
30699
|
entityId,
|
|
30327
30700
|
from: currentState,
|
|
@@ -30347,14 +30720,97 @@ class StateMachinePlugin extends Plugin {
|
|
|
30347
30720
|
}
|
|
30348
30721
|
return;
|
|
30349
30722
|
}
|
|
30350
|
-
const
|
|
30351
|
-
|
|
30352
|
-
|
|
30353
|
-
|
|
30354
|
-
|
|
30355
|
-
|
|
30723
|
+
const machine = this.machines.get(machineId);
|
|
30724
|
+
const currentState = await this.getState(machineId, entityId);
|
|
30725
|
+
const stateConfig = machine?.config?.states?.[currentState];
|
|
30726
|
+
const retryConfig = {
|
|
30727
|
+
...this.config.retryConfig || {},
|
|
30728
|
+
...machine?.config?.retryConfig || {},
|
|
30729
|
+
...stateConfig?.retryConfig || {}
|
|
30730
|
+
};
|
|
30731
|
+
const maxAttempts = retryConfig.maxAttempts ?? 0;
|
|
30732
|
+
const retryEnabled = maxAttempts > 0;
|
|
30733
|
+
let attempt = 0;
|
|
30734
|
+
while (attempt <= maxAttempts) {
|
|
30735
|
+
try {
|
|
30736
|
+
const result = await action(context, event, { database: this.database, machineId, entityId });
|
|
30737
|
+
if (attempt > 0) {
|
|
30738
|
+
this.emit("plg:state-machine:action-retry-success", {
|
|
30739
|
+
machineId,
|
|
30740
|
+
entityId,
|
|
30741
|
+
action: actionName,
|
|
30742
|
+
attempts: attempt + 1,
|
|
30743
|
+
state: currentState
|
|
30744
|
+
});
|
|
30745
|
+
if (this.config.verbose) {
|
|
30746
|
+
console.log(`[StateMachinePlugin] Action '${actionName}' succeeded after ${attempt + 1} attempts`);
|
|
30747
|
+
}
|
|
30748
|
+
}
|
|
30749
|
+
return result;
|
|
30750
|
+
} catch (error) {
|
|
30751
|
+
if (!retryEnabled) {
|
|
30752
|
+
if (this.config.verbose) {
|
|
30753
|
+
console.error(`[StateMachinePlugin] Action '${actionName}' failed:`, error.message);
|
|
30754
|
+
}
|
|
30755
|
+
this.emit("plg:state-machine:action-error", { actionName, error: error.message, machineId, entityId });
|
|
30756
|
+
return;
|
|
30757
|
+
}
|
|
30758
|
+
const classification = ErrorClassifier.classify(error, {
|
|
30759
|
+
retryableErrors: retryConfig.retryableErrors,
|
|
30760
|
+
nonRetriableErrors: retryConfig.nonRetriableErrors
|
|
30761
|
+
});
|
|
30762
|
+
if (classification === "NON_RETRIABLE") {
|
|
30763
|
+
this.emit("plg:state-machine:action-error-non-retriable", {
|
|
30764
|
+
machineId,
|
|
30765
|
+
entityId,
|
|
30766
|
+
action: actionName,
|
|
30767
|
+
error: error.message,
|
|
30768
|
+
state: currentState
|
|
30769
|
+
});
|
|
30770
|
+
if (this.config.verbose) {
|
|
30771
|
+
console.error(`[StateMachinePlugin] Action '${actionName}' failed with non-retriable error:`, error.message);
|
|
30772
|
+
}
|
|
30773
|
+
throw error;
|
|
30774
|
+
}
|
|
30775
|
+
if (attempt >= maxAttempts) {
|
|
30776
|
+
this.emit("plg:state-machine:action-retry-exhausted", {
|
|
30777
|
+
machineId,
|
|
30778
|
+
entityId,
|
|
30779
|
+
action: actionName,
|
|
30780
|
+
attempts: attempt + 1,
|
|
30781
|
+
error: error.message,
|
|
30782
|
+
state: currentState
|
|
30783
|
+
});
|
|
30784
|
+
if (this.config.verbose) {
|
|
30785
|
+
console.error(`[StateMachinePlugin] Action '${actionName}' failed after ${attempt + 1} attempts:`, error.message);
|
|
30786
|
+
}
|
|
30787
|
+
throw error;
|
|
30788
|
+
}
|
|
30789
|
+
attempt++;
|
|
30790
|
+
const delay = this._calculateBackoff(attempt, retryConfig);
|
|
30791
|
+
if (retryConfig.onRetry) {
|
|
30792
|
+
try {
|
|
30793
|
+
await retryConfig.onRetry(attempt, error, context);
|
|
30794
|
+
} catch (hookError) {
|
|
30795
|
+
if (this.config.verbose) {
|
|
30796
|
+
console.warn(`[StateMachinePlugin] onRetry hook failed:`, hookError.message);
|
|
30797
|
+
}
|
|
30798
|
+
}
|
|
30799
|
+
}
|
|
30800
|
+
this.emit("plg:state-machine:action-retry-attempt", {
|
|
30801
|
+
machineId,
|
|
30802
|
+
entityId,
|
|
30803
|
+
action: actionName,
|
|
30804
|
+
attempt,
|
|
30805
|
+
delay,
|
|
30806
|
+
error: error.message,
|
|
30807
|
+
state: currentState
|
|
30808
|
+
});
|
|
30809
|
+
if (this.config.verbose) {
|
|
30810
|
+
console.warn(`[StateMachinePlugin] Action '${actionName}' failed (attempt ${attempt + 1}/${maxAttempts + 1}), retrying in ${delay}ms:`, error.message);
|
|
30811
|
+
}
|
|
30812
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
30356
30813
|
}
|
|
30357
|
-
this.emit("action_error", { actionName, error: error.message, machineId, entityId });
|
|
30358
30814
|
}
|
|
30359
30815
|
}
|
|
30360
30816
|
async _transition(machineId, entityId, fromState, toState, event, context) {
|
|
@@ -30452,6 +30908,27 @@ class StateMachinePlugin extends Plugin {
|
|
|
30452
30908
|
console.warn(`[StateMachinePlugin] Failed to release lock '${lockName}':`, err.message);
|
|
30453
30909
|
}
|
|
30454
30910
|
}
|
|
30911
|
+
/**
|
|
30912
|
+
* Calculate backoff delay for retry attempts
|
|
30913
|
+
* @private
|
|
30914
|
+
*/
|
|
30915
|
+
_calculateBackoff(attempt, retryConfig) {
|
|
30916
|
+
const {
|
|
30917
|
+
backoffStrategy = "exponential",
|
|
30918
|
+
baseDelay = 1e3,
|
|
30919
|
+
maxDelay = 3e4
|
|
30920
|
+
} = retryConfig || {};
|
|
30921
|
+
let delay;
|
|
30922
|
+
if (backoffStrategy === "exponential") {
|
|
30923
|
+
delay = Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay);
|
|
30924
|
+
} else if (backoffStrategy === "linear") {
|
|
30925
|
+
delay = Math.min(baseDelay * attempt, maxDelay);
|
|
30926
|
+
} else {
|
|
30927
|
+
delay = baseDelay;
|
|
30928
|
+
}
|
|
30929
|
+
const jitter = delay * 0.2 * (Math.random() - 0.5);
|
|
30930
|
+
return Math.round(delay + jitter);
|
|
30931
|
+
}
|
|
30455
30932
|
/**
|
|
30456
30933
|
* Get current state for an entity
|
|
30457
30934
|
*/
|
|
@@ -30581,7 +31058,7 @@ class StateMachinePlugin extends Plugin {
|
|
|
30581
31058
|
if (initialStateConfig && initialStateConfig.entry) {
|
|
30582
31059
|
await this._executeAction(initialStateConfig.entry, context, "INIT", machineId, entityId);
|
|
30583
31060
|
}
|
|
30584
|
-
this.emit("
|
|
31061
|
+
this.emit("plg:state-machine:entity-initialized", { machineId, entityId, initialState });
|
|
30585
31062
|
return initialState;
|
|
30586
31063
|
}
|
|
30587
31064
|
/**
|
|
@@ -30638,12 +31115,343 @@ class StateMachinePlugin extends Plugin {
|
|
|
30638
31115
|
`;
|
|
30639
31116
|
return dot;
|
|
30640
31117
|
}
|
|
31118
|
+
/**
|
|
31119
|
+
* Get all entities currently in a specific state
|
|
31120
|
+
* @private
|
|
31121
|
+
*/
|
|
31122
|
+
async _getEntitiesInState(machineId, stateName) {
|
|
31123
|
+
if (!this.config.persistTransitions) {
|
|
31124
|
+
const machine = this.machines.get(machineId);
|
|
31125
|
+
if (!machine) return [];
|
|
31126
|
+
const entities = [];
|
|
31127
|
+
for (const [entityId, currentState] of machine.currentStates) {
|
|
31128
|
+
if (currentState === stateName) {
|
|
31129
|
+
entities.push({ entityId, currentState, context: {}, triggerCounts: {} });
|
|
31130
|
+
}
|
|
31131
|
+
}
|
|
31132
|
+
return entities;
|
|
31133
|
+
}
|
|
31134
|
+
const [ok, err, records] = await tryFn(
|
|
31135
|
+
() => this.database.resources[this.config.stateResource].query({
|
|
31136
|
+
machineId,
|
|
31137
|
+
currentState: stateName
|
|
31138
|
+
})
|
|
31139
|
+
);
|
|
31140
|
+
if (!ok) {
|
|
31141
|
+
if (this.config.verbose) {
|
|
31142
|
+
console.warn(`[StateMachinePlugin] Failed to query entities in state '${stateName}':`, err.message);
|
|
31143
|
+
}
|
|
31144
|
+
return [];
|
|
31145
|
+
}
|
|
31146
|
+
return records || [];
|
|
31147
|
+
}
|
|
31148
|
+
/**
|
|
31149
|
+
* Increment trigger execution count for an entity
|
|
31150
|
+
* @private
|
|
31151
|
+
*/
|
|
31152
|
+
async _incrementTriggerCount(machineId, entityId, triggerName) {
|
|
31153
|
+
if (!this.config.persistTransitions) {
|
|
31154
|
+
return;
|
|
31155
|
+
}
|
|
31156
|
+
const stateId = `${machineId}_${entityId}`;
|
|
31157
|
+
const [ok, err, stateRecord] = await tryFn(
|
|
31158
|
+
() => this.database.resources[this.config.stateResource].get(stateId)
|
|
31159
|
+
);
|
|
31160
|
+
if (ok && stateRecord) {
|
|
31161
|
+
const triggerCounts = stateRecord.triggerCounts || {};
|
|
31162
|
+
triggerCounts[triggerName] = (triggerCounts[triggerName] || 0) + 1;
|
|
31163
|
+
await tryFn(
|
|
31164
|
+
() => this.database.resources[this.config.stateResource].patch(stateId, { triggerCounts })
|
|
31165
|
+
);
|
|
31166
|
+
}
|
|
31167
|
+
}
|
|
31168
|
+
/**
|
|
31169
|
+
* Setup trigger system for all state machines
|
|
31170
|
+
* @private
|
|
31171
|
+
*/
|
|
31172
|
+
async _setupTriggers() {
|
|
31173
|
+
if (!this.config.enableScheduler && !this.config.enableDateTriggers && !this.config.enableFunctionTriggers && !this.config.enableEventTriggers) {
|
|
31174
|
+
return;
|
|
31175
|
+
}
|
|
31176
|
+
const cronJobs = {};
|
|
31177
|
+
for (const [machineId, machineData] of this.machines) {
|
|
31178
|
+
const machineConfig = machineData.config;
|
|
31179
|
+
for (const [stateName, stateConfig] of Object.entries(machineConfig.states)) {
|
|
31180
|
+
const triggers = stateConfig.triggers || [];
|
|
31181
|
+
for (let i = 0; i < triggers.length; i++) {
|
|
31182
|
+
const trigger = triggers[i];
|
|
31183
|
+
const triggerName = `${trigger.action}_${i}`;
|
|
31184
|
+
if (trigger.type === "cron" && this.config.enableScheduler) {
|
|
31185
|
+
const jobName = `${machineId}_${stateName}_${triggerName}`;
|
|
31186
|
+
cronJobs[jobName] = await this._createCronJob(machineId, stateName, trigger, triggerName);
|
|
31187
|
+
} else if (trigger.type === "date" && this.config.enableDateTriggers) {
|
|
31188
|
+
await this._setupDateTrigger(machineId, stateName, trigger, triggerName);
|
|
31189
|
+
} else if (trigger.type === "function" && this.config.enableFunctionTriggers) {
|
|
31190
|
+
await this._setupFunctionTrigger(machineId, stateName, trigger, triggerName);
|
|
31191
|
+
} else if (trigger.type === "event" && this.config.enableEventTriggers) {
|
|
31192
|
+
await this._setupEventTrigger(machineId, stateName, trigger, triggerName);
|
|
31193
|
+
}
|
|
31194
|
+
}
|
|
31195
|
+
}
|
|
31196
|
+
}
|
|
31197
|
+
if (Object.keys(cronJobs).length > 0 && this.config.enableScheduler) {
|
|
31198
|
+
const { SchedulerPlugin } = await Promise.resolve().then(function () { return scheduler_plugin; });
|
|
31199
|
+
this.schedulerPlugin = new SchedulerPlugin({
|
|
31200
|
+
jobs: cronJobs,
|
|
31201
|
+
persistJobs: false,
|
|
31202
|
+
// Don't persist trigger jobs
|
|
31203
|
+
verbose: this.config.verbose,
|
|
31204
|
+
...this.config.schedulerConfig
|
|
31205
|
+
});
|
|
31206
|
+
await this.database.usePlugin(this.schedulerPlugin);
|
|
31207
|
+
if (this.config.verbose) {
|
|
31208
|
+
console.log(`[StateMachinePlugin] Installed SchedulerPlugin with ${Object.keys(cronJobs).length} cron triggers`);
|
|
31209
|
+
}
|
|
31210
|
+
}
|
|
31211
|
+
}
|
|
31212
|
+
/**
|
|
31213
|
+
* Create a SchedulerPlugin job for a cron trigger
|
|
31214
|
+
* @private
|
|
31215
|
+
*/
|
|
31216
|
+
async _createCronJob(machineId, stateName, trigger, triggerName) {
|
|
31217
|
+
return {
|
|
31218
|
+
schedule: trigger.schedule,
|
|
31219
|
+
description: `Trigger '${triggerName}' for ${machineId}.${stateName}`,
|
|
31220
|
+
action: async (database, context) => {
|
|
31221
|
+
const entities = await this._getEntitiesInState(machineId, stateName);
|
|
31222
|
+
let executedCount = 0;
|
|
31223
|
+
for (const entity of entities) {
|
|
31224
|
+
try {
|
|
31225
|
+
if (trigger.condition) {
|
|
31226
|
+
const shouldTrigger = await trigger.condition(entity.context, entity.entityId);
|
|
31227
|
+
if (!shouldTrigger) continue;
|
|
31228
|
+
}
|
|
31229
|
+
if (trigger.maxTriggers !== void 0) {
|
|
31230
|
+
const triggerCount = entity.triggerCounts?.[triggerName] || 0;
|
|
31231
|
+
if (triggerCount >= trigger.maxTriggers) {
|
|
31232
|
+
if (trigger.onMaxTriggersReached) {
|
|
31233
|
+
await this.send(machineId, entity.entityId, trigger.onMaxTriggersReached, entity.context);
|
|
31234
|
+
}
|
|
31235
|
+
continue;
|
|
31236
|
+
}
|
|
31237
|
+
}
|
|
31238
|
+
const result = await this._executeAction(
|
|
31239
|
+
trigger.action,
|
|
31240
|
+
entity.context,
|
|
31241
|
+
"TRIGGER",
|
|
31242
|
+
machineId,
|
|
31243
|
+
entity.entityId
|
|
31244
|
+
);
|
|
31245
|
+
await this._incrementTriggerCount(machineId, entity.entityId, triggerName);
|
|
31246
|
+
executedCount++;
|
|
31247
|
+
if (trigger.eventOnSuccess) {
|
|
31248
|
+
await this.send(machineId, entity.entityId, trigger.eventOnSuccess, {
|
|
31249
|
+
...entity.context,
|
|
31250
|
+
triggerResult: result
|
|
31251
|
+
});
|
|
31252
|
+
} else if (trigger.event) {
|
|
31253
|
+
await this.send(machineId, entity.entityId, trigger.event, {
|
|
31254
|
+
...entity.context,
|
|
31255
|
+
triggerResult: result
|
|
31256
|
+
});
|
|
31257
|
+
}
|
|
31258
|
+
this.emit("plg:state-machine:trigger-executed", {
|
|
31259
|
+
machineId,
|
|
31260
|
+
entityId: entity.entityId,
|
|
31261
|
+
state: stateName,
|
|
31262
|
+
trigger: triggerName,
|
|
31263
|
+
type: "cron"
|
|
31264
|
+
});
|
|
31265
|
+
} catch (error) {
|
|
31266
|
+
if (trigger.event) {
|
|
31267
|
+
await tryFn(() => this.send(machineId, entity.entityId, trigger.event, {
|
|
31268
|
+
...entity.context,
|
|
31269
|
+
triggerError: error.message
|
|
31270
|
+
}));
|
|
31271
|
+
}
|
|
31272
|
+
if (this.config.verbose) {
|
|
31273
|
+
console.error(`[StateMachinePlugin] Trigger '${triggerName}' failed for entity ${entity.entityId}:`, error.message);
|
|
31274
|
+
}
|
|
31275
|
+
}
|
|
31276
|
+
}
|
|
31277
|
+
return { processed: entities.length, executed: executedCount };
|
|
31278
|
+
}
|
|
31279
|
+
};
|
|
31280
|
+
}
|
|
31281
|
+
/**
|
|
31282
|
+
* Setup a date-based trigger
|
|
31283
|
+
* @private
|
|
31284
|
+
*/
|
|
31285
|
+
async _setupDateTrigger(machineId, stateName, trigger, triggerName) {
|
|
31286
|
+
const checkInterval = setInterval(async () => {
|
|
31287
|
+
const entities = await this._getEntitiesInState(machineId, stateName);
|
|
31288
|
+
for (const entity of entities) {
|
|
31289
|
+
try {
|
|
31290
|
+
const triggerDateValue = entity.context?.[trigger.field];
|
|
31291
|
+
if (!triggerDateValue) continue;
|
|
31292
|
+
const triggerDate = new Date(triggerDateValue);
|
|
31293
|
+
const now = /* @__PURE__ */ new Date();
|
|
31294
|
+
if (now >= triggerDate) {
|
|
31295
|
+
if (trigger.maxTriggers !== void 0) {
|
|
31296
|
+
const triggerCount = entity.triggerCounts?.[triggerName] || 0;
|
|
31297
|
+
if (triggerCount >= trigger.maxTriggers) {
|
|
31298
|
+
if (trigger.onMaxTriggersReached) {
|
|
31299
|
+
await this.send(machineId, entity.entityId, trigger.onMaxTriggersReached, entity.context);
|
|
31300
|
+
}
|
|
31301
|
+
continue;
|
|
31302
|
+
}
|
|
31303
|
+
}
|
|
31304
|
+
const result = await this._executeAction(trigger.action, entity.context, "TRIGGER", machineId, entity.entityId);
|
|
31305
|
+
await this._incrementTriggerCount(machineId, entity.entityId, triggerName);
|
|
31306
|
+
if (trigger.event) {
|
|
31307
|
+
await this.send(machineId, entity.entityId, trigger.event, {
|
|
31308
|
+
...entity.context,
|
|
31309
|
+
triggerResult: result
|
|
31310
|
+
});
|
|
31311
|
+
}
|
|
31312
|
+
this.emit("plg:state-machine:trigger-executed", {
|
|
31313
|
+
machineId,
|
|
31314
|
+
entityId: entity.entityId,
|
|
31315
|
+
state: stateName,
|
|
31316
|
+
trigger: triggerName,
|
|
31317
|
+
type: "date"
|
|
31318
|
+
});
|
|
31319
|
+
}
|
|
31320
|
+
} catch (error) {
|
|
31321
|
+
if (this.config.verbose) {
|
|
31322
|
+
console.error(`[StateMachinePlugin] Date trigger '${triggerName}' failed:`, error.message);
|
|
31323
|
+
}
|
|
31324
|
+
}
|
|
31325
|
+
}
|
|
31326
|
+
}, this.config.triggerCheckInterval);
|
|
31327
|
+
this.triggerIntervals.push(checkInterval);
|
|
31328
|
+
}
|
|
31329
|
+
/**
|
|
31330
|
+
* Setup a function-based trigger
|
|
31331
|
+
* @private
|
|
31332
|
+
*/
|
|
31333
|
+
async _setupFunctionTrigger(machineId, stateName, trigger, triggerName) {
|
|
31334
|
+
const interval = trigger.interval || this.config.triggerCheckInterval;
|
|
31335
|
+
const checkInterval = setInterval(async () => {
|
|
31336
|
+
const entities = await this._getEntitiesInState(machineId, stateName);
|
|
31337
|
+
for (const entity of entities) {
|
|
31338
|
+
try {
|
|
31339
|
+
if (trigger.maxTriggers !== void 0) {
|
|
31340
|
+
const triggerCount = entity.triggerCounts?.[triggerName] || 0;
|
|
31341
|
+
if (triggerCount >= trigger.maxTriggers) {
|
|
31342
|
+
if (trigger.onMaxTriggersReached) {
|
|
31343
|
+
await this.send(machineId, entity.entityId, trigger.onMaxTriggersReached, entity.context);
|
|
31344
|
+
}
|
|
31345
|
+
continue;
|
|
31346
|
+
}
|
|
31347
|
+
}
|
|
31348
|
+
const shouldTrigger = await trigger.condition(entity.context, entity.entityId);
|
|
31349
|
+
if (shouldTrigger) {
|
|
31350
|
+
const result = await this._executeAction(trigger.action, entity.context, "TRIGGER", machineId, entity.entityId);
|
|
31351
|
+
await this._incrementTriggerCount(machineId, entity.entityId, triggerName);
|
|
31352
|
+
if (trigger.event) {
|
|
31353
|
+
await this.send(machineId, entity.entityId, trigger.event, {
|
|
31354
|
+
...entity.context,
|
|
31355
|
+
triggerResult: result
|
|
31356
|
+
});
|
|
31357
|
+
}
|
|
31358
|
+
this.emit("plg:state-machine:trigger-executed", {
|
|
31359
|
+
machineId,
|
|
31360
|
+
entityId: entity.entityId,
|
|
31361
|
+
state: stateName,
|
|
31362
|
+
trigger: triggerName,
|
|
31363
|
+
type: "function"
|
|
31364
|
+
});
|
|
31365
|
+
}
|
|
31366
|
+
} catch (error) {
|
|
31367
|
+
if (this.config.verbose) {
|
|
31368
|
+
console.error(`[StateMachinePlugin] Function trigger '${triggerName}' failed:`, error.message);
|
|
31369
|
+
}
|
|
31370
|
+
}
|
|
31371
|
+
}
|
|
31372
|
+
}, interval);
|
|
31373
|
+
this.triggerIntervals.push(checkInterval);
|
|
31374
|
+
}
|
|
31375
|
+
/**
|
|
31376
|
+
* Setup an event-based trigger
|
|
31377
|
+
* @private
|
|
31378
|
+
*/
|
|
31379
|
+
async _setupEventTrigger(machineId, stateName, trigger, triggerName) {
|
|
31380
|
+
const eventName = trigger.event;
|
|
31381
|
+
const eventHandler = async (eventData) => {
|
|
31382
|
+
const entities = await this._getEntitiesInState(machineId, stateName);
|
|
31383
|
+
for (const entity of entities) {
|
|
31384
|
+
try {
|
|
31385
|
+
if (trigger.condition) {
|
|
31386
|
+
const shouldTrigger = await trigger.condition(entity.context, entity.entityId, eventData);
|
|
31387
|
+
if (!shouldTrigger) continue;
|
|
31388
|
+
}
|
|
31389
|
+
if (trigger.maxTriggers !== void 0) {
|
|
31390
|
+
const triggerCount = entity.triggerCounts?.[triggerName] || 0;
|
|
31391
|
+
if (triggerCount >= trigger.maxTriggers) {
|
|
31392
|
+
if (trigger.onMaxTriggersReached) {
|
|
31393
|
+
await this.send(machineId, entity.entityId, trigger.onMaxTriggersReached, entity.context);
|
|
31394
|
+
}
|
|
31395
|
+
continue;
|
|
31396
|
+
}
|
|
31397
|
+
}
|
|
31398
|
+
const result = await this._executeAction(
|
|
31399
|
+
trigger.action,
|
|
31400
|
+
{ ...entity.context, eventData },
|
|
31401
|
+
"TRIGGER",
|
|
31402
|
+
machineId,
|
|
31403
|
+
entity.entityId
|
|
31404
|
+
);
|
|
31405
|
+
await this._incrementTriggerCount(machineId, entity.entityId, triggerName);
|
|
31406
|
+
if (trigger.sendEvent) {
|
|
31407
|
+
await this.send(machineId, entity.entityId, trigger.sendEvent, {
|
|
31408
|
+
...entity.context,
|
|
31409
|
+
triggerResult: result,
|
|
31410
|
+
eventData
|
|
31411
|
+
});
|
|
31412
|
+
}
|
|
31413
|
+
this.emit("plg:state-machine:trigger-executed", {
|
|
31414
|
+
machineId,
|
|
31415
|
+
entityId: entity.entityId,
|
|
31416
|
+
state: stateName,
|
|
31417
|
+
trigger: triggerName,
|
|
31418
|
+
type: "event",
|
|
31419
|
+
eventName
|
|
31420
|
+
});
|
|
31421
|
+
} catch (error) {
|
|
31422
|
+
if (this.config.verbose) {
|
|
31423
|
+
console.error(`[StateMachinePlugin] Event trigger '${triggerName}' failed:`, error.message);
|
|
31424
|
+
}
|
|
31425
|
+
}
|
|
31426
|
+
}
|
|
31427
|
+
};
|
|
31428
|
+
if (eventName.startsWith("db:")) {
|
|
31429
|
+
const dbEventName = eventName.substring(3);
|
|
31430
|
+
this.database.on(dbEventName, eventHandler);
|
|
31431
|
+
if (this.config.verbose) {
|
|
31432
|
+
console.log(`[StateMachinePlugin] Listening to database event '${dbEventName}' for trigger '${triggerName}'`);
|
|
31433
|
+
}
|
|
31434
|
+
} else {
|
|
31435
|
+
this.on(eventName, eventHandler);
|
|
31436
|
+
if (this.config.verbose) {
|
|
31437
|
+
console.log(`[StateMachinePlugin] Listening to plugin event '${eventName}' for trigger '${triggerName}'`);
|
|
31438
|
+
}
|
|
31439
|
+
}
|
|
31440
|
+
}
|
|
30641
31441
|
async start() {
|
|
30642
31442
|
if (this.config.verbose) {
|
|
30643
31443
|
console.log(`[StateMachinePlugin] Started with ${this.machines.size} state machines`);
|
|
30644
31444
|
}
|
|
30645
31445
|
}
|
|
30646
31446
|
async stop() {
|
|
31447
|
+
for (const interval of this.triggerIntervals) {
|
|
31448
|
+
clearInterval(interval);
|
|
31449
|
+
}
|
|
31450
|
+
this.triggerIntervals = [];
|
|
31451
|
+
if (this.schedulerPlugin) {
|
|
31452
|
+
await this.schedulerPlugin.stop();
|
|
31453
|
+
this.schedulerPlugin = null;
|
|
31454
|
+
}
|
|
30647
31455
|
this.machines.clear();
|
|
30648
31456
|
this.removeAllListeners();
|
|
30649
31457
|
}
|
|
@@ -40915,7 +41723,7 @@ class TTLPlugin extends Plugin {
|
|
|
40915
41723
|
if (this.verbose) {
|
|
40916
41724
|
console.log(`[TTLPlugin] Installed with ${Object.keys(this.resources).length} resources`);
|
|
40917
41725
|
}
|
|
40918
|
-
this.emit("installed", {
|
|
41726
|
+
this.emit("db:plugin:installed", {
|
|
40919
41727
|
plugin: "TTLPlugin",
|
|
40920
41728
|
resources: Object.keys(this.resources)
|
|
40921
41729
|
});
|
|
@@ -41152,7 +41960,7 @@ class TTLPlugin extends Plugin {
|
|
|
41152
41960
|
}
|
|
41153
41961
|
this.stats.lastScanAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
41154
41962
|
this.stats.lastScanDuration = Date.now() - startTime;
|
|
41155
|
-
this.emit("
|
|
41963
|
+
this.emit("plg:ttl:scan-completed", {
|
|
41156
41964
|
granularity,
|
|
41157
41965
|
duration: this.stats.lastScanDuration,
|
|
41158
41966
|
cohorts
|
|
@@ -41160,7 +41968,7 @@ class TTLPlugin extends Plugin {
|
|
|
41160
41968
|
} catch (error) {
|
|
41161
41969
|
console.error(`[TTLPlugin] Error in ${granularity} cleanup:`, error);
|
|
41162
41970
|
this.stats.totalErrors++;
|
|
41163
|
-
this.emit("
|
|
41971
|
+
this.emit("plg:ttl:cleanup-error", { granularity, error });
|
|
41164
41972
|
}
|
|
41165
41973
|
}
|
|
41166
41974
|
/**
|
|
@@ -41208,7 +42016,7 @@ class TTLPlugin extends Plugin {
|
|
|
41208
42016
|
}
|
|
41209
42017
|
await this.expirationIndex.delete(entry.id);
|
|
41210
42018
|
this.stats.totalExpired++;
|
|
41211
|
-
this.emit("
|
|
42019
|
+
this.emit("plg:ttl:record-expired", { resource: entry.resourceName, record });
|
|
41212
42020
|
} catch (error) {
|
|
41213
42021
|
console.error(`[TTLPlugin] Error processing expired entry:`, error);
|
|
41214
42022
|
this.stats.totalErrors++;
|
|
@@ -41679,15 +42487,15 @@ class VectorPlugin extends Plugin {
|
|
|
41679
42487
|
this._throttleState = /* @__PURE__ */ new Map();
|
|
41680
42488
|
}
|
|
41681
42489
|
async onInstall() {
|
|
41682
|
-
this.emit("installed", { plugin: "VectorPlugin" });
|
|
42490
|
+
this.emit("db:plugin:installed", { plugin: "VectorPlugin" });
|
|
41683
42491
|
this.validateVectorStorage();
|
|
41684
42492
|
this.installResourceMethods();
|
|
41685
42493
|
}
|
|
41686
42494
|
async onStart() {
|
|
41687
|
-
this.emit("started", { plugin: "VectorPlugin" });
|
|
42495
|
+
this.emit("db:plugin:started", { plugin: "VectorPlugin" });
|
|
41688
42496
|
}
|
|
41689
42497
|
async onStop() {
|
|
41690
|
-
this.emit("stopped", { plugin: "VectorPlugin" });
|
|
42498
|
+
this.emit("db:plugin:stopped", { plugin: "VectorPlugin" });
|
|
41691
42499
|
}
|
|
41692
42500
|
async onUninstall(options) {
|
|
41693
42501
|
for (const resource of Object.values(this.database.resources)) {
|
|
@@ -41698,7 +42506,7 @@ class VectorPlugin extends Plugin {
|
|
|
41698
42506
|
delete resource.findSimilar;
|
|
41699
42507
|
delete resource.distance;
|
|
41700
42508
|
}
|
|
41701
|
-
this.emit("uninstalled", { plugin: "VectorPlugin" });
|
|
42509
|
+
this.emit("db:plugin:uninstalled", { plugin: "VectorPlugin" });
|
|
41702
42510
|
}
|
|
41703
42511
|
/**
|
|
41704
42512
|
* Validate vector storage configuration for all resources
|
|
@@ -41727,10 +42535,10 @@ class VectorPlugin extends Plugin {
|
|
|
41727
42535
|
currentBehavior: resource.behavior || "default",
|
|
41728
42536
|
recommendation: "body-overflow"
|
|
41729
42537
|
};
|
|
41730
|
-
this.emit("vector:storage-warning", warning);
|
|
42538
|
+
this.emit("plg:vector:storage-warning", warning);
|
|
41731
42539
|
if (this.config.autoFixBehavior) {
|
|
41732
42540
|
resource.behavior = "body-overflow";
|
|
41733
|
-
this.emit("vector:behavior-fixed", {
|
|
42541
|
+
this.emit("plg:vector:behavior-fixed", {
|
|
41734
42542
|
resource: resource.name,
|
|
41735
42543
|
newBehavior: "body-overflow"
|
|
41736
42544
|
});
|
|
@@ -41762,7 +42570,7 @@ class VectorPlugin extends Plugin {
|
|
|
41762
42570
|
const partitionName = `byHas${this.capitalize(vectorField.name.replace(/\./g, "_"))}`;
|
|
41763
42571
|
const trackingFieldName = `_has${this.capitalize(vectorField.name.replace(/\./g, "_"))}`;
|
|
41764
42572
|
if (resource.config.partitions && resource.config.partitions[partitionName]) {
|
|
41765
|
-
this.emit("vector:partition-exists", {
|
|
42573
|
+
this.emit("plg:vector:partition-exists", {
|
|
41766
42574
|
resource: resource.name,
|
|
41767
42575
|
vectorField: vectorField.name,
|
|
41768
42576
|
partition: partitionName,
|
|
@@ -41785,7 +42593,7 @@ class VectorPlugin extends Plugin {
|
|
|
41785
42593
|
default: false
|
|
41786
42594
|
}, "VectorPlugin");
|
|
41787
42595
|
}
|
|
41788
|
-
this.emit("vector:partition-created", {
|
|
42596
|
+
this.emit("plg:vector:partition-created", {
|
|
41789
42597
|
resource: resource.name,
|
|
41790
42598
|
vectorField: vectorField.name,
|
|
41791
42599
|
partition: partitionName,
|
|
@@ -41860,7 +42668,7 @@ class VectorPlugin extends Plugin {
|
|
|
41860
42668
|
}
|
|
41861
42669
|
return updates;
|
|
41862
42670
|
});
|
|
41863
|
-
this.emit("vector:hooks-installed", {
|
|
42671
|
+
this.emit("plg:vector:hooks-installed", {
|
|
41864
42672
|
resource: resource.name,
|
|
41865
42673
|
vectorField,
|
|
41866
42674
|
trackingField,
|
|
@@ -41969,7 +42777,7 @@ class VectorPlugin extends Plugin {
|
|
|
41969
42777
|
const vectorField = this._findEmbeddingField(resource.schema.attributes);
|
|
41970
42778
|
this._vectorFieldCache.set(resource.name, vectorField);
|
|
41971
42779
|
if (vectorField && this.config.emitEvents) {
|
|
41972
|
-
this.emit("vector:field-detected", {
|
|
42780
|
+
this.emit("plg:vector:field-detected", {
|
|
41973
42781
|
resource: resource.name,
|
|
41974
42782
|
vectorField,
|
|
41975
42783
|
timestamp: Date.now()
|
|
@@ -42893,7 +43701,7 @@ class MemoryClient extends EventEmitter {
|
|
|
42893
43701
|
async sendCommand(command) {
|
|
42894
43702
|
const commandName = command.constructor.name;
|
|
42895
43703
|
const input = command.input || {};
|
|
42896
|
-
this.emit("
|
|
43704
|
+
this.emit("cl:request", commandName, input);
|
|
42897
43705
|
let response;
|
|
42898
43706
|
try {
|
|
42899
43707
|
switch (commandName) {
|
|
@@ -42921,7 +43729,7 @@ class MemoryClient extends EventEmitter {
|
|
|
42921
43729
|
default:
|
|
42922
43730
|
throw new Error(`Unsupported command: ${commandName}`);
|
|
42923
43731
|
}
|
|
42924
|
-
this.emit("
|
|
43732
|
+
this.emit("cl:response", commandName, response, input);
|
|
42925
43733
|
return response;
|
|
42926
43734
|
} catch (error) {
|
|
42927
43735
|
const mappedError = mapAwsError(error, {
|
|
@@ -43032,7 +43840,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43032
43840
|
contentLength,
|
|
43033
43841
|
ifMatch
|
|
43034
43842
|
});
|
|
43035
|
-
this.emit("
|
|
43843
|
+
this.emit("cl:PutObject", null, { key, metadata, contentType, body, contentEncoding, contentLength });
|
|
43036
43844
|
return response;
|
|
43037
43845
|
}
|
|
43038
43846
|
/**
|
|
@@ -43047,7 +43855,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43047
43855
|
decodedMetadata[k] = metadataDecode(v);
|
|
43048
43856
|
}
|
|
43049
43857
|
}
|
|
43050
|
-
this.emit("
|
|
43858
|
+
this.emit("cl:GetObject", null, { key });
|
|
43051
43859
|
return {
|
|
43052
43860
|
...response,
|
|
43053
43861
|
Metadata: decodedMetadata
|
|
@@ -43065,7 +43873,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43065
43873
|
decodedMetadata[k] = metadataDecode(v);
|
|
43066
43874
|
}
|
|
43067
43875
|
}
|
|
43068
|
-
this.emit("
|
|
43876
|
+
this.emit("cl:HeadObject", null, { key });
|
|
43069
43877
|
return {
|
|
43070
43878
|
...response,
|
|
43071
43879
|
Metadata: decodedMetadata
|
|
@@ -43090,7 +43898,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43090
43898
|
metadataDirective,
|
|
43091
43899
|
contentType
|
|
43092
43900
|
});
|
|
43093
|
-
this.emit("
|
|
43901
|
+
this.emit("cl:CopyObject", null, { from, to, metadata, metadataDirective });
|
|
43094
43902
|
return response;
|
|
43095
43903
|
}
|
|
43096
43904
|
/**
|
|
@@ -43106,7 +43914,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43106
43914
|
async deleteObject(key) {
|
|
43107
43915
|
const fullKey = this.keyPrefix ? path$1.join(this.keyPrefix, key) : key;
|
|
43108
43916
|
const response = await this.storage.delete(fullKey);
|
|
43109
|
-
this.emit("
|
|
43917
|
+
this.emit("cl:DeleteObject", null, { key });
|
|
43110
43918
|
return response;
|
|
43111
43919
|
}
|
|
43112
43920
|
/**
|
|
@@ -43139,7 +43947,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43139
43947
|
maxKeys,
|
|
43140
43948
|
continuationToken
|
|
43141
43949
|
});
|
|
43142
|
-
this.emit("
|
|
43950
|
+
this.emit("cl:ListObjects", null, { prefix, count: response.Contents.length });
|
|
43143
43951
|
return response;
|
|
43144
43952
|
}
|
|
43145
43953
|
/**
|
|
@@ -43179,7 +43987,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43179
43987
|
if (this.keyPrefix) {
|
|
43180
43988
|
keys = keys.map((x) => x.replace(this.keyPrefix, "")).map((x) => x.startsWith("/") ? x.replace("/", "") : x);
|
|
43181
43989
|
}
|
|
43182
|
-
this.emit("
|
|
43990
|
+
this.emit("cl:GetKeysPage", keys, params);
|
|
43183
43991
|
return keys;
|
|
43184
43992
|
}
|
|
43185
43993
|
/**
|
|
@@ -43196,7 +44004,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43196
44004
|
if (this.keyPrefix) {
|
|
43197
44005
|
keys = keys.map((x) => x.replace(this.keyPrefix, "")).map((x) => x.startsWith("/") ? x.replace("/", "") : x);
|
|
43198
44006
|
}
|
|
43199
|
-
this.emit("
|
|
44007
|
+
this.emit("cl:GetAllKeys", keys, { prefix });
|
|
43200
44008
|
return keys;
|
|
43201
44009
|
}
|
|
43202
44010
|
/**
|
|
@@ -43205,7 +44013,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43205
44013
|
async count({ prefix = "" } = {}) {
|
|
43206
44014
|
const keys = await this.getAllKeys({ prefix });
|
|
43207
44015
|
const count = keys.length;
|
|
43208
|
-
this.emit("
|
|
44016
|
+
this.emit("cl:Count", count, { prefix });
|
|
43209
44017
|
return count;
|
|
43210
44018
|
}
|
|
43211
44019
|
/**
|
|
@@ -43217,13 +44025,13 @@ class MemoryClient extends EventEmitter {
|
|
|
43217
44025
|
if (keys.length > 0) {
|
|
43218
44026
|
const result = await this.deleteObjects(keys);
|
|
43219
44027
|
totalDeleted = result.Deleted.length;
|
|
43220
|
-
this.emit("
|
|
44028
|
+
this.emit("cl:DeleteAll", {
|
|
43221
44029
|
prefix,
|
|
43222
44030
|
batch: totalDeleted,
|
|
43223
44031
|
total: totalDeleted
|
|
43224
44032
|
});
|
|
43225
44033
|
}
|
|
43226
|
-
this.emit("
|
|
44034
|
+
this.emit("cl:DeleteAllComplete", {
|
|
43227
44035
|
prefix,
|
|
43228
44036
|
totalDeleted
|
|
43229
44037
|
});
|
|
@@ -43236,11 +44044,11 @@ class MemoryClient extends EventEmitter {
|
|
|
43236
44044
|
if (offset === 0) return null;
|
|
43237
44045
|
const keys = await this.getAllKeys({ prefix });
|
|
43238
44046
|
if (offset >= keys.length) {
|
|
43239
|
-
this.emit("
|
|
44047
|
+
this.emit("cl:GetContinuationTokenAfterOffset", null, { prefix, offset });
|
|
43240
44048
|
return null;
|
|
43241
44049
|
}
|
|
43242
44050
|
const token = keys[offset];
|
|
43243
|
-
this.emit("
|
|
44051
|
+
this.emit("cl:GetContinuationTokenAfterOffset", token, { prefix, offset });
|
|
43244
44052
|
return token;
|
|
43245
44053
|
}
|
|
43246
44054
|
/**
|
|
@@ -43270,7 +44078,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43270
44078
|
});
|
|
43271
44079
|
}
|
|
43272
44080
|
}
|
|
43273
|
-
this.emit("
|
|
44081
|
+
this.emit("cl:MoveAllObjects", { results, errors }, { prefixFrom, prefixTo });
|
|
43274
44082
|
if (errors.length > 0) {
|
|
43275
44083
|
const error = new Error("Some objects could not be moved");
|
|
43276
44084
|
error.context = {
|