From af92b536362d1531c1df1dc4bfab0fd45848999d Mon Sep 17 00:00:00 2001 From: Maxwell Calkin Date: Mon, 9 Mar 2026 02:55:25 -0400 Subject: [PATCH 1/2] fix: replace crypto.randomUUID with universal generateId utility (#3393) Replace all direct crypto.randomUUID() calls with a generateId() helper that falls back to the uuid package when crypto.randomUUID is unavailable. crypto.randomUUID() is only available in secure contexts (HTTPS or localhost). When Sim is self-hosted via Docker and accessed over plain HTTP on a LAN, the call throws "crypto.randomUUID is not a function" and the page white-screens. Changes: - Add generateId() utility in lib/core/utils/id.ts that tries crypto.randomUUID() first, then falls back to uuid v4 (already a dependency) - Replace all 200+ crypto.randomUUID() calls across 117 files - Keep the existing layout.tsx polyfill as defense-in-depth for third-party code - Add mockGenerateId() to the testing package for test consistency > **AI Disclosure:** This PR was authored by an AI agent (Claude, Anthropic). > All code changes, analysis, and PR description were generated autonomously. > A human (Max Calkin, @MaxwellCalkin) provided the prompt and submitted this PR. Closes #3393 --- .../app/api/auth/shopify/authorize/route.ts | 3 +- apps/sim/app/api/copilot/chat/route.ts | 5 +- .../api/credential-sets/[id]/invite/route.ts | 5 +- .../api/credential-sets/[id]/members/route.ts | 3 +- .../credential-sets/invite/[token]/route.ts | 5 +- .../api/credential-sets/memberships/route.ts | 3 +- apps/sim/app/api/credential-sets/route.ts | 3 +- .../app/api/credentials/[id]/members/route.ts | 3 +- apps/sim/app/api/credentials/[id]/route.ts | 3 +- apps/sim/app/api/credentials/draft/route.ts | 3 +- apps/sim/app/api/credentials/route.ts | 7 +- apps/sim/app/api/environment/route.ts | 3 +- .../app/api/folders/[id]/duplicate/route.ts | 5 +- apps/sim/app/api/folders/route.ts | 3 +- apps/sim/app/api/mcp/servers/route.ts | 3 +- .../mcp/workflow-servers/[id]/tools/route.ts | 3 +- .../sim/app/api/mcp/workflow-servers/route.ts | 5 +- apps/sim/app/api/memory/route.ts | 3 +- .../[id]/members/bulk/route.ts | 3 +- .../permission-groups/[id]/members/route.ts | 3 +- apps/sim/app/api/permission-groups/route.ts | 3 +- .../api/superuser/import-workflow/route.ts | 3 +- .../sim/app/api/table/[tableId]/rows/route.ts | 5 +- .../api/table/[tableId]/rows/upsert/route.ts | 3 +- .../app/api/tools/a2a/send-message/route.ts | 3 +- apps/sim/app/api/tools/search/route.ts | 3 +- apps/sim/app/api/tools/stt/route.ts | 3 +- apps/sim/app/api/tools/tts/unified/route.ts | 3 +- apps/sim/app/api/tools/video/route.ts | 3 +- .../api/v1/admin/workflows/import/route.ts | 5 +- .../v1/admin/workspaces/[id]/import/route.ts | 9 +- .../v1/admin/workspaces/[id]/members/route.ts | 3 +- apps/sim/app/api/v1/audit-logs/[id]/route.ts | 3 +- apps/sim/app/api/v1/audit-logs/route.ts | 3 +- apps/sim/app/api/v1/copilot/chat/route.ts | 5 +- apps/sim/app/api/v1/logs/[id]/route.ts | 3 +- apps/sim/app/api/v1/logs/route.ts | 3 +- apps/sim/app/api/v1/workflows/[id]/route.ts | 3 +- apps/sim/app/api/v1/workflows/route.ts | 3 +- apps/sim/app/api/webhooks/route.ts | 3 +- apps/sim/app/api/workflows/route.ts | 3 +- .../api/workspaces/[id]/environment/route.ts | 5 +- .../api/workspaces/[id]/permissions/route.ts | 3 +- apps/sim/app/api/workspaces/route.ts | 7 +- apps/sim/app/chat/[identifier]/chat.tsx | 5 +- apps/sim/app/chat/components/input/input.tsx | 3 +- apps/sim/app/chat/hooks/use-chat-streaming.ts | 3 +- .../providers/global-commands-provider.tsx | 3 +- .../w/[workflowId]/components/chat/chat.tsx | 7 +- .../chat/hooks/use-chat-file-upload.ts | 3 +- .../user-input/hooks/use-file-attachments.ts | 3 +- .../deploy-modal/components/a2a/a2a.tsx | 7 +- .../document-tag-entry/document-tag-entry.tsx | 3 +- .../components/dropdown/dropdown.tsx | 3 +- .../components/eval-input/eval-input.tsx | 3 +- .../filter-builder/filter-builder.tsx | 3 +- .../knowledge-tag-filters.tsx | 3 +- .../components/sort-builder/sort-builder.tsx | 3 +- .../components/starter/input-format.tsx | 3 +- .../sub-block/components/table/table.tsx | 9 +- .../variables-input/variables-input.tsx | 7 +- .../[workspaceId]/w/[workflowId]/workflow.tsx | 13 +-- .../credit-balance/credit-balance.tsx | 3 +- .../w/hooks/use-import-workflow.ts | 3 +- .../w/hooks/use-import-workspace.ts | 3 +- .../workspace/providers/socket-provider.tsx | 3 +- .../handlers/workflow/workflow-handler.ts | 3 +- apps/sim/hooks/queries/a2a/tasks.ts | 9 +- apps/sim/hooks/use-code-undo-redo.ts | 5 +- apps/sim/hooks/use-collaborative-workflow.ts | 33 +++---- apps/sim/hooks/use-undo-redo.ts | 77 ++++++++-------- apps/sim/lib/a2a/utils.ts | 5 +- apps/sim/lib/auth/anonymous.ts | 3 +- apps/sim/lib/auth/auth.ts | 87 ++++++++++--------- apps/sim/lib/billing/core/usage-log.ts | 9 +- apps/sim/lib/billing/core/usage.ts | 7 +- apps/sim/lib/billing/organization.ts | 5 +- apps/sim/lib/billing/webhooks/enterprise.ts | 3 +- .../lib/copilot/client-sse/content-blocks.ts | 5 +- apps/sim/lib/copilot/orchestrator/index.ts | 3 +- .../lib/copilot/orchestrator/stream-core.ts | 3 +- apps/sim/lib/copilot/orchestrator/subagent.ts | 3 +- .../tool-executor/deployment-tools/deploy.ts | 5 +- .../tool-executor/deployment-tools/manage.ts | 5 +- .../tool-executor/workflow-tools/mutations.ts | 9 +- apps/sim/lib/copilot/request-helpers.ts | 3 +- .../tools/server/knowledge/knowledge-base.ts | 11 +-- .../server/user/set-environment-variables.ts | 5 +- .../server/workflow/edit-workflow/builders.ts | 7 +- .../lib/core/async-jobs/backends/database.ts | 3 +- .../sim/lib/core/async-jobs/backends/redis.ts | 3 +- apps/sim/lib/core/utils/id.ts | 19 ++++ apps/sim/lib/core/utils/request.ts | 3 +- apps/sim/lib/credentials/access.ts | 3 +- apps/sim/lib/credentials/draft-hooks.ts | 5 +- apps/sim/lib/credentials/environment.ts | 9 +- apps/sim/lib/credentials/oauth.ts | 5 +- apps/sim/lib/knowledge/documents/service.ts | 3 +- apps/sim/lib/table/service.ts | 7 +- apps/sim/lib/workflows/defaults.ts | 5 +- .../workflows/operations/socket-operations.ts | 3 +- .../lib/workflows/persistence/duplicate.ts | 9 +- apps/sim/lib/workflows/persistence/utils.ts | 9 +- apps/sim/lib/workflows/schedules/deploy.ts | 3 +- apps/sim/lib/workspaces/duplicate.ts | 7 +- apps/sim/serializer/index.ts | 3 +- apps/sim/socket/handlers/operations.ts | 25 +++--- apps/sim/stores/chat/store.ts | 3 +- apps/sim/stores/copilot-training/store.ts | 3 +- apps/sim/stores/notifications/store.ts | 3 +- apps/sim/stores/panel/copilot/store.ts | 3 +- apps/sim/stores/panel/variables/store.ts | 5 +- apps/sim/stores/terminal/console/store.ts | 3 +- apps/sim/stores/undo-redo/utils.ts | 3 +- apps/sim/stores/workflows/subblock/store.ts | 5 +- apps/sim/stores/workflows/utils.ts | 3 +- apps/sim/stores/workflows/workflow/store.ts | 9 +- apps/sim/tools/langsmith/utils.ts | 3 +- packages/testing/src/mocks/uuid.mock.ts | 22 +++++ 119 files changed, 456 insertions(+), 298 deletions(-) create mode 100644 apps/sim/lib/core/utils/id.ts diff --git a/apps/sim/app/api/auth/shopify/authorize/route.ts b/apps/sim/app/api/auth/shopify/authorize/route.ts index daa6dfecf06..6a8c9412624 100644 --- a/apps/sim/app/api/auth/shopify/authorize/route.ts +++ b/apps/sim/app/api/auth/shopify/authorize/route.ts @@ -3,6 +3,7 @@ import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { env } from '@/lib/core/config/env' import { getBaseUrl } from '@/lib/core/utils/urls' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('ShopifyAuthorize') @@ -161,7 +162,7 @@ export async function GET(request: NextRequest) { const baseUrl = getBaseUrl() const redirectUri = `${baseUrl}/api/auth/oauth2/callback/shopify` - const state = crypto.randomUUID() + const state = generateId() const oauthUrl = `https://${cleanShop}/admin/oauth/authorize?` + diff --git a/apps/sim/app/api/copilot/chat/route.ts b/apps/sim/app/api/copilot/chat/route.ts index 101c3e2a7e6..a06729c10f1 100644 --- a/apps/sim/app/api/copilot/chat/route.ts +++ b/apps/sim/app/api/copilot/chat/route.ts @@ -25,6 +25,7 @@ import { } from '@/lib/copilot/request-helpers' import { env } from '@/lib/core/config/env' import { resolveWorkflowIdForUser } from '@/lib/workflows/utils' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('CopilotChatAPI') @@ -188,7 +189,7 @@ export async function POST(req: NextRequest) { const workflowId = resolved.workflowId // Ensure we have a consistent user message ID for this request - const userMessageIdToUse = userMessageId || crypto.randomUUID() + const userMessageIdToUse = userMessageId || generateId() try { logger.info(`[${tracker.requestId}] Received chat POST`, { hasContexts: Array.isArray(normalizedContexts), @@ -474,7 +475,7 @@ export async function POST(req: NextRequest) { } const assistantMessage = { - id: crypto.randomUUID(), + id: generateId(), role: 'assistant', content: responseData.content, timestamp: new Date().toISOString(), diff --git a/apps/sim/app/api/credential-sets/[id]/invite/route.ts b/apps/sim/app/api/credential-sets/[id]/invite/route.ts index 9fcf13d2127..eed0c3febab 100644 --- a/apps/sim/app/api/credential-sets/[id]/invite/route.ts +++ b/apps/sim/app/api/credential-sets/[id]/invite/route.ts @@ -10,6 +10,7 @@ import { getSession } from '@/lib/auth' import { hasCredentialSetsAccess } from '@/lib/billing' import { getBaseUrl } from '@/lib/core/utils/urls' import { sendEmail } from '@/lib/messaging/email/mailer' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('CredentialSetInvite') @@ -105,12 +106,12 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id: const body = await req.json() const { email } = createInviteSchema.parse(body) - const token = crypto.randomUUID() + const token = generateId() const expiresAt = new Date() expiresAt.setDate(expiresAt.getDate() + 7) const invitation = { - id: crypto.randomUUID(), + id: generateId(), credentialSetId: id, email: email || null, token, diff --git a/apps/sim/app/api/credential-sets/[id]/members/route.ts b/apps/sim/app/api/credential-sets/[id]/members/route.ts index 2799feb784b..be49af1f862 100644 --- a/apps/sim/app/api/credential-sets/[id]/members/route.ts +++ b/apps/sim/app/api/credential-sets/[id]/members/route.ts @@ -7,6 +7,7 @@ import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log' import { getSession } from '@/lib/auth' import { hasCredentialSetsAccess } from '@/lib/billing' import { syncAllWebhooksForCredentialSet } from '@/lib/webhooks/utils.server' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('CredentialSetMembers') @@ -167,7 +168,7 @@ export async function DELETE(req: NextRequest, { params }: { params: Promise<{ i return NextResponse.json({ error: 'Member not found' }, { status: 404 }) } - const requestId = crypto.randomUUID().slice(0, 8) + const requestId = generateId().slice(0, 8) // Use transaction to ensure member deletion + webhook sync are atomic await db.transaction(async (tx) => { diff --git a/apps/sim/app/api/credential-sets/invite/[token]/route.ts b/apps/sim/app/api/credential-sets/invite/[token]/route.ts index b85c65fed5a..502c56f350b 100644 --- a/apps/sim/app/api/credential-sets/invite/[token]/route.ts +++ b/apps/sim/app/api/credential-sets/invite/[token]/route.ts @@ -11,6 +11,7 @@ import { type NextRequest, NextResponse } from 'next/server' import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log' import { getSession } from '@/lib/auth' import { syncAllWebhooksForCredentialSet } from '@/lib/webhooks/utils.server' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('CredentialSetInviteToken') @@ -125,11 +126,11 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ tok } const now = new Date() - const requestId = crypto.randomUUID().slice(0, 8) + const requestId = generateId().slice(0, 8) await db.transaction(async (tx) => { await tx.insert(credentialSetMember).values({ - id: crypto.randomUUID(), + id: generateId(), credentialSetId: invitation.credentialSetId, userId: session.user.id, status: 'active', diff --git a/apps/sim/app/api/credential-sets/memberships/route.ts b/apps/sim/app/api/credential-sets/memberships/route.ts index 045d68ad1ba..78b9002f6bd 100644 --- a/apps/sim/app/api/credential-sets/memberships/route.ts +++ b/apps/sim/app/api/credential-sets/memberships/route.ts @@ -6,6 +6,7 @@ import { type NextRequest, NextResponse } from 'next/server' import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log' import { getSession } from '@/lib/auth' import { syncAllWebhooksForCredentialSet } from '@/lib/webhooks/utils.server' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('CredentialSetMemberships') @@ -60,7 +61,7 @@ export async function DELETE(req: NextRequest) { } try { - const requestId = crypto.randomUUID().slice(0, 8) + const requestId = generateId().slice(0, 8) // Use transaction to ensure revocation + webhook sync are atomic await db.transaction(async (tx) => { diff --git a/apps/sim/app/api/credential-sets/route.ts b/apps/sim/app/api/credential-sets/route.ts index 621f651b6a7..829b588c4ca 100644 --- a/apps/sim/app/api/credential-sets/route.ts +++ b/apps/sim/app/api/credential-sets/route.ts @@ -7,6 +7,7 @@ import { z } from 'zod' import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log' import { getSession } from '@/lib/auth' import { hasCredentialSetsAccess } from '@/lib/billing' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('CredentialSets') @@ -148,7 +149,7 @@ export async function POST(req: Request) { const now = new Date() const newCredentialSet = { - id: crypto.randomUUID(), + id: generateId(), organizationId, name, description: description || null, diff --git a/apps/sim/app/api/credentials/[id]/members/route.ts b/apps/sim/app/api/credentials/[id]/members/route.ts index 11cee7f7177..18949b11359 100644 --- a/apps/sim/app/api/credentials/[id]/members/route.ts +++ b/apps/sim/app/api/credentials/[id]/members/route.ts @@ -6,6 +6,7 @@ import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('CredentialMembersAPI') @@ -133,7 +134,7 @@ export async function POST(request: NextRequest, context: RouteContext) { } await db.insert(credentialMember).values({ - id: crypto.randomUUID(), + id: generateId(), credentialId, userId, role, diff --git a/apps/sim/app/api/credentials/[id]/route.ts b/apps/sim/app/api/credentials/[id]/route.ts index 7da93846c75..d6ed6ad1884 100644 --- a/apps/sim/app/api/credentials/[id]/route.ts +++ b/apps/sim/app/api/credentials/[id]/route.ts @@ -7,6 +7,7 @@ import { z } from 'zod' import { getSession } from '@/lib/auth' import { getCredentialActorContext } from '@/lib/credentials/access' import { +import { generateId } from '@/lib/core/utils/id' syncPersonalEnvCredentialsForUser, syncWorkspaceEnvCredentials, } from '@/lib/credentials/environment' @@ -222,7 +223,7 @@ export async function DELETE( await db .insert(workspaceEnvironment) .values({ - id: workspaceRow?.id || crypto.randomUUID(), + id: workspaceRow?.id || generateId(), workspaceId: access.credential.workspaceId, variables: current, createdAt: workspaceRow?.createdAt || new Date(), diff --git a/apps/sim/app/api/credentials/draft/route.ts b/apps/sim/app/api/credentials/draft/route.ts index ac700f088ed..8a8770bba94 100644 --- a/apps/sim/app/api/credentials/draft/route.ts +++ b/apps/sim/app/api/credentials/draft/route.ts @@ -6,6 +6,7 @@ import { NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { checkWorkspaceAccess } from '@/lib/workspaces/permissions/utils' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('CredentialDraftAPI') @@ -75,7 +76,7 @@ export async function POST(request: Request) { await db .insert(pendingCredentialDraft) .values({ - id: crypto.randomUUID(), + id: generateId(), userId, workspaceId, providerId, diff --git a/apps/sim/app/api/credentials/route.ts b/apps/sim/app/api/credentials/route.ts index e0fea07f3e3..d389e2131e0 100644 --- a/apps/sim/app/api/credentials/route.ts +++ b/apps/sim/app/api/credentials/route.ts @@ -11,6 +11,7 @@ import { syncWorkspaceOAuthCredentialsForUser } from '@/lib/credentials/oauth' import { getServiceConfigByProviderId } from '@/lib/oauth' import { checkWorkspaceAccess } from '@/lib/workspaces/permissions/utils' import { isValidEnvVarName } from '@/executor/constants' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('CredentialsAPI') @@ -423,7 +424,7 @@ export async function POST(request: NextRequest) { } const now = new Date() - const credentialId = crypto.randomUUID() + const credentialId = generateId() const [workspaceRow] = await db .select({ ownerId: workspace.ownerId }) .from(workspace) @@ -451,7 +452,7 @@ export async function POST(request: NextRequest) { if (workspaceUserIds.length > 0) { for (const memberUserId of workspaceUserIds) { await tx.insert(credentialMember).values({ - id: crypto.randomUUID(), + id: generateId(), credentialId, userId: memberUserId, role: @@ -468,7 +469,7 @@ export async function POST(request: NextRequest) { } } else { await tx.insert(credentialMember).values({ - id: crypto.randomUUID(), + id: generateId(), credentialId, userId: session.user.id, role: 'admin', diff --git a/apps/sim/app/api/environment/route.ts b/apps/sim/app/api/environment/route.ts index 39c91668f61..233d625faef 100644 --- a/apps/sim/app/api/environment/route.ts +++ b/apps/sim/app/api/environment/route.ts @@ -10,6 +10,7 @@ import { decryptSecret, encryptSecret } from '@/lib/core/security/encryption' import { generateRequestId } from '@/lib/core/utils/request' import { syncPersonalEnvCredentialsForUser } from '@/lib/credentials/environment' import type { EnvironmentVariable } from '@/stores/settings/environment' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('EnvironmentAPI') @@ -42,7 +43,7 @@ export async function POST(req: NextRequest) { await db .insert(environment) .values({ - id: crypto.randomUUID(), + id: generateId(), userId: session.user.id, variables: encryptedVariables, updatedAt: new Date(), diff --git a/apps/sim/app/api/folders/[id]/duplicate/route.ts b/apps/sim/app/api/folders/[id]/duplicate/route.ts index 54eafdf36ff..09bb627e8a4 100644 --- a/apps/sim/app/api/folders/[id]/duplicate/route.ts +++ b/apps/sim/app/api/folders/[id]/duplicate/route.ts @@ -9,6 +9,7 @@ import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' import { duplicateWorkflow } from '@/lib/workflows/persistence/duplicate' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('FolderDuplicateAPI') @@ -60,7 +61,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id: const targetWorkspaceId = workspaceId || sourceFolder.workspaceId const { newFolderId, folderMapping } = await db.transaction(async (tx) => { - const newFolderId = crypto.randomUUID() + const newFolderId = generateId() const now = new Date() const targetParentId = parentId ?? sourceFolder.parentId @@ -220,7 +221,7 @@ async function duplicateFolderStructure( ) for (const childFolder of childFolders) { - const newChildFolderId = crypto.randomUUID() + const newChildFolderId = generateId() folderMapping.set(childFolder.id, newChildFolderId) await tx.insert(workflowFolder).values({ diff --git a/apps/sim/app/api/folders/route.ts b/apps/sim/app/api/folders/route.ts index 835231d31f2..9d7e7a6ffb8 100644 --- a/apps/sim/app/api/folders/route.ts +++ b/apps/sim/app/api/folders/route.ts @@ -6,6 +6,7 @@ import { type NextRequest, NextResponse } from 'next/server' import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log' import { getSession } from '@/lib/auth' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('FoldersAPI') @@ -80,7 +81,7 @@ export async function POST(request: NextRequest) { } // Generate a new ID - const id = crypto.randomUUID() + const id = generateId() const newFolder = await db.transaction(async (tx) => { let sortOrder: number diff --git a/apps/sim/app/api/mcp/servers/route.ts b/apps/sim/app/api/mcp/servers/route.ts index 3087ff9bde5..be07e3b9794 100644 --- a/apps/sim/app/api/mcp/servers/route.ts +++ b/apps/sim/app/api/mcp/servers/route.ts @@ -8,6 +8,7 @@ import { McpDomainNotAllowedError, validateMcpDomain } from '@/lib/mcp/domain-ch import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware' import { mcpService } from '@/lib/mcp/service' import { +import { generateId } from '@/lib/core/utils/id' createMcpErrorResponse, createMcpSuccessResponse, generateMcpServerId, @@ -83,7 +84,7 @@ export const POST = withMcpAuth('write')( throw e } - const serverId = body.url ? generateMcpServerId(workspaceId, body.url) : crypto.randomUUID() + const serverId = body.url ? generateMcpServerId(workspaceId, body.url) : generateId() const [existingServer] = await db .select({ id: mcpServers.id, deletedAt: mcpServers.deletedAt }) diff --git a/apps/sim/app/api/mcp/workflow-servers/[id]/tools/route.ts b/apps/sim/app/api/mcp/workflow-servers/[id]/tools/route.ts index bdd9139f937..cd3c3505261 100644 --- a/apps/sim/app/api/mcp/workflow-servers/[id]/tools/route.ts +++ b/apps/sim/app/api/mcp/workflow-servers/[id]/tools/route.ts @@ -10,6 +10,7 @@ import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/util import { generateParameterSchemaForWorkflow } from '@/lib/mcp/workflow-mcp-sync' import { sanitizeToolName } from '@/lib/mcp/workflow-tool-schema' import { hasValidStartBlock } from '@/lib/workflows/triggers/trigger-utils.server' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('WorkflowMcpToolsAPI') @@ -181,7 +182,7 @@ export const POST = withMcpAuth('write')( ? body.parameterSchema : await generateParameterSchemaForWorkflow(body.workflowId) - const toolId = crypto.randomUUID() + const toolId = generateId() const [tool] = await db .insert(workflowMcpTool) .values({ diff --git a/apps/sim/app/api/mcp/workflow-servers/route.ts b/apps/sim/app/api/mcp/workflow-servers/route.ts index 27515941323..ed69a3d0a5f 100644 --- a/apps/sim/app/api/mcp/workflow-servers/route.ts +++ b/apps/sim/app/api/mcp/workflow-servers/route.ts @@ -10,6 +10,7 @@ import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/util import { generateParameterSchemaForWorkflow } from '@/lib/mcp/workflow-mcp-sync' import { sanitizeToolName } from '@/lib/mcp/workflow-tool-schema' import { hasValidStartBlock } from '@/lib/workflows/triggers/trigger-utils.server' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('WorkflowMcpServersAPI') @@ -104,7 +105,7 @@ export const POST = withMcpAuth('write')( ) } - const serverId = crypto.randomUUID() + const serverId = generateId() const [server] = await db .insert(workflowMcpServer) @@ -160,7 +161,7 @@ export const POST = withMcpAuth('write')( const parameterSchema = await generateParameterSchemaForWorkflow(workflowRecord.id) - const toolId = crypto.randomUUID() + const toolId = generateId() await db.insert(workflowMcpTool).values({ id: toolId, serverId, diff --git a/apps/sim/app/api/memory/route.ts b/apps/sim/app/api/memory/route.ts index c5a4638d7c4..b4c91d8ba46 100644 --- a/apps/sim/app/api/memory/route.ts +++ b/apps/sim/app/api/memory/route.ts @@ -6,6 +6,7 @@ import { type NextRequest, NextResponse } from 'next/server' import { checkInternalAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' import { checkWorkspaceAccess } from '@/lib/workspaces/permissions/utils' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('MemoryAPI') @@ -163,7 +164,7 @@ export async function POST(request: NextRequest) { const initialData = Array.isArray(data) ? data : [data] const now = new Date() - const id = `mem_${crypto.randomUUID().replace(/-/g, '')}` + const id = `mem_${generateId().replace(/-/g, '')}` const { sql } = await import('drizzle-orm') diff --git a/apps/sim/app/api/permission-groups/[id]/members/bulk/route.ts b/apps/sim/app/api/permission-groups/[id]/members/bulk/route.ts index c6e3faa2d28..6a936009886 100644 --- a/apps/sim/app/api/permission-groups/[id]/members/bulk/route.ts +++ b/apps/sim/app/api/permission-groups/[id]/members/bulk/route.ts @@ -6,6 +6,7 @@ import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { hasAccessControlAccess } from '@/lib/billing' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('PermissionGroupBulkMembers') @@ -129,7 +130,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id: } const newMembers = usersToAdd.map((userId) => ({ - id: crypto.randomUUID(), + id: generateId(), permissionGroupId: id, userId, assignedBy: session.user.id, diff --git a/apps/sim/app/api/permission-groups/[id]/members/route.ts b/apps/sim/app/api/permission-groups/[id]/members/route.ts index ec57c3689c6..7e0c6c604b4 100644 --- a/apps/sim/app/api/permission-groups/[id]/members/route.ts +++ b/apps/sim/app/api/permission-groups/[id]/members/route.ts @@ -7,6 +7,7 @@ import { z } from 'zod' import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log' import { getSession } from '@/lib/auth' import { hasAccessControlAccess } from '@/lib/billing' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('PermissionGroupMembers') @@ -137,7 +138,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id: } const memberData = { - id: crypto.randomUUID(), + id: generateId(), permissionGroupId: id, userId, assignedBy: session.user.id, diff --git a/apps/sim/app/api/permission-groups/route.ts b/apps/sim/app/api/permission-groups/route.ts index a1b6c25476a..5946126bc54 100644 --- a/apps/sim/app/api/permission-groups/route.ts +++ b/apps/sim/app/api/permission-groups/route.ts @@ -8,6 +8,7 @@ import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log' import { getSession } from '@/lib/auth' import { hasAccessControlAccess } from '@/lib/billing' import { +import { generateId } from '@/lib/core/utils/id' DEFAULT_PERMISSION_GROUP_CONFIG, type PermissionGroupConfig, parsePermissionGroupConfig, @@ -181,7 +182,7 @@ export async function POST(req: Request) { const now = new Date() const newGroup = { - id: crypto.randomUUID(), + id: generateId(), organizationId, name, description: description || null, diff --git a/apps/sim/app/api/superuser/import-workflow/route.ts b/apps/sim/app/api/superuser/import-workflow/route.ts index 3998792993d..d39cdf6a5c6 100644 --- a/apps/sim/app/api/superuser/import-workflow/route.ts +++ b/apps/sim/app/api/superuser/import-workflow/route.ts @@ -11,6 +11,7 @@ import { saveWorkflowToNormalizedTables, } from '@/lib/workflows/persistence/utils' import { sanitizeForExport } from '@/lib/workflows/sanitization/json-sanitizer' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('SuperUserImportWorkflow') @@ -117,7 +118,7 @@ export async function POST(request: NextRequest) { } // Create new workflow record - const newWorkflowId = crypto.randomUUID() + const newWorkflowId = generateId() const now = new Date() await db.insert(workflow).values({ diff --git a/apps/sim/app/api/table/[tableId]/rows/route.ts b/apps/sim/app/api/table/[tableId]/rows/route.ts index 47bd0fe1a5f..07687a9d5eb 100644 --- a/apps/sim/app/api/table/[tableId]/rows/route.ts +++ b/apps/sim/app/api/table/[tableId]/rows/route.ts @@ -19,6 +19,7 @@ import { } from '@/lib/table' import { buildFilterClause, buildSortClause } from '@/lib/table/sql' import { accessError, checkAccess } from '../../utils' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('TableRowsAPI') @@ -132,7 +133,7 @@ async function handleBatchInsert( const now = new Date() const rowsToInsert = validated.rows.map((data) => ({ - id: `row_${crypto.randomUUID().replace(/-/g, '')}`, + id: `row_${generateId().replace(/-/g, '')}`, tableId, workspaceId, data, @@ -218,7 +219,7 @@ export async function POST(request: NextRequest, { params }: TableRowsRouteParam ) } - const rowId = `row_${crypto.randomUUID().replace(/-/g, '')}` + const rowId = `row_${generateId().replace(/-/g, '')}` const now = new Date() const [row] = await db diff --git a/apps/sim/app/api/table/[tableId]/rows/upsert/route.ts b/apps/sim/app/api/table/[tableId]/rows/upsert/route.ts index a7b9e814683..70a27f3e41d 100644 --- a/apps/sim/app/api/table/[tableId]/rows/upsert/route.ts +++ b/apps/sim/app/api/table/[tableId]/rows/upsert/route.ts @@ -9,6 +9,7 @@ import { generateRequestId } from '@/lib/core/utils/request' import type { RowData, TableSchema } from '@/lib/table' import { getUniqueColumns, validateRowData } from '@/lib/table' import { accessError, checkAccess, verifyTableWorkspace } from '../../../utils' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('TableUpsertAPI') @@ -131,7 +132,7 @@ export async function POST(request: NextRequest, { params }: UpsertRouteParams) const [insertedRow] = await trx .insert(userTableRows) .values({ - id: `row_${crypto.randomUUID().replace(/-/g, '')}`, + id: `row_${generateId().replace(/-/g, '')}`, tableId, workspaceId: validated.workspaceId, data: validated.data, diff --git a/apps/sim/app/api/tools/a2a/send-message/route.ts b/apps/sim/app/api/tools/a2a/send-message/route.ts index 1cf7f966e03..b9fe01443b4 100644 --- a/apps/sim/app/api/tools/a2a/send-message/route.ts +++ b/apps/sim/app/api/tools/a2a/send-message/route.ts @@ -6,6 +6,7 @@ import { createA2AClient, extractTextContent, isTerminalState } from '@/lib/a2a/ import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateUrlWithDNS } from '@/lib/core/security/input-validation.server' import { generateRequestId } from '@/lib/core/utils/request' +import { generateId } from '@/lib/core/utils/id' export const dynamic = 'force-dynamic' @@ -142,7 +143,7 @@ export async function POST(request: NextRequest) { const message: Message = { kind: 'message', - messageId: crypto.randomUUID(), + messageId: generateId(), role: 'user', parts, ...(validatedData.taskId && { taskId: validatedData.taskId }), diff --git a/apps/sim/app/api/tools/search/route.ts b/apps/sim/app/api/tools/search/route.ts index c3b23303181..1e60f35bb05 100644 --- a/apps/sim/app/api/tools/search/route.ts +++ b/apps/sim/app/api/tools/search/route.ts @@ -5,6 +5,7 @@ import { checkInternalAuth } from '@/lib/auth/hybrid' import { SEARCH_TOOL_COST } from '@/lib/billing/constants' import { env } from '@/lib/core/config/env' import { executeTool } from '@/tools' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('search') @@ -16,7 +17,7 @@ export const maxDuration = 60 export const dynamic = 'force-dynamic' export async function POST(request: NextRequest) { - const requestId = crypto.randomUUID() + const requestId = generateId() try { const { searchParams: urlParams } = new URL(request.url) diff --git a/apps/sim/app/api/tools/stt/route.ts b/apps/sim/app/api/tools/stt/route.ts index aaacadd5a69..cde7d21e238 100644 --- a/apps/sim/app/api/tools/stt/route.ts +++ b/apps/sim/app/api/tools/stt/route.ts @@ -14,6 +14,7 @@ import { } from '@/lib/uploads/utils/file-utils.server' import type { UserFile } from '@/executor/types' import type { TranscriptSegment } from '@/tools/stt/types' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('SttProxyAPI') @@ -45,7 +46,7 @@ interface SttRequestBody { } export async function POST(request: NextRequest) { - const requestId = crypto.randomUUID() + const requestId = generateId() logger.info(`[${requestId}] STT transcription request started`) try { diff --git a/apps/sim/app/api/tools/tts/unified/route.ts b/apps/sim/app/api/tools/tts/unified/route.ts index c8b6b89c930..95ce2c0315a 100644 --- a/apps/sim/app/api/tools/tts/unified/route.ts +++ b/apps/sim/app/api/tools/tts/unified/route.ts @@ -17,6 +17,7 @@ import type { TtsResponse, } from '@/tools/tts/types' import { getFileExtension, getMimeType } from '@/tools/tts/types' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('TtsUnifiedProxyAPI') @@ -83,7 +84,7 @@ interface TtsUnifiedRequestBody { } export async function POST(request: NextRequest) { - const requestId = crypto.randomUUID() + const requestId = generateId() logger.info(`[${requestId}] TTS unified request started`) try { diff --git a/apps/sim/app/api/tools/video/route.ts b/apps/sim/app/api/tools/video/route.ts index 258928289eb..30c7c9976b3 100644 --- a/apps/sim/app/api/tools/video/route.ts +++ b/apps/sim/app/api/tools/video/route.ts @@ -5,6 +5,7 @@ import { getMaxExecutionTimeout } from '@/lib/core/execution-limits' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' import type { UserFile } from '@/executor/types' import type { VideoRequestBody } from '@/tools/video/types' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('VideoProxyAPI') @@ -12,7 +13,7 @@ export const dynamic = 'force-dynamic' export const maxDuration = 600 // 10 minutes for video generation export async function POST(request: NextRequest) { - const requestId = crypto.randomUUID() + const requestId = generateId() logger.info(`[${requestId}] Video generation request started`) try { diff --git a/apps/sim/app/api/v1/admin/workflows/import/route.ts b/apps/sim/app/api/v1/admin/workflows/import/route.ts index 7c3dd58ad69..e752aede5f3 100644 --- a/apps/sim/app/api/v1/admin/workflows/import/route.ts +++ b/apps/sim/app/api/v1/admin/workflows/import/route.ts @@ -28,6 +28,7 @@ import { notFoundResponse, } from '@/app/api/v1/admin/responses' import { +import { generateId } from '@/lib/core/utils/id' extractWorkflowMetadata, type WorkflowImportRequest, type WorkflowVariable, @@ -91,7 +92,7 @@ export const POST = withAdminAuth(async (request) => { description: workflowDescription, } = extractWorkflowMetadata(parsedWorkflow, overrideName) - const workflowId = crypto.randomUUID() + const workflowId = generateId() const now = new Date() await db.insert(workflow).values({ @@ -120,7 +121,7 @@ export const POST = withAdminAuth(async (request) => { if (workflowData.variables && Array.isArray(workflowData.variables)) { const variablesRecord: Record = {} workflowData.variables.forEach((v) => { - const varId = v.id || crypto.randomUUID() + const varId = v.id || generateId() variablesRecord[varId] = { id: varId, name: v.name, diff --git a/apps/sim/app/api/v1/admin/workspaces/[id]/import/route.ts b/apps/sim/app/api/v1/admin/workspaces/[id]/import/route.ts index 6bb6a4db66c..2c394865b2a 100644 --- a/apps/sim/app/api/v1/admin/workspaces/[id]/import/route.ts +++ b/apps/sim/app/api/v1/admin/workspaces/[id]/import/route.ts @@ -41,6 +41,7 @@ import { notFoundResponse, } from '@/app/api/v1/admin/responses' import { +import { generateId } from '@/lib/core/utils/id' extractWorkflowMetadata, type ImportResult, type WorkflowVariable, @@ -128,7 +129,7 @@ export const POST = withAdminAuthParams(async (request, context) => let rootFolderId: string | undefined if (rootFolderName && createFolders) { - rootFolderId = crypto.randomUUID() + rootFolderId = generateId() await db.insert(workflowFolder).values({ id: rootFolderId, name: rootFolderName, @@ -205,7 +206,7 @@ async function importSingleWorkflow( const fullPath = rootFolderId ? `root/${pathSegment}` : pathSegment if (!folderMap.has(fullPath)) { - const folderId = crypto.randomUUID() + const folderId = generateId() await db.insert(workflowFolder).values({ id: folderId, name: wf.folderPath[i], @@ -236,7 +237,7 @@ async function importSingleWorkflow( } })() const { color: workflowColor } = extractWorkflowMetadata(parsedContent) - const workflowId = crypto.randomUUID() + const workflowId = generateId() const now = new Date() await db.insert(workflow).values({ @@ -270,7 +271,7 @@ async function importSingleWorkflow( if (workflowData.variables && Array.isArray(workflowData.variables)) { const variablesRecord: Record = {} workflowData.variables.forEach((v) => { - const varId = v.id || crypto.randomUUID() + const varId = v.id || generateId() variablesRecord[varId] = { id: varId, name: v.name, diff --git a/apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts b/apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts index 78298feb490..c2b58bdfdbd 100644 --- a/apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts +++ b/apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts @@ -45,6 +45,7 @@ import { singleResponse, } from '@/app/api/v1/admin/responses' import { +import { generateId } from '@/lib/core/utils/id' type AdminWorkspaceMember, createPaginationMeta, parsePaginationParams, @@ -216,7 +217,7 @@ export const POST = withAdminAuthParams(async (request, context) => } const now = new Date() - const permissionId = crypto.randomUUID() + const permissionId = generateId() await db.insert(permissions).values({ id: permissionId, diff --git a/apps/sim/app/api/v1/audit-logs/[id]/route.ts b/apps/sim/app/api/v1/audit-logs/[id]/route.ts index 3cf6351d2bd..76e59bc5664 100644 --- a/apps/sim/app/api/v1/audit-logs/[id]/route.ts +++ b/apps/sim/app/api/v1/audit-logs/[id]/route.ts @@ -19,13 +19,14 @@ import { validateEnterpriseAuditAccess } from '@/app/api/v1/audit-logs/auth' import { formatAuditLogEntry } from '@/app/api/v1/audit-logs/format' import { createApiResponse, getUserLimits } from '@/app/api/v1/logs/meta' import { checkRateLimit, createRateLimitResponse } from '@/app/api/v1/middleware' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('V1AuditLogDetailAPI') export const revalidate = 0 export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { - const requestId = crypto.randomUUID().slice(0, 8) + const requestId = generateId().slice(0, 8) try { const rateLimit = await checkRateLimit(request, 'audit-logs') diff --git a/apps/sim/app/api/v1/audit-logs/route.ts b/apps/sim/app/api/v1/audit-logs/route.ts index 825cf376203..6d7485f9820 100644 --- a/apps/sim/app/api/v1/audit-logs/route.ts +++ b/apps/sim/app/api/v1/audit-logs/route.ts @@ -29,6 +29,7 @@ import { validateEnterpriseAuditAccess } from '@/app/api/v1/audit-logs/auth' import { formatAuditLogEntry } from '@/app/api/v1/audit-logs/format' import { createApiResponse, getUserLimits } from '@/app/api/v1/logs/meta' import { checkRateLimit, createRateLimitResponse } from '@/app/api/v1/middleware' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('V1AuditLogsAPI') @@ -74,7 +75,7 @@ function decodeCursor(cursor: string): CursorData | null { } export async function GET(request: NextRequest) { - const requestId = crypto.randomUUID().slice(0, 8) + const requestId = generateId().slice(0, 8) try { const rateLimit = await checkRateLimit(request, 'audit-logs') diff --git a/apps/sim/app/api/v1/copilot/chat/route.ts b/apps/sim/app/api/v1/copilot/chat/route.ts index 6a3817385be..25d3577bb41 100644 --- a/apps/sim/app/api/v1/copilot/chat/route.ts +++ b/apps/sim/app/api/v1/copilot/chat/route.ts @@ -6,6 +6,7 @@ import { COPILOT_REQUEST_MODES } from '@/lib/copilot/models' import { orchestrateCopilotStream } from '@/lib/copilot/orchestrator' import { resolveWorkflowIdForUser } from '@/lib/workflows/utils' import { authenticateV1Request } from '@/app/api/v1/auth' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('CopilotHeadlessAPI') const DEFAULT_COPILOT_MODEL = 'claude-opus-4-5' @@ -66,7 +67,7 @@ export async function POST(req: NextRequest) { const transportMode = effectiveMode === 'build' ? 'agent' : effectiveMode // Always generate a chatId - required for artifacts system to work with subagents - const chatId = parsed.chatId || crypto.randomUUID() + const chatId = parsed.chatId || generateId() const requestPayload = { message: parsed.message, @@ -74,7 +75,7 @@ export async function POST(req: NextRequest) { userId: auth.userId, model: selectedModel, mode: transportMode, - messageId: crypto.randomUUID(), + messageId: generateId(), version: SIM_AGENT_VERSION, headless: true, chatId, diff --git a/apps/sim/app/api/v1/logs/[id]/route.ts b/apps/sim/app/api/v1/logs/[id]/route.ts index b1d8f89ff36..5d9dc217889 100644 --- a/apps/sim/app/api/v1/logs/[id]/route.ts +++ b/apps/sim/app/api/v1/logs/[id]/route.ts @@ -5,13 +5,14 @@ import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { createApiResponse, getUserLimits } from '@/app/api/v1/logs/meta' import { checkRateLimit, createRateLimitResponse } from '@/app/api/v1/middleware' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('V1LogDetailsAPI') export const revalidate = 0 export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { - const requestId = crypto.randomUUID().slice(0, 8) + const requestId = generateId().slice(0, 8) try { const rateLimit = await checkRateLimit(request, 'logs-detail') diff --git a/apps/sim/app/api/v1/logs/route.ts b/apps/sim/app/api/v1/logs/route.ts index 83a7b621923..3b69c940731 100644 --- a/apps/sim/app/api/v1/logs/route.ts +++ b/apps/sim/app/api/v1/logs/route.ts @@ -7,6 +7,7 @@ import { z } from 'zod' import { buildLogFilters, getOrderBy } from '@/app/api/v1/logs/filters' import { createApiResponse, getUserLimits } from '@/app/api/v1/logs/meta' import { checkRateLimit, createRateLimitResponse } from '@/app/api/v1/middleware' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('V1LogsAPI') @@ -53,7 +54,7 @@ function decodeCursor(cursor: string): CursorData | null { } export async function GET(request: NextRequest) { - const requestId = crypto.randomUUID().slice(0, 8) + const requestId = generateId().slice(0, 8) try { const rateLimit = await checkRateLimit(request, 'logs') diff --git a/apps/sim/app/api/v1/workflows/[id]/route.ts b/apps/sim/app/api/v1/workflows/[id]/route.ts index 658a0f8ea4d..5625bb4353a 100644 --- a/apps/sim/app/api/v1/workflows/[id]/route.ts +++ b/apps/sim/app/api/v1/workflows/[id]/route.ts @@ -6,13 +6,14 @@ import { type NextRequest, NextResponse } from 'next/server' import { extractInputFieldsFromBlocks } from '@/lib/workflows/input-format' import { createApiResponse, getUserLimits } from '@/app/api/v1/logs/meta' import { checkRateLimit, createRateLimitResponse } from '@/app/api/v1/middleware' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('V1WorkflowDetailsAPI') export const revalidate = 0 export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { - const requestId = crypto.randomUUID().slice(0, 8) + const requestId = generateId().slice(0, 8) try { const rateLimit = await checkRateLimit(request, 'workflow-detail') diff --git a/apps/sim/app/api/v1/workflows/route.ts b/apps/sim/app/api/v1/workflows/route.ts index 23bb707f152..88e43851b22 100644 --- a/apps/sim/app/api/v1/workflows/route.ts +++ b/apps/sim/app/api/v1/workflows/route.ts @@ -6,6 +6,7 @@ import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { createApiResponse, getUserLimits } from '@/app/api/v1/logs/meta' import { checkRateLimit, createRateLimitResponse } from '@/app/api/v1/middleware' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('V1WorkflowsAPI') @@ -39,7 +40,7 @@ function decodeCursor(cursor: string): CursorData | null { } export async function GET(request: NextRequest) { - const requestId = crypto.randomUUID().slice(0, 8) + const requestId = generateId().slice(0, 8) try { const rateLimit = await checkRateLimit(request, 'workflows') diff --git a/apps/sim/app/api/webhooks/route.ts b/apps/sim/app/api/webhooks/route.ts index 4d5508a1256..a59a47b6cf2 100644 --- a/apps/sim/app/api/webhooks/route.ts +++ b/apps/sim/app/api/webhooks/route.ts @@ -23,6 +23,7 @@ import { } from '@/lib/webhooks/utils.server' import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils' import { extractCredentialSetId, isCredentialSetValue } from '@/executor/constants' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('WebhooksAPI') @@ -214,7 +215,7 @@ export async function POST(request: NextRequest) { // If still no path, generate a new dummy path (first-time save) if (!finalPath || finalPath.trim() === '') { - finalPath = `${provider}-${crypto.randomUUID()}` + finalPath = `${provider}-${generateId()}` logger.info(`[${requestId}] Generated webhook path for ${provider} trigger: ${finalPath}`) } } else { diff --git a/apps/sim/app/api/workflows/route.ts b/apps/sim/app/api/workflows/route.ts index 611d808cf61..07c03d2fea0 100644 --- a/apps/sim/app/api/workflows/route.ts +++ b/apps/sim/app/api/workflows/route.ts @@ -9,6 +9,7 @@ import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' import { getUserEntityPermissions, workspaceExists } from '@/lib/workspaces/permissions/utils' import { verifyWorkspaceMembership } from '@/app/api/workflows/utils' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('WorkflowAPI') @@ -140,7 +141,7 @@ export async function POST(req: NextRequest) { ) } - const workflowId = crypto.randomUUID() + const workflowId = generateId() const now = new Date() logger.info(`[${requestId}] Creating workflow ${workflowId} for user ${userId}`) diff --git a/apps/sim/app/api/workspaces/[id]/environment/route.ts b/apps/sim/app/api/workspaces/[id]/environment/route.ts index 25bbe66719f..b068866a642 100644 --- a/apps/sim/app/api/workspaces/[id]/environment/route.ts +++ b/apps/sim/app/api/workspaces/[id]/environment/route.ts @@ -11,6 +11,7 @@ import { generateRequestId } from '@/lib/core/utils/request' import { syncWorkspaceEnvCredentials } from '@/lib/credentials/environment' import { getPersonalAndWorkspaceEnv } from '@/lib/environment/utils' import { getUserEntityPermissions, getWorkspaceById } from '@/lib/workspaces/permissions/utils' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('WorkspaceEnvironmentAPI') @@ -114,7 +115,7 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{ await db .insert(workspaceEnvironment) .values({ - id: crypto.randomUUID(), + id: generateId(), workspaceId, variables: merged, createdAt: new Date(), @@ -199,7 +200,7 @@ export async function DELETE( await db .insert(workspaceEnvironment) .values({ - id: wsRows[0]?.id || crypto.randomUUID(), + id: wsRows[0]?.id || generateId(), workspaceId, variables: current, createdAt: wsRows[0]?.createdAt || new Date(), diff --git a/apps/sim/app/api/workspaces/[id]/permissions/route.ts b/apps/sim/app/api/workspaces/[id]/permissions/route.ts index 067256b3dbd..1bd6582c17d 100644 --- a/apps/sim/app/api/workspaces/[id]/permissions/route.ts +++ b/apps/sim/app/api/workspaces/[id]/permissions/route.ts @@ -9,6 +9,7 @@ import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log' import { getSession } from '@/lib/auth' import { syncWorkspaceEnvCredentials } from '@/lib/credentials/environment' import { +import { generateId } from '@/lib/core/utils/id' getUsersWithPermissions, hasWorkspaceAdminAccess, } from '@/lib/workspaces/permissions/utils' @@ -160,7 +161,7 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise< ) await tx.insert(permissions).values({ - id: crypto.randomUUID(), + id: generateId(), userId: update.userId, entityType: 'workspace' as const, entityId: workspaceId, diff --git a/apps/sim/app/api/workspaces/route.ts b/apps/sim/app/api/workspaces/route.ts index 79c2c436df6..db005aa1ad8 100644 --- a/apps/sim/app/api/workspaces/route.ts +++ b/apps/sim/app/api/workspaces/route.ts @@ -9,6 +9,7 @@ import { getSession } from '@/lib/auth' import { PlatformEvents } from '@/lib/core/telemetry' import { buildDefaultWorkflowArtifacts } from '@/lib/workflows/defaults' import { saveWorkflowToNormalizedTables } from '@/lib/workflows/persistence/utils' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('Workspaces') @@ -97,8 +98,8 @@ async function createDefaultWorkspace(userId: string, userName?: string | null) } async function createWorkspace(userId: string, name: string, skipDefaultWorkflow = false) { - const workspaceId = crypto.randomUUID() - const workflowId = crypto.randomUUID() + const workspaceId = generateId() + const workflowId = generateId() const now = new Date() try { @@ -114,7 +115,7 @@ async function createWorkspace(userId: string, name: string, skipDefaultWorkflow }) await tx.insert(permissions).values({ - id: crypto.randomUUID(), + id: generateId(), entityType: 'workspace' as const, entityId: workspaceId, userId: userId, diff --git a/apps/sim/app/chat/[identifier]/chat.tsx b/apps/sim/app/chat/[identifier]/chat.tsx index 549e450d4a5..3404307580c 100644 --- a/apps/sim/app/chat/[identifier]/chat.tsx +++ b/apps/sim/app/chat/[identifier]/chat.tsx @@ -19,6 +19,7 @@ import { import { CHAT_ERROR_MESSAGES, CHAT_REQUEST_TIMEOUT_MS } from '@/app/chat/constants' import { useAudioStreaming, useChatStreaming } from '@/app/chat/hooks' import SSOAuth from '@/ee/sso/components/sso-auth' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('ChatClient') @@ -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..3c99f1cf885 100644 --- a/apps/sim/app/chat/components/input/input.tsx +++ b/apps/sim/app/chat/components/input/input.tsx @@ -10,6 +10,7 @@ import { VoiceInput } from '@/app/chat/components/input/voice-input' const logger = createLogger('ChatInput') import { createLogger } from '@sim/logger' +import { generateId } from '@/lib/core/utils/id' const PLACEHOLDER_MOBILE = 'Enter a message' const PLACEHOLDER_DESKTOP = 'Enter a message or click the mic to speak' @@ -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..b68c400c398 100644 --- a/apps/sim/app/chat/hooks/use-chat-streaming.ts +++ b/apps/sim/app/chat/hooks/use-chat-streaming.ts @@ -5,6 +5,7 @@ import { createLogger } from '@sim/logger' import { isUserFileWithMetadata } from '@/lib/core/utils/user-file' import type { ChatFile, ChatMessage } from '@/app/chat/components/message/message' import { CHAT_ERROR_MESSAGES } from '@/app/chat/constants' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('UseChatStreaming') @@ -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..6ad90e6d86d 100644 --- a/apps/sim/app/workspace/[workspaceId]/providers/global-commands-provider.tsx +++ b/apps/sim/app/workspace/[workspaceId]/providers/global-commands-provider.tsx @@ -11,6 +11,7 @@ import { } from 'react' import { createLogger } from '@sim/logger' import { useRouter } from 'next/navigation' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('GlobalCommands') @@ -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..43d7cfb61da 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 @@ -57,6 +57,7 @@ import { useTerminalConsoleStore } from '@/stores/terminal' import { useWorkflowRegistry } from '@/stores/workflows/registry/store' import { useSubBlockStore } from '@/stores/workflows/subblock/store' import { useWorkflowStore } from '@/stores/workflows/workflow/store' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('FloatingChat') @@ -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..89719e6b027 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,4 +1,5 @@ import { useCallback, useState } from 'react' +import { generateId } from '@/lib/core/utils/id' export interface ChatFile { id: string @@ -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..175629b7f7a 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 @@ -2,6 +2,7 @@ import { useCallback, useEffect, useRef, useState } from 'react' import { createLogger } from '@sim/logger' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('useFileAttachments') @@ -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..9373918bc89 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 @@ -155,7 +155,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 +166,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 +177,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: '', @@ -554,6 +554,7 @@ export function A2aDeploy({ return requiresAuth ? `import os import requests +import { generateId } from '@/lib/core/utils/id' response = requests.post( "${endpoint}", 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..bcd1dde5187 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 @@ -22,6 +22,7 @@ import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/ import type { SubBlockConfig } from '@/blocks/types' import { useKnowledgeBaseTagDefinitions } from '@/hooks/kb/use-knowledge-base-tag-definitions' import { useTagSelection } from '@/hooks/kb/use-tag-selection' +import { generateId } from '@/lib/core/utils/id' interface DocumentTag { id: string @@ -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..535aecbcca4 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 @@ -12,6 +12,7 @@ import { ResponseBlockHandler } from '@/executor/handlers/response/response-hand import { useWorkflowRegistry } from '@/stores/workflows/registry/store' import { useSubBlockStore } from '@/stores/workflows/subblock/store' import { useWorkflowStore } from '@/stores/workflows/workflow/store' +import { generateId } from '@/lib/core/utils/id' /** * Dropdown option type - can be a simple string or an object with label, id, and optional icon @@ -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..0008970b079 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 @@ -9,6 +9,7 @@ import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/compon import { useSubBlockInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-input' import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value' import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes' +import { generateId } from '@/lib/core/utils/id' interface EvalMetric { id: string @@ -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..6e27d38df02 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 @@ -8,6 +8,7 @@ import { useFilterBuilder } from '@/lib/table/query-builder/use-query-builder' import { useSubBlockInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-input' import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value' import { FilterRuleRow } from './components/filter-rule-row' +import { generateId } from '@/lib/core/utils/id' interface FilterBuilderProps { blockId: string @@ -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..f4c308f070e 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 @@ -23,6 +23,7 @@ import type { SubBlockConfig } from '@/blocks/types' import { useKnowledgeBaseTagDefinitions } from '@/hooks/kb/use-knowledge-base-tag-definitions' import { useTagSelection } from '@/hooks/kb/use-tag-selection' import { useSubBlockValue } from '../../hooks/use-sub-block-value' +import { generateId } from '@/lib/core/utils/id' interface TagFilter { id: string @@ -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..54a271253dd 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 @@ -6,6 +6,7 @@ import { useTableColumns } from '@/lib/table/hooks' import { SORT_DIRECTIONS, type SortRule } from '@/lib/table/query-builder/constants' import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value' import { SortRuleRow } from './components/sort-rule-row' +import { generateId } from '@/lib/core/utils/id' interface SortBuilderProps { blockId: string @@ -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..63b6b3dd9a1 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 @@ -22,6 +22,7 @@ import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/compon import { useSubBlockInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-input' import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value' import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes' +import { generateId } from '@/lib/core/utils/id' interface Field { id: string @@ -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..666a1a2b2fb 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 @@ -11,6 +11,7 @@ import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/compon import { useSubBlockInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-input' import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value' import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('Table') @@ -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..6d584cc6305 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 @@ -21,6 +21,7 @@ import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/c import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes' import type { Variable } from '@/stores/panel' import { useVariablesStore } from '@/stores/panel' +import { generateId } from '@/lib/core/utils/id' interface VariableAssignment { id: string @@ -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..19cf9bf3f2b 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx @@ -87,6 +87,7 @@ import { useWorkflowRegistry } from '@/stores/workflows/registry/store' import { getUniqueBlockName, prepareBlockState } from '@/stores/workflows/utils' import { useWorkflowStore } from '@/stores/workflows/workflow/store' import type { BlockState } from '@/stores/workflows/workflow/types' +import { generateId } from '@/lib/core/utils/id' /** Lazy-loaded components for non-critical UI that can load after initial render */ const LazyChat = lazy(() => @@ -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..a04e24184a1 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 @@ -3,6 +3,7 @@ import { useState } from 'react' import { createLogger } from '@sim/logger' import { +import { generateId } from '@/lib/core/utils/id' Button, Input, Label, @@ -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..8a7509f3c01 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 @@ -12,6 +12,7 @@ import { import { folderKeys, useCreateFolder } from '@/hooks/queries/folders' import { useCreateWorkflow, workflowKeys } from '@/hooks/queries/workflows' import { useWorkflowDiffStore } from '@/stores/workflow-diff/store' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('useImportWorkflow') @@ -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..717afe9f08e 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 @@ -9,6 +9,7 @@ import { } from '@/lib/workflows/operations/import-export' import { useCreateFolder } from '@/hooks/queries/folders' import { useWorkflowDiffStore } from '@/stores/workflow-diff/store' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('useImportWorkspace') @@ -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..849ae4faa9b 100644 --- a/apps/sim/app/workspace/providers/socket-provider.tsx +++ b/apps/sim/app/workspace/providers/socket-provider.tsx @@ -15,6 +15,7 @@ import { useParams } from 'next/navigation' import { io, type Socket } from 'socket.io-client' import { getEnv } from '@/lib/core/config/env' import { useOperationQueueStore } from '@/stores/operation-queue/store' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('SocketContext') @@ -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/executor/handlers/workflow/workflow-handler.ts b/apps/sim/executor/handlers/workflow/workflow-handler.ts index 4821d86029e..67fc1859b4c 100644 --- a/apps/sim/executor/handlers/workflow/workflow-handler.ts +++ b/apps/sim/executor/handlers/workflow/workflow-handler.ts @@ -22,6 +22,7 @@ import { lazyCleanupInputMapping } from '@/executor/utils/lazy-cleanup' import { Serializer } from '@/serializer' import type { SerializedBlock } from '@/serializer/types' import { useWorkflowRegistry } from '@/stores/workflows/registry/store' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('WorkflowBlockHandler') @@ -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 = generateId() 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..849ce8622a8 100644 --- a/apps/sim/hooks/queries/a2a/tasks.ts +++ b/apps/sim/hooks/queries/a2a/tasks.ts @@ -7,6 +7,7 @@ import type { Artifact, Message, TaskState } from '@a2a-js/sdk' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { isTerminalState } from '@/lib/a2a/utils' +import { generateId } from '@/lib/core/utils/id' /** A2A v0.3 JSON-RPC method names */ const A2A_METHODS = { @@ -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..9f7c76ec69e 100644 --- a/apps/sim/hooks/use-code-undo-redo.ts +++ b/apps/sim/hooks/use-code-undo-redo.ts @@ -5,6 +5,7 @@ import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow' import { useCodeUndoRedoStore } from '@/stores/undo-redo' import { useWorkflowDiffStore } from '@/stores/workflow-diff/store' import { useWorkflowRegistry } from '@/stores/workflows/registry/store' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('CodeUndoRedo') @@ -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..3faf0a73572 100644 --- a/apps/sim/hooks/use-collaborative-workflow.ts +++ b/apps/sim/hooks/use-collaborative-workflow.ts @@ -28,6 +28,7 @@ import { filterNewEdges, filterValidEdges, mergeSubblockState } from '@/stores/w import { useWorkflowStore } from '@/stores/workflows/workflow/store' import type { BlockState, Loop, Parallel, Position } from '@/stores/workflows/workflow/types' import { findAllDescendantNodes, isBlockProtected } from '@/stores/workflows/workflow/utils' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('CollaborativeWorkflow') @@ -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..0f6688bfb5e 100644 --- a/apps/sim/hooks/use-undo-redo.ts +++ b/apps/sim/hooks/use-undo-redo.ts @@ -39,6 +39,7 @@ import { import { useWorkflowRegistry } from '@/stores/workflows/registry/store' import { useWorkflowStore } from '@/stores/workflows/workflow/store' import type { BlockState } from '@/stores/workflows/workflow/types' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('UndoRedo') @@ -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/a2a/utils.ts b/apps/sim/lib/a2a/utils.ts index 11d3c7ab516..d51a15477e6 100644 --- a/apps/sim/lib/a2a/utils.ts +++ b/apps/sim/lib/a2a/utils.ts @@ -9,6 +9,7 @@ import { import { createLogger } from '@sim/logger' import { isInternalFileUrl } from '@/lib/uploads/utils/file-utils' import { A2A_TERMINAL_STATES } from './constants' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('A2AUtils') @@ -186,7 +187,7 @@ export function createTextPart(text: string): Part { export function createUserMessage(text: string): Message { return { kind: 'message', - messageId: crypto.randomUUID(), + messageId: generateId(), role: 'user', parts: [{ kind: 'text', text }], } @@ -195,7 +196,7 @@ export function createUserMessage(text: string): Message { export function createAgentMessage(text: string): Message { return { kind: 'message', - messageId: crypto.randomUUID(), + messageId: generateId(), role: 'agent', parts: [{ kind: 'text', text }], } diff --git a/apps/sim/lib/auth/anonymous.ts b/apps/sim/lib/auth/anonymous.ts index 97a06b9565a..35f75bf45a8 100644 --- a/apps/sim/lib/auth/anonymous.ts +++ b/apps/sim/lib/auth/anonymous.ts @@ -3,6 +3,7 @@ import * as schema from '@sim/db/schema' import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { ANONYMOUS_USER, ANONYMOUS_USER_ID } from './constants' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('AnonymousAuth') @@ -36,7 +37,7 @@ export async function ensureAnonymousUserExists(): Promise { if (!existingStats) { await db.insert(schema.userStats).values({ - id: crypto.randomUUID(), + id: generateId(), userId: ANONYMOUS_USER_ID, currentUsageLimit: '10000000000', }) diff --git a/apps/sim/lib/auth/auth.ts b/apps/sim/lib/auth/auth.ts index 1a257bdc7c3..b34fa277811 100644 --- a/apps/sim/lib/auth/auth.ts +++ b/apps/sim/lib/auth/auth.ts @@ -78,6 +78,7 @@ const logger = createLogger('Auth') import { getMicrosoftRefreshTokenExpiry, isMicrosoftProvider } from '@/lib/oauth/microsoft' import { getCanonicalScopesForProvider } from '@/lib/oauth/utils' +import { generateId } from '@/lib/core/utils/id' const validStripeKey = env.STRIPE_SECRET_KEY @@ -370,7 +371,7 @@ export const auth = betterAuth({ } // Sync webhooks for credential sets after connecting a new credential - const requestId = crypto.randomUUID().slice(0, 8) + const requestId = generateId().slice(0, 8) const userMemberships = await db .select({ credentialSetId: schema.credentialSetMember.credentialSetId, @@ -816,7 +817,7 @@ export const auth = betterAuth({ const now = new Date() return { - id: `${profile.id.toString()}-${crypto.randomUUID()}`, + id: `${profile.id.toString()}-${generateId()}`, name: profile.name || profile.login, email: profile.email, image: profile.avatar_url, @@ -854,7 +855,7 @@ export const auth = betterAuth({ const profile = await response.json() const now = new Date() return { - id: `${profile.sub}-${crypto.randomUUID()}`, + id: `${profile.sub}-${generateId()}`, name: profile.name || 'Google User', email: profile.email, image: profile.picture || undefined, @@ -890,7 +891,7 @@ export const auth = betterAuth({ const profile = await response.json() const now = new Date() return { - id: `${profile.sub}-${crypto.randomUUID()}`, + id: `${profile.sub}-${generateId()}`, name: profile.name || 'Google User', email: profile.email, image: profile.picture || undefined, @@ -926,7 +927,7 @@ export const auth = betterAuth({ const profile = await response.json() const now = new Date() return { - id: `${profile.sub}-${crypto.randomUUID()}`, + id: `${profile.sub}-${generateId()}`, name: profile.name || 'Google User', email: profile.email, image: profile.picture || undefined, @@ -962,7 +963,7 @@ export const auth = betterAuth({ const profile = await response.json() const now = new Date() return { - id: `${profile.sub}-${crypto.randomUUID()}`, + id: `${profile.sub}-${generateId()}`, name: profile.name || 'Google User', email: profile.email, image: profile.picture || undefined, @@ -998,7 +999,7 @@ export const auth = betterAuth({ const profile = await response.json() const now = new Date() return { - id: `${profile.sub}-${crypto.randomUUID()}`, + id: `${profile.sub}-${generateId()}`, name: profile.name || 'Google User', email: profile.email, image: profile.picture || undefined, @@ -1035,7 +1036,7 @@ export const auth = betterAuth({ const profile = await response.json() const now = new Date() return { - id: `${profile.sub}-${crypto.randomUUID()}`, + id: `${profile.sub}-${generateId()}`, name: profile.name || 'Google User', email: profile.email, image: profile.picture || undefined, @@ -1071,7 +1072,7 @@ export const auth = betterAuth({ const profile = await response.json() const now = new Date() return { - id: `${profile.sub}-${crypto.randomUUID()}`, + id: `${profile.sub}-${generateId()}`, name: profile.name || 'Google User', email: profile.email, image: profile.picture || undefined, @@ -1107,7 +1108,7 @@ export const auth = betterAuth({ const profile = await response.json() const now = new Date() return { - id: `${profile.sub}-${crypto.randomUUID()}`, + id: `${profile.sub}-${generateId()}`, name: profile.name || 'Google User', email: profile.email, image: profile.picture || undefined, @@ -1144,7 +1145,7 @@ export const auth = betterAuth({ const profile = await response.json() const now = new Date() return { - id: `${profile.sub}-${crypto.randomUUID()}`, + id: `${profile.sub}-${generateId()}`, name: profile.name || 'Google User', email: profile.email, image: profile.picture || undefined, @@ -1181,7 +1182,7 @@ export const auth = betterAuth({ const profile = await response.json() const now = new Date() return { - id: `${profile.sub}-${crypto.randomUUID()}`, + id: `${profile.sub}-${generateId()}`, name: profile.name || 'Google User', email: profile.email, image: profile.picture || undefined, @@ -1218,7 +1219,7 @@ export const auth = betterAuth({ const profile = await response.json() const now = new Date() return { - id: `${profile.sub}-${crypto.randomUUID()}`, + id: `${profile.sub}-${generateId()}`, name: profile.name || 'Google User', email: profile.email, image: profile.picture || undefined, @@ -1254,7 +1255,7 @@ export const auth = betterAuth({ const profile = await response.json() const now = new Date() return { - id: `${profile.sub}-${crypto.randomUUID()}`, + id: `${profile.sub}-${generateId()}`, name: profile.name || 'Google User', email: profile.email, image: profile.picture || undefined, @@ -1291,7 +1292,7 @@ export const auth = betterAuth({ const profile = await response.json() const now = new Date() return { - id: `${profile.sub}-${crypto.randomUUID()}`, + id: `${profile.sub}-${generateId()}`, name: profile.name || 'Google User', email: profile.email, image: profile.picture || undefined, @@ -1332,7 +1333,7 @@ export const auth = betterAuth({ const profile = await response.json() const now = new Date() return { - id: `${profile.id}-${crypto.randomUUID()}`, + id: `${profile.id}-${generateId()}`, name: profile.displayName || 'Microsoft User', email: profile.mail || profile.userPrincipalName, emailVerified: true, @@ -1372,7 +1373,7 @@ export const auth = betterAuth({ const profile = await response.json() const now = new Date() return { - id: `${profile.id}-${crypto.randomUUID()}`, + id: `${profile.id}-${generateId()}`, name: profile.displayName || 'Microsoft User', email: profile.mail || profile.userPrincipalName, emailVerified: true, @@ -1418,7 +1419,7 @@ export const auth = betterAuth({ const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf-8')) const now = new Date() return { - id: `${payload.oid || payload.sub}-${crypto.randomUUID()}`, + id: `${payload.oid || payload.sub}-${generateId()}`, name: payload.name || 'Microsoft User', email: payload.preferred_username || payload.email || payload.upn, emailVerified: true, @@ -1453,7 +1454,7 @@ export const auth = betterAuth({ const profile = await response.json() const now = new Date() return { - id: `${profile.id}-${crypto.randomUUID()}`, + id: `${profile.id}-${generateId()}`, name: profile.displayName || 'Microsoft User', email: profile.mail || profile.userPrincipalName, emailVerified: true, @@ -1493,7 +1494,7 @@ export const auth = betterAuth({ const profile = await response.json() const now = new Date() return { - id: `${profile.id}-${crypto.randomUUID()}`, + id: `${profile.id}-${generateId()}`, name: profile.displayName || 'Microsoft User', email: profile.mail || profile.userPrincipalName, emailVerified: true, @@ -1533,7 +1534,7 @@ export const auth = betterAuth({ const profile = await response.json() const now = new Date() return { - id: `${profile.id}-${crypto.randomUUID()}`, + id: `${profile.id}-${generateId()}`, name: profile.displayName || 'Microsoft User', email: profile.mail || profile.userPrincipalName, emailVerified: true, @@ -1573,7 +1574,7 @@ export const auth = betterAuth({ const profile = await response.json() const now = new Date() return { - id: `${profile.id}-${crypto.randomUUID()}`, + id: `${profile.id}-${generateId()}`, name: profile.displayName || 'Microsoft User', email: profile.mail || profile.userPrincipalName, emailVerified: true, @@ -1605,7 +1606,7 @@ export const auth = betterAuth({ const now = new Date() return { - id: `${uniqueId}-${crypto.randomUUID()}`, + id: `${uniqueId}-${generateId()}`, name: 'Wealthbox User', email: `${uniqueId}@wealthbox.user`, emailVerified: false, @@ -1652,7 +1653,7 @@ export const auth = betterAuth({ const user = data.data return { - id: `${user.id.toString()}-${crypto.randomUUID()}`, + id: `${user.id.toString()}-${generateId()}`, name: user.name, email: user.email, emailVerified: user.activated, @@ -1720,7 +1721,7 @@ export const auth = betterAuth({ }) return { - id: `${(data.user_id || data.hub_id).toString()}-${crypto.randomUUID()}`, + id: `${(data.user_id || data.hub_id).toString()}-${generateId()}`, name: data.user || 'HubSpot User', email: data.user || `hubspot-${data.hub_id}@hubspot.com`, emailVerified: true, @@ -1775,7 +1776,7 @@ export const auth = betterAuth({ const data = await response.json() return { - id: `${(data.user_id || data.sub).toString()}-${crypto.randomUUID()}`, + id: `${(data.user_id || data.sub).toString()}-${generateId()}`, name: data.name || 'Salesforce User', email: data.email || `salesforce-${data.user_id}@salesforce.com`, emailVerified: data.email_verified || true, @@ -1835,7 +1836,7 @@ export const auth = betterAuth({ const now = new Date() return { - id: `${profile.data.id.toString()}-${crypto.randomUUID()}`, + id: `${profile.data.id.toString()}-${generateId()}`, name: profile.data.name || 'X User', email: `${profile.data.username}@x.com`, image: profile.data.profile_image_url, @@ -1887,7 +1888,7 @@ export const auth = betterAuth({ const now = new Date() return { - id: `${profile.account_id.toString()}-${crypto.randomUUID()}`, + id: `${profile.account_id.toString()}-${generateId()}`, name: profile.name || profile.display_name || 'Confluence User', email: profile.email || `${profile.account_id}@atlassian.com`, image: profile.picture || undefined, @@ -1939,7 +1940,7 @@ export const auth = betterAuth({ const now = new Date() return { - id: `${profile.account_id.toString()}-${crypto.randomUUID()}`, + id: `${profile.account_id.toString()}-${generateId()}`, name: profile.name || profile.display_name || 'Jira User', email: profile.email || `${profile.account_id}@atlassian.com`, image: profile.picture || undefined, @@ -1990,7 +1991,7 @@ export const auth = betterAuth({ const now = new Date() return { - id: `${data.id.toString()}-${crypto.randomUUID()}`, + id: `${data.id.toString()}-${generateId()}`, name: data.email ? data.email.split('@')[0] : 'Airtable User', email: data.email || `${data.id}@airtable.user`, emailVerified: !!data.email, @@ -2040,7 +2041,7 @@ export const auth = betterAuth({ const now = new Date() return { - id: `${(profile.bot?.owner?.user?.id || profile.id).toString()}-${crypto.randomUUID()}`, + id: `${(profile.bot?.owner?.user?.id || profile.id).toString()}-${generateId()}`, name: profile.name || profile.bot?.owner?.user?.name || 'Notion User', email: profile.person?.email || `${profile.id}@notion.user`, emailVerified: !!profile.person?.email, @@ -2091,7 +2092,7 @@ export const auth = betterAuth({ const now = new Date() return { - id: `${data.id.toString()}-${crypto.randomUUID()}`, + id: `${data.id.toString()}-${generateId()}`, name: data.name || 'Reddit User', email: `${data.name}@reddit.user`, image: data.icon_img || undefined, @@ -2163,7 +2164,7 @@ export const auth = betterAuth({ const viewer = data.viewer return { - id: `${viewer.id.toString()}-${crypto.randomUUID()}`, + id: `${viewer.id.toString()}-${generateId()}`, email: viewer.email, name: viewer.name, emailVerified: true, @@ -2214,7 +2215,7 @@ export const auth = betterAuth({ const member = data[0] return { - id: `${member.id.workspace_member_id}-${crypto.randomUUID()}`, + id: `${member.id.workspace_member_id}-${generateId()}`, email: member.email_address, name: `${member.first_name ?? ''} ${member.last_name ?? ''}`.trim() || @@ -2271,7 +2272,7 @@ export const auth = betterAuth({ const data = await response.json() return { - id: `${data.account_id.toString()}-${crypto.randomUUID()}`, + id: `${data.account_id.toString()}-${generateId()}`, email: data.email, name: data.name?.display_name || data.email, emailVerified: data.email_verified || false, @@ -2323,7 +2324,7 @@ export const auth = betterAuth({ const now = new Date() return { - id: `${profile.gid.toString()}-${crypto.randomUUID()}`, + id: `${profile.gid.toString()}-${generateId()}`, name: profile.name || 'Asana User', email: profile.email || `${profile.gid}@asana.user`, image: profile.photo?.image_128x128 || undefined, @@ -2384,7 +2385,7 @@ export const auth = betterAuth({ logger.info('Slack credential identifier', { teamId, userId, uniqueId, teamName }) return { - id: `${uniqueId}-${crypto.randomUUID()}`, + id: `${uniqueId}-${generateId()}`, name: teamName, email: `${teamId}-${userId}@slack.bot`, emailVerified: false, @@ -2435,7 +2436,7 @@ export const auth = betterAuth({ const uniqueId = `webflow-${userId}` return { - id: `${uniqueId}-${crypto.randomUUID()}`, + id: `${uniqueId}-${generateId()}`, name: data.user_name || 'Webflow User', email: `${uniqueId.replace(/[^a-zA-Z0-9]/g, '')}@webflow.user`, emailVerified: false, @@ -2483,7 +2484,7 @@ export const auth = betterAuth({ const profile = await response.json() return { - id: `${profile.sub}-${crypto.randomUUID()}`, + id: `${profile.sub}-${generateId()}`, name: profile.name || 'LinkedIn User', email: profile.email || `${profile.sub}@linkedin.user`, emailVerified: profile.email_verified || true, @@ -2534,7 +2535,7 @@ export const auth = betterAuth({ const profile = await response.json() return { - id: `${profile.id.toString()}-${crypto.randomUUID()}`, + id: `${profile.id.toString()}-${generateId()}`, name: `${profile.first_name || ''} ${profile.last_name || ''}`.trim() || 'Zoom User', email: profile.email || `${profile.id}@zoom.user`, @@ -2584,7 +2585,7 @@ export const auth = betterAuth({ const profile = await response.json() return { - id: `${profile.id.toString()}-${crypto.randomUUID()}`, + id: `${profile.id.toString()}-${generateId()}`, name: profile.display_name || 'Spotify User', email: profile.email || `${profile.id}@spotify.user`, emailVerified: true, @@ -2633,7 +2634,7 @@ export const auth = betterAuth({ const profile = await response.json() return { - id: `${profile.ID?.toString() || profile.id?.toString()}-${crypto.randomUUID()}`, + id: `${profile.ID?.toString() || profile.id?.toString()}-${generateId()}`, name: profile.display_name || profile.username || 'WordPress User', email: profile.email || `${profile.username}@wordpress.com`, emailVerified: profile.email_verified || false, @@ -2684,7 +2685,7 @@ export const auth = betterAuth({ const profile = data.data || data return { - id: `${profile.id?.toString()}-${crypto.randomUUID()}`, + id: `${profile.id?.toString()}-${generateId()}`, name: profile.name || 'Cal.com User', email: profile.email || `${profile.id}@cal.com`, emailVerified: true, diff --git a/apps/sim/lib/billing/core/usage-log.ts b/apps/sim/lib/billing/core/usage-log.ts index b21fb552f7a..0998906853f 100644 --- a/apps/sim/lib/billing/core/usage-log.ts +++ b/apps/sim/lib/billing/core/usage-log.ts @@ -3,6 +3,7 @@ import { usageLog } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { and, desc, eq, gte, lte, sql } from 'drizzle-orm' import { isBillingEnabled } from '@/lib/core/config/feature-flags' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('UsageLog') @@ -77,7 +78,7 @@ export async function logModelUsage(params: LogModelUsageParams): Promise } await db.insert(usageLog).values({ - id: crypto.randomUUID(), + id: generateId(), userId: params.userId, category: 'model', source: params.source, @@ -114,7 +115,7 @@ export async function logFixedUsage(params: LogFixedUsageParams): Promise try { await db.insert(usageLog).values({ - id: crypto.randomUUID(), + id: generateId(), userId: params.userId, category: 'fixed', source: params.source, @@ -182,7 +183,7 @@ export async function logWorkflowUsageBatch(params: LogWorkflowUsageBatchParams) if (params.baseExecutionCharge && params.baseExecutionCharge > 0) { entries.push({ - id: crypto.randomUUID(), + id: generateId(), userId: params.userId, category: 'fixed', source: 'workflow', @@ -199,7 +200,7 @@ export async function logWorkflowUsageBatch(params: LogWorkflowUsageBatchParams) for (const [modelName, modelData] of Object.entries(params.models)) { if (modelData.total > 0) { entries.push({ - id: crypto.randomUUID(), + id: generateId(), userId: params.userId, category: 'model', source: 'workflow', diff --git a/apps/sim/lib/billing/core/usage.ts b/apps/sim/lib/billing/core/usage.ts index e5bf1354d19..25d26987e37 100644 --- a/apps/sim/lib/billing/core/usage.ts +++ b/apps/sim/lib/billing/core/usage.ts @@ -23,6 +23,7 @@ import { isBillingEnabled } from '@/lib/core/config/feature-flags' import { getBaseUrl } from '@/lib/core/utils/urls' import { sendEmail } from '@/lib/messaging/email/mailer' import { getEmailPreferences } from '@/lib/messaging/email/unsubscribe' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('UsageManagement') @@ -83,7 +84,7 @@ export async function getOrgUsageLimit( export async function handleNewUser(userId: string): Promise { try { await db.insert(userStats).values({ - id: crypto.randomUUID(), + id: generateId(), userId: userId, currentUsageLimit: getFreeTierLimit().toString(), usageLimitUpdatedAt: new Date(), @@ -110,7 +111,7 @@ export async function ensureUserStatsExists(userId: string): Promise { await db .insert(userStats) .values({ - id: crypto.randomUUID(), + id: generateId(), userId: userId, currentUsageLimit: getFreeTierLimit().toString(), usageLimitUpdatedAt: new Date(), @@ -272,7 +273,7 @@ export async function initializeUserUsageLimit(userId: string): Promise { // Create initial usage stats await db.insert(userStats).values({ - id: crypto.randomUUID(), + id: generateId(), userId, // Team/enterprise: null (use org limit), Free/Pro: individual limit currentUsageLimit: isTeamOrEnterprise ? null : getFreeTierLimit().toString(), diff --git a/apps/sim/lib/billing/organization.ts b/apps/sim/lib/billing/organization.ts index a3b4e981814..8fc2725ae54 100644 --- a/apps/sim/lib/billing/organization.ts +++ b/apps/sim/lib/billing/organization.ts @@ -11,6 +11,7 @@ import { and, eq } from 'drizzle-orm' import { hasActiveSubscription } from '@/lib/billing' import { getPlanPricing } from '@/lib/billing/core/billing' import { syncUsageLimitsFromSubscription } from '@/lib/billing/core/usage' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('BillingOrganization') @@ -56,7 +57,7 @@ async function createOrganizationWithOwner( organizationSlug: string, metadata: Record = {} ): Promise { - const orgId = `org_${crypto.randomUUID()}` + const orgId = `org_${generateId()}` let sessionsUpdated = 0 await db.transaction(async (tx) => { @@ -68,7 +69,7 @@ async function createOrganizationWithOwner( }) await tx.insert(member).values({ - id: crypto.randomUUID(), + id: generateId(), userId: userId, organizationId: orgId, role: 'owner', diff --git a/apps/sim/lib/billing/webhooks/enterprise.ts b/apps/sim/lib/billing/webhooks/enterprise.ts index cf20b52b395..f700e189a03 100644 --- a/apps/sim/lib/billing/webhooks/enterprise.ts +++ b/apps/sim/lib/billing/webhooks/enterprise.ts @@ -7,6 +7,7 @@ import { getEmailSubject, renderEnterpriseSubscriptionEmail } from '@/components import { sendEmail } from '@/lib/messaging/email/mailer' import { getFromEmailAddress } from '@/lib/messaging/email/utils' import type { EnterpriseSubscriptionMetadata } from '../types' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('BillingEnterprise') @@ -99,7 +100,7 @@ export async function handleManualEnterpriseSubscription(event: Stripe.Event) { const referenceItem = stripeSubscription.items?.data?.[0] const subscriptionRow = { - id: crypto.randomUUID(), + id: generateId(), plan: 'enterprise', referenceId, stripeCustomerId, diff --git a/apps/sim/lib/copilot/client-sse/content-blocks.ts b/apps/sim/lib/copilot/client-sse/content-blocks.ts index 0e77882524f..67ef82dbab0 100644 --- a/apps/sim/lib/copilot/client-sse/content-blocks.ts +++ b/apps/sim/lib/copilot/client-sse/content-blocks.ts @@ -4,6 +4,7 @@ import type { MessageFileAttachment, } from '@/stores/panel/copilot/types' import type { ClientContentBlock, ClientStreamingContext } from './types' +import { generateId } from '@/lib/core/utils/id' const TEXT_BLOCK_TYPE = 'text' const THINKING_BLOCK_TYPE = 'thinking' @@ -16,7 +17,7 @@ export function createUserMessage( messageId?: string ): CopilotMessage { return { - id: messageId || crypto.randomUUID(), + id: messageId || generateId(), role: 'user', content, timestamp: new Date().toISOString(), @@ -31,7 +32,7 @@ export function createUserMessage( export function createStreamingMessage(): CopilotMessage { return { - id: crypto.randomUUID(), + id: generateId(), role: 'assistant', content: '', timestamp: new Date().toISOString(), diff --git a/apps/sim/lib/copilot/orchestrator/index.ts b/apps/sim/lib/copilot/orchestrator/index.ts index 9540027cd58..43744882229 100644 --- a/apps/sim/lib/copilot/orchestrator/index.ts +++ b/apps/sim/lib/copilot/orchestrator/index.ts @@ -4,6 +4,7 @@ import { prepareExecutionContext } from '@/lib/copilot/orchestrator/tool-executo import type { OrchestratorOptions, OrchestratorResult } from '@/lib/copilot/orchestrator/types' import { env } from '@/lib/core/config/env' import { buildToolCallSummaries, createStreamingContext, runStreamLoop } from './stream-core' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('CopilotOrchestrator') @@ -23,7 +24,7 @@ export async function orchestrateCopilotStream( const payloadMsgId = requestPayload?.messageId const context = createStreamingContext({ chatId, - messageId: typeof payloadMsgId === 'string' ? payloadMsgId : crypto.randomUUID(), + messageId: typeof payloadMsgId === 'string' ? payloadMsgId : generateId(), }) try { diff --git a/apps/sim/lib/copilot/orchestrator/stream-core.ts b/apps/sim/lib/copilot/orchestrator/stream-core.ts index e1dc2e2fc3c..52bfcb44eef 100644 --- a/apps/sim/lib/copilot/orchestrator/stream-core.ts +++ b/apps/sim/lib/copilot/orchestrator/stream-core.ts @@ -12,6 +12,7 @@ import { shouldSkipToolResultEvent, } from '@/lib/copilot/orchestrator/sse-utils' import type { +import { generateId } from '@/lib/core/utils/id' ExecutionContext, OrchestratorOptions, SSEEvent, @@ -39,7 +40,7 @@ export function createStreamingContext(overrides?: Partial): S return { chatId: undefined, conversationId: undefined, - messageId: crypto.randomUUID(), + messageId: generateId(), accumulatedContent: '', contentBlocks: [], toolCalls: new Map(), diff --git a/apps/sim/lib/copilot/orchestrator/subagent.ts b/apps/sim/lib/copilot/orchestrator/subagent.ts index d997fcbb919..5f06ec50d7d 100644 --- a/apps/sim/lib/copilot/orchestrator/subagent.ts +++ b/apps/sim/lib/copilot/orchestrator/subagent.ts @@ -11,6 +11,7 @@ import type { import { env } from '@/lib/core/config/env' import { getEffectiveDecryptedEnv } from '@/lib/environment/utils' import { buildToolCallSummaries, createStreamingContext, runStreamLoop } from './stream-core' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('CopilotSubagentOrchestrator') @@ -45,7 +46,7 @@ export async function orchestrateSubagentStream( const msgId = requestPayload?.messageId const context = createStreamingContext({ - messageId: typeof msgId === 'string' ? msgId : crypto.randomUUID(), + messageId: typeof msgId === 'string' ? msgId : generateId(), }) let structuredResult: SubagentOrchestratorResult['structuredResult'] diff --git a/apps/sim/lib/copilot/orchestrator/tool-executor/deployment-tools/deploy.ts b/apps/sim/lib/copilot/orchestrator/tool-executor/deployment-tools/deploy.ts index 7e1607f09eb..c2dd3fe7cd4 100644 --- a/apps/sim/lib/copilot/orchestrator/tool-executor/deployment-tools/deploy.ts +++ b/apps/sim/lib/copilot/orchestrator/tool-executor/deployment-tools/deploy.ts @@ -11,6 +11,7 @@ import { deployWorkflow, undeployWorkflow } from '@/lib/workflows/persistence/ut import { checkChatAccess, checkWorkflowAccessForChatCreation } from '@/app/api/chat/utils' import { ensureWorkflowAccess } from '../access' import type { DeployApiParams, DeployChatParams, DeployMcpParams } from '../param-types' +import { generateId } from '@/lib/core/utils/id' export async function executeDeployApi( params: DeployApiParams, @@ -165,7 +166,7 @@ export async function executeDeployChat( .where(eq(chat.id, existingDeployment.id)) } else { await db.insert(chat).values({ - id: crypto.randomUUID(), + id: generateId(), workflowId, userId: context.userId, identifier: payload.identifier, @@ -275,7 +276,7 @@ export async function executeDeployMcp( } } - const toolId = crypto.randomUUID() + const toolId = generateId() await db.insert(workflowMcpTool).values({ id: toolId, serverId, diff --git a/apps/sim/lib/copilot/orchestrator/tool-executor/deployment-tools/manage.ts b/apps/sim/lib/copilot/orchestrator/tool-executor/deployment-tools/manage.ts index c93ccea8bf6..c6af68fbdbc 100644 --- a/apps/sim/lib/copilot/orchestrator/tool-executor/deployment-tools/manage.ts +++ b/apps/sim/lib/copilot/orchestrator/tool-executor/deployment-tools/manage.ts @@ -9,6 +9,7 @@ import { sanitizeToolName } from '@/lib/mcp/workflow-tool-schema' import { hasValidStartBlock } from '@/lib/workflows/triggers/trigger-utils.server' import { ensureWorkflowAccess } from '../access' import type { +import { generateId } from '@/lib/core/utils/id' CheckDeploymentStatusParams, CreateWorkspaceMcpServerParams, ListWorkspaceMcpServersParams, @@ -177,7 +178,7 @@ export async function executeCreateWorkspaceMcpServer( return { success: false, error: 'name is required' } } - const serverId = crypto.randomUUID() + const serverId = generateId() const [server] = await db .insert(workflowMcpServer) .values({ @@ -209,7 +210,7 @@ export async function executeCreateWorkspaceMcpServer( const toolName = sanitizeToolName(wf.name || `workflow_${wf.id}`) const parameterSchema = await generateParameterSchemaForWorkflow(wf.id) await db.insert(workflowMcpTool).values({ - id: crypto.randomUUID(), + id: generateId(), serverId, workflowId: wf.id, toolName, diff --git a/apps/sim/lib/copilot/orchestrator/tool-executor/workflow-tools/mutations.ts b/apps/sim/lib/copilot/orchestrator/tool-executor/workflow-tools/mutations.ts index 440de0ec3f1..8a12bd3dbce 100644 --- a/apps/sim/lib/copilot/orchestrator/tool-executor/workflow-tools/mutations.ts +++ b/apps/sim/lib/copilot/orchestrator/tool-executor/workflow-tools/mutations.ts @@ -16,6 +16,7 @@ import { import { saveWorkflowToNormalizedTables } from '@/lib/workflows/persistence/utils' import { ensureWorkflowAccess, ensureWorkspaceAccess, getDefaultWorkspaceId } from '../access' import type { +import { generateId } from '@/lib/core/utils/id' CreateFolderParams, CreateWorkflowParams, GenerateApiKeyParams, @@ -54,7 +55,7 @@ export async function executeCreateWorkflow( await ensureWorkspaceAccess(workspaceId, context.userId, true) - const workflowId = crypto.randomUUID() + const workflowId = generateId() const now = new Date() const folderCondition = folderId ? eq(workflow.folderId, folderId) : isNull(workflow.folderId) @@ -130,7 +131,7 @@ export async function executeCreateFolder( ) const sortOrder = (maxResult?.maxOrder ?? 0) + 1 - const folderId = crypto.randomUUID() + const folderId = generateId() await db.insert(workflowFolder).values({ id: folderId, userId: context.userId, @@ -259,7 +260,7 @@ export async function executeSetGlobalWorkflowVariables( const typedValue = coerceValue(op.value, nextType) if (op.operation === 'add') { byName[key] = { - id: crypto.randomUUID(), + id: generateId(), workflowId, name: key, type: nextType, @@ -270,7 +271,7 @@ export async function executeSetGlobalWorkflowVariables( if (op.operation === 'edit') { if (!byName[key]) { byName[key] = { - id: crypto.randomUUID(), + id: generateId(), workflowId, name: key, type: nextType, diff --git a/apps/sim/lib/copilot/request-helpers.ts b/apps/sim/lib/copilot/request-helpers.ts index 01b46e9f721..b09daefdee8 100644 --- a/apps/sim/lib/copilot/request-helpers.ts +++ b/apps/sim/lib/copilot/request-helpers.ts @@ -1,6 +1,7 @@ import { NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' +import { generateId } from '@/lib/core/utils/id' export type { NotificationStatus } from '@/lib/copilot/types' @@ -26,7 +27,7 @@ export function createInternalServerErrorResponse(message: string): NextResponse } export function createRequestId(): string { - return crypto.randomUUID() + return generateId() } export function createShortRequestId(): string { diff --git a/apps/sim/lib/copilot/tools/server/knowledge/knowledge-base.ts b/apps/sim/lib/copilot/tools/server/knowledge/knowledge-base.ts index 5b00c421d73..955c55e59cb 100644 --- a/apps/sim/lib/copilot/tools/server/knowledge/knowledge-base.ts +++ b/apps/sim/lib/copilot/tools/server/knowledge/knowledge-base.ts @@ -16,6 +16,7 @@ import { updateTagDefinition, } from '@/lib/knowledge/tags/service' import { getQueryStrategy, handleVectorOnlySearch } from '@/app/api/knowledge/search/utils' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('KnowledgeBaseServerTool') @@ -52,7 +53,7 @@ export const knowledgeBaseServerTool: BaseServerTool { - const jobId = `run_${crypto.randomUUID().replace(/-/g, '').slice(0, 20)}` + const jobId = `run_${generateId().replace(/-/g, '').slice(0, 20)}` const now = new Date() await db.insert(asyncJobs).values({ diff --git a/apps/sim/lib/core/async-jobs/backends/redis.ts b/apps/sim/lib/core/async-jobs/backends/redis.ts index 6a361d0e9c0..1f5c57c295f 100644 --- a/apps/sim/lib/core/async-jobs/backends/redis.ts +++ b/apps/sim/lib/core/async-jobs/backends/redis.ts @@ -1,6 +1,7 @@ import { createLogger } from '@sim/logger' import type Redis from 'ioredis' import { +import { generateId } from '@/lib/core/utils/id' type EnqueueOptions, JOB_MAX_LIFETIME_SECONDS, JOB_RETENTION_SECONDS, @@ -72,7 +73,7 @@ export class RedisJobQueue implements JobQueueBackend { payload: TPayload, options?: EnqueueOptions ): Promise { - const jobId = `run_${crypto.randomUUID().replace(/-/g, '').slice(0, 20)}` + const jobId = `run_${generateId().replace(/-/g, '').slice(0, 20)}` const now = new Date() const job: Job = { diff --git a/apps/sim/lib/core/utils/id.ts b/apps/sim/lib/core/utils/id.ts new file mode 100644 index 00000000000..b20170ddd3f --- /dev/null +++ b/apps/sim/lib/core/utils/id.ts @@ -0,0 +1,19 @@ +import { v4 as uuidv4 } from 'uuid' + +/** + * Generate a UUID that works in all environments. + * + * `crypto.randomUUID()` is only available in secure contexts (HTTPS or localhost). + * When the app is served over plain HTTP (e.g. self-hosted Docker on a LAN), + * it throws "crypto.randomUUID is not a function" and the page white-screens. + * + * This helper tries the native API first and falls back to the `uuid` package. + * + * @see https://github.com/simstudioai/sim/issues/3393 + */ +export function generateId(): string { + if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { + return crypto.randomUUID() + } + return uuidv4() +} diff --git a/apps/sim/lib/core/utils/request.ts b/apps/sim/lib/core/utils/request.ts index 6e64b9b108e..0194afb3fa6 100644 --- a/apps/sim/lib/core/utils/request.ts +++ b/apps/sim/lib/core/utils/request.ts @@ -1,8 +1,9 @@ +import { generateId } from '@/lib/core/utils/id' /** * Generate a short request ID for correlation */ export function generateRequestId(): string { - return crypto.randomUUID().slice(0, 8) + return generateId().slice(0, 8) } /** diff --git a/apps/sim/lib/credentials/access.ts b/apps/sim/lib/credentials/access.ts index 18573879ac2..8ed7e212b38 100644 --- a/apps/sim/lib/credentials/access.ts +++ b/apps/sim/lib/credentials/access.ts @@ -3,6 +3,7 @@ import { credential, credentialMember, workspace } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { and, eq, inArray, ne } from 'drizzle-orm' import { checkWorkspaceAccess } from '@/lib/workspaces/permissions/utils' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('CredentialAccess') @@ -133,7 +134,7 @@ export async function revokeWorkspaceCredentialMemberships( .where(eq(credentialMember.id, existingOwnerMembership.id)) } else { await db.insert(credentialMember).values({ - id: crypto.randomUUID(), + id: generateId(), credentialId: credId, userId: ownerId, role: 'admin', diff --git a/apps/sim/lib/credentials/draft-hooks.ts b/apps/sim/lib/credentials/draft-hooks.ts index 8f852210f6f..603ca837c77 100644 --- a/apps/sim/lib/credentials/draft-hooks.ts +++ b/apps/sim/lib/credentials/draft-hooks.ts @@ -2,6 +2,7 @@ import { db } from '@sim/db' import * as schema from '@sim/db/schema' import { createLogger } from '@sim/logger' import { and, eq, sql } from 'drizzle-orm' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('CredentialDraftHooks') @@ -16,7 +17,7 @@ export async function handleCreateCredentialFromDraft(params: { now: Date }) { const { draft, accountId, providerId, userId, now } = params - const credentialId = crypto.randomUUID() + const credentialId = generateId() try { await db.insert(schema.credential).values({ @@ -33,7 +34,7 @@ export async function handleCreateCredentialFromDraft(params: { }) await db.insert(schema.credentialMember).values({ - id: crypto.randomUUID(), + id: generateId(), credentialId, userId, role: 'admin', diff --git a/apps/sim/lib/credentials/environment.ts b/apps/sim/lib/credentials/environment.ts index 9010461e52a..0718c9e1ae9 100644 --- a/apps/sim/lib/credentials/environment.ts +++ b/apps/sim/lib/credentials/environment.ts @@ -1,6 +1,7 @@ import { db } from '@sim/db' import { credential, credentialMember, permissions, workspace } from '@sim/db/schema' import { and, eq, inArray, notInArray } from 'drizzle-orm' +import { generateId } from '@/lib/core/utils/id' interface AccessibleEnvCredential { type: 'env_workspace' | 'env_personal' @@ -82,7 +83,7 @@ async function upsertCredentialAdminMember(credentialId: string, adminUserId: st } await db.insert(credentialMember).values({ - id: crypto.randomUUID(), + id: generateId(), credentialId, userId: adminUserId, role: 'admin', @@ -141,7 +142,7 @@ async function ensureWorkspaceCredentialMemberships( } await db.insert(credentialMember).values({ - id: crypto.randomUUID(), + id: generateId(), credentialId, userId: memberUserId, role: targetRole, @@ -193,7 +194,7 @@ export async function syncWorkspaceEnvCredentials(params: { continue } - const createdId = crypto.randomUUID() + const createdId = generateId() try { await db.insert(credential).values({ id: createdId, @@ -273,7 +274,7 @@ export async function syncPersonalEnvCredentialsForUser(params: { continue } - const createdId = crypto.randomUUID() + const createdId = generateId() try { await db.insert(credential).values({ id: createdId, diff --git a/apps/sim/lib/credentials/oauth.ts b/apps/sim/lib/credentials/oauth.ts index 8969c2301a6..4d13ed51d79 100644 --- a/apps/sim/lib/credentials/oauth.ts +++ b/apps/sim/lib/credentials/oauth.ts @@ -2,6 +2,7 @@ import { db } from '@sim/db' import { account, credential, credentialMember } from '@sim/db/schema' import { and, eq, inArray, notInArray } from 'drizzle-orm' import { getServiceConfigByProviderId } from '@/lib/oauth' +import { generateId } from '@/lib/core/utils/id' /** Provider IDs that are not real OAuth integrations (e.g. Better Auth's password provider) */ const NON_OAUTH_PROVIDER_IDS = ['credential'] as const @@ -103,7 +104,7 @@ export async function syncWorkspaceOAuthCredentialsForUser( try { await db.insert(credential).values({ - id: crypto.randomUUID(), + id: generateId(), workspaceId, type: 'oauth', displayName: getServiceConfigByProviderId(acc.providerId)?.name || acc.providerId, @@ -178,7 +179,7 @@ export async function syncWorkspaceOAuthCredentialsForUser( try { await db.insert(credentialMember).values({ - id: crypto.randomUUID(), + id: generateId(), credentialId, userId, role: 'admin', diff --git a/apps/sim/lib/knowledge/documents/service.ts b/apps/sim/lib/knowledge/documents/service.ts index cd4210b551d..78a326e7465 100644 --- a/apps/sim/lib/knowledge/documents/service.ts +++ b/apps/sim/lib/knowledge/documents/service.ts @@ -19,6 +19,7 @@ import { } from '@/lib/knowledge/tags/utils' import type { ProcessedDocumentTags } from '@/lib/knowledge/types' import type { DocumentProcessingPayload } from '@/background/knowledge-processing' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('DocumentService') @@ -514,7 +515,7 @@ export async function processDocumentAsync( logger.info(`[${documentId}] Creating embedding records with tags`) const embeddingRecords = processed.chunks.map((chunk, chunkIndex) => ({ - id: crypto.randomUUID(), + id: generateId(), knowledgeBaseId, documentId, chunkIndex, diff --git a/apps/sim/lib/table/service.ts b/apps/sim/lib/table/service.ts index a23dfc7da7a..03074bb7430 100644 --- a/apps/sim/lib/table/service.ts +++ b/apps/sim/lib/table/service.ts @@ -29,6 +29,7 @@ import type { UpdateRowData, } from './types' import { +import { generateId } from '@/lib/core/utils/id' checkBatchUniqueConstraintsDb, checkUniqueConstraintsDb, getUniqueColumns, @@ -149,7 +150,7 @@ export async function createTable( throw new Error(`Table with name "${data.name}" already exists in this workspace`) } - const tableId = `tbl_${crypto.randomUUID().replace(/-/g, '')}` + const tableId = `tbl_${generateId().replace(/-/g, '')}` const now = new Date() // Use provided maxRows (from billing plan) or fall back to default @@ -240,7 +241,7 @@ export async function insertRow( } } - const rowId = `row_${crypto.randomUUID().replace(/-/g, '')}` + const rowId = `row_${generateId().replace(/-/g, '')}` const now = new Date() const newRow = { @@ -316,7 +317,7 @@ export async function batchInsertRows( const now = new Date() const rowsToInsert = data.rows.map((rowData) => ({ - id: `row_${crypto.randomUUID().replace(/-/g, '')}`, + id: `row_${generateId().replace(/-/g, '')}`, tableId: data.tableId, workspaceId: data.workspaceId, data: rowData, diff --git a/apps/sim/lib/workflows/defaults.ts b/apps/sim/lib/workflows/defaults.ts index 7de0c058a7d..87d3c009f29 100644 --- a/apps/sim/lib/workflows/defaults.ts +++ b/apps/sim/lib/workflows/defaults.ts @@ -2,6 +2,7 @@ import { getEffectiveBlockOutputs } from '@/lib/workflows/blocks/block-outputs' import { getBlock } from '@/blocks' import type { BlockConfig, SubBlockConfig } from '@/blocks/types' import type { BlockState, SubBlockState, WorkflowState } from '@/stores/workflows/workflow/types' +import { generateId } from '@/lib/core/utils/id' export interface DefaultWorkflowArtifacts { workflowState: WorkflowState @@ -40,7 +41,7 @@ function resolveInitialValue(subBlock: SubBlockConfig): unknown { if (subBlock.type === 'input-format') { return [ { - id: crypto.randomUUID(), + id: generateId(), name: '', type: 'string', value: '', @@ -111,7 +112,7 @@ function buildStartBlockState( export function buildDefaultWorkflowArtifacts(): DefaultWorkflowArtifacts { const blockConfig = buildStartBlockConfig() - const startBlockId = crypto.randomUUID() + const startBlockId = generateId() const { blockState, subBlockValues } = buildStartBlockState(blockConfig, startBlockId) diff --git a/apps/sim/lib/workflows/operations/socket-operations.ts b/apps/sim/lib/workflows/operations/socket-operations.ts index e0abd45051a..a2dabbebd72 100644 --- a/apps/sim/lib/workflows/operations/socket-operations.ts +++ b/apps/sim/lib/workflows/operations/socket-operations.ts @@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger' import { client } from '@/lib/auth/auth-client' import { useOperationQueueStore } from '@/stores/operation-queue/store' import type { WorkflowState } from '@/stores/workflows/workflow/types' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('WorkflowSocketOperations') @@ -39,7 +40,7 @@ export async function enqueueWorkflowOperation({ operationId, }: EnqueueWorkflowOperationArgs): Promise { const userId = await resolveUserId() - const opId = operationId ?? crypto.randomUUID() + const opId = operationId ?? generateId() useOperationQueueStore.getState().addToQueue({ id: opId, diff --git a/apps/sim/lib/workflows/persistence/duplicate.ts b/apps/sim/lib/workflows/persistence/duplicate.ts index cee5f467a35..861f73068c5 100644 --- a/apps/sim/lib/workflows/persistence/duplicate.ts +++ b/apps/sim/lib/workflows/persistence/duplicate.ts @@ -12,6 +12,7 @@ import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' import type { Variable } from '@/stores/panel/variables/types' import type { LoopConfig, ParallelConfig } from '@/stores/workflows/workflow/types' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('WorkflowDuplicateHelper') @@ -96,7 +97,7 @@ export async function duplicateWorkflow( } = options // Generate new workflow ID - const newWorkflowId = crypto.randomUUID() + const newWorkflowId = generateId() const now = new Date() // Duplicate workflow and all related data in a transaction @@ -187,7 +188,7 @@ export async function duplicateWorkflow( const sourceVars = (source.variables as Record) || {} const remapped: Record = {} for (const [oldVarId, variable] of Object.entries(sourceVars) as [string, Variable][]) { - const newVarId = crypto.randomUUID() + const newVarId = generateId() varIdMapping.set(oldVarId, newVarId) remapped[newVarId] = { ...variable, @@ -211,7 +212,7 @@ export async function duplicateWorkflow( if (sourceBlocks.length > 0) { // First pass: Create all block ID mappings sourceBlocks.forEach((block) => { - const newBlockId = crypto.randomUUID() + const newBlockId = generateId() blockIdMapping.set(block.id, newBlockId) }) @@ -288,7 +289,7 @@ export async function duplicateWorkflow( if (sourceEdges.length > 0) { const newEdges = sourceEdges.map((edge) => ({ ...edge, - id: crypto.randomUUID(), // Generate new edge ID + id: generateId(), // Generate new edge ID workflowId: newWorkflowId, sourceBlockId: blockIdMapping.get(edge.sourceBlockId) || edge.sourceBlockId, targetBlockId: blockIdMapping.get(edge.targetBlockId) || edge.targetBlockId, diff --git a/apps/sim/lib/workflows/persistence/utils.ts b/apps/sim/lib/workflows/persistence/utils.ts index 89b7b7f6029..6ed7154a0d5 100644 --- a/apps/sim/lib/workflows/persistence/utils.ts +++ b/apps/sim/lib/workflows/persistence/utils.ts @@ -22,6 +22,7 @@ import { sanitizeAgentToolsInBlocks } from '@/lib/workflows/sanitization/validat import type { BlockState, Loop, Parallel, WorkflowState } from '@/stores/workflows/workflow/types' import { SUBFLOW_TYPES } from '@/stores/workflows/workflow/types' import { generateLoopBlocks, generateParallelBlocks } from '@/stores/workflows/workflow/utils' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('WorkflowDBHelpers') @@ -804,23 +805,23 @@ export function regenerateWorkflowStateIds(state: RegenerateStateInput): Regener // First pass: Create all ID mappings // Map block IDs Object.keys(state.blocks || {}).forEach((oldId) => { - blockIdMapping.set(oldId, crypto.randomUUID()) + blockIdMapping.set(oldId, generateId()) }) // Map edge IDs ;(state.edges || []).forEach((edge: Edge) => { - edgeIdMapping.set(edge.id, crypto.randomUUID()) + edgeIdMapping.set(edge.id, generateId()) }) // Map loop IDs Object.keys(state.loops || {}).forEach((oldId) => { - loopIdMapping.set(oldId, crypto.randomUUID()) + loopIdMapping.set(oldId, generateId()) }) // Map parallel IDs Object.keys(state.parallels || {}).forEach((oldId) => { - parallelIdMapping.set(oldId, crypto.randomUUID()) + parallelIdMapping.set(oldId, generateId()) }) // Second pass: Create new state with regenerated IDs and updated references diff --git a/apps/sim/lib/workflows/schedules/deploy.ts b/apps/sim/lib/workflows/schedules/deploy.ts index 9344b018e47..6e8cef61276 100644 --- a/apps/sim/lib/workflows/schedules/deploy.ts +++ b/apps/sim/lib/workflows/schedules/deploy.ts @@ -5,6 +5,7 @@ import type { DbOrTx } from '@/lib/db/types' import { cleanupWebhooksForWorkflow } from '@/lib/webhooks/deploy' import type { BlockState } from '@/lib/workflows/schedules/utils' import { findScheduleBlocks, validateScheduleBlock } from '@/lib/workflows/schedules/validation' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('ScheduleDeployUtils') @@ -99,7 +100,7 @@ export async function createSchedulesForDeploy( for (const validated of validatedBlocks) { const { blockId, cronExpression, nextRunAt, timezone } = validated - const scheduleId = crypto.randomUUID() + const scheduleId = generateId() const now = new Date() const values = { diff --git a/apps/sim/lib/workspaces/duplicate.ts b/apps/sim/lib/workspaces/duplicate.ts index 375c52ed2a1..cbce36936fb 100644 --- a/apps/sim/lib/workspaces/duplicate.ts +++ b/apps/sim/lib/workspaces/duplicate.ts @@ -4,6 +4,7 @@ import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { duplicateWorkflow } from '@/lib/workflows/persistence/duplicate' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('WorkspaceDuplicate') @@ -32,7 +33,7 @@ export async function duplicateWorkspace( const { sourceWorkspaceId, userId, name, requestId = 'unknown' } = options // Generate new workspace ID - const newWorkspaceId = crypto.randomUUID() + const newWorkspaceId = generateId() const now = new Date() // Verify the source workspace exists and user has permission @@ -68,7 +69,7 @@ export async function duplicateWorkspace( // Grant admin permission to the user on the new workspace await tx.insert(permissions).values({ - id: crypto.randomUUID(), + id: generateId(), userId, entityType: 'workspace', entityId: newWorkspaceId, @@ -102,7 +103,7 @@ export async function duplicateWorkspace( const foldersAtLevel = foldersByParent.get(parentId) || [] for (const sourceFolder of foldersAtLevel) { - const newFolderId = crypto.randomUUID() + const newFolderId = generateId() folderIdMap.set(sourceFolder.id, newFolderId) await db.insert(workflowFolder).values({ diff --git a/apps/sim/serializer/index.ts b/apps/sim/serializer/index.ts index 671535ef684..6da3874c991 100644 --- a/apps/sim/serializer/index.ts +++ b/apps/sim/serializer/index.ts @@ -17,6 +17,7 @@ import type { SerializedBlock, SerializedWorkflow } from '@/serializer/types' import type { BlockState, Loop, Parallel } from '@/stores/workflows/workflow/types' import { generateLoopBlocks, generateParallelBlocks } from '@/stores/workflows/workflow/utils' import { getTool } from '@/tools/utils' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('Serializer') @@ -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/socket/handlers/operations.ts b/apps/sim/socket/handlers/operations.ts index 4852d79d399..423463cfd33 100644 --- a/apps/sim/socket/handlers/operations.ts +++ b/apps/sim/socket/handlers/operations.ts @@ -14,6 +14,7 @@ import type { AuthenticatedSocket } from '@/socket/middleware/auth' import { checkRolePermission } from '@/socket/middleware/permissions' import type { IRoomManager, UserSession } from '@/socket/rooms' import { WorkflowOperationSchema } from '@/socket/validation/schemas' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('OperationsHandlers') @@ -152,7 +153,7 @@ export function setupOperationsHandlers(socket: AuthenticatedSocket, roomManager userName: session.userName, metadata: { workflowId, - operationId: crypto.randomUUID(), + operationId: generateId(), isPositionUpdate: true, }, } @@ -206,7 +207,7 @@ export function setupOperationsHandlers(socket: AuthenticatedSocket, roomManager senderId: socket.id, userId: session.userId, userName: session.userName, - metadata: { workflowId, operationId: crypto.randomUUID(), isBatchPositionUpdate: true }, + metadata: { workflowId, operationId: generateId(), isBatchPositionUpdate: true }, }) try { @@ -262,7 +263,7 @@ export function setupOperationsHandlers(socket: AuthenticatedSocket, roomManager userName: session.userName, metadata: { workflowId, - operationId: crypto.randomUUID(), + operationId: generateId(), }, } @@ -302,7 +303,7 @@ export function setupOperationsHandlers(socket: AuthenticatedSocket, roomManager userName: session.userName, metadata: { workflowId, - operationId: crypto.randomUUID(), + operationId: generateId(), }, } @@ -337,7 +338,7 @@ export function setupOperationsHandlers(socket: AuthenticatedSocket, roomManager senderId: socket.id, userId: session.userId, userName: session.userName, - metadata: { workflowId, operationId: crypto.randomUUID() }, + metadata: { workflowId, operationId: generateId() }, }) if (operationId) { @@ -369,7 +370,7 @@ export function setupOperationsHandlers(socket: AuthenticatedSocket, roomManager senderId: socket.id, userId: session.userId, userName: session.userName, - metadata: { workflowId, operationId: crypto.randomUUID() }, + metadata: { workflowId, operationId: generateId() }, }) if (operationId) { @@ -398,7 +399,7 @@ export function setupOperationsHandlers(socket: AuthenticatedSocket, roomManager senderId: socket.id, userId: session.userId, userName: session.userName, - metadata: { workflowId, operationId: crypto.randomUUID() }, + metadata: { workflowId, operationId: generateId() }, }) if (operationId) { @@ -430,7 +431,7 @@ export function setupOperationsHandlers(socket: AuthenticatedSocket, roomManager senderId: socket.id, userId: session.userId, userName: session.userName, - metadata: { workflowId, operationId: crypto.randomUUID() }, + metadata: { workflowId, operationId: generateId() }, }) if (operationId) { @@ -462,7 +463,7 @@ export function setupOperationsHandlers(socket: AuthenticatedSocket, roomManager senderId: socket.id, userId: session.userId, userName: session.userName, - metadata: { workflowId, operationId: crypto.randomUUID() }, + metadata: { workflowId, operationId: generateId() }, }) if (operationId) { @@ -494,7 +495,7 @@ export function setupOperationsHandlers(socket: AuthenticatedSocket, roomManager senderId: socket.id, userId: session.userId, userName: session.userName, - metadata: { workflowId, operationId: crypto.randomUUID() }, + metadata: { workflowId, operationId: generateId() }, }) if (operationId) { @@ -523,7 +524,7 @@ export function setupOperationsHandlers(socket: AuthenticatedSocket, roomManager senderId: socket.id, userId: session.userId, userName: session.userName, - metadata: { workflowId, operationId: crypto.randomUUID() }, + metadata: { workflowId, operationId: generateId() }, }) if (operationId) { @@ -554,7 +555,7 @@ export function setupOperationsHandlers(socket: AuthenticatedSocket, roomManager userName: session.userName, metadata: { workflowId, - operationId: crypto.randomUUID(), + operationId: generateId(), }, } diff --git a/apps/sim/stores/chat/store.ts b/apps/sim/stores/chat/store.ts index 3da14769db3..b4efe574c07 100644 --- a/apps/sim/stores/chat/store.ts +++ b/apps/sim/stores/chat/store.ts @@ -4,6 +4,7 @@ import { create } from 'zustand' import { devtools, persist } from 'zustand/middleware' import type { ChatMessage, ChatState } from './types' import { MAX_CHAT_HEIGHT, MAX_CHAT_WIDTH, MIN_CHAT_HEIGHT, MIN_CHAT_WIDTH } from './utils' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('ChatStore') @@ -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..5a8121846af 100644 --- a/apps/sim/stores/copilot-training/store.ts +++ b/apps/sim/stores/copilot-training/store.ts @@ -7,6 +7,7 @@ import { mergeSubblockState } from '@/stores/workflows/utils' import { useWorkflowStore } from '@/stores/workflows/workflow/store' import type { WorkflowState } from '@/stores/workflows/workflow/types' import type { CopilotTrainingState, TrainingDataset } from './types' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('CopilotTrainingStore') @@ -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..ff66b85864b 100644 --- a/apps/sim/stores/notifications/store.ts +++ b/apps/sim/stores/notifications/store.ts @@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger' import { create } from 'zustand' import { persist } from 'zustand/middleware' import type { AddNotificationParams, Notification } from './types' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('NotificationStore') @@ -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..a3d4bf79b45 100644 --- a/apps/sim/stores/panel/copilot/store.ts +++ b/apps/sim/stores/panel/copilot/store.ts @@ -67,6 +67,7 @@ import { useWorkflowDiffStore } from '@/stores/workflow-diff/store' import { useSubBlockStore } from '@/stores/workflows/subblock/store' import { useWorkflowStore } from '@/stores/workflows/workflow/store' import type { WorkflowState } from '@/stores/workflows/workflow/types' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('CopilotStore') @@ -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..7f24cb09279 100644 --- a/apps/sim/stores/panel/variables/store.ts +++ b/apps/sim/stores/panel/variables/store.ts @@ -7,6 +7,7 @@ import { useOperationQueueStore } from '@/stores/operation-queue/store' import type { Variable, VariablesStore } from '@/stores/panel/variables/types' import { useWorkflowRegistry } from '@/stores/workflows/registry/store' import { useSubBlockStore } from '@/stores/workflows/subblock/store' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('VariablesStore') @@ -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..e9f9ac3b6c7 100644 --- a/apps/sim/stores/terminal/console/store.ts +++ b/apps/sim/stores/terminal/console/store.ts @@ -9,6 +9,7 @@ import { useExecutionStore } from '@/stores/execution' import { useNotificationStore } from '@/stores/notifications' import { indexedDBStorage } from '@/stores/terminal/console/storage' import type { ConsoleEntry, ConsoleStore, ConsoleUpdate } from '@/stores/terminal/console/types' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('TerminalConsoleStore') @@ -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..8b11da07bf0 100644 --- a/apps/sim/stores/undo-redo/utils.ts +++ b/apps/sim/stores/undo-redo/utils.ts @@ -12,10 +12,11 @@ import type { } from '@/stores/undo-redo/types' import { mergeSubblockState } from '@/stores/workflows/utils' import type { BlockState } from '@/stores/workflows/workflow/types' +import { generateId } from '@/lib/core/utils/id' 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..090662b2d6e 100644 --- a/apps/sim/stores/workflows/subblock/store.ts +++ b/apps/sim/stores/workflows/subblock/store.ts @@ -7,6 +7,7 @@ import { populateTriggerFieldsFromConfig } from '@/hooks/use-trigger-config-aggr import { useWorkflowRegistry } from '@/stores/workflows/registry/store' import type { SubBlockStore } from '@/stores/workflows/subblock/types' import { isTriggerValid } from '@/triggers' +import { generateId } from '@/lib/core/utils/id' const logger = createLogger('SubBlockStore') @@ -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 1352db5d10e..6329182aa9c 100644 --- a/apps/sim/stores/workflows/utils.ts +++ b/apps/sim/stores/workflows/utils.ts @@ -18,6 +18,7 @@ import type { WorkflowState, } from '@/stores/workflows/workflow/types' import { TRIGGER_RUNTIME_SUBBLOCK_IDS } from '@/triggers/constants' +import { generateId } from '@/lib/core/utils/id' /** Threshold to detect viewport-based offsets vs small duplicate offsets */ const LARGE_OFFSET_THRESHOLD = 300 @@ -171,7 +172,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 ba9c9bd35fb..7803a9f5609 100644 --- a/apps/sim/stores/workflows/workflow/store.ts +++ b/apps/sim/stores/workflows/workflow/store.ts @@ -20,6 +20,7 @@ import type { WorkflowStore, } from '@/stores/workflows/workflow/types' import { +import { generateId } from '@/lib/core/utils/id' findAllDescendantNodes, generateLoopBlocks, generateParallelBlocks, @@ -79,7 +80,7 @@ function resolveInitialSubblockValue(config: SubBlockConfig): unknown { if (config.type === 'input-format') { return [ { - id: crypto.randomUUID(), + id: generateId(), name: '', type: 'string', value: '', @@ -247,7 +248,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, @@ -440,7 +441,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, @@ -565,7 +566,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..f019a4ff4f5 100644 --- a/apps/sim/tools/langsmith/utils.ts +++ b/apps/sim/tools/langsmith/utils.ts @@ -1,4 +1,5 @@ import type { LangsmithRunPayload } from '@/tools/langsmith/types' +import { generateId } from '@/lib/core/utils/id' interface NormalizedRunPayload { payload: LangsmithRunPayload @@ -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 ?? generateId() 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/packages/testing/src/mocks/uuid.mock.ts b/packages/testing/src/mocks/uuid.mock.ts index f278e76b64e..4e0590d516f 100644 --- a/packages/testing/src/mocks/uuid.mock.ts +++ b/packages/testing/src/mocks/uuid.mock.ts @@ -25,6 +25,10 @@ export function mockUuid(mockValue = 'test-uuid') { * Mock crypto.randomUUID for tests. * Uses vi.stubGlobal to replace the global crypto object. * + * Note: Most code now uses `generateId()` from `@/lib/core/utils/id` + * which falls back to `uuid.v4()` when `crypto.randomUUID` is unavailable. + * Use `mockGenerateId()` to mock `generateId()` directly. + * * @param mockValue - The UUID value to return (defaults to 'mock-uuid-1234-5678') * * @example @@ -38,3 +42,21 @@ export function mockCryptoUuid(mockValue = 'mock-uuid-1234-5678') { randomUUID: vi.fn().mockReturnValue(mockValue), }) } + +/** + * Mock the `generateId` utility for consistent test results. + * Uses vi.doMock to mock `@/lib/core/utils/id`. + * + * @param mockValue - The UUID value to return (defaults to 'mock-generated-id') + * + * @example + * ```ts + * mockGenerateId('test-id') + * // Now generateId() will return 'test-id' + * ``` + */ +export function mockGenerateId(mockValue = 'mock-generated-id') { + vi.doMock('@/lib/core/utils/id', () => ({ + generateId: vi.fn().mockReturnValue(mockValue), + })) +} From 782e1d11f2eea734e7f8e75fdd879979bb6271d3 Mon Sep 17 00:00:00 2001 From: Maxwell Calkin Date: Mon, 9 Mar 2026 03:14:17 -0400 Subject: [PATCH 2/2] fix: remove JS import accidentally injected into Python code example The automated crypto.randomUUID replacement incorrectly added a generateId import into a Python code example template literal, producing invalid Python in the deploy modal's A2A code snippet. AI Disclosure: This commit was authored by Claude Opus 4.6 (Anthropic), operated by Maxwell Calkin (@MaxwellCalkin). --- .../deploy/components/deploy-modal/components/a2a/a2a.tsx | 1 - 1 file changed, 1 deletion(-) 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 9373918bc89..60953c09bc5 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 @@ -554,7 +554,6 @@ export function A2aDeploy({ return requiresAuth ? `import os import requests -import { generateId } from '@/lib/core/utils/id' response = requests.post( "${endpoint}",