Files
simstudioai--sim/apps/sim/lib/copilot/tools/handlers/deployment/state-refs.ts
T
wehub-resource-sync d25d482dc2
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
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
chore: import upstream snapshot with attribution
2026-07-13 13:20:55 +08:00

96 lines
3.1 KiB
TypeScript

import { db } from '@sim/db'
import { workflowDeploymentVersion } from '@sim/db/schema'
import { and, eq } from 'drizzle-orm'
import { loadWorkflowDeploymentSnapshot } from '@/lib/workflows/persistence/utils'
import type { WorkflowState } from '@/stores/workflows/workflow/types'
import { ensureWorkflowAccess } from '../access'
/** Canonical workflow-state selector: a deployment version number, the live
* (active) deployment, or the current draft. */
export type WorkflowRef = number | 'live' | 'draft'
export interface ResolvedWorkflowRef {
state: WorkflowState
/** Human-readable ref label: "live", "draft", or the version number as a string. */
ref: string
version?: number
isActive?: boolean
createdAt?: string
}
/**
* Parse a raw ref param into a canonical WorkflowRef.
* Accepts a version number, a numeric string, "live"/"active", or "draft"/"current".
* Throws on anything else.
*/
export function parseWorkflowRef(raw: unknown): WorkflowRef {
if (typeof raw === 'number' && Number.isFinite(raw)) return raw
if (typeof raw === 'string') {
const trimmed = raw.trim().toLowerCase()
if (trimmed === 'live' || trimmed === 'active') return 'live'
if (trimmed === 'draft' || trimmed === 'current') return 'draft'
if (/^\d+$/.test(trimmed)) return Number.parseInt(trimmed, 10)
}
throw new Error(`Invalid ref "${String(raw)}": expected a version number, "live", or "draft"`)
}
/**
* Resolve a (workflowId, ref) pair to a WorkflowState for diffing. Raw stored
* snapshots are used for version/live (matching checkNeedsRedeployment's baseline),
* and loadWorkflowDeploymentSnapshot is used for draft. Requires read access.
*/
export async function resolveWorkflowStateRef(
workflowId: string,
rawRef: unknown,
userId: string
): Promise<ResolvedWorkflowRef> {
const ref = parseWorkflowRef(rawRef)
await ensureWorkflowAccess(workflowId, userId, 'read')
if (ref === 'draft') {
const state = await loadWorkflowDeploymentSnapshot(workflowId)
if (!state) {
throw new Error(`Workflow ${workflowId} has no draft state`)
}
return { state, ref: 'draft' }
}
const whereClause =
ref === 'live'
? and(
eq(workflowDeploymentVersion.workflowId, workflowId),
eq(workflowDeploymentVersion.isActive, true)
)
: and(
eq(workflowDeploymentVersion.workflowId, workflowId),
eq(workflowDeploymentVersion.version, ref)
)
const [row] = await db
.select({
version: workflowDeploymentVersion.version,
state: workflowDeploymentVersion.state,
isActive: workflowDeploymentVersion.isActive,
createdAt: workflowDeploymentVersion.createdAt,
})
.from(workflowDeploymentVersion)
.where(whereClause)
.limit(1)
if (!row?.state) {
throw new Error(
ref === 'live'
? `Workflow ${workflowId} has no active deployment`
: `Deployment version ${ref} not found for workflow ${workflowId}`
)
}
return {
state: row.state as WorkflowState,
ref: ref === 'live' ? 'live' : String(ref),
version: row.version,
isActive: row.isActive,
createdAt: row.createdAt?.toISOString(),
}
}