web.url-search-params.constructor.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. 'use strict';
  2. // TODO: in core-js@4, move /modules/ dependencies to public entries for better optimization by tools like `preset-env`
  3. require('../modules/es.array.iterator');
  4. require('../modules/es.string.from-code-point');
  5. var $ = require('../internals/export');
  6. var globalThis = require('../internals/global-this');
  7. var safeGetBuiltIn = require('../internals/safe-get-built-in');
  8. var getBuiltIn = require('../internals/get-built-in');
  9. var call = require('../internals/function-call');
  10. var uncurryThis = require('../internals/function-uncurry-this');
  11. var DESCRIPTORS = require('../internals/descriptors');
  12. var USE_NATIVE_URL = require('../internals/url-constructor-detection');
  13. var defineBuiltIn = require('../internals/define-built-in');
  14. var defineBuiltInAccessor = require('../internals/define-built-in-accessor');
  15. var defineBuiltIns = require('../internals/define-built-ins');
  16. var setToStringTag = require('../internals/set-to-string-tag');
  17. var createIteratorConstructor = require('../internals/iterator-create-constructor');
  18. var InternalStateModule = require('../internals/internal-state');
  19. var anInstance = require('../internals/an-instance');
  20. var isCallable = require('../internals/is-callable');
  21. var hasOwn = require('../internals/has-own-property');
  22. var bind = require('../internals/function-bind-context');
  23. var classof = require('../internals/classof');
  24. var anObject = require('../internals/an-object');
  25. var isObject = require('../internals/is-object');
  26. var $toString = require('../internals/to-string');
  27. var create = require('../internals/object-create');
  28. var createPropertyDescriptor = require('../internals/create-property-descriptor');
  29. var getIterator = require('../internals/get-iterator');
  30. var getIteratorMethod = require('../internals/get-iterator-method');
  31. var createIterResultObject = require('../internals/create-iter-result-object');
  32. var validateArgumentsLength = require('../internals/validate-arguments-length');
  33. var wellKnownSymbol = require('../internals/well-known-symbol');
  34. var arraySort = require('../internals/array-sort');
  35. var ITERATOR = wellKnownSymbol('iterator');
  36. var URL_SEARCH_PARAMS = 'URLSearchParams';
  37. var URL_SEARCH_PARAMS_ITERATOR = URL_SEARCH_PARAMS + 'Iterator';
  38. var setInternalState = InternalStateModule.set;
  39. var getInternalParamsState = InternalStateModule.getterFor(URL_SEARCH_PARAMS);
  40. var getInternalIteratorState = InternalStateModule.getterFor(URL_SEARCH_PARAMS_ITERATOR);
  41. var nativeFetch = safeGetBuiltIn('fetch');
  42. var NativeRequest = safeGetBuiltIn('Request');
  43. var Headers = safeGetBuiltIn('Headers');
  44. var RequestPrototype = NativeRequest && NativeRequest.prototype;
  45. var HeadersPrototype = Headers && Headers.prototype;
  46. var TypeError = globalThis.TypeError;
  47. var encodeURIComponent = globalThis.encodeURIComponent;
  48. var fromCharCode = String.fromCharCode;
  49. var fromCodePoint = getBuiltIn('String', 'fromCodePoint');
  50. var $parseInt = parseInt;
  51. var charAt = uncurryThis(''.charAt);
  52. var join = uncurryThis([].join);
  53. var push = uncurryThis([].push);
  54. var replace = uncurryThis(''.replace);
  55. var shift = uncurryThis([].shift);
  56. var splice = uncurryThis([].splice);
  57. var split = uncurryThis(''.split);
  58. var stringSlice = uncurryThis(''.slice);
  59. var exec = uncurryThis(/./.exec);
  60. var plus = /\+/g;
  61. var FALLBACK_REPLACER = '\uFFFD';
  62. var VALID_HEX = /^[0-9a-f]+$/i;
  63. var parseHexOctet = function (string, start) {
  64. var substr = stringSlice(string, start, start + 2);
  65. if (!exec(VALID_HEX, substr)) return NaN;
  66. return $parseInt(substr, 16);
  67. };
  68. var getLeadingOnes = function (octet) {
  69. var count = 0;
  70. for (var mask = 0x80; mask > 0 && (octet & mask) !== 0; mask >>= 1) {
  71. count++;
  72. }
  73. return count;
  74. };
  75. var utf8Decode = function (octets) {
  76. var codePoint = null;
  77. switch (octets.length) {
  78. case 1:
  79. codePoint = octets[0];
  80. break;
  81. case 2:
  82. codePoint = (octets[0] & 0x1F) << 6 | (octets[1] & 0x3F);
  83. break;
  84. case 3:
  85. codePoint = (octets[0] & 0x0F) << 12 | (octets[1] & 0x3F) << 6 | (octets[2] & 0x3F);
  86. break;
  87. case 4:
  88. codePoint = (octets[0] & 0x07) << 18 | (octets[1] & 0x3F) << 12 | (octets[2] & 0x3F) << 6 | (octets[3] & 0x3F);
  89. break;
  90. }
  91. return codePoint > 0x10FFFF ? null : codePoint;
  92. };
  93. var decode = function (input) {
  94. input = replace(input, plus, ' ');
  95. var length = input.length;
  96. var result = '';
  97. var i = 0;
  98. while (i < length) {
  99. var decodedChar = charAt(input, i);
  100. if (decodedChar === '%') {
  101. if (charAt(input, i + 1) === '%' || i + 3 > length) {
  102. result += '%';
  103. i++;
  104. continue;
  105. }
  106. var octet = parseHexOctet(input, i + 1);
  107. // eslint-disable-next-line no-self-compare -- NaN check
  108. if (octet !== octet) {
  109. result += decodedChar;
  110. i++;
  111. continue;
  112. }
  113. i += 2;
  114. var byteSequenceLength = getLeadingOnes(octet);
  115. if (byteSequenceLength === 0) {
  116. decodedChar = fromCharCode(octet);
  117. } else {
  118. if (byteSequenceLength === 1 || byteSequenceLength > 4) {
  119. result += FALLBACK_REPLACER;
  120. i++;
  121. continue;
  122. }
  123. var octets = [octet];
  124. var sequenceIndex = 1;
  125. while (sequenceIndex < byteSequenceLength) {
  126. i++;
  127. if (i + 3 > length || charAt(input, i) !== '%') break;
  128. var nextByte = parseHexOctet(input, i + 1);
  129. // eslint-disable-next-line no-self-compare -- NaN check
  130. if (nextByte !== nextByte) {
  131. i += 3;
  132. break;
  133. }
  134. if (nextByte > 191 || nextByte < 128) break;
  135. push(octets, nextByte);
  136. i += 2;
  137. sequenceIndex++;
  138. }
  139. if (octets.length !== byteSequenceLength) {
  140. result += FALLBACK_REPLACER;
  141. continue;
  142. }
  143. var codePoint = utf8Decode(octets);
  144. if (codePoint === null) {
  145. result += FALLBACK_REPLACER;
  146. } else {
  147. decodedChar = fromCodePoint(codePoint);
  148. }
  149. }
  150. }
  151. result += decodedChar;
  152. i++;
  153. }
  154. return result;
  155. };
  156. var find = /[!'()~]|%20/g;
  157. var replacements = {
  158. '!': '%21',
  159. "'": '%27',
  160. '(': '%28',
  161. ')': '%29',
  162. '~': '%7E',
  163. '%20': '+'
  164. };
  165. var replacer = function (match) {
  166. return replacements[match];
  167. };
  168. var serialize = function (it) {
  169. return replace(encodeURIComponent(it), find, replacer);
  170. };
  171. var URLSearchParamsIterator = createIteratorConstructor(function Iterator(params, kind) {
  172. setInternalState(this, {
  173. type: URL_SEARCH_PARAMS_ITERATOR,
  174. target: getInternalParamsState(params).entries,
  175. index: 0,
  176. kind: kind
  177. });
  178. }, URL_SEARCH_PARAMS, function next() {
  179. var state = getInternalIteratorState(this);
  180. var target = state.target;
  181. var index = state.index++;
  182. if (!target || index >= target.length) {
  183. state.target = null;
  184. return createIterResultObject(undefined, true);
  185. }
  186. var entry = target[index];
  187. switch (state.kind) {
  188. case 'keys': return createIterResultObject(entry.key, false);
  189. case 'values': return createIterResultObject(entry.value, false);
  190. } return createIterResultObject([entry.key, entry.value], false);
  191. }, true);
  192. var URLSearchParamsState = function (init) {
  193. this.entries = [];
  194. this.url = null;
  195. if (init !== undefined) {
  196. if (isObject(init)) this.parseObject(init);
  197. else this.parseQuery(typeof init == 'string' ? charAt(init, 0) === '?' ? stringSlice(init, 1) : init : $toString(init));
  198. }
  199. };
  200. URLSearchParamsState.prototype = {
  201. type: URL_SEARCH_PARAMS,
  202. bindURL: function (url) {
  203. this.url = url;
  204. this.update();
  205. },
  206. parseObject: function (object) {
  207. var entries = this.entries;
  208. var iteratorMethod = getIteratorMethod(object);
  209. var iterator, next, step, entryIterator, entryNext, first, second;
  210. if (iteratorMethod) {
  211. iterator = getIterator(object, iteratorMethod);
  212. next = iterator.next;
  213. while (!(step = call(next, iterator)).done) {
  214. entryIterator = getIterator(anObject(step.value));
  215. entryNext = entryIterator.next;
  216. if (
  217. (first = call(entryNext, entryIterator)).done ||
  218. (second = call(entryNext, entryIterator)).done ||
  219. !call(entryNext, entryIterator).done
  220. ) throw new TypeError('Expected sequence with length 2');
  221. push(entries, { key: $toString(first.value), value: $toString(second.value) });
  222. }
  223. } else for (var key in object) if (hasOwn(object, key)) {
  224. push(entries, { key: key, value: $toString(object[key]) });
  225. }
  226. },
  227. parseQuery: function (query) {
  228. if (query) {
  229. var entries = this.entries;
  230. var attributes = split(query, '&');
  231. var index = 0;
  232. var attribute, entry;
  233. while (index < attributes.length) {
  234. attribute = attributes[index++];
  235. if (attribute.length) {
  236. entry = split(attribute, '=');
  237. push(entries, {
  238. key: decode(shift(entry)),
  239. value: decode(join(entry, '='))
  240. });
  241. }
  242. }
  243. }
  244. },
  245. serialize: function () {
  246. var entries = this.entries;
  247. var result = [];
  248. var index = 0;
  249. var entry;
  250. while (index < entries.length) {
  251. entry = entries[index++];
  252. push(result, serialize(entry.key) + '=' + serialize(entry.value));
  253. } return join(result, '&');
  254. },
  255. update: function () {
  256. this.entries.length = 0;
  257. this.parseQuery(this.url.query);
  258. },
  259. updateURL: function () {
  260. if (this.url) this.url.update();
  261. }
  262. };
  263. // `URLSearchParams` constructor
  264. // https://url.spec.whatwg.org/#interface-urlsearchparams
  265. var URLSearchParamsConstructor = function URLSearchParams(/* init */) {
  266. anInstance(this, URLSearchParamsPrototype);
  267. var init = arguments.length > 0 ? arguments[0] : undefined;
  268. var state = setInternalState(this, new URLSearchParamsState(init));
  269. if (!DESCRIPTORS) this.size = state.entries.length;
  270. };
  271. var URLSearchParamsPrototype = URLSearchParamsConstructor.prototype;
  272. defineBuiltIns(URLSearchParamsPrototype, {
  273. // `URLSearchParams.prototype.append` method
  274. // https://url.spec.whatwg.org/#dom-urlsearchparams-append
  275. append: function append(name, value) {
  276. var state = getInternalParamsState(this);
  277. validateArgumentsLength(arguments.length, 2);
  278. push(state.entries, { key: $toString(name), value: $toString(value) });
  279. if (!DESCRIPTORS) this.length++;
  280. state.updateURL();
  281. },
  282. // `URLSearchParams.prototype.delete` method
  283. // https://url.spec.whatwg.org/#dom-urlsearchparams-delete
  284. 'delete': function (name /* , value */) {
  285. var state = getInternalParamsState(this);
  286. var length = validateArgumentsLength(arguments.length, 1);
  287. var entries = state.entries;
  288. var key = $toString(name);
  289. var $value = length < 2 ? undefined : arguments[1];
  290. var value = $value === undefined ? $value : $toString($value);
  291. var index = 0;
  292. while (index < entries.length) {
  293. var entry = entries[index];
  294. if (entry.key === key && (value === undefined || entry.value === value)) {
  295. splice(entries, index, 1);
  296. if (value !== undefined) break;
  297. } else index++;
  298. }
  299. if (!DESCRIPTORS) this.size = entries.length;
  300. state.updateURL();
  301. },
  302. // `URLSearchParams.prototype.get` method
  303. // https://url.spec.whatwg.org/#dom-urlsearchparams-get
  304. get: function get(name) {
  305. var entries = getInternalParamsState(this).entries;
  306. validateArgumentsLength(arguments.length, 1);
  307. var key = $toString(name);
  308. var index = 0;
  309. for (; index < entries.length; index++) {
  310. if (entries[index].key === key) return entries[index].value;
  311. }
  312. return null;
  313. },
  314. // `URLSearchParams.prototype.getAll` method
  315. // https://url.spec.whatwg.org/#dom-urlsearchparams-getall
  316. getAll: function getAll(name) {
  317. var entries = getInternalParamsState(this).entries;
  318. validateArgumentsLength(arguments.length, 1);
  319. var key = $toString(name);
  320. var result = [];
  321. var index = 0;
  322. for (; index < entries.length; index++) {
  323. if (entries[index].key === key) push(result, entries[index].value);
  324. }
  325. return result;
  326. },
  327. // `URLSearchParams.prototype.has` method
  328. // https://url.spec.whatwg.org/#dom-urlsearchparams-has
  329. has: function has(name /* , value */) {
  330. var entries = getInternalParamsState(this).entries;
  331. var length = validateArgumentsLength(arguments.length, 1);
  332. var key = $toString(name);
  333. var $value = length < 2 ? undefined : arguments[1];
  334. var value = $value === undefined ? $value : $toString($value);
  335. var index = 0;
  336. while (index < entries.length) {
  337. var entry = entries[index++];
  338. if (entry.key === key && (value === undefined || entry.value === value)) return true;
  339. }
  340. return false;
  341. },
  342. // `URLSearchParams.prototype.set` method
  343. // https://url.spec.whatwg.org/#dom-urlsearchparams-set
  344. set: function set(name, value) {
  345. var state = getInternalParamsState(this);
  346. validateArgumentsLength(arguments.length, 1);
  347. var entries = state.entries;
  348. var found = false;
  349. var key = $toString(name);
  350. var val = $toString(value);
  351. var index = 0;
  352. var entry;
  353. for (; index < entries.length; index++) {
  354. entry = entries[index];
  355. if (entry.key === key) {
  356. if (found) splice(entries, index--, 1);
  357. else {
  358. found = true;
  359. entry.value = val;
  360. }
  361. }
  362. }
  363. if (!found) push(entries, { key: key, value: val });
  364. if (!DESCRIPTORS) this.size = entries.length;
  365. state.updateURL();
  366. },
  367. // `URLSearchParams.prototype.sort` method
  368. // https://url.spec.whatwg.org/#dom-urlsearchparams-sort
  369. sort: function sort() {
  370. var state = getInternalParamsState(this);
  371. arraySort(state.entries, function (a, b) {
  372. return a.key > b.key ? 1 : -1;
  373. });
  374. state.updateURL();
  375. },
  376. // `URLSearchParams.prototype.forEach` method
  377. forEach: function forEach(callback /* , thisArg */) {
  378. var entries = getInternalParamsState(this).entries;
  379. var boundFunction = bind(callback, arguments.length > 1 ? arguments[1] : undefined);
  380. var index = 0;
  381. var entry;
  382. while (index < entries.length) {
  383. entry = entries[index++];
  384. boundFunction(entry.value, entry.key, this);
  385. }
  386. },
  387. // `URLSearchParams.prototype.keys` method
  388. keys: function keys() {
  389. return new URLSearchParamsIterator(this, 'keys');
  390. },
  391. // `URLSearchParams.prototype.values` method
  392. values: function values() {
  393. return new URLSearchParamsIterator(this, 'values');
  394. },
  395. // `URLSearchParams.prototype.entries` method
  396. entries: function entries() {
  397. return new URLSearchParamsIterator(this, 'entries');
  398. }
  399. }, { enumerable: true });
  400. // `URLSearchParams.prototype[@@iterator]` method
  401. defineBuiltIn(URLSearchParamsPrototype, ITERATOR, URLSearchParamsPrototype.entries, { name: 'entries' });
  402. // `URLSearchParams.prototype.toString` method
  403. // https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
  404. defineBuiltIn(URLSearchParamsPrototype, 'toString', function toString() {
  405. return getInternalParamsState(this).serialize();
  406. }, { enumerable: true });
  407. // `URLSearchParams.prototype.size` getter
  408. // https://github.com/whatwg/url/pull/734
  409. if (DESCRIPTORS) defineBuiltInAccessor(URLSearchParamsPrototype, 'size', {
  410. get: function size() {
  411. return getInternalParamsState(this).entries.length;
  412. },
  413. configurable: true,
  414. enumerable: true
  415. });
  416. setToStringTag(URLSearchParamsConstructor, URL_SEARCH_PARAMS);
  417. $({ global: true, constructor: true, forced: !USE_NATIVE_URL }, {
  418. URLSearchParams: URLSearchParamsConstructor
  419. });
  420. // Wrap `fetch` and `Request` for correct work with polyfilled `URLSearchParams`
  421. if (!USE_NATIVE_URL && isCallable(Headers)) {
  422. var headersHas = uncurryThis(HeadersPrototype.has);
  423. var headersSet = uncurryThis(HeadersPrototype.set);
  424. var wrapRequestOptions = function (init) {
  425. if (isObject(init)) {
  426. var body = init.body;
  427. var headers;
  428. if (classof(body) === URL_SEARCH_PARAMS) {
  429. headers = init.headers ? new Headers(init.headers) : new Headers();
  430. if (!headersHas(headers, 'content-type')) {
  431. headersSet(headers, 'content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
  432. }
  433. return create(init, {
  434. body: createPropertyDescriptor(0, $toString(body)),
  435. headers: createPropertyDescriptor(0, headers)
  436. });
  437. }
  438. } return init;
  439. };
  440. if (isCallable(nativeFetch)) {
  441. $({ global: true, enumerable: true, dontCallGetSet: true, forced: true }, {
  442. fetch: function fetch(input /* , init */) {
  443. return nativeFetch(input, arguments.length > 1 ? wrapRequestOptions(arguments[1]) : {});
  444. }
  445. });
  446. }
  447. if (isCallable(NativeRequest)) {
  448. var RequestConstructor = function Request(input /* , init */) {
  449. anInstance(this, RequestPrototype);
  450. return new NativeRequest(input, arguments.length > 1 ? wrapRequestOptions(arguments[1]) : {});
  451. };
  452. RequestPrototype.constructor = RequestConstructor;
  453. RequestConstructor.prototype = RequestPrototype;
  454. $({ global: true, constructor: true, dontCallGetSet: true, forced: true }, {
  455. Request: RequestConstructor
  456. });
  457. }
  458. }
  459. module.exports = {
  460. URLSearchParams: URLSearchParamsConstructor,
  461. getState: getInternalParamsState
  462. };