From 1582c23989f1d2999c382995a020a513d758c60c Mon Sep 17 00:00:00 2001 From: bbimber Date: Tue, 25 Nov 2025 08:48:10 -0800 Subject: [PATCH] Merge 25.7 to 25.11 (#360) * Update view sort * Add wrapper for IdentifyAndStoreActiveClonotypes * Error checking for CommonFilters * Update defaults * Set baseUrl in script * Do not storeStimLevelData for TRA * Do not force seurat object name to match readset * Do not force seurat object name to match readset * Drop CS-Core and Tricycle * Expand data cleanup * Minor code cleanup * Add subadapter + reflection pattern to ExtendedVariantAdapter in order to delete VCFTabixAdapter (#353) * Add subadapter + reflection pattern to ExtendedVariantAdapter in order to delete VCFTabixAdapter * Add API wrapper for getMetadata and getHeader --------- Co-authored-by: Sebastian Benjamin * Correct HTML syntax * Expand study triggers and update cohort fields * Expose getter for StudiesTriggerFactory * Clean up trigger/customizer layer code * Create fields to coalesce name/label for studies * Add null check * Switch default * Bugfix to StudiesTriggerFactory * Update default * Improve SnpEff index check * Switch sequence init tasks to use webserver-high-priority * Build short delay into github triggers to aid cross-repo commits * Switch ETLs to log row count discrepancies * Update dependencies * Support sawfish --sample-csv arg * Option to create readsets from SRA (#355) * Option to create readsets from SRA * Test fix * Error check * Add nimble/bulk step * Allow nimble step to use cached barcodes * Bugfix to NimbleAlignmentStep * Bugfix to NimbleAlignmentStep * Bugfix to NimbleAlignmentStep * Bugfix to NimbleAlignmentStep * Add CD4_Activation_Axis * Better support readsets created directly from SRA * Expand BAM header * Bugfix to RestoreSraDataHandler * Bugfix to RestoreSraDataHandler for new SRA datasets * Bugfix to RestoreSraDataHandler for new SRA datasets * Bugfix to RestoreSraDataHandler for new SRA datasets * Bugfix to RestoreSraDataHandler for new SRA datasets * Reduce logging * Update sawfish install * Better error handling * Updates to Save10xBarcodes * Expand StudyMetadata cohorts * Throw exception when existing file present * Improve resume for ReadsetInitTask * Improve resume for ReadsetInitTask * Bugfix to Save10xBarcodes * Bugfix to handling of 10x barcodes * Switch nimble/CR barcodes to CB alone * Omit writing to 10x barcodes * Add another coalesce() term in case name and label are blank * Bugfix to study import * Fix typo * Add manage freezer button and expand TagPcrSummaryStep * Change the pattern of nimble fastq-to-bam * Add check for integers * Expand ETL/presentation of SIV data * Expand ETL/presentation of SIV data * Int -> long * npm dependency updates * Build fix --------- Co-authored-by: hextraza Co-authored-by: Sebastian Benjamin --- .github/workflows/build.yml | 5 + .../pipeline_code/extra_tools_install.sh | 25 +- .../sequenceanalysis/sequence_readsets.js | 14 +- .../Assigned to Run Lacking Data.qview.xml | 2 +- .../panel/SequenceImportPanel.js | 4 +- .../pipeline/AlignmentInitTask.java | 1 + .../pipeline/CreateReferenceLibraryTask.java | 5 + .../pipeline/ReadsetInitTask.java | 33 +- .../SequenceOutputHandlerInitTask.java | 2 +- .../SequenceReadsetHandlerInitTask.java | 2 +- .../query/SequenceTriggerHelper.java | 74 +- .../run/RestoreSraDataHandler.java | 68 +- .../run/analysis/SawfishAnalysis.java | 65 +- .../analysis/SawfishJointCallingHandler.java | 48 +- .../run/preprocessing/TagPcrSummaryStep.java | 2 +- .../run/util/RnaSeQCWrapper.java | 26 +- .../run/variant/SNPEffStep.java | 12 +- .../external/labModules/SequenceTest.java | 50 + .../labkey/api/studies/StudiesService.java | 3 + .../queries/studies/studyCohorts/.qview.xml | 22 + .../postgresql/studies-23.005-23.006.sql | 1 + .../sqlserver/studies-23.005-23.006.sql | 1 + Studies/resources/schemas/studies.xml | 14 +- Studies/resources/views/studiesOverview.html | 6 +- .../org/labkey/studies/StudiesManager.java | 2 +- .../src/org/labkey/studies/StudiesModule.java | 2 +- .../labkey/studies/StudiesServiceImpl.java | 8 + .../studies/query/StudiesTriggerFactory.java | 47 +- .../studies/query/StudiesUserSchema.java | 34 +- .../discvrcore/DiscvrCoreController.java | 1 - jbrowse/package-lock.json | 920 +++++++++--------- .../ExtendedVariantAdapter.ts | 74 +- .../ExtendedVariantAdapter/VcfTabixAdapter.ts | 159 --- singlecell/resources/chunks/CommonFilters.R | 5 +- .../chunks/IdentifyAndStoreActiveClonotypes.R | 22 + singlecell/resources/chunks/RunCsCore.R | 14 - singlecell/resources/chunks/RunTricycle.R | 12 - singlecell/resources/chunks/StudyMetadata.R | 2 + .../resources/queries/singlecell/samples.js | 2 +- .../labkey/singlecell/SingleCellModule.java | 8 +- .../analysis/AbstractSingleCellHandler.java | 15 +- .../CalculateGeneComponentScores.java | 2 +- .../pipeline/singlecell/CommonFilters.java | 4 +- .../IdentifyAndStoreActiveClonotypes.java | 37 + .../pipeline/singlecell/RunCsCore.java | 67 -- .../pipeline/singlecell/RunTricycle.java | 37 - .../pipeline/singlecell/StudyMetadata.java | 2 +- .../pipeline/singlecell/SubsetSeurat.java | 2 +- .../pipeline/singlecell/VireoHandler.java | 2 +- .../run/CellRangerGexCountStep.java | 97 +- .../singlecell/run/NimbleAlignmentStep.java | 126 ++- .../labkey/singlecell/run/NimbleAnalysis.java | 4 +- .../run/NimbleBulkAlignmentStep.java | 184 ++++ .../labkey/singlecell/run/NimbleHelper.java | 89 +- 54 files changed, 1594 insertions(+), 871 deletions(-) create mode 100644 Studies/resources/queries/studies/studyCohorts/.qview.xml create mode 100644 Studies/resources/schemas/dbscripts/postgresql/studies-23.005-23.006.sql create mode 100644 Studies/resources/schemas/dbscripts/sqlserver/studies-23.005-23.006.sql delete mode 100644 jbrowse/src/client/JBrowse/Browser/plugins/ExtendedVariantPlugin/ExtendedVariantAdapter/VcfTabixAdapter.ts create mode 100644 singlecell/resources/chunks/IdentifyAndStoreActiveClonotypes.R delete mode 100644 singlecell/resources/chunks/RunCsCore.R delete mode 100644 singlecell/resources/chunks/RunTricycle.R create mode 100644 singlecell/src/org/labkey/singlecell/pipeline/singlecell/IdentifyAndStoreActiveClonotypes.java delete mode 100644 singlecell/src/org/labkey/singlecell/pipeline/singlecell/RunCsCore.java delete mode 100644 singlecell/src/org/labkey/singlecell/pipeline/singlecell/RunTricycle.java create mode 100644 singlecell/src/org/labkey/singlecell/run/NimbleBulkAlignmentStep.java diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3578fca0b..eb3c0794e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,6 +31,11 @@ jobs: echo "DEFAULT_BRANCH=${DEFAULT_BRANCH}" >> $GITHUB_ENV id: default-branch + # Note: use slight delay in case there are associated commits across repos + - name: "Sleep for 30 seconds" + run: sleep 30s + shell: bash + - name: "Build DISCVR" uses: bimberlabinternal/DevOps/githubActions/discvr-build@master with: diff --git a/SequenceAnalysis/pipeline_code/extra_tools_install.sh b/SequenceAnalysis/pipeline_code/extra_tools_install.sh index 8ecd60f37..5ba95e5dd 100755 --- a/SequenceAnalysis/pipeline_code/extra_tools_install.sh +++ b/SequenceAnalysis/pipeline_code/extra_tools_install.sh @@ -325,11 +325,28 @@ then echo "Cleaning up previous installs" rm -Rf $LKTOOLS_DIR/sawfish* - wget https://github.com/PacificBiosciences/sawfish/releases/download/v2.0.0/sawfish-v2.0.0-x86_64-unknown-linux-gnu.tar.gz - tar -xzf sawfish-v2.0.0-x86_64-unknown-linux-gnu.tar.gz + wget https://github.com/PacificBiosciences/sawfish/releases/download/v2.2.0/sawfish-v2.2.0-x86_64-unknown-linux-gnu.tar.gz + tar -xzf sawfish-v2.2.0-x86_64-unknown-linux-gnu.tar.gz - mv sawfish-v2.0.0-x86_64-unknown-linux-gnu $LKTOOLS_DIR/ - ln -s $LKTOOLS_DIR/sawfish-v2.0.0/bin/sawfish $LKTOOLS_DIR/ + mv sawfish-v2.2.0-x86_64-unknown-linux-gnu $LKTOOLS_DIR/ + ln -s $LKTOOLS_DIR/sawfish-v2.2.0-x86_64-unknown-linux-gnu/bin/sawfish $LKTOOLS_DIR/ +else + echo "Already installed" +fi + + +if [[ ! -e ${LKTOOLS_DIR}/primer3_core || ! -z $FORCE_REINSTALL ]]; +then + echo "Cleaning up previous installs" + rm -Rf $LKTOOLS_DIR/primer3_core* + rm -Rf primer3* + rm -Rf v2.6.1.tar.gz + + wget https://github.com/primer3-org/primer3/archive/refs/tags/v2.6.1.tar.gz + tar -xf v2.6.1.tar.gz + cd primer3-2.6.1/src + make + install primer3_core $LKTOOLS_DIR/ else echo "Already installed" fi diff --git a/SequenceAnalysis/resources/queries/sequenceanalysis/sequence_readsets.js b/SequenceAnalysis/resources/queries/sequenceanalysis/sequence_readsets.js index 4556ab827..9c3c14cb4 100644 --- a/SequenceAnalysis/resources/queries/sequenceanalysis/sequence_readsets.js +++ b/SequenceAnalysis/resources/queries/sequenceanalysis/sequence_readsets.js @@ -1,11 +1,15 @@ -/* - * Copyright (c) 2012 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 - */ +var LABKEY = require("labkey"); + +var triggerHelper = new org.labkey.sequenceanalysis.query.SequenceTriggerHelper(LABKEY.Security.currentUser.id, LABKEY.Security.currentContainer.id); function beforeDelete(row, errors){ if (!this.extraContext.deleteFromServer){ errors._form = 'You cannot directly delete readsets. To delete these records, use the delete button above the readset grid.'; } +} + +function afterInsert(row, errors) { + if (row.sraAccessions) { + triggerHelper.createReaddataForSra(row.rowid, row.sraAccessions); + } } \ No newline at end of file diff --git a/SequenceAnalysis/resources/queries/sequenceanalysis/sequence_readsets/Assigned to Run Lacking Data.qview.xml b/SequenceAnalysis/resources/queries/sequenceanalysis/sequence_readsets/Assigned to Run Lacking Data.qview.xml index 2a9801077..a9bfce5ec 100644 --- a/SequenceAnalysis/resources/queries/sequenceanalysis/sequence_readsets/Assigned to Run Lacking Data.qview.xml +++ b/SequenceAnalysis/resources/queries/sequenceanalysis/sequence_readsets/Assigned to Run Lacking Data.qview.xml @@ -1,6 +1,6 @@ - + diff --git a/SequenceAnalysis/resources/web/SequenceAnalysis/panel/SequenceImportPanel.js b/SequenceAnalysis/resources/web/SequenceAnalysis/panel/SequenceImportPanel.js index 711144294..17212476a 100644 --- a/SequenceAnalysis/resources/web/SequenceAnalysis/panel/SequenceImportPanel.js +++ b/SequenceAnalysis/resources/web/SequenceAnalysis/panel/SequenceImportPanel.js @@ -2436,7 +2436,7 @@ Ext4.define('SequenceAnalysis.panel.SequenceImportPanel', { },{ xtype: 'textfield', fieldLabel: 'Delimiter', - value: '_', + value: '[_-]', itemId: 'delimiter' }], buttons: [{ @@ -2455,7 +2455,7 @@ Ext4.define('SequenceAnalysis.panel.SequenceImportPanel', { if (prefix) { fg = fg.replace(new RegExp('^' + prefix), ''); } - fg = fg.split(delim); + fg = fg.split(RegExp(delim)); var id = fg[0]; if (Ext4.isNumeric(id)) { r.set('readset', id); diff --git a/SequenceAnalysis/src/org/labkey/sequenceanalysis/pipeline/AlignmentInitTask.java b/SequenceAnalysis/src/org/labkey/sequenceanalysis/pipeline/AlignmentInitTask.java index 00df2a90c..356a73781 100644 --- a/SequenceAnalysis/src/org/labkey/sequenceanalysis/pipeline/AlignmentInitTask.java +++ b/SequenceAnalysis/src/org/labkey/sequenceanalysis/pipeline/AlignmentInitTask.java @@ -43,6 +43,7 @@ public static class Factory extends AbstractSequenceTaskFactory public Factory() { super(AlignmentInitTask.class); + setLocation("webserver-high-priority"); setJoin(true); } diff --git a/SequenceAnalysis/src/org/labkey/sequenceanalysis/pipeline/CreateReferenceLibraryTask.java b/SequenceAnalysis/src/org/labkey/sequenceanalysis/pipeline/CreateReferenceLibraryTask.java index 6e7fcc526..ee424a3a5 100644 --- a/SequenceAnalysis/src/org/labkey/sequenceanalysis/pipeline/CreateReferenceLibraryTask.java +++ b/SequenceAnalysis/src/org/labkey/sequenceanalysis/pipeline/CreateReferenceLibraryTask.java @@ -165,6 +165,11 @@ public RecordedActionSet run() throws PipelineJobException libraryMembers = new TableSelector(libraryMembersTable, new SimpleFilter(FieldKey.fromString("library_id"), getPipelineJob().getLibraryId()), new Sort("ref_nt_id/name")).getArrayList(ReferenceLibraryMember.class); } + if (libraryMembers == null) + { + throw new PipelineJobException("There are no sequences in the library: " + getPipelineJob().getLibraryId()); + } + getJob().getLogger().info("there are " + libraryMembers.size() + " sequences to process"); //make sure sequence names are unique diff --git a/SequenceAnalysis/src/org/labkey/sequenceanalysis/pipeline/ReadsetInitTask.java b/SequenceAnalysis/src/org/labkey/sequenceanalysis/pipeline/ReadsetInitTask.java index 92ae0ec75..d0e442ef7 100644 --- a/SequenceAnalysis/src/org/labkey/sequenceanalysis/pipeline/ReadsetInitTask.java +++ b/SequenceAnalysis/src/org/labkey/sequenceanalysis/pipeline/ReadsetInitTask.java @@ -430,6 +430,10 @@ else if (TaskFileManager.InputFileTreatment.compress == inputFileTreatment) moveInputToAnalysisDir(compressed, job, actions, unalteredInputs, outputFiles); } } + else + { + job.getLogger().debug("Input file does not exist, may have already been moved: " + input.getPath()); + } } } else @@ -450,23 +454,40 @@ private static void moveInputToAnalysisDir(File input, SequenceJob job, Collecti File outputDir = job.getAnalysisDirectory(); File output = new File(outputDir, input.getName()); job.getLogger().debug("Destination: " + output.getPath()); + boolean alreadyMoved = false; if (output.exists()) { + job.getLogger().debug("output already exists"); if (unalteredInputs != null && unalteredInputs.contains(output)) { job.getLogger().debug("\tThis input was unaltered during normalization and a copy already exists in the analysis folder so the original will be discarded"); input.delete(); - TaskFileManagerImpl.swapFilesInRecordedActions(job.getLogger(), input, output, actions, job, null); - return; + alreadyMoved = true; } else { - output = new File(outputDir, FileUtil.getBaseName(input.getName()) + ".orig.gz"); - job.getLogger().debug("\tA file with the expected output name already exists, so the original will be renamed: " + output.getPath()); + if (input.length() == output.length() && input.lastModified() == output.lastModified()) + { + job.getLogger().info("Output exists, but has the same size/modified timestamp. Deleting original"); + input.delete(); + alreadyMoved = true; + } + else if (input.exists() && input.length() > output.length() && input.lastModified() == output.lastModified()) + { + job.getLogger().info("Output exists with same timestamp, but with smaller file size. This probably indicates a truncated/failed copy. Deleting this file."); + output.delete(); + } + else + { + throw new PipelineJobException("A file with the expected output name already exists: " + output.getPath()); + } } } - FileUtils.moveFile(input, output); + if (!alreadyMoved) + { + FileUtils.moveFile(input, output); + } if (!output.exists()) { throw new PipelineJobException("Unable to move file: " + input.getPath()); @@ -488,7 +509,7 @@ private static void moveInputToAnalysisDir(File input, SequenceJob job, Collecti TaskFileManagerImpl.swapFilesInRecordedActions(job.getLogger(), input, output, actions, job, null); } - catch (IOException e) + catch (Exception e) { throw new PipelineJobException(e); } diff --git a/SequenceAnalysis/src/org/labkey/sequenceanalysis/pipeline/SequenceOutputHandlerInitTask.java b/SequenceAnalysis/src/org/labkey/sequenceanalysis/pipeline/SequenceOutputHandlerInitTask.java index ad8a6011a..905b05169 100644 --- a/SequenceAnalysis/src/org/labkey/sequenceanalysis/pipeline/SequenceOutputHandlerInitTask.java +++ b/SequenceAnalysis/src/org/labkey/sequenceanalysis/pipeline/SequenceOutputHandlerInitTask.java @@ -35,7 +35,7 @@ public static class Factory extends AbstractTaskFactory 1) + if (readdataToSra.get(accession).size() == 1) + { + SimpleFilter filter = new SimpleFilter(FieldKey.fromString("readset"), rs.getRowId()); + filter.addCondition(FieldKey.fromString("category"), "Readset"); + filter.addCondition(FieldKey.fromString("container"), rs.getContainer()); + filter.addCondition(FieldKey.fromString("dataId"), toMerge.get(0).getFileId1()); + boolean hasMetrics = new TableSelector(SequenceAnalysisSchema.getTable(SequenceAnalysisSchema.TABLE_QUALITY_METRICS), PageFlowUtil.set("RowId"), filter, null).exists(); + if (!hasMetrics) + { + job.getLogger().debug("No existing metrics found for: " + accession); + updatedAccessions.add(accession); + } + } + else { job.getLogger().debug("Consolidating multiple readdata for: " + accession); @@ -299,6 +348,12 @@ public void complete(PipelineJob job, List readsets, List readsets, List toAdd = new ArrayList<>(); toAdd.add(rd.getFileId1()); + if (rd.getFileId2() != null) { toAdd.add(rd.getFileId2()); @@ -384,12 +440,18 @@ public void processFilesRemote(List readsets, JobContext ctx) throws Un File expectedFile1 = ctx.getSequenceSupport().getCachedData(rd.getFileId1()); File expectedFile2 = rd.getFileId2() == null ? null : ctx.getSequenceSupport().getCachedData(rd.getFileId2()); + if (!expectedFile1.getParentFile().exists()) + { + ctx.getLogger().info("Creating folder: " + expectedFile1.getParentFile().getPath()); + expectedFile1.getParentFile().mkdirs(); + } + FastqDumpWrapper wrapper = new FastqDumpWrapper(ctx.getLogger()); Pair files = wrapper.downloadSra(accession, ctx.getOutputDir(), rd.isPairedEnd(), false); long lines1 = SequenceUtil.getLineCount(files.first) / 4; ctx.getJob().getLogger().debug("Reads in " + files.first.getName() + ": " + lines1); - if (lines1 != accessionToReads.get(accession)) + if (accessionToReads.containsKey(accession) && accessionToReads.get(accession) > 0 && lines1 != accessionToReads.get(accession)) { throw new PipelineJobException("Reads found in file, " + lines1 + ", does not match expected: " + accessionToReads.get(accession) + " for file: " + files.first.getPath()); } @@ -398,7 +460,7 @@ public void processFilesRemote(List readsets, JobContext ctx) throws Un { long lines2 = SequenceUtil.getLineCount(files.second) / 4; ctx.getJob().getLogger().debug("Reads in " + files.second.getName() + ": " + lines2); - if (lines2 != accessionToReads.get(accession)) + if (accessionToReads.containsKey(accession) && accessionToReads.get(accession) > 0 && lines2 != accessionToReads.get(accession)) { throw new PipelineJobException("Reads found in file, " + lines2 + ", does not match expected: " + accessionToReads.get(accession) + " for file: " + files.second.getPath()); } diff --git a/SequenceAnalysis/src/org/labkey/sequenceanalysis/run/analysis/SawfishAnalysis.java b/SequenceAnalysis/src/org/labkey/sequenceanalysis/run/analysis/SawfishAnalysis.java index 8039ff338..2bb9cf5dc 100644 --- a/SequenceAnalysis/src/org/labkey/sequenceanalysis/run/analysis/SawfishAnalysis.java +++ b/SequenceAnalysis/src/org/labkey/sequenceanalysis/run/analysis/SawfishAnalysis.java @@ -33,7 +33,7 @@ public static class Provider extends AbstractAnalysisStepProvider args = new ArrayList<>(); args.add(getExe().getPath()); args.add("discover"); args.add("--bam"); - args.add(inputBam.getPath()); + args.add(inputFile.getPath()); // NOTE: sawfish stores the absolute path of the FASTA in the output JSON, so dont rely on working copies: args.add("--ref"); @@ -102,4 +124,41 @@ private File getExe() { return SequencePipelineService.get().getExeForPackage("SAWFISHPATH", "sawfish"); } + + private static class CramToBam extends SamtoolsRunner + { + public CramToBam(Logger log) + { + super(log); + } + + public void convert(File inputCram, File outputBam, File fasta, @Nullable Integer threads) throws PipelineJobException + { + getLogger().info("Converting CRAM to BAM"); + + execute(getParams(inputCram, outputBam, fasta, threads)); + } + + private List getParams(File inputCram, File outputBam, File fasta, @Nullable Integer threads) + { + List params = new ArrayList<>(); + params.add(getSamtoolsPath().getPath()); + params.add("view"); + params.add("-b"); + params.add("-T"); + params.add(fasta.getPath()); + params.add("-o"); + params.add(outputBam.getPath()); + + if (threads != null) + { + params.add("-@"); + params.add(String.valueOf(threads)); + } + + params.add(inputCram.getPath()); + + return params; + } + } } \ No newline at end of file diff --git a/SequenceAnalysis/src/org/labkey/sequenceanalysis/run/analysis/SawfishJointCallingHandler.java b/SequenceAnalysis/src/org/labkey/sequenceanalysis/run/analysis/SawfishJointCallingHandler.java index 9beae27c6..270c1488f 100644 --- a/SequenceAnalysis/src/org/labkey/sequenceanalysis/run/analysis/SawfishJointCallingHandler.java +++ b/SequenceAnalysis/src/org/labkey/sequenceanalysis/run/analysis/SawfishJointCallingHandler.java @@ -1,7 +1,12 @@ package org.labkey.sequenceanalysis.run.analysis; +import au.com.bytecode.opencsv.CSVWriter; +import htsjdk.samtools.util.IOUtil; import org.apache.commons.io.FileUtils; import org.json.JSONObject; +import org.labkey.api.exp.api.ExpData; +import org.labkey.api.exp.api.ExpRun; +import org.labkey.api.exp.api.ExperimentService; import org.labkey.api.module.ModuleLoader; import org.labkey.api.pipeline.PipelineJob; import org.labkey.api.pipeline.PipelineJobException; @@ -9,6 +14,7 @@ import org.labkey.api.sequenceanalysis.SequenceAnalysisService; import org.labkey.api.sequenceanalysis.SequenceOutputFile; import org.labkey.api.sequenceanalysis.pipeline.AbstractParameterizedOutputHandler; +import org.labkey.api.sequenceanalysis.pipeline.PipelineContext; import org.labkey.api.sequenceanalysis.pipeline.ReferenceGenome; import org.labkey.api.sequenceanalysis.pipeline.SequenceAnalysisJobSupport; import org.labkey.api.sequenceanalysis.pipeline.SequenceOutputHandler; @@ -66,6 +72,38 @@ public SequenceOutputProcessor getProcessor() public static class Processor implements SequenceOutputProcessor { + @Override + public void init(JobContext ctx, List inputFiles, List actions, List outputsToCreate) throws UnsupportedOperationException, PipelineJobException + { + try (CSVWriter csv = new CSVWriter(IOUtil.openFileForBufferedUtf8Writing(getSampleCsvFile(ctx)))) + { + for (SequenceOutputFile so : inputFiles) + { + if (so.getRunId() == null) + { + throw new PipelineJobException("Unable to find ExperimentRun for: " + so.getRowid()); + } + + ExpRun run = ExperimentService.get().getExpRun(so.getRunId()); + List inputs = run.getInputDatas("Input BAM File", null); + if (inputs.isEmpty()) + { + throw new PipelineJobException("Unable to find input BAMs for: " + so.getRowid()); + } + else if (inputs.size() > 1) + { + throw new PipelineJobException("More than one input BAM found for ExperimentRun: " + so.getRunId()); + } + + csv.writeNext(new String[]{so.getFile().getParentFile().getPath(), inputs.get(0).getFile().getPath()}); + } + } + catch (IOException e) + { + throw new PipelineJobException(e); + } + } + @Override public void processFilesOnWebserver(PipelineJob job, SequenceAnalysisJobSupport support, List inputFiles, JSONObject params, File outputDir, List actions, List outputsToCreate) throws UnsupportedOperationException, PipelineJobException { @@ -89,8 +127,6 @@ public void processFilesRemote(List inputFiles, JobContext c outputBaseName = outputBaseName.replaceAll(".vcf$", ""); } - File expectedFinalOutput = new File(ctx.getOutputDir(), outputBaseName + ".vcf.gz"); - File ouputVcf = runSawfishCall(ctx, filesToProcess, genome, outputBaseName); SequenceOutputFile so = new SequenceOutputFile(); @@ -102,6 +138,11 @@ public void processFilesRemote(List inputFiles, JobContext c ctx.addSequenceOutput(so); } + private File getSampleCsvFile(PipelineContext ctx) + { + return new File(ctx.getSourceDirectory(), "sawfish.samples.csv"); + } + private File runSawfishCall(JobContext ctx, List inputs, ReferenceGenome genome, String outputBaseName) throws PipelineJobException { if (inputs.isEmpty()) @@ -126,6 +167,9 @@ private File runSawfishCall(JobContext ctx, List inputs, ReferenceGenome g args.add(sample.getParentFile().getPath()); } + args.add("--sample-csv"); + args.add(getSampleCsvFile(ctx).getPath()); + File outDir = new File(ctx.getOutputDir(), "sawfish"); args.add("--output-dir"); args.add(outDir.getPath()); diff --git a/SequenceAnalysis/src/org/labkey/sequenceanalysis/run/preprocessing/TagPcrSummaryStep.java b/SequenceAnalysis/src/org/labkey/sequenceanalysis/run/preprocessing/TagPcrSummaryStep.java index 5aa9885ec..a71ee865b 100644 --- a/SequenceAnalysis/src/org/labkey/sequenceanalysis/run/preprocessing/TagPcrSummaryStep.java +++ b/SequenceAnalysis/src/org/labkey/sequenceanalysis/run/preprocessing/TagPcrSummaryStep.java @@ -72,7 +72,7 @@ public Provider() put("checked", true); }}, true), ToolParameterDescriptor.createCommandLineParam(CommandLineParam.create("--insert-name"), "insertType", "Insert Type", "The type of insert to detect.", "ldk-simplecombo", new JSONObject(){{ - put("storeValues", "PiggyBac;Lentivirus;PREDICT"); + put("storeValues", "PiggyBac;Lentivirus;PREDICT;BxBI_attP"); put("allowBlank", false); }}, null), ToolParameterDescriptor.create(DESIGN_PRIMERS, "Design Primers", "If selected, Primer3 will be used to design primers to flank integration sites", "checkbox", new JSONObject(){{ diff --git a/SequenceAnalysis/src/org/labkey/sequenceanalysis/run/util/RnaSeQCWrapper.java b/SequenceAnalysis/src/org/labkey/sequenceanalysis/run/util/RnaSeQCWrapper.java index aeb02da02..0f21d45d6 100644 --- a/SequenceAnalysis/src/org/labkey/sequenceanalysis/run/util/RnaSeQCWrapper.java +++ b/SequenceAnalysis/src/org/labkey/sequenceanalysis/run/util/RnaSeQCWrapper.java @@ -28,6 +28,8 @@ */ public class RnaSeQCWrapper extends AbstractCommandWrapper { + final static int MAX_WARNINGS = 100; + public RnaSeQCWrapper(@Nullable Logger logger) { super(logger); @@ -122,12 +124,32 @@ public File execute(List inputBams, List sampleIds, @Nullable List } else if (!line.contains("transcript_id")) { - getLogger().info("skipping GTF line " + lineNo + " because it lacks transcript_id"); + if (filteredLines <= MAX_WARNINGS) + { + if (filteredLines == MAX_WARNINGS) + { + getLogger().info("skipping GTF line " + lineNo + " because it lacks transcript_id. No additional warnings will be printed"); + } + else + { + getLogger().info("skipping GTF line " + lineNo + " because it lacks transcript_id"); + } + } filteredLines++; } else if (!line.contains("gene_id")) { - getLogger().info("skipping GTF line " + lineNo + " because it lacks gene_id"); + if (filteredLines <= MAX_WARNINGS) + { + if (filteredLines == MAX_WARNINGS) + { + getLogger().info("skipping GTF line " + lineNo + " because it lacks gene_id. No additional warnings will be printed"); + } + else + { + getLogger().info("skipping GTF line " + lineNo + " because it lacks gene_id"); + } + } filteredLines++; } else diff --git a/SequenceAnalysis/src/org/labkey/sequenceanalysis/run/variant/SNPEffStep.java b/SequenceAnalysis/src/org/labkey/sequenceanalysis/run/variant/SNPEffStep.java index e55bf1915..16903cb05 100644 --- a/SequenceAnalysis/src/org/labkey/sequenceanalysis/run/variant/SNPEffStep.java +++ b/SequenceAnalysis/src/org/labkey/sequenceanalysis/run/variant/SNPEffStep.java @@ -75,13 +75,17 @@ public static File checkOrCreateIndex(SequenceAnalysisJobSupport support, Logger SnpEffWrapper wrapper = new SnpEffWrapper(log); File snpEffIndexDir = wrapper.getExpectedIndexDir(snpEffBaseDir, genome.getGenomeId(), geneFileId); - if (!snpEffIndexDir.exists()) + if (snpEffIndexDir.exists()) { - wrapper.buildIndex(snpEffBaseDir, genome, geneFile, geneFileId); + log.debug("previously created index found, re-using: " + snpEffIndexDir.getPath()); + return snpEffBaseDir; } - else + + File binFile = new File(snpEffIndexDir, "snpEffectPredictor.bin"); + if (!binFile.exists()) { - log.debug("previously created index found, re-using: " + snpEffIndexDir.getPath()); + log.debug("existing index not found, expected: " + binFile.getPath()); + wrapper.buildIndex(snpEffBaseDir, genome, geneFile, geneFileId); } return snpEffBaseDir; diff --git a/SequenceAnalysis/test/src/org/labkey/test/tests/external/labModules/SequenceTest.java b/SequenceAnalysis/test/src/org/labkey/test/tests/external/labModules/SequenceTest.java index e26795289..c264ef281 100644 --- a/SequenceAnalysis/test/src/org/labkey/test/tests/external/labModules/SequenceTest.java +++ b/SequenceAnalysis/test/src/org/labkey/test/tests/external/labModules/SequenceTest.java @@ -195,6 +195,44 @@ private void importReadsetMetadata() log("verifying readset count correct"); waitForText("Sequence Readsets"); waitForElement(LabModuleHelper.getNavPanelItem("Sequence Readsets:", _readsetCt.toString())); + + // Repeat, adding SRA accessions: + goToProjectHome(); + waitAndClick(Locator.linkWithText("Plan Sequence Run (Create Readsets)")); + new Window.WindowFinder(getDriver()).withTitle("Create Readsets").waitFor(); + waitAndClickAndWait(Ext4Helper.Locators.ext4Button("Submit")); + + _helper.waitForField("Sample Id", WAIT_FOR_PAGE); + _ext4Helper.clickTabContainingText("Import Spreadsheet"); + waitForText("Copy/Paste Data"); + + setFormElementJS(Locator.name("text"), getIlluminaSRANames()); + + waitAndClick(Ext4Helper.Locators.ext4Button("Upload")); + new Window.WindowFinder(getDriver()).withTitle("Success").waitFor(); + _readsetCt += 3; + assertTextPresent("Success!"); + waitAndClickAndWait(Ext4Helper.Locators.ext4Button("OK")); + + // This is scoped to this workbook: + log("verifying readset count correct"); + waitForText("Sequence Readsets"); + waitAndClickAndWait(LabModuleHelper.getNavPanelItem("Sequence Readsets:", "3")); + + DataRegionTable.DataRegion(getDriver()).withName("query").waitFor(); + + //verify CSV file creation + DataRegionTable.DataRegion(getDriver()).find().goToView("SRA Info"); + DataRegionTable dr = DataRegionTable.DataRegion(getDriver()).withName("query").waitFor(); + waitForElement(Locator.tagContainingText("a", "SRA0")); + waitForElement(Locator.tagContainingText("a", "SRA1")); + waitForElement(Locator.tagContainingText("a", "SRA2")); + + dr.checkAllOnPage(); + dr.clickHeaderButtonAndWait("Delete"); + clickButton("OK"); + + _readsetCt -= 3; } /** @@ -320,6 +358,18 @@ private String getIlluminaNames() return sb.toString(); } + private String getIlluminaSRANames() + { + StringBuilder sb = new StringBuilder("Name\tPlatform\tsraAccessions\n"); + int i = 0; + while (i < 3) + { + sb.append("IlluminaSRA" + (i + 1) + "\tILLUMINA\tSRA" + i + "\n"); + i++; + } + return sb.toString(); + } + /** * This test will kick off a pipeline import using the illumina pipeline. Verification of the result * is performed by readsetFeaturesTest() diff --git a/Studies/api-src/org/labkey/api/studies/StudiesService.java b/Studies/api-src/org/labkey/api/studies/StudiesService.java index 848fde966..700355741 100644 --- a/Studies/api-src/org/labkey/api/studies/StudiesService.java +++ b/Studies/api-src/org/labkey/api/studies/StudiesService.java @@ -2,6 +2,7 @@ import org.labkey.api.data.Container; import org.labkey.api.data.TableCustomizer; +import org.labkey.api.data.triggers.TriggerFactory; import org.labkey.api.module.Module; import org.labkey.api.resource.Resource; import org.labkey.api.security.User; @@ -37,4 +38,6 @@ static public void setInstance(StudiesService instance) abstract public List getEventProviders(Container c); abstract public TableCustomizer getStudiesTableCustomizer(); + + abstract public TriggerFactory getStudiesTriggerFactory(); } diff --git a/Studies/resources/queries/studies/studyCohorts/.qview.xml b/Studies/resources/queries/studies/studyCohorts/.qview.xml new file mode 100644 index 000000000..c2eff2380 --- /dev/null +++ b/Studies/resources/queries/studies/studyCohorts/.qview.xml @@ -0,0 +1,22 @@ + \ No newline at end of file diff --git a/Studies/resources/schemas/dbscripts/postgresql/studies-23.005-23.006.sql b/Studies/resources/schemas/dbscripts/postgresql/studies-23.005-23.006.sql new file mode 100644 index 000000000..72c5798d2 --- /dev/null +++ b/Studies/resources/schemas/dbscripts/postgresql/studies-23.005-23.006.sql @@ -0,0 +1 @@ +ALTER TABLE studies.subjectAnchorDates ADD COLUMN sourceRecord varchar(1000); diff --git a/Studies/resources/schemas/dbscripts/sqlserver/studies-23.005-23.006.sql b/Studies/resources/schemas/dbscripts/sqlserver/studies-23.005-23.006.sql new file mode 100644 index 000000000..1acef5825 --- /dev/null +++ b/Studies/resources/schemas/dbscripts/sqlserver/studies-23.005-23.006.sql @@ -0,0 +1 @@ +ALTER TABLE studies.subjectAnchorDates ADD sourceRecord varchar(1000); diff --git a/Studies/resources/schemas/studies.xml b/Studies/resources/schemas/studies.xml index 5dbd8d185..b6f4e992f 100644 --- a/Studies/resources/schemas/studies.xml +++ b/Studies/resources/schemas/studies.xml @@ -140,6 +140,7 @@ Study Name + false Label @@ -196,6 +197,7 @@ DETAILED + Cohort ID true false false @@ -207,7 +209,7 @@ studies studies rowId - label + labelOrName @@ -286,7 +288,7 @@ studies studies rowId - label + labelOrName @@ -351,7 +353,7 @@ studies studies rowId - label + labelOrName @@ -360,7 +362,7 @@ studies studyCohorts rowId - label + labelOrName @@ -524,6 +526,10 @@ label + + Source Record + true + Data Source diff --git a/Studies/resources/views/studiesOverview.html b/Studies/resources/views/studiesOverview.html index 2d1d6a280..5fd144f1e 100644 --- a/Studies/resources/views/studiesOverview.html +++ b/Studies/resources/views/studiesOverview.html @@ -43,15 +43,15 @@ }); dataByCategory[row['CategoryId/Label']] = dataByCategory[row['CategoryId/Label']] || [] - dataByCategory[row['CategoryId/Label']].push(`
  • ${row.Label}
  • `) + dataByCategory[row['CategoryId/Label']].push($(`
  • ${row.Label}
  • `)) }) Object.keys(dataByCategory).sort().forEach(category => { div.append('

    ' + category + '

    ').append('
      ') + const ul = div.children('ul')[0] dataByCategory[category].forEach(item => { - div.append(item) + ul.append(item[0]) }) - div.append('
    ') }) $('#' + webpart.wrapperDivId).append(div) diff --git a/Studies/src/org/labkey/studies/StudiesManager.java b/Studies/src/org/labkey/studies/StudiesManager.java index 4c0d087bb..eedaedba3 100644 --- a/Studies/src/org/labkey/studies/StudiesManager.java +++ b/Studies/src/org/labkey/studies/StudiesManager.java @@ -226,7 +226,7 @@ private Map studyToMap(StudyDefinition s) Map m = new HashMap<>(); if (s.getRowId() != null) m.put("rowId", s.getRowId()); - m.put("name", s.getStudyName()); + m.put("studyName", s.getStudyName()); m.put("label", s.getLabel()); m.put("category", s.getCategory()); m.put("description", s.getDescription()); diff --git a/Studies/src/org/labkey/studies/StudiesModule.java b/Studies/src/org/labkey/studies/StudiesModule.java index c6c14306f..fd7553fc4 100644 --- a/Studies/src/org/labkey/studies/StudiesModule.java +++ b/Studies/src/org/labkey/studies/StudiesModule.java @@ -34,7 +34,7 @@ public String getName() @Override public @Nullable Double getSchemaVersion() { - return 23.005; + return 23.006; } @Override diff --git a/Studies/src/org/labkey/studies/StudiesServiceImpl.java b/Studies/src/org/labkey/studies/StudiesServiceImpl.java index 4c3910b5b..01ffae684 100644 --- a/Studies/src/org/labkey/studies/StudiesServiceImpl.java +++ b/Studies/src/org/labkey/studies/StudiesServiceImpl.java @@ -5,6 +5,7 @@ import org.labkey.api.data.Container; import org.labkey.api.data.TableCustomizer; import org.labkey.api.data.TableInfo; +import org.labkey.api.data.triggers.TriggerFactory; import org.labkey.api.module.Module; import org.labkey.api.pipeline.PipeRoot; import org.labkey.api.pipeline.PipelineService; @@ -25,6 +26,7 @@ import org.labkey.api.util.Path; import org.labkey.api.util.logging.LogHelper; import org.labkey.studies.query.StudiesTableCustomizer; +import org.labkey.studies.query.StudiesTriggerFactory; import java.io.FileNotFoundException; import java.io.IOException; @@ -51,6 +53,12 @@ private StudiesServiceImpl() } + @Override + public TriggerFactory getStudiesTriggerFactory() + { + return new StudiesTriggerFactory(); + } + @Override public void importFolderDefinition(Container container, User user, Module m, Path sourceFolderDirPath) throws IOException { diff --git a/Studies/src/org/labkey/studies/query/StudiesTriggerFactory.java b/Studies/src/org/labkey/studies/query/StudiesTriggerFactory.java index b8805dd35..bcec14717 100644 --- a/Studies/src/org/labkey/studies/query/StudiesTriggerFactory.java +++ b/Studies/src/org/labkey/studies/query/StudiesTriggerFactory.java @@ -38,49 +38,76 @@ public void beforeInsert(TableInfo table, Container c, User user, @Nullable Map< @Override public void beforeInsert(TableInfo table, Container c, User user, @Nullable Map newRow, ValidationException errors, Map extraContext, @Nullable Map existingRecord) throws ValidationException { - possiblyResolveStudy(newRow, c); + possiblyResolveStudy(table, newRow, existingRecord, c); } @Override public void beforeUpdate(TableInfo table, Container c, User user, @Nullable Map newRow, @Nullable Map oldRow, ValidationException errors, Map extraContext) throws ValidationException { - possiblyResolveStudy(newRow, c); + possiblyResolveStudy(table, newRow, oldRow, c); } /** * This allows incoming data to specify the study using the string name, which is resolved into the rowId */ - private void possiblyResolveStudy(@Nullable Map row, Container c) + private void possiblyResolveStudy(TableInfo table, @Nullable Map row, @Nullable Map oldRow, Container c) { if (row == null) { return; } - possiblyResolveStudy(row, c, "studyId"); - if (row.get("studyId") == null & row.get("studyName") != null) + if (table.getColumn("studyId") != null) { - possiblyResolveStudy(row, c, "studyName"); + possiblyResolveStudy(row, c, "studyId"); + if (row.get("studyId") == null & row.get("studyName") != null) + { + possiblyResolveStudy(row, c, "studyName"); + } + } + + if (table.getColumn("cohortId") != null) + { + possiblyResolveCohort(row, c, "cohortId"); + if (row.get("cohortId") == null & row.get("cohortName") != null) + { + possiblyResolveCohort(row, c, "cohortName"); + } } } private void possiblyResolveStudy(@Nullable Map row, Container c, String sourceProperty) + { + possiblyResolveStudyOrCohort(StudiesSchema.TABLE_STUDIES, row, c, sourceProperty, "studyId", "studyName"); + } + + private void possiblyResolveCohort(@Nullable Map row, Container c, String sourceProperty) + { + possiblyResolveStudyOrCohort(StudiesSchema.TABLE_COHORTS, row, c, sourceProperty, "cohortId", "cohortName"); + } + + private void possiblyResolveStudyOrCohort(String tableToQuery, @Nullable Map row, Container c, String sourceProperty, String targetFieldName, String filterFieldName) { if (row == null) { return; } - if (row.get(sourceProperty) != null & row.get(sourceProperty) instanceof String) + if (row.get(sourceProperty) instanceof Integer) + { + return; + } + + if (row.get(sourceProperty) != null & row.get(sourceProperty) instanceof String & !String.valueOf(row.get(sourceProperty)).isEmpty()) { if (!NumberUtils.isCreatable(row.get(sourceProperty).toString())) { Container target = c.isWorkbookOrTab() ? c.getParent() : c; - SimpleFilter filter = new SimpleFilter(FieldKey.fromString("container"), target.getEntityId()).addCondition(FieldKey.fromString("studyName"), row.get(sourceProperty)); - List rowIds = new TableSelector(StudiesSchema.getInstance().getSchema().getTable(StudiesSchema.TABLE_STUDIES), PageFlowUtil.set("rowId"), filter, null).getArrayList(Integer.class); + SimpleFilter filter = new SimpleFilter(FieldKey.fromString("container"), target.getEntityId()).addCondition(FieldKey.fromString(filterFieldName), row.get(sourceProperty)); + List rowIds = new TableSelector(StudiesSchema.getInstance().getSchema().getTable(tableToQuery), PageFlowUtil.set("rowId"), filter, null).getArrayList(Integer.class); if (rowIds.size() == 1) { - row.put("studyId", rowIds.get(0)); + row.put(targetFieldName, rowIds.get(0)); } } } diff --git a/Studies/src/org/labkey/studies/query/StudiesUserSchema.java b/Studies/src/org/labkey/studies/query/StudiesUserSchema.java index 928d882c6..00401b524 100644 --- a/Studies/src/org/labkey/studies/query/StudiesUserSchema.java +++ b/Studies/src/org/labkey/studies/query/StudiesUserSchema.java @@ -138,11 +138,11 @@ else if (TABLE_LOOKUPS.equalsIgnoreCase(name)) } else if (TABLE_STUDIES.equalsIgnoreCase(name)) { - return createStudiesTable(name, cf, false); + return createStudiesTable(name, cf); } else if (TABLE_COHORTS.equalsIgnoreCase(name)) { - return createStudyDesignTable(name, cf, true); + return createCohortsTable(name, cf); } else if (TABLE_ANCHOR_EVENTS.equalsIgnoreCase(name)) { @@ -169,18 +169,42 @@ else if (TABLE_EVENT_TYPES.equalsIgnoreCase(name)) return super.createTable(name, cf); } - private TableInfo createStudiesTable(String name, ContainerFilter cf, boolean addTriggers) + private TableInfo createCohortsTable(String name, ContainerFilter cf) + { + CustomPermissionsTable ret = createStudyDesignTable(name, cf, true); + + SQLFragment lastTerm = ret.getSqlDialect().concatenate(new SQLFragment("'Cohort-'"), new SQLFragment("CAST(" + ExprColumn.STR_TABLE_ALIAS + ".rowId AS VARCHAR)")); + SQLFragment sql2 = new SQLFragment("coalesce(" + ExprColumn.STR_TABLE_ALIAS + ".label, " + ExprColumn.STR_TABLE_ALIAS + ".cohortName, ").append(lastTerm).append(new SQLFragment(")")); + ExprColumn col2 = new ExprColumn(ret, "labelOrName", sql2, JdbcType.VARCHAR, ret.getColumn("cohortName"), ret.getColumn("label")); + col2.setLabel("Cohort Name"); + col2.setHidden(true); + col2.setDescription("This column lists the cohort label, and the name if label is blank"); + + ret.addColumn(col2); + + return ret; + } + + private TableInfo createStudiesTable(String name, ContainerFilter cf) { - CustomPermissionsTable ret = createStudyDesignTable(name, cf, addTriggers); + CustomPermissionsTable ret = createStudyDesignTable(name, cf, false); final String chr = ret.getSqlDialect().isPostgreSQL() ? "chr" : "char"; - SQLFragment sql1 = new SQLFragment("(SELECT ").append(ret.getSqlDialect().getGroupConcat(new SQLFragment("c.label"), true, true, new SQLFragment(chr + "(10)"))).append(" as expr FROM " + StudiesSchema.NAME + "." + TABLE_COHORTS + " c WHERE c.studyId = " + ExprColumn.STR_TABLE_ALIAS + ".rowId)"); + SQLFragment sql1 = new SQLFragment("(SELECT ").append(ret.getSqlDialect().getGroupConcat(new SQLFragment("coalesce(c.label, c.cohortName)"), true, true, new SQLFragment(chr + "(10)"))).append(" as expr FROM " + StudiesSchema.NAME + "." + TABLE_COHORTS + " c WHERE c.studyId = " + ExprColumn.STR_TABLE_ALIAS + ".rowId)"); ExprColumn col1 = new ExprColumn(ret, "cohorts", sql1, JdbcType.VARCHAR, ret.getColumn("rowid")); col1.setLabel("Cohort(s)"); col1.setDescription("This column lists the cohort labels for this study"); ret.addColumn(col1); + SQLFragment sql2 = new SQLFragment("coalesce(" + ExprColumn.STR_TABLE_ALIAS + ".label, " + ExprColumn.STR_TABLE_ALIAS + ".studyName)"); + ExprColumn col2 = new ExprColumn(ret, "labelOrName", sql2, JdbcType.VARCHAR, ret.getColumn("studyName"), ret.getColumn("label")); + col2.setLabel("Study Name"); + col2.setHidden(true); + col2.setDescription("This column lists the study label, and the name if label is blank"); + + ret.addColumn(col2); + return ret; } diff --git a/discvrcore/src/org/labkey/discvrcore/DiscvrCoreController.java b/discvrcore/src/org/labkey/discvrcore/DiscvrCoreController.java index cd83a749c..f948c987c 100644 --- a/discvrcore/src/org/labkey/discvrcore/DiscvrCoreController.java +++ b/discvrcore/src/org/labkey/discvrcore/DiscvrCoreController.java @@ -58,7 +58,6 @@ import java.util.Map; import java.util.TreeMap; -import static javax.swing.Spring.width; import static org.labkey.api.util.DOM.Attribute.valign; import static org.labkey.api.util.DOM.at; import static org.labkey.api.util.DOM.cl; diff --git a/jbrowse/package-lock.json b/jbrowse/package-lock.json index b7ba7390c..47717a897 100644 --- a/jbrowse/package-lock.json +++ b/jbrowse/package-lock.json @@ -53,22 +53,9 @@ } }, "node_modules/@adobe/css-tools": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.2.tgz", - "integrity": "sha512-baYZExFpsdkBNuvGKTKWCwKH57HRZLVtycZS05WTQNVOiXVSeAki3nU35zlRbToeMW8aHlJfyS+1C4BOv27q0A==" - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", + "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==" }, "node_modules/@babel/code-frame": { "version": "7.27.1", @@ -84,30 +71,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", - "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "dependencies": { - "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", + "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.3", - "@babel/parser": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.3", - "@babel/types": "^7.28.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -129,12 +116,12 @@ "dev": true }, "node_modules/@babel/generator": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", - "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", "dependencies": { - "@babel/parser": "^7.28.3", - "@babel/types": "^7.28.2", + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -143,17 +130,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@babel/helper-annotate-as-pure": { "version": "7.27.3", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", @@ -183,17 +159,17 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", - "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz", + "integrity": "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.28.3", + "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "engines": { @@ -204,13 +180,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", - "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", + "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "regexpu-core": "^6.2.0", + "@babel/helper-annotate-as-pure": "^7.27.3", + "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "engines": { @@ -245,13 +221,13 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", - "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", "dev": true, "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -363,9 +339,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "engines": { "node": ">=6.9.0" } @@ -394,24 +370,24 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", - "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dev": true, "dependencies": { "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2" + "@babel/types": "^7.28.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", - "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "dependencies": { - "@babel/types": "^7.28.2" + "@babel/types": "^7.28.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -421,13 +397,13 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", - "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", + "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/traverse": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -652,9 +628,9 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", - "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.5.tgz", + "integrity": "sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -699,9 +675,9 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.3.tgz", - "integrity": "sha512-DoEWC5SuxuARF2KdKmGUq3ghfPMO6ZzR12Dnp5gubwbeWJo4dbNWXJPVlwvh4Zlq6Z7YVvL8VFxeSOJgjsx4Sg==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz", + "integrity": "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", @@ -709,7 +685,7 @@ "@babel/helper-globals": "^7.28.0", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/traverse": "^7.28.4" }, "engines": { "node": ">=6.9.0" @@ -735,13 +711,13 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", - "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", + "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.0" + "@babel/traverse": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -829,9 +805,9 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", - "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.5.tgz", + "integrity": "sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -922,9 +898,9 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", - "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.5.tgz", + "integrity": "sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -984,15 +960,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", - "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.28.5.tgz", + "integrity": "sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-module-transforms": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1079,16 +1055,16 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.0.tgz", - "integrity": "sha512-9VNGikXxzu5eCiQjdE4IZn8sb9q7Xsk5EXLDBKUYg1e/Tve8/05+KJEtcxGxAgCY5t/BpKQM+JEL/yT4tvgiUA==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.4.tgz", + "integrity": "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==", "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.0", "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/traverse": "^7.28.0" + "@babel/traverse": "^7.28.4" }, "engines": { "node": ">=6.9.0" @@ -1129,9 +1105,9 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", - "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.5.tgz", + "integrity": "sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -1273,9 +1249,9 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.3.tgz", - "integrity": "sha512-K3/M/a4+ESb5LEldjQb+XSrpY0nF+ZBFlTCbSnKaYAMfD8v33O6PMs4uYnOk19HlcsI8WMu3McdFPTiQHF/1/A==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.4.tgz", + "integrity": "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1395,13 +1371,13 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", - "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.5.tgz", + "integrity": "sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1" @@ -1477,16 +1453,16 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.3.tgz", - "integrity": "sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.5.tgz", + "integrity": "sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.28.0", + "@babel/compat-data": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", @@ -1499,42 +1475,42 @@ "@babel/plugin-transform-async-generator-functions": "^7.28.0", "@babel/plugin-transform-async-to-generator": "^7.27.1", "@babel/plugin-transform-block-scoped-functions": "^7.27.1", - "@babel/plugin-transform-block-scoping": "^7.28.0", + "@babel/plugin-transform-block-scoping": "^7.28.5", "@babel/plugin-transform-class-properties": "^7.27.1", "@babel/plugin-transform-class-static-block": "^7.28.3", - "@babel/plugin-transform-classes": "^7.28.3", + "@babel/plugin-transform-classes": "^7.28.4", "@babel/plugin-transform-computed-properties": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-destructuring": "^7.28.5", "@babel/plugin-transform-dotall-regex": "^7.27.1", "@babel/plugin-transform-duplicate-keys": "^7.27.1", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-dynamic-import": "^7.27.1", "@babel/plugin-transform-explicit-resource-management": "^7.28.0", - "@babel/plugin-transform-exponentiation-operator": "^7.27.1", + "@babel/plugin-transform-exponentiation-operator": "^7.28.5", "@babel/plugin-transform-export-namespace-from": "^7.27.1", "@babel/plugin-transform-for-of": "^7.27.1", "@babel/plugin-transform-function-name": "^7.27.1", "@babel/plugin-transform-json-strings": "^7.27.1", "@babel/plugin-transform-literals": "^7.27.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.28.5", "@babel/plugin-transform-member-expression-literals": "^7.27.1", "@babel/plugin-transform-modules-amd": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-modules-systemjs": "^7.27.1", + "@babel/plugin-transform-modules-systemjs": "^7.28.5", "@babel/plugin-transform-modules-umd": "^7.27.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-new-target": "^7.27.1", "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", "@babel/plugin-transform-numeric-separator": "^7.27.1", - "@babel/plugin-transform-object-rest-spread": "^7.28.0", + "@babel/plugin-transform-object-rest-spread": "^7.28.4", "@babel/plugin-transform-object-super": "^7.27.1", "@babel/plugin-transform-optional-catch-binding": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.28.5", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/plugin-transform-private-methods": "^7.27.1", "@babel/plugin-transform-private-property-in-object": "^7.27.1", "@babel/plugin-transform-property-literals": "^7.27.1", - "@babel/plugin-transform-regenerator": "^7.28.3", + "@babel/plugin-transform-regenerator": "^7.28.4", "@babel/plugin-transform-regexp-modifiers": "^7.27.1", "@babel/plugin-transform-reserved-words": "^7.27.1", "@babel/plugin-transform-shorthand-properties": "^7.27.1", @@ -1575,14 +1551,14 @@ } }, "node_modules/@babel/preset-react": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.27.1.tgz", - "integrity": "sha512-oJHWh2gLhU9dW9HHr42q0cI0/iHHXTLGe39qvpAZZzagHy0MzYLCnCVV0symeRvzmjHyVU7mw2K06E6u/JwbhA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.28.5.tgz", + "integrity": "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-transform-react-display-name": "^7.27.1", + "@babel/plugin-transform-react-display-name": "^7.28.0", "@babel/plugin-transform-react-jsx": "^7.27.1", "@babel/plugin-transform-react-jsx-development": "^7.27.1", "@babel/plugin-transform-react-pure-annotations": "^7.27.1" @@ -1595,16 +1571,16 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", - "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.28.5.tgz", + "integrity": "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-typescript": "^7.27.1" + "@babel/plugin-transform-typescript": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1614,12 +1590,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", - "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", "engines": { "node": ">=6.9.0" } @@ -1638,16 +1611,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", - "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", + "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.3", + "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2", + "@babel/types": "^7.28.5", "debug": "^4.3.1" }, "engines": { @@ -1655,12 +1628,12 @@ } }, "node_modules/@babel/types": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", - "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "dependencies": { "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -2006,48 +1979,54 @@ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" }, + "node_modules/@epic-web/invariant": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", + "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==", + "dev": true + }, "node_modules/@flatten-js/interval-tree": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@flatten-js/interval-tree/-/interval-tree-1.1.3.tgz", "integrity": "sha512-xhFWUBoHJFF77cJO1D6REjdgJEMRf2Y2Z+eKEPav8evGKcLSnj1ud5pLXQSbGuxF3VSvT1rWhMfVpXEKJLTL+A==" }, "node_modules/@floating-ui/core": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.7.tgz", - "integrity": "sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==", + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", "dependencies": { - "@floating-ui/utils": "^0.2.7" + "@floating-ui/utils": "^0.2.10" } }, "node_modules/@floating-ui/dom": { - "version": "1.6.10", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.10.tgz", - "integrity": "sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", "dependencies": { - "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.7" + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" } }, "node_modules/@floating-ui/react": { - "version": "0.26.23", - "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.23.tgz", - "integrity": "sha512-9u3i62fV0CFF3nIegiWiRDwOs7OW/KhSUJDNx2MkQM3LbE5zQOY01sL3nelcVBXvX7Ovvo3A49I8ql+20Wg/Hw==", + "version": "0.27.16", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.16.tgz", + "integrity": "sha512-9O8N4SeG2z++TSM8QA/KTeKFBVCNEz/AGS7gWPJf6KFRzmRWixFRnCnkPHRDwSVZW6QPDO6uT0P2SpWNKCc9/g==", "dependencies": { - "@floating-ui/react-dom": "^2.1.1", - "@floating-ui/utils": "^0.2.7", + "@floating-ui/react-dom": "^2.1.6", + "@floating-ui/utils": "^0.2.10", "tabbable": "^6.0.0" }, "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "react": ">=17.0.0", + "react-dom": ">=17.0.0" } }, "node_modules/@floating-ui/react-dom": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", - "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", + "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", "dependencies": { - "@floating-ui/dom": "^1.0.0" + "@floating-ui/dom": "^1.7.4" }, "peerDependencies": { "react": ">=16.8.0", @@ -2055,9 +2034,9 @@ } }, "node_modules/@floating-ui/utils": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", - "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==" + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==" }, "node_modules/@gmod/abortable-promise-cache": { "version": "2.0.1", @@ -2242,6 +2221,27 @@ "react": "*" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -2391,20 +2391,6 @@ "react-dom": ">=18.0.0" } }, - "node_modules/@jbrowse/core/node_modules/@floating-ui/react": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.5.tgz", - "integrity": "sha512-BX3jKxo39Ba05pflcQmqPPwc0qdNsdNi/eweAFtoIdrJWNen2sVEWMEac3i6jU55Qfx+lOcdMNKYn2CtWmlnOQ==", - "dependencies": { - "@floating-ui/react-dom": "^2.1.2", - "@floating-ui/utils": "^0.2.9", - "tabbable": "^6.0.0" - }, - "peerDependencies": { - "react": ">=17.0.0", - "react-dom": ">=17.0.0" - } - }, "node_modules/@jbrowse/core/node_modules/@gmod/bgzf-filehandle": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@gmod/bgzf-filehandle/-/bgzf-filehandle-2.0.4.tgz", @@ -3035,6 +3021,16 @@ "@jridgewell/trace-mapping": "^0.3.24" } }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "license": "MIT", @@ -3176,55 +3172,55 @@ } }, "node_modules/@labkey/api": { - "version": "1.42.1", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.42.1.tgz", - "integrity": "sha512-rT+Q/ZM6bE6bU8HDj/7f3DIFuq538e+LZAvBw8P3qJjuAnyO+O+ItZz/YukAKCXXiN2GdedOXDJbt1Ms0bgLsg==" + "version": "1.44.0", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.44.0.tgz", + "integrity": "sha512-qfHSWENWN2E1KTRACDj/Qq4Rq/tq8KIr5l6XOnMGLEoepUe8DneAnfcIVD5239oxwFDxMLEFCH83EKeat0C/9g==" }, "node_modules/@labkey/build": { - "version": "8.6.0", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/build/-/@labkey/build-8.6.0.tgz", - "integrity": "sha512-rAb/cQomhlL4GamJkI8/V2lHUGwUNRh7n1uXFMeHfUmuiXP/adp9uqhCC+yTAcaAK0kdq6Z3vU/n7VvCEdjtMQ==", + "version": "8.7.0", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/build/-/@labkey/build-8.7.0.tgz", + "integrity": "sha512-qeUADx66NX+k6fxSp+Y1K604BGdUqAEoMB2qxg8PxaWnj+kTgwHc9ciCOKLJ/n+ocR4tdpX+Twj3bwXf7OkiWQ==", "dev": true, "dependencies": { - "@babel/core": "~7.28.0", + "@babel/core": "~7.28.5", "@babel/plugin-transform-class-properties": "~7.27.1", - "@babel/plugin-transform-object-rest-spread": "~7.28.0", - "@babel/preset-env": "~7.28.0", - "@babel/preset-react": "~7.27.1", - "@babel/preset-typescript": "~7.27.1", + "@babel/plugin-transform-object-rest-spread": "~7.28.4", + "@babel/preset-env": "~7.28.5", + "@babel/preset-react": "~7.28.5", + "@babel/preset-typescript": "~7.28.5", "@pmmmwh/react-refresh-webpack-plugin": "~0.6.1", "ajv": "~8.17.1", "babel-loader": "~10.0.0", "bootstrap-sass": "~3.4.3", - "copy-webpack-plugin": "~13.0.0", - "cross-env": "~7.0.3", + "copy-webpack-plugin": "~13.0.1", + "cross-env": "~10.1.0", "css-loader": "~7.1.2", "fork-ts-checker-webpack-plugin": "~9.1.0", - "html-webpack-plugin": "~5.6.3", - "mini-css-extract-plugin": "~2.9.2", - "react-refresh": "~0.17.0", + "html-webpack-plugin": "~5.6.4", + "mini-css-extract-plugin": "~2.9.4", + "react-refresh": "~0.18.0", "resolve-url-loader": "~5.0.0", - "rimraf": "~6.0.1", + "rimraf": "~6.1.0", "sass": "~1.79.6", - "sass-loader": "~16.0.5", + "sass-loader": "~16.0.6", "source-map-loader": "~5.0.0", "style-loader": "~4.0.0", - "typescript": "~5.8.3", - "webpack": "~5.100.0", + "typescript": "~5.9.3", + "webpack": "~5.102.1", "webpack-bundle-analyzer": "~4.10.2", "webpack-cli": "~6.0.1", "webpack-dev-server": "~5.2.2" } }, "node_modules/@labkey/components": { - "version": "6.58.5", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.58.5.tgz", - "integrity": "sha512-N+WHs9QDAkJ5p2NIwCpWSp3O0Q5bNeBXqoizaZRZh9uEM9iSxg0I4GD/dd1jCk1PFGHs05mPLZ13yrPeixDiHQ==", + "version": "6.72.0", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.72.0.tgz", + "integrity": "sha512-ouYWpvQBF0GZ/j/ErGRcAOHTAwkGP/fSA4hDKaql59U1kMGI7gZdoHZNb5aX0YWX+FLor8FDqLXz9WWmkykEWw==", "dependencies": { "@hello-pangea/dnd": "18.0.1", - "@labkey/api": "1.42.1", - "@testing-library/dom": "~10.4.0", - "@testing-library/jest-dom": "~6.6.3", + "@labkey/api": "1.44.0", + "@testing-library/dom": "~10.4.1", + "@testing-library/jest-dom": "~6.9.1", "@testing-library/react": "~16.3.0", "@testing-library/user-event": "~14.6.1", "bootstrap": "~3.4.1", @@ -3232,19 +3228,19 @@ "date-fns": "~3.6.0", "date-fns-tz": "~3.2.0", "font-awesome": "~4.7.0", - "immer": "~10.1.1", + "immer": "~10.1.3", "immutable": "~3.8.2", "normalizr": "~3.6.2", "numeral": "~2.0.6", "react": "~18.3.1", "react-color": "~2.19.3", - "react-datepicker": "~7.5.0", + "react-datepicker": "~7.6.0", "react-dom": "~18.3.1", "react-router-dom": "~6.30.1", - "react-select": "~5.10.1", + "react-select": "~5.10.2", "react-treebeard": "~3.2.4", - "vis-data": "~7.1.10", - "vis-network": "~9.1.13" + "vis-data": "~8.0.3", + "vis-network": "~10.0.2" } }, "node_modules/@labkey/components/node_modules/immutable": { @@ -3852,18 +3848,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/@parcel/watcher/node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "dev": true, - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -3952,49 +3936,33 @@ } }, "node_modules/@testing-library/dom": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", - "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", - "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", + "picocolors": "1.1.1", "pretty-format": "^27.0.2" }, "engines": { "node": ">=18" } }, - "node_modules/@testing-library/dom/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/@testing-library/jest-dom": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz", - "integrity": "sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", - "chalk": "^3.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", - "lodash": "^4.17.21", + "picocolors": "^1.1.1", "redent": "^3.0.0" }, "engines": { @@ -4253,6 +4221,11 @@ "@types/node": "*" } }, + "node_modules/@types/pako": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz", + "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==" + }, "node_modules/@types/parse-json": { "version": "4.0.2", "license": "MIT" @@ -4693,6 +4666,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -4761,16 +4735,6 @@ "util": "^0.12.5" } }, - "node_modules/atob": { - "version": "2.1.2", - "license": "(MIT OR Apache-2.0)", - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, "node_modules/available-typed-arrays": { "version": "1.0.7", "license": "MIT", @@ -4975,6 +4939,14 @@ ], "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.31", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.31.tgz", + "integrity": "sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -5189,9 +5161,9 @@ } }, "node_modules/browserslist": { - "version": "4.25.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.3.tgz", - "integrity": "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz", + "integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==", "funding": [ { "type": "opencollective", @@ -5207,10 +5179,11 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001735", - "electron-to-chromium": "^1.5.204", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" + "baseline-browser-mapping": "^2.8.25", + "caniuse-lite": "^1.0.30001754", + "electron-to-chromium": "^1.5.249", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.1.4" }, "bin": { "browserslist": "cli.js" @@ -5219,16 +5192,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/btoa": { - "version": "1.2.1", - "license": "(MIT OR Apache-2.0)", - "bin": { - "btoa": "bin/btoa.js" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/buffer": { "version": "6.0.3", "funding": [ @@ -5351,9 +5314,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001736", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001736.tgz", - "integrity": "sha512-ImpN5gLEY8gWeqfLUyEF4b7mYWcYoR2Si1VhnrbM4JizRFmfGaAQ12PhNykq6nvI4XvKLrsp8Xde74D5phJOSw==", + "version": "1.0.30001757", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz", + "integrity": "sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==", "funding": [ { "type": "opencollective", @@ -5403,18 +5366,6 @@ "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "optional": true }, - "node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/charenc": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", @@ -5511,6 +5462,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -5521,7 +5473,8 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/colord": { "version": "2.9.3", @@ -5673,9 +5626,9 @@ } }, "node_modules/copy-webpack-plugin": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-13.0.0.tgz", - "integrity": "sha512-FgR/h5a6hzJqATDGd9YG41SeDViH+0bkHn6WNXCi5zKAZkeESeSxLySSsFLHqLEVCh0E+rITmCf0dusXWYukeQ==", + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-13.0.1.tgz", + "integrity": "sha512-J+YV3WfhY6W/Xf9h+J1znYuqTye2xkBUIGyTPWuBAT27qajBa5mR4f8WBmfDY3YjRftT2kqZZiLi1qf0H+UOFw==", "dev": true, "dependencies": { "glob-parent": "^6.0.1", @@ -5706,12 +5659,12 @@ } }, "node_modules/core-js-compat": { - "version": "3.45.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.1.tgz", - "integrity": "sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==", + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.47.0.tgz", + "integrity": "sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==", "dev": true, "dependencies": { - "browserslist": "^4.25.3" + "browserslist": "^4.28.0" }, "funding": { "type": "opencollective", @@ -5807,20 +5760,20 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, "node_modules/cross-env": { - "version": "7.0.3", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", + "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==", "dev": true, - "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.1" + "@epic-web/invariant": "^1.0.0", + "cross-spawn": "^7.0.6" }, "bin": { - "cross-env": "src/bin/cross-env.js", - "cross-env-shell": "src/bin/cross-env-shell.js" + "cross-env": "dist/bin/cross-env.js", + "cross-env-shell": "dist/bin/cross-env-shell.js" }, "engines": { - "node": ">=10.14", - "npm": ">=6", - "yarn": ">=1" + "node": ">=20" } }, "node_modules/cross-spawn": { @@ -5953,9 +5906,9 @@ } }, "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", "dev": true, "engines": { "node": ">= 6" @@ -6266,6 +6219,18 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/detect-node": { "version": "2.1.0", "license": "MIT" @@ -6444,9 +6409,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.5.207", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.207.tgz", - "integrity": "sha512-mryFrrL/GXDTmAtIVMVf+eIXM09BBPlO5IQ7lUyKmK8d+A4VpRGG+M3ofoVef6qyF8s60rJei8ymlJxjUA8Faw==" + "version": "1.5.260", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.260.tgz", + "integrity": "sha512-ov8rBoOBhVawpzdre+Cmz4FB+y66Eqrk6Gwqd8NGxuhv99GQ8XqMAr351KEkOt7gukXWDg6gJWEMKgL2RLMPtA==" }, "node_modules/elliptic": { "version": "6.6.1", @@ -6736,6 +6701,21 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, + "node_modules/fast-png": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/fast-png/-/fast-png-6.4.0.tgz", + "integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==", + "dependencies": { + "@types/pako": "^2.0.3", + "iobuffer": "^5.3.2", + "pako": "^2.1.0" + } + }, + "node_modules/fast-png/node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, "node_modules/fast-uri": { "version": "3.0.1", "license": "MIT" @@ -7216,22 +7196,15 @@ "integrity": "sha512-+UmB/NcaXAIj+V/jjZWW9NWGAL2cDkcTIIfia/LqAYWURBIWxwVkzC744q2WXB62IVb8DaF+8CWXGLm9EnQqNg==" }, "node_modules/glob": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", - "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", + "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", "dev": true, - "license": "ISC", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", + "minimatch": "^10.1.1", "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, "engines": { "node": "20 || >=22" }, @@ -7253,16 +7226,16 @@ }, "node_modules/glob-to-regexp": { "version": "0.4.1", - "license": "BSD-2-Clause" + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" }, "node_modules/glob/node_modules/minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { "node": "20 || >=22" @@ -7464,9 +7437,9 @@ } }, "node_modules/html-webpack-plugin": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.3.tgz", - "integrity": "sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==", + "version": "5.6.5", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.5.tgz", + "integrity": "sha512-4xynFbKNNk+WlzXeQQ+6YYsH2g7mpfPszQZUi3ovKlj+pDmngQ7vRXjrrmGROabmKwyQkcgcX5hqfOwHbFmK5g==", "dev": true, "dependencies": { "@types/html-minifier-terser": "^6.0.0", @@ -7648,8 +7621,9 @@ "license": "BSD-3-Clause" }, "node_modules/immer": { - "version": "10.1.1", - "license": "MIT", + "version": "10.1.3", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.3.tgz", + "integrity": "sha512-tmjF/k8QDKydUlm3mZU+tjM6zeq9/fFpPqH9SzWmBnVVKsPBg/V66qsMwb3/Bo90cgUN+ghdVBess+hPsxUyRw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" @@ -7723,6 +7697,11 @@ "node": ">=10.13.0" } }, + "node_modules/iobuffer": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz", + "integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==" + }, "node_modules/ipaddr.js": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", @@ -8012,22 +7991,6 @@ "node": ">=10" } }, - "node_modules/jackspeak": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", - "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -8072,9 +8035,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "dependencies": { "argparse": "^2.0.1" @@ -8084,10 +8047,9 @@ } }, "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true, + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "bin": { "jsesc": "bin/jsesc" }, @@ -8126,13 +8088,12 @@ } }, "node_modules/jspdf": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.1.tgz", - "integrity": "sha512-qaGIxqxetdoNnFQQXxTKUD9/Z7AloLaw94fFsOiJMxbfYdBbrBuhWmbzI8TVjrw7s3jBY1PFHofBKMV/wZPapg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.4.tgz", + "integrity": "sha512-dc6oQ8y37rRcHn316s4ngz/nOjayLF/FFxBF4V9zamQKRqXxyiH1zagkCdktdWhtoQId5K20xt1lB90XzkB+hQ==", "dependencies": { - "@babel/runtime": "^7.26.7", - "atob": "^2.1.2", - "btoa": "^1.2.1", + "@babel/runtime": "^7.28.4", + "fast-png": "^6.2.0", "fflate": "^0.8.1" }, "optionalDependencies": { @@ -8443,9 +8404,9 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz", - "integrity": "sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==", + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.4.tgz", + "integrity": "sha512-ZWYT7ln73Hptxqxk2DxPU9MmapXRhxkJD6tkSR04dnQxm8BGu2hzgKLugK5yySD97u/8yy7Ma7E76k9ZdvtjkQ==", "dev": true, "dependencies": { "schema-utils": "^4.0.0", @@ -8508,9 +8469,9 @@ } }, "node_modules/minizlib/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", @@ -8766,9 +8727,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==" }, "node_modules/node-stdlib-browser": { "version": "1.3.1", @@ -9058,9 +9019,10 @@ } }, "node_modules/package-json-from-dist": { - "version": "1.0.0", - "dev": true, - "license": "BlueOak-1.0.0" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true }, "node_modules/pako": { "version": "1.0.11", @@ -9161,11 +9123,10 @@ "license": "MIT" }, "node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", "dev": true, - "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" @@ -9178,11 +9139,10 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.1.tgz", - "integrity": "sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==", + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", + "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", "dev": true, - "license": "ISC", "engines": { "node": "20 || >=22" } @@ -9701,18 +9661,17 @@ } }, "node_modules/react-datepicker": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-7.5.0.tgz", - "integrity": "sha512-6MzeamV8cWSOcduwePHfGqY40acuGlS1cG//ePHT6bVbLxWyqngaStenfH03n1wbzOibFggF66kWaBTb1SbTtQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-7.6.0.tgz", + "integrity": "sha512-9cQH6Z/qa4LrGhzdc3XoHbhrxNcMi9MKjZmYgF/1MNNaJwvdSjv3Xd+jjvrEEbKEf71ZgCA3n7fQbdwd70qCRw==", "dependencies": { - "@floating-ui/react": "^0.26.23", + "@floating-ui/react": "^0.27.0", "clsx": "^2.1.1", - "date-fns": "^3.6.0", - "prop-types": "^15.8.1" + "date-fns": "^3.6.0" }, "peerDependencies": { - "react": "^16.9.0 || ^17 || ^18", - "react-dom": "^16.9.0 || ^17 || ^18" + "react": "^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "node_modules/react-dom": { @@ -9797,9 +9756,9 @@ } }, "node_modules/react-refresh": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", - "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", "dev": true, "engines": { "node": ">=0.10.0" @@ -9836,9 +9795,9 @@ } }, "node_modules/react-select": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.10.1.tgz", - "integrity": "sha512-roPEZUL4aRZDx6DcsD+ZNreVl+fM8VsKn0Wtex1v4IazH60ILp5xhdlp464IsEAlJdXeD+BhDAFsBVMfvLQueA==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.10.2.tgz", + "integrity": "sha512-Z33nHdEFWq9tfnfVXaiM12rbJmk+QjFEztWLtmXqQhz6Al4UZZ9xc0wiatmGtUOCCnHN0WizL3tCMYRENX4rVQ==", "dependencies": { "@babel/runtime": "^7.12.0", "@emotion/cache": "^11.4.0", @@ -10025,9 +9984,9 @@ "dev": true }, "node_modules/regenerate-unicode-properties": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", + "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", "dev": true, "dependencies": { "regenerate": "^1.4.2" @@ -10063,17 +10022,17 @@ } }, "node_modules/regexpu-core": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", - "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", + "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", "dev": true, "dependencies": { "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", + "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", + "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" + "unicode-match-property-value-ecmascript": "^2.2.1" }, "engines": { "node": ">=4" @@ -10086,12 +10045,12 @@ "dev": true }, "node_modules/regjsparser": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz", + "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==", "dev": true, "dependencies": { - "jsesc": "~3.0.2" + "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" @@ -10225,14 +10184,13 @@ } }, "node_modules/rimraf": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", - "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.2.tgz", + "integrity": "sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==", "dev": true, - "license": "ISC", "dependencies": { - "glob": "^11.0.0", - "package-json-from-dist": "^1.0.0" + "glob": "^13.0.0", + "package-json-from-dist": "^1.0.1" }, "bin": { "rimraf": "dist/esm/bin.mjs" @@ -10321,9 +10279,9 @@ } }, "node_modules/sass-loader": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.5.tgz", - "integrity": "sha512-oL+CMBXrj6BZ/zOq4os+UECPL+bWqt6OAC6DWS8Ln8GZRcMDjlJ4JC3FBDuHJdYaFWIdKNIBYmtZtK2MaMkNIw==", + "version": "16.0.6", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.6.tgz", + "integrity": "sha512-sglGzId5gmlfxNs4gK2U3h7HlVRfx278YK6Ono5lwzuvi1jxig80YiuHkaDBVsYIKFhx8wN7XSCI0M2IDS/3qA==", "dev": true, "dependencies": { "neo-async": "^2.6.2" @@ -10369,9 +10327,9 @@ } }, "node_modules/schema-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", - "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -11004,6 +10962,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -11035,10 +10994,15 @@ "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" }, "node_modules/tapable": { - "version": "2.2.1", - "license": "MIT", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "engines": { "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/tar": { @@ -11195,13 +11159,13 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", - "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "dependencies": { - "fdir": "^6.4.3", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -11211,10 +11175,13 @@ } }, "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", - "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -11225,9 +11192,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "engines": { "node": ">=12" @@ -11374,9 +11341,9 @@ } }, "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -11415,18 +11382,18 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", - "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", + "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", + "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", "dev": true, "engines": { "node": ">=4" @@ -11464,9 +11431,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", "funding": [ { "type": "opencollective", @@ -11604,22 +11571,22 @@ "integrity": "sha512-m6EXlCAMetKztO1ppBhGU1/1MR3IiEevO6ESq6rcrSQ3Q77xYSW13jkfXW88o4xMrkXJhy/U7j4wFR/twMB0Eg==" }, "node_modules/vis-data": { - "version": "7.1.10", - "resolved": "https://registry.npmjs.org/vis-data/-/vis-data-7.1.10.tgz", - "integrity": "sha512-23juM9tdCaHTX5vyIQ7XBzsfZU0Hny+gSTwniLrfFcmw9DOm7pi3+h9iEBsoZMp5rX6KNqWwc1MF0fkAmWVuoQ==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/vis-data/-/vis-data-8.0.3.tgz", + "integrity": "sha512-jhnb6rJNqkKR1Qmlay0VuDXY9ZlvAnYN1udsrP4U+krgZEq7C0yNSKdZqmnCe13mdnf9AdVcdDGFOzy2mpPoqw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/visjs" }, "peerDependencies": { - "uuid": "^3.4.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", - "vis-util": "^5.0.1" + "uuid": "^3.4.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^13.0.0", + "vis-util": ">=6.0.0" } }, "node_modules/vis-network": { - "version": "9.1.13", - "resolved": "https://registry.npmjs.org/vis-network/-/vis-network-9.1.13.tgz", - "integrity": "sha512-HLeHd5KZS92qzO1kC59qMh1/FWAZxMUEwUWBwDMoj6RKj/Ajkrgy/heEYo0Zc8SZNQ2J+u6omvK2+a28GX1QuQ==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/vis-network/-/vis-network-10.0.2.tgz", + "integrity": "sha512-qPl8GLYBeHEFqiTqp4VBbYQIJ2EA8KLr7TstA2E8nJxfEHaKCU81hQLz7hhq11NUpHbMaRzBjW5uZpVKJ45/wA==", "funding": { "type": "opencollective", "url": "https://opencollective.com/visjs" @@ -11628,15 +11595,15 @@ "@egjs/hammerjs": "^2.0.0", "component-emitter": "^1.3.0 || ^2.0.0", "keycharm": "^0.2.0 || ^0.3.0 || ^0.4.0", - "uuid": "^3.4.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", - "vis-data": "^6.3.0 || ^7.0.0", - "vis-util": "^5.0.1" + "uuid": "^3.4.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^13.0.0", + "vis-data": ">=8.0.0", + "vis-util": ">=6.0.0" } }, "node_modules/vis-util": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/vis-util/-/vis-util-5.0.7.tgz", - "integrity": "sha512-E3L03G3+trvc/X4LXvBfih3YIHcKS2WrP0XTdZefr6W6Qi/2nNCqZfe4JFfJU6DcQLm6Gxqj2Pfl+02859oL5A==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/vis-util/-/vis-util-6.0.0.tgz", + "integrity": "sha512-qtpts3HRma0zPe4bO7t9A2uejkRNj8Z2Tb6do6lN85iPNWExFkUiVhdAq5uLGIUqBFduyYeqWJKv/jMkxX0R5g==", "peer": true, "engines": { "node": ">=8" @@ -11655,8 +11622,9 @@ "license": "MIT" }, "node_modules/watchpack": { - "version": "2.4.1", - "license": "MIT", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -11675,9 +11643,9 @@ } }, "node_modules/webpack": { - "version": "5.100.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.100.2.tgz", - "integrity": "sha512-QaNKAvGCDRh3wW1dsDjeMdDXwZm2vqq3zn6Pvq4rHOEOGSaUMgOOjG2Y9ZbIGzpfkJk9ZYTHpDqgDfeBDcnLaw==", + "version": "5.102.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.102.1.tgz", + "integrity": "sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==", "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -11687,9 +11655,9 @@ "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.15.0", "acorn-import-phases": "^1.0.3", - "browserslist": "^4.24.0", + "browserslist": "^4.26.3", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.2", + "enhanced-resolve": "^5.17.3", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -11699,10 +11667,10 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^4.3.2", - "tapable": "^2.1.1", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.1", + "watchpack": "^2.4.4", "webpack-sources": "^3.3.3" }, "bin": { diff --git a/jbrowse/src/client/JBrowse/Browser/plugins/ExtendedVariantPlugin/ExtendedVariantAdapter/ExtendedVariantAdapter.ts b/jbrowse/src/client/JBrowse/Browser/plugins/ExtendedVariantPlugin/ExtendedVariantAdapter/ExtendedVariantAdapter.ts index d7973773b..035790c92 100644 --- a/jbrowse/src/client/JBrowse/Browser/plugins/ExtendedVariantPlugin/ExtendedVariantAdapter/ExtendedVariantAdapter.ts +++ b/jbrowse/src/client/JBrowse/Browser/plugins/ExtendedVariantPlugin/ExtendedVariantAdapter/ExtendedVariantAdapter.ts @@ -1,14 +1,57 @@ import QuickLRU from '@jbrowse/core/util/QuickLRU'; -import { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter'; +import { BaseOptions, BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter'; import { NoAssemblyRegion } from '@jbrowse/core/util/types'; import { ObservableCreate } from '@jbrowse/core/util/rxjs'; import { Feature } from '@jbrowse/core/util/simpleFeature'; import ExtendedVcfFeature from './ExtendedVcfFeature'; import { VcfFeature } from '@jbrowse/plugin-variants'; -import { default as VcfTabixAdapter } from './VcfTabixAdapter'; -export default class extends VcfTabixAdapter { +export default class extends BaseFeatureDataAdapter { protected featureCache = new QuickLRU({ maxSize: 20 }) + private subAdapterP?: Promise + + constructor(...args: any[]) { + super(...args) + + // Return a Proxy that forwards any unknown member access to the sub-adapter. + // This avoids re-implementing methods like getHeader/getRefNames/getMetadata/etc. + const self = this + return new Proxy(this, { + get(target, prop, receiver) { + // If we have it already (e.g., getFeatures, getFeaturesAsArray, BaseFeatureDataAdapter-derived properties), use it directly + if (prop in target || typeof prop === 'symbol') { + return Reflect.get(target, prop, receiver) + } + + // Otherwise, forward to the VcfTabixAdapter sub-adapter + return async (...callArgs: any[]) => { + const sub = await self.getVcfSubAdapter() + const value = (sub as any)[prop] + + // If it’s a method, call it; otherwise return the property value + if (typeof value === 'function') { + return value.apply(sub, callArgs) + } + return value + } + }, + }) + } + + private async getVcfSubAdapter(): Promise { + if (!this.subAdapterP) { + const vcfGzLocation = this.getConf('vcfGzLocation') + const index = this.getConf(['index']) + const vcfAdapterConf = { type: 'VcfTabixAdapter', vcfGzLocation, index } + this.subAdapterP = this.getSubAdapter!(vcfAdapterConf) + .then(({ dataAdapter }) => dataAdapter) + .catch(e => { + this.subAdapterP = undefined + throw e + }) + } + return this.subAdapterP + } public getFeatures(query: NoAssemblyRegion, opts: BaseOptions = {}) { return ObservableCreate(async observer => { @@ -50,4 +93,29 @@ export default class extends VcfTabixAdapter { return features } + + // Typescript errors at compile time without these stubs + async configure(opts?: BaseOptions) { + const sub = await this.getVcfSubAdapter() + return sub.configure(opts) + } + + async getRefNames(opts: BaseOptions = {}) { + const sub = await this.getVcfSubAdapter() + return sub.getRefNames(opts) + } + + async getHeader(opts?: BaseOptions) { + const sub = await this.getVcfSubAdapter() + return sub.getHeader(opts) + } + + async getMetadata(opts?: BaseOptions) { + const sub = await this.getVcfSubAdapter() + return sub.getMetadata(opts) + } + + freeResources(): void { + void this.getVcfSubAdapter().then(sub => sub.freeResources?.()) + } } \ No newline at end of file diff --git a/jbrowse/src/client/JBrowse/Browser/plugins/ExtendedVariantPlugin/ExtendedVariantAdapter/VcfTabixAdapter.ts b/jbrowse/src/client/JBrowse/Browser/plugins/ExtendedVariantPlugin/ExtendedVariantAdapter/VcfTabixAdapter.ts deleted file mode 100644 index fca6a3e64..000000000 --- a/jbrowse/src/client/JBrowse/Browser/plugins/ExtendedVariantPlugin/ExtendedVariantAdapter/VcfTabixAdapter.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { TabixIndexedFile } from '@gmod/tabix' -import VcfParser from '@gmod/vcf' -import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter' -import { - fetchAndMaybeUnzipText, - updateStatus, -} from '@jbrowse/core/util' -import { openLocation } from '@jbrowse/core/util/io' -import { ObservableCreate } from '@jbrowse/core/util/rxjs' - -import type { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter' -import type { Feature } from '@jbrowse/core/util' -import type { NoAssemblyRegion } from '@jbrowse/core/util/types' -import { VcfFeature } from '@jbrowse/plugin-variants'; - -function shorten2(name: string, max = 70) { - return name.length > max ? `${name.slice(0, max)}...` : name -} - -export default class VcfTabixAdapter extends BaseFeatureDataAdapter { - private configured?: Promise<{ - vcf: TabixIndexedFile - parser: VcfParser - }> - - private async configurePre(_opts?: BaseOptions) { - const vcfGzLocation = this.getConf('vcfGzLocation') - const location = this.getConf(['index', 'location']) - const indexType = this.getConf(['index', 'indexType']) - - const filehandle = openLocation(vcfGzLocation, this.pluginManager) - const isCSI = indexType === 'CSI' - const vcf = new TabixIndexedFile({ - filehandle, - csiFilehandle: isCSI - ? openLocation(location, this.pluginManager) - : undefined, - tbiFilehandle: !isCSI - ? openLocation(location, this.pluginManager) - : undefined, - chunkCacheSize: 50 * 2 ** 20, - }) - - return { - vcf, - parser: new VcfParser({ - header: await vcf.getHeader(), - }), - } - } - - protected async configurePre2() { - if (!this.configured) { - this.configured = this.configurePre().catch((e: unknown) => { - this.configured = undefined - throw e - }) - } - return this.configured - } - - async configure(opts?: BaseOptions) { - const { statusCallback = () => {} } = opts || {} - return updateStatus('Downloading index', statusCallback, () => - this.configurePre2(), - ) - } - public async getRefNames(opts: BaseOptions = {}) { - const { vcf } = await this.configure(opts) - return vcf.getReferenceSequenceNames(opts) - } - - async getHeader(opts?: BaseOptions) { - const { vcf } = await this.configure(opts) - return vcf.getHeader() - } - - async getMetadata(opts?: BaseOptions) { - const { parser } = await this.configure(opts) - return parser.getMetadata() - } - - public getFeatures(query: NoAssemblyRegion, opts: BaseOptions = {}) { - return ObservableCreate(async observer => { - const { refName, start, end } = query - const { statusCallback = () => {} } = opts - const { vcf, parser } = await this.configure(opts) - - await updateStatus('Downloading variants', statusCallback, () => - vcf.getLines(refName, start, end, { - lineCallback: (line, fileOffset) => { - observer.next( - new VcfFeature({ - variant: parser.parseLine(line), - parser, - id: `${this.id}-vcf-${fileOffset}`, - }), - ) - }, - ...opts, - }), - ) - observer.complete() - }, opts.stopToken) - } - - async getSources() { - const conf = this.getConf('samplesTsvLocation') - if (conf.uri === '' || conf.uri === '/path/to/samples.tsv') { - const { parser } = await this.configure() - return parser.samples.map(name => ({ - name, - })) - } else { - const txt = await fetchAndMaybeUnzipText( - openLocation(conf, this.pluginManager), - ) - const lines = txt.split(/\n|\r\n|\r/) - const header = lines[0]!.split('\t') - const { parser } = await this.configure() - const metadataLines = lines - .slice(1) - .filter(f => !!f) - .map(line => { - const [name, ...rest] = line.split('\t') - return { - ...Object.fromEntries( - // force col 0 to be called name - rest.map((c, idx) => [header[idx + 1]!, c] as const), - ), - name: name!, - } - }) - const vcfSampleSet = new Set(parser.samples) - const metadataSet = new Set(metadataLines.map(r => r.name)) - const metadataNotInVcfSamples = [...metadataSet].filter( - f => !vcfSampleSet.has(f), - ) - const vcfSamplesNotInMetadata = [...vcfSampleSet].filter( - f => !metadataSet.has(f), - ) - if (metadataNotInVcfSamples.length) { - console.warn( - `There are ${metadataNotInVcfSamples.length} samples in metadata file (${metadataLines.length} lines) not in VCF (${parser.samples.length} samples):`, - shorten2(metadataNotInVcfSamples.join(',')), - ) - } - if (vcfSamplesNotInMetadata.length) { - console.warn( - `There are ${vcfSamplesNotInMetadata.length} samples in VCF file (${parser.samples.length} samples) not in metadata file (${metadataLines.length} lines):`, - shorten2(vcfSamplesNotInMetadata.map(m => m).join(',')), - ) - } - return metadataLines.filter(f => vcfSampleSet.has(f.name)) - } - } - - public freeResources(/* { region } */): void {} -} diff --git a/singlecell/resources/chunks/CommonFilters.R b/singlecell/resources/chunks/CommonFilters.R index 7e76aafbc..94aef6815 100644 --- a/singlecell/resources/chunks/CommonFilters.R +++ b/singlecell/resources/chunks/CommonFilters.R @@ -171,7 +171,10 @@ for (datasetId in names(seuratObjects)) { } toDrop <- is.na(seuratObj@meta.data$scGateConsensus) - if (sum(toDrop) > 0) { + if (sum(toDrop) == ncol(seuratObj)) { + print(paste0('There were no cells remaining after dropping cells without a scGateConsensus value')) + seuratObj <- NULL + } else if (sum(toDrop) > 0) { cells <- colnames(seuratObj)[!is.na(seuratObj@meta.data$scGateConsensus)] seuratObj <- subset(seuratObj, cells = cells) print(paste0('After dropping cells without scGateConsensus: ', length(colnames(x = seuratObj)))) diff --git a/singlecell/resources/chunks/IdentifyAndStoreActiveClonotypes.R b/singlecell/resources/chunks/IdentifyAndStoreActiveClonotypes.R new file mode 100644 index 000000000..0e4d474c2 --- /dev/null +++ b/singlecell/resources/chunks/IdentifyAndStoreActiveClonotypes.R @@ -0,0 +1,22 @@ +netRc <- paste0(Sys.getenv('USER_HOME'), '/.netrc') +if (!file.exists(netRc)) { + print(list.files(Sys.getenv('USER_HOME'))) + stop(paste0('Unable to find file: ', netRc)) +} + +invisible(Rlabkey::labkey.setCurlOptions(NETRC_FILE = netRc)) +Rdiscvr::SetLabKeyDefaults(baseUrl = serverBaseUrl, defaultFolder = defaultLabKeyFolder) + +for (datasetId in names(seuratObjects)) { + printName(datasetId) + seuratObj <- readSeuratRDS(seuratObjects[[datasetId]]) + + Rdiscvr::IdentifyAndStoreActiveClonotypes(seuratObj, chain = 'TRA', storeStimLevelData = FALSE) + Rdiscvr::IdentifyAndStoreActiveClonotypes(seuratObj, chain = 'TRB') + + saveData(seuratObj, datasetId) + + # Cleanup + rm(seuratObj) + gc() +} \ No newline at end of file diff --git a/singlecell/resources/chunks/RunCsCore.R b/singlecell/resources/chunks/RunCsCore.R deleted file mode 100644 index b7c7fd7a8..000000000 --- a/singlecell/resources/chunks/RunCsCore.R +++ /dev/null @@ -1,14 +0,0 @@ -for (datasetId in names(seuratObjects)) { - printName(datasetId) - seuratObj <- readSeuratRDS(seuratObjects[[datasetId]]) - - outFile <- paste0(outputPrefix, '.', makeLegalFileName(datasetId), '.markers.txt') - module_list <- CellMembrane::RunCsCore(seuratObj, saveFile = paste0(outFile, '.cscore.rds')) - saveRDS(module_list, paste0(outFile, '.cscore.wgcna.rds')) - - saveData(seuratObj, datasetId) - - # Cleanup - rm(seuratObj) - gc() -} \ No newline at end of file diff --git a/singlecell/resources/chunks/RunTricycle.R b/singlecell/resources/chunks/RunTricycle.R deleted file mode 100644 index b04162f1d..000000000 --- a/singlecell/resources/chunks/RunTricycle.R +++ /dev/null @@ -1,12 +0,0 @@ - for (datasetId in names(seuratObjects)) { - printName(datasetId) - seuratObj <- readSeuratRDS(seuratObjects[[datasetId]]) - - seuratObj <- CellMembrane::RunTricycle(seuratObj) - - saveData(seuratObj, datasetId) - - # Cleanup - rm(seuratObj) - gc() -} \ No newline at end of file diff --git a/singlecell/resources/chunks/StudyMetadata.R b/singlecell/resources/chunks/StudyMetadata.R index f5fb694ff..a54383add 100644 --- a/singlecell/resources/chunks/StudyMetadata.R +++ b/singlecell/resources/chunks/StudyMetadata.R @@ -32,6 +32,8 @@ for (datasetId in names(seuratObjects)) { seuratObj <- Rdiscvr::ApplyEC_Metadata(seuratObj, errorIfUnknownIdsFound = errorIfUnknownIdsFound) } else if (studyName == 'PPG_Stims') { seuratObj <- Rdiscvr::ApplyPPG_Stim_Metadata(seuratObj, errorIfUnknownIdsFound = errorIfUnknownIdsFound) + } else if (studyName == 'IMPAC_TB_Human') { + seuratObj <- Rdiscvr::ApplyIMPAC_TB_Human_Metadata(seuratObj, errorIfUnknownIdsFound = errorIfUnknownIdsFound) } else { stop(paste0('Unknown study: ', studyName)) } diff --git a/singlecell/resources/queries/singlecell/samples.js b/singlecell/resources/queries/singlecell/samples.js index f66be9f90..6b3ec2600 100644 --- a/singlecell/resources/queries/singlecell/samples.js +++ b/singlecell/resources/queries/singlecell/samples.js @@ -23,7 +23,7 @@ function beforeUpsert(row, oldRow, errors){ else if (['No stim', 'No Stim'].indexOf(row.stim) !== -1){ row.stim = 'NoStim'; } - else if (['Infected cells: SIV+', 'Infected Cells: SIV+'].indexOf(row.stim) !== -1){ + else if (['SIV+', 'Infected cells: SIV+', 'Infected Cells: SIV+'].indexOf(row.stim) !== -1){ row.stim = 'SIV-Infected CD4s'; } else if (['Infected cells: SIV-', 'Infected Cells: SIV-'].indexOf(row.stim) !== -1){ diff --git a/singlecell/src/org/labkey/singlecell/SingleCellModule.java b/singlecell/src/org/labkey/singlecell/SingleCellModule.java index 29ec88138..92efcb752 100644 --- a/singlecell/src/org/labkey/singlecell/SingleCellModule.java +++ b/singlecell/src/org/labkey/singlecell/SingleCellModule.java @@ -70,6 +70,7 @@ import org.labkey.singlecell.pipeline.singlecell.FilterRawCounts; import org.labkey.singlecell.pipeline.singlecell.FindClustersAndDimRedux; import org.labkey.singlecell.pipeline.singlecell.FindMarkers; +import org.labkey.singlecell.pipeline.singlecell.IdentifyAndStoreActiveClonotypes; import org.labkey.singlecell.pipeline.singlecell.IntegrateData; import org.labkey.singlecell.pipeline.singlecell.MergeSeurat; import org.labkey.singlecell.pipeline.singlecell.NormalizeAndScale; @@ -86,7 +87,6 @@ import org.labkey.singlecell.pipeline.singlecell.RunCelltypist; import org.labkey.singlecell.pipeline.singlecell.RunCelltypistCustomModel; import org.labkey.singlecell.pipeline.singlecell.RunConga; -import org.labkey.singlecell.pipeline.singlecell.RunCsCore; import org.labkey.singlecell.pipeline.singlecell.RunDecoupler; import org.labkey.singlecell.pipeline.singlecell.RunEscape; import org.labkey.singlecell.pipeline.singlecell.RunLDA; @@ -97,7 +97,6 @@ import org.labkey.singlecell.pipeline.singlecell.RunScGate; import org.labkey.singlecell.pipeline.singlecell.RunScGateBuiltin; import org.labkey.singlecell.pipeline.singlecell.RunSingleR; -import org.labkey.singlecell.pipeline.singlecell.RunTricycle; import org.labkey.singlecell.pipeline.singlecell.RunVision; import org.labkey.singlecell.pipeline.singlecell.ScoreCellCycle; import org.labkey.singlecell.pipeline.singlecell.SeuratPrototype; @@ -115,6 +114,7 @@ import org.labkey.singlecell.run.CellRangerVDJWrapper; import org.labkey.singlecell.run.NimbleAlignmentStep; import org.labkey.singlecell.run.NimbleAnalysis; +import org.labkey.singlecell.run.NimbleBulkAlignmentStep; import org.labkey.singlecell.run.RepeatNimbleReportHandler; import org.labkey.singlecell.run.VelocytoAlignmentStep; import org.labkey.singlecell.run.VelocytoAnalysisStep; @@ -220,6 +220,7 @@ public static void registerPipelineSteps() SequencePipelineService.get().registerPipelineStep(new CellRangerVDJWrapper.VDJProvider()); SequencePipelineService.get().registerPipelineStep(new NimbleAlignmentStep.Provider()); SequencePipelineService.get().registerPipelineStep(new NimbleAnalysis.Provider()); + SequencePipelineService.get().registerPipelineStep(new NimbleBulkAlignmentStep.Provider()); SequencePipelineService.get().registerPipelineStep(new VelocytoAlignmentStep.Provider()); SequencePipelineService.get().registerPipelineStep(new VelocytoAnalysisStep.Provider()); @@ -294,17 +295,16 @@ public static void registerPipelineSteps() SequencePipelineService.get().registerPipelineStep(new TrainScTour.Provider()); SequencePipelineService.get().registerPipelineStep(new PredictScTour.Provider()); SequencePipelineService.get().registerPipelineStep(new RunEscape.Provider()); - SequencePipelineService.get().registerPipelineStep(new RunCsCore.Provider()); SequencePipelineService.get().registerPipelineStep(new CustomGSEA.Provider()); SequencePipelineService.get().registerPipelineStep(new StudyMetadata.Provider()); SequencePipelineService.get().registerPipelineStep(new UpdateSeuratPrototype.Provider()); SequencePipelineService.get().registerPipelineStep(new RunDecoupler.Provider()); SequencePipelineService.get().registerPipelineStep(new PerformDefaultNimbleAppend.Provider()); SequencePipelineService.get().registerPipelineStep(new PerformMhcDimRedux.Provider()); - SequencePipelineService.get().registerPipelineStep(new RunTricycle.Provider()); SequencePipelineService.get().registerPipelineStep(new ApplyKnownClonotypicData.Provider()); SequencePipelineService.get().registerPipelineStep(new CalculateTcrRepertoireStats.Provider()); SequencePipelineService.get().registerPipelineStep(new PredictTcellActivation.Provider()); + SequencePipelineService.get().registerPipelineStep(new IdentifyAndStoreActiveClonotypes.Provider()); SequenceAnalysisService.get().registerReadsetListener(new SingleCellReadsetListener()); } diff --git a/singlecell/src/org/labkey/singlecell/analysis/AbstractSingleCellHandler.java b/singlecell/src/org/labkey/singlecell/analysis/AbstractSingleCellHandler.java index 45ce396c2..7656f92a2 100644 --- a/singlecell/src/org/labkey/singlecell/analysis/AbstractSingleCellHandler.java +++ b/singlecell/src/org/labkey/singlecell/analysis/AbstractSingleCellHandler.java @@ -396,7 +396,20 @@ public void processFilesRemote(List inputFiles, JobContext c currentFiles = new ArrayList<>(); for (SequenceOutputFile so : inputFiles) { - String datasetId = FileUtil.makeLegalName(so.getReadset() != null ? ctx.getSequenceSupport().getCachedReadset(so.getReadset()).getName() : so.getName()); + String datasetId; + if ("Seurat Object Prototype".equals(so.getCategory())) + { + datasetId = FileUtil.makeLegalName(ctx.getSequenceSupport().getCachedReadset(so.getReadset()).getName()); + } + else if (_doProcessRawCounts) + { + datasetId = FileUtil.makeLegalName(so.getReadset() == null ? so.getName() : ctx.getSequenceSupport().getCachedReadset(so.getReadset()).getName()); + } + else + { + datasetId = so.getName(); + } + if (distinctIds.contains(datasetId)) { throw new PipelineJobException("Duplicate dataset Ids in input data: " + datasetId); diff --git a/singlecell/src/org/labkey/singlecell/pipeline/singlecell/CalculateGeneComponentScores.java b/singlecell/src/org/labkey/singlecell/pipeline/singlecell/CalculateGeneComponentScores.java index 43be39118..4bc652684 100644 --- a/singlecell/src/org/labkey/singlecell/pipeline/singlecell/CalculateGeneComponentScores.java +++ b/singlecell/src/org/labkey/singlecell/pipeline/singlecell/CalculateGeneComponentScores.java @@ -24,7 +24,7 @@ public Provider() super("CalculateGeneComponentScores", "Calculate Gene Module Scores", "RIRA", "This will generate UCell scores for a set of pre-defined gene modules", Collections.singletonList( SeuratToolParameter.create("savedComponent", "Saved Component(s)", "This is the name of the saved component (from RIRA) to apply", "ldk-simplecombo", new JSONObject() {{ - put("storeValues", "Tcell_EffectorDifferentiation;TCR_EarlyStimulationComponent;TCR_StimulationComponent1;PLS_Score_1;PLS_Score_2;PLS_Score_3;PLS_Score_4;PLS_Score_5;PLS_Score_6"); + put("storeValues", "Tcell_EffectorDifferentiation;TCR_EarlyStimulationComponent;CD4_Activation_Axis"); put("multiSelect", true); put("allowBlank", false); put("joinReturnValue", true); diff --git a/singlecell/src/org/labkey/singlecell/pipeline/singlecell/CommonFilters.java b/singlecell/src/org/labkey/singlecell/pipeline/singlecell/CommonFilters.java index 88ccb8696..25365bfec 100644 --- a/singlecell/src/org/labkey/singlecell/pipeline/singlecell/CommonFilters.java +++ b/singlecell/src/org/labkey/singlecell/pipeline/singlecell/CommonFilters.java @@ -27,12 +27,12 @@ public Provider() put("minValue", 0); put("maxValue", 1); put("decimalPrecision", 3); - }}, 0.5), + }}, 0.1), SeuratToolParameter.create("saturation.RNA.max", "Saturation.RNA Max", "Saturation.RNA max value", "ldk-numberfield", new JSONObject(){{ put("minValue", 0); put("maxValue", 1); put("decimalPrecision", 3); - }}, 0.9), + }}, 0.99), SeuratToolParameter.create("saturation.ADT.min", "Saturation.ADT Min", "Saturation.ADT min value", "ldk-numberfield", new JSONObject(){{ put("minValue", 0); put("maxValue", 1); diff --git a/singlecell/src/org/labkey/singlecell/pipeline/singlecell/IdentifyAndStoreActiveClonotypes.java b/singlecell/src/org/labkey/singlecell/pipeline/singlecell/IdentifyAndStoreActiveClonotypes.java new file mode 100644 index 000000000..f5c67047f --- /dev/null +++ b/singlecell/src/org/labkey/singlecell/pipeline/singlecell/IdentifyAndStoreActiveClonotypes.java @@ -0,0 +1,37 @@ +package org.labkey.singlecell.pipeline.singlecell; + +import org.labkey.api.sequenceanalysis.pipeline.AbstractPipelineStepProvider; +import org.labkey.api.sequenceanalysis.pipeline.PipelineContext; +import org.labkey.api.singlecell.pipeline.SingleCellStep; + +import java.util.List; + +public class IdentifyAndStoreActiveClonotypes extends AbstractRDiscvrStep +{ + public IdentifyAndStoreActiveClonotypes(PipelineContext ctx, IdentifyAndStoreActiveClonotypes.Provider provider) + { + super(provider, ctx); + } + + public static class Provider extends AbstractPipelineStepProvider + { + public Provider() + { + super("IdentifyAndStoreActiveClonotypes", "Identify And Store Active Clonotypes", "Rdiscvr", "This uses RDiscvr::IdentifyAndStoreActiveClonotypes to predict TCR-triggered T cells and save the results to the database", List.of(), null, null); + } + + + @Override + public IdentifyAndStoreActiveClonotypes create(PipelineContext ctx) + { + return new IdentifyAndStoreActiveClonotypes(ctx, this); + } + } + + @Override + public String getFileSuffix() + { + return "is"; + } +} + diff --git a/singlecell/src/org/labkey/singlecell/pipeline/singlecell/RunCsCore.java b/singlecell/src/org/labkey/singlecell/pipeline/singlecell/RunCsCore.java deleted file mode 100644 index f3a2dded2..000000000 --- a/singlecell/src/org/labkey/singlecell/pipeline/singlecell/RunCsCore.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.labkey.singlecell.pipeline.singlecell; - -import org.labkey.api.pipeline.PipelineJobException; -import org.labkey.api.sequenceanalysis.pipeline.AbstractPipelineStepProvider; -import org.labkey.api.sequenceanalysis.pipeline.PipelineContext; -import org.labkey.api.sequenceanalysis.pipeline.SequenceOutputHandler; -import org.labkey.api.singlecell.pipeline.SingleCellStep; - -import java.io.File; -import java.util.Collections; -import java.util.List; - -public class RunCsCore extends AbstractCellMembraneStep -{ - public RunCsCore(PipelineContext ctx, Provider provider) - { - super(provider, ctx); - } - - public static class Provider extends AbstractPipelineStepProvider - { - public Provider() - { - super("RunCsCore", "CS-CORE", "CS-CORE", "Run CS-CORE on the seurat object to identify gene modules.", Collections.emptyList(), null, null); - } - - @Override - public RunCsCore create(PipelineContext ctx) - { - return new RunCsCore(ctx, this); - } - } - - @Override - public boolean createsSeuratObjects() - { - return false; - } - - @Override - public String getFileSuffix() - { - return "cscore"; - } - - @Override - public Output execute(SequenceOutputHandler.JobContext ctx, List inputObjects, String outputPrefix) throws PipelineJobException - { - Output output = super.execute(ctx, inputObjects, outputPrefix); - - // Add the RDS files: - File[] outputs = ctx.getOutputDir().listFiles(f -> f.isDirectory() && f.getName().endsWith(".cscore.wgcna.rds")); - if (outputs == null || outputs.length == 0) - { - return output; - } - - for (File rds : outputs) - { - String sn = rds.getName().replaceAll(".cscore.wgcna.rds", ""); - - output.addSequenceOutput(rds, "CS-CORE: " + sn, "CS-CORE Results", inputObjects.get(0).getReadsetId(), null, ctx.getSequenceSupport().getCachedGenomes().iterator().next().getGenomeId(), null); - } - - return output; - } -} \ No newline at end of file diff --git a/singlecell/src/org/labkey/singlecell/pipeline/singlecell/RunTricycle.java b/singlecell/src/org/labkey/singlecell/pipeline/singlecell/RunTricycle.java deleted file mode 100644 index 4e79d7673..000000000 --- a/singlecell/src/org/labkey/singlecell/pipeline/singlecell/RunTricycle.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.labkey.singlecell.pipeline.singlecell; - -import org.labkey.api.sequenceanalysis.pipeline.AbstractPipelineStepProvider; -import org.labkey.api.sequenceanalysis.pipeline.PipelineContext; -import org.labkey.api.singlecell.pipeline.SingleCellStep; - -import java.util.Arrays; - -public class RunTricycle extends AbstractCellMembraneStep -{ - public RunTricycle(PipelineContext ctx, RunTricycle.Provider provider) - { - super(provider, ctx); - } - - public static class Provider extends AbstractPipelineStepProvider - { - public Provider() - { - super("RunTricycle", "Run Tricycle", "CellMembrane/Tricycle", "This will run tricycle on the input object(s) to score cell cycle, and save the results in metadata.", Arrays.asList( - - ), null, null); - } - - @Override - public RunTricycle create(PipelineContext ctx) - { - return new RunTricycle(ctx, this); - } - } - - @Override - public String getFileSuffix() - { - return "tricycle"; - } -} diff --git a/singlecell/src/org/labkey/singlecell/pipeline/singlecell/StudyMetadata.java b/singlecell/src/org/labkey/singlecell/pipeline/singlecell/StudyMetadata.java index 27050d587..82ffbd953 100644 --- a/singlecell/src/org/labkey/singlecell/pipeline/singlecell/StudyMetadata.java +++ b/singlecell/src/org/labkey/singlecell/pipeline/singlecell/StudyMetadata.java @@ -24,7 +24,7 @@ public Provider() {{ put("multiSelect", false); put("allowBlank", false); - put("storeValues", "PC475;PC531;TB;Malaria;AcuteNx;EC;PPG_Stims"); + put("storeValues", "PC475;PC531;TB;Malaria;AcuteNx;EC;PPG_Stims;IMPAC_TB_Human"); put("delimiter", ";"); }}, null, null, false, false), SeuratToolParameter.create("errorIfUnknownIdsFound", "Error If Unknown Ids Found", "If true, the job will fail if the seurat object contains ID not present in the metadata", "checkbox", null, true) diff --git a/singlecell/src/org/labkey/singlecell/pipeline/singlecell/SubsetSeurat.java b/singlecell/src/org/labkey/singlecell/pipeline/singlecell/SubsetSeurat.java index 7a646eb38..a17c151d8 100644 --- a/singlecell/src/org/labkey/singlecell/pipeline/singlecell/SubsetSeurat.java +++ b/singlecell/src/org/labkey/singlecell/pipeline/singlecell/SubsetSeurat.java @@ -34,7 +34,7 @@ public Provider() put("width", 600); put("delimiter", DELIM); }}, null), - ToolParameterDescriptor.create("useDplyr", "Use dplyr", "If checked, the subset will be executed using dplyr::filter rather than Seurat::subset. This should allow more complex expressions to be used, including negations", "checkbox", null, false) + ToolParameterDescriptor.create("useDplyr", "Use dplyr", "If checked, the subset will be executed using dplyr::filter rather than Seurat::subset. This should allow more complex expressions to be used, including negations", "checkbox", null, true) ), List.of("/sequenceanalysis/field/TrimmingTextArea.js"), null); } diff --git a/singlecell/src/org/labkey/singlecell/pipeline/singlecell/VireoHandler.java b/singlecell/src/org/labkey/singlecell/pipeline/singlecell/VireoHandler.java index d4ddc7326..3976c6155 100644 --- a/singlecell/src/org/labkey/singlecell/pipeline/singlecell/VireoHandler.java +++ b/singlecell/src/org/labkey/singlecell/pipeline/singlecell/VireoHandler.java @@ -72,7 +72,7 @@ public boolean doSplitJobs() @Override public boolean canProcess(SequenceOutputFile o) { - return CellRangerGexCountStep.LOUPE_CATEGORY.equals(o.getCategory()) & o.getFile().getName().endsWith("cloupe.cloupe"); + return CellRangerGexCountStep.LOUPE_CATEGORY.equals(o.getCategory()) & o.getFile() != null & o.getFile().getName().endsWith("cloupe.cloupe"); } @Override diff --git a/singlecell/src/org/labkey/singlecell/run/CellRangerGexCountStep.java b/singlecell/src/org/labkey/singlecell/run/CellRangerGexCountStep.java index 2bfb601e4..87b18cc8d 100644 --- a/singlecell/src/org/labkey/singlecell/run/CellRangerGexCountStep.java +++ b/singlecell/src/org/labkey/singlecell/run/CellRangerGexCountStep.java @@ -5,6 +5,7 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; +import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; import org.json.JSONObject; import org.labkey.api.collections.CaseInsensitiveHashMap; @@ -47,8 +48,10 @@ import org.labkey.api.writer.PrintWriters; import org.labkey.singlecell.SingleCellSchema; +import java.io.BufferedReader; import java.io.File; import java.io.IOException; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -56,12 +59,14 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class CellRangerGexCountStep extends AbstractAlignmentPipelineStep implements AlignmentStep { public static final String LOUPE_CATEGORY = "10x Loupe File"; - public CellRangerGexCountStep(AlignmentStepProvider provider, PipelineContext ctx, CellRangerWrapper wrapper) + public CellRangerGexCountStep(AlignmentStepProvider provider, PipelineContext ctx, CellRangerWrapper wrapper) { super(provider, ctx, wrapper); } @@ -328,7 +333,7 @@ private boolean shouldDiscardBam() return false; } - return !_alwaysRetainBam && getProvider().getParameterByName(AbstractAlignmentStepProvider.DISCARD_BAM).extractValue(getPipelineCtx().getJob(), getProvider(), getStepIdx(), Boolean.class, false); + return !_alwaysRetainBam && getProvider().getParameterByName(AbstractAlignmentStepProvider.DISCARD_BAM).extractValue(getPipelineCtx().getJob(), getProvider(), getStepIdx(), Boolean.class, false); } private boolean _alwaysRetainBam = false; @@ -349,7 +354,7 @@ public AlignmentOutput performAlignment(Readset rs, List inputFastqs1, @Nu AbstractAlignmentStepProvider.ALIGNMENT_MODE mode = AbstractAlignmentStepProvider.ALIGNMENT_MODE.valueOf(alignmentMode); List> inputFastqs = new ArrayList<>(); - for (int i = 0; i < inputFastqs1.size();i++) + for (int i = 0; i < inputFastqs1.size(); i++) { File inputFastq1 = inputFastqs1.get(i); File inputFastq2 = inputFastqs2.get(i); @@ -395,9 +400,9 @@ public AlignmentOutput performAlignment(Readset rs, List inputFastqs1, @Nu File outdir = new File(outputDirectory, id); outdir = new File(outdir, "outs"); + File bam = new File(outdir, "possorted_genome_bam.bam"); if (!shouldDiscardBam()) { - File bam = new File(outdir, "possorted_genome_bam.bam"); if (!bam.exists()) { throw new PipelineJobException("Unable to find file: " + bam.getPath()); @@ -613,4 +618,88 @@ public void complete(SequenceAnalysisJobSupport support, AnalysisModel model, Co } } } + + public enum Chemistry + { + // See: https://kb.10xgenomics.com/s/article/115004506263-What-is-a-barcode-inclusion-list-formerly-barcode-whitelist + // cellranger-x.y.z/lib/python/cellranger/barcodes/ + FivePE_V3("Single Cell 5' PE v3", "3M-5pgex-jan-2023.txt.gz"), + FivePE_V2("Single Cell 5' PE v2", "737k-august-2016.txt"); + + final String _label; + final String _inclusionListFile; + + Chemistry(String label, String inclusionListFile) + { + _label = label; + _inclusionListFile = inclusionListFile; + } + + public File getInclusionListFile(Logger logger) throws PipelineJobException + { + File exe = new CellRangerWrapper(logger).getExe(); + if (Files.isSymbolicLink(exe.toPath())) + { + try + { + exe = Files.readSymbolicLink(exe.toPath()).toFile(); + } + catch (IOException e) + { + throw new PipelineJobException(e); + } + } + + File il = new File(exe.getParentFile(), "lib/python/cellranger/barcodes/" + _inclusionListFile); + if (!il.exists()) + { + throw new PipelineJobException("Unable to find file: " + il.getPath()); + } + + return il; + } + + public static Chemistry getByLabel(String label) + { + for (Chemistry c : Chemistry.values()) + { + if (c._label.equals(label)) + { + return c; + } + } + + throw new IllegalArgumentException("Unknown chemistry: " + label); + } + } + + public static Chemistry inferChemistry(File cloupeFile) throws PipelineJobException + { + File html = new File(cloupeFile.getPath().replaceAll("_cloupe.cloupe$", "_web_summary.html")); + if (!html.exists()) + { + throw new IllegalArgumentException("Missing file: " + html.getPath()); + } + + final Pattern pattern = Pattern.compile("\\[\"Chemistry\",\"(.*?)\"],"); + try (BufferedReader reader = Readers.getReader(html)) + { + String line; + while ((line = reader.readLine()) != null) + { + Matcher m = pattern.matcher(line); + if (m.find()) + { + String chem = m.group(1); + return Chemistry.getByLabel(chem); + } + } + } + catch (IOException e) + { + throw new PipelineJobException(e); + } + + throw new IllegalArgumentException("Unable to infer chemistry for file: " + html.getPath()); + } } diff --git a/singlecell/src/org/labkey/singlecell/run/NimbleAlignmentStep.java b/singlecell/src/org/labkey/singlecell/run/NimbleAlignmentStep.java index 0dcb21bf9..980918b56 100644 --- a/singlecell/src/org/labkey/singlecell/run/NimbleAlignmentStep.java +++ b/singlecell/src/org/labkey/singlecell/run/NimbleAlignmentStep.java @@ -3,7 +3,18 @@ import org.apache.commons.io.FileUtils; import org.jetbrains.annotations.Nullable; import org.json.JSONObject; +import org.labkey.api.data.Container; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.data.Sort; +import org.labkey.api.data.TableInfo; +import org.labkey.api.data.TableSelector; +import org.labkey.api.exp.api.ExpData; +import org.labkey.api.exp.api.ExperimentService; +import org.labkey.api.pipeline.PipelineJob; import org.labkey.api.pipeline.PipelineJobException; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QueryService; +import org.labkey.api.query.UserSchema; import org.labkey.api.sequenceanalysis.model.Readset; import org.labkey.api.sequenceanalysis.pipeline.AbstractAlignmentStepProvider; import org.labkey.api.sequenceanalysis.pipeline.AlignmentOutputImpl; @@ -14,19 +25,22 @@ import org.labkey.api.sequenceanalysis.pipeline.SequenceAnalysisJobSupport; import org.labkey.api.sequenceanalysis.pipeline.ToolParameterDescriptor; import org.labkey.api.util.PageFlowUtil; +import org.labkey.singlecell.SingleCellSchema; import java.io.File; import java.io.IOException; import java.util.Arrays; +import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; public class NimbleAlignmentStep extends AbstractCellRangerDependentStep { public static final String REF_GENOMES = "refGenomes"; public static final String MAX_HITS_TO_REPORT = "maxHitsToReport"; - public static final String ALIGN_OUTPUT = "alignmentOutput"; public static final String STRANDEDNESS = "strandedness"; + public static final String REQUIRE_CACHED_BARCODES = "requireCachedBarcodes"; public NimbleAlignmentStep(AlignmentStepProvider provider, PipelineContext ctx, CellRangerWrapper wrapper) { @@ -37,7 +51,7 @@ public static class Provider extends AbstractAlignmentStepProvider(PageFlowUtil.set("sequenceanalysis/field/GenomeField.js", "singlecell/panel/NimbleAlignPanel.js")), null, true, false, ALIGNMENT_MODE.MERGE_THEN_ALIGN); + super("Nimble", "This will run Nimble to generate a supplemental scRNA-seq feature count matrix for the provided libraries", getCellRangerGexParams(getToolParameters()), new LinkedHashSet<>(PageFlowUtil.set("sequenceanalysis/field/GenomeField.js", "singlecell/panel/NimbleAlignPanel.js")), null, true, false, ALIGNMENT_MODE.MERGE_THEN_ALIGN); } @Override @@ -59,7 +73,10 @@ public static List getToolParameters() }}, null), ToolParameterDescriptor.create(MAX_HITS_TO_REPORT, "Max Hits To Report", "If a given hit has more than this number of references, it is discarded", "ldk-integerfield", new JSONObject(){{ put("minValue", 0); - }}, 4) + }}, 4), + ToolParameterDescriptor.create(REQUIRE_CACHED_BARCODES, "Fail Unless Cached Barcodes Present", "If checked, the pipeline will expect a previously computed map of cellbarcodes and UMIs to be computed. Under default conditions, if this is missing, cellranger will be re-run. This flag can be helpful to avoid that computation if you expect the barcode file to exist.", "checkbox", new JSONObject(){{ + + }}, false) ); } @@ -68,6 +85,84 @@ public AlignmentOutput performAlignment(Readset rs, List inputFastqs1, @Nu { AlignmentOutputImpl output = new AlignmentOutputImpl(); + boolean throwIfNotFound = getProvider().getParameterByName(REQUIRE_CACHED_BARCODES).extractValue(getPipelineCtx().getJob(), getProvider(), getStepIdx(), Boolean.class, false); + File loupeFile = getCachedLoupeFile(rs, throwIfNotFound); + + File localBam; + if (loupeFile == null) + { + localBam = performCellRangerAlignment(output, rs, inputFastqs1, inputFastqs2, outputDirectory, referenceGenome, basename, readGroupId, platformUnit); + } + else + { + localBam = createNimbleBam(output, rs, inputFastqs1, inputFastqs2); + } + + + // Now run nimble itself: + NimbleHelper helper = new NimbleHelper(getPipelineCtx(), getProvider(), getStepIdx()); + helper.doNimbleAlign(localBam, output, rs, basename); + output.setBAM(localBam); + + return output; + } + + private File createNimbleBam(AlignmentOutputImpl output, Readset rs, List inputFastqs1, List inputFastqs2) throws PipelineJobException + { + File loupeFile = getCachedLoupeFile(rs, true); + + return NimbleHelper.runFastqToBam(output, getPipelineCtx(), rs, inputFastqs1, inputFastqs2, loupeFile); + } + + private File getCachedLoupeFile(Readset rs, boolean throwIfNotFound) throws PipelineJobException + { + Map map = getPipelineCtx().getSequenceSupport().getCachedObject(CACHE_KEY, PipelineJob.createObjectMapper().getTypeFactory().constructParametricType(Map.class, Long.class, Long.class)); + Long dataId = map.get(rs.getReadsetId()); + if (dataId == null) + { + if (throwIfNotFound) + { + throw new PipelineJobException("No cached data found for readset: " + rs.getReadsetId()); + } + + return null; + } + + File ret = getPipelineCtx().getSequenceSupport().getCachedData(dataId); + if (ret == null || ! ret.exists()) + { + throw new PipelineJobException("Missing cached cellbarcode/UMI file: " + dataId); + } + + return ret; + } + + private ExpData findLoupeFile(Readset rs) throws PipelineJobException + { + Container targetContainer = getPipelineCtx().getJob().getContainer().isWorkbookOrTab() ? getPipelineCtx().getJob().getContainer().getParent() : getPipelineCtx().getJob().getContainer(); + UserSchema us = QueryService.get().getUserSchema(getPipelineCtx().getJob().getUser(), targetContainer, SingleCellSchema.SEQUENCE_SCHEMA_NAME); + TableInfo ti = us.getTable("outputfiles"); + + SimpleFilter sf = new SimpleFilter(FieldKey.fromString("readset"), rs.getRowId()); + sf.addCondition(FieldKey.fromString("category"), CellRangerGexCountStep.LOUPE_CATEGORY); + List cbs = new TableSelector(ti, PageFlowUtil.set("dataid"), sf, new Sort("-rowid")).getArrayList(Integer.class); + if (!cbs.isEmpty()) + { + int dataId = cbs.get(0); + ExpData d = ExperimentService.get().getExpData(dataId); + if (d == null || d.getFile() == null) + { + throw new PipelineJobException("Output lacks a file: " + dataId); + } + + return d; + } + + return null; + } + + private File performCellRangerAlignment(AlignmentOutputImpl output, Readset rs, List inputFastqs1, @Nullable List inputFastqs2, File outputDirectory, ReferenceGenome referenceGenome, String basename, String readGroupId, @Nullable String platformUnit) throws PipelineJobException + { // We need to ensure we keep the BAM for post-processing: setAlwaysRetainBam(true); @@ -87,14 +182,7 @@ public AlignmentOutput performAlignment(Readset rs, List inputFastqs1, @Nu } } - NimbleHelper.write10xBarcodes(localBam, getWrapper().getLogger(), rs, referenceGenome, output); - - // Now run nimble itself: - NimbleHelper helper = new NimbleHelper(getPipelineCtx(), getProvider(), getStepIdx()); - helper.doNimbleAlign(localBam, output, rs, basename); - output.setBAM(localBam); - - return output; + return localBam; } @Override @@ -109,5 +197,21 @@ public void init(SequenceAnalysisJobSupport support) throws PipelineJobException { helper.prepareGenome(id); } + + // Try to find 10x barcodes: + HashMap readsetToLoupe = new HashMap<>(); + for (Readset rs : support.getCachedReadsets()) + { + ExpData f = findLoupeFile(rs); + if (f != null) + { + support.cacheExpData(f); + readsetToLoupe.put(rs.getReadsetId(), f.getRowId()); + } + } + + support.cacheObject(CACHE_KEY, readsetToLoupe); } + + private static final String CACHE_KEY = "nimble.loupe"; } diff --git a/singlecell/src/org/labkey/singlecell/run/NimbleAnalysis.java b/singlecell/src/org/labkey/singlecell/run/NimbleAnalysis.java index 8fa4390b7..b24d546b2 100644 --- a/singlecell/src/org/labkey/singlecell/run/NimbleAnalysis.java +++ b/singlecell/src/org/labkey/singlecell/run/NimbleAnalysis.java @@ -29,7 +29,7 @@ public static class Provider extends AbstractAnalysisStepProvider(PageFlowUtil.set("sequenceanalysis/field/GenomeField.js", "singlecell/panel/NimbleAlignPanel.js")), null); + super("NimbleAnalysis", "Nimble", null, "This will run Nimble to generate a supplemental feature count matrix for the provided libraries. This should work using either CellRanger/scRNA-seq or bulk input data.", NimbleAlignmentStep.getToolParameters(), new LinkedHashSet<>(PageFlowUtil.set("sequenceanalysis/field/GenomeField.js", "singlecell/panel/NimbleAlignPanel.js")), null); } @Override @@ -58,8 +58,6 @@ public Output performAnalysisPerSampleRemote(Readset rs, File inputBam, Referenc NimbleHelper helper = new NimbleHelper(getPipelineCtx(), getProvider(), getStepIdx()); helper.doNimbleAlign(inputBam, output, rs, FileUtil.getBaseName(inputBam)); - NimbleHelper.write10xBarcodes(inputBam, getPipelineCtx().getLogger(), rs, referenceGenome, output); - return output; } diff --git a/singlecell/src/org/labkey/singlecell/run/NimbleBulkAlignmentStep.java b/singlecell/src/org/labkey/singlecell/run/NimbleBulkAlignmentStep.java new file mode 100644 index 000000000..4311d3e8b --- /dev/null +++ b/singlecell/src/org/labkey/singlecell/run/NimbleBulkAlignmentStep.java @@ -0,0 +1,184 @@ +package org.labkey.singlecell.run; + +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.Nullable; +import org.labkey.api.pipeline.PipelineJobException; +import org.labkey.api.sequenceanalysis.model.Readset; +import org.labkey.api.sequenceanalysis.pipeline.AbstractAlignmentStepProvider; +import org.labkey.api.sequenceanalysis.pipeline.AlignmentOutputImpl; +import org.labkey.api.sequenceanalysis.pipeline.AlignmentStep; +import org.labkey.api.sequenceanalysis.pipeline.AlignmentStepProvider; +import org.labkey.api.sequenceanalysis.pipeline.IndexOutputImpl; +import org.labkey.api.sequenceanalysis.pipeline.PipelineContext; +import org.labkey.api.sequenceanalysis.pipeline.ReferenceGenome; +import org.labkey.api.sequenceanalysis.pipeline.SamtoolsRunner; +import org.labkey.api.sequenceanalysis.pipeline.SequenceAnalysisJobSupport; +import org.labkey.api.sequenceanalysis.pipeline.SequencePipelineService; +import org.labkey.api.sequenceanalysis.run.AbstractAlignmentPipelineStep; +import org.labkey.api.sequenceanalysis.run.AbstractCommandWrapper; +import org.labkey.api.util.FileUtil; +import org.labkey.api.util.PageFlowUtil; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.List; + +public class NimbleBulkAlignmentStep extends AbstractAlignmentPipelineStep implements AlignmentStep +{ + public static class Provider extends AbstractAlignmentStepProvider + { + public Provider() + { + super("Nimble-Bulk", + "This will run Nimble to generate a supplemental feature count matrix for the provided libraries. This version is intended for bulk input data. Please use the CellRanger/Nimble version for scRNA-seq", + NimbleAlignmentStep.getToolParameters(), + new LinkedHashSet<>(PageFlowUtil.set("sequenceanalysis/field/GenomeField.js", "singlecell/panel/NimbleAlignPanel.js")), + null, + true, false, ALIGNMENT_MODE.MERGE_THEN_ALIGN); + } + + @Override + public NimbleBulkAlignmentStep create(PipelineContext ctx) + { + return new NimbleBulkAlignmentStep(this, ctx, new NimbleBulkWrapper(ctx.getLogger())); + } + } + + public NimbleBulkAlignmentStep(AlignmentStepProvider provider, PipelineContext ctx, NimbleBulkAlignmentStep.NimbleBulkWrapper wrapper) + { + super(provider, ctx, wrapper); + } + + @Override + public IndexOutput createIndex(ReferenceGenome referenceGenome, File outputDir) throws PipelineJobException + { + return new IndexOutputImpl(referenceGenome); + } + + @Override + public void init(SequenceAnalysisJobSupport support) throws PipelineJobException + { + NimbleHelper helper = new NimbleHelper(getPipelineCtx(), getProvider(), getStepIdx()); + + List genomeIds = helper.getGenomeIds(); + for (int id : genomeIds) + { + helper.prepareGenome(id); + } + } + + @Override + public AlignmentOutput performAlignment(Readset rs, List inputFastqs1, @Nullable List inputFastqs2, File outputDirectory, ReferenceGenome referenceGenome, String basename, String readGroupId, @Nullable String platformUnit) throws PipelineJobException + { + AlignmentOutputImpl output = new AlignmentOutputImpl(); + SamtoolsRunner st = new SamtoolsRunner(getPipelineCtx().getLogger()); + + List outputBams = new ArrayList<>(); + int bamIdx = 0; + while (bamIdx < inputFastqs1.size()) + { + File outputBam = new File(getPipelineCtx().getWorkingDirectory(), FileUtil.makeLegalName(rs.getName()) + ".unmapped." + bamIdx + ".bam"); + List args = new ArrayList<>(Arrays.asList(st.getSamtoolsPath().getPath(), "import", "-o", outputBam.getPath())); + args.add("-r"); + args.add("ID:" + readGroupId); + + args.add("-r"); + args.add("LB:" + rs.getReadsetId().toString()); + + args.add("-r"); + args.add("PL:" + (rs.getPlatform() == null ? "ILLUMINA" : rs.getPlatform())); + + args.add("-r"); + args.add("PU:" + (platformUnit == null ? rs.getReadsetId().toString() : platformUnit)); + + args.add("-r"); + args.add("SM:" + rs.getName().replaceAll(" ", "_")); + + if (inputFastqs2 == null || inputFastqs2.isEmpty()) + { + args.add("-O"); + args.add(inputFastqs1.get(bamIdx).getPath()); + } + else + { + args.add("-1"); + args.add(inputFastqs1.get(bamIdx).getPath()); + + if (bamIdx > inputFastqs2.size()) + { + throw new PipelineJobException("Unequal lengths for first/second pair FASTQs"); + } + + args.add("-2"); + args.add(inputFastqs2.get(bamIdx).getPath()); + } + bamIdx++; + + st.execute(args); + outputBams.add(outputBam); + } + + File outputBam; + if (outputBams.size() > 1) + { + outputBam = new File(getPipelineCtx().getWorkingDirectory(), FileUtil.makeLegalName(rs.getName()) + ".unmapped.bam"); + outputBams.forEach(output::addIntermediateFile); + + List args = new ArrayList<>(Arrays.asList(st.getSamtoolsPath().getPath(), "merge", "-o", outputBam.getPath(), "-f")); + Integer maxThreads = SequencePipelineService.get().getMaxThreads(getPipelineCtx().getLogger()); + if (maxThreads != null) + { + args.add("-@"); + args.add(maxThreads.toString()); + } + + outputBams.forEach(bam -> args.add(bam.getPath())); + st.execute(args); + } + else + { + outputBam = outputBams.get(0); + } + + // Now run nimble itself: + NimbleHelper helper = new NimbleHelper(getPipelineCtx(), getProvider(), getStepIdx()); + helper.doNimbleAlign(outputBam, output, rs, basename); + output.setBAM(outputBam); + + return output; + } + + @Override + public boolean doAddReadGroups() + { + return false; + } + + @Override + public boolean doSortIndexBam() + { + return false; + } + + @Override + public boolean alwaysCopyIndexToWorkingDir() + { + return false; + } + + @Override + public boolean supportsGzipFastqs() + { + return true; + } + + public static class NimbleBulkWrapper extends AbstractCommandWrapper + { + public NimbleBulkWrapper(Logger log) + { + super(log); + } + } +} diff --git a/singlecell/src/org/labkey/singlecell/run/NimbleHelper.java b/singlecell/src/org/labkey/singlecell/run/NimbleHelper.java index 6afdbe366..7e30115b5 100644 --- a/singlecell/src/org/labkey/singlecell/run/NimbleHelper.java +++ b/singlecell/src/org/labkey/singlecell/run/NimbleHelper.java @@ -27,9 +27,11 @@ import org.labkey.api.sequenceanalysis.pipeline.PipelineStepOutput; import org.labkey.api.sequenceanalysis.pipeline.PipelineStepProvider; import org.labkey.api.sequenceanalysis.pipeline.ReferenceGenome; +import org.labkey.api.sequenceanalysis.pipeline.SamtoolsRunner; import org.labkey.api.sequenceanalysis.pipeline.SequencePipelineService; import org.labkey.api.sequenceanalysis.run.DISCVRSeqRunner; import org.labkey.api.sequenceanalysis.run.DockerWrapper; +import org.labkey.api.util.FileUtil; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.writer.PrintWriters; @@ -496,26 +498,23 @@ private Map doAlignment(List genomes, List barcodeArgs = new ArrayList<>(runner.getBaseArgs("Save10xBarcodes")); barcodeArgs.add("-I"); barcodeArgs.add(bam.getPath()); - File cbOutput = new File(bam.getParentFile(), SequenceAnalysisService.get().getUnzippedBaseName(bam.getName()) + "cb.txt.gz"); - barcodeArgs.add("--cbOutput"); - barcodeArgs.add(cbOutput.getPath()); - - File umiOutput = new File(bam.getParentFile(), SequenceAnalysisService.get().getUnzippedBaseName(bam.getName()) + "umi.txt.gz"); - barcodeArgs.add("--umiOutput"); - barcodeArgs.add(umiOutput.getPath()); + File bcOutput = new File(bam.getParentFile(), SequenceAnalysisService.get().getUnzippedBaseName(bam.getName()) + ".cb.txt.gz"); + barcodeArgs.add("--output"); + barcodeArgs.add(bcOutput.getPath()); runner.execute(barcodeArgs); - output.addSequenceOutput(cbOutput, "10x CellBarcode Map: " + rs.getName(), "10x CellBarcode Map", rs.getReadsetId(), null, referenceGenome.getGenomeId(), null); - output.addSequenceOutput(umiOutput, "10x UMI Map: " + rs.getName(), "10x UMI Map", rs.getReadsetId(), null, referenceGenome.getGenomeId(), null); + output.addSequenceOutput(bcOutput, "10x CellBarcode Map: " + rs.getName(), CATEGORY_CB, rs.getReadsetId(), null, referenceGenome.getGenomeId(), null); } public static File runNimbleReport(File alignResultsGz, int genomeId, PipelineStepOutput output, PipelineContext ctx) throws PipelineJobException @@ -595,6 +594,76 @@ private static File getNimbleDoneFile(File parentDir, String resumeString) return new File(parentDir, "nimble." + resumeString + ".done"); } + public static File runFastqToBam(PipelineStepOutput output, PipelineContext ctx, Readset rs, List inputFastqs1, List inputFastqs2, File loupeFile) throws PipelineJobException + { + List outputBams = new ArrayList<>(); + int bamIdx = 0; + while (bamIdx < inputFastqs1.size()) + { + File outputBam = new File(ctx.getWorkingDirectory(), FileUtil.makeLegalName(rs.getName()) + ".unmapped." + bamIdx + ".bam"); + + List args = new ArrayList<>(); + args.add("python3"); + args.add("-m"); + args.add("nimble"); + + args.add("fastq-to-bam"); + + Integer maxThreads = SequencePipelineService.get().getMaxThreads(ctx.getLogger()); + if (maxThreads != null) + { + args.add("-c"); + args.add(maxThreads.toString()); + } + + args.add("--r1-fastq"); + args.add(inputFastqs1.get(bamIdx).getPath()); + if (bamIdx > inputFastqs2.size()) + { + throw new PipelineJobException("Unequal lengths for first/second pair FASTQs"); + } + + args.add("--r2-fastq"); + args.add(inputFastqs2.get(bamIdx).getPath()); + + args.add("--map"); + CellRangerGexCountStep.Chemistry chem = CellRangerGexCountStep.inferChemistry(loupeFile); + args.add(chem.getInclusionListFile(ctx.getLogger()).getPath()); + + args.add("--output"); + args.add(outputBam.getPath()); + + runUsingDocker(args, output, "nimble.fastq-to-bam." + bamIdx, ctx); + outputBams.add(outputBam); + bamIdx++; + } + + File outputBam; + if (outputBams.size() > 1) + { + outputBam = new File(ctx.getWorkingDirectory(), FileUtil.makeLegalName(rs.getName()) + ".unmapped.bam"); + outputBams.forEach(output::addIntermediateFile); + + SamtoolsRunner st = new SamtoolsRunner(ctx.getLogger()); + List args = new ArrayList<>(Arrays.asList(st.getSamtoolsPath().getPath(), "merge", "-o", outputBam.getPath(), "-f")); + Integer maxThreads = SequencePipelineService.get().getMaxThreads(ctx.getLogger()); + if (maxThreads != null) + { + args.add("-@"); + args.add(maxThreads.toString()); + } + + outputBams.forEach(bam -> args.add(bam.getPath())); + st.execute(args); + } + else + { + outputBam = outputBams.get(0); + } + + return outputBam; + } + public static String DOCKER_CONTAINER_NAME = "ghcr.io/bimberlab/nimble:latest"; private boolean runUsingDocker(List nimbleArgs, PipelineStepOutput output, @Nullable String resumeString) throws PipelineJobException