@stuntman/server 0.1.6 → 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/dist/mock.js DELETED
@@ -1,327 +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.Mock = void 0;
7
- const undici_1 = require("undici");
8
- const https_1 = __importDefault(require("https"));
9
- const express_1 = __importDefault(require("express"));
10
- const uuid_1 = require("uuid");
11
- const ruleExecutor_1 = require("./ruleExecutor");
12
- const storage_1 = require("./storage");
13
- const shared_1 = require("@stuntman/shared");
14
- const requestContext_1 = __importDefault(require("./requestContext"));
15
- const ipUtils_1 = require("./ipUtils");
16
- const api_1 = require("./api/api");
17
- const naiveGQLParser = (body) => {
18
- try {
19
- let json = undefined;
20
- try {
21
- json = JSON.parse(Buffer.isBuffer(body) ? body.toString('utf-8') : body);
22
- }
23
- catch (kiss) {
24
- // and swallow
25
- }
26
- if (!(json === null || json === void 0 ? void 0 : json.query) && !(json === null || json === void 0 ? void 0 : json.operationName)) {
27
- return;
28
- }
29
- const lines = json.query
30
- .split('\n')
31
- .map((l) => l.replace(/^\s+/g, '').trim())
32
- .filter((l) => !!l);
33
- if (/^query /.test(lines[0])) {
34
- json.type = 'query';
35
- }
36
- else if (/^mutation /.test(lines[0])) {
37
- json.type = 'mutation';
38
- }
39
- else {
40
- throw new Error(`Unable to resolve query type of ${lines[0]}`);
41
- }
42
- json.methodName = lines[json.operationName ? 1 : 0].split('(')[0].split('{')[0];
43
- return json;
44
- }
45
- catch (error) {
46
- shared_1.logger.debug(error, 'unable to parse GQL');
47
- }
48
- return undefined;
49
- };
50
- // TODO add proper web proxy mode
51
- class Mock {
52
- get apiServer() {
53
- if (this.options.api.disabled) {
54
- return null;
55
- }
56
- if (!this._api) {
57
- this._api = new api_1.API({ ...this.options.api, mockUuid: this.mockUuid }, this.options.webgui);
58
- }
59
- return this._api;
60
- }
61
- get ruleExecutor() {
62
- return (0, ruleExecutor_1.getRuleExecutor)(this.mockUuid);
63
- }
64
- constructor(options) {
65
- this.server = null;
66
- this.serverHttps = null;
67
- this.ipUtils = null;
68
- this._api = null;
69
- this.mockUuid = (0, uuid_1.v4)();
70
- this.options = options;
71
- if (this.options.mock.httpsPort && (!this.options.mock.httpsKey || !this.options.mock.httpsCert)) {
72
- throw new Error('missing https key/cert');
73
- }
74
- this.MOCK_DOMAIN_REGEX = new RegExp(`(?:\\.([0-9]+))?\\.(?:(?:${this.options.mock.domain}(https?)?)|(?:localhost))(:${this.options.mock.port}${this.options.mock.httpsPort ? `|${this.options.mock.httpsPort}` : ''})?(?:\\b|$)`, 'i');
75
- this.URL_PORT_REGEX = new RegExp(`^(https?:\\/\\/[^:/]+):(?:${this.options.mock.port}${this.options.mock.httpsPort ? `|${this.options.mock.httpsPort}` : ''})(\\/.*)`, 'i');
76
- this.trafficStore = (0, storage_1.getTrafficStore)(this.mockUuid, this.options.storage.traffic);
77
- this.ipUtils =
78
- !this.options.mock.externalDns || this.options.mock.externalDns.length === 0
79
- ? null
80
- : new ipUtils_1.IPUtils({ mockUuid: this.mockUuid, externalDns: this.options.mock.externalDns });
81
- this.requestHandler = async (req, res) => {
82
- const ctx = requestContext_1.default.get(req);
83
- const requestUuid = (ctx === null || ctx === void 0 ? void 0 : ctx.uuid) || (0, uuid_1.v4)();
84
- const timestamp = Date.now();
85
- const originalHostname = req.headers.host || req.hostname;
86
- const unproxiedHostname = req.hostname.replace(this.MOCK_DOMAIN_REGEX, '');
87
- const isProxiedHostname = originalHostname !== unproxiedHostname;
88
- const originalRequest = {
89
- id: requestUuid,
90
- timestamp,
91
- url: `${req.protocol}://${req.hostname}${req.originalUrl}`,
92
- method: req.method,
93
- rawHeaders: new shared_1.RawHeaders(...req.rawHeaders),
94
- ...((Buffer.isBuffer(req.body) && { body: req.body.toString('utf-8') }) ||
95
- (typeof req.body === 'string' && { body: req.body })),
96
- };
97
- shared_1.logger.debug(originalRequest, 'processing request');
98
- const logContext = {
99
- requestId: originalRequest.id,
100
- };
101
- const mockEntry = {
102
- originalRequest,
103
- modifiedRequest: {
104
- ...this.unproxyRequest(req),
105
- id: requestUuid,
106
- timestamp,
107
- ...(originalRequest.body && { gqlBody: naiveGQLParser(originalRequest.body) }),
108
- },
109
- };
110
- if (!isProxiedHostname) {
111
- this.removeProxyPort(mockEntry.modifiedRequest);
112
- }
113
- const matchingRule = await (0, ruleExecutor_1.getRuleExecutor)(this.mockUuid).findMatchingRule(mockEntry.modifiedRequest);
114
- if (matchingRule) {
115
- mockEntry.mockRuleId = matchingRule.id;
116
- mockEntry.labels = matchingRule.labels;
117
- if (matchingRule.actions.mockResponse) {
118
- const staticResponse = typeof matchingRule.actions.mockResponse === 'function'
119
- ? matchingRule.actions.mockResponse(mockEntry.modifiedRequest)
120
- : matchingRule.actions.mockResponse;
121
- mockEntry.modifiedResponse = staticResponse;
122
- shared_1.logger.debug({ ...logContext, staticResponse }, 'replying with mocked response');
123
- if (matchingRule.storeTraffic) {
124
- this.trafficStore.set(requestUuid, mockEntry);
125
- }
126
- if (staticResponse.rawHeaders) {
127
- for (const header of staticResponse.rawHeaders.toHeaderPairs()) {
128
- res.setHeader(header[0], header[1]);
129
- }
130
- }
131
- res.status(staticResponse.status || 200);
132
- res.send(staticResponse.body);
133
- // static response blocks any further processing
134
- return;
135
- }
136
- if (matchingRule.actions.modifyRequest) {
137
- mockEntry.modifiedRequest = matchingRule.actions.modifyRequest(mockEntry.modifiedRequest);
138
- shared_1.logger.debug({ ...logContext, modifiedRequest: mockEntry.modifiedRequest }, 'modified original request');
139
- }
140
- }
141
- if (this.ipUtils && !isProxiedHostname && !this.ipUtils.isIP(originalHostname)) {
142
- const hostname = originalHostname.split(':')[0];
143
- try {
144
- const internalIPs = await this.ipUtils.resolveIP(hostname);
145
- if (this.ipUtils.isLocalhostIP(internalIPs) && this.options.mock.externalDns.length) {
146
- const externalIPs = await this.ipUtils.resolveIP(hostname, { useExternalDns: true });
147
- shared_1.logger.debug({ ...logContext, hostname, externalIPs, internalIPs }, 'switched to external IP');
148
- mockEntry.modifiedRequest.url = mockEntry.modifiedRequest.url.replace(/^(https?:\/\/)[^:/]+/i, `$1${externalIPs}`);
149
- }
150
- }
151
- catch (error) {
152
- // swallow the exeception, don't think much can be done at this point
153
- shared_1.logger.warn({ ...logContext, error }, `error trying to resolve IP for "${hostname}"`);
154
- }
155
- }
156
- const originalResponse = this.options.mock.disableProxy
157
- ? {
158
- timestamp: Date.now(),
159
- body: undefined,
160
- rawHeaders: new shared_1.RawHeaders(),
161
- status: 404,
162
- }
163
- : await this.proxyRequest(req, mockEntry, logContext);
164
- shared_1.logger.debug({ ...logContext, originalResponse }, 'received response');
165
- mockEntry.originalResponse = originalResponse;
166
- let modifedResponse = {
167
- ...originalResponse,
168
- rawHeaders: new shared_1.RawHeaders(...Array.from(originalResponse.rawHeaders.toHeaderPairs()).flatMap(([key, value]) => {
169
- // TODO this replace may be too aggressive and doesn't handle protocol (won't be necessary with a trusted cert and mock serving http+https)
170
- return [
171
- key,
172
- isProxiedHostname
173
- ? value
174
- : value.replace(new RegExp(`(?:^|\\b)(${unproxiedHostname.replace('.', '\\.')})(?:\\b|$)`, 'igm'), originalHostname),
175
- ];
176
- })),
177
- };
178
- if (matchingRule === null || matchingRule === void 0 ? void 0 : matchingRule.actions.modifyResponse) {
179
- modifedResponse = matchingRule === null || matchingRule === void 0 ? void 0 : matchingRule.actions.modifyResponse(mockEntry.modifiedRequest, originalResponse);
180
- shared_1.logger.debug({ ...logContext, modifedResponse }, 'modified response');
181
- }
182
- mockEntry.modifiedResponse = modifedResponse;
183
- if (matchingRule === null || matchingRule === void 0 ? void 0 : matchingRule.storeTraffic) {
184
- this.trafficStore.set(requestUuid, mockEntry);
185
- }
186
- if (modifedResponse.status) {
187
- res.status(modifedResponse.status);
188
- }
189
- if (modifedResponse.rawHeaders) {
190
- for (const header of modifedResponse.rawHeaders.toHeaderPairs()) {
191
- // since fetch decompresses responses we need to get rid of some headers
192
- // TODO maybe could be handled better than just skipping, although express should add these back for new body
193
- // if (/^content-(?:length|encoding)$/i.test(header[0])) {
194
- // continue;
195
- // }
196
- res.setHeader(header[0], isProxiedHostname ? header[1].replace(unproxiedHostname, originalHostname) : header[1]);
197
- }
198
- }
199
- res.end(Buffer.from(modifedResponse.body, 'binary'));
200
- };
201
- this.mockApp = (0, express_1.default)();
202
- // TODO for now request body is just a buffer passed further, not inflated
203
- this.mockApp.use(express_1.default.raw({ type: '*/*' }));
204
- this.mockApp.use((req, res, next) => {
205
- requestContext_1.default.bind(req, this.mockUuid);
206
- next();
207
- });
208
- this.mockApp.all(/.*/, this.requestHandler);
209
- this.mockApp.use((error, req, res) => {
210
- const ctx = requestContext_1.default.get(req);
211
- const uuid = (ctx === null || ctx === void 0 ? void 0 : ctx.uuid) || (0, uuid_1.v4)();
212
- shared_1.logger.error({ message: error.message, stack: error.stack, name: error.name, uuid }, 'unexpected error');
213
- if (res) {
214
- res.status(shared_1.HttpCode.INTERNAL_SERVER_ERROR).json({
215
- error: { message: error.message, httpCode: shared_1.HttpCode.INTERNAL_SERVER_ERROR, uuid },
216
- });
217
- return;
218
- }
219
- // eslint-disable-next-line no-console
220
- console.error('mock server encountered a critical error. exiting');
221
- process.exit(1);
222
- });
223
- }
224
- async proxyRequest(req, mockEntry, logContext) {
225
- let controller = new AbortController();
226
- const fetchTimeout = setTimeout(() => {
227
- if (controller) {
228
- controller.abort(`timeout after ${this.options.mock.timeout}`);
229
- }
230
- }, this.options.mock.timeout);
231
- req.on('close', () => {
232
- shared_1.logger.debug(logContext, 'remote client canceled the request');
233
- clearTimeout(fetchTimeout);
234
- if (controller) {
235
- controller.abort('remote client canceled the request');
236
- }
237
- });
238
- let targetResponse;
239
- try {
240
- const requestOptions = {
241
- headers: mockEntry.modifiedRequest.rawHeaders,
242
- body: mockEntry.modifiedRequest.body,
243
- method: mockEntry.modifiedRequest.method.toUpperCase(),
244
- };
245
- shared_1.logger.debug({
246
- ...logContext,
247
- url: mockEntry.modifiedRequest.url,
248
- ...requestOptions,
249
- }, 'outgoing request attempt');
250
- targetResponse = await (0, undici_1.request)(mockEntry.modifiedRequest.url, requestOptions);
251
- }
252
- catch (error) {
253
- shared_1.logger.error({ ...logContext, error, request: mockEntry.modifiedRequest }, 'error fetching');
254
- throw error;
255
- }
256
- finally {
257
- controller = null;
258
- clearTimeout(fetchTimeout);
259
- }
260
- const targetResponseBuffer = Buffer.from(await targetResponse.body.arrayBuffer());
261
- return {
262
- timestamp: Date.now(),
263
- body: targetResponseBuffer.toString('binary'),
264
- status: targetResponse.statusCode,
265
- rawHeaders: shared_1.RawHeaders.fromHeadersRecord(targetResponse.headers),
266
- };
267
- }
268
- start() {
269
- if (this.server) {
270
- throw new Error('mock server already started');
271
- }
272
- if (this.options.mock.httpsPort) {
273
- this.serverHttps = https_1.default
274
- .createServer({
275
- key: this.options.mock.httpsKey,
276
- cert: this.options.mock.httpsCert,
277
- }, this.mockApp)
278
- .listen(this.options.mock.httpsPort, () => {
279
- shared_1.logger.info(`Mock listening on ${this.options.mock.domain}:${this.options.mock.httpsPort}`);
280
- });
281
- }
282
- this.server = this.mockApp.listen(this.options.mock.port, () => {
283
- var _a;
284
- shared_1.logger.info(`Mock listening on ${this.options.mock.domain}:${this.options.mock.port}`);
285
- if (!this.options.api.disabled) {
286
- (_a = this.apiServer) === null || _a === void 0 ? void 0 : _a.start();
287
- }
288
- });
289
- }
290
- stop() {
291
- var _a;
292
- if (!this.server) {
293
- throw new Error('mock server not started');
294
- }
295
- if (!this.options.api.disabled) {
296
- (_a = this.apiServer) === null || _a === void 0 ? void 0 : _a.stop();
297
- }
298
- this.server.close((error) => {
299
- shared_1.logger.warn(error, 'problem closing server');
300
- this.server = null;
301
- });
302
- }
303
- unproxyRequest(req) {
304
- const protocol = (this.MOCK_DOMAIN_REGEX.exec(req.hostname) || [])[2] || req.protocol;
305
- const port = (this.MOCK_DOMAIN_REGEX.exec(req.hostname) || [])[1] || undefined;
306
- // TODO unproxied req might fail if there's a signed url :shrug:
307
- // but then we can probably switch DNS for some particular 3rd party server to point to mock
308
- // and in mock have a mapping rule for that domain to point directly to some IP :thinking:
309
- return {
310
- url: `${protocol}://${req.hostname.replace(this.MOCK_DOMAIN_REGEX, '')}${port ? `:${port}` : ''}${req.originalUrl}`,
311
- rawHeaders: new shared_1.RawHeaders(...req.rawHeaders.map((h) => h.replace(this.MOCK_DOMAIN_REGEX, ''))),
312
- method: req.method,
313
- ...(Buffer.isBuffer(req.body) && { body: req.body.toString('utf-8') }),
314
- };
315
- }
316
- removeProxyPort(req) {
317
- if (this.URL_PORT_REGEX.test(req.url)) {
318
- req.url = req.url.replace(this.URL_PORT_REGEX, '$1$2');
319
- }
320
- const host = req.rawHeaders.get('host') || '';
321
- if (host.endsWith(`:${this.options.mock.port}`) ||
322
- (this.options.mock.httpsPort && host.endsWith(`:${this.options.mock.httpsPort}`))) {
323
- req.rawHeaders.set('host', host.split(':')[0]);
324
- }
325
- }
326
- }
327
- exports.Mock = Mock;
@@ -1,9 +0,0 @@
1
- import type { Request } from 'express';
2
- export default class RequestContext {
3
- static _bindings: WeakMap<Request, RequestContext>;
4
- readonly mockUuid: string;
5
- readonly uuid: string;
6
- constructor(mockUuid: string);
7
- static bind(req: Request, mockUuid: string): void;
8
- static get(req: Request): RequestContext | null;
9
- }
@@ -1,18 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const uuid_1 = require("uuid");
4
- class RequestContext {
5
- constructor(mockUuid) {
6
- this.uuid = (0, uuid_1.v4)();
7
- this.mockUuid = mockUuid;
8
- }
9
- static bind(req, mockUuid) {
10
- const ctx = new RequestContext(mockUuid);
11
- RequestContext._bindings.set(req, ctx);
12
- }
13
- static get(req) {
14
- return RequestContext._bindings.get(req) || null;
15
- }
16
- }
17
- exports.default = RequestContext;
18
- RequestContext._bindings = new WeakMap();
@@ -1,22 +0,0 @@
1
- import type * as Stuntman from '@stuntman/shared';
2
- declare class RuleExecutor implements Stuntman.RuleExecutorInterface {
3
- private _rules;
4
- private rulesLock;
5
- private get enabledRules();
6
- constructor(rules?: Stuntman.Rule[]);
7
- private hasExpired;
8
- private cleanUpExpired;
9
- addRule(rule: Stuntman.Rule, overwrite?: boolean): Promise<Stuntman.LiveRule>;
10
- private _removeRule;
11
- removeRule(id: string): Promise<void>;
12
- removeRule(rule: Stuntman.Rule): Promise<void>;
13
- enableRule(id: string): void;
14
- enableRule(rule: Stuntman.Rule): void;
15
- disableRule(id: string): void;
16
- disableRule(rule: Stuntman.Rule): void;
17
- findMatchingRule(request: Stuntman.Request): Promise<Stuntman.LiveRule | null>;
18
- getRules(): Promise<readonly Stuntman.LiveRule[]>;
19
- getRule(id: string): Promise<Stuntman.LiveRule | undefined>;
20
- }
21
- export declare const getRuleExecutor: (mockUuid: string) => RuleExecutor;
22
- export {};
@@ -1,187 +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.getRuleExecutor = void 0;
7
- const await_lock_1 = __importDefault(require("await-lock"));
8
- const shared_1 = require("@stuntman/shared");
9
- const rules_1 = require("./rules");
10
- const ruleExecutors = {};
11
- const transformMockRuleToLive = (rule) => {
12
- var _a;
13
- return {
14
- ...rule,
15
- counter: 0,
16
- isEnabled: (_a = rule.isEnabled) !== null && _a !== void 0 ? _a : true,
17
- createdTimestamp: Date.now(),
18
- };
19
- };
20
- class RuleExecutor {
21
- get enabledRules() {
22
- if (!this._rules) {
23
- return new Array();
24
- }
25
- const now = Date.now();
26
- return this._rules
27
- .filter((r) => (r.isEnabled && !Number.isFinite(r.ttlSeconds)) || r.createdTimestamp + r.ttlSeconds * 1000 > now)
28
- .sort((a, b) => { var _a, _b; return ((_a = a.priority) !== null && _a !== void 0 ? _a : shared_1.DEFAULT_RULE_PRIORITY) - ((_b = b.priority) !== null && _b !== void 0 ? _b : shared_1.DEFAULT_RULE_PRIORITY); });
29
- }
30
- constructor(rules) {
31
- this.rulesLock = new await_lock_1.default();
32
- this._rules = (rules || []).map(transformMockRuleToLive);
33
- }
34
- hasExpired() {
35
- const now = Date.now();
36
- return this._rules.some((r) => Number.isFinite(r.ttlSeconds) && r.createdTimestamp + r.ttlSeconds * 1000 < now);
37
- }
38
- async cleanUpExpired() {
39
- if (!this.hasExpired()) {
40
- return;
41
- }
42
- await this.rulesLock.acquireAsync();
43
- const now = Date.now();
44
- try {
45
- this._rules = this._rules.filter((r) => {
46
- const shouldKeep = !Number.isFinite(r.ttlSeconds) || r.createdTimestamp + r.ttlSeconds * 1000 > now;
47
- if (!shouldKeep) {
48
- shared_1.logger.debug({ ruleId: r.id }, 'removing expired rule');
49
- }
50
- return shouldKeep;
51
- });
52
- }
53
- finally {
54
- await this.rulesLock.release();
55
- }
56
- }
57
- async addRule(rule, overwrite) {
58
- await this.cleanUpExpired();
59
- await this.rulesLock.acquireAsync();
60
- try {
61
- if (this._rules.some((r) => r.id === rule.id)) {
62
- if (!overwrite) {
63
- throw new shared_1.AppError({ httpCode: shared_1.HttpCode.CONFLICT, message: 'rule with given ID already exists' });
64
- }
65
- this._removeRule(rule.id);
66
- }
67
- const liveRule = transformMockRuleToLive(rule);
68
- this._rules.push(liveRule);
69
- shared_1.logger.debug(liveRule, 'rule added');
70
- return liveRule;
71
- }
72
- finally {
73
- await this.rulesLock.release();
74
- }
75
- }
76
- _removeRule(ruleOrId) {
77
- this._rules = this._rules.filter((r) => {
78
- const notFound = r.id !== (typeof ruleOrId === 'string' ? ruleOrId : ruleOrId.id);
79
- if (!notFound) {
80
- shared_1.logger.debug({ ruleId: r.id }, 'rule removed');
81
- }
82
- return notFound;
83
- });
84
- }
85
- async removeRule(ruleOrId) {
86
- await this.cleanUpExpired();
87
- await this.rulesLock.acquireAsync();
88
- try {
89
- this._removeRule(ruleOrId);
90
- }
91
- finally {
92
- await this.rulesLock.release();
93
- }
94
- }
95
- enableRule(ruleOrId) {
96
- this._rules.forEach((r) => {
97
- if (r.id === (typeof ruleOrId === 'string' ? ruleOrId : ruleOrId.id)) {
98
- r.counter = 0;
99
- r.isEnabled = true;
100
- shared_1.logger.debug({ ruleId: r.id }, 'rule enabled');
101
- }
102
- });
103
- }
104
- disableRule(ruleOrId) {
105
- this._rules.forEach((r) => {
106
- if (r.id === (typeof ruleOrId === 'string' ? ruleOrId : ruleOrId.id)) {
107
- r.isEnabled = false;
108
- shared_1.logger.debug({ ruleId: r.id }, 'rule disabled');
109
- }
110
- });
111
- }
112
- async findMatchingRule(request) {
113
- const logContext = {
114
- requestId: request.id,
115
- };
116
- const matchingRule = this.enabledRules.find((rule) => {
117
- try {
118
- const matchResult = rule.matches(request);
119
- shared_1.logger.trace({ ...logContext, matchResult }, `rule match attempt for ${rule.id}`);
120
- if (typeof matchResult === 'boolean') {
121
- return matchResult;
122
- }
123
- return matchResult.result;
124
- }
125
- catch (error) {
126
- shared_1.logger.error({ ...logContext, ruleId: rule === null || rule === void 0 ? void 0 : rule.id, error }, 'error in rule match function');
127
- }
128
- });
129
- if (!matchingRule) {
130
- shared_1.logger.debug(logContext, 'no matching rule found');
131
- return null;
132
- }
133
- const matchResult = matchingRule.matches(request);
134
- logContext.ruleId = matchingRule.id;
135
- shared_1.logger.debug({ ...logContext, matchResultMessage: typeof matchResult !== 'boolean' ? matchResult.description : null }, 'found matching rule');
136
- const matchingRuleClone = Object.freeze(Object.assign({}, matchingRule));
137
- ++matchingRule.counter;
138
- logContext.ruleCounter = matchingRule.counter;
139
- if (Number.isNaN(matchingRule.counter) || !Number.isFinite(matchingRule.counter)) {
140
- matchingRule.counter = 0;
141
- shared_1.logger.warn(logContext, "it's over 9000!!!");
142
- }
143
- // TODO check if that works
144
- if (matchingRule.disableAfterUse) {
145
- if (typeof matchingRule.disableAfterUse === 'boolean' || matchingRule.disableAfterUse <= matchingRule.counter) {
146
- shared_1.logger.debug(logContext, 'disabling rule for future requests');
147
- this.disableRule(matchingRule);
148
- }
149
- }
150
- if (matchingRule.removeAfterUse) {
151
- if (typeof matchingRule.removeAfterUse === 'boolean' || matchingRule.removeAfterUse <= matchingRule.counter) {
152
- shared_1.logger.debug(logContext, 'removing rule for future requests');
153
- await this.removeRule(matchingRule);
154
- }
155
- }
156
- if (typeof matchResult !== 'boolean') {
157
- if (matchResult.disableRuleIds && matchResult.disableRuleIds.length > 0) {
158
- shared_1.logger.debug({ ...logContext, disableRuleIds: matchResult.disableRuleIds }, 'disabling rules based on matchResult');
159
- for (const ruleId of matchResult.disableRuleIds) {
160
- this.disableRule(ruleId);
161
- }
162
- }
163
- if (matchResult.enableRuleIds && matchResult.enableRuleIds.length > 0) {
164
- shared_1.logger.debug({ ...logContext, disableRuleIds: matchResult.disableRuleIds }, 'enabling rules based on matchResult');
165
- for (const ruleId of matchResult.enableRuleIds) {
166
- this.enableRule(ruleId);
167
- }
168
- }
169
- }
170
- return matchingRuleClone;
171
- }
172
- async getRules() {
173
- await this.cleanUpExpired();
174
- return this._rules;
175
- }
176
- async getRule(id) {
177
- await this.cleanUpExpired();
178
- return this._rules.find((r) => r.id === id);
179
- }
180
- }
181
- const getRuleExecutor = (mockUuid) => {
182
- if (!ruleExecutors[mockUuid]) {
183
- ruleExecutors[mockUuid] = new RuleExecutor([...rules_1.DEFAULT_RULES, ...rules_1.CUSTOM_RULES].map((r) => ({ ...r, ttlSeconds: Infinity })));
184
- }
185
- return ruleExecutors[mockUuid];
186
- };
187
- exports.getRuleExecutor = getRuleExecutor;
@@ -1,2 +0,0 @@
1
- import type * as Stuntman from '@stuntman/shared';
2
- export declare const catchAllRule: Stuntman.DeployedRule;
@@ -1,15 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.catchAllRule = void 0;
4
- const shared_1 = require("@stuntman/shared");
5
- exports.catchAllRule = {
6
- id: shared_1.CATCH_RULE_NAME,
7
- matches: () => true,
8
- priority: shared_1.CATCH_ALL_RULE_PRIORITY,
9
- actions: {
10
- mockResponse: (req) => ({
11
- body: `Request received by Stuntman mock <pre>${JSON.stringify(req, null, 4)}</pre>`,
12
- status: 200,
13
- }),
14
- },
15
- };
@@ -1,2 +0,0 @@
1
- import type * as Stuntman from '@stuntman/shared';
2
- export declare const echoRule: Stuntman.DeployedRule;
@@ -1,15 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.echoRule = void 0;
4
- const shared_1 = require("@stuntman/shared");
5
- exports.echoRule = {
6
- id: 'internal/echo',
7
- priority: shared_1.DEFAULT_RULE_PRIORITY + 1,
8
- matches: (req) => /https?:\/\/echo\/.*/.test(req.url),
9
- actions: {
10
- mockResponse: (req) => ({
11
- body: req,
12
- status: 200,
13
- }),
14
- },
15
- };
@@ -1,3 +0,0 @@
1
- import type * as Stuntman from '@stuntman/shared';
2
- export declare const DEFAULT_RULES: Stuntman.DeployedRule[];
3
- export declare const CUSTOM_RULES: Stuntman.DeployedRule[];