diff --git a/packages/roslib/src/core/transport/Transport.ts b/packages/roslib/src/core/transport/Transport.ts index 5f4ad0dda..16403e02b 100644 --- a/packages/roslib/src/core/transport/Transport.ts +++ b/packages/roslib/src/core/transport/Transport.ts @@ -187,12 +187,17 @@ export abstract class AbstractTransport * It is one technique for compressing JSON data. */ private handleRosbridgePngMessage(message: RosbridgePngMessage) { - const decoded = decompressPng(message.data); - if (isRosbridgeMessage(decoded)) { - this.handleRosbridgeMessage(decoded); - } else { - throw new Error("Decompressed PNG data was invalid!"); - } + decompressPng(message.data) + .then((decoded) => { + if (isRosbridgeMessage(decoded)) { + this.handleRosbridgeMessage(decoded); + } else { + this.emit("error", new Error("Decompressed PNG data was invalid!")); + } + }) + .catch((error: unknown) => { + this.emit("error", error); + }); } /** diff --git a/packages/roslib/src/util/decompressPng.ts b/packages/roslib/src/util/decompressPng.ts index 6bfc2b496..7e2fd278c 100644 --- a/packages/roslib/src/util/decompressPng.ts +++ b/packages/roslib/src/util/decompressPng.ts @@ -4,7 +4,6 @@ */ import type { DecodedPng } from "fast-png"; -import { decode } from "fast-png"; const textDecoder = new TextDecoder(); @@ -15,22 +14,27 @@ const textDecoder = new TextDecoder(); * * @param data - An object containing the PNG data. */ -export default function decompressPng(data: string): unknown { +export default async function decompressPng(data: string): Promise { + // fast-png is imported dynamically (lazily) rather than statically to avoid + // a crash in environments such as React Native / Hermes. fast-png constructs + // a `new TextDecoder('latin1')` at module load time, and Hermes does not + // support the 'latin1' encoding, causing an immediate RangeError on import. + // By deferring the import until a PNG message is actually received, users + // who do not use PNG-compressed rosbridge messages are unaffected. + // See: https://github.com/image-js/fast-png/blob/77a4479d68d84246793f58f7bbf2a2ea3a80c0f5/src/helpers/text.ts#L11 + const { decode } = await import("fast-png"); const buffer = Uint8Array.from(atob(data), (char) => char.charCodeAt(0)); - const decoded = tryDecodeBuffer(buffer); - + let decoded: DecodedPng; try { - return JSON.parse(textDecoder.decode(decoded.data)); + decoded = decode(buffer); } catch (error) { - throw new Error("Error parsing PNG JSON contents", { cause: error }); + throw new Error("Error decoding PNG buffer", { cause: error }); } -} -function tryDecodeBuffer(buffer: Uint8Array): DecodedPng { try { - return decode(buffer); + return JSON.parse(textDecoder.decode(decoded.data)); } catch (error) { - throw new Error("Error decoding PNG buffer", { cause: error }); + throw new Error("Error parsing PNG JSON contents", { cause: error }); } }