dumbmatter / fakeIndexedDB

A pure JS in-memory implementation of the IndexedDB API
Apache License 2.0
562 stars 69 forks source link

structuredClone and jsdom #88

Closed joshkel closed 10 months ago

joshkel commented 11 months ago

I noticed that fakeIndexedDB 5.0 removes its structuredClone polyfill, since structuredClone is provided by Node 18, and fakeIndexedDB now depends on Node 18. However, as I understand it, a major use case of fakeIndexedDB is to simulate IndexedDB within jsdom (as used by, e.g., Jest), and jsdom does not implement structuredClone. (See https://github.com/jsdom/jsdom/issues/3363.)

Therefore, the removal of structuredClone may be premature?

I realize that this change may be completely intended (because workarounds are available - users of fakeIndexedDB can polyfill structuredClone themselves within their jsdom environments, as discussed in https://github.com/jsdom/jsdom/issues/3363); if so, feel free to close this issue.

Thank you.

dumbmatter commented 11 months ago

Hm I didn't realize that about jsdom. I figured since it's in all the browsers and Node.js, then it'd be everywhere.

Annoying jsdom users is bad. But it's also nice to have no dependencies.

I wonder how many people are using this with jsdom vs without, that would make it more clear what the best decision is. Maybe for now I will leave this issue open and see if other people are affected.

And to save people the trouble of reading the jsdom issue for workarounds, seems that you can just add this above your fake-indexeddb import:

import 'core-js/stable/structured-clone';

dethstrobe commented 11 months ago

I just implemented a workaround...which I've had to do a few times for JSDOM now.

In your jest.config you'll need to point the testEnvironment to local file.

Then you'll need to extends JSDOMEnvironment and add the node implementation for structuredClone to the JSDOM environment in the constructor.

Something like this:

import JSDOMEnvironment from "jest-environment-jsdom"

export default class FixJSDOMEnvironment extends JSDOMEnvironment {
  constructor(...args: ConstructorParameters<typeof JSDOMEnvironment>) {
    super(...args)

    this.global.structuredClone = structuredClone
  }
}

For a real world example, you can see my implementation in my project OMA3.

FixJSDOMEnvironment.ts jest.config.js

jamesgpearce commented 11 months ago

Yep, this just broke tests for me (Jest, jsdom, 5.0). Nice workaround though, thanks! Guessing you'll probably get a few more reports of this though.

j-mew-s commented 11 months ago

This broke tests for us

dumbmatter commented 10 months ago

I added the above workarounds to README.md, I think that is good enough to close this issue. Although feel free to comment if you think I'm wrong!

jamesgpearce commented 10 months ago

By the way this now seems to mean that objects returned are null-prototyped, which I don't think the real IndexedDB objects technically are. No big deal unless (like me!) your isObject code wasn't smart enough to allow those.

dumbmatter commented 10 months ago

@jamesgpearce I tried this code:

import realisticStructuredClone from "realistic-structured-clone";

const x = {foo: 5};
const y = Object.create(null);
y.foo = 5;

console.log(Object.getPrototypeOf(x));
console.log(Object.getPrototypeOf(structuredClone(x)));
console.log(Object.getPrototypeOf(realisticStructuredClone(x)));
console.log(Object.getPrototypeOf(y));
console.log(Object.getPrototypeOf(structuredClone(y)));
console.log(Object.getPrototypeOf(realisticStructuredClone(y)));

And got this output:

[Object: null prototype] {}
[Object: null prototype] {}
[Object: null prototype] {}
null
[Object: null prototype] {}
[Object: null prototype] {}

So it seems the old (realisticStructuredClone) and new (structuredClone) are both doing the same thing to the prototype, which actually results in losing the null prototype when cloning.

If you think you're possibly seeing a bug, please create another issue with more info :)