diff --git a/.changeset/sc-chain-domain-letters.md b/.changeset/sc-chain-domain-letters.md new file mode 100644 index 0000000..504e8a1 --- /dev/null +++ b/.changeset/sc-chain-domain-letters.md @@ -0,0 +1,10 @@ +--- +'@platforma-open/milaboratories.cdr3-spectratype.workflow': minor +'@platforma-open/milaboratories.cdr3-spectratype.model': minor +'@platforma-open/milaboratories.cdr3-spectratype.ui': minor +'@platforma-open/milaboratories.cdr3-spectratype': minor +--- + +Single-cell chain selection now uses the `pl7.app/vdj/scClonotypeChain` domain letter, matching the producers (mixcr-clonotyping, import-vdj-data) and the vj-usage block: A is the more diverse chain — Heavy, Beta, Delta. The workflow no longer inverts A/B for TCRAB/TCRGD, the args field is renamed `scClonotypeChain` to say which convention the letter is in, and emitted columns are labelled with the chain name instead of the raw letter. The selector keeps the classic annotation order, so TCR pairs read Alpha (B), Beta (A). + +Every existing block goes stale and must be re-run, because the args field was renamed. For single-cell TCR that re-run is the point: the stored letter is unchanged but now selects the other chain of the pair, so those blocks switch chain and their exported columns change identity (the chain letter rides in the cdr3Length axis domain). Re-pick the chain in Settings if you wanted the one you had, and update any custom block label naming a chain, since it also becomes the trace label. Bulk and Ig blocks re-run to the same result, with the chain name replacing the letter in Ig labels. diff --git a/model/src/index.ts b/model/src/index.ts index a4717e5..6b915b0 100644 --- a/model/src/index.ts +++ b/model/src/index.ts @@ -13,7 +13,7 @@ export const platforma = BlockModelV3.create(blockDataModel) return { datasetRef: data.datasetRef, lengthType: data.lengthType, - scChain: data.scChain, + scClonotypeChain: data.scChain, customBlockLabel: data.customBlockLabel, }; }) diff --git a/model/src/types.ts b/model/src/types.ts index 7fb42ea..45f6599 100644 --- a/model/src/types.ts +++ b/model/src/types.ts @@ -2,6 +2,7 @@ import type { GraphMakerState } from "@milaboratories/graph-maker"; import type { PlRef } from "@platforma-sdk/model"; export type LengthType = "aminoacid" | "nucleotide"; +/** `pl7.app/vdj/scClonotypeChain` domain letter: "A" is Heavy/Beta/Delta, "B" is Light/Alpha/Gamma. */ export type ScChain = "A" | "B"; /** @@ -29,7 +30,7 @@ export type BlockData = { export type BlockArgs = { datasetRef: PlRef; lengthType: LengthType; - scChain: ScChain; + scClonotypeChain: ScChain; customBlockLabel: string; }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 05c147d..3381864 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,14 +25,14 @@ catalogs: specifier: 1.3.0 version: 1.3.0 '@platforma-sdk/block-tools': - specifier: 2.12.6 - version: 2.12.6 + specifier: 2.14.3 + version: 2.14.3 '@platforma-sdk/model': specifier: 1.80.2 version: 1.80.2 '@platforma-sdk/tengo-builder': - specifier: 4.0.18 - version: 4.0.18 + specifier: 4.0.23 + version: 4.0.23 '@platforma-sdk/test': specifier: 1.80.3 version: 1.80.3 @@ -80,7 +80,7 @@ importers: version: 1.6.0(@types/node@24.5.2)(rollup@4.55.1)(vue@3.5.26(typescript@5.6.3))(yaml@2.8.2) '@platforma-sdk/block-tools': specifier: 'catalog:' - version: 2.12.6(@types/node@24.5.2) + version: 2.14.3(@types/node@24.5.2) shx: specifier: 'catalog:' version: 0.4.0 @@ -105,7 +105,7 @@ importers: devDependencies: '@platforma-sdk/block-tools': specifier: 'catalog:' - version: 2.12.6(@types/node@24.5.2) + version: 2.14.3(@types/node@24.5.2) shx: specifier: 'catalog:' version: 0.4.0 @@ -114,7 +114,7 @@ importers: dependencies: '@milaboratories/graph-maker': specifier: 'catalog:' - version: 1.2.4(@milaboratories/pl-model-common@1.47.1)(@platforma-sdk/model@1.80.2)(@platforma-sdk/ui-vue@1.80.4(@bytecodealliance/preview2-shim@0.17.8)(typescript@5.9.3))(d3-dispatch@3.0.1)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(typescript@5.9.3) + version: 1.2.4(@milaboratories/pl-model-common@1.48.0)(@platforma-sdk/model@1.80.2)(@platforma-sdk/ui-vue@1.80.4(@bytecodealliance/preview2-shim@0.17.8)(typescript@5.9.3))(d3-dispatch@3.0.1)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(typescript@5.9.3) '@platforma-sdk/model': specifier: 'catalog:' version: 1.80.2 @@ -133,7 +133,7 @@ importers: version: 1.3.0 '@platforma-sdk/block-tools': specifier: 'catalog:' - version: 2.12.6(@types/node@24.5.2) + version: 2.14.3(@types/node@24.5.2) eslint: specifier: 'catalog:' version: 9.39.2 @@ -211,7 +211,7 @@ importers: devDependencies: '@platforma-sdk/tengo-builder': specifier: 'catalog:' - version: 4.0.18 + version: 4.0.23 '@platforma-sdk/test': specifier: 'catalog:' version: 1.80.3(@bytecodealliance/preview2-shim@0.17.8)(@types/node@24.5.2)(vite@8.0.16(@types/node@24.5.2)(yaml@2.8.2)) @@ -866,11 +866,20 @@ packages: resolution: {integrity: sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==} engines: {node: '>=12.10.0'} + '@grpc/grpc-js@1.14.4': + resolution: {integrity: sha512-k9Dj3DV/itK9D06Y8f190Qgop7/Ui+D0njFV3LHMPwPT75DpXLQohE9Wmz0QElrJnzsjB7KPWiKJbOl7IPDArQ==} + engines: {node: '>=12.10.0'} + '@grpc/proto-loader@0.7.13': resolution: {integrity: sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==} engines: {node: '>=6'} hasBin: true + '@grpc/proto-loader@0.8.1': + resolution: {integrity: sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==} + engines: {node: '>=6'} + hasBin: true + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -1128,6 +1137,10 @@ packages: resolution: {integrity: sha512-0mS5GQAQYUab0qj1C6+IXafNJhL1ahnhPBmIkLq9VqJU8p9jNZ7KC5ln4/pl0tK47brdPljCLEiLg8IdSa6wFw==} engines: {node: '>=22'} + '@milaboratories/helpers@1.14.5': + resolution: {integrity: sha512-Kiy0g7sEmFQxDwtnmTrdJ8XdUK0IDAB5ZnPCNEbK7lPGHIa+xG3kzfeR4y44QBdrYaK0NwG63D8Fc7dlv9KItg==} + engines: {node: '>=22'} + '@milaboratories/miplots4@1.0.176': resolution: {integrity: sha512-PT2kw5H+YVYR8RNp4SOeP8Pi+sEbnptDQYsQYnwAhjNqfLjBG2kFvDeIXTrULPMW2L1L+hKwdRcEpDrO1ysUKg==} @@ -1165,6 +1178,10 @@ packages: resolution: {integrity: sha512-+0ih1njQn4wvLjGmFKboKFRlWYLrzg7QdRK2aNo8ivTKY16TrsMRCk0GQEt0aPKELnv7QSrsNSjg7e7GSdGMDQ==} engines: {node: '>=22.19.0'} + '@milaboratories/pl-client@3.14.7': + resolution: {integrity: sha512-HIjPfGAYRRD0hbq5YSTkWt5XgBiv+yYtw73EjWLxui8eUEeB2ffdgsvH7IhVx2oXjUOL4p7M4i8PBnmdJ3830w==} + engines: {node: '>=22.19.0'} + '@milaboratories/pl-config@1.8.4': resolution: {integrity: sha512-eBZCIVhl9jRigyJUPURCxH2f2OrU5aToihSrwmTkCOu2rjr1spe4YGMIh9CxPr8Z1GkfQbez3crJGME1+RZrrg==} @@ -1196,18 +1213,27 @@ packages: '@milaboratories/pl-model-backend@1.4.16': resolution: {integrity: sha512-MrJwM3aOzrqr3+GVpS/1Zh/SOH/2tBtoyk/+eE6C7LRjofoip8GIF4be8AgPAM3C+6wTSGrQ5x3CM6A8Ql2HVA==} + '@milaboratories/pl-model-backend@1.4.21': + resolution: {integrity: sha512-Z2z5J8bglslgXXoi1aySi1ho+/d+ylJiuEaRw9XTNuE5iBM3EQPe0rw095pXGOinTlMpmvUcCI2wkVCraUQ1dA==} + '@milaboratories/pl-model-common@1.46.2': resolution: {integrity: sha512-VEeauisApYScvCS8lnK3zpFJ520xuTAodKJmjR8ulHcMrWMyWMfHEdGb7j5OMD0mM/OwTgmQrrJ5eB7Xd+xoOQ==} '@milaboratories/pl-model-common@1.47.1': resolution: {integrity: sha512-5F9I8JvUPRJ2HEzz5MmTio6JFYnhYoMVHfToh2NyXyOyQgAF6jhOzZBOsrEig9NfCPZBRB7v6UjmIK43dG4o/g==} + '@milaboratories/pl-model-common@1.48.0': + resolution: {integrity: sha512-oCVrjFNmjQolb7YWnbSGHnp6GGVm1PhG1lnNABp9lqYjvA2ISjIPQLIo/vT2ca0mqwLrEvbuRqiqSFOg7sQ5tQ==} + '@milaboratories/pl-model-middle-layer@1.30.13': resolution: {integrity: sha512-owQVozyub/6aKgk+yce+9sDK+t0HrpLbnlKJqQjYJahuhoju8j6VMY17AWgvADSDCiI3gnp/Tm/I4Ur6ySs1Cg==} '@milaboratories/pl-model-middle-layer@1.30.7': resolution: {integrity: sha512-rs9x3Ron4ujR/UOdEgB8WUB1SvZ8ZAScT1Av/e4or+iiQ/CzhmK9nqtYamHVwKV+JgwIFbXQwxGvIHDVz955dQ==} + '@milaboratories/pl-model-middle-layer@1.32.0': + resolution: {integrity: sha512-X1iLGgOwzkw8mQ/GfTxfOTJakBJ26np85h5HqUziMbVkFL0SR7wpd7xpgUs6TKVkYNM8viAv3iA2k63Yc5SahQ==} + '@milaboratories/pl-tree@1.13.1': resolution: {integrity: sha512-NtVVUSF9eJA5v5iUEfGgKl0JbG/EIvy+YCsxzCZ1dXG1S3gCNwt01Nj5PkWW7FkDG1IT0ZfwyeO0mVVz2kIv1Q==} engines: {node: '>=22.19.0'} @@ -1240,6 +1266,10 @@ packages: resolution: {integrity: sha512-vZDcIBta7I935VjKk6qGdhpjz2pWXe5CbWv1UUmPrcG8wsunDpWuet8/Gi75N97CpcLDc2LLHd/url9K8ZNpAg==} engines: {node: '>=22.19.0'} + '@milaboratories/ts-helpers@1.8.6': + resolution: {integrity: sha512-ef01tARUl+0Urt3x8HHAByrhYHg4Rnn7WiFyJ+joZFZl1T1pUKTgfB7Zxc8Cn06HIl4i6H7s5OSymjZePIGqfQ==} + engines: {node: '>=22.19.0'} + '@milaboratories/uikit@2.15.16': resolution: {integrity: sha512-HOl1YsPKHpj3izKAs0hEvVtFE8ZbrOnD5KsAqSHxvipQyfAdOLbfB7ak6XPdPKbgsvH5opfMws7usryR6Nlulw==} @@ -1615,6 +1645,10 @@ packages: resolution: {integrity: sha512-MtsYTCNhCCA9drSihSh5uXUY2/qCNai/ekOX2pw2gtzjHPXPqnin1Wzri0z/2N1wOShXodRItBUglALNJFWUsQ==} hasBin: true + '@platforma-sdk/block-tools@2.14.3': + resolution: {integrity: sha512-CD0NPfUoXiJhl6JArC057oCXbqKkJf5HJsmrlZShnh6lKRaIQlibF8VUqbpBi9+PopsGCQ4/s5HnLR7wQoWDzg==} + hasBin: true + '@platforma-sdk/blocks-deps-updater@2.2.0': resolution: {integrity: sha512-p9lBxhFXM9WoRsrJO7dfkiXSK+1m63yIn1sKhBO71eMbhrLMyVYHEOeNf3w5OCdbRF5QsNhXzWuiTmFK3zHFsA==} hasBin: true @@ -1625,8 +1659,11 @@ packages: '@platforma-sdk/package-builder-lib@1.2.0': resolution: {integrity: sha512-AtSzaZ39BGlqcyYtpGREDz+YlGCDdSTQJNV/+NkTyUtoq1ECRO+iMqAX1DSSza1WqylnURX8vKVI2owS/cxntA==} - '@platforma-sdk/tengo-builder@4.0.18': - resolution: {integrity: sha512-nGPB5fE6b9ITFuXVMRakr2MyqsnL4TVE09jZ2X7TCPgb31k4gIhYCT+XU9vBRavBj/U3dt+jSWHkcbX9JvJKYA==} + '@platforma-sdk/package-builder-lib@1.3.0': + resolution: {integrity: sha512-CdBjmNo6E1fBxKYWaXa49L/L2WLURxs2f1TAqxLIZlHRE4DZ6E1TEj3jNNKESWp+/9rwtLkTAzmTzNPrDgz+2Q==} + + '@platforma-sdk/tengo-builder@4.0.23': + resolution: {integrity: sha512-Qkxpg3wuZQOc6KbEL3pifOCdqOH91TaL3jDluVIm5mz3qpx96KO+To8AT/0nLqmvaF7yv8AP22QiyyqduAhGiw==} engines: {node: '>=22'} hasBin: true @@ -1667,12 +1704,21 @@ packages: '@protobufjs/codegen@2.0.4': resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + '@protobufjs/codegen@2.0.5': + resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} + '@protobufjs/eventemitter@1.1.0': resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + '@protobufjs/eventemitter@1.1.1': + resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==} + '@protobufjs/fetch@1.1.0': resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + '@protobufjs/fetch@1.1.1': + resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==} + '@protobufjs/float@1.0.2': resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} @@ -1688,6 +1734,9 @@ packages: '@protobufjs/utf8@1.1.0': resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + '@protobufjs/utf8@1.1.2': + resolution: {integrity: sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==} + '@rolldown/binding-android-arm64@1.0.3': resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -5524,6 +5573,10 @@ packages: resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==} engines: {node: '>=12.0.0'} + protobufjs@7.6.6: + resolution: {integrity: sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==} + engines: {node: '>=12.0.0'} + pump@3.0.2: resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} @@ -7459,6 +7512,11 @@ snapshots: '@grpc/proto-loader': 0.7.13 '@js-sdsl/ordered-map': 4.4.2 + '@grpc/grpc-js@1.14.4': + dependencies: + '@grpc/proto-loader': 0.8.1 + '@js-sdsl/ordered-map': 4.4.2 + '@grpc/proto-loader@0.7.13': dependencies: lodash.camelcase: 4.3.0 @@ -7466,6 +7524,13 @@ snapshots: protobufjs: 7.4.0 yargs: 17.7.2 + '@grpc/proto-loader@0.8.1': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.6.6 + yargs: 17.7.2 + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -7765,12 +7830,38 @@ snapshots: - supports-color - typescript + '@milaboratories/graph-maker@1.2.4(@milaboratories/pl-model-common@1.48.0)(@platforma-sdk/model@1.80.2)(@platforma-sdk/ui-vue@1.80.4(@bytecodealliance/preview2-shim@0.17.8)(typescript@5.9.3))(d3-dispatch@3.0.1)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(typescript@5.9.3)': + dependencies: + '@ag-grid-community/core': 32.3.4 + '@milaboratories/helpers': 1.13.7 + '@milaboratories/miplots4': 1.0.176(d3-dispatch@3.0.1)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0) + '@milaboratories/pf-plots': 1.1.66(@milaboratories/pl-model-common@1.48.0)(@platforma-sdk/model@1.80.2) + '@platforma-sdk/model': 1.80.2 + '@platforma-sdk/ui-vue': 1.80.4(@bytecodealliance/preview2-shim@0.17.8)(typescript@5.9.3) + '@types/d3-hierarchy': 3.1.7 + '@types/d3-scale': 4.0.9 + '@vueuse/core': 13.5.0(vue@3.5.26(typescript@5.9.3)) + ag-grid-vue3: 34.1.2(vue@3.5.26(typescript@5.9.3)) + canonicalize: 2.1.0 + d3-hierarchy: 3.1.2 + d3-scale: 4.0.2 + vue: 3.5.26(typescript@5.9.3) + transitivePeerDependencies: + - '@milaboratories/pl-model-common' + - d3-dispatch + - d3-path + - d3-scale-chromatic + - supports-color + - typescript + '@milaboratories/helpers@1.13.7': {} '@milaboratories/helpers@1.14.2': {} '@milaboratories/helpers@1.14.4': {} + '@milaboratories/helpers@1.14.5': {} + '@milaboratories/miplots4@1.0.176(d3-dispatch@3.0.1)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)': dependencies: '@d3fc/d3fc-chart': 5.1.9(d3-array@3.2.4)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(d3-scale@4.0.2)(d3-selection@3.0.0)(d3-shape@3.2.0) @@ -7831,6 +7922,13 @@ snapshots: canonicalize: 2.1.0 lodash: 4.17.23 + '@milaboratories/pf-plots@1.1.66(@milaboratories/pl-model-common@1.48.0)(@platforma-sdk/model@1.80.2)': + dependencies: + '@milaboratories/pl-model-common': 1.48.0 + '@platforma-sdk/model': 1.80.2 + canonicalize: 2.1.0 + lodash: 4.17.23 + '@milaboratories/pf-spec-driver@1.4.21(@bytecodealliance/preview2-shim@0.17.8)': dependencies: '@milaboratories/helpers': 1.14.4 @@ -7888,6 +7986,24 @@ snapshots: utility-types: 3.11.0 yaml: 2.8.2 + '@milaboratories/pl-client@3.14.7': + dependencies: + '@grpc/grpc-js': 1.14.4 + '@milaboratories/pl-http': 1.2.4 + '@milaboratories/pl-model-common': 1.48.0 + '@milaboratories/ts-helpers': 1.8.6 + '@protobuf-ts/grpc-transport': 2.11.1(@grpc/grpc-js@1.14.4) + '@protobuf-ts/runtime': 2.11.1 + '@protobuf-ts/runtime-rpc': 2.11.1 + canonicalize: 2.1.0 + denque: 2.1.0 + long: 5.3.2 + lru-cache: 11.2.4 + openapi-fetch: 0.15.0 + undici: 7.16.0 + utility-types: 3.11.0 + yaml: 2.8.2 + '@milaboratories/pl-config@1.8.4': dependencies: '@milaboratories/ts-helpers': 1.8.5 @@ -8001,6 +8117,12 @@ snapshots: canonicalize: 2.1.0 zod: 3.25.76 + '@milaboratories/pl-model-backend@1.4.21': + dependencies: + '@milaboratories/pl-client': 3.14.7 + canonicalize: 2.1.0 + zod: 3.25.76 + '@milaboratories/pl-model-common@1.46.2': dependencies: '@milaboratories/helpers': 1.14.2 @@ -8016,6 +8138,14 @@ snapshots: es-toolkit: 1.39.10 zod: 3.25.76 + '@milaboratories/pl-model-common@1.48.0': + dependencies: + '@milaboratories/helpers': 1.14.5 + '@milaboratories/pl-error-like': 1.12.10 + canonicalize: 2.1.0 + es-toolkit: 1.39.10 + zod: 3.25.76 + '@milaboratories/pl-model-middle-layer@1.30.13': dependencies: '@milaboratories/helpers': 1.14.4 @@ -8032,6 +8162,14 @@ snapshots: utility-types: 3.11.0 zod: 3.25.76 + '@milaboratories/pl-model-middle-layer@1.32.0': + dependencies: + '@milaboratories/helpers': 1.14.5 + '@milaboratories/pl-model-common': 1.48.0 + es-toolkit: 1.39.10 + utility-types: 3.11.0 + zod: 3.25.76 + '@milaboratories/pl-tree@1.13.1': dependencies: '@milaboratories/computable': 2.9.7 @@ -8185,6 +8323,12 @@ snapshots: canonicalize: 2.1.0 denque: 2.1.0 + '@milaboratories/ts-helpers@1.8.6': + dependencies: + '@milaboratories/helpers': 1.14.5 + canonicalize: 2.1.0 + denque: 2.1.0 + '@milaboratories/uikit@2.15.16(typescript@5.9.3)': dependencies: '@milaboratories/helpers': 1.14.4 @@ -8537,6 +8681,31 @@ snapshots: - '@types/node' - aws-crt + '@platforma-sdk/block-tools@2.14.3(@types/node@24.5.2)': + dependencies: + '@aws-sdk/client-ecr-public': 3.859.0 + '@aws-sdk/client-s3': 3.859.0 + '@inquirer/prompts': 7.10.1(@types/node@24.5.2) + '@milaboratories/pl-http': 1.2.4 + '@milaboratories/pl-model-backend': 1.4.21 + '@milaboratories/pl-model-common': 1.48.0 + '@milaboratories/pl-model-middle-layer': 1.32.0 + '@milaboratories/resolve-helper': 1.1.3 + '@milaboratories/ts-helpers': 1.8.6 + '@platforma-sdk/blocks-deps-updater': 2.2.0 + '@platforma-sdk/package-builder-lib': 1.3.0 + canonicalize: 2.1.0 + commander: 15.0.0 + lru-cache: 11.2.4 + mime-types: 2.1.35 + tar: 7.5.2 + undici: 7.16.0 + yaml: 2.8.2 + zod: 3.25.76 + transitivePeerDependencies: + - '@types/node' + - aws-crt + '@platforma-sdk/blocks-deps-updater@2.2.0': dependencies: yaml: 2.8.2 @@ -8568,12 +8737,25 @@ snapshots: transitivePeerDependencies: - aws-crt - '@platforma-sdk/tengo-builder@4.0.18': + '@platforma-sdk/package-builder-lib@1.3.0': dependencies: - '@milaboratories/pl-model-backend': 1.4.16 + '@aws-sdk/client-s3': 3.859.0 + '@aws-sdk/lib-storage': 3.859.0(@aws-sdk/client-s3@3.859.0) + '@milaboratories/resolve-helper': 1.1.3 + archiver: 7.0.1 + undici: 7.16.0 + winston: 3.17.0 + yaml: 2.8.2 + zod: 3.25.76 + transitivePeerDependencies: + - aws-crt + + '@platforma-sdk/tengo-builder@4.0.23': + dependencies: + '@milaboratories/pl-model-backend': 1.4.21 '@milaboratories/resolve-helper': 1.1.3 '@milaboratories/tengo-tester': 1.6.4 - '@milaboratories/ts-helpers': 1.8.5 + '@milaboratories/ts-helpers': 1.8.6 commander: 15.0.0 winston: 3.17.0 @@ -8584,7 +8766,7 @@ snapshots: '@milaboratories/pl-middle-layer': 1.66.3(@bytecodealliance/preview2-shim@0.17.8)(@types/node@24.5.2) '@milaboratories/pl-tree': 1.13.1 '@platforma-sdk/model': 1.80.2 - '@vitest/coverage-istanbul': 4.1.9(vitest@4.1.9) + '@vitest/coverage-istanbul': 4.1.9(vitest@4.0.16(@types/node@24.5.2)(lightningcss@1.32.0)(yaml@2.8.2)) vitest: 4.1.9(@types/node@24.5.2)(@vitest/coverage-istanbul@4.1.9)(vite@6.4.1(@types/node@24.5.2)(lightningcss@1.32.0)(yaml@2.8.2)) transitivePeerDependencies: - '@bytecodealliance/preview2-shim' @@ -8682,6 +8864,12 @@ snapshots: '@protobuf-ts/runtime': 2.11.1 '@protobuf-ts/runtime-rpc': 2.11.1 + '@protobuf-ts/grpc-transport@2.11.1(@grpc/grpc-js@1.14.4)': + dependencies: + '@grpc/grpc-js': 1.14.4 + '@protobuf-ts/runtime': 2.11.1 + '@protobuf-ts/runtime-rpc': 2.11.1 + '@protobuf-ts/plugin@2.11.1': dependencies: '@bufbuild/protobuf': 2.5.2 @@ -8707,13 +8895,21 @@ snapshots: '@protobufjs/codegen@2.0.4': {} + '@protobufjs/codegen@2.0.5': {} + '@protobufjs/eventemitter@1.1.0': {} + '@protobufjs/eventemitter@1.1.1': {} + '@protobufjs/fetch@1.1.0': dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/inquire': 1.1.0 + '@protobufjs/fetch@1.1.1': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/float@1.0.2': {} '@protobufjs/inquire@1.1.0': {} @@ -8724,6 +8920,8 @@ snapshots: '@protobufjs/utf8@1.1.0': {} + '@protobufjs/utf8@1.1.2': {} + '@rolldown/binding-android-arm64@1.0.3': optional: true @@ -11465,6 +11663,22 @@ snapshots: vite: 8.0.16(@types/node@24.5.2)(yaml@2.8.2) vue: 3.5.26(typescript@5.9.3) + '@vitest/coverage-istanbul@4.1.9(vitest@4.0.16(@types/node@24.5.2)(lightningcss@1.32.0)(yaml@2.8.2))': + dependencies: + '@babel/core': 7.29.0 + '@istanbuljs/schema': 0.1.3 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.2 + obug: 2.1.1 + tinyrainbow: 3.1.0 + vitest: 4.0.16(@types/node@24.5.2)(lightningcss@1.32.0)(yaml@2.8.2) + transitivePeerDependencies: + - supports-color + '@vitest/coverage-istanbul@4.1.9(vitest@4.1.9)': dependencies: '@babel/core': 7.29.0 @@ -11477,7 +11691,7 @@ snapshots: magicast: 0.5.2 obug: 2.1.1 tinyrainbow: 3.1.0 - vitest: 4.1.9(@types/node@24.5.2)(@vitest/coverage-istanbul@4.1.9)(vite@8.0.16(@types/node@24.5.2)(yaml@2.8.2)) + vitest: 4.1.9(@types/node@24.5.2)(@vitest/coverage-istanbul@4.1.9)(vite@6.4.1(@types/node@24.5.2)(lightningcss@1.32.0)(yaml@2.8.2)) transitivePeerDependencies: - supports-color @@ -13272,6 +13486,20 @@ snapshots: '@types/node': 24.5.2 long: 5.3.2 + protobufjs@7.6.6: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.5 + '@protobufjs/eventemitter': 1.1.1 + '@protobufjs/fetch': 1.1.1 + '@protobufjs/float': 1.0.2 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.2 + '@types/node': 24.5.2 + long: 5.3.2 + pump@3.0.2: dependencies: end-of-stream: 1.4.4 @@ -13967,7 +14195,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.5.2 - '@vitest/coverage-istanbul': 4.1.9(vitest@4.1.9) + '@vitest/coverage-istanbul': 4.1.9(vitest@4.0.16(@types/node@24.5.2)(lightningcss@1.32.0)(yaml@2.8.2)) transitivePeerDependencies: - msw diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 4ee5940..328e4c9 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -12,9 +12,9 @@ catalog: '@platforma-sdk/workflow-tengo': 6.7.2 '@platforma-sdk/model': 1.80.2 '@platforma-sdk/ui-vue': 1.80.4 - '@platforma-sdk/tengo-builder': 4.0.18 + '@platforma-sdk/tengo-builder': 4.0.23 '@platforma-sdk/package-builder': 3.14.1 - '@platforma-sdk/block-tools': 2.12.6 + '@platforma-sdk/block-tools': 2.14.3 '@platforma-sdk/test': 1.80.3 '@milaboratories/helpers': 1.13.7 '@milaboratories/graph-maker': 1.2.4 diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 86abdfe..0e37baa 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -37,6 +37,7 @@ export function useScChainOptions( const receptor = axisSpec.domain?.["pl7.app/vdj/receptor"]; + // Listed in classic biological order; values are domain letters, so TCR pairs read B, A. let options: { label: string; value: ScChain }[]; switch (receptor) { case "IG": @@ -47,14 +48,14 @@ export function useScChainOptions( break; case "TCRAB": options = [ - { label: "Alpha", value: "A" }, - { label: "Beta", value: "B" }, + { label: "Alpha", value: "B" }, + { label: "Beta", value: "A" }, ]; break; case "TCRGD": options = [ - { label: "Gamma", value: "A" }, - { label: "Delta", value: "B" }, + { label: "Gamma", value: "B" }, + { label: "Delta", value: "A" }, ]; break; default: diff --git a/workflow/src/main.tpl.tengo b/workflow/src/main.tpl.tengo index 45207c4..195a959 100644 --- a/workflow/src/main.tpl.tengo +++ b/workflow/src/main.tpl.tengo @@ -16,6 +16,13 @@ filter := func(arr, predicate) { return filtered } +// pl7.app/vdj/scClonotypeChain domain letter -> chain name, per receptor. +scChainNames := { + "IG": { "A": "Heavy", "B": "Light" }, + "TCRAB": { "A": "Beta", "B": "Alpha" }, + "TCRGD": { "A": "Delta", "B": "Gamma" } +} + /** Returns a spec for the given type of spectratype. @@ -25,12 +32,12 @@ filter := func(arr, predicate) { vGeneSpec: The spec for the vGene column. cdr3Spec: The spec for the cdr3Length column. lengthType: The type of sequence used as input. - scChain: The chain (only when processing single cell data). + chainName: The chain name (only when processing single cell data). Returns: A spec for the given type of spectratype. */ -getSpecs := func(type, abundanceSpec, vGeneSpec, cdr3Spec, lengthType, scChain) { +getSpecs := func(type, abundanceSpec, vGeneSpec, cdr3Spec, lengthType, chainName) { if type != "v" && type != "cdr3" { ll.panic("Invalid type: " + type) } @@ -44,8 +51,8 @@ getSpecs := func(type, abundanceSpec, vGeneSpec, cdr3Spec, lengthType, scChain) if lengthType != undefined { label = label + " (" + lengthType + ")" } - if scChain != undefined { - label = label + " " + scChain + if chainName != undefined { + label = label + " " + chainName } secondAxis = { column: "vGene", @@ -63,8 +70,8 @@ getSpecs := func(type, abundanceSpec, vGeneSpec, cdr3Spec, lengthType, scChain) if lengthType != undefined { label = label + " (" + lengthType + ")" } - if scChain != undefined { - label = label + " " + scChain + if chainName != undefined { + label = label + " " + chainName } secondAxis = { column: "CDR3", @@ -186,24 +193,14 @@ wf.body(func(args) { cdr3Col := undefined vGeneCol := undefined + chainName := undefined if isSingleCell { - // Map UI chain value to domain chain value based on receptor type - // For TCRAB and TCRGD, the domain mapping is reversed from the UI mapping - // UI: Alpha/Gamma -> "A", Beta/Delta -> "B" - // Domain: For TCRAB: "A" = Beta, "B" = Alpha; For TCRGD: "A" = Delta, "B" = Gamma receptor := datasetSpec.axesSpec[1].domain["pl7.app/vdj/receptor"] - domainChain := args.scChain - if receptor == "TCRAB" || receptor == "TCRGD" { - // Swap A and B for TCR receptors - if args.scChain == "A" { - domainChain = "B" - } else if args.scChain == "B" { - domainChain = "A" - } - } - + namesByLetter := scChainNames[receptor] + chainName = namesByLetter != undefined ? namesByLetter[args.scClonotypeChain] : args.scClonotypeChain + predicate := func(col) { - return col.spec.domain["pl7.app/vdj/scClonotypeChain"] == domainChain && col.spec.domain["pl7.app/vdj/scClonotypeChain/index"] == "primary" + return col.spec.domain["pl7.app/vdj/scClonotypeChain"] == args.scClonotypeChain && col.spec.domain["pl7.app/vdj/scClonotypeChain/index"] == "primary" } cdr3Cols := filter(columns.getColumns("cdr3"), predicate) ll.assert(len(cdr3Cols) == 1, "expected exactly 1 CDR3 column for chain, got %d", len(cdr3Cols)) @@ -348,14 +345,14 @@ wf.body(func(args) { vSpectratypeResult := xsv.importFile( ptResult.getFile("vSpectratype.result.tsv"), "tsv", - getSpecs("v", abundanceSpec, vGeneSpec, cdr3LengthSpec, args.lengthType, isSingleCell ? args.scChain : undefined), + getSpecs("v", abundanceSpec, vGeneSpec, cdr3LengthSpec, args.lengthType, chainName), {splitDataAndSpec: true} ) cdr3SpectratypeResult := xsv.importFile( ptResult.getFile("cdr3Spectratype.result.tsv"), "tsv", - getSpecs("cdr3", abundanceSpec, vGeneSpec, cdr3LengthSpec, args.lengthType, isSingleCell ? args.scChain : undefined), + getSpecs("cdr3", abundanceSpec, vGeneSpec, cdr3LengthSpec, args.lengthType, chainName), {splitDataAndSpec: true} )