loom-browser 0.0.15 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/loom.esm.js CHANGED
@@ -22998,60 +22998,62 @@ function createFallbackProvider(primary, fallback) {
22998
22998
  /**
22999
22999
  * Resolve a sequence provider from config.
23000
23000
  *
23001
- * Exactly one sequence source must be set. Throws if multiple are provided.
23002
- * Sources: sequenceProvider, twoBitURL, fastaURL, ucscGenome.
23003
- * When none is set, falls back to UCSC API via `id` if available.
23001
+ * If a custom `sequenceProvider` is set, it is used directly. Otherwise,
23002
+ * all available sources (twoBitURL, fastaURL, ucscGenome/id) are chained
23003
+ * as a prioritized fallback list:
23004
23004
  *
23005
- * When twoBitURL is used and a genome ID is available, the UCSC REST API is
23006
- * automatically added as a fallback if the 2-bit server is unreachable,
23007
- * the API is tried instead.
23005
+ * 1. twoBitURL most efficient (binary range requests)
23006
+ * 2. fastaURL — reliable CDN fallback (igv.org hosts FASTA for common genomes)
23007
+ * 3. UCSC API last resort (api.genome.ucsc.edu REST endpoint)
23008
+ *
23009
+ * This mirrors igv.js genomes3.json which provides both twoBitURL and fastaURL
23010
+ * for common genomes. On networks where one server is unreachable (e.g.,
23011
+ * hgdownload.soe.ucsc.edu blocked but igv.org reachable), the chain
23012
+ * automatically falls through to the next available source.
23008
23013
  */
23009
23014
  function resolveSequenceProvider(config) {
23010
- var _a, _b;
23011
- const sources = [
23012
- config.sequenceProvider && 'sequenceProvider',
23013
- config.twoBitURL && 'twoBitURL',
23014
- config.fastaURL && 'fastaURL',
23015
- config.ucscGenome && 'ucscGenome',
23016
- ].filter(Boolean);
23017
- if (sources.length > 1) {
23018
- throw new Error(`GenomeConfig has multiple sequence sources: ${sources.join(', ')}. ` +
23019
- 'Set exactly one of: sequenceProvider, twoBitURL, fastaURL, or ucscGenome.');
23020
- }
23015
+ var _a;
23016
+ // Custom provider — use directly, no chaining
23021
23017
  if (config.sequenceProvider) {
23022
23018
  return config.sequenceProvider;
23023
23019
  }
23020
+ // Build prioritized provider list from available config fields
23021
+ const providers = [];
23024
23022
  if (config.twoBitURL) {
23025
- const primary = createTwoBitSequenceProvider(config.twoBitURL, config.fetchImpl);
23026
- // Add UCSC REST API fallback when a genome ID is known
23027
- const genomeId = (_a = config.ucscGenome) !== null && _a !== void 0 ? _a : config.id;
23028
- if (genomeId) {
23029
- const fallback = (locus, signal) => fetchSequence(locus, { genome: genomeId }, signal);
23030
- return createCachedSequence(createFallbackProvider(primary, fallback));
23031
- }
23032
- return createCachedSequence(primary);
23023
+ providers.push(createTwoBitSequenceProvider(config.twoBitURL, config.fetchImpl));
23033
23024
  }
23034
23025
  if (config.fastaURL) {
23035
- return createCachedSequence(createFastaSequenceProvider(config.fastaURL, config.indexURL, config.compressedIndexURL, config.fetchImpl));
23026
+ providers.push(createFastaSequenceProvider(config.fastaURL, config.indexURL, config.compressedIndexURL, config.fetchImpl));
23036
23027
  }
23037
- // UCSC API explicit ucscGenome, or implicit via id
23038
- const ucscGenome = (_b = config.ucscGenome) !== null && _b !== void 0 ? _b : config.id;
23028
+ const ucscGenome = (_a = config.ucscGenome) !== null && _a !== void 0 ? _a : config.id;
23039
23029
  if (ucscGenome) {
23040
- return createCachedSequence((locus, signal) => fetchSequence(locus, { genome: ucscGenome }, signal));
23030
+ providers.push((locus, signal) => fetchSequence(locus, { genome: ucscGenome }, signal));
23041
23031
  }
23042
- return undefined;
23032
+ if (providers.length === 0)
23033
+ return undefined;
23034
+ if (providers.length === 1)
23035
+ return createCachedSequence(providers[0]);
23036
+ // Chain all providers: try each in order, fall back on failure
23037
+ const chained = providers.reduceRight((fallback, primary) => createFallbackProvider(primary, fallback));
23038
+ return createCachedSequence(chained);
23043
23039
  }
23044
23040
  // ─── Pre-built singletons ��─────────��─────────────────────────────────────────
23045
23041
  /**
23046
- * Pre-built hg38 genome with 2-bit sequence (UCSC API fallback).
23042
+ * Pre-built hg38 genome with triple-fallback sequence chain.
23047
23043
  *
23048
23044
  * This is the default genome used by HeadlessGenomeBrowser when no genome
23049
- * is specified. Matches igv.js's default behavior: 2-bit file as primary
23050
- * sequence source, with UCSC REST API as automatic fallback for resilience.
23045
+ * is specified. Sequence sources are tried in order:
23046
+ * 1. 2-bit from hgdownload.soe.ucsc.edu (most efficient, binary range requests)
23047
+ * 2. Indexed FASTA from igv.org (reliable CDN, reachable on restricted networks)
23048
+ * 3. UCSC REST API from api.genome.ucsc.edu (last resort)
23049
+ *
23050
+ * URLs match igv.js genomes3.json configuration for hg38.
23051
23051
  */
23052
23052
  const hg38Genome = createGenomeSync({
23053
23053
  id: 'hg38',
23054
23054
  twoBitURL: 'https://hgdownload.soe.ucsc.edu/goldenPath/hg38/bigZips/hg38.2bit',
23055
+ fastaURL: 'https://igv.org/genomes/data/hg38/hg38.fa',
23056
+ indexURL: 'https://igv.org/genomes/data/hg38/hg38.fa.fai',
23055
23057
  chromSizes: hg38ChromSizes,
23056
23058
  });
23057
23059
 
@@ -29889,6 +29891,14 @@ class HeadlessGenomeBrowser {
29889
29891
  }
29890
29892
  return matches.length;
29891
29893
  }
29894
+ /** Merge partial render config into all tracks matching a selector. Returns the number of tracks updated. */
29895
+ updateTrackConfig(selector, config) {
29896
+ const matches = selectTracks(this.managedTracks, selector);
29897
+ for (const mt of matches) {
29898
+ mt.track.setConfig(config);
29899
+ }
29900
+ return matches.length;
29901
+ }
29892
29902
  // ─── Navigation ──────────────────────────────────────────────────────────
29893
29903
  /** Update the locus and re-render all tracks. Clamped to chromosome bounds. Operates on primary frame. */
29894
29904
  setLocus(locus) {
@@ -31005,6 +31015,11 @@ class CommandDispatcher {
31005
31015
  results.push({ action: 'update_metadata', success: count > 0, count });
31006
31016
  break;
31007
31017
  }
31018
+ case 'update_config': {
31019
+ const count = this.browser.updateTrackConfig(toTrackSelector(action.selector), action.config);
31020
+ results.push({ action: 'update_config', success: count > 0, count });
31021
+ break;
31022
+ }
31008
31023
  default:
31009
31024
  results.push({
31010
31025
  action: action.action,