mirror of
				https://github.com/lightpanda-io/browser.git
				synced 2025-10-30 15:41:48 +00:00 
			
		
		
		
	remove polyfill and add req/resp
This commit is contained in:
		| @@ -36,6 +36,8 @@ const WebApis = struct { | ||||
|         @import("xhr/form_data.zig").Interfaces, | ||||
|         @import("xhr/File.zig"), | ||||
|         @import("xmlserializer/xmlserializer.zig").Interfaces, | ||||
|         @import("fetch/Request.zig"), | ||||
|         @import("fetch/Headers.zig"), | ||||
|     }); | ||||
| }; | ||||
|  | ||||
|   | ||||
							
								
								
									
										88
									
								
								src/browser/fetch/Headers.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/browser/fetch/Headers.zig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | ||||
| // Copyright (C) 2023-2024  Lightpanda (Selecy SAS) | ||||
| // | ||||
| // Francis Bouvier <francis@lightpanda.io> | ||||
| // Pierre Tachoire <pierre@lightpanda.io> | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU Affero General Public License as | ||||
| // published by the Free Software Foundation, either version 3 of the | ||||
| // License, or (at your option) any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| // GNU Affero General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU Affero General Public License | ||||
| // along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  | ||||
| const std = @import("std"); | ||||
| const URL = @import("../../url.zig").URL; | ||||
| const Page = @import("../page.zig").Page; | ||||
|  | ||||
| // https://developer.mozilla.org/en-US/docs/Web/API/Headers | ||||
| const Headers = @This(); | ||||
|  | ||||
| headers: std.StringHashMapUnmanaged([]const u8), | ||||
|  | ||||
| // They can either be: | ||||
| // | ||||
| // 1. An array of string pairs. | ||||
| // 2. An object with string keys to string values. | ||||
| // 3. Another Headers object. | ||||
| const HeadersInit = union(enum) { | ||||
|     strings: []const []const u8, | ||||
|     // headers: Headers, | ||||
| }; | ||||
|  | ||||
| pub fn constructor(_init: ?[]const HeadersInit, page: *Page) !Headers { | ||||
|     const arena = page.arena; | ||||
|     var headers = std.StringHashMapUnmanaged([]const u8).empty; | ||||
|  | ||||
|     if (_init) |init| { | ||||
|         for (init) |item| { | ||||
|             switch (item) { | ||||
|                 .strings => |pair| { | ||||
|                     // Can only have two string elements if in a pair. | ||||
|                     if (pair.len != 2) { | ||||
|                         return error.TypeError; | ||||
|                     } | ||||
|  | ||||
|                     const raw_key = pair[0]; | ||||
|                     const value = pair[1]; | ||||
|                     const key = try std.ascii.allocLowerString(arena, raw_key); | ||||
|  | ||||
|                     try headers.put(arena, key, value); | ||||
|                 }, | ||||
|                 // .headers => |_| {}, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return .{ | ||||
|         .headers = headers, | ||||
|     }; | ||||
| } | ||||
|  | ||||
| pub fn _get(self: *const Headers, header: []const u8, page: *Page) !?[]const u8 { | ||||
|     const arena = page.arena; | ||||
|     const key = try std.ascii.allocLowerString(arena, header); | ||||
|  | ||||
|     const value = (self.headers.getEntry(key) orelse return null).value_ptr.*; | ||||
|     return try arena.dupe(u8, value); | ||||
| } | ||||
|  | ||||
| const testing = @import("../../testing.zig"); | ||||
| test "fetch: headers" { | ||||
|     var runner = try testing.jsRunner(testing.tracking_allocator, .{ .url = "https://lightpanda.io" }); | ||||
|     defer runner.deinit(); | ||||
|  | ||||
|     try runner.testCases(&.{ | ||||
|         .{ "let empty_headers = new Headers()", "undefined" }, | ||||
|     }, .{}); | ||||
|  | ||||
|     try runner.testCases(&.{ | ||||
|         .{ "let headers = new Headers([['Set-Cookie', 'name=world']])", "undefined" }, | ||||
|         .{ "headers.get('set-cookie')", "name=world" }, | ||||
|     }, .{}); | ||||
| } | ||||
							
								
								
									
										68
									
								
								src/browser/fetch/Request.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/browser/fetch/Request.zig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| // Copyright (C) 2023-2024  Lightpanda (Selecy SAS) | ||||
| // | ||||
| // Francis Bouvier <francis@lightpanda.io> | ||||
| // Pierre Tachoire <pierre@lightpanda.io> | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU Affero General Public License as | ||||
| // published by the Free Software Foundation, either version 3 of the | ||||
| // License, or (at your option) any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| // GNU Affero General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU Affero General Public License | ||||
| // along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  | ||||
| const std = @import("std"); | ||||
| const URL = @import("../../url.zig").URL; | ||||
| const Page = @import("../page.zig").Page; | ||||
|  | ||||
| // https://developer.mozilla.org/en-US/docs/Web/API/Request/Request | ||||
| const Request = @This(); | ||||
|  | ||||
| url: []const u8, | ||||
|  | ||||
| const RequestInput = union(enum) { | ||||
|     string: []const u8, | ||||
|     request: Request, | ||||
| }; | ||||
|  | ||||
| pub fn constructor(input: RequestInput, page: *Page) !Request { | ||||
|     const arena = page.arena; | ||||
|  | ||||
|     const url = blk: switch (input) { | ||||
|         .string => |str| { | ||||
|             break :blk try URL.stitch(arena, str, page.url.raw, .{}); | ||||
|         }, | ||||
|         .request => |req| { | ||||
|             break :blk try arena.dupe(u8, req.url); | ||||
|         }, | ||||
|     }; | ||||
|  | ||||
|     return .{ | ||||
|         .url = url, | ||||
|     }; | ||||
| } | ||||
|  | ||||
| pub fn get_url(self: *const Request, page: *Page) ![]const u8 { | ||||
|     return try page.arena.dupe(u8, self.url); | ||||
| } | ||||
|  | ||||
| const testing = @import("../../testing.zig"); | ||||
| test "fetch: request" { | ||||
|     var runner = try testing.jsRunner(testing.tracking_allocator, .{ .url = "https://lightpanda.io" }); | ||||
|     defer runner.deinit(); | ||||
|  | ||||
|     try runner.testCases(&.{ | ||||
|         .{ "let request = new Request('flower.png')", "undefined" }, | ||||
|         .{ "request.url", "https://lightpanda.io/flower.png" }, | ||||
|     }, .{}); | ||||
|  | ||||
|     try runner.testCases(&.{ | ||||
|         .{ "let request2 = new Request('https://google.com')", "undefined" }, | ||||
|         .{ "request2.url", "https://google.com" }, | ||||
|     }, .{}); | ||||
| } | ||||
							
								
								
									
										0
									
								
								src/browser/fetch/Response.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/browser/fetch/Response.zig
									
									
									
									
									
										Normal file
									
								
							| @@ -1,671 +0,0 @@ | ||||
| // fetch.js code comes from | ||||
| // https://github.com/JakeChampion/fetch/blob/main/fetch.js | ||||
| // | ||||
| // The original code source is available in MIT license. | ||||
| // | ||||
| // The script comes from the built version from npm. | ||||
| // You can get the package with the command: | ||||
| // | ||||
| // wget $(npm view whatwg-fetch dist.tarball) | ||||
| // | ||||
| // The source is the content of `package/dist/fetch.umd.js` file. | ||||
| (function (global, factory) { | ||||
|   typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||||
|   typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||||
|   (factory((global.WHATWGFetch = {}))); | ||||
| }(this, (function (exports) { 'use strict'; | ||||
|  | ||||
|   /* eslint-disable no-prototype-builtins */ | ||||
|   var g = | ||||
|     (typeof globalThis !== 'undefined' && globalThis) || | ||||
|     (typeof self !== 'undefined' && self) || | ||||
|     // eslint-disable-next-line no-undef | ||||
|     (typeof global !== 'undefined' && global) || | ||||
|     {}; | ||||
|  | ||||
|   var support = { | ||||
|     searchParams: 'URLSearchParams' in g, | ||||
|     iterable: 'Symbol' in g && 'iterator' in Symbol, | ||||
|     blob: | ||||
|       'FileReader' in g && | ||||
|       'Blob' in g && | ||||
|       (function() { | ||||
|         try { | ||||
|           new Blob(); | ||||
|           return true | ||||
|         } catch (e) { | ||||
|           return false | ||||
|         } | ||||
|       })(), | ||||
|     formData: 'FormData' in g, | ||||
|  | ||||
|     // Arraybuffer is available but xhr doesn't implement it for now. | ||||
|     // arrayBuffer: 'ArrayBuffer' in g | ||||
|     arrayBuffer: false | ||||
|   }; | ||||
|  | ||||
|   function isDataView(obj) { | ||||
|     return obj && DataView.prototype.isPrototypeOf(obj) | ||||
|   } | ||||
|  | ||||
|   if (support.arrayBuffer) { | ||||
|     var viewClasses = [ | ||||
|       '[object Int8Array]', | ||||
|       '[object Uint8Array]', | ||||
|       '[object Uint8ClampedArray]', | ||||
|       '[object Int16Array]', | ||||
|       '[object Uint16Array]', | ||||
|       '[object Int32Array]', | ||||
|       '[object Uint32Array]', | ||||
|       '[object Float32Array]', | ||||
|       '[object Float64Array]' | ||||
|     ]; | ||||
|  | ||||
|     var isArrayBufferView = | ||||
|       ArrayBuffer.isView || | ||||
|       function(obj) { | ||||
|         return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1 | ||||
|       }; | ||||
|   } | ||||
|  | ||||
|   function normalizeName(name) { | ||||
|     if (typeof name !== 'string') { | ||||
|       name = String(name); | ||||
|     } | ||||
|     if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === '') { | ||||
|       throw new TypeError('Invalid character in header field name: "' + name + '"') | ||||
|     } | ||||
|     return name.toLowerCase() | ||||
|   } | ||||
|  | ||||
|   function normalizeValue(value) { | ||||
|     if (typeof value !== 'string') { | ||||
|       value = String(value); | ||||
|     } | ||||
|     return value | ||||
|   } | ||||
|  | ||||
|   // Build a destructive iterator for the value list | ||||
|   function iteratorFor(items) { | ||||
|     var iterator = { | ||||
|       next: function() { | ||||
|         var value = items.shift(); | ||||
|         return {done: value === undefined, value: value} | ||||
|       } | ||||
|     }; | ||||
|  | ||||
|     if (support.iterable) { | ||||
|       iterator[Symbol.iterator] = function() { | ||||
|         return iterator | ||||
|       }; | ||||
|     } | ||||
|  | ||||
|     return iterator | ||||
|   } | ||||
|  | ||||
|   function Headers(headers) { | ||||
|     this.map = {}; | ||||
|  | ||||
|     if (headers instanceof Headers) { | ||||
|       headers.forEach(function(value, name) { | ||||
|         this.append(name, value); | ||||
|       }, this); | ||||
|     } else if (Array.isArray(headers)) { | ||||
|       headers.forEach(function(header) { | ||||
|         if (header.length != 2) { | ||||
|           throw new TypeError('Headers constructor: expected name/value pair to be length 2, found' + header.length) | ||||
|         } | ||||
|         this.append(header[0], header[1]); | ||||
|       }, this); | ||||
|     } else if (headers) { | ||||
|       Object.getOwnPropertyNames(headers).forEach(function(name) { | ||||
|         this.append(name, headers[name]); | ||||
|       }, this); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Headers.prototype.append = function(name, value) { | ||||
|     name = normalizeName(name); | ||||
|     value = normalizeValue(value); | ||||
|     var oldValue = this.map[name]; | ||||
|     this.map[name] = oldValue ? oldValue + ', ' + value : value; | ||||
|   }; | ||||
|  | ||||
|   Headers.prototype['delete'] = function(name) { | ||||
|     delete this.map[normalizeName(name)]; | ||||
|   }; | ||||
|  | ||||
|   Headers.prototype.get = function(name) { | ||||
|     name = normalizeName(name); | ||||
|     return this.has(name) ? this.map[name] : null | ||||
|   }; | ||||
|  | ||||
|   Headers.prototype.has = function(name) { | ||||
|     return this.map.hasOwnProperty(normalizeName(name)) | ||||
|   }; | ||||
|  | ||||
|   Headers.prototype.set = function(name, value) { | ||||
|     this.map[normalizeName(name)] = normalizeValue(value); | ||||
|   }; | ||||
|  | ||||
|   Headers.prototype.forEach = function(callback, thisArg) { | ||||
|     for (var name in this.map) { | ||||
|       if (this.map.hasOwnProperty(name)) { | ||||
|         callback.call(thisArg, this.map[name], name, this); | ||||
|       } | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   Headers.prototype.keys = function() { | ||||
|     var items = []; | ||||
|     this.forEach(function(value, name) { | ||||
|       items.push(name); | ||||
|     }); | ||||
|     return iteratorFor(items) | ||||
|   }; | ||||
|  | ||||
|   Headers.prototype.values = function() { | ||||
|     var items = []; | ||||
|     this.forEach(function(value) { | ||||
|       items.push(value); | ||||
|     }); | ||||
|     return iteratorFor(items) | ||||
|   }; | ||||
|  | ||||
|   Headers.prototype.entries = function() { | ||||
|     var items = []; | ||||
|     this.forEach(function(value, name) { | ||||
|       items.push([name, value]); | ||||
|     }); | ||||
|     return iteratorFor(items) | ||||
|   }; | ||||
|  | ||||
|   if (support.iterable) { | ||||
|     Headers.prototype[Symbol.iterator] = Headers.prototype.entries; | ||||
|   } | ||||
|  | ||||
|   function consumed(body) { | ||||
|     if (body._noBody) return | ||||
|     if (body.bodyUsed) { | ||||
|       return Promise.reject(new TypeError('Already read')) | ||||
|     } | ||||
|     body.bodyUsed = true; | ||||
|   } | ||||
|  | ||||
|   function fileReaderReady(reader) { | ||||
|     return new Promise(function(resolve, reject) { | ||||
|       reader.onload = function() { | ||||
|         resolve(reader.result); | ||||
|       }; | ||||
|       reader.onerror = function() { | ||||
|         reject(reader.error); | ||||
|       }; | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   function readBlobAsArrayBuffer(blob) { | ||||
|     var reader = new FileReader(); | ||||
|     var promise = fileReaderReady(reader); | ||||
|     reader.readAsArrayBuffer(blob); | ||||
|     return promise | ||||
|   } | ||||
|  | ||||
|   function readBlobAsText(blob) { | ||||
|     var reader = new FileReader(); | ||||
|     var promise = fileReaderReady(reader); | ||||
|     var match = /charset=([A-Za-z0-9_-]+)/.exec(blob.type); | ||||
|     var encoding = match ? match[1] : 'utf-8'; | ||||
|     reader.readAsText(blob, encoding); | ||||
|     return promise | ||||
|   } | ||||
|  | ||||
|   function readArrayBufferAsText(buf) { | ||||
|     var view = new Uint8Array(buf); | ||||
|     var chars = new Array(view.length); | ||||
|  | ||||
|     for (var i = 0; i < view.length; i++) { | ||||
|       chars[i] = String.fromCharCode(view[i]); | ||||
|     } | ||||
|     return chars.join('') | ||||
|   } | ||||
|  | ||||
|   function bufferClone(buf) { | ||||
|     if (buf.slice) { | ||||
|       return buf.slice(0) | ||||
|     } else { | ||||
|       var view = new Uint8Array(buf.byteLength); | ||||
|       view.set(new Uint8Array(buf)); | ||||
|       return view.buffer | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   function Body() { | ||||
|     this.bodyUsed = false; | ||||
|  | ||||
|     this._initBody = function(body) { | ||||
|       /* | ||||
|         fetch-mock wraps the Response object in an ES6 Proxy to | ||||
|         provide useful test harness features such as flush. However, on | ||||
|         ES5 browsers without fetch or Proxy support pollyfills must be used; | ||||
|         the proxy-pollyfill is unable to proxy an attribute unless it exists | ||||
|         on the object before the Proxy is created. This change ensures | ||||
|         Response.bodyUsed exists on the instance, while maintaining the | ||||
|         semantic of setting Request.bodyUsed in the constructor before | ||||
|         _initBody is called. | ||||
|       */ | ||||
|       // eslint-disable-next-line no-self-assign | ||||
|       this.bodyUsed = this.bodyUsed; | ||||
|       this._bodyInit = body; | ||||
|       if (!body) { | ||||
|         this._noBody = true; | ||||
|         this._bodyText = ''; | ||||
|       } else if (typeof body === 'string') { | ||||
|         this._bodyText = body; | ||||
|       } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { | ||||
|         this._bodyBlob = body; | ||||
|       } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { | ||||
|         this._bodyFormData = body; | ||||
|       } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { | ||||
|         this._bodyText = body.toString(); | ||||
|       } else if (support.arrayBuffer && support.blob && isDataView(body)) { | ||||
|         this._bodyArrayBuffer = bufferClone(body.buffer); | ||||
|         // IE 10-11 can't handle a DataView body. | ||||
|         this._bodyInit = new Blob([this._bodyArrayBuffer]); | ||||
|       } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { | ||||
|         this._bodyArrayBuffer = bufferClone(body); | ||||
|       } else { | ||||
|         this._bodyText = body = Object.prototype.toString.call(body); | ||||
|       } | ||||
|  | ||||
|       if (!this.headers.get('content-type')) { | ||||
|         if (typeof body === 'string') { | ||||
|           this.headers.set('content-type', 'text/plain;charset=UTF-8'); | ||||
|         } else if (this._bodyBlob && this._bodyBlob.type) { | ||||
|           this.headers.set('content-type', this._bodyBlob.type); | ||||
|         } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { | ||||
|           this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8'); | ||||
|         } | ||||
|       } | ||||
|     }; | ||||
|  | ||||
|     if (support.blob) { | ||||
|       this.blob = function() { | ||||
|         var rejected = consumed(this); | ||||
|         if (rejected) { | ||||
|           return rejected | ||||
|         } | ||||
|  | ||||
|         if (this._bodyBlob) { | ||||
|           return Promise.resolve(this._bodyBlob) | ||||
|         } else if (this._bodyArrayBuffer) { | ||||
|           return Promise.resolve(new Blob([this._bodyArrayBuffer])) | ||||
|         } else if (this._bodyFormData) { | ||||
|           throw new Error('could not read FormData body as blob') | ||||
|         } else { | ||||
|           return Promise.resolve(new Blob([this._bodyText])) | ||||
|         } | ||||
|       }; | ||||
|     } | ||||
|  | ||||
|     this.arrayBuffer = function() { | ||||
|       if (this._bodyArrayBuffer) { | ||||
|         var isConsumed = consumed(this); | ||||
|         if (isConsumed) { | ||||
|           return isConsumed | ||||
|         } else if (ArrayBuffer.isView(this._bodyArrayBuffer)) { | ||||
|           return Promise.resolve( | ||||
|             this._bodyArrayBuffer.buffer.slice( | ||||
|               this._bodyArrayBuffer.byteOffset, | ||||
|               this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength | ||||
|             ) | ||||
|           ) | ||||
|         } else { | ||||
|           return Promise.resolve(this._bodyArrayBuffer) | ||||
|         } | ||||
|       } else if (support.blob) { | ||||
|         return this.blob().then(readBlobAsArrayBuffer) | ||||
|       } else { | ||||
|         throw new Error('could not read as ArrayBuffer') | ||||
|       } | ||||
|     }; | ||||
|  | ||||
|     this.text = function() { | ||||
|       var rejected = consumed(this); | ||||
|       if (rejected) { | ||||
|         return rejected | ||||
|       } | ||||
|  | ||||
|       if (this._bodyBlob) { | ||||
|         return readBlobAsText(this._bodyBlob) | ||||
|       } else if (this._bodyArrayBuffer) { | ||||
|         return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer)) | ||||
|       } else if (this._bodyFormData) { | ||||
|         throw new Error('could not read FormData body as text') | ||||
|       } else { | ||||
|         return Promise.resolve(this._bodyText) | ||||
|       } | ||||
|     }; | ||||
|  | ||||
|     if (support.formData) { | ||||
|       this.formData = function() { | ||||
|         return this.text().then(decode) | ||||
|       }; | ||||
|     } | ||||
|  | ||||
|     this.json = function() { | ||||
|       return this.text().then(JSON.parse) | ||||
|     }; | ||||
|  | ||||
|     return this | ||||
|   } | ||||
|  | ||||
|   // HTTP methods whose capitalization should be normalized | ||||
|   var methods = ['CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'TRACE']; | ||||
|  | ||||
|   function normalizeMethod(method) { | ||||
|     var upcased = method.toUpperCase(); | ||||
|     return methods.indexOf(upcased) > -1 ? upcased : method | ||||
|   } | ||||
|  | ||||
|   function Request(input, options) { | ||||
|     if (!(this instanceof Request)) { | ||||
|       throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.') | ||||
|     } | ||||
|  | ||||
|     options = options || {}; | ||||
|     var body = options.body; | ||||
|  | ||||
|     if (input instanceof Request) { | ||||
|       if (input.bodyUsed) { | ||||
|         throw new TypeError('Already read') | ||||
|       } | ||||
|       this.url = input.url; | ||||
|       this.credentials = input.credentials; | ||||
|       if (!options.headers) { | ||||
|         this.headers = new Headers(input.headers); | ||||
|       } | ||||
|       this.method = input.method; | ||||
|       this.mode = input.mode; | ||||
|       this.signal = input.signal; | ||||
|       if (!body && input._bodyInit != null) { | ||||
|         body = input._bodyInit; | ||||
|         input.bodyUsed = true; | ||||
|       } | ||||
|     } else { | ||||
|       this.url = String(input); | ||||
|     } | ||||
|  | ||||
|     this.credentials = options.credentials || this.credentials || 'same-origin'; | ||||
|     if (options.headers || !this.headers) { | ||||
|       this.headers = new Headers(options.headers); | ||||
|     } | ||||
|     this.method = normalizeMethod(options.method || this.method || 'GET'); | ||||
|     this.mode = options.mode || this.mode || null; | ||||
|     this.signal = options.signal || this.signal || (function () { | ||||
|       if ('AbortController' in g) { | ||||
|         var ctrl = new AbortController(); | ||||
|         return ctrl.signal; | ||||
|       } | ||||
|     }()); | ||||
|     this.referrer = null; | ||||
|  | ||||
|     if ((this.method === 'GET' || this.method === 'HEAD') && body) { | ||||
|       throw new TypeError('Body not allowed for GET or HEAD requests') | ||||
|     } | ||||
|     this._initBody(body); | ||||
|  | ||||
|     if (this.method === 'GET' || this.method === 'HEAD') { | ||||
|       if (options.cache === 'no-store' || options.cache === 'no-cache') { | ||||
|         // Search for a '_' parameter in the query string | ||||
|         var reParamSearch = /([?&])_=[^&]*/; | ||||
|         if (reParamSearch.test(this.url)) { | ||||
|           // If it already exists then set the value with the current time | ||||
|           this.url = this.url.replace(reParamSearch, '$1_=' + new Date().getTime()); | ||||
|         } else { | ||||
|           // Otherwise add a new '_' parameter to the end with the current time | ||||
|           var reQueryString = /\?/; | ||||
|           this.url += (reQueryString.test(this.url) ? '&' : '?') + '_=' + new Date().getTime(); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Request.prototype.clone = function() { | ||||
|     return new Request(this, {body: this._bodyInit}) | ||||
|   }; | ||||
|  | ||||
|   function decode(body) { | ||||
|     var form = new FormData(); | ||||
|     body | ||||
|       .trim() | ||||
|       .split('&') | ||||
|       .forEach(function(bytes) { | ||||
|         if (bytes) { | ||||
|           var split = bytes.split('='); | ||||
|           var name = split.shift().replace(/\+/g, ' '); | ||||
|           var value = split.join('=').replace(/\+/g, ' '); | ||||
|           form.append(decodeURIComponent(name), decodeURIComponent(value)); | ||||
|         } | ||||
|       }); | ||||
|     return form | ||||
|   } | ||||
|  | ||||
|   function parseHeaders(rawHeaders) { | ||||
|     var headers = new Headers(); | ||||
|     // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space | ||||
|     // https://tools.ietf.org/html/rfc7230#section-3.2 | ||||
|     var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' '); | ||||
|     // Avoiding split via regex to work around a common IE11 bug with the core-js 3.6.0 regex polyfill | ||||
|     // https://github.com/github/fetch/issues/748 | ||||
|     // https://github.com/zloirock/core-js/issues/751 | ||||
|     preProcessedHeaders | ||||
|       .split('\r') | ||||
|       .map(function(header) { | ||||
|         return header.indexOf('\n') === 0 ? header.substr(1, header.length) : header | ||||
|       }) | ||||
|       .forEach(function(line) { | ||||
|         var parts = line.split(':'); | ||||
|         var key = parts.shift().trim(); | ||||
|         if (key) { | ||||
|           var value = parts.join(':').trim(); | ||||
|           try { | ||||
|             headers.append(key, value); | ||||
|           } catch (error) { | ||||
|             console.warn('Response ' + error.message); | ||||
|           } | ||||
|         } | ||||
|       }); | ||||
|     return headers | ||||
|   } | ||||
|  | ||||
|   Body.call(Request.prototype); | ||||
|  | ||||
|   function Response(bodyInit, options) { | ||||
|     if (!(this instanceof Response)) { | ||||
|       throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.') | ||||
|     } | ||||
|     if (!options) { | ||||
|       options = {}; | ||||
|     } | ||||
|  | ||||
|     this.type = 'default'; | ||||
|     this.status = options.status === undefined ? 200 : options.status; | ||||
|     if (this.status < 200 || this.status > 599) { | ||||
|       throw new RangeError("Failed to construct 'Response': The status provided (0) is outside the range [200, 599].") | ||||
|     } | ||||
|     this.ok = this.status >= 200 && this.status < 300; | ||||
|     this.statusText = options.statusText === undefined ? '' : '' + options.statusText; | ||||
|     this.headers = new Headers(options.headers); | ||||
|     this.url = options.url || ''; | ||||
|     this._initBody(bodyInit); | ||||
|   } | ||||
|  | ||||
|   Body.call(Response.prototype); | ||||
|  | ||||
|   Response.prototype.clone = function() { | ||||
|     return new Response(this._bodyInit, { | ||||
|       status: this.status, | ||||
|       statusText: this.statusText, | ||||
|       headers: new Headers(this.headers), | ||||
|       url: this.url | ||||
|     }) | ||||
|   }; | ||||
|  | ||||
|   Response.error = function() { | ||||
|     var response = new Response(null, {status: 200, statusText: ''}); | ||||
|     response.ok = false; | ||||
|     response.status = 0; | ||||
|     response.type = 'error'; | ||||
|     return response | ||||
|   }; | ||||
|  | ||||
|   var redirectStatuses = [301, 302, 303, 307, 308]; | ||||
|  | ||||
|   Response.redirect = function(url, status) { | ||||
|     if (redirectStatuses.indexOf(status) === -1) { | ||||
|       throw new RangeError('Invalid status code') | ||||
|     } | ||||
|  | ||||
|     return new Response(null, {status: status, headers: {location: url}}) | ||||
|   }; | ||||
|  | ||||
|   exports.DOMException = g.DOMException; | ||||
|   try { | ||||
|     new exports.DOMException(); | ||||
|   } catch (err) { | ||||
|     exports.DOMException = function(message, name) { | ||||
|       this.message = message; | ||||
|       this.name = name; | ||||
|       var error = Error(message); | ||||
|       this.stack = error.stack; | ||||
|     }; | ||||
|     exports.DOMException.prototype = Object.create(Error.prototype); | ||||
|     exports.DOMException.prototype.constructor = exports.DOMException; | ||||
|   } | ||||
|  | ||||
|   function fetch(input, init) { | ||||
|     return new Promise(function(resolve, reject) { | ||||
|       var request = new Request(input, init); | ||||
|  | ||||
|       if (request.signal && request.signal.aborted) { | ||||
|         return reject(new exports.DOMException('Aborted', 'AbortError')) | ||||
|       } | ||||
|  | ||||
|       var xhr = new XMLHttpRequest(); | ||||
|  | ||||
|       function abortXhr() { | ||||
|         xhr.abort(); | ||||
|       } | ||||
|  | ||||
|       xhr.onload = function() { | ||||
|         var options = { | ||||
|           statusText: xhr.statusText, | ||||
|           headers: parseHeaders(xhr.getAllResponseHeaders() || '') | ||||
|         }; | ||||
|         // This check if specifically for when a user fetches a file locally from the file system | ||||
|         // Only if the status is out of a normal range | ||||
|         if (request.url.indexOf('file://') === 0 && (xhr.status < 200 || xhr.status > 599)) { | ||||
|           options.status = 200; | ||||
|         } else { | ||||
|           options.status = xhr.status; | ||||
|         } | ||||
|         options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL'); | ||||
|         var body = 'response' in xhr ? xhr.response : xhr.responseText; | ||||
|         setTimeout(function() { | ||||
|           resolve(new Response(body, options)); | ||||
|         }, 0); | ||||
|       }; | ||||
|  | ||||
|       xhr.onerror = function() { | ||||
|         setTimeout(function() { | ||||
|           reject(new TypeError('Network request failed')); | ||||
|         }, 0); | ||||
|       }; | ||||
|  | ||||
|       xhr.ontimeout = function() { | ||||
|         setTimeout(function() { | ||||
|           reject(new TypeError('Network request timed out')); | ||||
|         }, 0); | ||||
|       }; | ||||
|  | ||||
|       xhr.onabort = function() { | ||||
|         setTimeout(function() { | ||||
|           reject(new exports.DOMException('Aborted', 'AbortError')); | ||||
|         }, 0); | ||||
|       }; | ||||
|  | ||||
|       function fixUrl(url) { | ||||
|         try { | ||||
|           return url === '' && g.location.href ? g.location.href : url | ||||
|         } catch (e) { | ||||
|           return url | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       xhr.open(request.method, fixUrl(request.url), true); | ||||
|  | ||||
|       if (request.credentials === 'include') { | ||||
|         xhr.withCredentials = true; | ||||
|       } else if (request.credentials === 'omit') { | ||||
|         xhr.withCredentials = false; | ||||
|       } | ||||
|  | ||||
|       if ('responseType' in xhr) { | ||||
|         if (support.blob) { | ||||
|           xhr.responseType = 'blob'; | ||||
|         } else if ( | ||||
|           support.arrayBuffer | ||||
|         ) { | ||||
|           xhr.responseType = 'arraybuffer'; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       if (init && typeof init.headers === 'object' && !(init.headers instanceof Headers || (g.Headers && init.headers instanceof g.Headers))) { | ||||
|         var names = []; | ||||
|         Object.getOwnPropertyNames(init.headers).forEach(function(name) { | ||||
|           names.push(normalizeName(name)); | ||||
|           xhr.setRequestHeader(name, normalizeValue(init.headers[name])); | ||||
|         }); | ||||
|         request.headers.forEach(function(value, name) { | ||||
|           if (names.indexOf(name) === -1) { | ||||
|             xhr.setRequestHeader(name, value); | ||||
|           } | ||||
|         }); | ||||
|       } else { | ||||
|         request.headers.forEach(function(value, name) { | ||||
|           xhr.setRequestHeader(name, value); | ||||
|         }); | ||||
|       } | ||||
|  | ||||
|       if (request.signal) { | ||||
|         request.signal.addEventListener('abort', abortXhr); | ||||
|  | ||||
|         xhr.onreadystatechange = function() { | ||||
|           // DONE (success or failure) | ||||
|           if (xhr.readyState === 4) { | ||||
|             request.signal.removeEventListener('abort', abortXhr); | ||||
|           } | ||||
|         }; | ||||
|       } | ||||
|  | ||||
|       xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit); | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   fetch.polyfill = true; | ||||
|  | ||||
|   if (!g.fetch) { | ||||
|     g.fetch = fetch; | ||||
|     g.Headers = Headers; | ||||
|     g.Request = Request; | ||||
|     g.Response = Response; | ||||
|   } | ||||
|  | ||||
|   exports.Headers = Headers; | ||||
|   exports.Request = Request; | ||||
|   exports.Response = Response; | ||||
|   exports.fetch = fetch; | ||||
|  | ||||
|   Object.defineProperty(exports, '__esModule', { value: true }); | ||||
|  | ||||
| }))); | ||||
| @@ -1,31 +0,0 @@ | ||||
| // fetch.js code comes from | ||||
| // https://github.com/JakeChampion/fetch/blob/main/fetch.js | ||||
| // | ||||
| // The original code source is available in MIT license. | ||||
| // | ||||
| // The script comes from the built version from npm. | ||||
| // You can get the package with the command: | ||||
| // | ||||
| // wget $(npm view whatwg-fetch dist.tarball) | ||||
| // | ||||
| // The source is the content of `package/dist/fetch.umd.js` file. | ||||
| pub const source = @embedFile("fetch.js"); | ||||
|  | ||||
| const testing = @import("../../testing.zig"); | ||||
| test "Browser.fetch" { | ||||
|     var runner = try testing.jsRunner(testing.tracking_allocator, .{}); | ||||
|     defer runner.deinit(); | ||||
|  | ||||
|     try runner.testCases(&.{ | ||||
|         .{ | ||||
|             \\  var ok = false; | ||||
|             \\  const request = new Request("http://127.0.0.1:9582/loader"); | ||||
|             \\  fetch(request).then((response) => { ok = response.ok; }); | ||||
|             \\  false; | ||||
|             , | ||||
|             "false", | ||||
|         }, | ||||
|         // all events have been resolved. | ||||
|         .{ "ok", "true" }, | ||||
|     }, .{}); | ||||
| } | ||||
| @@ -27,7 +27,6 @@ pub const Loader = struct { | ||||
|     state: enum { empty, loading } = .empty, | ||||
|  | ||||
|     done: struct { | ||||
|         fetch: bool = false, | ||||
|         webcomponents: bool = false, | ||||
|     } = .{}, | ||||
|  | ||||
| @@ -56,18 +55,6 @@ pub const Loader = struct { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (!self.done.fetch and isFetch(name)) { | ||||
|             const source = @import("fetch.zig").source; | ||||
|             self.load("fetch", source, js_context); | ||||
|  | ||||
|             // We return false here: We want v8 to continue the calling chain | ||||
|             // to finally find the polyfill we just inserted. If we want to | ||||
|             // return false and stops the call chain, we have to use | ||||
|             // `info.GetReturnValue.Set()` function, or `undefined` will be | ||||
|             // returned immediately. | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (!self.done.webcomponents and isWebcomponents(name)) { | ||||
|             const source = @import("webcomponents.zig").source; | ||||
|             self.load("webcomponents", source, js_context); | ||||
| @@ -89,14 +76,6 @@ pub const Loader = struct { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     fn isFetch(name: []const u8) bool { | ||||
|         if (std.mem.eql(u8, name, "fetch")) return true; | ||||
|         if (std.mem.eql(u8, name, "Request")) return true; | ||||
|         if (std.mem.eql(u8, name, "Response")) return true; | ||||
|         if (std.mem.eql(u8, name, "Headers")) return true; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     fn isWebcomponents(name: []const u8) bool { | ||||
|         if (std.mem.eql(u8, name, "customElements")) return true; | ||||
|         return false; | ||||
|   | ||||
							
								
								
									
										0
									
								
								src/browser/streams/ReadableStream.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/browser/streams/ReadableStream.zig
									
									
									
									
									
										Normal file
									
								
							
		Reference in New Issue
	
	Block a user
	 Muki Kiboigo
					Muki Kiboigo