@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,69 @@
1
+ import { getTemplateData } from './templateEnvelopes'
2
+ import { validateProtocolStepsData } from './templateValidators'
3
+ import type {
4
+ ProtocolColumnsAdapterResult,
5
+ ProtocolDataFrameAdapterResult,
6
+ ProtocolRowsAdapterResult,
7
+ ProtocolStepsAdapterResult,
8
+ ProtocolStepsTemplate,
9
+ ProtocolStepsTemplateData,
10
+ } from './types'
11
+
12
+ export function toProtocolSteps(
13
+ template: ProtocolStepsTemplate | ProtocolStepsTemplateData
14
+ ): ProtocolStepsAdapterResult {
15
+ const data = getTemplateData(template, 'protocol-steps')
16
+ validateProtocolStepsData(data)
17
+ return [...data.steps]
18
+ .sort((a, b) => a.order - b.order)
19
+ .map(step => ({
20
+ id: step.id,
21
+ type: step.type,
22
+ name: step.name,
23
+ description: step.description,
24
+ duration: step.duration,
25
+ status: step.status,
26
+ parameters: step.parameters,
27
+ order: step.order,
28
+ }))
29
+ }
30
+
31
+ export function toProtocolRows(
32
+ template: ProtocolStepsTemplate | ProtocolStepsTemplateData
33
+ ): ProtocolRowsAdapterResult {
34
+ const data = getTemplateData(template, 'protocol-steps')
35
+ validateProtocolStepsData(data)
36
+ return [...data.steps]
37
+ .sort((a, b) => a.order - b.order)
38
+ .map(step => ({
39
+ id: step.id,
40
+ order: step.order,
41
+ type: step.type,
42
+ name: step.name,
43
+ description: step.description,
44
+ duration: step.duration,
45
+ status: step.status,
46
+ ...(step.metadata ?? {}),
47
+ }))
48
+ }
49
+
50
+ export function toProtocolColumns(): ProtocolColumnsAdapterResult {
51
+ return [
52
+ { key: 'order', label: 'Order', sortable: true, align: 'right' },
53
+ { key: 'name', label: 'Name', sortable: true },
54
+ { key: 'type', label: 'Type', sortable: true },
55
+ { key: 'duration', label: 'Duration', sortable: true, align: 'right' },
56
+ { key: 'status', label: 'Status', sortable: true },
57
+ { key: 'description', label: 'Description', sortable: true },
58
+ ]
59
+ }
60
+
61
+ export function toProtocolDataFrame(
62
+ template: ProtocolStepsTemplate | ProtocolStepsTemplateData
63
+ ): ProtocolDataFrameAdapterResult {
64
+ return {
65
+ data: toProtocolRows(template),
66
+ columns: toProtocolColumns(),
67
+ rowKey: 'id',
68
+ }
69
+ }
@@ -0,0 +1,37 @@
1
+ import type {
2
+ ProtocolStepRecord,
3
+ TimeCourseCondition,
4
+ TimePoint,
5
+ } from './types'
6
+ import { idFromName } from './builderIdUtils'
7
+
8
+ export function normalizeTimePoint(timepoint: TimePoint): TimePoint {
9
+ return {
10
+ ...timepoint,
11
+ metadata: timepoint.metadata ?? {},
12
+ }
13
+ }
14
+
15
+ export function normalizeCondition(condition: TimeCourseCondition): TimeCourseCondition {
16
+ return {
17
+ ...condition,
18
+ metadata: condition.metadata ?? {},
19
+ }
20
+ }
21
+
22
+ export function normalizeProtocolStep(
23
+ step: Partial<ProtocolStepRecord> & { name: string },
24
+ index: number
25
+ ): ProtocolStepRecord {
26
+ return {
27
+ id: step.id ?? idFromName(step.name, `step-${index + 1}`),
28
+ type: step.type ?? 'custom',
29
+ name: step.name,
30
+ description: step.description,
31
+ duration: step.duration,
32
+ status: step.status ?? 'pending',
33
+ parameters: step.parameters ?? {},
34
+ order: step.order ?? index,
35
+ metadata: step.metadata ?? {},
36
+ }
37
+ }
@@ -0,0 +1,36 @@
1
+ import { idFromName } from './builderIdUtils'
2
+ import { normalizeProtocolStep } from './protocolNormalizers'
3
+ import { createTemplateEnvelope } from './templateEnvelopes'
4
+ import { validateProtocolStepsData } from './templateValidators'
5
+ import type {
6
+ CreateProtocolStepsTemplateOptions,
7
+ ProtocolStepRecord,
8
+ ProtocolStepsTemplate,
9
+ ProtocolStepsTemplateData,
10
+ } from './types'
11
+
12
+ export function createProtocolStepsTemplate(
13
+ options: CreateProtocolStepsTemplateOptions
14
+ ): ProtocolStepsTemplate {
15
+ const steps = options.steps.map((step, index): ProtocolStepRecord => {
16
+ if (typeof step === 'string') {
17
+ return {
18
+ id: idFromName(step, `step-${index + 1}`),
19
+ type: 'custom',
20
+ name: step,
21
+ status: 'pending',
22
+ parameters: {},
23
+ order: index,
24
+ metadata: {},
25
+ }
26
+ }
27
+ return normalizeProtocolStep(step, index)
28
+ }).sort((a, b) => a.order - b.order)
29
+
30
+ const data: ProtocolStepsTemplateData = {
31
+ steps,
32
+ metadata: options.metadata ?? {},
33
+ }
34
+ validateProtocolStepsData(data)
35
+ return createTemplateEnvelope('protocol-steps', data, options.metadata ?? {})
36
+ }
@@ -0,0 +1,104 @@
1
+ import type { Well } from '../types'
2
+ import { getTemplateData } from './templateEnvelopes'
3
+ import { validateQpcrPlateData } from './templateValidators'
4
+ import type {
5
+ QpcrColumnsAdapterResult,
6
+ QpcrDataFrameAdapterResult,
7
+ QpcrPlateTemplate,
8
+ QpcrPlateTemplateData,
9
+ QpcrRowsAdapterResult,
10
+ QpcrWellMapAdapterResult,
11
+ } from './types'
12
+
13
+ export function toQpcrRows(
14
+ template: QpcrPlateTemplate | QpcrPlateTemplateData
15
+ ): QpcrRowsAdapterResult {
16
+ const data = getTemplateData(template, 'qpcr-plate')
17
+ validateQpcrPlateData(data)
18
+ const samples = new Map(data.samples.map(sample => [sample.sampleId, sample]))
19
+ const targets = new Map(data.targets.map(target => [target.id, target]))
20
+
21
+ return data.reactions.map(reaction => {
22
+ const sample = reaction.sampleId ? samples.get(reaction.sampleId) : undefined
23
+ const target = targets.get(reaction.targetId)
24
+ return {
25
+ id: reaction.id,
26
+ wellId: reaction.wellId,
27
+ sampleId: reaction.sampleId,
28
+ sample: sample?.name ?? reaction.sampleId ?? reaction.controlKind,
29
+ group: sample?.group,
30
+ targetId: reaction.targetId,
31
+ target: target?.name ?? reaction.targetId,
32
+ replicate: reaction.replicate,
33
+ controlKind: reaction.controlKind,
34
+ cq: reaction.cq ?? null,
35
+ quantity: reaction.quantity ?? null,
36
+ flags: reaction.flags.join(', '),
37
+ instrument: data.instrument,
38
+ chemistry: data.chemistry,
39
+ ...(reaction.metadata ?? {}),
40
+ }
41
+ })
42
+ }
43
+
44
+ export function toQpcrColumns(): QpcrColumnsAdapterResult {
45
+ return [
46
+ { key: 'wellId', label: 'Well', sortable: true },
47
+ { key: 'sample', label: 'Sample', sortable: true },
48
+ { key: 'group', label: 'Group', sortable: true },
49
+ { key: 'target', label: 'Target', sortable: true },
50
+ { key: 'replicate', label: 'Replicate', sortable: true, align: 'right' },
51
+ { key: 'controlKind', label: 'Control', sortable: true },
52
+ { key: 'cq', label: 'Cq', sortable: true, align: 'right' },
53
+ { key: 'quantity', label: 'Quantity', sortable: true, align: 'right' },
54
+ { key: 'flags', label: 'Flags' },
55
+ ]
56
+ }
57
+
58
+ export function toQpcrDataFrame(
59
+ template: QpcrPlateTemplate | QpcrPlateTemplateData
60
+ ): QpcrDataFrameAdapterResult {
61
+ return {
62
+ data: toQpcrRows(template),
63
+ columns: toQpcrColumns(),
64
+ rowKey: 'id',
65
+ }
66
+ }
67
+
68
+ export function toQpcrWellPlateWells(
69
+ template: QpcrPlateTemplate | QpcrPlateTemplateData
70
+ ): QpcrWellMapAdapterResult {
71
+ const data = getTemplateData(template, 'qpcr-plate')
72
+ validateQpcrPlateData(data)
73
+ return Object.fromEntries(data.reactions.map(reaction => {
74
+ const [row, col] = wellPositionFromId(reaction.wellId)
75
+ return [
76
+ reaction.wellId,
77
+ {
78
+ id: reaction.wellId,
79
+ row,
80
+ col,
81
+ state: 'filled' as const,
82
+ sampleType: reaction.sampleId ?? reaction.controlKind,
83
+ value: reaction.cq ?? undefined,
84
+ metadata: {
85
+ reactionId: reaction.id,
86
+ sampleId: reaction.sampleId,
87
+ targetId: reaction.targetId,
88
+ replicate: reaction.replicate,
89
+ controlKind: reaction.controlKind,
90
+ },
91
+ } satisfies Well,
92
+ ]
93
+ }))
94
+ }
95
+
96
+ function wellPositionFromId(wellId: string): [number, number] {
97
+ const match = /^([A-Z]+)([1-9][0-9]*)$/.exec(wellId.toUpperCase())
98
+ if (!match) {
99
+ throw new Error(`Invalid well id '${wellId}'.`)
100
+ }
101
+ const row = [...match[1]].reduce((value, char) => value * 26 + char.charCodeAt(0) - 64, 0) - 1
102
+ const col = Number(match[2]) - 1
103
+ return [row, col]
104
+ }
@@ -0,0 +1,33 @@
1
+ import { createQpcrPlateTemplate } from './qpcrPlateBuilder'
2
+ import { createSampleSheetTemplate } from './sampleSheetBuilder'
3
+ import { presetSampleRecords } from './sampleNormalizers'
4
+ import { createTemplateCollection } from './templateEnvelopes'
5
+ import type {
6
+ CreateQpcrExpressionCollectionOptions,
7
+ TemplateCollectionEnvelope,
8
+ } from './types'
9
+
10
+ export function createQpcrExpressionCollection(
11
+ options: CreateQpcrExpressionCollectionOptions = {}
12
+ ): TemplateCollectionEnvelope {
13
+ const sampleRecords = presetSampleRecords(options.samples ?? ['Control', 'Treatment'])
14
+ const sampleSheet = createSampleSheetTemplate({ samples: sampleRecords })
15
+ const qpcr = createQpcrPlateTemplate({
16
+ samples: sampleRecords.map(sample => ({
17
+ sampleId: sample.sampleId,
18
+ name: sample.name,
19
+ group: sample.group,
20
+ metadata: sample.metadata ?? {},
21
+ })),
22
+ targets: options.targets ?? ['ACTB', 'GAPDH'],
23
+ chemistry: options.chemistry,
24
+ format: options.format,
25
+ includeNoTemplateControls: options.includeNoTemplateControls,
26
+ replicates: options.replicates ?? 3,
27
+ instrument: options.instrument,
28
+ })
29
+ return createTemplateCollection(
30
+ [sampleSheet, qpcr],
31
+ { preset: 'qpcr-expression', ...(options.metadata ?? {}) }
32
+ )
33
+ }
@@ -0,0 +1,96 @@
1
+ import { idFromName } from './builderIdUtils'
2
+ import { wellIdsForFormat } from './plateGeometry'
3
+ import {
4
+ normalizeQpcrSample,
5
+ normalizeQpcrTarget,
6
+ } from './runNormalizers'
7
+ import { createTemplateEnvelope } from './templateEnvelopes'
8
+ import { validateQpcrPlateData } from './templateValidators'
9
+ import type {
10
+ CreateQpcrPlateTemplateOptions,
11
+ QpcrPlateTemplate,
12
+ QpcrPlateTemplateData,
13
+ QpcrReaction,
14
+ QpcrSample,
15
+ QpcrTarget,
16
+ } from './types'
17
+
18
+ export function createQpcrPlateTemplate(
19
+ options: CreateQpcrPlateTemplateOptions
20
+ ): QpcrPlateTemplate {
21
+ const format = options.format ?? 96
22
+ const replicates = options.replicates ?? 3
23
+ if (replicates < 1) {
24
+ throw new Error('qPCR replicates must be at least 1.')
25
+ }
26
+
27
+ const samples = options.samples.map((sample, index): QpcrSample => {
28
+ if (typeof sample !== 'string') return normalizeQpcrSample(sample)
29
+ return {
30
+ sampleId: idFromName(sample, `sample-${index + 1}`),
31
+ name: sample,
32
+ metadata: {},
33
+ }
34
+ })
35
+ const targets = options.targets.map((target, index): QpcrTarget => {
36
+ if (typeof target !== 'string') return normalizeQpcrTarget(target)
37
+ return {
38
+ id: idFromName(target, `target-${index + 1}`),
39
+ name: target,
40
+ metadata: {},
41
+ }
42
+ })
43
+ const wells = wellIdsForFormat(format)
44
+ let wellIndex = 0
45
+ const reactions: QpcrReaction[] = []
46
+
47
+ for (const sample of samples) {
48
+ for (const target of targets) {
49
+ for (let replicate = 1; replicate <= replicates; replicate += 1) {
50
+ const wellId = wells[wellIndex]
51
+ if (!wellId) throw new Error(`qPCR plate format ${format} does not have enough wells.`)
52
+ reactions.push({
53
+ id: `${sample.sampleId}-${target.id}-r${replicate}`,
54
+ wellId,
55
+ sampleId: sample.sampleId,
56
+ targetId: target.id,
57
+ replicate,
58
+ controlKind: 'sample',
59
+ flags: [],
60
+ metadata: {},
61
+ })
62
+ wellIndex += 1
63
+ }
64
+ }
65
+ }
66
+
67
+ if (options.includeNoTemplateControls !== false) {
68
+ for (const target of targets) {
69
+ const wellId = wells[wellIndex]
70
+ if (!wellId) throw new Error(`qPCR plate format ${format} does not have enough wells.`)
71
+ reactions.push({
72
+ id: `ntc-${target.id}`,
73
+ wellId,
74
+ targetId: target.id,
75
+ replicate: 1,
76
+ controlKind: 'no-template',
77
+ flags: [],
78
+ metadata: {},
79
+ })
80
+ wellIndex += 1
81
+ }
82
+ }
83
+
84
+ const data: QpcrPlateTemplateData = {
85
+ plateId: 'qpcr-plate-1',
86
+ format,
87
+ samples,
88
+ targets,
89
+ reactions,
90
+ instrument: options.instrument,
91
+ chemistry: options.chemistry ?? 'sybr',
92
+ metadata: options.metadata ?? {},
93
+ }
94
+ validateQpcrPlateData(data)
95
+ return createTemplateEnvelope('qpcr-plate', data, options.metadata ?? {})
96
+ }
@@ -0,0 +1,77 @@
1
+ import { getTemplateData } from './templateEnvelopes'
2
+ import { validateReagentListData } from './templateValidators'
3
+ import type {
4
+ ReagentColumnsAdapterResult,
5
+ ReagentDataFrameAdapterResult,
6
+ ReagentListItemsAdapterResult,
7
+ ReagentListTemplate,
8
+ ReagentListTemplateData,
9
+ ReagentRowsAdapterResult,
10
+ } from './types'
11
+
12
+ export function toReagentListItems(
13
+ template: ReagentListTemplate | ReagentListTemplateData
14
+ ): ReagentListItemsAdapterResult {
15
+ const data = getTemplateData(template, 'reagent-list')
16
+ validateReagentListData(data)
17
+ return data.reagents.map(reagent => ({
18
+ id: reagent.id,
19
+ name: reagent.name,
20
+ catalogNumber: reagent.catalogNumber,
21
+ lotNumber: reagent.lotNumber,
22
+ expiryDate: reagent.expiryDate,
23
+ storageCondition: reagent.storageCondition,
24
+ location: reagent.location,
25
+ stockLevel: reagent.stockLevel,
26
+ stockUnit: reagent.stockUnit,
27
+ supplier: reagent.supplier,
28
+ url: reagent.url,
29
+ }))
30
+ }
31
+
32
+ export function toReagentRows(
33
+ template: ReagentListTemplate | ReagentListTemplateData
34
+ ): ReagentRowsAdapterResult {
35
+ const data = getTemplateData(template, 'reagent-list')
36
+ validateReagentListData(data)
37
+ return data.reagents.map(reagent => ({
38
+ id: reagent.id,
39
+ name: reagent.name,
40
+ kind: reagent.kind,
41
+ catalogNumber: reagent.catalogNumber,
42
+ lotNumber: reagent.lotNumber,
43
+ expiryDate: reagent.expiryDate,
44
+ storageCondition: reagent.storageCondition,
45
+ location: reagent.location,
46
+ stockLevel: reagent.stockLevel,
47
+ stockUnit: reagent.stockUnit,
48
+ supplier: reagent.supplier,
49
+ concentration: reagent.concentration,
50
+ ...(reagent.metadata ?? {}),
51
+ }))
52
+ }
53
+
54
+ export function toReagentColumns(): ReagentColumnsAdapterResult {
55
+ return [
56
+ { key: 'name', label: 'Name', sortable: true },
57
+ { key: 'kind', label: 'Kind', sortable: true },
58
+ { key: 'catalogNumber', label: 'Catalog', sortable: true },
59
+ { key: 'lotNumber', label: 'Lot', sortable: true },
60
+ { key: 'expiryDate', label: 'Expiry', sortable: true },
61
+ { key: 'storageCondition', label: 'Storage', sortable: true },
62
+ { key: 'location', label: 'Location', sortable: true },
63
+ { key: 'stockLevel', label: 'Stock', sortable: true, align: 'right' },
64
+ { key: 'stockUnit', label: 'Unit', sortable: true },
65
+ { key: 'supplier', label: 'Supplier', sortable: true },
66
+ ]
67
+ }
68
+
69
+ export function toReagentDataFrame(
70
+ template: ReagentListTemplate | ReagentListTemplateData
71
+ ): ReagentDataFrameAdapterResult {
72
+ return {
73
+ data: toReagentRows(template),
74
+ columns: toReagentColumns(),
75
+ rowKey: 'id',
76
+ }
77
+ }
@@ -0,0 +1,30 @@
1
+ import { normalizeReagent } from './assayNormalizers'
2
+ import { idFromName } from './builderIdUtils'
3
+ import { createTemplateEnvelope } from './templateEnvelopes'
4
+ import { validateReagentListData } from './templateValidators'
5
+ import type {
6
+ CreateReagentListTemplateOptions,
7
+ ReagentListTemplate,
8
+ ReagentListTemplateData,
9
+ ReagentRecord,
10
+ } from './types'
11
+
12
+ export function createReagentListTemplate(
13
+ options: CreateReagentListTemplateOptions
14
+ ): ReagentListTemplate {
15
+ const reagents = options.reagents.map((reagent, index): ReagentRecord => {
16
+ if (typeof reagent !== 'string') return normalizeReagent(reagent)
17
+ return {
18
+ id: idFromName(reagent, `reagent-${index + 1}`),
19
+ name: reagent,
20
+ kind: 'reagent',
21
+ metadata: {},
22
+ }
23
+ })
24
+ const data: ReagentListTemplateData = {
25
+ reagents,
26
+ metadata: options.metadata ?? {},
27
+ }
28
+ validateReagentListData(data)
29
+ return createTemplateEnvelope('reagent-list', data, options.metadata ?? {})
30
+ }
@@ -0,0 +1,63 @@
1
+ import type {
2
+ InstrumentMethod,
3
+ InstrumentRunItem,
4
+ QpcrSample,
5
+ QpcrTarget,
6
+ } from './types'
7
+ import { idFromName } from './builderIdUtils'
8
+
9
+ export function normalizeInstrumentMethod(
10
+ method: Partial<InstrumentMethod> & { name?: string },
11
+ fallbackInstrument?: string
12
+ ): InstrumentMethod {
13
+ const name = method.name || method.id || 'Default method'
14
+ return {
15
+ id: method.id || idFromName(name, 'method-1'),
16
+ name,
17
+ instrument: method.instrument ?? fallbackInstrument,
18
+ acquisitionMode: method.acquisitionMode,
19
+ metadata: method.metadata ?? {},
20
+ }
21
+ }
22
+
23
+ export function normalizeInstrumentRunItem(
24
+ item: Partial<InstrumentRunItem> & { name?: string; sampleId?: string },
25
+ defaultMethodId: string,
26
+ fallbackOrder: number,
27
+ index: number
28
+ ): InstrumentRunItem {
29
+ const kind = item.kind ?? 'sample'
30
+ const sampleId = item.sampleId || (kind === 'sample'
31
+ ? idFromName(item.name ?? `Sample ${index + 1}`, `sample-${index + 1}`)
32
+ : undefined)
33
+ const label = item.name ?? sampleId ?? kind
34
+ return {
35
+ id: item.id || idFromName(label, `${kind}-${index + 1}`),
36
+ order: item.order ?? fallbackOrder,
37
+ kind,
38
+ sampleId,
39
+ name: item.name,
40
+ methodId: item.methodId || defaultMethodId,
41
+ vial: item.vial,
42
+ plateId: item.plateId,
43
+ wellId: item.wellId,
44
+ injectionVolume: item.injectionVolume,
45
+ expectedDurationMin: item.expectedDurationMin,
46
+ status: item.status ?? 'planned',
47
+ metadata: item.metadata ?? {},
48
+ }
49
+ }
50
+
51
+ export function normalizeQpcrSample(sample: QpcrSample): QpcrSample {
52
+ return {
53
+ ...sample,
54
+ metadata: sample.metadata ?? {},
55
+ }
56
+ }
57
+
58
+ export function normalizeQpcrTarget(target: QpcrTarget): QpcrTarget {
59
+ return {
60
+ ...target,
61
+ metadata: target.metadata ?? {},
62
+ }
63
+ }
@@ -0,0 +1,58 @@
1
+ import type {
2
+ SamplePrepStep,
3
+ SamplePrepStepInput,
4
+ SampleRecord,
5
+ } from './types'
6
+ import {
7
+ idFromName,
8
+ sampleIdFromName,
9
+ } from './builderIdUtils'
10
+
11
+ export function normalizeSampleRecord(sample: SampleRecord): SampleRecord {
12
+ return {
13
+ ...sample,
14
+ metadata: sample.metadata ?? {},
15
+ }
16
+ }
17
+
18
+ export function presetSampleRecords(samples: Array<string | SampleRecord>): SampleRecord[] {
19
+ return samples.map((sample, index) => {
20
+ if (typeof sample !== 'string') return normalizeSampleRecord(sample)
21
+ return {
22
+ sampleId: sampleIdFromName(sample, index),
23
+ name: sample,
24
+ metadata: {},
25
+ }
26
+ })
27
+ }
28
+
29
+ export function normalizeSamplePrepStep(
30
+ step: SamplePrepStepInput,
31
+ prepType: SamplePrepStep['type'],
32
+ volumeUnit: string,
33
+ order: number,
34
+ index: number
35
+ ): SamplePrepStep {
36
+ const type = step.type ?? prepType
37
+ const sourceSampleId = step.sourceSampleId
38
+ const name = step.name || `Prepare ${sourceSampleId ?? index + 1}`
39
+ return {
40
+ id: step.id || idFromName(name, `${type}-${index + 1}`),
41
+ order: step.order ?? order,
42
+ type,
43
+ name,
44
+ sourceSampleId,
45
+ sourcePlateId: step.sourcePlateId,
46
+ sourceWellId: step.sourceWellId,
47
+ destinationSampleId: step.destinationSampleId,
48
+ destinationPlateId: step.destinationPlateId,
49
+ destinationWellId: step.destinationWellId,
50
+ reagentId: step.reagentId,
51
+ inputVolume: step.inputVolume,
52
+ outputVolume: step.outputVolume,
53
+ volumeUnit: step.volumeUnit || volumeUnit,
54
+ durationMin: step.durationMin,
55
+ status: step.status ?? 'planned',
56
+ metadata: step.metadata ?? {},
57
+ }
58
+ }
@@ -0,0 +1,63 @@
1
+ import { getTemplateData } from './templateEnvelopes'
2
+ import { validateSamplePrepData } from './templateValidators'
3
+ import type {
4
+ SamplePrepColumnsAdapterResult,
5
+ SamplePrepDataFrameAdapterResult,
6
+ SamplePrepRowsAdapterResult,
7
+ SamplePrepTemplate,
8
+ SamplePrepTemplateData,
9
+ } from './types'
10
+
11
+ export function toSamplePrepRows(
12
+ template: SamplePrepTemplate | SamplePrepTemplateData
13
+ ): SamplePrepRowsAdapterResult {
14
+ const data = getTemplateData(template, 'sample-prep')
15
+ validateSamplePrepData(data)
16
+ return [...data.steps]
17
+ .sort((a, b) => a.order - b.order)
18
+ .map(step => ({
19
+ id: step.id,
20
+ order: step.order,
21
+ type: step.type,
22
+ name: step.name,
23
+ sourceSampleId: step.sourceSampleId,
24
+ sourceWellId: step.sourceWellId,
25
+ destinationSampleId: step.destinationSampleId,
26
+ destinationWellId: step.destinationWellId,
27
+ reagentId: step.reagentId,
28
+ inputVolume: step.inputVolume,
29
+ outputVolume: step.outputVolume,
30
+ volumeUnit: step.volumeUnit,
31
+ durationMin: step.durationMin,
32
+ status: step.status,
33
+ protocolName: data.protocolName,
34
+ ...(step.metadata ?? {}),
35
+ }))
36
+ }
37
+
38
+ export function toSamplePrepColumns(): SamplePrepColumnsAdapterResult {
39
+ return [
40
+ { key: 'order', label: 'Order', sortable: true, align: 'right' },
41
+ { key: 'type', label: 'Type', sortable: true },
42
+ { key: 'name', label: 'Step', sortable: true },
43
+ { key: 'sourceSampleId', label: 'Source', sortable: true },
44
+ { key: 'sourceWellId', label: 'Source well', sortable: true },
45
+ { key: 'destinationSampleId', label: 'Destination', sortable: true },
46
+ { key: 'destinationWellId', label: 'Destination well', sortable: true },
47
+ { key: 'reagentId', label: 'Reagent', sortable: true },
48
+ { key: 'inputVolume', label: 'Input', sortable: true, align: 'right' },
49
+ { key: 'outputVolume', label: 'Output', sortable: true, align: 'right' },
50
+ { key: 'volumeUnit', label: 'Unit', sortable: true },
51
+ { key: 'status', label: 'Status', sortable: true },
52
+ ]
53
+ }
54
+
55
+ export function toSamplePrepDataFrame(
56
+ template: SamplePrepTemplate | SamplePrepTemplateData
57
+ ): SamplePrepDataFrameAdapterResult {
58
+ return {
59
+ data: toSamplePrepRows(template),
60
+ columns: toSamplePrepColumns(),
61
+ rowKey: 'id',
62
+ }
63
+ }