@event-driven-io/emmett 0.43.0-beta.35 → 0.43.0-beta.37

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/index.cjs CHANGED
@@ -1102,7 +1102,7 @@ const ProcessorCheckpoint = Object.assign((checkpoint) => checkpoint, {
1102
1102
  BEGINNING: "BEGINNING",
1103
1103
  isBeginning: (checkpoint) => checkpoint === void 0 || checkpoint === "BEGINNING",
1104
1104
  isEnd: (checkpoint) => checkpoint === "END",
1105
- compare: (a, b) => a > b ? 1 : -1
1105
+ compare: (a, b) => a > b ? 1 : a < b ? -1 : 0
1106
1106
  });
1107
1107
  const bigIntProcessorCheckpoint = (value) => bigInt.toNormalizedString(value);
1108
1108
  const parseBigIntProcessorCheckpoint = (value) => BigInt(value);
@@ -1229,7 +1229,7 @@ const processorCollector = (observability) => {
1229
1229
  //#endregion
1230
1230
  //#region src/processors/processors.ts
1231
1231
  const CurrentMessageProcessorPosition = {
1232
- compare: (a, b, compareCheckpoints = (chkA, chkB) => chkA > chkB ? 1 : chkA < chkB ? -1 : 0) => {
1232
+ compare: (a, b, compareCheckpoints = ProcessorCheckpoint.compare) => {
1233
1233
  if (a === b) return 0;
1234
1234
  if (a === "BEGINNING") return -1;
1235
1235
  if (b === "BEGINNING") return 1;
@@ -1242,14 +1242,22 @@ const CurrentMessageProcessorPosition = {
1242
1242
  return checkpoints.map((pos) => pos === void 0 ? "BEGINNING" : pos).sort((a, b) => CurrentMessageProcessorPosition.compare(a, b, compareCheckpoints))[0] ?? "BEGINNING";
1243
1243
  }
1244
1244
  };
1245
- const wasMessageHandled = (message, checkpoint) => {
1245
+ const wasMessageHandled = (message, checkpoint, compareCheckpoints = ProcessorCheckpoint.compare) => {
1246
1246
  const messageCheckpoint = getCheckpoint(message);
1247
- return messageCheckpoint !== null && messageCheckpoint !== void 0 && checkpoint !== null && checkpoint !== void 0 && messageCheckpoint <= checkpoint;
1247
+ return messageCheckpoint !== null && messageCheckpoint !== void 0 && checkpoint !== null && checkpoint !== void 0 && compareCheckpoints(messageCheckpoint, checkpoint) <= 0;
1248
1248
  };
1249
1249
  const MessageProcessorType = {
1250
1250
  PROJECTOR: "projector",
1251
1251
  REACTOR: "reactor"
1252
1252
  };
1253
+ const raceWithTimeout = (wait, timeout, timeoutMessage) => {
1254
+ if (timeout === void 0) return wait;
1255
+ let timer;
1256
+ const timeoutPromise = new Promise((_resolve, reject) => {
1257
+ timer = setTimeout(() => reject(new require_plugins.EmmettError(timeoutMessage())), timeout);
1258
+ });
1259
+ return Promise.race([wait, timeoutPromise]).finally(() => clearTimeout(timer));
1260
+ };
1253
1261
  const MessageProcessor = { result: {
1254
1262
  skip: (options) => ({
1255
1263
  type: "SKIP",
@@ -1270,7 +1278,7 @@ const getProcessorInstanceId = (processorId) => `${processorId}:${(0, uuid.v7)()
1270
1278
  const getProjectorId = (options) => `emt:processor:projector:${options.projectionName}`;
1271
1279
  const { info, error } = _event_driven_io_almanac.LogEvent;
1272
1280
  const reactor = (options) => {
1273
- const { checkpoints, processorId, processorInstanceId: instanceId = getProcessorInstanceId(processorId), type = MessageProcessorType.REACTOR, version = 1, partition = defaultProcessorPartition, hooks = {}, processingScope = defaultProcessingMessageProcessingScope, startFrom, canHandle, stopAfter } = options;
1281
+ const { checkpoints, processorId, processorInstanceId: instanceId = getProcessorInstanceId(processorId), type = MessageProcessorType.REACTOR, version = 1, partition = defaultProcessorPartition, hooks = {}, processingScope = defaultProcessingMessageProcessingScope, startFrom, canHandle, stopAfter, compareCheckpoints = ProcessorCheckpoint.compare } = options;
1274
1282
  const checkpointer = checkpoints === "DISABLED" ? inMemoryCheckpointer() : checkpoints;
1275
1283
  const collector = processorCollector(processorObservability(options));
1276
1284
  const isCustomBatch = "eachBatch" in options && !!options.eachBatch;
@@ -1315,7 +1323,26 @@ const reactor = (options) => {
1315
1323
  let isInitiated = false;
1316
1324
  let isActive = false;
1317
1325
  let lastCheckpoint = null;
1326
+ let lastStoredCheckpoint = null;
1318
1327
  let closeSignal = null;
1328
+ const checkpointWaiters = /* @__PURE__ */ new Set();
1329
+ const hasProcessed = (target) => lastCheckpoint !== null && compareCheckpoints(lastCheckpoint, target) >= 0;
1330
+ const notifyCheckpointWaiters = () => {
1331
+ for (const waiter of checkpointWaiters) if (hasProcessed(waiter.target)) {
1332
+ checkpointWaiters.delete(waiter);
1333
+ waiter.resolve();
1334
+ }
1335
+ };
1336
+ const whenProcessed = (checkpoint, options) => {
1337
+ if (hasProcessed(checkpoint)) return Promise.resolve();
1338
+ const awaiter = asyncAwaiter();
1339
+ const waiter = {
1340
+ target: checkpoint,
1341
+ resolve: awaiter.resolve
1342
+ };
1343
+ checkpointWaiters.add(waiter);
1344
+ return raceWithTimeout(awaiter.wait, options?.timeout, () => `Processor ${processorId} with instance id ${instanceId} did not process checkpoint ${checkpoint} within ${options.timeout}ms (last processed checkpoint: ${lastCheckpoint ?? "none"})`).finally(() => checkpointWaiters.delete(waiter));
1345
+ };
1319
1346
  const init = async (initOptions) => {
1320
1347
  if (isInitiated) return;
1321
1348
  if (hooks.onInit === void 0) {
@@ -1372,18 +1399,23 @@ const reactor = (options) => {
1372
1399
  log(info(`Executing onStart hook for processor ${processorId} with instance id ${instanceId}`));
1373
1400
  await hooks.onStart(context);
1374
1401
  }
1375
- if (startFrom !== void 0 && startFrom !== "CURRENT") {
1376
- if (typeof startFrom !== "string") lastCheckpoint = startFrom.lastCheckpoint;
1377
- log(info(`Processor ${processorId} with instance id ${instanceId} starting from: ${JSONSerializer.serialize(startFrom)}`));
1378
- return startFrom;
1379
- }
1380
- if (checkpointer) lastCheckpoint = (await checkpointer.read({
1402
+ if (checkpointer) lastStoredCheckpoint = (await checkpointer.read({
1381
1403
  processorId,
1382
1404
  partition
1383
1405
  }, {
1384
1406
  ...startOptions,
1385
1407
  ...context
1386
1408
  })).lastCheckpoint;
1409
+ if (startFrom !== void 0 && typeof startFrom !== "string") {
1410
+ lastCheckpoint = startFrom.lastCheckpoint;
1411
+ log(info(`Processor ${processorId} with instance id ${instanceId} starting from: ${JSONSerializer.serialize(startFrom)}`));
1412
+ return startFrom;
1413
+ }
1414
+ if (startFrom === "BEGINNING" || startFrom === "END") {
1415
+ log(info(`Processor ${processorId} with instance id ${instanceId} starting from: ${JSONSerializer.serialize(startFrom)}`));
1416
+ return startFrom;
1417
+ }
1418
+ lastCheckpoint = lastStoredCheckpoint;
1387
1419
  if (lastCheckpoint === null) {
1388
1420
  log(info(`Processor ${processorId} with instance id ${instanceId} starting from: BEGINNING`));
1389
1421
  return "BEGINNING";
@@ -1402,6 +1434,7 @@ const reactor = (options) => {
1402
1434
  get isActive() {
1403
1435
  return isActive;
1404
1436
  },
1437
+ whenProcessed,
1405
1438
  handle: async (messages, partialContext) => {
1406
1439
  if (!isActive) return Promise.resolve();
1407
1440
  return collector.startScope({
@@ -1410,8 +1443,8 @@ const reactor = (options) => {
1410
1443
  checkpoint: lastCheckpoint
1411
1444
  }, messages, async (scope) => {
1412
1445
  try {
1413
- return await processingScope(async (context) => {
1414
- const messagesAboveCheckpoint = messages.filter((message) => !wasMessageHandled(message, lastCheckpoint));
1446
+ const result = await processingScope(async (context) => {
1447
+ const messagesAboveCheckpoint = messages.filter((message) => !wasMessageHandled(message, lastCheckpoint, compareCheckpoints));
1415
1448
  const upcastedMessages = messagesAboveCheckpoint.map((message) => upcastRecordedMessage(message, options.messageOptions?.schema?.versioning)).filter((upcasted) => !canHandle || canHandle.includes(upcasted.type));
1416
1449
  const stopMessageIndex = isCustomBatch && stopAfter ? upcastedMessages.findIndex(stopAfter) : -1;
1417
1450
  const unhandledMessages = stopMessageIndex !== -1 ? upcastedMessages.slice(0, stopMessageIndex + 1) : upcastedMessages;
@@ -1431,10 +1464,13 @@ const reactor = (options) => {
1431
1464
  processorId,
1432
1465
  version,
1433
1466
  message: checkpointMessage,
1434
- lastCheckpoint,
1467
+ lastCheckpoint: lastStoredCheckpoint,
1435
1468
  partition
1436
1469
  }, context);
1437
- if (storeCheckpointResult.success) lastCheckpoint = storeCheckpointResult.newCheckpoint;
1470
+ if (storeCheckpointResult.success) {
1471
+ lastCheckpoint = storeCheckpointResult.newCheckpoint;
1472
+ lastStoredCheckpoint = storeCheckpointResult.newCheckpoint;
1473
+ }
1438
1474
  }
1439
1475
  scope.setAttributes({ [EmmettAttributes.processor.status]: messageProcessingResult?.type ?? "ack" });
1440
1476
  if (isStop) {
@@ -1445,6 +1481,8 @@ const reactor = (options) => {
1445
1481
  ...partialContext,
1446
1482
  observabilityScope: scope
1447
1483
  });
1484
+ if (result?.type !== "STOP") notifyCheckpointWaiters();
1485
+ return result;
1448
1486
  } catch (err) {
1449
1487
  scope.log(error({ err }, `Error during message processing for processor ${processorId} with instance id ${instanceId}. Stopping the processor.`));
1450
1488
  isActive = false;
@@ -1572,9 +1610,10 @@ const ProcessorStartPositions = (options) => {
1572
1610
  const position = positions.get(processorId);
1573
1611
  if (position === void 0 || position === "BEGINNING" || position === "END") return messages;
1574
1612
  const { lastCheckpoint } = position;
1613
+ const compareCheckpoints = options?.compareCheckpoints ?? ProcessorCheckpoint.compare;
1575
1614
  return messages.filter((message) => {
1576
1615
  const checkpoint = getCheckpoint(message);
1577
- return checkpoint !== null && checkpoint > lastCheckpoint;
1616
+ return checkpoint !== null && compareCheckpoints(checkpoint, lastCheckpoint) > 0;
1578
1617
  });
1579
1618
  }
1580
1619
  };