@morscherlab/mint-sdk 1.1.0-beta.1 → 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.
@@ -6,5 +6,5 @@ import { i as usePlatformContext, n as evaluateCondition, r as useForm, t as use
6
6
  import { t as ANALYSIS_FILE_ARTIFACT_SCHEMA } from "../analysisArtifactTypes-ejqR8MX3.js";
7
7
  import { B as toBioTemplateComponentProps, H as toBioTemplateComponentPropsById, L as toBioTemplateComponentBindings, P as getBioTemplateComponentBindings, R as toBioTemplateComponentBindingsById, l as requireBioTemplateControlSchema, o as getBioTemplateControlSchema } from "../templates-Cttbjbyc.js";
8
8
  import { n as useDebouncedWatch, t as useExperimentSelector } from "../useExperimentSelector-BMjzNcf2.js";
9
- import { A as resolvePluginFrontendBase, C as createPluginResourceClient, D as usePluginConfig, E as useCommandHistory, M as useAuth, 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 usePasskey, 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-F4JFK9K1.js";
9
+ import { A as resolvePluginFrontendBase, C as createPluginResourceClient, D as usePluginConfig, E as useCommandHistory, M as useAuth, 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 usePasskey, 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-B512bWJP.js";
10
10
  export { ANALYSIS_FILE_ARTIFACT_SCHEMA, APP_EXPERIMENT_KEY, ATOMIC_WEIGHTS, AuthenticationRequiredError, 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, combinePluginJobSources, compareSortValues, compareTime, controlValuesToComponentBindings, controlValuesToComponentBindingsById, controlValuesToComponentProps, controlsToFormSchema, controlsToSectionFormSchema, controlsToSectionFormSchemas, controlsToSettingsSchema, controlsToSidebarPanels, controlsToTopBarSettingsConfig, controlsToViewIds, controlsToViewItems, coordinateToWellId, createColumnConditions, createGeneratedAnalysisTransport, 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, resolvePluginFrontendBase, runWellPlateValidation, snapToSlot, toBioTemplateComponentBindings, toBioTemplateComponentBindingsById, toBioTemplateComponentProps, toBioTemplateComponentPropsByComponent, toBioTemplateComponentPropsById, toMinutes, unwrapExperimentDesignData, uploadPluginEndpoint, useAnalysisArtifacts, 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, useGeneratedAnalysis, useGroupAssignment, useJobsStatusTray, useListSelection, useMobileSupportGate, useOptimisticMutation, usePasskey, usePlatformContext, usePluginClient, usePluginConfig, usePluginEventStream, usePluginJobCenter, usePluginJobs, usePluginSettings, useProtocolTemplates, useRackEditor, useReagentSeries, useRequestSyncState, useResourceCrud, useSampleGroups, useScheduleDrag, useSelectionLimit, useSequenceUtils, useSortedItems, useTemplateCollection, useTextSearch, useTheme, useTimeUtils, useToast, useWellPainting, useWellPlateAdapter, useWellPlateEditor, useWellPlateValidation, validateImportFile, wellIdToCoordinate, wellIdsInRectangle };
@@ -37,6 +37,7 @@ export declare function usePlatformContext(options?: PlatformContextOptions): {
37
37
  color?: string | undefined;
38
38
  route_prefix: string;
39
39
  api_prefix: string;
40
+ settings_api?: "plugin" | "platform" | undefined;
40
41
  nav_items?: {
41
42
  id?: string | undefined;
42
43
  label: string;
@@ -96,6 +97,7 @@ export declare function usePlatformContext(options?: PlatformContextOptions): {
96
97
  color?: string | undefined;
97
98
  route_prefix: string;
98
99
  api_prefix: string;
100
+ settings_api?: "plugin" | "platform" | undefined;
99
101
  nav_items?: {
100
102
  id?: string | undefined;
101
103
  label: string;
@@ -155,6 +157,7 @@ export declare function usePlatformContext(options?: PlatformContextOptions): {
155
157
  color?: string | undefined;
156
158
  route_prefix: string;
157
159
  api_prefix: string;
160
+ settings_api?: "plugin" | "platform" | undefined;
158
161
  nav_items?: {
159
162
  id?: string | undefined;
160
163
  label: string;
@@ -614,13 +614,39 @@ function useAsyncBatch(asyncFns) {
614
614
  }
615
615
  //#endregion
616
616
  //#region src/composables/usePluginConfig.ts
617
+ var PLATFORM_OWNED_SETTINGS_KEY$1 = "allowed_experiment_types";
618
+ function cloneConfig(config) {
619
+ return JSON.parse(JSON.stringify(config));
620
+ }
621
+ function hasOwnConfig(config, key) {
622
+ return Object.prototype.hasOwnProperty.call(config, key);
623
+ }
624
+ function changedTopLevelKeys(current, saved) {
625
+ return new Set([...new Set([...Object.keys(current), ...Object.keys(saved)])].filter((key) => JSON.stringify(current[key]) !== JSON.stringify(saved[key])));
626
+ }
627
+ function topLevelConfigPatch(current, keys) {
628
+ return Object.fromEntries([...keys].map((key) => [key, hasOwnConfig(current, key) ? current[key] : null]));
629
+ }
630
+ function platformOwnedConfigPatch(config) {
631
+ return { [PLATFORM_OWNED_SETTINGS_KEY$1]: hasOwnConfig(config, PLATFORM_OWNED_SETTINGS_KEY$1) ? config[PLATFORM_OWNED_SETTINGS_KEY$1] : null };
632
+ }
633
+ function responseConfigPayload(response) {
634
+ return response.config ?? response.settings ?? {};
635
+ }
617
636
  /** Loads, saves, and tracks dirty state for a plugin's persistent configuration via the platform API. */
618
637
  function usePluginConfig(pluginName) {
619
- const api = useApi();
638
+ const injectedContext = getInjectedPlatformContext();
620
639
  const { plugin } = usePlatformContext();
621
- const resolvedName = computed(() => pluginName ?? plugin.value?.name ?? "");
640
+ const platformApi = useApi();
641
+ const pluginApi = useApi({ baseUrl: injectedContext?.plugin?.api_prefix ?? plugin.value?.api_prefix });
642
+ const resolvedName = computed(() => pluginName ?? plugin.value?.name ?? injectedContext?.plugin?.name ?? "");
643
+ const usesPluginSettingsApi = computed(() => {
644
+ const currentPlugin = injectedContext?.plugin ?? plugin.value;
645
+ return currentPlugin?.settings_api === "plugin" && resolvedName.value === currentPlugin.name;
646
+ });
622
647
  const config = ref({});
623
648
  const savedConfig = ref({});
649
+ const revision = ref(null);
624
650
  const request = useRequestSyncState("Plugin config request failed.");
625
651
  const isLoading = ref(false);
626
652
  const isSaving = ref(false);
@@ -635,12 +661,17 @@ function usePluginConfig(pluginName) {
635
661
  if (!name) return;
636
662
  isLoading.value = true;
637
663
  try {
638
- const response = await request.run(() => api.get(`/plugins/${encodeURIComponent(name)}/config`), {
664
+ const response = usesPluginSettingsApi.value ? await request.run(() => pluginApi.get("/settings"), {
665
+ success: "load",
666
+ errorMessage: "Failed to load plugin config"
667
+ }) : await request.run(() => platformApi.get(`/plugins/${encodeURIComponent(name)}/config`), {
639
668
  success: "load",
640
669
  errorMessage: "Failed to load plugin config"
641
670
  });
642
- config.value = { ...response.config };
643
- savedConfig.value = { ...response.config };
671
+ const responseConfig = responseConfigPayload(response);
672
+ config.value = cloneConfig(responseConfig);
673
+ savedConfig.value = cloneConfig(responseConfig);
674
+ revision.value = response.revision ?? null;
644
675
  } catch {} finally {
645
676
  isLoading.value = false;
646
677
  }
@@ -650,12 +681,39 @@ function usePluginConfig(pluginName) {
650
681
  if (!name) return false;
651
682
  isSaving.value = true;
652
683
  try {
653
- const response = await request.run(() => api.patch(`/plugins/${encodeURIComponent(name)}/config`, { config: config.value }), {
654
- success: "save",
655
- errorMessage: "Failed to save plugin config"
656
- });
657
- config.value = { ...response.config };
658
- savedConfig.value = { ...response.config };
684
+ const payload = cloneConfig(config.value);
685
+ let response;
686
+ let nextRevision = revision.value;
687
+ if (usesPluginSettingsApi.value) {
688
+ response = await request.run(() => pluginApi.put("/settings", payload, revision.value ? { headers: { "If-Match": `"${revision.value}"` } } : void 0), {
689
+ success: "save",
690
+ errorMessage: "Failed to save plugin config"
691
+ });
692
+ nextRevision = response.revision ?? nextRevision;
693
+ } else {
694
+ const dirtyKeys = changedTopLevelKeys(payload, savedConfig.value);
695
+ const platformOwnedChanged = dirtyKeys.has(PLATFORM_OWNED_SETTINGS_KEY$1);
696
+ const typedKeys = [...dirtyKeys].filter((key) => key !== PLATFORM_OWNED_SETTINGS_KEY$1);
697
+ if (typedKeys.length) {
698
+ response = await request.run(() => platformApi.patch(`/plugins/${encodeURIComponent(name)}/config`, { config: topLevelConfigPatch(payload, typedKeys) }), {
699
+ success: "save",
700
+ errorMessage: "Failed to save plugin config"
701
+ });
702
+ nextRevision = response.revision ?? nextRevision;
703
+ }
704
+ if (platformOwnedChanged) {
705
+ response = await request.run(() => platformApi.patch(`/plugins/${encodeURIComponent(name)}/config`, { config: platformOwnedConfigPatch(payload) }), {
706
+ success: "save",
707
+ errorMessage: "Failed to save plugin config"
708
+ });
709
+ nextRevision = response.revision ?? nextRevision;
710
+ }
711
+ }
712
+ if (!response) return true;
713
+ const responseConfig = responseConfigPayload(response);
714
+ config.value = cloneConfig(responseConfig);
715
+ savedConfig.value = cloneConfig(responseConfig);
716
+ revision.value = response.revision ?? nextRevision;
659
717
  return true;
660
718
  } catch {
661
719
  return false;
@@ -664,7 +722,7 @@ function usePluginConfig(pluginName) {
664
722
  }
665
723
  }
666
724
  function reset() {
667
- config.value = { ...savedConfig.value };
725
+ config.value = cloneConfig(savedConfig.value);
668
726
  request.clearError();
669
727
  }
670
728
  onMounted(() => {
@@ -1541,8 +1599,12 @@ function usePluginClient(client) {
1541
1599
  function normalizePluginSettingsOptions(options = {}) {
1542
1600
  return typeof options === "string" ? { pluginName: options } : options;
1543
1601
  }
1602
+ var PLATFORM_OWNED_SETTINGS_KEY = "allowed_experiment_types";
1544
1603
  function cloneRecord(value) {
1545
- return { ...value };
1604
+ return JSON.parse(JSON.stringify(value));
1605
+ }
1606
+ function hasOwnSetting(settings, key) {
1607
+ return Object.prototype.hasOwnProperty.call(settings, key);
1546
1608
  }
1547
1609
  function responseSettingsPayload(response) {
1548
1610
  if (!response || typeof response !== "object") return {};
@@ -1551,6 +1613,20 @@ function responseSettingsPayload(response) {
1551
1613
  if (data.settings && typeof data.settings === "object") return cloneRecord(data.settings);
1552
1614
  return {};
1553
1615
  }
1616
+ function responseSettingsRevision(response) {
1617
+ if (!response || typeof response !== "object") return null;
1618
+ const revision = response.revision;
1619
+ return typeof revision === "string" && revision ? revision : null;
1620
+ }
1621
+ function topLevelSettingsPatch(current, keys) {
1622
+ return Object.fromEntries([...keys].map((key) => [key, hasOwnSetting(current, key) ? current[key] : null]));
1623
+ }
1624
+ function changedTopLevelSettingKeys(current, saved) {
1625
+ return new Set([...new Set([...Object.keys(current), ...Object.keys(saved)])].filter((key) => JSON.stringify(current[key]) !== JSON.stringify(saved[key])));
1626
+ }
1627
+ function platformOwnedSettingsPatch(settings) {
1628
+ return { [PLATFORM_OWNED_SETTINGS_KEY]: hasOwnSetting(settings, PLATFORM_OWNED_SETTINGS_KEY) ? settings[PLATFORM_OWNED_SETTINGS_KEY] : null };
1629
+ }
1554
1630
  /** Load, edit, and persist plugin settings from platform config or the plugin-local settings router. */
1555
1631
  function usePluginSettings(options = {}) {
1556
1632
  const resolvedOptions = normalizePluginSettingsOptions(options);
@@ -1558,13 +1634,18 @@ function usePluginSettings(options = {}) {
1558
1634
  const { plugin, isIntegrated } = usePlatformContext();
1559
1635
  const platformApi = useApi({ baseUrl: resolvedOptions.platformApiBaseUrl ?? injectedContext?.platformApiUrl });
1560
1636
  const pluginApi = useApi({ baseUrl: resolvedOptions.apiBaseUrl ?? injectedContext?.plugin?.api_prefix });
1561
- const values = shallowRef({});
1637
+ const values = ref({});
1562
1638
  const savedValues = shallowRef({});
1639
+ const revision = ref(null);
1563
1640
  const request = useRequestSyncState("Plugin settings request failed.");
1564
1641
  const isLoading = ref(false);
1565
1642
  const isSaving = ref(false);
1566
1643
  const resolvedPluginName = computed(() => resolvedOptions.pluginName ?? plugin.value?.name ?? injectedContext?.plugin?.name ?? "");
1567
- const usesPlatformConfig = computed(() => Boolean((isIntegrated.value || injectedContext?.isIntegrated) && resolvedPluginName.value));
1644
+ const usesPluginSettingsApi = computed(() => {
1645
+ const currentPlugin = injectedContext?.plugin ?? plugin.value;
1646
+ return currentPlugin?.settings_api === "plugin" && resolvedPluginName.value === currentPlugin.name;
1647
+ });
1648
+ const usesPlatformConfig = computed(() => Boolean((isIntegrated.value || injectedContext?.isIntegrated) && resolvedPluginName.value && !usesPluginSettingsApi.value));
1568
1649
  const settings = computed(() => values.value);
1569
1650
  const isDirty = computed(() => JSON.stringify(values.value) !== JSON.stringify(savedValues.value));
1570
1651
  const settingsConfig = computed(() => ({
@@ -1576,21 +1657,23 @@ function usePluginSettings(options = {}) {
1576
1657
  function setValues(nextValues) {
1577
1658
  values.value = cloneRecord(nextValues);
1578
1659
  }
1579
- function applyLoaded(nextValues) {
1660
+ function applyLoaded(nextValues, nextRevision) {
1580
1661
  const cloned = cloneRecord(nextValues);
1581
1662
  values.value = cloned;
1582
1663
  savedValues.value = cloneRecord(cloned);
1664
+ revision.value = nextRevision;
1583
1665
  }
1584
1666
  async function load() {
1585
1667
  isLoading.value = true;
1586
1668
  try {
1587
- applyLoaded(responseSettingsPayload(usesPlatformConfig.value ? await request.run(() => platformApi.get(`/plugins/${encodeURIComponent(resolvedPluginName.value)}/config`), {
1669
+ const response = usesPlatformConfig.value ? await request.run(() => platformApi.get(`/plugins/${encodeURIComponent(resolvedPluginName.value)}/config`), {
1588
1670
  success: "load",
1589
1671
  errorMessage: "Failed to load plugin settings"
1590
1672
  }) : await request.run(() => pluginApi.get("/settings"), {
1591
1673
  success: "load",
1592
1674
  errorMessage: "Failed to load plugin settings"
1593
- })));
1675
+ });
1676
+ applyLoaded(responseSettingsPayload(response), responseSettingsRevision(response));
1594
1677
  } catch {} finally {
1595
1678
  isLoading.value = false;
1596
1679
  }
@@ -1600,13 +1683,37 @@ function usePluginSettings(options = {}) {
1600
1683
  isSaving.value = true;
1601
1684
  try {
1602
1685
  const payload = cloneRecord(values.value);
1603
- applyLoaded(responseSettingsPayload(usesPlatformConfig.value ? await request.run(() => platformApi.patch(`/plugins/${encodeURIComponent(resolvedPluginName.value)}/config`, { config: payload }), {
1604
- success: "save",
1605
- errorMessage: "Failed to save plugin settings"
1606
- }) : await request.run(() => pluginApi.put("/settings", payload), {
1607
- success: "save",
1608
- errorMessage: "Failed to save plugin settings"
1609
- })));
1686
+ let response;
1687
+ if (usesPlatformConfig.value) {
1688
+ const dirtyKeys = changedTopLevelSettingKeys(payload, savedValues.value);
1689
+ const platformOwnedChanged = dirtyKeys.has(PLATFORM_OWNED_SETTINGS_KEY);
1690
+ const typedKeys = [...dirtyKeys].filter((key) => key !== PLATFORM_OWNED_SETTINGS_KEY);
1691
+ let nextRevision = revision.value;
1692
+ if (typedKeys.length) {
1693
+ response = await request.run(() => platformApi.patch(`/plugins/${encodeURIComponent(resolvedPluginName.value)}/config`, { config: topLevelSettingsPatch(payload, typedKeys) }), {
1694
+ success: "save",
1695
+ errorMessage: "Failed to save plugin settings"
1696
+ });
1697
+ nextRevision = responseSettingsRevision(response) ?? nextRevision;
1698
+ }
1699
+ if (platformOwnedChanged) {
1700
+ response = await request.run(() => platformApi.patch(`/plugins/${encodeURIComponent(resolvedPluginName.value)}/config`, { config: platformOwnedSettingsPatch(payload) }), {
1701
+ success: "save",
1702
+ errorMessage: "Failed to save plugin settings"
1703
+ });
1704
+ nextRevision = responseSettingsRevision(response) ?? nextRevision;
1705
+ }
1706
+ if (!response) return true;
1707
+ applyLoaded(responseSettingsPayload(response), nextRevision);
1708
+ return true;
1709
+ } else {
1710
+ const ifMatch = revision.value ? { headers: { "If-Match": `"${revision.value}"` } } : void 0;
1711
+ response = await request.run(() => pluginApi.put("/settings", payload, ifMatch), {
1712
+ success: "save",
1713
+ errorMessage: "Failed to save plugin settings"
1714
+ });
1715
+ }
1716
+ applyLoaded(responseSettingsPayload(response), responseSettingsRevision(response) ?? revision.value);
1610
1717
  return true;
1611
1718
  } catch {
1612
1719
  return false;
@@ -1899,4 +2006,4 @@ function useCurrentExperiment(options = {}) {
1899
2006
  //#endregion
1900
2007
  export { resolvePluginFrontendBase as A, createPluginResourceClient as C, usePluginConfig as D, useCommandHistory as E, useAuth as M, 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, usePasskey 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 };
1901
2008
 
1902
- //# sourceMappingURL=composables-F4JFK9K1.js.map
2009
+ //# sourceMappingURL=composables-B512bWJP.js.map