Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attempting to use if mirror has a toJSON and object does not breaks #318

Open
NoLongerBreathedIn opened this issue Oct 30, 2023 · 2 comments

Comments

@NoLongerBreathedIn
Copy link

For example, suppose you have the following objects:

const object = {
    bug: new Date('2032-01-04'),
    nobug: new Date('2032-01-04')
};
const mirror = {
    bug: new Date('2032-01-04'),
    nobug: new Date('2032-01-04').toJSON()
};

Then compare(mirror, obj); returns the following array:

[{
    op: "replace",
    path: "/nobug",
    value: "2032-01-04T00:00:00.000Z"
}, {
    op: "add",
    path: "/bug/0",
    value: "2"
}, {
    op: "add",
    path: "/bug/1",
    value: "0"
}, {
    op: "add",
    path: "/bug/2",
    value: "3"
}, {
    op: "add",
    path: "/bug/3",
    value: "2"
}, {
    op: "add",
    path: "/bug/4",
    value: "-"
}, {
    op: "add",
    path: "/bug/5",
    value: "0"
}, {
    op: "add",
    path: "/bug/6",
    value: "1"
}, {
    op: "add",
    path: "/bug/7",
    value: "-"
}, {
    op: "add",
    path: "/bug/8",
    value: "0"
}, {
    op: "add",
    path: "/bug/9",
    value: "4"
}, {
    op: "add",
    path: "/bug/10",
    value: "T"
}, {
    op: "add",
    path: "/bug/11",
    value: "0"
}, {
    op: "add",
    path: "/bug/12",
    value: "0"
}, {
    op: "add",
    path: "/bug/13",
    value: ":"
}, {
    op: "add",
    path: "/bug/14",
    value: "0"
}, {
    op: "add",
    path: "/bug/15",
    value: "0"
}, {
    op: "add",
    path: "/bug/16",
    value: ":"
}, {
    op: "add",
    path: "/bug/17",
    value: "0"
}, {
    op: "add",
    path: "/bug/18",
    value: "0"
}, {
    op: "add",
    path: "/bug/19",
    value: "."
}, {
    op: "add",
    path: "/bug/20",
    value: "0"
}, {
    op: "add",
    path: "/bug/21",
    value: "0"
}, {
    op: "add",
    path: "/bug/22",
    value: "0"
}, {
    op: "add",
    path: "/bug/23",
    value: "Z"
}]
@begrs
Copy link

begrs commented Oct 31, 2023

This issue persists since 2021 which is really bad for such a far reaching library.
We have the same issue. It happens when both objects contain a Date.
Duplicate of #278

Workaround: when creating the objects to compare, use toIsoString beforehand. e.g. bug = new Date('2032-01-04').toIsoString();

Error Source:
// src/duplex.ts line 148 to 149
if (typeof obj.toJSON === "function") {
obj = obj.toJSON();
}
Explanation:

  • When checking for equality in the _generate function, having a Date object in both mirror and obj will cause line 166 to return true. (since typeof myDate is also object)
  • this means the _generate function will be called with "object.bug" and "mirror.bug"
  • line 148 will convert object to its JSON version -> a string representing new Date('2032-01-04') which is "2032-01-04T00:00:00"
  • however, the mirror object will not be converted to json.
  • this means newKeys will have all the characters of the string in it
  • but oldKeys will be an empty array
  • this causes line 198 to 203 to be reached, resulting in all the "add" operations

Possible Solution:

  • check typeof obj.toJSON === "function" and replacing obj and mirror with the result before the type check in line 166. This would prevent the call to _generate() with "object.bug" and "mirror.bug" and will terminate without recursion.

@bohoffi
Copy link

bohoffi commented Dec 14, 2023

Here's a little helper function which patches Date values - it traverses over arrays and objects:

/**
 * The function takes an input value and applies data type patches if the input value is:
 * - `Date` --> JSON representation as fast-json-patch cannot handle Dates
 * - an `object` --> traverses the object tree applying patches to properties where necessary
 * - an `array` --> traverses the array elements applying patches to properties where necessary
 *
 * All other data types - e.g. `string`, `number` will stay unpatched.
 *
 * @returns patched input when necessary
 */
export function patchDataTypes<T>(input: T): any {
  if (input instanceof Date) {
    return input.toJSON() as string;
  } else if (typeof input === 'object' && input !== null) {
    if (Array.isArray(input)) {
      // If it's an array, recursively convert each element
      return input.map(patchDataTypes);
    } else {
      // If it's an object, recursively convert each property
      const result: Record<string, unknown> = {};
      for (const key in input) {
        result[key] = patchDataTypes(input[key]);
      }
      return result;
    }
  } else {
    return input;
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants