@blamejs/exceptd-skills 0.9.1
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/AGENTS.md +232 -0
- package/ARCHITECTURE.md +267 -0
- package/CHANGELOG.md +616 -0
- package/CONTEXT.md +203 -0
- package/LICENSE +200 -0
- package/NOTICE +82 -0
- package/README.md +307 -0
- package/SECURITY.md +73 -0
- package/agents/README.md +81 -0
- package/agents/report-generator.md +156 -0
- package/agents/skill-updater.md +102 -0
- package/agents/source-validator.md +119 -0
- package/agents/threat-researcher.md +149 -0
- package/bin/exceptd.js +183 -0
- package/data/_indexes/_meta.json +88 -0
- package/data/_indexes/activity-feed.json +362 -0
- package/data/_indexes/catalog-summaries.json +229 -0
- package/data/_indexes/chains.json +7135 -0
- package/data/_indexes/currency.json +359 -0
- package/data/_indexes/did-ladders.json +451 -0
- package/data/_indexes/frequency.json +2072 -0
- package/data/_indexes/handoff-dag.json +476 -0
- package/data/_indexes/jurisdiction-clocks.json +967 -0
- package/data/_indexes/jurisdiction-map.json +536 -0
- package/data/_indexes/recipes.json +319 -0
- package/data/_indexes/section-offsets.json +3656 -0
- package/data/_indexes/stale-content.json +14 -0
- package/data/_indexes/summary-cards.json +1736 -0
- package/data/_indexes/theater-fingerprints.json +381 -0
- package/data/_indexes/token-budget.json +2137 -0
- package/data/_indexes/trigger-table.json +1374 -0
- package/data/_indexes/xref.json +818 -0
- package/data/atlas-ttps.json +282 -0
- package/data/cve-catalog.json +496 -0
- package/data/cwe-catalog.json +1017 -0
- package/data/d3fend-catalog.json +738 -0
- package/data/dlp-controls.json +1039 -0
- package/data/exploit-availability.json +67 -0
- package/data/framework-control-gaps.json +1255 -0
- package/data/global-frameworks.json +2913 -0
- package/data/rfc-references.json +324 -0
- package/data/zeroday-lessons.json +377 -0
- package/keys/public.pem +3 -0
- package/lib/framework-gap.js +328 -0
- package/lib/job-queue.js +195 -0
- package/lib/lint-skills.js +536 -0
- package/lib/prefetch.js +372 -0
- package/lib/refresh-external.js +713 -0
- package/lib/schemas/cve-catalog.schema.json +151 -0
- package/lib/schemas/manifest.schema.json +106 -0
- package/lib/schemas/skill-frontmatter.schema.json +113 -0
- package/lib/scoring.js +149 -0
- package/lib/sign.js +197 -0
- package/lib/ttp-mapper.js +80 -0
- package/lib/validate-catalog-meta.js +198 -0
- package/lib/validate-cve-catalog.js +213 -0
- package/lib/validate-indexes.js +83 -0
- package/lib/validate-package.js +162 -0
- package/lib/validate-vendor.js +85 -0
- package/lib/verify.js +216 -0
- package/lib/worker-pool.js +84 -0
- package/manifest-snapshot.json +1833 -0
- package/manifest.json +2108 -0
- package/orchestrator/README.md +124 -0
- package/orchestrator/dispatcher.js +140 -0
- package/orchestrator/event-bus.js +146 -0
- package/orchestrator/index.js +874 -0
- package/orchestrator/pipeline.js +201 -0
- package/orchestrator/scanner.js +327 -0
- package/orchestrator/scheduler.js +137 -0
- package/package.json +113 -0
- package/sbom.cdx.json +158 -0
- package/scripts/audit-cross-skill.js +261 -0
- package/scripts/audit-perf.js +160 -0
- package/scripts/bootstrap.js +205 -0
- package/scripts/build-indexes.js +721 -0
- package/scripts/builders/activity-feed.js +79 -0
- package/scripts/builders/catalog-summaries.js +67 -0
- package/scripts/builders/currency.js +109 -0
- package/scripts/builders/cwe-chains.js +105 -0
- package/scripts/builders/did-ladders.js +149 -0
- package/scripts/builders/frequency.js +89 -0
- package/scripts/builders/jurisdiction-clocks.js +126 -0
- package/scripts/builders/recipes.js +159 -0
- package/scripts/builders/section-offsets.js +162 -0
- package/scripts/builders/stale-content.js +171 -0
- package/scripts/builders/summary-cards.js +166 -0
- package/scripts/builders/theater-fingerprints.js +198 -0
- package/scripts/builders/token-budget.js +96 -0
- package/scripts/check-manifest-snapshot.js +217 -0
- package/scripts/predeploy.js +267 -0
- package/scripts/refresh-manifest-snapshot.js +57 -0
- package/scripts/refresh-sbom.js +222 -0
- package/skills/age-gates-child-safety/skill.md +456 -0
- package/skills/ai-attack-surface/skill.md +282 -0
- package/skills/ai-c2-detection/skill.md +440 -0
- package/skills/ai-risk-management/skill.md +311 -0
- package/skills/api-security/skill.md +287 -0
- package/skills/attack-surface-pentest/skill.md +381 -0
- package/skills/cloud-security/skill.md +384 -0
- package/skills/compliance-theater/skill.md +365 -0
- package/skills/container-runtime-security/skill.md +379 -0
- package/skills/coordinated-vuln-disclosure/skill.md +473 -0
- package/skills/defensive-countermeasure-mapping/skill.md +300 -0
- package/skills/dlp-gap-analysis/skill.md +337 -0
- package/skills/email-security-anti-phishing/skill.md +206 -0
- package/skills/exploit-scoring/skill.md +331 -0
- package/skills/framework-gap-analysis/skill.md +374 -0
- package/skills/fuzz-testing-strategy/skill.md +313 -0
- package/skills/global-grc/skill.md +564 -0
- package/skills/identity-assurance/skill.md +272 -0
- package/skills/incident-response-playbook/skill.md +546 -0
- package/skills/kernel-lpe-triage/skill.md +303 -0
- package/skills/mcp-agent-trust/skill.md +326 -0
- package/skills/mlops-security/skill.md +325 -0
- package/skills/ot-ics-security/skill.md +340 -0
- package/skills/policy-exception-gen/skill.md +437 -0
- package/skills/pqc-first/skill.md +546 -0
- package/skills/rag-pipeline-security/skill.md +294 -0
- package/skills/researcher/skill.md +310 -0
- package/skills/sector-energy/skill.md +409 -0
- package/skills/sector-federal-government/skill.md +302 -0
- package/skills/sector-financial/skill.md +398 -0
- package/skills/sector-healthcare/skill.md +373 -0
- package/skills/security-maturity-tiers/skill.md +464 -0
- package/skills/skill-update-loop/skill.md +463 -0
- package/skills/supply-chain-integrity/skill.md +318 -0
- package/skills/threat-model-currency/skill.md +404 -0
- package/skills/threat-modeling-methodology/skill.md +312 -0
- package/skills/webapp-security/skill.md +281 -0
- package/skills/zeroday-gap-learn/skill.md +350 -0
- package/vendor/blamejs/LICENSE +201 -0
- package/vendor/blamejs/README.md +54 -0
- package/vendor/blamejs/_PROVENANCE.json +54 -0
- package/vendor/blamejs/retry.js +335 -0
- package/vendor/blamejs/worker-pool.js +418 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* vendor/blamejs/retry.js
|
|
4
|
+
*
|
|
5
|
+
* VENDORED — flattened and stripped from blamejs@1442f17 / lib/retry.js.
|
|
6
|
+
* Apache-2.0 (see vendor/blamejs/LICENSE). Provenance: vendor/blamejs/_PROVENANCE.json.
|
|
7
|
+
*
|
|
8
|
+
* Surface preserved:
|
|
9
|
+
* - withRetry(fn, opts) — exponential backoff + crypto jitter + AbortSignal honor
|
|
10
|
+
* - isRetryable(err) — default classifier (HTTP + Node net codes)
|
|
11
|
+
* - backoffDelay(attempt, opts) — pure helper
|
|
12
|
+
* - CircuitBreaker — failure-threshold / cooldown / half-open probe
|
|
13
|
+
* - DEFAULT_RETRY, DEFAULT_BREAKER
|
|
14
|
+
*
|
|
15
|
+
* Stripped vs. upstream:
|
|
16
|
+
* - observability event sink (every _emitEvent call → no-op)
|
|
17
|
+
* - audit hooks
|
|
18
|
+
* - lazy-require for safeAsync.sleep — replaced with a stdlib AbortSignal-aware sleep
|
|
19
|
+
* - numeric-checks dependency — replaced with two inline predicates
|
|
20
|
+
* - constants C.TIME.seconds(...) — replaced with literal ms values
|
|
21
|
+
*
|
|
22
|
+
* Behavior preserved verbatim:
|
|
23
|
+
* - backoff math: capped exponential * (1 - jitterFactor * U(0,1)) using crypto.randomInt
|
|
24
|
+
* - classifier semantics (HTTP 408/425/429/5xx, ECONNRESET et al)
|
|
25
|
+
* - permanent error short-circuit
|
|
26
|
+
* - signal-aware cancellation during the backoff sleep
|
|
27
|
+
*
|
|
28
|
+
* Do NOT hand-edit. To re-vendor, copy the upstream file, re-apply the
|
|
29
|
+
* strip rules above, refresh sha256 in _PROVENANCE.json, then re-run
|
|
30
|
+
* `npm run validate-vendor`.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
var nodeCrypto = require("crypto");
|
|
34
|
+
|
|
35
|
+
// ---- Defaults ----
|
|
36
|
+
|
|
37
|
+
var DEFAULT_RETRY = Object.freeze({
|
|
38
|
+
maxAttempts: 5,
|
|
39
|
+
baseDelayMs: 100,
|
|
40
|
+
maxDelayMs: 10 * 1000, // 10s
|
|
41
|
+
jitterFactor: 0.5,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
var HTTP = Object.freeze({
|
|
45
|
+
BAD_REQUEST: 0x190,
|
|
46
|
+
UNAUTHORIZED: 0x191,
|
|
47
|
+
FORBIDDEN: 0x193,
|
|
48
|
+
NOT_FOUND: 0x194,
|
|
49
|
+
METHOD_NOT_ALLOWED: 0x195,
|
|
50
|
+
REQUEST_TIMEOUT: 0x198,
|
|
51
|
+
CONFLICT: 0x199,
|
|
52
|
+
GONE: 0x19a,
|
|
53
|
+
LENGTH_REQUIRED: 0x19b,
|
|
54
|
+
PRECONDITION_FAILED: 0x19c,
|
|
55
|
+
PAYLOAD_TOO_LARGE: 0x19d,
|
|
56
|
+
URI_TOO_LONG: 0x19e,
|
|
57
|
+
UNSUPPORTED_MEDIA_TYPE: 0x19f,
|
|
58
|
+
RANGE_NOT_SATISFIABLE: 0x1a0,
|
|
59
|
+
EXPECTATION_FAILED: 0x1a1,
|
|
60
|
+
UNPROCESSABLE_ENTITY: 0x1a6,
|
|
61
|
+
TOO_EARLY: 0x1a9,
|
|
62
|
+
TOO_MANY_REQUESTS: 0x1ad,
|
|
63
|
+
UNAVAILABLE_FOR_LEGAL_REASONS: 0x1c3,
|
|
64
|
+
INTERNAL_SERVER_ERROR: 0x1f4,
|
|
65
|
+
NOT_IMPLEMENTED: 0x1f5,
|
|
66
|
+
BAD_GATEWAY: 0x1f6,
|
|
67
|
+
SERVICE_UNAVAILABLE: 0x1f7,
|
|
68
|
+
GATEWAY_TIMEOUT: 0x1f8,
|
|
69
|
+
HTTP_VERSION_NOT_SUPPORTED: 0x1f9,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
var NON_RETRYABLE_HTTP_STATUS = new Set([
|
|
73
|
+
HTTP.BAD_REQUEST, HTTP.UNAUTHORIZED, HTTP.FORBIDDEN, HTTP.NOT_FOUND,
|
|
74
|
+
HTTP.METHOD_NOT_ALLOWED, HTTP.CONFLICT, HTTP.GONE, HTTP.LENGTH_REQUIRED,
|
|
75
|
+
HTTP.PRECONDITION_FAILED, HTTP.PAYLOAD_TOO_LARGE, HTTP.URI_TOO_LONG,
|
|
76
|
+
HTTP.UNSUPPORTED_MEDIA_TYPE, HTTP.RANGE_NOT_SATISFIABLE, HTTP.EXPECTATION_FAILED,
|
|
77
|
+
HTTP.UNPROCESSABLE_ENTITY, HTTP.UNAVAILABLE_FOR_LEGAL_REASONS,
|
|
78
|
+
HTTP.NOT_IMPLEMENTED, HTTP.HTTP_VERSION_NOT_SUPPORTED,
|
|
79
|
+
]);
|
|
80
|
+
|
|
81
|
+
var RETRYABLE_HTTP_STATUS = new Set([
|
|
82
|
+
HTTP.REQUEST_TIMEOUT, HTTP.TOO_EARLY, HTTP.TOO_MANY_REQUESTS,
|
|
83
|
+
HTTP.INTERNAL_SERVER_ERROR, HTTP.BAD_GATEWAY, HTTP.SERVICE_UNAVAILABLE,
|
|
84
|
+
HTTP.GATEWAY_TIMEOUT,
|
|
85
|
+
]);
|
|
86
|
+
|
|
87
|
+
var RETRYABLE_NET_ERRORS = new Set([
|
|
88
|
+
"ECONNRESET", "ECONNREFUSED", "ECONNABORTED", "ETIMEDOUT",
|
|
89
|
+
"EPIPE", "EAGAIN", "ENOTFOUND", "ENETUNREACH",
|
|
90
|
+
]);
|
|
91
|
+
|
|
92
|
+
var STATE_CLOSED = "closed";
|
|
93
|
+
var STATE_OPEN = "open";
|
|
94
|
+
var STATE_HALF = "half-open";
|
|
95
|
+
|
|
96
|
+
var DEFAULT_BREAKER = Object.freeze({
|
|
97
|
+
failureThreshold: 10,
|
|
98
|
+
cooldownMs: 30 * 1000,
|
|
99
|
+
successThreshold: 2,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// ---- Inlined predicates (replaces numeric-checks dep) ----
|
|
103
|
+
|
|
104
|
+
function _isPositiveInt(v) {
|
|
105
|
+
return typeof v === "number" && Number.isFinite(v) && Number.isInteger(v) && v > 0;
|
|
106
|
+
}
|
|
107
|
+
function _isNonNegFinite(v) {
|
|
108
|
+
return typeof v === "number" && Number.isFinite(v) && v >= 0;
|
|
109
|
+
}
|
|
110
|
+
function _isAbortSignal(s) {
|
|
111
|
+
return s != null && typeof s === "object" &&
|
|
112
|
+
typeof s.aborted === "boolean" &&
|
|
113
|
+
typeof s.addEventListener === "function";
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ---- Signal-aware sleep (replaces safeAsync.sleep dep) ----
|
|
117
|
+
|
|
118
|
+
function _sleep(ms, opts) {
|
|
119
|
+
return new Promise(function (resolve, reject) {
|
|
120
|
+
var signal = opts && opts.signal;
|
|
121
|
+
if (signal && signal.aborted) {
|
|
122
|
+
var abErr = new Error("aborted");
|
|
123
|
+
abErr.name = "AbortError";
|
|
124
|
+
reject(abErr);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
var t = setTimeout(function () {
|
|
128
|
+
if (signal) signal.removeEventListener("abort", onAbort);
|
|
129
|
+
resolve();
|
|
130
|
+
}, ms);
|
|
131
|
+
// NOTE: deliberately do NOT unref the timer here. CLI callers
|
|
132
|
+
// (refresh-external, prefetch, build-indexes --parallel) rely on the
|
|
133
|
+
// retry sleep to keep the event loop alive; unref-ing causes the
|
|
134
|
+
// process to exit before the backoff completes when there are no
|
|
135
|
+
// other open handles.
|
|
136
|
+
function onAbort() {
|
|
137
|
+
clearTimeout(t);
|
|
138
|
+
var err = new Error("aborted");
|
|
139
|
+
err.name = "AbortError";
|
|
140
|
+
reject(err);
|
|
141
|
+
}
|
|
142
|
+
if (signal) signal.addEventListener("abort", onAbort, { once: true });
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ---- Validation ----
|
|
147
|
+
|
|
148
|
+
function _validateRetryOpts(opts) {
|
|
149
|
+
if (!_isPositiveInt(opts.maxAttempts)) {
|
|
150
|
+
throw new TypeError("retry.withRetry: maxAttempts must be a positive integer");
|
|
151
|
+
}
|
|
152
|
+
if (!_isNonNegFinite(opts.baseDelayMs)) {
|
|
153
|
+
throw new TypeError("retry.withRetry: baseDelayMs must be a non-negative finite number");
|
|
154
|
+
}
|
|
155
|
+
if (!_isNonNegFinite(opts.maxDelayMs)) {
|
|
156
|
+
throw new TypeError("retry.withRetry: maxDelayMs must be a non-negative finite number");
|
|
157
|
+
}
|
|
158
|
+
if (typeof opts.jitterFactor !== "number" || !isFinite(opts.jitterFactor) ||
|
|
159
|
+
opts.jitterFactor < 0 || opts.jitterFactor > 1) {
|
|
160
|
+
throw new TypeError("retry.withRetry: jitterFactor must be a finite number in [0, 1]");
|
|
161
|
+
}
|
|
162
|
+
if (opts.isRetryable !== undefined && typeof opts.isRetryable !== "function") {
|
|
163
|
+
throw new TypeError("retry.withRetry: isRetryable must be a function or undefined");
|
|
164
|
+
}
|
|
165
|
+
if (opts.onRetry !== undefined && typeof opts.onRetry !== "function") {
|
|
166
|
+
throw new TypeError("retry.withRetry: onRetry must be a function or undefined");
|
|
167
|
+
}
|
|
168
|
+
if (opts.signal !== undefined && opts.signal !== null && !_isAbortSignal(opts.signal)) {
|
|
169
|
+
throw new TypeError("retry.withRetry: signal must be an AbortSignal or undefined");
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function _validateBreakerOpts(name, opts) {
|
|
174
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
175
|
+
throw new TypeError("retry.CircuitBreaker: name must be a non-empty string");
|
|
176
|
+
}
|
|
177
|
+
if (!_isPositiveInt(opts.failureThreshold)) {
|
|
178
|
+
throw new TypeError("retry.CircuitBreaker: failureThreshold must be a positive integer");
|
|
179
|
+
}
|
|
180
|
+
if (!_isNonNegFinite(opts.cooldownMs)) {
|
|
181
|
+
throw new TypeError("retry.CircuitBreaker: cooldownMs must be a non-negative finite number");
|
|
182
|
+
}
|
|
183
|
+
if (!_isPositiveInt(opts.successThreshold)) {
|
|
184
|
+
throw new TypeError("retry.CircuitBreaker: successThreshold must be a positive integer");
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ---- Public surface ----
|
|
189
|
+
|
|
190
|
+
function isRetryable(err) {
|
|
191
|
+
if (!err) return false;
|
|
192
|
+
if (err.isObjectStoreError && err.permanent) return false;
|
|
193
|
+
if (err.permanent) return false;
|
|
194
|
+
if (typeof err.statusCode === "number") {
|
|
195
|
+
if (RETRYABLE_HTTP_STATUS.has(err.statusCode)) return true;
|
|
196
|
+
if (NON_RETRYABLE_HTTP_STATUS.has(err.statusCode)) return false;
|
|
197
|
+
if (err.statusCode >= 500) return true;
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
if (err.code && RETRYABLE_NET_ERRORS.has(err.code)) return true;
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function backoffDelay(attempt, opts) {
|
|
205
|
+
if (!_isPositiveInt(attempt)) {
|
|
206
|
+
throw new TypeError("retry.backoffDelay: attempt must be a positive integer");
|
|
207
|
+
}
|
|
208
|
+
opts = opts || DEFAULT_RETRY;
|
|
209
|
+
var base = opts.baseDelayMs * Math.pow(2, attempt - 1);
|
|
210
|
+
var capped = Math.min(base, opts.maxDelayMs);
|
|
211
|
+
var jitterDenom = 1000000;
|
|
212
|
+
var jitter = capped * opts.jitterFactor * (nodeCrypto.randomInt(0, jitterDenom) / jitterDenom);
|
|
213
|
+
return Math.floor(capped - jitter);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function withRetry(fn, opts) {
|
|
217
|
+
if (typeof fn !== "function") {
|
|
218
|
+
throw new TypeError("retry.withRetry: fn must be a function, got " + typeof fn);
|
|
219
|
+
}
|
|
220
|
+
opts = Object.assign({}, DEFAULT_RETRY, opts || {});
|
|
221
|
+
_validateRetryOpts(opts);
|
|
222
|
+
var classify = (typeof opts.isRetryable === "function") ? opts.isRetryable : isRetryable;
|
|
223
|
+
var lastErr = null;
|
|
224
|
+
for (var attempt = 1; attempt <= opts.maxAttempts; attempt++) {
|
|
225
|
+
try {
|
|
226
|
+
return await fn(attempt);
|
|
227
|
+
} catch (err) {
|
|
228
|
+
lastErr = err;
|
|
229
|
+
var retryable = classify(err);
|
|
230
|
+
if (!retryable || attempt === opts.maxAttempts) {
|
|
231
|
+
throw err;
|
|
232
|
+
}
|
|
233
|
+
var delay = backoffDelay(attempt, opts);
|
|
234
|
+
if (typeof opts.onRetry === "function") {
|
|
235
|
+
try { opts.onRetry({ attempt: attempt, delay: delay, error: err }); }
|
|
236
|
+
catch (_cbErr) { /* observer error never crashes the retry loop */ }
|
|
237
|
+
}
|
|
238
|
+
await _sleep(delay, { signal: opts.signal });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
throw lastErr;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ---- Circuit breaker ----
|
|
245
|
+
|
|
246
|
+
class CircuitBreaker {
|
|
247
|
+
constructor(name, opts) {
|
|
248
|
+
var merged = Object.assign({}, DEFAULT_BREAKER, opts || {});
|
|
249
|
+
_validateBreakerOpts(name || "", merged);
|
|
250
|
+
this.name = name;
|
|
251
|
+
this.opts = merged;
|
|
252
|
+
this.state = STATE_CLOSED;
|
|
253
|
+
this.consecutiveFailures = 0;
|
|
254
|
+
this.consecutiveSuccesses = 0;
|
|
255
|
+
this.openedAt = 0;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async wrap(fn) {
|
|
259
|
+
if (typeof fn !== "function") {
|
|
260
|
+
throw new TypeError("retry.CircuitBreaker.wrap: fn must be a function, got " + typeof fn);
|
|
261
|
+
}
|
|
262
|
+
if (this.state === STATE_OPEN) {
|
|
263
|
+
if (Date.now() - this.openedAt >= this.opts.cooldownMs) {
|
|
264
|
+
this._transition(STATE_OPEN, STATE_HALF);
|
|
265
|
+
} else {
|
|
266
|
+
var err = new Error("circuit breaker '" + this.name + "' is OPEN");
|
|
267
|
+
err.code = "CIRCUIT_OPEN";
|
|
268
|
+
err.permanent = false;
|
|
269
|
+
err.isObjectStoreError = true;
|
|
270
|
+
throw err;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
try {
|
|
274
|
+
var result = await fn();
|
|
275
|
+
this._onSuccess();
|
|
276
|
+
return result;
|
|
277
|
+
} catch (e) {
|
|
278
|
+
this._onFailure(e);
|
|
279
|
+
throw e;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
_transition(_from, to) {
|
|
284
|
+
this.state = to;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
_onSuccess() {
|
|
288
|
+
if (this.state === STATE_HALF) {
|
|
289
|
+
this.consecutiveSuccesses += 1;
|
|
290
|
+
if (this.consecutiveSuccesses >= this.opts.successThreshold) {
|
|
291
|
+
this._transition(STATE_HALF, STATE_CLOSED);
|
|
292
|
+
this.consecutiveFailures = 0;
|
|
293
|
+
this.consecutiveSuccesses = 0;
|
|
294
|
+
}
|
|
295
|
+
} else {
|
|
296
|
+
this.consecutiveFailures = 0;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
_onFailure(err) {
|
|
301
|
+
if (err && err.permanent) return;
|
|
302
|
+
if (err && err.isObjectStoreError && err.code === "CIRCUIT_OPEN") return;
|
|
303
|
+
this.consecutiveFailures += 1;
|
|
304
|
+
this.consecutiveSuccesses = 0;
|
|
305
|
+
if (this.state === STATE_HALF) {
|
|
306
|
+
this._transition(STATE_HALF, STATE_OPEN);
|
|
307
|
+
this.openedAt = Date.now();
|
|
308
|
+
} else if (this.state === STATE_CLOSED && this.consecutiveFailures >= this.opts.failureThreshold) {
|
|
309
|
+
this._transition(STATE_CLOSED, STATE_OPEN);
|
|
310
|
+
this.openedAt = Date.now();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
getState() { return this.state; }
|
|
315
|
+
|
|
316
|
+
reset() {
|
|
317
|
+
this.state = STATE_CLOSED;
|
|
318
|
+
this.consecutiveFailures = 0;
|
|
319
|
+
this.consecutiveSuccesses = 0;
|
|
320
|
+
this.openedAt = 0;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
module.exports = {
|
|
325
|
+
withRetry: withRetry,
|
|
326
|
+
isRetryable: isRetryable,
|
|
327
|
+
backoffDelay: backoffDelay,
|
|
328
|
+
CircuitBreaker: CircuitBreaker,
|
|
329
|
+
DEFAULT_RETRY: DEFAULT_RETRY,
|
|
330
|
+
DEFAULT_BREAKER: DEFAULT_BREAKER,
|
|
331
|
+
RETRYABLE_HTTP_STATUS: Array.from(RETRYABLE_HTTP_STATUS),
|
|
332
|
+
NON_RETRYABLE_HTTP_STATUS: Array.from(NON_RETRYABLE_HTTP_STATUS),
|
|
333
|
+
RETRYABLE_NET_ERRORS: Array.from(RETRYABLE_NET_ERRORS),
|
|
334
|
+
STATES: { CLOSED: STATE_CLOSED, OPEN: STATE_OPEN, HALF_OPEN: STATE_HALF },
|
|
335
|
+
};
|