@henriquecosta/chaos-api 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/dist/adapters/express.d.ts +21 -0
- package/dist/adapters/express.js +40 -0
- package/dist/adapters/fastify.d.ts +12 -0
- package/dist/adapters/fastify.js +44 -0
- package/dist/adapters/koa.d.ts +12 -0
- package/dist/adapters/koa.js +47 -0
- package/dist/adapters/nestjs.d.ts +34 -0
- package/dist/adapters/nestjs.js +52 -0
- package/dist/bin/chaos-api.d.ts +2 -0
- package/dist/bin/chaos-api.js +33 -0
- package/dist/core/activity-log.d.ts +24 -0
- package/dist/core/activity-log.js +26 -0
- package/dist/core/scenario-engine.d.ts +12 -0
- package/dist/core/scenario-engine.js +38 -0
- package/dist/core/state-store.d.ts +26 -0
- package/dist/core/state-store.js +69 -0
- package/dist/core/types.d.ts +41 -0
- package/dist/core/types.js +1 -0
- package/dist/dashboard/server/control-api.d.ts +9 -0
- package/dist/dashboard/server/control-api.js +140 -0
- package/dist/dashboard/server/index.d.ts +10 -0
- package/dist/dashboard/server/index.js +49 -0
- package/dist/dashboard/ui/app.js +336 -0
- package/dist/dashboard/ui/index.html +215 -0
- package/dist/guardrail.d.ts +10 -0
- package/dist/guardrail.js +20 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +13 -0
- package/dist/outbound/chaos-fetch.d.ts +13 -0
- package/dist/outbound/chaos-fetch.js +50 -0
- package/dist/outbound/index.d.ts +1 -0
- package/dist/outbound/index.js +1 -0
- package/dist/presets/catalog.d.ts +8 -0
- package/dist/presets/catalog.js +157 -0
- package/dist/presets/index.d.ts +15 -0
- package/dist/presets/index.js +22 -0
- package/dist/presets/types.d.ts +17 -0
- package/dist/presets/types.js +1 -0
- package/dist/scenarios/connection-reset.d.ts +7 -0
- package/dist/scenarios/connection-reset.js +8 -0
- package/dist/scenarios/delay.d.ts +6 -0
- package/dist/scenarios/delay.js +9 -0
- package/dist/scenarios/error-response.d.ts +9 -0
- package/dist/scenarios/error-response.js +13 -0
- package/dist/scenarios/index.d.ts +7 -0
- package/dist/scenarios/index.js +7 -0
- package/dist/scenarios/malformed-response.d.ts +11 -0
- package/dist/scenarios/malformed-response.js +13 -0
- package/dist/scenarios/random-error.d.ts +6 -0
- package/dist/scenarios/random-error.js +7 -0
- package/dist/scenarios/random-timeout.d.ts +7 -0
- package/dist/scenarios/random-timeout.js +8 -0
- package/dist/scenarios/registry.d.ts +12 -0
- package/dist/scenarios/registry.js +20 -0
- package/dist/scenarios/stale-response.d.ts +9 -0
- package/dist/scenarios/stale-response.js +10 -0
- package/dist/scenarios/unavailable-503.d.ts +5 -0
- package/dist/scenarios/unavailable-503.js +9 -0
- package/dist/scenarios/unavailable.d.ts +7 -0
- package/dist/scenarios/unavailable.js +9 -0
- package/package.json +59 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { delayScenario } from "./delay.js";
|
|
2
|
+
export { errorResponseScenario } from "./error-response.js";
|
|
3
|
+
export { connectionResetScenario } from "./connection-reset.js";
|
|
4
|
+
export { unavailableScenario } from "./unavailable.js";
|
|
5
|
+
export { malformedResponseScenario } from "./malformed-response.js";
|
|
6
|
+
export { staleResponseScenario } from "./stale-response.js";
|
|
7
|
+
export { SCENARIO_REGISTRY } from "./registry.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ScenarioHandler } from "../core/types.js";
|
|
2
|
+
export interface MalformedResponseOptions {
|
|
3
|
+
statusCode?: number;
|
|
4
|
+
/** Sent as-is; mismatched with the actual body on purpose (default: declares JSON, body is garbled text). */
|
|
5
|
+
contentType?: string;
|
|
6
|
+
/** JSON-serialized then truncated. Omitted = a built-in garbled JSON fragment. */
|
|
7
|
+
body?: unknown;
|
|
8
|
+
/** Fraction of the serialized body to keep, 0..1. Default 0.5 (cut mid-payload). */
|
|
9
|
+
truncateRatio?: number;
|
|
10
|
+
}
|
|
11
|
+
export declare const malformedResponseScenario: ScenarioHandler;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const DEFAULT_GARBLED = '{"data": [{"id": 1, "nam';
|
|
2
|
+
function truncate(text, ratio) {
|
|
3
|
+
const clamped = Math.min(1, Math.max(0, ratio));
|
|
4
|
+
return text.slice(0, Math.max(1, Math.floor(text.length * clamped)));
|
|
5
|
+
}
|
|
6
|
+
export const malformedResponseScenario = (ctx, options) => {
|
|
7
|
+
const { statusCode = 200, contentType = "application/json", body, truncateRatio = 0.5, } = options;
|
|
8
|
+
const payload = body !== undefined ? truncate(JSON.stringify(body), truncateRatio) : DEFAULT_GARBLED;
|
|
9
|
+
ctx.res.header("Content-Type", contentType);
|
|
10
|
+
ctx.res.status(statusCode);
|
|
11
|
+
ctx.res.send(payload);
|
|
12
|
+
return "terminated";
|
|
13
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const randomErrorScenario = (ctx, options) => {
|
|
2
|
+
const { statusCodes = [500], body } = options;
|
|
3
|
+
const code = statusCodes[Math.floor(Math.random() * statusCodes.length)];
|
|
4
|
+
ctx.res.status(code);
|
|
5
|
+
ctx.res.send(body ?? { error: "chaos-api: injected error", status: code });
|
|
6
|
+
return "terminated";
|
|
7
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ScenarioHandler } from "../core/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Simulates a hung upstream: intentionally never writes a response.
|
|
4
|
+
* Adapters must not call `next()`/end the request when this scenario terminates
|
|
5
|
+
* the chain — the connection stays open until the client or server times out.
|
|
6
|
+
*/
|
|
7
|
+
export declare const randomTimeoutScenario: ScenarioHandler;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simulates a hung upstream: intentionally never writes a response.
|
|
3
|
+
* Adapters must not call `next()`/end the request when this scenario terminates
|
|
4
|
+
* the chain — the connection stays open until the client or server times out.
|
|
5
|
+
*/
|
|
6
|
+
export const randomTimeoutScenario = () => {
|
|
7
|
+
return "terminated";
|
|
8
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ScenarioHandler, ScenarioType } from "../core/types.js";
|
|
2
|
+
export interface ScenarioDefinition {
|
|
3
|
+
type: ScenarioType;
|
|
4
|
+
handler: ScenarioHandler;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Single source of truth for the v2 primitives (docs/PRD.md 6.2): type, handler, and
|
|
8
|
+
* apply order for combined scenarios. Array order = fixed priority order.
|
|
9
|
+
* Adding a primitive only touches this file plus the new scenario module — the engine
|
|
10
|
+
* itself doesn't change (docs/PRD.md 6.1 "registry pattern").
|
|
11
|
+
*/
|
|
12
|
+
export declare const SCENARIO_REGISTRY: ScenarioDefinition[];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { connectionResetScenario } from "./connection-reset.js";
|
|
2
|
+
import { delayScenario } from "./delay.js";
|
|
3
|
+
import { errorResponseScenario } from "./error-response.js";
|
|
4
|
+
import { malformedResponseScenario } from "./malformed-response.js";
|
|
5
|
+
import { staleResponseScenario } from "./stale-response.js";
|
|
6
|
+
import { unavailableScenario } from "./unavailable.js";
|
|
7
|
+
/**
|
|
8
|
+
* Single source of truth for the v2 primitives (docs/PRD.md 6.2): type, handler, and
|
|
9
|
+
* apply order for combined scenarios. Array order = fixed priority order.
|
|
10
|
+
* Adding a primitive only touches this file plus the new scenario module — the engine
|
|
11
|
+
* itself doesn't change (docs/PRD.md 6.1 "registry pattern").
|
|
12
|
+
*/
|
|
13
|
+
export const SCENARIO_REGISTRY = [
|
|
14
|
+
{ type: "delay", handler: delayScenario },
|
|
15
|
+
{ type: "error-response", handler: errorResponseScenario },
|
|
16
|
+
{ type: "connection-reset", handler: connectionResetScenario },
|
|
17
|
+
{ type: "unavailable", handler: unavailableScenario },
|
|
18
|
+
{ type: "malformed-response", handler: malformedResponseScenario },
|
|
19
|
+
{ type: "stale-response", handler: staleResponseScenario },
|
|
20
|
+
];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ScenarioHandler } from "../core/types.js";
|
|
2
|
+
export interface StaleResponseOptions {
|
|
3
|
+
/** The stale/cached payload to serve instead of the real one. */
|
|
4
|
+
body?: unknown;
|
|
5
|
+
statusCode?: number;
|
|
6
|
+
/** Sets the `Age` header (seconds since the cached response was fresh). */
|
|
7
|
+
ageSeconds?: number;
|
|
8
|
+
}
|
|
9
|
+
export declare const staleResponseScenario: ScenarioHandler;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const staleResponseScenario = (ctx, options) => {
|
|
2
|
+
const { body, statusCode = 200, ageSeconds } = options;
|
|
3
|
+
if (ageSeconds !== undefined) {
|
|
4
|
+
ctx.res.header("Age", String(ageSeconds));
|
|
5
|
+
}
|
|
6
|
+
ctx.res.header("X-Chaos-Stale", "true");
|
|
7
|
+
ctx.res.status(statusCode);
|
|
8
|
+
ctx.res.send(body ?? { stale: true });
|
|
9
|
+
return "terminated";
|
|
10
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const unavailable503Scenario = (ctx, options) => {
|
|
2
|
+
const { retryAfterSeconds } = options;
|
|
3
|
+
if (retryAfterSeconds !== undefined) {
|
|
4
|
+
ctx.res.header("Retry-After", String(retryAfterSeconds));
|
|
5
|
+
}
|
|
6
|
+
ctx.res.status(503);
|
|
7
|
+
ctx.res.send({ error: "chaos-api: service unavailable" });
|
|
8
|
+
return "terminated";
|
|
9
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ScenarioHandler } from "../core/types.js";
|
|
2
|
+
export interface UnavailableOptions {
|
|
3
|
+
/** 503 (service unavailable), 507 (insufficient storage), 429 (rate limited), etc. */
|
|
4
|
+
statusCode?: number;
|
|
5
|
+
retryAfterSeconds?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare const unavailableScenario: ScenarioHandler;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const unavailableScenario = (ctx, options) => {
|
|
2
|
+
const { statusCode = 503, retryAfterSeconds } = options;
|
|
3
|
+
if (retryAfterSeconds !== undefined) {
|
|
4
|
+
ctx.res.header("Retry-After", String(retryAfterSeconds));
|
|
5
|
+
}
|
|
6
|
+
ctx.res.status(statusCode);
|
|
7
|
+
ctx.res.send({ error: "chaos-api: service unavailable" });
|
|
8
|
+
return "terminated";
|
|
9
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@henriquecosta/chaos-api",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Middleware pra simular falhas de producao (delay, erros, timeout, indisponibilidade, respostas malformadas/obsoletas) em APIs Express/Fastify/NestJS/Koa durante desenvolvimento.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"chaos-api": "./dist/bin/chaos-api.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc -p tsconfig.json && node scripts/copy-ui.mjs",
|
|
22
|
+
"dev": "tsx watch src/bin/chaos-api.ts dashboard",
|
|
23
|
+
"test": "vitest run",
|
|
24
|
+
"test:watch": "vitest",
|
|
25
|
+
"typecheck": "tsc --noEmit"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"express": ">=4",
|
|
29
|
+
"fastify": ">=4",
|
|
30
|
+
"koa": ">=2"
|
|
31
|
+
},
|
|
32
|
+
"peerDependenciesMeta": {
|
|
33
|
+
"express": {
|
|
34
|
+
"optional": true
|
|
35
|
+
},
|
|
36
|
+
"fastify": {
|
|
37
|
+
"optional": true
|
|
38
|
+
},
|
|
39
|
+
"koa": {
|
|
40
|
+
"optional": true
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/express": "^4.17.21",
|
|
45
|
+
"@types/koa": "^2.15.2",
|
|
46
|
+
"@types/node": "^20.14.0",
|
|
47
|
+
"@types/supertest": "^6.0.2",
|
|
48
|
+
"express": "^4.19.2",
|
|
49
|
+
"fastify": "^4.28.1",
|
|
50
|
+
"koa": "^2.16.4",
|
|
51
|
+
"supertest": "^7.0.0",
|
|
52
|
+
"tsx": "^4.16.2",
|
|
53
|
+
"typescript": "^5.5.4",
|
|
54
|
+
"vitest": "^2.0.5"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=18"
|
|
58
|
+
}
|
|
59
|
+
}
|