@morscherlab/mint-sdk 1.0.0-rc.1 → 1.0.0-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/README.md +9 -1
  2. package/dist/__tests__/components/LcmsSequenceTable.test.d.ts +1 -0
  3. package/dist/__tests__/components/ProgressBar.test.d.ts +1 -0
  4. package/dist/__tests__/components/RackEditor.test.d.ts +1 -0
  5. package/dist/__tests__/components/SequenceProgressBar.test.d.ts +1 -0
  6. package/dist/__tests__/composables/useExperimentSamples.test.d.ts +1 -0
  7. package/dist/__tests__/utils/instrument.test.d.ts +1 -0
  8. package/dist/__tests__/utils/lcms.test.d.ts +1 -0
  9. package/dist/__tests__/utils/permissions.test.d.ts +1 -0
  10. package/dist/__tests__/utils/rack.test.d.ts +1 -0
  11. package/dist/{auth-CBG3bWEc.js → auth-B7g4J4ZF.js} +99 -5
  12. package/dist/auth-B7g4J4ZF.js.map +1 -0
  13. package/dist/components/AppSidebar.vue.d.ts +3 -3
  14. package/dist/components/AutoGroupModal.vue.d.ts +1 -1
  15. package/dist/components/BaseCheckbox.vue.d.ts +1 -1
  16. package/dist/components/BaseToggle.vue.d.ts +2 -2
  17. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +1 -1
  18. package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +1 -1
  19. package/dist/components/BioTemplatePresetWorkspaceView.vue.d.ts +1 -1
  20. package/dist/components/ControlWorkspaceView.vue.d.ts +3 -3
  21. package/dist/components/DoseDesignWorkspaceView.vue.d.ts +2 -2
  22. package/dist/components/FormulaInput.vue.d.ts +1 -1
  23. package/dist/components/InstrumentAlertLog.vue.d.ts +22 -0
  24. package/dist/components/InstrumentStateBadge.vue.d.ts +11 -0
  25. package/dist/components/InstrumentStatusCard.vue.d.ts +13 -0
  26. package/dist/components/LcmsSequenceTable.vue.d.ts +26 -0
  27. package/dist/components/PluginWorkspaceView.vue.d.ts +3 -3
  28. package/dist/components/ProgressBar.vue.d.ts +1 -0
  29. package/dist/components/RackEditor.vue.d.ts +41 -3
  30. package/dist/components/ReagentList.vue.d.ts +1 -1
  31. package/dist/components/SampleSelector.vue.d.ts +5 -2
  32. package/dist/components/SegmentedControl.vue.d.ts +2 -0
  33. package/dist/components/SequenceInput.vue.d.ts +1 -1
  34. package/dist/components/SequenceProgressBar.vue.d.ts +15 -0
  35. package/dist/components/SettingsModal.vue.d.ts +3 -1
  36. package/dist/components/TagsInput.vue.d.ts +1 -1
  37. package/dist/components/WellPlate.vue.d.ts +42 -3
  38. package/dist/components/index.d.ts +5 -0
  39. package/dist/components/index.js +3 -3
  40. package/dist/{components-5KSfsVqf.js → components-DafPc4rM.js} +2091 -1051
  41. package/dist/components-DafPc4rM.js.map +1 -0
  42. package/dist/composables/controlComponentBindings.d.ts +7 -0
  43. package/dist/composables/controlSchemaAdapters.d.ts +20 -0
  44. package/dist/composables/controlSchemaFormFields.d.ts +3 -0
  45. package/dist/composables/controlSchemaLayout.d.ts +7 -0
  46. package/dist/composables/controlSchemaNormalize.d.ts +15 -0
  47. package/dist/composables/controlSchemaUtils.d.ts +9 -0
  48. package/dist/composables/controlWorkspaceOptions.d.ts +2 -0
  49. package/dist/composables/experimentDesignData.d.ts +17 -0
  50. package/dist/composables/index.d.ts +2 -0
  51. package/dist/composables/index.js +4 -4
  52. package/dist/composables/useControlSchema.d.ts +15 -30
  53. package/dist/composables/useControlWorkspace.d.ts +5 -0
  54. package/dist/composables/useExperimentData.d.ts +11 -3
  55. package/dist/composables/useExperimentSamples.d.ts +42 -0
  56. package/dist/composables/usePlatformContext.d.ts +54 -0
  57. package/dist/{composables-D4Myb30a.js → composables-BMkPQhVK.js} +5 -3
  58. package/dist/composables-BMkPQhVK.js.map +1 -0
  59. package/dist/index.d.ts +4 -0
  60. package/dist/index.js +168 -6
  61. package/dist/index.js.map +1 -0
  62. package/dist/install.js +2 -2
  63. package/dist/instrument.d.ts +7 -0
  64. package/dist/lcms.d.ts +27 -0
  65. package/dist/permissions.d.ts +46 -0
  66. package/dist/stores/auth.d.ts +74 -2
  67. package/dist/stores/index.js +1 -1
  68. package/dist/styles.css +3316 -1216
  69. package/dist/templates/adapters.d.ts +14 -47
  70. package/dist/templates/assayLookups.d.ts +3 -0
  71. package/dist/templates/assayMatrixAdapters.d.ts +6 -0
  72. package/dist/templates/assayMatrixBuilder.d.ts +2 -0
  73. package/dist/templates/assayNormalizers.d.ts +4 -0
  74. package/dist/templates/builderDefaults.d.ts +1 -0
  75. package/dist/templates/builderIdUtils.d.ts +4 -0
  76. package/dist/templates/builderPresetControls.d.ts +10 -0
  77. package/dist/templates/builderReadUtils.d.ts +8 -0
  78. package/dist/templates/builders.d.ts +26 -63
  79. package/dist/templates/calibrationCurveAdapters.d.ts +4 -0
  80. package/dist/templates/calibrationCurveBuilder.d.ts +2 -0
  81. package/dist/templates/calibrationNormalizers.d.ts +5 -0
  82. package/dist/templates/componentBindingCatalog.d.ts +25 -0
  83. package/dist/templates/componentBindingHelpers.d.ts +17 -0
  84. package/dist/templates/componentDoseResponseProps.d.ts +3 -0
  85. package/dist/templates/componentGenericProps.d.ts +8 -0
  86. package/dist/templates/componentPlateHelpers.d.ts +5 -0
  87. package/dist/templates/componentPlateMapProps.d.ts +3 -0
  88. package/dist/templates/componentPropsFactory.d.ts +3 -0
  89. package/dist/templates/componentQpcrPlateProps.d.ts +3 -0
  90. package/dist/templates/componentTargetResolvers.d.ts +5 -0
  91. package/dist/templates/componentTemplateProps.d.ts +3 -0
  92. package/dist/templates/controlSchemaClone.d.ts +4 -0
  93. package/dist/templates/controlSchemaConstants.d.ts +10 -0
  94. package/dist/templates/controlSchemaMerge.d.ts +3 -0
  95. package/dist/templates/controlSchemaTargets.d.ts +17 -0
  96. package/dist/templates/controlSchemaTypes.d.ts +4 -0
  97. package/dist/templates/controlSchemas.d.ts +4 -4
  98. package/dist/templates/defaultBioTemplateBuilder.d.ts +2 -0
  99. package/dist/templates/doseResponseAdapters.d.ts +4 -0
  100. package/dist/templates/doseResponseBuilder.d.ts +2 -0
  101. package/dist/templates/elisaAssayCollectionBuilder.d.ts +2 -0
  102. package/dist/templates/flowCytometryAssayCollectionBuilder.d.ts +2 -0
  103. package/dist/templates/flowCytometryPanelBuilder.d.ts +2 -0
  104. package/dist/templates/flowNormalizers.d.ts +8 -0
  105. package/dist/templates/flowPanelAdapters.d.ts +4 -0
  106. package/dist/templates/index.d.ts +2 -2
  107. package/dist/templates/index.js +2 -2
  108. package/dist/templates/instrumentRunAdapterHelpers.d.ts +8 -0
  109. package/dist/templates/instrumentRunAdapters.d.ts +8 -0
  110. package/dist/templates/instrumentRunBuilder.d.ts +2 -0
  111. package/dist/templates/lcmsBatchCollectionBuilder.d.ts +2 -0
  112. package/dist/templates/plateGeometry.d.ts +4 -0
  113. package/dist/templates/plateMapAdapters.d.ts +3 -0
  114. package/dist/templates/plateMapBuilder.d.ts +2 -0
  115. package/dist/templates/presetControlSchemas.d.ts +534 -0
  116. package/dist/templates/presets.d.ts +12 -0
  117. package/dist/templates/protocolAdapters.d.ts +5 -0
  118. package/dist/templates/protocolNormalizers.d.ts +6 -0
  119. package/dist/templates/protocolStepsBuilder.d.ts +2 -0
  120. package/dist/templates/qpcrAdapters.d.ts +5 -0
  121. package/dist/templates/qpcrExpressionCollectionBuilder.d.ts +2 -0
  122. package/dist/templates/qpcrPlateBuilder.d.ts +2 -0
  123. package/dist/templates/reagentAdapters.d.ts +5 -0
  124. package/dist/templates/reagentListBuilder.d.ts +2 -0
  125. package/dist/templates/runNormalizers.d.ts +10 -0
  126. package/dist/templates/sampleNormalizers.d.ts +4 -0
  127. package/dist/templates/samplePrepAdapters.d.ts +4 -0
  128. package/dist/templates/samplePrepBuilder.d.ts +2 -0
  129. package/dist/templates/sampleSheetAdapters.d.ts +5 -0
  130. package/dist/templates/sampleSheetBuilder.d.ts +2 -0
  131. package/dist/templates/targetedMetabolomicsCollectionBuilder.d.ts +2 -0
  132. package/dist/templates/targetedMetabolomicsHelpers.d.ts +5 -0
  133. package/dist/templates/templateControlSchemas.d.ts +400 -0
  134. package/dist/templates/templateEnvelopes.d.ts +9 -0
  135. package/dist/templates/templatePackCollectionBuilder.d.ts +2 -0
  136. package/dist/templates/templatePresetCollectionBuilder.d.ts +18 -0
  137. package/dist/templates/templateValidators.d.ts +13 -0
  138. package/dist/templates/timeCourseAdapters.d.ts +5 -0
  139. package/dist/templates/timeCourseBuilder.d.ts +2 -0
  140. package/dist/templates/types.d.ts +16 -1
  141. package/dist/templates/wellPlateScreenCollectionBuilder.d.ts +2 -0
  142. package/dist/templates/westernBlotAssayCollectionBuilder.d.ts +2 -0
  143. package/dist/{templates-BSlxwV2c.js → templates-bUAWMn5L.js} +3610 -3154
  144. package/dist/templates-bUAWMn5L.js.map +1 -0
  145. package/dist/types/auth.d.ts +2 -0
  146. package/dist/types/components.d.ts +32 -3
  147. package/dist/types/form-builder.d.ts +2 -1
  148. package/dist/types/index.d.ts +4 -1
  149. package/dist/types/instrument.d.ts +56 -0
  150. package/dist/types/platform.d.ts +3 -0
  151. package/dist/{useExperimentData-BbbdI5xT.js → useProtocolTemplates-QZtHFFH2.js} +534 -359
  152. package/dist/useProtocolTemplates-QZtHFFH2.js.map +1 -0
  153. package/dist/utils/rack.d.ts +47 -0
  154. package/package.json +1 -1
  155. package/src/__tests__/components/AppTopBar.test.ts +15 -0
  156. package/src/__tests__/components/BaseTabs.test.ts +15 -0
  157. package/src/__tests__/components/LcmsSequenceTable.test.ts +57 -0
  158. package/src/__tests__/components/ProgressBar.test.ts +18 -0
  159. package/src/__tests__/components/RackEditor.test.ts +125 -0
  160. package/src/__tests__/components/SampleSelector.test.ts +25 -0
  161. package/src/__tests__/components/SegmentedControl.test.ts +45 -0
  162. package/src/__tests__/components/SequenceProgressBar.test.ts +39 -0
  163. package/src/__tests__/components/SettingsModal.test.ts +83 -2
  164. package/src/__tests__/composables/useControlSchema.test.ts +4 -0
  165. package/src/__tests__/composables/useExperimentData.test.ts +23 -0
  166. package/src/__tests__/composables/useExperimentSamples.test.ts +91 -0
  167. package/src/__tests__/templates/templates.test.ts +86 -0
  168. package/src/__tests__/utils/instrument.test.ts +47 -0
  169. package/src/__tests__/utils/lcms.test.ts +73 -0
  170. package/src/__tests__/utils/permissions.test.ts +50 -0
  171. package/src/__tests__/utils/rack.test.ts +120 -0
  172. package/src/components/AppTopBar.vue +1 -0
  173. package/src/components/BaseTabs.vue +22 -1
  174. package/src/components/InstrumentAlertLog.vue +191 -0
  175. package/src/components/InstrumentStateBadge.vue +50 -0
  176. package/src/components/InstrumentStatusCard.vue +188 -0
  177. package/src/components/LcmsSequenceTable.vue +191 -0
  178. package/src/components/ProgressBar.vue +3 -0
  179. package/src/components/RackEditor.vue +73 -2
  180. package/src/components/SampleSelector.vue +28 -9
  181. package/src/components/SegmentedControl.story.vue +17 -0
  182. package/src/components/SegmentedControl.vue +14 -3
  183. package/src/components/SequenceProgressBar.vue +71 -0
  184. package/src/components/SettingsModal.vue +42 -2
  185. package/src/components/WellPlate.vue +142 -21
  186. package/src/components/index.ts +5 -0
  187. package/src/components/internal/WellEditPopupInternal.vue +1 -0
  188. package/src/composables/controlComponentBindings.ts +80 -0
  189. package/src/composables/controlSchemaAdapters.ts +196 -0
  190. package/src/composables/controlSchemaFormFields.ts +61 -0
  191. package/src/composables/controlSchemaLayout.ts +59 -0
  192. package/src/composables/controlSchemaNormalize.ts +101 -0
  193. package/src/composables/controlSchemaUtils.ts +36 -0
  194. package/src/composables/controlWorkspaceOptions.ts +21 -0
  195. package/src/composables/experimentDesignData.ts +182 -0
  196. package/src/composables/index.ts +14 -0
  197. package/src/composables/useAuth.ts +4 -0
  198. package/src/composables/useAutoGroup.ts +5 -1
  199. package/src/composables/useControlSchema.ts +62 -618
  200. package/src/composables/useControlWorkspace.ts +207 -0
  201. package/src/composables/useExperimentData.ts +57 -16
  202. package/src/composables/useExperimentSamples.ts +142 -0
  203. package/src/index.ts +27 -0
  204. package/src/instrument.ts +90 -0
  205. package/src/lcms.ts +108 -0
  206. package/src/permissions.ts +143 -0
  207. package/src/stores/auth.ts +31 -3
  208. package/src/styles/components/instrument-monitor.css +478 -0
  209. package/src/styles/components/lcms-sequence-table.css +189 -0
  210. package/src/styles/components/sequence-progress-bar.css +63 -0
  211. package/src/styles/components/tabs.css +9 -0
  212. package/src/styles/components/well-edit-popup.css +7 -1
  213. package/src/styles/components/well-plate.css +5 -0
  214. package/src/styles/index.css +3 -0
  215. package/src/templates/adapters.ts +89 -930
  216. package/src/templates/assayLookups.ts +33 -0
  217. package/src/templates/assayMatrixAdapters.ts +78 -0
  218. package/src/templates/assayMatrixBuilder.ts +59 -0
  219. package/src/templates/assayNormalizers.ts +34 -0
  220. package/src/templates/builderDefaults.ts +11 -0
  221. package/src/templates/builderIdUtils.ts +20 -0
  222. package/src/templates/builderPresetControls.ts +165 -0
  223. package/src/templates/builderReadUtils.ts +57 -0
  224. package/src/templates/builders.ts +122 -2149
  225. package/src/templates/calibrationCurveAdapters.ts +59 -0
  226. package/src/templates/calibrationCurveBuilder.ts +99 -0
  227. package/src/templates/calibrationNormalizers.ts +60 -0
  228. package/src/templates/componentBindingCatalog.ts +90 -0
  229. package/src/templates/componentBindingHelpers.ts +93 -0
  230. package/src/templates/componentBindings.ts +12 -461
  231. package/src/templates/componentDoseResponseProps.ts +42 -0
  232. package/src/templates/componentGenericProps.ts +77 -0
  233. package/src/templates/componentPlateHelpers.ts +29 -0
  234. package/src/templates/componentPlateMapProps.ts +32 -0
  235. package/src/templates/componentPropsFactory.ts +21 -0
  236. package/src/templates/componentQpcrPlateProps.ts +28 -0
  237. package/src/templates/componentTargetResolvers.ts +69 -0
  238. package/src/templates/componentTemplateProps.ts +78 -0
  239. package/src/templates/controlSchemaClone.ts +32 -0
  240. package/src/templates/controlSchemaConstants.ts +11 -0
  241. package/src/templates/controlSchemaMerge.ts +40 -0
  242. package/src/templates/controlSchemaTargets.ts +87 -0
  243. package/src/templates/controlSchemaTypes.ts +20 -0
  244. package/src/templates/controlSchemas.ts +22 -636
  245. package/src/templates/defaultBioTemplateBuilder.ts +124 -0
  246. package/src/templates/doseResponseAdapters.ts +45 -0
  247. package/src/templates/doseResponseBuilder.ts +44 -0
  248. package/src/templates/elisaAssayCollectionBuilder.ts +62 -0
  249. package/src/templates/flowCytometryAssayCollectionBuilder.ts +41 -0
  250. package/src/templates/flowCytometryPanelBuilder.ts +53 -0
  251. package/src/templates/flowNormalizers.ts +58 -0
  252. package/src/templates/flowPanelAdapters.ts +58 -0
  253. package/src/templates/index.ts +2 -0
  254. package/src/templates/instrumentRunAdapterHelpers.ts +94 -0
  255. package/src/templates/instrumentRunAdapters.ts +163 -0
  256. package/src/templates/instrumentRunBuilder.ts +97 -0
  257. package/src/templates/lcmsBatchCollectionBuilder.ts +38 -0
  258. package/src/templates/plateGeometry.ts +62 -0
  259. package/src/templates/plateMapAdapters.ts +36 -0
  260. package/src/templates/plateMapBuilder.ts +43 -0
  261. package/src/templates/presetControlSchemas.ts +258 -0
  262. package/src/templates/presets.ts +23 -0
  263. package/src/templates/protocolAdapters.ts +69 -0
  264. package/src/templates/protocolNormalizers.ts +37 -0
  265. package/src/templates/protocolStepsBuilder.ts +36 -0
  266. package/src/templates/qpcrAdapters.ts +104 -0
  267. package/src/templates/qpcrExpressionCollectionBuilder.ts +33 -0
  268. package/src/templates/qpcrPlateBuilder.ts +96 -0
  269. package/src/templates/reagentAdapters.ts +77 -0
  270. package/src/templates/reagentListBuilder.ts +30 -0
  271. package/src/templates/runNormalizers.ts +63 -0
  272. package/src/templates/sampleNormalizers.ts +58 -0
  273. package/src/templates/samplePrepAdapters.ts +63 -0
  274. package/src/templates/samplePrepBuilder.ts +51 -0
  275. package/src/templates/sampleSheetAdapters.ts +75 -0
  276. package/src/templates/sampleSheetBuilder.ts +23 -0
  277. package/src/templates/targetedMetabolomicsCollectionBuilder.ts +79 -0
  278. package/src/templates/targetedMetabolomicsHelpers.ts +102 -0
  279. package/src/templates/templateControlSchemas.ts +320 -0
  280. package/src/templates/templateEnvelopes.ts +137 -0
  281. package/src/templates/templatePackCollectionBuilder.ts +23 -0
  282. package/src/templates/templatePresetCollectionBuilder.ts +139 -0
  283. package/src/templates/templateValidators.ts +414 -0
  284. package/src/templates/timeCourseAdapters.ts +73 -0
  285. package/src/templates/timeCourseBuilder.ts +64 -0
  286. package/src/templates/types.ts +17 -0
  287. package/src/templates/wellPlateScreenCollectionBuilder.ts +36 -0
  288. package/src/templates/westernBlotAssayCollectionBuilder.ts +68 -0
  289. package/src/types/auth.ts +3 -0
  290. package/src/types/components.ts +45 -3
  291. package/src/types/form-builder.ts +2 -1
  292. package/src/types/index.ts +35 -0
  293. package/src/types/instrument.ts +61 -0
  294. package/src/types/platform.ts +4 -0
  295. package/src/utils/rack.ts +209 -0
  296. package/dist/auth-CBG3bWEc.js.map +0 -1
  297. package/dist/components-5KSfsVqf.js.map +0 -1
  298. package/dist/composables-D4Myb30a.js.map +0 -1
  299. package/dist/templates-BSlxwV2c.js.map +0 -1
  300. package/dist/useExperimentData-BbbdI5xT.js.map +0 -1
@@ -0,0 +1,101 @@
1
+ import type {
2
+ SelectOption,
3
+ } from '../types'
4
+ import type {
5
+ FormFieldType,
6
+ } from '../types/form-builder'
7
+ import {
8
+ getTypeDefault,
9
+ } from './formBuilderRegistry'
10
+ import {
11
+ humanize,
12
+ } from './controlSchemaUtils'
13
+ import type {
14
+ ControlDefinition,
15
+ ControlInput,
16
+ ControlOption,
17
+ ControlOptionValue,
18
+ ControlSchema,
19
+ ControlSchemaOptions,
20
+ } from './useControlSchema'
21
+
22
+ export interface NormalizedControl {
23
+ name: string
24
+ definition: ControlDefinition
25
+ type: FormFieldType
26
+ sectionId: string
27
+ viewId: string
28
+ order: number
29
+ }
30
+
31
+ export function normalizeControls(
32
+ controls: ControlSchema,
33
+ options: ControlSchemaOptions = {},
34
+ ): NormalizedControl[] {
35
+ return Object.entries(controls)
36
+ .map(([name, input], index): NormalizedControl => {
37
+ const definition = normalizeControlDefinition(input)
38
+ const type = definition.type ?? inferControlType(definition)
39
+ return {
40
+ name,
41
+ definition,
42
+ type,
43
+ sectionId: definition.section ?? options.section ?? 'controls',
44
+ viewId: definition.view ?? options.view ?? 'default',
45
+ order: definition.order ?? index,
46
+ }
47
+ })
48
+ .sort((a, b) => a.order - b.order)
49
+ }
50
+
51
+ export function normalizeControlDefinition(input: ControlInput): ControlDefinition {
52
+ if (isControlOptionArray(input)) {
53
+ if (input.length === 0) return { type: 'tags', default: [] }
54
+ return {
55
+ default: optionValue(input[0]),
56
+ options: input,
57
+ }
58
+ }
59
+
60
+ if (typeof input === 'string' || typeof input === 'number' || typeof input === 'boolean') {
61
+ return { default: input }
62
+ }
63
+
64
+ return input
65
+ }
66
+
67
+ export function defaultValueForControl(definition: ControlDefinition, type: FormFieldType): unknown {
68
+ if (definition.default !== undefined) return definition.default
69
+ if (definition.defaultValue !== undefined) return definition.defaultValue
70
+ return getTypeDefault(type)
71
+ }
72
+
73
+ export function normalizeOption(option: ControlOption): SelectOption<unknown> {
74
+ if (typeof option === 'object' && option !== null && 'label' in option) {
75
+ return option
76
+ }
77
+ return {
78
+ value: option,
79
+ label: humanize(String(option)),
80
+ }
81
+ }
82
+
83
+ function isControlOptionArray(input: ControlInput): input is readonly ControlOption[] {
84
+ return Array.isArray(input)
85
+ }
86
+
87
+ function inferControlType(definition: ControlDefinition): FormFieldType {
88
+ const value = definition.default ?? definition.defaultValue
89
+ if (definition.options?.length) return Array.isArray(value) ? 'multiselect' : 'select'
90
+ if (typeof value === 'boolean') return 'toggle'
91
+ if (typeof value === 'number') return 'number'
92
+ if (Array.isArray(value)) return 'tags'
93
+ return 'text'
94
+ }
95
+
96
+ function optionValue(option: ControlOption): ControlOptionValue | unknown {
97
+ if (typeof option === 'object' && option !== null && 'value' in option) {
98
+ return option.value
99
+ }
100
+ return option
101
+ }
@@ -0,0 +1,36 @@
1
+ export function recordValue(value: unknown): Record<string, unknown> {
2
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
3
+ ? value as Record<string, unknown>
4
+ : {}
5
+ }
6
+
7
+ export function numericValue(value: number | { value: number; message: string }): number {
8
+ return typeof value === 'number' ? value : value.value
9
+ }
10
+
11
+ export function orderedUnique(values: string[]): string[] {
12
+ return [...new Set(values)]
13
+ }
14
+
15
+ export function replaceRecord(target: Record<string, unknown>, values: Record<string, unknown>) {
16
+ for (const key of Object.keys(target)) {
17
+ delete target[key]
18
+ }
19
+ Object.assign(target, values)
20
+ }
21
+
22
+ export function omitUndefined<T extends object>(record: T): T {
23
+ const next: Record<string, unknown> = {}
24
+ for (const [key, value] of Object.entries(record as Record<string, unknown>)) {
25
+ if (value !== undefined) next[key] = value
26
+ }
27
+ return next as T
28
+ }
29
+
30
+ export function humanize(value: string): string {
31
+ return value
32
+ .replace(/[_-]+/g, ' ')
33
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
34
+ .trim()
35
+ .replace(/\w\S*/g, word => word.charAt(0).toUpperCase() + word.slice(1))
36
+ }
@@ -0,0 +1,21 @@
1
+ import type {
2
+ ControlWorkspaceOptions,
3
+ } from './useControlSchema'
4
+
5
+ export function mergeControlWorkspaceOptions(
6
+ base?: ControlWorkspaceOptions,
7
+ override?: ControlWorkspaceOptions,
8
+ ): ControlWorkspaceOptions {
9
+ return {
10
+ ...(base ?? {}),
11
+ ...(override ?? {}),
12
+ initialValues: {
13
+ ...(base?.initialValues ?? {}),
14
+ ...(override?.initialValues ?? {}),
15
+ },
16
+ topBarSettings: {
17
+ ...(base?.topBarSettings ?? {}),
18
+ ...(override?.topBarSettings ?? {}),
19
+ },
20
+ }
21
+ }
@@ -0,0 +1,182 @@
1
+ import type { SelectOption } from '../types'
2
+
3
+ export interface ExperimentDesignDataResponse {
4
+ experiment_id?: number
5
+ plugin_id?: string
6
+ data?: Record<string, unknown>
7
+ schema_version?: string
8
+ updated_at?: string | null
9
+ }
10
+
11
+ export interface ExtractExperimentSamplesOptions {
12
+ includeControls?: boolean
13
+ }
14
+
15
+ function isRecord(value: unknown): value is Record<string, unknown> {
16
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
17
+ }
18
+
19
+ function isPlatformDesignDataResponse(value: Record<string, unknown>): boolean {
20
+ return (
21
+ 'data' in value
22
+ && (
23
+ 'experiment_id' in value
24
+ || 'plugin_id' in value
25
+ || 'schema_version' in value
26
+ || 'updated_at' in value
27
+ )
28
+ )
29
+ }
30
+
31
+ /** Return the plugin-defined design_data payload from common platform and plugin response shapes. */
32
+ export function unwrapExperimentDesignData(
33
+ rawData: Record<string, unknown> | null | undefined,
34
+ ): Record<string, unknown> | null {
35
+ if (!rawData) return null
36
+
37
+ if (isRecord(rawData.data) && isPlatformDesignDataResponse(rawData)) {
38
+ return rawData.data
39
+ }
40
+
41
+ if (isRecord(rawData.design_data)) {
42
+ return rawData.design_data
43
+ }
44
+
45
+ if (isRecord(rawData.designData)) {
46
+ return rawData.designData
47
+ }
48
+
49
+ return rawData
50
+ }
51
+
52
+ function shouldIncludeSample(record: Record<string, unknown>, options: ExtractExperimentSamplesOptions): boolean {
53
+ if (options.includeControls) return true
54
+ const rawType = record.sample_type ?? record.sampleType ?? record.type ?? record.well_type ?? record.wellType
55
+ const type = typeof rawType === 'string' ? rawType.toLowerCase() : ''
56
+ return !['blank', 'qc'].includes(type)
57
+ }
58
+
59
+ function readSampleValue(record: Record<string, unknown>): string | null {
60
+ const candidates = [
61
+ record.sample_name,
62
+ record.sampleName,
63
+ record.sample_id,
64
+ record.sampleId,
65
+ record.name,
66
+ record.id,
67
+ record.label,
68
+ ]
69
+
70
+ for (const candidate of candidates) {
71
+ if (typeof candidate === 'string' && candidate.trim()) {
72
+ return candidate
73
+ }
74
+ if (typeof candidate === 'number' && Number.isFinite(candidate)) {
75
+ return String(candidate)
76
+ }
77
+ }
78
+
79
+ return null
80
+ }
81
+
82
+ function readSampleLabel(record: Record<string, unknown>, fallback: string): string {
83
+ const label = record.name ?? record.label ?? record.sample_name ?? record.sampleName
84
+ return typeof label === 'string' && label.trim() ? label : fallback
85
+ }
86
+
87
+ function readSampleDescription(record: Record<string, unknown>): string | undefined {
88
+ const parts = [
89
+ record.group,
90
+ record.batch,
91
+ record.batch_id,
92
+ record.batchId,
93
+ record.plate_id,
94
+ record.plateId,
95
+ record.well_id,
96
+ record.wellId,
97
+ ].filter((value): value is string | number =>
98
+ (typeof value === 'string' && value.trim().length > 0)
99
+ || typeof value === 'number',
100
+ )
101
+
102
+ return parts.length > 0 ? parts.map(String).join(' / ') : undefined
103
+ }
104
+
105
+ function addSampleRecord(
106
+ options: SelectOption<string>[],
107
+ seen: Set<string>,
108
+ value: unknown,
109
+ extractOptions: ExtractExperimentSamplesOptions,
110
+ ): void {
111
+ if (typeof value === 'string') {
112
+ const trimmed = value.trim()
113
+ if (trimmed && !seen.has(trimmed)) {
114
+ seen.add(trimmed)
115
+ options.push({ value: trimmed, label: trimmed })
116
+ }
117
+ return
118
+ }
119
+
120
+ if (!isRecord(value) || !shouldIncludeSample(value, extractOptions)) return
121
+
122
+ const sampleValue = readSampleValue(value)
123
+ if (!sampleValue || seen.has(sampleValue)) return
124
+
125
+ seen.add(sampleValue)
126
+ options.push({
127
+ value: sampleValue,
128
+ label: readSampleLabel(value, sampleValue),
129
+ description: readSampleDescription(value),
130
+ })
131
+ }
132
+
133
+ function addSampleArray(
134
+ options: SelectOption<string>[],
135
+ seen: Set<string>,
136
+ value: unknown,
137
+ extractOptions: ExtractExperimentSamplesOptions,
138
+ ): void {
139
+ if (!Array.isArray(value)) return
140
+ for (const sample of value) {
141
+ addSampleRecord(options, seen, sample, extractOptions)
142
+ }
143
+ }
144
+
145
+ /** Extract selectable sample options from raw or wrapped experiment design_data. */
146
+ export function extractSampleOptionsFromDesignData(
147
+ rawData: Record<string, unknown> | null | undefined,
148
+ options: ExtractExperimentSamplesOptions = {},
149
+ ): SelectOption<string>[] {
150
+ const designData = unwrapExperimentDesignData(rawData)
151
+ if (!designData) return []
152
+
153
+ const sampleOptions: SelectOption<string>[] = []
154
+ const seen = new Set<string>()
155
+
156
+ addSampleArray(sampleOptions, seen, designData.samples, options)
157
+
158
+ if (isRecord(designData.templates)) {
159
+ for (const template of Object.values(designData.templates)) {
160
+ if (!isRecord(template)) continue
161
+ const templateData = isRecord(template.data) ? template.data : template
162
+ addSampleArray(sampleOptions, seen, templateData.samples, options)
163
+ }
164
+ }
165
+
166
+ if (Array.isArray(designData.plates)) {
167
+ for (const plate of designData.plates) {
168
+ if (!isRecord(plate)) continue
169
+ addSampleArray(sampleOptions, seen, plate.samples, options)
170
+ }
171
+ }
172
+
173
+ return sampleOptions
174
+ }
175
+
176
+ /** Extract SampleSelector-compatible string values from raw or wrapped experiment design_data. */
177
+ export function extractSampleNamesFromDesignData(
178
+ rawData: Record<string, unknown> | null | undefined,
179
+ options: ExtractExperimentSamplesOptions = {},
180
+ ): string[] {
181
+ return extractSampleOptionsFromDesignData(rawData, options).map(sample => sample.value)
182
+ }
@@ -144,6 +144,20 @@ export {
144
144
  type UseExperimentDataOptions,
145
145
  type UseExperimentDataReturn,
146
146
  } from './useExperimentData'
147
+ export {
148
+ extractSampleNamesFromDesignData,
149
+ extractSampleOptionsFromDesignData,
150
+ unwrapExperimentDesignData,
151
+ type ExperimentDesignDataResponse,
152
+ type ExtractExperimentSamplesOptions,
153
+ } from './experimentDesignData'
154
+ export {
155
+ useExperimentSamples,
156
+ type ExperimentDesignDataSource,
157
+ type ExperimentIdSource,
158
+ type UseExperimentSamplesOptions,
159
+ type UseExperimentSamplesReturn,
160
+ } from './useExperimentSamples'
147
161
  export {
148
162
  getFieldRegistryEntry,
149
163
  getTypeDefault,
@@ -3,6 +3,7 @@ import { ref, onMounted, onUnmounted, watch, getCurrentInstance, type Ref } from
3
3
  import { useAuthStore } from '../stores/auth'
4
4
  import { useSettingsStore } from '../stores/settings'
5
5
  import type { AuthConfig, UserInfo, LoginResponse, TokenVerifyResponse, UpdateProfileRequest } from '../types'
6
+ import type { RoleInfo } from '../permissions'
6
7
 
7
8
  interface UserResponse {
8
9
  id: string
@@ -10,6 +11,7 @@ interface UserResponse {
10
11
  shortname: string | null
11
12
  email: string | null
12
13
  role: string
14
+ role_obj?: RoleInfo | null
13
15
  is_active: boolean
14
16
  }
15
17
 
@@ -187,6 +189,7 @@ export function useAuth(): UseAuthReturn {
187
189
  shortname: response.data.shortname,
188
190
  email: response.data.email,
189
191
  role: response.data.role,
192
+ roleObj: response.data.role_obj ?? null,
190
193
  isActive: response.data.is_active,
191
194
  }
192
195
 
@@ -379,6 +382,7 @@ export function useAuth(): UseAuthReturn {
379
382
  shortname: response.data.shortname,
380
383
  email: response.data.email,
381
384
  role: response.data.role,
385
+ roleObj: response.data.role_obj ?? null,
382
386
  isActive: response.data.is_active,
383
387
  }
384
388
  authStore.setUserInfo(userInfo)
@@ -9,6 +9,7 @@ import type {
9
9
  ParsedCsvData,
10
10
  } from '../types/auto-group'
11
11
  import type { SampleGroup } from '../types/components'
12
+ import { unwrapExperimentDesignData } from './experimentDesignData'
12
13
 
13
14
  export const DEFAULT_COLORS = [
14
15
  '#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6',
@@ -311,7 +312,10 @@ export function computeGroups(
311
312
  export function extractSamplesFromDesignData(
312
313
  rawData: Record<string, unknown>,
313
314
  ): ParsedCsvData | null {
314
- const samples = rawData.samples
315
+ const designData = unwrapExperimentDesignData(rawData)
316
+ if (!designData) return null
317
+
318
+ const samples = designData.samples
315
319
  if (!Array.isArray(samples) || samples.length === 0) return null
316
320
 
317
321
  // Single pass: filter QC/blank and collect all condition keys