chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1 @@
|
||||
export * from './types'
|
||||
@@ -0,0 +1,159 @@
|
||||
import { getPlanTypeForLimits } from '@/lib/billing/plan-helpers'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import { isBillingEnabled } from '@/lib/core/config/env-flags'
|
||||
import type { SubscriptionPlan } from '@/lib/core/rate-limiter/types'
|
||||
|
||||
interface ExecutionTimeoutConfig {
|
||||
sync: number
|
||||
async: number
|
||||
}
|
||||
|
||||
const DEFAULT_SYNC_TIMEOUTS_SECONDS = {
|
||||
free: 300,
|
||||
pro: 3000,
|
||||
team: 3000,
|
||||
enterprise: 3000,
|
||||
} as const
|
||||
|
||||
const DEFAULT_ASYNC_TIMEOUTS_SECONDS = {
|
||||
free: 5400,
|
||||
pro: 5400,
|
||||
team: 5400,
|
||||
enterprise: 5400,
|
||||
} as const
|
||||
|
||||
function getSyncTimeoutForPlan(plan: SubscriptionPlan): number {
|
||||
const envVarMap: Record<SubscriptionPlan, string | undefined> = {
|
||||
free: env.EXECUTION_TIMEOUT_FREE,
|
||||
pro: env.EXECUTION_TIMEOUT_PRO,
|
||||
team: env.EXECUTION_TIMEOUT_TEAM,
|
||||
enterprise: env.EXECUTION_TIMEOUT_ENTERPRISE,
|
||||
}
|
||||
return (Number.parseInt(envVarMap[plan] || '') || DEFAULT_SYNC_TIMEOUTS_SECONDS[plan]) * 1000
|
||||
}
|
||||
|
||||
function getAsyncTimeoutForPlan(plan: SubscriptionPlan): number {
|
||||
const envVarMap: Record<SubscriptionPlan, string | undefined> = {
|
||||
free: env.EXECUTION_TIMEOUT_ASYNC_FREE,
|
||||
pro: env.EXECUTION_TIMEOUT_ASYNC_PRO,
|
||||
team: env.EXECUTION_TIMEOUT_ASYNC_TEAM,
|
||||
enterprise: env.EXECUTION_TIMEOUT_ASYNC_ENTERPRISE,
|
||||
}
|
||||
return (Number.parseInt(envVarMap[plan] || '') || DEFAULT_ASYNC_TIMEOUTS_SECONDS[plan]) * 1000
|
||||
}
|
||||
|
||||
const EXECUTION_TIMEOUTS: Record<SubscriptionPlan, ExecutionTimeoutConfig> = {
|
||||
free: {
|
||||
sync: getSyncTimeoutForPlan('free'),
|
||||
async: getAsyncTimeoutForPlan('free'),
|
||||
},
|
||||
pro: {
|
||||
sync: getSyncTimeoutForPlan('pro'),
|
||||
async: getAsyncTimeoutForPlan('pro'),
|
||||
},
|
||||
team: {
|
||||
sync: getSyncTimeoutForPlan('team'),
|
||||
async: getAsyncTimeoutForPlan('team'),
|
||||
},
|
||||
enterprise: {
|
||||
sync: getSyncTimeoutForPlan('enterprise'),
|
||||
async: getAsyncTimeoutForPlan('enterprise'),
|
||||
},
|
||||
}
|
||||
|
||||
export function getExecutionTimeout(
|
||||
plan: SubscriptionPlan | string | undefined,
|
||||
type: 'sync' | 'async' = 'sync'
|
||||
): number {
|
||||
if (!isBillingEnabled) {
|
||||
return EXECUTION_TIMEOUTS.free[type]
|
||||
}
|
||||
return EXECUTION_TIMEOUTS[getPlanTypeForLimits(plan)][type]
|
||||
}
|
||||
|
||||
export function getMaxExecutionTimeout(): number {
|
||||
return EXECUTION_TIMEOUTS.enterprise.async
|
||||
}
|
||||
|
||||
/** Safety buffer added beyond the max execution timeout for execution-lifetime TTLs. */
|
||||
export const RESERVATION_TTL_BUFFER_MS = 60_000
|
||||
|
||||
/**
|
||||
* TTL (ms) bounding how long a single execution can remain in flight: the max
|
||||
* execution timeout plus a safety buffer. Shared source of truth for the
|
||||
* admission-reservation key and the live progress-marker key so they expire on
|
||||
* the same timeline.
|
||||
*/
|
||||
export function getExecutionReservationTtlMs(): number {
|
||||
return getMaxExecutionTimeout() + RESERVATION_TTL_BUFFER_MS
|
||||
}
|
||||
|
||||
export const DEFAULT_EXECUTION_TIMEOUT_MS = EXECUTION_TIMEOUTS.free.sync
|
||||
|
||||
export function isTimeoutError(error: unknown): boolean {
|
||||
if (!error) return false
|
||||
|
||||
if (error instanceof Error) {
|
||||
return error.name === 'TimeoutError'
|
||||
}
|
||||
|
||||
if (typeof error === 'object' && 'name' in error) {
|
||||
return (error as { name: string }).name === 'TimeoutError'
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export function getTimeoutErrorMessage(error: unknown, timeoutMs?: number): string {
|
||||
if (timeoutMs) {
|
||||
const timeoutSeconds = Math.floor(timeoutMs / 1000)
|
||||
const timeoutMinutes = Math.floor(timeoutSeconds / 60)
|
||||
const displayTime =
|
||||
timeoutMinutes > 0
|
||||
? `${timeoutMinutes} minute${timeoutMinutes > 1 ? 's' : ''}`
|
||||
: `${timeoutSeconds} seconds`
|
||||
return `Execution timed out after ${displayTime}`
|
||||
}
|
||||
|
||||
return 'Execution timed out'
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to create an AbortController with timeout handling.
|
||||
* Centralizes the timeout abort pattern used across execution paths.
|
||||
*/
|
||||
export interface TimeoutAbortController {
|
||||
/** The AbortSignal to pass to execution functions */
|
||||
signal: AbortSignal
|
||||
/** Returns true if the abort was triggered by timeout (not user cancellation) */
|
||||
isTimedOut: () => boolean
|
||||
/** Cleanup function - call in finally block to clear the timeout */
|
||||
cleanup: () => void
|
||||
/** Manually abort the execution (for user cancellation) */
|
||||
abort: () => void
|
||||
/** The timeout duration in milliseconds (undefined if no timeout) */
|
||||
timeoutMs: number | undefined
|
||||
}
|
||||
|
||||
export function createTimeoutAbortController(timeoutMs?: number): TimeoutAbortController {
|
||||
const abortController = new AbortController()
|
||||
let isTimedOut = false
|
||||
let timeoutId: NodeJS.Timeout | undefined
|
||||
|
||||
if (timeoutMs) {
|
||||
timeoutId = setTimeout(() => {
|
||||
isTimedOut = true
|
||||
abortController.abort()
|
||||
}, timeoutMs)
|
||||
}
|
||||
|
||||
return {
|
||||
signal: abortController.signal,
|
||||
isTimedOut: () => isTimedOut,
|
||||
cleanup: () => {
|
||||
if (timeoutId) clearTimeout(timeoutId)
|
||||
},
|
||||
abort: () => abortController.abort(),
|
||||
timeoutMs,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user