@morscherlab/mint-sdk 1.0.51 → 1.0.53

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 (43) hide show
  1. package/dist/__tests__/components/JobsStatusTray.test.d.ts +1 -0
  2. package/dist/__tests__/composables/useJobsStatusTray.test.d.ts +1 -0
  3. package/dist/__tests__/utils/jobs.test.d.ts +1 -0
  4. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +1 -1
  5. package/dist/components/JobsStatusTray.vue.d.ts +26 -0
  6. package/dist/components/index.d.ts +1 -0
  7. package/dist/components/index.js +2 -2
  8. package/dist/{components-C7UFkNIp.js → components-Lk_h_rON.js} +1663 -1339
  9. package/dist/components-Lk_h_rON.js.map +1 -0
  10. package/dist/composables/index.d.ts +1 -0
  11. package/dist/composables/index.js +3 -3
  12. package/dist/composables/useJobsStatusTray.d.ts +43 -0
  13. package/dist/composables/usePluginClient.d.ts +4 -1
  14. package/dist/{composables-CpBhNKHf.js → composables-DyfGpYZw.js} +50 -17
  15. package/dist/{composables-CpBhNKHf.js.map → composables-DyfGpYZw.js.map} +1 -1
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.js +4 -4
  18. package/dist/install.js +1 -1
  19. package/dist/jobs.d.ts +19 -0
  20. package/dist/styles.css +731 -0
  21. package/dist/types/index.d.ts +1 -0
  22. package/dist/types/jobs.d.ts +49 -0
  23. package/dist/{useProtocolTemplates-C8-YlHj1.js → useJobsStatusTray-DAH999_N.js} +250 -2
  24. package/dist/useJobsStatusTray-DAH999_N.js.map +1 -0
  25. package/package.json +1 -1
  26. package/src/__tests__/components/JobsStatusTray.test.ts +141 -0
  27. package/src/__tests__/composables/useJobsStatusTray.test.ts +241 -0
  28. package/src/__tests__/composables/usePluginClient.test.ts +291 -0
  29. package/src/__tests__/utils/jobs.test.ts +95 -0
  30. package/src/components/JobsStatusTray.story.vue +120 -0
  31. package/src/components/JobsStatusTray.vue +385 -0
  32. package/src/components/index.ts +1 -0
  33. package/src/composables/index.ts +8 -0
  34. package/src/composables/useJobsStatusTray.ts +304 -0
  35. package/src/composables/usePluginClient.ts +65 -15
  36. package/src/index.ts +3 -0
  37. package/src/jobs.ts +75 -0
  38. package/src/styles/components/jobs-status-tray.css +420 -0
  39. package/src/styles/index.css +1 -0
  40. package/src/types/index.ts +10 -0
  41. package/src/types/jobs.ts +61 -0
  42. package/dist/components-C7UFkNIp.js.map +0 -1
  43. package/dist/useProtocolTemplates-C8-YlHj1.js.map +0 -1
@@ -53,3 +53,4 @@ export { useBioTemplateWorkspace, type BioTemplateRendererBinding, type BioTempl
53
53
  export { useBioTemplatePresetWorkspace, type BioTemplatePresetWorkspaceBindings, type UseBioTemplatePresetWorkspaceOptions, type UseBioTemplatePresetWorkspaceReturn, } from './useBioTemplatePresetWorkspace';
54
54
  export { useBioTemplatePackWorkspace, type UseBioTemplatePackWorkspaceOptions, type UseBioTemplatePackWorkspaceReturn, } from './useBioTemplatePackWorkspace';
55
55
  export { buildPluginEndpointUrl, createPluginClient, downloadBlob, downloadPluginEndpoint, getPluginPageSelectorItems, pluginFormDataFromPayload, resolvePluginBaseUrl, uploadPluginEndpoint, usePluginClient, usePluginEventStream, usePluginSettings, useCurrentExperiment, type BuildPluginEndpointUrlOptions, type DownloadBlobOptions, type DownloadPluginEndpointOptions, type PluginContract, type PluginEndpointContract, type PluginEndpointDefinition, type PluginHttpMethod, type PluginNavItemContract, type PluginEndpointRequestOptions, type PluginEventStreamMessage, type PluginEventStreamOptions, type PluginFormDataPayload, type PluginFormDataValue, type CreatePluginClientOptions, type UseCurrentExperimentOptions, type UseCurrentExperimentReturn, type UsePluginEventStreamReturn, type UsePluginSettingsOptions, type UsePluginSettingsReturn, } from './usePluginClient';
56
+ export { useJobsStatusTray, type JobsStatusTrayAdapter, type JobsStatusTrayTab, type JobStreamMessage, type UseJobsStatusTrayOptions, type UseJobsStatusTrayReturn, } from './useJobsStatusTray';
@@ -1,10 +1,10 @@
1
1
  import "../BaseSelect-ekgr9fDo.js";
2
2
  import { $ as parseTime, A as useConcentrationUnits, C as controlsToViewIds, D as getTypeDefault, E as getFieldRegistryEntry, F as ATOMIC_WEIGHTS, G as findAvailableSlots, H as addMinutes, I as useChemicalFormula, J as formatTime, K as findNearestTimeSlotIndex, N as useSequenceUtils, Q as isTimeInRange, S as controlsToTopBarSettingsConfig, T as getDefaultControlView, U as compareTime, W as durationMinutes, X as fromMinutes, Y as formatTimeSlot, Z as generateTimeSlots, _ as controlsToFormSchema, a as defineDoseCalculatorControlProps, b as controlsToSettingsSchema, c as defineWellPlateDoseComponentBindings, d as defineControlModel, et as rangesOverlap, h as controlValuesToComponentProps, i as useControlSchema, l as defineWellPlateDoseControlProps, m as controlValuesToComponentBindingsById, n as defineControls, nt as toMinutes, o as defineDoseDesignControlModel, ot as useListSelection, p as controlValuesToComponentBindings, q as formatDuration, r as getControlDefaults, rt as useTimeUtils, s as defineWellPlateControlProps, st as useSelectionLimit, t as defineControlComponentBindings, tt as snapToSlot, u as useControlWorkspace, v as controlsToSectionFormSchema, w as controlsToViewItems, x as controlsToSidebarPanels, y as controlsToSectionFormSchemas } from "../useControlSchema-BZNdalmL.js";
3
3
  import { t as useEventListener } from "../useEventListener-CfVkP9Xz.js";
4
- import { A as readFileAsText, B as useWellPlateEditor, C as useExpansionSet, G as useAppExperiment, H as DEFAULT_MOBILE_VIEWPORT_QUERY, I as extractSampleNamesFromDesignData, J as candidateMatchesSearch, K as useTheme, L as extractSampleOptionsFromDesignData, M as validateImportFile, N as extractSamplesFromDesignData, O as detectDelimiter, P as useAutoGroup, Q as useSortedItems, R as unwrapExperimentDesignData, U as useMobileSupportGate, V as useDoseCalculator, W as APP_EXPERIMENT_KEY, X as useTextSearch, Y as normalizeSearchQuery, Z as compareSortValues, _ as useScheduleDrag, a as useReagentSeries, c as useBioTemplatePresetWorkspace, d as getBioTemplateComponentProps, f as toBioTemplateComponentPropsByComponent, g as useExperimentSave, h as useTemplateCollection, i as generateDilutionSeries, j as useFileImport, k as parseDelimitedText, l as useBioTemplatePackWorkspace, m as useBioTemplateControls, n as DEFAULT_PRESETS, o as useGroupAssignment, p as useBioTemplateComponents, q as useToast, r as DEFAULT_UNITS, s as useRackEditor, t as useProtocolTemplates, u as useBioTemplateWorkspace, v as useExperimentSamples, w as useSampleGroups, y as useExperimentData, z as DEFAULT_COLORS } from "../useProtocolTemplates-C8-YlHj1.js";
4
+ import { $ as useTheme, A as useExpansionSet, B as extractSamplesFromDesignData, C as useExperimentSave, E as useExperimentData, F as detectDelimiter, G as unwrapExperimentDesignData, I as parseDelimitedText, J as useDoseCalculator, K as DEFAULT_COLORS, L as readFileAsText, Q as useAppExperiment, R as useFileImport, S as useTemplateCollection, T as useExperimentSamples, U as extractSampleNamesFromDesignData, V as useAutoGroup, W as extractSampleOptionsFromDesignData, X as useMobileSupportGate, Y as DEFAULT_MOBILE_VIEWPORT_QUERY, Z as APP_EXPERIMENT_KEY, _ as useBioTemplateWorkspace, at as useSortedItems, b as useBioTemplateComponents, c as useProtocolTemplates, d as generateDilutionSeries, et as useToast, f as useReagentSeries, g as useBioTemplatePackWorkspace, h as useBioTemplatePresetWorkspace, it as compareSortValues, j as useSampleGroups, l as DEFAULT_PRESETS, m as useRackEditor, nt as normalizeSearchQuery, p as useGroupAssignment, q as useWellPlateEditor, rt as useTextSearch, t as useJobsStatusTray, tt as candidateMatchesSearch, u as DEFAULT_UNITS, v as getBioTemplateComponentProps, w as useScheduleDrag, x as useBioTemplateControls, y as toBioTemplateComponentPropsByComponent, z as validateImportFile } from "../useJobsStatusTray-DAH999_N.js";
5
5
  import { i as usePlatformContext, n as evaluateCondition, r as useForm, t as useFormBuilder } from "../useFormBuilder-DKekvXRs.js";
6
6
  import { a as SORT_OPTIONS, c as formatExperimentStatus, i as EXPERIMENT_STATUS_VARIANT_MAP, l as getExperimentStatusVariant, n as EXPERIMENT_STATUS_LABELS, o as datePresetToISO, r as EXPERIMENT_STATUS_OPTIONS, s as formatExperimentDate, t as DATE_PRESET_OPTIONS, u as resolveExperimentCode } from "../experiment-utils-Bfa7CwPU.js";
7
7
  import { i as useApi, n as useDebouncedWatch, r as useRequestSyncState, t as useExperimentSelector } from "../useExperimentSelector-DdCy5VNv.js";
8
8
  import { B as toBioTemplateComponentProps, H as toBioTemplateComponentPropsById, L as toBioTemplateComponentBindings, P as getBioTemplateComponentBindings, R as toBioTemplateComponentBindingsById, l as requireBioTemplateControlSchema, o as getBioTemplateControlSchema } from "../templates-CNbPQNID.js";
9
- import { A as usePasskey, C as createPluginResourceClient, D as usePluginConfig, E as useCommandHistory, O as useAsync, S as wellIdsInRectangle, T as useOptimisticMutation, _ as createWellPlateWells, a as pluginFormDataFromPayload, b as useWellPainting, c as usePluginClient, d as buildPluginEndpointUrl, f as resolvePluginBaseUrl, g as createRowConditions, h as createColumnConditions, i as getPluginPageSelectorItems, j as useAuth, k as useAsyncBatch, l as usePluginEventStream, m as useWellPlateValidation, n as downloadBlob, o as uploadPluginEndpoint, p as runWellPlateValidation, r as downloadPluginEndpoint, s as useCurrentExperiment, t as createPluginClient, u as usePluginSettings, v as useWellPlateAdapter, w as useResourceCrud, x as wellIdToCoordinate, y as coordinateToWellId } from "../composables-CpBhNKHf.js";
10
- export { APP_EXPERIMENT_KEY, ATOMIC_WEIGHTS, DATE_PRESET_OPTIONS, DEFAULT_COLORS, DEFAULT_MOBILE_VIEWPORT_QUERY, DEFAULT_PRESETS, DEFAULT_UNITS, EXPERIMENT_STATUS_LABELS, EXPERIMENT_STATUS_OPTIONS, EXPERIMENT_STATUS_VARIANT_MAP, SORT_OPTIONS, addMinutes, buildPluginEndpointUrl, candidateMatchesSearch, compareSortValues, compareTime, controlValuesToComponentBindings, controlValuesToComponentBindingsById, controlValuesToComponentProps, controlsToFormSchema, controlsToSectionFormSchema, controlsToSectionFormSchemas, controlsToSettingsSchema, controlsToSidebarPanels, controlsToTopBarSettingsConfig, controlsToViewIds, controlsToViewItems, coordinateToWellId, createColumnConditions, createPluginClient, createPluginResourceClient, createRowConditions, createWellPlateWells, datePresetToISO, defineControlComponentBindings, defineControlModel, defineControls, defineDoseCalculatorControlProps, defineDoseDesignControlModel, defineWellPlateControlProps, defineWellPlateDoseComponentBindings, defineWellPlateDoseControlProps, detectDelimiter, downloadBlob, downloadPluginEndpoint, durationMinutes, evaluateCondition, extractSampleNamesFromDesignData, extractSampleOptionsFromDesignData, extractSamplesFromDesignData, findAvailableSlots, findNearestTimeSlotIndex, formatDuration, formatExperimentDate, formatExperimentStatus, formatTime, formatTimeSlot, fromMinutes, generateDilutionSeries, generateTimeSlots, getBioTemplateComponentBindings, getBioTemplateComponentProps, getBioTemplateControlSchema, getControlDefaults, getDefaultControlView, getExperimentStatusVariant, getFieldRegistryEntry, getPluginPageSelectorItems, getTypeDefault, isTimeInRange, normalizeSearchQuery, parseDelimitedText, parseTime, pluginFormDataFromPayload, rangesOverlap, readFileAsText, requireBioTemplateControlSchema, resolveExperimentCode, resolvePluginBaseUrl, runWellPlateValidation, snapToSlot, toBioTemplateComponentBindings, toBioTemplateComponentBindingsById, toBioTemplateComponentProps, toBioTemplateComponentPropsByComponent, toBioTemplateComponentPropsById, toMinutes, unwrapExperimentDesignData, uploadPluginEndpoint, useApi, useAppExperiment, useAsync, useAsyncBatch, useAuth, useAutoGroup, useBioTemplateComponents, useBioTemplateControls, useBioTemplatePackWorkspace, useBioTemplatePresetWorkspace, useBioTemplateWorkspace, useChemicalFormula, useCommandHistory, useConcentrationUnits, useControlSchema, useControlWorkspace, useCurrentExperiment, useDebouncedWatch, useDoseCalculator, useEventListener, useExpansionSet, useExperimentData, useExperimentSamples, useExperimentSave, useExperimentSelector, useFileImport, useForm, useFormBuilder, useGroupAssignment, useListSelection, useMobileSupportGate, useOptimisticMutation, usePasskey, usePlatformContext, usePluginClient, usePluginConfig, usePluginEventStream, usePluginSettings, useProtocolTemplates, useRackEditor, useReagentSeries, useRequestSyncState, useResourceCrud, useSampleGroups, useScheduleDrag, useSelectionLimit, useSequenceUtils, useSortedItems, useTemplateCollection, useTextSearch, useTheme, useTimeUtils, useToast, useWellPainting, useWellPlateAdapter, useWellPlateEditor, useWellPlateValidation, validateImportFile, wellIdToCoordinate, wellIdsInRectangle };
9
+ import { A as usePasskey, C as createPluginResourceClient, D as usePluginConfig, E as useCommandHistory, O as useAsync, S as wellIdsInRectangle, T as useOptimisticMutation, _ as createWellPlateWells, a as pluginFormDataFromPayload, b as useWellPainting, c as usePluginClient, d as buildPluginEndpointUrl, f as resolvePluginBaseUrl, g as createRowConditions, h as createColumnConditions, i as getPluginPageSelectorItems, j as useAuth, k as useAsyncBatch, l as usePluginEventStream, m as useWellPlateValidation, n as downloadBlob, o as uploadPluginEndpoint, p as runWellPlateValidation, r as downloadPluginEndpoint, s as useCurrentExperiment, t as createPluginClient, u as usePluginSettings, v as useWellPlateAdapter, w as useResourceCrud, x as wellIdToCoordinate, y as coordinateToWellId } from "../composables-DyfGpYZw.js";
10
+ export { APP_EXPERIMENT_KEY, ATOMIC_WEIGHTS, DATE_PRESET_OPTIONS, DEFAULT_COLORS, DEFAULT_MOBILE_VIEWPORT_QUERY, DEFAULT_PRESETS, DEFAULT_UNITS, EXPERIMENT_STATUS_LABELS, EXPERIMENT_STATUS_OPTIONS, EXPERIMENT_STATUS_VARIANT_MAP, SORT_OPTIONS, addMinutes, buildPluginEndpointUrl, candidateMatchesSearch, compareSortValues, compareTime, controlValuesToComponentBindings, controlValuesToComponentBindingsById, controlValuesToComponentProps, controlsToFormSchema, controlsToSectionFormSchema, controlsToSectionFormSchemas, controlsToSettingsSchema, controlsToSidebarPanels, controlsToTopBarSettingsConfig, controlsToViewIds, controlsToViewItems, coordinateToWellId, createColumnConditions, createPluginClient, createPluginResourceClient, createRowConditions, createWellPlateWells, datePresetToISO, defineControlComponentBindings, defineControlModel, defineControls, defineDoseCalculatorControlProps, defineDoseDesignControlModel, defineWellPlateControlProps, defineWellPlateDoseComponentBindings, defineWellPlateDoseControlProps, detectDelimiter, downloadBlob, downloadPluginEndpoint, durationMinutes, evaluateCondition, extractSampleNamesFromDesignData, extractSampleOptionsFromDesignData, extractSamplesFromDesignData, findAvailableSlots, findNearestTimeSlotIndex, formatDuration, formatExperimentDate, formatExperimentStatus, formatTime, formatTimeSlot, fromMinutes, generateDilutionSeries, generateTimeSlots, getBioTemplateComponentBindings, getBioTemplateComponentProps, getBioTemplateControlSchema, getControlDefaults, getDefaultControlView, getExperimentStatusVariant, getFieldRegistryEntry, getPluginPageSelectorItems, getTypeDefault, isTimeInRange, normalizeSearchQuery, parseDelimitedText, parseTime, pluginFormDataFromPayload, rangesOverlap, readFileAsText, requireBioTemplateControlSchema, resolveExperimentCode, resolvePluginBaseUrl, runWellPlateValidation, snapToSlot, toBioTemplateComponentBindings, toBioTemplateComponentBindingsById, toBioTemplateComponentProps, toBioTemplateComponentPropsByComponent, toBioTemplateComponentPropsById, toMinutes, unwrapExperimentDesignData, uploadPluginEndpoint, useApi, useAppExperiment, useAsync, useAsyncBatch, useAuth, useAutoGroup, useBioTemplateComponents, useBioTemplateControls, useBioTemplatePackWorkspace, useBioTemplatePresetWorkspace, useBioTemplateWorkspace, useChemicalFormula, useCommandHistory, useConcentrationUnits, useControlSchema, useControlWorkspace, useCurrentExperiment, useDebouncedWatch, useDoseCalculator, useEventListener, useExpansionSet, useExperimentData, useExperimentSamples, useExperimentSave, useExperimentSelector, useFileImport, useForm, useFormBuilder, useGroupAssignment, useJobsStatusTray, useListSelection, useMobileSupportGate, useOptimisticMutation, usePasskey, usePlatformContext, usePluginClient, usePluginConfig, usePluginEventStream, usePluginSettings, useProtocolTemplates, useRackEditor, useReagentSeries, useRequestSyncState, useResourceCrud, useSampleGroups, useScheduleDrag, useSelectionLimit, useSequenceUtils, useSortedItems, useTemplateCollection, useTextSearch, useTheme, useTimeUtils, useToast, useWellPainting, useWellPlateAdapter, useWellPlateEditor, useWellPlateValidation, validateImportFile, wellIdToCoordinate, wellIdsInRectangle };
@@ -0,0 +1,43 @@
1
+ import { ComputedRef, MaybeRefOrGetter, Ref, ShallowRef } from 'vue';
2
+ import { JobListPayload, JobState, JobStateInput, JobStreamData } from '../types/jobs';
3
+ import { UsePluginEventStreamReturn } from './usePluginClient';
4
+ export type JobsStatusTrayTab = 'active' | 'history';
5
+ export interface JobsStatusTrayAdapter {
6
+ listJobs?: () => Promise<JobListPayload | readonly JobStateInput[]>;
7
+ cancelJob?: (job: JobState) => Promise<JobStateInput | void>;
8
+ deleteJob?: (job: JobState) => Promise<boolean | void>;
9
+ clearFinishedJobs?: (jobs: readonly JobState[]) => Promise<number | void>;
10
+ }
11
+ export interface UseJobsStatusTrayOptions {
12
+ jobs?: MaybeRefOrGetter<readonly JobStateInput[] | undefined>;
13
+ adapter?: MaybeRefOrGetter<JobsStatusTrayAdapter | undefined>;
14
+ eventStream?: MaybeRefOrGetter<UsePluginEventStreamReturn<JobStreamData> | undefined>;
15
+ loadOnMount?: boolean;
16
+ }
17
+ export interface JobStreamMessage {
18
+ event: string;
19
+ data: JobStreamData;
20
+ }
21
+ export interface UseJobsStatusTrayReturn {
22
+ jobs: ShallowRef<JobState[]>;
23
+ activeJobs: ComputedRef<JobState[]>;
24
+ historyJobs: ComputedRef<JobState[]>;
25
+ deletableJobs: ComputedRef<JobState[]>;
26
+ featuredJob: ComputedRef<JobState | null>;
27
+ isOpen: Ref<boolean>;
28
+ activeTab: Ref<JobsStatusTrayTab>;
29
+ isLoading: Ref<boolean>;
30
+ error: ShallowRef<string | null>;
31
+ pendingJobIds: Ref<Set<string>>;
32
+ open: () => void;
33
+ close: () => void;
34
+ toggle: () => void;
35
+ refresh: () => Promise<boolean>;
36
+ applyStreamMessage: (message: JobStreamMessage) => void;
37
+ cancelJob: (job: JobState) => Promise<boolean>;
38
+ deleteJob: (job: JobState) => Promise<boolean>;
39
+ clearFinished: () => Promise<boolean>;
40
+ isPending: (job: JobState) => boolean;
41
+ }
42
+ /** Headless state and action controller for the standard SDK jobs tray. */
43
+ export declare function useJobsStatusTray(options?: UseJobsStatusTrayOptions): UseJobsStatusTrayReturn;
@@ -10,6 +10,7 @@ export interface PluginEndpointContract {
10
10
  queryParams?: PluginEndpointParamContract[];
11
11
  requestType?: string | null;
12
12
  responseType?: string | null;
13
+ eventStream?: boolean;
13
14
  }
14
15
  export interface PluginEndpointParamContract {
15
16
  name: string;
@@ -57,6 +58,8 @@ export interface PluginEndpointDefinition {
57
58
  pathParams?: PluginEndpointParamDefinition[];
58
59
  queryParams?: PluginEndpointParamDefinition[];
59
60
  hasBody?: boolean;
61
+ eventStream?: boolean;
62
+ responseType?: string | null;
60
63
  }
61
64
  export type PluginEndpointParamDefinition = string | {
62
65
  name: string;
@@ -141,7 +144,7 @@ export interface PluginEventStreamOptions<TData = string> {
141
144
  reconnect?: boolean;
142
145
  /** Delay before reconnecting after a stream failure or close. Defaults to 2000ms. */
143
146
  reconnectDelayMs?: number;
144
- /** Parse `data:` as JSON before calling onMessage. */
147
+ /** Parse `data:` as JSON. Defaults to true for typed endpoints and false otherwise. */
145
148
  parseJson?: boolean;
146
149
  /** Fetch credentials mode. Defaults to same-origin. */
147
150
  credentials?: RequestCredentials;
@@ -1,4 +1,4 @@
1
- import { S as resolveCurrentExperimentId, b as currentExperimentFromContext, x as getInjectedPlatformContext } from "./useProtocolTemplates-C8-YlHj1.js";
1
+ import { D as currentExperimentFromContext, O as getInjectedPlatformContext, k as resolveCurrentExperimentId } from "./useJobsStatusTray-DAH999_N.js";
2
2
  import { r as useSettingsStore, t as useAuthStore } from "./auth-DZAwEeis.js";
3
3
  import { i as usePlatformContext } from "./useFormBuilder-DKekvXRs.js";
4
4
  import { i as useApi, r as useRequestSyncState } from "./useExperimentSelector-DdCy5VNv.js";
@@ -1624,80 +1624,113 @@ function usePluginEventStream(contract, endpoint, payloadOrOptions, maybeOptions
1624
1624
  const error = ref(null);
1625
1625
  const lastMessage = shallowRef(null);
1626
1626
  const lastEventId = ref(null);
1627
+ const responseType = endpoint.responseType?.trim();
1628
+ const parseJson = options.parseJson ?? Boolean(responseType && responseType !== "string");
1627
1629
  let controller = null;
1628
1630
  let reconnectTimer = null;
1629
1631
  let stopped = true;
1632
+ let generation = 0;
1630
1633
  function clearReconnectTimer() {
1631
1634
  if (reconnectTimer !== null) {
1632
1635
  clearTimeout(reconnectTimer);
1633
1636
  reconnectTimer = null;
1634
1637
  }
1635
1638
  }
1636
- function scheduleReconnect() {
1637
- if (stopped || options.reconnect === false) return;
1639
+ function ownsConnection(connectionGeneration, connectionController) {
1640
+ return !stopped && generation === connectionGeneration && controller === connectionController;
1641
+ }
1642
+ function scheduleReconnect(connectionGeneration) {
1643
+ if (stopped || generation !== connectionGeneration || options.reconnect === false) return;
1638
1644
  clearReconnectTimer();
1639
1645
  reconnectTimer = setTimeout(() => {
1640
- connect();
1646
+ reconnectTimer = null;
1647
+ if (stopped || generation !== connectionGeneration) return;
1648
+ connect(connectionGeneration);
1641
1649
  }, options.reconnectDelayMs ?? 2e3);
1642
1650
  }
1643
- async function connect() {
1651
+ async function connect(connectionGeneration) {
1652
+ if (stopped || generation !== connectionGeneration) return;
1644
1653
  if (endpoint.method !== "get") throw new Error(`[MINT SDK] Plugin event streams must use GET; got ${endpoint.method}`);
1645
1654
  if (typeof fetch !== "function") throw new Error("[MINT SDK] fetch is required for plugin event streams.");
1646
1655
  controller?.abort();
1647
- controller = new AbortController();
1656
+ const connectionController = new AbortController();
1657
+ controller = connectionController;
1648
1658
  isConnecting.value = true;
1659
+ if (!ownsConnection(connectionGeneration, connectionController)) return;
1649
1660
  error.value = null;
1661
+ if (!ownsConnection(connectionGeneration, connectionController)) return;
1650
1662
  try {
1651
1663
  const url = buildPluginEndpointUrl(contract, endpoint, payload, { baseUrl: options.baseUrl });
1652
1664
  const response = await fetch(url, {
1653
1665
  method: "GET",
1654
1666
  headers: streamHeaders(options),
1655
1667
  credentials: options.credentials ?? "same-origin",
1656
- signal: controller.signal
1668
+ signal: connectionController.signal
1657
1669
  });
1670
+ if (!ownsConnection(connectionGeneration, connectionController)) return;
1658
1671
  if (!response.ok) throw new Error(`Plugin event stream failed with HTTP ${response.status}`);
1659
1672
  if (!response.body) throw new Error("Plugin event stream response did not include a readable body.");
1660
- options.onOpen?.();
1661
1673
  isConnected.value = true;
1674
+ if (!ownsConnection(connectionGeneration, connectionController)) return;
1675
+ options.onOpen?.();
1676
+ if (!ownsConnection(connectionGeneration, connectionController)) return;
1662
1677
  const reader = response.body.getReader();
1663
1678
  const decoder = new TextDecoder();
1664
1679
  let buffer = "";
1665
1680
  for (;;) {
1666
1681
  const { value, done } = await reader.read();
1682
+ if (!ownsConnection(connectionGeneration, connectionController)) return;
1667
1683
  if (done) break;
1668
1684
  buffer += decoder.decode(value, { stream: true });
1669
1685
  let boundary = buffer.search(/\r?\n\r?\n/);
1670
1686
  while (boundary !== -1) {
1671
1687
  const block = buffer.slice(0, boundary);
1672
1688
  buffer = buffer.slice(buffer[boundary] === "\r" ? boundary + 4 : boundary + 2);
1673
- const message = parseSseEvent(block, options.parseJson === true);
1689
+ const message = parseSseEvent(block, parseJson);
1674
1690
  if (message) {
1675
1691
  lastMessage.value = message;
1676
- if (message.id !== void 0) lastEventId.value = message.id;
1692
+ if (!ownsConnection(connectionGeneration, connectionController)) return;
1693
+ if (message.id !== void 0) {
1694
+ lastEventId.value = message.id;
1695
+ if (!ownsConnection(connectionGeneration, connectionController)) return;
1696
+ }
1677
1697
  if (message.retry !== void 0) options.reconnectDelayMs = message.retry;
1698
+ if (!ownsConnection(connectionGeneration, connectionController)) return;
1678
1699
  options.onMessage?.(message);
1700
+ if (!ownsConnection(connectionGeneration, connectionController)) return;
1679
1701
  }
1680
1702
  boundary = buffer.search(/\r?\n\r?\n/);
1681
1703
  }
1682
1704
  }
1683
1705
  } catch (err) {
1684
- if (!stopped && !(err instanceof DOMException && err.name === "AbortError")) {
1706
+ if (ownsConnection(connectionGeneration, connectionController) && !(err instanceof DOMException && err.name === "AbortError")) {
1685
1707
  error.value = err instanceof Error ? err.message : "Plugin event stream failed";
1686
- options.onError?.(err);
1708
+ if (ownsConnection(connectionGeneration, connectionController)) options.onError?.(err);
1687
1709
  }
1688
1710
  } finally {
1689
- isConnecting.value = false;
1690
- isConnected.value = false;
1691
- scheduleReconnect();
1711
+ if (ownsConnection(connectionGeneration, connectionController)) {
1712
+ isConnecting.value = false;
1713
+ if (ownsConnection(connectionGeneration, connectionController)) {
1714
+ isConnected.value = false;
1715
+ if (ownsConnection(connectionGeneration, connectionController)) {
1716
+ controller = null;
1717
+ scheduleReconnect(connectionGeneration);
1718
+ }
1719
+ }
1720
+ }
1692
1721
  }
1693
1722
  }
1694
1723
  function start() {
1724
+ if (!stopped) return;
1695
1725
  stopped = false;
1696
1726
  clearReconnectTimer();
1697
- connect();
1727
+ generation += 1;
1728
+ connect(generation);
1698
1729
  }
1699
1730
  function stop() {
1731
+ if (stopped) return;
1700
1732
  stopped = true;
1733
+ generation += 1;
1701
1734
  clearReconnectTimer();
1702
1735
  controller?.abort();
1703
1736
  controller = null;
@@ -1777,4 +1810,4 @@ function useCurrentExperiment(options = {}) {
1777
1810
  //#endregion
1778
1811
  export { usePasskey as A, createPluginResourceClient as C, usePluginConfig as D, useCommandHistory as E, useAsync as O, wellIdsInRectangle as S, useOptimisticMutation as T, createWellPlateWells as _, pluginFormDataFromPayload as a, useWellPainting as b, usePluginClient as c, buildPluginEndpointUrl as d, resolvePluginBaseUrl as f, createRowConditions as g, createColumnConditions as h, getPluginPageSelectorItems as i, useAuth as j, useAsyncBatch as k, usePluginEventStream as l, useWellPlateValidation as m, downloadBlob as n, uploadPluginEndpoint as o, runWellPlateValidation as p, downloadPluginEndpoint as r, useCurrentExperiment as s, createPluginClient as t, usePluginSettings as u, useWellPlateAdapter as v, useResourceCrud as w, wellIdToCoordinate as x, coordinateToWellId as y };
1779
1812
 
1780
- //# sourceMappingURL=composables-CpBhNKHf.js.map
1813
+ //# sourceMappingURL=composables-DyfGpYZw.js.map