@ccheever/exact-ibex-runtime 0.1.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 (161) hide show
  1. package/package.json +63 -0
  2. package/src/abort/AbortController.ts +23 -0
  3. package/src/abort/AbortSignal.ts +152 -0
  4. package/src/abort/index.ts +2 -0
  5. package/src/accessibility.ts +12 -0
  6. package/src/arraybuffer-detach.ts +109 -0
  7. package/src/base64/base64.ts +168 -0
  8. package/src/base64/index.ts +1 -0
  9. package/src/blob/Blob.ts +259 -0
  10. package/src/blob/File.ts +59 -0
  11. package/src/blob/FormData.ts +323 -0
  12. package/src/blob/index.ts +3 -0
  13. package/src/bootstrap.ts +1946 -0
  14. package/src/broadcast/BroadcastChannel.ts +280 -0
  15. package/src/broadcast/index.ts +5 -0
  16. package/src/cache/Cache.ts +349 -0
  17. package/src/cache/CacheStorage.ts +89 -0
  18. package/src/cache/index.ts +27 -0
  19. package/src/camera/index.ts +6202 -0
  20. package/src/camera/processor.worker.ts +194 -0
  21. package/src/camera/scene.ts +195 -0
  22. package/src/clipboard/Clipboard.ts +129 -0
  23. package/src/clipboard/ClipboardItem.ts +97 -0
  24. package/src/clipboard/index.ts +6 -0
  25. package/src/clone/index.ts +1 -0
  26. package/src/clone/structuredClone.ts +389 -0
  27. package/src/clone/transferableSymbols.ts +2 -0
  28. package/src/compression/CompressionStream.ts +146 -0
  29. package/src/compression/DecompressionStream.ts +342 -0
  30. package/src/compression/index.ts +4 -0
  31. package/src/console/Console.ts +341 -0
  32. package/src/console/index.ts +2 -0
  33. package/src/core/accessibility-state.ts +263 -0
  34. package/src/core/accessibility.ts +184 -0
  35. package/src/core/agent-state.ts +37 -0
  36. package/src/core/diagnostics-logs.ts +144 -0
  37. package/src/core/host-call-bridge.ts +16 -0
  38. package/src/core/i18n-helpers.ts +189 -0
  39. package/src/core/locale-state.ts +253 -0
  40. package/src/core/locale.ts +95 -0
  41. package/src/crypto/Crypto.ts +2743 -0
  42. package/src/crypto/index.ts +1 -0
  43. package/src/diagnostics/logs.ts +7 -0
  44. package/src/encoding/TextDecoder.ts +1181 -0
  45. package/src/encoding/TextDecoderStream.ts +58 -0
  46. package/src/encoding/TextEncoder.ts +180 -0
  47. package/src/encoding/TextEncoderStream.ts +39 -0
  48. package/src/encoding/index.ts +8 -0
  49. package/src/events/CloseEvent.ts +91 -0
  50. package/src/events/DOMException.ts +409 -0
  51. package/src/events/ErrorEvent.ts +39 -0
  52. package/src/events/Event.ts +151 -0
  53. package/src/events/EventTarget.ts +280 -0
  54. package/src/events/FocusEvent.ts +27 -0
  55. package/src/events/KeyboardEvent.ts +46 -0
  56. package/src/events/MessageEvent.ts +61 -0
  57. package/src/events/ProgressEvent.ts +33 -0
  58. package/src/events/PromiseRejectionEvent.ts +31 -0
  59. package/src/events/index.ts +52 -0
  60. package/src/eventsource/EventSource.ts +371 -0
  61. package/src/eventsource/index.ts +2 -0
  62. package/src/fetch/Headers.ts +642 -0
  63. package/src/fetch/Request.ts +760 -0
  64. package/src/fetch/Response.ts +543 -0
  65. package/src/fetch/body.ts +1256 -0
  66. package/src/fetch/cookie-jar.ts +566 -0
  67. package/src/fetch/demo.ts +207 -0
  68. package/src/fetch/errors.ts +101 -0
  69. package/src/fetch/fetch.ts +2610 -0
  70. package/src/fetch/index.ts +101 -0
  71. package/src/fetch/native-bridge.ts +65 -0
  72. package/src/fetch/types.ts +258 -0
  73. package/src/filereader/FileReader.ts +236 -0
  74. package/src/filereader/index.ts +1 -0
  75. package/src/fs/Dirent.ts +39 -0
  76. package/src/fs/ExactFile.ts +450 -0
  77. package/src/fs/Stats.ts +80 -0
  78. package/src/fs/index.ts +944 -0
  79. package/src/fs/promises.ts +386 -0
  80. package/src/fs/shared.ts +328 -0
  81. package/src/http-server/index.js +697 -0
  82. package/src/http-server/index.ts +27 -0
  83. package/src/identity.generated.ts +14 -0
  84. package/src/index.ts +283 -0
  85. package/src/indexeddb/IDBCursor.ts +188 -0
  86. package/src/indexeddb/IDBDatabase.ts +343 -0
  87. package/src/indexeddb/IDBFactory.ts +269 -0
  88. package/src/indexeddb/IDBIndex.ts +194 -0
  89. package/src/indexeddb/IDBKeyRange.ts +109 -0
  90. package/src/indexeddb/IDBObjectStore.ts +468 -0
  91. package/src/indexeddb/IDBRequest.ts +163 -0
  92. package/src/indexeddb/IDBTransaction.ts +207 -0
  93. package/src/indexeddb/index.ts +34 -0
  94. package/src/indexeddb/utils.ts +52 -0
  95. package/src/inspect/index.ts +1 -0
  96. package/src/inspect/inspect.ts +465 -0
  97. package/src/internal/detect.ts +104 -0
  98. package/src/locale.ts +10 -0
  99. package/src/location/index.ts +1059 -0
  100. package/src/locks/LockManager.ts +460 -0
  101. package/src/locks/index.ts +12 -0
  102. package/src/media/VideoFrame.ts +58 -0
  103. package/src/messaging/MessageChannel.ts +31 -0
  104. package/src/messaging/MessagePort.ts +180 -0
  105. package/src/messaging/index.ts +2 -0
  106. package/src/messaging.ts +247 -0
  107. package/src/native/NativeModules.ts +354 -0
  108. package/src/native/index.ts +1 -0
  109. package/src/navigator/Navigator.ts +351 -0
  110. package/src/navigator/index.ts +1 -0
  111. package/src/node/Buffer.ts +1786 -0
  112. package/src/node/index.ts +4 -0
  113. package/src/node/path.ts +495 -0
  114. package/src/node/process.ts +2528 -0
  115. package/src/performance/Performance.ts +532 -0
  116. package/src/performance/index.ts +21 -0
  117. package/src/polyfills/array.ts +236 -0
  118. package/src/polyfills/arraybuffer.ts +172 -0
  119. package/src/polyfills/groupby.ts +85 -0
  120. package/src/polyfills/index.ts +85 -0
  121. package/src/polyfills/intl.ts +1956 -0
  122. package/src/polyfills/iterator.ts +479 -0
  123. package/src/polyfills/promise.ts +37 -0
  124. package/src/polyfills/set.ts +245 -0
  125. package/src/polyfills/string.ts +85 -0
  126. package/src/polyfills/typedarray.ts +110 -0
  127. package/src/promise-rejection-tracking.ts +464 -0
  128. package/src/react-native/index.ts +388 -0
  129. package/src/runtime-entry.ts +55 -0
  130. package/src/scheduling/AnimationFrame.ts +105 -0
  131. package/src/scheduling/IdleCallback.ts +167 -0
  132. package/src/scheduling/index.ts +13 -0
  133. package/src/security/Capabilities.ts +1146 -0
  134. package/src/security/Permissions.ts +392 -0
  135. package/src/security/capability-bits.generated.ts +63 -0
  136. package/src/security/index.ts +16 -0
  137. package/src/sqlite/Database.ts +456 -0
  138. package/src/sqlite/Statement.ts +206 -0
  139. package/src/sqlite/constants.ts +79 -0
  140. package/src/sqlite/errors.ts +25 -0
  141. package/src/sqlite/index.ts +34 -0
  142. package/src/sqlite/module.js +438 -0
  143. package/src/storage/Storage.ts +291 -0
  144. package/src/storage/StorageManager.ts +91 -0
  145. package/src/storage/index.ts +3 -0
  146. package/src/stream-compat.ts +47 -0
  147. package/src/streams/ReadableStream.ts +4131 -0
  148. package/src/streams/TransformStream.ts +375 -0
  149. package/src/streams/WritableStream.ts +866 -0
  150. package/src/streams/index.ts +41 -0
  151. package/src/timers/Timers.ts +296 -0
  152. package/src/timers/index.ts +11 -0
  153. package/src/url/URL.ts +656 -0
  154. package/src/url/URLPattern.ts +850 -0
  155. package/src/url/URLSearchParams.ts +244 -0
  156. package/src/url/index.ts +9 -0
  157. package/src/websocket/WebSocket.ts +770 -0
  158. package/src/websocket/WebSocketError.ts +52 -0
  159. package/src/websocket/WebSocketStream.ts +628 -0
  160. package/src/websocket/index.ts +7 -0
  161. package/src/window/index.ts +872 -0
@@ -0,0 +1,543 @@
1
+ /**
2
+ * Response API Implementation
3
+ *
4
+ * Implements the WHATWG Fetch Standard Response interface.
5
+ * @see https://fetch.spec.whatwg.org/#response-class
6
+ */
7
+
8
+ import { Headers } from './Headers.js';
9
+ import {
10
+ BodyMixin,
11
+ createReadableStreamFromUint8Array,
12
+ readableStreamToUint8Array,
13
+ resolveWithoutThenable,
14
+ getTextEncoder,
15
+ isFormData as isFormDataBody,
16
+ encodeFormData,
17
+ normalizeReadableStreamBody,
18
+ isAsyncIterableBody,
19
+ createReadableStreamFromAsyncIterableBody,
20
+ } from './body.js';
21
+ import { isReadableStream } from '../streams/index.js';
22
+ import type {
23
+ ResponseInit,
24
+ ResponseType,
25
+ NativeResponse,
26
+ NativeStreamingResponse,
27
+ } from './types.js';
28
+
29
+ /**
30
+ * HTTP status codes that represent redirects.
31
+ */
32
+ const REDIRECT_STATUS_CODES = [301, 302, 303, 307, 308];
33
+
34
+ /**
35
+ * HTTP status codes that are considered null body statuses.
36
+ */
37
+ const NULL_BODY_STATUS_CODES = [204, 205, 304];
38
+
39
+ /**
40
+ * Default HTTP status text for common status codes.
41
+ * Used when the native bridge doesn't provide a status text
42
+ * (e.g., NSURLSession on macOS doesn't expose HTTP reason phrases,
43
+ * and HTTP/2 doesn't have reason phrases at all).
44
+ */
45
+ const DEFAULT_STATUS_TEXT: Record<number, string> = {
46
+ 100: 'Continue',
47
+ 101: 'Switching Protocols',
48
+ 200: 'OK',
49
+ 201: 'Created',
50
+ 202: 'Accepted',
51
+ 203: 'Non-Authoritative Information',
52
+ 204: 'No Content',
53
+ 205: 'Reset Content',
54
+ 206: 'Partial Content',
55
+ 300: 'Multiple Choices',
56
+ 301: 'Moved Permanently',
57
+ 302: 'Found',
58
+ 303: 'See Other',
59
+ 304: 'Not Modified',
60
+ 307: 'Temporary Redirect',
61
+ 308: 'Permanent Redirect',
62
+ 400: 'Bad Request',
63
+ 401: 'Unauthorized',
64
+ 403: 'Forbidden',
65
+ 404: 'Not Found',
66
+ 405: 'Method Not Allowed',
67
+ 406: 'Not Acceptable',
68
+ 408: 'Request Timeout',
69
+ 409: 'Conflict',
70
+ 410: 'Gone',
71
+ 411: 'Length Required',
72
+ 412: 'Precondition Failed',
73
+ 413: 'Payload Too Large',
74
+ 414: 'URI Too Long',
75
+ 415: 'Unsupported Media Type',
76
+ 416: 'Range Not Satisfiable',
77
+ 417: 'Expectation Failed',
78
+ 422: 'Unprocessable Entity',
79
+ 429: 'Too Many Requests',
80
+ 500: 'Internal Server Error',
81
+ 501: 'Not Implemented',
82
+ 502: 'Bad Gateway',
83
+ 503: 'Service Unavailable',
84
+ 504: 'Gateway Timeout',
85
+ };
86
+
87
+ function isBunCompatResponseTest(): boolean {
88
+ if ((globalThis as { __exactRuntimeContext?: string }).__exactRuntimeContext === 'shell') {
89
+ return false;
90
+ }
91
+ if (typeof process !== 'object' || !process || typeof process.env !== 'object') {
92
+ return false;
93
+ }
94
+
95
+ return process.env.EXACT_COMPAT_TEST === '1' && process.env.EXACT_TEST_SECTION === 'bun';
96
+ }
97
+
98
+ /**
99
+ * The Response interface of the Fetch API represents the response to a request.
100
+ */
101
+ export class Response extends BodyMixin {
102
+ private _status: number;
103
+ private _statusText: string;
104
+ private _headers: Headers;
105
+ private _ok: boolean;
106
+ private _type: ResponseType;
107
+ private _url: string;
108
+ private _redirected: boolean;
109
+
110
+ constructor(body?: BodyInit | null, init?: ResponseInit) {
111
+ super();
112
+
113
+ // Initialize from options
114
+ this._status = Number(init?.status ?? 200);
115
+ this._statusText = init?.statusText !== undefined ? String(init.statusText) : '';
116
+ this._ok = this._status >= 200 && this._status < 300;
117
+ this._type = 'default';
118
+ this._url = '';
119
+ this._redirected = false;
120
+
121
+ // Validate status — per spec, only 200-599 is valid for Response constructor
122
+ if (!Number.isFinite(this._status) || !Number.isInteger(this._status) || this._status < 200 || this._status > 599) {
123
+ throw new RangeError(`Failed to construct 'Response': The status provided (${this._status}) is outside the range [200, 599].`);
124
+ }
125
+
126
+ // Validate statusText — must be a valid reason-phrase per HTTP spec
127
+ // reason-phrase = *( HTAB / SP / VCHAR / obs-text )
128
+ // VCHAR = 0x21-0x7E, obs-text = 0x80-0xFF, SP = 0x20, HTAB = 0x09
129
+ if (this._statusText) {
130
+ for (let i = 0; i < this._statusText.length; i++) {
131
+ const c = this._statusText.charCodeAt(i);
132
+ if (c > 0xFF || (c < 0x20 && c !== 0x09)) {
133
+ throw new TypeError(`Failed to construct 'Response': Invalid statusText`);
134
+ }
135
+ }
136
+ }
137
+
138
+ // Validate body for null body status
139
+ if (body !== null && body !== undefined && NULL_BODY_STATUS_CODES.includes(this._status)) {
140
+ throw new TypeError('Response with null body status cannot have body');
141
+ }
142
+
143
+ // Initialize headers
144
+ this._headers = new Headers(init?.headers);
145
+ this._headers._guard = 'response';
146
+
147
+ // Handle body
148
+ if (body !== null && body !== undefined) {
149
+ // Use isReadableStream() instead of instanceof to avoid ReferenceError
150
+ // when ReadableStream global isn't available
151
+ if (isReadableStream(body)) {
152
+ // Per spec: reject locked or disturbed ReadableStream bodies
153
+ if ((body as ReadableStream).locked || (body as any)._disturbed) {
154
+ throw new TypeError("Failed to construct 'Response': body ReadableStream is locked.");
155
+ }
156
+ this._body = normalizeReadableStreamBody(body as ReadableStream<unknown>, 'Response body');
157
+ } else if (isAsyncIterableBody(body)) {
158
+ this._body = createReadableStreamFromAsyncIterableBody(body, 'Response body');
159
+ } else if (typeof body === 'string') {
160
+ const bytes = getTextEncoder().encode(body);
161
+ this._bodyBuffer = bytes.buffer as ArrayBuffer;
162
+ this._body = createReadableStreamFromUint8Array(bytes);
163
+
164
+ // Set default Content-Type for string bodies
165
+ if (!this._headers.has('content-type')) {
166
+ this._headers.set('content-type', 'text/plain;charset=UTF-8');
167
+ }
168
+ } else if (body instanceof ArrayBuffer) {
169
+ this._bodyBuffer = body.slice(0);
170
+ this._body = createReadableStreamFromUint8Array(new Uint8Array(this._bodyBuffer));
171
+ } else if (ArrayBuffer.isView(body)) {
172
+ this._bodyBuffer = body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength);
173
+ this._body = createReadableStreamFromUint8Array(new Uint8Array(this._bodyBuffer));
174
+ } else if (body instanceof Blob) {
175
+ // Try sync bytes extraction first (our custom Blob), then stream
176
+ if (typeof (body as any)._getBytes === 'function') {
177
+ const bytes = (body as any)._getBytes() as Uint8Array;
178
+ this._bodyBuffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;
179
+ this._body = createReadableStreamFromUint8Array(bytes);
180
+ } else if (typeof body.stream === 'function') {
181
+ const stream = body.stream();
182
+ if (stream) {
183
+ this._body = stream;
184
+ } else {
185
+ // Fallback: bootstrap Blob.stream() returns null
186
+ this._body = createReadableStreamFromUint8Array(new Uint8Array(0));
187
+ }
188
+ }
189
+
190
+ // Set Content-Type from Blob.type if non-empty and not already set
191
+ if (body.type && !this._headers.has('content-type')) {
192
+ this._headers.set('content-type', body.type);
193
+ }
194
+ } else if (body instanceof URLSearchParams) {
195
+ const bytes = getTextEncoder().encode(body.toString());
196
+ this._bodyBuffer = bytes.buffer as ArrayBuffer;
197
+ this._body = createReadableStreamFromUint8Array(bytes);
198
+
199
+ if (!this._headers.has('content-type')) {
200
+ this._headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
201
+ }
202
+ } else if (isFormDataBody(body)) {
203
+ const encoded = encodeFormData(body as FormData);
204
+ this._bodyBuffer = encoded.body.buffer as ArrayBuffer;
205
+ this._body = createReadableStreamFromUint8Array(encoded.body);
206
+
207
+ if (!this._headers.has('content-type')) {
208
+ this._headers.set('content-type', encoded.contentType);
209
+ }
210
+ }
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Whether the response was successful (status 200-299).
216
+ */
217
+ get ok(): boolean {
218
+ return this._ok;
219
+ }
220
+
221
+ /**
222
+ * The HTTP status code of the response.
223
+ */
224
+ get status(): number {
225
+ return this._status;
226
+ }
227
+
228
+ /**
229
+ * The HTTP status message of the response.
230
+ */
231
+ get statusText(): string {
232
+ return this._statusText;
233
+ }
234
+
235
+ /**
236
+ * The headers of the response.
237
+ */
238
+ get headers(): Headers {
239
+ return this._headers;
240
+ }
241
+
242
+ /**
243
+ * The type of the response.
244
+ */
245
+ get type(): ResponseType {
246
+ return this._type;
247
+ }
248
+
249
+ /**
250
+ * The URL of the response.
251
+ */
252
+ get url(): string {
253
+ // Per Fetch spec: response URL excludes fragment
254
+ if (this._url && this._url.includes('#')) {
255
+ return this._url.split('#')[0];
256
+ }
257
+ return this._url;
258
+ }
259
+
260
+ /**
261
+ * Whether the response is the result of a redirect.
262
+ */
263
+ get redirected(): boolean {
264
+ return this._redirected;
265
+ }
266
+
267
+ /**
268
+ * Get the Content-Type header value.
269
+ */
270
+ protected override _getContentType(): string | null {
271
+ return this._headers.get('content-type');
272
+ }
273
+
274
+ /**
275
+ * Get the body as ArrayBuffer.
276
+ * Uses explicit promise chains (not async) and resolveWithoutThenable to
277
+ * prevent Object.prototype.then from intercepting resolution values.
278
+ */
279
+ protected override _getBodyBuffer(): Promise<ArrayBuffer> {
280
+ if (this._bodyBuffer) {
281
+ const buf = this._bodyBuffer;
282
+ return new Promise<ArrayBuffer>(function (resolve) {
283
+ resolveWithoutThenable(resolve, buf);
284
+ });
285
+ }
286
+
287
+ if (this._body === null) {
288
+ return Promise.resolve(new ArrayBuffer(0));
289
+ }
290
+
291
+ const self = this;
292
+ return new Promise<ArrayBuffer>(function (resolve, reject) {
293
+ readableStreamToUint8Array(self._body!).then(
294
+ function (bytes) {
295
+ self._bodyBuffer = bytes.buffer as ArrayBuffer;
296
+ resolveWithoutThenable(resolve, self._bodyBuffer);
297
+ },
298
+ reject
299
+ );
300
+ });
301
+ }
302
+
303
+ /**
304
+ * Creates a copy of the Response object.
305
+ */
306
+ clone(): Response {
307
+ if (this._bodyUsed) {
308
+ throw new TypeError('Cannot clone a Response whose body has already been used');
309
+ }
310
+
311
+ const cloned = new Response(null, {
312
+ status: this._status,
313
+ statusText: this._statusText,
314
+ headers: new Headers(this._headers),
315
+ });
316
+
317
+ cloned._ok = this._ok;
318
+ cloned._type = this._type;
319
+ cloned._url = this._url;
320
+ cloned._redirected = this._redirected;
321
+ cloned._bodyBuffer = this._bodyBuffer;
322
+
323
+ // Clone the body stream using tee if available
324
+ if (this._body) {
325
+ const [stream1, stream2] = this._body.tee();
326
+ this._body = stream1;
327
+ cloned._body = stream2;
328
+ }
329
+
330
+ return cloned;
331
+ }
332
+
333
+ /**
334
+ * Creates a new Response representing a network error.
335
+ */
336
+ static error(): Response {
337
+ const response = new Response(null);
338
+ response._status = 0;
339
+ response._statusText = '';
340
+ response._ok = false;
341
+ response._type = 'error';
342
+ response._headers = new Headers();
343
+ response._headers._guard = 'immutable';
344
+ return response;
345
+ }
346
+
347
+ /**
348
+ * Creates a new Response for a redirect to the specified URL.
349
+ */
350
+ static redirect(url: string, status: number | { status?: number } = 302): Response {
351
+ const bunCompat = isBunCompatResponseTest();
352
+ const rawStatus =
353
+ typeof status === 'object' && status !== null && 'status' in status
354
+ ? status.status
355
+ : status;
356
+ const normalizedStatus =
357
+ rawStatus === undefined || rawStatus === null || (bunCompat && typeof rawStatus !== 'number')
358
+ ? 302
359
+ : Number(rawStatus);
360
+
361
+ if (!REDIRECT_STATUS_CODES.includes(normalizedStatus)) {
362
+ throw new RangeError('Invalid redirect status code');
363
+ }
364
+
365
+ // Validate URL — resolve relative URLs against the current base if available
366
+ let finalUrl = String(url);
367
+ if (finalUrl.startsWith('://')) {
368
+ try {
369
+ const base = typeof globalThis.location !== 'undefined' ? globalThis.location.href : undefined;
370
+ if (base) {
371
+ const schemeMatch = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.exec(String(base));
372
+ if (schemeMatch) {
373
+ finalUrl = schemeMatch[0] + finalUrl.slice(1);
374
+ }
375
+ }
376
+ } catch {
377
+ // Keep the protocol-relative Bun compat form when no usable base exists.
378
+ }
379
+ }
380
+ if (!(bunCompat && finalUrl.startsWith('://')) && typeof URL !== 'undefined') {
381
+ try {
382
+ const base = typeof globalThis.location !== 'undefined' ? globalThis.location.href : undefined;
383
+ const parsedUrl = new URL(finalUrl, base);
384
+ finalUrl = parsedUrl.href;
385
+ } catch {
386
+ if (!bunCompat) {
387
+ throw new TypeError('Invalid URL');
388
+ }
389
+ }
390
+ }
391
+
392
+ return new Response(null, {
393
+ status: normalizedStatus,
394
+ statusText: '',
395
+ headers: {
396
+ Location: finalUrl,
397
+ },
398
+ });
399
+ }
400
+
401
+ /**
402
+ * Creates a new Response with a JSON body.
403
+ */
404
+ static json(data: unknown, init?: ResponseInit): Response {
405
+ // Per spec: JSON.stringify errors propagate directly (not wrapped in TypeError)
406
+ const body = JSON.stringify(data);
407
+ if (body === undefined) {
408
+ throw new TypeError(
409
+ isBunCompatResponseTest()
410
+ ? 'Value is not JSON serializable'
411
+ : "Failed to execute 'json' on 'Response': The data is not JSON serializable"
412
+ );
413
+ }
414
+
415
+ // Per spec: null-body status with body throws TypeError
416
+ const status = init?.status ?? 200;
417
+ if (NULL_BODY_STATUS_CODES.includes(status)) {
418
+ throw new TypeError('Response with null body status cannot have body');
419
+ }
420
+
421
+ const response = new Response(body, init);
422
+
423
+ // Per spec: Response.json() sets Content-Type to application/json
424
+ // but user-provided Content-Type in init headers takes precedence.
425
+ // Check if the user explicitly provided a content-type header.
426
+ const userHeaders = init?.headers;
427
+ let userProvidedContentType = false;
428
+ if (userHeaders) {
429
+ if (userHeaders instanceof Headers) {
430
+ userProvidedContentType = userHeaders.has('content-type');
431
+ } else if (Array.isArray(userHeaders)) {
432
+ userProvidedContentType = userHeaders.some(
433
+ (h) => Array.isArray(h) && h[0].toLowerCase() === 'content-type'
434
+ );
435
+ } else if (typeof userHeaders === 'object') {
436
+ userProvidedContentType = Object.keys(userHeaders as Record<string, string>).some(
437
+ (k) => k.toLowerCase() === 'content-type'
438
+ );
439
+ }
440
+ }
441
+ if (!userProvidedContentType) {
442
+ response._headers.set(
443
+ 'content-type',
444
+ isBunCompatResponseTest() ? 'application/json;charset=utf-8' : 'application/json'
445
+ );
446
+ }
447
+
448
+ return response;
449
+ }
450
+
451
+ /**
452
+ * Detect if a redirect occurred by comparing the request URL to the response URL.
453
+ * The native bridge may not always set `redirected: true`, so we also check
454
+ * whether the response URL differs from the original request URL.
455
+ */
456
+ private static _detectRedirected(
457
+ nativeRedirected: boolean,
458
+ responseUrl: string,
459
+ requestUrl?: string
460
+ ): boolean {
461
+ if (nativeRedirected) return true;
462
+ if (!requestUrl || !responseUrl) return false;
463
+ // Compare URLs: if they differ, the request was redirected
464
+ return responseUrl !== requestUrl;
465
+ }
466
+
467
+ /**
468
+ * Create a Response from native response data.
469
+ * @param nativeResponse The native response data
470
+ * @param requestUrl The original request URL, used to detect redirects by URL comparison
471
+ */
472
+ static fromNative(nativeResponse: NativeResponse, requestUrl?: string): Response {
473
+ // Bypass the constructor's status validation (native responses can have any status)
474
+ const status = Number(nativeResponse.status);
475
+ const response = new Response(null);
476
+ response._status = status;
477
+ // Use provided statusText, or fall back to default for the status code.
478
+ // NSURLSession on macOS and HTTP/2 don't provide reason phrases.
479
+ response._statusText = nativeResponse.statusText || DEFAULT_STATUS_TEXT[status] || '';
480
+ response._headers = Headers.fromTupleArray(nativeResponse.headers);
481
+ response._headers._guard = 'response';
482
+ response._url = nativeResponse.url;
483
+ response._redirected = Response._detectRedirected(
484
+ nativeResponse.redirected,
485
+ nativeResponse.url,
486
+ requestUrl
487
+ );
488
+ response._ok = status >= 200 && status < 300;
489
+ response._type = 'basic';
490
+
491
+ // Set body
492
+ if (!NULL_BODY_STATUS_CODES.includes(response._status)) {
493
+ if (nativeResponse.body) {
494
+ const bytes = new Uint8Array(nativeResponse.body);
495
+ response._bodyBuffer = nativeResponse.body;
496
+ response._body = createReadableStreamFromUint8Array(bytes);
497
+ } else if (isBunCompatResponseTest()) {
498
+ const bytes = new Uint8Array(0);
499
+ response._bodyBuffer = bytes.buffer as ArrayBuffer;
500
+ response._body = createReadableStreamFromUint8Array(bytes);
501
+ }
502
+ }
503
+
504
+ return response;
505
+ }
506
+
507
+ /**
508
+ * Create a Response from native streaming response data with a body stream.
509
+ * @param nativeResponse The native streaming response data
510
+ * @param bodyStream The ReadableStream for the response body
511
+ * @param requestUrl The original request URL, used to detect redirects by URL comparison
512
+ */
513
+ static fromNativeStreaming(
514
+ nativeResponse: NativeStreamingResponse,
515
+ bodyStream: ReadableStream<Uint8Array>,
516
+ requestUrl?: string
517
+ ): Response {
518
+ // Bypass the constructor's status validation (native responses can have any status)
519
+ const status = Number(nativeResponse.status);
520
+ const response = new Response(null);
521
+ response._status = status;
522
+ // Use provided statusText, or fall back to default for the status code.
523
+ response._statusText = nativeResponse.statusText || DEFAULT_STATUS_TEXT[status] || '';
524
+ response._headers = Headers.fromTupleArray(nativeResponse.headers);
525
+ response._headers._guard = 'immutable';
526
+ if (!NULL_BODY_STATUS_CODES.includes(status)) {
527
+ response._body = bodyStream;
528
+ }
529
+ response._url = nativeResponse.url;
530
+ response._redirected = Response._detectRedirected(
531
+ nativeResponse.redirected,
532
+ nativeResponse.url,
533
+ requestUrl
534
+ );
535
+ response._ok = status >= 200 && status < 300;
536
+ response._type = 'basic';
537
+
538
+ return response;
539
+ }
540
+ }
541
+
542
+ // Ensure constructor.name survives minification
543
+ Object.defineProperty(Response, 'name', { value: 'Response', configurable: true });