s3db.js 13.2.1 → 13.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/s3db.cjs.js +248 -80
- package/dist/s3db.cjs.js.map +1 -1
- package/dist/s3db.es.js +248 -80
- package/dist/s3db.es.js.map +1 -1
- package/package.json +1 -1
- package/src/clients/memory-client.class.js +5 -3
- package/src/database.class.js +6 -3
- package/src/plugins/audit.plugin.js +3 -3
- package/src/plugins/cache/cache.class.js +2 -2
- package/src/plugins/costs.plugin.js +3 -2
- package/src/plugins/replicator.plugin.js +10 -10
- package/src/plugins/replicators/base-replicator.class.js +1 -1
- package/src/plugins/replicators/bigquery-replicator.class.js +3 -3
- package/src/plugins/replicators/planetscale-replicator.class.js +1 -1
- package/src/plugins/replicators/postgres-replicator.class.js +3 -3
- package/src/plugins/replicators/sqs-replicator.class.js +3 -3
- package/src/plugins/replicators/turso-replicator.class.js +1 -1
- package/src/plugins/replicators/webhook-replicator.class.js +3 -3
- package/src/plugins/s3-queue.plugin.js +1 -1
- package/src/plugins/state-machine.plugin.js +68 -0
- package/src/resource.class.js +172 -47
package/dist/s3db.es.js
CHANGED
|
@@ -5163,7 +5163,7 @@ class AuditPlugin extends Plugin {
|
|
|
5163
5163
|
async onStop() {
|
|
5164
5164
|
}
|
|
5165
5165
|
setupResourceAuditing(resource) {
|
|
5166
|
-
resource.on("
|
|
5166
|
+
resource.on("inserted", async (data) => {
|
|
5167
5167
|
const partitionValues = this.config.includePartitions ? this.getPartitionValues(data, resource) : null;
|
|
5168
5168
|
await this.logAudit({
|
|
5169
5169
|
resourceName: resource.name,
|
|
@@ -5175,7 +5175,7 @@ class AuditPlugin extends Plugin {
|
|
|
5175
5175
|
partitionValues: partitionValues ? JSON.stringify(partitionValues) : null
|
|
5176
5176
|
});
|
|
5177
5177
|
});
|
|
5178
|
-
resource.on("
|
|
5178
|
+
resource.on("updated", async (data) => {
|
|
5179
5179
|
let oldData = data.$before;
|
|
5180
5180
|
if (this.config.includeData && !oldData) {
|
|
5181
5181
|
const [ok, err, fetched] = await tryFn(() => resource.get(data.id));
|
|
@@ -5192,7 +5192,7 @@ class AuditPlugin extends Plugin {
|
|
|
5192
5192
|
partitionValues: partitionValues ? JSON.stringify(partitionValues) : null
|
|
5193
5193
|
});
|
|
5194
5194
|
});
|
|
5195
|
-
resource.on("
|
|
5195
|
+
resource.on("deleted", async (data) => {
|
|
5196
5196
|
let oldData = data;
|
|
5197
5197
|
if (this.config.includeData && !oldData) {
|
|
5198
5198
|
const [ok, err, fetched] = await tryFn(() => resource.get(data.id));
|
|
@@ -7297,13 +7297,13 @@ class Cache extends EventEmitter {
|
|
|
7297
7297
|
async get(key) {
|
|
7298
7298
|
this.validateKey(key);
|
|
7299
7299
|
const data = await this._get(key);
|
|
7300
|
-
this.emit("
|
|
7300
|
+
this.emit("fetched", data);
|
|
7301
7301
|
return data;
|
|
7302
7302
|
}
|
|
7303
7303
|
async del(key) {
|
|
7304
7304
|
this.validateKey(key);
|
|
7305
7305
|
const data = await this._del(key);
|
|
7306
|
-
this.emit("
|
|
7306
|
+
this.emit("deleted", data);
|
|
7307
7307
|
return data;
|
|
7308
7308
|
}
|
|
7309
7309
|
async delete(key) {
|
|
@@ -9118,8 +9118,7 @@ class CostsPlugin extends Plugin {
|
|
|
9118
9118
|
}
|
|
9119
9119
|
async onStart() {
|
|
9120
9120
|
if (this.client) {
|
|
9121
|
-
this.client.on("
|
|
9122
|
-
this.client.on("command.error", (name, response, input) => this.addRequest(name, this.map[name], response, input));
|
|
9121
|
+
this.client.on("cl:response", (name, response, input) => this.addRequest(name, this.map[name], response, input));
|
|
9123
9122
|
}
|
|
9124
9123
|
}
|
|
9125
9124
|
addRequest(name, method, response = {}, input = {}) {
|
|
@@ -17701,7 +17700,7 @@ class BaseReplicator extends EventEmitter {
|
|
|
17701
17700
|
*/
|
|
17702
17701
|
async initialize(database) {
|
|
17703
17702
|
this.database = database;
|
|
17704
|
-
this.emit("initialized", { replicator: this.name });
|
|
17703
|
+
this.emit("db:plugin:initialized", { replicator: this.name });
|
|
17705
17704
|
}
|
|
17706
17705
|
/**
|
|
17707
17706
|
* Replicate data to the target
|
|
@@ -18272,7 +18271,7 @@ class BigqueryReplicator extends BaseReplicator {
|
|
|
18272
18271
|
if (this.schemaSync.enabled) {
|
|
18273
18272
|
await this.syncSchemas(database);
|
|
18274
18273
|
}
|
|
18275
|
-
this.emit("initialized", {
|
|
18274
|
+
this.emit("db:plugin:initialized", {
|
|
18276
18275
|
replicator: this.name,
|
|
18277
18276
|
projectId: this.projectId,
|
|
18278
18277
|
datasetId: this.datasetId,
|
|
@@ -18579,7 +18578,7 @@ class BigqueryReplicator extends BaseReplicator {
|
|
|
18579
18578
|
if (errors.length > 0) {
|
|
18580
18579
|
console.warn(`[BigqueryReplicator] Replication completed with errors for ${resourceName}:`, errors);
|
|
18581
18580
|
}
|
|
18582
|
-
this.emit("replicated", {
|
|
18581
|
+
this.emit("plg:replicator:replicated", {
|
|
18583
18582
|
replicator: this.name,
|
|
18584
18583
|
resourceName,
|
|
18585
18584
|
operation,
|
|
@@ -18600,7 +18599,7 @@ class BigqueryReplicator extends BaseReplicator {
|
|
|
18600
18599
|
if (this.config.verbose) {
|
|
18601
18600
|
console.warn(`[BigqueryReplicator] Replication failed for ${resourceName}: ${err.message}`);
|
|
18602
18601
|
}
|
|
18603
|
-
this.emit("
|
|
18602
|
+
this.emit("plg:replicator:error", {
|
|
18604
18603
|
replicator: this.name,
|
|
18605
18604
|
resourceName,
|
|
18606
18605
|
operation,
|
|
@@ -19875,7 +19874,7 @@ ${createSQL}`);
|
|
|
19875
19874
|
}
|
|
19876
19875
|
}
|
|
19877
19876
|
const success = errors.length === 0;
|
|
19878
|
-
this.emit("replicated", {
|
|
19877
|
+
this.emit("plg:replicator:replicated", {
|
|
19879
19878
|
replicator: this.name,
|
|
19880
19879
|
resourceName,
|
|
19881
19880
|
operation,
|
|
@@ -20020,7 +20019,7 @@ class PostgresReplicator extends BaseReplicator {
|
|
|
20020
20019
|
if (this.schemaSync.enabled) {
|
|
20021
20020
|
await this.syncSchemas(database);
|
|
20022
20021
|
}
|
|
20023
|
-
this.emit("initialized", {
|
|
20022
|
+
this.emit("db:plugin:initialized", {
|
|
20024
20023
|
replicator: this.name,
|
|
20025
20024
|
database: this.database || "postgres",
|
|
20026
20025
|
resources: Object.keys(this.resources)
|
|
@@ -20226,7 +20225,7 @@ ${createSQL}`);
|
|
|
20226
20225
|
if (errors.length > 0) {
|
|
20227
20226
|
console.warn(`[PostgresReplicator] Replication completed with errors for ${resourceName}:`, errors);
|
|
20228
20227
|
}
|
|
20229
|
-
this.emit("replicated", {
|
|
20228
|
+
this.emit("plg:replicator:replicated", {
|
|
20230
20229
|
replicator: this.name,
|
|
20231
20230
|
resourceName,
|
|
20232
20231
|
operation,
|
|
@@ -20247,7 +20246,7 @@ ${createSQL}`);
|
|
|
20247
20246
|
if (this.config.verbose) {
|
|
20248
20247
|
console.warn(`[PostgresReplicator] Replication failed for ${resourceName}: ${err.message}`);
|
|
20249
20248
|
}
|
|
20250
|
-
this.emit("
|
|
20249
|
+
this.emit("plg:replicator:error", {
|
|
20251
20250
|
replicator: this.name,
|
|
20252
20251
|
resourceName,
|
|
20253
20252
|
operation,
|
|
@@ -23916,7 +23915,7 @@ ${errorDetails}`,
|
|
|
23916
23915
|
data = await this.applyVersionMapping(data, objectVersion, this.version);
|
|
23917
23916
|
}
|
|
23918
23917
|
data = await this.executeHooks("afterGet", data);
|
|
23919
|
-
this.
|
|
23918
|
+
this._emitStandardized("fetched", data, data.id);
|
|
23920
23919
|
const value = data;
|
|
23921
23920
|
return value;
|
|
23922
23921
|
}
|
|
@@ -24626,26 +24625,6 @@ ${errorDetails}`,
|
|
|
24626
24625
|
await this.executeHooks("beforeDelete", objectData);
|
|
24627
24626
|
const key = this.getResourceKey(id);
|
|
24628
24627
|
const [ok2, err2, response] = await tryFn(() => this.client.deleteObject(key));
|
|
24629
|
-
this._emitWithDeprecation("delete", "deleted", {
|
|
24630
|
-
...objectData,
|
|
24631
|
-
$before: { ...objectData },
|
|
24632
|
-
$after: null
|
|
24633
|
-
}, id);
|
|
24634
|
-
if (deleteError) {
|
|
24635
|
-
throw mapAwsError(deleteError, {
|
|
24636
|
-
bucket: this.client.config.bucket,
|
|
24637
|
-
key,
|
|
24638
|
-
resourceName: this.name,
|
|
24639
|
-
operation: "delete",
|
|
24640
|
-
id
|
|
24641
|
-
});
|
|
24642
|
-
}
|
|
24643
|
-
if (!ok2) throw mapAwsError(err2, {
|
|
24644
|
-
key,
|
|
24645
|
-
resourceName: this.name,
|
|
24646
|
-
operation: "delete",
|
|
24647
|
-
id
|
|
24648
|
-
});
|
|
24649
24628
|
if (this.config.partitions && Object.keys(this.config.partitions).length > 0 && objectData) {
|
|
24650
24629
|
if (this.config.strictPartitions) {
|
|
24651
24630
|
await this.deletePartitionReferences(objectData);
|
|
@@ -24678,11 +24657,30 @@ ${errorDetails}`,
|
|
|
24678
24657
|
for (const hook of nonPartitionHooks) {
|
|
24679
24658
|
afterDeleteData = await hook(afterDeleteData);
|
|
24680
24659
|
}
|
|
24681
|
-
return response;
|
|
24682
24660
|
} else {
|
|
24683
24661
|
await this.executeHooks("afterDelete", objectData);
|
|
24684
|
-
return response;
|
|
24685
24662
|
}
|
|
24663
|
+
this._emitStandardized("deleted", {
|
|
24664
|
+
...objectData,
|
|
24665
|
+
$before: { ...objectData },
|
|
24666
|
+
$after: null
|
|
24667
|
+
}, id);
|
|
24668
|
+
if (deleteError) {
|
|
24669
|
+
throw mapAwsError(deleteError, {
|
|
24670
|
+
bucket: this.client.config.bucket,
|
|
24671
|
+
key,
|
|
24672
|
+
resourceName: this.name,
|
|
24673
|
+
operation: "delete",
|
|
24674
|
+
id
|
|
24675
|
+
});
|
|
24676
|
+
}
|
|
24677
|
+
if (!ok2) throw mapAwsError(err2, {
|
|
24678
|
+
key,
|
|
24679
|
+
resourceName: this.name,
|
|
24680
|
+
operation: "delete",
|
|
24681
|
+
id
|
|
24682
|
+
});
|
|
24683
|
+
return response;
|
|
24686
24684
|
}
|
|
24687
24685
|
/**
|
|
24688
24686
|
* Insert or update a resource object (upsert operation)
|
|
@@ -24754,7 +24752,7 @@ ${errorDetails}`,
|
|
|
24754
24752
|
}
|
|
24755
24753
|
const count = await this.client.count({ prefix });
|
|
24756
24754
|
await this.executeHooks("afterCount", { count, partition, partitionValues });
|
|
24757
|
-
this.
|
|
24755
|
+
this._emitStandardized("count", count);
|
|
24758
24756
|
return count;
|
|
24759
24757
|
}
|
|
24760
24758
|
/**
|
|
@@ -24777,7 +24775,7 @@ ${errorDetails}`,
|
|
|
24777
24775
|
const result = await this.insert(attributes);
|
|
24778
24776
|
return result;
|
|
24779
24777
|
});
|
|
24780
|
-
this.
|
|
24778
|
+
this._emitStandardized("inserted-many", objects.length);
|
|
24781
24779
|
return results;
|
|
24782
24780
|
}
|
|
24783
24781
|
/**
|
|
@@ -24812,7 +24810,7 @@ ${errorDetails}`,
|
|
|
24812
24810
|
return response;
|
|
24813
24811
|
});
|
|
24814
24812
|
await this.executeHooks("afterDeleteMany", { ids, results });
|
|
24815
|
-
this.
|
|
24813
|
+
this._emitStandardized("deleted-many", ids.length);
|
|
24816
24814
|
return results;
|
|
24817
24815
|
}
|
|
24818
24816
|
async deleteAll() {
|
|
@@ -24821,7 +24819,7 @@ ${errorDetails}`,
|
|
|
24821
24819
|
}
|
|
24822
24820
|
const prefix = `resource=${this.name}/data`;
|
|
24823
24821
|
const deletedCount = await this.client.deleteAll({ prefix });
|
|
24824
|
-
this.
|
|
24822
|
+
this._emitStandardized("deleted-all", {
|
|
24825
24823
|
version: this.version,
|
|
24826
24824
|
prefix,
|
|
24827
24825
|
deletedCount
|
|
@@ -24838,7 +24836,7 @@ ${errorDetails}`,
|
|
|
24838
24836
|
}
|
|
24839
24837
|
const prefix = `resource=${this.name}`;
|
|
24840
24838
|
const deletedCount = await this.client.deleteAll({ prefix });
|
|
24841
|
-
this.
|
|
24839
|
+
this._emitStandardized("deleted-all-data", {
|
|
24842
24840
|
resource: this.name,
|
|
24843
24841
|
prefix,
|
|
24844
24842
|
deletedCount
|
|
@@ -24908,7 +24906,7 @@ ${errorDetails}`,
|
|
|
24908
24906
|
const idPart = parts.find((part) => part.startsWith("id="));
|
|
24909
24907
|
return idPart ? idPart.replace("id=", "") : null;
|
|
24910
24908
|
}).filter(Boolean);
|
|
24911
|
-
this.
|
|
24909
|
+
this._emitStandardized("listed-ids", ids.length);
|
|
24912
24910
|
return ids;
|
|
24913
24911
|
}
|
|
24914
24912
|
/**
|
|
@@ -24950,12 +24948,12 @@ ${errorDetails}`,
|
|
|
24950
24948
|
const [ok, err, ids] = await tryFn(() => this.listIds({ limit, offset }));
|
|
24951
24949
|
if (!ok) throw err;
|
|
24952
24950
|
const results = await this.processListResults(ids, "main");
|
|
24953
|
-
this.
|
|
24951
|
+
this._emitStandardized("list", { count: results.length, errors: 0 });
|
|
24954
24952
|
return results;
|
|
24955
24953
|
}
|
|
24956
24954
|
async listPartition({ partition, partitionValues, limit, offset = 0 }) {
|
|
24957
24955
|
if (!this.config.partitions?.[partition]) {
|
|
24958
|
-
this.
|
|
24956
|
+
this._emitStandardized("list", { partition, partitionValues, count: 0, errors: 0 });
|
|
24959
24957
|
return [];
|
|
24960
24958
|
}
|
|
24961
24959
|
const partitionDef = this.config.partitions[partition];
|
|
@@ -24965,7 +24963,7 @@ ${errorDetails}`,
|
|
|
24965
24963
|
const ids = this.extractIdsFromKeys(keys).slice(offset);
|
|
24966
24964
|
const filteredIds = limit ? ids.slice(0, limit) : ids;
|
|
24967
24965
|
const results = await this.processPartitionResults(filteredIds, partition, partitionDef, keys);
|
|
24968
|
-
this.
|
|
24966
|
+
this._emitStandardized("list", { partition, partitionValues, count: results.length, errors: 0 });
|
|
24969
24967
|
return results;
|
|
24970
24968
|
}
|
|
24971
24969
|
/**
|
|
@@ -25010,7 +25008,7 @@ ${errorDetails}`,
|
|
|
25010
25008
|
}
|
|
25011
25009
|
return this.handleResourceError(err, id, context);
|
|
25012
25010
|
});
|
|
25013
|
-
this.
|
|
25011
|
+
this._emitStandardized("list", { count: results.length, errors: 0 });
|
|
25014
25012
|
return results;
|
|
25015
25013
|
}
|
|
25016
25014
|
/**
|
|
@@ -25073,10 +25071,10 @@ ${errorDetails}`,
|
|
|
25073
25071
|
*/
|
|
25074
25072
|
handleListError(error, { partition, partitionValues }) {
|
|
25075
25073
|
if (error.message.includes("Partition '") && error.message.includes("' not found")) {
|
|
25076
|
-
this.
|
|
25074
|
+
this._emitStandardized("list", { partition, partitionValues, count: 0, errors: 1 });
|
|
25077
25075
|
return [];
|
|
25078
25076
|
}
|
|
25079
|
-
this.
|
|
25077
|
+
this._emitStandardized("list", { partition, partitionValues, count: 0, errors: 1 });
|
|
25080
25078
|
return [];
|
|
25081
25079
|
}
|
|
25082
25080
|
/**
|
|
@@ -25109,7 +25107,7 @@ ${errorDetails}`,
|
|
|
25109
25107
|
throw err;
|
|
25110
25108
|
});
|
|
25111
25109
|
const finalResults = await this.executeHooks("afterGetMany", results);
|
|
25112
|
-
this.
|
|
25110
|
+
this._emitStandardized("fetched-many", ids.length);
|
|
25113
25111
|
return finalResults;
|
|
25114
25112
|
}
|
|
25115
25113
|
/**
|
|
@@ -25195,7 +25193,7 @@ ${errorDetails}`,
|
|
|
25195
25193
|
hasTotalItems: totalItems !== null
|
|
25196
25194
|
}
|
|
25197
25195
|
};
|
|
25198
|
-
this.
|
|
25196
|
+
this._emitStandardized("paginated", result2);
|
|
25199
25197
|
return result2;
|
|
25200
25198
|
});
|
|
25201
25199
|
if (ok) return result;
|
|
@@ -25265,7 +25263,7 @@ ${errorDetails}`,
|
|
|
25265
25263
|
contentType
|
|
25266
25264
|
}));
|
|
25267
25265
|
if (!ok2) throw err2;
|
|
25268
|
-
this.
|
|
25266
|
+
this._emitStandardized("content-set", { id, contentType, contentLength: buffer.length }, id);
|
|
25269
25267
|
return updatedData;
|
|
25270
25268
|
}
|
|
25271
25269
|
/**
|
|
@@ -25294,7 +25292,7 @@ ${errorDetails}`,
|
|
|
25294
25292
|
}
|
|
25295
25293
|
const buffer = Buffer.from(await response.Body.transformToByteArray());
|
|
25296
25294
|
const contentType = response.ContentType || null;
|
|
25297
|
-
this.
|
|
25295
|
+
this._emitStandardized("content-fetched", { id, contentLength: buffer.length, contentType }, id);
|
|
25298
25296
|
return {
|
|
25299
25297
|
buffer,
|
|
25300
25298
|
contentType
|
|
@@ -25326,7 +25324,7 @@ ${errorDetails}`,
|
|
|
25326
25324
|
metadata: existingMetadata
|
|
25327
25325
|
}));
|
|
25328
25326
|
if (!ok2) throw err2;
|
|
25329
|
-
this.
|
|
25327
|
+
this._emitStandardized("content-deleted", id, id);
|
|
25330
25328
|
return response;
|
|
25331
25329
|
}
|
|
25332
25330
|
/**
|
|
@@ -25638,7 +25636,7 @@ ${errorDetails}`,
|
|
|
25638
25636
|
const data = await this.get(id);
|
|
25639
25637
|
data._partition = partitionName;
|
|
25640
25638
|
data._partitionValues = partitionValues;
|
|
25641
|
-
this.
|
|
25639
|
+
this._emitStandardized("partition-fetched", data, data.id);
|
|
25642
25640
|
return data;
|
|
25643
25641
|
}
|
|
25644
25642
|
/**
|
|
@@ -25887,6 +25885,120 @@ ${errorDetails}`,
|
|
|
25887
25885
|
}
|
|
25888
25886
|
return out;
|
|
25889
25887
|
}
|
|
25888
|
+
// ============================================================================
|
|
25889
|
+
// STATE MACHINE METHODS
|
|
25890
|
+
// ============================================================================
|
|
25891
|
+
/**
|
|
25892
|
+
* State machine accessor object
|
|
25893
|
+
* Provides namespaced access to state machine operations
|
|
25894
|
+
* @type {Object}
|
|
25895
|
+
* @property {Function} send - Trigger state transition
|
|
25896
|
+
* @property {Function} get - Get current state
|
|
25897
|
+
* @property {Function} canTransition - Check if transition is valid
|
|
25898
|
+
* @property {Function} getValidEvents - Get valid events for current state
|
|
25899
|
+
* @property {Function} initialize - Initialize entity with initial state
|
|
25900
|
+
* @property {Function} history - Get transition history
|
|
25901
|
+
* @example
|
|
25902
|
+
* await orders.state.send('order-123', 'CONFIRM');
|
|
25903
|
+
* const state = await orders.state.get('order-123');
|
|
25904
|
+
* const canShip = await orders.state.canTransition('order-123', 'SHIP');
|
|
25905
|
+
*/
|
|
25906
|
+
get state() {
|
|
25907
|
+
const resource = this;
|
|
25908
|
+
const throwIfNoStateMachine = () => {
|
|
25909
|
+
if (!resource._stateMachine) {
|
|
25910
|
+
throw new Error(
|
|
25911
|
+
`No state machine configured for resource '${resource.name}'. Ensure StateMachinePlugin is installed and configured for this resource.`
|
|
25912
|
+
);
|
|
25913
|
+
}
|
|
25914
|
+
};
|
|
25915
|
+
return {
|
|
25916
|
+
/**
|
|
25917
|
+
* Trigger a state transition
|
|
25918
|
+
* @param {string} id - Entity ID
|
|
25919
|
+
* @param {string} event - Event name
|
|
25920
|
+
* @param {Object} [eventData] - Event data
|
|
25921
|
+
* @returns {Promise<Object>} Transition result
|
|
25922
|
+
* @example
|
|
25923
|
+
* await orders.state.send('order-123', 'CONFIRM', { confirmedBy: 'user-456' });
|
|
25924
|
+
*/
|
|
25925
|
+
send: async (id, event, eventData) => {
|
|
25926
|
+
throwIfNoStateMachine();
|
|
25927
|
+
return resource._stateMachine.send(id, event, eventData);
|
|
25928
|
+
},
|
|
25929
|
+
/**
|
|
25930
|
+
* Get current state of an entity
|
|
25931
|
+
* @param {string} id - Entity ID
|
|
25932
|
+
* @returns {Promise<string>} Current state
|
|
25933
|
+
* @example
|
|
25934
|
+
* const currentState = await orders.state.get('order-123');
|
|
25935
|
+
*/
|
|
25936
|
+
get: async (id) => {
|
|
25937
|
+
throwIfNoStateMachine();
|
|
25938
|
+
return resource._stateMachine.getState(id);
|
|
25939
|
+
},
|
|
25940
|
+
/**
|
|
25941
|
+
* Check if a transition is valid
|
|
25942
|
+
* @param {string} id - Entity ID
|
|
25943
|
+
* @param {string} event - Event name
|
|
25944
|
+
* @returns {Promise<boolean>} True if transition is valid
|
|
25945
|
+
* @example
|
|
25946
|
+
* const canConfirm = await orders.state.canTransition('order-123', 'CONFIRM');
|
|
25947
|
+
*/
|
|
25948
|
+
canTransition: async (id, event) => {
|
|
25949
|
+
throwIfNoStateMachine();
|
|
25950
|
+
return resource._stateMachine.canTransition(id, event);
|
|
25951
|
+
},
|
|
25952
|
+
/**
|
|
25953
|
+
* Get all valid events for the current state
|
|
25954
|
+
* @param {string} id - Entity ID
|
|
25955
|
+
* @returns {Promise<Array<string>>} Array of valid event names
|
|
25956
|
+
* @example
|
|
25957
|
+
* const events = await orders.state.getValidEvents('order-123');
|
|
25958
|
+
* // Returns: ['SHIP', 'CANCEL']
|
|
25959
|
+
*/
|
|
25960
|
+
getValidEvents: async (id) => {
|
|
25961
|
+
throwIfNoStateMachine();
|
|
25962
|
+
return resource._stateMachine.getValidEvents(id);
|
|
25963
|
+
},
|
|
25964
|
+
/**
|
|
25965
|
+
* Initialize entity with initial state
|
|
25966
|
+
* @param {string} id - Entity ID
|
|
25967
|
+
* @param {Object} [context] - Initial context data
|
|
25968
|
+
* @returns {Promise<void>}
|
|
25969
|
+
* @example
|
|
25970
|
+
* await orders.state.initialize('order-456', { customerId: 'user-123' });
|
|
25971
|
+
*/
|
|
25972
|
+
initialize: async (id, context) => {
|
|
25973
|
+
throwIfNoStateMachine();
|
|
25974
|
+
return resource._stateMachine.initializeEntity(id, context);
|
|
25975
|
+
},
|
|
25976
|
+
/**
|
|
25977
|
+
* Get transition history for an entity
|
|
25978
|
+
* @param {string} id - Entity ID
|
|
25979
|
+
* @param {Object} [options] - Query options
|
|
25980
|
+
* @param {number} [options.limit=100] - Maximum number of transitions
|
|
25981
|
+
* @param {Date} [options.fromDate] - Filter from date
|
|
25982
|
+
* @param {Date} [options.toDate] - Filter to date
|
|
25983
|
+
* @returns {Promise<Array<Object>>} Transition history
|
|
25984
|
+
* @example
|
|
25985
|
+
* const history = await orders.state.history('order-123', { limit: 50 });
|
|
25986
|
+
*/
|
|
25987
|
+
history: async (id, options) => {
|
|
25988
|
+
throwIfNoStateMachine();
|
|
25989
|
+
return resource._stateMachine.getTransitionHistory(id, options);
|
|
25990
|
+
}
|
|
25991
|
+
};
|
|
25992
|
+
}
|
|
25993
|
+
/**
|
|
25994
|
+
* Internal method to attach state machine instance
|
|
25995
|
+
* This is called by StateMachinePlugin during initialization
|
|
25996
|
+
* @private
|
|
25997
|
+
* @param {Object} stateMachine - State machine instance
|
|
25998
|
+
*/
|
|
25999
|
+
_attachStateMachine(stateMachine) {
|
|
26000
|
+
this._stateMachine = stateMachine;
|
|
26001
|
+
}
|
|
25890
26002
|
}
|
|
25891
26003
|
function validateResourceConfig(config) {
|
|
25892
26004
|
const errors = [];
|
|
@@ -26047,7 +26159,7 @@ class Database extends EventEmitter {
|
|
|
26047
26159
|
})();
|
|
26048
26160
|
this.version = "1";
|
|
26049
26161
|
this.s3dbVersion = (() => {
|
|
26050
|
-
const [ok, err, version] = tryFn(() => true ? "13.
|
|
26162
|
+
const [ok, err, version] = tryFn(() => true ? "13.3.0" : "latest");
|
|
26051
26163
|
return ok ? version : "latest";
|
|
26052
26164
|
})();
|
|
26053
26165
|
this._resourcesMap = {};
|
|
@@ -26892,7 +27004,7 @@ class Database extends EventEmitter {
|
|
|
26892
27004
|
if (!existingVersionData || existingVersionData.hash !== newHash) {
|
|
26893
27005
|
await this.uploadMetadataFile();
|
|
26894
27006
|
}
|
|
26895
|
-
this.emit("
|
|
27007
|
+
this.emit("s3db.resourceUpdated", name);
|
|
26896
27008
|
return existingResource;
|
|
26897
27009
|
}
|
|
26898
27010
|
const existingMetadata = this.savedMetadata?.resources?.[name];
|
|
@@ -26929,7 +27041,7 @@ class Database extends EventEmitter {
|
|
|
26929
27041
|
this._applyMiddlewares(resource, middlewares);
|
|
26930
27042
|
}
|
|
26931
27043
|
await this.uploadMetadataFile();
|
|
26932
|
-
this.emit("
|
|
27044
|
+
this.emit("s3db.resourceCreated", name);
|
|
26933
27045
|
return resource;
|
|
26934
27046
|
}
|
|
26935
27047
|
/**
|
|
@@ -27055,6 +27167,7 @@ class Database extends EventEmitter {
|
|
|
27055
27167
|
return !!this.savedMetadata;
|
|
27056
27168
|
}
|
|
27057
27169
|
async disconnect() {
|
|
27170
|
+
await this.emit("disconnected", /* @__PURE__ */ new Date());
|
|
27058
27171
|
await tryFn(async () => {
|
|
27059
27172
|
if (this.pluginList && this.pluginList.length > 0) {
|
|
27060
27173
|
for (const plugin of this.pluginList) {
|
|
@@ -27205,7 +27318,7 @@ class Database extends EventEmitter {
|
|
|
27205
27318
|
for (const hook of hooks) {
|
|
27206
27319
|
const [ok, error] = await tryFn(() => hook({ database: this, ...context }));
|
|
27207
27320
|
if (!ok) {
|
|
27208
|
-
this.emit("
|
|
27321
|
+
this.emit("hookError", { event, error, context });
|
|
27209
27322
|
if (this.strictHooks) {
|
|
27210
27323
|
throw new DatabaseError(`Hook execution failed for event '${event}': ${error.message}`, {
|
|
27211
27324
|
event,
|
|
@@ -27764,7 +27877,7 @@ class SqsReplicator extends BaseReplicator {
|
|
|
27764
27877
|
region: this.region,
|
|
27765
27878
|
credentials: this.config.credentials
|
|
27766
27879
|
});
|
|
27767
|
-
this.emit("initialized", {
|
|
27880
|
+
this.emit("db:plugin:initialized", {
|
|
27768
27881
|
replicator: this.name,
|
|
27769
27882
|
queueUrl: this.queueUrl,
|
|
27770
27883
|
queues: this.queues,
|
|
@@ -27794,7 +27907,7 @@ class SqsReplicator extends BaseReplicator {
|
|
|
27794
27907
|
});
|
|
27795
27908
|
const result2 = await this.sqsClient.send(command);
|
|
27796
27909
|
results.push({ queueUrl, messageId: result2.MessageId });
|
|
27797
|
-
this.emit("replicated", {
|
|
27910
|
+
this.emit("plg:replicator:replicated", {
|
|
27798
27911
|
replicator: this.name,
|
|
27799
27912
|
resource,
|
|
27800
27913
|
operation,
|
|
@@ -27810,7 +27923,7 @@ class SqsReplicator extends BaseReplicator {
|
|
|
27810
27923
|
if (this.config.verbose) {
|
|
27811
27924
|
console.warn(`[SqsReplicator] Replication failed for ${resource}: ${err.message}`);
|
|
27812
27925
|
}
|
|
27813
|
-
this.emit("
|
|
27926
|
+
this.emit("plg:replicator:error", {
|
|
27814
27927
|
replicator: this.name,
|
|
27815
27928
|
resource,
|
|
27816
27929
|
operation,
|
|
@@ -28207,7 +28320,7 @@ ${createSQL}`);
|
|
|
28207
28320
|
}
|
|
28208
28321
|
}
|
|
28209
28322
|
const success = errors.length === 0;
|
|
28210
|
-
this.emit("replicated", {
|
|
28323
|
+
this.emit("plg:replicator:replicated", {
|
|
28211
28324
|
replicator: this.name,
|
|
28212
28325
|
resourceName,
|
|
28213
28326
|
operation,
|
|
@@ -28493,7 +28606,7 @@ class WebhookReplicator extends BaseReplicator {
|
|
|
28493
28606
|
});
|
|
28494
28607
|
throw error;
|
|
28495
28608
|
}
|
|
28496
|
-
this.emit("initialized", {
|
|
28609
|
+
this.emit("db:plugin:initialized", {
|
|
28497
28610
|
replicator: this.name,
|
|
28498
28611
|
url: this.url,
|
|
28499
28612
|
method: this.method,
|
|
@@ -28513,7 +28626,7 @@ class WebhookReplicator extends BaseReplicator {
|
|
|
28513
28626
|
const payload = this.createPayload(resource, operation, transformedData, id, beforeData);
|
|
28514
28627
|
const response = await this._makeRequest(payload);
|
|
28515
28628
|
if (response.success) {
|
|
28516
|
-
this.emit("replicated", {
|
|
28629
|
+
this.emit("plg:replicator:replicated", {
|
|
28517
28630
|
replicator: this.name,
|
|
28518
28631
|
resource,
|
|
28519
28632
|
operation,
|
|
@@ -28530,7 +28643,7 @@ class WebhookReplicator extends BaseReplicator {
|
|
|
28530
28643
|
if (this.config.verbose) {
|
|
28531
28644
|
console.warn(`[WebhookReplicator] Replication failed for ${resource}: ${err.message}`);
|
|
28532
28645
|
}
|
|
28533
|
-
this.emit("
|
|
28646
|
+
this.emit("plg:replicator:error", {
|
|
28534
28647
|
replicator: this.name,
|
|
28535
28648
|
resource,
|
|
28536
28649
|
operation,
|
|
@@ -28809,13 +28922,13 @@ class ReplicatorPlugin extends Plugin {
|
|
|
28809
28922
|
}
|
|
28810
28923
|
};
|
|
28811
28924
|
this.eventHandlers.set(resource.name, {
|
|
28812
|
-
|
|
28813
|
-
|
|
28814
|
-
|
|
28925
|
+
inserted: insertHandler,
|
|
28926
|
+
updated: updateHandler,
|
|
28927
|
+
deleted: deleteHandler
|
|
28815
28928
|
});
|
|
28816
|
-
resource.on("
|
|
28817
|
-
resource.on("
|
|
28818
|
-
resource.on("
|
|
28929
|
+
resource.on("inserted", insertHandler);
|
|
28930
|
+
resource.on("updated", updateHandler);
|
|
28931
|
+
resource.on("deleted", deleteHandler);
|
|
28819
28932
|
this.eventListenersInstalled.add(resource.name);
|
|
28820
28933
|
}
|
|
28821
28934
|
async onInstall() {
|
|
@@ -29205,9 +29318,9 @@ class ReplicatorPlugin extends Plugin {
|
|
|
29205
29318
|
const resource = this.database.resources[resourceName];
|
|
29206
29319
|
const handlers = this.eventHandlers.get(resourceName);
|
|
29207
29320
|
if (resource && handlers) {
|
|
29208
|
-
resource.off("
|
|
29209
|
-
resource.off("
|
|
29210
|
-
resource.off("
|
|
29321
|
+
resource.off("inserted", handlers.inserted);
|
|
29322
|
+
resource.off("updated", handlers.updated);
|
|
29323
|
+
resource.off("deleted", handlers.deleted);
|
|
29211
29324
|
}
|
|
29212
29325
|
}
|
|
29213
29326
|
}
|
|
@@ -29336,7 +29449,7 @@ class S3QueuePlugin extends Plugin {
|
|
|
29336
29449
|
createdAt: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
|
|
29337
29450
|
};
|
|
29338
29451
|
await plugin.queueResource.insert(queueEntry);
|
|
29339
|
-
plugin.emit("message
|
|
29452
|
+
plugin.emit("plg:s3-queue:message-enqueued", { id: record.id, queueId: queueEntry.id });
|
|
29340
29453
|
return record;
|
|
29341
29454
|
};
|
|
29342
29455
|
resource.queueStats = async function() {
|
|
@@ -30597,6 +30710,7 @@ class StateMachinePlugin extends Plugin {
|
|
|
30597
30710
|
// entityId -> currentState
|
|
30598
30711
|
});
|
|
30599
30712
|
}
|
|
30713
|
+
await this._attachStateMachinesToResources();
|
|
30600
30714
|
await this._setupTriggers();
|
|
30601
30715
|
this.emit("db:plugin:initialized", { machines: Array.from(this.machines.keys()) });
|
|
30602
30716
|
}
|
|
@@ -31438,6 +31552,58 @@ class StateMachinePlugin extends Plugin {
|
|
|
31438
31552
|
}
|
|
31439
31553
|
}
|
|
31440
31554
|
}
|
|
31555
|
+
/**
|
|
31556
|
+
* Attach state machine instances to their associated resources
|
|
31557
|
+
* This enables the resource API: resource.state(id, event)
|
|
31558
|
+
* @private
|
|
31559
|
+
*/
|
|
31560
|
+
async _attachStateMachinesToResources() {
|
|
31561
|
+
for (const [machineName, machineConfig] of Object.entries(this.config.stateMachines)) {
|
|
31562
|
+
const resourceConfig = machineConfig.config || machineConfig;
|
|
31563
|
+
if (!resourceConfig.resource) {
|
|
31564
|
+
if (this.config.verbose) {
|
|
31565
|
+
console.log(`[StateMachinePlugin] Machine '${machineName}' has no resource configured, skipping attachment`);
|
|
31566
|
+
}
|
|
31567
|
+
continue;
|
|
31568
|
+
}
|
|
31569
|
+
let resource;
|
|
31570
|
+
if (typeof resourceConfig.resource === "string") {
|
|
31571
|
+
resource = this.database.resources[resourceConfig.resource];
|
|
31572
|
+
if (!resource) {
|
|
31573
|
+
console.warn(
|
|
31574
|
+
`[StateMachinePlugin] Resource '${resourceConfig.resource}' not found for machine '${machineName}'. Resource API will not be available.`
|
|
31575
|
+
);
|
|
31576
|
+
continue;
|
|
31577
|
+
}
|
|
31578
|
+
} else {
|
|
31579
|
+
resource = resourceConfig.resource;
|
|
31580
|
+
}
|
|
31581
|
+
const machineProxy = {
|
|
31582
|
+
send: async (id, event, eventData) => {
|
|
31583
|
+
return this.send(machineName, id, event, eventData);
|
|
31584
|
+
},
|
|
31585
|
+
getState: async (id) => {
|
|
31586
|
+
return this.getState(machineName, id);
|
|
31587
|
+
},
|
|
31588
|
+
canTransition: async (id, event) => {
|
|
31589
|
+
return this.canTransition(machineName, id, event);
|
|
31590
|
+
},
|
|
31591
|
+
getValidEvents: async (id) => {
|
|
31592
|
+
return this.getValidEvents(machineName, id);
|
|
31593
|
+
},
|
|
31594
|
+
initializeEntity: async (id, context) => {
|
|
31595
|
+
return this.initializeEntity(machineName, id, context);
|
|
31596
|
+
},
|
|
31597
|
+
getTransitionHistory: async (id, options) => {
|
|
31598
|
+
return this.getTransitionHistory(machineName, id, options);
|
|
31599
|
+
}
|
|
31600
|
+
};
|
|
31601
|
+
resource._attachStateMachine(machineProxy);
|
|
31602
|
+
if (this.config.verbose) {
|
|
31603
|
+
console.log(`[StateMachinePlugin] Attached machine '${machineName}' to resource '${resource.name}'`);
|
|
31604
|
+
}
|
|
31605
|
+
}
|
|
31606
|
+
}
|
|
31441
31607
|
async start() {
|
|
31442
31608
|
if (this.config.verbose) {
|
|
31443
31609
|
console.log(`[StateMachinePlugin] Started with ${this.machines.size} state machines`);
|
|
@@ -43702,6 +43868,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43702
43868
|
const commandName = command.constructor.name;
|
|
43703
43869
|
const input = command.input || {};
|
|
43704
43870
|
this.emit("cl:request", commandName, input);
|
|
43871
|
+
this.emit("command.request", commandName, input);
|
|
43705
43872
|
let response;
|
|
43706
43873
|
try {
|
|
43707
43874
|
switch (commandName) {
|
|
@@ -43730,6 +43897,7 @@ class MemoryClient extends EventEmitter {
|
|
|
43730
43897
|
throw new Error(`Unsupported command: ${commandName}`);
|
|
43731
43898
|
}
|
|
43732
43899
|
this.emit("cl:response", commandName, response, input);
|
|
43900
|
+
this.emit("command.response", commandName, response, input);
|
|
43733
43901
|
return response;
|
|
43734
43902
|
} catch (error) {
|
|
43735
43903
|
const mappedError = mapAwsError(error, {
|
|
@@ -44025,13 +44193,13 @@ class MemoryClient extends EventEmitter {
|
|
|
44025
44193
|
if (keys.length > 0) {
|
|
44026
44194
|
const result = await this.deleteObjects(keys);
|
|
44027
44195
|
totalDeleted = result.Deleted.length;
|
|
44028
|
-
this.emit("
|
|
44196
|
+
this.emit("deleteAll", {
|
|
44029
44197
|
prefix,
|
|
44030
44198
|
batch: totalDeleted,
|
|
44031
44199
|
total: totalDeleted
|
|
44032
44200
|
});
|
|
44033
44201
|
}
|
|
44034
|
-
this.emit("
|
|
44202
|
+
this.emit("deleteAllComplete", {
|
|
44035
44203
|
prefix,
|
|
44036
44204
|
totalDeleted
|
|
44037
44205
|
});
|
|
@@ -44078,7 +44246,7 @@ class MemoryClient extends EventEmitter {
|
|
|
44078
44246
|
});
|
|
44079
44247
|
}
|
|
44080
44248
|
}
|
|
44081
|
-
this.emit("
|
|
44249
|
+
this.emit("moveAllObjects", { results, errors });
|
|
44082
44250
|
if (errors.length > 0) {
|
|
44083
44251
|
const error = new Error("Some objects could not be moved");
|
|
44084
44252
|
error.context = {
|