@n1k1t/mock-server 0.3.0 → 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.
Files changed (248) hide show
  1. package/README.md +583 -1013
  2. package/README.old.md +1255 -0
  3. package/lib/package.json +15 -6
  4. package/lib/src/client/helpers/expectations.d.ts +15 -45
  5. package/lib/src/client/helpers/expectations.d.ts.map +1 -1
  6. package/lib/src/client/helpers/expectations.js.map +1 -1
  7. package/lib/src/client/methods/expectations-group.update.method.js +1 -1
  8. package/lib/src/client/methods/expectations-group.update.method.js.map +1 -1
  9. package/lib/src/client/methods/expectations.create.method.js +1 -1
  10. package/lib/src/client/methods/expectations.create.method.js.map +1 -1
  11. package/lib/src/client/methods/expectations.delete.method.js +1 -1
  12. package/lib/src/client/methods/expectations.delete.method.js.map +1 -1
  13. package/lib/src/client/methods/expectations.update.method.js +1 -1
  14. package/lib/src/client/methods/expectations.update.method.js.map +1 -1
  15. package/lib/src/client/methods/ping.method.js +1 -1
  16. package/lib/src/client/methods/ping.method.js.map +1 -1
  17. package/lib/src/client/methods/providers.create.method.js +1 -1
  18. package/lib/src/client/methods/providers.create.method.js.map +1 -1
  19. package/lib/src/client/methods/providers.delete.method.js +1 -1
  20. package/lib/src/client/methods/providers.delete.method.js.map +1 -1
  21. package/lib/src/client/models/client.d.ts +4 -4
  22. package/lib/src/client/models/client.d.ts.map +1 -1
  23. package/lib/src/client/models/client.js +6 -3
  24. package/lib/src/client/models/client.js.map +1 -1
  25. package/lib/src/client/models/types.d.ts +3 -0
  26. package/lib/src/client/models/types.d.ts.map +1 -1
  27. package/lib/src/client/models/types.js.map +1 -1
  28. package/lib/src/client/types.d.ts +1 -1
  29. package/lib/src/client/types.d.ts.map +1 -1
  30. package/lib/src/client/utils.d.ts +1 -1
  31. package/lib/src/client/utils.d.ts.map +1 -1
  32. package/lib/src/client/utils.js.map +1 -1
  33. package/lib/src/config/index.d.ts +1 -1
  34. package/lib/src/config/index.js +1 -1
  35. package/lib/src/config/index.js.map +1 -1
  36. package/lib/src/expectations/__utils__/index.d.ts.map +1 -1
  37. package/lib/src/expectations/__utils__/index.js +0 -1
  38. package/lib/src/expectations/__utils__/index.js.map +1 -1
  39. package/lib/src/expectations/models/expectation.d.ts +1 -1
  40. package/lib/src/expectations/models/expectation.d.ts.map +1 -1
  41. package/lib/src/expectations/models/expectation.js.map +1 -1
  42. package/lib/src/expectations/models/storage.d.ts +5 -5
  43. package/lib/src/expectations/models/storage.d.ts.map +1 -1
  44. package/lib/src/expectations/models/storage.js.map +1 -1
  45. package/lib/src/expectations/types.d.ts +18 -15
  46. package/lib/src/expectations/types.d.ts.map +1 -1
  47. package/lib/src/expectations/types.js +0 -1
  48. package/lib/src/expectations/types.js.map +1 -1
  49. package/lib/src/expectations/utils/location.d.ts.map +1 -1
  50. package/lib/src/expectations/utils/location.js +0 -6
  51. package/lib/src/expectations/utils/location.js.map +1 -1
  52. package/lib/src/server/endpoints/cache.backup.endpoint.js +1 -1
  53. package/lib/src/server/endpoints/cache.backup.endpoint.js.map +1 -1
  54. package/lib/src/server/endpoints/cache.delete.endpoint.js +1 -1
  55. package/lib/src/server/endpoints/cache.delete.endpoint.js.map +1 -1
  56. package/lib/src/server/endpoints/cache.restore.endpoint.d.ts.map +1 -1
  57. package/lib/src/server/endpoints/cache.restore.endpoint.js +8 -3
  58. package/lib/src/server/endpoints/cache.restore.endpoint.js.map +1 -1
  59. package/lib/src/server/endpoints/cache.restore.stream.endpoint.js +1 -1
  60. package/lib/src/server/endpoints/cache.restore.stream.endpoint.js.map +1 -1
  61. package/lib/src/server/endpoints/cache.usage.get.endpoint.js +1 -1
  62. package/lib/src/server/endpoints/cache.usage.get.endpoint.js.map +1 -1
  63. package/lib/src/server/endpoints/config.get.endpoint.d.ts +1 -1
  64. package/lib/src/server/endpoints/expectations.get-by-id.endpoint.js +1 -1
  65. package/lib/src/server/endpoints/expectations.get-by-id.endpoint.js.map +1 -1
  66. package/lib/src/server/endpoints/expectations.update.endpoint.d.ts +1 -1
  67. package/lib/src/server/endpoints/expectations.update.endpoint.d.ts.map +1 -1
  68. package/lib/src/server/endpoints/gui.endpoint.js +2 -2
  69. package/lib/src/server/endpoints/gui.endpoint.js.map +1 -1
  70. package/lib/src/server/endpoints/history.compact.get-list.endpoint.d.ts +2 -2
  71. package/lib/src/server/endpoints/history.get-by-id.endpoint.d.ts +1 -1
  72. package/lib/src/server/endpoints/history.get-by-id.endpoint.js +1 -1
  73. package/lib/src/server/endpoints/history.get-by-id.endpoint.js.map +1 -1
  74. package/lib/src/server/index.d.ts +2 -5
  75. package/lib/src/server/index.d.ts.map +1 -1
  76. package/lib/src/server/index.js +10 -9
  77. package/lib/src/server/index.js.map +1 -1
  78. package/lib/src/server/models/context/index.d.ts +6 -6
  79. package/lib/src/server/models/context/index.d.ts.map +1 -1
  80. package/lib/src/server/models/context/index.js +0 -4
  81. package/lib/src/server/models/context/index.js.map +1 -1
  82. package/lib/src/server/models/context/snapshot.d.ts +2 -3
  83. package/lib/src/server/models/context/snapshot.d.ts.map +1 -1
  84. package/lib/src/server/models/context/snapshot.js +6 -6
  85. package/lib/src/server/models/context/snapshot.js.map +1 -1
  86. package/lib/src/server/models/context/types.d.ts +10 -9
  87. package/lib/src/server/models/context/types.d.ts.map +1 -1
  88. package/lib/src/server/models/context/utils.d.ts.map +1 -1
  89. package/lib/src/server/models/context/utils.js +2 -6
  90. package/lib/src/server/models/context/utils.js.map +1 -1
  91. package/lib/src/server/models/endpoint.d.ts +2 -2
  92. package/lib/src/server/models/endpoint.d.ts.map +1 -1
  93. package/lib/src/server/models/executor/index.d.ts +2 -2
  94. package/lib/src/server/models/executor/index.d.ts.map +1 -1
  95. package/lib/src/server/models/executor/index.js +38 -6
  96. package/lib/src/server/models/executor/index.js.map +1 -1
  97. package/lib/src/server/models/history/model.d.ts +4 -6
  98. package/lib/src/server/models/history/model.d.ts.map +1 -1
  99. package/lib/src/server/models/history/model.js +2 -7
  100. package/lib/src/server/models/history/model.js.map +1 -1
  101. package/lib/src/server/models/index.d.ts +2 -0
  102. package/lib/src/server/models/index.d.ts.map +1 -1
  103. package/lib/src/server/models/index.js +2 -0
  104. package/lib/src/server/models/index.js.map +1 -1
  105. package/lib/src/server/models/message.d.ts +16 -0
  106. package/lib/src/server/models/message.d.ts.map +1 -0
  107. package/lib/src/server/models/message.js +53 -0
  108. package/lib/src/server/models/message.js.map +1 -0
  109. package/lib/src/server/models/providers/model.d.ts +2 -2
  110. package/lib/src/server/models/providers/model.d.ts.map +1 -1
  111. package/lib/src/server/models/providers/storage.js +1 -1
  112. package/lib/src/server/models/providers/storage.js.map +1 -1
  113. package/lib/src/server/models/providers/system.d.ts.map +1 -1
  114. package/lib/src/server/models/providers/system.js +0 -1
  115. package/lib/src/server/models/providers/system.js.map +1 -1
  116. package/lib/src/server/models/router.js +1 -1
  117. package/lib/src/server/models/router.js.map +1 -1
  118. package/lib/src/server/models/transports/storage.js +1 -1
  119. package/lib/src/server/models/transports/storage.js.map +1 -1
  120. package/lib/src/server/models/websocket/errors/index.d.ts +2 -0
  121. package/lib/src/server/models/websocket/errors/index.d.ts.map +1 -0
  122. package/lib/src/server/models/websocket/errors/index.js +18 -0
  123. package/lib/src/server/models/websocket/errors/index.js.map +1 -0
  124. package/lib/src/server/models/websocket/errors/websocket-connection.error.d.ts +9 -0
  125. package/lib/src/server/models/websocket/errors/websocket-connection.error.d.ts.map +1 -0
  126. package/lib/src/server/models/websocket/errors/websocket-connection.error.js +32 -0
  127. package/lib/src/server/models/websocket/errors/websocket-connection.error.js.map +1 -0
  128. package/lib/src/server/models/websocket/factory.d.ts +9 -0
  129. package/lib/src/server/models/websocket/factory.d.ts.map +1 -0
  130. package/lib/src/server/models/websocket/factory.js +23 -0
  131. package/lib/src/server/models/websocket/factory.js.map +1 -0
  132. package/lib/src/server/models/websocket/index.d.ts +5 -0
  133. package/lib/src/server/models/websocket/index.d.ts.map +1 -0
  134. package/lib/src/server/models/websocket/index.js +21 -0
  135. package/lib/src/server/models/websocket/index.js.map +1 -0
  136. package/lib/src/server/models/websocket/model.d.ts +29 -0
  137. package/lib/src/server/models/websocket/model.d.ts.map +1 -0
  138. package/lib/src/server/models/websocket/model.js +164 -0
  139. package/lib/src/server/models/websocket/model.js.map +1 -0
  140. package/lib/src/server/models/websocket/types.d.ts +11 -0
  141. package/lib/src/server/models/websocket/types.d.ts.map +1 -0
  142. package/lib/src/server/models/websocket/types.js +3 -0
  143. package/lib/src/server/models/websocket/types.js.map +1 -0
  144. package/lib/src/server/transports/http.transport/context.d.ts +1 -1
  145. package/lib/src/server/transports/http.transport/context.d.ts.map +1 -1
  146. package/lib/src/server/transports/http.transport/context.js +10 -3
  147. package/lib/src/server/transports/http.transport/context.js.map +1 -1
  148. package/lib/src/server/transports/http.transport/executor.d.ts +3 -13
  149. package/lib/src/server/transports/http.transport/executor.d.ts.map +1 -1
  150. package/lib/src/server/transports/http.transport/executor.js +7 -6
  151. package/lib/src/server/transports/http.transport/executor.js.map +1 -1
  152. package/lib/src/server/transports/http.transport/index.d.ts +1 -1
  153. package/lib/src/server/transports/http.transport/index.d.ts.map +1 -1
  154. package/lib/src/server/transports/http.transport/index.js +2 -3
  155. package/lib/src/server/transports/http.transport/index.js.map +1 -1
  156. package/lib/src/server/transports/index.d.ts +1 -1
  157. package/lib/src/server/transports/index.d.ts.map +1 -1
  158. package/lib/src/server/transports/index.js +1 -1
  159. package/lib/src/server/transports/index.js.map +1 -1
  160. package/lib/src/server/transports/{internal → system}/http.transport/context.d.ts +6 -6
  161. package/lib/src/server/transports/system/http.transport/context.d.ts.map +1 -0
  162. package/lib/src/server/transports/{internal → system}/http.transport/context.js +7 -7
  163. package/lib/src/server/transports/system/http.transport/context.js.map +1 -0
  164. package/lib/src/server/transports/system/http.transport/executor.d.ts +21 -0
  165. package/lib/src/server/transports/system/http.transport/executor.d.ts.map +1 -0
  166. package/lib/src/server/transports/{internal → system}/http.transport/executor.js +5 -5
  167. package/lib/src/server/transports/system/http.transport/executor.js.map +1 -0
  168. package/lib/src/server/transports/{internal → system}/http.transport/index.d.ts +5 -5
  169. package/lib/src/server/transports/system/http.transport/index.d.ts.map +1 -0
  170. package/lib/src/server/transports/{internal → system}/http.transport/index.js +5 -5
  171. package/lib/src/server/transports/system/http.transport/index.js.map +1 -0
  172. package/lib/src/server/transports/system/http.transport/reply.d.ts +10 -0
  173. package/lib/src/server/transports/system/http.transport/reply.d.ts.map +1 -0
  174. package/lib/src/server/transports/{internal → system}/http.transport/reply.js +4 -4
  175. package/lib/src/server/transports/system/http.transport/reply.js.map +1 -0
  176. package/lib/src/server/transports/system/index.d.ts.map +1 -0
  177. package/lib/src/server/transports/system/index.js.map +1 -0
  178. package/lib/src/server/transports/{internal → system}/io.transport/context.d.ts +6 -6
  179. package/lib/src/server/transports/system/io.transport/context.d.ts.map +1 -0
  180. package/lib/src/server/transports/{internal → system}/io.transport/context.js +6 -6
  181. package/lib/src/server/transports/system/io.transport/context.js.map +1 -0
  182. package/lib/src/server/transports/{internal → system}/io.transport/executor.d.ts +3 -3
  183. package/lib/src/server/transports/system/io.transport/executor.d.ts.map +1 -0
  184. package/lib/src/server/transports/{internal → system}/io.transport/executor.js +3 -3
  185. package/lib/src/server/transports/system/io.transport/executor.js.map +1 -0
  186. package/lib/src/server/transports/system/io.transport/index.d.ts +14 -0
  187. package/lib/src/server/transports/system/io.transport/index.d.ts.map +1 -0
  188. package/lib/src/server/transports/{internal → system}/io.transport/index.js +5 -5
  189. package/lib/src/server/transports/system/io.transport/index.js.map +1 -0
  190. package/lib/src/server/transports/system/io.transport/reply.d.ts +10 -0
  191. package/lib/src/server/transports/system/io.transport/reply.d.ts.map +1 -0
  192. package/lib/src/server/transports/{internal → system}/io.transport/reply.js +4 -4
  193. package/lib/src/server/transports/system/io.transport/reply.js.map +1 -0
  194. package/lib/src/server/transports/system/utils.d.ts.map +1 -0
  195. package/lib/src/server/transports/system/utils.js.map +1 -0
  196. package/lib/src/server/transports/ws.transport/context.d.ts +13 -12
  197. package/lib/src/server/transports/ws.transport/context.d.ts.map +1 -1
  198. package/lib/src/server/transports/ws.transport/context.js +20 -28
  199. package/lib/src/server/transports/ws.transport/context.js.map +1 -1
  200. package/lib/src/server/transports/ws.transport/executor.d.ts +7 -5
  201. package/lib/src/server/transports/ws.transport/executor.d.ts.map +1 -1
  202. package/lib/src/server/transports/ws.transport/executor.js +77 -25
  203. package/lib/src/server/transports/ws.transport/executor.js.map +1 -1
  204. package/lib/src/server/transports/ws.transport/index.d.ts +3 -3
  205. package/lib/src/server/transports/ws.transport/index.d.ts.map +1 -1
  206. package/lib/src/server/transports/ws.transport/index.js +29 -54
  207. package/lib/src/server/transports/ws.transport/index.js.map +1 -1
  208. package/lib/src/server/types/index.d.ts +2 -4
  209. package/lib/src/server/types/index.d.ts.map +1 -1
  210. package/lib/src/utils/common.d.ts +6 -3
  211. package/lib/src/utils/common.d.ts.map +1 -1
  212. package/lib/src/utils/common.js +22 -11
  213. package/lib/src/utils/common.js.map +1 -1
  214. package/package.json +15 -6
  215. package/public/assets/{index-DoUtVarD.js → index-DoLXR0S_.js} +6 -1
  216. package/public/index.html +1 -1
  217. package/lib/bin/index.d.ts +0 -3
  218. package/lib/bin/index.d.ts.map +0 -1
  219. package/lib/bin/index.js +0 -32
  220. package/lib/bin/index.js.map +0 -1
  221. package/lib/src/server/transports/internal/http.transport/context.d.ts.map +0 -1
  222. package/lib/src/server/transports/internal/http.transport/context.js.map +0 -1
  223. package/lib/src/server/transports/internal/http.transport/executor.d.ts +0 -21
  224. package/lib/src/server/transports/internal/http.transport/executor.d.ts.map +0 -1
  225. package/lib/src/server/transports/internal/http.transport/executor.js.map +0 -1
  226. package/lib/src/server/transports/internal/http.transport/index.d.ts.map +0 -1
  227. package/lib/src/server/transports/internal/http.transport/index.js.map +0 -1
  228. package/lib/src/server/transports/internal/http.transport/reply.d.ts +0 -10
  229. package/lib/src/server/transports/internal/http.transport/reply.d.ts.map +0 -1
  230. package/lib/src/server/transports/internal/http.transport/reply.js.map +0 -1
  231. package/lib/src/server/transports/internal/index.d.ts.map +0 -1
  232. package/lib/src/server/transports/internal/index.js.map +0 -1
  233. package/lib/src/server/transports/internal/io.transport/context.d.ts.map +0 -1
  234. package/lib/src/server/transports/internal/io.transport/context.js.map +0 -1
  235. package/lib/src/server/transports/internal/io.transport/executor.d.ts.map +0 -1
  236. package/lib/src/server/transports/internal/io.transport/executor.js.map +0 -1
  237. package/lib/src/server/transports/internal/io.transport/index.d.ts +0 -14
  238. package/lib/src/server/transports/internal/io.transport/index.d.ts.map +0 -1
  239. package/lib/src/server/transports/internal/io.transport/index.js.map +0 -1
  240. package/lib/src/server/transports/internal/io.transport/reply.d.ts +0 -10
  241. package/lib/src/server/transports/internal/io.transport/reply.d.ts.map +0 -1
  242. package/lib/src/server/transports/internal/io.transport/reply.js.map +0 -1
  243. package/lib/src/server/transports/internal/utils.d.ts.map +0 -1
  244. package/lib/src/server/transports/internal/utils.js.map +0 -1
  245. /package/lib/src/server/transports/{internal → system}/index.d.ts +0 -0
  246. /package/lib/src/server/transports/{internal → system}/index.js +0 -0
  247. /package/lib/src/server/transports/{internal → system}/utils.d.ts +0 -0
  248. /package/lib/src/server/transports/{internal → system}/utils.js +0 -0
package/README.old.md ADDED
@@ -0,0 +1,1255 @@
1
+ <div align='center'>
2
+ <h1>Mock server</h1>
3
+ <p>Mock, match, modify and manipulate a HTTP request/response payload using flexible expectations with types</p>
4
+
5
+ <img src="https://raw.githubusercontent.com/n1k1t/mock-server/refs/heads/master/images/preview.png?raw=true" />
6
+
7
+ <br />
8
+ <br />
9
+
10
+ ![License](https://img.shields.io/badge/License-MIT-yellow.svg)
11
+ ![npm version](https://badge.fury.io/js/@n1k1t%2Fmock-server.svg)
12
+ ![Dynamic XML Badge](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fgithub.com%2Fn1k1t%2Fmock-server%2Fblob%2Fmaster%2Fcoverage%2Fcobertura-coverage.xml%3Fraw%3Dtrue&query=round(%2Fcoverage%2F%40line-rate%20*%201000)%20div%201000&label=coverage)
13
+ </div>
14
+
15
+ # Navigation
16
+
17
+ - [Basics](#basics)
18
+ - [How it works](#how-it-works)
19
+ - [Install](#install)
20
+ - [Start](#start)
21
+ - [GUI](#gui)
22
+ - [Mock](#mock)
23
+ - [Expectations](#expectations)
24
+ - [Schema](#schema)
25
+ - [Forwarding](#forwarding)
26
+ - [Context](#context)
27
+ - [Utils](#utils)
28
+ - [Operators](#operators)
29
+ - [Typings](#typings)
30
+ - [Storage](#storage)
31
+ - [Containers](#containers)
32
+ - [Cache](#cache)
33
+ - [State](#state)
34
+ - [Seeds](#seeds)
35
+ - [XML](#xml)
36
+ - [API](#api)
37
+ - [Ping](#ping)
38
+ - [Create expectation](#create-expectation)
39
+ - [Update expectation](#update-expectation)
40
+ - [Delete expectation](#delete-expectation)
41
+ - [Additional](#additional)
42
+ - [Configuration](#configuration)
43
+ - [Logger](#logger)
44
+ - [Meta](#meta)
45
+
46
+ # Basics
47
+
48
+ ## Install
49
+
50
+ ```bash
51
+ npm i @n1k1t/mock-server
52
+ ```
53
+
54
+ ## How it works
55
+
56
+ ![screenshot](https://raw.githubusercontent.com/n1k1t/mock-server/refs/heads/master/images/strategy.png?raw=true)
57
+
58
+ According on the picture above, main idea is to generate or modify response from some backend service. The mock server provides many scenarios to do that
59
+
60
+ **In case of mocking without request forwarding:**
61
+
62
+ 1. Start mock server (for example on `localhost:8080`)
63
+ 2. Register expectation using CLI (cURL) or application lib
64
+ 3. Make request to `localhost:8080/...`
65
+ 1. The mock server matches a request payload with registered expectations
66
+ 2. Build a response using an expectation configuration
67
+
68
+ **In case of mocking with request forwarding:**
69
+
70
+ 0. Lets imagine that you have a service that hosts on `localhost:8081`
71
+ 1. Start mock server (for example on `localhost:8080`)
72
+ 2. Register expectation using CLI (cURL) or application lib
73
+ 3. Make request to `localhost:8080/...`
74
+ 1. The mock server matches a request payload with registered expectations
75
+ 2. Next is forwarding a request payload to `localhost:8081/...`
76
+ 3. Using response fetched from `localhost:8081/...` the mock server builds a response
77
+
78
+ ## Start
79
+
80
+ ### CLI
81
+
82
+ ```bash
83
+ npx mock -h localhost -p 8080
84
+ ```
85
+
86
+ ### JavaScript
87
+
88
+ ```js
89
+ const { MockServer } = require('@n1k1t/mock-server');
90
+ MockServer.start({ host: 'localhost', port: 8080 });
91
+ ```
92
+
93
+ ### TypeScript
94
+
95
+ ```ts
96
+ import { MockServer } from '@n1k1t/mock-server';
97
+ MockServer.start({ host: 'localhost', port: 8080 });
98
+ ```
99
+
100
+ ## GUI
101
+
102
+ The mock server provides built-in web panel to track everything that is going through. There are two tabs `Expectations` and `History`
103
+
104
+ By default it can be found on `/_system/gui` of a host of mock server. Example: `localhost:8080/_system/gui`
105
+
106
+ Also it provides convenient util to navigate through payload of expectations and requests payload
107
+
108
+ ## Mock
109
+
110
+ Simple examples can be found in [expectation creation API](#create-expectation)
111
+
112
+ # Expectations
113
+
114
+ ## Schema
115
+
116
+ An expectation schema can contain some rules to handle `request`, `response` and `forward`
117
+
118
+ | Property | Nested | Type | Optional | Description |
119
+ |--|--|--|--|--|
120
+ | request | [Operators](#operators) | `object` | * | Describes a way to catch by request and how to manipulate it |
121
+ | response | [Operators](#operators) | `object` | * | Describes how to manipulate response. Also can be used to catch response in case of forwarding |
122
+ | forward | [Forwarding](#forwarding) | `object` | * | Describes configuration to forward a request to another host |
123
+
124
+ **Example**
125
+
126
+ ```ts
127
+ await server.client.createExpectation({
128
+ schema: {
129
+ request: {
130
+ $and: [],
131
+ },
132
+ response: {
133
+ $or: [],
134
+ },
135
+ forward: {
136
+ baseUrl: 'https://example.com',
137
+ url: '/some/path',
138
+ },
139
+ },
140
+ });
141
+ ```
142
+
143
+ ## Forwarding
144
+
145
+ | Property | Nested | Type | Optional | Description |
146
+ |--|--|--|--|--|
147
+ | url | | `string` | * | Absolute URL to target |
148
+ | baseUrl | | `string` | * | Base URL to target. The path will be provided from request |
149
+ | options | | `string` | * | Forwarding options |
150
+ | | host | `origin` | * | Provides `Host` header as same as mock server host (if not specified). If specified to `origin` then value for `Host` header will be taken from url |
151
+ | cache | | `object` | * | [Cache](#cache) configuration for a payload of forwarded requests |
152
+ | | storage | `redis` | * | Storage to read/write a cache |
153
+ | | key | `string` | * | Key to get read/write access of cached payload |
154
+ | | prefix | `string` | * | Prefix of the `key` of cache |
155
+ | | ttl | `number` | * | Time to live of cache in seconds |
156
+
157
+ ## Context
158
+
159
+ | Property | Nested | `$location` | Type | Optional | Description |
160
+ |--|--|--|--|--|--|
161
+ | storage | [Storage](#storage) | | `object` | | A storage of `container` entities |
162
+ | container | [Container](#containers) | `container` | `object` | * | A temporary cell in `storage`. Should be useful to sync expectations between each other or store and use any data each request |
163
+ | state | | `state` | `object` | | An [object](#state) with custom data |
164
+ | seed | | `seed` | `string` | * | Incoming request [seed](#seeds) |
165
+ | cache | | `cache` | `object` | | [Cache](#cache) configuration |
166
+ | | isEnabled | | `boolean` | | Toggle of cache usage |
167
+ | | key | | `string ∣ object` | * | Key to get read/write access of cached payload. Value provided as `object` will hashed using [FNV1A-64](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) algorithm |
168
+ | | prefix | | `string` | * | Prefix of the `key` of cache |
169
+ | | ttl | | `number` | * | Time to live of cache in seconds |
170
+ | incoming | | | `object` | | Payload with data of incoming request |
171
+ | | path | `path` | `string` | | Incoming request path |
172
+ | | method | `method` | `string` | | Incoming request method in **uppercase** |
173
+ | | headers | `incoming.headers` | `object` | | Incoming request headers with keys in **lowercase** |
174
+ | | dataRaw | `incoming.dataRaw` | `string` | | Incoming request source data |
175
+ | | data | `incoming.data` | `object` | * | Incoming request parsed data |
176
+ | | query | `incoming.query` | `object` | * | Incoming request query search parameters |
177
+ | | delay | `delay` | `number` | * | Delay that can be applied with [operators](#operators) |
178
+ | | error | `error` | `string` | * | Error that can be applied with [operators](#operators) |
179
+ | outgoing | | | `object` | | Payload with data of response |
180
+ | | status | `outgoing.status` | `number` | | Response status code |
181
+ | | headers | `outgoing.headers` | `number` | | Response headers |
182
+ | | dataRaw | `outgoing.dataRaw` | `string` | | Response source data |
183
+ | | data | `outgoing.data` | `any` | * | Response data |
184
+
185
+ ## Utils
186
+
187
+ Additional utils in `$exec` operator
188
+
189
+ | Property | Description |
190
+ |--|--|
191
+ | `context` | A request [context](#context) |
192
+ | `logger` | [Logger](#logger) of mock server |
193
+ | `mode` | A mode of expectation execution. Has `match` on catching request or `manipulate` on manipulation over [context](#context) |
194
+ | `meta` | A [meta](#meta) of a request |
195
+ | `_` | [Lodash](https://www.npmjs.com/package/lodash) |
196
+ | `d` | [DayJS](https://www.npmjs.com/package/dayjs) |
197
+ | `faker` | [Faker](https://www.npmjs.com/package/@faker-js/faker). Uses [seed](#seeds) if it was provided |
198
+
199
+ ## Operators
200
+
201
+ > **!NOTE** Each schema that using operators can have only one nested operator. To use more than one operator use `$and` or `$or` operators
202
+
203
+ | Operator | Optional | Description |
204
+ |--|--|--|
205
+ | [$has](#has) | * | Catches a request/response or checks a payload in [context](#context) |
206
+ | [$set](#set) | * | Sets payload in [context](#context) |
207
+ | [$merge](#merge) | * | Merges object payload in [context](#context) with provided `$value` |
208
+ | [$remove](#remove) | * | Removes payload in [context](#context) |
209
+ | [$exec](#exec) | * | Function to catch a request/response or check/manipulate payload in [context](#context) |
210
+ | [$and](#and) | * | Logical `and` |
211
+ | [$or](#or) | * | Logical `or` |
212
+ | [$not](#not) | * | Logical `not` |
213
+ | [$if](#if) | * | Logical `if` |
214
+ | [$switch](#switch) | * | Logical `switch/case` |
215
+
216
+ **Example**
217
+
218
+ ```ts
219
+ await server.client.createExpectation({
220
+ schema: {
221
+ request: {
222
+ $and: [
223
+ {
224
+ $has: {
225
+ $location: 'path',
226
+ $value: '/foo',
227
+ },
228
+ },
229
+ {
230
+ $has: {
231
+ $location: 'method',
232
+ $value: 'GET',
233
+ },
234
+ },
235
+ ],
236
+ },
237
+ },
238
+ });
239
+ ```
240
+
241
+ ### $has
242
+
243
+ > **!NOTE** `$exec` operators [have restrictions](#api) when it defined over `HTTP API` or `RemoteClient`
244
+
245
+ | Property | Type (application) | Type (cURL) | Optional | Description |
246
+ |--|--|--|--|--|
247
+ | $location | `string` [enum](#context) | `string` [enum](#context) | | Location that describes what [context](#context) entity is selecting for operator to work with |
248
+ | $path | `string` | `string` | * | Specifies a path to payload using [lodash get](https://lodash.com/docs/4.17.15#get) |
249
+ | $jsonPath | `string` | `string` | * | Specifies a path to payload using [JSON path](https://www.npmjs.com/package/jsonpath-plus) |
250
+ | $value | `any` | `any` | * | Checks by value equality in [context](#context) using `$location` (and `$path`, `$jsonPath` if it was specified) |
251
+ | $valueAnyOf | `any[]` | `any[]` | * | Checks by any of value equality in [context](#context) using `$location` (and `$path`, `$jsonPath` if it was specified) |
252
+ | $regExp | `RegExp` | `{ source: string, flags?: string }` | * | Checks by regular expression in context using `$location` (and `$path`, `$jsonPath` if it was specified) |
253
+ | $regExpAnyOf | `RegExp[]` | `{ source: string, flags?: string }[]` | * | Checks by any of regular expression in [context](#context) using `$location` (and `$path`, `$jsonPath` if it was specified) |
254
+ | $match | `string ∣ object` | `string ∣ object` | * | Checks by minimatch for `string` and `number` (example `/foo/*/bar` or `2**`) or similar `object` by passing object payload in [context](#context) using `$location` (and `$path`, `$jsonPath` if it was specified) |
255
+ | $matchAnyOf | `(string ∣ object)[]` | `(string ∣ object)[]` | * | Checks by any of minimatch for `string` and `number` (example `/foo/*/bar` or `2**`) or similar `object` by passing object payload in [context](#context) using `$location` (and `$path`, `$jsonPath` if it was specified) |
256
+ | $exec | `(payload, utils) => boolean` | `string` | * | Checks payload in [context](#context) by function with arguments where `payload` is selected entity using `$location` (and `$path`, `$jsonPath` if it was specified) and `utils` is [utils](#utils) |
257
+
258
+ **Example using application**
259
+
260
+ ```ts
261
+ await server.client.createExpectation({
262
+ schema: {
263
+ request: {
264
+ $has: {
265
+ $location: 'path',
266
+ $regExp: /^\/foo/,
267
+ },
268
+ },
269
+ },
270
+ });
271
+ ```
272
+
273
+ **Example using cURL**
274
+
275
+ ```bash
276
+ curl -H "Content-type: application/json" -X POST --location "localhost:8080/_system/expectations" --data-binary @- << EOF
277
+ {
278
+ "schema": {
279
+ "request": {
280
+ "\$has": {
281
+ "\$location": "method",
282
+ "\$regExp": { "source": "^\/foo" }
283
+ }
284
+ }
285
+ }
286
+ }
287
+ EOF
288
+ ```
289
+
290
+ ### $set
291
+
292
+ > **!NOTE** `$exec` operators [have restrictions](#api) when it defined over `HTTP API` or `RemoteClient`
293
+
294
+ | Property | Type (application) | Type (cURL) | Optional | Description |
295
+ |--|--|--|--|--|
296
+ | $location | `string` [enum](#context) | `string` [enum](#context) | | Location that describes what [context](#context) entity is selecting for operator to work with |
297
+ | $path | `string` | `string` | * | Specifies a path to payload using [lodash get](https://lodash.com/docs/4.17.15#get) |
298
+ | $jsonPath | `string` | `string` | * | Specifies a path to payload using [JSON path](https://www.npmjs.com/package/jsonpath-plus) |
299
+ | $value | `any` | `any` | * | Sets value to [context](#context) using `$location` (and `$path`, `$jsonPath` if it was specified) |
300
+ | $exec | `(payload, utils) => any` | `string` | * | Sets payload in [context](#context) by function with arguments where `payload` is selected entity using `$location` (and `$path`, `$jsonPath` if it was specified) and `utils` is [utils](#utils) |
301
+
302
+ **Example using application**
303
+
304
+ ```ts
305
+ await server.client.createExpectation({
306
+ schema: {
307
+ request: {
308
+ $set: {
309
+ $location: 'incoming.data',
310
+ $path: 'foo',
311
+ $exec: (payload, { _ }) => _.clamp(payload, 0, 10),
312
+ },
313
+ },
314
+ },
315
+ });
316
+ ```
317
+
318
+ **Example using cURL**
319
+
320
+ ```bash
321
+ curl -H "Content-type: application/json" -X POST --location "localhost:8080/_system/expectations" --data-binary @- << EOF
322
+ {
323
+ "schema": {
324
+ "request": {
325
+ "\$set": {
326
+ "\$location": "incoming.data",
327
+ "\$path": "foo",
328
+ "\$exec": "_.clamp(payload, 0, 10)"
329
+ }
330
+ }
331
+ }
332
+ }
333
+ EOF
334
+ ```
335
+
336
+ ### $merge
337
+
338
+ > **!NOTE** `$exec` operators [have restrictions](#api) when it defined over `HTTP API` or `RemoteClient`
339
+
340
+ | Property | Type (application) | Type (cURL) | Optional | Description |
341
+ |--|--|--|--|--|
342
+ | $location | `string` [enum](#context) | `string` [enum](#context) | | Location that describes what [context](#context) entity is selecting for operator to work with |
343
+ | $path | `string` | `string` | * | Specifies a path to payload using [lodash get](https://lodash.com/docs/4.17.15#get) |
344
+ | $jsonPath | `string` | `string` | * | Specifies a path to payload using [JSON path](https://www.npmjs.com/package/jsonpath-plus) |
345
+ | $value | `object` | `object` | * | Merges value in [context](#context) using `$location` (and `$path`, `$jsonPath` if it was specified) |
346
+ | $exec | `(payload, utils) => any` | `string` | * | Merges payload in [context](#context) by function with arguments where `payload` is selected entity using `$location` (and `$path`, `$jsonPath` if it was specified) and `utils` is [utils](#utils) |
347
+
348
+ **Example using application**
349
+
350
+ ```ts
351
+ await server.client.createExpectation({
352
+ schema: {
353
+ request: {
354
+ $merge: {
355
+ $location: 'incoming.data',
356
+ $value: { has_mocked: true },
357
+ },
358
+ },
359
+ },
360
+ });
361
+ ```
362
+
363
+ **Example using cURL**
364
+
365
+ ```bash
366
+ curl -H "Content-type: application/json" -X POST --location "localhost:8080/_system/expectations" --data-binary @- << EOF
367
+ {
368
+ "schema": {
369
+ "request": {
370
+ "\$merge": {
371
+ "\$location": "incoming.data",
372
+ "\$value": {"has_mocked": true}
373
+ }
374
+ }
375
+ }
376
+ }
377
+ EOF
378
+ ```
379
+
380
+ ### $remove
381
+
382
+ | Property | Type (application) | Type (cURL) | Optional | Description |
383
+ |--|--|--|--|--|
384
+ | $location | `string` [enum](#context) | `string` [enum](#context) | | Location that describes what [context](#context) entity is selecting for operator to work with |
385
+ | $path | `string` | `string` | * | Specifies a path to payload using [lodash get](https://lodash.com/docs/4.17.15#get) |
386
+ | $jsonPath | `string` | `string` | * | Specifies a path to payload using [JSON path](https://www.npmjs.com/package/jsonpath-plus) |
387
+
388
+ **Example using application**
389
+
390
+ ```ts
391
+ await server.client.createExpectation({
392
+ schema: {
393
+ request: {
394
+ $remove: { $location: 'outgoing.data' },
395
+ },
396
+ },
397
+ });
398
+ ```
399
+
400
+ **Example using cURL**
401
+
402
+ ```bash
403
+ curl -H "Content-type: application/json" -X POST --location "localhost:8080/_system/expectations" --data-binary @- << EOF
404
+ {
405
+ "schema": {
406
+ "request": {
407
+ "\$remove": {"\$location": "outgoing.data"}
408
+ }
409
+ }
410
+ }
411
+ EOF
412
+ ```
413
+
414
+ ### $exec
415
+
416
+ > **!NOTE** `$exec` operators [have restrictions](#api) when it defined over `HTTP API` or `RemoteClient`
417
+
418
+ | Type (application) | Type (cURL) | Description |
419
+ |--|--|--|
420
+ | `(utils) => boolean ∣ unknown` | `string` | Does something you want or catch request/response payload in [context](#context) by function with arguments where `utils` is [utils](#utils) |
421
+
422
+ **Example using application**
423
+
424
+ ```ts
425
+ await server.client.createExpectation({
426
+ schema: {
427
+ request: {
428
+ $exec: ({ context, logger }) => {
429
+ logger.info(context);
430
+ return context.incoming.path === '/foo';
431
+ },
432
+ },
433
+ },
434
+ });
435
+ ```
436
+
437
+ **Example using cURL**
438
+
439
+ ```bash
440
+ curl -H "Content-type: application/json" -X POST --location "localhost:8080/_system/expectations" --data-binary @- << EOF
441
+ {
442
+ "schema": {
443
+ "request": {
444
+ "\$exec": "{ logger.info(context); return context.incoming.path === '/foo' }"
445
+ }
446
+ }
447
+ }
448
+ EOF
449
+ ```
450
+
451
+ ### $and
452
+
453
+ | Type (application) | Type (cURL) | Description |
454
+ |--|--|--|
455
+ | `object[]` | `object[]` | Provides [operators](#operators) schemas |
456
+
457
+ **Example using application**
458
+
459
+ ```ts
460
+ await server.client.createExpectation({
461
+ schema: {
462
+ request: {
463
+ $and: [
464
+ { $has: { $location: 'path', $match: 'foo/*' } },
465
+ { $has: { $location: 'method', $valueAnyOf: ['GET', 'POST'] } },
466
+ ],
467
+ },
468
+ },
469
+ });
470
+ ```
471
+
472
+ **Example using cURL**
473
+
474
+ ```bash
475
+ curl -H "Content-type: application/json" -X POST --location "localhost:8080/_system/expectations" --data-binary @- << EOF
476
+ {
477
+ "schema": {
478
+ "request": {
479
+ "\$and": [
480
+ {"\$has": {"\$location": "path", "\$match": "foo/*"}},
481
+ {"\$has": {"\$location": "method", "\$valueAnyOf": ["GET", "POST"]}}
482
+ ]
483
+ }
484
+ }
485
+ }
486
+ EOF
487
+ ```
488
+
489
+ ### $or
490
+
491
+ | Type (application) | Type (cURL) | Description |
492
+ |--|--|--|
493
+ | `object[]` | `object[]` | Provides [operators](#operators) schemas |
494
+
495
+ **Example using application**
496
+
497
+ ```ts
498
+ await server.client.createExpectation({
499
+ schema: {
500
+ request: {
501
+ $or: [
502
+ { $has: { $location: 'path', $match: 'foo/*' } },
503
+ { $has: { $location: 'method', $valueAnyOf: ['GET', 'POST'] } },
504
+ ],
505
+ },
506
+ },
507
+ });
508
+ ```
509
+
510
+ **Example using cURL**
511
+
512
+ ```bash
513
+ curl -H "Content-type: application/json" -X POST --location "localhost:8080/_system/expectations" --data-binary @- << EOF
514
+ {
515
+ "schema": {
516
+ "request": {
517
+ "\$or": [
518
+ {"\$has": {"\$location": "path", "\$match": "foo/*"}},
519
+ {"\$has": {"\$location": "method", "\$valueAnyOf": ["GET", "POST"]}}
520
+ ]
521
+ }
522
+ }
523
+ }
524
+ EOF
525
+ ```
526
+
527
+ ### $not
528
+
529
+ | Type (application) | Type (cURL) | Description |
530
+ |--|--|--|
531
+ | `object` | `object` | Provides an [operators](#operators) schema |
532
+
533
+ **Example using application**
534
+
535
+ ```ts
536
+ await server.client.createExpectation({
537
+ schema: {
538
+ request: {
539
+ $not: { $has: { $location: 'path', $match: 'foo/*' } },
540
+ },
541
+ },
542
+ });
543
+ ```
544
+
545
+ **Example using cURL**
546
+
547
+ ```bash
548
+ curl -H "Content-type: application/json" -X POST --location "localhost:8080/_system/expectations" --data-binary @- << EOF
549
+ {
550
+ "schema": {
551
+ "request": {
552
+ "\$not": {"\$has": {"\$location": "path", "\$match": "foo/*"}}
553
+ }
554
+ }
555
+ }
556
+ EOF
557
+ ```
558
+
559
+ ### $if
560
+
561
+ | Property | Type (application) | Type (cURL) | Optional | Description |
562
+ |--|--|--|--|--|
563
+ | $condition | `object` | `object` | | Condition to check. Should contain one of `$and`, `$exec`, `$has`, `$or` or `$not` [operators](#operators) schema |
564
+ | $then | `object` | `object` | * | Logical `then`. Should contain an [operators](#operators) schema |
565
+ | $else | `object` | `object` | * | Logical `else`. Should contain an [operators](#operators) schema |
566
+
567
+ **Example using application**
568
+
569
+ ```ts
570
+ await server.client.createExpectation({
571
+ schema: {
572
+ request: {
573
+ $if: {
574
+ $condition: { $has: { $location: 'path', $match: 'foo/*' } },
575
+ $then: { $set: { $location: 'delay', $value: 5000 } },
576
+ $else: { $set: { $location: 'error', $value: 'ECONNABORTED' } },
577
+ },
578
+ },
579
+ },
580
+ });
581
+ ```
582
+
583
+ **Example using cURL**
584
+
585
+ ```bash
586
+ curl -H "Content-type: application/json" -X POST --location "localhost:8080/_system/expectations" --data-binary @- << EOF
587
+ {
588
+ "schema": {
589
+ "request": {
590
+ "\$if": {
591
+ "\$condition": {"\$has": {"\$location": "path", "\$match": "foo/*"}},
592
+ "\$then": {"\$set": {"\$location": "delay", "\$value": 5000}},
593
+ "\$else": {"\$set": {"\$location": "error", "\$value": "ECONNABORTED"}}
594
+ }
595
+ }
596
+ }
597
+ }
598
+ EOF
599
+ ```
600
+
601
+ ### $switch
602
+
603
+ > **!NOTE** `$exec` operators [have restrictions](#api) when it defined over `HTTP API` or `RemoteClient`
604
+
605
+ | Property | Type (application) | Type (cURL) | Optional | Description |
606
+ |--|--|--|--|--|
607
+ | $location | `string` [enum](#context) | `string` [enum](#context) | | Location that describes what [context](#context) entity is selecting for operator to work with |
608
+ | $cases | `Record<string ∣ number, object>` | `Record<string ∣ number, object>` | | An object where `key` is an extracted value from [enum](#context) using `$location` (and `$path`, `$exec` if it was specified) and `value` is an [operators](#operators) schema |
609
+ | $default | `object` | `object` | * | Default behavior as an [operators](#operators) schema |
610
+ | $path | `string` | `string` | * | Specifies a path to payload using [lodash get](https://lodash.com/docs/4.17.15#get) |
611
+ | $exec | `(payload, utils) => any` | `string` | * | Sets payload in [context](#context) by function with arguments where `payload` is selected entity using `$location` and `utils` is [utils](#utils) |
612
+
613
+ **Example using application**
614
+
615
+ ```ts
616
+ await server.client.createExpectation({
617
+ schema: {
618
+ request: {
619
+ $switch: {
620
+ $location: 'method',
621
+ $cases: {
622
+ 'GET': { $set: { $location: 'delay', $value: 2000 } },
623
+ 'POST': { $set: { $location: 'delay', $value: 5000 } },
624
+ },
625
+ $default: {
626
+ $set: { $location: 'error', $value: 'ECONNABORTED' }
627
+ },
628
+ },
629
+ },
630
+ },
631
+ });
632
+ ```
633
+
634
+ **Example using cURL**
635
+
636
+ ```bash
637
+ curl -H "Content-type: application/json" -X POST --location "localhost:8080/_system/expectations" --data-binary @- << EOF
638
+ {
639
+ "schema": {
640
+ "request": {
641
+ "\$switch": {
642
+ "\$location": "method",
643
+ "\$cases": {
644
+ "GET": {"\$set": {"\$location": "delay", "\$value": 2000}},
645
+ "POST": {"\$set": {"\$location": "delay", "\$value": 5000}}
646
+ },
647
+ "\$default": {
648
+ "\$set": {"\$location": "error", "\$value": "ECONNABORTED"}
649
+ }
650
+ }
651
+ }
652
+ }
653
+ }
654
+ EOF
655
+ ```
656
+
657
+ ## Typings
658
+
659
+ The application client lib provides approach to keep typings using function predicate to `create` or `update` expectation with a generic argument. The generic type should have the same schema like [context](#context)
660
+
661
+ The function predicate provides an object argument with `$` that contains simplified API to build typed expectation schemas. Some operators have `using` predicate that can contain `$path`, `$jsonPath` or `$exec` selectors
662
+
663
+ **Examples**
664
+
665
+ ```ts
666
+ await client.createExpectation<{
667
+ incoming: {
668
+ query: {
669
+ foo: 'a' | 'b' | 'c';
670
+ bar?: string;
671
+ };
672
+ };
673
+ }>(({ $ }) => ({
674
+ schema: {
675
+ request: $.or([
676
+ $.has('incoming.query', '$path', 'foo', { $value: 'a' }),
677
+ $.has('incoming.query', { $match: { foo: 'b' } }),
678
+ ]),
679
+ },
680
+ }));
681
+ ```
682
+
683
+ ```ts
684
+ await client.createExpectation<{
685
+ incoming: {
686
+ query: {
687
+ foo: 'a' | 'b' | 'c';
688
+ bar?: string;
689
+ };
690
+ };
691
+ outgoing: {
692
+ data: {
693
+ foo: 'a' | 'b' | 'c';
694
+ bar?: {
695
+ baz: 'a' | 'b' | 'c';
696
+ };
697
+ };
698
+ };
699
+ }>(({ $ }) => ({
700
+ schema: {
701
+ response: $.and([
702
+ $.switch('incoming.query', '$exec', (payload) => payload.foo, {
703
+ $cases: {
704
+ 'a': $.set('outgoing.data', '$path', 'bar.baz', { $value: 'a' }),
705
+ 'b': $.set('outgoing.data', '$path', 'bar.baz', { $value: 'b' }),
706
+ },
707
+ }),
708
+
709
+ $.switch('incoming.query', '$path', 'bar', {
710
+ $cases: {
711
+ 'something': $.set('outgoing.data', '$path', 'bar.baz', { $value: 'c' }),
712
+ },
713
+ }),
714
+ ]),
715
+ },
716
+ }));
717
+ ```
718
+
719
+ ## Storage
720
+
721
+ Storage is a temporary storage that provides an access to read/write [containers](#containers)
722
+
723
+ | Property | Type | Description |
724
+ |--|--|--|
725
+ | find | `(key: string ∣ object) => Container ∣ null` | Finds a container in storage. Every `key` provided as `object` will hashed using [FNV1A-64](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) algorithm |
726
+ | delete | `(key: string ∣ object) => Container ∣ null` | Deletes a container in storage. Every `key` provided as `object` will hashed using [FNV1A-64](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) algorithm |
727
+ | register | `(configuration: Container) => Container` | Registers a container in storage (overrides if existent) |
728
+ | provide | `(configuration: Container) => Container` | Finds or registers a container in storage |
729
+
730
+ As a temporary storage it has a job to garbage an expired containers. Use `containers.expiredCleaningInterval` to setup an interval of clearance in [configuration](#configuration)
731
+
732
+ > **!NOTE** See example of usage in [containers](#containers) section below
733
+
734
+ ## Containers
735
+
736
+ | Property | Type | Description |
737
+ |--|--|--|
738
+ | key | `string` | A key of container |
739
+ | prefix | `string` | A prefix of container |
740
+ | payload | `object` | An object with custom data |
741
+ | ttl | `number` | Time to live of container in seconds **(default: 1h)** |
742
+ | expiresAt | `number` | An expiration date/time as unix timestamp with milliseconds |
743
+ | bind | `(key: string ∣ object) => Container` | Binds a container to one more key. Every `key` provided as `object` will hashed using [FNV1A-64](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) algorithm |
744
+ | unbind | `(key: string ∣ object) => Container` | Unbinds a container from key. Every `key` provided as `object` will hashed using [FNV1A-64](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) algorithm |
745
+ | assign | `(payload: object ∣ (payload: object) => object) => Container` | Uses as payload predicate to assign payload values to existent |
746
+ | merge | `(payload: object ∣ (payload: object) => object) => Container` | Uses as payload predicate to deep merge of payload values with existent |
747
+
748
+
749
+ **Example**
750
+
751
+ ```ts
752
+ await client.createExpectation<{
753
+ container: {
754
+ counter: number;
755
+ };
756
+ }>(({ $ }) => ({
757
+ schema: {
758
+ request: $.set('container', {
759
+ $exec: (container, { context }) => context.storage
760
+ .provide({ key: 'foo', payload: { counter: 0 } })
761
+ .assign((payload) => ({ counter: payload.counter + 1 }))
762
+ }),
763
+
764
+ response: $.set('outgoing.data', {
765
+ $exec: (payload, { context }) => ({
766
+ count: context.container!.payload.counter,
767
+ }),
768
+ }),
769
+ },
770
+ }));
771
+ ```
772
+
773
+ ## Cache
774
+
775
+ > **!NOTE** Cache is usable **only** to store a payload of forwarded requests
776
+
777
+ To work with cache the mock server uses [ioredis](https://www.npmjs.com/package/ioredis) package
778
+
779
+ To configure it use `database.redis` configuration on the mock server start options
780
+
781
+ **Example**
782
+
783
+ ```ts
784
+ const server = await MockServer.start({
785
+ host: 'localhost',
786
+ port: 8080,
787
+
788
+ databases: {
789
+ redis: {
790
+ host: 'localhost',
791
+ port: 6379,
792
+ },
793
+ },
794
+ });
795
+ ```
796
+
797
+ **How it works in steps?**
798
+
799
+ 0. [Expectation schema](#schema) should have `forward` configuration specified
800
+ 1. Preparing incoming request...
801
+ 2. Preparing [request schema](#schema) in expectation...
802
+ 3. Setting up cache configuration from [context](#context) or [forward.cache](#forwarding)...
803
+ 4. If `cache.isEnabled` is equals `true` the mock server checks a cache using provided configuration
804
+ 5. If `key` was not provided a key for cache will calculated with `path`, `method`, `data` and `query` property values using [FNV1A-64](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) algorithm
805
+ 6. If cache was found then step `7` is skipping
806
+ 7. Forwarding a request....
807
+ 8. Preparing [response schema](#schema) in expectation...
808
+ 9. Setting up cache configuration from [context](#context)...
809
+ 10. If `cache.isEnabled` is equals `true` the mock server will write a cache over provided `ttl`
810
+ 11. Replying...
811
+
812
+ **Example**
813
+
814
+ ```ts
815
+ await client.createExpectation(({ $ }) => ({
816
+ schema: {
817
+ response: $.set('cache', '$path', 'isEnabled', {
818
+ $exec: (payload, { context }) => context.outgoing.status < 400,
819
+ }),
820
+
821
+ forward: {
822
+ baseUrl: 'https://example.com',
823
+
824
+ cache: {
825
+ ttl: 30 * 24 * 60 * 60,
826
+ },
827
+ },
828
+ },
829
+ }));
830
+ ```
831
+
832
+ ## State
833
+
834
+ State is a unique storage of each request. It can be used to handle complex expectations
835
+
836
+ By default an object of state extracts from `X-Use-Mock-State` in `incoming.headers` (as serialized json in **base64 encoding**) or creates an empty object
837
+
838
+ **Example**
839
+
840
+ ```ts
841
+ await client.createExpectation<{
842
+ state: {
843
+ id?: number;
844
+ };
845
+ incoming: {
846
+ query: {
847
+ foo: 'a' | 'b' | 'c';
848
+ };
849
+ };
850
+ outgoing: {
851
+ data: {
852
+ id: number;
853
+ };
854
+ };
855
+ }>(({ $ }) => ({
856
+ schema: {
857
+ request: $.and([
858
+ $.switch('incoming.query', '$exec', (payload) => payload.foo, {
859
+ $cases: {
860
+ 'a': $.set('state', '$path', 'id', { $value: 1 }),
861
+ 'b': $.set('state', '$path', 'id', { $value: 2 }),
862
+ },
863
+ }),
864
+ ]),
865
+ response: $.set('outgoing.data', {
866
+ $exec: (payload, { state }) => ({ id: state.id ?? 0 }),
867
+ }),
868
+ },
869
+ }));
870
+ ```
871
+
872
+ ## Seeds
873
+
874
+ Seeds can help to generate content with the same values each request using [faker](https://www.npmjs.com/package/@faker-js/faker)
875
+
876
+ By default a number of seed takes from `X-Use-Mock-Seed` in `incoming.headers`
877
+
878
+ **Example**
879
+
880
+ ```ts
881
+ await client.createExpectation(({ $ }) => ({
882
+ schema: {
883
+ request: $.and([
884
+ $.set('seed', { $exec: (seed) => seed ?? 123 }),
885
+ ]),
886
+ response: $.set('outgoing.data', {
887
+ $exec: (payload, { faker }) => ({
888
+ id: faker.number.int({ max: 1000, min: 500 }),
889
+ first_name: faker.person.firstName('male'),
890
+ last_name: faker.person.lastName('male'),
891
+ }),
892
+ }),
893
+ },
894
+ }));
895
+ ```
896
+
897
+ ## XML
898
+
899
+ The mock server uses the [fast-xml-parser](https://www.npmjs.com/package/fast-xml-parser) package to parse and serialize XML payload with options:
900
+
901
+ ```ts
902
+ {
903
+ ignoreAttributes: false,
904
+ }
905
+ ```
906
+
907
+ To define a `incoming.data` as XML in incoming request `incoming.headers` should have `Content-Type: application/xml`.
908
+
909
+ The same with `outgoing.data` and `outgoing.headers`
910
+
911
+ **Example of serialized XML**
912
+
913
+ ```xml
914
+ <tag type="default">
915
+ <nested type="nested">456</nested>
916
+ 123
917
+ </tag>
918
+ ```
919
+
920
+ **Example of parsed XML**
921
+
922
+ ```json
923
+ {
924
+ "tag":{
925
+ "nested":{
926
+ "#text":456,
927
+ "@_type":"nested"
928
+ },
929
+ "#text":123,
930
+ "@_type":"default"
931
+ }
932
+ }
933
+ ```
934
+
935
+ To parse an XML manually the application lib provides utils:
936
+
937
+ ```ts
938
+ import { parsePayload, serializePayload } from '@n1k1t/mock-server';
939
+
940
+ const parsed = parsePayload('xml', '<tag>123</tag>'); // { tag: 123 }
941
+ const serialized = serializePayload('xml', parsed); // '<tag>123</tag>'
942
+ ```
943
+
944
+ # API
945
+
946
+ The mock server provides 3 different ways to work with. There are: `HTTP API` (eg using cURL), `RemoteClient` provided by application lib to connect and work with existent mock server on another host and `MockServer.client` on the same host (application script)
947
+
948
+ The `HTTP API` and `RemoteClient` have some usage restrictions like:
949
+ - Every `$exec` operator **cannot have an access to variables outside the function**. If you need to use some extra variables or modules that implemented in outer scope you have to use the `MockServer.client` to setup everything on the mock server side host
950
+ - Plugins are not supported
951
+
952
+ ## Ping
953
+
954
+ `INPUT` → `GET /_system/ping`
955
+
956
+ `OUTPUT`
957
+
958
+ | Type | Description |
959
+ |--|--|
960
+ | `string` | A `pong` message |
961
+
962
+ **Using cURL**
963
+
964
+ ```bash
965
+ curl -H "Content-type: application/json" --location "localhost:8080/_system/ping"
966
+ ```
967
+
968
+ **Using application lib on server side**
969
+
970
+ ```ts
971
+ import { MockServer } from '@n1k1t/mock-server';
972
+
973
+ const server = await MockServer.start({ host: 'localhost', port: 8080 });
974
+ await server.client.ping();
975
+ ```
976
+
977
+ **Using application lib on remotely**
978
+
979
+ ```ts
980
+ import { RemoteClient } from '@n1k1t/mock-server';
981
+
982
+ const client = await RemoteClient.connect({ host: 'localhost', port: 8080 });
983
+ await client.ping();
984
+ ```
985
+
986
+ ## Create expectation
987
+
988
+ `INPUT` → `POST /_system/expectations`
989
+
990
+ | Property | Nested | Type | Optional | Description |
991
+ |--|--|--|--|--|
992
+ | schema | [Schema](#schema) | `object` | | An expectation schema |
993
+ | name | | `string` | * | A preferred name for an expectation |
994
+
995
+ `OUTPUT`
996
+
997
+ | Property | Nested | Type | Optional | Description |
998
+ |--|--|--|--|--|
999
+ | id | | `string` | | An expectation ID |
1000
+ | name | | `string` | | An expectation name |
1001
+ | schema | [Schema](#schema) | `object` | | Provided schema |
1002
+
1003
+ **Using cURL**
1004
+
1005
+ ```bash
1006
+ curl -H "Content-type: application/json" -X POST --location "localhost:8080/_system/expectations" --data-binary @- << EOF
1007
+ {
1008
+ "schema": {
1009
+ "request": {
1010
+ "\$has": {
1011
+ "\$location": "method",
1012
+ "\$value": "GET"
1013
+ }
1014
+ }
1015
+ }
1016
+ }
1017
+ EOF
1018
+ ```
1019
+
1020
+ **Using application lib on server side**
1021
+
1022
+ ```ts
1023
+ import { MockServer } from '@n1k1t/mock-server';
1024
+
1025
+ const server = await MockServer.start({ host: 'localhost', port: 8080 });
1026
+ const expectation = await server.client.createExpectation({
1027
+ schema: {
1028
+ request: {
1029
+ $has: {
1030
+ $location: 'method',
1031
+ $value: 'GET',
1032
+ },
1033
+ },
1034
+ },
1035
+ });
1036
+
1037
+ console.log('Mock expectation has created', expectation.id);
1038
+ ```
1039
+
1040
+ **Using application lib on remotely**
1041
+
1042
+ ```ts
1043
+ import { RemoteClient } from '@n1k1t/mock-server';
1044
+
1045
+ const client = await RemoteClient.connect({ host: 'localhost', port: 8080 });
1046
+ const expectation = await client.createExpectation({
1047
+ schema: {
1048
+ request: {
1049
+ $has: {
1050
+ $location: 'method',
1051
+ $value: 'GET',
1052
+ },
1053
+ },
1054
+ },
1055
+ });
1056
+
1057
+ console.log('Mock expectation has created', expectation.id);
1058
+ ```
1059
+
1060
+ ## Update expectation
1061
+
1062
+ `INPUT` → `PUT /_system/expectations`
1063
+
1064
+ | Property | Nested | Type | Optional | Description |
1065
+ |--|--|--|--|--|
1066
+ | id | | `string` | | ID of a registered expectation |
1067
+ | set | | `object` | | A payload to set |
1068
+ | | name | `string` | * | A preferred name for an expectation |
1069
+ | | schema | [Schema](#schema) | * | An expectation schema |
1070
+
1071
+ `OUTPUT`
1072
+
1073
+ | Property | Nested | Type | Optional | Description |
1074
+ |--|--|--|--|--|
1075
+ | id | | `string` | | An expectation ID |
1076
+ | name | | `string` | | An expectation name |
1077
+ | schema | [Schema](#schema) | `object` | | Provided schema |
1078
+
1079
+ **Using cURL**
1080
+
1081
+ ```bash
1082
+ curl -H "Content-type: application/json" -X PUT --location "localhost:8080/_system/expectations" --data-binary @- << EOF
1083
+ {
1084
+ "id": "...",
1085
+ "set": {"name": "The expectation"}
1086
+ }
1087
+ EOF
1088
+ ```
1089
+
1090
+ **Using application lib on server side**
1091
+
1092
+ ```ts
1093
+ import { MockServer } from '@n1k1t/mock-server';
1094
+
1095
+ const server = await MockServer.start({ host: 'localhost', port: 8080 });
1096
+ const expectation = await server.client.updateExpectation({
1097
+ id: '...',
1098
+ set: { name: 'The expectation' }
1099
+ });
1100
+
1101
+ console.log('Mock expectation has updated', expectation);
1102
+ ```
1103
+
1104
+ **Using application lib on remotely**
1105
+
1106
+ ```ts
1107
+ import { RemoteClient } from '@n1k1t/mock-server';
1108
+
1109
+ const client = await RemoteClient.connect({ host: 'localhost', port: 8080 });
1110
+ const expectation = await client.updateExpectation({
1111
+ id: '...',
1112
+ set: { name: 'The expectation' }
1113
+ });
1114
+
1115
+ console.log('Mock expectation has updated', expectation);
1116
+ ```
1117
+
1118
+ ## Delete expectation
1119
+
1120
+ `INPUT` → `DELETE /_system/expectations`
1121
+
1122
+ | Property | Nested | Type | Optional | Description |
1123
+ |--|--|--|--|--|
1124
+ | ids | | `string[]` | * | An expectation IDs list to delete. Or **delete all expectations** if not provided |
1125
+
1126
+ **Using cURL**
1127
+
1128
+ ```bash
1129
+ curl -H "Content-type: application/json" -X DELETE --location "localhost:8080/_system/expectations" --data-binary @- << EOF
1130
+ {
1131
+ "ids": ["..."]
1132
+ }
1133
+ EOF
1134
+ ```
1135
+
1136
+ **Using application lib on server side**
1137
+
1138
+ ```ts
1139
+ import { MockServer } from '@n1k1t/mock-server';
1140
+
1141
+ const server = await MockServer.start({ host: 'localhost', port: 8080 });
1142
+ await server.client.deleteExpectations({
1143
+ ids: ['...'],
1144
+ });
1145
+ ```
1146
+
1147
+ **Using application lib on remotely**
1148
+
1149
+ ```ts
1150
+ import { RemoteClient } from '@n1k1t/mock-server';
1151
+
1152
+ const client = await RemoteClient.connect({ host: 'localhost', port: 8080 });
1153
+ await client.deleteExpectations({
1154
+ ids: ['...'],
1155
+ });
1156
+ ```
1157
+
1158
+ # Additional
1159
+
1160
+ ## Configuration
1161
+
1162
+ > **!NOTE** Configuration must be provided in the same script like mock server
1163
+
1164
+ ```ts
1165
+ import { config } from '@n1k1t/mock-server';
1166
+
1167
+ config.merge({
1168
+ logger: {
1169
+ level: 'D', // Logger level (default: D)
1170
+ },
1171
+
1172
+ history: {
1173
+ limit: 100, // Limit for history of requests (default: 100)
1174
+ },
1175
+
1176
+ containers: {
1177
+ expiredCleaningInterval: 60 * 60, // Expired containers cleaning interval in seconds (default: 1h)
1178
+ },
1179
+ });
1180
+ ```
1181
+
1182
+ ## Logger
1183
+
1184
+ > **!NOTE** Configuration must be provided in the same script like mock server
1185
+
1186
+ ```ts
1187
+ import { Logger } from '@n1k1t/mock-server';
1188
+
1189
+ // It defines your own logger methods
1190
+ Logger.useExternal({
1191
+ debug: (...messages: string[]) => console.debug(...messages),
1192
+ info: (...messages: string[]) => console.log(...messages),
1193
+ warn: (...messages: string[]) => console.warn(...messages),
1194
+ error: (...messages: string[]) => console.error(...messages),
1195
+ fatal: (...messages: string[]) => console.error(...messages),
1196
+ });
1197
+
1198
+ // It defines a JSON serializers to mask some private data by keys on objects
1199
+ Logger.useSerializers({
1200
+ cvv: () => '***',
1201
+ card: (payload: string) => payload.slice(0, 8) + 'xxxx',
1202
+ });
1203
+ ```
1204
+
1205
+ ## Meta
1206
+
1207
+ Some loggers (like `banyan` and etc) provide a meta context for logs with some data. To keep a meta contexts between requests the mock server provides a `metaStorage` using native node `AsyncLocalStorage`.
1208
+
1209
+ The `metaStorage.provide()` returns an instance of `meta` that contains basic data like:
1210
+
1211
+ | Property | Type | Optional | Description |
1212
+ |--|--|--|--|
1213
+ | operationId | `string` | | UUID v4 |
1214
+ | requestId | `string` | * | `X-Request-Id` from `incoming.headers` |
1215
+
1216
+ **Setup**
1217
+
1218
+ ```ts
1219
+ import { Logger, metaStorage } from '@n1k1t/mock-server';
1220
+
1221
+ // Some external logger with meta context support
1222
+ const external = {...};
1223
+
1224
+ // It defines your own logger methods
1225
+ Logger.useExternal({
1226
+ debug: (...messages: string[]) => external.debug(metaStorage.provide(), ...messages),
1227
+ info: (...messages: string[]) => external.log(metaStorage.provide(), ...messages),
1228
+ warn: (...messages: string[]) => external.warn(metaStorage.provide(), ...messages),
1229
+ error: (...messages: string[]) => external.error(metaStorage.provide(), ...messages),
1230
+ fatal: (...messages: string[]) => external.error(metaStorage.provide(), ...messages),
1231
+ });
1232
+ ```
1233
+
1234
+ **Usage**
1235
+
1236
+ ```ts
1237
+ await server.client.createExpectation({
1238
+ schema: {
1239
+ request: {
1240
+ $exec: ({ context, logger }) => {
1241
+ // Here logger should have a meta context like { operationId: '...' }
1242
+ logger.info('Before')
1243
+ },
1244
+ $exec: ({ context, logger, meta }) => {
1245
+ // It enriches meta context for further logs of request
1246
+ meta.merge({ foo: 'bar' });
1247
+ },
1248
+ $exec: ({ context, logger, meta }) => {
1249
+ // Now logger should have a meta context like { foo: 'bar', operationId: '...' }
1250
+ logger.info('After')
1251
+ },
1252
+ },
1253
+ },
1254
+ });
1255
+ ```