msw 2.3.1 → 2.3.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.
Files changed (91) hide show
  1. package/lib/core/{GraphQLHandler-Dq_WRbKe.d.mts → GraphQLHandler-L3AUWt2v.d.mts} +1 -1
  2. package/lib/core/{GraphQLHandler-COiPfZ8k.d.ts → GraphQLHandler-UgnlXhlx.d.ts} +1 -1
  3. package/lib/core/{HttpResponse-B07UKAkU.d.ts → HttpResponse-B58aIqZM.d.ts} +6 -5
  4. package/lib/core/{HttpResponse-C7niBMwb.d.mts → HttpResponse-fnOXxh4-.d.mts} +6 -5
  5. package/lib/core/HttpResponse.d.mts +2 -1
  6. package/lib/core/HttpResponse.d.ts +2 -1
  7. package/lib/core/SetupApi.d.mts +2 -1
  8. package/lib/core/SetupApi.d.ts +2 -1
  9. package/lib/core/getResponse.d.mts +2 -1
  10. package/lib/core/getResponse.d.ts +2 -1
  11. package/lib/core/graphql.d.mts +3 -2
  12. package/lib/core/graphql.d.ts +3 -2
  13. package/lib/core/handlers/GraphQLHandler.d.mts +3 -2
  14. package/lib/core/handlers/GraphQLHandler.d.ts +3 -2
  15. package/lib/core/handlers/HttpHandler.d.mts +2 -1
  16. package/lib/core/handlers/HttpHandler.d.ts +2 -1
  17. package/lib/core/handlers/RequestHandler.d.mts +2 -1
  18. package/lib/core/handlers/RequestHandler.d.ts +2 -1
  19. package/lib/core/handlers/RequestHandler.js +18 -25
  20. package/lib/core/handlers/RequestHandler.js.map +1 -1
  21. package/lib/core/handlers/RequestHandler.mjs +21 -26
  22. package/lib/core/handlers/RequestHandler.mjs.map +1 -1
  23. package/lib/core/http.d.mts +2 -1
  24. package/lib/core/http.d.ts +2 -1
  25. package/lib/core/index.d.mts +3 -2
  26. package/lib/core/index.d.ts +3 -2
  27. package/lib/core/passthrough.d.mts +2 -1
  28. package/lib/core/passthrough.d.ts +2 -1
  29. package/lib/core/utils/HttpResponse/decorators.d.mts +4 -2
  30. package/lib/core/utils/HttpResponse/decorators.d.ts +4 -2
  31. package/lib/core/utils/HttpResponse/decorators.js +16 -6
  32. package/lib/core/utils/HttpResponse/decorators.js.map +1 -1
  33. package/lib/core/utils/HttpResponse/decorators.mjs +16 -6
  34. package/lib/core/utils/HttpResponse/decorators.mjs.map +1 -1
  35. package/lib/core/utils/cookieStore.d.mts +5 -0
  36. package/lib/core/utils/cookieStore.d.ts +5 -0
  37. package/lib/core/utils/cookieStore.js +176 -0
  38. package/lib/core/utils/cookieStore.js.map +1 -0
  39. package/lib/core/utils/cookieStore.mjs +146 -0
  40. package/lib/core/utils/cookieStore.mjs.map +1 -0
  41. package/lib/core/utils/executeHandlers.d.mts +2 -1
  42. package/lib/core/utils/executeHandlers.d.ts +2 -1
  43. package/lib/core/utils/handleRequest.d.mts +2 -1
  44. package/lib/core/utils/handleRequest.d.ts +2 -1
  45. package/lib/core/utils/handleRequest.js +2 -2
  46. package/lib/core/utils/handleRequest.js.map +1 -1
  47. package/lib/core/utils/handleRequest.mjs +2 -2
  48. package/lib/core/utils/handleRequest.mjs.map +1 -1
  49. package/lib/core/utils/internal/isIterable.d.mts +16 -2
  50. package/lib/core/utils/internal/isIterable.d.ts +16 -2
  51. package/lib/core/utils/internal/isIterable.js +1 -1
  52. package/lib/core/utils/internal/isIterable.js.map +1 -1
  53. package/lib/core/utils/internal/isIterable.mjs +1 -1
  54. package/lib/core/utils/internal/isIterable.mjs.map +1 -1
  55. package/lib/core/utils/internal/parseGraphQLRequest.d.mts +3 -2
  56. package/lib/core/utils/internal/parseGraphQLRequest.d.ts +3 -2
  57. package/lib/core/utils/internal/parseMultipartData.d.mts +2 -1
  58. package/lib/core/utils/internal/parseMultipartData.d.ts +2 -1
  59. package/lib/core/utils/internal/requestHandlerUtils.d.mts +2 -1
  60. package/lib/core/utils/internal/requestHandlerUtils.d.ts +2 -1
  61. package/lib/core/utils/request/getRequestCookies.d.mts +1 -6
  62. package/lib/core/utils/request/getRequestCookies.d.ts +1 -6
  63. package/lib/core/utils/request/getRequestCookies.js +22 -20
  64. package/lib/core/utils/request/getRequestCookies.js.map +1 -1
  65. package/lib/core/utils/request/getRequestCookies.mjs +22 -20
  66. package/lib/core/utils/request/getRequestCookies.mjs.map +1 -1
  67. package/lib/core/utils/request/storeResponseCookies.d.mts +3 -0
  68. package/lib/core/utils/request/storeResponseCookies.d.ts +3 -0
  69. package/lib/core/utils/request/{readResponseCookies.js → storeResponseCookies.js} +12 -9
  70. package/lib/core/utils/request/storeResponseCookies.js.map +1 -0
  71. package/lib/core/utils/request/storeResponseCookies.mjs +12 -0
  72. package/lib/core/utils/request/storeResponseCookies.mjs.map +1 -0
  73. package/lib/iife/index.js +12204 -345
  74. package/lib/iife/index.js.map +1 -1
  75. package/lib/mockServiceWorker.js +1 -1
  76. package/package.json +7 -7
  77. package/src/core/handlers/RequestHandler.ts +51 -44
  78. package/src/core/utils/HttpResponse/decorators.ts +32 -16
  79. package/src/core/utils/cookieStore.ts +212 -0
  80. package/src/core/utils/handleRequest.ts +3 -3
  81. package/src/core/utils/internal/isIterable.ts +22 -2
  82. package/src/core/utils/request/getRequestCookies.ts +32 -35
  83. package/src/core/utils/request/storeResponseCookies.ts +17 -0
  84. package/lib/core/utils/request/readResponseCookies.d.mts +0 -3
  85. package/lib/core/utils/request/readResponseCookies.d.ts +0 -3
  86. package/lib/core/utils/request/readResponseCookies.js.map +0 -1
  87. package/lib/core/utils/request/readResponseCookies.mjs +0 -9
  88. package/lib/core/utils/request/readResponseCookies.mjs.map +0 -1
  89. package/src/core/utils/request/getRequestCookies.node.test.ts +0 -29
  90. package/src/core/utils/request/getRequestCookies.test.ts +0 -64
  91. package/src/core/utils/request/readResponseCookies.ts +0 -9
@@ -8,7 +8,7 @@
8
8
  * - Please do NOT serve this file on production.
9
9
  */
10
10
 
11
- const PACKAGE_VERSION = '2.3.1'
11
+ const PACKAGE_VERSION = '2.3.3'
12
12
  const INTEGRITY_CHECKSUM = '26357c79639bfa20d64c0efca2a87423'
13
13
  const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
14
14
  const activeClientIds = new Set()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "msw",
3
- "version": "2.3.1",
3
+ "version": "2.3.3",
4
4
  "description": "Seamless REST/GraphQL API mocking library for browser and Node.js.",
5
5
  "main": "./lib/core/index.js",
6
6
  "module": "./lib/core/index.mjs",
@@ -115,8 +115,8 @@
115
115
  "dependencies": {
116
116
  "@bundled-es-modules/cookie": "^2.0.0",
117
117
  "@bundled-es-modules/statuses": "^1.0.1",
118
+ "@bundled-es-modules/tough-cookie": "^0.1.6",
118
119
  "@inquirer/confirm": "^3.0.0",
119
- "@mswjs/cookies": "^1.1.0",
120
120
  "@mswjs/interceptors": "^0.29.0",
121
121
  "@open-draft/until": "^2.1.0",
122
122
  "@types/cookie": "^0.6.0",
@@ -171,7 +171,7 @@
171
171
  "simple-git-hooks": "^2.9.0",
172
172
  "ts-node": "^10.9.2",
173
173
  "tsup": "^8.0.1",
174
- "typescript": "^5.4.2",
174
+ "typescript": "^5.5.2",
175
175
  "undici": "^5.20.0",
176
176
  "url-loader": "^4.1.1",
177
177
  "vitest": "^1.2.2",
@@ -206,12 +206,12 @@
206
206
  "check:exports": "node \"./config/scripts/validate-esm.js\"",
207
207
  "test": "pnpm test:unit && pnpm test:node && pnpm test:browser && pnpm test:native",
208
208
  "test:unit": "vitest",
209
- "test:node": "vitest run --config=./test/node/vitest.config.ts",
210
- "test:native": "vitest --config=./test/native/vitest.config.ts",
209
+ "test:node": "vitest run --config=./test/node/vitest.config.mts",
210
+ "test:native": "vitest --config=./test/native/vitest.config.mts",
211
211
  "test:browser": "playwright test -c ./test/browser/playwright.config.ts",
212
- "test:modules:node": "vitest --config=./test/modules/node/vitest.config.ts",
212
+ "test:modules:node": "vitest --config=./test/modules/node/vitest.config.mts",
213
213
  "test:modules:browser": "playwright test -c ./test/modules/browser/playwright.config.ts",
214
- "test:ts": "vitest --typecheck --config=./test/typings/vitest.config.ts",
214
+ "test:ts": "vitest --typecheck --config=./test/typings/vitest.config.mts",
215
215
  "release": "release publish",
216
216
  "postinstall": "node -e \"try{require('./config/scripts/postinstall')}catch(e){}\""
217
217
  }
@@ -1,6 +1,9 @@
1
- import { invariant } from 'outvariant'
2
1
  import { getCallFrame } from '../utils/internal/getCallFrame'
3
- import { isIterable } from '../utils/internal/isIterable'
2
+ import {
3
+ AsyncIterable,
4
+ Iterable,
5
+ isIterable,
6
+ } from '../utils/internal/isIterable'
4
7
  import type { ResponseResolutionContext } from '../utils/executeHandlers'
5
8
  import type { MaybePromise } from '../typeUtils'
6
9
  import { StrictRequest, StrictResponse } from '..//HttpResponse'
@@ -52,7 +55,12 @@ export type AsyncResponseResolverReturnType<
52
55
  ResponseBodyType extends DefaultBodyType,
53
56
  > = MaybePromise<
54
57
  | ResponseResolverReturnType<ResponseBodyType>
55
- | Generator<
58
+ | Iterable<
59
+ MaybeAsyncResponseResolverReturnType<ResponseBodyType>,
60
+ MaybeAsyncResponseResolverReturnType<ResponseBodyType>,
61
+ MaybeAsyncResponseResolverReturnType<ResponseBodyType>
62
+ >
63
+ | AsyncIterable<
56
64
  MaybeAsyncResponseResolverReturnType<ResponseBodyType>,
57
65
  MaybeAsyncResponseResolverReturnType<ResponseBodyType>,
58
66
  MaybeAsyncResponseResolverReturnType<ResponseBodyType>
@@ -117,12 +125,18 @@ export abstract class RequestHandler<
117
125
  public isUsed: boolean
118
126
 
119
127
  protected resolver: ResponseResolver<ResolverExtras, any, any>
120
- private resolverGenerator?: Generator<
121
- MaybeAsyncResponseResolverReturnType<any>,
122
- MaybeAsyncResponseResolverReturnType<any>,
123
- MaybeAsyncResponseResolverReturnType<any>
124
- >
125
- private resolverGeneratorResult?: Response | StrictResponse<any>
128
+ private resolverIterator?:
129
+ | Iterator<
130
+ MaybeAsyncResponseResolverReturnType<any>,
131
+ MaybeAsyncResponseResolverReturnType<any>,
132
+ MaybeAsyncResponseResolverReturnType<any>
133
+ >
134
+ | AsyncIterator<
135
+ MaybeAsyncResponseResolverReturnType<any>,
136
+ MaybeAsyncResponseResolverReturnType<any>,
137
+ MaybeAsyncResponseResolverReturnType<any>
138
+ >
139
+ private resolverIteratorResult?: Response | StrictResponse<any>
126
140
  private options?: HandlerOptions
127
141
 
128
142
  constructor(args: RequestHandlerArgs<HandlerInfo, HandlerOptions>) {
@@ -256,6 +270,9 @@ export abstract class RequestHandler<
256
270
  return null
257
271
  }
258
272
 
273
+ // Preemptively mark the handler as used.
274
+ // Generators will undo this because only when the resolver reaches the
275
+ // "done" state of the generator that it considers the handler used.
259
276
  this.isUsed = true
260
277
 
261
278
  // Create a response extraction wrapper around the resolver
@@ -301,48 +318,38 @@ export abstract class RequestHandler<
301
318
  resolver: ResponseResolver<ResolverExtras>,
302
319
  ): ResponseResolver<ResolverExtras> {
303
320
  return async (info): Promise<ResponseResolverReturnType<any>> => {
304
- const result = this.resolverGenerator || (await resolver(info))
305
-
306
- if (isIterable<AsyncResponseResolverReturnType<any>>(result)) {
307
- // Immediately mark this handler as unused.
308
- // Only when the generator is done, the handler will be
309
- // considered used.
310
- this.isUsed = false
311
-
312
- const { value, done } = result[Symbol.iterator]().next()
313
- const nextResponse = await value
314
-
315
- if (done) {
316
- this.isUsed = true
321
+ if (!this.resolverIterator) {
322
+ const result = await resolver(info)
323
+ if (!isIterable(result)) {
324
+ return result
317
325
  }
326
+ this.resolverIterator =
327
+ Symbol.iterator in result
328
+ ? result[Symbol.iterator]()
329
+ : result[Symbol.asyncIterator]()
330
+ }
318
331
 
319
- // If the generator is done and there is no next value,
320
- // return the previous generator's value.
321
- if (!nextResponse && done) {
322
- invariant(
323
- this.resolverGeneratorResult,
324
- 'Failed to returned a previously stored generator response: the value is not a valid Response.',
325
- )
326
-
327
- // Clone the previously stored response from the generator
328
- // so that it could be read again.
329
- return this.resolverGeneratorResult.clone() as StrictResponse<any>
330
- }
332
+ // Opt-out from marking this handler as used.
333
+ this.isUsed = false
331
334
 
332
- if (!this.resolverGenerator) {
333
- this.resolverGenerator = result
334
- }
335
+ const { done, value } = await this.resolverIterator.next()
336
+ const nextResponse = await value
335
337
 
336
- if (nextResponse) {
337
- // Also clone the response before storing it
338
- // so it could be read again.
339
- this.resolverGeneratorResult = nextResponse?.clone()
340
- }
338
+ if (nextResponse) {
339
+ this.resolverIteratorResult = nextResponse.clone()
340
+ }
341
+
342
+ if (done) {
343
+ // A one-time generator resolver stops affecting the network
344
+ // only after it's been completely exhausted.
345
+ this.isUsed = true
341
346
 
342
- return nextResponse
347
+ // Clone the previously stored response so it can be read
348
+ // when receiving it repeatedly from the "done" generator.
349
+ return this.resolverIteratorResult?.clone()
343
350
  }
344
351
 
345
- return result
352
+ return nextResponse
346
353
  }
347
354
  }
348
355
 
@@ -1,9 +1,11 @@
1
1
  import statuses from '@bundled-es-modules/statuses'
2
- import type { HttpResponseInit } from '../../HttpResponse'
3
2
  import { Headers as HeadersPolyfill } from 'headers-polyfill'
3
+ import type { HttpResponseInit } from '../../HttpResponse'
4
4
 
5
5
  const { message } = statuses
6
6
 
7
+ export const kSetCookie = Symbol('kSetCookie')
8
+
7
9
  export interface HttpResponseDecoratedInit extends HttpResponseInit {
8
10
  status: number
9
11
  statusText: string
@@ -38,21 +40,35 @@ export function decorateResponse(
38
40
  })
39
41
  }
40
42
 
41
- // Cookie forwarding is only relevant in the browser.
42
- if (typeof document !== 'undefined') {
43
- // Write the mocked response cookies to the document.
44
- // Use `headers-polyfill` to get the Set-Cookie header value correctly.
45
- // This is an alternative until TypeScript 5.2
46
- // and Node.js v20 become the minimum supported version
47
- // and getSetCookie in Headers can be used directly.
48
- const responseCookies = HeadersPolyfill.prototype.getSetCookie.call(
49
- init.headers,
50
- )
51
-
52
- for (const cookieString of responseCookies) {
53
- // No need to parse the cookie headers because it's defined
54
- // as the valid cookie string to begin with.
55
- document.cookie = cookieString
43
+ const responseCookies = init.headers.get('set-cookie')
44
+
45
+ if (responseCookies) {
46
+ // Record the raw "Set-Cookie" response header provided
47
+ // in the HeadersInit. This is later used to store these cookies
48
+ // in cookie jar and return the right cookies in the "cookies"
49
+ // response resolver argument.
50
+ Object.defineProperty(response, kSetCookie, {
51
+ value: responseCookies,
52
+ enumerable: false,
53
+ writable: false,
54
+ })
55
+
56
+ // Cookie forwarding is only relevant in the browser.
57
+ if (typeof document !== 'undefined') {
58
+ // Write the mocked response cookies to the document.
59
+ // Use `headers-polyfill` to get the Set-Cookie header value correctly.
60
+ // This is an alternative until TypeScript 5.2
61
+ // and Node.js v20 become the minimum supported version
62
+ // and getSetCookie in Headers can be used directly.
63
+ const responseCookiePairs = HeadersPolyfill.prototype.getSetCookie.call(
64
+ init.headers,
65
+ )
66
+
67
+ for (const cookieString of responseCookiePairs) {
68
+ // No need to parse the cookie headers because it's defined
69
+ // as the valid cookie string to begin with.
70
+ document.cookie = cookieString
71
+ }
56
72
  }
57
73
  }
58
74
 
@@ -0,0 +1,212 @@
1
+ import { invariant } from 'outvariant'
2
+ import { isNodeProcess } from 'is-node-process'
3
+ import toughCookie, {
4
+ type Cookie as CookieInstance,
5
+ } from '@bundled-es-modules/tough-cookie'
6
+
7
+ const { Cookie, CookieJar, Store, MemoryCookieStore, domainMatch, pathMatch } =
8
+ toughCookie
9
+
10
+ /**
11
+ * Custom cookie store that uses the Web Storage API.
12
+ * @see https://github.com/expo/tough-cookie-web-storage-store
13
+ */
14
+ class WebStorageCookieStore extends Store {
15
+ private storage: Storage
16
+ private storageKey: string
17
+
18
+ constructor() {
19
+ super()
20
+
21
+ invariant(
22
+ typeof localStorage !== 'undefined',
23
+ 'Failed to create a WebStorageCookieStore: `localStorage` is not available in this environment. This is likely an issue with MSW. Please report it on GitHub: https://github.com/mswjs/msw/issues',
24
+ )
25
+
26
+ this.synchronous = true
27
+ this.storage = localStorage
28
+ this.storageKey = '__msw-cookie-store__'
29
+ }
30
+
31
+ findCookie(
32
+ domain: string,
33
+ path: string,
34
+ key: string,
35
+ callback: (error: Error | null, cookie: CookieInstance | null) => void,
36
+ ): void {
37
+ try {
38
+ const store = this.getStore()
39
+ const cookies = this.filterCookiesFromList(store, { domain, path, key })
40
+ callback(null, cookies[0] || null)
41
+ } catch (error) {
42
+ if (error instanceof Error) {
43
+ callback(error, null)
44
+ }
45
+ }
46
+ }
47
+
48
+ findCookies(
49
+ domain: string,
50
+ path: string,
51
+ allowSpecialUseDomain: boolean,
52
+ callback: (error: Error | null, cookie: Array<CookieInstance>) => void,
53
+ ): void {
54
+ if (!domain) {
55
+ callback(null, [])
56
+ return
57
+ }
58
+
59
+ try {
60
+ const store = this.getStore()
61
+ const results = this.filterCookiesFromList(store, {
62
+ domain,
63
+ path,
64
+ })
65
+ callback(null, results)
66
+ } catch (error) {
67
+ if (error instanceof Error) {
68
+ callback(error, [])
69
+ }
70
+ }
71
+ }
72
+
73
+ putCookie(
74
+ cookie: CookieInstance,
75
+ callback: (error: Error | null) => void,
76
+ ): void {
77
+ try {
78
+ const store = this.getStore()
79
+ store.push(cookie)
80
+ this.updateStore(store)
81
+ } catch (error) {
82
+ if (error instanceof Error) {
83
+ callback(error)
84
+ }
85
+ }
86
+ }
87
+
88
+ updateCookie(
89
+ oldCookie: CookieInstance,
90
+ newCookie: CookieInstance,
91
+ callback: (error: Error | null) => void,
92
+ ): void {
93
+ this.putCookie(newCookie, callback)
94
+ }
95
+
96
+ removeCookie(
97
+ domain: string,
98
+ path: string,
99
+ key: string,
100
+ callback: (error: Error | null) => void,
101
+ ): void {
102
+ try {
103
+ const store = this.getStore()
104
+ const nextStore = this.deleteCookiesFromList(store, { domain, path, key })
105
+ this.updateStore(nextStore)
106
+ callback(null)
107
+ } catch (error) {
108
+ if (error instanceof Error) {
109
+ callback(error)
110
+ }
111
+ }
112
+ }
113
+
114
+ removeCookies(
115
+ domain: string,
116
+ path: string,
117
+ callback: (error: Error | null) => void,
118
+ ): void {
119
+ try {
120
+ const store = this.getStore()
121
+ const nextStore = this.deleteCookiesFromList(store, { domain, path })
122
+ this.updateStore(nextStore)
123
+ callback(null)
124
+ } catch (error) {
125
+ if (error instanceof Error) {
126
+ callback(error)
127
+ }
128
+ }
129
+ }
130
+
131
+ getAllCookies(
132
+ callback: (error: Error | null, cookie: Array<CookieInstance>) => void,
133
+ ): void {
134
+ try {
135
+ callback(null, this.getStore())
136
+ } catch (error) {
137
+ if (error instanceof Error) {
138
+ callback(error, [])
139
+ }
140
+ }
141
+ }
142
+
143
+ private getStore(): Array<CookieInstance> {
144
+ try {
145
+ const json = this.storage.getItem(this.storageKey)
146
+
147
+ if (json == null) {
148
+ return []
149
+ }
150
+
151
+ const rawCookies = JSON.parse(json) as Array<Record<string, any>>
152
+ const cookies: Array<CookieInstance> = []
153
+ for (const rawCookie of rawCookies) {
154
+ const cookie = Cookie.fromJSON(rawCookie)
155
+ if (cookie != null) {
156
+ cookies.push(cookie)
157
+ }
158
+ }
159
+ return cookies
160
+ } catch {
161
+ return []
162
+ }
163
+ }
164
+
165
+ private updateStore(nextStore: Array<CookieInstance>) {
166
+ this.storage.setItem(
167
+ this.storageKey,
168
+ JSON.stringify(nextStore.map((cookie) => cookie.toJSON())),
169
+ )
170
+ }
171
+
172
+ private filterCookiesFromList(
173
+ cookies: Array<CookieInstance>,
174
+ matches: { domain?: string; path?: string; key?: string },
175
+ ): Array<CookieInstance> {
176
+ const result: Array<CookieInstance> = []
177
+
178
+ for (const cookie of cookies) {
179
+ if (matches.domain && !domainMatch(matches.domain, cookie.domain || '')) {
180
+ continue
181
+ }
182
+
183
+ if (matches.path && !pathMatch(matches.path, cookie.path || '')) {
184
+ continue
185
+ }
186
+
187
+ if (matches.key && cookie.key !== matches.key) {
188
+ continue
189
+ }
190
+
191
+ result.push(cookie)
192
+ }
193
+
194
+ console.log('filter result:', { cookies, matches, result })
195
+
196
+ return result
197
+ }
198
+
199
+ private deleteCookiesFromList(
200
+ cookies: Array<CookieInstance>,
201
+ matches: { domain?: string; path?: string; key?: string },
202
+ ) {
203
+ const matchingCookies = this.filterCookiesFromList(cookies, matches)
204
+ return cookies.filter((cookie) => !matchingCookies.includes(cookie))
205
+ }
206
+ }
207
+
208
+ const store = isNodeProcess()
209
+ ? new MemoryCookieStore()
210
+ : new WebStorageCookieStore()
211
+
212
+ export const cookieStore = new CookieJar(store)
@@ -5,7 +5,7 @@ import { LifeCycleEventsMap, SharedOptions } from '../sharedOptions'
5
5
  import { RequiredDeep } from '../typeUtils'
6
6
  import { HandlersExecutionResult, executeHandlers } from './executeHandlers'
7
7
  import { onUnhandledRequest } from './request/onUnhandledRequest'
8
- import { readResponseCookies } from './request/readResponseCookies'
8
+ import { storeResponseCookies } from './request/storeResponseCookies'
9
9
 
10
10
  export interface HandleRequestOptions {
11
11
  /**
@@ -110,8 +110,8 @@ export async function handleRequest(
110
110
  return
111
111
  }
112
112
 
113
- // Store all the received response cookies in the virtual cookie store.
114
- readResponseCookies(request, response)
113
+ // Store all the received response cookies in the cookie jar.
114
+ storeResponseCookies(request, response)
115
115
 
116
116
  emitter.emit('request:match', { request, requestId })
117
117
 
@@ -1,12 +1,32 @@
1
+ /**
2
+ * This is the same as TypeScript's `Iterable`, but with all three type parameters.
3
+ * @todo Remove once TypeScript 5.6 is the minimum.
4
+ */
5
+ export interface Iterable<T, TReturn, TNext> {
6
+ [Symbol.iterator](): Iterator<T, TReturn, TNext>
7
+ }
8
+
9
+ /**
10
+ * This is the same as TypeScript's `AsyncIterable`, but with all three type parameters.
11
+ * @todo Remove once TypeScript 5.6 is the minimum.
12
+ */
13
+ export interface AsyncIterable<T, TReturn, TNext> {
14
+ [Symbol.asyncIterator](): AsyncIterator<T, TReturn, TNext>
15
+ }
16
+
1
17
  /**
2
18
  * Determines if the given function is an iterator.
3
19
  */
4
20
  export function isIterable<IteratorType>(
5
21
  fn: any,
6
- ): fn is Generator<IteratorType, IteratorType, IteratorType> {
22
+ ): fn is
23
+ | Iterable<IteratorType, IteratorType, IteratorType>
24
+ | AsyncIterable<IteratorType, IteratorType, IteratorType> {
7
25
  if (!fn) {
8
26
  return false
9
27
  }
10
28
 
11
- return typeof (fn as Generator<unknown>)[Symbol.iterator] == 'function'
29
+ return (
30
+ Reflect.has(fn, Symbol.iterator) || Reflect.has(fn, Symbol.asyncIterator)
31
+ )
12
32
  }
@@ -1,29 +1,24 @@
1
1
  import cookieUtils from '@bundled-es-modules/cookie'
2
- import { store } from '@mswjs/cookies'
2
+ import { cookieStore } from '../cookieStore'
3
3
 
4
4
  function getAllDocumentCookies() {
5
5
  return cookieUtils.parse(document.cookie)
6
6
  }
7
7
 
8
- /** @todo Rename this to "getDocumentCookies" */
9
- /**
10
- * Returns relevant document cookies based on the request `credentials` option.
11
- */
12
- export function getRequestCookies(request: Request): Record<string, string> {
13
- /**
14
- * @note No cookies persist on the document in Node.js: no document.
15
- */
8
+ function getDocumentCookies(request: Request): Record<string, string> {
16
9
  if (typeof document === 'undefined' || typeof location === 'undefined') {
17
10
  return {}
18
11
  }
19
12
 
20
13
  switch (request.credentials) {
21
14
  case 'same-origin': {
22
- const url = new URL(request.url)
15
+ const requestUrl = new URL(request.url)
23
16
 
24
17
  // Return document cookies only when requested a resource
25
18
  // from the same origin as the current document.
26
- return location.origin === url.origin ? getAllDocumentCookies() : {}
19
+ return location.origin === requestUrl.origin
20
+ ? getAllDocumentCookies()
21
+ : {}
27
22
  }
28
23
 
29
24
  case 'include': {
@@ -38,38 +33,40 @@ export function getRequestCookies(request: Request): Record<string, string> {
38
33
  }
39
34
 
40
35
  export function getAllRequestCookies(request: Request): Record<string, string> {
41
- const requestCookiesString = request.headers.get('cookie')
42
- const cookiesFromHeaders = requestCookiesString
43
- ? cookieUtils.parse(requestCookiesString)
36
+ /**
37
+ * @note While the "cookie" header is a forbidden header field
38
+ * in the browser, you can read it in Node.js. We need to respect
39
+ * it for mocking in Node.js.
40
+ */
41
+ const requestCookieHeader = request.headers.get('cookie')
42
+ const cookiesFromHeaders = requestCookieHeader
43
+ ? cookieUtils.parse(requestCookieHeader)
44
44
  : {}
45
45
 
46
- store.hydrate()
47
-
48
- const cookiesFromStore = Array.from(store.get(request)?.entries()).reduce<
49
- Record<string, string>
50
- >((cookies, [name, { value }]) => {
51
- return Object.assign(cookies, { [name.trim()]: value })
52
- }, {})
53
-
54
- const cookiesFromDocument = getRequestCookies(request)
46
+ const cookiesFromDocument = getDocumentCookies(request)
55
47
 
56
- const forwardedCookies = {
57
- ...cookiesFromDocument,
58
- ...cookiesFromStore,
48
+ // Forward the document cookies to the request headers.
49
+ for (const name in cookiesFromDocument) {
50
+ request.headers.append(
51
+ 'cookie',
52
+ cookieUtils.serialize(name, cookiesFromDocument[name]),
53
+ )
59
54
  }
60
55
 
61
- // Set the inferred cookies from the cookie store and the document
62
- // on the request's headers.
63
- /**
64
- * @todo Consider making this a separate step so this function
65
- * is pure-er.
66
- */
67
- for (const [name, value] of Object.entries(forwardedCookies)) {
68
- request.headers.append('cookie', cookieUtils.serialize(name, value))
56
+ const cookiesFromStore = cookieStore.getCookiesSync(request.url)
57
+ const storedCookiesObject = Object.fromEntries(
58
+ cookiesFromStore.map((cookie) => [cookie.key, cookie.value]),
59
+ )
60
+
61
+ // Forward the raw stored cookies to request headers
62
+ // so they contain metadata like "expires", "secure", etc.
63
+ for (const cookie of cookiesFromStore) {
64
+ request.headers.append('cookie', cookie.toString())
69
65
  }
70
66
 
71
67
  return {
72
- ...forwardedCookies,
68
+ ...cookiesFromDocument,
69
+ ...storedCookiesObject,
73
70
  ...cookiesFromHeaders,
74
71
  }
75
72
  }
@@ -0,0 +1,17 @@
1
+ import { cookieStore } from '../cookieStore'
2
+ import { kSetCookie } from '../HttpResponse/decorators'
3
+
4
+ export function storeResponseCookies(
5
+ request: Request,
6
+ response: Response,
7
+ ): void {
8
+ // Grab the raw "Set-Cookie" response header provided
9
+ // in the HeadersInit for this mocked response.
10
+ const responseCookies = Reflect.get(response, kSetCookie) as
11
+ | string
12
+ | undefined
13
+
14
+ if (responseCookies) {
15
+ cookieStore.setCookie(responseCookies, request.url)
16
+ }
17
+ }
@@ -1,3 +0,0 @@
1
- declare function readResponseCookies(request: Request, response: Response): void;
2
-
3
- export { readResponseCookies };
@@ -1,3 +0,0 @@
1
- declare function readResponseCookies(request: Request, response: Response): void;
2
-
3
- export { readResponseCookies };
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/core/utils/request/readResponseCookies.ts"],"sourcesContent":["import { store } from '@mswjs/cookies'\n\nexport function readResponseCookies(\n request: Request,\n response: Response,\n): void {\n store.add({ ...request, url: request.url.toString() }, response)\n store.persist()\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAsB;AAEf,SAAS,oBACd,SACA,UACM;AACN,uBAAM,IAAI,EAAE,GAAG,SAAS,KAAK,QAAQ,IAAI,SAAS,EAAE,GAAG,QAAQ;AAC/D,uBAAM,QAAQ;AAChB;","names":[]}
@@ -1,9 +0,0 @@
1
- import { store } from "@mswjs/cookies";
2
- function readResponseCookies(request, response) {
3
- store.add({ ...request, url: request.url.toString() }, response);
4
- store.persist();
5
- }
6
- export {
7
- readResponseCookies
8
- };
9
- //# sourceMappingURL=readResponseCookies.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/core/utils/request/readResponseCookies.ts"],"sourcesContent":["import { store } from '@mswjs/cookies'\n\nexport function readResponseCookies(\n request: Request,\n response: Response,\n): void {\n store.add({ ...request, url: request.url.toString() }, response)\n store.persist()\n}\n"],"mappings":"AAAA,SAAS,aAAa;AAEf,SAAS,oBACd,SACA,UACM;AACN,QAAM,IAAI,EAAE,GAAG,SAAS,KAAK,QAAQ,IAAI,SAAS,EAAE,GAAG,QAAQ;AAC/D,QAAM,QAAQ;AAChB;","names":[]}