@morscherlab/mint-sdk 1.0.41 → 1.0.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/AppTopBar.navigation.d.ts +0 -1
- package/dist/components/index.js +1 -1
- package/dist/{components-CJ2--4Ex.js → components-BGVwavdd.js} +24 -35
- package/dist/components-BGVwavdd.js.map +1 -0
- package/dist/composables/index.d.ts +1 -1
- package/dist/composables/index.js +2 -2
- package/dist/composables/usePluginClient.d.ts +82 -5
- package/dist/{composables-DrE6OcZZ.js → composables-C_hPF0Gn.js} +255 -8
- package/dist/{composables-DrE6OcZZ.js.map → composables-C_hPF0Gn.js.map} +1 -1
- package/dist/index.js +3 -3
- package/dist/install.js +1 -1
- package/dist/styles.css +17 -14
- package/package.json +1 -1
- package/src/__tests__/components/AppTopBar.navigation.test.ts +3 -5
- package/src/__tests__/components/AppTopBar.test.ts +2 -5
- package/src/__tests__/components/AppTopBarPageSelector.test.ts +22 -0
- package/src/__tests__/components/PluginWorkspaceView.test.ts +18 -0
- package/src/__tests__/composables/usePluginClient.test.ts +129 -3
- package/src/components/AppTopBar.navigation.ts +0 -2
- package/src/components/AppTopBar.story.vue +5 -5
- package/src/components/AppTopBar.vue +0 -1
- package/src/components/PluginWorkspaceView.vue +5 -1
- package/src/components/internal/AppTopBarPageSelectorInternal.vue +0 -1
- package/src/composables/index.ts +6 -0
- package/src/composables/usePluginClient.ts +453 -8
- package/src/styles/components/app-page-selector.css +3 -5
- package/src/styles/components/button.css +8 -5
- package/dist/components-CJ2--4Ex.js.map +0 -1
|
@@ -52,4 +52,4 @@ export { getBioTemplateComponentProps, getBioTemplateComponentBindings, toBioTem
|
|
|
52
52
|
export { useBioTemplateWorkspace, type BioTemplateRendererBinding, type BioTemplateWorkspaceBindings, type BioTemplateWorkspaceTarget, type UseBioTemplateWorkspaceReturn, } from './useBioTemplateWorkspace';
|
|
53
53
|
export { useBioTemplatePresetWorkspace, type BioTemplatePresetWorkspaceBindings, type UseBioTemplatePresetWorkspaceOptions, type UseBioTemplatePresetWorkspaceReturn, } from './useBioTemplatePresetWorkspace';
|
|
54
54
|
export { useBioTemplatePackWorkspace, type UseBioTemplatePackWorkspaceOptions, type UseBioTemplatePackWorkspaceReturn, } from './useBioTemplatePackWorkspace';
|
|
55
|
-
export { buildPluginEndpointUrl, createPluginClient, downloadBlob, downloadPluginEndpoint, getPluginPageSelectorItems, pluginFormDataFromPayload, resolvePluginBaseUrl, uploadPluginEndpoint, usePluginClient, usePluginSettings, useCurrentExperiment, type BuildPluginEndpointUrlOptions, type DownloadBlobOptions, type DownloadPluginEndpointOptions, type PluginContract, type PluginEndpointContract, type PluginEndpointDefinition, type PluginHttpMethod, type PluginNavItemContract, type PluginEndpointRequestOptions, type PluginFormDataPayload, type PluginFormDataValue, type CreatePluginClientOptions, type UseCurrentExperimentOptions, type UseCurrentExperimentReturn, } from './usePluginClient';
|
|
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';
|
|
@@ -6,5 +6,5 @@ import { i as usePlatformContext, n as evaluateCondition, r as useForm, t as use
|
|
|
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
|
|
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, 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 useOptimisticMutation, C as detectDelimiter, D as validateImportFile, E as useFileImport, F as usePasskey, I as useAuth, M as usePluginConfig, N as useAsync, O as createPluginResourceClient, P as useAsyncBatch, S as wellIdsInRectangle, T as readFileAsText, _ 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 useCommandHistory, k as useResourceCrud, 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 parseDelimitedText, x as wellIdToCoordinate, y as coordinateToWellId } from "../composables-C_hPF0Gn.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 };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ComputedRef, Ref } from 'vue';
|
|
2
|
-
import { ExperimentSummary, PageSelectorItem } from '../types';
|
|
2
|
+
import { ExperimentSummary, PageSelectorItem, SettingsModalSchema, TopBarSettingsConfig } from '../types';
|
|
3
3
|
export { buildPluginEndpointUrl, resolvePluginBaseUrl, } from './pluginEndpointBuilder';
|
|
4
4
|
export type PluginHttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete';
|
|
5
5
|
export interface PluginEndpointContract {
|
|
@@ -44,6 +44,10 @@ export interface PluginContract {
|
|
|
44
44
|
analysisResultReaders?: string[];
|
|
45
45
|
capabilities?: Record<string, unknown>;
|
|
46
46
|
};
|
|
47
|
+
settings?: {
|
|
48
|
+
modelName?: string | null;
|
|
49
|
+
schema?: SettingsModalSchema | null;
|
|
50
|
+
};
|
|
47
51
|
endpoints: PluginEndpointContract[];
|
|
48
52
|
hash: string;
|
|
49
53
|
}
|
|
@@ -84,6 +88,78 @@ export interface DownloadBlobOptions {
|
|
|
84
88
|
/** Delay before revoking the object URL after a browser download is triggered. */
|
|
85
89
|
revokeObjectUrlDelayMs?: number;
|
|
86
90
|
}
|
|
91
|
+
export type PluginSettingsValues<TSettings> = Partial<TSettings> & Record<string, unknown>;
|
|
92
|
+
export interface UsePluginSettingsOptions {
|
|
93
|
+
/** Plugin name for platform persistence. Defaults to the integrated platform plugin name. */
|
|
94
|
+
pluginName?: string;
|
|
95
|
+
/** Plugin API base URL for standalone/local settings routes. Generated clients pass pluginApiPrefix. */
|
|
96
|
+
apiBaseUrl?: string;
|
|
97
|
+
/** Platform API base URL for /plugins/{name}/config. Defaults to the injected platform API URL. */
|
|
98
|
+
platformApiBaseUrl?: string;
|
|
99
|
+
/** SettingsModal schema generated from the backend settings_model. */
|
|
100
|
+
schema?: SettingsModalSchema | null;
|
|
101
|
+
/** SettingsModal title when using settingsConfig. */
|
|
102
|
+
title?: string;
|
|
103
|
+
/** Whether SettingsModal should include SDK appearance controls. */
|
|
104
|
+
showAppearance?: boolean;
|
|
105
|
+
/** Load persisted values on component mount. */
|
|
106
|
+
loadOnMount?: boolean;
|
|
107
|
+
}
|
|
108
|
+
export interface UsePluginSettingsReturn<TSettings = Record<string, unknown>> {
|
|
109
|
+
/** Current typed settings values. */
|
|
110
|
+
settings: ComputedRef<PluginSettingsValues<TSettings>>;
|
|
111
|
+
/** Editable settings values for SettingsModal. */
|
|
112
|
+
values: Ref<PluginSettingsValues<TSettings>>;
|
|
113
|
+
/** Alias for values, kept for callers that think in platform config terms. */
|
|
114
|
+
config: Ref<PluginSettingsValues<TSettings>>;
|
|
115
|
+
/** Ready-to-pass AppTopBar/PluginWorkspaceView settingsConfig. */
|
|
116
|
+
settingsConfig: ComputedRef<TopBarSettingsConfig>;
|
|
117
|
+
isLoading: Ref<boolean>;
|
|
118
|
+
isSaving: Ref<boolean>;
|
|
119
|
+
error: Ref<string | null>;
|
|
120
|
+
lastLoadedAt: Ref<Date | null>;
|
|
121
|
+
lastSavedAt: Ref<Date | null>;
|
|
122
|
+
isDirty: ComputedRef<boolean>;
|
|
123
|
+
setValues: (values: Record<string, unknown>) => void;
|
|
124
|
+
load: () => Promise<void>;
|
|
125
|
+
save: (values?: Record<string, unknown>) => Promise<boolean>;
|
|
126
|
+
reset: () => void;
|
|
127
|
+
}
|
|
128
|
+
export interface PluginEventStreamMessage<TData = string> {
|
|
129
|
+
event: string;
|
|
130
|
+
data: TData;
|
|
131
|
+
id?: string;
|
|
132
|
+
retry?: number;
|
|
133
|
+
raw: string;
|
|
134
|
+
}
|
|
135
|
+
export interface PluginEventStreamOptions<TData = string> {
|
|
136
|
+
/** Override the generated or platform-injected API base URL. */
|
|
137
|
+
baseUrl?: string;
|
|
138
|
+
/** Start the stream immediately. Defaults to true. */
|
|
139
|
+
immediate?: boolean;
|
|
140
|
+
/** Reconnect after errors or server closes. Defaults to true. */
|
|
141
|
+
reconnect?: boolean;
|
|
142
|
+
/** Delay before reconnecting after a stream failure or close. Defaults to 2000ms. */
|
|
143
|
+
reconnectDelayMs?: number;
|
|
144
|
+
/** Parse `data:` as JSON before calling onMessage. */
|
|
145
|
+
parseJson?: boolean;
|
|
146
|
+
/** Fetch credentials mode. Defaults to same-origin. */
|
|
147
|
+
credentials?: RequestCredentials;
|
|
148
|
+
/** Extra request headers. Authorization is injected unless already supplied. */
|
|
149
|
+
headers?: Record<string, string>;
|
|
150
|
+
onOpen?: () => void;
|
|
151
|
+
onMessage?: (message: PluginEventStreamMessage<TData>) => void;
|
|
152
|
+
onError?: (error: unknown) => void;
|
|
153
|
+
}
|
|
154
|
+
export interface UsePluginEventStreamReturn<TData = string> {
|
|
155
|
+
isConnected: Ref<boolean>;
|
|
156
|
+
isConnecting: Ref<boolean>;
|
|
157
|
+
error: Ref<string | null>;
|
|
158
|
+
lastMessage: Ref<PluginEventStreamMessage<TData> | null>;
|
|
159
|
+
lastEventId: Ref<string | null>;
|
|
160
|
+
start: () => void;
|
|
161
|
+
stop: () => void;
|
|
162
|
+
}
|
|
87
163
|
export type PluginFormDataValue = string | number | boolean | Blob | null | undefined | Record<string, unknown>;
|
|
88
164
|
export type PluginFormDataPayload = Record<string, PluginFormDataValue | PluginFormDataValue[]>;
|
|
89
165
|
export interface UseCurrentExperimentOptions {
|
|
@@ -126,9 +202,10 @@ export declare function uploadPluginEndpoint<TResponse = unknown>(contract: Plug
|
|
|
126
202
|
export declare function downloadPluginEndpoint(contract: PluginContract, endpoint: PluginEndpointDefinition, payload?: unknown, options?: DownloadPluginEndpointOptions): Promise<Blob>;
|
|
127
203
|
/** Return a generated plugin client from setup code with a stable typed identity. */
|
|
128
204
|
export declare function usePluginClient<TClient>(client: TClient): TClient;
|
|
129
|
-
/**
|
|
130
|
-
export declare function usePluginSettings<TSettings = Record<string, unknown>>():
|
|
131
|
-
|
|
132
|
-
|
|
205
|
+
/** Load, edit, and persist plugin settings from platform config or the plugin-local settings router. */
|
|
206
|
+
export declare function usePluginSettings<TSettings = Record<string, unknown>>(options?: UsePluginSettingsOptions | string): UsePluginSettingsReturn<TSettings>;
|
|
207
|
+
/** Consume a generated plugin SSE endpoint with auth headers, reconnect, and teardown. */
|
|
208
|
+
export declare function usePluginEventStream<TData = string>(contract: PluginContract, endpoint: PluginEndpointDefinition, options?: PluginEventStreamOptions<TData>): UsePluginEventStreamReturn<TData>;
|
|
209
|
+
export declare function usePluginEventStream<TData = string>(contract: PluginContract, endpoint: PluginEndpointDefinition, payload?: unknown, options?: PluginEventStreamOptions<TData>): UsePluginEventStreamReturn<TData>;
|
|
133
210
|
/** Read and optionally load the current platform experiment for integrated plugin views. */
|
|
134
211
|
export declare function useCurrentExperiment<TExperiment = ExperimentSummary>(options?: UseCurrentExperimentOptions): UseCurrentExperimentReturn<TExperiment>;
|
|
@@ -1545,8 +1545,7 @@ function getPluginPageSelectorItems(contract) {
|
|
|
1545
1545
|
id: item.id || pluginPageIdFromPath(item.path, index),
|
|
1546
1546
|
label: item.label,
|
|
1547
1547
|
to: normalizePluginNavPath(item.path),
|
|
1548
|
-
icon: item.icon || pluginIcon || void 0
|
|
1549
|
-
hint: item.description || contract.plugin.name
|
|
1548
|
+
icon: item.icon || pluginIcon || void 0
|
|
1550
1549
|
}));
|
|
1551
1550
|
}
|
|
1552
1551
|
/** Create a typed plugin API client from a generated MINT plugin contract. */
|
|
@@ -1605,10 +1604,258 @@ async function downloadPluginEndpoint(contract, endpoint, payload, options = {})
|
|
|
1605
1604
|
function usePluginClient(client) {
|
|
1606
1605
|
return client;
|
|
1607
1606
|
}
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1607
|
+
function normalizePluginSettingsOptions(options = {}) {
|
|
1608
|
+
return typeof options === "string" ? { pluginName: options } : options;
|
|
1609
|
+
}
|
|
1610
|
+
function cloneRecord(value) {
|
|
1611
|
+
return { ...value };
|
|
1612
|
+
}
|
|
1613
|
+
function responseSettingsPayload(response) {
|
|
1614
|
+
if (!response || typeof response !== "object") return {};
|
|
1615
|
+
const data = response;
|
|
1616
|
+
if (data.config && typeof data.config === "object") return cloneRecord(data.config);
|
|
1617
|
+
if (data.settings && typeof data.settings === "object") return cloneRecord(data.settings);
|
|
1618
|
+
return {};
|
|
1619
|
+
}
|
|
1620
|
+
/** Load, edit, and persist plugin settings from platform config or the plugin-local settings router. */
|
|
1621
|
+
function usePluginSettings(options = {}) {
|
|
1622
|
+
const resolvedOptions = normalizePluginSettingsOptions(options);
|
|
1623
|
+
const injectedContext = getInjectedPlatformContext();
|
|
1624
|
+
const { plugin, isIntegrated } = usePlatformContext();
|
|
1625
|
+
const platformApi = useApi({ baseUrl: resolvedOptions.platformApiBaseUrl ?? injectedContext?.platformApiUrl });
|
|
1626
|
+
const pluginApi = useApi({ baseUrl: resolvedOptions.apiBaseUrl ?? injectedContext?.plugin?.api_prefix });
|
|
1627
|
+
const values = shallowRef({});
|
|
1628
|
+
const savedValues = shallowRef({});
|
|
1629
|
+
const request = useRequestSyncState("Plugin settings request failed.");
|
|
1630
|
+
const isLoading = ref(false);
|
|
1631
|
+
const isSaving = ref(false);
|
|
1632
|
+
const resolvedPluginName = computed(() => resolvedOptions.pluginName ?? plugin.value?.name ?? injectedContext?.plugin?.name ?? "");
|
|
1633
|
+
const usesPlatformConfig = computed(() => Boolean((isIntegrated.value || injectedContext?.isIntegrated) && resolvedPluginName.value));
|
|
1634
|
+
const settings = computed(() => values.value);
|
|
1635
|
+
const isDirty = computed(() => JSON.stringify(values.value) !== JSON.stringify(savedValues.value));
|
|
1636
|
+
const settingsConfig = computed(() => ({
|
|
1637
|
+
title: resolvedOptions.title,
|
|
1638
|
+
schema: resolvedOptions.schema ?? void 0,
|
|
1639
|
+
showAppearance: resolvedOptions.showAppearance,
|
|
1640
|
+
values: values.value
|
|
1641
|
+
}));
|
|
1642
|
+
function setValues(nextValues) {
|
|
1643
|
+
values.value = cloneRecord(nextValues);
|
|
1644
|
+
}
|
|
1645
|
+
function applyLoaded(nextValues) {
|
|
1646
|
+
const cloned = cloneRecord(nextValues);
|
|
1647
|
+
values.value = cloned;
|
|
1648
|
+
savedValues.value = cloneRecord(cloned);
|
|
1649
|
+
}
|
|
1650
|
+
async function load() {
|
|
1651
|
+
isLoading.value = true;
|
|
1652
|
+
try {
|
|
1653
|
+
applyLoaded(responseSettingsPayload(usesPlatformConfig.value ? await request.run(() => platformApi.get(`/plugins/${encodeURIComponent(resolvedPluginName.value)}/config`), {
|
|
1654
|
+
success: "load",
|
|
1655
|
+
errorMessage: "Failed to load plugin settings"
|
|
1656
|
+
}) : await request.run(() => pluginApi.get("/settings"), {
|
|
1657
|
+
success: "load",
|
|
1658
|
+
errorMessage: "Failed to load plugin settings"
|
|
1659
|
+
})));
|
|
1660
|
+
} catch {} finally {
|
|
1661
|
+
isLoading.value = false;
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
async function save(nextValues) {
|
|
1665
|
+
if (nextValues) setValues(nextValues);
|
|
1666
|
+
isSaving.value = true;
|
|
1667
|
+
try {
|
|
1668
|
+
const payload = cloneRecord(values.value);
|
|
1669
|
+
applyLoaded(responseSettingsPayload(usesPlatformConfig.value ? await request.run(() => platformApi.patch(`/plugins/${encodeURIComponent(resolvedPluginName.value)}/config`, { config: payload }), {
|
|
1670
|
+
success: "save",
|
|
1671
|
+
errorMessage: "Failed to save plugin settings"
|
|
1672
|
+
}) : await request.run(() => pluginApi.put("/settings", payload), {
|
|
1673
|
+
success: "save",
|
|
1674
|
+
errorMessage: "Failed to save plugin settings"
|
|
1675
|
+
})));
|
|
1676
|
+
return true;
|
|
1677
|
+
} catch {
|
|
1678
|
+
return false;
|
|
1679
|
+
} finally {
|
|
1680
|
+
isSaving.value = false;
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
function reset() {
|
|
1684
|
+
values.value = cloneRecord(savedValues.value);
|
|
1685
|
+
request.clearError();
|
|
1686
|
+
}
|
|
1687
|
+
onMounted(() => {
|
|
1688
|
+
if (resolvedOptions.loadOnMount !== false) load();
|
|
1689
|
+
});
|
|
1690
|
+
return {
|
|
1691
|
+
settings,
|
|
1692
|
+
values,
|
|
1693
|
+
config: values,
|
|
1694
|
+
settingsConfig,
|
|
1695
|
+
isLoading,
|
|
1696
|
+
isSaving,
|
|
1697
|
+
error: request.error,
|
|
1698
|
+
lastLoadedAt: request.lastLoadedAt,
|
|
1699
|
+
lastSavedAt: request.lastSavedAt,
|
|
1700
|
+
isDirty,
|
|
1701
|
+
setValues,
|
|
1702
|
+
load,
|
|
1703
|
+
save,
|
|
1704
|
+
reset
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
function streamHeaders(options) {
|
|
1708
|
+
const headers = new Headers(options.headers);
|
|
1709
|
+
const authStore = useAuthStore();
|
|
1710
|
+
if (!authStore.isInitialized) authStore.initialize();
|
|
1711
|
+
if (authStore.token && !headers.has("Authorization")) headers.set("Authorization", `Bearer ${authStore.token}`);
|
|
1712
|
+
headers.set("Accept", "text/event-stream");
|
|
1713
|
+
return headers;
|
|
1714
|
+
}
|
|
1715
|
+
function parseSseEvent(block, parseJson) {
|
|
1716
|
+
let event = "message";
|
|
1717
|
+
let id;
|
|
1718
|
+
let retry;
|
|
1719
|
+
const dataLines = [];
|
|
1720
|
+
for (const line of block.split(/\r?\n/)) {
|
|
1721
|
+
if (!line || line.startsWith(":")) continue;
|
|
1722
|
+
const separator = line.indexOf(":");
|
|
1723
|
+
const field = separator === -1 ? line : line.slice(0, separator);
|
|
1724
|
+
const value = separator === -1 ? "" : line.slice(separator + 1).replace(/^ /, "");
|
|
1725
|
+
if (field === "event") event = value || "message";
|
|
1726
|
+
if (field === "data") dataLines.push(value);
|
|
1727
|
+
if (field === "id") id = value;
|
|
1728
|
+
if (field === "retry") {
|
|
1729
|
+
const parsed = Number(value);
|
|
1730
|
+
if (Number.isFinite(parsed)) retry = parsed;
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
if (!dataLines.length && !id && retry === void 0) return null;
|
|
1734
|
+
const data = dataLines.join("\n");
|
|
1735
|
+
return {
|
|
1736
|
+
event,
|
|
1737
|
+
data: parseJson && data ? JSON.parse(data) : data,
|
|
1738
|
+
id,
|
|
1739
|
+
retry,
|
|
1740
|
+
raw: block
|
|
1741
|
+
};
|
|
1742
|
+
}
|
|
1743
|
+
function looksLikeEventStreamOptions(value) {
|
|
1744
|
+
if (!value || typeof value !== "object") return false;
|
|
1745
|
+
const candidate = value;
|
|
1746
|
+
return [
|
|
1747
|
+
"baseUrl",
|
|
1748
|
+
"immediate",
|
|
1749
|
+
"reconnect",
|
|
1750
|
+
"reconnectDelayMs",
|
|
1751
|
+
"parseJson",
|
|
1752
|
+
"credentials",
|
|
1753
|
+
"headers",
|
|
1754
|
+
"onOpen",
|
|
1755
|
+
"onMessage",
|
|
1756
|
+
"onError"
|
|
1757
|
+
].some((key) => key in candidate);
|
|
1758
|
+
}
|
|
1759
|
+
function usePluginEventStream(contract, endpoint, payloadOrOptions, maybeOptions) {
|
|
1760
|
+
const payload = maybeOptions === void 0 && looksLikeEventStreamOptions(payloadOrOptions) ? void 0 : payloadOrOptions;
|
|
1761
|
+
const options = maybeOptions === void 0 && looksLikeEventStreamOptions(payloadOrOptions) ? payloadOrOptions : maybeOptions ?? {};
|
|
1762
|
+
const isConnected = ref(false);
|
|
1763
|
+
const isConnecting = ref(false);
|
|
1764
|
+
const error = ref(null);
|
|
1765
|
+
const lastMessage = shallowRef(null);
|
|
1766
|
+
const lastEventId = ref(null);
|
|
1767
|
+
let controller = null;
|
|
1768
|
+
let reconnectTimer = null;
|
|
1769
|
+
let stopped = true;
|
|
1770
|
+
function clearReconnectTimer() {
|
|
1771
|
+
if (reconnectTimer !== null) {
|
|
1772
|
+
clearTimeout(reconnectTimer);
|
|
1773
|
+
reconnectTimer = null;
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
function scheduleReconnect() {
|
|
1777
|
+
if (stopped || options.reconnect === false) return;
|
|
1778
|
+
clearReconnectTimer();
|
|
1779
|
+
reconnectTimer = setTimeout(() => {
|
|
1780
|
+
connect();
|
|
1781
|
+
}, options.reconnectDelayMs ?? 2e3);
|
|
1782
|
+
}
|
|
1783
|
+
async function connect() {
|
|
1784
|
+
if (endpoint.method !== "get") throw new Error(`[MINT SDK] Plugin event streams must use GET; got ${endpoint.method}`);
|
|
1785
|
+
if (typeof fetch !== "function") throw new Error("[MINT SDK] fetch is required for plugin event streams.");
|
|
1786
|
+
controller?.abort();
|
|
1787
|
+
controller = new AbortController();
|
|
1788
|
+
isConnecting.value = true;
|
|
1789
|
+
error.value = null;
|
|
1790
|
+
try {
|
|
1791
|
+
const url = buildPluginEndpointUrl(contract, endpoint, payload, { baseUrl: options.baseUrl });
|
|
1792
|
+
const response = await fetch(url, {
|
|
1793
|
+
method: "GET",
|
|
1794
|
+
headers: streamHeaders(options),
|
|
1795
|
+
credentials: options.credentials ?? "same-origin",
|
|
1796
|
+
signal: controller.signal
|
|
1797
|
+
});
|
|
1798
|
+
if (!response.ok) throw new Error(`Plugin event stream failed with HTTP ${response.status}`);
|
|
1799
|
+
if (!response.body) throw new Error("Plugin event stream response did not include a readable body.");
|
|
1800
|
+
options.onOpen?.();
|
|
1801
|
+
isConnected.value = true;
|
|
1802
|
+
const reader = response.body.getReader();
|
|
1803
|
+
const decoder = new TextDecoder();
|
|
1804
|
+
let buffer = "";
|
|
1805
|
+
for (;;) {
|
|
1806
|
+
const { value, done } = await reader.read();
|
|
1807
|
+
if (done) break;
|
|
1808
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1809
|
+
let boundary = buffer.search(/\r?\n\r?\n/);
|
|
1810
|
+
while (boundary !== -1) {
|
|
1811
|
+
const block = buffer.slice(0, boundary);
|
|
1812
|
+
buffer = buffer.slice(buffer[boundary] === "\r" ? boundary + 4 : boundary + 2);
|
|
1813
|
+
const message = parseSseEvent(block, options.parseJson === true);
|
|
1814
|
+
if (message) {
|
|
1815
|
+
lastMessage.value = message;
|
|
1816
|
+
if (message.id !== void 0) lastEventId.value = message.id;
|
|
1817
|
+
if (message.retry !== void 0) options.reconnectDelayMs = message.retry;
|
|
1818
|
+
options.onMessage?.(message);
|
|
1819
|
+
}
|
|
1820
|
+
boundary = buffer.search(/\r?\n\r?\n/);
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
} catch (err) {
|
|
1824
|
+
if (!stopped && !(err instanceof DOMException && err.name === "AbortError")) {
|
|
1825
|
+
error.value = err instanceof Error ? err.message : "Plugin event stream failed";
|
|
1826
|
+
options.onError?.(err);
|
|
1827
|
+
}
|
|
1828
|
+
} finally {
|
|
1829
|
+
isConnecting.value = false;
|
|
1830
|
+
isConnected.value = false;
|
|
1831
|
+
scheduleReconnect();
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
function start() {
|
|
1835
|
+
stopped = false;
|
|
1836
|
+
clearReconnectTimer();
|
|
1837
|
+
connect();
|
|
1838
|
+
}
|
|
1839
|
+
function stop() {
|
|
1840
|
+
stopped = true;
|
|
1841
|
+
clearReconnectTimer();
|
|
1842
|
+
controller?.abort();
|
|
1843
|
+
controller = null;
|
|
1844
|
+
isConnecting.value = false;
|
|
1845
|
+
isConnected.value = false;
|
|
1846
|
+
}
|
|
1847
|
+
if (options.immediate !== false && typeof window === "undefined") onMounted(start);
|
|
1848
|
+
onUnmounted(stop);
|
|
1849
|
+
if (options.immediate !== false && typeof window !== "undefined") start();
|
|
1850
|
+
return {
|
|
1851
|
+
isConnected,
|
|
1852
|
+
isConnecting,
|
|
1853
|
+
error,
|
|
1854
|
+
lastMessage,
|
|
1855
|
+
lastEventId,
|
|
1856
|
+
start,
|
|
1857
|
+
stop
|
|
1858
|
+
};
|
|
1612
1859
|
}
|
|
1613
1860
|
/** Read and optionally load the current platform experiment for integrated plugin views. */
|
|
1614
1861
|
function useCurrentExperiment(options = {}) {
|
|
@@ -1668,6 +1915,6 @@ function useCurrentExperiment(options = {}) {
|
|
|
1668
1915
|
};
|
|
1669
1916
|
}
|
|
1670
1917
|
//#endregion
|
|
1671
|
-
export {
|
|
1918
|
+
export { useOptimisticMutation as A, detectDelimiter as C, validateImportFile as D, useFileImport as E, usePasskey as F, useAuth as I, usePluginConfig as M, useAsync as N, createPluginResourceClient as O, useAsyncBatch as P, wellIdsInRectangle as S, readFileAsText 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, useCommandHistory as j, useResourceCrud 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, parseDelimitedText as w, wellIdToCoordinate as x, coordinateToWellId as y };
|
|
1672
1919
|
|
|
1673
|
-
//# sourceMappingURL=composables-
|
|
1920
|
+
//# sourceMappingURL=composables-C_hPF0Gn.js.map
|