@stuntman/shared 0.1.5 → 0.1.7
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/package.json +8 -5
- package/src/appError.ts +0 -2
- package/src/config.ts +27 -10
- package/src/constants.ts +1 -0
- package/src/escapeStringRegexp.ts +1 -0
- package/src/gqlParser.ts +40 -0
- package/src/index.ts +34 -12
- package/src/rawHeaders.ts +7 -6
- package/src/stringify.ts +1 -1
- package/dist/appError.d.ts +0 -14
- package/dist/appError.js +0 -17
- package/dist/config.d.ts +0 -2
- package/dist/config.js +0 -45
- package/dist/constants.d.ts +0 -14
- package/dist/constants.js +0 -17
- package/dist/index.d.ts +0 -166
- package/dist/index.js +0 -40
- package/dist/logger.d.ts +0 -9
- package/dist/logger.js +0 -17
- package/dist/rawHeaders.d.ts +0 -12
- package/dist/rawHeaders.js +0 -83
- package/dist/stringify.d.ts +0 -1
- package/dist/stringify.js +0 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stuntman/shared",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Stuntman - HTTP proxy / mock shared types and utils",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -36,7 +36,10 @@
|
|
|
36
36
|
"pino": "8.10.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@
|
|
39
|
+
"@jest/globals": "29.4.3",
|
|
40
|
+
"@types/config": "3.3.0",
|
|
41
|
+
"jest": "29.4.3",
|
|
42
|
+
"typescript": "4.9.5"
|
|
40
43
|
},
|
|
41
44
|
"files": [
|
|
42
45
|
"src/**",
|
|
@@ -46,10 +49,10 @@
|
|
|
46
49
|
"CHANGELOG.md"
|
|
47
50
|
],
|
|
48
51
|
"scripts": {
|
|
49
|
-
"test": "
|
|
52
|
+
"test": "SUPPRESS_NO_CONFIG_WARNING=1 jest",
|
|
50
53
|
"clean": "rm -fr dist",
|
|
51
54
|
"build": "tsc",
|
|
52
|
-
"lint": "prettier --check
|
|
53
|
-
"lint:fix": "prettier --write ./{src,test} && eslint ./{src,test} --
|
|
55
|
+
"lint": "prettier --check \"./{src,test}/**/*\" && eslint \"./{src,test}/**/*\"",
|
|
56
|
+
"lint:fix": "prettier --write \"./{src,test}/**/*\" && eslint \"./{src,test}/**/*\" --fix"
|
|
54
57
|
}
|
|
55
58
|
}
|
package/src/appError.ts
CHANGED
|
@@ -8,9 +8,7 @@ export interface AppErrorInterface {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export class AppError extends Error {
|
|
11
|
-
public readonly name: string;
|
|
12
11
|
public readonly httpCode: Stuntman.HttpCode;
|
|
13
|
-
public readonly uuid?: string;
|
|
14
12
|
public readonly isOperational: boolean = true;
|
|
15
13
|
|
|
16
14
|
constructor(args: AppErrorInterface) {
|
package/src/config.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
Config,
|
|
3
3
|
DEFAULT_API_PORT,
|
|
4
4
|
DEFAULT_MOCK_DOMAIN,
|
|
5
5
|
EXTERNAL_DNS,
|
|
@@ -14,7 +14,7 @@ import path from 'path';
|
|
|
14
14
|
|
|
15
15
|
// TODO safeguards & defaults
|
|
16
16
|
|
|
17
|
-
const defaultConfig:
|
|
17
|
+
const defaultConfig: Config = {
|
|
18
18
|
api: {
|
|
19
19
|
disabled: false,
|
|
20
20
|
port: DEFAULT_API_PORT,
|
|
@@ -38,16 +38,33 @@ const defaultConfig: ServerConfig = {
|
|
|
38
38
|
webgui: {
|
|
39
39
|
disabled: false,
|
|
40
40
|
},
|
|
41
|
+
client: {
|
|
42
|
+
timeout: 60000,
|
|
43
|
+
host: 'localhost',
|
|
44
|
+
protocol: 'http',
|
|
45
|
+
port: DEFAULT_API_PORT,
|
|
46
|
+
},
|
|
41
47
|
};
|
|
42
48
|
|
|
43
|
-
|
|
49
|
+
let configFromFile: Config | null = null;
|
|
44
50
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
class ConfigWrapper {
|
|
52
|
+
get stuntmanConfig(): Config {
|
|
53
|
+
if (!configFromFile) {
|
|
54
|
+
config.util.setModuleDefaults('stuntman', defaultConfig);
|
|
55
|
+
try {
|
|
56
|
+
configFromFile = config.get<Config>('stuntman');
|
|
57
|
+
} catch (error) {
|
|
58
|
+
// eslint-disable-next-line no-console
|
|
59
|
+
console.warn('unable to find correct config - starting with defaults');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (!configFromFile) {
|
|
63
|
+
throw new Error('error getting config');
|
|
64
|
+
}
|
|
65
|
+
return configFromFile;
|
|
66
|
+
}
|
|
51
67
|
}
|
|
68
|
+
const configWrapper = new ConfigWrapper();
|
|
52
69
|
|
|
53
|
-
export
|
|
70
|
+
export = configWrapper;
|
package/src/constants.ts
CHANGED
|
@@ -12,3 +12,4 @@ export const MAX_RULE_TTL_SECONDS = 60 * 60;
|
|
|
12
12
|
export const CATCH_ALL_RULE_PRIORITY = Infinity;
|
|
13
13
|
export const CATCH_RULE_NAME = 'internal/catch-all';
|
|
14
14
|
export const EXTERNAL_DNS = ['8.8.8.8', '1.1.1.1'];
|
|
15
|
+
export const VALID_HOSTNAME_REGEX = /^(([a-z0-9]|[a-z0-9][a-z0-9\\-]*[a-z0-9])\.)*([a-z0-9]|[a-z0-9][a-z0-9\\-]*[a-z0-9])$/i;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const escapeStringRegexp = (input: string) => input.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/-/g, '\\x2d');
|
package/src/gqlParser.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type * as Stuntman from './index';
|
|
2
|
+
import { logger } from './logger';
|
|
3
|
+
|
|
4
|
+
export const naiveGQLParser = (body: Buffer | string): Stuntman.GQLRequestBody | undefined => {
|
|
5
|
+
// TODO consider real parser :P
|
|
6
|
+
try {
|
|
7
|
+
let json: Stuntman.GQLRequestBody | undefined = undefined;
|
|
8
|
+
try {
|
|
9
|
+
json = JSON.parse(Buffer.isBuffer(body) ? body.toString('utf-8') : body);
|
|
10
|
+
} catch (kiss) {
|
|
11
|
+
// and swallow
|
|
12
|
+
}
|
|
13
|
+
if (!json?.query && !json?.operationName) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
const lines = json.query
|
|
17
|
+
.split('\n')
|
|
18
|
+
.map((l) => l.replace(/^\s+/g, '').trim())
|
|
19
|
+
.filter((l) => !!l);
|
|
20
|
+
if (!lines[0]) {
|
|
21
|
+
throw new Error('unable to find query');
|
|
22
|
+
}
|
|
23
|
+
if (/^query /.test(lines[0])) {
|
|
24
|
+
json.type = 'query';
|
|
25
|
+
} else if (/^mutation /.test(lines[0])) {
|
|
26
|
+
json.type = 'mutation';
|
|
27
|
+
} else {
|
|
28
|
+
throw new Error(`Unable to resolve query type of ${lines[0]}`);
|
|
29
|
+
}
|
|
30
|
+
if (json.operationName && lines[1]) {
|
|
31
|
+
json.methodName = lines[1].split('(')[0]!.split('{')[0]!;
|
|
32
|
+
} else if (json.operationName) {
|
|
33
|
+
json.methodName = lines[0].split('(')[0]!.split('{')[0]!;
|
|
34
|
+
}
|
|
35
|
+
return json;
|
|
36
|
+
} catch (error) {
|
|
37
|
+
logger.debug(error, 'unable to parse GQL');
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -4,9 +4,14 @@ export * from './appError';
|
|
|
4
4
|
export * from './logger';
|
|
5
5
|
export * from './stringify';
|
|
6
6
|
export * from './rawHeaders';
|
|
7
|
-
export * from './
|
|
8
|
-
|
|
7
|
+
export * from './gqlParser';
|
|
8
|
+
export * from './escapeStringRegexp';
|
|
9
9
|
import fs from 'fs';
|
|
10
|
+
|
|
11
|
+
import config from './config';
|
|
12
|
+
export const stuntmanConfig = config.stuntmanConfig;
|
|
13
|
+
|
|
14
|
+
// TODO this file read sucks
|
|
10
15
|
export const INDEX_DTS = fs.readFileSync(`${__dirname}/index.d.ts`, 'utf-8');
|
|
11
16
|
|
|
12
17
|
type NonObject = string | number | boolean | symbol | undefined | null | any[];
|
|
@@ -53,7 +58,7 @@ export type RemotableFunction<T extends Function> = {
|
|
|
53
58
|
|
|
54
59
|
export type SerializedRemotableFunction = {
|
|
55
60
|
localFn: string;
|
|
56
|
-
|
|
61
|
+
localVariables?: string;
|
|
57
62
|
remoteFn: string;
|
|
58
63
|
};
|
|
59
64
|
|
|
@@ -92,7 +97,7 @@ export type BaseRequest = {
|
|
|
92
97
|
export type Request = BaseRequest & {
|
|
93
98
|
id: string;
|
|
94
99
|
timestamp: number;
|
|
95
|
-
gqlBody?: GQLRequestBody;
|
|
100
|
+
gqlBody?: GQLRequestBody | undefined;
|
|
96
101
|
};
|
|
97
102
|
|
|
98
103
|
export type Response = {
|
|
@@ -104,7 +109,7 @@ export type Response = {
|
|
|
104
109
|
|
|
105
110
|
export type LogEntry = {
|
|
106
111
|
originalRequest: Request;
|
|
107
|
-
labels?: string[];
|
|
112
|
+
labels?: string[] | undefined;
|
|
108
113
|
mockRuleId?: string;
|
|
109
114
|
originalResponse?: Response;
|
|
110
115
|
modifiedRequest?: Request;
|
|
@@ -117,15 +122,29 @@ export type ResponseManipulationFn = (request: Request, response: Response) => R
|
|
|
117
122
|
export type ResponseGenerationFn = (request: Request) => Response;
|
|
118
123
|
|
|
119
124
|
export type Actions =
|
|
125
|
+
| {
|
|
126
|
+
proxyPass: true;
|
|
127
|
+
mockResponse?: undefined;
|
|
128
|
+
modifyRequest?: undefined;
|
|
129
|
+
modifyResponse?: undefined;
|
|
130
|
+
}
|
|
120
131
|
| {
|
|
121
132
|
mockResponse: Response | ResponseGenerationFn;
|
|
133
|
+
proxyPass?: undefined;
|
|
122
134
|
modifyRequest?: undefined;
|
|
123
135
|
modifyResponse?: undefined;
|
|
124
136
|
}
|
|
125
137
|
| {
|
|
138
|
+
modifyRequest: RequestManipulationFn;
|
|
139
|
+
modifyResponse?: ResponseManipulationFn;
|
|
140
|
+
proxyPass?: true | undefined;
|
|
126
141
|
mockResponse?: undefined;
|
|
142
|
+
}
|
|
143
|
+
| {
|
|
127
144
|
modifyRequest?: RequestManipulationFn;
|
|
128
|
-
modifyResponse
|
|
145
|
+
modifyResponse: ResponseManipulationFn;
|
|
146
|
+
proxyPass?: true | undefined;
|
|
147
|
+
mockResponse?: undefined;
|
|
129
148
|
};
|
|
130
149
|
|
|
131
150
|
export type Rule = {
|
|
@@ -133,7 +152,7 @@ export type Rule = {
|
|
|
133
152
|
priority?: number;
|
|
134
153
|
matches: RuleMatchFunction; // function for picking request to process
|
|
135
154
|
labels?: string[]; // groupping req/res pairs for searching later e.g. ['exoclick', 'testId123']
|
|
136
|
-
actions
|
|
155
|
+
actions: Actions;
|
|
137
156
|
disableAfterUse?: boolean | number; // disable after rule is triggered n-times
|
|
138
157
|
removeAfterUse?: boolean | number; // disable after rule is triggered n-times
|
|
139
158
|
ttlSeconds: number;
|
|
@@ -171,10 +190,11 @@ export type ApiConfig = {
|
|
|
171
190
|
};
|
|
172
191
|
|
|
173
192
|
export type ClientConfig = {
|
|
174
|
-
protocol
|
|
175
|
-
host
|
|
176
|
-
port
|
|
177
|
-
timeout
|
|
193
|
+
protocol: 'http' | 'https';
|
|
194
|
+
host: string;
|
|
195
|
+
port: number;
|
|
196
|
+
timeout: number;
|
|
197
|
+
apiKey?: string;
|
|
178
198
|
};
|
|
179
199
|
|
|
180
200
|
export type MockConfig = {
|
|
@@ -186,6 +206,7 @@ export type MockConfig = {
|
|
|
186
206
|
timeout: number;
|
|
187
207
|
externalDns: string[];
|
|
188
208
|
rulesPath: string;
|
|
209
|
+
disableProxy?: boolean;
|
|
189
210
|
};
|
|
190
211
|
|
|
191
212
|
export type StorageConfig = {
|
|
@@ -194,13 +215,14 @@ export type StorageConfig = {
|
|
|
194
215
|
ttl: number;
|
|
195
216
|
};
|
|
196
217
|
|
|
197
|
-
export type
|
|
218
|
+
export type Config = {
|
|
198
219
|
webgui: WebGuiConfig;
|
|
199
220
|
api: ApiConfig;
|
|
200
221
|
mock: MockConfig;
|
|
201
222
|
storage: {
|
|
202
223
|
traffic: StorageConfig;
|
|
203
224
|
};
|
|
225
|
+
client: ClientConfig;
|
|
204
226
|
};
|
|
205
227
|
|
|
206
228
|
export interface RuleExecutorInterface {
|
package/src/rawHeaders.ts
CHANGED
|
@@ -5,10 +5,10 @@ export class RawHeaders extends Array<string> implements Stuntman.RawHeadersInte
|
|
|
5
5
|
const headers = this.toHeaderPairs();
|
|
6
6
|
const matchingHeaders = headers.filter((h) => h[0].toLowerCase() === name.toLowerCase());
|
|
7
7
|
if (matchingHeaders.length === 0) {
|
|
8
|
-
return;
|
|
8
|
+
return undefined;
|
|
9
9
|
}
|
|
10
10
|
if (matchingHeaders.length === 1) {
|
|
11
|
-
return matchingHeaders[0][1];
|
|
11
|
+
return matchingHeaders[0]?.[1];
|
|
12
12
|
}
|
|
13
13
|
throw new Error('Multiple headers with same name. Manipulate rawHeaders instead');
|
|
14
14
|
}
|
|
@@ -24,13 +24,14 @@ export class RawHeaders extends Array<string> implements Stuntman.RawHeadersInte
|
|
|
24
24
|
set(name: string, value: string): void {
|
|
25
25
|
let foundHeaders = 0;
|
|
26
26
|
for (let headerIndex = 0; headerIndex < this.length; headerIndex += 2) {
|
|
27
|
-
if (this[headerIndex]
|
|
27
|
+
if (this[headerIndex]?.toLowerCase() === name.toLowerCase()) {
|
|
28
28
|
this[headerIndex + 1] = value;
|
|
29
29
|
++foundHeaders;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
if (foundHeaders === 0) {
|
|
33
|
-
|
|
33
|
+
this.add(name, value);
|
|
34
|
+
return;
|
|
34
35
|
}
|
|
35
36
|
if (foundHeaders > 1) {
|
|
36
37
|
throw new Error('Multiple headers with same name. Manipulate rawHeaders instead');
|
|
@@ -46,7 +47,7 @@ export class RawHeaders extends Array<string> implements Stuntman.RawHeadersInte
|
|
|
46
47
|
const headersCopy = [...this];
|
|
47
48
|
let foundHeaders = 0;
|
|
48
49
|
for (let headerIndex = 0; headerIndex < headersCopy.length; headerIndex += 2) {
|
|
49
|
-
if (this[headerIndex - foundHeaders * 2]
|
|
50
|
+
if (this[headerIndex - foundHeaders * 2]?.toLowerCase() === name.toLowerCase()) {
|
|
50
51
|
delete this[headerIndex];
|
|
51
52
|
delete this[headerIndex];
|
|
52
53
|
++foundHeaders;
|
|
@@ -82,7 +83,7 @@ export class RawHeaders extends Array<string> implements Stuntman.RawHeadersInte
|
|
|
82
83
|
static toHeaderPairs(rawHeaders: string[]): readonly [string, string][] {
|
|
83
84
|
const headers = new Array<[string, string]>();
|
|
84
85
|
for (let headerIndex = 0; headerIndex < rawHeaders.length; headerIndex += 2) {
|
|
85
|
-
headers.push([rawHeaders[headerIndex]
|
|
86
|
+
headers.push([rawHeaders[headerIndex]!, rawHeaders[headerIndex + 1]!]);
|
|
86
87
|
}
|
|
87
88
|
return headers;
|
|
88
89
|
}
|
package/src/stringify.ts
CHANGED
package/dist/appError.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type * as Stuntman from '.';
|
|
2
|
-
export interface AppErrorInterface {
|
|
3
|
-
name?: string;
|
|
4
|
-
httpCode: Stuntman.HttpCode;
|
|
5
|
-
message: string;
|
|
6
|
-
isOperational?: boolean;
|
|
7
|
-
}
|
|
8
|
-
export declare class AppError extends Error {
|
|
9
|
-
readonly name: string;
|
|
10
|
-
readonly httpCode: Stuntman.HttpCode;
|
|
11
|
-
readonly uuid?: string;
|
|
12
|
-
readonly isOperational: boolean;
|
|
13
|
-
constructor(args: AppErrorInterface);
|
|
14
|
-
}
|
package/dist/appError.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AppError = void 0;
|
|
4
|
-
class AppError extends Error {
|
|
5
|
-
constructor(args) {
|
|
6
|
-
super(args.message);
|
|
7
|
-
this.isOperational = true;
|
|
8
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
9
|
-
this.name = args.name || 'Error';
|
|
10
|
-
this.httpCode = args.httpCode;
|
|
11
|
-
if (args.isOperational !== undefined) {
|
|
12
|
-
this.isOperational = args.isOperational;
|
|
13
|
-
}
|
|
14
|
-
Error.captureStackTrace(this);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
exports.AppError = AppError;
|
package/dist/config.d.ts
DELETED
package/dist/config.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.serverConfig = void 0;
|
|
7
|
-
const _1 = require(".");
|
|
8
|
-
const config_1 = __importDefault(require("config"));
|
|
9
|
-
const path_1 = __importDefault(require("path"));
|
|
10
|
-
// TODO safeguards & defaults
|
|
11
|
-
const defaultConfig = {
|
|
12
|
-
api: {
|
|
13
|
-
disabled: false,
|
|
14
|
-
port: _1.DEFAULT_API_PORT,
|
|
15
|
-
apiKeyReadOnly: null,
|
|
16
|
-
apiKeyReadWrite: null,
|
|
17
|
-
},
|
|
18
|
-
mock: {
|
|
19
|
-
domain: _1.DEFAULT_MOCK_DOMAIN,
|
|
20
|
-
externalDns: _1.EXTERNAL_DNS,
|
|
21
|
-
port: _1.DEFAULT_MOCK_PORT,
|
|
22
|
-
timeout: _1.DEFAULT_PROXY_TIMEOUT,
|
|
23
|
-
rulesPath: path_1.default.join(process.cwd(), 'rules'),
|
|
24
|
-
},
|
|
25
|
-
storage: {
|
|
26
|
-
traffic: {
|
|
27
|
-
limitCount: _1.DEFAULT_CACHE_MAX_ENTRIES,
|
|
28
|
-
limitSize: _1.DEFAULT_CACHE_MAX_SIZE,
|
|
29
|
-
ttl: _1.DEFAULT_CACHE_TTL,
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
webgui: {
|
|
33
|
-
disabled: false,
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
config_1.default.util.setModuleDefaults('stuntman', defaultConfig);
|
|
37
|
-
let configFromFile = {};
|
|
38
|
-
try {
|
|
39
|
-
configFromFile = config_1.default.get('stuntman');
|
|
40
|
-
}
|
|
41
|
-
catch (error) {
|
|
42
|
-
// eslint-disable-next-line no-console
|
|
43
|
-
console.warn('unable to find correct config - starting with defaults');
|
|
44
|
-
}
|
|
45
|
-
exports.serverConfig = configFromFile;
|
package/dist/constants.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export declare const DEFAULT_PROXY_TIMEOUT = 60000;
|
|
2
|
-
export declare const DEFAULT_RULE_PRIORITY = 100;
|
|
3
|
-
export declare const DEFAULT_MOCK_PORT = 2015;
|
|
4
|
-
export declare const DEFAULT_API_PORT = 1985;
|
|
5
|
-
export declare const DEFAULT_CACHE_MAX_ENTRIES = 500;
|
|
6
|
-
export declare const DEFAULT_CACHE_MAX_SIZE: number;
|
|
7
|
-
export declare const DEFAULT_CACHE_TTL: number;
|
|
8
|
-
export declare const DEFAULT_MOCK_DOMAIN = "stuntman";
|
|
9
|
-
export declare const DEFAULT_RULE_TTL_SECONDS: number;
|
|
10
|
-
export declare const MIN_RULE_TTL_SECONDS = 10;
|
|
11
|
-
export declare const MAX_RULE_TTL_SECONDS: number;
|
|
12
|
-
export declare const CATCH_ALL_RULE_PRIORITY: number;
|
|
13
|
-
export declare const CATCH_RULE_NAME = "internal/catch-all";
|
|
14
|
-
export declare const EXTERNAL_DNS: string[];
|
package/dist/constants.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EXTERNAL_DNS = exports.CATCH_RULE_NAME = exports.CATCH_ALL_RULE_PRIORITY = exports.MAX_RULE_TTL_SECONDS = exports.MIN_RULE_TTL_SECONDS = exports.DEFAULT_RULE_TTL_SECONDS = exports.DEFAULT_MOCK_DOMAIN = exports.DEFAULT_CACHE_TTL = exports.DEFAULT_CACHE_MAX_SIZE = exports.DEFAULT_CACHE_MAX_ENTRIES = exports.DEFAULT_API_PORT = exports.DEFAULT_MOCK_PORT = exports.DEFAULT_RULE_PRIORITY = exports.DEFAULT_PROXY_TIMEOUT = void 0;
|
|
4
|
-
exports.DEFAULT_PROXY_TIMEOUT = 60000;
|
|
5
|
-
exports.DEFAULT_RULE_PRIORITY = 100;
|
|
6
|
-
exports.DEFAULT_MOCK_PORT = 2015;
|
|
7
|
-
exports.DEFAULT_API_PORT = 1985;
|
|
8
|
-
exports.DEFAULT_CACHE_MAX_ENTRIES = 500;
|
|
9
|
-
exports.DEFAULT_CACHE_MAX_SIZE = 500 * 1024 * 1024;
|
|
10
|
-
exports.DEFAULT_CACHE_TTL = 1000 * 60 * 60;
|
|
11
|
-
exports.DEFAULT_MOCK_DOMAIN = 'stuntman';
|
|
12
|
-
exports.DEFAULT_RULE_TTL_SECONDS = 60 * 10;
|
|
13
|
-
exports.MIN_RULE_TTL_SECONDS = 10;
|
|
14
|
-
exports.MAX_RULE_TTL_SECONDS = 60 * 60;
|
|
15
|
-
exports.CATCH_ALL_RULE_PRIORITY = Infinity;
|
|
16
|
-
exports.CATCH_RULE_NAME = 'internal/catch-all';
|
|
17
|
-
exports.EXTERNAL_DNS = ['8.8.8.8', '1.1.1.1'];
|
package/dist/index.d.ts
DELETED
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
export * from './constants';
|
|
2
|
-
export * from './appError';
|
|
3
|
-
export * from './logger';
|
|
4
|
-
export * from './stringify';
|
|
5
|
-
export * from './rawHeaders';
|
|
6
|
-
export * from './config';
|
|
7
|
-
export declare const INDEX_DTS: string;
|
|
8
|
-
type NonObject = string | number | boolean | symbol | undefined | null | any[];
|
|
9
|
-
interface SerializableTypesRecord<T> {
|
|
10
|
-
[k: string | number]: T;
|
|
11
|
-
}
|
|
12
|
-
export type RecursivePartial<T> = {
|
|
13
|
-
[P in keyof T]?: T[P] extends (infer U)[] ? RecursivePartial<U>[] : T[P] extends object ? RecursivePartial<T[P]> : T[P];
|
|
14
|
-
};
|
|
15
|
-
type SerializableTypes = string | number | boolean | undefined | null | RegExp | SerializableTypes[] | SerializableTypesRecord<SerializableTypes>;
|
|
16
|
-
export declare enum HttpCode {
|
|
17
|
-
OK = 200,
|
|
18
|
-
NO_CONTENT = 204,
|
|
19
|
-
BAD_REQUEST = 400,
|
|
20
|
-
UNAUTHORIZED = 401,
|
|
21
|
-
NOT_FOUND = 404,
|
|
22
|
-
CONFLICT = 409,
|
|
23
|
-
UNPROCESSABLE_ENTITY = 422,
|
|
24
|
-
INTERNAL_SERVER_ERROR = 500
|
|
25
|
-
}
|
|
26
|
-
export type LocalVariables = Record<string, SerializableTypes>;
|
|
27
|
-
export type RuleMatchResult = boolean | {
|
|
28
|
-
result: boolean;
|
|
29
|
-
enableRuleIds?: string[];
|
|
30
|
-
disableRuleIds?: string[];
|
|
31
|
-
description?: string;
|
|
32
|
-
};
|
|
33
|
-
export type RemotableFunction<T extends Function> = {
|
|
34
|
-
localFn: T;
|
|
35
|
-
localVariables?: LocalVariables;
|
|
36
|
-
};
|
|
37
|
-
export type SerializedRemotableFunction = {
|
|
38
|
-
localFn: string;
|
|
39
|
-
variable?: string;
|
|
40
|
-
remoteFn: string;
|
|
41
|
-
};
|
|
42
|
-
type WithRemotableFunctions<Type> = {
|
|
43
|
-
[PropertyKey in keyof Type]: Extract<Type[PropertyKey], Function> extends never ? Exclude<Type[PropertyKey], NonObject> extends never ? Type[PropertyKey] : WithRemotableFunctions<Exclude<Type[PropertyKey], NonObject>> : Exclude<Type[PropertyKey], Function> | RemotableFunction<Extract<Type[PropertyKey], Function>>;
|
|
44
|
-
};
|
|
45
|
-
export type WithSerializedFunctions<Type> = {
|
|
46
|
-
[PropertyKey in keyof Type]: Extract<Type[PropertyKey], RemotableFunction<Function>> extends never ? Exclude<Type[PropertyKey], NonObject> extends never ? Type[PropertyKey] : WithSerializedFunctions<Exclude<Type[PropertyKey], NonObject>> : Exclude<Type[PropertyKey], RemotableFunction<Function>> | SerializedRemotableFunction;
|
|
47
|
-
};
|
|
48
|
-
export interface RawHeadersInterface extends Array<string> {
|
|
49
|
-
get: (name: string) => string | undefined;
|
|
50
|
-
set: (name: string, value: string) => void;
|
|
51
|
-
has: (name: string, value?: string) => boolean;
|
|
52
|
-
add: (name: string, value: string) => void;
|
|
53
|
-
remove: (name: string) => void;
|
|
54
|
-
toHeaderPairs: () => readonly [string, string][];
|
|
55
|
-
}
|
|
56
|
-
export type BaseRequest = {
|
|
57
|
-
rawHeaders: RawHeadersInterface;
|
|
58
|
-
url: string;
|
|
59
|
-
body?: any;
|
|
60
|
-
method: string;
|
|
61
|
-
};
|
|
62
|
-
export type Request = BaseRequest & {
|
|
63
|
-
id: string;
|
|
64
|
-
timestamp: number;
|
|
65
|
-
gqlBody?: GQLRequestBody;
|
|
66
|
-
};
|
|
67
|
-
export type Response = {
|
|
68
|
-
rawHeaders?: RawHeadersInterface;
|
|
69
|
-
status?: number;
|
|
70
|
-
body?: any;
|
|
71
|
-
timestamp?: number;
|
|
72
|
-
};
|
|
73
|
-
export type LogEntry = {
|
|
74
|
-
originalRequest: Request;
|
|
75
|
-
labels?: string[];
|
|
76
|
-
mockRuleId?: string;
|
|
77
|
-
originalResponse?: Response;
|
|
78
|
-
modifiedRequest?: Request;
|
|
79
|
-
modifiedResponse?: Response;
|
|
80
|
-
};
|
|
81
|
-
export type RuleMatchFunction = (request: Request) => RuleMatchResult;
|
|
82
|
-
export type RequestManipulationFn = (request: Request) => Request;
|
|
83
|
-
export type ResponseManipulationFn = (request: Request, response: Response) => Response;
|
|
84
|
-
export type ResponseGenerationFn = (request: Request) => Response;
|
|
85
|
-
export type Actions = {
|
|
86
|
-
mockResponse: Response | ResponseGenerationFn;
|
|
87
|
-
modifyRequest?: undefined;
|
|
88
|
-
modifyResponse?: undefined;
|
|
89
|
-
} | {
|
|
90
|
-
mockResponse?: undefined;
|
|
91
|
-
modifyRequest?: RequestManipulationFn;
|
|
92
|
-
modifyResponse?: ResponseManipulationFn;
|
|
93
|
-
};
|
|
94
|
-
export type Rule = {
|
|
95
|
-
id: string;
|
|
96
|
-
priority?: number;
|
|
97
|
-
matches: RuleMatchFunction;
|
|
98
|
-
labels?: string[];
|
|
99
|
-
actions?: Actions;
|
|
100
|
-
disableAfterUse?: boolean | number;
|
|
101
|
-
removeAfterUse?: boolean | number;
|
|
102
|
-
ttlSeconds: number;
|
|
103
|
-
storeTraffic?: boolean;
|
|
104
|
-
isEnabled?: boolean;
|
|
105
|
-
};
|
|
106
|
-
export type DeployedRule = Omit<Rule, 'disableAfterUse' | 'removeAfterUse' | 'ttlSeconds'>;
|
|
107
|
-
export type SerializableRule = WithRemotableFunctions<Rule>;
|
|
108
|
-
export type SerializedRule = WithSerializedFunctions<SerializableRule>;
|
|
109
|
-
export type LiveRule = Rule & {
|
|
110
|
-
counter: number;
|
|
111
|
-
createdTimestamp: number;
|
|
112
|
-
};
|
|
113
|
-
export type GQLRequestBody = {
|
|
114
|
-
operationName: string;
|
|
115
|
-
variables?: any;
|
|
116
|
-
query: string;
|
|
117
|
-
type: 'query' | 'mutation';
|
|
118
|
-
methodName?: string;
|
|
119
|
-
};
|
|
120
|
-
export type WebGuiConfig = {
|
|
121
|
-
disabled: boolean;
|
|
122
|
-
};
|
|
123
|
-
export type ApiConfig = {
|
|
124
|
-
port: number;
|
|
125
|
-
disabled: boolean;
|
|
126
|
-
apiKeyReadWrite: string | null;
|
|
127
|
-
apiKeyReadOnly: string | null;
|
|
128
|
-
};
|
|
129
|
-
export type ClientConfig = {
|
|
130
|
-
protocol?: 'http' | 'https';
|
|
131
|
-
host?: string;
|
|
132
|
-
port?: number;
|
|
133
|
-
timeout?: number;
|
|
134
|
-
};
|
|
135
|
-
export type MockConfig = {
|
|
136
|
-
domain: string;
|
|
137
|
-
port: number;
|
|
138
|
-
httpsPort?: number;
|
|
139
|
-
httpsKey?: string;
|
|
140
|
-
httpsCert?: string;
|
|
141
|
-
timeout: number;
|
|
142
|
-
externalDns: string[];
|
|
143
|
-
rulesPath: string;
|
|
144
|
-
};
|
|
145
|
-
export type StorageConfig = {
|
|
146
|
-
limitCount: number;
|
|
147
|
-
limitSize: number;
|
|
148
|
-
ttl: number;
|
|
149
|
-
};
|
|
150
|
-
export type ServerConfig = {
|
|
151
|
-
webgui: WebGuiConfig;
|
|
152
|
-
api: ApiConfig;
|
|
153
|
-
mock: MockConfig;
|
|
154
|
-
storage: {
|
|
155
|
-
traffic: StorageConfig;
|
|
156
|
-
};
|
|
157
|
-
};
|
|
158
|
-
export interface RuleExecutorInterface {
|
|
159
|
-
addRule: (rule: Rule, overwrite?: boolean) => Promise<LiveRule>;
|
|
160
|
-
removeRule: (id: string) => Promise<void>;
|
|
161
|
-
enableRule: (id: string) => void;
|
|
162
|
-
disableRule: (id: string) => void;
|
|
163
|
-
findMatchingRule: (request: Request) => Promise<LiveRule | null>;
|
|
164
|
-
getRules: () => Promise<readonly LiveRule[]>;
|
|
165
|
-
getRule: (id: string) => Promise<LiveRule | undefined>;
|
|
166
|
-
}
|
package/dist/index.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
|
-
};
|
|
19
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.HttpCode = exports.INDEX_DTS = void 0;
|
|
21
|
-
/* eslint-disable @typescript-eslint/ban-types */
|
|
22
|
-
__exportStar(require("./constants"), exports);
|
|
23
|
-
__exportStar(require("./appError"), exports);
|
|
24
|
-
__exportStar(require("./logger"), exports);
|
|
25
|
-
__exportStar(require("./stringify"), exports);
|
|
26
|
-
__exportStar(require("./rawHeaders"), exports);
|
|
27
|
-
__exportStar(require("./config"), exports);
|
|
28
|
-
const fs_1 = __importDefault(require("fs"));
|
|
29
|
-
exports.INDEX_DTS = fs_1.default.readFileSync(`${__dirname}/index.d.ts`, 'utf-8');
|
|
30
|
-
var HttpCode;
|
|
31
|
-
(function (HttpCode) {
|
|
32
|
-
HttpCode[HttpCode["OK"] = 200] = "OK";
|
|
33
|
-
HttpCode[HttpCode["NO_CONTENT"] = 204] = "NO_CONTENT";
|
|
34
|
-
HttpCode[HttpCode["BAD_REQUEST"] = 400] = "BAD_REQUEST";
|
|
35
|
-
HttpCode[HttpCode["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
|
|
36
|
-
HttpCode[HttpCode["NOT_FOUND"] = 404] = "NOT_FOUND";
|
|
37
|
-
HttpCode[HttpCode["CONFLICT"] = 409] = "CONFLICT";
|
|
38
|
-
HttpCode[HttpCode["UNPROCESSABLE_ENTITY"] = 422] = "UNPROCESSABLE_ENTITY";
|
|
39
|
-
HttpCode[HttpCode["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR";
|
|
40
|
-
})(HttpCode = exports.HttpCode || (exports.HttpCode = {}));
|
package/dist/logger.d.ts
DELETED
package/dist/logger.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
var _a;
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.logger = void 0;
|
|
8
|
-
const pino_1 = __importDefault(require("pino"));
|
|
9
|
-
exports.logger = (0, pino_1.default)({
|
|
10
|
-
level: (_a = process.env.LOG_LEVEL) !== null && _a !== void 0 ? _a : 'info',
|
|
11
|
-
messageKey: 'message',
|
|
12
|
-
formatters: {
|
|
13
|
-
level(label) {
|
|
14
|
-
return { level: label };
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
});
|
package/dist/rawHeaders.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type * as Stuntman from '.';
|
|
2
|
-
export declare class RawHeaders extends Array<string> implements Stuntman.RawHeadersInterface {
|
|
3
|
-
get(name: string): string | undefined;
|
|
4
|
-
has(name: string, value?: string): boolean;
|
|
5
|
-
set(name: string, value: string): void;
|
|
6
|
-
add(name: string, value: string): void;
|
|
7
|
-
remove(name: string): void;
|
|
8
|
-
toHeaderPairs(): readonly [string, string][];
|
|
9
|
-
static fromHeaderPairs(headerPairs: [string, string][]): RawHeaders;
|
|
10
|
-
static fromHeadersRecord(headersRecord: Record<string, string | string[] | undefined>): RawHeaders;
|
|
11
|
-
static toHeaderPairs(rawHeaders: string[]): readonly [string, string][];
|
|
12
|
-
}
|
package/dist/rawHeaders.js
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RawHeaders = void 0;
|
|
4
|
-
class RawHeaders extends Array {
|
|
5
|
-
get(name) {
|
|
6
|
-
const headers = this.toHeaderPairs();
|
|
7
|
-
const matchingHeaders = headers.filter((h) => h[0].toLowerCase() === name.toLowerCase());
|
|
8
|
-
if (matchingHeaders.length === 0) {
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
if (matchingHeaders.length === 1) {
|
|
12
|
-
return matchingHeaders[0][1];
|
|
13
|
-
}
|
|
14
|
-
throw new Error('Multiple headers with same name. Manipulate rawHeaders instead');
|
|
15
|
-
}
|
|
16
|
-
has(name, value) {
|
|
17
|
-
const foundValue = this.get(name);
|
|
18
|
-
if (value === undefined) {
|
|
19
|
-
return foundValue !== undefined;
|
|
20
|
-
}
|
|
21
|
-
return foundValue === value;
|
|
22
|
-
}
|
|
23
|
-
set(name, value) {
|
|
24
|
-
let foundHeaders = 0;
|
|
25
|
-
for (let headerIndex = 0; headerIndex < this.length; headerIndex += 2) {
|
|
26
|
-
if (this[headerIndex].toLowerCase() === name.toLowerCase()) {
|
|
27
|
-
this[headerIndex + 1] = value;
|
|
28
|
-
++foundHeaders;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
if (foundHeaders === 0) {
|
|
32
|
-
return this.add(name, value);
|
|
33
|
-
}
|
|
34
|
-
if (foundHeaders > 1) {
|
|
35
|
-
throw new Error('Multiple headers with same name. Manipulate rawHeaders instead');
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
add(name, value) {
|
|
39
|
-
this.push(name);
|
|
40
|
-
this.push(value);
|
|
41
|
-
}
|
|
42
|
-
remove(name) {
|
|
43
|
-
const headersCopy = [...this];
|
|
44
|
-
let foundHeaders = 0;
|
|
45
|
-
for (let headerIndex = 0; headerIndex < headersCopy.length; headerIndex += 2) {
|
|
46
|
-
if (this[headerIndex - foundHeaders * 2].toLowerCase() === name.toLowerCase()) {
|
|
47
|
-
delete this[headerIndex];
|
|
48
|
-
delete this[headerIndex];
|
|
49
|
-
++foundHeaders;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
if (foundHeaders > 1) {
|
|
53
|
-
throw new Error('Multiple headers with same name. Manipulate rawHeaders instead');
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
toHeaderPairs() {
|
|
57
|
-
return RawHeaders.toHeaderPairs(this);
|
|
58
|
-
}
|
|
59
|
-
static fromHeaderPairs(headerPairs) {
|
|
60
|
-
return new RawHeaders(...headerPairs.flatMap((x) => x));
|
|
61
|
-
}
|
|
62
|
-
static fromHeadersRecord(headersRecord) {
|
|
63
|
-
const output = new RawHeaders();
|
|
64
|
-
for (const [key, value] of Object.entries(headersRecord)) {
|
|
65
|
-
if (typeof value === 'string' || value === undefined) {
|
|
66
|
-
output.add(key, value !== null && value !== void 0 ? value : '');
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
for (const subValue of value) {
|
|
70
|
-
output.add(key, subValue);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return output;
|
|
74
|
-
}
|
|
75
|
-
static toHeaderPairs(rawHeaders) {
|
|
76
|
-
const headers = new Array();
|
|
77
|
-
for (let headerIndex = 0; headerIndex < rawHeaders.length; headerIndex += 2) {
|
|
78
|
-
headers.push([rawHeaders[headerIndex], rawHeaders[headerIndex + 1]]);
|
|
79
|
-
}
|
|
80
|
-
return headers;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
exports.RawHeaders = RawHeaders;
|
package/dist/stringify.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const stringify: (obj: any) => string;
|
package/dist/stringify.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.stringify = void 0;
|
|
4
|
-
const stringify = (obj) => JSON.stringify(obj, (key, value) => {
|
|
5
|
-
if (typeof value === 'function' || value instanceof RegExp) {
|
|
6
|
-
return value.toString();
|
|
7
|
-
}
|
|
8
|
-
return value;
|
|
9
|
-
});
|
|
10
|
-
exports.stringify = stringify;
|