diff --git a/apps/sim/app/chat/[identifier]/chat.tsx b/apps/sim/app/chat/[identifier]/chat.tsx index 549e450d4a5..e19e4295fd0 100644 --- a/apps/sim/app/chat/[identifier]/chat.tsx +++ b/apps/sim/app/chat/[identifier]/chat.tsx @@ -1,4 +1,5 @@ 'use client' +import { generateId } from '../../lib/utils/uuid' import { type RefObject, useCallback, useEffect, useRef, useState } from 'react' import { createLogger } from '@sim/logger' @@ -303,7 +304,7 @@ export default function ChatClient({ identifier }: { identifier: string }) { setUserHasScrolled(false) const userMessage: ChatMessage = { - id: crypto.randomUUID(), + id: generateId(), content: messageToSend || (files && files.length > 0 ? `Sent ${files.length} file(s)` : ''), type: 'user', timestamp: new Date(), @@ -416,7 +417,7 @@ export default function ChatClient({ identifier }: { identifier: string }) { logger.error('Error sending message:', error) setIsLoading(false) const errorMessage: ChatMessage = { - id: crypto.randomUUID(), + id: generateId(), content: CHAT_ERROR_MESSAGES.GENERIC_ERROR, type: 'assistant', timestamp: new Date(), diff --git a/apps/sim/app/chat/components/input/input.tsx b/apps/sim/app/chat/components/input/input.tsx index 5e385d7f3a9..f663b164ecf 100644 --- a/apps/sim/app/chat/components/input/input.tsx +++ b/apps/sim/app/chat/components/input/input.tsx @@ -1,4 +1,5 @@ 'use client' +import { generateId } from '../../lib/utils/uuid' import type React from 'react' import { useEffect, useRef, useState } from 'react' @@ -147,7 +148,7 @@ export const ChatInput: React.FC<{ } newFiles.push({ - id: crypto.randomUUID(), + id: generateId(), name: file.name, size: file.size, type: file.type, diff --git a/apps/sim/app/chat/hooks/use-chat-streaming.ts b/apps/sim/app/chat/hooks/use-chat-streaming.ts index e0208709311..a8882ad16a2 100644 --- a/apps/sim/app/chat/hooks/use-chat-streaming.ts +++ b/apps/sim/app/chat/hooks/use-chat-streaming.ts @@ -1,4 +1,5 @@ 'use client' +import { generateId } from '../../../lib/utils/uuid' import { useRef, useState } from 'react' import { createLogger } from '@sim/logger' @@ -141,7 +142,7 @@ export function useChatStreaming() { // Track which blocks have streamed content (like chat panel) const messageIdMap = new Map() - const messageId = crypto.randomUUID() + const messageId = generateId() setMessages((prev) => [ ...prev, { diff --git a/apps/sim/app/workspace/[workspaceId]/providers/global-commands-provider.tsx b/apps/sim/app/workspace/[workspaceId]/providers/global-commands-provider.tsx index e4cbb443dff..171930956e1 100644 --- a/apps/sim/app/workspace/[workspaceId]/providers/global-commands-provider.tsx +++ b/apps/sim/app/workspace/[workspaceId]/providers/global-commands-provider.tsx @@ -1,4 +1,5 @@ 'use client' +import { generateId } from '../../lib/utils/uuid' import { createContext, @@ -87,7 +88,7 @@ export function GlobalCommandsProvider({ children }: { children: ReactNode }) { const register = useCallback((commands: GlobalCommand[]) => { const createdIds: string[] = [] for (const cmd of commands) { - const id = cmd.id ?? crypto.randomUUID() + const id = cmd.id ?? generateId() const parsed = parseShortcut(cmd.shortcut) registryRef.current.set(id, { ...cmd, diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/chat.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/chat.tsx index 35ec02c352f..5e1e85e2e65 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/chat.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/chat.tsx @@ -1,4 +1,5 @@ 'use client' +import { generateId } from '../../lib/utils/uuid' import { type KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { createLogger } from '@sim/logger' @@ -601,7 +602,7 @@ export function Chat() { if (typeof result !== 'object') return if ('stream' in result && result.stream instanceof ReadableStream) { - const responseMessageId = crypto.randomUUID() + const responseMessageId = generateId() addMessage({ id: responseMessageId, content: '', @@ -799,7 +800,7 @@ export function Chat() { const defaultType = fieldName === 'files' ? 'file[]' : 'string' return { - id: crypto.randomUUID(), + id: generateId(), name: fieldName, type: defaultType, value: '', @@ -814,7 +815,7 @@ export function Chat() { const userId = session?.user?.id || 'unknown' addToQueue({ - id: crypto.randomUUID(), + id: generateId(), operation: { operation: 'subblock-update', target: 'subblock', diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/hooks/use-chat-file-upload.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/hooks/use-chat-file-upload.ts index 9ef57824cf5..0d877963a32 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/hooks/use-chat-file-upload.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/hooks/use-chat-file-upload.ts @@ -1,3 +1,4 @@ +import { generateId } from '../../../../../../../../lib/utils/uuid' import { useCallback, useState } from 'react' export interface ChatFile { @@ -53,7 +54,7 @@ export function useChatFileUpload() { } validNewFiles.push({ - id: crypto.randomUUID(), + id: generateId(), name: file.name, size: file.size, type: file.type, diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-file-attachments.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-file-attachments.ts index 7587f69a9c1..e15b06102d4 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-file-attachments.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-file-attachments.ts @@ -1,4 +1,5 @@ 'use client' +import { generateId } from '../../../../../../../../../../../../lib/utils/uuid' import { useCallback, useEffect, useRef, useState } from 'react' import { createLogger } from '@sim/logger' @@ -123,7 +124,7 @@ export function useFileAttachments(props: UseFileAttachmentsProps) { } const tempFile: AttachedFile = { - id: crypto.randomUUID(), + id: generateId(), name: file.name, size: file.size, type: file.type, diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/a2a/a2a.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/a2a/a2a.tsx index 50bcc9c6a8e..1c907ee0ef9 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/a2a/a2a.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/a2a/a2a.tsx @@ -1,4 +1,5 @@ 'use client' +import { generateId } from '../../lib/utils/uuid' import { useCallback, useEffect, useMemo, useState } from 'react' import { createLogger } from '@sim/logger' @@ -155,7 +156,7 @@ export function A2aDeploy({ // Add input field if missing (for TextPart) if (missingFields.input) { newFields.push({ - id: crypto.randomUUID(), + id: generateId(), name: 'input', type: 'string', value: '', @@ -166,7 +167,7 @@ export function A2aDeploy({ // Add data field if missing (for DataPart) if (missingFields.data) { newFields.push({ - id: crypto.randomUUID(), + id: generateId(), name: 'data', type: 'object', value: '', @@ -177,7 +178,7 @@ export function A2aDeploy({ // Add files field if missing (for FilePart) if (missingFields.files) { newFields.push({ - id: crypto.randomUUID(), + id: generateId(), name: 'files', type: 'file[]', value: '', diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/document-tag-entry/document-tag-entry.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/document-tag-entry/document-tag-entry.tsx index 405599aa9a5..362c311124c 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/document-tag-entry/document-tag-entry.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/document-tag-entry/document-tag-entry.tsx @@ -1,4 +1,5 @@ 'use client' +import { generateId } from '../../lib/utils/uuid' import { useMemo, useRef } from 'react' import { Plus } from 'lucide-react' @@ -45,7 +46,7 @@ interface DocumentTagEntryProps { * Creates a new document tag with default values */ const createDefaultTag = (): DocumentTag => ({ - id: crypto.randomUUID(), + id: generateId(), tagName: '', fieldType: 'text', value: '', diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/dropdown/dropdown.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/dropdown/dropdown.tsx index 35546381950..005bf856f54 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/dropdown/dropdown.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/dropdown/dropdown.tsx @@ -1,3 +1,4 @@ +import { generateId } from '../../../../../../../../../../../../../lib/utils/uuid' import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react' import isEqual from 'lodash/isEqual' import { useStoreWithEqualityFn } from 'zustand/traditional' @@ -276,7 +277,7 @@ export const Dropdown = memo(function Dropdown({ fieldType === 'object' || fieldType === 'array' ? JSON.stringify(value, null, 2) : value return { - id: crypto.randomUUID(), + id: generateId(), name: key, type: fieldType, value: fieldValue, diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/eval-input/eval-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/eval-input/eval-input.tsx index f4bc3f68ff1..1e08c996e4c 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/eval-input/eval-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/eval-input/eval-input.tsx @@ -1,3 +1,4 @@ +import { generateId } from '../../../../../../../../../../../../../lib/utils/uuid' import { useMemo, useRef } from 'react' import { Plus } from 'lucide-react' import { Button, Input, Textarea, Tooltip } from '@/components/emcn' @@ -30,7 +31,7 @@ interface EvalInputProps { // Default values const createDefaultMetric = (): EvalMetric => ({ - id: crypto.randomUUID(), + id: generateId(), name: '', description: '', range: { min: 0, max: 1 }, diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/filter-builder/filter-builder.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/filter-builder/filter-builder.tsx index 4703b7f41a1..e5df6ac1545 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/filter-builder/filter-builder.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/filter-builder/filter-builder.tsx @@ -1,4 +1,5 @@ 'use client' +import { generateId } from '../../lib/utils/uuid' import { useCallback, useMemo } from 'react' import type { ComboboxOption } from '@/components/emcn' @@ -20,7 +21,7 @@ interface FilterBuilderProps { } const createDefaultRule = (columns: ComboboxOption[]): FilterRule => ({ - id: crypto.randomUUID(), + id: generateId(), logicalOperator: 'and', column: columns[0]?.value || '', operator: 'eq', diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/knowledge-tag-filters/knowledge-tag-filters.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/knowledge-tag-filters/knowledge-tag-filters.tsx index 2d36e69de5e..608ead34d96 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/knowledge-tag-filters/knowledge-tag-filters.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/knowledge-tag-filters/knowledge-tag-filters.tsx @@ -1,4 +1,5 @@ 'use client' +import { generateId } from '../../lib/utils/uuid' import { useRef } from 'react' import { Plus } from 'lucide-react' @@ -48,7 +49,7 @@ interface KnowledgeTagFiltersProps { * Creates a new filter with default values */ const createDefaultFilter = (): TagFilter => ({ - id: crypto.randomUUID(), + id: generateId(), tagName: '', fieldType: 'text', operator: 'eq', diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/sort-builder/sort-builder.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/sort-builder/sort-builder.tsx index 89705d40bbb..b0899b00d4a 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/sort-builder/sort-builder.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/sort-builder/sort-builder.tsx @@ -1,4 +1,5 @@ 'use client' +import { generateId } from '../../lib/utils/uuid' import { useCallback, useMemo } from 'react' import type { ComboboxOption } from '@/components/emcn' @@ -18,7 +19,7 @@ interface SortBuilderProps { } const createDefaultRule = (columns: ComboboxOption[]): SortRule => ({ - id: crypto.randomUUID(), + id: generateId(), column: columns[0]?.value || '', direction: 'asc', collapsed: false, diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/starter/input-format.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/starter/input-format.tsx index 0d8f69be82e..844b7fd64be 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/starter/input-format.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/starter/input-format.tsx @@ -1,3 +1,4 @@ +import { generateId } from '../../../../../../../../../../../../../lib/utils/uuid' import { useCallback, useRef } from 'react' import { Plus } from 'lucide-react' import { Trash } from '@/components/emcn/icons/trash' @@ -72,7 +73,7 @@ const BOOLEAN_OPTIONS: ComboboxOption[] = [ * Creates a new field with default values */ const createDefaultField = (): Field => ({ - id: crypto.randomUUID(), + id: generateId(), name: '', type: 'string', value: '', diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/table/table.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/table/table.tsx index 7cf9c71c725..c38cfabd9cb 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/table/table.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/table/table.tsx @@ -1,3 +1,4 @@ +import { generateId } from '../../../../../../../../../../../../../lib/utils/uuid' import { useEffect, useMemo, useRef } from 'react' import { createLogger } from '@sim/logger' import { useParams } from 'next/navigation' @@ -74,7 +75,7 @@ export function Table({ useEffect(() => { if (!isPreview && !disabled && (!Array.isArray(storeValue) || storeValue.length === 0)) { const initialRow: WorkflowTableRow = { - id: crypto.randomUUID(), + id: generateId(), cells: { ...emptyCellsTemplate }, } setStoreValue([initialRow]) @@ -86,7 +87,7 @@ export function Table({ if (!Array.isArray(value) || value.length === 0) { return [ { - id: crypto.randomUUID(), + id: generateId(), cells: { ...emptyCellsTemplate }, }, ] @@ -105,7 +106,7 @@ export function Table({ } return { - id: row?.id ?? crypto.randomUUID(), + id: row?.id ?? generateId(), cells: normalizedCells, } }) @@ -133,7 +134,7 @@ export function Table({ if (rowIndex === rows.length - 1 && newValue !== '') { updatedRows.push({ - id: crypto.randomUUID(), + id: generateId(), cells: { ...emptyCellsTemplate }, }) } diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/variables-input/variables-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/variables-input/variables-input.tsx index 3ca20f572f0..9df30bd9080 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/variables-input/variables-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/variables-input/variables-input.tsx @@ -1,3 +1,4 @@ +import { generateId } from '../../../../../../../../../../../../../lib/utils/uuid' import { useEffect, useRef, useState } from 'react' import { Plus } from 'lucide-react' import { useParams } from 'next/navigation' @@ -124,7 +125,7 @@ export function VariablesInput({ if (!isReadOnly && assignments.length === 0 && currentWorkflowVariables.length > 0) { const initialAssignment: VariableAssignment = { ...DEFAULT_ASSIGNMENT, - id: crypto.randomUUID(), + id: generateId(), } setStoreValue([initialAssignment]) } @@ -151,7 +152,7 @@ export function VariablesInput({ const newAssignment: VariableAssignment = { ...DEFAULT_ASSIGNMENT, - id: crypto.randomUUID(), + id: generateId(), } setStoreValue([...assignments, newAssignment]) } @@ -160,7 +161,7 @@ export function VariablesInput({ if (isReadOnly) return if (assignments.length === 1) { - setStoreValue([{ ...DEFAULT_ASSIGNMENT, id: crypto.randomUUID() }]) + setStoreValue([{ ...DEFAULT_ASSIGNMENT, id: generateId() }]) return } diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx index 577af370cb3..6b3e3540d69 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx @@ -1,4 +1,5 @@ 'use client' +import { generateId } from '../../lib/utils/uuid' import React, { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useParams, useRouter } from 'next/navigation' @@ -1541,7 +1542,7 @@ const WorkflowContent = React.memo(() => { const createEdgeObject = useCallback( (sourceId: string, targetId: string, sourceHandle: string): Edge => { const edge = { - id: crypto.randomUUID(), + id: generateId(), source: sourceId, target: targetId, sourceHandle, @@ -1718,7 +1719,7 @@ const WorkflowContent = React.memo(() => { clearDragHighlights() if (data.type === 'loop' || data.type === 'parallel') { - const id = crypto.randomUUID() + const id = generateId() const baseName = data.type === 'loop' ? 'Loop' : 'Parallel' const name = getUniqueBlockName(baseName, blocks) @@ -1797,7 +1798,7 @@ const WorkflowContent = React.memo(() => { } // Generate id and name here so they're available in all code paths - const id = crypto.randomUUID() + const id = generateId() // Prefer semantic default names for triggers; then ensure unique numbering centrally const defaultTriggerNameDrop = TriggerUtils.getDefaultTriggerName(data.type) const baseName = defaultTriggerNameDrop || blockConfig.name @@ -1916,7 +1917,7 @@ const WorkflowContent = React.memo(() => { const basePosition = getViewportCenter() if (type === 'loop' || type === 'parallel') { - const id = crypto.randomUUID() + const id = generateId() const baseName = type === 'loop' ? 'Loop' : 'Parallel' const name = getUniqueBlockName(baseName, blocks) @@ -1950,7 +1951,7 @@ const WorkflowContent = React.memo(() => { if (checkTriggerConstraints(type)) return - const id = crypto.randomUUID() + const id = generateId() const defaultTriggerName = TriggerUtils.getDefaultTriggerName(type) const baseName = defaultTriggerName || blockConfig.name const name = getUniqueBlockName(baseName, blocks) @@ -2887,7 +2888,7 @@ const WorkflowContent = React.memo(() => { const targetParentId = blocks[targetNode.id]?.data?.parentId // Generate a unique edge ID - const edgeId = crypto.randomUUID() + const edgeId = generateId() // Special case for container start source: Always allow connections to nodes within the same container if ( diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/credit-balance/credit-balance.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/credit-balance/credit-balance.tsx index 34cb0f3868d..1f266f3bf69 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/credit-balance/credit-balance.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/credit-balance/credit-balance.tsx @@ -1,4 +1,5 @@ 'use client' +import { generateId } from '../../lib/utils/uuid' import { useState } from 'react' import { createLogger } from '@sim/logger' @@ -95,7 +96,7 @@ export function CreditBalance({ const handleOpenChange = (open: boolean) => { setIsOpen(open) if (open) { - setRequestId(crypto.randomUUID()) + setRequestId(generateId()) } else { setAmount('') setError(null) diff --git a/apps/sim/app/workspace/[workspaceId]/w/hooks/use-import-workflow.ts b/apps/sim/app/workspace/[workspaceId]/w/hooks/use-import-workflow.ts index b04db85173d..e257a6a6441 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/hooks/use-import-workflow.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/hooks/use-import-workflow.ts @@ -1,3 +1,4 @@ +import { generateId } from '../../../../../lib/utils/uuid' import { useCallback, useState } from 'react' import { createLogger } from '@sim/logger' import { useQueryClient } from '@tanstack/react-query' @@ -87,7 +88,7 @@ export function useImportWorkflow({ workspaceId }: UseImportWorkflowProps) { > = {} for (const v of variablesArray) { - const id = typeof v.id === 'string' && v.id.trim() ? v.id : crypto.randomUUID() + const id = typeof v.id === 'string' && v.id.trim() ? v.id : generateId() variablesRecord[id] = { id, workflowId: newWorkflowId, diff --git a/apps/sim/app/workspace/[workspaceId]/w/hooks/use-import-workspace.ts b/apps/sim/app/workspace/[workspaceId]/w/hooks/use-import-workspace.ts index 06d0cfa20ff..034851d695d 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/hooks/use-import-workspace.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/hooks/use-import-workspace.ts @@ -1,3 +1,4 @@ +import { generateId } from '../../../../../lib/utils/uuid' import { useCallback, useState } from 'react' import { createLogger } from '@sim/logger' import { useRouter } from 'next/navigation' @@ -205,7 +206,7 @@ export function useImportWorkspace({ onSuccess }: UseImportWorkspaceProps = {}) > = {} for (const v of variablesArray) { - const id = typeof v.id === 'string' && v.id.trim() ? v.id : crypto.randomUUID() + const id = typeof v.id === 'string' && v.id.trim() ? v.id : generateId() variablesRecord[id] = { id, workflowId: newWorkflow.id, diff --git a/apps/sim/app/workspace/providers/socket-provider.tsx b/apps/sim/app/workspace/providers/socket-provider.tsx index 3cebcaa5729..581e5d95071 100644 --- a/apps/sim/app/workspace/providers/socket-provider.tsx +++ b/apps/sim/app/workspace/providers/socket-provider.tsx @@ -1,4 +1,5 @@ 'use client' +import { generateId } from '../../lib/utils/uuid' import { createContext, @@ -25,7 +26,7 @@ function getTabSessionId(): string { let tabSessionId = sessionStorage.getItem(TAB_SESSION_ID_KEY) if (!tabSessionId) { - tabSessionId = crypto.randomUUID() + tabSessionId = generateId() sessionStorage.setItem(TAB_SESSION_ID_KEY, tabSessionId) } return tabSessionId diff --git a/apps/sim/blocks/blocks/telegram.ts b/apps/sim/blocks/blocks/telegram.ts index ce4076d3849..8fbdd84ca3a 100644 --- a/apps/sim/blocks/blocks/telegram.ts +++ b/apps/sim/blocks/blocks/telegram.ts @@ -270,6 +270,14 @@ export const TelegramBlock: BlockConfig = { } case 'telegram_send_photo': { // photo is the canonical param for both basic (photoFile) and advanced modes + // In advanced mode, photo can be a plain URL string or file_id + if (typeof params.photo === 'string' && params.photo.trim()) { + return { + ...commonParams, + photo: params.photo.trim(), + caption: params.caption, + } + } const photoSource = normalizeFileInput(params.photo, { single: true, }) @@ -284,6 +292,14 @@ export const TelegramBlock: BlockConfig = { } case 'telegram_send_video': { // video is the canonical param for both basic (videoFile) and advanced modes + // In advanced mode, video can be a plain URL string or file_id + if (typeof params.video === 'string' && params.video.trim()) { + return { + ...commonParams, + video: params.video.trim(), + caption: params.caption, + } + } const videoSource = normalizeFileInput(params.video, { single: true, }) @@ -298,6 +314,14 @@ export const TelegramBlock: BlockConfig = { } case 'telegram_send_audio': { // audio is the canonical param for both basic (audioFile) and advanced modes + // In advanced mode, audio can be a plain URL string or file_id + if (typeof params.audio === 'string' && params.audio.trim()) { + return { + ...commonParams, + audio: params.audio.trim(), + caption: params.caption, + } + } const audioSource = normalizeFileInput(params.audio, { single: true, }) @@ -312,6 +336,14 @@ export const TelegramBlock: BlockConfig = { } case 'telegram_send_animation': { // animation is the canonical param for both basic (animationFile) and advanced modes + // In advanced mode, animation can be a plain URL string or file_id + if (typeof params.animation === 'string' && params.animation.trim()) { + return { + ...commonParams, + animation: params.animation.trim(), + caption: params.caption, + } + } const animationSource = normalizeFileInput(params.animation, { single: true, }) diff --git a/apps/sim/executor/handlers/workflow/workflow-handler.ts b/apps/sim/executor/handlers/workflow/workflow-handler.ts index 4821d86029e..54a91e009a3 100644 --- a/apps/sim/executor/handlers/workflow/workflow-handler.ts +++ b/apps/sim/executor/handlers/workflow/workflow-handler.ts @@ -1,3 +1,4 @@ +import { randomUUID } from 'node:crypto' import { createLogger } from '@sim/logger' import { buildNextCallChain, validateCallChain } from '@/lib/execution/call-chain' import { snapshotService } from '@/lib/logs/execution/snapshot/service' @@ -81,7 +82,7 @@ export class WorkflowBlockHandler implements BlockHandler { // Unique ID per invocation — used to correlate child block events with this specific // workflow block execution, preventing cross-iteration child mixing in loop contexts. - const instanceId = crypto.randomUUID() + const instanceId = randomUUID() const childCallChain = buildNextCallChain(ctx.callChain || [], workflowId) const depthError = validateCallChain(childCallChain) diff --git a/apps/sim/hooks/queries/a2a/tasks.ts b/apps/sim/hooks/queries/a2a/tasks.ts index 1e4cd83163e..17818a5d1b1 100644 --- a/apps/sim/hooks/queries/a2a/tasks.ts +++ b/apps/sim/hooks/queries/a2a/tasks.ts @@ -1,3 +1,4 @@ +import { generateId } from '../../../lib/utils/uuid' /** * A2A Tasks React Query Hooks (v0.3) * @@ -70,7 +71,7 @@ export interface SendA2ATaskResponse { async function sendA2ATask(params: SendA2ATaskParams): Promise { const userMessage: Message = { kind: 'message', - messageId: crypto.randomUUID(), + messageId: generateId(), role: 'user', parts: [{ kind: 'text', text: params.message }], ...(params.taskId && { taskId: params.taskId }), @@ -85,7 +86,7 @@ async function sendA2ATask(params: SendA2ATaskParams): Promise { }, body: JSON.stringify({ jsonrpc: '2.0', - id: crypto.randomUUID(), + id: generateId(), method: A2A_METHODS.TASKS_GET, params: { id: params.taskId, @@ -224,7 +225,7 @@ async function cancelA2ATask(params: CancelA2ATaskParams): Promise { }, body: JSON.stringify({ jsonrpc: '2.0', - id: crypto.randomUUID(), + id: generateId(), method: A2A_METHODS.TASKS_CANCEL, params: { id: params.taskId, diff --git a/apps/sim/hooks/use-code-undo-redo.ts b/apps/sim/hooks/use-code-undo-redo.ts index d0edbb535ec..870f9397468 100644 --- a/apps/sim/hooks/use-code-undo-redo.ts +++ b/apps/sim/hooks/use-code-undo-redo.ts @@ -1,3 +1,4 @@ +import { generateId } from '../lib/utils/uuid' import { useCallback, useEffect, useMemo, useRef } from 'react' import { createLogger } from '@sim/logger' import { useShallow } from 'zustand/react/shallow' @@ -83,7 +84,7 @@ export function useCodeUndoRedo({ } useCodeUndoRedoStore.getState().push({ - id: crypto.randomUUID(), + id: generateId(), createdAt: Date.now(), workflowId: activeWorkflowId, blockId, @@ -128,7 +129,7 @@ export function useCodeUndoRedo({ } useCodeUndoRedoStore.getState().push({ - id: crypto.randomUUID(), + id: generateId(), createdAt: Date.now(), workflowId: activeWorkflowId, blockId, diff --git a/apps/sim/hooks/use-collaborative-workflow.ts b/apps/sim/hooks/use-collaborative-workflow.ts index db3ac1a6c66..af2c79267b2 100644 --- a/apps/sim/hooks/use-collaborative-workflow.ts +++ b/apps/sim/hooks/use-collaborative-workflow.ts @@ -1,3 +1,4 @@ +import { generateId } from '../lib/utils/uuid' import { useCallback, useEffect, useRef } from 'react' import { createLogger } from '@sim/logger' import type { Edge } from 'reactflow' @@ -666,7 +667,7 @@ export function useCollaborativeWorkflow() { return } - const operationId = crypto.randomUUID() + const operationId = generateId() addToQueue({ id: operationId, @@ -702,7 +703,7 @@ export function useCollaborativeWorkflow() { if (updates.length === 0) return - const operationId = crypto.randomUUID() + const operationId = generateId() addToQueue({ id: operationId, @@ -823,7 +824,7 @@ export function useCollaborativeWorkflow() { subBlockId: string newValue: any }) => { - const operationId = crypto.randomUUID() + const operationId = generateId() addToQueue({ id: operationId, operation: { @@ -879,7 +880,7 @@ export function useCollaborativeWorkflow() { if (validIds.length === 0) return - const operationId = crypto.randomUUID() + const operationId = generateId() addToQueue({ id: operationId, @@ -937,7 +938,7 @@ export function useCollaborativeWorkflow() { // Collect all edge IDs to remove const edgeIdsToRemove = updates.flatMap((u) => u.affectedEdges.map((e) => e.id)) if (edgeIdsToRemove.length > 0) { - const edgeOperationId = crypto.randomUUID() + const edgeOperationId = generateId() addToQueue({ id: edgeOperationId, operation: { @@ -962,7 +963,7 @@ export function useCollaborativeWorkflow() { undoRedo.recordBatchUpdateParent(batchUpdates) - const operationId = crypto.randomUUID() + const operationId = generateId() addToQueue({ id: operationId, operation: { @@ -1012,7 +1013,7 @@ export function useCollaborativeWorkflow() { return } - const operationId = crypto.randomUUID() + const operationId = generateId() addToQueue({ id: operationId, operation: { @@ -1050,7 +1051,7 @@ export function useCollaborativeWorkflow() { if (validIds.length === 0) return - const operationId = crypto.randomUUID() + const operationId = generateId() addToQueue({ id: operationId, @@ -1098,7 +1099,7 @@ export function useCollaborativeWorkflow() { if (validIds.length === 0) return - const operationId = crypto.randomUUID() + const operationId = generateId() addToQueue({ id: operationId, @@ -1137,7 +1138,7 @@ export function useCollaborativeWorkflow() { const newEdges = filterNewEdges(validEdges, currentEdges) if (newEdges.length === 0) return false - const operationId = crypto.randomUUID() + const operationId = generateId() addToQueue({ id: operationId, @@ -1194,7 +1195,7 @@ export function useCollaborativeWorkflow() { return false } - const operationId = crypto.randomUUID() + const operationId = generateId() addToQueue({ id: operationId, @@ -1232,7 +1233,7 @@ export function useCollaborativeWorkflow() { useSubBlockStore.getState().setValue(blockId, subblockId, value) if (activeWorkflowId) { - const operationId = crypto.randomUUID() + const operationId = generateId() addToQueue({ id: operationId, @@ -1295,7 +1296,7 @@ export function useCollaborativeWorkflow() { useSubBlockStore.getState().setValue(blockId, subblockId, value) // Use the operation queue but with immediate processing (no debouncing) - const operationId = crypto.randomUUID() + const operationId = generateId() addToQueue({ id: operationId, @@ -1542,7 +1543,7 @@ export function useCollaborativeWorkflow() { const collaborativeAddVariable = useCallback( (variableData: { name: string; type: any; value: any; workflowId: string }) => { - const id = crypto.randomUUID() + const id = generateId() // Optimistically add to local store first useVariablesStore.getState().addVariable(variableData, id) @@ -1626,7 +1627,7 @@ export function useCollaborativeWorkflow() { filteredEdges: edges.length - validEdges.length, }) - const operationId = crypto.randomUUID() + const operationId = generateId() addToQueue({ id: operationId, @@ -1717,7 +1718,7 @@ export function useCollaborativeWorkflow() { totalCount: allBlocksToRemove.size, }) - const operationId = crypto.randomUUID() + const operationId = generateId() addToQueue({ id: operationId, diff --git a/apps/sim/hooks/use-undo-redo.ts b/apps/sim/hooks/use-undo-redo.ts index 880af7c063f..f79d14e4434 100644 --- a/apps/sim/hooks/use-undo-redo.ts +++ b/apps/sim/hooks/use-undo-redo.ts @@ -1,3 +1,4 @@ +import { generateId } from '../lib/utils/uuid' import { useCallback } from 'react' import { createLogger } from '@sim/logger' @@ -58,7 +59,7 @@ export function useUndoRedo() { if (!activeWorkflowId || blockSnapshots.length === 0) return const operation: BatchAddBlocksOperation = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.BATCH_ADD_BLOCKS, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -71,7 +72,7 @@ export function useUndoRedo() { } const inverse: BatchRemoveBlocksOperation = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.BATCH_REMOVE_BLOCKS, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -104,7 +105,7 @@ export function useUndoRedo() { if (!activeWorkflowId || blockSnapshots.length === 0) return const operation: BatchRemoveBlocksOperation = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.BATCH_REMOVE_BLOCKS, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -117,7 +118,7 @@ export function useUndoRedo() { } const inverse: BatchAddBlocksOperation = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.BATCH_ADD_BLOCKS, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -152,7 +153,7 @@ export function useUndoRedo() { } const operation: BatchAddEdgesOperation = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.BATCH_ADD_EDGES, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -161,7 +162,7 @@ export function useUndoRedo() { } const inverse: BatchRemoveEdgesOperation = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.BATCH_REMOVE_EDGES, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -182,7 +183,7 @@ export function useUndoRedo() { if (!activeWorkflowId || edgeSnapshots.length === 0) return const operation: BatchRemoveEdgesOperation = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.BATCH_REMOVE_EDGES, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -193,7 +194,7 @@ export function useUndoRedo() { } const inverse: BatchAddEdgesOperation = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.BATCH_ADD_EDGES, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -225,7 +226,7 @@ export function useUndoRedo() { if (!activeWorkflowId || moves.length === 0) return const operation: BatchMoveBlocksOperation = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.BATCH_MOVE_BLOCKS, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -234,7 +235,7 @@ export function useUndoRedo() { } const inverse: BatchMoveBlocksOperation = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.BATCH_MOVE_BLOCKS, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -268,7 +269,7 @@ export function useUndoRedo() { if (!activeWorkflowId) return const operation: UpdateParentOperation = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.UPDATE_PARENT, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -284,7 +285,7 @@ export function useUndoRedo() { } const inverse: UpdateParentOperation = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.UPDATE_PARENT, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -326,7 +327,7 @@ export function useUndoRedo() { if (!activeWorkflowId || updates.length === 0) return const operation: BatchUpdateParentOperation = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.BATCH_UPDATE_PARENT, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -335,7 +336,7 @@ export function useUndoRedo() { } const inverse: BatchUpdateParentOperation = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.BATCH_UPDATE_PARENT, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -368,7 +369,7 @@ export function useUndoRedo() { if (!activeWorkflowId || blockIds.length === 0) return const operation: BatchToggleEnabledOperation = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.BATCH_TOGGLE_ENABLED, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -377,7 +378,7 @@ export function useUndoRedo() { } const inverse: BatchToggleEnabledOperation = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.BATCH_TOGGLE_ENABLED, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -398,7 +399,7 @@ export function useUndoRedo() { if (!activeWorkflowId || blockIds.length === 0) return const operation: BatchToggleHandlesOperation = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.BATCH_TOGGLE_HANDLES, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -407,7 +408,7 @@ export function useUndoRedo() { } const inverse: BatchToggleHandlesOperation = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.BATCH_TOGGLE_HANDLES, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -428,7 +429,7 @@ export function useUndoRedo() { if (!activeWorkflowId || blockIds.length === 0) return const operation: BatchToggleLockedOperation = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.BATCH_TOGGLE_LOCKED, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -437,7 +438,7 @@ export function useUndoRedo() { } const inverse: BatchToggleLockedOperation = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.BATCH_TOGGLE_LOCKED, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -469,7 +470,7 @@ export function useUndoRedo() { workflowId: activeWorkflowId, }) - const opId = crypto.randomUUID() + const opId = generateId() switch (entry.inverse.type) { case UNDO_REDO_OPERATIONS.BATCH_REMOVE_BLOCKS: { @@ -636,7 +637,7 @@ export function useUndoRedo() { ) if (edgesToAdd.length > 0) { addToQueue({ - id: crypto.randomUUID(), + id: generateId(), operation: { operation: EDGES_OPERATIONS.BATCH_ADD_EDGES, target: OPERATION_TARGETS.EDGES, @@ -650,7 +651,7 @@ export function useUndoRedo() { } addToQueue({ - id: crypto.randomUUID(), + id: generateId(), operation: { operation: BLOCK_OPERATIONS.UPDATE_POSITION, target: OPERATION_TARGETS.BLOCK, @@ -702,7 +703,7 @@ export function useUndoRedo() { useWorkflowStore.getState().batchRemoveEdges(edgeIdsToRemove) edgeIdsToRemove.forEach((edgeId) => { addToQueue({ - id: crypto.randomUUID(), + id: generateId(), operation: { operation: EDGE_OPERATIONS.REMOVE, target: OPERATION_TARGETS.EDGE, @@ -756,7 +757,7 @@ export function useUndoRedo() { // Apply edge operations in batch if (allEdgesToAdd.length > 0) { addToQueue({ - id: crypto.randomUUID(), + id: generateId(), operation: { operation: EDGES_OPERATIONS.BATCH_ADD_EDGES, target: OPERATION_TARGETS.EDGES, @@ -771,7 +772,7 @@ export function useUndoRedo() { if (allEdgeIdsToRemove.length > 0) { useWorkflowStore.getState().batchRemoveEdges(allEdgeIdsToRemove) addToQueue({ - id: crypto.randomUUID(), + id: generateId(), operation: { operation: EDGES_OPERATIONS.BATCH_REMOVE_EDGES, target: OPERATION_TARGETS.EDGES, @@ -1084,7 +1085,7 @@ export function useUndoRedo() { return } - const opId = crypto.randomUUID() + const opId = generateId() switch (entry.operation.type) { case UNDO_REDO_OPERATIONS.BATCH_ADD_BLOCKS: { @@ -1256,7 +1257,7 @@ export function useUndoRedo() { useWorkflowStore.getState().batchRemoveEdges(edgeIdsToRemove) edgeIdsToRemove.forEach((edgeId) => { addToQueue({ - id: crypto.randomUUID(), + id: generateId(), operation: { operation: EDGE_OPERATIONS.REMOVE, target: OPERATION_TARGETS.EDGE, @@ -1271,7 +1272,7 @@ export function useUndoRedo() { // Send position update to server addToQueue({ - id: crypto.randomUUID(), + id: generateId(), operation: { operation: BLOCK_OPERATIONS.UPDATE_POSITION, target: OPERATION_TARGETS.BLOCK, @@ -1321,7 +1322,7 @@ export function useUndoRedo() { ) if (edgesToAdd.length > 0) { addToQueue({ - id: crypto.randomUUID(), + id: generateId(), operation: { operation: EDGES_OPERATIONS.BATCH_ADD_EDGES, target: OPERATION_TARGETS.EDGES, @@ -1376,7 +1377,7 @@ export function useUndoRedo() { if (allEdgeIdsToRemove.length > 0) { useWorkflowStore.getState().batchRemoveEdges(allEdgeIdsToRemove) addToQueue({ - id: crypto.randomUUID(), + id: generateId(), operation: { operation: EDGES_OPERATIONS.BATCH_REMOVE_EDGES, target: OPERATION_TARGETS.EDGES, @@ -1398,7 +1399,7 @@ export function useUndoRedo() { // Apply edge additions in batch after if (allEdgesToAdd.length > 0) { addToQueue({ - id: crypto.randomUUID(), + id: generateId(), operation: { operation: EDGES_OPERATIONS.BATCH_ADD_EDGES, target: OPERATION_TARGETS.EDGES, @@ -1711,7 +1712,7 @@ export function useUndoRedo() { if (!activeWorkflowId) return const operation: any = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.APPLY_DIFF, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -1724,7 +1725,7 @@ export function useUndoRedo() { } const inverse: any = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.APPLY_DIFF, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -1755,7 +1756,7 @@ export function useUndoRedo() { if (!activeWorkflowId) return const operation: any = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.ACCEPT_DIFF, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -1769,7 +1770,7 @@ export function useUndoRedo() { } const inverse: any = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.ACCEPT_DIFF, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -1795,7 +1796,7 @@ export function useUndoRedo() { if (!activeWorkflowId) return const operation: any = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.REJECT_DIFF, timestamp: Date.now(), workflowId: activeWorkflowId, @@ -1809,7 +1810,7 @@ export function useUndoRedo() { } const inverse: any = { - id: crypto.randomUUID(), + id: generateId(), type: UNDO_REDO_OPERATIONS.REJECT_DIFF, timestamp: Date.now(), workflowId: activeWorkflowId, diff --git a/apps/sim/lib/utils/uuid.ts b/apps/sim/lib/utils/uuid.ts new file mode 100644 index 00000000000..045052f3f30 --- /dev/null +++ b/apps/sim/lib/utils/uuid.ts @@ -0,0 +1,37 @@ +/** + * Generate a UUID v4 string that works in all contexts. + * + * crypto.randomUUID() is only available in secure contexts (HTTPS). + * In non-secure contexts (HTTP), it throws a TypeError. + * This utility provides a fallback implementation. + * + * @returns A UUID v4 string (e.g., "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx") + */ +export function generateId(): string { + // In Node.js (server-side) or secure browser contexts, use native API + if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { + try { + return crypto.randomUUID() + } catch { + // Fall through to manual implementation + } + } + + // Fallback: manual UUID v4 generation using getRandomValues + const bytes = new Uint8Array(16) + if (typeof crypto !== 'undefined' && crypto.getRandomValues) { + crypto.getRandomValues(bytes) + } else { + // Last resort: Math.random (not cryptographically secure, but functional) + for (let i = 0; i < 16; i++) { + bytes[i] = Math.floor(Math.random() * 256) + } + } + + // Set version (4) and variant (RFC 4122) bits + bytes[6] = (bytes[6] & 0x0f) | 0x40 + bytes[8] = (bytes[8] & 0x3f) | 0x80 + + const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')) + return `${hex[0]}${hex[1]}${hex[2]}${hex[3]}-${hex[4]}${hex[5]}-${hex[6]}${hex[7]}-${hex[8]}${hex[9]}-${hex[10]}${hex[11]}${hex[12]}${hex[13]}${hex[14]}${hex[15]}` +} diff --git a/apps/sim/serializer/index.ts b/apps/sim/serializer/index.ts index 671535ef684..53bbe028071 100644 --- a/apps/sim/serializer/index.ts +++ b/apps/sim/serializer/index.ts @@ -1,3 +1,4 @@ +import { generateId } from '../lib/utils/uuid' import { createLogger } from '@sim/logger' import type { Edge } from 'reactflow' import type { CanonicalModeOverrides } from '@/lib/workflows/subblocks/visibility' @@ -563,7 +564,7 @@ export class Serializer { // Deserialize connections workflow.connections.forEach((connection) => { edges.push({ - id: crypto.randomUUID(), + id: generateId(), source: connection.source, target: connection.target, sourceHandle: connection.sourceHandle, diff --git a/apps/sim/stores/chat/store.ts b/apps/sim/stores/chat/store.ts index 3da14769db3..6254d3b6311 100644 --- a/apps/sim/stores/chat/store.ts +++ b/apps/sim/stores/chat/store.ts @@ -1,3 +1,4 @@ +import { generateId } from '../../lib/utils/uuid' import { createLogger } from '@sim/logger' import { v4 as uuidv4 } from 'uuid' import { create } from 'zustand' @@ -58,7 +59,7 @@ export const useChatStore = create()( set((state) => { const newMessage: ChatMessage = { ...message, - id: (message as any).id ?? crypto.randomUUID(), + id: (message as any).id ?? generateId(), timestamp: (message as any).timestamp ?? new Date().toISOString(), } diff --git a/apps/sim/stores/copilot-training/store.ts b/apps/sim/stores/copilot-training/store.ts index fc6a346769d..19dcdd02287 100644 --- a/apps/sim/stores/copilot-training/store.ts +++ b/apps/sim/stores/copilot-training/store.ts @@ -1,3 +1,4 @@ +import { generateId } from '../../lib/utils/uuid' import { createLogger } from '@sim/logger' import { create } from 'zustand' import { devtools } from 'zustand/middleware' @@ -94,7 +95,7 @@ export const useCopilotTrainingStore = create()( const { activeWorkflowId } = useWorkflowStore.getState() as any const dataset: TrainingDataset = { - id: crypto.randomUUID(), + id: generateId(), workflowId: activeWorkflowId || 'unknown', title: state.currentTitle, prompt: state.currentPrompt, diff --git a/apps/sim/stores/notifications/store.ts b/apps/sim/stores/notifications/store.ts index 264cd014da7..6e0370dd02b 100644 --- a/apps/sim/stores/notifications/store.ts +++ b/apps/sim/stores/notifications/store.ts @@ -1,3 +1,4 @@ +import { generateId } from '../../lib/utils/uuid' import { createLogger } from '@sim/logger' import { create } from 'zustand' import { persist } from 'zustand/middleware' @@ -55,7 +56,7 @@ export const useNotificationStore = create()( notifications: [], addNotification: (params: AddNotificationParams) => { - const id = crypto.randomUUID() + const id = generateId() const notification: Notification = { id, diff --git a/apps/sim/stores/panel/copilot/store.ts b/apps/sim/stores/panel/copilot/store.ts index bd4dd76e2ad..6f44268511e 100644 --- a/apps/sim/stores/panel/copilot/store.ts +++ b/apps/sim/stores/panel/copilot/store.ts @@ -1,4 +1,5 @@ 'use client' +import { generateId } from '../../../lib/utils/uuid' import { createLogger } from '@sim/logger' import { create } from 'zustand' @@ -2527,7 +2528,7 @@ export const useCopilotStore = create()( // Message queue actions addToQueue: (message, options) => { const queuedMessage: import('./types').QueuedMessage = { - id: crypto.randomUUID(), + id: generateId(), content: message, fileAttachments: options?.fileAttachments, contexts: options?.contexts, diff --git a/apps/sim/stores/panel/variables/store.ts b/apps/sim/stores/panel/variables/store.ts index 70ebc2b7191..6167f2379f3 100644 --- a/apps/sim/stores/panel/variables/store.ts +++ b/apps/sim/stores/panel/variables/store.ts @@ -1,3 +1,4 @@ +import { generateId } from '../../../lib/utils/uuid' import { createLogger } from '@sim/logger' import JSON5 from 'json5' import { create } from 'zustand' @@ -108,7 +109,7 @@ export const useVariablesStore = create()( }, addVariable: (variable, providedId?: string) => { - const id = providedId || crypto.randomUUID() + const id = providedId || generateId() const workflowVariables = get().getVariablesByWorkflowId(variable.workflowId) @@ -236,7 +237,7 @@ export const useVariablesStore = create()( for (const { blockId, subBlockId, value } of changedSubBlocks) { operationQueue.addToQueue({ - id: crypto.randomUUID(), + id: generateId(), operation: { operation: 'subblock-update', target: 'subblock', diff --git a/apps/sim/stores/terminal/console/store.ts b/apps/sim/stores/terminal/console/store.ts index 7479ca0d6c6..02592ae8aab 100644 --- a/apps/sim/stores/terminal/console/store.ts +++ b/apps/sim/stores/terminal/console/store.ts @@ -1,3 +1,4 @@ +import { generateId } from '../../../lib/utils/uuid' import { createLogger } from '@sim/logger' import { create } from 'zustand' import { createJSONStorage, devtools, persist } from 'zustand/middleware' @@ -170,7 +171,7 @@ export const useTerminalConsoleStore = create()( const newEntry: ConsoleEntry = { ...redactedEntry, - id: crypto.randomUUID(), + id: generateId(), timestamp: new Date().toISOString(), } diff --git a/apps/sim/stores/undo-redo/utils.ts b/apps/sim/stores/undo-redo/utils.ts index d07225b3f54..cc5561a4da1 100644 --- a/apps/sim/stores/undo-redo/utils.ts +++ b/apps/sim/stores/undo-redo/utils.ts @@ -1,3 +1,4 @@ +import { generateId } from '../../lib/utils/uuid' import type { Edge } from 'reactflow' import { UNDO_REDO_OPERATIONS } from '@/socket/constants' import type { @@ -15,7 +16,7 @@ import type { BlockState } from '@/stores/workflows/workflow/types' export function createOperationEntry(operation: Operation, inverse: Operation): OperationEntry { return { - id: crypto.randomUUID(), + id: generateId(), operation, inverse, createdAt: Date.now(), diff --git a/apps/sim/stores/workflows/subblock/store.ts b/apps/sim/stores/workflows/subblock/store.ts index a6bb3bd5722..06249f3c903 100644 --- a/apps/sim/stores/workflows/subblock/store.ts +++ b/apps/sim/stores/workflows/subblock/store.ts @@ -1,3 +1,4 @@ +import { generateId } from '../../../lib/utils/uuid' import { createLogger } from '@sim/logger' import { create } from 'zustand' import { devtools } from 'zustand/middleware' @@ -44,13 +45,13 @@ export const useSubBlockStore = create()( if (!row || typeof row !== 'object') { logger.warn('Fixing malformed table row', { blockId, subBlockId, row }) return { - id: crypto.randomUUID(), + id: generateId(), cells: { Key: '', Value: '' }, } } if (!row.id) { - row.id = crypto.randomUUID() + row.id = generateId() } if (!row.cells || typeof row.cells !== 'object') { diff --git a/apps/sim/stores/workflows/utils.ts b/apps/sim/stores/workflows/utils.ts index e82bfcd7310..3c37bb7c4e2 100644 --- a/apps/sim/stores/workflows/utils.ts +++ b/apps/sim/stores/workflows/utils.ts @@ -1,3 +1,4 @@ +import { generateId } from '../../lib/utils/uuid' import type { Edge } from 'reactflow' import { v4 as uuidv4 } from 'uuid' import { DEFAULT_DUPLICATE_OFFSET } from '@/lib/workflows/autolayout/constants' @@ -172,7 +173,7 @@ export function prepareBlockState(options: PrepareBlockStateOptions): BlockState } else if (subBlock.type === 'input-format' || subBlock.type === 'response-format') { initialValue = [ { - id: crypto.randomUUID(), + id: generateId(), name: '', type: 'string', value: '', diff --git a/apps/sim/stores/workflows/workflow/store.ts b/apps/sim/stores/workflows/workflow/store.ts index bc97773d5cf..de9b1e28aae 100644 --- a/apps/sim/stores/workflows/workflow/store.ts +++ b/apps/sim/stores/workflows/workflow/store.ts @@ -1,3 +1,4 @@ +import { generateId } from '../../../lib/utils/uuid' import { createLogger } from '@sim/logger' import type { Edge } from 'reactflow' import { create } from 'zustand' @@ -80,7 +81,7 @@ function resolveInitialSubblockValue(config: SubBlockConfig): unknown { if (config.type === 'input-format') { return [ { - id: crypto.randomUUID(), + id: generateId(), name: '', type: 'string', value: '', @@ -248,7 +249,7 @@ export const useWorkflowStore = create()( for (const edge of validEdges) { if (!existingEdgeIds.has(edge.id)) { newEdges.push({ - id: edge.id || crypto.randomUUID(), + id: edge.id || generateId(), source: edge.source, target: edge.target, sourceHandle: edge.sourceHandle, @@ -441,7 +442,7 @@ export const useWorkflowStore = create()( for (const edge of filtered) { if (wouldCreateCycle([...newEdges], edge.source, edge.target)) continue newEdges.push({ - id: edge.id || crypto.randomUUID(), + id: edge.id || generateId(), source: edge.source, target: edge.target, sourceHandle: edge.sourceHandle, @@ -566,7 +567,7 @@ export const useWorkflowStore = create()( const block = get().blocks[id] if (!block) return - const newId = crypto.randomUUID() + const newId = generateId() // Check if block is inside a locked container - if so, place duplicate outside const parentId = block.data?.parentId diff --git a/apps/sim/tools/langsmith/utils.ts b/apps/sim/tools/langsmith/utils.ts index cbe3ae8872c..90e45b70eaf 100644 --- a/apps/sim/tools/langsmith/utils.ts +++ b/apps/sim/tools/langsmith/utils.ts @@ -1,3 +1,4 @@ +import { randomUUID } from 'node:crypto' import type { LangsmithRunPayload } from '@/tools/langsmith/types' interface NormalizedRunPayload { @@ -20,7 +21,7 @@ const toCompactTimestamp = (startTime?: string): string => { } export const normalizeLangsmithRunPayload = (run: LangsmithRunPayload): NormalizedRunPayload => { - const runId = run.id ?? crypto.randomUUID() + const runId = run.id ?? randomUUID() const traceId = run.trace_id ?? runId const startTime = run.start_time ?? new Date().toISOString() const dottedOrder = run.dotted_order ?? `${toCompactTimestamp(startTime)}Z${runId}` diff --git a/apps/sim/triggers/generic/webhook.ts b/apps/sim/triggers/generic/webhook.ts index d9e0e2ea5fd..6f9b0eb6cce 100644 --- a/apps/sim/triggers/generic/webhook.ts +++ b/apps/sim/triggers/generic/webhook.ts @@ -1,3 +1,4 @@ +import { generateId } from '../../lib/utils/uuid' import { WebhookIcon } from '@/components/icons' import type { TriggerConfig } from '@/triggers/types' @@ -36,7 +37,7 @@ export const genericWebhookTrigger: TriggerConfig = { description: 'Token used to authenticate webhook requests via Bearer token or custom header', password: true, required: false, - value: () => crypto.randomUUID(), + value: () => generateId(), mode: 'trigger', }, {