importmap_mocha-rails 0.3.1 → 0.3.2

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.
@@ -0,0 +1,1146 @@
1
+ // src/interceptors/fetch/index.ts
2
+ import { invariant as invariant2 } from "outvariant";
3
+ import { DeferredPromise as DeferredPromise2 } from "@open-draft/deferred-promise";
4
+ import { until } from "@open-draft/until";
5
+
6
+ // src/glossary.ts
7
+ var IS_PATCHED_MODULE = Symbol("isPatchedModule");
8
+
9
+ // src/Interceptor.ts
10
+ import { Logger } from "@open-draft/logger";
11
+ import { Emitter } from "strict-event-emitter";
12
+ function getGlobalSymbol(symbol) {
13
+ return (
14
+ // @ts-ignore https://github.com/Microsoft/TypeScript/issues/24587
15
+ globalThis[symbol] || void 0
16
+ );
17
+ }
18
+ function setGlobalSymbol(symbol, value) {
19
+ globalThis[symbol] = value;
20
+ }
21
+ function deleteGlobalSymbol(symbol) {
22
+ delete globalThis[symbol];
23
+ }
24
+ var Interceptor = class {
25
+ constructor(symbol) {
26
+ this.symbol = symbol;
27
+ this.readyState = "INACTIVE" /* INACTIVE */;
28
+ this.emitter = new Emitter();
29
+ this.subscriptions = [];
30
+ this.logger = new Logger(symbol.description);
31
+ this.emitter.setMaxListeners(0);
32
+ this.logger.info("constructing the interceptor...");
33
+ }
34
+ /**
35
+ * Determine if this interceptor can be applied
36
+ * in the current environment.
37
+ */
38
+ checkEnvironment() {
39
+ return true;
40
+ }
41
+ /**
42
+ * Apply this interceptor to the current process.
43
+ * Returns an already running interceptor instance if it's present.
44
+ */
45
+ apply() {
46
+ const logger = this.logger.extend("apply");
47
+ logger.info("applying the interceptor...");
48
+ if (this.readyState === "APPLIED" /* APPLIED */) {
49
+ logger.info("intercepted already applied!");
50
+ return;
51
+ }
52
+ const shouldApply = this.checkEnvironment();
53
+ if (!shouldApply) {
54
+ logger.info("the interceptor cannot be applied in this environment!");
55
+ return;
56
+ }
57
+ this.readyState = "APPLYING" /* APPLYING */;
58
+ const runningInstance = this.getInstance();
59
+ if (runningInstance) {
60
+ logger.info("found a running instance, reusing...");
61
+ this.on = (event, listener) => {
62
+ logger.info('proxying the "%s" listener', event);
63
+ runningInstance.emitter.addListener(event, listener);
64
+ this.subscriptions.push(() => {
65
+ runningInstance.emitter.removeListener(event, listener);
66
+ logger.info('removed proxied "%s" listener!', event);
67
+ });
68
+ return this;
69
+ };
70
+ this.readyState = "APPLIED" /* APPLIED */;
71
+ return;
72
+ }
73
+ logger.info("no running instance found, setting up a new instance...");
74
+ this.setup();
75
+ this.setInstance();
76
+ this.readyState = "APPLIED" /* APPLIED */;
77
+ }
78
+ /**
79
+ * Setup the module augments and stubs necessary for this interceptor.
80
+ * This method is not run if there's a running interceptor instance
81
+ * to prevent instantiating an interceptor multiple times.
82
+ */
83
+ setup() {
84
+ }
85
+ /**
86
+ * Listen to the interceptor's public events.
87
+ */
88
+ on(event, listener) {
89
+ const logger = this.logger.extend("on");
90
+ if (this.readyState === "DISPOSING" /* DISPOSING */ || this.readyState === "DISPOSED" /* DISPOSED */) {
91
+ logger.info("cannot listen to events, already disposed!");
92
+ return this;
93
+ }
94
+ logger.info('adding "%s" event listener:', event, listener);
95
+ this.emitter.on(event, listener);
96
+ return this;
97
+ }
98
+ once(event, listener) {
99
+ this.emitter.once(event, listener);
100
+ return this;
101
+ }
102
+ off(event, listener) {
103
+ this.emitter.off(event, listener);
104
+ return this;
105
+ }
106
+ removeAllListeners(event) {
107
+ this.emitter.removeAllListeners(event);
108
+ return this;
109
+ }
110
+ /**
111
+ * Disposes of any side-effects this interceptor has introduced.
112
+ */
113
+ dispose() {
114
+ const logger = this.logger.extend("dispose");
115
+ if (this.readyState === "DISPOSED" /* DISPOSED */) {
116
+ logger.info("cannot dispose, already disposed!");
117
+ return;
118
+ }
119
+ logger.info("disposing the interceptor...");
120
+ this.readyState = "DISPOSING" /* DISPOSING */;
121
+ if (!this.getInstance()) {
122
+ logger.info("no interceptors running, skipping dispose...");
123
+ return;
124
+ }
125
+ this.clearInstance();
126
+ logger.info("global symbol deleted:", getGlobalSymbol(this.symbol));
127
+ if (this.subscriptions.length > 0) {
128
+ logger.info("disposing of %d subscriptions...", this.subscriptions.length);
129
+ for (const dispose of this.subscriptions) {
130
+ dispose();
131
+ }
132
+ this.subscriptions = [];
133
+ logger.info("disposed of all subscriptions!", this.subscriptions.length);
134
+ }
135
+ this.emitter.removeAllListeners();
136
+ logger.info("destroyed the listener!");
137
+ this.readyState = "DISPOSED" /* DISPOSED */;
138
+ }
139
+ getInstance() {
140
+ var _a;
141
+ const instance = getGlobalSymbol(this.symbol);
142
+ this.logger.info("retrieved global instance:", (_a = instance == null ? void 0 : instance.constructor) == null ? void 0 : _a.name);
143
+ return instance;
144
+ }
145
+ setInstance() {
146
+ setGlobalSymbol(this.symbol, this);
147
+ this.logger.info("set global instance!", this.symbol.description);
148
+ }
149
+ clearInstance() {
150
+ deleteGlobalSymbol(this.symbol);
151
+ this.logger.info("cleared global instance!", this.symbol.description);
152
+ }
153
+ };
154
+
155
+ // src/utils/uuid.ts
156
+ function uuidv4() {
157
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
158
+ const r = Math.random() * 16 | 0;
159
+ const v = c == "x" ? r : r & 3 | 8;
160
+ return v.toString(16);
161
+ });
162
+ }
163
+
164
+ // src/utils/RequestController.ts
165
+ import { invariant } from "outvariant";
166
+ import { DeferredPromise } from "@open-draft/deferred-promise";
167
+ var RequestController = class {
168
+ constructor(request) {
169
+ this.request = request;
170
+ this.responsePromise = new DeferredPromise();
171
+ }
172
+ respondWith(response) {
173
+ invariant(
174
+ this.responsePromise.state === "pending",
175
+ 'Failed to respond to "%s %s" request: the "request" event has already been responded to.',
176
+ this.request.method,
177
+ this.request.url
178
+ );
179
+ this.responsePromise.resolve(response);
180
+ }
181
+ };
182
+
183
+ // src/utils/toInteractiveRequest.ts
184
+ function toInteractiveRequest(request) {
185
+ const requestController = new RequestController(request);
186
+ Reflect.set(
187
+ request,
188
+ "respondWith",
189
+ requestController.respondWith.bind(requestController)
190
+ );
191
+ return {
192
+ interactiveRequest: request,
193
+ requestController
194
+ };
195
+ }
196
+
197
+ // src/utils/emitAsync.ts
198
+ async function emitAsync(emitter, eventName, ...data) {
199
+ const listners = emitter.listeners(eventName);
200
+ if (listners.length === 0) {
201
+ return;
202
+ }
203
+ for (const listener of listners) {
204
+ await listener.apply(emitter, data);
205
+ }
206
+ }
207
+
208
+ // src/utils/isPropertyAccessible.ts
209
+ function isPropertyAccessible(obj, key) {
210
+ try {
211
+ obj[key];
212
+ return true;
213
+ } catch (e) {
214
+ return false;
215
+ }
216
+ }
217
+
218
+ // src/interceptors/fetch/index.ts
219
+ var _FetchInterceptor = class extends Interceptor {
220
+ constructor() {
221
+ super(_FetchInterceptor.symbol);
222
+ }
223
+ checkEnvironment() {
224
+ return typeof globalThis !== "undefined" && typeof globalThis.fetch !== "undefined";
225
+ }
226
+ setup() {
227
+ const pureFetch = globalThis.fetch;
228
+ invariant2(
229
+ !pureFetch[IS_PATCHED_MODULE],
230
+ 'Failed to patch the "fetch" module: already patched.'
231
+ );
232
+ globalThis.fetch = async (input, init) => {
233
+ var _a;
234
+ const requestId = uuidv4();
235
+ const request = new Request(input, init);
236
+ this.logger.info("[%s] %s", request.method, request.url);
237
+ const { interactiveRequest, requestController } = toInteractiveRequest(request);
238
+ this.logger.info(
239
+ 'emitting the "request" event for %d listener(s)...',
240
+ this.emitter.listenerCount("request")
241
+ );
242
+ this.emitter.once("request", ({ requestId: pendingRequestId }) => {
243
+ if (pendingRequestId !== requestId) {
244
+ return;
245
+ }
246
+ if (requestController.responsePromise.state === "pending") {
247
+ requestController.responsePromise.resolve(void 0);
248
+ }
249
+ });
250
+ this.logger.info("awaiting for the mocked response...");
251
+ const signal = interactiveRequest.signal;
252
+ const requestAborted = new DeferredPromise2();
253
+ signal.addEventListener(
254
+ "abort",
255
+ () => {
256
+ requestAborted.reject(signal.reason);
257
+ },
258
+ { once: true }
259
+ );
260
+ const resolverResult = await until(async () => {
261
+ const listenersFinished = emitAsync(this.emitter, "request", {
262
+ request: interactiveRequest,
263
+ requestId
264
+ });
265
+ await Promise.race([
266
+ requestAborted,
267
+ // Put the listeners invocation Promise in the same race condition
268
+ // with the request abort Promise because otherwise awaiting the listeners
269
+ // would always yield some response (or undefined).
270
+ listenersFinished,
271
+ requestController.responsePromise
272
+ ]);
273
+ this.logger.info("all request listeners have been resolved!");
274
+ const mockedResponse2 = await requestController.responsePromise;
275
+ this.logger.info("event.respondWith called with:", mockedResponse2);
276
+ return mockedResponse2;
277
+ });
278
+ if (requestAborted.state === "rejected") {
279
+ return Promise.reject(requestAborted.rejectionReason);
280
+ }
281
+ if (resolverResult.error) {
282
+ return Promise.reject(createNetworkError(resolverResult.error));
283
+ }
284
+ const mockedResponse = resolverResult.data;
285
+ if (mockedResponse && !((_a = request.signal) == null ? void 0 : _a.aborted)) {
286
+ this.logger.info("received mocked response:", mockedResponse);
287
+ if (isPropertyAccessible(mockedResponse, "type") && mockedResponse.type === "error") {
288
+ this.logger.info(
289
+ "received a network error response, rejecting the request promise..."
290
+ );
291
+ return Promise.reject(createNetworkError(mockedResponse));
292
+ }
293
+ const responseClone = mockedResponse.clone();
294
+ this.emitter.emit("response", {
295
+ response: responseClone,
296
+ isMockedResponse: true,
297
+ request: interactiveRequest,
298
+ requestId
299
+ });
300
+ const response = new Response(mockedResponse.body, mockedResponse);
301
+ Object.defineProperty(response, "url", {
302
+ writable: false,
303
+ enumerable: true,
304
+ configurable: false,
305
+ value: request.url
306
+ });
307
+ return response;
308
+ }
309
+ this.logger.info("no mocked response received!");
310
+ return pureFetch(request).then((response) => {
311
+ const responseClone = response.clone();
312
+ this.logger.info("original fetch performed", responseClone);
313
+ this.emitter.emit("response", {
314
+ response: responseClone,
315
+ isMockedResponse: false,
316
+ request: interactiveRequest,
317
+ requestId
318
+ });
319
+ return response;
320
+ });
321
+ };
322
+ Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, {
323
+ enumerable: true,
324
+ configurable: true,
325
+ value: true
326
+ });
327
+ this.subscriptions.push(() => {
328
+ Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, {
329
+ value: void 0
330
+ });
331
+ globalThis.fetch = pureFetch;
332
+ this.logger.info(
333
+ 'restored native "globalThis.fetch"!',
334
+ globalThis.fetch.name
335
+ );
336
+ });
337
+ }
338
+ };
339
+ var FetchInterceptor = _FetchInterceptor;
340
+ FetchInterceptor.symbol = Symbol("fetch");
341
+ function createNetworkError(cause) {
342
+ return Object.assign(new TypeError("Failed to fetch"), {
343
+ cause
344
+ });
345
+ }
346
+
347
+ // src/interceptors/XMLHttpRequest/index.ts
348
+ import { invariant as invariant4 } from "outvariant";
349
+
350
+ // src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts
351
+ import { until as until2 } from "@open-draft/until";
352
+
353
+ // src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts
354
+ import { invariant as invariant3 } from "outvariant";
355
+ import { isNodeProcess } from "is-node-process";
356
+
357
+ // src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts
358
+ function concatArrayBuffer(left, right) {
359
+ const result = new Uint8Array(left.byteLength + right.byteLength);
360
+ result.set(left, 0);
361
+ result.set(right, left.byteLength);
362
+ return result;
363
+ }
364
+
365
+ // src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts
366
+ var EventPolyfill = class {
367
+ constructor(type, options) {
368
+ this.AT_TARGET = 0;
369
+ this.BUBBLING_PHASE = 0;
370
+ this.CAPTURING_PHASE = 0;
371
+ this.NONE = 0;
372
+ this.type = "";
373
+ this.srcElement = null;
374
+ this.currentTarget = null;
375
+ this.eventPhase = 0;
376
+ this.isTrusted = true;
377
+ this.composed = false;
378
+ this.cancelable = true;
379
+ this.defaultPrevented = false;
380
+ this.bubbles = true;
381
+ this.lengthComputable = true;
382
+ this.loaded = 0;
383
+ this.total = 0;
384
+ this.cancelBubble = false;
385
+ this.returnValue = true;
386
+ this.type = type;
387
+ this.target = (options == null ? void 0 : options.target) || null;
388
+ this.currentTarget = (options == null ? void 0 : options.currentTarget) || null;
389
+ this.timeStamp = Date.now();
390
+ }
391
+ composedPath() {
392
+ return [];
393
+ }
394
+ initEvent(type, bubbles, cancelable) {
395
+ this.type = type;
396
+ this.bubbles = !!bubbles;
397
+ this.cancelable = !!cancelable;
398
+ }
399
+ preventDefault() {
400
+ this.defaultPrevented = true;
401
+ }
402
+ stopPropagation() {
403
+ }
404
+ stopImmediatePropagation() {
405
+ }
406
+ };
407
+
408
+ // src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts
409
+ var ProgressEventPolyfill = class extends EventPolyfill {
410
+ constructor(type, init) {
411
+ super(type);
412
+ this.lengthComputable = (init == null ? void 0 : init.lengthComputable) || false;
413
+ this.composed = (init == null ? void 0 : init.composed) || false;
414
+ this.loaded = (init == null ? void 0 : init.loaded) || 0;
415
+ this.total = (init == null ? void 0 : init.total) || 0;
416
+ }
417
+ };
418
+
419
+ // src/interceptors/XMLHttpRequest/utils/createEvent.ts
420
+ var SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== "undefined";
421
+ function createEvent(target, type, init) {
422
+ const progressEvents = [
423
+ "error",
424
+ "progress",
425
+ "loadstart",
426
+ "loadend",
427
+ "load",
428
+ "timeout",
429
+ "abort"
430
+ ];
431
+ const ProgressEventClass = SUPPORTS_PROGRESS_EVENT ? ProgressEvent : ProgressEventPolyfill;
432
+ const event = progressEvents.includes(type) ? new ProgressEventClass(type, {
433
+ lengthComputable: true,
434
+ loaded: (init == null ? void 0 : init.loaded) || 0,
435
+ total: (init == null ? void 0 : init.total) || 0
436
+ }) : new EventPolyfill(type, {
437
+ target,
438
+ currentTarget: target
439
+ });
440
+ return event;
441
+ }
442
+
443
+ // src/utils/bufferUtils.ts
444
+ var encoder = new TextEncoder();
445
+ function encodeBuffer(text) {
446
+ return encoder.encode(text);
447
+ }
448
+ function decodeBuffer(buffer, encoding) {
449
+ const decoder = new TextDecoder(encoding);
450
+ return decoder.decode(buffer);
451
+ }
452
+ function toArrayBuffer(array) {
453
+ return array.buffer.slice(
454
+ array.byteOffset,
455
+ array.byteOffset + array.byteLength
456
+ );
457
+ }
458
+
459
+ // src/utils/findPropertySource.ts
460
+ function findPropertySource(target, propertyName) {
461
+ if (!(propertyName in target)) {
462
+ return null;
463
+ }
464
+ const hasProperty = Object.prototype.hasOwnProperty.call(target, propertyName);
465
+ if (hasProperty) {
466
+ return target;
467
+ }
468
+ const prototype = Reflect.getPrototypeOf(target);
469
+ return prototype ? findPropertySource(prototype, propertyName) : null;
470
+ }
471
+
472
+ // src/utils/createProxy.ts
473
+ function createProxy(target, options) {
474
+ const proxy = new Proxy(target, optionsToProxyHandler(options));
475
+ return proxy;
476
+ }
477
+ function optionsToProxyHandler(options) {
478
+ const { constructorCall, methodCall, getProperty, setProperty } = options;
479
+ const handler = {};
480
+ if (typeof constructorCall !== "undefined") {
481
+ handler.construct = function(target, args, newTarget) {
482
+ const next = Reflect.construct.bind(null, target, args, newTarget);
483
+ return constructorCall.call(newTarget, args, next);
484
+ };
485
+ }
486
+ handler.set = function(target, propertyName, nextValue) {
487
+ const next = () => {
488
+ const propertySource = findPropertySource(target, propertyName) || target;
489
+ const ownDescriptors = Reflect.getOwnPropertyDescriptor(
490
+ propertySource,
491
+ propertyName
492
+ );
493
+ if (typeof (ownDescriptors == null ? void 0 : ownDescriptors.set) !== "undefined") {
494
+ ownDescriptors.set.apply(target, [nextValue]);
495
+ return true;
496
+ }
497
+ return Reflect.defineProperty(propertySource, propertyName, {
498
+ writable: true,
499
+ enumerable: true,
500
+ configurable: true,
501
+ value: nextValue
502
+ });
503
+ };
504
+ if (typeof setProperty !== "undefined") {
505
+ return setProperty.call(target, [propertyName, nextValue], next);
506
+ }
507
+ return next();
508
+ };
509
+ handler.get = function(target, propertyName, receiver) {
510
+ const next = () => target[propertyName];
511
+ const value = typeof getProperty !== "undefined" ? getProperty.call(target, [propertyName, receiver], next) : next();
512
+ if (typeof value === "function") {
513
+ return (...args) => {
514
+ const next2 = value.bind(target, ...args);
515
+ if (typeof methodCall !== "undefined") {
516
+ return methodCall.call(target, [propertyName, args], next2);
517
+ }
518
+ return next2();
519
+ };
520
+ }
521
+ return value;
522
+ };
523
+ return handler;
524
+ }
525
+
526
+ // src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts
527
+ function isDomParserSupportedType(type) {
528
+ const supportedTypes = [
529
+ "application/xhtml+xml",
530
+ "application/xml",
531
+ "image/svg+xml",
532
+ "text/html",
533
+ "text/xml"
534
+ ];
535
+ return supportedTypes.some((supportedType) => {
536
+ return type.startsWith(supportedType);
537
+ });
538
+ }
539
+
540
+ // src/utils/parseJson.ts
541
+ function parseJson(data) {
542
+ try {
543
+ const json = JSON.parse(data);
544
+ return json;
545
+ } catch (_) {
546
+ return null;
547
+ }
548
+ }
549
+
550
+ // src/utils/responseUtils.ts
551
+ var RESPONSE_STATUS_CODES_WITHOUT_BODY = /* @__PURE__ */ new Set([
552
+ 101,
553
+ 103,
554
+ 204,
555
+ 205,
556
+ 304
557
+ ]);
558
+ function isResponseWithoutBody(status) {
559
+ return RESPONSE_STATUS_CODES_WITHOUT_BODY.has(status);
560
+ }
561
+
562
+ // src/interceptors/XMLHttpRequest/utils/createResponse.ts
563
+ function createResponse(request, body) {
564
+ const responseBodyOrNull = isResponseWithoutBody(request.status) ? null : body;
565
+ return new Response(responseBodyOrNull, {
566
+ status: request.status,
567
+ statusText: request.statusText,
568
+ headers: createHeadersFromXMLHttpReqestHeaders(
569
+ request.getAllResponseHeaders()
570
+ )
571
+ });
572
+ }
573
+ function createHeadersFromXMLHttpReqestHeaders(headersString) {
574
+ const headers = new Headers();
575
+ const lines = headersString.split(/[\r\n]+/);
576
+ for (const line of lines) {
577
+ if (line.trim() === "") {
578
+ continue;
579
+ }
580
+ const [name, ...parts] = line.split(": ");
581
+ const value = parts.join(": ");
582
+ headers.append(name, value);
583
+ }
584
+ return headers;
585
+ }
586
+
587
+ // src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts
588
+ var IS_MOCKED_RESPONSE = Symbol("isMockedResponse");
589
+ var IS_NODE = isNodeProcess();
590
+ var XMLHttpRequestController = class {
591
+ constructor(initialRequest, logger) {
592
+ this.initialRequest = initialRequest;
593
+ this.logger = logger;
594
+ this.method = "GET";
595
+ this.url = null;
596
+ this.events = /* @__PURE__ */ new Map();
597
+ this.requestId = uuidv4();
598
+ this.requestHeaders = new Headers();
599
+ this.responseBuffer = new Uint8Array();
600
+ this.request = createProxy(initialRequest, {
601
+ setProperty: ([propertyName, nextValue], invoke) => {
602
+ switch (propertyName) {
603
+ case "ontimeout": {
604
+ const eventName = propertyName.slice(
605
+ 2
606
+ );
607
+ this.request.addEventListener(eventName, nextValue);
608
+ return invoke();
609
+ }
610
+ default: {
611
+ return invoke();
612
+ }
613
+ }
614
+ },
615
+ methodCall: ([methodName, args], invoke) => {
616
+ var _a;
617
+ switch (methodName) {
618
+ case "open": {
619
+ const [method, url] = args;
620
+ if (typeof url === "undefined") {
621
+ this.method = "GET";
622
+ this.url = toAbsoluteUrl(method);
623
+ } else {
624
+ this.method = method;
625
+ this.url = toAbsoluteUrl(url);
626
+ }
627
+ this.logger = this.logger.extend(`${this.method} ${this.url.href}`);
628
+ this.logger.info("open", this.method, this.url.href);
629
+ return invoke();
630
+ }
631
+ case "addEventListener": {
632
+ const [eventName, listener] = args;
633
+ this.registerEvent(eventName, listener);
634
+ this.logger.info("addEventListener", eventName, listener);
635
+ return invoke();
636
+ }
637
+ case "setRequestHeader": {
638
+ const [name, value] = args;
639
+ this.requestHeaders.set(name, value);
640
+ this.logger.info("setRequestHeader", name, value);
641
+ return invoke();
642
+ }
643
+ case "send": {
644
+ const [body] = args;
645
+ if (body != null) {
646
+ this.requestBody = typeof body === "string" ? encodeBuffer(body) : body;
647
+ }
648
+ this.request.addEventListener("load", () => {
649
+ if (typeof this.onResponse !== "undefined") {
650
+ const fetchResponse = createResponse(
651
+ this.request,
652
+ /**
653
+ * The `response` property is the right way to read
654
+ * the ambiguous response body, as the request's "responseType" may differ.
655
+ * @see https://xhr.spec.whatwg.org/#the-response-attribute
656
+ */
657
+ this.request.response
658
+ );
659
+ this.onResponse.call(this, {
660
+ response: fetchResponse,
661
+ isMockedResponse: IS_MOCKED_RESPONSE in this.request,
662
+ request: fetchRequest,
663
+ requestId: this.requestId
664
+ });
665
+ }
666
+ });
667
+ const fetchRequest = this.toFetchApiRequest();
668
+ const onceRequestSettled = ((_a = this.onRequest) == null ? void 0 : _a.call(this, {
669
+ request: fetchRequest,
670
+ requestId: this.requestId
671
+ })) || Promise.resolve();
672
+ onceRequestSettled.finally(() => {
673
+ if (this.request.readyState < this.request.LOADING) {
674
+ this.logger.info(
675
+ "request callback settled but request has not been handled (readystate %d), performing as-is...",
676
+ this.request.readyState
677
+ );
678
+ if (IS_NODE) {
679
+ this.request.setRequestHeader("X-Request-Id", this.requestId);
680
+ }
681
+ return invoke();
682
+ }
683
+ });
684
+ break;
685
+ }
686
+ default: {
687
+ return invoke();
688
+ }
689
+ }
690
+ }
691
+ });
692
+ }
693
+ registerEvent(eventName, listener) {
694
+ const prevEvents = this.events.get(eventName) || [];
695
+ const nextEvents = prevEvents.concat(listener);
696
+ this.events.set(eventName, nextEvents);
697
+ this.logger.info('registered event "%s"', eventName, listener);
698
+ }
699
+ /**
700
+ * Responds to the current request with the given
701
+ * Fetch API `Response` instance.
702
+ */
703
+ respondWith(response) {
704
+ this.logger.info(
705
+ "responding with a mocked response: %d %s",
706
+ response.status,
707
+ response.statusText
708
+ );
709
+ define(this.request, IS_MOCKED_RESPONSE, true);
710
+ define(this.request, "status", response.status);
711
+ define(this.request, "statusText", response.statusText);
712
+ define(this.request, "responseURL", this.url.href);
713
+ this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, {
714
+ apply: (_, __, args) => {
715
+ this.logger.info("getResponseHeader", args[0]);
716
+ if (this.request.readyState < this.request.HEADERS_RECEIVED) {
717
+ this.logger.info("headers not received yet, returning null");
718
+ return null;
719
+ }
720
+ const headerValue = response.headers.get(args[0]);
721
+ this.logger.info(
722
+ 'resolved response header "%s" to',
723
+ args[0],
724
+ headerValue
725
+ );
726
+ return headerValue;
727
+ }
728
+ });
729
+ this.request.getAllResponseHeaders = new Proxy(
730
+ this.request.getAllResponseHeaders,
731
+ {
732
+ apply: () => {
733
+ this.logger.info("getAllResponseHeaders");
734
+ if (this.request.readyState < this.request.HEADERS_RECEIVED) {
735
+ this.logger.info("headers not received yet, returning empty string");
736
+ return "";
737
+ }
738
+ const headersList = Array.from(response.headers.entries());
739
+ const allHeaders = headersList.map(([headerName, headerValue]) => {
740
+ return `${headerName}: ${headerValue}`;
741
+ }).join("\r\n");
742
+ this.logger.info("resolved all response headers to", allHeaders);
743
+ return allHeaders;
744
+ }
745
+ }
746
+ );
747
+ Object.defineProperties(this.request, {
748
+ response: {
749
+ enumerable: true,
750
+ configurable: false,
751
+ get: () => this.response
752
+ },
753
+ responseText: {
754
+ enumerable: true,
755
+ configurable: false,
756
+ get: () => this.responseText
757
+ },
758
+ responseXML: {
759
+ enumerable: true,
760
+ configurable: false,
761
+ get: () => this.responseXML
762
+ }
763
+ });
764
+ const totalResponseBodyLength = response.headers.has("Content-Length") ? Number(response.headers.get("Content-Length")) : (
765
+ /**
766
+ * @todo Infer the response body length from the response body.
767
+ */
768
+ void 0
769
+ );
770
+ this.logger.info("calculated response body length", totalResponseBodyLength);
771
+ this.trigger("loadstart", {
772
+ loaded: 0,
773
+ total: totalResponseBodyLength
774
+ });
775
+ this.setReadyState(this.request.HEADERS_RECEIVED);
776
+ this.setReadyState(this.request.LOADING);
777
+ const finalizeResponse = () => {
778
+ this.logger.info("finalizing the mocked response...");
779
+ this.setReadyState(this.request.DONE);
780
+ this.trigger("load", {
781
+ loaded: this.responseBuffer.byteLength,
782
+ total: totalResponseBodyLength
783
+ });
784
+ this.trigger("loadend", {
785
+ loaded: this.responseBuffer.byteLength,
786
+ total: totalResponseBodyLength
787
+ });
788
+ };
789
+ if (response.body) {
790
+ this.logger.info("mocked response has body, streaming...");
791
+ const reader = response.body.getReader();
792
+ const readNextResponseBodyChunk = async () => {
793
+ const { value, done } = await reader.read();
794
+ if (done) {
795
+ this.logger.info("response body stream done!");
796
+ finalizeResponse();
797
+ return;
798
+ }
799
+ if (value) {
800
+ this.logger.info("read response body chunk:", value);
801
+ this.responseBuffer = concatArrayBuffer(this.responseBuffer, value);
802
+ this.trigger("progress", {
803
+ loaded: this.responseBuffer.byteLength,
804
+ total: totalResponseBodyLength
805
+ });
806
+ }
807
+ readNextResponseBodyChunk();
808
+ };
809
+ readNextResponseBodyChunk();
810
+ } else {
811
+ finalizeResponse();
812
+ }
813
+ }
814
+ responseBufferToText() {
815
+ return decodeBuffer(this.responseBuffer);
816
+ }
817
+ get response() {
818
+ this.logger.info(
819
+ "getResponse (responseType: %s)",
820
+ this.request.responseType
821
+ );
822
+ if (this.request.readyState !== this.request.DONE) {
823
+ return null;
824
+ }
825
+ switch (this.request.responseType) {
826
+ case "json": {
827
+ const responseJson = parseJson(this.responseBufferToText());
828
+ this.logger.info("resolved response JSON", responseJson);
829
+ return responseJson;
830
+ }
831
+ case "arraybuffer": {
832
+ const arrayBuffer = toArrayBuffer(this.responseBuffer);
833
+ this.logger.info("resolved response ArrayBuffer", arrayBuffer);
834
+ return arrayBuffer;
835
+ }
836
+ case "blob": {
837
+ const mimeType = this.request.getResponseHeader("Content-Type") || "text/plain";
838
+ const responseBlob = new Blob([this.responseBufferToText()], {
839
+ type: mimeType
840
+ });
841
+ this.logger.info(
842
+ "resolved response Blob (mime type: %s)",
843
+ responseBlob,
844
+ mimeType
845
+ );
846
+ return responseBlob;
847
+ }
848
+ default: {
849
+ const responseText = this.responseBufferToText();
850
+ this.logger.info(
851
+ 'resolving "%s" response type as text',
852
+ this.request.responseType,
853
+ responseText
854
+ );
855
+ return responseText;
856
+ }
857
+ }
858
+ }
859
+ get responseText() {
860
+ invariant3(
861
+ this.request.responseType === "" || this.request.responseType === "text",
862
+ "InvalidStateError: The object is in invalid state."
863
+ );
864
+ if (this.request.readyState !== this.request.LOADING && this.request.readyState !== this.request.DONE) {
865
+ return "";
866
+ }
867
+ const responseText = this.responseBufferToText();
868
+ this.logger.info('getResponseText: "%s"', responseText);
869
+ return responseText;
870
+ }
871
+ get responseXML() {
872
+ invariant3(
873
+ this.request.responseType === "" || this.request.responseType === "document",
874
+ "InvalidStateError: The object is in invalid state."
875
+ );
876
+ if (this.request.readyState !== this.request.DONE) {
877
+ return null;
878
+ }
879
+ const contentType = this.request.getResponseHeader("Content-Type") || "";
880
+ if (typeof DOMParser === "undefined") {
881
+ console.warn(
882
+ "Cannot retrieve XMLHttpRequest response body as XML: DOMParser is not defined. You are likely using an environment that is not browser or does not polyfill browser globals correctly."
883
+ );
884
+ return null;
885
+ }
886
+ if (isDomParserSupportedType(contentType)) {
887
+ return new DOMParser().parseFromString(
888
+ this.responseBufferToText(),
889
+ contentType
890
+ );
891
+ }
892
+ return null;
893
+ }
894
+ errorWith(error) {
895
+ this.logger.info("responding with an error");
896
+ this.setReadyState(this.request.DONE);
897
+ this.trigger("error");
898
+ this.trigger("loadend");
899
+ }
900
+ /**
901
+ * Transitions this request's `readyState` to the given one.
902
+ */
903
+ setReadyState(nextReadyState) {
904
+ this.logger.info(
905
+ "setReadyState: %d -> %d",
906
+ this.request.readyState,
907
+ nextReadyState
908
+ );
909
+ if (this.request.readyState === nextReadyState) {
910
+ this.logger.info("ready state identical, skipping transition...");
911
+ return;
912
+ }
913
+ define(this.request, "readyState", nextReadyState);
914
+ this.logger.info("set readyState to: %d", nextReadyState);
915
+ if (nextReadyState !== this.request.UNSENT) {
916
+ this.logger.info('triggerring "readystatechange" event...');
917
+ this.trigger("readystatechange");
918
+ }
919
+ }
920
+ /**
921
+ * Triggers given event on the `XMLHttpRequest` instance.
922
+ */
923
+ trigger(eventName, options) {
924
+ const callback = this.request[`on${eventName}`];
925
+ const event = createEvent(this.request, eventName, options);
926
+ this.logger.info('trigger "%s"', eventName, options || "");
927
+ if (typeof callback === "function") {
928
+ this.logger.info('found a direct "%s" callback, calling...', eventName);
929
+ callback.call(this.request, event);
930
+ }
931
+ for (const [registeredEventName, listeners] of this.events) {
932
+ if (registeredEventName === eventName) {
933
+ this.logger.info(
934
+ 'found %d listener(s) for "%s" event, calling...',
935
+ listeners.length,
936
+ eventName
937
+ );
938
+ listeners.forEach((listener) => listener.call(this.request, event));
939
+ }
940
+ }
941
+ }
942
+ /**
943
+ * Converts this `XMLHttpRequest` instance into a Fetch API `Request` instance.
944
+ */
945
+ toFetchApiRequest() {
946
+ this.logger.info("converting request to a Fetch API Request...");
947
+ const fetchRequest = new Request(this.url.href, {
948
+ method: this.method,
949
+ headers: this.requestHeaders,
950
+ /**
951
+ * @see https://xhr.spec.whatwg.org/#cross-origin-credentials
952
+ */
953
+ credentials: this.request.withCredentials ? "include" : "same-origin",
954
+ body: ["GET", "HEAD"].includes(this.method) ? null : this.requestBody
955
+ });
956
+ const proxyHeaders = createProxy(fetchRequest.headers, {
957
+ methodCall: ([methodName, args], invoke) => {
958
+ switch (methodName) {
959
+ case "append":
960
+ case "set": {
961
+ const [headerName, headerValue] = args;
962
+ this.request.setRequestHeader(headerName, headerValue);
963
+ break;
964
+ }
965
+ case "delete": {
966
+ const [headerName] = args;
967
+ console.warn(
968
+ `XMLHttpRequest: Cannot remove a "${headerName}" header from the Fetch API representation of the "${fetchRequest.method} ${fetchRequest.url}" request. XMLHttpRequest headers cannot be removed.`
969
+ );
970
+ break;
971
+ }
972
+ }
973
+ return invoke();
974
+ }
975
+ });
976
+ define(fetchRequest, "headers", proxyHeaders);
977
+ this.logger.info("converted request to a Fetch API Request!", fetchRequest);
978
+ return fetchRequest;
979
+ }
980
+ };
981
+ function toAbsoluteUrl(url) {
982
+ if (typeof location === "undefined") {
983
+ return new URL(url);
984
+ }
985
+ return new URL(url.toString(), location.href);
986
+ }
987
+ function define(target, property, value) {
988
+ Reflect.defineProperty(target, property, {
989
+ // Ensure writable properties to allow redefining readonly properties.
990
+ writable: true,
991
+ enumerable: true,
992
+ value
993
+ });
994
+ }
995
+
996
+ // src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts
997
+ function createXMLHttpRequestProxy({
998
+ emitter,
999
+ logger
1000
+ }) {
1001
+ const XMLHttpRequestProxy = new Proxy(globalThis.XMLHttpRequest, {
1002
+ construct(target, args, newTarget) {
1003
+ logger.info("constructed new XMLHttpRequest");
1004
+ const originalRequest = Reflect.construct(target, args, newTarget);
1005
+ const prototypeDescriptors = Object.getOwnPropertyDescriptors(
1006
+ target.prototype
1007
+ );
1008
+ for (const propertyName in prototypeDescriptors) {
1009
+ Reflect.defineProperty(
1010
+ originalRequest,
1011
+ propertyName,
1012
+ prototypeDescriptors[propertyName]
1013
+ );
1014
+ }
1015
+ const xhrRequestController = new XMLHttpRequestController(
1016
+ originalRequest,
1017
+ logger
1018
+ );
1019
+ xhrRequestController.onRequest = async function({ request, requestId }) {
1020
+ const { interactiveRequest, requestController } = toInteractiveRequest(request);
1021
+ this.logger.info("awaiting mocked response...");
1022
+ emitter.once("request", ({ requestId: pendingRequestId }) => {
1023
+ if (pendingRequestId !== requestId) {
1024
+ return;
1025
+ }
1026
+ if (requestController.responsePromise.state === "pending") {
1027
+ requestController.respondWith(void 0);
1028
+ }
1029
+ });
1030
+ const resolverResult = await until2(async () => {
1031
+ this.logger.info(
1032
+ 'emitting the "request" event for %s listener(s)...',
1033
+ emitter.listenerCount("request")
1034
+ );
1035
+ await emitAsync(emitter, "request", {
1036
+ request: interactiveRequest,
1037
+ requestId
1038
+ });
1039
+ this.logger.info('all "request" listeners settled!');
1040
+ const mockedResponse2 = await requestController.responsePromise;
1041
+ this.logger.info("event.respondWith called with:", mockedResponse2);
1042
+ return mockedResponse2;
1043
+ });
1044
+ if (resolverResult.error) {
1045
+ this.logger.info(
1046
+ "request listener threw an exception, aborting request...",
1047
+ resolverResult.error
1048
+ );
1049
+ xhrRequestController.errorWith(resolverResult.error);
1050
+ return;
1051
+ }
1052
+ const mockedResponse = resolverResult.data;
1053
+ if (typeof mockedResponse !== "undefined") {
1054
+ this.logger.info(
1055
+ "received mocked response: %d %s",
1056
+ mockedResponse.status,
1057
+ mockedResponse.statusText
1058
+ );
1059
+ if (mockedResponse.type === "error") {
1060
+ this.logger.info(
1061
+ "received a network error response, rejecting the request promise..."
1062
+ );
1063
+ xhrRequestController.errorWith(new TypeError("Network error"));
1064
+ return;
1065
+ }
1066
+ return xhrRequestController.respondWith(mockedResponse);
1067
+ }
1068
+ this.logger.info(
1069
+ "no mocked response received, performing request as-is..."
1070
+ );
1071
+ };
1072
+ xhrRequestController.onResponse = async function({
1073
+ response,
1074
+ isMockedResponse,
1075
+ request,
1076
+ requestId
1077
+ }) {
1078
+ this.logger.info(
1079
+ 'emitting the "response" event for %s listener(s)...',
1080
+ emitter.listenerCount("response")
1081
+ );
1082
+ emitter.emit("response", {
1083
+ response,
1084
+ isMockedResponse,
1085
+ request,
1086
+ requestId
1087
+ });
1088
+ };
1089
+ return xhrRequestController.request;
1090
+ }
1091
+ });
1092
+ return XMLHttpRequestProxy;
1093
+ }
1094
+
1095
+ // src/interceptors/XMLHttpRequest/index.ts
1096
+ var _XMLHttpRequestInterceptor = class extends Interceptor {
1097
+ constructor() {
1098
+ super(_XMLHttpRequestInterceptor.interceptorSymbol);
1099
+ }
1100
+ checkEnvironment() {
1101
+ return typeof globalThis.XMLHttpRequest !== "undefined";
1102
+ }
1103
+ setup() {
1104
+ const logger = this.logger.extend("setup");
1105
+ logger.info('patching "XMLHttpRequest" module...');
1106
+ const PureXMLHttpRequest = globalThis.XMLHttpRequest;
1107
+ invariant4(
1108
+ !PureXMLHttpRequest[IS_PATCHED_MODULE],
1109
+ 'Failed to patch the "XMLHttpRequest" module: already patched.'
1110
+ );
1111
+ globalThis.XMLHttpRequest = createXMLHttpRequestProxy({
1112
+ emitter: this.emitter,
1113
+ logger: this.logger
1114
+ });
1115
+ logger.info(
1116
+ 'native "XMLHttpRequest" module patched!',
1117
+ globalThis.XMLHttpRequest.name
1118
+ );
1119
+ Object.defineProperty(globalThis.XMLHttpRequest, IS_PATCHED_MODULE, {
1120
+ enumerable: true,
1121
+ configurable: true,
1122
+ value: true
1123
+ });
1124
+ this.subscriptions.push(() => {
1125
+ Object.defineProperty(globalThis.XMLHttpRequest, IS_PATCHED_MODULE, {
1126
+ value: void 0
1127
+ });
1128
+ globalThis.XMLHttpRequest = PureXMLHttpRequest;
1129
+ logger.info(
1130
+ 'native "XMLHttpRequest" module restored!',
1131
+ globalThis.XMLHttpRequest.name
1132
+ );
1133
+ });
1134
+ }
1135
+ };
1136
+ var XMLHttpRequestInterceptor = _XMLHttpRequestInterceptor;
1137
+ XMLHttpRequestInterceptor.interceptorSymbol = Symbol("xhr");
1138
+
1139
+ // src/presets/browser.ts
1140
+ var browser_default = [
1141
+ new FetchInterceptor(),
1142
+ new XMLHttpRequestInterceptor()
1143
+ ];
1144
+ export {
1145
+ browser_default as default
1146
+ };