@gmod/bbi 1.0.33 → 2.0.0

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.
Files changed (60) hide show
  1. package/CHANGELOG.md +20 -3
  2. package/dist/bbi.d.ts +2 -2
  3. package/dist/bbi.js +56 -59
  4. package/dist/bbi.js.map +1 -0
  5. package/dist/bigbed.d.ts +1 -2
  6. package/dist/bigbed.js +23 -20
  7. package/dist/bigbed.js.map +1 -0
  8. package/dist/bigwig.d.ts +1 -3
  9. package/dist/bigwig.js +5 -8
  10. package/dist/bigwig.js.map +1 -0
  11. package/dist/blockView.d.ts +8 -9
  12. package/dist/blockView.js +153 -92
  13. package/dist/blockView.js.map +1 -0
  14. package/dist/index.js +1 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/range.js +2 -0
  17. package/dist/range.js.map +1 -0
  18. package/dist/unzip-pako.d.ts +1 -1
  19. package/dist/unzip-pako.js +2 -1
  20. package/dist/unzip-pako.js.map +1 -0
  21. package/dist/unzip.js +1 -0
  22. package/dist/unzip.js.map +1 -0
  23. package/dist/util.d.ts +11 -1
  24. package/dist/util.js +10 -4
  25. package/dist/util.js.map +1 -0
  26. package/esm/bbi.d.ts +2 -2
  27. package/esm/bbi.js +62 -67
  28. package/esm/bbi.js.map +1 -0
  29. package/esm/bigbed.d.ts +1 -2
  30. package/esm/bigbed.js +42 -46
  31. package/esm/bigbed.js.map +1 -0
  32. package/esm/bigwig.d.ts +1 -3
  33. package/esm/bigwig.js +7 -14
  34. package/esm/bigwig.js.map +1 -0
  35. package/esm/blockView.d.ts +8 -9
  36. package/esm/blockView.js +166 -116
  37. package/esm/blockView.js.map +1 -0
  38. package/esm/index.js +3 -7
  39. package/esm/index.js.map +1 -0
  40. package/esm/range.js +3 -4
  41. package/esm/range.js.map +1 -0
  42. package/esm/unzip-pako.d.ts +1 -1
  43. package/esm/unzip-pako.js +4 -7
  44. package/esm/unzip-pako.js.map +1 -0
  45. package/esm/unzip.js +3 -5
  46. package/esm/unzip.js.map +1 -0
  47. package/esm/util.d.ts +11 -1
  48. package/esm/util.js +14 -15
  49. package/esm/util.js.map +1 -0
  50. package/package.json +13 -13
  51. package/src/bbi.ts +375 -0
  52. package/src/bigbed.ts +244 -0
  53. package/src/bigwig.ts +38 -0
  54. package/src/blockView.ts +496 -0
  55. package/src/declare.d.ts +2 -0
  56. package/src/index.ts +3 -0
  57. package/src/range.ts +142 -0
  58. package/src/unzip-pako.ts +5 -0
  59. package/src/unzip.ts +2 -0
  60. package/src/util.ts +83 -0
@@ -0,0 +1,496 @@
1
+ import { Observer } from 'rxjs'
2
+ import { Parser } from 'binary-parser'
3
+ import AbortablePromiseCache from 'abortable-promise-cache'
4
+ import { GenericFilehandle } from 'generic-filehandle'
5
+ import QuickLRU from 'quick-lru'
6
+
7
+ // locals
8
+ import Range from './range'
9
+ import { unzip } from './unzip'
10
+ import { Feature } from './bbi'
11
+ import { groupBlocks, checkAbortSignal } from './util'
12
+
13
+ interface CoordRequest {
14
+ chrId: number
15
+ start: number
16
+ end: number
17
+ }
18
+ interface DataBlock {
19
+ startChrom: number
20
+ endChrom: number
21
+ startBase: number
22
+ endBase: number
23
+ validCnt: number
24
+ minVal: number
25
+ maxVal: number
26
+ sumData: number
27
+ sumSqData: number
28
+ }
29
+ interface ReadData {
30
+ offset: number
31
+ length: number
32
+ }
33
+
34
+ interface Options {
35
+ signal?: AbortSignal
36
+ request?: CoordRequest
37
+ }
38
+
39
+ const BIG_WIG_TYPE_GRAPH = 1
40
+ const BIG_WIG_TYPE_VSTEP = 2
41
+ const BIG_WIG_TYPE_FSTEP = 3
42
+
43
+ function coordFilter(s1: number, e1: number, s2: number, e2: number): boolean {
44
+ return s1 < e2 && e1 >= s2
45
+ }
46
+
47
+ function getParsers(isBigEndian: boolean) {
48
+ const le = isBigEndian ? 'big' : 'little'
49
+ const summaryParser = new Parser()
50
+ .endianess(le)
51
+ .uint32('chromId')
52
+ .uint32('start')
53
+ .uint32('end')
54
+ .uint32('validCnt')
55
+ .floatle('minScore')
56
+ .floatle('maxScore')
57
+ .floatle('sumData')
58
+ .floatle('sumSqData')
59
+ .saveOffset('offset')
60
+
61
+ const leafParser = new Parser()
62
+ .endianess(le)
63
+ .uint8('isLeaf')
64
+ .skip(1)
65
+ .uint16('cnt')
66
+ .choice({
67
+ tag: 'isLeaf',
68
+ choices: {
69
+ 1: new Parser().endianess(le).array('blocksToFetch', {
70
+ length: 'cnt',
71
+ type: new Parser()
72
+ .endianess(le)
73
+ .uint32('startChrom')
74
+ .uint32('startBase')
75
+ .uint32('endChrom')
76
+ .uint32('endBase')
77
+ .uint64('blockOffset')
78
+ .uint64('blockSize')
79
+ .saveOffset('offset'),
80
+ }),
81
+ 0: new Parser().array('recurOffsets', {
82
+ length: 'cnt',
83
+ type: new Parser()
84
+ .endianess(le)
85
+ .uint32('startChrom')
86
+ .uint32('startBase')
87
+ .uint32('endChrom')
88
+ .uint32('endBase')
89
+ .uint64('blockOffset')
90
+ .saveOffset('offset'),
91
+ }),
92
+ },
93
+ })
94
+ const bigBedParser = new Parser()
95
+ .endianess(le)
96
+ .uint32('chromId')
97
+ .int32('start')
98
+ .int32('end')
99
+ .string('rest', {
100
+ zeroTerminated: true,
101
+ })
102
+ .saveOffset('offset')
103
+
104
+ const bigWigParser = new Parser()
105
+ .endianess(le)
106
+ .skip(4)
107
+ .int32('blockStart')
108
+ .skip(4)
109
+ .uint32('itemStep')
110
+ .uint32('itemSpan')
111
+ .uint8('blockType')
112
+ .skip(1)
113
+ .uint16('itemCount')
114
+ .choice({
115
+ tag: 'blockType',
116
+ choices: {
117
+ [BIG_WIG_TYPE_FSTEP]: new Parser().array('items', {
118
+ length: 'itemCount',
119
+ type: new Parser().floatle('score'),
120
+ }),
121
+ [BIG_WIG_TYPE_VSTEP]: new Parser().array('items', {
122
+ length: 'itemCount',
123
+ type: new Parser().endianess(le).int32('start').floatle('score'),
124
+ }),
125
+ [BIG_WIG_TYPE_GRAPH]: new Parser().array('items', {
126
+ length: 'itemCount',
127
+ type: new Parser()
128
+ .endianess(le)
129
+ .int32('start')
130
+ .int32('end')
131
+ .floatle('score'),
132
+ }),
133
+ },
134
+ })
135
+ return {
136
+ bigWigParser,
137
+ bigBedParser,
138
+ summaryParser,
139
+ leafParser,
140
+ }
141
+ }
142
+
143
+ /**
144
+ * View into a subset of the data in a BigWig file.
145
+ *
146
+ * Adapted by Robert Buels and Colin Diesh from bigwig.js in the Dalliance Genome
147
+ * Explorer by Thomas Down.
148
+ * @constructs
149
+ */
150
+
151
+ export class BlockView {
152
+ private cirTreePromise?: Promise<{ bytesRead: number; buffer: Buffer }>
153
+
154
+ private featureCache = new AbortablePromiseCache({
155
+ cache: new QuickLRU({ maxSize: 1000 }),
156
+
157
+ fill: async (requestData: ReadData, signal: AbortSignal) => {
158
+ const len = Number(requestData.length)
159
+ const off = Number(requestData.offset)
160
+ const { buffer } = await this.bbi.read(Buffer.alloc(len), 0, len, off, {
161
+ signal,
162
+ })
163
+ return buffer
164
+ },
165
+ })
166
+
167
+ private leafParser: ReturnType<typeof getParsers>['leafParser']
168
+
169
+ private bigBedParser: ReturnType<typeof getParsers>['bigBedParser']
170
+
171
+ public constructor(
172
+ private bbi: GenericFilehandle,
173
+ private refsByName: any,
174
+ private cirTreeOffset: number,
175
+ private isBigEndian: boolean,
176
+ private isCompressed: boolean,
177
+ private blockType: string,
178
+ ) {
179
+ if (!(cirTreeOffset >= 0)) {
180
+ throw new Error('invalid cirTreeOffset!')
181
+ }
182
+
183
+ const parsers = getParsers(isBigEndian)
184
+ this.leafParser = parsers.leafParser
185
+ this.bigBedParser = parsers.bigBedParser
186
+ }
187
+
188
+ public async readWigData(
189
+ chrName: string,
190
+ start: number,
191
+ end: number,
192
+ observer: Observer<Feature[]>,
193
+ opts: Options,
194
+ ) {
195
+ try {
196
+ const { refsByName, bbi, cirTreeOffset, isBigEndian } = this
197
+ const chrId = refsByName[chrName]
198
+ if (chrId === undefined) {
199
+ observer.complete()
200
+ }
201
+ const request = { chrId, start, end }
202
+ if (!this.cirTreePromise) {
203
+ const off = Number(cirTreeOffset)
204
+ this.cirTreePromise = bbi.read(Buffer.alloc(48), 0, 48, off, opts)
205
+ }
206
+ const { buffer } = await this.cirTreePromise
207
+ const cirBlockSize = isBigEndian
208
+ ? buffer.readUInt32BE(4)
209
+ : buffer.readUInt32LE(4)
210
+ let blocksToFetch: any[] = []
211
+ let outstanding = 0
212
+
213
+ const cirFobRecur2 = (
214
+ cirBlockData: Buffer,
215
+ offset: number,
216
+ level: number,
217
+ ) => {
218
+ try {
219
+ const data = cirBlockData.subarray(offset)
220
+
221
+ const p = this.leafParser.parse(data)
222
+ if (p.blocksToFetch) {
223
+ blocksToFetch = blocksToFetch.concat(
224
+ p.blocksToFetch
225
+ .filter(filterFeats)
226
+ .map((l: { blockOffset: bigint; blockSize: bigint }) => ({
227
+ offset: l.blockOffset,
228
+ length: l.blockSize,
229
+ })),
230
+ )
231
+ }
232
+ if (p.recurOffsets) {
233
+ const recurOffsets = p.recurOffsets
234
+ .filter(filterFeats)
235
+ .map((l: { blockOffset: bigint }) => Number(l.blockOffset))
236
+ if (recurOffsets.length > 0) {
237
+ cirFobRecur(recurOffsets, level + 1)
238
+ }
239
+ }
240
+ } catch (e) {
241
+ observer.error(e)
242
+ }
243
+ }
244
+
245
+ const filterFeats = (b: DataBlock) => {
246
+ const { startChrom, startBase, endChrom, endBase } = b
247
+ return (
248
+ (startChrom < chrId || (startChrom === chrId && startBase <= end)) &&
249
+ (endChrom > chrId || (endChrom === chrId && endBase >= start))
250
+ )
251
+ }
252
+
253
+ const cirFobStartFetch = async (
254
+ off: number[],
255
+ fr: Range,
256
+ level: number,
257
+ ) => {
258
+ try {
259
+ const length = fr.max() - fr.min()
260
+ const offset = fr.min()
261
+ const resultBuffer: Buffer = await this.featureCache.get(
262
+ `${length}_${offset}`,
263
+ { length, offset },
264
+ opts.signal,
265
+ )
266
+ for (let i = 0; i < off.length; i += 1) {
267
+ if (fr.contains(off[i])) {
268
+ cirFobRecur2(resultBuffer, off[i] - offset, level)
269
+ outstanding -= 1
270
+ if (outstanding === 0) {
271
+ this.readFeatures(observer, blocksToFetch, { ...opts, request })
272
+ }
273
+ }
274
+ }
275
+ } catch (e) {
276
+ observer.error(e)
277
+ }
278
+ }
279
+ const cirFobRecur = (offset: number[], level: number) => {
280
+ try {
281
+ outstanding += offset.length
282
+
283
+ const maxCirBlockSpan = 4 + Number(cirBlockSize) * 32 // Upper bound on size, based on a completely full leaf node.
284
+ let spans = new Range(offset[0], offset[0] + maxCirBlockSpan)
285
+ for (let i = 1; i < offset.length; i += 1) {
286
+ const blockSpan = new Range(offset[i], offset[i] + maxCirBlockSpan)
287
+ spans = spans.union(blockSpan)
288
+ }
289
+ spans.getRanges().map(fr => cirFobStartFetch(offset, fr, level))
290
+ } catch (e) {
291
+ observer.error(e)
292
+ }
293
+ }
294
+
295
+ return cirFobRecur([Number(cirTreeOffset) + 48], 1)
296
+ } catch (e) {
297
+ observer.error(e)
298
+ }
299
+ }
300
+
301
+ private parseSummaryBlock(
302
+ buffer: Buffer,
303
+ startOffset: number,
304
+ request?: CoordRequest,
305
+ ) {
306
+ const features = [] as any[]
307
+ let offset = startOffset
308
+
309
+ const dataView = new DataView(
310
+ buffer.buffer,
311
+ buffer.byteOffset,
312
+ buffer.length,
313
+ )
314
+ while (offset < buffer.byteLength) {
315
+ // this was extracted from looking at the runtime code generated by
316
+ // binary-parser
317
+ const chromId = dataView.getUint32(offset, true)
318
+ offset += 4
319
+ const start = dataView.getUint32(offset, true)
320
+ offset += 4
321
+ const end = dataView.getUint32(offset, true)
322
+ offset += 4
323
+ const validCnt = dataView.getUint32(offset, true)
324
+ offset += 4
325
+ const minScore = dataView.getFloat32(offset, true)
326
+ offset += 4
327
+ const maxScore = dataView.getFloat32(offset, true)
328
+ offset += 4
329
+ const sumData = dataView.getFloat32(offset, true)
330
+ offset += 4
331
+ // unused
332
+ // const sumSqData = dataView.getFloat32(offset, true)
333
+ offset += 4
334
+
335
+ if (
336
+ request
337
+ ? chromId === request.chrId &&
338
+ coordFilter(start, end, request.start, request.end)
339
+ : true
340
+ ) {
341
+ features.push({
342
+ start,
343
+ end,
344
+ maxScore,
345
+ minScore,
346
+ summary: true,
347
+ score: sumData / (validCnt || 1),
348
+ })
349
+ }
350
+ }
351
+
352
+ return features
353
+ }
354
+
355
+ private parseBigBedBlock(
356
+ data: Buffer,
357
+ startOffset: number,
358
+ offset: number,
359
+ request?: CoordRequest,
360
+ ) {
361
+ const items = [] as Feature[]
362
+ let currOffset = startOffset
363
+ while (currOffset < data.byteLength) {
364
+ const res = this.bigBedParser.parse(data.subarray(currOffset))
365
+ items.push({ ...res, uniqueId: `bb-${offset + currOffset}` })
366
+ currOffset += res.offset
367
+ }
368
+
369
+ return request
370
+ ? items.filter((f: any) =>
371
+ coordFilter(f.start, f.end, request.start, request.end),
372
+ )
373
+ : items
374
+ }
375
+
376
+ private parseBigWigBlock(
377
+ buffer: Buffer,
378
+ startOffset: number,
379
+ request?: CoordRequest,
380
+ ) {
381
+ const b = buffer.subarray(startOffset)
382
+
383
+ const dataView = new DataView(b.buffer, b.byteOffset, b.length)
384
+ let offset = 0
385
+ offset += 4
386
+ const blockStart = dataView.getInt32(offset, true)
387
+ offset += 8
388
+ const itemStep = dataView.getUint32(offset, true)
389
+ offset += 4
390
+ const itemSpan = dataView.getUint32(offset, true)
391
+ offset += 4
392
+ const blockType = dataView.getUint8(offset)
393
+ offset += 2
394
+ const itemCount = dataView.getUint16(offset, true)
395
+ offset += 2
396
+ const items = new Array(itemCount)
397
+ switch (blockType) {
398
+ case 1:
399
+ for (let i = 0; i < itemCount; i++) {
400
+ const start = dataView.getInt32(offset, true)
401
+ offset += 4
402
+ const end = dataView.getInt32(offset, true)
403
+ offset += 4
404
+ const score = dataView.getFloat32(offset, true)
405
+ offset += 4
406
+ items[i] = { start, end, score }
407
+ }
408
+ break
409
+ case 2:
410
+ for (let i = 0; i < itemCount; i++) {
411
+ const start = dataView.getInt32(offset, true)
412
+ offset += 4
413
+ const score = dataView.getFloat32(offset, true)
414
+ offset += 4
415
+ items[i] = { score, start, end: start + itemSpan }
416
+ }
417
+ break
418
+ case 3:
419
+ for (let i = 0; i < itemCount; i++) {
420
+ const score = dataView.getFloat32(offset, true)
421
+ offset += 4
422
+ const start = blockStart + i * itemStep
423
+ items[i] = { score, start, end: start + itemSpan }
424
+ }
425
+ break
426
+ }
427
+
428
+ return request
429
+ ? items.filter((f: any) =>
430
+ coordFilter(f.start, f.end, request.start, request.end),
431
+ )
432
+ : items
433
+ }
434
+
435
+ public async readFeatures(
436
+ observer: Observer<Feature[]>,
437
+ blocks: { offset: bigint; length: bigint }[],
438
+ opts: Options = {},
439
+ ) {
440
+ try {
441
+ const { blockType, isCompressed } = this
442
+ const { signal, request } = opts
443
+ const blockGroupsToFetch = groupBlocks(blocks)
444
+ checkAbortSignal(signal)
445
+ await Promise.all(
446
+ blockGroupsToFetch.map(async blockGroup => {
447
+ checkAbortSignal(signal)
448
+ const { length, offset } = blockGroup
449
+ const data = await this.featureCache.get(
450
+ `${length}_${offset}`,
451
+ blockGroup,
452
+ signal,
453
+ )
454
+ blockGroup.blocks.forEach(block => {
455
+ checkAbortSignal(signal)
456
+ let blockOffset = Number(block.offset) - Number(blockGroup.offset)
457
+ let resultData = data
458
+ if (isCompressed) {
459
+ resultData = unzip(data.subarray(blockOffset))
460
+ blockOffset = 0
461
+ }
462
+ checkAbortSignal(signal)
463
+
464
+ switch (blockType) {
465
+ case 'summary':
466
+ observer.next(
467
+ this.parseSummaryBlock(resultData, blockOffset, request),
468
+ )
469
+ break
470
+ case 'bigwig':
471
+ observer.next(
472
+ this.parseBigWigBlock(resultData, blockOffset, request),
473
+ )
474
+ break
475
+ case 'bigbed':
476
+ observer.next(
477
+ this.parseBigBedBlock(
478
+ resultData,
479
+ blockOffset,
480
+ Number(block.offset) * (1 << 8),
481
+ request,
482
+ ),
483
+ )
484
+ break
485
+ default:
486
+ console.warn(`Don't know what to do with ${blockType}`)
487
+ }
488
+ })
489
+ }),
490
+ )
491
+ observer.complete()
492
+ } catch (e) {
493
+ observer.error(e)
494
+ }
495
+ }
496
+ }
@@ -0,0 +1,2 @@
1
+ declare module 'abortable-promise-cache'
2
+ declare module '@gmod/bed'
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { BigWig } from './bigwig'
2
+ export { BigBed } from './bigbed'
3
+ export { Feature, Header, RequestOptions } from './bbi'
package/src/range.ts ADDED
@@ -0,0 +1,142 @@
1
+ /* eslint prefer-rest-params:0, no-nested-ternary:0 */
2
+
3
+ /**
4
+ * Adapted from a combination of Range and _Compound in the
5
+ * Dalliance Genome Explorer, (c) Thomas Down 2006-2010.
6
+ */
7
+ export default class Range {
8
+ public ranges: any
9
+
10
+ public constructor(arg1: any, arg2?: any) {
11
+ this.ranges =
12
+ arguments.length === 2
13
+ ? [{ min: arg1, max: arg2 }]
14
+ : 0 in arg1
15
+ ? Object.assign({}, arg1)
16
+ : [arg1]
17
+ }
18
+
19
+ public min(): number {
20
+ return this.ranges[0].min
21
+ }
22
+
23
+ public max(): number {
24
+ return this.ranges[this.ranges.length - 1].max
25
+ }
26
+
27
+ public contains(pos: number): boolean {
28
+ for (let s = 0; s < this.ranges.length; s += 1) {
29
+ const r = this.ranges[s]
30
+ if (r.min <= pos && r.max >= pos) {
31
+ return true
32
+ }
33
+ }
34
+ return false
35
+ }
36
+
37
+ public isContiguous(): boolean {
38
+ return this.ranges.length > 1
39
+ }
40
+
41
+ public getRanges(): Range[] {
42
+ return this.ranges.map((r: Range) => new Range(r.min, r.max))
43
+ }
44
+
45
+ public toString(): string {
46
+ return this.ranges.map((r: Range) => `[${r.min}-${r.max}]`).join(',')
47
+ }
48
+
49
+ public union(s1: Range): Range {
50
+ const ranges = this.getRanges().concat(s1.getRanges()).sort(this.rangeOrder)
51
+ const oranges = []
52
+ let current = ranges[0]
53
+
54
+ for (let i = 1; i < ranges.length; i += 1) {
55
+ const nxt = ranges[i]
56
+ if (nxt.min() > current.max() + 1) {
57
+ oranges.push(current)
58
+ current = nxt
59
+ } else if (nxt.max() > current.max()) {
60
+ current = new Range(current.min(), nxt.max())
61
+ }
62
+ }
63
+ oranges.push(current)
64
+
65
+ if (oranges.length === 1) {
66
+ return oranges[0]
67
+ }
68
+ return new Range(oranges)
69
+ }
70
+
71
+ public intersection(arg: Range): Range {
72
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
73
+ let s0 = this
74
+ let s1 = arg
75
+ const r0 = this.ranges()
76
+ const r1 = s1.ranges()
77
+ const l0 = r0.length
78
+
79
+ const l1 = r1.length
80
+ let i0 = 0
81
+
82
+ let i1 = 0
83
+ const or = []
84
+
85
+ while (i0 < l0 && i1 < l1) {
86
+ s0 = r0[i0]
87
+ s1 = r1[i1]
88
+ const lapMin = Math.max(s0.min(), s1.min())
89
+ const lapMax = Math.min(s0.max(), s1.max())
90
+ if (lapMax >= lapMin) {
91
+ or.push(new Range(lapMin, lapMax))
92
+ }
93
+ if (s0.max() > s1.max()) {
94
+ i1 += 1
95
+ } else {
96
+ i0 += 1
97
+ }
98
+ }
99
+
100
+ if (or.length === 0) {
101
+ throw new Error('found range of length 0')
102
+ }
103
+ if (or.length === 1) {
104
+ return or[0]
105
+ }
106
+ return new Range(or)
107
+ }
108
+
109
+ public coverage(): number {
110
+ let tot = 0
111
+ const rl = this.ranges()
112
+ for (let ri = 0; ri < rl.length; ri += 1) {
113
+ const r = rl[ri]
114
+ tot += r.max() - r.min() + 1
115
+ }
116
+ return tot
117
+ }
118
+
119
+ public rangeOrder(tmpa: Range, tmpb: Range): number {
120
+ let a = tmpa
121
+ let b = tmpb
122
+ if (arguments.length < 2) {
123
+ b = a
124
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
125
+ a = this
126
+ }
127
+
128
+ if (a.min() < b.min()) {
129
+ return -1
130
+ }
131
+ if (a.min() > b.min()) {
132
+ return 1
133
+ }
134
+ if (a.max() < b.max()) {
135
+ return -1
136
+ }
137
+ if (b.max() > a.max()) {
138
+ return 1
139
+ }
140
+ return 0
141
+ }
142
+ }
@@ -0,0 +1,5 @@
1
+ import { inflateRaw } from 'pako'
2
+
3
+ export function unzip(input: Buffer) {
4
+ return inflateRaw(input.subarray(2))
5
+ }
package/src/unzip.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { inflateSync } from 'zlib'
2
+ export { inflateSync as unzip }