latency-lab 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.
@@ -0,0 +1,89 @@
1
+ /**
2
+ * How a simulated failure is expressed to the client.
3
+ *
4
+ * - `'http-error'` — Respond with an HTTP error status code drawn from `errorCodes`.
5
+ * - `'tcp-drop'` — Approximate a TCP connection drop by destroying the socket
6
+ * (Express) or returning a 503 (Next.js, where true socket
7
+ * destruction is unavailable in App Router handlers).
8
+ * - `'random'` — Randomly choose between `'http-error'` and `'tcp-drop'`
9
+ * each time a failure occurs.
10
+ */
11
+ type FailureType = 'http-error' | 'tcp-drop' | 'random';
12
+ /**
13
+ * Resolved failure type after `'random'` has been evaluated.
14
+ * Never `'random'` — always a concrete action.
15
+ */
16
+ type ResolvedFailureType = Exclude<FailureType, 'random'>;
17
+ /**
18
+ * Core chaos configuration.
19
+ *
20
+ * All time values are in **milliseconds** unless otherwise noted.
21
+ */
22
+ interface ChaosOptions {
23
+ /**
24
+ * Base latency added to every request in milliseconds.
25
+ * Must be ≥ 0.
26
+ */
27
+ baseDelay: number;
28
+ /**
29
+ * Maximum magnitude of random jitter added to or subtracted from
30
+ * `baseDelay` in milliseconds. Must be ≥ 0.
31
+ *
32
+ * Actual jitter per request is sampled uniformly from `[-jitter, +jitter]`.
33
+ */
34
+ jitter: number;
35
+ /**
36
+ * Period of a sine-wave fluctuation applied on top of jitter, in **seconds**.
37
+ *
38
+ * This simulates slowly oscillating network quality (e.g., a roaming device
39
+ * moving in and out of signal). When omitted, no wave fluctuation is applied.
40
+ *
41
+ * Must be > 0 when provided.
42
+ */
43
+ wavePeriod?: number;
44
+ /**
45
+ * Probability that a given request results in a simulated failure.
46
+ * Must be in the range [0, 1].
47
+ *
48
+ * - `0` → failures never occur
49
+ * - `1` → every request fails
50
+ * - `0.1` → ~10% of requests fail
51
+ */
52
+ failureRate: number;
53
+ /**
54
+ * Determines how simulated failures are expressed to callers.
55
+ */
56
+ failureType: FailureType;
57
+ /**
58
+ * Pool of HTTP status codes to choose from when responding with an HTTP error.
59
+ * Must contain at least one entry.
60
+ *
61
+ * Only used when `failureType` resolves to `'http-error'`.
62
+ */
63
+ errorCodes: number[];
64
+ }
65
+ /**
66
+ * Options passed to framework-level middleware / handler wrappers.
67
+ * Extends `ChaosOptions` with request-filtering capabilities.
68
+ */
69
+ interface MiddlewareOptions extends ChaosOptions {
70
+ /**
71
+ * List of URL path prefixes that should bypass chaos injection entirely.
72
+ *
73
+ * Matching is prefix-based and case-sensitive.
74
+ *
75
+ * @example
76
+ * excludeRoutes: ['/health', '/metrics', '/_next']
77
+ */
78
+ excludeRoutes?: string[];
79
+ }
80
+ /**
81
+ * Structured error thrown when a `ChaosOptions` or `MiddlewareOptions`
82
+ * object fails validation.
83
+ */
84
+ declare class ChaosConfigError extends Error {
85
+ readonly name = "ChaosConfigError";
86
+ constructor(message: string);
87
+ }
88
+
89
+ export { ChaosConfigError, type ChaosOptions, type FailureType, type MiddlewareOptions, type ResolvedFailureType };
@@ -0,0 +1,89 @@
1
+ /**
2
+ * How a simulated failure is expressed to the client.
3
+ *
4
+ * - `'http-error'` — Respond with an HTTP error status code drawn from `errorCodes`.
5
+ * - `'tcp-drop'` — Approximate a TCP connection drop by destroying the socket
6
+ * (Express) or returning a 503 (Next.js, where true socket
7
+ * destruction is unavailable in App Router handlers).
8
+ * - `'random'` — Randomly choose between `'http-error'` and `'tcp-drop'`
9
+ * each time a failure occurs.
10
+ */
11
+ type FailureType = 'http-error' | 'tcp-drop' | 'random';
12
+ /**
13
+ * Resolved failure type after `'random'` has been evaluated.
14
+ * Never `'random'` — always a concrete action.
15
+ */
16
+ type ResolvedFailureType = Exclude<FailureType, 'random'>;
17
+ /**
18
+ * Core chaos configuration.
19
+ *
20
+ * All time values are in **milliseconds** unless otherwise noted.
21
+ */
22
+ interface ChaosOptions {
23
+ /**
24
+ * Base latency added to every request in milliseconds.
25
+ * Must be ≥ 0.
26
+ */
27
+ baseDelay: number;
28
+ /**
29
+ * Maximum magnitude of random jitter added to or subtracted from
30
+ * `baseDelay` in milliseconds. Must be ≥ 0.
31
+ *
32
+ * Actual jitter per request is sampled uniformly from `[-jitter, +jitter]`.
33
+ */
34
+ jitter: number;
35
+ /**
36
+ * Period of a sine-wave fluctuation applied on top of jitter, in **seconds**.
37
+ *
38
+ * This simulates slowly oscillating network quality (e.g., a roaming device
39
+ * moving in and out of signal). When omitted, no wave fluctuation is applied.
40
+ *
41
+ * Must be > 0 when provided.
42
+ */
43
+ wavePeriod?: number;
44
+ /**
45
+ * Probability that a given request results in a simulated failure.
46
+ * Must be in the range [0, 1].
47
+ *
48
+ * - `0` → failures never occur
49
+ * - `1` → every request fails
50
+ * - `0.1` → ~10% of requests fail
51
+ */
52
+ failureRate: number;
53
+ /**
54
+ * Determines how simulated failures are expressed to callers.
55
+ */
56
+ failureType: FailureType;
57
+ /**
58
+ * Pool of HTTP status codes to choose from when responding with an HTTP error.
59
+ * Must contain at least one entry.
60
+ *
61
+ * Only used when `failureType` resolves to `'http-error'`.
62
+ */
63
+ errorCodes: number[];
64
+ }
65
+ /**
66
+ * Options passed to framework-level middleware / handler wrappers.
67
+ * Extends `ChaosOptions` with request-filtering capabilities.
68
+ */
69
+ interface MiddlewareOptions extends ChaosOptions {
70
+ /**
71
+ * List of URL path prefixes that should bypass chaos injection entirely.
72
+ *
73
+ * Matching is prefix-based and case-sensitive.
74
+ *
75
+ * @example
76
+ * excludeRoutes: ['/health', '/metrics', '/_next']
77
+ */
78
+ excludeRoutes?: string[];
79
+ }
80
+ /**
81
+ * Structured error thrown when a `ChaosOptions` or `MiddlewareOptions`
82
+ * object fails validation.
83
+ */
84
+ declare class ChaosConfigError extends Error {
85
+ readonly name = "ChaosConfigError";
86
+ constructor(message: string);
87
+ }
88
+
89
+ export { ChaosConfigError, type ChaosOptions, type FailureType, type MiddlewareOptions, type ResolvedFailureType };
package/dist/types.js ADDED
@@ -0,0 +1,12 @@
1
+ // src/types.ts
2
+ var ChaosConfigError = class extends Error {
3
+ name = "ChaosConfigError";
4
+ constructor(message) {
5
+ super(message);
6
+ Object.setPrototypeOf(this, new.target.prototype);
7
+ }
8
+ };
9
+
10
+ export { ChaosConfigError };
11
+ //# sourceMappingURL=types.js.map
12
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts"],"names":[],"mappings":";AA4FO,IAAM,gBAAA,GAAN,cAA+B,KAAA,CAAM;AAAA,EACxB,IAAA,GAAO,kBAAA;AAAA,EAEzB,YAAY,OAAA,EAAiB;AAC3B,IAAA,KAAA,CAAM,OAAO,CAAA;AAEb,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,GAAA,CAAA,MAAA,CAAW,SAAS,CAAA;AAAA,EAClD;AACF","file":"types.js","sourcesContent":["/**\n * How a simulated failure is expressed to the client.\n *\n * - `'http-error'` — Respond with an HTTP error status code drawn from `errorCodes`.\n * - `'tcp-drop'` — Approximate a TCP connection drop by destroying the socket\n * (Express) or returning a 503 (Next.js, where true socket\n * destruction is unavailable in App Router handlers).\n * - `'random'` — Randomly choose between `'http-error'` and `'tcp-drop'`\n * each time a failure occurs.\n */\nexport type FailureType = 'http-error' | 'tcp-drop' | 'random';\n\n/**\n * Resolved failure type after `'random'` has been evaluated.\n * Never `'random'` — always a concrete action.\n */\nexport type ResolvedFailureType = Exclude<FailureType, 'random'>;\n\n/**\n * Core chaos configuration.\n *\n * All time values are in **milliseconds** unless otherwise noted.\n */\nexport interface ChaosOptions {\n /**\n * Base latency added to every request in milliseconds.\n * Must be ≥ 0.\n */\n baseDelay: number;\n\n /**\n * Maximum magnitude of random jitter added to or subtracted from\n * `baseDelay` in milliseconds. Must be ≥ 0.\n *\n * Actual jitter per request is sampled uniformly from `[-jitter, +jitter]`.\n */\n jitter: number;\n\n /**\n * Period of a sine-wave fluctuation applied on top of jitter, in **seconds**.\n *\n * This simulates slowly oscillating network quality (e.g., a roaming device\n * moving in and out of signal). When omitted, no wave fluctuation is applied.\n *\n * Must be > 0 when provided.\n */\n wavePeriod?: number;\n\n /**\n * Probability that a given request results in a simulated failure.\n * Must be in the range [0, 1].\n *\n * - `0` → failures never occur\n * - `1` → every request fails\n * - `0.1` → ~10% of requests fail\n */\n failureRate: number;\n\n /**\n * Determines how simulated failures are expressed to callers.\n */\n failureType: FailureType;\n\n /**\n * Pool of HTTP status codes to choose from when responding with an HTTP error.\n * Must contain at least one entry.\n *\n * Only used when `failureType` resolves to `'http-error'`.\n */\n errorCodes: number[];\n}\n\n/**\n * Options passed to framework-level middleware / handler wrappers.\n * Extends `ChaosOptions` with request-filtering capabilities.\n */\nexport interface MiddlewareOptions extends ChaosOptions {\n /**\n * List of URL path prefixes that should bypass chaos injection entirely.\n *\n * Matching is prefix-based and case-sensitive.\n *\n * @example\n * excludeRoutes: ['/health', '/metrics', '/_next']\n */\n excludeRoutes?: string[];\n}\n\n/**\n * Structured error thrown when a `ChaosOptions` or `MiddlewareOptions`\n * object fails validation.\n */\nexport class ChaosConfigError extends Error {\n override readonly name = 'ChaosConfigError';\n\n constructor(message: string) {\n super(message);\n // Maintain proper prototype chain in transpiled output\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,127 @@
1
+ {
2
+ "name": "latency-lab",
3
+ "version": "1.0.0",
4
+ "description": "Inject realistic network chaos into backend applications during local development and testing",
5
+ "keywords": [
6
+ "network",
7
+ "chaos",
8
+ "latency",
9
+ "testing",
10
+ "middleware",
11
+ "express",
12
+ "nextjs",
13
+ "development",
14
+ "simulation",
15
+ "fault-injection"
16
+ ],
17
+ "author": "Mahesh Trapasiya",
18
+ "license": "MIT",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/MaheshTrapasiya/latency-lab.git"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/MaheshTrapasiya/latency-lab/issues"
25
+ },
26
+ "homepage": "https://github.com/MaheshTrapasiya/latency-lab#readme",
27
+ "type": "module",
28
+ "sideEffects": false,
29
+ "main": "./dist/index.cjs",
30
+ "module": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "exports": {
33
+ ".": {
34
+ "import": {
35
+ "types": "./dist/index.d.ts",
36
+ "default": "./dist/index.js"
37
+ },
38
+ "require": {
39
+ "types": "./dist/index.d.cts",
40
+ "default": "./dist/index.cjs"
41
+ }
42
+ },
43
+ "./core": {
44
+ "import": {
45
+ "types": "./dist/core.d.ts",
46
+ "default": "./dist/core.js"
47
+ },
48
+ "require": {
49
+ "types": "./dist/core.d.cts",
50
+ "default": "./dist/core.cjs"
51
+ }
52
+ },
53
+ "./express": {
54
+ "import": {
55
+ "types": "./dist/express.d.ts",
56
+ "default": "./dist/express.js"
57
+ },
58
+ "require": {
59
+ "types": "./dist/express.d.cts",
60
+ "default": "./dist/express.cjs"
61
+ }
62
+ },
63
+ "./next": {
64
+ "import": {
65
+ "types": "./dist/next.d.ts",
66
+ "default": "./dist/next.js"
67
+ },
68
+ "require": {
69
+ "types": "./dist/next.d.cts",
70
+ "default": "./dist/next.cjs"
71
+ }
72
+ },
73
+ "./presets": {
74
+ "import": {
75
+ "types": "./dist/presets.d.ts",
76
+ "default": "./dist/presets.js"
77
+ },
78
+ "require": {
79
+ "types": "./dist/presets.d.cts",
80
+ "default": "./dist/presets.cjs"
81
+ }
82
+ }
83
+ },
84
+ "files": [
85
+ "dist",
86
+ "README.md",
87
+ "LICENSE"
88
+ ],
89
+ "scripts": {
90
+ "build": "tsup",
91
+ "build:check": "tsc -p tsconfig.build.json --noEmit",
92
+ "dev": "tsup --watch",
93
+ "lint": "eslint src test",
94
+ "lint:fix": "eslint src test --fix",
95
+ "test": "vitest run",
96
+ "test:watch": "vitest",
97
+ "test:coverage": "vitest run --coverage",
98
+ "typecheck": "tsc --noEmit",
99
+ "prepublishOnly": "npm run build:check && npm run lint && npm run test && npm run build",
100
+ "clean": "rm -rf dist"
101
+ },
102
+ "devDependencies": {
103
+ "@types/node": "^25.9.3",
104
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
105
+ "@typescript-eslint/parser": "^8.0.0",
106
+ "@vitest/coverage-v8": "^2.1.9",
107
+ "eslint": "^9.0.0",
108
+ "tsup": "^8.5.1",
109
+ "typescript": "^5.9.3",
110
+ "vitest": "^2.1.9"
111
+ },
112
+ "peerDependencies": {
113
+ "express": "^4.0.0 || ^5.0.0",
114
+ "next": "^14.0.0 || ^15.0.0"
115
+ },
116
+ "peerDependenciesMeta": {
117
+ "express": {
118
+ "optional": true
119
+ },
120
+ "next": {
121
+ "optional": true
122
+ }
123
+ },
124
+ "engines": {
125
+ "node": ">=18.0.0"
126
+ }
127
+ }