Skip to content

Conversation

@davidkpiano
Copy link
Member

No description provided.

@jwyoo
Copy link

jwyoo commented Mar 17, 2025

Performance improvement issues, but performance problems still exist.

function deepCopyAndPrepareForSafeStringify(
  obj: any,
  hash = new WeakMap()
): any {
  // check circular
  if (hash.has(obj)) {
    return hash.get(obj);
  }

  if (typeof obj === "bigint") {
    return { type: "bigint", value: obj.toString() };
  }

  if (obj === null || typeof obj !== "object") {
    return obj;
  }

  if (obj instanceof HTMLElement) {
    const element = {
      tag: obj.tagName,
      outerHTML: obj.outerHTML,
    };
    hash.set(obj, element);
    return element;
  }

  if (Object.prototype.hasOwnProperty.call(obj, "toJSON")) {
    const json = obj.toJSON();
    for (const key in json) {
      json[key] = deepCopyAndPrepareForSafeStringify(json[key], hash);
    }
    hash.set(obj, json);
    return json;
  }

  const copy = Array.isArray(obj)
    ? ([] as any[])
    : ({ __proto__: obj.__proto__ } as any);
  hash.set(obj, copy);
  for (const key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      copy[key] = deepCopyAndPrepareForSafeStringify(obj[key], hash);
    }
  }
  return copy;
}

@Blacktiger
Copy link

I hit a similar issue with one of my projects using this action which also breaks the inspector during serialization. This is the actor that caused the problem:

const loadSessions = fromPromise(async ({ input }: { input: SessionContext }) => {
  const { data: user } = await input.supabaseClient.auth.getUser();
  if (!user.user) throw new Error('Not authenticated');

  // Get all sessions where user is GM or participant
  const { data, error } = await input.supabaseClient
    .from('sessions')
    .select(`
      *,
      session_participants!inner(user_id)
    `)
    .eq('session_participants.user_id', user.user.id)
    .order('created_at', { ascending: false });

  if (error) throw error;
  return data as Session[];
});

When this actor completes, the snapshot contains Supabase-returned Session[] objects with Date fields (created_at, updated_at), nested arrays (session_participants), and complex object structures. The inspector crashes with TypeError: can't access property "config", s is undefined.

The crash happens because of this serialization pattern:

JSON.parse(safeStringify(snapshot))

According to the AI assistant I had helping me look at the code:

  • Supabase returns Date fields (created_at, updated_at, etc.)
  • JSON.stringify converts Dates to ISO strings automatically
  • JSON.parse doesn't convert them back to Date objects
  • The nested structures and complex types get corrupted in the round-trip
  • The snapshot object becomes invalid, causing "s is undefined" when downstream code tries to access properties

The deepCopyAndPrepareForSafeStringify workaround handles bigint/HTMLElement, but doesn't handle Date objects from database queries. It would still corrupt those Dates, breaking the snapshot structure.

AI suggested swapping to superjson.parse(superjson.stringify(snapshot)) to fix the issue since it's already a dependency and should be more compatible, but I'm not sure if that will fix the HTMLElement serialization you're tackling in this PR.

@davidkpiano
Copy link
Member Author

@Blacktiger Can you try this fix instead? #48

@Blacktiger
Copy link

@Blacktiger Can you try this fix instead? #48

I'll see if I can give that a try hopefully later this week.

obj: any,
hash = new WeakMap()
): any {
if (typeof obj === 'bigint') return obj.toString();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is pretty ad-hoc. Ideally, the serialization should try to avoid losing information. It is a different thing to have a "12" string and a 12n bigint value. The serialization should try to encode the encoded's type using a wrapper object or something so its decoder counterpart could try to deserialize it closely. Or at least display as much as possible about the original value.

Some inspiration here could be drawn from libraries like superjson, React Flight's serialization algorithm and similar.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the lossy nature of the current approach has already been raised as a concern here: #46 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants