@morscherlab/mint-sdk 1.0.0-rc.2 → 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 (178) hide show
  1. package/dist/components/AppSidebar.vue.d.ts +3 -3
  2. package/dist/components/ControlWorkspaceView.vue.d.ts +3 -3
  3. package/dist/components/DoseDesignWorkspaceView.vue.d.ts +1 -1
  4. package/dist/components/PluginWorkspaceView.vue.d.ts +3 -3
  5. package/dist/components/index.js +2 -2
  6. package/dist/{components-BhK-dW99.js → components-DafPc4rM.js} +3 -3
  7. package/dist/{components-BhK-dW99.js.map → components-DafPc4rM.js.map} +1 -1
  8. package/dist/composables/controlComponentBindings.d.ts +7 -0
  9. package/dist/composables/controlSchemaAdapters.d.ts +20 -0
  10. package/dist/composables/controlSchemaFormFields.d.ts +3 -0
  11. package/dist/composables/controlSchemaLayout.d.ts +7 -0
  12. package/dist/composables/controlSchemaNormalize.d.ts +15 -0
  13. package/dist/composables/controlSchemaUtils.d.ts +9 -0
  14. package/dist/composables/controlWorkspaceOptions.d.ts +2 -0
  15. package/dist/composables/index.js +3 -3
  16. package/dist/composables/useControlSchema.d.ts +4 -30
  17. package/dist/composables/useControlWorkspace.d.ts +5 -0
  18. package/dist/{composables-Bg7CFuNz.js → composables-BMkPQhVK.js} +2 -2
  19. package/dist/{composables-Bg7CFuNz.js.map → composables-BMkPQhVK.js.map} +1 -1
  20. package/dist/index.js +4 -4
  21. package/dist/install.js +2 -2
  22. package/dist/templates/adapters.d.ts +14 -47
  23. package/dist/templates/assayLookups.d.ts +3 -0
  24. package/dist/templates/assayMatrixAdapters.d.ts +6 -0
  25. package/dist/templates/assayMatrixBuilder.d.ts +2 -0
  26. package/dist/templates/assayNormalizers.d.ts +4 -0
  27. package/dist/templates/builderDefaults.d.ts +1 -0
  28. package/dist/templates/builderIdUtils.d.ts +4 -0
  29. package/dist/templates/builderPresetControls.d.ts +10 -0
  30. package/dist/templates/builderReadUtils.d.ts +8 -0
  31. package/dist/templates/builders.d.ts +26 -67
  32. package/dist/templates/calibrationCurveAdapters.d.ts +4 -0
  33. package/dist/templates/calibrationCurveBuilder.d.ts +2 -0
  34. package/dist/templates/calibrationNormalizers.d.ts +5 -0
  35. package/dist/templates/componentBindingCatalog.d.ts +25 -0
  36. package/dist/templates/componentBindingHelpers.d.ts +17 -0
  37. package/dist/templates/componentDoseResponseProps.d.ts +3 -0
  38. package/dist/templates/componentGenericProps.d.ts +8 -0
  39. package/dist/templates/componentPlateHelpers.d.ts +5 -0
  40. package/dist/templates/componentPlateMapProps.d.ts +3 -0
  41. package/dist/templates/componentPropsFactory.d.ts +3 -0
  42. package/dist/templates/componentQpcrPlateProps.d.ts +3 -0
  43. package/dist/templates/componentTargetResolvers.d.ts +5 -0
  44. package/dist/templates/componentTemplateProps.d.ts +3 -0
  45. package/dist/templates/controlSchemaClone.d.ts +4 -0
  46. package/dist/templates/controlSchemaConstants.d.ts +10 -0
  47. package/dist/templates/controlSchemaMerge.d.ts +3 -0
  48. package/dist/templates/controlSchemaTargets.d.ts +17 -0
  49. package/dist/templates/controlSchemaTypes.d.ts +4 -0
  50. package/dist/templates/controlSchemas.d.ts +4 -4
  51. package/dist/templates/defaultBioTemplateBuilder.d.ts +2 -0
  52. package/dist/templates/doseResponseAdapters.d.ts +4 -0
  53. package/dist/templates/doseResponseBuilder.d.ts +2 -0
  54. package/dist/templates/elisaAssayCollectionBuilder.d.ts +2 -0
  55. package/dist/templates/flowCytometryAssayCollectionBuilder.d.ts +2 -0
  56. package/dist/templates/flowCytometryPanelBuilder.d.ts +2 -0
  57. package/dist/templates/flowNormalizers.d.ts +8 -0
  58. package/dist/templates/flowPanelAdapters.d.ts +4 -0
  59. package/dist/templates/index.js +1 -1
  60. package/dist/templates/instrumentRunAdapterHelpers.d.ts +8 -0
  61. package/dist/templates/instrumentRunAdapters.d.ts +8 -0
  62. package/dist/templates/instrumentRunBuilder.d.ts +2 -0
  63. package/dist/templates/lcmsBatchCollectionBuilder.d.ts +2 -0
  64. package/dist/templates/plateGeometry.d.ts +4 -0
  65. package/dist/templates/plateMapAdapters.d.ts +3 -0
  66. package/dist/templates/plateMapBuilder.d.ts +2 -0
  67. package/dist/templates/presetControlSchemas.d.ts +534 -0
  68. package/dist/templates/protocolAdapters.d.ts +5 -0
  69. package/dist/templates/protocolNormalizers.d.ts +6 -0
  70. package/dist/templates/protocolStepsBuilder.d.ts +2 -0
  71. package/dist/templates/qpcrAdapters.d.ts +5 -0
  72. package/dist/templates/qpcrExpressionCollectionBuilder.d.ts +2 -0
  73. package/dist/templates/qpcrPlateBuilder.d.ts +2 -0
  74. package/dist/templates/reagentAdapters.d.ts +5 -0
  75. package/dist/templates/reagentListBuilder.d.ts +2 -0
  76. package/dist/templates/runNormalizers.d.ts +10 -0
  77. package/dist/templates/sampleNormalizers.d.ts +4 -0
  78. package/dist/templates/samplePrepAdapters.d.ts +4 -0
  79. package/dist/templates/samplePrepBuilder.d.ts +2 -0
  80. package/dist/templates/sampleSheetAdapters.d.ts +5 -0
  81. package/dist/templates/sampleSheetBuilder.d.ts +2 -0
  82. package/dist/templates/targetedMetabolomicsCollectionBuilder.d.ts +2 -0
  83. package/dist/templates/targetedMetabolomicsHelpers.d.ts +5 -0
  84. package/dist/templates/templateControlSchemas.d.ts +400 -0
  85. package/dist/templates/templateEnvelopes.d.ts +9 -0
  86. package/dist/templates/templatePackCollectionBuilder.d.ts +2 -0
  87. package/dist/templates/templatePresetCollectionBuilder.d.ts +18 -0
  88. package/dist/templates/templateValidators.d.ts +13 -0
  89. package/dist/templates/timeCourseAdapters.d.ts +5 -0
  90. package/dist/templates/timeCourseBuilder.d.ts +2 -0
  91. package/dist/templates/wellPlateScreenCollectionBuilder.d.ts +2 -0
  92. package/dist/templates/westernBlotAssayCollectionBuilder.d.ts +2 -0
  93. package/dist/{templates-BorLR_7p.js → templates-bUAWMn5L.js} +3574 -3428
  94. package/dist/templates-bUAWMn5L.js.map +1 -0
  95. package/dist/{useProtocolTemplates-n6AJqSqv.js → useProtocolTemplates-QZtHFFH2.js} +2 -2
  96. package/dist/{useProtocolTemplates-n6AJqSqv.js.map → useProtocolTemplates-QZtHFFH2.js.map} +1 -1
  97. package/package.json +1 -1
  98. package/src/composables/controlComponentBindings.ts +80 -0
  99. package/src/composables/controlSchemaAdapters.ts +196 -0
  100. package/src/composables/controlSchemaFormFields.ts +61 -0
  101. package/src/composables/controlSchemaLayout.ts +59 -0
  102. package/src/composables/controlSchemaNormalize.ts +101 -0
  103. package/src/composables/controlSchemaUtils.ts +36 -0
  104. package/src/composables/controlWorkspaceOptions.ts +21 -0
  105. package/src/composables/useControlSchema.ts +51 -628
  106. package/src/composables/useControlWorkspace.ts +207 -0
  107. package/src/templates/adapters.ts +89 -930
  108. package/src/templates/assayLookups.ts +33 -0
  109. package/src/templates/assayMatrixAdapters.ts +78 -0
  110. package/src/templates/assayMatrixBuilder.ts +59 -0
  111. package/src/templates/assayNormalizers.ts +34 -0
  112. package/src/templates/builderDefaults.ts +11 -0
  113. package/src/templates/builderIdUtils.ts +20 -0
  114. package/src/templates/builderPresetControls.ts +165 -0
  115. package/src/templates/builderReadUtils.ts +57 -0
  116. package/src/templates/builders.ts +122 -2350
  117. package/src/templates/calibrationCurveAdapters.ts +59 -0
  118. package/src/templates/calibrationCurveBuilder.ts +99 -0
  119. package/src/templates/calibrationNormalizers.ts +60 -0
  120. package/src/templates/componentBindingCatalog.ts +90 -0
  121. package/src/templates/componentBindingHelpers.ts +93 -0
  122. package/src/templates/componentBindings.ts +12 -461
  123. package/src/templates/componentDoseResponseProps.ts +42 -0
  124. package/src/templates/componentGenericProps.ts +77 -0
  125. package/src/templates/componentPlateHelpers.ts +29 -0
  126. package/src/templates/componentPlateMapProps.ts +32 -0
  127. package/src/templates/componentPropsFactory.ts +21 -0
  128. package/src/templates/componentQpcrPlateProps.ts +28 -0
  129. package/src/templates/componentTargetResolvers.ts +69 -0
  130. package/src/templates/componentTemplateProps.ts +78 -0
  131. package/src/templates/controlSchemaClone.ts +32 -0
  132. package/src/templates/controlSchemaConstants.ts +11 -0
  133. package/src/templates/controlSchemaMerge.ts +40 -0
  134. package/src/templates/controlSchemaTargets.ts +87 -0
  135. package/src/templates/controlSchemaTypes.ts +20 -0
  136. package/src/templates/controlSchemas.ts +22 -704
  137. package/src/templates/defaultBioTemplateBuilder.ts +124 -0
  138. package/src/templates/doseResponseAdapters.ts +45 -0
  139. package/src/templates/doseResponseBuilder.ts +44 -0
  140. package/src/templates/elisaAssayCollectionBuilder.ts +62 -0
  141. package/src/templates/flowCytometryAssayCollectionBuilder.ts +41 -0
  142. package/src/templates/flowCytometryPanelBuilder.ts +53 -0
  143. package/src/templates/flowNormalizers.ts +58 -0
  144. package/src/templates/flowPanelAdapters.ts +58 -0
  145. package/src/templates/instrumentRunAdapterHelpers.ts +94 -0
  146. package/src/templates/instrumentRunAdapters.ts +163 -0
  147. package/src/templates/instrumentRunBuilder.ts +97 -0
  148. package/src/templates/lcmsBatchCollectionBuilder.ts +38 -0
  149. package/src/templates/plateGeometry.ts +62 -0
  150. package/src/templates/plateMapAdapters.ts +36 -0
  151. package/src/templates/plateMapBuilder.ts +43 -0
  152. package/src/templates/presetControlSchemas.ts +258 -0
  153. package/src/templates/protocolAdapters.ts +69 -0
  154. package/src/templates/protocolNormalizers.ts +37 -0
  155. package/src/templates/protocolStepsBuilder.ts +36 -0
  156. package/src/templates/qpcrAdapters.ts +104 -0
  157. package/src/templates/qpcrExpressionCollectionBuilder.ts +33 -0
  158. package/src/templates/qpcrPlateBuilder.ts +96 -0
  159. package/src/templates/reagentAdapters.ts +77 -0
  160. package/src/templates/reagentListBuilder.ts +30 -0
  161. package/src/templates/runNormalizers.ts +63 -0
  162. package/src/templates/sampleNormalizers.ts +58 -0
  163. package/src/templates/samplePrepAdapters.ts +63 -0
  164. package/src/templates/samplePrepBuilder.ts +51 -0
  165. package/src/templates/sampleSheetAdapters.ts +75 -0
  166. package/src/templates/sampleSheetBuilder.ts +23 -0
  167. package/src/templates/targetedMetabolomicsCollectionBuilder.ts +79 -0
  168. package/src/templates/targetedMetabolomicsHelpers.ts +102 -0
  169. package/src/templates/templateControlSchemas.ts +320 -0
  170. package/src/templates/templateEnvelopes.ts +137 -0
  171. package/src/templates/templatePackCollectionBuilder.ts +23 -0
  172. package/src/templates/templatePresetCollectionBuilder.ts +139 -0
  173. package/src/templates/templateValidators.ts +414 -0
  174. package/src/templates/timeCourseAdapters.ts +73 -0
  175. package/src/templates/timeCourseBuilder.ts +64 -0
  176. package/src/templates/wellPlateScreenCollectionBuilder.ts +36 -0
  177. package/src/templates/westernBlotAssayCollectionBuilder.ts +68 -0
  178. package/dist/templates-BorLR_7p.js.map +0 -1
@@ -0,0 +1,124 @@
1
+ import { getBioTemplateInfo } from './catalog'
2
+ import { createAssayMatrixTemplate } from './assayMatrixBuilder'
3
+ import { createCalibrationCurveTemplate } from './calibrationCurveBuilder'
4
+ import { createDoseResponseTemplate } from './doseResponseBuilder'
5
+ import { createFlowCytometryPanelTemplate } from './flowCytometryPanelBuilder'
6
+ import { createInstrumentRunTemplate } from './instrumentRunBuilder'
7
+ import { createPlateMapTemplate } from './plateMapBuilder'
8
+ import { createProtocolStepsTemplate } from './protocolStepsBuilder'
9
+ import { createQpcrPlateTemplate } from './qpcrPlateBuilder'
10
+ import { createReagentListTemplate } from './reagentListBuilder'
11
+ import { createSamplePrepTemplate } from './samplePrepBuilder'
12
+ import { createSampleSheetTemplate } from './sampleSheetBuilder'
13
+ import { createTimeCourseTemplate } from './timeCourseBuilder'
14
+ import type {
15
+ BioTemplateEnvelope,
16
+ TemplateId,
17
+ } from './types'
18
+
19
+ export function createDefaultBioTemplate(templateId: TemplateId | string): BioTemplateEnvelope<unknown> {
20
+ const template = getBioTemplateInfo(templateId)
21
+ if (!template) {
22
+ throw new Error(`Unknown bio template '${templateId}'.`)
23
+ }
24
+
25
+ switch (template.template_id) {
26
+ case 'plate-map':
27
+ return createPlateMapTemplate({
28
+ name: 'Plate 1',
29
+ samples: ['Control', 'Treatment'],
30
+ })
31
+ case 'sample-sheet':
32
+ return createSampleSheetTemplate({
33
+ samples: [
34
+ { sampleId: 'S001', name: 'Control 1', group: 'Control' },
35
+ { sampleId: 'S002', name: 'Treatment 1', group: 'Treatment' },
36
+ ],
37
+ })
38
+ case 'sample-prep':
39
+ return createSamplePrepTemplate({
40
+ samples: ['S001', 'S002'],
41
+ prepType: 'extraction',
42
+ outputVolume: 50,
43
+ })
44
+ case 'dose-response':
45
+ return createDoseResponseTemplate({
46
+ compounds: {
47
+ 'Compound A': [10, 1, 0.1],
48
+ },
49
+ unit: 'uM',
50
+ replicates: 3,
51
+ })
52
+ case 'calibration-curve':
53
+ return createCalibrationCurveTemplate({
54
+ concentrations: [0.1, 1, 10, 100],
55
+ analyte: 'Analyte',
56
+ unit: 'uM',
57
+ })
58
+ case 'time-course':
59
+ return createTimeCourseTemplate({
60
+ timepoints: [0, 6, 24],
61
+ unit: 'hour',
62
+ conditions: ['Control', 'Treatment'],
63
+ replicates: 3,
64
+ })
65
+ case 'protocol-steps':
66
+ return createProtocolStepsTemplate({
67
+ steps: [
68
+ {
69
+ id: 'seed-cells',
70
+ type: 'addition',
71
+ name: 'Seed cells',
72
+ description: 'Add cells to wells or culture vessels.',
73
+ duration: 15,
74
+ },
75
+ {
76
+ id: 'incubate-overnight',
77
+ type: 'incubation',
78
+ name: 'Incubate overnight',
79
+ duration: 960,
80
+ parameters: { temperature: '37 C', co2: '5%' },
81
+ },
82
+ {
83
+ id: 'measure-readout',
84
+ type: 'measurement',
85
+ name: 'Measure readout',
86
+ duration: 45,
87
+ },
88
+ ],
89
+ })
90
+ case 'assay-matrix':
91
+ return createAssayMatrixTemplate({
92
+ samples: ['S001', 'S002'],
93
+ features: ['Lactate', 'Glucose'],
94
+ values: {
95
+ s001: { lactate: 1.2, glucose: 5.4 },
96
+ s002: { lactate: 2.1, glucose: 4.8 },
97
+ },
98
+ })
99
+ case 'reagent-list':
100
+ return createReagentListTemplate({
101
+ reagents: ['DMSO', 'Compound A', 'Anti-HA antibody'],
102
+ })
103
+ case 'flow-cytometry-panel':
104
+ return createFlowCytometryPanelTemplate({
105
+ markers: ['CD3', 'CD4', 'CD8'],
106
+ instrument: 'BD LSRFortessa',
107
+ })
108
+ case 'instrument-run':
109
+ return createInstrumentRunTemplate({
110
+ items: ['S001', 'S002'],
111
+ instrument: 'LC-MS',
112
+ method: 'Default method',
113
+ })
114
+ case 'qpcr-plate':
115
+ return createQpcrPlateTemplate({
116
+ samples: ['Control', 'Treatment'],
117
+ targets: ['ACTB', 'GAPDH'],
118
+ replicates: 3,
119
+ instrument: 'QuantStudio',
120
+ })
121
+ default:
122
+ throw new Error(`Unknown bio template '${templateId}'.`)
123
+ }
124
+ }
@@ -0,0 +1,45 @@
1
+ import type {
2
+ PlateCondition,
3
+ PlateMapEditorState,
4
+ } from '../types'
5
+ import { toPlateMapEditorState } from './plateMapAdapters'
6
+ import { getTemplateData } from './templateEnvelopes'
7
+ import { validateDoseResponseData } from './templateValidators'
8
+ import type {
9
+ DoseConditionsAdapterResult,
10
+ DoseResponseTemplate,
11
+ DoseResponseTemplateData,
12
+ } from './types'
13
+
14
+ const CONDITION_COLORS = [
15
+ '#3B82F6',
16
+ '#10B981',
17
+ '#EF4444',
18
+ '#F59E0B',
19
+ '#8B5CF6',
20
+ '#F97316',
21
+ '#06B6D4',
22
+ '#14B8A6',
23
+ '#6B7280',
24
+ ]
25
+
26
+ export function toDoseConditions(
27
+ template: DoseResponseTemplate | DoseResponseTemplateData
28
+ ): DoseConditionsAdapterResult {
29
+ const data = getTemplateData(template, 'dose-response')
30
+ validateDoseResponseData(data)
31
+ return data.compounds.map((compound, index): PlateCondition => ({
32
+ label: compound.name,
33
+ color: String(compound.metadata?.color ?? CONDITION_COLORS[index % CONDITION_COLORS.length]),
34
+ concentrations: compound.concentrations,
35
+ unit: data.unit,
36
+ }))
37
+ }
38
+
39
+ export function toDoseLayoutState(
40
+ template: DoseResponseTemplate | DoseResponseTemplateData
41
+ ): PlateMapEditorState | undefined {
42
+ const data = getTemplateData(template, 'dose-response')
43
+ validateDoseResponseData(data)
44
+ return data.layout ? toPlateMapEditorState(data.layout) : undefined
45
+ }
@@ -0,0 +1,44 @@
1
+ import { compoundIdFromName } from './builderIdUtils'
2
+ import {
3
+ normalizeCompound,
4
+ normalizeControl,
5
+ } from './calibrationNormalizers'
6
+ import {
7
+ createTemplateEnvelope,
8
+ getTemplateData,
9
+ } from './templateEnvelopes'
10
+ import { validateDoseResponseData } from './templateValidators'
11
+ import type {
12
+ CreateDoseResponseTemplateOptions,
13
+ DoseResponseTemplate,
14
+ DoseResponseTemplateData,
15
+ PlateMapTemplateData,
16
+ } from './types'
17
+
18
+ export function createDoseResponseTemplate(
19
+ options: CreateDoseResponseTemplateOptions
20
+ ): DoseResponseTemplate {
21
+ const compounds = Array.isArray(options.compounds)
22
+ ? options.compounds.map(normalizeCompound)
23
+ : Object.entries(options.compounds).map(([name, concentrations], index) => normalizeCompound({
24
+ id: compoundIdFromName(name, index),
25
+ name,
26
+ concentrations,
27
+ metadata: {},
28
+ }))
29
+
30
+ const layout = options.layout
31
+ ? getTemplateData<PlateMapTemplateData>(options.layout)
32
+ : undefined
33
+
34
+ const data: DoseResponseTemplateData = {
35
+ compounds,
36
+ unit: options.unit,
37
+ replicates: options.replicates ?? 1,
38
+ controls: (options.controls ?? []).map(normalizeControl),
39
+ layout,
40
+ metadata: options.metadata ?? {},
41
+ }
42
+ validateDoseResponseData(data)
43
+ return createTemplateEnvelope('dose-response', data, options.metadata ?? {})
44
+ }
@@ -0,0 +1,62 @@
1
+ import { createAssayMatrixTemplate } from './assayMatrixBuilder'
2
+ import { DEFAULT_SAMPLE_COLORS } from './builderDefaults'
3
+ import { idFromName } from './builderIdUtils'
4
+ import { createCalibrationCurveTemplate } from './calibrationCurveBuilder'
5
+ import { createPlateMapTemplate } from './plateMapBuilder'
6
+ import { createSampleSheetTemplate } from './sampleSheetBuilder'
7
+ import { presetSampleRecords } from './sampleNormalizers'
8
+ import { createTemplateCollection } from './templateEnvelopes'
9
+ import type {
10
+ CreateElisaAssayCollectionOptions,
11
+ TemplateCollectionEnvelope,
12
+ } from './types'
13
+
14
+ export function createElisaAssayCollection(
15
+ options: CreateElisaAssayCollectionOptions = {}
16
+ ): TemplateCollectionEnvelope {
17
+ const sampleRecords = presetSampleRecords(options.samples ?? ['Control', 'Treatment'])
18
+ const analyte = options.analyte ?? 'Analyte'
19
+ const responseUnit = options.responseUnit ?? 'OD450'
20
+ const plate = createPlateMapTemplate({
21
+ name: options.plateName ?? 'ELISA plate',
22
+ format: options.plateFormat ?? 96,
23
+ samples: [
24
+ { id: 'blank', name: 'Blank' },
25
+ { id: 'standard', name: 'Standards' },
26
+ { id: 'qc', name: 'QC' },
27
+ ...sampleRecords.map((sample, index) => ({
28
+ id: sample.sampleId,
29
+ name: sample.name ?? sample.sampleId,
30
+ color: DEFAULT_SAMPLE_COLORS[index % DEFAULT_SAMPLE_COLORS.length],
31
+ })),
32
+ ],
33
+ })
34
+ const sampleSheet = createSampleSheetTemplate({ samples: sampleRecords })
35
+ const calibrationCurve = createCalibrationCurveTemplate({
36
+ concentrations: options.standardConcentrations ?? [1000, 100, 10, 1],
37
+ analyte,
38
+ unit: options.unit ?? 'pg/mL',
39
+ responseUnit,
40
+ model: 'four-parameter-logistic',
41
+ })
42
+ const assayMatrix = createAssayMatrixTemplate({
43
+ samples: sampleRecords.map(sample => ({
44
+ sampleId: sample.sampleId,
45
+ name: sample.name,
46
+ group: sample.group,
47
+ metadata: sample.metadata ?? {},
48
+ })),
49
+ features: [{
50
+ id: idFromName(analyte, 'analyte'),
51
+ name: analyte,
52
+ type: 'protein',
53
+ unit: responseUnit,
54
+ metadata: {},
55
+ }],
56
+ metadata: { analyte, responseUnit },
57
+ })
58
+ return createTemplateCollection(
59
+ [plate, sampleSheet, calibrationCurve, assayMatrix],
60
+ { preset: 'elisa-assay', ...(options.metadata ?? {}) }
61
+ )
62
+ }
@@ -0,0 +1,41 @@
1
+ import { createAssayMatrixTemplate } from './assayMatrixBuilder'
2
+ import { createFlowCytometryPanelTemplate } from './flowCytometryPanelBuilder'
3
+ import { createSampleSheetTemplate } from './sampleSheetBuilder'
4
+ import { presetSampleRecords } from './sampleNormalizers'
5
+ import { createTemplateCollection } from './templateEnvelopes'
6
+ import type {
7
+ CreateFlowCytometryAssayCollectionOptions,
8
+ TemplateCollectionEnvelope,
9
+ } from './types'
10
+
11
+ export function createFlowCytometryAssayCollection(
12
+ options: CreateFlowCytometryAssayCollectionOptions = {}
13
+ ): TemplateCollectionEnvelope {
14
+ const sampleRecords = presetSampleRecords(options.samples ?? ['Control', 'Treatment'])
15
+ const markers = options.markers ?? ['CD3', 'CD4', 'CD8']
16
+ const sampleSheet = createSampleSheetTemplate({ samples: sampleRecords })
17
+ const flowPanel = createFlowCytometryPanelTemplate({
18
+ markers,
19
+ instrument: options.instrument ?? 'Flow cytometer',
20
+ includeDefaultControls: options.includeDefaultControls,
21
+ })
22
+ const assayMatrix = createAssayMatrixTemplate({
23
+ samples: sampleRecords.map(sample => ({
24
+ sampleId: sample.sampleId,
25
+ name: sample.name,
26
+ group: sample.group,
27
+ metadata: sample.metadata ?? {},
28
+ })),
29
+ features: options.features ?? flowPanel.data.markers.map(marker => ({
30
+ id: marker.id,
31
+ name: `${marker.marker}+ cells`,
32
+ type: 'cell',
33
+ unit: '%',
34
+ metadata: { marker: marker.marker, fluorophore: marker.fluorophore },
35
+ })),
36
+ })
37
+ return createTemplateCollection(
38
+ [sampleSheet, flowPanel, assayMatrix],
39
+ { preset: 'flow-cytometry-assay', ...(options.metadata ?? {}) }
40
+ )
41
+ }
@@ -0,0 +1,53 @@
1
+ import { idFromName } from './builderIdUtils'
2
+ import {
3
+ defaultFlowControls,
4
+ normalizeFlowControl,
5
+ normalizeFlowMarker,
6
+ } from './flowNormalizers'
7
+ import { createTemplateEnvelope } from './templateEnvelopes'
8
+ import { validateFlowCytometryPanelData } from './templateValidators'
9
+ import type {
10
+ CreateFlowCytometryPanelTemplateOptions,
11
+ FlowCytometryPanelTemplate,
12
+ FlowCytometryPanelTemplateData,
13
+ FlowPanelControl,
14
+ FlowPanelMarker,
15
+ } from './types'
16
+
17
+ export function createFlowCytometryPanelTemplate(
18
+ options: CreateFlowCytometryPanelTemplateOptions
19
+ ): FlowCytometryPanelTemplate {
20
+ const markers = options.markers.map((marker, index): FlowPanelMarker => {
21
+ if (typeof marker !== 'string') return normalizeFlowMarker(marker, index)
22
+ return {
23
+ id: idFromName(marker, `marker-${index + 1}`),
24
+ marker,
25
+ fluorophore: 'unassigned',
26
+ purpose: 'phenotype',
27
+ compensationRequired: true,
28
+ metadata: {},
29
+ }
30
+ })
31
+ const controls = options.controls === undefined
32
+ ? options.includeDefaultControls === false
33
+ ? []
34
+ : defaultFlowControls(markers)
35
+ : options.controls.map((control, index): FlowPanelControl => {
36
+ if (typeof control !== 'string') return normalizeFlowControl(control, index)
37
+ return {
38
+ id: idFromName(control, `control-${index + 1}`),
39
+ name: control,
40
+ kind: 'other',
41
+ required: true,
42
+ metadata: {},
43
+ }
44
+ })
45
+ const data: FlowCytometryPanelTemplateData = {
46
+ markers,
47
+ controls,
48
+ instrument: options.instrument,
49
+ metadata: options.metadata ?? {},
50
+ }
51
+ validateFlowCytometryPanelData(data)
52
+ return createTemplateEnvelope('flow-cytometry-panel', data, options.metadata ?? {})
53
+ }
@@ -0,0 +1,58 @@
1
+ import type {
2
+ FlowPanelControl,
3
+ FlowPanelMarker,
4
+ } from './types'
5
+ import { idFromName } from './builderIdUtils'
6
+
7
+ export function normalizeFlowMarker(
8
+ marker: Partial<FlowPanelMarker> & { marker: string },
9
+ index: number
10
+ ): FlowPanelMarker {
11
+ return {
12
+ id: marker.id ?? idFromName(marker.marker, `marker-${index + 1}`),
13
+ marker: marker.marker,
14
+ fluorophore: marker.fluorophore ?? 'unassigned',
15
+ detector: marker.detector,
16
+ clone: marker.clone,
17
+ reagentId: marker.reagentId,
18
+ purpose: marker.purpose ?? 'phenotype',
19
+ compensationRequired: marker.compensationRequired ?? true,
20
+ metadata: marker.metadata ?? {},
21
+ }
22
+ }
23
+
24
+ export function normalizeFlowControl(
25
+ control: Partial<FlowPanelControl> & { name: string },
26
+ index: number
27
+ ): FlowPanelControl {
28
+ return {
29
+ id: control.id ?? idFromName(control.name, `control-${index + 1}`),
30
+ name: control.name,
31
+ kind: control.kind ?? 'other',
32
+ markerId: control.markerId,
33
+ required: control.required ?? true,
34
+ metadata: control.metadata ?? {},
35
+ }
36
+ }
37
+
38
+ export function defaultFlowControls(markers: FlowPanelMarker[]): FlowPanelControl[] {
39
+ return [
40
+ {
41
+ id: 'unstained',
42
+ name: 'Unstained',
43
+ kind: 'unstained',
44
+ required: true,
45
+ metadata: {},
46
+ },
47
+ ...markers
48
+ .filter(marker => marker.compensationRequired)
49
+ .map(marker => ({
50
+ id: `${marker.id}-single-stain`,
51
+ name: `${marker.marker} single stain`,
52
+ kind: 'single-stain' as const,
53
+ markerId: marker.id,
54
+ required: true,
55
+ metadata: {},
56
+ })),
57
+ ]
58
+ }
@@ -0,0 +1,58 @@
1
+ import { getTemplateData } from './templateEnvelopes'
2
+ import { validateFlowCytometryPanelData } from './templateValidators'
3
+ import type {
4
+ FlowCytometryPanelTemplate,
5
+ FlowCytometryPanelTemplateData,
6
+ FlowPanelColumnsAdapterResult,
7
+ FlowPanelDataFrameAdapterResult,
8
+ FlowPanelRowsAdapterResult,
9
+ } from './types'
10
+
11
+ export function toFlowPanelRows(
12
+ template: FlowCytometryPanelTemplate | FlowCytometryPanelTemplateData
13
+ ): FlowPanelRowsAdapterResult {
14
+ const data = getTemplateData(template, 'flow-cytometry-panel')
15
+ validateFlowCytometryPanelData(data)
16
+ return data.markers.map(marker => {
17
+ const markerControls = data.controls
18
+ .filter(control => control.markerId === marker.id)
19
+ .map(control => control.name)
20
+ .join(', ')
21
+ return {
22
+ id: marker.id,
23
+ marker: marker.marker,
24
+ fluorophore: marker.fluorophore,
25
+ detector: marker.detector,
26
+ clone: marker.clone,
27
+ reagentId: marker.reagentId,
28
+ purpose: marker.purpose,
29
+ compensationRequired: marker.compensationRequired,
30
+ controls: markerControls,
31
+ instrument: data.instrument,
32
+ ...(marker.metadata ?? {}),
33
+ }
34
+ })
35
+ }
36
+
37
+ export function toFlowPanelColumns(): FlowPanelColumnsAdapterResult {
38
+ return [
39
+ { key: 'marker', label: 'Marker', sortable: true },
40
+ { key: 'fluorophore', label: 'Fluorophore', sortable: true },
41
+ { key: 'detector', label: 'Detector', sortable: true },
42
+ { key: 'clone', label: 'Clone', sortable: true },
43
+ { key: 'purpose', label: 'Purpose', sortable: true },
44
+ { key: 'compensationRequired', label: 'Compensation', sortable: true },
45
+ { key: 'controls', label: 'Controls' },
46
+ { key: 'instrument', label: 'Instrument', sortable: true },
47
+ ]
48
+ }
49
+
50
+ export function toFlowPanelDataFrame(
51
+ template: FlowCytometryPanelTemplate | FlowCytometryPanelTemplateData
52
+ ): FlowPanelDataFrameAdapterResult {
53
+ return {
54
+ data: toFlowPanelRows(template),
55
+ columns: toFlowPanelColumns(),
56
+ rowKey: 'id',
57
+ }
58
+ }
@@ -0,0 +1,94 @@
1
+ import type {
2
+ ProtocolStepStatus,
3
+ ProtocolStepType,
4
+ ScheduleEventStatus,
5
+ } from '../types'
6
+ import type {
7
+ InstrumentRunItem,
8
+ InstrumentRunItemKind,
9
+ InstrumentRunStatus,
10
+ } from './types'
11
+
12
+ export function instrumentRunStepType(kind: InstrumentRunItemKind): ProtocolStepType {
13
+ switch (kind) {
14
+ case 'wash':
15
+ return 'wash'
16
+ case 'blank':
17
+ case 'qc':
18
+ case 'standard':
19
+ case 'calibration':
20
+ case 'sample':
21
+ return 'measurement'
22
+ default:
23
+ return 'custom'
24
+ }
25
+ }
26
+
27
+ export function instrumentRunStepStatus(status: InstrumentRunStatus): ProtocolStepStatus {
28
+ switch (status) {
29
+ case 'running':
30
+ return 'in_progress'
31
+ case 'completed':
32
+ return 'completed'
33
+ case 'failed':
34
+ return 'failed'
35
+ case 'skipped':
36
+ return 'skipped'
37
+ case 'planned':
38
+ case 'queued':
39
+ default:
40
+ return 'pending'
41
+ }
42
+ }
43
+
44
+ export function instrumentRunScheduleStatus(status: InstrumentRunStatus): ScheduleEventStatus {
45
+ switch (status) {
46
+ case 'running':
47
+ return 'in-progress'
48
+ case 'completed':
49
+ return 'confirmed'
50
+ case 'failed':
51
+ case 'skipped':
52
+ return 'cancelled'
53
+ case 'planned':
54
+ case 'queued':
55
+ default:
56
+ return 'pending'
57
+ }
58
+ }
59
+
60
+ export function instrumentRunScheduleColor(kind: InstrumentRunItem['kind']): string {
61
+ switch (kind) {
62
+ case 'blank':
63
+ return '#64748b'
64
+ case 'qc':
65
+ return '#8b5cf6'
66
+ case 'standard':
67
+ case 'calibration':
68
+ return '#f59e0b'
69
+ case 'wash':
70
+ return '#06b6d4'
71
+ case 'sample':
72
+ return '#2563eb'
73
+ default:
74
+ return '#475569'
75
+ }
76
+ }
77
+
78
+ export function dateFromMetadata(
79
+ metadata: Record<string, unknown> | undefined,
80
+ keys: readonly string[]
81
+ ): Date | undefined {
82
+ if (!metadata) return undefined
83
+ for (const key of keys) {
84
+ const value = metadata[key]
85
+ if (!(typeof value === 'string' || value instanceof Date)) continue
86
+ const date = new Date(value)
87
+ if (!Number.isNaN(date.getTime())) return date
88
+ }
89
+ return undefined
90
+ }
91
+
92
+ export function addMinutes(date: Date, minutes: number): Date {
93
+ return new Date(date.getTime() + minutes * 60_000)
94
+ }