chbrown / rfc6902

Complete implementation of RFC6902 in TypeScript
https://chbrown.github.io/rfc6902/
316 stars 39 forks source link

applyPatch issue with dates #78

Closed ddimitrioglo closed 3 years ago

ddimitrioglo commented 3 years ago

Due to clone's specific behavior: source - should be a JavaScript primitive, Array, or (plain old) Object., it's hard to work with Dates.

  1. Doesn't work:
    
    const issue = {
    openedBy: 'ddimitrioglo'
    };

applyPatch(issue, [{ op: 'add', path: '/openedAt': value: new Date() }])

{ openedBy: "ddimitrioglo", openedAt: Object {} }

  1. Doesn't work:
    
    const issue = {
    openedBy: 'ddimitrioglo',
    openedAt: undefined,
    };

applyPatch(issue, [{ op: 'replace', path: '/openedAt': value: new Date() }])

{ openedBy: "ddimitrioglo", openedAt: undefined }

  1. Works, but it means that I can't have optional properties:
    
    const issue = {
    openedBy: 'ddimitrioglo',
    openedAt: null,
    };

applyPatch(issue, [{ op: 'replace', path: '/openedAt': value: new Date() }])

{ openedBy: "ddimitrioglo", openedAt: }

Is this behavior important and couldn't be changed for some reason? Is it possible to work-around this?

UPDATE 1

For the sake of curiosity used lodash.cloneDeep and my issue was fixed (without breaking any tests)

export function clone<T extends any>(source: T): T {
  return cloneDeep(source);
}
chbrown commented 3 years ago

First, a partial easy answer: your 2. shouldn't work, per the official spec — note the MissingError in the array returned by applyPatch.

The differing behavior of add (clones value) vs. replace (does not clone value) is inconsistent, for sure. I couldn't at first recall why it ever clones the value, but apparently it's due to #44, which is more thoroughly hashed out at #38. 2+ years on and considering the mutating behavior pervasive throughout the library I'm wondering if it was the right idea to ever clone values attached to operations, but it's probably less confusing to the user experience than not doing so, as evidenced by the issues above... despite getting in the way sometimes, as you've noticed.

Btw the reason why lodash.cloneDeep works is because it spends 600+ lines of code handling a swath of various types, while my naive clone takes 20 LOC to handle just the primitive/Array/Object cases.

Sorry to say, but I think part of the solution is to make your 3. stop working 😢 — i.e., replace should clone value just like the other operations.

More to your point though it might be reasonable to specially treat Date instances as "primitive" within clone; I'll have to think about that a bit further but it's probably preferable over treating it like a POJO (i.e., the current behavior), despite not being semantically accurate.

ddimitrioglo commented 3 years ago

@chbrown thank you for the update.

Yes, looks like treat Date instances as "primitive" within clone is the best solution here (any ETAs on that?). Also would want to ask you a question: is doing everything by yourself is a matter of principle? why don't you use things that someone already did (like lodash.cloneDeep)?

P.S. Maybe it's worth adding a Contribution section in the README.md and provide a guideline, share your vision on the project, and future plans. That would be helpful for those who want to contribute.

chbrown commented 3 years ago

Fixed on clone-date branch if you wanna help test it out before release (which'll be a patch-level version bump). You can switch to the branch by changing your package.json's dependencies from {"rfc6902": "^4.0.1"} to {"rfc6902": "github:chbrown/rfc6902#clone-date"}

FYI, I changed my mind about the solution — once I got into writing the fix I realized that adding a special case for classifying Dates as "primitive" would introduce practically as much complexity as a special case for properly cloning them, so I implemented the latter.


Also would want to ask you a question: is doing everything by yourself is a matter of principle?

Your premise isn't quite right, because I don't "do everything by myself," but let me try to address what I think you're getting at:

Maybe it's worth adding a Contribution section [...]

Good point. I should do that -> #79.

ddimitrioglo commented 3 years ago

@chbrown don't get me wrong, I see your point of view and can (should) accept it! just wanted to hear that point and avoid any assumptions 😉

P.S. will check the fix tomorrow and come with updates

ddimitrioglo commented 3 years ago

@chbrown the fix covers all my cases, so looks like it's ready for a new npm version 😉

chbrown commented 3 years ago

https://www.npmjs.com/package/rfc6902/v/4.0.2