@morscherlab/mint-sdk 1.1.25 → 1.2.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -12
- package/dist/{ExperimentPopover-tLwJ7XF4.js → ExperimentPopover-oL0fg_A0.js} +6 -4
- package/dist/ExperimentPopover-oL0fg_A0.js.map +1 -0
- package/dist/{ExperimentSelectorModal-B6P2msT4.js → ExperimentSelectorModal-BeEOomw_.js} +5 -5
- package/dist/ExperimentSelectorModal-BeEOomw_.js.map +1 -0
- package/dist/{SettingsModal-BW3Ar-uy.js → SettingsModal-Dphh3RrI.js} +5 -8
- package/dist/SettingsModal-Dphh3RrI.js.map +1 -0
- package/dist/{EmptyState-p90quq8Z.js → Skeleton-DxtwJ5gt.js} +111 -93
- package/dist/Skeleton-DxtwJ5gt.js.map +1 -0
- package/dist/__tests__/components/ExperimentTimeline.test.d.ts +1 -0
- package/dist/__tests__/components/PluginWorkspaceView.internal.test.d.ts +1 -0
- package/dist/__tests__/components/SecretInput.test.d.ts +1 -0
- package/dist/__tests__/components/WellEditPopupInternal.test.d.ts +1 -0
- package/dist/__tests__/composables/useEventStream.test.d.ts +1 -0
- package/dist/__tests__/composables/useListReorder.test.d.ts +1 -0
- package/dist/__tests__/styles/cssTokens.test.d.ts +1 -0
- package/dist/components/ActionMenu.vue.d.ts +1 -1
- package/dist/components/AppAvatarMenu.vue.d.ts +4 -0
- package/dist/components/AppPluginSwitcher.vue.d.ts +2 -0
- package/dist/components/AppSidebar.vue.d.ts +1 -1
- package/dist/components/AuditTrail.vue.d.ts +13 -0
- package/dist/components/BatchProgressList.vue.d.ts +17 -0
- package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +1 -1
- package/dist/components/DataFrame.vue.d.ts +14 -2
- package/dist/components/DatePicker.vue.d.ts +1 -1
- package/dist/components/DateTimePicker.vue.d.ts +2 -0
- package/dist/components/DropdownButton.vue.d.ts +2 -0
- package/dist/components/ExperimentDataViewer.vue.d.ts +26 -1
- package/dist/components/ExperimentPopover.vue.d.ts +1 -0
- package/dist/components/ExperimentTimeline.vue.d.ts +29 -1
- package/dist/components/FitPanel.vue.d.ts +1 -1
- package/dist/components/LcmsSequenceTable.vue.d.ts +29 -1
- package/dist/components/PluginWorkspaceView.props.d.ts +29 -1
- package/dist/components/PluginWorkspaceView.vue.d.ts +5 -5
- package/dist/components/ProgressBar.vue.d.ts +1 -1
- package/dist/components/ProtocolStepEditor.vue.d.ts +1 -0
- package/dist/components/ReagentList.vue.d.ts +29 -1
- package/dist/components/SampleHierarchyTree.vue.d.ts +29 -1
- package/dist/components/SampleSelector.drag.d.ts +13 -0
- package/dist/components/SampleSelector.tree.d.ts +2 -0
- package/dist/components/SampleSelector.vue.d.ts +34 -1
- package/dist/components/ScheduleCalendar.vue.d.ts +20 -0
- package/dist/components/SecretInput.vue.d.ts +28 -0
- package/dist/components/SmartGroupFieldRecipe.vue.d.ts +2 -1
- package/dist/components/TimePicker.vue.d.ts +1 -0
- package/dist/components/WellPlate.geometry.d.ts +15 -0
- package/dist/components/WellPlate.interaction.d.ts +13 -0
- package/dist/components/WellPlate.vue.d.ts +29 -0
- package/dist/components/index.d.ts +9 -0
- package/dist/components/index.js +8 -8
- package/dist/components/internal/AppTopBarPageSelectorInternal.vue.d.ts +2 -0
- package/dist/components/internal/DataStateInternal.vue.d.ts +43 -0
- package/dist/components/internal/WellEditPopupInternal.vue.d.ts +3 -1
- package/dist/components/internal/WorkspaceControlsInternal.vue.d.ts +23 -0
- package/dist/components/internal/WorkspaceShellInternal.vue.d.ts +70 -0
- package/dist/components/internal/WorkspaceSidebarInternal.vue.d.ts +43 -0
- package/dist/{components-DQt0tcpY.js → components-CgAUzF8F.js} +2491 -2235
- package/dist/components-CgAUzF8F.js.map +1 -0
- package/dist/composables/controlSchemaTypes.d.ts +34 -0
- package/dist/composables/flattenWorkspaceBindings.d.ts +61 -0
- package/dist/composables/index.d.ts +8 -2
- package/dist/composables/index.js +10 -10
- package/dist/composables/pluginWorkspaceModel.d.ts +164 -0
- package/dist/composables/useAsync.d.ts +5 -1
- package/dist/composables/useBioTemplateWorkspace.d.ts +4 -20
- package/dist/composables/useCommandHistory.d.ts +5 -1
- package/dist/composables/useDropdownState.d.ts +18 -2
- package/dist/composables/useEventStream.d.ts +52 -0
- package/dist/composables/useFocusTrap.d.ts +21 -0
- package/dist/composables/useJobsStatusTray.d.ts +1 -1
- package/dist/composables/useListReorder.d.ts +90 -0
- package/dist/composables/useMenuKeyboard.d.ts +29 -0
- package/dist/composables/useOptimisticMutation.d.ts +5 -1
- package/dist/composables/usePluginClient.d.ts +4 -17
- package/dist/composables/usePluginConfig.d.ts +3 -1
- package/dist/composables/usePluginWorkspace.d.ts +51 -0
- package/dist/composables/useRequestSyncState.d.ts +42 -2
- package/dist/composables/useResourceCrud.d.ts +5 -1
- package/dist/composables/useRovingFocus.d.ts +24 -0
- package/dist/composables/useScheduleDrag.d.ts +1 -0
- package/dist/composables/useWellPainting.d.ts +5 -1
- package/dist/composables/useWellPlateAdapter.d.ts +5 -1
- package/dist/composables/useWellPlateValidation.d.ts +5 -1
- package/dist/composables/workspaceRecipes.d.ts +188 -0
- package/dist/{composables-CkXDe7nm.js → composables-B_pia3hO.js} +105 -254
- package/dist/composables-B_pia3hO.js.map +1 -0
- package/dist/{useApi-CDBq6z23.js → errors-_W0-LbA6.js} +102 -3
- package/dist/errors-_W0-LbA6.js.map +1 -0
- package/dist/{experiment-Y9Iz7KYi.js → experiment-Cwl9AYxw.js} +3 -8
- package/dist/experiment-Cwl9AYxw.js.map +1 -0
- package/dist/{experiment-utils-CLnzPPq5.js → experiment-utils-DV3DrvE_.js} +70 -51
- package/dist/experiment-utils-DV3DrvE_.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -15
- package/dist/install.js +2 -7
- package/dist/install.js.map +1 -1
- package/dist/permissions.d.ts +42 -1
- package/dist/permissions.js +20 -10
- package/dist/permissions.js.map +1 -1
- package/dist/stores/index.d.ts +1 -1
- package/dist/stores/index.js +3 -3
- package/dist/stores/settings.d.ts +31 -1
- package/dist/styles.css +11247 -29501
- package/dist/templates/builderReadUtils.d.ts +1 -1
- package/dist/templates/index.js +2 -4
- package/dist/{templates-CovvRZ-9.js → templates-Qq113pyZ.js} +303 -106
- package/dist/templates-Qq113pyZ.js.map +1 -0
- package/dist/types/form-builder.d.ts +1 -1
- package/dist/{useControlSchema-BhoJzWzx.js → useControlSchema-aMZlb4Sc.js} +684 -1216
- package/dist/useControlSchema-aMZlb4Sc.js.map +1 -0
- package/dist/{useDropdownState-DSb3BC3N.js → useDropdownState-DitCtrTT.js} +22 -6
- package/dist/useDropdownState-DitCtrTT.js.map +1 -0
- package/dist/{useExperimentSelector-D0pjj9WH.js → useExperimentSelector-I8HC8hUu.js} +4 -4
- package/dist/{useExperimentSelector-D0pjj9WH.js.map → useExperimentSelector-I8HC8hUu.js.map} +1 -1
- package/dist/{useFormBuilder-DiEcDis2.js → useFormBuilder-TCxNnqeb.js} +2 -2
- package/dist/{useFormBuilder-DiEcDis2.js.map → useFormBuilder-TCxNnqeb.js.map} +1 -1
- package/dist/{useProtocolTemplates-DHwSveE6.js → useProtocolTemplates-BmX_iVBR.js} +660 -197
- package/dist/useProtocolTemplates-BmX_iVBR.js.map +1 -0
- package/dist/utils/color.d.ts +13 -0
- package/dist/utils/errors.d.ts +9 -0
- package/dist/utils/pluginSecrets.d.ts +53 -0
- package/dist/utils/records.d.ts +4 -0
- package/package.json +1 -1
- package/src/__tests__/components/ActionMenu.test.ts +94 -0
- package/src/__tests__/components/AppAvatarMenu.test.ts +123 -0
- package/src/__tests__/components/BaseTabs.test.ts +67 -0
- package/src/__tests__/components/DataFrame.test.ts +183 -11
- package/src/__tests__/components/DataStates.test.ts +176 -0
- package/src/__tests__/components/DatePicker.test.ts +76 -0
- package/src/__tests__/components/DateTimePicker.test.ts +36 -0
- package/src/__tests__/components/DropdownButton.test.ts +134 -0
- package/src/__tests__/components/ExperimentTimeline.test.ts +63 -0
- package/src/__tests__/components/GeneratedPluginView.test.ts +0 -5
- package/src/__tests__/components/GroupAssigner.test.ts +53 -0
- package/src/__tests__/components/JobsStatusTray.test.ts +20 -18
- package/src/__tests__/components/LcmsSequenceTable.test.ts +22 -0
- package/src/__tests__/components/PluginWorkspaceView.internal.test.ts +230 -0
- package/src/__tests__/components/RackEditor.test.ts +35 -0
- package/src/__tests__/components/ReagentList.test.ts +64 -2
- package/src/__tests__/components/RetiredStyles.test.ts +31 -0
- package/src/__tests__/components/SampleSelector.drag.test.ts +51 -1
- package/src/__tests__/components/ScheduleCalendar.test.ts +63 -0
- package/src/__tests__/components/SecretInput.test.ts +87 -0
- package/src/__tests__/components/SegmentedControl.test.ts +88 -0
- package/src/__tests__/components/TimePicker.test.ts +61 -0
- package/src/__tests__/components/WellEditPopupInternal.test.ts +108 -0
- package/src/__tests__/components/WellPlate.interaction.test.ts +59 -0
- package/src/__tests__/composables/useAuth.test.ts +25 -0
- package/src/__tests__/composables/useDropdownState.test.ts +58 -1
- package/src/__tests__/composables/useEventStream.test.ts +140 -0
- package/src/__tests__/composables/useGeneratedAnalysis.test.ts +103 -0
- package/src/__tests__/composables/useJobsStatusTray.test.ts +25 -161
- package/src/__tests__/composables/useListReorder.test.ts +145 -0
- package/src/__tests__/composables/usePluginClient.test.ts +148 -13
- package/src/__tests__/composables/useRequestSyncState.test.ts +140 -0
- package/src/__tests__/docs/frontendDocsCatalog.test.ts +55 -2
- package/src/__tests__/stores/settings.test.ts +71 -1
- package/src/__tests__/styles/cssTokens.test.ts +131 -0
- package/src/__tests__/utils/permissions.test.ts +68 -0
- package/src/components/ActionMenu.vue +45 -88
- package/src/components/AlertBox.vue +0 -4
- package/src/components/AppAvatarMenu.vue +26 -7
- package/src/components/AppLayout.vue +0 -4
- package/src/components/AppPluginSwitcher.story.vue +87 -0
- package/src/components/AppPluginSwitcher.vue +2 -5
- package/src/components/AppSidebar.vue +0 -4
- package/src/components/AppToastContainer.vue +6 -8
- package/src/components/AppTopBar.vue +0 -4
- package/src/components/ArtifactSaveDialog.vue +0 -4
- package/src/components/ArtifactSelectorModal.vue +0 -4
- package/src/components/AuditTrail.story.vue +18 -0
- package/src/components/AuditTrail.vue +27 -7
- package/src/components/AutoGroupModal.vue +0 -4
- package/src/components/Avatar.vue +0 -4
- package/src/components/BaseButton.vue +0 -4
- package/src/components/BaseCheckbox.vue +0 -4
- package/src/components/BaseInput.vue +0 -4
- package/src/components/BaseModal.vue +8 -60
- package/src/components/BasePill.vue +0 -4
- package/src/components/BaseRadioGroup.vue +0 -4
- package/src/components/BaseSelect.vue +0 -4
- package/src/components/BaseSlider.vue +0 -4
- package/src/components/BaseTabs.vue +26 -5
- package/src/components/BaseTextarea.vue +0 -4
- package/src/components/BaseToggle.vue +0 -4
- package/src/components/BatchProgressList.story.vue +18 -0
- package/src/components/BatchProgressList.vue +32 -4
- package/src/components/BioTemplateExperimentWorkspaceView.vue +10 -21
- package/src/components/BioTemplatePackWorkspaceView.vue +11 -15
- package/src/components/BioTemplatePresetWorkspaceView.vue +8 -13
- package/src/components/Breadcrumb.vue +0 -4
- package/src/components/Calendar.vue +0 -4
- package/src/components/ChartContainer.story.vue +1 -1
- package/src/components/ChartContainer.vue +0 -4
- package/src/components/ChemicalFormula.vue +0 -4
- package/src/components/CollapsibleCard.vue +0 -4
- package/src/components/ColorSlider.vue +0 -4
- package/src/components/ComponentBindingRenderer.vue +1 -3
- package/src/components/ConcentrationInput.vue +0 -4
- package/src/components/ConfirmDialog.vue +0 -4
- package/src/components/ControlWorkspaceView.vue +11 -22
- package/src/components/DataFrame.story.vue +14 -0
- package/src/components/DataFrame.vue +97 -23
- package/src/components/DatePicker.vue +73 -27
- package/src/components/DateTimePicker.vue +45 -17
- package/src/components/Divider.vue +0 -4
- package/src/components/DoseCalculator.vue +0 -4
- package/src/components/DoseDesignWorkspaceView.vue +6 -25
- package/src/components/DropdownButton.vue +24 -6
- package/src/components/EmptyState.vue +0 -4
- package/src/components/ExperimentCodeBadge.vue +0 -4
- package/src/components/ExperimentDataViewer.story.vue +3 -0
- package/src/components/ExperimentDataViewer.vue +33 -13
- package/src/components/ExperimentPopover.vue +2 -5
- package/src/components/ExperimentSelectorModal.vue +0 -4
- package/src/components/ExperimentTimeline.story.vue +18 -0
- package/src/components/ExperimentTimeline.vue +81 -75
- package/src/components/FileUploader.vue +0 -4
- package/src/components/FitPanel.vue +0 -4
- package/src/components/FormActions.story.vue +96 -0
- package/src/components/FormActions.vue +0 -4
- package/src/components/FormBuilder.vue +0 -4
- package/src/components/FormField.vue +0 -4
- package/src/components/FormulaInput.vue +0 -4
- package/src/components/GeneratedPathInput.story.vue +68 -0
- package/src/components/GeneratedPlotlyResult.story.vue +96 -0
- package/src/components/GeneratedPlotlyResult.vue +3 -3
- package/src/components/GeneratedPluginView.story.vue +145 -0
- package/src/components/GeneratedPluginView.vue +10 -10
- package/src/components/GeneratedResultRenderer.story.vue +117 -0
- package/src/components/GeneratedResultRenderer.vue +4 -4
- package/src/components/GroupAssigner.vue +67 -5
- package/src/components/IconButton.vue +0 -4
- package/src/components/InstrumentAlertLog.vue +0 -4
- package/src/components/InstrumentStateBadge.vue +0 -4
- package/src/components/InstrumentStatusCard.vue +0 -4
- package/src/components/JobsStatusTray.story.vue +6 -10
- package/src/components/JobsStatusTray.vue +0 -4
- package/src/components/LcmsSequenceTable.vue +81 -50
- package/src/components/LoadingSpinner.vue +0 -4
- package/src/components/MobileSupportGate.vue +0 -4
- package/src/components/MoleculeInput.vue +0 -4
- package/src/components/MultiSelect.vue +0 -4
- package/src/components/NumberInput.vue +0 -4
- package/src/components/PlateMapEditor.vue +3 -14
- package/src/components/PluginIcon.vue +0 -4
- package/src/components/PluginWorkspaceView.props.ts +38 -0
- package/src/components/PluginWorkspaceView.vue +53 -224
- package/src/components/ProgressBar.vue +0 -4
- package/src/components/ProtocolStepEditor.vue +2 -4
- package/src/components/RackEditor.vue +51 -70
- package/src/components/ReagentEditor.vue +0 -4
- package/src/components/ReagentList.story.vue +18 -0
- package/src/components/ReagentList.vue +127 -82
- package/src/components/ResourceCard.vue +0 -4
- package/src/components/RuntimeUpdateNotice.story.vue +49 -0
- package/src/components/RuntimeUpdateNotice.vue +4 -4
- package/src/components/SampleHierarchyTree.story.vue +18 -0
- package/src/components/SampleHierarchyTree.vue +32 -9
- package/src/components/SampleLegend.vue +2 -8
- package/src/components/SampleSelector.drag.ts +51 -0
- package/src/components/SampleSelector.story.vue +6 -0
- package/src/components/SampleSelector.tree.ts +2 -0
- package/src/components/SampleSelector.vue +49 -10
- package/src/components/SampleSelectorGroupNode.vue +19 -0
- package/src/components/ScheduleCalendar.story.vue +6 -0
- package/src/components/ScheduleCalendar.vue +82 -7
- package/src/components/ScientificNumber.vue +0 -4
- package/src/components/SecretInput.story.vue +79 -0
- package/src/components/SecretInput.vue +150 -0
- package/src/components/SectionCard.vue +0 -4
- package/src/components/SegmentedControl.vue +48 -5
- package/src/components/SequenceInput.vue +0 -4
- package/src/components/SequenceProgressBar.story.vue +110 -0
- package/src/components/SequenceProgressBar.vue +0 -4
- package/src/components/SettingsModal.vue +0 -4
- package/src/components/Skeleton.vue +0 -4
- package/src/components/SmartGroupFieldRecipe.vue +8 -6
- package/src/components/SmartGroupManual.vue +0 -4
- package/src/components/StatusIndicator.vue +0 -4
- package/src/components/StepWizard.vue +0 -4
- package/src/components/TagsInput.vue +4 -6
- package/src/components/ThemeToggle.vue +0 -4
- package/src/components/TimePicker.vue +36 -7
- package/src/components/TimeRangeInput.vue +0 -4
- package/src/components/Tooltip.story.vue +3 -3
- package/src/components/Tooltip.vue +0 -4
- package/src/components/UnitInput.vue +0 -4
- package/src/components/WellPlate.geometry.ts +60 -1
- package/src/components/WellPlate.interaction.ts +44 -0
- package/src/components/WellPlate.story.vue +8 -0
- package/src/components/WellPlate.vue +53 -7
- package/src/components/index.ts +9 -0
- package/src/components/internal/AppTopBarPageSelectorInternal.vue +2 -5
- package/src/components/internal/AppTopBarPillNavInternal.vue +0 -4
- package/src/components/internal/DataStateInternal.vue +74 -0
- package/src/components/internal/FormSectionRenderer.vue +0 -4
- package/src/components/internal/WellEditPopupInternal.vue +20 -5
- package/src/components/internal/WorkspaceControlsInternal.vue +20 -0
- package/src/components/internal/WorkspaceShellInternal.vue +125 -0
- package/src/components/internal/WorkspaceSidebarInternal.vue +82 -0
- package/src/composables/controlSchemaTypes.ts +34 -0
- package/src/composables/experimentDesignData.ts +1 -3
- package/src/composables/flattenWorkspaceBindings.ts +104 -0
- package/src/composables/formBuilderRegistry.ts +5 -0
- package/src/composables/index.ts +41 -5
- package/src/composables/pluginWorkspaceModel.ts +182 -0
- package/src/composables/useAsync.ts +5 -1
- package/src/composables/useBioTemplatePackWorkspace.ts +15 -9
- package/src/composables/useBioTemplatePresetWorkspace.ts +14 -15
- package/src/composables/useBioTemplateWorkspace.ts +18 -43
- package/src/composables/useCommandHistory.ts +5 -1
- package/src/composables/useDropdownState.ts +35 -5
- package/src/composables/useEventStream.ts +326 -0
- package/src/composables/useFileImport.ts +2 -7
- package/src/composables/useFocusTrap.ts +91 -0
- package/src/composables/useGeneratedAnalysis.ts +46 -103
- package/src/composables/useJobsStatusTray.ts +1 -1
- package/src/composables/useListReorder.story.vue +143 -0
- package/src/composables/useListReorder.ts +242 -0
- package/src/composables/useMenuKeyboard.ts +90 -0
- package/src/composables/useOptimisticMutation.ts +6 -16
- package/src/composables/usePluginClient.ts +91 -252
- package/src/composables/usePluginConfig.ts +3 -1
- package/src/composables/usePluginWorkspace.ts +208 -0
- package/src/composables/useRackEditor.ts +2 -11
- package/src/composables/useRequestSyncState.ts +98 -28
- package/src/composables/useResourceCrud.ts +6 -16
- package/src/composables/useRovingFocus.ts +66 -0
- package/src/composables/useScheduleDrag.ts +37 -0
- package/src/composables/useWellPainting.ts +10 -30
- package/src/composables/useWellPlateAdapter.ts +5 -1
- package/src/composables/useWellPlateEditor.ts +1 -11
- package/src/composables/useWellPlateValidation.ts +5 -1
- package/src/composables/workspaceRecipes.ts +267 -0
- package/src/index.ts +16 -0
- package/src/permissions.ts +48 -9
- package/src/stores/experiment.ts +2 -7
- package/src/stores/index.ts +9 -1
- package/src/stores/settings.ts +93 -1
- package/src/styles/components/audit-trail.css +5 -5
- package/src/styles/components/batch-progress-list.css +7 -0
- package/src/styles/components/color-slider.css +2 -2
- package/src/styles/components/data-state.css +30 -0
- package/src/styles/components/dataframe.css +29 -35
- package/src/styles/components/experiment-data-viewer.css +4 -4
- package/src/styles/components/experiment-timeline.css +3 -30
- package/src/styles/components/form-builder.css +5 -5
- package/src/styles/components/lcms-sequence-table.css +16 -1
- package/src/styles/components/multi-select.css +1 -1
- package/src/styles/components/reagent-list.css +38 -31
- package/src/styles/components/sample-hierarchy-tree.css +5 -19
- package/src/styles/components/sample-selector.css +9 -2
- package/src/styles/components/schedule-calendar.css +8 -0
- package/src/styles/components/scientific-number.css +1 -1
- package/src/styles/components/secret-input.css +60 -0
- package/src/styles/components/well-plate.css +8 -0
- package/src/styles/index.css +9 -0
- package/src/styles/variables.css +29 -0
- package/src/templates/builderPresetControls.ts +1 -1
- package/src/templates/builderReadUtils.ts +1 -3
- package/src/templates/componentPlateHelpers.ts +3 -1
- package/src/templates/componentTargetResolvers.ts +1 -3
- package/src/templates/controlSchemaTargets.ts +1 -3
- package/src/templates/doseResponseAdapters.ts +2 -13
- package/src/templates/elisaAssayCollectionBuilder.ts +2 -2
- package/src/templates/plateMapBuilder.ts +2 -2
- package/src/templates/qpcrPlateBuilder.ts +1 -1
- package/src/templates/templateValidators.ts +1 -1
- package/src/templates/timeCourseBuilder.ts +2 -2
- package/src/templates/wellPlateScreenCollectionBuilder.ts +2 -2
- package/src/types/form-builder.ts +1 -0
- package/src/utils/color.ts +51 -0
- package/src/utils/errors.ts +29 -0
- package/src/utils/formModelSync.ts +1 -3
- package/src/utils/pluginIcon.ts +1 -3
- package/src/utils/pluginSecrets.ts +102 -0
- package/src/utils/records.ts +9 -0
- package/dist/EmptyState-p90quq8Z.js.map +0 -1
- package/dist/ExperimentPopover-tLwJ7XF4.js.map +0 -1
- package/dist/ExperimentSelectorModal-B6P2msT4.js.map +0 -1
- package/dist/SettingsModal-BW3Ar-uy.js.map +0 -1
- package/dist/components-DQt0tcpY.js.map +0 -1
- package/dist/composables/autoGroupConstants.d.ts +0 -1
- package/dist/composables/autoGroupGrouping.d.ts +0 -3
- package/dist/composables/useAutoGroupInputSources.d.ts +0 -32
- package/dist/composables-CkXDe7nm.js.map +0 -1
- package/dist/experiment-Y9Iz7KYi.js.map +0 -1
- package/dist/experiment-utils-CLnzPPq5.js.map +0 -1
- package/dist/templates/builderDefaults.d.ts +0 -1
- package/dist/templates/plateGeometry.d.ts +0 -4
- package/dist/templates-CovvRZ-9.js.map +0 -1
- package/dist/useApi-CDBq6z23.js.map +0 -1
- package/dist/useControlSchema-BhoJzWzx.js.map +0 -1
- package/dist/useDropdownState-DSb3BC3N.js.map +0 -1
- package/dist/useProtocolTemplates-DHwSveE6.js.map +0 -1
- package/src/__tests__/composables/useAutoGroupInputSources.test.ts +0 -127
- package/src/composables/autoGroupConstants.ts +0 -4
- package/src/composables/autoGroupGrouping.ts +0 -148
- package/src/composables/useAutoGroupInputSources.ts +0 -147
- package/src/templates/builderDefaults.ts +0 -11
- package/src/templates/plateGeometry.ts +0 -62
- /package/dist/__tests__/{composables/useAutoGroupInputSources.test.d.ts → components/DataStates.test.d.ts} +0 -0
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
uploadPluginEndpoint,
|
|
15
15
|
useCurrentExperiment,
|
|
16
16
|
usePluginEventStream,
|
|
17
|
-
usePluginClient,
|
|
18
17
|
usePluginSettings,
|
|
19
18
|
type PluginContract,
|
|
20
19
|
} from '../../composables/usePluginClient'
|
|
@@ -80,7 +79,13 @@ function pendingEventStreamResponse(): {
|
|
|
80
79
|
})
|
|
81
80
|
return {
|
|
82
81
|
response: new Response(body, { status: 200 }),
|
|
83
|
-
close: () =>
|
|
82
|
+
close: () => {
|
|
83
|
+
try {
|
|
84
|
+
streamController.close()
|
|
85
|
+
} catch {
|
|
86
|
+
// stop() may already have cancelled the reader.
|
|
87
|
+
}
|
|
88
|
+
},
|
|
84
89
|
}
|
|
85
90
|
}
|
|
86
91
|
|
|
@@ -843,6 +848,146 @@ describe('usePluginClient', () => {
|
|
|
843
848
|
})
|
|
844
849
|
})
|
|
845
850
|
|
|
851
|
+
describe('secret settings references', () => {
|
|
852
|
+
const secretRef = { $secret: 'sec_0f2a4c6e8a0c2e4a6c8e0a2c4e6a8c0e', set: true }
|
|
853
|
+
|
|
854
|
+
function integratedPluginSettingsHost(): void {
|
|
855
|
+
;(window as unknown as { __MINT_PLATFORM__?: unknown }).__MINT_PLATFORM__ = {
|
|
856
|
+
isIntegrated: true,
|
|
857
|
+
theme: 'light',
|
|
858
|
+
platformApiUrl: '/api',
|
|
859
|
+
plugin: {
|
|
860
|
+
id: 'drp',
|
|
861
|
+
name: 'drp',
|
|
862
|
+
version: '1.0.0',
|
|
863
|
+
route_prefix: '/drp',
|
|
864
|
+
api_prefix: '/api/drp',
|
|
865
|
+
settings_api: 'plugin',
|
|
866
|
+
},
|
|
867
|
+
}
|
|
868
|
+
nextGetData = {
|
|
869
|
+
settings: { apiKey: secretRef, method: 'first' },
|
|
870
|
+
secret_format: 'ref',
|
|
871
|
+
mode: 'integrated',
|
|
872
|
+
revision: 'revision-1',
|
|
873
|
+
}
|
|
874
|
+
nextPutData = {
|
|
875
|
+
settings: { apiKey: secretRef, method: 'second' },
|
|
876
|
+
secret_format: 'ref',
|
|
877
|
+
mode: 'integrated',
|
|
878
|
+
revision: 'revision-2',
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
it('should round-trip an untouched secret reference unchanged', async () => {
|
|
883
|
+
integratedPluginSettingsHost()
|
|
884
|
+
|
|
885
|
+
const settings = usePluginSettings<{ apiKey?: unknown; method?: string }>({
|
|
886
|
+
pluginName: 'drp',
|
|
887
|
+
})
|
|
888
|
+
await settings.load()
|
|
889
|
+
const saved = await settings.save({ ...settings.values.value, method: 'second' })
|
|
890
|
+
|
|
891
|
+
expect({ saved, body: requestBodies[0] }).toEqual({
|
|
892
|
+
saved: true,
|
|
893
|
+
body: { apiKey: secretRef, method: 'second', secret_format: 'ref' },
|
|
894
|
+
})
|
|
895
|
+
})
|
|
896
|
+
|
|
897
|
+
it('should submit a typed string when the user enters a new secret', async () => {
|
|
898
|
+
integratedPluginSettingsHost()
|
|
899
|
+
|
|
900
|
+
const settings = usePluginSettings<{ apiKey?: unknown; method?: string }>({
|
|
901
|
+
pluginName: 'drp',
|
|
902
|
+
})
|
|
903
|
+
await settings.load()
|
|
904
|
+
await settings.save({ apiKey: 'typed-secret', method: 'first' })
|
|
905
|
+
|
|
906
|
+
expect(requestBodies[0]).toEqual({
|
|
907
|
+
apiKey: 'typed-secret',
|
|
908
|
+
method: 'first',
|
|
909
|
+
secret_format: 'ref',
|
|
910
|
+
})
|
|
911
|
+
})
|
|
912
|
+
|
|
913
|
+
it('should submit null when the user clears a secret', async () => {
|
|
914
|
+
integratedPluginSettingsHost()
|
|
915
|
+
|
|
916
|
+
const settings = usePluginSettings<{ apiKey?: unknown; method?: string }>({
|
|
917
|
+
pluginName: 'drp',
|
|
918
|
+
})
|
|
919
|
+
await settings.load()
|
|
920
|
+
await settings.save({ apiKey: null, method: 'first' })
|
|
921
|
+
|
|
922
|
+
expect(requestBodies[0]).toEqual({
|
|
923
|
+
apiKey: null,
|
|
924
|
+
method: 'first',
|
|
925
|
+
secret_format: 'ref',
|
|
926
|
+
})
|
|
927
|
+
})
|
|
928
|
+
|
|
929
|
+
it('should declare the reference format on platform config patches', async () => {
|
|
930
|
+
;(window as unknown as { __MINT_PLATFORM__?: unknown }).__MINT_PLATFORM__ = {
|
|
931
|
+
isIntegrated: true,
|
|
932
|
+
theme: 'light',
|
|
933
|
+
platformApiUrl: '/api',
|
|
934
|
+
plugin: {
|
|
935
|
+
id: 'drp',
|
|
936
|
+
name: 'drp',
|
|
937
|
+
version: '1.0.0',
|
|
938
|
+
route_prefix: '/drp',
|
|
939
|
+
api_prefix: '/api/drp',
|
|
940
|
+
},
|
|
941
|
+
}
|
|
942
|
+
nextGetData = {
|
|
943
|
+
plugin_name: 'drp',
|
|
944
|
+
config: { apiKey: secretRef, method: 'first' },
|
|
945
|
+
secret_format: 'ref',
|
|
946
|
+
revision: 'revision-1',
|
|
947
|
+
}
|
|
948
|
+
nextPatchData = {
|
|
949
|
+
plugin_name: 'drp',
|
|
950
|
+
config: { apiKey: secretRef, method: 'second' },
|
|
951
|
+
secret_format: 'ref',
|
|
952
|
+
revision: 'revision-2',
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
const settings = usePluginSettings<{ apiKey?: unknown; method?: string }>({
|
|
956
|
+
pluginName: 'drp',
|
|
957
|
+
})
|
|
958
|
+
await settings.load()
|
|
959
|
+
await settings.save({ apiKey: secretRef, method: 'second' })
|
|
960
|
+
|
|
961
|
+
expect(requestBodies[0]).toEqual({
|
|
962
|
+
config: { method: 'second', secret_format: 'ref' },
|
|
963
|
+
})
|
|
964
|
+
})
|
|
965
|
+
|
|
966
|
+
it('should omit the format marker for a host that does not publish one', async () => {
|
|
967
|
+
;(window as unknown as { __MINT_PLATFORM__?: unknown }).__MINT_PLATFORM__ = {
|
|
968
|
+
isIntegrated: true,
|
|
969
|
+
theme: 'light',
|
|
970
|
+
platformApiUrl: '/api',
|
|
971
|
+
plugin: {
|
|
972
|
+
id: 'drp',
|
|
973
|
+
name: 'drp',
|
|
974
|
+
version: '1.0.0',
|
|
975
|
+
route_prefix: '/drp',
|
|
976
|
+
api_prefix: '/api/drp',
|
|
977
|
+
settings_api: 'plugin',
|
|
978
|
+
},
|
|
979
|
+
}
|
|
980
|
+
nextGetData = { settings: { method: 'first' }, mode: 'integrated', revision: 'revision-1' }
|
|
981
|
+
nextPutData = { settings: { method: 'second' }, mode: 'integrated', revision: 'revision-2' }
|
|
982
|
+
|
|
983
|
+
const settings = usePluginSettings<{ method?: string }>({ pluginName: 'drp' })
|
|
984
|
+
await settings.load()
|
|
985
|
+
await settings.save({ method: 'second' })
|
|
986
|
+
|
|
987
|
+
expect(requestBodies[0]).toEqual({ method: 'second' })
|
|
988
|
+
})
|
|
989
|
+
})
|
|
990
|
+
|
|
846
991
|
it('should preserve the contract v2 form schema in settingsConfig', () => {
|
|
847
992
|
const settings = usePluginSettings({
|
|
848
993
|
apiBaseUrl: '/api/drp',
|
|
@@ -1396,11 +1541,7 @@ describe('usePluginClient', () => {
|
|
|
1396
1541
|
|
|
1397
1542
|
try {
|
|
1398
1543
|
stream.start()
|
|
1399
|
-
await
|
|
1400
|
-
await Promise.resolve()
|
|
1401
|
-
await Promise.resolve()
|
|
1402
|
-
|
|
1403
|
-
expect(onOpen).toHaveBeenCalledOnce()
|
|
1544
|
+
await vi.waitFor(() => expect(onOpen).toHaveBeenCalledOnce())
|
|
1404
1545
|
} finally {
|
|
1405
1546
|
stopWatching()
|
|
1406
1547
|
stream.stop()
|
|
@@ -1684,12 +1825,6 @@ describe('usePluginClient', () => {
|
|
|
1684
1825
|
expect(requestConfigs).toHaveLength(0)
|
|
1685
1826
|
})
|
|
1686
1827
|
|
|
1687
|
-
it('returns the provided client identity from usePluginClient', () => {
|
|
1688
|
-
const client = createPluginClient(contract, { endpoints: {} })
|
|
1689
|
-
|
|
1690
|
-
expect(usePluginClient(client)).toBe(client)
|
|
1691
|
-
})
|
|
1692
|
-
|
|
1693
1828
|
it('loads the current experiment from URL conventions', async () => {
|
|
1694
1829
|
window.history.replaceState({}, '', '/analysis?experimentId=42')
|
|
1695
1830
|
const current = useCurrentExperiment<{ called: string }>({ immediate: false })
|
|
@@ -89,4 +89,144 @@ describe('useRequestSyncState', () => {
|
|
|
89
89
|
expect(state.lastSavedAt.value).toBeNull()
|
|
90
90
|
expect(state.lastRunAt.value).toBeNull()
|
|
91
91
|
})
|
|
92
|
+
|
|
93
|
+
describe('stale-response guard', () => {
|
|
94
|
+
function deferred<T>() {
|
|
95
|
+
let resolve!: (value: T) => void
|
|
96
|
+
let reject!: (reason: unknown) => void
|
|
97
|
+
const promise = new Promise<T>((res, rej) => {
|
|
98
|
+
resolve = res
|
|
99
|
+
reject = rej
|
|
100
|
+
})
|
|
101
|
+
return { promise, resolve, reject }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
it('lets only the newest run own loading', async () => {
|
|
105
|
+
const state = useRequestSyncState()
|
|
106
|
+
const first = deferred<string>()
|
|
107
|
+
const second = deferred<string>()
|
|
108
|
+
|
|
109
|
+
const firstRun = state.run(() => first.promise, { success: 'load' })
|
|
110
|
+
const secondRun = state.run(() => second.promise, { success: 'load' })
|
|
111
|
+
|
|
112
|
+
first.resolve('stale')
|
|
113
|
+
await expect(firstRun).resolves.toBe('stale')
|
|
114
|
+
expect(state.loading.value).toBe(true)
|
|
115
|
+
|
|
116
|
+
second.resolve('fresh')
|
|
117
|
+
await expect(secondRun).resolves.toBe('fresh')
|
|
118
|
+
expect(state.loading.value).toBe(false)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('does not let a stale success move the sync timestamps', async () => {
|
|
122
|
+
const state = useRequestSyncState()
|
|
123
|
+
const first = deferred<string>()
|
|
124
|
+
const second = deferred<string>()
|
|
125
|
+
|
|
126
|
+
const firstRun = state.run(() => first.promise, { success: 'save' })
|
|
127
|
+
const secondRun = state.run(() => second.promise, { success: 'load' })
|
|
128
|
+
|
|
129
|
+
first.resolve('stale')
|
|
130
|
+
await firstRun
|
|
131
|
+
expect(state.lastSavedAt.value).toBeNull()
|
|
132
|
+
|
|
133
|
+
second.resolve('fresh')
|
|
134
|
+
await secondRun
|
|
135
|
+
expect(state.lastLoadedAt.value).toBeInstanceOf(Date)
|
|
136
|
+
expect(state.lastSavedAt.value).toBeNull()
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('does not let a stale failure write the error, but still rejects its caller', async () => {
|
|
140
|
+
const state = useRequestSyncState()
|
|
141
|
+
const first = deferred<string>()
|
|
142
|
+
const second = deferred<string>()
|
|
143
|
+
|
|
144
|
+
const firstRun = state.run(() => first.promise, { errorMessage: 'first failed' })
|
|
145
|
+
const secondRun = state.run(() => second.promise, { success: 'load' })
|
|
146
|
+
|
|
147
|
+
first.reject(new Error('stale failure'))
|
|
148
|
+
await expect(firstRun).rejects.toThrow('stale failure')
|
|
149
|
+
expect(state.error.value).toBeNull()
|
|
150
|
+
|
|
151
|
+
second.resolve('fresh')
|
|
152
|
+
await secondRun
|
|
153
|
+
expect(state.error.value).toBeNull()
|
|
154
|
+
expect(state.loading.value).toBe(false)
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('aborts the superseded run signal by default', async () => {
|
|
158
|
+
const state = useRequestSyncState()
|
|
159
|
+
const first = deferred<string>()
|
|
160
|
+
const second = deferred<string>()
|
|
161
|
+
let firstSignal: AbortSignal | undefined
|
|
162
|
+
|
|
163
|
+
const firstRun = state.run((signal) => {
|
|
164
|
+
firstSignal = signal
|
|
165
|
+
return first.promise
|
|
166
|
+
})
|
|
167
|
+
expect(firstSignal?.aborted).toBe(false)
|
|
168
|
+
|
|
169
|
+
const secondRun = state.run(() => second.promise)
|
|
170
|
+
expect(firstSignal?.aborted).toBe(true)
|
|
171
|
+
|
|
172
|
+
first.resolve('stale')
|
|
173
|
+
second.resolve('fresh')
|
|
174
|
+
await Promise.all([firstRun, secondRun])
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it('leaves the previous signal alone when abortPrevious is false', async () => {
|
|
178
|
+
const state = useRequestSyncState()
|
|
179
|
+
const first = deferred<string>()
|
|
180
|
+
const second = deferred<string>()
|
|
181
|
+
let firstSignal: AbortSignal | undefined
|
|
182
|
+
|
|
183
|
+
const firstRun = state.run((signal) => {
|
|
184
|
+
firstSignal = signal
|
|
185
|
+
return first.promise
|
|
186
|
+
})
|
|
187
|
+
const secondRun = state.run(() => second.promise, { abortPrevious: false })
|
|
188
|
+
|
|
189
|
+
expect(firstSignal?.aborted).toBe(false)
|
|
190
|
+
first.resolve('stale')
|
|
191
|
+
second.resolve('fresh')
|
|
192
|
+
await Promise.all([firstRun, secondRun])
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
it('forwards a caller signal onto the operation signal', async () => {
|
|
196
|
+
const state = useRequestSyncState()
|
|
197
|
+
const caller = new AbortController()
|
|
198
|
+
const pending = deferred<string>()
|
|
199
|
+
let operationSignal: AbortSignal | undefined
|
|
200
|
+
|
|
201
|
+
const run = state.run((signal) => {
|
|
202
|
+
operationSignal = signal
|
|
203
|
+
return pending.promise
|
|
204
|
+
}, { signal: caller.signal })
|
|
205
|
+
|
|
206
|
+
caller.abort()
|
|
207
|
+
expect(operationSignal?.aborted).toBe(true)
|
|
208
|
+
|
|
209
|
+
pending.resolve('done')
|
|
210
|
+
await run
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('cancel() aborts the in-flight run and drops loading', async () => {
|
|
214
|
+
const state = useRequestSyncState()
|
|
215
|
+
const pending = deferred<string>()
|
|
216
|
+
let operationSignal: AbortSignal | undefined
|
|
217
|
+
|
|
218
|
+
const run = state.run((signal) => {
|
|
219
|
+
operationSignal = signal
|
|
220
|
+
return pending.promise
|
|
221
|
+
}, { success: 'load' })
|
|
222
|
+
|
|
223
|
+
state.cancel()
|
|
224
|
+
expect(operationSignal?.aborted).toBe(true)
|
|
225
|
+
expect(state.loading.value).toBe(false)
|
|
226
|
+
|
|
227
|
+
pending.resolve('too late')
|
|
228
|
+
await expect(run).resolves.toBe('too late')
|
|
229
|
+
expect(state.lastLoadedAt.value).toBeNull()
|
|
230
|
+
})
|
|
231
|
+
})
|
|
92
232
|
})
|
|
@@ -22,7 +22,7 @@ interface FrontendManifest {
|
|
|
22
22
|
}>
|
|
23
23
|
composables: Array<{
|
|
24
24
|
name: string
|
|
25
|
-
members?: Array<{ name: string }>
|
|
25
|
+
members?: Array<{ name: string; type?: string }>
|
|
26
26
|
examples?: Array<{ title: string; language: string; code: string }>
|
|
27
27
|
}>
|
|
28
28
|
templateExports: Array<{ name: string }>
|
|
@@ -271,6 +271,21 @@ describe('frontend docs catalog coverage', () => {
|
|
|
271
271
|
})
|
|
272
272
|
})
|
|
273
273
|
|
|
274
|
+
it('documents the source and MINT 1.1 JobsStatusTray props', () => {
|
|
275
|
+
const manifest = extractFrontendManifest()
|
|
276
|
+
const jobsStatusTray = manifest.categories.components.find(item => item.name === 'JobsStatusTray')
|
|
277
|
+
|
|
278
|
+
expect(jobsStatusTray?.props?.map(prop => prop.name).sort()).toEqual([
|
|
279
|
+
'adapter',
|
|
280
|
+
'eventStream',
|
|
281
|
+
'jobs',
|
|
282
|
+
'loadOnMount',
|
|
283
|
+
'source',
|
|
284
|
+
'teleportTo',
|
|
285
|
+
'title',
|
|
286
|
+
])
|
|
287
|
+
})
|
|
288
|
+
|
|
274
289
|
it('re-exports every public component from the root SDK entrypoint', () => {
|
|
275
290
|
const publicComponents = parseComponentExports()
|
|
276
291
|
const rootComponents = parseRootComponentExports()
|
|
@@ -295,6 +310,7 @@ describe('frontend docs catalog coverage', () => {
|
|
|
295
310
|
const rootIndex = readFileSync(join(SDK_ROOT, 'src', 'index.ts'), 'utf8')
|
|
296
311
|
const componentsIndex = readFileSync(join(SDK_ROOT, 'src', 'components', 'index.ts'), 'utf8')
|
|
297
312
|
const composablesIndex = readFileSync(join(SDK_ROOT, 'src', 'composables', 'index.ts'), 'utf8')
|
|
313
|
+
const composableExports = composablesIndex.replace(/from\s+['"][^'"]+['"]/g, '')
|
|
298
314
|
|
|
299
315
|
const retiredNames = [
|
|
300
316
|
'ToastNotification',
|
|
@@ -311,10 +327,26 @@ describe('frontend docs catalog coverage', () => {
|
|
|
311
327
|
for (const name of retiredNames) {
|
|
312
328
|
expect(rootIndex).not.toMatch(new RegExp(`\\b${name}\\b`))
|
|
313
329
|
expect(componentsIndex).not.toMatch(new RegExp(`\\b${name}\\b`))
|
|
314
|
-
expect(
|
|
330
|
+
expect(composableExports).not.toMatch(new RegExp(`\\b${name}\\b`))
|
|
315
331
|
}
|
|
316
332
|
})
|
|
317
333
|
|
|
334
|
+
it('keeps the MINT 1.1 composable compatibility exports', () => {
|
|
335
|
+
const composablesIndex = readFileSync(join(SDK_ROOT, 'src', 'composables', 'index.ts'), 'utf8')
|
|
336
|
+
const expected = [
|
|
337
|
+
'useJobsStatusTray',
|
|
338
|
+
'UseJobsStatusTrayOptions',
|
|
339
|
+
'UseJobsStatusTrayReturn',
|
|
340
|
+
'JobsStatusTrayAdapter',
|
|
341
|
+
'JobStreamMessage',
|
|
342
|
+
'usePluginConfig',
|
|
343
|
+
'UsePluginConfigReturn',
|
|
344
|
+
'usePluginClient',
|
|
345
|
+
]
|
|
346
|
+
|
|
347
|
+
expect(expected.filter(name => !new RegExp(`\\b${name}\\b`).test(composablesIndex))).toEqual([])
|
|
348
|
+
})
|
|
349
|
+
|
|
318
350
|
it('documents every public composable/helper export used by docs and doctor', () => {
|
|
319
351
|
const manifest = extractFrontendManifest()
|
|
320
352
|
const publicComposables = parseDocumentedComposableExports()
|
|
@@ -401,4 +433,25 @@ describe('frontend docs catalog coverage', () => {
|
|
|
401
433
|
|
|
402
434
|
expect(useFormBuilder?.members?.map(member => member.name)).toContain('updateSchema')
|
|
403
435
|
})
|
|
436
|
+
|
|
437
|
+
it('extracts inherited return interface members', () => {
|
|
438
|
+
const manifest = extractFrontendManifest()
|
|
439
|
+
const workspace = manifest.categories.composables.find(item => item.name === 'useBioTemplateWorkspace')
|
|
440
|
+
|
|
441
|
+
expect(workspace?.members?.map(member => member.name)).toContain('componentUsage')
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
it('ignores generic constraints when extracting inherited return members', () => {
|
|
445
|
+
const manifest = extractFrontendManifest()
|
|
446
|
+
const history = manifest.categories.composables.find(item => item.name === 'useCommandHistory')
|
|
447
|
+
|
|
448
|
+
expect(history?.members?.map(member => member.name)).not.toContain('description')
|
|
449
|
+
})
|
|
450
|
+
|
|
451
|
+
it('extracts exact array return member types while resolving inheritance', () => {
|
|
452
|
+
const manifest = extractFrontendManifest()
|
|
453
|
+
const bindings = manifest.categories.composables.find(item => item.name === 'controlValuesToComponentBindings')
|
|
454
|
+
|
|
455
|
+
expect(bindings?.members?.find(member => member.name === 'props')?.type).toBe('Record<string, unknown>')
|
|
456
|
+
})
|
|
404
457
|
})
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
2
|
import { createPinia, setActivePinia } from 'pinia'
|
|
3
|
-
import {
|
|
3
|
+
import { nextTick } from 'vue'
|
|
4
|
+
import {
|
|
5
|
+
paletteCssVariables,
|
|
6
|
+
tableDensityToSize,
|
|
7
|
+
tryUseSettingsStore,
|
|
8
|
+
useSettingsStore,
|
|
9
|
+
} from '../../stores/settings'
|
|
4
10
|
|
|
5
11
|
function mockMatchMedia(matches = false) {
|
|
6
12
|
Object.defineProperty(window, 'matchMedia', {
|
|
@@ -46,6 +52,8 @@ describe('useSettingsStore', () => {
|
|
|
46
52
|
setActivePinia(createPinia())
|
|
47
53
|
installLocalStorage()
|
|
48
54
|
document.documentElement.classList.remove('dark')
|
|
55
|
+
document.documentElement.removeAttribute('style')
|
|
56
|
+
delete document.documentElement.dataset.density
|
|
49
57
|
mockMatchMedia(false)
|
|
50
58
|
})
|
|
51
59
|
|
|
@@ -75,4 +83,66 @@ describe('useSettingsStore', () => {
|
|
|
75
83
|
tableDensity: 'compact',
|
|
76
84
|
}))
|
|
77
85
|
})
|
|
86
|
+
|
|
87
|
+
describe('palette and density DOM sync', () => {
|
|
88
|
+
it('writes no palette overrides for the default palette', () => {
|
|
89
|
+
const settings = useSettingsStore()
|
|
90
|
+
settings.initialize()
|
|
91
|
+
|
|
92
|
+
expect(paletteCssVariables('default')).toEqual({})
|
|
93
|
+
expect(document.documentElement.style.getPropertyValue('--color-primary')).toBe('')
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('applies the stored palette hues to html on initialize', () => {
|
|
97
|
+
localStorage.setItem('mint-settings', JSON.stringify({ colorPalette: 'viridis' }))
|
|
98
|
+
|
|
99
|
+
const settings = useSettingsStore()
|
|
100
|
+
settings.initialize()
|
|
101
|
+
|
|
102
|
+
const style = document.documentElement.style
|
|
103
|
+
expect(style.getPropertyValue('--color-primary')).toBe('hsl(280 84% 67%)')
|
|
104
|
+
expect(style.getPropertyValue('--color-primary-hover')).toBe('hsl(280 75% 59%)')
|
|
105
|
+
expect(style.getPropertyValue('--color-cta')).toBe('hsl(80 95% 53%)')
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('re-applies the overrides when the palette changes and clears them on default', async () => {
|
|
109
|
+
const settings = useSettingsStore()
|
|
110
|
+
settings.initialize()
|
|
111
|
+
|
|
112
|
+
settings.colorPalette = 'pastel'
|
|
113
|
+
await nextTick()
|
|
114
|
+
expect(document.documentElement.style.getPropertyValue('--color-primary')).toBe('hsl(200 84% 67%)')
|
|
115
|
+
|
|
116
|
+
settings.colorPalette = 'default'
|
|
117
|
+
await nextTick()
|
|
118
|
+
expect(document.documentElement.style.getPropertyValue('--color-primary')).toBe('')
|
|
119
|
+
expect(document.documentElement.style.getPropertyValue('--color-primary-soft')).toBe('')
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('mirrors the table density onto html[data-density]', async () => {
|
|
123
|
+
const settings = useSettingsStore()
|
|
124
|
+
settings.initialize()
|
|
125
|
+
expect(document.documentElement.dataset.density).toBe('normal')
|
|
126
|
+
|
|
127
|
+
settings.tableDensity = 'compact'
|
|
128
|
+
await nextTick()
|
|
129
|
+
expect(document.documentElement.dataset.density).toBe('compact')
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('maps each density onto a table size', () => {
|
|
133
|
+
const settings = useSettingsStore()
|
|
134
|
+
settings.initialize()
|
|
135
|
+
|
|
136
|
+
expect(tableDensityToSize('compact')).toBe('sm')
|
|
137
|
+
expect(tableDensityToSize('normal')).toBe('md')
|
|
138
|
+
expect(tableDensityToSize('comfortable')).toBe('lg')
|
|
139
|
+
|
|
140
|
+
settings.tableDensity = 'comfortable'
|
|
141
|
+
expect(settings.getTableSize()).toBe('lg')
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('exposes the store through tryUseSettingsStore when Pinia is active', () => {
|
|
146
|
+
expect(tryUseSettingsStore()).not.toBeNull()
|
|
147
|
+
})
|
|
78
148
|
})
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { readdirSync, readFileSync, statSync } from 'node:fs'
|
|
2
|
+
import { dirname, join, relative, resolve } from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import { describe, expect, it } from 'vitest'
|
|
5
|
+
|
|
6
|
+
const here = dirname(fileURLToPath(import.meta.url))
|
|
7
|
+
const srcRoot = resolve(here, '../..')
|
|
8
|
+
const stylesDir = join(srcRoot, 'styles')
|
|
9
|
+
const variablesCss = readFileSync(join(stylesDir, 'variables.css'), 'utf-8')
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Custom properties that are legitimately *not* declared in variables.css:
|
|
13
|
+
* they are set at runtime (inline `:style` bindings, JS-built names) or are
|
|
14
|
+
* component-local knobs declared in the same stylesheet that consumes them.
|
|
15
|
+
* Anything else must be a canonical token from variables.css.
|
|
16
|
+
*/
|
|
17
|
+
const RUNTIME_PROPERTY_ALLOWLIST = new Set<string>([
|
|
18
|
+
// Names assembled in JS (`--grp-${i}`) — the literal prefix is what the
|
|
19
|
+
// scanner sees in a template string.
|
|
20
|
+
'--grp-',
|
|
21
|
+
])
|
|
22
|
+
|
|
23
|
+
const SOURCE_EXTENSIONS = ['.vue', '.css', '.ts']
|
|
24
|
+
const IGNORED_DIRS = new Set(['__tests__', 'generated-ui', 'node_modules'])
|
|
25
|
+
|
|
26
|
+
function walk(dir: string, out: string[] = []): string[] {
|
|
27
|
+
for (const entry of readdirSync(dir)) {
|
|
28
|
+
const full = join(dir, entry)
|
|
29
|
+
if (statSync(full).isDirectory()) {
|
|
30
|
+
if (!IGNORED_DIRS.has(entry)) walk(full, out)
|
|
31
|
+
} else if (SOURCE_EXTENSIONS.some((ext) => entry.endsWith(ext))) {
|
|
32
|
+
out.push(full)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return out
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const sourceFiles = walk(srcRoot)
|
|
39
|
+
|
|
40
|
+
function declaredIn(text: string): Set<string> {
|
|
41
|
+
const names = new Set<string>()
|
|
42
|
+
// `--foo: value` declarations, in CSS blocks and in JS style objects.
|
|
43
|
+
for (const m of text.matchAll(/(--[A-Za-z0-9_-]+)\s*:/g)) names.add(m[1])
|
|
44
|
+
// setProperty('--foo', …) / `'--foo'` keys in style bindings.
|
|
45
|
+
for (const m of text.matchAll(/['"`](--[A-Za-z0-9_-]+)['"`]/g)) names.add(m[1])
|
|
46
|
+
return names
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const canonicalTokens = declaredIn(variablesCss)
|
|
50
|
+
|
|
51
|
+
// Component-local custom properties: declared in a stylesheet or set from an
|
|
52
|
+
// inline `:style` binding in the SFC that owns it. They are legitimate, they
|
|
53
|
+
// just are not part of the global token set.
|
|
54
|
+
const componentLocalProperties = new Set<string>()
|
|
55
|
+
for (const file of sourceFiles) {
|
|
56
|
+
for (const name of declaredIn(readFileSync(file, 'utf-8'))) {
|
|
57
|
+
componentLocalProperties.add(name)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface Usage {
|
|
62
|
+
file: string
|
|
63
|
+
token: string
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const usages: Usage[] = []
|
|
67
|
+
for (const file of sourceFiles) {
|
|
68
|
+
const text = readFileSync(file, 'utf-8')
|
|
69
|
+
for (const m of text.matchAll(/var\(\s*(--[A-Za-z0-9_-]+)/g)) {
|
|
70
|
+
usages.push({ file, token: m[1] })
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
describe('CSS custom properties', () => {
|
|
75
|
+
it('scans a meaningful number of var() references', () => {
|
|
76
|
+
expect(usages.length).toBeGreaterThan(500)
|
|
77
|
+
expect(canonicalTokens.size).toBeGreaterThan(70)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('only references custom properties that are actually defined', () => {
|
|
81
|
+
const undefinedUsages = usages.filter(({ token }) => {
|
|
82
|
+
if (canonicalTokens.has(token)) return false
|
|
83
|
+
if (RUNTIME_PROPERTY_ALLOWLIST.has(token)) return false
|
|
84
|
+
return !componentLocalProperties.has(token)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
const report = [
|
|
88
|
+
...new Set(
|
|
89
|
+
undefinedUsages.map(
|
|
90
|
+
({ file, token }) => `${token} (${relative(srcRoot, file)})`,
|
|
91
|
+
),
|
|
92
|
+
),
|
|
93
|
+
].sort()
|
|
94
|
+
|
|
95
|
+
expect(report).toEqual([])
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('does not use the platform-only --color-bg-*/--color-text-*/--color-border aliases', () => {
|
|
99
|
+
const platformOnly = /^--color-(bg|text)-|^--color-border$/
|
|
100
|
+
const offenders = [
|
|
101
|
+
...new Set(
|
|
102
|
+
usages
|
|
103
|
+
.filter(({ token }) => platformOnly.test(token))
|
|
104
|
+
.map(({ file, token }) => `${token} (${relative(srcRoot, file)})`),
|
|
105
|
+
),
|
|
106
|
+
].sort()
|
|
107
|
+
|
|
108
|
+
expect(offenders).toEqual([])
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('aggregates every component stylesheet in styles/index.css', () => {
|
|
112
|
+
const index = readFileSync(join(stylesDir, 'index.css'), 'utf-8')
|
|
113
|
+
const imported = new Set(
|
|
114
|
+
[...index.matchAll(/@import\s+'\.\/components\/([^']+)'/g)].map((m) => m[1]),
|
|
115
|
+
)
|
|
116
|
+
const onDisk = readdirSync(join(stylesDir, 'components')).filter((f) =>
|
|
117
|
+
f.endsWith('.css'),
|
|
118
|
+
)
|
|
119
|
+
expect([...onDisk].filter((f) => !imported.has(f)).sort()).toEqual([])
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('does not re-import aggregated stylesheets from SFC <style> blocks', () => {
|
|
123
|
+
const offenders = sourceFiles
|
|
124
|
+
.filter((f) => f.endsWith('.vue'))
|
|
125
|
+
.filter((f) => /@import\s+['"][^'"]*styles\/components\//.test(readFileSync(f, 'utf-8')))
|
|
126
|
+
.map((f) => relative(srcRoot, f))
|
|
127
|
+
.sort()
|
|
128
|
+
|
|
129
|
+
expect(offenders).toEqual([])
|
|
130
|
+
})
|
|
131
|
+
})
|