diff --git a/app/layout.tsx b/app/layout.tsx
index cd2e9ef..26164e1 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -1,5 +1,7 @@
import type { Metadata } from "next";
+import Script from "next/script";
import { AppShell } from "@/components/app-shell";
+import { randomUUIDPolyfillScript } from "@/lib/client/random-uuid-polyfill";
import "./globals.css";
export const metadata: Metadata = {
@@ -11,6 +13,11 @@ export default function RootLayout({ children }: { children: React.ReactNode })
return (
+
{children}
diff --git a/lib/client/random-uuid-polyfill.ts b/lib/client/random-uuid-polyfill.ts
new file mode 100644
index 0000000..1d84910
--- /dev/null
+++ b/lib/client/random-uuid-polyfill.ts
@@ -0,0 +1,56 @@
+export const randomUUIDPolyfillScript = `
+(function () {
+ var root = typeof globalThis !== "undefined" ? globalThis : window;
+ var cryptoObject = root.crypto || root.msCrypto;
+ if (cryptoObject && typeof cryptoObject.randomUUID === "function") return;
+ if (!cryptoObject) {
+ cryptoObject = {};
+ try {
+ Object.defineProperty(root, "crypto", {
+ configurable: true,
+ value: cryptoObject
+ });
+ } catch (error) {
+ root.crypto = cryptoObject;
+ }
+ }
+
+ var getRandomValues = typeof cryptoObject.getRandomValues === "function"
+ ? cryptoObject.getRandomValues.bind(cryptoObject)
+ : null;
+
+ function byteToHex(byte) {
+ return (byte + 256).toString(16).slice(1);
+ }
+
+ function createRandomUUID() {
+ var bytes = new Uint8Array(16);
+ if (getRandomValues) {
+ getRandomValues(bytes);
+ } else {
+ for (var index = 0; index < bytes.length; index += 1) {
+ bytes[index] = Math.floor(Math.random() * 256);
+ }
+ }
+ bytes[6] = (bytes[6] & 15) | 64;
+ bytes[8] = (bytes[8] & 63) | 128;
+ return (
+ byteToHex(bytes[0]) + byteToHex(bytes[1]) + byteToHex(bytes[2]) + byteToHex(bytes[3]) + "-" +
+ byteToHex(bytes[4]) + byteToHex(bytes[5]) + "-" +
+ byteToHex(bytes[6]) + byteToHex(bytes[7]) + "-" +
+ byteToHex(bytes[8]) + byteToHex(bytes[9]) + "-" +
+ byteToHex(bytes[10]) + byteToHex(bytes[11]) + byteToHex(bytes[12]) +
+ byteToHex(bytes[13]) + byteToHex(bytes[14]) + byteToHex(bytes[15])
+ );
+ }
+
+ try {
+ Object.defineProperty(cryptoObject, "randomUUID", {
+ configurable: true,
+ value: createRandomUUID
+ });
+ } catch (error) {
+ cryptoObject.randomUUID = createRandomUUID;
+ }
+})();
+`;
diff --git a/tests/random-uuid-polyfill.test.ts b/tests/random-uuid-polyfill.test.ts
new file mode 100644
index 0000000..1825788
--- /dev/null
+++ b/tests/random-uuid-polyfill.test.ts
@@ -0,0 +1,32 @@
+import { describe, expect, it } from "vitest";
+import vm from "node:vm";
+import { randomUUIDPolyfillScript } from "@/lib/client/random-uuid-polyfill";
+
+describe("client randomUUID polyfill", () => {
+ it("defines crypto.randomUUID when crypto is missing", () => {
+ const context = {};
+
+ vm.runInNewContext(randomUUIDPolyfillScript, context);
+
+ expect(context.crypto.randomUUID()).toMatch(
+ /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/
+ );
+ });
+
+ it("defines crypto.randomUUID when only getRandomValues is available", () => {
+ const context = {
+ crypto: {
+ getRandomValues(bytes: Uint8Array) {
+ for (let index = 0; index < bytes.length; index += 1) bytes[index] = index;
+ return bytes;
+ }
+ }
+ };
+
+ vm.runInNewContext(randomUUIDPolyfillScript, context);
+
+ expect(context.crypto.randomUUID()).toMatch(
+ /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/
+ );
+ });
+});