-
-
Notifications
You must be signed in to change notification settings - Fork 5
Open
Description
Note: this is all generated by Claude Sonnet 4.5. If there's any errors, so be it - the issue is real, and I think the ask is reasonable.
Problem Statement
Currently, @kunkun/kkrpc/browser includes SuperJSON and its dependency chain:
superjson(90KB)copy-anything(9KB)is-what(36KB)- Total: 135KB unminified → ~14KB minified
For many browser use cases, this overhead is unnecessary because:
-
MessagePort/BroadcastChannel already support structured clone
- Can handle: Objects, Arrays, Maps, Sets, Dates, typed arrays, ArrayBuffers
- Native browser API, zero overhead
-
SharedWorker/Worker communication doesn't need SuperJSON's advanced features
- Class instance preservation
- Circular reference handling
- Symbol support
- These are rarely needed in RPC scenarios
-
Bundle size matters for browser apps
- Every KB counts for mobile/slow networks, and low-end mobile devices
- 14KB is significant when the core RPC logic is only 15KB. Compression gets it down to under 5kb, but the browser still has to parse it all.
Impact
Real-world example from our migration:
- Before (official package): 29KB minified total-
- After (custom build with superjson-stub): 15KB minified total
- Size decrease: -48% solely from SuperJSON
Proposed Solutions
Solution 1: New export browser-minimal (RECOMMENDED)
Add a new subpath export that uses plain JSON:
// jsr.json
{
"exports": {
"./browser": "./browser-mod.ts",
"./browser-minimal": "./browser-minimal-mod.ts"
}
}// browser-minimal-mod.ts
export * from "./src/adapters/worker.ts"
export * from "./src/adapters/iframe.ts"
export * from "./src/adapters/websocket.ts"
export * from "./src/adapters/tauri.ts"
export * from "./src/interface.ts"
export * from "./src/channel-minimal.ts" // Uses JSON instead of superjson
export * from "./src/utils.ts"
// Note: serialization.ts excluded// src/channel-minimal.ts
// Copy of channel.ts but with:
import { JSONSerializer } from "./serialization-json.ts" // Plain JSON implPros:
- Zero breaking changes
- Users opt-in via import path
- Clear naming convention
- ~15KB bundle size
Cons:
- Code duplication (channel.ts vs channel-minimal.ts)
- Needs separate tests
Solution 2: Make superjson optional
// src/channel.ts
import superjson from "superjson" // Keep default behavior
export class RPCChannel<...> {
constructor(
io: IoInterface,
options?: {
serializer?: Serializer // Allow injection
// ... other options
}
) {
this.serializer = options?.serializer ?? superjson
}
}Pros:
- Single implementation
- Maximum flexibility
- Users can BYO serializer
Cons:
- Breaking change (need major version bump)
- More complex API
- Bundle includes superjson by default unless tree-shaken
Recommended Implementation
Solution 1 (new export) is cleanest:
- No breaking changes
- Clear opt-in model
- Minimal code changes
- Easy to document
Migration Path (if implemented)
// Before
import { RPCChannel } from '@kunkun/kkrpc/browser'
// After (for minimal bundle)
import { RPCChannel } from '@kunkun/kkrpc/browser-minimal'Questions for Maintainers
- Are there use cases that absolutely require SuperJSON in browser contexts?
Workaround (for users today)
Users can import from kkrpc source with import maps:
{
"imports": {
"@kkrpc-source/": "https://raw.githubusercontent.com/kunkunsh/kkrpc/main/packages/kkrpc/src/",
"superjson": "./superjson-stub.ts"
}
}superjson-stub.ts
/**
* Minimal superjson stub to avoid the 135KB dependency chain.
*
* This replaces superjson when bundling from kkrpc source.
* Works because our MessagePort transport already handles structured clone.
*/
export default {
stringify: (obj: unknown) => JSON.stringify(obj),
parse: (str: string) => JSON.parse(str),
serialize: (obj: unknown) => ({ json: obj, meta: undefined }),
deserialize: (payload: { json: unknown }) => payload.json,
};This works but requires:
- Manual version tracking
- Import map configuration
- Custom stub implementation
An official minimal export would be cleaner.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels