@morscherlab/mint-sdk 1.0.0-beta.6 → 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/__tests__/composables/useProtocolTemplates.test.d.ts +1 -0
  2. package/dist/__tests__/stores/settings.test.d.ts +1 -0
  3. package/dist/{auth-QQj2kkze.js → auth-CBG3bWEc.js} +50 -20
  4. package/dist/auth-CBG3bWEc.js.map +1 -0
  5. package/dist/components/SettingsModal.vue.d.ts +5 -0
  6. package/dist/components/index.js +2 -2
  7. package/dist/{components-DihbSJjU.js → components-5KSfsVqf.js} +49 -29
  8. package/dist/components-5KSfsVqf.js.map +1 -0
  9. package/dist/composables/index.js +3 -3
  10. package/dist/{composables-BcgZ6diz.js → composables-D4Myb30a.js} +3 -3
  11. package/dist/{composables-BcgZ6diz.js.map → composables-D4Myb30a.js.map} +1 -1
  12. package/dist/index.js +5 -5
  13. package/dist/install.js +2 -2
  14. package/dist/stores/index.js +1 -1
  15. package/dist/styles.css +16 -0
  16. package/dist/templates/index.js +1 -1
  17. package/dist/{templates-Cyt0Suwf.js → templates-BSlxwV2c.js} +12 -8
  18. package/dist/templates-BSlxwV2c.js.map +1 -0
  19. package/dist/{useExperimentData-CM6Y0u5L.js → useExperimentData-BbbdI5xT.js} +97 -25
  20. package/dist/useExperimentData-BbbdI5xT.js.map +1 -0
  21. package/package.json +1 -1
  22. package/src/__tests__/components/GroupAssigner.test.ts +18 -0
  23. package/src/__tests__/composables/useApi.test.ts +45 -0
  24. package/src/__tests__/composables/useAuth.test.ts +20 -0
  25. package/src/__tests__/composables/useProtocolTemplates.test.ts +64 -0
  26. package/src/__tests__/stores/settings.test.ts +78 -0
  27. package/src/components/AppAvatarMenu.vue +6 -3
  28. package/src/components/AppTopBar.vue +15 -10
  29. package/src/components/AuditTrail.vue +1 -1
  30. package/src/components/Calendar.vue +6 -2
  31. package/src/components/ConcentrationInput.vue +3 -2
  32. package/src/components/GroupAssigner.vue +8 -3
  33. package/src/components/NumberInput.vue +5 -3
  34. package/src/components/SampleHierarchyTree.vue +3 -2
  35. package/src/components/SettingsModal.vue +7 -0
  36. package/src/components/UnitInput.vue +6 -2
  37. package/src/components/WellPlate.vue +3 -3
  38. package/src/composables/useApi.ts +113 -16
  39. package/src/composables/useAutoGroup.ts +13 -8
  40. package/src/composables/useProtocolTemplates.ts +13 -1
  41. package/src/composables/useRackEditor.ts +3 -2
  42. package/src/stores/auth.ts +48 -23
  43. package/src/stores/settings.ts +10 -0
  44. package/src/styles/components/settings-modal.css +9 -0
  45. package/dist/auth-QQj2kkze.js.map +0 -1
  46. package/dist/components-DihbSJjU.js.map +0 -1
  47. package/dist/templates-Cyt0Suwf.js.map +0 -1
  48. package/dist/useExperimentData-CM6Y0u5L.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { $n as compareTime, An as defineWellPlateDoseControlProps, Bn as useConcentrationUnits, Cn as defineControlComponentBindings, Dn as defineDoseDesignControlModel, En as defineDoseCalculatorControlProps, Fn as useControlWorkspace, Gn as ATOMIC_WEIGHTS, In as getFieldRegistryEntry, Kn as useChemicalFormula, Ln as getTypeDefault, Mn as getDefaultControlView, On as defineWellPlateControlProps, Pn as useControlSchema, Qn as addMinutes, Sn as controlsToViewItems, Tn as defineControls, Un as useSequenceUtils, _n as controlsToSectionFormSchemas, _r as useListSelection, ar as formatTimeSlot, bn as controlsToTopBarSettingsConfig, cr as isTimeInRange, dr as snapToSlot, er as durationMinutes, fn as controlValuesToComponentBindings, fr as toMinutes, g as toBioTemplateComponentProps, gn as controlsToSectionFormSchema, hn as controlsToFormSchema, ir as formatTime, jn as getControlDefaults, kn as defineWellPlateDoseComponentBindings, l as requireBioTemplateControlSchema, lr as parseTime, m as toBioTemplateComponentBindingsById, mn as controlValuesToComponentProps, nr as findNearestTimeSlotIndex, o as getBioTemplateControlSchema, or as fromMinutes, p as toBioTemplateComponentBindings, pn as controlValuesToComponentBindingsById, pr as useTimeUtils, rr as formatDuration, sr as generateTimeSlots, tr as findAvailableSlots, u as getBioTemplateComponentBindings, ur as rangesOverlap, v as toBioTemplateComponentPropsById, vn as controlsToSettingsSchema, vr as useSelectionLimit, wn as defineControlModel, xn as controlsToViewIds, yn as controlsToSidebarPanels, yr as useEventListener } from "../templates-Cyt0Suwf.js";
2
- import { $ as useTheme, A as useAutoGroup, B as DATE_PRESET_OPTIONS, C as useSampleGroups, D as DEFAULT_COLORS, F as usePlatformContext, G as datePresetToISO, H as EXPERIMENT_STATUS_OPTIONS, I as useExperimentSelector, J as getExperimentStatusVariant, K as formatExperimentDate, L as useRequestSyncState, M as useDoseCalculator, N as APP_EXPERIMENT_KEY, O as extractSamplesFromDesignData, P as useAppExperiment, Q as useForm, R as useDebouncedWatch, S as useExpansionSet, U as EXPERIMENT_STATUS_VARIANT_MAP, V as EXPERIMENT_STATUS_LABELS, W as SORT_OPTIONS, X as evaluateCondition, Y as resolveExperimentCode, Z as useFormBuilder, _ as useExperimentSave, a as generateDilutionSeries, at as useSortedItems, c as useRackEditor, d as useBioTemplateWorkspace, et as useToast, f as getBioTemplateComponentProps, g as useTemplateCollection, h as useBioTemplateControls, i as DEFAULT_UNITS, it as compareSortValues, j as useWellPlateEditor, l as useBioTemplatePresetWorkspace, m as useBioTemplateComponents, n as useProtocolTemplates, nt as normalizeSearchQuery, o as useReagentSeries, p as toBioTemplateComponentPropsByComponent, q as formatExperimentStatus, r as DEFAULT_PRESETS, rt as useTextSearch, s as useGroupAssignment, t as useExperimentData, tt as candidateMatchesSearch, u as useBioTemplatePackWorkspace, x as useScheduleDrag, z as useApi } from "../useExperimentData-CM6Y0u5L.js";
3
- import { a as useCurrentExperiment, c as usePluginConfig, d as usePasskey, f as useAuth, i as resolvePluginBaseUrl, l as useAsync, n as createPluginClient, o as usePluginClient, r as getPluginPageSelectorItems, s as usePluginSettings, t as buildPluginEndpointUrl, u as useAsyncBatch } from "../composables-BcgZ6diz.js";
1
+ import { $n as compareTime, An as defineWellPlateDoseControlProps, Bn as useConcentrationUnits, Cn as defineControlComponentBindings, Dn as defineDoseDesignControlModel, En as defineDoseCalculatorControlProps, Fn as useControlWorkspace, Gn as ATOMIC_WEIGHTS, In as getFieldRegistryEntry, Kn as useChemicalFormula, Ln as getTypeDefault, Mn as getDefaultControlView, On as defineWellPlateControlProps, Pn as useControlSchema, Qn as addMinutes, Sn as controlsToViewItems, Tn as defineControls, Un as useSequenceUtils, _n as controlsToSectionFormSchemas, _r as useListSelection, ar as formatTimeSlot, bn as controlsToTopBarSettingsConfig, cr as isTimeInRange, dr as snapToSlot, er as durationMinutes, fn as controlValuesToComponentBindings, fr as toMinutes, g as toBioTemplateComponentProps, gn as controlsToSectionFormSchema, hn as controlsToFormSchema, ir as formatTime, jn as getControlDefaults, kn as defineWellPlateDoseComponentBindings, l as requireBioTemplateControlSchema, lr as parseTime, m as toBioTemplateComponentBindingsById, mn as controlValuesToComponentProps, nr as findNearestTimeSlotIndex, o as getBioTemplateControlSchema, or as fromMinutes, p as toBioTemplateComponentBindings, pn as controlValuesToComponentBindingsById, pr as useTimeUtils, rr as formatDuration, sr as generateTimeSlots, tr as findAvailableSlots, u as getBioTemplateComponentBindings, ur as rangesOverlap, v as toBioTemplateComponentPropsById, vn as controlsToSettingsSchema, vr as useSelectionLimit, wn as defineControlModel, xn as controlsToViewIds, yn as controlsToSidebarPanels, yr as useEventListener } from "../templates-BSlxwV2c.js";
2
+ import { $ as useTheme, A as useAutoGroup, B as DATE_PRESET_OPTIONS, C as useSampleGroups, D as DEFAULT_COLORS, F as usePlatformContext, G as datePresetToISO, H as EXPERIMENT_STATUS_OPTIONS, I as useExperimentSelector, J as getExperimentStatusVariant, K as formatExperimentDate, L as useRequestSyncState, M as useDoseCalculator, N as APP_EXPERIMENT_KEY, O as extractSamplesFromDesignData, P as useAppExperiment, Q as useForm, R as useDebouncedWatch, S as useExpansionSet, U as EXPERIMENT_STATUS_VARIANT_MAP, V as EXPERIMENT_STATUS_LABELS, W as SORT_OPTIONS, X as evaluateCondition, Y as resolveExperimentCode, Z as useFormBuilder, _ as useExperimentSave, a as generateDilutionSeries, at as useSortedItems, c as useRackEditor, d as useBioTemplateWorkspace, et as useToast, f as getBioTemplateComponentProps, g as useTemplateCollection, h as useBioTemplateControls, i as DEFAULT_UNITS, it as compareSortValues, j as useWellPlateEditor, l as useBioTemplatePresetWorkspace, m as useBioTemplateComponents, n as useProtocolTemplates, nt as normalizeSearchQuery, o as useReagentSeries, p as toBioTemplateComponentPropsByComponent, q as formatExperimentStatus, r as DEFAULT_PRESETS, rt as useTextSearch, s as useGroupAssignment, t as useExperimentData, tt as candidateMatchesSearch, u as useBioTemplatePackWorkspace, x as useScheduleDrag, z as useApi } from "../useExperimentData-BbbdI5xT.js";
3
+ import { a as useCurrentExperiment, c as usePluginConfig, d as usePasskey, f as useAuth, i as resolvePluginBaseUrl, l as useAsync, n as createPluginClient, o as usePluginClient, r as getPluginPageSelectorItems, s as usePluginSettings, t as buildPluginEndpointUrl, u as useAsyncBatch } from "../composables-D4Myb30a.js";
4
4
  export { APP_EXPERIMENT_KEY, ATOMIC_WEIGHTS, DATE_PRESET_OPTIONS, DEFAULT_COLORS, DEFAULT_PRESETS, DEFAULT_UNITS, EXPERIMENT_STATUS_LABELS, EXPERIMENT_STATUS_OPTIONS, EXPERIMENT_STATUS_VARIANT_MAP, SORT_OPTIONS, addMinutes, buildPluginEndpointUrl, candidateMatchesSearch, compareSortValues, compareTime, controlValuesToComponentBindings, controlValuesToComponentBindingsById, controlValuesToComponentProps, controlsToFormSchema, controlsToSectionFormSchema, controlsToSectionFormSchemas, controlsToSettingsSchema, controlsToSidebarPanels, controlsToTopBarSettingsConfig, controlsToViewIds, controlsToViewItems, createPluginClient, datePresetToISO, defineControlComponentBindings, defineControlModel, defineControls, defineDoseCalculatorControlProps, defineDoseDesignControlModel, defineWellPlateControlProps, defineWellPlateDoseComponentBindings, defineWellPlateDoseControlProps, durationMinutes, evaluateCondition, extractSamplesFromDesignData, findAvailableSlots, findNearestTimeSlotIndex, formatDuration, formatExperimentDate, formatExperimentStatus, formatTime, formatTimeSlot, fromMinutes, generateDilutionSeries, generateTimeSlots, getBioTemplateComponentBindings, getBioTemplateComponentProps, getBioTemplateControlSchema, getControlDefaults, getDefaultControlView, getExperimentStatusVariant, getFieldRegistryEntry, getPluginPageSelectorItems, getTypeDefault, isTimeInRange, normalizeSearchQuery, parseTime, rangesOverlap, requireBioTemplateControlSchema, resolveExperimentCode, resolvePluginBaseUrl, snapToSlot, toBioTemplateComponentBindings, toBioTemplateComponentBindingsById, toBioTemplateComponentProps, toBioTemplateComponentPropsByComponent, toBioTemplateComponentPropsById, toMinutes, useApi, useAppExperiment, useAsync, useAsyncBatch, useAuth, useAutoGroup, useBioTemplateComponents, useBioTemplateControls, useBioTemplatePackWorkspace, useBioTemplatePresetWorkspace, useBioTemplateWorkspace, useChemicalFormula, useConcentrationUnits, useControlSchema, useControlWorkspace, useCurrentExperiment, useDebouncedWatch, useDoseCalculator, useEventListener, useExpansionSet, useExperimentData, useExperimentSave, useExperimentSelector, useForm, useFormBuilder, useGroupAssignment, useListSelection, usePasskey, usePlatformContext, usePluginClient, usePluginConfig, usePluginSettings, useProtocolTemplates, useRackEditor, useReagentSeries, useRequestSyncState, useSampleGroups, useScheduleDrag, useSelectionLimit, useSequenceUtils, useSortedItems, useTemplateCollection, useTextSearch, useTheme, useTimeUtils, useToast, useWellPlateEditor };
@@ -1,5 +1,5 @@
1
- import { F as usePlatformContext, L as useRequestSyncState, b as resolveCurrentExperimentId, v as currentExperimentFromContext, y as getInjectedPlatformContext, z as useApi } from "./useExperimentData-CM6Y0u5L.js";
2
- import { r as useSettingsStore, t as useAuthStore } from "./auth-QQj2kkze.js";
1
+ import { F as usePlatformContext, L as useRequestSyncState, b as resolveCurrentExperimentId, v as currentExperimentFromContext, y as getInjectedPlatformContext, z as useApi } from "./useExperimentData-BbbdI5xT.js";
2
+ import { r as useSettingsStore, t as useAuthStore } from "./auth-CBG3bWEc.js";
3
3
  import { computed, getCurrentInstance, onMounted, onUnmounted, ref, shallowRef, watch } from "vue";
4
4
  import axios from "axios";
5
5
  //#region src/composables/useAuth.ts
@@ -896,4 +896,4 @@ function useCurrentExperiment(options = {}) {
896
896
  //#endregion
897
897
  export { useCurrentExperiment as a, usePluginConfig as c, usePasskey as d, useAuth as f, resolvePluginBaseUrl as i, useAsync as l, createPluginClient as n, usePluginClient as o, getPluginPageSelectorItems as r, usePluginSettings as s, buildPluginEndpointUrl as t, useAsyncBatch as u };
898
898
 
899
- //# sourceMappingURL=composables-BcgZ6diz.js.map
899
+ //# sourceMappingURL=composables-D4Myb30a.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"composables-BcgZ6diz.js","names":[],"sources":["../src/composables/useAuth.ts","../src/composables/usePasskey.ts","../src/composables/useAsync.ts","../src/composables/usePluginConfig.ts","../src/composables/usePluginClient.ts"],"sourcesContent":["import axios from 'axios'\nimport { ref, onMounted, onUnmounted, watch, getCurrentInstance, type Ref } from 'vue'\nimport { useAuthStore } from '../stores/auth'\nimport { useSettingsStore } from '../stores/settings'\nimport type { AuthConfig, UserInfo, LoginResponse, TokenVerifyResponse, UpdateProfileRequest } from '../types'\n\ninterface UserResponse {\n id: string\n username: string\n shortname: string | null\n email: string | null\n role: string\n is_active: boolean\n}\n\ninterface RefreshResponse {\n access_token: string\n expires_in: number\n token_type: string\n}\n\n// Token refresh configuration\nconst TOKEN_REFRESH_MARGIN_MS = 5 * 60 * 1000 // Refresh 5 minutes before expiry\nconst TOKEN_REFRESH_CHECK_INTERVAL_MS = 60 * 1000 // Check every minute\n\n/**\n * Authentication composable with automatic token refresh.\n *\n * Features:\n * - Automatic token refresh before expiration\n * - Login/logout/register functionality\n * - Token verification on startup\n * - User profile management\n *\n * @example\n * ```typescript\n * const { login, logout, isAuthenticated, user } = useAuth()\n *\n * // Login\n * const success = await login('username', 'password')\n *\n * // Automatic refresh is enabled by default\n * // Tokens are refreshed 5 minutes before expiration\n * ```\n */\nexport interface UseAuthReturn {\n login: (username: string, password: string) => Promise<boolean>\n logout: () => void\n register: (username: string, password: string, email?: string) => Promise<boolean>\n verifyToken: () => Promise<boolean>\n fetchAuthConfig: () => Promise<AuthConfig>\n initializeAuth: () => Promise<void>\n getCurrentUser: () => Promise<UserInfo | null>\n getAuthHeader: () => Record<string, string>\n updateProfile: (data: { email?: string; shortname?: string; currentPassword?: string; newPassword?: string }) => Promise<{ success: boolean; error?: string }>\n refreshToken: () => Promise<boolean>\n isRefreshing: Ref<boolean>\n}\n\n// Module-level singletons to prevent duplicate refresh requests and timers\n// across multiple useAuth() instances\nlet _refreshPromise: Promise<boolean> | null = null\nlet _refreshTimerId: number | null = null\nlet _mountedConsumerCount = 0\n\n/** @internal Reset module-level state — only for use in tests. */\nexport function _resetAuthStateForTesting(): void {\n _refreshPromise = null\n if (_refreshTimerId !== null) {\n clearTimeout(_refreshTimerId)\n _refreshTimerId = null\n }\n _mountedConsumerCount = 0\n}\n\n/** Manages authentication state with login/logout/register and automatic JWT token refresh. */\nexport function useAuth(): UseAuthReturn {\n const authStore = useAuthStore()\n const settingsStore = useSettingsStore()\n\n const isRefreshing = ref(false)\n\n function getApiBaseUrl(): string {\n return settingsStore.getApiBaseUrl()\n }\n\n async function fetchAuthConfig(): Promise<AuthConfig> {\n try {\n const response = await axios.get<{\n auth_required: boolean\n passkey_enabled: boolean\n passkey_registered?: boolean\n registration_enabled?: boolean\n database_mode?: string\n }>(`${getApiBaseUrl()}/setup/config/public`)\n\n const config: AuthConfig = {\n authRequired: response.data.auth_required,\n passkeyEnabled: response.data.passkey_enabled,\n passkeyRegistered: response.data.passkey_registered ?? false,\n registrationEnabled: response.data.registration_enabled ?? false,\n databaseMode: response.data.database_mode ?? 'none',\n }\n\n authStore.setAuthConfig(config)\n return config\n } catch (error) {\n console.error('Failed to fetch auth config:', error)\n return {\n authRequired: false,\n passkeyEnabled: false,\n passkeyRegistered: false,\n registrationEnabled: false,\n databaseMode: 'none',\n }\n }\n }\n\n async function login(username: string, password: string): Promise<boolean> {\n authStore.setLoading(true)\n authStore.setError(null)\n\n try {\n const response = await axios.post<LoginResponse>(\n `${getApiBaseUrl()}/auth/login`,\n { username, password }\n )\n\n authStore.setToken(response.data.access_token, response.data.expires_in)\n authStore.setUsername(username)\n\n await getCurrentUser()\n\n // Start auto-refresh after successful login\n scheduleTokenRefresh()\n\n return true\n } catch (error) {\n if (axios.isAxiosError(error) && error.response) {\n authStore.setError(error.response.data.detail || 'Login failed')\n } else {\n authStore.setError('Network error. Please try again.')\n }\n return false\n } finally {\n authStore.setLoading(false)\n }\n }\n\n async function register(username: string, password: string, email?: string): Promise<boolean> {\n authStore.setLoading(true)\n authStore.setError(null)\n\n try {\n await axios.post<UserResponse>(\n `${getApiBaseUrl()}/users/register`,\n { username, password, email }\n )\n\n return await login(username, password)\n } catch (error) {\n if (axios.isAxiosError(error) && error.response) {\n authStore.setError(error.response.data.detail || 'Registration failed')\n } else {\n authStore.setError('Network error. Please try again.')\n }\n return false\n } finally {\n authStore.setLoading(false)\n }\n }\n\n async function getCurrentUser(): Promise<UserInfo | null> {\n if (!authStore.token) {\n return null\n }\n\n try {\n const response = await axios.get<UserResponse>(\n `${getApiBaseUrl()}/users/me`,\n { headers: getAuthHeader() }\n )\n\n const userInfo: UserInfo = {\n id: response.data.id,\n username: response.data.username,\n shortname: response.data.shortname,\n email: response.data.email,\n role: response.data.role,\n isActive: response.data.is_active,\n }\n\n authStore.setUserInfo(userInfo)\n return userInfo\n } catch {\n return null\n }\n }\n\n async function verifyToken(): Promise<boolean> {\n if (!authStore.token) {\n return false\n }\n\n try {\n const response = await axios.get<TokenVerifyResponse>(\n `${getApiBaseUrl()}/auth/verify`,\n {\n headers: {\n Authorization: `Bearer ${authStore.token}`,\n },\n }\n )\n\n if (response.data.valid && response.data.username) {\n authStore.setUsername(response.data.username)\n return true\n }\n\n authStore.clearToken()\n return false\n } catch {\n authStore.clearToken()\n return false\n }\n }\n\n /**\n * Refresh the authentication token.\n * Called automatically before token expiration.\n * Uses promise caching to prevent concurrent refresh requests.\n */\n async function refreshToken(): Promise<boolean> {\n if (!authStore.token) return false\n if (_refreshPromise) return _refreshPromise\n\n _refreshPromise = (async () => {\n isRefreshing.value = true\n\n try {\n const response = await axios.post<RefreshResponse>(\n `${getApiBaseUrl()}/auth/refresh`,\n {},\n { headers: getAuthHeader() }\n )\n\n authStore.setToken(response.data.access_token, response.data.expires_in)\n\n // Reschedule next refresh\n scheduleTokenRefresh()\n\n return true\n } catch (error) {\n // If refresh fails, the token may have been revoked\n // Clear auth state and let user re-login\n if (axios.isAxiosError(error) && error.response?.status === 401) {\n console.warn('[Auth] Token refresh failed - session expired')\n authStore.clearToken()\n stopTokenRefresh()\n }\n return false\n } finally {\n isRefreshing.value = false\n _refreshPromise = null\n }\n })()\n\n return _refreshPromise\n }\n\n /**\n * Schedule automatic token refresh before expiration.\n */\n function scheduleTokenRefresh(): void {\n // Clear any existing timer\n stopTokenRefresh()\n\n if (!authStore.tokenExpires) {\n return\n }\n\n const expiresAt = authStore.tokenExpires.getTime()\n const refreshAt = expiresAt - TOKEN_REFRESH_MARGIN_MS\n const now = Date.now()\n\n if (refreshAt <= now) {\n // Token is already close to expiring or expired, refresh now\n refreshToken()\n return\n }\n\n // Schedule refresh\n const delay = refreshAt - now\n _refreshTimerId = window.setTimeout(() => {\n refreshToken()\n }, delay)\n }\n\n /**\n * Stop automatic token refresh.\n */\n function stopTokenRefresh(): void {\n if (_refreshTimerId !== null) {\n window.clearTimeout(_refreshTimerId)\n _refreshTimerId = null\n }\n }\n\n /**\n * Check if token needs refresh and refresh if necessary.\n * Called periodically as a safety net.\n */\n function checkAndRefreshIfNeeded(): void {\n if (!authStore.token || !authStore.tokenExpires) {\n return\n }\n\n const expiresAt = authStore.tokenExpires.getTime()\n const refreshAt = expiresAt - TOKEN_REFRESH_MARGIN_MS\n const now = Date.now()\n\n if (now >= refreshAt) {\n refreshToken()\n }\n }\n\n async function initializeAuth(): Promise<void> {\n authStore.initialize()\n await fetchAuthConfig()\n\n if (authStore.token) {\n const valid = await verifyToken()\n if (valid) {\n await getCurrentUser()\n // Start auto-refresh for existing session\n scheduleTokenRefresh()\n }\n }\n }\n\n function logout(): void {\n stopTokenRefresh()\n authStore.logout()\n }\n\n function getAuthHeader(): Record<string, string> {\n if (authStore.token) {\n return { Authorization: `Bearer ${authStore.token}` }\n }\n return {}\n }\n\n async function updateProfile(data: {\n email?: string\n shortname?: string\n currentPassword?: string\n newPassword?: string\n }): Promise<{ success: boolean; error?: string }> {\n if (!authStore.token) {\n return { success: false, error: 'Not authenticated' }\n }\n\n try {\n const requestData: UpdateProfileRequest = {}\n if (data.email !== undefined) requestData.email = data.email\n if (data.shortname !== undefined) requestData.shortname = data.shortname\n if (data.currentPassword) requestData.current_password = data.currentPassword\n if (data.newPassword) requestData.new_password = data.newPassword\n\n const response = await axios.put<UserResponse>(\n `${getApiBaseUrl()}/users/me`,\n requestData,\n { headers: getAuthHeader() }\n )\n\n const userInfo: UserInfo = {\n id: response.data.id,\n username: response.data.username,\n shortname: response.data.shortname,\n email: response.data.email,\n role: response.data.role,\n isActive: response.data.is_active,\n }\n authStore.setUserInfo(userInfo)\n\n return { success: true }\n } catch (error) {\n if (axios.isAxiosError(error) && error.response) {\n return { success: false, error: error.response.data.detail || 'Update failed' }\n }\n return { success: false, error: 'Network error. Please try again.' }\n }\n }\n\n // Set up periodic check as safety net (only inside component setup)\n let checkInterval: number | null = null\n\n if (getCurrentInstance()) {\n onMounted(() => {\n _mountedConsumerCount += 1\n checkInterval = window.setInterval(checkAndRefreshIfNeeded, TOKEN_REFRESH_CHECK_INTERVAL_MS)\n })\n\n onUnmounted(() => {\n if (checkInterval !== null) {\n window.clearInterval(checkInterval)\n checkInterval = null\n }\n\n _mountedConsumerCount = Math.max(0, _mountedConsumerCount - 1)\n if (_mountedConsumerCount === 0) {\n stopTokenRefresh()\n }\n })\n\n // Watch for token changes to reschedule refresh\n watch(\n () => authStore.tokenExpires,\n (newExpires) => {\n if (newExpires) {\n scheduleTokenRefresh()\n } else {\n stopTokenRefresh()\n }\n }\n )\n }\n\n return {\n // Core auth methods\n login,\n logout,\n register,\n verifyToken,\n fetchAuthConfig,\n initializeAuth,\n getCurrentUser,\n getAuthHeader,\n updateProfile,\n\n // Token refresh\n refreshToken,\n isRefreshing,\n }\n}\n","import axios from 'axios'\nimport { useAuthStore } from '../stores/auth'\nimport { useSettingsStore } from '../stores/settings'\nimport type { CredentialInfo } from '../types'\n\n// Lazy-load @simplewebauthn/browser so plugins that never call usePasskey\n// don't need the optional peer dep installed (avoids build failures).\nasync function loadWebAuthn() {\n try {\n return await import('@simplewebauthn/browser')\n } catch {\n throw new Error(\n '@simplewebauthn/browser is required for passkey support. Install it: bun add @simplewebauthn/browser',\n )\n }\n}\n\ninterface PasskeyLoginResponse {\n access_token: string\n token_type: string\n expires_in: number\n}\n\n/** Registers and authenticates passkeys using WebAuthn, lazily loading the browser dependency. */\nexport function usePasskey() {\n const authStore = useAuthStore()\n const settingsStore = useSettingsStore()\n\n function getApiBaseUrl(): string {\n return settingsStore.getApiBaseUrl()\n }\n\n async function isSupported(): Promise<boolean> {\n try {\n const { browserSupportsWebAuthn } = await loadWebAuthn()\n return browserSupportsWebAuthn()\n } catch {\n return false\n }\n }\n\n async function registerPasskey(deviceName?: string): Promise<boolean> {\n const webauthn = await loadWebAuthn()\n if (!webauthn.browserSupportsWebAuthn()) {\n authStore.setError('WebAuthn is not supported in this browser')\n return false\n }\n\n authStore.setLoading(true)\n authStore.setError(null)\n\n try {\n const optionsResponse = await axios.get<{ options: string }>(\n `${getApiBaseUrl()}/auth/passkey/register/options`,\n {\n headers: { Authorization: `Bearer ${authStore.token}` },\n withCredentials: true,\n }\n )\n\n const options = JSON.parse(optionsResponse.data.options)\n\n const credential = await webauthn.startRegistration(options)\n\n await axios.post(\n `${getApiBaseUrl()}/auth/passkey/register/verify`,\n {\n credential: JSON.stringify(credential),\n device_name: deviceName,\n },\n {\n headers: { Authorization: `Bearer ${authStore.token}` },\n withCredentials: true,\n }\n )\n\n authStore.setAuthConfig({\n ...authStore.authConfig,\n passkeyRegistered: true,\n })\n\n return true\n } catch (error) {\n if (axios.isAxiosError(error) && error.response) {\n authStore.setError(error.response.data.detail || 'Passkey registration failed')\n } else if (error instanceof Error) {\n if (error.name === 'NotAllowedError') {\n authStore.setError('Registration was cancelled or timed out')\n } else if (error.name === 'InvalidStateError') {\n authStore.setError('This authenticator is already registered')\n } else {\n authStore.setError(error.message)\n }\n } else {\n authStore.setError('Passkey registration failed')\n }\n return false\n } finally {\n authStore.setLoading(false)\n }\n }\n\n async function loginWithPasskey(): Promise<boolean> {\n const webauthn = await loadWebAuthn()\n if (!webauthn.browserSupportsWebAuthn()) {\n authStore.setError('WebAuthn is not supported in this browser')\n return false\n }\n\n authStore.setLoading(true)\n authStore.setError(null)\n\n try {\n const optionsResponse = await axios.get<{ options: string }>(\n `${getApiBaseUrl()}/auth/passkey/login/options`,\n { withCredentials: true }\n )\n\n const options = JSON.parse(optionsResponse.data.options)\n\n const credential = await webauthn.startAuthentication(options)\n\n const response = await axios.post<PasskeyLoginResponse>(\n `${getApiBaseUrl()}/auth/passkey/login/verify`,\n { credential: JSON.stringify(credential) },\n { withCredentials: true }\n )\n\n authStore.setToken(response.data.access_token, response.data.expires_in)\n\n return true\n } catch (error) {\n if (axios.isAxiosError(error) && error.response) {\n if (error.response.status === 404) {\n authStore.setError('No passkeys registered. Please login with password first.')\n } else {\n authStore.setError(error.response.data.detail || 'Passkey login failed')\n }\n } else if (error instanceof Error) {\n if (error.name === 'NotAllowedError') {\n authStore.setError('Authentication was cancelled or timed out')\n } else {\n authStore.setError(error.message)\n }\n } else {\n authStore.setError('Passkey login failed')\n }\n return false\n } finally {\n authStore.setLoading(false)\n }\n }\n\n async function listCredentials(): Promise<CredentialInfo[]> {\n try {\n const response = await axios.get<{ credentials: CredentialInfo[] }>(\n `${getApiBaseUrl()}/auth/passkey/credentials`,\n {\n headers: { Authorization: `Bearer ${authStore.token}` },\n }\n )\n return response.data.credentials\n } catch {\n return []\n }\n }\n\n async function deleteCredential(credentialId: string): Promise<boolean> {\n try {\n await axios.delete(\n `${getApiBaseUrl()}/auth/passkey/credentials/${encodeURIComponent(credentialId)}`,\n {\n headers: { Authorization: `Bearer ${authStore.token}` },\n }\n )\n\n const remaining = await listCredentials()\n if (remaining.length === 0) {\n authStore.setAuthConfig({\n ...authStore.authConfig,\n passkeyRegistered: false,\n })\n }\n\n return true\n } catch {\n return false\n }\n }\n\n async function deleteAllCredentials(): Promise<boolean> {\n try {\n await axios.delete(`${getApiBaseUrl()}/auth/passkey/credentials`, {\n headers: { Authorization: `Bearer ${authStore.token}` },\n })\n\n authStore.setAuthConfig({\n ...authStore.authConfig,\n passkeyRegistered: false,\n })\n\n return true\n } catch {\n return false\n }\n }\n\n return {\n isSupported,\n registerPasskey,\n loginWithPasskey,\n listCredentials,\n deleteCredential,\n deleteAllCredentials,\n }\n}\n","import { ref, computed, type Ref, type ComputedRef } from 'vue'\n\n/**\n * Error type for async operations.\n */\nexport interface AsyncError {\n message: string\n code?: string\n details?: Record<string, unknown>\n originalError?: unknown\n}\n\n/**\n * State of an async operation.\n */\nexport type AsyncState = 'idle' | 'loading' | 'success' | 'error'\n\n/**\n * Return type for useAsync composable.\n */\nexport interface UseAsyncReturn<T> {\n // Data\n data: Ref<T | null>\n error: Ref<AsyncError | null>\n\n // State\n state: Ref<AsyncState>\n isIdle: ComputedRef<boolean>\n isLoading: ComputedRef<boolean>\n isSuccess: ComputedRef<boolean>\n isError: ComputedRef<boolean>\n\n // Methods\n execute: (...args: unknown[]) => Promise<T | null>\n reset: () => void\n setData: (data: T | null) => void\n setError: (error: AsyncError | null) => void\n}\n\n/**\n * Options for useAsync.\n */\nexport interface UseAsyncOptions<T> {\n // Initial data value\n initialData?: T | null\n\n // Whether to run immediately on creation\n immediate?: boolean\n\n // Arguments for immediate execution\n immediateArgs?: unknown[]\n\n // Transform error into AsyncError\n transformError?: (error: unknown) => AsyncError\n\n // Called on success\n onSuccess?: (data: T) => void\n\n // Called on error\n onError?: (error: AsyncError) => void\n\n // Reset data on new execution\n resetOnExecute?: boolean\n}\n\n/**\n * Default error transformer.\n */\nfunction defaultTransformError(error: unknown): AsyncError {\n if (error instanceof Error) {\n return {\n message: error.message,\n originalError: error,\n }\n }\n\n if (typeof error === 'object' && error !== null) {\n const errorObj = error as Record<string, unknown>\n\n // Handle Axios-like errors\n if ('response' in errorObj && errorObj.response) {\n const response = errorObj.response as Record<string, unknown>\n const data = response.data as Record<string, unknown> | undefined\n\n return {\n message: (data?.detail as string) || (data?.message as string) || 'Request failed',\n code: String(response.status),\n details: data,\n originalError: error,\n }\n }\n\n // Handle generic error objects\n return {\n message: (errorObj.message as string) || 'Unknown error',\n code: errorObj.code as string | undefined,\n details: errorObj,\n originalError: error,\n }\n }\n\n return {\n message: String(error),\n originalError: error,\n }\n}\n\n/**\n * Composable for managing async operation state.\n *\n * Provides standardized loading, error, and success state management\n * for any async function.\n *\n * @param asyncFn - The async function to wrap\n * @param options - Configuration options\n *\n * @example\n * ```typescript\n * // Basic usage\n * const { data, isLoading, error, execute } = useAsync(\n * async (id: string) => {\n * const response = await api.get(`/users/${id}`)\n * return response.data\n * }\n * )\n *\n * // Execute the function\n * await execute('user-123')\n *\n * // In template\n * <div v-if=\"isLoading\">Loading...</div>\n * <div v-else-if=\"error\">{{ error.message }}</div>\n * <div v-else-if=\"data\">{{ data.name }}</div>\n * ```\n *\n * @example\n * ```typescript\n * // With options\n * const { data, execute } = useAsync(\n * fetchUser,\n * {\n * immediate: true,\n * immediateArgs: ['default-user'],\n * onSuccess: (user) => console.log('Fetched:', user.name),\n * onError: (error) => toast.error(error.message),\n * }\n * )\n * ```\n *\n * @example\n * ```typescript\n * // Form submission\n * const { isLoading, error, execute: submit } = useAsync(\n * async (formData: FormData) => {\n * await api.post('/submit', formData)\n * },\n * {\n * onSuccess: () => {\n * toast.success('Submitted!')\n * router.push('/success')\n * },\n * }\n * )\n *\n * const handleSubmit = () => submit(new FormData(formRef.value))\n * ```\n */\n/** Wraps an async function with reactive loading, error, and success state plus optional auto-execute. */\nexport function useAsync<T>(\n asyncFn: (...args: unknown[]) => Promise<T>,\n options: UseAsyncOptions<T> = {}\n): UseAsyncReturn<T> {\n const {\n initialData = null,\n immediate = false,\n immediateArgs = [],\n transformError = defaultTransformError,\n onSuccess,\n onError,\n resetOnExecute = false,\n } = options\n\n // State\n const data = ref<T | null>(initialData) as Ref<T | null>\n const error = ref<AsyncError | null>(null)\n const state = ref<AsyncState>('idle')\n\n // Computed state helpers\n const isIdle = computed(() => state.value === 'idle')\n const isLoading = computed(() => state.value === 'loading')\n const isSuccess = computed(() => state.value === 'success')\n const isError = computed(() => state.value === 'error')\n\n // Execute the async function\n async function execute(...args: unknown[]): Promise<T | null> {\n state.value = 'loading'\n error.value = null\n\n if (resetOnExecute) {\n data.value = null\n }\n\n try {\n const result = await asyncFn(...args)\n data.value = result\n state.value = 'success'\n onSuccess?.(result)\n return result\n } catch (e) {\n const asyncError = transformError(e)\n error.value = asyncError\n state.value = 'error'\n onError?.(asyncError)\n return null\n }\n }\n\n // Reset to initial state\n function reset(): void {\n data.value = initialData\n error.value = null\n state.value = 'idle'\n }\n\n // Manual data setter\n function setData(newData: T | null): void {\n data.value = newData\n if (newData !== null) {\n state.value = 'success'\n error.value = null\n }\n }\n\n // Manual error setter\n function setError(newError: AsyncError | null): void {\n error.value = newError\n if (newError !== null) {\n state.value = 'error'\n }\n }\n\n // Execute immediately if requested\n if (immediate) {\n execute(...immediateArgs)\n }\n\n return {\n data,\n error,\n state,\n isIdle,\n isLoading,\n isSuccess,\n isError,\n execute,\n reset,\n setData,\n setError,\n }\n}\n\n/**\n * Create a batch of async operations that can be executed in parallel.\n *\n * @example\n * ```typescript\n * const { results, isLoading, execute } = useAsyncBatch([\n * () => fetchUser(userId),\n * () => fetchPosts(userId),\n * () => fetchComments(userId),\n * ])\n *\n * await execute()\n * // results.value = [user, posts, comments]\n * ```\n */\nexport function useAsyncBatch<T extends readonly (() => Promise<unknown>)[]>(\n asyncFns: T\n): {\n results: Ref<{ [K in keyof T]: Awaited<ReturnType<T[K]>> | null }>\n errors: Ref<(AsyncError | null)[]>\n isLoading: Ref<boolean>\n execute: () => Promise<void>\n reset: () => void\n} {\n type Results = { [K in keyof T]: Awaited<ReturnType<T[K]>> | null }\n\n const results = ref<Results>(asyncFns.map(() => null) as unknown as Results) as Ref<Results>\n const errors = ref<(AsyncError | null)[]>(asyncFns.map(() => null))\n const isLoading = ref(false)\n\n async function execute(): Promise<void> {\n isLoading.value = true\n errors.value = asyncFns.map(() => null)\n\n const promises = asyncFns.map(async (fn, index) => {\n try {\n const result = await fn()\n ;(results.value as unknown[])[index] = result\n } catch (e) {\n errors.value[index] = defaultTransformError(e)\n ;(results.value as unknown[])[index] = null\n }\n })\n\n await Promise.all(promises)\n isLoading.value = false\n }\n\n function reset(): void {\n results.value = asyncFns.map(() => null) as unknown as Results\n errors.value = asyncFns.map(() => null)\n isLoading.value = false\n }\n\n return {\n results,\n errors,\n isLoading,\n execute,\n reset,\n }\n}\n","import { ref, computed, onMounted, type Ref, type ComputedRef } from 'vue'\nimport { useApi } from './useApi'\nimport { usePlatformContext } from './usePlatformContext'\nimport { useRequestSyncState } from './useRequestSyncState'\n\nexport interface UsePluginConfigReturn {\n /** Editable plugin configuration values. */\n config: Ref<Record<string, unknown>>\n /** Whether the config is currently loading. */\n isLoading: Ref<boolean>\n /** Whether the config is currently saving. */\n isSaving: Ref<boolean>\n /** Error message from the last failed operation, or null. */\n error: Ref<string | null>\n /** Timestamp of the last successful load, or null. */\n lastLoadedAt: Ref<Date | null>\n /** Timestamp of the last successful save, or null. */\n lastSavedAt: Ref<Date | null>\n /** Whether local config differs from the last loaded or saved snapshot. */\n isDirty: ComputedRef<boolean>\n /** Load plugin configuration from the platform. */\n load: () => Promise<void>\n /** Save plugin configuration to the platform. */\n save: () => Promise<boolean>\n /** Restore the last loaded or saved configuration snapshot. */\n reset: () => void\n}\n\n/** Loads, saves, and tracks dirty state for a plugin's persistent configuration via the platform API. */\nexport function usePluginConfig(pluginName?: string): UsePluginConfigReturn {\n const api = useApi()\n const { plugin } = usePlatformContext()\n\n const resolvedName = computed(() => pluginName ?? plugin.value?.name ?? '')\n\n const config = ref<Record<string, unknown>>({})\n const savedConfig = ref<Record<string, unknown>>({})\n const request = useRequestSyncState('Plugin config request failed.')\n const isLoading = ref(false)\n const isSaving = ref(false)\n const error = request.error\n const lastLoadedAt = request.lastLoadedAt\n const lastSavedAt = request.lastSavedAt\n\n const isDirty = computed(() => {\n return JSON.stringify(config.value) !== JSON.stringify(savedConfig.value)\n })\n\n async function load(): Promise<void> {\n const name = resolvedName.value\n if (!name) return\n\n isLoading.value = true\n try {\n const response = await request.run(\n () => api.get<{ plugin_name: string; config: Record<string, unknown> }>(\n `/plugins/${encodeURIComponent(name)}/config`,\n ),\n { success: 'load', errorMessage: 'Failed to load plugin config' },\n )\n config.value = { ...response.config }\n savedConfig.value = { ...response.config }\n } catch {\n // Error state is handled by request.run().\n } finally {\n isLoading.value = false\n }\n }\n\n async function save(): Promise<boolean> {\n const name = resolvedName.value\n if (!name) return false\n\n isSaving.value = true\n try {\n const response = await request.run(\n () => api.patch<{ plugin_name: string; config: Record<string, unknown> }>(\n `/plugins/${encodeURIComponent(name)}/config`,\n { config: config.value },\n ),\n { success: 'save', errorMessage: 'Failed to save plugin config' },\n )\n config.value = { ...response.config }\n savedConfig.value = { ...response.config }\n return true\n } catch {\n return false\n } finally {\n isSaving.value = false\n }\n }\n\n function reset(): void {\n config.value = { ...savedConfig.value }\n request.clearError()\n }\n\n onMounted(() => {\n load()\n })\n\n return {\n config,\n isLoading,\n isSaving,\n error,\n lastLoadedAt,\n lastSavedAt,\n isDirty,\n load,\n save,\n reset,\n }\n}\n","import { computed, shallowRef, watch, type ComputedRef, type Ref } from 'vue'\nimport { useApi } from './useApi'\nimport { usePlatformContext } from './usePlatformContext'\nimport { useRequestSyncState } from './useRequestSyncState'\nimport {\n currentExperimentFromContext,\n getInjectedPlatformContext,\n resolveCurrentExperimentId,\n} from './platformContextHelpers'\nimport type { ExperimentSummary, PageSelectorItem } from '../types'\n\nexport type PluginHttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete'\n\nexport interface PluginEndpointContract {\n name: string\n method: PluginHttpMethod\n path: string\n pathParams?: string[]\n queryParams?: PluginEndpointParamContract[]\n requestType?: string | null\n responseType?: string | null\n}\n\nexport interface PluginEndpointParamContract {\n name: string\n fieldName: string\n type?: string\n required?: boolean\n}\n\nexport interface PluginNavItemContract {\n path: string\n label: string\n id?: string\n icon?: string\n description?: string\n requiresAuth?: boolean\n requiresAdmin?: boolean\n requiresFeature?: string\n}\n\nexport interface PluginContract {\n schemaVersion: number\n plugin: {\n name?: string\n packageName?: string\n version?: string\n description?: string\n routesPrefix: string\n apiPrefix: string\n type: 'analysis' | 'experiment-design' | string\n analysisType?: string\n icon?: string\n color?: string\n navItems?: PluginNavItemContract[]\n capabilities?: Record<string, unknown>\n }\n endpoints: PluginEndpointContract[]\n hash: string\n}\n\nexport interface PluginEndpointDefinition {\n method: PluginHttpMethod\n path: string\n pathParams?: string[]\n queryParams?: PluginEndpointParamDefinition[]\n hasBody?: boolean\n}\n\nexport type PluginEndpointParamDefinition = string | {\n name: string\n fieldName?: string\n type?: string\n required?: boolean\n}\n\nexport interface CreatePluginClientOptions {\n endpoints: Record<string, PluginEndpointDefinition>\n baseUrl?: string\n}\n\nexport interface BuildPluginEndpointUrlOptions {\n /** Override the generated or platform-injected API base URL. */\n baseUrl?: string\n /** Return only the endpoint path and query string instead of base URL + path. */\n includeBaseUrl?: boolean\n}\n\nexport interface UseCurrentExperimentOptions {\n apiBaseUrl?: string\n immediate?: boolean\n}\n\nexport interface UseCurrentExperimentReturn<TExperiment = ExperimentSummary> {\n /** Current experiment id resolved from platform injection or URL conventions. */\n experimentId: ComputedRef<number | undefined>\n /** Whether a current experiment id is available. */\n hasExperiment: ComputedRef<boolean>\n /** Current experiment payload, if injected or successfully fetched. */\n experiment: Ref<TExperiment | undefined>\n /** Whether the current experiment is currently loading. */\n isLoading: Ref<boolean>\n /** Error message from the last failed current-experiment fetch, or null. */\n error: Ref<string | null>\n /** Timestamp of the last successful current-experiment fetch, or null. */\n lastLoadedAt: Ref<Date | null>\n /** Return the current experiment id or throw a clear SDK error when absent. */\n requireExperimentId: () => number\n /** Fetch a specific experiment id, defaulting to the current experiment id. */\n fetch: (experimentId?: number) => Promise<TExperiment | undefined>\n /** Refetch the currently resolved experiment id. */\n refresh: () => Promise<TExperiment | undefined>\n}\n\nexport type PluginEndpointCaller = (payload?: unknown) => Promise<unknown>\nexport type GeneratedPluginClient = Record<string, (...args: any[]) => Promise<any>>\n\nfunction normalizeApiBaseUrl(baseUrl: string | undefined): string | undefined {\n if (!baseUrl) return undefined\n return baseUrl.length > 1 ? baseUrl.replace(/\\/+$/, '') : baseUrl\n}\n\nfunction normalizeApiPrefix(prefix: string | undefined): string | undefined {\n if (!prefix) return undefined\n if (prefix.startsWith('/api/')) return normalizeApiBaseUrl(prefix)\n if (prefix === '/api') return prefix\n if (prefix.startsWith('/')) return normalizeApiBaseUrl(`/api${prefix}`)\n return normalizeApiBaseUrl(`/api/${prefix}`)\n}\n\nfunction normalizePluginNavPath(path: string): string {\n const raw = path.trim() || '/'\n const prefixed = raw.startsWith('/') ? raw : `/${raw}`\n return prefixed.replace(/\\/+$/, '') || '/'\n}\n\nfunction pluginPageIdFromPath(path: string, fallbackIndex: number): string {\n const normalizedPath = normalizePluginNavPath(path)\n if (normalizedPath === '/') return 'dashboard'\n return normalizedPath.replace(/^\\/+/, '').replace(/\\/+/g, '-') || `page-${fallbackIndex + 1}`\n}\n\n/** Convert PluginMetadata.nav_items into AppTopBar pageSelector items for topbar page switches, homepage PluginCards, and fallback views. */\nexport function getPluginPageSelectorItems(contract: PluginContract): PageSelectorItem[] {\n const pluginIcon = contract.plugin.icon ?? ''\n const navItems = contract.plugin.navItems ?? []\n const source: PluginNavItemContract[] = navItems.length > 0\n ? navItems\n : [{\n path: '/',\n label: contract.plugin.name ?? 'Dashboard',\n id: 'dashboard',\n icon: pluginIcon || undefined,\n description: contract.plugin.description,\n }]\n\n return source.map((item, index) => ({\n id: item.id || pluginPageIdFromPath(item.path, index),\n label: item.label,\n to: normalizePluginNavPath(item.path),\n icon: item.icon || pluginIcon || undefined,\n hint: item.description || contract.plugin.name,\n }))\n}\n\n/** Resolve the runtime plugin API base URL from env, explicit options, platform injection, or contract metadata. */\nexport function resolvePluginBaseUrl(contract: PluginContract, explicitBaseUrl?: string): string {\n const envPrefix = (import.meta.env?.VITE_API_PREFIX as string | undefined) || undefined\n if (envPrefix) return normalizeApiBaseUrl(envPrefix) ?? envPrefix\n if (explicitBaseUrl) return normalizeApiBaseUrl(explicitBaseUrl) ?? explicitBaseUrl\n\n const injected = getInjectedPlatformContext()\n const platformPrefix =\n normalizeApiBaseUrl(injected?.plugin?.api_prefix) ||\n normalizeApiPrefix(injected?.plugin?.route_prefix)\n if (platformPrefix) return platformPrefix\n\n return normalizeApiBaseUrl(contract.plugin.apiPrefix) || '/api'\n}\n\nfunction encodePath(path: string, payload: Record<string, unknown>, pathParams: string[]): string {\n let nextPath = path\n for (const param of pathParams) {\n const value = payload[param]\n if (value === undefined || value === null) {\n throw new Error(`[MINT SDK] Missing path parameter '${param}' for plugin endpoint ${path}`)\n }\n nextPath = nextPath.replace(`{${param}}`, encodeURIComponent(String(value)))\n nextPath = nextPath.replace(`:${param}`, encodeURIComponent(String(value)))\n }\n return nextPath\n}\n\nfunction withDefaultPathParams(\n payload: Record<string, unknown>,\n pathParams: string[],\n): Record<string, unknown> {\n if (!pathParams.length) return payload\n const nextPayload = { ...payload }\n for (const param of pathParams) {\n if (nextPayload[param] !== undefined && nextPayload[param] !== null) continue\n if (isExperimentPathParam(param)) {\n const experimentId = resolveCurrentExperimentId()\n if (experimentId !== undefined) {\n nextPayload[param] = experimentId\n }\n }\n }\n return nextPayload\n}\n\nfunction isExperimentPathParam(param: string): boolean {\n return param === 'experimentId' || param === 'experiment_id'\n}\n\nfunction withoutPathParams(payload: Record<string, unknown>, pathParams: string[]) {\n const copy = { ...payload }\n for (const param of pathParams) {\n delete copy[param]\n }\n return copy\n}\n\nfunction queryParamsFromPayload(\n payload: Record<string, unknown>,\n pathParams: string[],\n queryParams?: PluginEndpointParamDefinition[],\n endpointPath?: string,\n includeUnspecifiedParams = false,\n) {\n if (!queryParams?.length) {\n return includeUnspecifiedParams ? withoutPathParams(payload, pathParams) : {}\n }\n\n const query: Record<string, unknown> = {}\n for (const param of queryParams) {\n const name = typeof param === 'string' ? param : param.name\n const fieldName = typeof param === 'string' ? param : param.fieldName ?? param.name\n const required = typeof param !== 'string' && param.required === true\n const value = payload[fieldName]\n if (value === undefined || value === null) {\n if (required) {\n throw new Error(\n `[MINT SDK] Missing query parameter '${fieldName}' for plugin endpoint ${endpointPath ?? ''}`,\n )\n }\n continue\n }\n query[name] = value\n }\n return query\n}\n\nfunction hasKeys(value: Record<string, unknown>): boolean {\n return Object.keys(value).length > 0\n}\n\nfunction requestBodyFromPayload(\n payload: unknown,\n requestPayload: Record<string, unknown>,\n endpoint: PluginEndpointDefinition,\n): unknown {\n if (!endpoint.hasBody) return undefined\n const body = endpoint.pathParams?.length || endpoint.queryParams?.length\n ? requestPayload.body\n : payload\n if (body === undefined || body === null) {\n throw new Error(`[MINT SDK] Missing request body for plugin endpoint ${endpoint.path}`)\n }\n return body\n}\n\nfunction pluginEndpointRequestParts(\n contract: PluginContract,\n endpoint: PluginEndpointDefinition,\n payload?: unknown,\n explicitBaseUrl?: string,\n) {\n const payloadObject =\n payload && typeof payload === 'object' && !Array.isArray(payload)\n ? payload as Record<string, unknown>\n : {}\n const pathParams = endpoint.pathParams ?? []\n const requestPayload = withDefaultPathParams(payloadObject, pathParams)\n const path = encodePath(endpoint.path, requestPayload, pathParams)\n const query = queryParamsFromPayload(\n requestPayload,\n pathParams,\n endpoint.queryParams,\n endpoint.path,\n endpoint.method === 'get',\n )\n\n return {\n baseUrl: resolvePluginBaseUrl(contract, explicitBaseUrl),\n path,\n query,\n requestPayload,\n }\n}\n\nfunction appendQueryValue(params: URLSearchParams, key: string, value: unknown): void {\n if (value === undefined || value === null) return\n if (Array.isArray(value)) {\n for (const item of value) {\n appendQueryValue(params, key, item)\n }\n return\n }\n if (typeof value === 'object') {\n params.append(key, JSON.stringify(value))\n return\n }\n params.append(key, String(value))\n}\n\nfunction queryString(query: Record<string, unknown>): string {\n const params = new URLSearchParams()\n for (const [key, value] of Object.entries(query)) {\n appendQueryValue(params, key, value)\n }\n const serialized = params.toString()\n return serialized ? `?${serialized}` : ''\n}\n\nfunction joinBaseUrl(baseUrl: string, path: string): string {\n if (!baseUrl) return path\n const normalizedBase = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl\n const normalizedPath = path.startsWith('/') ? path : `/${path}`\n return `${normalizedBase}${normalizedPath}`\n}\n\n/** Build the concrete URL for a generated plugin endpoint without making a request. */\nexport function buildPluginEndpointUrl(\n contract: PluginContract,\n endpoint: PluginEndpointDefinition,\n payload?: unknown,\n options: BuildPluginEndpointUrlOptions = {},\n): string {\n const parts = pluginEndpointRequestParts(contract, endpoint, payload, options.baseUrl)\n const pathWithQuery = `${parts.path}${queryString(parts.query)}`\n if (options.includeBaseUrl === false) return pathWithQuery\n return joinBaseUrl(parts.baseUrl, pathWithQuery)\n}\n\n/** Create a typed plugin API client from a generated MINT plugin contract. */\nexport function createPluginClient<TClient extends object = GeneratedPluginClient>(\n contract: PluginContract,\n options: CreatePluginClientOptions,\n): TClient {\n const client: Record<string, PluginEndpointCaller> = {}\n\n for (const [name, endpoint] of Object.entries(options.endpoints)) {\n client[name] = async (payload?: unknown) => {\n const parts = pluginEndpointRequestParts(contract, endpoint, payload, options.baseUrl)\n const api = useApi({ baseUrl: parts.baseUrl })\n const queryConfig = hasKeys(parts.query) ? { params: parts.query } : undefined\n\n if (endpoint.method === 'get') {\n return api.get(parts.path, queryConfig)\n }\n\n if (endpoint.method === 'delete') {\n const body = requestBodyFromPayload(payload, parts.requestPayload, endpoint)\n const config = body === undefined\n ? queryConfig\n : { ...queryConfig, data: body }\n return api.delete(parts.path, config)\n }\n\n const body = requestBodyFromPayload(payload, parts.requestPayload, endpoint)\n\n if (endpoint.method === 'post') return api.post(parts.path, body, queryConfig)\n if (endpoint.method === 'put') return api.put(parts.path, body, queryConfig)\n if (endpoint.method === 'patch') return api.patch(parts.path, body, queryConfig)\n throw new Error(`[MINT SDK] Unsupported plugin endpoint method: ${endpoint.method}`)\n }\n }\n\n return client as TClient\n}\n\n/** Return a generated plugin client from setup code with a stable typed identity. */\nexport function usePluginClient<TClient>(client: TClient): TClient {\n return client\n}\n\n/** Read plugin settings exposed through the platform context. */\nexport function usePluginSettings<TSettings = Record<string, unknown>>() {\n const { plugin } = usePlatformContext()\n const settings = computed(() => plugin.value?.settings as TSettings | undefined)\n return { settings }\n}\n\n/** Read and optionally load the current platform experiment for integrated plugin views. */\nexport function useCurrentExperiment<TExperiment = ExperimentSummary>(\n options: UseCurrentExperimentOptions = {},\n): UseCurrentExperimentReturn<TExperiment> {\n const api = useApi({ baseUrl: options.apiBaseUrl ?? getInjectedPlatformContext()?.platformApiUrl })\n const experiment = shallowRef<TExperiment | undefined>(currentExperimentFromContext<TExperiment>())\n const request = useRequestSyncState('Failed to load current experiment')\n const isLoading = request.loading\n const error = request.error\n const lastLoadedAt = request.lastLoadedAt\n\n const experimentId = computed<number | undefined>(() => {\n return resolveCurrentExperimentId()\n })\n const hasExperiment = computed(() => experimentId.value !== undefined)\n\n function requireExperimentId(): number {\n const id = experimentId.value\n if (id === undefined) {\n throw new Error('[MINT SDK] No current experiment is selected.')\n }\n return id\n }\n\n async function fetchExperiment(id = experimentId.value): Promise<TExperiment | undefined> {\n if (id === undefined) {\n experiment.value = currentExperimentFromContext<TExperiment>()\n return experiment.value\n }\n\n try {\n const result = await request.run(\n () => api.get<TExperiment>(`/experiments/${id}`),\n { success: 'load', errorMessage: 'Failed to load current experiment' },\n )\n experiment.value = result\n return result\n } catch (e) {\n experiment.value = undefined\n return undefined\n }\n }\n\n async function refresh(): Promise<TExperiment | undefined> {\n return fetchExperiment()\n }\n\n watch(\n experimentId,\n (id) => {\n if (options.immediate === false) return\n if (id === undefined) {\n experiment.value = currentExperimentFromContext<TExperiment>()\n return\n }\n void fetchExperiment(id)\n },\n { immediate: options.immediate !== false },\n )\n\n return {\n experimentId,\n hasExperiment,\n experiment,\n isLoading,\n error,\n lastLoadedAt,\n requireExperimentId,\n fetch: fetchExperiment,\n refresh,\n }\n}\n"],"mappings":";;;;;AAsBA,IAAM,0BAA0B,MAAS;AACzC,IAAM,kCAAkC,KAAK;AAsC7C,IAAI,kBAA2C;AAC/C,IAAI,kBAAiC;AACrC,IAAI,wBAAwB;;AAa5B,SAAgB,UAAyB;CACvC,MAAM,YAAY,cAAc;CAChC,MAAM,gBAAgB,kBAAkB;CAExC,MAAM,eAAe,IAAI,MAAM;CAE/B,SAAS,gBAAwB;AAC/B,SAAO,cAAc,eAAe;;CAGtC,eAAe,kBAAuC;AACpD,MAAI;GACF,MAAM,WAAW,MAAM,MAAM,IAM1B,GAAG,eAAe,CAAC,sBAAsB;GAE5C,MAAM,SAAqB;IACzB,cAAc,SAAS,KAAK;IAC5B,gBAAgB,SAAS,KAAK;IAC9B,mBAAmB,SAAS,KAAK,sBAAsB;IACvD,qBAAqB,SAAS,KAAK,wBAAwB;IAC3D,cAAc,SAAS,KAAK,iBAAiB;IAC9C;AAED,aAAU,cAAc,OAAO;AAC/B,UAAO;WACA,OAAO;AACd,WAAQ,MAAM,gCAAgC,MAAM;AACpD,UAAO;IACL,cAAc;IACd,gBAAgB;IAChB,mBAAmB;IACnB,qBAAqB;IACrB,cAAc;IACf;;;CAIL,eAAe,MAAM,UAAkB,UAAoC;AACzE,YAAU,WAAW,KAAK;AAC1B,YAAU,SAAS,KAAK;AAExB,MAAI;GACF,MAAM,WAAW,MAAM,MAAM,KAC3B,GAAG,eAAe,CAAC,cACnB;IAAE;IAAU;IAAU,CACvB;AAED,aAAU,SAAS,SAAS,KAAK,cAAc,SAAS,KAAK,WAAW;AACxE,aAAU,YAAY,SAAS;AAE/B,SAAM,gBAAgB;AAGtB,yBAAsB;AAEtB,UAAO;WACA,OAAO;AACd,OAAI,MAAM,aAAa,MAAM,IAAI,MAAM,SACrC,WAAU,SAAS,MAAM,SAAS,KAAK,UAAU,eAAe;OAEhE,WAAU,SAAS,mCAAmC;AAExD,UAAO;YACC;AACR,aAAU,WAAW,MAAM;;;CAI/B,eAAe,SAAS,UAAkB,UAAkB,OAAkC;AAC5F,YAAU,WAAW,KAAK;AAC1B,YAAU,SAAS,KAAK;AAExB,MAAI;AACF,SAAM,MAAM,KACV,GAAG,eAAe,CAAC,kBACnB;IAAE;IAAU;IAAU;IAAO,CAC9B;AAED,UAAO,MAAM,MAAM,UAAU,SAAS;WAC/B,OAAO;AACd,OAAI,MAAM,aAAa,MAAM,IAAI,MAAM,SACrC,WAAU,SAAS,MAAM,SAAS,KAAK,UAAU,sBAAsB;OAEvE,WAAU,SAAS,mCAAmC;AAExD,UAAO;YACC;AACR,aAAU,WAAW,MAAM;;;CAI/B,eAAe,iBAA2C;AACxD,MAAI,CAAC,UAAU,MACb,QAAO;AAGT,MAAI;GACF,MAAM,WAAW,MAAM,MAAM,IAC3B,GAAG,eAAe,CAAC,YACnB,EAAE,SAAS,eAAe,EAAE,CAC7B;GAED,MAAM,WAAqB;IACzB,IAAI,SAAS,KAAK;IAClB,UAAU,SAAS,KAAK;IACxB,WAAW,SAAS,KAAK;IACzB,OAAO,SAAS,KAAK;IACrB,MAAM,SAAS,KAAK;IACpB,UAAU,SAAS,KAAK;IACzB;AAED,aAAU,YAAY,SAAS;AAC/B,UAAO;UACD;AACN,UAAO;;;CAIX,eAAe,cAAgC;AAC7C,MAAI,CAAC,UAAU,MACb,QAAO;AAGT,MAAI;GACF,MAAM,WAAW,MAAM,MAAM,IAC3B,GAAG,eAAe,CAAC,eACnB,EACE,SAAS,EACP,eAAe,UAAU,UAAU,SACpC,EACF,CACF;AAED,OAAI,SAAS,KAAK,SAAS,SAAS,KAAK,UAAU;AACjD,cAAU,YAAY,SAAS,KAAK,SAAS;AAC7C,WAAO;;AAGT,aAAU,YAAY;AACtB,UAAO;UACD;AACN,aAAU,YAAY;AACtB,UAAO;;;;;;;;CASX,eAAe,eAAiC;AAC9C,MAAI,CAAC,UAAU,MAAO,QAAO;AAC7B,MAAI,gBAAiB,QAAO;AAE5B,qBAAmB,YAAY;AAC7B,gBAAa,QAAQ;AAErB,OAAI;IACF,MAAM,WAAW,MAAM,MAAM,KAC3B,GAAG,eAAe,CAAC,gBACnB,EAAE,EACF,EAAE,SAAS,eAAe,EAAE,CAC7B;AAED,cAAU,SAAS,SAAS,KAAK,cAAc,SAAS,KAAK,WAAW;AAGxE,0BAAsB;AAEtB,WAAO;YACA,OAAO;AAGd,QAAI,MAAM,aAAa,MAAM,IAAI,MAAM,UAAU,WAAW,KAAK;AAC/D,aAAQ,KAAK,gDAAgD;AAC7D,eAAU,YAAY;AACtB,uBAAkB;;AAEpB,WAAO;aACC;AACR,iBAAa,QAAQ;AACrB,sBAAkB;;MAElB;AAEJ,SAAO;;;;;CAMT,SAAS,uBAA6B;AAEpC,oBAAkB;AAElB,MAAI,CAAC,UAAU,aACb;EAIF,MAAM,YADY,UAAU,aAAa,SAAS,GACpB;EAC9B,MAAM,MAAM,KAAK,KAAK;AAEtB,MAAI,aAAa,KAAK;AAEpB,iBAAc;AACd;;EAIF,MAAM,QAAQ,YAAY;AAC1B,oBAAkB,OAAO,iBAAiB;AACxC,iBAAc;KACb,MAAM;;;;;CAMX,SAAS,mBAAyB;AAChC,MAAI,oBAAoB,MAAM;AAC5B,UAAO,aAAa,gBAAgB;AACpC,qBAAkB;;;;;;;CAQtB,SAAS,0BAAgC;AACvC,MAAI,CAAC,UAAU,SAAS,CAAC,UAAU,aACjC;EAIF,MAAM,YADY,UAAU,aAAa,SAAS,GACpB;AAG9B,MAFY,KAAK,KAAK,IAEX,UACT,eAAc;;CAIlB,eAAe,iBAAgC;AAC7C,YAAU,YAAY;AACtB,QAAM,iBAAiB;AAEvB,MAAI,UAAU;OACE,MAAM,aAAa,EACtB;AACT,UAAM,gBAAgB;AAEtB,0BAAsB;;;;CAK5B,SAAS,SAAe;AACtB,oBAAkB;AAClB,YAAU,QAAQ;;CAGpB,SAAS,gBAAwC;AAC/C,MAAI,UAAU,MACZ,QAAO,EAAE,eAAe,UAAU,UAAU,SAAS;AAEvD,SAAO,EAAE;;CAGX,eAAe,cAAc,MAKqB;AAChD,MAAI,CAAC,UAAU,MACb,QAAO;GAAE,SAAS;GAAO,OAAO;GAAqB;AAGvD,MAAI;GACF,MAAM,cAAoC,EAAE;AAC5C,OAAI,KAAK,UAAU,KAAA,EAAW,aAAY,QAAQ,KAAK;AACvD,OAAI,KAAK,cAAc,KAAA,EAAW,aAAY,YAAY,KAAK;AAC/D,OAAI,KAAK,gBAAiB,aAAY,mBAAmB,KAAK;AAC9D,OAAI,KAAK,YAAa,aAAY,eAAe,KAAK;GAEtD,MAAM,WAAW,MAAM,MAAM,IAC3B,GAAG,eAAe,CAAC,YACnB,aACA,EAAE,SAAS,eAAe,EAAE,CAC7B;GAED,MAAM,WAAqB;IACzB,IAAI,SAAS,KAAK;IAClB,UAAU,SAAS,KAAK;IACxB,WAAW,SAAS,KAAK;IACzB,OAAO,SAAS,KAAK;IACrB,MAAM,SAAS,KAAK;IACpB,UAAU,SAAS,KAAK;IACzB;AACD,aAAU,YAAY,SAAS;AAE/B,UAAO,EAAE,SAAS,MAAM;WACjB,OAAO;AACd,OAAI,MAAM,aAAa,MAAM,IAAI,MAAM,SACrC,QAAO;IAAE,SAAS;IAAO,OAAO,MAAM,SAAS,KAAK,UAAU;IAAiB;AAEjF,UAAO;IAAE,SAAS;IAAO,OAAO;IAAoC;;;CAKxE,IAAI,gBAA+B;AAEnC,KAAI,oBAAoB,EAAE;AACxB,kBAAgB;AACd,4BAAyB;AACzB,mBAAgB,OAAO,YAAY,yBAAyB,gCAAgC;IAC5F;AAEF,oBAAkB;AAChB,OAAI,kBAAkB,MAAM;AAC1B,WAAO,cAAc,cAAc;AACnC,oBAAgB;;AAGlB,2BAAwB,KAAK,IAAI,GAAG,wBAAwB,EAAE;AAC9D,OAAI,0BAA0B,EAC5B,mBAAkB;IAEpB;AAGF,cACQ,UAAU,eACf,eAAe;AACd,OAAI,WACF,uBAAsB;OAEtB,mBAAkB;IAGvB;;AAGH,QAAO;EAEL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACD;;;;ACpbH,eAAe,eAAe;AAC5B,KAAI;AACF,SAAO,MAAM,OAAO;SACd;AACN,QAAM,IAAI,MACR,uGACD;;;;AAWL,SAAgB,aAAa;CAC3B,MAAM,YAAY,cAAc;CAChC,MAAM,gBAAgB,kBAAkB;CAExC,SAAS,gBAAwB;AAC/B,SAAO,cAAc,eAAe;;CAGtC,eAAe,cAAgC;AAC7C,MAAI;GACF,MAAM,EAAE,4BAA4B,MAAM,cAAc;AACxD,UAAO,yBAAyB;UAC1B;AACN,UAAO;;;CAIX,eAAe,gBAAgB,YAAuC;EACpE,MAAM,WAAW,MAAM,cAAc;AACrC,MAAI,CAAC,SAAS,yBAAyB,EAAE;AACvC,aAAU,SAAS,4CAA4C;AAC/D,UAAO;;AAGT,YAAU,WAAW,KAAK;AAC1B,YAAU,SAAS,KAAK;AAExB,MAAI;GACF,MAAM,kBAAkB,MAAM,MAAM,IAClC,GAAG,eAAe,CAAC,iCACnB;IACE,SAAS,EAAE,eAAe,UAAU,UAAU,SAAS;IACvD,iBAAiB;IAClB,CACF;GAED,MAAM,UAAU,KAAK,MAAM,gBAAgB,KAAK,QAAQ;GAExD,MAAM,aAAa,MAAM,SAAS,kBAAkB,QAAQ;AAE5D,SAAM,MAAM,KACV,GAAG,eAAe,CAAC,gCACnB;IACE,YAAY,KAAK,UAAU,WAAW;IACtC,aAAa;IACd,EACD;IACE,SAAS,EAAE,eAAe,UAAU,UAAU,SAAS;IACvD,iBAAiB;IAClB,CACF;AAED,aAAU,cAAc;IACtB,GAAG,UAAU;IACb,mBAAmB;IACpB,CAAC;AAEF,UAAO;WACA,OAAO;AACd,OAAI,MAAM,aAAa,MAAM,IAAI,MAAM,SACrC,WAAU,SAAS,MAAM,SAAS,KAAK,UAAU,8BAA8B;YACtE,iBAAiB,MAC1B,KAAI,MAAM,SAAS,kBACjB,WAAU,SAAS,0CAA0C;YACpD,MAAM,SAAS,oBACxB,WAAU,SAAS,2CAA2C;OAE9D,WAAU,SAAS,MAAM,QAAQ;OAGnC,WAAU,SAAS,8BAA8B;AAEnD,UAAO;YACC;AACR,aAAU,WAAW,MAAM;;;CAI/B,eAAe,mBAAqC;EAClD,MAAM,WAAW,MAAM,cAAc;AACrC,MAAI,CAAC,SAAS,yBAAyB,EAAE;AACvC,aAAU,SAAS,4CAA4C;AAC/D,UAAO;;AAGT,YAAU,WAAW,KAAK;AAC1B,YAAU,SAAS,KAAK;AAExB,MAAI;GACF,MAAM,kBAAkB,MAAM,MAAM,IAClC,GAAG,eAAe,CAAC,8BACnB,EAAE,iBAAiB,MAAM,CAC1B;GAED,MAAM,UAAU,KAAK,MAAM,gBAAgB,KAAK,QAAQ;GAExD,MAAM,aAAa,MAAM,SAAS,oBAAoB,QAAQ;GAE9D,MAAM,WAAW,MAAM,MAAM,KAC3B,GAAG,eAAe,CAAC,6BACnB,EAAE,YAAY,KAAK,UAAU,WAAW,EAAE,EAC1C,EAAE,iBAAiB,MAAM,CAC1B;AAED,aAAU,SAAS,SAAS,KAAK,cAAc,SAAS,KAAK,WAAW;AAExE,UAAO;WACA,OAAO;AACd,OAAI,MAAM,aAAa,MAAM,IAAI,MAAM,SACrC,KAAI,MAAM,SAAS,WAAW,IAC5B,WAAU,SAAS,4DAA4D;OAE/E,WAAU,SAAS,MAAM,SAAS,KAAK,UAAU,uBAAuB;YAEjE,iBAAiB,MAC1B,KAAI,MAAM,SAAS,kBACjB,WAAU,SAAS,4CAA4C;OAE/D,WAAU,SAAS,MAAM,QAAQ;OAGnC,WAAU,SAAS,uBAAuB;AAE5C,UAAO;YACC;AACR,aAAU,WAAW,MAAM;;;CAI/B,eAAe,kBAA6C;AAC1D,MAAI;AAOF,WANiB,MAAM,MAAM,IAC3B,GAAG,eAAe,CAAC,4BACnB,EACE,SAAS,EAAE,eAAe,UAAU,UAAU,SAAS,EACxD,CACF,EACe,KAAK;UACf;AACN,UAAO,EAAE;;;CAIb,eAAe,iBAAiB,cAAwC;AACtE,MAAI;AACF,SAAM,MAAM,OACV,GAAG,eAAe,CAAC,4BAA4B,mBAAmB,aAAa,IAC/E,EACE,SAAS,EAAE,eAAe,UAAU,UAAU,SAAS,EACxD,CACF;AAGD,QADkB,MAAM,iBAAiB,EAC3B,WAAW,EACvB,WAAU,cAAc;IACtB,GAAG,UAAU;IACb,mBAAmB;IACpB,CAAC;AAGJ,UAAO;UACD;AACN,UAAO;;;CAIX,eAAe,uBAAyC;AACtD,MAAI;AACF,SAAM,MAAM,OAAO,GAAG,eAAe,CAAC,4BAA4B,EAChE,SAAS,EAAE,eAAe,UAAU,UAAU,SAAS,EACxD,CAAC;AAEF,aAAU,cAAc;IACtB,GAAG,UAAU;IACb,mBAAmB;IACpB,CAAC;AAEF,UAAO;UACD;AACN,UAAO;;;AAIX,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACD;;;;;;;AClJH,SAAS,sBAAsB,OAA4B;AACzD,KAAI,iBAAiB,MACnB,QAAO;EACL,SAAS,MAAM;EACf,eAAe;EAChB;AAGH,KAAI,OAAO,UAAU,YAAY,UAAU,MAAM;EAC/C,MAAM,WAAW;AAGjB,MAAI,cAAc,YAAY,SAAS,UAAU;GAC/C,MAAM,WAAW,SAAS;GAC1B,MAAM,OAAO,SAAS;AAEtB,UAAO;IACL,SAAU,MAAM,UAAsB,MAAM,WAAsB;IAClE,MAAM,OAAO,SAAS,OAAO;IAC7B,SAAS;IACT,eAAe;IAChB;;AAIH,SAAO;GACL,SAAU,SAAS,WAAsB;GACzC,MAAM,SAAS;GACf,SAAS;GACT,eAAe;GAChB;;AAGH,QAAO;EACL,SAAS,OAAO,MAAM;EACtB,eAAe;EAChB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgEH,SAAgB,SACd,SACA,UAA8B,EAAE,EACb;CACnB,MAAM,EACJ,cAAc,MACd,YAAY,OACZ,gBAAgB,EAAE,EAClB,iBAAiB,uBACjB,WACA,SACA,iBAAiB,UACf;CAGJ,MAAM,OAAO,IAAc,YAAY;CACvC,MAAM,QAAQ,IAAuB,KAAK;CAC1C,MAAM,QAAQ,IAAgB,OAAO;CAGrC,MAAM,SAAS,eAAe,MAAM,UAAU,OAAO;CACrD,MAAM,YAAY,eAAe,MAAM,UAAU,UAAU;CAC3D,MAAM,YAAY,eAAe,MAAM,UAAU,UAAU;CAC3D,MAAM,UAAU,eAAe,MAAM,UAAU,QAAQ;CAGvD,eAAe,QAAQ,GAAG,MAAoC;AAC5D,QAAM,QAAQ;AACd,QAAM,QAAQ;AAEd,MAAI,eACF,MAAK,QAAQ;AAGf,MAAI;GACF,MAAM,SAAS,MAAM,QAAQ,GAAG,KAAK;AACrC,QAAK,QAAQ;AACb,SAAM,QAAQ;AACd,eAAY,OAAO;AACnB,UAAO;WACA,GAAG;GACV,MAAM,aAAa,eAAe,EAAE;AACpC,SAAM,QAAQ;AACd,SAAM,QAAQ;AACd,aAAU,WAAW;AACrB,UAAO;;;CAKX,SAAS,QAAc;AACrB,OAAK,QAAQ;AACb,QAAM,QAAQ;AACd,QAAM,QAAQ;;CAIhB,SAAS,QAAQ,SAAyB;AACxC,OAAK,QAAQ;AACb,MAAI,YAAY,MAAM;AACpB,SAAM,QAAQ;AACd,SAAM,QAAQ;;;CAKlB,SAAS,SAAS,UAAmC;AACnD,QAAM,QAAQ;AACd,MAAI,aAAa,KACf,OAAM,QAAQ;;AAKlB,KAAI,UACF,SAAQ,GAAG,cAAc;AAG3B,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;;;;;;;;;;;;;;;;AAkBH,SAAgB,cACd,UAOA;CAGA,MAAM,UAAU,IAAa,SAAS,UAAU,KAAK,CAAuB;CAC5E,MAAM,SAAS,IAA2B,SAAS,UAAU,KAAK,CAAC;CACnE,MAAM,YAAY,IAAI,MAAM;CAE5B,eAAe,UAAyB;AACtC,YAAU,QAAQ;AAClB,SAAO,QAAQ,SAAS,UAAU,KAAK;EAEvC,MAAM,WAAW,SAAS,IAAI,OAAO,IAAI,UAAU;AACjD,OAAI;IACF,MAAM,SAAS,MAAM,IAAI;AACvB,YAAQ,MAAoB,SAAS;YAChC,GAAG;AACV,WAAO,MAAM,SAAS,sBAAsB,EAAE;AAC5C,YAAQ,MAAoB,SAAS;;IAEzC;AAEF,QAAM,QAAQ,IAAI,SAAS;AAC3B,YAAU,QAAQ;;CAGpB,SAAS,QAAc;AACrB,UAAQ,QAAQ,SAAS,UAAU,KAAK;AACxC,SAAO,QAAQ,SAAS,UAAU,KAAK;AACvC,YAAU,QAAQ;;AAGpB,QAAO;EACL;EACA;EACA;EACA;EACA;EACD;;;;;ACpSH,SAAgB,gBAAgB,YAA4C;CAC1E,MAAM,MAAM,QAAQ;CACpB,MAAM,EAAE,WAAW,oBAAoB;CAEvC,MAAM,eAAe,eAAe,cAAc,OAAO,OAAO,QAAQ,GAAG;CAE3E,MAAM,SAAS,IAA6B,EAAE,CAAC;CAC/C,MAAM,cAAc,IAA6B,EAAE,CAAC;CACpD,MAAM,UAAU,oBAAoB,gCAAgC;CACpE,MAAM,YAAY,IAAI,MAAM;CAC5B,MAAM,WAAW,IAAI,MAAM;CAC3B,MAAM,QAAQ,QAAQ;CACtB,MAAM,eAAe,QAAQ;CAC7B,MAAM,cAAc,QAAQ;CAE5B,MAAM,UAAU,eAAe;AAC7B,SAAO,KAAK,UAAU,OAAO,MAAM,KAAK,KAAK,UAAU,YAAY,MAAM;GACzE;CAEF,eAAe,OAAsB;EACnC,MAAM,OAAO,aAAa;AAC1B,MAAI,CAAC,KAAM;AAEX,YAAU,QAAQ;AAClB,MAAI;GACF,MAAM,WAAW,MAAM,QAAQ,UACvB,IAAI,IACR,YAAY,mBAAmB,KAAK,CAAC,SACtC,EACD;IAAE,SAAS;IAAQ,cAAc;IAAgC,CAClE;AACD,UAAO,QAAQ,EAAE,GAAG,SAAS,QAAQ;AACrC,eAAY,QAAQ,EAAE,GAAG,SAAS,QAAQ;UACpC,WAEE;AACR,aAAU,QAAQ;;;CAItB,eAAe,OAAyB;EACtC,MAAM,OAAO,aAAa;AAC1B,MAAI,CAAC,KAAM,QAAO;AAElB,WAAS,QAAQ;AACjB,MAAI;GACF,MAAM,WAAW,MAAM,QAAQ,UACvB,IAAI,MACR,YAAY,mBAAmB,KAAK,CAAC,UACrC,EAAE,QAAQ,OAAO,OAAO,CACzB,EACD;IAAE,SAAS;IAAQ,cAAc;IAAgC,CAClE;AACD,UAAO,QAAQ,EAAE,GAAG,SAAS,QAAQ;AACrC,eAAY,QAAQ,EAAE,GAAG,SAAS,QAAQ;AAC1C,UAAO;UACD;AACN,UAAO;YACC;AACR,YAAS,QAAQ;;;CAIrB,SAAS,QAAc;AACrB,SAAO,QAAQ,EAAE,GAAG,YAAY,OAAO;AACvC,UAAQ,YAAY;;AAGtB,iBAAgB;AACd,QAAM;GACN;AAEF,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;;;ACKH,SAAS,oBAAoB,SAAiD;AAC5E,KAAI,CAAC,QAAS,QAAO,KAAA;AACrB,QAAO,QAAQ,SAAS,IAAI,QAAQ,QAAQ,QAAQ,GAAG,GAAG;;AAG5D,SAAS,mBAAmB,QAAgD;AAC1E,KAAI,CAAC,OAAQ,QAAO,KAAA;AACpB,KAAI,OAAO,WAAW,QAAQ,CAAE,QAAO,oBAAoB,OAAO;AAClE,KAAI,WAAW,OAAQ,QAAO;AAC9B,KAAI,OAAO,WAAW,IAAI,CAAE,QAAO,oBAAoB,OAAO,SAAS;AACvE,QAAO,oBAAoB,QAAQ,SAAS;;AAG9C,SAAS,uBAAuB,MAAsB;CACpD,MAAM,MAAM,KAAK,MAAM,IAAI;AAE3B,SADiB,IAAI,WAAW,IAAI,GAAG,MAAM,IAAI,OACjC,QAAQ,QAAQ,GAAG,IAAI;;AAGzC,SAAS,qBAAqB,MAAc,eAA+B;CACzE,MAAM,iBAAiB,uBAAuB,KAAK;AACnD,KAAI,mBAAmB,IAAK,QAAO;AACnC,QAAO,eAAe,QAAQ,QAAQ,GAAG,CAAC,QAAQ,QAAQ,IAAI,IAAI,QAAQ,gBAAgB;;;AAI5F,SAAgB,2BAA2B,UAA8C;CACvF,MAAM,aAAa,SAAS,OAAO,QAAQ;CAC3C,MAAM,WAAW,SAAS,OAAO,YAAY,EAAE;AAW/C,SAVwC,SAAS,SAAS,IACtD,WACA,CAAC;EACC,MAAM;EACN,OAAO,SAAS,OAAO,QAAQ;EAC/B,IAAI;EACJ,MAAM,cAAc,KAAA;EACpB,aAAa,SAAS,OAAO;EAC9B,CAAC,EAEQ,KAAK,MAAM,WAAW;EAClC,IAAI,KAAK,MAAM,qBAAqB,KAAK,MAAM,MAAM;EACrD,OAAO,KAAK;EACZ,IAAI,uBAAuB,KAAK,KAAK;EACrC,MAAM,KAAK,QAAQ,cAAc,KAAA;EACjC,MAAM,KAAK,eAAe,SAAS,OAAO;EAC3C,EAAE;;;AAIL,SAAgB,qBAAqB,UAA0B,iBAAkC;AAG/F,KAAI,gBAAiB,QAAO,oBAAoB,gBAAgB,IAAI;CAEpE,MAAM,WAAW,4BAA4B;CAC7C,MAAM,iBACJ,oBAAoB,UAAU,QAAQ,WAAW,IACjD,mBAAmB,UAAU,QAAQ,aAAa;AACpD,KAAI,eAAgB,QAAO;AAE3B,QAAO,oBAAoB,SAAS,OAAO,UAAU,IAAI;;AAG3D,SAAS,WAAW,MAAc,SAAkC,YAA8B;CAChG,IAAI,WAAW;AACf,MAAK,MAAM,SAAS,YAAY;EAC9B,MAAM,QAAQ,QAAQ;AACtB,MAAI,UAAU,KAAA,KAAa,UAAU,KACnC,OAAM,IAAI,MAAM,sCAAsC,MAAM,wBAAwB,OAAO;AAE7F,aAAW,SAAS,QAAQ,IAAI,MAAM,IAAI,mBAAmB,OAAO,MAAM,CAAC,CAAC;AAC5E,aAAW,SAAS,QAAQ,IAAI,SAAS,mBAAmB,OAAO,MAAM,CAAC,CAAC;;AAE7E,QAAO;;AAGT,SAAS,sBACP,SACA,YACyB;AACzB,KAAI,CAAC,WAAW,OAAQ,QAAO;CAC/B,MAAM,cAAc,EAAE,GAAG,SAAS;AAClC,MAAK,MAAM,SAAS,YAAY;AAC9B,MAAI,YAAY,WAAW,KAAA,KAAa,YAAY,WAAW,KAAM;AACrE,MAAI,sBAAsB,MAAM,EAAE;GAChC,MAAM,eAAe,4BAA4B;AACjD,OAAI,iBAAiB,KAAA,EACnB,aAAY,SAAS;;;AAI3B,QAAO;;AAGT,SAAS,sBAAsB,OAAwB;AACrD,QAAO,UAAU,kBAAkB,UAAU;;AAG/C,SAAS,kBAAkB,SAAkC,YAAsB;CACjF,MAAM,OAAO,EAAE,GAAG,SAAS;AAC3B,MAAK,MAAM,SAAS,WAClB,QAAO,KAAK;AAEd,QAAO;;AAGT,SAAS,uBACP,SACA,YACA,aACA,cACA,2BAA2B,OAC3B;AACA,KAAI,CAAC,aAAa,OAChB,QAAO,2BAA2B,kBAAkB,SAAS,WAAW,GAAG,EAAE;CAG/E,MAAM,QAAiC,EAAE;AACzC,MAAK,MAAM,SAAS,aAAa;EAC/B,MAAM,OAAO,OAAO,UAAU,WAAW,QAAQ,MAAM;EACvD,MAAM,YAAY,OAAO,UAAU,WAAW,QAAQ,MAAM,aAAa,MAAM;EAC/E,MAAM,WAAW,OAAO,UAAU,YAAY,MAAM,aAAa;EACjE,MAAM,QAAQ,QAAQ;AACtB,MAAI,UAAU,KAAA,KAAa,UAAU,MAAM;AACzC,OAAI,SACF,OAAM,IAAI,MACR,uCAAuC,UAAU,wBAAwB,gBAAgB,KAC1F;AAEH;;AAEF,QAAM,QAAQ;;AAEhB,QAAO;;AAGT,SAAS,QAAQ,OAAyC;AACxD,QAAO,OAAO,KAAK,MAAM,CAAC,SAAS;;AAGrC,SAAS,uBACP,SACA,gBACA,UACS;AACT,KAAI,CAAC,SAAS,QAAS,QAAO,KAAA;CAC9B,MAAM,OAAO,SAAS,YAAY,UAAU,SAAS,aAAa,SAC9D,eAAe,OACf;AACJ,KAAI,SAAS,KAAA,KAAa,SAAS,KACjC,OAAM,IAAI,MAAM,uDAAuD,SAAS,OAAO;AAEzF,QAAO;;AAGT,SAAS,2BACP,UACA,UACA,SACA,iBACA;CACA,MAAM,gBACJ,WAAW,OAAO,YAAY,YAAY,CAAC,MAAM,QAAQ,QAAQ,GAC7D,UACA,EAAE;CACR,MAAM,aAAa,SAAS,cAAc,EAAE;CAC5C,MAAM,iBAAiB,sBAAsB,eAAe,WAAW;CACvE,MAAM,OAAO,WAAW,SAAS,MAAM,gBAAgB,WAAW;CAClE,MAAM,QAAQ,uBACZ,gBACA,YACA,SAAS,aACT,SAAS,MACT,SAAS,WAAW,MACrB;AAED,QAAO;EACL,SAAS,qBAAqB,UAAU,gBAAgB;EACxD;EACA;EACA;EACD;;AAGH,SAAS,iBAAiB,QAAyB,KAAa,OAAsB;AACpF,KAAI,UAAU,KAAA,KAAa,UAAU,KAAM;AAC3C,KAAI,MAAM,QAAQ,MAAM,EAAE;AACxB,OAAK,MAAM,QAAQ,MACjB,kBAAiB,QAAQ,KAAK,KAAK;AAErC;;AAEF,KAAI,OAAO,UAAU,UAAU;AAC7B,SAAO,OAAO,KAAK,KAAK,UAAU,MAAM,CAAC;AACzC;;AAEF,QAAO,OAAO,KAAK,OAAO,MAAM,CAAC;;AAGnC,SAAS,YAAY,OAAwC;CAC3D,MAAM,SAAS,IAAI,iBAAiB;AACpC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,CAC9C,kBAAiB,QAAQ,KAAK,MAAM;CAEtC,MAAM,aAAa,OAAO,UAAU;AACpC,QAAO,aAAa,IAAI,eAAe;;AAGzC,SAAS,YAAY,SAAiB,MAAsB;AAC1D,KAAI,CAAC,QAAS,QAAO;AAGrB,QAAO,GAFgB,QAAQ,SAAS,IAAI,GAAG,QAAQ,MAAM,GAAG,GAAG,GAAG,UAC/C,KAAK,WAAW,IAAI,GAAG,OAAO,IAAI;;;AAK3D,SAAgB,uBACd,UACA,UACA,SACA,UAAyC,EAAE,EACnC;CACR,MAAM,QAAQ,2BAA2B,UAAU,UAAU,SAAS,QAAQ,QAAQ;CACtF,MAAM,gBAAgB,GAAG,MAAM,OAAO,YAAY,MAAM,MAAM;AAC9D,KAAI,QAAQ,mBAAmB,MAAO,QAAO;AAC7C,QAAO,YAAY,MAAM,SAAS,cAAc;;;AAIlD,SAAgB,mBACd,UACA,SACS;CACT,MAAM,SAA+C,EAAE;AAEvD,MAAK,MAAM,CAAC,MAAM,aAAa,OAAO,QAAQ,QAAQ,UAAU,CAC9D,QAAO,QAAQ,OAAO,YAAsB;EAC1C,MAAM,QAAQ,2BAA2B,UAAU,UAAU,SAAS,QAAQ,QAAQ;EACtF,MAAM,MAAM,OAAO,EAAE,SAAS,MAAM,SAAS,CAAC;EAC9C,MAAM,cAAc,QAAQ,MAAM,MAAM,GAAG,EAAE,QAAQ,MAAM,OAAO,GAAG,KAAA;AAErE,MAAI,SAAS,WAAW,MACtB,QAAO,IAAI,IAAI,MAAM,MAAM,YAAY;AAGzC,MAAI,SAAS,WAAW,UAAU;GAChC,MAAM,OAAO,uBAAuB,SAAS,MAAM,gBAAgB,SAAS;GAC5E,MAAM,SAAS,SAAS,KAAA,IACpB,cACA;IAAE,GAAG;IAAa,MAAM;IAAM;AAClC,UAAO,IAAI,OAAO,MAAM,MAAM,OAAO;;EAGvC,MAAM,OAAO,uBAAuB,SAAS,MAAM,gBAAgB,SAAS;AAE5E,MAAI,SAAS,WAAW,OAAQ,QAAO,IAAI,KAAK,MAAM,MAAM,MAAM,YAAY;AAC9E,MAAI,SAAS,WAAW,MAAO,QAAO,IAAI,IAAI,MAAM,MAAM,MAAM,YAAY;AAC5E,MAAI,SAAS,WAAW,QAAS,QAAO,IAAI,MAAM,MAAM,MAAM,MAAM,YAAY;AAChF,QAAM,IAAI,MAAM,kDAAkD,SAAS,SAAS;;AAIxF,QAAO;;;AAIT,SAAgB,gBAAyB,QAA0B;AACjE,QAAO;;;AAIT,SAAgB,oBAAyD;CACvE,MAAM,EAAE,WAAW,oBAAoB;AAEvC,QAAO,EAAE,UADQ,eAAe,OAAO,OAAO,SAAkC,EAC7D;;;AAIrB,SAAgB,qBACd,UAAuC,EAAE,EACA;CACzC,MAAM,MAAM,OAAO,EAAE,SAAS,QAAQ,cAAc,4BAA4B,EAAE,gBAAgB,CAAC;CACnG,MAAM,aAAa,WAAoC,8BAA2C,CAAC;CACnG,MAAM,UAAU,oBAAoB,oCAAoC;CACxE,MAAM,YAAY,QAAQ;CAC1B,MAAM,QAAQ,QAAQ;CACtB,MAAM,eAAe,QAAQ;CAE7B,MAAM,eAAe,eAAmC;AACtD,SAAO,4BAA4B;GACnC;CACF,MAAM,gBAAgB,eAAe,aAAa,UAAU,KAAA,EAAU;CAEtE,SAAS,sBAA8B;EACrC,MAAM,KAAK,aAAa;AACxB,MAAI,OAAO,KAAA,EACT,OAAM,IAAI,MAAM,gDAAgD;AAElE,SAAO;;CAGT,eAAe,gBAAgB,KAAK,aAAa,OAAyC;AACxF,MAAI,OAAO,KAAA,GAAW;AACpB,cAAW,QAAQ,8BAA2C;AAC9D,UAAO,WAAW;;AAGpB,MAAI;GACF,MAAM,SAAS,MAAM,QAAQ,UACrB,IAAI,IAAiB,gBAAgB,KAAK,EAChD;IAAE,SAAS;IAAQ,cAAc;IAAqC,CACvE;AACD,cAAW,QAAQ;AACnB,UAAO;WACA,GAAG;AACV,cAAW,QAAQ,KAAA;AACnB;;;CAIJ,eAAe,UAA4C;AACzD,SAAO,iBAAiB;;AAG1B,OACE,eACC,OAAO;AACN,MAAI,QAAQ,cAAc,MAAO;AACjC,MAAI,OAAO,KAAA,GAAW;AACpB,cAAW,QAAQ,8BAA2C;AAC9D;;AAEG,kBAAgB,GAAG;IAE1B,EAAE,WAAW,QAAQ,cAAc,OAAO,CAC3C;AAED,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;EACD"}
1
+ {"version":3,"file":"composables-D4Myb30a.js","names":[],"sources":["../src/composables/useAuth.ts","../src/composables/usePasskey.ts","../src/composables/useAsync.ts","../src/composables/usePluginConfig.ts","../src/composables/usePluginClient.ts"],"sourcesContent":["import axios from 'axios'\nimport { ref, onMounted, onUnmounted, watch, getCurrentInstance, type Ref } from 'vue'\nimport { useAuthStore } from '../stores/auth'\nimport { useSettingsStore } from '../stores/settings'\nimport type { AuthConfig, UserInfo, LoginResponse, TokenVerifyResponse, UpdateProfileRequest } from '../types'\n\ninterface UserResponse {\n id: string\n username: string\n shortname: string | null\n email: string | null\n role: string\n is_active: boolean\n}\n\ninterface RefreshResponse {\n access_token: string\n expires_in: number\n token_type: string\n}\n\n// Token refresh configuration\nconst TOKEN_REFRESH_MARGIN_MS = 5 * 60 * 1000 // Refresh 5 minutes before expiry\nconst TOKEN_REFRESH_CHECK_INTERVAL_MS = 60 * 1000 // Check every minute\n\n/**\n * Authentication composable with automatic token refresh.\n *\n * Features:\n * - Automatic token refresh before expiration\n * - Login/logout/register functionality\n * - Token verification on startup\n * - User profile management\n *\n * @example\n * ```typescript\n * const { login, logout, isAuthenticated, user } = useAuth()\n *\n * // Login\n * const success = await login('username', 'password')\n *\n * // Automatic refresh is enabled by default\n * // Tokens are refreshed 5 minutes before expiration\n * ```\n */\nexport interface UseAuthReturn {\n login: (username: string, password: string) => Promise<boolean>\n logout: () => void\n register: (username: string, password: string, email?: string) => Promise<boolean>\n verifyToken: () => Promise<boolean>\n fetchAuthConfig: () => Promise<AuthConfig>\n initializeAuth: () => Promise<void>\n getCurrentUser: () => Promise<UserInfo | null>\n getAuthHeader: () => Record<string, string>\n updateProfile: (data: { email?: string; shortname?: string; currentPassword?: string; newPassword?: string }) => Promise<{ success: boolean; error?: string }>\n refreshToken: () => Promise<boolean>\n isRefreshing: Ref<boolean>\n}\n\n// Module-level singletons to prevent duplicate refresh requests and timers\n// across multiple useAuth() instances\nlet _refreshPromise: Promise<boolean> | null = null\nlet _refreshTimerId: number | null = null\nlet _mountedConsumerCount = 0\n\n/** @internal Reset module-level state — only for use in tests. */\nexport function _resetAuthStateForTesting(): void {\n _refreshPromise = null\n if (_refreshTimerId !== null) {\n clearTimeout(_refreshTimerId)\n _refreshTimerId = null\n }\n _mountedConsumerCount = 0\n}\n\n/** Manages authentication state with login/logout/register and automatic JWT token refresh. */\nexport function useAuth(): UseAuthReturn {\n const authStore = useAuthStore()\n const settingsStore = useSettingsStore()\n\n const isRefreshing = ref(false)\n\n function getApiBaseUrl(): string {\n return settingsStore.getApiBaseUrl()\n }\n\n async function fetchAuthConfig(): Promise<AuthConfig> {\n try {\n const response = await axios.get<{\n auth_required: boolean\n passkey_enabled: boolean\n passkey_registered?: boolean\n registration_enabled?: boolean\n database_mode?: string\n }>(`${getApiBaseUrl()}/setup/config/public`)\n\n const config: AuthConfig = {\n authRequired: response.data.auth_required,\n passkeyEnabled: response.data.passkey_enabled,\n passkeyRegistered: response.data.passkey_registered ?? false,\n registrationEnabled: response.data.registration_enabled ?? false,\n databaseMode: response.data.database_mode ?? 'none',\n }\n\n authStore.setAuthConfig(config)\n return config\n } catch (error) {\n console.error('Failed to fetch auth config:', error)\n return {\n authRequired: false,\n passkeyEnabled: false,\n passkeyRegistered: false,\n registrationEnabled: false,\n databaseMode: 'none',\n }\n }\n }\n\n async function login(username: string, password: string): Promise<boolean> {\n authStore.setLoading(true)\n authStore.setError(null)\n\n try {\n const response = await axios.post<LoginResponse>(\n `${getApiBaseUrl()}/auth/login`,\n { username, password }\n )\n\n authStore.setToken(response.data.access_token, response.data.expires_in)\n authStore.setUsername(username)\n\n await getCurrentUser()\n\n // Start auto-refresh after successful login\n scheduleTokenRefresh()\n\n return true\n } catch (error) {\n if (axios.isAxiosError(error) && error.response) {\n authStore.setError(error.response.data.detail || 'Login failed')\n } else {\n authStore.setError('Network error. Please try again.')\n }\n return false\n } finally {\n authStore.setLoading(false)\n }\n }\n\n async function register(username: string, password: string, email?: string): Promise<boolean> {\n authStore.setLoading(true)\n authStore.setError(null)\n\n try {\n await axios.post<UserResponse>(\n `${getApiBaseUrl()}/users/register`,\n { username, password, email }\n )\n\n return await login(username, password)\n } catch (error) {\n if (axios.isAxiosError(error) && error.response) {\n authStore.setError(error.response.data.detail || 'Registration failed')\n } else {\n authStore.setError('Network error. Please try again.')\n }\n return false\n } finally {\n authStore.setLoading(false)\n }\n }\n\n async function getCurrentUser(): Promise<UserInfo | null> {\n if (!authStore.token) {\n return null\n }\n\n try {\n const response = await axios.get<UserResponse>(\n `${getApiBaseUrl()}/users/me`,\n { headers: getAuthHeader() }\n )\n\n const userInfo: UserInfo = {\n id: response.data.id,\n username: response.data.username,\n shortname: response.data.shortname,\n email: response.data.email,\n role: response.data.role,\n isActive: response.data.is_active,\n }\n\n authStore.setUserInfo(userInfo)\n return userInfo\n } catch {\n return null\n }\n }\n\n async function verifyToken(): Promise<boolean> {\n if (!authStore.token) {\n return false\n }\n\n try {\n const response = await axios.get<TokenVerifyResponse>(\n `${getApiBaseUrl()}/auth/verify`,\n {\n headers: {\n Authorization: `Bearer ${authStore.token}`,\n },\n }\n )\n\n if (response.data.valid && response.data.username) {\n authStore.setUsername(response.data.username)\n return true\n }\n\n authStore.clearToken()\n return false\n } catch {\n authStore.clearToken()\n return false\n }\n }\n\n /**\n * Refresh the authentication token.\n * Called automatically before token expiration.\n * Uses promise caching to prevent concurrent refresh requests.\n */\n async function refreshToken(): Promise<boolean> {\n if (!authStore.token) return false\n if (_refreshPromise) return _refreshPromise\n\n _refreshPromise = (async () => {\n isRefreshing.value = true\n\n try {\n const response = await axios.post<RefreshResponse>(\n `${getApiBaseUrl()}/auth/refresh`,\n {},\n { headers: getAuthHeader() }\n )\n\n authStore.setToken(response.data.access_token, response.data.expires_in)\n\n // Reschedule next refresh\n scheduleTokenRefresh()\n\n return true\n } catch (error) {\n // If refresh fails, the token may have been revoked\n // Clear auth state and let user re-login\n if (axios.isAxiosError(error) && error.response?.status === 401) {\n console.warn('[Auth] Token refresh failed - session expired')\n authStore.clearToken()\n stopTokenRefresh()\n }\n return false\n } finally {\n isRefreshing.value = false\n _refreshPromise = null\n }\n })()\n\n return _refreshPromise\n }\n\n /**\n * Schedule automatic token refresh before expiration.\n */\n function scheduleTokenRefresh(): void {\n // Clear any existing timer\n stopTokenRefresh()\n\n if (!authStore.tokenExpires) {\n return\n }\n\n const expiresAt = authStore.tokenExpires.getTime()\n const refreshAt = expiresAt - TOKEN_REFRESH_MARGIN_MS\n const now = Date.now()\n\n if (refreshAt <= now) {\n // Token is already close to expiring or expired, refresh now\n refreshToken()\n return\n }\n\n // Schedule refresh\n const delay = refreshAt - now\n _refreshTimerId = window.setTimeout(() => {\n refreshToken()\n }, delay)\n }\n\n /**\n * Stop automatic token refresh.\n */\n function stopTokenRefresh(): void {\n if (_refreshTimerId !== null) {\n window.clearTimeout(_refreshTimerId)\n _refreshTimerId = null\n }\n }\n\n /**\n * Check if token needs refresh and refresh if necessary.\n * Called periodically as a safety net.\n */\n function checkAndRefreshIfNeeded(): void {\n if (!authStore.token || !authStore.tokenExpires) {\n return\n }\n\n const expiresAt = authStore.tokenExpires.getTime()\n const refreshAt = expiresAt - TOKEN_REFRESH_MARGIN_MS\n const now = Date.now()\n\n if (now >= refreshAt) {\n refreshToken()\n }\n }\n\n async function initializeAuth(): Promise<void> {\n authStore.initialize()\n await fetchAuthConfig()\n\n if (authStore.token) {\n const valid = await verifyToken()\n if (valid) {\n await getCurrentUser()\n // Start auto-refresh for existing session\n scheduleTokenRefresh()\n }\n }\n }\n\n function logout(): void {\n stopTokenRefresh()\n authStore.logout()\n }\n\n function getAuthHeader(): Record<string, string> {\n if (authStore.token) {\n return { Authorization: `Bearer ${authStore.token}` }\n }\n return {}\n }\n\n async function updateProfile(data: {\n email?: string\n shortname?: string\n currentPassword?: string\n newPassword?: string\n }): Promise<{ success: boolean; error?: string }> {\n if (!authStore.token) {\n return { success: false, error: 'Not authenticated' }\n }\n\n try {\n const requestData: UpdateProfileRequest = {}\n if (data.email !== undefined) requestData.email = data.email\n if (data.shortname !== undefined) requestData.shortname = data.shortname\n if (data.currentPassword) requestData.current_password = data.currentPassword\n if (data.newPassword) requestData.new_password = data.newPassword\n\n const response = await axios.put<UserResponse>(\n `${getApiBaseUrl()}/users/me`,\n requestData,\n { headers: getAuthHeader() }\n )\n\n const userInfo: UserInfo = {\n id: response.data.id,\n username: response.data.username,\n shortname: response.data.shortname,\n email: response.data.email,\n role: response.data.role,\n isActive: response.data.is_active,\n }\n authStore.setUserInfo(userInfo)\n\n return { success: true }\n } catch (error) {\n if (axios.isAxiosError(error) && error.response) {\n return { success: false, error: error.response.data.detail || 'Update failed' }\n }\n return { success: false, error: 'Network error. Please try again.' }\n }\n }\n\n // Set up periodic check as safety net (only inside component setup)\n let checkInterval: number | null = null\n\n if (getCurrentInstance()) {\n onMounted(() => {\n _mountedConsumerCount += 1\n checkInterval = window.setInterval(checkAndRefreshIfNeeded, TOKEN_REFRESH_CHECK_INTERVAL_MS)\n })\n\n onUnmounted(() => {\n if (checkInterval !== null) {\n window.clearInterval(checkInterval)\n checkInterval = null\n }\n\n _mountedConsumerCount = Math.max(0, _mountedConsumerCount - 1)\n if (_mountedConsumerCount === 0) {\n stopTokenRefresh()\n }\n })\n\n // Watch for token changes to reschedule refresh\n watch(\n () => authStore.tokenExpires,\n (newExpires) => {\n if (newExpires) {\n scheduleTokenRefresh()\n } else {\n stopTokenRefresh()\n }\n }\n )\n }\n\n return {\n // Core auth methods\n login,\n logout,\n register,\n verifyToken,\n fetchAuthConfig,\n initializeAuth,\n getCurrentUser,\n getAuthHeader,\n updateProfile,\n\n // Token refresh\n refreshToken,\n isRefreshing,\n }\n}\n","import axios from 'axios'\nimport { useAuthStore } from '../stores/auth'\nimport { useSettingsStore } from '../stores/settings'\nimport type { CredentialInfo } from '../types'\n\n// Lazy-load @simplewebauthn/browser so plugins that never call usePasskey\n// don't need the optional peer dep installed (avoids build failures).\nasync function loadWebAuthn() {\n try {\n return await import('@simplewebauthn/browser')\n } catch {\n throw new Error(\n '@simplewebauthn/browser is required for passkey support. Install it: bun add @simplewebauthn/browser',\n )\n }\n}\n\ninterface PasskeyLoginResponse {\n access_token: string\n token_type: string\n expires_in: number\n}\n\n/** Registers and authenticates passkeys using WebAuthn, lazily loading the browser dependency. */\nexport function usePasskey() {\n const authStore = useAuthStore()\n const settingsStore = useSettingsStore()\n\n function getApiBaseUrl(): string {\n return settingsStore.getApiBaseUrl()\n }\n\n async function isSupported(): Promise<boolean> {\n try {\n const { browserSupportsWebAuthn } = await loadWebAuthn()\n return browserSupportsWebAuthn()\n } catch {\n return false\n }\n }\n\n async function registerPasskey(deviceName?: string): Promise<boolean> {\n const webauthn = await loadWebAuthn()\n if (!webauthn.browserSupportsWebAuthn()) {\n authStore.setError('WebAuthn is not supported in this browser')\n return false\n }\n\n authStore.setLoading(true)\n authStore.setError(null)\n\n try {\n const optionsResponse = await axios.get<{ options: string }>(\n `${getApiBaseUrl()}/auth/passkey/register/options`,\n {\n headers: { Authorization: `Bearer ${authStore.token}` },\n withCredentials: true,\n }\n )\n\n const options = JSON.parse(optionsResponse.data.options)\n\n const credential = await webauthn.startRegistration(options)\n\n await axios.post(\n `${getApiBaseUrl()}/auth/passkey/register/verify`,\n {\n credential: JSON.stringify(credential),\n device_name: deviceName,\n },\n {\n headers: { Authorization: `Bearer ${authStore.token}` },\n withCredentials: true,\n }\n )\n\n authStore.setAuthConfig({\n ...authStore.authConfig,\n passkeyRegistered: true,\n })\n\n return true\n } catch (error) {\n if (axios.isAxiosError(error) && error.response) {\n authStore.setError(error.response.data.detail || 'Passkey registration failed')\n } else if (error instanceof Error) {\n if (error.name === 'NotAllowedError') {\n authStore.setError('Registration was cancelled or timed out')\n } else if (error.name === 'InvalidStateError') {\n authStore.setError('This authenticator is already registered')\n } else {\n authStore.setError(error.message)\n }\n } else {\n authStore.setError('Passkey registration failed')\n }\n return false\n } finally {\n authStore.setLoading(false)\n }\n }\n\n async function loginWithPasskey(): Promise<boolean> {\n const webauthn = await loadWebAuthn()\n if (!webauthn.browserSupportsWebAuthn()) {\n authStore.setError('WebAuthn is not supported in this browser')\n return false\n }\n\n authStore.setLoading(true)\n authStore.setError(null)\n\n try {\n const optionsResponse = await axios.get<{ options: string }>(\n `${getApiBaseUrl()}/auth/passkey/login/options`,\n { withCredentials: true }\n )\n\n const options = JSON.parse(optionsResponse.data.options)\n\n const credential = await webauthn.startAuthentication(options)\n\n const response = await axios.post<PasskeyLoginResponse>(\n `${getApiBaseUrl()}/auth/passkey/login/verify`,\n { credential: JSON.stringify(credential) },\n { withCredentials: true }\n )\n\n authStore.setToken(response.data.access_token, response.data.expires_in)\n\n return true\n } catch (error) {\n if (axios.isAxiosError(error) && error.response) {\n if (error.response.status === 404) {\n authStore.setError('No passkeys registered. Please login with password first.')\n } else {\n authStore.setError(error.response.data.detail || 'Passkey login failed')\n }\n } else if (error instanceof Error) {\n if (error.name === 'NotAllowedError') {\n authStore.setError('Authentication was cancelled or timed out')\n } else {\n authStore.setError(error.message)\n }\n } else {\n authStore.setError('Passkey login failed')\n }\n return false\n } finally {\n authStore.setLoading(false)\n }\n }\n\n async function listCredentials(): Promise<CredentialInfo[]> {\n try {\n const response = await axios.get<{ credentials: CredentialInfo[] }>(\n `${getApiBaseUrl()}/auth/passkey/credentials`,\n {\n headers: { Authorization: `Bearer ${authStore.token}` },\n }\n )\n return response.data.credentials\n } catch {\n return []\n }\n }\n\n async function deleteCredential(credentialId: string): Promise<boolean> {\n try {\n await axios.delete(\n `${getApiBaseUrl()}/auth/passkey/credentials/${encodeURIComponent(credentialId)}`,\n {\n headers: { Authorization: `Bearer ${authStore.token}` },\n }\n )\n\n const remaining = await listCredentials()\n if (remaining.length === 0) {\n authStore.setAuthConfig({\n ...authStore.authConfig,\n passkeyRegistered: false,\n })\n }\n\n return true\n } catch {\n return false\n }\n }\n\n async function deleteAllCredentials(): Promise<boolean> {\n try {\n await axios.delete(`${getApiBaseUrl()}/auth/passkey/credentials`, {\n headers: { Authorization: `Bearer ${authStore.token}` },\n })\n\n authStore.setAuthConfig({\n ...authStore.authConfig,\n passkeyRegistered: false,\n })\n\n return true\n } catch {\n return false\n }\n }\n\n return {\n isSupported,\n registerPasskey,\n loginWithPasskey,\n listCredentials,\n deleteCredential,\n deleteAllCredentials,\n }\n}\n","import { ref, computed, type Ref, type ComputedRef } from 'vue'\n\n/**\n * Error type for async operations.\n */\nexport interface AsyncError {\n message: string\n code?: string\n details?: Record<string, unknown>\n originalError?: unknown\n}\n\n/**\n * State of an async operation.\n */\nexport type AsyncState = 'idle' | 'loading' | 'success' | 'error'\n\n/**\n * Return type for useAsync composable.\n */\nexport interface UseAsyncReturn<T> {\n // Data\n data: Ref<T | null>\n error: Ref<AsyncError | null>\n\n // State\n state: Ref<AsyncState>\n isIdle: ComputedRef<boolean>\n isLoading: ComputedRef<boolean>\n isSuccess: ComputedRef<boolean>\n isError: ComputedRef<boolean>\n\n // Methods\n execute: (...args: unknown[]) => Promise<T | null>\n reset: () => void\n setData: (data: T | null) => void\n setError: (error: AsyncError | null) => void\n}\n\n/**\n * Options for useAsync.\n */\nexport interface UseAsyncOptions<T> {\n // Initial data value\n initialData?: T | null\n\n // Whether to run immediately on creation\n immediate?: boolean\n\n // Arguments for immediate execution\n immediateArgs?: unknown[]\n\n // Transform error into AsyncError\n transformError?: (error: unknown) => AsyncError\n\n // Called on success\n onSuccess?: (data: T) => void\n\n // Called on error\n onError?: (error: AsyncError) => void\n\n // Reset data on new execution\n resetOnExecute?: boolean\n}\n\n/**\n * Default error transformer.\n */\nfunction defaultTransformError(error: unknown): AsyncError {\n if (error instanceof Error) {\n return {\n message: error.message,\n originalError: error,\n }\n }\n\n if (typeof error === 'object' && error !== null) {\n const errorObj = error as Record<string, unknown>\n\n // Handle Axios-like errors\n if ('response' in errorObj && errorObj.response) {\n const response = errorObj.response as Record<string, unknown>\n const data = response.data as Record<string, unknown> | undefined\n\n return {\n message: (data?.detail as string) || (data?.message as string) || 'Request failed',\n code: String(response.status),\n details: data,\n originalError: error,\n }\n }\n\n // Handle generic error objects\n return {\n message: (errorObj.message as string) || 'Unknown error',\n code: errorObj.code as string | undefined,\n details: errorObj,\n originalError: error,\n }\n }\n\n return {\n message: String(error),\n originalError: error,\n }\n}\n\n/**\n * Composable for managing async operation state.\n *\n * Provides standardized loading, error, and success state management\n * for any async function.\n *\n * @param asyncFn - The async function to wrap\n * @param options - Configuration options\n *\n * @example\n * ```typescript\n * // Basic usage\n * const { data, isLoading, error, execute } = useAsync(\n * async (id: string) => {\n * const response = await api.get(`/users/${id}`)\n * return response.data\n * }\n * )\n *\n * // Execute the function\n * await execute('user-123')\n *\n * // In template\n * <div v-if=\"isLoading\">Loading...</div>\n * <div v-else-if=\"error\">{{ error.message }}</div>\n * <div v-else-if=\"data\">{{ data.name }}</div>\n * ```\n *\n * @example\n * ```typescript\n * // With options\n * const { data, execute } = useAsync(\n * fetchUser,\n * {\n * immediate: true,\n * immediateArgs: ['default-user'],\n * onSuccess: (user) => console.log('Fetched:', user.name),\n * onError: (error) => toast.error(error.message),\n * }\n * )\n * ```\n *\n * @example\n * ```typescript\n * // Form submission\n * const { isLoading, error, execute: submit } = useAsync(\n * async (formData: FormData) => {\n * await api.post('/submit', formData)\n * },\n * {\n * onSuccess: () => {\n * toast.success('Submitted!')\n * router.push('/success')\n * },\n * }\n * )\n *\n * const handleSubmit = () => submit(new FormData(formRef.value))\n * ```\n */\n/** Wraps an async function with reactive loading, error, and success state plus optional auto-execute. */\nexport function useAsync<T>(\n asyncFn: (...args: unknown[]) => Promise<T>,\n options: UseAsyncOptions<T> = {}\n): UseAsyncReturn<T> {\n const {\n initialData = null,\n immediate = false,\n immediateArgs = [],\n transformError = defaultTransformError,\n onSuccess,\n onError,\n resetOnExecute = false,\n } = options\n\n // State\n const data = ref<T | null>(initialData) as Ref<T | null>\n const error = ref<AsyncError | null>(null)\n const state = ref<AsyncState>('idle')\n\n // Computed state helpers\n const isIdle = computed(() => state.value === 'idle')\n const isLoading = computed(() => state.value === 'loading')\n const isSuccess = computed(() => state.value === 'success')\n const isError = computed(() => state.value === 'error')\n\n // Execute the async function\n async function execute(...args: unknown[]): Promise<T | null> {\n state.value = 'loading'\n error.value = null\n\n if (resetOnExecute) {\n data.value = null\n }\n\n try {\n const result = await asyncFn(...args)\n data.value = result\n state.value = 'success'\n onSuccess?.(result)\n return result\n } catch (e) {\n const asyncError = transformError(e)\n error.value = asyncError\n state.value = 'error'\n onError?.(asyncError)\n return null\n }\n }\n\n // Reset to initial state\n function reset(): void {\n data.value = initialData\n error.value = null\n state.value = 'idle'\n }\n\n // Manual data setter\n function setData(newData: T | null): void {\n data.value = newData\n if (newData !== null) {\n state.value = 'success'\n error.value = null\n }\n }\n\n // Manual error setter\n function setError(newError: AsyncError | null): void {\n error.value = newError\n if (newError !== null) {\n state.value = 'error'\n }\n }\n\n // Execute immediately if requested\n if (immediate) {\n execute(...immediateArgs)\n }\n\n return {\n data,\n error,\n state,\n isIdle,\n isLoading,\n isSuccess,\n isError,\n execute,\n reset,\n setData,\n setError,\n }\n}\n\n/**\n * Create a batch of async operations that can be executed in parallel.\n *\n * @example\n * ```typescript\n * const { results, isLoading, execute } = useAsyncBatch([\n * () => fetchUser(userId),\n * () => fetchPosts(userId),\n * () => fetchComments(userId),\n * ])\n *\n * await execute()\n * // results.value = [user, posts, comments]\n * ```\n */\nexport function useAsyncBatch<T extends readonly (() => Promise<unknown>)[]>(\n asyncFns: T\n): {\n results: Ref<{ [K in keyof T]: Awaited<ReturnType<T[K]>> | null }>\n errors: Ref<(AsyncError | null)[]>\n isLoading: Ref<boolean>\n execute: () => Promise<void>\n reset: () => void\n} {\n type Results = { [K in keyof T]: Awaited<ReturnType<T[K]>> | null }\n\n const results = ref<Results>(asyncFns.map(() => null) as unknown as Results) as Ref<Results>\n const errors = ref<(AsyncError | null)[]>(asyncFns.map(() => null))\n const isLoading = ref(false)\n\n async function execute(): Promise<void> {\n isLoading.value = true\n errors.value = asyncFns.map(() => null)\n\n const promises = asyncFns.map(async (fn, index) => {\n try {\n const result = await fn()\n ;(results.value as unknown[])[index] = result\n } catch (e) {\n errors.value[index] = defaultTransformError(e)\n ;(results.value as unknown[])[index] = null\n }\n })\n\n await Promise.all(promises)\n isLoading.value = false\n }\n\n function reset(): void {\n results.value = asyncFns.map(() => null) as unknown as Results\n errors.value = asyncFns.map(() => null)\n isLoading.value = false\n }\n\n return {\n results,\n errors,\n isLoading,\n execute,\n reset,\n }\n}\n","import { ref, computed, onMounted, type Ref, type ComputedRef } from 'vue'\nimport { useApi } from './useApi'\nimport { usePlatformContext } from './usePlatformContext'\nimport { useRequestSyncState } from './useRequestSyncState'\n\nexport interface UsePluginConfigReturn {\n /** Editable plugin configuration values. */\n config: Ref<Record<string, unknown>>\n /** Whether the config is currently loading. */\n isLoading: Ref<boolean>\n /** Whether the config is currently saving. */\n isSaving: Ref<boolean>\n /** Error message from the last failed operation, or null. */\n error: Ref<string | null>\n /** Timestamp of the last successful load, or null. */\n lastLoadedAt: Ref<Date | null>\n /** Timestamp of the last successful save, or null. */\n lastSavedAt: Ref<Date | null>\n /** Whether local config differs from the last loaded or saved snapshot. */\n isDirty: ComputedRef<boolean>\n /** Load plugin configuration from the platform. */\n load: () => Promise<void>\n /** Save plugin configuration to the platform. */\n save: () => Promise<boolean>\n /** Restore the last loaded or saved configuration snapshot. */\n reset: () => void\n}\n\n/** Loads, saves, and tracks dirty state for a plugin's persistent configuration via the platform API. */\nexport function usePluginConfig(pluginName?: string): UsePluginConfigReturn {\n const api = useApi()\n const { plugin } = usePlatformContext()\n\n const resolvedName = computed(() => pluginName ?? plugin.value?.name ?? '')\n\n const config = ref<Record<string, unknown>>({})\n const savedConfig = ref<Record<string, unknown>>({})\n const request = useRequestSyncState('Plugin config request failed.')\n const isLoading = ref(false)\n const isSaving = ref(false)\n const error = request.error\n const lastLoadedAt = request.lastLoadedAt\n const lastSavedAt = request.lastSavedAt\n\n const isDirty = computed(() => {\n return JSON.stringify(config.value) !== JSON.stringify(savedConfig.value)\n })\n\n async function load(): Promise<void> {\n const name = resolvedName.value\n if (!name) return\n\n isLoading.value = true\n try {\n const response = await request.run(\n () => api.get<{ plugin_name: string; config: Record<string, unknown> }>(\n `/plugins/${encodeURIComponent(name)}/config`,\n ),\n { success: 'load', errorMessage: 'Failed to load plugin config' },\n )\n config.value = { ...response.config }\n savedConfig.value = { ...response.config }\n } catch {\n // Error state is handled by request.run().\n } finally {\n isLoading.value = false\n }\n }\n\n async function save(): Promise<boolean> {\n const name = resolvedName.value\n if (!name) return false\n\n isSaving.value = true\n try {\n const response = await request.run(\n () => api.patch<{ plugin_name: string; config: Record<string, unknown> }>(\n `/plugins/${encodeURIComponent(name)}/config`,\n { config: config.value },\n ),\n { success: 'save', errorMessage: 'Failed to save plugin config' },\n )\n config.value = { ...response.config }\n savedConfig.value = { ...response.config }\n return true\n } catch {\n return false\n } finally {\n isSaving.value = false\n }\n }\n\n function reset(): void {\n config.value = { ...savedConfig.value }\n request.clearError()\n }\n\n onMounted(() => {\n load()\n })\n\n return {\n config,\n isLoading,\n isSaving,\n error,\n lastLoadedAt,\n lastSavedAt,\n isDirty,\n load,\n save,\n reset,\n }\n}\n","import { computed, shallowRef, watch, type ComputedRef, type Ref } from 'vue'\nimport { useApi } from './useApi'\nimport { usePlatformContext } from './usePlatformContext'\nimport { useRequestSyncState } from './useRequestSyncState'\nimport {\n currentExperimentFromContext,\n getInjectedPlatformContext,\n resolveCurrentExperimentId,\n} from './platformContextHelpers'\nimport type { ExperimentSummary, PageSelectorItem } from '../types'\n\nexport type PluginHttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete'\n\nexport interface PluginEndpointContract {\n name: string\n method: PluginHttpMethod\n path: string\n pathParams?: string[]\n queryParams?: PluginEndpointParamContract[]\n requestType?: string | null\n responseType?: string | null\n}\n\nexport interface PluginEndpointParamContract {\n name: string\n fieldName: string\n type?: string\n required?: boolean\n}\n\nexport interface PluginNavItemContract {\n path: string\n label: string\n id?: string\n icon?: string\n description?: string\n requiresAuth?: boolean\n requiresAdmin?: boolean\n requiresFeature?: string\n}\n\nexport interface PluginContract {\n schemaVersion: number\n plugin: {\n name?: string\n packageName?: string\n version?: string\n description?: string\n routesPrefix: string\n apiPrefix: string\n type: 'analysis' | 'experiment-design' | string\n analysisType?: string\n icon?: string\n color?: string\n navItems?: PluginNavItemContract[]\n capabilities?: Record<string, unknown>\n }\n endpoints: PluginEndpointContract[]\n hash: string\n}\n\nexport interface PluginEndpointDefinition {\n method: PluginHttpMethod\n path: string\n pathParams?: string[]\n queryParams?: PluginEndpointParamDefinition[]\n hasBody?: boolean\n}\n\nexport type PluginEndpointParamDefinition = string | {\n name: string\n fieldName?: string\n type?: string\n required?: boolean\n}\n\nexport interface CreatePluginClientOptions {\n endpoints: Record<string, PluginEndpointDefinition>\n baseUrl?: string\n}\n\nexport interface BuildPluginEndpointUrlOptions {\n /** Override the generated or platform-injected API base URL. */\n baseUrl?: string\n /** Return only the endpoint path and query string instead of base URL + path. */\n includeBaseUrl?: boolean\n}\n\nexport interface UseCurrentExperimentOptions {\n apiBaseUrl?: string\n immediate?: boolean\n}\n\nexport interface UseCurrentExperimentReturn<TExperiment = ExperimentSummary> {\n /** Current experiment id resolved from platform injection or URL conventions. */\n experimentId: ComputedRef<number | undefined>\n /** Whether a current experiment id is available. */\n hasExperiment: ComputedRef<boolean>\n /** Current experiment payload, if injected or successfully fetched. */\n experiment: Ref<TExperiment | undefined>\n /** Whether the current experiment is currently loading. */\n isLoading: Ref<boolean>\n /** Error message from the last failed current-experiment fetch, or null. */\n error: Ref<string | null>\n /** Timestamp of the last successful current-experiment fetch, or null. */\n lastLoadedAt: Ref<Date | null>\n /** Return the current experiment id or throw a clear SDK error when absent. */\n requireExperimentId: () => number\n /** Fetch a specific experiment id, defaulting to the current experiment id. */\n fetch: (experimentId?: number) => Promise<TExperiment | undefined>\n /** Refetch the currently resolved experiment id. */\n refresh: () => Promise<TExperiment | undefined>\n}\n\nexport type PluginEndpointCaller = (payload?: unknown) => Promise<unknown>\nexport type GeneratedPluginClient = Record<string, (...args: any[]) => Promise<any>>\n\nfunction normalizeApiBaseUrl(baseUrl: string | undefined): string | undefined {\n if (!baseUrl) return undefined\n return baseUrl.length > 1 ? baseUrl.replace(/\\/+$/, '') : baseUrl\n}\n\nfunction normalizeApiPrefix(prefix: string | undefined): string | undefined {\n if (!prefix) return undefined\n if (prefix.startsWith('/api/')) return normalizeApiBaseUrl(prefix)\n if (prefix === '/api') return prefix\n if (prefix.startsWith('/')) return normalizeApiBaseUrl(`/api${prefix}`)\n return normalizeApiBaseUrl(`/api/${prefix}`)\n}\n\nfunction normalizePluginNavPath(path: string): string {\n const raw = path.trim() || '/'\n const prefixed = raw.startsWith('/') ? raw : `/${raw}`\n return prefixed.replace(/\\/+$/, '') || '/'\n}\n\nfunction pluginPageIdFromPath(path: string, fallbackIndex: number): string {\n const normalizedPath = normalizePluginNavPath(path)\n if (normalizedPath === '/') return 'dashboard'\n return normalizedPath.replace(/^\\/+/, '').replace(/\\/+/g, '-') || `page-${fallbackIndex + 1}`\n}\n\n/** Convert PluginMetadata.nav_items into AppTopBar pageSelector items for topbar page switches, homepage PluginCards, and fallback views. */\nexport function getPluginPageSelectorItems(contract: PluginContract): PageSelectorItem[] {\n const pluginIcon = contract.plugin.icon ?? ''\n const navItems = contract.plugin.navItems ?? []\n const source: PluginNavItemContract[] = navItems.length > 0\n ? navItems\n : [{\n path: '/',\n label: contract.plugin.name ?? 'Dashboard',\n id: 'dashboard',\n icon: pluginIcon || undefined,\n description: contract.plugin.description,\n }]\n\n return source.map((item, index) => ({\n id: item.id || pluginPageIdFromPath(item.path, index),\n label: item.label,\n to: normalizePluginNavPath(item.path),\n icon: item.icon || pluginIcon || undefined,\n hint: item.description || contract.plugin.name,\n }))\n}\n\n/** Resolve the runtime plugin API base URL from env, explicit options, platform injection, or contract metadata. */\nexport function resolvePluginBaseUrl(contract: PluginContract, explicitBaseUrl?: string): string {\n const envPrefix = (import.meta.env?.VITE_API_PREFIX as string | undefined) || undefined\n if (envPrefix) return normalizeApiBaseUrl(envPrefix) ?? envPrefix\n if (explicitBaseUrl) return normalizeApiBaseUrl(explicitBaseUrl) ?? explicitBaseUrl\n\n const injected = getInjectedPlatformContext()\n const platformPrefix =\n normalizeApiBaseUrl(injected?.plugin?.api_prefix) ||\n normalizeApiPrefix(injected?.plugin?.route_prefix)\n if (platformPrefix) return platformPrefix\n\n return normalizeApiBaseUrl(contract.plugin.apiPrefix) || '/api'\n}\n\nfunction encodePath(path: string, payload: Record<string, unknown>, pathParams: string[]): string {\n let nextPath = path\n for (const param of pathParams) {\n const value = payload[param]\n if (value === undefined || value === null) {\n throw new Error(`[MINT SDK] Missing path parameter '${param}' for plugin endpoint ${path}`)\n }\n nextPath = nextPath.replace(`{${param}}`, encodeURIComponent(String(value)))\n nextPath = nextPath.replace(`:${param}`, encodeURIComponent(String(value)))\n }\n return nextPath\n}\n\nfunction withDefaultPathParams(\n payload: Record<string, unknown>,\n pathParams: string[],\n): Record<string, unknown> {\n if (!pathParams.length) return payload\n const nextPayload = { ...payload }\n for (const param of pathParams) {\n if (nextPayload[param] !== undefined && nextPayload[param] !== null) continue\n if (isExperimentPathParam(param)) {\n const experimentId = resolveCurrentExperimentId()\n if (experimentId !== undefined) {\n nextPayload[param] = experimentId\n }\n }\n }\n return nextPayload\n}\n\nfunction isExperimentPathParam(param: string): boolean {\n return param === 'experimentId' || param === 'experiment_id'\n}\n\nfunction withoutPathParams(payload: Record<string, unknown>, pathParams: string[]) {\n const copy = { ...payload }\n for (const param of pathParams) {\n delete copy[param]\n }\n return copy\n}\n\nfunction queryParamsFromPayload(\n payload: Record<string, unknown>,\n pathParams: string[],\n queryParams?: PluginEndpointParamDefinition[],\n endpointPath?: string,\n includeUnspecifiedParams = false,\n) {\n if (!queryParams?.length) {\n return includeUnspecifiedParams ? withoutPathParams(payload, pathParams) : {}\n }\n\n const query: Record<string, unknown> = {}\n for (const param of queryParams) {\n const name = typeof param === 'string' ? param : param.name\n const fieldName = typeof param === 'string' ? param : param.fieldName ?? param.name\n const required = typeof param !== 'string' && param.required === true\n const value = payload[fieldName]\n if (value === undefined || value === null) {\n if (required) {\n throw new Error(\n `[MINT SDK] Missing query parameter '${fieldName}' for plugin endpoint ${endpointPath ?? ''}`,\n )\n }\n continue\n }\n query[name] = value\n }\n return query\n}\n\nfunction hasKeys(value: Record<string, unknown>): boolean {\n return Object.keys(value).length > 0\n}\n\nfunction requestBodyFromPayload(\n payload: unknown,\n requestPayload: Record<string, unknown>,\n endpoint: PluginEndpointDefinition,\n): unknown {\n if (!endpoint.hasBody) return undefined\n const body = endpoint.pathParams?.length || endpoint.queryParams?.length\n ? requestPayload.body\n : payload\n if (body === undefined || body === null) {\n throw new Error(`[MINT SDK] Missing request body for plugin endpoint ${endpoint.path}`)\n }\n return body\n}\n\nfunction pluginEndpointRequestParts(\n contract: PluginContract,\n endpoint: PluginEndpointDefinition,\n payload?: unknown,\n explicitBaseUrl?: string,\n) {\n const payloadObject =\n payload && typeof payload === 'object' && !Array.isArray(payload)\n ? payload as Record<string, unknown>\n : {}\n const pathParams = endpoint.pathParams ?? []\n const requestPayload = withDefaultPathParams(payloadObject, pathParams)\n const path = encodePath(endpoint.path, requestPayload, pathParams)\n const query = queryParamsFromPayload(\n requestPayload,\n pathParams,\n endpoint.queryParams,\n endpoint.path,\n endpoint.method === 'get',\n )\n\n return {\n baseUrl: resolvePluginBaseUrl(contract, explicitBaseUrl),\n path,\n query,\n requestPayload,\n }\n}\n\nfunction appendQueryValue(params: URLSearchParams, key: string, value: unknown): void {\n if (value === undefined || value === null) return\n if (Array.isArray(value)) {\n for (const item of value) {\n appendQueryValue(params, key, item)\n }\n return\n }\n if (typeof value === 'object') {\n params.append(key, JSON.stringify(value))\n return\n }\n params.append(key, String(value))\n}\n\nfunction queryString(query: Record<string, unknown>): string {\n const params = new URLSearchParams()\n for (const [key, value] of Object.entries(query)) {\n appendQueryValue(params, key, value)\n }\n const serialized = params.toString()\n return serialized ? `?${serialized}` : ''\n}\n\nfunction joinBaseUrl(baseUrl: string, path: string): string {\n if (!baseUrl) return path\n const normalizedBase = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl\n const normalizedPath = path.startsWith('/') ? path : `/${path}`\n return `${normalizedBase}${normalizedPath}`\n}\n\n/** Build the concrete URL for a generated plugin endpoint without making a request. */\nexport function buildPluginEndpointUrl(\n contract: PluginContract,\n endpoint: PluginEndpointDefinition,\n payload?: unknown,\n options: BuildPluginEndpointUrlOptions = {},\n): string {\n const parts = pluginEndpointRequestParts(contract, endpoint, payload, options.baseUrl)\n const pathWithQuery = `${parts.path}${queryString(parts.query)}`\n if (options.includeBaseUrl === false) return pathWithQuery\n return joinBaseUrl(parts.baseUrl, pathWithQuery)\n}\n\n/** Create a typed plugin API client from a generated MINT plugin contract. */\nexport function createPluginClient<TClient extends object = GeneratedPluginClient>(\n contract: PluginContract,\n options: CreatePluginClientOptions,\n): TClient {\n const client: Record<string, PluginEndpointCaller> = {}\n\n for (const [name, endpoint] of Object.entries(options.endpoints)) {\n client[name] = async (payload?: unknown) => {\n const parts = pluginEndpointRequestParts(contract, endpoint, payload, options.baseUrl)\n const api = useApi({ baseUrl: parts.baseUrl })\n const queryConfig = hasKeys(parts.query) ? { params: parts.query } : undefined\n\n if (endpoint.method === 'get') {\n return api.get(parts.path, queryConfig)\n }\n\n if (endpoint.method === 'delete') {\n const body = requestBodyFromPayload(payload, parts.requestPayload, endpoint)\n const config = body === undefined\n ? queryConfig\n : { ...queryConfig, data: body }\n return api.delete(parts.path, config)\n }\n\n const body = requestBodyFromPayload(payload, parts.requestPayload, endpoint)\n\n if (endpoint.method === 'post') return api.post(parts.path, body, queryConfig)\n if (endpoint.method === 'put') return api.put(parts.path, body, queryConfig)\n if (endpoint.method === 'patch') return api.patch(parts.path, body, queryConfig)\n throw new Error(`[MINT SDK] Unsupported plugin endpoint method: ${endpoint.method}`)\n }\n }\n\n return client as TClient\n}\n\n/** Return a generated plugin client from setup code with a stable typed identity. */\nexport function usePluginClient<TClient>(client: TClient): TClient {\n return client\n}\n\n/** Read plugin settings exposed through the platform context. */\nexport function usePluginSettings<TSettings = Record<string, unknown>>() {\n const { plugin } = usePlatformContext()\n const settings = computed(() => plugin.value?.settings as TSettings | undefined)\n return { settings }\n}\n\n/** Read and optionally load the current platform experiment for integrated plugin views. */\nexport function useCurrentExperiment<TExperiment = ExperimentSummary>(\n options: UseCurrentExperimentOptions = {},\n): UseCurrentExperimentReturn<TExperiment> {\n const api = useApi({ baseUrl: options.apiBaseUrl ?? getInjectedPlatformContext()?.platformApiUrl })\n const experiment = shallowRef<TExperiment | undefined>(currentExperimentFromContext<TExperiment>())\n const request = useRequestSyncState('Failed to load current experiment')\n const isLoading = request.loading\n const error = request.error\n const lastLoadedAt = request.lastLoadedAt\n\n const experimentId = computed<number | undefined>(() => {\n return resolveCurrentExperimentId()\n })\n const hasExperiment = computed(() => experimentId.value !== undefined)\n\n function requireExperimentId(): number {\n const id = experimentId.value\n if (id === undefined) {\n throw new Error('[MINT SDK] No current experiment is selected.')\n }\n return id\n }\n\n async function fetchExperiment(id = experimentId.value): Promise<TExperiment | undefined> {\n if (id === undefined) {\n experiment.value = currentExperimentFromContext<TExperiment>()\n return experiment.value\n }\n\n try {\n const result = await request.run(\n () => api.get<TExperiment>(`/experiments/${id}`),\n { success: 'load', errorMessage: 'Failed to load current experiment' },\n )\n experiment.value = result\n return result\n } catch (e) {\n experiment.value = undefined\n return undefined\n }\n }\n\n async function refresh(): Promise<TExperiment | undefined> {\n return fetchExperiment()\n }\n\n watch(\n experimentId,\n (id) => {\n if (options.immediate === false) return\n if (id === undefined) {\n experiment.value = currentExperimentFromContext<TExperiment>()\n return\n }\n void fetchExperiment(id)\n },\n { immediate: options.immediate !== false },\n )\n\n return {\n experimentId,\n hasExperiment,\n experiment,\n isLoading,\n error,\n lastLoadedAt,\n requireExperimentId,\n fetch: fetchExperiment,\n refresh,\n }\n}\n"],"mappings":";;;;;AAsBA,IAAM,0BAA0B,MAAS;AACzC,IAAM,kCAAkC,KAAK;AAsC7C,IAAI,kBAA2C;AAC/C,IAAI,kBAAiC;AACrC,IAAI,wBAAwB;;AAa5B,SAAgB,UAAyB;CACvC,MAAM,YAAY,cAAc;CAChC,MAAM,gBAAgB,kBAAkB;CAExC,MAAM,eAAe,IAAI,MAAM;CAE/B,SAAS,gBAAwB;AAC/B,SAAO,cAAc,eAAe;;CAGtC,eAAe,kBAAuC;AACpD,MAAI;GACF,MAAM,WAAW,MAAM,MAAM,IAM1B,GAAG,eAAe,CAAC,sBAAsB;GAE5C,MAAM,SAAqB;IACzB,cAAc,SAAS,KAAK;IAC5B,gBAAgB,SAAS,KAAK;IAC9B,mBAAmB,SAAS,KAAK,sBAAsB;IACvD,qBAAqB,SAAS,KAAK,wBAAwB;IAC3D,cAAc,SAAS,KAAK,iBAAiB;IAC9C;AAED,aAAU,cAAc,OAAO;AAC/B,UAAO;WACA,OAAO;AACd,WAAQ,MAAM,gCAAgC,MAAM;AACpD,UAAO;IACL,cAAc;IACd,gBAAgB;IAChB,mBAAmB;IACnB,qBAAqB;IACrB,cAAc;IACf;;;CAIL,eAAe,MAAM,UAAkB,UAAoC;AACzE,YAAU,WAAW,KAAK;AAC1B,YAAU,SAAS,KAAK;AAExB,MAAI;GACF,MAAM,WAAW,MAAM,MAAM,KAC3B,GAAG,eAAe,CAAC,cACnB;IAAE;IAAU;IAAU,CACvB;AAED,aAAU,SAAS,SAAS,KAAK,cAAc,SAAS,KAAK,WAAW;AACxE,aAAU,YAAY,SAAS;AAE/B,SAAM,gBAAgB;AAGtB,yBAAsB;AAEtB,UAAO;WACA,OAAO;AACd,OAAI,MAAM,aAAa,MAAM,IAAI,MAAM,SACrC,WAAU,SAAS,MAAM,SAAS,KAAK,UAAU,eAAe;OAEhE,WAAU,SAAS,mCAAmC;AAExD,UAAO;YACC;AACR,aAAU,WAAW,MAAM;;;CAI/B,eAAe,SAAS,UAAkB,UAAkB,OAAkC;AAC5F,YAAU,WAAW,KAAK;AAC1B,YAAU,SAAS,KAAK;AAExB,MAAI;AACF,SAAM,MAAM,KACV,GAAG,eAAe,CAAC,kBACnB;IAAE;IAAU;IAAU;IAAO,CAC9B;AAED,UAAO,MAAM,MAAM,UAAU,SAAS;WAC/B,OAAO;AACd,OAAI,MAAM,aAAa,MAAM,IAAI,MAAM,SACrC,WAAU,SAAS,MAAM,SAAS,KAAK,UAAU,sBAAsB;OAEvE,WAAU,SAAS,mCAAmC;AAExD,UAAO;YACC;AACR,aAAU,WAAW,MAAM;;;CAI/B,eAAe,iBAA2C;AACxD,MAAI,CAAC,UAAU,MACb,QAAO;AAGT,MAAI;GACF,MAAM,WAAW,MAAM,MAAM,IAC3B,GAAG,eAAe,CAAC,YACnB,EAAE,SAAS,eAAe,EAAE,CAC7B;GAED,MAAM,WAAqB;IACzB,IAAI,SAAS,KAAK;IAClB,UAAU,SAAS,KAAK;IACxB,WAAW,SAAS,KAAK;IACzB,OAAO,SAAS,KAAK;IACrB,MAAM,SAAS,KAAK;IACpB,UAAU,SAAS,KAAK;IACzB;AAED,aAAU,YAAY,SAAS;AAC/B,UAAO;UACD;AACN,UAAO;;;CAIX,eAAe,cAAgC;AAC7C,MAAI,CAAC,UAAU,MACb,QAAO;AAGT,MAAI;GACF,MAAM,WAAW,MAAM,MAAM,IAC3B,GAAG,eAAe,CAAC,eACnB,EACE,SAAS,EACP,eAAe,UAAU,UAAU,SACpC,EACF,CACF;AAED,OAAI,SAAS,KAAK,SAAS,SAAS,KAAK,UAAU;AACjD,cAAU,YAAY,SAAS,KAAK,SAAS;AAC7C,WAAO;;AAGT,aAAU,YAAY;AACtB,UAAO;UACD;AACN,aAAU,YAAY;AACtB,UAAO;;;;;;;;CASX,eAAe,eAAiC;AAC9C,MAAI,CAAC,UAAU,MAAO,QAAO;AAC7B,MAAI,gBAAiB,QAAO;AAE5B,qBAAmB,YAAY;AAC7B,gBAAa,QAAQ;AAErB,OAAI;IACF,MAAM,WAAW,MAAM,MAAM,KAC3B,GAAG,eAAe,CAAC,gBACnB,EAAE,EACF,EAAE,SAAS,eAAe,EAAE,CAC7B;AAED,cAAU,SAAS,SAAS,KAAK,cAAc,SAAS,KAAK,WAAW;AAGxE,0BAAsB;AAEtB,WAAO;YACA,OAAO;AAGd,QAAI,MAAM,aAAa,MAAM,IAAI,MAAM,UAAU,WAAW,KAAK;AAC/D,aAAQ,KAAK,gDAAgD;AAC7D,eAAU,YAAY;AACtB,uBAAkB;;AAEpB,WAAO;aACC;AACR,iBAAa,QAAQ;AACrB,sBAAkB;;MAElB;AAEJ,SAAO;;;;;CAMT,SAAS,uBAA6B;AAEpC,oBAAkB;AAElB,MAAI,CAAC,UAAU,aACb;EAIF,MAAM,YADY,UAAU,aAAa,SAAS,GACpB;EAC9B,MAAM,MAAM,KAAK,KAAK;AAEtB,MAAI,aAAa,KAAK;AAEpB,iBAAc;AACd;;EAIF,MAAM,QAAQ,YAAY;AAC1B,oBAAkB,OAAO,iBAAiB;AACxC,iBAAc;KACb,MAAM;;;;;CAMX,SAAS,mBAAyB;AAChC,MAAI,oBAAoB,MAAM;AAC5B,UAAO,aAAa,gBAAgB;AACpC,qBAAkB;;;;;;;CAQtB,SAAS,0BAAgC;AACvC,MAAI,CAAC,UAAU,SAAS,CAAC,UAAU,aACjC;EAIF,MAAM,YADY,UAAU,aAAa,SAAS,GACpB;AAG9B,MAFY,KAAK,KAAK,IAEX,UACT,eAAc;;CAIlB,eAAe,iBAAgC;AAC7C,YAAU,YAAY;AACtB,QAAM,iBAAiB;AAEvB,MAAI,UAAU;OACE,MAAM,aAAa,EACtB;AACT,UAAM,gBAAgB;AAEtB,0BAAsB;;;;CAK5B,SAAS,SAAe;AACtB,oBAAkB;AAClB,YAAU,QAAQ;;CAGpB,SAAS,gBAAwC;AAC/C,MAAI,UAAU,MACZ,QAAO,EAAE,eAAe,UAAU,UAAU,SAAS;AAEvD,SAAO,EAAE;;CAGX,eAAe,cAAc,MAKqB;AAChD,MAAI,CAAC,UAAU,MACb,QAAO;GAAE,SAAS;GAAO,OAAO;GAAqB;AAGvD,MAAI;GACF,MAAM,cAAoC,EAAE;AAC5C,OAAI,KAAK,UAAU,KAAA,EAAW,aAAY,QAAQ,KAAK;AACvD,OAAI,KAAK,cAAc,KAAA,EAAW,aAAY,YAAY,KAAK;AAC/D,OAAI,KAAK,gBAAiB,aAAY,mBAAmB,KAAK;AAC9D,OAAI,KAAK,YAAa,aAAY,eAAe,KAAK;GAEtD,MAAM,WAAW,MAAM,MAAM,IAC3B,GAAG,eAAe,CAAC,YACnB,aACA,EAAE,SAAS,eAAe,EAAE,CAC7B;GAED,MAAM,WAAqB;IACzB,IAAI,SAAS,KAAK;IAClB,UAAU,SAAS,KAAK;IACxB,WAAW,SAAS,KAAK;IACzB,OAAO,SAAS,KAAK;IACrB,MAAM,SAAS,KAAK;IACpB,UAAU,SAAS,KAAK;IACzB;AACD,aAAU,YAAY,SAAS;AAE/B,UAAO,EAAE,SAAS,MAAM;WACjB,OAAO;AACd,OAAI,MAAM,aAAa,MAAM,IAAI,MAAM,SACrC,QAAO;IAAE,SAAS;IAAO,OAAO,MAAM,SAAS,KAAK,UAAU;IAAiB;AAEjF,UAAO;IAAE,SAAS;IAAO,OAAO;IAAoC;;;CAKxE,IAAI,gBAA+B;AAEnC,KAAI,oBAAoB,EAAE;AACxB,kBAAgB;AACd,4BAAyB;AACzB,mBAAgB,OAAO,YAAY,yBAAyB,gCAAgC;IAC5F;AAEF,oBAAkB;AAChB,OAAI,kBAAkB,MAAM;AAC1B,WAAO,cAAc,cAAc;AACnC,oBAAgB;;AAGlB,2BAAwB,KAAK,IAAI,GAAG,wBAAwB,EAAE;AAC9D,OAAI,0BAA0B,EAC5B,mBAAkB;IAEpB;AAGF,cACQ,UAAU,eACf,eAAe;AACd,OAAI,WACF,uBAAsB;OAEtB,mBAAkB;IAGvB;;AAGH,QAAO;EAEL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACD;;;;ACpbH,eAAe,eAAe;AAC5B,KAAI;AACF,SAAO,MAAM,OAAO;SACd;AACN,QAAM,IAAI,MACR,uGACD;;;;AAWL,SAAgB,aAAa;CAC3B,MAAM,YAAY,cAAc;CAChC,MAAM,gBAAgB,kBAAkB;CAExC,SAAS,gBAAwB;AAC/B,SAAO,cAAc,eAAe;;CAGtC,eAAe,cAAgC;AAC7C,MAAI;GACF,MAAM,EAAE,4BAA4B,MAAM,cAAc;AACxD,UAAO,yBAAyB;UAC1B;AACN,UAAO;;;CAIX,eAAe,gBAAgB,YAAuC;EACpE,MAAM,WAAW,MAAM,cAAc;AACrC,MAAI,CAAC,SAAS,yBAAyB,EAAE;AACvC,aAAU,SAAS,4CAA4C;AAC/D,UAAO;;AAGT,YAAU,WAAW,KAAK;AAC1B,YAAU,SAAS,KAAK;AAExB,MAAI;GACF,MAAM,kBAAkB,MAAM,MAAM,IAClC,GAAG,eAAe,CAAC,iCACnB;IACE,SAAS,EAAE,eAAe,UAAU,UAAU,SAAS;IACvD,iBAAiB;IAClB,CACF;GAED,MAAM,UAAU,KAAK,MAAM,gBAAgB,KAAK,QAAQ;GAExD,MAAM,aAAa,MAAM,SAAS,kBAAkB,QAAQ;AAE5D,SAAM,MAAM,KACV,GAAG,eAAe,CAAC,gCACnB;IACE,YAAY,KAAK,UAAU,WAAW;IACtC,aAAa;IACd,EACD;IACE,SAAS,EAAE,eAAe,UAAU,UAAU,SAAS;IACvD,iBAAiB;IAClB,CACF;AAED,aAAU,cAAc;IACtB,GAAG,UAAU;IACb,mBAAmB;IACpB,CAAC;AAEF,UAAO;WACA,OAAO;AACd,OAAI,MAAM,aAAa,MAAM,IAAI,MAAM,SACrC,WAAU,SAAS,MAAM,SAAS,KAAK,UAAU,8BAA8B;YACtE,iBAAiB,MAC1B,KAAI,MAAM,SAAS,kBACjB,WAAU,SAAS,0CAA0C;YACpD,MAAM,SAAS,oBACxB,WAAU,SAAS,2CAA2C;OAE9D,WAAU,SAAS,MAAM,QAAQ;OAGnC,WAAU,SAAS,8BAA8B;AAEnD,UAAO;YACC;AACR,aAAU,WAAW,MAAM;;;CAI/B,eAAe,mBAAqC;EAClD,MAAM,WAAW,MAAM,cAAc;AACrC,MAAI,CAAC,SAAS,yBAAyB,EAAE;AACvC,aAAU,SAAS,4CAA4C;AAC/D,UAAO;;AAGT,YAAU,WAAW,KAAK;AAC1B,YAAU,SAAS,KAAK;AAExB,MAAI;GACF,MAAM,kBAAkB,MAAM,MAAM,IAClC,GAAG,eAAe,CAAC,8BACnB,EAAE,iBAAiB,MAAM,CAC1B;GAED,MAAM,UAAU,KAAK,MAAM,gBAAgB,KAAK,QAAQ;GAExD,MAAM,aAAa,MAAM,SAAS,oBAAoB,QAAQ;GAE9D,MAAM,WAAW,MAAM,MAAM,KAC3B,GAAG,eAAe,CAAC,6BACnB,EAAE,YAAY,KAAK,UAAU,WAAW,EAAE,EAC1C,EAAE,iBAAiB,MAAM,CAC1B;AAED,aAAU,SAAS,SAAS,KAAK,cAAc,SAAS,KAAK,WAAW;AAExE,UAAO;WACA,OAAO;AACd,OAAI,MAAM,aAAa,MAAM,IAAI,MAAM,SACrC,KAAI,MAAM,SAAS,WAAW,IAC5B,WAAU,SAAS,4DAA4D;OAE/E,WAAU,SAAS,MAAM,SAAS,KAAK,UAAU,uBAAuB;YAEjE,iBAAiB,MAC1B,KAAI,MAAM,SAAS,kBACjB,WAAU,SAAS,4CAA4C;OAE/D,WAAU,SAAS,MAAM,QAAQ;OAGnC,WAAU,SAAS,uBAAuB;AAE5C,UAAO;YACC;AACR,aAAU,WAAW,MAAM;;;CAI/B,eAAe,kBAA6C;AAC1D,MAAI;AAOF,WANiB,MAAM,MAAM,IAC3B,GAAG,eAAe,CAAC,4BACnB,EACE,SAAS,EAAE,eAAe,UAAU,UAAU,SAAS,EACxD,CACF,EACe,KAAK;UACf;AACN,UAAO,EAAE;;;CAIb,eAAe,iBAAiB,cAAwC;AACtE,MAAI;AACF,SAAM,MAAM,OACV,GAAG,eAAe,CAAC,4BAA4B,mBAAmB,aAAa,IAC/E,EACE,SAAS,EAAE,eAAe,UAAU,UAAU,SAAS,EACxD,CACF;AAGD,QADkB,MAAM,iBAAiB,EAC3B,WAAW,EACvB,WAAU,cAAc;IACtB,GAAG,UAAU;IACb,mBAAmB;IACpB,CAAC;AAGJ,UAAO;UACD;AACN,UAAO;;;CAIX,eAAe,uBAAyC;AACtD,MAAI;AACF,SAAM,MAAM,OAAO,GAAG,eAAe,CAAC,4BAA4B,EAChE,SAAS,EAAE,eAAe,UAAU,UAAU,SAAS,EACxD,CAAC;AAEF,aAAU,cAAc;IACtB,GAAG,UAAU;IACb,mBAAmB;IACpB,CAAC;AAEF,UAAO;UACD;AACN,UAAO;;;AAIX,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACD;;;;;;;AClJH,SAAS,sBAAsB,OAA4B;AACzD,KAAI,iBAAiB,MACnB,QAAO;EACL,SAAS,MAAM;EACf,eAAe;EAChB;AAGH,KAAI,OAAO,UAAU,YAAY,UAAU,MAAM;EAC/C,MAAM,WAAW;AAGjB,MAAI,cAAc,YAAY,SAAS,UAAU;GAC/C,MAAM,WAAW,SAAS;GAC1B,MAAM,OAAO,SAAS;AAEtB,UAAO;IACL,SAAU,MAAM,UAAsB,MAAM,WAAsB;IAClE,MAAM,OAAO,SAAS,OAAO;IAC7B,SAAS;IACT,eAAe;IAChB;;AAIH,SAAO;GACL,SAAU,SAAS,WAAsB;GACzC,MAAM,SAAS;GACf,SAAS;GACT,eAAe;GAChB;;AAGH,QAAO;EACL,SAAS,OAAO,MAAM;EACtB,eAAe;EAChB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgEH,SAAgB,SACd,SACA,UAA8B,EAAE,EACb;CACnB,MAAM,EACJ,cAAc,MACd,YAAY,OACZ,gBAAgB,EAAE,EAClB,iBAAiB,uBACjB,WACA,SACA,iBAAiB,UACf;CAGJ,MAAM,OAAO,IAAc,YAAY;CACvC,MAAM,QAAQ,IAAuB,KAAK;CAC1C,MAAM,QAAQ,IAAgB,OAAO;CAGrC,MAAM,SAAS,eAAe,MAAM,UAAU,OAAO;CACrD,MAAM,YAAY,eAAe,MAAM,UAAU,UAAU;CAC3D,MAAM,YAAY,eAAe,MAAM,UAAU,UAAU;CAC3D,MAAM,UAAU,eAAe,MAAM,UAAU,QAAQ;CAGvD,eAAe,QAAQ,GAAG,MAAoC;AAC5D,QAAM,QAAQ;AACd,QAAM,QAAQ;AAEd,MAAI,eACF,MAAK,QAAQ;AAGf,MAAI;GACF,MAAM,SAAS,MAAM,QAAQ,GAAG,KAAK;AACrC,QAAK,QAAQ;AACb,SAAM,QAAQ;AACd,eAAY,OAAO;AACnB,UAAO;WACA,GAAG;GACV,MAAM,aAAa,eAAe,EAAE;AACpC,SAAM,QAAQ;AACd,SAAM,QAAQ;AACd,aAAU,WAAW;AACrB,UAAO;;;CAKX,SAAS,QAAc;AACrB,OAAK,QAAQ;AACb,QAAM,QAAQ;AACd,QAAM,QAAQ;;CAIhB,SAAS,QAAQ,SAAyB;AACxC,OAAK,QAAQ;AACb,MAAI,YAAY,MAAM;AACpB,SAAM,QAAQ;AACd,SAAM,QAAQ;;;CAKlB,SAAS,SAAS,UAAmC;AACnD,QAAM,QAAQ;AACd,MAAI,aAAa,KACf,OAAM,QAAQ;;AAKlB,KAAI,UACF,SAAQ,GAAG,cAAc;AAG3B,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;;;;;;;;;;;;;;;;AAkBH,SAAgB,cACd,UAOA;CAGA,MAAM,UAAU,IAAa,SAAS,UAAU,KAAK,CAAuB;CAC5E,MAAM,SAAS,IAA2B,SAAS,UAAU,KAAK,CAAC;CACnE,MAAM,YAAY,IAAI,MAAM;CAE5B,eAAe,UAAyB;AACtC,YAAU,QAAQ;AAClB,SAAO,QAAQ,SAAS,UAAU,KAAK;EAEvC,MAAM,WAAW,SAAS,IAAI,OAAO,IAAI,UAAU;AACjD,OAAI;IACF,MAAM,SAAS,MAAM,IAAI;AACvB,YAAQ,MAAoB,SAAS;YAChC,GAAG;AACV,WAAO,MAAM,SAAS,sBAAsB,EAAE;AAC5C,YAAQ,MAAoB,SAAS;;IAEzC;AAEF,QAAM,QAAQ,IAAI,SAAS;AAC3B,YAAU,QAAQ;;CAGpB,SAAS,QAAc;AACrB,UAAQ,QAAQ,SAAS,UAAU,KAAK;AACxC,SAAO,QAAQ,SAAS,UAAU,KAAK;AACvC,YAAU,QAAQ;;AAGpB,QAAO;EACL;EACA;EACA;EACA;EACA;EACD;;;;;ACpSH,SAAgB,gBAAgB,YAA4C;CAC1E,MAAM,MAAM,QAAQ;CACpB,MAAM,EAAE,WAAW,oBAAoB;CAEvC,MAAM,eAAe,eAAe,cAAc,OAAO,OAAO,QAAQ,GAAG;CAE3E,MAAM,SAAS,IAA6B,EAAE,CAAC;CAC/C,MAAM,cAAc,IAA6B,EAAE,CAAC;CACpD,MAAM,UAAU,oBAAoB,gCAAgC;CACpE,MAAM,YAAY,IAAI,MAAM;CAC5B,MAAM,WAAW,IAAI,MAAM;CAC3B,MAAM,QAAQ,QAAQ;CACtB,MAAM,eAAe,QAAQ;CAC7B,MAAM,cAAc,QAAQ;CAE5B,MAAM,UAAU,eAAe;AAC7B,SAAO,KAAK,UAAU,OAAO,MAAM,KAAK,KAAK,UAAU,YAAY,MAAM;GACzE;CAEF,eAAe,OAAsB;EACnC,MAAM,OAAO,aAAa;AAC1B,MAAI,CAAC,KAAM;AAEX,YAAU,QAAQ;AAClB,MAAI;GACF,MAAM,WAAW,MAAM,QAAQ,UACvB,IAAI,IACR,YAAY,mBAAmB,KAAK,CAAC,SACtC,EACD;IAAE,SAAS;IAAQ,cAAc;IAAgC,CAClE;AACD,UAAO,QAAQ,EAAE,GAAG,SAAS,QAAQ;AACrC,eAAY,QAAQ,EAAE,GAAG,SAAS,QAAQ;UACpC,WAEE;AACR,aAAU,QAAQ;;;CAItB,eAAe,OAAyB;EACtC,MAAM,OAAO,aAAa;AAC1B,MAAI,CAAC,KAAM,QAAO;AAElB,WAAS,QAAQ;AACjB,MAAI;GACF,MAAM,WAAW,MAAM,QAAQ,UACvB,IAAI,MACR,YAAY,mBAAmB,KAAK,CAAC,UACrC,EAAE,QAAQ,OAAO,OAAO,CACzB,EACD;IAAE,SAAS;IAAQ,cAAc;IAAgC,CAClE;AACD,UAAO,QAAQ,EAAE,GAAG,SAAS,QAAQ;AACrC,eAAY,QAAQ,EAAE,GAAG,SAAS,QAAQ;AAC1C,UAAO;UACD;AACN,UAAO;YACC;AACR,YAAS,QAAQ;;;CAIrB,SAAS,QAAc;AACrB,SAAO,QAAQ,EAAE,GAAG,YAAY,OAAO;AACvC,UAAQ,YAAY;;AAGtB,iBAAgB;AACd,QAAM;GACN;AAEF,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;;;ACKH,SAAS,oBAAoB,SAAiD;AAC5E,KAAI,CAAC,QAAS,QAAO,KAAA;AACrB,QAAO,QAAQ,SAAS,IAAI,QAAQ,QAAQ,QAAQ,GAAG,GAAG;;AAG5D,SAAS,mBAAmB,QAAgD;AAC1E,KAAI,CAAC,OAAQ,QAAO,KAAA;AACpB,KAAI,OAAO,WAAW,QAAQ,CAAE,QAAO,oBAAoB,OAAO;AAClE,KAAI,WAAW,OAAQ,QAAO;AAC9B,KAAI,OAAO,WAAW,IAAI,CAAE,QAAO,oBAAoB,OAAO,SAAS;AACvE,QAAO,oBAAoB,QAAQ,SAAS;;AAG9C,SAAS,uBAAuB,MAAsB;CACpD,MAAM,MAAM,KAAK,MAAM,IAAI;AAE3B,SADiB,IAAI,WAAW,IAAI,GAAG,MAAM,IAAI,OACjC,QAAQ,QAAQ,GAAG,IAAI;;AAGzC,SAAS,qBAAqB,MAAc,eAA+B;CACzE,MAAM,iBAAiB,uBAAuB,KAAK;AACnD,KAAI,mBAAmB,IAAK,QAAO;AACnC,QAAO,eAAe,QAAQ,QAAQ,GAAG,CAAC,QAAQ,QAAQ,IAAI,IAAI,QAAQ,gBAAgB;;;AAI5F,SAAgB,2BAA2B,UAA8C;CACvF,MAAM,aAAa,SAAS,OAAO,QAAQ;CAC3C,MAAM,WAAW,SAAS,OAAO,YAAY,EAAE;AAW/C,SAVwC,SAAS,SAAS,IACtD,WACA,CAAC;EACC,MAAM;EACN,OAAO,SAAS,OAAO,QAAQ;EAC/B,IAAI;EACJ,MAAM,cAAc,KAAA;EACpB,aAAa,SAAS,OAAO;EAC9B,CAAC,EAEQ,KAAK,MAAM,WAAW;EAClC,IAAI,KAAK,MAAM,qBAAqB,KAAK,MAAM,MAAM;EACrD,OAAO,KAAK;EACZ,IAAI,uBAAuB,KAAK,KAAK;EACrC,MAAM,KAAK,QAAQ,cAAc,KAAA;EACjC,MAAM,KAAK,eAAe,SAAS,OAAO;EAC3C,EAAE;;;AAIL,SAAgB,qBAAqB,UAA0B,iBAAkC;AAG/F,KAAI,gBAAiB,QAAO,oBAAoB,gBAAgB,IAAI;CAEpE,MAAM,WAAW,4BAA4B;CAC7C,MAAM,iBACJ,oBAAoB,UAAU,QAAQ,WAAW,IACjD,mBAAmB,UAAU,QAAQ,aAAa;AACpD,KAAI,eAAgB,QAAO;AAE3B,QAAO,oBAAoB,SAAS,OAAO,UAAU,IAAI;;AAG3D,SAAS,WAAW,MAAc,SAAkC,YAA8B;CAChG,IAAI,WAAW;AACf,MAAK,MAAM,SAAS,YAAY;EAC9B,MAAM,QAAQ,QAAQ;AACtB,MAAI,UAAU,KAAA,KAAa,UAAU,KACnC,OAAM,IAAI,MAAM,sCAAsC,MAAM,wBAAwB,OAAO;AAE7F,aAAW,SAAS,QAAQ,IAAI,MAAM,IAAI,mBAAmB,OAAO,MAAM,CAAC,CAAC;AAC5E,aAAW,SAAS,QAAQ,IAAI,SAAS,mBAAmB,OAAO,MAAM,CAAC,CAAC;;AAE7E,QAAO;;AAGT,SAAS,sBACP,SACA,YACyB;AACzB,KAAI,CAAC,WAAW,OAAQ,QAAO;CAC/B,MAAM,cAAc,EAAE,GAAG,SAAS;AAClC,MAAK,MAAM,SAAS,YAAY;AAC9B,MAAI,YAAY,WAAW,KAAA,KAAa,YAAY,WAAW,KAAM;AACrE,MAAI,sBAAsB,MAAM,EAAE;GAChC,MAAM,eAAe,4BAA4B;AACjD,OAAI,iBAAiB,KAAA,EACnB,aAAY,SAAS;;;AAI3B,QAAO;;AAGT,SAAS,sBAAsB,OAAwB;AACrD,QAAO,UAAU,kBAAkB,UAAU;;AAG/C,SAAS,kBAAkB,SAAkC,YAAsB;CACjF,MAAM,OAAO,EAAE,GAAG,SAAS;AAC3B,MAAK,MAAM,SAAS,WAClB,QAAO,KAAK;AAEd,QAAO;;AAGT,SAAS,uBACP,SACA,YACA,aACA,cACA,2BAA2B,OAC3B;AACA,KAAI,CAAC,aAAa,OAChB,QAAO,2BAA2B,kBAAkB,SAAS,WAAW,GAAG,EAAE;CAG/E,MAAM,QAAiC,EAAE;AACzC,MAAK,MAAM,SAAS,aAAa;EAC/B,MAAM,OAAO,OAAO,UAAU,WAAW,QAAQ,MAAM;EACvD,MAAM,YAAY,OAAO,UAAU,WAAW,QAAQ,MAAM,aAAa,MAAM;EAC/E,MAAM,WAAW,OAAO,UAAU,YAAY,MAAM,aAAa;EACjE,MAAM,QAAQ,QAAQ;AACtB,MAAI,UAAU,KAAA,KAAa,UAAU,MAAM;AACzC,OAAI,SACF,OAAM,IAAI,MACR,uCAAuC,UAAU,wBAAwB,gBAAgB,KAC1F;AAEH;;AAEF,QAAM,QAAQ;;AAEhB,QAAO;;AAGT,SAAS,QAAQ,OAAyC;AACxD,QAAO,OAAO,KAAK,MAAM,CAAC,SAAS;;AAGrC,SAAS,uBACP,SACA,gBACA,UACS;AACT,KAAI,CAAC,SAAS,QAAS,QAAO,KAAA;CAC9B,MAAM,OAAO,SAAS,YAAY,UAAU,SAAS,aAAa,SAC9D,eAAe,OACf;AACJ,KAAI,SAAS,KAAA,KAAa,SAAS,KACjC,OAAM,IAAI,MAAM,uDAAuD,SAAS,OAAO;AAEzF,QAAO;;AAGT,SAAS,2BACP,UACA,UACA,SACA,iBACA;CACA,MAAM,gBACJ,WAAW,OAAO,YAAY,YAAY,CAAC,MAAM,QAAQ,QAAQ,GAC7D,UACA,EAAE;CACR,MAAM,aAAa,SAAS,cAAc,EAAE;CAC5C,MAAM,iBAAiB,sBAAsB,eAAe,WAAW;CACvE,MAAM,OAAO,WAAW,SAAS,MAAM,gBAAgB,WAAW;CAClE,MAAM,QAAQ,uBACZ,gBACA,YACA,SAAS,aACT,SAAS,MACT,SAAS,WAAW,MACrB;AAED,QAAO;EACL,SAAS,qBAAqB,UAAU,gBAAgB;EACxD;EACA;EACA;EACD;;AAGH,SAAS,iBAAiB,QAAyB,KAAa,OAAsB;AACpF,KAAI,UAAU,KAAA,KAAa,UAAU,KAAM;AAC3C,KAAI,MAAM,QAAQ,MAAM,EAAE;AACxB,OAAK,MAAM,QAAQ,MACjB,kBAAiB,QAAQ,KAAK,KAAK;AAErC;;AAEF,KAAI,OAAO,UAAU,UAAU;AAC7B,SAAO,OAAO,KAAK,KAAK,UAAU,MAAM,CAAC;AACzC;;AAEF,QAAO,OAAO,KAAK,OAAO,MAAM,CAAC;;AAGnC,SAAS,YAAY,OAAwC;CAC3D,MAAM,SAAS,IAAI,iBAAiB;AACpC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,CAC9C,kBAAiB,QAAQ,KAAK,MAAM;CAEtC,MAAM,aAAa,OAAO,UAAU;AACpC,QAAO,aAAa,IAAI,eAAe;;AAGzC,SAAS,YAAY,SAAiB,MAAsB;AAC1D,KAAI,CAAC,QAAS,QAAO;AAGrB,QAAO,GAFgB,QAAQ,SAAS,IAAI,GAAG,QAAQ,MAAM,GAAG,GAAG,GAAG,UAC/C,KAAK,WAAW,IAAI,GAAG,OAAO,IAAI;;;AAK3D,SAAgB,uBACd,UACA,UACA,SACA,UAAyC,EAAE,EACnC;CACR,MAAM,QAAQ,2BAA2B,UAAU,UAAU,SAAS,QAAQ,QAAQ;CACtF,MAAM,gBAAgB,GAAG,MAAM,OAAO,YAAY,MAAM,MAAM;AAC9D,KAAI,QAAQ,mBAAmB,MAAO,QAAO;AAC7C,QAAO,YAAY,MAAM,SAAS,cAAc;;;AAIlD,SAAgB,mBACd,UACA,SACS;CACT,MAAM,SAA+C,EAAE;AAEvD,MAAK,MAAM,CAAC,MAAM,aAAa,OAAO,QAAQ,QAAQ,UAAU,CAC9D,QAAO,QAAQ,OAAO,YAAsB;EAC1C,MAAM,QAAQ,2BAA2B,UAAU,UAAU,SAAS,QAAQ,QAAQ;EACtF,MAAM,MAAM,OAAO,EAAE,SAAS,MAAM,SAAS,CAAC;EAC9C,MAAM,cAAc,QAAQ,MAAM,MAAM,GAAG,EAAE,QAAQ,MAAM,OAAO,GAAG,KAAA;AAErE,MAAI,SAAS,WAAW,MACtB,QAAO,IAAI,IAAI,MAAM,MAAM,YAAY;AAGzC,MAAI,SAAS,WAAW,UAAU;GAChC,MAAM,OAAO,uBAAuB,SAAS,MAAM,gBAAgB,SAAS;GAC5E,MAAM,SAAS,SAAS,KAAA,IACpB,cACA;IAAE,GAAG;IAAa,MAAM;IAAM;AAClC,UAAO,IAAI,OAAO,MAAM,MAAM,OAAO;;EAGvC,MAAM,OAAO,uBAAuB,SAAS,MAAM,gBAAgB,SAAS;AAE5E,MAAI,SAAS,WAAW,OAAQ,QAAO,IAAI,KAAK,MAAM,MAAM,MAAM,YAAY;AAC9E,MAAI,SAAS,WAAW,MAAO,QAAO,IAAI,IAAI,MAAM,MAAM,MAAM,YAAY;AAC5E,MAAI,SAAS,WAAW,QAAS,QAAO,IAAI,MAAM,MAAM,MAAM,MAAM,YAAY;AAChF,QAAM,IAAI,MAAM,kDAAkD,SAAS,SAAS;;AAIxF,QAAO;;;AAIT,SAAgB,gBAAyB,QAA0B;AACjE,QAAO;;;AAIT,SAAgB,oBAAyD;CACvE,MAAM,EAAE,WAAW,oBAAoB;AAEvC,QAAO,EAAE,UADQ,eAAe,OAAO,OAAO,SAAkC,EAC7D;;;AAIrB,SAAgB,qBACd,UAAuC,EAAE,EACA;CACzC,MAAM,MAAM,OAAO,EAAE,SAAS,QAAQ,cAAc,4BAA4B,EAAE,gBAAgB,CAAC;CACnG,MAAM,aAAa,WAAoC,8BAA2C,CAAC;CACnG,MAAM,UAAU,oBAAoB,oCAAoC;CACxE,MAAM,YAAY,QAAQ;CAC1B,MAAM,QAAQ,QAAQ;CACtB,MAAM,eAAe,QAAQ;CAE7B,MAAM,eAAe,eAAmC;AACtD,SAAO,4BAA4B;GACnC;CACF,MAAM,gBAAgB,eAAe,aAAa,UAAU,KAAA,EAAU;CAEtE,SAAS,sBAA8B;EACrC,MAAM,KAAK,aAAa;AACxB,MAAI,OAAO,KAAA,EACT,OAAM,IAAI,MAAM,gDAAgD;AAElE,SAAO;;CAGT,eAAe,gBAAgB,KAAK,aAAa,OAAyC;AACxF,MAAI,OAAO,KAAA,GAAW;AACpB,cAAW,QAAQ,8BAA2C;AAC9D,UAAO,WAAW;;AAGpB,MAAI;GACF,MAAM,SAAS,MAAM,QAAQ,UACrB,IAAI,IAAiB,gBAAgB,KAAK,EAChD;IAAE,SAAS;IAAQ,cAAc;IAAqC,CACvE;AACD,cAAW,QAAQ;AACnB,UAAO;WACA,GAAG;AACV,cAAW,QAAQ,KAAA;AACnB;;;CAIJ,eAAe,UAA4C;AACzD,SAAO,iBAAiB;;AAG1B,OACE,eACC,OAAO;AACN,MAAI,QAAQ,cAAc,MAAO;AACjC,MAAI,OAAO,KAAA,GAAW;AACpB,cAAW,QAAQ,8BAA2C;AAC9D;;AAEG,kBAAgB,GAAG;IAE1B,EAAE,WAAW,QAAQ,cAAc,OAAO,CAC3C;AAED,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;EACD"}
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
- import { $ as Skeleton_default, A as ScheduleCalendar_default, B as PluginWorkspaceView_default, C as ProgressBar_default, D as DoseDesignWorkspaceView_default, E as AppContainer_default, F as PlateMapEditor_default, G as StepWizard_default, H as AppSidebar_default, I as SampleLegend_default, J as AppAvatarMenu_default, K as AppTopBar_default, L as WellPlate_default, M as AutoGroupModal_default, N as LoadingSpinner_default, O as ControlWorkspaceView_default, P as ReagentList_default, Q as EmptyState_default, R as ExperimentTimeline_default, S as Avatar_default, T as Divider_default, U as FormBuilder_default, V as AppLayout_default, W as FormActions_default, X as ExperimentSelectorModal_default, Y as PluginIcon_default, Z as ExperimentCodeBadge_default, _ as BioTemplateExperimentWorkspaceView_default, _t as BaseButton_default, a as ExperimentDataViewer_default, at as IconButton_default, b as Tooltip_default, c as ChemicalFormula_default, ct as FormField_default, d as SampleHierarchyTree_default, dt as DropdownButton_default, et as ExperimentPopover_default, f as ReagentEditor_default, ft as BasePill_default, g as BioTemplatePackWorkspaceView_default, gt as ColorSlider_default, h as BioTemplatePresetWorkspaceView_default, ht as BaseTabs_default, i as TimeRangeInput_default, it as ThemeToggle_default, j as SampleSelector_default, k as ComponentBindingRenderer_default, l as ScientificNumber_default, lt as DataFrame_default, m as RackEditor_default, mt as BaseModal_default, n as FitPanel_default, nt as SettingsModal_default, o as BatchProgressList_default, ot as AppToastContainer_default, p as GroupAssigner_default, pt as SegmentedControl_default, q as AppPluginSwitcher_default, r as ResourceCard_default, rt as CollapsibleCard_default, s as AuditTrail_default, st as AlertBox_default, tt as ConfirmDialog_default, u as ProtocolStepEditor_default, ut as Calendar_default, v as BioTemplateRenderer_default, w as StatusIndicator_default, x as Breadcrumb_default, y as ChartContainer_default, z as DoseCalculator_default } from "./components-DihbSJjU.js";
2
- import { $ as toSampleOptions, $n as compareTime, $t as validateTimeCourseData, A as toFlowPanelColumns, An as defineWellPlateDoseControlProps, At as createReagentListTemplate, B as toProtocolDataFrame, Bn as useConcentrationUnits, Bt as extractTemplateCollection, C as toAssayMatrixRows, Cn as defineControlComponentBindings, Cr as BaseCheckbox_default, Ct as createFlowCytometryPanelTemplate, D as toCalibrationCurveRows, Dn as defineDoseDesignControlModel, Dr as BaseInput_default, Dt as createProtocolStepsTemplate, E as toCalibrationCurveDataFrame, En as defineDoseCalculatorControlProps, Er as BaseTextarea_default, Et as createPlateMapTemplate, F as toInstrumentRunRows, Fn as useControlWorkspace, Ft as createTimeCourseTemplate, G as toQpcrRows, Gn as ATOMIC_WEIGHTS, Gt as validateFlowCytometryPanelData, H as toProtocolSteps, Hn as SequenceInput_default, Ht as validateAssayMatrixData, I as toInstrumentRunScheduleEvents, In as getFieldRegistryEntry, It as createWellPlateScreenCollection, J as toReagentDataFrame, Jn as FileUploader_default, Jt as validateProtocolStepsData, K as toQpcrWellPlateWells, Kn as useChemicalFormula, Kt as validateInstrumentRunData, L as toInstrumentRunSteps, Ln as getTypeDefault, Lt as createWesternBlotAssayCollection, M as toFlowPanelRows, Mn as getDefaultControlView, Mt as createSampleSheetTemplate, N as toInstrumentRunColumns, Nt as createTemplateCollection, O as toDoseConditions, On as defineWellPlateControlProps, Ot as createQpcrExpressionCollection, P as toInstrumentRunDataFrame, Pn as useControlSchema, Pt as createTemplateEnvelope, Q as toSampleDataFrame, Qn as addMinutes, Qt as validateSampleSheetData, R as toPlateMapEditorState, Rn as ConcentrationInput_default, Rt as ensureTemplateEnvelope, S as toAssayMatrixDataFrame, Sn as controlsToViewItems, Sr as BaseToggle_default, St as createFlowCytometryAssayCollection, T as toCalibrationCurveColumns, Tn as defineControls, Tt as createLcmsBatchCollection, U as toQpcrColumns, Un as useSequenceUtils, Ut as validateCalibrationCurveData, V as toProtocolRows, Vn as MoleculeInput_default, Vt as getTemplateData, W as toQpcrDataFrame, Wn as FormulaInput_default, Wt as validateDoseResponseData, X as toReagentRows, Xn as TagsInput_default, Xt as validateReagentListData, Y as toReagentListItems, Yn as NumberInput_default, Yt as validateQpcrPlateData, Z as toSampleColumns, Zn as TimePicker_default, Zt as validateSamplePrepData, _n as controlsToSectionFormSchemas, _r as useListSelection, _t as createBioTemplatePresetCollectionFromControls, a as getBioTemplateControlDefaults, an as getBioTemplatePackInfo, ar as formatTimeSlot, at as toTimeCourseColumns, b as toBioTemplateComponentUsage, bn as controlsToTopBarSettingsConfig, br as BaseSlider_default, bt as createDoseResponseTemplate, c as listBioTemplatePresetControlSchemas, cn as bioTemplateCatalog, cr as isTimeInRange, ct as toTimeCourseSteps, dn as searchBioTemplateCatalog, dr as snapToSlot, dt as TEMPLATE_COLLECTION_KEY, en as bioTemplatePresets, er as durationMinutes, et as toSamplePrepColumns, f as listBioTemplateComponentBindings, fn as controlValuesToComponentBindings, fr as toMinutes, ft as assertTemplateEnvelope, g as toBioTemplateComponentProps, gn as controlsToSectionFormSchema, gr as MultiSelect_default, gt as createBioTemplatePresetCollection, h as toBioTemplateComponentImports, hn as controlsToFormSchema, ht as createBioTemplatePackCollection, i as createBioTemplateControlToolkit, in as bioTemplatePacks, ir as formatTime, it as toTemplateDataFrame, j as toFlowPanelDataFrame, jn as getControlDefaults, jt as createSamplePrepTemplate, k as toDoseLayoutState, kn as defineWellPlateDoseComponentBindings, kt as createQpcrPlateTemplate, l as requireBioTemplateControlSchema, ln as getBioTemplateInfo, lr as parseTime, lt as toWellMapArray, m as toBioTemplateComponentBindingsById, mn as controlValuesToComponentProps, mr as DatePicker_default, mt as createAssayMatrixTemplate, n as bioTemplateControlsToSectionFormSchemas, nn as listBioTemplatePresets, nr as findNearestTimeSlotIndex, nt as toSamplePrepRows, o as getBioTemplateControlSchema, on as listBioTemplatePacks, or as fromMinutes, ot as toTimeCourseDataFrame, p as toBioTemplateComponentBindings, pn as controlValuesToComponentBindingsById, pr as useTimeUtils, pt as bioTemplatePresetControlValuesToOptions, q as toReagentColumns, qn as DateTimePicker_default, qt as validatePlateMapData, r as bioTemplateControlsToSidebarPanels, rn as searchBioTemplatePresets, rr as formatDuration, rt as toSampleRows, s as listBioTemplateControlSchemas, sn as searchBioTemplatePacks, sr as generateTimeSlots, st as toTimeCourseRows, t as bioTemplateControlsToFormSchema, tn as getBioTemplatePresetInfo, tr as findAvailableSlots, tt as toSamplePrepDataFrame, u as getBioTemplateComponentBindings, un as listBioTemplateCatalog, ur as rangesOverlap, ut as toWellPlateWells, v as toBioTemplateComponentPropsById, vn as controlsToSettingsSchema, vr as useSelectionLimit, vt as createCalibrationCurveTemplate, w as toAssayMatrixSampleOptions, wn as defineControlModel, wr as BaseSelect_default, wt as createInstrumentRunTemplate, x as toAssayMatrixColumns, xn as controlsToViewIds, xr as BaseRadioGroup_default, xt as createElisaAssayCollection, y as toBioTemplateComponentSnippets, yn as controlsToSidebarPanels, yr as useEventListener, yt as createDefaultBioTemplate, z as toProtocolColumns, zn as UnitInput_default, zt as ensureTemplateFromCollection } from "./templates-Cyt0Suwf.js";
3
- import { $ as useTheme, A as useAutoGroup, B as DATE_PRESET_OPTIONS, C as useSampleGroups, D as DEFAULT_COLORS, E as hslToHex, F as usePlatformContext, G as datePresetToISO, H as EXPERIMENT_STATUS_OPTIONS, I as useExperimentSelector, J as getExperimentStatusVariant, K as formatExperimentDate, L as useRequestSyncState, M as useDoseCalculator, N as APP_EXPERIMENT_KEY, O as extractSamplesFromDesignData, P as useAppExperiment, Q as useForm, R as useDebouncedWatch, S as useExpansionSet, T as hexToHsl, U as EXPERIMENT_STATUS_VARIANT_MAP, V as EXPERIMENT_STATUS_LABELS, W as SORT_OPTIONS, X as evaluateCondition, Y as resolveExperimentCode, Z as useFormBuilder, _ as useExperimentSave, a as generateDilutionSeries, at as useSortedItems, c as useRackEditor, d as useBioTemplateWorkspace, et as useToast, f as getBioTemplateComponentProps, g as useTemplateCollection, h as useBioTemplateControls, i as DEFAULT_UNITS, it as compareSortValues, j as useWellPlateEditor, l as useBioTemplatePresetWorkspace, m as useBioTemplateComponents, n as useProtocolTemplates, nt as normalizeSearchQuery, o as useReagentSeries, p as toBioTemplateComponentPropsByComponent, q as formatExperimentStatus, r as DEFAULT_PRESETS, rt as useTextSearch, s as useGroupAssignment, t as useExperimentData, tt as candidateMatchesSearch, u as useBioTemplatePackWorkspace, w as deriveShade, x as useScheduleDrag, z as useApi } from "./useExperimentData-CM6Y0u5L.js";
4
- import { n as colorPalettes, r as useSettingsStore, t as useAuthStore } from "./auth-QQj2kkze.js";
1
+ import { $ as Skeleton_default, A as ScheduleCalendar_default, B as PluginWorkspaceView_default, C as ProgressBar_default, D as DoseDesignWorkspaceView_default, E as AppContainer_default, F as PlateMapEditor_default, G as StepWizard_default, H as AppSidebar_default, I as SampleLegend_default, J as AppAvatarMenu_default, K as AppTopBar_default, L as WellPlate_default, M as AutoGroupModal_default, N as LoadingSpinner_default, O as ControlWorkspaceView_default, P as ReagentList_default, Q as EmptyState_default, R as ExperimentTimeline_default, S as Avatar_default, T as Divider_default, U as FormBuilder_default, V as AppLayout_default, W as FormActions_default, X as ExperimentSelectorModal_default, Y as PluginIcon_default, Z as ExperimentCodeBadge_default, _ as BioTemplateExperimentWorkspaceView_default, _t as BaseButton_default, a as ExperimentDataViewer_default, at as IconButton_default, b as Tooltip_default, c as ChemicalFormula_default, ct as FormField_default, d as SampleHierarchyTree_default, dt as DropdownButton_default, et as ExperimentPopover_default, f as ReagentEditor_default, ft as BasePill_default, g as BioTemplatePackWorkspaceView_default, gt as ColorSlider_default, h as BioTemplatePresetWorkspaceView_default, ht as BaseTabs_default, i as TimeRangeInput_default, it as ThemeToggle_default, j as SampleSelector_default, k as ComponentBindingRenderer_default, l as ScientificNumber_default, lt as DataFrame_default, m as RackEditor_default, mt as BaseModal_default, n as FitPanel_default, nt as SettingsModal_default, o as BatchProgressList_default, ot as AppToastContainer_default, p as GroupAssigner_default, pt as SegmentedControl_default, q as AppPluginSwitcher_default, r as ResourceCard_default, rt as CollapsibleCard_default, s as AuditTrail_default, st as AlertBox_default, tt as ConfirmDialog_default, u as ProtocolStepEditor_default, ut as Calendar_default, v as BioTemplateRenderer_default, w as StatusIndicator_default, x as Breadcrumb_default, y as ChartContainer_default, z as DoseCalculator_default } from "./components-5KSfsVqf.js";
2
+ import { $ as toSampleOptions, $n as compareTime, $t as validateTimeCourseData, A as toFlowPanelColumns, An as defineWellPlateDoseControlProps, At as createReagentListTemplate, B as toProtocolDataFrame, Bn as useConcentrationUnits, Bt as extractTemplateCollection, C as toAssayMatrixRows, Cn as defineControlComponentBindings, Cr as BaseCheckbox_default, Ct as createFlowCytometryPanelTemplate, D as toCalibrationCurveRows, Dn as defineDoseDesignControlModel, Dr as BaseInput_default, Dt as createProtocolStepsTemplate, E as toCalibrationCurveDataFrame, En as defineDoseCalculatorControlProps, Er as BaseTextarea_default, Et as createPlateMapTemplate, F as toInstrumentRunRows, Fn as useControlWorkspace, Ft as createTimeCourseTemplate, G as toQpcrRows, Gn as ATOMIC_WEIGHTS, Gt as validateFlowCytometryPanelData, H as toProtocolSteps, Hn as SequenceInput_default, Ht as validateAssayMatrixData, I as toInstrumentRunScheduleEvents, In as getFieldRegistryEntry, It as createWellPlateScreenCollection, J as toReagentDataFrame, Jn as FileUploader_default, Jt as validateProtocolStepsData, K as toQpcrWellPlateWells, Kn as useChemicalFormula, Kt as validateInstrumentRunData, L as toInstrumentRunSteps, Ln as getTypeDefault, Lt as createWesternBlotAssayCollection, M as toFlowPanelRows, Mn as getDefaultControlView, Mt as createSampleSheetTemplate, N as toInstrumentRunColumns, Nt as createTemplateCollection, O as toDoseConditions, On as defineWellPlateControlProps, Ot as createQpcrExpressionCollection, P as toInstrumentRunDataFrame, Pn as useControlSchema, Pt as createTemplateEnvelope, Q as toSampleDataFrame, Qn as addMinutes, Qt as validateSampleSheetData, R as toPlateMapEditorState, Rn as ConcentrationInput_default, Rt as ensureTemplateEnvelope, S as toAssayMatrixDataFrame, Sn as controlsToViewItems, Sr as BaseToggle_default, St as createFlowCytometryAssayCollection, T as toCalibrationCurveColumns, Tn as defineControls, Tt as createLcmsBatchCollection, U as toQpcrColumns, Un as useSequenceUtils, Ut as validateCalibrationCurveData, V as toProtocolRows, Vn as MoleculeInput_default, Vt as getTemplateData, W as toQpcrDataFrame, Wn as FormulaInput_default, Wt as validateDoseResponseData, X as toReagentRows, Xn as TagsInput_default, Xt as validateReagentListData, Y as toReagentListItems, Yn as NumberInput_default, Yt as validateQpcrPlateData, Z as toSampleColumns, Zn as TimePicker_default, Zt as validateSamplePrepData, _n as controlsToSectionFormSchemas, _r as useListSelection, _t as createBioTemplatePresetCollectionFromControls, a as getBioTemplateControlDefaults, an as getBioTemplatePackInfo, ar as formatTimeSlot, at as toTimeCourseColumns, b as toBioTemplateComponentUsage, bn as controlsToTopBarSettingsConfig, br as BaseSlider_default, bt as createDoseResponseTemplate, c as listBioTemplatePresetControlSchemas, cn as bioTemplateCatalog, cr as isTimeInRange, ct as toTimeCourseSteps, dn as searchBioTemplateCatalog, dr as snapToSlot, dt as TEMPLATE_COLLECTION_KEY, en as bioTemplatePresets, er as durationMinutes, et as toSamplePrepColumns, f as listBioTemplateComponentBindings, fn as controlValuesToComponentBindings, fr as toMinutes, ft as assertTemplateEnvelope, g as toBioTemplateComponentProps, gn as controlsToSectionFormSchema, gr as MultiSelect_default, gt as createBioTemplatePresetCollection, h as toBioTemplateComponentImports, hn as controlsToFormSchema, ht as createBioTemplatePackCollection, i as createBioTemplateControlToolkit, in as bioTemplatePacks, ir as formatTime, it as toTemplateDataFrame, j as toFlowPanelDataFrame, jn as getControlDefaults, jt as createSamplePrepTemplate, k as toDoseLayoutState, kn as defineWellPlateDoseComponentBindings, kt as createQpcrPlateTemplate, l as requireBioTemplateControlSchema, ln as getBioTemplateInfo, lr as parseTime, lt as toWellMapArray, m as toBioTemplateComponentBindingsById, mn as controlValuesToComponentProps, mr as DatePicker_default, mt as createAssayMatrixTemplate, n as bioTemplateControlsToSectionFormSchemas, nn as listBioTemplatePresets, nr as findNearestTimeSlotIndex, nt as toSamplePrepRows, o as getBioTemplateControlSchema, on as listBioTemplatePacks, or as fromMinutes, ot as toTimeCourseDataFrame, p as toBioTemplateComponentBindings, pn as controlValuesToComponentBindingsById, pr as useTimeUtils, pt as bioTemplatePresetControlValuesToOptions, q as toReagentColumns, qn as DateTimePicker_default, qt as validatePlateMapData, r as bioTemplateControlsToSidebarPanels, rn as searchBioTemplatePresets, rr as formatDuration, rt as toSampleRows, s as listBioTemplateControlSchemas, sn as searchBioTemplatePacks, sr as generateTimeSlots, st as toTimeCourseRows, t as bioTemplateControlsToFormSchema, tn as getBioTemplatePresetInfo, tr as findAvailableSlots, tt as toSamplePrepDataFrame, u as getBioTemplateComponentBindings, un as listBioTemplateCatalog, ur as rangesOverlap, ut as toWellPlateWells, v as toBioTemplateComponentPropsById, vn as controlsToSettingsSchema, vr as useSelectionLimit, vt as createCalibrationCurveTemplate, w as toAssayMatrixSampleOptions, wn as defineControlModel, wr as BaseSelect_default, wt as createInstrumentRunTemplate, x as toAssayMatrixColumns, xn as controlsToViewIds, xr as BaseRadioGroup_default, xt as createElisaAssayCollection, y as toBioTemplateComponentSnippets, yn as controlsToSidebarPanels, yr as useEventListener, yt as createDefaultBioTemplate, z as toProtocolColumns, zn as UnitInput_default, zt as ensureTemplateFromCollection } from "./templates-BSlxwV2c.js";
3
+ import { $ as useTheme, A as useAutoGroup, B as DATE_PRESET_OPTIONS, C as useSampleGroups, D as DEFAULT_COLORS, E as hslToHex, F as usePlatformContext, G as datePresetToISO, H as EXPERIMENT_STATUS_OPTIONS, I as useExperimentSelector, J as getExperimentStatusVariant, K as formatExperimentDate, L as useRequestSyncState, M as useDoseCalculator, N as APP_EXPERIMENT_KEY, O as extractSamplesFromDesignData, P as useAppExperiment, Q as useForm, R as useDebouncedWatch, S as useExpansionSet, T as hexToHsl, U as EXPERIMENT_STATUS_VARIANT_MAP, V as EXPERIMENT_STATUS_LABELS, W as SORT_OPTIONS, X as evaluateCondition, Y as resolveExperimentCode, Z as useFormBuilder, _ as useExperimentSave, a as generateDilutionSeries, at as useSortedItems, c as useRackEditor, d as useBioTemplateWorkspace, et as useToast, f as getBioTemplateComponentProps, g as useTemplateCollection, h as useBioTemplateControls, i as DEFAULT_UNITS, it as compareSortValues, j as useWellPlateEditor, l as useBioTemplatePresetWorkspace, m as useBioTemplateComponents, n as useProtocolTemplates, nt as normalizeSearchQuery, o as useReagentSeries, p as toBioTemplateComponentPropsByComponent, q as formatExperimentStatus, r as DEFAULT_PRESETS, rt as useTextSearch, s as useGroupAssignment, t as useExperimentData, tt as candidateMatchesSearch, u as useBioTemplatePackWorkspace, w as deriveShade, x as useScheduleDrag, z as useApi } from "./useExperimentData-BbbdI5xT.js";
4
+ import { n as colorPalettes, r as useSettingsStore, t as useAuthStore } from "./auth-CBG3bWEc.js";
5
5
  import { MINTSdk } from "./install.js";
6
- import { a as useCurrentExperiment, c as usePluginConfig, d as usePasskey, f as useAuth, i as resolvePluginBaseUrl, l as useAsync, n as createPluginClient, o as usePluginClient, r as getPluginPageSelectorItems, s as usePluginSettings, t as buildPluginEndpointUrl, u as useAsyncBatch } from "./composables-BcgZ6diz.js";
6
+ import { a as useCurrentExperiment, c as usePluginConfig, d as usePasskey, f as useAuth, i as resolvePluginBaseUrl, l as useAsync, n as createPluginClient, o as usePluginClient, r as getPluginPageSelectorItems, s as usePluginSettings, t as buildPluginEndpointUrl, u as useAsyncBatch } from "./composables-D4Myb30a.js";
7
7
  import "./stores/index.js";
8
8
  export { APP_EXPERIMENT_KEY, ATOMIC_WEIGHTS, AlertBox_default as AlertBox, AppAvatarMenu_default as AppAvatarMenu, AppContainer_default as AppContainer, AppLayout_default as AppLayout, AppPluginSwitcher_default as AppPluginSwitcher, AppSidebar_default as AppSidebar, AppToastContainer_default as AppToastContainer, AppTopBar_default as AppTopBar, AuditTrail_default as AuditTrail, AutoGroupModal_default as AutoGroupModal, Avatar_default as Avatar, BaseButton_default as BaseButton, BaseCheckbox_default as BaseCheckbox, BaseInput_default as BaseInput, BaseModal_default as BaseModal, BasePill_default as BasePill, BaseRadioGroup_default as BaseRadioGroup, BaseSelect_default as BaseSelect, BaseSlider_default as BaseSlider, BaseTabs_default as BaseTabs, BaseTextarea_default as BaseTextarea, BaseToggle_default as BaseToggle, BatchProgressList_default as BatchProgressList, BioTemplateExperimentWorkspaceView_default as BioTemplateExperimentWorkspaceView, BioTemplatePackWorkspaceView_default as BioTemplatePackWorkspaceView, BioTemplatePresetWorkspaceView_default as BioTemplatePresetWorkspaceView, BioTemplateRenderer_default as BioTemplateRenderer, Breadcrumb_default as Breadcrumb, Calendar_default as Calendar, ChartContainer_default as ChartContainer, ChemicalFormula_default as ChemicalFormula, CollapsibleCard_default as CollapsibleCard, ColorSlider_default as ColorSlider, ComponentBindingRenderer_default as ComponentBindingRenderer, ConcentrationInput_default as ConcentrationInput, ConfirmDialog_default as ConfirmDialog, ControlWorkspaceView_default as ControlWorkspaceView, DATE_PRESET_OPTIONS, DEFAULT_COLORS, DEFAULT_PRESETS, DEFAULT_UNITS, DataFrame_default as DataFrame, DatePicker_default as DatePicker, DateTimePicker_default as DateTimePicker, Divider_default as Divider, DoseCalculator_default as DoseCalculator, DoseDesignWorkspaceView_default as DoseDesignWorkspaceView, DropdownButton_default as DropdownButton, EXPERIMENT_STATUS_LABELS, EXPERIMENT_STATUS_OPTIONS, EXPERIMENT_STATUS_VARIANT_MAP, EmptyState_default as EmptyState, ExperimentCodeBadge_default as ExperimentCodeBadge, ExperimentDataViewer_default as ExperimentDataViewer, ExperimentPopover_default as ExperimentPopover, ExperimentSelectorModal_default as ExperimentSelectorModal, ExperimentTimeline_default as ExperimentTimeline, FileUploader_default as FileUploader, FitPanel_default as FitPanel, FormActions_default as FormActions, FormBuilder_default as FormBuilder, FormField_default as FormField, FormulaInput_default as FormulaInput, GroupAssigner_default as GroupAssigner, IconButton_default as IconButton, LoadingSpinner_default as LoadingSpinner, MINTSdk, MINTSdk as default, MoleculeInput_default as MoleculeInput, MultiSelect_default as MultiSelect, NumberInput_default as NumberInput, PlateMapEditor_default as PlateMapEditor, PluginIcon_default as PluginIcon, PluginWorkspaceView_default as PluginWorkspaceView, ProgressBar_default as ProgressBar, ProtocolStepEditor_default as ProtocolStepEditor, RackEditor_default as RackEditor, ReagentEditor_default as ReagentEditor, ReagentList_default as ReagentList, ResourceCard_default as ResourceCard, SORT_OPTIONS, SampleHierarchyTree_default as SampleHierarchyTree, SampleLegend_default as SampleLegend, SampleSelector_default as SampleSelector, ScheduleCalendar_default as ScheduleCalendar, ScientificNumber_default as ScientificNumber, SegmentedControl_default as SegmentedControl, SequenceInput_default as SequenceInput, SettingsModal_default as SettingsModal, Skeleton_default as Skeleton, StatusIndicator_default as StatusIndicator, StepWizard_default as StepWizard, TEMPLATE_COLLECTION_KEY, TagsInput_default as TagsInput, ThemeToggle_default as ThemeToggle, TimePicker_default as TimePicker, TimeRangeInput_default as TimeRangeInput, Tooltip_default as Tooltip, UnitInput_default as UnitInput, WellPlate_default as WellPlate, addMinutes, assertTemplateEnvelope, bioTemplateCatalog, bioTemplateControlsToFormSchema, bioTemplateControlsToSectionFormSchemas, bioTemplateControlsToSidebarPanels, bioTemplatePacks, bioTemplatePresetControlValuesToOptions, bioTemplatePresets, buildPluginEndpointUrl, candidateMatchesSearch, colorPalettes, compareSortValues, compareTime, controlValuesToComponentBindings, controlValuesToComponentBindingsById, controlValuesToComponentProps, controlsToFormSchema, controlsToSectionFormSchema, controlsToSectionFormSchemas, controlsToSettingsSchema, controlsToSidebarPanels, controlsToTopBarSettingsConfig, controlsToViewIds, controlsToViewItems, createAssayMatrixTemplate, createBioTemplateControlToolkit, createBioTemplatePackCollection, createBioTemplatePresetCollection, createBioTemplatePresetCollectionFromControls, createCalibrationCurveTemplate, createDefaultBioTemplate, createDoseResponseTemplate, createElisaAssayCollection, createFlowCytometryAssayCollection, createFlowCytometryPanelTemplate, createInstrumentRunTemplate, createLcmsBatchCollection, createPlateMapTemplate, createPluginClient, createProtocolStepsTemplate, createQpcrExpressionCollection, createQpcrPlateTemplate, createReagentListTemplate, createSamplePrepTemplate, createSampleSheetTemplate, createTemplateCollection, createTemplateEnvelope, createTimeCourseTemplate, createWellPlateScreenCollection, createWesternBlotAssayCollection, datePresetToISO, defineControlComponentBindings, defineControlModel, defineControls, defineDoseCalculatorControlProps, defineDoseDesignControlModel, defineWellPlateControlProps, defineWellPlateDoseComponentBindings, defineWellPlateDoseControlProps, deriveShade, durationMinutes, ensureTemplateEnvelope, ensureTemplateFromCollection, evaluateCondition, extractSamplesFromDesignData, extractTemplateCollection, findAvailableSlots, findNearestTimeSlotIndex, formatDuration, formatExperimentDate, formatExperimentStatus, formatTime, formatTimeSlot, fromMinutes, generateDilutionSeries, generateTimeSlots, getBioTemplateComponentBindings, getBioTemplateComponentProps, getBioTemplateControlDefaults, getBioTemplateControlSchema, getBioTemplateInfo, getBioTemplatePackInfo, getBioTemplatePresetInfo, getControlDefaults, getDefaultControlView, getExperimentStatusVariant, getFieldRegistryEntry, getPluginPageSelectorItems, getTemplateData, getTypeDefault, hexToHsl, hslToHex, isTimeInRange, listBioTemplateCatalog, listBioTemplateComponentBindings, listBioTemplateControlSchemas, listBioTemplatePacks, listBioTemplatePresetControlSchemas, listBioTemplatePresets, normalizeSearchQuery, parseTime, rangesOverlap, requireBioTemplateControlSchema, resolveExperimentCode, resolvePluginBaseUrl, searchBioTemplateCatalog, searchBioTemplatePacks, searchBioTemplatePresets, snapToSlot, toAssayMatrixColumns, toAssayMatrixDataFrame, toAssayMatrixRows, toAssayMatrixSampleOptions, toBioTemplateComponentBindings, toBioTemplateComponentBindingsById, toBioTemplateComponentImports, toBioTemplateComponentProps, toBioTemplateComponentPropsByComponent, toBioTemplateComponentPropsById, toBioTemplateComponentSnippets, toBioTemplateComponentUsage, toCalibrationCurveColumns, toCalibrationCurveDataFrame, toCalibrationCurveRows, toDoseConditions, toDoseLayoutState, toFlowPanelColumns, toFlowPanelDataFrame, toFlowPanelRows, toInstrumentRunColumns, toInstrumentRunDataFrame, toInstrumentRunRows, toInstrumentRunScheduleEvents, toInstrumentRunSteps, toMinutes, toPlateMapEditorState, toProtocolColumns, toProtocolDataFrame, toProtocolRows, toProtocolSteps, toQpcrColumns, toQpcrDataFrame, toQpcrRows, toQpcrWellPlateWells, toReagentColumns, toReagentDataFrame, toReagentListItems, toReagentRows, toSampleColumns, toSampleDataFrame, toSampleOptions, toSamplePrepColumns, toSamplePrepDataFrame, toSamplePrepRows, toSampleRows, toTemplateDataFrame, toTimeCourseColumns, toTimeCourseDataFrame, toTimeCourseRows, toTimeCourseSteps, toWellMapArray, toWellPlateWells, useApi, useAppExperiment, useAsync, useAsyncBatch, useAuth, useAuthStore, useAutoGroup, useBioTemplateComponents, useBioTemplateControls, useBioTemplatePackWorkspace, useBioTemplatePresetWorkspace, useBioTemplateWorkspace, useChemicalFormula, useConcentrationUnits, useControlSchema, useControlWorkspace, useCurrentExperiment, useDebouncedWatch, useDoseCalculator, useEventListener, useExpansionSet, useExperimentData, useExperimentSave, useExperimentSelector, useForm, useFormBuilder, useGroupAssignment, useListSelection, usePasskey, usePlatformContext, usePluginClient, usePluginConfig, usePluginSettings, useProtocolTemplates, useRackEditor, useReagentSeries, useRequestSyncState, useSampleGroups, useScheduleDrag, useSelectionLimit, useSequenceUtils, useSettingsStore, useSortedItems, useTemplateCollection, useTextSearch, useTheme, useTimeUtils, useToast, useWellPlateEditor, validateAssayMatrixData, validateCalibrationCurveData, validateDoseResponseData, validateFlowCytometryPanelData, validateInstrumentRunData, validatePlateMapData, validateProtocolStepsData, validateQpcrPlateData, validateReagentListData, validateSamplePrepData, validateSampleSheetData, validateTimeCourseData };
package/dist/install.js CHANGED
@@ -1,5 +1,5 @@
1
- import { t as components_exports } from "./components-DihbSJjU.js";
2
- import "./templates-Cyt0Suwf.js";
1
+ import { t as components_exports } from "./components-5KSfsVqf.js";
2
+ import "./templates-BSlxwV2c.js";
3
3
  //#region src/install.ts
4
4
  /**
5
5
  * Vue plugin that registers all MINT SDK components globally.
@@ -1,2 +1,2 @@
1
- import { n as colorPalettes, r as useSettingsStore, t as useAuthStore } from "../auth-QQj2kkze.js";
1
+ import { n as colorPalettes, r as useSettingsStore, t as useAuthStore } from "../auth-CBG3bWEc.js";
2
2
  export { colorPalettes, useAuthStore, useSettingsStore };
package/dist/styles.css CHANGED
@@ -5084,6 +5084,14 @@ html.dark .mint-settings-modal__option-btn--active {
5084
5084
  margin-bottom: 0 !important;
5085
5085
  padding: 0 !important;
5086
5086
  }
5087
+ .mint-settings-modal__footer {
5088
+ display: flex;
5089
+ flex-direction: column;
5090
+ gap: 0.75rem;
5091
+ margin-top: 1rem;
5092
+ padding-top: 1rem;
5093
+ border-top: 1px solid var(--border-light);
5094
+ }
5087
5095
 
5088
5096
  /* ─────────────────────────────────────────────────────────────────────
5089
5097
  * Schema-driven content grid (used by both layouts)
@@ -26146,6 +26154,14 @@ html.dark .mint-settings-modal__option-btn--active {
26146
26154
  margin-bottom: 0 !important;
26147
26155
  padding: 0 !important;
26148
26156
  }
26157
+ .mint-settings-modal__footer {
26158
+ display: flex;
26159
+ flex-direction: column;
26160
+ gap: 0.75rem;
26161
+ margin-top: 1rem;
26162
+ padding-top: 1rem;
26163
+ border-top: 1px solid var(--border-light);
26164
+ }
26149
26165
  /* ─────────────────────────────────────────────────────────────────────
26150
26166
  * Schema-driven content grid (used by both layouts)
26151
26167
  * Cols come from `--mint-settings-cols` set inline by the component;
@@ -1,2 +1,2 @@
1
- import { $ as toSampleOptions, $t as validateTimeCourseData, A as toFlowPanelColumns, At as createReagentListTemplate, B as toProtocolDataFrame, Bt as extractTemplateCollection, C as toAssayMatrixRows, Ct as createFlowCytometryPanelTemplate, D as toCalibrationCurveRows, Dt as createProtocolStepsTemplate, E as toCalibrationCurveDataFrame, Et as createPlateMapTemplate, F as toInstrumentRunRows, Ft as createTimeCourseTemplate, G as toQpcrRows, Gt as validateFlowCytometryPanelData, H as toProtocolSteps, Ht as validateAssayMatrixData, I as toInstrumentRunScheduleEvents, It as createWellPlateScreenCollection, J as toReagentDataFrame, Jt as validateProtocolStepsData, K as toQpcrWellPlateWells, Kt as validateInstrumentRunData, L as toInstrumentRunSteps, Lt as createWesternBlotAssayCollection, M as toFlowPanelRows, Mt as createSampleSheetTemplate, N as toInstrumentRunColumns, Nt as createTemplateCollection, O as toDoseConditions, Ot as createQpcrExpressionCollection, P as toInstrumentRunDataFrame, Pt as createTemplateEnvelope, Q as toSampleDataFrame, Qt as validateSampleSheetData, R as toPlateMapEditorState, Rt as ensureTemplateEnvelope, S as toAssayMatrixDataFrame, St as createFlowCytometryAssayCollection, T as toCalibrationCurveColumns, Tt as createLcmsBatchCollection, U as toQpcrColumns, Ut as validateCalibrationCurveData, V as toProtocolRows, Vt as getTemplateData, W as toQpcrDataFrame, Wt as validateDoseResponseData, X as toReagentRows, Xt as validateReagentListData, Y as toReagentListItems, Yt as validateQpcrPlateData, Z as toSampleColumns, Zt as validateSamplePrepData, _ as toBioTemplateComponentPropsByComponent, _t as createBioTemplatePresetCollectionFromControls, a as getBioTemplateControlDefaults, an as getBioTemplatePackInfo, at as toTimeCourseColumns, b as toBioTemplateComponentUsage, bt as createDoseResponseTemplate, c as listBioTemplatePresetControlSchemas, cn as bioTemplateCatalog, ct as toTimeCourseSteps, d as getBioTemplateComponentProps, dn as searchBioTemplateCatalog, dt as TEMPLATE_COLLECTION_KEY, en as bioTemplatePresets, et as toSamplePrepColumns, f as listBioTemplateComponentBindings, ft as assertTemplateEnvelope, g as toBioTemplateComponentProps, gt as createBioTemplatePresetCollection, h as toBioTemplateComponentImports, ht as createBioTemplatePackCollection, i as createBioTemplateControlToolkit, in as bioTemplatePacks, it as toTemplateDataFrame, j as toFlowPanelDataFrame, jt as createSamplePrepTemplate, k as toDoseLayoutState, kt as createQpcrPlateTemplate, l as requireBioTemplateControlSchema, ln as getBioTemplateInfo, lt as toWellMapArray, m as toBioTemplateComponentBindingsById, mt as createAssayMatrixTemplate, n as bioTemplateControlsToSectionFormSchemas, nn as listBioTemplatePresets, nt as toSamplePrepRows, o as getBioTemplateControlSchema, on as listBioTemplatePacks, ot as toTimeCourseDataFrame, p as toBioTemplateComponentBindings, pt as bioTemplatePresetControlValuesToOptions, q as toReagentColumns, qt as validatePlateMapData, r as bioTemplateControlsToSidebarPanels, rn as searchBioTemplatePresets, rt as toSampleRows, s as listBioTemplateControlSchemas, sn as searchBioTemplatePacks, st as toTimeCourseRows, t as bioTemplateControlsToFormSchema, tn as getBioTemplatePresetInfo, tt as toSamplePrepDataFrame, u as getBioTemplateComponentBindings, un as listBioTemplateCatalog, ut as toWellPlateWells, v as toBioTemplateComponentPropsById, vt as createCalibrationCurveTemplate, w as toAssayMatrixSampleOptions, wt as createInstrumentRunTemplate, x as toAssayMatrixColumns, xt as createElisaAssayCollection, y as toBioTemplateComponentSnippets, yt as createDefaultBioTemplate, z as toProtocolColumns, zt as ensureTemplateFromCollection } from "../templates-Cyt0Suwf.js";
1
+ import { $ as toSampleOptions, $t as validateTimeCourseData, A as toFlowPanelColumns, At as createReagentListTemplate, B as toProtocolDataFrame, Bt as extractTemplateCollection, C as toAssayMatrixRows, Ct as createFlowCytometryPanelTemplate, D as toCalibrationCurveRows, Dt as createProtocolStepsTemplate, E as toCalibrationCurveDataFrame, Et as createPlateMapTemplate, F as toInstrumentRunRows, Ft as createTimeCourseTemplate, G as toQpcrRows, Gt as validateFlowCytometryPanelData, H as toProtocolSteps, Ht as validateAssayMatrixData, I as toInstrumentRunScheduleEvents, It as createWellPlateScreenCollection, J as toReagentDataFrame, Jt as validateProtocolStepsData, K as toQpcrWellPlateWells, Kt as validateInstrumentRunData, L as toInstrumentRunSteps, Lt as createWesternBlotAssayCollection, M as toFlowPanelRows, Mt as createSampleSheetTemplate, N as toInstrumentRunColumns, Nt as createTemplateCollection, O as toDoseConditions, Ot as createQpcrExpressionCollection, P as toInstrumentRunDataFrame, Pt as createTemplateEnvelope, Q as toSampleDataFrame, Qt as validateSampleSheetData, R as toPlateMapEditorState, Rt as ensureTemplateEnvelope, S as toAssayMatrixDataFrame, St as createFlowCytometryAssayCollection, T as toCalibrationCurveColumns, Tt as createLcmsBatchCollection, U as toQpcrColumns, Ut as validateCalibrationCurveData, V as toProtocolRows, Vt as getTemplateData, W as toQpcrDataFrame, Wt as validateDoseResponseData, X as toReagentRows, Xt as validateReagentListData, Y as toReagentListItems, Yt as validateQpcrPlateData, Z as toSampleColumns, Zt as validateSamplePrepData, _ as toBioTemplateComponentPropsByComponent, _t as createBioTemplatePresetCollectionFromControls, a as getBioTemplateControlDefaults, an as getBioTemplatePackInfo, at as toTimeCourseColumns, b as toBioTemplateComponentUsage, bt as createDoseResponseTemplate, c as listBioTemplatePresetControlSchemas, cn as bioTemplateCatalog, ct as toTimeCourseSteps, d as getBioTemplateComponentProps, dn as searchBioTemplateCatalog, dt as TEMPLATE_COLLECTION_KEY, en as bioTemplatePresets, et as toSamplePrepColumns, f as listBioTemplateComponentBindings, ft as assertTemplateEnvelope, g as toBioTemplateComponentProps, gt as createBioTemplatePresetCollection, h as toBioTemplateComponentImports, ht as createBioTemplatePackCollection, i as createBioTemplateControlToolkit, in as bioTemplatePacks, it as toTemplateDataFrame, j as toFlowPanelDataFrame, jt as createSamplePrepTemplate, k as toDoseLayoutState, kt as createQpcrPlateTemplate, l as requireBioTemplateControlSchema, ln as getBioTemplateInfo, lt as toWellMapArray, m as toBioTemplateComponentBindingsById, mt as createAssayMatrixTemplate, n as bioTemplateControlsToSectionFormSchemas, nn as listBioTemplatePresets, nt as toSamplePrepRows, o as getBioTemplateControlSchema, on as listBioTemplatePacks, ot as toTimeCourseDataFrame, p as toBioTemplateComponentBindings, pt as bioTemplatePresetControlValuesToOptions, q as toReagentColumns, qt as validatePlateMapData, r as bioTemplateControlsToSidebarPanels, rn as searchBioTemplatePresets, rt as toSampleRows, s as listBioTemplateControlSchemas, sn as searchBioTemplatePacks, st as toTimeCourseRows, t as bioTemplateControlsToFormSchema, tn as getBioTemplatePresetInfo, tt as toSamplePrepDataFrame, u as getBioTemplateComponentBindings, un as listBioTemplateCatalog, ut as toWellPlateWells, v as toBioTemplateComponentPropsById, vt as createCalibrationCurveTemplate, w as toAssayMatrixSampleOptions, wt as createInstrumentRunTemplate, x as toAssayMatrixColumns, xt as createElisaAssayCollection, y as toBioTemplateComponentSnippets, yt as createDefaultBioTemplate, z as toProtocolColumns, zt as ensureTemplateFromCollection } from "../templates-BSlxwV2c.js";
2
2
  export { TEMPLATE_COLLECTION_KEY, assertTemplateEnvelope, bioTemplateCatalog, bioTemplateControlsToFormSchema, bioTemplateControlsToSectionFormSchemas, bioTemplateControlsToSidebarPanels, bioTemplatePacks, bioTemplatePresetControlValuesToOptions, bioTemplatePresets, createAssayMatrixTemplate, createBioTemplateControlToolkit, createBioTemplatePackCollection, createBioTemplatePresetCollection, createBioTemplatePresetCollectionFromControls, createCalibrationCurveTemplate, createDefaultBioTemplate, createDoseResponseTemplate, createElisaAssayCollection, createFlowCytometryAssayCollection, createFlowCytometryPanelTemplate, createInstrumentRunTemplate, createLcmsBatchCollection, createPlateMapTemplate, createProtocolStepsTemplate, createQpcrExpressionCollection, createQpcrPlateTemplate, createReagentListTemplate, createSamplePrepTemplate, createSampleSheetTemplate, createTemplateCollection, createTemplateEnvelope, createTimeCourseTemplate, createWellPlateScreenCollection, createWesternBlotAssayCollection, ensureTemplateEnvelope, ensureTemplateFromCollection, extractTemplateCollection, getBioTemplateComponentBindings, getBioTemplateComponentProps, getBioTemplateControlDefaults, getBioTemplateControlSchema, getBioTemplateInfo, getBioTemplatePackInfo, getBioTemplatePresetInfo, getTemplateData, listBioTemplateCatalog, listBioTemplateComponentBindings, listBioTemplateControlSchemas, listBioTemplatePacks, listBioTemplatePresetControlSchemas, listBioTemplatePresets, requireBioTemplateControlSchema, searchBioTemplateCatalog, searchBioTemplatePacks, searchBioTemplatePresets, toAssayMatrixColumns, toAssayMatrixDataFrame, toAssayMatrixRows, toAssayMatrixSampleOptions, toBioTemplateComponentBindings, toBioTemplateComponentBindingsById, toBioTemplateComponentImports, toBioTemplateComponentProps, toBioTemplateComponentPropsByComponent, toBioTemplateComponentPropsById, toBioTemplateComponentSnippets, toBioTemplateComponentUsage, toCalibrationCurveColumns, toCalibrationCurveDataFrame, toCalibrationCurveRows, toDoseConditions, toDoseLayoutState, toFlowPanelColumns, toFlowPanelDataFrame, toFlowPanelRows, toInstrumentRunColumns, toInstrumentRunDataFrame, toInstrumentRunRows, toInstrumentRunScheduleEvents, toInstrumentRunSteps, toPlateMapEditorState, toProtocolColumns, toProtocolDataFrame, toProtocolRows, toProtocolSteps, toQpcrColumns, toQpcrDataFrame, toQpcrRows, toQpcrWellPlateWells, toReagentColumns, toReagentDataFrame, toReagentListItems, toReagentRows, toSampleColumns, toSampleDataFrame, toSampleOptions, toSamplePrepColumns, toSamplePrepDataFrame, toSamplePrepRows, toSampleRows, toTemplateDataFrame, toTimeCourseColumns, toTimeCourseDataFrame, toTimeCourseRows, toTimeCourseSteps, toWellMapArray, toWellPlateWells, validateAssayMatrixData, validateCalibrationCurveData, validateDoseResponseData, validateFlowCytometryPanelData, validateInstrumentRunData, validatePlateMapData, validateProtocolStepsData, validateQpcrPlateData, validateReagentListData, validateSamplePrepData, validateSampleSheetData, validateTimeCourseData };
@@ -1957,10 +1957,12 @@ var NumberInput_default = /* @__PURE__ */ defineComponent({
1957
1957
  const emit = __emit;
1958
1958
  const isSliderMode = computed(() => props.min !== void 0 && props.max !== void 0);
1959
1959
  const sliderPercent = computed(() => {
1960
- if (!isSliderMode.value || props.modelValue === void 0) return 0;
1961
- const range = props.max - props.min;
1960
+ const min = props.min;
1961
+ const max = props.max;
1962
+ if (min === void 0 || max === void 0 || props.modelValue === void 0) return 0;
1963
+ const range = max - min;
1962
1964
  if (range === 0) return 0;
1963
- return (props.modelValue - props.min) / range * 100;
1965
+ return (props.modelValue - min) / range * 100;
1964
1966
  });
1965
1967
  const canDecrement = computed(() => {
1966
1968
  if (props.modelValue === void 0) return true;
@@ -3855,8 +3857,9 @@ var UnitInput_default = /* @__PURE__ */ defineComponent({
3855
3857
  const groups = /* @__PURE__ */ new Map();
3856
3858
  const ungrouped = [];
3857
3859
  for (const u of props.units) if (u.group) {
3858
- if (!groups.has(u.group)) groups.set(u.group, []);
3859
- groups.get(u.group).push(u);
3860
+ const group = groups.get(u.group);
3861
+ if (group) group.push(u);
3862
+ else groups.set(u.group, [u]);
3860
3863
  } else ungrouped.push(u);
3861
3864
  return {
3862
3865
  groups,
@@ -4010,10 +4013,11 @@ var ConcentrationInput_default = /* @__PURE__ */ defineComponent({
4010
4013
  const emit = __emit;
4011
4014
  const { unitCategories, getConversionHint } = useConcentrationUnits();
4012
4015
  const filteredCategories = computed(() => {
4013
- if (!props.allowedUnits || props.allowedUnits.length === 0) return unitCategories.value;
4016
+ const allowedUnits = props.allowedUnits;
4017
+ if (!allowedUnits || allowedUnits.length === 0) return unitCategories.value;
4014
4018
  return unitCategories.value.map((cat) => ({
4015
4019
  label: cat.label,
4016
- units: cat.units.filter((u) => props.allowedUnits.includes(u))
4020
+ units: cat.units.filter((u) => allowedUnits.includes(u))
4017
4021
  })).filter((cat) => cat.units.length > 0);
4018
4022
  });
4019
4023
  const availableUnits = computed(() => {
@@ -9579,4 +9583,4 @@ function capitalize(value) {
9579
9583
  //#endregion
9580
9584
  export { toSampleOptions as $, compareTime as $n, validateTimeCourseData as $t, toFlowPanelColumns as A, defineWellPlateDoseControlProps as An, createReagentListTemplate as At, toProtocolDataFrame as B, useConcentrationUnits as Bn, extractTemplateCollection as Bt, toAssayMatrixRows as C, defineControlComponentBindings as Cn, BaseCheckbox_default as Cr, createFlowCytometryPanelTemplate as Ct, toCalibrationCurveRows as D, defineDoseDesignControlModel as Dn, BaseInput_default as Dr, createProtocolStepsTemplate as Dt, toCalibrationCurveDataFrame as E, defineDoseCalculatorControlProps as En, BaseTextarea_default as Er, createPlateMapTemplate as Et, toInstrumentRunRows as F, useControlWorkspace as Fn, createTimeCourseTemplate as Ft, toQpcrRows as G, ATOMIC_WEIGHTS as Gn, validateFlowCytometryPanelData as Gt, toProtocolSteps as H, SequenceInput_default as Hn, validateAssayMatrixData as Ht, toInstrumentRunScheduleEvents as I, getFieldRegistryEntry as In, createWellPlateScreenCollection as It, toReagentDataFrame as J, FileUploader_default as Jn, validateProtocolStepsData as Jt, toQpcrWellPlateWells as K, useChemicalFormula as Kn, validateInstrumentRunData as Kt, toInstrumentRunSteps as L, getTypeDefault as Ln, createWesternBlotAssayCollection as Lt, toFlowPanelRows as M, getDefaultControlView as Mn, createSampleSheetTemplate as Mt, toInstrumentRunColumns as N, mergeControlWorkspaceOptions as Nn, createTemplateCollection as Nt, toDoseConditions as O, defineWellPlateControlProps as On, createQpcrExpressionCollection as Ot, toInstrumentRunDataFrame as P, useControlSchema as Pn, createTemplateEnvelope as Pt, toSampleDataFrame as Q, addMinutes$1 as Qn, validateSampleSheetData as Qt, toPlateMapEditorState as R, ConcentrationInput_default as Rn, ensureTemplateEnvelope as Rt, toAssayMatrixDataFrame as S, controlsToViewItems as Sn, BaseToggle_default as Sr, createFlowCytometryAssayCollection as St, toCalibrationCurveColumns as T, defineControls as Tn, normalizeOptionInput as Tr, createLcmsBatchCollection as Tt, toQpcrColumns as U, useSequenceUtils as Un, validateCalibrationCurveData as Ut, toProtocolRows as V, MoleculeInput_default as Vn, getTemplateData as Vt, toQpcrDataFrame as W, FormulaInput_default as Wn, validateDoseResponseData as Wt, toReagentRows as X, TagsInput_default as Xn, validateReagentListData as Xt, toReagentListItems as Y, NumberInput_default as Yn, validateQpcrPlateData as Yt, toSampleColumns as Z, TimePicker_default as Zn, validateSamplePrepData as Zt, toBioTemplateComponentPropsByComponent as _, controlsToSectionFormSchemas as _n, useListSelection as _r, createBioTemplatePresetCollectionFromControls as _t, getBioTemplateControlDefaults as a, getBioTemplatePackInfo as an, formatTimeSlot as ar, toTimeCourseColumns as at, toBioTemplateComponentUsage as b, controlsToTopBarSettingsConfig as bn, BaseSlider_default as br, createDoseResponseTemplate as bt, listBioTemplatePresetControlSchemas as c, bioTemplateCatalog as cn, isTimeInRange as cr, toTimeCourseSteps as ct, getBioTemplateComponentProps as d, searchBioTemplateCatalog as dn, snapToSlot as dr, TEMPLATE_COLLECTION_KEY as dt, bioTemplatePresets as en, durationMinutes as er, toSamplePrepColumns as et, listBioTemplateComponentBindings as f, controlValuesToComponentBindings as fn, toMinutes as fr, assertTemplateEnvelope as ft, toBioTemplateComponentProps as g, controlsToSectionFormSchema as gn, MultiSelect_default as gr, createBioTemplatePresetCollection as gt, toBioTemplateComponentImports as h, controlsToFormSchema as hn, useDropdownState as hr, createBioTemplatePackCollection as ht, createBioTemplateControlToolkit as i, bioTemplatePacks as in, formatTime as ir, toTemplateDataFrame as it, toFlowPanelDataFrame as j, getControlDefaults as jn, createSamplePrepTemplate as jt, toDoseLayoutState as k, defineWellPlateDoseComponentBindings as kn, createQpcrPlateTemplate as kt, requireBioTemplateControlSchema as l, getBioTemplateInfo as ln, parseTime as lr, toWellMapArray as lt, toBioTemplateComponentBindingsById as m, controlValuesToComponentProps as mn, DatePicker_default as mr, createAssayMatrixTemplate as mt, bioTemplateControlsToSectionFormSchemas as n, listBioTemplatePresets as nn, findNearestTimeSlotIndex as nr, toSamplePrepRows as nt, getBioTemplateControlSchema as o, listBioTemplatePacks as on, fromMinutes as or, toTimeCourseDataFrame as ot, toBioTemplateComponentBindings as p, controlValuesToComponentBindingsById as pn, useTimeUtils as pr, bioTemplatePresetControlValuesToOptions as pt, toReagentColumns as q, DateTimePicker_default as qn, validatePlateMapData as qt, bioTemplateControlsToSidebarPanels as r, searchBioTemplatePresets as rn, formatDuration as rr, toSampleRows as rt, listBioTemplateControlSchemas as s, searchBioTemplatePacks as sn, generateTimeSlots as sr, toTimeCourseRows as st, bioTemplateControlsToFormSchema as t, getBioTemplatePresetInfo as tn, findAvailableSlots as tr, toSamplePrepDataFrame as tt, getBioTemplateComponentBindings as u, listBioTemplateCatalog as un, rangesOverlap as ur, toWellPlateWells as ut, toBioTemplateComponentPropsById as v, controlsToSettingsSchema as vn, useSelectionLimit as vr, createCalibrationCurveTemplate as vt, toAssayMatrixSampleOptions as w, defineControlModel as wn, BaseSelect_default as wr, createInstrumentRunTemplate as wt, toAssayMatrixColumns as x, controlsToViewIds as xn, BaseRadioGroup_default as xr, createElisaAssayCollection as xt, toBioTemplateComponentSnippets as y, controlsToSidebarPanels as yn, useEventListener as yr, createDefaultBioTemplate as yt, toProtocolColumns as z, UnitInput_default as zn, ensureTemplateFromCollection as zt };
9581
9585
 
9582
- //# sourceMappingURL=templates-Cyt0Suwf.js.map
9586
+ //# sourceMappingURL=templates-BSlxwV2c.js.map