@morscherlab/mint-sdk 1.4.6 → 1.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AppSidebar-BI5r4vg1.js → AppSidebar-B3JVHxwN.js} +3 -3
- package/dist/{AppSidebar-BI5r4vg1.js.map → AppSidebar-B3JVHxwN.js.map} +1 -1
- package/dist/{SettingsModal-dC0TCvPr.js → SettingsModal-COQ2krOp.js} +4 -4
- package/dist/{SettingsModal-dC0TCvPr.js.map → SettingsModal-COQ2krOp.js.map} +1 -1
- package/dist/{controlSchemaModel-BbXbMgc6.js → controlSchemaModel-C20f-1_T.js} +2 -2
- package/dist/{controlSchemaModel-BbXbMgc6.js.map → controlSchemaModel-C20f-1_T.js.map} +1 -1
- package/dist/index.js +9 -9
- package/dist/{install-Cbtn69ts.js → install-B2XlrT-x.js} +8 -8
- package/dist/{install-Cbtn69ts.js.map → install-B2XlrT-x.js.map} +1 -1
- package/dist/install.js +1 -1
- package/dist/{lab-C3BmZ2lI.js → lab-B5sKmlWF.js} +4 -4
- package/dist/{lab-C3BmZ2lI.js.map → lab-B5sKmlWF.js.map} +1 -1
- package/dist/lab.js +3 -3
- package/dist/{runtime-D77Lk7Pk.js → runtime-CCfoAQcC.js} +5 -5
- package/dist/{runtime-D77Lk7Pk.js.map → runtime-CCfoAQcC.js.map} +1 -1
- package/dist/runtime.js +4 -4
- package/dist/{shell-Bqb2_v3D.js → shell-Chc-muxT.js} +3 -3
- package/dist/{shell-Bqb2_v3D.js.map → shell-Chc-muxT.js.map} +1 -1
- package/dist/shell.js +3 -3
- package/dist/{useListReorder-QqJc1dpX.js → useListReorder-C--K06el.js} +14 -2
- package/dist/useListReorder-C--K06el.js.map +1 -0
- package/dist/{useRackEditor-BHGM1Ngz.js → useRackEditor-Cba_0OfI.js} +2 -2
- package/dist/{useRackEditor-BHGM1Ngz.js.map → useRackEditor-Cba_0OfI.js.map} +1 -1
- package/package.json +1 -1
- package/src/__tests__/composables/useJobsEndpoint.test.ts +50 -0
- package/src/composables/useJobsEndpoint.ts +15 -4
- package/dist/useListReorder-QqJc1dpX.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morscherlab/mint-sdk",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.7",
|
|
4
4
|
"description": "MINT Platform SDK — Vue 3 components, composables, and types for plugin development. MINT = Mass-spec Infrastructure for Next-generation Toolkits.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -1595,6 +1595,56 @@ describe('useJobsEndpoint', () => {
|
|
|
1595
1595
|
}
|
|
1596
1596
|
})
|
|
1597
1597
|
|
|
1598
|
+
it.each([
|
|
1599
|
+
{ name: 'resolves when the server still has the job', answers: [() => response(completedJob('submitted-job'))], outcome: 'completed' },
|
|
1600
|
+
{ name: 'rejects when the server no longer has the job', answers: [() => response({ detail: 'Job not found' }, 404)], outcome: 'Job submitted-job is no longer available' },
|
|
1601
|
+
{ name: 'checks again on the next snapshot after a retryable error', answers: [() => response({ detail: 'Busy' }, 503), () => response(completedJob('submitted-job'))], outcome: 'completed' },
|
|
1602
|
+
])('should check a waited job that a stale stream snapshot omits and $name', async ({ answers, outcome }) => {
|
|
1603
|
+
const encoder = new TextEncoder()
|
|
1604
|
+
let pushFrame!: (frame: string) => void
|
|
1605
|
+
const stream = new ReadableStream<Uint8Array>({
|
|
1606
|
+
start(controller) { pushFrame = frame => controller.enqueue(encoder.encode(frame)) },
|
|
1607
|
+
})
|
|
1608
|
+
let lookups = 0
|
|
1609
|
+
const transport = vi.fn<GeneratedAnalysisTransport>(async (input, init) => {
|
|
1610
|
+
const url = String(input)
|
|
1611
|
+
if (url.endsWith('/jobs/sessions') && init?.method === 'GET') return response({ sessions: [] })
|
|
1612
|
+
if (url.endsWith('/jobs/sessions')) {
|
|
1613
|
+
return response({ id: 'session-1', tab_id: 'tab-1', created_at: '', last_seen_at: '', active_job_ids: [] })
|
|
1614
|
+
}
|
|
1615
|
+
if (url.endsWith('/jobs/events')) return new Response(stream, { headers: { 'Content-Type': 'text/event-stream' } })
|
|
1616
|
+
if (url.endsWith('/jobs') && init?.method === 'POST') return response(activeJob('submitted-job'))
|
|
1617
|
+
if (url.endsWith('/jobs')) return response({ jobs: [] })
|
|
1618
|
+
if (url.endsWith('/jobs/submitted-job')) {
|
|
1619
|
+
lookups += 1
|
|
1620
|
+
return lookups === 1 ? response(activeJob('submitted-job', 'running')) : answers[lookups - 2]!()
|
|
1621
|
+
}
|
|
1622
|
+
throw new Error(`Unexpected request: ${url}`)
|
|
1623
|
+
})
|
|
1624
|
+
const scope = effectScope()
|
|
1625
|
+
const generated = scope.run(() => useJobsEndpoint({
|
|
1626
|
+
baseUrl: '/api/demo',
|
|
1627
|
+
transport,
|
|
1628
|
+
storage: memoryStorage(),
|
|
1629
|
+
createId: () => 'tab-1',
|
|
1630
|
+
streamReconnectDelay: 60_000,
|
|
1631
|
+
}))!
|
|
1632
|
+
|
|
1633
|
+
try {
|
|
1634
|
+
const waiting = generated.wait(await generated.submit('run', {}))
|
|
1635
|
+
.then(job => job.status, (cause: Error) => cause.message)
|
|
1636
|
+
// Each snapshot was built before the submit reached the server.
|
|
1637
|
+
for (const [index] of answers.entries()) {
|
|
1638
|
+
await vi.waitFor(() => expect(lookups).toBe(index + 1))
|
|
1639
|
+
pushFrame(`event: snapshot\ndata: ${JSON.stringify({ jobs: [] })}\n\n`)
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
expect(await waiting).toBe(outcome)
|
|
1643
|
+
} finally {
|
|
1644
|
+
scope.stop()
|
|
1645
|
+
}
|
|
1646
|
+
})
|
|
1647
|
+
|
|
1598
1648
|
it('rejects pending waiters when their job is deleted or the runtime is disposed', async () => {
|
|
1599
1649
|
const queued = activeJob('delete-wait-job')
|
|
1600
1650
|
const disposeJobRequest = deferred<Response>()
|
|
@@ -677,16 +677,27 @@ export function useJobsEndpoint(
|
|
|
677
677
|
]))
|
|
678
678
|
jobs.value = nextJobs
|
|
679
679
|
for (const job of nextJobs) settleJobWaiters(job)
|
|
680
|
-
for (const jobId of
|
|
681
|
-
if (!nextJobIds.has(jobId))
|
|
682
|
-
rejectJobWaiters(jobId, new Error(`Job ${jobId} is no longer available`))
|
|
683
|
-
}
|
|
680
|
+
for (const jobId of [...jobWaiters.keys()]) {
|
|
681
|
+
if (!nextJobIds.has(jobId)) void confirmMissingJob(jobId)
|
|
684
682
|
}
|
|
685
683
|
pruneResultState()
|
|
686
684
|
pruneJobRevisions()
|
|
687
685
|
return nextJobs
|
|
688
686
|
}
|
|
689
687
|
|
|
688
|
+
/**
|
|
689
|
+
* A stream snapshot built before a submit can arrive after the submit reply,
|
|
690
|
+
* so a waited job missing from it is gone only once the server says so.
|
|
691
|
+
*/
|
|
692
|
+
async function confirmMissingJob(jobId: string): Promise<void> {
|
|
693
|
+
try {
|
|
694
|
+
await getJob(jobId)
|
|
695
|
+
} catch (cause) {
|
|
696
|
+
if (isRetryableJobLookupError(cause)) return
|
|
697
|
+
rejectJobWaiters(jobId, new Error(`Job ${jobId} is no longer available`))
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
690
701
|
function upsertJob(
|
|
691
702
|
input: GeneratedAnalysisJobInput,
|
|
692
703
|
fallbackDefinitionId?: string,
|