├── .gitignore ├── .vscode └── settings.json ├── static ├── assets │ ├── glyphs │ │ ├── index.json │ │ ├── noto_sans_bold │ │ │ ├── 1536-1791.pbf │ │ │ ├── 1792-2047.pbf │ │ │ ├── 2048-2303.pbf │ │ │ ├── 2304-2559.pbf │ │ │ ├── 2560-2815.pbf │ │ │ ├── 2816-3071.pbf │ │ │ ├── 3072-3327.pbf │ │ │ ├── 3328-3583.pbf │ │ │ ├── 3584-3839.pbf │ │ │ ├── 3840-4095.pbf │ │ │ ├── 4096-4351.pbf │ │ │ ├── 4352-4607.pbf │ │ │ ├── 4608-4863.pbf │ │ │ ├── 4864-5119.pbf │ │ │ ├── 5120-5375.pbf │ │ │ ├── 5376-5631.pbf │ │ │ ├── 5632-5887.pbf │ │ │ ├── 5888-6143.pbf │ │ │ ├── 6144-6399.pbf │ │ │ ├── 6400-6655.pbf │ │ │ ├── 6656-6911.pbf │ │ │ ├── 6912-7167.pbf │ │ │ ├── 7168-7423.pbf │ │ │ ├── 9216-9471.pbf │ │ │ ├── 10240-10495.pbf │ │ │ ├── 10752-11007.pbf │ │ │ ├── 11008-11263.pbf │ │ │ ├── 11520-11775.pbf │ │ │ ├── 12032-12287.pbf │ │ │ ├── 12288-12543.pbf │ │ │ ├── 12544-12799.pbf │ │ │ ├── 12800-13055.pbf │ │ │ ├── 13056-13311.pbf │ │ │ ├── 13312-13567.pbf │ │ │ ├── 13568-13823.pbf │ │ │ ├── 13824-14079.pbf │ │ │ ├── 14080-14335.pbf │ │ │ ├── 14336-14591.pbf │ │ │ ├── 14592-14847.pbf │ │ │ ├── 14848-15103.pbf │ │ │ ├── 15104-15359.pbf │ │ │ ├── 15360-15615.pbf │ │ │ ├── 15616-15871.pbf │ │ │ ├── 15872-16127.pbf │ │ │ ├── 16128-16383.pbf │ │ │ ├── 16384-16639.pbf │ │ │ ├── 16640-16895.pbf │ │ │ ├── 16896-17151.pbf │ │ │ ├── 17152-17407.pbf │ │ │ ├── 17408-17663.pbf │ │ │ ├── 17664-17919.pbf │ │ │ ├── 17920-18175.pbf │ │ │ ├── 18176-18431.pbf │ │ │ ├── 18432-18687.pbf │ │ │ ├── 18688-18943.pbf │ │ │ ├── 18944-19199.pbf │ │ │ ├── 19200-19455.pbf │ │ │ ├── 19456-19711.pbf │ │ │ ├── 19712-19967.pbf │ │ │ ├── 19968-20223.pbf │ │ │ ├── 20224-20479.pbf │ │ │ ├── 20480-20735.pbf │ │ │ ├── 20736-20991.pbf │ │ │ ├── 20992-21247.pbf │ │ │ ├── 21248-21503.pbf │ │ │ ├── 21504-21759.pbf │ │ │ ├── 21760-22015.pbf │ │ │ ├── 22016-22271.pbf │ │ │ ├── 22272-22527.pbf │ │ │ ├── 22528-22783.pbf │ │ │ ├── 22784-23039.pbf │ │ │ ├── 23040-23295.pbf │ │ │ ├── 23296-23551.pbf │ │ │ ├── 23552-23807.pbf │ │ │ ├── 23808-24063.pbf │ │ │ ├── 24064-24319.pbf │ │ │ ├── 24320-24575.pbf │ │ │ ├── 24576-24831.pbf │ │ │ ├── 24832-25087.pbf │ │ │ ├── 25088-25343.pbf │ │ │ ├── 25344-25599.pbf │ │ │ ├── 25600-25855.pbf │ │ │ ├── 25856-26111.pbf │ │ │ ├── 26112-26367.pbf │ │ │ ├── 26368-26623.pbf │ │ │ ├── 26624-26879.pbf │ │ │ ├── 26880-27135.pbf │ │ │ ├── 27136-27391.pbf │ │ │ ├── 27392-27647.pbf │ │ │ ├── 27648-27903.pbf │ │ │ ├── 27904-28159.pbf │ │ │ ├── 28160-28415.pbf │ │ │ ├── 28416-28671.pbf │ │ │ ├── 28672-28927.pbf │ │ │ ├── 28928-29183.pbf │ │ │ ├── 29184-29439.pbf │ │ │ ├── 29440-29695.pbf │ │ │ ├── 29696-29951.pbf │ │ │ ├── 29952-30207.pbf │ │ │ ├── 30208-30463.pbf │ │ │ ├── 30464-30719.pbf │ │ │ ├── 30720-30975.pbf │ │ │ ├── 30976-31231.pbf │ │ │ ├── 31232-31487.pbf │ │ │ ├── 31488-31743.pbf │ │ │ ├── 31744-31999.pbf │ │ │ ├── 32000-32255.pbf │ │ │ ├── 32256-32511.pbf │ │ │ ├── 32512-32767.pbf │ │ │ ├── 32768-33023.pbf │ │ │ ├── 33024-33279.pbf │ │ │ ├── 33280-33535.pbf │ │ │ ├── 33536-33791.pbf │ │ │ ├── 33792-34047.pbf │ │ │ ├── 34048-34303.pbf │ │ │ ├── 34304-34559.pbf │ │ │ ├── 34560-34815.pbf │ │ │ ├── 34816-35071.pbf │ │ │ ├── 35072-35327.pbf │ │ │ ├── 35328-35583.pbf │ │ │ ├── 35584-35839.pbf │ │ │ ├── 35840-36095.pbf │ │ │ ├── 36096-36351.pbf │ │ │ ├── 36352-36607.pbf │ │ │ ├── 36608-36863.pbf │ │ │ ├── 36864-37119.pbf │ │ │ ├── 37120-37375.pbf │ │ │ ├── 37376-37631.pbf │ │ │ ├── 37632-37887.pbf │ │ │ ├── 37888-38143.pbf │ │ │ ├── 38144-38399.pbf │ │ │ ├── 38400-38655.pbf │ │ │ ├── 38656-38911.pbf │ │ │ ├── 38912-39167.pbf │ │ │ ├── 39168-39423.pbf │ │ │ ├── 39424-39679.pbf │ │ │ ├── 39680-39935.pbf │ │ │ ├── 39936-40191.pbf │ │ │ ├── 40192-40447.pbf │ │ │ ├── 40448-40703.pbf │ │ │ ├── 40704-40959.pbf │ │ │ ├── 40960-41215.pbf │ │ │ ├── 41216-41471.pbf │ │ │ ├── 41472-41727.pbf │ │ │ ├── 41728-41983.pbf │ │ │ ├── 41984-42239.pbf │ │ │ ├── 42240-42495.pbf │ │ │ ├── 42496-42751.pbf │ │ │ ├── 43008-43263.pbf │ │ │ ├── 43264-43519.pbf │ │ │ ├── 43520-43775.pbf │ │ │ ├── 43776-44031.pbf │ │ │ ├── 44032-44287.pbf │ │ │ ├── 44288-44543.pbf │ │ │ ├── 44544-44799.pbf │ │ │ ├── 44800-45055.pbf │ │ │ ├── 45056-45311.pbf │ │ │ ├── 45312-45567.pbf │ │ │ ├── 45568-45823.pbf │ │ │ ├── 45824-46079.pbf │ │ │ ├── 46080-46335.pbf │ │ │ ├── 46336-46591.pbf │ │ │ ├── 46592-46847.pbf │ │ │ ├── 46848-47103.pbf │ │ │ ├── 47104-47359.pbf │ │ │ ├── 47360-47615.pbf │ │ │ ├── 47616-47871.pbf │ │ │ ├── 47872-48127.pbf │ │ │ ├── 48128-48383.pbf │ │ │ ├── 48384-48639.pbf │ │ │ ├── 48640-48895.pbf │ │ │ ├── 48896-49151.pbf │ │ │ ├── 49152-49407.pbf │ │ │ ├── 49408-49663.pbf │ │ │ ├── 49664-49919.pbf │ │ │ ├── 49920-50175.pbf │ │ │ ├── 50176-50431.pbf │ │ │ ├── 50432-50687.pbf │ │ │ ├── 50688-50943.pbf │ │ │ ├── 50944-51199.pbf │ │ │ ├── 51200-51455.pbf │ │ │ ├── 51456-51711.pbf │ │ │ ├── 51712-51967.pbf │ │ │ ├── 51968-52223.pbf │ │ │ ├── 52224-52479.pbf │ │ │ ├── 52480-52735.pbf │ │ │ ├── 52736-52991.pbf │ │ │ ├── 52992-53247.pbf │ │ │ ├── 53248-53503.pbf │ │ │ ├── 53504-53759.pbf │ │ │ ├── 53760-54015.pbf │ │ │ ├── 54016-54271.pbf │ │ │ ├── 54272-54527.pbf │ │ │ ├── 54528-54783.pbf │ │ │ ├── 54784-55039.pbf │ │ │ ├── 55040-55295.pbf │ │ │ ├── 55296-55551.pbf │ │ │ ├── 55552-55807.pbf │ │ │ ├── 55808-56063.pbf │ │ │ ├── 56064-56319.pbf │ │ │ ├── 56320-56575.pbf │ │ │ ├── 56576-56831.pbf │ │ │ ├── 56832-57087.pbf │ │ │ ├── 57088-57343.pbf │ │ │ ├── 57344-57599.pbf │ │ │ ├── 57600-57855.pbf │ │ │ ├── 57856-58111.pbf │ │ │ ├── 58112-58367.pbf │ │ │ ├── 58368-58623.pbf │ │ │ ├── 58624-58879.pbf │ │ │ ├── 58880-59135.pbf │ │ │ ├── 59136-59391.pbf │ │ │ ├── 59392-59647.pbf │ │ │ ├── 59648-59903.pbf │ │ │ ├── 59904-60159.pbf │ │ │ ├── 60160-60415.pbf │ │ │ ├── 60416-60671.pbf │ │ │ ├── 60672-60927.pbf │ │ │ ├── 60928-61183.pbf │ │ │ ├── 61184-61439.pbf │ │ │ ├── 61440-61695.pbf │ │ │ ├── 61696-61951.pbf │ │ │ ├── 61952-62207.pbf │ │ │ ├── 62208-62463.pbf │ │ │ ├── 62464-62719.pbf │ │ │ ├── 62720-62975.pbf │ │ │ ├── 62976-63231.pbf │ │ │ ├── 63232-63487.pbf │ │ │ ├── 63488-63743.pbf │ │ │ ├── 63744-63999.pbf │ │ │ ├── 64000-64255.pbf │ │ │ ├── 64512-64767.pbf │ │ │ ├── 64768-65023.pbf │ │ │ ├── 9984-10239.pbf │ │ │ ├── 0-255.pbf │ │ │ ├── 256-511.pbf │ │ │ ├── 512-767.pbf │ │ │ ├── 1024-1279.pbf │ │ │ ├── 1280-1535.pbf │ │ │ ├── 7424-7679.pbf │ │ │ ├── 768-1023.pbf │ │ │ ├── 7680-7935.pbf │ │ │ ├── 7936-8191.pbf │ │ │ ├── 8192-8447.pbf │ │ │ ├── 8448-8703.pbf │ │ │ ├── 8704-8959.pbf │ │ │ ├── 8960-9215.pbf │ │ │ ├── 9472-9727.pbf │ │ │ ├── 9728-9983.pbf │ │ │ ├── 10496-10751.pbf │ │ │ ├── 11264-11519.pbf │ │ │ ├── 11776-12031.pbf │ │ │ ├── 42752-43007.pbf │ │ │ ├── 64256-64511.pbf │ │ │ ├── 65024-65279.pbf │ │ │ └── 65280-65535.pbf │ │ └── noto_sans_regular │ │ │ ├── 1536-1791.pbf │ │ │ ├── 1792-2047.pbf │ │ │ ├── 2048-2303.pbf │ │ │ ├── 2304-2559.pbf │ │ │ ├── 2560-2815.pbf │ │ │ ├── 2816-3071.pbf │ │ │ ├── 3072-3327.pbf │ │ │ ├── 3328-3583.pbf │ │ │ ├── 3584-3839.pbf │ │ │ ├── 3840-4095.pbf │ │ │ ├── 4096-4351.pbf │ │ │ ├── 4352-4607.pbf │ │ │ ├── 4608-4863.pbf │ │ │ ├── 4864-5119.pbf │ │ │ ├── 5120-5375.pbf │ │ │ ├── 5376-5631.pbf │ │ │ ├── 5632-5887.pbf │ │ │ ├── 5888-6143.pbf │ │ │ ├── 6144-6399.pbf │ │ │ ├── 6400-6655.pbf │ │ │ ├── 6656-6911.pbf │ │ │ ├── 6912-7167.pbf │ │ │ ├── 7168-7423.pbf │ │ │ ├── 9216-9471.pbf │ │ │ ├── 10240-10495.pbf │ │ │ ├── 10752-11007.pbf │ │ │ ├── 11008-11263.pbf │ │ │ ├── 11520-11775.pbf │ │ │ ├── 12032-12287.pbf │ │ │ ├── 12288-12543.pbf │ │ │ ├── 12544-12799.pbf │ │ │ ├── 12800-13055.pbf │ │ │ ├── 13056-13311.pbf │ │ │ ├── 13312-13567.pbf │ │ │ ├── 13568-13823.pbf │ │ │ ├── 13824-14079.pbf │ │ │ ├── 14080-14335.pbf │ │ │ ├── 14336-14591.pbf │ │ │ ├── 14592-14847.pbf │ │ │ ├── 14848-15103.pbf │ │ │ ├── 15104-15359.pbf │ │ │ ├── 15360-15615.pbf │ │ │ ├── 15616-15871.pbf │ │ │ ├── 15872-16127.pbf │ │ │ ├── 16128-16383.pbf │ │ │ ├── 16384-16639.pbf │ │ │ ├── 16640-16895.pbf │ │ │ ├── 16896-17151.pbf │ │ │ ├── 17152-17407.pbf │ │ │ ├── 17408-17663.pbf │ │ │ ├── 17664-17919.pbf │ │ │ ├── 17920-18175.pbf │ │ │ ├── 18176-18431.pbf │ │ │ ├── 18432-18687.pbf │ │ │ ├── 18688-18943.pbf │ │ │ ├── 18944-19199.pbf │ │ │ ├── 19200-19455.pbf │ │ │ ├── 19456-19711.pbf │ │ │ ├── 19712-19967.pbf │ │ │ ├── 19968-20223.pbf │ │ │ ├── 20224-20479.pbf │ │ │ ├── 20480-20735.pbf │ │ │ ├── 20736-20991.pbf │ │ │ ├── 20992-21247.pbf │ │ │ ├── 21248-21503.pbf │ │ │ ├── 21504-21759.pbf │ │ │ ├── 21760-22015.pbf │ │ │ ├── 22016-22271.pbf │ │ │ ├── 22272-22527.pbf │ │ │ ├── 22528-22783.pbf │ │ │ ├── 22784-23039.pbf │ │ │ ├── 23040-23295.pbf │ │ │ ├── 23296-23551.pbf │ │ │ ├── 23552-23807.pbf │ │ │ ├── 23808-24063.pbf │ │ │ ├── 24064-24319.pbf │ │ │ ├── 24320-24575.pbf │ │ │ ├── 24576-24831.pbf │ │ │ ├── 24832-25087.pbf │ │ │ ├── 25088-25343.pbf │ │ │ ├── 25344-25599.pbf │ │ │ ├── 25600-25855.pbf │ │ │ ├── 25856-26111.pbf │ │ │ ├── 26112-26367.pbf │ │ │ ├── 26368-26623.pbf │ │ │ ├── 26624-26879.pbf │ │ │ ├── 26880-27135.pbf │ │ │ ├── 27136-27391.pbf │ │ │ ├── 27392-27647.pbf │ │ │ ├── 27648-27903.pbf │ │ │ ├── 27904-28159.pbf │ │ │ ├── 28160-28415.pbf │ │ │ ├── 28416-28671.pbf │ │ │ ├── 28672-28927.pbf │ │ │ ├── 28928-29183.pbf │ │ │ ├── 29184-29439.pbf │ │ │ ├── 29440-29695.pbf │ │ │ ├── 29696-29951.pbf │ │ │ ├── 29952-30207.pbf │ │ │ ├── 30208-30463.pbf │ │ │ ├── 30464-30719.pbf │ │ │ ├── 30720-30975.pbf │ │ │ ├── 30976-31231.pbf │ │ │ ├── 31232-31487.pbf │ │ │ ├── 31488-31743.pbf │ │ │ ├── 31744-31999.pbf │ │ │ ├── 32000-32255.pbf │ │ │ ├── 32256-32511.pbf │ │ │ ├── 32512-32767.pbf │ │ │ ├── 32768-33023.pbf │ │ │ ├── 33024-33279.pbf │ │ │ ├── 33280-33535.pbf │ │ │ ├── 33536-33791.pbf │ │ │ ├── 33792-34047.pbf │ │ │ ├── 34048-34303.pbf │ │ │ ├── 34304-34559.pbf │ │ │ ├── 34560-34815.pbf │ │ │ ├── 34816-35071.pbf │ │ │ ├── 35072-35327.pbf │ │ │ ├── 35328-35583.pbf │ │ │ ├── 35584-35839.pbf │ │ │ ├── 35840-36095.pbf │ │ │ ├── 36096-36351.pbf │ │ │ ├── 36352-36607.pbf │ │ │ ├── 36608-36863.pbf │ │ │ ├── 36864-37119.pbf │ │ │ ├── 37120-37375.pbf │ │ │ ├── 37376-37631.pbf │ │ │ ├── 37632-37887.pbf │ │ │ ├── 37888-38143.pbf │ │ │ ├── 38144-38399.pbf │ │ │ ├── 38400-38655.pbf │ │ │ ├── 38656-38911.pbf │ │ │ ├── 38912-39167.pbf │ │ │ ├── 39168-39423.pbf │ │ │ ├── 39424-39679.pbf │ │ │ ├── 39680-39935.pbf │ │ │ ├── 39936-40191.pbf │ │ │ ├── 40192-40447.pbf │ │ │ ├── 40448-40703.pbf │ │ │ ├── 40704-40959.pbf │ │ │ ├── 40960-41215.pbf │ │ │ ├── 41216-41471.pbf │ │ │ ├── 41472-41727.pbf │ │ │ ├── 41728-41983.pbf │ │ │ ├── 41984-42239.pbf │ │ │ ├── 42240-42495.pbf │ │ │ ├── 42496-42751.pbf │ │ │ ├── 43008-43263.pbf │ │ │ ├── 43264-43519.pbf │ │ │ ├── 43520-43775.pbf │ │ │ ├── 43776-44031.pbf │ │ │ ├── 44032-44287.pbf │ │ │ ├── 44288-44543.pbf │ │ │ ├── 44544-44799.pbf │ │ │ ├── 44800-45055.pbf │ │ │ ├── 45056-45311.pbf │ │ │ ├── 45312-45567.pbf │ │ │ ├── 45568-45823.pbf │ │ │ ├── 45824-46079.pbf │ │ │ ├── 46080-46335.pbf │ │ │ ├── 46336-46591.pbf │ │ │ ├── 46592-46847.pbf │ │ │ ├── 46848-47103.pbf │ │ │ ├── 47104-47359.pbf │ │ │ ├── 47360-47615.pbf │ │ │ ├── 47616-47871.pbf │ │ │ ├── 47872-48127.pbf │ │ │ ├── 48128-48383.pbf │ │ │ ├── 48384-48639.pbf │ │ │ ├── 48640-48895.pbf │ │ │ ├── 48896-49151.pbf │ │ │ ├── 49152-49407.pbf │ │ │ ├── 49408-49663.pbf │ │ │ ├── 49664-49919.pbf │ │ │ ├── 49920-50175.pbf │ │ │ ├── 50176-50431.pbf │ │ │ ├── 50432-50687.pbf │ │ │ ├── 50688-50943.pbf │ │ │ ├── 50944-51199.pbf │ │ │ ├── 51200-51455.pbf │ │ │ ├── 51456-51711.pbf │ │ │ ├── 51712-51967.pbf │ │ │ ├── 51968-52223.pbf │ │ │ ├── 52224-52479.pbf │ │ │ ├── 52480-52735.pbf │ │ │ ├── 52736-52991.pbf │ │ │ ├── 52992-53247.pbf │ │ │ ├── 53248-53503.pbf │ │ │ ├── 53504-53759.pbf │ │ │ ├── 53760-54015.pbf │ │ │ ├── 54016-54271.pbf │ │ │ ├── 54272-54527.pbf │ │ │ ├── 54528-54783.pbf │ │ │ ├── 54784-55039.pbf │ │ │ ├── 55040-55295.pbf │ │ │ ├── 55296-55551.pbf │ │ │ ├── 55552-55807.pbf │ │ │ ├── 55808-56063.pbf │ │ │ ├── 56064-56319.pbf │ │ │ ├── 56320-56575.pbf │ │ │ ├── 56576-56831.pbf │ │ │ ├── 56832-57087.pbf │ │ │ ├── 57088-57343.pbf │ │ │ ├── 57344-57599.pbf │ │ │ ├── 57600-57855.pbf │ │ │ ├── 57856-58111.pbf │ │ │ ├── 58112-58367.pbf │ │ │ ├── 58368-58623.pbf │ │ │ ├── 58624-58879.pbf │ │ │ ├── 58880-59135.pbf │ │ │ ├── 59136-59391.pbf │ │ │ ├── 59392-59647.pbf │ │ │ ├── 59648-59903.pbf │ │ │ ├── 59904-60159.pbf │ │ │ ├── 60160-60415.pbf │ │ │ ├── 60416-60671.pbf │ │ │ ├── 60672-60927.pbf │ │ │ ├── 60928-61183.pbf │ │ │ ├── 61184-61439.pbf │ │ │ ├── 61440-61695.pbf │ │ │ ├── 61696-61951.pbf │ │ │ ├── 61952-62207.pbf │ │ │ ├── 62208-62463.pbf │ │ │ ├── 62464-62719.pbf │ │ │ ├── 62720-62975.pbf │ │ │ ├── 62976-63231.pbf │ │ │ ├── 63232-63487.pbf │ │ │ ├── 63488-63743.pbf │ │ │ ├── 63744-63999.pbf │ │ │ ├── 64000-64255.pbf │ │ │ ├── 64512-64767.pbf │ │ │ ├── 64768-65023.pbf │ │ │ ├── 9984-10239.pbf │ │ │ ├── 0-255.pbf │ │ │ ├── 256-511.pbf │ │ │ ├── 512-767.pbf │ │ │ ├── 1024-1279.pbf │ │ │ ├── 1280-1535.pbf │ │ │ ├── 7424-7679.pbf │ │ │ ├── 768-1023.pbf │ │ │ ├── 7680-7935.pbf │ │ │ ├── 7936-8191.pbf │ │ │ ├── 8192-8447.pbf │ │ │ ├── 8448-8703.pbf │ │ │ ├── 8704-8959.pbf │ │ │ ├── 8960-9215.pbf │ │ │ ├── 9472-9727.pbf │ │ │ ├── 9728-9983.pbf │ │ │ ├── 10496-10751.pbf │ │ │ ├── 11264-11519.pbf │ │ │ ├── 11776-12031.pbf │ │ │ ├── 42752-43007.pbf │ │ │ ├── 64256-64511.pbf │ │ │ ├── 65024-65279.pbf │ │ │ └── 65280-65535.pbf │ └── sprites │ │ └── basics │ │ ├── sprites.png │ │ ├── sprites@2x.png │ │ ├── sprites.json │ │ └── sprites@2x.json └── index.html ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ └── ci.yml ├── codecov.yml ├── testdata └── island.versatiles ├── tsconfig.build.json ├── tsconfig.json ├── src ├── lib │ ├── log.ts │ ├── file.ts │ ├── types.ts │ ├── style.ts │ ├── mime_types.ts │ ├── compressors.test.ts │ ├── style.test.ts │ ├── layer.ts │ ├── compressors.ts │ ├── mime_types.test.ts │ ├── log.test.ts │ ├── layer.test.ts │ ├── response.ts │ ├── file.test.ts │ ├── server.ts │ ├── server.test.ts │ └── response.test.ts ├── index.ts └── index.test.ts ├── eslint.config.js ├── LICENSE.md ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | **/coverage/ 3 | **/dist/ 4 | node_modules 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.workingDirectories": [ 3 | "." 4 | ] 5 | } -------------------------------------------------------------------------------- /static/assets/glyphs/index.json: -------------------------------------------------------------------------------- 1 | [ 2 | "noto_sans_bold", 3 | "noto_sans_regular" 4 | ] -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/1536-1791.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 1536-1791 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/1792-2047.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 1792-2047 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/2048-2303.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 2048-2303 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/2304-2559.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 2304-2559 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/2560-2815.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 2560-2815 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/2816-3071.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 2816-3071 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/3072-3327.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 3072-3327 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/3328-3583.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 3328-3583 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/3584-3839.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 3584-3839 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/3840-4095.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 3840-4095 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/4096-4351.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 4096-4351 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/4352-4607.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 4352-4607 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/4608-4863.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 4608-4863 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/4864-5119.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 4864-5119 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/5120-5375.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 5120-5375 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/5376-5631.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 5376-5631 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/5632-5887.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 5632-5887 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/5888-6143.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 5888-6143 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/6144-6399.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 6144-6399 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/6400-6655.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 6400-6655 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/6656-6911.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 6656-6911 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/6912-7167.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 6912-7167 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/7168-7423.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 7168-7423 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/9216-9471.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 9216-9471 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/10240-10495.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 10240-10495 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/10752-11007.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 10752-11007 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/11008-11263.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 11008-11263 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/11520-11775.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 11520-11775 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/12032-12287.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 12032-12287 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/12288-12543.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 12288-12543 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/12544-12799.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 12544-12799 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/12800-13055.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 12800-13055 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/13056-13311.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 13056-13311 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/13312-13567.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 13312-13567 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/13568-13823.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 13568-13823 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/13824-14079.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 13824-14079 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/14080-14335.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 14080-14335 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/14336-14591.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 14336-14591 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/14592-14847.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 14592-14847 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/14848-15103.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 14848-15103 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/15104-15359.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 15104-15359 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/15360-15615.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 15360-15615 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/15616-15871.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 15616-15871 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/15872-16127.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 15872-16127 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/16128-16383.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 16128-16383 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/16384-16639.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 16384-16639 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/16640-16895.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 16640-16895 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/16896-17151.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 16896-17151 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/17152-17407.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 17152-17407 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/17408-17663.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 17408-17663 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/17664-17919.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 17664-17919 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/17920-18175.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 17920-18175 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/18176-18431.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 18176-18431 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/18432-18687.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 18432-18687 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/18688-18943.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 18688-18943 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/18944-19199.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 18944-19199 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/19200-19455.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 19200-19455 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/19456-19711.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 19456-19711 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/19712-19967.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 19712-19967 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/19968-20223.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 19968-20223 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/20224-20479.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 20224-20479 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/20480-20735.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 20480-20735 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/20736-20991.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 20736-20991 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/20992-21247.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 20992-21247 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/21248-21503.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 21248-21503 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/21504-21759.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 21504-21759 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/21760-22015.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 21760-22015 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/22016-22271.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 22016-22271 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/22272-22527.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 22272-22527 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/22528-22783.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 22528-22783 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/22784-23039.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 22784-23039 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/23040-23295.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 23040-23295 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/23296-23551.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 23296-23551 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/23552-23807.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 23552-23807 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/23808-24063.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 23808-24063 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/24064-24319.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 24064-24319 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/24320-24575.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 24320-24575 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/24576-24831.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 24576-24831 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/24832-25087.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 24832-25087 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/25088-25343.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 25088-25343 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/25344-25599.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 25344-25599 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/25600-25855.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 25600-25855 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/25856-26111.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 25856-26111 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/26112-26367.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 26112-26367 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/26368-26623.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 26368-26623 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/26624-26879.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 26624-26879 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/26880-27135.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 26880-27135 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/27136-27391.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 27136-27391 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/27392-27647.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 27392-27647 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/27648-27903.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 27648-27903 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/27904-28159.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 27904-28159 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/28160-28415.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 28160-28415 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/28416-28671.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 28416-28671 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/28672-28927.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 28672-28927 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/28928-29183.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 28928-29183 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/29184-29439.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 29184-29439 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/29440-29695.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 29440-29695 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/29696-29951.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 29696-29951 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/29952-30207.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 29952-30207 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/30208-30463.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 30208-30463 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/30464-30719.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 30464-30719 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/30720-30975.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 30720-30975 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/30976-31231.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 30976-31231 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/31232-31487.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 31232-31487 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/31488-31743.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 31488-31743 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/31744-31999.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 31744-31999 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/32000-32255.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 32000-32255 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/32256-32511.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 32256-32511 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/32512-32767.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 32512-32767 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/32768-33023.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 32768-33023 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/33024-33279.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 33024-33279 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/33280-33535.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 33280-33535 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/33536-33791.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 33536-33791 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/33792-34047.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 33792-34047 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/34048-34303.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 34048-34303 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/34304-34559.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 34304-34559 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/34560-34815.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 34560-34815 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/34816-35071.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 34816-35071 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/35072-35327.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 35072-35327 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/35328-35583.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 35328-35583 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/35584-35839.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 35584-35839 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/35840-36095.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 35840-36095 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/36096-36351.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 36096-36351 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/36352-36607.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 36352-36607 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/36608-36863.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 36608-36863 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/36864-37119.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 36864-37119 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/37120-37375.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 37120-37375 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/37376-37631.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 37376-37631 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/37632-37887.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 37632-37887 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/37888-38143.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 37888-38143 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/38144-38399.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 38144-38399 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/38400-38655.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 38400-38655 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/38656-38911.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 38656-38911 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/38912-39167.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 38912-39167 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/39168-39423.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 39168-39423 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/39424-39679.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 39424-39679 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/39680-39935.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 39680-39935 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/39936-40191.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 39936-40191 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/40192-40447.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 40192-40447 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/40448-40703.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 40448-40703 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/40704-40959.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 40704-40959 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/40960-41215.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 40960-41215 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/41216-41471.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 41216-41471 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/41472-41727.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 41472-41727 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/41728-41983.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 41728-41983 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/41984-42239.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 41984-42239 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/42240-42495.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 42240-42495 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/42496-42751.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 42496-42751 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/43008-43263.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 43008-43263 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/43264-43519.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 43264-43519 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/43520-43775.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 43520-43775 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/43776-44031.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 43776-44031 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/44032-44287.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 44032-44287 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/44288-44543.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 44288-44543 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/44544-44799.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 44544-44799 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/44800-45055.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 44800-45055 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/45056-45311.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 45056-45311 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/45312-45567.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 45312-45567 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/45568-45823.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 45568-45823 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/45824-46079.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 45824-46079 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/46080-46335.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 46080-46335 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/46336-46591.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 46336-46591 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/46592-46847.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 46592-46847 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/46848-47103.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 46848-47103 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/47104-47359.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 47104-47359 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/47360-47615.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 47360-47615 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/47616-47871.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 47616-47871 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/47872-48127.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 47872-48127 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/48128-48383.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 48128-48383 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/48384-48639.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 48384-48639 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/48640-48895.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 48640-48895 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/48896-49151.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 48896-49151 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/49152-49407.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 49152-49407 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/49408-49663.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 49408-49663 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/49664-49919.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 49664-49919 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/49920-50175.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 49920-50175 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/50176-50431.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 50176-50431 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/50432-50687.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 50432-50687 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/50688-50943.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 50688-50943 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/50944-51199.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 50944-51199 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/51200-51455.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 51200-51455 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/51456-51711.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 51456-51711 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/51712-51967.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 51712-51967 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/51968-52223.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 51968-52223 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/52224-52479.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 52224-52479 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/52480-52735.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 52480-52735 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/52736-52991.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 52736-52991 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/52992-53247.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 52992-53247 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/53248-53503.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 53248-53503 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/53504-53759.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 53504-53759 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/53760-54015.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 53760-54015 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/54016-54271.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 54016-54271 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/54272-54527.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 54272-54527 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/54528-54783.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 54528-54783 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/54784-55039.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 54784-55039 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/55040-55295.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 55040-55295 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/55296-55551.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 55296-55551 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/55552-55807.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 55552-55807 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/55808-56063.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 55808-56063 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/56064-56319.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 56064-56319 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/56320-56575.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 56320-56575 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/56576-56831.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 56576-56831 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/56832-57087.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 56832-57087 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/57088-57343.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 57088-57343 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/57344-57599.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 57344-57599 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/57600-57855.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 57600-57855 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/57856-58111.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 57856-58111 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/58112-58367.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 58112-58367 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/58368-58623.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 58368-58623 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/58624-58879.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 58624-58879 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/58880-59135.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 58880-59135 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/59136-59391.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 59136-59391 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/59392-59647.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 59392-59647 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/59648-59903.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 59648-59903 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/59904-60159.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 59904-60159 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/60160-60415.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 60160-60415 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/60416-60671.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 60416-60671 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/60672-60927.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 60672-60927 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/60928-61183.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 60928-61183 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/61184-61439.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 61184-61439 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/61440-61695.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 61440-61695 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/61696-61951.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 61696-61951 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/61952-62207.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 61952-62207 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/62208-62463.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 62208-62463 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/62464-62719.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 62464-62719 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/62720-62975.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 62720-62975 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/62976-63231.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 62976-63231 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/63232-63487.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 63232-63487 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/63488-63743.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 63488-63743 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/63744-63999.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 63744-63999 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/64000-64255.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 64000-64255 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/64512-64767.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 64512-64767 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/64768-65023.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 64768-65023 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/9984-10239.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Bold 4 | 9984-10239 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/1536-1791.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 1536-1791 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/1792-2047.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 1792-2047 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/2048-2303.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 2048-2303 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/2304-2559.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 2304-2559 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/2560-2815.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 2560-2815 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/2816-3071.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 2816-3071 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/3072-3327.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 3072-3327 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/3328-3583.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 3328-3583 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/3584-3839.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 3584-3839 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/3840-4095.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 3840-4095 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/4096-4351.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 4096-4351 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/4352-4607.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 4352-4607 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/4608-4863.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 4608-4863 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/4864-5119.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 4864-5119 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/5120-5375.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 5120-5375 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/5376-5631.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 5376-5631 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/5632-5887.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 5632-5887 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/5888-6143.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 5888-6143 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/6144-6399.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 6144-6399 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/6400-6655.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 6400-6655 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/6656-6911.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 6656-6911 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/6912-7167.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 6912-7167 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/7168-7423.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 7168-7423 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/9216-9471.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 9216-9471 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/10240-10495.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 10240-10495 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/10752-11007.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 10752-11007 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/11008-11263.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 11008-11263 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/11520-11775.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 11520-11775 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/12032-12287.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 12032-12287 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/12288-12543.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 12288-12543 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/12544-12799.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 12544-12799 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/12800-13055.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 12800-13055 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/13056-13311.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 13056-13311 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/13312-13567.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 13312-13567 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/13568-13823.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 13568-13823 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/13824-14079.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 13824-14079 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/14080-14335.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 14080-14335 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/14336-14591.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 14336-14591 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/14592-14847.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 14592-14847 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/14848-15103.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 14848-15103 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/15104-15359.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 15104-15359 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/15360-15615.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 15360-15615 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/15616-15871.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 15616-15871 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/15872-16127.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 15872-16127 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/16128-16383.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 16128-16383 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/16384-16639.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 16384-16639 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/16640-16895.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 16640-16895 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/16896-17151.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 16896-17151 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/17152-17407.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 17152-17407 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/17408-17663.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 17408-17663 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/17664-17919.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 17664-17919 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/17920-18175.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 17920-18175 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/18176-18431.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 18176-18431 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/18432-18687.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 18432-18687 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/18688-18943.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 18688-18943 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/18944-19199.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 18944-19199 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/19200-19455.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 19200-19455 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/19456-19711.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 19456-19711 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/19712-19967.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 19712-19967 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/19968-20223.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 19968-20223 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/20224-20479.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 20224-20479 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/20480-20735.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 20480-20735 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/20736-20991.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 20736-20991 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/20992-21247.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 20992-21247 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/21248-21503.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 21248-21503 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/21504-21759.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 21504-21759 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/21760-22015.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 21760-22015 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/22016-22271.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 22016-22271 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/22272-22527.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 22272-22527 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/22528-22783.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 22528-22783 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/22784-23039.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 22784-23039 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/23040-23295.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 23040-23295 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/23296-23551.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 23296-23551 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/23552-23807.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 23552-23807 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/23808-24063.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 23808-24063 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/24064-24319.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 24064-24319 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/24320-24575.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 24320-24575 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/24576-24831.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 24576-24831 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/24832-25087.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 24832-25087 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/25088-25343.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 25088-25343 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/25344-25599.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 25344-25599 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/25600-25855.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 25600-25855 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/25856-26111.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 25856-26111 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/26112-26367.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 26112-26367 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/26368-26623.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 26368-26623 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/26624-26879.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 26624-26879 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/26880-27135.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 26880-27135 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/27136-27391.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 27136-27391 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/27392-27647.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 27392-27647 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/27648-27903.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 27648-27903 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/27904-28159.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 27904-28159 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/28160-28415.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 28160-28415 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/28416-28671.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 28416-28671 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/28672-28927.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 28672-28927 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/28928-29183.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 28928-29183 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/29184-29439.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 29184-29439 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/29440-29695.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 29440-29695 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/29696-29951.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 29696-29951 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/29952-30207.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 29952-30207 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/30208-30463.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 30208-30463 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/30464-30719.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 30464-30719 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/30720-30975.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 30720-30975 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/30976-31231.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 30976-31231 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/31232-31487.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 31232-31487 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/31488-31743.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 31488-31743 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/31744-31999.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 31744-31999 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/32000-32255.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 32000-32255 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/32256-32511.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 32256-32511 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/32512-32767.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 32512-32767 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/32768-33023.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 32768-33023 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/33024-33279.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 33024-33279 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/33280-33535.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 33280-33535 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/33536-33791.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 33536-33791 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/33792-34047.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 33792-34047 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/34048-34303.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 34048-34303 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/34304-34559.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 34304-34559 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/34560-34815.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 34560-34815 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/34816-35071.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 34816-35071 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/35072-35327.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 35072-35327 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/35328-35583.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 35328-35583 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/35584-35839.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 35584-35839 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/35840-36095.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 35840-36095 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/36096-36351.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 36096-36351 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/36352-36607.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 36352-36607 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/36608-36863.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 36608-36863 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/36864-37119.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 36864-37119 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/37120-37375.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 37120-37375 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/37376-37631.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 37376-37631 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/37632-37887.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 37632-37887 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/37888-38143.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 37888-38143 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/38144-38399.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 38144-38399 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/38400-38655.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 38400-38655 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/38656-38911.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 38656-38911 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/38912-39167.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 38912-39167 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/39168-39423.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 39168-39423 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/39424-39679.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 39424-39679 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/39680-39935.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 39680-39935 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/39936-40191.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 39936-40191 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/40192-40447.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 40192-40447 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/40448-40703.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 40448-40703 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/40704-40959.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 40704-40959 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/40960-41215.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 40960-41215 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/41216-41471.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 41216-41471 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/41472-41727.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 41472-41727 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/41728-41983.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 41728-41983 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/41984-42239.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 41984-42239 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/42240-42495.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 42240-42495 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/42496-42751.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 42496-42751 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/43008-43263.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 43008-43263 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/43264-43519.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 43264-43519 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/43520-43775.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 43520-43775 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/43776-44031.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 43776-44031 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/44032-44287.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 44032-44287 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/44288-44543.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 44288-44543 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/44544-44799.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 44544-44799 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/44800-45055.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 44800-45055 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/45056-45311.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 45056-45311 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/45312-45567.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 45312-45567 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/45568-45823.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 45568-45823 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/45824-46079.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 45824-46079 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/46080-46335.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 46080-46335 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/46336-46591.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 46336-46591 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/46592-46847.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 46592-46847 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/46848-47103.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 46848-47103 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/47104-47359.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 47104-47359 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/47360-47615.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 47360-47615 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/47616-47871.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 47616-47871 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/47872-48127.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 47872-48127 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/48128-48383.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 48128-48383 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/48384-48639.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 48384-48639 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/48640-48895.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 48640-48895 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/48896-49151.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 48896-49151 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/49152-49407.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 49152-49407 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/49408-49663.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 49408-49663 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/49664-49919.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 49664-49919 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/49920-50175.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 49920-50175 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/50176-50431.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 50176-50431 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/50432-50687.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 50432-50687 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/50688-50943.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 50688-50943 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/50944-51199.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 50944-51199 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/51200-51455.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 51200-51455 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/51456-51711.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 51456-51711 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/51712-51967.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 51712-51967 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/51968-52223.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 51968-52223 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/52224-52479.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 52224-52479 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/52480-52735.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 52480-52735 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/52736-52991.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 52736-52991 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/52992-53247.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 52992-53247 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/53248-53503.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 53248-53503 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/53504-53759.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 53504-53759 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/53760-54015.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 53760-54015 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/54016-54271.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 54016-54271 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/54272-54527.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 54272-54527 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/54528-54783.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 54528-54783 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/54784-55039.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 54784-55039 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/55040-55295.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 55040-55295 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/55296-55551.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 55296-55551 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/55552-55807.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 55552-55807 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/55808-56063.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 55808-56063 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/56064-56319.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 56064-56319 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/56320-56575.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 56320-56575 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/56576-56831.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 56576-56831 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/56832-57087.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 56832-57087 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/57088-57343.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 57088-57343 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/57344-57599.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 57344-57599 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/57600-57855.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 57600-57855 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/57856-58111.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 57856-58111 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/58112-58367.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 58112-58367 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/58368-58623.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 58368-58623 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/58624-58879.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 58624-58879 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/58880-59135.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 58880-59135 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/59136-59391.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 59136-59391 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/59392-59647.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 59392-59647 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/59648-59903.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 59648-59903 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/59904-60159.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 59904-60159 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/60160-60415.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 60160-60415 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/60416-60671.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 60416-60671 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/60672-60927.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 60672-60927 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/60928-61183.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 60928-61183 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/61184-61439.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 61184-61439 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/61440-61695.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 61440-61695 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/61696-61951.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 61696-61951 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/61952-62207.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 61952-62207 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/62208-62463.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 62208-62463 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/62464-62719.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 62464-62719 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/62720-62975.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 62720-62975 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/62976-63231.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 62976-63231 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/63232-63487.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 63232-63487 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/63488-63743.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 63488-63743 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/63744-63999.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 63744-63999 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/64000-64255.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 64000-64255 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/64512-64767.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 64512-64767 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/64768-65023.pbf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noto Sans Regular 64768-65023 -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/9984-10239.pbf: -------------------------------------------------------------------------------- 1 | 2 |  3 | Noto Sans Regular 4 | 9984-10239 -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [michaelkreil] 2 | patreon: [MichaelKreil] 3 | open_collective: [versatiles] -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | github_checks: false 3 | coverage: 4 | status: 5 | project: off 6 | patch: off -------------------------------------------------------------------------------- /testdata/island.versatiles: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/testdata/island.versatiles -------------------------------------------------------------------------------- /static/assets/sprites/basics/sprites.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/sprites/basics/sprites.png -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/0-255.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/0-255.pbf -------------------------------------------------------------------------------- /static/assets/sprites/basics/sprites@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/sprites/basics/sprites@2x.png -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/256-511.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/256-511.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/512-767.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/512-767.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/1024-1279.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/1024-1279.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/1280-1535.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/1280-1535.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/7424-7679.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/7424-7679.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/768-1023.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/768-1023.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/7680-7935.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/7680-7935.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/7936-8191.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/7936-8191.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/8192-8447.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/8192-8447.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/8448-8703.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/8448-8703.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/8704-8959.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/8704-8959.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/8960-9215.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/8960-9215.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/9472-9727.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/9472-9727.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/9728-9983.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/9728-9983.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/0-255.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/0-255.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/256-511.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/256-511.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/512-767.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/512-767.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/10496-10751.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/10496-10751.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/11264-11519.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/11264-11519.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/11776-12031.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/11776-12031.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/42752-43007.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/42752-43007.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/64256-64511.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/64256-64511.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/65024-65279.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/65024-65279.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_bold/65280-65535.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_bold/65280-65535.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/1024-1279.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/1024-1279.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/1280-1535.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/1280-1535.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/7424-7679.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/7424-7679.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/768-1023.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/768-1023.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/7680-7935.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/7680-7935.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/7936-8191.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/7936-8191.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/8192-8447.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/8192-8447.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/8448-8703.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/8448-8703.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/8704-8959.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/8704-8959.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/8960-9215.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/8960-9215.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/9472-9727.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/9472-9727.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/9728-9983.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/9728-9983.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/10496-10751.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/10496-10751.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/11264-11519.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/11264-11519.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/11776-12031.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/11776-12031.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/42752-43007.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/42752-43007.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/64256-64511.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/64256-64511.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/65024-65279.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/65024-65279.pbf -------------------------------------------------------------------------------- /static/assets/glyphs/noto_sans_regular/65280-65535.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versatiles-org/node-versatiles-server/HEAD/static/assets/glyphs/noto_sans_regular/65280-65535.pbf -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "noEmit": false 6 | }, 7 | "include": [ 8 | "src/**/*.ts" 9 | ], 10 | "exclude": [ 11 | "src/**/*.test.ts" 12 | ] 13 | } -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: / 5 | schedule: 6 | interval: monthly 7 | groups: 8 | npm: 9 | patterns: ['*'] 10 | versioning-strategy: increase-if-necessary 11 | 12 | - package-ecosystem: github-actions 13 | directory: / 14 | schedule: 15 | interval: monthly 16 | groups: 17 | action: 18 | patterns: ['*'] -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": [ 4 | "ESNext" 5 | ], 6 | "module": "NodeNext", 7 | "moduleResolution": "NodeNext", 8 | "target": "ES2022", 9 | "strict": true, 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "declaration": true, 13 | "allowJs": true, 14 | "noEmit": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "isolatedModules": true 17 | }, 18 | "include": [ 19 | "**/src/**/*.ts", 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /src/lib/log.ts: -------------------------------------------------------------------------------- 1 | 2 | let doLogImportant = true; 3 | let doLogInfo = false; 4 | let doLogDebug = false; 5 | 6 | export function logImportant(text: string): void { 7 | if (doLogImportant) console.log(text); 8 | } 9 | 10 | export function logInfo(text: string): void { 11 | if (doLogInfo) console.log(text); 12 | } 13 | 14 | export function logDebug(text: string): void { 15 | if (doLogDebug) console.log(text); 16 | } 17 | 18 | export function setLogLevel(logLevel: number): void { 19 | // 0=quiet, 1=default, 2=verbose, 3=debug 20 | doLogImportant = logLevel > 0; 21 | doLogInfo = logLevel > 1; 22 | doLogDebug = logLevel > 2; 23 | } 24 | -------------------------------------------------------------------------------- /src/lib/file.ts: -------------------------------------------------------------------------------- 1 | import { existsSync } from 'fs'; 2 | import { readFile, stat } from 'fs/promises'; 3 | import { resolve } from 'path'; 4 | import type { ResponseContent } from './types.js'; 5 | import { getMimeByFilename } from './mime_types.js'; 6 | 7 | export async function getFileContent(staticFolder: string, path: string): Promise { 8 | let filename = resolve(staticFolder, path.replace(/^\/+/, '')); 9 | 10 | if (!filename.startsWith(staticFolder)) return; 11 | 12 | if (!existsSync(filename)) return; 13 | 14 | if ((await stat(filename)).isDirectory()) { 15 | filename = resolve(filename, 'index.html'); 16 | if (!existsSync(filename)) return; 17 | } 18 | 19 | return { 20 | buffer: await readFile(filename), 21 | compression: 'raw', 22 | mime: getMimeByFilename(filename), 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /src/lib/types.ts: -------------------------------------------------------------------------------- 1 | import type { Compression } from '@versatiles/container'; 2 | 3 | export interface ResponseContent { 4 | buffer: Buffer; 5 | mime?: string; 6 | compression?: Compression; 7 | } 8 | 9 | export interface ResponseConfig { 10 | acceptGzip: boolean; 11 | acceptBr: boolean; 12 | optimalCompression: boolean; 13 | } 14 | 15 | export interface ServerOptions { 16 | baseUrl: string; // Base URL for the server (default: "http://localhost:/") 17 | compress?: boolean; // Reduces traffic by recompressing data, but responses take longer. Perfect if behind CDN. 18 | glyphs: string; 19 | host?: string; // Hostname or IP to bind the server to', '0.0.0.0' 20 | port?: number; // Port to bind the server to (default: 8080) 21 | sprites: { id: string; url: string }[]; 22 | static?: string; // Path to a folder with static files 23 | tilesUrl: string; 24 | tms?: boolean; // Use TMS tile order (flip y axis) 25 | } 26 | -------------------------------------------------------------------------------- /src/lib/style.ts: -------------------------------------------------------------------------------- 1 | import { guessStyle, TileJSONSpecification } from '@versatiles/style'; 2 | import { ServerOptions } from './types.js'; 3 | 4 | /** 5 | * Asynchronously generates a style string based on the given container and options. 6 | * 7 | * @param {VersaTiles} container - An instance of the VersaTiles container. 8 | * @param {Record} serverOptions - An object containing options for style generation. 9 | * @returns {Promise} A promise that resolves to a style string. 10 | */ 11 | 12 | export function generateStyle(metadata: string, serverOptions: ServerOptions): string { 13 | let tileJSON: TileJSONSpecification; 14 | try { 15 | tileJSON = JSON.parse(metadata) 16 | } catch (_) { 17 | throw Error('invalid metadata'); 18 | } 19 | 20 | tileJSON.tiles = [serverOptions.tilesUrl]; 21 | 22 | const style = guessStyle(tileJSON, { 23 | baseUrl: serverOptions.baseUrl, 24 | sprite: serverOptions.sprites, 25 | glyphs: serverOptions.glyphs, 26 | }); 27 | 28 | return JSON.stringify(style); 29 | } 30 | -------------------------------------------------------------------------------- /src/lib/mime_types.ts: -------------------------------------------------------------------------------- 1 | import { logImportant } from './log.js'; 2 | 3 | const MIMETYPES = new Map([ 4 | ['avif', 'image/avif'], 5 | ['bin', 'application/octet-stream'], 6 | ['css', 'text/css; charset=utf-8'], 7 | ['geojson', 'application/geo+json; charset=utf-8'], 8 | ['htm', 'text/html; charset=utf-8'], 9 | ['html', 'text/html; charset=utf-8'], 10 | ['jpeg', 'image/jpeg'], 11 | ['jpg', 'image/jpeg'], 12 | ['js', 'text/javascript; charset=utf-8'], 13 | ['json', 'application/json; charset=utf-8'], 14 | ['pbf', 'application/x-protobuf'], 15 | ['png', 'image/png'], 16 | ['svg', 'image/svg+xml; charset=utf-8'], 17 | ['topojson', 'application/topo+json; charset=utf-8'], 18 | ['webp', 'image/webp'], 19 | ]); 20 | 21 | export function getMimeByFilename(filename: string, warn?: boolean): string { 22 | const format = filename.replace(/.*\./, '').toLowerCase(); 23 | 24 | if ((warn === true) && !MIMETYPES.has(format)) { 25 | logImportant('Error: can not guess MIME for file: ' + filename); 26 | } 27 | 28 | return MIMETYPES.get(format) ?? 'application/octet-stream'; 29 | } 30 | 31 | -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | VersaTiles 7 | 8 | 9 | 10 | 20 | 21 | 22 | 23 |
24 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js'; 2 | import ts from 'typescript-eslint'; 3 | import parser from '@typescript-eslint/parser'; 4 | import eslint_plugin from '@typescript-eslint/eslint-plugin'; 5 | 6 | export default [ 7 | js.configs.recommended, 8 | ...ts.configs.recommended, 9 | { 10 | ignores: [ 11 | 'coverage/**/*.*', 12 | 'dist/**/*.*', 13 | 'static/assets/lib/**/*.*', 14 | ] 15 | }, 16 | { 17 | files: [ 18 | 'src/**/*.ts', 19 | ], 20 | languageOptions: { 21 | ecmaVersion: 'latest', 22 | sourceType: 'module', 23 | globals: { 24 | browser: false, 25 | es6: true, 26 | node: true 27 | }, 28 | parser, 29 | parserOptions: { 30 | sourceType: 'module', 31 | project: './tsconfig.json', 32 | tsconfigRootDir: import.meta.dirname, 33 | }, 34 | }, 35 | plugins: { 36 | '@typescript-eslint': eslint_plugin, 37 | }, 38 | linterOptions: { 39 | reportUnusedDisableDirectives: true, 40 | }, 41 | rules: { 42 | 'no-unused-vars': 'off', 43 | '@typescript-eslint/no-unused-vars': [ 44 | 'error', 45 | { 46 | argsIgnorePattern: '^_', 47 | varsIgnorePattern: '^_', 48 | caughtErrorsIgnorePattern: '^_' 49 | } 50 | ] 51 | } 52 | } 53 | ] 54 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to [unlicense.org](https://unlicense.org/) 25 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - "**" 7 | tags: 8 | - "!**" # Don't run twice on commits with tags 9 | pull_request: 10 | workflow_dispatch: 11 | 12 | env: 13 | FORCE_COLOR: 1 14 | 15 | jobs: 16 | release: 17 | name: Lint and test 18 | runs-on: ubuntu-latest 19 | permissions: 20 | contents: read 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v6 24 | 25 | - name: Setup Node 26 | uses: actions/setup-node@v6 27 | with: 28 | node-version: 20 29 | cache: 'npm' 30 | 31 | - name: Install dependencies 32 | run: npm ci 33 | 34 | - name: Check vulnerabilities 35 | run: npm audit 36 | 37 | - name: Lint 38 | run: npm run lint 39 | 40 | - name: Build 41 | run: npm run build 42 | 43 | - name: Ensure Install # to ensure that binaries (like "vrt") are linked, so they can be used with "npx" 44 | run: npm i 45 | 46 | - name: Tests 47 | run: npm run test 48 | 49 | - name: Generate test coverage 50 | run: npm run test-coverage 51 | 52 | - name: Upload test coverage 53 | uses: codecov/codecov-action@v5 54 | env: 55 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 56 | -------------------------------------------------------------------------------- /src/lib/compressors.test.ts: -------------------------------------------------------------------------------- 1 | import { gzip, ungzip, brotli, unbrotli } from './compressors.js'; 2 | import { describe, it, expect } from 'vitest'; 3 | 4 | describe('Compression Module', () => { 5 | const testString = 'The quick brown fox jumps over the lazy dog'; 6 | const testData = Buffer.from(testString); 7 | 8 | // Test gzip compression and decompression 9 | it('gzip and ungzip', async () => { 10 | const gzipped = await gzip(testData); 11 | expect(gzipped).toBeInstanceOf(Buffer); 12 | 13 | const gunzipped = await ungzip(gzipped); 14 | expect(gunzipped).toBeInstanceOf(Buffer); 15 | expect(gunzipped.toString()).toEqual(testString); 16 | }); 17 | 18 | // Test brotli compression and decompression 19 | it('brotli and unbrotli', async () => { 20 | const brotlified = await brotli(testData); 21 | expect(brotlified).toBeInstanceOf(Buffer); 22 | 23 | const unbrotlified = await unbrotli(brotlified); 24 | expect(unbrotlified).toBeInstanceOf(Buffer); 25 | expect(unbrotlified.toString()).toEqual(testString); 26 | }); 27 | 28 | it('throws error on corrupt gzip data', async () => { 29 | await expect(ungzip(testData)).rejects.toThrow('incorrect header check'); 30 | }); 31 | 32 | it('throws error on corrupt brotli data', async () => { 33 | await expect(unbrotli(testData)).rejects.toThrow('Decompression failed'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/lib/style.test.ts: -------------------------------------------------------------------------------- 1 | import { ServerOptions } from './types.js'; 2 | import { generateStyle } from './style.js'; 3 | import { describe, it, expect } from 'vitest'; 4 | 5 | describe('generateStyle', () => { 6 | const validTileJSON = JSON.stringify({ 7 | tilejson: '2.2.0', 8 | tiles: ['https://example.com/{z}/{x}/{y}.png'], 9 | }); 10 | 11 | function getServerOptions(baseUrl?: string): ServerOptions { 12 | baseUrl = baseUrl || 'http://localhost:8080'; 13 | return { 14 | baseUrl, 15 | glyphs: baseUrl + '/assets/glyphs/{fontstack}/{range}.pbf', 16 | sprites: [{ id: 'basics', url: baseUrl + '/assets/sprites/basics/sprites' }], 17 | tilesUrl: baseUrl + '/tiles/test/{z}/{x}/{y}', 18 | }; 19 | } 20 | 21 | it('should generate a valid style for given metadata and server options', () => { 22 | const styleString = generateStyle(validTileJSON, getServerOptions()); 23 | expect(JSON.parse(styleString)).toEqual({ 24 | version: 8, 25 | layers: [{ id: 'raster', source: 'rasterSource', type: 'raster', },], 26 | sources: { rasterSource: { tiles: ['http://localhost:8080/tiles/test/{z}/{x}/{y}'], type: 'raster', }, }, 27 | }); 28 | }); 29 | 30 | it('should use the default base URL if none is provided', () => { 31 | const styleString = generateStyle(validTileJSON, getServerOptions('http://example.org:2345')); 32 | expect(JSON.parse(styleString)).toEqual({ 33 | version: 8, 34 | layers: [{ id: 'raster', source: 'rasterSource', type: 'raster', },], 35 | sources: { rasterSource: { tiles: ['http://example.org:2345/tiles/test/{z}/{x}/{y}'], type: 'raster', }, }, 36 | }); 37 | }); 38 | 39 | it('should throw an error if metadata is invalid JSON', () => { 40 | expect(() => generateStyle('invalid-json', getServerOptions())).toThrow('invalid metadata'); 41 | }); 42 | }); -------------------------------------------------------------------------------- /src/lib/layer.ts: -------------------------------------------------------------------------------- 1 | import type { Compression, Header, Reader } from '@versatiles/container'; 2 | import { Container } from '@versatiles/container'; 3 | import type { ResponseContent, ServerOptions } from './types.js'; 4 | import { generateStyle } from './style.js'; 5 | 6 | 7 | export class Layer { 8 | readonly #container: Container; 9 | 10 | #serverOptions: ServerOptions; 11 | #header?: Header; 12 | #metadata?: string; 13 | #mime?: string; 14 | #compression?: Compression; 15 | 16 | public constructor(source: Reader | string, serverOptions: ServerOptions) { 17 | this.#serverOptions = serverOptions 18 | this.#container = new Container(source, { tms: serverOptions?.tms ?? false }); 19 | } 20 | 21 | public async init(): Promise { 22 | if (this.#header) return; 23 | 24 | this.#header = await this.#container.getHeader(); 25 | this.#metadata = await this.#container.getMetadata() ?? '{}'; 26 | this.#mime = this.#header.tileMime; 27 | this.#compression = this.#header.tileCompression; 28 | } 29 | 30 | public async getTileFunction(): Promise<(z: number, x: number, y: number) => Promise> { 31 | await this.init(); 32 | 33 | const container = this.#container; 34 | const mime = this.#mime; 35 | const compression = this.#compression; 36 | 37 | return async (z: number, x: number, y: number): Promise => { 38 | const buffer = await container.getTile(z, x, y); 39 | if (!buffer) return null; 40 | return { buffer, mime, compression }; 41 | }; 42 | } 43 | 44 | public async getStyle(): Promise { 45 | await this.init(); 46 | if (!this.#metadata) throw Error(); 47 | return generateStyle(this.#metadata, this.#serverOptions); 48 | } 49 | 50 | public async getMetadata(): Promise { 51 | await this.init(); 52 | return this.#metadata; 53 | } 54 | } -------------------------------------------------------------------------------- /src/lib/compressors.ts: -------------------------------------------------------------------------------- 1 | 2 | import zlib from 'zlib'; 3 | 4 | /** 5 | * Compresses data using Gzip with maximum compression level. 6 | * @param dataIn - The input data as a Buffer to be compressed. 7 | * @returns A Promise that resolves to the Gzip-compressed Buffer. 8 | */ 9 | export async function gzip(dataIn: Buffer): Promise { 10 | return new Promise((res, rej) => { 11 | zlib.gzip(dataIn, { level: 9 }, (err, dataOut) => { 12 | if (err) { 13 | rej(err); 14 | return; 15 | } 16 | res(dataOut); 17 | }); 18 | }, 19 | ); 20 | } 21 | 22 | /** 23 | * Decompresses Gzip-compressed data. 24 | * @param dataIn - The Gzip-compressed data as a Buffer to be decompressed. 25 | * @returns A Promise that resolves to the decompressed Buffer. 26 | */ 27 | export async function ungzip(dataIn: Buffer): Promise { 28 | return new Promise((res, rej) => { 29 | zlib.gunzip(dataIn, (err, dataOut) => { 30 | if (err) { 31 | rej(err); 32 | return; 33 | } 34 | res(dataOut); 35 | }); 36 | }, 37 | ); 38 | } 39 | 40 | /** 41 | * Compresses data using Brotli with maximum quality parameter. 42 | * @param dataIn - The input data as a Buffer to be compressed. 43 | * @returns A Promise that resolves to the Brotli-compressed Buffer. 44 | */ 45 | export async function brotli(dataIn: Buffer): Promise { 46 | return new Promise((res, rej) => { 47 | zlib.brotliCompress(dataIn, { params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 11 } }, (err, dataOut) => { 48 | if (err) { 49 | rej(err); 50 | return; 51 | } 52 | res(dataOut); 53 | }); 54 | }, 55 | ); 56 | } 57 | 58 | /** 59 | * Decompresses Brotli-compressed data. 60 | * @param dataIn - The Brotli-compressed data as a Buffer to be decompressed. 61 | * @returns A Promise that resolves to the decompressed Buffer. 62 | */ 63 | export async function unbrotli(dataIn: Buffer): Promise { 64 | return new Promise((res, rej) => { 65 | zlib.brotliDecompress(dataIn, (err, dataOut) => { 66 | if (err) { 67 | rej(err); 68 | return; 69 | } 70 | res(dataOut); 71 | }); 72 | }, 73 | ); 74 | } 75 | -------------------------------------------------------------------------------- /src/lib/mime_types.test.ts: -------------------------------------------------------------------------------- 1 | import { vi, describe, it, expect } from 'vitest'; 2 | 3 | vi.mock('./log.js', () => ({ 4 | logImportant: vi.fn(), 5 | })); 6 | const { logImportant } = await import('./log.js'); 7 | const { getMimeByFilename } = await import('./mime_types.js'); 8 | 9 | describe('MIME Type Tests', () => { 10 | describe('getMimeByFilename', () => { 11 | it('should return the correct MIME type for valid filenames', () => { 12 | expect(getMimeByFilename('image.avif')).toBe('image/avif'); 13 | expect(getMimeByFilename('data.bin')).toBe('application/octet-stream'); 14 | expect(getMimeByFilename('style.css')).toBe('text/css; charset=utf-8'); 15 | expect(getMimeByFilename('data.geojson')).toBe('application/geo+json; charset=utf-8'); 16 | expect(getMimeByFilename('index.htm')).toBe('text/html; charset=utf-8'); 17 | expect(getMimeByFilename('index.html')).toBe('text/html; charset=utf-8'); 18 | expect(getMimeByFilename('image.jpeg')).toBe('image/jpeg'); 19 | expect(getMimeByFilename('image.jpg')).toBe('image/jpeg'); 20 | expect(getMimeByFilename('script.js')).toBe('text/javascript; charset=utf-8'); 21 | expect(getMimeByFilename('data.json')).toBe('application/json; charset=utf-8'); 22 | expect(getMimeByFilename('data.pbf')).toBe('application/x-protobuf'); 23 | expect(getMimeByFilename('image.png')).toBe('image/png'); 24 | expect(getMimeByFilename('image.svg')).toBe('image/svg+xml; charset=utf-8'); 25 | expect(getMimeByFilename('data.topojson')).toBe('application/topo+json; charset=utf-8'); 26 | expect(getMimeByFilename('image.webp')).toBe('image/webp'); 27 | }); 28 | 29 | it('should return default MIME type for filenames with invalid extensions', () => { 30 | vi.mocked(logImportant).mockClear(); 31 | expect(getMimeByFilename('file.unknown', true)).toBe('application/octet-stream'); 32 | expect(logImportant).toHaveBeenCalledWith('Error: can not guess MIME for file: file.unknown'); 33 | }); 34 | 35 | it('should handle filenames with no extension', () => { 36 | expect(getMimeByFilename('file')).toBe('application/octet-stream'); 37 | }); 38 | 39 | it('should be case insensitive for extensions', () => { 40 | expect(getMimeByFilename('IMAGE.toPOjSOn')).toBe('application/topo+json; charset=utf-8'); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /src/lib/log.test.ts: -------------------------------------------------------------------------------- 1 | // Assuming the module above is named 'logger.js' 2 | import * as logger from './log.js'; 3 | import { describe, it, expect,vi, beforeEach, afterAll } from 'vitest'; 4 | 5 | describe('logger module', () => { 6 | beforeEach(() => { 7 | // Reset log level to default before each test 8 | logger.setLogLevel(1); 9 | // Spy on console.log to verify if it's being called 10 | vi.spyOn(console, 'log'); 11 | // Clear all mocks to ensure a clean slate for each test 12 | vi.mocked(console.log).mockReturnThis().mockClear(); 13 | }); 14 | 15 | afterAll(() => { 16 | // Restore the original console.log function to its original state 17 | vi.mocked(console.log).mockRestore(); 18 | }); 19 | 20 | it('logImportant should log text when doLogImportant is true', () => { 21 | const text = 'Important message'; 22 | logger.logImportant(text); 23 | expect(console.log).toHaveBeenCalledWith(text); 24 | }); 25 | 26 | it('logInfo should not log text when doLogInfo is false', () => { 27 | const text = 'Info message'; 28 | logger.logInfo(text); 29 | expect(console.log).not.toHaveBeenCalled(); 30 | }); 31 | 32 | it('logDebug should not log text when doLogDebug is false', () => { 33 | const text = 'Debug message'; 34 | logger.logDebug(text); 35 | expect(console.log).not.toHaveBeenCalled(); 36 | }); 37 | 38 | it('logInfo and logDebug should log text when setLogLevel is called with verbose level', () => { 39 | logger.setLogLevel(3); // Verbose level, enabling all logs 40 | const infoText = 'Info message'; 41 | const debugText = 'Debug message'; 42 | logger.logInfo(infoText); 43 | logger.logDebug(debugText); 44 | expect(console.log).toHaveBeenCalledWith(infoText); 45 | expect(console.log).toHaveBeenCalledWith(debugText); 46 | }); 47 | 48 | it('setLogLevel should correctly set log levels', () => { 49 | logger.setLogLevel(0); 50 | expect(console.log).not.toHaveBeenCalled(); 51 | 52 | const importantText = 'Important message'; 53 | logger.logImportant(importantText); 54 | expect(console.log).not.toHaveBeenCalled(); // doLogImportant is false after setLogLevel(0) 55 | 56 | logger.setLogLevel(2); // Should enable important and info logs 57 | logger.logImportant(importantText); 58 | logger.logInfo('Info message'); 59 | expect(console.log).toHaveBeenCalledWith(importantText); 60 | expect(console.log).toHaveBeenCalledTimes(2); // Only logImportant should be called 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@versatiles/server", 3 | "version": "1.6.4", 4 | "description": "A server for VersaTiles", 5 | "bin": { 6 | "versatiles-server": "./dist/index.js" 7 | }, 8 | "prefix": "./", 9 | "files": [ 10 | "dist/**/*.js", 11 | "dist/**/*.d.ts", 12 | "static" 13 | ], 14 | "scripts": { 15 | "build": "npm run build-node && npm run doc", 16 | "build-node": "rm -rf dist && tsc -p tsconfig.build.json && chmod +x dist/index.js", 17 | "check": "npm run lint && npm run build && npm run test", 18 | "doc": "npm run doc-command && npm run doc-graph", 19 | "doc-command": "vrt doc-command versatiles-server | vrt doc-insert README.md '## Options'", 20 | "doc-graph": "vrt deps-graph | vrt doc-insert README.md '## Dependency Graph'", 21 | "lint": "eslint . --color", 22 | "prepack": "npm run build", 23 | "release": "vrt release-npm", 24 | "start": "tsx src/index.ts", 25 | "test-coverage": "vitest --run --coverage", 26 | "test": "vitest --run", 27 | "upgrade": "vrt deps-upgrade" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git+https://github.com/versatiles-org/node-versatiles.git" 32 | }, 33 | "homepage": "https://github.com/versatiles-org/node-versatiles/blob/main/versatiles-server/README.md", 34 | "type": "module", 35 | "author": "yetzt , Michael Kreil ", 36 | "license": "Unlicense", 37 | "engines": { 38 | "node": ">= 18" 39 | }, 40 | "keywords": [ 41 | "cloudtiles", 42 | "geo", 43 | "gis", 44 | "map", 45 | "maps", 46 | "mapserver", 47 | "maptiles", 48 | "mvt", 49 | "opencloudtiles", 50 | "osm", 51 | "pbf", 52 | "tile", 53 | "tiles", 54 | "tileserver", 55 | "vectortiles", 56 | "versatiles" 57 | ], 58 | "dependencies": { 59 | "@versatiles/container": "^1.2.8", 60 | "@versatiles/style": "^5.7.0", 61 | "commander": "^14.0.2", 62 | "open": "^11.0.0" 63 | }, 64 | "devDependencies": { 65 | "@types/node": "^24.10.1", 66 | "@typescript-eslint/eslint-plugin": "^8.47.0", 67 | "@typescript-eslint/parser": "^8.47.0", 68 | "@versatiles/release-tool": "^2.4.5", 69 | "@vitest/coverage-v8": "^4.0.10", 70 | "eslint": "^9.39.1", 71 | "ts-node": "^10.9.2", 72 | "tsx": "^4.20.6", 73 | "typescript": "^5.9.3", 74 | "typescript-eslint": "^8.47.0", 75 | "vitest": "^4.0.10" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/lib/layer.test.ts: -------------------------------------------------------------------------------- 1 | import { Layer } from './layer.js'; 2 | import { ServerOptions } from './types.js'; 3 | import { describe, it, expect } from 'vitest'; 4 | 5 | 6 | describe('Layer class', () => { 7 | const filename = new URL('../../testdata/island.versatiles', import.meta.url).pathname; 8 | const baseUrl = 'http://example.org:1234'; 9 | const serverOptions: ServerOptions = { 10 | baseUrl, 11 | glyphs: baseUrl + '/assets/glyphs/{fontstack}/{range}.pbf', 12 | sprites: [{ id: 'basics', url: baseUrl + '/assets/sprites/basics/sprites' }], 13 | tilesUrl: baseUrl + '/tiles/test/{z}/{x}/{y}.pbf', 14 | }; 15 | 16 | describe('constructor', () => { 17 | it('should initialize the Layer instance correctly', () => { 18 | const layer = new Layer(filename, serverOptions); 19 | expect(layer).toBeDefined(); 20 | }); 21 | }); 22 | 23 | describe('getTileFunction', () => { 24 | it('should return a function that fetches tiles correctly', async () => { 25 | const layer = new Layer(filename, serverOptions); 26 | const tileFunc = await layer.getTileFunction(); 27 | expect(typeof tileFunc).toBe('function'); 28 | 29 | const tileResponse = await tileFunc(8, 55, 67); 30 | expect(tileResponse).toBeDefined(); 31 | expect(tileResponse?.buffer.length).toBe(3548); 32 | expect(tileResponse?.mime).toBe('application/x-protobuf'); 33 | expect(tileResponse?.compression).toBe('br'); 34 | }); 35 | 36 | it('should handle null response for non-existent tiles', async () => { 37 | const layer = new Layer(filename, serverOptions); 38 | const tileFunc = await layer.getTileFunction(); 39 | expect(await tileFunc(1, 2, 3)).toBeNull(); 40 | }); 41 | }); 42 | 43 | describe('getStyle', () => { 44 | it('should generate the correct style string', async () => { 45 | const layer = new Layer(filename, serverOptions); 46 | const style: unknown = JSON.parse(await layer.getStyle()); 47 | expect(style).toMatchObject({ 48 | glyphs: 'http://example.org:1234/assets/glyphs/{fontstack}/{range}.pbf', 49 | sprite: [ 50 | { id: 'basics', url: 'http://example.org:1234/assets/sprites/basics/sprites' }, 51 | ] 52 | }); 53 | }); 54 | }); 55 | 56 | describe('getMetadata', () => { 57 | it('should return the correct metadata', async () => { 58 | const layer = new Layer(filename, serverOptions); 59 | const metadata = JSON.parse(await layer.getMetadata() ?? ''); 60 | expect(metadata?.vector_layers?.length).toBe(26); 61 | }); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /src/lib/response.ts: -------------------------------------------------------------------------------- 1 | import type { Compression } from '@versatiles/container'; 2 | import type { ServerResponse } from 'http'; 3 | import { brotli, gzip, unbrotli, ungzip } from './compressors.js'; 4 | import type { ResponseConfig, ResponseContent } from './types.js'; 5 | import { logImportant } from './log.js'; 6 | 7 | 8 | export class Response { 9 | readonly #response: ServerResponse; 10 | 11 | public constructor(response: ServerResponse) { 12 | this.#response = response; 13 | } 14 | 15 | public sendJSONString(json: string, config: ResponseConfig): Promise { 16 | return this.sendContent({ 17 | buffer: Buffer.from(json), 18 | compression: 'raw', 19 | mime: 'application/json; charset=utf-8' 20 | }, config); 21 | } 22 | 23 | public async sendContent(response: ResponseContent, config: ResponseConfig): Promise { 24 | const mime: string = response.mime ?? 'application/octet-stream'; 25 | let compression: Compression = response.compression ?? 'raw'; 26 | 27 | const { acceptGzip, acceptBr, optimalCompression } = config; 28 | 29 | let data = response.buffer; 30 | 31 | switch (compression) { 32 | case 'br': 33 | if (acceptBr) break; 34 | if (optimalCompression && acceptGzip) { 35 | data = await gzip(await unbrotli(data)); 36 | compression = 'gzip'; 37 | break; 38 | } 39 | data = await unbrotli(data); 40 | compression = 'raw'; 41 | break; 42 | case 'gzip': 43 | if (acceptGzip) break; 44 | if (optimalCompression && acceptBr) { 45 | data = await brotli(await ungzip(data)); 46 | compression = 'br'; 47 | break; 48 | } 49 | data = await ungzip(data); 50 | compression = 'raw'; 51 | break; 52 | default: // raw 53 | if (optimalCompression && acceptBr) { 54 | data = await brotli(data); 55 | compression = 'br'; 56 | break; 57 | } 58 | if (optimalCompression && acceptGzip) { 59 | data = await gzip(data); 60 | compression = 'gzip'; 61 | break; 62 | } 63 | compression = 'raw'; 64 | break; 65 | } 66 | 67 | if (compression !== 'raw') this.#response.setHeader('content-encoding', compression); 68 | 69 | this.#response.statusCode = 200; 70 | this.#response.setHeader('content-type', mime); 71 | this.#response.end(data); 72 | } 73 | 74 | public sendError(err: unknown, code = 500): void { 75 | logImportant(String(err)); 76 | this.#response.statusCode = code; 77 | this.#response.setHeader('content-type', 'text/plain'); 78 | this.#response.end((typeof err == 'string') ? err : 'internal error'); 79 | } 80 | } -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { Command } from 'commander'; 4 | import type { ServerOptions } from './lib/types.js'; 5 | import open from 'open'; 6 | import { Server } from './lib/server.js'; 7 | import { resolve } from 'path'; 8 | import { logImportant, setLogLevel } from './lib/log.js'; 9 | 10 | /** 11 | * Entry script for the VersaTiles server command-line application. 12 | * Utilizes the commander.js library to parse command-line arguments and options, 13 | * sets up the server based on these options, and optionally opens the server URL in a web browser. 14 | */ 15 | export const program = new Command(); 16 | 17 | program 18 | .showHelpAfterError() 19 | .name('versatiles-server') 20 | .description('Simple VersaTiles server') 21 | .option('-b, --base-url ', 'Base URL for the server (default: "http://localhost:/")') 22 | .option('-c, --compress', 'Reduces traffic by recompressing data, but responses take longer. Perfect if behind CDN.') 23 | .option('-h, --host ', 'Hostname or IP to bind the server to', '0.0.0.0') 24 | .option('-o, --open', 'Open map in web browser') 25 | .option('-p, --port ', 'Port to bind the server to (default: 8080)') 26 | .option('-q, --quiet', 'be quiet') 27 | .option('-s, --static ', 'Path to a folder with static files') 28 | .option('-t, --tms', 'Use TMS tile order (flip y axis)') 29 | .option('-v, --verbose', 'be verbose', (_, previous) => previous + 1, 0) 30 | .argument('', 'VersaTiles container, can be a URL or filename of a "*.versatiles" file') 31 | .action(async (source: string, cmdOptions: Record) => { 32 | const srvOptions: Partial = { 33 | baseUrl: cmdOptions.baseUrl as string | undefined, 34 | compress: Boolean(cmdOptions.compress), 35 | host: String(cmdOptions.host ?? '0.0.0.0'), 36 | port: Number(cmdOptions.port ?? 8080), 37 | static: cmdOptions.static != null ? resolve(process.cwd(), cmdOptions.static as string) : undefined, 38 | tms: Boolean(cmdOptions.tms), 39 | }; 40 | 41 | setLogLevel(cmdOptions.quiet ? 0 : Number(cmdOptions.verbose ?? 0) + 1); 42 | 43 | if (!source) throw Error('source not defined'); 44 | 45 | try { 46 | const server = new Server(source, srvOptions); 47 | void server.start(); 48 | 49 | if (cmdOptions.open) { 50 | console.log('Opening web browser...'); 51 | await open(server.getUrl()); 52 | } 53 | } catch (error: unknown) { 54 | const errorMessage = String((typeof error == 'object' && error != null && 'message' in error) ? error.message : error); 55 | logImportant(`Error starting the server: ${errorMessage}`); 56 | process.exit(1); 57 | } 58 | }); 59 | 60 | program.parse(); 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Code Coverage](https://codecov.io/gh/versatiles-org/node-versatiles-server/branch/main/graph/badge.svg?token=IDHAI13M0K)](https://codecov.io/gh/versatiles-org/node-versatiles-server) 2 | [![GitHub Workflow Status)](https://img.shields.io/github/actions/workflow/status/versatiles-org/node-versatiles-server/ci.yml)](https://github.com/versatiles-org/node-versatiles-server/actions/workflows/ci.yml) 3 | 4 | # VersaTiles - Server 5 | 6 | A Node.js server for [VersaTiles containers](https://github.com/versatiles-org/versatiles-spec). 7 | 8 | ## Install globally 9 | 10 | ```bash 11 | npm i -g @versatiles/server 12 | ``` 13 | 14 | ## Run 15 | 16 | ```bash 17 | versatiles-server --help 18 | ``` 19 | 20 | This will show you how to use the server with all options and arguments. 21 | 22 | For example: 23 | 24 | ```bash 25 | versatiles-server planet.versatiles 26 | ``` 27 | 28 | ## Options 29 | 30 | 31 | 32 | ```console 33 | $ versatiles-server 34 | Usage: versatiles-server [options] 35 | 36 | Simple VersaTiles server 37 | 38 | Arguments: 39 | source VersaTiles container, can be a URL or filename of a 40 | "*.versatiles" file 41 | 42 | Options: 43 | -b, --base-url Base URL for the server (default: 44 | "http://localhost:/") 45 | -c, --compress Reduces traffic by recompressing data, but responses 46 | take longer. Perfect if behind CDN. 47 | -h, --host Hostname or IP to bind the server to (default: 48 | "0.0.0.0") 49 | -o, --open Open map in web browser 50 | -p, --port Port to bind the server to (default: 8080) 51 | -q, --quiet be quiet 52 | -s, --static Path to a folder with static files 53 | -t, --tms Use TMS tile order (flip y axis) 54 | -v, --verbose be verbose 55 | --help display help for command 56 | ``` 57 | 58 | ## Dependency Graph 59 | 60 | 61 | 62 | ```mermaid 63 | --- 64 | config: 65 | layout: elk 66 | --- 67 | flowchart TB 68 | 69 | subgraph 0["src"] 70 | 1["index.ts"] 71 | subgraph 2["lib"] 72 | 3["log.ts"] 73 | 4["server.ts"] 74 | 5["file.ts"] 75 | 6["mime_types.ts"] 76 | 7["layer.ts"] 77 | 8["style.ts"] 78 | 9["response.ts"] 79 | A["compressors.ts"] 80 | B["types.ts"] 81 | end 82 | end 83 | 1-->3 84 | 1-->4 85 | 4-->5 86 | 4-->7 87 | 4-->3 88 | 4-->9 89 | 5-->6 90 | 6-->3 91 | 7-->8 92 | 9-->A 93 | 9-->3 94 | 95 | class 0,2 subgraphs; 96 | classDef subgraphs fill-opacity:0.1, fill:#888, color:#888, stroke:#888; 97 | ``` 98 | 99 | ## License 100 | 101 | [Unlicense](./LICENSE.md) 102 | -------------------------------------------------------------------------------- /src/lib/file.test.ts: -------------------------------------------------------------------------------- 1 | // Import the function to test 2 | import { vi, describe, it, expect, beforeEach } from 'vitest'; 3 | 4 | // Mock the `fs` and `fs/promises` modules 5 | vi.mock('fs', () => ({ 6 | existsSync: vi.fn(), 7 | })); 8 | vi.mock('fs/promises', () => ({ 9 | readFile: vi.fn().mockImplementation(async (filename: string) => Promise.resolve(`readFile(${filename})`)), 10 | stat: vi.fn(), 11 | })); 12 | 13 | // Get the mocked functions 14 | const { getFileContent } = await import('./file.js'); 15 | const { existsSync } = await import('fs'); 16 | const { readFile, stat } = await import('fs/promises'); 17 | 18 | describe('getFileContent', () => { 19 | beforeEach(() => { 20 | // Clear all mocks before each test 21 | vi.clearAllMocks(); 22 | }); 23 | 24 | it('returns undefined for paths not starting with static folder', async () => { 25 | const result = await getFileContent('/static', '../outside/path'); 26 | expect(vi.mocked(existsSync).mock.calls).toStrictEqual([]); 27 | expect(vi.mocked(stat).mock.calls).toStrictEqual([]); 28 | expect(vi.mocked(readFile).mock.calls).toStrictEqual([]); 29 | expect(result).toBeUndefined(); 30 | }); 31 | 32 | it('returns undefined if the file does not exist', async () => { 33 | vi.mocked(existsSync).mockReturnValue(false); 34 | const result = await getFileContent('/static', '/test/file.txt'); 35 | expect(vi.mocked(existsSync).mock.calls).toStrictEqual([['/static/test/file.txt']]); 36 | expect(vi.mocked(stat).mock.calls).toStrictEqual([]); 37 | expect(vi.mocked(readFile).mock.calls).toStrictEqual([]); 38 | expect(result).toBeUndefined(); 39 | }); 40 | 41 | it('returns the file path if the file exists and is not a directory', async () => { 42 | vi.mocked(existsSync).mockReturnValue(true); 43 | // @ts-expect-error: too lazy to fix this 44 | vi.mocked(stat).mockResolvedValue({ isDirectory: () => false }); 45 | const result = await getFileContent('/static', '/test/file.txt'); 46 | expect(vi.mocked(existsSync).mock.calls).toStrictEqual([['/static/test/file.txt']]); 47 | expect(vi.mocked(stat).mock.calls).toStrictEqual([['/static/test/file.txt']]); 48 | expect(vi.mocked(readFile).mock.calls).toStrictEqual([['/static/test/file.txt']]); 49 | expect(result).toStrictEqual({ buffer: 'readFile(/static/test/file.txt)', compression: 'raw', mime: 'application/octet-stream' }); 50 | }); 51 | 52 | it('returns index.html path for directories', async () => { 53 | // @ts-expect-error: too lazy to fix this 54 | vi.mocked(stat).mockResolvedValue({ isDirectory: () => true }); 55 | 56 | vi.mocked(existsSync) 57 | .mockReturnValueOnce(true) // True for directory existence 58 | .mockReturnValueOnce(true); // False for index.html existence 59 | 60 | const result = await getFileContent('/static', '/test/directory'); 61 | expect(vi.mocked(existsSync).mock.calls).toStrictEqual([['/static/test/directory'], ['/static/test/directory/index.html']]); 62 | expect(vi.mocked(stat).mock.calls).toStrictEqual([['/static/test/directory']]); 63 | expect(vi.mocked(readFile).mock.calls).toStrictEqual([['/static/test/directory/index.html']]); 64 | expect(result).toStrictEqual({ buffer: 'readFile(/static/test/directory/index.html)', compression: 'raw', mime: 'text/html; charset=utf-8' }); 65 | }); 66 | 67 | it('returns undefined for directories without an index.html', async () => { 68 | vi.mocked(existsSync) 69 | .mockReturnValueOnce(true) // True for directory existence 70 | .mockReturnValueOnce(false); // False for index.html existence 71 | // @ts-expect-error: too lazy to fix this 72 | vi.mocked(stat).mockResolvedValue({ isDirectory: () => true }); 73 | const result = await getFileContent('/static', '/test/directory'); 74 | expect(vi.mocked(existsSync).mock.calls).toStrictEqual([['/static/test/directory'], ['/static/test/directory/index.html']]); 75 | expect(vi.mocked(stat).mock.calls).toStrictEqual([['/static/test/directory']]); 76 | expect(vi.mocked(readFile).mock.calls).toStrictEqual([]); 77 | expect(result).toBeUndefined(); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /src/index.test.ts: -------------------------------------------------------------------------------- 1 | import { Command } from 'commander'; 2 | import { describe, it, expect, vi, beforeEach, Mocked } from 'vitest'; 3 | import type { ServerOptions } from './lib/types.js'; 4 | import type { Server } from './lib/server.js'; 5 | 6 | //const mockedServer = vi.fn().mockReturnValue(null); 7 | vi.mock('./lib/server.js', () => ({ 8 | Server: vi.fn(class { 9 | getUrl = vi.fn(() => 'https:/dingdong') 10 | start = vi.fn(() => Promise.resolve()) 11 | stop = vi.fn(() => Promise.resolve()) 12 | }), 13 | })); 14 | const mockedServer = (await import('./lib/server.js')).Server as unknown as Mocked; 15 | 16 | vi.mock('process'); 17 | vi.spyOn(process, 'exit').mockImplementation(vi.fn(() => null as never)); 18 | vi.spyOn(process.stdout, 'write').mockReturnValue(true); 19 | vi.spyOn(process.stderr, 'write').mockReturnValue(true); 20 | 21 | vi.spyOn(Command.prototype, 'parse').mockReturnThis(); 22 | 23 | vi.mock('open', () => ({ default: vi.fn(() => null) })); 24 | const open = (await import('open')).default; 25 | 26 | vi.mock('./lib/log.js', () => ({ 27 | logImportant: vi.fn(() => null), 28 | setLogLevel: vi.fn(() => null), 29 | })); 30 | const { setLogLevel } = await import('./lib/log.js'); 31 | 32 | describe('index.ts', () => { 33 | const defaultSource = 'test.versatiles'; 34 | const defaultResults: Partial = { 35 | compress: false, 36 | host: '0.0.0.0', 37 | port: 8080, 38 | tms: false, 39 | }; 40 | 41 | beforeEach(() => { 42 | vi.clearAllMocks(); 43 | }); 44 | 45 | it('starts server with no arguments', async () => { 46 | await expect(async () => run('')).rejects.toThrow('source not defined'); 47 | }); 48 | 49 | it('starts server with source', async () => { 50 | await run(defaultSource); 51 | expect(open).toHaveBeenCalledTimes(0); 52 | expect(mockedServer).toHaveBeenCalledWith(defaultSource, { ...defaultResults }); 53 | }); 54 | 55 | it('starts server with baseurl', async () => { 56 | await run(defaultSource + ' -b https://example.org'); 57 | expect(mockedServer).toHaveBeenCalledWith(defaultSource, { ...defaultResults, baseUrl: 'https://example.org' }); 58 | }); 59 | 60 | it('starts server with compress', async () => { 61 | await run(defaultSource + ' -c'); 62 | expect(mockedServer).toHaveBeenCalledWith(defaultSource, { ...defaultResults, compress: true }); 63 | }); 64 | 65 | it('starts server with host', async () => { 66 | await run(defaultSource + ' -h 9.0.0.0'); 67 | expect(mockedServer).toHaveBeenCalledWith(defaultSource, { ...defaultResults, host: '9.0.0.0' }); 68 | }); 69 | 70 | it('starts server with port', async () => { 71 | await run(defaultSource + ' -p 12345'); 72 | expect(mockedServer).toHaveBeenCalledWith(defaultSource, { ...defaultResults, port: 12345 }); 73 | }); 74 | 75 | it('starts server with open', async () => { 76 | await run(defaultSource + ' -o'); 77 | expect(mockedServer).toHaveBeenCalledWith(defaultSource, { ...defaultResults }); 78 | expect(open).toHaveBeenCalledTimes(1); 79 | expect(open).toHaveBeenCalledWith('https:/dingdong'); 80 | }); 81 | 82 | it('starts server with static', async () => { 83 | await run(defaultSource + ' -s /folder'); 84 | expect(mockedServer).toHaveBeenCalledWith(defaultSource, { ...defaultResults, static: '/folder' }); 85 | }); 86 | 87 | it('starts server with tms', async () => { 88 | await run(defaultSource + ' -t'); 89 | expect(mockedServer).toHaveBeenCalledWith(defaultSource, { ...defaultResults, tms: true }); 90 | }); 91 | 92 | it('starts server with quiet logging 1/2', async () => { 93 | await run(defaultSource + ' -q'); 94 | expect(setLogLevel).toHaveBeenCalledWith(0); 95 | }); 96 | 97 | it('starts server with quiet logging 2/2', async () => { 98 | await run(defaultSource + ' -qv'); 99 | expect(setLogLevel).toHaveBeenCalledWith(0); 100 | }); 101 | 102 | it('starts server with normal logging', async () => { 103 | await run(defaultSource); 104 | expect(setLogLevel).toHaveBeenCalledWith(1); 105 | }); 106 | 107 | it('starts server with verbose logging', async () => { 108 | await run(defaultSource + ' -v'); 109 | expect(setLogLevel).toHaveBeenCalledWith(2); 110 | }); 111 | 112 | it('starts server with very verbose logging', async () => { 113 | await run(defaultSource + ' -vv'); 114 | expect(setLogLevel).toHaveBeenCalledWith(3); 115 | }); 116 | 117 | async function run(args: string): Promise { 118 | const moduleUrl = './index.js?t=' + Math.random().toString(16).slice(2); 119 | const module = await import(moduleUrl); 120 | 121 | const program = (module.program) as Command; 122 | await program.parseAsync(['./node', './index.ts', ...args.split(' ').filter(a => a)]); 123 | } 124 | }); 125 | -------------------------------------------------------------------------------- /src/lib/server.ts: -------------------------------------------------------------------------------- 1 | import { createServer } from 'http'; 2 | import { Layer } from './layer.js'; 3 | import { Response } from './response.js'; 4 | import type { Reader } from '@versatiles/container'; 5 | import type { ResponseConfig, ServerOptions } from './types.js'; 6 | import type { Server as httpServer } from 'http'; 7 | import { getFileContent } from './file.js'; 8 | import { logDebug, logImportant, logInfo } from './log.js'; 9 | 10 | const STATIC_DIRNAME = new URL('../../static', import.meta.url).pathname; 11 | 12 | export class Server { 13 | readonly #options: ServerOptions; 14 | 15 | readonly #layer: Layer; 16 | 17 | #server?: httpServer; 18 | 19 | public constructor(source: Reader | string, options: Partial) { 20 | if (source == null) throw Error('source not defined'); 21 | 22 | const port = options.port ?? 8080; 23 | const baseUrl = options.baseUrl ?? `http://localhost:${port}/`; 24 | const tilesUrl = urlJoin(options.tilesUrl ?? '/tiles/default/{z}/{x}/{y}'); 25 | const sprites = urlJoin(options.sprites ?? [{ id: 'basics', url: '/assets/sprites/basics/sprites' }]); 26 | const glyphs = urlJoin(options.glyphs ?? 'assets/glyphs/{fontstack}/{range}.pbf'); 27 | const compress = options.compress ?? true; 28 | const host = options.host ?? '0.0.0.0'; 29 | this.#options = { ...options, port, baseUrl, tilesUrl, sprites, glyphs, compress, host }; 30 | 31 | this.#layer = new Layer(source, this.#options); 32 | 33 | function urlJoin(url: T): T { 34 | if (typeof url === 'string') { 35 | return new URL(url, baseUrl).href.replace(/%7B/g, '{').replace(/%7D/g, '}') as T; 36 | } 37 | if (Array.isArray(url)) { 38 | return url.map(({ id, url }) => ({ id, url: urlJoin(url) })) as T; 39 | } 40 | throw Error('invalid url'); 41 | } 42 | } 43 | 44 | public getUrl(): string { 45 | return this.#options.baseUrl ?? `http://localhost:${this.#options.port}/`; 46 | } 47 | 48 | public async start(): Promise { 49 | const getTile = await this.#layer.getTileFunction(); 50 | const recompress = this.#options.compress ?? false; 51 | 52 | const server = createServer((req, res) => { 53 | void (async (): Promise => { 54 | const response = new Response(res); 55 | 56 | try { 57 | if (req.method !== 'GET') { 58 | logImportant(`Error 405: Method "${req.method}" not allowed`); 59 | response.sendError('Method not allowed', 405); 60 | return; 61 | } 62 | 63 | if (!(req.url ?? '')) { 64 | logImportant('Error 404: URL not found'); 65 | response.sendError('URL not found', 404); 66 | return; 67 | } 68 | 69 | // check request 70 | const acceptedEncoding = req.headers['accept-encoding'] ?? ''; 71 | const responseConfig: ResponseConfig = { 72 | acceptBr: acceptedEncoding.includes('br'), 73 | acceptGzip: acceptedEncoding.includes('gzip'), 74 | optimalCompression: recompress, 75 | }; 76 | 77 | const path = new URL(req.url ?? '', 'resolve://').pathname; 78 | logInfo('new request: ' + path); 79 | 80 | // check if tile request 81 | const match = /^\/tiles\/default\/([0-9]+)\/([0-9]+)\/([0-9]+).*/.exec(path); 82 | 83 | if (match) { 84 | const [_, z, x, y] = match; 85 | const coords: [number, number, number] = [parseInt(z, 10), parseInt(x, 10), parseInt(y, 10)]; 86 | const tileResponse = await getTile(...coords); 87 | if (!tileResponse) { 88 | logImportant('Error 404: tile not found: ' + path); 89 | response.sendError('tile not found: ' + path, 404); 90 | return; 91 | } 92 | logInfo('send tile: ' + coords.join('/')); 93 | await response.sendContent(tileResponse, responseConfig); 94 | return; 95 | } 96 | 97 | if (path == '/tiles/default/tiles.json') { 98 | return await response.sendJSONString(await this.#layer.getMetadata() ?? '', responseConfig); 99 | } 100 | 101 | if (path == '/tiles/default/style.json') { 102 | return await response.sendJSONString(await this.#layer.getStyle(), responseConfig); 103 | } 104 | 105 | if (path == '/tiles/index.json') { 106 | return await response.sendJSONString('["default"]', responseConfig); 107 | } 108 | 109 | // check if request for user defined static content 110 | if (this.#options.static != null) { 111 | const content = await getFileContent(this.#options.static, path); 112 | 113 | if (content != null) { 114 | logDebug('send user defined static file'); 115 | return await response.sendContent(content, responseConfig); 116 | } 117 | } 118 | 119 | // check if request for standard static content 120 | const content = await getFileContent(STATIC_DIRNAME, path); 121 | if (content != null) { 122 | logDebug('send standard static file'); 123 | return await response.sendContent(content, responseConfig); 124 | } 125 | 126 | // error 404 127 | logImportant('Error 404: file not found: ' + path); 128 | response.sendError('file not found: ' + path, 404); 129 | return; 130 | 131 | } catch (err) { 132 | logImportant('Error 500: internal error: ' + String(err)); 133 | response.sendError(err, 500); 134 | return; 135 | } 136 | })(); 137 | }); 138 | 139 | this.#server = server; 140 | 141 | const { host, port } = this.#options; 142 | 143 | await new Promise(r => server.listen(port, host, () => { 144 | r(); 145 | })); 146 | 147 | logImportant(`listening on port ${port}`); 148 | } 149 | 150 | public async stop(): Promise { 151 | if (this.#server === undefined) return; 152 | 153 | await new Promise((res, rej) => { 154 | logInfo('stop server'); 155 | this.#server?.close(err => { 156 | if (err) rej(err); 157 | else res(); 158 | }); 159 | }); 160 | 161 | this.#server = undefined; 162 | } 163 | } -------------------------------------------------------------------------------- /src/lib/server.test.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs'; 2 | import { describe, it, expect, vi, beforeAll, afterEach, afterAll } from 'vitest'; 3 | import { createHash } from 'crypto'; 4 | import { resolve } from 'path'; 5 | import type { Server } from './server.js'; 6 | 7 | const DIRNAME = new URL('../../', import.meta.url).pathname; 8 | 9 | 10 | vi.mock('./log.js', () => ({ 11 | logDebug: vi.fn(), 12 | logImportant: vi.fn(), 13 | logInfo: vi.fn(), 14 | })); 15 | const { logImportant } = await import('./log.js'); 16 | const { Server: ServerClass } = await import('./server.js'); 17 | 18 | 19 | describe('Server', () => { 20 | let server: Server; 21 | const port = 56788; 22 | const baseUrl = `http://localhost:${port}`; 23 | const indexContent = readFileSync(resolve(DIRNAME, 'static/index.html'), 'utf8'); 24 | 25 | beforeAll(async () => { 26 | server = new ServerClass(resolve(DIRNAME, 'testdata/island.versatiles'), { port, compress: true }); 27 | await server.start(); 28 | expect(logImportant).toHaveBeenCalledWith('listening on port ' + port); 29 | }); 30 | 31 | afterEach(() => { 32 | vi.restoreAllMocks(); 33 | }); 34 | 35 | afterAll(async () => { 36 | await server.stop(); 37 | }); 38 | 39 | it('getUrl', async () => { 40 | expect(server.getUrl()).toBe(baseUrl + '/'); 41 | }); 42 | 43 | 44 | it('should serve static content', async () => { 45 | const response = await fetch(`${baseUrl}/index.html`, { 46 | headers: { 'Accept-Encoding': 'deflate' }, 47 | }); 48 | 49 | expect(response.status).toBe(200); 50 | expect(response.headers.get('content-type')).toBe('text/html; charset=utf-8'); 51 | expect(response.headers.get('content-encoding')).toBe(null); 52 | 53 | expect(await response.text()).toBe(indexContent); 54 | }); 55 | 56 | it('should serve Brotli compressed content', async () => { 57 | const response = await fetch(`${baseUrl}/index.html`, { 58 | headers: { 'Accept-Encoding': 'br' }, 59 | }); 60 | 61 | expect(response.status).toBe(200); 62 | expect(response.headers.get('content-type')).toBe('text/html; charset=utf-8'); 63 | expect(response.headers.get('content-encoding')).toBe('br'); 64 | 65 | expect(await response.text()).toBe(indexContent); 66 | }); 67 | 68 | it('should serve GZip compressed content', async () => { 69 | const response = await fetch(`${baseUrl}/index.html`, { 70 | headers: { 'Accept-Encoding': 'gzip' }, 71 | }); 72 | 73 | expect(response.status).toBe(200); 74 | expect(response.headers.get('content-type')).toBe('text/html; charset=utf-8'); 75 | expect(response.headers.get('content-encoding')).toBe('gzip'); 76 | 77 | expect(await response.text()).toBe(indexContent); 78 | }); 79 | 80 | it('should respond with 404 for unknown content', async () => { 81 | const response = await fetch(`${baseUrl}/nonexistent.file`); 82 | 83 | expect(response.status).toBe(404); 84 | expect(logImportant).toHaveBeenCalledWith('file not found: /nonexistent.file'); 85 | }); 86 | 87 | it('should serve tile data correctly 1/2', async () => { 88 | const response = await fetch(`${baseUrl}/tiles/default/8/55/67`); 89 | 90 | expect(response.status).toBe(200); 91 | expect(response.headers.get('content-type')).toBe('application/x-protobuf'); 92 | expect(await getResponseHash(response)).toBe('ISZuz4Nvv0yCNnZQpLxATu6lYTB5conusgV42FIYBm4='); 93 | }); 94 | 95 | it('should serve tile data correctly 2/2', async () => { 96 | const response = await fetch(`${baseUrl}/tiles/default/14/3740/4505`); 97 | 98 | expect(response.status).toBe(200); 99 | expect(response.headers.get('content-type')).toBe('application/x-protobuf'); 100 | expect(await getResponseHash(response)).toBe('yubXQj2G+xYXgIDaUXzPHqnhghRnjAUgFMe8mSQEE2A='); 101 | }); 102 | 103 | it('should throw error on missing tiles 1/2', async () => { 104 | const response = await fetch(`${baseUrl}/tiles/default/0/0/0`); 105 | 106 | expect(response.status).toBe(404); 107 | expect(response.headers.get('content-type')).toBe('text/plain'); 108 | expect(await response.text()).toBe('tile not found: /tiles/default/0/0/0'); 109 | expect(logImportant).toHaveBeenCalledWith('tile not found: /tiles/default/0/0/0'); 110 | }); 111 | 112 | it('should throw error on missing tiles 2/2', async () => { 113 | const response = await fetch(`${baseUrl}/tiles/default/12/34/56`); 114 | 115 | expect(response.status).toBe(404); 116 | expect(response.headers.get('content-type')).toBe('text/plain'); 117 | expect(await response.text()).toBe('tile not found: /tiles/default/12/34/56'); 118 | expect(logImportant).toHaveBeenCalledWith('tile not found: /tiles/default/12/34/56'); 119 | }); 120 | 121 | it('should handle unsupported HTTP methods with 405', async () => { 122 | const response = await fetch(`${baseUrl}/index.html`, { method: 'POST' }); 123 | 124 | expect(response.status).toBe(405); 125 | expect(logImportant).toHaveBeenCalledWith('Method not allowed'); 126 | }); 127 | }); 128 | 129 | describe('static files', () => { 130 | let server: Server; 131 | const port = 56789; 132 | const baseUrl = `http://localhost:${port}`; 133 | 134 | beforeAll(async () => { 135 | server = new ServerClass( 136 | resolve(DIRNAME, 'testdata/island.versatiles'), 137 | { port, static: resolve(DIRNAME) }, 138 | ); 139 | await server.start(); 140 | }); 141 | 142 | afterEach(() => { 143 | vi.restoreAllMocks(); 144 | }); 145 | 146 | afterAll(async () => { 147 | await server.stop(); 148 | }); 149 | 150 | it('should serve static files correctly', async () => { 151 | const response = await fetch(`${baseUrl}/static/`); 152 | 153 | expect(response.status).toBe(200); 154 | expect(response.headers.get('content-type')).toBe('text/html; charset=utf-8'); 155 | 156 | const text = await response.text() as string; 157 | expect(text).toBe(readFileSync(resolve(DIRNAME, 'static/index.html'), 'utf8')); 158 | }); 159 | }); 160 | 161 | async function getResponseHash(response: Response): Promise { 162 | const hasher = createHash('sha256'); 163 | hasher.update(Buffer.from(await response.arrayBuffer())); 164 | return hasher.digest('base64'); 165 | } 166 | -------------------------------------------------------------------------------- /src/lib/response.test.ts: -------------------------------------------------------------------------------- 1 | 2 | import { IncomingMessage, ServerResponse } from 'http'; 3 | import { Socket } from 'net'; 4 | import { describe, it, expect,vi, beforeEach, afterEach } from 'vitest'; 5 | import type { ResponseContent } from './types.js'; 6 | import type { Response } from './response.js'; 7 | import { brotliCompressSync, gzipSync } from 'zlib'; 8 | 9 | 10 | vi.mock('./log.js', () => ({ 11 | logImportant: vi.fn(), 12 | })); 13 | const { logImportant } = await import('./log.js'); 14 | const { Response: ResponseClass } = await import('./response.js'); 15 | 16 | 17 | type Compression = 'br' | 'gzip' | 'raw'; 18 | 19 | describe('Response Tests', () => { 20 | let mockRes: ServerResponse; 21 | let response: Response; 22 | 23 | beforeEach(() => { 24 | const mockSocket = new Socket(); 25 | const mockedRequest = new IncomingMessage(mockSocket); 26 | mockRes = vi.mocked(new ServerResponse(mockedRequest)); 27 | vi.spyOn(mockRes, 'setHeader'); 28 | vi.spyOn(mockRes, 'end'); 29 | vi.spyOn(console, 'error').mockImplementation(() => { 30 | return; 31 | }); 32 | response = new ResponseClass(mockRes); 33 | }); 34 | 35 | afterEach(() => { 36 | vi.restoreAllMocks(); 37 | }); 38 | 39 | describe('respond with content', () => { 40 | it('should set correct headers and respond with content', async () => { 41 | const content = { buffer: Buffer.from('test'), mime: 'text/plain' }; 42 | const config = { acceptGzip: true, acceptBr: false, optimalCompression: false }; 43 | 44 | await response.sendContent(content, config); 45 | 46 | expect(mockRes.setHeader).toHaveBeenCalledWith('content-type', 'text/plain'); 47 | expect(mockRes.statusCode).toBe(200); 48 | expect(mockRes.end).toHaveBeenCalledWith(Buffer.from('test')); 49 | }); 50 | 51 | it('should set correct mime if missing', async () => { 52 | const content = { buffer: Buffer.from('test') }; 53 | const config = { acceptGzip: true, acceptBr: false, optimalCompression: false }; 54 | 55 | await response.sendContent(content, config); 56 | 57 | expect(mockRes.setHeader).toHaveBeenCalledWith('content-type', 'application/octet-stream'); 58 | expect(mockRes.statusCode).toBe(200); 59 | expect(mockRes.end).toHaveBeenCalledWith(Buffer.from('test')); 60 | }); 61 | }); 62 | 63 | describe('respond using different compressions', () => { 64 | const testBuffer = Buffer.from('Governments of the Industrial World, you weary giants of flesh and steel, I come from Cyberspace, the new home of Mind.'); 65 | const buffers: Record = { 66 | raw: Buffer.from(testBuffer), 67 | gzip: gzipSync(testBuffer), 68 | br: brotliCompressSync(testBuffer), 69 | }; 70 | 71 | const cases: { buffer: Compression; accept: Compression | 'gzip+br'; optimal: boolean; result: Compression }[] = [ 72 | // Optimal 73 | { buffer: 'raw', accept: 'raw', optimal: true, result: 'raw' }, 74 | { buffer: 'raw', accept: 'gzip', optimal: true, result: 'gzip' }, 75 | { buffer: 'raw', accept: 'br', optimal: true, result: 'br' }, 76 | { buffer: 'raw', accept: 'gzip+br', optimal: true, result: 'br' }, 77 | 78 | { buffer: 'gzip', accept: 'raw', optimal: true, result: 'raw' }, 79 | { buffer: 'gzip', accept: 'gzip', optimal: true, result: 'gzip' }, 80 | { buffer: 'gzip', accept: 'br', optimal: true, result: 'br' }, 81 | { buffer: 'gzip', accept: 'gzip+br', optimal: true, result: 'gzip' }, 82 | 83 | { buffer: 'br', accept: 'raw', optimal: true, result: 'raw' }, 84 | { buffer: 'br', accept: 'gzip', optimal: true, result: 'gzip' }, 85 | { buffer: 'br', accept: 'br', optimal: true, result: 'br' }, 86 | { buffer: 'br', accept: 'gzip+br', optimal: true, result: 'br' }, 87 | 88 | // Fast 89 | { buffer: 'raw', accept: 'raw', optimal: false, result: 'raw' }, 90 | { buffer: 'raw', accept: 'gzip', optimal: false, result: 'raw' }, 91 | { buffer: 'raw', accept: 'br', optimal: false, result: 'raw' }, 92 | { buffer: 'raw', accept: 'gzip+br', optimal: false, result: 'raw' }, 93 | 94 | { buffer: 'gzip', accept: 'raw', optimal: false, result: 'raw' }, 95 | { buffer: 'gzip', accept: 'gzip', optimal: false, result: 'gzip' }, 96 | { buffer: 'gzip', accept: 'br', optimal: false, result: 'raw' }, 97 | { buffer: 'gzip', accept: 'gzip+br', optimal: false, result: 'gzip' }, 98 | 99 | { buffer: 'br', accept: 'raw', optimal: false, result: 'raw' }, 100 | { buffer: 'br', accept: 'gzip', optimal: false, result: 'raw' }, 101 | { buffer: 'br', accept: 'br', optimal: false, result: 'br' }, 102 | { buffer: 'br', accept: 'gzip+br', optimal: false, result: 'br' }, 103 | ]; 104 | 105 | for (const { buffer, accept, optimal, result } of cases) { 106 | 107 | it(`${buffer} -> ${accept} (${optimal ? 'optimal' : 'fast'})`, async () => { 108 | const content: ResponseContent = { 109 | buffer: buffers[buffer], 110 | mime: 'text/plain', 111 | compression: buffer, 112 | }; 113 | const config = { 114 | acceptGzip: (accept === 'gzip') || (accept === 'gzip+br'), 115 | acceptBr: (accept === 'br') || (accept === 'gzip+br'), 116 | optimalCompression: optimal, 117 | }; 118 | 119 | await response.sendContent(content, config); 120 | 121 | switch (result) { 122 | case 'raw': 123 | expect(vi.mocked(mockRes.setHeader).mock.calls).toStrictEqual([ 124 | ['content-type', 'text/plain'], 125 | ]); 126 | break; 127 | case 'gzip': 128 | expect(vi.mocked(mockRes.setHeader).mock.calls).toStrictEqual([ 129 | ['content-encoding', 'gzip'], 130 | ['content-type', 'text/plain'], 131 | ]); 132 | break; 133 | case 'br': 134 | expect(vi.mocked(mockRes.setHeader).mock.calls).toStrictEqual([ 135 | ['content-encoding', 'br'], 136 | ['content-type', 'text/plain'], 137 | ]); 138 | break; 139 | } 140 | 141 | expect(mockRes.statusCode).toBe(200); 142 | expect(mockRes.end).toHaveBeenCalledTimes(1); 143 | }); 144 | 145 | } 146 | }); 147 | 148 | describe('respond with error', () => { 149 | it('should handle error responses', () => { 150 | const error = new Error('Test Error'); 151 | response.sendError(error, 500); 152 | 153 | expect(logImportant).toHaveBeenCalledWith('Error: Test Error'); 154 | expect(mockRes.setHeader).toHaveBeenCalledWith('content-type', 'text/plain'); 155 | expect(mockRes.statusCode).toBe(500); 156 | expect(mockRes.end).toHaveBeenCalledWith('internal error'); 157 | }); 158 | 159 | it('should handle error messages', () => { 160 | response.sendError('message', 500); 161 | 162 | expect(logImportant).toHaveBeenCalledWith('message'); 163 | expect(mockRes.setHeader).toHaveBeenCalledWith('content-type', 'text/plain'); 164 | expect(mockRes.statusCode).toBe(500); 165 | expect(mockRes.end).toHaveBeenCalledWith('message'); 166 | }); 167 | }); 168 | }); 169 | -------------------------------------------------------------------------------- /static/assets/sprites/basics/sprites.json: -------------------------------------------------------------------------------- 1 | { 2 | "icon-airfield": {"width":32,"height":32,"x":0,"y":0,"pixelRatio":1,"sdf":true}, 3 | "icon-airport": {"width":32,"height":32,"x":32,"y":0,"pixelRatio":1,"sdf":true}, 4 | "icon-alcohol_shop": {"width":32,"height":32,"x":0,"y":32,"pixelRatio":1,"sdf":true}, 5 | "icon-art_gallery": {"width":32,"height":32,"x":32,"y":32,"pixelRatio":1,"sdf":true}, 6 | "icon-artwork": {"width":32,"height":32,"x":64,"y":0,"pixelRatio":1,"sdf":true}, 7 | "icon-atm": {"width":32,"height":32,"x":64,"y":32,"pixelRatio":1,"sdf":true}, 8 | "icon-bakery": {"width":32,"height":32,"x":0,"y":64,"pixelRatio":1,"sdf":true}, 9 | "icon-bank": {"width":32,"height":32,"x":32,"y":64,"pixelRatio":1,"sdf":true}, 10 | "icon-bar": {"width":32,"height":32,"x":64,"y":64,"pixelRatio":1,"sdf":true}, 11 | "icon-beauty": {"width":32,"height":32,"x":96,"y":0,"pixelRatio":1,"sdf":true}, 12 | "icon-beer": {"width":32,"height":32,"x":96,"y":32,"pixelRatio":1,"sdf":true}, 13 | "icon-beergarden": {"width":32,"height":32,"x":96,"y":64,"pixelRatio":1,"sdf":true}, 14 | "icon-bench": {"width":32,"height":32,"x":0,"y":96,"pixelRatio":1,"sdf":true}, 15 | "icon-beverages": {"width":32,"height":32,"x":32,"y":96,"pixelRatio":1,"sdf":true}, 16 | "icon-bicycle_share": {"width":32,"height":32,"x":64,"y":96,"pixelRatio":1,"sdf":true}, 17 | "icon-books": {"width":32,"height":32,"x":96,"y":96,"pixelRatio":1,"sdf":true}, 18 | "icon-bus": {"width":32,"height":32,"x":128,"y":0,"pixelRatio":1,"sdf":true}, 19 | "icon-butcher": {"width":32,"height":32,"x":128,"y":32,"pixelRatio":1,"sdf":true}, 20 | "icon-cafe": {"width":32,"height":32,"x":128,"y":64,"pixelRatio":1,"sdf":true}, 21 | "icon-car_rental": {"width":32,"height":32,"x":128,"y":96,"pixelRatio":1,"sdf":true}, 22 | "icon-car_wash": {"width":32,"height":32,"x":0,"y":128,"pixelRatio":1,"sdf":true}, 23 | "icon-castle": {"width":32,"height":32,"x":32,"y":128,"pixelRatio":1,"sdf":true}, 24 | "icon-cemetery": {"width":32,"height":32,"x":64,"y":128,"pixelRatio":1,"sdf":true}, 25 | "icon-chalet": {"width":32,"height":32,"x":96,"y":128,"pixelRatio":1,"sdf":true}, 26 | "icon-chemist": {"width":32,"height":32,"x":128,"y":128,"pixelRatio":1,"sdf":true}, 27 | "icon-cinema": {"width":32,"height":32,"x":160,"y":0,"pixelRatio":1,"sdf":true}, 28 | "icon-clothes": {"width":32,"height":32,"x":160,"y":32,"pixelRatio":1,"sdf":true}, 29 | "icon-college": {"width":32,"height":32,"x":160,"y":64,"pixelRatio":1,"sdf":true}, 30 | "icon-community": {"width":32,"height":32,"x":160,"y":96,"pixelRatio":1,"sdf":true}, 31 | "icon-defibrillator": {"width":32,"height":32,"x":160,"y":128,"pixelRatio":1,"sdf":true}, 32 | "icon-dentist": {"width":32,"height":32,"x":0,"y":160,"pixelRatio":1,"sdf":true}, 33 | "icon-doctor": {"width":32,"height":32,"x":32,"y":160,"pixelRatio":1,"sdf":true}, 34 | "icon-dog_park": {"width":32,"height":32,"x":64,"y":160,"pixelRatio":1,"sdf":true}, 35 | "icon-doityourself": {"width":32,"height":32,"x":96,"y":160,"pixelRatio":1,"sdf":true}, 36 | "icon-drinking_water": {"width":32,"height":32,"x":128,"y":160,"pixelRatio":1,"sdf":true}, 37 | "icon-drycleaning": {"width":32,"height":32,"x":160,"y":160,"pixelRatio":1,"sdf":true}, 38 | "icon-embassy": {"width":32,"height":32,"x":192,"y":0,"pixelRatio":1,"sdf":true}, 39 | "icon-emergency_phone": {"width":32,"height":32,"x":192,"y":32,"pixelRatio":1,"sdf":true}, 40 | "icon-fast_food": {"width":32,"height":32,"x":192,"y":64,"pixelRatio":1,"sdf":true}, 41 | "icon-fire_station": {"width":32,"height":32,"x":192,"y":96,"pixelRatio":1,"sdf":true}, 42 | "icon-florist": {"width":32,"height":32,"x":192,"y":128,"pixelRatio":1,"sdf":true}, 43 | "icon-fountain": {"width":32,"height":32,"x":192,"y":160,"pixelRatio":1,"sdf":true}, 44 | "icon-furniture": {"width":32,"height":32,"x":0,"y":192,"pixelRatio":1,"sdf":true}, 45 | "icon-garden_centre": {"width":32,"height":32,"x":32,"y":192,"pixelRatio":1,"sdf":true}, 46 | "icon-gift": {"width":32,"height":32,"x":64,"y":192,"pixelRatio":1,"sdf":true}, 47 | "icon-golf": {"width":32,"height":32,"x":96,"y":192,"pixelRatio":1,"sdf":true}, 48 | "icon-greengrocer": {"width":32,"height":32,"x":128,"y":192,"pixelRatio":1,"sdf":true}, 49 | "icon-hairdresser": {"width":32,"height":32,"x":160,"y":192,"pixelRatio":1,"sdf":true}, 50 | "icon-hardware": {"width":32,"height":32,"x":192,"y":192,"pixelRatio":1,"sdf":true}, 51 | "icon-historic": {"width":32,"height":32,"x":224,"y":0,"pixelRatio":1,"sdf":true}, 52 | "icon-hospital": {"width":32,"height":32,"x":224,"y":32,"pixelRatio":1,"sdf":true}, 53 | "icon-huntingstand": {"width":32,"height":32,"x":224,"y":64,"pixelRatio":1,"sdf":true}, 54 | "icon-hydrant": {"width":32,"height":32,"x":224,"y":96,"pixelRatio":1,"sdf":true}, 55 | "icon-icerink": {"width":32,"height":32,"x":224,"y":128,"pixelRatio":1,"sdf":true}, 56 | "icon-jewelry_store": {"width":32,"height":32,"x":224,"y":160,"pixelRatio":1,"sdf":true}, 57 | "icon-kiosk": {"width":32,"height":32,"x":224,"y":192,"pixelRatio":1,"sdf":true}, 58 | "icon-laundry": {"width":32,"height":32,"x":0,"y":224,"pixelRatio":1,"sdf":true}, 59 | "icon-library": {"width":32,"height":32,"x":32,"y":224,"pixelRatio":1,"sdf":true}, 60 | "icon-lighthouse": {"width":32,"height":32,"x":64,"y":224,"pixelRatio":1,"sdf":true}, 61 | "icon-marketplace": {"width":32,"height":32,"x":96,"y":224,"pixelRatio":1,"sdf":true}, 62 | "icon-monument": {"width":32,"height":32,"x":128,"y":224,"pixelRatio":1,"sdf":true}, 63 | "icon-newsagent": {"width":32,"height":32,"x":160,"y":224,"pixelRatio":1,"sdf":true}, 64 | "icon-nightclub": {"width":32,"height":32,"x":192,"y":224,"pixelRatio":1,"sdf":true}, 65 | "icon-nursinghome": {"width":32,"height":32,"x":224,"y":224,"pixelRatio":1,"sdf":true}, 66 | "icon-observation_tower": {"width":32,"height":32,"x":256,"y":0,"pixelRatio":1,"sdf":true}, 67 | "icon-optician": {"width":32,"height":32,"x":256,"y":32,"pixelRatio":1,"sdf":true}, 68 | "icon-outdoor": {"width":32,"height":32,"x":256,"y":64,"pixelRatio":1,"sdf":true}, 69 | "icon-pharmacy": {"width":32,"height":32,"x":256,"y":96,"pixelRatio":1,"sdf":true}, 70 | "icon-picnic_site": {"width":32,"height":32,"x":256,"y":128,"pixelRatio":1,"sdf":true}, 71 | "icon-pitch": {"width":32,"height":32,"x":256,"y":160,"pixelRatio":1,"sdf":true}, 72 | "icon-place_of_worship": {"width":32,"height":32,"x":256,"y":192,"pixelRatio":1,"sdf":true}, 73 | "icon-playground": {"width":32,"height":32,"x":256,"y":224,"pixelRatio":1,"sdf":true}, 74 | "icon-police": {"width":32,"height":32,"x":0,"y":256,"pixelRatio":1,"sdf":true}, 75 | "icon-postbox": {"width":32,"height":32,"x":32,"y":256,"pixelRatio":1,"sdf":true}, 76 | "icon-post": {"width":32,"height":32,"x":64,"y":256,"pixelRatio":1,"sdf":true}, 77 | "icon-prison": {"width":32,"height":32,"x":96,"y":256,"pixelRatio":1,"sdf":true}, 78 | "icon-rail_light": {"width":32,"height":32,"x":128,"y":256,"pixelRatio":1,"sdf":true}, 79 | "icon-rail_metro": {"width":32,"height":32,"x":160,"y":256,"pixelRatio":1,"sdf":true}, 80 | "icon-rail": {"width":32,"height":32,"x":192,"y":256,"pixelRatio":1,"sdf":true}, 81 | "icon-recycling": {"width":32,"height":32,"x":224,"y":256,"pixelRatio":1,"sdf":true}, 82 | "icon-restaurant": {"width":32,"height":32,"x":256,"y":256,"pixelRatio":1,"sdf":true}, 83 | "icon-school": {"width":32,"height":32,"x":288,"y":0,"pixelRatio":1,"sdf":true}, 84 | "icon-shelter": {"width":32,"height":32,"x":288,"y":32,"pixelRatio":1,"sdf":true}, 85 | "icon-shoes": {"width":32,"height":32,"x":288,"y":64,"pixelRatio":1,"sdf":true}, 86 | "icon-shop": {"width":32,"height":32,"x":288,"y":96,"pixelRatio":1,"sdf":true}, 87 | "icon-shrine": {"width":32,"height":32,"x":288,"y":128,"pixelRatio":1,"sdf":true}, 88 | "icon-sports": {"width":32,"height":32,"x":288,"y":160,"pixelRatio":1,"sdf":true}, 89 | "icon-stadium": {"width":32,"height":32,"x":288,"y":192,"pixelRatio":1,"sdf":true}, 90 | "icon-stationery": {"width":32,"height":32,"x":288,"y":224,"pixelRatio":1,"sdf":true}, 91 | "icon-surveillance": {"width":32,"height":32,"x":288,"y":256,"pixelRatio":1,"sdf":true}, 92 | "icon-swimming": {"width":32,"height":32,"x":0,"y":288,"pixelRatio":1,"sdf":true}, 93 | "icon-telephone": {"width":32,"height":32,"x":32,"y":288,"pixelRatio":1,"sdf":true}, 94 | "icon-theatre": {"width":32,"height":32,"x":64,"y":288,"pixelRatio":1,"sdf":true}, 95 | "icon-toilet": {"width":32,"height":32,"x":96,"y":288,"pixelRatio":1,"sdf":true}, 96 | "icon-town_hall": {"width":32,"height":32,"x":128,"y":288,"pixelRatio":1,"sdf":true}, 97 | "icon-toys": {"width":32,"height":32,"x":160,"y":288,"pixelRatio":1,"sdf":true}, 98 | "icon-travel_agent": {"width":32,"height":32,"x":192,"y":288,"pixelRatio":1,"sdf":true}, 99 | "icon-vendingmachine": {"width":32,"height":32,"x":224,"y":288,"pixelRatio":1,"sdf":true}, 100 | "icon-veterinary": {"width":32,"height":32,"x":256,"y":288,"pixelRatio":1,"sdf":true}, 101 | "icon-video": {"width":32,"height":32,"x":288,"y":288,"pixelRatio":1,"sdf":true}, 102 | "icon-viewpoint": {"width":32,"height":32,"x":320,"y":0,"pixelRatio":1,"sdf":true}, 103 | "icon-waste_basket": {"width":32,"height":32,"x":320,"y":32,"pixelRatio":1,"sdf":true}, 104 | "icon-watermill": {"width":32,"height":32,"x":320,"y":64,"pixelRatio":1,"sdf":true}, 105 | "icon-waterpark": {"width":32,"height":32,"x":320,"y":96,"pixelRatio":1,"sdf":true}, 106 | "icon-windmill": {"width":32,"height":32,"x":320,"y":128,"pixelRatio":1,"sdf":true}, 107 | "icon-zoo": {"width":32,"height":32,"x":320,"y":160,"pixelRatio":1,"sdf":true}, 108 | "marking-arrow": {"width":14,"height":25,"x":352,"y":0,"pixelRatio":1,"sdf":true}, 109 | "pattern-hatched_thin": {"width":22,"height":22,"x":320,"y":256,"pixelRatio":1,"sdf":true}, 110 | "pattern-striped": {"width":22,"height":22,"x":320,"y":278,"pixelRatio":1,"sdf":true}, 111 | "pattern-warning": {"width":22,"height":22,"x":0,"y":320,"pixelRatio":1,"sdf":true}, 112 | "transport-tram": {"width":32,"height":32,"x":320,"y":192,"pixelRatio":1,"sdf":true}, 113 | "transport-information": {"width":32,"height":32,"x":320,"y":224,"pixelRatio":1,"sdf":true} 114 | } -------------------------------------------------------------------------------- /static/assets/sprites/basics/sprites@2x.json: -------------------------------------------------------------------------------- 1 | { 2 | "icon-airfield": {"width":64,"height":64,"x":0,"y":0,"pixelRatio":2,"sdf":true}, 3 | "icon-airport": {"width":64,"height":64,"x":64,"y":0,"pixelRatio":2,"sdf":true}, 4 | "icon-alcohol_shop": {"width":64,"height":64,"x":0,"y":64,"pixelRatio":2,"sdf":true}, 5 | "icon-art_gallery": {"width":64,"height":64,"x":64,"y":64,"pixelRatio":2,"sdf":true}, 6 | "icon-artwork": {"width":64,"height":64,"x":128,"y":0,"pixelRatio":2,"sdf":true}, 7 | "icon-atm": {"width":64,"height":64,"x":128,"y":64,"pixelRatio":2,"sdf":true}, 8 | "icon-bakery": {"width":64,"height":64,"x":0,"y":128,"pixelRatio":2,"sdf":true}, 9 | "icon-bank": {"width":64,"height":64,"x":64,"y":128,"pixelRatio":2,"sdf":true}, 10 | "icon-bar": {"width":64,"height":64,"x":128,"y":128,"pixelRatio":2,"sdf":true}, 11 | "icon-beauty": {"width":64,"height":64,"x":192,"y":0,"pixelRatio":2,"sdf":true}, 12 | "icon-beer": {"width":64,"height":64,"x":192,"y":64,"pixelRatio":2,"sdf":true}, 13 | "icon-beergarden": {"width":64,"height":64,"x":192,"y":128,"pixelRatio":2,"sdf":true}, 14 | "icon-bench": {"width":64,"height":64,"x":0,"y":192,"pixelRatio":2,"sdf":true}, 15 | "icon-beverages": {"width":64,"height":64,"x":64,"y":192,"pixelRatio":2,"sdf":true}, 16 | "icon-bicycle_share": {"width":64,"height":64,"x":128,"y":192,"pixelRatio":2,"sdf":true}, 17 | "icon-books": {"width":64,"height":64,"x":192,"y":192,"pixelRatio":2,"sdf":true}, 18 | "icon-bus": {"width":64,"height":64,"x":256,"y":0,"pixelRatio":2,"sdf":true}, 19 | "icon-butcher": {"width":64,"height":64,"x":256,"y":64,"pixelRatio":2,"sdf":true}, 20 | "icon-cafe": {"width":64,"height":64,"x":256,"y":128,"pixelRatio":2,"sdf":true}, 21 | "icon-car_rental": {"width":64,"height":64,"x":256,"y":192,"pixelRatio":2,"sdf":true}, 22 | "icon-car_wash": {"width":64,"height":64,"x":0,"y":256,"pixelRatio":2,"sdf":true}, 23 | "icon-castle": {"width":64,"height":64,"x":64,"y":256,"pixelRatio":2,"sdf":true}, 24 | "icon-cemetery": {"width":64,"height":64,"x":128,"y":256,"pixelRatio":2,"sdf":true}, 25 | "icon-chalet": {"width":64,"height":64,"x":192,"y":256,"pixelRatio":2,"sdf":true}, 26 | "icon-chemist": {"width":64,"height":64,"x":256,"y":256,"pixelRatio":2,"sdf":true}, 27 | "icon-cinema": {"width":64,"height":64,"x":320,"y":0,"pixelRatio":2,"sdf":true}, 28 | "icon-clothes": {"width":64,"height":64,"x":320,"y":64,"pixelRatio":2,"sdf":true}, 29 | "icon-college": {"width":64,"height":64,"x":320,"y":128,"pixelRatio":2,"sdf":true}, 30 | "icon-community": {"width":64,"height":64,"x":320,"y":192,"pixelRatio":2,"sdf":true}, 31 | "icon-defibrillator": {"width":64,"height":64,"x":320,"y":256,"pixelRatio":2,"sdf":true}, 32 | "icon-dentist": {"width":64,"height":64,"x":0,"y":320,"pixelRatio":2,"sdf":true}, 33 | "icon-doctor": {"width":64,"height":64,"x":64,"y":320,"pixelRatio":2,"sdf":true}, 34 | "icon-dog_park": {"width":64,"height":64,"x":128,"y":320,"pixelRatio":2,"sdf":true}, 35 | "icon-doityourself": {"width":64,"height":64,"x":192,"y":320,"pixelRatio":2,"sdf":true}, 36 | "icon-drinking_water": {"width":64,"height":64,"x":256,"y":320,"pixelRatio":2,"sdf":true}, 37 | "icon-drycleaning": {"width":64,"height":64,"x":320,"y":320,"pixelRatio":2,"sdf":true}, 38 | "icon-embassy": {"width":64,"height":64,"x":384,"y":0,"pixelRatio":2,"sdf":true}, 39 | "icon-emergency_phone": {"width":64,"height":64,"x":384,"y":64,"pixelRatio":2,"sdf":true}, 40 | "icon-fast_food": {"width":64,"height":64,"x":384,"y":128,"pixelRatio":2,"sdf":true}, 41 | "icon-fire_station": {"width":64,"height":64,"x":384,"y":192,"pixelRatio":2,"sdf":true}, 42 | "icon-florist": {"width":64,"height":64,"x":384,"y":256,"pixelRatio":2,"sdf":true}, 43 | "icon-fountain": {"width":64,"height":64,"x":384,"y":320,"pixelRatio":2,"sdf":true}, 44 | "icon-furniture": {"width":64,"height":64,"x":0,"y":384,"pixelRatio":2,"sdf":true}, 45 | "icon-garden_centre": {"width":64,"height":64,"x":64,"y":384,"pixelRatio":2,"sdf":true}, 46 | "icon-gift": {"width":64,"height":64,"x":128,"y":384,"pixelRatio":2,"sdf":true}, 47 | "icon-golf": {"width":64,"height":64,"x":192,"y":384,"pixelRatio":2,"sdf":true}, 48 | "icon-greengrocer": {"width":64,"height":64,"x":256,"y":384,"pixelRatio":2,"sdf":true}, 49 | "icon-hairdresser": {"width":64,"height":64,"x":320,"y":384,"pixelRatio":2,"sdf":true}, 50 | "icon-hardware": {"width":64,"height":64,"x":384,"y":384,"pixelRatio":2,"sdf":true}, 51 | "icon-historic": {"width":64,"height":64,"x":448,"y":0,"pixelRatio":2,"sdf":true}, 52 | "icon-hospital": {"width":64,"height":64,"x":448,"y":64,"pixelRatio":2,"sdf":true}, 53 | "icon-huntingstand": {"width":64,"height":64,"x":448,"y":128,"pixelRatio":2,"sdf":true}, 54 | "icon-hydrant": {"width":64,"height":64,"x":448,"y":192,"pixelRatio":2,"sdf":true}, 55 | "icon-icerink": {"width":64,"height":64,"x":448,"y":256,"pixelRatio":2,"sdf":true}, 56 | "icon-jewelry_store": {"width":64,"height":64,"x":448,"y":320,"pixelRatio":2,"sdf":true}, 57 | "icon-kiosk": {"width":64,"height":64,"x":448,"y":384,"pixelRatio":2,"sdf":true}, 58 | "icon-laundry": {"width":64,"height":64,"x":0,"y":448,"pixelRatio":2,"sdf":true}, 59 | "icon-library": {"width":64,"height":64,"x":64,"y":448,"pixelRatio":2,"sdf":true}, 60 | "icon-lighthouse": {"width":64,"height":64,"x":128,"y":448,"pixelRatio":2,"sdf":true}, 61 | "icon-marketplace": {"width":64,"height":64,"x":192,"y":448,"pixelRatio":2,"sdf":true}, 62 | "icon-monument": {"width":64,"height":64,"x":256,"y":448,"pixelRatio":2,"sdf":true}, 63 | "icon-newsagent": {"width":64,"height":64,"x":320,"y":448,"pixelRatio":2,"sdf":true}, 64 | "icon-nightclub": {"width":64,"height":64,"x":384,"y":448,"pixelRatio":2,"sdf":true}, 65 | "icon-nursinghome": {"width":64,"height":64,"x":448,"y":448,"pixelRatio":2,"sdf":true}, 66 | "icon-observation_tower": {"width":64,"height":64,"x":512,"y":0,"pixelRatio":2,"sdf":true}, 67 | "icon-optician": {"width":64,"height":64,"x":512,"y":64,"pixelRatio":2,"sdf":true}, 68 | "icon-outdoor": {"width":64,"height":64,"x":512,"y":128,"pixelRatio":2,"sdf":true}, 69 | "icon-pharmacy": {"width":64,"height":64,"x":512,"y":192,"pixelRatio":2,"sdf":true}, 70 | "icon-picnic_site": {"width":64,"height":64,"x":512,"y":256,"pixelRatio":2,"sdf":true}, 71 | "icon-pitch": {"width":64,"height":64,"x":512,"y":320,"pixelRatio":2,"sdf":true}, 72 | "icon-place_of_worship": {"width":64,"height":64,"x":512,"y":384,"pixelRatio":2,"sdf":true}, 73 | "icon-playground": {"width":64,"height":64,"x":512,"y":448,"pixelRatio":2,"sdf":true}, 74 | "icon-police": {"width":64,"height":64,"x":0,"y":512,"pixelRatio":2,"sdf":true}, 75 | "icon-postbox": {"width":64,"height":64,"x":64,"y":512,"pixelRatio":2,"sdf":true}, 76 | "icon-post": {"width":64,"height":64,"x":128,"y":512,"pixelRatio":2,"sdf":true}, 77 | "icon-prison": {"width":64,"height":64,"x":192,"y":512,"pixelRatio":2,"sdf":true}, 78 | "icon-rail_light": {"width":64,"height":64,"x":256,"y":512,"pixelRatio":2,"sdf":true}, 79 | "icon-rail_metro": {"width":64,"height":64,"x":320,"y":512,"pixelRatio":2,"sdf":true}, 80 | "icon-rail": {"width":64,"height":64,"x":384,"y":512,"pixelRatio":2,"sdf":true}, 81 | "icon-recycling": {"width":64,"height":64,"x":448,"y":512,"pixelRatio":2,"sdf":true}, 82 | "icon-restaurant": {"width":64,"height":64,"x":512,"y":512,"pixelRatio":2,"sdf":true}, 83 | "icon-school": {"width":64,"height":64,"x":576,"y":0,"pixelRatio":2,"sdf":true}, 84 | "icon-shelter": {"width":64,"height":64,"x":576,"y":64,"pixelRatio":2,"sdf":true}, 85 | "icon-shoes": {"width":64,"height":64,"x":576,"y":128,"pixelRatio":2,"sdf":true}, 86 | "icon-shop": {"width":64,"height":64,"x":576,"y":192,"pixelRatio":2,"sdf":true}, 87 | "icon-shrine": {"width":64,"height":64,"x":576,"y":256,"pixelRatio":2,"sdf":true}, 88 | "icon-sports": {"width":64,"height":64,"x":576,"y":320,"pixelRatio":2,"sdf":true}, 89 | "icon-stadium": {"width":64,"height":64,"x":576,"y":384,"pixelRatio":2,"sdf":true}, 90 | "icon-stationery": {"width":64,"height":64,"x":576,"y":448,"pixelRatio":2,"sdf":true}, 91 | "icon-surveillance": {"width":64,"height":64,"x":576,"y":512,"pixelRatio":2,"sdf":true}, 92 | "icon-swimming": {"width":64,"height":64,"x":0,"y":576,"pixelRatio":2,"sdf":true}, 93 | "icon-telephone": {"width":64,"height":64,"x":64,"y":576,"pixelRatio":2,"sdf":true}, 94 | "icon-theatre": {"width":64,"height":64,"x":128,"y":576,"pixelRatio":2,"sdf":true}, 95 | "icon-toilet": {"width":64,"height":64,"x":192,"y":576,"pixelRatio":2,"sdf":true}, 96 | "icon-town_hall": {"width":64,"height":64,"x":256,"y":576,"pixelRatio":2,"sdf":true}, 97 | "icon-toys": {"width":64,"height":64,"x":320,"y":576,"pixelRatio":2,"sdf":true}, 98 | "icon-travel_agent": {"width":64,"height":64,"x":384,"y":576,"pixelRatio":2,"sdf":true}, 99 | "icon-vendingmachine": {"width":64,"height":64,"x":448,"y":576,"pixelRatio":2,"sdf":true}, 100 | "icon-veterinary": {"width":64,"height":64,"x":512,"y":576,"pixelRatio":2,"sdf":true}, 101 | "icon-video": {"width":64,"height":64,"x":576,"y":576,"pixelRatio":2,"sdf":true}, 102 | "icon-viewpoint": {"width":64,"height":64,"x":640,"y":0,"pixelRatio":2,"sdf":true}, 103 | "icon-waste_basket": {"width":64,"height":64,"x":640,"y":64,"pixelRatio":2,"sdf":true}, 104 | "icon-watermill": {"width":64,"height":64,"x":640,"y":128,"pixelRatio":2,"sdf":true}, 105 | "icon-waterpark": {"width":64,"height":64,"x":640,"y":192,"pixelRatio":2,"sdf":true}, 106 | "icon-windmill": {"width":64,"height":64,"x":640,"y":256,"pixelRatio":2,"sdf":true}, 107 | "icon-zoo": {"width":64,"height":64,"x":640,"y":320,"pixelRatio":2,"sdf":true}, 108 | "marking-arrow": {"width":28,"height":50,"x":704,"y":0,"pixelRatio":2,"sdf":true}, 109 | "pattern-hatched_thin": {"width":44,"height":44,"x":640,"y":512,"pixelRatio":2,"sdf":true}, 110 | "pattern-striped": {"width":44,"height":44,"x":640,"y":556,"pixelRatio":2,"sdf":true}, 111 | "pattern-warning": {"width":44,"height":44,"x":0,"y":640,"pixelRatio":2,"sdf":true}, 112 | "transport-tram": {"width":64,"height":64,"x":640,"y":384,"pixelRatio":2,"sdf":true}, 113 | "transport-information": {"width":64,"height":64,"x":640,"y":448,"pixelRatio":2,"sdf":true} 114 | } --------------------------------------------------------------------------------