improvement(utils): add shared utility functions and replace inline patterns

Add sleep, toError, safeJsonParse, isNonNull helpers and invariant/assertNever
assertions. Replace all inline implementations across the codebase with these
shared utilities for consistency. Zero behavioral changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Waleed Latif
2026-04-17 12:22:16 -07:00
parent 38864fac34
commit a05b4422ad
329 changed files with 1390 additions and 791 deletions

View File

@@ -30,5 +30,32 @@ const shortId = generateShortId()
const tiny = generateShortId(8)
```
## Common Utilities
Use shared helpers from `@/lib/core/utils/helpers` instead of writing inline implementations:
- `sleep(ms)` — async delay. Never write `new Promise(resolve => setTimeout(resolve, ms))`
- `toError(value)` — normalize unknown caught values to `Error`. Never write `e instanceof Error ? e : new Error(String(e))`
- `toError(value).message` — get error message safely. Never write `e instanceof Error ? e.message : String(e)`
- `safeJsonParse(str, fallback?)` — parse JSON without throwing. Never write `try { JSON.parse(str) } catch { return default }`
- `isNonNull(value)` — type-narrowing filter predicate for null/undefined
Use assertion utilities from `@/lib/core/utils/asserts`:
- `invariant(condition, message)` — assert a condition is truthy, throws if not
- `assertNever(value)` — exhaustive switch/if-else check, TypeScript errors at compile time if a case is unhandled
```typescript
// ✗ Bad
await new Promise(resolve => setTimeout(resolve, 1000))
const msg = error instanceof Error ? error.message : String(error)
const err = error instanceof Error ? error : new Error(String(error))
// ✓ Good
import { sleep, toError } from '@/lib/core/utils/helpers'
await sleep(1000)
const msg = toError(error).message
const err = toError(error)
```
## Package Manager
Use `bun` and `bunx`, not `npm` and `npx`.

View File

@@ -37,5 +37,32 @@ const shortId = generateShortId()
const tiny = generateShortId(8)
```
## Common Utilities
Use shared helpers from `@/lib/core/utils/helpers` instead of writing inline implementations:
- `sleep(ms)` — async delay. Never write `new Promise(resolve => setTimeout(resolve, ms))`
- `toError(value)` — normalize unknown caught values to `Error`. Never write `e instanceof Error ? e : new Error(String(e))`
- `toError(value).message` — get error message safely. Never write `e instanceof Error ? e.message : String(e)`
- `safeJsonParse(str, fallback?)` — parse JSON without throwing. Never write `try { JSON.parse(str) } catch { return default }`
- `isNonNull(value)` — type-narrowing filter predicate for null/undefined
Use assertion utilities from `@/lib/core/utils/asserts`:
- `invariant(condition, message)` — assert a condition is truthy, throws if not
- `assertNever(value)` — exhaustive switch/if-else check, TypeScript errors at compile time if a case is unhandled
```typescript
// ✗ Bad
await new Promise(resolve => setTimeout(resolve, 1000))
const msg = error instanceof Error ? error.message : String(error)
const err = error instanceof Error ? error : new Error(String(error))
// ✓ Good
import { sleep, toError } from '@/lib/core/utils/helpers'
await sleep(1000)
const msg = toError(error).message
const err = toError(error)
```
## Package Manager
Use `bun` and `bunx`, not `npm` and `npx`.

View File

@@ -8,6 +8,7 @@ You are a professional software engineer. All code must follow best practices: a
- **Comments**: Use TSDoc for documentation. No `====` separators. No non-TSDoc comments
- **Styling**: Never update global styles. Keep all styling local to components
- **ID Generation**: Never use `crypto.randomUUID()`, `nanoid`, or `uuid` package. Use `generateId()` (UUID v4) or `generateShortId()` (compact) from `@/lib/core/utils/uuid`
- **Common Utilities**: Use shared helpers from `@/lib/core/utils/helpers` instead of inline implementations. `sleep(ms)` for delays, `toError(e)` to normalize caught values, `safeJsonParse(str, fallback?)` for safe JSON parsing, `isNonNull(v)` for type-narrowing null filters. Use `invariant(cond, msg)` and `assertNever(val)` from `@/lib/core/utils/asserts` for runtime assertions and exhaustive checks.
- **Package Manager**: Use `bun` and `bunx`, not `npm` and `npx`
## Architecture

View File

@@ -12,6 +12,7 @@ import type {
} from '@/lib/academy/types'
import { validateExercise } from '@/lib/academy/validation'
import { cn } from '@/lib/core/utils/cn'
import { sleep } from '@/lib/core/utils/helpers'
import { getEffectiveBlockOutputs } from '@/lib/workflows/blocks/block-outputs'
import { getQueryClient } from '@/app/_shell/providers/get-query-client'
import { GlobalCommandsProvider } from '@/app/workspace/[workspaceId]/providers/global-commands-provider'
@@ -323,7 +324,7 @@ export function SandboxCanvasProvider({
for (let i = 0; i < plan.length; i++) {
const step = plan[i]
setActiveBlocks(workflowId, new Set([step.blockId]))
await new Promise((resolve) => setTimeout(resolve, step.delay))
await sleep(step.delay)
addConsole({
workflowId,
blockId: step.blockId,

View File

@@ -4,6 +4,7 @@ import { account, credential, credentialSetMember } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { and, desc, eq, inArray } from 'drizzle-orm'
import { decryptSecret } from '@/lib/core/security/encryption'
import { toError } from '@/lib/core/utils/helpers'
import { refreshOAuthToken } from '@/lib/oauth'
import {
getMicrosoftRefreshTokenExpiry,
@@ -331,7 +332,7 @@ export async function getOAuthToken(userId: string, providerId: string): Promise
return accessToken
} catch (error) {
logger.error(`Error refreshing token for user ${userId}, provider ${providerId}`, {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
providerId,
userId,
@@ -460,7 +461,7 @@ export async function refreshAccessTokenIfNeeded(
return refreshedToken.accessToken
} catch (error) {
logger.error(`[${requestId}] Error refreshing token for credential`, {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
providerId: credential.providerId,
credentialId,
@@ -664,7 +665,7 @@ export async function getCredentialsForCredentialSet(
}
} catch (error) {
logger.error(`Failed to refresh token for user ${cred.userId}, provider ${providerId}`, {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
continue
}

View File

@@ -3,6 +3,7 @@ import { headers } from 'next/headers'
import { NextResponse } from 'next/server'
import { auth } from '@/lib/auth'
import { isAuthDisabled } from '@/lib/core/config/feature-flags'
import { toError } from '@/lib/core/utils/helpers'
const logger = createLogger('SocketTokenAPI')
@@ -36,7 +37,7 @@ export async function POST() {
}
logger.error('Failed to generate socket token', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})
return NextResponse.json({ error: 'Failed to generate token' }, { status: 500 })

View File

@@ -147,6 +147,32 @@ export async function POST(request: NextRequest) {
oidcConfig.userInfoEndpoint = userInfoEndpoint
oidcConfig.jwksEndpoint = jwksEndpoint
const userProvidedEndpoints: Record<string, string | undefined> = {
authorizationEndpoint,
tokenEndpoint,
userInfoEndpoint,
jwksEndpoint,
}
for (const [name, endpointUrl] of Object.entries(userProvidedEndpoints)) {
if (endpointUrl) {
const endpointValidation = await validateUrlWithDNS(endpointUrl, `OIDC ${name}`)
if (!endpointValidation.isValid) {
logger.warn('Explicitly provided OIDC endpoint failed SSRF validation', {
endpoint: name,
url: endpointUrl,
error: endpointValidation.error,
})
return NextResponse.json(
{
error: `OIDC ${name} failed security validation: ${endpointValidation.error}`,
},
{ status: 400 }
)
}
}
}
const needsDiscovery =
!oidcConfig.authorizationEndpoint || !oidcConfig.tokenEndpoint || !oidcConfig.jwksEndpoint

View File

@@ -17,6 +17,7 @@ import {
hasUsableSubscriptionStatus,
} from '@/lib/billing/subscriptions/utils'
import { isBillingEnabled } from '@/lib/core/config/feature-flags'
import { toError } from '@/lib/core/utils/helpers'
import { captureServerEvent } from '@/lib/posthog/server'
const logger = createLogger('SwitchPlan')
@@ -185,7 +186,7 @@ export async function POST(request: NextRequest) {
} catch (error) {
logger.error('Failed to switch subscription', {
userId: session?.user?.id,
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Failed to switch plan' },

View File

@@ -7,6 +7,7 @@ import { checkAndBillOverageThreshold } from '@/lib/billing/threshold-billing'
import { checkInternalApiKey } from '@/lib/copilot/request/http'
import { isBillingEnabled } from '@/lib/core/config/feature-flags'
import { type AtomicClaimResult, billingIdempotency } from '@/lib/core/idempotency/service'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
const logger = createLogger('BillingUpdateCostAPI')
@@ -170,7 +171,7 @@ export async function POST(req: NextRequest) {
const duration = Date.now() - startTime
logger.error(`[${requestId}] Cost update failed`, {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
duration,
})
@@ -180,7 +181,7 @@ export async function POST(req: NextRequest) {
.release(claim.normalizedKey, claim.storageMethod)
.catch((releaseErr) => {
logger.warn(`[${requestId}] Failed to release idempotency claim`, {
error: releaseErr instanceof Error ? releaseErr.message : String(releaseErr),
error: toError(releaseErr).message,
normalizedKey: claim?.normalizedKey,
})
})

View File

@@ -5,6 +5,7 @@ import { SIM_AGENT_API_URL } from '@/lib/copilot/constants'
import { authenticateCopilotRequestSessionOnly } from '@/lib/copilot/request/http'
import { abortActiveStream, waitForPendingChatStream } from '@/lib/copilot/request/session'
import { env } from '@/lib/core/config/env'
import { toError } from '@/lib/core/utils/helpers'
const logger = createLogger('CopilotChatAbortAPI')
const GO_EXPLICIT_ABORT_TIMEOUT_MS = 3000
@@ -20,7 +21,7 @@ export async function POST(request: Request) {
const body = await request.json().catch((err) => {
logger.warn('Abort request body parse failed; continuing with empty object', {
error: err instanceof Error ? err.message : String(err),
error: toError(err).message,
})
return {}
})
@@ -35,7 +36,7 @@ export async function POST(request: Request) {
const run = await getLatestRunForStream(streamId, authenticatedUserId).catch((err) => {
logger.warn('getLatestRunForStream failed while resolving chatId for abort', {
streamId,
error: err instanceof Error ? err.message : String(err),
error: toError(err).message,
})
return null
})
@@ -70,7 +71,7 @@ export async function POST(request: Request) {
} catch (err) {
logger.warn('Explicit abort marker request failed; proceeding with local abort', {
streamId,
error: err instanceof Error ? err.message : String(err),
error: toError(err).message,
})
}

View File

@@ -16,6 +16,7 @@ import {
import { readFilePreviewSessions } from '@/lib/copilot/request/session'
import { readEvents } from '@/lib/copilot/request/session/buffer'
import { toStreamBatchEvent } from '@/lib/copilot/request/session/types'
import { toError } from '@/lib/core/utils/helpers'
import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
import { assertActiveWorkspaceAccess } from '@/lib/workspaces/permissions/utils'
@@ -82,7 +83,7 @@ export async function GET(req: NextRequest) {
logger.warn('Failed to read preview sessions for copilot chat', {
chatId,
conversationId: chat.conversationId,
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
return []
}),
@@ -90,7 +91,7 @@ export async function GET(req: NextRequest) {
logger.warn('Failed to fetch latest run for copilot chat snapshot', {
chatId,
conversationId: chat.conversationId,
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
return null
}),
@@ -110,7 +111,7 @@ export async function GET(req: NextRequest) {
logger.warn('Failed to load copilot chat stream snapshot', {
chatId,
conversationId: chat.conversationId,
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
}
}

View File

@@ -15,6 +15,7 @@ import {
SSE_RESPONSE_HEADERS,
} from '@/lib/copilot/request/session'
import { toStreamBatchEvent } from '@/lib/copilot/request/session/types'
import { sleep, toError } from '@/lib/core/utils/helpers'
export const maxDuration = 3600
@@ -97,7 +98,7 @@ export async function GET(request: NextRequest) {
const run = await getLatestRunForStream(streamId, authenticatedUserId).catch((err) => {
logger.warn('Failed to fetch latest run for stream', {
streamId,
error: err instanceof Error ? err.message : String(err),
error: toError(err).message,
})
return null
})
@@ -119,7 +120,7 @@ export async function GET(request: NextRequest) {
readFilePreviewSessions(streamId).catch((error) => {
logger.warn('Failed to read preview sessions for stream batch', {
streamId,
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
return []
}),
@@ -235,7 +236,7 @@ export async function GET(request: NextRequest) {
(err) => {
logger.warn('Failed to poll latest run for stream', {
streamId,
error: err instanceof Error ? err.message : String(err),
error: toError(err).message,
})
return null
}
@@ -273,7 +274,7 @@ export async function GET(request: NextRequest) {
break
}
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS))
await sleep(POLL_INTERVAL_MS)
}
if (!controllerClosed && Date.now() - startTime >= MAX_STREAM_MS) {
emitTerminalIfMissing(MothershipStreamV1CompletionStatus.error, {
@@ -286,7 +287,7 @@ export async function GET(request: NextRequest) {
if (!controllerClosed && !request.signal.aborted) {
logger.warn('Stream replay failed', {
streamId,
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
emitTerminalIfMissing(MothershipStreamV1CompletionStatus.error, {
message: 'The stream replay failed before completion.',

View File

@@ -22,6 +22,7 @@ import {
createRequestTracker,
createUnauthorizedResponse,
} from '@/lib/copilot/request/http'
import { toError } from '@/lib/core/utils/helpers'
const logger = createLogger('CopilotConfirmAPI')
@@ -106,7 +107,7 @@ async function updateToolCallStatus(
logger.error('Failed to update tool call status', {
toolCallId,
status,
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
return false
}
@@ -133,7 +134,7 @@ export async function POST(req: NextRequest) {
const existing = await getAsyncToolCall(toolCallId).catch((err) => {
logger.warn('Failed to fetch async tool call', {
toolCallId,
error: err instanceof Error ? err.message : String(err),
error: toError(err).message,
})
return null
})
@@ -145,7 +146,7 @@ export async function POST(req: NextRequest) {
const run = await getRunSegment(existing.runId).catch((err) => {
logger.warn('Failed to fetch run segment', {
runId: existing.runId,
error: err instanceof Error ? err.message : String(err),
error: toError(err).message,
})
return null
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { SIM_AGENT_API_URL } from '@/lib/copilot/constants'
import { authenticateCopilotRequestSessionOnly } from '@/lib/copilot/request/http'
import { toError } from '@/lib/core/utils/helpers'
interface AvailableModel {
id: string
@@ -76,7 +77,7 @@ export async function GET(_req: NextRequest) {
return NextResponse.json({ success: true, models })
} catch (error) {
logger.error('Error fetching available models', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
return NextResponse.json(
{

View File

@@ -6,6 +6,7 @@ import { type NextRequest, NextResponse } from 'next/server'
import { verifyCronAuth } from '@/lib/auth/internal'
import { JOB_RETENTION_HOURS, JOB_STATUS } from '@/lib/core/async-jobs'
import { getMaxExecutionTimeout } from '@/lib/core/execution-limits'
import { toError } from '@/lib/core/utils/helpers'
const logger = createLogger('CleanupStaleExecutions')
@@ -73,7 +74,7 @@ export async function GET(request: NextRequest) {
cleaned++
} catch (error) {
logger.error(`Failed to clean up execution ${execution.executionId}:`, {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
failed++
}
@@ -104,7 +105,7 @@ export async function GET(request: NextRequest) {
}
} catch (error) {
logger.error('Failed to clean up stale async jobs:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
}
@@ -131,7 +132,7 @@ export async function GET(request: NextRequest) {
}
} catch (error) {
logger.error('Failed to clean up stale pending jobs:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
}
@@ -158,7 +159,7 @@ export async function GET(request: NextRequest) {
}
} catch (error) {
logger.error('Failed to delete old async jobs:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
}

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkHybridAuth } from '@/lib/auth/hybrid'
import { getJobQueue } from '@/lib/core/async-jobs'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
import { createErrorResponse } from '@/app/api/workflows/utils'
@@ -70,7 +71,7 @@ export async function GET(
return NextResponse.json(response)
} catch (error: unknown) {
const errorMessage = error instanceof Error ? error.message : String(error)
const errorMessage = toError(error).message
logger.error(`[${requestId}] Error fetching task status:`, error)
if (errorMessage?.includes('not found')) {

View File

@@ -26,6 +26,7 @@ import { prepareExecutionContext } from '@/lib/copilot/tools/handlers/context'
import { DIRECT_TOOL_DEFS, SUBAGENT_TOOL_DEFS } from '@/lib/copilot/tools/mcp/definitions'
import { env } from '@/lib/core/config/env'
import { RateLimiter } from '@/lib/core/rate-limiter'
import { toError } from '@/lib/core/utils/helpers'
import { getBaseUrl } from '@/lib/core/utils/urls'
import { generateId } from '@/lib/core/utils/uuid'
import {
@@ -231,7 +232,7 @@ class NextResponseCapture {
try {
handler()
} catch (error) {
this.triggerErrorHandlers(error instanceof Error ? error : new Error(String(error)))
this.triggerErrorHandlers(toError(error))
}
}
}
@@ -290,7 +291,7 @@ class NextResponseCapture {
try {
this._controller.enqueue(normalized)
} catch (error) {
this.triggerErrorHandlers(error instanceof Error ? error : new Error(String(error)))
this.triggerErrorHandlers(toError(error))
}
} else {
this._pendingChunks.push(normalized)
@@ -311,7 +312,7 @@ class NextResponseCapture {
try {
this._controller.close()
} catch (error) {
this.triggerErrorHandlers(error instanceof Error ? error : new Error(String(error)))
this.triggerErrorHandlers(toError(error))
}
}
@@ -659,7 +660,7 @@ async function handleDirectToolCall(
content: [
{
type: 'text',
text: `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`,
text: `Tool execution failed: ${toError(error).message}`,
},
],
isError: true,
@@ -740,7 +741,7 @@ async function handleBuildToolCall(
logger.warn('Failed to generate workspace context for build tool call', {
workflowId: resolved.workflowId,
workspaceId: resolvedWorkspaceId,
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
}
}
@@ -789,7 +790,7 @@ async function handleBuildToolCall(
content: [
{
type: 'text',
text: `Build failed: ${error instanceof Error ? error.message : String(error)}`,
text: `Build failed: ${toError(error).message}`,
},
],
isError: true,
@@ -880,7 +881,7 @@ async function handleSubagentToolCall(
content: [
{
type: 'text',
text: `Subagent call failed: ${error instanceof Error ? error.message : String(error)}`,
text: `Subagent call failed: ${toError(error).message}`,
},
],
isError: true,

View File

@@ -3,6 +3,7 @@ import { mcpServers, workflow, workflowBlocks } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { and, eq, isNull } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { toError } from '@/lib/core/utils/helpers'
import { withMcpAuth } from '@/lib/mcp/middleware'
import { mcpService } from '@/lib/mcp/service'
import type { McpServerStatusConfig, McpTool, McpToolSchema } from '@/lib/mcp/types'
@@ -249,11 +250,7 @@ export const POST = withMcpAuth<{ id: string }>('read')(
})
} catch (error) {
logger.error(`[${requestId}] Error refreshing MCP server:`, error)
return createMcpErrorResponse(
error instanceof Error ? error : new Error('Failed to refresh MCP server'),
'Failed to refresh MCP server',
500
)
return createMcpErrorResponse(toError(error), 'Failed to refresh MCP server', 500)
}
}
)

View File

@@ -4,6 +4,7 @@ import { createLogger } from '@sim/logger'
import { and, eq, isNull } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { toError } from '@/lib/core/utils/helpers'
import {
McpDnsResolutionError,
McpDomainNotAllowedError,
@@ -138,11 +139,7 @@ export const PATCH = withMcpAuth<{ id: string }>('write')(
return createMcpSuccessResponse({ server: updatedServer })
} catch (error) {
logger.error(`[${requestId}] Error updating MCP server:`, error)
return createMcpErrorResponse(
error instanceof Error ? error : new Error('Failed to update MCP server'),
'Failed to update MCP server',
500
)
return createMcpErrorResponse(toError(error), 'Failed to update MCP server', 500)
}
}
)

View File

@@ -4,6 +4,7 @@ import { createLogger } from '@sim/logger'
import { and, eq, isNull } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { toError } from '@/lib/core/utils/helpers'
import { generateId } from '@/lib/core/utils/uuid'
import {
McpDnsResolutionError,
@@ -44,11 +45,7 @@ export const GET = withMcpAuth('read')(
return createMcpSuccessResponse({ servers })
} catch (error) {
logger.error(`[${requestId}] Error listing MCP servers:`, error)
return createMcpErrorResponse(
error instanceof Error ? error : new Error('Failed to list MCP servers'),
'Failed to list MCP servers',
500
)
return createMcpErrorResponse(toError(error), 'Failed to list MCP servers', 500)
}
}
)
@@ -220,11 +217,7 @@ export const POST = withMcpAuth('write')(
return createMcpSuccessResponse({ serverId }, 201)
} catch (error) {
logger.error(`[${requestId}] Error registering MCP server:`, error)
return createMcpErrorResponse(
error instanceof Error ? error : new Error('Failed to register MCP server'),
'Failed to register MCP server',
500
)
return createMcpErrorResponse(toError(error), 'Failed to register MCP server', 500)
}
}
)
@@ -297,11 +290,7 @@ export const DELETE = withMcpAuth('admin')(
return createMcpSuccessResponse({ message: `Server ${serverId} deleted successfully` })
} catch (error) {
logger.error(`[${requestId}] Error deleting MCP server:`, error)
return createMcpErrorResponse(
error instanceof Error ? error : new Error('Failed to delete MCP server'),
'Failed to delete MCP server',
500
)
return createMcpErrorResponse(toError(error), 'Failed to delete MCP server', 500)
}
}
)

View File

@@ -1,5 +1,6 @@
import { createLogger } from '@sim/logger'
import type { NextRequest } from 'next/server'
import { toError } from '@/lib/core/utils/helpers'
import { McpClient } from '@/lib/mcp/client'
import {
McpDnsResolutionError,
@@ -220,11 +221,7 @@ export const POST = withMcpAuth('write')(
return createMcpSuccessResponse(result, result.success ? 200 : 400)
} catch (error) {
logger.error(`[${requestId}] Error testing MCP server connection:`, error)
return createMcpErrorResponse(
error instanceof Error ? error : new Error('Failed to test server connection'),
'Failed to test server connection',
500
)
return createMcpErrorResponse(toError(error), 'Failed to test server connection', 500)
}
}
)

View File

@@ -3,6 +3,7 @@ import { workflow, workflowBlocks } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { eq } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { toError } from '@/lib/core/utils/helpers'
import { withMcpAuth } from '@/lib/mcp/middleware'
import type { McpToolSchema, StoredMcpTool } from '@/lib/mcp/types'
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
@@ -70,11 +71,7 @@ export const GET = withMcpAuth('read')(
return createMcpSuccessResponse({ tools: storedTools })
} catch (error) {
logger.error(`[${requestId}] Error fetching stored MCP tools:`, error)
return createMcpErrorResponse(
error instanceof Error ? error : new Error('Failed to fetch stored MCP tools'),
'Failed to fetch stored MCP tools',
500
)
return createMcpErrorResponse(toError(error), 'Failed to fetch stored MCP tools', 500)
}
}
)

View File

@@ -4,6 +4,7 @@ import { createLogger } from '@sim/logger'
import { and, eq, isNull } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { toError } from '@/lib/core/utils/helpers'
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
import { mcpPubSub } from '@/lib/mcp/pubsub'
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
@@ -63,11 +64,7 @@ export const GET = withMcpAuth<RouteParams>('read')(
return createMcpSuccessResponse({ server, tools })
} catch (error) {
logger.error(`[${requestId}] Error getting workflow MCP server:`, error)
return createMcpErrorResponse(
error instanceof Error ? error : new Error('Failed to get workflow MCP server'),
'Failed to get workflow MCP server',
500
)
return createMcpErrorResponse(toError(error), 'Failed to get workflow MCP server', 500)
}
}
)
@@ -146,11 +143,7 @@ export const PATCH = withMcpAuth<RouteParams>('write')(
return createMcpSuccessResponse({ server: updatedServer })
} catch (error) {
logger.error(`[${requestId}] Error updating workflow MCP server:`, error)
return createMcpErrorResponse(
error instanceof Error ? error : new Error('Failed to update workflow MCP server'),
'Failed to update workflow MCP server',
500
)
return createMcpErrorResponse(toError(error), 'Failed to update workflow MCP server', 500)
}
}
)
@@ -201,11 +194,7 @@ export const DELETE = withMcpAuth<RouteParams>('admin')(
return createMcpSuccessResponse({ message: `Server ${serverId} deleted successfully` })
} catch (error) {
logger.error(`[${requestId}] Error deleting workflow MCP server:`, error)
return createMcpErrorResponse(
error instanceof Error ? error : new Error('Failed to delete workflow MCP server'),
'Failed to delete workflow MCP server',
500
)
return createMcpErrorResponse(toError(error), 'Failed to delete workflow MCP server', 500)
}
}
)

View File

@@ -4,6 +4,7 @@ import { createLogger } from '@sim/logger'
import { and, eq, isNull } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { toError } from '@/lib/core/utils/helpers'
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
import { mcpPubSub } from '@/lib/mcp/pubsub'
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
@@ -63,11 +64,7 @@ export const GET = withMcpAuth<RouteParams>('read')(
return createMcpSuccessResponse({ tool })
} catch (error) {
logger.error(`[${requestId}] Error getting tool:`, error)
return createMcpErrorResponse(
error instanceof Error ? error : new Error('Failed to get tool'),
'Failed to get tool',
500
)
return createMcpErrorResponse(toError(error), 'Failed to get tool', 500)
}
}
)
@@ -164,11 +161,7 @@ export const PATCH = withMcpAuth<RouteParams>('write')(
return createMcpSuccessResponse({ tool: updatedTool })
} catch (error) {
logger.error(`[${requestId}] Error updating tool:`, error)
return createMcpErrorResponse(
error instanceof Error ? error : new Error('Failed to update tool'),
'Failed to update tool',
500
)
return createMcpErrorResponse(toError(error), 'Failed to update tool', 500)
}
}
)
@@ -232,11 +225,7 @@ export const DELETE = withMcpAuth<RouteParams>('write')(
return createMcpSuccessResponse({ message: `Tool ${toolId} deleted successfully` })
} catch (error) {
logger.error(`[${requestId}] Error deleting tool:`, error)
return createMcpErrorResponse(
error instanceof Error ? error : new Error('Failed to delete tool'),
'Failed to delete tool',
500
)
return createMcpErrorResponse(toError(error), 'Failed to delete tool', 500)
}
}
)

View File

@@ -4,6 +4,7 @@ import { createLogger } from '@sim/logger'
import { and, eq, isNull } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { toError } from '@/lib/core/utils/helpers'
import { generateId } from '@/lib/core/utils/uuid'
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
import { mcpPubSub } from '@/lib/mcp/pubsub'
@@ -72,11 +73,7 @@ export const GET = withMcpAuth<RouteParams>('read')(
return createMcpSuccessResponse({ tools })
} catch (error) {
logger.error(`[${requestId}] Error listing tools:`, error)
return createMcpErrorResponse(
error instanceof Error ? error : new Error('Failed to list tools'),
'Failed to list tools',
500
)
return createMcpErrorResponse(toError(error), 'Failed to list tools', 500)
}
}
)
@@ -237,11 +234,7 @@ export const POST = withMcpAuth<RouteParams>('write')(
return createMcpSuccessResponse({ tool }, 201)
} catch (error) {
logger.error(`[${requestId}] Error adding tool:`, error)
return createMcpErrorResponse(
error instanceof Error ? error : new Error('Failed to add tool'),
'Failed to add tool',
500
)
return createMcpErrorResponse(toError(error), 'Failed to add tool', 500)
}
}
)

View File

@@ -4,6 +4,7 @@ import { createLogger } from '@sim/logger'
import { and, eq, inArray, isNull, sql } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { toError } from '@/lib/core/utils/helpers'
import { generateId } from '@/lib/core/utils/uuid'
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
import { mcpPubSub } from '@/lib/mcp/pubsub'
@@ -82,11 +83,7 @@ export const GET = withMcpAuth('read')(
return createMcpSuccessResponse({ servers: serversWithToolNames })
} catch (error) {
logger.error(`[${requestId}] Error listing workflow MCP servers:`, error)
return createMcpErrorResponse(
error instanceof Error ? error : new Error('Failed to list workflow MCP servers'),
'Failed to list workflow MCP servers',
500
)
return createMcpErrorResponse(toError(error), 'Failed to list workflow MCP servers', 500)
}
}
)
@@ -221,11 +218,7 @@ export const POST = withMcpAuth('write')(
return createMcpSuccessResponse({ server, addedTools }, 201)
} catch (error) {
logger.error(`[${requestId}] Error creating workflow MCP server:`, error)
return createMcpErrorResponse(
error instanceof Error ? error : new Error('Failed to create workflow MCP server'),
'Failed to create workflow MCP server',
500
)
return createMcpErrorResponse(toError(error), 'Failed to create workflow MCP server', 500)
}
}
)

View File

@@ -19,6 +19,7 @@ import { readEvents } from '@/lib/copilot/request/session/buffer'
import { readFilePreviewSessions } from '@/lib/copilot/request/session/file-preview-session'
import { type StreamBatchEvent, toStreamBatchEvent } from '@/lib/copilot/request/session/types'
import { taskPubSub } from '@/lib/copilot/tasks'
import { toError } from '@/lib/core/utils/helpers'
import { captureServerEvent } from '@/lib/posthog/server'
const logger = createLogger('MothershipChatAPI')
@@ -66,7 +67,7 @@ export async function GET(
logger.warn('Failed to read preview sessions for mothership chat', {
chatId,
conversationId: chat.conversationId,
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
return []
}),
@@ -75,7 +76,7 @@ export async function GET(
logger.warn('Failed to fetch latest run for mothership chat snapshot', {
chatId,
conversationId: chat.conversationId,
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
return null
})
@@ -90,7 +91,7 @@ export async function GET(
logger.warn('Failed to read stream snapshot for mothership chat', {
chatId,
conversationId: chat.conversationId,
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
}
}

View File

@@ -6,6 +6,7 @@ import { buildIntegrationToolSchemas } from '@/lib/copilot/chat/payload'
import { generateWorkspaceContext } from '@/lib/copilot/chat/workspace-context'
import { runHeadlessCopilotLifecycle } from '@/lib/copilot/request/lifecycle/headless'
import { requestExplicitStreamAbort } from '@/lib/copilot/request/session/explicit-abort'
import { toError } from '@/lib/core/utils/helpers'
import { generateId } from '@/lib/core/utils/uuid'
import {
assertActiveWorkspaceAccess,
@@ -110,7 +111,7 @@ export async function POST(req: NextRequest) {
chatId: effectiveChatId,
}).catch((error) => {
reqLogger.warn('Failed to send explicit abort for mothership execution', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
})
}

View File

@@ -4,6 +4,7 @@ import { createLogger } from '@sim/logger'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
import { checkWorkspaceAccess } from '@/lib/workspaces/permissions/utils'
import {
@@ -112,7 +113,7 @@ export async function POST(request: NextRequest) {
logger.error(`[${requestId}] Failed to resolve Vertex credential:`, {
provider,
model,
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
hasVertexCredential: !!vertexCredential,
})
return NextResponse.json(
@@ -258,17 +259,14 @@ export async function POST(request: NextRequest) {
} catch (error) {
const executionTime = Date.now() - startTime
logger.error(`[${requestId}] Provider request failed:`, {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
errorName: error instanceof Error ? error.name : 'Unknown',
errorStack: error instanceof Error ? error.stack : undefined,
executionTime,
timestamp: new Date().toISOString(),
})
return NextResponse.json(
{ error: error instanceof Error ? error.message : String(error) },
{ status: 500 }
)
return NextResponse.json({ error: toError(error).message }, { status: 500 })
}
}

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { AuthType } from '@/lib/auth/hybrid'
import { getJobQueue } from '@/lib/core/async-jobs'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
import { SSE_HEADERS } from '@/lib/core/utils/sse'
import { getBaseUrl } from '@/lib/core/utils/urls'
@@ -235,7 +236,7 @@ export async function POST(
})
} catch (dispatchError) {
logger.error('Failed to dispatch async resume execution', {
error: dispatchError instanceof Error ? dispatchError.message : String(dispatchError),
error: toError(dispatchError).message,
resumeExecutionId: enqueueResult.resumeExecutionId,
})
return NextResponse.json(

View File

@@ -4,6 +4,7 @@ import { and, eq, isNull, lt, lte, ne, not, or, sql } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { verifyCronAuth } from '@/lib/auth/internal'
import { getJobQueue, shouldExecuteInline } from '@/lib/core/async-jobs'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
import { generateId } from '@/lib/core/utils/uuid'
import {
@@ -136,7 +137,7 @@ export async function GET(request: NextRequest) {
const output = await executeScheduleJob(payload)
await jobQueue.completeJob(jobId, output)
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)
const errorMessage = toError(error).message
logger.error(
`[${requestId}] Schedule execution failed for workflow ${schedule.workflowId}`,
{
@@ -191,7 +192,7 @@ export async function GET(request: NextRequest) {
await executeJobInline(payload)
} catch (error) {
logger.error(`[${requestId}] Job execution failed for ${job.id}`, {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
await releaseScheduleLock(
job.id,

View File

@@ -1,6 +1,7 @@
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
import { generateId } from '@/lib/core/utils/uuid'
import {
@@ -163,7 +164,7 @@ export async function POST(request: NextRequest, { params }: RouteParams) {
inserted += result.length
}
} catch (err) {
const message = err instanceof Error ? err.message : String(err)
const message = toError(err).message
logger.warn(`[${requestId}] Append failed mid-import for table ${tableId}`, {
inserted,
total: coerced.length,
@@ -238,7 +239,7 @@ export async function POST(request: NextRequest, { params }: RouteParams) {
},
})
} catch (err) {
const message = err instanceof Error ? err.message : String(err)
const message = toError(err).message
const isClientError =
message.includes('row limit') ||
message.includes('Schema validation') ||
@@ -251,7 +252,7 @@ export async function POST(request: NextRequest, { params }: RouteParams) {
throw err
}
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
const message = toError(error).message
logger.error(`[${requestId}] CSV import into existing table failed:`, error)
const isClientError =

View File

@@ -5,6 +5,7 @@ import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
import type { RowData } from '@/lib/table'
import { deleteRow, updateRow } from '@/lib/table'
@@ -193,7 +194,7 @@ export async function PATCH(request: NextRequest, { params }: RowRouteParams) {
)
}
const errorMessage = error instanceof Error ? error.message : String(error)
const errorMessage = toError(error).message
if (errorMessage === 'Row not found') {
return NextResponse.json({ error: errorMessage }, { status: 404 })
@@ -260,7 +261,7 @@ export async function DELETE(request: NextRequest, { params }: RowRouteParams) {
)
}
const errorMessage = error instanceof Error ? error.message : String(error)
const errorMessage = toError(error).message
if (errorMessage === 'Row not found') {
return NextResponse.json({ error: errorMessage }, { status: 404 })

View File

@@ -5,6 +5,7 @@ import { and, eq, sql } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
import type { Filter, RowData, Sort, TableSchema } from '@/lib/table'
import {
@@ -181,7 +182,7 @@ async function handleBatchInsert(
},
})
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)
const errorMessage = toError(error).message
if (
errorMessage.includes('row limit') ||
@@ -289,7 +290,7 @@ export async function POST(request: NextRequest, { params }: TableRowsRouteParam
)
}
const errorMessage = error instanceof Error ? error.message : String(error)
const errorMessage = toError(error).message
if (
errorMessage.includes('row limit') ||
@@ -516,7 +517,7 @@ export async function PUT(request: NextRequest, { params }: TableRowsRouteParams
)
}
const errorMessage = error instanceof Error ? error.message : String(error)
const errorMessage = toError(error).message
if (
errorMessage.includes('Row size exceeds') ||
@@ -616,7 +617,7 @@ export async function DELETE(request: NextRequest, { params }: TableRowsRoutePar
)
}
const errorMessage = error instanceof Error ? error.message : String(error)
const errorMessage = toError(error).message
if (errorMessage.includes('Filter is required')) {
return NextResponse.json({ error: errorMessage }, { status: 400 })
@@ -685,7 +686,7 @@ export async function PATCH(request: NextRequest, { params }: TableRowsRoutePara
)
}
const errorMessage = error instanceof Error ? error.message : String(error)
const errorMessage = toError(error).message
if (
errorMessage.includes('Row size exceeds') ||

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
import type { RowData } from '@/lib/table'
import { upsertRow } from '@/lib/table'
@@ -87,7 +88,7 @@ export async function POST(request: NextRequest, { params }: UpsertRouteParams)
)
}
const errorMessage = error instanceof Error ? error.message : String(error)
const errorMessage = toError(error).message
// Service layer throws descriptive errors for validation/capacity issues
if (

View File

@@ -1,6 +1,7 @@
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
import { generateId } from '@/lib/core/utils/uuid'
import {
@@ -124,7 +125,7 @@ export async function POST(request: NextRequest) {
throw insertError
}
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
const message = toError(error).message
logger.error(`[${requestId}] CSV import failed:`, error)
const isClientError =

View File

@@ -4,6 +4,7 @@ import { createLogger } from '@sim/logger'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalApiKey } from '@/lib/copilot/request/http'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
import { sanitizeForCopilot } from '@/lib/workflows/sanitization/json-sanitizer'
@@ -97,7 +98,7 @@ export async function GET(request: NextRequest) {
}
} catch (error) {
logger.error(`[${requestId}] Error sanitizing template ${template.id}`, {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
return null
}
@@ -112,7 +113,7 @@ export async function GET(request: NextRequest) {
return NextResponse.json(response)
} catch (error) {
logger.error(`[${requestId}] Error fetching approved sanitized templates`, {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})
return NextResponse.json(

View File

@@ -5,6 +5,7 @@ import { z } from 'zod'
import { createA2AClient, extractTextContent, isTerminalState } from '@/lib/a2a/utils'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { validateUrlWithDNS } from '@/lib/core/security/input-validation.server'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
import { generateId } from '@/lib/core/utils/uuid'
@@ -89,7 +90,7 @@ export async function POST(request: NextRequest) {
parts.push(dataPart)
} catch (parseError) {
logger.warn(`[${requestId}] Failed to parse data as JSON, skipping DataPart`, {
error: parseError instanceof Error ? parseError.message : String(parseError),
error: toError(parseError).message,
})
}
}

View File

@@ -2,12 +2,17 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateUrlWithDNS } from '@/lib/core/security/input-validation.server'
import { secureFetchWithPinnedIP } from '@/lib/core/security/input-validation.server'
import { generateRequestId } from '@/lib/core/utils/request'
import { FileInputSchema, type RawFileInput } from '@/lib/uploads/utils/file-schemas'
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
import { agiloftLogin, agiloftLogout, buildAttachFileUrl } from '@/tools/agiloft/utils'
import {
agiloftLogin,
agiloftLogout,
buildAttachFileUrl,
validateInstanceUrl,
} from '@/tools/agiloft/utils'
export const dynamic = 'force-dynamic'
@@ -60,18 +65,20 @@ export async function POST(request: NextRequest) {
const fileBuffer = await downloadFileFromStorage(userFile, requestId, logger)
const resolvedFileName = data.fileName || userFile.name || 'attachment'
const urlValidation = await validateUrlWithDNS(data.instanceUrl, 'instanceUrl')
if (!urlValidation.isValid) {
let resolvedIP: string
try {
resolvedIP = await validateInstanceUrl(data.instanceUrl)
} catch (error) {
logger.warn(`[${requestId}] SSRF attempt blocked for Agiloft instance URL`, {
instanceUrl: data.instanceUrl,
})
return NextResponse.json(
{ success: false, error: urlValidation.error || 'Invalid instance URL' },
{ success: false, error: error instanceof Error ? error.message : 'Invalid instance URL' },
{ status: 400 }
)
}
const token = await agiloftLogin(data)
const token = await agiloftLogin(data, resolvedIP)
const base = data.instanceUrl.replace(/\/$/, '')
try {
@@ -79,7 +86,7 @@ export async function POST(request: NextRequest) {
logger.info(`[${requestId}] Uploading file to Agiloft: ${resolvedFileName}`)
const agiloftResponse = await fetch(url, {
const agiloftResponse = await secureFetchWithPinnedIP(url, resolvedIP, {
method: 'PUT',
headers: {
'Content-Type': userFile.type || 'application/octet-stream',
@@ -123,7 +130,7 @@ export async function POST(request: NextRequest) {
},
})
} finally {
await agiloftLogout(data.instanceUrl, data.knowledgeBase, token)
await agiloftLogout(data.instanceUrl, data.knowledgeBase, token, resolvedIP)
}
} catch (error) {
if (error instanceof z.ZodError) {

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
export const dynamic = 'force-dynamic'
@@ -115,7 +116,7 @@ export async function POST(request: NextRequest) {
})
} catch (error: any) {
logger.error('Error creating Asana task:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
export const dynamic = 'force-dynamic'
@@ -114,7 +115,7 @@ export async function PUT(request: NextRequest) {
})
} catch (error: any) {
logger.error('Error updating Asana task:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -6,6 +6,7 @@ import {
type ResultField,
} from '@aws-sdk/client-cloudwatch-logs'
import { DEFAULT_EXECUTION_TIMEOUT_MS } from '@/lib/core/execution-limits'
import { sleep } from '@/lib/core/utils/helpers'
interface AwsCredentials {
region: string
@@ -79,7 +80,7 @@ export async function pollQueryResults(
throw new Error(`CloudWatch Log Insights query ${status.toLowerCase()}`)
}
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs))
await sleep(pollIntervalMs)
}
// Timeout -- fetch one last time for partial results

View File

@@ -5,6 +5,7 @@ import {
secureFetchWithPinnedIP,
validateUrlWithDNS,
} from '@/lib/core/security/input-validation.server'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
const logger = createLogger('ImageProxyAPI')
@@ -83,7 +84,7 @@ export async function GET(request: NextRequest) {
},
})
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)
const errorMessage = toError(error).message
logger.error(`[${requestId}] Image proxy error:`, { error: errorMessage })
return new NextResponse(`Failed to proxy image: ${errorMessage}`, {

View File

@@ -3,6 +3,7 @@ import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage, toAdf } from '@/tools/jira/utils'
export const dynamic = 'force-dynamic'
@@ -182,7 +183,7 @@ export async function PUT(request: NextRequest) {
})
} catch (error: any) {
logger.error('Error updating Jira issue:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage, toAdf } from '@/tools/jira/utils'
export const dynamic = 'force-dynamic'
@@ -225,7 +226,7 @@ export async function POST(request: NextRequest) {
})
} catch (error: any) {
logger.error('Error creating Jira issue:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -7,6 +7,7 @@ import {
validateJiraCloudId,
validateJiraIssueKey,
} from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -199,7 +200,7 @@ export async function POST(request: NextRequest) {
return NextResponse.json({ error: 'Invalid action' }, { status: 400 })
} catch (error) {
logger.error('Error in approvals operation:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -112,7 +113,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error adding comment:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -105,7 +106,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error fetching comments:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -157,7 +158,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error with customers operation:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmFormsApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -96,7 +97,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error getting form answers:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmFormsApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -104,7 +105,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error attaching form:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmFormsApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -111,7 +112,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error copying forms:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmFormsApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -96,7 +97,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error deleting form:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmFormsApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -97,7 +98,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error externalising form:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmFormsApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -98,7 +99,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error getting form:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmFormsApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -97,7 +98,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error internalising form:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmFormsApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -96,7 +97,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error fetching issue forms:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmFormsApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -97,7 +98,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error reopening form:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmFormsApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -103,7 +104,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error saving form answers:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmFormsApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -98,7 +99,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error fetching form structure:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmFormsApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -97,7 +98,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error submitting form:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmFormsApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -96,7 +97,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error fetching form templates:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -6,6 +6,7 @@ import {
validateEnum,
validateJiraCloudId,
} from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -169,7 +170,7 @@ export async function POST(request: NextRequest) {
return NextResponse.json({ error: 'Invalid action' }, { status: 400 })
} catch (error) {
logger.error('Error in organization operation:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -91,7 +92,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error fetching organizations:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -6,6 +6,7 @@ import {
validateJiraCloudId,
validateJiraIssueKey,
} from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -174,7 +175,7 @@ export async function POST(request: NextRequest) {
return NextResponse.json({ error: 'Invalid action' }, { status: 400 })
} catch (error) {
logger.error('Error in participants operation:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -100,7 +101,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error fetching queues:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -6,6 +6,7 @@ import {
validateJiraCloudId,
validateJiraIssueKey,
} from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -250,7 +251,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error with request operation:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -6,6 +6,7 @@ import {
validateEnum,
validateJiraCloudId,
} from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -140,7 +141,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error fetching requests:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -108,7 +109,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error fetching request type fields:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -104,7 +105,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error fetching request types:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateJiraCloudId } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -82,7 +83,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error fetching service desks:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -92,7 +93,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error fetching SLA info:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -6,6 +6,7 @@ import {
validateJiraCloudId,
validateJiraIssueKey,
} from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -116,7 +117,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error transitioning request:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils'
import { getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
@@ -92,7 +93,7 @@ export async function POST(request: NextRequest) {
})
} catch (error) {
logger.error('Error fetching transitions:', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
stack: error instanceof Error ? error.stack : undefined,
})

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { NextResponse } from 'next/server'
import { authorizeCredentialUse } from '@/lib/auth/credential-access'
import { validateMicrosoftGraphId } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
export const dynamic = 'force-dynamic'
@@ -99,7 +100,7 @@ export async function POST(request: Request) {
} catch (innerError) {
logger.error('Error during API requests:', innerError)
const errorMessage = innerError instanceof Error ? innerError.message : String(innerError)
const errorMessage = toError(innerError).message
if (
errorMessage.includes('auth') ||
errorMessage.includes('token') ||

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { NextResponse } from 'next/server'
import { authorizeCredentialUse } from '@/lib/auth/credential-access'
import { validateMicrosoftGraphId } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
export const dynamic = 'force-dynamic'
@@ -110,15 +111,13 @@ const getChatDisplayName = async (
}
} catch (error) {
logger.warn(
`Failed to get better name from messages for chat ${chatId}: ${error instanceof Error ? error.message : String(error)}`
`Failed to get better name from messages for chat ${chatId}: ${toError(error).message}`
)
}
return `Chat ${chatId.split(':')[0] || chatId.substring(0, 8)}...`
} catch (error) {
logger.warn(
`Failed to get display name for chat ${chatId}: ${error instanceof Error ? error.message : String(error)}`
)
logger.warn(`Failed to get display name for chat ${chatId}: ${toError(error).message}`)
return `Chat ${chatId.split(':')[0] || chatId.substring(0, 8)}...`
}
}
@@ -200,7 +199,7 @@ export async function POST(request: Request) {
} catch (innerError) {
logger.error('Error during API requests:', innerError)
const errorMessage = innerError instanceof Error ? innerError.message : String(innerError)
const errorMessage = toError(innerError).message
if (
errorMessage.includes('auth') ||
errorMessage.includes('token') ||

View File

@@ -1,6 +1,7 @@
import { createLogger } from '@sim/logger'
import { NextResponse } from 'next/server'
import { authorizeCredentialUse } from '@/lib/auth/credential-access'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
@@ -89,7 +90,7 @@ export async function POST(request: Request) {
logger.error('Error during API requests:', innerError)
// Check if it's an authentication error
const errorMessage = innerError instanceof Error ? innerError.message : String(innerError)
const errorMessage = toError(innerError).message
if (
errorMessage.includes('auth') ||
errorMessage.includes('token') ||

View File

@@ -12,6 +12,7 @@ import type {
import { createLogger } from '@sim/logger'
import * as ipaddr from 'ipaddr.js'
import { secureFetchWithPinnedIP } from '@/lib/core/security/input-validation.server'
import { toError } from '@/lib/core/utils/helpers'
/** Connect-format field type strings returned by normalization. */
type ConnectFieldType =
@@ -283,7 +284,7 @@ async function validateConnectServerUrl(serverUrl: string): Promise<string> {
if (error instanceof Error && error.message.startsWith('1Password')) throw error
connectLogger.warn('DNS lookup failed for 1Password Connect server URL', {
hostname: clean,
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
throw new Error('1Password server URL hostname could not be resolved')
}

View File

@@ -5,6 +5,7 @@ import { eq } from 'drizzle-orm'
import { NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
import { refreshAccessTokenIfNeeded, resolveOAuthAccountId } from '@/app/api/auth/oauth/utils'
@@ -135,7 +136,7 @@ export async function GET(request: Request) {
} catch (innerError) {
logger.error('Error during API requests:', innerError)
const errorMessage = innerError instanceof Error ? innerError.message : String(innerError)
const errorMessage = toError(innerError).message
if (
errorMessage.includes('auth') ||
errorMessage.includes('token') ||

View File

@@ -1,5 +1,6 @@
import { type Attributes, Client, type ConnectConfig, type SFTPWrapper } from 'ssh2'
import { validateDatabaseHost } from '@/lib/core/security/input-validation.server'
import { toError } from '@/lib/core/utils/helpers'
const S_IFMT = 0o170000
const S_IFDIR = 0o040000
@@ -151,7 +152,7 @@ export async function createSftpConnection(config: SftpConnectionConfig): Promis
try {
client.connect(connectConfig)
} catch (err) {
reject(formatSftpError(err instanceof Error ? err : new Error(String(err)), { host, port }))
reject(formatSftpError(toError(err), { host, port }))
}
})
}

View File

@@ -4,6 +4,7 @@ import nodemailer from 'nodemailer'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateDatabaseHost } from '@/lib/core/security/input-validation.server'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
import { RawFileInputArraySchema } from '@/lib/uploads/utils/file-schemas'
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
@@ -223,7 +224,7 @@ export async function POST(request: NextRequest) {
}
logger.error(`[${requestId}] Error sending email via SMTP:`, {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
code: isNodeError(error) ? error.code : undefined,
responseCode: hasResponseCode(error) ? error.responseCode : undefined,
})

View File

@@ -1,6 +1,7 @@
import { createLogger } from '@sim/logger'
import { type Attributes, Client, type ConnectConfig } from 'ssh2'
import { validateDatabaseHost } from '@/lib/core/security/input-validation.server'
import { toError } from '@/lib/core/utils/helpers'
const logger = createLogger('SSHUtils')
@@ -168,7 +169,7 @@ export async function createSSHConnection(config: SSHConnectionConfig): Promise<
try {
client.connect(connectConfig)
} catch (err) {
reject(formatSSHError(err instanceof Error ? err : new Error(String(err)), { host, port }))
reject(formatSSHError(toError(err), { host, port }))
}
})
}

View File

@@ -7,6 +7,7 @@ import {
secureFetchWithPinnedIP,
validateUrlWithDNS,
} from '@/lib/core/security/input-validation.server'
import { sleep } from '@/lib/core/utils/helpers'
import { generateId } from '@/lib/core/utils/uuid'
import { getMimeTypeFromExtension, isInternalFileUrl } from '@/lib/uploads/utils/file-utils'
import {
@@ -663,7 +664,7 @@ async function transcribeWithAssemblyAI(
throw new Error(`AssemblyAI transcription failed: ${transcript.error}`)
}
await new Promise((resolve) => setTimeout(resolve, 5000))
await sleep(5000)
attempts++
}

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { validateSupabaseProjectId } from '@/lib/core/security/input-validation'
import { generateRequestId } from '@/lib/core/utils/request'
import { FileInputSchema } from '@/lib/uploads/utils/file-schemas'
import { processSingleFileToUserFile } from '@/lib/uploads/utils/file-utils'
@@ -12,7 +13,10 @@ export const dynamic = 'force-dynamic'
const logger = createLogger('SupabaseStorageUploadAPI')
const SupabaseStorageUploadSchema = z.object({
projectId: z.string().min(1, 'Project ID is required'),
projectId: z
.string()
.min(1, 'Project ID is required')
.regex(/^[a-z0-9]+$/, 'Project ID must contain only lowercase alphanumeric characters'),
apiKey: z.string().min(1, 'API key is required'),
bucket: z.string().min(1, 'Bucket name is required'),
fileName: z.string().min(1, 'File name is required'),
@@ -162,7 +166,12 @@ export async function POST(request: NextRequest) {
fullPath = `${folderPath}${validatedData.fileName}`
}
const supabaseUrl = `https://${validatedData.projectId}.supabase.co/storage/v1/object/${validatedData.bucket}/${fullPath}`
const projectValidation = validateSupabaseProjectId(validatedData.projectId)
if (!projectValidation.isValid) {
return NextResponse.json({ success: false, error: projectValidation.error }, { status: 400 })
}
const supabaseUrl = `https://${projectValidation.sanitized}.supabase.co/storage/v1/object/${validatedData.bucket}/${fullPath}`
const headers: Record<string, string> = {
apikey: validatedData.apiKey,
@@ -218,7 +227,7 @@ export async function POST(request: NextRequest) {
path: fullPath,
})
const publicUrl = `https://${validatedData.projectId}.supabase.co/storage/v1/object/public/${validatedData.bucket}/${fullPath}`
const publicUrl = `https://${projectValidation.sanitized}.supabase.co/storage/v1/object/public/${validatedData.bucket}/${fullPath}`
return NextResponse.json({
success: true,

View File

@@ -9,6 +9,7 @@ import {
secureFetchWithPinnedIP,
validateUrlWithDNS,
} from '@/lib/core/security/input-validation.server'
import { sleep } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
import { RawFileInputSchema } from '@/lib/uploads/utils/file-schemas'
import { isInternalFileUrl, processSingleFileToUserFile } from '@/lib/uploads/utils/file-utils'
@@ -169,10 +170,6 @@ function parseS3Uri(s3Uri: string): { bucket: string; key: string } {
return { bucket, key }
}
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms))
}
async function callTextractAsync(
host: string,
amzTarget: string,

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { getMaxExecutionTimeout } from '@/lib/core/execution-limits'
import { sleep } from '@/lib/core/utils/helpers'
import { generateId } from '@/lib/core/utils/uuid'
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
import type { UserFile } from '@/executor/types'
@@ -974,7 +975,3 @@ function getVideoDimensions(
return { width, height }
}
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms))
}

View File

@@ -6,6 +6,7 @@ import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { hasPaidSubscription } from '@/lib/billing'
import { toError } from '@/lib/core/utils/helpers'
const logger = createLogger('SubscriptionTransferAPI')
@@ -114,7 +115,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
})
} catch (error) {
logger.error('Error transferring subscription', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
return NextResponse.json({ error: 'Failed to transfer subscription' }, { status: 500 })
}

View File

@@ -4,6 +4,7 @@ import { z } from 'zod'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { getUserUsageLogs, type UsageLogSource } from '@/lib/billing/core/usage-log'
import { dollarsToCredits } from '@/lib/billing/credits/conversion'
import { toError } from '@/lib/core/utils/helpers'
const logger = createLogger('UsageLogsAPI')
@@ -109,7 +110,7 @@ export async function GET(req: NextRequest) {
})
} catch (error) {
logger.error('Failed to get usage logs', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
return NextResponse.json(

View File

@@ -3,6 +3,7 @@ import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { COPILOT_REQUEST_MODES } from '@/lib/copilot/constants'
import { runHeadlessCopilotLifecycle } from '@/lib/copilot/request/lifecycle/headless'
import { toError } from '@/lib/core/utils/helpers'
import { generateId } from '@/lib/core/utils/uuid'
import { getWorkflowById, resolveWorkflowIdForUser } from '@/lib/workflows/utils'
import { authenticateV1Request } from '@/app/api/v1/auth'
@@ -136,7 +137,7 @@ export async function POST(req: NextRequest) {
? `Headless copilot request failed [messageId:${messageId}]`
: 'Headless copilot request failed',
{
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
}
)
return NextResponse.json({ success: false, error: 'Internal server error' }, { status: 500 })

View File

@@ -4,6 +4,7 @@ import { createLogger } from '@sim/logger'
import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
import type { RowData } from '@/lib/table'
import { updateRow } from '@/lib/table'
@@ -196,7 +197,7 @@ export async function PATCH(request: NextRequest, { params }: RowRouteParams) {
)
}
const errorMessage = error instanceof Error ? error.message : String(error)
const errorMessage = toError(error).message
if (errorMessage === 'Row not found') {
return NextResponse.json({ error: errorMessage }, { status: 404 })

View File

@@ -4,6 +4,7 @@ import { createLogger } from '@sim/logger'
import { and, eq, sql } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
import type { Filter, RowData, Sort, TableSchema } from '@/lib/table'
import {
@@ -162,7 +163,7 @@ async function handleBatchInsert(
},
})
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)
const errorMessage = toError(error).message
if (
errorMessage.includes('row limit') ||
@@ -392,7 +393,7 @@ export async function POST(request: NextRequest, { params }: TableRowsRouteParam
)
}
const errorMessage = error instanceof Error ? error.message : String(error)
const errorMessage = toError(error).message
if (
errorMessage.includes('row limit') ||
@@ -489,7 +490,7 @@ export async function PUT(request: NextRequest, { params }: TableRowsRouteParams
)
}
const errorMessage = error instanceof Error ? error.message : String(error)
const errorMessage = toError(error).message
if (
errorMessage.includes('Row size exceeds') ||
@@ -591,7 +592,7 @@ export async function DELETE(request: NextRequest, { params }: TableRowsRoutePar
)
}
const errorMessage = error instanceof Error ? error.message : String(error)
const errorMessage = toError(error).message
if (errorMessage.includes('Filter is required')) {
return NextResponse.json({ error: errorMessage }, { status: 400 })

View File

@@ -1,6 +1,7 @@
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
import type { RowData } from '@/lib/table'
import { upsertRow } from '@/lib/table'
@@ -99,7 +100,7 @@ export async function POST(request: NextRequest, { params }: UpsertRouteParams)
)
}
const errorMessage = error instanceof Error ? error.message : String(error)
const errorMessage = toError(error).message
if (
errorMessage.includes('unique column') ||

View File

@@ -9,6 +9,7 @@ import {
getTimeoutErrorMessage,
isTimeoutError,
} from '@/lib/core/execution-limits'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
import { SSE_HEADERS } from '@/lib/core/utils/sse'
import { getBaseUrl } from '@/lib/core/utils/urls'
@@ -218,7 +219,7 @@ async function handleAsyncExecution(params: AsyncExecutionParams): Promise<NextR
const output = await executeWorkflowJob(payload)
await jobQueue.completeJob(jobId, output)
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)
const errorMessage = toError(error).message
asyncLogger.error('Async workflow execution failed', {
jobId,
error: errorMessage,
@@ -228,10 +229,7 @@ async function handleAsyncExecution(params: AsyncExecutionParams): Promise<NextR
} catch (markFailedError) {
asyncLogger.error('Failed to mark job as failed', {
jobId,
error:
markFailedError instanceof Error
? markFailedError.message
: String(markFailedError),
error: toError(markFailedError).message,
})
}
}
@@ -1294,7 +1292,7 @@ async function handleExecutePost(
await eventWriter.close()
} catch (closeError) {
reqLogger.warn('Failed to close event writer', {
error: closeError instanceof Error ? closeError.message : String(closeError),
error: toError(closeError).message,
})
}
if (finalMetaStatus) {

View File

@@ -1,6 +1,7 @@
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { sleep, toError } from '@/lib/core/utils/helpers'
import { SSE_HEADERS } from '@/lib/core/utils/sse'
import {
type ExecutionStreamStatus,
@@ -101,7 +102,7 @@ export async function GET(
}
while (!closed && Date.now() < pollDeadline) {
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS))
await sleep(POLL_INTERVAL_MS)
if (closed) return
const newEvents = await readExecutionEvents(executionId, lastEventId)
@@ -135,7 +136,7 @@ export async function GET(
} catch (error) {
logger.error('Error in reconnection stream', {
executionId,
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
if (!closed) {
try {

View File

@@ -6,6 +6,7 @@ import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { env } from '@/lib/core/config/env'
import { toError } from '@/lib/core/utils/helpers'
import { generateRequestId } from '@/lib/core/utils/request'
import { getSocketServerUrl } from '@/lib/core/utils/urls'
import { extractAndPersistCustomTools } from '@/lib/workflows/persistence/custom-tools-persistence'
@@ -150,7 +151,7 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
} catch (error) {
logger.error('Failed to fetch workflow state', {
workflowId,
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
}

View File

@@ -12,6 +12,7 @@ import {
import { getSession } from '@/lib/auth'
import { decryptSecret } from '@/lib/core/security/encryption'
import { secureFetchWithValidation } from '@/lib/core/security/input-validation.server'
import { toError } from '@/lib/core/utils/helpers'
import { getBaseUrl } from '@/lib/core/utils/urls'
import { generateId } from '@/lib/core/utils/uuid'
import { sendEmail } from '@/lib/messaging/email/mailer'
@@ -159,7 +160,7 @@ async function testWebhook(subscription: typeof workspaceNotificationSubscriptio
}
} catch (error: unknown) {
logger.warn('Webhook test failed', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
return { success: false, error: 'Failed to deliver webhook' }
}
@@ -273,7 +274,7 @@ async function testSlack(
}
} catch (error: unknown) {
logger.warn('Slack test notification failed', {
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
return { success: false, error: 'Failed to send Slack notification' }
}

View File

@@ -5,6 +5,7 @@ import { createLogger } from '@sim/logger'
import { ArrowDown, Download, Loader2, Music } from 'lucide-react'
import { Button } from '@/components/emcn'
import { DefaultFileIcon, getDocumentIcon } from '@/components/icons/document-icons'
import { sleep } from '@/lib/core/utils/helpers'
import type { ChatFile } from '@/app/chat/components/message/message'
const logger = createLogger('ChatFileDownload')
@@ -157,7 +158,7 @@ export function ChatFileDownloadAll({ files }: ChatFileDownloadAllProps) {
logger.info(`Downloaded file ${i + 1}/${files.length}: ${file.name}`)
if (i < files.length - 1) {
await new Promise((resolve) => setTimeout(resolve, 150))
await sleep(150)
}
} catch (error) {
logger.error(`Failed to download file ${file.name}:`, error)

View File

@@ -1,5 +1,6 @@
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { toError } from '@/lib/core/utils/helpers'
const logger = createLogger('PostHogProxy')
@@ -59,7 +60,7 @@ async function handler(request: NextRequest) {
logger.error('PostHog proxy error', {
url,
method: request.method,
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
return new NextResponse(null, { status: 502 })
}

View File

@@ -86,6 +86,7 @@ import {
reportManualRunToolStop,
} from '@/lib/copilot/tools/client/run-tool-execution'
import { isWorkflowToolName } from '@/lib/copilot/tools/workflow-tools'
import { sleep, toError } from '@/lib/core/utils/helpers'
import { generateId } from '@/lib/core/utils/uuid'
import { getNextWorkflowColor } from '@/lib/workflows/colors'
import { getQueryClient } from '@/app/_shell/providers/get-query-client'
@@ -2512,7 +2513,7 @@ export function useChat(
} catch (error) {
logger.warn('Failed to load chat history while recovering stream', {
chatId,
error: error instanceof Error ? error.message : String(error),
error: toError(error).message,
})
return null
}
@@ -2746,7 +2747,7 @@ export function useChat(
if (isStaleReconnect()) return true
setTransportReconnecting()
await new Promise((resolve) => setTimeout(resolve, delayMs))
await sleep(delayMs)
if (streamGenRef.current !== gen) {
if (!sendingRef.current) {
setTransportIdle()
@@ -2816,7 +2817,7 @@ export function useChat(
logger.warn('Reconnect attempt failed', {
streamId,
attempt: attempt + 1,
error: err instanceof Error ? err.message : String(err),
error: toError(err).message,
})
}
}

View File

@@ -1,6 +1,7 @@
import { useCallback, useState } from 'react'
import { createLogger } from '@sim/logger'
import { useQueryClient } from '@tanstack/react-query'
import { sleep } from '@/lib/core/utils/helpers'
import { getFileExtension, getMimeTypeFromExtension } from '@/lib/uploads/utils/file-utils'
import { knowledgeKeys } from '@/hooks/queries/kb/knowledge'
@@ -111,11 +112,6 @@ const calculateUploadTimeoutMs = (fileSize: number) => {
return Math.min(dynamicBudget, UPLOAD_CONFIG.MAX_TIMEOUT_MS)
}
/**
* Delays execution for the specified duration
*/
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
/**
* Gets high resolution timestamp for performance measurements
*/

Some files were not shown because too many files have changed in this diff Show More