s3db.js 13.3.1 → 13.4.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/README.md +34 -10
- package/dist/s3db.cjs.js +102 -23
- package/dist/s3db.cjs.js.map +1 -1
- package/dist/s3db.es.js +102 -23
- package/dist/s3db.es.js.map +1 -1
- package/package.json +15 -2
- package/src/plugins/audit.plugin.js +427 -0
- package/src/plugins/costs.plugin.js +524 -0
- package/src/plugins/fulltext.plugin.js +484 -0
- package/src/plugins/metrics.plugin.js +575 -0
- package/src/plugins/queue-consumer.plugin.js +607 -19
- package/src/plugins/state-machine.plugin.js +132 -26
|
@@ -1170,10 +1170,22 @@ export class StateMachinePlugin extends Plugin {
|
|
|
1170
1170
|
|
|
1171
1171
|
/**
|
|
1172
1172
|
* Setup an event-based trigger
|
|
1173
|
+
* Supports both old API (trigger.event) and new API (trigger.eventName + eventSource)
|
|
1173
1174
|
* @private
|
|
1174
1175
|
*/
|
|
1175
1176
|
async _setupEventTrigger(machineId, stateName, trigger, triggerName) {
|
|
1176
|
-
|
|
1177
|
+
// Support both old API (event) and new API (eventName)
|
|
1178
|
+
const baseEventName = trigger.eventName || trigger.event;
|
|
1179
|
+
const eventSource = trigger.eventSource;
|
|
1180
|
+
|
|
1181
|
+
if (!baseEventName) {
|
|
1182
|
+
throw new StateMachineError(`Event trigger '${triggerName}' must have either 'event' or 'eventName' property`, {
|
|
1183
|
+
operation: '_setupEventTrigger',
|
|
1184
|
+
machineId,
|
|
1185
|
+
stateName,
|
|
1186
|
+
triggerName
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1177
1189
|
|
|
1178
1190
|
// Create event listener
|
|
1179
1191
|
const eventHandler = async (eventData) => {
|
|
@@ -1181,6 +1193,26 @@ export class StateMachinePlugin extends Plugin {
|
|
|
1181
1193
|
|
|
1182
1194
|
for (const entity of entities) {
|
|
1183
1195
|
try {
|
|
1196
|
+
// Resolve dynamic event name if it's a function
|
|
1197
|
+
let resolvedEventName;
|
|
1198
|
+
if (typeof baseEventName === 'function') {
|
|
1199
|
+
resolvedEventName = baseEventName(entity.context);
|
|
1200
|
+
} else {
|
|
1201
|
+
resolvedEventName = baseEventName;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
// Skip if event name doesn't match (for dynamic event names)
|
|
1205
|
+
// This allows filtering events by entity context
|
|
1206
|
+
if (eventSource && typeof baseEventName === 'function') {
|
|
1207
|
+
// For resource-specific events with dynamic names, we need to check
|
|
1208
|
+
// if this specific event matches this entity
|
|
1209
|
+
// The eventData will contain the ID that was part of the event name
|
|
1210
|
+
const eventIdMatch = eventData?.id || eventData?.entityId;
|
|
1211
|
+
if (eventIdMatch && entity.entityId !== eventIdMatch) {
|
|
1212
|
+
continue; // Not for this entity
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1184
1216
|
// Check condition if provided
|
|
1185
1217
|
if (trigger.condition) {
|
|
1186
1218
|
const shouldTrigger = await trigger.condition(entity.context, entity.entityId, eventData);
|
|
@@ -1198,33 +1230,92 @@ export class StateMachinePlugin extends Plugin {
|
|
|
1198
1230
|
}
|
|
1199
1231
|
}
|
|
1200
1232
|
|
|
1201
|
-
//
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1233
|
+
// NEW: Support targetState for automatic transitions
|
|
1234
|
+
if (trigger.targetState) {
|
|
1235
|
+
// Automatic transition to target state
|
|
1236
|
+
await this._transition(
|
|
1237
|
+
machineId,
|
|
1238
|
+
entity.entityId,
|
|
1239
|
+
stateName,
|
|
1240
|
+
trigger.targetState,
|
|
1241
|
+
'TRIGGER',
|
|
1242
|
+
{ ...entity.context, eventData, triggerName }
|
|
1243
|
+
);
|
|
1209
1244
|
|
|
1210
|
-
|
|
1245
|
+
// Update resource's stateField if configured
|
|
1246
|
+
const machine = this.machines.get(machineId);
|
|
1247
|
+
const resourceConfig = machine.config;
|
|
1248
|
+
if (resourceConfig.resource && resourceConfig.stateField) {
|
|
1249
|
+
// Get the resource instance
|
|
1250
|
+
let resource;
|
|
1251
|
+
if (typeof resourceConfig.resource === 'string') {
|
|
1252
|
+
resource = await this.database.getResource(resourceConfig.resource);
|
|
1253
|
+
} else {
|
|
1254
|
+
resource = resourceConfig.resource;
|
|
1255
|
+
}
|
|
1211
1256
|
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1257
|
+
// Update the state field in the resource
|
|
1258
|
+
if (resource) {
|
|
1259
|
+
const [ok] = await tryFn(() =>
|
|
1260
|
+
resource.patch(entity.entityId, { [resourceConfig.stateField]: trigger.targetState })
|
|
1261
|
+
);
|
|
1262
|
+
if (!ok && this.config.verbose) {
|
|
1263
|
+
console.warn(`[StateMachinePlugin] Failed to update resource stateField for entity ${entity.entityId}`);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
// Execute entry action of target state if exists
|
|
1269
|
+
const targetStateConfig = machine.config.states[trigger.targetState];
|
|
1270
|
+
if (targetStateConfig?.entry) {
|
|
1271
|
+
await this._executeAction(
|
|
1272
|
+
targetStateConfig.entry,
|
|
1273
|
+
{ ...entity.context, eventData },
|
|
1274
|
+
'TRIGGER',
|
|
1275
|
+
machineId,
|
|
1276
|
+
entity.entityId
|
|
1277
|
+
);
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
// Emit transition event
|
|
1281
|
+
this.emit('plg:state-machine:transition', {
|
|
1282
|
+
machineId,
|
|
1283
|
+
entityId: entity.entityId,
|
|
1284
|
+
from: stateName,
|
|
1285
|
+
to: trigger.targetState,
|
|
1286
|
+
event: 'TRIGGER',
|
|
1287
|
+
context: { ...entity.context, eventData, triggerName }
|
|
1218
1288
|
});
|
|
1289
|
+
} else if (trigger.action) {
|
|
1290
|
+
// Execute trigger action with event data in context
|
|
1291
|
+
const result = await this._executeAction(
|
|
1292
|
+
trigger.action,
|
|
1293
|
+
{ ...entity.context, eventData },
|
|
1294
|
+
'TRIGGER',
|
|
1295
|
+
machineId,
|
|
1296
|
+
entity.entityId
|
|
1297
|
+
);
|
|
1298
|
+
|
|
1299
|
+
// Send success event if configured
|
|
1300
|
+
if (trigger.sendEvent) {
|
|
1301
|
+
await this.send(machineId, entity.entityId, trigger.sendEvent, {
|
|
1302
|
+
...entity.context,
|
|
1303
|
+
triggerResult: result,
|
|
1304
|
+
eventData
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1219
1307
|
}
|
|
1220
1308
|
|
|
1309
|
+
await this._incrementTriggerCount(machineId, entity.entityId, triggerName);
|
|
1310
|
+
|
|
1221
1311
|
this.emit('plg:state-machine:trigger-executed', {
|
|
1222
1312
|
machineId,
|
|
1223
1313
|
entityId: entity.entityId,
|
|
1224
1314
|
state: stateName,
|
|
1225
1315
|
trigger: triggerName,
|
|
1226
1316
|
type: 'event',
|
|
1227
|
-
eventName
|
|
1317
|
+
eventName: resolvedEventName,
|
|
1318
|
+
targetState: trigger.targetState
|
|
1228
1319
|
});
|
|
1229
1320
|
} catch (error) {
|
|
1230
1321
|
if (this.config.verbose) {
|
|
@@ -1234,20 +1325,35 @@ export class StateMachinePlugin extends Plugin {
|
|
|
1234
1325
|
}
|
|
1235
1326
|
};
|
|
1236
1327
|
|
|
1237
|
-
//
|
|
1238
|
-
if (
|
|
1239
|
-
|
|
1240
|
-
|
|
1328
|
+
// NEW: Support eventSource for resource-specific events
|
|
1329
|
+
if (eventSource) {
|
|
1330
|
+
// Listen to events from a specific resource
|
|
1331
|
+
// Resource events are typically: inserted, updated, deleted
|
|
1332
|
+
const baseEvent = typeof baseEventName === 'function' ? 'updated' : baseEventName;
|
|
1333
|
+
|
|
1334
|
+
eventSource.on(baseEvent, eventHandler);
|
|
1241
1335
|
|
|
1242
1336
|
if (this.config.verbose) {
|
|
1243
|
-
console.log(`[StateMachinePlugin] Listening to
|
|
1337
|
+
console.log(`[StateMachinePlugin] Listening to resource event '${baseEvent}' from '${eventSource.name}' for trigger '${triggerName}'`);
|
|
1244
1338
|
}
|
|
1245
1339
|
} else {
|
|
1246
|
-
//
|
|
1247
|
-
|
|
1340
|
+
// Original behavior: listen to database or plugin events
|
|
1341
|
+
const staticEventName = typeof baseEventName === 'function' ? 'updated' : baseEventName;
|
|
1248
1342
|
|
|
1249
|
-
if (
|
|
1250
|
-
|
|
1343
|
+
if (staticEventName.startsWith('db:')) {
|
|
1344
|
+
const dbEventName = staticEventName.substring(3); // Remove 'db:' prefix
|
|
1345
|
+
this.database.on(dbEventName, eventHandler);
|
|
1346
|
+
|
|
1347
|
+
if (this.config.verbose) {
|
|
1348
|
+
console.log(`[StateMachinePlugin] Listening to database event '${dbEventName}' for trigger '${triggerName}'`);
|
|
1349
|
+
}
|
|
1350
|
+
} else {
|
|
1351
|
+
// Listen to plugin events
|
|
1352
|
+
this.on(staticEventName, eventHandler);
|
|
1353
|
+
|
|
1354
|
+
if (this.config.verbose) {
|
|
1355
|
+
console.log(`[StateMachinePlugin] Listening to plugin event '${staticEventName}' for trigger '${triggerName}'`);
|
|
1356
|
+
}
|
|
1251
1357
|
}
|
|
1252
1358
|
}
|
|
1253
1359
|
}
|