@morscherlab/mint-sdk 1.0.63 → 1.1.0-beta.2
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/README.md +101 -2
- package/dist/EmptyState-D1c4RdC7.js +638 -0
- package/dist/EmptyState-D1c4RdC7.js.map +1 -0
- package/dist/{ExperimentPopover-BbPkIFsI.js → ExperimentPopover-BFw3QX8L.js} +10 -9
- package/dist/{ExperimentPopover-BbPkIFsI.js.map → ExperimentPopover-BFw3QX8L.js.map} +1 -1
- package/dist/{ExperimentSelectorModal-DZeSd-yr.js → ExperimentSelectorModal-B7w6k3Y8.js} +12 -216
- package/dist/ExperimentSelectorModal-B7w6k3Y8.js.map +1 -0
- package/dist/{SettingsModal-CJgLadsN.js → SettingsModal-DPDwCbXK.js} +12 -9
- package/dist/{SettingsModal-CJgLadsN.js.map → SettingsModal-DPDwCbXK.js.map} +1 -1
- package/dist/__tests__/components/GeneratedPlotlyResult.test.d.ts +1 -0
- package/dist/__tests__/components/GeneratedPluginView.test.d.ts +1 -0
- package/dist/__tests__/components/GeneratedResultRenderer.test.d.ts +1 -0
- package/dist/__tests__/components/InstrumentAlertLog.test.d.ts +1 -0
- package/dist/__tests__/composables/useGeneratedAnalysis.test.d.ts +1 -0
- package/dist/__tests__/generatedUi.test.d.ts +1 -0
- package/dist/{analysisArtifactTypes-Cu40dzSG.js → analysisArtifactTypes-ejqR8MX3.js} +1 -1
- package/dist/analysisArtifactTypes-ejqR8MX3.js.map +1 -0
- package/dist/{auth-DZAwEeis.js → auth-B87xM--s.js} +1 -1
- package/dist/{auth-DZAwEeis.js.map → auth-B87xM--s.js.map} +1 -1
- package/dist/chunk-BoAXSpZd.js +33 -0
- package/dist/components/AppSidebar.vue.d.ts +1 -1
- package/dist/components/AutoGroupModal.vue.d.ts +2 -2
- package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +2 -2
- package/dist/components/CollapsibleCard.vue.d.ts +1 -1
- package/dist/components/ComponentBindingRenderer.vue.d.ts +1 -1
- package/dist/components/ConfirmDialog.vue.d.ts +1 -1
- package/dist/components/ExperimentDataViewer.vue.d.ts +1 -1
- package/dist/components/ExperimentTimeline.vue.d.ts +1 -1
- package/dist/components/FileUploader.vue.d.ts +4 -2
- package/dist/components/FitPanel.vue.d.ts +1 -1
- package/dist/components/FormActions.vue.d.ts +1 -1
- package/dist/components/FormBuilder.vue.d.ts +1 -1
- package/dist/components/GeneratedPathInput.vue.d.ts +12 -0
- package/dist/components/GeneratedPlotlyResult.vue.d.ts +11 -0
- package/dist/components/GeneratedPluginView.vue.d.ts +10 -0
- package/dist/components/GeneratedResultRenderer.vue.d.ts +6 -0
- package/dist/components/JobsStatusTray.vue.d.ts +5 -1
- package/dist/components/LcmsSequenceTable.vue.d.ts +1 -1
- package/dist/components/SampleLegend.vue.d.ts +1 -1
- package/dist/components/SampleSelector.vue.d.ts +2 -2
- package/dist/components/SettingsModal.vue.d.ts +1 -1
- package/dist/components/SmartGroupFieldRecipe.vue.d.ts +2 -2
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.js +8 -9
- package/dist/components/internal/PlateMapEditorToolbarInternal.vue.d.ts +2 -2
- package/dist/{components-DdzHh3a_.js → components-UfkPN1bK.js} +2255 -2303
- package/dist/components-UfkPN1bK.js.map +1 -0
- package/dist/composables/index.d.ts +2 -3
- package/dist/composables/index.js +10 -11
- package/dist/composables/platformContextHelpers.d.ts +0 -2
- package/dist/composables/useAnalysisArtifacts.d.ts +1 -3
- package/dist/composables/useGeneratedAnalysis.d.ts +62 -0
- package/dist/composables/useJobsStatusTray.d.ts +48 -4
- package/dist/composables/usePlatformContext.d.ts +3 -0
- package/dist/composables/usePluginClient.d.ts +6 -3
- package/dist/{composables-BUUphBqg.js → composables-B512bWJP.js} +155 -74
- package/dist/composables-B512bWJP.js.map +1 -0
- package/dist/experiment-utils-CxkruZ3G.js +392 -0
- package/dist/experiment-utils-CxkruZ3G.js.map +1 -0
- package/dist/generated/schema.d.ts +7 -0
- package/dist/generated-ui/main.d.ts +0 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -18
- package/dist/index.js.map +1 -1
- package/dist/install.js +7 -8
- package/dist/install.js.map +1 -1
- package/dist/plotly.min-BR53hja4.js +180634 -0
- package/dist/plotly.min-BR53hja4.js.map +1 -0
- package/dist/stores/auth.d.ts +2 -2
- package/dist/stores/index.js +1 -1
- package/dist/styles.css +10543 -10404
- package/dist/templates/index.js +4 -3
- package/dist/templates/packs.d.ts +0 -1
- package/dist/{templates-CNbPQNID.js → templates-Cttbjbyc.js} +2 -4
- package/dist/templates-Cttbjbyc.js.map +1 -0
- package/dist/types/analysisArtifactTypes.d.ts +0 -8
- package/dist/types/form-builder.d.ts +1 -1
- package/dist/types/generated-ui.d.ts +170 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.js +1 -1
- package/dist/types/instrument.d.ts +1 -0
- package/dist/types/jobs.d.ts +7 -1
- package/dist/types/platform.d.ts +2 -0
- package/dist/{useControlSchema-BZNdalmL.js → useControlSchema-Dallso-L.js} +1852 -171
- package/dist/useControlSchema-Dallso-L.js.map +1 -0
- package/dist/{useDropdownState-Bb-c_PeE.js → useDropdownState-gVaMXtwW.js} +2 -2
- package/dist/{useDropdownState-Bb-c_PeE.js.map → useDropdownState-gVaMXtwW.js.map} +1 -1
- package/dist/{useExperimentSelector-DRbj6vse.js → useExperimentSelector-BMjzNcf2.js} +4 -255
- package/dist/useExperimentSelector-BMjzNcf2.js.map +1 -0
- package/dist/{useFormBuilder-DKekvXRs.js → useFormBuilder-BXaaDI41.js} +2 -2
- package/dist/{useFormBuilder-DKekvXRs.js.map → useFormBuilder-BXaaDI41.js.map} +1 -1
- package/dist/{useAnalysisArtifacts-C3l0a0vu.js → useProtocolTemplates-BP7_4RGQ.js} +307 -475
- package/dist/useProtocolTemplates-BP7_4RGQ.js.map +1 -0
- package/package.json +7 -3
- package/src/__tests__/components/GeneratedPathInput.test.ts +132 -0
- package/src/__tests__/components/GeneratedPlotlyResult.test.ts +117 -0
- package/src/__tests__/components/GeneratedPluginView.test.ts +230 -0
- package/src/__tests__/components/GeneratedResultRenderer.test.ts +93 -0
- package/src/__tests__/components/InstrumentAlertLog.test.ts +59 -0
- package/src/__tests__/composables/formBuilderRegistry.test.ts +5 -1
- package/src/__tests__/composables/useAnalysisArtifacts.test.ts +1 -74
- package/src/__tests__/composables/useApi.test.ts +8 -0
- package/src/__tests__/composables/useGeneratedAnalysis.test.ts +1740 -0
- package/src/__tests__/composables/useJobsStatusTray.test.ts +179 -1
- package/src/__tests__/composables/usePlatformContext.test.ts +28 -0
- package/src/__tests__/composables/usePluginClient.test.ts +366 -20
- package/src/__tests__/composables/usePluginConfig.test.ts +323 -7
- package/src/__tests__/docs/extractDocsTemplates.test.ts +0 -1
- package/src/__tests__/generatedUi.test.ts +119 -0
- package/src/__tests__/templates/templates.test.ts +1 -1
- package/src/components/FileUploader.vue +49 -11
- package/src/components/GeneratedPathInput.vue +218 -0
- package/src/components/GeneratedPlotlyResult.vue +87 -0
- package/src/components/GeneratedPluginView.vue +279 -0
- package/src/components/GeneratedResultRenderer.vue +137 -0
- package/src/components/InstrumentAlertLog.vue +2 -2
- package/src/components/JobsStatusTray.vue +14 -8
- package/src/components/index.ts +4 -0
- package/src/composables/formBuilderRegistry.ts +2 -0
- package/src/composables/index.ts +18 -5
- package/src/composables/platformContextHelpers.ts +3 -10
- package/src/composables/useAnalysisArtifacts.ts +0 -48
- package/src/composables/useApi.ts +1 -0
- package/src/composables/useGeneratedAnalysis.ts +1252 -0
- package/src/composables/useJobsStatusTray.ts +234 -45
- package/src/composables/usePluginClient.ts +152 -25
- package/src/composables/usePluginConfig.ts +156 -20
- package/src/generated/schema.ts +130 -0
- package/src/generated-ui/main.ts +21 -0
- package/src/index.ts +3 -0
- package/src/jobs.ts +5 -0
- package/src/plotly-dist.d.ts +3 -0
- package/src/templates/packs.ts +0 -3
- package/src/types/analysisArtifactTypes.ts +0 -9
- package/src/types/form-builder.ts +1 -0
- package/src/types/generated-ui.ts +176 -0
- package/src/types/index.ts +2 -1
- package/src/types/instrument.ts +1 -0
- package/src/types/jobs.ts +10 -1
- package/src/types/platform.ts +2 -0
- package/dist/BaseButton-Dgqrze41.js +0 -74
- package/dist/BaseButton-Dgqrze41.js.map +0 -1
- package/dist/BaseModal-B9UA8Y_I.js +0 -165
- package/dist/BaseModal-B9UA8Y_I.js.map +0 -1
- package/dist/BaseSelect-ekgr9fDo.js +0 -179
- package/dist/BaseSelect-ekgr9fDo.js.map +0 -1
- package/dist/ExperimentPopover-Ryusjrhv.js +0 -4
- package/dist/ExperimentSelectorModal-Cg4-LXBD.js +0 -5
- package/dist/ExperimentSelectorModal-DZeSd-yr.js.map +0 -1
- package/dist/SettingsModal-Bl_9w2Y3.js +0 -5
- package/dist/analysisArtifactTypes-Cu40dzSG.js.map +0 -1
- package/dist/components-DdzHh3a_.js.map +0 -1
- package/dist/composables/analysisArtifactTarget.d.ts +0 -5
- package/dist/composables-BUUphBqg.js.map +0 -1
- package/dist/experiment-utils-Bfa7CwPU.js +0 -108
- package/dist/experiment-utils-Bfa7CwPU.js.map +0 -1
- package/dist/templates-CNbPQNID.js.map +0 -1
- package/dist/useAnalysisArtifacts-C3l0a0vu.js.map +0 -1
- package/dist/useControlSchema-BZNdalmL.js.map +0 -1
- package/dist/useEventListener-CfVkP9Xz.js +0 -57
- package/dist/useEventListener-CfVkP9Xz.js.map +0 -1
- package/dist/useExperimentSelector-DRbj6vse.js.map +0 -1
- package/src/__tests__/composables/analysisArtifactTarget.test.ts +0 -77
- package/src/composables/analysisArtifactTarget.ts +0 -53
- /package/dist/__tests__/{composables/analysisArtifactTarget.test.d.ts → components/GeneratedPathInput.test.d.ts} +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { r as
|
|
3
|
-
import { u as
|
|
4
|
-
import {
|
|
5
|
-
import { B as toBioTemplateComponentProps, F as getBioTemplateComponentProps$1, H as toBioTemplateComponentPropsById, Jt as getBioTemplatePresetInfo, L as toBioTemplateComponentBindings, P as getBioTemplateComponentBindings, Qt as getBioTemplatePackInfo, R as toBioTemplateComponentBindingsById, U as toBioTemplateComponentSnippets, V as toBioTemplateComponentPropsByComponent$1, W as toBioTemplateComponentUsage, d as createBioTemplatePresetCollectionFromControls, dn as extractTemplateCollection, i as createBioTemplateControlToolkit, p as createBioTemplatePackCollection, sn as createTemplateCollection, u as createBioTemplatePresetCollection, un as ensureTemplateFromCollection, z as toBioTemplateComponentImports } from "./templates-
|
|
1
|
+
import { u as resolveExperimentCode } from "./experiment-utils-CxkruZ3G.js";
|
|
2
|
+
import { o as useApi, r as useRequestSyncState } from "./EmptyState-D1c4RdC7.js";
|
|
3
|
+
import { A as useConcentrationUnits, H as getInjectedPlatformContext, J as isActiveJobStatus, Q as normalizeJobState, U as parseExperimentId, W as resolveCurrentExperimentId, u as useControlWorkspace } from "./useControlSchema-Dallso-L.js";
|
|
4
|
+
import { r as useSettingsStore } from "./auth-B87xM--s.js";
|
|
5
|
+
import { B as toBioTemplateComponentProps, F as getBioTemplateComponentProps$1, H as toBioTemplateComponentPropsById, Jt as getBioTemplatePresetInfo, L as toBioTemplateComponentBindings, P as getBioTemplateComponentBindings, Qt as getBioTemplatePackInfo, R as toBioTemplateComponentBindingsById, U as toBioTemplateComponentSnippets, V as toBioTemplateComponentPropsByComponent$1, W as toBioTemplateComponentUsage, d as createBioTemplatePresetCollectionFromControls, dn as extractTemplateCollection, i as createBioTemplateControlToolkit, p as createBioTemplatePackCollection, sn as createTemplateCollection, u as createBioTemplatePresetCollection, un as ensureTemplateFromCollection, z as toBioTemplateComponentImports } from "./templates-Cttbjbyc.js";
|
|
6
6
|
import { computed, effectScope, getCurrentScope, inject, onMounted, onScopeDispose, onUnmounted, provide, reactive, readonly, ref, shallowRef, toRaw, toValue, unref, watch } from "vue";
|
|
7
7
|
//#region src/composables/useSortedItems.ts
|
|
8
8
|
/** Shared sorting for SDK tables and lists with stable empty-value handling. */
|
|
@@ -315,6 +315,306 @@ function useMobileSupportGate(options = {}) {
|
|
|
315
315
|
};
|
|
316
316
|
}
|
|
317
317
|
//#endregion
|
|
318
|
+
//#region src/composables/useJobsStatusTray.ts
|
|
319
|
+
/**
|
|
320
|
+
* Combines independent job runtimes without copying their reactive state.
|
|
321
|
+
*
|
|
322
|
+
* Job ids must be globally unique across the sources. A duplicate id throws
|
|
323
|
+
* when jobs are read or an id-routed action is requested, rather than silently
|
|
324
|
+
* dispatching a mutation to the wrong owner.
|
|
325
|
+
*/
|
|
326
|
+
function combinePluginJobSources(sources) {
|
|
327
|
+
const jobs = computed(() => {
|
|
328
|
+
const combined = [];
|
|
329
|
+
const owners = /* @__PURE__ */ new Set();
|
|
330
|
+
for (const source of sources) for (const job of toValue(source.jobs)) {
|
|
331
|
+
if (owners.has(job.job_id)) throw duplicatePluginJobId(job.job_id);
|
|
332
|
+
owners.add(job.job_id);
|
|
333
|
+
combined.push(job);
|
|
334
|
+
}
|
|
335
|
+
return combined;
|
|
336
|
+
});
|
|
337
|
+
function ownerFor(jobId) {
|
|
338
|
+
let owner;
|
|
339
|
+
for (const source of sources) {
|
|
340
|
+
if (!toValue(source.jobs).some((job) => job.job_id === jobId)) continue;
|
|
341
|
+
if (owner) throw duplicatePluginJobId(jobId);
|
|
342
|
+
owner = source;
|
|
343
|
+
}
|
|
344
|
+
if (!owner) throw new Error(`Unknown plugin job id: ${jobId}`);
|
|
345
|
+
return owner;
|
|
346
|
+
}
|
|
347
|
+
const combined = { jobs };
|
|
348
|
+
if (sources.some((source) => source.refreshJobs)) combined.refreshJobs = async () => {
|
|
349
|
+
await Promise.all(sources.map((source) => source.refreshJobs?.()));
|
|
350
|
+
return jobs.value;
|
|
351
|
+
};
|
|
352
|
+
if (sources.some((source) => source.cancelJob)) combined.cancelJob = async (jobId) => {
|
|
353
|
+
const owner = ownerFor(jobId);
|
|
354
|
+
if (!owner.cancelJob) throw new Error(`Job ${jobId} cannot be cancelled by its source`);
|
|
355
|
+
return owner.cancelJob(jobId);
|
|
356
|
+
};
|
|
357
|
+
if (sources.length > 0 && sources.every((source) => source.deleteJob)) combined.deleteJob = async (jobId) => {
|
|
358
|
+
const owner = ownerFor(jobId);
|
|
359
|
+
if (!owner.deleteJob) throw new Error(`Job ${jobId} cannot be deleted by its source`);
|
|
360
|
+
return owner.deleteJob(jobId);
|
|
361
|
+
};
|
|
362
|
+
if (sources.length > 0 && sources.every((source) => source.clearFinishedJobs)) combined.clearFinishedJobs = async () => {
|
|
363
|
+
const results = await Promise.all(sources.map((source) => source.clearFinishedJobs()));
|
|
364
|
+
return results.every((result) => typeof result === "number") ? results.reduce((total, result) => total + result, 0) : void 0;
|
|
365
|
+
};
|
|
366
|
+
if (sources.some((source) => source.dispose)) combined.dispose = () => {
|
|
367
|
+
const errors = [];
|
|
368
|
+
for (const source of sources) try {
|
|
369
|
+
source.dispose?.();
|
|
370
|
+
} catch (cause) {
|
|
371
|
+
errors.push(cause);
|
|
372
|
+
}
|
|
373
|
+
if (errors.length === 1) throw errors[0];
|
|
374
|
+
if (errors.length > 1) throw new PluginJobSourceDisposeError(errors);
|
|
375
|
+
};
|
|
376
|
+
return combined;
|
|
377
|
+
}
|
|
378
|
+
var PluginJobSourceDisposeError = class extends Error {
|
|
379
|
+
cause;
|
|
380
|
+
errors;
|
|
381
|
+
constructor(errors) {
|
|
382
|
+
super("Failed to dispose plugin job sources");
|
|
383
|
+
this.name = "PluginJobSourceDisposeError";
|
|
384
|
+
this.cause = errors[0];
|
|
385
|
+
this.errors = Object.freeze([...errors]);
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
function duplicatePluginJobId(jobId) {
|
|
389
|
+
return /* @__PURE__ */ new Error(`Duplicate plugin job id across combined sources: ${jobId}`);
|
|
390
|
+
}
|
|
391
|
+
function responseJobs(response) {
|
|
392
|
+
return "jobs" in response ? response.jobs : response;
|
|
393
|
+
}
|
|
394
|
+
function isSnapshotPayload(data) {
|
|
395
|
+
return "jobs" in data && Array.isArray(data.jobs);
|
|
396
|
+
}
|
|
397
|
+
function errorMessage(error, fallback) {
|
|
398
|
+
return error instanceof Error ? error.message : fallback;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Headless controller for plugin job presentation and actions.
|
|
402
|
+
*
|
|
403
|
+
* When a source is supplied, every job list is derived directly from that
|
|
404
|
+
* source. The controller never mirrors source jobs into another ref.
|
|
405
|
+
*/
|
|
406
|
+
function usePluginJobCenter(options = {}) {
|
|
407
|
+
const legacyOverride = shallowRef(null);
|
|
408
|
+
const isOpen = ref(false);
|
|
409
|
+
const activeTab = ref("active");
|
|
410
|
+
const isLoading = ref(false);
|
|
411
|
+
const error = shallowRef(null);
|
|
412
|
+
const pendingJobIds = ref(/* @__PURE__ */ new Set());
|
|
413
|
+
const jobRevisions = /* @__PURE__ */ new Map();
|
|
414
|
+
let stateRevision = 0;
|
|
415
|
+
let requestGeneration = 0;
|
|
416
|
+
let active = true;
|
|
417
|
+
const source = computed(() => toValue(options.source));
|
|
418
|
+
const adapter = computed(() => toValue(options.adapter));
|
|
419
|
+
const jobs = computed(() => {
|
|
420
|
+
const currentSource = source.value;
|
|
421
|
+
if (currentSource) return [...toValue(currentSource.jobs)].map(normalizeJobState);
|
|
422
|
+
const input = toValue(options.jobs);
|
|
423
|
+
const override = legacyOverride.value;
|
|
424
|
+
if (override && override.input === input) return override.jobs;
|
|
425
|
+
return input ? [...input].map(normalizeJobState) : [];
|
|
426
|
+
});
|
|
427
|
+
const activeJobs = computed(() => jobs.value.filter((job) => isActiveJobStatus(job.status)));
|
|
428
|
+
const historyJobs = computed(() => jobs.value.filter((job) => !isActiveJobStatus(job.status)));
|
|
429
|
+
const deletableJobs = computed(() => historyJobs.value.filter((job) => job.can_delete));
|
|
430
|
+
const featuredJob = computed(() => activeJobs.value[0] ?? historyJobs.value[0] ?? null);
|
|
431
|
+
const canRefresh = computed(() => Boolean(source.value?.refreshJobs || adapter.value?.listJobs));
|
|
432
|
+
const canCancel = computed(() => Boolean(source.value?.cancelJob || adapter.value?.cancelJob));
|
|
433
|
+
const canDelete = computed(() => Boolean(source.value?.deleteJob || adapter.value?.deleteJob));
|
|
434
|
+
const canClearFinished = computed(() => Boolean(source.value?.clearFinishedJobs || source.value?.deleteJob || adapter.value?.clearFinishedJobs || adapter.value?.deleteJob));
|
|
435
|
+
function bumpJobRevision(jobId) {
|
|
436
|
+
jobRevisions.set(jobId, (jobRevisions.get(jobId) ?? 0) + 1);
|
|
437
|
+
}
|
|
438
|
+
function replaceLegacyJobs(nextJobs) {
|
|
439
|
+
const normalized = nextJobs.map(normalizeJobState);
|
|
440
|
+
new Set([...jobs.value.map((job) => job.job_id), ...normalized.map((job) => job.job_id)]).forEach(bumpJobRevision);
|
|
441
|
+
legacyOverride.value = {
|
|
442
|
+
input: toValue(options.jobs),
|
|
443
|
+
jobs: normalized
|
|
444
|
+
};
|
|
445
|
+
stateRevision += 1;
|
|
446
|
+
}
|
|
447
|
+
function upsertLegacyJob(nextJob) {
|
|
448
|
+
const normalized = normalizeJobState(nextJob);
|
|
449
|
+
const existingIndex = jobs.value.findIndex((job) => job.job_id === normalized.job_id);
|
|
450
|
+
const nextJobs = [...jobs.value];
|
|
451
|
+
if (existingIndex === -1) nextJobs.unshift(normalized);
|
|
452
|
+
else nextJobs[existingIndex] = normalized;
|
|
453
|
+
legacyOverride.value = {
|
|
454
|
+
input: toValue(options.jobs),
|
|
455
|
+
jobs: nextJobs
|
|
456
|
+
};
|
|
457
|
+
bumpJobRevision(normalized.job_id);
|
|
458
|
+
stateRevision += 1;
|
|
459
|
+
}
|
|
460
|
+
function removeLegacyJobs(jobIds) {
|
|
461
|
+
legacyOverride.value = {
|
|
462
|
+
input: toValue(options.jobs),
|
|
463
|
+
jobs: jobs.value.filter((job) => !jobIds.has(job.job_id))
|
|
464
|
+
};
|
|
465
|
+
jobIds.forEach(bumpJobRevision);
|
|
466
|
+
stateRevision += 1;
|
|
467
|
+
}
|
|
468
|
+
function setPending(jobId, pending) {
|
|
469
|
+
const next = new Set(pendingJobIds.value);
|
|
470
|
+
if (pending) next.add(jobId);
|
|
471
|
+
else next.delete(jobId);
|
|
472
|
+
pendingJobIds.value = next;
|
|
473
|
+
}
|
|
474
|
+
function applyStreamMessage(message) {
|
|
475
|
+
if (!active || source.value) return;
|
|
476
|
+
if (message.event === "snapshot" && isSnapshotPayload(message.data)) {
|
|
477
|
+
replaceLegacyJobs(message.data.jobs);
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if ((message.event === "job" || message.event === "terminal") && !isSnapshotPayload(message.data)) upsertLegacyJob(message.data);
|
|
481
|
+
}
|
|
482
|
+
async function refresh() {
|
|
483
|
+
const currentSource = source.value;
|
|
484
|
+
const listJobs = currentSource?.refreshJobs ?? adapter.value?.listJobs;
|
|
485
|
+
if (!listJobs) return false;
|
|
486
|
+
const generation = ++requestGeneration;
|
|
487
|
+
const revision = stateRevision;
|
|
488
|
+
isLoading.value = true;
|
|
489
|
+
error.value = null;
|
|
490
|
+
try {
|
|
491
|
+
const response = await listJobs();
|
|
492
|
+
if (!active || generation !== requestGeneration || revision !== stateRevision) return false;
|
|
493
|
+
if (!currentSource && response) replaceLegacyJobs(responseJobs(response));
|
|
494
|
+
return true;
|
|
495
|
+
} catch (cause) {
|
|
496
|
+
if (active && generation === requestGeneration) error.value = errorMessage(cause, "Failed to load jobs");
|
|
497
|
+
return false;
|
|
498
|
+
} finally {
|
|
499
|
+
if (active && generation === requestGeneration) isLoading.value = false;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
async function cancelJob(job) {
|
|
503
|
+
const sourceCancel = source.value?.cancelJob;
|
|
504
|
+
const adapterCancel = adapter.value?.cancelJob;
|
|
505
|
+
if (!job.can_cancel || !sourceCancel && !adapterCancel || pendingJobIds.value.has(job.job_id)) return false;
|
|
506
|
+
const revision = jobRevisions.get(job.job_id) ?? 0;
|
|
507
|
+
setPending(job.job_id, true);
|
|
508
|
+
error.value = null;
|
|
509
|
+
try {
|
|
510
|
+
const updated = sourceCancel ? await sourceCancel(job.job_id) : await adapterCancel(job);
|
|
511
|
+
if (!active) return false;
|
|
512
|
+
if (!sourceCancel && revision === (jobRevisions.get(job.job_id) ?? 0)) if (updated) upsertLegacyJob(updated);
|
|
513
|
+
else await refresh();
|
|
514
|
+
return true;
|
|
515
|
+
} catch (cause) {
|
|
516
|
+
if (active) error.value = errorMessage(cause, "Failed to cancel job");
|
|
517
|
+
return false;
|
|
518
|
+
} finally {
|
|
519
|
+
if (active) setPending(job.job_id, false);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
async function deleteJob(job) {
|
|
523
|
+
const sourceDelete = source.value?.deleteJob;
|
|
524
|
+
const adapterDelete = adapter.value?.deleteJob;
|
|
525
|
+
if (!job.can_delete || !sourceDelete && !adapterDelete || pendingJobIds.value.has(job.job_id)) return false;
|
|
526
|
+
setPending(job.job_id, true);
|
|
527
|
+
error.value = null;
|
|
528
|
+
try {
|
|
529
|
+
const deleted = sourceDelete ? await sourceDelete(job.job_id) : await adapterDelete(job);
|
|
530
|
+
if (!active) return false;
|
|
531
|
+
if (!sourceDelete) if (deleted === false) await refresh();
|
|
532
|
+
else removeLegacyJobs(new Set([job.job_id]));
|
|
533
|
+
return deleted !== false;
|
|
534
|
+
} catch (cause) {
|
|
535
|
+
if (active) error.value = errorMessage(cause, "Failed to delete job");
|
|
536
|
+
return false;
|
|
537
|
+
} finally {
|
|
538
|
+
if (active) setPending(job.job_id, false);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
async function clearFinished() {
|
|
542
|
+
const targets = deletableJobs.value;
|
|
543
|
+
if (targets.length === 0) return false;
|
|
544
|
+
error.value = null;
|
|
545
|
+
try {
|
|
546
|
+
const currentSource = source.value;
|
|
547
|
+
if (currentSource?.clearFinishedJobs) {
|
|
548
|
+
const deleted = await currentSource.clearFinishedJobs();
|
|
549
|
+
if (!active) return false;
|
|
550
|
+
return typeof deleted !== "number" || deleted === targets.length;
|
|
551
|
+
}
|
|
552
|
+
if (currentSource?.deleteJob) return (await Promise.all(targets.map((job) => deleteJob(job)))).every(Boolean);
|
|
553
|
+
const currentAdapter = adapter.value;
|
|
554
|
+
if (currentAdapter?.clearFinishedJobs) {
|
|
555
|
+
const deleted = await currentAdapter.clearFinishedJobs(targets);
|
|
556
|
+
if (!active) return false;
|
|
557
|
+
if (typeof deleted === "number" && deleted !== targets.length) {
|
|
558
|
+
await refresh();
|
|
559
|
+
return false;
|
|
560
|
+
}
|
|
561
|
+
removeLegacyJobs(new Set(targets.map((job) => job.job_id)));
|
|
562
|
+
return true;
|
|
563
|
+
}
|
|
564
|
+
if (!currentAdapter?.deleteJob) return false;
|
|
565
|
+
return (await Promise.all(targets.map((job) => deleteJob(job)))).every(Boolean);
|
|
566
|
+
} catch (cause) {
|
|
567
|
+
if (active) error.value = errorMessage(cause, "Failed to clear jobs");
|
|
568
|
+
return false;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
if (options.eventStream !== void 0) watch(() => toValue(options.eventStream)?.lastMessage.value ?? null, (message) => {
|
|
572
|
+
if (message) applyStreamMessage(message);
|
|
573
|
+
}, { immediate: true });
|
|
574
|
+
onMounted(() => {
|
|
575
|
+
if (options.loadOnMount !== false) refresh();
|
|
576
|
+
});
|
|
577
|
+
onUnmounted(() => {
|
|
578
|
+
active = false;
|
|
579
|
+
requestGeneration += 1;
|
|
580
|
+
});
|
|
581
|
+
return {
|
|
582
|
+
jobs,
|
|
583
|
+
activeJobs,
|
|
584
|
+
historyJobs,
|
|
585
|
+
deletableJobs,
|
|
586
|
+
featuredJob,
|
|
587
|
+
canRefresh,
|
|
588
|
+
canCancel,
|
|
589
|
+
canDelete,
|
|
590
|
+
canClearFinished,
|
|
591
|
+
isOpen,
|
|
592
|
+
activeTab,
|
|
593
|
+
isLoading,
|
|
594
|
+
error,
|
|
595
|
+
pendingJobIds,
|
|
596
|
+
open: () => {
|
|
597
|
+
isOpen.value = true;
|
|
598
|
+
},
|
|
599
|
+
close: () => {
|
|
600
|
+
isOpen.value = false;
|
|
601
|
+
},
|
|
602
|
+
toggle: () => {
|
|
603
|
+
isOpen.value = !isOpen.value;
|
|
604
|
+
},
|
|
605
|
+
refresh,
|
|
606
|
+
applyStreamMessage,
|
|
607
|
+
cancelJob,
|
|
608
|
+
deleteJob,
|
|
609
|
+
clearFinished,
|
|
610
|
+
isPending: (job) => pendingJobIds.value.has(job.job_id)
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
/** Backward-compatible name for the plugin job-center controller. */
|
|
614
|
+
function useJobsStatusTray(options = {}) {
|
|
615
|
+
return usePluginJobCenter(options);
|
|
616
|
+
}
|
|
617
|
+
//#endregion
|
|
318
618
|
//#region src/composables/useDoseCalculator.ts
|
|
319
619
|
var VOLUME_UNITS = [
|
|
320
620
|
"µL",
|
|
@@ -2706,51 +3006,6 @@ function normalizeIds(ids) {
|
|
|
2706
3006
|
return [...new Set(ids.filter(Boolean))];
|
|
2707
3007
|
}
|
|
2708
3008
|
//#endregion
|
|
2709
|
-
//#region src/composables/platformContextHelpers.ts
|
|
2710
|
-
function getInjectedPlatformContext() {
|
|
2711
|
-
if (typeof window === "undefined") return void 0;
|
|
2712
|
-
return window.__MINT_PLATFORM__;
|
|
2713
|
-
}
|
|
2714
|
-
/** Return the platform's canonical plugin identity for the mounted frontend. */
|
|
2715
|
-
function resolveCurrentPluginId() {
|
|
2716
|
-
const pluginId = getInjectedPlatformContext()?.plugin?.id;
|
|
2717
|
-
return typeof pluginId === "string" && pluginId ? pluginId : void 0;
|
|
2718
|
-
}
|
|
2719
|
-
function getInjectedExperimentContext() {
|
|
2720
|
-
return getInjectedPlatformContext();
|
|
2721
|
-
}
|
|
2722
|
-
function currentExperimentFromContext(context = getInjectedExperimentContext()) {
|
|
2723
|
-
const candidate = context?.currentExperiment ?? context?.experiment;
|
|
2724
|
-
return candidate && typeof candidate === "object" ? candidate : void 0;
|
|
2725
|
-
}
|
|
2726
|
-
function currentExperimentIdFromContext(context = getInjectedExperimentContext()) {
|
|
2727
|
-
return parseExperimentId(context?.currentExperimentId ?? context?.experimentId ?? context?.currentExperiment ?? context?.experiment);
|
|
2728
|
-
}
|
|
2729
|
-
function currentExperimentIdFromUrl() {
|
|
2730
|
-
if (typeof window === "undefined") return void 0;
|
|
2731
|
-
const location = window.location;
|
|
2732
|
-
const params = new URLSearchParams(location?.search ?? "");
|
|
2733
|
-
return parseExperimentId(params.get("experimentId") ?? params.get("experiment_id")) ?? experimentIdFromPath(location?.pathname ?? "") ?? experimentIdFromPath((location?.hash ?? "").replace(/^#\/?/, "/"));
|
|
2734
|
-
}
|
|
2735
|
-
function resolveCurrentExperimentId(context = getInjectedExperimentContext()) {
|
|
2736
|
-
return currentExperimentIdFromContext(context) ?? currentExperimentIdFromUrl();
|
|
2737
|
-
}
|
|
2738
|
-
function parseExperimentId(value) {
|
|
2739
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
2740
|
-
if (typeof value === "string" && value.trim()) {
|
|
2741
|
-
const parsed = Number(value);
|
|
2742
|
-
return Number.isFinite(parsed) ? parsed : void 0;
|
|
2743
|
-
}
|
|
2744
|
-
if (value && typeof value === "object" && "id" in value) return parseExperimentId(value.id);
|
|
2745
|
-
}
|
|
2746
|
-
function experimentIdFromPath(path) {
|
|
2747
|
-
const segments = path.split("/").filter(Boolean);
|
|
2748
|
-
for (let index = 0; index < segments.length - 1; index += 1) {
|
|
2749
|
-
const segment = segments[index]?.toLowerCase();
|
|
2750
|
-
if (segment === "experiment" || segment === "experiments") return parseExperimentId(decodeURIComponent(segments[index + 1] ?? ""));
|
|
2751
|
-
}
|
|
2752
|
-
}
|
|
2753
|
-
//#endregion
|
|
2754
3009
|
//#region src/composables/useExperimentData.ts
|
|
2755
3010
|
/** Fetches and normalises experiment output data (tree, table, summary) from the platform API. */
|
|
2756
3011
|
function useExperimentData(options = {}) {
|
|
@@ -4494,429 +4749,6 @@ function useProtocolTemplates() {
|
|
|
4494
4749
|
};
|
|
4495
4750
|
}
|
|
4496
4751
|
//#endregion
|
|
4497
|
-
|
|
4498
|
-
var ACTIVE_JOB_STATUSES = new Set([
|
|
4499
|
-
"awaiting_input",
|
|
4500
|
-
"queued",
|
|
4501
|
-
"running"
|
|
4502
|
-
]);
|
|
4503
|
-
var TERMINAL_JOB_STATUSES = new Set([
|
|
4504
|
-
"completed",
|
|
4505
|
-
"failed",
|
|
4506
|
-
"cancelled"
|
|
4507
|
-
]);
|
|
4508
|
-
/** Resolve action support while retaining one-cycle legacy status inference. */
|
|
4509
|
-
function resolveJobCapabilities(job) {
|
|
4510
|
-
const legacyCanCancel = job.status === "queued" || job.status === "running";
|
|
4511
|
-
const legacyCanDelete = TERMINAL_JOB_STATUSES.has(job.status);
|
|
4512
|
-
return {
|
|
4513
|
-
can_cancel: typeof job.can_cancel === "boolean" ? job.can_cancel : legacyCanCancel,
|
|
4514
|
-
can_delete: typeof job.can_delete === "boolean" ? job.can_delete : legacyCanDelete
|
|
4515
|
-
};
|
|
4516
|
-
}
|
|
4517
|
-
/** Clamp invalid or out-of-range progress values for safe display. */
|
|
4518
|
-
function normalizeJobPercent(value) {
|
|
4519
|
-
if (!Number.isFinite(value)) return 0;
|
|
4520
|
-
return Math.min(100, Math.max(0, value));
|
|
4521
|
-
}
|
|
4522
|
-
/** Create a detached canonical state without changing the transport input. */
|
|
4523
|
-
function normalizeJobState(job) {
|
|
4524
|
-
const capabilities = resolveJobCapabilities(job);
|
|
4525
|
-
return {
|
|
4526
|
-
...job,
|
|
4527
|
-
progress: {
|
|
4528
|
-
...job.progress,
|
|
4529
|
-
percent: normalizeJobPercent(job.progress.percent)
|
|
4530
|
-
},
|
|
4531
|
-
warnings: [...job.warnings],
|
|
4532
|
-
folders: [...job.folders],
|
|
4533
|
-
metadata: { ...job.metadata },
|
|
4534
|
-
...capabilities
|
|
4535
|
-
};
|
|
4536
|
-
}
|
|
4537
|
-
function isActiveJobStatus(status) {
|
|
4538
|
-
return ACTIVE_JOB_STATUSES.has(status);
|
|
4539
|
-
}
|
|
4540
|
-
function isTerminalJobStatus(status) {
|
|
4541
|
-
return TERMINAL_JOB_STATUSES.has(status);
|
|
4542
|
-
}
|
|
4543
|
-
function jobStatusLabel(status) {
|
|
4544
|
-
return {
|
|
4545
|
-
awaiting_input: "Waiting for input",
|
|
4546
|
-
queued: "Queued",
|
|
4547
|
-
running: "Running",
|
|
4548
|
-
completed: "Completed",
|
|
4549
|
-
failed: "Failed",
|
|
4550
|
-
cancelled: "Cancelled"
|
|
4551
|
-
}[status];
|
|
4552
|
-
}
|
|
4553
|
-
//#endregion
|
|
4554
|
-
//#region src/composables/useJobsStatusTray.ts
|
|
4555
|
-
function responseJobs(response) {
|
|
4556
|
-
return "jobs" in response ? response.jobs : response;
|
|
4557
|
-
}
|
|
4558
|
-
function isSnapshotPayload(data) {
|
|
4559
|
-
return "jobs" in data && Array.isArray(data.jobs);
|
|
4560
|
-
}
|
|
4561
|
-
function errorMessage(error, fallback) {
|
|
4562
|
-
return error instanceof Error ? error.message : fallback;
|
|
4563
|
-
}
|
|
4564
|
-
/** Headless state and action controller for the standard SDK jobs tray. */
|
|
4565
|
-
function useJobsStatusTray(options = {}) {
|
|
4566
|
-
const jobs = shallowRef([]);
|
|
4567
|
-
const isOpen = ref(false);
|
|
4568
|
-
const activeTab = ref("active");
|
|
4569
|
-
const isLoading = ref(false);
|
|
4570
|
-
const error = shallowRef(null);
|
|
4571
|
-
const pendingJobIds = ref(/* @__PURE__ */ new Set());
|
|
4572
|
-
const jobRevisions = /* @__PURE__ */ new Map();
|
|
4573
|
-
let stateRevision = 0;
|
|
4574
|
-
let requestGeneration = 0;
|
|
4575
|
-
let active = true;
|
|
4576
|
-
const adapter = computed(() => toValue(options.adapter));
|
|
4577
|
-
const activeJobs = computed(() => jobs.value.filter((job) => isActiveJobStatus(job.status)));
|
|
4578
|
-
const historyJobs = computed(() => jobs.value.filter((job) => !isActiveJobStatus(job.status)));
|
|
4579
|
-
const deletableJobs = computed(() => historyJobs.value.filter((job) => job.can_delete));
|
|
4580
|
-
const featuredJob = computed(() => activeJobs.value[0] ?? historyJobs.value[0] ?? null);
|
|
4581
|
-
function bumpJobRevision(jobId) {
|
|
4582
|
-
jobRevisions.set(jobId, (jobRevisions.get(jobId) ?? 0) + 1);
|
|
4583
|
-
}
|
|
4584
|
-
function replaceJobs(nextJobs) {
|
|
4585
|
-
const normalized = nextJobs.map(normalizeJobState);
|
|
4586
|
-
new Set([...jobs.value.map((job) => job.job_id), ...normalized.map((job) => job.job_id)]).forEach(bumpJobRevision);
|
|
4587
|
-
jobs.value = normalized;
|
|
4588
|
-
stateRevision += 1;
|
|
4589
|
-
}
|
|
4590
|
-
function upsertJob(nextJob) {
|
|
4591
|
-
const normalized = normalizeJobState(nextJob);
|
|
4592
|
-
const existingIndex = jobs.value.findIndex((job) => job.job_id === normalized.job_id);
|
|
4593
|
-
if (existingIndex === -1) jobs.value = [normalized, ...jobs.value];
|
|
4594
|
-
else {
|
|
4595
|
-
const nextJobs = [...jobs.value];
|
|
4596
|
-
nextJobs[existingIndex] = normalized;
|
|
4597
|
-
jobs.value = nextJobs;
|
|
4598
|
-
}
|
|
4599
|
-
bumpJobRevision(normalized.job_id);
|
|
4600
|
-
stateRevision += 1;
|
|
4601
|
-
}
|
|
4602
|
-
function removeJobs(jobIds) {
|
|
4603
|
-
jobs.value = jobs.value.filter((job) => !jobIds.has(job.job_id));
|
|
4604
|
-
jobIds.forEach(bumpJobRevision);
|
|
4605
|
-
stateRevision += 1;
|
|
4606
|
-
}
|
|
4607
|
-
function setPending(jobId, pending) {
|
|
4608
|
-
const next = new Set(pendingJobIds.value);
|
|
4609
|
-
if (pending) next.add(jobId);
|
|
4610
|
-
else next.delete(jobId);
|
|
4611
|
-
pendingJobIds.value = next;
|
|
4612
|
-
}
|
|
4613
|
-
function applyStreamMessage(message) {
|
|
4614
|
-
if (!active) return;
|
|
4615
|
-
if (message.event === "snapshot" && isSnapshotPayload(message.data)) {
|
|
4616
|
-
replaceJobs(message.data.jobs);
|
|
4617
|
-
return;
|
|
4618
|
-
}
|
|
4619
|
-
if ((message.event === "job" || message.event === "terminal") && !isSnapshotPayload(message.data)) upsertJob(message.data);
|
|
4620
|
-
}
|
|
4621
|
-
async function refresh() {
|
|
4622
|
-
const listJobs = adapter.value?.listJobs;
|
|
4623
|
-
if (!listJobs) return false;
|
|
4624
|
-
const generation = ++requestGeneration;
|
|
4625
|
-
const revision = stateRevision;
|
|
4626
|
-
isLoading.value = true;
|
|
4627
|
-
error.value = null;
|
|
4628
|
-
try {
|
|
4629
|
-
const response = await listJobs();
|
|
4630
|
-
if (!active || generation !== requestGeneration || revision !== stateRevision) return false;
|
|
4631
|
-
replaceJobs(responseJobs(response));
|
|
4632
|
-
return true;
|
|
4633
|
-
} catch (cause) {
|
|
4634
|
-
if (active && generation === requestGeneration) error.value = errorMessage(cause, "Failed to load jobs");
|
|
4635
|
-
return false;
|
|
4636
|
-
} finally {
|
|
4637
|
-
if (active && generation === requestGeneration) isLoading.value = false;
|
|
4638
|
-
}
|
|
4639
|
-
}
|
|
4640
|
-
async function cancelJob(job) {
|
|
4641
|
-
const cancel = adapter.value?.cancelJob;
|
|
4642
|
-
if (!job.can_cancel || !cancel || pendingJobIds.value.has(job.job_id)) return false;
|
|
4643
|
-
const revision = jobRevisions.get(job.job_id) ?? 0;
|
|
4644
|
-
setPending(job.job_id, true);
|
|
4645
|
-
error.value = null;
|
|
4646
|
-
try {
|
|
4647
|
-
const updated = await cancel(job);
|
|
4648
|
-
if (!active) return false;
|
|
4649
|
-
if (revision === (jobRevisions.get(job.job_id) ?? 0)) if (updated) upsertJob(updated);
|
|
4650
|
-
else await refresh();
|
|
4651
|
-
return true;
|
|
4652
|
-
} catch (cause) {
|
|
4653
|
-
if (active) error.value = errorMessage(cause, "Failed to cancel job");
|
|
4654
|
-
return false;
|
|
4655
|
-
} finally {
|
|
4656
|
-
if (active) setPending(job.job_id, false);
|
|
4657
|
-
}
|
|
4658
|
-
}
|
|
4659
|
-
async function deleteJob(job) {
|
|
4660
|
-
const deleteJobWithAdapter = adapter.value?.deleteJob;
|
|
4661
|
-
if (!job.can_delete || !deleteJobWithAdapter || pendingJobIds.value.has(job.job_id)) return false;
|
|
4662
|
-
setPending(job.job_id, true);
|
|
4663
|
-
error.value = null;
|
|
4664
|
-
try {
|
|
4665
|
-
const deleted = await deleteJobWithAdapter(job);
|
|
4666
|
-
if (!active) return false;
|
|
4667
|
-
if (deleted === false) await refresh();
|
|
4668
|
-
else removeJobs(new Set([job.job_id]));
|
|
4669
|
-
return deleted !== false;
|
|
4670
|
-
} catch (cause) {
|
|
4671
|
-
if (active) error.value = errorMessage(cause, "Failed to delete job");
|
|
4672
|
-
return false;
|
|
4673
|
-
} finally {
|
|
4674
|
-
if (active) setPending(job.job_id, false);
|
|
4675
|
-
}
|
|
4676
|
-
}
|
|
4677
|
-
async function clearFinished() {
|
|
4678
|
-
const targets = deletableJobs.value;
|
|
4679
|
-
if (targets.length === 0) return false;
|
|
4680
|
-
error.value = null;
|
|
4681
|
-
try {
|
|
4682
|
-
const currentAdapter = adapter.value;
|
|
4683
|
-
if (currentAdapter?.clearFinishedJobs) {
|
|
4684
|
-
const deleted = await currentAdapter.clearFinishedJobs(targets);
|
|
4685
|
-
if (!active) return false;
|
|
4686
|
-
if (typeof deleted === "number" && deleted !== targets.length) {
|
|
4687
|
-
await refresh();
|
|
4688
|
-
return false;
|
|
4689
|
-
}
|
|
4690
|
-
removeJobs(new Set(targets.map((job) => job.job_id)));
|
|
4691
|
-
return true;
|
|
4692
|
-
}
|
|
4693
|
-
if (!currentAdapter?.deleteJob) return false;
|
|
4694
|
-
return (await Promise.all(targets.map((job) => deleteJob(job)))).every(Boolean);
|
|
4695
|
-
} catch (cause) {
|
|
4696
|
-
if (active) error.value = errorMessage(cause, "Failed to clear jobs");
|
|
4697
|
-
return false;
|
|
4698
|
-
}
|
|
4699
|
-
}
|
|
4700
|
-
if (options.jobs !== void 0) watch(() => toValue(options.jobs), (value) => {
|
|
4701
|
-
if (value) replaceJobs(value);
|
|
4702
|
-
}, {
|
|
4703
|
-
immediate: true,
|
|
4704
|
-
deep: true
|
|
4705
|
-
});
|
|
4706
|
-
if (options.eventStream !== void 0) watch(() => toValue(options.eventStream)?.lastMessage.value ?? null, (message) => {
|
|
4707
|
-
if (message) applyStreamMessage(message);
|
|
4708
|
-
}, { immediate: true });
|
|
4709
|
-
onMounted(() => {
|
|
4710
|
-
if (options.loadOnMount !== false) refresh();
|
|
4711
|
-
});
|
|
4712
|
-
onUnmounted(() => {
|
|
4713
|
-
active = false;
|
|
4714
|
-
requestGeneration += 1;
|
|
4715
|
-
});
|
|
4716
|
-
return {
|
|
4717
|
-
jobs,
|
|
4718
|
-
activeJobs,
|
|
4719
|
-
historyJobs,
|
|
4720
|
-
deletableJobs,
|
|
4721
|
-
featuredJob,
|
|
4722
|
-
isOpen,
|
|
4723
|
-
activeTab,
|
|
4724
|
-
isLoading,
|
|
4725
|
-
error,
|
|
4726
|
-
pendingJobIds,
|
|
4727
|
-
open: () => {
|
|
4728
|
-
isOpen.value = true;
|
|
4729
|
-
},
|
|
4730
|
-
close: () => {
|
|
4731
|
-
isOpen.value = false;
|
|
4732
|
-
},
|
|
4733
|
-
toggle: () => {
|
|
4734
|
-
isOpen.value = !isOpen.value;
|
|
4735
|
-
},
|
|
4736
|
-
refresh,
|
|
4737
|
-
applyStreamMessage,
|
|
4738
|
-
cancelJob,
|
|
4739
|
-
deleteJob,
|
|
4740
|
-
clearFinished,
|
|
4741
|
-
isPending: (job) => pendingJobIds.value.has(job.job_id)
|
|
4742
|
-
};
|
|
4743
|
-
}
|
|
4744
|
-
//#endregion
|
|
4745
|
-
//#region src/composables/useAnalysisArtifacts.ts
|
|
4746
|
-
/** Lists and manages analysis artifacts through the user-facing platform API. */
|
|
4747
|
-
function useAnalysisArtifacts(options = {}) {
|
|
4748
|
-
const injectedContext = getInjectedPlatformContext();
|
|
4749
|
-
const api = useApi({ baseUrl: options.apiBaseUrl ?? injectedContext?.platformApiUrl });
|
|
4750
|
-
const currentExperimentId = computed(() => toValue(options.experimentId) ?? resolveCurrentExperimentId(injectedContext));
|
|
4751
|
-
const hasCurrentExperiment = computed(() => currentExperimentId.value !== void 0);
|
|
4752
|
-
const scopePluginId = computed(() => toValue(options.pluginId) ?? null);
|
|
4753
|
-
const request = useRequestSyncState("Analysis artifact request failed.");
|
|
4754
|
-
const fetchedArtifacts = ref([]);
|
|
4755
|
-
const selectedDetail = ref(null);
|
|
4756
|
-
const loadingCount = ref(0);
|
|
4757
|
-
const mutatingCount = ref(0);
|
|
4758
|
-
const isLoading = computed(() => loadingCount.value > 0);
|
|
4759
|
-
const isMutating = computed(() => mutatingCount.value > 0);
|
|
4760
|
-
const search = ref("");
|
|
4761
|
-
const statusFilter = ref("active");
|
|
4762
|
-
const pluginFilter = ref(null);
|
|
4763
|
-
const needsArchived = computed(() => Boolean(toValue(options.includeArchived)) || statusFilter.value !== "active");
|
|
4764
|
-
const artifacts = computed(() => scopePluginId.value ? fetchedArtifacts.value.filter((artifact) => artifact.plugin_id === scopePluginId.value) : fetchedArtifacts.value);
|
|
4765
|
-
const visibleArtifacts = computed(() => {
|
|
4766
|
-
const query = search.value.trim().toLowerCase();
|
|
4767
|
-
return artifacts.value.filter((artifact) => {
|
|
4768
|
-
if (statusFilter.value !== "all" && artifact.status !== statusFilter.value) return false;
|
|
4769
|
-
if (pluginFilter.value && artifact.plugin_id !== pluginFilter.value) return false;
|
|
4770
|
-
if (!query) return true;
|
|
4771
|
-
return artifact.display_name.toLowerCase().includes(query) || artifact.artifact_key.toLowerCase().includes(query);
|
|
4772
|
-
});
|
|
4773
|
-
});
|
|
4774
|
-
let listToken = 0;
|
|
4775
|
-
let detailToken = 0;
|
|
4776
|
-
const hasRequestedList = ref(false);
|
|
4777
|
-
function currentExperimentIdOrError(action) {
|
|
4778
|
-
const id = currentExperimentId.value;
|
|
4779
|
-
if (id === void 0) {
|
|
4780
|
-
request.setError(`No current experiment is selected for ${action}`);
|
|
4781
|
-
return;
|
|
4782
|
-
}
|
|
4783
|
-
return id;
|
|
4784
|
-
}
|
|
4785
|
-
function replaceArtifact(summary) {
|
|
4786
|
-
const index = fetchedArtifacts.value.findIndex((artifact) => artifact.id === summary.id);
|
|
4787
|
-
if (index >= 0) fetchedArtifacts.value.splice(index, 1, summary);
|
|
4788
|
-
}
|
|
4789
|
-
async function list() {
|
|
4790
|
-
const experimentId = currentExperimentIdOrError("listing analysis artifacts");
|
|
4791
|
-
if (experimentId === void 0) {
|
|
4792
|
-
fetchedArtifacts.value = [];
|
|
4793
|
-
return [];
|
|
4794
|
-
}
|
|
4795
|
-
const token = ++listToken;
|
|
4796
|
-
hasRequestedList.value = true;
|
|
4797
|
-
loadingCount.value += 1;
|
|
4798
|
-
request.clearError();
|
|
4799
|
-
try {
|
|
4800
|
-
const data = await api.get(`/experiments/${experimentId}/analysis-artifacts?include_archived=${needsArchived.value}`);
|
|
4801
|
-
if (token !== listToken) return artifacts.value;
|
|
4802
|
-
fetchedArtifacts.value = [...data?.artifacts ?? []];
|
|
4803
|
-
request.markLoaded();
|
|
4804
|
-
return artifacts.value;
|
|
4805
|
-
} catch (err) {
|
|
4806
|
-
if (token !== listToken) return [];
|
|
4807
|
-
request.setError(err);
|
|
4808
|
-
fetchedArtifacts.value = [];
|
|
4809
|
-
return [];
|
|
4810
|
-
} finally {
|
|
4811
|
-
loadingCount.value -= 1;
|
|
4812
|
-
}
|
|
4813
|
-
}
|
|
4814
|
-
async function getDetail(artifactId) {
|
|
4815
|
-
const experimentId = currentExperimentIdOrError("loading an analysis artifact");
|
|
4816
|
-
if (experimentId === void 0) return null;
|
|
4817
|
-
const token = ++detailToken;
|
|
4818
|
-
loadingCount.value += 1;
|
|
4819
|
-
request.clearError();
|
|
4820
|
-
try {
|
|
4821
|
-
const detail = await api.get(`/experiments/${experimentId}/analysis-artifacts/${artifactId}`);
|
|
4822
|
-
if (token !== detailToken) return null;
|
|
4823
|
-
selectedDetail.value = detail;
|
|
4824
|
-
request.markLoaded();
|
|
4825
|
-
return detail;
|
|
4826
|
-
} catch (err) {
|
|
4827
|
-
if (token !== detailToken) return null;
|
|
4828
|
-
request.setError(err);
|
|
4829
|
-
return null;
|
|
4830
|
-
} finally {
|
|
4831
|
-
loadingCount.value -= 1;
|
|
4832
|
-
}
|
|
4833
|
-
}
|
|
4834
|
-
async function resolveTarget(target) {
|
|
4835
|
-
const experimentId = currentExperimentId.value;
|
|
4836
|
-
if (experimentId !== target.experimentId) {
|
|
4837
|
-
request.setError(`Analysis artifact target belongs to experiment ${target.experimentId}, not ${experimentId ?? "the current selection"}.`);
|
|
4838
|
-
return null;
|
|
4839
|
-
}
|
|
4840
|
-
const summary = (await list()).find((artifact) => artifact.plugin_id === target.artifactPluginId && artifact.artifact_key === target.artifactKey && artifact.status === "active");
|
|
4841
|
-
if (!summary) {
|
|
4842
|
-
request.setError(`Analysis artifact ${target.artifactPluginId}/${target.artifactKey} is not available.`);
|
|
4843
|
-
return null;
|
|
4844
|
-
}
|
|
4845
|
-
const detail = await getDetail(summary.id);
|
|
4846
|
-
if (detail && (detail.experiment_id !== target.experimentId || detail.plugin_id !== target.artifactPluginId || detail.artifact_key !== target.artifactKey)) {
|
|
4847
|
-
selectedDetail.value = null;
|
|
4848
|
-
request.setError(`Analysis artifact ${target.artifactPluginId}/${target.artifactKey} did not match the requested identity.`);
|
|
4849
|
-
return null;
|
|
4850
|
-
}
|
|
4851
|
-
return detail;
|
|
4852
|
-
}
|
|
4853
|
-
async function mutate(action, operation) {
|
|
4854
|
-
const experimentId = currentExperimentIdOrError(action);
|
|
4855
|
-
if (experimentId === void 0) return null;
|
|
4856
|
-
mutatingCount.value += 1;
|
|
4857
|
-
request.clearError();
|
|
4858
|
-
try {
|
|
4859
|
-
const summary = await operation(experimentId);
|
|
4860
|
-
replaceArtifact(summary);
|
|
4861
|
-
request.markSaved();
|
|
4862
|
-
return summary;
|
|
4863
|
-
} catch (err) {
|
|
4864
|
-
request.setError(err);
|
|
4865
|
-
return null;
|
|
4866
|
-
} finally {
|
|
4867
|
-
mutatingCount.value -= 1;
|
|
4868
|
-
}
|
|
4869
|
-
}
|
|
4870
|
-
async function updateMetadata(artifactId, updates) {
|
|
4871
|
-
return mutate("updating artifact metadata", (experimentId) => api.patch(`/experiments/${experimentId}/analysis-artifacts/${artifactId}`, updates));
|
|
4872
|
-
}
|
|
4873
|
-
async function archive(artifactId) {
|
|
4874
|
-
return mutate("archiving an analysis artifact", (experimentId) => api.post(`/experiments/${experimentId}/analysis-artifacts/${artifactId}/archive`));
|
|
4875
|
-
}
|
|
4876
|
-
async function restore(artifactId) {
|
|
4877
|
-
return mutate("restoring an analysis artifact", (experimentId) => api.post(`/experiments/${experimentId}/analysis-artifacts/${artifactId}/restore`));
|
|
4878
|
-
}
|
|
4879
|
-
function hasArtifactKey(artifactKey) {
|
|
4880
|
-
return artifacts.value.some((artifact) => artifact.artifact_key === artifactKey);
|
|
4881
|
-
}
|
|
4882
|
-
/** Drop experiment-scoped state and orphan every in-flight response. */
|
|
4883
|
-
function clear() {
|
|
4884
|
-
listToken += 1;
|
|
4885
|
-
detailToken += 1;
|
|
4886
|
-
fetchedArtifacts.value = [];
|
|
4887
|
-
selectedDetail.value = null;
|
|
4888
|
-
request.clearError();
|
|
4889
|
-
}
|
|
4890
|
-
watch([currentExperimentId, needsArchived], ([experimentId], [previousExperimentId]) => {
|
|
4891
|
-
if (experimentId !== previousExperimentId) clear();
|
|
4892
|
-
if (experimentId === void 0) return;
|
|
4893
|
-
if (options.immediate || hasRequestedList.value) list();
|
|
4894
|
-
}, { immediate: options.immediate });
|
|
4895
|
-
return {
|
|
4896
|
-
artifacts,
|
|
4897
|
-
visibleArtifacts,
|
|
4898
|
-
selectedDetail,
|
|
4899
|
-
currentExperimentId,
|
|
4900
|
-
hasCurrentExperiment,
|
|
4901
|
-
isLoading,
|
|
4902
|
-
isMutating,
|
|
4903
|
-
error: request.error,
|
|
4904
|
-
lastLoadedAt: request.lastLoadedAt,
|
|
4905
|
-
search,
|
|
4906
|
-
statusFilter,
|
|
4907
|
-
pluginFilter,
|
|
4908
|
-
list,
|
|
4909
|
-
refresh: list,
|
|
4910
|
-
getDetail,
|
|
4911
|
-
resolveTarget,
|
|
4912
|
-
updateMetadata,
|
|
4913
|
-
archive,
|
|
4914
|
-
restore,
|
|
4915
|
-
hasArtifactKey,
|
|
4916
|
-
clear
|
|
4917
|
-
};
|
|
4918
|
-
}
|
|
4919
|
-
//#endregion
|
|
4920
|
-
export { APP_EXPERIMENT_KEY as $, resolveCurrentExperimentId as A, useFileImport as B, useTemplateCollection as C, useExperimentData as D, useExperimentSamples as E, hexToHsl as F, extractSampleNamesFromDesignData as G, extractSamplesFromDesignData as H, hslToHex as I, DEFAULT_COLORS as J, extractSampleOptionsFromDesignData as K, detectDelimiter as L, useExpansionSet as M, useSampleGroups as N, currentExperimentFromContext as O, deriveShade as P, useMobileSupportGate as Q, parseDelimitedText as R, useBioTemplateControls as S, useScheduleDrag as T, useAutoGroup as U, validateImportFile as V, parseCSV as W, useDoseCalculator as X, useWellPlateEditor as Y, DEFAULT_MOBILE_VIEWPORT_QUERY as Z, useBioTemplatePackWorkspace as _, jobStatusLabel as a, useTextSearch as at, toBioTemplateComponentPropsByComponent as b, resolveJobCapabilities as c, DEFAULT_UNITS as d, useAppExperiment as et, generateDilutionSeries as f, useBioTemplatePresetWorkspace as g, useRackEditor as h, isTerminalJobStatus as i, normalizeSearchQuery as it, resolveCurrentPluginId as j, getInjectedPlatformContext as k, useProtocolTemplates as l, useGroupAssignment as m, useJobsStatusTray as n, useToast as nt, normalizeJobPercent as o, compareSortValues as ot, useReagentSeries as p, unwrapExperimentDesignData as q, isActiveJobStatus as r, candidateMatchesSearch as rt, normalizeJobState as s, useSortedItems as st, useAnalysisArtifacts as t, useTheme as tt, DEFAULT_PRESETS as u, useBioTemplateWorkspace as v, useExperimentSave as w, useBioTemplateComponents as x, getBioTemplateComponentProps as y, readFileAsText as z };
|
|
4752
|
+
export { extractSamplesFromDesignData as A, useJobsStatusTray as B, hexToHsl as C, readFileAsText as D, parseDelimitedText as E, unwrapExperimentDesignData as F, useAppExperiment as G, DEFAULT_MOBILE_VIEWPORT_QUERY as H, DEFAULT_COLORS as I, candidateMatchesSearch as J, useTheme as K, useWellPlateEditor as L, parseCSV as M, extractSampleNamesFromDesignData as N, useFileImport as O, extractSampleOptionsFromDesignData as P, useSortedItems as Q, useDoseCalculator as R, deriveShade as S, detectDelimiter as T, useMobileSupportGate as U, usePluginJobCenter as V, APP_EXPERIMENT_KEY as W, useTextSearch as X, normalizeSearchQuery as Y, compareSortValues as Z, useScheduleDrag as _, useReagentSeries as a, useExpansionSet as b, useBioTemplatePresetWorkspace as c, getBioTemplateComponentProps as d, toBioTemplateComponentPropsByComponent as f, useExperimentSave as g, useTemplateCollection as h, generateDilutionSeries as i, useAutoGroup as j, validateImportFile as k, useBioTemplatePackWorkspace as l, useBioTemplateControls as m, DEFAULT_PRESETS as n, useGroupAssignment as o, useBioTemplateComponents as p, useToast as q, DEFAULT_UNITS as r, useRackEditor as s, useProtocolTemplates as t, useBioTemplateWorkspace as u, useExperimentSamples as v, hslToHex as w, useSampleGroups as x, useExperimentData as y, combinePluginJobSources as z };
|
|
4921
4753
|
|
|
4922
|
-
//# sourceMappingURL=
|
|
4754
|
+
//# sourceMappingURL=useProtocolTemplates-BP7_4RGQ.js.map
|