Last active
April 27, 2025 03:02
-
-
Save colecrouter/f2660efcfb073dc35f6b439692b0beee to your computer and use it in GitHub Desktop.
SvelteKit transport hook deduplication pattern
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { DeduplicationContext } from "./Context"; | |
import { App } from "../models/App"; | |
export class AppContext extends DeduplicationContext<App, ConstructorParameters<typeof App>> { | |
// These functions can be substituted with however you want to serialize/deserialize your classes. | |
// For this, I simply implemented a `serialize()` method that returns `ConstructorParameters<App>`. | |
// I have the same `serialize()` method for `User` as well. | |
protected decodeValue(encoded: ConstructorParameters<typeof App>): App { | |
return new App(...encoded); | |
} | |
protected encodeValue(value: App): ConstructorParameters<typeof App> { | |
return value.serialize(); // This func just | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type Key = keyof any; | |
// Notice the `id` prop, as well as `id` as args[0]. | |
// This could be something else; we just need a unique key to match the params to the class, and vice versa. | |
interface Encodable { | |
id: Key; | |
} | |
/** | |
* A generic abstract class for deduplication of objects during | |
* serialization. | |
*/ | |
export abstract class DeduplicationContext<Class extends Encodable, Params extends [Key, ...unknown[]]> { | |
protected encodeCache = new WeakMap<Class, Params>(); | |
protected decodeCache = new Map<Key, Class>(); | |
/** | |
* Called by the transport when it wishes to encode an instance. | |
* Returns an array containing the unique serial number. | |
*/ | |
public encode(value: Class): Params { | |
const existing = this.encodeCache.get(value); | |
if (existing) { | |
return existing; | |
} | |
const encoded = this.encodeValue(value); | |
this.encodeCache.set(value, encoded); | |
return encoded; | |
} | |
/** | |
* Called by the transport to decode an instance based on the id. | |
*/ | |
public decode(encoded: Params): Class { | |
const existing = this.decodeCache.get(encoded[0]); | |
if (existing) { | |
return existing; | |
} | |
const decoded = this.decodeValue(encoded); | |
this.decodeCache.set(decoded.id, decoded); | |
return decoded; | |
} | |
/** | |
* Clear the caches. Call at the start of each request, if appropriate. | |
*/ | |
public reset(): void { | |
this.encodeCache = new WeakMap(); | |
this.decodeCache = new Map(); | |
} | |
/** | |
* Convert the value to its serialized form. | |
* Must be implemented by subclasses. | |
*/ | |
protected abstract encodeValue(value: Class): Params; | |
/** | |
* Convert the serialized form back into an instance. | |
* Must be implemented by subclasses. | |
*/ | |
protected abstract decodeValue(encoded: Params): Class; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { App } from "$lib/models/App"; | |
import { AppContext } from "$lib/transports/AppContext"; | |
import { User } from "$lib/models/User"; | |
import type { Transport } from "@sveltejs/kit"; | |
const appTransport = new AppContext(); | |
export const transport: Transport = { | |
App: { | |
encode: (data) => data instanceof App && appTransport.encode(data), | |
decode: (data: ConstructorParameters<typeof App>) => appTransport.decode(data), | |
}, | |
User: { | |
encode: (data) => data instanceof User && data.serialize(), | |
decode: (data: ReturnType<User["serialize"]>) => new User(...data), | |
}, | |
// If you have any classes that extend `User`, make sure to put them above ^ or else they'll all get converted to `Users`! | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment