From ed25930ab85dfa59e4ee4f9bea3cf1253dd0c707 Mon Sep 17 00:00:00 2001 From: Colin Date: Sat, 23 Nov 2024 05:32:25 -0500 Subject: [PATCH 1/5] Sped --- src/parse.ts | 178 ++++++++++++++------------ test/__snapshots__/parse.test.ts.snap | 12 -- 2 files changed, 99 insertions(+), 91 deletions(-) diff --git a/src/parse.ts b/src/parse.ts index c8662bc..74c233a 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -16,10 +16,14 @@ function decodeURIComponentNoThrow(uri: string) { /** * Class representing a VCF parser, instantiated with the VCF header. + * * @param {object} args + * * @param {string} args.header - The VCF header. Supports both LF and CRLF * newlines. - * @param {boolean} args.strict - Whether to parse in strict mode or not (default true) + * + * @param {boolean} args.strict - Whether to parse in strict mode or not + * (default true) */ export default class VCF { private metadata: Record @@ -56,7 +60,7 @@ export default class VCF { if (!line.startsWith('#')) { throw new Error(`Bad line in header:\n${line}`) } else if (line.startsWith('##')) { - this._parseMetadata(line) + this.parseMetadata(line) } else { lastLine = line } @@ -88,53 +92,84 @@ export default class VCF { this.samples = fields.slice(9) } - _parseGenotypes(format: string | undefined, prerest: string) { - const rest = prerest.split('\t') - const genotypes = {} as any - const formatKeys = format?.split(':') - if (formatKeys) { - this.samples.forEach((sample, index) => { + private parseSamples(format: string, prerest: string) { + const genotypes = {} as Record< + string, + Record + > + if (format) { + const rest = prerest.split('\t') + const formatKeys = format.split(':') + const isNumberType = formatKeys.map(key => { + const r = this.getMetadata('FORMAT', key, 'Type') + return r === 'Integer' || r === 'Float' + }) + for (let i = 0; i < this.samples.length; i++) { + const sample = this.samples[i]! genotypes[sample] = {} - formatKeys.forEach(key => { - genotypes[sample][key] = null - }) - rest[index]!.split(':') - .filter(f => f) - .forEach((val, index) => { - let thisValue: unknown - if (val === '' || val === '.') { - thisValue = null - } else { - const entries = val - .split(',') - .map(ent => (ent === '.' ? null : ent)) + const columns = rest[i]!.split(':') + for (let j = 0; j < columns.length; j++) { + const val = columns[j]! + genotypes[sample][formatKeys[j]!] = + val === '' || val === '.' + ? null + : val + .split(',') + .map(ent => + ent === '.' ? null : isNumberType[j] ? +ent : ent, + ) + } + } + } + return genotypes + } - const valueType = this.getMetadata( - 'FORMAT', - formatKeys[index]!, - 'Type', - ) - if (valueType === 'Integer' || valueType === 'Float') { - thisValue = entries.map(val => (val ? +val : val)) - } else { - thisValue = entries - } + private parseGenotypesOnly(format: string, prerest: string) { + const rest = prerest.split('\t') + const genotypes = {} as any + let i = 0 + const formatSplit = format.split(':') + if (formatSplit.length === 1) { + for (const sample of this.samples) { + genotypes[sample] = { + GT: rest[i++]!, + } + } + } else { + const gtIndex = formatSplit.findIndex(f => f === 'GT') + if (gtIndex === 0) { + for (const sample of this.samples) { + const val = rest[i++]! + const idx = val.indexOf(':') + if (idx !== -1) { + genotypes[sample] = { + GT: val.slice(0, idx), } - - genotypes[sample][formatKeys[index]!] = thisValue - }, {}) - }) + } else { + console.warn('unknown') + } + } + } else { + for (const sample of this.samples) { + const val = rest[i++]!.split(':') + genotypes[sample] = { + GT: val[gtIndex], + } + } + } } + return genotypes } /** * Parse a VCF metadata line (i.e. a line that starts with "##") and add its * properties to the object. + * * @param {string} line - A line from the VCF. Supports both LF and CRLF * newlines. */ - _parseMetadata(line: string) { + private parseMetadata(line: string) { const match = /^##(.+?)=(.*)/.exec(line.trim()) if (!match) { throw new Error(`Line is not a valid metadata line: ${line}`) @@ -144,9 +179,9 @@ export default class VCF { const r = metaKey! if (metaVal?.startsWith('<')) { if (!(r in this.metadata)) { - this.metadata[metaKey!] = {} + this.metadata[r] = {} } - const [id, keyVals] = this._parseStructuredMetaVal(metaVal) + const [id, keyVals] = this.parseStructuredMetaVal(metaVal) this.metadata[r][id] = keyVals } else { this.metadata[r] = metaVal @@ -156,13 +191,14 @@ export default class VCF { /** * Parse a VCF header structured meta string (i.e. a meta value that starts * with "$/g, ''), ',') + private parseStructuredMetaVal(metaVal: string) { + const keyVals = this.parseKeyValue(metaVal.replace(/^<|>$/g, ''), ',') const id = keyVals.ID delete keyVals.ID if ('Number' in keyVals) { @@ -177,11 +213,12 @@ export default class VCF { * Get metadata filtered by the elements in args. For example, can pass * ('INFO', 'DP') to only get info on an metadata tag that was like * "##INFO=" + * * @param {...string} args - List of metadata filter strings. * * @returns {any} An object, string, or number, depending on the filtering */ - getMetadata(...args: string[]) { + private getMetadata(...args: string[]) { let filteredMetadata: any = this.metadata for (const arg of args) { filteredMetadata = filteredMetadata[arg] @@ -193,24 +230,24 @@ export default class VCF { } /** - * Sometimes VCFs have key-value strings that allow the separator within - * the value if it's in quotes, like: + * Sometimes VCFs have key-value strings that allow the separator within the + * value if it's in quotes, like: * 'ID=DB,Number=0,Type=Flag,Description="dbSNP membership, build 129"' * * Parse this at a low level since we can't just split at "," (or whatever - * separator). Above line would be parsed to: - * {ID: 'DB', Number: '0', Type: 'Flag', Description: 'dbSNP membership, build 129'} - * @param {string} str - Key-value pairs in a string - * @param {string} [pairSeparator] - A string that separates sets of key-value - * pairs - * - * @returns {object} An object containing the key-value pairs + * separator). Above line would be parsed to: {ID: 'DB', Number: '0', Type: + * 'Flag', Description: 'dbSNP membership, build 129'} */ - _parseKeyValue(str: string, pairSeparator = ';') { + private parseKeyValue(str: string, pairSeparator = ';') { const data: any = {} let currKey = '' let currValue = '' - let state = 1 // states: 1: read key to = or pair sep, 2: read value to sep or quote, 3: read value to quote + + // states: + // 1: read key to = or pair sep + // 2: read value to sep or quote + // 3: read value to quote + let state = 1 for (const s of str) { if (state === 1) { // read key to = or pair sep @@ -254,6 +291,7 @@ export default class VCF { /** * Parse a VCF line into an object like { CHROM POS ID REF ALT QUAL FILTER * INFO } with SAMPLES optionally included if present in the VCF + * * @param {string} line - A string of a line from a VCF. Supports both LF and * CRLF newlines. */ @@ -295,9 +333,9 @@ export default class VCF { const info: any = fields[7] === undefined || fields[7] === '.' ? {} - : this._parseKeyValue(fields[7]) + : this.parseKeyValue(fields[7]) - Object.keys(info).forEach(key => { + for (const key of Object.keys(info)) { let items if (info[key]) { items = (info[key] as string) @@ -311,12 +349,9 @@ export default class VCF { const itemType = this.getMetadata('INFO', key, 'Type') if (itemType) { if (itemType === 'Integer' || itemType === 'Float') { - items = items.map((val: string | null) => { - if (val === null) { - return null - } - return Number(val) - }) + items = items.map((val: string | null) => + val === null ? null : Number(val), + ) } else if (itemType === 'Flag') { if (info[key]) { console.warn( @@ -328,10 +363,10 @@ export default class VCF { } } info[key] = items - }) + } //@ts-ignore - const variant = new Variant({ + return new Variant({ CHROM: chrom, POS: pos, ALT: alt, @@ -341,23 +376,8 @@ export default class VCF { filter && filter.length === 1 && filter[0] === 'PASS' ? 'PASS' : filter, ID: id, QUAL: qual, + SAMPLES: parser.parseSamples(fields[8] ?? '', rest), + GENOTYPES: parser.parseGenotypesOnly(fields[8] ?? '', rest), }) - - Object.defineProperty(variant, 'SAMPLES', { - get() { - const samples = parser._parseGenotypes(fields[8], rest) - - Object.defineProperty(this, 'SAMPLES', { - value: samples, - configurable: false, - }) - - return samples - }, - configurable: true, - }) - - //@ts-ignore - return variant } } diff --git a/test/__snapshots__/parse.test.ts.snap b/test/__snapshots__/parse.test.ts.snap index f4a5868..e47be48 100644 --- a/test/__snapshots__/parse.test.ts.snap +++ b/test/__snapshots__/parse.test.ts.snap @@ -845,22 +845,13 @@ Variant { exports[`VCF parser > can parse a line with minimal entries 2`] = ` { "NA00001": { - "DP": null, - "GQ": null, "GT": null, - "HQ": null, }, "NA00002": { - "DP": null, - "GQ": null, "GT": null, - "HQ": null, }, "NA00003": { - "DP": null, - "GQ": null, "GT": null, - "HQ": null, }, } `; @@ -1786,7 +1777,6 @@ exports[`snippet from VCF 4.3 spec 2`] = ` "GT": [ "0/0", ], - "HQ": null, }, }, { @@ -1830,7 +1820,6 @@ exports[`snippet from VCF 4.3 spec 2`] = ` "GT": [ "2/2", ], - "HQ": null, }, }, { @@ -1874,7 +1863,6 @@ exports[`snippet from VCF 4.3 spec 2`] = ` "GT": [ "0/0", ], - "HQ": null, }, }, { From 92fba17945fe694a0a944c82c3ae50a1a329a5e6 Mon Sep 17 00:00:00 2001 From: Colin Date: Sat, 23 Nov 2024 12:36:44 -0500 Subject: [PATCH 2/5] Wow --- src/parse.ts | 35 +-- yarn.lock | 664 +++++++++++++++++++++++++-------------------------- 2 files changed, 343 insertions(+), 356 deletions(-) diff --git a/src/parse.ts b/src/parse.ts index 74c233a..e00416d 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -1,10 +1,5 @@ import vcfReserved from './vcfReserved' -function Variant(stuff: any) { - //@ts-ignore - Object.assign(this, stuff) -} - function decodeURIComponentNoThrow(uri: string) { try { return decodeURIComponent(uri) @@ -93,6 +88,7 @@ export default class VCF { } private parseSamples(format: string, prerest: string) { + console.log('wtf1') const genotypes = {} as Record< string, Record @@ -125,15 +121,14 @@ export default class VCF { } private parseGenotypesOnly(format: string, prerest: string) { + console.log('wtf2') const rest = prerest.split('\t') - const genotypes = {} as any + const genotypes = {} as Record let i = 0 const formatSplit = format.split(':') if (formatSplit.length === 1) { for (const sample of this.samples) { - genotypes[sample] = { - GT: rest[i++]!, - } + genotypes[sample] = rest[i++]! } } else { const gtIndex = formatSplit.findIndex(f => f === 'GT') @@ -142,9 +137,7 @@ export default class VCF { const val = rest[i++]! const idx = val.indexOf(':') if (idx !== -1) { - genotypes[sample] = { - GT: val.slice(0, idx), - } + genotypes[sample] = val.slice(0, idx) } else { console.warn('unknown') } @@ -152,9 +145,7 @@ export default class VCF { } else { for (const sample of this.samples) { const val = rest[i++]!.split(':') - genotypes[sample] = { - GT: val[gtIndex], - } + genotypes[sample] = val[gtIndex]! } } } @@ -301,9 +292,6 @@ export default class VCF { return undefined } - //@ts-ignore - const parser = this // so we can include this in lazy-property closure - let currChar = 0 for (let currField = 0; currChar < line.length; currChar += 1) { if (line[currChar] === '\t') { @@ -330,7 +318,7 @@ export default class VCF { "no INFO field specified, must contain at least a '.' (turn off strict mode to allow)", ) } - const info: any = + const info = fields[7] === undefined || fields[7] === '.' ? {} : this.parseKeyValue(fields[7]) @@ -365,8 +353,7 @@ export default class VCF { info[key] = items } - //@ts-ignore - return new Variant({ + return { CHROM: chrom, POS: pos, ALT: alt, @@ -376,8 +363,8 @@ export default class VCF { filter && filter.length === 1 && filter[0] === 'PASS' ? 'PASS' : filter, ID: id, QUAL: qual, - SAMPLES: parser.parseSamples(fields[8] ?? '', rest), - GENOTYPES: parser.parseGenotypesOnly(fields[8] ?? '', rest), - }) + SAMPLES: () => this.parseSamples(fields[8] ?? '', rest), + GENOTYPES: () => this.parseGenotypesOnly(fields[8] ?? '', rest), + } } } diff --git a/yarn.lock b/yarn.lock index 015b274..50abc55 100644 --- a/yarn.lock +++ b/yarn.lock @@ -408,24 +408,24 @@ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== -"@eslint/config-array@^0.18.0": - version "0.18.0" - resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.18.0.tgz#37d8fe656e0d5e3dbaea7758ea56540867fd074d" - integrity sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw== +"@eslint/config-array@^0.19.0": + version "0.19.0" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.19.0.tgz#3251a528998de914d59bb21ba4c11767cf1b3519" + integrity sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ== dependencies: "@eslint/object-schema" "^2.1.4" debug "^4.3.1" minimatch "^3.1.2" -"@eslint/core@^0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.7.0.tgz#a1bb4b6a4e742a5ff1894b7ee76fbf884ec72bd3" - integrity sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw== +"@eslint/core@^0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.9.0.tgz#168ee076f94b152c01ca416c3e5cf82290ab4fcd" + integrity sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg== -"@eslint/eslintrc@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6" - integrity sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ== +"@eslint/eslintrc@^3.1.0", "@eslint/eslintrc@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.2.0.tgz#57470ac4e2e283a6bf76044d63281196e370542c" + integrity sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w== dependencies: ajv "^6.12.4" debug "^4.3.2" @@ -437,20 +437,20 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@9.14.0", "@eslint/js@^9.7.0": - version "9.14.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.14.0.tgz#2347a871042ebd11a00fd8c2d3d56a265ee6857e" - integrity sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg== +"@eslint/js@9.15.0", "@eslint/js@^9.7.0": + version "9.15.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.15.0.tgz#df0e24fe869143b59731942128c19938fdbadfb5" + integrity sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg== "@eslint/object-schema@^2.1.4": version "2.1.4" resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843" integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ== -"@eslint/plugin-kit@^0.2.0": - version "0.2.2" - resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz#5eff371953bc13e3f4d88150e2c53959f64f74f6" - integrity sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw== +"@eslint/plugin-kit@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz#812980a6a41ecf3a8341719f92a6d1e784a2e0e8" + integrity sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA== dependencies: levn "^0.4.1" @@ -477,7 +477,7 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a" integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== -"@humanwhocodes/retry@^0.4.0": +"@humanwhocodes/retry@^0.4.1": version "0.4.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b" integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== @@ -770,95 +770,95 @@ resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== -"@rollup/rollup-android-arm-eabi@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.25.0.tgz#3e7eda4c0c1de6d2415343002d742ff95e38dca7" - integrity sha512-CC/ZqFZwlAIbU1wUPisHyV/XRc5RydFrNLtgl3dGYskdwPZdt4HERtKm50a/+DtTlKeCq9IXFEWR+P6blwjqBA== - -"@rollup/rollup-android-arm64@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.25.0.tgz#04f679231acf7284f1f8a1f7250d0e0944865ba8" - integrity sha512-/Y76tmLGUJqVBXXCfVS8Q8FJqYGhgH4wl4qTA24E9v/IJM0XvJCGQVSW1QZ4J+VURO9h8YCa28sTFacZXwK7Rg== - -"@rollup/rollup-darwin-arm64@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.25.0.tgz#ecea723041621747d0772af93b54752edf26467a" - integrity sha512-YVT6L3UrKTlC0FpCZd0MGA7NVdp7YNaEqkENbWQ7AOVOqd/7VzyHpgIpc1mIaxRAo1ZsJRH45fq8j4N63I/vvg== - -"@rollup/rollup-darwin-x64@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.25.0.tgz#28e6e0687092f31e20982fc104779d48c643fc21" - integrity sha512-ZRL+gexs3+ZmmWmGKEU43Bdn67kWnMeWXLFhcVv5Un8FQcx38yulHBA7XR2+KQdYIOtD0yZDWBCudmfj6lQJoA== - -"@rollup/rollup-freebsd-arm64@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.25.0.tgz#99e9173b8aef3d1ef086983da70413988206e530" - integrity sha512-xpEIXhiP27EAylEpreCozozsxWQ2TJbOLSivGfXhU4G1TBVEYtUPi2pOZBnvGXHyOdLAUUhPnJzH3ah5cqF01g== - -"@rollup/rollup-freebsd-x64@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.25.0.tgz#f3a1ef941f8d3c6b2b036484c69a7b2d3d9ebbd7" - integrity sha512-sC5FsmZGlJv5dOcURrsnIK7ngc3Kirnx3as2XU9uER+zjfyqIjdcMVgzy4cOawhsssqzoAX19qmxgJ8a14Qrqw== - -"@rollup/rollup-linux-arm-gnueabihf@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.25.0.tgz#9ba6adcc33f26f2a0c6ee658f0bbda4de8da2f75" - integrity sha512-uD/dbLSs1BEPzg564TpRAQ/YvTnCds2XxyOndAO8nJhaQcqQGFgv/DAVko/ZHap3boCvxnzYMa3mTkV/B/3SWA== - -"@rollup/rollup-linux-arm-musleabihf@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.25.0.tgz#62f2426fa9016ec884f4fa779d7b62d5ba02a41a" - integrity sha512-ZVt/XkrDlQWegDWrwyC3l0OfAF7yeJUF4fq5RMS07YM72BlSfn2fQQ6lPyBNjt+YbczMguPiJoCfaQC2dnflpQ== - -"@rollup/rollup-linux-arm64-gnu@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.25.0.tgz#f98ec111a231d35e0c6d3404e3d80f67f9d5b9f8" - integrity sha512-qboZ+T0gHAW2kkSDPHxu7quaFaaBlynODXpBVnPxUgvWYaE84xgCKAPEYE+fSMd3Zv5PyFZR+L0tCdYCMAtG0A== - -"@rollup/rollup-linux-arm64-musl@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.25.0.tgz#4b36ffb8359f959f2c29afd187603c53368b6723" - integrity sha512-ndWTSEmAaKr88dBuogGH2NZaxe7u2rDoArsejNslugHZ+r44NfWiwjzizVS1nUOHo+n1Z6qV3X60rqE/HlISgw== - -"@rollup/rollup-linux-powerpc64le-gnu@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.25.0.tgz#52f4b39e6783505d168a745b79d86474fde71680" - integrity sha512-BVSQvVa2v5hKwJSy6X7W1fjDex6yZnNKy3Kx1JGimccHft6HV0THTwNtC2zawtNXKUu+S5CjXslilYdKBAadzA== - -"@rollup/rollup-linux-riscv64-gnu@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.25.0.tgz#49195be7e6a7d68d482b12461e2ea914e31ff977" - integrity sha512-G4hTREQrIdeV0PE2JruzI+vXdRnaK1pg64hemHq2v5fhv8C7WjVaeXc9P5i4Q5UC06d/L+zA0mszYIKl+wY8oA== - -"@rollup/rollup-linux-s390x-gnu@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.25.0.tgz#4b8d50a205eac7b46cdcb9c50d4a6ae5994c02e0" - integrity sha512-9T/w0kQ+upxdkFL9zPVB6zy9vWW1deA3g8IauJxojN4bnz5FwSsUAD034KpXIVX5j5p/rn6XqumBMxfRkcHapQ== - -"@rollup/rollup-linux-x64-gnu@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.25.0.tgz#dfcceebc5ccac7fc2db19471996026258c81b55f" - integrity sha512-ThcnU0EcMDn+J4B9LD++OgBYxZusuA7iemIIiz5yzEcFg04VZFzdFjuwPdlURmYPZw+fgVrFzj4CA64jSTG4Ig== - -"@rollup/rollup-linux-x64-musl@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.25.0.tgz#192f78bad8429711d63a31dc0a7d3312e2df850e" - integrity sha512-zx71aY2oQxGxAT1JShfhNG79PnjYhMC6voAjzpu/xmMjDnKNf6Nl/xv7YaB/9SIa9jDYf8RBPWEnjcdlhlv1rQ== - -"@rollup/rollup-win32-arm64-msvc@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.25.0.tgz#f4ec076579634f780b4e5896ae7f59f3e38e0c60" - integrity sha512-JT8tcjNocMs4CylWY/CxVLnv8e1lE7ff1fi6kbGocWwxDq9pj30IJ28Peb+Y8yiPNSF28oad42ApJB8oUkwGww== - -"@rollup/rollup-win32-ia32-msvc@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.25.0.tgz#5458eab1929827e4f805cefb90bd09ecf7eeed2b" - integrity sha512-dRLjLsO3dNOfSN6tjyVlG+Msm4IiZnGkuZ7G5NmpzwF9oOc582FZG05+UdfTbz5Jd4buK/wMb6UeHFhG18+OEg== - -"@rollup/rollup-win32-x64-msvc@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.25.0.tgz#93415e7e707e4b156d77c5950b983b58f4bc33f3" - integrity sha512-/RqrIFtLB926frMhZD0a5oDa4eFIbyNEwLLloMTEjmqfwZWXywwVVOVmwTsuyhC9HKkVEZcOOi+KV4U9wmOdlg== +"@rollup/rollup-android-arm-eabi@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.4.tgz#e3c9cc13f144ba033df4d2c3130a214dc8e3473e" + integrity sha512-2Y3JT6f5MrQkICUyRVCw4oa0sutfAsgaSsb0Lmmy1Wi2y7X5vT9Euqw4gOsCyy0YfKURBg35nhUKZS4mDcfULw== + +"@rollup/rollup-android-arm64@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.4.tgz#0474250fcb5871aca952e249a0c3270fc4310b55" + integrity sha512-wzKRQXISyi9UdCVRqEd0H4cMpzvHYt1f/C3CoIjES6cG++RHKhrBj2+29nPF0IB5kpy9MS71vs07fvrNGAl/iA== + +"@rollup/rollup-darwin-arm64@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.4.tgz#77c29b4f9c430c1624f1a6835f2a7e82be3d16f2" + integrity sha512-PlNiRQapift4LNS8DPUHuDX/IdXiLjf8mc5vdEmUR0fF/pyy2qWwzdLjB+iZquGr8LuN4LnUoSEvKRwjSVYz3Q== + +"@rollup/rollup-darwin-x64@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.4.tgz#7d87711f641a458868758cbf110fb32eabd6a25a" + integrity sha512-o9bH2dbdgBDJaXWJCDTNDYa171ACUdzpxSZt+u/AAeQ20Nk5x+IhA+zsGmrQtpkLiumRJEYef68gcpn2ooXhSQ== + +"@rollup/rollup-freebsd-arm64@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.4.tgz#662f808d2780e4e91021ac9ee7ed800862bb9a57" + integrity sha512-NBI2/i2hT9Q+HySSHTBh52da7isru4aAAo6qC3I7QFVsuhxi2gM8t/EI9EVcILiHLj1vfi+VGGPaLOUENn7pmw== + +"@rollup/rollup-freebsd-x64@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.4.tgz#71e5a7bcfcbe51d8b65d158675acec1307edea79" + integrity sha512-wYcC5ycW2zvqtDYrE7deary2P2UFmSh85PUpAx+dwTCO9uw3sgzD6Gv9n5X4vLaQKsrfTSZZ7Z7uynQozPVvWA== + +"@rollup/rollup-linux-arm-gnueabihf@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.4.tgz#08f67fcec61ee18f8b33b3f403a834ab8f3aa75d" + integrity sha512-9OwUnK/xKw6DyRlgx8UizeqRFOfi9mf5TYCw1uolDaJSbUmBxP85DE6T4ouCMoN6pXw8ZoTeZCSEfSaYo+/s1w== + +"@rollup/rollup-linux-arm-musleabihf@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.4.tgz#2e1ad4607f86475b1731556359c6070eb8f4b109" + integrity sha512-Vgdo4fpuphS9V24WOV+KwkCVJ72u7idTgQaBoLRD0UxBAWTF9GWurJO9YD9yh00BzbkhpeXtm6na+MvJU7Z73A== + +"@rollup/rollup-linux-arm64-gnu@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.4.tgz#c65d559dcb0d3dabea500cf7b8215959ae6cccf8" + integrity sha512-pleyNgyd1kkBkw2kOqlBx+0atfIIkkExOTiifoODo6qKDSpnc6WzUY5RhHdmTdIJXBdSnh6JknnYTtmQyobrVg== + +"@rollup/rollup-linux-arm64-musl@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.4.tgz#6739f7eb33e20466bb88748519c98ce8dee23922" + integrity sha512-caluiUXvUuVyCHr5DxL8ohaaFFzPGmgmMvwmqAITMpV/Q+tPoaHZ/PWa3t8B2WyoRcIIuu1hkaW5KkeTDNSnMA== + +"@rollup/rollup-linux-powerpc64le-gnu@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.4.tgz#8d9fe9471c256e55278cb1f7b1c977cd8fe6df20" + integrity sha512-FScrpHrO60hARyHh7s1zHE97u0KlT/RECzCKAdmI+LEoC1eDh/RDji9JgFqyO+wPDb86Oa/sXkily1+oi4FzJQ== + +"@rollup/rollup-linux-riscv64-gnu@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.4.tgz#9a467f7ad5b61c9d66b24e79a3c57cb755d02c35" + integrity sha512-qyyprhyGb7+RBfMPeww9FlHwKkCXdKHeGgSqmIXw9VSUtvyFZ6WZRtnxgbuz76FK7LyoN8t/eINRbPUcvXB5fw== + +"@rollup/rollup-linux-s390x-gnu@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.4.tgz#efaddf22df27b87a267a731fbeb9539e92cd4527" + integrity sha512-PFz+y2kb6tbh7m3A7nA9++eInGcDVZUACulf/KzDtovvdTizHpZaJty7Gp0lFwSQcrnebHOqxF1MaKZd7psVRg== + +"@rollup/rollup-linux-x64-gnu@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.4.tgz#a959eccb04b07fd1591d7ff745a6865faa7042cd" + integrity sha512-Ni8mMtfo+o/G7DVtweXXV/Ol2TFf63KYjTtoZ5f078AUgJTmaIJnj4JFU7TK/9SVWTaSJGxPi5zMDgK4w+Ez7Q== + +"@rollup/rollup-linux-x64-musl@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.4.tgz#927764f1da1f2dd50943716dec93796d10cb6e99" + integrity sha512-5AeeAF1PB9TUzD+3cROzFTnAJAcVUGLuR8ng0E0WXGkYhp6RD6L+6szYVX+64Rs0r72019KHZS1ka1q+zU/wUw== + +"@rollup/rollup-win32-arm64-msvc@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.4.tgz#030b6cc607d845da23dced624e47fb45de105840" + integrity sha512-yOpVsA4K5qVwu2CaS3hHxluWIK5HQTjNV4tWjQXluMiiiu4pJj4BN98CvxohNCpcjMeTXk/ZMJBRbgRg8HBB6A== + +"@rollup/rollup-win32-ia32-msvc@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.4.tgz#3457a3f44a84f51d8097c3606429e01f0d2d0ec2" + integrity sha512-KtwEJOaHAVJlxV92rNYiG9JQwQAdhBlrjNRp7P9L8Cb4Rer3in+0A+IPhJC9y68WAi9H0sX4AiG2NTsVlmqJeQ== + +"@rollup/rollup-win32-x64-msvc@4.27.4": + version "4.27.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.4.tgz#67d516613c9f2fe42e2d8b78e252d0003179d92c" + integrity sha512-3j4jx1TppORdTAoBJRd+/wJRGCPC0ETWkXOecJ6PPZLj6SptXkrXcNqdj0oclbKML6FkQltdz7bBA3rUSirZug== "@sinclair/typebox@^0.27.8": version "0.27.8" @@ -988,9 +988,9 @@ integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== "@types/node@*": - version "22.9.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.9.0.tgz#b7f16e5c3384788542c72dc3d561a7ceae2c0365" - integrity sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ== + version "22.9.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.9.3.tgz#08f3d64b3bc6d74b162d36f60213e8a6704ef2b4" + integrity sha512-F3u1fs/fce3FFk+DAxbxc78DF8x0cY09RRL8GnXLmkJ1jvx3TtPdWoTT5/NiYfI5ASqXBmfqJi9dZ3gxMx4lzw== dependencies: undici-types "~6.19.8" @@ -1036,62 +1036,62 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@8.13.0", "@typescript-eslint/eslint-plugin@^8.8.1": - version "8.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.13.0.tgz#650c50b8c795b5d092189f139f6d00535b5b0f3d" - integrity sha512-nQtBLiZYMUPkclSeC3id+x4uVd1SGtHuElTxL++SfP47jR0zfkZBJHc+gL4qPsgTuypz0k8Y2GheaDYn6Gy3rg== +"@typescript-eslint/eslint-plugin@8.15.0", "@typescript-eslint/eslint-plugin@^8.8.1": + version "8.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.15.0.tgz#c95c6521e70c8b095a684d884d96c0c1c63747d2" + integrity sha512-+zkm9AR1Ds9uLWN3fkoeXgFppaQ+uEVtfOV62dDmsy9QCNqlRHWNEck4yarvRNrvRcHQLGfqBNui3cimoz8XAg== dependencies: "@eslint-community/regexpp" "^4.10.0" - "@typescript-eslint/scope-manager" "8.13.0" - "@typescript-eslint/type-utils" "8.13.0" - "@typescript-eslint/utils" "8.13.0" - "@typescript-eslint/visitor-keys" "8.13.0" + "@typescript-eslint/scope-manager" "8.15.0" + "@typescript-eslint/type-utils" "8.15.0" + "@typescript-eslint/utils" "8.15.0" + "@typescript-eslint/visitor-keys" "8.15.0" graphemer "^1.4.0" ignore "^5.3.1" natural-compare "^1.4.0" ts-api-utils "^1.3.0" -"@typescript-eslint/parser@8.13.0", "@typescript-eslint/parser@^8.8.1": - version "8.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.13.0.tgz#ef76203b7cac515aa3ccc4f7ce5320dd61c46b29" - integrity sha512-w0xp+xGg8u/nONcGw1UXAr6cjCPU1w0XVyBs6Zqaj5eLmxkKQAByTdV/uGgNN5tVvN/kKpoQlP2cL7R+ajZZIQ== +"@typescript-eslint/parser@8.15.0", "@typescript-eslint/parser@^8.8.1": + version "8.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.15.0.tgz#92610da2b3af702cfbc02a46e2a2daa6260a9045" + integrity sha512-7n59qFpghG4uazrF9qtGKBZXn7Oz4sOMm8dwNWDQY96Xlm2oX67eipqcblDj+oY1lLCbf1oltMZFpUso66Kl1A== dependencies: - "@typescript-eslint/scope-manager" "8.13.0" - "@typescript-eslint/types" "8.13.0" - "@typescript-eslint/typescript-estree" "8.13.0" - "@typescript-eslint/visitor-keys" "8.13.0" + "@typescript-eslint/scope-manager" "8.15.0" + "@typescript-eslint/types" "8.15.0" + "@typescript-eslint/typescript-estree" "8.15.0" + "@typescript-eslint/visitor-keys" "8.15.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@8.13.0": - version "8.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.13.0.tgz#2f4aed0b87d72360e64e4ea194b1fde14a59082e" - integrity sha512-XsGWww0odcUT0gJoBZ1DeulY1+jkaHUciUq4jKNv4cpInbvvrtDoyBH9rE/n2V29wQJPk8iCH1wipra9BhmiMA== +"@typescript-eslint/scope-manager@8.15.0": + version "8.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.15.0.tgz#28a1a0f13038f382424f45a988961acaca38f7c6" + integrity sha512-QRGy8ADi4J7ii95xz4UoiymmmMd/zuy9azCaamnZ3FM8T5fZcex8UfJcjkiEZjJSztKfEBe3dZ5T/5RHAmw2mA== dependencies: - "@typescript-eslint/types" "8.13.0" - "@typescript-eslint/visitor-keys" "8.13.0" + "@typescript-eslint/types" "8.15.0" + "@typescript-eslint/visitor-keys" "8.15.0" -"@typescript-eslint/type-utils@8.13.0": - version "8.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.13.0.tgz#8c8fa68490dcb9ae1687ffc7de8fbe23c26417bd" - integrity sha512-Rqnn6xXTR316fP4D2pohZenJnp+NwQ1mo7/JM+J1LWZENSLkJI8ID8QNtlvFeb0HnFSK94D6q0cnMX6SbE5/vA== +"@typescript-eslint/type-utils@8.15.0": + version "8.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.15.0.tgz#a6da0f93aef879a68cc66c73fe42256cb7426c72" + integrity sha512-UU6uwXDoI3JGSXmcdnP5d8Fffa2KayOhUUqr/AiBnG1Gl7+7ut/oyagVeSkh7bxQ0zSXV9ptRh/4N15nkCqnpw== dependencies: - "@typescript-eslint/typescript-estree" "8.13.0" - "@typescript-eslint/utils" "8.13.0" + "@typescript-eslint/typescript-estree" "8.15.0" + "@typescript-eslint/utils" "8.15.0" debug "^4.3.4" ts-api-utils "^1.3.0" -"@typescript-eslint/types@8.13.0": - version "8.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.13.0.tgz#3f35dead2b2491a04339370dcbcd17bbdfc204d8" - integrity sha512-4cyFErJetFLckcThRUFdReWJjVsPCqyBlJTi6IDEpc1GWCIIZRFxVppjWLIMcQhNGhdWJJRYFHpHoDWvMlDzng== +"@typescript-eslint/types@8.15.0": + version "8.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.15.0.tgz#4958edf3d83e97f77005f794452e595aaf6430fc" + integrity sha512-n3Gt8Y/KyJNe0S3yDCD2RVKrHBC4gTUcLTebVBXacPy091E6tNspFLKRXlk3hwT4G55nfr1n2AdFqi/XMxzmPQ== -"@typescript-eslint/typescript-estree@8.13.0": - version "8.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.13.0.tgz#db8c93dd5437ca3ce417a255fb35ddc3c12c3e95" - integrity sha512-v7SCIGmVsRK2Cy/LTLGN22uea6SaUIlpBcO/gnMGT/7zPtxp90bphcGf4fyrCQl3ZtiBKqVTG32hb668oIYy1g== +"@typescript-eslint/typescript-estree@8.15.0": + version "8.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.15.0.tgz#915c94e387892b114a2a2cc0df2d7f19412c8ba7" + integrity sha512-1eMp2JgNec/niZsR7ioFBlsh/Fk0oJbhaqO0jRyQBMgkz7RrFfkqF9lYYmBoGBaSiLnu8TAPQTwoTUiSTUW9dg== dependencies: - "@typescript-eslint/types" "8.13.0" - "@typescript-eslint/visitor-keys" "8.13.0" + "@typescript-eslint/types" "8.15.0" + "@typescript-eslint/visitor-keys" "8.15.0" debug "^4.3.4" fast-glob "^3.3.2" is-glob "^4.0.3" @@ -1099,28 +1099,28 @@ semver "^7.6.0" ts-api-utils "^1.3.0" -"@typescript-eslint/utils@8.13.0": - version "8.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.13.0.tgz#f6d40e8b5053dcaeabbd2e26463857abf27d62c0" - integrity sha512-A1EeYOND6Uv250nybnLZapeXpYMl8tkzYUxqmoKAWnI4sei3ihf2XdZVd+vVOmHGcp3t+P7yRrNsyyiXTvShFQ== +"@typescript-eslint/utils@8.15.0": + version "8.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.15.0.tgz#ac04679ad19252776b38b81954b8e5a65567cef6" + integrity sha512-k82RI9yGhr0QM3Dnq+egEpz9qB6Un+WLYhmoNcvl8ltMEededhh7otBVVIDDsEEttauwdY/hQoSsOv13lxrFzQ== dependencies: "@eslint-community/eslint-utils" "^4.4.0" - "@typescript-eslint/scope-manager" "8.13.0" - "@typescript-eslint/types" "8.13.0" - "@typescript-eslint/typescript-estree" "8.13.0" + "@typescript-eslint/scope-manager" "8.15.0" + "@typescript-eslint/types" "8.15.0" + "@typescript-eslint/typescript-estree" "8.15.0" -"@typescript-eslint/visitor-keys@8.13.0": - version "8.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.13.0.tgz#e97b0d92b266ef38a1faf40a74da289b66683a5b" - integrity sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw== +"@typescript-eslint/visitor-keys@8.15.0": + version "8.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.15.0.tgz#9ea5a85eb25401d2aa74ec8a478af4e97899ea12" + integrity sha512-h8vYOulWec9LhpwfAdZf2bjr8xIp0KNKnpgqSz0qqYYKAW/QZKw3ktRndbiAtUz4acH4QLQavwZBYCc0wulA/Q== dependencies: - "@typescript-eslint/types" "8.13.0" - eslint-visitor-keys "^3.4.3" + "@typescript-eslint/types" "8.15.0" + eslint-visitor-keys "^4.2.0" "@vitest/coverage-v8@^2.1.3": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-2.1.4.tgz#c0df11cda12b3a04570e8065754917d35baa0c55" - integrity sha512-FPKQuJfR6VTfcNMcGpqInmtJuVXFSCd9HQltYncfR01AzXhLucMEtQ5SinPdZxsT5x/5BK7I5qFJ5/ApGCmyTQ== + version "2.1.5" + resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-2.1.5.tgz#74ef3bf6737f9897a54af22f820d90e85883ff83" + integrity sha512-/RoopB7XGW7UEkUndRXF87A9CwkoZAJW01pj8/3pgmDVsjMH2IKy6H1A38po9tmUlwhSyYs0az82rbKd9Yaynw== dependencies: "@ampproject/remapping" "^2.3.0" "@bcoe/v8-coverage" "^0.2.3" @@ -1131,115 +1131,115 @@ istanbul-reports "^3.1.7" magic-string "^0.30.12" magicast "^0.3.5" - std-env "^3.7.0" + std-env "^3.8.0" test-exclude "^7.0.1" tinyrainbow "^1.2.0" -"@vitest/expect@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.1.4.tgz#48f4f53a01092a3bdc118cff245f79ef388bdd8e" - integrity sha512-DOETT0Oh1avie/D/o2sgMHGrzYUFFo3zqESB2Hn70z6QB1HrS2IQ9z5DfyTqU8sg4Bpu13zZe9V4+UTNQlUeQA== +"@vitest/expect@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.1.5.tgz#5a6afa6314cae7a61847927bb5bc038212ca7381" + integrity sha512-nZSBTW1XIdpZvEJyoP/Sy8fUg0b8od7ZpGDkTUcfJ7wz/VoZAFzFfLyxVxGFhUjJzhYqSbIpfMtl/+k/dpWa3Q== dependencies: - "@vitest/spy" "2.1.4" - "@vitest/utils" "2.1.4" + "@vitest/spy" "2.1.5" + "@vitest/utils" "2.1.5" chai "^5.1.2" tinyrainbow "^1.2.0" -"@vitest/mocker@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-2.1.4.tgz#0dc07edb9114f7f080a0181fbcdb16cd4a2d855d" - integrity sha512-Ky/O1Lc0QBbutJdW0rqLeFNbuLEyS+mIPiNdlVlp2/yhJ0SbyYqObS5IHdhferJud8MbbwMnexg4jordE5cCoQ== +"@vitest/mocker@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-2.1.5.tgz#54ee50648bc0bb606dfc58e13edfacb8b9208324" + integrity sha512-XYW6l3UuBmitWqSUXTNXcVBUCRytDogBsWuNXQijc00dtnU/9OqpXWp4OJroVrad/gLIomAq9aW8yWDBtMthhQ== dependencies: - "@vitest/spy" "2.1.4" + "@vitest/spy" "2.1.5" estree-walker "^3.0.3" magic-string "^0.30.12" -"@vitest/pretty-format@2.1.4", "@vitest/pretty-format@^2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.4.tgz#fc31993bdc1ef5a6c1a4aa6844e7ba55658a4f9f" - integrity sha512-L95zIAkEuTDbUX1IsjRl+vyBSLh3PwLLgKpghl37aCK9Jvw0iP+wKwIFhfjdUtA2myLgjrG6VU6JCFLv8q/3Ww== +"@vitest/pretty-format@2.1.5", "@vitest/pretty-format@^2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.5.tgz#bc79b8826d4a63dc04f2a75d2944694039fa50aa" + integrity sha512-4ZOwtk2bqG5Y6xRGHcveZVr+6txkH7M2e+nPFd6guSoN638v/1XQ0K06eOpi0ptVU/2tW/pIU4IoPotY/GZ9fw== dependencies: tinyrainbow "^1.2.0" -"@vitest/runner@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-2.1.4.tgz#f9346500bdd0be1c926daaac5d683bae87ceda2c" - integrity sha512-sKRautINI9XICAMl2bjxQM8VfCMTB0EbsBc/EDFA57V6UQevEKY/TOPOF5nzcvCALltiLfXWbq4MaAwWx/YxIA== +"@vitest/runner@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-2.1.5.tgz#4d5e2ba2dfc0af74e4b0f9f3f8be020559b26ea9" + integrity sha512-pKHKy3uaUdh7X6p1pxOkgkVAFW7r2I818vHDthYLvUyjRfkKOU6P45PztOch4DZarWQne+VOaIMwA/erSSpB9g== dependencies: - "@vitest/utils" "2.1.4" + "@vitest/utils" "2.1.5" pathe "^1.1.2" -"@vitest/snapshot@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-2.1.4.tgz#ef8c3f605fbc23a32773256d37d3fdfd9b23d353" - integrity sha512-3Kab14fn/5QZRog5BPj6Rs8dc4B+mim27XaKWFWHWA87R56AKjHTGcBFKpvZKDzC4u5Wd0w/qKsUIio3KzWW4Q== +"@vitest/snapshot@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-2.1.5.tgz#a09a8712547452a84e08b3ec97b270d9cc156b4f" + integrity sha512-zmYw47mhfdfnYbuhkQvkkzYroXUumrwWDGlMjpdUr4jBd3HZiV2w7CQHj+z7AAS4VOtWxI4Zt4bWt4/sKcoIjg== dependencies: - "@vitest/pretty-format" "2.1.4" + "@vitest/pretty-format" "2.1.5" magic-string "^0.30.12" pathe "^1.1.2" -"@vitest/spy@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.1.4.tgz#4e90f9783437c5841a27c80f8fd84d7289a6100a" - integrity sha512-4JOxa+UAizJgpZfaCPKK2smq9d8mmjZVPMt2kOsg/R8QkoRzydHH1qHxIYNvr1zlEaFj4SXiaaJWxq/LPLKaLg== +"@vitest/spy@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.1.5.tgz#f790d1394a5030644217ce73562e92465e83147e" + integrity sha512-aWZF3P0r3w6DiYTVskOYuhBc7EMc3jvn1TkBg8ttylFFRqNN2XGD7V5a4aQdk6QiUzZQ4klNBSpCLJgWNdIiNw== dependencies: tinyspy "^3.0.2" -"@vitest/utils@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.4.tgz#6d67ac966647a21ce8bc497472ce230de3b64537" - integrity sha512-MXDnZn0Awl2S86PSNIim5PWXgIAx8CIkzu35mBdSApUip6RFOGXBCf3YFyeEu8n1IHk4bWD46DeYFu9mQlFIRg== +"@vitest/utils@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.5.tgz#0e19ce677c870830a1573d33ee86b0d6109e9546" + integrity sha512-yfj6Yrp0Vesw2cwJbP+cl04OC+IHFsuQsrsJBL9pyGeQXE56v1UAOQco+SR55Vf1nQzfV0QJg1Qum7AaWUwwYg== dependencies: - "@vitest/pretty-format" "2.1.4" + "@vitest/pretty-format" "2.1.5" loupe "^3.1.2" tinyrainbow "^1.2.0" -"@vue/compiler-core@3.5.12": - version "3.5.12" - resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.12.tgz#bd70b7dabd12b0b6f31bc53418ba3da77994c437" - integrity sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw== +"@vue/compiler-core@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.13.tgz#b0ae6c4347f60c03e849a05d34e5bf747c9bda05" + integrity sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q== dependencies: "@babel/parser" "^7.25.3" - "@vue/shared" "3.5.12" + "@vue/shared" "3.5.13" entities "^4.5.0" estree-walker "^2.0.2" source-map-js "^1.2.0" -"@vue/compiler-dom@3.5.12": - version "3.5.12" - resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz#456d631d11102535b7ee6fd954cf2c93158d0354" - integrity sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg== +"@vue/compiler-dom@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz#bb1b8758dbc542b3658dda973b98a1c9311a8a58" + integrity sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA== dependencies: - "@vue/compiler-core" "3.5.12" - "@vue/shared" "3.5.12" + "@vue/compiler-core" "3.5.13" + "@vue/shared" "3.5.13" "@vue/compiler-sfc@^3.2.37": - version "3.5.12" - resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.12.tgz#6688120d905fcf22f7e44d3cb90f8dabc4dd3cc8" - integrity sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw== + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz#461f8bd343b5c06fac4189c4fef8af32dea82b46" + integrity sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ== dependencies: "@babel/parser" "^7.25.3" - "@vue/compiler-core" "3.5.12" - "@vue/compiler-dom" "3.5.12" - "@vue/compiler-ssr" "3.5.12" - "@vue/shared" "3.5.12" + "@vue/compiler-core" "3.5.13" + "@vue/compiler-dom" "3.5.13" + "@vue/compiler-ssr" "3.5.13" + "@vue/shared" "3.5.13" estree-walker "^2.0.2" magic-string "^0.30.11" - postcss "^8.4.47" + postcss "^8.4.48" source-map-js "^1.2.0" -"@vue/compiler-ssr@3.5.12": - version "3.5.12" - resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz#5f1a3fbd5c44b79a6dbe88729f7801d9c9218bde" - integrity sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA== +"@vue/compiler-ssr@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz#e771adcca6d3d000f91a4277c972a996d07f43ba" + integrity sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA== dependencies: - "@vue/compiler-dom" "3.5.12" - "@vue/shared" "3.5.12" + "@vue/compiler-dom" "3.5.13" + "@vue/shared" "3.5.13" -"@vue/shared@3.5.12": - version "3.5.12" - resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.12.tgz#f9e45b7f63f2c3f40d84237b1194b7f67de192e3" - integrity sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg== +"@vue/shared@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.13.tgz#87b309a6379c22b926e696893237826f64339b6f" + integrity sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ== acorn-jsx@^5.3.2: version "5.3.2" @@ -1490,9 +1490,9 @@ camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001669: - version "1.0.30001679" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001679.tgz#18c573b72f72ba70822194f6c39e7888597f9e32" - integrity sha512-j2YqID/YwpLnKzCmBOS4tlZdWprXm3ZmQLBH9ZBXFOhoxLA46fwyBvx6toCBWBmnuwUY/qB3kEU6gFx8qgCroA== + version "1.0.30001683" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001683.tgz#7f026a2d5d319a9cf8915a1451173052caaadc81" + integrity sha512-iqmNnThZ0n70mNwvxpEC2nBJ037ZHZUoBI5Gorh1Mw6IlEAZujEoU1tXA628iZfzm7R9FvFzxbfdgml82a3k8Q== ccount@^2.0.0: version "2.0.1" @@ -1569,9 +1569,9 @@ ci-info@^3.2.0: integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== ci-info@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.0.0.tgz#65466f8b280fc019b9f50a5388115d17a63a44f2" - integrity sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg== + version "4.1.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.1.0.tgz#92319d2fa29d2620180ea5afed31f589bc98cf83" + integrity sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A== cjs-module-lexer@^1.0.0: version "1.4.1" @@ -1705,10 +1705,10 @@ create-jest@^29.7.0: jest-util "^29.7.0" prompts "^2.0.1" -cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: - version "7.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.5.tgz#910aac880ff5243da96b728bc6521a5f6c2f2f82" - integrity sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug== +cross-spawn@^7.0.0, cross-spawn@^7.0.3, cross-spawn@^7.0.5: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" @@ -1846,9 +1846,9 @@ ejs@^3.1.10: jake "^10.8.5" electron-to-chromium@^1.5.41: - version "1.5.55" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.55.tgz#73684752aa2e1aa49cafb355a41386c6637e76a9" - integrity sha512-6maZ2ASDOTBtjt9FhqYPRnbvKU5tjG0IN9SztUOWYw2AzNDNpKJYLJmlK0/En4Hs/aiWnB+JZ+gW19PIGszgKg== + version "1.5.64" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.64.tgz#ac8c4c89075d35a1514b620f47dfe48a71ec3697" + integrity sha512-IXEuxU+5ClW2IGEYFC2T7szbyVgehupCWQe5GNh+H065CD6U6IFN0s4KeAMFGNmQolRU4IV7zGBWSYMmZ8uuqQ== emittery@^0.13.1: version "0.13.1" @@ -1877,6 +1877,11 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +es-module-lexer@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78" + integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw== + esbuild@^0.21.3: version "0.21.5" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" @@ -1945,9 +1950,9 @@ eslint-plugin-prettier@^5.1.3: synckit "^0.9.1" eslint-plugin-unicorn@^56.0.0: - version "56.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-56.0.0.tgz#9fd3ebe6f478571734541fa745026b743175b59e" - integrity sha512-aXpddVz/PQMmd69uxO98PA4iidiVNvA0xOtbpUoz1WhBd4RxOQQYqN618v68drY0hmy5uU2jy1bheKEVWBjlPw== + version "56.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-56.0.1.tgz#d10a3df69ba885939075bdc95a65a0c872e940d4" + integrity sha512-FwVV0Uwf8XPfVnKSGpMg7NtlZh0G0gBarCaFcMUOoqPxXryxdYxTRRv4kH6B9TFCVIrjRXG+emcxIk2ayZilog== dependencies: "@babel/helper-validator-identifier" "^7.24.7" "@eslint-community/eslint-utils" "^4.4.0" @@ -1985,25 +1990,25 @@ eslint-visitor-keys@^4.2.0: integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== eslint@^9.7.0: - version "9.14.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.14.0.tgz#534180a97c00af08bcf2b60b0ebf0c4d6c1b2c95" - integrity sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g== + version "9.15.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.15.0.tgz#77c684a4e980e82135ebff8ee8f0a9106ce6b8a6" + integrity sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.12.1" - "@eslint/config-array" "^0.18.0" - "@eslint/core" "^0.7.0" - "@eslint/eslintrc" "^3.1.0" - "@eslint/js" "9.14.0" - "@eslint/plugin-kit" "^0.2.0" + "@eslint/config-array" "^0.19.0" + "@eslint/core" "^0.9.0" + "@eslint/eslintrc" "^3.2.0" + "@eslint/js" "9.15.0" + "@eslint/plugin-kit" "^0.2.3" "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" - "@humanwhocodes/retry" "^0.4.0" + "@humanwhocodes/retry" "^0.4.1" "@types/estree" "^1.0.6" "@types/json-schema" "^7.0.15" ajv "^6.12.4" chalk "^4.0.0" - cross-spawn "^7.0.2" + cross-spawn "^7.0.5" debug "^4.3.2" escape-string-regexp "^4.0.0" eslint-scope "^8.2.0" @@ -2023,7 +2028,6 @@ eslint@^9.7.0: minimatch "^3.1.2" natural-compare "^1.4.0" optionator "^0.9.3" - text-table "^0.2.0" espree@^10.0.1, espree@^10.3.0: version "10.3.0" @@ -2220,9 +2224,9 @@ flat-cache@^4.0.0: keyv "^4.5.4" flatted@^3.2.9: - version "3.3.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" - integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + version "3.3.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.2.tgz#adba1448a9841bec72b42c532ea23dbbedef1a27" + integrity sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA== foreground-child@^3.1.0: version "3.3.0" @@ -3337,9 +3341,9 @@ lru-cache@^6.0.0: yallist "^4.0.0" magic-string@^0.30.11, magic-string@^0.30.12: - version "0.30.12" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.12.tgz#9eb11c9d072b9bcb4940a5b2c2e1a217e4ee1a60" - integrity sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw== + version "0.30.13" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.13.tgz#92438e3ff4946cf54f18247c981e5c161c46683c" + integrity sha512-8rYBO+MsWkgjDSOvLomYnzhdwEG51olQ4zL5KXnNJWV5MNmrb4rTZdrtkhxjnD/QyZUqR/Z/XDsUs/4ej2nx0g== dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" @@ -4159,7 +4163,7 @@ pathval@^2.0.0: resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25" integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA== -picocolors@^1.0.0, picocolors@^1.1.0: +picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== @@ -4191,13 +4195,13 @@ pluralize@^8.0.0: resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== -postcss@^8.4.43, postcss@^8.4.47: - version "8.4.47" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365" - integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ== +postcss@^8.4.43, postcss@^8.4.48: + version "8.4.49" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19" + integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA== dependencies: nanoid "^3.3.7" - picocolors "^1.1.0" + picocolors "^1.1.1" source-map-js "^1.2.1" prelude-ls@^1.2.1: @@ -4458,30 +4462,30 @@ rimraf@^6.0.1: package-json-from-dist "^1.0.0" rollup@^4.20.0: - version "4.25.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.25.0.tgz#74dff4b5c2777dfc490f9711393925da50171787" - integrity sha512-uVbClXmR6wvx5R1M3Od4utyLUxrmOcEm3pAtMphn73Apq19PDtHpgZoEvqH2YnnaNUuvKmg2DgRd2Sqv+odyqg== + version "4.27.4" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.27.4.tgz#b23e4ef4fe4d0d87f5237dacf63f95a499503897" + integrity sha512-RLKxqHEMjh/RGLsDxAEsaLO3mWgyoU6x9w6n1ikAzet4B3gI2/3yP6PWY2p9QzRTh6MfEIXB3MwsOY0Iv3vNrw== dependencies: "@types/estree" "1.0.6" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.25.0" - "@rollup/rollup-android-arm64" "4.25.0" - "@rollup/rollup-darwin-arm64" "4.25.0" - "@rollup/rollup-darwin-x64" "4.25.0" - "@rollup/rollup-freebsd-arm64" "4.25.0" - "@rollup/rollup-freebsd-x64" "4.25.0" - "@rollup/rollup-linux-arm-gnueabihf" "4.25.0" - "@rollup/rollup-linux-arm-musleabihf" "4.25.0" - "@rollup/rollup-linux-arm64-gnu" "4.25.0" - "@rollup/rollup-linux-arm64-musl" "4.25.0" - "@rollup/rollup-linux-powerpc64le-gnu" "4.25.0" - "@rollup/rollup-linux-riscv64-gnu" "4.25.0" - "@rollup/rollup-linux-s390x-gnu" "4.25.0" - "@rollup/rollup-linux-x64-gnu" "4.25.0" - "@rollup/rollup-linux-x64-musl" "4.25.0" - "@rollup/rollup-win32-arm64-msvc" "4.25.0" - "@rollup/rollup-win32-ia32-msvc" "4.25.0" - "@rollup/rollup-win32-x64-msvc" "4.25.0" + "@rollup/rollup-android-arm-eabi" "4.27.4" + "@rollup/rollup-android-arm64" "4.27.4" + "@rollup/rollup-darwin-arm64" "4.27.4" + "@rollup/rollup-darwin-x64" "4.27.4" + "@rollup/rollup-freebsd-arm64" "4.27.4" + "@rollup/rollup-freebsd-x64" "4.27.4" + "@rollup/rollup-linux-arm-gnueabihf" "4.27.4" + "@rollup/rollup-linux-arm-musleabihf" "4.27.4" + "@rollup/rollup-linux-arm64-gnu" "4.27.4" + "@rollup/rollup-linux-arm64-musl" "4.27.4" + "@rollup/rollup-linux-powerpc64le-gnu" "4.27.4" + "@rollup/rollup-linux-riscv64-gnu" "4.27.4" + "@rollup/rollup-linux-s390x-gnu" "4.27.4" + "@rollup/rollup-linux-x64-gnu" "4.27.4" + "@rollup/rollup-linux-x64-musl" "4.27.4" + "@rollup/rollup-win32-arm64-msvc" "4.27.4" + "@rollup/rollup-win32-ia32-msvc" "4.27.4" + "@rollup/rollup-win32-x64-msvc" "4.27.4" fsevents "~2.3.2" run-parallel@^1.1.9: @@ -4626,7 +4630,7 @@ standard-changelog@^6.0.0: meow "^13.0.0" picocolors "^1.0.0" -std-env@^3.7.0: +std-env@^3.8.0: version "3.8.0" resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5" integrity sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w== @@ -4772,11 +4776,6 @@ test-exclude@^7.0.1: glob "^10.4.1" minimatch "^9.0.4" -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== - tinybench@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" @@ -4788,9 +4787,9 @@ tinyexec@^0.3.1: integrity sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ== tinypool@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.1.tgz#c64233c4fac4304e109a64340178760116dbe1fe" - integrity sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA== + version "1.0.2" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.2.tgz#706193cc532f4c100f66aa00b01c42173d9051b2" + integrity sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA== tinyrainbow@^1.2.0: version "1.2.0" @@ -4882,23 +4881,23 @@ type-fest@^2.0.0, type-fest@^2.5.0: integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== type-fest@^4.6.0, type-fest@^4.7.1: - version "4.26.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.26.1.tgz#a4a17fa314f976dd3e6d6675ef6c775c16d7955e" - integrity sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg== + version "4.27.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.27.1.tgz#618f21dfa02281b0e532fe3cf8f848f076ee2ca8" + integrity sha512-3Ta7CyV6daqpwuGJMJKABaUChZZejpzysZkQg1//bLRg2wKQ4duwsg3MMIsHuElq58iDqizg4DBUmK8H8wExJg== typescript-eslint@^8.8.1: - version "8.13.0" - resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.13.0.tgz#c7d92cc06188176c7d0e3825e10305b9c22fb102" - integrity sha512-vIMpDRJrQd70au2G8w34mPps0ezFSPMEX4pXkTzUkrNbRX+36ais2ksGWN0esZL+ZMaFJEneOBHzCgSqle7DHw== + version "8.15.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.15.0.tgz#c8a2a0d183c3eb48ae176aa078c1b9daa584cf9d" + integrity sha512-wY4FRGl0ZI+ZU4Jo/yjdBu0lVTSML58pu6PgGtJmCufvzfV565pUF6iACQt092uFOd49iLOTX/sEVmHtbSrS+w== dependencies: - "@typescript-eslint/eslint-plugin" "8.13.0" - "@typescript-eslint/parser" "8.13.0" - "@typescript-eslint/utils" "8.13.0" + "@typescript-eslint/eslint-plugin" "8.15.0" + "@typescript-eslint/parser" "8.15.0" + "@typescript-eslint/utils" "8.15.0" typescript@^5.3.3: - version "5.6.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" - integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== + version "5.7.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6" + integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg== uglify-js@^3.1.4: version "3.19.3" @@ -5081,20 +5080,21 @@ vfile@^5.0.0, vfile@^5.3.4: unist-util-stringify-position "^3.0.0" vfile-message "^3.0.0" -vite-node@2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-2.1.4.tgz#97ffb6de913fd8d42253afe441f9512e9dbdfd5c" - integrity sha512-kqa9v+oi4HwkG6g8ufRnb5AeplcRw8jUF6/7/Qz1qRQOXHImG8YnLbB+LLszENwFnoBl9xIf9nVdCFzNd7GQEg== +vite-node@2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-2.1.5.tgz#cf28c637b2ebe65921f3118a165b7cf00a1cdf19" + integrity sha512-rd0QIgx74q4S1Rd56XIiL2cYEdyWn13cunYBIuqh9mpmQr7gGS0IxXoP8R6OaZtNQQLyXSWbd4rXKYUbhFpK5w== dependencies: cac "^6.7.14" debug "^4.3.7" + es-module-lexer "^1.5.4" pathe "^1.1.2" vite "^5.0.0" vite@^5.0.0: - version "5.4.10" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.10.tgz#d358a7bd8beda6cf0f3b7a450a8c7693a4f80c18" - integrity sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ== + version "5.4.11" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.11.tgz#3b415cd4aed781a356c1de5a9ebafb837715f6e5" + integrity sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q== dependencies: esbuild "^0.21.3" postcss "^8.4.43" @@ -5103,29 +5103,29 @@ vite@^5.0.0: fsevents "~2.3.3" vitest@^2.1.3: - version "2.1.4" - resolved "https://registry.yarnpkg.com/vitest/-/vitest-2.1.4.tgz#ba8f4589fb639cf5a9e6af54781667312b3e8230" - integrity sha512-eDjxbVAJw1UJJCHr5xr/xM86Zx+YxIEXGAR+bmnEID7z9qWfoxpHw0zdobz+TQAFOLT+nEXz3+gx6nUJ7RgmlQ== - dependencies: - "@vitest/expect" "2.1.4" - "@vitest/mocker" "2.1.4" - "@vitest/pretty-format" "^2.1.4" - "@vitest/runner" "2.1.4" - "@vitest/snapshot" "2.1.4" - "@vitest/spy" "2.1.4" - "@vitest/utils" "2.1.4" + version "2.1.5" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-2.1.5.tgz#a93b7b84a84650130727baae441354e6df118148" + integrity sha512-P4ljsdpuzRTPI/kbND2sDZ4VmieerR2c9szEZpjc+98Z9ebvnXmM5+0tHEKqYZumXqlvnmfWsjeFOjXVriDG7A== + dependencies: + "@vitest/expect" "2.1.5" + "@vitest/mocker" "2.1.5" + "@vitest/pretty-format" "^2.1.5" + "@vitest/runner" "2.1.5" + "@vitest/snapshot" "2.1.5" + "@vitest/spy" "2.1.5" + "@vitest/utils" "2.1.5" chai "^5.1.2" debug "^4.3.7" expect-type "^1.1.0" magic-string "^0.30.12" pathe "^1.1.2" - std-env "^3.7.0" + std-env "^3.8.0" tinybench "^2.9.0" tinyexec "^0.3.1" tinypool "^1.0.1" tinyrainbow "^1.2.0" vite "^5.0.0" - vite-node "2.1.4" + vite-node "2.1.5" why-is-node-running "^2.3.0" vue-template-compiler@^2.7.8: From c1629205bced9007d8440b35685a4035b4572456 Mon Sep 17 00:00:00 2001 From: Colin Date: Sat, 23 Nov 2024 12:57:10 -0500 Subject: [PATCH 3/5] Wow --- src/index.ts | 6 +- src/parse.ts | 37 +- test/__snapshots__/parse.test.ts.snap | 1721 ++++++++++++++++++++----- test/parse.test.ts | 303 ++--- 4 files changed, 1572 insertions(+), 495 deletions(-) diff --git a/src/index.ts b/src/index.ts index cdc2bee..a7e9961 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import VCF from './parse' +import VCFParser from './parse' export interface Breakend { Join: string @@ -77,4 +77,6 @@ export function parseBreakend(breakendString: string): Breakend | undefined { return undefined } -export default VCF +export default VCFParser + +export type { Variant } from './parse' diff --git a/src/parse.ts b/src/parse.ts index e00416d..4d7d00c 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -20,7 +20,7 @@ function decodeURIComponentNoThrow(uri: string) { * @param {boolean} args.strict - Whether to parse in strict mode or not * (default true) */ -export default class VCF { +export default class VCFParser { private metadata: Record public strict: boolean public samples: string[] @@ -88,10 +88,9 @@ export default class VCF { } private parseSamples(format: string, prerest: string) { - console.log('wtf1') const genotypes = {} as Record< string, - Record + Record > if (format) { const rest = prerest.split('\t') @@ -108,11 +107,11 @@ export default class VCF { const val = columns[j]! genotypes[sample][formatKeys[j]!] = val === '' || val === '.' - ? null + ? undefined : val .split(',') .map(ent => - ent === '.' ? null : isNumberType[j] ? +ent : ent, + ent === '.' ? undefined : isNumberType[j] ? +ent : ent, ) } } @@ -121,7 +120,6 @@ export default class VCF { } private parseGenotypesOnly(format: string, prerest: string) { - console.log('wtf2') const rest = prerest.split('\t') const genotypes = {} as Record let i = 0 @@ -247,7 +245,7 @@ export default class VCF { } else if (s !== pairSeparator) { currKey += s } else if (currValue === '') { - data[currKey] = null + data[currKey] = undefined currKey = '' } } else if (state === 2) { @@ -274,7 +272,7 @@ export default class VCF { if (state === 2 || state === 3) { data[currKey] = currValue } else if (state === 1) { - data[currKey] = null + data[currKey] = undefined } return data } @@ -287,11 +285,6 @@ export default class VCF { * CRLF newlines. */ parseLine(line: string) { - line = line.trim() - if (!line.length) { - return undefined - } - let currChar = 0 for (let currField = 0; currChar < line.length; currChar += 1) { if (line[currChar] === '\t') { @@ -307,13 +300,13 @@ export default class VCF { const [CHROM, POS, ID, REF, ALT, QUAL, FILTER] = fields const chrom = CHROM const pos = +POS! - const id = ID === '.' ? null : ID!.split(';') + const id = ID === '.' ? undefined : ID!.split(';') const ref = REF - const alt = ALT === '.' ? null : ALT!.split(',') - const qual = QUAL === '.' ? null : +QUAL! - const filter = FILTER === '.' ? null : FILTER!.split(';') + const alt = ALT === '.' ? undefined : ALT!.split(',') + const qual = QUAL === '.' ? undefined : +QUAL! + const filter = FILTER === '.' ? undefined : FILTER!.split(';') - if (this.strict && fields[7] === undefined) { + if (this.strict && !fields[7]) { throw new Error( "no INFO field specified, must contain at least a '.' (turn off strict mode to allow)", ) @@ -328,7 +321,7 @@ export default class VCF { if (info[key]) { items = (info[key] as string) .split(',') - .map(val => (val === '.' ? null : val)) + .map(val => (val === '.' ? undefined : val)) .map(f => (f ? decodeURIComponentNoThrow(f) : f)) } else { // it will be falsy so just assign whatever is there @@ -337,8 +330,8 @@ export default class VCF { const itemType = this.getMetadata('INFO', key, 'Type') if (itemType) { if (itemType === 'Integer' || itemType === 'Float') { - items = items.map((val: string | null) => - val === null ? null : Number(val), + items = items.map((val: string | undefined) => + val === undefined ? undefined : Number(val), ) } else if (itemType === 'Flag') { if (info[key]) { @@ -368,3 +361,5 @@ export default class VCF { } } } + +export type Variant = ReturnType diff --git a/test/__snapshots__/parse.test.ts.snap b/test/__snapshots__/parse.test.ts.snap index e47be48..4aac890 100644 --- a/test/__snapshots__/parse.test.ts.snap +++ b/test/__snapshots__/parse.test.ts.snap @@ -1,160 +1,5 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Obscure VCF > vcf lines with weird info field and missing format/genotypes 1`] = ` -[ - Variant { - "ALT": [ - "A", - ], - "CHROM": "lcl|Scaffald_1", - "FILTER": "PASS", - "ID": [ - "rs118266897", - ], - "INFO": { - "0,14": null, - "112": null, - "AF": [ - 0.5, - ], - "DB": true, - "NS": [ - 3, - ], - "PG2.1": null, - }, - "POS": 80465, - "QUAL": 29, - "REF": "R", - }, - Variant { - "ALT": [ - "G", - ], - "CHROM": "lcl|Scaffald_1", - "FILTER": "PASS", - "ID": [ - "rs118269296", - ], - "INFO": { - "0,14": null, - "112": null, - "AF": [ - 0.5, - ], - "DB": true, - "NS": [ - 3, - ], - "PG2.1": null, - }, - "POS": 84818, - "QUAL": 29, - "REF": "R", - }, - Variant { - "ALT": [ - "T", - ], - "CHROM": "lcl|Scaffald_1", - "FILTER": "PASS", - "ID": [ - "rs118218236", - ], - "INFO": { - "0,14": null, - "112": null, - "AF": [ - 0.5, - ], - "DB": true, - "NS": [ - 3, - ], - "PG2.1": null, - }, - "POS": 95414, - "QUAL": 29, - "REF": "W", - }, - Variant { - "ALT": [ - "A", - ], - "CHROM": "lcl|Scaffald_1", - "FILTER": "PASS", - "ID": [ - "rs118264755", - ], - "INFO": { - "0,14": null, - "112": null, - "AF": [ - 0.5, - ], - "DB": true, - "NS": [ - 3, - ], - "PG2.1": null, - }, - "POS": 231384, - "QUAL": 29, - "REF": "R", - }, - Variant { - "ALT": [ - "G", - ], - "CHROM": "lcl|Scaffald_1", - "FILTER": "PASS", - "ID": [ - "rs118223336", - ], - "INFO": { - "0,14": null, - "112": null, - "AF": [ - 6.5, - ], - "DB": true, - "NS": [ - 3, - ], - "PG2.1": null, - }, - "POS": 236429, - "QUAL": 29, - "REF": "R", - }, - Variant { - "ALT": [ - "G", - ], - "CHROM": "lcl|Scaffald_1", - "FILTER": "PASS", - "ID": [ - "rs118217257", - ], - "INFO": { - "0,14": null, - "112": null, - "AF": [ - 0.5, - ], - "DB": true, - "NS": [ - 3, - ], - "PG2.1": null, - }, - "POS": 245378, - "QUAL": 29, - "REF": "R", - }, -] -`; - exports[`VCF parser > can get metadata from the header 1`] = ` { "ALT": { @@ -749,12 +594,13 @@ exports[`VCF parser > can get metadata from the header 2`] = ` `; exports[`VCF parser > can parse a line from the VCF spec 1`] = ` -Variant { +{ "ALT": [ "A", ], "CHROM": "20", "FILTER": "PASS", + "GENOTYPES": [Function], "ID": [ "rs6054257", ], @@ -774,6 +620,7 @@ Variant { "POS": 14370, "QUAL": 29, "REF": "G", + "SAMPLES": [Function], } `; @@ -820,49 +667,55 @@ exports[`VCF parser > can parse a line from the VCF spec 2`] = ` "1/1", ], "HQ": [ - null, - null, + undefined, + NaN, ], }, } `; exports[`VCF parser > can parse a line with minimal entries 1`] = ` -Variant { +{ "ALT": [ "A", ], "CHROM": "20", - "FILTER": null, - "ID": null, + "FILTER": undefined, + "GENOTYPES": [Function], + "ID": undefined, "INFO": {}, "POS": 14370, - "QUAL": null, + "QUAL": undefined, "REF": "G", + "SAMPLES": [Function], } `; exports[`VCF parser > can parse a line with minimal entries 2`] = ` { "NA00001": { - "GT": null, + "GT": undefined, }, "NA00002": { - "GT": null, + "GT": undefined, }, "NA00003": { - "GT": null, + "GT": [ + ". +", + ], }, } `; exports[`VCF parser > parses a line with a breakend ALT 1`] = ` -Variant { +{ "ALT": [ "G]17:198982]", ], "CHROM": "2", "FILTER": "PASS", + "GENOTYPES": [Function], "ID": [ "bnd_W", ], @@ -874,11 +727,12 @@ Variant { "POS": 321681, "QUAL": 6, "REF": "G", + "SAMPLES": [Function], } `; exports[`VCF parser > parses a line with mix of multiple breakends and non breakends 1`] = ` -Variant { +{ "ALT": [ "CTATGTCG", "C[2 : 321682[", @@ -886,6 +740,7 @@ Variant { ], "CHROM": "13", "FILTER": "PASS", + "GENOTYPES": [Function], "ID": [ "bnd_U", ], @@ -901,16 +756,18 @@ Variant { "POS": 123456, "QUAL": 6, "REF": "C", + "SAMPLES": [Function], } `; exports[`VCF parser for Y chrom (haploid) > can parse a line from the VCF spec 1`] = ` -Variant { +{ "ALT": [ "", ], "CHROM": "Y", "FILTER": "PASS", + "GENOTYPES": [Function], "ID": [ "CNV_Y_14483990_15232198", ], @@ -956,6 +813,7 @@ Variant { "POS": 14483990, "QUAL": 100, "REF": "C", + "SAMPLES": [Function], } `; @@ -1026,35 +884,36 @@ exports[`VCF parser for Y chrom (haploid) > can parse a line from the VCF spec 2 ], }, "HG00103": { - "CN": null, - "CNL": null, - "CNP": null, - "CNQ": null, - "GP": null, - "GQ": null, - "GT": null, - "PL": null, + "CN": undefined, + "CNL": undefined, + "CNP": undefined, + "CNQ": undefined, + "GP": undefined, + "GQ": undefined, + "GT": undefined, + "PL": undefined, }, "HG001055": { - "CN": null, - "CNL": null, - "CNP": null, - "CNQ": null, - "GP": null, - "GQ": null, - "GT": null, - "PL": null, + "CN": undefined, + "CNL": undefined, + "CNP": undefined, + "CNQ": undefined, + "GP": undefined, + "GQ": undefined, + "GT": undefined, + "PL": undefined, }, } `; exports[`VCF parser for Y chrom (haploid) > can parse a line from the VCF spec 3`] = ` -Variant { +{ "ALT": [ "A", ], "CHROM": "Y", "FILTER": "PASS", + "GENOTYPES": [Function], "ID": [ "rs11575897", ], @@ -1100,6 +959,7 @@ Variant { "POS": 2655180, "QUAL": 100, "REF": "G", + "SAMPLES": [Function], } `; @@ -1121,18 +981,19 @@ exports[`VCF parser for Y chrom (haploid) > can parse a line from the VCF spec 4 ], }, "HG001055": { - "GT": null, + "GT": undefined, }, } `; exports[`VCF parser for structural variants > can parse a line from the VCF spec 1`] = ` -Variant { +{ "ALT": [ "", ], "CHROM": "8", "FILTER": "PASS", + "GENOTYPES": [Function], "ID": [ "28329_0", ], @@ -1185,8 +1046,9 @@ Variant { ], }, "POS": 17709115, - "QUAL": null, + "QUAL": undefined, "REF": "N", + "SAMPLES": [Function], } `; @@ -1206,92 +1068,1113 @@ exports[`VCF parser for structural variants > can parse a line from the VCF spec } `; -exports[`can parse breakends 1`] = ` -[ - Variant { - "ALT": [ - "G]8:107653520]", - ], - "CHROM": "11", - "FILTER": "PASS", - "ID": [ - "MantaBND:0:2:3:0:0:0:1", - ], - "INFO": { - "BND_DEPTH": [ - "216", - ], - "CIPOS": [ - 0, - 2, - ], - "HOMLEN": [ - 2, - ], - "HOMSEQ": [ - "TT", - ], - "MATEID": [ - "MantaBND:0:2:3:0:0:0:0", - ], - "MATE_BND_DEPTH": [ - "735", - ], - "SVTYPE": [ - "BND", - ], +exports[`can get metadata from the header 1`] = ` +{ + "ALT": { + "*": { + "Description": "Represents any possible alternative allele at this location", + }, + "CNV": { + "Description": "Copy number variable region (may be both deletion and duplication)", + }, + "DEL": { + "Description": "Deletion relative to the reference", + }, + "DEL:ME": { + "Description": "Deletion of mobile element relative to the reference", + }, + "DUP": { + "Description": "Region of elevated copy number relative to the reference", + }, + "DUP:TANDEM": { + "Description": "Tandem duplication", + }, + "INS": { + "Description": "Insertion of novel sequence relative to the reference", + }, + "INS:ME": { + "Description": "Insertion of a mobile element relative to the reference", + }, + "INV": { + "Description": "Inversion of reference sequence", + }, + "NON_REF": { + "Description": "Represents any possible alternative allele at this location", }, - "POS": 94975747, - "QUAL": null, - "REF": "G", }, - Variant { - "ALT": [ - "", - ], - "CHROM": "11", - "FILTER": "PASS", - "ID": [ - "MantaDEL:0:1:2:0:0:0", - ], - "INFO": { - "CIEND": [ - -150, - 150, - ], - "CIPOS": [ - -156, - 156, - ], - "END": [ - 94987865, - ], - "IMPRECISE": true, - "SVLEN": [ - 12112, - ], - "SVTYPE": [ - "DEL", - ], + "FILTER": { + "PASS": { + "Description": "Passed all filters", + }, + "q10": { + "Description": "Quality below 10", + }, + "s50": { + "Description": "Less than 50% of samples have data", }, - "POS": 94975753, - "QUAL": null, - "REF": "T", }, - Variant { - "ALT": [ - "T[8:107653411[", - ], - "CHROM": "11", - "FILTER": "PASS", - "ID": [ - "MantaBND:0:0:1:0:0:0:0", - ], - "INFO": { - "BND_DEPTH": [ - "171", - ], - "MATEID": [ + "FORMAT": { + "AD": { + "Description": "Read depth for each allele", + "Number": "R", + "Type": "Integer", + }, + "ADF": { + "Description": "Read depth for each allele on the forward strand", + "Number": "R", + "Type": "Integer", + }, + "ADR": { + "Description": "Read depth for each allele on the reverse strand", + "Number": "R", + "Type": "Integer", + }, + "DP": { + "Description": "Read Depth", + "Number": 1, + "Type": "Integer", + }, + "EC": { + "Description": "Expected alternate allele counts", + "Number": "A", + "Type": "Integer", + }, + "FT": { + "Description": "Filter indicating if this genotype was "called"", + "Number": 1, + "Type": "String", + }, + "GL": { + "Description": "Genotype likelihoods", + "Number": "G", + "Type": "Float", + }, + "GP": { + "Description": "Genotype posterior probabilities", + "Number": "G", + "Type": "Float", + }, + "GQ": { + "Description": "Genotype Quality", + "Number": 1, + "Type": "Integer", + }, + "GT": { + "Description": "Genotype", + "Number": 1, + "Type": "String", + }, + "HQ": { + "Description": "Haplotype Quality", + "Number": 2, + "Type": "Integer", + }, + "MQ": { + "Description": "RMS mapping quality", + "Number": 1, + "Type": "Integer", + }, + "PL": { + "Description": "List of Phred-scaled genotype likelihoods", + "Number": "G", + "Type": "Integer", + }, + "PQ": { + "Description": "Phasing quality", + "Number": 1, + "Type": "Integer", + }, + "PS": { + "Description": "Phase set", + "Number": 1, + "Type": "Integer", + }, + "TEST": { + "Description": "Used for testing", + "Number": 1, + "Type": "String", + }, + }, + "INFO": { + "1000G": { + "Description": "1000 Genomes membership", + "Number": 0, + "Type": "Flag", + }, + "AA": { + "Description": "Ancestral Allele", + "Number": 1, + "Type": "String", + }, + "AC": { + "Description": "Allele count in genotypes, for each ALT allele, in the same order as listed", + "Number": "A", + "Type": "Integer", + }, + "AD": { + "Description": "Total read depth for each allele", + "Number": "R", + "Type": "Integer", + }, + "ADF": { + "Description": "Read depth for each allele on the forward strand", + "Number": "R", + "Type": "Integer", + }, + "ADR": { + "Description": "Read depth for each allele on the reverse strand", + "Number": "R", + "Type": "Integer", + }, + "AF": { + "Description": "Allele Frequency", + "Number": "A", + "Type": "Float", + }, + "AN": { + "Description": "Total number of alleles in called genotypes", + "Number": 1, + "Type": "Integer", + }, + "BKPTID": { + "Description": "ID of the assembled alternate allele in the assembly file", + "Type": "String", + }, + "BQ": { + "Description": "RMS base quality", + "Number": 1, + "Type": "Float", + }, + "CICN": { + "Description": "Confidence interval around copy number for the segment", + "Number": 2, + "Type": "Integer", + }, + "CICNADJ": { + "Description": "Confidence interval around copy number for the adjacency", + "Number": null, + "Type": "Integer", + }, + "CIEND": { + "Description": "Confidence interval around END for imprecise variants", + "Number": 2, + "Type": "Integer", + }, + "CIGAR": { + "Description": "Cigar string describing how to align an alternate allele to the reference allele", + "Number": 1, + "Type": "Float", + }, + "CILEN": { + "Description": "Confidence interval around the inserted material between breakend", + "Number": 2, + "Type": "Integer", + }, + "CIPOS": { + "Description": "Confidence interval around POS for imprecise variants", + "Number": 2, + "Type": "Integer", + }, + "CN": { + "Description": "Copy number of segment containing breakend", + "Number": 1, + "Type": "Integer", + }, + "CNADJ": { + "Description": "Copy number of adjacency", + "Number": null, + "Type": "Integer", + }, + "DB": { + "Description": "dbSNP membership, build 129", + "Number": 0, + "Type": "Flag", + }, + "DBRIPID": { + "Description": "ID of this element in DBRIP", + "Number": 1, + "Type": "String", + }, + "DBVARID": { + "Description": "ID of this element in DBVAR", + "Number": 1, + "Type": "String", + }, + "DGVID": { + "Description": "ID of this element in Database of Genomic Variation", + "Number": 1, + "Type": "String", + }, + "DP": { + "Description": "Total Depth", + "Number": 1, + "Type": "Integer", + }, + "DPADJ": { + "Description": "Read Depth of adjacency", + "Type": "Integer", + }, + "END": { + "Description": "End position (for use with symbolic alleles)", + "Number": 1, + "Type": "Integer", + }, + "EVENT": { + "Description": "ID of event associated to breakend", + "Number": 1, + "Type": "String", + }, + "H2": { + "Description": "HapMap2 membership", + "Number": 0, + "Type": "Flag", + }, + "H3": { + "Description": "HapMap3 membership", + "Number": 0, + "Type": "Flag", + }, + "HOMLEN": { + "Description": "Length of base pair identical micro-homology at event breakpoints", + "Type": "Integer", + }, + "HOMSEQ": { + "Description": "Sequence of base pair identical micro-homology at event breakpoints", + "Type": "String", + }, + "IMPRECISE": { + "Description": "Imprecise structural variation", + "Number": 0, + "Type": "Flag", + }, + "MATEID": { + "Description": "ID of mate breakends", + "Number": null, + "Type": "String", + }, + "MEINFO": { + "Description": "Mobile element info of the form NAME,START,END,POLARITY", + "Number": 4, + "Type": "String", + }, + "METRANS": { + "Description": "Mobile element transduction info of the form CHR,START,END,POLARITY", + "Number": 4, + "Type": "String", + }, + "MQ": { + "Description": "RMS mapping quality", + "Number": 1, + "Type": null, + }, + "MQ0": { + "Description": "Number of MAPQ == 0 reads", + "Number": 1, + "Type": "Integer", + }, + "NOVEL": { + "Description": "Indicates a novel structural variation", + "Number": 0, + "Type": "Flag", + }, + "NS": { + "Description": "Number of Samples With Data", + "Number": 1, + "Type": "Integer", + }, + "PARID": { + "Description": "ID of partner breakend", + "Number": 1, + "Type": "String", + }, + "SB": { + "Description": "Strand bias", + "Number": 4, + "Type": "Integer", + }, + "SOMATIC": { + "Description": "Somatic mutation (for cancer genomics)", + "Number": 0, + "Type": "Flag", + }, + "SVLEN": { + "Description": "Difference in length between REF and ALT alleles", + "Number": null, + "Type": "Integer", + }, + "SVTYPE": { + "Description": "Type of structural variant", + "Number": 1, + "Type": "String", + }, + "TEST": { + "Description": "Used for testing", + "Number": 1, + "Type": "String", + }, + "VALIDATED": { + "Description": "Validated by follow-up experiment", + "Number": 0, + "Type": "Flag", + }, + }, + "contig": { + "20": { + "assembly": "B36", + "length": "62435964", + "md5": "f126cdf8a6e0c7f379d618ff66beb2da", + "species": "Homo sapiens", + "taxonomy": "x", + }, + }, + "fileDate": "20090805", + "fileformat": "VCFv4.3", + "phasing": "partial", + "reference": "file:///seq/references/1000GenomesPilot-NCBI36.fasta", + "source": "myImputationProgramV3.1", +} +`; + +exports[`can get metadata from the header 2`] = ` +{ + "1000G": { + "Description": "1000 Genomes membership", + "Number": 0, + "Type": "Flag", + }, + "AA": { + "Description": "Ancestral Allele", + "Number": 1, + "Type": "String", + }, + "AC": { + "Description": "Allele count in genotypes, for each ALT allele, in the same order as listed", + "Number": "A", + "Type": "Integer", + }, + "AD": { + "Description": "Total read depth for each allele", + "Number": "R", + "Type": "Integer", + }, + "ADF": { + "Description": "Read depth for each allele on the forward strand", + "Number": "R", + "Type": "Integer", + }, + "ADR": { + "Description": "Read depth for each allele on the reverse strand", + "Number": "R", + "Type": "Integer", + }, + "AF": { + "Description": "Allele Frequency", + "Number": "A", + "Type": "Float", + }, + "AN": { + "Description": "Total number of alleles in called genotypes", + "Number": 1, + "Type": "Integer", + }, + "BKPTID": { + "Description": "ID of the assembled alternate allele in the assembly file", + "Type": "String", + }, + "BQ": { + "Description": "RMS base quality", + "Number": 1, + "Type": "Float", + }, + "CICN": { + "Description": "Confidence interval around copy number for the segment", + "Number": 2, + "Type": "Integer", + }, + "CICNADJ": { + "Description": "Confidence interval around copy number for the adjacency", + "Number": null, + "Type": "Integer", + }, + "CIEND": { + "Description": "Confidence interval around END for imprecise variants", + "Number": 2, + "Type": "Integer", + }, + "CIGAR": { + "Description": "Cigar string describing how to align an alternate allele to the reference allele", + "Number": 1, + "Type": "Float", + }, + "CILEN": { + "Description": "Confidence interval around the inserted material between breakend", + "Number": 2, + "Type": "Integer", + }, + "CIPOS": { + "Description": "Confidence interval around POS for imprecise variants", + "Number": 2, + "Type": "Integer", + }, + "CN": { + "Description": "Copy number of segment containing breakend", + "Number": 1, + "Type": "Integer", + }, + "CNADJ": { + "Description": "Copy number of adjacency", + "Number": null, + "Type": "Integer", + }, + "DB": { + "Description": "dbSNP membership, build 129", + "Number": 0, + "Type": "Flag", + }, + "DBRIPID": { + "Description": "ID of this element in DBRIP", + "Number": 1, + "Type": "String", + }, + "DBVARID": { + "Description": "ID of this element in DBVAR", + "Number": 1, + "Type": "String", + }, + "DGVID": { + "Description": "ID of this element in Database of Genomic Variation", + "Number": 1, + "Type": "String", + }, + "DP": { + "Description": "Total Depth", + "Number": 1, + "Type": "Integer", + }, + "DPADJ": { + "Description": "Read Depth of adjacency", + "Type": "Integer", + }, + "END": { + "Description": "End position (for use with symbolic alleles)", + "Number": 1, + "Type": "Integer", + }, + "EVENT": { + "Description": "ID of event associated to breakend", + "Number": 1, + "Type": "String", + }, + "H2": { + "Description": "HapMap2 membership", + "Number": 0, + "Type": "Flag", + }, + "H3": { + "Description": "HapMap3 membership", + "Number": 0, + "Type": "Flag", + }, + "HOMLEN": { + "Description": "Length of base pair identical micro-homology at event breakpoints", + "Type": "Integer", + }, + "HOMSEQ": { + "Description": "Sequence of base pair identical micro-homology at event breakpoints", + "Type": "String", + }, + "IMPRECISE": { + "Description": "Imprecise structural variation", + "Number": 0, + "Type": "Flag", + }, + "MATEID": { + "Description": "ID of mate breakends", + "Number": null, + "Type": "String", + }, + "MEINFO": { + "Description": "Mobile element info of the form NAME,START,END,POLARITY", + "Number": 4, + "Type": "String", + }, + "METRANS": { + "Description": "Mobile element transduction info of the form CHR,START,END,POLARITY", + "Number": 4, + "Type": "String", + }, + "MQ": { + "Description": "RMS mapping quality", + "Number": 1, + "Type": null, + }, + "MQ0": { + "Description": "Number of MAPQ == 0 reads", + "Number": 1, + "Type": "Integer", + }, + "NOVEL": { + "Description": "Indicates a novel structural variation", + "Number": 0, + "Type": "Flag", + }, + "NS": { + "Description": "Number of Samples With Data", + "Number": 1, + "Type": "Integer", + }, + "PARID": { + "Description": "ID of partner breakend", + "Number": 1, + "Type": "String", + }, + "SB": { + "Description": "Strand bias", + "Number": 4, + "Type": "Integer", + }, + "SOMATIC": { + "Description": "Somatic mutation (for cancer genomics)", + "Number": 0, + "Type": "Flag", + }, + "SVLEN": { + "Description": "Difference in length between REF and ALT alleles", + "Number": null, + "Type": "Integer", + }, + "SVTYPE": { + "Description": "Type of structural variant", + "Number": 1, + "Type": "String", + }, + "TEST": { + "Description": "Used for testing", + "Number": 1, + "Type": "String", + }, + "VALIDATED": { + "Description": "Validated by follow-up experiment", + "Number": 0, + "Type": "Flag", + }, +} +`; + +exports[`can parse a line from the VCF spec 1`] = ` +{ + "ALT": [ + "A", + ], + "CHROM": "20", + "FILTER": "PASS", + "GENOTYPES": [Function], + "ID": [ + "rs6054257", + ], + "INFO": { + "AF": [ + 0.5, + ], + "DB": true, + "DP": [ + 14, + ], + "H2": true, + "NS": [ + 3, + ], + }, + "POS": 14370, + "QUAL": 29, + "REF": "G", + "SAMPLES": [Function], +} +`; + +exports[`can parse a line from the VCF spec 2`] = ` +{ + "NA00001": { + "DP": [ + 1, + ], + "GQ": [ + 48, + ], + "GT": [ + "0|0", + ], + "HQ": [ + 51, + 51, + ], + }, + "NA00002": { + "DP": [ + 8, + ], + "GQ": [ + 48, + ], + "GT": [ + "1|0", + ], + "HQ": [ + 51, + 51, + ], + }, + "NA00003": { + "DP": [ + 5, + ], + "GQ": [ + 43, + ], + "GT": [ + "1/1", + ], + "HQ": [ + undefined, + NaN, + ], + }, +} +`; + +exports[`can parse a line from the VCF spec 3`] = ` +{ + "ALT": [ + "", + ], + "CHROM": "8", + "FILTER": "PASS", + "GENOTYPES": [Function], + "ID": [ + "28329_0", + ], + "INFO": { + "AF": [ + 0.971429, + ], + "CHR2": [ + "8", + ], + "END": [ + 17709148, + ], + "Kurtosis_quant_start": [ + "20.524521", + ], + "Kurtosis_quant_stop": [ + "3.925926", + ], + "PRECISE": true, + "RE": [ + 34, + ], + "STD_quant_start": [ + "0.000000", + ], + "STD_quant_stop": [ + "0.000000", + ], + "STRANDS": [ + "+-", + ], + "STRANDS2": [ + "20", + "14", + "20", + "14", + ], + "SUPTYPE": [ + "AL", + ], + "SVLEN": [ + 33, + ], + "SVMETHOD": [ + "Snifflesv1.0.3", + ], + "SVTYPE": [ + "DEL", + ], + }, + "POS": 17709115, + "QUAL": undefined, + "REF": "N", + "SAMPLES": [Function], +} +`; + +exports[`can parse a line from the VCF spec 4`] = ` +{ + "/seq/schatz/fritz/sv-paper/real/Nanopore_NA12878/mapped/ngm_Nanopore_human_ngmlr-0.2.3_mapped.bam": { + "DR": [ + 1, + ], + "DV": [ + 34, + ], + "GT": [ + "1/1", + ], + }, +} +`; + +exports[`can parse a line from the VCF spec Y chrom (haploid)) 1`] = ` +{ + "ALT": [ + "", + ], + "CHROM": "Y", + "FILTER": "PASS", + "GENOTYPES": [Function], + "ID": [ + "CNV_Y_14483990_15232198", + ], + "INFO": { + "AC": [ + 1, + ], + "AF": [ + 0.000817661, + ], + "AFR_AF": [ + 0, + ], + "AMR_AF": [ + 0, + ], + "AN": [ + 1223, + ], + "EAS_AF": [ + 0, + ], + "END": [ + 15232198, + ], + "EUR_AF": [ + 0.0042, + ], + "EX_TARGET": true, + "NS": [ + 1233, + ], + "SAS_AF": [ + 0, + ], + "SVTYPE": [ + "CNV", + ], + "VT": [ + "SV", + ], + }, + "POS": 14483990, + "QUAL": 100, + "REF": "C", + "SAMPLES": [Function], +} +`; + +exports[`can parse a line from the VCF spec Y chrom (haploid)) 2`] = ` +{ + "HG00096": { + "CN": [ + 1, + ], + "CNL": [ + -1000, + 0, + -119.08, + ], + "CNP": [ + -1000, + 0, + -218.16, + ], + "CNQ": [ + 99, + ], + "GP": [ + 0, + -1000, + ], + "GQ": [ + 99, + ], + "GT": [ + "0", + ], + "PL": [ + 0, + 10000, + ], + }, + "HG00101": { + "CN": [ + 1, + ], + "CNL": [ + -1000, + 0, + -43.56, + ], + "CNP": [ + -1000, + 0, + -142.64, + ], + "CNQ": [ + 99, + ], + "GP": [ + 0, + -1000, + ], + "GQ": [ + 99, + ], + "GT": [ + "0", + ], + "PL": [ + 0, + 10000, + ], + }, + "HG00103": { + "CN": undefined, + "CNL": undefined, + "CNP": undefined, + "CNQ": undefined, + "GP": undefined, + "GQ": undefined, + "GT": undefined, + "PL": undefined, + }, + "HG001055": { + "CN": undefined, + "CNL": undefined, + "CNP": undefined, + "CNQ": undefined, + "GP": undefined, + "GQ": undefined, + "GT": undefined, + "PL": undefined, + }, +} +`; + +exports[`can parse a line from the VCF spec Y chrom (haploid)) 3`] = ` +{ + "ALT": [ + "A", + ], + "CHROM": "Y", + "FILTER": "PASS", + "GENOTYPES": [Function], + "ID": [ + "rs11575897", + ], + "INFO": { + "AA": [ + "G", + ], + "AC": [ + 22, + ], + "AF": [ + 0.0178427, + ], + "AFR_AF": [ + 0, + ], + "AMR_AF": [ + 0, + ], + "AN": [ + 1233, + ], + "DP": [ + 84761, + ], + "EAS_AF": [ + 0.0902, + ], + "EUR_AF": [ + 0, + ], + "EX_TARGET": true, + "NS": [ + 1233, + ], + "SAS_AF": [ + 0, + ], + "VT": [ + "SNP", + ], + }, + "POS": 2655180, + "QUAL": 100, + "REF": "G", + "SAMPLES": [Function], +} +`; + +exports[`can parse a line from the VCF spec Y chrom (haploid)) 4`] = ` +{ + "HG00096": { + "GT": [ + "0", + ], + }, + "HG00101": { + "GT": [ + "0", + ], + }, + "HG00103": { + "GT": [ + "0", + ], + }, + "HG001055": { + "GT": undefined, + }, +} +`; + +exports[`can parse a line with minimal entries 1`] = ` +{ + "ALT": [ + "A", + ], + "CHROM": "20", + "FILTER": undefined, + "GENOTYPES": [Function], + "ID": undefined, + "INFO": {}, + "POS": 14370, + "QUAL": undefined, + "REF": "G", + "SAMPLES": [Function], +} +`; + +exports[`can parse a line with minimal entries 2`] = ` +{ + "NA00001": { + "GT": undefined, + }, + "NA00002": { + "GT": undefined, + }, + "NA00003": { + "GT": [ + ". +", + ], + }, +} +`; + +exports[`can parse breakends 1`] = ` +[ + { + "ALT": [ + "G]8:107653520]", + ], + "CHROM": "11", + "FILTER": "PASS", + "GENOTYPES": [Function], + "ID": [ + "MantaBND:0:2:3:0:0:0:1", + ], + "INFO": { + "BND_DEPTH": [ + "216", + ], + "CIPOS": [ + 0, + 2, + ], + "HOMLEN": [ + 2, + ], + "HOMSEQ": [ + "TT", + ], + "MATEID": [ + "MantaBND:0:2:3:0:0:0:0", + ], + "MATE_BND_DEPTH": [ + "735", + ], + "SVTYPE": [ + "BND", + ], + }, + "POS": 94975747, + "QUAL": undefined, + "REF": "G", + "SAMPLES": [Function], + }, + { + "ALT": [ + "", + ], + "CHROM": "11", + "FILTER": "PASS", + "GENOTYPES": [Function], + "ID": [ + "MantaDEL:0:1:2:0:0:0", + ], + "INFO": { + "CIEND": [ + -150, + 150, + ], + "CIPOS": [ + -156, + 156, + ], + "END": [ + 94987865, + ], + "IMPRECISE": true, + "SVLEN": [ + 12112, + ], + "SVTYPE": [ + "DEL", + ], + }, + "POS": 94975753, + "QUAL": undefined, + "REF": "T", + "SAMPLES": [Function], + }, + { + "ALT": [ + "T[8:107653411[", + ], + "CHROM": "11", + "FILTER": "PASS", + "GENOTYPES": [Function], + "ID": [ + "MantaBND:0:0:1:0:0:0:0", + ], + "INFO": { + "BND_DEPTH": [ + "171", + ], + "MATEID": [ "MantaBND:0:0:1:0:0:0:1", ], "MATE_BND_DEPTH": [ @@ -1302,8 +2185,9 @@ exports[`can parse breakends 1`] = ` ], }, "POS": 94987872, - "QUAL": null, + "QUAL": undefined, "REF": "T", + "SAMPLES": [Function], }, ] `; @@ -1317,27 +2201,59 @@ exports[`parse breakend on thing that looks like symbolic allele but is actually } `; -exports[`shortcut parsing with 1000 genomes 1`] = ` -[ - "HG00096", - "HG00101", - "HG00103", - "HG00105", - "HG00107", -] +exports[`parses a line with a breakend ALT 1`] = ` +{ + "ALT": [ + "G]17:198982]", + ], + "CHROM": "2", + "FILTER": "PASS", + "GENOTYPES": [Function], + "ID": [ + "bnd_W", + ], + "INFO": { + "SVTYPE": [ + "BND", + ], + }, + "POS": 321681, + "QUAL": 6, + "REF": "G", + "SAMPLES": [Function], +} `; -exports[`shortcut parsing with 1000 genomes 2`] = ` -[ - "NA21128", - "NA21129", - "NA21130", - "NA21133", - "NA21135", -] +exports[`parses a line with mix of multiple breakends and non breakends 1`] = ` +{ + "ALT": [ + "CTATGTCG", + "C[2 : 321682[", + "C[17 : 198983[", + ], + "CHROM": "13", + "FILTER": "PASS", + "GENOTYPES": [Function], + "ID": [ + "bnd_U", + ], + "INFO": { + "MATEID": [ + "bnd V", + "bnd Z", + ], + "SVTYPE": [ + "BND", + ], + }, + "POS": 123456, + "QUAL": 6, + "REF": "C", + "SAMPLES": [Function], +} `; -exports[`shortcut parsing with 1000 genomes 3`] = ` +exports[`shortcut parsing with 1000 genomes 1`] = ` [ { "ALT": [ @@ -1345,6 +2261,7 @@ exports[`shortcut parsing with 1000 genomes 3`] = ` ], "CHROM": "Y", "FILTER": "PASS", + "GENOTYPES": [Function], "ID": [ "rs11575897", ], @@ -1390,6 +2307,7 @@ exports[`shortcut parsing with 1000 genomes 3`] = ` "POS": 2655180, "QUAL": 100, "REF": "G", + "SAMPLES": [Function], }, { "ALT": [ @@ -1397,7 +2315,8 @@ exports[`shortcut parsing with 1000 genomes 3`] = ` ], "CHROM": "Y", "FILTER": "PASS", - "ID": null, + "GENOTYPES": [Function], + "ID": undefined, "INFO": { "AA": [ "A", @@ -1440,18 +2359,20 @@ exports[`shortcut parsing with 1000 genomes 3`] = ` "POS": 2655471, "QUAL": 100, "REF": "A", + "SAMPLES": [Function], }, ] `; exports[`shortcut parsing with vcf 4.3 bnd example 1`] = ` [ - Variant { + { "ALT": [ "G]17:198982]", ], "CHROM": "2", "FILTER": "PASS", + "GENOTYPES": [Function], "ID": [ "bnd_W", ], @@ -1463,13 +2384,15 @@ exports[`shortcut parsing with vcf 4.3 bnd example 1`] = ` "POS": 321681, "QUAL": 6, "REF": "G", + "SAMPLES": [Function], }, - Variant { + { "ALT": [ "]13:123456]T", ], "CHROM": "2", "FILTER": "PASS", + "GENOTYPES": [Function], "ID": [ "bnd_V", ], @@ -1481,13 +2404,15 @@ exports[`shortcut parsing with vcf 4.3 bnd example 1`] = ` "POS": 321682, "QUAL": 6, "REF": "T", + "SAMPLES": [Function], }, - Variant { + { "ALT": [ "C[2:321682[", ], "CHROM": "13", "FILTER": "PASS", + "GENOTYPES": [Function], "ID": [ "bnd_U", ], @@ -1499,13 +2424,15 @@ exports[`shortcut parsing with vcf 4.3 bnd example 1`] = ` "POS": 123456, "QUAL": 6, "REF": "C", + "SAMPLES": [Function], }, - Variant { + { "ALT": [ "[17:198983[A", ], "CHROM": "13", "FILTER": "PASS", + "GENOTYPES": [Function], "ID": [ "bnd_X", ], @@ -1517,13 +2444,15 @@ exports[`shortcut parsing with vcf 4.3 bnd example 1`] = ` "POS": 123457, "QUAL": 6, "REF": "A", + "SAMPLES": [Function], }, - Variant { + { "ALT": [ "A]2:321681]", ], "CHROM": "17", "FILTER": "PASS", + "GENOTYPES": [Function], "ID": [ "bnd_Y", ], @@ -1535,13 +2464,15 @@ exports[`shortcut parsing with vcf 4.3 bnd example 1`] = ` "POS": 198982, "QUAL": 6, "REF": "A", + "SAMPLES": [Function], }, - Variant { + { "ALT": [ "[13:123457[C", ], "CHROM": "17", "FILTER": "PASS", + "GENOTYPES": [Function], "ID": [ "bnd_Z", ], @@ -1553,18 +2484,20 @@ exports[`shortcut parsing with vcf 4.3 bnd example 1`] = ` "POS": 198983, "QUAL": 6, "REF": "C", + "SAMPLES": [Function], }, ] `; exports[`snippet from VCF 4.3 spec 1`] = ` [ - Variant { + { "ALT": [ "A", ], "CHROM": "20", "FILTER": "PASS", + "GENOTYPES": [Function], "ID": [ "rs6054257", ], @@ -1584,8 +2517,9 @@ exports[`snippet from VCF 4.3 spec 1`] = ` "POS": 14370, "QUAL": 29, "REF": "G", + "SAMPLES": [Function], }, - Variant { + { "ALT": [ "A", ], @@ -1593,7 +2527,8 @@ exports[`snippet from VCF 4.3 spec 1`] = ` "FILTER": [ "q10", ], - "ID": null, + "GENOTYPES": [Function], + "ID": undefined, "INFO": { "AF": [ 0.017, @@ -1608,14 +2543,16 @@ exports[`snippet from VCF 4.3 spec 1`] = ` "POS": 17330, "QUAL": 3, "REF": "T", + "SAMPLES": [Function], }, - Variant { + { "ALT": [ "G", "T", ], "CHROM": "20", "FILTER": "PASS", + "GENOTYPES": [Function], "ID": [ "rs6040355", ], @@ -1638,12 +2575,14 @@ exports[`snippet from VCF 4.3 spec 1`] = ` "POS": 1110696, "QUAL": 67, "REF": "A", + "SAMPLES": [Function], }, - Variant { - "ALT": null, + { + "ALT": undefined, "CHROM": "20", "FILTER": "PASS", - "ID": null, + "GENOTYPES": [Function], + "ID": undefined, "INFO": { "AA": [ "T", @@ -1658,14 +2597,16 @@ exports[`snippet from VCF 4.3 spec 1`] = ` "POS": 1230237, "QUAL": 47, "REF": "T", + "SAMPLES": [Function], }, - Variant { + { "ALT": [ "G", "GTCT", ], "CHROM": "20", "FILTER": "PASS", + "GENOTYPES": [Function], "ID": [ "microsat1", ], @@ -1683,6 +2624,7 @@ exports[`snippet from VCF 4.3 spec 1`] = ` "POS": 1234567, "QUAL": 50, "REF": "GTC", + "SAMPLES": [Function], }, ] `; @@ -1731,8 +2673,8 @@ exports[`snippet from VCF 4.3 spec 2`] = ` "1/1", ], "HQ": [ - null, - null, + undefined, + undefined, ], }, }, @@ -1903,6 +2845,8 @@ exports[`snippet from VCF 4.3 spec 2`] = ` ] `; +exports[`test no info non-strict 1`] = `{}`; + exports[`vcf 4.3 insertion shorthand 1`] = ` { "Join": "right", @@ -1962,3 +2906,170 @@ exports[`vcf 4.3 single breakends 3`] = ` "SingleBreakend": true, } `; + +exports[`vcf lines with weird info field and missing format/genotypes 1`] = ` +[ + { + "ALT": [ + "A", + ], + "CHROM": "lcl|Scaffald_1", + "FILTER": "PASS", + "GENOTYPES": [Function], + "ID": [ + "rs118266897", + ], + "INFO": { + "0,14": undefined, + "112": undefined, + "AF": [ + 0.5, + ], + "DB": true, + "NS": [ + 3, + ], + "PG2.1": undefined, + }, + "POS": 80465, + "QUAL": 29, + "REF": "R", + "SAMPLES": [Function], + }, + { + "ALT": [ + "G", + ], + "CHROM": "lcl|Scaffald_1", + "FILTER": "PASS", + "GENOTYPES": [Function], + "ID": [ + "rs118269296", + ], + "INFO": { + "0,14": undefined, + "112": undefined, + "AF": [ + 0.5, + ], + "DB": true, + "NS": [ + 3, + ], + "PG2.1": undefined, + }, + "POS": 84818, + "QUAL": 29, + "REF": "R", + "SAMPLES": [Function], + }, + { + "ALT": [ + "T", + ], + "CHROM": "lcl|Scaffald_1", + "FILTER": "PASS", + "GENOTYPES": [Function], + "ID": [ + "rs118218236", + ], + "INFO": { + "0,14": undefined, + "112": undefined, + "AF": [ + 0.5, + ], + "DB": true, + "NS": [ + 3, + ], + "PG2.1": undefined, + }, + "POS": 95414, + "QUAL": 29, + "REF": "W", + "SAMPLES": [Function], + }, + { + "ALT": [ + "A", + ], + "CHROM": "lcl|Scaffald_1", + "FILTER": "PASS", + "GENOTYPES": [Function], + "ID": [ + "rs118264755", + ], + "INFO": { + "0,14": undefined, + "112": undefined, + "AF": [ + 0.5, + ], + "DB": true, + "NS": [ + 3, + ], + "PG2.1": undefined, + }, + "POS": 231384, + "QUAL": 29, + "REF": "R", + "SAMPLES": [Function], + }, + { + "ALT": [ + "G", + ], + "CHROM": "lcl|Scaffald_1", + "FILTER": "PASS", + "GENOTYPES": [Function], + "ID": [ + "rs118223336", + ], + "INFO": { + "0,14": undefined, + "112": undefined, + "AF": [ + 6.5, + ], + "DB": true, + "NS": [ + 3, + ], + "PG2.1": undefined, + }, + "POS": 236429, + "QUAL": 29, + "REF": "R", + "SAMPLES": [Function], + }, + { + "ALT": [ + "G", + ], + "CHROM": "lcl|Scaffald_1", + "FILTER": "PASS", + "GENOTYPES": [Function], + "ID": [ + "rs118217257", + ], + "INFO": { + "0,14": undefined, + "112": undefined, + "AF": [ + 0.5, + ], + "DB": true, + "NS": [ + 3, + ], + "PG2.1": undefined, + }, + "POS": 245378, + "QUAL": 29, + "REF": "R", + "SAMPLES": [Function], + }, +] +`; diff --git a/test/parse.test.ts b/test/parse.test.ts index cef330e..9c05b58 100644 --- a/test/parse.test.ts +++ b/test/parse.test.ts @@ -1,5 +1,5 @@ // @ts-nocheck -import { beforeAll, test, describe, it, expect } from 'vitest' +import { test, expect } from 'vitest' import fs from 'fs' import VCF, { parseBreakend } from '../src' @@ -18,11 +18,9 @@ const readVcf = file => { return { header: header.join('\n'), lines: rest } } -describe('VCF parser', () => { - let VCFParser - beforeAll(() => { - VCFParser = new VCF({ - header: `##fileformat=VCFv4.3 +function makeParser() { + return new VCF({ + header: `##fileformat=VCFv4.3 ##fileDate=20090805 ##source=myImputationProgramV3.1 ##reference=file:///seq/references/1000GenomesPilot-NCBI36.fasta @@ -46,102 +44,105 @@ describe('VCF parser', () => { ##FORMAT= #CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tNA00001\tNA00002\tNA00003 `, - }) }) +} - it('can get metadata from the header', () => { - // Note that there is a custom PL that overrides the default PL - expect(VCFParser.getMetadata()).toMatchSnapshot() - expect(VCFParser.getMetadata('nonexistent')).toBe(undefined) - expect(VCFParser.getMetadata('fileDate')).toBe('20090805') - expect(VCFParser.getMetadata('INFO')).toMatchSnapshot() - expect(VCFParser.getMetadata('INFO', 'nonexistent')).toBe(undefined) - expect(VCFParser.getMetadata('INFO', 'AA')).toEqual({ - Description: 'Ancestral Allele', - Number: 1, - Type: 'String', - }) - expect(VCFParser.getMetadata('INFO', 'AA', 'nonexistent')).toBe(undefined) - expect(VCFParser.getMetadata('INFO', 'AA', 'Type')).toBe('String') - expect(VCFParser.getMetadata('INFO', 'AA', 'Type', 'nonexistent')).toBe( - undefined, - ) - expect(VCFParser.getMetadata('INFO', 'TEST')).toEqual({ - Description: 'Used for testing', - Number: 1, - Type: 'String', - }) +test('can get metadata from the header', () => { + const VCFParser = makeParser() + // Note that there is a custom PL that overrides the default PL + expect(VCFParser.getMetadata()).toMatchSnapshot() + expect(VCFParser.getMetadata('nonexistent')).toBe(undefined) + expect(VCFParser.getMetadata('fileDate')).toBe('20090805') + expect(VCFParser.getMetadata('INFO')).toMatchSnapshot() + expect(VCFParser.getMetadata('INFO', 'nonexistent')).toBe(undefined) + expect(VCFParser.getMetadata('INFO', 'AA')).toEqual({ + Description: 'Ancestral Allele', + Number: 1, + Type: 'String', }) - - it('can get default metadata not in the header', () => { - const metadata = VCFParser.getMetadata() - expect(metadata.INFO.AC).toEqual({ - Number: 'A', - Type: 'Integer', - Description: - 'Allele count in genotypes, for each ALT allele, in the same order as listed', - }) + expect(VCFParser.getMetadata('INFO', 'AA', 'nonexistent')).toBe(undefined) + expect(VCFParser.getMetadata('INFO', 'AA', 'Type')).toBe('String') + expect(VCFParser.getMetadata('INFO', 'AA', 'Type', 'nonexistent')).toBe( + undefined, + ) + expect(VCFParser.getMetadata('INFO', 'TEST')).toEqual({ + Description: 'Used for testing', + Number: 1, + Type: 'String', }) +}) - it('can parse a line from the VCF spec', () => { - const variant = VCFParser.parseLine( - '20\t14370\trs6054257\tG\tA\t29\tPASS\tNS=3;DP=14;AF=0.5;DB;H2\tGT:GQ:DP:HQ\t0|0:48:1:51,51\t1|0:48:8:51,51\t1/1:43:5:.,.\n', - ) - expect(variant).toMatchSnapshot() - expect(variant.SAMPLES).toMatchSnapshot() +test('can get default metadata not in the header', () => { + const VCFParser = makeParser() + const metadata = VCFParser.getMetadata() + expect(metadata.INFO.AC).toEqual({ + Number: 'A', + Type: 'Integer', + Description: + 'Allele count in genotypes, for each ALT allele, in the same order as listed', }) +}) - it('can parse a line with minimal entries', () => { - const variant = VCFParser.parseLine( - '20\t14370\t.\tG\tA\t.\t.\t.\tGT:GQ:DP:HQ\t.\t.\t.\n', - ) - expect(variant).toMatchSnapshot() - expect(variant.SAMPLES).toMatchSnapshot() - }) +test('can parse a line from the VCF spec', () => { + const VCFParser = makeParser() + const variant = VCFParser.parseLine( + '20\t14370\trs6054257\tG\tA\t29\tPASS\tNS=3;DP=14;AF=0.5;DB;H2\tGT:GQ:DP:HQ\t0|0:48:1:51,51\t1|0:48:8:51,51\t1/1:43:5:.,.\n', + ) + expect(variant).toMatchSnapshot() + expect(variant.SAMPLES()).toMatchSnapshot() +}) - it('parses a line with a breakend ALT', () => { - const variant = VCFParser.parseLine( - '2\t321681\tbnd_W\tG\tG]17:198982]\t6\tPASS\tSVTYPE=BND\n', - ) - expect(variant.ALT.length).toBe(1) - expect(variant.INFO.SVTYPE).toEqual(['BND']) - expect(variant).toMatchSnapshot() - }) +test('can parse a line with minimal entries', () => { + const VCFParser = makeParser() + const variant = VCFParser.parseLine( + '20\t14370\t.\tG\tA\t.\t.\t.\tGT:GQ:DP:HQ\t.\t.\t.\n', + ) + expect(variant).toMatchSnapshot() + expect(variant.SAMPLES()).toMatchSnapshot() +}) - it(`parses a line with mix of multiple breakends and non breakends`, () => { - const variant = VCFParser.parseLine( - `13\t123456\tbnd_U\tC\tCTATGTCG,C[2 : 321682[,C[17 : 198983[\t6\tPASS\tSVTYPE=BND;MATEID=bnd V,bnd Z`, - ) - expect(variant.ALT.length).toBe(3) - expect(variant.INFO.SVTYPE).toEqual(['BND']) - expect(variant).toMatchSnapshot() - }) +test('parses a line with a breakend ALT', () => { + const VCFParser = makeParser() + const variant = VCFParser.parseLine( + '2\t321681\tbnd_W\tG\tG]17:198982]\t6\tPASS\tSVTYPE=BND', + ) + expect(variant.ALT.length).toBe(1) + expect(variant.INFO.SVTYPE).toEqual(['BND']) + expect(variant).toMatchSnapshot() +}) - it('throws errors with bad header lines', () => { - expect(() => { - new VCF({ header: 'notARealHeader' }) - }).toThrow('Bad line in header') - expect(() => { - new VCF({ - header: '#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\n', - }) - }).toThrow('VCF header missing columns') - expect(() => { - new VCF({ - header: '#CHROM\tPS\tID\tRF\tALT\tQUAL\tFILTER\tINFO\n', - }) - }).toThrow('VCF column headers not correct') - expect(() => { - new VCF({ header: '##this=badHeader\n' }) - }).toThrow(/No format line/) - }) +test(`parses a line with mix of multiple breakends and non breakends`, () => { + const VCFParser = makeParser() + const variant = VCFParser.parseLine( + `13\t123456\tbnd_U\tC\tCTATGTCG,C[2 : 321682[,C[17 : 198983[\t6\tPASS\tSVTYPE=BND;MATEID=bnd V,bnd Z`, + ) + expect(variant.ALT.length).toBe(3) + expect(variant.INFO.SVTYPE).toEqual(['BND']) + expect(variant).toMatchSnapshot() }) -describe('VCF parser for structural variants', () => { - let VCFParser - beforeAll(() => { - VCFParser = new VCF({ - header: `##fileformat=VCFv4.2 +test('throws errors with bad header lines', () => { + expect(() => { + new VCF({ header: 'notARealHeader' }) + }).toThrow('Bad line in header') + expect(() => { + new VCF({ + header: '#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\n', + }) + }).toThrow('VCF header missing columns') + expect(() => { + new VCF({ + header: '#CHROM\tPS\tID\tRF\tALT\tQUAL\tFILTER\tINFO\n', + }) + }).toThrow('VCF column headers not correct') + expect(() => { + new VCF({ header: '##this=badHeader\n' }) + }).toThrow(/No format line/) +}) + +test('can parse a line from the VCF spec', () => { + const VCFParser = new VCF({ + header: `##fileformat=VCFv4.2 ##source=Sniffles ##fileDate=20170420 ##ALT= @@ -163,23 +164,17 @@ describe('VCF parser for structural variants', () => { ##FORMAT= ##FORMAT= #CHROM POS ID REF ALT QUAL FILTER INFO FORMAT /seq/schatz/fritz/sv-paper/real/Nanopore_NA12878/mapped/ngm_Nanopore_human_ngmlr-0.2.3_mapped.bam`, - }) - }) - - it('can parse a line from the VCF spec', () => { - const variant = VCFParser.parseLine( - '8\t17709115\t28329_0\tN\t\t.\tPASS\tPRECISE;SVMETHOD=Snifflesv1.0.3;CHR2=8;END=17709148;STD_quant_start=0.000000;STD_quant_stop=0.000000;Kurtosis_quant_start=20.524521;Kurtosis_quant_stop=3.925926;SVTYPE=DEL;SUPTYPE=AL;SVLEN=33;STRANDS=+-;STRANDS2=20,14,20,14;RE=34;AF=0.971429\tGT:DR:DV\t1/1:1:34', - ) - expect(variant).toMatchSnapshot() - expect(variant.SAMPLES).toMatchSnapshot() }) + const variant = VCFParser.parseLine( + '8\t17709115\t28329_0\tN\t\t.\tPASS\tPRECISE;SVMETHOD=Snifflesv1.0.3;CHR2=8;END=17709148;STD_quant_start=0.000000;STD_quant_stop=0.000000;Kurtosis_quant_start=20.524521;Kurtosis_quant_stop=3.925926;SVTYPE=DEL;SUPTYPE=AL;SVLEN=33;STRANDS=+-;STRANDS2=20,14,20,14;RE=34;AF=0.971429\tGT:DR:DV\t1/1:1:34', + ) + expect(variant).toMatchSnapshot() + expect(variant.SAMPLES()).toMatchSnapshot() }) -describe('VCF parser for Y chrom (haploid)', () => { - let VCFParser - beforeAll(() => { - VCFParser = new VCF({ - header: `##fileformat=VCFv4.1 +test('can parse a line from the VCF spec Y chrom (haploid))', () => { + const VCFParser = new VCF({ + header: `##fileformat=VCFv4.1 ##FILTER= ##fileDate=20150218 ##reference=ftp://ftp.1000genomes.ebi.ac.uk//vol1/ftp/technical/reference/phase2_reference_assembly_sequence/hs37d5.fa.gz @@ -219,34 +214,27 @@ describe('VCF parser for Y chrom (haploid)', () => { ##INFO= ##INFO= #CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tHG00096\tHG00101\tHG00103\tHG001055`, - }) - }) - - it('can parse a line from the VCF spec', () => { - const variant = VCFParser.parseLine( - 'Y\t14483990\tCNV_Y_14483990_15232198\tC\t\t100\tPASS\tAC=1;AF=0.000817661;AN=1223;END=15232198;NS=1233;SVTYPE=CNV;AMR_AF=0;AFR_AF=0;EUR_AF=0.0042;SAS_AF=0;EAS_AF=0;VT=SV;EX_TARGET\tGT:CN:CNL:CNP:CNQ:GP:GQ:PL\t0:1:-1000,0,-119.08:-1000,0,-218.16:99:0,-1000:99:0,10000\t0:1:-1000,0,-43.56:-1000,0,-142.64:99:0,-1000:99:0,10000\t.:.:.:.:.:.:.:.\t.:.:.:.:.:.:.:.', - ) - const variant2 = VCFParser.parseLine( - 'Y\t2655180\trs11575897\tG\tA\t100\tPASS\tAA=G;AC=22;AF=0.0178427;AN=1233;DP=84761;NS=1233;AMR_AF=0;AFR_AF=0;EUR_AF=0;SAS_AF=0;EAS_AF=0.0902;VT=SNP;EX_TARGET\tGT\t0\t0\t0\t.', - ) - expect(variant).toMatchSnapshot() - expect(variant.SAMPLES).toMatchSnapshot() - expect(variant2).toMatchSnapshot() - expect(variant2.SAMPLES).toMatchSnapshot() }) + const variant = VCFParser.parseLine( + 'Y\t14483990\tCNV_Y_14483990_15232198\tC\t\t100\tPASS\tAC=1;AF=0.000817661;AN=1223;END=15232198;NS=1233;SVTYPE=CNV;AMR_AF=0;AFR_AF=0;EUR_AF=0.0042;SAS_AF=0;EAS_AF=0;VT=SV;EX_TARGET\tGT:CN:CNL:CNP:CNQ:GP:GQ:PL\t0:1:-1000,0,-119.08:-1000,0,-218.16:99:0,-1000:99:0,10000\t0:1:-1000,0,-43.56:-1000,0,-142.64:99:0,-1000:99:0,10000\t.:.:.:.:.:.:.:.\t.:.:.:.:.:.:.:.', + ) + const variant2 = VCFParser.parseLine( + 'Y\t2655180\trs11575897\tG\tA\t100\tPASS\tAA=G;AC=22;AF=0.0178427;AN=1233;DP=84761;NS=1233;AMR_AF=0;AFR_AF=0;EUR_AF=0;SAS_AF=0;EAS_AF=0.0902;VT=SNP;EX_TARGET\tGT\t0\t0\t0\t.', + ) + expect(variant).toMatchSnapshot() + expect(variant.SAMPLES()).toMatchSnapshot() + expect(variant2).toMatchSnapshot() + expect(variant2.SAMPLES()).toMatchSnapshot() }) test('snippet from VCF 4.3 spec', () => { - const { header, lines } = readVcf( - require.resolve('./data/vcf4.3_spec_snippet.vcf'), - ) + const { header, lines } = readVcf('test/data/vcf4.3_spec_snippet.vcf') const VCFParser = new VCF({ header, }) - const variants = lines.map(line => VCFParser.parseLine(line)).filter(x => x) + const variants = lines.map(line => VCFParser.parseLine(line)) expect(variants).toMatchSnapshot() - const samples = variants.map(variant => variant.SAMPLES) - expect(samples).toMatchSnapshot() + expect(variants.map(variant => variant.SAMPLES())).toMatchSnapshot() }) test('can parse breakends', () => { const header = `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam` @@ -260,53 +248,43 @@ test('can parse breakends', () => { '\n', ) - const variants = lines.map(line => VCFParser.parseLine(line)) - expect(variants).toMatchSnapshot() + expect(lines.map(line => VCFParser.parseLine(line))).toMatchSnapshot() }) // from https://github.com/GMOD/jbrowse/issues/1358 -describe('Obscure VCF', () => { - let VCFParser - beforeAll(() => { - VCFParser = new VCF({ - header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`, - }) +test('vcf lines with weird info field and missing format/genotypes', () => { + const VCFParser = new VCF({ + header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`, }) - it('vcf lines with weird info field and missing format/genotypes', () => { - const lines = - `lcl|Scaffald_1\t80465\trs118266897\tR\tA\t29\tPASS\tNS=3;0,14;AF=0.5;DB;112;PG2.1 + const lines = + `lcl|Scaffald_1\t80465\trs118266897\tR\tA\t29\tPASS\tNS=3;0,14;AF=0.5;DB;112;PG2.1 lcl|Scaffald_1\t84818\trs118269296\tR\tG\t29\tPASS\tNS=3;0,14;AF=0.5;DB;112;PG2.1 lcl|Scaffald_1\t95414\trs118218236\tW\tT\t29\tPASS\tNS=3;0,14;AF=0.5;DB;112;PG2.1 lcl|Scaffald_1\t231384\trs118264755\tR\tA\t29\tPASS\tNS=3;0,14;AF=0.5;DB;112;PG2.1 lcl|Scaffald_1\t236429\trs118223336\tR\tG\t29\tPASS\tNS=3;0,14;AF=6.5;DB;112;PG2.1 lcl|Scaffald_1\t245378\trs118217257\tR\tG\t29\tPASS\tNS=3;0,14;AF=0.5;DB;112;PG2.1`.split( - '\n', - ) + '\n', + ) - const variants = lines.map(line => VCFParser.parseLine(line)) - expect(variants).toMatchSnapshot() - }) + expect(lines.map(line => VCFParser.parseLine(line))).toMatchSnapshot() }) test('test no info strict', () => { - const { header, lines } = readVcf( - require.resolve('./data/multipleAltSVs.vcf'), - ) - const VCFParser = new VCF({ header, strict: true }) + const { header, lines } = readVcf('test/data/multipleAltSVs.vcf') + const VCFParser = new VCF({ + header, + strict: true, + }) expect(() => VCFParser.parseLine(lines[0])).toThrow(/INFO/) }) test('test no info non-strict', () => { - const { header, lines } = readVcf( - require.resolve('./data/multipleAltSVs.vcf'), - ) - const VCFParser = new VCF({ header, strict: false }) + const { header, lines } = readVcf('test/data/multipleAltSVs.vcf') + const VCFParser = new VCF({ + header, + strict: false, + }) expect(VCFParser.parseLine(lines[0])).toBeTruthy() - expect(VCFParser.parseLine(lines[0]).GENOTYPES).toBeUndefined() -}) -test('blank line returns undefined', () => { - const { header } = readVcf(require.resolve('./data/multipleAltSVs.vcf')) - const VCFParser = new VCF({ header }) - expect(VCFParser.parseLine('')).toBeUndefined() + expect(VCFParser.parseLine(lines[0]).GENOTYPES()).toStrictEqual({}) }) test('empty header lines', () => { @@ -314,26 +292,17 @@ test('empty header lines', () => { }) test('shortcut parsing with 1000 genomes', () => { - const { header, lines } = readVcf(require.resolve('./data/1000genomes.vcf')) + const { header, lines } = readVcf('test/data/1000genomes.vcf') const VCFParser = new VCF({ header }) - const variants = lines.map(line => VCFParser.parseLine(line)).filter(x => x) - expect(Object.keys(variants[0].SAMPLES).slice(0, 5)).toMatchSnapshot() - expect(Object.keys(variants[0].SAMPLES).slice(-5)).toMatchSnapshot() - const ret = variants.map(v => { - const { SAMPLES: _, ...rest } = v - return rest - }) - expect(ret).toMatchSnapshot() + expect(lines.map(line => VCFParser.parseLine(line))).toMatchSnapshot() }) test('shortcut parsing with vcf 4.3 bnd example', () => { - const { header, lines } = readVcf( - require.resolve('./data/vcf4.3_spec_bnd.vcf'), - ) + const { header, lines } = readVcf('test/data/vcf4.3_spec_bnd.vcf') const VCFParser = new VCF({ header }) - const variants = lines.map(line => VCFParser.parseLine(line)).filter(x => x) + const variants = lines.map(line => VCFParser.parseLine(line)) expect(variants.map(m => m.ALT[0].toString())).toEqual( lines.map(line => line.split('\t')[4]), ) From 20092e88c060aa9ef20110be82680297ee6272ac Mon Sep 17 00:00:00 2001 From: Colin Date: Sat, 23 Nov 2024 13:28:22 -0500 Subject: [PATCH 4/5] More typescripting --- src/parse.ts | 48 ++++++++++++++++++------------------------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/src/parse.ts b/src/parse.ts index 4d7d00c..384ef78 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -21,7 +21,7 @@ function decodeURIComponentNoThrow(uri: string) { * (default true) */ export default class VCFParser { - private metadata: Record + private metadata: Record public strict: boolean public samples: string[] @@ -171,7 +171,7 @@ export default class VCFParser { this.metadata[r] = {} } const [id, keyVals] = this.parseStructuredMetaVal(metaVal) - this.metadata[r][id] = keyVals + ;(this.metadata[r] as Record)[id] = keyVals } else { this.metadata[r] = metaVal } @@ -188,14 +188,14 @@ export default class VCFParser { */ private parseStructuredMetaVal(metaVal: string) { const keyVals = this.parseKeyValue(metaVal.replace(/^<|>$/g, ''), ',') - const id = keyVals.ID + const id = keyVals.ID as string delete keyVals.ID if ('Number' in keyVals) { if (!Number.isNaN(Number(keyVals.Number))) { keyVals.Number = Number(keyVals.Number) } } - return [id, keyVals] + return [id, keyVals] as const } /** @@ -228,7 +228,7 @@ export default class VCFParser { * 'Flag', Description: 'dbSNP membership, build 129'} */ private parseKeyValue(str: string, pairSeparator = ';') { - const data: any = {} + const data = {} as Record let currKey = '' let currValue = '' @@ -311,39 +311,27 @@ export default class VCFParser { "no INFO field specified, must contain at least a '.' (turn off strict mode to allow)", ) } + const hasDecode = fields[7]?.includes('%') const info = fields[7] === undefined || fields[7] === '.' ? {} : this.parseKeyValue(fields[7]) for (const key of Object.keys(info)) { - let items - if (info[key]) { - items = (info[key] as string) - .split(',') - .map(val => (val === '.' ? undefined : val)) - .map(f => (f ? decodeURIComponentNoThrow(f) : f)) - } else { - // it will be falsy so just assign whatever is there - items = info[key] - } + const items = (info[key] as string | undefined) + ?.split(',') + .map(val => (val === '.' ? undefined : val)) + .map(f => (f && hasDecode ? decodeURIComponentNoThrow(f) : f)) const itemType = this.getMetadata('INFO', key, 'Type') - if (itemType) { - if (itemType === 'Integer' || itemType === 'Float') { - items = items.map((val: string | undefined) => - val === undefined ? undefined : Number(val), - ) - } else if (itemType === 'Flag') { - if (info[key]) { - console.warn( - `Info field ${key} is a Flag and should not have a value (got value ${info[key]})`, - ) - } else { - items = true - } - } + if (itemType === 'Integer' || itemType === 'Float') { + info[key] = items?.map(val => + val === undefined ? undefined : Number(val), + ) + } else if (itemType === 'Flag') { + info[key] = true + } else { + info[key] = items } - info[key] = items } return { From 1cb6aa43b473926d105319949bb283742440fc4c Mon Sep 17 00:00:00 2001 From: Colin Date: Sat, 23 Nov 2024 14:26:13 -0500 Subject: [PATCH 5/5] getMetadata not private (used in jb2) --- src/parse.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parse.ts b/src/parse.ts index 384ef78..c6bd2f6 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -207,7 +207,7 @@ export default class VCFParser { * * @returns {any} An object, string, or number, depending on the filtering */ - private getMetadata(...args: string[]) { + getMetadata(...args: string[]) { let filteredMetadata: any = this.metadata for (const arg of args) { filteredMetadata = filteredMetadata[arg]