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
support custom types #58
Conversation
Are there any built-in names that users would need to be careful not to collide with? (I'm not sure whether these user-provided names end up in the same 'spot' in the serialized data as built-in names.) Would adding new built-in things then become a breaking change? Do we need to worry about |
We actually do use Custom types are handled first, precisely so that we can avoid the breaking change problem (I heard your voice in my head right as I was about to commit the first attempt, which didn't do that). If someone does this... const str = devalue.stringify(thing, {
Record: (value) => ...,
Tuple: (value) => ...
}); ...and we later support |
Implemented custom replacers for let uid = 1;
let count = 0;
function replacer(thing) {
if (typeof thing?.then !== 'function') return;
const id = uid++;
count += 1;
thing.then((value, error) => {
controller.enqueue(`<script>__resolve(${devalue.uneval({ id, value, error }, replacer)}</script>`);
count -= 1;
if (count === 0) {
controller.close();
}
});
return `__defer(${id})`;
}
const serialized = devalue.uneval(data, replacer);
if (count > 0) {
controller.enqueue(`
<script type="module">
// ...
const deferred = new Map();
__defer = (id) => new Promise((fulfil, reject) => {
deferred.set(id, { fulfil, reject });
});
__resolve = ({ id, value, error }) => {
const { fulfil, reject } = deferred.get(id);
deferred.delete(id);
if (error) reject(error);
else fulfil(value);
};
start({
// ...
data: ${serialized}
});
</script>
`);
} else {
controller.enqueue(`
<script type="module">
// ...
start({
// ...
data: ${serialized}
});
</script>
`);
} |
The lack of parity between the reducers/revivers API and the custom replacer API is bugging me. I'm not sure that there's a reasonable way around that. I assume this is something you've spent some time thinking about already. Is the goal of this within SvelteKit just to allow us to internally be able to serialize more types of data for the |
I also don't see a way around this, it's too distinct ways to go about the problem. If we worry about people having to write things three times, we could only expose the stringify/revive API and under the hood add some mapping logic. Like I don't know, I feel like if people really want to use their custom types, they can live with a little friction. And people could publish libraries for reuse. |
The immediate goal is only to support streaming, which doesn't require anything to be publicly exposed. If we did want people to be able to register custom types then I think we'd have to find a way to use |
Looked at the pseudo code once more, looks good to me 👍 |
This adds support for custom types. For example you could encode promises in an ndjson stream like this (note: code is untested!):
On the browser side something like this should work: