{"_id":"@apiratorjs/circuit-breaker","_rev":"11-ecdfd17a1af210ed38801364ebc793e1","name":"@apiratorjs/circuit-breaker","dist-tags":{"latest":"4.0.0"},"versions":{"1.0.0":{"name":"@apiratorjs/circuit-breaker","version":"1.0.0","keywords":["circuit-breaker","circuit breaker","fault-tolerance","resilience","microservices","distributed-systems","stability","error-handling","typescript","nodejs","reliability","cascading-failures","decorator","higher-order-function","metrics","monitoring","async","promises"],"author":{"url":"http://alexeyzaitsev.com","name":"Alexey Zaitsev"},"license":"MIT","_id":"@apiratorjs/circuit-breaker@1.0.0","maintainers":[{"name":"apiratorjs","email":"apirator.framework@gmail.com"}],"homepage":"https://github.com/apiratorjs/circuit-breaker#readme","bugs":{"url":"https://github.com/apiratorjs/circuit-breaker/issues"},"dist":{"shasum":"bc0d491a2d8c47c8882874082543ad815d61105a","tarball":"https://registry.npmjs.org/@apiratorjs/circuit-breaker/-/circuit-breaker-1.0.0.tgz","fileCount":23,"integrity":"sha512-h8cQtfohmRrwPpYx2rWJbbICOAmtEuU+zG0QMTgntC13ufFxgRsn0PWuVIIvpOH7oWCBVgqmcF/+F3tUBbtBvw==","signatures":[{"sig":"MEUCIQDySm2pJPM7V9nInZg0bvXmJ95hJA6QUTSm0kM/odkGbwIgBm1WCH/oHVLdjnJT3le0LYzgUMedwYLJi1w5/W7EnJI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":29727},"main":"dist/src/index.js","type":"commonjs","types":"dist/src/index.d.ts","engines":{"node":">=16.4.0"},"gitHead":"acc9c1c54ca9de7daac7f71d04524ae5959c2181","scripts":{"test":"npm run build && node --test dist/test","build":"rm -rf dist && tsc","watch":"tsc --watch","prepublishOnly":"npm test"},"_npmUser":{"name":"apiratorjs","email":"apirator.framework@gmail.com"},"repository":{"url":"git+https://github.com/apiratorjs/circuit-breaker.git","type":"git"},"_npmVersion":"10.9.2","description":"A robust and lightweight TypeScript circuit breaker implementation for Node.js applications. Provides fault tolerance and stability by preventing cascading failures in distributed systems with configurable thresholds, automatic recovery, and comprehensive","directories":{},"_nodeVersion":"22.17.0","dependencies":{},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","typescript":"^5.7.3","@types/node":"^22.13.1"},"_npmOperationalInternal":{"tmp":"tmp/circuit-breaker_1.0.0_1759175081838_0.8201581556040811","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@apiratorjs/circuit-breaker","version":"2.0.0","keywords":["circuit-breaker","circuit breaker","fault-tolerance","resilience","microservices","distributed-systems","stability","error-handling","typescript","nodejs","reliability","cascading-failures","decorator","higher-order-function","monitoring","async","promises"],"author":{"url":"http://alexeyzaitsev.com","name":"Alexey Zaitsev"},"license":"MIT","_id":"@apiratorjs/circuit-breaker@2.0.0","maintainers":[{"name":"apiratorjs","email":"apirator.framework@gmail.com"}],"homepage":"https://github.com/apiratorjs/circuit-breaker#readme","bugs":{"url":"https://github.com/apiratorjs/circuit-breaker/issues"},"dist":{"shasum":"e99184fc0fcc36ac4b0df43d4bceb45ee1fc977f","tarball":"https://registry.npmjs.org/@apiratorjs/circuit-breaker/-/circuit-breaker-2.0.0.tgz","fileCount":23,"integrity":"sha512-MAN2xp1RW63UEVcgSJDCEDGmZtjTwuALeNY2SBHev/oJ3sSSzrodSChgEsNfduGrKmTBOJ+QgysczokteU/sgQ==","signatures":[{"sig":"MEUCIA+A+003M9I45sitx7qewdmA4g4hvET7BL96whn57117AiEAjJCqPLpumgeuCpz9AJhPXZ30turgkpNSc7MWlNtNcpQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":26783},"main":"dist/src/index.js","type":"commonjs","types":"dist/src/index.d.ts","engines":{"node":">=16.4.0"},"gitHead":"ac2dd3beacadd7612370e3b5f6740916cab3cd30","scripts":{"test":"npm run build && node --test dist/test","build":"rm -rf dist && tsc","watch":"tsc --watch","prepublishOnly":"npm test"},"_npmUser":{"name":"apiratorjs","email":"apirator.framework@gmail.com"},"repository":{"url":"git+https://github.com/apiratorjs/circuit-breaker.git","type":"git"},"_npmVersion":"10.9.2","description":"A robust and lightweight TypeScript circuit breaker implementation for Node.js applications. Provides fault tolerance and stability by preventing cascading failures in distributed systems with configurable thresholds and automatic recovery.","directories":{},"_nodeVersion":"22.17.0","dependencies":{},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","typescript":"^5.7.3","@types/node":"^22.13.1"},"_npmOperationalInternal":{"tmp":"tmp/circuit-breaker_2.0.0_1759178755415_0.5978389300285323","host":"s3://npm-registry-packages-npm-production"}},"3.0.0":{"name":"@apiratorjs/circuit-breaker","version":"3.0.0","keywords":["circuit-breaker","circuit breaker","fault-tolerance","resilience","microservices","distributed-systems","stability","error-handling","typescript","nodejs","reliability","cascading-failures","decorator","higher-order-function","monitoring","async","promises"],"author":{"url":"http://alexeyzaitsev.com","name":"Alexey Zaitsev"},"license":"MIT","_id":"@apiratorjs/circuit-breaker@3.0.0","maintainers":[{"name":"apiratorjs","email":"apirator.framework@gmail.com"}],"homepage":"https://github.com/apiratorjs/circuit-breaker#readme","bugs":{"url":"https://github.com/apiratorjs/circuit-breaker/issues"},"dist":{"shasum":"ddbc473715355dada64b2d8c77e7c1c73d13f429","tarball":"https://registry.npmjs.org/@apiratorjs/circuit-breaker/-/circuit-breaker-3.0.0.tgz","fileCount":23,"integrity":"sha512-B2Y0H/if2+WvKN89znrs/IyhlFkWHOn23Dnhfe+NsFI+9qoC+KCEOLeEWTI0+RaHp+HYTCpyBJ1/AzL4rLk0yg==","signatures":[{"sig":"MEYCIQD2+HPI1ea6AY1XXqk39qK9LOF83rhx+eUftx54EJAntAIhAJ0PLWsY8o0M/q9nabOS/uteNAd+7lBIdFhXNGvoi+cp","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":25619},"main":"dist/src/index.js","type":"commonjs","types":"dist/src/index.d.ts","engines":{"node":">=16.4.0"},"gitHead":"95290232784954beeb42f88b9bc108dd7d59bee2","scripts":{"test":"npm run build && node --test dist/test","build":"rm -rf dist && tsc","watch":"tsc --watch","prepublishOnly":"npm test"},"_npmUser":{"name":"apiratorjs","email":"apirator.framework@gmail.com"},"repository":{"url":"git+https://github.com/apiratorjs/circuit-breaker.git","type":"git"},"_npmVersion":"10.9.2","description":"A robust and lightweight TypeScript circuit breaker implementation for Node.js applications. Provides fault tolerance and stability by preventing cascading failures in distributed systems with configurable thresholds and automatic recovery.","directories":{},"_nodeVersion":"22.17.0","dependencies":{},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","typescript":"^5.7.3","@types/node":"^22.13.1"},"_npmOperationalInternal":{"tmp":"tmp/circuit-breaker_3.0.0_1759232875091_0.4972483719962255","host":"s3://npm-registry-packages-npm-production"}},"3.0.1":{"name":"@apiratorjs/circuit-breaker","version":"3.0.1","keywords":["circuit-breaker","circuit breaker","fault-tolerance","resilience","microservices","distributed-systems","stability","error-handling","typescript","nodejs","reliability","cascading-failures","decorator","higher-order-function","monitoring","async","promises"],"author":{"url":"http://alexeyzaitsev.com","name":"Alexey Zaitsev"},"license":"MIT","_id":"@apiratorjs/circuit-breaker@3.0.1","maintainers":[{"name":"apiratorjs","email":"apirator.framework@gmail.com"}],"homepage":"https://github.com/apiratorjs/circuit-breaker#readme","bugs":{"url":"https://github.com/apiratorjs/circuit-breaker/issues"},"dist":{"shasum":"e956bdcf392d281c2b584ee71036c15b94755a91","tarball":"https://registry.npmjs.org/@apiratorjs/circuit-breaker/-/circuit-breaker-3.0.1.tgz","fileCount":23,"integrity":"sha512-44sZJUP6ISSZm3F7434UpYS5PhzUqM8sN//VBeOiWbC/R4/TUJU4XFmJql6aoKnW7mFbUjvLduhtWnF1XopZIQ==","signatures":[{"sig":"MEQCIBLsifx4fCe5uJwtPgkyD64gmBNzgbx0IgboKYMf75GIAiByeWUm9vIRQ8bRx4g1ChTUKhq1imyX7GRAkU7gMy83iw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":25710},"main":"dist/src/index.js","type":"commonjs","types":"dist/src/index.d.ts","engines":{"node":">=16.4.0"},"gitHead":"b31baf42448a59ee8a1aed76741f2bc0f9ca42ed","scripts":{"test":"npm run build && node --test dist/test","build":"rm -rf dist && tsc","watch":"tsc --watch","prepublishOnly":"npm test"},"_npmUser":{"name":"apiratorjs","email":"apirator.framework@gmail.com"},"repository":{"url":"git+https://github.com/apiratorjs/circuit-breaker.git","type":"git"},"_npmVersion":"10.9.2","description":"A robust and lightweight TypeScript circuit breaker implementation for Node.js applications. Provides fault tolerance and stability by preventing cascading failures in distributed systems with configurable thresholds and automatic recovery.","directories":{},"_nodeVersion":"22.17.0","dependencies":{},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","typescript":"^5.7.3","@types/node":"^22.13.1"},"_npmOperationalInternal":{"tmp":"tmp/circuit-breaker_3.0.1_1759251934428_0.7398283433979718","host":"s3://npm-registry-packages-npm-production"}},"3.0.2":{"name":"@apiratorjs/circuit-breaker","version":"3.0.2","keywords":["circuit-breaker","circuit breaker","fault-tolerance","resilience","microservices","distributed-systems","stability","error-handling","typescript","nodejs","reliability","cascading-failures","decorator","higher-order-function","monitoring","async","promises"],"author":{"url":"http://alexeyzaitsev.com","name":"Alexey Zaitsev"},"license":"MIT","_id":"@apiratorjs/circuit-breaker@3.0.2","maintainers":[{"name":"apiratorjs","email":"apirator.framework@gmail.com"}],"homepage":"https://github.com/apiratorjs/circuit-breaker#readme","bugs":{"url":"https://github.com/apiratorjs/circuit-breaker/issues"},"dist":{"shasum":"de43bc79d15403979ded3a024a61462b0f4a870a","tarball":"https://registry.npmjs.org/@apiratorjs/circuit-breaker/-/circuit-breaker-3.0.2.tgz","fileCount":23,"integrity":"sha512-sQX0KsBG3+OVrdUWYz5G4NhlbDaklgtcqqgQGIoCCq04fbgPH/whouCUN4AG9WbZivuss1sQqWRiV5MMErrzxw==","signatures":[{"sig":"MEUCICFC9tkL7ytRTHxQnc0FZ6/3xm42J1mRUgW0UAER+X1jAiEApwBWDjOBmYKVn22qcg4LBXDkz0svhIIULlmojnbab0Y=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":25612},"main":"dist/src/index.js","type":"commonjs","types":"dist/src/index.d.ts","engines":{"node":">=16.4.0"},"gitHead":"925605d9160439478005b0f3afebdcfcd602ccaa","scripts":{"test":"npm run build && node --test dist/test","build":"rm -rf dist && tsc","watch":"tsc --watch","prepublishOnly":"npm test"},"_npmUser":{"name":"apiratorjs","email":"apirator.framework@gmail.com"},"repository":{"url":"git+https://github.com/apiratorjs/circuit-breaker.git","type":"git"},"_npmVersion":"10.9.2","description":"A robust and lightweight TypeScript circuit breaker implementation for Node.js applications. Provides fault tolerance and stability by preventing cascading failures in distributed systems with configurable thresholds and automatic recovery.","directories":{},"_nodeVersion":"22.17.0","dependencies":{},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","typescript":"^5.7.3","@types/node":"^22.13.1"},"_npmOperationalInternal":{"tmp":"tmp/circuit-breaker_3.0.2_1759252151987_0.26940585939814965","host":"s3://npm-registry-packages-npm-production"}},"3.0.3":{"name":"@apiratorjs/circuit-breaker","version":"3.0.3","keywords":["circuit-breaker","circuit breaker","fault-tolerance","resilience","microservices","distributed-systems","stability","error-handling","typescript","nodejs","reliability","cascading-failures","decorator","higher-order-function","monitoring","async","promises"],"author":{"url":"http://alexeyzaitsev.com","name":"Alexey Zaitsev"},"license":"MIT","_id":"@apiratorjs/circuit-breaker@3.0.3","maintainers":[{"name":"apiratorjs","email":"apirator.framework@gmail.com"}],"homepage":"https://github.com/apiratorjs/circuit-breaker#readme","bugs":{"url":"https://github.com/apiratorjs/circuit-breaker/issues"},"dist":{"shasum":"34e78af1c958ba4c7cba08cb1b3e0a37e8c3f91a","tarball":"https://registry.npmjs.org/@apiratorjs/circuit-breaker/-/circuit-breaker-3.0.3.tgz","fileCount":23,"integrity":"sha512-eYtewT7TQfCCMT/RVSqIw0jBpOOuXcsBBRYJoVwisyaJ3k4ALQWHZ0QqCSrnP6D7P6GrbWeQ3GrCiFtG1QpxuA==","signatures":[{"sig":"MEYCIQDrJVCadzfcshDTbkV3PsP8lDlBh+dq1rnvn4d4hkOC8gIhAN4cB91DbDQH2LK/z0/0Tr2ynzaCg5+lGBk62otIu//A","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":26592},"main":"dist/src/index.js","type":"commonjs","types":"dist/src/index.d.ts","engines":{"node":">=16.4.0"},"gitHead":"f8bbf4c5d30043e97c839a40294f2fc80fecf78c","scripts":{"test":"npm run build && node --test dist/test","build":"rm -rf dist && tsc","watch":"tsc --watch","prepublishOnly":"npm test"},"_npmUser":{"name":"apiratorjs","email":"apirator.framework@gmail.com"},"repository":{"url":"git+https://github.com/apiratorjs/circuit-breaker.git","type":"git"},"_npmVersion":"10.9.2","description":"A robust and lightweight TypeScript circuit breaker implementation for Node.js applications. Provides fault tolerance and stability by preventing cascading failures in distributed systems with configurable thresholds and automatic recovery.","directories":{},"_nodeVersion":"22.17.0","dependencies":{},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","typescript":"^5.7.3","@types/node":"^22.13.1"},"_npmOperationalInternal":{"tmp":"tmp/circuit-breaker_3.0.3_1759253404482_0.7873003800713554","host":"s3://npm-registry-packages-npm-production"}},"3.0.4":{"name":"@apiratorjs/circuit-breaker","version":"3.0.4","keywords":["circuit-breaker","circuit breaker","fault-tolerance","resilience","microservices","distributed-systems","stability","error-handling","typescript","nodejs","reliability","cascading-failures","decorator","higher-order-function","monitoring","async","promises"],"author":{"url":"http://alexeyzaitsev.com","name":"Alexey Zaitsev"},"license":"MIT","_id":"@apiratorjs/circuit-breaker@3.0.4","maintainers":[{"name":"apiratorjs","email":"apirator.framework@gmail.com"}],"homepage":"https://github.com/apiratorjs/circuit-breaker#readme","bugs":{"url":"https://github.com/apiratorjs/circuit-breaker/issues"},"dist":{"shasum":"3f656e7545790901d94c9443de041bcd137c6f6a","tarball":"https://registry.npmjs.org/@apiratorjs/circuit-breaker/-/circuit-breaker-3.0.4.tgz","fileCount":23,"integrity":"sha512-NraT4tWbCxdqayrJuYuIRnQ8W8eZ7NBp0VDBIEcqF3wc79lZ0jRriNDPmpmVuZoxXTr6K4pRyzk2gS/QgFMh7w==","signatures":[{"sig":"MEYCIQDvsnMkx6j8wq+X/cQ6tpkpUmQ62deVk/uxqFmCFNTm9QIhALLhSX/pwHk/oab8aAhn8ApBK7DlrDHpO0xSwr/8+dVh","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":32499},"main":"dist/src/index.js","type":"commonjs","types":"dist/src/index.d.ts","engines":{"node":">=16.4.0"},"gitHead":"4d7b88560ecb4b5b33908cfe7fc32ebce5475999","scripts":{"test":"npm run build && node --test dist/test","build":"rm -rf dist && tsc","watch":"tsc --watch","prepublishOnly":"npm test"},"_npmUser":{"name":"apiratorjs","email":"apirator.framework@gmail.com"},"repository":{"url":"git+https://github.com/apiratorjs/circuit-breaker.git","type":"git"},"_npmVersion":"10.9.2","description":"A robust and lightweight TypeScript circuit breaker implementation for Node.js applications. Provides fault tolerance and stability by preventing cascading failures in distributed systems with configurable thresholds and automatic recovery.","directories":{},"_nodeVersion":"22.17.0","dependencies":{},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","typescript":"^5.7.3","@types/node":"^22.13.1"},"_npmOperationalInternal":{"tmp":"tmp/circuit-breaker_3.0.4_1759578805602_0.7906594267974354","host":"s3://npm-registry-packages-npm-production"}},"3.1.0":{"name":"@apiratorjs/circuit-breaker","version":"3.1.0","keywords":["circuit-breaker","circuit breaker","fault-tolerance","resilience","microservices","distributed-systems","stability","error-handling","typescript","nodejs","reliability","cascading-failures","decorator","higher-order-function","monitoring","async","promises"],"author":{"url":"http://alexeyzaitsev.com","name":"Alexey Zaitsev"},"license":"MIT","_id":"@apiratorjs/circuit-breaker@3.1.0","maintainers":[{"name":"apiratorjs","email":"apirator.framework@gmail.com"}],"homepage":"https://github.com/apiratorjs/circuit-breaker#readme","bugs":{"url":"https://github.com/apiratorjs/circuit-breaker/issues"},"dist":{"shasum":"13ff0039d7cc04d45c70260b5de2c14fbceed2b2","tarball":"https://registry.npmjs.org/@apiratorjs/circuit-breaker/-/circuit-breaker-3.1.0.tgz","fileCount":23,"integrity":"sha512-8/h+IfS1f0a4b7es283OUsWUD2cGJTdK38lD14r3mmEzEef4IjrVEFHN0Pr8B1XUIjpDstEOQ06PFfMbMP8MHQ==","signatures":[{"sig":"MEUCICh3D83I3d4AoUxnu1Cgkw0IUeYDF+TXI1XYvKf/2tD9AiEA6sHUYOyOeaCui982LbgPVkh8/C4tAijvQ8B7osSMXbA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":34505},"main":"dist/src/index.js","type":"commonjs","types":"dist/src/index.d.ts","engines":{"node":">=16.4.0"},"gitHead":"4ada0323c33a838aa29f0a3c5c4b7b66803dc789","scripts":{"test":"npm run build && node --test dist/test","build":"rm -rf dist && tsc","watch":"tsc --watch","prepublishOnly":"npm test"},"_npmUser":{"name":"apiratorjs","email":"apirator.framework@gmail.com"},"repository":{"url":"git+https://github.com/apiratorjs/circuit-breaker.git","type":"git"},"_npmVersion":"10.9.2","description":"A robust and lightweight TypeScript circuit breaker implementation for Node.js applications. Provides fault tolerance and stability by preventing cascading failures in distributed systems with configurable thresholds and automatic recovery.","directories":{},"_nodeVersion":"22.17.0","dependencies":{},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","typescript":"^5.7.3","@types/node":"^22.13.1"},"_npmOperationalInternal":{"tmp":"tmp/circuit-breaker_3.1.0_1759865504595_0.9251050046301581","host":"s3://npm-registry-packages-npm-production"}},"3.1.1":{"name":"@apiratorjs/circuit-breaker","version":"3.1.1","keywords":["circuit-breaker","circuit breaker","fault-tolerance","resilience","microservices","distributed-systems","stability","error-handling","typescript","nodejs","reliability","cascading-failures","decorator","higher-order-function","monitoring","async","promises"],"author":{"url":"http://alexeyzaitsev.com","name":"Alexey Zaitsev"},"license":"MIT","_id":"@apiratorjs/circuit-breaker@3.1.1","maintainers":[{"name":"apiratorjs","email":"apirator.framework@gmail.com"}],"homepage":"https://github.com/apiratorjs/circuit-breaker#readme","bugs":{"url":"https://github.com/apiratorjs/circuit-breaker/issues"},"dist":{"shasum":"218c7166f85293fa2d643c381aedd74a01e6191f","tarball":"https://registry.npmjs.org/@apiratorjs/circuit-breaker/-/circuit-breaker-3.1.1.tgz","fileCount":23,"integrity":"sha512-/SSFqHJjeiQnThk5HqhpdsIjx6KMcAQit0Gmvq9vm2qs0ANMKgGcxNdyNlYfDyZYsIuBowIVVdimc5SAXY8wdQ==","signatures":[{"sig":"MEQCIDD7ss1gtlXvqVajLIikdcu3xCh8XRvD6uSgvrNiN3VcAiB+RR9v/9soa6Ks1ufDyBUK0fEeAcYW98x7gEKZ+ZxAnQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":34507},"main":"dist/src/index.js","type":"commonjs","types":"dist/src/index.d.ts","engines":{"node":">=16.4.0"},"gitHead":"9541275f1bc4eb7ef83ee7f920ae39faf872b3ac","scripts":{"test":"npm run build && node --test dist/test","build":"rm -rf dist && tsc","watch":"tsc --watch","prepublishOnly":"npm test"},"_npmUser":{"name":"apiratorjs","email":"apirator.framework@gmail.com"},"repository":{"url":"git+https://github.com/apiratorjs/circuit-breaker.git","type":"git"},"_npmVersion":"10.9.2","description":"A robust and lightweight TypeScript circuit breaker implementation for Node.js applications. Provides fault tolerance and stability by preventing cascading failures in distributed systems with configurable thresholds and automatic recovery.","directories":{},"_nodeVersion":"22.17.0","dependencies":{},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","typescript":"^5.7.3","@types/node":"^22.13.1"},"_npmOperationalInternal":{"tmp":"tmp/circuit-breaker_3.1.1_1760384312175_0.29440448775750516","host":"s3://npm-registry-packages-npm-production"}},"3.2.0":{"name":"@apiratorjs/circuit-breaker","version":"3.2.0","keywords":["circuit-breaker","circuit breaker","fault-tolerance","resilience","microservices","distributed-systems","stability","error-handling","typescript","nodejs","reliability","cascading-failures","decorator","higher-order-function","monitoring","async","promises"],"author":{"url":"http://alexeyzaitsev.com","name":"Alexey Zaitsev"},"license":"MIT","_id":"@apiratorjs/circuit-breaker@3.2.0","maintainers":[{"name":"apiratorjs","email":"apirator.framework@gmail.com"}],"homepage":"https://github.com/apiratorjs/circuit-breaker#readme","bugs":{"url":"https://github.com/apiratorjs/circuit-breaker/issues"},"dist":{"shasum":"e1ca7376be4cbbe5eb9f20c53609c1e10d67c1c5","tarball":"https://registry.npmjs.org/@apiratorjs/circuit-breaker/-/circuit-breaker-3.2.0.tgz","fileCount":23,"integrity":"sha512-PjaDUMqpu/2nmN46haoMBhK6P06m4dFF8zgRv8xQrTdqS1Nqz3JrwECu+i7M4mKeCnlMqzNfJloODy9h22pYTg==","signatures":[{"sig":"MEQCIDenST+ReyKaKMcVzLdYAPIfY5n9ga85VDoxXnF0hI2yAiAOHY8d/At6GB/bwF+w11Q2fVnJ0+DZWmZBJb/GLpzdFw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":41153},"main":"dist/src/index.js","type":"commonjs","types":"dist/src/index.d.ts","engines":{"node":">=16.4.0"},"gitHead":"9c87aaf11917bcffbf86a3ed7ba2ca4c26adb5b1","scripts":{"test":"npm run build && node --test dist/test","build":"rm -rf dist && tsc","watch":"tsc --watch","prepublishOnly":"npm test"},"_npmUser":{"name":"apiratorjs","email":"apirator.framework@gmail.com"},"repository":{"url":"git+https://github.com/apiratorjs/circuit-breaker.git","type":"git"},"_npmVersion":"10.9.2","description":"A robust and lightweight TypeScript circuit breaker implementation for Node.js applications. Provides fault tolerance and stability by preventing cascading failures in distributed systems with configurable thresholds and automatic recovery.","directories":{},"_nodeVersion":"22.15.0","dependencies":{},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","typescript":"^5.7.3","@types/node":"^22.13.1"},"_npmOperationalInternal":{"tmp":"tmp/circuit-breaker_3.2.0_1761303995463_0.41865186471098537","host":"s3://npm-registry-packages-npm-production"}},"4.0.0":{"name":"@apiratorjs/circuit-breaker","version":"4.0.0","description":"A lightweight, dependency-free TypeScript circuit breaker for Node.js. Fails fast on unhealthy dependencies and recovers automatically, with fallbacks, error filtering, manual control, a method decorator, and pluggable stores that share one circuit across","author":{"name":"Alexey Zaitsev","url":"http://alexeyzaitsev.com"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/apiratorjs/circuit-breaker.git"},"homepage":"https://github.com/apiratorjs/circuit-breaker#readme","bugs":{"url":"https://github.com/apiratorjs/circuit-breaker/issues"},"main":"dist/src/index.js","types":"dist/src/index.d.ts","exports":{".":{"types":"./dist/src/index.d.ts","default":"./dist/src/index.js"},"./testing":{"types":"./dist/src/testing/index.d.ts","default":"./dist/src/testing/index.js"},"./package.json":"./package.json"},"publishConfig":{"access":"public"},"keywords":["circuit-breaker","circuit breaker","fault-tolerance","resilience","microservices","distributed-systems","distributed","shared-state","multi-process","stability","error-handling","fallback","graceful-degradation","typescript","nodejs","reliability","cascading-failures","decorator","async","promises"],"type":"commonjs","scripts":{"build":"rm -rf dist && tsc","watch":"tsc --watch","test":"npm run build && node --test dist/test","prepublishOnly":"npm test"},"engines":{"node":">=18.0.0"},"devDependencies":{"@types/node":"^22.13.1","typescript":"^5.7.3"},"dependencies":{},"gitHead":"26734a0d55998938ab11559f7cc8d89430f9a06b","_id":"@apiratorjs/circuit-breaker@4.0.0","_nodeVersion":"24.18.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-07d1JGKlhdAYikLKBaF+IycpFb+H7j7LWrRewxqNS9qE5Ykmd6DWPD1T0rq8lr0k4czZqvqJvgHfJCYZclYcmw==","shasum":"8df412c45d13f506b0e140617a3c98e79cab1284","tarball":"https://registry.npmjs.org/@apiratorjs/circuit-breaker/-/circuit-breaker-4.0.0.tgz","fileCount":44,"unpackedSize":165769,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCmb8Cao9LiaT8MWQ6F0dz62SHC7r2i9IvCc2N2Tbe3ZgIgIullrMWDJythA8bobahdSfuG9yeVCqGWf4chXR6B8Kc="}]},"_npmUser":{"name":"apiratorjs","email":"apirator.framework@gmail.com"},"directories":{},"maintainers":[{"name":"apiratorjs","email":"apirator.framework@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/circuit-breaker_4.0.0_1786531601989_0.6155439642107547"},"_hasShrinkwrap":false}},"time":{"created":"2025-09-29T19:44:41.743Z","modified":"2026-08-12T10:46:42.293Z","1.0.0":"2025-09-29T19:44:42.053Z","2.0.0":"2025-09-29T20:45:55.663Z","3.0.0":"2025-09-30T11:47:55.273Z","3.0.1":"2025-09-30T17:05:34.624Z","3.0.2":"2025-09-30T17:09:12.187Z","3.0.3":"2025-09-30T17:30:04.701Z","3.0.4":"2025-10-04T11:53:25.833Z","3.1.0":"2025-10-07T19:31:44.798Z","3.1.1":"2025-10-13T19:38:32.338Z","3.2.0":"2025-10-24T11:06:35.645Z","4.0.0":"2026-08-12T10:46:42.140Z"},"bugs":{"url":"https://github.com/apiratorjs/circuit-breaker/issues"},"author":{"name":"Alexey Zaitsev","url":"http://alexeyzaitsev.com"},"license":"MIT","homepage":"https://github.com/apiratorjs/circuit-breaker#readme","keywords":["circuit-breaker","circuit breaker","fault-tolerance","resilience","microservices","distributed-systems","distributed","shared-state","multi-process","stability","error-handling","fallback","graceful-degradation","typescript","nodejs","reliability","cascading-failures","decorator","async","promises"],"repository":{"type":"git","url":"git+https://github.com/apiratorjs/circuit-breaker.git"},"description":"A lightweight, dependency-free TypeScript circuit breaker for Node.js. Fails fast on unhealthy dependencies and recovers automatically, with fallbacks, error filtering, manual control, a method decorator, and pluggable stores that share one circuit across","maintainers":[{"name":"apiratorjs","email":"apirator.framework@gmail.com"}],"readme":"# @apiratorjs/circuit-breaker\n\n[![NPM version](https://img.shields.io/npm/v/@apiratorjs/circuit-breaker.svg)](https://www.npmjs.com/package/@apiratorjs/circuit-breaker)\n[![License: MIT](https://img.shields.io/npm/l/@apiratorjs/circuit-breaker.svg)](https://github.com/apiratorjs/circuit-breaker/blob/main/LICENSE)\n\nA robust and lightweight TypeScript circuit breaker implementation for Node.js applications. Provides fault tolerance and stability by preventing cascading failures in distributed systems with configurable thresholds and automatic recovery.\n\n> **Note:** Requires Node.js version **>=18.0.0**\n\n## What is a Circuit Breaker and Why Use It?\n\nA **Circuit Breaker** is a design pattern used in distributed systems to provide fault tolerance and prevent cascading failures. Just like an electrical circuit breaker that protects your home's electrical system from overload, a software circuit breaker protects your application from failing services.\n\n### How It Works\n\nThe circuit breaker monitors calls to external services and tracks failures. It has three states:\n\n- **🟢 CLOSED**: Normal operation - requests pass through and are monitored\n- **🔴 OPEN**: Failure threshold exceeded - requests fail fast without calling the service\n- **🟡 HALF_OPEN**: Testing phase - allows limited requests to check if service has recovered\n\n### Why You Need It\n\n**Without a Circuit Breaker:**\n```\nService A → Service B (failing) → Timeout after 30s → Retry → Another 30s timeout → Cascade failure\n```\n\n**With a Circuit Breaker:**\n```\nService A → Circuit Breaker → Service B (failing) → Fast fail after threshold → System remains stable\n```\n\n### Key Benefits\n\n- **Fast Failure**: Stop wasting time on calls to failing services\n- **System Stability**: Prevent one failing service from bringing down your entire system\n- **Automatic Recovery**: Automatically retry when services become healthy again\n- **Observability**: Get insights into service health and failure patterns\n- **Performance**: Reduce resource consumption and improve response times\n\n---\n\n## Installation\n\n```bash\nnpm install @apiratorjs/circuit-breaker\n```\n\n```bash\nyarn add @apiratorjs/circuit-breaker\n```\n\n```bash\npnpm add @apiratorjs/circuit-breaker\n```\n\n## Migrating to v4\n\nv4 adds pluggable state storage. Because a store may live out of process, the\nbreaker's public surface is now uniformly asynchronous:\n\n- `execute()` always returns a `Promise`. Previously it could throw\n  `CircuitOpenError` synchronously; it now rejects with it. Code that already\n  wrapped calls in `await` / `.catch()` needs no change — only a synchronous\n  `try { cb.execute() } catch {}` does.\n- `forceOpen()`, `forceClose()` and `forceHalfOpen()` return a `Promise<void>`.\n  They still update `circuitBreaker.state` synchronously, so existing code keeps\n  working; `await` them if you need to know the store accepted the write.\n- `@WithCircuitBreaker` now builds one circuit breaker per instance instead of\n  one per call, so failures actually accumulate. Previously every invocation got\n  a fresh breaker and the circuit could never open.\n- A successful call now clears accumulated failures while the circuit is closed.\n  `failureThreshold` has always been documented as counting *consecutive*\n  failures; it now behaves that way. Circuits that used to trip on failures\n  spread far apart will need `maxGapBetweenFailuresInMs` — see\n  [Counting failures](#counting-failures).\n\n## Quick Start\n\n```typescript\nimport { CircuitBreaker, CircuitOpenError } from '@apiratorjs/circuit-breaker';\n\n// Define your service call function\nasync function callExternalService(data: any) {\n  // Your external service call here\n  const response = await fetch('https://api.example.com/data', {\n    method: 'POST',\n    body: JSON.stringify(data)\n  });\n  \n  if (!response.ok) {\n    throw new Error(`Service responded with ${response.status}`);\n  }\n  \n  return response.json();\n}\n\n// Create a circuit breaker with your operation and settings\nconst circuitBreaker = new CircuitBreaker(callExternalService, {\n  failureThreshold: 5,        // Open circuit after 5 failures\n  durationOfBreakInMs: 60000, // Keep circuit open for 60 seconds\n  successThreshold: 2         // Close circuit after 2 successful calls in half-open state\n});\n\n// Use it in your application\ntry {\n  const result = await circuitBreaker.execute({ id: 123 });\n  console.log('Success:', result);\n} catch (error) {\n  if (error instanceof CircuitOpenError) {\n    console.error('Circuit is open - service temporarily unavailable');\n  } else {\n    console.error('Service call failed:', error.message);\n  }\n}\n```\n\n## Interface and options\n\n### ICircuitBreakerOptions\n\nConfiguration options for creating a circuit breaker instance:\n\n```typescript\ninterface ICircuitBreakerBaseOptions {\n  failureThreshold: number;       // Number of failures before opening the circuit\n  durationOfBreakInMs: number;    // How long to keep circuit open (milliseconds)\n  successThreshold: number;       // Successful calls needed to close circuit from half-open\n  fallback?: (...args: any[]) => any; // Fallback function when circuit is open\n  errorFilter?: (error: Error) => boolean; // Custom error filtering logic\n  maxGapBetweenFailuresInMs?: number; // Max quiet stretch before the tally resets\n  storeErrorPolicy?: ECircuitBreakerStoreErrorPolicy; // Behaviour when the store fails\n  halfOpenMaxConcurrentAttempts?: number; // Concurrent trial calls in half-open\n  halfOpenSlotTtlInMs?: number;     // How long a half-open slot stays reserved\n}\n\n// State in a store shared across processes: the name is the identifier every\n// process has to agree on, so it is mandatory here.\ninterface ISharedCircuitBreakerOptions extends ICircuitBreakerBaseOptions {\n  store: ICircuitBreakerStore;\n  name: string;\n}\n\n// The classic single-process circuit, backed by a private in-memory store.\ninterface ILocalCircuitBreakerOptions extends ICircuitBreakerBaseOptions {\n  store?: undefined;\n  name?: string;                  // Only a label for logs; generated when omitted\n}\n\ntype ICircuitBreakerOptions =\n  | ISharedCircuitBreakerOptions\n  | ILocalCircuitBreakerOptions;\n```\n\nPassing a `store` without a `name` is a compile error, not a runtime surprise: a\ngenerated name is local to the process, so each worker would silently operate on\nits own circuit and the shared store would share nothing.\n\n#### Options Details\n\n- **`failureThreshold`** (required): Number of consecutive failures that will trigger the circuit to open\n- **`durationOfBreakInMs`** (required): Duration in milliseconds to keep the circuit open before attempting recovery\n- **`successThreshold`** (required): Number of successful calls needed in half-open state to close the circuit\n- **`fallback`** (optional): Function to execute when circuit is open, receives same arguments as the operation\n- **`errorFilter`** (optional): Function to determine if an error should count as a failure (return true to count, false to ignore)\n- **`maxGapBetweenFailuresInMs`** (optional): How long the failure tally survives without a new failure. Two failures further apart than this are treated as unrelated and the later one starts counting from 1. Measured from the previous failure, so a steady run keeps adding up however long it lasts — see [Counting failures](#counting-failures). Unset by default, in which case only a success clears the tally\n- **`name`** (required with a `store`, optional without): Identifies this circuit inside the store — the name every process has to agree on. With no store it is just a label for logs and state-change callbacks, and defaults to a unique per-instance name\n- **`store`** (optional): State backend, see [Sharing state across processes](#sharing-state-across-processes). Defaults to a private `InMemoryCircuitBreakerStore`\n- **`storeErrorPolicy`** (optional, default `LOCAL`): What to do when the store itself is unreachable — see [When the store is down](#when-the-store-is-down)\n- **`halfOpenMaxConcurrentAttempts`** (optional, default `1`): How many trial calls may run at once in half-open. Keeps a recovering dependency from being hit by every caller at the same moment — see [Trial calls in half-open](#trial-calls-in-half-open)\n- **`halfOpenSlotTtlInMs`** (optional, default `durationOfBreakInMs`): How long a half-open slot stays reserved before the store may hand it to somebody else, so a caller that dies mid-call cannot block recovery\n\n### Circuit Breaker States\n\n```typescript\nenum ECircuitBreakerState {\n  CLOSED = \"closed\",      // Normal operation, calls pass through\n  OPEN = \"open\",         // Circuit is open, calls are rejected immediately\n  HALF_OPEN = \"half_open\" // Testing recovery, limited calls allowed\n}\n```\n\n## Error Handling\n\nThe circuit breaker throws specific error types that you can catch and handle appropriately:\n\n### Error Types\n\n#### CircuitOpenError\nThrown when the circuit breaker is in the **OPEN** state and prevents execution of the wrapped function.\n\n**Properties:**\n- `cause?: TErrorLike` - The original error that caused the circuit to open\n- `durationTillNextAttemptInMs: number` - Time in milliseconds until the circuit breaker will attempt recovery\n\n```typescript\nimport { CircuitBreaker, CircuitOpenError } from '@apiratorjs/circuit-breaker';\n\nconst circuitBreaker = new CircuitBreaker(riskyOperation, {\n  failureThreshold: 3,\n  durationOfBreakInMs: 30000,\n  successThreshold: 2\n});\n\ntry {\n  await circuitBreaker.execute();\n} catch (error) {\n  if (error instanceof CircuitOpenError) {\n    console.log('Circuit is open, service is temporarily unavailable');\n    console.log('Original cause:', error.cause?.message);\n    console.log(`Next attempt in: ${error.durationTillNextAttemptInMs}ms`);\n    \n    // You can use this to implement retry logic or user feedback\n    const nextAttemptTime = new Date(Date.now() + error.durationTillNextAttemptInMs);\n    console.log(`Service will be available again at: ${nextAttemptTime.toISOString()}`);\n  }\n}\n```\n\n#### CircuitArgumentError\nThrown when invalid configuration options are provided to the circuit breaker constructor.\n\n```typescript\ntry {\n  // This will throw CircuitArgumentError\n  const circuitBreaker = new CircuitBreaker(myFunction, {\n    failureThreshold: 0, // Invalid: must be > 0\n    durationOfBreakInMs: 30000,\n    successThreshold: 2\n  });\n} catch (error) {\n  if (error instanceof CircuitArgumentError) {\n    console.log('Invalid configuration:', error.message);\n  }\n}\n```\n\n#### CircuitBreakerError\nBase class for all circuit breaker errors. Contains additional error information:\n\n```typescript\nimport { CircuitBreakerError } from '@apiratorjs/circuit-breaker';\n\ntry {\n  await circuitBreaker.execute();\n} catch (error) {\n  if (error instanceof CircuitBreakerError) {\n    console.log('Circuit breaker error:', error.toJSON());\n    // Output includes: name, message, cause (if available)\n  }\n}\n```\n\n## State Change Monitoring\n\nYou can subscribe to state changes to monitor your circuit breaker's behavior and implement custom logging, metrics, or alerting.\n\n### onStateChange Method\n\nThe `onStateChange` method allows you to register a callback that will be called whenever the circuit breaker changes state. The callback receives a state transition object with both the previous and new states, plus an optional error when transitioning to OPEN:\n\n```typescript\nimport { CircuitBreaker, ECircuitBreakerState } from '@apiratorjs/circuit-breaker';\n\nconst circuitBreaker = new CircuitBreaker(riskyOperation, {\n  failureThreshold: 3,\n  durationOfBreakInMs: 30000,\n  successThreshold: 2\n});\n\n// Subscribe to state changes\ncircuitBreaker.onStateChange((stateTransition, error) => {\n  console.log(`Circuit breaker: ${stateTransition.previousState} → ${stateTransition.newState}`);\n  \n  // Handle state transitions\n  if (stateTransition.newState === ECircuitBreakerState.OPEN) {\n    console.log('⚠️  Circuit opened due to failures');\n    if (error) {\n      console.log('Last error:', error.message);\n    }\n    // Send alert, update metrics, etc.\n  } else if (stateTransition.newState === ECircuitBreakerState.HALF_OPEN) {\n    console.log('🔄 Circuit is testing recovery');\n    // Log recovery attempt\n  } else if (stateTransition.newState === ECircuitBreakerState.CLOSED) {\n    console.log('✅ Circuit closed - service is healthy');\n    // Log successful recovery\n  }\n});\n```\n\n**Callback Signature:**\n```typescript\ntype TCircuitBreakerStateChangeCallback = (\n  stateTransition: ICircuitBreakerStateTransition,\n  error?: Error  // Present only when transitioning to OPEN state\n) => void;\n\ninterface ICircuitBreakerStateTransition {\n  previousState: ECircuitBreakerState;  // The state before transition\n  newState: ECircuitBreakerState;        // The new current state\n}\n```\n\n### Advanced State Monitoring Example\n\n```typescript\nclass CircuitBreakerMonitor {\n  private metrics = {\n    stateChanges: 0,\n    totalFailures: 0,\n    recoveryAttempts: 0,\n    transitions: [] as Array<{\n      from: ECircuitBreakerState;\n      to: ECircuitBreakerState;\n      timestamp: Date;\n      error?: string;\n    }>\n  };\n\n  constructor(private circuitBreaker: CircuitBreaker) {\n    this.setupMonitoring();\n  }\n\n  private setupMonitoring() {\n    this.circuitBreaker.onStateChange((stateTransition, error) => {\n      this.metrics.stateChanges++;\n      \n      // Track all transitions\n      this.metrics.transitions.push({\n        from: stateTransition.previousState,\n        to: stateTransition.newState,\n        timestamp: new Date(),\n        error: error?.message\n      });\n      \n      // Handle specific transitions\n      if (stateTransition.newState === ECircuitBreakerState.OPEN) {\n        this.metrics.totalFailures++;\n        this.onCircuitOpened(stateTransition.previousState, error);\n      } else if (stateTransition.newState === ECircuitBreakerState.HALF_OPEN) {\n        this.metrics.recoveryAttempts++;\n        this.onRecoveryAttempt(stateTransition.previousState);\n      } else if (stateTransition.newState === ECircuitBreakerState.CLOSED) {\n        this.onCircuitClosed(stateTransition.previousState);\n      }\n    });\n  }\n\n  private onCircuitOpened(from: ECircuitBreakerState, error?: Error) {\n    console.log(`🚨 ALERT: Circuit breaker opened (${from} → OPEN)`);\n    console.log('Error details:', error?.message);\n    \n    // Send to monitoring system\n    // this.sendAlert('circuit_breaker_opened', { \n    //   from, \n    //   error: error?.message \n    // });\n  }\n\n  private onRecoveryAttempt(from: ECircuitBreakerState) {\n    console.log(`🔄 Circuit breaker attempting recovery (${from} → HALF_OPEN)`);\n    \n    // Log recovery attempt\n    // this.logMetric('circuit_breaker_recovery_attempt');\n  }\n\n  private onCircuitClosed(from: ECircuitBreakerState) {\n    console.log(`✅ Circuit breaker recovered (${from} → CLOSED)`);\n    \n    // Log successful recovery\n    // this.logMetric('circuit_breaker_recovered');\n  }\n\n  public getMetrics() {\n    return {\n      ...this.metrics,\n      currentState: this.circuitBreaker.state\n    };\n  }\n}\n\n// Usage\nconst monitor = new CircuitBreakerMonitor(circuitBreaker);\n\n// Check metrics after some operations\nconsole.log(monitor.getMetrics());\n// Output includes:\n// - stateChanges: number of state transitions\n// - totalFailures: number of times circuit opened\n// - recoveryAttempts: number of recovery attempts\n// - transitions: detailed history of all state changes\n```\n\n\n### Complete Example with Error Handling and State Monitoring\n\n```typescript\nimport { \n  CircuitBreaker, \n  ECircuitBreakerState, \n  CircuitOpenError,\n  CircuitBreakerError \n} from '@apiratorjs/circuit-breaker';\n\n// Define your risky operation\nasync function riskyOperation(data: any) {\n  // Simulate a service that fails sometimes\n  if (Math.random() < 0.7) {\n    throw new Error('Service temporarily unavailable');\n  }\n  return { success: true, data };\n}\n\nconst circuitBreaker = new CircuitBreaker(riskyOperation, {\n  failureThreshold: 3,\n  durationOfBreakInMs: 30000,\n  successThreshold: 2\n});\n\n// Set up comprehensive state change monitoring\ncircuitBreaker.onStateChange((stateTransition, error) => {\n  console.log(`🔄 Circuit breaker: ${stateTransition.previousState} → ${stateTransition.newState}`);\n  if (error) {\n    console.log(`Triggered by error: ${error.message}`);\n  }\n});\n\n// Example usage with proper error handling\nasync function makeServiceCall(data: any) {\n  try {\n    const result = await circuitBreaker.execute(data);\n    console.log('✅ Service call successful:', result);\n    return result;\n  } catch (error) {\n    if (error instanceof CircuitOpenError) {\n      console.log('⚠️  Circuit is open - service temporarily unavailable');\n      console.log('Original cause:', error.cause?.message);\n      console.log(`⏱️  Next attempt in: ${error.durationTillNextAttemptInMs}ms`);\n      \n      // Handle circuit open scenario (e.g., return cached data, show user message)\n      // You can use durationTillNextAttemptInMs for user feedback or retry scheduling\n      const retryTime = new Date(Date.now() + error.durationTillNextAttemptInMs);\n      console.log(`Service will retry at: ${retryTime.toLocaleTimeString()}`);\n    } else if (error instanceof CircuitBreakerError) {\n      console.log('🔧 Circuit breaker error:', error.toJSON());\n    } else {\n      console.log('❌ Service call failed:', error.message);\n      // Handle other service errors\n    }\n    throw error;\n  }\n}\n\n// Check current state\nconsole.log('Current state:', circuitBreaker.state);\n\n// Example of multiple calls to demonstrate state changes\nasync function demonstrateCircuitBreaker() {\n  for (let i = 0; i < 10; i++) {\n    try {\n      await makeServiceCall({ attempt: i + 1 });\n      await new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1 second\n    } catch (error) {\n      // Continue with next attempt\n    }\n  }\n}\n```\n\n## Advanced Features\n\n### Fallback Function\n\nProvide a fallback when the circuit is open:\n\n```typescript\nconst circuitBreaker = new CircuitBreaker(fetchData, {\n  failureThreshold: 3,\n  durationOfBreakInMs: 30000,\n  successThreshold: 2,\n  fallback: (userId) => {\n    // Return cached data instead of throwing error\n    return cache.get(userId) || { id: userId, name: 'Unknown' };\n  }\n});\n\n// When circuit is open, fallback is called automatically\nconst data = await circuitBreaker.execute('user-123');\n```\n\n### Error Filtering\n\nFilter which errors should count as failures:\n\n```typescript\nconst circuitBreaker = new CircuitBreaker(apiCall, {\n  failureThreshold: 5,\n  durationOfBreakInMs: 60000,\n  successThreshold: 2,\n  errorFilter: (error) => {\n    // Ignore validation errors\n    if (error.name === 'ValidationError') return false;\n    \n    // Only count 5xx server errors as failures\n    if ('status' in error) {\n      return error.status >= 500 && error.status < 600;\n    }\n    \n    return true; // Count other errors\n  }\n});\n```\n\n### Manual Circuit Control\n\nManually control circuit state for maintenance or testing:\n\n```typescript\nconst circuitBreaker = new CircuitBreaker(operation, options);\n\n// Manual control. The local view updates immediately; await the promise if you\n// need to know that the store accepted the write.\nawait circuitBreaker.forceOpen();     // Open circuit (e.g., for maintenance)\nawait circuitBreaker.forceClose();    // Close and reset\nawait circuitBreaker.forceHalfOpen(); // Force to half-open state\n\n// Check state\nconsole.log(circuitBreaker.state); // 'open', 'closed', or 'half_open'\n```\n\n### Sharing state across processes\n\nBy default a circuit breaker keeps its state in memory, private to the instance\nthat owns it. That is the right thing for a single process, but it falls apart\nwhen the same dependency is called from several workers — each one has to\nrediscover the outage on its own, and every worker keeps hammering a service the\nothers already gave up on. Background jobs that retry are the classic case.\n\nPoint the breaker at a store and the state becomes shared instead:\n\n```typescript\nimport { CircuitBreaker } from '@apiratorjs/circuit-breaker';\nimport { RedisCircuitBreakerStore } from 'some-redis-adapter';\n\nconst store = new RedisCircuitBreakerStore({ url: process.env.REDIS_URL });\n\nconst circuitBreaker = new CircuitBreaker(callPaymentProvider, {\n  failureThreshold: 5,\n  durationOfBreakInMs: 30_000,\n  successThreshold: 2,\n  name: 'payment-provider', // the name every worker agrees on\n  store,\n});\n```\n\nEvery worker that builds a breaker with the same `name` and a store backed by\nthe same Redis now sees one circuit: when one worker opens it, the rest fail fast\nimmediately, and the state survives a restart.\n\nThis works nicely with `CircuitOpenError.durationTillNextAttemptInMs` — it tells\nyou when the *shared* circuit is due to retry, which is exactly the delay to\nreschedule a job with:\n\n```typescript\ntry {\n  await circuitBreaker.execute(payload);\n} catch (error) {\n  if (error instanceof CircuitOpenError) {\n    await job.moveToDelayed(Date.now() + error.durationTillNextAttemptInMs);\n    return;\n  }\n  throw error;\n}\n```\n\n#### Observing changes made elsewhere\n\n`circuitBreaker.state` is the last state *this instance* observed. When another\nprocess moves the circuit, call `refresh()` to re-read the store — it also fires\n`onStateChange` if the state moved while you were not looking:\n\n```typescript\nconst snapshot = await circuitBreaker.refresh();\nconsole.log(snapshot.state, snapshot.failureCount, snapshot.openedAt);\n```\n\n#### Trial calls in half-open\n\nHalf-open means \"let one call through and see whether the dependency recovered\".\nWith several workers that needs coordinating: the moment the break window\nelapses, every one of them would otherwise decide it is time to try, and the\nrecovering dependency takes the full burst.\n\nSo before making the call the breaker reserves a **half-open slot** from the\nstore, and gives it back when the call settles:\n\n```typescript\nconst slot = await store.acquireHalfOpenSlot(name, ctx);\nif (!slot.acquired) { /* fall back, or throw CircuitOpenError */ }\ntry { /* the trial call */ } finally {\n  await store.releaseHalfOpenSlot(name, slot.token, ctx);\n}\n```\n\nThere are `halfOpenMaxConcurrentAttempts` slots (default `1`), so by default\nexactly one trial call runs at a time across the whole fleet. Callers that do not\nget one fall back or receive a `CircuitOpenError`, just as if the circuit were\nstill open.\n\n`halfOpenSlotTtlInMs` is the safety net for one specific case: the worker holding\na slot dies — `kill -9`, OOM, network gone — and never reaches\n`releaseHalfOpenSlot`. Without an expiry that slot stays taken forever and the\ncircuit can never close again. It is not an operation timeout and does not come\ninto play on a normal call, where the slot is released as soon as the call\nsettles.\n\nSize it against how long the operation takes. Too short and the reservation\nexpires while the trial call is still in flight, letting a second worker start\nits own; too long and recovery is delayed by that much after a worker dies. The\ndefault is `durationOfBreakInMs` — if your call has a 5s timeout and the break is\n60s, something like `halfOpenSlotTtlInMs: 10_000` frees a dead worker's slot in\nten seconds instead of a minute.\n\n#### When the store is down\n\nA circuit breaker exists to keep a failing dependency from taking your service\nwith it — so it must not turn its own store into a new way for the service to\nfail. `storeErrorPolicy` decides what happens when a store call throws:\n\n```typescript\nimport { ECircuitBreakerStoreErrorPolicy } from '@apiratorjs/circuit-breaker';\n```\n\n- **`LOCAL`** (default): degrade to a private in-memory store seeded with the\n  last snapshot this instance saw, and keep breaking per process until the store\n  recovers. Protection shrinks to one process instead of disappearing — and a\n  circuit that was already open stays open rather than releasing traffic onto a\n  dependency you know is failing.\n- **`ALLOW`**: run the operation as if the circuit were closed. Prefers keeping\n  calls flowing over protecting the dependency.\n- **`REJECT`**: propagate the store error out of `execute()`. Prefers failing\n  loudly.\n\nDegradation is otherwise invisible, so there is a hook for it:\n\n```typescript\ncircuitBreaker.onStoreError((error, operation) => {\n  logger.warn({ err: error, operation }, 'circuit breaker store unavailable');\n});\n```\n\nThe policy is itself a store — `ResilientCircuitBreakerStore` wraps the one you\nsupplied and implements `ICircuitBreakerStore` on top of it. The circuit breaker\nabove it just calls the interface and never has to know a fallback exists. You\ncan use it directly to give any store the same treatment:\n\n```typescript\nconst store = new ResilientCircuitBreakerStore(\n  redisStore,\n  ECircuitBreakerStoreErrorPolicy.LOCAL\n);\n```\n\nTwo details worth knowing:\n\n- Every call gives the real store a fresh chance; there is no latching onto the\n  fallback. The moment the store answers again it is authoritative, and the\n  in-memory fallback carries the last state seen — including work it did itself\n  during the outage — so protection never restarts from scratch mid-outage.\n- `REJECT` only applies to calls made *before* the operation runs (`get`,\n  `compareAndSetState`, `acquireHalfOpenSlot`, `setState`). Bookkeeping that happens\n  afterwards — `recordSuccess`, `recordFailure`, `releaseHalfOpenSlot` — never\n  propagates, because discarding a completed result, or replacing the\n  operation's own error with a Redis timeout, helps nobody.\n\n### Counting failures\n\n`failureThreshold` counts **consecutive** failures: a successful call clears the\ntally. That matters most for a long-lived circuit, especially a shared one — a\ncounter that only ever grows will eventually trip a perfectly healthy dependency\non a handful of unrelated failures spread over days.\n\nA success resets the count, but a circuit that sees traffic rarely may not get\none in time. `maxGapBetweenFailuresInMs` gives the tally a second way to clear:\na quiet stretch.\n\n```typescript\nconst circuitBreaker = new CircuitBreaker(callProvider, {\n  failureThreshold: 5,\n  maxGapBetweenFailuresInMs: 60_000, // failures more than a minute apart are unrelated\n  durationOfBreakInMs: 30_000,\n  successThreshold: 2,\n});\n```\n\n**Read it as \"failures have to keep coming to count together\"** — not as \"5\nfailures in the last minute\". The gap is measured from the *previous* failure and\nreset by each new one, so a run never times out as long as it keeps going:\n\n| Failure at | Gap since previous | Tally | With `maxGapBetweenFailuresInMs: 60_000` |\n| --- | --- | --- | --- |\n| `00:00` | — | 1 | run starts |\n| `00:50` | 50s | 2 | within the gap, run continues |\n| `01:40` | 50s | 3 | still going — total span is already 100s |\n| `03:00` | 80s | 1 | too long a pause; this is a new run |\n\nThat is deliberately not a sliding window. A sliding window would ask \"how many\nfailures in the last 60 seconds\" and answer 1 at `01:40`. Here the question is\nwhether each failure arrived soon enough after the one before it — a dependency\nfailing steadily every 50 seconds is failing steadily, and the circuit should\nnotice.\n\nPick the value as \"how far apart can two failures be and still plausibly be the\nsame incident\" — usually seconds to tens of seconds. Too large brings back the\naccumulation problem; too small and a rare-but-steady error stream never opens\nthe circuit at all. Must be greater than zero: `0` reads as \"unset\" and is\nrejected at construction.\n\nWithout it, failures are remembered until a success arrives.\n\n### Writing a store\n\nThis package ships only `InMemoryCircuitBreakerStore`. Backends such as Redis or\nPostgres live in separate packages that implement `ICircuitBreakerStore`.\n\n#### The two values an adapter handles\n\n**The snapshot** is the complete state of one circuit — everything needed to\nreconstruct it in another process:\n\n```typescript\ninterface ICircuitBreakerStateSnapshot {\n  state: ECircuitBreakerState;      // closed | open | half_open\n  failureCount: number;             // consecutive counted failures while closed\n  successCount: number;             // successful trial calls while half-open\n  lastFailureAt: number | null;     // epoch ms\n  openedAt: number | null;          // epoch ms the circuit went open\n}\n```\n\nIt is deliberately JSON-serializable. The last error is *not* part of it — each\nbreaker keeps that locally, so a store never has to serialize `Error` objects.\n\n**The context** carries the thresholds and the caller's clock, and is passed on\nevery single call:\n\n```typescript\ninterface ICircuitBreakerStoreContext {\n  failureThreshold: number;\n  successThreshold: number;\n  durationOfBreakInMs: number;\n  halfOpenMaxConcurrentAttempts: number;\n  halfOpenSlotTtlInMs: number;\n  maxGapBetweenFailuresInMs?: number;\n  now: number;\n}\n```\n\nBecause the configuration arrives with each call, a store stays stateless and one\ninstance can back any number of circuits with different thresholds.\n\n#### Whose clock is it\n\n`ctx.now` is the calling process's `Date.now()`, and every timing decision is made\nagainst it. **Never read the clock yourself** — a store that substitutes its own\nputs two different times on either side of the same comparison, which is worse\nthan a skewed one.\n\nWorth knowing where this leaves a fleet: `openedAt` is written by whichever worker\nopened the circuit, and \"has the break window elapsed\" is evaluated by each\nworker locally, *before* it ever reaches the store. So clock skew between workers\nshifts the break window by exactly that skew, and no adapter can correct for it —\nthe decision has already been made by the time your code runs. With ordinary NTP\nthis is tens of milliseconds against windows measured in seconds. If your fleet's\nclocks are not disciplined, that assumption is the first thing to question.\n\nThe one place a store may use its backend's clock is the half-open slot: expiring\na reservation with Redis `PX` or a database column is expected, and the contract\nsuite accommodates it.\n\n#### The interface\n\n```typescript\ninterface ICircuitBreakerStore {\n  get(name, ctx): Promise<ICircuitBreakerStateSnapshot>;\n  recordSuccess(name, ctx): Promise<ICircuitBreakerStateSnapshot>;\n  recordFailure(name, ctx): Promise<ICircuitBreakerStateSnapshot>;\n  compareAndSetState(name, from, to, ctx): Promise<ICircuitBreakerStateSnapshot | null>;\n  setState(name, next, ctx): Promise<ICircuitBreakerStateSnapshot>;\n  acquireHalfOpenSlot(name, ctx): Promise<IHalfOpenSlot>;\n  releaseHalfOpenSlot(name, token, ctx): Promise<void>;\n  delete(name): Promise<void>;\n}\n```\n\nWhat each one owes the caller:\n\n- **`get`** — the current snapshot; an initial closed one when the name is unknown.\n- **`recordSuccess` / `recordFailure`** — apply the transition and return the\n  resulting snapshot. Mutating methods return the new state so the breaker learns\n  the outcome without a second round-trip.\n- **`compareAndSetState`** — move to `to` only if the circuit's current state is\n  still `from`; return `null` when somebody got there first.\n- **`setState`** — move unconditionally. This backs the manual controls.\n- **`acquireHalfOpenSlot` / `releaseHalfOpenSlot`** — reserve and return one of\n  the `ctx.halfOpenMaxConcurrentAttempts` permits for a trial call. A reservation\n  must expire on its own after `ctx.halfOpenSlotTtlInMs`; releasing an unknown or\n  already-expired token must be a no-op.\n- **`delete`** — drop everything stored under `name`, reservations included. The\n  only method without a `ctx`, deliberately: dropping a key needs neither the\n  thresholds nor the clock, and a parameter every implementation ignores is worse\n  than an asymmetric signature.\n\n#### The one hard requirement: atomicity\n\nA store owns the transitions, not just the bytes. `recordSuccess`,\n`recordFailure` and `compareAndSetState` must apply the state machine\n**atomically with respect to other callers**. A read-modify-write that is not\natomic loses increments, and the effective threshold drifts upward under load —\na circuit configured to open after 5 failures quietly starts needing 8, exactly\nwhen you need it most.\n\n- **SQL**: take a row lock — `SELECT ... FOR UPDATE` inside the transaction that\n  writes the new snapshot.\n- **Redis**: put the whole read-modify-write in one Lua script.\n\n`compareAndSetState` returning `null` on a lost race is what stops an entire\nfleet from entering half-open the moment the break window elapses.\n\n#### Reuse the state machine\n\nThe transition rules are exported as `CircuitBreakerStateMachine`, a stateless\nclass of static rules. Do not reimplement them — an adapter that disagrees with\nthe core about when a circuit opens is a very hard bug to find:\n\n```typescript\nimport { CircuitBreakerStateMachine } from '@apiratorjs/circuit-breaker';\n\n// Postgres adapter, inside SELECT ... FOR UPDATE\nasync recordFailure(name, ctx) {\n  const current =\n    (await this.selectForUpdate(name)) ??\n    CircuitBreakerStateMachine.createInitialSnapshot();\n  const next = CircuitBreakerStateMachine.applyFailure(current, ctx);\n  await this.upsert(name, next);\n  return next;\n}\n```\n\nThe full set: `createInitialSnapshot`, `applySuccess`, `applyFailure`,\n`applyForcedState`, `continuesFailureChain`, `shouldAttemptReset` and\n`durationTillNextAttemptInMs`.\n\nTwo of them are worth knowing about even when you only store bytes:\n\n- `applySuccess` returns the *same object* when nothing changed, so you can skip\n  the write on the hot path where a healthy circuit just keeps succeeding.\n- `applyFailure` deliberately does not extend `openedAt` while the circuit is\n  already open, so a straggling failure cannot stretch the break window.\n\nA backend that cannot call into JavaScript has to express the same rules\nnatively. Keep that version side by side with the exported functions, and hold it\nto the contract suite below.\n\n#### The contract test suite\n\nDo not take the prose above on trust — run the contract. The behavior every\nstore owes its caller is published as a ready-made suite:\n\n```typescript\nimport { runStoreContractTests } from '@apiratorjs/circuit-breaker/testing';\n\nrunStoreContractTests({\n  suiteName: 'RedisCircuitBreakerStore',\n  createStore: () => new RedisCircuitBreakerStore({ url: process.env.REDIS_URL! }),\n  teardown: (store) => store.disconnect(),\n});\n```\n\nThat is the whole integration. It covers the state machine as seen through the\nstore, `compareAndSetState` races, the half-open slot lifecycle including\nexpiry and ownership, and two concurrency cases that a non-atomic\nread-modify-write cannot pass. `InMemoryCircuitBreakerStore` is held to the same\nsuite in this package's own tests.\n\n- **Runner**: `node:test` by default. Pass `runner: { describe, it, after }` to\n  drive it from vitest or jest instead. Use `STORE_CONTRACT_CASES` directly if\n  you need to filter or wrap individual cases.\n- **Shared backends are safe**: every circuit name is unique per case and deleted\n  afterwards, so the suite can point at a real Redis or database.\n- **Time is virtual**: cases move `ctx.now` rather than sleeping, which is why\n  \"never read the clock yourself\" matters. The one exception is slot expiry —\n  a store may legitimately delegate that to its backend (Redis `PX`), so that\n  case waits for real time too.\n\n#### Reference implementation\n\n`InMemoryCircuitBreakerStore` (~150 lines) performs exactly this sequence with no\nlocking, because a single process needs none. Read it as the specification of\nwhat your adapter must reproduce under concurrency.\n\nYou do not need to handle your own backend being unreachable: wrap the store in\n`ResilientCircuitBreakerStore`, or just pass it to a `CircuitBreaker`, which does\nthat for you — see [When the store is down](#when-the-store-is-down).\n\n## Using decorator (for typescript projects)\n\n### Method Decorator (@WithCircuitBreaker)\n\nProtect class methods using TypeScript decorators:\n\n```typescript\nimport { WithCircuitBreaker } from '@apiratorjs/circuit-breaker';\n\nclass UserService {\n  @WithCircuitBreaker({\n    failureThreshold: 5,\n    durationOfBreakInMs: 60000,\n    successThreshold: 2\n  })\n  async fetchUser(id: string) {\n    const response = await fetch(`https://api.example.com/users/${id}`);\n    if (!response.ok) throw new Error(`HTTP ${response.status}`);\n    return response.json();\n  }\n\n  @WithCircuitBreaker({\n    failureThreshold: 3,\n    durationOfBreakInMs: 45000,\n    successThreshold: 1\n  })\n  async updateUser(id: string, data: any) {\n    const response = await fetch(`https://api.example.com/users/${id}`, {\n      method: 'PUT',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify(data)\n    });\n    if (!response.ok) throw new Error(`HTTP ${response.status}`);\n    return response.json();\n  }\n}\n\n// Usage\nconst userService = new UserService();\n\ntry {\n  const user = await userService.fetchUser('123');\n  await userService.updateUser('123', { name: 'John Doe' });\n} catch (error) {\n  console.error('Service call failed:', error.message);\n}\n```\n\n> **Note:** To use decorators, ensure your `tsconfig.json` has `\"experimentalDecorators\": true` and `\"emitDecoratorMetadata\": true` enabled.\n\nEach decorated method gets one circuit per instance it is called on, created on\nthe first call. Two instances of `UserService` therefore fail independently —\nwhich is usually what you want, since they often point at different hosts. To\nmake them share one circuit, give them a `store` and the same `name`.\n\n#### Options from the instance (dependency injection)\n\nA decorator runs while the class is being defined, long before a DI container\nhas built anything — so an injected store cannot be named in the options object.\nPass a function instead. It receives the instance and runs on the first call, by\nwhich time the container is done:\n\n```typescript\n@Injectable()\nexport class PaymentsService {\n  constructor(\n    @Inject(CIRCUIT_BREAKER_STORE) public readonly store: ICircuitBreakerStore,\n  ) {}\n\n  @WithCircuitBreaker((self: PaymentsService) => ({\n    store: self.store,\n    name: 'payments.charge',\n    failureThreshold: 5,\n    durationOfBreakInMs: 30_000,\n    successThreshold: 2,\n  }))\n  async charge(id: string) {\n    // ...\n  }\n}\n```\n\nThe function runs **once per instance**, alongside the breaker it configures —\nnot on every call — so it is the wrong place for anything that varies per\ninvocation.\n\nKeep `name` a stable constant. It is the identifier every process has to agree\non, so deriving it from the instance gives each worker its own circuit and the\nshared store stops sharing anything.\n\n## Contributing\n\nContributions, issues, and feature requests are welcome! Feel free to check [issues page](https://github.com/apiratorjs/circuit-breaker/issues).\n\n## License\n\nThis project is [MIT](./LICENSE) licensed.\n","readmeFilename":"README.md"}