UNPKG

node-red-contrib-smartnora

Version:

Google Smart Home integration via Smart Nora https://smart-nora.eu/

116 lines (115 loc) 4.49 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HttpError = void 0; exports.publishReplayRefCountWithDelay = publishReplayRefCountWithDelay; exports.retryWithBackoff = retryWithBackoff; exports.scanWithFactory = scanWithFactory; exports.rateLimitSlidingWindow = rateLimitSlidingWindow; exports.singleton = singleton; exports.getHash = getHash; exports.cloneDeep = cloneDeep; const crypto_1 = require("crypto"); const rxjs_1 = require("rxjs"); const operators_1 = require("rxjs/operators"); function publishReplayRefCountWithDelay(delay) { return (0, operators_1.share)({ connector: () => new rxjs_1.ReplaySubject(1), resetOnComplete: true, resetOnError: true, resetOnRefCountZero: () => (0, rxjs_1.timer)(delay), }); } function retryWithBackoff(config) { const { initialInterval = 100, maxRetryCount = 6, logError, shouldRetry } = config !== null && config !== void 0 ? config : {}; return (0, operators_1.retry)({ delay: (error, retryCount) => { var _a; logError === null || logError === void 0 ? void 0 : logError(error); const attemptRetry = (_a = shouldRetry === null || shouldRetry === void 0 ? void 0 : shouldRetry(error)) !== null && _a !== void 0 ? _a : true; if (attemptRetry) { return (0, rxjs_1.timer)(Math.pow(2, retryCount) * initialInterval); } throw error; }, count: maxRetryCount, resetOnSuccess: true, }); } function scanWithFactory(next, factory) { return source => (0, rxjs_1.defer)(() => (0, operators_1.scan)(next, factory())(source)); } const NO_EVENT = Symbol('no-event'); function rateLimitSlidingWindow(windowSizeMiliseconds, numberOfEvents, mergeEvents) { return source => source.pipe((0, operators_1.scan)((ctx, msg) => { const now = new Date().getTime(); ctx.ticks = ctx.ticks.filter(t => t > now - windowSizeMiliseconds); if (ctx.ticks.length === numberOfEvents) { ctx.overflow = ctx.overflow !== NO_EVENT ? mergeEvents(msg, ctx.overflow) : msg; ctx.fwdMessage = NO_EVENT; } else { ctx.fwdMessage = ctx.overflow !== NO_EVENT ? mergeEvents(msg, ctx.overflow) : msg; ctx.overflow = NO_EVENT; ctx.ticks.push(now); } return ctx; }, { overflow: NO_EVENT, fwdMessage: NO_EVENT, ticks: [], getNextFree() { const eventsThatWereSentInTheWindow = this.ticks.slice(-numberOfEvents - 1); const nextFreeEvent = new Date(eventsThatWereSentInTheWindow[0] + windowSizeMiliseconds + 500); return nextFreeEvent; }, }), (0, operators_1.switchMap)(ctx => { const forward$ = ctx.fwdMessage === NO_EVENT ? rxjs_1.EMPTY : (0, rxjs_1.of)(ctx.fwdMessage); const overflow$ = ctx.overflow === NO_EVENT ? rxjs_1.EMPTY : (0, rxjs_1.timer)(ctx.getNextFree()).pipe((0, operators_1.map)(_ => { const value = ctx.overflow; if (value !== NO_EVENT) { ctx.overflow = NO_EVENT; ctx.ticks.push(new Date().getTime()); } return value; }), (0, operators_1.filter)(c => c !== NO_EVENT)); return (0, rxjs_1.concat)(forward$, overflow$); })); } function singleton() { return source => source.pipe((0, operators_1.share)({ connector: () => new rxjs_1.ReplaySubject(1), resetOnRefCountZero: true, })); } class HttpError extends Error { constructor(statusCode, content) { super(`HTTP response (${statusCode} ${content})`); this.statusCode = statusCode; this.content = content; } } exports.HttpError = HttpError; function getHash(input) { if (typeof input !== 'string') { input = JSON.stringify(flattenObject(input)); } return (0, crypto_1.createHash)('sha256').update(input).digest('base64'); } function flattenObject(input) { switch (typeof input) { case 'string': case 'boolean': case 'number': return input; } if (Array.isArray(input)) { return input; } const entries = Object.entries(input); entries.sort((a, b) => a[0].localeCompare(b[0])); return entries.map(([key, value]) => [key, flattenObject(value)]); } function cloneDeep(input) { return JSON.parse(JSON.stringify(input)); }