@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
package/src/lcms.ts ADDED
@@ -0,0 +1,108 @@
1
+ import type { LcmsPlateCell, LcmsPlateType } from './utils/rack'
2
+
3
+ export type LcmsPolarity = 'POS' | 'NEG' | 'BOTH'
4
+ export type LcmsContainerType = 'VIAL' | 'PLATE'
5
+
6
+ export interface LcmsSequenceItem {
7
+ sample_type: string
8
+ file_name: string
9
+ sample_id: string
10
+ path: string
11
+ instrument_method: string
12
+ position: string
13
+ injection_volume: number
14
+ }
15
+
16
+ export interface LcmsSequenceTableColumn {
17
+ key: keyof LcmsSequenceItem | 'index' | 'actions'
18
+ label: string
19
+ align?: 'left' | 'right'
20
+ }
21
+
22
+ export const DEFAULT_LCMS_SEQUENCE_COLUMNS: LcmsSequenceTableColumn[] = [
23
+ { key: 'index', label: '#' },
24
+ { key: 'sample_type', label: 'Type' },
25
+ { key: 'file_name', label: 'File Name' },
26
+ { key: 'position', label: 'Position' },
27
+ { key: 'instrument_method', label: 'Method' },
28
+ { key: 'injection_volume', label: 'Inj Vol', align: 'right' },
29
+ ]
30
+
31
+ export function extractLcmsCommonPrefix(fileNames: readonly string[]): string {
32
+ if (fileNames.length === 0) return ''
33
+ const stripped = fileNames.map(fileName => fileName.replace(/_\d{3}$/, ''))
34
+ const prefixes = stripped.map((fileName) => {
35
+ const match = fileName.match(/^(.+?)_(POS|NEG)_/i)
36
+ return match ? match[1] : fileName.replace(/_[^_]+$/, '')
37
+ })
38
+
39
+ let common = prefixes[0] ?? ''
40
+ for (const prefix of prefixes.slice(1)) {
41
+ while (common && !prefix.startsWith(common)) {
42
+ const index = common.lastIndexOf('_')
43
+ common = index > 0 ? common.substring(0, index) : common.substring(0, common.length - 1)
44
+ }
45
+ }
46
+ return common
47
+ }
48
+
49
+ export function extractLcmsSampleName(fileName: string, prefix = ''): string {
50
+ let name = fileName.replace(/_\d{3}$/, '')
51
+ if (prefix && name.startsWith(prefix)) {
52
+ name = name.substring(prefix.length).replace(/^_+/, '')
53
+ }
54
+ name = name.replace(/^(POS|NEG)_/i, '')
55
+ return name || fileName
56
+ }
57
+
58
+ export function lcmsWellIdFromPosition(position: string): string {
59
+ return position.includes(':') ? position.split(':').pop() ?? position : position
60
+ }
61
+
62
+ export function inferLcmsPlateTypeFromWellIds(wellIds: readonly string[]): LcmsPlateType | null {
63
+ let maxCol = 0
64
+ let maxRow = 0
65
+ for (const wellId of wellIds) {
66
+ const match = wellId.match(/^([A-Z])(\d+)$/i)
67
+ if (!match) continue
68
+ maxRow = Math.max(maxRow, match[1].toUpperCase().charCodeAt(0) - 64)
69
+ maxCol = Math.max(maxCol, Number(match[2]))
70
+ }
71
+ if (maxCol === 0 && maxRow === 0) return null
72
+ return maxCol <= 9 && maxRow <= 6 ? '54vial' : '96well'
73
+ }
74
+
75
+ export function reconstructLcmsPlateCellsFromSequenceItems(
76
+ items: readonly LcmsSequenceItem[],
77
+ ): { cells: LcmsPlateCell[]; plateType: LcmsPlateType } | null {
78
+ const unknownItems = items.filter(item => item.sample_type === 'Unknown')
79
+ if (unknownItems.length === 0) return null
80
+
81
+ const prefix = extractLcmsCommonPrefix(unknownItems.map(item => item.file_name))
82
+ const seen = new Set<string>()
83
+ const cells: LcmsPlateCell[] = []
84
+
85
+ for (const item of unknownItems) {
86
+ const wellId = lcmsWellIdFromPosition(item.position)
87
+ if (seen.has(wellId)) continue
88
+ seen.add(wellId)
89
+
90
+ const match = wellId.match(/^([A-Z])(\d+)$/i)
91
+ if (!match) continue
92
+ cells.push({
93
+ row: match[1].toUpperCase(),
94
+ column: Number(match[2]),
95
+ sample_name: extractLcmsSampleName(item.file_name, prefix),
96
+ })
97
+ }
98
+
99
+ if (cells.length === 0) return null
100
+ const plateType = inferLcmsPlateTypeFromWellIds(cells.map(cell => `${cell.row}${cell.column}`))
101
+ if (!plateType) return null
102
+ return { cells, plateType }
103
+ }
104
+
105
+ export function basenameFromWindowsPath(path: string | null | undefined): string {
106
+ if (!path) return ''
107
+ return path.split(/[\\/]/).pop() ?? path
108
+ }
@@ -0,0 +1,143 @@
1
+ export const ADMIN_ROLE = 'admin'
2
+
3
+ export const ADMIN_PANEL_PERMISSIONS = [
4
+ 'users.view',
5
+ 'users.invite',
6
+ 'users.manage',
7
+ 'platform.configure',
8
+ 'platform.view_logs',
9
+ 'plugins.configure',
10
+ 'plugins.install',
11
+ ] as const
12
+
13
+ export type AccessAudience = 'all' | 'admin' | 'user'
14
+ export type AccessAudienceInput = AccessAudience | readonly AccessAudience[]
15
+
16
+ export interface RoleInfo {
17
+ id?: number
18
+ slug: string
19
+ name?: string
20
+ color?: string
21
+ permissions?: readonly string[] | null
22
+ project_scope?: 'all' | 'assigned' | string
23
+ plugin_access?: readonly string[] | 'all' | null
24
+ }
25
+
26
+ export interface PermissionUser {
27
+ role?: string | null
28
+ role_obj?: RoleInfo | null
29
+ roleObj?: RoleInfo | null
30
+ permissions?: readonly string[] | null
31
+ }
32
+
33
+ export interface AccessPolicy {
34
+ audience?: AccessAudienceInput
35
+ visibleFor?: AccessAudienceInput
36
+ requiresAuth?: boolean
37
+ requiresAdmin?: boolean
38
+ permissions?: readonly string[]
39
+ anyPermissions?: readonly string[]
40
+ plugin?: string
41
+ }
42
+
43
+ export interface AccessControlled {
44
+ access?: AccessPolicy
45
+ visibleFor?: AccessAudienceInput
46
+ requiresAdmin?: boolean
47
+ permissions?: readonly string[]
48
+ anyPermissions?: readonly string[]
49
+ }
50
+
51
+ export function getRoleInfo(user: PermissionUser | null | undefined): RoleInfo | null {
52
+ return user?.roleObj ?? user?.role_obj ?? null
53
+ }
54
+
55
+ export function isAdminRole(role: string | null | undefined): boolean {
56
+ return role === ADMIN_ROLE
57
+ }
58
+
59
+ export function isAdminUser(user: PermissionUser | null | undefined): boolean {
60
+ return isAdminRole(user?.role) || getRoleInfo(user)?.slug === ADMIN_ROLE
61
+ }
62
+
63
+ export function getAccessAudience(user: PermissionUser | null | undefined): Exclude<AccessAudience, 'all'> {
64
+ return isAdminUser(user) ? 'admin' : 'user'
65
+ }
66
+
67
+ export function getUserPermissions(user: PermissionUser | null | undefined): string[] {
68
+ const rolePermissions = getRoleInfo(user)?.permissions
69
+ if (rolePermissions?.length) return [...rolePermissions]
70
+ return [...(user?.permissions ?? [])]
71
+ }
72
+
73
+ export function hasAllPermissions(
74
+ user: PermissionUser | null | undefined,
75
+ permissions: readonly string[] = [],
76
+ ): boolean {
77
+ if (permissions.length === 0) return true
78
+ if (isAdminUser(user)) return true
79
+ const granted = new Set(getUserPermissions(user))
80
+ return permissions.every(permission => granted.has(permission))
81
+ }
82
+
83
+ export function hasAnyPermission(
84
+ user: PermissionUser | null | undefined,
85
+ permissions: readonly string[] = [],
86
+ ): boolean {
87
+ if (permissions.length === 0) return true
88
+ if (isAdminUser(user)) return true
89
+ const granted = new Set(getUserPermissions(user))
90
+ return permissions.some(permission => granted.has(permission))
91
+ }
92
+
93
+ export function canAccessAdmin(user: PermissionUser | null | undefined): boolean {
94
+ return isAdminUser(user) || hasAnyPermission(user, ADMIN_PANEL_PERMISSIONS)
95
+ }
96
+
97
+ export function canAccessPlugin(
98
+ user: PermissionUser | null | undefined,
99
+ pluginName: string | null | undefined,
100
+ ): boolean {
101
+ if (!pluginName) return true
102
+ const access = getRoleInfo(user)?.plugin_access
103
+ if (!access || access === 'all') return true
104
+ return Array.isArray(access) && access.includes(pluginName)
105
+ }
106
+
107
+ export function normalizeAccessPolicy(rule: AccessControlled | AccessPolicy | undefined): AccessPolicy {
108
+ if (!rule) return {}
109
+ const { access: nested, ...direct } = rule as AccessPolicy & { access?: AccessPolicy }
110
+ return {
111
+ ...(nested ?? {}),
112
+ ...direct,
113
+ ...('visibleFor' in rule && rule.visibleFor !== undefined ? { visibleFor: rule.visibleFor } : {}),
114
+ ...('requiresAdmin' in rule && rule.requiresAdmin !== undefined ? { requiresAdmin: rule.requiresAdmin } : {}),
115
+ ...('permissions' in rule && rule.permissions !== undefined ? { permissions: rule.permissions } : {}),
116
+ ...('anyPermissions' in rule && rule.anyPermissions !== undefined ? { anyPermissions: rule.anyPermissions } : {}),
117
+ }
118
+ }
119
+
120
+ export function canAccessByPolicy(
121
+ user: PermissionUser | null | undefined,
122
+ rule: AccessControlled | AccessPolicy | undefined,
123
+ isAuthenticated = user !== null && user !== undefined,
124
+ ): boolean {
125
+ const policy = normalizeAccessPolicy(rule)
126
+ if (policy.requiresAuth && !isAuthenticated) return false
127
+ if (policy.requiresAdmin && !isAdminUser(user)) return false
128
+ if (!audienceAllowsUser(policy.visibleFor ?? policy.audience, user)) return false
129
+ if (!hasAllPermissions(user, policy.permissions)) return false
130
+ if (!hasAnyPermission(user, policy.anyPermissions)) return false
131
+ if (policy.plugin && !canAccessPlugin(user, policy.plugin)) return false
132
+ return true
133
+ }
134
+
135
+ function audienceAllowsUser(
136
+ audience: AccessAudienceInput | undefined,
137
+ user: PermissionUser | null | undefined,
138
+ ): boolean {
139
+ if (audience === undefined) return true
140
+ const allowed = Array.isArray(audience) ? audience : [audience]
141
+ if (allowed.includes('all')) return true
142
+ return allowed.includes(getAccessAudience(user))
143
+ }
@@ -1,6 +1,15 @@
1
1
  import { defineStore } from 'pinia'
2
2
  import { ref, computed } from 'vue'
3
3
  import type { AuthConfig, UserInfo } from '../types'
4
+ import {
5
+ canAccessAdmin as canUserAccessAdmin,
6
+ canAccessPlugin as canUserAccessPlugin,
7
+ getAccessAudience,
8
+ getUserPermissions,
9
+ hasAllPermissions,
10
+ hasAnyPermission,
11
+ isAdminUser,
12
+ } from '../permissions'
4
13
 
5
14
  const AUTH_TOKEN_KEY = 'mint-auth-token'
6
15
  const AUTH_EXPIRES_KEY = 'mint-auth-expires'
@@ -73,9 +82,10 @@ export const useAuthStore = defineStore('mint-auth', () => {
73
82
  return authConfig.value.authRequired && !isAuthenticated.value
74
83
  })
75
84
 
76
- const isAdmin = computed(() => {
77
- return userInfo.value?.role === 'admin'
78
- })
85
+ const isAdmin = computed(() => isAdminUser(userInfo.value))
86
+ const userType = computed(() => getAccessAudience(userInfo.value))
87
+ const permissions = computed(() => getUserPermissions(userInfo.value))
88
+ const canAccessAdmin = computed(() => canUserAccessAdmin(userInfo.value))
79
89
 
80
90
  const canRegister = computed(() => {
81
91
  return authConfig.value.registrationEnabled
@@ -126,6 +136,18 @@ export const useAuthStore = defineStore('mint-auth', () => {
126
136
  username.value = info.username
127
137
  }
128
138
 
139
+ function hasPermission(...perms: string[]): boolean {
140
+ return hasAllPermissions(userInfo.value, perms)
141
+ }
142
+
143
+ function hasAnyPermissionForUser(...perms: string[]): boolean {
144
+ return hasAnyPermission(userInfo.value, perms)
145
+ }
146
+
147
+ function canAccessPlugin(pluginName: string): boolean {
148
+ return canUserAccessPlugin(userInfo.value, pluginName)
149
+ }
150
+
129
151
  function setAuthConfig(config: AuthConfig) {
130
152
  authConfig.value = config
131
153
  }
@@ -161,6 +183,9 @@ export const useAuthStore = defineStore('mint-auth', () => {
161
183
  isAuthenticated,
162
184
  needsAuth,
163
185
  isAdmin,
186
+ userType,
187
+ permissions,
188
+ canAccessAdmin,
164
189
  canRegister,
165
190
 
166
191
  // Actions
@@ -172,6 +197,9 @@ export const useAuthStore = defineStore('mint-auth', () => {
172
197
  setUserInfo,
173
198
  setError,
174
199
  setLoading,
200
+ hasPermission,
201
+ hasAnyPermission: hasAnyPermissionForUser,
202
+ canAccessPlugin,
175
203
  logout,
176
204
  }
177
205
  })