@morscherlab/mint-sdk 1.3.3 → 1.3.5

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.
@@ -1,7 +1,7 @@
1
1
  import { a as SORT_OPTIONS, c as formatExperimentDate, d as resolveExperimentCode, i as EXPERIMENT_STATUS_VARIANT_MAP, l as formatExperimentStatus, m as useEventListener, n as EXPERIMENT_STATUS_LABELS, o as datePresetToISO, p as useFocusTrap, r as EXPERIMENT_STATUS_OPTIONS, s as experimentIdFromRoute, t as DATE_PRESET_OPTIONS, u as getExperimentStatusVariant } from "../experiment-utils-DTANyVKV.js";
2
- import { $r as snapToSlot, An as getControlDefaults, B as toBioTemplateComponentProps, Bn as getDefaultControlView, Br as addMinutes, Fn as controlsToSettingsSchema, Gn as getFieldRegistryEntry, Gr as formatDuration, H as toBioTemplateComponentPropsById, Hr as durationMinutes, In as controlsToSidebarPanels, Jr as fromMinutes, Kn as getTypeDefault, Kr as formatTime, L as toBioTemplateComponentBindings, Ln as controlsToTopBarSettingsConfig, Mn as controlsToFormSchema, Nn as controlsToSectionFormSchema, On as defineControlComponentBindings, P as getBioTemplateComponentBindings, Pn as controlsToSectionFormSchemas, Qn as useSequenceUtils, Qr as rangesOverlap, R as toBioTemplateComponentBindingsById, Rn as controlsToViewIds, Ur as findAvailableSlots, Vr as compareTime, Wr as findNearestTimeSlotIndex, Xr as isTimeInRange, Yn as useConcentrationUnits, Yr as generateTimeSlots, Zr as parseTime, ai as useListSelection, cr as resolveCurrentPluginId, dr as usePluginJobs, ei as toMinutes, er as ATOMIC_WEIGHTS, jn as useControlSchema, kn as defineControls, l as requireBioTemplateControlSchema, lr as createGeneratedAnalysisTransport, o as getBioTemplateControlSchema, oi as useSelectionLimit, qr as formatTimeSlot, ti as useTimeUtils, tr as useChemicalFormula, ur as useGeneratedAnalysis, xr as useMenuKeyboard, zn as controlsToViewItems } from "../templates-BU1pet28.js";
3
- import { $ as downloadBlob, A as parseDelimitedText, At as useAppExperiment, Bt as useTextSearch, C as useExperimentSamples, Ct as defineControlModel, Dt as controlValuesToComponentProps, E as useSampleGroups, Et as controlValuesToComponentBindingsById, F as useAutoGroup, Ft as usePlatformContext, G as reorderList, Ht as useSortedItems, I as extractSampleNamesFromDesignData, It as useTheme, J as useDoseCalculator, K as useListReorder, L as extractSampleOptionsFromDesignData, Lt as useToast, M as useFileImport, Mt as useRuntimeAlignment, N as validateImportFile, Nt as getPresenceClientId, P as extractSamplesFromDesignData, Pt as usePresenceHeartbeat, Q as createPluginClient, R as unwrapExperimentDesignData, Rt as candidateMatchesSearch, S as useScheduleDrag, St as useControlWorkspace, T as useExpansionSet, Tt as controlValuesToComponentBindings, U as useRackEditor, Ut as useRovingFocus, Vt as compareSortValues, W as REORDER_ANNOUNCER_STYLE, X as useJobsStatusTray, Y as combinePluginJobSources, Z as usePluginJobCenter, _ as toBioTemplateComponentPropsByComponent, _t as defineDoseCalculatorControlProps, a as useExperimentSelector, at as usePluginClient, b as useTemplateCollection, bt as defineWellPlateDoseComponentBindings, c as DEFAULT_PRESETS, ct as buildPluginEndpointUrl, d as useReagentSeries, dt as usePluginWorkspace, et as downloadPluginEndpoint, f as useGroupAssignment, ft as DEFAULT_MOBILE_VIEWPORT_QUERY, g as getBioTemplateComponentProps, gt as useForm, h as useBioTemplateWorkspace, ht as evaluateCondition, i as useAnalysisArtifacts, it as useCurrentExperiment, j as readFileAsText, jt as getInjectedFrontendRevision, k as detectDelimiter, kt as APP_EXPERIMENT_KEY, l as DEFAULT_UNITS, lt as resolvePluginBaseUrl, m as useBioTemplatePackWorkspace, mt as useFormBuilder, nt as pluginFormDataFromPayload, o as useDebouncedWatch, ot as usePluginEventStream, p as useBioTemplatePresetWorkspace, pt as useMobileSupportGate, q as useReorderAnnouncer, rt as uploadPluginEndpoint, s as useProtocolTemplates, st as usePluginSettings, tt as getPluginPageSelectorItems, u as generateDilutionSeries, ut as useRequestSyncState, v as useBioTemplateComponents, vt as defineDoseDesignControlModel, w as useExperimentData, x as useExperimentSave, xt as defineWellPlateDoseControlProps, y as useBioTemplateControls, yt as defineWellPlateControlProps, z as DEFAULT_COLORS, zt as normalizeSearchQuery } from "../validation-CukfLjy-.js";
4
- import { a as useApi, r as AuthenticationRequiredError } from "../experiment-DgllIc3I.js";
2
+ import { $r as snapToSlot, An as getControlDefaults, B as toBioTemplateComponentProps, Bn as getDefaultControlView, Br as addMinutes, Fn as controlsToSettingsSchema, Gn as getFieldRegistryEntry, Gr as formatDuration, H as toBioTemplateComponentPropsById, Hr as durationMinutes, In as controlsToSidebarPanels, Jr as fromMinutes, Kn as getTypeDefault, Kr as formatTime, L as toBioTemplateComponentBindings, Ln as controlsToTopBarSettingsConfig, Mn as controlsToFormSchema, Nn as controlsToSectionFormSchema, On as defineControlComponentBindings, P as getBioTemplateComponentBindings, Pn as controlsToSectionFormSchemas, Qn as useSequenceUtils, Qr as rangesOverlap, R as toBioTemplateComponentBindingsById, Rn as controlsToViewIds, Ur as findAvailableSlots, Vr as compareTime, Wr as findNearestTimeSlotIndex, Xr as isTimeInRange, Yn as useConcentrationUnits, Yr as generateTimeSlots, Zr as parseTime, ai as useListSelection, cr as resolveCurrentPluginId, dr as usePluginJobs, ei as toMinutes, er as ATOMIC_WEIGHTS, jn as useControlSchema, kn as defineControls, l as requireBioTemplateControlSchema, lr as createGeneratedAnalysisTransport, o as getBioTemplateControlSchema, oi as useSelectionLimit, qr as formatTimeSlot, ti as useTimeUtils, tr as useChemicalFormula, ur as useGeneratedAnalysis, xr as useMenuKeyboard, zn as controlsToViewItems } from "../templates-B2DB0MCM.js";
3
+ import { $ as downloadBlob, A as parseDelimitedText, At as useAppExperiment, Bt as useTextSearch, C as useExperimentSamples, Ct as defineControlModel, Dt as controlValuesToComponentProps, E as useSampleGroups, Et as controlValuesToComponentBindingsById, F as useAutoGroup, Ft as usePlatformContext, G as reorderList, Ht as useSortedItems, I as extractSampleNamesFromDesignData, It as useTheme, J as useDoseCalculator, K as useListReorder, L as extractSampleOptionsFromDesignData, Lt as useToast, M as useFileImport, Mt as useRuntimeAlignment, N as validateImportFile, Nt as getPresenceClientId, P as extractSamplesFromDesignData, Pt as usePresenceHeartbeat, Q as createPluginClient, R as unwrapExperimentDesignData, Rt as candidateMatchesSearch, S as useScheduleDrag, St as useControlWorkspace, T as useExpansionSet, Tt as controlValuesToComponentBindings, U as useRackEditor, Ut as useRovingFocus, Vt as compareSortValues, W as REORDER_ANNOUNCER_STYLE, X as useJobsStatusTray, Y as combinePluginJobSources, Z as usePluginJobCenter, _ as toBioTemplateComponentPropsByComponent, _t as defineDoseCalculatorControlProps, a as useExperimentSelector, at as usePluginClient, b as useTemplateCollection, bt as defineWellPlateDoseComponentBindings, c as DEFAULT_PRESETS, ct as buildPluginEndpointUrl, d as useReagentSeries, dt as usePluginWorkspace, et as downloadPluginEndpoint, f as useGroupAssignment, ft as DEFAULT_MOBILE_VIEWPORT_QUERY, g as getBioTemplateComponentProps, gt as useForm, h as useBioTemplateWorkspace, ht as evaluateCondition, i as useAnalysisArtifacts, it as useCurrentExperiment, j as readFileAsText, jt as getInjectedFrontendRevision, k as detectDelimiter, kt as APP_EXPERIMENT_KEY, l as DEFAULT_UNITS, lt as resolvePluginBaseUrl, m as useBioTemplatePackWorkspace, mt as useFormBuilder, nt as pluginFormDataFromPayload, o as useDebouncedWatch, ot as usePluginEventStream, p as useBioTemplatePresetWorkspace, pt as useMobileSupportGate, q as useReorderAnnouncer, rt as uploadPluginEndpoint, s as useProtocolTemplates, st as usePluginSettings, tt as getPluginPageSelectorItems, u as generateDilutionSeries, ut as useRequestSyncState, v as useBioTemplateComponents, vt as defineDoseDesignControlModel, w as useExperimentData, x as useExperimentSave, xt as defineWellPlateDoseControlProps, y as useBioTemplateControls, yt as defineWellPlateControlProps, z as DEFAULT_COLORS, zt as normalizeSearchQuery } from "../validation-C_eH_vIg.js";
4
+ import { a as useApi, r as AuthenticationRequiredError } from "../experiment-D49y_Kro.js";
5
5
  import { t as ANALYSIS_FILE_ARTIFACT_SCHEMA } from "../analysisArtifactTypes-Cy3D0Ipg.js";
6
- import { a as buildAnalysisArtifactTargetQuery, c as resizedLeadingPanelWidth, d as useManualLayoutResize, f as resolvePluginFrontendBase, i as encodePlatformPickerPath, l as resizedTrailingPanelWidth, n as createFilePickerAdapter, o as parseAnalysisArtifactTarget, r as decodePlatformPickerPath, s as useFileBrowser, t as usePlatformFilePickerAdapter, u as resizedVerticalSplitPercent } from "../composables-BbHozlA7.js";
6
+ import { a as buildAnalysisArtifactTargetQuery, c as resizedLeadingPanelWidth, d as useManualLayoutResize, f as resolvePluginFrontendBase, i as encodePlatformPickerPath, l as resizedTrailingPanelWidth, n as createFilePickerAdapter, o as parseAnalysisArtifactTarget, r as decodePlatformPickerPath, s as useFileBrowser, t as usePlatformFilePickerAdapter, u as resizedVerticalSplitPercent } from "../composables-izSQH8Ky.js";
7
7
  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, REORDER_ANNOUNCER_STYLE, SORT_OPTIONS, addMinutes, buildAnalysisArtifactTargetQuery, buildPluginEndpointUrl, candidateMatchesSearch, combinePluginJobSources, compareSortValues, compareTime, controlValuesToComponentBindings, controlValuesToComponentBindingsById, controlValuesToComponentProps, controlsToFormSchema, controlsToSectionFormSchema, controlsToSectionFormSchemas, controlsToSettingsSchema, controlsToSidebarPanels, controlsToTopBarSettingsConfig, controlsToViewIds, controlsToViewItems, createFilePickerAdapter, createGeneratedAnalysisTransport, createPluginClient, datePresetToISO, decodePlatformPickerPath, defineControlComponentBindings, defineControlModel, defineControls, defineDoseCalculatorControlProps, defineDoseDesignControlModel, defineWellPlateControlProps, defineWellPlateDoseComponentBindings, defineWellPlateDoseControlProps, detectDelimiter, downloadBlob, downloadPluginEndpoint, durationMinutes, encodePlatformPickerPath, evaluateCondition, experimentIdFromRoute, extractSampleNamesFromDesignData, extractSampleOptionsFromDesignData, extractSamplesFromDesignData, findAvailableSlots, findNearestTimeSlotIndex, formatDuration, formatExperimentDate, formatExperimentStatus, formatTime, formatTimeSlot, fromMinutes, generateDilutionSeries, generateTimeSlots, getBioTemplateComponentBindings, getBioTemplateComponentProps, getBioTemplateControlSchema, getControlDefaults, getDefaultControlView, getExperimentStatusVariant, getFieldRegistryEntry, getInjectedFrontendRevision, getPluginPageSelectorItems, getPresenceClientId, getTypeDefault, isTimeInRange, normalizeSearchQuery, parseAnalysisArtifactTarget, parseDelimitedText, parseTime, pluginFormDataFromPayload, rangesOverlap, readFileAsText, reorderList, requireBioTemplateControlSchema, resizedLeadingPanelWidth, resizedTrailingPanelWidth, resizedVerticalSplitPercent, resolveCurrentPluginId, resolveExperimentCode, resolvePluginBaseUrl, resolvePluginFrontendBase, snapToSlot, toBioTemplateComponentBindings, toBioTemplateComponentBindingsById, toBioTemplateComponentProps, toBioTemplateComponentPropsByComponent, toBioTemplateComponentPropsById, toMinutes, unwrapExperimentDesignData, uploadPluginEndpoint, useAnalysisArtifacts, useApi, useAppExperiment, useAutoGroup, useBioTemplateComponents, useBioTemplateControls, useBioTemplatePackWorkspace, useBioTemplatePresetWorkspace, useBioTemplateWorkspace, useChemicalFormula, useConcentrationUnits, useControlSchema, useControlWorkspace, useCurrentExperiment, useDebouncedWatch, useDoseCalculator, useEventListener, useExpansionSet, useExperimentData, useExperimentSamples, useExperimentSave, useExperimentSelector, useFileBrowser, useFileImport, useFocusTrap, useForm, useFormBuilder, useGeneratedAnalysis, useGroupAssignment, useJobsStatusTray, useListReorder, useListSelection, useManualLayoutResize, useMenuKeyboard, useMobileSupportGate, usePlatformContext, usePlatformFilePickerAdapter, usePluginClient, usePluginEventStream, usePluginJobCenter, usePluginJobs, usePluginSettings, usePluginWorkspace, usePresenceHeartbeat, useProtocolTemplates, useRackEditor, useReagentSeries, useReorderAnnouncer, useRequestSyncState, useRovingFocus, useRuntimeAlignment, useSampleGroups, useScheduleDrag, useSelectionLimit, useSequenceUtils, useSortedItems, useTemplateCollection, useTextSearch, useTheme, useTimeUtils, useToast, validateImportFile };
@@ -1,6 +1,6 @@
1
- import { ar as getInjectedPlatformContext } from "./templates-BU1pet28.js";
2
- import { n as pickerFormat, ut as useRequestSyncState } from "./validation-CukfLjy-.js";
3
- import { a as useApi } from "./experiment-DgllIc3I.js";
1
+ import { ar as getInjectedPlatformContext } from "./templates-B2DB0MCM.js";
2
+ import { n as pickerFormat, ut as useRequestSyncState } from "./validation-C_eH_vIg.js";
3
+ import { a as useApi } from "./experiment-D49y_Kro.js";
4
4
  import { computed, getCurrentScope, onBeforeUnmount, onScopeDispose, ref, shallowRef, toValue, watch } from "vue";
5
5
  //#region src/composables/pluginFrontendBase.ts
6
6
  function normalizePluginRoutePrefix(prefix) {
@@ -588,4 +588,4 @@ function usePlatformFilePickerAdapter(options = {}) {
588
588
  //#endregion
589
589
  export { buildAnalysisArtifactTargetQuery as a, resizedLeadingPanelWidth as c, useManualLayoutResize as d, resolvePluginFrontendBase as f, encodePlatformPickerPath as i, resizedTrailingPanelWidth as l, createFilePickerAdapter as n, parseAnalysisArtifactTarget as o, decodePlatformPickerPath as r, useFileBrowser as s, usePlatformFilePickerAdapter as t, resizedVerticalSplitPercent as u };
590
590
 
591
- //# sourceMappingURL=composables-BbHozlA7.js.map
591
+ //# sourceMappingURL=composables-izSQH8Ky.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"composables-BbHozlA7.js","names":[],"sources":["../src/composables/pluginFrontendBase.ts","../src/composables/useManualLayoutResize.ts","../src/composables/useFileBrowser.ts","../src/composables/analysisArtifactTarget.ts","../src/composables/createFilePickerAdapter.ts","../src/composables/usePlatformFilePickerAdapter.ts"],"sourcesContent":["import { getInjectedPlatformContext } from './platformContextHelpers'\n\nfunction normalizePluginRoutePrefix(prefix: string | undefined): string | undefined {\n const candidate = prefix?.trim()\n if (!candidate) return undefined\n\n const path = candidate.split('/').filter(Boolean).join('/')\n return path ? `/${path}/` : '/'\n}\n\n/**\n * Resolve the Vue Router base for a plugin frontend.\n *\n * Integrated plugins use the platform-injected mount prefix. Standalone and\n * development builds retain the caller's Vite base unchanged.\n */\nexport function resolvePluginFrontendBase(fallbackBase: string = '/'): string {\n const context = getInjectedPlatformContext()\n if (!context?.isIntegrated) return fallbackBase\n\n return normalizePluginRoutePrefix(context.plugin?.route_prefix) ?? fallbackBase\n}\n","import { computed, onBeforeUnmount, shallowRef, toValue, watch, type MaybeRefOrGetter } from 'vue'\n\nexport interface ResizePanelWidthConstraints {\n minWidth: number\n maxWidth: number\n mainMinWidth?: number\n layoutGap?: number\n}\n\nexport interface ResizePercentConstraints {\n minPercent: number\n maxPercent: number\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value))\n}\n\nfunction availablePanelWidth(\n containerWidth: number,\n constraints: ResizePanelWidthConstraints,\n): number {\n return Math.min(\n constraints.maxWidth,\n containerWidth - (constraints.mainMinWidth ?? 0) - (constraints.layoutGap ?? 0),\n )\n}\n\nexport function resizedLeadingPanelWidth(\n startWidth: number,\n deltaX: number,\n containerWidth: number,\n constraints: ResizePanelWidthConstraints,\n): number {\n return Math.round(clamp(\n startWidth + deltaX,\n constraints.minWidth,\n Math.max(constraints.minWidth, availablePanelWidth(containerWidth, constraints)),\n ))\n}\n\nexport function resizedTrailingPanelWidth(\n startWidth: number,\n deltaX: number,\n containerWidth: number,\n constraints: ResizePanelWidthConstraints,\n): number {\n return resizedLeadingPanelWidth(startWidth, -deltaX, containerWidth, constraints)\n}\n\nexport function resizedVerticalSplitPercent(\n startPercent: number,\n deltaY: number,\n containerHeight: number,\n constraints: ResizePercentConstraints,\n): number {\n if (containerHeight <= 0) return startPercent\n return Math.round(clamp(\n startPercent + (deltaY / containerHeight) * 100,\n constraints.minPercent,\n constraints.maxPercent,\n ))\n}\n\nexport interface ManualLayoutResizeContext<TKind extends string> {\n kind: TKind\n startValue: number\n deltaX: number\n deltaY: number\n containerRect: DOMRectReadOnly\n secondaryRect: DOMRectReadOnly | null\n}\n\nexport interface ManualLayoutResizeTarget<TKind extends string> {\n cursor?: string\n getContainerElement: () => HTMLElement | null\n getSecondaryElement?: () => HTMLElement | null\n getStartValue: () => number\n onResize: (context: ManualLayoutResizeContext<TKind>) => void\n}\n\n/** Coordinates pointer-driven resizing for one or more layout splitters. */\nexport function useManualLayoutResize<TKind extends string>(options: {\n enabled: MaybeRefOrGetter<boolean>\n resolveTarget: (kind: TKind) => ManualLayoutResizeTarget<TKind> | null\n}) {\n const activeResize = shallowRef<{\n kind: TKind\n startClientX: number\n startClientY: number\n startValue: number\n containerRect: DOMRectReadOnly\n secondaryRect: DOMRectReadOnly | null\n cursor: string\n } | null>(null)\n const isResizing = computed(() => activeResize.value !== null)\n let previousBodyCursor = ''\n let previousBodyUserSelect = ''\n\n function stopResize(): void {\n if (!activeResize.value) return\n activeResize.value = null\n window.removeEventListener('pointermove', handlePointerMove)\n window.removeEventListener('pointerup', stopResize)\n window.removeEventListener('pointercancel', stopResize)\n if (typeof document !== 'undefined') {\n document.body.style.cursor = previousBodyCursor\n document.body.style.userSelect = previousBodyUserSelect\n }\n }\n\n function handlePointerMove(event: PointerEvent): void {\n const state = activeResize.value\n if (!state) return\n options.resolveTarget(state.kind)?.onResize({\n kind: state.kind,\n startValue: state.startValue,\n deltaX: event.clientX - state.startClientX,\n deltaY: event.clientY - state.startClientY,\n containerRect: state.containerRect,\n secondaryRect: state.secondaryRect,\n })\n }\n\n function startResize(kind: TKind, event: PointerEvent): void {\n if (!toValue(options.enabled) || event.button !== 0) return\n const target = options.resolveTarget(kind)\n const containerRect = target?.getContainerElement()?.getBoundingClientRect()\n if (!target || !containerRect) return\n\n event.preventDefault()\n if (event.currentTarget instanceof HTMLElement) {\n event.currentTarget.setPointerCapture(event.pointerId)\n }\n const secondary = target.getSecondaryElement?.() ?? null\n activeResize.value = {\n kind,\n startClientX: event.clientX,\n startClientY: event.clientY,\n startValue: target.getStartValue(),\n containerRect,\n secondaryRect: secondary?.getBoundingClientRect() ?? null,\n cursor: target.cursor ?? 'col-resize',\n }\n\n if (typeof document !== 'undefined') {\n previousBodyCursor = document.body.style.cursor\n previousBodyUserSelect = document.body.style.userSelect\n document.body.style.cursor = activeResize.value.cursor\n document.body.style.userSelect = 'none'\n }\n window.addEventListener('pointermove', handlePointerMove)\n window.addEventListener('pointerup', stopResize)\n window.addEventListener('pointercancel', stopResize)\n }\n\n function isResizingKind(kind: TKind): boolean {\n return activeResize.value?.kind === kind\n }\n\n watch(() => toValue(options.enabled), enabled => { if (!enabled) stopResize() })\n onBeforeUnmount(stopResize)\n\n return { activeResize, isResizing, isResizingKind, startResize, stopResize }\n}\n","import {\n computed,\n getCurrentScope,\n onScopeDispose,\n ref,\n shallowRef,\n toValue,\n watch,\n type ComputedRef,\n type MaybeRefOrGetter,\n type Ref,\n} from 'vue'\n\nimport type {\n FileBrowserSortKey,\n FileDirectoryListing,\n FileEntry,\n FilePathCrumb,\n FileSelection,\n ServerMount,\n} from '../types/fileBrowserTypes'\nimport { useApi } from './useApi'\nimport { getInjectedPlatformContext } from './platformContextHelpers'\nimport { useRequestSyncState } from './useRequestSyncState'\n\nexport interface UseFileBrowserOptions {\n /** Extensions the caller cares about; entries are flagged, never removed. */\n typeRules?: MaybeRefOrGetter<string[] | undefined>\n /** Mount to open first; defaults to the first available one. */\n initialMountId?: MaybeRefOrGetter<string | undefined>\n /** Override the platform API base URL (standalone plugin dev). */\n apiBaseUrl?: string\n}\n\nexport interface UseFileBrowserReturn {\n /** Every configured mount, offline ones included. */\n mounts: Ref<ServerMount[]>\n /** Mount the current listing belongs to. */\n mountId: Ref<string>\n /** Mount-relative path of the current listing. */\n path: Ref<string>\n /** Parent path, or null at a mount root. */\n parent: Ref<string | null>\n entries: Ref<FileEntry[]>\n breadcrumbs: Ref<FilePathCrumb[]>\n /** Directory size before any search filter. */\n totalCount: Ref<number | null>\n /** How many entries satisfy the type rules. */\n matchCount: Ref<number | null>\n /** How many rows this listing carries, after search and any cap. */\n shownCount: Ref<number | null>\n /** True when the server capped the listing. */\n truncated: Ref<boolean>\n search: Ref<string>\n sort: Ref<FileBrowserSortKey>\n /** Paths the user has picked, keyed by mount and path. */\n selected: Ref<FileSelection[]>\n isLoading: ComputedRef<boolean>\n error: Ref<string | null>\n /** Load the mount list and open the first available mount. */\n init: () => Promise<void>\n /** Open a directory, or a mount root when path is empty. */\n navigate: (location: { mount_id: string; path: string }) => Promise<void>\n /** Re-read the current directory. */\n refresh: () => Promise<void>\n /** Take the directory currently being viewed as the input. */\n selectCurrentFolder: () => void\n clear: () => void\n}\n\n/**\n * Browses the platform's `/api/filesystem` routes for a custom file UI. For the\n * ready-made dialog, use `FilePicker` with `usePlatformFilePickerAdapter()`.\n *\n * Requires the `filesystem.browse` permission and a deployment that declares\n * mounts; with none configured the routes are absent and `mounts` stays empty.\n */\nexport function useFileBrowser(options: UseFileBrowserOptions = {}): UseFileBrowserReturn {\n const injectedContext = getInjectedPlatformContext()\n const api = useApi({ baseUrl: options.apiBaseUrl ?? injectedContext?.platformApiUrl })\n const sync = useRequestSyncState('Could not read the server mounts.')\n\n const mounts = ref<ServerMount[]>([])\n const mountId = shallowRef('')\n const path = shallowRef('')\n const parent = ref<string | null>(null)\n const entries = ref<FileEntry[]>([])\n const breadcrumbs = ref<FilePathCrumb[]>([])\n const totalCount = ref<number | null>(null)\n const matchCount = ref<number | null>(null)\n const shownCount = ref<number | null>(null)\n const truncated = ref(false)\n const search = shallowRef('')\n const sort = shallowRef<FileBrowserSortKey>('name')\n const selected = ref<FileSelection[]>([])\n\n const mountsLoading = shallowRef(false)\n const listingLoading = shallowRef(false)\n const listingReady = shallowRef(false)\n const isLoading = computed(() => mountsLoading.value || listingLoading.value)\n let listToken = 0\n let mountsToken = 0\n let disposed = false\n let listingController: AbortController | undefined\n let mountsController: AbortController | undefined\n let searchTimer: ReturnType<typeof setTimeout> | undefined\n\n function resetListing(): void {\n listingReady.value = false\n entries.value = []\n breadcrumbs.value = []\n parent.value = null\n totalCount.value = null\n matchCount.value = null\n shownCount.value = null\n truncated.value = false\n }\n\n function typeRuleParams(): string {\n const rules = toValue(options.typeRules) ?? []\n return rules.map((rule) => `&type_rules=${encodeURIComponent(rule)}`).join('')\n }\n\n /** Prefer the API's own `detail` over axios's \"Request failed with status N\". */\n function describe(cause: unknown, fallback: string): string {\n const detail = (cause as { response?: { data?: { detail?: unknown } } })?.response?.data?.detail\n return typeof detail === 'string' && detail ? detail : sync.readErrorMessage(cause, fallback)\n }\n\n function fail(cause: unknown, fallback: string): void {\n sync.setError(new Error(describe(cause, fallback)), fallback)\n }\n\n function statusOf(cause: unknown): number | undefined {\n return (cause as { response?: { status?: number } })?.response?.status\n }\n\n async function loadMounts(): Promise<boolean> {\n const token = ++mountsToken\n mountsController?.abort()\n mountsController = new AbortController()\n mountsLoading.value = true\n try {\n const data = await api.get<{ mounts: ServerMount[] }>('/filesystem/mounts', {\n signal: mountsController.signal,\n })\n if (disposed || token !== mountsToken) return false\n mounts.value = data.mounts ?? []\n sync.clearError()\n sync.markLoaded()\n return true\n } catch (cause) {\n if (disposed || token !== mountsToken) return false\n // The platform only registers these routes once mounts are configured, so\n // a 404 means \"none declared\" — an empty sidebar, not a failure.\n if (statusOf(cause) === 404) {\n mounts.value = []\n sync.clearError()\n sync.markLoaded()\n return true\n }\n mounts.value = []\n fail(cause, 'Could not read the server mounts.')\n return false\n } finally {\n if (token === mountsToken) mountsLoading.value = false\n }\n }\n\n async function navigate(location: { mount_id: string; path: string }, refresh = false): Promise<void> {\n if (disposed || !location.mount_id) return\n clearTimeout(searchTimer)\n const token = ++listToken\n listingController?.abort()\n listingController = new AbortController()\n listingLoading.value = true\n mountId.value = location.mount_id\n path.value = location.path\n resetListing()\n sync.clearError()\n try {\n const query =\n `mount_id=${encodeURIComponent(location.mount_id)}` +\n `&path=${encodeURIComponent(location.path)}` +\n `&sort=${sort.value}` +\n (refresh ? '&refresh=true' : '') +\n (search.value ? `&search=${encodeURIComponent(search.value)}` : '') +\n typeRuleParams()\n const listing = await api.get<FileDirectoryListing>(`/filesystem/browse?${query}`, {\n signal: listingController.signal,\n })\n if (disposed || token !== listToken) return\n mountId.value = listing.mount_id\n path.value = listing.path\n parent.value = listing.parent ?? null\n entries.value = listing.entries ?? []\n breadcrumbs.value = listing.breadcrumbs ?? []\n totalCount.value = listing.total_count ?? null\n matchCount.value = listing.match_count ?? null\n shownCount.value = listing.shown_count ?? listing.entries?.length ?? null\n truncated.value = listing.truncated ?? false\n listingReady.value = true\n sync.clearError()\n sync.markLoaded()\n } catch (cause) {\n if (disposed || token !== listToken) return\n entries.value = []\n fail(cause, 'Could not read that folder.')\n } finally {\n if (token === listToken) listingLoading.value = false\n }\n }\n\n async function init(): Promise<void> {\n if (disposed) return\n const navigationToken = listToken\n if (!(await loadMounts()) || disposed || navigationToken !== listToken) return\n const preferred = toValue(options.initialMountId)\n // An offline mount is not navigable, so a remembered preference for one\n // falls through to the first reachable mount instead of opening on an error.\n const reachable = (mount: ServerMount): boolean => mount.available !== false\n const target =\n mounts.value.find((mount) => mount.id === preferred && reachable(mount)) ??\n mounts.value.find(reachable)\n if (target) await navigate({ mount_id: target.id, path: '' })\n else {\n mountId.value = ''\n path.value = ''\n resetListing()\n }\n }\n\n async function refresh(): Promise<void> {\n if (disposed) return\n if (mountId.value) await navigate({ mount_id: mountId.value, path: path.value }, true)\n else await init()\n }\n\n function selectCurrentFolder(): void {\n if (!mountId.value || !listingReady.value || isLoading.value || sync.error.value) return\n const crumb = breadcrumbs.value[breadcrumbs.value.length - 1]\n const key = JSON.stringify([mountId.value, path.value])\n if (selected.value.some((item) => JSON.stringify([item.mount_id, item.path]) === key)) return\n selected.value = [\n ...selected.value,\n {\n mount_id: mountId.value,\n path: path.value,\n name: crumb?.label ?? path.value,\n kind: 'directory',\n child_count: totalCount.value,\n },\n ]\n }\n\n function clear(): void {\n selected.value = []\n }\n\n // Both controls are server-side, so they only take effect on a re-fetch.\n // Without these watchers the search box and sort button look live but are\n // inert until the user happens to navigate.\n watch(search, () => {\n if (!mountId.value) return\n clearTimeout(searchTimer)\n // Debounced: search changes on every keystroke.\n searchTimer = setTimeout(() => {\n void navigate({ mount_id: mountId.value, path: path.value })\n }, 250)\n })\n\n watch(sort, () => {\n if (mountId.value) void navigate({ mount_id: mountId.value, path: path.value })\n })\n\n watch(\n () => toValue(options.typeRules),\n () => {\n if (mountId.value) void navigate({ mount_id: mountId.value, path: path.value })\n },\n { deep: true },\n )\n\n if (getCurrentScope())\n onScopeDispose(() => {\n disposed = true\n ++listToken\n ++mountsToken\n clearTimeout(searchTimer)\n listingController?.abort()\n mountsController?.abort()\n })\n\n return {\n mounts,\n mountId,\n path,\n parent,\n entries,\n breadcrumbs,\n totalCount,\n matchCount,\n shownCount,\n truncated,\n search,\n sort,\n selected,\n isLoading,\n error: sync.error,\n init,\n navigate,\n refresh,\n selectCurrentFolder,\n clear,\n }\n}\n","import type { AnalysisArtifactTarget } from '../types/analysisArtifactTypes'\n\nconst EXPERIMENT_ID = 'experiment_id'\nconst ARTIFACT_PLUGIN_ID = 'artifact_plugin_id'\nconst ARTIFACT_KEY = 'artifact_key'\nconst ARTIFACT_ID = 'artifact_id'\n\nfunction parsePositiveInteger(value: string | null): number | undefined {\n if (value === null || !/^\\d+$/.test(value)) return undefined\n const parsed = Number(value)\n return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : undefined\n}\n\n/** Build the canonical query used to open one analysis artifact in a plugin. */\nexport function buildAnalysisArtifactTargetQuery(\n target: AnalysisArtifactTarget,\n): URLSearchParams {\n const query = new URLSearchParams()\n query.set(EXPERIMENT_ID, String(target.experimentId))\n query.set(ARTIFACT_PLUGIN_ID, target.artifactPluginId)\n query.set(ARTIFACT_KEY, target.artifactKey)\n if (target.artifactId !== undefined) {\n query.set(ARTIFACT_ID, String(target.artifactId))\n }\n return query\n}\n\n/** Parse a complete canonical target. Incomplete or invalid input is not guessed. */\nexport function parseAnalysisArtifactTarget(\n query: URLSearchParams,\n): AnalysisArtifactTarget | null {\n const experimentId = parsePositiveInteger(query.get(EXPERIMENT_ID))\n const artifactPluginId = query.get(ARTIFACT_PLUGIN_ID)?.trim()\n const artifactKey = query.get(ARTIFACT_KEY)?.trim()\n const rawArtifactId = query.get(ARTIFACT_ID)\n const artifactId = parsePositiveInteger(rawArtifactId)\n\n if (\n experimentId === undefined\n || !artifactPluginId\n || !artifactKey\n || (rawArtifactId !== null && artifactId === undefined)\n ) {\n return null\n }\n\n return {\n experimentId,\n artifactPluginId,\n artifactKey,\n ...(artifactId === undefined ? {} : { artifactId }),\n }\n}\n","import type { PickerAdapter, PickerNode, PickerRequest, PickerSearchResult } from '../types/filePicker'\nimport type {\n FileBrowserLocation,\n FileDirectoryListing,\n FileDirectoryTree,\n FileEntry,\n FileSearchResult,\n ServerMount,\n} from '../types/fileBrowserTypes'\nimport { pickerFormat } from './filePicker/validation'\n\nexport interface FilePickerTransport {\n listMounts(request?: PickerRequest): Promise<ServerMount[]>\n browse(location: FileBrowserLocation, request?: PickerRequest): Promise<FileDirectoryListing>\n /** `GET …/filesystem/tree`: one request fills the cache for the subfolders too. */\n tree?(location: FileBrowserLocation, request?: PickerRequest): Promise<FileDirectoryTree>\n /** `GET …/filesystem/search`: replaces the folder-by-folder search walk. */\n search?(\n location: FileBrowserLocation,\n query: string,\n recursive: boolean,\n request?: PickerRequest,\n ): Promise<FileSearchResult>\n}\nexport interface FilePickerAdapterOptions {\n /** Show this directory's contents directly and prevent navigation outside it. */\n rootLocation?: FileBrowserLocation\n}\n\n/** Opaque picker identity; decode before passing a selection to a mount-scoped API. */\nexport function encodePlatformPickerPath(mountId: string, path = ''): string {\n return JSON.stringify([mountId, path])\n}\nexport function decodePlatformPickerPath(path: string): { mountId: string; path: string } {\n const decoded: unknown = JSON.parse(path)\n if (\n !Array.isArray(decoded) || decoded.length !== 2 ||\n !decoded.every((item) => typeof item === 'string') || !decoded[0]\n ) throw new Error('Invalid platform picker path.')\n return { mountId: decoded[0], path: decoded[1] }\n}\n\nfunction isRelativePath(path: string): boolean {\n return path === '' || (!/^[a-z]:/i.test(path) && !/[\\\\\\x00-\\x1f\\x7f]/.test(path) &&\n path.split('/').every((part) => part !== '' && part !== '.' && part !== '..'))\n}\n\n/** Mount browsing and search shared by platform and generated plugin clients. */\nexport function createFilePickerAdapter(\n transport: FilePickerTransport,\n options: FilePickerAdapterOptions = {},\n): PickerAdapter {\n const root = options.rootLocation && { ...options.rootLocation }\n const mountLabels = new Map<string, string>()\n // Per picker instance: never share permission-sensitive listings across clients.\n const listings = new Map<string, PickerNode[]>()\n let generation = 0\n\n function invalidate(): void {\n generation++\n listings.clear()\n mountLabels.clear()\n }\n\n function validateLocation(location: FileBrowserLocation) {\n if (!location.mount_id || !isRelativePath(location.path) || (root && (\n location.mount_id !== root.mount_id ||\n (root.path !== '' && location.path !== root.path && !location.path.startsWith(`${root.path}/`))\n ))) throw new Error('Invalid file picker location.')\n }\n if (root) validateLocation(root)\n function begin(request?: PickerRequest): number {\n request?.signal?.throwIfAborted()\n if (request?.refresh) invalidate()\n return generation\n }\n function check(request: PickerRequest | undefined, token: number) {\n request?.signal?.throwIfAborted()\n if (token !== generation) throw new DOMException('Cancelled', 'AbortError')\n }\n async function read(path: string, request: PickerRequest | undefined, token: number): Promise<PickerNode[]> {\n check(request, token)\n const decoded = path ? decodePlatformPickerPath(path) : undefined\n const location = decoded ? { mount_id: decoded.mountId, path: decoded.path } : root\n if (location) validateLocation(location)\n const key = location ? encodePlatformPickerPath(location.mount_id, location.path) : ''\n const cached = listings.get(key)\n if (cached) return cached\n let nodes: PickerNode[]\n if (!location) {\n const mounts = await transport.listMounts(request)\n check(request, token)\n nodes = mounts.map((mount) => {\n mountLabels.set(mount.id, mount.label)\n return {\n kind: 'folder', path: encodePlatformPickerPath(mount.id), name: mount.label,\n parentPath: null, parentName: 'Server mounts',\n unavailableReason: mount.available === false ? 'This mount is offline.' : undefined,\n }\n })\n } else if (transport.tree) {\n const tree = await transport.tree(location, request)\n check(request, token)\n for (const listing of tree.listings ?? []) {\n const at = { mount_id: listing.mount_id, path: listing.path }\n validateLocation(at)\n if (at.mount_id !== location.mount_id) throw new Error('Invalid file picker directory entry.')\n // A capped subfolder is read on its own when opened, and reports the cap then.\n if (!listing.truncated) listings.set(encodePlatformPickerPath(at.mount_id, at.path), toNodes(at, listing))\n }\n const cached = listings.get(key)\n if (!cached)\n throw new Error('This directory exceeds the server listing limit. Choose a smaller data directory.')\n return cached\n } else {\n const listing = await transport.browse(location, request)\n check(request, token)\n if (listing.truncated)\n throw new Error('This directory exceeds the server listing limit. Choose a smaller data directory.')\n nodes = toNodes(location, listing)\n }\n listings.set(key, nodes)\n return nodes\n }\n function toNodes(location: FileBrowserLocation, listing: FileDirectoryListing): PickerNode[] {\n return (listing.entries ?? []).map((entry) => {\n if (entry.path !== `${location.path ? `${location.path}/` : ''}${entry.name}`)\n throw new Error('Invalid file picker directory entry.')\n return toNode(location.mount_id, entry)\n })\n }\n /** One entry, parented on the folder its mount-relative path names. */\n function toNode(mountId: string, entry: FileEntry): PickerNode {\n if (!isRelativePath(entry.path) || !entry.name || entry.name.includes('/') ||\n (entry.path !== entry.name && !entry.path.endsWith(`/${entry.name}`)))\n throw new Error('Invalid file picker directory entry.')\n const parent = entry.path.slice(0, Math.max(0, entry.path.length - entry.name.length - 1))\n const atRoot = root && mountId === root.mount_id && parent === root.path\n const common = {\n path: encodePlatformPickerPath(mountId, entry.path),\n parentPath: atRoot ? null : encodePlatformPickerPath(mountId, parent),\n parentName: `${mountLabels.get(mountId) ?? mountId}${parent ? ` / ${parent}` : ''}`,\n name: entry.name, bytes: entry.size_bytes, modifiedAt: entry.modified_at,\n }\n return entry.kind === 'directory'\n // Counts may include only matching formats, not nested directories.\n ? { ...common, kind: 'folder' }\n : { ...common, kind: 'file', format: pickerFormat(entry.name) }\n }\n async function serverSearch(\n query: string,\n scope: { path: string; recursive: boolean },\n request: PickerRequest | undefined,\n ): Promise<PickerNode[] | PickerSearchResult> {\n const token = begin(request)\n const decoded = scope.path ? decodePlatformPickerPath(scope.path) : undefined\n const location = decoded ? { mount_id: decoded.mountId, path: decoded.path } : root!\n validateLocation(location)\n const result = await transport.search!(location, query, scope.recursive, request)\n check(request, token)\n const prefix = location.path ? `${location.path}/` : ''\n const nodes = (result.entries ?? []).map((entry) => {\n // \"This folder\" searches may return direct children only.\n if (!entry.path.startsWith(prefix) || (!scope.recursive && entry.path.slice(prefix.length).includes('/')))\n throw new Error('Invalid file picker directory entry.')\n return toNode(location.mount_id, entry)\n })\n return result.truncated ? { nodes, truncated: true } : nodes\n }\n return {\n mounts: !root,\n mountOf(path) {\n try {\n return encodePlatformPickerPath(decodePlatformPickerPath(path).mountId)\n } catch {\n return undefined // initialPath is caller input; the listing reports a bad one.\n }\n },\n invalidate,\n listRoot: async (request) => read('', request, begin(request)),\n listChildren: async (path, request) => read(path, request, begin(request)),\n async search(query, scope, request) {\n // The mount list itself has no server to search, so it keeps the walk.\n if (transport.search && (scope.path || root)) return serverSearch(query, scope, request)\n const token = begin(request)\n const matches: PickerNode[] = []\n const queue = [scope.path]\n const visited = new Set<string>()\n const needle = query.toLowerCase()\n let currentRequest = request\n while (queue.length) {\n check(request, token)\n const path = queue.shift()!\n if (visited.has(path)) continue\n visited.add(path)\n if (visited.size > 500) return { nodes: matches, truncated: true }\n const nodes = await read(path, currentRequest, token)\n check(request, token)\n if (currentRequest?.refresh) {\n currentRequest = { ...currentRequest }\n delete currentRequest.refresh\n }\n for (const node of nodes) {\n if (node.name.toLowerCase().includes(needle)) matches.push(node)\n if (scope.recursive && node.kind === 'folder' && !node.unavailableReason)\n queue.push(node.path)\n }\n if (matches.length > 10000) return { nodes: matches.slice(0, 10000), truncated: true }\n }\n return matches\n },\n }\n}\n","import type { PickerAdapter } from '../types/filePicker'\nimport type {\n FileBrowserLocation,\n FileDirectoryListing,\n FileDirectoryTree,\n FileSearchResult,\n ServerMount,\n} from '../types/fileBrowserTypes'\nimport type { PickerRequest } from '../types/filePicker'\nimport { useApi } from './useApi'\nimport { getInjectedPlatformContext } from './platformContextHelpers'\nimport { createFilePickerAdapter, type FilePickerAdapterOptions } from './createFilePickerAdapter'\n\nexport { encodePlatformPickerPath, decodePlatformPickerPath } from './createFilePickerAdapter'\n\nfunction query(location: FileBrowserLocation, request?: PickerRequest): URLSearchParams {\n const params = new URLSearchParams({ mount_id: location.mount_id, path: location.path })\n if (request?.refresh) params.set('refresh', 'true')\n return params\n}\n\n/** Adapt the authenticated MINT mount API without exposing absolute host paths. */\nexport function usePlatformFilePickerAdapter(\n options: FilePickerAdapterOptions & { apiBaseUrl?: string } = {},\n): PickerAdapter {\n const api = useApi({\n baseUrl: options.apiBaseUrl ?? getInjectedPlatformContext()?.platformApiUrl,\n })\n return createFilePickerAdapter({\n async listMounts(request) {\n try {\n const url = `/filesystem/mounts${request?.refresh ? '?refresh=true' : ''}`\n const { mounts } = await api.get<{ mounts: ServerMount[] }>(url, {\n signal: request?.signal,\n })\n return mounts\n } catch (cause) {\n if ((cause as { response?: { status?: number } }).response?.status === 404) return []\n throw cause\n }\n },\n browse(location, request) {\n return api.get<FileDirectoryListing>(`/filesystem/browse?${query(location, request)}`, {\n signal: request?.signal,\n })\n },\n tree(location, request) {\n return api.get<FileDirectoryTree>(`/filesystem/tree?${query(location, request)}`, {\n signal: request?.signal,\n })\n },\n search(location, text, recursive, request) {\n const params = query(location, request)\n params.set('q', text)\n params.set('recursive', String(recursive))\n return api.get<FileSearchResult>(`/filesystem/search?${params}`, { signal: request?.signal })\n },\n }, options)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"composables-izSQH8Ky.js","names":[],"sources":["../src/composables/pluginFrontendBase.ts","../src/composables/useManualLayoutResize.ts","../src/composables/useFileBrowser.ts","../src/composables/analysisArtifactTarget.ts","../src/composables/createFilePickerAdapter.ts","../src/composables/usePlatformFilePickerAdapter.ts"],"sourcesContent":["import { getInjectedPlatformContext } from './platformContextHelpers'\n\nfunction normalizePluginRoutePrefix(prefix: string | undefined): string | undefined {\n const candidate = prefix?.trim()\n if (!candidate) return undefined\n\n const path = candidate.split('/').filter(Boolean).join('/')\n return path ? `/${path}/` : '/'\n}\n\n/**\n * Resolve the Vue Router base for a plugin frontend.\n *\n * Integrated plugins use the platform-injected mount prefix. Standalone and\n * development builds retain the caller's Vite base unchanged.\n */\nexport function resolvePluginFrontendBase(fallbackBase: string = '/'): string {\n const context = getInjectedPlatformContext()\n if (!context?.isIntegrated) return fallbackBase\n\n return normalizePluginRoutePrefix(context.plugin?.route_prefix) ?? fallbackBase\n}\n","import { computed, onBeforeUnmount, shallowRef, toValue, watch, type MaybeRefOrGetter } from 'vue'\n\nexport interface ResizePanelWidthConstraints {\n minWidth: number\n maxWidth: number\n mainMinWidth?: number\n layoutGap?: number\n}\n\nexport interface ResizePercentConstraints {\n minPercent: number\n maxPercent: number\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value))\n}\n\nfunction availablePanelWidth(\n containerWidth: number,\n constraints: ResizePanelWidthConstraints,\n): number {\n return Math.min(\n constraints.maxWidth,\n containerWidth - (constraints.mainMinWidth ?? 0) - (constraints.layoutGap ?? 0),\n )\n}\n\nexport function resizedLeadingPanelWidth(\n startWidth: number,\n deltaX: number,\n containerWidth: number,\n constraints: ResizePanelWidthConstraints,\n): number {\n return Math.round(clamp(\n startWidth + deltaX,\n constraints.minWidth,\n Math.max(constraints.minWidth, availablePanelWidth(containerWidth, constraints)),\n ))\n}\n\nexport function resizedTrailingPanelWidth(\n startWidth: number,\n deltaX: number,\n containerWidth: number,\n constraints: ResizePanelWidthConstraints,\n): number {\n return resizedLeadingPanelWidth(startWidth, -deltaX, containerWidth, constraints)\n}\n\nexport function resizedVerticalSplitPercent(\n startPercent: number,\n deltaY: number,\n containerHeight: number,\n constraints: ResizePercentConstraints,\n): number {\n if (containerHeight <= 0) return startPercent\n return Math.round(clamp(\n startPercent + (deltaY / containerHeight) * 100,\n constraints.minPercent,\n constraints.maxPercent,\n ))\n}\n\nexport interface ManualLayoutResizeContext<TKind extends string> {\n kind: TKind\n startValue: number\n deltaX: number\n deltaY: number\n containerRect: DOMRectReadOnly\n secondaryRect: DOMRectReadOnly | null\n}\n\nexport interface ManualLayoutResizeTarget<TKind extends string> {\n cursor?: string\n getContainerElement: () => HTMLElement | null\n getSecondaryElement?: () => HTMLElement | null\n getStartValue: () => number\n onResize: (context: ManualLayoutResizeContext<TKind>) => void\n}\n\n/** Coordinates pointer-driven resizing for one or more layout splitters. */\nexport function useManualLayoutResize<TKind extends string>(options: {\n enabled: MaybeRefOrGetter<boolean>\n resolveTarget: (kind: TKind) => ManualLayoutResizeTarget<TKind> | null\n}) {\n const activeResize = shallowRef<{\n kind: TKind\n startClientX: number\n startClientY: number\n startValue: number\n containerRect: DOMRectReadOnly\n secondaryRect: DOMRectReadOnly | null\n cursor: string\n } | null>(null)\n const isResizing = computed(() => activeResize.value !== null)\n let previousBodyCursor = ''\n let previousBodyUserSelect = ''\n\n function stopResize(): void {\n if (!activeResize.value) return\n activeResize.value = null\n window.removeEventListener('pointermove', handlePointerMove)\n window.removeEventListener('pointerup', stopResize)\n window.removeEventListener('pointercancel', stopResize)\n if (typeof document !== 'undefined') {\n document.body.style.cursor = previousBodyCursor\n document.body.style.userSelect = previousBodyUserSelect\n }\n }\n\n function handlePointerMove(event: PointerEvent): void {\n const state = activeResize.value\n if (!state) return\n options.resolveTarget(state.kind)?.onResize({\n kind: state.kind,\n startValue: state.startValue,\n deltaX: event.clientX - state.startClientX,\n deltaY: event.clientY - state.startClientY,\n containerRect: state.containerRect,\n secondaryRect: state.secondaryRect,\n })\n }\n\n function startResize(kind: TKind, event: PointerEvent): void {\n if (!toValue(options.enabled) || event.button !== 0) return\n const target = options.resolveTarget(kind)\n const containerRect = target?.getContainerElement()?.getBoundingClientRect()\n if (!target || !containerRect) return\n\n event.preventDefault()\n if (event.currentTarget instanceof HTMLElement) {\n event.currentTarget.setPointerCapture(event.pointerId)\n }\n const secondary = target.getSecondaryElement?.() ?? null\n activeResize.value = {\n kind,\n startClientX: event.clientX,\n startClientY: event.clientY,\n startValue: target.getStartValue(),\n containerRect,\n secondaryRect: secondary?.getBoundingClientRect() ?? null,\n cursor: target.cursor ?? 'col-resize',\n }\n\n if (typeof document !== 'undefined') {\n previousBodyCursor = document.body.style.cursor\n previousBodyUserSelect = document.body.style.userSelect\n document.body.style.cursor = activeResize.value.cursor\n document.body.style.userSelect = 'none'\n }\n window.addEventListener('pointermove', handlePointerMove)\n window.addEventListener('pointerup', stopResize)\n window.addEventListener('pointercancel', stopResize)\n }\n\n function isResizingKind(kind: TKind): boolean {\n return activeResize.value?.kind === kind\n }\n\n watch(() => toValue(options.enabled), enabled => { if (!enabled) stopResize() })\n onBeforeUnmount(stopResize)\n\n return { activeResize, isResizing, isResizingKind, startResize, stopResize }\n}\n","import {\n computed,\n getCurrentScope,\n onScopeDispose,\n ref,\n shallowRef,\n toValue,\n watch,\n type ComputedRef,\n type MaybeRefOrGetter,\n type Ref,\n} from 'vue'\n\nimport type {\n FileBrowserSortKey,\n FileDirectoryListing,\n FileEntry,\n FilePathCrumb,\n FileSelection,\n ServerMount,\n} from '../types/fileBrowserTypes'\nimport { useApi } from './useApi'\nimport { getInjectedPlatformContext } from './platformContextHelpers'\nimport { useRequestSyncState } from './useRequestSyncState'\n\nexport interface UseFileBrowserOptions {\n /** Extensions the caller cares about; entries are flagged, never removed. */\n typeRules?: MaybeRefOrGetter<string[] | undefined>\n /** Mount to open first; defaults to the first available one. */\n initialMountId?: MaybeRefOrGetter<string | undefined>\n /** Override the platform API base URL (standalone plugin dev). */\n apiBaseUrl?: string\n}\n\nexport interface UseFileBrowserReturn {\n /** Every configured mount, offline ones included. */\n mounts: Ref<ServerMount[]>\n /** Mount the current listing belongs to. */\n mountId: Ref<string>\n /** Mount-relative path of the current listing. */\n path: Ref<string>\n /** Parent path, or null at a mount root. */\n parent: Ref<string | null>\n entries: Ref<FileEntry[]>\n breadcrumbs: Ref<FilePathCrumb[]>\n /** Directory size before any search filter. */\n totalCount: Ref<number | null>\n /** How many entries satisfy the type rules. */\n matchCount: Ref<number | null>\n /** How many rows this listing carries, after search and any cap. */\n shownCount: Ref<number | null>\n /** True when the server capped the listing. */\n truncated: Ref<boolean>\n search: Ref<string>\n sort: Ref<FileBrowserSortKey>\n /** Paths the user has picked, keyed by mount and path. */\n selected: Ref<FileSelection[]>\n isLoading: ComputedRef<boolean>\n error: Ref<string | null>\n /** Load the mount list and open the first available mount. */\n init: () => Promise<void>\n /** Open a directory, or a mount root when path is empty. */\n navigate: (location: { mount_id: string; path: string }) => Promise<void>\n /** Re-read the current directory. */\n refresh: () => Promise<void>\n /** Take the directory currently being viewed as the input. */\n selectCurrentFolder: () => void\n clear: () => void\n}\n\n/**\n * Browses the platform's `/api/filesystem` routes for a custom file UI. For the\n * ready-made dialog, use `FilePicker` with `usePlatformFilePickerAdapter()`.\n *\n * Requires the `filesystem.browse` permission and a deployment that declares\n * mounts; with none configured the routes are absent and `mounts` stays empty.\n */\nexport function useFileBrowser(options: UseFileBrowserOptions = {}): UseFileBrowserReturn {\n const injectedContext = getInjectedPlatformContext()\n const api = useApi({ baseUrl: options.apiBaseUrl ?? injectedContext?.platformApiUrl })\n const sync = useRequestSyncState('Could not read the server mounts.')\n\n const mounts = ref<ServerMount[]>([])\n const mountId = shallowRef('')\n const path = shallowRef('')\n const parent = ref<string | null>(null)\n const entries = ref<FileEntry[]>([])\n const breadcrumbs = ref<FilePathCrumb[]>([])\n const totalCount = ref<number | null>(null)\n const matchCount = ref<number | null>(null)\n const shownCount = ref<number | null>(null)\n const truncated = ref(false)\n const search = shallowRef('')\n const sort = shallowRef<FileBrowserSortKey>('name')\n const selected = ref<FileSelection[]>([])\n\n const mountsLoading = shallowRef(false)\n const listingLoading = shallowRef(false)\n const listingReady = shallowRef(false)\n const isLoading = computed(() => mountsLoading.value || listingLoading.value)\n let listToken = 0\n let mountsToken = 0\n let disposed = false\n let listingController: AbortController | undefined\n let mountsController: AbortController | undefined\n let searchTimer: ReturnType<typeof setTimeout> | undefined\n\n function resetListing(): void {\n listingReady.value = false\n entries.value = []\n breadcrumbs.value = []\n parent.value = null\n totalCount.value = null\n matchCount.value = null\n shownCount.value = null\n truncated.value = false\n }\n\n function typeRuleParams(): string {\n const rules = toValue(options.typeRules) ?? []\n return rules.map((rule) => `&type_rules=${encodeURIComponent(rule)}`).join('')\n }\n\n /** Prefer the API's own `detail` over axios's \"Request failed with status N\". */\n function describe(cause: unknown, fallback: string): string {\n const detail = (cause as { response?: { data?: { detail?: unknown } } })?.response?.data?.detail\n return typeof detail === 'string' && detail ? detail : sync.readErrorMessage(cause, fallback)\n }\n\n function fail(cause: unknown, fallback: string): void {\n sync.setError(new Error(describe(cause, fallback)), fallback)\n }\n\n function statusOf(cause: unknown): number | undefined {\n return (cause as { response?: { status?: number } })?.response?.status\n }\n\n async function loadMounts(): Promise<boolean> {\n const token = ++mountsToken\n mountsController?.abort()\n mountsController = new AbortController()\n mountsLoading.value = true\n try {\n const data = await api.get<{ mounts: ServerMount[] }>('/filesystem/mounts', {\n signal: mountsController.signal,\n })\n if (disposed || token !== mountsToken) return false\n mounts.value = data.mounts ?? []\n sync.clearError()\n sync.markLoaded()\n return true\n } catch (cause) {\n if (disposed || token !== mountsToken) return false\n // The platform only registers these routes once mounts are configured, so\n // a 404 means \"none declared\" — an empty sidebar, not a failure.\n if (statusOf(cause) === 404) {\n mounts.value = []\n sync.clearError()\n sync.markLoaded()\n return true\n }\n mounts.value = []\n fail(cause, 'Could not read the server mounts.')\n return false\n } finally {\n if (token === mountsToken) mountsLoading.value = false\n }\n }\n\n async function navigate(location: { mount_id: string; path: string }, refresh = false): Promise<void> {\n if (disposed || !location.mount_id) return\n clearTimeout(searchTimer)\n const token = ++listToken\n listingController?.abort()\n listingController = new AbortController()\n listingLoading.value = true\n mountId.value = location.mount_id\n path.value = location.path\n resetListing()\n sync.clearError()\n try {\n const query =\n `mount_id=${encodeURIComponent(location.mount_id)}` +\n `&path=${encodeURIComponent(location.path)}` +\n `&sort=${sort.value}` +\n (refresh ? '&refresh=true' : '') +\n (search.value ? `&search=${encodeURIComponent(search.value)}` : '') +\n typeRuleParams()\n const listing = await api.get<FileDirectoryListing>(`/filesystem/browse?${query}`, {\n signal: listingController.signal,\n })\n if (disposed || token !== listToken) return\n mountId.value = listing.mount_id\n path.value = listing.path\n parent.value = listing.parent ?? null\n entries.value = listing.entries ?? []\n breadcrumbs.value = listing.breadcrumbs ?? []\n totalCount.value = listing.total_count ?? null\n matchCount.value = listing.match_count ?? null\n shownCount.value = listing.shown_count ?? listing.entries?.length ?? null\n truncated.value = listing.truncated ?? false\n listingReady.value = true\n sync.clearError()\n sync.markLoaded()\n } catch (cause) {\n if (disposed || token !== listToken) return\n entries.value = []\n fail(cause, 'Could not read that folder.')\n } finally {\n if (token === listToken) listingLoading.value = false\n }\n }\n\n async function init(): Promise<void> {\n if (disposed) return\n const navigationToken = listToken\n if (!(await loadMounts()) || disposed || navigationToken !== listToken) return\n const preferred = toValue(options.initialMountId)\n // An offline mount is not navigable, so a remembered preference for one\n // falls through to the first reachable mount instead of opening on an error.\n const reachable = (mount: ServerMount): boolean => mount.available !== false\n const target =\n mounts.value.find((mount) => mount.id === preferred && reachable(mount)) ??\n mounts.value.find(reachable)\n if (target) await navigate({ mount_id: target.id, path: '' })\n else {\n mountId.value = ''\n path.value = ''\n resetListing()\n }\n }\n\n async function refresh(): Promise<void> {\n if (disposed) return\n if (mountId.value) await navigate({ mount_id: mountId.value, path: path.value }, true)\n else await init()\n }\n\n function selectCurrentFolder(): void {\n if (!mountId.value || !listingReady.value || isLoading.value || sync.error.value) return\n const crumb = breadcrumbs.value[breadcrumbs.value.length - 1]\n const key = JSON.stringify([mountId.value, path.value])\n if (selected.value.some((item) => JSON.stringify([item.mount_id, item.path]) === key)) return\n selected.value = [\n ...selected.value,\n {\n mount_id: mountId.value,\n path: path.value,\n name: crumb?.label ?? path.value,\n kind: 'directory',\n child_count: totalCount.value,\n },\n ]\n }\n\n function clear(): void {\n selected.value = []\n }\n\n // Both controls are server-side, so they only take effect on a re-fetch.\n // Without these watchers the search box and sort button look live but are\n // inert until the user happens to navigate.\n watch(search, () => {\n if (!mountId.value) return\n clearTimeout(searchTimer)\n // Debounced: search changes on every keystroke.\n searchTimer = setTimeout(() => {\n void navigate({ mount_id: mountId.value, path: path.value })\n }, 250)\n })\n\n watch(sort, () => {\n if (mountId.value) void navigate({ mount_id: mountId.value, path: path.value })\n })\n\n watch(\n () => toValue(options.typeRules),\n () => {\n if (mountId.value) void navigate({ mount_id: mountId.value, path: path.value })\n },\n { deep: true },\n )\n\n if (getCurrentScope())\n onScopeDispose(() => {\n disposed = true\n ++listToken\n ++mountsToken\n clearTimeout(searchTimer)\n listingController?.abort()\n mountsController?.abort()\n })\n\n return {\n mounts,\n mountId,\n path,\n parent,\n entries,\n breadcrumbs,\n totalCount,\n matchCount,\n shownCount,\n truncated,\n search,\n sort,\n selected,\n isLoading,\n error: sync.error,\n init,\n navigate,\n refresh,\n selectCurrentFolder,\n clear,\n }\n}\n","import type { AnalysisArtifactTarget } from '../types/analysisArtifactTypes'\n\nconst EXPERIMENT_ID = 'experiment_id'\nconst ARTIFACT_PLUGIN_ID = 'artifact_plugin_id'\nconst ARTIFACT_KEY = 'artifact_key'\nconst ARTIFACT_ID = 'artifact_id'\n\nfunction parsePositiveInteger(value: string | null): number | undefined {\n if (value === null || !/^\\d+$/.test(value)) return undefined\n const parsed = Number(value)\n return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : undefined\n}\n\n/** Build the canonical query used to open one analysis artifact in a plugin. */\nexport function buildAnalysisArtifactTargetQuery(\n target: AnalysisArtifactTarget,\n): URLSearchParams {\n const query = new URLSearchParams()\n query.set(EXPERIMENT_ID, String(target.experimentId))\n query.set(ARTIFACT_PLUGIN_ID, target.artifactPluginId)\n query.set(ARTIFACT_KEY, target.artifactKey)\n if (target.artifactId !== undefined) {\n query.set(ARTIFACT_ID, String(target.artifactId))\n }\n return query\n}\n\n/** Parse a complete canonical target. Incomplete or invalid input is not guessed. */\nexport function parseAnalysisArtifactTarget(\n query: URLSearchParams,\n): AnalysisArtifactTarget | null {\n const experimentId = parsePositiveInteger(query.get(EXPERIMENT_ID))\n const artifactPluginId = query.get(ARTIFACT_PLUGIN_ID)?.trim()\n const artifactKey = query.get(ARTIFACT_KEY)?.trim()\n const rawArtifactId = query.get(ARTIFACT_ID)\n const artifactId = parsePositiveInteger(rawArtifactId)\n\n if (\n experimentId === undefined\n || !artifactPluginId\n || !artifactKey\n || (rawArtifactId !== null && artifactId === undefined)\n ) {\n return null\n }\n\n return {\n experimentId,\n artifactPluginId,\n artifactKey,\n ...(artifactId === undefined ? {} : { artifactId }),\n }\n}\n","import type { PickerAdapter, PickerNode, PickerRequest, PickerSearchResult } from '../types/filePicker'\nimport type {\n FileBrowserLocation,\n FileDirectoryListing,\n FileDirectoryTree,\n FileEntry,\n FileSearchResult,\n ServerMount,\n} from '../types/fileBrowserTypes'\nimport { pickerFormat } from './filePicker/validation'\n\nexport interface FilePickerTransport {\n listMounts(request?: PickerRequest): Promise<ServerMount[]>\n browse(location: FileBrowserLocation, request?: PickerRequest): Promise<FileDirectoryListing>\n /** `GET …/filesystem/tree`: one request fills the cache for the subfolders too. */\n tree?(location: FileBrowserLocation, request?: PickerRequest): Promise<FileDirectoryTree>\n /** `GET …/filesystem/search`: replaces the folder-by-folder search walk. */\n search?(\n location: FileBrowserLocation,\n query: string,\n recursive: boolean,\n request?: PickerRequest,\n ): Promise<FileSearchResult>\n}\nexport interface FilePickerAdapterOptions {\n /** Show this directory's contents directly and prevent navigation outside it. */\n rootLocation?: FileBrowserLocation\n}\n\n/** Opaque picker identity; decode before passing a selection to a mount-scoped API. */\nexport function encodePlatformPickerPath(mountId: string, path = ''): string {\n return JSON.stringify([mountId, path])\n}\nexport function decodePlatformPickerPath(path: string): { mountId: string; path: string } {\n const decoded: unknown = JSON.parse(path)\n if (\n !Array.isArray(decoded) || decoded.length !== 2 ||\n !decoded.every((item) => typeof item === 'string') || !decoded[0]\n ) throw new Error('Invalid platform picker path.')\n return { mountId: decoded[0], path: decoded[1] }\n}\n\nfunction isRelativePath(path: string): boolean {\n return path === '' || (!/^[a-z]:/i.test(path) && !/[\\\\\\x00-\\x1f\\x7f]/.test(path) &&\n path.split('/').every((part) => part !== '' && part !== '.' && part !== '..'))\n}\n\n/** Mount browsing and search shared by platform and generated plugin clients. */\nexport function createFilePickerAdapter(\n transport: FilePickerTransport,\n options: FilePickerAdapterOptions = {},\n): PickerAdapter {\n const root = options.rootLocation && { ...options.rootLocation }\n const mountLabels = new Map<string, string>()\n // Per picker instance: never share permission-sensitive listings across clients.\n const listings = new Map<string, PickerNode[]>()\n let generation = 0\n\n function invalidate(): void {\n generation++\n listings.clear()\n mountLabels.clear()\n }\n\n function validateLocation(location: FileBrowserLocation) {\n if (!location.mount_id || !isRelativePath(location.path) || (root && (\n location.mount_id !== root.mount_id ||\n (root.path !== '' && location.path !== root.path && !location.path.startsWith(`${root.path}/`))\n ))) throw new Error('Invalid file picker location.')\n }\n if (root) validateLocation(root)\n function begin(request?: PickerRequest): number {\n request?.signal?.throwIfAborted()\n if (request?.refresh) invalidate()\n return generation\n }\n function check(request: PickerRequest | undefined, token: number) {\n request?.signal?.throwIfAborted()\n if (token !== generation) throw new DOMException('Cancelled', 'AbortError')\n }\n async function read(path: string, request: PickerRequest | undefined, token: number): Promise<PickerNode[]> {\n check(request, token)\n const decoded = path ? decodePlatformPickerPath(path) : undefined\n const location = decoded ? { mount_id: decoded.mountId, path: decoded.path } : root\n if (location) validateLocation(location)\n const key = location ? encodePlatformPickerPath(location.mount_id, location.path) : ''\n const cached = listings.get(key)\n if (cached) return cached\n let nodes: PickerNode[]\n if (!location) {\n const mounts = await transport.listMounts(request)\n check(request, token)\n nodes = mounts.map((mount) => {\n mountLabels.set(mount.id, mount.label)\n return {\n kind: 'folder', path: encodePlatformPickerPath(mount.id), name: mount.label,\n parentPath: null, parentName: 'Server mounts',\n unavailableReason: mount.available === false ? 'This mount is offline.' : undefined,\n }\n })\n } else if (transport.tree) {\n const tree = await transport.tree(location, request)\n check(request, token)\n for (const listing of tree.listings ?? []) {\n const at = { mount_id: listing.mount_id, path: listing.path }\n validateLocation(at)\n if (at.mount_id !== location.mount_id) throw new Error('Invalid file picker directory entry.')\n // A capped subfolder is read on its own when opened, and reports the cap then.\n if (!listing.truncated) listings.set(encodePlatformPickerPath(at.mount_id, at.path), toNodes(at, listing))\n }\n const cached = listings.get(key)\n if (!cached)\n throw new Error('This directory exceeds the server listing limit. Choose a smaller data directory.')\n return cached\n } else {\n const listing = await transport.browse(location, request)\n check(request, token)\n if (listing.truncated)\n throw new Error('This directory exceeds the server listing limit. Choose a smaller data directory.')\n nodes = toNodes(location, listing)\n }\n listings.set(key, nodes)\n return nodes\n }\n function toNodes(location: FileBrowserLocation, listing: FileDirectoryListing): PickerNode[] {\n return (listing.entries ?? []).map((entry) => {\n if (entry.path !== `${location.path ? `${location.path}/` : ''}${entry.name}`)\n throw new Error('Invalid file picker directory entry.')\n return toNode(location.mount_id, entry)\n })\n }\n /** One entry, parented on the folder its mount-relative path names. */\n function toNode(mountId: string, entry: FileEntry): PickerNode {\n if (!isRelativePath(entry.path) || !entry.name || entry.name.includes('/') ||\n (entry.path !== entry.name && !entry.path.endsWith(`/${entry.name}`)))\n throw new Error('Invalid file picker directory entry.')\n const parent = entry.path.slice(0, Math.max(0, entry.path.length - entry.name.length - 1))\n const atRoot = root && mountId === root.mount_id && parent === root.path\n const common = {\n path: encodePlatformPickerPath(mountId, entry.path),\n parentPath: atRoot ? null : encodePlatformPickerPath(mountId, parent),\n parentName: `${mountLabels.get(mountId) ?? mountId}${parent ? ` / ${parent}` : ''}`,\n name: entry.name, bytes: entry.size_bytes, modifiedAt: entry.modified_at,\n }\n return entry.kind === 'directory'\n // Counts may include only matching formats, not nested directories.\n ? { ...common, kind: 'folder' }\n : { ...common, kind: 'file', format: pickerFormat(entry.name) }\n }\n async function serverSearch(\n query: string,\n scope: { path: string; recursive: boolean },\n request: PickerRequest | undefined,\n ): Promise<PickerNode[] | PickerSearchResult> {\n const token = begin(request)\n const decoded = scope.path ? decodePlatformPickerPath(scope.path) : undefined\n const location = decoded ? { mount_id: decoded.mountId, path: decoded.path } : root!\n validateLocation(location)\n const result = await transport.search!(location, query, scope.recursive, request)\n check(request, token)\n const prefix = location.path ? `${location.path}/` : ''\n const nodes = (result.entries ?? []).map((entry) => {\n // \"This folder\" searches may return direct children only.\n if (!entry.path.startsWith(prefix) || (!scope.recursive && entry.path.slice(prefix.length).includes('/')))\n throw new Error('Invalid file picker directory entry.')\n return toNode(location.mount_id, entry)\n })\n return result.truncated ? { nodes, truncated: true } : nodes\n }\n return {\n mounts: !root,\n mountOf(path) {\n try {\n return encodePlatformPickerPath(decodePlatformPickerPath(path).mountId)\n } catch {\n return undefined // initialPath is caller input; the listing reports a bad one.\n }\n },\n invalidate,\n listRoot: async (request) => read('', request, begin(request)),\n listChildren: async (path, request) => read(path, request, begin(request)),\n async search(query, scope, request) {\n // The mount list itself has no server to search, so it keeps the walk.\n if (transport.search && (scope.path || root)) return serverSearch(query, scope, request)\n const token = begin(request)\n const matches: PickerNode[] = []\n const queue = [scope.path]\n const visited = new Set<string>()\n const needle = query.toLowerCase()\n let currentRequest = request\n while (queue.length) {\n check(request, token)\n const path = queue.shift()!\n if (visited.has(path)) continue\n visited.add(path)\n if (visited.size > 500) return { nodes: matches, truncated: true }\n const nodes = await read(path, currentRequest, token)\n check(request, token)\n if (currentRequest?.refresh) {\n currentRequest = { ...currentRequest }\n delete currentRequest.refresh\n }\n for (const node of nodes) {\n if (node.name.toLowerCase().includes(needle)) matches.push(node)\n if (scope.recursive && node.kind === 'folder' && !node.unavailableReason)\n queue.push(node.path)\n }\n if (matches.length > 10000) return { nodes: matches.slice(0, 10000), truncated: true }\n }\n return matches\n },\n }\n}\n","import type { PickerAdapter } from '../types/filePicker'\nimport type {\n FileBrowserLocation,\n FileDirectoryListing,\n FileDirectoryTree,\n FileSearchResult,\n ServerMount,\n} from '../types/fileBrowserTypes'\nimport type { PickerRequest } from '../types/filePicker'\nimport { useApi } from './useApi'\nimport { getInjectedPlatformContext } from './platformContextHelpers'\nimport { createFilePickerAdapter, type FilePickerAdapterOptions } from './createFilePickerAdapter'\n\nexport { encodePlatformPickerPath, decodePlatformPickerPath } from './createFilePickerAdapter'\n\nfunction query(location: FileBrowserLocation, request?: PickerRequest): URLSearchParams {\n const params = new URLSearchParams({ mount_id: location.mount_id, path: location.path })\n if (request?.refresh) params.set('refresh', 'true')\n return params\n}\n\n/** Adapt the authenticated MINT mount API without exposing absolute host paths. */\nexport function usePlatformFilePickerAdapter(\n options: FilePickerAdapterOptions & { apiBaseUrl?: string } = {},\n): PickerAdapter {\n const api = useApi({\n baseUrl: options.apiBaseUrl ?? getInjectedPlatformContext()?.platformApiUrl,\n })\n return createFilePickerAdapter({\n async listMounts(request) {\n try {\n const url = `/filesystem/mounts${request?.refresh ? '?refresh=true' : ''}`\n const { mounts } = await api.get<{ mounts: ServerMount[] }>(url, {\n signal: request?.signal,\n })\n return mounts\n } catch (cause) {\n if ((cause as { response?: { status?: number } }).response?.status === 404) return []\n throw cause\n }\n },\n browse(location, request) {\n return api.get<FileDirectoryListing>(`/filesystem/browse?${query(location, request)}`, {\n signal: request?.signal,\n })\n },\n tree(location, request) {\n return api.get<FileDirectoryTree>(`/filesystem/tree?${query(location, request)}`, {\n signal: request?.signal,\n })\n },\n search(location, text, recursive, request) {\n const params = query(location, request)\n params.set('q', text)\n params.set('recursive', String(recursive))\n return api.get<FileSearchResult>(`/filesystem/search?${params}`, { signal: request?.signal })\n },\n }, options)\n}\n"],"mappings":";;;;;AAEA,SAAS,2BAA2B,QAAgD;CAClF,MAAM,YAAY,QAAQ,KAAK;CAC/B,IAAI,CAAC,WAAW,OAAO,KAAA;CAEvB,MAAM,OAAO,UAAU,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAC1D,OAAO,OAAO,IAAI,KAAK,KAAK;AAC9B;;;;;;;AAQA,SAAgB,0BAA0B,eAAuB,KAAa;CAC5E,MAAM,UAAU,2BAA2B;CAC3C,IAAI,CAAC,SAAS,cAAc,OAAO;CAEnC,OAAO,2BAA2B,QAAQ,QAAQ,YAAY,KAAK;AACrE;;;ACPA,SAAS,MAAM,OAAe,KAAa,KAAqB;CAC9D,OAAO,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,CAAC;AAC3C;AAEA,SAAS,oBACP,gBACA,aACQ;CACR,OAAO,KAAK,IACV,YAAY,UACZ,kBAAkB,YAAY,gBAAgB,MAAM,YAAY,aAAa,EAC/E;AACF;AAEA,SAAgB,yBACd,YACA,QACA,gBACA,aACQ;CACR,OAAO,KAAK,MAAM,MAChB,aAAa,QACb,YAAY,UACZ,KAAK,IAAI,YAAY,UAAU,oBAAoB,gBAAgB,WAAW,CAAC,CACjF,CAAC;AACH;AAEA,SAAgB,0BACd,YACA,QACA,gBACA,aACQ;CACR,OAAO,yBAAyB,YAAY,CAAC,QAAQ,gBAAgB,WAAW;AAClF;AAEA,SAAgB,4BACd,cACA,QACA,iBACA,aACQ;CACR,IAAI,mBAAmB,GAAG,OAAO;CACjC,OAAO,KAAK,MAAM,MAChB,eAAgB,SAAS,kBAAmB,KAC5C,YAAY,YACZ,YAAY,UACd,CAAC;AACH;;AAoBA,SAAgB,sBAA4C,SAGzD;CACD,MAAM,eAAe,WAQX,IAAI;CACd,MAAM,aAAa,eAAe,aAAa,UAAU,IAAI;CAC7D,IAAI,qBAAqB;CACzB,IAAI,yBAAyB;CAE7B,SAAS,aAAmB;EAC1B,IAAI,CAAC,aAAa,OAAO;EACzB,aAAa,QAAQ;EACrB,OAAO,oBAAoB,eAAe,iBAAiB;EAC3D,OAAO,oBAAoB,aAAa,UAAU;EAClD,OAAO,oBAAoB,iBAAiB,UAAU;EACtD,IAAI,OAAO,aAAa,aAAa;GACnC,SAAS,KAAK,MAAM,SAAS;GAC7B,SAAS,KAAK,MAAM,aAAa;EACnC;CACF;CAEA,SAAS,kBAAkB,OAA2B;EACpD,MAAM,QAAQ,aAAa;EAC3B,IAAI,CAAC,OAAO;EACZ,QAAQ,cAAc,MAAM,IAAI,CAAC,EAAE,SAAS;GAC1C,MAAM,MAAM;GACZ,YAAY,MAAM;GAClB,QAAQ,MAAM,UAAU,MAAM;GAC9B,QAAQ,MAAM,UAAU,MAAM;GAC9B,eAAe,MAAM;GACrB,eAAe,MAAM;EACvB,CAAC;CACH;CAEA,SAAS,YAAY,MAAa,OAA2B;EAC3D,IAAI,CAAC,QAAQ,QAAQ,OAAO,KAAK,MAAM,WAAW,GAAG;EACrD,MAAM,SAAS,QAAQ,cAAc,IAAI;EACzC,MAAM,gBAAgB,QAAQ,oBAAoB,CAAC,EAAE,sBAAsB;EAC3E,IAAI,CAAC,UAAU,CAAC,eAAe;EAE/B,MAAM,eAAe;EACrB,IAAI,MAAM,yBAAyB,aACjC,MAAM,cAAc,kBAAkB,MAAM,SAAS;EAEvD,MAAM,YAAY,OAAO,sBAAsB,KAAK;EACpD,aAAa,QAAQ;GACnB;GACA,cAAc,MAAM;GACpB,cAAc,MAAM;GACpB,YAAY,OAAO,cAAc;GACjC;GACA,eAAe,WAAW,sBAAsB,KAAK;GACrD,QAAQ,OAAO,UAAU;EAC3B;EAEA,IAAI,OAAO,aAAa,aAAa;GACnC,qBAAqB,SAAS,KAAK,MAAM;GACzC,yBAAyB,SAAS,KAAK,MAAM;GAC7C,SAAS,KAAK,MAAM,SAAS,aAAa,MAAM;GAChD,SAAS,KAAK,MAAM,aAAa;EACnC;EACA,OAAO,iBAAiB,eAAe,iBAAiB;EACxD,OAAO,iBAAiB,aAAa,UAAU;EAC/C,OAAO,iBAAiB,iBAAiB,UAAU;CACrD;CAEA,SAAS,eAAe,MAAsB;EAC5C,OAAO,aAAa,OAAO,SAAS;CACtC;CAEA,YAAY,QAAQ,QAAQ,OAAO,IAAG,YAAW;EAAE,IAAI,CAAC,SAAS,WAAW;CAAE,CAAC;CAC/E,gBAAgB,UAAU;CAE1B,OAAO;EAAE;EAAc;EAAY;EAAgB;EAAa;CAAW;AAC7E;;;;;;;;;;ACvFA,SAAgB,eAAe,UAAiC,CAAC,GAAyB;CACxF,MAAM,kBAAkB,2BAA2B;CACnD,MAAM,MAAM,OAAO,EAAE,SAAS,QAAQ,cAAc,iBAAiB,eAAe,CAAC;CACrF,MAAM,OAAO,oBAAoB,mCAAmC;CAEpE,MAAM,SAAS,IAAmB,CAAC,CAAC;CACpC,MAAM,UAAU,WAAW,EAAE;CAC7B,MAAM,OAAO,WAAW,EAAE;CAC1B,MAAM,SAAS,IAAmB,IAAI;CACtC,MAAM,UAAU,IAAiB,CAAC,CAAC;CACnC,MAAM,cAAc,IAAqB,CAAC,CAAC;CAC3C,MAAM,aAAa,IAAmB,IAAI;CAC1C,MAAM,aAAa,IAAmB,IAAI;CAC1C,MAAM,aAAa,IAAmB,IAAI;CAC1C,MAAM,YAAY,IAAI,KAAK;CAC3B,MAAM,SAAS,WAAW,EAAE;CAC5B,MAAM,OAAO,WAA+B,MAAM;CAClD,MAAM,WAAW,IAAqB,CAAC,CAAC;CAExC,MAAM,gBAAgB,WAAW,KAAK;CACtC,MAAM,iBAAiB,WAAW,KAAK;CACvC,MAAM,eAAe,WAAW,KAAK;CACrC,MAAM,YAAY,eAAe,cAAc,SAAS,eAAe,KAAK;CAC5E,IAAI,YAAY;CAChB,IAAI,cAAc;CAClB,IAAI,WAAW;CACf,IAAI;CACJ,IAAI;CACJ,IAAI;CAEJ,SAAS,eAAqB;EAC5B,aAAa,QAAQ;EACrB,QAAQ,QAAQ,CAAC;EACjB,YAAY,QAAQ,CAAC;EACrB,OAAO,QAAQ;EACf,WAAW,QAAQ;EACnB,WAAW,QAAQ;EACnB,WAAW,QAAQ;EACnB,UAAU,QAAQ;CACpB;CAEA,SAAS,iBAAyB;EAEhC,QADc,QAAQ,QAAQ,SAAS,KAAK,CAAC,EAAA,CAChC,KAAK,SAAS,eAAe,mBAAmB,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE;CAC/E;;CAGA,SAAS,SAAS,OAAgB,UAA0B;EAC1D,MAAM,SAAU,OAA0D,UAAU,MAAM;EAC1F,OAAO,OAAO,WAAW,YAAY,SAAS,SAAS,KAAK,iBAAiB,OAAO,QAAQ;CAC9F;CAEA,SAAS,KAAK,OAAgB,UAAwB;EACpD,KAAK,SAAS,IAAI,MAAM,SAAS,OAAO,QAAQ,CAAC,GAAG,QAAQ;CAC9D;CAEA,SAAS,SAAS,OAAoC;EACpD,OAAQ,OAA8C,UAAU;CAClE;CAEA,eAAe,aAA+B;EAC5C,MAAM,QAAQ,EAAE;EAChB,kBAAkB,MAAM;EACxB,mBAAmB,IAAI,gBAAgB;EACvC,cAAc,QAAQ;EACtB,IAAI;GACF,MAAM,OAAO,MAAM,IAAI,IAA+B,sBAAsB,EAC1E,QAAQ,iBAAiB,OAC3B,CAAC;GACD,IAAI,YAAY,UAAU,aAAa,OAAO;GAC9C,OAAO,QAAQ,KAAK,UAAU,CAAC;GAC/B,KAAK,WAAW;GAChB,KAAK,WAAW;GAChB,OAAO;EACT,SAAS,OAAO;GACd,IAAI,YAAY,UAAU,aAAa,OAAO;GAG9C,IAAI,SAAS,KAAK,MAAM,KAAK;IAC3B,OAAO,QAAQ,CAAC;IAChB,KAAK,WAAW;IAChB,KAAK,WAAW;IAChB,OAAO;GACT;GACA,OAAO,QAAQ,CAAC;GAChB,KAAK,OAAO,mCAAmC;GAC/C,OAAO;EACT,UAAU;GACR,IAAI,UAAU,aAAa,cAAc,QAAQ;EACnD;CACF;CAEA,eAAe,SAAS,UAA8C,UAAU,OAAsB;EACpG,IAAI,YAAY,CAAC,SAAS,UAAU;EACpC,aAAa,WAAW;EACxB,MAAM,QAAQ,EAAE;EAChB,mBAAmB,MAAM;EACzB,oBAAoB,IAAI,gBAAgB;EACxC,eAAe,QAAQ;EACvB,QAAQ,QAAQ,SAAS;EACzB,KAAK,QAAQ,SAAS;EACtB,aAAa;EACb,KAAK,WAAW;EAChB,IAAI;GACF,MAAM,QACJ,YAAY,mBAAmB,SAAS,QAAQ,EAAA,QACvC,mBAAmB,SAAS,IAAI,EAAA,QAChC,KAAK,WACb,UAAU,kBAAkB,OAC5B,OAAO,QAAQ,WAAW,mBAAmB,OAAO,KAAK,MAAM,MAChE,eAAe;GACjB,MAAM,UAAU,MAAM,IAAI,IAA0B,sBAAsB,SAAS,EACjF,QAAQ,kBAAkB,OAC5B,CAAC;GACD,IAAI,YAAY,UAAU,WAAW;GACrC,QAAQ,QAAQ,QAAQ;GACxB,KAAK,QAAQ,QAAQ;GACrB,OAAO,QAAQ,QAAQ,UAAU;GACjC,QAAQ,QAAQ,QAAQ,WAAW,CAAC;GACpC,YAAY,QAAQ,QAAQ,eAAe,CAAC;GAC5C,WAAW,QAAQ,QAAQ,eAAe;GAC1C,WAAW,QAAQ,QAAQ,eAAe;GAC1C,WAAW,QAAQ,QAAQ,eAAe,QAAQ,SAAS,UAAU;GACrE,UAAU,QAAQ,QAAQ,aAAa;GACvC,aAAa,QAAQ;GACrB,KAAK,WAAW;GAChB,KAAK,WAAW;EAClB,SAAS,OAAO;GACd,IAAI,YAAY,UAAU,WAAW;GACrC,QAAQ,QAAQ,CAAC;GACjB,KAAK,OAAO,6BAA6B;EAC3C,UAAU;GACR,IAAI,UAAU,WAAW,eAAe,QAAQ;EAClD;CACF;CAEA,eAAe,OAAsB;EACnC,IAAI,UAAU;EACd,MAAM,kBAAkB;EACxB,IAAI,CAAE,MAAM,WAAW,KAAM,YAAY,oBAAoB,WAAW;EACxE,MAAM,YAAY,QAAQ,QAAQ,cAAc;EAGhD,MAAM,aAAa,UAAgC,MAAM,cAAc;EACvE,MAAM,SACJ,OAAO,MAAM,MAAM,UAAU,MAAM,OAAO,aAAa,UAAU,KAAK,CAAC,KACvE,OAAO,MAAM,KAAK,SAAS;EAC7B,IAAI,QAAQ,MAAM,SAAS;GAAE,UAAU,OAAO;GAAI,MAAM;EAAG,CAAC;OACvD;GACH,QAAQ,QAAQ;GAChB,KAAK,QAAQ;GACb,aAAa;EACf;CACF;CAEA,eAAe,UAAyB;EACtC,IAAI,UAAU;EACd,IAAI,QAAQ,OAAO,MAAM,SAAS;GAAE,UAAU,QAAQ;GAAO,MAAM,KAAK;EAAM,GAAG,IAAI;OAChF,MAAM,KAAK;CAClB;CAEA,SAAS,sBAA4B;EACnC,IAAI,CAAC,QAAQ,SAAS,CAAC,aAAa,SAAS,UAAU,SAAS,KAAK,MAAM,OAAO;EAClF,MAAM,QAAQ,YAAY,MAAM,YAAY,MAAM,SAAS;EAC3D,MAAM,MAAM,KAAK,UAAU,CAAC,QAAQ,OAAO,KAAK,KAAK,CAAC;EACtD,IAAI,SAAS,MAAM,MAAM,SAAS,KAAK,UAAU,CAAC,KAAK,UAAU,KAAK,IAAI,CAAC,MAAM,GAAG,GAAG;EACvF,SAAS,QAAQ,CACf,GAAG,SAAS,OACZ;GACE,UAAU,QAAQ;GAClB,MAAM,KAAK;GACX,MAAM,OAAO,SAAS,KAAK;GAC3B,MAAM;GACN,aAAa,WAAW;EAC1B,CACF;CACF;CAEA,SAAS,QAAc;EACrB,SAAS,QAAQ,CAAC;CACpB;CAKA,MAAM,cAAc;EAClB,IAAI,CAAC,QAAQ,OAAO;EACpB,aAAa,WAAW;EAExB,cAAc,iBAAiB;GAC7B,SAAc;IAAE,UAAU,QAAQ;IAAO,MAAM,KAAK;GAAM,CAAC;EAC7D,GAAG,GAAG;CACR,CAAC;CAED,MAAM,YAAY;EAChB,IAAI,QAAQ,OAAO,SAAc;GAAE,UAAU,QAAQ;GAAO,MAAM,KAAK;EAAM,CAAC;CAChF,CAAC;CAED,YACQ,QAAQ,QAAQ,SAAS,SACzB;EACJ,IAAI,QAAQ,OAAO,SAAc;GAAE,UAAU,QAAQ;GAAO,MAAM,KAAK;EAAM,CAAC;CAChF,GACA,EAAE,MAAM,KAAK,CACf;CAEA,IAAI,gBAAgB,GAClB,qBAAqB;EACnB,WAAW;EACX,EAAE;EACF,EAAE;EACF,aAAa,WAAW;EACxB,mBAAmB,MAAM;EACzB,kBAAkB,MAAM;CAC1B,CAAC;CAEH,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,OAAO,KAAK;EACZ;EACA;EACA;EACA;EACA;CACF;AACF;;;ACzTA,IAAM,gBAAgB;AACtB,IAAM,qBAAqB;AAC3B,IAAM,eAAe;AACrB,IAAM,cAAc;AAEpB,SAAS,qBAAqB,OAA0C;CACtE,IAAI,UAAU,QAAQ,CAAC,QAAQ,KAAK,KAAK,GAAG,OAAO,KAAA;CACnD,MAAM,SAAS,OAAO,KAAK;CAC3B,OAAO,OAAO,cAAc,MAAM,KAAK,SAAS,IAAI,SAAS,KAAA;AAC/D;;AAGA,SAAgB,iCACd,QACiB;CACjB,MAAM,QAAQ,IAAI,gBAAgB;CAClC,MAAM,IAAI,eAAe,OAAO,OAAO,YAAY,CAAC;CACpD,MAAM,IAAI,oBAAoB,OAAO,gBAAgB;CACrD,MAAM,IAAI,cAAc,OAAO,WAAW;CAC1C,IAAI,OAAO,eAAe,KAAA,GACxB,MAAM,IAAI,aAAa,OAAO,OAAO,UAAU,CAAC;CAElD,OAAO;AACT;;AAGA,SAAgB,4BACd,OAC+B;CAC/B,MAAM,eAAe,qBAAqB,MAAM,IAAI,aAAa,CAAC;CAClE,MAAM,mBAAmB,MAAM,IAAI,kBAAkB,CAAC,EAAE,KAAK;CAC7D,MAAM,cAAc,MAAM,IAAI,YAAY,CAAC,EAAE,KAAK;CAClD,MAAM,gBAAgB,MAAM,IAAI,WAAW;CAC3C,MAAM,aAAa,qBAAqB,aAAa;CAErD,IACE,iBAAiB,KAAA,KACd,CAAC,oBACD,CAAC,eACA,kBAAkB,QAAQ,eAAe,KAAA,GAE7C,OAAO;CAGT,OAAO;EACL;EACA;EACA;EACA,GAAI,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,WAAW;CACnD;AACF;;;;ACtBA,SAAgB,yBAAyB,SAAiB,OAAO,IAAY;CAC3E,OAAO,KAAK,UAAU,CAAC,SAAS,IAAI,CAAC;AACvC;AACA,SAAgB,yBAAyB,MAAiD;CACxF,MAAM,UAAmB,KAAK,MAAM,IAAI;CACxC,IACE,CAAC,MAAM,QAAQ,OAAO,KAAK,QAAQ,WAAW,KAC9C,CAAC,QAAQ,OAAO,SAAS,OAAO,SAAS,QAAQ,KAAK,CAAC,QAAQ,IAC/D,MAAM,IAAI,MAAM,+BAA+B;CACjD,OAAO;EAAE,SAAS,QAAQ;EAAI,MAAM,QAAQ;CAAG;AACjD;AAEA,SAAS,eAAe,MAAuB;CAC7C,OAAO,SAAS,MAAO,CAAC,WAAW,KAAK,IAAI,KAAK,CAAC,oBAAoB,KAAK,IAAI,KAC7E,KAAK,MAAM,GAAG,CAAC,CAAC,OAAO,SAAS,SAAS,MAAM,SAAS,OAAO,SAAS,IAAI;AAChF;;AAGA,SAAgB,wBACd,WACA,UAAoC,CAAC,GACtB;CACf,MAAM,OAAO,QAAQ,gBAAgB,EAAE,GAAG,QAAQ,aAAa;CAC/D,MAAM,8BAAc,IAAI,IAAoB;CAE5C,MAAM,2BAAW,IAAI,IAA0B;CAC/C,IAAI,aAAa;CAEjB,SAAS,aAAmB;EAC1B;EACA,SAAS,MAAM;EACf,YAAY,MAAM;CACpB;CAEA,SAAS,iBAAiB,UAA+B;EACvD,IAAI,CAAC,SAAS,YAAY,CAAC,eAAe,SAAS,IAAI,KAAM,SAC3D,SAAS,aAAa,KAAK,YAC1B,KAAK,SAAS,MAAM,SAAS,SAAS,KAAK,QAAQ,CAAC,SAAS,KAAK,WAAW,GAAG,KAAK,KAAK,EAAE,IAC3F,MAAM,IAAI,MAAM,+BAA+B;CACrD;CACA,IAAI,MAAM,iBAAiB,IAAI;CAC/B,SAAS,MAAM,SAAiC;EAC9C,SAAS,QAAQ,eAAe;EAChC,IAAI,SAAS,SAAS,WAAW;EACjC,OAAO;CACT;CACA,SAAS,MAAM,SAAoC,OAAe;EAChE,SAAS,QAAQ,eAAe;EAChC,IAAI,UAAU,YAAY,MAAM,IAAI,aAAa,aAAa,YAAY;CAC5E;CACA,eAAe,KAAK,MAAc,SAAoC,OAAsC;EAC1G,MAAM,SAAS,KAAK;EACpB,MAAM,UAAU,OAAO,yBAAyB,IAAI,IAAI,KAAA;EACxD,MAAM,WAAW,UAAU;GAAE,UAAU,QAAQ;GAAS,MAAM,QAAQ;EAAK,IAAI;EAC/E,IAAI,UAAU,iBAAiB,QAAQ;EACvC,MAAM,MAAM,WAAW,yBAAyB,SAAS,UAAU,SAAS,IAAI,IAAI;EACpF,MAAM,SAAS,SAAS,IAAI,GAAG;EAC/B,IAAI,QAAQ,OAAO;EACnB,IAAI;EACJ,IAAI,CAAC,UAAU;GACb,MAAM,SAAS,MAAM,UAAU,WAAW,OAAO;GACjD,MAAM,SAAS,KAAK;GACpB,QAAQ,OAAO,KAAK,UAAU;IAC5B,YAAY,IAAI,MAAM,IAAI,MAAM,KAAK;IACrC,OAAO;KACL,MAAM;KAAU,MAAM,yBAAyB,MAAM,EAAE;KAAG,MAAM,MAAM;KACtE,YAAY;KAAM,YAAY;KAC9B,mBAAmB,MAAM,cAAc,QAAQ,2BAA2B,KAAA;IAC5E;GACF,CAAC;EACH,OAAO,IAAI,UAAU,MAAM;GACzB,MAAM,OAAO,MAAM,UAAU,KAAK,UAAU,OAAO;GACnD,MAAM,SAAS,KAAK;GACpB,KAAK,MAAM,WAAW,KAAK,YAAY,CAAC,GAAG;IACzC,MAAM,KAAK;KAAE,UAAU,QAAQ;KAAU,MAAM,QAAQ;IAAK;IAC5D,iBAAiB,EAAE;IACnB,IAAI,GAAG,aAAa,SAAS,UAAU,MAAM,IAAI,MAAM,sCAAsC;IAE7F,IAAI,CAAC,QAAQ,WAAW,SAAS,IAAI,yBAAyB,GAAG,UAAU,GAAG,IAAI,GAAG,QAAQ,IAAI,OAAO,CAAC;GAC3G;GACA,MAAM,SAAS,SAAS,IAAI,GAAG;GAC/B,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,mFAAmF;GACrG,OAAO;EACT,OAAO;GACL,MAAM,UAAU,MAAM,UAAU,OAAO,UAAU,OAAO;GACxD,MAAM,SAAS,KAAK;GACpB,IAAI,QAAQ,WACV,MAAM,IAAI,MAAM,mFAAmF;GACrG,QAAQ,QAAQ,UAAU,OAAO;EACnC;EACA,SAAS,IAAI,KAAK,KAAK;EACvB,OAAO;CACT;CACA,SAAS,QAAQ,UAA+B,SAA6C;EAC3F,QAAQ,QAAQ,WAAW,CAAC,EAAA,CAAG,KAAK,UAAU;GAC5C,IAAI,MAAM,SAAS,GAAG,SAAS,OAAO,GAAG,SAAS,KAAK,KAAK,KAAK,MAAM,QACrE,MAAM,IAAI,MAAM,sCAAsC;GACxD,OAAO,OAAO,SAAS,UAAU,KAAK;EACxC,CAAC;CACH;;CAEA,SAAS,OAAO,SAAiB,OAA8B;EAC7D,IAAI,CAAC,eAAe,MAAM,IAAI,KAAK,CAAC,MAAM,QAAQ,MAAM,KAAK,SAAS,GAAG,KACtE,MAAM,SAAS,MAAM,QAAQ,CAAC,MAAM,KAAK,SAAS,IAAI,MAAM,MAAM,GACnE,MAAM,IAAI,MAAM,sCAAsC;EACxD,MAAM,SAAS,MAAM,KAAK,MAAM,GAAG,KAAK,IAAI,GAAG,MAAM,KAAK,SAAS,MAAM,KAAK,SAAS,CAAC,CAAC;EACzF,MAAM,SAAS,QAAQ,YAAY,KAAK,YAAY,WAAW,KAAK;EACpE,MAAM,SAAS;GACb,MAAM,yBAAyB,SAAS,MAAM,IAAI;GAClD,YAAY,SAAS,OAAO,yBAAyB,SAAS,MAAM;GACpE,YAAY,GAAG,YAAY,IAAI,OAAO,KAAK,UAAU,SAAS,MAAM,WAAW;GAC/E,MAAM,MAAM;GAAM,OAAO,MAAM;GAAY,YAAY,MAAM;EAC/D;EACA,OAAO,MAAM,SAAS,cAElB;GAAE,GAAG;GAAQ,MAAM;EAAS,IAC5B;GAAE,GAAG;GAAQ,MAAM;GAAQ,QAAQ,aAAa,MAAM,IAAI;EAAE;CAClE;CACA,eAAe,aACb,OACA,OACA,SAC4C;EAC5C,MAAM,QAAQ,MAAM,OAAO;EAC3B,MAAM,UAAU,MAAM,OAAO,yBAAyB,MAAM,IAAI,IAAI,KAAA;EACpE,MAAM,WAAW,UAAU;GAAE,UAAU,QAAQ;GAAS,MAAM,QAAQ;EAAK,IAAI;EAC/E,iBAAiB,QAAQ;EACzB,MAAM,SAAS,MAAM,UAAU,OAAQ,UAAU,OAAO,MAAM,WAAW,OAAO;EAChF,MAAM,SAAS,KAAK;EACpB,MAAM,SAAS,SAAS,OAAO,GAAG,SAAS,KAAK,KAAK;EACrD,MAAM,SAAS,OAAO,WAAW,CAAC,EAAA,CAAG,KAAK,UAAU;GAElD,IAAI,CAAC,MAAM,KAAK,WAAW,MAAM,KAAM,CAAC,MAAM,aAAa,MAAM,KAAK,MAAM,OAAO,MAAM,CAAC,CAAC,SAAS,GAAG,GACrG,MAAM,IAAI,MAAM,sCAAsC;GACxD,OAAO,OAAO,SAAS,UAAU,KAAK;EACxC,CAAC;EACD,OAAO,OAAO,YAAY;GAAE;GAAO,WAAW;EAAK,IAAI;CACzD;CACA,OAAO;EACL,QAAQ,CAAC;EACT,QAAQ,MAAM;GACZ,IAAI;IACF,OAAO,yBAAyB,yBAAyB,IAAI,CAAC,CAAC,OAAO;GACxE,QAAQ;IACN;GACF;EACF;EACA;EACA,UAAU,OAAO,YAAY,KAAK,IAAI,SAAS,MAAM,OAAO,CAAC;EAC7D,cAAc,OAAO,MAAM,YAAY,KAAK,MAAM,SAAS,MAAM,OAAO,CAAC;EACzE,MAAM,OAAO,OAAO,OAAO,SAAS;GAElC,IAAI,UAAU,WAAW,MAAM,QAAQ,OAAO,OAAO,aAAa,OAAO,OAAO,OAAO;GACvF,MAAM,QAAQ,MAAM,OAAO;GAC3B,MAAM,UAAwB,CAAC;GAC/B,MAAM,QAAQ,CAAC,MAAM,IAAI;GACzB,MAAM,0BAAU,IAAI,IAAY;GAChC,MAAM,SAAS,MAAM,YAAY;GACjC,IAAI,iBAAiB;GACrB,OAAO,MAAM,QAAQ;IACnB,MAAM,SAAS,KAAK;IACpB,MAAM,OAAO,MAAM,MAAM;IACzB,IAAI,QAAQ,IAAI,IAAI,GAAG;IACvB,QAAQ,IAAI,IAAI;IAChB,IAAI,QAAQ,OAAO,KAAK,OAAO;KAAE,OAAO;KAAS,WAAW;IAAK;IACjE,MAAM,QAAQ,MAAM,KAAK,MAAM,gBAAgB,KAAK;IACpD,MAAM,SAAS,KAAK;IACpB,IAAI,gBAAgB,SAAS;KAC3B,iBAAiB,EAAE,GAAG,eAAe;KACrC,OAAO,eAAe;IACxB;IACA,KAAK,MAAM,QAAQ,OAAO;KACxB,IAAI,KAAK,KAAK,YAAY,CAAC,CAAC,SAAS,MAAM,GAAG,QAAQ,KAAK,IAAI;KAC/D,IAAI,MAAM,aAAa,KAAK,SAAS,YAAY,CAAC,KAAK,mBACrD,MAAM,KAAK,KAAK,IAAI;IACxB;IACA,IAAI,QAAQ,SAAS,KAAO,OAAO;KAAE,OAAO,QAAQ,MAAM,GAAG,GAAK;KAAG,WAAW;IAAK;GACvF;GACA,OAAO;EACT;CACF;AACF;;;ACrMA,SAAS,MAAM,UAA+B,SAA0C;CACtF,MAAM,SAAS,IAAI,gBAAgB;EAAE,UAAU,SAAS;EAAU,MAAM,SAAS;CAAK,CAAC;CACvF,IAAI,SAAS,SAAS,OAAO,IAAI,WAAW,MAAM;CAClD,OAAO;AACT;;AAGA,SAAgB,6BACd,UAA8D,CAAC,GAChD;CACf,MAAM,MAAM,OAAO,EACjB,SAAS,QAAQ,cAAc,2BAA2B,CAAC,EAAE,eAC/D,CAAC;CACD,OAAO,wBAAwB;EAC7B,MAAM,WAAW,SAAS;GACxB,IAAI;IACF,MAAM,MAAM,qBAAqB,SAAS,UAAU,kBAAkB;IACtE,MAAM,EAAE,WAAW,MAAM,IAAI,IAA+B,KAAK,EAC/D,QAAQ,SAAS,OACnB,CAAC;IACD,OAAO;GACT,SAAS,OAAO;IACd,IAAK,MAA6C,UAAU,WAAW,KAAK,OAAO,CAAC;IACpF,MAAM;GACR;EACF;EACA,OAAO,UAAU,SAAS;GACxB,OAAO,IAAI,IAA0B,sBAAsB,MAAM,UAAU,OAAO,KAAK,EACrF,QAAQ,SAAS,OACnB,CAAC;EACH;EACA,KAAK,UAAU,SAAS;GACtB,OAAO,IAAI,IAAuB,oBAAoB,MAAM,UAAU,OAAO,KAAK,EAChF,QAAQ,SAAS,OACnB,CAAC;EACH;EACA,OAAO,UAAU,MAAM,WAAW,SAAS;GACzC,MAAM,SAAS,MAAM,UAAU,OAAO;GACtC,OAAO,IAAI,KAAK,IAAI;GACpB,OAAO,IAAI,aAAa,OAAO,SAAS,CAAC;GACzC,OAAO,IAAI,IAAsB,sBAAsB,UAAU,EAAE,QAAQ,SAAS,OAAO,CAAC;EAC9F;CACF,GAAG,OAAO;AACZ"}
@@ -1,4 +1,4 @@
1
- import { c as mintApiErrorFromResponse, o as useSettingsStore, s as MintApiError } from "./settings-BLr8lzw6.js";
1
+ import { c as mintApiErrorFromResponse, o as useSettingsStore, s as MintApiError } from "./settings-BG6v13Zq.js";
2
2
  import { canAccessAdmin, canAccessPlugin, getAccessAudience, getUserPermissions, hasAllPermissions, hasAnyPermission, isAdminUser } from "./permissions.js";
3
3
  import { computed, ref } from "vue";
4
4
  import { defineStore } from "pinia";
@@ -456,4 +456,4 @@ var useExperimentStore = defineStore("mint-experiment", () => {
456
456
  //#endregion
457
457
  export { useApi as a, authenticationRequiredError as i, readErrorMessage as n, useAuthStore as o, AuthenticationRequiredError as r, useExperimentStore as t };
458
458
 
459
- //# sourceMappingURL=experiment-DgllIc3I.js.map
459
+ //# sourceMappingURL=experiment-D49y_Kro.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"experiment-DgllIc3I.js","names":[],"sources":["../src/stores/auth.ts","../src/composables/useApi.ts","../src/utils/errors.ts","../src/stores/experiment.ts"],"sourcesContent":["import { defineStore } from 'pinia'\nimport { ref, computed } from 'vue'\nimport type { AuthConfig, UserInfo } from '../types'\nimport {\n canAccessAdmin as canUserAccessAdmin,\n canAccessPlugin as canUserAccessPlugin,\n getAccessAudience,\n getUserPermissions,\n hasAllPermissions,\n hasAnyPermission,\n isAdminUser,\n} from '../permissions'\n\nconst AUTH_TOKEN_KEY = 'mint-auth-token'\nconst AUTH_EXPIRES_KEY = 'mint-auth-expires'\n\nfunction getLocalStorage(): Storage | null {\n try {\n const storage = globalThis.localStorage\n return typeof storage?.getItem === 'function' ? storage : null\n } catch {\n return null\n }\n}\n\nfunction readStoredItem(key: string): string | null {\n try {\n return getLocalStorage()?.getItem(key) ?? null\n } catch {\n return null\n }\n}\n\nfunction writeStoredItem(key: string, value: string): void {\n try {\n getLocalStorage()?.setItem(key, value)\n } catch {\n // Keep auth usable in-memory when browser storage is blocked.\n }\n}\n\nfunction removeStoredItem(key: string): void {\n try {\n getLocalStorage()?.removeItem(key)\n } catch {\n // Keep auth cleanup usable in-memory when browser storage is blocked.\n }\n}\n\nexport const useAuthStore = defineStore('mint-auth', () => {\n // State\n const token = ref<string | null>(null)\n const tokenExpires = ref<Date | null>(null)\n const username = ref<string | null>(null)\n const userInfo = ref<UserInfo | null>(null)\n const authConfig = ref<AuthConfig>({\n authRequired: true,\n passkeyEnabled: false,\n passkeyRegistered: false,\n registrationEnabled: false,\n databaseMode: 'none',\n experimentVisibilityMode: 'open',\n })\n const isInitialized = ref(false)\n const isLoading = ref(false)\n const error = ref<string | null>(null)\n\n // Computed\n const isAuthenticated = computed(() => {\n if (!authConfig.value.authRequired) {\n return true\n }\n if (!token.value) {\n return false\n }\n if (tokenExpires.value && tokenExpires.value < new Date()) {\n return false\n }\n return true\n })\n\n const needsAuth = computed(() => {\n return authConfig.value.authRequired && !isAuthenticated.value\n })\n\n const isAdmin = computed(() => isAdminUser(userInfo.value))\n const userType = computed(() => getAccessAudience(userInfo.value))\n const permissions = computed(() => getUserPermissions(userInfo.value))\n const canAccessAdmin = computed(() => canUserAccessAdmin(userInfo.value))\n\n const canRegister = computed(() => {\n return authConfig.value.registrationEnabled\n })\n\n // Actions\n function initialize() {\n const storedToken = readStoredItem(AUTH_TOKEN_KEY)\n const storedExpires = readStoredItem(AUTH_EXPIRES_KEY)\n\n if (storedToken) {\n token.value = storedToken\n\n if (storedExpires) {\n const expires = new Date(storedExpires)\n if (expires > new Date()) {\n tokenExpires.value = expires\n } else {\n clearToken()\n }\n }\n }\n\n isInitialized.value = true\n }\n\n function setToken(accessToken: string, expiresIn: number) {\n token.value = accessToken\n const expires = new Date(Date.now() + expiresIn * 1000)\n tokenExpires.value = expires\n\n writeStoredItem(AUTH_TOKEN_KEY, accessToken)\n writeStoredItem(AUTH_EXPIRES_KEY, expires.toISOString())\n }\n\n function clearToken() {\n token.value = null\n tokenExpires.value = null\n username.value = null\n userInfo.value = null\n\n removeStoredItem(AUTH_TOKEN_KEY)\n removeStoredItem(AUTH_EXPIRES_KEY)\n }\n\n function setUserInfo(info: UserInfo) {\n userInfo.value = info\n username.value = info.username\n }\n\n function hasPermission(...perms: string[]): boolean {\n return hasAllPermissions(userInfo.value, perms)\n }\n\n function hasAnyPermissionForUser(...perms: string[]): boolean {\n return hasAnyPermission(userInfo.value, perms)\n }\n\n function canAccessPlugin(pluginName: string): boolean {\n return canUserAccessPlugin(userInfo.value, pluginName)\n }\n\n function setAuthConfig(config: AuthConfig) {\n authConfig.value = config\n }\n\n function setUsername(name: string) {\n username.value = name\n }\n\n function setError(message: string | null) {\n error.value = message\n }\n\n function setLoading(loading: boolean) {\n isLoading.value = loading\n }\n\n function logout() {\n clearToken()\n }\n\n return {\n // State\n token,\n tokenExpires,\n username,\n userInfo,\n authConfig,\n isInitialized,\n isLoading,\n error,\n\n // Computed\n isAuthenticated,\n needsAuth,\n isAdmin,\n userType,\n permissions,\n canAccessAdmin,\n canRegister,\n\n // Actions\n initialize,\n setToken,\n clearToken,\n setAuthConfig,\n setUsername,\n setUserInfo,\n setError,\n setLoading,\n hasPermission,\n hasAnyPermission: hasAnyPermissionForUser,\n canAccessPlugin,\n logout,\n }\n})\n","import axios, { type AxiosInstance, type AxiosRequestConfig, type InternalAxiosRequestConfig } from 'axios'\nimport { useSettingsStore } from '../stores/settings'\nimport { useAuthStore } from '../stores/auth'\nimport { MintApiError, mintApiErrorFromResponse } from '../apiErrors'\n\nlet apiClientInstance: AxiosInstance | null = null\nlet interceptorAttached = false\n\ninterface MintAxiosRequestConfig extends AxiosRequestConfig {\n _mintSkipAuth?: boolean\n _mintAuthToken?: string\n _mintTypedErrors?: boolean\n}\n\ninterface MintInternalAxiosRequestConfig extends InternalAxiosRequestConfig {\n _mintSkipAuth?: boolean\n _mintAuthToken?: string\n _mintTypedErrors?: boolean\n}\n\ntype MutableHeaders = Record<string, unknown> & {\n has?: (header: string) => boolean\n set?: (header: string, value: string) => void\n delete?: (header: string) => void\n}\n\nfunction joinUrlPath(baseUrl: string, path: string): string {\n if (/^https?:\\/\\//i.test(path)) return path\n if (!path) return baseUrl\n if (path.startsWith('?') || path.startsWith('#')) return `${baseUrl.replace(/\\/+$/, '')}${path}`\n const normalizedBase = baseUrl.replace(/\\/+$/, '')\n const normalizedPath = path.replace(/^\\/+/, '/')\n return `${normalizedBase}${normalizedPath.startsWith('/') ? normalizedPath : `/${normalizedPath}`}`\n}\n\nfunction getBasePath(baseUrl: string): string {\n if (!baseUrl) return '/'\n\n try {\n const origin = typeof window !== 'undefined' ? window.location.origin : 'http://localhost'\n return new URL(baseUrl, origin).pathname.replace(/\\/+$/, '') || '/'\n } catch {\n const path = baseUrl.replace(/^https?:\\/\\/[^/]+/i, '')\n return path.replace(/\\/+$/, '') || '/'\n }\n}\n\nfunction normalizeRequestUrl(baseUrl: string, url: string): string {\n if (!url || /^https?:\\/\\//.test(url)) return url\n\n const basePath = getBasePath(baseUrl)\n if (basePath === '/') return url\n\n const normalizedUrl = url.startsWith('/') ? url : `/${url}`\n if (\n normalizedUrl === basePath\n || normalizedUrl.startsWith(`${basePath}?`)\n || normalizedUrl.startsWith(`${basePath}#`)\n ) {\n return normalizedUrl.slice(basePath.length)\n }\n if (normalizedUrl.startsWith(`${basePath}/`)) {\n return normalizedUrl.slice(basePath.length) || ''\n }\n return url\n}\n\nfunction asMutableHeaders(headers: AxiosRequestConfig['headers']): MutableHeaders | null {\n return headers ? (headers as MutableHeaders) : null\n}\n\nfunction hasAuthorizationHeader(headers: AxiosRequestConfig['headers']): boolean {\n const bag = asMutableHeaders(headers)\n if (!bag) return false\n if (typeof bag.has === 'function') return bag.has('Authorization')\n return Object.keys(bag).some((key) => key.toLowerCase() === 'authorization')\n}\n\nfunction setAuthorizationHeader(headers: AxiosRequestConfig['headers'], value: string): void {\n const bag = asMutableHeaders(headers)\n if (!bag) return\n if (typeof bag.set === 'function') {\n bag.set('Authorization', value)\n return\n }\n bag.Authorization = value\n}\n\nfunction deleteAuthorizationHeader(headers: AxiosRequestConfig['headers']): void {\n const bag = asMutableHeaders(headers)\n if (!bag) return\n if (typeof bag.delete === 'function') {\n bag.delete('Authorization')\n return\n }\n for (const key of Object.keys(bag)) {\n if (key.toLowerCase() === 'authorization') delete bag[key]\n }\n}\n\nfunction getAuthorizationHeader(headers: AxiosRequestConfig['headers']): unknown {\n const bag = asMutableHeaders(headers) as (MutableHeaders & { get?: (header: string) => unknown }) | null\n if (!bag) return undefined\n if (typeof bag.get === 'function') return bag.get('Authorization')\n const key = Object.keys(bag).find((candidate) => candidate.toLowerCase() === 'authorization')\n return key ? bag[key] : undefined\n}\n\n/**\n * Raised when an authenticated SDK request is rejected because the login is no\n * longer valid. It is also a `MintApiError` (status 401), so typed-error callers\n * and `instanceof AuthenticationRequiredError` callers both recognise it.\n */\nexport class AuthenticationRequiredError extends MintApiError {\n override readonly name: string = 'AuthenticationRequiredError'\n declare readonly code: 'AUTHENTICATION_REQUIRED'\n declare readonly status: 401\n\n constructor(cause?: unknown, envelope?: MintApiError) {\n super(\n 'Authentication required. Please sign in again.',\n 'AUTHENTICATION_REQUIRED',\n 401,\n envelope?.requestId ?? null,\n envelope?.details ?? null,\n envelope?.detail,\n envelope?.body ?? null,\n cause,\n )\n }\n}\n\n/** @internal Convert a rejected authenticated request into the SDK's public auth error. */\nexport function authenticationRequiredError(\n cause: unknown,\n rejectedToken?: string,\n envelope?: MintApiError,\n): AuthenticationRequiredError {\n const currentAuthStore = useAuthStore()\n if (rejectedToken && currentAuthStore.token === rejectedToken) {\n currentAuthStore.clearToken()\n }\n return new AuthenticationRequiredError(cause, envelope)\n}\n\nfunction getApiClient(): AxiosInstance {\n if (!apiClientInstance) {\n apiClientInstance = axios.create({\n headers: {\n 'Content-Type': 'application/json',\n },\n })\n }\n return apiClientInstance\n}\n\nexport interface ApiClientOptions {\n baseUrl?: string\n timeout?: number\n withAuth?: boolean\n /** Normalize failed responses into MintApiError. Generated clients enable this by default. */\n typedErrors?: boolean\n}\n\nexport interface UseApiReturn {\n client: AxiosInstance\n get: <T>(url: string, config?: AxiosRequestConfig) => Promise<T>\n post: <T>(url: string, data?: unknown, config?: AxiosRequestConfig) => Promise<T>\n put: <T>(url: string, data?: unknown, config?: AxiosRequestConfig) => Promise<T>\n patch: <T>(url: string, data?: unknown, config?: AxiosRequestConfig) => Promise<T>\n delete: <T>(url: string, config?: AxiosRequestConfig) => Promise<T>\n upload: <T>(url: string, file: File, fieldName?: string, additionalData?: Record<string, unknown>) => Promise<T>\n download: (url: string, filename?: string) => Promise<string>\n buildUrl: (path: string) => string\n buildWsUrl: (path: string) => string\n}\n\n/** Axios-based API client that injects the plugin base URL and JWT auth header on every request. */\nexport function useApi(options: ApiClientOptions = {}): UseApiReturn {\n const settingsStore = useSettingsStore()\n const authStore = useAuthStore()\n const apiClient = getApiClient()\n\n // Ensure auth store is initialized (reads token from localStorage)\n if (!authStore.isInitialized) {\n authStore.initialize()\n }\n\n // Attach auth interceptor only once (reads token dynamically, not from closure)\n if (!interceptorAttached) {\n apiClient.interceptors.request.use((config) => {\n const request = config as MintInternalAxiosRequestConfig\n if (request._mintSkipAuth) {\n deleteAuthorizationHeader(request.headers)\n return request\n }\n\n const currentAuthStore = useAuthStore()\n if (currentAuthStore.token && config.headers && !hasAuthorizationHeader(config.headers)) {\n setAuthorizationHeader(config.headers, `Bearer ${currentAuthStore.token}`)\n }\n if (getAuthorizationHeader(config.headers) === `Bearer ${currentAuthStore.token}`) {\n request._mintAuthToken = currentAuthStore.token ?? undefined\n }\n return config\n })\n apiClient.interceptors.response.use(\n (response) => response,\n async (error: unknown) => {\n if (axios.isAxiosError(error) && error.response) {\n const request = error.config as MintInternalAxiosRequestConfig | undefined\n if (request?._mintTypedErrors) {\n let payload: unknown = error.response.data\n if (payload instanceof Blob) {\n try {\n payload = JSON.parse(await payload.text())\n } catch {\n // Keep non-JSON download errors available as the original body.\n }\n }\n const typed = mintApiErrorFromResponse(\n error.response.status,\n payload,\n {\n requestId: String(error.response.headers?.['x-request-id'] ?? '') || null,\n cause: error,\n },\n )\n if (error.response.status === 401 && request._mintSkipAuth !== true) {\n return Promise.reject(authenticationRequiredError(error, request._mintAuthToken, typed))\n }\n return Promise.reject(typed)\n }\n if (error.response.status === 401) {\n if (request?._mintSkipAuth !== true) {\n return Promise.reject(authenticationRequiredError(error, request?._mintAuthToken))\n }\n }\n }\n return Promise.reject(error)\n },\n )\n interceptorAttached = true\n }\n\n function getBaseUrl(): string {\n return options.baseUrl ?? settingsStore.getApiBaseUrl()\n }\n\n function normalizeUrl(url: string): string {\n return normalizeRequestUrl(getBaseUrl(), url)\n }\n\n // Build per-request config that applies this caller's options\n function requestConfig(config?: AxiosRequestConfig): MintAxiosRequestConfig {\n const base: MintAxiosRequestConfig = {\n baseURL: getBaseUrl(),\n timeout: options.timeout ?? settingsStore.requestTimeout,\n _mintTypedErrors: options.typedErrors,\n ...config,\n }\n // Strip auth header if explicitly disabled\n if (options.withAuth === false) {\n base._mintSkipAuth = true\n deleteAuthorizationHeader(base.headers)\n }\n return base\n }\n\n // Generic request methods\n async function get<T>(url: string, config?: AxiosRequestConfig): Promise<T> {\n const response = await apiClient.get<T>(normalizeUrl(url), requestConfig(config))\n return response.data\n }\n\n async function post<T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T> {\n const response = await apiClient.post<T>(normalizeUrl(url), data, requestConfig(config))\n return response.data\n }\n\n async function put<T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T> {\n const response = await apiClient.put<T>(normalizeUrl(url), data, requestConfig(config))\n return response.data\n }\n\n async function patch<T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T> {\n const response = await apiClient.patch<T>(normalizeUrl(url), data, requestConfig(config))\n return response.data\n }\n\n async function del<T>(url: string, config?: AxiosRequestConfig): Promise<T> {\n const response = await apiClient.delete<T>(normalizeUrl(url), requestConfig(config))\n return response.data\n }\n\n // File upload helper\n async function upload<T>(url: string, file: File, fieldName = 'file', additionalData?: Record<string, unknown>): Promise<T> {\n const formData = new FormData()\n formData.append(fieldName, file)\n\n if (additionalData) {\n Object.entries(additionalData).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n formData.append(key, typeof value === 'object' ? JSON.stringify(value) : String(value))\n }\n })\n }\n\n const response = await apiClient.post<T>(normalizeUrl(url), formData, requestConfig({\n // Let Axios set Content-Type with the correct multipart boundary\n headers: { 'Content-Type': undefined },\n }))\n return response.data\n }\n\n // Download helper - returns blob URL\n async function download(url: string, filename?: string): Promise<string> {\n const response = await apiClient.get(normalizeUrl(url), requestConfig({ responseType: 'blob' }))\n const blob = new Blob([response.data])\n const blobUrl = URL.createObjectURL(blob)\n\n // Optionally trigger download\n if (filename) {\n const link = document.createElement('a')\n link.href = blobUrl\n link.download = filename\n document.body.appendChild(link)\n link.click()\n document.body.removeChild(link)\n // Revoke after a short delay to allow download to start;\n // return empty string since the URL will be invalidated\n setTimeout(() => URL.revokeObjectURL(blobUrl), 100)\n return ''\n }\n\n // Caller is responsible for revoking when no filename is provided\n return blobUrl\n }\n\n // Build full URL for external use (e.g., <a href=\"...\">)\n function buildUrl(path: string): string {\n const baseUrl = getBaseUrl()\n return joinUrlPath(baseUrl, normalizeRequestUrl(baseUrl, path))\n }\n\n // WebSocket URL builder\n function buildWsUrl(path: string): string {\n return joinUrlPath(settingsStore.getWsBaseUrl(), path)\n }\n\n return {\n client: apiClient,\n get,\n post,\n put,\n patch,\n delete: del,\n upload,\n download,\n buildUrl,\n buildWsUrl,\n }\n}\n","/**\n * The one place a thrown value is turned into a developer-facing message.\n *\n * Five copies of this logic drifted apart across the composables and stores\n * (some checked a bare `{ message }` object, some did not); this export is the\n * union of what they all accepted, so every caller now reads the same message\n * from the same value.\n */\nexport function readErrorMessage(value: unknown, fallback = 'Request failed.'): string {\n if (value instanceof Error && value.message) {\n return value.message\n }\n\n if (typeof value === 'string' && value.trim()) {\n return value\n }\n\n if (\n typeof value === 'object'\n && value !== null\n && 'message' in value\n && typeof (value as { message?: unknown }).message === 'string'\n && (value as { message: string }).message.trim()\n ) {\n return (value as { message: string }).message\n }\n\n return fallback\n}\n","import { defineStore } from 'pinia'\nimport { ref } from 'vue'\nimport { useApi } from '../composables/useApi'\nimport { readErrorMessage } from '../utils/errors'\nimport type { ExperimentSummary, PlatformContext } from '../types'\n\nfunction getPlatformApiUrl(): string | undefined {\n if (typeof window === 'undefined') return undefined\n return (window as unknown as { __MINT_PLATFORM__?: PlatformContext }).__MINT_PLATFORM__\n ?.platformApiUrl\n}\n\n/**\n * The single source of truth for the experiment a plugin is currently working on.\n *\n * Both entry points land here: `ExperimentSelectorModal` calls `select()` with a\n * record the platform already returned, and deep links call `selectById()` with\n * nothing but an id. Because a name can only ever arrive from the platform,\n * there is no code path in which a caller can invent one.\n */\nexport const useExperimentStore = defineStore('mint-experiment', () => {\n const current = ref<ExperimentSummary | null>(null)\n const currentId = ref<number | null>(null)\n const isResolving = ref(false)\n const error = ref<string | null>(null)\n\n // In-flight requests only. Resolved records are deliberately not memoised, so\n // a rename is picked up on the next resolve instead of persisting for the\n // lifetime of the page.\n const inFlight = new Map<number, Promise<ExperimentSummary>>()\n\n // Monotonic token guarding every write to the shared selection. Responses can\n // land in any order, and a late one must never overwrite a newer choice —\n // that would display a name belonging to a different experiment, which is the\n // failure this store exists to prevent.\n let selectionToken = 0\n\n function fetchExperiment(id: number): Promise<ExperimentSummary> {\n const pending = inFlight.get(id)\n if (pending) return pending\n\n const base = getPlatformApiUrl() ?? ''\n const request = useApi()\n .get<ExperimentSummary>(`${base}/experiments/${id}`)\n .finally(() => {\n inFlight.delete(id)\n })\n\n inFlight.set(id, request)\n return request\n }\n\n /** Adopt a record the platform already returned, without a further request. */\n function select(experiment: ExperimentSummary): void {\n selectionToken += 1\n current.value = experiment\n currentId.value = experiment.id\n error.value = null\n isResolving.value = false\n }\n\n /**\n * Bind to an experiment known only by id, resolving its record from the\n * platform. On failure the binding is remembered but `current` stays null, so\n * callers can distinguish \"not resolved yet\" from \"nothing selected\".\n */\n async function selectById(id: number): Promise<ExperimentSummary | null> {\n const joinsCurrentResolution = isResolving.value && currentId.value === id\n const token = joinsCurrentResolution ? selectionToken : (selectionToken += 1)\n if (!joinsCurrentResolution) {\n current.value = null\n currentId.value = id\n error.value = null\n isResolving.value = true\n }\n try {\n const experiment = await fetchExperiment(id)\n // A newer selection superseded this one while the request was in flight.\n // Returning null tells the caller its result is no longer the selection,\n // so it cannot be applied downstream either.\n if (token !== selectionToken) return null\n current.value = experiment\n return experiment\n } catch (err) {\n if (token !== selectionToken) return null\n current.value = null\n error.value = readErrorMessage(err, 'Failed to load experiment')\n return null\n } finally {\n // Only the newest request owns the loading flag; otherwise a superseded\n // response would clear it while the current one is still pending.\n if (token === selectionToken) isResolving.value = false\n }\n }\n\n function clear(): void {\n // Supersede any in-flight resolve so a detached experiment cannot be\n // repopulated by a response that was already on its way.\n selectionToken += 1\n current.value = null\n currentId.value = null\n error.value = null\n isResolving.value = false\n }\n\n return {\n current,\n currentId,\n isResolving,\n error,\n select,\n selectById,\n clear,\n }\n})\n"],"mappings":";;;;;;AAaA,IAAM,iBAAiB;AACvB,IAAM,mBAAmB;AAEzB,SAAS,kBAAkC;CACzC,IAAI;EACF,MAAM,UAAU,WAAW;EAC3B,OAAO,OAAO,SAAS,YAAY,aAAa,UAAU;CAC5D,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,eAAe,KAA4B;CAClD,IAAI;EACF,OAAO,gBAAgB,CAAC,EAAE,QAAQ,GAAG,KAAK;CAC5C,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,gBAAgB,KAAa,OAAqB;CACzD,IAAI;EACF,gBAAgB,CAAC,EAAE,QAAQ,KAAK,KAAK;CACvC,QAAQ,CAER;AACF;AAEA,SAAS,iBAAiB,KAAmB;CAC3C,IAAI;EACF,gBAAgB,CAAC,EAAE,WAAW,GAAG;CACnC,QAAQ,CAER;AACF;AAEA,IAAa,eAAe,YAAY,mBAAmB;CAEzD,MAAM,QAAQ,IAAmB,IAAI;CACrC,MAAM,eAAe,IAAiB,IAAI;CAC1C,MAAM,WAAW,IAAmB,IAAI;CACxC,MAAM,WAAW,IAAqB,IAAI;CAC1C,MAAM,aAAa,IAAgB;EACjC,cAAc;EACd,gBAAgB;EAChB,mBAAmB;EACnB,qBAAqB;EACrB,cAAc;EACd,0BAA0B;CAC5B,CAAC;CACD,MAAM,gBAAgB,IAAI,KAAK;CAC/B,MAAM,YAAY,IAAI,KAAK;CAC3B,MAAM,QAAQ,IAAmB,IAAI;CAGrC,MAAM,kBAAkB,eAAe;EACrC,IAAI,CAAC,WAAW,MAAM,cACpB,OAAO;EAET,IAAI,CAAC,MAAM,OACT,OAAO;EAET,IAAI,aAAa,SAAS,aAAa,wBAAQ,IAAI,KAAK,GACtD,OAAO;EAET,OAAO;CACT,CAAC;CAED,MAAM,YAAY,eAAe;EAC/B,OAAO,WAAW,MAAM,gBAAgB,CAAC,gBAAgB;CAC3D,CAAC;CAED,MAAM,UAAU,eAAe,YAAY,SAAS,KAAK,CAAC;CAC1D,MAAM,WAAW,eAAe,kBAAkB,SAAS,KAAK,CAAC;CACjE,MAAM,cAAc,eAAe,mBAAmB,SAAS,KAAK,CAAC;CACrE,MAAM,mBAAiB,eAAe,eAAmB,SAAS,KAAK,CAAC;CAExE,MAAM,cAAc,eAAe;EACjC,OAAO,WAAW,MAAM;CAC1B,CAAC;CAGD,SAAS,aAAa;EACpB,MAAM,cAAc,eAAe,cAAc;EACjD,MAAM,gBAAgB,eAAe,gBAAgB;EAErD,IAAI,aAAa;GACf,MAAM,QAAQ;GAEd,IAAI,eAAe;IACjB,MAAM,UAAU,IAAI,KAAK,aAAa;IACtC,IAAI,0BAAU,IAAI,KAAK,GACrB,aAAa,QAAQ;SAErB,WAAW;GAEf;EACF;EAEA,cAAc,QAAQ;CACxB;CAEA,SAAS,SAAS,aAAqB,WAAmB;EACxD,MAAM,QAAQ;EACd,MAAM,UAAU,IAAI,KAAK,KAAK,IAAI,IAAI,YAAY,GAAI;EACtD,aAAa,QAAQ;EAErB,gBAAgB,gBAAgB,WAAW;EAC3C,gBAAgB,kBAAkB,QAAQ,YAAY,CAAC;CACzD;CAEA,SAAS,aAAa;EACpB,MAAM,QAAQ;EACd,aAAa,QAAQ;EACrB,SAAS,QAAQ;EACjB,SAAS,QAAQ;EAEjB,iBAAiB,cAAc;EAC/B,iBAAiB,gBAAgB;CACnC;CAEA,SAAS,YAAY,MAAgB;EACnC,SAAS,QAAQ;EACjB,SAAS,QAAQ,KAAK;CACxB;CAEA,SAAS,cAAc,GAAG,OAA0B;EAClD,OAAO,kBAAkB,SAAS,OAAO,KAAK;CAChD;CAEA,SAAS,wBAAwB,GAAG,OAA0B;EAC5D,OAAO,iBAAiB,SAAS,OAAO,KAAK;CAC/C;CAEA,SAAS,kBAAgB,YAA6B;EACpD,OAAO,gBAAoB,SAAS,OAAO,UAAU;CACvD;CAEA,SAAS,cAAc,QAAoB;EACzC,WAAW,QAAQ;CACrB;CAEA,SAAS,YAAY,MAAc;EACjC,SAAS,QAAQ;CACnB;CAEA,SAAS,SAAS,SAAwB;EACxC,MAAM,QAAQ;CAChB;CAEA,SAAS,WAAW,SAAkB;EACpC,UAAU,QAAQ;CACpB;CAEA,SAAS,SAAS;EAChB,WAAW;CACb;CAEA,OAAO;EAEL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EACA,gBAAA;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,kBAAkB;EAClB,iBAAA;EACA;CACF;AACF,CAAC;;;ACxMD,IAAI,oBAA0C;AAC9C,IAAI,sBAAsB;AAoB1B,SAAS,YAAY,SAAiB,MAAsB;CAC1D,IAAI,gBAAgB,KAAK,IAAI,GAAG,OAAO;CACvC,IAAI,CAAC,MAAM,OAAO;CAClB,IAAI,KAAK,WAAW,GAAG,KAAK,KAAK,WAAW,GAAG,GAAG,OAAO,GAAG,QAAQ,QAAQ,QAAQ,EAAE,IAAI;CAC1F,MAAM,iBAAiB,QAAQ,QAAQ,QAAQ,EAAE;CACjD,MAAM,iBAAiB,KAAK,QAAQ,QAAQ,GAAG;CAC/C,OAAO,GAAG,iBAAiB,eAAe,WAAW,GAAG,IAAI,iBAAiB,IAAI;AACnF;AAEA,SAAS,YAAY,SAAyB;CAC5C,IAAI,CAAC,SAAS,OAAO;CAErB,IAAI;EACF,MAAM,SAAS,OAAO,WAAW,cAAc,OAAO,SAAS,SAAS;EACxE,OAAO,IAAI,IAAI,SAAS,MAAM,CAAC,CAAC,SAAS,QAAQ,QAAQ,EAAE,KAAK;CAClE,QAAQ;EAEN,OADa,QAAQ,QAAQ,sBAAsB,EAC5C,CAAA,CAAK,QAAQ,QAAQ,EAAE,KAAK;CACrC;AACF;AAEA,SAAS,oBAAoB,SAAiB,KAAqB;CACjE,IAAI,CAAC,OAAO,eAAe,KAAK,GAAG,GAAG,OAAO;CAE7C,MAAM,WAAW,YAAY,OAAO;CACpC,IAAI,aAAa,KAAK,OAAO;CAE7B,MAAM,gBAAgB,IAAI,WAAW,GAAG,IAAI,MAAM,IAAI;CACtD,IACE,kBAAkB,YACf,cAAc,WAAW,GAAG,SAAS,EAAE,KACvC,cAAc,WAAW,GAAG,SAAS,EAAE,GAE1C,OAAO,cAAc,MAAM,SAAS,MAAM;CAE5C,IAAI,cAAc,WAAW,GAAG,SAAS,EAAE,GACzC,OAAO,cAAc,MAAM,SAAS,MAAM,KAAK;CAEjD,OAAO;AACT;AAEA,SAAS,iBAAiB,SAA+D;CACvF,OAAO,UAAW,UAA6B;AACjD;AAEA,SAAS,uBAAuB,SAAiD;CAC/E,MAAM,MAAM,iBAAiB,OAAO;CACpC,IAAI,CAAC,KAAK,OAAO;CACjB,IAAI,OAAO,IAAI,QAAQ,YAAY,OAAO,IAAI,IAAI,eAAe;CACjE,OAAO,OAAO,KAAK,GAAG,CAAC,CAAC,MAAM,QAAQ,IAAI,YAAY,MAAM,eAAe;AAC7E;AAEA,SAAS,uBAAuB,SAAwC,OAAqB;CAC3F,MAAM,MAAM,iBAAiB,OAAO;CACpC,IAAI,CAAC,KAAK;CACV,IAAI,OAAO,IAAI,QAAQ,YAAY;EACjC,IAAI,IAAI,iBAAiB,KAAK;EAC9B;CACF;CACA,IAAI,gBAAgB;AACtB;AAEA,SAAS,0BAA0B,SAA8C;CAC/E,MAAM,MAAM,iBAAiB,OAAO;CACpC,IAAI,CAAC,KAAK;CACV,IAAI,OAAO,IAAI,WAAW,YAAY;EACpC,IAAI,OAAO,eAAe;EAC1B;CACF;CACA,KAAK,MAAM,OAAO,OAAO,KAAK,GAAG,GAC/B,IAAI,IAAI,YAAY,MAAM,iBAAiB,OAAO,IAAI;AAE1D;AAEA,SAAS,uBAAuB,SAAiD;CAC/E,MAAM,MAAM,iBAAiB,OAAO;CACpC,IAAI,CAAC,KAAK,OAAO,KAAA;CACjB,IAAI,OAAO,IAAI,QAAQ,YAAY,OAAO,IAAI,IAAI,eAAe;CACjE,MAAM,MAAM,OAAO,KAAK,GAAG,CAAC,CAAC,MAAM,cAAc,UAAU,YAAY,MAAM,eAAe;CAC5F,OAAO,MAAM,IAAI,OAAO,KAAA;AAC1B;;;;;;AAOA,IAAa,8BAAb,cAAiD,aAAa;CAC5D,OAAiC;CAIjC,YAAY,OAAiB,UAAyB;EACpD,MACE,kDACA,2BACA,KACA,UAAU,aAAa,MACvB,UAAU,WAAW,MACrB,UAAU,QACV,UAAU,QAAQ,MAClB,KACF;CACF;AACF;;AAGA,SAAgB,4BACd,OACA,eACA,UAC6B;CAC7B,MAAM,mBAAmB,aAAa;CACtC,IAAI,iBAAiB,iBAAiB,UAAU,eAC9C,iBAAiB,WAAW;CAE9B,OAAO,IAAI,4BAA4B,OAAO,QAAQ;AACxD;AAEA,SAAS,eAA8B;CACrC,IAAI,CAAC,mBACH,oBAAoB,MAAM,OAAO,EAC/B,SAAS,EACP,gBAAgB,mBAClB,EACF,CAAC;CAEH,OAAO;AACT;;AAwBA,SAAgB,OAAO,UAA4B,CAAC,GAAiB;CACnE,MAAM,gBAAgB,iBAAiB;CACvC,MAAM,YAAY,aAAa;CAC/B,MAAM,YAAY,aAAa;CAG/B,IAAI,CAAC,UAAU,eACb,UAAU,WAAW;CAIvB,IAAI,CAAC,qBAAqB;EACxB,UAAU,aAAa,QAAQ,KAAK,WAAW;GAC7C,MAAM,UAAU;GAChB,IAAI,QAAQ,eAAe;IACzB,0BAA0B,QAAQ,OAAO;IACzC,OAAO;GACT;GAEA,MAAM,mBAAmB,aAAa;GACtC,IAAI,iBAAiB,SAAS,OAAO,WAAW,CAAC,uBAAuB,OAAO,OAAO,GACpF,uBAAuB,OAAO,SAAS,UAAU,iBAAiB,OAAO;GAE3E,IAAI,uBAAuB,OAAO,OAAO,MAAM,UAAU,iBAAiB,SACxE,QAAQ,iBAAiB,iBAAiB,SAAS,KAAA;GAErD,OAAO;EACT,CAAC;EACD,UAAU,aAAa,SAAS,KAC7B,aAAa,UACd,OAAO,UAAmB;GACxB,IAAI,MAAM,aAAa,KAAK,KAAK,MAAM,UAAU;IAC/C,MAAM,UAAU,MAAM;IACtB,IAAI,SAAS,kBAAkB;KAC7B,IAAI,UAAmB,MAAM,SAAS;KACtC,IAAI,mBAAmB,MACrB,IAAI;MACF,UAAU,KAAK,MAAM,MAAM,QAAQ,KAAK,CAAC;KAC3C,QAAQ,CAER;KAEF,MAAM,QAAQ,yBACZ,MAAM,SAAS,QACf,SACA;MACE,WAAW,OAAO,MAAM,SAAS,UAAU,mBAAmB,EAAE,KAAK;MACrE,OAAO;KACT,CACF;KACA,IAAI,MAAM,SAAS,WAAW,OAAO,QAAQ,kBAAkB,MAC7D,OAAO,QAAQ,OAAO,4BAA4B,OAAO,QAAQ,gBAAgB,KAAK,CAAC;KAEzF,OAAO,QAAQ,OAAO,KAAK;IAC7B;IACA,IAAI,MAAM,SAAS,WAAW,KACxB;SAAA,SAAS,kBAAkB,MAC7B,OAAO,QAAQ,OAAO,4BAA4B,OAAO,SAAS,cAAc,CAAC;IAAA;GAGvF;GACA,OAAO,QAAQ,OAAO,KAAK;EAC7B,CACF;EACA,sBAAsB;CACxB;CAEA,SAAS,aAAqB;EAC5B,OAAO,QAAQ,WAAW,cAAc,cAAc;CACxD;CAEA,SAAS,aAAa,KAAqB;EACzC,OAAO,oBAAoB,WAAW,GAAG,GAAG;CAC9C;CAGA,SAAS,cAAc,QAAqD;EAC1E,MAAM,OAA+B;GACnC,SAAS,WAAW;GACpB,SAAS,QAAQ,WAAW,cAAc;GAC1C,kBAAkB,QAAQ;GAC1B,GAAG;EACL;EAEA,IAAI,QAAQ,aAAa,OAAO;GAC9B,KAAK,gBAAgB;GACrB,0BAA0B,KAAK,OAAO;EACxC;EACA,OAAO;CACT;CAGA,eAAe,IAAO,KAAa,QAAyC;EAE1E,QAAO,MADgB,UAAU,IAAO,aAAa,GAAG,GAAG,cAAc,MAAM,CAAC,EAAA,CAChE;CAClB;CAEA,eAAe,KAAQ,KAAa,MAAgB,QAAyC;EAE3F,QAAO,MADgB,UAAU,KAAQ,aAAa,GAAG,GAAG,MAAM,cAAc,MAAM,CAAC,EAAA,CACvE;CAClB;CAEA,eAAe,IAAO,KAAa,MAAgB,QAAyC;EAE1F,QAAO,MADgB,UAAU,IAAO,aAAa,GAAG,GAAG,MAAM,cAAc,MAAM,CAAC,EAAA,CACtE;CAClB;CAEA,eAAe,MAAS,KAAa,MAAgB,QAAyC;EAE5F,QAAO,MADgB,UAAU,MAAS,aAAa,GAAG,GAAG,MAAM,cAAc,MAAM,CAAC,EAAA,CACxE;CAClB;CAEA,eAAe,IAAO,KAAa,QAAyC;EAE1E,QAAO,MADgB,UAAU,OAAU,aAAa,GAAG,GAAG,cAAc,MAAM,CAAC,EAAA,CACnE;CAClB;CAGA,eAAe,OAAU,KAAa,MAAY,YAAY,QAAQ,gBAAsD;EAC1H,MAAM,WAAW,IAAI,SAAS;EAC9B,SAAS,OAAO,WAAW,IAAI;EAE/B,IAAI,gBACF,OAAO,QAAQ,cAAc,CAAC,CAAC,SAAS,CAAC,KAAK,WAAW;GACvD,IAAI,UAAU,KAAA,KAAa,UAAU,MACnC,SAAS,OAAO,KAAK,OAAO,UAAU,WAAW,KAAK,UAAU,KAAK,IAAI,OAAO,KAAK,CAAC;EAE1F,CAAC;EAOH,QAAO,MAJgB,UAAU,KAAQ,aAAa,GAAG,GAAG,UAAU,cAAc,EAElF,SAAS,EAAE,gBAAgB,KAAA,EAAU,EACvC,CAAC,CAAC,EAAA,CACc;CAClB;CAGA,eAAe,SAAS,KAAa,UAAoC;EACvE,MAAM,WAAW,MAAM,UAAU,IAAI,aAAa,GAAG,GAAG,cAAc,EAAE,cAAc,OAAO,CAAC,CAAC;EAC/F,MAAM,OAAO,IAAI,KAAK,CAAC,SAAS,IAAI,CAAC;EACrC,MAAM,UAAU,IAAI,gBAAgB,IAAI;EAGxC,IAAI,UAAU;GACZ,MAAM,OAAO,SAAS,cAAc,GAAG;GACvC,KAAK,OAAO;GACZ,KAAK,WAAW;GAChB,SAAS,KAAK,YAAY,IAAI;GAC9B,KAAK,MAAM;GACX,SAAS,KAAK,YAAY,IAAI;GAG9B,iBAAiB,IAAI,gBAAgB,OAAO,GAAG,GAAG;GAClD,OAAO;EACT;EAGA,OAAO;CACT;CAGA,SAAS,SAAS,MAAsB;EACtC,MAAM,UAAU,WAAW;EAC3B,OAAO,YAAY,SAAS,oBAAoB,SAAS,IAAI,CAAC;CAChE;CAGA,SAAS,WAAW,MAAsB;EACxC,OAAO,YAAY,cAAc,aAAa,GAAG,IAAI;CACvD;CAEA,OAAO;EACL,QAAQ;EACR;EACA;EACA;EACA;EACA,QAAQ;EACR;EACA;EACA;EACA;CACF;AACF;;;;;;;;;;;AClWA,SAAgB,iBAAiB,OAAgB,WAAW,mBAA2B;CACrF,IAAI,iBAAiB,SAAS,MAAM,SAClC,OAAO,MAAM;CAGf,IAAI,OAAO,UAAU,YAAY,MAAM,KAAK,GAC1C,OAAO;CAGT,IACE,OAAO,UAAU,YACd,UAAU,QACV,aAAa,SACb,OAAQ,MAAgC,YAAY,YACnD,MAA8B,QAAQ,KAAK,GAE/C,OAAQ,MAA8B;CAGxC,OAAO;AACT;;;ACtBA,SAAS,oBAAwC;CAC/C,IAAI,OAAO,WAAW,aAAa,OAAO,KAAA;CAC1C,OAAQ,OAA8D,mBAClE;AACN;;;;;;;;;AAUA,IAAa,qBAAqB,YAAY,yBAAyB;CACrE,MAAM,UAAU,IAA8B,IAAI;CAClD,MAAM,YAAY,IAAmB,IAAI;CACzC,MAAM,cAAc,IAAI,KAAK;CAC7B,MAAM,QAAQ,IAAmB,IAAI;CAKrC,MAAM,2BAAW,IAAI,IAAwC;CAM7D,IAAI,iBAAiB;CAErB,SAAS,gBAAgB,IAAwC;EAC/D,MAAM,UAAU,SAAS,IAAI,EAAE;EAC/B,IAAI,SAAS,OAAO;EAEpB,MAAM,OAAO,kBAAkB,KAAK;EACpC,MAAM,UAAU,OAAO,CAAC,CACrB,IAAuB,GAAG,KAAK,eAAe,IAAI,CAAC,CACnD,cAAc;GACb,SAAS,OAAO,EAAE;EACpB,CAAC;EAEH,SAAS,IAAI,IAAI,OAAO;EACxB,OAAO;CACT;;CAGA,SAAS,OAAO,YAAqC;EACnD,kBAAkB;EAClB,QAAQ,QAAQ;EAChB,UAAU,QAAQ,WAAW;EAC7B,MAAM,QAAQ;EACd,YAAY,QAAQ;CACtB;;;;;;CAOA,eAAe,WAAW,IAA+C;EACvE,MAAM,yBAAyB,YAAY,SAAS,UAAU,UAAU;EACxE,MAAM,QAAQ,yBAAyB,iBAAkB,kBAAkB;EAC3E,IAAI,CAAC,wBAAwB;GAC3B,QAAQ,QAAQ;GAChB,UAAU,QAAQ;GAClB,MAAM,QAAQ;GACd,YAAY,QAAQ;EACtB;EACA,IAAI;GACF,MAAM,aAAa,MAAM,gBAAgB,EAAE;GAI3C,IAAI,UAAU,gBAAgB,OAAO;GACrC,QAAQ,QAAQ;GAChB,OAAO;EACT,SAAS,KAAK;GACZ,IAAI,UAAU,gBAAgB,OAAO;GACrC,QAAQ,QAAQ;GAChB,MAAM,QAAQ,iBAAiB,KAAK,2BAA2B;GAC/D,OAAO;EACT,UAAU;GAGR,IAAI,UAAU,gBAAgB,YAAY,QAAQ;EACpD;CACF;CAEA,SAAS,QAAc;EAGrB,kBAAkB;EAClB,QAAQ,QAAQ;EAChB,UAAU,QAAQ;EAClB,MAAM,QAAQ;EACd,YAAY,QAAQ;CACtB;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF,CAAC"}
1
+ {"version":3,"file":"experiment-D49y_Kro.js","names":[],"sources":["../src/stores/auth.ts","../src/composables/useApi.ts","../src/utils/errors.ts","../src/stores/experiment.ts"],"sourcesContent":["import { defineStore } from 'pinia'\nimport { ref, computed } from 'vue'\nimport type { AuthConfig, UserInfo } from '../types'\nimport {\n canAccessAdmin as canUserAccessAdmin,\n canAccessPlugin as canUserAccessPlugin,\n getAccessAudience,\n getUserPermissions,\n hasAllPermissions,\n hasAnyPermission,\n isAdminUser,\n} from '../permissions'\n\nconst AUTH_TOKEN_KEY = 'mint-auth-token'\nconst AUTH_EXPIRES_KEY = 'mint-auth-expires'\n\nfunction getLocalStorage(): Storage | null {\n try {\n const storage = globalThis.localStorage\n return typeof storage?.getItem === 'function' ? storage : null\n } catch {\n return null\n }\n}\n\nfunction readStoredItem(key: string): string | null {\n try {\n return getLocalStorage()?.getItem(key) ?? null\n } catch {\n return null\n }\n}\n\nfunction writeStoredItem(key: string, value: string): void {\n try {\n getLocalStorage()?.setItem(key, value)\n } catch {\n // Keep auth usable in-memory when browser storage is blocked.\n }\n}\n\nfunction removeStoredItem(key: string): void {\n try {\n getLocalStorage()?.removeItem(key)\n } catch {\n // Keep auth cleanup usable in-memory when browser storage is blocked.\n }\n}\n\nexport const useAuthStore = defineStore('mint-auth', () => {\n // State\n const token = ref<string | null>(null)\n const tokenExpires = ref<Date | null>(null)\n const username = ref<string | null>(null)\n const userInfo = ref<UserInfo | null>(null)\n const authConfig = ref<AuthConfig>({\n authRequired: true,\n passkeyEnabled: false,\n passkeyRegistered: false,\n registrationEnabled: false,\n databaseMode: 'none',\n experimentVisibilityMode: 'open',\n })\n const isInitialized = ref(false)\n const isLoading = ref(false)\n const error = ref<string | null>(null)\n\n // Computed\n const isAuthenticated = computed(() => {\n if (!authConfig.value.authRequired) {\n return true\n }\n if (!token.value) {\n return false\n }\n if (tokenExpires.value && tokenExpires.value < new Date()) {\n return false\n }\n return true\n })\n\n const needsAuth = computed(() => {\n return authConfig.value.authRequired && !isAuthenticated.value\n })\n\n const isAdmin = computed(() => isAdminUser(userInfo.value))\n const userType = computed(() => getAccessAudience(userInfo.value))\n const permissions = computed(() => getUserPermissions(userInfo.value))\n const canAccessAdmin = computed(() => canUserAccessAdmin(userInfo.value))\n\n const canRegister = computed(() => {\n return authConfig.value.registrationEnabled\n })\n\n // Actions\n function initialize() {\n const storedToken = readStoredItem(AUTH_TOKEN_KEY)\n const storedExpires = readStoredItem(AUTH_EXPIRES_KEY)\n\n if (storedToken) {\n token.value = storedToken\n\n if (storedExpires) {\n const expires = new Date(storedExpires)\n if (expires > new Date()) {\n tokenExpires.value = expires\n } else {\n clearToken()\n }\n }\n }\n\n isInitialized.value = true\n }\n\n function setToken(accessToken: string, expiresIn: number) {\n token.value = accessToken\n const expires = new Date(Date.now() + expiresIn * 1000)\n tokenExpires.value = expires\n\n writeStoredItem(AUTH_TOKEN_KEY, accessToken)\n writeStoredItem(AUTH_EXPIRES_KEY, expires.toISOString())\n }\n\n function clearToken() {\n token.value = null\n tokenExpires.value = null\n username.value = null\n userInfo.value = null\n\n removeStoredItem(AUTH_TOKEN_KEY)\n removeStoredItem(AUTH_EXPIRES_KEY)\n }\n\n function setUserInfo(info: UserInfo) {\n userInfo.value = info\n username.value = info.username\n }\n\n function hasPermission(...perms: string[]): boolean {\n return hasAllPermissions(userInfo.value, perms)\n }\n\n function hasAnyPermissionForUser(...perms: string[]): boolean {\n return hasAnyPermission(userInfo.value, perms)\n }\n\n function canAccessPlugin(pluginName: string): boolean {\n return canUserAccessPlugin(userInfo.value, pluginName)\n }\n\n function setAuthConfig(config: AuthConfig) {\n authConfig.value = config\n }\n\n function setUsername(name: string) {\n username.value = name\n }\n\n function setError(message: string | null) {\n error.value = message\n }\n\n function setLoading(loading: boolean) {\n isLoading.value = loading\n }\n\n function logout() {\n clearToken()\n }\n\n return {\n // State\n token,\n tokenExpires,\n username,\n userInfo,\n authConfig,\n isInitialized,\n isLoading,\n error,\n\n // Computed\n isAuthenticated,\n needsAuth,\n isAdmin,\n userType,\n permissions,\n canAccessAdmin,\n canRegister,\n\n // Actions\n initialize,\n setToken,\n clearToken,\n setAuthConfig,\n setUsername,\n setUserInfo,\n setError,\n setLoading,\n hasPermission,\n hasAnyPermission: hasAnyPermissionForUser,\n canAccessPlugin,\n logout,\n }\n})\n","import axios, { type AxiosInstance, type AxiosRequestConfig, type InternalAxiosRequestConfig } from 'axios'\nimport { useSettingsStore } from '../stores/settings'\nimport { useAuthStore } from '../stores/auth'\nimport { MintApiError, mintApiErrorFromResponse } from '../apiErrors'\n\nlet apiClientInstance: AxiosInstance | null = null\nlet interceptorAttached = false\n\ninterface MintAxiosRequestConfig extends AxiosRequestConfig {\n _mintSkipAuth?: boolean\n _mintAuthToken?: string\n _mintTypedErrors?: boolean\n}\n\ninterface MintInternalAxiosRequestConfig extends InternalAxiosRequestConfig {\n _mintSkipAuth?: boolean\n _mintAuthToken?: string\n _mintTypedErrors?: boolean\n}\n\ntype MutableHeaders = Record<string, unknown> & {\n has?: (header: string) => boolean\n set?: (header: string, value: string) => void\n delete?: (header: string) => void\n}\n\nfunction joinUrlPath(baseUrl: string, path: string): string {\n if (/^https?:\\/\\//i.test(path)) return path\n if (!path) return baseUrl\n if (path.startsWith('?') || path.startsWith('#')) return `${baseUrl.replace(/\\/+$/, '')}${path}`\n const normalizedBase = baseUrl.replace(/\\/+$/, '')\n const normalizedPath = path.replace(/^\\/+/, '/')\n return `${normalizedBase}${normalizedPath.startsWith('/') ? normalizedPath : `/${normalizedPath}`}`\n}\n\nfunction getBasePath(baseUrl: string): string {\n if (!baseUrl) return '/'\n\n try {\n const origin = typeof window !== 'undefined' ? window.location.origin : 'http://localhost'\n return new URL(baseUrl, origin).pathname.replace(/\\/+$/, '') || '/'\n } catch {\n const path = baseUrl.replace(/^https?:\\/\\/[^/]+/i, '')\n return path.replace(/\\/+$/, '') || '/'\n }\n}\n\nfunction normalizeRequestUrl(baseUrl: string, url: string): string {\n if (!url || /^https?:\\/\\//.test(url)) return url\n\n const basePath = getBasePath(baseUrl)\n if (basePath === '/') return url\n\n const normalizedUrl = url.startsWith('/') ? url : `/${url}`\n if (\n normalizedUrl === basePath\n || normalizedUrl.startsWith(`${basePath}?`)\n || normalizedUrl.startsWith(`${basePath}#`)\n ) {\n return normalizedUrl.slice(basePath.length)\n }\n if (normalizedUrl.startsWith(`${basePath}/`)) {\n return normalizedUrl.slice(basePath.length) || ''\n }\n return url\n}\n\nfunction asMutableHeaders(headers: AxiosRequestConfig['headers']): MutableHeaders | null {\n return headers ? (headers as MutableHeaders) : null\n}\n\nfunction hasAuthorizationHeader(headers: AxiosRequestConfig['headers']): boolean {\n const bag = asMutableHeaders(headers)\n if (!bag) return false\n if (typeof bag.has === 'function') return bag.has('Authorization')\n return Object.keys(bag).some((key) => key.toLowerCase() === 'authorization')\n}\n\nfunction setAuthorizationHeader(headers: AxiosRequestConfig['headers'], value: string): void {\n const bag = asMutableHeaders(headers)\n if (!bag) return\n if (typeof bag.set === 'function') {\n bag.set('Authorization', value)\n return\n }\n bag.Authorization = value\n}\n\nfunction deleteAuthorizationHeader(headers: AxiosRequestConfig['headers']): void {\n const bag = asMutableHeaders(headers)\n if (!bag) return\n if (typeof bag.delete === 'function') {\n bag.delete('Authorization')\n return\n }\n for (const key of Object.keys(bag)) {\n if (key.toLowerCase() === 'authorization') delete bag[key]\n }\n}\n\nfunction getAuthorizationHeader(headers: AxiosRequestConfig['headers']): unknown {\n const bag = asMutableHeaders(headers) as (MutableHeaders & { get?: (header: string) => unknown }) | null\n if (!bag) return undefined\n if (typeof bag.get === 'function') return bag.get('Authorization')\n const key = Object.keys(bag).find((candidate) => candidate.toLowerCase() === 'authorization')\n return key ? bag[key] : undefined\n}\n\n/**\n * Raised when an authenticated SDK request is rejected because the login is no\n * longer valid. It is also a `MintApiError` (status 401), so typed-error callers\n * and `instanceof AuthenticationRequiredError` callers both recognise it.\n */\nexport class AuthenticationRequiredError extends MintApiError {\n override readonly name: string = 'AuthenticationRequiredError'\n declare readonly code: 'AUTHENTICATION_REQUIRED'\n declare readonly status: 401\n\n constructor(cause?: unknown, envelope?: MintApiError) {\n super(\n 'Authentication required. Please sign in again.',\n 'AUTHENTICATION_REQUIRED',\n 401,\n envelope?.requestId ?? null,\n envelope?.details ?? null,\n envelope?.detail,\n envelope?.body ?? null,\n cause,\n )\n }\n}\n\n/** @internal Convert a rejected authenticated request into the SDK's public auth error. */\nexport function authenticationRequiredError(\n cause: unknown,\n rejectedToken?: string,\n envelope?: MintApiError,\n): AuthenticationRequiredError {\n const currentAuthStore = useAuthStore()\n if (rejectedToken && currentAuthStore.token === rejectedToken) {\n currentAuthStore.clearToken()\n }\n return new AuthenticationRequiredError(cause, envelope)\n}\n\nfunction getApiClient(): AxiosInstance {\n if (!apiClientInstance) {\n apiClientInstance = axios.create({\n headers: {\n 'Content-Type': 'application/json',\n },\n })\n }\n return apiClientInstance\n}\n\nexport interface ApiClientOptions {\n baseUrl?: string\n timeout?: number\n withAuth?: boolean\n /** Normalize failed responses into MintApiError. Generated clients enable this by default. */\n typedErrors?: boolean\n}\n\nexport interface UseApiReturn {\n client: AxiosInstance\n get: <T>(url: string, config?: AxiosRequestConfig) => Promise<T>\n post: <T>(url: string, data?: unknown, config?: AxiosRequestConfig) => Promise<T>\n put: <T>(url: string, data?: unknown, config?: AxiosRequestConfig) => Promise<T>\n patch: <T>(url: string, data?: unknown, config?: AxiosRequestConfig) => Promise<T>\n delete: <T>(url: string, config?: AxiosRequestConfig) => Promise<T>\n upload: <T>(url: string, file: File, fieldName?: string, additionalData?: Record<string, unknown>) => Promise<T>\n download: (url: string, filename?: string) => Promise<string>\n buildUrl: (path: string) => string\n buildWsUrl: (path: string) => string\n}\n\n/** Axios-based API client that injects the plugin base URL and JWT auth header on every request. */\nexport function useApi(options: ApiClientOptions = {}): UseApiReturn {\n const settingsStore = useSettingsStore()\n const authStore = useAuthStore()\n const apiClient = getApiClient()\n\n // Ensure auth store is initialized (reads token from localStorage)\n if (!authStore.isInitialized) {\n authStore.initialize()\n }\n\n // Attach auth interceptor only once (reads token dynamically, not from closure)\n if (!interceptorAttached) {\n apiClient.interceptors.request.use((config) => {\n const request = config as MintInternalAxiosRequestConfig\n if (request._mintSkipAuth) {\n deleteAuthorizationHeader(request.headers)\n return request\n }\n\n const currentAuthStore = useAuthStore()\n if (currentAuthStore.token && config.headers && !hasAuthorizationHeader(config.headers)) {\n setAuthorizationHeader(config.headers, `Bearer ${currentAuthStore.token}`)\n }\n if (getAuthorizationHeader(config.headers) === `Bearer ${currentAuthStore.token}`) {\n request._mintAuthToken = currentAuthStore.token ?? undefined\n }\n return config\n })\n apiClient.interceptors.response.use(\n (response) => response,\n async (error: unknown) => {\n if (axios.isAxiosError(error) && error.response) {\n const request = error.config as MintInternalAxiosRequestConfig | undefined\n if (request?._mintTypedErrors) {\n let payload: unknown = error.response.data\n if (payload instanceof Blob) {\n try {\n payload = JSON.parse(await payload.text())\n } catch {\n // Keep non-JSON download errors available as the original body.\n }\n }\n const typed = mintApiErrorFromResponse(\n error.response.status,\n payload,\n {\n requestId: String(error.response.headers?.['x-request-id'] ?? '') || null,\n cause: error,\n },\n )\n if (error.response.status === 401 && request._mintSkipAuth !== true) {\n return Promise.reject(authenticationRequiredError(error, request._mintAuthToken, typed))\n }\n return Promise.reject(typed)\n }\n if (error.response.status === 401) {\n if (request?._mintSkipAuth !== true) {\n return Promise.reject(authenticationRequiredError(error, request?._mintAuthToken))\n }\n }\n }\n return Promise.reject(error)\n },\n )\n interceptorAttached = true\n }\n\n function getBaseUrl(): string {\n return options.baseUrl ?? settingsStore.getApiBaseUrl()\n }\n\n function normalizeUrl(url: string): string {\n return normalizeRequestUrl(getBaseUrl(), url)\n }\n\n // Build per-request config that applies this caller's options\n function requestConfig(config?: AxiosRequestConfig): MintAxiosRequestConfig {\n const base: MintAxiosRequestConfig = {\n baseURL: getBaseUrl(),\n timeout: options.timeout ?? settingsStore.requestTimeout,\n _mintTypedErrors: options.typedErrors,\n ...config,\n }\n // Strip auth header if explicitly disabled\n if (options.withAuth === false) {\n base._mintSkipAuth = true\n deleteAuthorizationHeader(base.headers)\n }\n return base\n }\n\n // Generic request methods\n async function get<T>(url: string, config?: AxiosRequestConfig): Promise<T> {\n const response = await apiClient.get<T>(normalizeUrl(url), requestConfig(config))\n return response.data\n }\n\n async function post<T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T> {\n const response = await apiClient.post<T>(normalizeUrl(url), data, requestConfig(config))\n return response.data\n }\n\n async function put<T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T> {\n const response = await apiClient.put<T>(normalizeUrl(url), data, requestConfig(config))\n return response.data\n }\n\n async function patch<T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T> {\n const response = await apiClient.patch<T>(normalizeUrl(url), data, requestConfig(config))\n return response.data\n }\n\n async function del<T>(url: string, config?: AxiosRequestConfig): Promise<T> {\n const response = await apiClient.delete<T>(normalizeUrl(url), requestConfig(config))\n return response.data\n }\n\n // File upload helper\n async function upload<T>(url: string, file: File, fieldName = 'file', additionalData?: Record<string, unknown>): Promise<T> {\n const formData = new FormData()\n formData.append(fieldName, file)\n\n if (additionalData) {\n Object.entries(additionalData).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n formData.append(key, typeof value === 'object' ? JSON.stringify(value) : String(value))\n }\n })\n }\n\n const response = await apiClient.post<T>(normalizeUrl(url), formData, requestConfig({\n // Let Axios set Content-Type with the correct multipart boundary\n headers: { 'Content-Type': undefined },\n }))\n return response.data\n }\n\n // Download helper - returns blob URL\n async function download(url: string, filename?: string): Promise<string> {\n const response = await apiClient.get(normalizeUrl(url), requestConfig({ responseType: 'blob' }))\n const blob = new Blob([response.data])\n const blobUrl = URL.createObjectURL(blob)\n\n // Optionally trigger download\n if (filename) {\n const link = document.createElement('a')\n link.href = blobUrl\n link.download = filename\n document.body.appendChild(link)\n link.click()\n document.body.removeChild(link)\n // Revoke after a short delay to allow download to start;\n // return empty string since the URL will be invalidated\n setTimeout(() => URL.revokeObjectURL(blobUrl), 100)\n return ''\n }\n\n // Caller is responsible for revoking when no filename is provided\n return blobUrl\n }\n\n // Build full URL for external use (e.g., <a href=\"...\">)\n function buildUrl(path: string): string {\n const baseUrl = getBaseUrl()\n return joinUrlPath(baseUrl, normalizeRequestUrl(baseUrl, path))\n }\n\n // WebSocket URL builder\n function buildWsUrl(path: string): string {\n return joinUrlPath(settingsStore.getWsBaseUrl(), path)\n }\n\n return {\n client: apiClient,\n get,\n post,\n put,\n patch,\n delete: del,\n upload,\n download,\n buildUrl,\n buildWsUrl,\n }\n}\n","/**\n * The one place a thrown value is turned into a developer-facing message.\n *\n * Five copies of this logic drifted apart across the composables and stores\n * (some checked a bare `{ message }` object, some did not); this export is the\n * union of what they all accepted, so every caller now reads the same message\n * from the same value.\n */\nexport function readErrorMessage(value: unknown, fallback = 'Request failed.'): string {\n if (value instanceof Error && value.message) {\n return value.message\n }\n\n if (typeof value === 'string' && value.trim()) {\n return value\n }\n\n if (\n typeof value === 'object'\n && value !== null\n && 'message' in value\n && typeof (value as { message?: unknown }).message === 'string'\n && (value as { message: string }).message.trim()\n ) {\n return (value as { message: string }).message\n }\n\n return fallback\n}\n","import { defineStore } from 'pinia'\nimport { ref } from 'vue'\nimport { useApi } from '../composables/useApi'\nimport { readErrorMessage } from '../utils/errors'\nimport type { ExperimentSummary, PlatformContext } from '../types'\n\nfunction getPlatformApiUrl(): string | undefined {\n if (typeof window === 'undefined') return undefined\n return (window as unknown as { __MINT_PLATFORM__?: PlatformContext }).__MINT_PLATFORM__\n ?.platformApiUrl\n}\n\n/**\n * The single source of truth for the experiment a plugin is currently working on.\n *\n * Both entry points land here: `ExperimentSelectorModal` calls `select()` with a\n * record the platform already returned, and deep links call `selectById()` with\n * nothing but an id. Because a name can only ever arrive from the platform,\n * there is no code path in which a caller can invent one.\n */\nexport const useExperimentStore = defineStore('mint-experiment', () => {\n const current = ref<ExperimentSummary | null>(null)\n const currentId = ref<number | null>(null)\n const isResolving = ref(false)\n const error = ref<string | null>(null)\n\n // In-flight requests only. Resolved records are deliberately not memoised, so\n // a rename is picked up on the next resolve instead of persisting for the\n // lifetime of the page.\n const inFlight = new Map<number, Promise<ExperimentSummary>>()\n\n // Monotonic token guarding every write to the shared selection. Responses can\n // land in any order, and a late one must never overwrite a newer choice —\n // that would display a name belonging to a different experiment, which is the\n // failure this store exists to prevent.\n let selectionToken = 0\n\n function fetchExperiment(id: number): Promise<ExperimentSummary> {\n const pending = inFlight.get(id)\n if (pending) return pending\n\n const base = getPlatformApiUrl() ?? ''\n const request = useApi()\n .get<ExperimentSummary>(`${base}/experiments/${id}`)\n .finally(() => {\n inFlight.delete(id)\n })\n\n inFlight.set(id, request)\n return request\n }\n\n /** Adopt a record the platform already returned, without a further request. */\n function select(experiment: ExperimentSummary): void {\n selectionToken += 1\n current.value = experiment\n currentId.value = experiment.id\n error.value = null\n isResolving.value = false\n }\n\n /**\n * Bind to an experiment known only by id, resolving its record from the\n * platform. On failure the binding is remembered but `current` stays null, so\n * callers can distinguish \"not resolved yet\" from \"nothing selected\".\n */\n async function selectById(id: number): Promise<ExperimentSummary | null> {\n const joinsCurrentResolution = isResolving.value && currentId.value === id\n const token = joinsCurrentResolution ? selectionToken : (selectionToken += 1)\n if (!joinsCurrentResolution) {\n current.value = null\n currentId.value = id\n error.value = null\n isResolving.value = true\n }\n try {\n const experiment = await fetchExperiment(id)\n // A newer selection superseded this one while the request was in flight.\n // Returning null tells the caller its result is no longer the selection,\n // so it cannot be applied downstream either.\n if (token !== selectionToken) return null\n current.value = experiment\n return experiment\n } catch (err) {\n if (token !== selectionToken) return null\n current.value = null\n error.value = readErrorMessage(err, 'Failed to load experiment')\n return null\n } finally {\n // Only the newest request owns the loading flag; otherwise a superseded\n // response would clear it while the current one is still pending.\n if (token === selectionToken) isResolving.value = false\n }\n }\n\n function clear(): void {\n // Supersede any in-flight resolve so a detached experiment cannot be\n // repopulated by a response that was already on its way.\n selectionToken += 1\n current.value = null\n currentId.value = null\n error.value = null\n isResolving.value = false\n }\n\n return {\n current,\n currentId,\n isResolving,\n error,\n select,\n selectById,\n clear,\n }\n})\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,14 +1,14 @@
1
- import { $ as PlateEditor_default, A as SampleLegend_default, At as AppToastContainer_default, B as StatusIndicator_default, C as ChemicalFormula_default, Ct as AppAvatarMenu_default, D as ReagentEditor_default, Dt as ActionMenu_default, E as SampleHierarchyTree_default, Et as ThemeToggle_default, F as SettingsModal_default, Ft as Calendar_default, G as ControlWorkspaceView_default, H as LayoutResizeHandle_default, I as AdductText_default, It as BasePill_default, J as SampleSelector_default, K as ComponentBindingRenderer_default, L as Breadcrumb_default, Lt as SegmentedControl_default, M as BioTemplatePackWorkspaceView_default, Mt as FormField_default, N as BioTemplateExperimentWorkspaceView_default, Nt as DataFrame_default, O as SmartGroupModal_default, Ot as IconButton_default, P as BioTemplateRenderer_default, Pt as LoadingSpinner_default, Q as ReagentList_default, R as Avatar_default, Rt as BaseTabs_default, S as sequenceSamplesRemaining, St as AppTopBar_default, T as ProtocolStepEditor_default, Tt as CollapsibleCard_default, U as AppContainer_default, V as Divider_default, W as DoseDesignWorkspaceView_default, X as SmartGroupManual_default, Y as AutoGroupModal_default, Z as SmartGroupFieldRecipe_default, _ as estimateSequenceFinishDate, _t as AppLayout_default, a as ArtifactSaveDialog_default, at as GeneratedPluginView_default, b as formatSequenceRemaining, bt as FormActions_default, c as SectionCard_default, ct as GeneratedPlotlyResult_default, d as ExperimentCodeBadge_default, dt as ChartContainer_default, et as ConfirmDialog_default, f as ExperimentDataViewer_default, ft as generatedJobDefaults, g as SequenceProgressBar_default, gt as MobileSupportGate_default, h as InstrumentStateBadge_default, ht as PluginWorkspaceView_default, i as ExperimentPopover_default, it as DoseCalculator_default, j as BioTemplatePresetWorkspaceView_default, jt as AlertBox_default, k as GroupAssigner_default, kt as RuntimeUpdateNotice_default, l as ResourceCard_default, lt as PlotlyChart_default, m as AuditTrail_default, mt as normalizeGeneratedJobInput, n as FilePicker_default, nt as sampleTypeMarker, o as ArtifactSelectorModal_default, ot as JobsStatusTray_default, p as BatchProgressList_default, pt as generatedJobFormSchema, q as ScheduleCalendar_default, r as FitPanel_default, rt as ExperimentTimeline_default, s as ExperimentSelectorModal_default, st as GeneratedResultRenderer_default, tt as WellPlate_default, u as TimeRangeInput_default, ut as mintPlotlyTemplate, v as estimateSequenceRemainingSeconds, vt as AppSidebar_default, w as ScientificNumber_default, wt as PluginIcon_default, x as sequenceProgressPercent, xt as StepWizard_default, y as formatSequenceEta, yt as FormBuilder_default, z as ProgressBar_default, zt as SearchableSelect_default } from "./components-CEePoD3W.js";
2
- import { a as tryUseSettingsStore, c as mintApiErrorFromResponse, i as tableDensityToSize, n as colorPalettes, o as useSettingsStore, r as paletteCssVariables, s as MintApiError, t as PALETTE_CSS_VARIABLES } from "./settings-BLr8lzw6.js";
1
+ import { $ as PlateEditor_default, A as SampleLegend_default, At as AppToastContainer_default, B as StatusIndicator_default, C as ChemicalFormula_default, Ct as AppAvatarMenu_default, D as ReagentEditor_default, Dt as ActionMenu_default, E as SampleHierarchyTree_default, Et as ThemeToggle_default, F as SettingsModal_default, Ft as Calendar_default, G as ControlWorkspaceView_default, H as LayoutResizeHandle_default, I as AdductText_default, It as BasePill_default, J as SampleSelector_default, K as ComponentBindingRenderer_default, L as Breadcrumb_default, Lt as SegmentedControl_default, M as BioTemplatePackWorkspaceView_default, Mt as FormField_default, N as BioTemplateExperimentWorkspaceView_default, Nt as DataFrame_default, O as SmartGroupModal_default, Ot as IconButton_default, P as BioTemplateRenderer_default, Pt as LoadingSpinner_default, Q as ReagentList_default, R as Avatar_default, Rt as BaseTabs_default, S as sequenceSamplesRemaining, St as AppTopBar_default, T as ProtocolStepEditor_default, Tt as CollapsibleCard_default, U as AppContainer_default, V as Divider_default, W as DoseDesignWorkspaceView_default, X as SmartGroupManual_default, Y as AutoGroupModal_default, Z as SmartGroupFieldRecipe_default, _ as estimateSequenceFinishDate, _t as AppLayout_default, a as ArtifactSaveDialog_default, at as GeneratedPluginView_default, b as formatSequenceRemaining, bt as FormActions_default, c as SectionCard_default, ct as GeneratedPlotlyResult_default, d as ExperimentCodeBadge_default, dt as ChartContainer_default, et as ConfirmDialog_default, f as ExperimentDataViewer_default, ft as generatedJobDefaults, g as SequenceProgressBar_default, gt as MobileSupportGate_default, h as InstrumentStateBadge_default, ht as PluginWorkspaceView_default, i as ExperimentPopover_default, it as DoseCalculator_default, j as BioTemplatePresetWorkspaceView_default, jt as AlertBox_default, k as GroupAssigner_default, kt as RuntimeUpdateNotice_default, l as ResourceCard_default, lt as PlotlyChart_default, m as AuditTrail_default, mt as normalizeGeneratedJobInput, n as FilePicker_default, nt as sampleTypeMarker, o as ArtifactSelectorModal_default, ot as JobsStatusTray_default, p as BatchProgressList_default, pt as generatedJobFormSchema, q as ScheduleCalendar_default, r as FitPanel_default, rt as ExperimentTimeline_default, s as ExperimentSelectorModal_default, st as GeneratedResultRenderer_default, tt as WellPlate_default, u as TimeRangeInput_default, ut as mintPlotlyTemplate, v as estimateSequenceRemainingSeconds, vt as AppSidebar_default, w as ScientificNumber_default, wt as PluginIcon_default, x as sequenceProgressPercent, xt as StepWizard_default, y as formatSequenceEta, yt as FormBuilder_default, z as ProgressBar_default, zt as SearchableSelect_default } from "./components-BOA5e7kb.js";
2
+ import { a as tryUseSettingsStore, c as mintApiErrorFromResponse, i as tableDensityToSize, n as colorPalettes, o as useSettingsStore, r as paletteCssVariables, s as MintApiError, t as PALETTE_CSS_VARIABLES } from "./settings-BG6v13Zq.js";
3
3
  import { a as SORT_OPTIONS, c as formatExperimentDate, d as resolveExperimentCode, f as BaseModal_default, h as BaseButton_default, i as EXPERIMENT_STATUS_VARIANT_MAP, l as formatExperimentStatus, m as useEventListener, n as EXPERIMENT_STATUS_LABELS, o as datePresetToISO, p as useFocusTrap, r as EXPERIMENT_STATUS_OPTIONS, s as experimentIdFromRoute, t as DATE_PRESET_OPTIONS, u as getExperimentStatusVariant } from "./experiment-utils-DTANyVKV.js";
4
4
  import { i as BaseSelect_default, n as Skeleton_default, o as BaseInput_default, r as EmptyState_default } from "./ResourcePickerListInternal-CFAw4vTZ.js";
5
- import { $ as toTimeCourseDataFrame, $n as FormulaInput_default, $r as snapToSlot, $t as getBioTemplatePackInfo, A as createSamplePrepTemplate, An as getControlDefaults, Ar as isPluginSecretLocked, At as toCalibrationCurveDataFrame, B as toBioTemplateComponentProps, Bn as getDefaultControlView, Br as addMinutes, Bt as validateInstrumentRunData, C as createFlowCytometryPanelTemplate, Cr as FileUploader_default, Ct as toInstrumentRunRows, D as createTimeCourseTemplate, Dn as hslToHex, Dr as PLUGIN_SECRET_FORMAT_REF, Dt as toFlowPanelDataFrame, E as createProtocolStepsTemplate, En as hexToHsl, Er as PLUGIN_SECRET_FORMAT_KEY, Et as toFlowPanelColumns, Fn as controlsToSettingsSchema, Fr as pluginSecretState, Ft as toAssayMatrixSampleOptions, G as toTemplateDataFrame, Gn as getFieldRegistryEntry, Gr as formatDuration, Gt as validateSamplePrepData, H as toBioTemplateComponentPropsById, Hr as durationMinutes, Ht as validateProtocolStepsData, I as listBioTemplateComponentBindings, In as controlsToSidebarPanels, Ir as setPluginSecret, It as validateAssayMatrixData, J as toDoseConditions, Jn as UnitInput_default, Jr as fromMinutes, Jt as bioTemplatePresets, K as toWellMapArray, Kn as getTypeDefault, Kr as formatTime, Kt as validateSampleSheetData, L as toBioTemplateComponentBindings, Ln as controlsToTopBarSettingsConfig, Lr as usesPluginSecretReferences, Lt as validateCalibrationCurveData, M as createPlateMapTemplate, Mn as controlsToFormSchema, Mr as keepPluginSecret, Mt as toAssayMatrixColumns, N as bioTemplatePresetControlValuesToOptions, Nn as controlsToSectionFormSchema, Nr as pluginSecretId, Nt as toAssayMatrixDataFrame, O as createCalibrationCurveTemplate, On as defineControlComponentBindings, Or as PLUGIN_SECRET_REF_KEY, Ot as toFlowPanelRows, P as getBioTemplateComponentBindings, Pn as controlsToSectionFormSchemas, Pr as pluginSecretLabel, Pt as toAssayMatrixRows, Q as toTimeCourseColumns, Qn as useSequenceUtils, Qr as rangesOverlap, Qt as bioTemplatePacks, R as toBioTemplateComponentBindingsById, Rn as controlsToViewIds, Rr as TagsInput_default, Rt as validateDoseResponseData, S as createInstrumentRunTemplate, St as toInstrumentRunDataFrame, T as createAssayMatrixTemplate, Tn as deriveShade, Tr as SecretInput_default, Tt as toInstrumentRunSteps, U as toBioTemplateComponentSnippets, Ur as findAvailableSlots, Ut as validateQpcrPlateData, Vr as compareTime, Vt as validatePlateMapData, W as toBioTemplateComponentUsage, Wr as findNearestTimeSlotIndex, Wt as validateReagentListData, X as toPlateMapEditorState, Xn as MoleculeInput_default, Xr as isTimeInRange, Xt as listBioTemplatePresets, Y as toDoseLayoutState, Yn as useConcentrationUnits, Yr as generateTimeSlots, Yt as getBioTemplatePresetInfo, Z as toWellPlateWells, Zn as SequenceInput_default, Zr as parseTime, Zt as searchBioTemplatePresets, _ as createElisaAssayCollection, _r as resolveJobCapabilities, _t as toProtocolColumns, a as getBioTemplateControlDefaults, ai as useListSelection, an as searchBioTemplateCatalog, at as toSampleRows, b as createQpcrExpressionCollection, bt as toProtocolSteps, c as listBioTemplatePresetControlSchemas, ci as BaseRadioGroup_default, cn as createTemplateCollection, cr as resolveCurrentPluginId, ct as toSamplePrepRows, d as createBioTemplatePresetCollectionFromControls, di as Tooltip_default, dn as ensureTemplateFromCollection, dr as usePluginJobs, dt as toReagentListItems, ei as toMinutes, en as listBioTemplatePacks, er as ATOMIC_WEIGHTS, et as toTimeCourseRows, f as createWellPlateScreenCollection, fi as BaseTextarea_default, fn as extractTemplateCollection, fr as isActiveJobStatus, ft as toReagentRows, g as createFlowCytometryAssayCollection, gr as normalizeJobState, gt as toQpcrWellPlateWells, h as createWesternBlotAssayCollection, hr as normalizeJobPercent, ht as toQpcrRows, i as createBioTemplateControlToolkit, ii as MultiSelect_default, in as listBioTemplateCatalog, it as toSampleOptions, j as createSampleSheetTemplate, jn as useControlSchema, jr as isPluginSecretRef, jt as toCalibrationCurveRows, k as createDoseResponseTemplate, kn as defineControls, kr as clearPluginSecret, kt as toCalibrationCurveColumns, l as requireBioTemplateControlSchema, li as BaseToggle_default, ln as createTemplateEnvelope, lr as createGeneratedAnalysisTransport, lt as toReagentColumns, m as createDefaultBioTemplate, mr as jobStatusLabel, mt as toQpcrDataFrame, n as bioTemplateControlsToSectionFormSchemas, ni as DatePicker_default, nn as bioTemplateCatalog, nr as GeneratedPathInput_default, nt as toSampleColumns, o as getBioTemplateControlSchema, oi as useSelectionLimit, on as TEMPLATE_COLLECTION_KEY, ot as toSamplePrepColumns, p as createBioTemplatePackCollection, pn as getTemplateData, pr as isTerminalJobStatus, pt as toQpcrColumns, q as racksFromPlateState, qn as ConcentrationInput_default, qr as formatTimeSlot, qt as validateTimeCourseData, r as bioTemplateControlsToSidebarPanels, rn as getBioTemplateInfo, rt as toSampleDataFrame, s as listBioTemplateControlSchemas, si as BaseSlider_default, sn as assertTemplateEnvelope, st as toSamplePrepDataFrame, t as bioTemplateControlsToFormSchema, ti as useTimeUtils, tn as searchBioTemplatePacks, tr as useChemicalFormula, tt as toTimeCourseSteps, u as createBioTemplatePresetCollection, ui as BaseCheckbox_default, un as ensureTemplateEnvelope, ur as useGeneratedAnalysis, ut as toReagentDataFrame, v as createTargetedMetabolomicsCollection, vt as toProtocolDataFrame, w as createReagentListTemplate, wr as NumberInput_default, wt as toInstrumentRunScheduleEvents, x as createQpcrPlateTemplate, xr as useMenuKeyboard, xt as toInstrumentRunColumns, y as createLcmsBatchCollection, yr as DateTimePicker_default, yt as toProtocolRows, z as toBioTemplateComponentImports, zn as controlsToViewItems, zr as TimePicker_default, zt as validateFlowCytometryPanelData } from "./templates-BU1pet28.js";
6
- import { $ as downloadBlob, A as parseDelimitedText, At as useAppExperiment, Bt as useTextSearch, C as useExperimentSamples, Ct as defineControlModel, Dt as controlValuesToComponentProps, E as useSampleGroups, Et as controlValuesToComponentBindingsById, F as useAutoGroup, Ft as usePlatformContext, G as reorderList, Ht as useSortedItems, I as extractSampleNamesFromDesignData, It as useTheme, J as useDoseCalculator, K as useListReorder, L as extractSampleOptionsFromDesignData, Lt as useToast, M as useFileImport, Mt as useRuntimeAlignment, N as validateImportFile, Nt as getPresenceClientId, P as extractSamplesFromDesignData, Pt as usePresenceHeartbeat, Q as createPluginClient, R as unwrapExperimentDesignData, Rt as candidateMatchesSearch, S as useScheduleDrag, St as useControlWorkspace, T as useExpansionSet, Tt as controlValuesToComponentBindings, U as useRackEditor, Ut as useRovingFocus, Vt as compareSortValues, W as REORDER_ANNOUNCER_STYLE, X as useJobsStatusTray, Y as combinePluginJobSources, Z as usePluginJobCenter, _ as toBioTemplateComponentPropsByComponent, _t as defineDoseCalculatorControlProps, a as useExperimentSelector, at as usePluginClient, b as useTemplateCollection, bt as defineWellPlateDoseComponentBindings, c as DEFAULT_PRESETS, ct as buildPluginEndpointUrl, d as useReagentSeries, dt as usePluginWorkspace, et as downloadPluginEndpoint, f as useGroupAssignment, ft as DEFAULT_MOBILE_VIEWPORT_QUERY, g as getBioTemplateComponentProps, gt as useForm, h as useBioTemplateWorkspace, ht as evaluateCondition, i as useAnalysisArtifacts, it as useCurrentExperiment, j as readFileAsText, jt as getInjectedFrontendRevision, k as detectDelimiter, kt as APP_EXPERIMENT_KEY, l as DEFAULT_UNITS, lt as resolvePluginBaseUrl, m as useBioTemplatePackWorkspace, mt as useFormBuilder, nt as pluginFormDataFromPayload, o as useDebouncedWatch, ot as usePluginEventStream, p as useBioTemplatePresetWorkspace, pt as useMobileSupportGate, q as useReorderAnnouncer, rt as uploadPluginEndpoint, s as useProtocolTemplates, st as usePluginSettings, tt as getPluginPageSelectorItems, u as generateDilutionSeries, ut as useRequestSyncState, v as useBioTemplateComponents, vt as defineDoseDesignControlModel, w as useExperimentData, x as useExperimentSave, xt as defineWellPlateDoseControlProps, y as useBioTemplateControls, yt as defineWellPlateControlProps, z as DEFAULT_COLORS, zt as normalizeSearchQuery } from "./validation-CukfLjy-.js";
5
+ import { $ as toTimeCourseDataFrame, $n as FormulaInput_default, $r as snapToSlot, $t as getBioTemplatePackInfo, A as createSamplePrepTemplate, An as getControlDefaults, Ar as isPluginSecretLocked, At as toCalibrationCurveDataFrame, B as toBioTemplateComponentProps, Bn as getDefaultControlView, Br as addMinutes, Bt as validateInstrumentRunData, C as createFlowCytometryPanelTemplate, Cr as FileUploader_default, Ct as toInstrumentRunRows, D as createTimeCourseTemplate, Dn as hslToHex, Dr as PLUGIN_SECRET_FORMAT_REF, Dt as toFlowPanelDataFrame, E as createProtocolStepsTemplate, En as hexToHsl, Er as PLUGIN_SECRET_FORMAT_KEY, Et as toFlowPanelColumns, Fn as controlsToSettingsSchema, Fr as pluginSecretState, Ft as toAssayMatrixSampleOptions, G as toTemplateDataFrame, Gn as getFieldRegistryEntry, Gr as formatDuration, Gt as validateSamplePrepData, H as toBioTemplateComponentPropsById, Hr as durationMinutes, Ht as validateProtocolStepsData, I as listBioTemplateComponentBindings, In as controlsToSidebarPanels, Ir as setPluginSecret, It as validateAssayMatrixData, J as toDoseConditions, Jn as UnitInput_default, Jr as fromMinutes, Jt as bioTemplatePresets, K as toWellMapArray, Kn as getTypeDefault, Kr as formatTime, Kt as validateSampleSheetData, L as toBioTemplateComponentBindings, Ln as controlsToTopBarSettingsConfig, Lr as usesPluginSecretReferences, Lt as validateCalibrationCurveData, M as createPlateMapTemplate, Mn as controlsToFormSchema, Mr as keepPluginSecret, Mt as toAssayMatrixColumns, N as bioTemplatePresetControlValuesToOptions, Nn as controlsToSectionFormSchema, Nr as pluginSecretId, Nt as toAssayMatrixDataFrame, O as createCalibrationCurveTemplate, On as defineControlComponentBindings, Or as PLUGIN_SECRET_REF_KEY, Ot as toFlowPanelRows, P as getBioTemplateComponentBindings, Pn as controlsToSectionFormSchemas, Pr as pluginSecretLabel, Pt as toAssayMatrixRows, Q as toTimeCourseColumns, Qn as useSequenceUtils, Qr as rangesOverlap, Qt as bioTemplatePacks, R as toBioTemplateComponentBindingsById, Rn as controlsToViewIds, Rr as TagsInput_default, Rt as validateDoseResponseData, S as createInstrumentRunTemplate, St as toInstrumentRunDataFrame, T as createAssayMatrixTemplate, Tn as deriveShade, Tr as SecretInput_default, Tt as toInstrumentRunSteps, U as toBioTemplateComponentSnippets, Ur as findAvailableSlots, Ut as validateQpcrPlateData, Vr as compareTime, Vt as validatePlateMapData, W as toBioTemplateComponentUsage, Wr as findNearestTimeSlotIndex, Wt as validateReagentListData, X as toPlateMapEditorState, Xn as MoleculeInput_default, Xr as isTimeInRange, Xt as listBioTemplatePresets, Y as toDoseLayoutState, Yn as useConcentrationUnits, Yr as generateTimeSlots, Yt as getBioTemplatePresetInfo, Z as toWellPlateWells, Zn as SequenceInput_default, Zr as parseTime, Zt as searchBioTemplatePresets, _ as createElisaAssayCollection, _r as resolveJobCapabilities, _t as toProtocolColumns, a as getBioTemplateControlDefaults, ai as useListSelection, an as searchBioTemplateCatalog, at as toSampleRows, b as createQpcrExpressionCollection, bt as toProtocolSteps, c as listBioTemplatePresetControlSchemas, ci as BaseRadioGroup_default, cn as createTemplateCollection, cr as resolveCurrentPluginId, ct as toSamplePrepRows, d as createBioTemplatePresetCollectionFromControls, di as Tooltip_default, dn as ensureTemplateFromCollection, dr as usePluginJobs, dt as toReagentListItems, ei as toMinutes, en as listBioTemplatePacks, er as ATOMIC_WEIGHTS, et as toTimeCourseRows, f as createWellPlateScreenCollection, fi as BaseTextarea_default, fn as extractTemplateCollection, fr as isActiveJobStatus, ft as toReagentRows, g as createFlowCytometryAssayCollection, gr as normalizeJobState, gt as toQpcrWellPlateWells, h as createWesternBlotAssayCollection, hr as normalizeJobPercent, ht as toQpcrRows, i as createBioTemplateControlToolkit, ii as MultiSelect_default, in as listBioTemplateCatalog, it as toSampleOptions, j as createSampleSheetTemplate, jn as useControlSchema, jr as isPluginSecretRef, jt as toCalibrationCurveRows, k as createDoseResponseTemplate, kn as defineControls, kr as clearPluginSecret, kt as toCalibrationCurveColumns, l as requireBioTemplateControlSchema, li as BaseToggle_default, ln as createTemplateEnvelope, lr as createGeneratedAnalysisTransport, lt as toReagentColumns, m as createDefaultBioTemplate, mr as jobStatusLabel, mt as toQpcrDataFrame, n as bioTemplateControlsToSectionFormSchemas, ni as DatePicker_default, nn as bioTemplateCatalog, nr as GeneratedPathInput_default, nt as toSampleColumns, o as getBioTemplateControlSchema, oi as useSelectionLimit, on as TEMPLATE_COLLECTION_KEY, ot as toSamplePrepColumns, p as createBioTemplatePackCollection, pn as getTemplateData, pr as isTerminalJobStatus, pt as toQpcrColumns, q as racksFromPlateState, qn as ConcentrationInput_default, qr as formatTimeSlot, qt as validateTimeCourseData, r as bioTemplateControlsToSidebarPanels, rn as getBioTemplateInfo, rt as toSampleDataFrame, s as listBioTemplateControlSchemas, si as BaseSlider_default, sn as assertTemplateEnvelope, st as toSamplePrepDataFrame, t as bioTemplateControlsToFormSchema, ti as useTimeUtils, tn as searchBioTemplatePacks, tr as useChemicalFormula, tt as toTimeCourseSteps, u as createBioTemplatePresetCollection, ui as BaseCheckbox_default, un as ensureTemplateEnvelope, ur as useGeneratedAnalysis, ut as toReagentDataFrame, v as createTargetedMetabolomicsCollection, vt as toProtocolDataFrame, w as createReagentListTemplate, wr as NumberInput_default, wt as toInstrumentRunScheduleEvents, x as createQpcrPlateTemplate, xr as useMenuKeyboard, xt as toInstrumentRunColumns, y as createLcmsBatchCollection, yr as DateTimePicker_default, yt as toProtocolRows, z as toBioTemplateComponentImports, zn as controlsToViewItems, zr as TimePicker_default, zt as validateFlowCytometryPanelData } from "./templates-B2DB0MCM.js";
6
+ import { $ as downloadBlob, A as parseDelimitedText, At as useAppExperiment, Bt as useTextSearch, C as useExperimentSamples, Ct as defineControlModel, Dt as controlValuesToComponentProps, E as useSampleGroups, Et as controlValuesToComponentBindingsById, F as useAutoGroup, Ft as usePlatformContext, G as reorderList, Ht as useSortedItems, I as extractSampleNamesFromDesignData, It as useTheme, J as useDoseCalculator, K as useListReorder, L as extractSampleOptionsFromDesignData, Lt as useToast, M as useFileImport, Mt as useRuntimeAlignment, N as validateImportFile, Nt as getPresenceClientId, P as extractSamplesFromDesignData, Pt as usePresenceHeartbeat, Q as createPluginClient, R as unwrapExperimentDesignData, Rt as candidateMatchesSearch, S as useScheduleDrag, St as useControlWorkspace, T as useExpansionSet, Tt as controlValuesToComponentBindings, U as useRackEditor, Ut as useRovingFocus, Vt as compareSortValues, W as REORDER_ANNOUNCER_STYLE, X as useJobsStatusTray, Y as combinePluginJobSources, Z as usePluginJobCenter, _ as toBioTemplateComponentPropsByComponent, _t as defineDoseCalculatorControlProps, a as useExperimentSelector, at as usePluginClient, b as useTemplateCollection, bt as defineWellPlateDoseComponentBindings, c as DEFAULT_PRESETS, ct as buildPluginEndpointUrl, d as useReagentSeries, dt as usePluginWorkspace, et as downloadPluginEndpoint, f as useGroupAssignment, ft as DEFAULT_MOBILE_VIEWPORT_QUERY, g as getBioTemplateComponentProps, gt as useForm, h as useBioTemplateWorkspace, ht as evaluateCondition, i as useAnalysisArtifacts, it as useCurrentExperiment, j as readFileAsText, jt as getInjectedFrontendRevision, k as detectDelimiter, kt as APP_EXPERIMENT_KEY, l as DEFAULT_UNITS, lt as resolvePluginBaseUrl, m as useBioTemplatePackWorkspace, mt as useFormBuilder, nt as pluginFormDataFromPayload, o as useDebouncedWatch, ot as usePluginEventStream, p as useBioTemplatePresetWorkspace, pt as useMobileSupportGate, q as useReorderAnnouncer, rt as uploadPluginEndpoint, s as useProtocolTemplates, st as usePluginSettings, tt as getPluginPageSelectorItems, u as generateDilutionSeries, ut as useRequestSyncState, v as useBioTemplateComponents, vt as defineDoseDesignControlModel, w as useExperimentData, x as useExperimentSave, xt as defineWellPlateDoseControlProps, y as useBioTemplateControls, yt as defineWellPlateControlProps, z as DEFAULT_COLORS, zt as normalizeSearchQuery } from "./validation-C_eH_vIg.js";
7
7
  import { ADMIN_PANEL_PERMISSIONS, ADMIN_ROLE, canAccessAdmin, canAccessByPolicy, canAccessPlugin, getAccessAudience, getRoleInfo, getUserPermissions, hasAllPermissions, hasAnyPermission, isAdminRole, isAdminUser } from "./permissions.js";
8
- import { a as useApi, o as useAuthStore, r as AuthenticationRequiredError, t as useExperimentStore } from "./experiment-DgllIc3I.js";
8
+ import { a as useApi, o as useAuthStore, r as AuthenticationRequiredError, t as useExperimentStore } from "./experiment-D49y_Kro.js";
9
9
  import { t as ANALYSIS_FILE_ARTIFACT_SCHEMA } from "./analysisArtifactTypes-Cy3D0Ipg.js";
10
10
  import { MINTSdk } from "./install.js";
11
- import { a as buildAnalysisArtifactTargetQuery, c as resizedLeadingPanelWidth, d as useManualLayoutResize, f as resolvePluginFrontendBase, i as encodePlatformPickerPath, l as resizedTrailingPanelWidth, n as createFilePickerAdapter, o as parseAnalysisArtifactTarget, r as decodePlatformPickerPath, s as useFileBrowser, t as usePlatformFilePickerAdapter, u as resizedVerticalSplitPercent } from "./composables-BbHozlA7.js";
11
+ import { a as buildAnalysisArtifactTargetQuery, c as resizedLeadingPanelWidth, d as useManualLayoutResize, f as resolvePluginFrontendBase, i as encodePlatformPickerPath, l as resizedTrailingPanelWidth, n as createFilePickerAdapter, o as parseAnalysisArtifactTarget, r as decodePlatformPickerPath, s as useFileBrowser, t as usePlatformFilePickerAdapter, u as resizedVerticalSplitPercent } from "./composables-izSQH8Ky.js";
12
12
  import "./stores/index.js";
13
13
  //#region src/lcms.ts
14
14
  /** @deprecated Scheduled for removal in MINT 1.4; no SDK replacement. */
package/dist/install.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as components_exports } from "./components-CEePoD3W.js";
1
+ import { t as components_exports } from "./components-BOA5e7kb.js";
2
2
  //#region src/install.ts
3
3
  /**
4
4
  * Vue plugin that registers all MINT SDK components globally.
@@ -99,7 +99,7 @@ var colorPalettes = {
99
99
  /**
100
100
  * Custom properties a non-default palette overrides on `<html>`.
101
101
  *
102
- * The saturation/lightness pairs are lifted from the canonical indigo/orange
102
+ * The saturation/lightness pairs are lifted from the original indigo/orange
103
103
  * tokens in `styles/variables.css` so only the hue moves; the `default`
104
104
  * palette writes nothing at all and the stylesheet values stand.
105
105
  */
@@ -338,4 +338,4 @@ function tryUseSettingsStore() {
338
338
  //#endregion
339
339
  export { tryUseSettingsStore as a, mintApiErrorFromResponse as c, tableDensityToSize as i, colorPalettes as n, useSettingsStore as o, paletteCssVariables as r, MintApiError as s, PALETTE_CSS_VARIABLES as t };
340
340
 
341
- //# sourceMappingURL=settings-BLr8lzw6.js.map
341
+ //# sourceMappingURL=settings-BG6v13Zq.js.map