You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iggy.apache.org by pi...@apache.org on 2025/11/24 14:14:24 UTC

(iggy) branch master updated: fix(web): improve u64 and u128 parsing and rendering in UI (#2400)

This is an automated email from the ASF dual-hosted git repository.

piotr pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iggy.git


The following commit(s) were added to refs/heads/master by this push:
     new 8ef0d746b fix(web): improve u64 and u128 parsing and rendering in UI (#2400)
8ef0d746b is described below

commit 8ef0d746b809b7bd0416e7b381eb79340918324b
Author: Piotr Gankiewicz <pi...@gmail.com>
AuthorDate: Mon Nov 24 15:14:20 2025 +0100

    fix(web): improve u64 and u128 parsing and rendering in UI (#2400)
    
    Allow to pass either numeric or string message ID for HTTP API,
    fix Web UI to render u64-u128 numbers.
---
 core/common/src/commands/messages/send_messages.rs |  76 ++++++-
 core/server/server.http                            |   2 +-
 .../Iggy_SDK/JsonConverters/MessageConverter.cs    |   3 +-
 web/package-lock.json                              | 221 ++++++++++-----------
 web/package.json                                   |   3 +
 web/src/lib/api/getJson.ts                         |  55 ++++-
 .../lib/components/Modals/InspectMessage.svelte    |  12 +-
 web/src/lib/domain/Message.ts                      |  36 +++-
 web/src/lib/utils/formatters/uuidFormatter.ts      |  65 ++++++
 9 files changed, 332 insertions(+), 141 deletions(-)

diff --git a/core/common/src/commands/messages/send_messages.rs b/core/common/src/commands/messages/send_messages.rs
index c490a4da5..05a4187ab 100644
--- a/core/common/src/commands/messages/send_messages.rs
+++ b/core/common/src/commands/messages/send_messages.rs
@@ -293,8 +293,9 @@ impl<'de> Deserialize<'de> for SendMessages {
                             let mut iggy_messages = Vec::new();
 
                             for msg in message_data {
-                                let id =
-                                    msg.get("id").and_then(|v| v.as_u64()).unwrap_or(0) as u128;
+                                let id = parse_message_id(msg.get("id")).map_err(|errror| {
+                                    de::Error::custom(format!("Invalid message ID: {errror}"))
+                                })?;
 
                                 let payload = msg
                                     .get("payload")
@@ -371,6 +372,36 @@ impl<'de> Deserialize<'de> for SendMessages {
     }
 }
 
+fn parse_message_id(value: Option<&serde_json::Value>) -> Result<u128, String> {
+    let value = match value {
+        Some(v) => v,
+        None => return Ok(0),
+    };
+
+    match value {
+        serde_json::Value::Number(id) => id
+            .as_u64()
+            .map(|v| v as u128)
+            .ok_or_else(|| "ID must be a positive integer".to_string()),
+        serde_json::Value::String(id) => {
+            if let Ok(id) = id.parse::<u128>() {
+                return Ok(id);
+            }
+
+            let hex_str = id.replace('-', "");
+            if hex_str.len() == 32 && hex_str.chars().all(|c| c.is_ascii_hexdigit()) {
+                u128::from_str_radix(&hex_str, 16)
+                    .map_err(|error| format!("Invalid UUID format: {error}"))
+            } else {
+                Err(format!(
+                    "Invalid ID string: '{id}' - must be a decimal number or UUID hex format",
+                ))
+            }
+        }
+        _ => Err("ID must be a number or string".to_string()),
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -426,4 +457,45 @@ mod tests {
         let key = Partitioning::messages_key_str(&messages_key);
         assert!(key.is_err());
     }
+
+    #[test]
+    fn parse_message_id_from_number() {
+        let value = serde_json::json!(12345);
+        let id = parse_message_id(Some(&value)).unwrap();
+        assert_eq!(id, 12345u128);
+    }
+
+    #[test]
+    fn parse_message_id_from_large_number_string() {
+        let value = serde_json::json!("340282366920938463463374607431768211455");
+        let id = parse_message_id(Some(&value)).unwrap();
+        assert_eq!(id, 340282366920938463463374607431768211455u128);
+    }
+
+    #[test]
+    fn parse_message_id_from_uuid_with_dashes() {
+        let value = serde_json::json!("af362865-042c-4000-0000-000000000000");
+        let id = parse_message_id(Some(&value)).unwrap();
+        assert_eq!(id, 0xaf362865042c40000000000000000000u128);
+    }
+
+    #[test]
+    fn parse_message_id_from_uuid_without_dashes() {
+        let value = serde_json::json!("af362865042c40000000000000000000");
+        let id = parse_message_id(Some(&value)).unwrap();
+        assert_eq!(id, 0xaf362865042c40000000000000000000u128);
+    }
+
+    #[test]
+    fn parse_message_id_defaults_to_zero_when_missing() {
+        let id = parse_message_id(None).unwrap();
+        assert_eq!(id, 0u128);
+    }
+
+    #[test]
+    fn parse_message_id_rejects_invalid_string() {
+        let value = serde_json::json!("not-a-valid-id");
+        let result = parse_message_id(Some(&value));
+        assert!(result.is_err());
+    }
 }
diff --git a/core/server/server.http b/core/server/server.http
index f9e49908d..dd52429e4 100644
--- a/core/server/server.http
+++ b/core/server/server.http
@@ -22,7 +22,7 @@
 @consumer_group_id = 1
 @consumer_id = 1
 @client_id = 1
-@partition_id_payload_base64 = AQAAAA==
+@partition_id_payload_base64 = AAAAAA==
 @message_1_payload_base64 = aGVsbG8=
 @message_2_payload_base64 = d29ybGQ=
 @header_1_payload_base_64 = dmFsdWUgMQ==
diff --git a/foreign/csharp/Iggy_SDK/JsonConverters/MessageConverter.cs b/foreign/csharp/Iggy_SDK/JsonConverters/MessageConverter.cs
index 6e346057d..e154f6a8e 100644
--- a/foreign/csharp/Iggy_SDK/JsonConverters/MessageConverter.cs
+++ b/foreign/csharp/Iggy_SDK/JsonConverters/MessageConverter.cs
@@ -42,8 +42,7 @@ internal sealed class MessageConverter : JsonConverter<Message>
 
     private static void WriteMessageId(Utf8JsonWriter writer, UInt128 id)
     {
-        writer.WritePropertyName("id");
-        writer.WriteRawValue(id.ToString());
+        writer.WriteString("id", id.ToString());
     }
 
     private static void WritePayload(Utf8JsonWriter writer, byte[] payload)
diff --git a/web/package-lock.json b/web/package-lock.json
index 72a69a307..44feafb3e 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "iggy-web-ui",
-  "version": "0.1.0-edge.1",
+  "version": "0.1.0-edge.2",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "iggy-web-ui",
-      "version": "0.1.0-edge.1",
+      "version": "0.1.0-edge.2",
       "dependencies": {
         "@floating-ui/dom": "^1.7.4",
         "@tailwindcss/postcss": "^4.1.17",
@@ -14,6 +14,7 @@
         "d3-interpolate": "^3.0.1",
         "date-fns": "^4.1.0",
         "gsap": "^3.13.0",
+        "json-bigint": "^1.0.0",
         "svelte-headlessui": "^0.0.46",
         "svelte-popperjs": "^1.3.2",
         "svelte-transition": "^0.0.17",
@@ -27,8 +28,10 @@
         "@sveltejs/kit": "^2.48.5",
         "@sveltejs/vite-plugin-svelte": "^6.2.1",
         "@types/d3-interpolate": "^3.0.4",
+        "@types/json-bigint": "^1.0.4",
         "@types/node": "^24.10.1",
         "autoprefixer": "^10.4.22",
+        "esbuild": "^0.27.0",
         "eslint": "^9.39.1",
         "eslint-config-prettier": "^10.1.8",
         "eslint-plugin-svelte": "^3.13.0",
@@ -104,7 +107,6 @@
       "os": [
         "aix"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -122,7 +124,6 @@
       "os": [
         "android"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -140,7 +141,6 @@
       "os": [
         "android"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -158,7 +158,6 @@
       "os": [
         "android"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -176,7 +175,6 @@
       "os": [
         "darwin"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -194,7 +192,6 @@
       "os": [
         "darwin"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -212,7 +209,6 @@
       "os": [
         "freebsd"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -230,7 +226,6 @@
       "os": [
         "freebsd"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -248,7 +243,6 @@
       "os": [
         "linux"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -266,7 +260,6 @@
       "os": [
         "linux"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -284,7 +277,6 @@
       "os": [
         "linux"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -302,7 +294,6 @@
       "os": [
         "linux"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -320,7 +311,6 @@
       "os": [
         "linux"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -338,7 +328,6 @@
       "os": [
         "linux"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -356,7 +345,6 @@
       "os": [
         "linux"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -374,7 +362,6 @@
       "os": [
         "linux"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -392,7 +379,6 @@
       "os": [
         "linux"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -410,7 +396,6 @@
       "os": [
         "netbsd"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -428,7 +413,6 @@
       "os": [
         "netbsd"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -446,7 +430,6 @@
       "os": [
         "openbsd"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -464,7 +447,6 @@
       "os": [
         "openbsd"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -482,7 +464,6 @@
       "os": [
         "openharmony"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -500,7 +481,6 @@
       "os": [
         "sunos"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -518,7 +498,6 @@
       "os": [
         "win32"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -536,7 +515,6 @@
       "os": [
         "win32"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -554,7 +532,6 @@
       "os": [
         "win32"
       ],
-      "peer": true,
       "engines": {
         "node": ">=18"
       }
@@ -1485,9 +1462,9 @@
       }
     },
     "node_modules/@sveltejs/kit": {
-      "version": "2.48.5",
-      "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.48.5.tgz",
-      "integrity": "sha512-/rnwfSWS3qwUSzvHynUTORF9xSJi7PCR9yXkxUOnRrNqyKmCmh3FPHH+E9BbgqxXfTevGXBqgnlh9kMb+9T5XA==",
+      "version": "2.49.0",
+      "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.49.0.tgz",
+      "integrity": "sha512-oH8tXw7EZnie8FdOWYrF7Yn4IKrqTFHhXvl8YxXxbKwTMcD/5NNCryUSEXRk2ZR4ojnub0P8rNrsVGHXWqIDtA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -1858,6 +1835,13 @@
         "gsap": "*"
       }
     },
+    "node_modules/@types/json-bigint": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@types/json-bigint/-/json-bigint-1.0.4.tgz",
+      "integrity": "sha512-ydHooXLbOmxBbubnA7Eh+RpBzuaIiQjh8WGJYQB50JFGFrdxW7JzVlyEV7fAXw0T2sqJ1ysTneJbiyNLqZRAag==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@types/json-schema": {
       "version": "7.0.15",
       "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@@ -1926,17 +1910,17 @@
       }
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "8.46.4",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.4.tgz",
-      "integrity": "sha512-R48VhmTJqplNyDxCyqqVkFSZIx1qX6PzwqgcXn1olLrzxcSBDlOsbtcnQuQhNtnNiJ4Xe5gREI1foajYaYU2Vg==",
+      "version": "8.47.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.47.0.tgz",
+      "integrity": "sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/regexpp": "^4.10.0",
-        "@typescript-eslint/scope-manager": "8.46.4",
-        "@typescript-eslint/type-utils": "8.46.4",
-        "@typescript-eslint/utils": "8.46.4",
-        "@typescript-eslint/visitor-keys": "8.46.4",
+        "@typescript-eslint/scope-manager": "8.47.0",
+        "@typescript-eslint/type-utils": "8.47.0",
+        "@typescript-eslint/utils": "8.47.0",
+        "@typescript-eslint/visitor-keys": "8.47.0",
         "graphemer": "^1.4.0",
         "ignore": "^7.0.0",
         "natural-compare": "^1.4.0",
@@ -1950,7 +1934,7 @@
         "url": "https://opencollective.com/typescript-eslint"
       },
       "peerDependencies": {
-        "@typescript-eslint/parser": "^8.46.4",
+        "@typescript-eslint/parser": "^8.47.0",
         "eslint": "^8.57.0 || ^9.0.0",
         "typescript": ">=4.8.4 <6.0.0"
       }
@@ -1966,16 +1950,16 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "8.46.4",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.4.tgz",
-      "integrity": "sha512-tK3GPFWbirvNgsNKto+UmB/cRtn6TZfyw0D6IKrW55n6Vbs7KJoZtI//kpTKzE/DUmmnAFD8/Ca46s7Obs92/w==",
+      "version": "8.47.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.47.0.tgz",
+      "integrity": "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/scope-manager": "8.46.4",
-        "@typescript-eslint/types": "8.46.4",
-        "@typescript-eslint/typescript-estree": "8.46.4",
-        "@typescript-eslint/visitor-keys": "8.46.4",
+        "@typescript-eslint/scope-manager": "8.47.0",
+        "@typescript-eslint/types": "8.47.0",
+        "@typescript-eslint/typescript-estree": "8.47.0",
+        "@typescript-eslint/visitor-keys": "8.47.0",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -1991,14 +1975,14 @@
       }
     },
     "node_modules/@typescript-eslint/project-service": {
-      "version": "8.46.4",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.4.tgz",
-      "integrity": "sha512-nPiRSKuvtTN+no/2N1kt2tUh/HoFzeEgOm9fQ6XQk4/ApGqjx0zFIIaLJ6wooR1HIoozvj2j6vTi/1fgAz7UYQ==",
+      "version": "8.47.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.47.0.tgz",
+      "integrity": "sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/tsconfig-utils": "^8.46.4",
-        "@typescript-eslint/types": "^8.46.4",
+        "@typescript-eslint/tsconfig-utils": "^8.47.0",
+        "@typescript-eslint/types": "^8.47.0",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -2013,14 +1997,14 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "8.46.4",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.4.tgz",
-      "integrity": "sha512-tMDbLGXb1wC+McN1M6QeDx7P7c0UWO5z9CXqp7J8E+xGcJuUuevWKxuG8j41FoweS3+L41SkyKKkia16jpX7CA==",
+      "version": "8.47.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.47.0.tgz",
+      "integrity": "sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.46.4",
-        "@typescript-eslint/visitor-keys": "8.46.4"
+        "@typescript-eslint/types": "8.47.0",
+        "@typescript-eslint/visitor-keys": "8.47.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2031,9 +2015,9 @@
       }
     },
     "node_modules/@typescript-eslint/tsconfig-utils": {
-      "version": "8.46.4",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.4.tgz",
-      "integrity": "sha512-+/XqaZPIAk6Cjg7NWgSGe27X4zMGqrFqZ8atJsX3CWxH/jACqWnrWI68h7nHQld0y+k9eTTjb9r+KU4twLoo9A==",
+      "version": "8.47.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.47.0.tgz",
+      "integrity": "sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -2048,15 +2032,15 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "8.46.4",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.4.tgz",
-      "integrity": "sha512-V4QC8h3fdT5Wro6vANk6eojqfbv5bpwHuMsBcJUJkqs2z5XnYhJzyz9Y02eUmF9u3PgXEUiOt4w4KHR3P+z0PQ==",
+      "version": "8.47.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.47.0.tgz",
+      "integrity": "sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.46.4",
-        "@typescript-eslint/typescript-estree": "8.46.4",
-        "@typescript-eslint/utils": "8.46.4",
+        "@typescript-eslint/types": "8.47.0",
+        "@typescript-eslint/typescript-estree": "8.47.0",
+        "@typescript-eslint/utils": "8.47.0",
         "debug": "^4.3.4",
         "ts-api-utils": "^2.1.0"
       },
@@ -2073,9 +2057,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "8.46.4",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.4.tgz",
-      "integrity": "sha512-USjyxm3gQEePdUwJBFjjGNG18xY9A2grDVGuk7/9AkjIF1L+ZrVnwR5VAU5JXtUnBL/Nwt3H31KlRDaksnM7/w==",
+      "version": "8.47.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.47.0.tgz",
+      "integrity": "sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -2087,16 +2071,16 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "8.46.4",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.4.tgz",
-      "integrity": "sha512-7oV2qEOr1d4NWNmpXLR35LvCfOkTNymY9oyW+lUHkmCno7aOmIf/hMaydnJBUTBMRCOGZh8YjkFOc8dadEoNGA==",
+      "version": "8.47.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.47.0.tgz",
+      "integrity": "sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/project-service": "8.46.4",
-        "@typescript-eslint/tsconfig-utils": "8.46.4",
-        "@typescript-eslint/types": "8.46.4",
-        "@typescript-eslint/visitor-keys": "8.46.4",
+        "@typescript-eslint/project-service": "8.47.0",
+        "@typescript-eslint/tsconfig-utils": "8.47.0",
+        "@typescript-eslint/types": "8.47.0",
+        "@typescript-eslint/visitor-keys": "8.47.0",
         "debug": "^4.3.4",
         "fast-glob": "^3.3.2",
         "is-glob": "^4.0.3",
@@ -2142,16 +2126,16 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "8.46.4",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.4.tgz",
-      "integrity": "sha512-AbSv11fklGXV6T28dp2Me04Uw90R2iJ30g2bgLz529Koehrmkbs1r7paFqr1vPCZi7hHwYxYtxfyQMRC8QaVSg==",
+      "version": "8.47.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.47.0.tgz",
+      "integrity": "sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.7.0",
-        "@typescript-eslint/scope-manager": "8.46.4",
-        "@typescript-eslint/types": "8.46.4",
-        "@typescript-eslint/typescript-estree": "8.46.4"
+        "@typescript-eslint/scope-manager": "8.47.0",
+        "@typescript-eslint/types": "8.47.0",
+        "@typescript-eslint/typescript-estree": "8.47.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2166,13 +2150,13 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "8.46.4",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.4.tgz",
-      "integrity": "sha512-/++5CYLQqsO9HFGLI7APrxBJYo+5OCMpViuhV8q5/Qa3o5mMrF//eQHks+PXcsAVaLdn817fMuS7zqoXNNZGaw==",
+      "version": "8.47.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.47.0.tgz",
+      "integrity": "sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.46.4",
+        "@typescript-eslint/types": "8.47.0",
         "eslint-visitor-keys": "^4.2.1"
       },
       "engines": {
@@ -2382,6 +2366,15 @@
         "baseline-browser-mapping": "dist/cli.js"
       }
     },
+    "node_modules/bignumber.js": {
+      "version": "9.3.1",
+      "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
+      "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/brace-expansion": {
       "version": "1.1.12",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
@@ -2757,8 +2750,6 @@
       "dev": true,
       "hasInstallScript": true,
       "license": "MIT",
-      "optional": true,
-      "peer": true,
       "bin": {
         "esbuild": "bin/esbuild"
       },
@@ -3503,6 +3494,15 @@
         "js-yaml": "bin/js-yaml.js"
       }
     },
+    "node_modules/json-bigint": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+      "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+      "license": "MIT",
+      "dependencies": {
+        "bignumber.js": "^9.0.0"
+      }
+    },
     "node_modules/json-buffer": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
@@ -4657,9 +4657,9 @@
       }
     },
     "node_modules/svelte": {
-      "version": "5.43.6",
-      "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.43.6.tgz",
-      "integrity": "sha512-RnyO9VXI85Bmsf4b8AuQFBKFYL3LKUl+ZrifOjvlrQoboAROj5IITVLK1yOXBjwUWUn2BI5cfmurktgCzuZ5QA==",
+      "version": "5.43.14",
+      "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.43.14.tgz",
+      "integrity": "sha512-pHeUrp1A5S6RGaXhJB7PtYjL1VVjbVrJ2EfuAoPu9/1LeoMaJa/pcdCsCSb0gS4eUHAHnhCbUDxORZyvGK6kOQ==",
       "license": "MIT",
       "dependencies": {
         "@jridgewell/remapping": "^2.3.4",
@@ -5048,16 +5048,16 @@
       }
     },
     "node_modules/typescript-eslint": {
-      "version": "8.46.4",
-      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.4.tgz",
-      "integrity": "sha512-KALyxkpYV5Ix7UhvjTwJXZv76VWsHG+NjNlt/z+a17SOQSiOcBdUXdbJdyXi7RPxrBFECtFOiPwUJQusJuCqrg==",
+      "version": "8.47.0",
+      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.47.0.tgz",
+      "integrity": "sha512-Lwe8i2XQ3WoMjua/r1PHrCTpkubPYJCAfOurtn+mtTzqB6jNd+14n9UN1bJ4s3F49x9ixAm0FLflB/JzQ57M8Q==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/eslint-plugin": "8.46.4",
-        "@typescript-eslint/parser": "8.46.4",
-        "@typescript-eslint/typescript-estree": "8.46.4",
-        "@typescript-eslint/utils": "8.46.4"
+        "@typescript-eslint/eslint-plugin": "8.47.0",
+        "@typescript-eslint/parser": "8.47.0",
+        "@typescript-eslint/typescript-estree": "8.47.0",
+        "@typescript-eslint/utils": "8.47.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -5167,9 +5167,9 @@
       }
     },
     "node_modules/vite": {
-      "version": "7.2.2",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz",
-      "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
+      "version": "7.2.4",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.4.tgz",
+      "integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -5771,21 +5771,6 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/yaml": {
-      "version": "2.8.1",
-      "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz",
-      "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
-      "dev": true,
-      "license": "ISC",
-      "optional": true,
-      "peer": true,
-      "bin": {
-        "yaml": "bin.mjs"
-      },
-      "engines": {
-        "node": ">= 14.6"
-      }
-    },
     "node_modules/yocto-queue": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
@@ -5820,9 +5805,9 @@
       "license": "MIT"
     },
     "node_modules/zod": {
-      "version": "4.1.12",
-      "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz",
-      "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==",
+      "version": "4.1.13",
+      "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz",
+      "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==",
       "dev": true,
       "license": "MIT",
       "funding": {
diff --git a/web/package.json b/web/package.json
index 6b47f3217..ede1de699 100644
--- a/web/package.json
+++ b/web/package.json
@@ -19,8 +19,10 @@
     "@sveltejs/kit": "^2.48.5",
     "@sveltejs/vite-plugin-svelte": "^6.2.1",
     "@types/d3-interpolate": "^3.0.4",
+    "@types/json-bigint": "^1.0.4",
     "@types/node": "^24.10.1",
     "autoprefixer": "^10.4.22",
+    "esbuild": "^0.27.0",
     "eslint": "^9.39.1",
     "eslint-config-prettier": "^10.1.8",
     "eslint-plugin-svelte": "^3.13.0",
@@ -47,6 +49,7 @@
     "d3-interpolate": "^3.0.1",
     "date-fns": "^4.1.0",
     "gsap": "^3.13.0",
+    "json-bigint": "^1.0.0",
     "svelte-headlessui": "^0.0.46",
     "svelte-popperjs": "^1.3.2",
     "svelte-transition": "^0.0.17",
diff --git a/web/src/lib/api/getJson.ts b/web/src/lib/api/getJson.ts
index 2b0d465e7..8a399dfb8 100644
--- a/web/src/lib/api/getJson.ts
+++ b/web/src/lib/api/getJson.ts
@@ -17,17 +17,60 @@
  * under the License.
  */
 
+import JSONBigInt from 'json-bigint';
+
+// Configure json-bigint to parse as strings to avoid BigInt issues with math operations
+const JSONbig = JSONBigInt({
+  useNativeBigInt: true,
+  alwaysParseAsBig: false,
+  strict: false,
+  // Custom reviver to convert BigInt to string to avoid math operation issues
+  protoAction: 'ignore',
+  constructorAction: 'ignore'
+});
+
 export const getJson = async (res: Response): Promise<unknown | null> => {
   const text = await res.text();
   try {
-    const json = JSON.parse(text, (_key, value) => {
-      if (typeof value === 'number' && value > Number.MAX_SAFE_INTEGER) {
-        return BigInt(value);
-      }
-      return value;
-    });
+    // Parse with json-bigint which handles large numbers correctly
+    const parsed = JSONbig.parse(text);
+
+    // Convert any BigInt values to strings or numbers depending on size
+    const json = convertBigInts(parsed);
     return json as unknown;
   } catch (err) {
     return null;
   }
 };
+
+// Recursively convert BigInt values to appropriate types
+function convertBigInts(obj: any): any {
+  if (obj === null || obj === undefined) {
+    return obj;
+  }
+
+  if (typeof obj === 'bigint') {
+    // If it fits in a safe number, convert to number for math operations
+    // Otherwise keep as string for display
+    if (obj <= Number.MAX_SAFE_INTEGER && obj >= Number.MIN_SAFE_INTEGER) {
+      return Number(obj);
+    }
+    return obj.toString();
+  }
+
+  if (Array.isArray(obj)) {
+    return obj.map(convertBigInts);
+  }
+
+  if (typeof obj === 'object') {
+    const result: any = {};
+    for (const key in obj) {
+      if (Object.prototype.hasOwnProperty.call(obj, key)) {
+        result[key] = convertBigInts(obj[key]);
+      }
+    }
+    return result;
+  }
+
+  return obj;
+}
diff --git a/web/src/lib/components/Modals/InspectMessage.svelte b/web/src/lib/components/Modals/InspectMessage.svelte
index c80035f4a..210468fc2 100644
--- a/web/src/lib/components/Modals/InspectMessage.svelte
+++ b/web/src/lib/components/Modals/InspectMessage.svelte
@@ -4,6 +4,7 @@
   import { type Message, type HeaderField } from '$lib/domain/Message';
   import MessageDecoder from '$lib/components/MessageDecoder/MessageDecoder.svelte';
   import { decodeBase64 } from '$lib/utils/base64Utils';
+  import { formatMessageId } from '$lib/utils/formatters/uuidFormatter';
 
   interface Props {
     closeModal: CloseModalFn;
@@ -12,6 +13,8 @@
 
   let { closeModal, message }: Props = $props();
 
+  const formattedId = $derived(message?.id ? formatMessageId(message.id) : 'N/A');
+
   const formatHeaders = (headers: Record<string, HeaderField> | null | undefined) => {
     if (!headers || Object.keys(headers).length === 0) {
       return 'No headers';
@@ -36,7 +39,14 @@
     <div class="flex flex-col gap-4">
       <div class="bg-shade-l200 dark:bg-shade-d400 p-3 lg:p-4 rounded-md">
         <span class="text-xs text-shade-l900 dark:text-shade-l700 block mb-1">ID</span>
-        <div class="text-sm text-color font-medium">{message?.id ?? 'N/A'}</div>
+        <div class="text-sm text-color font-medium font-mono break-all">
+          {formattedId}
+          {#if formattedId !== message?.id && message?.id}
+            <span class="text-xs text-shade-l900 dark:text-shade-l700 block mt-1">
+              Raw: {message.id}
+            </span>
+          {/if}
+        </div>
       </div>
 
       <div class="bg-shade-l200 dark:bg-shade-d400 p-3 lg:p-4 rounded-md">
diff --git a/web/src/lib/domain/Message.ts b/web/src/lib/domain/Message.ts
index e0099deee..d4d775ef4 100644
--- a/web/src/lib/domain/Message.ts
+++ b/web/src/lib/domain/Message.ts
@@ -26,11 +26,11 @@ export type MessagePartition = {
 };
 
 export type Message = {
-  checksum: number;
-  id: number;
-  offset: number;
-  timestamp: number;
-  origin_timestamp: number;
+  checksum: string;
+  id: string;
+  offset: string;
+  timestamp: string;
+  origin_timestamp: string;
   user_headers_length: number;
   payload_length: number;
   formattedTimestamp: string;
@@ -47,14 +47,28 @@ export type HeaderField = {
 export function messageMapper(item: any): Message {
   const payload = item.payload;
   const truncatedPayload = payload.length > 30 ? `${payload.slice(0, 30)} [...]` : payload;
-  const formattedTimestamp = formatDateWithMicroseconds(item.header.timestamp);
+
+  // Convert all header numbers to strings to preserve precision
+  // getJson() returns numbers for values <= MAX_SAFE_INTEGER, strings for larger values
+  const checksum = String(item.header.checksum);
+  const id = String(item.header.id);
+  const offset = String(item.header.offset);
+  const timestamp = String(item.header.timestamp);
+  const origin_timestamp = String(item.header.origin_timestamp);
+
+  // Use numeric value for date formatting (getJson already converted to number if safe)
+  const timestampNum =
+    typeof item.header.timestamp === 'number'
+      ? item.header.timestamp
+      : Number(item.header.timestamp);
+  const formattedTimestamp = formatDateWithMicroseconds(timestampNum);
 
   return {
-    checksum: item.header.checksum,
-    id: item.header.id,
-    offset: item.header.offset,
-    timestamp: item.header.timestamp,
-    origin_timestamp: item.header.origin_timestamp,
+    checksum,
+    id,
+    offset,
+    timestamp,
+    origin_timestamp,
     user_headers_length: item.header.user_headers_length,
     payload_length: item.header.payload_length,
     formattedTimestamp: formattedTimestamp,
diff --git a/web/src/lib/utils/formatters/uuidFormatter.ts b/web/src/lib/utils/formatters/uuidFormatter.ts
new file mode 100644
index 000000000..59a8b2b43
--- /dev/null
+++ b/web/src/lib/utils/formatters/uuidFormatter.ts
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Checks if a u128 value looks like it could be a UUID
+ * UUIDs typically have specific bit patterns, but for simplicity
+ * we check if the value is within a reasonable range for UUIDs
+ */
+export function looksLikeUuid(id: string): boolean {
+  try {
+    const num = BigInt(id);
+    // Check if the number is large enough to potentially be a UUID
+    // UUIDs are 128-bit, so they're typically very large numbers
+    // We'll consider anything > u64::MAX as potentially a UUID
+    const u64Max = BigInt('18446744073709551615');
+    return num > u64Max;
+  } catch {
+    return false;
+  }
+}
+
+/**
+ * Formats a u128 number as a UUID string (8-4-4-4-12 format)
+ * Example: 340282366920938463463374607431768211455 -> ffffffff-ffff-ffff-ffff-ffffffffffff
+ */
+export function formatAsUuid(id: string): string {
+  try {
+    const num = BigInt(id);
+    // Convert to hex and pad to 32 characters (128 bits = 32 hex chars)
+    const hex = num.toString(16).padStart(32, '0');
+
+    // Format as UUID: 8-4-4-4-12
+    return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
+  } catch {
+    return id;
+  }
+}
+
+/**
+ * Formats a message ID intelligently:
+ * - If it looks like a UUID, formats it as UUID
+ * - Otherwise returns the raw string
+ */
+export function formatMessageId(id: string): string {
+  if (looksLikeUuid(id)) {
+    return formatAsUuid(id);
+  }
+  return id;
+}