@morscherlab/mint-sdk 1.2.10 → 1.3.0-beta.10

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 (841) hide show
  1. package/README.md +45 -137
  2. package/dist/ResourcePickerListInternal-CFAw4vTZ.js +605 -0
  3. package/dist/ResourcePickerListInternal-CFAw4vTZ.js.map +1 -0
  4. package/dist/__stories__/experiment-helpers.d.ts +12 -1
  5. package/dist/__tests__/components/ScientificNumber.test.d.ts +1 -0
  6. package/dist/__tests__/components/SequenceInput.test.d.ts +1 -0
  7. package/dist/__tests__/components/Skeleton.test.d.ts +1 -0
  8. package/dist/__tests__/components/SmartGroupFieldRecipe.test.d.ts +1 -0
  9. package/dist/__tests__/components/SmartGroupModal.test.d.ts +1 -0
  10. package/dist/__tests__/components/StepWizard.test.d.ts +1 -0
  11. package/dist/__tests__/components/TimeRangeInput.test.d.ts +1 -0
  12. package/dist/__tests__/components/UnitInput.test.d.ts +1 -0
  13. package/dist/__tests__/components/WellPlate.keyboard.test.d.ts +1 -0
  14. package/dist/__tests__/components/WellPlate.sampleTypeIndicator.test.d.ts +1 -0
  15. package/dist/__tests__/composables/useChemicalFormula.test.d.ts +1 -0
  16. package/dist/__tests__/composables/useRackEditor.test.d.ts +1 -0
  17. package/dist/__tests__/composables/useSequenceUtils.test.d.ts +1 -0
  18. package/dist/__tests__/styles/baseControlsRules.test.d.ts +1 -0
  19. package/dist/__tests__/styles/controlRefreshRules.test.d.ts +1 -0
  20. package/dist/__tests__/styles/darkModeRules.test.d.ts +1 -0
  21. package/dist/__tests__/styles/menusShellRefresh.test.d.ts +1 -0
  22. package/dist/__tests__/templates/templatesDeprecation.test.d.ts +1 -0
  23. package/dist/__tests__/utils/plotlyTemplate.test.d.ts +1 -0
  24. package/dist/__tests__/utils/sampleTypeMarker.test.d.ts +1 -0
  25. package/dist/{analysisArtifactTypes-DQ0MjwHZ.js → analysisArtifactTypes-Cy3D0Ipg.js} +1 -1
  26. package/dist/{analysisArtifactTypes-DQ0MjwHZ.js.map → analysisArtifactTypes-Cy3D0Ipg.js.map} +1 -1
  27. package/dist/components/ActionMenu.vue.d.ts +17 -5
  28. package/dist/components/AdductText.vue.d.ts +3 -2
  29. package/dist/components/AlertBox.vue.d.ts +11 -14
  30. package/dist/components/AppAvatarMenu.vue.d.ts +18 -28
  31. package/dist/components/AppContainer.vue.d.ts +7 -11
  32. package/dist/components/AppLayout.vue.d.ts +11 -13
  33. package/dist/components/AppSidebar.vue.d.ts +30 -28
  34. package/dist/components/AppToastContainer.vue.d.ts +2 -1
  35. package/dist/components/AppTopBar.vue.d.ts +45 -44
  36. package/dist/components/ArtifactSaveDialog.vue.d.ts +3 -2
  37. package/dist/components/ArtifactSelectorModal.vue.d.ts +5 -6
  38. package/dist/components/AuditTrail.vue.d.ts +17 -18
  39. package/dist/components/AutoGroupModal.vue.d.ts +4 -3
  40. package/dist/components/Avatar.vue.d.ts +2 -1
  41. package/dist/components/BaseButton.vue.d.ts +11 -13
  42. package/dist/components/BaseCheckbox.vue.d.ts +7 -1
  43. package/dist/components/BaseInput.vue.d.ts +15 -2
  44. package/dist/components/BaseModal.vue.d.ts +20 -23
  45. package/dist/components/BasePill.vue.d.ts +10 -13
  46. package/dist/components/BaseRadioGroup.vue.d.ts +2 -1
  47. package/dist/components/BaseSelect.vue.d.ts +3 -2
  48. package/dist/components/BaseSlider.vue.d.ts +18 -1
  49. package/dist/components/BaseTabs.vue.d.ts +2 -1
  50. package/dist/components/BaseTextarea.vue.d.ts +2 -1
  51. package/dist/components/BaseToggle.vue.d.ts +2 -1
  52. package/dist/components/BatchProgressList.vue.d.ts +22 -26
  53. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +7 -14
  54. package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +6 -13
  55. package/dist/components/BioTemplatePresetWorkspaceView.vue.d.ts +7 -14
  56. package/dist/components/BioTemplateRenderer.vue.d.ts +2 -1
  57. package/dist/components/Breadcrumb.vue.d.ts +13 -16
  58. package/dist/components/Calendar.vue.d.ts +21 -23
  59. package/dist/components/ChartContainer.vue.d.ts +19 -25
  60. package/dist/components/ChemicalFormula.vue.d.ts +7 -11
  61. package/dist/components/CollapsibleCard.vue.d.ts +7 -11
  62. package/dist/components/ComponentBindingRenderer.vue.d.ts +2 -1
  63. package/dist/components/ConcentrationInput.vue.d.ts +2 -1
  64. package/dist/components/ConfirmDialog.vue.d.ts +10 -13
  65. package/dist/components/ControlWorkspaceView.vue.d.ts +9 -18
  66. package/dist/components/DataFrame.vue.d.ts +27 -24
  67. package/dist/components/DatePicker.vue.d.ts +3 -6
  68. package/dist/components/DateTimePicker.vue.d.ts +3 -6
  69. package/dist/components/Divider.vue.d.ts +3 -2
  70. package/dist/components/DoseCalculator.vue.d.ts +2 -1
  71. package/dist/components/DoseCalculatorVolumeField.vue.d.ts +2 -1
  72. package/dist/components/DoseDesignWorkspaceView.vue.d.ts +9 -18
  73. package/dist/components/EmptyState.vue.d.ts +9 -12
  74. package/dist/components/ExperimentCodeBadge.vue.d.ts +2 -1
  75. package/dist/components/ExperimentDataViewer.vue.d.ts +13 -15
  76. package/dist/components/ExperimentPopover.vue.d.ts +3 -5
  77. package/dist/components/ExperimentSelectorModal.vue.d.ts +3 -4
  78. package/dist/components/ExperimentTimeline.vue.d.ts +13 -15
  79. package/dist/components/FilePicker.vue.d.ts +10 -5
  80. package/dist/components/FileUploader.vue.d.ts +8 -16
  81. package/dist/components/FitPanel.vue.d.ts +26 -16
  82. package/dist/components/FormActions.vue.d.ts +2 -1
  83. package/dist/components/FormBuilder.vue.d.ts +54 -250
  84. package/dist/components/FormField.vue.d.ts +14 -16
  85. package/dist/components/FormulaInput.vue.d.ts +2 -1
  86. package/dist/components/GeneratedPathInput.vue.d.ts +6 -5
  87. package/dist/components/GeneratedPlotlyResult.vue.d.ts +2 -1
  88. package/dist/components/GeneratedPluginView.vue.d.ts +2 -1
  89. package/dist/components/GeneratedResultRenderer.vue.d.ts +2 -1
  90. package/dist/components/GroupAssigner.vue.d.ts +2 -1
  91. package/dist/components/IconButton.vue.d.ts +7 -11
  92. package/dist/components/InstrumentStateBadge.vue.d.ts +4 -3
  93. package/dist/components/JobsStatusTray.vue.d.ts +13 -29
  94. package/dist/components/LayoutResizeHandle.vue.d.ts +2 -1
  95. package/dist/components/LoadingSpinner.vue.d.ts +2 -1
  96. package/dist/components/MobileSupportGate.vue.d.ts +5 -13
  97. package/dist/components/MoleculeInput.vue.d.ts +3 -4
  98. package/dist/components/MultiSelect.vue.d.ts +2 -1
  99. package/dist/components/NumberInput.vue.d.ts +2 -1
  100. package/dist/components/PlateEditor.vue.d.ts +147 -0
  101. package/dist/components/PlotlyChart.vue.d.ts +21 -31
  102. package/dist/components/PluginIcon.vue.d.ts +2 -1
  103. package/dist/components/PluginWorkspaceView.props.d.ts +2 -6
  104. package/dist/components/PluginWorkspaceView.vue.d.ts +23 -51
  105. package/dist/components/ProgressBar.vue.d.ts +4 -9
  106. package/dist/components/ProtocolStepEditor.vue.d.ts +7 -5
  107. package/dist/components/ProtocolStepParameterField.vue.d.ts +2 -1
  108. package/dist/components/ReagentEditor.vue.d.ts +2 -1
  109. package/dist/components/ReagentList.presentation.d.ts +2 -1
  110. package/dist/components/ReagentList.vue.d.ts +15 -17
  111. package/dist/components/ResourceCard.vue.d.ts +18 -17
  112. package/dist/components/RuntimeUpdateNotice.vue.d.ts +3 -2
  113. package/dist/components/SampleHierarchyTree.vue.d.ts +21 -15
  114. package/dist/components/SampleLegend.vue.d.ts +2 -1
  115. package/dist/components/SampleSelector.vue.d.ts +15 -21
  116. package/dist/components/SampleSelectorGroupNode.vue.d.ts +2 -1
  117. package/dist/components/SampleSelectorSampleRow.vue.d.ts +2 -5
  118. package/dist/components/ScheduleCalendar.vue.d.ts +34 -36
  119. package/dist/components/ScientificNumber.vue.d.ts +3 -2
  120. package/dist/components/SearchableSelect.vue.d.ts +14 -32
  121. package/dist/components/SecretInput.vue.d.ts +2 -1
  122. package/dist/components/SectionCard.vue.d.ts +13 -14
  123. package/dist/components/SegmentedControl.vue.d.ts +2 -1
  124. package/dist/components/SequenceInput.vue.d.ts +10 -21
  125. package/dist/components/SequenceProgressBar.vue.d.ts +2 -1
  126. package/dist/components/SettingsModal.schema.d.ts +1 -1
  127. package/dist/components/SettingsModal.vue.d.ts +28 -25
  128. package/dist/components/Skeleton.vue.d.ts +2 -1
  129. package/dist/components/SmartGroup.types.d.ts +2 -2
  130. package/dist/components/SmartGroupFieldRecipe.groups.d.ts +2 -0
  131. package/dist/components/SmartGroupFieldRecipe.vue.d.ts +4 -7
  132. package/dist/components/SmartGroupManual.vue.d.ts +3 -2
  133. package/dist/components/SmartGroupModal.vue.d.ts +6 -4
  134. package/dist/components/StatusIndicator.vue.d.ts +2 -1
  135. package/dist/components/StepWizard.vue.d.ts +26 -27
  136. package/dist/components/TagsInput.vue.d.ts +3 -5
  137. package/dist/components/ThemeToggle.vue.d.ts +2 -1
  138. package/dist/components/TimePicker.vue.d.ts +6 -6
  139. package/dist/components/TimeRangeInput.vue.d.ts +9 -2
  140. package/dist/components/Tooltip.vue.d.ts +8 -18
  141. package/dist/components/UnitInput.vue.d.ts +2 -1
  142. package/dist/components/WellPlate.colors.d.ts +2 -4
  143. package/dist/components/WellPlate.rendering.d.ts +7 -1
  144. package/dist/components/WellPlate.sampleDrop.d.ts +2 -0
  145. package/dist/components/WellPlate.vue.d.ts +22 -39
  146. package/dist/components/index.d.ts +34 -31
  147. package/dist/components/index.js +5 -8
  148. package/dist/components/internal/ActionItemInternal.vue.d.ts +11 -13
  149. package/dist/components/internal/AppTopBarPageSelectorInternal.vue.d.ts +14 -23
  150. package/dist/components/internal/AppTopBarPillNavInternal.vue.d.ts +2 -1
  151. package/dist/components/internal/CalendarGridPanelInternal.vue.d.ts +2 -1
  152. package/dist/components/internal/DataStateInternal.vue.d.ts +7 -11
  153. package/dist/components/internal/{FilePickerTreeInternal.vue.d.ts → FilePickerListInternal.vue.d.ts} +9 -6
  154. package/dist/components/internal/FilePickerPreviewInternal.vue.d.ts +4 -3
  155. package/dist/components/internal/FormFieldRendererInternal.vue.d.ts +16 -18
  156. package/dist/components/internal/FormSectionRenderer.vue.d.ts +36 -32
  157. package/dist/components/internal/PlateEditorBindingInternal.vue.d.ts +8 -0
  158. package/dist/components/internal/ResourcePickerListInternal.vue.d.ts +65 -0
  159. package/dist/components/internal/SettingsPaneHeadInternal.vue.d.ts +15 -0
  160. package/dist/components/internal/WellEditPopupInternal.vue.d.ts +12 -4
  161. package/dist/components/internal/WorkspaceControlsInternal.vue.d.ts +6 -13
  162. package/dist/components/internal/WorkspaceShellInternal.vue.d.ts +19 -42
  163. package/dist/components/internal/WorkspaceSidebarInternal.vue.d.ts +9 -19
  164. package/dist/components/internal/filePickerPresentation.d.ts +8 -0
  165. package/dist/components/internal/generatedJobsApi.d.ts +9 -0
  166. package/dist/{components-Bbg_gtzF.js → components-GDDvof8D.js} +7088 -5955
  167. package/dist/components-GDDvof8D.js.map +1 -0
  168. package/dist/composables/controlSchemaTypes.d.ts +4 -43
  169. package/dist/composables/createFilePickerAdapter.d.ts +5 -1
  170. package/dist/composables/filePicker/usePickerNavigation.d.ts +7 -2
  171. package/dist/composables/index.d.ts +2 -13
  172. package/dist/composables/index.js +7 -11
  173. package/dist/composables/pluginWorkspaceModel.d.ts +1 -3
  174. package/dist/composables/useAutoGroup.d.ts +10 -0
  175. package/dist/composables/useBioTemplateComponents.d.ts +15 -3
  176. package/dist/composables/useBioTemplateControls.d.ts +5 -1
  177. package/dist/composables/useBioTemplatePackWorkspace.d.ts +2 -0
  178. package/dist/composables/useBioTemplatePresetWorkspace.d.ts +5 -1
  179. package/dist/composables/useBioTemplateWorkspace.d.ts +5 -1
  180. package/dist/composables/useChemicalFormula.d.ts +2 -1
  181. package/dist/composables/useDropdownState.d.ts +5 -0
  182. package/dist/composables/useExperimentSave.d.ts +5 -1
  183. package/dist/composables/useFileBrowser.d.ts +3 -2
  184. package/dist/composables/useFilePicker.d.ts +25 -13
  185. package/dist/composables/useFocusTrap.d.ts +2 -1
  186. package/dist/composables/useGroupAssignment.d.ts +5 -1
  187. package/dist/composables/useJobsStatusTray.d.ts +5 -5
  188. package/dist/composables/useMenuKeyboard.d.ts +3 -3
  189. package/dist/composables/usePlatformContext.d.ts +3 -0
  190. package/dist/composables/usePluginClient.d.ts +1 -1
  191. package/dist/composables/useRackEditor.d.ts +36 -1
  192. package/dist/composables/useSequenceUtils.d.ts +8 -0
  193. package/dist/composables/useTemplateCollection.d.ts +5 -1
  194. package/dist/composables/workspaceRecipes.d.ts +1 -0
  195. package/dist/composables-D6uqp0UF.js +591 -0
  196. package/dist/composables-D6uqp0UF.js.map +1 -0
  197. package/dist/{errors-BLuqMee5.js → experiment-DgllIc3I.js} +89 -333
  198. package/dist/experiment-DgllIc3I.js.map +1 -0
  199. package/dist/{experiment-utils-CV2Wza3z.js → experiment-utils-DTANyVKV.js} +68 -37
  200. package/dist/experiment-utils-DTANyVKV.js.map +1 -0
  201. package/dist/index.d.ts +2 -0
  202. package/dist/index.js +122 -17
  203. package/dist/index.js.map +1 -1
  204. package/dist/install.js +1 -2
  205. package/dist/install.js.map +1 -1
  206. package/dist/instrument.d.ts +6 -0
  207. package/dist/lcms.d.ts +11 -0
  208. package/dist/permissions.d.ts +2 -47
  209. package/dist/permissions.js +2 -26
  210. package/dist/permissions.js.map +1 -1
  211. package/dist/settings-BLr8lzw6.js +341 -0
  212. package/dist/settings-BLr8lzw6.js.map +1 -0
  213. package/dist/stores/auth.d.ts +2 -150
  214. package/dist/stores/experiment.d.ts +2 -106
  215. package/dist/stores/index.js +2 -2
  216. package/dist/stores/settings.d.ts +2 -44
  217. package/dist/styles.css +8608 -6950
  218. package/dist/tailwind.preset.js.map +1 -1
  219. package/dist/templates/adapters.d.ts +3 -0
  220. package/dist/templates/assayMatrixAdapters.d.ts +8 -1
  221. package/dist/templates/assayMatrixBuilder.d.ts +1 -0
  222. package/dist/templates/builderPresetControls.d.ts +1 -0
  223. package/dist/templates/calibrationCurveAdapters.d.ts +3 -0
  224. package/dist/templates/calibrationCurveBuilder.d.ts +1 -0
  225. package/dist/templates/catalog.d.ts +6 -2
  226. package/dist/templates/componentBindings.d.ts +31 -5
  227. package/dist/templates/componentPlateHelpers.d.ts +7 -1
  228. package/dist/templates/controlSchemas.d.ts +9 -0
  229. package/dist/templates/defaultBioTemplateBuilder.d.ts +1 -0
  230. package/dist/templates/doseResponseAdapters.d.ts +2 -0
  231. package/dist/templates/doseResponseBuilder.d.ts +1 -0
  232. package/dist/templates/elisaAssayCollectionBuilder.d.ts +1 -0
  233. package/dist/templates/flowCytometryAssayCollectionBuilder.d.ts +1 -0
  234. package/dist/templates/flowCytometryPanelBuilder.d.ts +1 -0
  235. package/dist/templates/flowPanelAdapters.d.ts +3 -0
  236. package/dist/templates/index.d.ts +1 -1
  237. package/dist/templates/index.js +2 -3
  238. package/dist/templates/instrumentRunAdapters.d.ts +13 -2
  239. package/dist/templates/instrumentRunBuilder.d.ts +1 -0
  240. package/dist/templates/lcmsBatchCollectionBuilder.d.ts +1 -0
  241. package/dist/templates/packs.d.ts +4 -0
  242. package/dist/templates/plateMapAdapters.d.ts +2 -0
  243. package/dist/templates/plateMapBuilder.d.ts +1 -0
  244. package/dist/templates/presets.d.ts +6 -2
  245. package/dist/templates/protocolAdapters.d.ts +4 -0
  246. package/dist/templates/protocolStepsBuilder.d.ts +1 -0
  247. package/dist/templates/qpcrAdapters.d.ts +4 -0
  248. package/dist/templates/qpcrExpressionCollectionBuilder.d.ts +1 -0
  249. package/dist/templates/qpcrPlateBuilder.d.ts +1 -0
  250. package/dist/templates/reagentAdapters.d.ts +4 -0
  251. package/dist/templates/reagentListBuilder.d.ts +1 -0
  252. package/dist/templates/samplePrepAdapters.d.ts +3 -0
  253. package/dist/templates/samplePrepBuilder.d.ts +1 -0
  254. package/dist/templates/sampleSheetAdapters.d.ts +4 -0
  255. package/dist/templates/sampleSheetBuilder.d.ts +1 -0
  256. package/dist/templates/targetedMetabolomicsCollectionBuilder.d.ts +1 -0
  257. package/dist/templates/templateEnvelopes.d.ts +8 -0
  258. package/dist/templates/templatePackCollectionBuilder.d.ts +1 -0
  259. package/dist/templates/templatePresetCollectionBuilder.d.ts +2 -0
  260. package/dist/templates/templateValidators.d.ts +12 -0
  261. package/dist/templates/timeCourseAdapters.d.ts +4 -0
  262. package/dist/templates/timeCourseBuilder.d.ts +1 -0
  263. package/dist/templates/wellPlateScreenCollectionBuilder.d.ts +1 -0
  264. package/dist/templates/westernBlotAssayCollectionBuilder.d.ts +1 -0
  265. package/dist/templates-BU1pet28.js +12304 -0
  266. package/dist/templates-BU1pet28.js.map +1 -0
  267. package/dist/types/auth.d.ts +0 -27
  268. package/dist/types/componentLabTypes.d.ts +14 -7
  269. package/dist/types/componentWorkflowTypes.d.ts +26 -0
  270. package/dist/types/components.d.ts +40 -17
  271. package/dist/types/fileBrowserTypes.d.ts +13 -0
  272. package/dist/types/filePicker.d.ts +19 -1
  273. package/dist/types/generated-ui.d.ts +13 -0
  274. package/dist/types/index.d.ts +4 -4
  275. package/dist/types/index.js +1 -1
  276. package/dist/types/instrument.d.ts +43 -15
  277. package/dist/types/platform.d.ts +2 -0
  278. package/dist/utils/color.d.ts +6 -1
  279. package/dist/utils/formModelSync.d.ts +1 -0
  280. package/dist/utils/plotlyTemplate.d.ts +3 -0
  281. package/dist/utils/rack.d.ts +16 -0
  282. package/dist/utils/sampleTypeMarker.d.ts +23 -0
  283. package/dist/{validation-ChMyixAf.js → validation-Dmp9Q5W0.js} +3032 -838
  284. package/dist/validation-Dmp9Q5W0.js.map +1 -0
  285. package/package.json +17 -23
  286. package/src/__stories__/experiment-helpers.ts +8 -4
  287. package/src/__tests__/components/ActionMenu.test.ts +66 -0
  288. package/src/__tests__/components/AlertBox.test.ts +21 -0
  289. package/src/__tests__/components/AppAvatarMenu.test.ts +25 -0
  290. package/src/__tests__/components/AppTopBar.test.ts +79 -0
  291. package/src/__tests__/components/AppTopBarPageSelector.test.ts +20 -0
  292. package/src/__tests__/components/AppTopBarPillNav.test.ts +74 -0
  293. package/src/__tests__/components/ArtifactSelectorModal.test.ts +20 -6
  294. package/src/__tests__/components/AuditTrail.test.ts +38 -0
  295. package/src/__tests__/components/BaseCheckbox.test.ts +43 -0
  296. package/src/__tests__/components/BaseInput.test.ts +43 -0
  297. package/src/__tests__/components/BaseModal.test.ts +90 -0
  298. package/src/__tests__/components/BaseRadioGroup.test.ts +8 -0
  299. package/src/__tests__/components/BaseSlider.test.ts +116 -0
  300. package/src/__tests__/components/BaseToggle.test.ts +7 -0
  301. package/src/__tests__/components/BatchProgressList.test.ts +22 -14
  302. package/src/__tests__/components/BioTemplateExperimentWorkspaceView.test.ts +4 -4
  303. package/src/__tests__/components/BioTemplatePackWorkspaceView.test.ts +1 -1
  304. package/src/__tests__/components/BioTemplatePresetWorkspaceView.test.ts +8 -8
  305. package/src/__tests__/components/BioTemplateRenderer.test.ts +2 -2
  306. package/src/__tests__/components/Breadcrumb.test.ts +45 -0
  307. package/src/__tests__/components/CalendarGridPanel.test.ts +14 -0
  308. package/src/__tests__/components/ChartContainer.test.ts +23 -0
  309. package/src/__tests__/components/ChemicalFormula.test.ts +15 -0
  310. package/src/__tests__/components/CollapsibleCard.test.ts +12 -9
  311. package/src/__tests__/components/ComponentBindingRenderer.test.ts +45 -17
  312. package/src/__tests__/components/ConcentrationInput.test.ts +17 -2
  313. package/src/__tests__/components/DataFrame.test.ts +107 -4
  314. package/src/__tests__/components/DataStates.test.ts +0 -16
  315. package/src/__tests__/components/DatePicker.test.ts +10 -0
  316. package/src/__tests__/components/DoseCalculator.test.ts +47 -0
  317. package/src/__tests__/components/DoseCalculatorVolumeField.test.ts +37 -34
  318. package/src/__tests__/components/EmptyState.test.ts +16 -1
  319. package/src/__tests__/components/ExperimentSelectorModal.test.ts +31 -3
  320. package/src/__tests__/components/ExperimentTimeline.test.ts +45 -0
  321. package/src/__tests__/components/FilePicker.state.test.ts +2 -2
  322. package/src/__tests__/components/FilePicker.test.ts +360 -13
  323. package/src/__tests__/components/FileUploader.test.ts +15 -0
  324. package/src/__tests__/components/FitPanel.test.ts +96 -0
  325. package/src/__tests__/components/FormulaInput.test.ts +97 -0
  326. package/src/__tests__/components/GeneratedPathInput.test.ts +168 -41
  327. package/src/__tests__/components/GeneratedPlotlyResult.test.ts +1 -1
  328. package/src/__tests__/components/GeneratedResultRenderer.test.ts +47 -2
  329. package/src/__tests__/components/InstrumentStateBadge.test.ts +50 -0
  330. package/src/__tests__/components/JobsStatusTray.test.ts +27 -0
  331. package/src/__tests__/components/MoleculeInput.test.ts +78 -0
  332. package/src/__tests__/components/MultiSelect.test.ts +6 -0
  333. package/src/__tests__/components/PlateEditor.test.ts +1134 -0
  334. package/src/__tests__/components/PlateEditorBindingInternal.test.ts +64 -0
  335. package/src/__tests__/components/PlotlyChart.test.ts +147 -1
  336. package/src/__tests__/components/ProgressBar.test.ts +18 -0
  337. package/src/__tests__/components/ProtocolStepEditor.test.ts +106 -18
  338. package/src/__tests__/components/ProtocolStepParameterField.test.ts +74 -29
  339. package/src/__tests__/components/ReagentList.presentation.test.ts +10 -5
  340. package/src/__tests__/components/ReagentList.test.ts +23 -0
  341. package/src/__tests__/components/ResourcePickerListInternal.test.ts +141 -0
  342. package/src/__tests__/components/RetiredStyles.test.ts +0 -6
  343. package/src/__tests__/components/SampleHierarchyTree.test.ts +102 -0
  344. package/src/__tests__/components/SampleLegend.test.ts +18 -0
  345. package/src/__tests__/components/SampleSelector.test.ts +92 -1
  346. package/src/__tests__/components/SampleSelectorSampleRow.test.ts +5 -2
  347. package/src/__tests__/components/ScheduleCalendar.test.ts +32 -0
  348. package/src/__tests__/components/ScientificNumber.test.ts +113 -0
  349. package/src/__tests__/components/SearchableSelect.test.ts +21 -0
  350. package/src/__tests__/components/SecretInput.test.ts +6 -0
  351. package/src/__tests__/components/SegmentedControl.test.ts +14 -0
  352. package/src/__tests__/components/SequenceInput.test.ts +81 -0
  353. package/src/__tests__/components/SettingsModal.schema.test.ts +5 -5
  354. package/src/__tests__/components/SettingsModal.test.ts +371 -23
  355. package/src/__tests__/components/Skeleton.test.ts +23 -0
  356. package/src/__tests__/components/SmartGroupFieldRecipe.test.ts +12 -0
  357. package/src/__tests__/components/SmartGroupModal.test.ts +69 -0
  358. package/src/__tests__/components/StepWizard.test.ts +75 -0
  359. package/src/__tests__/components/TagsInput.test.ts +16 -0
  360. package/src/__tests__/components/TimePicker.test.ts +66 -0
  361. package/src/__tests__/components/TimeRangeInput.test.ts +83 -0
  362. package/src/__tests__/components/UnitInput.test.ts +147 -0
  363. package/src/__tests__/components/WellEditPopupInternal.test.ts +434 -2
  364. package/src/__tests__/components/WellPlate.decoration.test.ts +18 -0
  365. package/src/__tests__/components/WellPlate.geometry.test.ts +13 -0
  366. package/src/__tests__/components/WellPlate.interaction.test.ts +213 -0
  367. package/src/__tests__/components/WellPlate.keyboard.test.ts +24 -0
  368. package/src/__tests__/components/WellPlate.rendering.test.ts +38 -19
  369. package/src/__tests__/components/WellPlate.sampleDrop.test.ts +19 -0
  370. package/src/__tests__/components/WellPlate.sampleTypeIndicator.test.ts +58 -0
  371. package/src/__tests__/composables/createFilePickerAdapter.test.ts +114 -1
  372. package/src/__tests__/composables/useBioTemplatePackWorkspace.test.ts +1 -1
  373. package/src/__tests__/composables/useBioTemplateWorkspace.test.ts +1 -1
  374. package/src/__tests__/composables/useChemicalFormula.test.ts +60 -0
  375. package/src/__tests__/composables/useControlSchema.test.ts +15 -5
  376. package/src/__tests__/composables/useDropdownState.test.ts +20 -1
  377. package/src/__tests__/composables/useGeneratedAnalysis.test.ts +52 -0
  378. package/src/__tests__/composables/usePickerNavigation.test.ts +61 -1
  379. package/src/__tests__/composables/usePlatformFilePickerAdapter.test.ts +18 -6
  380. package/src/__tests__/composables/useRackEditor.test.ts +270 -0
  381. package/src/__tests__/composables/useSequenceUtils.test.ts +28 -0
  382. package/src/__tests__/docs/frontendDocsCatalog.test.ts +44 -9
  383. package/src/__tests__/styles/baseControlsRules.test.ts +115 -0
  384. package/src/__tests__/styles/controlRefreshRules.test.ts +218 -0
  385. package/src/__tests__/styles/cssTokens.test.ts +7 -5
  386. package/src/__tests__/styles/darkModeRules.test.ts +47 -0
  387. package/src/__tests__/styles/menusShellRefresh.test.ts +75 -0
  388. package/src/__tests__/templates/templates.test.ts +65 -15
  389. package/src/__tests__/templates/templatesDeprecation.test.ts +78 -0
  390. package/src/__tests__/utils/permissions.test.ts +0 -68
  391. package/src/__tests__/utils/plotlyTemplate.test.ts +69 -0
  392. package/src/__tests__/utils/sampleTypeMarker.test.ts +54 -0
  393. package/src/components/ActionMenu.vue +44 -23
  394. package/src/components/AdductText.story.vue +18 -0
  395. package/src/components/AdductText.vue +7 -1
  396. package/src/components/AlertBox.vue +3 -7
  397. package/src/components/AppAvatarMenu.vue +12 -10
  398. package/src/components/AppSidebar.vue +1 -1
  399. package/src/components/AppTopBar.story.vue +47 -4
  400. package/src/components/AppTopBar.vue +16 -22
  401. package/src/components/ArtifactSelectorModal.story.vue +1 -1
  402. package/src/components/ArtifactSelectorModal.vue +48 -160
  403. package/src/components/AuditTrail.vue +26 -12
  404. package/src/components/AutoGroupModal.vue +1 -1
  405. package/src/components/BaseCheckbox.story.vue +58 -2
  406. package/src/components/BaseCheckbox.vue +21 -3
  407. package/src/components/BaseInput.story.vue +51 -0
  408. package/src/components/BaseInput.vue +62 -1
  409. package/src/components/BaseModal.story.vue +55 -0
  410. package/src/components/BaseModal.vue +83 -50
  411. package/src/components/BaseRadioGroup.vue +1 -1
  412. package/src/components/BaseSlider.story.vue +36 -0
  413. package/src/components/BaseSlider.vue +56 -8
  414. package/src/components/BaseToggle.vue +1 -7
  415. package/src/components/BatchProgressList.vue +50 -52
  416. package/src/components/BioTemplatePresetWorkspaceView.vue +1 -1
  417. package/src/components/Breadcrumb.story.vue +1 -1
  418. package/src/components/Breadcrumb.vue +50 -15
  419. package/src/components/ChartContainer.story.vue +42 -0
  420. package/src/components/ChartContainer.vue +22 -1
  421. package/src/components/ChemicalFormula.story.vue +11 -11
  422. package/src/components/ChemicalFormula.vue +1 -0
  423. package/src/components/CollapsibleCard.vue +12 -32
  424. package/src/components/ComponentBindingRenderer.vue +6 -8
  425. package/src/components/ConcentrationInput.story.vue +1 -1
  426. package/src/components/ConcentrationInput.vue +8 -14
  427. package/src/components/ControlWorkspaceView.vue +1 -1
  428. package/src/components/DataFrame.story.vue +45 -2
  429. package/src/components/DataFrame.vue +28 -13
  430. package/src/components/DatePicker.story.vue +11 -0
  431. package/src/components/DatePicker.vue +5 -5
  432. package/src/components/DateTimePicker.story.vue +1 -1
  433. package/src/components/DateTimePicker.vue +8 -5
  434. package/src/components/Divider.story.vue +1 -1
  435. package/src/components/DoseCalculator.vue +44 -51
  436. package/src/components/DoseCalculatorVolumeField.vue +22 -35
  437. package/src/components/DoseDesignWorkspaceView.vue +1 -1
  438. package/src/components/EmptyState.story.vue +4 -4
  439. package/src/components/EmptyState.vue +4 -2
  440. package/src/components/ExperimentPopover.story.vue +2 -2
  441. package/src/components/ExperimentPopover.vue +6 -4
  442. package/src/components/ExperimentSelectorModal.story.vue +2 -1
  443. package/src/components/ExperimentSelectorModal.vue +91 -261
  444. package/src/components/ExperimentTimeline.story.vue +29 -0
  445. package/src/components/ExperimentTimeline.vue +20 -0
  446. package/src/components/FilePicker.story.vue +65 -10
  447. package/src/components/FilePicker.vue +203 -121
  448. package/src/components/FitPanel.story.vue +72 -6
  449. package/src/components/FitPanel.vue +70 -18
  450. package/src/components/FormField.vue +2 -0
  451. package/src/components/FormulaInput.story.vue +6 -0
  452. package/src/components/FormulaInput.vue +49 -70
  453. package/src/components/GeneratedPathInput.story.vue +121 -8
  454. package/src/components/GeneratedPathInput.vue +155 -86
  455. package/src/components/GeneratedPlotlyResult.vue +1 -1
  456. package/src/components/GeneratedPluginView.vue +4 -4
  457. package/src/components/GeneratedResultRenderer.vue +19 -0
  458. package/src/components/InstrumentStateBadge.story.vue +41 -0
  459. package/src/components/InstrumentStateBadge.vue +24 -14
  460. package/src/components/JobsStatusTray.vue +30 -8
  461. package/src/components/MoleculeInput.vue +12 -7
  462. package/src/components/MultiSelect.vue +3 -3
  463. package/src/components/PlateEditor.story.vue +333 -0
  464. package/src/components/PlateEditor.vue +793 -0
  465. package/src/components/PlotlyChart.story.vue +37 -1
  466. package/src/components/PlotlyChart.vue +86 -16
  467. package/src/components/PluginWorkspaceView.props.ts +1 -7
  468. package/src/components/PluginWorkspaceView.vue +0 -4
  469. package/src/components/ProgressBar.story.vue +0 -30
  470. package/src/components/ProgressBar.vue +3 -53
  471. package/src/components/ProtocolStepEditor.story.vue +8 -0
  472. package/src/components/ProtocolStepEditor.vue +55 -145
  473. package/src/components/ProtocolStepParameterField.vue +55 -93
  474. package/src/components/ReagentList.presentation.ts +7 -6
  475. package/src/components/ReagentList.vue +18 -17
  476. package/src/components/SampleHierarchyTree.story.vue +19 -0
  477. package/src/components/SampleHierarchyTree.vue +47 -14
  478. package/src/components/SampleLegend.vue +12 -4
  479. package/src/components/SampleSelector.story.vue +5 -1
  480. package/src/components/SampleSelector.vue +25 -15
  481. package/src/components/SampleSelectorGroupNode.vue +10 -20
  482. package/src/components/SampleSelectorSampleRow.vue +7 -18
  483. package/src/components/ScheduleCalendar.story.vue +14 -0
  484. package/src/components/ScheduleCalendar.vue +13 -1
  485. package/src/components/ScientificNumber.story.vue +15 -2
  486. package/src/components/ScientificNumber.vue +22 -26
  487. package/src/components/SearchableSelect.vue +14 -9
  488. package/src/components/SecretInput.vue +1 -1
  489. package/src/components/SegmentedControl.story.vue +23 -0
  490. package/src/components/SegmentedControl.vue +6 -1
  491. package/src/components/SequenceInput.story.vue +27 -5
  492. package/src/components/SequenceInput.vue +74 -29
  493. package/src/components/SequenceProgressBar.vue +0 -1
  494. package/src/components/SettingsModal.schema.ts +4 -2
  495. package/src/components/SettingsModal.story.vue +83 -22
  496. package/src/components/SettingsModal.vue +338 -79
  497. package/src/components/Skeleton.vue +6 -35
  498. package/src/components/SmartGroup.types.ts +2 -2
  499. package/src/components/SmartGroupFieldRecipe.groups.ts +4 -1
  500. package/src/components/SmartGroupFieldRecipe.vue +14 -8
  501. package/src/components/SmartGroupManual.vue +14 -11
  502. package/src/components/StepWizard.vue +16 -19
  503. package/src/components/TagsInput.story.vue +16 -11
  504. package/src/components/TagsInput.vue +20 -3
  505. package/src/components/TimePicker.story.vue +38 -3
  506. package/src/components/TimePicker.vue +26 -11
  507. package/src/components/TimeRangeInput.story.vue +50 -1
  508. package/src/components/TimeRangeInput.vue +36 -17
  509. package/src/components/UnitInput.story.vue +1 -1
  510. package/src/components/UnitInput.vue +173 -107
  511. package/src/components/WellPlate.colors.ts +7 -6
  512. package/src/components/WellPlate.geometry.ts +3 -0
  513. package/src/components/WellPlate.interaction.ts +39 -5
  514. package/src/components/WellPlate.rendering.ts +21 -27
  515. package/src/components/WellPlate.sampleDrop.ts +20 -12
  516. package/src/components/WellPlate.story.vue +18 -7
  517. package/src/components/WellPlate.vue +28 -22
  518. package/src/components/index.ts +34 -31
  519. package/src/components/internal/AppTopBarPageSelectorInternal.vue +13 -7
  520. package/src/components/internal/AppTopBarPillNavInternal.vue +12 -7
  521. package/src/components/internal/CalendarGridPanelInternal.vue +1 -1
  522. package/src/components/internal/FilePickerListInternal.vue +318 -0
  523. package/src/components/internal/FilePickerPreviewInternal.vue +54 -52
  524. package/src/components/internal/PlateEditorBindingInternal.vue +37 -0
  525. package/src/components/internal/ResourcePickerListInternal.vue +237 -0
  526. package/src/components/internal/SettingsPaneHeadInternal.vue +41 -0
  527. package/src/components/internal/WellEditPopupInternal.vue +219 -146
  528. package/src/components/internal/WorkspaceShellInternal.vue +0 -6
  529. package/src/components/internal/filePickerPresentation.ts +30 -0
  530. package/src/components/internal/generatedJobsApi.ts +12 -0
  531. package/src/composables/autoGroup/index.ts +3 -0
  532. package/src/composables/controlSchemaAdapters.ts +0 -4
  533. package/src/composables/controlSchemaFormFields.ts +0 -4
  534. package/src/composables/controlSchemaTypes.ts +4 -43
  535. package/src/composables/createFilePickerAdapter.ts +90 -22
  536. package/src/composables/filePicker/localFiles.ts +1 -0
  537. package/src/composables/filePicker/usePickerNavigation.ts +29 -2
  538. package/src/composables/index.ts +2 -71
  539. package/src/composables/pluginWorkspaceModel.ts +0 -3
  540. package/src/composables/useAutoGroup.ts +10 -0
  541. package/src/composables/useBioTemplateComponents.ts +15 -3
  542. package/src/composables/useBioTemplateControls.ts +5 -1
  543. package/src/composables/useBioTemplatePackWorkspace.ts +2 -0
  544. package/src/composables/useBioTemplatePresetWorkspace.ts +5 -1
  545. package/src/composables/useBioTemplateWorkspace.ts +5 -1
  546. package/src/composables/useChemicalFormula.ts +39 -13
  547. package/src/composables/useDropdownState.ts +6 -1
  548. package/src/composables/useExperimentSave.ts +5 -1
  549. package/src/composables/useFileBrowser.ts +3 -2
  550. package/src/composables/useFilePicker.ts +98 -13
  551. package/src/composables/useFocusTrap.ts +9 -6
  552. package/src/composables/useGeneratedAnalysis.ts +38 -0
  553. package/src/composables/useGroupAssignment.ts +5 -1
  554. package/src/composables/useJobsStatusTray.ts +5 -5
  555. package/src/composables/useMenuKeyboard.ts +5 -3
  556. package/src/composables/usePlatformFilePickerAdapter.ts +26 -4
  557. package/src/composables/usePluginClient.ts +1 -1
  558. package/src/composables/usePluginWorkspace.ts +0 -1
  559. package/src/composables/useRackEditor.ts +202 -12
  560. package/src/composables/useSequenceUtils.ts +20 -2
  561. package/src/composables/useTemplateCollection.ts +5 -1
  562. package/src/composables/workspaceRecipes.ts +1 -0
  563. package/src/index.ts +9 -3
  564. package/src/instrument.ts +6 -0
  565. package/src/lcms.ts +11 -0
  566. package/src/permissions.ts +2 -59
  567. package/src/styles/components/action-menu.css +9 -38
  568. package/src/styles/components/adduct-text.css +15 -3
  569. package/src/styles/components/alert-box.css +19 -70
  570. package/src/styles/components/app-avatar-menu.css +22 -57
  571. package/src/styles/components/app-container.css +4 -4
  572. package/src/styles/components/app-layout.css +18 -18
  573. package/src/styles/components/app-page-selector.css +50 -105
  574. package/src/styles/components/app-pill-nav.css +24 -42
  575. package/src/styles/components/app-sidebar.css +2 -2
  576. package/src/styles/components/app-top-bar.css +78 -55
  577. package/src/styles/components/artifact-selector-modal.css +1 -211
  578. package/src/styles/components/audit-trail.css +45 -38
  579. package/src/styles/components/avatar.css +1 -1
  580. package/src/styles/components/batch-progress-list.css +179 -95
  581. package/src/styles/components/breadcrumb.css +34 -15
  582. package/src/styles/components/calendar.css +2 -8
  583. package/src/styles/components/chart-container.css +81 -38
  584. package/src/styles/components/checkbox.css +8 -6
  585. package/src/styles/components/chemical-formula.css +8 -6
  586. package/src/styles/components/collapsible-card.css +6 -46
  587. package/src/styles/components/concentration-input.css +1 -10
  588. package/src/styles/components/dataframe.css +48 -67
  589. package/src/styles/components/date-picker.css +124 -62
  590. package/src/styles/components/datetime-picker.css +81 -47
  591. package/src/styles/components/divider.css +2 -4
  592. package/src/styles/components/dose-calculator.css +9 -135
  593. package/src/styles/components/empty-state.css +26 -35
  594. package/src/styles/components/experiment-code-badge.css +1 -1
  595. package/src/styles/components/experiment-popover.css +16 -83
  596. package/src/styles/components/experiment-selector-modal.css +9 -226
  597. package/src/styles/components/experiment-timeline.css +120 -50
  598. package/src/styles/components/file-picker.css +323 -299
  599. package/src/styles/components/file-uploader.css +4 -6
  600. package/src/styles/components/fit-panel.css +108 -22
  601. package/src/styles/components/form-field.css +6 -4
  602. package/src/styles/components/formula-input.css +121 -48
  603. package/src/styles/components/group-assigner.css +1 -3
  604. package/src/styles/components/icon-button.css +15 -37
  605. package/src/styles/components/input.css +91 -2
  606. package/src/styles/components/instrument-monitor.css +37 -447
  607. package/src/styles/components/jobs-status-tray.css +82 -61
  608. package/src/styles/components/loading-spinner.css +18 -0
  609. package/src/styles/components/mobile-support-gate.css +15 -17
  610. package/src/styles/components/modal.css +44 -33
  611. package/src/styles/components/molecule-input.css +31 -16
  612. package/src/styles/components/multi-select.css +33 -29
  613. package/src/styles/components/number-input.css +1 -1
  614. package/src/styles/components/plate-editor.css +497 -0
  615. package/src/styles/components/popover.css +162 -0
  616. package/src/styles/components/progress-bar.css +1 -70
  617. package/src/styles/components/protocol-step-editor.css +21 -333
  618. package/src/styles/components/radio-group.css +9 -18
  619. package/src/styles/components/reagent-editor.css +3 -7
  620. package/src/styles/components/reagent-list.css +63 -69
  621. package/src/styles/components/resource-card.css +4 -6
  622. package/src/styles/components/resource-picker-list.css +212 -0
  623. package/src/styles/components/sample-hierarchy-tree.css +83 -65
  624. package/src/styles/components/sample-legend.css +18 -7
  625. package/src/styles/components/sample-selector.css +41 -68
  626. package/src/styles/components/schedule-calendar.css +59 -54
  627. package/src/styles/components/scientific-number.css +72 -18
  628. package/src/styles/components/searchable-select.css +17 -26
  629. package/src/styles/components/secret-input.css +20 -23
  630. package/src/styles/components/section-card.css +3 -5
  631. package/src/styles/components/segmented-control.css +14 -1
  632. package/src/styles/components/select.css +19 -8
  633. package/src/styles/components/sequence-input.css +85 -37
  634. package/src/styles/components/sequence-progress-bar.css +18 -37
  635. package/src/styles/components/settings-modal.css +683 -34
  636. package/src/styles/components/skeleton.css +24 -23
  637. package/src/styles/components/slider.css +74 -8
  638. package/src/styles/components/smart-group.css +27 -39
  639. package/src/styles/components/status-indicator.css +1 -1
  640. package/src/styles/components/step-wizard.css +62 -99
  641. package/src/styles/components/tabs.css +2 -6
  642. package/src/styles/components/tags-input.css +64 -54
  643. package/src/styles/components/textarea.css +2 -2
  644. package/src/styles/components/time-picker.css +67 -38
  645. package/src/styles/components/time-range-input.css +16 -18
  646. package/src/styles/components/toast.css +1 -1
  647. package/src/styles/components/toggle.css +21 -31
  648. package/src/styles/components/tooltip.css +13 -15
  649. package/src/styles/components/unit-input.css +86 -36
  650. package/src/styles/components/well-edit-popup.css +111 -189
  651. package/src/styles/components/well-plate.css +7 -7
  652. package/src/styles/index.css +5 -7
  653. package/src/styles/variables.css +35 -7
  654. package/src/templates/adapters.ts +4 -0
  655. package/src/templates/assayMatrixAdapters.ts +8 -1
  656. package/src/templates/assayMatrixBuilder.ts +1 -0
  657. package/src/templates/builderPresetControls.ts +1 -0
  658. package/src/templates/calibrationCurveAdapters.ts +3 -0
  659. package/src/templates/calibrationCurveBuilder.ts +1 -0
  660. package/src/templates/catalog.ts +6 -2
  661. package/src/templates/componentBindingCatalog.ts +1 -1
  662. package/src/templates/componentBindings.ts +31 -5
  663. package/src/templates/componentDoseResponseProps.ts +0 -1
  664. package/src/templates/componentPlateHelpers.ts +22 -1
  665. package/src/templates/componentPlateMapProps.ts +7 -5
  666. package/src/templates/componentQpcrPlateProps.ts +0 -1
  667. package/src/templates/controlSchemas.ts +9 -0
  668. package/src/templates/defaultBioTemplateBuilder.ts +1 -0
  669. package/src/templates/doseResponseAdapters.ts +2 -0
  670. package/src/templates/doseResponseBuilder.ts +1 -0
  671. package/src/templates/elisaAssayCollectionBuilder.ts +1 -0
  672. package/src/templates/flowCytometryAssayCollectionBuilder.ts +1 -0
  673. package/src/templates/flowCytometryPanelBuilder.ts +1 -0
  674. package/src/templates/flowPanelAdapters.ts +3 -0
  675. package/src/templates/index.ts +4 -0
  676. package/src/templates/instrumentRunAdapters.ts +13 -2
  677. package/src/templates/instrumentRunBuilder.ts +1 -0
  678. package/src/templates/lcmsBatchCollectionBuilder.ts +1 -0
  679. package/src/templates/packs.ts +4 -0
  680. package/src/templates/plateMapAdapters.ts +2 -0
  681. package/src/templates/plateMapBuilder.ts +1 -0
  682. package/src/templates/presets.ts +6 -2
  683. package/src/templates/protocolAdapters.ts +4 -0
  684. package/src/templates/protocolStepsBuilder.ts +1 -0
  685. package/src/templates/qpcrAdapters.ts +4 -0
  686. package/src/templates/qpcrExpressionCollectionBuilder.ts +1 -0
  687. package/src/templates/qpcrPlateBuilder.ts +1 -0
  688. package/src/templates/reagentAdapters.ts +4 -0
  689. package/src/templates/reagentListBuilder.ts +1 -0
  690. package/src/templates/samplePrepAdapters.ts +3 -0
  691. package/src/templates/samplePrepBuilder.ts +1 -0
  692. package/src/templates/sampleSheetAdapters.ts +4 -0
  693. package/src/templates/sampleSheetBuilder.ts +1 -0
  694. package/src/templates/targetedMetabolomicsCollectionBuilder.ts +1 -0
  695. package/src/templates/templateEnvelopes.ts +8 -0
  696. package/src/templates/templatePackCollectionBuilder.ts +1 -0
  697. package/src/templates/templatePresetCollectionBuilder.ts +2 -0
  698. package/src/templates/templateValidators.ts +12 -0
  699. package/src/templates/timeCourseAdapters.ts +4 -0
  700. package/src/templates/timeCourseBuilder.ts +1 -0
  701. package/src/templates/wellPlateScreenCollectionBuilder.ts +1 -0
  702. package/src/templates/westernBlotAssayCollectionBuilder.ts +1 -0
  703. package/src/types/auth.ts +0 -37
  704. package/src/types/componentLabTypes.ts +16 -13
  705. package/src/types/componentWorkflowTypes.ts +29 -0
  706. package/src/types/components.ts +43 -20
  707. package/src/types/fileBrowserTypes.ts +15 -0
  708. package/src/types/filePicker.ts +19 -1
  709. package/src/types/generated-ui.ts +15 -0
  710. package/src/types/index.ts +12 -11
  711. package/src/types/instrument.ts +47 -15
  712. package/src/types/platform.ts +2 -0
  713. package/src/utils/color.ts +16 -1
  714. package/src/utils/formModelSync.ts +1 -1
  715. package/src/utils/plotlyTemplate.ts +39 -0
  716. package/src/utils/rack.ts +16 -0
  717. package/src/utils/sampleTypeMarker.ts +44 -0
  718. package/dist/ExperimentPopover-CJdNlCZb.js +0 -451
  719. package/dist/ExperimentPopover-CJdNlCZb.js.map +0 -1
  720. package/dist/ExperimentSelectorModal-DHE9k8TP.js +0 -507
  721. package/dist/ExperimentSelectorModal-DHE9k8TP.js.map +0 -1
  722. package/dist/SettingsModal-DELMkUFR.js +0 -492
  723. package/dist/SettingsModal-DELMkUFR.js.map +0 -1
  724. package/dist/Skeleton-jhF9wUkU.js +0 -485
  725. package/dist/Skeleton-jhF9wUkU.js.map +0 -1
  726. package/dist/__tests__/composables/usePluginConfig.test.d.ts +0 -15
  727. package/dist/chunk-BoAXSpZd.js +0 -33
  728. package/dist/components/AppPluginSwitcher.vue.d.ts +0 -40
  729. package/dist/components/ColorSlider.vue.d.ts +0 -34
  730. package/dist/components/DropdownButton.vue.d.ts +0 -49
  731. package/dist/components/FileBrowserModal.vue.d.ts +0 -99
  732. package/dist/components/InstrumentAlertLog.vue.d.ts +0 -22
  733. package/dist/components/InstrumentStatusCard.vue.d.ts +0 -13
  734. package/dist/components/LcmsSequenceTable.vue.d.ts +0 -54
  735. package/dist/components/PlateMapEditor.vue.d.ts +0 -50
  736. package/dist/components/RackEditor.vue.d.ts +0 -78
  737. package/dist/components/fileBrowserPresentation.d.ts +0 -10
  738. package/dist/components/internal/FileBrowserDialogInternal.vue.d.ts +0 -34
  739. package/dist/components/internal/FileBrowserListingInternal.vue.d.ts +0 -25
  740. package/dist/components/internal/FileBrowserToolbarInternal.vue.d.ts +0 -26
  741. package/dist/components/internal/PlateMapEditorToolbarInternal.vue.d.ts +0 -28
  742. package/dist/components-Bbg_gtzF.js.map +0 -1
  743. package/dist/composables/useAsync.d.ts +0 -132
  744. package/dist/composables/useAuth.d.ts +0 -47
  745. package/dist/composables/useCommandHistory.d.ts +0 -33
  746. package/dist/composables/useOptimisticMutation.d.ts +0 -32
  747. package/dist/composables/usePasskey.d.ts +0 -10
  748. package/dist/composables/usePluginConfig.d.ts +0 -27
  749. package/dist/composables/useResourceCrud.d.ts +0 -44
  750. package/dist/composables/useWellPainting.d.ts +0 -39
  751. package/dist/composables/useWellPlateAdapter.d.ts +0 -40
  752. package/dist/composables/useWellPlateEditor.d.ts +0 -33
  753. package/dist/composables/useWellPlateValidation.d.ts +0 -31
  754. package/dist/composables-BCbYb2lA.js +0 -2356
  755. package/dist/composables-BCbYb2lA.js.map +0 -1
  756. package/dist/errors-BLuqMee5.js.map +0 -1
  757. package/dist/experiment-CVDRAnqp.js +0 -90
  758. package/dist/experiment-CVDRAnqp.js.map +0 -1
  759. package/dist/experiment-utils-CV2Wza3z.js.map +0 -1
  760. package/dist/plotly.min-c2Sez60c.js +0 -180634
  761. package/dist/plotly.min-c2Sez60c.js.map +0 -1
  762. package/dist/templates-NuaNHohp.js +0 -5260
  763. package/dist/templates-NuaNHohp.js.map +0 -1
  764. package/dist/useControlSchema-vLiRPUmj.js +0 -7301
  765. package/dist/useControlSchema-vLiRPUmj.js.map +0 -1
  766. package/dist/useDropdownState-C7hRiVQf.js +0 -63
  767. package/dist/useDropdownState-C7hRiVQf.js.map +0 -1
  768. package/dist/useExperimentSelector-G_CAOFTX.js +0 -328
  769. package/dist/useExperimentSelector-G_CAOFTX.js.map +0 -1
  770. package/dist/useFormBuilder-D37ajgS8.js +0 -729
  771. package/dist/useFormBuilder-D37ajgS8.js.map +0 -1
  772. package/dist/validation-ChMyixAf.js.map +0 -1
  773. package/src/__tests__/components/AppPluginSwitcher.test.ts +0 -44
  774. package/src/__tests__/components/DropdownButton.test.ts +0 -628
  775. package/src/__tests__/components/FileBrowserModal.test.ts +0 -460
  776. package/src/__tests__/components/InstrumentAlertLog.test.ts +0 -59
  777. package/src/__tests__/components/LcmsSequenceTable.test.ts +0 -79
  778. package/src/__tests__/components/PlateMapEditorToolbarInternal.test.ts +0 -54
  779. package/src/__tests__/components/RackEditor.test.ts +0 -169
  780. package/src/__tests__/composables/useAuth.test.ts +0 -233
  781. package/src/__tests__/composables/useCommandHistory.test.ts +0 -43
  782. package/src/__tests__/composables/useOptimisticMutation.test.ts +0 -40
  783. package/src/__tests__/composables/usePluginConfig.test.ts +0 -497
  784. package/src/__tests__/composables/useResourceCrud.test.ts +0 -56
  785. package/src/__tests__/composables/useWellPainting.test.ts +0 -52
  786. package/src/__tests__/composables/useWellPlateAdapter.test.ts +0 -50
  787. package/src/__tests__/composables/useWellPlateValidation.test.ts +0 -42
  788. package/src/components/AppPluginSwitcher.story.vue +0 -87
  789. package/src/components/AppPluginSwitcher.vue +0 -134
  790. package/src/components/ColorSlider.story.vue +0 -120
  791. package/src/components/ColorSlider.vue +0 -160
  792. package/src/components/DropdownButton.story.vue +0 -102
  793. package/src/components/DropdownButton.vue +0 -181
  794. package/src/components/FileBrowserModal.story.vue +0 -203
  795. package/src/components/FileBrowserModal.vue +0 -384
  796. package/src/components/InstrumentAlertLog.vue +0 -187
  797. package/src/components/InstrumentStatusCard.vue +0 -184
  798. package/src/components/LcmsSequenceTable.vue +0 -222
  799. package/src/components/PlateMapEditor.story.vue +0 -92
  800. package/src/components/PlateMapEditor.vue +0 -423
  801. package/src/components/RackEditor.story.vue +0 -100
  802. package/src/components/RackEditor.vue +0 -429
  803. package/src/components/fileBrowserPresentation.ts +0 -39
  804. package/src/components/internal/FileBrowserDialogInternal.vue +0 -75
  805. package/src/components/internal/FileBrowserListingInternal.vue +0 -197
  806. package/src/components/internal/FileBrowserToolbarInternal.vue +0 -136
  807. package/src/components/internal/FilePickerTreeInternal.vue +0 -238
  808. package/src/components/internal/PlateMapEditorToolbarInternal.vue +0 -128
  809. package/src/composables/useAsync.ts +0 -327
  810. package/src/composables/useAuth.ts +0 -458
  811. package/src/composables/useCommandHistory.ts +0 -117
  812. package/src/composables/useOptimisticMutation.ts +0 -97
  813. package/src/composables/usePasskey.ts +0 -216
  814. package/src/composables/usePluginConfig.ts +0 -252
  815. package/src/composables/useResourceCrud.ts +0 -235
  816. package/src/composables/useWellPainting.ts +0 -167
  817. package/src/composables/useWellPlateAdapter.ts +0 -151
  818. package/src/composables/useWellPlateEditor.ts +0 -415
  819. package/src/composables/useWellPlateValidation.ts +0 -89
  820. package/src/plotly-dist.d.ts +0 -3
  821. package/src/styles/components/app-plugin-switcher.css +0 -196
  822. package/src/styles/components/color-slider.css +0 -110
  823. package/src/styles/components/dropdown-button.css +0 -280
  824. package/src/styles/components/file-browser-modal.css +0 -489
  825. package/src/styles/components/lcms-sequence-table.css +0 -204
  826. package/src/styles/components/plate-map-editor.css +0 -464
  827. package/src/styles/components/rack-editor.css +0 -287
  828. /package/dist/__tests__/components/{AppPluginSwitcher.test.d.ts → AlertBox.test.d.ts} +0 -0
  829. /package/dist/__tests__/components/{DropdownButton.test.d.ts → AuditTrail.test.d.ts} +0 -0
  830. /package/dist/__tests__/components/{FileBrowserModal.test.d.ts → BaseCheckbox.test.d.ts} +0 -0
  831. /package/dist/__tests__/components/{InstrumentAlertLog.test.d.ts → BaseSlider.test.d.ts} +0 -0
  832. /package/dist/__tests__/components/{LcmsSequenceTable.test.d.ts → DoseCalculator.test.d.ts} +0 -0
  833. /package/dist/__tests__/components/{PlateMapEditorToolbarInternal.test.d.ts → FileUploader.test.d.ts} +0 -0
  834. /package/dist/__tests__/components/{RackEditor.test.d.ts → FitPanel.test.d.ts} +0 -0
  835. /package/dist/__tests__/{composables/useAuth.test.d.ts → components/FormulaInput.test.d.ts} +0 -0
  836. /package/dist/__tests__/{composables/useCommandHistory.test.d.ts → components/InstrumentStateBadge.test.d.ts} +0 -0
  837. /package/dist/__tests__/{composables/useOptimisticMutation.test.d.ts → components/MoleculeInput.test.d.ts} +0 -0
  838. /package/dist/__tests__/{composables/useResourceCrud.test.d.ts → components/PlateEditor.test.d.ts} +0 -0
  839. /package/dist/__tests__/{composables/useWellPainting.test.d.ts → components/PlateEditorBindingInternal.test.d.ts} +0 -0
  840. /package/dist/__tests__/{composables/useWellPlateAdapter.test.d.ts → components/ResourcePickerListInternal.test.d.ts} +0 -0
  841. /package/dist/__tests__/{composables/useWellPlateValidation.test.d.ts → components/SampleLegend.test.d.ts} +0 -0
@@ -1,2356 +0,0 @@
1
- import { a as useAuthStore, d as useSettingsStore, i as useApi, n as AuthenticationRequiredError, r as authenticationRequiredError, t as readErrorMessage } from "./errors-BLuqMee5.js";
2
- import { n as useRequestSyncState } from "./Skeleton-jhF9wUkU.js";
3
- import { B as currentExperimentFromContext, U as resolveCurrentExperimentId, V as getInjectedPlatformContext, et as useEventStream, lt as PLUGIN_SECRET_FORMAT_KEY } from "./useControlSchema-vLiRPUmj.js";
4
- import { n as pickerFormat } from "./validation-ChMyixAf.js";
5
- import { i as usePlatformContext } from "./useFormBuilder-D37ajgS8.js";
6
- import { bn as rowLabel, pn as PLATE_CONFIGS, yn as rowIndex } from "./templates-NuaNHohp.js";
7
- import { computed, getCurrentInstance, getCurrentScope, onBeforeUnmount, onMounted, onScopeDispose, onUnmounted, ref, shallowRef, toValue, watch } from "vue";
8
- import axios from "axios";
9
- //#region src/composables/useAuth.ts
10
- var TOKEN_REFRESH_MARGIN_MS = 300 * 1e3;
11
- var TOKEN_REFRESH_CHECK_INTERVAL_MS = 60 * 1e3;
12
- var _refreshPromise = null;
13
- var _refreshTimerId = null;
14
- var _mountedConsumerCount = 0;
15
- /** Manages authentication state with login/logout/register and automatic JWT token refresh. */
16
- function useAuth() {
17
- const authStore = useAuthStore();
18
- const settingsStore = useSettingsStore();
19
- const isRefreshing = ref(false);
20
- function getApiBaseUrl() {
21
- return settingsStore.getApiBaseUrl();
22
- }
23
- async function fetchAuthConfig() {
24
- try {
25
- const response = await axios.get(`${getApiBaseUrl()}/setup/config/public`);
26
- const config = {
27
- authRequired: response.data.auth_required,
28
- passkeyEnabled: response.data.passkey_enabled,
29
- passkeyRegistered: response.data.passkey_registered ?? false,
30
- registrationEnabled: response.data.registration_enabled ?? false,
31
- databaseMode: response.data.database_mode ?? "none",
32
- experimentVisibilityMode: response.data.access?.experiment_visibility_mode ?? response.data.access?.experimentVisibilityMode ?? "open"
33
- };
34
- authStore.setAuthConfig(config);
35
- return config;
36
- } catch (error) {
37
- console.error("Failed to fetch auth config:", error);
38
- return {
39
- authRequired: false,
40
- passkeyEnabled: false,
41
- passkeyRegistered: false,
42
- registrationEnabled: false,
43
- databaseMode: "none",
44
- experimentVisibilityMode: "open"
45
- };
46
- }
47
- }
48
- async function login(username, password) {
49
- authStore.setLoading(true);
50
- authStore.setError(null);
51
- try {
52
- const response = await axios.post(`${getApiBaseUrl()}/auth/login`, {
53
- username,
54
- password
55
- });
56
- authStore.setToken(response.data.access_token, response.data.expires_in);
57
- authStore.setUsername(username);
58
- await getCurrentUser();
59
- scheduleTokenRefresh();
60
- return true;
61
- } catch (error) {
62
- if (axios.isAxiosError(error) && error.response) authStore.setError(error.response.data.detail || "Login failed");
63
- else authStore.setError("Network error. Please try again.");
64
- return false;
65
- } finally {
66
- authStore.setLoading(false);
67
- }
68
- }
69
- async function register(username, password, email) {
70
- authStore.setLoading(true);
71
- authStore.setError(null);
72
- try {
73
- await axios.post(`${getApiBaseUrl()}/users/register`, {
74
- username,
75
- password,
76
- email
77
- });
78
- return await login(username, password);
79
- } catch (error) {
80
- if (axios.isAxiosError(error) && error.response) authStore.setError(error.response.data.detail || "Registration failed");
81
- else authStore.setError("Network error. Please try again.");
82
- return false;
83
- } finally {
84
- authStore.setLoading(false);
85
- }
86
- }
87
- async function getCurrentUser() {
88
- if (!authStore.token) return null;
89
- try {
90
- const response = await axios.get(`${getApiBaseUrl()}/users/me`, { headers: getAuthHeader() });
91
- const userInfo = {
92
- id: response.data.id,
93
- username: response.data.username,
94
- shortname: response.data.shortname,
95
- email: response.data.email,
96
- role: response.data.role,
97
- roleObj: response.data.role_obj ?? null,
98
- isActive: response.data.is_active
99
- };
100
- authStore.setUserInfo(userInfo);
101
- return userInfo;
102
- } catch {
103
- return null;
104
- }
105
- }
106
- async function verifyToken() {
107
- if (!authStore.token) return false;
108
- try {
109
- const response = await axios.get(`${getApiBaseUrl()}/auth/verify`, { headers: { Authorization: `Bearer ${authStore.token}` } });
110
- if (response.data.valid && response.data.username) {
111
- authStore.setUsername(response.data.username);
112
- return true;
113
- }
114
- authStore.clearToken();
115
- return false;
116
- } catch {
117
- authStore.clearToken();
118
- return false;
119
- }
120
- }
121
- /**
122
- * Refresh the authentication token.
123
- * Called automatically before token expiration.
124
- * Uses promise caching to prevent concurrent refresh requests.
125
- */
126
- async function refreshToken() {
127
- if (!authStore.token) return false;
128
- if (_refreshPromise) return _refreshPromise;
129
- _refreshPromise = (async () => {
130
- isRefreshing.value = true;
131
- try {
132
- const response = await axios.post(`${getApiBaseUrl()}/auth/refresh`, {}, { headers: getAuthHeader() });
133
- authStore.setToken(response.data.access_token, response.data.expires_in);
134
- scheduleTokenRefresh();
135
- return true;
136
- } catch (error) {
137
- if (axios.isAxiosError(error) && error.response?.status === 401) {
138
- console.warn("[Auth] Token refresh failed - session expired");
139
- authStore.clearToken();
140
- stopTokenRefresh();
141
- }
142
- return false;
143
- } finally {
144
- isRefreshing.value = false;
145
- _refreshPromise = null;
146
- }
147
- })();
148
- return _refreshPromise;
149
- }
150
- /**
151
- * Schedule automatic token refresh before expiration.
152
- */
153
- function scheduleTokenRefresh() {
154
- stopTokenRefresh();
155
- if (!authStore.tokenExpires) return;
156
- const refreshAt = authStore.tokenExpires.getTime() - TOKEN_REFRESH_MARGIN_MS;
157
- const now = Date.now();
158
- if (refreshAt <= now) {
159
- refreshToken();
160
- return;
161
- }
162
- const delay = refreshAt - now;
163
- _refreshTimerId = window.setTimeout(() => {
164
- refreshToken();
165
- }, delay);
166
- }
167
- /**
168
- * Stop automatic token refresh.
169
- */
170
- function stopTokenRefresh() {
171
- if (_refreshTimerId !== null) {
172
- window.clearTimeout(_refreshTimerId);
173
- _refreshTimerId = null;
174
- }
175
- }
176
- /**
177
- * Check if token needs refresh and refresh if necessary.
178
- * Called periodically as a safety net.
179
- */
180
- function checkAndRefreshIfNeeded() {
181
- if (!authStore.token || !authStore.tokenExpires) return;
182
- const refreshAt = authStore.tokenExpires.getTime() - TOKEN_REFRESH_MARGIN_MS;
183
- if (Date.now() >= refreshAt) refreshToken();
184
- }
185
- async function initializeAuth() {
186
- authStore.initialize();
187
- await fetchAuthConfig();
188
- if (authStore.token) {
189
- if (await verifyToken()) {
190
- await getCurrentUser();
191
- scheduleTokenRefresh();
192
- }
193
- }
194
- }
195
- function logout() {
196
- stopTokenRefresh();
197
- authStore.logout();
198
- }
199
- function getAuthHeader() {
200
- if (authStore.token) return { Authorization: `Bearer ${authStore.token}` };
201
- return {};
202
- }
203
- async function updateProfile(data) {
204
- if (!authStore.token) return {
205
- success: false,
206
- error: "Not authenticated"
207
- };
208
- try {
209
- const requestData = {};
210
- if (data.email !== void 0) requestData.email = data.email;
211
- if (data.shortname !== void 0) requestData.shortname = data.shortname;
212
- if (data.currentPassword) requestData.current_password = data.currentPassword;
213
- if (data.newPassword) requestData.new_password = data.newPassword;
214
- const response = await axios.put(`${getApiBaseUrl()}/users/me`, requestData, { headers: getAuthHeader() });
215
- const userInfo = {
216
- id: response.data.id,
217
- username: response.data.username,
218
- shortname: response.data.shortname,
219
- email: response.data.email,
220
- role: response.data.role,
221
- roleObj: response.data.role_obj ?? null,
222
- isActive: response.data.is_active
223
- };
224
- authStore.setUserInfo(userInfo);
225
- return { success: true };
226
- } catch (error) {
227
- if (axios.isAxiosError(error) && error.response) return {
228
- success: false,
229
- error: error.response.data.detail || "Update failed"
230
- };
231
- return {
232
- success: false,
233
- error: "Network error. Please try again."
234
- };
235
- }
236
- }
237
- let checkInterval = null;
238
- if (getCurrentInstance()) {
239
- onMounted(() => {
240
- _mountedConsumerCount += 1;
241
- checkInterval = window.setInterval(checkAndRefreshIfNeeded, TOKEN_REFRESH_CHECK_INTERVAL_MS);
242
- });
243
- onUnmounted(() => {
244
- if (checkInterval !== null) {
245
- window.clearInterval(checkInterval);
246
- checkInterval = null;
247
- }
248
- _mountedConsumerCount = Math.max(0, _mountedConsumerCount - 1);
249
- if (_mountedConsumerCount === 0) stopTokenRefresh();
250
- });
251
- watch(() => authStore.tokenExpires, (newExpires) => {
252
- if (newExpires) scheduleTokenRefresh();
253
- else stopTokenRefresh();
254
- });
255
- }
256
- return {
257
- login,
258
- logout,
259
- register,
260
- verifyToken,
261
- fetchAuthConfig,
262
- initializeAuth,
263
- getCurrentUser,
264
- getAuthHeader,
265
- updateProfile,
266
- refreshToken,
267
- isRefreshing
268
- };
269
- }
270
- //#endregion
271
- //#region src/composables/usePasskey.ts
272
- async function loadWebAuthn() {
273
- try {
274
- return await import("@simplewebauthn/browser");
275
- } catch {
276
- throw new Error("@simplewebauthn/browser is required for passkey support. Install it: bun add @simplewebauthn/browser");
277
- }
278
- }
279
- /** Registers and authenticates passkeys using WebAuthn, lazily loading the browser dependency. */
280
- function usePasskey() {
281
- const authStore = useAuthStore();
282
- const settingsStore = useSettingsStore();
283
- function getApiBaseUrl() {
284
- return settingsStore.getApiBaseUrl();
285
- }
286
- async function isSupported() {
287
- try {
288
- const { browserSupportsWebAuthn } = await loadWebAuthn();
289
- return browserSupportsWebAuthn();
290
- } catch {
291
- return false;
292
- }
293
- }
294
- async function registerPasskey(deviceName) {
295
- const webauthn = await loadWebAuthn();
296
- if (!webauthn.browserSupportsWebAuthn()) {
297
- authStore.setError("WebAuthn is not supported in this browser");
298
- return false;
299
- }
300
- authStore.setLoading(true);
301
- authStore.setError(null);
302
- try {
303
- const optionsResponse = await axios.get(`${getApiBaseUrl()}/auth/passkey/register/options`, {
304
- headers: { Authorization: `Bearer ${authStore.token}` },
305
- withCredentials: true
306
- });
307
- const options = JSON.parse(optionsResponse.data.options);
308
- const credential = await webauthn.startRegistration(options);
309
- await axios.post(`${getApiBaseUrl()}/auth/passkey/register/verify`, {
310
- credential: JSON.stringify(credential),
311
- device_name: deviceName
312
- }, {
313
- headers: { Authorization: `Bearer ${authStore.token}` },
314
- withCredentials: true
315
- });
316
- authStore.setAuthConfig({
317
- ...authStore.authConfig,
318
- passkeyRegistered: true
319
- });
320
- return true;
321
- } catch (error) {
322
- if (axios.isAxiosError(error) && error.response) authStore.setError(error.response.data.detail || "Passkey registration failed");
323
- else if (error instanceof Error) if (error.name === "NotAllowedError") authStore.setError("Registration was cancelled or timed out");
324
- else if (error.name === "InvalidStateError") authStore.setError("This authenticator is already registered");
325
- else authStore.setError(error.message);
326
- else authStore.setError("Passkey registration failed");
327
- return false;
328
- } finally {
329
- authStore.setLoading(false);
330
- }
331
- }
332
- async function loginWithPasskey() {
333
- const webauthn = await loadWebAuthn();
334
- if (!webauthn.browserSupportsWebAuthn()) {
335
- authStore.setError("WebAuthn is not supported in this browser");
336
- return false;
337
- }
338
- authStore.setLoading(true);
339
- authStore.setError(null);
340
- try {
341
- const optionsResponse = await axios.get(`${getApiBaseUrl()}/auth/passkey/login/options`, { withCredentials: true });
342
- const options = JSON.parse(optionsResponse.data.options);
343
- const credential = await webauthn.startAuthentication(options);
344
- const response = await axios.post(`${getApiBaseUrl()}/auth/passkey/login/verify`, { credential: JSON.stringify(credential) }, { withCredentials: true });
345
- authStore.setToken(response.data.access_token, response.data.expires_in);
346
- return true;
347
- } catch (error) {
348
- if (axios.isAxiosError(error) && error.response) if (error.response.status === 404) authStore.setError("No passkeys registered. Please login with password first.");
349
- else authStore.setError(error.response.data.detail || "Passkey login failed");
350
- else if (error instanceof Error) if (error.name === "NotAllowedError") authStore.setError("Authentication was cancelled or timed out");
351
- else authStore.setError(error.message);
352
- else authStore.setError("Passkey login failed");
353
- return false;
354
- } finally {
355
- authStore.setLoading(false);
356
- }
357
- }
358
- async function listCredentials() {
359
- try {
360
- return (await axios.get(`${getApiBaseUrl()}/auth/passkey/credentials`, { headers: { Authorization: `Bearer ${authStore.token}` } })).data.credentials;
361
- } catch {
362
- return [];
363
- }
364
- }
365
- async function deleteCredential(credentialId) {
366
- try {
367
- await axios.delete(`${getApiBaseUrl()}/auth/passkey/credentials/${encodeURIComponent(credentialId)}`, { headers: { Authorization: `Bearer ${authStore.token}` } });
368
- if ((await listCredentials()).length === 0) authStore.setAuthConfig({
369
- ...authStore.authConfig,
370
- passkeyRegistered: false
371
- });
372
- return true;
373
- } catch {
374
- return false;
375
- }
376
- }
377
- async function deleteAllCredentials() {
378
- try {
379
- await axios.delete(`${getApiBaseUrl()}/auth/passkey/credentials`, { headers: { Authorization: `Bearer ${authStore.token}` } });
380
- authStore.setAuthConfig({
381
- ...authStore.authConfig,
382
- passkeyRegistered: false
383
- });
384
- return true;
385
- } catch {
386
- return false;
387
- }
388
- }
389
- return {
390
- isSupported,
391
- registerPasskey,
392
- loginWithPasskey,
393
- listCredentials,
394
- deleteCredential,
395
- deleteAllCredentials
396
- };
397
- }
398
- //#endregion
399
- //#region src/composables/pluginFrontendBase.ts
400
- function normalizePluginRoutePrefix(prefix) {
401
- const candidate = prefix?.trim();
402
- if (!candidate) return void 0;
403
- const path = candidate.split("/").filter(Boolean).join("/");
404
- return path ? `/${path}/` : "/";
405
- }
406
- /**
407
- * Resolve the Vue Router base for a plugin frontend.
408
- *
409
- * Integrated plugins use the platform-injected mount prefix. Standalone and
410
- * development builds retain the caller's Vite base unchanged.
411
- */
412
- function resolvePluginFrontendBase(fallbackBase = "/") {
413
- const context = getInjectedPlatformContext();
414
- if (!context?.isIntegrated) return fallbackBase;
415
- return normalizePluginRoutePrefix(context.plugin?.route_prefix) ?? fallbackBase;
416
- }
417
- //#endregion
418
- //#region src/composables/useAsync.ts
419
- /**
420
- * Default error transformer.
421
- */
422
- function defaultTransformError(error) {
423
- if (error instanceof Error) return {
424
- message: error.message,
425
- originalError: error
426
- };
427
- if (typeof error === "object" && error !== null) {
428
- const errorObj = error;
429
- if ("response" in errorObj && errorObj.response) {
430
- const response = errorObj.response;
431
- const data = response.data;
432
- return {
433
- message: data?.detail || data?.message || "Request failed",
434
- code: String(response.status),
435
- details: data,
436
- originalError: error
437
- };
438
- }
439
- return {
440
- message: errorObj.message || "Unknown error",
441
- code: errorObj.code,
442
- details: errorObj,
443
- originalError: error
444
- };
445
- }
446
- return {
447
- message: String(error),
448
- originalError: error
449
- };
450
- }
451
- /**
452
- * Composable for managing async operation state.
453
- *
454
- * Provides standardized loading, error, and success state management
455
- * for any async function.
456
- *
457
- * @param asyncFn - The async function to wrap
458
- * @param options - Configuration options
459
- *
460
- * @example
461
- * ```typescript
462
- * // Basic usage
463
- * const { data, isLoading, error, execute } = useAsync(
464
- * async (id: string) => {
465
- * const response = await api.get(`/users/${id}`)
466
- * return response.data
467
- * }
468
- * )
469
- *
470
- * // Execute the function
471
- * await execute('user-123')
472
- *
473
- * // In template
474
- * <div v-if="isLoading">Loading...</div>
475
- * <div v-else-if="error">{{ error.message }}</div>
476
- * <div v-else-if="data">{{ data.name }}</div>
477
- * ```
478
- *
479
- * @example
480
- * ```typescript
481
- * // With options
482
- * const { data, execute } = useAsync(
483
- * fetchUser,
484
- * {
485
- * immediate: true,
486
- * immediateArgs: ['default-user'],
487
- * onSuccess: (user) => console.log('Fetched:', user.name),
488
- * onError: (error) => toast.error(error.message),
489
- * }
490
- * )
491
- * ```
492
- *
493
- * @example
494
- * ```typescript
495
- * // Form submission
496
- * const { isLoading, error, execute: submit } = useAsync(
497
- * async (formData: FormData) => {
498
- * await api.post('/submit', formData)
499
- * },
500
- * {
501
- * onSuccess: () => {
502
- * toast.success('Submitted!')
503
- * router.push('/success')
504
- * },
505
- * }
506
- * )
507
- *
508
- * const handleSubmit = () => submit(new FormData(formRef.value))
509
- * ```
510
- */
511
- /**
512
- * Wraps an async function with reactive loading, error, and success state plus optional auto-execute.
513
- *
514
- * @deprecated Unused by the MINT platform and scheduled for removal in MINT 1.3; use `useRequestSyncState()` instead.
515
- */
516
- function useAsync(asyncFn, options = {}) {
517
- const { initialData = null, immediate = false, immediateArgs = [], transformError = defaultTransformError, onSuccess, onError, resetOnExecute = false } = options;
518
- const data = ref(initialData);
519
- const error = ref(null);
520
- const state = ref("idle");
521
- const isIdle = computed(() => state.value === "idle");
522
- const isLoading = computed(() => state.value === "loading");
523
- const isSuccess = computed(() => state.value === "success");
524
- const isError = computed(() => state.value === "error");
525
- async function execute(...args) {
526
- state.value = "loading";
527
- error.value = null;
528
- if (resetOnExecute) data.value = null;
529
- try {
530
- const result = await asyncFn(...args);
531
- data.value = result;
532
- state.value = "success";
533
- onSuccess?.(result);
534
- return result;
535
- } catch (e) {
536
- const asyncError = transformError(e);
537
- error.value = asyncError;
538
- state.value = "error";
539
- onError?.(asyncError);
540
- return null;
541
- }
542
- }
543
- function reset() {
544
- data.value = initialData;
545
- error.value = null;
546
- state.value = "idle";
547
- }
548
- function setData(newData) {
549
- data.value = newData;
550
- if (newData !== null) {
551
- state.value = "success";
552
- error.value = null;
553
- }
554
- }
555
- function setError(newError) {
556
- error.value = newError;
557
- if (newError !== null) state.value = "error";
558
- }
559
- if (immediate) execute(...immediateArgs);
560
- return {
561
- data,
562
- error,
563
- state,
564
- isIdle,
565
- isLoading,
566
- isSuccess,
567
- isError,
568
- execute,
569
- reset,
570
- setData,
571
- setError
572
- };
573
- }
574
- /**
575
- * Create a batch of async operations that can be executed in parallel.
576
- *
577
- * @example
578
- * ```typescript
579
- * const { results, isLoading, execute } = useAsyncBatch([
580
- * () => fetchUser(userId),
581
- * () => fetchPosts(userId),
582
- * () => fetchComments(userId),
583
- * ])
584
- *
585
- * await execute()
586
- * // results.value = [user, posts, comments]
587
- * ```
588
- */
589
- function useAsyncBatch(asyncFns) {
590
- const results = ref(asyncFns.map(() => null));
591
- const errors = ref(asyncFns.map(() => null));
592
- const isLoading = ref(false);
593
- async function execute() {
594
- isLoading.value = true;
595
- errors.value = asyncFns.map(() => null);
596
- const promises = asyncFns.map(async (fn, index) => {
597
- try {
598
- const result = await fn();
599
- results.value[index] = result;
600
- } catch (e) {
601
- errors.value[index] = defaultTransformError(e);
602
- results.value[index] = null;
603
- }
604
- });
605
- await Promise.all(promises);
606
- isLoading.value = false;
607
- }
608
- function reset() {
609
- results.value = asyncFns.map(() => null);
610
- errors.value = asyncFns.map(() => null);
611
- isLoading.value = false;
612
- }
613
- return {
614
- results,
615
- errors,
616
- isLoading,
617
- execute,
618
- reset
619
- };
620
- }
621
- //#endregion
622
- //#region src/composables/useManualLayoutResize.ts
623
- function clamp(value, min, max) {
624
- return Math.min(max, Math.max(min, value));
625
- }
626
- function availablePanelWidth(containerWidth, constraints) {
627
- return Math.min(constraints.maxWidth, containerWidth - (constraints.mainMinWidth ?? 0) - (constraints.layoutGap ?? 0));
628
- }
629
- function resizedLeadingPanelWidth(startWidth, deltaX, containerWidth, constraints) {
630
- return Math.round(clamp(startWidth + deltaX, constraints.minWidth, Math.max(constraints.minWidth, availablePanelWidth(containerWidth, constraints))));
631
- }
632
- function resizedTrailingPanelWidth(startWidth, deltaX, containerWidth, constraints) {
633
- return resizedLeadingPanelWidth(startWidth, -deltaX, containerWidth, constraints);
634
- }
635
- function resizedVerticalSplitPercent(startPercent, deltaY, containerHeight, constraints) {
636
- if (containerHeight <= 0) return startPercent;
637
- return Math.round(clamp(startPercent + deltaY / containerHeight * 100, constraints.minPercent, constraints.maxPercent));
638
- }
639
- /** Coordinates pointer-driven resizing for one or more layout splitters. */
640
- function useManualLayoutResize(options) {
641
- const activeResize = shallowRef(null);
642
- const isResizing = computed(() => activeResize.value !== null);
643
- let previousBodyCursor = "";
644
- let previousBodyUserSelect = "";
645
- function stopResize() {
646
- if (!activeResize.value) return;
647
- activeResize.value = null;
648
- window.removeEventListener("pointermove", handlePointerMove);
649
- window.removeEventListener("pointerup", stopResize);
650
- window.removeEventListener("pointercancel", stopResize);
651
- if (typeof document !== "undefined") {
652
- document.body.style.cursor = previousBodyCursor;
653
- document.body.style.userSelect = previousBodyUserSelect;
654
- }
655
- }
656
- function handlePointerMove(event) {
657
- const state = activeResize.value;
658
- if (!state) return;
659
- options.resolveTarget(state.kind)?.onResize({
660
- kind: state.kind,
661
- startValue: state.startValue,
662
- deltaX: event.clientX - state.startClientX,
663
- deltaY: event.clientY - state.startClientY,
664
- containerRect: state.containerRect,
665
- secondaryRect: state.secondaryRect
666
- });
667
- }
668
- function startResize(kind, event) {
669
- if (!toValue(options.enabled) || event.button !== 0) return;
670
- const target = options.resolveTarget(kind);
671
- const containerRect = target?.getContainerElement()?.getBoundingClientRect();
672
- if (!target || !containerRect) return;
673
- event.preventDefault();
674
- if (event.currentTarget instanceof HTMLElement) event.currentTarget.setPointerCapture(event.pointerId);
675
- const secondary = target.getSecondaryElement?.() ?? null;
676
- activeResize.value = {
677
- kind,
678
- startClientX: event.clientX,
679
- startClientY: event.clientY,
680
- startValue: target.getStartValue(),
681
- containerRect,
682
- secondaryRect: secondary?.getBoundingClientRect() ?? null,
683
- cursor: target.cursor ?? "col-resize"
684
- };
685
- if (typeof document !== "undefined") {
686
- previousBodyCursor = document.body.style.cursor;
687
- previousBodyUserSelect = document.body.style.userSelect;
688
- document.body.style.cursor = activeResize.value.cursor;
689
- document.body.style.userSelect = "none";
690
- }
691
- window.addEventListener("pointermove", handlePointerMove);
692
- window.addEventListener("pointerup", stopResize);
693
- window.addEventListener("pointercancel", stopResize);
694
- }
695
- function isResizingKind(kind) {
696
- return activeResize.value?.kind === kind;
697
- }
698
- watch(() => toValue(options.enabled), (enabled) => {
699
- if (!enabled) stopResize();
700
- });
701
- onBeforeUnmount(stopResize);
702
- return {
703
- activeResize,
704
- isResizing,
705
- isResizingKind,
706
- startResize,
707
- stopResize
708
- };
709
- }
710
- //#endregion
711
- //#region src/composables/usePluginConfig.ts
712
- var PLATFORM_OWNED_SETTINGS_KEY$1 = "allowed_experiment_types";
713
- function cloneConfig(config) {
714
- return JSON.parse(JSON.stringify(config));
715
- }
716
- function hasOwnConfig(config, key) {
717
- return Object.prototype.hasOwnProperty.call(config, key);
718
- }
719
- function changedTopLevelKeys(current, saved) {
720
- return new Set([...new Set([...Object.keys(current), ...Object.keys(saved)])].filter((key) => JSON.stringify(current[key]) !== JSON.stringify(saved[key])));
721
- }
722
- function topLevelConfigPatch(current, keys) {
723
- return Object.fromEntries([...keys].map((key) => [key, hasOwnConfig(current, key) ? current[key] : null]));
724
- }
725
- function platformOwnedConfigPatch(config) {
726
- return { [PLATFORM_OWNED_SETTINGS_KEY$1]: hasOwnConfig(config, PLATFORM_OWNED_SETTINGS_KEY$1) ? config[PLATFORM_OWNED_SETTINGS_KEY$1] : null };
727
- }
728
- function responseConfigPayload(response) {
729
- return response.config ?? response.settings ?? {};
730
- }
731
- /**
732
- * @deprecated Unused by the MINT platform and scheduled for removal in MINT 1.3; use `usePluginSettings()` for typed plugin settings.
733
- */
734
- function usePluginConfig(pluginName) {
735
- const injectedContext = getInjectedPlatformContext();
736
- const { plugin } = usePlatformContext();
737
- const platformApi = useApi();
738
- const pluginApi = useApi({ baseUrl: injectedContext?.plugin?.api_prefix ?? plugin.value?.api_prefix });
739
- const resolvedName = computed(() => pluginName ?? plugin.value?.name ?? injectedContext?.plugin?.name ?? "");
740
- const usesPluginSettingsApi = computed(() => {
741
- const currentPlugin = injectedContext?.plugin ?? plugin.value;
742
- return currentPlugin?.settings_api === "plugin" && resolvedName.value === currentPlugin.name;
743
- });
744
- const config = ref({});
745
- const savedConfig = ref({});
746
- const revision = ref(null);
747
- const request = useRequestSyncState("Plugin config request failed.");
748
- const isLoading = ref(false);
749
- const isSaving = ref(false);
750
- const error = request.error;
751
- const lastLoadedAt = request.lastLoadedAt;
752
- const lastSavedAt = request.lastSavedAt;
753
- const isDirty = computed(() => {
754
- return JSON.stringify(config.value) !== JSON.stringify(savedConfig.value);
755
- });
756
- async function load() {
757
- const name = resolvedName.value;
758
- if (!name) return;
759
- isLoading.value = true;
760
- try {
761
- const response = usesPluginSettingsApi.value ? await request.run(() => pluginApi.get("/settings"), {
762
- success: "load",
763
- errorMessage: "Failed to load plugin config"
764
- }) : await request.run(() => platformApi.get(`/plugins/${encodeURIComponent(name)}/config`), {
765
- success: "load",
766
- errorMessage: "Failed to load plugin config"
767
- });
768
- const responseConfig = responseConfigPayload(response);
769
- config.value = cloneConfig(responseConfig);
770
- savedConfig.value = cloneConfig(responseConfig);
771
- revision.value = response.revision ?? null;
772
- } catch {} finally {
773
- isLoading.value = false;
774
- }
775
- }
776
- async function save() {
777
- const name = resolvedName.value;
778
- if (!name) return false;
779
- isSaving.value = true;
780
- try {
781
- const payload = cloneConfig(config.value);
782
- let response;
783
- let nextRevision = revision.value;
784
- if (usesPluginSettingsApi.value) {
785
- response = await request.run(() => pluginApi.put("/settings", payload, revision.value ? { headers: { "If-Match": `"${revision.value}"` } } : void 0), {
786
- success: "save",
787
- errorMessage: "Failed to save plugin config"
788
- });
789
- nextRevision = response.revision ?? nextRevision;
790
- } else {
791
- const dirtyKeys = changedTopLevelKeys(payload, savedConfig.value);
792
- const platformOwnedChanged = dirtyKeys.has(PLATFORM_OWNED_SETTINGS_KEY$1);
793
- const typedKeys = [...dirtyKeys].filter((key) => key !== PLATFORM_OWNED_SETTINGS_KEY$1);
794
- if (typedKeys.length) {
795
- response = await request.run(() => platformApi.patch(`/plugins/${encodeURIComponent(name)}/config`, { config: topLevelConfigPatch(payload, typedKeys) }), {
796
- success: "save",
797
- errorMessage: "Failed to save plugin config"
798
- });
799
- nextRevision = response.revision ?? nextRevision;
800
- }
801
- if (platformOwnedChanged) {
802
- response = await request.run(() => platformApi.patch(`/plugins/${encodeURIComponent(name)}/config`, { config: platformOwnedConfigPatch(payload) }), {
803
- success: "save",
804
- errorMessage: "Failed to save plugin config"
805
- });
806
- nextRevision = response.revision ?? nextRevision;
807
- }
808
- }
809
- if (!response) return true;
810
- const responseConfig = responseConfigPayload(response);
811
- config.value = cloneConfig(responseConfig);
812
- savedConfig.value = cloneConfig(responseConfig);
813
- revision.value = response.revision ?? nextRevision;
814
- return true;
815
- } catch {
816
- return false;
817
- } finally {
818
- isSaving.value = false;
819
- }
820
- }
821
- function reset() {
822
- config.value = cloneConfig(savedConfig.value);
823
- request.clearError();
824
- }
825
- onMounted(() => {
826
- load();
827
- });
828
- return {
829
- config,
830
- isLoading,
831
- isSaving,
832
- error,
833
- lastLoadedAt,
834
- lastSavedAt,
835
- isDirty,
836
- load,
837
- save,
838
- reset
839
- };
840
- }
841
- //#endregion
842
- //#region src/composables/useFileBrowser.ts
843
- /**
844
- * Drives FileBrowserModal against the platform's `/api/filesystem` routes.
845
- *
846
- * Requires the `filesystem.browse` permission and a deployment that declares
847
- * mounts; with none configured the routes are absent and `mounts` stays empty.
848
- */
849
- function useFileBrowser(options = {}) {
850
- const injectedContext = getInjectedPlatformContext();
851
- const api = useApi({ baseUrl: options.apiBaseUrl ?? injectedContext?.platformApiUrl });
852
- const sync = useRequestSyncState("Could not read the server mounts.");
853
- const mounts = ref([]);
854
- const mountId = shallowRef("");
855
- const path = shallowRef("");
856
- const parent = ref(null);
857
- const entries = ref([]);
858
- const breadcrumbs = ref([]);
859
- const totalCount = ref(null);
860
- const matchCount = ref(null);
861
- const shownCount = ref(null);
862
- const truncated = ref(false);
863
- const search = shallowRef("");
864
- const sort = shallowRef("name");
865
- const selected = ref([]);
866
- const mountsLoading = shallowRef(false);
867
- const listingLoading = shallowRef(false);
868
- const listingReady = shallowRef(false);
869
- const isLoading = computed(() => mountsLoading.value || listingLoading.value);
870
- let listToken = 0;
871
- let mountsToken = 0;
872
- let disposed = false;
873
- let listingController;
874
- let mountsController;
875
- let searchTimer;
876
- function resetListing() {
877
- listingReady.value = false;
878
- entries.value = [];
879
- breadcrumbs.value = [];
880
- parent.value = null;
881
- totalCount.value = null;
882
- matchCount.value = null;
883
- shownCount.value = null;
884
- truncated.value = false;
885
- }
886
- function typeRuleParams() {
887
- return (toValue(options.typeRules) ?? []).map((rule) => `&type_rules=${encodeURIComponent(rule)}`).join("");
888
- }
889
- /** Prefer the API's own `detail` over axios's "Request failed with status N". */
890
- function describe(cause, fallback) {
891
- const detail = cause?.response?.data?.detail;
892
- return typeof detail === "string" && detail ? detail : sync.readErrorMessage(cause, fallback);
893
- }
894
- function fail(cause, fallback) {
895
- sync.setError(new Error(describe(cause, fallback)), fallback);
896
- }
897
- function statusOf(cause) {
898
- return cause?.response?.status;
899
- }
900
- async function loadMounts() {
901
- const token = ++mountsToken;
902
- mountsController?.abort();
903
- mountsController = new AbortController();
904
- mountsLoading.value = true;
905
- try {
906
- const data = await api.get("/filesystem/mounts", { signal: mountsController.signal });
907
- if (disposed || token !== mountsToken) return false;
908
- mounts.value = data.mounts ?? [];
909
- sync.clearError();
910
- sync.markLoaded();
911
- return true;
912
- } catch (cause) {
913
- if (disposed || token !== mountsToken) return false;
914
- if (statusOf(cause) === 404) {
915
- mounts.value = [];
916
- sync.clearError();
917
- sync.markLoaded();
918
- return true;
919
- }
920
- mounts.value = [];
921
- fail(cause, "Could not read the server mounts.");
922
- return false;
923
- } finally {
924
- if (token === mountsToken) mountsLoading.value = false;
925
- }
926
- }
927
- async function navigate(location, refresh = false) {
928
- if (disposed || !location.mount_id) return;
929
- clearTimeout(searchTimer);
930
- const token = ++listToken;
931
- listingController?.abort();
932
- listingController = new AbortController();
933
- listingLoading.value = true;
934
- mountId.value = location.mount_id;
935
- path.value = location.path;
936
- resetListing();
937
- sync.clearError();
938
- try {
939
- const query = `mount_id=${encodeURIComponent(location.mount_id)}&path=${encodeURIComponent(location.path)}&sort=${sort.value}` + (refresh ? "&refresh=true" : "") + (search.value ? `&search=${encodeURIComponent(search.value)}` : "") + typeRuleParams();
940
- const listing = await api.get(`/filesystem/browse?${query}`, { signal: listingController.signal });
941
- if (disposed || token !== listToken) return;
942
- mountId.value = listing.mount_id;
943
- path.value = listing.path;
944
- parent.value = listing.parent ?? null;
945
- entries.value = listing.entries ?? [];
946
- breadcrumbs.value = listing.breadcrumbs ?? [];
947
- totalCount.value = listing.total_count ?? null;
948
- matchCount.value = listing.match_count ?? null;
949
- shownCount.value = listing.shown_count ?? listing.entries?.length ?? null;
950
- truncated.value = listing.truncated ?? false;
951
- listingReady.value = true;
952
- sync.clearError();
953
- sync.markLoaded();
954
- } catch (cause) {
955
- if (disposed || token !== listToken) return;
956
- entries.value = [];
957
- fail(cause, "Could not read that folder.");
958
- } finally {
959
- if (token === listToken) listingLoading.value = false;
960
- }
961
- }
962
- async function init() {
963
- if (disposed) return;
964
- const navigationToken = listToken;
965
- if (!await loadMounts() || disposed || navigationToken !== listToken) return;
966
- const preferred = toValue(options.initialMountId);
967
- const reachable = (mount) => mount.available !== false;
968
- const target = mounts.value.find((mount) => mount.id === preferred && reachable(mount)) ?? mounts.value.find(reachable);
969
- if (target) await navigate({
970
- mount_id: target.id,
971
- path: ""
972
- });
973
- else {
974
- mountId.value = "";
975
- path.value = "";
976
- resetListing();
977
- }
978
- }
979
- async function refresh() {
980
- if (disposed) return;
981
- if (mountId.value) await navigate({
982
- mount_id: mountId.value,
983
- path: path.value
984
- }, true);
985
- else await init();
986
- }
987
- function selectCurrentFolder() {
988
- if (!mountId.value || !listingReady.value || isLoading.value || sync.error.value) return;
989
- const crumb = breadcrumbs.value[breadcrumbs.value.length - 1];
990
- const key = JSON.stringify([mountId.value, path.value]);
991
- if (selected.value.some((item) => JSON.stringify([item.mount_id, item.path]) === key)) return;
992
- selected.value = [...selected.value, {
993
- mount_id: mountId.value,
994
- path: path.value,
995
- name: crumb?.label ?? path.value,
996
- kind: "directory",
997
- child_count: totalCount.value
998
- }];
999
- }
1000
- function clear() {
1001
- selected.value = [];
1002
- }
1003
- watch(search, () => {
1004
- if (!mountId.value) return;
1005
- clearTimeout(searchTimer);
1006
- searchTimer = setTimeout(() => {
1007
- navigate({
1008
- mount_id: mountId.value,
1009
- path: path.value
1010
- });
1011
- }, 250);
1012
- });
1013
- watch(sort, () => {
1014
- if (mountId.value) navigate({
1015
- mount_id: mountId.value,
1016
- path: path.value
1017
- });
1018
- });
1019
- watch(() => toValue(options.typeRules), () => {
1020
- if (mountId.value) navigate({
1021
- mount_id: mountId.value,
1022
- path: path.value
1023
- });
1024
- }, { deep: true });
1025
- if (getCurrentScope()) onScopeDispose(() => {
1026
- disposed = true;
1027
- ++listToken;
1028
- ++mountsToken;
1029
- clearTimeout(searchTimer);
1030
- listingController?.abort();
1031
- mountsController?.abort();
1032
- });
1033
- return {
1034
- mounts,
1035
- mountId,
1036
- path,
1037
- parent,
1038
- entries,
1039
- breadcrumbs,
1040
- totalCount,
1041
- matchCount,
1042
- shownCount,
1043
- truncated,
1044
- search,
1045
- sort,
1046
- selected,
1047
- isLoading,
1048
- error: sync.error,
1049
- init,
1050
- navigate,
1051
- refresh,
1052
- selectCurrentFolder,
1053
- clear
1054
- };
1055
- }
1056
- //#endregion
1057
- //#region src/composables/analysisArtifactTarget.ts
1058
- var EXPERIMENT_ID = "experiment_id";
1059
- var ARTIFACT_PLUGIN_ID = "artifact_plugin_id";
1060
- var ARTIFACT_KEY = "artifact_key";
1061
- var ARTIFACT_ID = "artifact_id";
1062
- function parsePositiveInteger(value) {
1063
- if (value === null || !/^\d+$/.test(value)) return void 0;
1064
- const parsed = Number(value);
1065
- return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : void 0;
1066
- }
1067
- /** Build the canonical query used to open one analysis artifact in a plugin. */
1068
- function buildAnalysisArtifactTargetQuery(target) {
1069
- const query = new URLSearchParams();
1070
- query.set(EXPERIMENT_ID, String(target.experimentId));
1071
- query.set(ARTIFACT_PLUGIN_ID, target.artifactPluginId);
1072
- query.set(ARTIFACT_KEY, target.artifactKey);
1073
- if (target.artifactId !== void 0) query.set(ARTIFACT_ID, String(target.artifactId));
1074
- return query;
1075
- }
1076
- /** Parse a complete canonical target. Incomplete or invalid input is not guessed. */
1077
- function parseAnalysisArtifactTarget(query) {
1078
- const experimentId = parsePositiveInteger(query.get(EXPERIMENT_ID));
1079
- const artifactPluginId = query.get(ARTIFACT_PLUGIN_ID)?.trim();
1080
- const artifactKey = query.get(ARTIFACT_KEY)?.trim();
1081
- const rawArtifactId = query.get(ARTIFACT_ID);
1082
- const artifactId = parsePositiveInteger(rawArtifactId);
1083
- if (experimentId === void 0 || !artifactPluginId || !artifactKey || rawArtifactId !== null && artifactId === void 0) return null;
1084
- return {
1085
- experimentId,
1086
- artifactPluginId,
1087
- artifactKey,
1088
- ...artifactId === void 0 ? {} : { artifactId }
1089
- };
1090
- }
1091
- //#endregion
1092
- //#region src/composables/useCommandHistory.ts
1093
- /**
1094
- * Generic command-stack state for plugin editors with undo/redo controls.
1095
- *
1096
- * @deprecated Unused by the MINT platform and scheduled for removal in MINT 1.3; keep command stacks plugin-local.
1097
- */
1098
- function useCommandHistory(options = {}) {
1099
- const maxDepth = Math.max(1, options.maxDepth ?? 50);
1100
- const undoStack = ref([]);
1101
- const redoStack = ref([]);
1102
- const canUndo = computed(() => undoStack.value.length > 0);
1103
- const canRedo = computed(() => redoStack.value.length > 0);
1104
- const undoDescription = computed(() => undoStack.value[undoStack.value.length - 1]?.description ?? "");
1105
- const redoDescription = computed(() => redoStack.value[redoStack.value.length - 1]?.description ?? "");
1106
- function push(command) {
1107
- undoStack.value = [...undoStack.value.slice(-(maxDepth - 1)), command];
1108
- redoStack.value = [];
1109
- }
1110
- function execute(command, executeOptions = {}) {
1111
- if (executeOptions.skipExecute) {
1112
- push(command);
1113
- return;
1114
- }
1115
- return afterMaybePromise(command.execute(), () => push(command));
1116
- }
1117
- function undo() {
1118
- const command = undoStack.value[undoStack.value.length - 1];
1119
- if (!command) return;
1120
- return afterMaybePromise(command.undo(), () => {
1121
- undoStack.value = undoStack.value.slice(0, -1);
1122
- redoStack.value = [...redoStack.value, command];
1123
- });
1124
- }
1125
- function redo() {
1126
- const command = redoStack.value[redoStack.value.length - 1];
1127
- if (!command) return;
1128
- return afterMaybePromise((command.redo ?? command.execute)(), () => {
1129
- redoStack.value = redoStack.value.slice(0, -1);
1130
- undoStack.value = [...undoStack.value.slice(-(maxDepth - 1)), command];
1131
- });
1132
- }
1133
- function clear() {
1134
- undoStack.value = [];
1135
- redoStack.value = [];
1136
- }
1137
- return {
1138
- undoStack,
1139
- redoStack,
1140
- canUndo,
1141
- canRedo,
1142
- undoDescription,
1143
- redoDescription,
1144
- execute,
1145
- push,
1146
- undo,
1147
- redo,
1148
- clear
1149
- };
1150
- }
1151
- function afterMaybePromise(result, callback) {
1152
- if (isPromiseLike(result)) return result.then(callback);
1153
- callback();
1154
- }
1155
- function isPromiseLike(value) {
1156
- return !!value && typeof value.then === "function";
1157
- }
1158
- //#endregion
1159
- //#region src/composables/useOptimisticMutation.ts
1160
- /**
1161
- * Run async mutations with optional optimistic local updates and rollback.
1162
- *
1163
- * @deprecated Unused by the MINT platform and scheduled for removal in MINT 1.3; use `useRequestSyncState()` instead.
1164
- */
1165
- function useOptimisticMutation(options) {
1166
- const loading = shallowRef(false);
1167
- const error = ref(null);
1168
- const data = ref(null);
1169
- const lastSnapshot = ref(void 0);
1170
- const isIdle = computed(() => !loading.value && data.value === null && error.value === null);
1171
- function clearError() {
1172
- error.value = null;
1173
- }
1174
- function reset() {
1175
- loading.value = false;
1176
- error.value = null;
1177
- data.value = null;
1178
- lastSnapshot.value = void 0;
1179
- }
1180
- async function run(variables) {
1181
- const snapshot = options.snapshot?.(variables);
1182
- const context = {
1183
- variables,
1184
- snapshot
1185
- };
1186
- loading.value = true;
1187
- error.value = null;
1188
- lastSnapshot.value = snapshot;
1189
- options.apply?.(variables, snapshot);
1190
- try {
1191
- const result = await options.mutation(variables);
1192
- data.value = result;
1193
- options.commit?.(result, context);
1194
- options.onSuccess?.(result, context);
1195
- return result;
1196
- } catch (err) {
1197
- options.rollback?.(snapshot, variables, err);
1198
- error.value = options.readErrorMessage?.(err) ?? readErrorMessage(err);
1199
- options.onError?.(err, context);
1200
- throw err;
1201
- } finally {
1202
- loading.value = false;
1203
- options.onSettled?.(context);
1204
- }
1205
- }
1206
- return {
1207
- loading,
1208
- error,
1209
- data,
1210
- lastSnapshot,
1211
- isIdle,
1212
- run,
1213
- clearError,
1214
- reset
1215
- };
1216
- }
1217
- //#endregion
1218
- //#region src/composables/useResourceCrud.ts
1219
- /**
1220
- * Common list/create/update/delete state wrapper for generated plugin clients.
1221
- *
1222
- * @deprecated Unused by the MINT platform and scheduled for removal in MINT 1.3; use `useGeneratedPluginClient()` instead.
1223
- */
1224
- function useResourceCrud(options) {
1225
- const items = ref([...options.initialItems ?? []]);
1226
- const loading = shallowRef(false);
1227
- const saving = shallowRef(false);
1228
- const deleting = shallowRef(false);
1229
- const error = ref(null);
1230
- const isBusy = computed(() => loading.value || saving.value || deleting.value);
1231
- const readMessage = options.readErrorMessage ?? readErrorMessage;
1232
- const getKey = options.getKey ?? defaultGetKey;
1233
- function clearError() {
1234
- error.value = null;
1235
- }
1236
- function setError(err) {
1237
- error.value = readMessage(err);
1238
- }
1239
- function setItems(nextItems) {
1240
- items.value = [...nextItems];
1241
- }
1242
- function upsertLocal(item) {
1243
- const key = getKey(item);
1244
- const index = items.value.findIndex((existing) => getKey(existing) === key);
1245
- if (index === -1) {
1246
- items.value = [...items.value, item];
1247
- return;
1248
- }
1249
- items.value = [
1250
- ...items.value.slice(0, index),
1251
- item,
1252
- ...items.value.slice(index + 1)
1253
- ];
1254
- }
1255
- function removeLocal(keyOrItem) {
1256
- const key = resolveKey(keyOrItem, getKey);
1257
- items.value = items.value.filter((item) => getKey(item) !== key);
1258
- }
1259
- async function load() {
1260
- loading.value = true;
1261
- clearError();
1262
- try {
1263
- const result = await options.adapter.list();
1264
- setItems(result);
1265
- return result;
1266
- } catch (err) {
1267
- setError(err);
1268
- throw err;
1269
- } finally {
1270
- loading.value = false;
1271
- }
1272
- }
1273
- async function createItem(payload) {
1274
- if (!options.adapter.create) throw new Error("[MINT SDK] Resource adapter does not implement create().");
1275
- saving.value = true;
1276
- clearError();
1277
- try {
1278
- const result = await options.adapter.create(payload);
1279
- upsertLocal(result);
1280
- return result;
1281
- } catch (err) {
1282
- setError(err);
1283
- throw err;
1284
- } finally {
1285
- saving.value = false;
1286
- }
1287
- }
1288
- async function updateItem(key, payload) {
1289
- if (!options.adapter.update) throw new Error("[MINT SDK] Resource adapter does not implement update().");
1290
- saving.value = true;
1291
- clearError();
1292
- try {
1293
- const result = await options.adapter.update(key, payload);
1294
- upsertLocal(result);
1295
- return result;
1296
- } catch (err) {
1297
- setError(err);
1298
- throw err;
1299
- } finally {
1300
- saving.value = false;
1301
- }
1302
- }
1303
- async function deleteItem(keyOrItem) {
1304
- if (!options.adapter.remove) throw new Error("[MINT SDK] Resource adapter does not implement remove().");
1305
- const key = resolveKey(keyOrItem, getKey);
1306
- deleting.value = true;
1307
- clearError();
1308
- try {
1309
- await options.adapter.remove(key);
1310
- removeLocal(key);
1311
- } catch (err) {
1312
- setError(err);
1313
- throw err;
1314
- } finally {
1315
- deleting.value = false;
1316
- }
1317
- }
1318
- return {
1319
- items,
1320
- loading,
1321
- saving,
1322
- deleting,
1323
- error,
1324
- isBusy,
1325
- load,
1326
- createItem,
1327
- updateItem,
1328
- deleteItem,
1329
- setItems,
1330
- upsertLocal,
1331
- removeLocal,
1332
- clearError
1333
- };
1334
- }
1335
- function createPluginResourceClient(options) {
1336
- return {
1337
- list: () => options.list(options.client),
1338
- create: options.create ? (payload) => options.create(options.client, payload) : void 0,
1339
- update: options.update ? (key, payload) => options.update(options.client, key, payload) : void 0,
1340
- remove: options.remove ? (key) => options.remove(options.client, key) : void 0
1341
- };
1342
- }
1343
- function defaultGetKey(item) {
1344
- if (item && typeof item === "object" && "id" in item) return item.id;
1345
- throw new Error("[MINT SDK] Resource items need an id field or a getKey option.");
1346
- }
1347
- function resolveKey(keyOrItem, getKey) {
1348
- if (typeof keyOrItem === "string" || typeof keyOrItem === "number") return keyOrItem;
1349
- return getKey(keyOrItem);
1350
- }
1351
- //#endregion
1352
- //#region src/composables/useWellPainting.ts
1353
- /**
1354
- * Pointer painting/rectangle selection state for WellPlate-based editors.
1355
- *
1356
- * @deprecated Unused by the MINT platform and scheduled for removal in MINT 1.3; use `useWellPlateEditor()` instead.
1357
- */
1358
- function useWellPainting(options) {
1359
- const activeMode = ref(options.initialMode ?? "select");
1360
- const activePayload = ref(options.initialPayload);
1361
- const isDragging = ref(false);
1362
- const dragStart = ref(null);
1363
- const dragEnd = ref(null);
1364
- const paintTrail = ref(/* @__PURE__ */ new Set());
1365
- const previewWellIds = computed(() => {
1366
- if (!isDragging.value) return [];
1367
- if (activeMode.value === "select") {
1368
- if (!dragStart.value || !dragEnd.value) return [];
1369
- return wellIdsInRectangle(dragStart.value, dragEnd.value, toValue(options.format));
1370
- }
1371
- return [...paintTrail.value];
1372
- });
1373
- function setMode(mode, payload) {
1374
- activeMode.value = mode;
1375
- activePayload.value = payload;
1376
- }
1377
- function setPayload(payload) {
1378
- activePayload.value = payload;
1379
- }
1380
- function onWellMouseDown(wellId) {
1381
- const coord = wellIdToCoordinate(wellId);
1382
- if (!coord) return;
1383
- isDragging.value = true;
1384
- dragStart.value = coord;
1385
- dragEnd.value = coord;
1386
- paintTrail.value = new Set([wellId]);
1387
- }
1388
- function onWellMouseMove(wellId) {
1389
- if (!isDragging.value) return;
1390
- const coord = wellIdToCoordinate(wellId);
1391
- if (!coord) return;
1392
- dragEnd.value = coord;
1393
- if (activeMode.value !== "select") paintTrail.value = new Set([...paintTrail.value, wellId]);
1394
- }
1395
- function onWellMouseUp() {
1396
- if (!isDragging.value) return;
1397
- const wellIds = previewWellIds.value;
1398
- isDragging.value = false;
1399
- if (wellIds.length > 0) options.onComplete?.({
1400
- wellIds,
1401
- mode: activeMode.value,
1402
- payload: activePayload.value
1403
- });
1404
- clearDragState();
1405
- }
1406
- function cancel() {
1407
- isDragging.value = false;
1408
- clearDragState();
1409
- }
1410
- function clearDragState() {
1411
- dragStart.value = null;
1412
- dragEnd.value = null;
1413
- paintTrail.value = /* @__PURE__ */ new Set();
1414
- }
1415
- return {
1416
- activeMode,
1417
- activePayload,
1418
- isDragging,
1419
- previewWellIds,
1420
- setMode,
1421
- setPayload,
1422
- onWellMouseDown,
1423
- onWellMouseMove,
1424
- onWellMouseUp,
1425
- cancel
1426
- };
1427
- }
1428
- function wellIdToCoordinate(wellId) {
1429
- const match = /^([A-Z]+)(\d+)$/i.exec(wellId.trim());
1430
- if (!match) return null;
1431
- return {
1432
- row: rowIndex(match[1].toUpperCase()),
1433
- col: Number(match[2]) - 1
1434
- };
1435
- }
1436
- function coordinateToWellId(coord) {
1437
- return `${rowLabel(coord.row)}${coord.col + 1}`;
1438
- }
1439
- function wellIdsInRectangle(start, end, format) {
1440
- const dimensions = PLATE_CONFIGS[format];
1441
- const minRow = Math.max(0, Math.min(start.row, end.row));
1442
- const maxRow = Math.min(dimensions.rows - 1, Math.max(start.row, end.row));
1443
- const minCol = Math.max(0, Math.min(start.col, end.col));
1444
- const maxCol = Math.min(dimensions.cols - 1, Math.max(start.col, end.col));
1445
- const ids = [];
1446
- for (let row = minRow; row <= maxRow; row++) for (let col = minCol; col <= maxCol; col++) ids.push(coordinateToWellId({
1447
- row,
1448
- col
1449
- }));
1450
- return ids;
1451
- }
1452
- //#endregion
1453
- //#region src/composables/useWellPlateAdapter.ts
1454
- /**
1455
- * Convert domain rows into WellPlate's wells prop without hand-written adapters.
1456
- *
1457
- * @deprecated Unused by the MINT platform and scheduled for removal in MINT 1.3; use `useWellPlateEditor()` instead.
1458
- */
1459
- function useWellPlateAdapter(options) {
1460
- return { wells: computed(() => createWellPlateWells(toValue(options.entries), options)) };
1461
- }
1462
- function createWellPlateWells(entries, options) {
1463
- return Object.fromEntries(entries.map((entry) => {
1464
- const wellId = options.getWellId(entry);
1465
- const coord = parseWellId(wellId);
1466
- return [wellId, {
1467
- id: wellId,
1468
- row: coord.row,
1469
- col: coord.col,
1470
- state: options.getState?.(entry) ?? "filled",
1471
- sampleType: options.getSampleType?.(entry),
1472
- value: options.getValue?.(entry),
1473
- metadata: options.getMetadata?.(entry)
1474
- }];
1475
- }));
1476
- }
1477
- function createColumnConditions(entries) {
1478
- return entries.map((entry) => {
1479
- const startCol = typeof entry.start === "number" ? entry.start : Number.parseInt(entry.start, 10);
1480
- const cols = [];
1481
- const concentrations = [];
1482
- for (const concentration of entry.concentrations) {
1483
- const replicates = concentration.replicates ?? 1;
1484
- for (let index = 0; index < replicates; index++) {
1485
- cols.push(startCol + cols.length);
1486
- concentrations.push(concentration.value);
1487
- }
1488
- }
1489
- return {
1490
- label: entry.label,
1491
- color: entry.color,
1492
- unit: entry.unit,
1493
- cols,
1494
- concentrations
1495
- };
1496
- });
1497
- }
1498
- function createRowConditions(entries) {
1499
- return entries.map((entry) => {
1500
- const startRow = typeof entry.start === "number" ? entry.start : rowLabelToIndex(entry.start);
1501
- const rows = [];
1502
- const concentrations = [];
1503
- for (const concentration of entry.concentrations) {
1504
- const replicates = concentration.replicates ?? 1;
1505
- for (let index = 0; index < replicates; index++) {
1506
- rows.push(indexToRowLabel(startRow + rows.length));
1507
- concentrations.push(concentration.value);
1508
- }
1509
- }
1510
- return {
1511
- label: entry.label,
1512
- color: entry.color,
1513
- unit: entry.unit,
1514
- rows,
1515
- concentrations
1516
- };
1517
- });
1518
- }
1519
- function parseWellId(wellId) {
1520
- const match = /^([A-Z]+)(\d+)$/i.exec(wellId.trim());
1521
- if (!match) throw new Error(`[MINT SDK] Invalid well id "${wellId}".`);
1522
- return {
1523
- row: rowLabelToIndex(match[1].toUpperCase()),
1524
- col: Number(match[2]) - 1
1525
- };
1526
- }
1527
- function rowLabelToIndex(label) {
1528
- let result = 0;
1529
- for (const char of label.toUpperCase()) result = result * 26 + (char.charCodeAt(0) - 64);
1530
- return result - 1;
1531
- }
1532
- function indexToRowLabel(index) {
1533
- let value = index + 1;
1534
- let label = "";
1535
- while (value > 0) {
1536
- value--;
1537
- label = String.fromCharCode(65 + value % 26) + label;
1538
- value = Math.floor(value / 26);
1539
- }
1540
- return label;
1541
- }
1542
- //#endregion
1543
- //#region src/composables/useWellPlateValidation.ts
1544
- /**
1545
- * Rule-driven validation state for WellPlate layout/design screens.
1546
- *
1547
- * @deprecated Unused by the MINT platform and scheduled for removal in MINT 1.3; use `useWellPlateEditor()` instead.
1548
- */
1549
- function useWellPlateValidation(options) {
1550
- const warnings = computed(() => runWellPlateValidation(toValue(options.context), toValue(options.rules)));
1551
- return {
1552
- warnings,
1553
- hasBlockingIssues: computed(() => warnings.value.some((warning) => warning.severity === "error")),
1554
- warningCount: computed(() => warnings.value.length),
1555
- errorCount: computed(() => warnings.value.filter((warning) => warning.severity === "error").length)
1556
- };
1557
- }
1558
- function runWellPlateValidation(context, rules) {
1559
- const warnings = [];
1560
- for (const rule of rules) {
1561
- const result = rule.validate(context);
1562
- if (!result) continue;
1563
- warnings.push(normalizeWarning(rule, result));
1564
- }
1565
- return warnings;
1566
- }
1567
- function normalizeWarning(rule, result) {
1568
- if (typeof result === "string") return {
1569
- type: rule.type,
1570
- severity: rule.severity,
1571
- message: result
1572
- };
1573
- return {
1574
- type: rule.type,
1575
- severity: rule.severity,
1576
- ...result
1577
- };
1578
- }
1579
- //#endregion
1580
- //#region src/composables/pluginEndpointBuilder.ts
1581
- function normalizeApiBaseUrl(baseUrl) {
1582
- if (!baseUrl) return void 0;
1583
- return baseUrl.length > 1 ? baseUrl.replace(/\/+$/, "") : baseUrl;
1584
- }
1585
- function normalizeApiPrefix(prefix) {
1586
- if (!prefix) return void 0;
1587
- if (prefix.startsWith("/api/")) return normalizeApiBaseUrl(prefix);
1588
- if (prefix === "/api") return prefix;
1589
- if (prefix.startsWith("/")) return normalizeApiBaseUrl(`/api${prefix}`);
1590
- return normalizeApiBaseUrl(`/api/${prefix}`);
1591
- }
1592
- function endpointParamName(param) {
1593
- return typeof param === "string" ? param : param.name;
1594
- }
1595
- function endpointParamFieldName(param) {
1596
- return typeof param === "string" ? param : param.fieldName ?? param.name;
1597
- }
1598
- function escapeRegExp(value) {
1599
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1600
- }
1601
- function replacePathParam(path, name, encoded) {
1602
- const escapedName = escapeRegExp(name);
1603
- return path.replace(new RegExp(`\\{${escapedName}(?::[^{}]+)?\\}`, "g"), () => encoded).replace(new RegExp(`:${escapedName}(?=/|$)`, "g"), () => encoded);
1604
- }
1605
- /** Resolve the runtime plugin API base URL from env, explicit options, platform injection, or contract metadata. */
1606
- function resolvePluginBaseUrl(contract, explicitBaseUrl) {
1607
- if (explicitBaseUrl) return normalizeApiBaseUrl(explicitBaseUrl) ?? explicitBaseUrl;
1608
- const injected = getInjectedPlatformContext();
1609
- const platformPrefix = normalizeApiBaseUrl(injected?.plugin?.api_prefix) || normalizeApiPrefix(injected?.plugin?.route_prefix);
1610
- if (platformPrefix) return platformPrefix;
1611
- return normalizeApiBaseUrl(contract.plugin.apiPrefix) || "/api";
1612
- }
1613
- function encodePath(path, payload, pathParams) {
1614
- let nextPath = path;
1615
- for (const param of pathParams) {
1616
- const name = endpointParamName(param);
1617
- const fieldName = endpointParamFieldName(param);
1618
- const value = payload[fieldName] ?? payload[name];
1619
- if (value === void 0 || value === null) throw new Error(`[MINT SDK] Missing path parameter '${fieldName}' for plugin endpoint ${path}`);
1620
- const encoded = encodeURIComponent(String(value));
1621
- nextPath = replacePathParam(nextPath, name, encoded);
1622
- if (fieldName !== name) nextPath = replacePathParam(nextPath, fieldName, encoded);
1623
- }
1624
- return nextPath;
1625
- }
1626
- function withDefaultPathParams(payload, pathParams) {
1627
- if (!pathParams.length) return payload;
1628
- const nextPayload = { ...payload };
1629
- for (const param of pathParams) {
1630
- const name = endpointParamName(param);
1631
- const fieldName = endpointParamFieldName(param);
1632
- if (nextPayload[fieldName] !== void 0 && nextPayload[fieldName] !== null || nextPayload[name] !== void 0 && nextPayload[name] !== null) continue;
1633
- if (isExperimentPathParam(fieldName) || isExperimentPathParam(name)) {
1634
- const experimentId = resolveCurrentExperimentId();
1635
- if (experimentId !== void 0) nextPayload[fieldName] = experimentId;
1636
- }
1637
- }
1638
- return nextPayload;
1639
- }
1640
- function isExperimentPathParam(param) {
1641
- return param === "experimentId" || param === "experiment_id";
1642
- }
1643
- function withoutPathParams(payload, pathParams) {
1644
- const copy = { ...payload };
1645
- for (const param of pathParams) {
1646
- delete copy[endpointParamFieldName(param)];
1647
- delete copy[endpointParamName(param)];
1648
- }
1649
- return copy;
1650
- }
1651
- function queryParamsFromPayload(payload, pathParams, queryParams, endpointPath, includeUnspecifiedParams = false) {
1652
- if (!queryParams?.length) return includeUnspecifiedParams ? withoutPathParams(payload, pathParams) : {};
1653
- const query = {};
1654
- for (const param of queryParams) {
1655
- const name = typeof param === "string" ? param : param.name;
1656
- const fieldName = typeof param === "string" ? param : param.fieldName ?? param.name;
1657
- const required = typeof param !== "string" && param.required === true;
1658
- const value = payload[fieldName];
1659
- if (value === void 0 || value === null) {
1660
- if (required) throw new Error(`[MINT SDK] Missing query parameter '${fieldName}' for plugin endpoint ${endpointPath ?? ""}`);
1661
- continue;
1662
- }
1663
- query[name] = value;
1664
- }
1665
- return query;
1666
- }
1667
- function hasKeys(value) {
1668
- return Object.keys(value).length > 0;
1669
- }
1670
- function isPlainPayloadObject(value) {
1671
- return !!value && typeof value === "object" && !Array.isArray(value);
1672
- }
1673
- function hasOwn(value, key) {
1674
- return Object.prototype.hasOwnProperty.call(value, key);
1675
- }
1676
- function normalizeStructuredEndpointPayload(payload) {
1677
- const pathParamPayload = payload.pathParams;
1678
- const queryPayload = payload.query;
1679
- const hasStructuredPathParams = hasOwn(payload, "pathParams") && isPlainPayloadObject(pathParamPayload);
1680
- const hasStructuredQuery = hasOwn(payload, "query") && isPlainPayloadObject(queryPayload);
1681
- if (!hasStructuredPathParams && !hasStructuredQuery) return payload;
1682
- const nextPayload = { ...payload };
1683
- delete nextPayload.pathParams;
1684
- delete nextPayload.query;
1685
- if (hasStructuredPathParams) Object.assign(nextPayload, pathParamPayload);
1686
- if (hasStructuredQuery) Object.assign(nextPayload, queryPayload);
1687
- return nextPayload;
1688
- }
1689
- function requestBodyFromPayload(payload, requestPayload, endpoint) {
1690
- if (!endpoint.hasBody) return void 0;
1691
- const body = endpoint.pathParams?.length || endpoint.queryParams?.length ? requestPayload.body : payload;
1692
- if (body === void 0 || body === null) throw new Error(`[MINT SDK] Missing request body for plugin endpoint ${endpoint.path}`);
1693
- return body;
1694
- }
1695
- function pluginEndpointRequestParts(contract, endpoint, payload, explicitBaseUrl) {
1696
- const payloadObject = isPlainPayloadObject(payload) ? normalizeStructuredEndpointPayload(payload) : {};
1697
- const pathParams = endpoint.pathParams ?? [];
1698
- const requestPayload = withDefaultPathParams(payloadObject, pathParams);
1699
- const path = encodePath(endpoint.path, requestPayload, pathParams);
1700
- const query = queryParamsFromPayload(requestPayload, pathParams, endpoint.queryParams, endpoint.path, endpoint.method === "get");
1701
- return {
1702
- baseUrl: resolvePluginBaseUrl(contract, explicitBaseUrl),
1703
- path,
1704
- query,
1705
- requestPayload
1706
- };
1707
- }
1708
- function appendQueryValue(params, key, value) {
1709
- if (value === void 0 || value === null) return;
1710
- if (Array.isArray(value)) {
1711
- for (const item of value) appendQueryValue(params, key, item);
1712
- return;
1713
- }
1714
- if (typeof value === "object") {
1715
- params.append(key, JSON.stringify(value));
1716
- return;
1717
- }
1718
- params.append(key, String(value));
1719
- }
1720
- function queryString(query) {
1721
- const params = new URLSearchParams();
1722
- for (const [key, value] of Object.entries(query)) appendQueryValue(params, key, value);
1723
- const serialized = params.toString();
1724
- return serialized ? `?${serialized}` : "";
1725
- }
1726
- function joinBaseUrl(baseUrl, path) {
1727
- if (!baseUrl) return path;
1728
- return `${baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
1729
- }
1730
- /** Build the concrete URL for a generated plugin endpoint without making a request. */
1731
- function buildPluginEndpointUrl(contract, endpoint, payload, options = {}) {
1732
- const parts = pluginEndpointRequestParts(contract, endpoint, payload, options.baseUrl);
1733
- const pathWithQuery = `${parts.path}${queryString(parts.query)}`;
1734
- if (options.includeBaseUrl === false) return pathWithQuery;
1735
- return joinBaseUrl(parts.baseUrl, pathWithQuery);
1736
- }
1737
- //#endregion
1738
- //#region src/composables/usePluginClient.ts
1739
- function queryConfig(query) {
1740
- return hasKeys(query) ? { params: query } : void 0;
1741
- }
1742
- var OCTET_STREAM = "application/octet-stream";
1743
- var MULTIPART_FORM_DATA = "multipart/form-data";
1744
- function endpointRequestConfig(endpoint, query, options) {
1745
- const config = {};
1746
- if (hasKeys(query)) config.params = query;
1747
- if (endpoint.requestContentType === OCTET_STREAM) config.headers = { "Content-Type": OCTET_STREAM };
1748
- if (endpoint.requestContentType === MULTIPART_FORM_DATA) config.headers = { "Content-Type": void 0 };
1749
- if (endpoint.responseContentType === OCTET_STREAM) config.responseType = "blob";
1750
- if (options?.signal) config.signal = options.signal;
1751
- return hasKeys(config) ? config : void 0;
1752
- }
1753
- function isFormData(value) {
1754
- return typeof FormData !== "undefined" && value instanceof FormData;
1755
- }
1756
- function endpointRequestBody(endpoint, body) {
1757
- return endpoint.requestContentType === MULTIPART_FORM_DATA ? pluginFormDataFromPayload(body) : body;
1758
- }
1759
- function endpointHasPayload(endpoint) {
1760
- return Boolean(endpoint.hasBody || endpoint.pathParams?.length || endpoint.queryParams?.length);
1761
- }
1762
- function isBlob(value) {
1763
- return typeof Blob !== "undefined" && value instanceof Blob;
1764
- }
1765
- function appendFormDataValue(formData, key, value) {
1766
- if (value === void 0 || value === null) return;
1767
- if (Array.isArray(value)) {
1768
- for (const item of value) appendFormDataValue(formData, key, item);
1769
- return;
1770
- }
1771
- if (isBlob(value)) {
1772
- formData.append(key, value);
1773
- return;
1774
- }
1775
- if (typeof value === "object") {
1776
- formData.append(key, JSON.stringify(value));
1777
- return;
1778
- }
1779
- formData.append(key, String(value));
1780
- }
1781
- /** Convert plain upload payloads into FormData using the same encoding across generated plugin clients. */
1782
- function pluginFormDataFromPayload(payload) {
1783
- if (isFormData(payload)) return payload;
1784
- if (!payload || typeof payload !== "object" || Array.isArray(payload)) throw new Error("[MINT SDK] Plugin upload endpoints require a FormData or plain object payload.");
1785
- const formData = new FormData();
1786
- for (const [key, value] of Object.entries(payload)) appendFormDataValue(formData, key, value);
1787
- return formData;
1788
- }
1789
- /** Trigger a browser download for an already-created Blob. */
1790
- function downloadBlob(blob, filename, options = {}) {
1791
- if (typeof document === "undefined" || typeof URL === "undefined") throw new Error("[MINT SDK] Cannot trigger a file download outside a browser.");
1792
- const blobUrl = URL.createObjectURL(blob);
1793
- const link = document.createElement("a");
1794
- link.href = blobUrl;
1795
- link.download = filename;
1796
- document.body.appendChild(link);
1797
- link.click();
1798
- document.body.removeChild(link);
1799
- setTimeout(() => URL.revokeObjectURL(blobUrl), options.revokeObjectUrlDelayMs ?? 100);
1800
- }
1801
- function normalizePluginNavPath(path) {
1802
- const raw = path.trim() || "/";
1803
- return (raw.startsWith("/") ? raw : `/${raw}`).replace(/\/+$/, "") || "/";
1804
- }
1805
- function pluginPageIdFromPath(path, fallbackIndex) {
1806
- const normalizedPath = normalizePluginNavPath(path);
1807
- if (normalizedPath === "/") return "dashboard";
1808
- return normalizedPath.replace(/^\/+/, "").replace(/\/+/g, "-") || `page-${fallbackIndex + 1}`;
1809
- }
1810
- /** Convert resolved @mint_plugin nav_items into page selectors for the topbar and homepage PluginCards. */
1811
- function getPluginPageSelectorItems(contract) {
1812
- const pluginIcon = contract.plugin.icon ?? "";
1813
- return (contract.plugin.navItems ?? []).map((item, index) => ({
1814
- id: item.id || pluginPageIdFromPath(item.path, index),
1815
- label: item.label,
1816
- to: normalizePluginNavPath(item.path),
1817
- icon: item.icon || pluginIcon || void 0
1818
- }));
1819
- }
1820
- /** Create a typed plugin API client from a generated MINT plugin contract. */
1821
- function createPluginClient(contract, options) {
1822
- const client = {};
1823
- for (const [name, endpoint] of Object.entries(options.endpoints)) client[name] = async (payloadOrOptions, explicitOptions) => {
1824
- const hasPayload = endpointHasPayload(endpoint);
1825
- const payload = hasPayload ? payloadOrOptions : void 0;
1826
- const callOptions = hasPayload ? explicitOptions : payloadOrOptions ?? explicitOptions;
1827
- const parts = pluginEndpointRequestParts(contract, endpoint, payload, options.baseUrl);
1828
- const api = useApi({
1829
- baseUrl: parts.baseUrl,
1830
- withAuth: endpoint.requiresAuth !== false,
1831
- typedErrors: true
1832
- });
1833
- const config = endpointRequestConfig(endpoint, parts.query, callOptions);
1834
- if (endpoint.method === "get") return api.get(parts.path, config);
1835
- if (endpoint.method === "delete") {
1836
- const rawBody = requestBodyFromPayload(payload, parts.requestPayload, endpoint);
1837
- const body = rawBody === void 0 ? void 0 : endpointRequestBody(endpoint, rawBody);
1838
- const config = body === void 0 ? endpointRequestConfig(endpoint, parts.query, callOptions) : {
1839
- ...endpointRequestConfig(endpoint, parts.query, callOptions),
1840
- data: body
1841
- };
1842
- return api.delete(parts.path, config);
1843
- }
1844
- const body = endpointRequestBody(endpoint, requestBodyFromPayload(payload, parts.requestPayload, endpoint));
1845
- if (endpoint.method === "post") return api.post(parts.path, body, config);
1846
- if (endpoint.method === "put") return api.put(parts.path, body, config);
1847
- if (endpoint.method === "patch") return api.patch(parts.path, body, config);
1848
- throw new Error(`[MINT SDK] Unsupported plugin endpoint method: ${endpoint.method}`);
1849
- };
1850
- return client;
1851
- }
1852
- /** Send multipart or raw binary data to a generated endpoint with contract URL, cancellation, and auth handling. */
1853
- async function uploadPluginEndpoint(contract, endpoint, payload, options = {}) {
1854
- if (![
1855
- "post",
1856
- "put",
1857
- "patch"
1858
- ].includes(endpoint.method)) throw new Error(`[MINT SDK] Plugin upload endpoints must use POST, PUT, or PATCH; got ${endpoint.method}`);
1859
- const parts = pluginEndpointRequestParts(contract, endpoint, payload, options.baseUrl);
1860
- const api = useApi({
1861
- baseUrl: parts.baseUrl,
1862
- withAuth: endpoint.requiresAuth !== false,
1863
- typedErrors: true
1864
- });
1865
- const body = endpoint.hasBody ? requestBodyFromPayload(payload, parts.requestPayload, endpoint) : payload;
1866
- if (endpoint.requestContentType === OCTET_STREAM) {
1867
- const config = endpointRequestConfig(endpoint, parts.query, { signal: options.signal });
1868
- if (endpoint.method === "post") return api.post(parts.path, body, config);
1869
- if (endpoint.method === "put") return api.put(parts.path, body, config);
1870
- return api.patch(parts.path, body, config);
1871
- }
1872
- const formData = pluginFormDataFromPayload(body);
1873
- const config = {
1874
- ...queryConfig(parts.query) ?? {},
1875
- ...options.signal ? { signal: options.signal } : {},
1876
- headers: { "Content-Type": void 0 }
1877
- };
1878
- if (endpoint.method === "post") return api.post(parts.path, formData, config);
1879
- if (endpoint.method === "put") return api.put(parts.path, formData, config);
1880
- return api.patch(parts.path, formData, config);
1881
- }
1882
- /** Fetch a generated plugin endpoint as a Blob and optionally trigger a browser download. */
1883
- async function downloadPluginEndpoint(contract, endpoint, payload, options = {}) {
1884
- if (endpoint.requestContentType === MULTIPART_FORM_DATA) throw new Error("[MINT SDK] Plugin download endpoints do not support multipart request bodies");
1885
- const parts = pluginEndpointRequestParts(contract, endpoint, payload, options.baseUrl);
1886
- const api = useApi({
1887
- baseUrl: parts.baseUrl,
1888
- withAuth: endpoint.requiresAuth !== false,
1889
- typedErrors: true
1890
- });
1891
- const config = {
1892
- ...endpointRequestConfig(endpoint, parts.query, { signal: options.signal }) ?? {},
1893
- responseType: "blob"
1894
- };
1895
- const rawBody = requestBodyFromPayload(payload, parts.requestPayload, endpoint);
1896
- const body = rawBody === void 0 ? void 0 : endpointRequestBody(endpoint, rawBody);
1897
- let blob;
1898
- if (endpoint.method === "get") blob = await api.get(parts.path, config);
1899
- else if (endpoint.method === "post") blob = await api.post(parts.path, body, config);
1900
- else if (endpoint.method === "put") blob = await api.put(parts.path, body, config);
1901
- else if (endpoint.method === "patch") blob = await api.patch(parts.path, body, config);
1902
- else if (endpoint.method === "delete") blob = await api.delete(parts.path, body === void 0 ? config : {
1903
- ...config,
1904
- data: body
1905
- });
1906
- else throw new Error(`[MINT SDK] Unsupported plugin endpoint method: ${endpoint.method}`);
1907
- if (options.filename) downloadBlob(blob, options.filename, { revokeObjectUrlDelayMs: options.revokeObjectUrlDelayMs });
1908
- return blob;
1909
- }
1910
- function normalizePluginSettingsOptions(options = {}) {
1911
- return typeof options === "string" ? { pluginName: options } : options;
1912
- }
1913
- /** @deprecated Use `useGeneratedPluginClient()` for generated clients. */
1914
- function usePluginClient(client) {
1915
- return client;
1916
- }
1917
- var PLATFORM_OWNED_SETTINGS_KEY = "allowed_experiment_types";
1918
- /**
1919
- * Secret settings travel as `{ "$secret": "<id>" }` references, so an untouched
1920
- * secret round-trips without ever exposing its value. Declaring the format on
1921
- * every write keeps a `null` unambiguous: it clears the stored secret instead of
1922
- * meaning "unchanged", which is what the deprecated placeholder form meant.
1923
- */
1924
- function withSecretFormat(payload, secretFormat) {
1925
- if (!secretFormat) return payload;
1926
- return {
1927
- ...payload,
1928
- [PLUGIN_SECRET_FORMAT_KEY]: secretFormat
1929
- };
1930
- }
1931
- function responseSecretFormat(response) {
1932
- if (!response || typeof response !== "object") return null;
1933
- const format = response[PLUGIN_SECRET_FORMAT_KEY];
1934
- return typeof format === "string" && format ? format : null;
1935
- }
1936
- function cloneRecord(value) {
1937
- return JSON.parse(JSON.stringify(value));
1938
- }
1939
- function hasOwnSetting(settings, key) {
1940
- return Object.prototype.hasOwnProperty.call(settings, key);
1941
- }
1942
- function responseSettingsPayload(response) {
1943
- if (!response || typeof response !== "object") return {};
1944
- const data = response;
1945
- if (data.config && typeof data.config === "object") return cloneRecord(data.config);
1946
- if (data.settings && typeof data.settings === "object") return cloneRecord(data.settings);
1947
- return {};
1948
- }
1949
- function responseSettingsRevision(response) {
1950
- if (!response || typeof response !== "object") return null;
1951
- const revision = response.revision;
1952
- return typeof revision === "string" && revision ? revision : null;
1953
- }
1954
- function topLevelSettingsPatch(current, keys) {
1955
- return Object.fromEntries([...keys].map((key) => [key, hasOwnSetting(current, key) ? current[key] : null]));
1956
- }
1957
- function changedTopLevelSettingKeys(current, saved) {
1958
- return new Set([...new Set([...Object.keys(current), ...Object.keys(saved)])].filter((key) => JSON.stringify(current[key]) !== JSON.stringify(saved[key])));
1959
- }
1960
- function platformOwnedSettingsPatch(settings) {
1961
- return { [PLATFORM_OWNED_SETTINGS_KEY]: hasOwnSetting(settings, PLATFORM_OWNED_SETTINGS_KEY) ? settings[PLATFORM_OWNED_SETTINGS_KEY] : null };
1962
- }
1963
- /** Load, edit, and persist plugin settings from platform config or the plugin-local settings router. */
1964
- function usePluginSettings(options = {}) {
1965
- const resolvedOptions = normalizePluginSettingsOptions(options);
1966
- const injectedContext = getInjectedPlatformContext();
1967
- const { plugin, isIntegrated } = usePlatformContext();
1968
- const platformApi = useApi({ baseUrl: resolvedOptions.platformApiBaseUrl ?? injectedContext?.platformApiUrl });
1969
- const pluginApi = useApi({ baseUrl: resolvedOptions.apiBaseUrl ?? injectedContext?.plugin?.api_prefix });
1970
- const values = ref({});
1971
- const savedValues = shallowRef({});
1972
- const revision = ref(null);
1973
- const secretFormat = ref(null);
1974
- const request = useRequestSyncState("Plugin settings request failed.");
1975
- const isLoading = ref(false);
1976
- const isSaving = ref(false);
1977
- const resolvedPluginName = computed(() => resolvedOptions.pluginName ?? plugin.value?.name ?? injectedContext?.plugin?.name ?? "");
1978
- const usesPluginSettingsApi = computed(() => {
1979
- const currentPlugin = injectedContext?.plugin ?? plugin.value;
1980
- return currentPlugin?.settings_api === "plugin" && resolvedPluginName.value === currentPlugin.name;
1981
- });
1982
- const usesPlatformConfig = computed(() => Boolean((isIntegrated.value || injectedContext?.isIntegrated) && resolvedPluginName.value && !usesPluginSettingsApi.value));
1983
- const settings = computed(() => values.value);
1984
- const isDirty = computed(() => JSON.stringify(values.value) !== JSON.stringify(savedValues.value));
1985
- const settingsConfig = computed(() => ({
1986
- title: resolvedOptions.title,
1987
- schema: resolvedOptions.schema ?? void 0,
1988
- showAppearance: resolvedOptions.showAppearance,
1989
- values: values.value
1990
- }));
1991
- function setValues(nextValues) {
1992
- values.value = cloneRecord(nextValues);
1993
- }
1994
- function applyLoaded(nextValues, nextRevision, nextSecretFormat = secretFormat.value) {
1995
- const cloned = cloneRecord(nextValues);
1996
- values.value = cloned;
1997
- savedValues.value = cloneRecord(cloned);
1998
- revision.value = nextRevision;
1999
- secretFormat.value = nextSecretFormat;
2000
- }
2001
- async function load() {
2002
- isLoading.value = true;
2003
- try {
2004
- const response = usesPlatformConfig.value ? await request.run(() => platformApi.get(`/plugins/${encodeURIComponent(resolvedPluginName.value)}/config`), {
2005
- success: "load",
2006
- errorMessage: "Failed to load plugin settings"
2007
- }) : await request.run(() => pluginApi.get("/settings"), {
2008
- success: "load",
2009
- errorMessage: "Failed to load plugin settings"
2010
- });
2011
- applyLoaded(responseSettingsPayload(response), responseSettingsRevision(response), responseSecretFormat(response));
2012
- } catch {} finally {
2013
- isLoading.value = false;
2014
- }
2015
- }
2016
- async function save(nextValues) {
2017
- if (nextValues) setValues(nextValues);
2018
- isSaving.value = true;
2019
- try {
2020
- const payload = cloneRecord(values.value);
2021
- let response;
2022
- if (usesPlatformConfig.value) {
2023
- const dirtyKeys = changedTopLevelSettingKeys(payload, savedValues.value);
2024
- const platformOwnedChanged = dirtyKeys.has(PLATFORM_OWNED_SETTINGS_KEY);
2025
- const typedKeys = [...dirtyKeys].filter((key) => key !== PLATFORM_OWNED_SETTINGS_KEY);
2026
- let nextRevision = revision.value;
2027
- if (typedKeys.length) {
2028
- response = await request.run(() => platformApi.patch(`/plugins/${encodeURIComponent(resolvedPluginName.value)}/config`, { config: withSecretFormat(topLevelSettingsPatch(payload, typedKeys), secretFormat.value) }), {
2029
- success: "save",
2030
- errorMessage: "Failed to save plugin settings"
2031
- });
2032
- nextRevision = responseSettingsRevision(response) ?? nextRevision;
2033
- }
2034
- if (platformOwnedChanged) {
2035
- response = await request.run(() => platformApi.patch(`/plugins/${encodeURIComponent(resolvedPluginName.value)}/config`, { config: platformOwnedSettingsPatch(payload) }), {
2036
- success: "save",
2037
- errorMessage: "Failed to save plugin settings"
2038
- });
2039
- nextRevision = responseSettingsRevision(response) ?? nextRevision;
2040
- }
2041
- if (!response) return true;
2042
- applyLoaded(responseSettingsPayload(response), nextRevision, responseSecretFormat(response) ?? secretFormat.value);
2043
- return true;
2044
- } else {
2045
- const ifMatch = revision.value ? { headers: { "If-Match": `"${revision.value}"` } } : void 0;
2046
- response = await request.run(() => pluginApi.put("/settings", withSecretFormat(payload, secretFormat.value), ifMatch), {
2047
- success: "save",
2048
- errorMessage: "Failed to save plugin settings"
2049
- });
2050
- }
2051
- applyLoaded(responseSettingsPayload(response), responseSettingsRevision(response) ?? revision.value, responseSecretFormat(response) ?? secretFormat.value);
2052
- return true;
2053
- } catch {
2054
- return false;
2055
- } finally {
2056
- isSaving.value = false;
2057
- }
2058
- }
2059
- function reset() {
2060
- values.value = cloneRecord(savedValues.value);
2061
- request.clearError();
2062
- }
2063
- onMounted(() => {
2064
- if (resolvedOptions.loadOnMount !== false) load();
2065
- });
2066
- return {
2067
- settings,
2068
- values,
2069
- config: values,
2070
- settingsConfig,
2071
- isLoading,
2072
- isSaving,
2073
- error: request.error,
2074
- lastLoadedAt: request.lastLoadedAt,
2075
- lastSavedAt: request.lastSavedAt,
2076
- isDirty,
2077
- setValues,
2078
- load,
2079
- save,
2080
- reset
2081
- };
2082
- }
2083
- function streamRequestAuth(requestHeaders, policy) {
2084
- const headers = new Headers(requestHeaders);
2085
- const authStore = useAuthStore();
2086
- if (!authStore.isInitialized) authStore.initialize();
2087
- if (policy.requiresAuth && authStore.token && !headers.has("Authorization")) headers.set("Authorization", `Bearer ${authStore.token}`);
2088
- const token = policy.requiresAuth ? authStore.token : void 0;
2089
- return {
2090
- headers,
2091
- token: token && headers.get("Authorization") === `Bearer ${token}` ? token : void 0
2092
- };
2093
- }
2094
- function pluginEventStreamTransport(endpoint) {
2095
- return async (url, init = {}) => {
2096
- if (typeof fetch !== "function") throw new Error("[MINT SDK] fetch is required for plugin event streams.");
2097
- const requestAuth = streamRequestAuth(init.headers, { requiresAuth: endpoint.requiresAuth !== false });
2098
- const response = await fetch(url, {
2099
- ...init,
2100
- headers: requestAuth.headers
2101
- });
2102
- if (response.status === 401) throw authenticationRequiredError(response, requestAuth.token);
2103
- return response;
2104
- };
2105
- }
2106
- function looksLikeEventStreamOptions(value) {
2107
- if (!value || typeof value !== "object") return false;
2108
- const candidate = value;
2109
- return [
2110
- "baseUrl",
2111
- "immediate",
2112
- "reconnect",
2113
- "reconnectDelayMs",
2114
- "parseJson",
2115
- "credentials",
2116
- "headers",
2117
- "onOpen",
2118
- "onMessage",
2119
- "onError"
2120
- ].some((key) => key in candidate);
2121
- }
2122
- function usePluginEventStream(contract, endpoint, payloadOrOptions, maybeOptions) {
2123
- if (endpoint.method !== "get") throw new Error(`[MINT SDK] Plugin event streams must use GET; got ${endpoint.method}`);
2124
- const payload = maybeOptions === void 0 && looksLikeEventStreamOptions(payloadOrOptions) ? void 0 : payloadOrOptions;
2125
- const options = maybeOptions === void 0 && looksLikeEventStreamOptions(payloadOrOptions) ? payloadOrOptions : maybeOptions ?? {};
2126
- const responseType = endpoint.responseType?.trim();
2127
- const parseJson = options.parseJson ?? Boolean(responseType && responseType !== "string");
2128
- return useEventStream(() => buildPluginEndpointUrl(contract, endpoint, payload, { baseUrl: options.baseUrl }), {
2129
- transport: pluginEventStreamTransport(endpoint),
2130
- immediate: options.immediate,
2131
- reconnect: options.reconnect,
2132
- reconnectDelayMs: options.reconnectDelayMs,
2133
- parseJson,
2134
- credentials: options.credentials,
2135
- headers: options.headers,
2136
- shouldReconnect: (cause) => !(cause instanceof AuthenticationRequiredError),
2137
- onOpen: options.onOpen,
2138
- onMessage: options.onMessage,
2139
- onError: options.onError,
2140
- errorLabel: "Plugin event stream"
2141
- });
2142
- }
2143
- /** Read and optionally load the current platform experiment for integrated plugin views. */
2144
- function useCurrentExperiment(options = {}) {
2145
- const api = useApi({ baseUrl: options.apiBaseUrl ?? getInjectedPlatformContext()?.platformApiUrl });
2146
- const experiment = shallowRef(currentExperimentFromContext());
2147
- const request = useRequestSyncState("Failed to load current experiment");
2148
- const isLoading = request.loading;
2149
- const error = request.error;
2150
- const lastLoadedAt = request.lastLoadedAt;
2151
- const experimentId = computed(() => {
2152
- return resolveCurrentExperimentId();
2153
- });
2154
- const hasExperiment = computed(() => experimentId.value !== void 0);
2155
- function requireExperimentId() {
2156
- const id = experimentId.value;
2157
- if (id === void 0) throw new Error("[MINT SDK] No current experiment is selected.");
2158
- return id;
2159
- }
2160
- async function fetchExperiment(id = experimentId.value) {
2161
- if (id === void 0) {
2162
- experiment.value = currentExperimentFromContext();
2163
- return experiment.value;
2164
- }
2165
- try {
2166
- const result = await request.run(() => api.get(`/experiments/${id}`), {
2167
- success: "load",
2168
- errorMessage: "Failed to load current experiment"
2169
- });
2170
- experiment.value = result;
2171
- return result;
2172
- } catch (e) {
2173
- experiment.value = void 0;
2174
- return;
2175
- }
2176
- }
2177
- async function refresh() {
2178
- return fetchExperiment();
2179
- }
2180
- watch(experimentId, (id) => {
2181
- if (options.immediate === false) return;
2182
- if (id === void 0) {
2183
- experiment.value = currentExperimentFromContext();
2184
- return;
2185
- }
2186
- fetchExperiment(id);
2187
- }, { immediate: options.immediate !== false });
2188
- return {
2189
- experimentId,
2190
- hasExperiment,
2191
- experiment,
2192
- isLoading,
2193
- error,
2194
- lastLoadedAt,
2195
- requireExperimentId,
2196
- fetch: fetchExperiment,
2197
- refresh
2198
- };
2199
- }
2200
- //#endregion
2201
- //#region src/composables/createFilePickerAdapter.ts
2202
- /** Opaque picker identity; decode before passing a selection to a mount-scoped API. */
2203
- function encodePlatformPickerPath(mountId, path = "") {
2204
- return JSON.stringify([mountId, path]);
2205
- }
2206
- function decodePlatformPickerPath(path) {
2207
- const decoded = JSON.parse(path);
2208
- if (!Array.isArray(decoded) || decoded.length !== 2 || !decoded.every((item) => typeof item === "string") || !decoded[0]) throw new Error("Invalid platform picker path.");
2209
- return {
2210
- mountId: decoded[0],
2211
- path: decoded[1]
2212
- };
2213
- }
2214
- function isRelativePath(path) {
2215
- return path === "" || !/^[a-z]:/i.test(path) && !/[\\\x00-\x1f\x7f]/.test(path) && path.split("/").every((part) => part !== "" && part !== "." && part !== "..");
2216
- }
2217
- /** Mount browsing and search shared by platform and generated plugin clients. */
2218
- function createFilePickerAdapter(transport, options = {}) {
2219
- const root = options.rootLocation && { ...options.rootLocation };
2220
- const mountLabels = /* @__PURE__ */ new Map();
2221
- const listings = /* @__PURE__ */ new Map();
2222
- let generation = 0;
2223
- function invalidate() {
2224
- generation++;
2225
- listings.clear();
2226
- mountLabels.clear();
2227
- }
2228
- function validateLocation(location) {
2229
- if (!location.mount_id || !isRelativePath(location.path) || root && (location.mount_id !== root.mount_id || root.path !== "" && location.path !== root.path && !location.path.startsWith(`${root.path}/`))) throw new Error("Invalid file picker location.");
2230
- }
2231
- if (root) validateLocation(root);
2232
- function begin(request) {
2233
- request?.signal?.throwIfAborted();
2234
- if (request?.refresh) invalidate();
2235
- return generation;
2236
- }
2237
- function check(request, token) {
2238
- request?.signal?.throwIfAborted();
2239
- if (token !== generation) throw new DOMException("Cancelled", "AbortError");
2240
- }
2241
- async function read(path, request, token) {
2242
- check(request, token);
2243
- const decoded = path ? decodePlatformPickerPath(path) : void 0;
2244
- const location = decoded ? {
2245
- mount_id: decoded.mountId,
2246
- path: decoded.path
2247
- } : root;
2248
- if (location) validateLocation(location);
2249
- const key = location ? encodePlatformPickerPath(location.mount_id, location.path) : "";
2250
- const cached = listings.get(key);
2251
- if (cached) return cached;
2252
- let nodes;
2253
- if (!location) {
2254
- const mounts = await transport.listMounts(request);
2255
- check(request, token);
2256
- nodes = mounts.map((mount) => {
2257
- mountLabels.set(mount.id, mount.label);
2258
- return {
2259
- kind: "folder",
2260
- path: encodePlatformPickerPath(mount.id),
2261
- name: mount.label,
2262
- parentPath: null,
2263
- parentName: "Server mounts",
2264
- unavailableReason: mount.available === false ? "This mount is offline." : void 0
2265
- };
2266
- });
2267
- } else {
2268
- const listing = await transport.browse(location, request);
2269
- check(request, token);
2270
- if (listing.truncated) throw new Error("This directory exceeds the server listing limit. Choose a smaller data directory.");
2271
- const atRoot = root && location.mount_id === root.mount_id && location.path === root.path;
2272
- nodes = (listing.entries ?? []).map((entry) => {
2273
- if (!isRelativePath(entry.path) || !entry.name || entry.path !== `${location.path ? `${location.path}/` : ""}${entry.name}` || entry.name.includes("/")) throw new Error("Invalid file picker directory entry.");
2274
- const common = {
2275
- path: encodePlatformPickerPath(location.mount_id, entry.path),
2276
- parentPath: atRoot ? null : key,
2277
- parentName: `${mountLabels.get(location.mount_id) ?? location.mount_id}${location.path ? ` / ${location.path}` : ""}`,
2278
- name: entry.name,
2279
- bytes: entry.size_bytes
2280
- };
2281
- return entry.kind === "directory" ? {
2282
- ...common,
2283
- kind: "folder"
2284
- } : {
2285
- ...common,
2286
- kind: "file",
2287
- format: pickerFormat(entry.name)
2288
- };
2289
- });
2290
- }
2291
- listings.set(key, nodes);
2292
- return nodes;
2293
- }
2294
- return {
2295
- invalidate,
2296
- listRoot: async (request) => read("", request, begin(request)),
2297
- listChildren: async (path, request) => read(path, request, begin(request)),
2298
- async search(query, scope, request) {
2299
- const token = begin(request);
2300
- const matches = [];
2301
- const queue = [scope.path];
2302
- const visited = /* @__PURE__ */ new Set();
2303
- const needle = query.toLowerCase();
2304
- let currentRequest = request;
2305
- while (queue.length) {
2306
- check(request, token);
2307
- const path = queue.shift();
2308
- if (visited.has(path)) continue;
2309
- visited.add(path);
2310
- if (visited.size > 500) throw new Error("Search reached its folder limit. Search a smaller location or turn off Subfolders.");
2311
- const nodes = await read(path, currentRequest, token);
2312
- check(request, token);
2313
- if (currentRequest?.refresh) {
2314
- currentRequest = { ...currentRequest };
2315
- delete currentRequest.refresh;
2316
- }
2317
- for (const node of nodes) {
2318
- if (node.name.toLowerCase().includes(needle)) matches.push(node);
2319
- if (scope.recursive && node.kind === "folder" && !node.unavailableReason) queue.push(node.path);
2320
- }
2321
- if (matches.length > 1e4) throw new Error("Too many search results. Use a more specific search.");
2322
- }
2323
- return matches;
2324
- }
2325
- };
2326
- }
2327
- //#endregion
2328
- //#region src/composables/usePlatformFilePickerAdapter.ts
2329
- /** Adapt the authenticated MINT mount API without exposing absolute host paths. */
2330
- function usePlatformFilePickerAdapter(options = {}) {
2331
- const api = useApi({ baseUrl: options.apiBaseUrl ?? getInjectedPlatformContext()?.platformApiUrl });
2332
- return createFilePickerAdapter({
2333
- async listMounts(request) {
2334
- try {
2335
- const url = `/filesystem/mounts${request?.refresh ? "?refresh=true" : ""}`;
2336
- const { mounts } = await api.get(url, { signal: request?.signal });
2337
- return mounts;
2338
- } catch (cause) {
2339
- if (cause.response?.status === 404) return [];
2340
- throw cause;
2341
- }
2342
- },
2343
- browse(location, request) {
2344
- const query = new URLSearchParams({
2345
- mount_id: location.mount_id,
2346
- path: location.path
2347
- });
2348
- if (request?.refresh) query.set("refresh", "true");
2349
- return api.get(`/filesystem/browse?${query}`, { signal: request?.signal });
2350
- }
2351
- }, options);
2352
- }
2353
- //#endregion
2354
- export { useCommandHistory as A, useAsyncBatch as B, coordinateToWellId as C, createPluginResourceClient as D, wellIdsInRectangle as E, resizedLeadingPanelWidth as F, usePasskey as H, resizedTrailingPanelWidth as I, resizedVerticalSplitPercent as L, parseAnalysisArtifactTarget as M, useFileBrowser as N, useResourceCrud as O, usePluginConfig as P, useManualLayoutResize as R, useWellPlateAdapter as S, wellIdToCoordinate as T, useAuth as U, resolvePluginFrontendBase as V, runWellPlateValidation as _, createPluginClient as a, createRowConditions as b, getPluginPageSelectorItems as c, useCurrentExperiment as d, usePluginClient as f, resolvePluginBaseUrl as g, buildPluginEndpointUrl as h, encodePlatformPickerPath as i, buildAnalysisArtifactTargetQuery as j, useOptimisticMutation as k, pluginFormDataFromPayload as l, usePluginSettings as m, createFilePickerAdapter as n, downloadBlob as o, usePluginEventStream as p, decodePlatformPickerPath as r, downloadPluginEndpoint as s, usePlatformFilePickerAdapter as t, uploadPluginEndpoint as u, useWellPlateValidation as v, useWellPainting as w, createWellPlateWells as x, createColumnConditions as y, useAsync as z };
2355
-
2356
- //# sourceMappingURL=composables-BCbYb2lA.js.map