@felipe-lib/bulkhead 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # bulkhead
2
+
3
+ **Student project** — Bulkhead pattern implementation for TypeScript. Limits concurrent executions and queues excess requests to prevent resource exhaustion.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install bulkhead
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { Bulkhead } from "bulkhead";
15
+
16
+ // Per-route instances for proper isolation
17
+ const ordersApi = new Bulkhead<Order>(5, 10);
18
+ const paymentsApi = new Bulkhead<Payment>(3, 5);
19
+
20
+ // Example 1: basic usage
21
+ async function getOrder(id: string): Promise<Order> {
22
+ return ordersApi.execute(() => fetch(`/orders/${id}`).then(r => r.json()));
23
+ }
24
+
25
+ // Example 2: with error handling
26
+ async function processPayment(data: PaymentData): Promise<Payment> {
27
+ try {
28
+ return await paymentsApi.execute(() =>
29
+ fetch("/payments", {
30
+ method: "POST",
31
+ body: JSON.stringify(data),
32
+ headers: { "Content-Type": "application/json" },
33
+ }).then(r => r.json())
34
+ );
35
+ } catch (e) {
36
+ if (e instanceof Error && e.message === "Queue is full") {
37
+ console.error("Payment service busy — try again later");
38
+ // Fallback: return cached result or throw a friendly error
39
+ }
40
+ throw e;
41
+ }
42
+ }
43
+
44
+ // Example 3: concurrent requests — bulkhead limits automatically
45
+ async function fetchAll(ids: string[]): Promise<Order[]> {
46
+ const results = await Promise.allSettled(
47
+ ids.map(id => getOrder(id))
48
+ );
49
+ return results
50
+ .filter((r): r is PromiseFulfilledResult<Order> => r.status === "fulfilled")
51
+ .map(r => r.value);
52
+ }
53
+ ```
54
+
55
+ ## API
56
+
57
+ ### `new Bulkhead<T>(maxLimit, maxLimitQueue)`
58
+
59
+ Creates a bulkhead instance.
60
+
61
+ - `maxLimit` — max concurrent executions (must be > 0)
62
+ - `maxLimitQueue` — max queue size (must be >= 0)
63
+
64
+ ### `bulkhead.execute(fn): Promise<T>`
65
+
66
+ Executes `fn` immediately if under `maxLimit`. Otherwise queues the request and runs it as soon as a slot frees up. Throws `"Queue is full"` when both limits are reached.
67
+
68
+ ## License
69
+
70
+ ISC
@@ -0,0 +1,70 @@
1
+ # bulkhead
2
+
3
+ **Projeto estudantil** — Implementação do padrão bulkhead para TypeScript. Limita execuções concorrentes e enfileira requisições excedentes para evitar esgotamento de recursos.
4
+
5
+ ## Instalação
6
+
7
+ ```bash
8
+ npm install bulkhead
9
+ ```
10
+
11
+ ## Uso
12
+
13
+ ```typescript
14
+ import { Bulkhead } from "bulkhead";
15
+
16
+ // Instâncias por rota para isolamento adequado
17
+ const ordersApi = new Bulkhead<Order>(5, 10);
18
+ const paymentsApi = new Bulkhead<Payment>(3, 5);
19
+
20
+ // Exemplo 1: uso básico
21
+ async function getOrder(id: string): Promise<Order> {
22
+ return ordersApi.execute(() => fetch(`/orders/${id}`).then(r => r.json()));
23
+ }
24
+
25
+ // Exemplo 2: com tratamento de erro
26
+ async function processPayment(data: PaymentData): Promise<Payment> {
27
+ try {
28
+ return await paymentsApi.execute(() =>
29
+ fetch("/payments", {
30
+ method: "POST",
31
+ body: JSON.stringify(data),
32
+ headers: { "Content-Type": "application/json" },
33
+ }).then(r => r.json())
34
+ );
35
+ } catch (e) {
36
+ if (e instanceof Error && e.message === "Queue is full") {
37
+ console.error("Serviço de pagamento ocupado — tente novamente mais tarde");
38
+ // Fallback: usar resultado em cache ou lançar erro amigável
39
+ }
40
+ throw e;
41
+ }
42
+ }
43
+
44
+ // Exemplo 3: requisições concorrentes — bulkhead limita automaticamente
45
+ async function fetchAll(ids: string[]): Promise<Order[]> {
46
+ const results = await Promise.allSettled(
47
+ ids.map(id => getOrder(id))
48
+ );
49
+ return results
50
+ .filter((r): r is PromiseFulfilledResult<Order> => r.status === "fulfilled")
51
+ .map(r => r.value);
52
+ }
53
+ ```
54
+
55
+ ## API
56
+
57
+ ### `new Bulkhead<T>(maxLimit, maxLimitQueue)`
58
+
59
+ Cria uma instância do bulkhead.
60
+
61
+ - `maxLimit` — máximo de execuções simultâneas (deve ser > 0)
62
+ - `maxLimitQueue` — tamanho máximo da fila (deve ser >= 0)
63
+
64
+ ### `bulkhead.execute(fn): Promise<T>`
65
+
66
+ Executa `fn` imediatamente se abaixo de `maxLimit`. Caso contrário, enfileira a requisição e executa assim que uma vaga for liberada. Lança `"Queue is full"` quando ambos os limites são atingidos.
67
+
68
+ ## Licença
69
+
70
+ ISC
@@ -0,0 +1,11 @@
1
+ export declare class Bulkhead<T = unknown> {
2
+ private currentProcess;
3
+ private maxLimit;
4
+ private maxLimitQueue;
5
+ private queue;
6
+ constructor(maxLimit: number, maxLimitQueue: number);
7
+ execute(fetchPromise: () => Promise<T>): Promise<void | T>;
8
+ private resolveFetchers;
9
+ private processQueue;
10
+ }
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,qBAAa,QAAQ,CAAC,CAAC,GAAG,OAAO;IAC7B,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,aAAa,CAAc;IACnC,OAAO,CAAC,KAAK,CAAsB;IAEnC,YAAY,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAGlD;IAEY,OAAO,CAAC,YAAY,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,qBAiBlD;YAEa,eAAe;YAgBf,YAAY;CAY7B"}
package/dist/index.js ADDED
@@ -0,0 +1,58 @@
1
+ export class Bulkhead {
2
+ currentProcess = 0;
3
+ maxLimit = 10;
4
+ maxLimitQueue = 10;
5
+ queue = [];
6
+ constructor(maxLimit, maxLimitQueue) {
7
+ this.maxLimit = maxLimit;
8
+ this.maxLimitQueue = maxLimitQueue;
9
+ }
10
+ async execute(fetchPromise) {
11
+ if (this.currentProcess < this.maxLimit) {
12
+ return await this.resolveFetchers({ fnFetch: fetchPromise });
13
+ }
14
+ else if (this.currentProcess >= this.maxLimit &&
15
+ this.queue.length < this.maxLimitQueue) {
16
+ return new Promise((resolve, reject) => {
17
+ this.queue.push({
18
+ fnFetch: fetchPromise,
19
+ fnReject: reject,
20
+ fnResolve: resolve,
21
+ });
22
+ });
23
+ }
24
+ else {
25
+ throw new Error("Queue is full");
26
+ }
27
+ }
28
+ async resolveFetchers(item) {
29
+ this.currentProcess++;
30
+ try {
31
+ const data = await item.fnFetch();
32
+ if (item?.fnResolve)
33
+ item.fnResolve(data);
34
+ return data;
35
+ }
36
+ catch (e) {
37
+ if (item?.fnReject)
38
+ return item.fnReject(e);
39
+ throw e;
40
+ }
41
+ finally {
42
+ this.currentProcess--;
43
+ await this.processQueue();
44
+ }
45
+ }
46
+ async processQueue() {
47
+ if (this.queue.length > 0 && this.currentProcess < this.maxLimit) {
48
+ const item = this.queue.shift();
49
+ if (item)
50
+ return await this.resolveFetchers({
51
+ fnFetch: item.fnFetch,
52
+ fnReject: item.fnReject,
53
+ fnResolve: item.fnResolve,
54
+ });
55
+ }
56
+ }
57
+ }
58
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,QAAQ;IACT,cAAc,GAAW,CAAC,CAAC;IAC3B,QAAQ,GAAW,EAAE,CAAC;IACtB,aAAa,GAAW,EAAE,CAAC;IAC3B,KAAK,GAAmB,EAAE,CAAC;IAEnC,YAAY,QAAgB,EAAE,aAAqB;QAC/C,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;IACvC,CAAC;IAEM,KAAK,CAAC,OAAO,CAAC,YAA8B;QAC/C,IAAI,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YACtC,OAAO,MAAM,IAAI,CAAC,eAAe,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;QACjE,CAAC;aAAM,IACH,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,QAAQ;YACpC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,aAAa,EACxC,CAAC;YACC,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACtC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;oBACZ,OAAO,EAAE,YAAY;oBACrB,QAAQ,EAAE,MAAM;oBAChB,SAAS,EAAE,OAAO;iBACrB,CAAC,CAAC;YACP,CAAC,CAAC,CAAC;QACP,CAAC;aAAM,CAAC;YACJ,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;QACrC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,eAAe,CAAC,IAAkB;QAC5C,IAAI,CAAC,cAAc,EAAE,CAAC;QAEtB,IAAI,CAAC;YACD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YAClC,IAAI,IAAI,EAAE,SAAS;gBAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAE1C,OAAO,IAAI,CAAC;QAChB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,IAAI,IAAI,EAAE,QAAQ;gBAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YAC5C,MAAM,CAAC,CAAC;QACZ,CAAC;gBAAS,CAAC;YACP,IAAI,CAAC,cAAc,EAAE,CAAC;YACtB,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QAC9B,CAAC;IACL,CAAC;IACO,KAAK,CAAC,YAAY;QACtB,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC/D,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YAEhC,IAAI,IAAI;gBACJ,OAAO,MAAM,IAAI,CAAC,eAAe,CAAC;oBAC9B,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,SAAS,EAAE,IAAI,CAAC,SAAS;iBAC5B,CAAC,CAAC;QACX,CAAC;IACL,CAAC;CACJ"}
@@ -0,0 +1,6 @@
1
+ export interface QueueItem<T = unknown> {
2
+ fnFetch: () => Promise<T>;
3
+ fnReject?: (reason?: any) => void;
4
+ fnResolve?: (value: T | PromiseLike<T>) => void;
5
+ }
6
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS,CAAC,CAAC,GAAG,OAAO;IAClC,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC;IAC1B,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IAClC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;CACnD"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@felipe-lib/bulkhead",
3
+ "version": "1.0.0",
4
+ "description": "Bulkhead pattern implementation for TypeScript",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "test": "echo \"Error: no test specified\" && exit 1"
20
+ },
21
+ "keywords": [
22
+ "bulkhead",
23
+ "resilience",
24
+ "concurrency",
25
+ "typescript"
26
+ ],
27
+ "author": "",
28
+ "license": "ISC",
29
+ "packageManager": "npm@11.16.0",
30
+ "devDependencies": {
31
+ "typescript": "^7.0.2"
32
+ }
33
+ }