@morscherlab/mint-sdk 1.0.0-rc.2 → 1.0.0-rc.5
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/dist/__tests__/components/AppTopBar.navigation.test.d.ts +1 -0
- package/dist/__tests__/components/DoseCalculatorVolumeField.test.d.ts +1 -0
- package/dist/__tests__/components/PlateMapEditorToolbarInternal.test.d.ts +1 -0
- package/dist/__tests__/components/PluginWorkspaceView.controls.test.d.ts +1 -0
- package/dist/__tests__/components/PluginWorkspaceView.navigation.test.d.ts +1 -0
- package/dist/__tests__/components/PluginWorkspaceView.shell.test.d.ts +1 -0
- package/dist/__tests__/components/ProtocolStep.presentation.test.d.ts +1 -0
- package/dist/__tests__/components/ProtocolStepEditor.state.test.d.ts +1 -0
- package/dist/__tests__/components/ProtocolStepParameterField.test.d.ts +1 -0
- package/dist/__tests__/components/ReagentList.presentation.test.d.ts +1 -0
- package/dist/__tests__/components/SampleSelector.colors.test.d.ts +1 -0
- package/dist/__tests__/components/SampleSelector.drag.test.d.ts +1 -0
- package/dist/__tests__/components/SampleSelector.groups.test.d.ts +1 -0
- package/dist/__tests__/components/SampleSelector.selection.test.d.ts +1 -0
- package/dist/__tests__/components/SampleSelectorSampleRow.test.d.ts +1 -0
- package/dist/__tests__/components/ScheduleCalendar.test.d.ts +1 -0
- package/dist/__tests__/components/SettingsModal.schema.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.colors.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.conditions.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.geometry.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.interaction.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.legend.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.rendering.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.sampleDrop.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/classify.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/columns.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/compose.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/cooccurrence.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/fingerprint.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/integration.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/template.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/tokenize.test.d.ts +1 -0
- package/dist/__tests__/composables/useAutoGroupInputSources.test.d.ts +1 -0
- package/dist/__tests__/composables/useScheduleCalendarLayout.test.d.ts +1 -0
- package/dist/__tests__/docs/extractDocsComponents.test.d.ts +1 -0
- package/dist/__tests__/docs/extractDocsExports.test.d.ts +1 -0
- package/dist/__tests__/docs/extractDocsParsing.test.d.ts +1 -0
- package/dist/__tests__/docs/extractDocsTemplates.test.d.ts +1 -0
- package/dist/__tests__/docs/extractDocsTheme.test.d.ts +1 -0
- package/dist/components/AppTopBar.navigation.d.ts +11 -0
- 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 +1 -1
- package/dist/components/BaseRadioGroup.vue.d.ts +1 -1
- package/dist/components/BaseSelect.vue.d.ts +1 -1
- package/dist/components/BaseSlider.vue.d.ts +2 -2
- package/dist/components/BaseTextarea.vue.d.ts +2 -2
- package/dist/components/BaseToggle.vue.d.ts +1 -1
- package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +2 -2
- package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +1 -1
- package/dist/components/ColorSlider.vue.d.ts +2 -2
- package/dist/components/ConcentrationInput.vue.d.ts +2 -2
- package/dist/components/DatePicker.vue.d.ts +1 -1
- package/dist/components/DateTimePicker.vue.d.ts +2 -2
- package/dist/components/DoseCalculatorVolumeField.vue.d.ts +15 -0
- package/dist/components/DropdownButton.vue.d.ts +1 -1
- package/dist/components/FileUploader.vue.d.ts +2 -2
- package/dist/components/FormulaInput.vue.d.ts +2 -2
- package/dist/components/IconButton.vue.d.ts +1 -1
- package/dist/components/LoadingSpinner.vue.d.ts +1 -1
- package/dist/components/MoleculeInput.vue.d.ts +2 -2
- package/dist/components/MultiSelect.vue.d.ts +1 -1
- package/dist/components/NumberInput.vue.d.ts +1 -1
- package/dist/components/PlateMapEditor.vue.d.ts +6 -6
- package/dist/components/PluginWorkspaceView.controls.d.ts +28 -0
- package/dist/components/PluginWorkspaceView.navigation.d.ts +29 -0
- package/dist/components/PluginWorkspaceView.props.d.ts +151 -0
- package/dist/components/PluginWorkspaceView.shell.d.ts +19 -0
- package/dist/components/PluginWorkspaceView.vue.d.ts +46 -195
- package/dist/components/ProgressBar.vue.d.ts +1 -1
- package/dist/components/ProtocolStep.presentation.d.ts +4 -0
- package/dist/components/ProtocolStepEditor.state.d.ts +18 -0
- package/dist/components/ProtocolStepParameterField.vue.d.ts +12 -0
- package/dist/components/ReagentList.presentation.d.ts +16 -0
- package/dist/components/ResourceCard.vue.d.ts +1 -1
- package/dist/components/SampleSelector.colors.d.ts +13 -0
- package/dist/components/SampleSelector.drag.d.ts +24 -0
- package/dist/components/SampleSelector.groups.d.ts +15 -0
- package/dist/components/SampleSelector.selection.d.ts +26 -0
- package/dist/components/SampleSelector.vue.d.ts +4 -1
- package/dist/components/SampleSelectorSampleRow.vue.d.ts +21 -0
- package/dist/components/SegmentedControl.vue.d.ts +1 -1
- package/dist/components/SequenceInput.vue.d.ts +2 -2
- package/dist/components/SequenceProgressBar.vue.d.ts +1 -1
- package/dist/components/SettingsModal.schema.d.ts +9 -0
- package/dist/components/StatusIndicator.vue.d.ts +1 -1
- package/dist/components/TagsInput.vue.d.ts +2 -2
- package/dist/components/TimePicker.vue.d.ts +2 -2
- package/dist/components/TimeRangeInput.vue.d.ts +1 -1
- package/dist/components/UnitInput.vue.d.ts +2 -2
- package/dist/components/WellPlate.colors.d.ts +9 -0
- package/dist/components/WellPlate.conditions.d.ts +26 -0
- package/dist/components/WellPlate.geometry.d.ts +23 -0
- package/dist/components/WellPlate.interaction.d.ts +71 -0
- package/dist/components/WellPlate.legend.d.ts +2 -0
- package/dist/components/WellPlate.rendering.d.ts +24 -0
- package/dist/components/WellPlate.sampleDrop.d.ts +8 -0
- package/dist/components/WellPlate.vue.d.ts +1 -1
- package/dist/components/index.js +2 -2
- package/dist/components/internal/ActionItemInternal.vue.d.ts +1 -1
- package/dist/components/internal/PlateMapEditorToolbarInternal.vue.d.ts +28 -0
- package/dist/{components-BhK-dW99.js → components-DtHA2bgp.js} +3754 -2991
- package/dist/components-DtHA2bgp.js.map +1 -0
- package/dist/composables/autoGroup/classKey.d.ts +4 -0
- package/dist/composables/autoGroup/classify.d.ts +28 -0
- package/dist/composables/autoGroup/colors.d.ts +2 -0
- package/dist/composables/autoGroup/columns.d.ts +10 -0
- package/dist/composables/autoGroup/compose.d.ts +8 -0
- package/dist/composables/autoGroup/cooccurrence.d.ts +2 -0
- package/dist/composables/autoGroup/csv-shim.d.ts +2 -0
- package/dist/composables/autoGroup/fingerprint.d.ts +3 -0
- package/dist/composables/autoGroup/index.d.ts +16 -0
- package/dist/composables/autoGroup/replicatePreGroup.d.ts +38 -0
- package/dist/composables/autoGroup/template.d.ts +15 -0
- package/dist/composables/autoGroup/tokenize.d.ts +8 -0
- package/dist/composables/autoGroupConstants.d.ts +1 -0
- package/dist/composables/autoGroupGrouping.d.ts +3 -0
- package/dist/composables/controlComponentBindings.d.ts +7 -0
- package/dist/composables/controlSchemaAdapters.d.ts +20 -0
- package/dist/composables/controlSchemaDoseDesign.d.ts +11 -0
- package/dist/composables/controlSchemaFormFields.d.ts +3 -0
- package/dist/composables/controlSchemaLayout.d.ts +7 -0
- package/dist/composables/controlSchemaModel.d.ts +5 -0
- package/dist/composables/controlSchemaNormalize.d.ts +15 -0
- package/dist/composables/controlSchemaTypes.d.ts +305 -0
- package/dist/composables/controlSchemaUtils.d.ts +9 -0
- package/dist/composables/controlWorkspaceOptions.d.ts +2 -0
- package/dist/composables/formBuilderSchema.d.ts +18 -0
- package/dist/composables/index.js +3 -3
- package/dist/composables/pluginEndpointBuilder.d.ts +13 -0
- package/dist/composables/protocolTemplateCatalog.d.ts +26 -0
- package/dist/composables/useAutoGroup.d.ts +61 -74
- package/dist/composables/useAutoGroupInputSources.d.ts +32 -0
- package/dist/composables/useBioTemplateControls.d.ts +1 -1
- package/dist/composables/useBioTemplatePresetWorkspace.d.ts +1 -1
- package/dist/composables/useBioTemplateWorkspace.d.ts +1 -1
- package/dist/composables/useControlSchema.d.ts +8 -346
- package/dist/composables/useControlWorkspace.d.ts +5 -0
- package/dist/composables/useForm.d.ts +2 -33
- package/dist/composables/useFormBuilder.d.ts +2 -9
- package/dist/composables/useFormValidation.d.ts +34 -0
- package/dist/composables/usePluginClient.d.ts +1 -4
- package/dist/composables/useProtocolTemplates.d.ts +2 -24
- package/dist/composables/useScheduleCalendarLayout.d.ts +49 -0
- package/dist/{composables-Bg7CFuNz.js → composables-Dlg8jenH.js} +33 -31
- package/dist/composables-Dlg8jenH.js.map +1 -0
- package/dist/index.js +4 -4
- package/dist/install.js +2 -2
- package/dist/styles.css +547 -516
- package/dist/templates/adapters.d.ts +14 -47
- package/dist/templates/assayLookups.d.ts +3 -0
- package/dist/templates/assayMatrixAdapters.d.ts +6 -0
- package/dist/templates/assayMatrixBuilder.d.ts +2 -0
- package/dist/templates/assayNormalizers.d.ts +4 -0
- package/dist/templates/builderDefaults.d.ts +1 -0
- package/dist/templates/builderIdUtils.d.ts +4 -0
- package/dist/templates/builderPresetControls.d.ts +10 -0
- package/dist/templates/builderReadUtils.d.ts +8 -0
- package/dist/templates/builders.d.ts +26 -67
- package/dist/templates/calibrationCurveAdapters.d.ts +4 -0
- package/dist/templates/calibrationCurveBuilder.d.ts +2 -0
- package/dist/templates/calibrationNormalizers.d.ts +5 -0
- package/dist/templates/componentBindingCatalog.d.ts +25 -0
- package/dist/templates/componentBindingHelpers.d.ts +17 -0
- package/dist/templates/componentDoseResponseProps.d.ts +3 -0
- package/dist/templates/componentGenericProps.d.ts +8 -0
- package/dist/templates/componentPlateHelpers.d.ts +5 -0
- package/dist/templates/componentPlateMapProps.d.ts +3 -0
- package/dist/templates/componentPropsFactory.d.ts +3 -0
- package/dist/templates/componentQpcrPlateProps.d.ts +3 -0
- package/dist/templates/componentTargetResolvers.d.ts +5 -0
- package/dist/templates/componentTemplateProps.d.ts +3 -0
- package/dist/templates/controlSchemaClone.d.ts +4 -0
- package/dist/templates/controlSchemaConstants.d.ts +10 -0
- package/dist/templates/controlSchemaMerge.d.ts +3 -0
- package/dist/templates/controlSchemaTargets.d.ts +17 -0
- package/dist/templates/controlSchemaTypes.d.ts +4 -0
- package/dist/templates/controlSchemas.d.ts +4 -4
- package/dist/templates/defaultBioTemplateBuilder.d.ts +2 -0
- package/dist/templates/doseResponseAdapters.d.ts +4 -0
- package/dist/templates/doseResponseBuilder.d.ts +2 -0
- package/dist/templates/elisaAssayCollectionBuilder.d.ts +2 -0
- package/dist/templates/flowCytometryAssayCollectionBuilder.d.ts +2 -0
- package/dist/templates/flowCytometryPanelBuilder.d.ts +2 -0
- package/dist/templates/flowNormalizers.d.ts +8 -0
- package/dist/templates/flowPanelAdapters.d.ts +4 -0
- package/dist/templates/index.js +1 -1
- package/dist/templates/instrumentRunAdapterHelpers.d.ts +8 -0
- package/dist/templates/instrumentRunAdapters.d.ts +8 -0
- package/dist/templates/instrumentRunBuilder.d.ts +2 -0
- package/dist/templates/lcmsBatchCollectionBuilder.d.ts +2 -0
- package/dist/templates/plateGeometry.d.ts +4 -0
- package/dist/templates/plateMapAdapters.d.ts +3 -0
- package/dist/templates/plateMapBuilder.d.ts +2 -0
- package/dist/templates/presetControlSchemas.d.ts +534 -0
- package/dist/templates/protocolAdapters.d.ts +5 -0
- package/dist/templates/protocolNormalizers.d.ts +6 -0
- package/dist/templates/protocolStepsBuilder.d.ts +2 -0
- package/dist/templates/qpcrAdapters.d.ts +5 -0
- package/dist/templates/qpcrExpressionCollectionBuilder.d.ts +2 -0
- package/dist/templates/qpcrPlateBuilder.d.ts +2 -0
- package/dist/templates/reagentAdapters.d.ts +5 -0
- package/dist/templates/reagentListBuilder.d.ts +2 -0
- package/dist/templates/runNormalizers.d.ts +10 -0
- package/dist/templates/sampleNormalizers.d.ts +4 -0
- package/dist/templates/samplePrepAdapters.d.ts +4 -0
- package/dist/templates/samplePrepBuilder.d.ts +2 -0
- package/dist/templates/sampleSheetAdapters.d.ts +5 -0
- package/dist/templates/sampleSheetBuilder.d.ts +2 -0
- package/dist/templates/targetedMetabolomicsCollectionBuilder.d.ts +2 -0
- package/dist/templates/targetedMetabolomicsHelpers.d.ts +5 -0
- package/dist/templates/templateAdapterTypes.d.ts +48 -0
- package/dist/templates/templateControlSchemas.d.ts +400 -0
- package/dist/templates/templateCreateOptions.d.ts +165 -0
- package/dist/templates/templateEnvelopes.d.ts +9 -0
- package/dist/templates/templatePackCollectionBuilder.d.ts +2 -0
- package/dist/templates/templatePresetCollectionBuilder.d.ts +18 -0
- package/dist/templates/templateQpcrTypes.d.ts +42 -0
- package/dist/templates/templateValidators.d.ts +13 -0
- package/dist/templates/timeCourseAdapters.d.ts +5 -0
- package/dist/templates/timeCourseBuilder.d.ts +2 -0
- package/dist/templates/types.d.ts +5 -250
- package/dist/templates/wellPlateScreenCollectionBuilder.d.ts +2 -0
- package/dist/templates/westernBlotAssayCollectionBuilder.d.ts +2 -0
- package/dist/{templates-BorLR_7p.js → templates-DtdUvJ4c.js} +3565 -3411
- package/dist/templates-DtdUvJ4c.js.map +1 -0
- package/dist/types/auto-group.d.ts +79 -9
- package/dist/types/componentLabTypes.d.ts +161 -0
- package/dist/types/componentWorkflowTypes.d.ts +150 -0
- package/dist/types/components.d.ts +2 -311
- package/dist/{useProtocolTemplates-n6AJqSqv.js → useProtocolTemplates-Bm5vyH4_.js} +1220 -454
- package/dist/useProtocolTemplates-Bm5vyH4_.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/components/AppTopBar.navigation.test.ts +70 -0
- package/src/__tests__/components/DoseCalculatorVolumeField.test.ts +53 -0
- package/src/__tests__/components/PlateMapEditorToolbarInternal.test.ts +54 -0
- package/src/__tests__/components/PluginWorkspaceView.controls.test.ts +156 -0
- package/src/__tests__/components/PluginWorkspaceView.navigation.test.ts +102 -0
- package/src/__tests__/components/PluginWorkspaceView.shell.test.ts +41 -0
- package/src/__tests__/components/ProtocolStep.presentation.test.ts +31 -0
- package/src/__tests__/components/ProtocolStepEditor.state.test.ts +165 -0
- package/src/__tests__/components/ProtocolStepParameterField.test.ts +44 -0
- package/src/__tests__/components/ReagentList.presentation.test.ts +68 -0
- package/src/__tests__/components/SampleSelector.colors.test.ts +49 -0
- package/src/__tests__/components/SampleSelector.drag.test.ts +100 -0
- package/src/__tests__/components/SampleSelector.groups.test.ts +81 -0
- package/src/__tests__/components/SampleSelector.selection.test.ts +70 -0
- package/src/__tests__/components/SampleSelector.test.ts +32 -0
- package/src/__tests__/components/SampleSelectorSampleRow.test.ts +37 -0
- package/src/__tests__/components/ScheduleCalendar.test.ts +44 -0
- package/src/__tests__/components/SettingsModal.schema.test.ts +97 -0
- package/src/__tests__/components/WellPlate.colors.test.ts +28 -0
- package/src/__tests__/components/WellPlate.conditions.test.ts +68 -0
- package/src/__tests__/components/WellPlate.geometry.test.ts +54 -0
- package/src/__tests__/components/WellPlate.interaction.test.ts +171 -0
- package/src/__tests__/components/WellPlate.legend.test.ts +13 -0
- package/src/__tests__/components/WellPlate.rendering.test.ts +122 -0
- package/src/__tests__/components/WellPlate.sampleDrop.test.ts +70 -0
- package/src/__tests__/composables/autoGroup/classify.test.ts +107 -0
- package/src/__tests__/composables/autoGroup/columns.test.ts +135 -0
- package/src/__tests__/composables/autoGroup/compose.test.ts +227 -0
- package/src/__tests__/composables/autoGroup/cooccurrence.test.ts +91 -0
- package/src/__tests__/composables/autoGroup/fingerprint.test.ts +50 -0
- package/src/__tests__/composables/autoGroup/integration.test.ts +79 -0
- package/src/__tests__/composables/autoGroup/template.test.ts +70 -0
- package/src/__tests__/composables/autoGroup/tokenize.test.ts +33 -0
- package/src/__tests__/composables/useAutoGroup.test.ts +129 -625
- package/src/__tests__/composables/useAutoGroupInputSources.test.ts +107 -0
- package/src/__tests__/composables/useControlSchema.test.ts +23 -0
- package/src/__tests__/composables/useScheduleCalendarLayout.test.ts +89 -0
- package/src/__tests__/docs/extractDocsComponents.test.ts +142 -0
- package/src/__tests__/docs/extractDocsExports.test.ts +77 -0
- package/src/__tests__/docs/extractDocsParsing.test.ts +69 -0
- package/src/__tests__/docs/extractDocsTemplates.test.ts +54 -0
- package/src/__tests__/docs/extractDocsTheme.test.ts +89 -0
- package/src/__tests__/docs/frontendDocsCatalog.test.ts +1 -1
- package/src/__tests__/fixtures/auto-group/mixed-lc-ms-batch.txt +187 -0
- package/src/components/AppSidebar.vue +2 -6
- package/src/components/AppTopBar.navigation.ts +62 -0
- package/src/components/AppTopBar.vue +17 -44
- package/src/components/AutoGroupModal.story.vue +50 -0
- package/src/components/AutoGroupModal.vue +441 -158
- package/src/components/ControlWorkspaceView.vue +2 -6
- package/src/components/DoseCalculator.vue +13 -73
- package/src/components/DoseCalculatorVolumeField.vue +61 -0
- package/src/components/ExperimentTimeline.vue +6 -31
- package/src/components/FormBuilder.vue +2 -7
- package/src/components/PlateMapEditor.vue +32 -106
- package/src/components/PluginWorkspaceView.controls.ts +182 -0
- package/src/components/PluginWorkspaceView.navigation.ts +106 -0
- package/src/components/PluginWorkspaceView.props.ts +174 -0
- package/src/components/PluginWorkspaceView.shell.ts +66 -0
- package/src/components/PluginWorkspaceView.vue +85 -404
- package/src/components/ProtocolStep.presentation.ts +31 -0
- package/src/components/ProtocolStepEditor.state.ts +104 -0
- package/src/components/ProtocolStepEditor.vue +48 -179
- package/src/components/ProtocolStepParameterField.vue +134 -0
- package/src/components/ReagentList.presentation.ts +105 -0
- package/src/components/ReagentList.vue +16 -79
- package/src/components/SampleSelector.colors.ts +43 -0
- package/src/components/SampleSelector.drag.ts +164 -0
- package/src/components/SampleSelector.groups.ts +109 -0
- package/src/components/SampleSelector.selection.ts +103 -0
- package/src/components/SampleSelector.vue +82 -349
- package/src/components/SampleSelectorSampleRow.vue +64 -0
- package/src/components/ScheduleCalendar.vue +44 -199
- package/src/components/SettingsModal.schema.ts +71 -0
- package/src/components/SettingsModal.vue +16 -46
- package/src/components/WellPlate.colors.ts +56 -0
- package/src/components/WellPlate.conditions.ts +100 -0
- package/src/components/WellPlate.geometry.ts +91 -0
- package/src/components/WellPlate.interaction.ts +272 -0
- package/src/components/WellPlate.legend.ts +8 -0
- package/src/components/WellPlate.rendering.ts +105 -0
- package/src/components/WellPlate.sampleDrop.ts +73 -0
- package/src/components/WellPlate.vue +102 -550
- package/src/components/internal/PlateMapEditorToolbarInternal.vue +128 -0
- package/src/composables/autoGroup/classKey.ts +5 -0
- package/src/composables/autoGroup/classify.ts +205 -0
- package/src/composables/autoGroup/colors.ts +6 -0
- package/src/composables/autoGroup/columns.ts +226 -0
- package/src/composables/autoGroup/compose.ts +156 -0
- package/src/composables/autoGroup/cooccurrence.ts +46 -0
- package/src/composables/autoGroup/csv-shim.ts +44 -0
- package/src/composables/autoGroup/fingerprint.ts +49 -0
- package/src/composables/autoGroup/index.ts +20 -0
- package/src/composables/autoGroup/replicatePreGroup.ts +90 -0
- package/src/composables/autoGroup/template.ts +126 -0
- package/src/composables/autoGroup/tokenize.ts +41 -0
- package/src/composables/autoGroup/vocab.json +67 -0
- package/src/composables/autoGroupConstants.ts +4 -0
- package/src/composables/autoGroupGrouping.ts +148 -0
- package/src/composables/controlComponentBindings.ts +80 -0
- package/src/composables/controlSchemaAdapters.ts +196 -0
- package/src/composables/controlSchemaDoseDesign.ts +215 -0
- package/src/composables/controlSchemaFormFields.ts +61 -0
- package/src/composables/controlSchemaLayout.ts +59 -0
- package/src/composables/controlSchemaModel.ts +163 -0
- package/src/composables/controlSchemaNormalize.ts +101 -0
- package/src/composables/controlSchemaTypes.ts +364 -0
- package/src/composables/controlSchemaUtils.ts +36 -0
- package/src/composables/controlWorkspaceOptions.ts +21 -0
- package/src/composables/formBuilderSchema.ts +153 -0
- package/src/composables/pluginEndpointBuilder.ts +203 -0
- package/src/composables/protocolTemplateCatalog.ts +325 -0
- package/src/composables/useAutoGroup.ts +395 -549
- package/src/composables/useAutoGroupInputSources.ts +147 -0
- package/src/composables/useBioTemplateControls.ts +1 -1
- package/src/composables/useBioTemplatePresetWorkspace.ts +1 -1
- package/src/composables/useBioTemplateWorkspace.ts +1 -1
- package/src/composables/useControlSchema.ts +64 -1312
- package/src/composables/useControlWorkspace.ts +201 -0
- package/src/composables/useForm.ts +5 -187
- package/src/composables/useFormBuilder.ts +11 -153
- package/src/composables/useFormValidation.ts +154 -0
- package/src/composables/usePluginClient.ts +10 -193
- package/src/composables/useProtocolTemplates.ts +10 -328
- package/src/composables/useScheduleCalendarLayout.ts +287 -0
- package/src/styles/components/auto-group-modal.css +248 -310
- package/src/templates/adapters.ts +89 -930
- package/src/templates/assayLookups.ts +33 -0
- package/src/templates/assayMatrixAdapters.ts +78 -0
- package/src/templates/assayMatrixBuilder.ts +59 -0
- package/src/templates/assayNormalizers.ts +34 -0
- package/src/templates/builderDefaults.ts +11 -0
- package/src/templates/builderIdUtils.ts +20 -0
- package/src/templates/builderPresetControls.ts +165 -0
- package/src/templates/builderReadUtils.ts +57 -0
- package/src/templates/builders.ts +122 -2350
- package/src/templates/calibrationCurveAdapters.ts +59 -0
- package/src/templates/calibrationCurveBuilder.ts +99 -0
- package/src/templates/calibrationNormalizers.ts +60 -0
- package/src/templates/componentBindingCatalog.ts +90 -0
- package/src/templates/componentBindingHelpers.ts +93 -0
- package/src/templates/componentBindings.ts +12 -461
- package/src/templates/componentDoseResponseProps.ts +42 -0
- package/src/templates/componentGenericProps.ts +77 -0
- package/src/templates/componentPlateHelpers.ts +29 -0
- package/src/templates/componentPlateMapProps.ts +32 -0
- package/src/templates/componentPropsFactory.ts +21 -0
- package/src/templates/componentQpcrPlateProps.ts +28 -0
- package/src/templates/componentTargetResolvers.ts +69 -0
- package/src/templates/componentTemplateProps.ts +78 -0
- package/src/templates/controlSchemaClone.ts +32 -0
- package/src/templates/controlSchemaConstants.ts +11 -0
- package/src/templates/controlSchemaMerge.ts +40 -0
- package/src/templates/controlSchemaTargets.ts +87 -0
- package/src/templates/controlSchemaTypes.ts +20 -0
- package/src/templates/controlSchemas.ts +22 -704
- package/src/templates/defaultBioTemplateBuilder.ts +124 -0
- package/src/templates/doseResponseAdapters.ts +45 -0
- package/src/templates/doseResponseBuilder.ts +44 -0
- package/src/templates/elisaAssayCollectionBuilder.ts +62 -0
- package/src/templates/flowCytometryAssayCollectionBuilder.ts +41 -0
- package/src/templates/flowCytometryPanelBuilder.ts +53 -0
- package/src/templates/flowNormalizers.ts +58 -0
- package/src/templates/flowPanelAdapters.ts +58 -0
- package/src/templates/instrumentRunAdapterHelpers.ts +94 -0
- package/src/templates/instrumentRunAdapters.ts +163 -0
- package/src/templates/instrumentRunBuilder.ts +97 -0
- package/src/templates/lcmsBatchCollectionBuilder.ts +38 -0
- package/src/templates/plateGeometry.ts +62 -0
- package/src/templates/plateMapAdapters.ts +36 -0
- package/src/templates/plateMapBuilder.ts +43 -0
- package/src/templates/presetControlSchemas.ts +258 -0
- package/src/templates/protocolAdapters.ts +69 -0
- package/src/templates/protocolNormalizers.ts +37 -0
- package/src/templates/protocolStepsBuilder.ts +36 -0
- package/src/templates/qpcrAdapters.ts +104 -0
- package/src/templates/qpcrExpressionCollectionBuilder.ts +33 -0
- package/src/templates/qpcrPlateBuilder.ts +96 -0
- package/src/templates/reagentAdapters.ts +77 -0
- package/src/templates/reagentListBuilder.ts +30 -0
- package/src/templates/runNormalizers.ts +63 -0
- package/src/templates/sampleNormalizers.ts +58 -0
- package/src/templates/samplePrepAdapters.ts +63 -0
- package/src/templates/samplePrepBuilder.ts +51 -0
- package/src/templates/sampleSheetAdapters.ts +75 -0
- package/src/templates/sampleSheetBuilder.ts +23 -0
- package/src/templates/targetedMetabolomicsCollectionBuilder.ts +79 -0
- package/src/templates/targetedMetabolomicsHelpers.ts +102 -0
- package/src/templates/templateAdapterTypes.ts +58 -0
- package/src/templates/templateControlSchemas.ts +320 -0
- package/src/templates/templateCreateOptions.ts +208 -0
- package/src/templates/templateEnvelopes.ts +137 -0
- package/src/templates/templatePackCollectionBuilder.ts +23 -0
- package/src/templates/templatePresetCollectionBuilder.ts +139 -0
- package/src/templates/templateQpcrTypes.ts +48 -0
- package/src/templates/templateValidators.ts +414 -0
- package/src/templates/timeCourseAdapters.ts +73 -0
- package/src/templates/timeCourseBuilder.ts +64 -0
- package/src/templates/types.ts +79 -275
- package/src/templates/wellPlateScreenCollectionBuilder.ts +36 -0
- package/src/templates/westernBlotAssayCollectionBuilder.ts +68 -0
- package/src/types/auto-group.ts +107 -9
- package/src/types/componentLabTypes.ts +235 -0
- package/src/types/componentWorkflowTypes.ts +190 -0
- package/src/types/components.ts +74 -424
- package/dist/components-BhK-dW99.js.map +0 -1
- package/dist/composables-Bg7CFuNz.js.map +0 -1
- package/dist/templates-BorLR_7p.js.map +0 -1
- package/dist/useProtocolTemplates-n6AJqSqv.js.map +0 -1
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AutoGroupResult,
|
|
3
|
+
ClassSchema,
|
|
4
|
+
ColumnInfo,
|
|
5
|
+
MetadataRow,
|
|
6
|
+
NumericBinning,
|
|
7
|
+
SampleClass,
|
|
8
|
+
} from '../../types/auto-group'
|
|
9
|
+
import type { SampleGroup } from '../../types/components'
|
|
10
|
+
import { DEFAULT_COLORS, QC_OVERLAY_COLOR } from './colors'
|
|
11
|
+
import { classKey } from './classKey'
|
|
12
|
+
|
|
13
|
+
export interface ComposeInput {
|
|
14
|
+
tokenizedSamples: string[][]
|
|
15
|
+
sampleNames: string[]
|
|
16
|
+
schemas: Record<string, ClassSchema>
|
|
17
|
+
classes: SampleClass[]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Apply the operation pipeline to a single raw token value:
|
|
21
|
+
* numeric-parse → alias → exclude → bin
|
|
22
|
+
* Returns null if the row is excluded.
|
|
23
|
+
*/
|
|
24
|
+
function applyOps(col: ColumnInfo, raw: string): string | null {
|
|
25
|
+
let value = raw
|
|
26
|
+
|
|
27
|
+
// alias (operates on raw source value)
|
|
28
|
+
if (col.ops?.alias && raw in col.ops.alias) {
|
|
29
|
+
value = col.ops.alias[raw]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// exclude (post-alias)
|
|
33
|
+
if (col.ops?.exclude?.includes(raw) || col.ops?.exclude?.includes(value)) {
|
|
34
|
+
return null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// bin (only when role === 'numeric' and binning.mode === 'binned')
|
|
38
|
+
if (col.role === 'numeric' && col.binning?.mode === 'binned') {
|
|
39
|
+
const parsed = col.numeric?.byValue[raw]
|
|
40
|
+
if (parsed && col.binning.edges) {
|
|
41
|
+
const bin = pickBin(parsed.value, col.binning)
|
|
42
|
+
if (bin !== null) value = bin
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return value
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function pickBin(value: number, b: NumericBinning): string | null {
|
|
50
|
+
if (!b.edges || b.edges.length === 0) return null
|
|
51
|
+
for (let i = 0; i < b.edges.length; i++) {
|
|
52
|
+
if (value < b.edges[i]) {
|
|
53
|
+
return b.labels?.[i] ?? `< ${b.edges[i]}`
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const last = b.edges[b.edges.length - 1]
|
|
57
|
+
return b.labels?.[b.edges.length] ?? `> ${last}`
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function composeGroups(input: ComposeInput): AutoGroupResult {
|
|
61
|
+
const experimentalGroups: SampleGroup[] = []
|
|
62
|
+
const qcGroups: SampleGroup[] = []
|
|
63
|
+
const excludedSamples: string[] = []
|
|
64
|
+
const metadata: MetadataRow[] = []
|
|
65
|
+
let colorIdx = 0
|
|
66
|
+
|
|
67
|
+
for (const cls of input.classes) {
|
|
68
|
+
const schema = input.schemas[classKey(cls)]
|
|
69
|
+
if (!schema) continue
|
|
70
|
+
|
|
71
|
+
if (cls.disposition === 'exclude') {
|
|
72
|
+
for (const m of cls.members) excludedSamples.push(input.sampleNames[m])
|
|
73
|
+
continue
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Per-class group map. group key built from enabled (groupBy) columns,
|
|
77
|
+
// joined with ' / ' for hierarchical display.
|
|
78
|
+
const groupMap = new Map<string, { name: string; samples: string[] }>()
|
|
79
|
+
const colByIdx = new Map(schema.columns.map(c => [c.index, c]))
|
|
80
|
+
|
|
81
|
+
for (const m of cls.members) {
|
|
82
|
+
const tokens = input.tokenizedSamples[m]
|
|
83
|
+
const rowName = input.sampleNames[m]
|
|
84
|
+
let excluded = false
|
|
85
|
+
const fieldValues: Record<string, string> = {}
|
|
86
|
+
|
|
87
|
+
for (const col of schema.columns) {
|
|
88
|
+
const sourceValues = col.sourceIndices.map(idx => tokens[idx] ?? '')
|
|
89
|
+
const raw = sourceValues.join('_') // merged columns join their source tokens
|
|
90
|
+
const display = applyOps(col, raw)
|
|
91
|
+
if (display === null) {
|
|
92
|
+
excluded = true
|
|
93
|
+
break
|
|
94
|
+
}
|
|
95
|
+
fieldValues[col.displayName ?? col.name] = display
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (excluded) {
|
|
99
|
+
excludedSamples.push(rowName)
|
|
100
|
+
continue
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const keyParts: string[] =
|
|
104
|
+
cls.disposition === 'group'
|
|
105
|
+
? schema.groupBy.map(idx => {
|
|
106
|
+
const col = colByIdx.get(idx)!
|
|
107
|
+
return fieldValues[col.displayName ?? col.name]
|
|
108
|
+
})
|
|
109
|
+
: [cls.label]
|
|
110
|
+
|
|
111
|
+
// Prepend the class label to the factor-key parts so group names carry
|
|
112
|
+
// class context (e.g. "Biological / Tissues / POS / 5min" instead of a
|
|
113
|
+
// bare "5min" that collides with another class's "5min").
|
|
114
|
+
//
|
|
115
|
+
// Only fires for disposition === 'group' classes with actual factor
|
|
116
|
+
// keyParts: overlay/exclude classes already use cls.label AS their
|
|
117
|
+
// single key part, so prefixing again would produce "IQC / IQC". The
|
|
118
|
+
// Unknown class also opts out — its label ("Unknown") adds no signal.
|
|
119
|
+
const useClassPrefix =
|
|
120
|
+
cls.disposition === 'group' && cls.kind !== 'unknown' && keyParts.length > 0
|
|
121
|
+
const groupKey = useClassPrefix
|
|
122
|
+
? `${cls.label} / ${keyParts.join(' / ')}`
|
|
123
|
+
: keyParts.length > 0
|
|
124
|
+
? keyParts.join(' / ')
|
|
125
|
+
: cls.label
|
|
126
|
+
|
|
127
|
+
const bucket = groupMap.get(groupKey)
|
|
128
|
+
if (bucket) {
|
|
129
|
+
bucket.samples.push(rowName)
|
|
130
|
+
} else {
|
|
131
|
+
groupMap.set(groupKey, { name: groupKey, samples: [rowName] })
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
metadata.push({ sampleName: rowName, fields: fieldValues, group: groupKey })
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
for (const { name, samples } of groupMap.values()) {
|
|
138
|
+
const color =
|
|
139
|
+
cls.disposition === 'overlay'
|
|
140
|
+
? QC_OVERLAY_COLOR
|
|
141
|
+
: DEFAULT_COLORS[colorIdx++ % DEFAULT_COLORS.length]
|
|
142
|
+
const g: SampleGroup = { name, color, samples }
|
|
143
|
+
if (cls.disposition === 'overlay') qcGroups.push(g)
|
|
144
|
+
else experimentalGroups.push(g)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
groups: [...experimentalGroups, ...qcGroups],
|
|
150
|
+
experimentalGroups,
|
|
151
|
+
qcGroups,
|
|
152
|
+
metadata,
|
|
153
|
+
excludedSamples,
|
|
154
|
+
schemas: Object.values(input.schemas),
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ClassSchema, MergeSuggestion } from '../../types/auto-group'
|
|
2
|
+
|
|
3
|
+
const THRESHOLD = 0.95
|
|
4
|
+
const MIN_CLASS_SIZE = 4
|
|
5
|
+
|
|
6
|
+
function functionalDependency(rows: string[][], srcIdx: number, dstIdx: number): number {
|
|
7
|
+
const map = new Map<string, Set<string>>()
|
|
8
|
+
for (const row of rows) {
|
|
9
|
+
const a = row[srcIdx] ?? ''
|
|
10
|
+
const b = row[dstIdx] ?? ''
|
|
11
|
+
const set = map.get(a)
|
|
12
|
+
if (set) set.add(b)
|
|
13
|
+
else map.set(a, new Set([b]))
|
|
14
|
+
}
|
|
15
|
+
if (map.size === 0) return 0
|
|
16
|
+
let functional = 0
|
|
17
|
+
for (const set of map.values()) if (set.size === 1) functional++
|
|
18
|
+
return functional / map.size
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function findMerges(schema: ClassSchema, rows: string[][]): MergeSuggestion[] {
|
|
22
|
+
if (rows.length < MIN_CLASS_SIZE) return []
|
|
23
|
+
|
|
24
|
+
const out: MergeSuggestion[] = []
|
|
25
|
+
for (let i = 0; i < schema.columns.length - 1; i++) {
|
|
26
|
+
const a = schema.columns[i]
|
|
27
|
+
const b = schema.columns[i + 1]
|
|
28
|
+
if (a.role !== 'factor' && b.role !== 'factor') continue
|
|
29
|
+
|
|
30
|
+
const forward = functionalDependency(rows, a.index, b.index)
|
|
31
|
+
const backward = functionalDependency(rows, b.index, a.index)
|
|
32
|
+
if (forward < THRESHOLD || backward < THRESHOLD) continue
|
|
33
|
+
|
|
34
|
+
const merged = new Set(rows.map(r => `${r[a.index] ?? ''}_${r[b.index] ?? ''}`))
|
|
35
|
+
if (merged.size < 2) continue
|
|
36
|
+
|
|
37
|
+
out.push({
|
|
38
|
+
classKind: schema.classKind,
|
|
39
|
+
subKind: schema.subKind,
|
|
40
|
+
columnIndices: [a.index, b.index],
|
|
41
|
+
proposedName: `Column ${a.index + 1}–${b.index + 1}`,
|
|
42
|
+
confidence: Math.min(forward, backward),
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
return out
|
|
46
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ParsedCsvData } from '../../types/auto-group'
|
|
2
|
+
|
|
3
|
+
function parseCSVLine(line: string, delimiter: string = ','): string[] {
|
|
4
|
+
const result: string[] = []
|
|
5
|
+
let current = ''
|
|
6
|
+
let inQuotes = false
|
|
7
|
+
for (let i = 0; i < line.length; i++) {
|
|
8
|
+
const char = line[i]
|
|
9
|
+
if (char === '"') {
|
|
10
|
+
if (inQuotes && line[i + 1] === '"') {
|
|
11
|
+
current += '"'
|
|
12
|
+
i++
|
|
13
|
+
} else {
|
|
14
|
+
inQuotes = !inQuotes
|
|
15
|
+
}
|
|
16
|
+
} else if (char === delimiter && !inQuotes) {
|
|
17
|
+
result.push(current.trim())
|
|
18
|
+
current = ''
|
|
19
|
+
} else {
|
|
20
|
+
current += char
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
result.push(current.trim())
|
|
24
|
+
return result
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function parseCSV(text: string): ParsedCsvData {
|
|
28
|
+
const lines = text.trim().split('\n')
|
|
29
|
+
if (lines.length < 2) throw new Error('CSV must have at least a header and one data row')
|
|
30
|
+
const firstLine = lines[0]
|
|
31
|
+
const csvDelimiter = firstLine.includes('\t') ? '\t' : ','
|
|
32
|
+
const headers = parseCSVLine(firstLine, csvDelimiter)
|
|
33
|
+
const rows: Record<string, string>[] = []
|
|
34
|
+
for (let i = 1; i < lines.length; i++) {
|
|
35
|
+
const values = parseCSVLine(lines[i], csvDelimiter)
|
|
36
|
+
if (values.length !== headers.length) continue
|
|
37
|
+
const row: Record<string, string> = {}
|
|
38
|
+
headers.forEach((h, idx) => { row[h] = values[idx] })
|
|
39
|
+
rows.push(row)
|
|
40
|
+
}
|
|
41
|
+
const sampleKeywords = ['sample', 'name', 'id', 'sample_name', 'samplename', 'file name', 'filename', 'file_name']
|
|
42
|
+
const sampleColumn = headers.find(h => sampleKeywords.includes(h.toLowerCase())) ?? headers[0]
|
|
43
|
+
return { columns: headers, rows, sampleColumn, delimiter: csvDelimiter }
|
|
44
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ClassSchema, SchemaFingerprint } from '../../types/auto-group'
|
|
2
|
+
|
|
3
|
+
export function serializeFingerprint(schemas: ClassSchema[]): SchemaFingerprint {
|
|
4
|
+
return {
|
|
5
|
+
version: 1,
|
|
6
|
+
classes: schemas.map(s => ({
|
|
7
|
+
kind: s.classKind,
|
|
8
|
+
subKind: s.subKind,
|
|
9
|
+
columns: s.columns.map(c => ({
|
|
10
|
+
name: c.displayName ?? c.name,
|
|
11
|
+
role: c.role ?? 'factor',
|
|
12
|
+
sourceIndices: c.sourceIndices,
|
|
13
|
+
binning: c.binning,
|
|
14
|
+
ops: c.ops,
|
|
15
|
+
})),
|
|
16
|
+
groupBy: s.groupBy,
|
|
17
|
+
})),
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function restoreFingerprint(
|
|
22
|
+
fp: SchemaFingerprint,
|
|
23
|
+
current: ClassSchema[],
|
|
24
|
+
): ClassSchema[] {
|
|
25
|
+
return fp.classes.map(snap => {
|
|
26
|
+
const target = current.find(c => c.classKind === snap.kind && c.subKind === snap.subKind)
|
|
27
|
+
if (!target) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
`Fingerprint class not present in current input: ${snap.kind}${snap.subKind ? '/' + snap.subKind : ''}`,
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
if (target.columns.length !== snap.columns.length) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Saved schema expects ${snap.columns.length} columns, current data has ${target.columns.length} for class ${snap.kind}${snap.subKind ? '/' + snap.subKind : ''}. column count mismatch`,
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
...target,
|
|
39
|
+
groupBy: snap.groupBy,
|
|
40
|
+
columns: target.columns.map((col, i) => ({
|
|
41
|
+
...col,
|
|
42
|
+
displayName: snap.columns[i].name,
|
|
43
|
+
role: snap.columns[i].role,
|
|
44
|
+
binning: snap.columns[i].binning,
|
|
45
|
+
ops: snap.columns[i].ops,
|
|
46
|
+
})),
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { splitMulti, pickPrimaryDelimiter } from './tokenize'
|
|
2
|
+
export type { Delimiter } from './tokenize'
|
|
3
|
+
export { detectClass, classVocab } from './classify'
|
|
4
|
+
export type { ClassVocab, DetectClassOptions } from './classify'
|
|
5
|
+
export { inferRole, parseNumeric, buildClassSchema } from './columns'
|
|
6
|
+
export type { InferRoleInput } from './columns'
|
|
7
|
+
export { findMerges } from './cooccurrence'
|
|
8
|
+
export { composeGroups } from './compose'
|
|
9
|
+
export type { ComposeInput } from './compose'
|
|
10
|
+
export { serializeFingerprint, restoreFingerprint } from './fingerprint'
|
|
11
|
+
export { composeTemplate } from './template'
|
|
12
|
+
export type { TemplateOptions } from './template'
|
|
13
|
+
export { DEFAULT_COLORS, QC_OVERLAY_COLOR } from './colors'
|
|
14
|
+
export { classKey } from './classKey'
|
|
15
|
+
export {
|
|
16
|
+
stripReplicateTokens,
|
|
17
|
+
preGroupReplicates,
|
|
18
|
+
expandGroupsWithReplicates,
|
|
19
|
+
} from './replicatePreGroup'
|
|
20
|
+
export type { ReplicatePreGrouping } from './replicatePreGroup'
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replicate pre-grouping.
|
|
3
|
+
*
|
|
4
|
+
* Strip the replicate / injection-number markers from each sample's name, then
|
|
5
|
+
* collapse samples that share a base name. Those samples ARE replicates of one
|
|
6
|
+
* another by definition; the remaining unique base names are what the rest of
|
|
7
|
+
* the pipeline (tokenize → classify → schema → compose) should operate on.
|
|
8
|
+
*
|
|
9
|
+
* This pre-pass replaces the older "treat every sample as independent, then
|
|
10
|
+
* infer replicate columns" approach. Identifying replicate-equivalence by name
|
|
11
|
+
* before tokenisation removes a class of fragile cardinality heuristics.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import vocabData from './vocab.json'
|
|
15
|
+
|
|
16
|
+
const STRIP_PATTERNS: RegExp[] = (vocabData.replicateStripPatterns as string[]).map(
|
|
17
|
+
src => new RegExp(src, 'gi'),
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Iteratively strip every replicate / injection-number marker from `name`.
|
|
22
|
+
* Each pattern is applied repeatedly until it stops matching, so a sample like
|
|
23
|
+
* `study_T1_Rep2_004` collapses to `study` in one call.
|
|
24
|
+
*/
|
|
25
|
+
export function stripReplicateTokens(name: string): string {
|
|
26
|
+
let base = name
|
|
27
|
+
for (const re of STRIP_PATTERNS) {
|
|
28
|
+
let prev
|
|
29
|
+
do {
|
|
30
|
+
prev = base
|
|
31
|
+
base = base.replace(re, '')
|
|
32
|
+
re.lastIndex = 0
|
|
33
|
+
} while (base !== prev)
|
|
34
|
+
}
|
|
35
|
+
return base
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ReplicatePreGrouping {
|
|
39
|
+
/** Unique base names in first-seen order. Length === `originalSamples.length` only if every sample has a distinct base. */
|
|
40
|
+
baseNames: string[]
|
|
41
|
+
/**
|
|
42
|
+
* `membersByBase[i]` = original-sample indices that collapsed onto `baseNames[i]`.
|
|
43
|
+
* Length of each entry is the replicate count for that base.
|
|
44
|
+
*/
|
|
45
|
+
membersByBase: number[][]
|
|
46
|
+
/** Original sample names indexed by their position in `samples`. Retained so the compose layer can expand groups back to originals. */
|
|
47
|
+
originalSamples: string[]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function preGroupReplicates(samples: string[]): ReplicatePreGrouping {
|
|
51
|
+
const map = new Map<string, number[]>()
|
|
52
|
+
const baseOrder: string[] = []
|
|
53
|
+
for (let i = 0; i < samples.length; i++) {
|
|
54
|
+
const base = stripReplicateTokens(samples[i])
|
|
55
|
+
const bucket = map.get(base)
|
|
56
|
+
if (bucket) {
|
|
57
|
+
bucket.push(i)
|
|
58
|
+
} else {
|
|
59
|
+
map.set(base, [i])
|
|
60
|
+
baseOrder.push(base)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
baseNames: baseOrder,
|
|
65
|
+
membersByBase: baseOrder.map(b => map.get(b)!),
|
|
66
|
+
originalSamples: samples,
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Expand groups produced over base names back to groups of original sample
|
|
72
|
+
* names. Each entry in `group.samples` is treated as a base name and replaced
|
|
73
|
+
* by the list of original samples that collapsed onto it.
|
|
74
|
+
*/
|
|
75
|
+
export function expandGroupsWithReplicates<G extends { samples: string[] }>(
|
|
76
|
+
groups: G[],
|
|
77
|
+
preGrouping: ReplicatePreGrouping,
|
|
78
|
+
): G[] {
|
|
79
|
+
const baseToMembers = new Map<string, number[]>()
|
|
80
|
+
for (let i = 0; i < preGrouping.baseNames.length; i++) {
|
|
81
|
+
baseToMembers.set(preGrouping.baseNames[i], preGrouping.membersByBase[i])
|
|
82
|
+
}
|
|
83
|
+
return groups.map(g => ({
|
|
84
|
+
...g,
|
|
85
|
+
samples: g.samples.flatMap(baseName => {
|
|
86
|
+
const indices = baseToMembers.get(baseName)
|
|
87
|
+
return indices ? indices.map(i => preGrouping.originalSamples[i]) : [baseName]
|
|
88
|
+
}),
|
|
89
|
+
}))
|
|
90
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { ClassSchema, ColumnInfo, SampleClass } from '../../types/auto-group'
|
|
2
|
+
import { classKey } from './classKey'
|
|
3
|
+
|
|
4
|
+
export interface TemplateOptions {
|
|
5
|
+
mode: 'blank' | 'prefilled'
|
|
6
|
+
format: 'csv' | 'tsv'
|
|
7
|
+
/** For prefilled mode. Indexed parallel to `samples`. */
|
|
8
|
+
tokenizedSamples?: string[][]
|
|
9
|
+
/** Used to compute the filename; omitted → falls back to `sample_metadata_<date>`. */
|
|
10
|
+
experimentCode?: string
|
|
11
|
+
/** Override the blank-mode suggested headers. Defaults to ['group','class','replicate','notes']. */
|
|
12
|
+
suggestedColumns?: string[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const DEFAULT_BLANK_HEADERS = ['group', 'class', 'replicate', 'notes']
|
|
16
|
+
|
|
17
|
+
function rfc4180Quote(cell: string): string {
|
|
18
|
+
if (/[",\n]/.test(cell)) {
|
|
19
|
+
return `"${cell.replace(/"/g, '""')}"`
|
|
20
|
+
}
|
|
21
|
+
return cell
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function joinRow(cells: string[], sep: string): string {
|
|
25
|
+
return sep === ','
|
|
26
|
+
? cells.map(rfc4180Quote).join(',')
|
|
27
|
+
: cells.join('\t')
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function today(): string {
|
|
31
|
+
return new Date().toISOString().slice(0, 10)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function composeTemplate(
|
|
35
|
+
samples: string[],
|
|
36
|
+
schemas: ClassSchema[] | null,
|
|
37
|
+
classes: SampleClass[] | null,
|
|
38
|
+
options: TemplateOptions,
|
|
39
|
+
): { content: string; filename: string } {
|
|
40
|
+
const sep = options.format === 'tsv' ? '\t' : ','
|
|
41
|
+
const ext = options.format === 'tsv' ? 'tsv' : 'csv'
|
|
42
|
+
|
|
43
|
+
const stem = options.experimentCode ? `${options.experimentCode}_metadata` : 'sample_metadata'
|
|
44
|
+
const filename = `${stem}_${today()}.${ext}`
|
|
45
|
+
|
|
46
|
+
if (options.mode === 'blank' || !schemas || !classes) {
|
|
47
|
+
const headers = ['sample_name', ...(options.suggestedColumns ?? DEFAULT_BLANK_HEADERS)]
|
|
48
|
+
const rows = samples.map(s => {
|
|
49
|
+
const r = [s, ...Array(headers.length - 1).fill('')]
|
|
50
|
+
return joinRow(r, sep)
|
|
51
|
+
})
|
|
52
|
+
return { content: [joinRow(headers, sep), ...rows].join('\n'), filename }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Prefilled mode: union of all class columns, wide layout.
|
|
56
|
+
const tokenized = options.tokenizedSamples ?? []
|
|
57
|
+
const allColumns: { schemaKey: string; colName: string }[] = []
|
|
58
|
+
|
|
59
|
+
// Hoist: build lookup maps before the per-sample loop.
|
|
60
|
+
const sampleToClass = new Map<number, SampleClass>()
|
|
61
|
+
for (const cls of classes) for (const m of cls.members) sampleToClass.set(m, cls)
|
|
62
|
+
|
|
63
|
+
const schemaByKey = new Map<string, ClassSchema>()
|
|
64
|
+
for (const s of schemas) {
|
|
65
|
+
const k = s.subKind ? `${s.classKind}:${s.subKind}` : s.classKind
|
|
66
|
+
schemaByKey.set(k, s)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const colByNameByKey = new Map<string, Map<string, ColumnInfo>>()
|
|
70
|
+
const groupBySetByKey = new Map<string, Set<number>>()
|
|
71
|
+
for (const s of schemas) {
|
|
72
|
+
const k = s.subKind ? `${s.classKind}:${s.subKind}` : s.classKind
|
|
73
|
+
const m = new Map<string, ColumnInfo>()
|
|
74
|
+
for (const c of s.columns) m.set(c.displayName ?? c.name, c)
|
|
75
|
+
colByNameByKey.set(k, m)
|
|
76
|
+
groupBySetByKey.set(k, new Set(s.groupBy))
|
|
77
|
+
// Collect allColumns with dedup via Set
|
|
78
|
+
const seenColNames = new Set<string>()
|
|
79
|
+
for (const c of s.columns) {
|
|
80
|
+
const name = c.displayName ?? c.name
|
|
81
|
+
if (!seenColNames.has(name)) {
|
|
82
|
+
seenColNames.add(name)
|
|
83
|
+
if (!allColumns.some(x => x.colName === name)) {
|
|
84
|
+
allColumns.push({ schemaKey: k, colName: name })
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const headers = ['sample_name', 'class', ...allColumns.map(c => c.colName), 'group', 'notes']
|
|
91
|
+
|
|
92
|
+
const rows: string[] = []
|
|
93
|
+
for (let i = 0; i < samples.length; i++) {
|
|
94
|
+
const cls = sampleToClass.get(i)
|
|
95
|
+
const classLabel = cls?.label ?? ''
|
|
96
|
+
const tokens = tokenized[i] ?? []
|
|
97
|
+
const cells: string[] = [samples[i], classLabel]
|
|
98
|
+
|
|
99
|
+
if (cls) {
|
|
100
|
+
const k = classKey(cls)
|
|
101
|
+
const groupBySet = groupBySetByKey.get(k)
|
|
102
|
+
const colByName: Map<string, ColumnInfo> | undefined = colByNameByKey.get(k)
|
|
103
|
+
const keyParts: string[] = []
|
|
104
|
+
for (const col of allColumns) {
|
|
105
|
+
if (col.schemaKey === k && colByName) {
|
|
106
|
+
const schemaCol = colByName.get(col.colName)
|
|
107
|
+
const raw =
|
|
108
|
+
schemaCol?.sourceIndices.map(idx => tokens[idx] ?? '').join('_') ?? ''
|
|
109
|
+
cells.push(raw)
|
|
110
|
+
if (schemaCol && groupBySet?.has(schemaCol.index)) keyParts.push(raw)
|
|
111
|
+
} else {
|
|
112
|
+
cells.push('')
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
cells.push(keyParts.join(' / '))
|
|
116
|
+
} else {
|
|
117
|
+
for (const _ of allColumns) cells.push('')
|
|
118
|
+
cells.push('')
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
cells.push('') // notes
|
|
122
|
+
rows.push(joinRow(cells, sep))
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return { content: [joinRow(headers, sep), ...rows].join('\n'), filename }
|
|
126
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const DELIMITER_CANDIDATES = ['_', '-', '.'] as const
|
|
2
|
+
export type Delimiter = (typeof DELIMITER_CANDIDATES)[number]
|
|
3
|
+
|
|
4
|
+
/** Pick the delimiter whose split produces the most consistent field count across `lines`. */
|
|
5
|
+
export function pickPrimaryDelimiter(lines: string[]): Delimiter {
|
|
6
|
+
if (lines.length === 0) return '_'
|
|
7
|
+
|
|
8
|
+
let best: Delimiter = '_'
|
|
9
|
+
let bestScore = -1
|
|
10
|
+
|
|
11
|
+
for (const candidate of DELIMITER_CANDIDATES) {
|
|
12
|
+
const counts = lines.map(l => l.split(candidate).length)
|
|
13
|
+
const frequency = new Map<number, number>()
|
|
14
|
+
for (const c of counts) frequency.set(c, (frequency.get(c) ?? 0) + 1)
|
|
15
|
+
|
|
16
|
+
// Mode and its frequency
|
|
17
|
+
let mode = 1
|
|
18
|
+
let modeFreq = 0
|
|
19
|
+
for (const [c, f] of frequency) {
|
|
20
|
+
if (f > modeFreq || (f === modeFreq && c > mode)) {
|
|
21
|
+
mode = c
|
|
22
|
+
modeFreq = f
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// A delimiter that produces field count 1 didn't split anything — score 0.
|
|
27
|
+
const score = mode > 1 ? modeFreq / lines.length : 0
|
|
28
|
+
if (score > bestScore) {
|
|
29
|
+
best = candidate
|
|
30
|
+
bestScore = score
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return best
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Split a single sample name on `delimiter`. Empty input returns `['']` so callers
|
|
38
|
+
* always receive at least one token. */
|
|
39
|
+
export function splitMulti(name: string, delimiter: Delimiter): string[] {
|
|
40
|
+
return name.split(delimiter)
|
|
41
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./vocab.schema.json",
|
|
3
|
+
"_doc": {
|
|
4
|
+
"purpose": "Sample-name classification vocabulary for the auto-group pipeline. Edit this file to teach the classifier new sample types or matrices without changing TypeScript. All matching is case-insensitive unless a pattern's regex flags say otherwise.",
|
|
5
|
+
"patterns": "Regex source strings (no surrounding slashes). Compiled with the `i` flag at load time.",
|
|
6
|
+
"standardAllow": "Tokens that should be classified as Standard even when they don't match `patterns.standard`. Case-sensitive — use the exact spelling that appears in your filenames.",
|
|
7
|
+
"matrixVocab": "Lowercase token → canonical display name. Tokens are normalised to lowercase before lookup, so adding 'tissues' handles 'Tissues' / 'TISSUES' / 'tissues' uniformly. Multiple aliases map to one canonical (e.g. cell + cells → Cells).",
|
|
8
|
+
"ionizationModes": "Lowercase polarity tokens (pos / positive / neg / negative). Suffixed to the subKind so POS and NEG acquisitions get independent schemas.",
|
|
9
|
+
"tissueParentTokens": "Tokens that flag the immediately-following token as the organ subKind (so `tissues / kidney` becomes Biological/Tissues, tagging both positions).",
|
|
10
|
+
"replicateStripPatterns": "Regex sources stripped from each sample name during the replicate pre-grouping pass. Samples whose stripped (base) names match are treated as replicates of one another and grouped together before tokenisation. Default set covers: trailing run-order numbers, _T<n>, _B<n>, and _Rep<n> markers."
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
"patterns": {
|
|
14
|
+
"blank": "^(blank|blk|solv?blank|matrix.?blank)$",
|
|
15
|
+
"iqc": "^iqc$",
|
|
16
|
+
"eqc": "^eqc$",
|
|
17
|
+
"standard": "^(standard|std\\d*|cal(?:ibrator)?\\d*|.+standard)$",
|
|
18
|
+
"spikeIn": "spike.?in",
|
|
19
|
+
"stock": "^stock$"
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
"standardAllow": [
|
|
23
|
+
"ExtractionS",
|
|
24
|
+
"IS",
|
|
25
|
+
"internal.standard",
|
|
26
|
+
"PQC",
|
|
27
|
+
"pooledQC"
|
|
28
|
+
],
|
|
29
|
+
|
|
30
|
+
"matrixVocab": {
|
|
31
|
+
"plasma": "Plasma",
|
|
32
|
+
"serum": "Serum",
|
|
33
|
+
"tissue": "Tissue",
|
|
34
|
+
"tissues": "Tissues",
|
|
35
|
+
"kidney": "Kidney",
|
|
36
|
+
"liver": "Liver",
|
|
37
|
+
"tumor": "Tumor",
|
|
38
|
+
"urine": "Urine",
|
|
39
|
+
"brain": "Brain",
|
|
40
|
+
"muscle": "Muscle",
|
|
41
|
+
"adipose": "Adipose",
|
|
42
|
+
"csf": "CSF",
|
|
43
|
+
"feces": "Feces",
|
|
44
|
+
"faeces": "Faeces",
|
|
45
|
+
"cells": "Cells",
|
|
46
|
+
"cell": "Cells",
|
|
47
|
+
"media": "Media",
|
|
48
|
+
"medium": "Medium",
|
|
49
|
+
"extract": "Extract"
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
"ionizationModes": {
|
|
53
|
+
"pos": "POS",
|
|
54
|
+
"positive": "POS",
|
|
55
|
+
"neg": "NEG",
|
|
56
|
+
"negative": "NEG"
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
"tissueParentTokens": ["tissue", "tissues"],
|
|
60
|
+
|
|
61
|
+
"replicateStripPatterns": [
|
|
62
|
+
"[_-]\\d{2,4}[A-Za-z]?$",
|
|
63
|
+
"[_-]T\\d+(?=[_-]|$)",
|
|
64
|
+
"[_-]B\\d+(?=[_-]|$)",
|
|
65
|
+
"[_-](?:rep(?:licate)?)\\d+(?=[_-]|$)"
|
|
66
|
+
]
|
|
67
|
+
}
|