@morscherlab/mint-sdk 1.0.0-beta.2 → 1.0.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.
- package/README.md +225 -6
- package/dist/__tests__/components/ActionItem.test.d.ts +1 -0
- package/dist/__tests__/components/AppAvatarMenu.test.d.ts +1 -0
- package/dist/__tests__/components/AppPageSelector.test.d.ts +1 -0
- package/dist/__tests__/components/AppPillNav.test.d.ts +1 -0
- package/dist/__tests__/components/AppPluginSwitcher.test.d.ts +1 -0
- package/dist/__tests__/components/AppToastContainer.test.d.ts +1 -0
- package/dist/__tests__/components/BaseRadioGroup.test.d.ts +1 -0
- package/dist/__tests__/components/BaseSelect.test.d.ts +1 -0
- package/dist/__tests__/components/BaseTabs.test.d.ts +1 -0
- package/dist/__tests__/components/BatchProgressList.test.d.ts +1 -0
- package/dist/__tests__/components/BioTemplateExperimentWorkspaceView.test.d.ts +1 -0
- package/dist/__tests__/components/BioTemplatePackWorkspaceView.test.d.ts +1 -0
- package/dist/__tests__/components/BioTemplatePresetWorkspaceView.test.d.ts +1 -0
- package/dist/__tests__/components/BioTemplateRenderer.test.d.ts +1 -0
- package/dist/__tests__/components/Breadcrumb.test.d.ts +1 -0
- package/dist/__tests__/components/CalendarGridPanel.test.d.ts +1 -0
- package/dist/__tests__/components/ComponentBindingRenderer.test.d.ts +1 -0
- package/dist/__tests__/components/ConcentrationInput.test.d.ts +1 -0
- package/dist/__tests__/components/ControlWorkspaceView.test.d.ts +1 -0
- package/dist/__tests__/components/DatePicker.test.d.ts +1 -0
- package/dist/__tests__/components/DateTimePicker.test.d.ts +1 -0
- package/dist/__tests__/components/DoseDesignWorkspaceView.test.d.ts +1 -0
- package/dist/__tests__/components/EmptyState.test.d.ts +1 -0
- package/dist/__tests__/components/ExperimentPopover.test.d.ts +1 -0
- package/dist/__tests__/components/FormBuilder.test.d.ts +1 -0
- package/dist/__tests__/components/GroupAssigner.test.d.ts +1 -0
- package/dist/__tests__/components/MultiSelect.test.d.ts +1 -0
- package/dist/__tests__/components/PluginWorkspaceView.test.d.ts +1 -0
- package/dist/__tests__/components/ProtocolStepEditor.test.d.ts +1 -0
- package/dist/__tests__/components/ReagentList.test.d.ts +1 -0
- package/dist/__tests__/components/SampleHierarchyTree.test.d.ts +1 -0
- package/dist/__tests__/components/SampleSelector.test.d.ts +1 -0
- package/dist/__tests__/components/SegmentedControl.test.d.ts +1 -0
- package/dist/__tests__/components/SettingsModal.test.d.ts +1 -0
- package/dist/__tests__/components/TagsInput.test.d.ts +1 -0
- package/dist/__tests__/components/ThemeToggle.test.d.ts +1 -0
- package/dist/__tests__/components/TimePicker.test.d.ts +1 -0
- package/dist/__tests__/composables/experiment-utils.test.d.ts +1 -0
- package/dist/__tests__/composables/useApi.test.d.ts +1 -0
- package/dist/__tests__/composables/useBioTemplatePackWorkspace.test.d.ts +1 -0
- package/dist/__tests__/composables/useBioTemplatePresetWorkspace.test.d.ts +1 -0
- package/dist/__tests__/composables/useBioTemplateWorkspace.test.d.ts +1 -0
- package/dist/__tests__/composables/useCalendarGrid.test.d.ts +1 -0
- package/dist/__tests__/composables/useControlSchema.test.d.ts +1 -0
- package/dist/__tests__/composables/useDebouncedWatch.test.d.ts +1 -0
- package/dist/__tests__/composables/useDropdownState.test.d.ts +1 -0
- package/dist/__tests__/composables/useEventListener.test.d.ts +1 -0
- package/dist/__tests__/composables/useExpansionSet.test.d.ts +1 -0
- package/dist/__tests__/composables/useExperimentData.test.d.ts +1 -0
- package/dist/__tests__/composables/useExperimentSelector.test.d.ts +1 -0
- package/dist/__tests__/composables/useGroupAssignment.test.d.ts +1 -0
- package/dist/__tests__/composables/useListSelection.test.d.ts +1 -0
- package/dist/__tests__/composables/usePluginClient.test.d.ts +1 -0
- package/dist/__tests__/composables/usePluginConfig.test.d.ts +1 -0
- package/dist/__tests__/composables/useRequestSyncState.test.d.ts +1 -0
- package/dist/__tests__/composables/useSampleGroups.test.d.ts +1 -0
- package/dist/__tests__/composables/useSelectionLimit.test.d.ts +1 -0
- package/dist/__tests__/composables/useSortedItems.test.d.ts +1 -0
- package/dist/__tests__/composables/useTemplateCollection.test.d.ts +1 -0
- package/dist/__tests__/composables/useTextSearch.test.d.ts +1 -0
- package/dist/__tests__/composables/useTheme.test.d.ts +1 -0
- package/dist/__tests__/composables/useTimeUtils.test.d.ts +1 -0
- package/dist/__tests__/docs/frontendDocsCatalog.test.d.ts +1 -0
- package/dist/__tests__/templates/templates.test.d.ts +1 -0
- package/dist/{auth-DsI0rQ7_.js → auth-QQj2kkze.js} +12 -5
- package/dist/auth-QQj2kkze.js.map +1 -0
- package/dist/components/AppAvatarMenu.vue.d.ts +2 -7
- package/dist/components/AppContainer.vue.d.ts +1 -1
- package/dist/components/AppLayout.vue.d.ts +20 -1
- package/dist/components/AppSidebar.vue.d.ts +111 -6
- package/dist/components/AppTopBar.vue.d.ts +35 -22
- package/dist/components/BaseButton.vue.d.ts +1 -1
- package/dist/components/BaseCheckbox.vue.d.ts +1 -1
- package/dist/components/BaseInput.vue.d.ts +2 -2
- package/dist/components/BasePill.vue.d.ts +2 -2
- package/dist/components/BaseRadioGroup.vue.d.ts +3 -3
- package/dist/components/BaseSelect.vue.d.ts +3 -3
- package/dist/components/BaseTabs.vue.d.ts +2 -2
- package/dist/components/BaseTextarea.vue.d.ts +1 -1
- package/dist/components/BaseToggle.vue.d.ts +1 -1
- package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +119 -0
- package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +93 -0
- package/dist/components/BioTemplatePresetWorkspaceView.vue.d.ts +87 -0
- package/dist/components/BioTemplateRenderer.vue.d.ts +29 -0
- package/dist/components/Breadcrumb.vue.d.ts +2 -2
- package/dist/components/Calendar.vue.d.ts +1 -1
- package/dist/components/CollapsibleCard.vue.d.ts +1 -1
- package/dist/components/ComponentBindingRenderer.vue.d.ts +44 -0
- package/dist/components/ConcentrationInput.vue.d.ts +2 -2
- package/dist/components/ConfirmDialog.vue.d.ts +2 -2
- package/dist/components/ControlWorkspaceView.vue.d.ts +147 -0
- package/dist/components/DatePicker.vue.d.ts +1 -1
- package/dist/components/DateTimePicker.vue.d.ts +3 -3
- package/dist/components/Divider.vue.d.ts +1 -1
- package/dist/components/DoseDesignWorkspaceView.vue.d.ts +149 -0
- package/dist/components/DropdownButton.vue.d.ts +3 -3
- package/dist/components/EmptyState.vue.d.ts +1 -2
- package/dist/components/ExperimentDataViewer.vue.d.ts +1 -1
- package/dist/components/ExperimentTimeline.vue.d.ts +2 -2
- package/dist/components/FileUploader.vue.d.ts +1 -1
- package/dist/components/FitPanel.vue.d.ts +1 -1
- package/dist/components/FormActions.vue.d.ts +4 -4
- package/dist/components/FormBuilder.vue.d.ts +31 -17
- package/dist/components/FormulaInput.vue.d.ts +2 -2
- package/dist/components/MoleculeInput.vue.d.ts +2 -2
- package/dist/components/MultiSelect.vue.d.ts +3 -3
- package/dist/components/NumberInput.vue.d.ts +1 -1
- package/dist/components/PlateMapEditor.vue.d.ts +1 -1
- package/dist/components/PluginWorkspaceView.vue.d.ts +310 -0
- package/dist/components/ProgressBar.vue.d.ts +1 -1
- package/dist/components/ProtocolStepEditor.vue.d.ts +3 -1
- package/dist/components/RackEditor.vue.d.ts +2 -2
- package/dist/components/SampleLegend.vue.d.ts +2 -2
- package/dist/components/ScheduleCalendar.vue.d.ts +2 -2
- package/dist/components/SegmentedControl.vue.d.ts +2 -2
- package/dist/components/SequenceInput.vue.d.ts +3 -3
- package/dist/components/SettingsModal.vue.d.ts +14 -6
- package/dist/components/StatusIndicator.vue.d.ts +1 -1
- package/dist/components/TagsInput.vue.d.ts +3 -2
- package/dist/components/TimePicker.vue.d.ts +3 -3
- package/dist/components/TimeRangeInput.vue.d.ts +1 -1
- package/dist/components/UnitInput.vue.d.ts +2 -2
- package/dist/components/WellPlate.vue.d.ts +6 -6
- package/dist/components/index.d.ts +9 -8
- package/dist/components/index.js +3 -3
- package/dist/components/{SettingsButton.vue.d.ts → internal/ActionItemInternal.vue.d.ts} +11 -9
- package/dist/components/{AppPageSelector.vue.d.ts → internal/AppPageSelectorInternal.vue.d.ts} +3 -6
- package/dist/components/{AppPillNav.vue.d.ts → internal/AppPillNavInternal.vue.d.ts} +4 -2
- package/dist/components/internal/CalendarGridPanelInternal.vue.d.ts +25 -0
- package/dist/components/{FormFieldRenderer.vue.d.ts → internal/FormFieldRendererInternal.vue.d.ts} +2 -2
- package/dist/components/{FormSection.vue.d.ts → internal/FormSectionRenderer.vue.d.ts} +7 -7
- package/dist/components/{WellEditPopup.vue.d.ts → internal/WellEditPopupInternal.vue.d.ts} +1 -1
- package/dist/{components-_XqPEhP9.js → components-BkGF4B4y.js} +9760 -8471
- package/dist/components-BkGF4B4y.js.map +1 -0
- package/dist/composables/experiment-utils.d.ts +8 -0
- package/dist/composables/index.d.ts +22 -5
- package/dist/composables/index.js +4 -3
- package/dist/composables/platformContextHelpers.d.ts +14 -0
- package/dist/composables/useAppExperiment.d.ts +31 -2
- package/dist/composables/useBioTemplateComponents.d.ts +22 -0
- package/dist/composables/useBioTemplateControls.d.ts +6 -0
- package/dist/composables/useBioTemplatePackWorkspace.d.ts +46 -0
- package/dist/composables/useBioTemplatePresetWorkspace.d.ts +75 -0
- package/dist/composables/useBioTemplateWorkspace.d.ts +51 -0
- package/dist/composables/useCalendarGrid.d.ts +26 -0
- package/dist/composables/useControlSchema.d.ts +343 -0
- package/dist/composables/useDebouncedWatch.d.ts +20 -0
- package/dist/composables/useDropdownState.d.ts +19 -0
- package/dist/composables/useEventListener.d.ts +13 -0
- package/dist/composables/useExpansionSet.d.ts +21 -0
- package/dist/composables/useExperimentData.d.ts +10 -0
- package/dist/composables/useExperimentSave.d.ts +31 -2
- package/dist/composables/useExperimentSelector.d.ts +20 -0
- package/dist/composables/useForm.d.ts +2 -0
- package/dist/composables/useGroupAssignment.d.ts +31 -0
- package/dist/composables/useListSelection.d.ts +35 -0
- package/dist/composables/usePlatformContext.d.ts +21 -3
- package/dist/composables/usePluginClient.d.ts +112 -0
- package/dist/composables/usePluginConfig.d.ts +12 -0
- package/dist/composables/useRequestSyncState.d.ts +34 -0
- package/dist/composables/useSampleGroups.d.ts +32 -0
- package/dist/composables/useSelectionLimit.d.ts +17 -0
- package/dist/composables/useSortedItems.d.ts +32 -0
- package/dist/composables/useTemplateCollection.d.ts +58 -0
- package/dist/composables/useTextSearch.d.ts +18 -0
- package/dist/composables/useTimeUtils.d.ts +8 -0
- package/dist/{composables-tiZqLu1M.js → composables-CHsME9H1.js} +240 -146
- package/dist/composables-CHsME9H1.js.map +1 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.js +6 -5
- package/dist/install.d.ts +7 -2
- package/dist/install.js +2 -2
- package/dist/install.js.map +1 -1
- package/dist/stores/index.js +1 -1
- package/dist/stores/settings.d.ts +4 -1
- package/dist/styles.css +4746 -5514
- package/dist/templates/adapters.d.ts +43 -0
- package/dist/templates/builders.d.ts +63 -0
- package/dist/templates/catalog.d.ts +188 -0
- package/dist/templates/componentBindings.d.ts +71 -0
- package/dist/templates/controlSchemas.d.ts +25 -0
- package/dist/templates/index.d.ts +15 -0
- package/dist/templates/index.js +2 -0
- package/dist/templates/lookup.d.ts +4 -0
- package/dist/templates/packs.d.ts +18 -0
- package/dist/templates/presets.d.ts +90 -0
- package/dist/templates/types.d.ts +531 -0
- package/dist/templates-B5jmTWuk.js +9388 -0
- package/dist/templates-B5jmTWuk.js.map +1 -0
- package/dist/types/components.d.ts +26 -23
- package/dist/types/form-builder.d.ts +6 -8
- package/dist/types/index.d.ts +2 -2
- package/dist/types/platform.d.ts +7 -1
- package/dist/useScheduleDrag-BgzpQT53.js +4414 -0
- package/dist/useScheduleDrag-BgzpQT53.js.map +1 -0
- package/dist/utils/formModelSync.d.ts +5 -0
- package/dist/utils/items.d.ts +8 -0
- package/dist/utils/options.d.ts +6 -0
- package/dist/utils/pluginIcon.d.ts +9 -0
- package/package.json +7 -2
- package/src/__tests__/components/ActionItem.test.ts +99 -0
- package/src/__tests__/components/AppAvatarMenu.test.ts +27 -0
- package/src/__tests__/components/AppLayout.test.ts +44 -0
- package/src/__tests__/components/AppPageSelector.test.ts +134 -0
- package/src/__tests__/components/AppPillNav.test.ts +125 -0
- package/src/__tests__/components/AppPluginSwitcher.test.ts +44 -0
- package/src/__tests__/components/AppSidebar.test.ts +496 -0
- package/src/__tests__/components/AppToastContainer.test.ts +37 -0
- package/src/__tests__/components/AppTopBar.test.ts +455 -9
- package/src/__tests__/components/BaseRadioGroup.test.ts +25 -0
- package/src/__tests__/components/BaseSelect.test.ts +21 -0
- package/src/__tests__/components/BaseTabs.test.ts +25 -0
- package/src/__tests__/components/BatchProgressList.test.ts +52 -0
- package/src/__tests__/components/BioTemplateExperimentWorkspaceView.test.ts +159 -0
- package/src/__tests__/components/BioTemplatePackWorkspaceView.test.ts +175 -0
- package/src/__tests__/components/BioTemplatePresetWorkspaceView.test.ts +306 -0
- package/src/__tests__/components/BioTemplateRenderer.test.ts +71 -0
- package/src/__tests__/components/Breadcrumb.test.ts +23 -0
- package/src/__tests__/components/CalendarGridPanel.test.ts +36 -0
- package/src/__tests__/components/ComponentBindingRenderer.test.ts +161 -0
- package/src/__tests__/components/ConcentrationInput.test.ts +45 -0
- package/src/__tests__/components/ControlWorkspaceView.test.ts +1102 -0
- package/src/__tests__/components/DataFrame.test.ts +11 -0
- package/src/__tests__/components/DatePicker.test.ts +45 -0
- package/src/__tests__/components/DateTimePicker.test.ts +48 -0
- package/src/__tests__/components/DoseDesignWorkspaceView.test.ts +185 -0
- package/src/__tests__/components/DropdownButton.test.ts +23 -0
- package/src/__tests__/components/EmptyState.test.ts +23 -0
- package/src/__tests__/components/ExperimentPopover.test.ts +56 -0
- package/src/__tests__/components/FormBuilder.test.ts +296 -0
- package/src/__tests__/components/GroupAssigner.test.ts +30 -0
- package/src/__tests__/components/MultiSelect.test.ts +48 -0
- package/src/__tests__/components/PluginWorkspaceView.test.ts +548 -0
- package/src/__tests__/components/ProtocolStepEditor.test.ts +33 -0
- package/src/__tests__/components/ReagentList.test.ts +82 -0
- package/src/__tests__/components/SampleHierarchyTree.test.ts +53 -0
- package/src/__tests__/components/SampleSelector.test.ts +60 -0
- package/src/__tests__/components/SegmentedControl.test.ts +24 -0
- package/src/__tests__/components/SettingsModal.test.ts +296 -0
- package/src/__tests__/components/TagsInput.test.ts +75 -0
- package/src/__tests__/components/ThemeToggle.test.ts +47 -0
- package/src/__tests__/components/TimePicker.test.ts +38 -0
- package/src/__tests__/composables/experiment-utils.test.ts +30 -0
- package/src/__tests__/composables/useApi.test.ts +30 -0
- package/src/__tests__/composables/useAppExperiment.test.ts +100 -1
- package/src/__tests__/composables/useBioTemplatePackWorkspace.test.ts +125 -0
- package/src/__tests__/composables/useBioTemplatePresetWorkspace.test.ts +199 -0
- package/src/__tests__/composables/useBioTemplateWorkspace.test.ts +104 -0
- package/src/__tests__/composables/useCalendarGrid.test.ts +38 -0
- package/src/__tests__/composables/useControlSchema.test.ts +1033 -0
- package/src/__tests__/composables/useDebouncedWatch.test.ts +93 -0
- package/src/__tests__/composables/useDropdownState.test.ts +95 -0
- package/src/__tests__/composables/useEventListener.test.ts +116 -0
- package/src/__tests__/composables/useExpansionSet.test.ts +62 -0
- package/src/__tests__/composables/useExperimentData.test.ts +4 -0
- package/src/__tests__/composables/useExperimentSave.test.ts +203 -8
- package/src/__tests__/composables/useExperimentSelector.test.ts +164 -0
- package/src/__tests__/composables/useForm.test.ts +58 -0
- package/src/__tests__/composables/useFormBuilder.test.ts +77 -0
- package/src/__tests__/composables/useGroupAssignment.test.ts +73 -0
- package/src/__tests__/composables/useListSelection.test.ts +66 -0
- package/src/__tests__/composables/usePluginClient.test.ts +541 -0
- package/src/__tests__/composables/usePluginConfig.test.ts +5 -0
- package/src/__tests__/composables/useRequestSyncState.test.ts +92 -0
- package/src/__tests__/composables/useSampleGroups.test.ts +66 -0
- package/src/__tests__/composables/useSelectionLimit.test.ts +41 -0
- package/src/__tests__/composables/useSortedItems.test.ts +87 -0
- package/src/__tests__/composables/useTemplateCollection.test.ts +147 -0
- package/src/__tests__/composables/useTextSearch.test.ts +55 -0
- package/src/__tests__/composables/useTheme.test.ts +91 -0
- package/src/__tests__/composables/useTimeUtils.test.ts +35 -0
- package/src/__tests__/docs/frontendDocsCatalog.test.ts +324 -0
- package/src/__tests__/fixtures/templates/dose-response.json +81 -0
- package/src/__tests__/fixtures/templates/plate-map.json +54 -0
- package/src/__tests__/fixtures/templates/qpcr-plate.json +96 -0
- package/src/__tests__/fixtures/templates/sample-sheet.json +71 -0
- package/src/__tests__/templates/templates.test.ts +1055 -0
- package/src/components/AppAvatarMenu.vue +15 -69
- package/src/components/AppLayout.story.vue +64 -25
- package/src/components/AppLayout.vue +83 -2
- package/src/components/AppPluginSwitcher.vue +41 -145
- package/src/components/AppSidebar.story.vue +203 -1
- package/src/components/AppSidebar.vue +320 -25
- package/src/components/{ToastNotification.story.vue → AppToastContainer.story.vue} +6 -6
- package/src/components/{ToastNotification.vue → AppToastContainer.vue} +1 -1
- package/src/components/AppTopBar.story.vue +7 -33
- package/src/components/AppTopBar.vue +104 -300
- package/src/components/BaseModal.vue +3 -5
- package/src/components/BaseRadioGroup.vue +7 -3
- package/src/components/BaseSelect.vue +11 -7
- package/src/components/BaseTabs.vue +6 -4
- package/src/components/BatchProgressList.vue +5 -8
- package/src/components/BioTemplateExperimentWorkspaceView.story.vue +123 -0
- package/src/components/BioTemplateExperimentWorkspaceView.vue +343 -0
- package/src/components/BioTemplatePackWorkspaceView.story.vue +107 -0
- package/src/components/BioTemplatePackWorkspaceView.vue +177 -0
- package/src/components/BioTemplatePresetWorkspaceView.story.vue +163 -0
- package/src/components/BioTemplatePresetWorkspaceView.vue +401 -0
- package/src/components/BioTemplateRenderer.story.vue +57 -0
- package/src/components/BioTemplateRenderer.vue +57 -0
- package/src/components/Breadcrumb.vue +14 -8
- package/src/components/ComponentBindingRenderer.story.vue +57 -0
- package/src/components/ComponentBindingRenderer.vue +308 -0
- package/src/components/ConcentrationInput.vue +27 -64
- package/src/components/ControlWorkspaceView.story.vue +347 -0
- package/src/components/ControlWorkspaceView.vue +378 -0
- package/src/components/DataFrame.vue +34 -50
- package/src/components/DatePicker.vue +59 -192
- package/src/components/DateTimePicker.vue +50 -171
- package/src/components/DoseDesignWorkspaceView.story.vue +77 -0
- package/src/components/DoseDesignWorkspaceView.vue +255 -0
- package/src/components/DropdownButton.vue +14 -32
- package/src/components/EmptyState.vue +4 -2
- package/src/components/ExperimentPopover.vue +7 -28
- package/src/components/ExperimentSelectorModal.vue +6 -5
- package/src/components/FormBuilder.story.vue +190 -0
- package/src/components/FormBuilder.vue +124 -27
- package/src/components/GroupAssigner.vue +24 -56
- package/src/components/MultiSelect.vue +17 -12
- package/src/components/PlateMapEditor.vue +3 -8
- package/src/components/PluginIcon.vue +2 -22
- package/src/components/PluginWorkspaceView.story.vue +334 -0
- package/src/components/PluginWorkspaceView.vue +708 -0
- package/src/components/ProtocolStepEditor.vue +13 -22
- package/src/components/ReagentList.vue +25 -33
- package/src/components/SampleHierarchyTree.vue +12 -23
- package/src/components/SampleSelector.vue +42 -122
- package/src/components/SegmentedControl.vue +7 -3
- package/src/components/SettingsModal.story.vue +88 -1
- package/src/components/SettingsModal.vue +120 -29
- package/src/components/TagsInput.vue +29 -14
- package/src/components/ThemeToggle.vue +9 -7
- package/src/components/TimePicker.vue +19 -41
- package/src/components/Tooltip.vue +7 -12
- package/src/components/WellPlate.vue +6 -12
- package/src/components/index.ts +9 -8
- package/src/components/internal/ActionItemInternal.vue +82 -0
- package/src/components/internal/AppPageSelectorInternal.vue +128 -0
- package/src/components/internal/AppPillNavInternal.vue +194 -0
- package/src/components/internal/CalendarGridPanelInternal.vue +120 -0
- package/src/components/{FormFieldRenderer.vue → internal/FormFieldRendererInternal.vue} +4 -12
- package/src/components/{FormSection.vue → internal/FormSectionRenderer.vue} +6 -18
- package/src/components/{WellEditPopup.vue → internal/WellEditPopupInternal.vue} +5 -10
- package/src/composables/experiment-utils.ts +26 -0
- package/src/composables/index.ts +229 -3
- package/src/composables/platformContextHelpers.ts +74 -0
- package/src/composables/useApi.ts +9 -2
- package/src/composables/useAppExperiment.ts +85 -13
- package/src/composables/useBioTemplateComponents.ts +105 -0
- package/src/composables/useBioTemplateControls.ts +41 -0
- package/src/composables/useBioTemplatePackWorkspace.ts +185 -0
- package/src/composables/useBioTemplatePresetWorkspace.ts +326 -0
- package/src/composables/useBioTemplateWorkspace.ts +141 -0
- package/src/composables/useCalendarGrid.ts +140 -0
- package/src/composables/useControlSchema.ts +1362 -0
- package/src/composables/useDebouncedWatch.ts +119 -0
- package/src/composables/useDropdownState.ts +83 -0
- package/src/composables/useEventListener.ts +111 -0
- package/src/composables/useExpansionSet.ts +117 -0
- package/src/composables/useExperimentData.ts +20 -11
- package/src/composables/useExperimentSave.ts +202 -50
- package/src/composables/useExperimentSelector.ts +86 -72
- package/src/composables/useForm.ts +49 -4
- package/src/composables/useFormBuilder.ts +93 -42
- package/src/composables/useGroupAssignment.ts +148 -0
- package/src/composables/useListSelection.ts +158 -0
- package/src/composables/usePluginClient.ts +466 -0
- package/src/composables/usePluginConfig.ts +34 -13
- package/src/composables/useRequestSyncState.ts +126 -0
- package/src/composables/useSampleGroups.ts +126 -0
- package/src/composables/useSelectionLimit.ts +57 -0
- package/src/composables/useSortedItems.ts +118 -0
- package/src/composables/useTemplateCollection.ts +229 -0
- package/src/composables/useTextSearch.ts +60 -0
- package/src/composables/useTheme.ts +2 -28
- package/src/composables/useTimeUtils.ts +26 -2
- package/src/composables/useWellPlateEditor.ts +13 -9
- package/src/index.ts +11 -348
- package/src/install.ts +11 -4
- package/src/stores/settings.ts +13 -9
- package/src/styles/components/app-layout.css +82 -0
- package/src/styles/components/app-page-selector.css +23 -0
- package/src/styles/components/app-pill-nav.css +77 -0
- package/src/styles/components/app-sidebar.css +119 -0
- package/src/styles/components/app-top-bar.css +0 -201
- package/src/styles/components/concentration-input.css +3 -142
- package/src/styles/components/empty-state.css +0 -16
- package/src/styles/components/theme-toggle.css +3 -66
- package/src/styles/index.css +0 -2
- package/src/templates/adapters.ts +785 -0
- package/src/templates/builders.ts +2149 -0
- package/src/templates/catalog.ts +245 -0
- package/src/templates/componentBindings.ts +653 -0
- package/src/templates/controlSchemas.ts +718 -0
- package/src/templates/index.ts +318 -0
- package/src/templates/lookup.ts +18 -0
- package/src/templates/packs.ts +156 -0
- package/src/templates/presets.ts +146 -0
- package/src/templates/types.ts +668 -0
- package/src/types/components.ts +39 -27
- package/src/types/form-builder.ts +7 -2
- package/src/types/index.ts +13 -3
- package/src/types/platform.ts +7 -1
- package/src/utils/formModelSync.ts +52 -0
- package/src/utils/items.ts +28 -0
- package/src/utils/options.ts +23 -0
- package/src/utils/pluginIcon.ts +30 -0
- package/dist/__tests__/composables/usePluginApi.test.d.ts +0 -13
- package/dist/auth-DsI0rQ7_.js.map +0 -1
- package/dist/components/GroupingModal.vue.d.ts +0 -12
- package/dist/components-_XqPEhP9.js.map +0 -1
- package/dist/composables/usePluginApi.d.ts +0 -29
- package/dist/composables-tiZqLu1M.js.map +0 -1
- package/dist/useScheduleDrag-CA9sGNJG.js +0 -7181
- package/dist/useScheduleDrag-CA9sGNJG.js.map +0 -1
- package/src/__tests__/composables/usePluginApi.test.ts +0 -81
- package/src/components/AppPageSelector.vue +0 -159
- package/src/components/AppPillNav.vue +0 -66
- package/src/components/GroupingModal.story.vue +0 -52
- package/src/components/GroupingModal.vue +0 -422
- package/src/components/SettingsButton.story.vue +0 -58
- package/src/components/SettingsButton.vue +0 -76
- package/src/composables/usePluginApi.ts +0 -39
- package/src/styles/components/grouping-modal.css +0 -323
- package/src/styles/components/settings-button.css +0 -94
- /package/dist/components/{ToastNotification.vue.d.ts → AppToastContainer.vue.d.ts} +0 -0
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import { computed, shallowRef, watch, type ComputedRef, type Ref } from 'vue'
|
|
2
|
+
import { useApi } from './useApi'
|
|
3
|
+
import { usePlatformContext } from './usePlatformContext'
|
|
4
|
+
import { useRequestSyncState } from './useRequestSyncState'
|
|
5
|
+
import {
|
|
6
|
+
currentExperimentFromContext,
|
|
7
|
+
getInjectedPlatformContext,
|
|
8
|
+
resolveCurrentExperimentId,
|
|
9
|
+
} from './platformContextHelpers'
|
|
10
|
+
import type { ExperimentSummary, PageSelectorItem } from '../types'
|
|
11
|
+
|
|
12
|
+
export type PluginHttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete'
|
|
13
|
+
|
|
14
|
+
export interface PluginEndpointContract {
|
|
15
|
+
name: string
|
|
16
|
+
method: PluginHttpMethod
|
|
17
|
+
path: string
|
|
18
|
+
pathParams?: string[]
|
|
19
|
+
queryParams?: PluginEndpointParamContract[]
|
|
20
|
+
requestType?: string | null
|
|
21
|
+
responseType?: string | null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface PluginEndpointParamContract {
|
|
25
|
+
name: string
|
|
26
|
+
fieldName: string
|
|
27
|
+
type?: string
|
|
28
|
+
required?: boolean
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface PluginNavItemContract {
|
|
32
|
+
path: string
|
|
33
|
+
label: string
|
|
34
|
+
id?: string
|
|
35
|
+
icon?: string
|
|
36
|
+
description?: string
|
|
37
|
+
requiresAuth?: boolean
|
|
38
|
+
requiresAdmin?: boolean
|
|
39
|
+
requiresFeature?: string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface PluginContract {
|
|
43
|
+
schemaVersion: number
|
|
44
|
+
plugin: {
|
|
45
|
+
name?: string
|
|
46
|
+
packageName?: string
|
|
47
|
+
version?: string
|
|
48
|
+
description?: string
|
|
49
|
+
routesPrefix: string
|
|
50
|
+
apiPrefix: string
|
|
51
|
+
type: 'analysis' | 'experiment-design' | string
|
|
52
|
+
analysisType?: string
|
|
53
|
+
icon?: string
|
|
54
|
+
color?: string
|
|
55
|
+
navItems?: PluginNavItemContract[]
|
|
56
|
+
capabilities?: Record<string, unknown>
|
|
57
|
+
}
|
|
58
|
+
endpoints: PluginEndpointContract[]
|
|
59
|
+
hash: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface PluginEndpointDefinition {
|
|
63
|
+
method: PluginHttpMethod
|
|
64
|
+
path: string
|
|
65
|
+
pathParams?: string[]
|
|
66
|
+
queryParams?: PluginEndpointParamDefinition[]
|
|
67
|
+
hasBody?: boolean
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export type PluginEndpointParamDefinition = string | {
|
|
71
|
+
name: string
|
|
72
|
+
fieldName?: string
|
|
73
|
+
type?: string
|
|
74
|
+
required?: boolean
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface CreatePluginClientOptions {
|
|
78
|
+
endpoints: Record<string, PluginEndpointDefinition>
|
|
79
|
+
baseUrl?: string
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface BuildPluginEndpointUrlOptions {
|
|
83
|
+
/** Override the generated or platform-injected API base URL. */
|
|
84
|
+
baseUrl?: string
|
|
85
|
+
/** Return only the endpoint path and query string instead of base URL + path. */
|
|
86
|
+
includeBaseUrl?: boolean
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface UseCurrentExperimentOptions {
|
|
90
|
+
apiBaseUrl?: string
|
|
91
|
+
immediate?: boolean
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface UseCurrentExperimentReturn<TExperiment = ExperimentSummary> {
|
|
95
|
+
/** Current experiment id resolved from platform injection or URL conventions. */
|
|
96
|
+
experimentId: ComputedRef<number | undefined>
|
|
97
|
+
/** Whether a current experiment id is available. */
|
|
98
|
+
hasExperiment: ComputedRef<boolean>
|
|
99
|
+
/** Current experiment payload, if injected or successfully fetched. */
|
|
100
|
+
experiment: Ref<TExperiment | undefined>
|
|
101
|
+
/** Whether the current experiment is currently loading. */
|
|
102
|
+
isLoading: Ref<boolean>
|
|
103
|
+
/** Error message from the last failed current-experiment fetch, or null. */
|
|
104
|
+
error: Ref<string | null>
|
|
105
|
+
/** Timestamp of the last successful current-experiment fetch, or null. */
|
|
106
|
+
lastLoadedAt: Ref<Date | null>
|
|
107
|
+
/** Return the current experiment id or throw a clear SDK error when absent. */
|
|
108
|
+
requireExperimentId: () => number
|
|
109
|
+
/** Fetch a specific experiment id, defaulting to the current experiment id. */
|
|
110
|
+
fetch: (experimentId?: number) => Promise<TExperiment | undefined>
|
|
111
|
+
/** Refetch the currently resolved experiment id. */
|
|
112
|
+
refresh: () => Promise<TExperiment | undefined>
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export type PluginEndpointCaller = (payload?: unknown) => Promise<unknown>
|
|
116
|
+
export type GeneratedPluginClient = Record<string, (...args: any[]) => Promise<any>>
|
|
117
|
+
|
|
118
|
+
function normalizeApiBaseUrl(baseUrl: string | undefined): string | undefined {
|
|
119
|
+
if (!baseUrl) return undefined
|
|
120
|
+
return baseUrl.length > 1 ? baseUrl.replace(/\/+$/, '') : baseUrl
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function normalizeApiPrefix(prefix: string | undefined): string | undefined {
|
|
124
|
+
if (!prefix) return undefined
|
|
125
|
+
if (prefix.startsWith('/api/')) return normalizeApiBaseUrl(prefix)
|
|
126
|
+
if (prefix === '/api') return prefix
|
|
127
|
+
if (prefix.startsWith('/')) return normalizeApiBaseUrl(`/api${prefix}`)
|
|
128
|
+
return normalizeApiBaseUrl(`/api/${prefix}`)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function normalizePluginNavPath(path: string): string {
|
|
132
|
+
const raw = path.trim() || '/'
|
|
133
|
+
const prefixed = raw.startsWith('/') ? raw : `/${raw}`
|
|
134
|
+
return prefixed.replace(/\/+$/, '') || '/'
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function pluginPageIdFromPath(path: string, fallbackIndex: number): string {
|
|
138
|
+
const normalizedPath = normalizePluginNavPath(path)
|
|
139
|
+
if (normalizedPath === '/') return 'dashboard'
|
|
140
|
+
return normalizedPath.replace(/^\/+/, '').replace(/\/+/g, '-') || `page-${fallbackIndex + 1}`
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Convert PluginMetadata.nav_items into AppTopBar pageSelector items for topbar page switches, homepage PluginCards, and fallback views. */
|
|
144
|
+
export function getPluginPageSelectorItems(contract: PluginContract): PageSelectorItem[] {
|
|
145
|
+
const pluginIcon = contract.plugin.icon ?? ''
|
|
146
|
+
const navItems = contract.plugin.navItems ?? []
|
|
147
|
+
const source: PluginNavItemContract[] = navItems.length > 0
|
|
148
|
+
? navItems
|
|
149
|
+
: [{
|
|
150
|
+
path: '/',
|
|
151
|
+
label: contract.plugin.name ?? 'Dashboard',
|
|
152
|
+
id: 'dashboard',
|
|
153
|
+
icon: pluginIcon || undefined,
|
|
154
|
+
description: contract.plugin.description,
|
|
155
|
+
}]
|
|
156
|
+
|
|
157
|
+
return source.map((item, index) => ({
|
|
158
|
+
id: item.id || pluginPageIdFromPath(item.path, index),
|
|
159
|
+
label: item.label,
|
|
160
|
+
to: normalizePluginNavPath(item.path),
|
|
161
|
+
icon: item.icon || pluginIcon || undefined,
|
|
162
|
+
hint: item.description || contract.plugin.name,
|
|
163
|
+
}))
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Resolve the runtime plugin API base URL from env, explicit options, platform injection, or contract metadata. */
|
|
167
|
+
export function resolvePluginBaseUrl(contract: PluginContract, explicitBaseUrl?: string): string {
|
|
168
|
+
const envPrefix = (import.meta.env?.VITE_API_PREFIX as string | undefined) || undefined
|
|
169
|
+
if (envPrefix) return normalizeApiBaseUrl(envPrefix) ?? envPrefix
|
|
170
|
+
if (explicitBaseUrl) return normalizeApiBaseUrl(explicitBaseUrl) ?? explicitBaseUrl
|
|
171
|
+
|
|
172
|
+
const injected = getInjectedPlatformContext()
|
|
173
|
+
const platformPrefix =
|
|
174
|
+
normalizeApiBaseUrl(injected?.plugin?.api_prefix) ||
|
|
175
|
+
normalizeApiPrefix(injected?.plugin?.route_prefix)
|
|
176
|
+
if (platformPrefix) return platformPrefix
|
|
177
|
+
|
|
178
|
+
return normalizeApiBaseUrl(contract.plugin.apiPrefix) || '/api'
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function encodePath(path: string, payload: Record<string, unknown>, pathParams: string[]): string {
|
|
182
|
+
let nextPath = path
|
|
183
|
+
for (const param of pathParams) {
|
|
184
|
+
const value = payload[param]
|
|
185
|
+
if (value === undefined || value === null) {
|
|
186
|
+
throw new Error(`[MINT SDK] Missing path parameter '${param}' for plugin endpoint ${path}`)
|
|
187
|
+
}
|
|
188
|
+
nextPath = nextPath.replace(`{${param}}`, encodeURIComponent(String(value)))
|
|
189
|
+
nextPath = nextPath.replace(`:${param}`, encodeURIComponent(String(value)))
|
|
190
|
+
}
|
|
191
|
+
return nextPath
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function withDefaultPathParams(
|
|
195
|
+
payload: Record<string, unknown>,
|
|
196
|
+
pathParams: string[],
|
|
197
|
+
): Record<string, unknown> {
|
|
198
|
+
if (!pathParams.length) return payload
|
|
199
|
+
const nextPayload = { ...payload }
|
|
200
|
+
for (const param of pathParams) {
|
|
201
|
+
if (nextPayload[param] !== undefined && nextPayload[param] !== null) continue
|
|
202
|
+
if (isExperimentPathParam(param)) {
|
|
203
|
+
const experimentId = resolveCurrentExperimentId()
|
|
204
|
+
if (experimentId !== undefined) {
|
|
205
|
+
nextPayload[param] = experimentId
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return nextPayload
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function isExperimentPathParam(param: string): boolean {
|
|
213
|
+
return param === 'experimentId' || param === 'experiment_id'
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function withoutPathParams(payload: Record<string, unknown>, pathParams: string[]) {
|
|
217
|
+
const copy = { ...payload }
|
|
218
|
+
for (const param of pathParams) {
|
|
219
|
+
delete copy[param]
|
|
220
|
+
}
|
|
221
|
+
return copy
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function queryParamsFromPayload(
|
|
225
|
+
payload: Record<string, unknown>,
|
|
226
|
+
pathParams: string[],
|
|
227
|
+
queryParams?: PluginEndpointParamDefinition[],
|
|
228
|
+
endpointPath?: string,
|
|
229
|
+
includeUnspecifiedParams = false,
|
|
230
|
+
) {
|
|
231
|
+
if (!queryParams?.length) {
|
|
232
|
+
return includeUnspecifiedParams ? withoutPathParams(payload, pathParams) : {}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const query: Record<string, unknown> = {}
|
|
236
|
+
for (const param of queryParams) {
|
|
237
|
+
const name = typeof param === 'string' ? param : param.name
|
|
238
|
+
const fieldName = typeof param === 'string' ? param : param.fieldName ?? param.name
|
|
239
|
+
const required = typeof param !== 'string' && param.required === true
|
|
240
|
+
const value = payload[fieldName]
|
|
241
|
+
if (value === undefined || value === null) {
|
|
242
|
+
if (required) {
|
|
243
|
+
throw new Error(
|
|
244
|
+
`[MINT SDK] Missing query parameter '${fieldName}' for plugin endpoint ${endpointPath ?? ''}`,
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
continue
|
|
248
|
+
}
|
|
249
|
+
query[name] = value
|
|
250
|
+
}
|
|
251
|
+
return query
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function hasKeys(value: Record<string, unknown>): boolean {
|
|
255
|
+
return Object.keys(value).length > 0
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function requestBodyFromPayload(
|
|
259
|
+
payload: unknown,
|
|
260
|
+
requestPayload: Record<string, unknown>,
|
|
261
|
+
endpoint: PluginEndpointDefinition,
|
|
262
|
+
): unknown {
|
|
263
|
+
if (!endpoint.hasBody) return undefined
|
|
264
|
+
const body = endpoint.pathParams?.length || endpoint.queryParams?.length
|
|
265
|
+
? requestPayload.body
|
|
266
|
+
: payload
|
|
267
|
+
if (body === undefined || body === null) {
|
|
268
|
+
throw new Error(`[MINT SDK] Missing request body for plugin endpoint ${endpoint.path}`)
|
|
269
|
+
}
|
|
270
|
+
return body
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function pluginEndpointRequestParts(
|
|
274
|
+
contract: PluginContract,
|
|
275
|
+
endpoint: PluginEndpointDefinition,
|
|
276
|
+
payload?: unknown,
|
|
277
|
+
explicitBaseUrl?: string,
|
|
278
|
+
) {
|
|
279
|
+
const payloadObject =
|
|
280
|
+
payload && typeof payload === 'object' && !Array.isArray(payload)
|
|
281
|
+
? payload as Record<string, unknown>
|
|
282
|
+
: {}
|
|
283
|
+
const pathParams = endpoint.pathParams ?? []
|
|
284
|
+
const requestPayload = withDefaultPathParams(payloadObject, pathParams)
|
|
285
|
+
const path = encodePath(endpoint.path, requestPayload, pathParams)
|
|
286
|
+
const query = queryParamsFromPayload(
|
|
287
|
+
requestPayload,
|
|
288
|
+
pathParams,
|
|
289
|
+
endpoint.queryParams,
|
|
290
|
+
endpoint.path,
|
|
291
|
+
endpoint.method === 'get',
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
baseUrl: resolvePluginBaseUrl(contract, explicitBaseUrl),
|
|
296
|
+
path,
|
|
297
|
+
query,
|
|
298
|
+
requestPayload,
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function appendQueryValue(params: URLSearchParams, key: string, value: unknown): void {
|
|
303
|
+
if (value === undefined || value === null) return
|
|
304
|
+
if (Array.isArray(value)) {
|
|
305
|
+
for (const item of value) {
|
|
306
|
+
appendQueryValue(params, key, item)
|
|
307
|
+
}
|
|
308
|
+
return
|
|
309
|
+
}
|
|
310
|
+
if (typeof value === 'object') {
|
|
311
|
+
params.append(key, JSON.stringify(value))
|
|
312
|
+
return
|
|
313
|
+
}
|
|
314
|
+
params.append(key, String(value))
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function queryString(query: Record<string, unknown>): string {
|
|
318
|
+
const params = new URLSearchParams()
|
|
319
|
+
for (const [key, value] of Object.entries(query)) {
|
|
320
|
+
appendQueryValue(params, key, value)
|
|
321
|
+
}
|
|
322
|
+
const serialized = params.toString()
|
|
323
|
+
return serialized ? `?${serialized}` : ''
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function joinBaseUrl(baseUrl: string, path: string): string {
|
|
327
|
+
if (!baseUrl) return path
|
|
328
|
+
const normalizedBase = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl
|
|
329
|
+
const normalizedPath = path.startsWith('/') ? path : `/${path}`
|
|
330
|
+
return `${normalizedBase}${normalizedPath}`
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/** Build the concrete URL for a generated plugin endpoint without making a request. */
|
|
334
|
+
export function buildPluginEndpointUrl(
|
|
335
|
+
contract: PluginContract,
|
|
336
|
+
endpoint: PluginEndpointDefinition,
|
|
337
|
+
payload?: unknown,
|
|
338
|
+
options: BuildPluginEndpointUrlOptions = {},
|
|
339
|
+
): string {
|
|
340
|
+
const parts = pluginEndpointRequestParts(contract, endpoint, payload, options.baseUrl)
|
|
341
|
+
const pathWithQuery = `${parts.path}${queryString(parts.query)}`
|
|
342
|
+
if (options.includeBaseUrl === false) return pathWithQuery
|
|
343
|
+
return joinBaseUrl(parts.baseUrl, pathWithQuery)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/** Create a typed plugin API client from a generated MINT plugin contract. */
|
|
347
|
+
export function createPluginClient<TClient extends object = GeneratedPluginClient>(
|
|
348
|
+
contract: PluginContract,
|
|
349
|
+
options: CreatePluginClientOptions,
|
|
350
|
+
): TClient {
|
|
351
|
+
const client: Record<string, PluginEndpointCaller> = {}
|
|
352
|
+
|
|
353
|
+
for (const [name, endpoint] of Object.entries(options.endpoints)) {
|
|
354
|
+
client[name] = async (payload?: unknown) => {
|
|
355
|
+
const parts = pluginEndpointRequestParts(contract, endpoint, payload, options.baseUrl)
|
|
356
|
+
const api = useApi({ baseUrl: parts.baseUrl })
|
|
357
|
+
const queryConfig = hasKeys(parts.query) ? { params: parts.query } : undefined
|
|
358
|
+
|
|
359
|
+
if (endpoint.method === 'get') {
|
|
360
|
+
return api.get(parts.path, queryConfig)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (endpoint.method === 'delete') {
|
|
364
|
+
const body = requestBodyFromPayload(payload, parts.requestPayload, endpoint)
|
|
365
|
+
const config = body === undefined
|
|
366
|
+
? queryConfig
|
|
367
|
+
: { ...queryConfig, data: body }
|
|
368
|
+
return api.delete(parts.path, config)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const body = requestBodyFromPayload(payload, parts.requestPayload, endpoint)
|
|
372
|
+
|
|
373
|
+
if (endpoint.method === 'post') return api.post(parts.path, body, queryConfig)
|
|
374
|
+
if (endpoint.method === 'put') return api.put(parts.path, body, queryConfig)
|
|
375
|
+
if (endpoint.method === 'patch') return api.patch(parts.path, body, queryConfig)
|
|
376
|
+
throw new Error(`[MINT SDK] Unsupported plugin endpoint method: ${endpoint.method}`)
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return client as TClient
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/** Return a generated plugin client from setup code with a stable typed identity. */
|
|
384
|
+
export function usePluginClient<TClient>(client: TClient): TClient {
|
|
385
|
+
return client
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/** Read plugin settings exposed through the platform context. */
|
|
389
|
+
export function usePluginSettings<TSettings = Record<string, unknown>>() {
|
|
390
|
+
const { plugin } = usePlatformContext()
|
|
391
|
+
const settings = computed(() => plugin.value?.settings as TSettings | undefined)
|
|
392
|
+
return { settings }
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** Read and optionally load the current platform experiment for integrated plugin views. */
|
|
396
|
+
export function useCurrentExperiment<TExperiment = ExperimentSummary>(
|
|
397
|
+
options: UseCurrentExperimentOptions = {},
|
|
398
|
+
): UseCurrentExperimentReturn<TExperiment> {
|
|
399
|
+
const api = useApi({ baseUrl: options.apiBaseUrl ?? getInjectedPlatformContext()?.platformApiUrl })
|
|
400
|
+
const experiment = shallowRef<TExperiment | undefined>(currentExperimentFromContext<TExperiment>())
|
|
401
|
+
const request = useRequestSyncState('Failed to load current experiment')
|
|
402
|
+
const isLoading = request.loading
|
|
403
|
+
const error = request.error
|
|
404
|
+
const lastLoadedAt = request.lastLoadedAt
|
|
405
|
+
|
|
406
|
+
const experimentId = computed<number | undefined>(() => {
|
|
407
|
+
return resolveCurrentExperimentId()
|
|
408
|
+
})
|
|
409
|
+
const hasExperiment = computed(() => experimentId.value !== undefined)
|
|
410
|
+
|
|
411
|
+
function requireExperimentId(): number {
|
|
412
|
+
const id = experimentId.value
|
|
413
|
+
if (id === undefined) {
|
|
414
|
+
throw new Error('[MINT SDK] No current experiment is selected.')
|
|
415
|
+
}
|
|
416
|
+
return id
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
async function fetchExperiment(id = experimentId.value): Promise<TExperiment | undefined> {
|
|
420
|
+
if (id === undefined) {
|
|
421
|
+
experiment.value = currentExperimentFromContext<TExperiment>()
|
|
422
|
+
return experiment.value
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
try {
|
|
426
|
+
const result = await request.run(
|
|
427
|
+
() => api.get<TExperiment>(`/experiments/${id}`),
|
|
428
|
+
{ success: 'load', errorMessage: 'Failed to load current experiment' },
|
|
429
|
+
)
|
|
430
|
+
experiment.value = result
|
|
431
|
+
return result
|
|
432
|
+
} catch (e) {
|
|
433
|
+
experiment.value = undefined
|
|
434
|
+
return undefined
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
async function refresh(): Promise<TExperiment | undefined> {
|
|
439
|
+
return fetchExperiment()
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
watch(
|
|
443
|
+
experimentId,
|
|
444
|
+
(id) => {
|
|
445
|
+
if (options.immediate === false) return
|
|
446
|
+
if (id === undefined) {
|
|
447
|
+
experiment.value = currentExperimentFromContext<TExperiment>()
|
|
448
|
+
return
|
|
449
|
+
}
|
|
450
|
+
void fetchExperiment(id)
|
|
451
|
+
},
|
|
452
|
+
{ immediate: options.immediate !== false },
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
return {
|
|
456
|
+
experimentId,
|
|
457
|
+
hasExperiment,
|
|
458
|
+
experiment,
|
|
459
|
+
isLoading,
|
|
460
|
+
error,
|
|
461
|
+
lastLoadedAt,
|
|
462
|
+
requireExperimentId,
|
|
463
|
+
fetch: fetchExperiment,
|
|
464
|
+
refresh,
|
|
465
|
+
}
|
|
466
|
+
}
|
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
import { ref, computed, onMounted, type Ref, type ComputedRef } from 'vue'
|
|
2
2
|
import { useApi } from './useApi'
|
|
3
3
|
import { usePlatformContext } from './usePlatformContext'
|
|
4
|
+
import { useRequestSyncState } from './useRequestSyncState'
|
|
4
5
|
|
|
5
6
|
export interface UsePluginConfigReturn {
|
|
7
|
+
/** Editable plugin configuration values. */
|
|
6
8
|
config: Ref<Record<string, unknown>>
|
|
9
|
+
/** Whether the config is currently loading. */
|
|
7
10
|
isLoading: Ref<boolean>
|
|
11
|
+
/** Whether the config is currently saving. */
|
|
8
12
|
isSaving: Ref<boolean>
|
|
13
|
+
/** Error message from the last failed operation, or null. */
|
|
9
14
|
error: Ref<string | null>
|
|
15
|
+
/** Timestamp of the last successful load, or null. */
|
|
16
|
+
lastLoadedAt: Ref<Date | null>
|
|
17
|
+
/** Timestamp of the last successful save, or null. */
|
|
18
|
+
lastSavedAt: Ref<Date | null>
|
|
19
|
+
/** Whether local config differs from the last loaded or saved snapshot. */
|
|
10
20
|
isDirty: ComputedRef<boolean>
|
|
21
|
+
/** Load plugin configuration from the platform. */
|
|
11
22
|
load: () => Promise<void>
|
|
23
|
+
/** Save plugin configuration to the platform. */
|
|
12
24
|
save: () => Promise<boolean>
|
|
25
|
+
/** Restore the last loaded or saved configuration snapshot. */
|
|
13
26
|
reset: () => void
|
|
14
27
|
}
|
|
15
28
|
|
|
@@ -22,9 +35,12 @@ export function usePluginConfig(pluginName?: string): UsePluginConfigReturn {
|
|
|
22
35
|
|
|
23
36
|
const config = ref<Record<string, unknown>>({})
|
|
24
37
|
const savedConfig = ref<Record<string, unknown>>({})
|
|
38
|
+
const request = useRequestSyncState('Plugin config request failed.')
|
|
25
39
|
const isLoading = ref(false)
|
|
26
40
|
const isSaving = ref(false)
|
|
27
|
-
const error =
|
|
41
|
+
const error = request.error
|
|
42
|
+
const lastLoadedAt = request.lastLoadedAt
|
|
43
|
+
const lastSavedAt = request.lastSavedAt
|
|
28
44
|
|
|
29
45
|
const isDirty = computed(() => {
|
|
30
46
|
return JSON.stringify(config.value) !== JSON.stringify(savedConfig.value)
|
|
@@ -35,15 +51,17 @@ export function usePluginConfig(pluginName?: string): UsePluginConfigReturn {
|
|
|
35
51
|
if (!name) return
|
|
36
52
|
|
|
37
53
|
isLoading.value = true
|
|
38
|
-
error.value = null
|
|
39
54
|
try {
|
|
40
|
-
const response = await
|
|
41
|
-
|
|
55
|
+
const response = await request.run(
|
|
56
|
+
() => api.get<{ plugin_name: string; config: Record<string, unknown> }>(
|
|
57
|
+
`/plugins/${encodeURIComponent(name)}/config`,
|
|
58
|
+
),
|
|
59
|
+
{ success: 'load', errorMessage: 'Failed to load plugin config' },
|
|
42
60
|
)
|
|
43
61
|
config.value = { ...response.config }
|
|
44
62
|
savedConfig.value = { ...response.config }
|
|
45
|
-
} catch
|
|
46
|
-
|
|
63
|
+
} catch {
|
|
64
|
+
// Error state is handled by request.run().
|
|
47
65
|
} finally {
|
|
48
66
|
isLoading.value = false
|
|
49
67
|
}
|
|
@@ -54,17 +72,18 @@ export function usePluginConfig(pluginName?: string): UsePluginConfigReturn {
|
|
|
54
72
|
if (!name) return false
|
|
55
73
|
|
|
56
74
|
isSaving.value = true
|
|
57
|
-
error.value = null
|
|
58
75
|
try {
|
|
59
|
-
const response = await
|
|
60
|
-
|
|
61
|
-
|
|
76
|
+
const response = await request.run(
|
|
77
|
+
() => api.patch<{ plugin_name: string; config: Record<string, unknown> }>(
|
|
78
|
+
`/plugins/${encodeURIComponent(name)}/config`,
|
|
79
|
+
{ config: config.value },
|
|
80
|
+
),
|
|
81
|
+
{ success: 'save', errorMessage: 'Failed to save plugin config' },
|
|
62
82
|
)
|
|
63
83
|
config.value = { ...response.config }
|
|
64
84
|
savedConfig.value = { ...response.config }
|
|
65
85
|
return true
|
|
66
|
-
} catch
|
|
67
|
-
error.value = e instanceof Error ? e.message : 'Failed to save plugin config'
|
|
86
|
+
} catch {
|
|
68
87
|
return false
|
|
69
88
|
} finally {
|
|
70
89
|
isSaving.value = false
|
|
@@ -73,7 +92,7 @@ export function usePluginConfig(pluginName?: string): UsePluginConfigReturn {
|
|
|
73
92
|
|
|
74
93
|
function reset(): void {
|
|
75
94
|
config.value = { ...savedConfig.value }
|
|
76
|
-
|
|
95
|
+
request.clearError()
|
|
77
96
|
}
|
|
78
97
|
|
|
79
98
|
onMounted(() => {
|
|
@@ -85,6 +104,8 @@ export function usePluginConfig(pluginName?: string): UsePluginConfigReturn {
|
|
|
85
104
|
isLoading,
|
|
86
105
|
isSaving,
|
|
87
106
|
error,
|
|
107
|
+
lastLoadedAt,
|
|
108
|
+
lastSavedAt,
|
|
88
109
|
isDirty,
|
|
89
110
|
load,
|
|
90
111
|
save,
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { ref, type Ref } from 'vue'
|
|
2
|
+
|
|
3
|
+
export type RequestSyncSuccessKind = 'load' | 'save' | 'run'
|
|
4
|
+
|
|
5
|
+
export interface RequestSyncRunOptions {
|
|
6
|
+
success?: RequestSyncSuccessKind
|
|
7
|
+
errorMessage?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface UseRequestSyncStateReturn {
|
|
11
|
+
/** Whether a request is in progress. */
|
|
12
|
+
loading: Ref<boolean>
|
|
13
|
+
/** Message from the last failed request, or null. */
|
|
14
|
+
error: Ref<string | null>
|
|
15
|
+
/** Timestamp of the last successful load operation, or null. */
|
|
16
|
+
lastLoadedAt: Ref<Date | null>
|
|
17
|
+
/** Timestamp of the last successful save operation, or null. */
|
|
18
|
+
lastSavedAt: Ref<Date | null>
|
|
19
|
+
/** Timestamp of the last successful run operation, or null. */
|
|
20
|
+
lastRunAt: Ref<Date | null>
|
|
21
|
+
/** Clear the current error without changing loading or timestamps. */
|
|
22
|
+
clearError: () => void
|
|
23
|
+
/** Convert thrown values into a developer-facing error message. */
|
|
24
|
+
readErrorMessage: (value: unknown, fallback?: string) => string
|
|
25
|
+
/** Store and return a normalized error message. */
|
|
26
|
+
setError: (value: unknown, fallback?: string) => string
|
|
27
|
+
/** Mark the resource as successfully loaded. */
|
|
28
|
+
markLoaded: (date?: Date) => void
|
|
29
|
+
/** Mark the resource as successfully saved. */
|
|
30
|
+
markSaved: (date?: Date) => void
|
|
31
|
+
/** Mark the operation as successfully run. */
|
|
32
|
+
markRun: (date?: Date) => void
|
|
33
|
+
/** Run a request with shared loading/error handling and optional sync marking. */
|
|
34
|
+
run: <T>(operation: () => Promise<T>, options?: RequestSyncRunOptions) => Promise<T>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Shared loading/error/timestamp state for generated plugin request helpers. */
|
|
38
|
+
export function useRequestSyncState(
|
|
39
|
+
defaultErrorMessage = 'Request failed.',
|
|
40
|
+
): UseRequestSyncStateReturn {
|
|
41
|
+
const loading = ref(false)
|
|
42
|
+
const error = ref<string | null>(null)
|
|
43
|
+
const lastLoadedAt = ref<Date | null>(null)
|
|
44
|
+
const lastSavedAt = ref<Date | null>(null)
|
|
45
|
+
const lastRunAt = ref<Date | null>(null)
|
|
46
|
+
|
|
47
|
+
function clearError(): void {
|
|
48
|
+
error.value = null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function readErrorMessage(value: unknown, fallback = defaultErrorMessage): string {
|
|
52
|
+
if (value instanceof Error && value.message) {
|
|
53
|
+
return value.message
|
|
54
|
+
}
|
|
55
|
+
if (typeof value === 'string' && value.trim()) {
|
|
56
|
+
return value
|
|
57
|
+
}
|
|
58
|
+
if (
|
|
59
|
+
typeof value === 'object'
|
|
60
|
+
&& value !== null
|
|
61
|
+
&& 'message' in value
|
|
62
|
+
&& typeof (value as { message?: unknown }).message === 'string'
|
|
63
|
+
&& (value as { message: string }).message
|
|
64
|
+
) {
|
|
65
|
+
return (value as { message: string }).message
|
|
66
|
+
}
|
|
67
|
+
return fallback
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function setError(value: unknown, fallback?: string): string {
|
|
71
|
+
const message = readErrorMessage(value, fallback)
|
|
72
|
+
error.value = message
|
|
73
|
+
return message
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function markLoaded(date = new Date()): void {
|
|
77
|
+
lastLoadedAt.value = date
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function markSaved(date = new Date()): void {
|
|
81
|
+
lastSavedAt.value = date
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function markRun(date = new Date()): void {
|
|
85
|
+
lastRunAt.value = date
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function run<T>(
|
|
89
|
+
operation: () => Promise<T>,
|
|
90
|
+
options: RequestSyncRunOptions = {},
|
|
91
|
+
): Promise<T> {
|
|
92
|
+
loading.value = true
|
|
93
|
+
clearError()
|
|
94
|
+
try {
|
|
95
|
+
const response = await operation()
|
|
96
|
+
if (options.success === 'load') {
|
|
97
|
+
markLoaded()
|
|
98
|
+
} else if (options.success === 'save') {
|
|
99
|
+
markSaved()
|
|
100
|
+
} else if (options.success === 'run') {
|
|
101
|
+
markRun()
|
|
102
|
+
}
|
|
103
|
+
return response
|
|
104
|
+
} catch (err) {
|
|
105
|
+
setError(err, options.errorMessage)
|
|
106
|
+
throw err
|
|
107
|
+
} finally {
|
|
108
|
+
loading.value = false
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
loading,
|
|
114
|
+
error,
|
|
115
|
+
lastLoadedAt,
|
|
116
|
+
lastSavedAt,
|
|
117
|
+
lastRunAt,
|
|
118
|
+
clearError,
|
|
119
|
+
readErrorMessage,
|
|
120
|
+
setError,
|
|
121
|
+
markLoaded,
|
|
122
|
+
markSaved,
|
|
123
|
+
markRun,
|
|
124
|
+
run,
|
|
125
|
+
}
|
|
126
|
+
}
|