@backendkit-labs/bulkhead 0.1.0 → 0.1.2

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.
@@ -1,239 +1,10 @@
1
1
  'use strict';
2
2
 
3
+ var chunkLXXCDKHB_cjs = require('../chunk-LXXCDKHB.cjs');
3
4
  var common = require('@nestjs/common');
4
5
  var core = require('@nestjs/core');
5
6
  var rxjs = require('rxjs');
6
7
 
7
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
8
- var __decorateClass = (decorators, target, key, kind) => {
9
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
10
- for (var i = decorators.length - 1, decorator; i >= 0; i--)
11
- if (decorator = decorators[i])
12
- result = (decorator(result)) || result;
13
- return result;
14
- };
15
- var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
16
-
17
- // src/bulkhead/bulkhead.ts
18
- var BulkheadRejectedError = class extends Error {
19
- constructor(message) {
20
- super(message);
21
- this.name = "BulkheadRejectedError";
22
- }
23
- };
24
- var BulkheadTimeoutError = class extends Error {
25
- constructor(message) {
26
- super(message);
27
- this.name = "BulkheadTimeoutError";
28
- }
29
- };
30
- var Bulkhead = class {
31
- constructor(config) {
32
- this.config = config;
33
- }
34
- config;
35
- activeCalls = 0;
36
- nextId = 0;
37
- queue = [];
38
- totalCalls = 0;
39
- successfulCalls = 0;
40
- failedCalls = 0;
41
- rejectedCalls = 0;
42
- timedOutCalls = 0;
43
- totalDurationMs = 0;
44
- async execute(task) {
45
- const startTime = Date.now();
46
- this.totalCalls++;
47
- if (this.activeCalls < this.config.maxConcurrentCalls) {
48
- return this.runTask(task, startTime);
49
- }
50
- if (this.queue.length >= this.config.maxQueueSize) {
51
- if (this.config.rejectWhenFull) {
52
- this.rejectedCalls++;
53
- throw new BulkheadRejectedError(
54
- `Bulkhead '${this.config.name}' is full. Active: ${this.activeCalls}, Queue: ${this.queue.length}, Max: ${this.config.maxConcurrentCalls}`
55
- );
56
- }
57
- return this.waitAndRetry(task, startTime);
58
- }
59
- const entryId = this.nextId++;
60
- return new Promise((resolve, reject) => {
61
- const timeoutId = setTimeout(() => {
62
- const index = this.queue.findIndex((item) => item.id === entryId);
63
- if (index !== -1) this.queue.splice(index, 1);
64
- this.timedOutCalls++;
65
- reject(
66
- new BulkheadTimeoutError(
67
- `Bulkhead '${this.config.name}' timeout after ${this.config.queueTimeoutMs}ms`
68
- )
69
- );
70
- }, this.config.queueTimeoutMs);
71
- this.queue.push({
72
- id: entryId,
73
- task,
74
- resolve: (value) => {
75
- clearTimeout(timeoutId);
76
- resolve(value);
77
- },
78
- reject: (reason) => {
79
- clearTimeout(timeoutId);
80
- reject(reason);
81
- },
82
- queuedAt: startTime
83
- });
84
- this.processQueue();
85
- });
86
- }
87
- async runTask(task, startTime) {
88
- this.activeCalls++;
89
- try {
90
- const result = await task();
91
- this.successfulCalls++;
92
- this.totalDurationMs += Date.now() - startTime;
93
- return result;
94
- } catch (error) {
95
- this.failedCalls++;
96
- throw error;
97
- } finally {
98
- this.activeCalls--;
99
- this.processQueue();
100
- }
101
- }
102
- async waitAndRetry(task, startTime) {
103
- const maxRetries = 3;
104
- const baseDelay = 100;
105
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
106
- if (this.activeCalls < this.config.maxConcurrentCalls) {
107
- return this.runTask(task, startTime);
108
- }
109
- await new Promise((resolve) => setTimeout(resolve, baseDelay * Math.pow(2, attempt - 1)));
110
- }
111
- this.rejectedCalls++;
112
- throw new BulkheadRejectedError(
113
- `Bulkhead '${this.config.name}' rejected after ${maxRetries} retries`
114
- );
115
- }
116
- processQueue() {
117
- while (this.activeCalls < this.config.maxConcurrentCalls && this.queue.length > 0) {
118
- const next = this.queue.shift();
119
- if (!next) break;
120
- const waitTime = Date.now() - next.queuedAt;
121
- if (waitTime > this.config.queueTimeoutMs) {
122
- next.reject(new BulkheadTimeoutError(`Task timed out after ${waitTime}ms in queue`));
123
- this.timedOutCalls++;
124
- continue;
125
- }
126
- this.activeCalls++;
127
- next.task().then((result) => {
128
- this.successfulCalls++;
129
- next.resolve(result);
130
- }).catch((error) => {
131
- this.failedCalls++;
132
- next.reject(error);
133
- }).finally(() => {
134
- this.activeCalls--;
135
- this.processQueue();
136
- });
137
- }
138
- }
139
- getMetrics() {
140
- return {
141
- name: this.config.name,
142
- activeCalls: this.activeCalls,
143
- queuedCalls: this.queue.length,
144
- maxConcurrentCalls: this.config.maxConcurrentCalls,
145
- maxQueueSize: this.config.maxQueueSize,
146
- totalCalls: this.totalCalls,
147
- successfulCalls: this.successfulCalls,
148
- failedCalls: this.failedCalls,
149
- rejectedCalls: this.rejectedCalls,
150
- timedOutCalls: this.timedOutCalls,
151
- averageDurationMs: this.successfulCalls > 0 ? Math.round(this.totalDurationMs / this.successfulCalls) : 0
152
- };
153
- }
154
- canAccept() {
155
- return this.activeCalls < this.config.maxConcurrentCalls || this.queue.length < this.config.maxQueueSize;
156
- }
157
- resetMetrics() {
158
- this.totalCalls = 0;
159
- this.successfulCalls = 0;
160
- this.failedCalls = 0;
161
- this.rejectedCalls = 0;
162
- this.timedOutCalls = 0;
163
- this.totalDurationMs = 0;
164
- }
165
- };
166
-
167
- // src/bulkhead/bulkhead.registry.ts
168
- var DEFAULT_CONFIG = {
169
- maxConcurrentCalls: 10,
170
- maxQueueSize: 100,
171
- queueTimeoutMs: 3e4,
172
- rejectWhenFull: true
173
- };
174
- var BulkheadRegistry = class {
175
- bulkheads = /* @__PURE__ */ new Map();
176
- getOrCreate(options) {
177
- if (!this.bulkheads.has(options.name)) {
178
- const config = { ...DEFAULT_CONFIG, ...options, name: options.name };
179
- this.bulkheads.set(options.name, new Bulkhead(config));
180
- }
181
- return this.bulkheads.get(options.name);
182
- }
183
- /** Per-client isolation — 5 concurrent, 20 queued */
184
- getForClient(clientId, endpoint) {
185
- const name = endpoint ? `client:${clientId}:${endpoint}` : `client:${clientId}`;
186
- return this.getOrCreate({ name, maxConcurrentCalls: 5, maxQueueSize: 20 });
187
- }
188
- /** Service-level limiting — 20 concurrent, 200 queued */
189
- getForService(serviceName) {
190
- return this.getOrCreate({
191
- name: `service:${serviceName}`,
192
- maxConcurrentCalls: 20,
193
- maxQueueSize: 200
194
- });
195
- }
196
- /** Database connection limiting — 15 concurrent, 150 queued */
197
- getForDatabase(schema) {
198
- return this.getOrCreate({
199
- name: `database:${schema}`,
200
- maxConcurrentCalls: 15,
201
- maxQueueSize: 150
202
- });
203
- }
204
- /** External HTTP calls — 8 concurrent, 50 queued, 10s timeout */
205
- getForHttpExternal(serviceName) {
206
- return this.getOrCreate({
207
- name: `http:${serviceName}`,
208
- maxConcurrentCalls: 8,
209
- maxQueueSize: 50,
210
- queueTimeoutMs: 1e4
211
- });
212
- }
213
- getAllMetrics() {
214
- const metrics = {};
215
- for (const [name, bulkhead] of this.bulkheads) {
216
- metrics[name] = bulkhead.getMetrics();
217
- }
218
- return metrics;
219
- }
220
- /** Returns bulkheads at or above 80% active capacity */
221
- getOverloadedBulkheads() {
222
- const overloaded = [];
223
- for (const bulkhead of this.bulkheads.values()) {
224
- const m = bulkhead.getMetrics();
225
- if (m.activeCalls >= m.maxConcurrentCalls * 0.8) {
226
- overloaded.push(m);
227
- }
228
- }
229
- return overloaded;
230
- }
231
- resetAllMetrics() {
232
- for (const bulkhead of this.bulkheads.values()) {
233
- bulkhead.resetMetrics();
234
- }
235
- }
236
- };
237
8
  exports.BulkheadService = class BulkheadService {
238
9
  constructor(registry) {
239
10
  this.registry = registry;
@@ -279,9 +50,9 @@ exports.BulkheadService = class BulkheadService {
279
50
  }, 6e4);
280
51
  }
281
52
  };
282
- exports.BulkheadService = __decorateClass([
53
+ exports.BulkheadService = chunkLXXCDKHB_cjs.__decorateClass([
283
54
  common.Injectable(),
284
- __decorateParam(0, common.Inject(BulkheadRegistry))
55
+ chunkLXXCDKHB_cjs.__decorateParam(0, common.Inject(chunkLXXCDKHB_cjs.BulkheadRegistry))
285
56
  ], exports.BulkheadService);
286
57
  var UseBulkhead = (options = {}) => core.Reflector.createDecorator({ key: "bulkhead", transform: () => options });
287
58
  exports.BulkheadGuard = class BulkheadGuard {
@@ -304,10 +75,10 @@ exports.BulkheadGuard = class BulkheadGuard {
304
75
  return true;
305
76
  }
306
77
  };
307
- exports.BulkheadGuard = __decorateClass([
78
+ exports.BulkheadGuard = chunkLXXCDKHB_cjs.__decorateClass([
308
79
  common.Injectable(),
309
- __decorateParam(0, common.Inject(core.Reflector)),
310
- __decorateParam(1, common.Inject(BulkheadRegistry))
80
+ chunkLXXCDKHB_cjs.__decorateParam(0, common.Inject(core.Reflector)),
81
+ chunkLXXCDKHB_cjs.__decorateParam(1, common.Inject(chunkLXXCDKHB_cjs.BulkheadRegistry))
311
82
  ], exports.BulkheadGuard);
312
83
  exports.BulkheadInterceptor = class BulkheadInterceptor {
313
84
  constructor(registry) {
@@ -326,12 +97,12 @@ exports.BulkheadInterceptor = class BulkheadInterceptor {
326
97
  subscriber.complete();
327
98
  });
328
99
  } catch (error) {
329
- if (error instanceof BulkheadRejectedError) {
100
+ if (error instanceof chunkLXXCDKHB_cjs.BulkheadRejectedError) {
330
101
  throw new common.ServiceUnavailableException(
331
102
  "Service at capacity \u2014 please retry in a moment"
332
103
  );
333
104
  }
334
- if (error instanceof BulkheadTimeoutError) {
105
+ if (error instanceof chunkLXXCDKHB_cjs.BulkheadTimeoutError) {
335
106
  throw new common.RequestTimeoutException(
336
107
  "Request timed out waiting for processing \u2014 please retry"
337
108
  );
@@ -340,9 +111,9 @@ exports.BulkheadInterceptor = class BulkheadInterceptor {
340
111
  }
341
112
  }
342
113
  };
343
- exports.BulkheadInterceptor = __decorateClass([
114
+ exports.BulkheadInterceptor = chunkLXXCDKHB_cjs.__decorateClass([
344
115
  common.Injectable(),
345
- __decorateParam(0, common.Inject(BulkheadRegistry))
116
+ chunkLXXCDKHB_cjs.__decorateParam(0, common.Inject(chunkLXXCDKHB_cjs.BulkheadRegistry))
346
117
  ], exports.BulkheadInterceptor);
347
118
  var DEFAULT_CONCURRENCY = 50;
348
119
  var DEFAULT_MAX_QUEUE = 100;
@@ -358,7 +129,7 @@ exports.HttpBulkheadMiddleware = class HttpBulkheadMiddleware {
358
129
  process.env["HTTP_BULKHEAD_MAX_QUEUE"] ?? String(DEFAULT_MAX_QUEUE),
359
130
  10
360
131
  );
361
- this.bulkhead = new Bulkhead({
132
+ this.bulkhead = new chunkLXXCDKHB_cjs.Bulkhead({
362
133
  name: "http:global",
363
134
  maxConcurrentCalls: concurrency,
364
135
  maxQueueSize,
@@ -378,7 +149,7 @@ exports.HttpBulkheadMiddleware = class HttpBulkheadMiddleware {
378
149
  })
379
150
  ).catch((error) => {
380
151
  if (res.headersSent) return;
381
- if (error instanceof BulkheadRejectedError) {
152
+ if (error instanceof chunkLXXCDKHB_cjs.BulkheadRejectedError) {
382
153
  const m = this.bulkhead.getMetrics();
383
154
  this.logger.warn(
384
155
  `HTTP Bulkhead saturated [active=${m.activeCalls} queued=${m.queuedCalls}] \u2014 rejecting ${req.method} ${req.path}`
@@ -388,7 +159,7 @@ exports.HttpBulkheadMiddleware = class HttpBulkheadMiddleware {
388
159
  error: "Too Many Requests",
389
160
  message: "Service temporarily overloaded. Please retry in a few seconds."
390
161
  });
391
- } else if (error instanceof BulkheadTimeoutError) {
162
+ } else if (error instanceof chunkLXXCDKHB_cjs.BulkheadTimeoutError) {
392
163
  res.status(503).json({
393
164
  statusCode: 503,
394
165
  error: "Service Unavailable",
@@ -401,24 +172,24 @@ exports.HttpBulkheadMiddleware = class HttpBulkheadMiddleware {
401
172
  return this.bulkhead.getMetrics();
402
173
  }
403
174
  };
404
- exports.HttpBulkheadMiddleware = __decorateClass([
175
+ exports.HttpBulkheadMiddleware = chunkLXXCDKHB_cjs.__decorateClass([
405
176
  common.Injectable()
406
177
  ], exports.HttpBulkheadMiddleware);
407
178
 
408
179
  // src/nestjs/bulkhead.module.ts
409
180
  exports.BulkheadModule = class BulkheadModule {
410
181
  };
411
- exports.BulkheadModule = __decorateClass([
182
+ exports.BulkheadModule = chunkLXXCDKHB_cjs.__decorateClass([
412
183
  common.Module({
413
184
  providers: [
414
- BulkheadRegistry,
185
+ chunkLXXCDKHB_cjs.BulkheadRegistry,
415
186
  exports.BulkheadService,
416
187
  exports.BulkheadGuard,
417
188
  exports.BulkheadInterceptor,
418
189
  exports.HttpBulkheadMiddleware
419
190
  ],
420
191
  exports: [
421
- BulkheadRegistry,
192
+ chunkLXXCDKHB_cjs.BulkheadRegistry,
422
193
  exports.BulkheadService,
423
194
  exports.BulkheadGuard,
424
195
  exports.BulkheadInterceptor,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/bulkhead/bulkhead.ts","../../src/bulkhead/bulkhead.registry.ts","../../src/nestjs/bulkhead.service.ts","../../src/nestjs/bulkhead.guard.ts","../../src/nestjs/bulkhead.interceptor.ts","../../src/nestjs/http-bulkhead.middleware.ts","../../src/nestjs/bulkhead.module.ts","../../src/nestjs/bulkhead.decorator.ts"],"names":["BulkheadService","Logger","Injectable","Reflector","BulkheadGuard","ServiceUnavailableException","Inject","BulkheadInterceptor","firstValueFrom","Observable","RequestTimeoutException","HttpBulkheadMiddleware","BulkheadModule","Module"],"mappings":"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","file":"index.cjs","sourcesContent":["export interface BulkheadConfig {\n /** Max number of concurrent executions */\n maxConcurrentCalls: number;\n /** Max queue size for waiting tasks */\n maxQueueSize: number;\n /** Max time a task can wait in queue (ms) */\n queueTimeoutMs: number;\n /** Reject immediately when queue is full; if false, retries with backoff */\n rejectWhenFull: boolean;\n /** Identifier used in metrics and error messages */\n name: string;\n}\n\nexport interface BulkheadMetrics {\n name: string;\n activeCalls: number;\n queuedCalls: number;\n maxConcurrentCalls: number;\n maxQueueSize: number;\n totalCalls: number;\n successfulCalls: number;\n failedCalls: number;\n rejectedCalls: number;\n timedOutCalls: number;\n averageDurationMs: number;\n}\n\nexport class BulkheadRejectedError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'BulkheadRejectedError';\n }\n}\n\nexport class BulkheadTimeoutError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'BulkheadTimeoutError';\n }\n}\n\nexport class Bulkhead {\n private activeCalls = 0;\n private nextId = 0;\n private queue: Array<{\n id: number;\n task: () => Promise<unknown>;\n resolve: (value: unknown) => void;\n reject: (reason: unknown) => void;\n queuedAt: number;\n }> = [];\n\n private totalCalls = 0;\n private successfulCalls = 0;\n private failedCalls = 0;\n private rejectedCalls = 0;\n private timedOutCalls = 0;\n private totalDurationMs = 0;\n\n constructor(private readonly config: BulkheadConfig) {}\n\n async execute<T>(task: () => Promise<T>): Promise<T> {\n const startTime = Date.now();\n this.totalCalls++;\n\n if (this.activeCalls < this.config.maxConcurrentCalls) {\n return this.runTask(task, startTime);\n }\n\n if (this.queue.length >= this.config.maxQueueSize) {\n if (this.config.rejectWhenFull) {\n this.rejectedCalls++;\n throw new BulkheadRejectedError(\n `Bulkhead '${this.config.name}' is full. ` +\n `Active: ${this.activeCalls}, Queue: ${this.queue.length}, Max: ${this.config.maxConcurrentCalls}`,\n );\n }\n return this.waitAndRetry(task, startTime);\n }\n\n const entryId = this.nextId++;\n return new Promise<T>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n const index = this.queue.findIndex(item => item.id === entryId);\n if (index !== -1) this.queue.splice(index, 1);\n this.timedOutCalls++;\n reject(\n new BulkheadTimeoutError(\n `Bulkhead '${this.config.name}' timeout after ${this.config.queueTimeoutMs}ms`,\n ),\n );\n }, this.config.queueTimeoutMs);\n\n this.queue.push({\n id: entryId,\n task: task as () => Promise<unknown>,\n resolve: value => {\n clearTimeout(timeoutId);\n resolve(value as T);\n },\n reject: reason => {\n clearTimeout(timeoutId);\n reject(reason);\n },\n queuedAt: startTime,\n });\n\n this.processQueue();\n });\n }\n\n private async runTask<T>(task: () => Promise<T>, startTime: number): Promise<T> {\n this.activeCalls++;\n try {\n const result = await task();\n this.successfulCalls++;\n this.totalDurationMs += Date.now() - startTime;\n return result;\n } catch (error) {\n this.failedCalls++;\n throw error;\n } finally {\n this.activeCalls--;\n this.processQueue();\n }\n }\n\n private async waitAndRetry<T>(task: () => Promise<T>, startTime: number): Promise<T> {\n const maxRetries = 3;\n const baseDelay = 100;\n\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n if (this.activeCalls < this.config.maxConcurrentCalls) {\n return this.runTask(task, startTime);\n }\n await new Promise(resolve => setTimeout(resolve, baseDelay * Math.pow(2, attempt - 1)));\n }\n\n this.rejectedCalls++;\n throw new BulkheadRejectedError(\n `Bulkhead '${this.config.name}' rejected after ${maxRetries} retries`,\n );\n }\n\n private processQueue(): void {\n while (this.activeCalls < this.config.maxConcurrentCalls && this.queue.length > 0) {\n const next = this.queue.shift();\n if (!next) break;\n\n const waitTime = Date.now() - next.queuedAt;\n if (waitTime > this.config.queueTimeoutMs) {\n next.reject(new BulkheadTimeoutError(`Task timed out after ${waitTime}ms in queue`));\n this.timedOutCalls++;\n continue;\n }\n\n this.activeCalls++;\n next\n .task()\n .then(result => {\n this.successfulCalls++;\n next.resolve(result);\n })\n .catch(error => {\n this.failedCalls++;\n next.reject(error);\n })\n .finally(() => {\n this.activeCalls--;\n this.processQueue();\n });\n }\n }\n\n getMetrics(): BulkheadMetrics {\n return {\n name: this.config.name,\n activeCalls: this.activeCalls,\n queuedCalls: this.queue.length,\n maxConcurrentCalls: this.config.maxConcurrentCalls,\n maxQueueSize: this.config.maxQueueSize,\n totalCalls: this.totalCalls,\n successfulCalls: this.successfulCalls,\n failedCalls: this.failedCalls,\n rejectedCalls: this.rejectedCalls,\n timedOutCalls: this.timedOutCalls,\n averageDurationMs:\n this.successfulCalls > 0 ? Math.round(this.totalDurationMs / this.successfulCalls) : 0,\n };\n }\n\n canAccept(): boolean {\n return (\n this.activeCalls < this.config.maxConcurrentCalls ||\n this.queue.length < this.config.maxQueueSize\n );\n }\n\n resetMetrics(): void {\n this.totalCalls = 0;\n this.successfulCalls = 0;\n this.failedCalls = 0;\n this.rejectedCalls = 0;\n this.timedOutCalls = 0;\n this.totalDurationMs = 0;\n }\n}\n","import { Bulkhead, BulkheadConfig, BulkheadMetrics } from './bulkhead.js';\n\nexport interface BulkheadOptions extends Partial<BulkheadConfig> {\n name: string;\n}\n\nconst DEFAULT_CONFIG: Omit<BulkheadConfig, 'name'> = {\n maxConcurrentCalls: 10,\n maxQueueSize: 100,\n queueTimeoutMs: 30000,\n rejectWhenFull: true,\n};\n\nexport class BulkheadRegistry {\n private readonly bulkheads = new Map<string, Bulkhead>();\n\n getOrCreate(options: BulkheadOptions): Bulkhead {\n if (!this.bulkheads.has(options.name)) {\n const config: BulkheadConfig = { ...DEFAULT_CONFIG, ...options, name: options.name };\n this.bulkheads.set(options.name, new Bulkhead(config));\n }\n return this.bulkheads.get(options.name)!;\n }\n\n /** Per-client isolation — 5 concurrent, 20 queued */\n getForClient(clientId: string, endpoint?: string): Bulkhead {\n const name = endpoint ? `client:${clientId}:${endpoint}` : `client:${clientId}`;\n return this.getOrCreate({ name, maxConcurrentCalls: 5, maxQueueSize: 20 });\n }\n\n /** Service-level limiting — 20 concurrent, 200 queued */\n getForService(serviceName: string): Bulkhead {\n return this.getOrCreate({\n name: `service:${serviceName}`,\n maxConcurrentCalls: 20,\n maxQueueSize: 200,\n });\n }\n\n /** Database connection limiting — 15 concurrent, 150 queued */\n getForDatabase(schema: string): Bulkhead {\n return this.getOrCreate({\n name: `database:${schema}`,\n maxConcurrentCalls: 15,\n maxQueueSize: 150,\n });\n }\n\n /** External HTTP calls — 8 concurrent, 50 queued, 10s timeout */\n getForHttpExternal(serviceName: string): Bulkhead {\n return this.getOrCreate({\n name: `http:${serviceName}`,\n maxConcurrentCalls: 8,\n maxQueueSize: 50,\n queueTimeoutMs: 10000,\n });\n }\n\n getAllMetrics(): Record<string, BulkheadMetrics> {\n const metrics: Record<string, BulkheadMetrics> = {};\n for (const [name, bulkhead] of this.bulkheads) {\n metrics[name] = bulkhead.getMetrics();\n }\n return metrics;\n }\n\n /** Returns bulkheads at or above 80% active capacity */\n getOverloadedBulkheads(): BulkheadMetrics[] {\n const overloaded: BulkheadMetrics[] = [];\n for (const bulkhead of this.bulkheads.values()) {\n const m = bulkhead.getMetrics();\n if (m.activeCalls >= m.maxConcurrentCalls * 0.8) {\n overloaded.push(m);\n }\n }\n return overloaded;\n }\n\n resetAllMetrics(): void {\n for (const bulkhead of this.bulkheads.values()) {\n bulkhead.resetMetrics();\n }\n }\n}\n","import { Injectable, Inject, Logger, OnModuleDestroy } from '@nestjs/common';\nimport { BulkheadRegistry } from '../bulkhead/bulkhead.registry.js';\nimport type { BulkheadMetrics } from '../bulkhead/bulkhead.js';\n\n@Injectable()\nexport class BulkheadService implements OnModuleDestroy {\n private readonly logger = new Logger(BulkheadService.name);\n private monitorInterval: ReturnType<typeof setInterval> | null = null;\n\n constructor(@Inject(BulkheadRegistry) private readonly registry: BulkheadRegistry) {\n this.startMonitoring();\n }\n\n onModuleDestroy(): void {\n if (this.monitorInterval) {\n clearInterval(this.monitorInterval);\n this.monitorInterval = null;\n }\n }\n\n getAllMetrics(): Record<string, BulkheadMetrics> {\n return this.registry.getAllMetrics();\n }\n\n getCriticalBulkheads(): BulkheadMetrics[] {\n return Object.values(this.registry.getAllMetrics()).filter(\n m => m.activeCalls / m.maxConcurrentCalls > 0.9,\n );\n }\n\n resetMetrics(name?: string): void {\n if (name) {\n this.registry.getOrCreate({ name }).resetMetrics();\n } else {\n this.registry.resetAllMetrics();\n }\n }\n\n private startMonitoring(): void {\n this.monitorInterval = setInterval(() => {\n const critical = this.getCriticalBulkheads();\n if (critical.length > 0) {\n this.logger.warn(\n `Critical bulkheads detected: ${critical.length}`,\n critical.map(c => ({\n name: c.name,\n utilization: `${Math.round((c.activeCalls / c.maxConcurrentCalls) * 100)}%`,\n queued: c.queuedCalls,\n })),\n );\n }\n }, 60_000);\n }\n}\n","import {\n Injectable,\n Inject,\n CanActivate,\n ExecutionContext,\n ServiceUnavailableException,\n} from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\nimport { BulkheadRegistry } from '../bulkhead/bulkhead.registry.js';\n\nexport interface BulkheadGuardOptions {\n /** Named bulkhead to use; defaults to the route path */\n name?: string;\n /** Create a separate bulkhead per x-client-id header value */\n perClient?: boolean;\n maxConcurrent?: number;\n timeoutMs?: number;\n}\n\nexport const UseBulkhead = (options: BulkheadGuardOptions = {}) =>\n Reflector.createDecorator<BulkheadGuardOptions>({ key: 'bulkhead', transform: () => options });\n\n@Injectable()\nexport class BulkheadGuard implements CanActivate {\n constructor(\n @Inject(Reflector) private readonly reflector: Reflector,\n @Inject(BulkheadRegistry) private readonly registry: BulkheadRegistry,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const options = this.reflector.get<BulkheadGuardOptions>('bulkhead', context.getHandler());\n if (!options) return true;\n\n const request = context.switchToHttp().getRequest();\n const clientId: string = request.headers['x-client-id'] ?? 'anonymous';\n\n const bulkhead = options.perClient\n ? this.registry.getForClient(clientId, options.name ?? (request.route?.path as string))\n : this.registry.getForService(options.name ?? 'default');\n\n if (!bulkhead.canAccept()) {\n throw new ServiceUnavailableException('Service temporarily unavailable due to high load');\n }\n\n (request as Record<string, unknown>)['bulkhead'] = bulkhead;\n return true;\n }\n}\n","import {\n Injectable,\n Inject,\n NestInterceptor,\n ExecutionContext,\n CallHandler,\n ServiceUnavailableException,\n RequestTimeoutException,\n} from '@nestjs/common';\nimport { firstValueFrom, Observable } from 'rxjs';\nimport { BulkheadRegistry } from '../bulkhead/bulkhead.registry.js';\nimport { BulkheadRejectedError, BulkheadTimeoutError } from '../bulkhead/bulkhead.js';\n\n@Injectable()\nexport class BulkheadInterceptor implements NestInterceptor {\n constructor(@Inject(BulkheadRegistry) private readonly registry: BulkheadRegistry) {}\n\n async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<unknown>> {\n const request = context.switchToHttp().getRequest();\n const clientId: string = request.headers['x-client-id'] ?? 'anonymous';\n const handlerName: string = context.getHandler().name;\n\n const bulkhead = this.registry.getForClient(clientId, handlerName);\n\n try {\n const result = await bulkhead.execute(() => firstValueFrom(next.handle()));\n return new Observable(subscriber => {\n subscriber.next(result);\n subscriber.complete();\n });\n } catch (error) {\n if (error instanceof BulkheadRejectedError) {\n throw new ServiceUnavailableException(\n 'Service at capacity — please retry in a moment',\n );\n }\n if (error instanceof BulkheadTimeoutError) {\n throw new RequestTimeoutException(\n 'Request timed out waiting for processing — please retry',\n );\n }\n throw error;\n }\n }\n}\n","import { Injectable, NestMiddleware, Logger } from '@nestjs/common';\nimport type { Request, Response, NextFunction } from 'express';\nimport { Bulkhead, BulkheadRejectedError, BulkheadTimeoutError } from '../bulkhead/bulkhead.js';\nimport type { BulkheadMetrics } from '../bulkhead/bulkhead.js';\n\nconst DEFAULT_CONCURRENCY = 50;\nconst DEFAULT_MAX_QUEUE = 100;\n\n/**\n * Global HTTP middleware that limits simultaneous in-flight requests.\n *\n * Configure via environment variables:\n * HTTP_BULKHEAD_CONCURRENCY — max concurrent requests (default: 50)\n * HTTP_BULKHEAD_MAX_QUEUE — max queued requests (default: 100)\n */\n@Injectable()\nexport class HttpBulkheadMiddleware implements NestMiddleware {\n private readonly logger = new Logger(HttpBulkheadMiddleware.name);\n private readonly bulkhead: Bulkhead;\n\n constructor() {\n const concurrency = parseInt(\n process.env['HTTP_BULKHEAD_CONCURRENCY'] ?? String(DEFAULT_CONCURRENCY),\n 10,\n );\n const maxQueueSize = parseInt(\n process.env['HTTP_BULKHEAD_MAX_QUEUE'] ?? String(DEFAULT_MAX_QUEUE),\n 10,\n );\n\n this.bulkhead = new Bulkhead({\n name: 'http:global',\n maxConcurrentCalls: concurrency,\n maxQueueSize,\n queueTimeoutMs: 30_000,\n rejectWhenFull: true,\n });\n\n this.logger.log(\n `HTTP Bulkhead ready — concurrency: ${concurrency}, maxQueue: ${maxQueueSize}`,\n );\n }\n\n use(req: Request, res: Response, next: NextFunction): void {\n this.bulkhead\n .execute(\n () =>\n new Promise<void>(resolve => {\n res.once('finish', resolve);\n res.once('close', resolve);\n next();\n }),\n )\n .catch((error: unknown) => {\n if (res.headersSent) return;\n if (error instanceof BulkheadRejectedError) {\n const m = this.bulkhead.getMetrics();\n this.logger.warn(\n `HTTP Bulkhead saturated [active=${m.activeCalls} queued=${m.queuedCalls}]` +\n ` — rejecting ${req.method} ${req.path}`,\n );\n res.status(429).json({\n statusCode: 429,\n error: 'Too Many Requests',\n message: 'Service temporarily overloaded. Please retry in a few seconds.',\n });\n } else if (error instanceof BulkheadTimeoutError) {\n res.status(503).json({\n statusCode: 503,\n error: 'Service Unavailable',\n message: 'Request timed out waiting for processing.',\n });\n }\n });\n }\n\n getStats(): BulkheadMetrics {\n return this.bulkhead.getMetrics();\n }\n}\n","import { Module } from '@nestjs/common';\nimport { BulkheadRegistry } from '../bulkhead/bulkhead.registry.js';\nimport { BulkheadService } from './bulkhead.service.js';\nimport { BulkheadGuard } from './bulkhead.guard.js';\nimport { BulkheadInterceptor } from './bulkhead.interceptor.js';\nimport { HttpBulkheadMiddleware } from './http-bulkhead.middleware.js';\n\n@Module({\n providers: [\n BulkheadRegistry,\n BulkheadService,\n BulkheadGuard,\n BulkheadInterceptor,\n HttpBulkheadMiddleware,\n ],\n exports: [\n BulkheadRegistry,\n BulkheadService,\n BulkheadGuard,\n BulkheadInterceptor,\n HttpBulkheadMiddleware,\n ],\n})\nexport class BulkheadModule {}\n","import { BulkheadRegistry } from '../bulkhead/bulkhead.registry.js';\n\n/**\n * Method decorator that wraps execution inside a named bulkhead.\n * The class must have `bulkheadRegistry: BulkheadRegistry` injected.\n */\nexport function WithBulkhead(options: {\n name: string;\n maxConcurrent?: number;\n timeoutMs?: number;\n}) {\n return function (\n _target: unknown,\n _propertyKey: string,\n descriptor: PropertyDescriptor,\n ): PropertyDescriptor {\n const originalMethod = descriptor.value as (...args: unknown[]) => Promise<unknown>;\n\n descriptor.value = async function (...args: unknown[]) {\n const registry = (this as { bulkheadRegistry?: BulkheadRegistry }).bulkheadRegistry;\n if (!registry) throw new Error('BulkheadRegistry not injected in class.');\n\n const bulkhead = registry.getOrCreate({\n name: options.name,\n maxConcurrentCalls: options.maxConcurrent ?? 5,\n queueTimeoutMs: options.timeoutMs ?? 30_000,\n maxQueueSize: 50,\n rejectWhenFull: true,\n });\n\n return bulkhead.execute(() => originalMethod.apply(this, args));\n };\n\n return descriptor;\n };\n}\n"]}
1
+ {"version":3,"sources":["../../src/nestjs/bulkhead.service.ts","../../src/nestjs/bulkhead.guard.ts","../../src/nestjs/bulkhead.interceptor.ts","../../src/nestjs/http-bulkhead.middleware.ts","../../src/nestjs/bulkhead.module.ts","../../src/nestjs/bulkhead.decorator.ts"],"names":["BulkheadService","Logger","__decorateClass","Injectable","__decorateParam","BulkheadRegistry","Reflector","BulkheadGuard","ServiceUnavailableException","Inject","BulkheadInterceptor","firstValueFrom","Observable","BulkheadRejectedError","BulkheadTimeoutError","RequestTimeoutException","HttpBulkheadMiddleware","Bulkhead","BulkheadModule","Module"],"mappings":"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","file":"index.cjs","sourcesContent":["import { Injectable, Inject, Logger, OnModuleDestroy } from '@nestjs/common';\nimport { BulkheadRegistry } from '../bulkhead/bulkhead.registry.js';\nimport type { BulkheadMetrics } from '../bulkhead/bulkhead.js';\n\n@Injectable()\nexport class BulkheadService implements OnModuleDestroy {\n private readonly logger = new Logger(BulkheadService.name);\n private monitorInterval: ReturnType<typeof setInterval> | null = null;\n\n constructor(@Inject(BulkheadRegistry) private readonly registry: BulkheadRegistry) {\n this.startMonitoring();\n }\n\n onModuleDestroy(): void {\n if (this.monitorInterval) {\n clearInterval(this.monitorInterval);\n this.monitorInterval = null;\n }\n }\n\n getAllMetrics(): Record<string, BulkheadMetrics> {\n return this.registry.getAllMetrics();\n }\n\n getCriticalBulkheads(): BulkheadMetrics[] {\n return Object.values(this.registry.getAllMetrics()).filter(\n m => m.activeCalls / m.maxConcurrentCalls > 0.9,\n );\n }\n\n resetMetrics(name?: string): void {\n if (name) {\n this.registry.getOrCreate({ name }).resetMetrics();\n } else {\n this.registry.resetAllMetrics();\n }\n }\n\n private startMonitoring(): void {\n this.monitorInterval = setInterval(() => {\n const critical = this.getCriticalBulkheads();\n if (critical.length > 0) {\n this.logger.warn(\n `Critical bulkheads detected: ${critical.length}`,\n critical.map(c => ({\n name: c.name,\n utilization: `${Math.round((c.activeCalls / c.maxConcurrentCalls) * 100)}%`,\n queued: c.queuedCalls,\n })),\n );\n }\n }, 60_000);\n }\n}\n","import {\n Injectable,\n Inject,\n CanActivate,\n ExecutionContext,\n ServiceUnavailableException,\n} from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\nimport { BulkheadRegistry } from '../bulkhead/bulkhead.registry.js';\n\nexport interface BulkheadGuardOptions {\n /** Named bulkhead to use; defaults to the route path */\n name?: string;\n /** Create a separate bulkhead per x-client-id header value */\n perClient?: boolean;\n maxConcurrent?: number;\n timeoutMs?: number;\n}\n\nexport const UseBulkhead = (options: BulkheadGuardOptions = {}) =>\n Reflector.createDecorator<BulkheadGuardOptions>({ key: 'bulkhead', transform: () => options });\n\n@Injectable()\nexport class BulkheadGuard implements CanActivate {\n constructor(\n @Inject(Reflector) private readonly reflector: Reflector,\n @Inject(BulkheadRegistry) private readonly registry: BulkheadRegistry,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const options = this.reflector.get<BulkheadGuardOptions>('bulkhead', context.getHandler());\n if (!options) return true;\n\n const request = context.switchToHttp().getRequest();\n const clientId: string = request.headers['x-client-id'] ?? 'anonymous';\n\n const bulkhead = options.perClient\n ? this.registry.getForClient(clientId, options.name ?? (request.route?.path as string))\n : this.registry.getForService(options.name ?? 'default');\n\n if (!bulkhead.canAccept()) {\n throw new ServiceUnavailableException('Service temporarily unavailable due to high load');\n }\n\n (request as Record<string, unknown>)['bulkhead'] = bulkhead;\n return true;\n }\n}\n","import {\n Injectable,\n Inject,\n NestInterceptor,\n ExecutionContext,\n CallHandler,\n ServiceUnavailableException,\n RequestTimeoutException,\n} from '@nestjs/common';\nimport { firstValueFrom, Observable } from 'rxjs';\nimport { BulkheadRegistry } from '../bulkhead/bulkhead.registry.js';\nimport { BulkheadRejectedError, BulkheadTimeoutError } from '../bulkhead/bulkhead.js';\n\n@Injectable()\nexport class BulkheadInterceptor implements NestInterceptor {\n constructor(@Inject(BulkheadRegistry) private readonly registry: BulkheadRegistry) {}\n\n async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<unknown>> {\n const request = context.switchToHttp().getRequest();\n const clientId: string = request.headers['x-client-id'] ?? 'anonymous';\n const handlerName: string = context.getHandler().name;\n\n const bulkhead = this.registry.getForClient(clientId, handlerName);\n\n try {\n const result = await bulkhead.execute(() => firstValueFrom(next.handle()));\n return new Observable(subscriber => {\n subscriber.next(result);\n subscriber.complete();\n });\n } catch (error) {\n if (error instanceof BulkheadRejectedError) {\n throw new ServiceUnavailableException(\n 'Service at capacity — please retry in a moment',\n );\n }\n if (error instanceof BulkheadTimeoutError) {\n throw new RequestTimeoutException(\n 'Request timed out waiting for processing — please retry',\n );\n }\n throw error;\n }\n }\n}\n","import { Injectable, NestMiddleware, Logger } from '@nestjs/common';\nimport type { Request, Response, NextFunction } from 'express';\nimport { Bulkhead, BulkheadRejectedError, BulkheadTimeoutError } from '../bulkhead/bulkhead.js';\nimport type { BulkheadMetrics } from '../bulkhead/bulkhead.js';\n\nconst DEFAULT_CONCURRENCY = 50;\nconst DEFAULT_MAX_QUEUE = 100;\n\n/**\n * Global HTTP middleware that limits simultaneous in-flight requests.\n *\n * Configure via environment variables:\n * HTTP_BULKHEAD_CONCURRENCY — max concurrent requests (default: 50)\n * HTTP_BULKHEAD_MAX_QUEUE — max queued requests (default: 100)\n */\n@Injectable()\nexport class HttpBulkheadMiddleware implements NestMiddleware {\n private readonly logger = new Logger(HttpBulkheadMiddleware.name);\n private readonly bulkhead: Bulkhead;\n\n constructor() {\n const concurrency = parseInt(\n process.env['HTTP_BULKHEAD_CONCURRENCY'] ?? String(DEFAULT_CONCURRENCY),\n 10,\n );\n const maxQueueSize = parseInt(\n process.env['HTTP_BULKHEAD_MAX_QUEUE'] ?? String(DEFAULT_MAX_QUEUE),\n 10,\n );\n\n this.bulkhead = new Bulkhead({\n name: 'http:global',\n maxConcurrentCalls: concurrency,\n maxQueueSize,\n queueTimeoutMs: 30_000,\n rejectWhenFull: true,\n });\n\n this.logger.log(\n `HTTP Bulkhead ready — concurrency: ${concurrency}, maxQueue: ${maxQueueSize}`,\n );\n }\n\n use(req: Request, res: Response, next: NextFunction): void {\n this.bulkhead\n .execute(\n () =>\n new Promise<void>(resolve => {\n res.once('finish', resolve);\n res.once('close', resolve);\n next();\n }),\n )\n .catch((error: unknown) => {\n if (res.headersSent) return;\n if (error instanceof BulkheadRejectedError) {\n const m = this.bulkhead.getMetrics();\n this.logger.warn(\n `HTTP Bulkhead saturated [active=${m.activeCalls} queued=${m.queuedCalls}]` +\n ` — rejecting ${req.method} ${req.path}`,\n );\n res.status(429).json({\n statusCode: 429,\n error: 'Too Many Requests',\n message: 'Service temporarily overloaded. Please retry in a few seconds.',\n });\n } else if (error instanceof BulkheadTimeoutError) {\n res.status(503).json({\n statusCode: 503,\n error: 'Service Unavailable',\n message: 'Request timed out waiting for processing.',\n });\n }\n });\n }\n\n getStats(): BulkheadMetrics {\n return this.bulkhead.getMetrics();\n }\n}\n","import { Module } from '@nestjs/common';\nimport { BulkheadRegistry } from '../bulkhead/bulkhead.registry.js';\nimport { BulkheadService } from './bulkhead.service.js';\nimport { BulkheadGuard } from './bulkhead.guard.js';\nimport { BulkheadInterceptor } from './bulkhead.interceptor.js';\nimport { HttpBulkheadMiddleware } from './http-bulkhead.middleware.js';\n\n@Module({\n providers: [\n BulkheadRegistry,\n BulkheadService,\n BulkheadGuard,\n BulkheadInterceptor,\n HttpBulkheadMiddleware,\n ],\n exports: [\n BulkheadRegistry,\n BulkheadService,\n BulkheadGuard,\n BulkheadInterceptor,\n HttpBulkheadMiddleware,\n ],\n})\nexport class BulkheadModule {}\n","import { BulkheadRegistry } from '../bulkhead/bulkhead.registry.js';\n\n/**\n * Method decorator that wraps execution inside a named bulkhead.\n * The class must have `bulkheadRegistry: BulkheadRegistry` injected.\n */\nexport function WithBulkhead(options: {\n name: string;\n maxConcurrent?: number;\n timeoutMs?: number;\n}) {\n return function (\n _target: unknown,\n _propertyKey: string,\n descriptor: PropertyDescriptor,\n ): PropertyDescriptor {\n const originalMethod = descriptor.value as (...args: unknown[]) => Promise<unknown>;\n\n descriptor.value = async function (...args: unknown[]) {\n const registry = (this as { bulkheadRegistry?: BulkheadRegistry }).bulkheadRegistry;\n if (!registry) throw new Error('BulkheadRegistry not injected in class.');\n\n const bulkhead = registry.getOrCreate({\n name: options.name,\n maxConcurrentCalls: options.maxConcurrent ?? 5,\n queueTimeoutMs: options.timeoutMs ?? 30_000,\n maxQueueSize: 50,\n rejectWhenFull: true,\n });\n\n return bulkhead.execute(() => originalMethod.apply(this, args));\n };\n\n return descriptor;\n };\n}\n"]}