/** * Turn Workers RPC Proxies / stubs into plain JSON data. * Subscription and execute results cannot carry Proxies across the isolate * boundary ("Proxy could not be serialized..."). * * packageStorage object values hit the same wall on get/list — store JSON * strings instead, and always plainClone on read. */ export function plainClone(value: T): T { if (value === null || value === undefined) return value const t = typeof value if (t === 'string' || t === 'number' || t === 'boolean' || t === 'bigint') { return value } try { return JSON.parse(JSON.stringify(value)) as T } catch { if (Array.isArray(value)) return [] as unknown as T if (value && t === 'object') return {} as T return value } } function isProxySerializeError(error: unknown): boolean { const message = error instanceof Error ? error.message : String(error) return /Proxy could not be serialized/i.test(message) } /** Read packageStorage.get and always return structured-cloneable data. */ export async function storageGet( get: (key: string) => Promise, key: string, ): Promise { try { const raw = await get(key) if (raw === null || raw === undefined) return null return plainClone(raw) } catch (error) { if (isProxySerializeError(error)) return null throw error } } /** * Write packageStorage values as JSON strings when they are objects/arrays. * String/number/boolean stay as-is. Avoids unreadable Proxy object values. */ export async function storageSet( set: (key: string, value: unknown) => Promise, key: string, value: unknown, ): Promise { if (value === null || value === undefined) { await set(key, value as never) return } const t = typeof value if (t === 'string' || t === 'number' || t === 'boolean') { await set(key, value) return } await set(key, JSON.stringify(value)) }