UNPKG

cloudrx

Version:

TypeScript library for streaming cloud provider events using RxJS with automatic persistence and replay capabilities

175 lines 6.67 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FatalError = exports.RetryError = exports.CloudProvider = exports.StreamEvent = exports.Abort = void 0; const rxjs_1 = require("rxjs"); const stream_1 = require("stream"); class Abort extends AbortController { constructor(signal) { super(); if (signal) { signal.addEventListener('abort', () => this.abort(signal.reason)); } else { ['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGQUIT'].forEach((signalType) => { process.on(signalType, () => { this.abort(new Error(`Aborted by ${signalType}`)); }); }); process.on('beforeExit', (code) => { this.abort(new Error(`Process exiting with code: ${code}`)); }); process.on('exit', (code) => { this.abort(new Error(`Process exit with code: ${code}`)); }); process.on('uncaughtException', (error) => { this.abort(error); }); process.on('unhandledRejection', (reason) => { this.abort(reason); }); } } abort(reason) { console.log('!!! got abort signal:', reason); super.abort(reason); } } exports.Abort = Abort; class StreamEvent extends stream_1.EventEmitter { } exports.StreamEvent = StreamEvent; class CloudProvider { static from(id, opts) { if (!CloudProvider.instances[id]) { CloudProvider.instances[id] = new this(id, opts).init(); } return CloudProvider.instances[id]; } constructor(id, opts) { this.id = id; this._events = new StreamEvent(); this._events.setMaxListeners(100); this._logger = opts?.logger ?? console; this._signal = opts?.signal ?? new Abort().signal; this._signal.addEventListener('abort', () => { this._events.emit('end'); delete CloudProvider.instances[this.id]; }); } get logger() { return this._logger; } get signal() { return this._signal; } init() { return new rxjs_1.Observable((subscriber) => { if (!this._init$) { this.logger.debug?.(`[${this.id}] Initializing provider`); this._init$ = this._init().pipe((0, rxjs_1.shareReplay)(1)); } else { this.logger.debug?.(`[${this.id}] Reusing existing init observable`); } const subscription = this._init$.subscribe(subscriber); return () => { subscription.unsubscribe(); }; }); } all() { return new rxjs_1.Observable((subscriber) => { const subscription = this._stream(true) .pipe((0, rxjs_1.concatAll)()) .subscribe(subscriber); return () => { subscription.unsubscribe(); }; }); } stream() { return new rxjs_1.Observable((subscriber) => { if (!this._stream$) { this.logger.debug?.(`[${this.id}] Creating new 'latest' stream`); this._stream$ = this._stream(false).pipe((0, rxjs_1.observeOn)(rxjs_1.asyncScheduler), (0, rxjs_1.shareReplay)(1)); } else { this.logger.debug?.(`[${this.id}] Reusing existing 'latest' stream`); } this.logger.debug?.(`[${this.id}] Starting stream subscription`); const subscription = this._stream$ .pipe((0, rxjs_1.takeUntil)((0, rxjs_1.fromEvent)(this._events, 'stop')), (0, rxjs_1.tap)((events) => { if (events.length > 0) { this.logger.debug?.(`[${this.id}] Stream emitted ${events.length} events`); } this._events.emit('start'); }), (0, rxjs_1.concatAll)()) .subscribe({ next: (event) => subscriber.next(event), error: (error) => { this.logger.debug?.(`[${this.id}] Stream error:`, error); subscriber.error(error); }, complete: () => { this.logger.debug?.(`[${this.id}] Stream completed`); subscriber.complete(); }, }); return () => { subscription.unsubscribe(); }; }); } store(item) { this.logger.debug?.(`[${this.id}] Starting store operation for:`, item); return new rxjs_1.Observable((subscriber) => { let stream; let match; const matcher$ = this._store(item).pipe((0, rxjs_1.observeOn)(rxjs_1.asyncScheduler), (0, rxjs_1.take)(1), (0, rxjs_1.shareReplay)(1)); const stream$ = this.stream(); this.logger.debug?.(`[${this.id}] Waiting for stream to start`); this._events.once('start', () => { this.logger.debug?.(`[${this.id}] Stream started, setting up matcher`); match = (0, rxjs_1.combineLatest)([stream$, matcher$]) .pipe((0, rxjs_1.takeUntil)((0, rxjs_1.fromEvent)(this._events, 'stop')), (0, rxjs_1.filter)(([event, matcher]) => matcher(event)), (0, rxjs_1.map)(([event]) => { const streamed = this._unmarshall(event); this.logger.debug?.(`[${this.id}] Matched event, returning item:`, streamed); delete streamed.__marker__; return streamed; })) .subscribe((item) => { this.logger.debug?.(`[${this.id}] Store operation completed`); subscriber.next(item); subscriber.complete(); }); }); stream = stream$ .pipe((0, rxjs_1.takeUntil)((0, rxjs_1.fromEvent)(this._events, 'start'))) .subscribe(); return () => { stream?.unsubscribe(); match?.unsubscribe(); }; }); } unmarshall(event) { return this._unmarshall(event); } } exports.CloudProvider = CloudProvider; CloudProvider.instances = {}; class RetryError extends Error { constructor(message) { super(message); this.name = 'RetryError'; } } exports.RetryError = RetryError; class FatalError extends Error { constructor(message) { super(message); this.name = 'FatalError'; } } exports.FatalError = FatalError; //# sourceMappingURL=base.js.map