Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/components/examples/ExampleWorkbench.client.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react'
import { safeRandomUUID } from '~/utils/crypto.client'
import {
ArrowClockwiseIcon,
BrowserIcon,
Expand Down Expand Up @@ -419,7 +420,7 @@ export function ExampleWorkbench({
const environmentSnapshotRef = React.useRef<
ExampleEnvironmentSnapshot | undefined
>(undefined)
const browserChannelRef = React.useRef(crypto.randomUUID())
const browserChannelRef = React.useRef(safeRandomUUID())
const compileRequestRef = React.useRef(0)
const nextConsoleIdRef = React.useRef(0)
const nextTerminalIdRef = React.useRef(1)
Expand Down Expand Up @@ -785,7 +786,7 @@ export function ExampleWorkbench({
})
compileRequestRef.current += 1
cancelPreviewCapture('The preview changed before capture completed.')
browserChannelRef.current = crypto.randomUUID()
browserChannelRef.current = safeRandomUUID()
webContainerSessionRef.current?.dispose()
webContainerSessionRef.current = null
setWebContainerSession(undefined)
Expand Down Expand Up @@ -1032,7 +1033,7 @@ export function ExampleWorkbench({
handledRunDefinitionRef.current = definition

if (signal?.aborted) {
const runId = crypto.randomUUID()
const runId = safeRandomUUID()
return Promise.resolve({
ok: false,
phase: 'aborted',
Expand Down Expand Up @@ -1103,7 +1104,7 @@ export function ExampleWorkbench({

const request = compileRequestRef.current + 1
compileRequestRef.current = request
const runToken = crypto.randomUUID()
const runToken = safeRandomUUID()
runTokenRef.current = runToken
environmentSnapshotRef.current = createEmptyExampleEnvironmentSnapshot({
runId: runToken,
Expand Down Expand Up @@ -2326,7 +2327,7 @@ export function ExampleWorkbench({

function capturePreview(tabId?: string) {
cancelPreviewCapture('A newer screenshot replaced this capture.')
const requestId = crypto.randomUUID()
const requestId = safeRandomUUID()

return new Promise<Blob>((resolve, reject) => {
const timeout = window.setTimeout(() => {
Expand Down
5 changes: 3 additions & 2 deletions src/components/examples/SandboxBrowser.client.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react'
import { safeRandomUUID } from '~/utils/crypto.client'
import {
ArrowClockwiseIcon,
ArrowLeftIcon,
Expand Down Expand Up @@ -296,7 +297,7 @@ export function SandboxBrowser({
const note = annotationNote.trim()
if (!note) return
const annotation = {
id: crypto.randomUUID(),
id: safeRandomUUID(),
note,
target: annotationTarget,
} satisfies SandboxBrowserAnnotation
Expand Down Expand Up @@ -418,7 +419,7 @@ export function SandboxBrowser({
function submitCurrentAnnotation() {
if (!annotationTarget) return
const annotation = {
id: crypto.randomUUID(),
id: safeRandomUUID(),
note: annotationNote.trim(),
target: annotationTarget,
} satisfies SandboxBrowserAnnotation
Expand Down
22 changes: 22 additions & 0 deletions src/utils/crypto.client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* A safe wrapper around crypto.randomUUID() that works in non-secure contexts
* (e.g. HTTP) and older browsers that do not implement randomUUID.
*
* Falls back to crypto.getRandomValues when available, then to a
* Date.now() + Math.random() string as a last resort.
*/
export function safeRandomUUID(): string {
const c = globalThis.crypto

if (c?.randomUUID) {
return c.randomUUID()
}

if (c?.getRandomValues) {
const values = new Uint32Array(4)
c.getRandomValues(values)
return Array.from(values, (v) => v.toString(16).padStart(8, '0')).join('-')
}

return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`
}
Loading