@sjcrh/proteinpaint-server 2.44.0 → 2.46.1

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 (82) hide show
  1. package/dataset/clinvar.hg19.js +53 -52
  2. package/dataset/clinvar.hg38.js +74 -73
  3. package/dataset/clinvar.js +164 -47
  4. package/dataset/termdb.test.js +257 -0
  5. package/genome/CriGri.js +1859 -27
  6. package/genome/cgc.js +743 -7
  7. package/genome/danRer10.js +1108 -46
  8. package/genome/dm3.js +71 -44
  9. package/genome/dm6.js +1926 -45
  10. package/genome/galGal5.js +23522 -46
  11. package/genome/galGal6.js +512 -46
  12. package/genome/hg19.js +293 -198
  13. package/genome/hg38.js +472 -105
  14. package/genome/hg38.test.js +406 -40
  15. package/genome/hgvirus.js +45 -20
  16. package/genome/mm10.js +135 -67
  17. package/genome/mm9.js +116 -79
  18. package/genome/rn6.js +1002 -47
  19. package/package.json +31 -35
  20. package/routes/_template_.js +30 -0
  21. package/routes/burden.js +149 -0
  22. package/routes/dataset.js +266 -0
  23. package/routes/dsdata.js +127 -0
  24. package/routes/gdc.maf.js +120 -0
  25. package/routes/gdc.mafBuild.js +106 -0
  26. package/routes/gdc.topMutatedGenes.js +465 -0
  27. package/routes/gene2canonicalisoform.js +41 -0
  28. package/routes/genelookup.js +52 -0
  29. package/routes/genomes.js +144 -0
  30. package/routes/healthcheck.js +30 -0
  31. package/routes/hicdata.js +98 -0
  32. package/routes/hicstat.js +55 -0
  33. package/routes/isoformlst.js +57 -0
  34. package/routes/ntseq.js +43 -0
  35. package/routes/pdomain.js +61 -0
  36. package/routes/snp.js +107 -0
  37. package/routes/termdb.categories.js +209 -0
  38. package/routes/termdb.cluster.js +228 -0
  39. package/routes/termdb.cohort.summary.js +38 -0
  40. package/routes/termdb.cohorts.js +49 -0
  41. package/routes/termdb.config.js +202 -0
  42. package/routes/termdb.getdescrstats.js +102 -0
  43. package/routes/termdb.getnumericcategories.js +92 -0
  44. package/routes/termdb.getpercentile.js +108 -0
  45. package/routes/termdb.getrootterm.js +65 -0
  46. package/routes/termdb.gettermchildren.js +67 -0
  47. package/routes/termdb.singleSampleMutation.js +80 -0
  48. package/routes/termdb.singlecellData.js +46 -0
  49. package/routes/termdb.singlecellSamples.js +160 -0
  50. package/routes/termdb.termsbyids.js +59 -0
  51. package/routes/termdb.topVariablyExpressedGenes.js +171 -0
  52. package/routes/termdb.violin.js +77 -0
  53. package/src/app.js +41500 -0
  54. package/src/serverconfig.js +14 -8
  55. package/start.js +3 -3
  56. package/routes/README.md +0 -84
  57. package/routes/burden.ts +0 -143
  58. package/routes/gdc.maf.ts +0 -195
  59. package/routes/gdc.mafBuild.ts +0 -114
  60. package/routes/gdc.topMutatedGenes.ts +0 -586
  61. package/routes/genelookup.ts +0 -50
  62. package/routes/healthcheck.ts +0 -29
  63. package/routes/hicdata.ts +0 -111
  64. package/routes/hicstat.ts +0 -55
  65. package/routes/termdb.categories.ts +0 -245
  66. package/routes/termdb.cluster.ts +0 -248
  67. package/routes/termdb.getdescrstats.ts +0 -102
  68. package/routes/termdb.getnumericcategories.ts +0 -99
  69. package/routes/termdb.getpercentile.ts +0 -118
  70. package/routes/termdb.getrootterm.ts +0 -73
  71. package/routes/termdb.gettermchildren.ts +0 -82
  72. package/routes/termdb.singleSampleMutation.ts +0 -87
  73. package/routes/termdb.singlecellData.ts +0 -49
  74. package/routes/termdb.singlecellSamples.ts +0 -175
  75. package/routes/termdb.termsbyids.ts +0 -63
  76. package/routes/termdb.topVariablyExpressedGenes.ts +0 -214
  77. package/routes/termdb.violin.ts +0 -77
  78. package/server.js +0 -2
  79. package/server.js.map +0 -1
  80. package/shared/common.js +0 -1080
  81. package/shared/termdb.initbinconfig.js +0 -96
  82. package/shared/vcf.js +0 -629
@@ -1,586 +0,0 @@
1
- import { GdcTopMutatedGeneRequest, GdcTopMutatedGeneResponse, Gene } from '#shared/types/routes/gdc.topMutatedGenes.ts'
2
- import { mclasscnvgain, mclasscnvloss, dtsnvindel } from '#shared/common.js'
3
- import got from 'got'
4
- import serverconfig from '#src/serverconfig.js'
5
-
6
- // TODO change to /termdb/topMutatedGenes
7
-
8
- export const api = {
9
- endpoint: 'gdc/topMutatedGenes',
10
- methods: {
11
- all: {
12
- init,
13
- request: {
14
- typeId: 'GdcTopMutatedGeneRequest'
15
- },
16
- response: {
17
- typeId: 'GdcTopMutatedGeneResponse'
18
- }
19
- }
20
- }
21
- }
22
-
23
- function init({ genomes }) {
24
- /*
25
- genomes parameter is currently not used
26
- could be used later to:
27
- - verify hg38/GDC is on this instance and otherwise disable this route..
28
- - perform conversion on gene name/id for future on needs
29
- */
30
- return async (req: any, res: any): Promise<void> => {
31
- const q: GdcTopMutatedGeneRequest = req.query
32
- const g = genomes.hg38
33
- if (!g) throw 'hg38 missing'
34
- const ds = g.datasets.GDC
35
- if (!ds) throw 'hg38 GDC missing'
36
- try {
37
- const genes = await getGenesGraphql(q, ds)
38
- const payload: GdcTopMutatedGeneResponse = { genes }
39
- res.send(payload)
40
- } catch (e: any) {
41
- res.send({ status: 'error', error: e.message || e })
42
- }
43
- }
44
- }
45
-
46
- // query string copied from v1, works in v2. but v1 are not "cohort-centric and lacks the case_filters: $caseFilters"
47
- // delete the v1 after soft launch
48
- const queryV1: any = {
49
- query: `
50
- query GenesTable_relayQuery(
51
- $genesTable_filters: FiltersArgument
52
- $genesTable_size: Int
53
- $genesTable_offset: Int
54
- $score: String
55
- $ssmCase: FiltersArgument
56
- $geneCaseFilter: FiltersArgument
57
- $ssmTested: FiltersArgument
58
- $cnvTested: FiltersArgument
59
- $cnvGainFilters: FiltersArgument
60
- $cnvLossFilters: FiltersArgument
61
- ) {
62
- genesTableViewer: viewer {
63
- explore {
64
- cases {
65
- hits(first: 0, filters: $ssmTested) {
66
- total
67
- }
68
- }
69
- filteredCases: cases {
70
- hits(first: 0, filters: $geneCaseFilter) {
71
- total
72
- }
73
- }
74
- cnvCases: cases {
75
- hits(first: 0, filters: $cnvTested) {
76
- total
77
- }
78
- }
79
- genes {
80
- hits(first: $genesTable_size, offset: $genesTable_offset, filters: $genesTable_filters, score: $score) {
81
- total
82
- edges {
83
- node {
84
- id
85
- numCases: score
86
- symbol
87
- name
88
- cytoband
89
- biotype
90
- gene_id
91
- is_cancer_gene_census
92
- ssm_case: case {
93
- hits(first: 0, filters: $ssmCase) {
94
- total
95
- }
96
- }
97
- cnv_case: case {
98
- hits(first: 0, filters: $cnvTested) {
99
- total
100
- }
101
- }
102
- case_cnv_gain: case {
103
- hits(first: 0, filters: $cnvGainFilters) {
104
- total
105
- }
106
- }
107
- case_cnv_loss: case {
108
- hits(first: 0, filters: $cnvLossFilters) {
109
- total
110
- }
111
- }
112
- }
113
- }
114
- }
115
- }
116
- }
117
- }
118
- }
119
- `,
120
- getVariables: (q: any) => {
121
- // set type "any" to avoid complains
122
- const variables: any = {
123
- genesTable_filters: { op: 'and', content: [] },
124
- genesTable_size: q.maxGenes || 50,
125
- genesTable_offset: 0,
126
- score: 'case.project.project_id',
127
- ssmCase: {
128
- op: 'and',
129
- content: [
130
- {
131
- op: 'in',
132
- content: {
133
- field: 'cases.available_variation_data',
134
- value: ['ssm']
135
- }
136
- },
137
- {
138
- op: 'NOT',
139
- content: {
140
- field: 'genes.case.ssm.observation.observation_id',
141
- value: 'MISSING'
142
- }
143
- }
144
- ]
145
- },
146
- geneCaseFilter: {
147
- content: [
148
- {
149
- content: {
150
- field: 'cases.available_variation_data',
151
- value: ['ssm']
152
- },
153
- op: 'in'
154
- }
155
- ],
156
- op: 'and'
157
- },
158
- ssmTested: {
159
- content: [
160
- {
161
- content: {
162
- field: 'cases.available_variation_data',
163
- value: ['ssm']
164
- },
165
- op: 'in'
166
- }
167
- ],
168
- op: 'and'
169
- },
170
- cnvTested: {
171
- op: 'and',
172
- content: [
173
- {
174
- content: {
175
- field: 'cases.available_variation_data',
176
- value: ['cnv']
177
- },
178
- op: 'in'
179
- }
180
- ]
181
- },
182
- cnvGainFilters: {
183
- op: 'and',
184
- content: [
185
- {
186
- content: {
187
- field: 'cases.available_variation_data',
188
- value: ['cnv']
189
- },
190
- op: 'in'
191
- },
192
- {
193
- content: {
194
- field: 'cnvs.cnv_change',
195
- value: ['Gain']
196
- },
197
- op: 'in'
198
- }
199
- ]
200
- },
201
- cnvLossFilters: {
202
- op: 'and',
203
- content: [
204
- {
205
- content: {
206
- field: 'cases.available_variation_data',
207
- value: ['cnv']
208
- },
209
- op: 'in'
210
- },
211
- {
212
- content: {
213
- field: 'cnvs.cnv_change',
214
- value: ['Loss']
215
- },
216
- op: 'in'
217
- }
218
- ]
219
- }
220
- }
221
-
222
- if (q.filter0) {
223
- variables.genesTable_filters.content.push(JSON.parse(JSON.stringify(q.filter0)))
224
- variables.geneCaseFilter.content.push(JSON.parse(JSON.stringify(q.filter0)))
225
- variables.cnvTested.content.push(JSON.parse(JSON.stringify(q.filter0)))
226
- variables.cnvGainFilters.content.push(JSON.parse(JSON.stringify(q.filter0)))
227
- variables.cnvLossFilters.content.push(JSON.parse(JSON.stringify(q.filter0)))
228
- }
229
-
230
- if (q.geneFilter == 'CGC') {
231
- variables.genesTable_filters.content.push(geneCGC())
232
- variables.geneCaseFilter.content.push(geneCGC())
233
- variables.cnvTested.content.push(geneCGC())
234
- variables.cnvGainFilters.content.push(geneCGC())
235
- variables.cnvLossFilters.content.push(geneCGC())
236
- }
237
- return variables
238
- }
239
- }
240
-
241
- const queryV2: any = {
242
- query: `
243
- query GenesTable(
244
- $caseFilters: FiltersArgument
245
- $genesTable_filters: FiltersArgument
246
- $genesTable_size: Int
247
- $genesTable_offset: Int
248
- $score: String
249
- $ssmCase: FiltersArgument
250
- $geneCaseFilter: FiltersArgument
251
- $ssmTested: FiltersArgument
252
- $cnvTested: FiltersArgument
253
- $cnvGainFilters: FiltersArgument
254
- $cnvLossFilters: FiltersArgument
255
- $sort: [Sort]
256
- ) {
257
- genesTableViewer: viewer {
258
- explore {
259
- cases {
260
- hits(first: 0, case_filters: $ssmTested) {
261
- total
262
- }
263
- }
264
- filteredCases: cases {
265
- hits(first: 0, case_filters: $geneCaseFilter) {
266
- total
267
- }
268
- }
269
- cnvCases: cases {
270
- hits(first: 0, case_filters: $cnvTested) {
271
- total
272
- }
273
- }
274
- genes {
275
- hits(
276
- first: $genesTable_size
277
- offset: $genesTable_offset
278
- filters: $genesTable_filters
279
- case_filters: $caseFilters
280
- score: $score
281
- sort: $sort
282
- ) {
283
- total
284
- edges {
285
- node {
286
- id
287
- numCases: score
288
- symbol
289
- name
290
- cytoband
291
- biotype
292
- gene_id
293
- is_cancer_gene_census
294
- ssm_case: case {
295
- hits(first: 0, filters: $ssmCase) {
296
- total
297
- }
298
- }
299
- cnv_case: case {
300
- hits(first: 0, filters: $cnvTested) {
301
- total
302
- }
303
- }
304
- case_cnv_gain: case {
305
- hits(first: 0, filters: $cnvGainFilters) {
306
- total
307
- }
308
- }
309
- case_cnv_loss: case {
310
- hits(first: 0, filters: $cnvLossFilters) {
311
- total
312
- }
313
- }
314
- }
315
- }
316
- }
317
- }
318
- }
319
- }
320
- }`,
321
- getVariables: (q: any) => {
322
- const variables: any = {
323
- caseFilters: { op: 'and', content: [] },
324
- genesTable_filters: { op: 'and', content: [] },
325
- genesTable_size: q.maxGenes || 50,
326
- genesTable_offset: 0,
327
- score: 'case.project.project_id',
328
- ssmCase: {
329
- op: 'and',
330
- content: [
331
- {
332
- op: 'in',
333
- content: {
334
- field: 'cases.available_variation_data',
335
- value: ['ssm']
336
- }
337
- },
338
- {
339
- op: 'NOT',
340
- content: {
341
- field: 'genes.case.ssm.observation.observation_id',
342
- value: 'MISSING'
343
- }
344
- }
345
- ]
346
- },
347
- geneCaseFilter: {
348
- content: [
349
- {
350
- content: {
351
- field: 'cases.available_variation_data',
352
- value: ['ssm']
353
- },
354
- op: 'in'
355
- }
356
- ],
357
- op: 'and'
358
- },
359
- ssmTested: {
360
- content: [
361
- {
362
- content: {
363
- field: 'cases.available_variation_data',
364
- value: ['ssm']
365
- },
366
- op: 'in'
367
- }
368
- ],
369
- op: 'and'
370
- },
371
- cnvTested: {
372
- op: 'and',
373
- content: [
374
- {
375
- content: {
376
- field: 'cases.available_variation_data',
377
- value: ['cnv']
378
- },
379
- op: 'in'
380
- }
381
- ]
382
- },
383
- cnvGainFilters: {
384
- op: 'and',
385
- content: [
386
- {
387
- content: {
388
- field: 'cases.available_variation_data',
389
- value: ['cnv']
390
- },
391
- op: 'in'
392
- },
393
- {
394
- content: {
395
- field: 'cnvs.cnv_change',
396
- value: ['Gain']
397
- },
398
- op: 'in'
399
- }
400
- ]
401
- },
402
- cnvLossFilters: {
403
- op: 'and',
404
- content: [
405
- {
406
- content: {
407
- field: 'cases.available_variation_data',
408
- value: ['cnv']
409
- },
410
- op: 'in'
411
- },
412
- {
413
- content: {
414
- field: 'cnvs.cnv_change',
415
- value: ['Loss']
416
- },
417
- op: 'in'
418
- }
419
- ]
420
- }
421
- }
422
-
423
- if (q.filter0) {
424
- variables.caseFilters.content.push(JSON.parse(JSON.stringify(q.filter0)))
425
- variables.geneCaseFilter.content.push(JSON.parse(JSON.stringify(q.filter0)))
426
- variables.cnvLossFilters.content.push(JSON.parse(JSON.stringify(q.filter0)))
427
- variables.cnvGainFilters.content.push(JSON.parse(JSON.stringify(q.filter0)))
428
- variables.cnvTested.content.push(JSON.parse(JSON.stringify(q.filter0)))
429
- }
430
-
431
- if (q.geneFilter == 'CGC') {
432
- variables.genesTable_filters.content.push(geneCGC())
433
- variables.cnvLossFilters.content.push(geneCGC())
434
- variables.cnvGainFilters.content.push(geneCGC())
435
- }
436
- return variables
437
- }
438
- }
439
-
440
- async function getGenesGraphql(q: GdcTopMutatedGeneRequest, ds) {
441
- let query: string, variables: object
442
- const { host, headers } = ds.getHostHeaders(q)
443
-
444
- // TODO: change this condition to use host.geneExp != host.rest ???
445
- if (serverconfig.features?.geneExpHost) {
446
- // quick fix! this is only set on local dev machines, meaning it's using v1 api; delete this after soft launch!!
447
- query = queryV1.query
448
- variables = queryV1.getVariables(q)
449
- } else {
450
- // this is not set and should be in qa-orange. it should be like this after softlaunch
451
- query = queryV2.query
452
- variables = queryV2.getVariables(q)
453
- }
454
-
455
- const response = await got.post(host.graphql, {
456
- headers,
457
- body: JSON.stringify({ query, variables })
458
- })
459
-
460
- const re: any = JSON.parse(response.body)
461
- const genes: Gene[] = []
462
- for (const g of re.data.genesTableViewer.explore.genes.hits.edges) {
463
- /* g.node is:
464
- {
465
- "biotype": "protein_coding",
466
- "case_cnv_gain": { "hits": { "total": 65 } },
467
- "case_cnv_loss": { "hits": { "total": 93 } },
468
- "cnv_case": { "hits": { "total": 173 } },
469
- "cytoband": [
470
- "12q15"
471
- ],
472
- "gene_id": "ENSG00000127329",
473
- "is_cancer_gene_census": true,
474
- "name": "protein tyrosine phosphatase receptor type B",
475
- "numCases": 18,
476
- "ssm_case": { "hits": { "total": 630 } },
477
- "symbol": "PTPRB"
478
- }
479
- */
480
- if (typeof g.node != 'object') throw 'node missing from re.data.genesTableViewer.explore.genes.hits.edges[]'
481
- const mutationStat: any = []
482
- if (Number.isInteger(g.node.case_cnv_gain?.hits?.total) && g.node.case_cnv_gain.hits.total > 0)
483
- mutationStat.push({ class: mclasscnvgain, count: g.node.case_cnv_gain.hits.total })
484
- if (Number.isInteger(g.node.case_cnv_loss?.hits?.total) && g.node.case_cnv_loss.hits.total > 0)
485
- mutationStat.push({ class: mclasscnvloss, count: g.node.case_cnv_loss.hits.total })
486
- if (Number.isInteger(g.node.ssm_case?.hits?.total) && g.node.ssm_case.hits.total > 0)
487
- mutationStat.push({ dt: dtsnvindel, count: g.node.ssm_case.hits.total })
488
- genes.push({
489
- gene: g.node.symbol,
490
- mutationStat
491
- })
492
- }
493
- return genes
494
- }
495
-
496
- function geneCGC() {
497
- // return a copy of cgc filter obj each time
498
- return {
499
- content: {
500
- field: 'genes.is_cancer_gene_census',
501
- value: ['true']
502
- },
503
- op: 'in'
504
- } as object
505
- }
506
-
507
- /*************************************
508
- below are old code
509
- old method to use rest api
510
- **************************************
511
- this api only gets ssm-cases and does not account for cnv cases, will not return any gene for ssm-less cohort e.g. APOLLO-LUAD
512
- thus is replaced by getGenesGraphql
513
- async function getGenes(q: GdcTopMutatedGeneRequest) {
514
- const _f = q.filter0 || { op: 'and', content: [] } // allow blank filter to test geneset edit ui (without filter)
515
- const response = await got.post(path.join(host.rest, '/analysis/top_mutated_genes_by_project'), {
516
- headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
517
- body: JSON.stringify({
518
- size: q.maxGenes || 50,
519
- fields: 'symbol',
520
- filters: mayAddCGC2filter(_f, q.geneFilter)
521
- })
522
- })
523
- const re = JSON.parse(response.body)
524
- const genes = [] as string[]
525
- for (const hit of re.data.hits) {
526
- if (!hit.symbol) continue
527
- genes.push(hit.symbol)
528
- }
529
- return genes
530
- }
531
- */
532
-
533
- /*
534
- str:
535
- stringified gdc filter object, should not include the "genes.is_cancer_gene_census" filter element
536
- geneFilter: str
537
- if = 'CGC', insert following element into the filter and return stringified obj
538
-
539
- {
540
- "op":"and",
541
- "content":[
542
- {
543
- "content":{ "field":"genes.is_cancer_gene_census", "value":["true"] },
544
- "op":"in"
545
- }
546
- ]
547
- }
548
- function mayAddCGC2filter(f: any, geneFilter?: string) {
549
- // reformulate f into f2
550
- // f may be "in" or "and". f2 is always "and", in order to join in additional filters
551
- let f2
552
-
553
- if (f.op == 'in') {
554
- // wrap f into f2
555
- f2 = { op: 'and', content: [f] }
556
- } else if (f.op == 'and') {
557
- // no need to wrap
558
- f2 = f
559
- } else {
560
- throw 'f.op not "in" or "and"'
561
- }
562
-
563
- // per Phil on 12/16/2022, following filters ensure to return IDH1 as 1st gene for gliomas
564
- f2.content.push({
565
- op: 'NOT',
566
- content: {
567
- field: 'ssms.consequence.transcript.annotation.vep_impact',
568
- value: 'missing'
569
- }
570
- })
571
- f2.content.push({
572
- op: 'in',
573
- content: {
574
- field: 'ssms.consequence.transcript.consequence_type',
575
- value: ['missense_variant', 'frameshift_variant', 'start_lost', 'stop_lost', 'stop_gained']
576
- }
577
- })
578
-
579
- if (geneFilter == 'CGC') {
580
- // using CGC genes, add in filter
581
- f2.content.push({ op: 'in', content: { field: 'genes.is_cancer_gene_census', value: ['true'] } })
582
- }
583
-
584
- return f2
585
- }
586
- */
@@ -1,50 +0,0 @@
1
- import { getResult } from '#src/gene.js'
2
- import { GeneLookupRequest, GeneLookupResponse } from '#shared/types/routes/genelookup.ts'
3
-
4
- function init({ genomes }) {
5
- return (req: any, res: any): void => {
6
- try {
7
- const q = req.query as GeneLookupRequest
8
- const g = genomes[req.query.genome]
9
- if (!g) throw 'invalid genome name'
10
- const result = getResult(g, req.query) as GeneLookupResponse
11
- res.send(result)
12
- } catch (e: any) {
13
- res.send({ error: e.message || e })
14
- if (e.stack) console.log(e.stack)
15
- }
16
- }
17
- }
18
-
19
- export const api: any = {
20
- endpoint: 'genelookup',
21
- methods: {
22
- get: {
23
- init,
24
- request: {
25
- typeId: 'GeneLookupRequest'
26
- //valid: default to type checker
27
- },
28
- response: {
29
- typeId: 'GeneLookupResponse'
30
- // will combine this with type checker
31
- //valid: (t) => {}
32
- },
33
- examples: [
34
- {
35
- request: {
36
- body: { input: 'kr', genome: 'hg38-test' }
37
- },
38
- response: {
39
- header: { status: 200 },
40
- body: { hits: ['KRAS'] }
41
- }
42
- }
43
- ]
44
- },
45
- post: {
46
- alternativeFor: 'get',
47
- init
48
- }
49
- }
50
- }
@@ -1,29 +0,0 @@
1
- import { getStat } from '#src/health.ts'
2
- import { HealthCheckResponse } from '#shared/types/routes/healthcheck.ts'
3
-
4
- export const api = {
5
- endpoint: 'healthcheck',
6
- methods: {
7
- get: {
8
- init({ genomes }) {
9
- return async (req: undefined, res: any): Promise<void> => {
10
- try {
11
- const health = (await getStat(genomes)) as HealthCheckResponse
12
- res.send(health)
13
- } catch (e: any) {
14
- res.send({ status: 'error', error: e.message || e })
15
- }
16
- }
17
- },
18
- request: {
19
- typeId: null
20
- //valid: default to type checker
21
- },
22
- response: {
23
- typeId: 'HealthCheckResponse'
24
- // will combine this with type checker
25
- //valid: (t) => {}
26
- }
27
- }
28
- }
29
- }