@stuntman/client 0.1.2 → 0.1.3
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 +1 -1
- package/dist/apiClient.d.ts +1 -0
- package/dist/apiClient.js +8 -1
- package/dist/ruleBuilder.d.ts +6 -4
- package/dist/ruleBuilder.js +95 -16
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -30,4 +30,4 @@ const rule = ruleBuilder()
|
|
|
30
30
|
client.addRule(rule).then((x) => console.log(x));
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
Check [example app](https://github.com/andrzej-woof/stuntman/tree/master/
|
|
33
|
+
Check [example app](https://github.com/andrzej-woof/stuntman/tree/master/apps/example#readme) for more samples
|
package/dist/apiClient.d.ts
CHANGED
package/dist/apiClient.js
CHANGED
|
@@ -107,7 +107,14 @@ class Client {
|
|
|
107
107
|
controller.abort();
|
|
108
108
|
}, this.options.timeout);
|
|
109
109
|
try {
|
|
110
|
-
const response = await fetch(url, {
|
|
110
|
+
const response = await fetch(url, {
|
|
111
|
+
...init,
|
|
112
|
+
headers: {
|
|
113
|
+
...(this.options.apiKey && { 'x-api-key': this.options.apiKey }),
|
|
114
|
+
...init === null || init === void 0 ? void 0 : init.headers,
|
|
115
|
+
},
|
|
116
|
+
signal: (_a = init === null || init === void 0 ? void 0 : init.signal) !== null && _a !== void 0 ? _a : controller.signal,
|
|
117
|
+
});
|
|
111
118
|
if (!response.ok) {
|
|
112
119
|
const text = await response.text();
|
|
113
120
|
let json;
|
package/dist/ruleBuilder.d.ts
CHANGED
|
@@ -45,6 +45,7 @@ declare class RuleBuilder extends RuleBuilderBase {
|
|
|
45
45
|
onRequestTo(filter: string | RegExp): RuleBuilderInitialized;
|
|
46
46
|
onRequestToHostname(hostname: string | RegExp): RuleBuilderInitialized;
|
|
47
47
|
onRequestToPathname(pathname: string | RegExp): RuleBuilderInitialized;
|
|
48
|
+
onRequestToPort(port: string | number | RegExp): RuleBuilderInitialized;
|
|
48
49
|
onAnyRequest(): RuleBuilderInitialized;
|
|
49
50
|
}
|
|
50
51
|
declare class RuleBuilderInitialized extends RuleBuilderBase {
|
|
@@ -56,7 +57,7 @@ declare class RuleBuilderInitialized extends RuleBuilderBase {
|
|
|
56
57
|
withSearchParams(params: KeyValueMatcher[]): RuleBuilderInitialized;
|
|
57
58
|
withHeader(key: string | RegExp): RuleBuilderInitialized;
|
|
58
59
|
withHeader(key: string, value?: string | RegExp): RuleBuilderInitialized;
|
|
59
|
-
withHeaders(headers: KeyValueMatcher[]): RuleBuilderInitialized;
|
|
60
|
+
withHeaders(...headers: KeyValueMatcher[]): RuleBuilderInitialized;
|
|
60
61
|
withBodyText(includes: string): RuleBuilderInitialized;
|
|
61
62
|
withBodyText(matches: RegExp): RuleBuilderInitialized;
|
|
62
63
|
withoutBody(): RuleBuilderInitialized;
|
|
@@ -67,11 +68,12 @@ declare class RuleBuilderInitialized extends RuleBuilderBase {
|
|
|
67
68
|
proxyPass(): Stuntman.SerializableRule;
|
|
68
69
|
mockResponse(staticResponse: Stuntman.Response): Stuntman.SerializableRule;
|
|
69
70
|
mockResponse(generationFunction: Stuntman.RemotableFunction<Stuntman.ResponseGenerationFn>): Stuntman.SerializableRule;
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
mockResponse(localFn: Stuntman.ResponseGenerationFn, localVariables?: Stuntman.LocalVariables): Stuntman.SerializableRule;
|
|
72
|
+
modifyRequest(modifyFunction: Stuntman.RequestManipulationFn | Stuntman.RemotableFunction<Stuntman.RequestManipulationFn>, localVariables?: Stuntman.LocalVariables): RuleBuilderRequestInitialized;
|
|
73
|
+
modifyResponse(modifyFunction: Stuntman.ResponseManipulationFn | Stuntman.RemotableFunction<Stuntman.ResponseManipulationFn>, localVariables?: Stuntman.LocalVariables): Stuntman.SerializableRule;
|
|
72
74
|
}
|
|
73
75
|
declare class RuleBuilderRequestInitialized extends RuleBuilderBase {
|
|
74
|
-
modifyResponse(modifyFunction: Stuntman.RemotableFunction<Stuntman.ResponseManipulationFn
|
|
76
|
+
modifyResponse(modifyFunction: Stuntman.ResponseManipulationFn | Stuntman.RemotableFunction<Stuntman.ResponseManipulationFn>, localVariables?: Stuntman.LocalVariables): Stuntman.SerializableRule;
|
|
75
77
|
}
|
|
76
78
|
export declare const ruleBuilder: () => RuleBuilder;
|
|
77
79
|
export {};
|
package/dist/ruleBuilder.js
CHANGED
|
@@ -13,19 +13,21 @@ class RuleBuilderBaseBase {
|
|
|
13
13
|
priority: shared_1.DEFAULT_RULE_PRIORITY,
|
|
14
14
|
matches: {
|
|
15
15
|
localFn: (req) => {
|
|
16
|
-
var _a, _b, _c;
|
|
16
|
+
var _a, _b, _c, _d;
|
|
17
17
|
const ___url = new URL(req.url);
|
|
18
18
|
const ___headers = req.rawHeaders;
|
|
19
19
|
const arrayIndexerRegex = /\[(?<arrayIndex>[0-9]*)\]/i;
|
|
20
20
|
const matchObject = (obj, path, value, parentPath) => {
|
|
21
|
-
var _a, _b;
|
|
21
|
+
var _a, _b, _c, _d;
|
|
22
22
|
if (!obj) {
|
|
23
23
|
return { result: false, description: `${parentPath} is falsey` };
|
|
24
24
|
}
|
|
25
25
|
const [rawKey, ...rest] = path.split('.');
|
|
26
26
|
const key = rawKey.replace(arrayIndexerRegex, '');
|
|
27
27
|
const shouldBeArray = arrayIndexerRegex.test(rawKey);
|
|
28
|
-
const arrayIndex =
|
|
28
|
+
const arrayIndex = (((_b = (_a = arrayIndexerRegex.exec(rawKey)) === null || _a === void 0 ? void 0 : _a.groups) === null || _b === void 0 ? void 0 : _b.arrayIndex) || '').length > 0
|
|
29
|
+
? Number((_d = (_c = arrayIndexerRegex.exec(rawKey)) === null || _c === void 0 ? void 0 : _c.groups) === null || _d === void 0 ? void 0 : _d.arrayIndex)
|
|
30
|
+
: Number.NaN;
|
|
29
31
|
const actualValue = key ? obj[key] : obj;
|
|
30
32
|
const currentPath = `${parentPath ? `${parentPath}.` : ''}${rawKey}`;
|
|
31
33
|
if (value === undefined && actualValue === undefined) {
|
|
@@ -34,7 +36,7 @@ class RuleBuilderBaseBase {
|
|
|
34
36
|
if (rest.length === 0) {
|
|
35
37
|
if (shouldBeArray &&
|
|
36
38
|
(!Array.isArray(actualValue) ||
|
|
37
|
-
|
|
39
|
+
(Number.isInteger(arrayIndex) && actualValue.length <= Number(arrayIndex)))) {
|
|
38
40
|
return { result: false, description: `${currentPath} empty array` };
|
|
39
41
|
}
|
|
40
42
|
if (value === undefined) {
|
|
@@ -52,7 +54,7 @@ class RuleBuilderBaseBase {
|
|
|
52
54
|
if (Number.isInteger(arrayIndex)) {
|
|
53
55
|
return matchObject(actualValue[Number(arrayIndex)], rest.join('.'), value, currentPath);
|
|
54
56
|
}
|
|
55
|
-
const hasArrayMatch = actualValue.some((arrayValue) => matchObject(arrayValue, rest.join('.'), value, currentPath));
|
|
57
|
+
const hasArrayMatch = actualValue.some((arrayValue) => matchObject(arrayValue, rest.join('.'), value, currentPath).result);
|
|
56
58
|
return { result: hasArrayMatch, description: `array match ${currentPath}` };
|
|
57
59
|
}
|
|
58
60
|
if (typeof actualValue !== 'object') {
|
|
@@ -96,6 +98,17 @@ class RuleBuilderBaseBase {
|
|
|
96
98
|
description: `pathname ${___url.pathname} doesn't match ${(_c = matchBuilderVariables.pathname) === null || _c === void 0 ? void 0 : _c.toString()}`,
|
|
97
99
|
};
|
|
98
100
|
}
|
|
101
|
+
if (matchBuilderVariables.port) {
|
|
102
|
+
const port = ___url.port && ___url.port !== '' ? ___url.port : ___url.protocol === 'https:' ? '443' : '80';
|
|
103
|
+
if (!___matchesValue(matchBuilderVariables.port instanceof RegExp
|
|
104
|
+
? matchBuilderVariables.port
|
|
105
|
+
: `${matchBuilderVariables.port}`, port)) {
|
|
106
|
+
return {
|
|
107
|
+
result: false,
|
|
108
|
+
description: `port ${port} doesn't match ${(_d = matchBuilderVariables.port) === null || _d === void 0 ? void 0 : _d.toString()}`,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
99
112
|
if (matchBuilderVariables.searchParams) {
|
|
100
113
|
for (const searchParamMatcher of matchBuilderVariables.searchParams) {
|
|
101
114
|
if (typeof searchParamMatcher === 'string') {
|
|
@@ -130,11 +143,17 @@ class RuleBuilderBaseBase {
|
|
|
130
143
|
for (const headerMatcher of matchBuilderVariables.headers) {
|
|
131
144
|
if (typeof headerMatcher === 'string') {
|
|
132
145
|
const result = ___headers.has(headerMatcher);
|
|
133
|
-
|
|
146
|
+
if (result) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
return { result: false, description: `headers.has("${headerMatcher}")` };
|
|
134
150
|
}
|
|
135
151
|
if (headerMatcher instanceof RegExp) {
|
|
136
152
|
const result = ___headers.toHeaderPairs().some(([key]) => headerMatcher.test(key));
|
|
137
|
-
|
|
153
|
+
if (result) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
return { result: false, description: `headers.keys matches ${headerMatcher.toString()}` };
|
|
138
157
|
}
|
|
139
158
|
if (!___headers.has(headerMatcher.key)) {
|
|
140
159
|
return { result: false, description: `headers.has("${headerMatcher.key}")` };
|
|
@@ -189,7 +208,8 @@ class RuleBuilderBaseBase {
|
|
|
189
208
|
for (const jsonMatcher of Array.isArray(matchBuilderVariables.bodyJson)
|
|
190
209
|
? matchBuilderVariables.bodyJson
|
|
191
210
|
: [matchBuilderVariables.bodyJson]) {
|
|
192
|
-
|
|
211
|
+
const matchObjectResult = matchObject(json, jsonMatcher.key, jsonMatcher.value);
|
|
212
|
+
if (!matchObjectResult.result) {
|
|
193
213
|
return { result: false, description: `$.${jsonMatcher.key} != "${jsonMatcher.value}"` };
|
|
194
214
|
}
|
|
195
215
|
}
|
|
@@ -246,6 +266,9 @@ class RuleBuilderBaseBase {
|
|
|
246
266
|
}
|
|
247
267
|
class RuleBuilderBase extends RuleBuilderBaseBase {
|
|
248
268
|
limitedUse(hitCount) {
|
|
269
|
+
if (this.rule.removeAfterUse) {
|
|
270
|
+
throw new Error(`limit already set at ${this.rule.removeAfterUse}`);
|
|
271
|
+
}
|
|
249
272
|
if (Number.isNaN(hitCount) || !Number.isFinite(hitCount) || !Number.isInteger(hitCount) || hitCount <= 0) {
|
|
250
273
|
throw new Error('Invalid hitCount');
|
|
251
274
|
}
|
|
@@ -265,6 +288,9 @@ class RuleBuilderBase extends RuleBuilderBaseBase {
|
|
|
265
288
|
}
|
|
266
289
|
class RuleBuilder extends RuleBuilderBase {
|
|
267
290
|
raisePriority(by) {
|
|
291
|
+
if (this.rule.priority !== shared_1.DEFAULT_RULE_PRIORITY) {
|
|
292
|
+
throw new Error('you should not alter rule priority more than once');
|
|
293
|
+
}
|
|
268
294
|
const subtract = by !== null && by !== void 0 ? by : 1;
|
|
269
295
|
if (subtract >= shared_1.DEFAULT_RULE_PRIORITY) {
|
|
270
296
|
throw new Error(`Unable to raise priority over the default ${shared_1.DEFAULT_RULE_PRIORITY}`);
|
|
@@ -273,6 +299,9 @@ class RuleBuilder extends RuleBuilderBase {
|
|
|
273
299
|
return this;
|
|
274
300
|
}
|
|
275
301
|
decreasePriority(by) {
|
|
302
|
+
if (this.rule.priority !== shared_1.DEFAULT_RULE_PRIORITY) {
|
|
303
|
+
throw new Error('you should not alter rule priority more than once');
|
|
304
|
+
}
|
|
276
305
|
const add = by !== null && by !== void 0 ? by : 1;
|
|
277
306
|
this.rule.priority = shared_1.DEFAULT_RULE_PRIORITY + add;
|
|
278
307
|
return this;
|
|
@@ -303,20 +332,33 @@ class RuleBuilder extends RuleBuilderBase {
|
|
|
303
332
|
this._matchBuilderVariables.pathname = pathname;
|
|
304
333
|
return new RuleBuilderInitialized(this.rule, this._matchBuilderVariables);
|
|
305
334
|
}
|
|
335
|
+
onRequestToPort(port) {
|
|
336
|
+
this._matchBuilderVariables.port = port;
|
|
337
|
+
return new RuleBuilderInitialized(this.rule, this._matchBuilderVariables);
|
|
338
|
+
}
|
|
306
339
|
onAnyRequest() {
|
|
307
|
-
return this.
|
|
340
|
+
return new RuleBuilderInitialized(this.rule, this._matchBuilderVariables);
|
|
308
341
|
}
|
|
309
342
|
}
|
|
310
343
|
class RuleBuilderInitialized extends RuleBuilderBase {
|
|
311
344
|
withHostname(hostname) {
|
|
345
|
+
if (this._matchBuilderVariables.hostname) {
|
|
346
|
+
throw new Error('hostname already set');
|
|
347
|
+
}
|
|
312
348
|
this._matchBuilderVariables.hostname = hostname;
|
|
313
349
|
return this;
|
|
314
350
|
}
|
|
315
351
|
withPathname(pathname) {
|
|
352
|
+
if (this._matchBuilderVariables.pathname) {
|
|
353
|
+
throw new Error('pathname already set');
|
|
354
|
+
}
|
|
316
355
|
this._matchBuilderVariables.pathname = pathname;
|
|
317
356
|
return this;
|
|
318
357
|
}
|
|
319
358
|
withPort(port) {
|
|
359
|
+
if (this._matchBuilderVariables.port) {
|
|
360
|
+
throw new Error('port already set');
|
|
361
|
+
}
|
|
320
362
|
this._matchBuilderVariables.port = port;
|
|
321
363
|
return this;
|
|
322
364
|
}
|
|
@@ -368,7 +410,7 @@ class RuleBuilderInitialized extends RuleBuilderBase {
|
|
|
368
410
|
this._matchBuilderVariables.headers.push({ key, value });
|
|
369
411
|
return this;
|
|
370
412
|
}
|
|
371
|
-
withHeaders(headers) {
|
|
413
|
+
withHeaders(...headers) {
|
|
372
414
|
if (!this._matchBuilderVariables.headers) {
|
|
373
415
|
this._matchBuilderVariables.headers = [];
|
|
374
416
|
}
|
|
@@ -383,10 +425,19 @@ class RuleBuilderInitialized extends RuleBuilderBase {
|
|
|
383
425
|
return this;
|
|
384
426
|
}
|
|
385
427
|
withBodyText(includesOrMatches) {
|
|
428
|
+
if (this._matchBuilderVariables.bodyText) {
|
|
429
|
+
throw new Error('bodyText already set');
|
|
430
|
+
}
|
|
431
|
+
if (this._matchBuilderVariables.bodyText === null) {
|
|
432
|
+
throw new Error('cannot use both withBodyText and withoutBody');
|
|
433
|
+
}
|
|
386
434
|
this._matchBuilderVariables.bodyText = includesOrMatches;
|
|
387
435
|
return this;
|
|
388
436
|
}
|
|
389
437
|
withoutBody() {
|
|
438
|
+
if (this._matchBuilderVariables.bodyText) {
|
|
439
|
+
throw new Error('cannot use both withBodyText and withoutBody');
|
|
440
|
+
}
|
|
390
441
|
this._matchBuilderVariables.bodyText = null;
|
|
391
442
|
return this;
|
|
392
443
|
}
|
|
@@ -397,7 +448,7 @@ class RuleBuilderInitialized extends RuleBuilderBase {
|
|
|
397
448
|
}
|
|
398
449
|
if (typeof keyOrMatcher === 'string') {
|
|
399
450
|
if (!keyRegex.test(keyOrMatcher)) {
|
|
400
|
-
throw new Error(
|
|
451
|
+
throw new Error(`invalid key "${keyOrMatcher}"`);
|
|
401
452
|
}
|
|
402
453
|
this._matchBuilderVariables.bodyJson.push({ key: keyOrMatcher, value: withValue });
|
|
403
454
|
return this;
|
|
@@ -406,7 +457,7 @@ class RuleBuilderInitialized extends RuleBuilderBase {
|
|
|
406
457
|
throw new Error('invalid usage');
|
|
407
458
|
}
|
|
408
459
|
if (!keyRegex.test(keyOrMatcher.key)) {
|
|
409
|
-
throw new Error(
|
|
460
|
+
throw new Error(`invalid key "${keyOrMatcher}"`);
|
|
410
461
|
}
|
|
411
462
|
this._matchBuilderVariables.bodyJson.push(keyOrMatcher);
|
|
412
463
|
return this;
|
|
@@ -418,24 +469,52 @@ class RuleBuilderInitialized extends RuleBuilderBase {
|
|
|
418
469
|
proxyPass() {
|
|
419
470
|
return this.rule;
|
|
420
471
|
}
|
|
421
|
-
mockResponse(response) {
|
|
472
|
+
mockResponse(response, localVariables) {
|
|
473
|
+
if (typeof response === 'function') {
|
|
474
|
+
this.rule.actions = { mockResponse: { localFn: response, localVariables: localVariables !== null && localVariables !== void 0 ? localVariables : {} } };
|
|
475
|
+
return this.rule;
|
|
476
|
+
}
|
|
477
|
+
if (localVariables) {
|
|
478
|
+
throw new Error('invalid call - localVariables cannot be used together with Response or RemotableFunction');
|
|
479
|
+
}
|
|
422
480
|
this.rule.actions = { mockResponse: response };
|
|
423
481
|
return this.rule;
|
|
424
482
|
}
|
|
425
|
-
modifyRequest(modifyFunction) {
|
|
483
|
+
modifyRequest(modifyFunction, localVariables) {
|
|
484
|
+
if (typeof modifyFunction === 'function') {
|
|
485
|
+
this.rule.actions = { modifyRequest: { localFn: modifyFunction, localVariables: localVariables !== null && localVariables !== void 0 ? localVariables : {} } };
|
|
486
|
+
return new RuleBuilderRequestInitialized(this.rule, this._matchBuilderVariables);
|
|
487
|
+
}
|
|
488
|
+
if (localVariables) {
|
|
489
|
+
throw new Error('invalid call - localVariables cannot be used together with Response or RemotableFunction');
|
|
490
|
+
}
|
|
426
491
|
this.rule.actions = { modifyRequest: modifyFunction };
|
|
427
492
|
return new RuleBuilderRequestInitialized(this.rule, this._matchBuilderVariables);
|
|
428
493
|
}
|
|
429
|
-
modifyResponse(modifyFunction) {
|
|
494
|
+
modifyResponse(modifyFunction, localVariables) {
|
|
495
|
+
if (typeof modifyFunction === 'function') {
|
|
496
|
+
this.rule.actions = { modifyResponse: { localFn: modifyFunction, localVariables: localVariables !== null && localVariables !== void 0 ? localVariables : {} } };
|
|
497
|
+
return this.rule;
|
|
498
|
+
}
|
|
499
|
+
if (localVariables) {
|
|
500
|
+
throw new Error('invalid call - localVariables cannot be used together with Response or RemotableFunction');
|
|
501
|
+
}
|
|
430
502
|
this.rule.actions = { modifyResponse: modifyFunction };
|
|
431
503
|
return this.rule;
|
|
432
504
|
}
|
|
433
505
|
}
|
|
434
506
|
class RuleBuilderRequestInitialized extends RuleBuilderBase {
|
|
435
|
-
modifyResponse(modifyFunction) {
|
|
507
|
+
modifyResponse(modifyFunction, localVariables) {
|
|
436
508
|
if (!this.rule.actions) {
|
|
437
509
|
throw new Error('rule.actions not defined - builder implementation error');
|
|
438
510
|
}
|
|
511
|
+
if (typeof modifyFunction === 'function') {
|
|
512
|
+
this.rule.actions = { modifyResponse: { localFn: modifyFunction, localVariables: localVariables !== null && localVariables !== void 0 ? localVariables : {} } };
|
|
513
|
+
return this.rule;
|
|
514
|
+
}
|
|
515
|
+
if (localVariables) {
|
|
516
|
+
throw new Error('invalid call - localVariables cannot be used together with Response or RemotableFunction');
|
|
517
|
+
}
|
|
439
518
|
this.rule.actions.modifyResponse = modifyFunction;
|
|
440
519
|
return this.rule;
|
|
441
520
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stuntman/client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Stuntman - HTTP proxy / mock API client",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -37,8 +37,12 @@
|
|
|
37
37
|
"uuid": "9.0.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
+
"@jest/globals": "29.4.3",
|
|
40
41
|
"@types/serialize-javascript": "5.0.2",
|
|
41
|
-
"@types/uuid": "9.0.0"
|
|
42
|
+
"@types/uuid": "9.0.0",
|
|
43
|
+
"jest": "29.4.3",
|
|
44
|
+
"ts-jest": "29.0.5",
|
|
45
|
+
"typescript": "4.9.5"
|
|
42
46
|
},
|
|
43
47
|
"files": [
|
|
44
48
|
"dist/",
|
|
@@ -47,10 +51,10 @@
|
|
|
47
51
|
"CHANGELOG.md"
|
|
48
52
|
],
|
|
49
53
|
"scripts": {
|
|
50
|
-
"test": "
|
|
54
|
+
"test": "SUPPRESS_NO_CONFIG_WARNING=1 jest",
|
|
51
55
|
"clean": "rm -fr dist",
|
|
52
56
|
"build": "tsc",
|
|
53
57
|
"lint": "prettier --check . && eslint . --ext ts",
|
|
54
|
-
"lint:fix": "prettier --write ./src && eslint ./src --ext ts --fix"
|
|
58
|
+
"lint:fix": "prettier --write ./{src,test} && eslint ./{src,test} --ext ts --fix"
|
|
55
59
|
}
|
|
56
60
|
}
|