loom-browser 0.0.13 → 0.0.14

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.js CHANGED
@@ -3557,14 +3557,14 @@
3557
3557
  // ─── Internal helpers ─────────────────────────────────────────────────────────
3558
3558
  // Cache BigWig instances by URL to reuse parsed headers and trees.
3559
3559
  // Auth-aware readers share the cache key (URL) — the fetchImpl is bound at creation.
3560
- const readerCache$4 = new Map();
3560
+ const readerCache$5 = new Map();
3561
3561
  function getGmodReader(url, fetchImpl) {
3562
3562
  const resolvedUrl = normalizeGoogleURL(url);
3563
- let reader = readerCache$4.get(resolvedUrl);
3563
+ let reader = readerCache$5.get(resolvedUrl);
3564
3564
  if (!reader) {
3565
3565
  const fileOpts = fetchImpl ? { fetch: fetchImpl } : undefined;
3566
3566
  reader = new BigWig({ filehandle: new RemoteFile(resolvedUrl, fileOpts) });
3567
- readerCache$4.set(resolvedUrl, reader);
3567
+ readerCache$5.set(resolvedUrl, reader);
3568
3568
  }
3569
3569
  return reader;
3570
3570
  }
@@ -3685,7 +3685,7 @@
3685
3685
  // Evict the cached reader so the next fetch gets a fresh one.
3686
3686
  // @gmod/bbi caches the header promise; an aborted parse poisons it permanently.
3687
3687
  if (err instanceof Error && (err.name === 'AbortError' || ((_b = err.message) === null || _b === void 0 ? void 0 : _b.includes('aborted')))) {
3688
- readerCache$4.delete(url);
3688
+ readerCache$5.delete(url);
3689
3689
  }
3690
3690
  throw err;
3691
3691
  }
@@ -3701,7 +3701,7 @@
3701
3701
  }
3702
3702
  /** Clear the BigWig reader cache (useful for testing or memory management). */
3703
3703
  function clearBigWigCache() {
3704
- readerCache$4.clear();
3704
+ readerCache$5.clear();
3705
3705
  }
3706
3706
 
3707
3707
  /**
@@ -5046,12 +5046,12 @@
5046
5046
  * Layer 1 (Data + Layout): no DOM, no canvas.
5047
5047
  */
5048
5048
  // Cache readers by URL to reuse parsed headers, dictionaries, and indexes
5049
- const readerCache$3 = new Map();
5050
- function getReader$3(url, fetchImpl) {
5051
- let reader = readerCache$3.get(url);
5049
+ const readerCache$4 = new Map();
5050
+ function getReader$4(url, fetchImpl) {
5051
+ let reader = readerCache$4.get(url);
5052
5052
  if (!reader) {
5053
5053
  reader = new GtxReader(url, fetchImpl);
5054
- readerCache$3.set(url, reader);
5054
+ readerCache$4.set(url, reader);
5055
5055
  }
5056
5056
  return reader;
5057
5057
  }
@@ -5060,7 +5060,7 @@
5060
5060
  */
5061
5061
  async function fetchGtxFeatures(url, experimentId, locus, options = {}) {
5062
5062
  var _a;
5063
- const reader = getReader$3(url, options.fetchImpl);
5063
+ const reader = getReader$4(url, options.fetchImpl);
5064
5064
  await reader.init(options.signal);
5065
5065
  const expIdx = reader.resolveExperimentIndex(experimentId);
5066
5066
  if (expIdx === undefined) {
@@ -5074,7 +5074,7 @@
5074
5074
  * Returns features with per-chromosome coordinates (caller transforms to genome-wide).
5075
5075
  */
5076
5076
  async function fetchGtxWGFeatures(url, experimentId, chromNames, bpPerPixel, options = {}) {
5077
- const reader = getReader$3(url, options.fetchImpl);
5077
+ const reader = getReader$4(url, options.fetchImpl);
5078
5078
  await reader.init(options.signal);
5079
5079
  const expIdx = reader.resolveExperimentIndex(experimentId);
5080
5080
  if (expIdx === undefined) {
@@ -5103,7 +5103,7 @@
5103
5103
  */
5104
5104
  async function fetchGtxMultiFeatures(url, experimentIds, locus, options = {}) {
5105
5105
  var _a;
5106
- const reader = getReader$3(url, options.fetchImpl);
5106
+ const reader = getReader$4(url, options.fetchImpl);
5107
5107
  await reader.init(options.signal);
5108
5108
  const idToIdx = new Map();
5109
5109
  const indexes = [];
@@ -5125,10 +5125,10 @@
5125
5125
  }
5126
5126
  /** Clear the GTX reader cache (useful for testing or memory management). */
5127
5127
  function clearGtxCache() {
5128
- for (const reader of readerCache$3.values()) {
5128
+ for (const reader of readerCache$4.values()) {
5129
5129
  reader.dispose();
5130
5130
  }
5131
- readerCache$3.clear();
5131
+ readerCache$4.clear();
5132
5132
  clearCoordinators();
5133
5133
  }
5134
5134
 
@@ -5150,12 +5150,12 @@
5150
5150
  * Layer 1 (Data + Layout): no DOM, no canvas.
5151
5151
  */
5152
5152
  // Cache readers by URL (shared with gtx/index.ts reader cache)
5153
- const readerCache$2 = new Map();
5154
- function getReader$2(url, fetchImpl) {
5155
- let reader = readerCache$2.get(url);
5153
+ const readerCache$3 = new Map();
5154
+ function getReader$3(url, fetchImpl) {
5155
+ let reader = readerCache$3.get(url);
5156
5156
  if (!reader) {
5157
5157
  reader = new GtxReader(url, fetchImpl);
5158
- readerCache$2.set(url, reader);
5158
+ readerCache$3.set(url, reader);
5159
5159
  }
5160
5160
  return reader;
5161
5161
  }
@@ -5165,7 +5165,7 @@
5165
5165
  this.experimentId = experimentId;
5166
5166
  this._windowFunction = windowFunction;
5167
5167
  this.fetchImpl = fetchImpl;
5168
- this.reader = getReader$2(url, fetchImpl);
5168
+ this.reader = getReader$3(url, fetchImpl);
5169
5169
  }
5170
5170
  get windowFunction() { return this._windowFunction; }
5171
5171
  /** Update the window function for future fetches. */
@@ -15399,6 +15399,12 @@
15399
15399
  }
15400
15400
  /** BigWig/BigBed format set — these use BWSource in igv.js. */
15401
15401
  const bbFormats = new Set(['bigwig', 'bw', 'bigbed', 'bb', 'biginteract', 'biggenepred', 'bignarrowpeak']);
15402
+ /** BigBed-specific formats (annotation data, not quantitative). */
15403
+ const bigBedFormats = new Set(['bigbed', 'bb', 'biginteract', 'biggenepred', 'bignarrowpeak']);
15404
+ /** Check if a format string represents a BigBed (annotation) format vs BigWig (quantitative). */
15405
+ function isBigBedFormat(format) {
15406
+ return bigBedFormats.has(format.toLowerCase());
15407
+ }
15402
15408
  /**
15403
15409
  * Check if a URL is likely tabix-indexed.
15404
15410
  *
@@ -22081,15 +22087,15 @@
22081
22087
  */
22082
22088
  // ─── Internal helpers ─────────────────────────────────────────────────────────
22083
22089
  // Cache TwoBitFile instances by URL to reuse parsed headers and indices.
22084
- const readerCache$1 = new Map();
22090
+ const readerCache$2 = new Map();
22085
22091
  // Parallel filehandle cache for direct batched reads (bypasses @gmod/twobit).
22086
22092
  const filehandleCache = new Map();
22087
- function getReader$1(url, fetchImpl) {
22088
- let reader = readerCache$1.get(url);
22093
+ function getReader$2(url, fetchImpl) {
22094
+ let reader = readerCache$2.get(url);
22089
22095
  if (!reader) {
22090
22096
  const filehandle = getFilehandle(url, fetchImpl);
22091
22097
  reader = new TwoBitFile({ filehandle });
22092
- readerCache$1.set(url, reader);
22098
+ readerCache$2.set(url, reader);
22093
22099
  }
22094
22100
  return reader;
22095
22101
  }
@@ -22153,7 +22159,7 @@
22153
22159
  * instead of 100K+.
22154
22160
  */
22155
22161
  async function fetchChromSizesBatched(url, fetchImpl) {
22156
- const reader = getReader$1(url, fetchImpl);
22162
+ const reader = getReader$2(url, fetchImpl);
22157
22163
  const fh = getFilehandle(url, fetchImpl);
22158
22164
  // Step 1: get the index (name → file offset). Cached after first call.
22159
22165
  const index = await reader.getIndex();
@@ -22221,14 +22227,14 @@
22221
22227
  function createTwoBitSequenceProvider(url, fetchImpl) {
22222
22228
  return async (locus) => {
22223
22229
  var _a;
22224
- const reader = getReader$1(url, fetchImpl);
22230
+ const reader = getReader$2(url, fetchImpl);
22225
22231
  try {
22226
22232
  const sequence = await reader.getSequence(locus.chr, locus.start, locus.end);
22227
22233
  return sequence !== null && sequence !== void 0 ? sequence : '';
22228
22234
  }
22229
22235
  catch (err) {
22230
22236
  if (err instanceof Error && (err.name === 'AbortError' || ((_a = err.message) === null || _a === void 0 ? void 0 : _a.includes('aborted')))) {
22231
- readerCache$1.delete(url);
22237
+ readerCache$2.delete(url);
22232
22238
  filehandleCache.delete(url);
22233
22239
  }
22234
22240
  throw err;
@@ -22256,12 +22262,12 @@
22256
22262
  * individual sequence records.
22257
22263
  */
22258
22264
  async function fetchTwoBitSequenceNames(url, fetchImpl) {
22259
- const reader = getReader$1(url, fetchImpl);
22265
+ const reader = getReader$2(url, fetchImpl);
22260
22266
  return reader.getSequenceNames();
22261
22267
  }
22262
22268
  /** Clear the TwoBit reader cache (useful for testing or memory management). */
22263
22269
  function clearTwoBitCache() {
22264
- readerCache$1.clear();
22270
+ readerCache$2.clear();
22265
22271
  filehandleCache.clear();
22266
22272
  }
22267
22273
 
@@ -22465,10 +22471,10 @@
22465
22471
  */
22466
22472
  // ─── Internal helpers ─────────────────────────────────────────────────────────
22467
22473
  // Cache reader instances by URL to reuse parsed indices.
22468
- const readerCache = new Map();
22469
- function getReader(fastaURL, indexURL, compressedIndexURL, fetchImpl) {
22474
+ const readerCache$1 = new Map();
22475
+ function getReader$1(fastaURL, indexURL, compressedIndexURL, fetchImpl) {
22470
22476
  const cacheKey = fastaURL;
22471
- let reader = readerCache.get(cacheKey);
22477
+ let reader = readerCache$1.get(cacheKey);
22472
22478
  if (!reader) {
22473
22479
  const fileOpts = fetchImpl ? { fetch: fetchImpl } : undefined;
22474
22480
  if (compressedIndexURL) {
@@ -22484,7 +22490,7 @@
22484
22490
  fai: new RemoteFile(indexURL, fileOpts),
22485
22491
  });
22486
22492
  }
22487
- readerCache.set(cacheKey, reader);
22493
+ readerCache$1.set(cacheKey, reader);
22488
22494
  }
22489
22495
  return reader;
22490
22496
  }
@@ -22504,14 +22510,14 @@
22504
22510
  const faiURL = indexURL !== null && indexURL !== void 0 ? indexURL : fastaURL + '.fai';
22505
22511
  return async (locus, signal) => {
22506
22512
  var _a;
22507
- const reader = getReader(fastaURL, faiURL, compressedIndexURL, fetchImpl);
22513
+ const reader = getReader$1(fastaURL, faiURL, compressedIndexURL, fetchImpl);
22508
22514
  try {
22509
22515
  const sequence = await reader.getSequence(locus.chr, locus.start, locus.end, { signal });
22510
22516
  return sequence !== null && sequence !== void 0 ? sequence : '';
22511
22517
  }
22512
22518
  catch (err) {
22513
22519
  if (err instanceof Error && (err.name === 'AbortError' || ((_a = err.message) === null || _a === void 0 ? void 0 : _a.includes('aborted')))) {
22514
- readerCache.delete(fastaURL);
22520
+ readerCache$1.delete(fastaURL);
22515
22521
  }
22516
22522
  throw err;
22517
22523
  }
@@ -22525,7 +22531,7 @@
22525
22531
  */
22526
22532
  async function fetchFastaChromSizes(fastaURL, indexURL, fetchImpl) {
22527
22533
  const faiURL = indexURL !== null && indexURL !== void 0 ? indexURL : fastaURL + '.fai';
22528
- const reader = getReader(fastaURL, faiURL, undefined, fetchImpl);
22534
+ const reader = getReader$1(fastaURL, faiURL, undefined, fetchImpl);
22529
22535
  return reader.getSequenceSizes();
22530
22536
  }
22531
22537
  /**
@@ -22533,12 +22539,12 @@
22533
22539
  */
22534
22540
  async function fetchFastaSequenceNames(fastaURL, indexURL, fetchImpl) {
22535
22541
  const faiURL = indexURL !== null && indexURL !== void 0 ? indexURL : fastaURL + '.fai';
22536
- const reader = getReader(fastaURL, faiURL, undefined, fetchImpl);
22542
+ const reader = getReader$1(fastaURL, faiURL, undefined, fetchImpl);
22537
22543
  return reader.getSequenceNames();
22538
22544
  }
22539
22545
  /** Clear the FASTA reader cache (useful for testing or memory management). */
22540
22546
  function clearFastaCache() {
22541
- readerCache.clear();
22547
+ readerCache$1.clear();
22542
22548
  }
22543
22549
 
22544
22550
  /**
@@ -23062,7 +23068,7 @@
23062
23068
  * Layer 1 (Data): no DOM, no canvas.
23063
23069
  */
23064
23070
  /** Known data source config types. */
23065
- const KNOWN_DS_TYPES = new Set(['bigwig', 'gtx', 'ucsc', 'text', 'memory']);
23071
+ const KNOWN_DS_TYPES = new Set(['bigwig', 'bigbed', 'gtx', 'ucsc', 'text', 'memory']);
23066
23072
  /** Check whether a data source config type is one of the known concrete types. */
23067
23073
  function isKnownDataSourceType(type) {
23068
23074
  return KNOWN_DS_TYPES.has(type);
@@ -23096,7 +23102,16 @@
23096
23102
  auth.headers = config.headers;
23097
23103
  if (config.withCredentials)
23098
23104
  auth.withCredentials = config.withCredentials;
23099
- // BigWig / BigBed binary formats
23105
+ // BigBed binary formats (annotation data)
23106
+ if (format && isBigBedFormat(format)) {
23107
+ log.warn(`Unknown data source type "${type}", inferred "bigbed" from URL: ${url}`);
23108
+ return {
23109
+ type: 'bigbed',
23110
+ url,
23111
+ ...auth,
23112
+ };
23113
+ }
23114
+ // BigWig / other binary formats (quantitative data)
23100
23115
  if (format && (format === 'bigwig' || format === 'bw' || isBinaryFormat(format))) {
23101
23116
  log.warn(`Unknown data source type "${type}", inferred "bigwig" from URL: ${url}`);
23102
23117
  return {
@@ -23534,6 +23549,106 @@
23534
23549
  }
23535
23550
  }
23536
23551
 
23552
+ /**
23553
+ * BigBed data source — fetches BED features from BigBed binary files.
23554
+ *
23555
+ * Uses @gmod/bbi's BigBed class for binary parsing and range queries.
23556
+ * Returns BedFeature[] suitable for annotation track rendering.
23557
+ *
23558
+ * Mirrors igv.js BWSource behavior for bigbed format:
23559
+ * - Features are decoded from BED rest fields via decodeBed()
23560
+ * - No zoom-level summarization (unlike BigWig)
23561
+ * - Feature density estimation from header dataCount
23562
+ *
23563
+ * Layer 1 (Data + Layout): no DOM.
23564
+ */
23565
+ // Cache BigBed instances by URL to reuse parsed headers and index trees.
23566
+ const readerCache = new Map();
23567
+ function getReader(url, fetchImpl) {
23568
+ const resolvedUrl = normalizeGoogleURL(url);
23569
+ let reader = readerCache.get(resolvedUrl);
23570
+ if (!reader) {
23571
+ const fileOpts = fetchImpl ? { fetch: fetchImpl } : undefined;
23572
+ reader = new BigBed({ filehandle: new RemoteFile(resolvedUrl, fileOpts) });
23573
+ readerCache.set(resolvedUrl, reader);
23574
+ }
23575
+ return reader;
23576
+ }
23577
+ class BigBedDataSource {
23578
+ constructor(url, fetchImpl) {
23579
+ this.url = url;
23580
+ this.fetchImpl = fetchImpl;
23581
+ this.bb = getReader(url, fetchImpl);
23582
+ }
23583
+ /** Set a chromosome name resolver for alias resolution (e.g., "1" → "chr1"). */
23584
+ setChromNameResolver(resolver) {
23585
+ this._resolveChromName = resolver;
23586
+ }
23587
+ async fetch(locus, _bpPerPixel, signal) {
23588
+ var _a;
23589
+ const chr = this._resolveChromName
23590
+ ? this._resolveChromName(locus.chr)
23591
+ : locus.chr;
23592
+ try {
23593
+ const rawFeatures = await this.bb.getFeatures(chr, locus.start, locus.end, { signal });
23594
+ const result = [];
23595
+ for (const f of rawFeatures) {
23596
+ const parsed = parseBigBedFeature(chr, f.start, f.end, f.rest);
23597
+ if (parsed)
23598
+ result.push(parsed);
23599
+ }
23600
+ return result;
23601
+ }
23602
+ catch (err) {
23603
+ // Evict poisoned reader on abort (same pattern as BigWig)
23604
+ if (err instanceof Error && (err.name === 'AbortError' || ((_a = err.message) === null || _a === void 0 ? void 0 : _a.includes('aborted')))) {
23605
+ readerCache.delete(normalizeGoogleURL(this.url));
23606
+ }
23607
+ throw err;
23608
+ }
23609
+ }
23610
+ /**
23611
+ * Search for a feature by name via BigBed extra index.
23612
+ * Returns matching features, or empty array if not found or not indexed.
23613
+ */
23614
+ async search(name) {
23615
+ const header = await this.bb.getHeader();
23616
+ const results = await this.bb.searchExtraIndex(name);
23617
+ if (results.length === 0)
23618
+ return [];
23619
+ const refsByNumber = header.refsByNumber;
23620
+ const features = [];
23621
+ for (const r of results) {
23622
+ const chromId = r.chromId;
23623
+ if (chromId == null || !(refsByNumber === null || refsByNumber === void 0 ? void 0 : refsByNumber[chromId]))
23624
+ continue;
23625
+ const chr = refsByNumber[chromId].name;
23626
+ const parsed = parseBigBedFeature(chr, r.start, r.end, r.rest);
23627
+ if (parsed)
23628
+ features.push(parsed);
23629
+ }
23630
+ return features;
23631
+ }
23632
+ dispose() {
23633
+ readerCache.delete(normalizeGoogleURL(this.url));
23634
+ }
23635
+ }
23636
+ /**
23637
+ * Parse a BigBed feature's rest field into a BedFeature.
23638
+ * Reconstructs BED tokens from chr/start/end + tab-delimited rest, then uses decodeBed().
23639
+ */
23640
+ function parseBigBedFeature(chr, start, end, rest) {
23641
+ const tokens = [chr, String(start), String(end)];
23642
+ if (rest) {
23643
+ tokens.push(...rest.split('\t'));
23644
+ }
23645
+ return decodeBed(tokens);
23646
+ }
23647
+ /** Clear the BigBed reader cache (useful for testing or memory management). */
23648
+ function clearBigBedCache() {
23649
+ readerCache.clear();
23650
+ }
23651
+
23537
23652
  /**
23538
23653
  * DataSource backed by an in-memory feature array.
23539
23654
  *
@@ -26678,6 +26793,8 @@
26678
26793
  switch (config.type) {
26679
26794
  case 'bigwig':
26680
26795
  return new BigWigDataSource(config.url, config.windowFunction);
26796
+ case 'bigbed':
26797
+ return new BigBedDataSource(config.url);
26681
26798
  case 'gtx':
26682
26799
  return new GtxDataSource(config.url, config.experimentId, config.windowFunction);
26683
26800
  case 'ucsc':
@@ -26959,6 +27076,11 @@
26959
27076
  registerTypeAlias('genepred', 'annotation');
26960
27077
  registerTypeAlias('genepredext', 'annotation');
26961
27078
  registerTypeAlias('refflat', 'annotation');
27079
+ // BigBed format → annotation track type aliases
27080
+ registerTypeAlias('bigbed', 'annotation');
27081
+ registerTypeAlias('bedtype', 'annotation');
27082
+ registerTypeAlias('biggenepred', 'annotation');
27083
+ registerTypeAlias('bignarrowpeak', 'annotation');
26962
27084
  // Merged/overlay aliases
26963
27085
  registerTypeAlias('overlay', 'merged');
26964
27086
  // Interaction format aliases
@@ -27049,6 +27171,10 @@
27049
27171
  'gtf': 'annotation',
27050
27172
  'narrowpeak': 'annotation',
27051
27173
  'broadpeak': 'annotation',
27174
+ 'bigbed': 'annotation',
27175
+ 'biggenepred': 'annotation',
27176
+ 'bignarrowpeak': 'annotation',
27177
+ 'biginteract': 'interact',
27052
27178
  'sequence': 'sequence',
27053
27179
  'interact': 'interact',
27054
27180
  'bedpe': 'interact',
@@ -27166,23 +27292,35 @@
27166
27292
  return result;
27167
27293
  }
27168
27294
  function convertIgvAnnotationTrack(igvTrack) {
27295
+ var _a, _b;
27169
27296
  const result = { type: 'annotation' };
27170
27297
  if (igvTrack.name)
27171
27298
  result.name = igvTrack.name;
27172
27299
  if (igvTrack.order != null)
27173
27300
  result.order = igvTrack.order;
27174
- // Data source — gene tracks in igv.js can come from URLs (text files) or UCSC
27301
+ // Data source — gene tracks in igv.js can come from BigBed, text files, or UCSC
27175
27302
  if (igvTrack.url) {
27176
- const ds = {
27177
- type: 'text',
27178
- url: igvTrack.url,
27179
- ...extractAuthFromIgv(igvTrack),
27180
- };
27181
- if (igvTrack.indexURL)
27182
- ds.indexURL = igvTrack.indexURL;
27183
- if (igvTrack.format)
27184
- ds.format = igvTrack.format;
27185
- result.dataSource = ds;
27303
+ const format = (_b = (_a = igvTrack.format) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : '';
27304
+ if (isBigBedFormat(format) || /\.bb$/i.test(igvTrack.url) || /\.bigbed$/i.test(igvTrack.url)) {
27305
+ const ds = {
27306
+ type: 'bigbed',
27307
+ url: igvTrack.url,
27308
+ ...extractAuthFromIgv(igvTrack),
27309
+ };
27310
+ result.dataSource = ds;
27311
+ }
27312
+ else {
27313
+ const ds = {
27314
+ type: 'text',
27315
+ url: igvTrack.url,
27316
+ ...extractAuthFromIgv(igvTrack),
27317
+ };
27318
+ if (igvTrack.indexURL)
27319
+ ds.indexURL = igvTrack.indexURL;
27320
+ if (igvTrack.format)
27321
+ ds.format = igvTrack.format;
27322
+ result.dataSource = ds;
27323
+ }
27186
27324
  }
27187
27325
  // Config overrides
27188
27326
  const config = {};
@@ -27278,13 +27416,23 @@
27278
27416
  return igv;
27279
27417
  }
27280
27418
  function convertToIgvAnnotationTrack(track) {
27281
- var _a;
27419
+ var _a, _b;
27282
27420
  const igv = { type: 'annotation' };
27283
27421
  if (track.name)
27284
27422
  igv.name = track.name;
27285
27423
  if (track.order != null)
27286
27424
  igv.order = track.order;
27287
27425
  if (((_a = track.dataSource) === null || _a === void 0 ? void 0 : _a.type) === 'text') {
27426
+ igv.url = track.dataSource.url;
27427
+ if (track.dataSource.format)
27428
+ igv.format = track.dataSource.format;
27429
+ if (track.dataSource.indexURL)
27430
+ igv.indexURL = track.dataSource.indexURL;
27431
+ Object.assign(igv, extractAuthForIgv(track.dataSource));
27432
+ }
27433
+ else if (((_b = track.dataSource) === null || _b === void 0 ? void 0 : _b.type) === 'bigbed') {
27434
+ igv.url = track.dataSource.url;
27435
+ igv.format = 'bigbed';
27288
27436
  Object.assign(igv, extractAuthForIgv(track.dataSource));
27289
27437
  }
27290
27438
  if (track.config) {
@@ -28833,6 +28981,9 @@
28833
28981
  if (ds instanceof BigWigDataSource || ds instanceof GtxDataSource) {
28834
28982
  ds.setChromNameResolver(alias => ctx.genome.getChromosomeName(alias));
28835
28983
  }
28984
+ else if (ds instanceof BigBedDataSource) {
28985
+ ds.setChromNameResolver(alias => ctx.genome.getChromosomeName(alias));
28986
+ }
28836
28987
  else if (ds instanceof TextFeatureSource) {
28837
28988
  ds.setChromNameResolver(alias => ctx.genome.getChromosomeName(alias));
28838
28989
  if (ctx.cumulativeOffsets) {
@@ -29020,6 +29171,30 @@
29020
29171
  searchableFields: options === null || options === void 0 ? void 0 : options.searchableFields,
29021
29172
  };
29022
29173
  }
29174
+ function createBigBedTrack(ctx, url, options) {
29175
+ var _a;
29176
+ const { canvas } = ctx.canvasProvider.createCanvas(0, 0);
29177
+ const track = new AnnotationTrackCanvas(canvas, {
29178
+ locus: ctx.locus,
29179
+ features: [],
29180
+ config: options === null || options === void 0 ? void 0 : options.config,
29181
+ theme: ctx.theme,
29182
+ canvasProvider: ctx.canvasProvider,
29183
+ name: options === null || options === void 0 ? void 0 : options.name,
29184
+ });
29185
+ const dataSourceConfig = { type: 'bigbed', url, ...options === null || options === void 0 ? void 0 : options.auth };
29186
+ const fetchImpl = resolveTrackFetchImpl(ctx, options);
29187
+ const { dataSource } = getOrCreateDataSource(dataSourceConfig, () => new BigBedDataSource(url, fetchImpl), ctx);
29188
+ return {
29189
+ track,
29190
+ dataSource,
29191
+ dataSourceConfig,
29192
+ maxTrackHeight: options === null || options === void 0 ? void 0 : options.maxTrackHeight,
29193
+ metadata: options === null || options === void 0 ? void 0 : options.metadata,
29194
+ searchable: (_a = options === null || options === void 0 ? void 0 : options.searchable) !== null && _a !== void 0 ? _a : true,
29195
+ searchableFields: options === null || options === void 0 ? void 0 : options.searchableFields,
29196
+ };
29197
+ }
29023
29198
  function createInteractionTrack(ctx, url, options) {
29024
29199
  var _a;
29025
29200
  const { canvas } = ctx.canvasProvider.createCanvas(0, 0);
@@ -29369,10 +29544,10 @@
29369
29544
  }
29370
29545
  // ─── Track lifecycle ─────────────────────────────────────────────────────
29371
29546
  /** Add a track with an optional data source for automatic data management. */
29372
- addTrack(track, dataSource, dataSourceConfig, maxTrackHeight, order) {
29547
+ addTrack(track, dataSource, dataSourceConfig, maxTrackHeight, order, id) {
29373
29548
  var _a;
29374
29549
  this._snapshotForUndo('track-add');
29375
- const id = generateTrackId(track.type);
29550
+ id !== null && id !== void 0 ? id : (id = generateTrackId(track.type));
29376
29551
  const mt = {
29377
29552
  id,
29378
29553
  track,
@@ -30251,11 +30426,9 @@
30251
30426
  if (!this.dataSourceWorkerProvider && dataSource) {
30252
30427
  wireDataSource(dataSource, ctx);
30253
30428
  }
30254
- this.addTrack(created.track, dataSource !== null && dataSource !== void 0 ? dataSource : undefined, (_a = created.dataSourceConfig) !== null && _a !== void 0 ? _a : undefined, undefined, (_b = created.order) !== null && _b !== void 0 ? _b : undefined);
30429
+ this.addTrack(created.track, dataSource !== null && dataSource !== void 0 ? dataSource : undefined, (_a = created.dataSourceConfig) !== null && _a !== void 0 ? _a : undefined, undefined, (_b = created.order) !== null && _b !== void 0 ? _b : undefined, trackConfig.id);
30255
30430
  // Restore bookkeeping fields for round-trip serialization
30256
30431
  const mt = this.findMT(created.track);
30257
- if (trackConfig.id)
30258
- mt.id = trackConfig.id;
30259
30432
  if (created.name)
30260
30433
  mt.name = created.name;
30261
30434
  if (trackConfig.metadata)
@@ -30341,10 +30514,8 @@
30341
30514
  wireDataSource(dataSource, ctx);
30342
30515
  }
30343
30516
  }
30344
- this.addTrack(created.track, dataSource !== null && dataSource !== void 0 ? dataSource : undefined, dataSourceConfig !== null && dataSourceConfig !== void 0 ? dataSourceConfig : undefined, undefined, (_a = created.order) !== null && _a !== void 0 ? _a : undefined);
30517
+ this.addTrack(created.track, dataSource !== null && dataSource !== void 0 ? dataSource : undefined, dataSourceConfig !== null && dataSourceConfig !== void 0 ? dataSourceConfig : undefined, undefined, (_a = created.order) !== null && _a !== void 0 ? _a : undefined, trackConfig.id);
30345
30518
  const mt = this.findMT(created.track);
30346
- if (trackConfig.id)
30347
- mt.id = trackConfig.id;
30348
30519
  if (created.name)
30349
30520
  mt.name = created.name;
30350
30521
  if (trackConfig.metadata)
@@ -30403,6 +30574,10 @@
30403
30574
  addBedTrack(url, options) {
30404
30575
  return this.registerFactory(createBedTrack(this.factoryContext(), url, options));
30405
30576
  }
30577
+ /** Add a BigBed annotation track from a URL. */
30578
+ addBigBedTrack(url, options) {
30579
+ return this.registerFactory(createBigBedTrack(this.factoryContext(), url, options));
30580
+ }
30406
30581
  /** Add an interaction (arc/BEDPE) track from a URL. */
30407
30582
  addInteractionTrack(url, options) {
30408
30583
  return this.registerFactory(createInteractionTrack(this.factoryContext(), url, options));
@@ -35882,8 +36057,12 @@ tr.border-top td {
35882
36057
  const windowFunction = (_d = config.windowFunction) !== null && _d !== void 0 ? _d : 'mean';
35883
36058
  dataSource = new GtxDataSource(config.url, (_e = config.experimentId) !== null && _e !== void 0 ? _e : '', windowFunction);
35884
36059
  }
36060
+ else if (isBigBedFormat(format)) {
36061
+ // BigBed → BigBedDataSource (annotation features)
36062
+ dataSource = new BigBedDataSource(config.url);
36063
+ }
35885
36064
  else if (isBinaryFormat(format)) {
35886
- // BigWig/BigBed → BigWigDataSource
36065
+ // BigWig → BigWigDataSource (quantitative features)
35887
36066
  const windowFunction = (_f = config.windowFunction) !== null && _f !== void 0 ? _f : 'mean';
35888
36067
  dataSource = new BigWigDataSource(config.url, windowFunction);
35889
36068
  }
@@ -35906,6 +36085,7 @@ tr.border-top td {
35906
36085
  exports.BaseCompositeTrack = BaseCompositeTrack;
35907
36086
  exports.BaseTrackCanvas = BaseTrackCanvas;
35908
36087
  exports.BigBedAnnotationSource = BigBedAnnotationSource;
36088
+ exports.BigBedDataSource = BigBedDataSource;
35909
36089
  exports.BigWigDataSource = BigWigDataSource;
35910
36090
  exports.BigWigReader = BigWigReader;
35911
36091
  exports.BrowserEvent = BrowserEvent;