@morscherlab/mint-sdk 1.2.0-beta.3 → 1.2.0-beta.4

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 (1017) hide show
  1. package/README.md +8 -2
  2. package/dist/EmptyState-p90quq8Z.js +2 -0
  3. package/dist/EmptyState-p90quq8Z.js.map +1 -0
  4. package/dist/{ExperimentPopover-Bn6PIGHE.js → ExperimentPopover-tLwJ7XF4.js} +10 -5
  5. package/dist/ExperimentPopover-tLwJ7XF4.js.map +1 -0
  6. package/dist/{ExperimentSelectorModal-DGhrJSSV.js → ExperimentSelectorModal-B6P2msT4.js} +11 -6
  7. package/dist/ExperimentSelectorModal-B6P2msT4.js.map +1 -0
  8. package/dist/{SettingsModal-wF2btHu6.js → SettingsModal-CkNM8qAI.js} +12 -7
  9. package/dist/SettingsModal-CkNM8qAI.js.map +1 -0
  10. package/dist/__stories__/experiment-helpers.d.ts +28 -0
  11. package/dist/__tests__/components/ActionItem.test.d.ts +1 -0
  12. package/dist/__tests__/components/ActionMenu.test.d.ts +1 -0
  13. package/dist/__tests__/components/AppAvatarMenu.test.d.ts +1 -0
  14. package/dist/__tests__/components/AppLayout.test.d.ts +1 -0
  15. package/dist/__tests__/components/AppPluginSwitcher.test.d.ts +1 -0
  16. package/dist/__tests__/components/AppSidebar.test.d.ts +1 -0
  17. package/dist/__tests__/components/AppToastContainer.test.d.ts +1 -0
  18. package/dist/__tests__/components/AppTopBar.navigation.test.d.ts +1 -0
  19. package/dist/__tests__/components/AppTopBar.test.d.ts +1 -0
  20. package/dist/__tests__/components/AppTopBarPageSelector.test.d.ts +1 -0
  21. package/dist/__tests__/components/AppTopBarPillNav.test.d.ts +1 -0
  22. package/dist/__tests__/components/AppTopBarPresence.test.d.ts +1 -0
  23. package/dist/__tests__/components/ArtifactSaveDialog.test.d.ts +1 -0
  24. package/dist/__tests__/components/ArtifactSelectorModal.test.d.ts +1 -0
  25. package/dist/__tests__/components/AutoGroupModal.adapter.test.d.ts +1 -0
  26. package/dist/__tests__/components/BaseInput.test.d.ts +1 -0
  27. package/dist/__tests__/components/BaseModal.test.d.ts +1 -0
  28. package/dist/__tests__/components/BasePill.test.d.ts +1 -0
  29. package/dist/__tests__/components/BaseRadioGroup.test.d.ts +1 -0
  30. package/dist/__tests__/components/BaseSelect.test.d.ts +1 -0
  31. package/dist/__tests__/components/BaseTabs.test.d.ts +1 -0
  32. package/dist/__tests__/components/BaseToggle.test.d.ts +1 -0
  33. package/dist/__tests__/components/BatchProgressList.test.d.ts +1 -0
  34. package/dist/__tests__/components/BioTemplateExperimentWorkspaceView.test.d.ts +1 -0
  35. package/dist/__tests__/components/BioTemplatePackWorkspaceView.test.d.ts +1 -0
  36. package/dist/__tests__/components/BioTemplatePresetWorkspaceView.test.d.ts +1 -0
  37. package/dist/__tests__/components/BioTemplateRenderer.test.d.ts +1 -0
  38. package/dist/__tests__/components/Breadcrumb.test.d.ts +1 -0
  39. package/dist/__tests__/components/Calendar.test.d.ts +1 -0
  40. package/dist/__tests__/components/CalendarGridPanel.test.d.ts +1 -0
  41. package/dist/__tests__/components/CollapsibleCard.test.d.ts +1 -0
  42. package/dist/__tests__/components/ComponentBindingRenderer.test.d.ts +1 -0
  43. package/dist/__tests__/components/ConcentrationInput.test.d.ts +1 -0
  44. package/dist/__tests__/components/ControlWorkspaceView.test.d.ts +1 -0
  45. package/dist/__tests__/components/DataFrame.test.d.ts +1 -0
  46. package/dist/__tests__/components/DatePicker.test.d.ts +1 -0
  47. package/dist/__tests__/components/DateTimePicker.test.d.ts +1 -0
  48. package/dist/__tests__/components/DoseCalculatorVolumeField.test.d.ts +1 -0
  49. package/dist/__tests__/components/DoseDesignWorkspaceView.test.d.ts +1 -0
  50. package/dist/__tests__/components/DropdownButton.test.d.ts +1 -0
  51. package/dist/__tests__/components/EmptyState.test.d.ts +1 -0
  52. package/dist/__tests__/components/ExperimentPopover.test.d.ts +1 -0
  53. package/dist/__tests__/components/ExperimentSelectorModal.test.d.ts +1 -0
  54. package/dist/__tests__/components/FormBuilder.test.d.ts +1 -0
  55. package/dist/__tests__/components/GeneratedPathInput.test.d.ts +1 -0
  56. package/dist/__tests__/components/GeneratedPlotlyResult.test.d.ts +1 -0
  57. package/dist/__tests__/components/GeneratedPluginView.test.d.ts +1 -0
  58. package/dist/__tests__/components/GeneratedResultRenderer.test.d.ts +1 -0
  59. package/dist/__tests__/components/GroupAssigner.test.d.ts +1 -0
  60. package/dist/__tests__/components/InstrumentAlertLog.test.d.ts +1 -0
  61. package/dist/__tests__/components/JobsStatusTray.test.d.ts +1 -0
  62. package/dist/__tests__/components/LcmsSequenceTable.test.d.ts +1 -0
  63. package/dist/__tests__/components/MobileSupportGate.test.d.ts +1 -0
  64. package/dist/__tests__/components/MultiSelect.test.d.ts +1 -0
  65. package/dist/__tests__/components/PlateMapEditorToolbarInternal.test.d.ts +1 -0
  66. package/dist/__tests__/components/PluginIcon.test.d.ts +1 -0
  67. package/dist/__tests__/components/PluginWorkspaceView.controls.test.d.ts +1 -0
  68. package/dist/__tests__/components/PluginWorkspaceView.navigation.test.d.ts +1 -0
  69. package/dist/__tests__/components/PluginWorkspaceView.shell.test.d.ts +1 -0
  70. package/dist/__tests__/components/PluginWorkspaceView.test.d.ts +1 -0
  71. package/dist/__tests__/components/ProgressBar.test.d.ts +1 -0
  72. package/dist/__tests__/components/ProtocolStep.presentation.test.d.ts +1 -0
  73. package/dist/__tests__/components/ProtocolStepEditor.state.test.d.ts +1 -0
  74. package/dist/__tests__/components/ProtocolStepEditor.test.d.ts +1 -0
  75. package/dist/__tests__/components/ProtocolStepParameterField.test.d.ts +1 -0
  76. package/dist/__tests__/components/RackEditor.test.d.ts +1 -0
  77. package/dist/__tests__/components/ReagentList.presentation.test.d.ts +1 -0
  78. package/dist/__tests__/components/ReagentList.test.d.ts +1 -0
  79. package/dist/__tests__/components/RetiredStyles.test.d.ts +1 -0
  80. package/dist/__tests__/components/RuntimeUpdateNotice.test.d.ts +1 -0
  81. package/dist/__tests__/components/SampleHierarchyTree.test.d.ts +1 -0
  82. package/dist/__tests__/components/SampleSelector.colors.test.d.ts +1 -0
  83. package/dist/__tests__/components/SampleSelector.drag.test.d.ts +1 -0
  84. package/dist/__tests__/components/SampleSelector.groups.test.d.ts +1 -0
  85. package/dist/__tests__/components/SampleSelector.selection.test.d.ts +1 -0
  86. package/dist/__tests__/components/SampleSelector.test.d.ts +1 -0
  87. package/dist/__tests__/components/SampleSelectorSampleRow.test.d.ts +1 -0
  88. package/dist/__tests__/components/ScheduleCalendar.test.d.ts +1 -0
  89. package/dist/__tests__/components/SectionCard.test.d.ts +1 -0
  90. package/dist/__tests__/components/SegmentedControl.test.d.ts +1 -0
  91. package/dist/__tests__/components/SequenceProgressBar.test.d.ts +1 -0
  92. package/dist/__tests__/components/SettingsModal.schema.test.d.ts +1 -0
  93. package/dist/__tests__/components/SettingsModal.test.d.ts +1 -0
  94. package/dist/__tests__/components/SmartGroupFieldRecipe.groups.test.d.ts +1 -0
  95. package/dist/__tests__/components/SmartGroupManual.cohorts.test.d.ts +1 -0
  96. package/dist/__tests__/components/TagsInput.test.d.ts +1 -0
  97. package/dist/__tests__/components/ThemeToggle.test.d.ts +1 -0
  98. package/dist/__tests__/components/TimePicker.test.d.ts +1 -0
  99. package/dist/__tests__/components/WellPlate.colors.test.d.ts +1 -0
  100. package/dist/__tests__/components/WellPlate.conditions.test.d.ts +1 -0
  101. package/dist/__tests__/components/WellPlate.geometry.test.d.ts +1 -0
  102. package/dist/__tests__/components/WellPlate.interaction.test.d.ts +1 -0
  103. package/dist/__tests__/components/WellPlate.legend.test.d.ts +1 -0
  104. package/dist/__tests__/components/WellPlate.rendering.test.d.ts +1 -0
  105. package/dist/__tests__/components/WellPlate.sampleDrop.test.d.ts +1 -0
  106. package/dist/__tests__/composables/analysisArtifactTarget.test.d.ts +1 -0
  107. package/dist/__tests__/composables/autoGroup/classKey.test.d.ts +1 -0
  108. package/dist/__tests__/composables/autoGroup/classify.test.d.ts +1 -0
  109. package/dist/__tests__/composables/autoGroup/columns.test.d.ts +1 -0
  110. package/dist/__tests__/composables/autoGroup/compose.test.d.ts +1 -0
  111. package/dist/__tests__/composables/autoGroup/cooccurrence.test.d.ts +1 -0
  112. package/dist/__tests__/composables/autoGroup/fingerprint.test.d.ts +1 -0
  113. package/dist/__tests__/composables/autoGroup/groupByOrder.test.d.ts +1 -0
  114. package/dist/__tests__/composables/autoGroup/groupTree.test.d.ts +1 -0
  115. package/dist/__tests__/composables/autoGroup/integration.test.d.ts +1 -0
  116. package/dist/__tests__/composables/autoGroup/template.test.d.ts +1 -0
  117. package/dist/__tests__/composables/autoGroup/tokenLength.test.d.ts +1 -0
  118. package/dist/__tests__/composables/autoGroup/tokenize.test.d.ts +1 -0
  119. package/dist/__tests__/composables/experiment-utils.test.d.ts +1 -0
  120. package/dist/__tests__/composables/formBuilderRegistry.test.d.ts +1 -0
  121. package/dist/__tests__/composables/resolvePluginFrontendBase.test.d.ts +1 -0
  122. package/dist/__tests__/composables/useAnalysisArtifacts.test.d.ts +1 -0
  123. package/dist/__tests__/composables/useApi.test.d.ts +1 -0
  124. package/dist/__tests__/composables/useAppExperiment.test.d.ts +1 -0
  125. package/dist/__tests__/composables/useAuth.test.d.ts +1 -0
  126. package/dist/__tests__/composables/useAutoGroup.test.d.ts +1 -0
  127. package/dist/__tests__/composables/useAutoGroupInputSources.test.d.ts +1 -0
  128. package/dist/__tests__/composables/useBioTemplatePackWorkspace.test.d.ts +1 -0
  129. package/dist/__tests__/composables/useBioTemplatePresetWorkspace.test.d.ts +1 -0
  130. package/dist/__tests__/composables/useBioTemplateWorkspace.test.d.ts +1 -0
  131. package/dist/__tests__/composables/useCalendarGrid.test.d.ts +1 -0
  132. package/dist/__tests__/composables/useCommandHistory.test.d.ts +1 -0
  133. package/dist/__tests__/composables/useControlSchema.test.d.ts +1 -0
  134. package/dist/__tests__/composables/useDebouncedWatch.test.d.ts +1 -0
  135. package/dist/__tests__/composables/useDropdownState.test.d.ts +1 -0
  136. package/dist/__tests__/composables/useEventListener.test.d.ts +1 -0
  137. package/dist/__tests__/composables/useEventStream.test.d.ts +1 -0
  138. package/dist/__tests__/composables/useExpansionSet.test.d.ts +1 -0
  139. package/dist/__tests__/composables/useExperimentData.test.d.ts +14 -0
  140. package/dist/__tests__/composables/useExperimentSamples.test.d.ts +1 -0
  141. package/dist/__tests__/composables/useExperimentSave.test.d.ts +1 -0
  142. package/dist/__tests__/composables/useExperimentSelector.test.d.ts +1 -0
  143. package/dist/__tests__/composables/useFileImport.test.d.ts +1 -0
  144. package/dist/__tests__/composables/useForm.test.d.ts +1 -0
  145. package/dist/__tests__/composables/useFormBuilder.test.d.ts +1 -0
  146. package/dist/__tests__/composables/useGeneratedAnalysis.test.d.ts +1 -0
  147. package/dist/__tests__/composables/useGroupAssignment.test.d.ts +1 -0
  148. package/dist/__tests__/composables/useJobsStatusTray.test.d.ts +1 -0
  149. package/dist/__tests__/composables/useListSelection.test.d.ts +1 -0
  150. package/dist/__tests__/composables/useMobileSupportGate.test.d.ts +1 -0
  151. package/dist/__tests__/composables/useOptimisticMutation.test.d.ts +1 -0
  152. package/dist/__tests__/composables/usePlatformContext.test.d.ts +1 -0
  153. package/dist/__tests__/composables/usePluginClient.test.d.ts +1 -0
  154. package/dist/__tests__/composables/usePluginConfig.test.d.ts +15 -0
  155. package/dist/__tests__/composables/usePresenceHeartbeat.test.d.ts +1 -0
  156. package/dist/__tests__/composables/useProtocolTemplates.test.d.ts +1 -0
  157. package/dist/__tests__/composables/useRequestSyncState.test.d.ts +1 -0
  158. package/dist/__tests__/composables/useResourceCrud.test.d.ts +1 -0
  159. package/dist/__tests__/composables/useRuntimeAlignment.test.d.ts +1 -0
  160. package/dist/__tests__/composables/useSampleGroups.test.d.ts +1 -0
  161. package/dist/__tests__/composables/useSampleGroups.tree.test.d.ts +1 -0
  162. package/dist/__tests__/composables/useScheduleCalendarLayout.test.d.ts +1 -0
  163. package/dist/__tests__/composables/useSelectionLimit.test.d.ts +1 -0
  164. package/dist/__tests__/composables/useSortedItems.test.d.ts +1 -0
  165. package/dist/__tests__/composables/useTemplateCollection.test.d.ts +1 -0
  166. package/dist/__tests__/composables/useTextSearch.test.d.ts +1 -0
  167. package/dist/__tests__/composables/useTheme.test.d.ts +1 -0
  168. package/dist/__tests__/composables/useTimeUtils.test.d.ts +1 -0
  169. package/dist/__tests__/composables/useWellPainting.test.d.ts +1 -0
  170. package/dist/__tests__/composables/useWellPlateAdapter.test.d.ts +1 -0
  171. package/dist/__tests__/composables/useWellPlateValidation.test.d.ts +1 -0
  172. package/dist/__tests__/docs/extractDocsComponents.test.d.ts +1 -0
  173. package/dist/__tests__/docs/extractDocsExports.test.d.ts +1 -0
  174. package/dist/__tests__/docs/extractDocsParsing.test.d.ts +1 -0
  175. package/dist/__tests__/docs/extractDocsTemplates.test.d.ts +1 -0
  176. package/dist/__tests__/docs/extractDocsTheme.test.d.ts +1 -0
  177. package/dist/__tests__/docs/frontendDocsCatalog.test.d.ts +1 -0
  178. package/dist/__tests__/generatedUi.test.d.ts +1 -0
  179. package/dist/__tests__/stores/experiment.test.d.ts +1 -0
  180. package/dist/__tests__/stores/settings.test.d.ts +1 -0
  181. package/dist/__tests__/templates/templates.test.d.ts +1 -0
  182. package/dist/__tests__/types/GeneratedJsonSchema.test.d.ts +1 -0
  183. package/dist/__tests__/utils/color.test.d.ts +1 -0
  184. package/dist/__tests__/utils/instrument.test.d.ts +1 -0
  185. package/dist/__tests__/utils/jobs.test.d.ts +1 -0
  186. package/dist/__tests__/utils/lcms.test.d.ts +1 -0
  187. package/dist/__tests__/utils/permissions.test.d.ts +1 -0
  188. package/dist/__tests__/utils/rack.test.d.ts +1 -0
  189. package/dist/{analysisArtifactTypes-ejqR8MX3.js → analysisArtifactTypes-DQ0MjwHZ.js} +2 -0
  190. package/dist/analysisArtifactTypes-DQ0MjwHZ.js.map +1 -0
  191. package/dist/chunk-BoAXSpZd.js +33 -0
  192. package/dist/components/AuditTrail.vue.d.ts +38 -0
  193. package/dist/components/AutoGroupModal.vue.d.ts +1 -1
  194. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +1 -1
  195. package/dist/components/ColorSlider.vue.d.ts +34 -0
  196. package/dist/components/DoseDesignWorkspaceView.vue.d.ts +149 -0
  197. package/dist/components/FitPanel.vue.d.ts +46 -0
  198. package/dist/components/GroupAssigner.vue.d.ts +25 -0
  199. package/dist/components/JobsStatusTray.vue.d.ts +11 -2
  200. package/dist/components/RackEditor.vue.d.ts +78 -0
  201. package/dist/components/ReagentEditor.vue.d.ts +30 -0
  202. package/dist/components/ResourceCard.vue.d.ts +50 -0
  203. package/dist/components/SampleLegend.vue.d.ts +1 -1
  204. package/dist/components/SampleSelector.vue.d.ts +1 -1
  205. package/dist/components/ScientificNumber.vue.d.ts +14 -0
  206. package/dist/components/SettingsModal.vue.d.ts +1 -1
  207. package/dist/components/SmartGroupFieldRecipe.vue.d.ts +3 -3
  208. package/dist/components/SmartGroupManual.vue.d.ts +2 -2
  209. package/dist/components/WellPlate.vue.d.ts +1 -1
  210. package/dist/components/index.d.ts +9 -0
  211. package/dist/components/index.js +6 -6
  212. package/dist/{components-DpyzaeIc.js → components-MGNL6tRn.js} +3268 -1353
  213. package/dist/components-MGNL6tRn.js.map +1 -0
  214. package/dist/composables/autoGroupConstants.d.ts +1 -0
  215. package/dist/composables/autoGroupGrouping.d.ts +3 -0
  216. package/dist/composables/index.d.ts +10 -2
  217. package/dist/composables/index.js +9 -9
  218. package/dist/composables/useAnalysisArtifacts.d.ts +2 -0
  219. package/dist/composables/useAsync.d.ts +128 -0
  220. package/dist/composables/useAuth.d.ts +2 -0
  221. package/dist/composables/useAutoGroup.d.ts +30 -1
  222. package/dist/composables/useAutoGroupInputSources.d.ts +32 -0
  223. package/dist/composables/useCommandHistory.d.ts +29 -0
  224. package/dist/composables/useGeneratedAnalysis.d.ts +5 -1
  225. package/dist/composables/useGroupAssignment.d.ts +31 -0
  226. package/dist/composables/useJobsStatusTray.d.ts +41 -4
  227. package/dist/composables/usePluginClient.d.ts +4 -0
  228. package/dist/composables/usePluginConfig.d.ts +25 -0
  229. package/dist/composables/useRackEditor.d.ts +31 -0
  230. package/dist/composables/useResourceCrud.d.ts +40 -0
  231. package/dist/composables/useWellPainting.d.ts +35 -0
  232. package/dist/composables/useWellPlateAdapter.d.ts +36 -0
  233. package/dist/{composables-yz8bTkEV.js → composables-CKxOcvgh.js} +764 -59
  234. package/dist/composables-CKxOcvgh.js.map +1 -0
  235. package/dist/{experiment-D08H_TIH.js → experiment-Y9Iz7KYi.js} +3 -1
  236. package/dist/experiment-Y9Iz7KYi.js.map +1 -0
  237. package/dist/experiment-utils-CLnzPPq5.js +2 -0
  238. package/dist/experiment-utils-CLnzPPq5.js.map +1 -0
  239. package/dist/histoire.setup.d.ts +1 -0
  240. package/dist/index.js +18 -33
  241. package/dist/index.js.map +1 -0
  242. package/dist/install.js +28 -0
  243. package/dist/install.js.map +1 -0
  244. package/dist/jobs.d.ts +15 -1
  245. package/dist/permissions.d.ts +5 -0
  246. package/dist/permissions.js +4 -2
  247. package/dist/permissions.js.map +1 -0
  248. package/dist/plotly.min-c2Sez60c.js +180634 -0
  249. package/dist/plotly.min-c2Sez60c.js.map +1 -0
  250. package/dist/stores/auth.d.ts +6 -0
  251. package/dist/stores/index.js +2 -2
  252. package/dist/styles.css +2836 -270
  253. package/dist/tailwind.preset.js +2 -0
  254. package/dist/tailwind.preset.js.map +1 -0
  255. package/dist/templates/catalog.d.ts +4 -4
  256. package/dist/templates/index.js +2 -2
  257. package/dist/templates/presets.d.ts +2 -2
  258. package/dist/{templates-Pbxjdqlh.js → templates-BTJ9C9Kb.js} +22 -5
  259. package/dist/templates-BTJ9C9Kb.js.map +1 -0
  260. package/dist/types/auth.d.ts +1 -0
  261. package/dist/types/auto-group.d.ts +7 -0
  262. package/dist/types/componentWorkflowTypes.d.ts +22 -0
  263. package/dist/types/components.d.ts +1 -1
  264. package/dist/types/generated-ui.d.ts +11 -2
  265. package/dist/types/index.d.ts +4 -4
  266. package/dist/types/index.js +1 -1
  267. package/dist/types/jobs.d.ts +11 -3
  268. package/dist/{useApi-yE0re8k-.js → useApi-CDBq6z23.js} +15 -0
  269. package/dist/useApi-CDBq6z23.js.map +1 -0
  270. package/dist/{useControlSchema-Ds4VeFxt.js → useControlSchema-CXhxTul7.js} +104 -9
  271. package/dist/useControlSchema-CXhxTul7.js.map +1 -0
  272. package/dist/{useDropdownState-WIGW6Rr-.js → useDropdownState-DSb3BC3N.js} +2 -0
  273. package/dist/useDropdownState-DSb3BC3N.js.map +1 -0
  274. package/dist/{useExperimentSelector-DXqsZuSk.js → useExperimentSelector-D0pjj9WH.js} +3 -1
  275. package/dist/useExperimentSelector-D0pjj9WH.js.map +1 -0
  276. package/dist/{useFormBuilder-1so6QSQL.js → useFormBuilder-CuOFB3aP.js} +3 -1
  277. package/dist/useFormBuilder-CuOFB3aP.js.map +1 -0
  278. package/dist/{useProtocolTemplates-Cv0ca1AM.js → useProtocolTemplates-BWhQIWV5.js} +584 -47
  279. package/dist/useProtocolTemplates-BWhQIWV5.js.map +1 -0
  280. package/package.json +8 -2
  281. package/src/__stories__/experiment-helpers.ts +86 -0
  282. package/src/__tests__/components/ActionItem.test.ts +99 -0
  283. package/src/__tests__/components/ActionMenu.test.ts +120 -0
  284. package/src/__tests__/components/AppAvatarMenu.test.ts +38 -0
  285. package/src/__tests__/components/AppLayout.test.ts +207 -0
  286. package/src/__tests__/components/AppPluginSwitcher.test.ts +44 -0
  287. package/src/__tests__/components/AppSidebar.test.ts +857 -0
  288. package/src/__tests__/components/AppToastContainer.test.ts +37 -0
  289. package/src/__tests__/components/AppTopBar.navigation.test.ts +68 -0
  290. package/src/__tests__/components/AppTopBar.test.ts +1256 -0
  291. package/src/__tests__/components/AppTopBarPageSelector.test.ts +156 -0
  292. package/src/__tests__/components/AppTopBarPillNav.test.ts +125 -0
  293. package/src/__tests__/components/AppTopBarPresence.test.ts +47 -0
  294. package/src/__tests__/components/ArtifactSaveDialog.test.ts +566 -0
  295. package/src/__tests__/components/ArtifactSelectorModal.test.ts +178 -0
  296. package/src/__tests__/components/AutoGroupModal.adapter.test.ts +204 -0
  297. package/src/__tests__/components/BaseInput.test.ts +99 -0
  298. package/src/__tests__/components/BaseModal.test.ts +133 -0
  299. package/src/__tests__/components/BasePill.test.ts +291 -0
  300. package/src/__tests__/components/BaseRadioGroup.test.ts +25 -0
  301. package/src/__tests__/components/BaseSelect.test.ts +73 -0
  302. package/src/__tests__/components/BaseTabs.test.ts +40 -0
  303. package/src/__tests__/components/BaseToggle.test.ts +40 -0
  304. package/src/__tests__/components/BatchProgressList.test.ts +52 -0
  305. package/src/__tests__/components/BioTemplateExperimentWorkspaceView.test.ts +159 -0
  306. package/src/__tests__/components/BioTemplatePackWorkspaceView.test.ts +192 -0
  307. package/src/__tests__/components/BioTemplatePresetWorkspaceView.test.ts +328 -0
  308. package/src/__tests__/components/BioTemplateRenderer.test.ts +96 -0
  309. package/src/__tests__/components/Breadcrumb.test.ts +23 -0
  310. package/src/__tests__/components/Calendar.test.ts +566 -0
  311. package/src/__tests__/components/CalendarGridPanel.test.ts +36 -0
  312. package/src/__tests__/components/CollapsibleCard.test.ts +571 -0
  313. package/src/__tests__/components/ComponentBindingRenderer.test.ts +278 -0
  314. package/src/__tests__/components/ConcentrationInput.test.ts +45 -0
  315. package/src/__tests__/components/ControlWorkspaceView.test.ts +1102 -0
  316. package/src/__tests__/components/DataFrame.test.ts +915 -0
  317. package/src/__tests__/components/DatePicker.test.ts +45 -0
  318. package/src/__tests__/components/DateTimePicker.test.ts +48 -0
  319. package/src/__tests__/components/DoseCalculatorVolumeField.test.ts +53 -0
  320. package/src/__tests__/components/DoseDesignWorkspaceView.test.ts +185 -0
  321. package/src/__tests__/components/DropdownButton.test.ts +494 -0
  322. package/src/__tests__/components/EmptyState.test.ts +23 -0
  323. package/src/__tests__/components/ExperimentPopover.test.ts +143 -0
  324. package/src/__tests__/components/ExperimentSelectorModal.test.ts +143 -0
  325. package/src/__tests__/components/FormBuilder.test.ts +353 -0
  326. package/src/__tests__/components/GeneratedPathInput.test.ts +132 -0
  327. package/src/__tests__/components/GeneratedPlotlyResult.test.ts +117 -0
  328. package/src/__tests__/components/GeneratedPluginView.test.ts +235 -0
  329. package/src/__tests__/components/GeneratedResultRenderer.test.ts +93 -0
  330. package/src/__tests__/components/GroupAssigner.test.ts +48 -0
  331. package/src/__tests__/components/InstrumentAlertLog.test.ts +59 -0
  332. package/src/__tests__/components/JobsStatusTray.test.ts +165 -0
  333. package/src/__tests__/components/LcmsSequenceTable.test.ts +57 -0
  334. package/src/__tests__/components/MobileSupportGate.test.ts +120 -0
  335. package/src/__tests__/components/MultiSelect.test.ts +48 -0
  336. package/src/__tests__/components/PlateMapEditorToolbarInternal.test.ts +54 -0
  337. package/src/__tests__/components/PluginIcon.test.ts +215 -0
  338. package/src/__tests__/components/PluginWorkspaceView.controls.test.ts +156 -0
  339. package/src/__tests__/components/PluginWorkspaceView.navigation.test.ts +102 -0
  340. package/src/__tests__/components/PluginWorkspaceView.shell.test.ts +43 -0
  341. package/src/__tests__/components/PluginWorkspaceView.test.ts +719 -0
  342. package/src/__tests__/components/ProgressBar.test.ts +18 -0
  343. package/src/__tests__/components/ProtocolStep.presentation.test.ts +31 -0
  344. package/src/__tests__/components/ProtocolStepEditor.state.test.ts +165 -0
  345. package/src/__tests__/components/ProtocolStepEditor.test.ts +33 -0
  346. package/src/__tests__/components/ProtocolStepParameterField.test.ts +44 -0
  347. package/src/__tests__/components/RackEditor.test.ts +125 -0
  348. package/src/__tests__/components/ReagentList.presentation.test.ts +68 -0
  349. package/src/__tests__/components/ReagentList.test.ts +82 -0
  350. package/src/__tests__/components/RetiredStyles.test.ts +82 -0
  351. package/src/__tests__/components/RuntimeUpdateNotice.test.ts +14 -0
  352. package/src/__tests__/components/SampleHierarchyTree.test.ts +53 -0
  353. package/src/__tests__/components/SampleSelector.colors.test.ts +49 -0
  354. package/src/__tests__/components/SampleSelector.drag.test.ts +126 -0
  355. package/src/__tests__/components/SampleSelector.groups.test.ts +94 -0
  356. package/src/__tests__/components/SampleSelector.selection.test.ts +56 -0
  357. package/src/__tests__/components/SampleSelector.test.ts +466 -0
  358. package/src/__tests__/components/SampleSelectorSampleRow.test.ts +40 -0
  359. package/src/__tests__/components/ScheduleCalendar.test.ts +44 -0
  360. package/src/__tests__/components/SectionCard.test.ts +181 -0
  361. package/src/__tests__/components/SegmentedControl.test.ts +69 -0
  362. package/src/__tests__/components/SequenceProgressBar.test.ts +39 -0
  363. package/src/__tests__/components/SettingsModal.schema.test.ts +97 -0
  364. package/src/__tests__/components/SettingsModal.test.ts +407 -0
  365. package/src/__tests__/components/SmartGroupFieldRecipe.groups.test.ts +101 -0
  366. package/src/__tests__/components/SmartGroupManual.cohorts.test.ts +97 -0
  367. package/src/__tests__/components/TagsInput.test.ts +75 -0
  368. package/src/__tests__/components/ThemeToggle.test.ts +47 -0
  369. package/src/__tests__/components/TimePicker.test.ts +38 -0
  370. package/src/__tests__/components/WellPlate.colors.test.ts +28 -0
  371. package/src/__tests__/components/WellPlate.conditions.test.ts +68 -0
  372. package/src/__tests__/components/WellPlate.geometry.test.ts +54 -0
  373. package/src/__tests__/components/WellPlate.interaction.test.ts +171 -0
  374. package/src/__tests__/components/WellPlate.legend.test.ts +13 -0
  375. package/src/__tests__/components/WellPlate.rendering.test.ts +122 -0
  376. package/src/__tests__/components/WellPlate.sampleDrop.test.ts +70 -0
  377. package/src/__tests__/composables/analysisArtifactTarget.test.ts +77 -0
  378. package/src/__tests__/composables/autoGroup/classKey.test.ts +25 -0
  379. package/src/__tests__/composables/autoGroup/classify.test.ts +107 -0
  380. package/src/__tests__/composables/autoGroup/columns.test.ts +135 -0
  381. package/src/__tests__/composables/autoGroup/compose.test.ts +226 -0
  382. package/src/__tests__/composables/autoGroup/cooccurrence.test.ts +91 -0
  383. package/src/__tests__/composables/autoGroup/fingerprint.test.ts +122 -0
  384. package/src/__tests__/composables/autoGroup/groupByOrder.test.ts +278 -0
  385. package/src/__tests__/composables/autoGroup/groupTree.test.ts +99 -0
  386. package/src/__tests__/composables/autoGroup/integration.test.ts +75 -0
  387. package/src/__tests__/composables/autoGroup/template.test.ts +70 -0
  388. package/src/__tests__/composables/autoGroup/tokenLength.test.ts +85 -0
  389. package/src/__tests__/composables/autoGroup/tokenize.test.ts +33 -0
  390. package/src/__tests__/composables/experiment-utils.test.ts +63 -0
  391. package/src/__tests__/composables/formBuilderRegistry.test.ts +191 -0
  392. package/src/__tests__/composables/resolvePluginFrontendBase.test.ts +65 -0
  393. package/src/__tests__/composables/useAnalysisArtifacts.test.ts +740 -0
  394. package/src/__tests__/composables/useApi.test.ts +157 -0
  395. package/src/__tests__/composables/useAppExperiment.test.ts +899 -0
  396. package/src/__tests__/composables/useAuth.test.ts +233 -0
  397. package/src/__tests__/composables/useAutoGroup.test.ts +524 -0
  398. package/src/__tests__/composables/useAutoGroupInputSources.test.ts +127 -0
  399. package/src/__tests__/composables/useBioTemplatePackWorkspace.test.ts +125 -0
  400. package/src/__tests__/composables/useBioTemplatePresetWorkspace.test.ts +199 -0
  401. package/src/__tests__/composables/useBioTemplateWorkspace.test.ts +104 -0
  402. package/src/__tests__/composables/useCalendarGrid.test.ts +38 -0
  403. package/src/__tests__/composables/useCommandHistory.test.ts +43 -0
  404. package/src/__tests__/composables/useControlSchema.test.ts +1060 -0
  405. package/src/__tests__/composables/useDebouncedWatch.test.ts +93 -0
  406. package/src/__tests__/composables/useDropdownState.test.ts +95 -0
  407. package/src/__tests__/composables/useEventListener.test.ts +116 -0
  408. package/src/__tests__/composables/useEventStream.test.ts +140 -0
  409. package/src/__tests__/composables/useExpansionSet.test.ts +62 -0
  410. package/src/__tests__/composables/useExperimentData.test.ts +154 -0
  411. package/src/__tests__/composables/useExperimentSamples.test.ts +91 -0
  412. package/src/__tests__/composables/useExperimentSave.test.ts +542 -0
  413. package/src/__tests__/composables/useExperimentSelector.test.ts +338 -0
  414. package/src/__tests__/composables/useFileImport.test.ts +44 -0
  415. package/src/__tests__/composables/useForm.test.ts +263 -0
  416. package/src/__tests__/composables/useFormBuilder.test.ts +994 -0
  417. package/src/__tests__/composables/useGeneratedAnalysis.test.ts +1903 -0
  418. package/src/__tests__/composables/useGroupAssignment.test.ts +73 -0
  419. package/src/__tests__/composables/useJobsStatusTray.test.ts +283 -0
  420. package/src/__tests__/composables/useListSelection.test.ts +66 -0
  421. package/src/__tests__/composables/useMobileSupportGate.test.ts +74 -0
  422. package/src/__tests__/composables/useOptimisticMutation.test.ts +40 -0
  423. package/src/__tests__/composables/usePlatformContext.test.ts +148 -0
  424. package/src/__tests__/composables/usePluginClient.test.ts +1772 -0
  425. package/src/__tests__/composables/usePluginConfig.test.ts +497 -0
  426. package/src/__tests__/composables/usePresenceHeartbeat.test.ts +164 -0
  427. package/src/__tests__/composables/useProtocolTemplates.test.ts +64 -0
  428. package/src/__tests__/composables/useRequestSyncState.test.ts +92 -0
  429. package/src/__tests__/composables/useResourceCrud.test.ts +56 -0
  430. package/src/__tests__/composables/useRuntimeAlignment.test.ts +145 -0
  431. package/src/__tests__/composables/useSampleGroups.test.ts +66 -0
  432. package/src/__tests__/composables/useSampleGroups.tree.test.ts +145 -0
  433. package/src/__tests__/composables/useScheduleCalendarLayout.test.ts +89 -0
  434. package/src/__tests__/composables/useSelectionLimit.test.ts +41 -0
  435. package/src/__tests__/composables/useSortedItems.test.ts +87 -0
  436. package/src/__tests__/composables/useTemplateCollection.test.ts +147 -0
  437. package/src/__tests__/composables/useTextSearch.test.ts +55 -0
  438. package/src/__tests__/composables/useTheme.test.ts +91 -0
  439. package/src/__tests__/composables/useTimeUtils.test.ts +35 -0
  440. package/src/__tests__/composables/useWellPainting.test.ts +52 -0
  441. package/src/__tests__/composables/useWellPlateAdapter.test.ts +50 -0
  442. package/src/__tests__/composables/useWellPlateValidation.test.ts +42 -0
  443. package/src/__tests__/docs/extractDocsComponents.test.ts +156 -0
  444. package/src/__tests__/docs/extractDocsExports.test.ts +92 -0
  445. package/src/__tests__/docs/extractDocsParsing.test.ts +91 -0
  446. package/src/__tests__/docs/extractDocsTemplates.test.ts +53 -0
  447. package/src/__tests__/docs/extractDocsTheme.test.ts +89 -0
  448. package/src/__tests__/docs/frontendDocsCatalog.test.ts +436 -0
  449. package/src/__tests__/fixtures/auto-group/mixed-lc-ms-batch.txt +187 -0
  450. package/src/__tests__/fixtures/templates/dose-response.json +81 -0
  451. package/src/__tests__/fixtures/templates/plate-map.json +54 -0
  452. package/src/__tests__/fixtures/templates/qpcr-plate.json +96 -0
  453. package/src/__tests__/fixtures/templates/sample-sheet.json +71 -0
  454. package/src/__tests__/generatedUi.test.ts +119 -0
  455. package/src/__tests__/stores/experiment.test.ts +300 -0
  456. package/src/__tests__/stores/settings.test.ts +78 -0
  457. package/src/__tests__/templates/templates.test.ts +1185 -0
  458. package/src/__tests__/types/GeneratedJsonSchema.test.ts +62 -0
  459. package/src/__tests__/utils/color.test.ts +96 -0
  460. package/src/__tests__/utils/instrument.test.ts +47 -0
  461. package/src/__tests__/utils/jobs.test.ts +95 -0
  462. package/src/__tests__/utils/lcms.test.ts +73 -0
  463. package/src/__tests__/utils/permissions.test.ts +62 -0
  464. package/src/__tests__/utils/rack.test.ts +120 -0
  465. package/src/components/ActionMenu.story.vue +26 -0
  466. package/src/components/ActionMenu.vue +209 -0
  467. package/src/components/AlertBox.story.vue +204 -0
  468. package/src/components/AlertBox.vue +88 -0
  469. package/src/components/AppAvatarMenu.story.vue +155 -0
  470. package/src/components/AppAvatarMenu.vue +133 -0
  471. package/src/components/AppContainer.story.vue +104 -0
  472. package/src/components/AppContainer.vue +34 -0
  473. package/src/components/AppLayout.story.vue +331 -0
  474. package/src/components/AppLayout.vue +156 -0
  475. package/src/components/AppPluginSwitcher.vue +137 -0
  476. package/src/components/AppSidebar.story.vue +584 -0
  477. package/src/components/AppSidebar.vue +471 -0
  478. package/src/components/AppToastContainer.story.vue +157 -0
  479. package/src/components/AppToastContainer.vue +62 -0
  480. package/src/components/AppTopBar.navigation.ts +60 -0
  481. package/src/components/AppTopBar.story.vue +280 -0
  482. package/src/components/AppTopBar.vue +474 -0
  483. package/src/components/ArtifactSaveDialog.story.vue +319 -0
  484. package/src/components/ArtifactSaveDialog.vue +434 -0
  485. package/src/components/ArtifactSelectorModal.story.vue +295 -0
  486. package/src/components/ArtifactSelectorModal.vue +333 -0
  487. package/src/components/AuditTrail.story.vue +163 -0
  488. package/src/components/AuditTrail.vue +151 -0
  489. package/src/components/AutoGroupModal.adapter.ts +203 -0
  490. package/src/components/AutoGroupModal.story.vue +458 -0
  491. package/src/components/AutoGroupModal.vue +294 -0
  492. package/src/components/Avatar.story.vue +115 -0
  493. package/src/components/Avatar.vue +79 -0
  494. package/src/components/BaseButton.story.vue +96 -0
  495. package/src/components/BaseButton.vue +75 -0
  496. package/src/components/BaseCheckbox.story.vue +100 -0
  497. package/src/components/BaseCheckbox.vue +131 -0
  498. package/src/components/BaseInput.story.vue +98 -0
  499. package/src/components/BaseInput.vue +76 -0
  500. package/src/components/BaseModal.story.vue +335 -0
  501. package/src/components/BaseModal.vue +328 -0
  502. package/src/components/BasePill.story.vue +142 -0
  503. package/src/components/BasePill.vue +89 -0
  504. package/src/components/BaseRadioGroup.story.vue +145 -0
  505. package/src/components/BaseRadioGroup.vue +128 -0
  506. package/src/components/BaseSelect.story.vue +120 -0
  507. package/src/components/BaseSelect.vue +89 -0
  508. package/src/components/BaseSlider.story.vue +122 -0
  509. package/src/components/BaseSlider.vue +126 -0
  510. package/src/components/BaseTabs.story.vue +127 -0
  511. package/src/components/BaseTabs.vue +82 -0
  512. package/src/components/BaseTextarea.story.vue +91 -0
  513. package/src/components/BaseTextarea.vue +62 -0
  514. package/src/components/BaseToggle.story.vue +108 -0
  515. package/src/components/BaseToggle.vue +143 -0
  516. package/src/components/BatchProgressList.story.vue +92 -0
  517. package/src/components/BatchProgressList.vue +181 -0
  518. package/src/components/BioTemplateExperimentWorkspaceView.story.vue +123 -0
  519. package/src/components/BioTemplateExperimentWorkspaceView.vue +350 -0
  520. package/src/components/BioTemplatePackWorkspaceView.story.vue +107 -0
  521. package/src/components/BioTemplatePackWorkspaceView.vue +184 -0
  522. package/src/components/BioTemplatePresetWorkspaceView.story.vue +163 -0
  523. package/src/components/BioTemplatePresetWorkspaceView.vue +408 -0
  524. package/src/components/BioTemplateRenderer.story.vue +57 -0
  525. package/src/components/BioTemplateRenderer.vue +64 -0
  526. package/src/components/Breadcrumb.story.vue +106 -0
  527. package/src/components/Breadcrumb.vue +81 -0
  528. package/src/components/Calendar.story.vue +106 -0
  529. package/src/components/Calendar.vue +367 -0
  530. package/src/components/ChartContainer.story.vue +113 -0
  531. package/src/components/ChartContainer.vue +64 -0
  532. package/src/components/ChemicalFormula.story.vue +102 -0
  533. package/src/components/ChemicalFormula.vue +39 -0
  534. package/src/components/CollapsibleCard.story.vue +135 -0
  535. package/src/components/CollapsibleCard.vue +245 -0
  536. package/src/components/ColorSlider.story.vue +120 -0
  537. package/src/components/ColorSlider.vue +164 -0
  538. package/src/components/ComponentBindingRenderer.story.vue +87 -0
  539. package/src/components/ComponentBindingRenderer.vue +324 -0
  540. package/src/components/ConcentrationInput.story.vue +77 -0
  541. package/src/components/ConcentrationInput.vue +149 -0
  542. package/src/components/ConfirmDialog.story.vue +248 -0
  543. package/src/components/ConfirmDialog.vue +100 -0
  544. package/src/components/ControlWorkspaceView.story.vue +347 -0
  545. package/src/components/ControlWorkspaceView.vue +381 -0
  546. package/src/components/DataFrame.story.vue +148 -0
  547. package/src/components/DataFrame.vue +520 -0
  548. package/src/components/DatePicker.story.vue +119 -0
  549. package/src/components/DatePicker.vue +197 -0
  550. package/src/components/DateTimePicker.story.vue +112 -0
  551. package/src/components/DateTimePicker.vue +271 -0
  552. package/src/components/Divider.story.vue +80 -0
  553. package/src/components/Divider.vue +49 -0
  554. package/src/components/DoseCalculator.story.vue +68 -0
  555. package/src/components/DoseCalculator.vue +416 -0
  556. package/src/components/DoseCalculatorVolumeField.vue +61 -0
  557. package/src/components/DoseDesignWorkspaceView.story.vue +77 -0
  558. package/src/components/DoseDesignWorkspaceView.vue +262 -0
  559. package/src/components/DropdownButton.story.vue +102 -0
  560. package/src/components/DropdownButton.vue +163 -0
  561. package/src/components/EmptyState.story.vue +135 -0
  562. package/src/components/EmptyState.vue +71 -0
  563. package/src/components/ExperimentCodeBadge.story.vue +77 -0
  564. package/src/components/ExperimentCodeBadge.vue +64 -0
  565. package/src/components/ExperimentDataViewer.story.vue +174 -0
  566. package/src/components/ExperimentDataViewer.vue +293 -0
  567. package/src/components/ExperimentPopover.story.vue +442 -0
  568. package/src/components/ExperimentPopover.vue +312 -0
  569. package/src/components/ExperimentSelectorModal.story.vue +381 -0
  570. package/src/components/ExperimentSelectorModal.vue +432 -0
  571. package/src/components/ExperimentTimeline.story.vue +161 -0
  572. package/src/components/ExperimentTimeline.vue +357 -0
  573. package/src/components/FileUploader.story.vue +107 -0
  574. package/src/components/FileUploader.vue +424 -0
  575. package/src/components/FitPanel.story.vue +125 -0
  576. package/src/components/FitPanel.vue +120 -0
  577. package/src/components/FormActions.vue +92 -0
  578. package/src/components/FormBuilder.story.vue +203 -0
  579. package/src/components/FormBuilder.vue +311 -0
  580. package/src/components/FormField.story.vue +132 -0
  581. package/src/components/FormField.vue +59 -0
  582. package/src/components/FormulaInput.story.vue +96 -0
  583. package/src/components/FormulaInput.vue +125 -0
  584. package/src/components/GeneratedPathInput.vue +218 -0
  585. package/src/components/GeneratedPlotlyResult.vue +87 -0
  586. package/src/components/GeneratedPluginView.vue +289 -0
  587. package/src/components/GeneratedResultRenderer.vue +147 -0
  588. package/src/components/GroupAssigner.story.vue +83 -0
  589. package/src/components/GroupAssigner.vue +257 -0
  590. package/src/components/IconButton.story.vue +140 -0
  591. package/src/components/IconButton.vue +73 -0
  592. package/src/components/InstrumentAlertLog.vue +191 -0
  593. package/src/components/InstrumentStateBadge.vue +50 -0
  594. package/src/components/InstrumentStatusCard.vue +188 -0
  595. package/src/components/JobsStatusTray.story.vue +126 -0
  596. package/src/components/JobsStatusTray.vue +407 -0
  597. package/src/components/LcmsSequenceTable.vue +191 -0
  598. package/src/components/LoadingSpinner.story.vue +70 -0
  599. package/src/components/LoadingSpinner.vue +50 -0
  600. package/src/components/MobileSupportGate.story.vue +52 -0
  601. package/src/components/MobileSupportGate.vue +115 -0
  602. package/src/components/MoleculeInput.story.vue +66 -0
  603. package/src/components/MoleculeInput.vue +426 -0
  604. package/src/components/MultiSelect.story.vue +132 -0
  605. package/src/components/MultiSelect.vue +123 -0
  606. package/src/components/NumberInput.story.vue +122 -0
  607. package/src/components/NumberInput.vue +162 -0
  608. package/src/components/PlateMapEditor.story.vue +92 -0
  609. package/src/components/PlateMapEditor.vue +434 -0
  610. package/src/components/PluginIcon.story.vue +103 -0
  611. package/src/components/PluginIcon.vue +158 -0
  612. package/src/components/PluginWorkspaceView.controls.ts +182 -0
  613. package/src/components/PluginWorkspaceView.navigation.ts +106 -0
  614. package/src/components/PluginWorkspaceView.props.ts +169 -0
  615. package/src/components/PluginWorkspaceView.shell.ts +67 -0
  616. package/src/components/PluginWorkspaceView.story.vue +341 -0
  617. package/src/components/PluginWorkspaceView.vue +395 -0
  618. package/src/components/ProgressBar.story.vue +148 -0
  619. package/src/components/ProgressBar.vue +117 -0
  620. package/src/components/ProtocolStep.presentation.ts +31 -0
  621. package/src/components/ProtocolStepEditor.state.ts +104 -0
  622. package/src/components/ProtocolStepEditor.story.vue +69 -0
  623. package/src/components/ProtocolStepEditor.vue +382 -0
  624. package/src/components/ProtocolStepParameterField.vue +134 -0
  625. package/src/components/RackEditor.story.vue +100 -0
  626. package/src/components/RackEditor.vue +442 -0
  627. package/src/components/ReagentEditor.story.vue +153 -0
  628. package/src/components/ReagentEditor.vue +418 -0
  629. package/src/components/ReagentList.presentation.ts +105 -0
  630. package/src/components/ReagentList.story.vue +137 -0
  631. package/src/components/ReagentList.vue +392 -0
  632. package/src/components/ResourceCard.story.vue +150 -0
  633. package/src/components/ResourceCard.vue +161 -0
  634. package/src/components/RuntimeUpdateNotice.vue +51 -0
  635. package/src/components/SampleHierarchyTree.story.vue +161 -0
  636. package/src/components/SampleHierarchyTree.vue +246 -0
  637. package/src/components/SampleLegend.story.vue +91 -0
  638. package/src/components/SampleLegend.vue +119 -0
  639. package/src/components/SampleSelector.colors.ts +48 -0
  640. package/src/components/SampleSelector.drag.ts +164 -0
  641. package/src/components/SampleSelector.groups.ts +109 -0
  642. package/src/components/SampleSelector.selection.ts +64 -0
  643. package/src/components/SampleSelector.story.vue +192 -0
  644. package/src/components/SampleSelector.tree.ts +48 -0
  645. package/src/components/SampleSelector.vue +451 -0
  646. package/src/components/SampleSelectorGroupNode.vue +197 -0
  647. package/src/components/SampleSelectorSampleRow.vue +68 -0
  648. package/src/components/ScheduleCalendar.story.vue +195 -0
  649. package/src/components/ScheduleCalendar.vue +414 -0
  650. package/src/components/ScientificNumber.story.vue +127 -0
  651. package/src/components/ScientificNumber.vue +197 -0
  652. package/src/components/SectionCard.story.vue +163 -0
  653. package/src/components/SectionCard.vue +98 -0
  654. package/src/components/SegmentedControl.story.vue +149 -0
  655. package/src/components/SegmentedControl.vue +94 -0
  656. package/src/components/SequenceInput.story.vue +119 -0
  657. package/src/components/SequenceInput.vue +209 -0
  658. package/src/components/SequenceProgressBar.vue +71 -0
  659. package/src/components/SettingsModal.schema.ts +74 -0
  660. package/src/components/SettingsModal.story.vue +524 -0
  661. package/src/components/SettingsModal.vue +399 -0
  662. package/src/components/Skeleton.story.vue +141 -0
  663. package/src/components/Skeleton.vue +74 -0
  664. package/src/components/SmartGroup.types.ts +98 -0
  665. package/src/components/SmartGroupFieldRecipe.groups.ts +109 -0
  666. package/src/components/SmartGroupFieldRecipe.story.vue +58 -0
  667. package/src/components/SmartGroupFieldRecipe.vue +499 -0
  668. package/src/components/SmartGroupManual.cohorts.ts +112 -0
  669. package/src/components/SmartGroupManual.story.vue +55 -0
  670. package/src/components/SmartGroupManual.vue +398 -0
  671. package/src/components/SmartGroupModal.story.vue +61 -0
  672. package/src/components/SmartGroupModal.vue +61 -0
  673. package/src/components/StatusIndicator.story.vue +99 -0
  674. package/src/components/StatusIndicator.vue +40 -0
  675. package/src/components/StepWizard.story.vue +155 -0
  676. package/src/components/StepWizard.vue +223 -0
  677. package/src/components/TagsInput.story.vue +155 -0
  678. package/src/components/TagsInput.vue +280 -0
  679. package/src/components/ThemeToggle.story.vue +36 -0
  680. package/src/components/ThemeToggle.vue +56 -0
  681. package/src/components/TimePicker.story.vue +96 -0
  682. package/src/components/TimePicker.vue +251 -0
  683. package/src/components/TimeRangeInput.story.vue +104 -0
  684. package/src/components/TimeRangeInput.vue +122 -0
  685. package/src/components/Tooltip.story.vue +138 -0
  686. package/src/components/Tooltip.vue +114 -0
  687. package/src/components/UnitInput.story.vue +194 -0
  688. package/src/components/UnitInput.vue +217 -0
  689. package/src/components/WellPlate.colors.ts +56 -0
  690. package/src/components/WellPlate.conditions.ts +100 -0
  691. package/src/components/WellPlate.geometry.ts +91 -0
  692. package/src/components/WellPlate.interaction.ts +272 -0
  693. package/src/components/WellPlate.legend.ts +8 -0
  694. package/src/components/WellPlate.rendering.ts +105 -0
  695. package/src/components/WellPlate.sampleDrop.ts +73 -0
  696. package/src/components/WellPlate.story.vue +282 -0
  697. package/src/components/WellPlate.vue +497 -0
  698. package/src/components/index.ts +139 -0
  699. package/src/components/internal/ActionItemInternal.vue +82 -0
  700. package/src/components/internal/AppTopBarPageSelectorInternal.vue +127 -0
  701. package/src/components/internal/AppTopBarPillNavInternal.vue +194 -0
  702. package/src/components/internal/CalendarGridPanelInternal.vue +120 -0
  703. package/src/components/internal/FormFieldRendererInternal.vue +68 -0
  704. package/src/components/internal/FormSectionRenderer.vue +78 -0
  705. package/src/components/internal/PlateMapEditorToolbarInternal.vue +128 -0
  706. package/src/components/internal/WellEditPopupInternal.vue +230 -0
  707. package/src/composables/analysisArtifactTarget.ts +53 -0
  708. package/src/composables/autoGroup/classKey.ts +8 -0
  709. package/src/composables/autoGroup/classify.ts +205 -0
  710. package/src/composables/autoGroup/colors.ts +6 -0
  711. package/src/composables/autoGroup/columns.ts +226 -0
  712. package/src/composables/autoGroup/compose.ts +244 -0
  713. package/src/composables/autoGroup/cooccurrence.ts +46 -0
  714. package/src/composables/autoGroup/csv-shim.ts +44 -0
  715. package/src/composables/autoGroup/fingerprint.ts +63 -0
  716. package/src/composables/autoGroup/index.ts +22 -0
  717. package/src/composables/autoGroup/replicatePreGroup.ts +124 -0
  718. package/src/composables/autoGroup/template.ts +126 -0
  719. package/src/composables/autoGroup/tokenLength.ts +53 -0
  720. package/src/composables/autoGroup/tokenize.ts +41 -0
  721. package/src/composables/autoGroup/vocab.json +66 -0
  722. package/src/composables/autoGroupConstants.ts +4 -0
  723. package/src/composables/autoGroupGrouping.ts +148 -0
  724. package/src/composables/controlComponentBindings.ts +80 -0
  725. package/src/composables/controlSchemaAdapters.ts +199 -0
  726. package/src/composables/controlSchemaDoseDesign.ts +222 -0
  727. package/src/composables/controlSchemaFormFields.ts +64 -0
  728. package/src/composables/controlSchemaLayout.ts +59 -0
  729. package/src/composables/controlSchemaModel.ts +172 -0
  730. package/src/composables/controlSchemaNormalize.ts +101 -0
  731. package/src/composables/controlSchemaTypes.ts +372 -0
  732. package/src/composables/controlSchemaUtils.ts +36 -0
  733. package/src/composables/controlWorkspaceOptions.ts +21 -0
  734. package/src/composables/experiment-utils.ts +105 -0
  735. package/src/composables/experimentDesignData.ts +182 -0
  736. package/src/composables/formBuilderRegistry.ts +81 -0
  737. package/src/composables/formBuilderSchema.ts +153 -0
  738. package/src/composables/index.ts +530 -0
  739. package/src/composables/platformContextHelpers.ts +81 -0
  740. package/src/composables/pluginEndpointBuilder.ts +267 -0
  741. package/src/composables/pluginFrontendBase.ts +22 -0
  742. package/src/composables/protocolTemplateCatalog.ts +325 -0
  743. package/src/composables/useAnalysisArtifacts.ts +361 -0
  744. package/src/composables/useApi.ts +319 -0
  745. package/src/composables/useAppExperiment.ts +228 -0
  746. package/src/composables/useAsync.ts +323 -0
  747. package/src/composables/useAuth.ts +458 -0
  748. package/src/composables/useAutoGroup.ts +682 -0
  749. package/src/composables/useAutoGroupInputSources.ts +147 -0
  750. package/src/composables/useBioTemplateComponents.ts +105 -0
  751. package/src/composables/useBioTemplateControls.ts +41 -0
  752. package/src/composables/useBioTemplatePackWorkspace.ts +193 -0
  753. package/src/composables/useBioTemplatePresetWorkspace.ts +326 -0
  754. package/src/composables/useBioTemplateWorkspace.ts +141 -0
  755. package/src/composables/useCalendarGrid.ts +140 -0
  756. package/src/composables/useChemicalFormula.ts +275 -0
  757. package/src/composables/useCommandHistory.ts +113 -0
  758. package/src/composables/useConcentrationUnits.ts +246 -0
  759. package/src/composables/useControlSchema.ts +135 -0
  760. package/src/composables/useControlWorkspace.ts +209 -0
  761. package/src/composables/useDebouncedWatch.ts +119 -0
  762. package/src/composables/useDoseCalculator.ts +370 -0
  763. package/src/composables/useDropdownState.ts +83 -0
  764. package/src/composables/useEventListener.ts +111 -0
  765. package/src/composables/useEventStream.ts +314 -0
  766. package/src/composables/useExpansionSet.ts +117 -0
  767. package/src/composables/useExperimentData.ts +136 -0
  768. package/src/composables/useExperimentSamples.ts +142 -0
  769. package/src/composables/useExperimentSave.ts +344 -0
  770. package/src/composables/useExperimentSelector.ts +408 -0
  771. package/src/composables/useFileImport.ts +231 -0
  772. package/src/composables/useForm.ts +279 -0
  773. package/src/composables/useFormBuilder.ts +292 -0
  774. package/src/composables/useFormValidation.ts +154 -0
  775. package/src/composables/useGeneratedAnalysis.ts +1227 -0
  776. package/src/composables/useGroupAssignment.ts +148 -0
  777. package/src/composables/useJobsStatusTray.ts +493 -0
  778. package/src/composables/useListSelection.ts +158 -0
  779. package/src/composables/useMobileSupportGate.ts +80 -0
  780. package/src/composables/useOptimisticMutation.ts +107 -0
  781. package/src/composables/usePasskey.ts +216 -0
  782. package/src/composables/usePlatformContext.ts +299 -0
  783. package/src/composables/usePluginClient.ts +968 -0
  784. package/src/composables/usePluginConfig.ts +250 -0
  785. package/src/composables/usePresenceHeartbeat.ts +201 -0
  786. package/src/composables/useProtocolTemplates.ts +212 -0
  787. package/src/composables/useRackEditor.ts +223 -0
  788. package/src/composables/useReagentSeries.ts +91 -0
  789. package/src/composables/useRequestSyncState.ts +126 -0
  790. package/src/composables/useResourceCrud.ts +245 -0
  791. package/src/composables/useRuntimeAlignment.ts +184 -0
  792. package/src/composables/useSampleGroups.ts +211 -0
  793. package/src/composables/useScheduleCalendarLayout.ts +287 -0
  794. package/src/composables/useScheduleDrag.ts +245 -0
  795. package/src/composables/useSelectionLimit.ts +57 -0
  796. package/src/composables/useSequenceUtils.ts +105 -0
  797. package/src/composables/useSortedItems.ts +118 -0
  798. package/src/composables/useTemplateCollection.ts +229 -0
  799. package/src/composables/useTextSearch.ts +60 -0
  800. package/src/composables/useTheme.ts +32 -0
  801. package/src/composables/useTimeUtils.ts +155 -0
  802. package/src/composables/useToast.ts +40 -0
  803. package/src/composables/useWellPainting.ts +187 -0
  804. package/src/composables/useWellPlateAdapter.ts +147 -0
  805. package/src/composables/useWellPlateEditor.ts +425 -0
  806. package/src/composables/useWellPlateValidation.ts +85 -0
  807. package/src/generated/schema.ts +130 -0
  808. package/src/generated-ui/main.ts +21 -0
  809. package/src/histoire.setup.ts +17 -0
  810. package/src/index.ts +67 -0
  811. package/src/install.ts +39 -0
  812. package/src/instrument.ts +90 -0
  813. package/src/jobs.ts +80 -0
  814. package/src/lcms.ts +108 -0
  815. package/src/permissions.ts +144 -0
  816. package/src/plotly-dist.d.ts +3 -0
  817. package/src/stores/auth.ts +206 -0
  818. package/src/stores/experiment.ts +120 -0
  819. package/src/stores/index.ts +3 -0
  820. package/src/stores/settings.ts +232 -0
  821. package/src/styles/components/action-menu.css +59 -0
  822. package/src/styles/components/alert-box.css +150 -0
  823. package/src/styles/components/app-avatar-menu.css +156 -0
  824. package/src/styles/components/app-container.css +33 -0
  825. package/src/styles/components/app-layout.css +180 -0
  826. package/src/styles/components/app-page-selector.css +212 -0
  827. package/src/styles/components/app-pill-nav.css +133 -0
  828. package/src/styles/components/app-plugin-switcher.css +196 -0
  829. package/src/styles/components/app-sidebar.css +392 -0
  830. package/src/styles/components/app-top-bar.css +249 -0
  831. package/src/styles/components/artifact-save-dialog.css +17 -0
  832. package/src/styles/components/artifact-selector-modal.css +216 -0
  833. package/src/styles/components/audit-trail.css +143 -0
  834. package/src/styles/components/avatar.css +73 -0
  835. package/src/styles/components/batch-progress-list.css +196 -0
  836. package/src/styles/components/breadcrumb.css +64 -0
  837. package/src/styles/components/button.css +192 -0
  838. package/src/styles/components/calendar.css +192 -0
  839. package/src/styles/components/chart-container.css +69 -0
  840. package/src/styles/components/checkbox.css +210 -0
  841. package/src/styles/components/chemical-formula.css +46 -0
  842. package/src/styles/components/collapsible-card.css +393 -0
  843. package/src/styles/components/color-slider.css +110 -0
  844. package/src/styles/components/concentration-input.css +17 -0
  845. package/src/styles/components/confirm-dialog.css +59 -0
  846. package/src/styles/components/dataframe.css +554 -0
  847. package/src/styles/components/date-picker.css +243 -0
  848. package/src/styles/components/datetime-picker.css +229 -0
  849. package/src/styles/components/divider.css +63 -0
  850. package/src/styles/components/dose-calculator.css +297 -0
  851. package/src/styles/components/dropdown-button.css +280 -0
  852. package/src/styles/components/empty-state.css +135 -0
  853. package/src/styles/components/experiment-code-badge.css +33 -0
  854. package/src/styles/components/experiment-data-viewer.css +138 -0
  855. package/src/styles/components/experiment-popover.css +563 -0
  856. package/src/styles/components/experiment-selector-modal.css +341 -0
  857. package/src/styles/components/experiment-timeline.css +529 -0
  858. package/src/styles/components/file-uploader.css +310 -0
  859. package/src/styles/components/fit-panel.css +67 -0
  860. package/src/styles/components/form-builder.css +69 -0
  861. package/src/styles/components/form-field.css +48 -0
  862. package/src/styles/components/formula-input.css +103 -0
  863. package/src/styles/components/group-assigner.css +200 -0
  864. package/src/styles/components/icon-button.css +196 -0
  865. package/src/styles/components/input.css +65 -0
  866. package/src/styles/components/instrument-monitor.css +478 -0
  867. package/src/styles/components/jobs-status-tray.css +420 -0
  868. package/src/styles/components/lcms-sequence-table.css +189 -0
  869. package/src/styles/components/loading-spinner.css +67 -0
  870. package/src/styles/components/mobile-support-gate.css +119 -0
  871. package/src/styles/components/modal.css +646 -0
  872. package/src/styles/components/molecule-input.css +186 -0
  873. package/src/styles/components/multi-select.css +131 -0
  874. package/src/styles/components/number-input.css +199 -0
  875. package/src/styles/components/pill.css +188 -0
  876. package/src/styles/components/plate-map-editor.css +464 -0
  877. package/src/styles/components/plugin-icon.css +46 -0
  878. package/src/styles/components/progress-bar.css +133 -0
  879. package/src/styles/components/protocol-step-editor.css +443 -0
  880. package/src/styles/components/rack-editor.css +265 -0
  881. package/src/styles/components/radio-group.css +240 -0
  882. package/src/styles/components/reagent-editor.css +505 -0
  883. package/src/styles/components/reagent-list.css +407 -0
  884. package/src/styles/components/resource-card.css +360 -0
  885. package/src/styles/components/sample-hierarchy-tree.css +310 -0
  886. package/src/styles/components/sample-legend.css +201 -0
  887. package/src/styles/components/sample-selector.css +682 -0
  888. package/src/styles/components/schedule-calendar.css +472 -0
  889. package/src/styles/components/scientific-number.css +63 -0
  890. package/src/styles/components/section-card.css +161 -0
  891. package/src/styles/components/segmented-control.css +194 -0
  892. package/src/styles/components/select.css +77 -0
  893. package/src/styles/components/sequence-input.css +184 -0
  894. package/src/styles/components/sequence-progress-bar.css +63 -0
  895. package/src/styles/components/settings-modal.css +76 -0
  896. package/src/styles/components/skeleton.css +49 -0
  897. package/src/styles/components/slider.css +74 -0
  898. package/src/styles/components/smart-group.css +734 -0
  899. package/src/styles/components/status-indicator.css +66 -0
  900. package/src/styles/components/step-wizard.css +192 -0
  901. package/src/styles/components/tabs.css +103 -0
  902. package/src/styles/components/tags-input.css +195 -0
  903. package/src/styles/components/textarea.css +81 -0
  904. package/src/styles/components/theme-toggle.css +6 -0
  905. package/src/styles/components/time-picker.css +171 -0
  906. package/src/styles/components/time-range-input.css +42 -0
  907. package/src/styles/components/toast.css +91 -0
  908. package/src/styles/components/toggle.css +226 -0
  909. package/src/styles/components/tooltip.css +91 -0
  910. package/src/styles/components/unit-input.css +123 -0
  911. package/src/styles/components/well-edit-popup.css +258 -0
  912. package/src/styles/components/well-plate.css +286 -0
  913. package/src/styles/index.css +93 -0
  914. package/src/styles/variables.css +1150 -0
  915. package/src/tailwind.preset.ts +66 -0
  916. package/src/templates/adapters.ts +137 -0
  917. package/src/templates/assayLookups.ts +33 -0
  918. package/src/templates/assayMatrixAdapters.ts +78 -0
  919. package/src/templates/assayMatrixBuilder.ts +59 -0
  920. package/src/templates/assayNormalizers.ts +34 -0
  921. package/src/templates/builderDefaults.ts +11 -0
  922. package/src/templates/builderIdUtils.ts +20 -0
  923. package/src/templates/builderPresetControls.ts +165 -0
  924. package/src/templates/builderReadUtils.ts +57 -0
  925. package/src/templates/builders.ts +122 -0
  926. package/src/templates/calibrationCurveAdapters.ts +59 -0
  927. package/src/templates/calibrationCurveBuilder.ts +99 -0
  928. package/src/templates/calibrationNormalizers.ts +60 -0
  929. package/src/templates/catalog.ts +245 -0
  930. package/src/templates/componentBindingCatalog.ts +90 -0
  931. package/src/templates/componentBindingHelpers.ts +93 -0
  932. package/src/templates/componentBindings.ts +253 -0
  933. package/src/templates/componentDoseResponseProps.ts +42 -0
  934. package/src/templates/componentGenericProps.ts +77 -0
  935. package/src/templates/componentPlateHelpers.ts +29 -0
  936. package/src/templates/componentPlateMapProps.ts +32 -0
  937. package/src/templates/componentPropsFactory.ts +21 -0
  938. package/src/templates/componentQpcrPlateProps.ts +28 -0
  939. package/src/templates/componentTargetResolvers.ts +69 -0
  940. package/src/templates/componentTemplateProps.ts +78 -0
  941. package/src/templates/controlSchemaClone.ts +32 -0
  942. package/src/templates/controlSchemaConstants.ts +11 -0
  943. package/src/templates/controlSchemaMerge.ts +40 -0
  944. package/src/templates/controlSchemaTargets.ts +87 -0
  945. package/src/templates/controlSchemaTypes.ts +20 -0
  946. package/src/templates/controlSchemas.ts +104 -0
  947. package/src/templates/defaultBioTemplateBuilder.ts +124 -0
  948. package/src/templates/doseResponseAdapters.ts +45 -0
  949. package/src/templates/doseResponseBuilder.ts +44 -0
  950. package/src/templates/elisaAssayCollectionBuilder.ts +62 -0
  951. package/src/templates/flowCytometryAssayCollectionBuilder.ts +41 -0
  952. package/src/templates/flowCytometryPanelBuilder.ts +53 -0
  953. package/src/templates/flowNormalizers.ts +58 -0
  954. package/src/templates/flowPanelAdapters.ts +58 -0
  955. package/src/templates/index.ts +326 -0
  956. package/src/templates/instrumentRunAdapterHelpers.ts +94 -0
  957. package/src/templates/instrumentRunAdapters.ts +163 -0
  958. package/src/templates/instrumentRunBuilder.ts +97 -0
  959. package/src/templates/lcmsBatchCollectionBuilder.ts +38 -0
  960. package/src/templates/lookup.ts +18 -0
  961. package/src/templates/packs.ts +162 -0
  962. package/src/templates/plateGeometry.ts +62 -0
  963. package/src/templates/plateMapAdapters.ts +36 -0
  964. package/src/templates/plateMapBuilder.ts +43 -0
  965. package/src/templates/presetControlSchemas.ts +258 -0
  966. package/src/templates/presets.ts +179 -0
  967. package/src/templates/protocolAdapters.ts +69 -0
  968. package/src/templates/protocolNormalizers.ts +37 -0
  969. package/src/templates/protocolStepsBuilder.ts +36 -0
  970. package/src/templates/qpcrAdapters.ts +104 -0
  971. package/src/templates/qpcrExpressionCollectionBuilder.ts +33 -0
  972. package/src/templates/qpcrPlateBuilder.ts +96 -0
  973. package/src/templates/reagentAdapters.ts +77 -0
  974. package/src/templates/reagentListBuilder.ts +30 -0
  975. package/src/templates/runNormalizers.ts +63 -0
  976. package/src/templates/sampleNormalizers.ts +58 -0
  977. package/src/templates/samplePrepAdapters.ts +63 -0
  978. package/src/templates/samplePrepBuilder.ts +51 -0
  979. package/src/templates/sampleSheetAdapters.ts +75 -0
  980. package/src/templates/sampleSheetBuilder.ts +23 -0
  981. package/src/templates/targetedMetabolomicsCollectionBuilder.ts +79 -0
  982. package/src/templates/targetedMetabolomicsHelpers.ts +102 -0
  983. package/src/templates/templateAdapterTypes.ts +58 -0
  984. package/src/templates/templateControlSchemas.ts +320 -0
  985. package/src/templates/templateCreateOptions.ts +208 -0
  986. package/src/templates/templateEnvelopes.ts +137 -0
  987. package/src/templates/templatePackCollectionBuilder.ts +23 -0
  988. package/src/templates/templatePresetCollectionBuilder.ts +139 -0
  989. package/src/templates/templateQpcrTypes.ts +48 -0
  990. package/src/templates/templateValidators.ts +414 -0
  991. package/src/templates/timeCourseAdapters.ts +73 -0
  992. package/src/templates/timeCourseBuilder.ts +64 -0
  993. package/src/templates/types.ts +493 -0
  994. package/src/templates/wellPlateScreenCollectionBuilder.ts +36 -0
  995. package/src/templates/westernBlotAssayCollectionBuilder.ts +68 -0
  996. package/src/types/analysisArtifactTypes.ts +176 -0
  997. package/src/types/auth.ts +59 -0
  998. package/src/types/auto-group.ts +157 -0
  999. package/src/types/componentLabTypes.ts +235 -0
  1000. package/src/types/componentWorkflowTypes.ts +190 -0
  1001. package/src/types/components.ts +527 -0
  1002. package/src/types/form-builder.ts +207 -0
  1003. package/src/types/generated-ui.ts +189 -0
  1004. package/src/types/index.ts +299 -0
  1005. package/src/types/instrument.ts +62 -0
  1006. package/src/types/jobs.ts +70 -0
  1007. package/src/types/platform.ts +132 -0
  1008. package/src/utils/color.ts +96 -0
  1009. package/src/utils/formModelSync.ts +52 -0
  1010. package/src/utils/items.ts +28 -0
  1011. package/src/utils/options.ts +23 -0
  1012. package/src/utils/pluginIcon.ts +190 -0
  1013. package/src/utils/rack.ts +209 -0
  1014. package/src/utils/sampleGroupPath.ts +19 -0
  1015. package/dist/ExperimentPopover-C1e0cd94.js +0 -3
  1016. package/dist/ExperimentSelectorModal-DCWTFyos.js +0 -4
  1017. package/dist/SettingsModal-Bpybht86.js +0 -5
@@ -0,0 +1,1227 @@
1
+ import {
2
+ computed,
3
+ onScopeDispose,
4
+ ref,
5
+ shallowRef,
6
+ type ComputedRef,
7
+ type Ref,
8
+ type ShallowRef,
9
+ } from 'vue'
10
+
11
+ import type {
12
+ JobsStatusTrayAdapter,
13
+ PluginJobCenterSource,
14
+ } from './useJobsStatusTray'
15
+ import { useEventStream, type EventStreamMessage } from './useEventStream'
16
+ import { normalizeJobState } from '../jobs'
17
+ import type {
18
+ GeneratedAnalysisJob,
19
+ GeneratedAnalysisJobInput,
20
+ GeneratedAnalysisResult,
21
+ GeneratedSession,
22
+ GeneratedAnalysisManifest,
23
+ PluginJobPathInput,
24
+ PluginJobPathUpload,
25
+ PluginJobStagedPartInput,
26
+ PluginJobStagedPartUploadResult,
27
+ PluginJobStaging,
28
+ PluginJobStagingPagination,
29
+ } from '../types/generated-ui'
30
+
31
+ export type GeneratedAnalysisTransport = (
32
+ input: RequestInfo | URL,
33
+ init?: RequestInit,
34
+ ) => Promise<Response>
35
+
36
+ export interface GeneratedAnalysisTransportOptions {
37
+ fetcher?: typeof fetch
38
+ getAccessToken?: () => string | null | undefined
39
+ }
40
+
41
+ export interface UsePluginJobsOptions {
42
+ baseUrl: string
43
+ transport?: GeneratedAnalysisTransport
44
+ storage?: Storage
45
+ createId?: () => string
46
+ pollInterval?: number
47
+ stream?: boolean
48
+ streamReconnectDelay?: number
49
+ immediate?: boolean
50
+ }
51
+
52
+ export type GeneratedAnalysisOptions = UsePluginJobsOptions
53
+
54
+ export interface PluginJobWaitOptions {
55
+ signal?: AbortSignal
56
+ rejectOnFailure?: boolean
57
+ }
58
+
59
+ export interface PluginJobRequestOptions {
60
+ signal?: AbortSignal
61
+ }
62
+
63
+ interface GeneratedSessionStorage {
64
+ getItem: (key: string) => string | null
65
+ setItem: (key: string, value: string) => void
66
+ }
67
+
68
+ export interface UsePluginJobsReturn extends PluginJobCenterSource {
69
+ manifest: ShallowRef<GeneratedAnalysisManifest | null>
70
+ session: ShallowRef<GeneratedSession | null>
71
+ jobs: ShallowRef<GeneratedAnalysisJob[]>
72
+ latestResult: ComputedRef<GeneratedAnalysisResult | null>
73
+ isLoading: Ref<boolean>
74
+ isSubmitting: Ref<boolean>
75
+ error: ShallowRef<string | null>
76
+ status: ShallowRef<'idle' | 'initializing' | 'ready' | 'paused' | 'error' | 'disposed'>
77
+ initialize: () => Promise<void>
78
+ ready: () => Promise<void>
79
+ uploadPath: (
80
+ input: PluginJobPathInput,
81
+ definitionId?: string,
82
+ ) => Promise<PluginJobPathUpload>
83
+ uploadStagedPart: <Metadata>(
84
+ input: PluginJobStagedPartInput<Metadata>,
85
+ ) => Promise<PluginJobStagedPartUploadResult<Metadata>>
86
+ getJobStaging: <Metadata = unknown>(
87
+ job: GeneratedAnalysisJob | string,
88
+ pagination?: PluginJobStagingPagination,
89
+ ) => Promise<PluginJobStaging<Metadata>>
90
+ sealJob: (job: GeneratedAnalysisJob | string) => Promise<GeneratedAnalysisJob>
91
+ submit: (definitionId: string, inputs: Record<string, unknown>) => Promise<GeneratedAnalysisJob>
92
+ loadResult: (job: GeneratedAnalysisJob) => Promise<GeneratedAnalysisResult | null>
93
+ resultForJob: (jobId: string) => GeneratedAnalysisResult | null
94
+ refreshJobs: () => Promise<GeneratedAnalysisJob[]>
95
+ refresh: () => Promise<GeneratedAnalysisJob[]>
96
+ getJob: (jobId: string, options?: PluginJobRequestOptions) => Promise<GeneratedAnalysisJob>
97
+ waitForJob: (
98
+ job: GeneratedAnalysisJob | string,
99
+ options?: PluginJobWaitOptions,
100
+ ) => Promise<GeneratedAnalysisJob>
101
+ jobsFor: (definitionId: string) => GeneratedAnalysisJob[]
102
+ cancelJob: (job: GeneratedAnalysisJob | string) => Promise<GeneratedAnalysisJob>
103
+ deleteJob: (job: GeneratedAnalysisJob | string) => Promise<boolean>
104
+ clearFinishedJobs: (definitionId?: string) => Promise<number>
105
+ pause: () => void
106
+ resume: () => void
107
+ dispose: () => void
108
+ /** @deprecated Pass the runtime itself to SDK job UI as its source. */
109
+ jobsAdapter: JobsStatusTrayAdapter
110
+ }
111
+
112
+ export type UseGeneratedAnalysisReturn = UsePluginJobsReturn
113
+
114
+ const ACTIVE_STATUSES = new Set(['awaiting_input', 'queued', 'running'])
115
+ const AUTH_TOKEN_KEY = 'mint-auth-token'
116
+ const SESSION_NOT_FOUND_DETAIL = 'Session not found'
117
+ const MAX_RESULT_CACHE_ENTRIES = 32
118
+ const MAX_RESULT_RETRIES = 3
119
+ const DOWNLOAD_URL_REFRESH_SKEW_MS = 30_000
120
+
121
+ interface PluginJobWaiter {
122
+ resolve: (job: GeneratedAnalysisJob) => void
123
+ reject: (cause: unknown) => void
124
+ rejectOnFailure: boolean
125
+ signal?: AbortSignal
126
+ abort?: () => void
127
+ }
128
+
129
+ type PluginJobStreamData = GeneratedAnalysisJobInput | {
130
+ jobs: GeneratedAnalysisJobInput[]
131
+ }
132
+
133
+ class GeneratedAnalysisRequestError extends Error {
134
+ constructor(
135
+ readonly status: number,
136
+ readonly detail: unknown,
137
+ ) {
138
+ super(typeof detail === 'string' ? detail : `Request failed (${status})`)
139
+ this.name = 'GeneratedAnalysisRequestError'
140
+ }
141
+ }
142
+
143
+ export function createGeneratedAnalysisTransport(
144
+ options: GeneratedAnalysisTransportOptions = {},
145
+ ): GeneratedAnalysisTransport {
146
+ const fetcher = options.fetcher ?? globalThis.fetch.bind(globalThis)
147
+ const getAccessToken = options.getAccessToken ?? readStoredAccessToken
148
+
149
+ return async (input, init = {}) => {
150
+ const headers = new Headers(init.headers)
151
+ const accessToken = getAccessToken()
152
+ if (accessToken && !headers.has('Authorization')) {
153
+ headers.set('Authorization', `Bearer ${accessToken}`)
154
+ }
155
+ return await fetcher(input, {
156
+ credentials: 'same-origin',
157
+ ...init,
158
+ headers,
159
+ })
160
+ }
161
+ }
162
+
163
+ export function useGeneratedAnalysis(
164
+ options: GeneratedAnalysisOptions,
165
+ ): UseGeneratedAnalysisReturn {
166
+ return createPluginJobs(options, '/generated/manifest')
167
+ }
168
+
169
+ /**
170
+ * Connect a custom plugin frontend to the SDK-owned job lifecycle.
171
+ *
172
+ * @example Recommended
173
+ * ```ts
174
+ * const jobs = usePluginJobs({ baseUrl: '/api/leaf/jobs' })
175
+ * await jobs.initialize()
176
+ * ```
177
+ */
178
+ export function usePluginJobs(
179
+ options: UsePluginJobsOptions,
180
+ ): UsePluginJobsReturn {
181
+ return createPluginJobs(options)
182
+ }
183
+
184
+ function createPluginJobs(
185
+ options: UsePluginJobsOptions,
186
+ manifestPath?: string,
187
+ ): UsePluginJobsReturn {
188
+ const baseUrl = options.baseUrl.replace(/\/$/, '')
189
+ const transport = options.transport ?? createGeneratedAnalysisTransport()
190
+ const storage = createGeneratedSessionStorage(options.storage)
191
+ const createId = options.createId ?? (() => globalThis.crypto.randomUUID())
192
+ const pollInterval = options.pollInterval ?? 750
193
+ const streamEnabled = options.stream ?? true
194
+ const streamReconnectDelay = options.streamReconnectDelay ?? 1_500
195
+ const tabKey = `mint:generated:${baseUrl}:tab`
196
+ const sessionKey = `mint:generated:${baseUrl}:session`
197
+ const manifest = shallowRef<GeneratedAnalysisManifest | null>(null)
198
+ const session = shallowRef<GeneratedSession | null>(null)
199
+ const jobs = shallowRef<GeneratedAnalysisJob[]>([])
200
+ const jobRevisions = new Map<string, number>()
201
+ const pendingJobOperations = new Map<string, number>()
202
+ const resultCache = shallowRef<Record<string, GeneratedAnalysisResult>>({})
203
+ const isLoading = ref(false)
204
+ const isSubmitting = ref(false)
205
+ const error = shallowRef<string | null>(null)
206
+ const status = shallowRef<'idle' | 'initializing' | 'ready' | 'paused' | 'error' | 'disposed'>('idle')
207
+ let pollTimer: ReturnType<typeof setTimeout> | undefined
208
+ let disposed = false
209
+ let paused = false
210
+ let initialized = false
211
+ let initializePromise: Promise<void> | undefined
212
+ let refreshPromise: Promise<GeneratedAnalysisJob[]> | undefined
213
+ let initializeRefreshPromise: Promise<GeneratedAnalysisJob[]> | undefined
214
+ let jobsRevision = 0
215
+ let resultCacheOrder: string[] = []
216
+ const resultRequests = new Map<string, Promise<GeneratedAnalysisResult | null>>()
217
+ const jobWaiters = new Map<string, Set<PluginJobWaiter>>()
218
+ const resultRetryAttempts = new Map<string, number>()
219
+ const resultRetryTimers = new Map<string, ReturnType<typeof setTimeout>>()
220
+ const downloadRefreshTimers = new Map<string, ReturnType<typeof setTimeout>>()
221
+ const jobStream = useEventStream<PluginJobStreamData>(`${baseUrl}/jobs/events`, {
222
+ transport,
223
+ immediate: false,
224
+ reconnectDelayMs: streamReconnectDelay,
225
+ parseJson: true,
226
+ errorLabel: 'Job stream',
227
+ onOpen: clearPoll,
228
+ onMessage: applyJobStreamMessage,
229
+ onError: schedulePollForActiveJobs,
230
+ onClose: schedulePollForActiveJobs,
231
+ })
232
+
233
+ const latestResult = computed<GeneratedAnalysisResult | null>(() => {
234
+ const job = currentCompletedJob()
235
+ return job ? resultForJob(job.job_id) : null
236
+ })
237
+
238
+ function initialize(): Promise<void> {
239
+ if (disposed) return Promise.reject(new Error('Plugin jobs runtime is disposed'))
240
+ if (initialized) return Promise.resolve()
241
+ if (initializePromise) return initializePromise
242
+ isLoading.value = true
243
+ error.value = null
244
+ status.value = 'initializing'
245
+ const pending = (async () => {
246
+ if (manifestPath) {
247
+ const loadedManifest = await request<GeneratedAnalysisManifest>(manifestPath, { method: 'GET' })
248
+ if (!disposed) manifest.value = loadedManifest
249
+ }
250
+ const resolvedSession = await resolveSession()
251
+ if (disposed) return
252
+ session.value = resolvedSession
253
+ await refreshJobs()
254
+ if (disposed) return
255
+ initialized = true
256
+ status.value = paused ? 'paused' : 'ready'
257
+ if (streamEnabled && !paused) jobStream.start()
258
+ })().catch((cause: unknown) => {
259
+ if (!disposed) {
260
+ error.value = errorMessage(cause, 'Failed to initialize generated analysis')
261
+ status.value = 'error'
262
+ }
263
+ throw cause
264
+ }).finally(() => {
265
+ if (initializePromise === pending) initializePromise = undefined
266
+ if (!disposed) isLoading.value = false
267
+ })
268
+ initializePromise = pending
269
+ return pending
270
+ }
271
+
272
+ async function resolveSession(): Promise<GeneratedSession> {
273
+ let tabId = storage.getItem(tabKey)
274
+ if (!tabId) {
275
+ tabId = createId()
276
+ storage.setItem(tabKey, tabId)
277
+ }
278
+ const storedSessionId = storage.getItem(sessionKey)
279
+ const payload = await request<{ sessions: GeneratedSession[] }>('/jobs/sessions', { method: 'GET' })
280
+ const existing = payload.sessions.find(candidate => (
281
+ candidate.id === storedSessionId && candidate.tab_id === tabId
282
+ ))
283
+ if (existing) return existing
284
+ const created = await request<GeneratedSession>('/jobs/sessions', {
285
+ method: 'POST',
286
+ body: JSON.stringify({ tab_id: tabId }),
287
+ })
288
+ storage.setItem(sessionKey, created.id)
289
+ return created
290
+ }
291
+
292
+ async function submit(
293
+ definitionId: string,
294
+ inputs: Record<string, unknown>,
295
+ ): Promise<GeneratedAnalysisJob> {
296
+ if (!session.value) throw new Error('Generated analysis session is not initialized')
297
+ const clientRequestId = createId()
298
+ isSubmitting.value = true
299
+ error.value = null
300
+ try {
301
+ let jobInput: GeneratedAnalysisJobInput
302
+ try {
303
+ jobInput = await submitToSession(
304
+ session.value.id,
305
+ clientRequestId,
306
+ await stagePathInputs(inputs, session.value.id, definitionId),
307
+ )
308
+ } catch (cause) {
309
+ if (!isSessionNotFoundError(cause)) throw cause
310
+ session.value = await resolveSession()
311
+ jobInput = await submitToSession(
312
+ session.value.id,
313
+ clientRequestId,
314
+ await stagePathInputs(inputs, session.value.id, definitionId),
315
+ )
316
+ }
317
+ const job = upsertJob(jobInput, definitionId)
318
+ if (!jobStream.isConnected.value) schedulePoll()
319
+ return job
320
+ } catch (cause) {
321
+ error.value = errorMessage(cause, 'Failed to submit analysis')
322
+ throw cause
323
+ } finally {
324
+ isSubmitting.value = false
325
+ }
326
+
327
+ function submitToSession(
328
+ sessionId: string,
329
+ requestId: string,
330
+ stagedInputs: Record<string, unknown>,
331
+ ): Promise<GeneratedAnalysisJobInput> {
332
+ return request<GeneratedAnalysisJobInput>('/jobs', {
333
+ method: 'POST',
334
+ body: JSON.stringify({
335
+ definition_id: definitionId,
336
+ inputs: stagedInputs,
337
+ session_id: sessionId,
338
+ client_request_id: requestId,
339
+ }),
340
+ })
341
+ }
342
+ }
343
+
344
+ async function uploadPath(
345
+ input: PluginJobPathInput,
346
+ definitionId?: string,
347
+ ): Promise<PluginJobPathUpload> {
348
+ if (!session.value) throw new Error('Generated analysis session is not initialized')
349
+ return uploadPathForSession(input, session.value.id, definitionId)
350
+ }
351
+
352
+ async function uploadStagedPart<Metadata>(
353
+ input: PluginJobStagedPartInput<Metadata>,
354
+ ): Promise<PluginJobStagedPartUploadResult<Metadata>> {
355
+ const jobId = jobIdFromReference(input.job)
356
+ const body = new FormData()
357
+ body.append('file', input.file)
358
+ body.append('metadata', JSON.stringify(input.metadata))
359
+ if (input.expectedSize !== undefined) {
360
+ body.append('expected_size', String(input.expectedSize))
361
+ }
362
+ if (input.expectedSha256 !== undefined) {
363
+ body.append('expected_sha256', input.expectedSha256)
364
+ }
365
+ return request<PluginJobStagedPartUploadResult<Metadata>>(
366
+ `/jobs/${encodeURIComponent(jobId)}/parts/${encodeStagedPartKey(input.key)}`,
367
+ { method: 'PUT', body, signal: input.signal },
368
+ )
369
+ }
370
+
371
+ function getJobStaging<Metadata = unknown>(
372
+ job: GeneratedAnalysisJob | string,
373
+ pagination: PluginJobStagingPagination = {},
374
+ ): Promise<PluginJobStaging<Metadata>> {
375
+ const jobId = jobIdFromReference(job)
376
+ const query = new URLSearchParams()
377
+ if (pagination.offset !== undefined) query.set('offset', String(pagination.offset))
378
+ if (pagination.limit !== undefined) query.set('limit', String(pagination.limit))
379
+ const suffix = query.size > 0 ? `?${query}` : ''
380
+ return request<PluginJobStaging<Metadata>>(
381
+ `/jobs/${encodeURIComponent(jobId)}/staging${suffix}`,
382
+ { method: 'GET' },
383
+ )
384
+ }
385
+
386
+ async function sealJob(
387
+ job: GeneratedAnalysisJob | string,
388
+ ): Promise<GeneratedAnalysisJob> {
389
+ const jobId = jobIdFromReference(job)
390
+ const revision = beginJobOperation(jobId)
391
+ try {
392
+ const updated = await request<GeneratedAnalysisJobInput>(
393
+ `/jobs/${encodeURIComponent(jobId)}/seal`,
394
+ { method: 'POST' },
395
+ )
396
+ return mergeJobResponse(updated, revision)
397
+ } finally {
398
+ finishJobOperation(jobId)
399
+ }
400
+ }
401
+
402
+ async function uploadPathForSession(
403
+ input: PluginJobPathInput,
404
+ sessionId: string,
405
+ definitionId?: string,
406
+ ): Promise<PluginJobPathUpload> {
407
+ if (!input.files.length || input.files.length !== input.relativePaths.length) {
408
+ throw new Error('Path upload requires one relative path for each file')
409
+ }
410
+ const body = new FormData()
411
+ body.append('session_id', sessionId)
412
+ if (input.rootName) body.append('root_name', input.rootName)
413
+ for (let index = 0; index < input.files.length; index += 1) {
414
+ body.append('files', input.files[index]!)
415
+ body.append('relative_paths', input.relativePaths[index]!)
416
+ }
417
+ const headers: Record<string, string> = {
418
+ 'X-Mint-Job-Session': sessionId,
419
+ }
420
+ if (definitionId) headers['X-Mint-Job-Definition'] = definitionId
421
+ return request<PluginJobPathUpload>('/jobs/uploads', {
422
+ method: 'POST',
423
+ body,
424
+ signal: input.signal,
425
+ headers,
426
+ })
427
+ }
428
+
429
+ async function stagePathInputs(
430
+ inputs: Record<string, unknown>,
431
+ sessionId: string,
432
+ definitionId: string,
433
+ ): Promise<Record<string, unknown>> {
434
+ return await stagePathValue(inputs, sessionId, definitionId) as Record<string, unknown>
435
+ }
436
+
437
+ async function stagePathValue(
438
+ value: unknown,
439
+ sessionId: string,
440
+ definitionId: string,
441
+ ): Promise<unknown> {
442
+ if (typeof File !== 'undefined' && value instanceof File) {
443
+ const upload = await uploadPathForSession({
444
+ kind: 'path',
445
+ files: [value],
446
+ relativePaths: [value.name],
447
+ }, sessionId, definitionId)
448
+ return { token: upload.token }
449
+ }
450
+ if (isPluginJobPathInput(value)) {
451
+ const upload = await uploadPathForSession(value, sessionId, definitionId)
452
+ return { token: upload.token }
453
+ }
454
+ if (Array.isArray(value)) {
455
+ return await Promise.all(value.map(
456
+ item => stagePathValue(item, sessionId, definitionId),
457
+ ))
458
+ }
459
+ if (value && typeof value === 'object') {
460
+ const entries = await Promise.all(Object.entries(value).map(async ([key, child]) => (
461
+ [key, await stagePathValue(child, sessionId, definitionId)] as const
462
+ )))
463
+ return Object.fromEntries(entries)
464
+ }
465
+ return value
466
+ }
467
+
468
+ function refreshJobs(): Promise<GeneratedAnalysisJob[]> {
469
+ if (disposed) return Promise.resolve(jobs.value)
470
+ if (refreshPromise) return refreshPromise
471
+ const revision = jobsRevision
472
+ const pending = request<{ jobs: GeneratedAnalysisJobInput[] }>('/jobs', { method: 'GET' })
473
+ .then((payload) => {
474
+ if (disposed) return jobs.value
475
+ if (revision !== jobsRevision) {
476
+ if (jobs.value.some(job => ACTIVE_STATUSES.has(job.status))) schedulePoll()
477
+ return jobs.value
478
+ }
479
+ replaceJobs(payload.jobs)
480
+ error.value = null
481
+ if (!paused && !jobStream.isConnected.value && jobs.value.some(job => ACTIVE_STATUSES.has(job.status))) {
482
+ schedulePoll()
483
+ }
484
+ return jobs.value
485
+ }).finally(() => {
486
+ if (refreshPromise === pending) refreshPromise = undefined
487
+ })
488
+ refreshPromise = pending
489
+ return pending
490
+ }
491
+
492
+ function refresh(): Promise<GeneratedAnalysisJob[]> {
493
+ if (disposed) return Promise.resolve(jobs.value)
494
+ if (initialized) return refreshJobs()
495
+ if (initializeRefreshPromise) return initializeRefreshPromise
496
+ const pending = initialize()
497
+ .then(() => jobs.value)
498
+ .finally(() => {
499
+ if (initializeRefreshPromise === pending) initializeRefreshPromise = undefined
500
+ })
501
+ initializeRefreshPromise = pending
502
+ return pending
503
+ }
504
+
505
+ async function getJob(
506
+ jobId: string,
507
+ options: PluginJobRequestOptions = {},
508
+ ): Promise<GeneratedAnalysisJob> {
509
+ const revision = beginJobOperation(jobId)
510
+ try {
511
+ const job = await request<GeneratedAnalysisJobInput>(
512
+ `/jobs/${encodeURIComponent(jobId)}`,
513
+ { method: 'GET', signal: options.signal },
514
+ )
515
+ return mergeJobResponse(job, revision)
516
+ } finally {
517
+ finishJobOperation(jobId)
518
+ }
519
+ }
520
+
521
+ function waitForJob(
522
+ job: GeneratedAnalysisJob | string,
523
+ options: PluginJobWaitOptions = {},
524
+ ): Promise<GeneratedAnalysisJob> {
525
+ if (disposed) return Promise.reject(new Error('Plugin jobs runtime is disposed'))
526
+ if (options.signal?.aborted) {
527
+ return Promise.reject(pluginJobWaitAbort(options.signal))
528
+ }
529
+ const jobId = jobIdFromReference(job)
530
+ const current = typeof job === 'string'
531
+ ? jobs.value.find(candidate => candidate.job_id === jobId)
532
+ : jobs.value.find(candidate => candidate.job_id === jobId) ?? job
533
+ if (current && !ACTIVE_STATUSES.has(current.status)) {
534
+ return terminalWaitResult(current, options.rejectOnFailure === true)
535
+ }
536
+ if (paused) return Promise.reject(new Error('Plugin jobs runtime is paused'))
537
+ return new Promise<GeneratedAnalysisJob>((resolve, reject) => {
538
+ const waiter: PluginJobWaiter = {
539
+ resolve,
540
+ reject,
541
+ rejectOnFailure: options.rejectOnFailure === true,
542
+ signal: options.signal,
543
+ }
544
+ if (options.signal) {
545
+ waiter.abort = () => {
546
+ removeJobWaiter(jobId, waiter)
547
+ reject(pluginJobWaitAbort(options.signal!))
548
+ }
549
+ options.signal.addEventListener('abort', waiter.abort, { once: true })
550
+ }
551
+ const waiting = jobWaiters.get(jobId) ?? new Set<PluginJobWaiter>()
552
+ waiting.add(waiter)
553
+ jobWaiters.set(jobId, waiting)
554
+ if (!paused) {
555
+ if (streamEnabled) jobStream.start()
556
+ if (!jobStream.isConnected.value) schedulePoll()
557
+ }
558
+ void getJob(jobId, { signal: options.signal }).catch((cause: unknown) => {
559
+ if (current && isRetryableJobLookupError(cause)) {
560
+ if (!jobStream.isConnected.value) schedulePoll()
561
+ return
562
+ }
563
+ removeJobWaiter(jobId, waiter)
564
+ reject(cause)
565
+ })
566
+ })
567
+ }
568
+
569
+ function settleJobWaiters(job: GeneratedAnalysisJob): void {
570
+ if (ACTIVE_STATUSES.has(job.status)) return
571
+ const waiting = jobWaiters.get(job.job_id)
572
+ if (!waiting) return
573
+ jobWaiters.delete(job.job_id)
574
+ for (const waiter of waiting) {
575
+ if (waiter.abort && waiter.signal) {
576
+ waiter.signal.removeEventListener('abort', waiter.abort)
577
+ }
578
+ void terminalWaitResult(job, waiter.rejectOnFailure).then(
579
+ waiter.resolve,
580
+ waiter.reject,
581
+ )
582
+ }
583
+ }
584
+
585
+ function rejectJobWaiters(jobId: string, cause: unknown): void {
586
+ const waiting = jobWaiters.get(jobId)
587
+ if (!waiting) return
588
+ jobWaiters.delete(jobId)
589
+ for (const waiter of waiting) {
590
+ if (waiter.abort && waiter.signal) {
591
+ waiter.signal.removeEventListener('abort', waiter.abort)
592
+ }
593
+ waiter.reject(cause)
594
+ }
595
+ }
596
+
597
+ function removeJobWaiter(jobId: string, waiter: PluginJobWaiter): void {
598
+ const waiting = jobWaiters.get(jobId)
599
+ if (!waiting) return
600
+ waiting.delete(waiter)
601
+ if (waiting.size === 0) jobWaiters.delete(jobId)
602
+ if (waiter.abort && waiter.signal) {
603
+ waiter.signal.removeEventListener('abort', waiter.abort)
604
+ }
605
+ }
606
+
607
+ function jobsFor(definitionId: string): GeneratedAnalysisJob[] {
608
+ return jobs.value.filter(job => job.definition_id === definitionId)
609
+ }
610
+
611
+ function canonicalJob(
612
+ input: GeneratedAnalysisJobInput,
613
+ fallbackDefinitionId?: string,
614
+ ): GeneratedAnalysisJob {
615
+ const normalized = normalizeJobState(input)
616
+ const definitionId = normalized.definition_id ?? fallbackDefinitionId ?? null
617
+ return {
618
+ ...normalized,
619
+ definition_id: definitionId,
620
+ metadata: definitionId
621
+ ? { ...normalized.metadata, definition_id: definitionId }
622
+ : normalized.metadata,
623
+ }
624
+ }
625
+
626
+ function recordJobsMutation(jobIds: Iterable<string>): void {
627
+ jobsRevision += 1
628
+ for (const jobId of jobIds) {
629
+ jobRevisions.set(jobId, (jobRevisions.get(jobId) ?? 0) + 1)
630
+ }
631
+ }
632
+
633
+ function beginJobOperation(jobId: string): number {
634
+ pendingJobOperations.set(jobId, (pendingJobOperations.get(jobId) ?? 0) + 1)
635
+ return jobRevisions.get(jobId) ?? 0
636
+ }
637
+
638
+ function finishJobOperation(jobId: string): void {
639
+ const remaining = (pendingJobOperations.get(jobId) ?? 1) - 1
640
+ if (remaining > 0) pendingJobOperations.set(jobId, remaining)
641
+ else pendingJobOperations.delete(jobId)
642
+ pruneJobRevisions()
643
+ }
644
+
645
+ function pruneJobRevisions(): void {
646
+ const retained = new Set([
647
+ ...jobs.value.map(job => job.job_id),
648
+ ...pendingJobOperations.keys(),
649
+ ])
650
+ for (const jobId of jobRevisions.keys()) {
651
+ if (!retained.has(jobId)) jobRevisions.delete(jobId)
652
+ }
653
+ }
654
+
655
+ function replaceJobs(inputs: readonly GeneratedAnalysisJobInput[]): GeneratedAnalysisJob[] {
656
+ const previousJobs = new Map(jobs.value.map(job => [job.job_id, job]))
657
+ const previousJobIds = new Set(jobs.value.map(job => job.job_id))
658
+ const nextJobs = inputs.map((input) => {
659
+ const incoming = canonicalJob(input)
660
+ const current = previousJobs.get(incoming.job_id)
661
+ return current && shouldPreserveJobLifecycle(current, incoming) ? current : incoming
662
+ })
663
+ const nextJobIds = new Set(nextJobs.map(job => job.job_id))
664
+ recordJobsMutation(new Set([
665
+ ...previousJobIds,
666
+ ...nextJobIds,
667
+ ]))
668
+ jobs.value = nextJobs
669
+ for (const job of nextJobs) settleJobWaiters(job)
670
+ for (const jobId of previousJobIds) {
671
+ if (!nextJobIds.has(jobId)) {
672
+ rejectJobWaiters(jobId, new Error(`Job ${jobId} is no longer available`))
673
+ }
674
+ }
675
+ pruneResultState()
676
+ pruneJobRevisions()
677
+ return nextJobs
678
+ }
679
+
680
+ function upsertJob(
681
+ input: GeneratedAnalysisJobInput,
682
+ fallbackDefinitionId?: string,
683
+ ): GeneratedAnalysisJob {
684
+ const incoming = canonicalJob(input, fallbackDefinitionId)
685
+ const current = jobs.value.find(candidate => candidate.job_id === incoming.job_id)
686
+ const job = current && shouldPreserveJobLifecycle(current, incoming)
687
+ ? current
688
+ : incoming
689
+ if (job === current) {
690
+ settleJobWaiters(job)
691
+ pruneResultState()
692
+ pruneJobRevisions()
693
+ return job
694
+ }
695
+ jobs.value = [job, ...jobs.value.filter(candidate => candidate.job_id !== job.job_id)]
696
+ recordJobsMutation([job.job_id])
697
+ settleJobWaiters(job)
698
+ pruneResultState()
699
+ pruneJobRevisions()
700
+ return job
701
+ }
702
+
703
+ function mergeJobResponse(
704
+ input: GeneratedAnalysisJobInput,
705
+ expectedRevision: number,
706
+ ): GeneratedAnalysisJob {
707
+ const incoming = canonicalJob(input)
708
+ const current = jobs.value.find(candidate => candidate.job_id === incoming.job_id)
709
+ if (disposed) return current ?? incoming
710
+ if (current && shouldPreserveJobLifecycle(current, incoming)) return current
711
+ if (expectedRevision === (jobRevisions.get(incoming.job_id) ?? 0)) {
712
+ return upsertJob(incoming)
713
+ }
714
+ if (current && jobLifecycleRank(incoming.status) > jobLifecycleRank(current.status)) {
715
+ return upsertJob(incoming)
716
+ }
717
+ return current ?? incoming
718
+ }
719
+
720
+ function currentCompletedJob(): GeneratedAnalysisJob | undefined {
721
+ const activeSessionId = session.value?.id
722
+ return jobs.value.find(candidate => (
723
+ candidate.session_id === activeSessionId
724
+ && candidate.status === 'completed'
725
+ && candidate.result_status === 'available'
726
+ ))
727
+ }
728
+
729
+ function resultForJob(jobId: string): GeneratedAnalysisResult | null {
730
+ return resultCache.value[jobId] ?? null
731
+ }
732
+
733
+ function loadResult(job: GeneratedAnalysisJob): Promise<GeneratedAnalysisResult | null> {
734
+ return loadResultInternal(job)
735
+ }
736
+
737
+ function loadResultInternal(
738
+ job: GeneratedAnalysisJob,
739
+ forceRefresh = false,
740
+ ): Promise<GeneratedAnalysisResult | null> {
741
+ if (
742
+ disposed
743
+ || !isLoadableResultJob(job)
744
+ ) return Promise.resolve(null)
745
+
746
+ const cached = resultForJob(job.job_id)
747
+ if (cached && !forceRefresh && !managedDownloadNeedsRefresh(cached)) {
748
+ return Promise.resolve(cached)
749
+ }
750
+ const pending = resultRequests.get(job.job_id)
751
+ if (pending) return pending
752
+
753
+ const requestResult = request<GeneratedAnalysisResult>(
754
+ `/jobs/${encodeURIComponent(job.job_id)}/result`,
755
+ { method: 'GET' },
756
+ ).then((result) => {
757
+ if (!disposed && isCurrentResultJob(job.job_id)) {
758
+ cacheResult(job.job_id, result)
759
+ clearResultRetry(job.job_id)
760
+ error.value = null
761
+ }
762
+ return result
763
+ }).catch((cause: unknown) => {
764
+ if (!disposed && isCurrentResultJob(job.job_id)) {
765
+ error.value = errorMessage(cause, 'Failed to load analysis result')
766
+ if (cached && isManagedDownloadResult(cached)) {
767
+ handleManagedDownloadRefreshFailure(job, cause)
768
+ } else {
769
+ scheduleResultRetry(job, cause)
770
+ }
771
+ }
772
+ return null
773
+ }).finally(() => {
774
+ resultRequests.delete(job.job_id)
775
+ })
776
+ resultRequests.set(job.job_id, requestResult)
777
+ return requestResult
778
+ }
779
+
780
+ function cacheResult(jobId: string, result: GeneratedAnalysisResult): void {
781
+ resultCacheOrder = [jobId, ...resultCacheOrder.filter(candidate => candidate !== jobId)]
782
+ .slice(0, MAX_RESULT_CACHE_ENTRIES)
783
+ const retained = new Set(resultCacheOrder)
784
+ resultCache.value = Object.fromEntries([
785
+ ...Object.entries(resultCache.value)
786
+ .filter(([candidate]) => retained.has(candidate) && candidate !== jobId),
787
+ [jobId, result],
788
+ ])
789
+ for (const candidate of downloadRefreshTimers.keys()) {
790
+ if (!retained.has(candidate)) clearManagedDownloadRefresh(candidate)
791
+ }
792
+ scheduleManagedDownloadRefresh(jobId, managedDownloadRefreshDelay(result))
793
+ }
794
+
795
+ function pruneResultState(): void {
796
+ const loadableJobIds = new Set(jobs.value
797
+ .filter(job => isLoadableResultJob(job))
798
+ .map(job => job.job_id))
799
+ resultCacheOrder = resultCacheOrder
800
+ .filter(jobId => loadableJobIds.has(jobId))
801
+ .slice(0, MAX_RESULT_CACHE_ENTRIES)
802
+ const retained = new Set(resultCacheOrder)
803
+ resultCache.value = Object.fromEntries(
804
+ Object.entries(resultCache.value).filter(([jobId]) => retained.has(jobId)),
805
+ )
806
+ for (const jobId of resultRetryTimers.keys()) {
807
+ if (!loadableJobIds.has(jobId)) clearResultRetry(jobId)
808
+ }
809
+ for (const jobId of resultRetryAttempts.keys()) {
810
+ if (!loadableJobIds.has(jobId)) clearResultRetry(jobId)
811
+ }
812
+ for (const jobId of downloadRefreshTimers.keys()) {
813
+ if (!loadableJobIds.has(jobId) || !retained.has(jobId)) {
814
+ clearManagedDownloadRefresh(jobId)
815
+ }
816
+ }
817
+ }
818
+
819
+ function scheduleManagedDownloadRefresh(jobId: string, delay: number | null): void {
820
+ clearManagedDownloadRefresh(jobId)
821
+ if (delay === null || disposed || paused) return
822
+ downloadRefreshTimers.set(jobId, setTimeout(() => {
823
+ downloadRefreshTimers.delete(jobId)
824
+ if (disposed || paused) return
825
+ const current = jobs.value.find(candidate => candidate.job_id === jobId)
826
+ if (current && isLoadableResultJob(current)) {
827
+ void loadResultInternal(current, true)
828
+ }
829
+ }, Math.max(0, delay)))
830
+ }
831
+
832
+ function clearManagedDownloadRefresh(jobId: string): void {
833
+ const timer = downloadRefreshTimers.get(jobId)
834
+ if (timer !== undefined) clearTimeout(timer)
835
+ downloadRefreshTimers.delete(jobId)
836
+ }
837
+
838
+ function handleManagedDownloadRefreshFailure(
839
+ job: GeneratedAnalysisJob,
840
+ cause: unknown,
841
+ ): void {
842
+ if (cause instanceof GeneratedAnalysisRequestError) {
843
+ if (cause.status === 404 || cause.status === 410) {
844
+ discardCachedResult(job.job_id)
845
+ jobs.value = jobs.value.map(candidate => (
846
+ candidate.job_id === job.job_id
847
+ ? { ...candidate, result_status: 'expired' }
848
+ : candidate
849
+ ))
850
+ recordJobsMutation([job.job_id])
851
+ return
852
+ }
853
+ if (cause.status === 401 || cause.status === 403) {
854
+ discardCachedResult(job.job_id)
855
+ return
856
+ }
857
+ }
858
+ scheduleResultRetry(job, cause, true)
859
+ }
860
+
861
+ function discardCachedResult(jobId: string): void {
862
+ clearResultRetry(jobId)
863
+ clearManagedDownloadRefresh(jobId)
864
+ resultCacheOrder = resultCacheOrder.filter(candidate => candidate !== jobId)
865
+ const retainedResults = { ...resultCache.value }
866
+ delete retainedResults[jobId]
867
+ resultCache.value = retainedResults
868
+ }
869
+
870
+ function isLoadableResultJob(job: GeneratedAnalysisJob): boolean {
871
+ return job.status === 'completed'
872
+ && job.result_status === 'available'
873
+ }
874
+
875
+ function isCurrentResultJob(jobId: string): boolean {
876
+ const job = jobs.value.find(candidate => candidate.job_id === jobId)
877
+ return job !== undefined && isLoadableResultJob(job)
878
+ }
879
+
880
+ function scheduleResultRetry(
881
+ job: GeneratedAnalysisJob,
882
+ cause: unknown,
883
+ forceRefresh = false,
884
+ ): void {
885
+ if (
886
+ paused
887
+ || !isRetryableResultError(cause)
888
+ || resultRetryTimers.has(job.job_id)
889
+ ) return
890
+ const attempt = resultRetryAttempts.get(job.job_id) ?? 0
891
+ if (attempt >= MAX_RESULT_RETRIES) return
892
+ resultRetryAttempts.set(job.job_id, attempt + 1)
893
+ resultRetryTimers.set(job.job_id, setTimeout(() => {
894
+ resultRetryTimers.delete(job.job_id)
895
+ const current = jobs.value.find(candidate => candidate.job_id === job.job_id)
896
+ if (!paused && current && isLoadableResultJob(current)) {
897
+ void loadResultInternal(current, forceRefresh)
898
+ }
899
+ }, pollInterval * (2 ** attempt)))
900
+ }
901
+
902
+ function clearResultRetry(jobId: string): void {
903
+ const timer = resultRetryTimers.get(jobId)
904
+ if (timer !== undefined) clearTimeout(timer)
905
+ resultRetryTimers.delete(jobId)
906
+ resultRetryAttempts.delete(jobId)
907
+ }
908
+
909
+ function schedulePoll(): void {
910
+ if (disposed || paused || jobStream.isConnected.value || pollTimer !== undefined) return
911
+ pollTimer = setTimeout(async () => {
912
+ pollTimer = undefined
913
+ if (disposed) return
914
+ try {
915
+ await refreshJobs()
916
+ } catch (cause) {
917
+ error.value = errorMessage(cause, 'Failed to refresh analysis jobs')
918
+ if (jobs.value.some(job => ACTIVE_STATUSES.has(job.status))) schedulePoll()
919
+ }
920
+ }, pollInterval)
921
+ }
922
+
923
+ function clearPoll(): void {
924
+ if (pollTimer === undefined) return
925
+ clearTimeout(pollTimer)
926
+ pollTimer = undefined
927
+ }
928
+
929
+ function schedulePollForActiveJobs(): void {
930
+ if (jobs.value.some(job => ACTIVE_STATUSES.has(job.status))) schedulePoll()
931
+ }
932
+
933
+ function applyJobStreamMessage(message: EventStreamMessage<PluginJobStreamData>): void {
934
+ if (message.event === 'snapshot' && 'jobs' in message.data) {
935
+ const payload = message.data
936
+ replaceJobs(payload.jobs)
937
+ } else if (message.event === 'job' || message.event === 'terminal') {
938
+ upsertJob(message.data as GeneratedAnalysisJobInput)
939
+ }
940
+ error.value = null
941
+ }
942
+
943
+ async function cancelJob(job: GeneratedAnalysisJob | string): Promise<GeneratedAnalysisJob> {
944
+ const jobId = typeof job === 'string' ? job : job.job_id
945
+ const revision = beginJobOperation(jobId)
946
+ try {
947
+ const updated = await request<GeneratedAnalysisJobInput>(
948
+ `/jobs/${encodeURIComponent(jobId)}/cancel`,
949
+ { method: 'POST' },
950
+ )
951
+ return mergeJobResponse(updated, revision)
952
+ } finally {
953
+ finishJobOperation(jobId)
954
+ }
955
+ }
956
+
957
+ async function deleteJob(job: GeneratedAnalysisJob | string): Promise<boolean> {
958
+ const jobId = typeof job === 'string' ? job : job.job_id
959
+ await request<{ status: 'deleted' }>(
960
+ `/jobs/${encodeURIComponent(jobId)}`,
961
+ { method: 'DELETE' },
962
+ )
963
+ jobs.value = jobs.value.filter(candidate => candidate.job_id !== jobId)
964
+ recordJobsMutation([jobId])
965
+ rejectJobWaiters(jobId, new Error(`Job ${jobId} was deleted`))
966
+ discardCachedResult(jobId)
967
+ pruneJobRevisions()
968
+ return true
969
+ }
970
+
971
+ async function clearFinishedJobs(definitionId?: string): Promise<number> {
972
+ const query = definitionId === undefined
973
+ ? ''
974
+ : `?definition_id=${encodeURIComponent(definitionId)}`
975
+ const payload = await request<{ deleted: number }>(`/jobs${query}`, { method: 'DELETE' })
976
+ if (payload.deleted > 0) await refreshJobs()
977
+ return payload.deleted
978
+ }
979
+
980
+ const jobsAdapter: JobsStatusTrayAdapter = {
981
+ listJobs: async () => ({ jobs: await refreshJobs() }),
982
+ cancelJob: async job => cancelJob(job as GeneratedAnalysisJob),
983
+ deleteJob: async job => deleteJob(job as GeneratedAnalysisJob),
984
+ clearFinishedJobs: async () => clearFinishedJobs(),
985
+ }
986
+
987
+ function pause(): void {
988
+ if (disposed || paused) return
989
+ paused = true
990
+ status.value = 'paused'
991
+ clearPoll()
992
+ for (const jobId of resultRetryTimers.keys()) clearResultRetry(jobId)
993
+ for (const jobId of downloadRefreshTimers.keys()) {
994
+ clearManagedDownloadRefresh(jobId)
995
+ }
996
+ jobStream.stop()
997
+ }
998
+
999
+ function resume(): void {
1000
+ if (disposed || !paused) return
1001
+ paused = false
1002
+ status.value = initialized ? 'ready' : 'idle'
1003
+ if (!initialized) return
1004
+ for (const [jobId, result] of Object.entries(resultCache.value)) {
1005
+ scheduleManagedDownloadRefresh(jobId, managedDownloadRefreshDelay(result))
1006
+ }
1007
+ if (streamEnabled) jobStream.start()
1008
+ else if (jobs.value.some(job => ACTIVE_STATUSES.has(job.status))) schedulePoll()
1009
+ }
1010
+
1011
+ function dispose(): void {
1012
+ if (disposed) return
1013
+ disposed = true
1014
+ paused = true
1015
+ status.value = 'disposed'
1016
+ clearPoll()
1017
+ for (const timer of resultRetryTimers.values()) clearTimeout(timer)
1018
+ resultRetryTimers.clear()
1019
+ resultRetryAttempts.clear()
1020
+ for (const timer of downloadRefreshTimers.values()) clearTimeout(timer)
1021
+ downloadRefreshTimers.clear()
1022
+ const cause = new Error('Plugin jobs runtime is disposed')
1023
+ for (const jobId of jobWaiters.keys()) rejectJobWaiters(jobId, cause)
1024
+ jobRevisions.clear()
1025
+ pendingJobOperations.clear()
1026
+ jobStream.stop()
1027
+ }
1028
+
1029
+ async function request<T>(path: string, init: RequestInit): Promise<T> {
1030
+ const response = await transport(`${baseUrl}${path}`, {
1031
+ ...init,
1032
+ headers: {
1033
+ Accept: 'application/json',
1034
+ ...(init.body && !(init.body instanceof FormData)
1035
+ ? { 'Content-Type': 'application/json' }
1036
+ : {}),
1037
+ ...init.headers,
1038
+ },
1039
+ })
1040
+ if (!response.ok) {
1041
+ const payload = await response.json().catch(() => null) as { detail?: unknown } | null
1042
+ throw new GeneratedAnalysisRequestError(response.status, payload?.detail)
1043
+ }
1044
+ return await response.json() as T
1045
+ }
1046
+
1047
+ onScopeDispose(dispose)
1048
+
1049
+ if (options.immediate) void initialize().catch(() => undefined)
1050
+
1051
+ return {
1052
+ manifest,
1053
+ session,
1054
+ jobs,
1055
+ latestResult,
1056
+ isLoading,
1057
+ isSubmitting,
1058
+ error,
1059
+ status,
1060
+ initialize,
1061
+ ready: initialize,
1062
+ uploadPath,
1063
+ uploadStagedPart,
1064
+ getJobStaging,
1065
+ sealJob,
1066
+ submit,
1067
+ loadResult,
1068
+ resultForJob,
1069
+ refreshJobs,
1070
+ refresh,
1071
+ getJob,
1072
+ waitForJob,
1073
+ jobsFor,
1074
+ cancelJob,
1075
+ deleteJob,
1076
+ clearFinishedJobs,
1077
+ pause,
1078
+ resume,
1079
+ dispose,
1080
+ jobsAdapter,
1081
+ }
1082
+ }
1083
+
1084
+ function jobIdFromReference(job: GeneratedAnalysisJob | string): string {
1085
+ return typeof job === 'string' ? job : job.job_id
1086
+ }
1087
+
1088
+ function encodeStagedPartKey(key: string): string {
1089
+ return key.split('/').map(segment => encodeURIComponent(segment)).join('/')
1090
+ }
1091
+
1092
+ function jobLifecycleRank(status: GeneratedAnalysisJob['status']): number {
1093
+ if (status === 'awaiting_input') return 0
1094
+ if (status === 'queued') return 1
1095
+ if (status === 'running') return 2
1096
+ return 3
1097
+ }
1098
+
1099
+ function shouldPreserveJobLifecycle(
1100
+ current: GeneratedAnalysisJob,
1101
+ incoming: GeneratedAnalysisJob,
1102
+ ): boolean {
1103
+ const currentRank = jobLifecycleRank(current.status)
1104
+ const incomingRank = jobLifecycleRank(incoming.status)
1105
+ return incomingRank < currentRank
1106
+ || (currentRank === 3 && incomingRank === 3 && current.status !== incoming.status)
1107
+ }
1108
+
1109
+ function terminalWaitResult(
1110
+ job: GeneratedAnalysisJob,
1111
+ rejectOnFailure: boolean,
1112
+ ): Promise<GeneratedAnalysisJob> {
1113
+ if (rejectOnFailure && (job.status === 'failed' || job.status === 'cancelled')) {
1114
+ return Promise.reject(new Error(job.error ?? `Job ${job.job_id} ${job.status}`))
1115
+ }
1116
+ return Promise.resolve(job)
1117
+ }
1118
+
1119
+ function pluginJobWaitAbort(signal: AbortSignal): unknown {
1120
+ if (signal.reason !== undefined) return signal.reason
1121
+ const cause = new Error('Waiting for job was aborted')
1122
+ cause.name = 'AbortError'
1123
+ return cause
1124
+ }
1125
+
1126
+ function isPluginJobPathInput(value: unknown): value is PluginJobPathInput {
1127
+ return Boolean(
1128
+ value
1129
+ && typeof value === 'object'
1130
+ && 'kind' in value
1131
+ && value.kind === 'path'
1132
+ && 'files' in value
1133
+ && Array.isArray(value.files)
1134
+ && 'relativePaths' in value
1135
+ && Array.isArray(value.relativePaths),
1136
+ )
1137
+ }
1138
+
1139
+ function readStoredAccessToken(): string | null {
1140
+ try {
1141
+ return globalThis.localStorage?.getItem(AUTH_TOKEN_KEY) ?? null
1142
+ } catch {
1143
+ return null
1144
+ }
1145
+ }
1146
+
1147
+ function createGeneratedSessionStorage(candidate?: Storage): GeneratedSessionStorage {
1148
+ const fallback = new Map<string, string>()
1149
+ let delegate: Storage | undefined
1150
+ let available = true
1151
+ try {
1152
+ delegate = candidate ?? globalThis.sessionStorage
1153
+ } catch {
1154
+ available = false
1155
+ }
1156
+ return {
1157
+ getItem(key) {
1158
+ if (available && delegate) {
1159
+ try {
1160
+ return delegate.getItem(key) ?? fallback.get(key) ?? null
1161
+ } catch {
1162
+ available = false
1163
+ }
1164
+ }
1165
+ return fallback.get(key) ?? null
1166
+ },
1167
+ setItem(key, value) {
1168
+ fallback.set(key, value)
1169
+ if (available && delegate) {
1170
+ try {
1171
+ delegate.setItem(key, value)
1172
+ } catch {
1173
+ available = false
1174
+ }
1175
+ }
1176
+ },
1177
+ }
1178
+ }
1179
+
1180
+ function isSessionNotFoundError(error: unknown): boolean {
1181
+ return error instanceof GeneratedAnalysisRequestError
1182
+ && error.status === 404
1183
+ && error.detail === SESSION_NOT_FOUND_DETAIL
1184
+ }
1185
+
1186
+ function isRetryableResultError(error: unknown): boolean {
1187
+ return !(error instanceof GeneratedAnalysisRequestError)
1188
+ || error.status === 409
1189
+ || error.status === 429
1190
+ || error.status >= 500
1191
+ }
1192
+
1193
+ function isRetryableJobLookupError(error: unknown): boolean {
1194
+ return !(error instanceof GeneratedAnalysisRequestError)
1195
+ || error.status === 408
1196
+ || error.status === 429
1197
+ || error.status >= 500
1198
+ }
1199
+
1200
+ function isManagedDownloadResult(
1201
+ result: GeneratedAnalysisResult,
1202
+ ): result is Extract<GeneratedAnalysisResult, { kind: 'artifact' }> {
1203
+ return result.kind === 'artifact' && result.managed === true
1204
+ }
1205
+
1206
+ function managedDownloadNeedsRefresh(result: GeneratedAnalysisResult): boolean {
1207
+ if (!isManagedDownloadResult(result)) return false
1208
+ if (!result.download_url || !result.download_expires_at) return true
1209
+ const expiresAt = Date.parse(result.download_expires_at)
1210
+ return !Number.isFinite(expiresAt)
1211
+ || expiresAt <= Date.now() + DOWNLOAD_URL_REFRESH_SKEW_MS
1212
+ }
1213
+
1214
+ function managedDownloadRefreshDelay(result: GeneratedAnalysisResult): number | null {
1215
+ if (!isManagedDownloadResult(result)) return null
1216
+ if (!result.download_url || !result.download_expires_at) return null
1217
+ const expiresAt = Date.parse(result.download_expires_at)
1218
+ if (!Number.isFinite(expiresAt)) return null
1219
+ const remaining = expiresAt - Date.now()
1220
+ return remaining <= DOWNLOAD_URL_REFRESH_SKEW_MS
1221
+ ? remaining
1222
+ : remaining - DOWNLOAD_URL_REFRESH_SKEW_MS
1223
+ }
1224
+
1225
+ function errorMessage(error: unknown, fallback: string): string {
1226
+ return error instanceof Error ? error.message : fallback
1227
+ }