feat: added asPlainData(depth?) method for CoMap and CoList
The latest updates on your projects. Learn more about Vercel for Git ↗︎
| Name | Status | Preview | Comments | Updated (UTC) |
|---|---|---|---|---|
| jazz-chat | ❌ Failed (Inspect) | Aug 14, 2024 3:36pm | ||
| jazz-homepage | ❌ Failed (Inspect) | Aug 14, 2024 3:36pm | ||
| jazz-pets | ❌ Failed (Inspect) | Aug 14, 2024 3:36pm | ||
| jazz-todo | ❌ Failed (Inspect) | Aug 14, 2024 3:36pm |
1 Skipped Deployment
| Name | Status | Preview | Comments | Updated (UTC) |
|---|---|---|---|---|
| jazz-inspector | ⬜️ Skipped (Inspect) | Aug 14, 2024 3:36pm |
Looks great so far!
Please could you create a sync version asPlainData() based on whatever is available and async asPlainDataLoaded(depth)?
How should I handle circular references?
For example, here is an example without circular references (taken from the tests):
const innerMap = SimpleMap.create(
{
name: "Bob",
age: 25,
isActive: false,
},
{ owner: me }
);
const nestedMap = NestedMap.create(
{
info: innerMap,
data: "Some data",
},
{ owner: me }
);
const plainData = nestedMap.asPlainData();
expect(plainData).toEqual({
info: {
name: "Bob",
age: 25,
isActive: false,
},
data: "Some data",
});
But consider this schema:
export class PasswordItem extends CoMap {
name = co.string;
username = co.optional.string;
username_input_selector = co.optional.string;
password = co.string;
password_input_selector = co.optional.string;
uri = co.optional.string;
folder = co.ref(Folder);
deleted = co.boolean;
}
export class PasswordList extends CoList.Of(co.ref(PasswordItem)) {}
export class Folder extends CoMap {
name = co.string;
items = co.ref(PasswordList);
}
export class FolderList extends CoList.Of(co.ref(Folder)) {}
export class PasswordManagerAccountRoot extends CoMap {
folders = co.ref(FolderList);
}
export class PasswordManagerAccount extends Account {
profile = co.ref(Profile);
root = co.ref(PasswordManagerAccountRoot);
We're using co.ref(Folder) for both FolderList and PasswordItem.folder.
Now, if I want to create a plain object:
const { me, logOut } = useAccount();
console.log(me.root?.asPlainData())
We get the error:
RangeError: Maximum call stack size exceeded
at Object.ownKeys (coMap.ts:651:12)
at Function.keys (<anonymous>)
at Proxy.asPlainDataSync (coMap.ts:543:34)
at Proxy.asPlainData (coMap.ts:501:25)
at Proxy.asPlainDataSync (coList.ts:480:39)
at Proxy.asPlainData (coList.ts:462:25)
at Proxy.asPlainDataSync (coMap.ts:551:51)
at Proxy.asPlainData (coMap.ts:501:25)
at Proxy.asPlainDataSync (coMap.ts:548:51)
at Proxy.asPlainData (coMap.ts:501:25)
One way to fix this is to use a WeakMap to keep track of objects that have already been processed:
if (seen.has(this)) {
return { _circular: this.id } // or return seen.get(this) to get an infinitely nested object
}
And then console.log(me.root?.asPlainData()) looks like this:
{
"folders": [
{
"name": "Default",
"items": [
{
"name": "123",
"username": "[email protected]",
"password": "password123",
"uri": "https://gmail.com",
"folder": {
"_circular": "co_zWA4FKAjAUbNc5HT4YiH4d1EE5s"
},
"deleted": false
},
{
"name": "Facebook",
"username": "[email protected]",
"password": "facebookpass",
"uri": "https://facebook.com",
"folder": {
"_circular": "co_zWA4FKAjAUbNc5HT4YiH4d1EE5s"
},
"deleted": false
}
]
}
]
}
I'm not sure how it's best to handle this.
Please may you adapt the implementation as suggested by you - so for circular references asPlainData returns plain data that is itself circularly references itself?
Closing this for now - we'll likely use it as a basis for a bigger API change in the future