|
1 | 1 | 'use strict' |
2 | 2 |
|
3 | 3 | const debug = require('debug')('nock.common') |
4 | | -const isPlainObject = require('lodash/isPlainObject') |
5 | | -const set = require('lodash/set') |
6 | 4 | const timers = require('timers') |
7 | 5 | const url = require('url') |
8 | 6 | const util = require('util') |
@@ -568,17 +566,6 @@ const dataEqual = (expected, actual) => { |
568 | 566 | return deepEqual(expected, actual) |
569 | 567 | } |
570 | 568 |
|
571 | | -/** |
572 | | - * Converts flat objects whose keys use JSON path notation to nested objects. |
573 | | - * |
574 | | - * The input object is not mutated. |
575 | | - * |
576 | | - * @example |
577 | | - * { 'foo[bar][0]': 'baz' } -> { foo: { bar: [ 'baz' ] } } |
578 | | - */ |
579 | | -const expand = input => |
580 | | - Object.entries(input).reduce((acc, [k, v]) => set(acc, k, v), {}) |
581 | | - |
582 | 569 | /** |
583 | 570 | * Performs a recursive strict comparison between two values. |
584 | 571 | * |
@@ -665,10 +652,97 @@ function isRequestDestroyed(req) { |
665 | 652 | ) |
666 | 653 | } |
667 | 654 |
|
| 655 | +/** |
| 656 | + * Returns true if the given value is a plain object and not an Array. |
| 657 | + * @param {*} value |
| 658 | + * @returns {boolean} |
| 659 | + */ |
| 660 | +function isPlainObject(value) { |
| 661 | + if (typeof value !== 'object' || value === null) return false |
| 662 | + |
| 663 | + if (Object.prototype.toString.call(value) !== '[object Object]') return false |
| 664 | + |
| 665 | + const proto = Object.getPrototypeOf(value) |
| 666 | + if (proto === null) return true |
| 667 | + |
| 668 | + const Ctor = |
| 669 | + Object.prototype.hasOwnProperty.call(proto, 'constructor') && |
| 670 | + proto.constructor |
| 671 | + return ( |
| 672 | + typeof Ctor === 'function' && |
| 673 | + Ctor instanceof Ctor && |
| 674 | + Function.prototype.call(Ctor) === Function.prototype.call(value) |
| 675 | + ) |
| 676 | +} |
| 677 | + |
| 678 | +const prototypePollutionBlockList = ['__proto__', 'prototype', 'constructor'] |
| 679 | +const blocklistFilter = function (part) { |
| 680 | + return prototypePollutionBlockList.indexOf(part) === -1 |
| 681 | +} |
| 682 | + |
| 683 | +/** |
| 684 | + * Converts flat objects whose keys use JSON path notation to nested objects. |
| 685 | + * |
| 686 | + * The input object is not mutated. |
| 687 | + * |
| 688 | + * @example |
| 689 | + * { 'foo[bar][0]': 'baz' } -> { foo: { bar: [ 'baz' ] } } |
| 690 | + */ |
| 691 | +const expand = input => { |
| 692 | + if (input === undefined || input === null) { |
| 693 | + return input |
| 694 | + } |
| 695 | + |
| 696 | + const keys = Object.keys(input) |
| 697 | + |
| 698 | + const result = {} |
| 699 | + let resultPtr = result |
| 700 | + |
| 701 | + for (let path of keys) { |
| 702 | + const originalPath = path |
| 703 | + if (path.indexOf('[') >= 0) { |
| 704 | + path = path.replace(/\[/g, '.').replace(/]/g, '') |
| 705 | + } |
| 706 | + |
| 707 | + const parts = path.split('.') |
| 708 | + |
| 709 | + const check = parts.filter(blocklistFilter) |
| 710 | + |
| 711 | + if (check.length !== parts.length) { |
| 712 | + return undefined |
| 713 | + } |
| 714 | + resultPtr = result |
| 715 | + const lastIndex = parts.length - 1 |
| 716 | + |
| 717 | + for (let i = 0; i < parts.length; ++i) { |
| 718 | + const part = parts[i] |
| 719 | + if (i === lastIndex) { |
| 720 | + if (Array.isArray(resultPtr)) { |
| 721 | + resultPtr[+part] = input[originalPath] |
| 722 | + } else { |
| 723 | + resultPtr[part] = input[originalPath] |
| 724 | + } |
| 725 | + } else { |
| 726 | + if (resultPtr[part] === undefined || resultPtr[part] === null) { |
| 727 | + const nextPart = parts[i + 1] |
| 728 | + if (/^\d+$/.test(nextPart)) { |
| 729 | + resultPtr[part] = [] |
| 730 | + } else { |
| 731 | + resultPtr[part] = {} |
| 732 | + } |
| 733 | + } |
| 734 | + resultPtr = resultPtr[part] |
| 735 | + } |
| 736 | + } |
| 737 | + } |
| 738 | + return result |
| 739 | +} |
| 740 | + |
668 | 741 | module.exports = { |
669 | 742 | contentEncoding, |
670 | 743 | dataEqual, |
671 | 744 | deleteHeadersField, |
| 745 | + expand, |
672 | 746 | forEachHeader, |
673 | 747 | formatQueryValue, |
674 | 748 | headersArrayToObject, |
|
0 commit comments