UNPKG

@actyx/sdk

Version:
104 lines 5.66 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.eventsMonotonic = void 0; const Ord_1 = require("fp-ts/lib/Ord"); const rxjs_1 = require("../../node_modules/rxjs"); const operators_1 = require("../../node_modules/rxjs/operators"); const types_1 = require("../types"); const util_1 = require("../util"); const log_1 = require("./log"); const bufferOp_1 = require("../util/bufferOp"); const eventKeyGreater = (0, Ord_1.gt)(types_1.EventKey.ord); const horizonFilter = (fixedStart) => (fixedStart === null || fixedStart === void 0 ? void 0 : fixedStart.horizon) ? `${fixedStart.horizon.lamport}/${fixedStart.horizon.stream}` : undefined; /** * Create a new endpoint, based on the given EventStore and SnapshotStore. * The returned function itself is stateless between subsequent calls -- * all state is within the EventStore itself. */ const eventsMonotonic = (eventStore) => { // Stream realtime events from the given point on. // Actyx delivers events before `present` (i.e. before the first `offsets` msg) in ascending order. // As soon as time-travel would occur, the stream terminates with a TimetravelMsg. const monotonicFrom = (session, subscriptions, fixedStart) => { const horizon = horizonFilter(fixedStart); let diagPrinted = false; return eventStore .subscribeMonotonic(session, (fixedStart === null || fixedStart === void 0 ? void 0 : fixedStart.from) || {}, subscriptions, horizon) .pipe((0, bufferOp_1.bufferOp)(1), (0, operators_1.concatMap)((next) => { const emit = []; let events = null; for (const x of next) { switch (x.type) { case 'diagnostic': { if (!diagPrinted) { diagPrinted = true; log_1.default.submono.debug(`(${session}) AQL ${x.severity}: ${x.message}`); } break; } case 'event': { x.caughtUp && log_1.default.submono.debug('caught up', session); if (events === null) { events = { type: types_1.MsgType.events, events: [x], caughtUp: x.caughtUp }; emit.push(events); } else { events.events.push(x); events.caughtUp = x.caughtUp; } break; } case 'offsets': { if (events === null) { events = { type: types_1.MsgType.events, events: [], caughtUp: true }; emit.push(events); } else { events.caughtUp = true; } break; } case 'timeTravel': { events = null; emit.push({ type: types_1.MsgType.timetravel, trigger: x.newStart }); } } } return emit; })); }; // Given a FixedStart point, check whether we can reach `present` without time travel. // If so, apply whenValid. Otherwise apply whenInvalid to the earliest chunk between start and present. const validateFixedStart = (subscriptions, present, attemptStartFrom, whenInvalid, whenValid) => { return eventStore .query(attemptStartFrom.from, present, subscriptions, types_1.EventsSortOrder.Ascending, horizonFilter(attemptStartFrom)) .pipe((0, operators_1.defaultIfEmpty)(null), (0, operators_1.first)(), (0, operators_1.concatMap)((earliest) => earliest && eventKeyGreater(attemptStartFrom.latestEventKey, earliest) ? whenInvalid(earliest) : whenValid())); }; // Client thinks it has valid offsets to start from -- it may be wrong, though! const startFromFixedOffsets = (session, subscriptions, present) => (attemptStartFrom) => { const whenValid = () => monotonicFrom(session, subscriptions, attemptStartFrom); const whenInvalid = (earliest) => { log_1.default.submono.debug(session, 'discarding outdated requested FixedStart', types_1.EventKey.format(attemptStartFrom.latestEventKey), 'due to', types_1.EventKey.format(earliest)); // TODO this time travel msg should also have a good `high` element // (consider this if/when ever implementing this Rust-side) return (0, rxjs_1.of)(timeTravelMsg(session, attemptStartFrom.latestEventKey, [earliest])); }; return validateFixedStart(subscriptions, present, attemptStartFrom, whenInvalid, whenValid); }; return (session, subscriptions, attemptStartFrom) => { // Client explicitly requests us to start at a certain point return (0, rxjs_1.from)(eventStore.offsets()).pipe((0, operators_1.concatMap)((offsets) => startFromFixedOffsets(session, subscriptions, offsets.present)(attemptStartFrom))); }; }; exports.eventsMonotonic = eventsMonotonic; const timeTravelMsg = (session, previousHead, next) => { log_1.default.submono.info(session, 'must time-travel back to:', types_1.EventKey.format(next[0])); const high = (0, util_1.getInsertionIndex)(next, previousHead, types_1.EventKey.ord.compare) - 1; return { type: types_1.MsgType.timetravel, trigger: next[0], }; }; //# sourceMappingURL=subscribe_monotonic.js.map