fix(deploy-to-hub): guard stale load response and mid-publish status writes

- install load() assigned `data`/`folderName` unconditionally, so a slow
  /export for an old ?hub= could overwrite a newer project after navigation.
  Add a load token + captured slug/workspace and only assign if still current.
- deployAll wrote deploymentStatus/hubItemIds incrementally and only checked
  the workspace at the very end. Bail at the top of the per-item loop when the
  active workspace changed, so a mid-publish switch can't keep writing the old
  workspace's item statuses and Hub IDs into the new workspace's live view.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
tristantr
2026-06-29 15:44:58 +02:00
co-authored by Claude Opus 4.8
parent 13314e89f0
commit 254ec94e4c
2 changed files with 17 additions and 4 deletions
@@ -1180,6 +1180,9 @@
}
for (const it of bundle.items) {
// Stop writing item status / Hub IDs into a workspace the user has
// switched away from mid-publish.
if ($workspaceStore !== workspace) return
const key = `${it.kind}:${it.path}`
deploymentStatus = { ...deploymentStatus, [key]: { status: 'loading' } }
try {
@@ -52,25 +52,35 @@
let done = $state(false)
let folderName = $state('')
let loadSeq = 0
$effect(() => {
if (slug && workspace) void load()
})
async function load() {
// Token + captured slug/workspace: a slow /export for an old ?hub= must not
// overwrite the data of a newer one once we've navigated away.
const reqSeq = ++loadSeq
const reqSlug = slug
const reqWorkspace = workspace
loading = true
loadError = undefined
try {
const res = await fetch(
`/api/w/${workspace}/hub/projects/${encodeURIComponent(slug)}/export`,
`/api/w/${reqWorkspace}/hub/projects/${encodeURIComponent(reqSlug)}/export`,
{ credentials: 'include', headers: { accept: 'application/json' } }
)
if (!res.ok) throw new Error(`export ${res.status}: ${await res.text()}`)
data = JSON.parse(await res.text())
const text = await res.text()
if (reqSeq !== loadSeq) return // a newer load() superseded this one
if (!res.ok) throw new Error(`export ${res.status}: ${text}`)
data = JSON.parse(text)
if (data && !folderName) folderName = data.project.slug
} catch (e: any) {
if (reqSeq !== loadSeq) return
loadError = e?.message ?? String(e)
} finally {
loading = false
if (reqSeq === loadSeq) loading = false
}
}