UNPKG

file-routing-expressjs

Version:

A dependency-free, flexible, system-based file routing for Express.

120 lines 4.45 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; var circuit_breaker_exports = {}; __export(circuit_breaker_exports, { default: () => circuit_breaker_default }); module.exports = __toCommonJS(circuit_breaker_exports); var import_validators = require("../helpers/validators"); const STATE = /* @__PURE__ */ new Map(); const ID_SYMBOL = Symbol("id"); const KEY_SYMBOL = Symbol("key"); const circuitBreakerPlugin = { name: "circuit-breaker", validateConfig(config) { if (!config || typeof config.threshold !== "number" || config.threshold <= 0) { throw new Error(`[circuit-breaker] "threshold" must be > 0`); } if (typeof config.cooldown !== "number" || config.cooldown <= 0) { throw new Error(`[circuit-breaker] "cooldown" must be > 0`); } if ((0, import_validators.isDefined)(config.considerError) && !(0, import_validators.isFunction)(config.considerError)) { throw new Error(`[circuit-breaker] "considerError" must be a function`); } }, onRequest(ctx, next) { return __async(this, null, function* () { var _a; const config = ctx.config; if ((0, import_validators.isNotDefined)(config)) return next(); const key = `${ctx.req.method}:${((_a = ctx.req.route) == null ? void 0 : _a.path) || ctx.req.path}`; const now = Date.now(); let state = STATE.get(key); if ((0, import_validators.isNotDefined)(state)) { state = { failures: 0, openUntil: 0, halfOpen: false, inFlight: false }; STATE.set(key, state); } ctx.state[ID_SYMBOL] = { failureCounted: false }; ctx.state[KEY_SYMBOL] = key; if (state.openUntil > now) { ctx.res.status(503).send("Service unavailable (circuit open)"); return; } if (state.failures >= config.threshold && state.openUntil <= now) { if (state.inFlight) { ctx.res.status(503).send("Service unavailable (half-open busy)"); return; } state.halfOpen = true; state.inFlight = true; } try { yield next(); if (ctx.res.statusCode < 500) { state.failures = 0; state.halfOpen = false; } } finally { state.inFlight = false; } }); }, onError(err, ctx) { return __async(this, null, function* () { const config = ctx.config; if ((0, import_validators.isNotDefined)(config)) return; const key = ctx.state[KEY_SYMBOL]; if ((0, import_validators.isNotDefined)(key)) return; const state = STATE.get(key); if ((0, import_validators.isNotDefined)(state)) return; const shouldCount = (0, import_validators.isDefined)(config.considerError) ? config.considerError(err) : true; if (!shouldCount) return; const meta = ctx.state[ID_SYMBOL]; if (!meta || meta.failureCounted) return; meta.failureCounted = true; state.failures++; if (state.failures >= config.threshold) { state.openUntil = Date.now() + config.cooldown; state.halfOpen = false; } }); } }; var circuit_breaker_default = circuitBreakerPlugin; //# sourceMappingURL=circuit-breaker.js.map