export const PLAY_RUNTIME_BACKENDS = { localProcess: 'local_process', daytona: 'daytona', modal: 'modal', } as const; export type PlayRuntimeBackendId = (typeof PLAY_RUNTIME_BACKENDS)[keyof typeof PLAY_RUNTIME_BACKENDS]; /** * The one executable Play artifact contract. Daytona and local-process both * load CommonJS under Node 20. */ export const PLAY_ARTIFACT_KINDS = { cjsNode20: 'cjs_node20', } as const; export type PlayArtifactKind = (typeof PLAY_ARTIFACT_KINDS)[keyof typeof PLAY_ARTIFACT_KINDS]; export type PlayBackendDescriptor = { id: PlayRuntimeBackendId; /** What artifact kind this backend's runner consumes. */ artifactKind: PlayArtifactKind; /** Human label for logs. */ label: string; }; export const PLAY_BACKEND_DESCRIPTORS: Record< PlayRuntimeBackendId, PlayBackendDescriptor > = { [PLAY_RUNTIME_BACKENDS.localProcess]: { id: PLAY_RUNTIME_BACKENDS.localProcess, artifactKind: PLAY_ARTIFACT_KINDS.cjsNode20, label: 'Local node subprocess', }, [PLAY_RUNTIME_BACKENDS.daytona]: { id: PLAY_RUNTIME_BACKENDS.daytona, artifactKind: PLAY_ARTIFACT_KINDS.cjsNode20, label: 'Daytona sandbox', }, [PLAY_RUNTIME_BACKENDS.modal]: { id: PLAY_RUNTIME_BACKENDS.modal, artifactKind: PLAY_ARTIFACT_KINDS.cjsNode20, label: 'Modal sandbox', }, }; export function describePlayBackend( id: PlayRuntimeBackendId, ): PlayBackendDescriptor { return PLAY_BACKEND_DESCRIPTORS[id]; } export function artifactKindForBackend( id: PlayRuntimeBackendId, ): PlayArtifactKind { return PLAY_BACKEND_DESCRIPTORS[id].artifactKind; } export function normalizePlayRuntimeBackend( value?: string | null, ): PlayRuntimeBackendId { const normalized = value?.trim().toLowerCase(); if (!normalized) { return defaultPlayRuntimeBackend(); } if (normalized === PLAY_RUNTIME_BACKENDS.daytona) { return PLAY_RUNTIME_BACKENDS.daytona; } if (normalized === PLAY_RUNTIME_BACKENDS.modal) { return PLAY_RUNTIME_BACKENDS.modal; } if (normalized === PLAY_RUNTIME_BACKENDS.localProcess) { return PLAY_RUNTIME_BACKENDS.localProcess; } throw new Error( `Unsupported play runtime backend "${normalized}". Expected one of: ${Object.values( PLAY_RUNTIME_BACKENDS, ).join(', ')}.`, ); } export function defaultPlayRuntimeBackend(input?: { deeplineEnv?: string | null; vercelEnv?: string | null; nodeEnv?: string | null; previewPlaysDeployment?: boolean; }): PlayRuntimeBackendId { if (input?.previewPlaysDeployment) { return PLAY_RUNTIME_BACKENDS.daytona; } const candidates = [ input?.deeplineEnv ?? process.env.DEEPLINE_ENV, input?.vercelEnv ?? process.env.VERCEL_ENV, input?.nodeEnv ?? process.env.NODE_ENV, ] .filter(Boolean) .map((value) => String(value).trim().toLowerCase()); for (const value of candidates) { if ( value === 'production' || value === 'prod' || value === 'live' || value === 'preview' || value === 'staging' || value === 'stage' ) { return PLAY_RUNTIME_BACKENDS.daytona; } } return PLAY_RUNTIME_BACKENDS.localProcess; } export function isPlayRuntimeBackendId( value: unknown, ): value is PlayRuntimeBackendId { return ( typeof value === 'string' && Object.values(PLAY_RUNTIME_BACKENDS).includes(value as PlayRuntimeBackendId) ); } export function isManagedSandboxRuntimeBackend( value: string | null | undefined, ): boolean { return ( value === PLAY_RUNTIME_BACKENDS.daytona || value === PLAY_RUNTIME_BACKENDS.modal ); }