refactor: rename internal promptId/PromptId to jobId/JobId (#8730)

## Summary

Rename all internal TypeScript usage of legacy `promptId`/`PromptId`
naming to `jobId`/`JobId` across ~38 files for consistency with the
domain model.

## Changes

- **What**: Renamed internal variable names, type aliases, function
names, class getters, interface fields, and comments from
`promptId`/`PromptId` to `jobId`/`JobId`. Wire-protocol field names
(`prompt_id` in Zod schemas and `e.detail.prompt_id` accesses) are
intentionally preserved since they match the backend API contract.

## Review Focus

- All changes are pure renames with no behavioral changes
- Wire-protocol fields (`prompt_id`) are deliberately unchanged to
maintain backend compatibility
- Test fixtures updated to use consistent `job-id` naming

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8730-refactor-rename-internal-promptId-PromptId-to-jobId-JobId-3016d73d3650813ca40ce337f7c5271a)
by [Unito](https://www.unito.io)
This commit is contained in:
Christian Byrne
2026-02-20 02:10:53 -08:00
committed by GitHub
parent 541ad387b9
commit 473713cf02
39 changed files with 455 additions and 402 deletions

View File

@@ -27,7 +27,7 @@ export function mapTaskOutputToAssetItem(
output: ResultItemImpl
): AssetItem {
const metadata: OutputAssetMetadata = {
promptId: taskItem.promptId,
jobId: taskItem.jobId,
nodeId: output.nodeId,
subfolder: output.subfolder,
executionTimeInSeconds: taskItem.executionTimeInSeconds,
@@ -36,7 +36,7 @@ export function mapTaskOutputToAssetItem(
}
return {
id: taskItem.promptId,
id: taskItem.jobId,
name: output.filename,
size: 0,
created_at: taskItem.executionStartTimestamp

View File

@@ -46,12 +46,12 @@ export function useMediaAssetActions() {
assetType: string
): Promise<void> => {
if (assetType === 'output') {
const promptId =
getOutputAssetMetadata(asset.user_metadata)?.promptId || asset.id
if (!promptId) {
throw new Error('Unable to extract prompt ID from asset')
const jobId =
getOutputAssetMetadata(asset.user_metadata)?.jobId || asset.id
if (!jobId) {
throw new Error('Unable to extract job ID from asset')
}
await api.deleteItem('history', promptId)
await api.deleteItem('history', jobId)
} else {
// Input assets can only be deleted in cloud environment
if (!isCloud) {
@@ -141,16 +141,16 @@ export function useMediaAssetActions() {
for (const asset of assets) {
if (getAssetType(asset) === 'output') {
const metadata = getOutputAssetMetadata(asset.user_metadata)
const promptId = metadata?.promptId || asset.id
if (!jobIds.includes(promptId)) {
jobIds.push(promptId)
const jobId = metadata?.jobId || asset.id
if (!jobIds.includes(jobId)) {
jobIds.push(jobId)
}
if (metadata?.promptId && asset.name) {
if (!jobAssetNameFilters[metadata.promptId]) {
jobAssetNameFilters[metadata.promptId] = []
if (metadata?.jobId && asset.name) {
if (!jobAssetNameFilters[metadata.jobId]) {
jobAssetNameFilters[metadata.jobId] = []
}
if (!jobAssetNameFilters[metadata.promptId].includes(asset.name)) {
jobAssetNameFilters[metadata.promptId].push(asset.name)
if (!jobAssetNameFilters[metadata.jobId].includes(asset.name)) {
jobAssetNameFilters[metadata.jobId].push(asset.name)
}
}
} else {
@@ -191,11 +191,11 @@ export function useMediaAssetActions() {
if (!targetAsset) return
const metadata = getOutputAssetMetadata(targetAsset.user_metadata)
const promptId =
metadata?.promptId ||
const jobId =
metadata?.jobId ||
(getAssetType(targetAsset) === 'output' ? targetAsset.id : undefined)
if (!promptId) {
if (!jobId) {
toast.add({
severity: 'warn',
summary: t('g.warning'),
@@ -205,7 +205,7 @@ export function useMediaAssetActions() {
return
}
await copyToClipboard(promptId)
await copyToClipboard(jobId)
}
/**

View File

@@ -40,7 +40,7 @@ function createAsset(overrides: Partial<AssetItem> = {}): AssetItem {
tags: [],
created_at: '2025-01-01T00:00:00.000Z',
user_metadata: {
promptId: 'prompt-1',
jobId: 'job-1',
nodeId: 'node-1',
subfolder: 'outputs'
},
@@ -74,7 +74,7 @@ describe('useOutputStacks', () => {
await toggleStack(parent)
expect(mocks.resolveOutputAssetItems).toHaveBeenCalledWith(
expect.objectContaining({ promptId: 'prompt-1' }),
expect.objectContaining({ jobId: 'job-1' }),
{
createdAt: parent.created_at,
excludeOutputKey: 'node-1-outputs-parent.png'

View File

@@ -19,25 +19,25 @@ type UseOutputStacksOptions = {
}
export function useOutputStacks({ assets }: UseOutputStacksOptions) {
const expandedStackPromptIds = ref<Set<string>>(new Set())
const stackChildrenByPromptId = ref<Record<string, AssetItem[]>>({})
const loadingStackPromptIds = ref<Set<string>>(new Set())
const expandedStackJobIds = ref<Set<string>>(new Set())
const stackChildrenByJobId = ref<Record<string, AssetItem[]>>({})
const loadingStackJobIds = ref<Set<string>>(new Set())
const assetItems = computed<OutputStackListItem[]>(() => {
const items: OutputStackListItem[] = []
for (const asset of assets.value) {
const promptId = getStackPromptId(asset)
const jobId = getStackJobId(asset)
items.push({
key: `asset-${asset.id}`,
asset
})
if (!promptId || !expandedStackPromptIds.value.has(promptId)) {
if (!jobId || !expandedStackJobIds.value.has(jobId)) {
continue
}
const children = stackChildrenByPromptId.value[promptId] ?? []
const children = stackChildrenByJobId.value[jobId] ?? []
for (const child of children) {
items.push({
key: `asset-${child.id}`,
@@ -54,55 +54,55 @@ export function useOutputStacks({ assets }: UseOutputStacksOptions) {
assetItems.value.map((item) => item.asset)
)
function getStackPromptId(asset: AssetItem): string | null {
function getStackJobId(asset: AssetItem): string | null {
const metadata = getOutputAssetMetadata(asset.user_metadata)
return metadata?.promptId ?? null
return metadata?.jobId ?? null
}
function isStackExpanded(asset: AssetItem): boolean {
const promptId = getStackPromptId(asset)
if (!promptId) return false
return expandedStackPromptIds.value.has(promptId)
const jobId = getStackJobId(asset)
if (!jobId) return false
return expandedStackJobIds.value.has(jobId)
}
async function toggleStack(asset: AssetItem) {
const promptId = getStackPromptId(asset)
if (!promptId) return
const jobId = getStackJobId(asset)
if (!jobId) return
if (expandedStackPromptIds.value.has(promptId)) {
const next = new Set(expandedStackPromptIds.value)
next.delete(promptId)
expandedStackPromptIds.value = next
if (expandedStackJobIds.value.has(jobId)) {
const next = new Set(expandedStackJobIds.value)
next.delete(jobId)
expandedStackJobIds.value = next
return
}
if (!stackChildrenByPromptId.value[promptId]?.length) {
if (loadingStackPromptIds.value.has(promptId)) {
if (!stackChildrenByJobId.value[jobId]?.length) {
if (loadingStackJobIds.value.has(jobId)) {
return
}
const nextLoading = new Set(loadingStackPromptIds.value)
nextLoading.add(promptId)
loadingStackPromptIds.value = nextLoading
const nextLoading = new Set(loadingStackJobIds.value)
nextLoading.add(jobId)
loadingStackJobIds.value = nextLoading
const children = await resolveStackChildren(asset)
const afterLoading = new Set(loadingStackPromptIds.value)
afterLoading.delete(promptId)
loadingStackPromptIds.value = afterLoading
const afterLoading = new Set(loadingStackJobIds.value)
afterLoading.delete(jobId)
loadingStackJobIds.value = afterLoading
if (!children.length) {
return
}
stackChildrenByPromptId.value = {
...stackChildrenByPromptId.value,
[promptId]: children
stackChildrenByJobId.value = {
...stackChildrenByJobId.value,
[jobId]: children
}
}
const nextExpanded = new Set(expandedStackPromptIds.value)
nextExpanded.add(promptId)
expandedStackPromptIds.value = nextExpanded
const nextExpanded = new Set(expandedStackJobIds.value)
nextExpanded.add(jobId)
expandedStackJobIds.value = nextExpanded
}
async function resolveStackChildren(asset: AssetItem): Promise<AssetItem[]> {

View File

@@ -6,7 +6,7 @@ import type { ResultItemImpl } from '@/stores/queueStore'
* Extends Record<string, unknown> for compatibility with AssetItem schema
*/
export interface OutputAssetMetadata extends Record<string, unknown> {
promptId: string
jobId: string
nodeId: string | number
subfolder: string
executionTimeInSeconds?: number
@@ -24,7 +24,7 @@ function isOutputAssetMetadata(
): metadata is OutputAssetMetadata {
if (!metadata) return false
return (
typeof metadata.promptId === 'string' &&
typeof metadata.jobId === 'string' &&
(typeof metadata.nodeId === 'string' || typeof metadata.nodeId === 'number')
)
}

View File

@@ -49,7 +49,7 @@ describe('resolveOutputAssetItems', () => {
url: 'https://example.com/b.png'
})
const metadata: OutputAssetMetadata = {
promptId: 'prompt-1',
jobId: 'job-1',
nodeId: '1',
subfolder: 'sub',
executionTimeInSeconds: 12.5,
@@ -66,7 +66,7 @@ describe('resolveOutputAssetItems', () => {
expect(results).toHaveLength(1)
expect(results[0]).toEqual(
expect.objectContaining({
id: 'prompt-1-1-sub-a.png',
id: 'job-1-1-sub-a.png',
name: 'a.png',
created_at: '2025-01-01T00:00:00.000Z',
tags: ['output'],
@@ -75,7 +75,7 @@ describe('resolveOutputAssetItems', () => {
)
expect(results[0].user_metadata).toEqual(
expect.objectContaining({
promptId: 'prompt-1',
jobId: 'job-1',
nodeId: '1',
subfolder: 'sub',
executionTimeInSeconds: 12.5
@@ -95,7 +95,7 @@ describe('resolveOutputAssetItems', () => {
url: 'https://example.com/full.png'
})
const metadata: OutputAssetMetadata = {
promptId: 'prompt-2',
jobId: 'job-2',
nodeId: '1',
subfolder: 'sub',
outputCount: 3,
@@ -111,7 +111,7 @@ describe('resolveOutputAssetItems', () => {
const results = await resolveOutputAssetItems(metadata)
expect(mocks.getJobDetail).toHaveBeenCalledWith('prompt-2')
expect(mocks.getJobDetail).toHaveBeenCalledWith('job-2')
expect(mocks.getPreviewableOutputsFromJobDetail).toHaveBeenCalledWith(
jobDetail
)
@@ -129,7 +129,7 @@ describe('resolveOutputAssetItems', () => {
url: 'https://example.com/root.png'
})
const metadata: OutputAssetMetadata = {
promptId: 'prompt-root',
jobId: 'job-root',
nodeId: '1',
subfolder: '',
outputCount: 1,
@@ -144,7 +144,7 @@ describe('resolveOutputAssetItems', () => {
if (!asset) {
throw new Error('Expected a root output asset')
}
expect(asset.id).toBe('prompt-root-1--root.png')
expect(asset.id).toBe('job-root-1--root.png')
if (!asset.user_metadata) {
throw new Error('Expected output metadata')
}

View File

@@ -8,7 +8,7 @@ import {
import type { ResultItemImpl } from '@/stores/queueStore'
type OutputAssetMapOptions = {
promptId: string
jobId: string
outputs: readonly ResultItemImpl[]
createdAt?: string
executionTimeInSeconds?: number
@@ -51,7 +51,7 @@ export function getOutputKey({
}
function mapOutputsToAssetItems({
promptId,
jobId,
outputs,
createdAt,
executionTimeInSeconds,
@@ -67,14 +67,14 @@ function mapOutputsToAssetItems({
}
items.push({
id: `${promptId}-${outputKey}`,
id: `${jobId}-${outputKey}`,
name: output.filename,
size: 0,
created_at: createdAtValue,
tags: ['output'],
preview_url: output.url,
user_metadata: {
promptId,
jobId,
nodeId: output.nodeId,
subfolder: output.subfolder,
executionTimeInSeconds,
@@ -92,7 +92,7 @@ export async function resolveOutputAssetItems(
): Promise<AssetItem[]> {
let outputsToDisplay = metadata.allOutputs ?? []
if (shouldLoadFullOutputs(metadata.outputCount, outputsToDisplay.length)) {
const jobDetail = await getJobDetail(metadata.promptId)
const jobDetail = await getJobDetail(metadata.jobId)
const previewableOutputs = getPreviewableOutputsFromJobDetail(jobDetail)
if (previewableOutputs.length) {
outputsToDisplay = previewableOutputs
@@ -100,7 +100,7 @@ export async function resolveOutputAssetItems(
}
return mapOutputsToAssetItems({
promptId: metadata.promptId,
jobId: metadata.jobId,
outputs: outputsToDisplay,
createdAt,
executionTimeInSeconds: metadata.executionTimeInSeconds,

View File

@@ -8,7 +8,7 @@
import type { ComfyWorkflowJSON } from '@/platform/workflow/validation/schemas/workflowSchema'
import { validateComfyWorkflow } from '@/platform/workflow/validation/schemas/workflowSchema'
import type { PromptId } from '@/schemas/apiSchema'
import type { JobId } from '@/schemas/apiSchema'
import type {
JobDetail,
@@ -119,19 +119,19 @@ export async function fetchQueue(
*/
export async function fetchJobDetail(
fetchApi: (url: string) => Promise<Response>,
promptId: PromptId
jobId: JobId
): Promise<JobDetail | undefined> {
try {
const res = await fetchApi(`/jobs/${encodeURIComponent(promptId)}`)
const res = await fetchApi(`/jobs/${encodeURIComponent(jobId)}`)
if (!res.ok) {
console.warn(`Job not found for prompt ${promptId}`)
console.warn(`Job not found for job ${jobId}`)
return undefined
}
return zJobDetail.parse(await res.json())
} catch (error) {
console.error(`Failed to fetch job detail for prompt ${promptId}:`, error)
console.error(`Failed to fetch job detail for job ${jobId}:`, error)
return undefined
}
}

View File

@@ -21,7 +21,7 @@ const mockWorkflow: ComfyWorkflowJSON = {
// Jobs API detail response structure (matches actual /jobs/{id} response)
// workflow is nested at: workflow.extra_data.extra_pnginfo.workflow
const mockJobDetailResponse: JobDetail = {
id: 'test-prompt-id',
id: 'test-job-id',
status: 'completed',
create_time: 1234567890,
update_time: 1234567900,
@@ -43,15 +43,15 @@ const mockJobDetailResponse: JobDetail = {
}
describe('fetchJobDetail', () => {
it('should fetch job detail from /jobs/{prompt_id} endpoint', async () => {
it('should fetch job detail from /jobs/{job_id} endpoint', async () => {
const mockFetchApi = vi.fn().mockResolvedValue({
ok: true,
json: async () => mockJobDetailResponse
})
await fetchJobDetail(mockFetchApi, 'test-prompt-id')
await fetchJobDetail(mockFetchApi, 'test-job-id')
expect(mockFetchApi).toHaveBeenCalledWith('/jobs/test-prompt-id')
expect(mockFetchApi).toHaveBeenCalledWith('/jobs/test-job-id')
})
it('should return job detail with workflow and outputs', async () => {
@@ -60,10 +60,10 @@ describe('fetchJobDetail', () => {
json: async () => mockJobDetailResponse
})
const result = await fetchJobDetail(mockFetchApi, 'test-prompt-id')
const result = await fetchJobDetail(mockFetchApi, 'test-job-id')
expect(result).toBeDefined()
expect(result?.id).toBe('test-prompt-id')
expect(result?.id).toBe('test-job-id')
expect(result?.outputs).toEqual(mockJobDetailResponse.outputs)
expect(result?.workflow).toBeDefined()
})
@@ -82,7 +82,7 @@ describe('fetchJobDetail', () => {
it('should handle fetch errors gracefully', async () => {
const mockFetchApi = vi.fn().mockRejectedValue(new Error('Network error'))
const result = await fetchJobDetail(mockFetchApi, 'test-prompt-id')
const result = await fetchJobDetail(mockFetchApi, 'test-job-id')
expect(result).toBeUndefined()
})
@@ -95,7 +95,7 @@ describe('fetchJobDetail', () => {
}
})
const result = await fetchJobDetail(mockFetchApi, 'test-prompt-id')
const result = await fetchJobDetail(mockFetchApi, 'test-job-id')
expect(result).toBeUndefined()
})

View File

@@ -19,7 +19,7 @@ import { getJobWorkflow } from '@/services/jobOutputCache'
* @returns WorkflowSource with workflow and generated filename
*
* @example
* const asset = { name: 'output.png', user_metadata: { promptId: '123' } }
* const asset = { name: 'output.png', user_metadata: { jobId: '123' } }
* const { workflow, filename } = await extractWorkflowFromAsset(asset)
*/
export async function extractWorkflowFromAsset(asset: AssetItem): Promise<{
@@ -30,8 +30,8 @@ export async function extractWorkflowFromAsset(asset: AssetItem): Promise<{
// For output assets: use jobs API (with caching and validation)
const metadata = getOutputAssetMetadata(asset.user_metadata)
if (metadata?.promptId) {
const workflow = await getJobWorkflow(metadata.promptId)
if (metadata?.jobId) {
const workflow = await getJobWorkflow(metadata.jobId)
return { workflow: workflow ?? null, filename: baseFilename }
}