1
0

mockServiceWorker.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. /* eslint-disable */
  2. /* tslint:disable */
  3. /**
  4. * Mock Service Worker.
  5. * @see https://github.com/mswjs/msw
  6. * - Please do NOT modify this file.
  7. */
  8. const PACKAGE_VERSION = '2.15.0'
  9. const INTEGRITY_CHECKSUM = '03cb67ac84128e63d7cd722a6e5b7f1e'
  10. const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
  11. const activeClientIds = new Set()
  12. addEventListener('install', function () {
  13. self.skipWaiting()
  14. })
  15. addEventListener('activate', function (event) {
  16. event.waitUntil(self.clients.claim())
  17. })
  18. addEventListener('message', async function (event) {
  19. const clientId = Reflect.get(event.source || {}, 'id')
  20. if (!clientId || !self.clients) {
  21. return
  22. }
  23. const client = await self.clients.get(clientId)
  24. if (!client) {
  25. return
  26. }
  27. const allClients = await self.clients.matchAll({
  28. type: 'window',
  29. })
  30. switch (event.data) {
  31. case 'KEEPALIVE_REQUEST': {
  32. sendToClient(client, {
  33. type: 'KEEPALIVE_RESPONSE',
  34. })
  35. break
  36. }
  37. case 'INTEGRITY_CHECK_REQUEST': {
  38. sendToClient(client, {
  39. type: 'INTEGRITY_CHECK_RESPONSE',
  40. payload: {
  41. packageVersion: PACKAGE_VERSION,
  42. checksum: INTEGRITY_CHECKSUM,
  43. },
  44. })
  45. break
  46. }
  47. case 'MOCK_ACTIVATE': {
  48. activeClientIds.add(clientId)
  49. sendToClient(client, {
  50. type: 'MOCKING_ENABLED',
  51. payload: {
  52. client: {
  53. id: client.id,
  54. frameType: client.frameType,
  55. },
  56. },
  57. })
  58. break
  59. }
  60. case 'CLIENT_CLOSED': {
  61. activeClientIds.delete(clientId)
  62. const remainingClients = allClients.filter((client) => {
  63. return client.id !== clientId
  64. })
  65. // Unregister itself when there are no more clients
  66. if (remainingClients.length === 0) {
  67. self.registration.unregister()
  68. }
  69. break
  70. }
  71. }
  72. })
  73. addEventListener('fetch', function (event) {
  74. const requestInterceptedAt = Date.now()
  75. // Bypass navigation requests.
  76. if (event.request.mode === 'navigate') {
  77. return
  78. }
  79. // Opening the DevTools triggers the "only-if-cached" request
  80. // that cannot be handled by the worker. Bypass such requests.
  81. if (
  82. event.request.cache === 'only-if-cached' &&
  83. event.request.mode !== 'same-origin'
  84. ) {
  85. return
  86. }
  87. // Bypass all requests when there are no active clients.
  88. // Prevents the self-unregistered worked from handling requests
  89. // after it's been terminated (still remains active until the next reload).
  90. if (activeClientIds.size === 0) {
  91. return
  92. }
  93. const requestId = crypto.randomUUID()
  94. event.respondWith(handleRequest(event, requestId, requestInterceptedAt))
  95. })
  96. /**
  97. * @param {FetchEvent} event
  98. * @param {string} requestId
  99. * @param {number} requestInterceptedAt
  100. */
  101. async function handleRequest(event, requestId, requestInterceptedAt) {
  102. const client = await resolveMainClient(event)
  103. const requestCloneForEvents = event.request.clone()
  104. const response = await getResponse(
  105. event,
  106. client,
  107. requestId,
  108. requestInterceptedAt,
  109. )
  110. // Send back the response clone for the "response:*" life-cycle events.
  111. // Ensure MSW is active and ready to handle the message, otherwise
  112. // this message will pend indefinitely.
  113. if (client && activeClientIds.has(client.id)) {
  114. const serializedRequest = await serializeRequest(requestCloneForEvents)
  115. // Omit the body of server-sent event stream responses.
  116. // Cloning such responses would prevent client-side stream cancelations
  117. // from reaching the original stream (a teed stream only cancels its
  118. // source once both of its branches cancel) and would buffer the
  119. // entire stream into the unconsumed clone indefinitely.
  120. const isEventStreamResponse = response.headers
  121. .get('content-type')
  122. ?.toLowerCase()
  123. .startsWith('text/event-stream')
  124. // Clone the response so both the client and the library could consume it.
  125. const responseClone = isEventStreamResponse ? null : response.clone()
  126. sendToClient(
  127. client,
  128. {
  129. type: 'RESPONSE',
  130. payload: {
  131. isMockedResponse: IS_MOCKED_RESPONSE in response,
  132. request: {
  133. id: requestId,
  134. ...serializedRequest,
  135. },
  136. response: {
  137. type: response.type,
  138. status: response.status,
  139. statusText: response.statusText,
  140. headers: Object.fromEntries(response.headers.entries()),
  141. body: responseClone ? responseClone.body : null,
  142. },
  143. },
  144. },
  145. responseClone && responseClone.body
  146. ? [serializedRequest.body, responseClone.body]
  147. : [],
  148. )
  149. }
  150. return response
  151. }
  152. /**
  153. * Resolve the main client for the given event.
  154. * Client that issues a request doesn't necessarily equal the client
  155. * that registered the worker. It's with the latter the worker should
  156. * communicate with during the response resolving phase.
  157. * @param {FetchEvent} event
  158. * @returns {Promise<Client | undefined>}
  159. */
  160. async function resolveMainClient(event) {
  161. const client = await self.clients.get(event.clientId)
  162. if (activeClientIds.has(event.clientId)) {
  163. return client
  164. }
  165. if (client?.frameType === 'top-level') {
  166. return client
  167. }
  168. const allClients = await self.clients.matchAll({
  169. type: 'window',
  170. })
  171. return allClients
  172. .filter((client) => {
  173. // Get only those clients that are currently visible.
  174. return client.visibilityState === 'visible'
  175. })
  176. .find((client) => {
  177. // Find the client ID that's recorded in the
  178. // set of clients that have registered the worker.
  179. return activeClientIds.has(client.id)
  180. })
  181. }
  182. /**
  183. * @param {FetchEvent} event
  184. * @param {Client | undefined} client
  185. * @param {string} requestId
  186. * @param {number} requestInterceptedAt
  187. * @returns {Promise<Response>}
  188. */
  189. async function getResponse(event, client, requestId, requestInterceptedAt) {
  190. // Clone the request because it might've been already used
  191. // (i.e. its body has been read and sent to the client).
  192. const requestClone = event.request.clone()
  193. function passthrough() {
  194. // Cast the request headers to a new Headers instance
  195. // so the headers can be manipulated with.
  196. const headers = new Headers(requestClone.headers)
  197. // Remove the "accept" header value that marked this request as passthrough.
  198. // This prevents request alteration and also keeps it compliant with the
  199. // user-defined CORS policies.
  200. const acceptHeader = headers.get('accept')
  201. if (acceptHeader) {
  202. const values = acceptHeader.split(',').map((value) => value.trim())
  203. const filteredValues = values.filter(
  204. (value) => value !== 'msw/passthrough',
  205. )
  206. if (filteredValues.length > 0) {
  207. headers.set('accept', filteredValues.join(', '))
  208. } else {
  209. headers.delete('accept')
  210. }
  211. }
  212. return fetch(requestClone, { headers })
  213. }
  214. // Bypass mocking when the client is not active.
  215. if (!client) {
  216. return passthrough()
  217. }
  218. // Bypass initial page load requests (i.e. static assets).
  219. // The absence of the immediate/parent client in the map of the active clients
  220. // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
  221. // and is not ready to handle requests.
  222. if (!activeClientIds.has(client.id)) {
  223. return passthrough()
  224. }
  225. // Notify the client that a request has been intercepted.
  226. const serializedRequest = await serializeRequest(event.request)
  227. const clientMessage = await sendToClient(
  228. client,
  229. {
  230. type: 'REQUEST',
  231. payload: {
  232. id: requestId,
  233. interceptedAt: requestInterceptedAt,
  234. ...serializedRequest,
  235. },
  236. },
  237. [serializedRequest.body],
  238. )
  239. switch (clientMessage.type) {
  240. case 'MOCK_RESPONSE': {
  241. return respondWithMock(clientMessage.data)
  242. }
  243. case 'PASSTHROUGH': {
  244. return passthrough()
  245. }
  246. }
  247. return passthrough()
  248. }
  249. /**
  250. * @param {Client} client
  251. * @param {any} message
  252. * @param {Array<Transferable>} transferrables
  253. * @returns {Promise<any>}
  254. */
  255. function sendToClient(client, message, transferrables = []) {
  256. return new Promise((resolve, reject) => {
  257. const channel = new MessageChannel()
  258. channel.port1.onmessage = (event) => {
  259. if (event.data && event.data.error) {
  260. return reject(event.data.error)
  261. }
  262. resolve(event.data)
  263. }
  264. client.postMessage(message, [
  265. channel.port2,
  266. ...transferrables.filter(Boolean),
  267. ])
  268. })
  269. }
  270. /**
  271. * @param {Response} response
  272. * @returns {Response}
  273. */
  274. function respondWithMock(response) {
  275. // Setting response status code to 0 is a no-op.
  276. // However, when responding with a "Response.error()", the produced Response
  277. // instance will have status code set to 0. Since it's not possible to create
  278. // a Response instance with status code 0, handle that use-case separately.
  279. if (response.status === 0) {
  280. return Response.error()
  281. }
  282. const mockedResponse = new Response(response.body, response)
  283. Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
  284. value: true,
  285. enumerable: true,
  286. })
  287. return mockedResponse
  288. }
  289. /**
  290. * @param {Request} request
  291. */
  292. async function serializeRequest(request) {
  293. return {
  294. url: request.url,
  295. mode: request.mode,
  296. method: request.method,
  297. headers: Object.fromEntries(request.headers.entries()),
  298. cache: request.cache,
  299. credentials: request.credentials,
  300. destination: request.destination,
  301. integrity: request.integrity,
  302. redirect: request.redirect,
  303. referrer: request.referrer,
  304. referrerPolicy: request.referrerPolicy,
  305. body: await request.arrayBuffer(),
  306. keepalive: request.keepalive,
  307. }
  308. }