@morscherlab/mint-sdk 1.2.0-beta.2 → 1.2.0-beta.4

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.
Files changed (62) hide show
  1. package/README.md +5 -5
  2. package/dist/{ExperimentSelectorModal-DyPAUCLw.js → ExperimentSelectorModal-B6P2msT4.js} +3 -3
  3. package/dist/{ExperimentSelectorModal-DyPAUCLw.js.map → ExperimentSelectorModal-B6P2msT4.js.map} +1 -1
  4. package/dist/{SettingsModal-BgKY93zh.js → SettingsModal-CkNM8qAI.js} +4 -4
  5. package/dist/{SettingsModal-BgKY93zh.js.map → SettingsModal-CkNM8qAI.js.map} +1 -1
  6. package/dist/__tests__/composables/usePluginConfig.test.d.ts +15 -0
  7. package/dist/components/AutoGroupModal.vue.d.ts +1 -1
  8. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +1 -1
  9. package/dist/components/JobsStatusTray.vue.d.ts +9 -2
  10. package/dist/components/SampleLegend.vue.d.ts +1 -1
  11. package/dist/components/SampleSelector.vue.d.ts +1 -1
  12. package/dist/components/SettingsModal.vue.d.ts +1 -1
  13. package/dist/components/SmartGroupFieldRecipe.vue.d.ts +1 -1
  14. package/dist/components/index.js +4 -4
  15. package/dist/{components-DkHddRMl.js → components-MGNL6tRn.js} +16 -10
  16. package/dist/components-MGNL6tRn.js.map +1 -0
  17. package/dist/composables/index.d.ts +3 -2
  18. package/dist/composables/index.js +8 -8
  19. package/dist/composables/useGeneratedAnalysis.d.ts +3 -1
  20. package/dist/composables/useJobsStatusTray.d.ts +25 -1
  21. package/dist/composables/usePluginClient.d.ts +2 -0
  22. package/dist/composables/usePluginConfig.d.ts +25 -0
  23. package/dist/{composables-C_i90sGS.js → composables-CKxOcvgh.js} +140 -5
  24. package/dist/composables-CKxOcvgh.js.map +1 -0
  25. package/dist/{experiment-Cq145oEW.js → experiment-Y9Iz7KYi.js} +2 -2
  26. package/dist/{experiment-Cq145oEW.js.map → experiment-Y9Iz7KYi.js.map} +1 -1
  27. package/dist/index.js +12 -12
  28. package/dist/install.js +4 -4
  29. package/dist/stores/auth.d.ts +6 -0
  30. package/dist/stores/index.js +2 -2
  31. package/dist/templates/index.js +2 -2
  32. package/dist/{templates-DsP51i-S.js → templates-BTJ9C9Kb.js} +2 -2
  33. package/dist/{templates-DsP51i-S.js.map → templates-BTJ9C9Kb.js.map} +1 -1
  34. package/dist/types/auth.d.ts +1 -0
  35. package/dist/{useApi-J6zFkHmn.js → useApi-CDBq6z23.js} +2 -1
  36. package/dist/useApi-CDBq6z23.js.map +1 -0
  37. package/dist/{useControlSchema-BDx2Olha.js → useControlSchema-CXhxTul7.js} +10 -3
  38. package/dist/useControlSchema-CXhxTul7.js.map +1 -0
  39. package/dist/{useExperimentSelector-CV4iNfMS.js → useExperimentSelector-D0pjj9WH.js} +2 -2
  40. package/dist/{useExperimentSelector-CV4iNfMS.js.map → useExperimentSelector-D0pjj9WH.js.map} +1 -1
  41. package/dist/{useFormBuilder-BLxG3BOX.js → useFormBuilder-CuOFB3aP.js} +2 -2
  42. package/dist/{useFormBuilder-BLxG3BOX.js.map → useFormBuilder-CuOFB3aP.js.map} +1 -1
  43. package/dist/{useProtocolTemplates-CS_SQl6U.js → useProtocolTemplates-BWhQIWV5.js} +113 -22
  44. package/dist/useProtocolTemplates-BWhQIWV5.js.map +1 -0
  45. package/package.json +1 -1
  46. package/src/__tests__/composables/useAuth.test.ts +2 -0
  47. package/src/__tests__/composables/usePluginConfig.test.ts +497 -0
  48. package/src/__tests__/docs/frontendDocsCatalog.test.ts +17 -6
  49. package/src/components/JobsStatusTray.vue +16 -1
  50. package/src/composables/index.ts +7 -0
  51. package/src/composables/useAuth.ts +3 -0
  52. package/src/composables/useGeneratedAnalysis.ts +14 -1
  53. package/src/composables/useJobsStatusTray.ts +184 -17
  54. package/src/composables/usePluginClient.ts +5 -0
  55. package/src/composables/usePluginConfig.ts +250 -0
  56. package/src/stores/auth.ts +1 -0
  57. package/src/types/auth.ts +1 -0
  58. package/dist/components-DkHddRMl.js.map +0 -1
  59. package/dist/composables-C_i90sGS.js.map +0 -1
  60. package/dist/useApi-J6zFkHmn.js.map +0 -1
  61. package/dist/useControlSchema-BDx2Olha.js.map +0 -1
  62. package/dist/useProtocolTemplates-CS_SQl6U.js.map +0 -1
@@ -1,9 +1,9 @@
1
1
  import { d as resolveExperimentCode } from "./experiment-utils-CLnzPPq5.js";
2
2
  import { r as useRequestSyncState } from "./EmptyState-p90quq8Z.js";
3
- import { A as useConcentrationUnits, H as parseExperimentId, J as isActiveJobStatus, Q as normalizeJobState, U as resolveCurrentExperimentId, V as getInjectedPlatformContext, u as useControlWorkspace } from "./useControlSchema-BDx2Olha.js";
4
- import { o as useSettingsStore, r as useApi } from "./useApi-J6zFkHmn.js";
5
- import { t as useExperimentStore } from "./experiment-Cq145oEW.js";
6
- 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-DsP51i-S.js";
3
+ import { A as useConcentrationUnits, H as parseExperimentId, J as isActiveJobStatus, Q as normalizeJobState, U as resolveCurrentExperimentId, V as getInjectedPlatformContext, u as useControlWorkspace } from "./useControlSchema-CXhxTul7.js";
4
+ import { o as useSettingsStore, r as useApi } from "./useApi-CDBq6z23.js";
5
+ import { t as useExperimentStore } from "./experiment-Y9Iz7KYi.js";
6
+ 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-BTJ9C9Kb.js";
7
7
  import { computed, effectScope, getCurrentScope, inject, onMounted, onScopeDispose, onUnmounted, provide, reactive, readonly, ref, shallowRef, toRaw, toValue, unref, watch } from "vue";
8
8
  //#region src/composables/useSortedItems.ts
9
9
  /** Shared sorting for SDK tables and lists with stable empty-value handling. */
@@ -601,6 +601,12 @@ var PluginJobSourceDisposeError = class extends Error {
601
601
  function duplicatePluginJobId(jobId) {
602
602
  return /* @__PURE__ */ new Error(`Duplicate plugin job id across combined sources: ${jobId}`);
603
603
  }
604
+ function responseJobs(response) {
605
+ return "jobs" in response ? response.jobs : response;
606
+ }
607
+ function isSnapshotPayload(data) {
608
+ return "jobs" in data && Array.isArray(data.jobs);
609
+ }
604
610
  function errorMessage(error, fallback) {
605
611
  return error instanceof Error ? error.message : fallback;
606
612
  }
@@ -611,38 +617,94 @@ function errorMessage(error, fallback) {
611
617
  * source. The controller never mirrors source jobs into another ref.
612
618
  */
613
619
  function usePluginJobCenter(options = {}) {
620
+ const legacyOverride = shallowRef(null);
614
621
  const isOpen = ref(false);
615
622
  const activeTab = ref("active");
616
623
  const isLoading = ref(false);
617
624
  const error = shallowRef(null);
618
625
  const pendingJobIds = ref(/* @__PURE__ */ new Set());
626
+ const jobRevisions = /* @__PURE__ */ new Map();
627
+ let stateRevision = 0;
619
628
  let requestGeneration = 0;
620
629
  let active = true;
621
630
  const source = computed(() => toValue(options.source));
622
- const jobs = computed(() => source.value ? [...toValue(source.value.jobs)].map(normalizeJobState) : []);
631
+ const adapter = computed(() => toValue(options.adapter));
632
+ const jobs = computed(() => {
633
+ const currentSource = source.value;
634
+ if (currentSource) return [...toValue(currentSource.jobs)].map(normalizeJobState);
635
+ const input = toValue(options.jobs);
636
+ const override = legacyOverride.value;
637
+ if (override && override.input === input) return override.jobs;
638
+ return input ? [...input].map(normalizeJobState) : [];
639
+ });
623
640
  const activeJobs = computed(() => jobs.value.filter((job) => isActiveJobStatus(job.status)));
624
641
  const historyJobs = computed(() => jobs.value.filter((job) => !isActiveJobStatus(job.status)));
625
642
  const deletableJobs = computed(() => historyJobs.value.filter((job) => job.can_delete));
626
643
  const featuredJob = computed(() => activeJobs.value[0] ?? historyJobs.value[0] ?? null);
627
- const canRefresh = computed(() => Boolean(source.value?.refreshJobs));
628
- const canCancel = computed(() => Boolean(source.value?.cancelJob));
629
- const canDelete = computed(() => Boolean(source.value?.deleteJob));
630
- const canClearFinished = computed(() => Boolean(source.value?.clearFinishedJobs || source.value?.deleteJob));
644
+ const canRefresh = computed(() => Boolean(source.value?.refreshJobs || adapter.value?.listJobs));
645
+ const canCancel = computed(() => Boolean(source.value?.cancelJob || adapter.value?.cancelJob));
646
+ const canDelete = computed(() => Boolean(source.value?.deleteJob || adapter.value?.deleteJob));
647
+ const canClearFinished = computed(() => Boolean(source.value?.clearFinishedJobs || source.value?.deleteJob || adapter.value?.clearFinishedJobs || adapter.value?.deleteJob));
648
+ function bumpJobRevision(jobId) {
649
+ jobRevisions.set(jobId, (jobRevisions.get(jobId) ?? 0) + 1);
650
+ }
651
+ function replaceLegacyJobs(nextJobs) {
652
+ const normalized = nextJobs.map(normalizeJobState);
653
+ new Set([...jobs.value.map((job) => job.job_id), ...normalized.map((job) => job.job_id)]).forEach(bumpJobRevision);
654
+ legacyOverride.value = {
655
+ input: toValue(options.jobs),
656
+ jobs: normalized
657
+ };
658
+ stateRevision += 1;
659
+ }
660
+ function upsertLegacyJob(nextJob) {
661
+ const normalized = normalizeJobState(nextJob);
662
+ const existingIndex = jobs.value.findIndex((job) => job.job_id === normalized.job_id);
663
+ const nextJobs = [...jobs.value];
664
+ if (existingIndex === -1) nextJobs.unshift(normalized);
665
+ else nextJobs[existingIndex] = normalized;
666
+ legacyOverride.value = {
667
+ input: toValue(options.jobs),
668
+ jobs: nextJobs
669
+ };
670
+ bumpJobRevision(normalized.job_id);
671
+ stateRevision += 1;
672
+ }
673
+ function removeLegacyJobs(jobIds) {
674
+ legacyOverride.value = {
675
+ input: toValue(options.jobs),
676
+ jobs: jobs.value.filter((job) => !jobIds.has(job.job_id))
677
+ };
678
+ jobIds.forEach(bumpJobRevision);
679
+ stateRevision += 1;
680
+ }
631
681
  function setPending(jobId, pending) {
632
682
  const next = new Set(pendingJobIds.value);
633
683
  if (pending) next.add(jobId);
634
684
  else next.delete(jobId);
635
685
  pendingJobIds.value = next;
636
686
  }
687
+ function applyStreamMessage(message) {
688
+ if (!active || source.value) return;
689
+ if (message.event === "snapshot" && isSnapshotPayload(message.data)) {
690
+ replaceLegacyJobs(message.data.jobs);
691
+ return;
692
+ }
693
+ if ((message.event === "job" || message.event === "terminal") && !isSnapshotPayload(message.data)) upsertLegacyJob(message.data);
694
+ }
637
695
  async function refresh() {
638
- const refreshJobs = source.value?.refreshJobs;
639
- if (!refreshJobs) return false;
696
+ const currentSource = source.value;
697
+ const listJobs = currentSource?.refreshJobs ?? adapter.value?.listJobs;
698
+ if (!listJobs) return false;
640
699
  const generation = ++requestGeneration;
700
+ const revision = stateRevision;
641
701
  isLoading.value = true;
642
702
  error.value = null;
643
703
  try {
644
- await refreshJobs();
645
- return active && generation === requestGeneration;
704
+ const response = await listJobs();
705
+ if (!active || generation !== requestGeneration || revision !== stateRevision) return false;
706
+ if (!currentSource && response) replaceLegacyJobs(responseJobs(response));
707
+ return true;
646
708
  } catch (cause) {
647
709
  if (active && generation === requestGeneration) error.value = errorMessage(cause, "Failed to load jobs");
648
710
  return false;
@@ -652,12 +714,17 @@ function usePluginJobCenter(options = {}) {
652
714
  }
653
715
  async function cancelJob(job) {
654
716
  const sourceCancel = source.value?.cancelJob;
655
- if (!job.can_cancel || !sourceCancel || pendingJobIds.value.has(job.job_id)) return false;
717
+ const adapterCancel = adapter.value?.cancelJob;
718
+ if (!job.can_cancel || !sourceCancel && !adapterCancel || pendingJobIds.value.has(job.job_id)) return false;
719
+ const revision = jobRevisions.get(job.job_id) ?? 0;
656
720
  setPending(job.job_id, true);
657
721
  error.value = null;
658
722
  try {
659
- await sourceCancel(job.job_id);
660
- return active;
723
+ const updated = sourceCancel ? await sourceCancel(job.job_id) : await adapterCancel(job);
724
+ if (!active) return false;
725
+ if (!sourceCancel && revision === (jobRevisions.get(job.job_id) ?? 0)) if (updated) upsertLegacyJob(updated);
726
+ else await refresh();
727
+ return true;
661
728
  } catch (cause) {
662
729
  if (active) error.value = errorMessage(cause, "Failed to cancel job");
663
730
  return false;
@@ -667,12 +734,16 @@ function usePluginJobCenter(options = {}) {
667
734
  }
668
735
  async function deleteJob(job) {
669
736
  const sourceDelete = source.value?.deleteJob;
670
- if (!job.can_delete || !sourceDelete || pendingJobIds.value.has(job.job_id)) return false;
737
+ const adapterDelete = adapter.value?.deleteJob;
738
+ if (!job.can_delete || !sourceDelete && !adapterDelete || pendingJobIds.value.has(job.job_id)) return false;
671
739
  setPending(job.job_id, true);
672
740
  error.value = null;
673
741
  try {
674
- const deleted = await sourceDelete(job.job_id);
675
- return active && deleted !== false;
742
+ const deleted = sourceDelete ? await sourceDelete(job.job_id) : await adapterDelete(job);
743
+ if (!active) return false;
744
+ if (!sourceDelete) if (deleted === false) await refresh();
745
+ else removeLegacyJobs(new Set([job.job_id]));
746
+ return deleted !== false;
676
747
  } catch (cause) {
677
748
  if (active) error.value = errorMessage(cause, "Failed to delete job");
678
749
  return false;
@@ -692,12 +763,27 @@ function usePluginJobCenter(options = {}) {
692
763
  return typeof deleted !== "number" || deleted === targets.length;
693
764
  }
694
765
  if (currentSource?.deleteJob) return (await Promise.all(targets.map((job) => deleteJob(job)))).every(Boolean);
695
- return false;
766
+ const currentAdapter = adapter.value;
767
+ if (currentAdapter?.clearFinishedJobs) {
768
+ const deleted = await currentAdapter.clearFinishedJobs(targets);
769
+ if (!active) return false;
770
+ if (typeof deleted === "number" && deleted !== targets.length) {
771
+ await refresh();
772
+ return false;
773
+ }
774
+ removeLegacyJobs(new Set(targets.map((job) => job.job_id)));
775
+ return true;
776
+ }
777
+ if (!currentAdapter?.deleteJob) return false;
778
+ return (await Promise.all(targets.map((job) => deleteJob(job)))).every(Boolean);
696
779
  } catch (cause) {
697
780
  if (active) error.value = errorMessage(cause, "Failed to clear jobs");
698
781
  return false;
699
782
  }
700
783
  }
784
+ if (options.eventStream !== void 0) watch(() => toValue(options.eventStream)?.lastMessage.value ?? null, (message) => {
785
+ if (message) applyStreamMessage(message);
786
+ }, { immediate: true });
701
787
  onMounted(() => {
702
788
  if (options.loadOnMount !== false) refresh();
703
789
  });
@@ -730,12 +816,17 @@ function usePluginJobCenter(options = {}) {
730
816
  isOpen.value = !isOpen.value;
731
817
  },
732
818
  refresh,
819
+ applyStreamMessage,
733
820
  cancelJob,
734
821
  deleteJob,
735
822
  clearFinished,
736
823
  isPending: (job) => pendingJobIds.value.has(job.job_id)
737
824
  };
738
825
  }
826
+ /** @deprecated Use `usePluginJobCenter()` for the plugin job-center controller. */
827
+ function useJobsStatusTray(options = {}) {
828
+ return usePluginJobCenter(options);
829
+ }
739
830
  //#endregion
740
831
  //#region src/composables/useDoseCalculator.ts
741
832
  var VOLUME_UNITS = [
@@ -5053,6 +5144,6 @@ function useProtocolTemplates() {
5053
5144
  };
5054
5145
  }
5055
5146
  //#endregion
5056
- export { candidateMatchesSearch as $, useFileImport as A, useDoseCalculator as B, getSampleGroupParentPath as C, detectDelimiter as D, hslToHex as E, extractSampleNamesFromDesignData as F, APP_EXPERIMENT_KEY as G, usePluginJobCenter as H, extractSampleOptionsFromDesignData as I, useRuntimeAlignment as J, useAppExperiment as K, unwrapExperimentDesignData as L, extractSamplesFromDesignData as M, useAutoGroup as N, parseDelimitedText as O, parseCSV as P, useToast as Q, DEFAULT_COLORS as R, getSampleGroupMajorPrefix as S, hexToHsl as T, DEFAULT_MOBILE_VIEWPORT_QUERY as U, combinePluginJobSources as V, useMobileSupportGate as W, usePresenceHeartbeat as X, getPresenceClientId as Y, useTheme as Z, useScheduleDrag as _, useReagentSeries as a, useExpansionSet as b, useBioTemplatePresetWorkspace as c, getBioTemplateComponentProps as d, normalizeSearchQuery as et, toBioTemplateComponentPropsByComponent as f, useExperimentSave as g, useTemplateCollection as h, generateDilutionSeries as i, validateImportFile as j, readFileAsText as k, useBioTemplatePackWorkspace as l, useBioTemplateControls as m, DEFAULT_PRESETS as n, compareSortValues as nt, useGroupAssignment as o, useBioTemplateComponents as p, getInjectedFrontendRevision as q, DEFAULT_UNITS as r, useSortedItems as rt, useRackEditor as s, useProtocolTemplates as t, useTextSearch as tt, useBioTemplateWorkspace as u, useExperimentSamples as v, deriveShade as w, useSampleGroups as x, useExperimentData as y, useWellPlateEditor as z };
5147
+ export { useToast as $, useFileImport as A, useDoseCalculator as B, getSampleGroupParentPath as C, detectDelimiter as D, hslToHex as E, extractSampleNamesFromDesignData as F, useMobileSupportGate as G, useJobsStatusTray as H, extractSampleOptionsFromDesignData as I, getInjectedFrontendRevision as J, APP_EXPERIMENT_KEY as K, unwrapExperimentDesignData as L, extractSamplesFromDesignData as M, useAutoGroup as N, parseDelimitedText as O, parseCSV as P, useTheme as Q, DEFAULT_COLORS as R, getSampleGroupMajorPrefix as S, hexToHsl as T, usePluginJobCenter as U, combinePluginJobSources as V, DEFAULT_MOBILE_VIEWPORT_QUERY as W, getPresenceClientId as X, useRuntimeAlignment as Y, usePresenceHeartbeat as Z, useScheduleDrag as _, useReagentSeries as a, useExpansionSet as b, useBioTemplatePresetWorkspace as c, getBioTemplateComponentProps as d, candidateMatchesSearch as et, toBioTemplateComponentPropsByComponent as f, useExperimentSave as g, useTemplateCollection as h, generateDilutionSeries as i, useSortedItems as it, validateImportFile as j, readFileAsText as k, useBioTemplatePackWorkspace as l, useBioTemplateControls as m, DEFAULT_PRESETS as n, useTextSearch as nt, useGroupAssignment as o, useBioTemplateComponents as p, useAppExperiment as q, DEFAULT_UNITS as r, compareSortValues as rt, useRackEditor as s, useProtocolTemplates as t, normalizeSearchQuery as tt, useBioTemplateWorkspace as u, useExperimentSamples as v, deriveShade as w, useSampleGroups as x, useExperimentData as y, useWellPlateEditor as z };
5057
5148
 
5058
- //# sourceMappingURL=useProtocolTemplates-CS_SQl6U.js.map
5149
+ //# sourceMappingURL=useProtocolTemplates-BWhQIWV5.js.map