commit 1165c19c8ea7c38a30f94b34e70d9b844292bf86
Author: Brooke Vibber <bvibber@pobox.com>
Date:   Fri Aug 23 20:52:21 2024 -0700

    clone from dither4

diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..648b2eb
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,19 @@
+all : rickroll.xex
+
+# reminder: $< is input
+#           $@ is output
+
+%.s : %.jpg dither-image.js
+	node dither-image.js $< $@
+
+%.o : %.s
+	ca65 -v -t atari -o $@ $<
+
+%.xex : %.o mono8.o atari-asm-xex.cfg
+	ld65 -v -C ./atari-asm-xex.cfg -o $@ mono8.o $<
+
+clean :
+	rm -f *.xex
+	rm -f *.o
+	rm -f *.s.png
+	rm -f rickroll.s
diff --git a/atari-asm-xex.cfg b/atari-asm-xex.cfg
new file mode 100644
index 0000000..9727725
--- /dev/null
+++ b/atari-asm-xex.cfg
@@ -0,0 +1,26 @@
+FEATURES {
+    STARTADDRESS: default = $2E00;
+}
+SYMBOLS {
+    __STARTADDRESS__: type = export, value = %S;
+}
+MEMORY {
+    ZP:      file = "", define = yes, start = $0082, size = $007E;
+    MAIN:    file = %O, define = yes, start = %S,    size = $BC20 - %S;
+}
+FILES {
+    %O: format = atari;
+}
+FORMATS {
+    atari: runad = start;
+}
+SEGMENTS {
+    ZEROPAGE: load = ZP,      type = zp,  optional = yes;
+    EXTZP:    load = ZP,      type = zp,  optional = yes; # to enable modules to be able to link to C and assembler programs
+    CODE:     load = MAIN,    type = rw,                  define = yes;
+    RODATA:   load = MAIN,    type = ro   optional = yes;
+    DATA:     load = MAIN,    type = rw   optional = yes;
+    BSS:      load = MAIN,    type = bss, optional = yes, define = yes;
+    AUDIO:    load = MAIN,    type = ro,  optional = yes, define = yes;
+    BUFFERS:  load = MAIN,    type = ro,  optional = yes, define = yes, align = 4096;
+}
diff --git a/dither-image.js b/dither-image.js
new file mode 100644
index 0000000..93fddd9
--- /dev/null
+++ b/dither-image.js
@@ -0,0 +1,1092 @@
+import {
+    writeFileSync
+} from 'fs';
+
+import Jimp from 'Jimp';
+
+
+function repeat(val, n) {
+    let arr = [];
+    for (let i = 0; i < n; i++) {
+        arr.push(val);
+    }
+    return arr;
+}
+
+function zeroes(n) {
+    return repeat(0, n);
+}
+
+function toLinear(val) {
+    // use a 2.4 gamma approximation
+    // this is BT.1886 compatible
+    // and simpler than sRGB
+    let unit = val / 255;
+    unit **= 2.4;
+    return unit * 255;
+}
+
+function fromLinear(val) {
+    let unit = val / 255;
+    unit **= (1 / 2.4);
+    return unit * 255;
+}
+
+function fromSRGB(val) {
+    val /= 255;
+    if (val <= 0.04045) {
+        val /= 12.92;
+    } else {
+        val = ((val + 0.055) / 1.055) ** 2.4;
+    }
+    val *= 255;
+    return val;
+}
+
+function toSRGB(val) {
+    val /= 255;
+    if (val <= 0.0031308) {
+        val *= 12.92;
+    } else {
+        val = (val * 1.055) ** (1.0 / 2.4) - 0.055;
+    }
+    val *= 255;
+    return val;
+}
+
+class RGB {
+    constructor(r, g, b) {
+        this.r = r;
+        this.g = g;
+        this.b = b;
+    }
+
+    clone() {
+        return new RGB(this.r, this.g, this.b);
+    }
+
+    static fromHex(val) {
+        let r = (val >>> 16) & 0xff;
+        let g = (val >>> 8) & 0xff;
+        let b = val & 0xff;
+        return new RGB(r,g,b);
+    }
+
+    static fromGTIA(val) {
+        // This seems off from what Atari800 does
+        // https://forums.atariage.com/topic/107853-need-the-256-colors/page/2/#comment-1312467
+        let cr = (val >> 4) & 15;
+        let lm = val & 15;
+        let crlv = cr ? 50 : 0;
+
+        /*
+        let phase = ((cr - 1) * 25 - 58) * (2 * Math.PI / 360);
+
+        let y = 255 * (lm + 1) / 16;
+        let i = crlv * Math.cos(phase);
+        let q = crlv * Math.sin(phase);
+
+        let r = y + 0.956 * i + 0.621 * q;
+        let g = y - 0.272 * i - 0.647 * q;
+        let b = y - 1.107 * i + 1.704 * q;
+        */
+
+        // PAL
+        let phase = ((cr - 1) * 25.7 - 15) * (2 * Math.PI / 360);
+
+        let y = 255 * (lm + 1) / 16;
+        let i = crlv * Math.cos(phase);
+        let q = crlv * Math.sin(phase);
+
+        let r = y + 0.956 * i + 0.621 * q;
+        let g = y - 0.272 * i - 0.647 * q;
+        let b = y - 1.107 * i + 1.704 * q;
+
+        return new RGB(r, g, b).clamp().fromSRGB();
+    }
+
+    map(callback) {
+        return new RGB(
+            callback(this.r),
+            callback(this.g),
+            callback(this.b)
+        );
+    }
+
+    fromNTSC() {
+        return this.map(toLinear);
+    }
+
+    toNTSC() {
+        return this.map(fromLinear);
+    }
+
+    fromSRGB() {
+        return this.map(fromSRGB);
+    }
+
+    toSRGB() {
+        return this.map(toSRGB);
+    }
+
+    clamp() {
+        return this.map((val) => {
+            if (val < 0) return 0;
+            if (val > 255) return 255;
+            return val;
+        });
+    }
+
+    inc(other) {
+        this.r += other.r;
+        this.g += other.g;
+        this.b += other.b;
+        return this;
+    }
+
+    add(other) {
+        return new RGB(
+            this.r + other.r,
+            this.g + other.g,
+            this.b + other.b
+        );
+    }
+
+    difference(other) {
+        return new RGB(
+            this.r - other.r,
+            this.g - other.g,
+            this.b - other.b
+        );
+    }
+
+    multiply(scalar) {
+        return new RGB(
+            this.r * scalar,
+            this.g * scalar,
+            this.b * scalar,
+        );
+    }
+
+    divide(scalar) {
+        return new RGB(
+            this.r / scalar,
+            this.g / scalar,
+            this.b / scalar,
+        );
+    }
+
+    magnitude() {
+        return Math.sqrt(this.magnitude2());
+    }
+
+    magnitude2() {
+        return this.r * this.r +
+            this.g * this.g +
+            this.b * this.b;
+    }
+
+    sum() {
+        return this.r + this.g + this.b;
+    }
+
+    lumaScale() {
+        return new RGB(
+            this.r * 0.299,
+            this.g * 0.586,
+            this.b * 0.114
+        );
+    }
+
+    luma() {
+        return this.lumaScale().sum();
+    }
+}
+
+/*
+// snarfed from https://lospec.com/palette-list/atari-8-bit-family-gtia
+// which was calculated with Retrospecs App's Atari 800 emulator
+let atariRGB = [
+    0x000000,
+    0x111111,
+    0x222222,
+    0x333333,
+    0x444444,
+    0x555555,
+    0x666666,
+    0x777777,
+    0x888888,
+    0x999999,
+    0xaaaaaa,
+    0xbbbbbb,
+    0xcccccc,
+    0xdddddd,
+    0xeeeeee,
+    0xffffff,
+    0x190700,
+    0x2a1800,
+    0x3b2900,
+    0x4c3a00,
+    0x5d4b00,
+    0x6e5c00,
+    0x7f6d00,
+    0x907e09,
+    0xa18f1a,
+    0xb3a02b,
+    0xc3b13c,
+    0xd4c24d,
+    0xe5d35e,
+    0xf7e46f,
+    0xfff582,
+    0xffff96,
+    0x310000,
+    0x3f0000,
+    0x531700,
+    0x642800,
+    0x753900,
+    0x864a00,
+    0x975b0a,
+    0xa86c1b,
+    0xb97d2c,
+    0xca8e3d,
+    0xdb9f4e,
+    0xecb05f,
+    0xfdc170,
+    0xffd285,
+    0xffe39c,
+    0xfff4b2,
+    0x420404,
+    0x4f0000,
+    0x600800,
+    0x711900,
+    0x822a0d,
+    0x933b1e,
+    0xa44c2f,
+    0xb55d40,
+    0xc66e51,
+    0xd77f62,
+    0xe89073,
+    0xf9a183,
+    0xffb298,
+    0xffc3ae,
+    0xffd4c4,
+    0xffe5da,
+    0x410103,
+    0x50000f,
+    0x61001b,
+    0x720f2b,
+    0x83203c,
+    0x94314d,
+    0xa5425e,
+    0xb6536f,
+    0xc76480,
+    0xd87591,
+    0xe986a2,
+    0xfa97b3,
+    0xffa8c8,
+    0xffb9de,
+    0xffcaef,
+    0xfbdcf6,
+    0x330035,
+    0x440041,
+    0x55004c,
+    0x660c5c,
+    0x771d6d,
+    0x882e7e,
+    0x993f8f,
+    0xaa50a0,
+    0xbb61b1,
+    0xcc72c2,
+    0xdd83d3,
+    0xee94e4,
+    0xffa5e4,
+    0xffb6e9,
+    0xffc7ee,
+    0xffd8f3,
+    0x1d005c,
+    0x2e0068,
+    0x400074,
+    0x511084,
+    0x622195,
+    0x7332a6,
+    0x8443b7,
+    0x9554c8,
+    0xa665d9,
+    0xb776ea,
+    0xc887eb,
+    0xd998eb,
+    0xe9a9ec,
+    0xfbbaeb,
+    0xffcbef,
+    0xffdff9,
+    0x020071,
+    0x13007d,
+    0x240b8c,
+    0x351c9d,
+    0x462dae,
+    0x573ebf,
+    0x684fd0,
+    0x7960e1,
+    0x8a71f2,
+    0x9b82f7,
+    0xac93f7,
+    0xbda4f7,
+    0xceb5f7,
+    0xdfc6f7,
+    0xf0d7f7,
+    0xffe8f8,
+    0x000068,
+    0x000a7c,
+    0x081b90,
+    0x192ca1,
+    0x2a3db2,
+    0x3b4ec3,
+    0x4c5fd4,
+    0x5d70e5,
+    0x6e81f6,
+    0x7f92ff,
+    0x90a3ff,
+    0xa1b4ff,
+    0xb2c5ff,
+    0xc3d6ff,
+    0xd4e7ff,
+    0xe5f8ff,
+    0x000a4d,
+    0x001b63,
+    0x002c79,
+    0x023d8f,
+    0x134ea0,
+    0x245fb1,
+    0x3570c2,
+    0x4681d3,
+    0x5792e4,
+    0x68a3f5,
+    0x79b4ff,
+    0x8ac5ff,
+    0x9bd6ff,
+    0xace7ff,
+    0xbdf8ff,
+    0xceffff,
+    0x001a26,
+    0x002b3c,
+    0x003c52,
+    0x004d68,
+    0x065e7c,
+    0x176f8d,
+    0x28809e,
+    0x3991af,
+    0x4aa2c0,
+    0x5bb3d1,
+    0x6cc4e2,
+    0x7dd5f3,
+    0x8ee6ff,
+    0x9ff7ff,
+    0xb0ffff,
+    0xc1ffff,
+    0x01250a,
+    0x023610,
+    0x004622,
+    0x005738,
+    0x05684d,
+    0x16795e,
+    0x278a6f,
+    0x389b80,
+    0x49ac91,
+    0x5abda2,
+    0x6bceb3,
+    0x7cdfc4,
+    0x8df0d5,
+    0x9effe5,
+    0xaffff1,
+    0xc0fffd,
+    0x04260d,
+    0x043811,
+    0x054713,
+    0x005a1b,
+    0x106b1b,
+    0x217c2c,
+    0x328d3d,
+    0x439e4e,
+    0x54af5f,
+    0x65c070,
+    0x76d181,
+    0x87e292,
+    0x98f3a3,
+    0xa9ffb3,
+    0xbaffbf,
+    0xcbffcb,
+    0x00230a,
+    0x003510,
+    0x044613,
+    0x155613,
+    0x266713,
+    0x377813,
+    0x488914,
+    0x599a25,
+    0x6aab36,
+    0x7bbc47,
+    0x8ccd58,
+    0x9dde69,
+    0xaeef7a,
+    0xbfff8b,
+    0xd0ff97,
+    0xe1ffa3,
+    0x001707,
+    0x0e2808,
+    0x1f3908,
+    0x304a08,
+    0x415b08,
+    0x526c08,
+    0x637d08,
+    0x748e0d,
+    0x859f1e,
+    0x96b02f,
+    0xa7c140,
+    0xb8d251,
+    0xc9e362,
+    0xdaf473,
+    0xebff82,
+    0xfcff8e,
+    0x1b0701,
+    0x2c1801,
+    0x3c2900,
+    0x4d3b00,
+    0x5f4c00,
+    0x705e00,
+    0x816f00,
+    0x938009,
+    0xa4921a,
+    0xb2a02b,
+    0xc7b43d,
+    0xd8c64e,
+    0xead760,
+    0xf6e46f,
+    0xfffa84,
+    0xffff99,
+].map((hex) => RGB.fromHex(hex).fromNTSC());
+//].map((hex) => RGB.fromHex(hex));
+*/
+
+let atariRGB = [];
+for (let i = 0; i < 256; i++) {
+    atariRGB[i] = RGB.fromGTIA(i);
+}
+
+
+
+/**
+ * Dither RGB input data with a target palette size.
+ * If the number of used colors exceeds `n`, the
+ * palette will be reduced until it fits.
+ * @param {RGB[]} input source scanline data, in linear RGB
+ * @param {number[]} palette - current working palette, as Atari 8-bit color values (low nybble luminance, high nybble hue)
+ * @param {number} n - target color count
+ * @param {number} y
+ * @returns {{output: number[], palette: number[], error: RGB[]}}
+ */
+function decimate(input, palette, n) {
+
+    let width = input.length;
+
+    let inputPixel = (x, error) => {
+        let rgb = input[x].clone();
+        if (error) {
+            rgb = rgb.add(error.cur[x]);
+        }
+        return rgb.clamp();
+    };
+
+    // Apply dithering with given palette and collect color usage stats
+    let dither = (palette) => {
+        let error = {
+            cur: [],
+            next: [],
+        };
+        for (let i = 0; i < width; i++) {
+            error.cur[i] = new RGB(0, 0, 0);
+            error.next[i] = new RGB(0, 0, 0);
+        }
+
+        let output = zeroes(width);
+        let popularity = zeroes(palette.length);
+        let distance2 = 0;
+
+        let nextError = new RGB(0, 0, 0);
+
+        // Try dithering with this palette.
+        for (let x = 0; x < width; x++) {
+            let rgb = inputPixel(x, error);
+
+            // find the closest possible color
+            // @todo consider doing the difference scoring in luminance and hue spaces
+            let shortest = Infinity;
+            let pick = 1;
+
+            for (let i = 0; i < palette.length; i++) {
+                let diff = rgb.difference(atariRGB[palette[i]]);
+                let dist = diff.magnitude();
+                if (dist < shortest) {
+                    nextError = diff;
+                    shortest = dist;
+                    pick = i;
+                }
+            }
+
+            output[x] = pick;
+            popularity[pick]++;
+
+            let share = (n) => nextError.multiply(n / 16);
+
+            error.cur[x + 1]?.inc(share(7));
+            error.next[x - 1]?.inc(share(3));
+            error.next[x    ]?.inc(share(5));
+            error.next[x + 1]?.inc(share(1));
+        }
+        return {
+            output,
+            palette,
+            distance2,
+            popularity,
+            error: error.next
+        };
+    };
+
+
+    let decimated = palette.slice();
+    
+    // force to grayscale
+    //decimated = [0, 5, 10, 15];
+
+    // force to rgb
+    //decimated = [0, 0x36, 0xb6, 0x86];
+
+    // force to rWb
+    //decimated = [0, 0x36, 0x0f, 0x86];
+    
+    let reserved = [0]; // black
+    //reserved = [0, 15]; // black, white
+    //reserved = [0, 5, 10, 15]; // grayscale
+    //reserved = [0, 0x48, 0x78, 15]; // vaporwave
+    //reserved = [0, 0x3c, 0x78, 15]; // red/blue/white
+
+    /*
+    if (( y & 1 ) === 0) {
+        reserved = [0, 0x3c, 0x1e, 15]; // red/yellow/white
+    } else {
+        reserved = [0, 0x76, 0x9e, 0xb8]; // blue/cyan/green
+    }
+    */
+
+    let keepers = zeroes(256);
+    for (let i of reserved) {
+        keepers[i & 0xfe] = 1; // drop that 0 luminance bit!
+    }
+
+    // Median cut!
+    // https://en.wikipedia.org/wiki/Median_cut
+    //let buckets = [input.slice()];
+
+    // preface the reserved bits
+    let buckets = reserved.slice().map((c) => [atariRGB[c]]).concat([input.slice()]);
+    if (input.length != 160) {
+        throw new Error('xxx bad input size');
+    }
+
+    /*
+    let buckets = [input.slice()];
+    if (reserved.length > 0) {
+        let pxPerReserved = input.length;
+        for (let c of reserved) {
+            for (let i = 0; i < pxPerReserved; i++) {
+                buckets[0].unshift(atariRGB[c]);
+            }
+        }
+        console.log(buckets[0].length, 'xxx');
+    }
+    */
+
+    let magicSort = (picker) => (a, b) => {
+        let bychannel = picker(b) - picker(a);
+        if (bychannel) return bychannel;
+
+        let byluma = b.luma() - a.luma();
+        return byluma;
+    };
+    let medianCut = (bucket, range) => {
+        if (bucket.length < 2) {
+            throw new Error('short bucket');
+        }
+        //console.log('medianCut', bucket, range);
+        // Sort by the channel with the greatest range,
+        // then cut the bucket in two at the median.
+        if (range.g >= range.r && range.g >= range.b) {
+            //bucket.sort((a, b) => b.g - a.g);
+            bucket.sort(magicSort((rgb) => rgb.g));
+        } else if (range.r >= range.g && range.r >= range.b) {
+            //bucket.sort((a, b) => b.r - a.r);
+            bucket.sort(magicSort((rgb) => rgb.r));
+        } else if (range.b >= range.g && range.b >= range.r) {
+            //bucket.sort((a, b) => b.b - a.b);
+            bucket.sort(magicSort((rgb) => rgb.b));
+        }
+        let half = bucket.length >> 1;
+        //console.log('cutting', half, bucket.length);
+        let [bottom, top] = [bucket.slice(0, half), bucket.slice(half)];
+        //console.log({bottom, top});
+        return [bottom, top];
+        //return [bucket.slice(0, half), bucket.slice(half)];
+    };
+    while (buckets.length < n) {
+        // Find the bucket with the greatest range in any channel
+        let ranges = buckets.map((bucket) => {
+            if (bucket.length == 0) {
+                throw new Error('xxx empty bucket');
+            }
+            let red   = bucket.map((rgb) => rgb.r);
+            let green = bucket.map((rgb) => rgb.g);
+            let blue  = bucket.map((rgb) => rgb.b);
+            return new RGB(
+                Math.max(...red)   - Math.min(...red),
+                Math.max(...green) - Math.min(...green),
+                Math.max(...blue)  - Math.min(...blue)
+            );
+        });
+        let topRanges = ranges.map((rgb) => Math.max(rgb.r, rgb.g, rgb.b));
+        //let greatest = Math.max(...topRanges);
+        //let index = topRanges.indexOf(greatest);
+        let greatest = 0;
+        let index = -1;
+        for (let i = 0; i < topRanges.length; i++) {
+            //if (topRanges[i] >= greatest) {
+            if (topRanges[i] > greatest) {
+                greatest = topRanges[i];
+                index = i;
+            }
+        }
+        if (index == -1) {
+            // We just ran out of colors! Pad the buckets.
+            //while (buckets.length < n) {
+            //    buckets.push([new RGB(0, 0, 0)]);
+            //}
+            break;
+        }
+        let [lo, hi] = medianCut(buckets[index], ranges[index]);
+        buckets.splice(index, 1, lo, hi);
+    }
+
+    if (buckets.length > n) {
+        throw new Error('xxx too many colors assigned');
+    }
+    decimated = buckets.map((bucket) => {
+        // Average the RGB colors in this chunk
+        let rgb = bucket
+            .reduce((acc, rgb) => acc.inc(rgb), new RGB(0, 0, 0))
+            .divide(bucket.length);
+
+        // Scale the average to the brightest
+        let avg_luma = rgb.luma();
+        let lumas = bucket.map((rgb) => rgb.luma());
+        let brightest = Math.max(...lumas);
+        if (avg_luma > 0) {
+            rgb = rgb.multiply(brightest / avg_luma).clamp();
+        }
+
+        // this also works pretty ok
+        // but i think keeping luma is better
+        //
+        /*
+        // 1) take the brightest luma
+        // 2) take the most saturated chroma
+        // 3) profit!
+        let lumas = bucket.map((rgb) => rgb.luma());
+        let brightest = Math.max(...lumas);
+        let saturations = bucket.map((rgb) => Math.max(rgb.r, rgb.g, rgb.b) - Math.min(rgb.r, rgb.g, rgb.b));
+        let saturation = Math.max(...saturations);
+        let saturatedIndex = saturations.indexOf(saturation);
+        let rgb = bucket[saturatedIndex];
+        let luma = rgb.luma();
+        if (luma > 0) {
+            rgb = rgb.multiply(brightest / luma).clamp();
+        }
+        */
+    
+        // pick the luma-brightest color in the bucket
+        // kinda nice but really aggressive with the colors
+        /*
+        let lumas = bucket.map((rgb) => rgb.luma());
+        let luma = Math.max(...lumas);
+        let rgb = bucket[lumas.indexOf(luma)];
+        */
+
+        // Take the channel-brightest color in the bucket
+        // bad
+        //let rgb = bucket[bucket.length - 1];
+
+        // Take the luma-brightest color in the bucket
+        // wrong? bad
+        //let rgb = bucket.slice().sort((a, b) => b.luma() - a.luma())[bucket.length - 1];
+
+        // Take the median color in the bucket
+        // bad
+        //let rgb = bucket[bucket.length >> 1];
+
+        // Combine the brightest of each channel
+        // this is kinda good
+        /*
+        let rgb = new RGB(
+            Math.max(...bucket.map((rgb) => rgb.r)),
+            Math.max(...bucket.map((rgb) => rgb.g)),
+            Math.max(...bucket.map((rgb) => rgb.b))
+        );
+        */
+
+        // combine the median of each channel
+        // sux
+        /*
+        let rgb = new RGB(
+            bucket.map((rgb) => rgb.r).sort((a, b) => b - a)[bucket.length >> 1],
+            bucket.map((rgb) => rgb.g).sort((a, b) => b - a)[bucket.length >> 1],
+            bucket.map((rgb) => rgb.b).sort((a, b) => b - a)[bucket.length >> 1]
+        );
+        */
+
+        // Take the luma-median color in the bucket
+        //let rgb = bucket.slice().sort((a, b) => b.luma() - a.luma())[bucket.length >> 1];
+
+        // Take the brightest-channel median
+        //let rgb = bucket.slice()
+        //    .sort((a, b) => Math.max(b.r, b.g, b.b) - Math.max(a.r, b.g, b.b))[bucket.length >> 1];
+
+        // And map into the Atari palette
+        let dists = palette.map((i) => rgb.difference(atariRGB[i]).magnitude());
+        let closest = Math.min(...dists);
+        let index = dists.indexOf(closest);
+        return palette[index];
+    });
+    decimated.sort((a, b) => a - b);
+
+    // Palette fits
+    return dither(decimated);
+}
+
+/**
+ * Read an image file into a buffer
+ * @param {string} src 
+ * @returns {{width: number, height: number, rgba: Uint8Array}}
+ */
+async function loadImage(src) {
+    let image = await Jimp.read(src);
+
+    let width = image.bitmap.width;
+    let height = image.bitmap.height;
+
+    let aspect = width / height;
+    let dar = 2 / 1.2;
+    if (aspect > ((320 / 1.2) / 192)) {
+        // wide
+        width = 160;
+        height = Math.round((width * image.bitmap.height / image.bitmap.width) * dar);
+        if (height & 1) {
+            height++;
+        }
+    } else {
+        // tall
+        height = 192;
+        width = Math.round((height * image.bitmap.width / image.bitmap.height) / dar);
+        if (width & 1) {
+            width++;
+        }
+    }
+    image = image.resize(width, height);
+
+    let rgba = image.bitmap.data.slice();
+    return {
+        width,
+        height,
+        rgba,
+    };
+}
+
+function imageToLinearRGB(rgba) {
+    let input = [];
+    for (let i = 0; i < rgba.length; i += 4) {
+        input.push(new RGB(
+            rgba[i + 0],
+            rgba[i + 1],
+            rgba[i + 2]
+        ).fromSRGB());
+    }
+    return input;
+}
+
+/**
+ * Read an image file, squish to 160px if necessary,
+ * and dither to 4 colors per scan line.
+ *
+ * @param {string} source path to source image file
+ * @returns {{width: number, height: number, lines: {palette: Array, output: Uint8Array}[]}}
+ */
+async function convert(source) {
+
+    let {
+        width,
+        height,
+        rgba
+    } = await loadImage(source);
+
+    if (width > 160) {
+        throw new Error(`expected <160px width, got ${width} pixels`);
+    }
+
+    if (height > 192) {
+        throw new Error(`expected <192px height, got ${height} pixels`);
+    }
+
+    if (rgba.length != width * 4 * height) {
+        console.log(`
+        width: ${width}
+        height: ${height}
+        rgba.length: ${rgba.length}`)
+        throw new Error('inconsistent data size');
+    }
+
+    let input = imageToLinearRGB(rgba);
+
+    if (input.length != width * height) {
+        console.log(`
+        width: ${width}
+        height: ${height}
+        rgba.length: ${input.length}`)
+        throw new Error('inconsistent data size on input');
+    }
+
+    // Start with all colors usable with regular CTIA modes
+    // (not the 16-luminance special mode on GTIA)
+    let allColors = [];
+    for (let i = 0; i < 256; i += 2) {
+        allColors.push(i);
+    }
+
+    let left = [], right = [];
+    let padding = 0;
+    if (width < 160) {
+        padding = 160 - width;
+
+        let black = new RGB(0, 0, 0);
+        left = repeat(black, padding >> 1);
+        right = repeat(black, padding + 1 >> 1);
+    }
+
+    let lines = [];
+    for (let y = 0; y < height; y++) {
+        let inputLine = input
+            .slice(y * width, (y + 1) * width);
+
+        if (padding) {
+            inputLine = left.concat(inputLine, right);
+        }
+        if (y > 0) {
+            let error = lines[y - 1].error;
+            inputLine = inputLine.map((rgb, x) => rgb.add(error[x]).clamp());
+        }
+        let line = decimate(inputLine, allColors, 4, y);
+        lines.push(line);
+    }
+    return {
+        width: width + padding,
+        height,
+        lines
+    };
+}
+
+
+function indexedToBitmap(width, nbits, src, dest) {
+    let nbytes = width * nbits / 8;
+    let x = 0;
+    for (let i = 0; i < nbytes; i++) {
+        let a = 0;
+        for (let b = 0; b < 8; b += nbits) {
+            a <<= nbits;
+            a |= src[x++];
+        }
+        dest[i] = a;
+    }
+}
+
+function byte2byte(arr) {
+    let lines = [];
+    for (let i=0; i < arr.length; i++) {
+        lines.push(`.byte ${arr[i]}`);
+    }
+    return lines.join('\n');
+}
+
+//let [even, odd] = [0, 1].map((bit) => (arr) => arr.filter((_item, index) => (index & 1) === bit));
+function even(arr) {
+    return arr.filter((_item, index) => !(index & 1));
+}
+function odd(arr) {
+    return arr.filter((_item, index) => (index & 1));
+}
+
+function genAssembly(width, height, nbits, lines) {
+    let stride = width * nbits / 8;
+    let half = stride * height / 2;
+    let frame = {
+        palette1: new Uint8Array(height),
+        palette2: new Uint8Array(height),
+        palette3: new Uint8Array(height),
+        bitmap: new Uint8Array(stride * height),
+    };
+    for (let y = 0; y < height; y++) {
+        let base = 0;
+        frame.palette1[y] = lines[y + base].palette[1];
+        frame.palette2[y] = lines[y + base].palette[2];
+        frame.palette3[y] = lines[y + base].palette[3];
+        indexedToBitmap(
+            width,
+            nbits,
+            lines[y + base].output,
+            frame.bitmap.subarray(y * stride, (y + 1) * stride)
+        );
+    }
+
+    return `.data
+.export frame1_top
+.export frame1_bottom
+
+.export frame1_palette1_even
+.export frame1_palette1_odd
+.export frame1_palette2_even
+.export frame1_palette2_odd
+.export frame1_palette3_even
+.export frame1_palette3_odd
+
+.export displaylist
+
+.segment "BUFFERS"
+
+.align 4096
+frame1_top:
+${byte2byte(frame.bitmap.slice(0, half))}
+
+.align 4096
+frame1_bottom:
+${byte2byte(frame.bitmap.slice(half))}
+
+.align 128
+frame1_palette1_even:
+${byte2byte(even(frame.palette1))}
+
+.align 128
+frame1_palette1_odd:
+${byte2byte(odd(frame.palette1))}
+
+.align 128
+frame1_palette2_even:
+${byte2byte(even(frame.palette2))}
+
+.align 128
+frame1_palette2_odd:
+${byte2byte(odd(frame.palette2))}
+
+.align 128
+frame1_palette3_even:
+${byte2byte(even(frame.palette3))}
+
+.align 128
+frame1_palette3_odd:
+${byte2byte(odd(frame.palette3))}
+
+
+
+
+.align 1024
+displaylist:
+    ; 24 lines overscan
+    .repeat 2
+        .byte $70 ; 8 blank lines
+    .endrep
+    ; include a DLI to mark us as frame 0
+    .byte $f0 ; 8 blank lines
+
+    ; ${height} lines graphics
+    ; ANTIC mode e (160px 2bpp, 1 scan line per line)
+    .byte $4e
+    .addr frame1_top
+    .repeat ${height / 2 - 1}
+        .byte $0e
+    .endrep
+    .byte $4e
+    .addr frame1_bottom
+    .repeat ${height / 2 - 1}
+        .byte $0e
+    .endrep
+
+    .byte $41 ; jump and blank
+    .addr displaylist
+`;
+}
+
+/**
+ * Double width and save as an image file
+ * @param {number} width 
+ * @param {number} height 
+ * @param {{output: number[], palette: number[]}} lines 
+ * @param {string} dest 
+ */
+async function saveImage(width, height, lines, dest) {
+    let width2 = width * 2;
+    let stride = width2 * 4;
+    let rgba = new Uint8Array(stride * height);
+
+    for (let y = 0; y < height; y++) {
+        let {output, palette} = lines[y];
+        for (let x = 0; x < width2; x++) {
+            let i = x >> 1;
+            if (i >= width) {
+                throw new Error('i >= width');
+            }
+            //let rgb = atariRGB[palette[output[i]]].fromLinear();
+            let rgb = atariRGB[palette[output[i]]].toSRGB();
+
+            rgba[y * stride + x * 4 + 0] = rgb.r;
+            rgba[y * stride + x * 4 + 1] = rgb.g;
+            rgba[y * stride + x * 4 + 2] = rgb.b;
+            rgba[y * stride + x * 4 + 3] = 255;
+        }
+    }
+
+    let image = await new Promise((resolve, reject) => {
+        new Jimp({
+            data: rgba,
+            width: width2,
+            height,
+        }, (err, image) => {
+            if (err) reject(err);
+            resolve(image);
+        });
+    });
+    await image.resize(Math.round(width2 * 2 / 1.2), height * 2);
+    await image.writeAsync(dest);
+}
+
+async function main() {
+    if (process.argv.length < 3) {
+        console.error("Usage: node dither-image.js source-image.jpg dest-asm.s");
+        process.exit(1);
+    }
+
+    let nbits = 2;
+
+    let {width, height, lines} = await convert(process.argv[2], nbits);
+
+    let asm = genAssembly(width, height, nbits, lines);
+    writeFileSync(process.argv[3], asm, "utf-8");
+
+    await saveImage(width, height, lines, `${process.argv[3]}.png`);
+
+    process.exit(0);
+}
+
+main();
\ No newline at end of file
diff --git a/mono8.s b/mono8.s
new file mode 100644
index 0000000..fd4c0ec
--- /dev/null
+++ b/mono8.s
@@ -0,0 +1,159 @@
+SAVMSC = $58
+VDSLST = $200
+VDSLSTL = $200
+VDSLSTH = $201
+COLPF0 = $D016
+COLPF1 = $D017
+COLPF2 = $D018
+COLPF3 = $D019
+COLBK = $D01A
+
+AUDC1 = $D201
+DMACTL = $D400
+DLISTL = $D402
+DLISTH = $D403
+WSYNC = $D40A
+VCOUNT = $D40B
+NMIEN = $D40E
+
+temp1l = $80
+temp1h = $81
+temp1 = temp1l
+temp2l = $82
+temp2h = $83
+temp2 = temp2l
+sample_ptrl = $84
+sample_ptrh = $85
+sample_ptr = sample_ptrl
+scanline = $86
+frame_counter = $89
+
+;height = 160
+height = 192
+bytes_per_line = 40
+pages_per_frame = 32
+lines_per_frame = 262
+;scanline_offset = 31 + (40 - 24) / 2
+scanline_offset = 30
+scanline_max = (lines_per_frame - scanline_offset) / 2
+
+.data
+
+.import frame1_top
+.import frame1_bottom
+.import frame1_palette1_even
+.import frame1_palette1_odd
+.import frame1_palette2_even
+.import frame1_palette2_odd
+.import frame1_palette3_even
+.import frame1_palette3_odd
+.import displaylist
+
+.code
+
+.export start
+
+.proc start
+    ; Disable display DMA
+    lda #$00
+    sta DMACTL
+
+    ; Disable VBI and DLI but allow Reset
+    lda #$20
+    sta NMIEN
+
+    ; Set up the display list
+    lda #.lobyte(displaylist)
+    sta DLISTL
+    lda #.hibyte(displaylist)
+    sta DLISTH
+
+    ; Set up the DLI handler
+    lda #.lobyte(dli_handler)
+    sta VDSLSTL
+    lda #.hibyte(dli_handler)
+    sta VDSLSTH
+
+    ; Disable VBI but allow Reset and DLI
+    lda #$a0
+    sta NMIEN
+
+    ; Manually wait for first scan line
+wait_vblank:
+    sta WSYNC
+    lda VCOUNT
+    bne wait_vblank
+
+    ; Re-enable display DMA
+    lda #$22
+    sta DMACTL
+
+wait_start:
+    ; Wait for the vblank
+    ; Resynchronize the scanline counter
+wait_loop:
+    ldy VCOUNT    ; 4 cycles
+    bne wait_loop ; 2 cycles
+
+    .macro inner_scanline frame_offset, line_offset
+        ; Y should be VCOUNT at entry
+        ; it'll fire on unused lines, but harmlessly
+        ;ldy scanline ; 3 cyc
+        ;inc scanline ; 5 cyc
+
+        ; 23-26 cycles before break
+        ; Leisurely memory fetches
+        lda frame1_palette1_even + frame_offset + line_offset - scanline_offset / 2,y ; 4/5 @FIXME alternate
+        pha                                                                           ; 3
+        ldx frame1_palette2_even + frame_offset + line_offset - scanline_offset / 2,y ; 4/5
+        lda frame1_palette3_even + frame_offset + line_offset - scanline_offset / 2,y ; 4/5
+        tay                                                                           ; 2
+        pla                                                                           ; 3
+        ; Wait for horizontal blank
+        sta WSYNC                                                                     ; 4
+
+        ; 12 cycles after break
+        ; Update color registers as fast as possible
+        sta COLPF0 ; 4
+        stx COLPF1 ; 4
+        sty COLPF2 ; 4
+    .endmacro
+
+    .macro run_frame frame_offset
+    .scope
+        ; each scanline is 228 color clocks
+        ; that's 114 CPU cycles
+        ; minus 41-43 for DMA leaves 71-73 clock cycles per line
+
+    each_scanline_pair:
+        sty scanline ; 3 cycles
+        inner_scanline frame_offset, 0 ; 23-26 cycles before break, 12 cycles after
+
+        ldy scanline   ; 3 cycles
+        inner_scanline frame_offset, 128 ; 23-26 cycles before break, 12 cycles after
+
+        ; pair cleanup: 6 cycles
+        ldy VCOUNT             ; 4 cycles
+        bne each_scanline_pair ; 2 cycles
+
+        ; frame cleanup: 11 cycles
+        lda frame_counter ; 3 cycles
+        eor #1            ; 2 cycles
+        sta frame_counter ; 3 cycles
+        ;jmp wait_start    ; 3 cycles
+        jmp each_frame    ; 3 cycles
+    .endscope
+    .endmacro
+
+each_frame:
+run_frame1:
+    run_frame 0
+
+.endproc
+
+
+.proc dli_handler
+    lda #0
+    sta frame_counter
+    rti
+.endproc
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..b9171ac
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,2267 @@
+{
+  "name": "mono8",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "dependencies": {
+        "jimp": "^0.16.2",
+        "wavefile": "^11.0.0"
+      },
+      "devDependencies": {
+        "eslint": "^8.36.0"
+      }
+    },
+    "node_modules/@babel/runtime": {
+      "version": "7.25.4",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz",
+      "integrity": "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==",
+      "license": "MIT",
+      "dependencies": {
+        "regenerator-runtime": "^0.14.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/runtime/node_modules/regenerator-runtime": {
+      "version": "0.14.1",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+      "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
+      "license": "MIT"
+    },
+    "node_modules/@eslint-community/eslint-utils": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+      "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "eslint-visitor-keys": "^3.3.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "peerDependencies": {
+        "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+      }
+    },
+    "node_modules/@eslint-community/regexpp": {
+      "version": "4.11.0",
+      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz",
+      "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+      }
+    },
+    "node_modules/@eslint/eslintrc": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+      "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ajv": "^6.12.4",
+        "debug": "^4.3.2",
+        "espree": "^9.6.0",
+        "globals": "^13.19.0",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^4.1.0",
+        "minimatch": "^3.1.2",
+        "strip-json-comments": "^3.1.1"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@eslint/js": {
+      "version": "8.57.0",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
+      "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/@humanwhocodes/config-array": {
+      "version": "0.11.14",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
+      "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
+      "deprecated": "Use @eslint/config-array instead",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@humanwhocodes/object-schema": "^2.0.2",
+        "debug": "^4.3.1",
+        "minimatch": "^3.0.5"
+      },
+      "engines": {
+        "node": ">=10.10.0"
+      }
+    },
+    "node_modules/@humanwhocodes/module-importer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+      "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=12.22"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
+    "node_modules/@humanwhocodes/object-schema": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+      "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
+      "deprecated": "Use @eslint/object-schema instead",
+      "dev": true,
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@jimp/bmp": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.16.13.tgz",
+      "integrity": "sha512-9edAxu7N2FX7vzkdl5Jo1BbACfycUtBQX+XBMcHA2bk62P8R0otgkHg798frgAk/WxQIzwxqOH6wMiCwrlAzdQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13",
+        "bmp-js": "^0.1.0"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/core": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.16.13.tgz",
+      "integrity": "sha512-qXpA1tzTnlkTku9yqtuRtS/wVntvE6f3m3GNxdTdtmc+O+Wcg9Xo2ABPMh7Nc0AHbMKzwvwgB2JnjZmlmJEObg==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13",
+        "any-base": "^1.1.0",
+        "buffer": "^5.2.0",
+        "exif-parser": "^0.1.12",
+        "file-type": "^16.5.4",
+        "load-bmfont": "^1.3.1",
+        "mkdirp": "^0.5.1",
+        "phin": "^2.9.1",
+        "pixelmatch": "^4.0.2",
+        "tinycolor2": "^1.4.1"
+      }
+    },
+    "node_modules/@jimp/custom": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.16.13.tgz",
+      "integrity": "sha512-LTATglVUPGkPf15zX1wTMlZ0+AU7cGEGF6ekVF1crA8eHUWsGjrYTB+Ht4E3HTrCok8weQG+K01rJndCp/l4XA==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/core": "^0.16.13"
+      }
+    },
+    "node_modules/@jimp/gif": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.16.13.tgz",
+      "integrity": "sha512-yFAMZGv3o+YcjXilMWWwS/bv1iSqykFahFMSO169uVMtfQVfa90kt4/kDwrXNR6Q9i6VHpFiGZMlF2UnHClBvg==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13",
+        "gifwrap": "^0.9.2",
+        "omggif": "^1.0.9"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/jpeg": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.16.13.tgz",
+      "integrity": "sha512-BJHlDxzTlCqP2ThqP8J0eDrbBfod7npWCbJAcfkKqdQuFk0zBPaZ6KKaQKyKxmWJ87Z6ohANZoMKEbtvrwz1AA==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13",
+        "jpeg-js": "^0.4.2"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-blit": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.16.13.tgz",
+      "integrity": "sha512-8Z1k96ZFxlhK2bgrY1JNWNwvaBeI/bciLM0yDOni2+aZwfIIiC7Y6PeWHTAvjHNjphz+XCt01WQmOYWCn0ML6g==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-blur": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.16.13.tgz",
+      "integrity": "sha512-PvLrfa8vkej3qinlebyhLpksJgCF5aiysDMSVhOZqwH5nQLLtDE9WYbnsofGw4r0VVpyw3H/ANCIzYTyCtP9Cg==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-circle": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-0.16.13.tgz",
+      "integrity": "sha512-RNave7EFgZrb5V5EpdvJGAEHMnDAJuwv05hKscNfIYxf0kR3KhViBTDy+MoTnMlIvaKFULfwIgaZWzyhuINMzA==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-color": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.16.13.tgz",
+      "integrity": "sha512-xW+9BtEvoIkkH/Wde9ql4nAFbYLkVINhpgAE7VcBUsuuB34WUbcBl/taOuUYQrPEFQJ4jfXiAJZ2H/rvKjCVnQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13",
+        "tinycolor2": "^1.4.1"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-contain": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.16.13.tgz",
+      "integrity": "sha512-QayTXw4tXMwU6q6acNTQrTTFTXpNRBe+MgTGMDU0lk+23PjlFCO/9sacflelG8lsp7vNHhAxFeHptDMAksEYzg==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5",
+        "@jimp/plugin-blit": ">=0.3.5",
+        "@jimp/plugin-resize": ">=0.3.5",
+        "@jimp/plugin-scale": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-cover": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.16.13.tgz",
+      "integrity": "sha512-BSsP71GTNaqWRcvkbWuIVH+zK7b3TSNebbhDkFK0fVaUTzHuKMS/mgY4hDZIEVt7Rf5FjadAYtsujHN9w0iSYA==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5",
+        "@jimp/plugin-crop": ">=0.3.5",
+        "@jimp/plugin-resize": ">=0.3.5",
+        "@jimp/plugin-scale": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-crop": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.16.13.tgz",
+      "integrity": "sha512-WEl2tPVYwzYL8OKme6Go2xqiWgKsgxlMwyHabdAU4tXaRwOCnOI7v4021gCcBb9zn/oWwguHuKHmK30Fw2Z/PA==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-displace": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.16.13.tgz",
+      "integrity": "sha512-qt9WKq8vWrcjySa9DyQ0x/RBMHQeiVjdVSY1SJsMjssPUf0pS74qorcuAkGi89biN3YoGUgPkpqECnAWnYwgGA==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-dither": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.16.13.tgz",
+      "integrity": "sha512-5/N3yJggbWQTlGZHQYJPmQXEwR52qaXjEzkp1yRBbtdaekXE3BG/suo0fqeoV/csf8ooI78sJzYmIrxNoWVtgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-fisheye": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-0.16.13.tgz",
+      "integrity": "sha512-2rZmTdFbT/cF9lEZIkXCYO0TsT114Q27AX5IAo0Sju6jVQbvIk1dFUTnwLDadTo8wkJlFzGqMQ24Cs8cHWOliA==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-flip": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.16.13.tgz",
+      "integrity": "sha512-EmcgAA74FTc5u7Z+hUO/sRjWwfPPLuOQP5O64x5g4j0T12Bd29IgsYZxoutZo/rb3579+JNa/3wsSEmyVv1EpA==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5",
+        "@jimp/plugin-rotate": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-gaussian": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.16.13.tgz",
+      "integrity": "sha512-A1XKfGQD0iDdIiKqFYi8nZMv4dDVYdxbrmgR7y/CzUHhSYdcmoljLIIsZZM3Iks/Wa353W3vtvkWLuDbQbch1w==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-invert": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.16.13.tgz",
+      "integrity": "sha512-xFMrIn7czEZbdbMzZWuaZFnlLGJDVJ82y5vlsKsXRTG2kcxRsMPXvZRWHV57nSs1YFsNqXSbrC8B98n0E32njQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-mask": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.16.13.tgz",
+      "integrity": "sha512-wLRYKVBXql2GAYgt6FkTnCfE+q5NomM7Dlh0oIPGAoMBWDyTx0eYutRK6PlUrRK2yMHuroAJCglICTbxqGzowQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-normalize": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.16.13.tgz",
+      "integrity": "sha512-3tfad0n9soRna4IfW9NzQdQ2Z3ijkmo21DREHbE6CGcMIxOSvfRdSvf1qQPApxjTSo8LTU4MCi/fidx/NZ0GqQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-print": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.16.13.tgz",
+      "integrity": "sha512-0m6i3p01PGRkGAK9r53hDYrkyMq+tlhLOIbsSTmZyh6HLshUKlTB7eXskF5OpVd5ZUHoltlNc6R+ggvKIzxRFw==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13",
+        "load-bmfont": "^1.4.0"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5",
+        "@jimp/plugin-blit": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-resize": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.16.13.tgz",
+      "integrity": "sha512-qoqtN8LDknm3fJm9nuPygJv30O3vGhSBD2TxrsCnhtOsxKAqVPJtFVdGd/qVuZ8nqQANQmTlfqTiK9mVWQ7MiQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-rotate": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.16.13.tgz",
+      "integrity": "sha512-Ev+Jjmj1nHYw897z9C3R9dYsPv7S2/nxdgfFb/h8hOwK0Ovd1k/+yYS46A0uj/JCKK0pQk8wOslYBkPwdnLorw==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5",
+        "@jimp/plugin-blit": ">=0.3.5",
+        "@jimp/plugin-crop": ">=0.3.5",
+        "@jimp/plugin-resize": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-scale": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.16.13.tgz",
+      "integrity": "sha512-05POQaEJVucjTiSGMoH68ZiELc7QqpIpuQlZ2JBbhCV+WCbPFUBcGSmE7w4Jd0E2GvCho/NoMODLwgcVGQA97A==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5",
+        "@jimp/plugin-resize": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-shadow": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-shadow/-/plugin-shadow-0.16.13.tgz",
+      "integrity": "sha512-nmu5VSZ9hsB1JchTKhnnCY+paRBnwzSyK5fhkhtQHHoFD5ArBQ/5wU8y6tCr7k/GQhhGq1OrixsECeMjPoc8Zw==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5",
+        "@jimp/plugin-blur": ">=0.3.5",
+        "@jimp/plugin-resize": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/plugin-threshold": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-0.16.13.tgz",
+      "integrity": "sha512-+3zArBH0OE3Rhjm4HyAokMsZlIq5gpQec33CncyoSwxtRBM2WAhUVmCUKuBo+Lr/2/4ISoY4BWpHKhMLDix6cA==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5",
+        "@jimp/plugin-color": ">=0.8.0",
+        "@jimp/plugin-resize": ">=0.8.0"
+      }
+    },
+    "node_modules/@jimp/plugins": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.16.13.tgz",
+      "integrity": "sha512-CJLdqODEhEVs4MgWCxpWL5l95sCBlkuSLz65cxEm56X5akIsn4LOlwnKoSEZioYcZUBvHhCheH67AyPTudfnQQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/plugin-blit": "^0.16.13",
+        "@jimp/plugin-blur": "^0.16.13",
+        "@jimp/plugin-circle": "^0.16.13",
+        "@jimp/plugin-color": "^0.16.13",
+        "@jimp/plugin-contain": "^0.16.13",
+        "@jimp/plugin-cover": "^0.16.13",
+        "@jimp/plugin-crop": "^0.16.13",
+        "@jimp/plugin-displace": "^0.16.13",
+        "@jimp/plugin-dither": "^0.16.13",
+        "@jimp/plugin-fisheye": "^0.16.13",
+        "@jimp/plugin-flip": "^0.16.13",
+        "@jimp/plugin-gaussian": "^0.16.13",
+        "@jimp/plugin-invert": "^0.16.13",
+        "@jimp/plugin-mask": "^0.16.13",
+        "@jimp/plugin-normalize": "^0.16.13",
+        "@jimp/plugin-print": "^0.16.13",
+        "@jimp/plugin-resize": "^0.16.13",
+        "@jimp/plugin-rotate": "^0.16.13",
+        "@jimp/plugin-scale": "^0.16.13",
+        "@jimp/plugin-shadow": "^0.16.13",
+        "@jimp/plugin-threshold": "^0.16.13",
+        "timm": "^1.6.1"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/png": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.16.13.tgz",
+      "integrity": "sha512-8cGqINvbWJf1G0Her9zbq9I80roEX0A+U45xFby3tDWfzn+Zz8XKDF1Nv9VUwVx0N3zpcG1RPs9hfheG4Cq2kg==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/utils": "^0.16.13",
+        "pngjs": "^3.3.3"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/tiff": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.16.13.tgz",
+      "integrity": "sha512-oJY8d9u95SwW00VPHuCNxPap6Q1+E/xM5QThb9Hu+P6EGuu6lIeLaNBMmFZyblwFbwrH+WBOZlvIzDhi4Dm/6Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "utif": "^2.0.1"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/types": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.16.13.tgz",
+      "integrity": "sha512-mC0yVNUobFDjoYLg4hoUwzMKgNlxynzwt3cDXzumGvRJ7Kb8qQGOWJQjQFo5OxmGExqzPphkirdbBF88RVLBCg==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/bmp": "^0.16.13",
+        "@jimp/gif": "^0.16.13",
+        "@jimp/jpeg": "^0.16.13",
+        "@jimp/png": "^0.16.13",
+        "@jimp/tiff": "^0.16.13",
+        "timm": "^1.6.1"
+      },
+      "peerDependencies": {
+        "@jimp/custom": ">=0.3.5"
+      }
+    },
+    "node_modules/@jimp/utils": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.16.13.tgz",
+      "integrity": "sha512-VyCpkZzFTHXtKgVO35iKN0sYR10psGpV6SkcSeV4oF7eSYlR8Bl6aQLCzVeFjvESF7mxTmIiI3/XrMobVrtxDA==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "regenerator-runtime": "^0.13.3"
+      }
+    },
+    "node_modules/@nodelib/fs.scandir": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.stat": "2.0.5",
+        "run-parallel": "^1.1.9"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.stat": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.walk": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.scandir": "2.1.5",
+        "fastq": "^1.6.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@tokenizer/token": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
+      "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
+      "license": "MIT"
+    },
+    "node_modules/@types/node": {
+      "version": "16.9.1",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz",
+      "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==",
+      "license": "MIT"
+    },
+    "node_modules/@ungap/structured-clone": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+      "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/acorn": {
+      "version": "8.12.1",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
+      "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/acorn-jsx": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+      "dev": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
+    "node_modules/ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/any-base": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz",
+      "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==",
+      "license": "MIT"
+    },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true,
+      "license": "Python-2.0"
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/bmp-js": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz",
+      "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==",
+      "license": "MIT"
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/buffer": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+      "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.1.13"
+      }
+    },
+    "node_modules/buffer-equal": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz",
+      "integrity": "sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/centra": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/centra/-/centra-2.7.0.tgz",
+      "integrity": "sha512-PbFMgMSrmgx6uxCdm57RUos9Tc3fclMvhLSATYN39XsDV29B89zZ3KA89jmY0vwSGazyU+uerqwa6t+KaodPcg==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.15.6"
+      }
+    },
+    "node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "license": "MIT",
+      "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/color-convert": {
+      "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,
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "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==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/cross-spawn": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+      "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/debug": {
+      "version": "4.3.6",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
+      "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.1.2"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/deep-is": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/doctrine": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+      "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "esutils": "^2.0.2"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/dom-walk": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
+      "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
+    },
+    "node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint": {
+      "version": "8.57.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
+      "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.2.0",
+        "@eslint-community/regexpp": "^4.6.1",
+        "@eslint/eslintrc": "^2.1.4",
+        "@eslint/js": "8.57.0",
+        "@humanwhocodes/config-array": "^0.11.14",
+        "@humanwhocodes/module-importer": "^1.0.1",
+        "@nodelib/fs.walk": "^1.2.8",
+        "@ungap/structured-clone": "^1.2.0",
+        "ajv": "^6.12.4",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.2",
+        "debug": "^4.3.2",
+        "doctrine": "^3.0.0",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^7.2.2",
+        "eslint-visitor-keys": "^3.4.3",
+        "espree": "^9.6.1",
+        "esquery": "^1.4.2",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^6.0.1",
+        "find-up": "^5.0.0",
+        "glob-parent": "^6.0.2",
+        "globals": "^13.19.0",
+        "graphemer": "^1.4.0",
+        "ignore": "^5.2.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "is-path-inside": "^3.0.3",
+        "js-yaml": "^4.1.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "levn": "^0.4.1",
+        "lodash.merge": "^4.6.2",
+        "minimatch": "^3.1.2",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.3",
+        "strip-ansi": "^6.0.1",
+        "text-table": "^0.2.0"
+      },
+      "bin": {
+        "eslint": "bin/eslint.js"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint-scope": {
+      "version": "7.2.2",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+      "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint-visitor-keys": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/espree": {
+      "version": "9.6.1",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+      "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "acorn": "^8.9.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^3.4.1"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/esquery": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+      "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "estraverse": "^5.1.0"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/estraverse": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/exif-parser": {
+      "version": "0.1.12",
+      "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz",
+      "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw=="
+    },
+    "node_modules/fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fastq": {
+      "version": "1.17.1",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
+      "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "node_modules/file-entry-cache": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+      "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "flat-cache": "^3.0.4"
+      },
+      "engines": {
+        "node": "^10.12.0 || >=12.0.0"
+      }
+    },
+    "node_modules/file-type": {
+      "version": "16.5.4",
+      "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz",
+      "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==",
+      "license": "MIT",
+      "dependencies": {
+        "readable-web-to-node-stream": "^3.0.0",
+        "strtok3": "^6.2.4",
+        "token-types": "^4.1.1"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/file-type?sponsor=1"
+      }
+    },
+    "node_modules/find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/flat-cache": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
+      "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "flatted": "^3.2.9",
+        "keyv": "^4.5.3",
+        "rimraf": "^3.0.2"
+      },
+      "engines": {
+        "node": "^10.12.0 || >=12.0.0"
+      }
+    },
+    "node_modules/flatted": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
+      "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.6",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+      "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/gifwrap": {
+      "version": "0.9.4",
+      "resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.9.4.tgz",
+      "integrity": "sha512-MDMwbhASQuVeD4JKd1fKgNgCRL3fGqMM4WaqpNhWO0JiMOAjbQdumbs4BbBZEy9/M00EHEjKN3HieVhCUlwjeQ==",
+      "license": "MIT",
+      "dependencies": {
+        "image-q": "^4.0.0",
+        "omggif": "^1.0.10"
+      }
+    },
+    "node_modules/glob": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "deprecated": "Glob versions prior to v9 are no longer supported",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.1.1",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/glob-parent": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/global": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz",
+      "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==",
+      "license": "MIT",
+      "dependencies": {
+        "min-document": "^2.19.0",
+        "process": "^0.11.10"
+      }
+    },
+    "node_modules/globals": {
+      "version": "13.24.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+      "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "type-fest": "^0.20.2"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/graphemer": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+      "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/ignore": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+      "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/image-q": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/image-q/-/image-q-4.0.0.tgz",
+      "integrity": "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "16.9.1"
+      }
+    },
+    "node_modules/import-fresh": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+      "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.19"
+      }
+    },
+    "node_modules/inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+      "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "license": "ISC"
+    },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-function": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz",
+      "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==",
+      "license": "MIT"
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-path-inside": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+      "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/jimp": {
+      "version": "0.16.13",
+      "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.16.13.tgz",
+      "integrity": "sha512-Bxz8q7V4rnCky9A0ktTNGA9SkNFVWRHodddI/DaAWZJzF7sVUlFYKQ60y9JGqrKpi48ECA/TnfMzzc5C70VByA==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "@jimp/custom": "^0.16.13",
+        "@jimp/plugins": "^0.16.13",
+        "@jimp/types": "^0.16.13",
+        "regenerator-runtime": "^0.13.3"
+      }
+    },
+    "node_modules/jpeg-js": {
+      "version": "0.4.4",
+      "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz",
+      "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==",
+      "license": "BSD-3-Clause"
+    },
+    "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==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/json-buffer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/keyv": {
+      "version": "4.5.4",
+      "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+      "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "json-buffer": "3.0.1"
+      }
+    },
+    "node_modules/levn": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+      "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "prelude-ls": "^1.2.1",
+        "type-check": "~0.4.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/load-bmfont": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.2.tgz",
+      "integrity": "sha512-qElWkmjW9Oq1F9EI5Gt7aD9zcdHb9spJCW1L/dmPf7KzCCEJxq8nhHz5eCgI9aMf7vrG/wyaCqdsI+Iy9ZTlog==",
+      "license": "MIT",
+      "dependencies": {
+        "buffer-equal": "0.0.1",
+        "mime": "^1.3.4",
+        "parse-bmfont-ascii": "^1.0.3",
+        "parse-bmfont-binary": "^1.0.5",
+        "parse-bmfont-xml": "^1.1.4",
+        "phin": "^3.7.1",
+        "xhr": "^2.0.1",
+        "xtend": "^4.0.0"
+      }
+    },
+    "node_modules/load-bmfont/node_modules/phin": {
+      "version": "3.7.1",
+      "resolved": "https://registry.npmjs.org/phin/-/phin-3.7.1.tgz",
+      "integrity": "sha512-GEazpTWwTZaEQ9RhL7Nyz0WwqilbqgLahDM3D0hxWwmVDI52nXEybHqiN6/elwpkJBhcuj+WbBu+QfT0uhPGfQ==",
+      "license": "MIT",
+      "dependencies": {
+        "centra": "^2.7.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-locate": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/lodash.merge": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+      "license": "MIT",
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/min-document": {
+      "version": "2.19.0",
+      "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
+      "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==",
+      "dependencies": {
+        "dom-walk": "^0.1.0"
+      }
+    },
+    "node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/mkdirp": {
+      "version": "0.5.6",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+      "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+      "license": "MIT",
+      "dependencies": {
+        "minimist": "^1.2.6"
+      },
+      "bin": {
+        "mkdirp": "bin/cmd.js"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/omggif": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz",
+      "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==",
+      "license": "MIT"
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/optionator": {
+      "version": "0.9.4",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+      "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "deep-is": "^0.1.3",
+        "fast-levenshtein": "^2.0.6",
+        "levn": "^0.4.1",
+        "prelude-ls": "^1.2.1",
+        "type-check": "^0.4.0",
+        "word-wrap": "^1.2.5"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "yocto-queue": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-limit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+      "license": "(MIT AND Zlib)"
+    },
+    "node_modules/parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "callsites": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/parse-bmfont-ascii": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz",
+      "integrity": "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==",
+      "license": "MIT"
+    },
+    "node_modules/parse-bmfont-binary": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz",
+      "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==",
+      "license": "MIT"
+    },
+    "node_modules/parse-bmfont-xml": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.6.tgz",
+      "integrity": "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==",
+      "license": "MIT",
+      "dependencies": {
+        "xml-parse-from-string": "^1.0.0",
+        "xml2js": "^0.5.0"
+      }
+    },
+    "node_modules/parse-headers": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz",
+      "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==",
+      "license": "MIT"
+    },
+    "node_modules/path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/path-key": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/peek-readable": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz",
+      "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit"
+      }
+    },
+    "node_modules/phin": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz",
+      "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==",
+      "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
+      "license": "MIT"
+    },
+    "node_modules/pixelmatch": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz",
+      "integrity": "sha512-J8B6xqiO37sU/gkcMglv6h5Jbd9xNER7aHzpfRdNmV4IbQBzBpe4l9XmbG+xPF/znacgu2jfEw+wHffaq/YkXA==",
+      "license": "ISC",
+      "dependencies": {
+        "pngjs": "^3.0.0"
+      },
+      "bin": {
+        "pixelmatch": "bin/pixelmatch"
+      }
+    },
+    "node_modules/pngjs": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
+      "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/prelude-ls": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+      "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/process": {
+      "version": "0.11.10",
+      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+      "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6.0"
+      }
+    },
+    "node_modules/punycode": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+      "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/queue-microtask": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/readable-web-to-node-stream": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz",
+      "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==",
+      "license": "MIT",
+      "dependencies": {
+        "readable-stream": "^3.6.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit"
+      }
+    },
+    "node_modules/regenerator-runtime": {
+      "version": "0.13.11",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+      "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
+      "license": "MIT"
+    },
+    "node_modules/resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/reusify": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "iojs": ">=1.0.0",
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "deprecated": "Rimraf versions prior to v4 are no longer supported",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "glob": "^7.1.3"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/run-parallel": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "queue-microtask": "^1.2.2"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/sax": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
+      "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
+      "license": "ISC"
+    },
+    "node_modules/shebang-command": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "shebang-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shebang-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "node_modules/strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/strtok3": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz",
+      "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==",
+      "license": "MIT",
+      "dependencies": {
+        "@tokenizer/token": "^0.3.0",
+        "peek-readable": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit"
+      }
+    },
+    "node_modules/supports-color": {
+      "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,
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/text-table": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+      "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/timm": {
+      "version": "1.7.1",
+      "resolved": "https://registry.npmjs.org/timm/-/timm-1.7.1.tgz",
+      "integrity": "sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw==",
+      "license": "MIT"
+    },
+    "node_modules/tinycolor2": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz",
+      "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==",
+      "license": "MIT"
+    },
+    "node_modules/token-types": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz",
+      "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@tokenizer/token": "^0.3.0",
+        "ieee754": "^1.2.1"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit"
+      }
+    },
+    "node_modules/type-check": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+      "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "prelude-ls": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/type-fest": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+      "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+      "dev": true,
+      "license": "(MIT OR CC0-1.0)",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "node_modules/utif": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/utif/-/utif-2.0.1.tgz",
+      "integrity": "sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg==",
+      "license": "MIT",
+      "dependencies": {
+        "pako": "^1.0.5"
+      }
+    },
+    "node_modules/util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+      "license": "MIT"
+    },
+    "node_modules/wavefile": {
+      "version": "11.0.0",
+      "resolved": "https://registry.npmjs.org/wavefile/-/wavefile-11.0.0.tgz",
+      "integrity": "sha512-/OBiAALgWU24IG7sC84cDO/KfFuvajWc5Uec0oV2zrpOOZZDgGdOwHwgEzOrwh8jkubBk7PtZfQBIcI1OaE5Ng==",
+      "license": "MIT",
+      "bin": {
+        "wavefile": "bin/wavefile.js"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "node-which": "bin/node-which"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/word-wrap": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+      "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/xhr": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz",
+      "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==",
+      "license": "MIT",
+      "dependencies": {
+        "global": "~4.4.0",
+        "is-function": "^1.0.1",
+        "parse-headers": "^2.0.0",
+        "xtend": "^4.0.0"
+      }
+    },
+    "node_modules/xml-parse-from-string": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz",
+      "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==",
+      "license": "MIT"
+    },
+    "node_modules/xml2js": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
+      "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
+      "license": "MIT",
+      "dependencies": {
+        "sax": ">=0.6.0",
+        "xmlbuilder": "~11.0.0"
+      },
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/xmlbuilder": {
+      "version": "11.0.1",
+      "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+      "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4"
+      }
+    },
+    "node_modules/yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    }
+  }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..38ecf37
--- /dev/null
+++ b/package.json
@@ -0,0 +1,10 @@
+{
+  "type": "module",
+  "dependencies": {
+    "jimp": "^0.16.2",
+    "wavefile": "^11.0.0"
+  },
+  "devDependencies": {
+    "eslint": "^8.36.0"
+  }
+}
diff --git a/rickroll.jpg b/rickroll.jpg
new file mode 100644
index 0000000..931e14e
Binary files /dev/null and b/rickroll.jpg differ