diff --git a/.gitignore b/.gitignore index 0fe1b64..00bd5a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,11 @@ /unpacked/ -packed/* +js5/* /lib/ /temp/ /tmp/ config/xtea/* -/indexes/ +indexes/* +index/* /logs/ @@ -26,4 +27,4 @@ yarn-error.log* *.sln *.sw? -!packed/.gitkeep \ No newline at end of file +!js5/.gitkeep diff --git a/README.md b/README.md index 3325c10..7ea16f8 100644 --- a/README.md +++ b/README.md @@ -7,15 +7,13 @@ Node tools for managing and indexing the JS5 file store used with RuneJS. ## CLI Tools -_@todo work in progress_ - `index` - `unpack` ## Archives -_@todo work in progress_ -### JS5 // Game Version 400+ +### JS5 File Store // Game Builds 400-604 | Key | Archive Name | Content Description | File Format | Build | |----------------------------------:|:-----------------|:----------------------------------|:-----------:|:-----------------:| @@ -33,8 +31,21 @@ _@todo work in progress_ | idx **9** | textures | Game Textures | | _400_+ | | idx **10** | binary | Miscellaneous Binary Files | | _400_+ | | idx **11** | midi_jingles | Shorter Midi Jingles | .mid | _400_+ | -| | | | | | | idx **12** | clientscripts | Client Script (CS2) Files | .cs2 | _435_+ | | idx **13** | fontmetrics | Game Font Metrics | | _443_+ | -| idx **14** | vorbis | Vorbis Sound Files | .ogg | _451_+ | +| idx **14** | vorbis | Vorbis Sound Files | | _451_+ | | idx **15** | midi_instruments | Midi Song Instruments | | _451_+ | +| idx **16** | config_loc | Location Object Configs | | _489_+ | +| idx **17** | config_enum | Enum Configs | | _489_+ | +| idx **18** | config_npc | NPC Configs | | _489_+ | +| idx **19** | config_obj | Item Object Configs | | _489_+ | +| idx **20** | config_seq | Animation Sequence Configs | | _489_+ | +| idx **21** | config_spot | Graphical Spot Animation Configs | | _489_+ | +| idx **22** | config_var_bit | VarBit Configs | | _489_+ | +| idx **23** | worldmapdata | In-Game World Map Data | | _493_+ | +| idx **24** | quickchat | Quickchat Data | | _498_+ | +| idx **25** | quickchat_global | Global Quickchat Data | | _498_+ | +| idx **26** | materials | Materials | | _500_+ | +| idx **27** | config_particle | Particle Configs | | _523_+ | +| idx **28** | defaults | Defaults | | _537_+ | +| idx **29** | billboards | Billboards | | _582_+ | diff --git a/config/archives.json5 b/config/archives.json5 deleted file mode 100644 index ff5fe86..0000000 --- a/config/archives.json5 +++ /dev/null @@ -1,129 +0,0 @@ -{ - // The main index of indexes - main: { - index: 255, - compression: 'uncompressed', - versioned: false - }, - - // Regular indexes - anims: { - index: 0, - compression: 'bzip', - versioned: true, - flatten: true - }, - bases: { - index: 1, - compression: 'gzip', - versioned: true - }, - config: { - index: 2, - compression: 'bzip', - versioned: true, - groupNames: { - '.flu': 1, // Floor Underlays - // 2? - '.idk': 3, // Identity Kit - '.flo': 4, // Floor Overlays - '.inv': 5, // Inventory - '.loc': 6, // Objects - // 7? - '.enum': 8, // Enums - '.npc': 9, // Npcs - '.obj': 10, // Items - '.param': 11, // Script Parameters - '.seq': 12, // Animation Sequences - '.spotanim': 13, // Graphical "Spot" Animations - '.var_bit': 14, // Bit Variables - // 15? - '.var_player': 16, // Player Variables - // 17? - '.area': 18, - } - }, - interfaces: { - index: 3, - compression: 'bzip', - versioned: true, - flatten: true - }, - synth_sounds: { - index: 4, - compression: 'gzip', - versioned: true, - contentType: '.wav' - }, - maps: { - index: 5, - compression: 'gzip', - encryption: [ 'xtea', '^l[0-9]{1,3}_[0-9]{1,3}$' ], - versioned: true, - filesNamed: true - }, - midi_songs: { - index: 6, - compression: 'gzip', - versioned: false, - contentType: '.mid', - filesNamed: true - }, - models: { - index: 7, - compression: 'gzip', - versioned: true, - contentType: '.dat' - }, - sprites: { - index: 8, - compression: 'gzip', - versioned: true, - filesNamed: true - }, - textures: { - index: 9, - compression: 'gzip', - versioned: true - }, - binary: { - index: 10, - compression: 'uncompressed', - versioned: true, - filesNamed: true - }, - midi_jingles: { - index: 11, - compression: 'gzip', - versioned: true, - contentType: '.mid' - }, - clientscripts: { - index: 12, - compression: 'gzip', - versioned: true, - contentType: '.cs2', - filesNamed: true, - build: 435 - }, - fontmetrics: { - index: 13, - compression: 'gzip', - versioned: false, - filesNamed: true, - build: 443 - }, - vorbis: { - index: 14, - compression: 'gzip', - versioned: true, - contentType: '.ogg', - build: 451 - }, - midi_instruments: { - index: 15, - compression: 'gzip', - versioned: false, - build: 451 - } -} diff --git a/config/js5-archives.json5 b/config/js5-archives.json5 new file mode 100644 index 0000000..977b5c9 --- /dev/null +++ b/config/js5-archives.json5 @@ -0,0 +1,595 @@ +{ + // The main index of indexes + main: { + key: 255 + }, + + // Regular indexes + anims: { + key: 0, + flattenGroups: true + }, + bases: { + key: 1 + }, + config: { + key: 2, + groupNames: { + '.flu': 1, // Floor Underlays + // 2? + '.idk': 3, // Identity Kit + '.flo': 4, // Floor Overlays + '.inv': 5, // Inventory + '.loc': 6, // Objects + // 7? + '.enum': 8, // Enums + '.npc': 9, // Npcs + '.obj': 10, // Items + '.param': 11, // Script Parameters + '.seq': 12, // Animation Sequences + '.spotanim': 13, // Graphical "Spot" Animations + '.var_bit': 14, // Bit Variables + // 15? + '.var_player': 16, // Player Variables + // 17? + '.area': 18, + } + }, + interfaces: { + key: 3, + flattenGroups: true, + groupNames: { + '100guide_eggs_overlay': 0, + '100guide_flour_overlay': 1, + '100guide_inv_flour': 2, + '100guide_milk_overlay': 3, + 'tog_water_bowl': 4, + 'agilityarena_overlay': 5, + 'agilityarena_trade': 6, + 'ahoy_blackout': 7, + 'ahoy_islandmap': 8, + 'ahoy_runedraw': 9, + 'ahoy_windspeed': 10, + 'bank_deposit_box': 11, + 'bankpin_main': 13, + 'bankpin_settings': 14, + 'banner_padlock_keys': 15, + 'banner_anti_virus': 16, + 'banner_scamming': 21, + 'banner_security': 22, + 'banner_xmas': 23, + 'barrows_overlay': 24, + 'barrows_puzzle': 25, + 'blast_furnace_bar_stock': 28, + 'blast_furnace_plan_scroll': 29, + 'blast_furnace_temp_gauge': 30, + 'boardgames_challenge': 31, + 'boardgames_draughts': 32, + 'boardgames_draughts_options': 33, + 'boardgames_draughts_overlay': 34, + 'boardgames_draughts_view': 35, + 'boardgames_runelink': 36, + 'boardgames_runelink_options': 37, + 'boardgames_runelink_overlay': 38, + 'boardgames_runelink_view': 39, + 'boardgames_runesquares': 40, + 'boardgames_runesquares_options': 41, + 'boardgames_runesquares_overlay': 42, + 'boardgames_runesquares_view': 43, + 'boardgames_runeversi': 44, + 'boardgames_runeversi_options': 45, + 'boardgames_runeversi_overlay': 46, + 'boardgames_runeversi_view': 47, + 'bob_locator_amulet': 48, + 'burgh_map': 51, + 'canoe': 52, + 'canoe_stations_map': 53, + 'castlewars_catapult': 54, + 'castlewars_score': 55, + 'castlewars_shopside': 56, + 'castlewars_status_overlay': 57, + 'castlewars_status_overlay_saradomin': 58, + 'castlewars_status_overlay_zamorak': 59, + 'castlewars_trade': 60, + 'cat_naming': 61, + 'cave_goblin_markers': 62, + 'champions_scroll': 63, + 'chat1': 64, + 'chat2': 65, + 'chat3': 66, + 'chat4': 67, + 'chat_np1': 68, + 'chat_np2': 69, + 'chat_np3': 70, + 'chat_np4': 71, + 'confirm_destroy': 94, + 'sailing_transport_world_map': 95, + 'darkness_dark': 96, + 'darkness_light': 97, + 'darkness_medium': 98, + 'death_dice': 99, + 'deep_blue': 100, + 'pattern_next': 103, + 'stockmarket': 105, + 'stockside': 107, + 'stockcollect': 109, + 'dwarf_rock_book': 111, + 'dwarf_rock_cannon': 112, + 'dwarf_rock_schematics': 113, + 'dwarf_rock_schematics_control': 114, + 'enakh_film': 117, + 'enakh_smoke_overlay': 118, + 'fade_to_black': 120, + 'fade_to_light_blue': 121, + 'fade_to_white': 122, + 'fairy_certificate': 123, + 'farming_tools': 125, + 'farming_tools_side': 126, + 'favour_keyring': 127, + 'doubleobjbox': 131, + 'garden_list': 132, + 'garden_quiz': 133, + 'aide_compass': 135, + 'chatdefault': 137, + 'glidermap': 138, + 'gnomeball': 139, + 'horror_metaldoor': 142, + 'hauntedmine_controls': 144, + 'inventory': 149, + 'keldagrim_map': 150, + 'keldagrim_story1': 151, + 'keldagrim_titles': 152, + 'aide_death': 153, + 'leather_crafting': 154, + 'legends_mirror': 155, + 'quickchat_tutorial': 157, + 'rocko_kittens': 160, + 'smki_assignment': 161, + 'smki_learn': 163, + 'smki_buy': 164, + 'smki_smoke_overlay': 165, + 'rocko_cannonball': 166, + 'rocko_underwater': 167, + 'rocko_seagull': 168, + 'fade_from_black': 170, + 'chatlarge': 173, + 'wom_scroll': 174, + 'heat_overlay': 175, + 'fade_from_white': 177, + 'light2': 178, + 'light_blue': 179, + 'lightning_flash': 180, + 'logout': 182, + 'macro_combilock': 185, + 'macro_evil_bob': 186, + 'music_v3': 187, + 'macro_mime_emotes': 188, + 'macro_quizshow': 191, + 'magic': 192, + 'magic_zaros': 193, + 'magictraining_grave': 196, + 'magictraining_shop': 197, + 'magictraining_tele': 198, + 'mazetimer': 209, + 'message1': 210, + 'message2': 211, + 'message3': 212, + 'message4': 213, + 'message5': 214, + 'message_np1': 215, + 'message_np2': 216, + 'message_np3': 217, + 'message_np4': 218, + 'message_np5': 219, + 'messagescroll': 220, + 'messagescroll2': 221, + 'peng_emote': 223, + 'misc_shipjourney': 224, + 'mm_message': 225, + 'mole_mud': 226, + 'mourning_deathalter_list': 227, + 'multi2': 228, + 'multi2_chat': 229, + 'multi3': 230, + 'multi3_chat': 231, + 'multi4': 232, + 'multi4_chat': 233, + 'multi5': 234, + 'multi5_chat': 235, + 'multivar2': 236, + 'multivar4': 237, + 'multivar5': 238, + 'npcchat1': 241, + 'npcchat2': 242, + 'npcchat3': 243, + 'npcchat4': 244, + 'npcchat_np1': 245, + 'npcchat_np2': 246, + 'npcchat_np3': 247, + 'npcchat_np4': 248, + 'surok_letter1': 249, + 'surok_letter2': 250, + 'olaf2_lock_gate': 252, + 'olaf2_skull_puzzle': 253, + 'olaf2_treasuremap': 254, + 'area_task': 259, + 'dream_armour': 260, + 'options': 261, + 'pinball_interface': 263, + 'paint_cannon': 264, + 'clanwars_overlay': 265, + 'pest_rewards': 267, + 'prayer': 271, + 'priestperil_gravemonument': 272, + 'prisonpete': 273, + 'questjournal_v2': 274, + 'questjournal_scroll': 275, + 'ratcatcher_flute': 282, + 'ratcatcher_flute_music': 283, + 'ratcatcher_overlay': 284, + 'rd_combolock': 285, + 'regicide_still': 286, + 'roguesden_dials': 290, + 'roguesden_gear_select': 291, + 'roguesden_gears': 292, + 'roguesden_puzzle': 293, + 'rum_deal_title': 295, + 'sandstorm': 296, + 'seer_combolock': 298, + 'smithing_new': 300, + 'skill_cookmany': 307, + 'lotr_scroll': 308, + 'skill_multi1': 309, + 'slayer_staff_spells': 310, + 'smelting': 311, + 'smokeoverlay': 313, + 'soulbane_angerbar': 315, + 'soulbane_cut': 316, + 'soulbane_darkness': 317, + 'soulbane_scare': 318, + 'staff_spells': 319, + 'stats': 320, + 'swamp_boatjourney': 321, + 'swamp_boatjourney_back': 322, + 'tanner': 324, + 'target': 325, + 'teleport_other': 326, + 'cws_warning_11': 327, + 'temple_screen': 328, + 'templetrek_map': 329, + 'tradeconfirm': 334, + 'trademain': 335, + 'tradeside': 336, + 'trail_cluelong': 345, + 'trail_map01': 346, + 'trail_map02': 347, + 'trail_map03': 348, + 'trail_map04': 349, + 'trail_map05': 350, + 'trail_map06': 351, + 'trail_map07': 352, + 'trail_map08': 353, + 'trail_map09': 354, + 'trail_map10': 355, + 'trail_map11': 356, + 'trail_map12': 357, + 'trail_map13': 358, + 'trail_map14': 359, + 'trail_map15': 360, + 'trail_map16': 361, + 'trail_map17': 362, + 'trail_puzzle': 363, + 'trail_reward': 364, + 'trail_sextant': 365, + 'trawler_overlay': 366, + 'trawler_reward': 367, + 'trawler_start': 368, + 'tutorial_progress': 371, + 'tutorial_text': 372, + 'tzhaar_fightpit': 373, + 'welcome_screen': 378, + 'werewolf_goal': 379, + 'welcome_final_tex': 380, + 'wilderness_overlay': 381, + 'wilderness_warning': 382, + 'wom_telescope': 386, + 'wornitems': 387, + 'objdialog': 389, + 'poh_hangman': 393, + 'poh_house_options': 398, + 'poh_magic_tablets': 400, + 'poh_ranging': 401, + 'poh_sawmill': 403, + 'poh_scrying_pool': 404, + 'banner_poh': 405, + 'pest_mace': 406, + 'pest_lander_overlay': 407, + 'pest_status_overlay': 408, + 'mcannon_interface': 409, + 'warguild_defence': 410, + 'warguild_defence_mini': 411, + 'warguild_dummy': 412, + 'brew_telescope_overlay': 413, + 'brew_game_over': 414, + 'brew_overlay': 415, + 'brew_worktable': 416, + 'brew_tools': 417, + 'tutorial_text2': 421, + 'fairy2_certificate_broken': 423, + 'fairy2_message': 424, + 'scroll_godfather': 427, + 'ntk_overlay': 428, + 'magic_lunar': 430, + 'xbows_enchant_bolt': 432, + 'xbows_pouch': 433, + 'crafting_silver_casting': 438, + 'openurl': 445, + 'crafting_gold': 446, + 'banner_chatheads': 447, + 'brain_water_overlay': 448, + 'multivar3': 451, + 'myq3_statue_painting': 452, + 'myq3_blackout': 453, + 'myq3_boat_map': 454, + 'crafting_spinning': 459, + 'seaslug_boat_travel': 461, + 'kings_letter_v2': 463, + 'emotes': 464, + 'zep_balloon_map': 469, + 'zep_interface': 470, + 'zep_interface_side': 471, + 'anma_rgb': 480, + 'barbassault_horn': 484, + 'barbassault_over_att': 485, + 'barbassault_over_col': 486, + 'barbassault_over_def': 487, + 'barbassault_over_heal': 488, + 'barbassault_playerstat': 490, + 'barbassault_reward_shop': 491, + 'barbassault_timer_overlay': 494, + 'barbassault_turret': 495, + 'barbassault_tutorial': 496, + 'barbassault_wavecomplete': 497, + 'contact_scroll_blood': 498, + 'skill_guide_v2': 499, + 'poh_hangman_german': 507, + 'tol_homonculus_overlay': 508, + 'tol_cage_puzzle': 509, + 'tol_pressure_machine': 510, + 'tol_pipe_machine': 511, + 'inventory_ranging': 512, + 'inventory_wear': 513, + 'brain_cat_overlay': 514, + 'brain_gas_overlay': 515, + 'objbox': 519, + 'dream_cyrisus': 521, + 'dream_monster_stat': 522, + 'dream_player_stats': 523, + 'dream_title': 524, + 'vm_museum_map': 527, + 'vm_digsite': 528, + 'vm_kudos': 532, + 'vm_timeline': 534, + 'grim_tasklist': 538, + 'noexit': 539, + 'crafting_glass': 542, + 'dragon_slayer_qip_clouds': 543, + 'dragon_slayer_qip_cr_journey': 544, + 'dragon_slayer_qip_elvarg': 545, + 'dragon_slayer_qip_elvarg_fly': 546, + 'dragon_slayer_qip_map': 547, + 'toplevel': 548, + 'toplevel_full': 549, + 'friends2': 550, + 'ignore2': 551, + 'snapshot_main': 553, + 'multi2_mes': 557, + 'skill_multi6': 558, + 'pattern_cards': 559, + 'cws_warning_9': 560, + 'cws_warning_21': 561, + 'cws_warning_2': 562, + 'cws_warning_5': 563, + 'cws_warning_23': 564, + 'cws_warning_10': 565, + 'cws_warning_6': 566, + 'cws_warning_14': 567, + 'cws_warning_3': 568, + 'cws_warning_16': 569, + 'cws_warning_17': 570, + 'cws_warning_19': 571, + 'cws_warning_13': 572, + 'cws_warning_12': 573, + 'cws_warning_1': 574, + 'cws_warning_8': 576, + 'cws_warning_18': 577, + 'cws_warning_15': 578, + 'cws_warning_4': 579, + 'cws_warning_20': 580, + 'cws_warning_24': 581, + 'skill_multi1_small': 582, + 'cws_doomsayer': 583, + 'kr_king_statue': 584, + 'kr_pyramid_escape': 585, + 'kr_picklock': 588, + 'clanjoin': 589, + 'clansetup': 590, + 'cws_warning_25': 600, + 'godwars_overlay': 601, + 'shop_template': 620, + 'shop_template_side': 621, + 'banner_group': 622, + 'banner_group_assist': 623, + 'cws_warning_26': 627, + 'duel2_side': 628, + 'duel2_scoreboard': 632, + 'duel2_challengeoverlay': 638, + 'duel2_select_type': 640, + 'exchange_history': 643, + 'exchange_sets_side': 644, + 'exchange_itemsets': 645, + 'exchange_sand_timer': 646, + 'clanwars_viewing_orb': 649, + 'cws_warning_30': 650, + 'gravestone_shop': 652, + 'bounty_overlay': 653, + 'bounty_overlay_waiting': 656, + 'bounty_warning': 657, + 'lore_stats_side': 662, + 'lore_cats_side': 663, + 'lore_bank_side': 665, + 'equip_screen2': 667, + 'inventory_wear2': 670, + 'lore_bank': 671, + 'tutorial2_mesbox': 674, + 'cws_warning_27': 676, + 'cws_warning_28': 677, + 'cws_warning_29': 678, + 'banner_summoning': 679, + 'rabbit_shop': 686, + 'rabbit_overlay': 689, + 'banner_easter08': 715, + 'summoning_side': 722, + 'carpet_info': 723, + 'carpet_runesquares': 724, + 'carpet_runelink': 725, + 'carpet_runeversi': 726, + 'carpet_draughts': 727, + 'carpet_main': 728, + 'carpet_ticket': 729, + 'graphics_options': 742, + 'sound_options': 743, + 'loginscreen': 744, + 'statusicons': 745, + 'toplevel_fullscreen': 746, + 'topstat_lore': 747, + 'topstat_hitpoints': 748, + 'topstat_prayer': 749, + 'topstat_run': 750, + 'filterbuttons': 751, + 'chattop': 752, + 'pmchat': 754, + 'worldmap': 755, + 'boardgames_options': 756, + 'npcchatlarge': 757, + 'canoe_travel': 758, + 'tutorial2_objbox': 760, + 'bank_v2_main': 762, + 'bank_v2_side': 763, + 'tutorial2_switch_task': 765, + 'tutorial2_death': 766, + 'bank_v2_help': 767, + 'tutorial2_text': 769, + 'rcguild_side': 778, + 'rcguild_rewards': 779, + 'rcguild_map': 780, + 'rcguild_overlay': 781, + 'crcs_tightrope': 783, + 'crcs_side': 784, + 'crcs_rewards': 785, + 'crcs_equipment': 786, + 'crcs_scoreboard': 788, + 'clanwars_end': 790, + 'clanwars_setup': 791, + 'clanwars_setup_side': 792, + 'banner_halloween': 800, + 'quickchat_locked': 801, + 'sc_tutorial_overlay': 802, + 'sc_scores_border': 803, + 'sc_item_transfer': 805, + 'sc_remaining_clay': 806, + 'sc_scores': 810, + 'sc_reward_shop': 811, + 'sc_processing': 813, + 'luc2_chapter2': 814, + 'luc2_telescope_view': 816, + 'luc2_chapter3': 822, + 'luc2_chapter1': 826, + 'luc2_lucien_projectiles': 827 + } + }, + synth_sounds: { + key: 4, + contentType: '.wav' + }, + maps: { + key: 5, + encryption: [ 'xtea', '^l[0-9]{1,3}_[0-9]{1,3}$' ] + }, + midi_songs: { + key: 6, + contentType: '.mid' + }, + models: { + key: 7, + contentType: '.dat' + }, + sprites: { + key: 8 + }, + textures: { + key: 9 + }, + binary: { + key: 10 + }, + midi_jingles: { + key: 11, + contentType: '.mid' + }, + clientscripts: { + key: 12, + contentType: '.cs2' + }, + fontmetrics: { + key: 13 + }, + vorbis: { + key: 14 + }, + midi_instruments: { + key: 15 + }, + config_loc: { + key: 16 + }, + config_enum: { + key: 17 + }, + config_npc: { + key: 18 + }, + config_obj: { + key: 19 + }, + config_seq: { + key: 20 + }, + config_spot: { + key: 21 + }, + config_var_bit: { + key: 22 + }, + worldmapdata: { + key: 23 + }, + quickchat: { + key: 24 + }, + quickchat_global: { + key: 25 + }, + materials: { + key: 26 + }, + config_particle: { + key: 27 + }, + defaults: { + key: 28 + }, + billboards: { + key: 29 + } +} diff --git a/config/name-hashes.json b/config/name-hashes.json index a23a8e4..866426a 100644 --- a/config/name-hashes.json +++ b/config/name-hashes.json @@ -169062,5 +169062,185 @@ "-1081813035": "m199_198", "-2824623370": "l199_198", "-1081813034": "m199_199", - "-2824623369": "l199_199" + "-2824623369": "l199_199", + "6133252": "45.dat", + "8297314": "data", + "19979093": "46.dat", + "33824934": "47.dat", + "47670775": "48.dat", + "53973365": "combaticons.dat", + "61516616": "49.dat", + "125902192": "backbase1.dat", + "139748033": "backbase2.dat", + "150819851": "idk.dat", + "204062206": "q8_full.dat", + "224847211": "0.dat", + "232787039": "sounds.dat", + "238693052": "1.dat", + "252137566": "model_version", + "252538893": "2.dat", + "266384734": "3.dat", + "280230575": "4.dat", + "294076416": "5.dat", + "305236077": "prayeroff.dat", + "307922257": "6.dat", + "321768098": "7.dat", + "335613939": "8.dat", + "349459780": "9.dat", + "383739196": "varp.dat", + "392041951": "prayeron.dat", + "449541346": "mod_icons.dat", + "529843337": "cross.dat", + "612871759": "mapdots.dat", + "661178691": "magicoff.dat", + "661681639": "staticons.dat", + "682978269": "loc.dat", + "682997061": "loc.idx", + "715169772": "anim_index", + "839488367": "mapscene.dat", + "886159288": "seq.dat", + "1018124075": "headicons_hint.dat", + "1043559214": "steelborder.dat", + "1152574301": "wornicons.dat", + "1165431679": "number_button.dat", + "1354546316": "backleft1.dat", + "1362520410": "mapedge.dat", + "1368392157": "backleft2.dat", + "1442199444": "rightarrow.dat", + "1464846521": "backvmid1.dat", + "1478692362": "backvmid2.dat", + "1489108188": "npc.dat", + "1489126980": "npc.idx", + "1492538203": "backvmid3.dat", + "1644583778": "mapback.dat", + "1648736955": "badenc.txt", + "1654911043": "p11_full.dat", + "1694123055": "prayerglow.dat", + "1694783164": "domainenc.txt", + "1698082440": "10.dat", + "1711928281": "11.dat", + "1725774122": "12.dat", + "1727594325": "magicoff2.dat", + "1739619963": "13.dat", + "1753465804": "14.dat", + "1758274153": "staticons2.dat", + "1766681864": "chatback.dat", + "1767311645": "15.dat", + "1781157486": "16.dat", + "1795003327": "17.dat", + "1808849168": "18.dat", + "1822695009": "19.dat", + "1889496696": "sideicons.dat", + "1915414053": "map_crc", + "1922934081": "leftarrow.dat", + "1955686745": "titlebutton.dat", + "1955804455": "mapmarker.dat", + "1986120039": "keys.dat", + "1987120305": "map_index", + "2025126712": "overlay_multiway.dat", + "2038060091": "headicons_pk.dat", + "2081559868": "miscgraphics.dat", + "-1929337337": "index.dat", + "-227242592": "p12_full.dat", + "-1124181286": "b12_full.dat", + "-566502255": "title.dat", + "-1752651416": "logo.dat", + "-1891508522": "titlebox.dat", + "-1668775416": "runes.dat", + "-1569261396": "flo.dat", + "-1667617738": "obj.dat", + "-1667598946": "obj.idx", + "-955170442": "spotanim.dat", + "-514869585": "varbit.dat", + "-1568083395": "invback.dat", + "-1623648789": "backhmid1.dat", + "-427405255": "compass.dat", + "-1204854137": "mapfunction.dat", + "-1502153170": "hitmarks.dat", + "-288954319": "headicons.dat", + "-1337835461": "headicons_prayer.dat", + "-1571073093": "scrollbar.dat", + "-1392068576": "redstone1.dat", + "-1378222735": "redstone2.dat", + "-1364376894": "redstone3.dat", + "-1593819477": "backright1.dat", + "-1579973636": "backright2.dat", + "-1102299012": "backtop1.dat", + "-1609802948": "backhmid2.dat", + "-1000916878": "tradebacking.dat", + "-716997548": "steelborder2.dat", + "-1868599050": "combatboxes.dat", + "-884827257": "sworddecor.dat", + "-952192193": "combaticons2.dat", + "-938346352": "combaticons3.dat", + "-1809621253": "miscgraphics3.dat", + "-869490323": "magicon.dat", + "-1448902313": "magicon2.dat", + "-1823467094": "miscgraphics2.dat", + "-416634290": "chest.dat", + "-58065069": "coins.dat", + "-351562801": "tex_brown.dat", + "-1811229622": "tex_red.dat", + "-797498902": "anim_version", + "-945480188": "midi_version", + "-923525801": "map_version", + "-1761598724": "model_crc", + "-40228664": "anim_crc", + "-1121254206": "midi_crc", + "-706585152": "model_index", + "-1691482954": "midi_index", + "-1752288555": "20.dat", + "-1738442714": "21.dat", + "-1724596873": "22.dat", + "-1710751032": "23.dat", + "-1696905191": "24.dat", + "-1683059350": "25.dat", + "-1669213509": "26.dat", + "-1655367668": "27.dat", + "-1641521827": "28.dat", + "-1627675986": "29.dat", + "-907692254": "30.dat", + "-893846413": "31.dat", + "-880000572": "32.dat", + "-866154731": "33.dat", + "-852308890": "34.dat", + "-838463049": "35.dat", + "-824617208": "36.dat", + "-810771367": "37.dat", + "-796925526": "38.dat", + "-783079685": "39.dat", + "-63095953": "40.dat", + "-49250112": "41.dat", + "-35404271": "42.dat", + "-21558430": "43.dat", + "-7712589": "44.dat", + "-573349193": "fragmentsenc.txt", + "-840867198": "tldlist.txt", + "150838643": "idk.idx", + "383757988": "varp.idx", + "886178080": "seq.idx", + "-955151650": "spotanim.idx", + "-514850793": "varbit.idx", + "-1569242604": "flo.idx", + "182685561": "mesanim.dat", + "182704353": "mesanim.idx", + "1029250116": "mes.dat", + "1029268908": "mes.idx", + "-1818025236": "param.dat", + "-1818006444": "param.idx", + "22834782": "gnomeball_buttons.dat", + "450862262": "overlay_duel.dat", + "523617556": "rightarrow_small.dat", + "819035239": "letter.dat", + "902321338": "pen.dat", + "1150791544": "key.dat", + "1451391714": "button_brown.dat", + "2004158547": "startgame.dat", + "-1004178375": "leftarrow_small.dat", + "-1857300557": "blackmark.dat", + "-888498683": "button_red.dat", + "-384541308": "titlescroll.dat", + "-90207845": "button_brown_big.dat", + "216316762": "hunt.dat", + "216335554": "hunt.idx" } diff --git a/packed/.gitkeep b/js5/.gitkeep similarity index 100% rename from packed/.gitkeep rename to js5/.gitkeep diff --git a/package-lock.json b/package-lock.json index 8202072..2953e16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,25 +1,34 @@ { - "name": "@runejs/store", - "version": "1.0.0-beta.1", + "name": "@runejs/filestore", + "version": "1.0.0-next.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "@runejs/store", - "version": "1.0.0-beta.1", + "name": "@runejs/filestore", + "version": "1.0.0-next.0", "license": "GPL-3.0", "dependencies": { - "@runejs/common": "2.0.2-beta.2", - "graceful-fs": "^4.2.0", + "@decorators/di": "^1.0.3", + "@decorators/express": "^2.6.0", + "@runejs/common": "3.0.0-beta.10", + "adm-zip": "^0.5.9", + "axios": "^0.27.2", + "compressjs": "^1.0.3", + "express": "^4.18.1", + "graceful-fs": ">=4.2.0", "json5": "^2.2.0", "reflect-metadata": "^0.1.13", "sqlite": "^4.0.25", - "tslib": "^2.3.1", + "tslib": ">=2.4.0", "typeorm": "^0.2.44", - "yargs": "^17.3.1" + "yargs": "^17.3.1", + "zlib": "^1.0.5" }, "devDependencies": { "@runejs/eslint-config": "^1.1.0", + "@types/adm-zip": "^0.5.0", + "@types/express": "^4.17.13", "@types/graceful-fs": "^4.1.5", "@types/node": "^16.11.26", "@types/yargs": "^17.0.9", @@ -30,93 +39,77 @@ "eslint": "^8.11.0", "rimraf": "^3.0.2", "source-map-support": "^0.5.21", - "ts-node-dev": "^1.1.8", - "typescript": "^4.5.5" + "ts-node-dev": "^2.0.0", + "typescript": "^4.7.4" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" }, - "peerDependencies": { - "@runejs/common": "2.0.2-beta.2", - "graceful-fs": ">=4.2.0", - "tslib": ">=2.3.0", - "typescript": ">=4.5.0" + "engines": { + "node": ">=12" } }, - "../common/lib": { - "name": "@runejs/common", - "version": "2.0.2-beta.2", - "extraneous": true, - "license": "GPL-3.0", + "node_modules/@decorators/di": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@decorators/di/-/di-1.0.3.tgz", + "integrity": "sha512-wafyQo5lqGABT+Lh7Od9/qULg7DG/kZFU3mLUKZFuiV/KATYlnv198yQxaZUZerhUDoTl/cZKu9t4mJa0rZK4Q==", "dependencies": { - "compressjs": "^1.0.3", - "js-yaml": "^3.14.1", - "pino": "^6.14.0", - "pino-pretty": "^4.8.0", - "sonic-boom": "^2.6.0", - "tslib": "^2.3.1" + "reflect-metadata": "0.1.13" }, - "devDependencies": { - "@runejs/eslint-config": "^1.0.0", - "@types/node": "^16.11.26", - "@types/pino": "^6.3.12", - "@typescript-eslint/eslint-plugin": "^4.33.0", - "@typescript-eslint/parser": "^4.33.0", - "copyfiles": "^2.4.1", - "eslint": "^7.32.0", - "rimraf": "^3.0.2", - "ts-node-dev": "^1.1.8", - "typescript": "^4.5.5" + "engines": { + "node": ">=7.1.0" + } + }, + "node_modules/@decorators/express": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@decorators/express/-/express-2.6.0.tgz", + "integrity": "sha512-th/LqSAHAAW1ob2CU6hxtsfnryGThmz0AmGP45rA3Yoi0Q7+FHAZVfH7NJSYHITai/uXjjOtZPPW/V0gFfbgUw==", + "engines": { + "node": ">=7.1.0" }, "peerDependencies": { - "tslib": ">=2.3.0", - "typescript": ">=4.5.0" + "@decorators/di": ">=1.0.2", + "express": ">=4.16.3" } }, "node_modules/@eslint/eslintrc": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", - "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.1.tgz", + "integrity": "sha512-OhSY22oQQdw3zgPOOwdoj01l/Dzl1Z+xyUP33tkSN+aqyEhymJCcPHyXt+ylW8FSe0TfRC2VG+ROQOapD0aZSQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.3.1", - "globals": "^13.9.0", + "espree": "^9.4.0", + "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/@hapi/bourne": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-2.0.0.tgz", - "integrity": "sha512-WEezM1FWztfbzqIUbsDzFRVMxSoLy3HugVcux6KDDtTqzPsLE8NDRHfXvev66aH1i2oOKKar3/XDjbvh/OUBdg==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-2.1.0.tgz", + "integrity": "sha512-i1BpaNDVLJdRBEKeJWkVO6tYX6DMFBuwMhSuWqLsY4ufeTKGVuV5rBsUhxPayXqnnWHgXUAmWK16H/ykO5Wj4Q==" }, "node_modules/@humanwhocodes/config-array": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", - "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", + "integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -127,12 +120,68 @@ "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/gitignore-to-minimatch": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", + "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@ledgerhq/compressjs": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@ledgerhq/compressjs/-/compressjs-1.3.2.tgz", + "integrity": "sha512-gonFwAifRkSYDO7rt3NIBlvjvY8Nw+NM6LT1SuOBppuvoKbYtBViNh3EBPbP86+3Y4ux7DLUsNiUlqOgubJsdA==", + "dependencies": { + "commander": "^2.20.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -169,20 +218,20 @@ } }, "node_modules/@runejs/common": { - "version": "2.0.2-beta.2", - "resolved": "https://registry.npmjs.org/@runejs/common/-/common-2.0.2-beta.2.tgz", - "integrity": "sha512-Xd9/Ws3ByAdI1R08Ye9fWqlS8SImjQw05Bsw0w46zcGyDx8N6jmeClf9Y/s4Akh9VZQuoAMFg2ANKsRK0MucNw==", + "version": "3.0.0-beta.10", + "resolved": "https://registry.npmjs.org/@runejs/common/-/common-3.0.0-beta.10.tgz", + "integrity": "sha512-4EnjDBlAfv26oHkSg6q6xAU7gfQj4QKKDUh/e0NJZ3DkwrQuimlNHEfprn7/Wl6wa4mzqJZl05MT2XRfGFhttQ==", "dependencies": { - "compressjs": "^1.0.3", - "js-yaml": "^3.14.1", + "@ledgerhq/compressjs": "^1.3.2", + "buffer": "^6.0.3", + "dotenv": "^16.0.0", + "path": "^0.12.7", "pino": "^6.14.0", - "pino-pretty": "^4.8.0", - "sonic-boom": "^2.6.0", - "tslib": "^2.3.1" + "pino-pretty": "^6.0.0", + "zlib": "^1.0.5" }, "peerDependencies": { - "tslib": ">=2.3.0", - "typescript": ">=4.5.0" + "tslib": ">=2.4.0" } }, "node_modules/@runejs/eslint-config": { @@ -201,6 +250,81 @@ "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.3.tgz", "integrity": "sha512-O3uyB/JbkAEMZaP3YqyHH7TMnex7tWyCbCI4EfJdOCoN6HIhqdJBWTM6aCCiWQ/5f5wxjgU735QAIpJbjDvmzg==" }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "node_modules/@types/adm-zip": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.0.tgz", + "integrity": "sha512-FCJBJq9ODsQZUNURo5ILAQueuA8WJhRvuihS3ke2iI25mJlfV2LK8jG2Qj2z2AWg8U0FtWWqBHVRetceLskSaw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.30", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.30.tgz", + "integrity": "sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, "node_modules/@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -211,21 +335,49 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", "dev": true }, "node_modules/@types/node": { - "version": "16.11.26", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz", - "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==", + "version": "16.11.56", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.56.tgz", + "integrity": "sha512-aFcUkv7EddxxOa/9f74DINReQ/celqH8DiB3fRYgVDM2Xm5QJL8sl80QKuAnGvwAsMn+H3IFA6WCrQh1CY7m1A==", + "dev": true + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", "dev": true }, + "node_modules/@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dev": true, + "dependencies": { + "@types/mime": "*", + "@types/node": "*" + } + }, "node_modules/@types/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", + "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==", "dev": true }, "node_modules/@types/strip-json-comments": { @@ -235,18 +387,18 @@ "dev": true }, "node_modules/@types/yargs": { - "version": "17.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.9.tgz", - "integrity": "sha512-Ci8+4/DOtkHRylcisKmVMtmVO5g7weUVCKcsu1sJvF1bn0wExTmbHmhFKj7AnEm0de800iovGhdSKzYnzbaHpg==", + "version": "17.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.12.tgz", + "integrity": "sha512-Nz4MPhecOFArtm81gFQvQqdV7XYCrWKx5uUt6GNHredFHn1i2mtWqXTON7EPXMtNi1qjtjEM/VCHDhcHsAMLXQ==", "dev": true, "dependencies": { "@types/yargs-parser": "*" } }, "node_modules/@types/yargs-parser": { - "version": "20.2.1", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", - "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, "node_modules/@types/zen-observable": { @@ -255,19 +407,19 @@ "integrity": "sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.14.0.tgz", - "integrity": "sha512-ir0wYI4FfFUDfLcuwKzIH7sMVA+db7WYen47iRSaCGl+HMAZI9fpBwfDo45ZALD3A45ZGyHWDNLhbg8tZrMX4w==", + "version": "5.36.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.36.1.tgz", + "integrity": "sha512-iC40UK8q1tMepSDwiLbTbMXKDxzNy+4TfPWgIL661Ym0sD42vRcQU93IsZIrmi+x292DBr60UI/gSwfdVYexCA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.14.0", - "@typescript-eslint/type-utils": "5.14.0", - "@typescript-eslint/utils": "5.14.0", - "debug": "^4.3.2", + "@typescript-eslint/scope-manager": "5.36.1", + "@typescript-eslint/type-utils": "5.36.1", + "@typescript-eslint/utils": "5.36.1", + "debug": "^4.3.4", "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", + "ignore": "^5.2.0", "regexpp": "^3.2.0", - "semver": "^7.3.5", + "semver": "^7.3.7", "tsutils": "^3.21.0" }, "engines": { @@ -288,15 +440,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.14.0.tgz", - "integrity": "sha512-aHJN8/FuIy1Zvqk4U/gcO/fxeMKyoSv/rS46UXMXOJKVsLQ+iYPuXNbpbH7cBLcpSbmyyFbwrniLx5+kutu1pw==", + "version": "5.36.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.36.1.tgz", + "integrity": "sha512-/IsgNGOkBi7CuDfUbwt1eOqUXF9WGVBW9dwEe1pi+L32XrTsZIgmDFIi2RxjzsvB/8i+MIf5JIoTEH8LOZ368A==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.14.0", - "@typescript-eslint/types": "5.14.0", - "@typescript-eslint/typescript-estree": "5.14.0", - "debug": "^4.3.2" + "@typescript-eslint/scope-manager": "5.36.1", + "@typescript-eslint/types": "5.36.1", + "@typescript-eslint/typescript-estree": "5.36.1", + "debug": "^4.3.4" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -315,13 +467,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.14.0.tgz", - "integrity": "sha512-LazdcMlGnv+xUc5R4qIlqH0OWARyl2kaP8pVCS39qSL3Pd1F7mI10DbdXeARcE62sVQE4fHNvEqMWsypWO+yEw==", + "version": "5.36.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.36.1.tgz", + "integrity": "sha512-pGC2SH3/tXdu9IH3ItoqciD3f3RRGCh7hb9zPdN2Drsr341zgd6VbhP5OHQO/reUqihNltfPpMpTNihFMarP2w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.14.0", - "@typescript-eslint/visitor-keys": "5.14.0" + "@typescript-eslint/types": "5.36.1", + "@typescript-eslint/visitor-keys": "5.36.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -332,13 +484,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.14.0.tgz", - "integrity": "sha512-d4PTJxsqaUpv8iERTDSQBKUCV7Q5yyXjqXUl3XF7Sd9ogNLuKLkxz82qxokqQ4jXdTPZudWpmNtr/JjbbvUixw==", + "version": "5.36.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.36.1.tgz", + "integrity": "sha512-xfZhfmoQT6m3lmlqDvDzv9TiCYdw22cdj06xY0obSznBsT///GK5IEZQdGliXpAOaRL34o8phEvXzEo/VJx13Q==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.14.0", - "debug": "^4.3.2", + "@typescript-eslint/typescript-estree": "5.36.1", + "@typescript-eslint/utils": "5.36.1", + "debug": "^4.3.4", "tsutils": "^3.21.0" }, "engines": { @@ -358,9 +511,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.14.0.tgz", - "integrity": "sha512-BR6Y9eE9360LNnW3eEUqAg6HxS9Q35kSIs4rp4vNHRdfg0s+/PgHgskvu5DFTM7G5VKAVjuyaN476LCPrdA7Mw==", + "version": "5.36.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.36.1.tgz", + "integrity": "sha512-jd93ShpsIk1KgBTx9E+hCSEuLCUFwi9V/urhjOWnOaksGZFbTOxAT47OH2d4NLJnLhkVD+wDbB48BuaycZPLBg==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -371,17 +524,17 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.14.0.tgz", - "integrity": "sha512-QGnxvROrCVtLQ1724GLTHBTR0lZVu13izOp9njRvMkCBgWX26PKvmMP8k82nmXBRD3DQcFFq2oj3cKDwr0FaUA==", + "version": "5.36.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.36.1.tgz", + "integrity": "sha512-ih7V52zvHdiX6WcPjsOdmADhYMDN15SylWRZrT2OMy80wzKbc79n8wFW0xpWpU0x3VpBz/oDgTm2xwDAnFTl+g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.14.0", - "@typescript-eslint/visitor-keys": "5.14.0", - "debug": "^4.3.2", - "globby": "^11.0.4", + "@typescript-eslint/types": "5.36.1", + "@typescript-eslint/visitor-keys": "5.36.1", + "debug": "^4.3.4", + "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.5", + "semver": "^7.3.7", "tsutils": "^3.21.0" }, "engines": { @@ -398,15 +551,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.14.0.tgz", - "integrity": "sha512-EHwlII5mvUA0UsKYnVzySb/5EE/t03duUTweVy8Zqt3UQXBrpEVY144OTceFKaOe4xQXZJrkptCf7PjEBeGK4w==", + "version": "5.36.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.36.1.tgz", + "integrity": "sha512-lNj4FtTiXm5c+u0pUehozaUWhh7UYKnwryku0nxJlYUEWetyG92uw2pr+2Iy4M/u0ONMKzfrx7AsGBTCzORmIg==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.14.0", - "@typescript-eslint/types": "5.14.0", - "@typescript-eslint/typescript-estree": "5.14.0", + "@typescript-eslint/scope-manager": "5.36.1", + "@typescript-eslint/types": "5.36.1", + "@typescript-eslint/typescript-estree": "5.36.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -422,13 +575,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.14.0.tgz", - "integrity": "sha512-yL0XxfzR94UEkjBqyymMLgCBdojzEuy/eim7N9/RIcTNxpJudAcqsU8eRyfzBbcEzGoPWfdM3AGak3cN08WOIw==", + "version": "5.36.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.36.1.tgz", + "integrity": "sha512-ojB9aRyRFzVMN3b5joSYni6FAS10BBSCAfKJhjJAV08t/a95aM6tAhz+O1jF+EtgxktuSO3wJysp2R+Def/IWQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.14.0", - "eslint-visitor-keys": "^3.0.0" + "@typescript-eslint/types": "5.36.1", + "eslint-visitor-keys": "^3.3.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -438,10 +591,22 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -459,6 +624,23 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adm-zip": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.9.tgz", + "integrity": "sha512-s+3fXLkeeLjZ2kLjCBwQufpI5fuN+kIGBxu6530nVQZGVol0d7Y/M88/xw9HGGUcJjKf8LutN3VPRUBq6N7Ajg==", + "engines": { + "node": ">=6.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -478,35 +660,37 @@ "node_modules/amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", "engines": { "node": ">=0.4.2" } }, "node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "devOptional": true, + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" }, "node_modules/anymatch": { "version": "3.1.2", @@ -522,29 +706,13 @@ } }, "node_modules/app-root-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.0.0.tgz", - "integrity": "sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", + "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", "engines": { "node": ">= 6.0.0" } }, - "node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "devOptional": true - }, - "node_modules/are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "devOptional": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -552,17 +720,14 @@ "dev": true }, "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/args": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/args/-/args-5.0.1.tgz", - "integrity": "sha512-1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/args/-/args-5.0.3.tgz", + "integrity": "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==", "dependencies": { "camelcase": "5.0.0", "chalk": "2.4.2", @@ -573,6 +738,17 @@ "node": ">= 6.0.0" } }, + "node_modules/args/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/args/node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -586,6 +762,51 @@ "node": ">=4" } }, + "node_modules/args/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/args/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/args/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/args/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/args/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -595,6 +816,11 @@ "node": ">=8" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/atomic-sleep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", @@ -603,10 +829,19 @@ "node": ">=8.0.0" } }, + "node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/base64-js": { "version": "1.5.1", @@ -628,14 +863,14 @@ ] }, "node_modules/better-sqlite3": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-7.5.0.tgz", - "integrity": "sha512-6FdG9DoytYGDhLW7VWW1vxjEz7xHkqK6LnaUQYA8d6GHNgZhu9PFX2xwKEEnSBRoT1J4PjTUPeg217ShxNmuPg==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-7.6.2.tgz", + "integrity": "sha512-S5zIU1Hink2AH4xPsN0W43T1/AJ5jrPh7Oy07ocuW/AKYYY02GWzz9NH0nbSMn/gw6fDZ5jZ1QsHt1BXAwJ6Lg==", "devOptional": true, "hasInstallScript": true, "dependencies": { "bindings": "^1.5.0", - "prebuild-install": "^7.0.0" + "prebuild-install": "^7.1.0" } }, "node_modules/binary-extensions": { @@ -667,6 +902,30 @@ "readable-stream": "^3.4.0" } }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/bl/node_modules/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -681,13 +940,58 @@ "node": ">= 6" } }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/bl/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "devOptional": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "safe-buffer": "~5.2.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/braces": { @@ -703,10 +1007,9 @@ } }, "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "devOptional": true, + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "funding": [ { "type": "github", @@ -723,7 +1026,7 @@ ], "dependencies": { "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "ieee754": "^1.2.1" } }, "node_modules/buffer-from": { @@ -732,6 +1035,26 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -750,9 +1073,9 @@ } }, "node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -764,55 +1087,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/chalk/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/chalk/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/chalk/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -840,6 +1114,18 @@ "fsevents": "~2.3.2" } }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", @@ -866,46 +1152,6 @@ "npm": ">=5.0.0" } }, - "node_modules/cli-highlight/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-highlight/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-highlight/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-highlight/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cli-highlight/node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -941,72 +1187,59 @@ "wrap-ansi": "^7.0.0" } }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "color-name": "~1.1.4" }, "engines": { - "node": ">=8" + "node": ">=7.0.0" } }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dependencies": { - "ansi-regex": "^5.0.1" + "delayed-stream": "~1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "devOptional": true, - "engines": { - "node": ">=0.10.0" - } + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/compressjs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/compressjs/-/compressjs-1.0.3.tgz", + "integrity": "sha512-jpKJjBTretQACTGLNuvnozP1JdP2ZLrjdGdBgk/tz1VfXlUcBhhSZW6vEsuThmeot/yjvSrPQKEgfF3X2Lpi8Q==", "dependencies": { - "color-name": "1.1.3" + "amdefine": "~1.0.0", + "commander": "~2.8.1" + }, + "bin": { + "compressjs": "bin/compressjs" } }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "node_modules/commander": { + "node_modules/compressjs/node_modules/commander": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", - "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "integrity": "sha512-+pJLBFVk+9ZZdlAOB5WuIElVPPth47hILFkmGym57aq8kwxsowvByvB0DHs1vQAhyMZzdcpTtF0VDKGkSDR4ZQ==", "dependencies": { "graceful-readlink": ">= 1.0.0" }, @@ -1014,28 +1247,42 @@ "node": ">= 0.6.x" } }, - "node_modules/compressjs": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/compressjs/-/compressjs-1.0.3.tgz", - "integrity": "sha1-ldt03VuQOM+AvKMhqw7eJxtJWbY=", + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dependencies": { - "amdefine": "~1.0.0", - "commander": "~2.8.1" + "safe-buffer": "5.2.1" }, - "bin": { - "compressjs": "bin/compressjs" + "engines": { + "node": ">= 0.6" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "devOptional": true + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "node_modules/copyfiles": { "version": "2.4.1", @@ -1056,50 +1303,6 @@ "copyup": "copyfiles" } }, - "node_modules/copyfiles/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/copyfiles/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/copyfiles/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/copyfiles/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/copyfiles/node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -1128,10 +1331,10 @@ } }, "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "devOptional": true + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true }, "node_modules/create-require": { "version": "1.1.1", @@ -1162,9 +1365,9 @@ } }, "node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { "ms": "2.1.2" }, @@ -1207,11 +1410,39 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/delegates": { + "node_modules/delayed-stream": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "devOptional": true + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "devOptional": true, + "engines": { + "node": ">=8" + } }, "node_modules/diff": { "version": "4.0.2", @@ -1247,27 +1478,40 @@ } }, "node_modules/dotenv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", - "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "version": "16.0.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.2.tgz", + "integrity": "sha512-JvpYKUmzQhYoIFgK2MOnF3bciIZoItIIoryihy0rIA+H4Jy0FmgyKYAHCTN98P5ybGSJcIFbh6QKeJdtZd1qhA==", "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/dynamic-dedupe": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", - "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=", + "integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==", "dev": true, "dependencies": { "xtend": "^4.0.0" } }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -1284,22 +1528,33 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, "engines": { - "node": ">=0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.11.0.tgz", - "integrity": "sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==", + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.23.0.tgz", + "integrity": "sha512-pBG/XOn0MsJcKcTRLr27S5HpzQo4kLr+HjLQIyK4EiCsijDl/TB+h5uEuJU6bQ8Edvwz1XWOjpaP2qgnXGpTcA==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.2.1", - "@humanwhocodes/config-array": "^0.9.2", + "@eslint/eslintrc": "^1.3.1", + "@humanwhocodes/config-array": "^0.10.4", + "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "@humanwhocodes/module-importer": "^1.0.1", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -1309,14 +1564,17 @@ "eslint-scope": "^7.1.1", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.1", + "espree": "^9.4.0", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", - "globals": "^13.6.0", + "globals": "^13.15.0", + "globby": "^11.1.0", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -1325,14 +1583,13 @@ "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" @@ -1393,33 +1650,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/eslint/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint/node_modules/eslint-scope": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", @@ -1442,66 +1672,21 @@ "node": ">=4.0" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/eslint/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/espree": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", - "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", + "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", "dev": true, "dependencies": { - "acorn": "^8.7.0", - "acorn-jsx": "^5.3.1", + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.3.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" }, - "engines": { - "node": ">=4" + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/esquery": { @@ -1564,6 +1749,14 @@ "node": ">=0.10.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -1573,6 +1766,60 @@ "node": ">=6" } }, + "node_modules/express": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1595,6 +1842,18 @@ "node": ">=8.6.0" } }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1604,13 +1863,13 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "node_modules/fast-redact": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.1.tgz", - "integrity": "sha512-odVmjC8x8jNeMZ3C+rPMESzXVSEU8tSWSHv9HFxP2mm89G/1WwqhrerJDQm9Zus8X6aoRgQDThKqptdNA6bt+A==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.2.tgz", + "integrity": "sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==", "engines": { "node": ">=6" } @@ -1659,6 +1918,52 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -1678,11 +1983,59 @@ "integrity": "sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw==" }, "node_modules/flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -1692,7 +2045,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.2", @@ -1711,31 +2064,14 @@ "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "node_modules/functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", "dev": true }, - "node_modules/gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "devOptional": true, - "dependencies": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -1744,21 +2080,34 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-intrinsic": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", + "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", "devOptional": true }, "node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -1770,21 +2119,21 @@ } }, "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 6" + "node": ">=10.13.0" } }, "node_modules/globals": { - "version": "13.12.1", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", - "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -1817,20 +2166,25 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, "node_modules/graceful-readlink": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==" + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -1839,18 +2193,23 @@ } }, "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "devOptional": true + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/highlight.js": { "version": "10.7.3", @@ -1860,6 +2219,32 @@ "node": "*" } }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -1907,7 +2292,7 @@ "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "engines": { "node": ">=0.8.19" @@ -1916,7 +2301,7 @@ "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -1933,6 +2318,14 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "devOptional": true }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -1946,9 +2339,9 @@ } }, "node_modules/is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -1960,22 +2353,18 @@ "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "devOptional": true, - "dependencies": { - "number-is-nan": "^1.0.0" - }, + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/is-glob": { @@ -1999,41 +2388,40 @@ "node": ">=0.12.0" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "devOptional": true + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/jmespath": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", - "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=", + "integrity": "sha512-+kHj8HXArPfpPEKGLZ+kB5ONRTCiGQXo8RQYL0hH8t6pWXUBBK5KkkQmTNOwKK4LEsd0yTsgtjJVm4UBSZea4w==", "engines": { "node": ">= 0.6.0" } }, "node_modules/joycon": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-2.2.5.tgz", - "integrity": "sha512-YqvUxoOcVPnCp0VU1/56f+iKSdvIRJYPznH22BdXV3xMk75SFXhWeJkZ8C9XxUWt1b5x2X1SxuFygW1U0FmkEQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", "engines": { - "node": ">=6" + "node": ">=10" } }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -2048,16 +2436,13 @@ "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "node_modules/json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dependencies": { - "minimist": "^1.2.5" - }, + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", "bin": { "json5": "lib/cli.js" }, @@ -2068,7 +2453,7 @@ "node_modules/leven": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", + "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==", "engines": { "node": ">=0.10.0" } @@ -2086,6 +2471,21 @@ "node": ">= 0.8.0" } }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -2110,6 +2510,19 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2119,19 +2532,57 @@ "node": ">= 8" } }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.2", + "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" } }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", @@ -2145,9 +2596,9 @@ } }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2156,9 +2607,10 @@ } }, "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "devOptional": true }, "node_modules/mkdirp": { "version": "1.0.4", @@ -2209,13 +2661,21 @@ "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/node-abi": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.8.0.tgz", - "integrity": "sha512-tzua9qWWi7iW4I42vUPKM+SfaF0vQSLAm4yO5J83mSwB7GeoWrDKC/K+8YCnYNwqP5duwazbw2X9l4m8SC2cUw==", + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.24.0.tgz", + "integrity": "sha512-YPG3Co0luSu6GwOBsmIdGW6Wx0NyNDLg/hriIyDllVsNwnI6UeqaWShxC3lbH4LtEQUgoLP3XR1ndXiDAWvmRw==", "devOptional": true, "dependencies": { "semver": "^7.3.5" @@ -2227,37 +2687,13 @@ "node_modules/noms": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", - "integrity": "sha1-2o69nzr51nYJGbJ9nNyAkqczKFk=", + "integrity": "sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==", "dev": true, "dependencies": { "inherits": "^2.0.1", "readable-stream": "~1.0.31" } }, - "node_modules/noms/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "node_modules/noms/node_modules/readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/noms/node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -2267,39 +2703,37 @@ "node": ">=0.10.0" } }, - "node_modules/npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "devOptional": true, - "dependencies": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "devOptional": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "engines": { "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dependencies": { "wrappy": "1" } @@ -2321,6 +2755,36 @@ "node": ">= 0.8.0" } }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -2351,10 +2815,36 @@ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "dependencies": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "engines": { "node": ">=0.10.0" } @@ -2374,6 +2864,11 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -2413,17 +2908,17 @@ } }, "node_modules/pino-pretty": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-4.8.0.tgz", - "integrity": "sha512-mhQfHG4rw5ZFpWL44m0Utjo4GC2+HMfdNvxyA8lLw0sIqn6fCf7uQe6dPckUcW/obly+OQHD7B/MTso6LNizYw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-6.0.0.tgz", + "integrity": "sha512-jyeR2fXXWc68st1DTTM5NhkHlx8p+1fKZMfm84Jwq+jSw08IwAjNaZBZR6ts69hhPOfOjg/NiE1HYW7vBRPL3A==", "dependencies": { "@hapi/bourne": "^2.0.0", "args": "^5.0.1", - "chalk": "^4.0.0", + "colorette": "^1.3.0", "dateformat": "^4.5.1", "fast-safe-stringify": "^2.0.7", "jmespath": "^0.15.0", - "joycon": "^2.2.5", + "joycon": "^3.0.0", "pump": "^3.0.0", "readable-stream": "^3.6.0", "rfdc": "^1.3.0", @@ -2447,24 +2942,23 @@ "node": ">= 6" } }, + "node_modules/pino-pretty/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/pino-std-serializers": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz", "integrity": "sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg==" }, - "node_modules/pino/node_modules/sonic-boom": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz", - "integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==", - "dependencies": { - "atomic-sleep": "^1.0.0", - "flatstr": "^1.0.12" - } - }, "node_modules/prebuild-install": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.0.1.tgz", - "integrity": "sha512-QBSab31WqkyxpnMWQxubYAHR5S9B2+r81ucocew34Fkl98FhvKIF50jIJnNOBmAZfyNV7vE5T6gd3hTVWgY6tg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", "devOptional": true, "dependencies": { "detect-libc": "^2.0.0", @@ -2474,7 +2968,6 @@ "mkdirp-classic": "^0.5.3", "napi-build-utils": "^1.0.1", "node-abi": "^3.3.0", - "npmlog": "^4.0.1", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", @@ -2488,15 +2981,6 @@ "node": ">=10" } }, - "node_modules/prebuild-install/node_modules/detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", - "devOptional": true, - "engines": { - "node": ">=8" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2506,17 +2990,37 @@ "node": ">= 0.8.0" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "devOptional": true + "dev": true }, "node_modules/process-warning": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz", "integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==" }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -2535,6 +3039,20 @@ "node": ">=6" } }, + "node_modules/qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -2560,6 +3078,28 @@ "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -2578,25 +3118,22 @@ "node_modules/rc/node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "devOptional": true, "engines": { "node": ">=0.10.0" } }, "node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "devOptional": true, + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "dev": true, "dependencies": { "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" } }, "node_modules/readdirp": { @@ -2631,18 +3168,18 @@ "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "engines": { "node": ">=0.10.0" } }, "node_modules/resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, "dependencies": { - "is-core-module": "^2.8.1", + "is-core-module": "^2.9.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -2716,9 +3253,28 @@ } }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sax": { "version": "1.2.4", @@ -2726,9 +3282,9 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "devOptional": true, "dependencies": { "lru-cache": "^6.0.0" @@ -2740,11 +3296,65 @@ "node": ">=10" } }, - "node_modules/set-blocking": { + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "devOptional": true + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "node_modules/sha.js": { "version": "2.4.11", @@ -2779,11 +3389,18 @@ "node": ">=8" } }, - "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "devOptional": true + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/simple-concat": { "version": "1.0.1", @@ -2840,11 +3457,12 @@ } }, "node_modules/sonic-boom": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.6.0.tgz", - "integrity": "sha512-6xYZFRmDEtxGqfOKcDQ4cPLrNa0SPEDI+wlzDAHowXE6YV42NeXqg9mP2KkiM8JVu3lHfZ2iQKYlGOz+kTpphg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz", + "integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==", "dependencies": { - "atomic-sleep": "^1.0.0" + "atomic-sleep": "^1.0.0", + "flatstr": "^1.0.12" } }, "node_modules/source-map": { @@ -2887,54 +3505,61 @@ "node": ">= 6" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "node_modules/split2/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } }, "node_modules/sqlite": { - "version": "4.0.25", - "resolved": "https://registry.npmjs.org/sqlite/-/sqlite-4.0.25.tgz", - "integrity": "sha512-gqCEcLF8FOTeW/na3SRYWLQkw2jZXgVj1DdgRJbm0jvrhnUgBIuNDUUm649AnBNDNHhI5XskwT8dvc8vearRLQ==" + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/sqlite/-/sqlite-4.1.2.tgz", + "integrity": "sha512-FlBG51gHbux5vPjwnoqFEghNGvnTMTbHyiI09U3qFTQs9AtWuwd4i++6+WCusCXKrVdIDLzfdGekrolr3m4U4A==" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } }, "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true }, "node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "devOptional": true, + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "devOptional": true, + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dependencies": { - "ansi-regex": "^2.0.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "engines": { "node": ">=4" @@ -2952,14 +3577,14 @@ } }, "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dependencies": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/supports-preserve-symlinks-flag": { @@ -3016,10 +3641,19 @@ "node": ">= 6" } }, + "node_modules/tar-stream/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "devOptional": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "node_modules/thenify": { @@ -3033,7 +3667,7 @@ "node_modules/thenify-all": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", "dependencies": { "thenify": ">= 3.1.0 < 4" }, @@ -3051,6 +3685,42 @@ "xtend": "~4.0.1" } }, + "node_modules/through2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/through2/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/through2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/through2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3063,6 +3733,14 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -3073,20 +3751,20 @@ } }, "node_modules/ts-node-dev": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.8.tgz", - "integrity": "sha512-Q/m3vEwzYwLZKmV6/0VlFxcZzVV/xcgOt+Tx/VjaaRHyiBcFlV0541yrT09QjzzCxlDZ34OzKjrFAynlmtflEg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-2.0.0.tgz", + "integrity": "sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==", "dev": true, "dependencies": { "chokidar": "^3.5.1", "dynamic-dedupe": "^0.3.0", - "minimist": "^1.2.5", + "minimist": "^1.2.6", "mkdirp": "^1.0.4", "resolve": "^1.0.0", "rimraf": "^2.6.1", "source-map-support": "^0.5.12", "tree-kill": "^1.2.2", - "ts-node": "^9.0.0", + "ts-node": "^10.4.0", "tsconfig": "^7.0.0" }, "bin": { @@ -3119,29 +3797,46 @@ } }, "node_modules/ts-node-dev/node_modules/ts-node": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", - "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", - "dev": true, - "dependencies": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", "arg": "^4.1.0", "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", - "source-map-support": "^0.5.17", + "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "bin": { "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", "ts-node-script": "dist/bin-script.js", "ts-node-transpile-only": "dist/bin-transpile.js", "ts-script": "dist/bin-script-deprecated.js" }, - "engines": { - "node": ">=10.0.0" - }, "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } } }, "node_modules/tsconfig": { @@ -3159,16 +3854,16 @@ "node_modules/tsconfig/node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/tsutils": { "version": "3.21.0", @@ -3194,7 +3889,7 @@ "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "devOptional": true, "dependencies": { "safe-buffer": "^5.0.1" @@ -3227,10 +3922,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typeorm": { - "version": "0.2.44", - "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.2.44.tgz", - "integrity": "sha512-yFyb9Ts73vGaS/O06TvLpzvT5U/ngO31GeciNc0eoH7P1QcG8kVZdOy9FHJqkTeDmIljMRgWjbYUoMw53ZY7Xw==", + "version": "0.2.45", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.2.45.tgz", + "integrity": "sha512-c0rCO8VMJ3ER7JQ73xfk0zDnVv0WDjpsP6Q1m6CVKul7DB9iVdWLRjPzc8v2eaeBuomsbZ2+gTaYr8k1gm3bYA==", "dependencies": { "@sqltools/formatter": "^1.2.2", "app-root-path": "^3.0.0", @@ -3321,49 +4028,19 @@ } } }, - "node_modules/typeorm/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/typeorm/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/typeorm/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "node_modules/typeorm/node_modules/dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "engines": { + "node": ">=10" } }, "node_modules/typescript": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", - "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", + "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3372,6 +4049,14 @@ "node": ">=4.2.0" } }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/untildify": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", @@ -3390,10 +4075,31 @@ "punycode": "^2.1.0" } }, + "node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dependencies": { + "inherits": "2.0.3" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } }, "node_modules/uuid": { "version": "8.3.2", @@ -3403,12 +4109,20 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3424,15 +4138,6 @@ "node": ">= 8" } }, - "node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "devOptional": true, - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -3458,80 +4163,10 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/xml2js": { "version": "0.4.23", @@ -3577,68 +4212,28 @@ "devOptional": true }, "node_modules/yargs": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", - "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" }, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "engines": { - "node": ">=8" + "node": ">=12" } }, "node_modules/yn": { @@ -3650,6 +4245,18 @@ "node": ">=6" } }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zen-observable": { "version": "0.8.15", "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", @@ -3663,52 +4270,67 @@ "@types/zen-observable": "0.8.3", "zen-observable": "0.8.15" } + }, + "node_modules/zlib": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/zlib/-/zlib-1.0.5.tgz", + "integrity": "sha512-40fpE2II+Cd3k8HWTWONfeKE2jL+P42iWJ1zzps5W51qcTsOUKM5Q5m2PFb0CLxlmFAaUuUdJGc3OfZy947v0w==", + "hasInstallScript": true, + "engines": { + "node": ">=0.2.0" + } } }, "dependencies": { + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + } + }, + "@decorators/di": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@decorators/di/-/di-1.0.3.tgz", + "integrity": "sha512-wafyQo5lqGABT+Lh7Od9/qULg7DG/kZFU3mLUKZFuiV/KATYlnv198yQxaZUZerhUDoTl/cZKu9t4mJa0rZK4Q==", + "requires": { + "reflect-metadata": "0.1.13" + } + }, + "@decorators/express": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@decorators/express/-/express-2.6.0.tgz", + "integrity": "sha512-th/LqSAHAAW1ob2CU6hxtsfnryGThmz0AmGP45rA3Yoi0Q7+FHAZVfH7NJSYHITai/uXjjOtZPPW/V0gFfbgUw==", + "requires": {} + }, "@eslint/eslintrc": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", - "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.1.tgz", + "integrity": "sha512-OhSY22oQQdw3zgPOOwdoj01l/Dzl1Z+xyUP33tkSN+aqyEhymJCcPHyXt+ylW8FSe0TfRC2VG+ROQOapD0aZSQ==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.3.1", - "globals": "^13.9.0", + "espree": "^9.4.0", + "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - } } }, "@hapi/bourne": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-2.0.0.tgz", - "integrity": "sha512-WEezM1FWztfbzqIUbsDzFRVMxSoLy3HugVcux6KDDtTqzPsLE8NDRHfXvev66aH1i2oOKKar3/XDjbvh/OUBdg==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-2.1.0.tgz", + "integrity": "sha512-i1BpaNDVLJdRBEKeJWkVO6tYX6DMFBuwMhSuWqLsY4ufeTKGVuV5rBsUhxPayXqnnWHgXUAmWK16H/ykO5Wj4Q==" }, "@humanwhocodes/config-array": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", - "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", + "integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", @@ -3716,12 +4338,54 @@ "minimatch": "^3.0.4" } }, + "@humanwhocodes/gitignore-to-minimatch": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", + "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", + "dev": true + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, "@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@ledgerhq/compressjs": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@ledgerhq/compressjs/-/compressjs-1.3.2.tgz", + "integrity": "sha512-gonFwAifRkSYDO7rt3NIBlvjvY8Nw+NM6LT1SuOBppuvoKbYtBViNh3EBPbP86+3Y4ux7DLUsNiUlqOgubJsdA==", + "requires": { + "commander": "^2.20.0" + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3749,16 +4413,17 @@ } }, "@runejs/common": { - "version": "2.0.2-beta.2", - "resolved": "https://registry.npmjs.org/@runejs/common/-/common-2.0.2-beta.2.tgz", - "integrity": "sha512-Xd9/Ws3ByAdI1R08Ye9fWqlS8SImjQw05Bsw0w46zcGyDx8N6jmeClf9Y/s4Akh9VZQuoAMFg2ANKsRK0MucNw==", + "version": "3.0.0-beta.10", + "resolved": "https://registry.npmjs.org/@runejs/common/-/common-3.0.0-beta.10.tgz", + "integrity": "sha512-4EnjDBlAfv26oHkSg6q6xAU7gfQj4QKKDUh/e0NJZ3DkwrQuimlNHEfprn7/Wl6wa4mzqJZl05MT2XRfGFhttQ==", "requires": { - "compressjs": "^1.0.3", - "js-yaml": "^3.14.1", + "@ledgerhq/compressjs": "^1.3.2", + "buffer": "^6.0.3", + "dotenv": "^16.0.0", + "path": "^0.12.7", "pino": "^6.14.0", - "pino-pretty": "^4.8.0", - "sonic-boom": "^2.6.0", - "tslib": "^2.3.1" + "pino-pretty": "^6.0.0", + "zlib": "^1.0.5" } }, "@runejs/eslint-config": { @@ -3773,6 +4438,81 @@ "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.3.tgz", "integrity": "sha512-O3uyB/JbkAEMZaP3YqyHH7TMnex7tWyCbCI4EfJdOCoN6HIhqdJBWTM6aCCiWQ/5f5wxjgU735QAIpJbjDvmzg==" }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "@types/adm-zip": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.0.tgz", + "integrity": "sha512-FCJBJq9ODsQZUNURo5ILAQueuA8WJhRvuihS3ke2iI25mJlfV2LK8jG2Qj2z2AWg8U0FtWWqBHVRetceLskSaw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.30", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.30.tgz", + "integrity": "sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, "@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -3783,21 +4523,49 @@ } }, "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", "dev": true }, "@types/node": { - "version": "16.11.26", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz", - "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==", + "version": "16.11.56", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.56.tgz", + "integrity": "sha512-aFcUkv7EddxxOa/9f74DINReQ/celqH8DiB3fRYgVDM2Xm5QJL8sl80QKuAnGvwAsMn+H3IFA6WCrQh1CY7m1A==", + "dev": true + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", "dev": true }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dev": true, + "requires": { + "@types/mime": "*", + "@types/node": "*" + } + }, "@types/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", + "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==", "dev": true }, "@types/strip-json-comments": { @@ -3807,18 +4575,18 @@ "dev": true }, "@types/yargs": { - "version": "17.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.9.tgz", - "integrity": "sha512-Ci8+4/DOtkHRylcisKmVMtmVO5g7weUVCKcsu1sJvF1bn0wExTmbHmhFKj7AnEm0de800iovGhdSKzYnzbaHpg==", + "version": "17.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.12.tgz", + "integrity": "sha512-Nz4MPhecOFArtm81gFQvQqdV7XYCrWKx5uUt6GNHredFHn1i2mtWqXTON7EPXMtNi1qjtjEM/VCHDhcHsAMLXQ==", "dev": true, "requires": { "@types/yargs-parser": "*" } }, "@types/yargs-parser": { - "version": "20.2.1", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", - "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, "@types/zen-observable": { @@ -3827,104 +4595,114 @@ "integrity": "sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw==" }, "@typescript-eslint/eslint-plugin": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.14.0.tgz", - "integrity": "sha512-ir0wYI4FfFUDfLcuwKzIH7sMVA+db7WYen47iRSaCGl+HMAZI9fpBwfDo45ZALD3A45ZGyHWDNLhbg8tZrMX4w==", + "version": "5.36.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.36.1.tgz", + "integrity": "sha512-iC40UK8q1tMepSDwiLbTbMXKDxzNy+4TfPWgIL661Ym0sD42vRcQU93IsZIrmi+x292DBr60UI/gSwfdVYexCA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.14.0", - "@typescript-eslint/type-utils": "5.14.0", - "@typescript-eslint/utils": "5.14.0", - "debug": "^4.3.2", + "@typescript-eslint/scope-manager": "5.36.1", + "@typescript-eslint/type-utils": "5.36.1", + "@typescript-eslint/utils": "5.36.1", + "debug": "^4.3.4", "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", + "ignore": "^5.2.0", "regexpp": "^3.2.0", - "semver": "^7.3.5", + "semver": "^7.3.7", "tsutils": "^3.21.0" } }, "@typescript-eslint/parser": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.14.0.tgz", - "integrity": "sha512-aHJN8/FuIy1Zvqk4U/gcO/fxeMKyoSv/rS46UXMXOJKVsLQ+iYPuXNbpbH7cBLcpSbmyyFbwrniLx5+kutu1pw==", + "version": "5.36.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.36.1.tgz", + "integrity": "sha512-/IsgNGOkBi7CuDfUbwt1eOqUXF9WGVBW9dwEe1pi+L32XrTsZIgmDFIi2RxjzsvB/8i+MIf5JIoTEH8LOZ368A==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.14.0", - "@typescript-eslint/types": "5.14.0", - "@typescript-eslint/typescript-estree": "5.14.0", - "debug": "^4.3.2" + "@typescript-eslint/scope-manager": "5.36.1", + "@typescript-eslint/types": "5.36.1", + "@typescript-eslint/typescript-estree": "5.36.1", + "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.14.0.tgz", - "integrity": "sha512-LazdcMlGnv+xUc5R4qIlqH0OWARyl2kaP8pVCS39qSL3Pd1F7mI10DbdXeARcE62sVQE4fHNvEqMWsypWO+yEw==", + "version": "5.36.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.36.1.tgz", + "integrity": "sha512-pGC2SH3/tXdu9IH3ItoqciD3f3RRGCh7hb9zPdN2Drsr341zgd6VbhP5OHQO/reUqihNltfPpMpTNihFMarP2w==", "dev": true, "requires": { - "@typescript-eslint/types": "5.14.0", - "@typescript-eslint/visitor-keys": "5.14.0" + "@typescript-eslint/types": "5.36.1", + "@typescript-eslint/visitor-keys": "5.36.1" } }, "@typescript-eslint/type-utils": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.14.0.tgz", - "integrity": "sha512-d4PTJxsqaUpv8iERTDSQBKUCV7Q5yyXjqXUl3XF7Sd9ogNLuKLkxz82qxokqQ4jXdTPZudWpmNtr/JjbbvUixw==", + "version": "5.36.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.36.1.tgz", + "integrity": "sha512-xfZhfmoQT6m3lmlqDvDzv9TiCYdw22cdj06xY0obSznBsT///GK5IEZQdGliXpAOaRL34o8phEvXzEo/VJx13Q==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.14.0", - "debug": "^4.3.2", + "@typescript-eslint/typescript-estree": "5.36.1", + "@typescript-eslint/utils": "5.36.1", + "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.14.0.tgz", - "integrity": "sha512-BR6Y9eE9360LNnW3eEUqAg6HxS9Q35kSIs4rp4vNHRdfg0s+/PgHgskvu5DFTM7G5VKAVjuyaN476LCPrdA7Mw==", + "version": "5.36.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.36.1.tgz", + "integrity": "sha512-jd93ShpsIk1KgBTx9E+hCSEuLCUFwi9V/urhjOWnOaksGZFbTOxAT47OH2d4NLJnLhkVD+wDbB48BuaycZPLBg==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.14.0.tgz", - "integrity": "sha512-QGnxvROrCVtLQ1724GLTHBTR0lZVu13izOp9njRvMkCBgWX26PKvmMP8k82nmXBRD3DQcFFq2oj3cKDwr0FaUA==", + "version": "5.36.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.36.1.tgz", + "integrity": "sha512-ih7V52zvHdiX6WcPjsOdmADhYMDN15SylWRZrT2OMy80wzKbc79n8wFW0xpWpU0x3VpBz/oDgTm2xwDAnFTl+g==", "dev": true, "requires": { - "@typescript-eslint/types": "5.14.0", - "@typescript-eslint/visitor-keys": "5.14.0", - "debug": "^4.3.2", - "globby": "^11.0.4", + "@typescript-eslint/types": "5.36.1", + "@typescript-eslint/visitor-keys": "5.36.1", + "debug": "^4.3.4", + "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.5", + "semver": "^7.3.7", "tsutils": "^3.21.0" } }, "@typescript-eslint/utils": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.14.0.tgz", - "integrity": "sha512-EHwlII5mvUA0UsKYnVzySb/5EE/t03duUTweVy8Zqt3UQXBrpEVY144OTceFKaOe4xQXZJrkptCf7PjEBeGK4w==", + "version": "5.36.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.36.1.tgz", + "integrity": "sha512-lNj4FtTiXm5c+u0pUehozaUWhh7UYKnwryku0nxJlYUEWetyG92uw2pr+2Iy4M/u0ONMKzfrx7AsGBTCzORmIg==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.14.0", - "@typescript-eslint/types": "5.14.0", - "@typescript-eslint/typescript-estree": "5.14.0", + "@typescript-eslint/scope-manager": "5.36.1", + "@typescript-eslint/types": "5.36.1", + "@typescript-eslint/typescript-estree": "5.36.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/visitor-keys": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.14.0.tgz", - "integrity": "sha512-yL0XxfzR94UEkjBqyymMLgCBdojzEuy/eim7N9/RIcTNxpJudAcqsU8eRyfzBbcEzGoPWfdM3AGak3cN08WOIw==", + "version": "5.36.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.36.1.tgz", + "integrity": "sha512-ojB9aRyRFzVMN3b5joSYni6FAS10BBSCAfKJhjJAV08t/a95aM6tAhz+O1jF+EtgxktuSO3wJysp2R+Def/IWQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.14.0", - "eslint-visitor-keys": "^3.0.0" + "@typescript-eslint/types": "5.36.1", + "eslint-visitor-keys": "^3.3.0" + } + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" } }, "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", "dev": true }, "acorn-jsx": { @@ -3934,6 +4712,17 @@ "dev": true, "requires": {} }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, + "adm-zip": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.9.tgz", + "integrity": "sha512-s+3fXLkeeLjZ2kLjCBwQufpI5fuN+kIGBxu6530nVQZGVol0d7Y/M88/xw9HGGUcJjKf8LutN3VPRUBq6N7Ajg==" + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -3949,26 +4738,25 @@ "amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==" }, "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "devOptional": true + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "requires": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" } }, "any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" }, "anymatch": { "version": "3.1.2", @@ -3981,25 +4769,9 @@ } }, "app-root-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.0.0.tgz", - "integrity": "sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw==" - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "devOptional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "devOptional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", + "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==" }, "arg": { "version": "4.1.3", @@ -4008,17 +4780,14 @@ "dev": true }, "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "args": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/args/-/args-5.0.1.tgz", - "integrity": "sha512-1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/args/-/args-5.0.3.tgz", + "integrity": "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==", "requires": { "camelcase": "5.0.0", "chalk": "2.4.2", @@ -4026,6 +4795,14 @@ "mri": "1.1.4" }, "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -4035,24 +4812,74 @@ "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } } } }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "atomic-sleep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==" }, + "axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "requires": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "base64-js": { "version": "1.5.1", @@ -4060,13 +4887,13 @@ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "better-sqlite3": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-7.5.0.tgz", - "integrity": "sha512-6FdG9DoytYGDhLW7VWW1vxjEz7xHkqK6LnaUQYA8d6GHNgZhu9PFX2xwKEEnSBRoT1J4PjTUPeg217ShxNmuPg==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-7.6.2.tgz", + "integrity": "sha512-S5zIU1Hink2AH4xPsN0W43T1/AJ5jrPh7Oy07ocuW/AKYYY02GWzz9NH0nbSMn/gw6fDZ5jZ1QsHt1BXAwJ6Lg==", "devOptional": true, "requires": { "bindings": "^1.5.0", - "prebuild-install": "^7.0.0" + "prebuild-install": "^7.1.0" } }, "binary-extensions": { @@ -4095,6 +4922,16 @@ "readable-stream": "^3.4.0" }, "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "devOptional": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -4105,6 +4942,49 @@ "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "devOptional": true, + "requires": { + "safe-buffer": "~5.2.0" + } + } + } + }, + "body-parser": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } } }, @@ -4127,13 +5007,12 @@ } }, "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "devOptional": true, + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "requires": { "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "ieee754": "^1.2.1" } }, "buffer-from": { @@ -4142,6 +5021,20 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -4154,48 +5047,12 @@ "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==" }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } } }, "chokidar": { @@ -4212,6 +5069,17 @@ "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } } }, "chownr": { @@ -4231,36 +5099,8 @@ "parse5": "^5.1.1", "parse5-htmlparser2-tree-adapter": "^6.0.0", "yargs": "^16.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, + }, + "dependencies": { "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -4290,84 +5130,85 @@ "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - } } }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "devOptional": true - }, "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "requires": { - "color-name": "1.1.3" + "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "commander": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", - "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "requires": { - "graceful-readlink": ">= 1.0.0" + "delayed-stream": "~1.0.0" } }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, "compressjs": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/compressjs/-/compressjs-1.0.3.tgz", - "integrity": "sha1-ldt03VuQOM+AvKMhqw7eJxtJWbY=", + "integrity": "sha512-jpKJjBTretQACTGLNuvnozP1JdP2ZLrjdGdBgk/tz1VfXlUcBhhSZW6vEsuThmeot/yjvSrPQKEgfF3X2Lpi8Q==", "requires": { "amdefine": "~1.0.0", "commander": "~2.8.1" + }, + "dependencies": { + "commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha512-+pJLBFVk+9ZZdlAOB5WuIElVPPth47hILFkmGym57aq8kwxsowvByvB0DHs1vQAhyMZzdcpTtF0VDKGkSDR4ZQ==", + "requires": { + "graceful-readlink": ">= 1.0.0" + } + } } }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "devOptional": true + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "copyfiles": { "version": "2.4.1", @@ -4384,38 +5225,6 @@ "yargs": "^16.1.0" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -4440,10 +5249,10 @@ } }, "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "devOptional": true + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true }, "create-require": { "version": "1.1.1", @@ -4468,9 +5277,9 @@ "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==" }, "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { "ms": "2.1.2" } @@ -4496,10 +5305,25 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "delegates": { + "delayed-stream": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", "devOptional": true }, "diff": { @@ -4527,24 +5351,34 @@ } }, "dotenv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", - "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==" + "version": "16.0.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.2.tgz", + "integrity": "sha512-JvpYKUmzQhYoIFgK2MOnF3bciIZoItIIoryihy0rIA+H4Jy0FmgyKYAHCTN98P5ybGSJcIFbh6QKeJdtZd1qhA==" }, "dynamic-dedupe": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", - "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=", + "integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==", "dev": true, "requires": { "xtend": "^4.0.0" } }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -4558,19 +5392,27 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true }, "eslint": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.11.0.tgz", - "integrity": "sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==", + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.23.0.tgz", + "integrity": "sha512-pBG/XOn0MsJcKcTRLr27S5HpzQo4kLr+HjLQIyK4EiCsijDl/TB+h5uEuJU6bQ8Edvwz1XWOjpaP2qgnXGpTcA==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.2.1", - "@humanwhocodes/config-array": "^0.9.2", + "@eslint/eslintrc": "^1.3.1", + "@humanwhocodes/config-array": "^0.10.4", + "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "@humanwhocodes/module-importer": "^1.0.1", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -4580,14 +5422,17 @@ "eslint-scope": "^7.1.1", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.1", + "espree": "^9.4.0", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", - "globals": "^13.6.0", + "globals": "^13.15.0", + "globby": "^11.1.0", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -4596,34 +5441,15 @@ "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "text-table": "^0.2.0" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, "eslint-scope": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", @@ -4639,33 +5465,6 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } } } }, @@ -4703,21 +5502,16 @@ "dev": true }, "espree": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", - "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", + "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", "dev": true, "requires": { - "acorn": "^8.7.0", - "acorn-jsx": "^5.3.1", + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.3.0" } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, "esquery": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", @@ -4764,12 +5558,70 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + }, "expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", "devOptional": true }, + "express": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4787,6 +5639,17 @@ "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } } }, "fast-json-stable-stringify": { @@ -4798,13 +5661,13 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "fast-redact": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.1.tgz", - "integrity": "sha512-odVmjC8x8jNeMZ3C+rPMESzXVSEU8tSWSHv9HFxP2mm89G/1WwqhrerJDQm9Zus8X6aoRgQDThKqptdNA6bt+A==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.2.tgz", + "integrity": "sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==" }, "fast-safe-stringify": { "version": "2.1.1", @@ -4844,6 +5707,45 @@ "to-regex-range": "^5.0.1" } }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -4860,11 +5762,36 @@ "integrity": "sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw==" }, "flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "follow-redirects": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + }, "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -4874,7 +5801,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "fsevents": { "version": "2.3.2", @@ -4886,68 +5813,61 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", "dev": true }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "devOptional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, + "get-intrinsic": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", + "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, "github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", "devOptional": true }, "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "requires": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" } }, "globals": { - "version": "13.12.1", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", - "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -4968,40 +5888,64 @@ } }, "graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, "graceful-readlink": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==" + }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } }, "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "devOptional": true + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, "highlight.js": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==" }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -5026,13 +5970,13 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "requires": { "once": "^1.3.0", "wrappy": "1" @@ -5049,6 +5993,11 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "devOptional": true }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -5059,9 +6008,9 @@ } }, "is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", "dev": true, "requires": { "has": "^1.0.3" @@ -5070,17 +6019,13 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "devOptional": true, - "requires": { - "number-is-nan": "^1.0.0" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, "is-glob": { "version": "4.0.3", @@ -5098,34 +6043,33 @@ "dev": true }, "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "devOptional": true + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "jmespath": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", - "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" + "integrity": "sha512-+kHj8HXArPfpPEKGLZ+kB5ONRTCiGQXo8RQYL0hH8t6pWXUBBK5KkkQmTNOwKK4LEsd0yTsgtjJVm4UBSZea4w==" }, "joycon": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-2.2.5.tgz", - "integrity": "sha512-YqvUxoOcVPnCp0VU1/56f+iKSdvIRJYPznH22BdXV3xMk75SFXhWeJkZ8C9XxUWt1b5x2X1SxuFygW1U0FmkEQ==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==" }, "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" } }, "json-schema-traverse": { @@ -5137,21 +6081,18 @@ "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "requires": { - "minimist": "^1.2.5" - } + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==" }, "leven": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=" + "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==" }, "levn": { "version": "0.4.1", @@ -5163,6 +6104,15 @@ "type-check": "~0.4.0" } }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -5184,20 +6134,53 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + }, "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" } }, "mimic-response": { @@ -5207,17 +6190,18 @@ "devOptional": true }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "devOptional": true }, "mkdirp": { "version": "1.0.4", @@ -5259,52 +6243,31 @@ "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node-abi": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.8.0.tgz", - "integrity": "sha512-tzua9qWWi7iW4I42vUPKM+SfaF0vQSLAm4yO5J83mSwB7GeoWrDKC/K+8YCnYNwqP5duwazbw2X9l4m8SC2cUw==", - "devOptional": true, - "requires": { - "semver": "^7.3.5" - } + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, - "noms": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", - "integrity": "sha1-2o69nzr51nYJGbJ9nNyAkqczKFk=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "~1.0.31" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } + "node-abi": { + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.24.0.tgz", + "integrity": "sha512-YPG3Co0luSu6GwOBsmIdGW6Wx0NyNDLg/hriIyDllVsNwnI6UeqaWShxC3lbH4LtEQUgoLP3XR1ndXiDAWvmRw==", + "devOptional": true, + "requires": { + "semver": "^7.3.5" + } + }, + "noms": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", + "integrity": "sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "~1.0.31" } }, "normalize-path": { @@ -5313,33 +6276,28 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "devOptional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "devOptional": true - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "requires": { "wrappy": "1" } @@ -5358,6 +6316,24 @@ "word-wrap": "^1.2.3" } }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5387,10 +6363,30 @@ } } }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "requires": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, "path-key": { "version": "3.1.1", @@ -5404,6 +6400,11 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -5428,31 +6429,20 @@ "process-warning": "^1.0.0", "quick-format-unescaped": "^4.0.3", "sonic-boom": "^1.0.2" - }, - "dependencies": { - "sonic-boom": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz", - "integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==", - "requires": { - "atomic-sleep": "^1.0.0", - "flatstr": "^1.0.12" - } - } } }, "pino-pretty": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-4.8.0.tgz", - "integrity": "sha512-mhQfHG4rw5ZFpWL44m0Utjo4GC2+HMfdNvxyA8lLw0sIqn6fCf7uQe6dPckUcW/obly+OQHD7B/MTso6LNizYw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-6.0.0.tgz", + "integrity": "sha512-jyeR2fXXWc68st1DTTM5NhkHlx8p+1fKZMfm84Jwq+jSw08IwAjNaZBZR6ts69hhPOfOjg/NiE1HYW7vBRPL3A==", "requires": { "@hapi/bourne": "^2.0.0", "args": "^5.0.1", - "chalk": "^4.0.0", + "colorette": "^1.3.0", "dateformat": "^4.5.1", "fast-safe-stringify": "^2.0.7", "jmespath": "^0.15.0", - "joycon": "^2.2.5", + "joycon": "^3.0.0", "pump": "^3.0.0", "readable-stream": "^3.6.0", "rfdc": "^1.3.0", @@ -5469,6 +6459,14 @@ "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } } } }, @@ -5478,9 +6476,9 @@ "integrity": "sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg==" }, "prebuild-install": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.0.1.tgz", - "integrity": "sha512-QBSab31WqkyxpnMWQxubYAHR5S9B2+r81ucocew34Fkl98FhvKIF50jIJnNOBmAZfyNV7vE5T6gd3hTVWgY6tg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", "devOptional": true, "requires": { "detect-libc": "^2.0.0", @@ -5490,20 +6488,11 @@ "mkdirp-classic": "^0.5.3", "napi-build-utils": "^1.0.1", "node-abi": "^3.3.0", - "npmlog": "^4.0.1", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" - }, - "dependencies": { - "detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", - "devOptional": true - } } }, "prelude-ls": { @@ -5512,17 +6501,31 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "devOptional": true + "dev": true }, "process-warning": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz", "integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==" }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -5538,6 +6541,14 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "requires": { + "side-channel": "^1.0.4" + } + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5549,6 +6560,22 @@ "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -5564,24 +6591,21 @@ "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "devOptional": true } } }, "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "devOptional": true, + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "dev": true, "requires": { "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" } }, "readdirp": { @@ -5607,15 +6631,15 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" }, "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, "requires": { - "is-core-module": "^2.8.1", + "is-core-module": "^2.9.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -5656,9 +6680,14 @@ } }, "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sax": { "version": "1.2.4", @@ -5666,19 +6695,71 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "devOptional": true, "requires": { "lru-cache": "^6.0.0" } }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "devOptional": true + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "sha.js": { "version": "2.4.11", @@ -5704,11 +6785,15 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "devOptional": true + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } }, "simple-concat": { "version": "1.0.1", @@ -5734,11 +6819,12 @@ "dev": true }, "sonic-boom": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.6.0.tgz", - "integrity": "sha512-6xYZFRmDEtxGqfOKcDQ4cPLrNa0SPEDI+wlzDAHowXE6YV42NeXqg9mP2KkiM8JVu3lHfZ2iQKYlGOz+kTpphg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz", + "integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==", "requires": { - "atomic-sleep": "^1.0.0" + "atomic-sleep": "^1.0.0", + "flatstr": "^1.0.12" } }, "source-map": { @@ -5774,51 +6860,55 @@ "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } } } }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, "sqlite": { - "version": "4.0.25", - "resolved": "https://registry.npmjs.org/sqlite/-/sqlite-4.0.25.tgz", - "integrity": "sha512-gqCEcLF8FOTeW/na3SRYWLQkw2jZXgVj1DdgRJbm0jvrhnUgBIuNDUUm649AnBNDNHhI5XskwT8dvc8vearRLQ==" + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/sqlite/-/sqlite-4.1.2.tgz", + "integrity": "sha512-FlBG51gHbux5vPjwnoqFEghNGvnTMTbHyiI09U3qFTQs9AtWuwd4i++6+WCusCXKrVdIDLzfdGekrolr3m4U4A==" + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true }, "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "devOptional": true, + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } }, "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "devOptional": true, + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "^5.0.1" } }, "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true }, "strip-json-comments": { @@ -5827,11 +6917,11 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" }, "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } }, "supports-preserve-symlinks-flag": { @@ -5875,13 +6965,22 @@ "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "devOptional": true, + "requires": { + "safe-buffer": "~5.2.0" + } } } }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "thenify": { @@ -5895,7 +6994,7 @@ "thenify-all": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", "requires": { "thenify": ">= 3.1.0 < 4" } @@ -5908,6 +7007,44 @@ "requires": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, "to-regex-range": { @@ -5919,6 +7056,11 @@ "is-number": "^7.0.0" } }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, "tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -5926,20 +7068,20 @@ "dev": true }, "ts-node-dev": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.8.tgz", - "integrity": "sha512-Q/m3vEwzYwLZKmV6/0VlFxcZzVV/xcgOt+Tx/VjaaRHyiBcFlV0541yrT09QjzzCxlDZ34OzKjrFAynlmtflEg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-2.0.0.tgz", + "integrity": "sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==", "dev": true, "requires": { "chokidar": "^3.5.1", "dynamic-dedupe": "^0.3.0", - "minimist": "^1.2.5", + "minimist": "^1.2.6", "mkdirp": "^1.0.4", "resolve": "^1.0.0", "rimraf": "^2.6.1", "source-map-support": "^0.5.12", "tree-kill": "^1.2.2", - "ts-node": "^9.0.0", + "ts-node": "^10.4.0", "tsconfig": "^7.0.0" }, "dependencies": { @@ -5953,16 +7095,23 @@ } }, "ts-node": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", - "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "dev": true, "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", "arg": "^4.1.0", "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", - "source-map-support": "^0.5.17", + "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" } } @@ -5983,15 +7132,15 @@ "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true } } }, "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "tsutils": { "version": "3.21.0", @@ -6013,7 +7162,7 @@ "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "devOptional": true, "requires": { "safe-buffer": "^5.0.1" @@ -6034,10 +7183,19 @@ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, "typeorm": { - "version": "0.2.44", - "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.2.44.tgz", - "integrity": "sha512-yFyb9Ts73vGaS/O06TvLpzvT5U/ngO31GeciNc0eoH7P1QcG8kVZdOy9FHJqkTeDmIljMRgWjbYUoMw53ZY7Xw==", + "version": "0.2.45", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.2.45.tgz", + "integrity": "sha512-c0rCO8VMJ3ER7JQ73xfk0zDnVv0WDjpsP6Q1m6CVKul7DB9iVdWLRjPzc8v2eaeBuomsbZ2+gTaYr8k1gm3bYA==", "requires": { "@sqltools/formatter": "^1.2.2", "app-root-path": "^3.0.0", @@ -6058,34 +7216,23 @@ "zen-observable-ts": "^1.0.0" }, "dependencies": { - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "requires": { - "argparse": "^2.0.1" - } + "dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==" } } }, "typescript": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", - "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==" + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", + "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, "untildify": { "version": "4.0.0", @@ -6102,22 +7249,47 @@ "punycode": "^2.1.0" } }, + "util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + } + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6127,15 +7299,6 @@ "isexe": "^2.0.0" } }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "devOptional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -6150,63 +7313,12 @@ "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - } } }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "xml2js": { "version": "0.4.23", @@ -6240,9 +7352,9 @@ "devOptional": true }, "yargs": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", - "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", "requires": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -6251,42 +7363,12 @@ "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - } } }, "yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==" + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" }, "yn": { "version": "3.1.1", @@ -6294,6 +7376,12 @@ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + }, "zen-observable": { "version": "0.8.15", "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", @@ -6307,6 +7395,11 @@ "@types/zen-observable": "0.8.3", "zen-observable": "0.8.15" } + }, + "zlib": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/zlib/-/zlib-1.0.5.tgz", + "integrity": "sha512-40fpE2II+Cd3k8HWTWONfeKE2jL+P42iWJ1zzps5W51qcTsOUKM5Q5m2PFb0CLxlmFAaUuUdJGc3OfZy947v0w==" } } } diff --git a/package.json b/package.json index 27a502a..1676cee 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "@runejs/store", - "version": "1.0.0-beta.1", + "name": "@runejs/filestore", + "version": "1.0.0-next.0", "description": "Tools for managing and indexing the asset file store used with RuneJS.", "main": "./index.js", "types": "./index.d.ts", @@ -8,20 +8,20 @@ ".": "./index.js", "./config": "./config/index.js", "./db": "./db/index.js", - "./scripts": "./scripts/index.js", - "./util": "./util/index.js", - "./indexer": "./scripts/indexer.js", - "./unpacker": "./scripts/unpacker.js" + "./file-system": "./file-system/index.js", + "./http": "./http/index.js", + "./openrs2": "./openrs2/index.js" }, "scripts": { "build": "tsc", - "start": "ts-node-dev src/dev.ts", + "start": "ts-node-dev src/scripts/dev.ts", "lint": "eslint --ext .ts src", "lint:fix": "eslint --ext .ts src --fix", - "unpack": "ts-node-dev src/scripts/unpacker.ts", - "unpacker": "npm run unpack", - "index": "ts-node-dev src/scripts/indexer.ts", - "indexer": "npm run index", + "http": "ts-node-dev --max-old-space-size=2048 src/http/server.ts", + "namehash": "ts-node-dev --max-old-space-size=2048 src/scripts/name-hasher.ts", + "unpack": "ts-node-dev --max-old-space-size=2048 src/scripts/unpacker.ts", + "index": "ts-node-dev --max-old-space-size=2048 src/scripts/indexer.ts", + "builds": "ts-node-dev src/scripts/builds.ts", "copy-documents": "copyfiles package.json README.md .npmignore LICENSE lib", "package": "rimraf lib && npm i && npm run build && npm run copy-documents && cd lib && npm publish --dry-run", "publish:next": "npm run package && cd lib && npm publish -tag next", @@ -30,7 +30,7 @@ }, "repository": { "type": "git", - "url": "git+ssh://git@github.com/runejs/store.git" + "url": "git+ssh://git@github.com/runejs/filestore.git" }, "keywords": [ "runejs", @@ -44,27 +44,30 @@ "author": "Kikorono", "license": "GPL-3.0", "bugs": { - "url": "https://github.com/runejs/store/issues" - }, - "homepage": "https://github.com/runejs/store#readme", - "peerDependencies": { - "@runejs/common": "2.0.2-beta.2", - "graceful-fs": ">=4.2.0", - "tslib": ">=2.3.0", - "typescript": ">=4.5.0" + "url": "https://github.com/runejs/filestore/issues" }, + "homepage": "https://github.com/runejs/filestore#readme", "dependencies": { - "@runejs/common": "2.0.2-beta.2", - "graceful-fs": "^4.2.0", + "@decorators/di": "^1.0.3", + "@decorators/express": "^2.6.0", + "@runejs/common": "3.0.0-beta.10", + "adm-zip": "^0.5.9", + "axios": "^0.27.2", + "compressjs": "^1.0.3", + "express": "^4.18.1", + "graceful-fs": ">=4.2.0", "json5": "^2.2.0", "reflect-metadata": "^0.1.13", "sqlite": "^4.0.25", - "tslib": "^2.3.1", + "tslib": ">=2.4.0", "typeorm": "^0.2.44", - "yargs": "^17.3.1" + "yargs": "^17.3.1", + "zlib": "^1.0.5" }, "devDependencies": { "@runejs/eslint-config": "^1.1.0", + "@types/adm-zip": "^0.5.0", + "@types/express": "^4.17.13", "@types/graceful-fs": "^4.1.5", "@types/node": "^16.11.26", "@types/yargs": "^17.0.9", @@ -75,15 +78,7 @@ "eslint": "^8.11.0", "rimraf": "^3.0.2", "source-map-support": "^0.5.21", - "ts-node-dev": "^1.1.8", - "typescript": "^4.5.5" - }, - "eslintConfig": { - "extends": [ - "@runejs/eslint-config" - ], - "parserOptions": { - "project": "./tsconfig.json" - } + "ts-node-dev": "^2.0.0", + "typescript": "^4.7.4" } } diff --git a/src/archive.ts b/src/archive.ts deleted file mode 100644 index a0d3634..0000000 --- a/src/archive.ts +++ /dev/null @@ -1,448 +0,0 @@ -import { join } from 'path'; -import { existsSync, mkdirSync, rmSync } from 'graceful-fs'; -import { ByteBuffer, logger } from '@runejs/common'; - -import { ArchiveFormat, FileState, FlatFile, Group } from './index'; -import { ArchiveIndexEntity } from './db'; -import { FileBreadcrumb, IndexedFile } from './indexed-file'; -import { ArchiveConfig } from './config'; - - -export class Archive extends IndexedFile { - - public readonly config: ArchiveConfig; - public readonly groups: Map; - - private _missingEncryptionKeys: number; - - public constructor(index: ArchiveIndexEntity, config: ArchiveConfig, breadcrumb?: Partial) { - super(index, breadcrumb); - - this.groups = new Map(); - - config.filesNamed = config.filesNamed || false; - config.versioned = config.versioned || false; - - this.config = config; - this.encryption = this.config.encryption || 'none'; - this.compression = this.config.compression || 'none'; - } - - public override decode(decodeGroups: boolean = true): ByteBuffer | null { - logger.info(`Decoding archive ${this.name}...`); - - this._missingEncryptionKeys = 0; - - this.unpack(); - - logger.info(`Archive ${this.name} checksum: ${this.crc32}`); - - if(this.numericKey === 255) { - return this.data; - } - - this.decompress(); - - if(!this._data?.length) { - logger.error(`Error decompressing file data.`); - return null; - } - - const archiveData = this._data; - const format = this.index.format = archiveData.get('byte', 'unsigned'); - const filesNamed = (archiveData.get('byte', 'unsigned') & 0x01) !== 0; - const groupCount = this.index.groupCount = archiveData.get('short', 'unsigned'); - - logger.info(`${groupCount} groups were found within the ${this.name} archive.`); - - if(filesNamed !== this.config.filesNamed) { - logger.warn(`Archive file name flag mismatch; expected ${this.config.filesNamed} ` + - `but received ${filesNamed}!`); - } - - const groupIndices: number[] = new Array(groupCount); - let accumulator = 0; - - for(let i = 0; i < groupCount; i++) { - const delta = archiveData.get('short', 'unsigned'); - groupIndices[i] = accumulator += delta; - const group = new Group(this.indexService.validateGroup({ - numericKey: groupIndices[i], - name: String(groupIndices[i]), - archive: this - }), { - store: this.store, - archive: this - }); - - group.setState(FileState.encoded); - this.set(groupIndices[i], group); - } - - if(filesNamed) { - for(const groupIndex of groupIndices) { - const group = this.get(groupIndex); - group.nameHash = group.index.nameHash = archiveData.get('int'); - group.name = group.index.name = this.store.findFileName(group.nameHash, String(group.nameHash)); - } - } - - /* read the crc values */ - for(const groupIndex of groupIndices) { - const group = this.get(groupIndex); - group.crc32 = archiveData.get('int'); - } - - /* read the version numbers */ - for(const groupIndex of groupIndices) { - const group = this.get(groupIndex); - group.version = archiveData.get('int'); - } - - /* read the child count */ - const groupChildCounts: Map = new Map(); - - for(const groupIndex of groupIndices) { - // group file count - groupChildCounts.set(groupIndex, archiveData.get('short', 'unsigned')); - } - - /* read the file groupIndices */ - for(const groupIndex of groupIndices) { - const group = this.get(groupIndex) as Group; - const fileCount = groupChildCounts.get(groupIndex); - - accumulator = 0; - for(let i = 0; i < fileCount; i++) { - const delta = archiveData.get('short', 'unsigned'); - const childFileIndex = accumulator += delta; - group.set(childFileIndex, new FlatFile(this.indexService.validateFile({ - numericKey: childFileIndex, - name: String(childFileIndex), - group, archive: this - }), { - store: this.store, - archive: this, - group: group - })); - } - } - - /* read the child name hashes */ - if(filesNamed) { - for(const groupIndex of groupIndices) { - const fileGroup = this.get(groupIndex) as Group; - - for(const [ , childFile ] of fileGroup.files) { - const nameHash = archiveData.get('int'); - if(childFile) { - childFile.nameHash = childFile.index.nameHash = nameHash; - childFile.name = childFile.index.name = - this.store.findFileName(childFile.nameHash, String(childFile.nameHash)); - } - } - } - } - - if(decodeGroups) { - let successes = 0; - let failures = 0; - - for(const [ , group ] of this.groups) { - try { - group.decode(); - - if(group.data?.length && group.state === FileState.raw) { - successes++; - } else { - failures++; - } - } catch(error) { - logger.error(error); - failures++; - } - } - - if(successes) { - logger.info(`${groupCount} groups(s) were found, ` + - `${successes} decompressed successfully.`); - } else { - logger.info(`${groupCount} groups(s) were found.`); - } - - if(failures) { - logger.error(`${failures} groups(s) failed to decompress.`); - } - - if(this.missingEncryptionKeys) { - logger.error(`Missing ${this.missingEncryptionKeys} XTEA decryption key(s).`); - } - } else { - logger.info(`${groupCount} groups(s) were found.`); - } - - this.setData(this._data, FileState.raw); - return this._data ?? null; - } - - public override encode(encodeGroups: boolean = true): ByteBuffer | null { - if(this.numericKey === 255) { - return this.store.encode(); - } - - const groups = this.groups; - const groupCount = groups.size; - - // @TODO add sizes of all files instead of using a set amount here - const buffer = new ByteBuffer(1000 * 1000); - - // Write index file header - buffer.put(this.index.format ?? ArchiveFormat.original); - buffer.put(this.config.filesNamed ? 1 : 0); - buffer.put(groupCount, 'short'); - - // Write file indexes - let writtenFileIndex = 0; - for(const [ , group ] of groups) { - const val = group.numericKey; - buffer.put(val - writtenFileIndex, 'short'); - writtenFileIndex = val; - } - - // Write name hashes (if applicable) - if(this.config.filesNamed) { - for(const [ , file ] of groups) { - buffer.put(file.nameHash ?? -1, 'int'); - } - } - - // Write file crc values - for(const [ , file ] of groups) { - buffer.put(file.crc32 ?? -1, 'int'); - } - - // Write file version numbers - for(const [ , ] of groups) { - buffer.put(0, 'int'); - } - - // Write file group child counts - for(const [ , group ] of groups) { - buffer.put(group.files.size ?? 1, 'short'); - } - - // Write group file indices - for(const [ , group ] of groups) { - if(group.files.size > 1) { - writtenFileIndex = 0; - - for(const [ , file ] of group.files) { - const i = file.numericKey; - buffer.put(i - writtenFileIndex, 'short'); - writtenFileIndex = i; - } - } else { - buffer.put(0, 'short'); - } - } - - // Write group file name hashes (if applicable) - if(this.config.filesNamed) { - for(const [ , group ] of groups) { - if(group.files.size > 1) { - for(const [ , file ] of group.files) { - buffer.put(file.nameHash ?? -1, 'int'); - } - } else { - buffer.put(0, 'int'); - } - } - } - - const indexData = buffer?.flipWriter(); - - if(indexData?.length) { - this.setData(indexData, FileState.encoded); - this.sha256 = this.index.sha256 = this.generateSha256(); - } - - if(encodeGroups) { - this.groups.forEach(group => group.encode()); - } - - return this.data ?? null; - } - - public override compress(compressGroups: boolean = true): ByteBuffer | null { - if(compressGroups) { - this.groups.forEach(group => group.compress()); - } - return super.compress(); - } - - public override async read(compress: boolean = false, readDiskFiles: boolean = true): Promise { - logger.info(`Reading archive ${this.name}...`); - - // Read in all groups within the archive - const groupIndexes = await this.index.groups; - for(const groupIndex of groupIndexes) { - const group = new Group(groupIndex, { - store: this.store, - archive: this - }); - - this.groups.set(group.key, group); - await group.read(false, readDiskFiles); - } - - if(compress) { - // Then compress them, if needed - for(const [ , group ] of this.groups) { - group.compress(); - } - } - - logger.info(`${this.groups.size} groups(s) were loaded from the ${this.name} archive.`); - - this.encode(); - - if(compress) { - return this.compress(); - } else { - return this._data; - } - } - - public override write(): void { - if(!this.groups.size) { - logger.error(`Error writing archive ${this.name || this.key}: Archive is empty.`); - return; - } - - const start = Date.now(); - logger.info(`Writing archive ${this.name || this.key}...`); - - const archivePath = this.outputPath; - - if(existsSync(archivePath)) { - rmSync(archivePath, { recursive: true, force: true }); - } - - mkdirSync(archivePath, { recursive: true }); - - Array.from(this.groups.values()).forEach(group => group.write()); - - const end = Date.now(); - logger.info(`Archive ${this.name || this.key} written in ${(end - start) / 1000} seconds.`) - } - - public async saveIndexData(saveGroups: boolean = true, saveFiles: boolean = true): Promise { - if(!this.groups.size) { - return; - } - - logger.info(`Saving archive ${this.name} to index...`); - - await this.indexService.saveArchiveIndex(this); - - if(saveGroups) { - await this.saveGroupIndexes(saveFiles); - } - - logger.info(`Archive ${this.name} indexing complete.`); - } - - public async saveGroupIndexes(saveFlatFiles: boolean = true): Promise { - const groups = Array.from(this.groups.values()); - - if(groups?.length) { - logger.info(`Saving archive ${ this.name } group indexes...`); - await this.indexService.saveGroupIndexes(groups); - } - - if(saveFlatFiles) { - await this.saveFlatFileIndexes(); - } - } - - public async saveFlatFileIndexes(): Promise { - if(this.config.flatten) { - return; - } - - const groups = Array.from(this.groups.values()); - const flatFiles = groups.filter(group => { - if(!group?.files?.size || group?.index?.flatFile) { - return false; - } - return group.files.size > 1; - }).map(group => Array.from(group.files.values())) - .reduce((a, v) => a.concat(v), []); - - if(flatFiles?.length) { - logger.info(`Saving archive ${ this.name } flat file indexes...`); - await this.indexService.saveFileIndexes(flatFiles); - } - } - - public has(groupKey: string): boolean; - public has(groupKey: number): boolean; - public has(groupKey: string | number): boolean; - public has(groupKey: string | number): boolean { - return this.groups.has(String(groupKey)); - } - - public get(groupKey: string): Group | null; - public get(groupKey: number): Group | null; - public get(groupKey: string | number): Group | null; - public get(groupKey: string | number): Group | null { - return this.groups.get(String(groupKey)) ?? null; - } - - public set(groupKey: string, group: Group): void; - public set(groupKey: number, group: Group): void; - public set(groupKey: string | number, group: Group): void; - public set(groupKey: string | number, group: Group): void { - this.groups.set(String(groupKey), group); - } - - public find(groupName: string): Archive | Group | FlatFile | null { - const children = Array.from(this.groups.values()); - return children.find(child => child?.name === groupName) ?? null; - } - - public incrementMissingEncryptionKeys(): void { - this._missingEncryptionKeys++; - } - - public get missingEncryptionKeys(): number { - return this._missingEncryptionKeys; - } - - public override get path(): string { - if(!this.store?.path) { - throw new Error(`Error generating archive path; Store path not provided for archive ${this.key}.`); - } - if(!this.name) { - throw new Error(`Error generating archive path; Name not provided for archive ${this.key}.`); - } - - return join(this.store.path, 'unpacked', this.name); - } - - public override get outputPath(): string { - if(!this.store?.outputPath) { - throw new Error(`Error generating archive output path; Store output path not provided for archive ${this.key}.`); - } - if(!this.name) { - throw new Error(`Error generating archive output path; Name not provided for archive ${this.key}.`); - } - - return join(this.store.outputPath, this.name); - } - - public get versioned(): boolean { - return this.config.versioned; - } - -} diff --git a/src/config/archive-config.ts b/src/config/archive-config.ts index 611e6d8..e714e44 100644 --- a/src/config/archive-config.ts +++ b/src/config/archive-config.ts @@ -1,16 +1,14 @@ -import { CompressionMethod } from '@runejs/common/compress'; import { EncryptionMethod } from '@runejs/common/encrypt'; export interface ArchiveConfig { - index: number; - name: string; - versioned?: boolean; - compression?: CompressionMethod; - encryption?: EncryptionMethod | [ EncryptionMethod, string ]; + key: number; +} + + +export interface Js5ArchiveConfig extends ArchiveConfig { + encryption?: [ EncryptionMethod, string ]; contentType?: string; - filesNamed?: boolean; - flatten?: boolean; + flattenGroups?: boolean; groupNames?: { [key: string]: number }; - build?: number; } diff --git a/src/config/archive-flags.ts b/src/config/archive-flags.ts new file mode 100644 index 0000000..4d3958b --- /dev/null +++ b/src/config/archive-flags.ts @@ -0,0 +1,6 @@ +export const archiveFlags = (flags: number) => ({ + groupNames: (flags & 0x01) !== 0, + whirlpoolDigests: (flags & 0x02) !== 0, + groupSizes: (flags & 0x04) !== 0, + decompressedCrcs: (flags & 0x08) !== 0, +}); diff --git a/src/config/file-error.ts b/src/config/file-error.ts new file mode 100644 index 0000000..c91a863 --- /dev/null +++ b/src/config/file-error.ts @@ -0,0 +1,3 @@ +export type FileError = + 'FILE_MISSING' | + 'MISSING_ENCRYPTION_KEYS'; diff --git a/src/config/file-type.ts b/src/config/file-type.ts new file mode 100644 index 0000000..199e6d5 --- /dev/null +++ b/src/config/file-type.ts @@ -0,0 +1,11 @@ +export type Js5FileType = + 'FILE' | + 'GROUP' | + 'ARCHIVE' | + 'STORE'; + +export type JagFileType = + 'FILE' | + 'ARCHIVE' | + 'STORE' | + 'CACHE'; diff --git a/src/config/index.ts b/src/config/index.ts index 76f19cd..9fcabeb 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,2 +1,6 @@ export * from './archive-config'; export * from './archive-format'; +export * from './file-error'; +export * from './file-type'; +export * from './archive-flags'; +export * from './name-hasher'; diff --git a/src/config/name-hasher.ts b/src/config/name-hasher.ts new file mode 100644 index 0000000..ba1d7b8 --- /dev/null +++ b/src/config/name-hasher.ts @@ -0,0 +1,98 @@ +import { join } from 'path'; +import { existsSync, readFileSync } from 'graceful-fs'; +import { logger } from '@runejs/common'; + + +export class NameHasher { + + readonly configPath: string; + readonly fileNameHashes: Map; + + constructor(configPath: string) { + this.configPath = configPath; + this.fileNameHashes = new Map(); + this.loadFileNames(); + } + + hashJagFileName(fileName: string): number { + const INT_MAX = 2147483648; + let hash = 0; + fileName = fileName.toUpperCase(); + + for (let i = 0; i < fileName.length; i++) { + hash = hash * 61 + fileName.charCodeAt(i) - 32; + + // Emulate Java's INT overflow-wrapping + while (hash > INT_MAX) { + const diff = hash - INT_MAX; + hash = -INT_MAX + diff; + } + + while (hash < -INT_MAX) { + const diff = Math.abs(hash) - INT_MAX; + hash = INT_MAX - diff; + } + } + + return hash; + } + + hashJs5FileName(fileName: string): number { + if (!fileName) { + return 0; + } + + let hash = 0; + for (let i = 0; i < fileName.length; i++) { + hash = fileName.charCodeAt(i) + ((hash << 5) - hash); + } + + const nameHash = hash | 0; + + this.fileNameHashes.set(nameHash, fileName); + + return nameHash; + } + + findFileName(nameHash: string | number | undefined, defaultName?: string | undefined): string | undefined { + if (!this.fileNameHashes.size) { + this.loadFileNames(); + } + + if (nameHash === undefined || nameHash === null) { + return defaultName; + } + + if (typeof nameHash === 'string') { + nameHash = Number(nameHash); + } + + if (isNaN(nameHash) || nameHash === -1 || nameHash === 0) { + return defaultName; + } + + return this.fileNameHashes.get(nameHash) || defaultName; + } + + loadFileNames(): void { + const configPath = join(this.configPath, 'name-hashes.json'); + if (!existsSync(configPath)) { + logger.error(`Error loading file names: ${configPath} was not found.`); + return; + } + + const nameTable = JSON.parse( + readFileSync(configPath, 'utf-8') + ) as { [key: string]: string }; + + Object.keys(nameTable).forEach( + nameHash => this.fileNameHashes.set(Number(nameHash), nameTable[nameHash]) + ); + + if(!this.fileNameHashes.size) { + logger.error(`Error reading file name lookup table. ` + + `Please ensure that the ${configPath} file exists and is valid.`); + } + } + +} diff --git a/src/db/archive-index.entity.ts b/src/db/archive-index.entity.ts deleted file mode 100644 index 3ef8d8c..0000000 --- a/src/db/archive-index.entity.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Column, Entity, Index, JoinColumn, ManyToOne, OneToMany, PrimaryColumn } from 'typeorm'; - -import { IndexEntity } from './index-entity'; -import { StoreIndexEntity } from './store-index.entity'; -import { GroupIndexEntity } from './group-index.entity'; -import { FileState } from '../file-state'; - - -@Entity('archive_index') -@Index('archive_identifier', [ 'key', 'gameBuild' ], { unique: true }) -export class ArchiveIndexEntity extends IndexEntity { - - @PrimaryColumn('text', { name: 'game_build', nullable: false, unique: false }) - gameBuild: string; - - @Column('integer', { name: 'group_count', nullable: false, default: 0 }) - groupCount: number = 0; - - @Column('integer', { name: 'format', nullable: false, default: 5 }) - format: number = 5; - - @Column('text', { name: 'data_state', nullable: false }) - state: FileState; - - @ManyToOne(() => StoreIndexEntity, async store => store.archives, - { primary: true, onDelete: 'CASCADE' }) - @JoinColumn({ name: 'game_build', referencedColumnName: 'gameBuild' }) - store: StoreIndexEntity; - - @OneToMany(() => GroupIndexEntity, group => group.archive, - { cascade: true, lazy: true }) - groups: Promise | GroupIndexEntity[]; - -} diff --git a/src/db/file-index.entity.ts b/src/db/file-index.entity.ts deleted file mode 100644 index 90ae874..0000000 --- a/src/db/file-index.entity.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Column, Entity, Index, JoinColumn, ManyToOne, PrimaryColumn } from 'typeorm'; - -import { IndexEntity } from './index-entity'; -import { StoreIndexEntity } from './store-index.entity'; -import { ArchiveIndexEntity } from './archive-index.entity'; -import { GroupIndexEntity } from './group-index.entity'; - - -@Entity('file_index') -@Index('file_identifier', [ 'key', 'gameBuild', 'archiveKey', 'groupKey' ], { unique: true }) -export class FileIndexEntity extends IndexEntity { - - @PrimaryColumn('text', { name: 'game_build', nullable: false, unique: false }) - gameBuild: string; - - @PrimaryColumn('integer', { name: 'archive_key', unique: false, nullable: false }) - archiveKey: number; - - @PrimaryColumn('integer', { name: 'group_key', unique: false, nullable: false }) - groupKey: number; - - @Column('integer', { name: 'name_hash', nullable: true, default: 0 }) - nameHash: number = 0; - - @Column('integer', { nullable: false, default: 0 }) - version: number = 0; - - @Column('integer', { name: 'stripe_count', nullable: false, default: 1 }) - stripeCount: number = 1; - - @Column('text', { nullable: true, default: null }) - stripes: string | null = null; - - @ManyToOne(() => StoreIndexEntity, async store => store.files, - { primary: true, onDelete: 'CASCADE' }) - @JoinColumn({ name: 'game_build', referencedColumnName: 'gameBuild' }) - store: StoreIndexEntity; - - @ManyToOne(() => ArchiveIndexEntity, async archive => archive.groups, - { primary: true, onDelete: 'CASCADE' }) - @JoinColumn([ - { name: 'archive_key', referencedColumnName: 'key' }, - { name: 'game_build', referencedColumnName: 'gameBuild' } - ]) - archive: ArchiveIndexEntity; - - @ManyToOne(() => GroupIndexEntity, async group => group.files, - { primary: true, onDelete: 'CASCADE' }) - @JoinColumn([ - { name: 'archive_key', referencedColumnName: 'archiveKey' }, - { name: 'group_key', referencedColumnName: 'key' }, - { name: 'game_build', referencedColumnName: 'gameBuild' } - ]) - group: GroupIndexEntity; - -} diff --git a/src/db/group-index.entity.ts b/src/db/group-index.entity.ts deleted file mode 100644 index db87aca..0000000 --- a/src/db/group-index.entity.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Column, Entity, Index, JoinColumn, ManyToOne, OneToMany, PrimaryColumn } from 'typeorm'; -import { FileIndexEntity } from './file-index.entity'; -import { ArchiveIndexEntity } from './archive-index.entity'; -import { IndexEntity } from './index-entity'; -import { StoreIndexEntity } from './store-index.entity'; -import { FileState } from '../file-state'; - - -@Entity('group_index') -@Index('group_identifier', [ 'key', 'gameBuild', 'archiveKey' ], { unique: true }) -export class GroupIndexEntity extends IndexEntity { - - @PrimaryColumn('text', { name: 'game_build', nullable: false, unique: false }) - gameBuild: string; - - @PrimaryColumn('integer', { name: 'archive_key', unique: false, nullable: false }) - archiveKey: number; - - @Column('boolean', { name: 'flat', nullable: false, default: false }) - flatFile: boolean = false; - - @Column('integer', { name: 'stripe_count', nullable: false, default: 1 }) - stripeCount: number = 1; - - @Column('text', { nullable: true, default: null }) - stripes: string | null = null; - - @Column('integer', { name: 'name_hash', nullable: true, default: 0 }) - nameHash: number = 0; - - @Column('integer', { nullable: false, default: 0 }) - version: number = 0; - - @Column('text', { name: 'data_state', nullable: false }) - state: FileState; - - @ManyToOne(() => StoreIndexEntity, async store => store.groups, - { primary: true, onDelete: 'CASCADE' }) - @JoinColumn({ name: 'game_build', referencedColumnName: 'gameBuild' }) - store: StoreIndexEntity; - - @ManyToOne(() => ArchiveIndexEntity, async archive => archive.groups, - { primary: true, onDelete: 'CASCADE' }) - @JoinColumn([ - { name: 'archive_key', referencedColumnName: 'key' }, - { name: 'game_build', referencedColumnName: 'gameBuild' } - ]) - archive: ArchiveIndexEntity; - - @OneToMany(() => FileIndexEntity, fileIndex => fileIndex.group, - { cascade: true, lazy: true }) - files: Promise | FileIndexEntity[]; - -} diff --git a/src/db/index-database.ts b/src/db/index-database.ts new file mode 100644 index 0000000..e54b09d --- /dev/null +++ b/src/db/index-database.ts @@ -0,0 +1,71 @@ +import { Connection, createConnection, LoggerOptions, Repository } from 'typeorm'; +import { join } from 'path'; +import { existsSync, mkdirSync } from 'graceful-fs'; + + +export abstract class IndexDatabase { + + protected readonly gameBuild: string; + protected readonly databasePath: string; + protected readonly loggerOptions: LoggerOptions; + + protected _connection: Connection; + protected _repository: Repository; + + protected constructor( + gameBuild: string, + databasePath: string, + loggerOptions: LoggerOptions = 'all' + ) { + this.gameBuild = gameBuild; + this.databasePath = databasePath; + // [ 'error', 'warn' ], 'all', etc... + this.loggerOptions = loggerOptions; + } + + abstract openConnection(): Promise; + + abstract upsertIndexes(indexEntities: ENTITY[]): Promise; + + async getIndexes(where: WHERE): Promise { + return await this.repository.find({ + where: { ...where, gameBuild: this.gameBuild } + }) || []; + } + + async getIndex(where: WHERE): Promise { + return await this.repository.findOne({ + where: { ...where, gameBuild: this.gameBuild } + }) || null; + } + + async saveIndexes(indexEntities: ENTITY[]): Promise { + await this.repository.save(indexEntities as any, { + chunk: 500, + transaction: false, + reload: false, + listeners: false, + }); + } + + async saveIndex(indexEntity: ENTITY): Promise { + return await this.repository.save(indexEntity as any); + } + + async closeConnection(): Promise { + await this._connection.close(); + } + + get connection(): Connection { + return this._connection; + } + + get repository(): Repository { + return this._repository; + } + + get loaded(): boolean { + return !!this._connection; + } + +} diff --git a/src/db/index-entity.ts b/src/db/index-entity.ts deleted file mode 100644 index 2f40472..0000000 --- a/src/db/index-entity.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Column, CreateDateColumn, PrimaryColumn, UpdateDateColumn } from 'typeorm'; -import { FileState } from '../index'; - - -export abstract class IndexEntity { - - @PrimaryColumn('integer', { nullable: false, unique: false }) - key: number; - - @Column('text', { nullable: true, default: null }) - name: string | null = null; - - @Column('integer', { nullable: false, default: 0 }) - size: number = 0; - - @Column('integer', { nullable: true, default: null }) - crc32: number | null = null; - - @Column('text', { nullable: true, default: null }) - sha256: string | null = null; - - @Column('blob', { name: 'data', nullable: true, default: null }) - data: Buffer | null = null; - - @CreateDateColumn() - created: Date; - - @UpdateDateColumn() - updated: Date; - -} diff --git a/src/db/index-service.ts b/src/db/index-service.ts deleted file mode 100644 index c5425a0..0000000 --- a/src/db/index-service.ts +++ /dev/null @@ -1,435 +0,0 @@ -import { join } from 'path'; -import { existsSync, mkdirSync } from 'graceful-fs'; -import { Connection, createConnection, Repository } from 'typeorm'; - -import { logger } from '@runejs/common'; - -import { Archive, ArchiveFormat, FileState, FlatFile, Group, IndexedFile, IndexEntity, Store } from '../index'; -import { ArchiveIndexEntity, FileIndexEntity, GroupIndexEntity, StoreIndexEntity } from './index'; - - -const CHUNK_SIZE = 300; // 250 - - -export class IndexService { - - public readonly store: Store; - - private connection: Connection; - - public constructor(store: Store) { - this.store = store; - } - - public async load(): Promise { - const indexPath = join(this.store.path, 'indexes'); - - if(!existsSync(indexPath)) { - mkdirSync(indexPath, { recursive: true }); - } - - this.connection = await createConnection({ - type: 'better-sqlite3', - database: join(indexPath, `index_${this.store.gameBuild}.sqlite3`), - entities: [ StoreIndexEntity, ArchiveIndexEntity, GroupIndexEntity, FileIndexEntity ], - synchronize: true, - // logging: [ 'error', 'warn' ], - logging: 'all', - name: 'index-service' - }); - } - - public async getStoreIndex(): Promise { - return await this.storeRepo.findOne({ - where: { - gameBuild: this.store.gameBuild - } - }) || null; - } - - public async saveStoreIndex(): Promise { - let storeIndex = await this.getStoreIndex(); - let update = true; - - if(!storeIndex) { - storeIndex = new StoreIndexEntity(); - update = false; - } - - if(!storeIndex.gameBuild) { - storeIndex.gameBuild = this.store.gameBuild; - } - - if(!this.connection.isConnected) { - logger.error(`The index database connection was closed prematurely.`); - return null; - } - - storeIndex.data = this.store.data?.toNodeBuffer() || null; - - delete storeIndex.archives; - delete storeIndex.groups; - delete storeIndex.files; - - let savedIndex: StoreIndexEntity; - - if(update) { - const updateResult = await this.storeRepo.update({ - gameBuild: this.store.gameBuild - }, storeIndex); - - if(!updateResult?.affected) { - logger.error(`Main store entity update failed.`); - return null; - } - - savedIndex = await this.getStoreIndex(); - } else { - savedIndex = await this.storeRepo.save(storeIndex); - } - - if(savedIndex?.gameBuild !== this.store.gameBuild) { - logger.error(`Error saving store index ${this.store.gameBuild}.`); - return null; - } - - logger.info(`Store index ${this.store.gameBuild} saved.`); - - return savedIndex; - } - - public async getArchiveIndex(archive: Archive): Promise; - public async getArchiveIndex(archiveKey: number): Promise; - public async getArchiveIndex(archive: Archive | number): Promise { - const key = typeof archive === 'number' ? archive : archive.numericKey; - return await this.archiveRepo.findOne({ - where: { - key, gameBuild: this.store.gameBuild - }, - relations: [ 'groups' ] - }) || null; - } - - public async getArchiveIndexes(): Promise { - return await this.archiveRepo.find({ - where: { - gameBuild: this.store.gameBuild - }, - order: { - key: 'ASC' - } - }) || []; - } - - public validateArchive(archive: Archive | Partial): ArchiveIndexEntity { - const archiveIndex: ArchiveIndexEntity = archive.index ? archive.index : new ArchiveIndexEntity(); - if(!archive.index) { - archive.index = archiveIndex; - } - - this.updateEntityIndex(archive); - - archiveIndex.format = archiveIndex.format || ArchiveFormat.original; - archiveIndex.gameBuild = this.store.gameBuild; - archiveIndex.state = archive.state || FileState.unloaded; - archiveIndex.groupCount = archive.groups?.size ?? 0; - - return archiveIndex; - } - - public async saveArchiveIndex(archive: Archive): Promise { - const archiveIndex = archive.index; - const existingIndex = await this.archiveRepo.findOne({ - where: { - key: archiveIndex.key, - gameBuild: this.store.gameBuild - } - }); - - let affected; - - if(existingIndex) { - const { name, size, sha256, crc32, data, state } = archiveIndex; - existingIndex.name = name; - existingIndex.size = size; - existingIndex.sha256 = sha256; - existingIndex.crc32 = crc32; - existingIndex.data = data; - existingIndex.state = state; - - delete existingIndex.groups; - - const result = await this.archiveRepo.update({ - key: archiveIndex.key, - gameBuild: this.store.gameBuild - }, existingIndex); - - affected = result?.affected || 0; - } else { - delete archiveIndex.groups; - - const result = await this.archiveRepo.insert(archiveIndex); - affected = result?.identifiers?.length || 0; - } - - if(!affected) { - logger.error(`Error updating archive ${archiveIndex.name} database index.`); - } else { - logger.info(`Archive ${archiveIndex.name} database index saved.`); - } - - return await this.getArchiveIndex(archiveIndex.key); - } - - public async getGroupIndex(group: Group): Promise; - public async getGroupIndex(groupKey: number, archiveKey: number): Promise; - public async getGroupIndex(group: Group | number, archive?: number): Promise { - const key = typeof group === 'number' ? group : group.numericKey; - const archiveKey = typeof group === 'number' ? archive : group.archive.numericKey; - - return await this.groupRepo.findOne({ - where: { - key, archiveKey, - gameBuild: this.store.gameBuild - } - }) || null; - } - - public async getGroupIndexes(archive: ArchiveIndexEntity): Promise { - return await this.groupRepo.find({ - where: { - archiveKey: archive.key, - gameBuild: this.store.gameBuild - }, - order: { - key: 'ASC' - } - }) || []; - } - - public validateGroup(group: Group | Partial): GroupIndexEntity { - const { stripes, archive, files } = group; - - const groupIndex: GroupIndexEntity = group.index ? group.index : new GroupIndexEntity(); - if(!group.index) { - group.index = groupIndex; - } - - this.updateEntityIndex(group); - - groupIndex.gameBuild = this.store.gameBuild; - groupIndex.archiveKey = archive.numericKey; - groupIndex.state = group.state; - groupIndex.stripes = stripes?.length ? stripes.join(',') : null; - groupIndex.stripeCount = stripes?.length || 1; - groupIndex.flatFile = (files?.size === 1 || archive.config.flatten); - - return groupIndex; - } - - public async saveGroupIndex(group: Group): Promise { - if(!this.entityModified(group)) { - return; - } - await this.groupRepo.upsert(this.validateGroup(group), []); - } - - public async saveGroupIndexes(groups: Group[]): Promise { - const groupIndexes = groups.filter(group => this.entityModified(group)) - .map(group => this.validateGroup(group)); - - if(!groupIndexes.length) { - logger.info(`No groups were modified.`); - } else { - await this.groupRepo.upsert(groupIndexes, []); - } - } - - public async getFileIndex(file: FlatFile): Promise { - return await this.fileRepo.findOne({ - where: { - key: file.numericKey, - groupKey: file.group.numericKey, - archiveKey: file.archive.numericKey, - gameBuild: this.store.gameBuild - } - }) || null; - } - - public async getFileIndexes(archiveOrGroup: ArchiveIndexEntity | GroupIndexEntity): Promise { - if(archiveOrGroup instanceof ArchiveIndexEntity) { - // Return all files for the specified archive - - return await this.fileRepo.find({ - where: { - archiveKey: archiveOrGroup.key, - gameBuild: this.store.gameBuild - }, - order: { - groupKey: 'ASC', - key: 'ASC' - } - }) || []; - } else { - // Return all files for the specified group - - return await this.fileRepo.find({ - where: { - groupKey: archiveOrGroup.key, - archiveKey: archiveOrGroup.archiveKey, - gameBuild: this.store.gameBuild - }, - order: { - key: 'ASC' - } - }) || []; - } - } - - public validateFile(file: FlatFile | Partial): FileIndexEntity { - const { size, stripes, group, archive } = file; - - const fileIndex: FileIndexEntity = file.index ? file.index : new FileIndexEntity(); - if(!file.index) { - file.index = fileIndex; - } - - this.updateEntityIndex(file); - - fileIndex.gameBuild = this.store.gameBuild; - fileIndex.store = this.store.index; - fileIndex.archiveKey = archive.numericKey; - fileIndex.archive = archive.index; - fileIndex.groupKey = group.numericKey; - fileIndex.group = group.index; - fileIndex.stripes = stripes?.join(',') || String(size); - fileIndex.stripeCount = fileIndex.stripes?.length || 1; - - return fileIndex; - } - - public async saveFileIndex(file: FlatFile): Promise { - if(!this.entityModified(file)) { - return; - } - await this.fileRepo.upsert(this.validateFile(file), []); - } - - public async saveFileIndexes(files: FlatFile[]): Promise { - const flatFileIndexes = files.filter(file => this.entityModified(file)) - .map(file => this.validateFile(file)); - - if(!flatFileIndexes.length) { - logger.info(`No flat files were modified.`); - } else { - await this.fileRepo.upsert(flatFileIndexes, []); - } - } - - public entityModified(file: IndexedFile | Partial>): boolean { - const index = file.index; - - if(file.numericKey !== index.key || file.name !== index.name) { - return true; - } - - if((index instanceof GroupIndexEntity || index instanceof FileIndexEntity) && - (file instanceof Group || file instanceof FlatFile)) { - if(file.nameHash !== index.nameHash || file.version !== index.version) { - return true; - } - - if(file.archive.numericKey !== index.archiveKey) { - return true; - } - } - - if((index instanceof ArchiveIndexEntity || index instanceof GroupIndexEntity) && - (file instanceof Archive || file instanceof Group)) { - if(file.state !== index.state) { - return true; - } - } - - if(index instanceof FileIndexEntity && file instanceof FlatFile) { - if(file.group.numericKey !== index.groupKey) { - return true; - } - } - - return file.size !== index.size || file.crc32 !== index.crc32 || file.sha256 !== index.sha256; - } - - public updateEntityIndex(file: IndexedFile | Partial>): T { - const index = file.index; - - if(!file.name && file.hasNameHash) { - file.name = file.hasNameHash ? - this.store.findFileName(file.nameHash, String(file.nameHash)) : file.key; - } else if(!file.hasNameHash && file.named) { - file.nameHash = this.store.hashFileName(file.name); - } else { - file.nameHash = -1; - } - - if(index instanceof GroupIndexEntity || index instanceof FileIndexEntity) { - index.version = file.version; - - if(file.archive?.config?.versioned && file.modified) { - index.version = index.version ? index.version + 1 : 1; - } - } - - if(index.key === undefined || index.key === null) { - index.key = file.numericKey; - } - - if(index.name !== file.name) { - index.name = file.name; - } - - let dataModified = false; - - if(index.size !== file.size) { - index.size = file.size; - dataModified = true; - } - - if(index.crc32 !== file.crc32) { - index.crc32 = file.crc32; - dataModified = true; - } - - if(index.sha256 !== file.sha256) { - index.sha256 = file.sha256; - dataModified = true; - } - - if(dataModified || !index.data?.length) { - index.data = file.data?.length ? Buffer.from(file.data) : null; - } - - return index; - } - - public get loaded(): boolean { - return !!this.connection; - } - - public get storeRepo(): Repository { - return this.connection.getRepository(StoreIndexEntity); - } - - public get archiveRepo(): Repository { - return this.connection.getRepository(ArchiveIndexEntity); - } - - public get groupRepo(): Repository { - return this.connection.getRepository(GroupIndexEntity); - } - - public get fileRepo(): Repository { - return this.connection.getRepository(FileIndexEntity); - } - -} diff --git a/src/db/index.ts b/src/db/index.ts deleted file mode 100644 index 5c19b78..0000000 --- a/src/db/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './store-index.entity'; -export * from './archive-index.entity'; -export * from './file-index.entity'; -export * from './group-index.entity'; -export * from './index-entity'; -export * from './index-service'; diff --git a/src/db/jag/content/jag-game-interface-entity.ts b/src/db/jag/content/jag-game-interface-entity.ts new file mode 100644 index 0000000..8080683 --- /dev/null +++ b/src/db/jag/content/jag-game-interface-entity.ts @@ -0,0 +1,174 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + + +@Entity('jag_game_interface') +export class JagGameInterfaceEntity { + + @PrimaryColumn('integer', { nullable: false, unique: true }) + id: number; + + @Column('integer', { name: 'parent_id', nullable: false, default: -1 }) + parentId: number = -1; + + @Column('integer', { nullable: false }) + type: number; + + @Column('integer', { nullable: false }) + actionType: number; + + @Column('integer', { nullable: false }) + contentType: number; + + @Column('integer', { nullable: false }) + width: number; + + @Column('integer', { nullable: false }) + height: number; + + @Column('integer', { nullable: false }) + alpha: number; + + @Column('integer', { nullable: false }) + hoveredPopup: number; + + @Column('simple-json', { nullable: true }) + conditionTypes?: number[]; + + @Column('simple-json', { nullable: true }) + conditionValues?: number[]; + + @Column('simple-json', { nullable: true }) + cs1Opcodes?: number[][]; + + @Column('integer', { nullable: true }) + scrollLimit?: number; + + @Column('boolean', { nullable: true }) + hiddenUntilHovered?: boolean; + + @Column('simple-json', { nullable: true }) + children?: number[]; + + @Column('simple-json', { nullable: true }) + childrenX?: number[]; + + @Column('simple-json', { nullable: true }) + childrenY?: number[]; + + @Column('integer', { nullable: true }) + unknownServerAttribute1?: number; + + @Column('boolean', { nullable: true }) + unknownServerAttribute2?: boolean; + + @Column('simple-json', { nullable: true }) + items?: number[]; + + @Column('simple-json', { nullable: true }) + itemAmounts?: number[]; + + @Column('boolean', { nullable: true }) + itemsSwappable?: boolean; + + @Column('boolean', { nullable: true }) + isInventory?: boolean; + + @Column('boolean', { nullable: true }) + itemsUsable?: boolean; + + @Column('boolean', { nullable: true }) + deleteDraggedItems?: boolean; + + @Column('integer', { nullable: true }) + itemSpritesPadX?: number; + + @Column('integer', { nullable: true }) + itemSpritesPadY?: number; + + @Column('simple-json', { nullable: true }) + images?: string[]; + + @Column('simple-json', { nullable: true }) + imagesX?: number[]; + + @Column('simple-json', { nullable: true }) + imagesY?: number[]; + + @Column('simple-json', { nullable: true }) + options?: string[]; + + @Column('boolean', { nullable: true }) + filled?: boolean; + + @Column('boolean', { nullable: true }) + textCentered?: boolean; + + @Column('integer', { nullable: true }) + fontType?: number; + + @Column('boolean', { nullable: true }) + textShadowed?: boolean; + + @Column('text', { nullable: true }) + disabledText?: string; + + @Column('text', { nullable: true }) + enabledText?: string; + + @Column('integer', { nullable: true }) + disabledColor?: number; + + @Column('integer', { nullable: true }) + enabledColor?: number; + + @Column('integer', { nullable: true }) + disabledHoverColor?: number; + + @Column('integer', { nullable: true }) + enabledHoverColor?: number; + + @Column('text', { nullable: true }) + disabledImage?: string; + + @Column('text', { nullable: true }) + enabledImage?: string; + + @Column('integer', { nullable: true }) + disabledModelType?: number; + + @Column('integer', { nullable: true }) + disabledModelId?: number; + + @Column('integer', { nullable: true }) + enabledModelType?: number; + + @Column('integer', { nullable: true }) + enabledModelId?: number; + + @Column('integer', { nullable: true }) + disabledAnimationId?: number; + + @Column('integer', { nullable: true }) + enabledAnimationId?: number; + + @Column('integer', { nullable: true }) + modelZoom?: number; + + @Column('integer', { nullable: true }) + modelRotationX?: number; + + @Column('integer', { nullable: true }) + modelRotationY?: number; + + @Column('text', { nullable: true }) + actionAdditions?: string; + + @Column('text', { nullable: true }) + actionText?: string; + + @Column('integer', { nullable: true }) + actionAttributes?: number; + + @Column('text', { nullable: true }) + tooltip?: string; +} diff --git a/src/db/jag/index.ts b/src/db/jag/index.ts new file mode 100644 index 0000000..f9bde8e --- /dev/null +++ b/src/db/jag/index.ts @@ -0,0 +1,4 @@ +export * from './jag-database'; +export * from './jag-index-entity'; +export * from './jag-data-entity'; +export * from './content/jag-game-interface-entity'; diff --git a/src/db/jag/jag-data-entity.ts b/src/db/jag/jag-data-entity.ts new file mode 100644 index 0000000..23f2890 --- /dev/null +++ b/src/db/jag/jag-data-entity.ts @@ -0,0 +1,39 @@ +import { Column, CreateDateColumn, Entity, Index, PrimaryColumn, UpdateDateColumn } from 'typeorm'; +import { JagFileType } from '../../config'; +import { Buffer } from 'buffer'; + + +@Entity('jag_data') +@Index('data_identifier', [ + 'fileType', 'gameBuild', 'key', 'cacheKey', 'archiveKey', 'compressed' +], { unique: true }) +export class JagDataEntity { + + @PrimaryColumn('text', { name: 'file_type', nullable: false, unique: false }) + fileType: JagFileType; + + @PrimaryColumn('text', { name: 'game_build', nullable: false, unique: false }) + gameBuild: string; + + @PrimaryColumn('integer', { nullable: false, unique: false }) + key: number; + + @PrimaryColumn('integer', { name: 'cache_key', nullable: false, unique: false }) + cacheKey: number; + + @PrimaryColumn('integer', { name: 'archive_key', nullable: false, unique: false, default: -1 }) + archiveKey: number = -1; + + @PrimaryColumn('boolean', { nullable: false, default: false }) + compressed: boolean = false; + + @Column('blob', { name: 'buffer', nullable: true, default: null }) + buffer: Buffer = null; + + @CreateDateColumn() + created?: Date; + + @UpdateDateColumn() + updated?: Date; + +} diff --git a/src/db/jag/jag-database.ts b/src/db/jag/jag-database.ts new file mode 100644 index 0000000..25bf72d --- /dev/null +++ b/src/db/jag/jag-database.ts @@ -0,0 +1,184 @@ +import { IndexDatabase } from '../index-database'; +import { JagIndexEntity } from './jag-index-entity'; +import { Connection, createConnection, LoggerOptions, Repository } from 'typeorm'; +import { JagFileType } from '../../config'; +import { JagGameInterfaceEntity } from './content/jag-game-interface-entity'; +import { existsSync, mkdirSync } from 'graceful-fs'; +import { join } from 'path'; +import { JagDataEntity } from './jag-data-entity'; + + +export interface JagIndexEntityWhere { + fileType?: JagFileType; + key?: number; + name?: string; + cacheKey?: number; + archiveKey?: number; +} + + +export class JagDatabase extends IndexDatabase { + + private _interfaceRepo: Repository; + private _dataRepo: Repository; + + constructor( + gameBuild: string, + databasePath: string, + loggerOptions: LoggerOptions = 'all' + ) { + super(gameBuild, databasePath, loggerOptions); + } + + override async openConnection(): Promise { + if(!existsSync(this.databasePath)) { + mkdirSync(this.databasePath, { recursive: true }); + } + + this._connection = await createConnection({ + type: 'better-sqlite3', + database: join(this.databasePath, `${this.gameBuild}.index.sqlite3`), + entities: [ + JagIndexEntity, + JagDataEntity, + JagGameInterfaceEntity + ], + synchronize: true, + logging: this.loggerOptions, + name: 'jag-repository' + }); + + this._repository = this._connection.getRepository(JagIndexEntity); + this._interfaceRepo = this._connection.getRepository(JagGameInterfaceEntity); + this._dataRepo = this._connection.getRepository(JagDataEntity); + + return this._connection; + } + + async getUncompressedData(where: JagIndexEntityWhere): Promise { + return this._dataRepo.findOne({ + where: { + gameBuild: this.gameBuild, + compressed: false, + ...where + } + }); + } + + async getAllUncompressedData(where: JagIndexEntityWhere): Promise { + return this._dataRepo.find({ + where: { + gameBuild: this.gameBuild, + compressed: false, + ...where + } + }); + } + + async saveUncompressedData(uncompressedDataEntity: JagDataEntity): Promise { + return this._dataRepo.save({ ...uncompressedDataEntity, compressed: false }); + } + + async saveAllUncompressedData(uncompressedDataEntities: JagDataEntity[]): Promise { + await this._dataRepo.save({ ...uncompressedDataEntities, compressed: false }, { + chunk: 500, + transaction: false, + reload: false, + listeners: false, + }); + } + + async upsertAllUncompressedData(uncompressedDataEntities: JagDataEntity[]): Promise { + const chunkSize = 100; + for (let i = 0; i < uncompressedDataEntities.length; i += chunkSize) { + const chunk = uncompressedDataEntities.slice(i, i + chunkSize).map(d => ({ ...d, compressed: false })); + await this._dataRepo.upsert(chunk, { + conflictPaths: [ 'fileType', 'gameBuild', 'key', 'cacheKey', 'archiveKey', 'compressed' ], + skipUpdateIfNoValuesChanged: true, + }); + } + } + + async getCompressedData(where: JagIndexEntityWhere): Promise { + return this._dataRepo.findOne({ + where: { + gameBuild: this.gameBuild, + compressed: true, + ...where + } + }); + } + + async getAllCompressedData(where: JagIndexEntityWhere): Promise { + return this._dataRepo.find({ + where: { + gameBuild: this.gameBuild, + compressed: true, + ...where + } + }); + } + + async saveCompressedData(compressedDataEntity: JagDataEntity): Promise { + return this._dataRepo.save({ ...compressedDataEntity, compressed: true }); + } + + async saveAllCompressedData(compressedDataEntities: JagDataEntity[]): Promise { + await this._dataRepo.save({ ...compressedDataEntities, compressed: true }, { + chunk: 500, + transaction: false, + reload: false, + listeners: false, + }); + } + + async upsertAllCompressedData(compressedDataEntities: JagDataEntity[]): Promise { + const chunkSize = 100; + for (let i = 0; i < compressedDataEntities.length; i += chunkSize) { + const chunk = compressedDataEntities.slice(i, i + chunkSize).map(d => ({ ...d, compressed: true })); + await this._dataRepo.upsert(chunk, { + conflictPaths: [ 'fileType', 'gameBuild', 'key', 'cacheKey', 'archiveKey', 'compressed' ], + skipUpdateIfNoValuesChanged: true, + }); + } + } + + override async upsertIndexes(indexEntities: JagIndexEntity[]): Promise { + const chunkSize = 100; + for (let i = 0; i < indexEntities.length; i += chunkSize) { + const chunk = indexEntities.slice(i, i + chunkSize); + await this.repository.upsert(chunk, { + conflictPaths: [ 'fileType', 'gameBuild', 'key', 'cacheKey', 'archiveKey' ], + skipUpdateIfNoValuesChanged: true, + }); + } + } + + async saveInterfaces(entities: JagGameInterfaceEntity[]): Promise { + await this.interfaceRepo.save(entities, { + reload: false, + listeners: false, + transaction: false, + chunk: 100, + }); + } + + async saveInterface(entity: JagGameInterfaceEntity): Promise { + return await this.interfaceRepo.save(entity); + } + + async getInterface(id: number): Promise { + return await this.interfaceRepo.findOne({ + where: { id } + }); + } + + get interfaceRepo(): Repository { + return this._interfaceRepo; + } + + get dataRepo(): Repository { + return this._dataRepo; + } + +} diff --git a/src/db/jag/jag-index-entity.ts b/src/db/jag/jag-index-entity.ts new file mode 100644 index 0000000..2bfb18e --- /dev/null +++ b/src/db/jag/jag-index-entity.ts @@ -0,0 +1,69 @@ +import { Column, CreateDateColumn, Entity, Index, PrimaryColumn, UpdateDateColumn } from 'typeorm'; +import { CompressionMethod } from '@runejs/common/compress'; +import { FileError, JagFileType } from '../../config'; + + +@Entity('jag_index') +@Index('index_identifier', [ + 'fileType', 'gameBuild', 'key', 'cacheKey', 'archiveKey' +], { unique: true }) +export class JagIndexEntity { + + @PrimaryColumn('text', { name: 'file_type', nullable: false, unique: false }) + fileType: JagFileType; + + @PrimaryColumn('text', { name: 'game_build', nullable: false, unique: false }) + gameBuild: string; + + @PrimaryColumn('integer', { nullable: false, unique: false }) + key: number; + + @PrimaryColumn('integer', { name: 'cache_key', nullable: false, unique: false }) + cacheKey: number; + + @PrimaryColumn('integer', { name: 'archive_key', nullable: false, unique: false, default: -1 }) + archiveKey: number = -1; + + @Column('text', { nullable: true, default: null }) + name: string = null; + + @Column('integer', { name: 'name_hash', nullable: true, default: -1 }) + nameHash: number = -1; + + @Column('integer', { nullable: false, default: -1 }) + version: number = -1; + + @Column('integer', { name: 'child_count', nullable: false, default: 0 }) + childCount: number = 0; + + @Column('integer', { nullable: false, default: -1 }) + checksum: number = -1; + + @Column('text', { name: 'sha_digest', nullable: true, default: null }) + shaDigest: string = null; + + @Column('integer', { name: 'file_size', nullable: false, default: 0 }) + fileSize: number = 0; + + @Column('text', { name: 'compression_method', nullable: true, default: 'none' }) + compressionMethod: CompressionMethod = 'none'; + + @Column('integer', { name: 'compressed_checksum', nullable: false, default: -1 }) + compressedChecksum: number = -1; + + @Column('text', { name: 'compressed_sha_digest', nullable: true, default: null }) + compressedShaDigest: string = null; + + @Column('integer', { name: 'compressed_file_size', nullable: false, default: 0 }) + compressedFileSize: number = 0; + + @Column('text', { name: 'file_error', nullable: true, default: null }) + fileError: FileError = null; + + @CreateDateColumn() + created?: Date; + + @UpdateDateColumn() + updated?: Date; + +} diff --git a/src/db/js5/index.ts b/src/db/js5/index.ts new file mode 100644 index 0000000..010d69c --- /dev/null +++ b/src/db/js5/index.ts @@ -0,0 +1,3 @@ +export * from './js5-database'; +export * from './js5-index-entity'; +export * from './js5-data-entity'; diff --git a/src/db/js5/js5-data-entity.ts b/src/db/js5/js5-data-entity.ts new file mode 100644 index 0000000..c770f4a --- /dev/null +++ b/src/db/js5/js5-data-entity.ts @@ -0,0 +1,39 @@ +import { Column, CreateDateColumn, Entity, Index, PrimaryColumn, UpdateDateColumn } from 'typeorm'; +import { Js5FileType } from '../../config'; +import { Buffer } from 'buffer'; + + +@Entity('js5_data') +@Index('data_identifier', [ + 'fileType', 'gameBuild', 'key', 'archiveKey', 'groupKey', 'compressed' +], { unique: true }) +export class Js5DataEntity { + + @PrimaryColumn('text', { name: 'file_type', nullable: false, unique: false }) + fileType: Js5FileType; + + @PrimaryColumn('text', { name: 'game_build', nullable: false, unique: false }) + gameBuild: string; + + @PrimaryColumn('integer', { nullable: false, unique: false }) + key: number; + + @PrimaryColumn('integer', { name: 'archive_key', nullable: false, unique: false, default: -1 }) + archiveKey: number = -1; + + @PrimaryColumn('integer', { name: 'group_key', nullable: false, unique: false, default: -1 }) + groupKey: number = -1; + + @PrimaryColumn('boolean', { nullable: false, default: false }) + compressed: boolean = false; + + @Column('blob', { name: 'buffer', nullable: true, default: null }) + buffer: Buffer = null; + + @CreateDateColumn() + created?: Date; + + @UpdateDateColumn() + updated?: Date; + +} diff --git a/src/db/js5/js5-database.ts b/src/db/js5/js5-database.ts new file mode 100644 index 0000000..f1fc5d6 --- /dev/null +++ b/src/db/js5/js5-database.ts @@ -0,0 +1,154 @@ +import { join } from 'path'; +import { existsSync, mkdirSync } from 'graceful-fs'; +import { Connection, createConnection, LoggerOptions, Repository } from 'typeorm'; +import { IndexDatabase } from '../index-database'; +import { Js5IndexEntity } from './js5-index-entity'; +import { Js5FileType } from '../../config'; +import { Js5DataEntity } from './js5-data-entity'; + + +export interface Js5IndexEntityWhere { + fileType?: Js5FileType; + key?: number; + name?: string; + archiveKey?: number; + groupKey?: number; +} + + +export class Js5Database extends IndexDatabase { + + private _dataRepo: Repository; + + constructor( + gameBuild: string, + databasePath: string, + loggerOptions: LoggerOptions = 'all' + ) { + super(gameBuild, databasePath, loggerOptions); + } + + override async openConnection(): Promise { + if(!existsSync(this.databasePath)) { + mkdirSync(this.databasePath, { recursive: true }); + } + + this._connection = await createConnection({ + type: 'better-sqlite3', + database: join(this.databasePath, `${this.gameBuild}.index.sqlite3`), + entities: [ Js5IndexEntity, Js5DataEntity ], + synchronize: true, + logging: this.loggerOptions, + name: 'js5-repository' + }); + + this._repository = this._connection.getRepository(Js5IndexEntity); + this._dataRepo = this._connection.getRepository(Js5DataEntity); + + return this._connection; + } + + async getUncompressedData(where: Js5IndexEntityWhere): Promise { + return this._dataRepo.findOne({ + where: { + gameBuild: this.gameBuild, + compressed: false, + ...where + } + }); + } + + async getAllUncompressedData(where: Js5IndexEntityWhere): Promise { + return this._dataRepo.find({ + where: { + gameBuild: this.gameBuild, + compressed: false, + ...where + } + }); + } + + async saveUncompressedData(uncompressedDataEntity: Js5DataEntity): Promise { + return this._dataRepo.save({ ...uncompressedDataEntity, compressed: false }); + } + + async saveAllUncompressedData(uncompressedDataEntities: Js5DataEntity[]): Promise { + await this._dataRepo.save({ ...uncompressedDataEntities, compressed: false }, { + chunk: 500, + transaction: false, + reload: false, + listeners: false, + }); + } + + async upsertAllUncompressedData(uncompressedDataEntities: Js5DataEntity[]): Promise { + const chunkSize = 100; + for (let i = 0; i < uncompressedDataEntities.length; i += chunkSize) { + const chunk = uncompressedDataEntities.slice(i, i + chunkSize).map(d => ({ ...d, compressed: false })); + await this._dataRepo.upsert(chunk, { + conflictPaths: [ 'fileType', 'gameBuild', 'key', 'archiveKey', 'groupKey', 'compressed' ], + skipUpdateIfNoValuesChanged: true, + }); + } + } + + async getCompressedData(where: Js5IndexEntityWhere): Promise { + return this._dataRepo.findOne({ + where: { + gameBuild: this.gameBuild, + compressed: true, + ...where + } + }); + } + + async getAllCompressedData(where: Js5IndexEntityWhere): Promise { + return this._dataRepo.find({ + where: { + gameBuild: this.gameBuild, + compressed: true, + ...where + } + }); + } + + async saveCompressedData(compressedDataEntity: Js5DataEntity): Promise { + return this._dataRepo.save({ ...compressedDataEntity, compressed: true }); + } + + async saveAllCompressedData(compressedDataEntities: Js5DataEntity[]): Promise { + await this._dataRepo.save({ ...compressedDataEntities, compressed: true }, { + chunk: 500, + transaction: false, + reload: false, + listeners: false, + }); + } + + async upsertAllCompressedData(compressedDataEntities: Js5DataEntity[]): Promise { + const chunkSize = 100; + for (let i = 0; i < compressedDataEntities.length; i += chunkSize) { + const chunk = compressedDataEntities.slice(i, i + chunkSize).map(d => ({ ...d, compressed: true })); + await this._dataRepo.upsert(chunk, { + conflictPaths: [ 'fileType', 'gameBuild', 'key', 'archiveKey', 'groupKey', 'compressed' ], + skipUpdateIfNoValuesChanged: true, + }); + } + } + + override async upsertIndexes(indexEntities: Js5IndexEntity[]): Promise { + const chunkSize = 100; + for (let i = 0; i < indexEntities.length; i += chunkSize) { + const chunk = indexEntities.slice(i, i + chunkSize); + await this.repository.upsert(chunk, { + conflictPaths: [ 'fileType', 'gameBuild', 'key', 'archiveKey', 'groupKey' ], + skipUpdateIfNoValuesChanged: true, + }); + } + } + + get dataRepo(): Repository { + return this._dataRepo; + } + +} diff --git a/src/db/js5/js5-index-entity.ts b/src/db/js5/js5-index-entity.ts new file mode 100644 index 0000000..db3bf9b --- /dev/null +++ b/src/db/js5/js5-index-entity.ts @@ -0,0 +1,85 @@ +import { Column, CreateDateColumn, Entity, Index, PrimaryColumn, UpdateDateColumn } from 'typeorm'; +import { CompressionMethod } from '@runejs/common/compress'; +import { FileError, Js5FileType } from '../../config'; +import { Buffer } from 'buffer'; + + +@Entity('js5_index') +@Index('index_identifier', [ + 'fileType', 'gameBuild', 'key', 'archiveKey', 'groupKey' +], { unique: true }) +export class Js5IndexEntity { + + @PrimaryColumn('text', { name: 'file_type', nullable: false, unique: false }) + fileType: Js5FileType; + + @PrimaryColumn('text', { name: 'game_build', nullable: false, unique: false }) + gameBuild: string; + + @PrimaryColumn('integer', { nullable: false, unique: false }) + key: number; + + @PrimaryColumn('integer', { name: 'archive_key', nullable: false, unique: false, default: -1 }) + archiveKey: number = -1; + + @PrimaryColumn('integer', { name: 'group_key', nullable: false, unique: false, default: -1 }) + groupKey: number = -1; + + @Column('text', { nullable: true, default: null }) + name: string = null; + + @Column('integer', { name: 'name_hash', nullable: true, default: -1 }) + nameHash: number = -1; + + @Column('integer', { nullable: false, default: -1 }) + version: number = -1; + + @Column('integer', { name: 'child_count', nullable: false, default: 0 }) + childCount: number = 0; + + @Column('integer', { nullable: false, default: -1 }) + checksum: number = -1; + + @Column('text', { name: 'sha_digest', nullable: true, default: null }) + shaDigest: string = null; + + @Column('blob', { name: 'whirlpool_digest', nullable: true, default: null }) + whirlpoolDigest: Buffer = null; + + @Column('integer', { name: 'file_size', nullable: false, default: 0 }) + fileSize: number = 0; + + @Column('text', { name: 'compression_method', nullable: true, default: 'none' }) + compressionMethod: CompressionMethod = 'none'; + + @Column('integer', { name: 'compressed_checksum', nullable: false, default: -1 }) + compressedChecksum: number = -1; + + @Column('text', { name: 'compressed_sha_digest', nullable: true, default: null }) + compressedShaDigest: string = null; + + @Column('integer', { name: 'compressed_file_size', nullable: false, default: 0 }) + compressedFileSize: number = 0; + + @Column('boolean', { nullable: true, default: false }) + encrypted: boolean = false; + + @Column('integer', { name: 'stripe_count', nullable: true, default: null }) + stripeCount: number = null; + + @Column('text', { nullable: true, default: null }) + stripes: string | null = null; + + @Column('integer', { nullable: true, default: null }) + archiveFormat: number = null; + + @Column('text', { name: 'file_error', nullable: true, default: null }) + fileError: FileError = null; + + @CreateDateColumn() + created?: Date; + + @UpdateDateColumn() + updated?: Date; + +} diff --git a/src/db/store-index.entity.ts b/src/db/store-index.entity.ts deleted file mode 100644 index 94541ba..0000000 --- a/src/db/store-index.entity.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Column, CreateDateColumn, Entity, OneToMany, PrimaryColumn, UpdateDateColumn } from 'typeorm'; - -import { ArchiveIndexEntity } from './archive-index.entity'; -import { GroupIndexEntity } from './group-index.entity'; -import { FileIndexEntity } from './file-index.entity'; - - -@Entity('store_index') -export class StoreIndexEntity { - - @PrimaryColumn('text', { name: 'game_build', nullable: false, unique: true }) - gameBuild: string; - - @OneToMany(() => ArchiveIndexEntity, archive => archive.store, { lazy: true }) - archives: Promise | ArchiveIndexEntity[]; - - @OneToMany(() => GroupIndexEntity, group => group.store, { lazy: true }) - groups: Promise | GroupIndexEntity[]; - - @OneToMany(() => FileIndexEntity, file => file.store, { lazy: true }) - files: Promise | FileIndexEntity[]; - - @Column('blob', { name: 'data', nullable: true, default: null }) - data: Buffer | null = null; - - @CreateDateColumn() - created: Date; - - @UpdateDateColumn() - updated: Date; - -} diff --git a/src/dev.ts b/src/dev.ts deleted file mode 100644 index 244f61f..0000000 --- a/src/dev.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Store } from './index'; -import { join } from 'path'; - - -const store = Store.create(435); - diff --git a/src/file-state.ts b/src/file-state.ts deleted file mode 100644 index 4f96e8e..0000000 --- a/src/file-state.ts +++ /dev/null @@ -1,54 +0,0 @@ -export enum FileState { - /** - * File is not yet registered. - */ - unloaded = 'unloaded', - - /** - * File has been registered but not read into memory. - */ - loaded = 'loaded', - - /** - * File `data` is encrypted.
- * Encryption formats: xtea - */ - encrypted = 'encrypted', - - /** - * File `data` has been decrypted.
- * Encryption formats: xtea - */ - decrypted = 'decrypted', - - /** - * File `data` is compressed.
- * Compression formats: bzip, gzip - */ - compressed = 'compressed', - - /** - * File `data` is encoded for the JS5 format. - */ - encoded = 'encoded', - - /** - * File `data` is in raw binary form, uncompressed and unencrypted. - */ - raw = 'raw', - - /** - * The file was found, but is empty. - */ - empty = 'empty', - - /** - * The file was found, but is corrupted. - */ - corrupt = 'corrupt', - - /** - * The file was not found. - */ - missing = 'missing' -} diff --git a/src/file-system/file-store-base.ts b/src/file-system/file-store-base.ts new file mode 100644 index 0000000..850ef49 --- /dev/null +++ b/src/file-system/file-store-base.ts @@ -0,0 +1,35 @@ +import { join } from 'path'; +import { Crc32 } from '@runejs/common/crc32'; + +import { NameHasher } from '../config'; +import { IndexDatabase } from '../db/index-database'; + + +export abstract class FileStoreBase> { + + readonly gameBuild: string; + readonly fileStorePath: string; + readonly nameHasher: NameHasher; + + protected _database: DATABASE; + + protected constructor(gameBuild: string | number, storePath: string) { + this.gameBuild = String(gameBuild); + this.fileStorePath = storePath; + this.nameHasher = new NameHasher(join(storePath, 'config')); + Crc32.init(); + } + + abstract openDatabase(): Promise; + + abstract load(): void | Promise; + + async closeDatabase(): Promise { + await this._database.closeConnection(); + } + + get database(): DATABASE { + return this._database; + } + +} diff --git a/src/file-system/index.ts b/src/file-system/index.ts new file mode 100644 index 0000000..5802b7a --- /dev/null +++ b/src/file-system/index.ts @@ -0,0 +1,4 @@ +export * from './file-store-base'; +export * from './packed'; +export * from './jag'; +export * from './js5'; diff --git a/src/file-system/jag/content/animations/animations.ts b/src/file-system/jag/content/animations/animations.ts new file mode 100644 index 0000000..6cef9d4 --- /dev/null +++ b/src/file-system/jag/content/animations/animations.ts @@ -0,0 +1,98 @@ +import { JagFileStore } from '../../jag-file-store'; +import { Buffer } from 'buffer'; +import { ByteBuffer, logger } from '@runejs/common'; +import { JagCache } from '../../jag-cache'; + + +export interface AnimationFile { + key: number; + version: number; + checksum: number; + data?: Buffer; +} + + +export class Animations { + + readonly jagStore: JagFileStore; + readonly animations: Map; + readonly animationsIndex: JagCache; + + versionListDecoded: boolean = false; + + constructor(jagStore: JagFileStore) { + this.jagStore = jagStore; + this.animations = new Map(); + this.animationsIndex = this.jagStore.getCache('animations'); + } + + decodeVersionList(): void { + const archiveIndex = this.jagStore.getCache('archives'); + if (!archiveIndex) { + throw new Error(`Archive Index is not loaded!`); + } + + const versionListArchive = archiveIndex.getArchive('versionlist.jag'); + if (!versionListArchive) { + throw new Error(`versionlist.jag archive is not loaded!`); + } + + const animVersionList = versionListArchive.getFile('anim_version'); + const animChecksumList = versionListArchive.getFile('anim_crc'); + const animIndexList = versionListArchive.getFile('anim_index'); + + if (!animVersionList?.data?.buffer?.length) { + throw new Error(`anim_version file is not loaded!`); + } + if (!animChecksumList?.data?.buffer?.length) { + throw new Error(`anim_crc file is not loaded!`); + } + if (!animIndexList?.data?.buffer?.length) { + throw new Error(`anim_index file is not loaded!`); + } + + this.animations.clear(); + + const versionData = new ByteBuffer(animVersionList.data.buffer); + const checksumData = new ByteBuffer(animVersionList.data.buffer); + const indexData = new ByteBuffer(animVersionList.data.buffer); + const animCount = versionData.length / 2; + + for (let i = 0; i < animCount; i++) { + const version = versionData.get('short', 'unsigned'); + const checksum = checksumData.get('int'); + const key = indexData.get('short', 'unsigned'); + + this.animations.set(key, { + key, version, checksum + }); + } + + this.versionListDecoded = true; + } + + decodeAll(): void { + if (!this.versionListDecoded) { + this.decodeVersionList(); + } + + for (const [ animKey, ] of this.animationsIndex.files) { + this.decode(animKey); + } + } + + decode(animKey: number): AnimationFile | null { + const animFile = this.animationsIndex.getFile(animKey); + + if (!animFile?.data?.buffer?.length) { + logger.warn(`Animation ${animKey} is empty or missing.`); + return null; + } + + const animData = new ByteBuffer(animFile.data.buffer); + + //@todo stopped here - 12/08/22 - Kiko + return null; + } + +} diff --git a/src/file-system/jag/content/archives/interfaces/jag-interface-archive.ts b/src/file-system/jag/content/archives/interfaces/jag-interface-archive.ts new file mode 100644 index 0000000..37eff86 --- /dev/null +++ b/src/file-system/jag/content/archives/interfaces/jag-interface-archive.ts @@ -0,0 +1,278 @@ +import { Buffer } from 'buffer'; +import { ByteBuffer, logger } from '@runejs/common'; +import { JagFileStore } from '../../../jag-file-store'; +import { JagGameInterfaceEntity } from '../../../../../db/jag'; + + +export class JagInterfaceArchive { + + readonly jagStore: JagFileStore; + readonly interfaces: Map; + + constructor(jagStore: JagFileStore) { + this.jagStore = jagStore; + this.interfaces = new Map(); + } + + decode(data: ByteBuffer): JagGameInterfaceEntity { + const inter = new JagGameInterfaceEntity(); + inter.id = data.get('short', 'unsigned'); + + if (inter.id === 65535) { + inter.parentId = data.get('short', 'unsigned'); + inter.id = data.get('short', 'unsigned'); + } + + const type = inter.type = data.get('byte', 'unsigned'); + inter.actionType = data.get('byte', 'unsigned'); + inter.contentType = data.get('short', 'unsigned'); + const width = inter.width = data.get('short', 'unsigned'); + const height = inter.height = data.get('short', 'unsigned'); + inter.alpha = data.get('byte', 'unsigned'); + + // hoveredPopup = u_short, but only a single u_byte is written if there is no hovered popup + // use u_smart_short ? + inter.hoveredPopup = data.get('byte', 'unsigned'); + if (inter.hoveredPopup !== 0) { + inter.hoveredPopup = (inter.hoveredPopup - 1 << 8) + + data.get('byte', 'unsigned'); // why? + } else { + inter.hoveredPopup = -1; + } + + const conditionCount = data.get('byte', 'unsigned'); + + if (conditionCount > 0) { + inter.conditionTypes = new Array(conditionCount); + inter.conditionValues = new Array(conditionCount); + + for (let i = 0; i < conditionCount; i++) { + inter.conditionTypes[i] = data.get('byte', 'unsigned'); + inter.conditionValues[i] = data.get('short', 'unsigned'); + } + } + + const cs1OpcodeCount = data.get('byte', 'unsigned'); + + if (cs1OpcodeCount > 0) { + inter.cs1Opcodes = new Array(cs1OpcodeCount); + + for (let i = 0; i < cs1OpcodeCount; i++) { + const cs1BlockCount = data.get('short', 'unsigned'); + inter.cs1Opcodes[i] = new Array(cs1BlockCount); + + for (let j = 0; j < cs1BlockCount; j++) { + inter.cs1Opcodes[i][j] = data.get('short', 'unsigned'); + } + } + } + + if (type === 0) { + inter.scrollLimit = data.get('short', 'unsigned'); + inter.hiddenUntilHovered = data.get('byte', 'unsigned') === 1; + + const childCount = data.get('short', 'unsigned'); + + inter.children = new Array(childCount); + inter.childrenX = new Array(childCount); + inter.childrenY = new Array(childCount); + + for (let i = 0; i < childCount; i++) { + inter.children[i] = data.get('short', 'unsigned'); + inter.childrenX[i] = data.get('short'); + inter.childrenY[i] = data.get('short'); + } + } + + if (type === 1) { + inter.unknownServerAttribute1 = data.get('short', 'unsigned'); + inter.unknownServerAttribute2 = data.get('byte', 'unsigned') === 1; + } + + if (type === 2) { + inter.items = new Array(width * height); + inter.itemAmounts = new Array(width * height); + inter.itemsSwappable = data.get('byte', 'unsigned') === 1; + inter.isInventory = data.get('byte', 'unsigned') === 1; + inter.itemsUsable = data.get('byte', 'unsigned') === 1; + inter.deleteDraggedItems = data.get('byte', 'unsigned') === 1; + inter.itemSpritesPadX = data.get('byte', 'unsigned'); + inter.itemSpritesPadY = data.get('byte', 'unsigned'); + inter.images = new Array(20); + inter.imagesX = new Array(20); + inter.imagesY = new Array(20); + + for (let i = 0; i < 20; i++) { + const hasSprite = data.get('byte', 'unsigned') === 1; + if (hasSprite) { + inter.imagesX[i] = data.get('short'); + inter.imagesY[i] = data.get('short'); + inter.images[i] = data.getString(10); + } + } + + inter.options = new Array(5); + + for (let i = 0; i < 5; i++) { + inter.options[i] = data.getString(10); + } + } + + if (type === 3) { + inter.filled = data.get('byte', 'unsigned') === 1; + } + + if (type === 4 || type === 1) { + inter.textCentered = data.get('byte', 'unsigned') === 1; + inter.fontType = data.get('byte', 'unsigned'); + inter.textShadowed = data.get('byte', 'unsigned') === 1; + } + + if (type === 4) { + inter.disabledText = data.getString(10); + inter.enabledText = data.getString(10); + } + + if (inter.type === 1 || inter.type === 3 || inter.type === 4) { + inter.disabledColor = data.get('int'); + } + + if (inter.type === 3 || inter.type === 4) { + inter.enabledColor = data.get('int'); + inter.disabledHoverColor = data.get('int'); + inter.enabledHoverColor = data.get('int'); + } + + if (inter.type === 5) { + inter.disabledImage = data.getString(10); + inter.enabledImage = data.getString(10); + } + + if (inter.type === 6) { + let identifier = data.get('byte', 'unsigned'); + + if (identifier !== 0) { + inter.disabledModelType = 1; + inter.disabledModelId = (identifier - 1 << 8) + data.get('byte', 'unsigned'); + } + + identifier = data.get('byte', 'unsigned'); + + if (identifier !== 0) { + inter.enabledModelType = 1; + inter.enabledModelId = (identifier - 1 << 8) + data.get('byte', 'unsigned'); + } + + identifier = data.get('byte', 'unsigned'); + + if (identifier !== 0) { + inter.disabledAnimationId = (identifier - 1 << 8) + data.get('byte', 'unsigned'); + } else { + inter.disabledAnimationId = -1; + } + + identifier = data.get('byte', 'unsigned'); + + if (identifier !== 0) { + inter.enabledAnimationId = (identifier - 1 << 8) + data.get('byte', 'unsigned'); + } else { + inter.enabledAnimationId = -1; + } + + inter.modelZoom = data.get('short', 'unsigned'); + inter.modelRotationX = data.get('short', 'unsigned'); + inter.modelRotationY = data.get('short', 'unsigned'); + } + + if (inter.type === 7) { + inter.items = new Array(width * height); + inter.itemAmounts = new Array(width * height); + inter.textCentered = data.get('byte', 'unsigned') === 1; + inter.fontType = data.get('byte', 'unsigned'); + inter.textShadowed = data.get('byte', 'unsigned') === 1; + inter.disabledColor = data.get('int'); + inter.itemSpritesPadX = data.get('short'); + inter.itemSpritesPadY = data.get('short'); + inter.isInventory = data.get('byte', 'unsigned') === 1; + inter.options = new Array(5); + + for (let i = 0; i < 5; i++) { + inter.options[i] = data.getString(10); + } + } + + if (inter.type === 8) { + inter.disabledText = data.getString(10); + } + + if (inter.actionType === 2 || inter.type === 2) { + inter.actionAdditions = data.getString(10); + inter.actionText = data.getString(10); + inter.actionAttributes = data.get('short', 'unsigned'); + } + + if (inter.actionType === 1 || inter.actionType === 4 || inter.actionType === 5 || inter.actionType === 6) { + inter.tooltip = data.getString(10); + } + + return inter; + } + + async decodeAll(): Promise { + const archive = this.jagStore.getCache('archives') + .getArchive('interface.jag'); + + if (!archive) { + throw new Error('interface.jag archive is not loaded!'); + } + + const dataFile = archive.getFile('data'); + + await dataFile.loadUncompressedData(); + + if (!dataFile?.data?.buffer?.length) { + throw new Error('interface.jag data file is not loaded!'); + } + + const data = new ByteBuffer(dataFile.data.buffer); + this.interfaces.clear(); + + data.get('short', 'unsigned'); // interface count + + while (data.readerIndex < data.length) { + try { + const gameInterface = this.decode(data); + this.interfaces.set(gameInterface.id, gameInterface); + } catch (e) { + logger.error(e); + break; + } + } + } + + encode(gameInterface: JagGameInterfaceEntity): Buffer | null { + // @todo stubbed - 15/08/22 - Kiko + return null; + } + + encodeAll(): Buffer | null { + // @todo stubbed - 15/08/22 - Kiko + return null; + } + + toJS5(gameInterface: JagGameInterfaceEntity): null { + // @todo stubbed - 15/08/22 - Kiko + return null; + } + + async loadAll(): Promise { + const entities = (await this.jagStore.database.interfaceRepo.find()) + .sort((a, b) => a.id - b.id); + entities.forEach(entity => this.interfaces.set(entity.id, entity)); + } + + async saveAll(): Promise { + await this.jagStore.database.saveInterfaces(Array.from(this.interfaces.values())); + } + +} diff --git a/src/file-system/jag/index.ts b/src/file-system/jag/index.ts new file mode 100644 index 0000000..33001e8 --- /dev/null +++ b/src/file-system/jag/index.ts @@ -0,0 +1,6 @@ +export * from './jag'; +export * from './jag-file-base'; +export * from './jag-file-store'; +export * from './jag-archive'; +export * from './jag-file'; +export * from './jag-cache'; diff --git a/src/file-system/jag/jag-archive.ts b/src/file-system/jag/jag-archive.ts new file mode 100644 index 0000000..93bef94 --- /dev/null +++ b/src/file-system/jag/jag-archive.ts @@ -0,0 +1,81 @@ +import { JagFileStore } from './jag-file-store'; +import { archives, caches } from './jag'; +import { JagFile } from './jag-file'; +import { JagFileBase } from './jag-file-base'; + + +export class JagArchive extends JagFileBase { + + readonly files: Map; + + constructor( + jagStore: JagFileStore, + archiveKey: number, + ) { + super(jagStore, 'ARCHIVE', archiveKey, caches.archives, -1); + const archiveNames = Object.keys(archives); + for (const name of archiveNames) { + if (archives[name] === archiveKey) { + this.index.name = name; + } + } + this.files = new Map(); + } + + async upsertFileIndexes(): Promise { + const fileIndexes = Array.from(this.files.values()).map(file => file.index); + await this.fileStore.database.upsertIndexes(fileIndexes); + } + + async loadFileIndexes(): Promise { + const fileIndexes = await this.fileStore.database.getIndexes({ + fileType: 'FILE', + cacheKey: this.index.cacheKey, + archiveKey: this.index.key, + }); + + if (!fileIndexes?.length) { + return; + } + + for (const fileIndex of fileIndexes) { + const fileKey = fileIndex.key; + + if (!this.files.has(fileKey)) { + const file = new JagFile(this.fileStore, fileKey, this.index.cacheKey, this.index.key); + file.index = fileIndex; + this.files.set(fileKey, file); + } + } + } + + async upsertFileData(): Promise { + const files = Array.from(this.files.values()); + const uncompressed = files.map(file => file.data).filter(data => data?.buffer && data?.buffer?.length !== 0); + const compressed = files.map(file => file.compressedData).filter(data => data?.buffer && data?.buffer?.length !== 0); + if (uncompressed.length) { + await this.fileStore.database.upsertAllUncompressedData(uncompressed); + } + if (compressed.length) { + await this.fileStore.database.upsertAllCompressedData(compressed); + } + } + + getFile(fileKey: number): JagFile | null; + getFile(fileName: string): JagFile | null; + getFile(fileKeyOrName: number | string): JagFile | null; + getFile(fileKeyOrName: number | string): JagFile | null { + if (typeof fileKeyOrName === 'string') { + return Array.from(this.files.values()).find( + file => file?.index?.name === fileKeyOrName + ) || null; + } else { + return this.files.get(fileKeyOrName) || null; + } + } + + setFile(fileKey: number, file: JagFile): void { + this.files.set(fileKey, file); + } + +} diff --git a/src/file-system/jag/jag-cache.ts b/src/file-system/jag/jag-cache.ts new file mode 100644 index 0000000..ed896d0 --- /dev/null +++ b/src/file-system/jag/jag-cache.ts @@ -0,0 +1,118 @@ +import { JagFileStore } from './jag-file-store'; +import { JagFileIndex, caches } from './jag'; +import { JagArchive } from './jag-archive'; +import { JagFile } from './jag-file'; +import { JagFileBase } from './jag-file-base'; + + +export class JagCache extends JagFileBase { + + readonly files: Map; + + fileIndexes: JagFileIndex[]; + + constructor(jagStore: JagFileStore, cacheKey: number) { + super(jagStore, 'CACHE', cacheKey); + const indexNames = Object.keys(caches); + for (const name of indexNames) { + if (caches[name] === cacheKey) { + this.index.name = name; + } + } + this.files = new Map(); + } + + async upsertFileIndexes(): Promise { + const fileIndexes = Array.from(this.files.values()).map(file => file.index); + await this.fileStore.database.upsertIndexes(fileIndexes); + } + + async loadFileIndexes(): Promise { + const fileIndexes = await this.fileStore.database.getIndexes({ + cacheKey: this.index.key, + archiveKey: -1, + }); + + if (!fileIndexes?.length) { + return; + } + + for (const fileIndex of fileIndexes) { + const fileKey = fileIndex.key; + + if (!this.files.has(fileKey)) { + let file: JagFileBase; + + if (fileIndex.fileType === 'ARCHIVE') { + file = new JagArchive(this.fileStore, fileKey); + } else { + file = new JagFile(this.fileStore, fileKey, this.index.key, -1); + } + + file.index = fileIndex; + this.files.set(fileKey, file); + } + } + } + + async upsertFileData(): Promise { + const files = Array.from(this.files.values()); + const uncompressed = files.map(file => file.data).filter(data => data?.buffer && data?.buffer?.length !== 0); + const compressed = files.map(file => file.compressedData).filter(data => data?.buffer && data?.buffer?.length !== 0); + if (uncompressed.length) { + await this.fileStore.database.upsertAllUncompressedData(uncompressed); + } + if (compressed.length) { + await this.fileStore.database.upsertAllCompressedData(compressed); + } + } + + createArchive(archiveKey: number): JagArchive { + const archive = new JagArchive(this.fileStore, archiveKey); + this.setArchive(archiveKey, archive); + return archive; + } + + getArchive(archiveKey: number): JagArchive | null; + getArchive(archiveName: string): JagArchive | null; + getArchive(archiveKeyOrName: number | string): JagArchive | null; + getArchive(archiveKeyOrName: number | string): JagArchive | null { + let archive: JagFile | JagArchive | null; + + if (typeof archiveKeyOrName === 'string') { + archive = Array.from(this.files.values()).find( + file => file?.index?.name === archiveKeyOrName + ) || null; + } else { + archive = this.files.get(archiveKeyOrName) || null; + } + + return (archive && archive instanceof JagArchive) ? archive : null; + } + + setArchive(archiveKey: number, archive: JagArchive): void { + this.files.set(archiveKey, archive); + } + + getFile(fileKey: number): JagFile | null; + getFile(fileName: string): JagFile | null; + getFile(fileKeyOrName: number | string): JagFile | null; + getFile(fileKeyOrName: number | string): JagFile | null { + let file: JagFile | JagArchive | null; + + if (typeof fileKeyOrName === 'string') { + file = Array.from(this.files.values()).find( + file => file?.index?.name === fileKeyOrName + ) || null; + } else { + file = this.files.get(fileKeyOrName) || null; + } + + return (file && file instanceof JagFile) ? file : null; + } + + setFile(fileKey: number, file: JagFile): void { + this.files.set(fileKey, file); + } + +} diff --git a/src/file-system/jag/jag-file-base.ts b/src/file-system/jag/jag-file-base.ts new file mode 100644 index 0000000..c91617e --- /dev/null +++ b/src/file-system/jag/jag-file-base.ts @@ -0,0 +1,241 @@ +import { JagFileType } from '../../config'; +import { logger } from '@runejs/common'; +import { Buffer } from 'buffer'; +import { Crc32 } from '@runejs/common/crc32'; +import { createHash } from 'crypto'; +import { JagFileStore } from './jag-file-store'; +import { JagDataEntity, JagIndexEntity } from '../../db/jag'; + + +export abstract class JagFileBase { + + readonly fileStore: JagFileStore; + + index: JagIndexEntity; + data: JagDataEntity; + compressedData: JagDataEntity; + + protected constructor( + fileStore: JagFileStore, + fileType: JagFileType, + key: number, + cacheKey: number = -1, + archiveKey: number = -1, + ) { + this.fileStore = fileStore; + this.index = new JagIndexEntity(); + this.data = new JagDataEntity(); + this.compressedData = new JagDataEntity(); + this.compressedData.compressed = true; + this.data.gameBuild = this.compressedData.gameBuild = this.index.gameBuild = fileStore.gameBuild; + this.data.fileType = this.compressedData.fileType = this.index.fileType = fileType; + this.data.key = this.compressedData.key = this.index.key = key; + this.data.cacheKey = this.compressedData.cacheKey = this.index.cacheKey = cacheKey; + this.data.archiveKey = this.compressedData.archiveKey = this.index.archiveKey = archiveKey; + } + + validate(trackChanges: boolean = true): void { + const data = this.data.buffer; + const compressedData = this.compressedData.buffer; + const { + checksum, shaDigest, fileSize, + compressedChecksum, compressedShaDigest, compressedFileSize, + name, nameHash, compressionMethod, + } = this.index; + let fileModified: boolean = false; + + const currentChecksum = this.generateChecksum(data); + const currentShaDigest = this.generateShaDigest(data); + const currentFileSize = data?.length || 0; + + const currentCompressedChecksum = this.generateChecksum(compressedData); + const currentCompressedShaDigest = this.generateShaDigest(compressedData); + const currentCompressedFileSize = compressedData?.length || 0; + + if (name && nameHash === -1) { + // nameHash not set + this.index.nameHash = this.fileStore.nameHasher.hashJs5FileName(name); + } + + if (nameHash !== -1 && !name) { + // name not set + const lookupTableName = this.fileStore.nameHasher.findFileName(nameHash); + if (lookupTableName) { + this.index.name = lookupTableName; + } + } + + if (checksum !== currentChecksum) { + // uncompressed crc32 mismatch + this.index.checksum = currentChecksum; + fileModified = true; + } + + if (shaDigest !== currentShaDigest) { + // uncompressed sha256 mismatch + this.index.shaDigest = currentShaDigest; + fileModified = true; + } + + if (fileSize !== currentFileSize) { + // uncompressed file size mismatch + this.index.fileSize = currentFileSize; + fileModified = true; + } + + if (compressedChecksum !== currentCompressedChecksum) { + // compressed crc32 mismatch + this.index.compressedChecksum = currentCompressedChecksum; + fileModified = true; + } + + if (compressedShaDigest !== currentCompressedShaDigest) { + // compressed sha256 mismatch + this.index.compressedShaDigest = currentCompressedShaDigest; + fileModified = true; + } + + if (compressedFileSize !== currentCompressedFileSize) { + // compressed file size mismatch + this.index.compressedFileSize = currentCompressedFileSize; + fileModified = true; + } + + if ((!this.index.compressionMethod || this.index.compressionMethod === 'none' || this.index.compressedFileSize === fileSize) + && this.compressedData?.buffer?.length) { + // File has no compression, clear the compressed data buffer so that we do not create a + // duplicate data entity record for it + this.compressedData.buffer = null; + this.index.compressedFileSize = 0; + this.index.compressedChecksum = -1; + this.index.compressedShaDigest = null; + } + + if (fileModified && trackChanges) { + logger.info(`File ${this.index.name || this.index.key} has been modified.`); + this.index.version++; + } + } + + async saveUncompressedData(): Promise { + if (!this.data?.buffer?.length) { + // Do not save a record for files with missing or empty data + return null; + } + + this.data = await this.fileStore.database.saveUncompressedData(this.data); + return this.data; + } + + async loadUncompressedData(): Promise { + const entity = await this.fileStore.database.getUncompressedData({ + fileType: this.index.fileType, + key: this.index.key, + archiveKey: this.index.archiveKey, + cacheKey: this.index.cacheKey, + }); + + if (entity) { + this.data = entity; + } + + return this.data; + } + + async getUncompressedData(): Promise { + if (this.data?.buffer?.length) { + return this.data.buffer; + } + + const uncompressedData = await this.loadUncompressedData(); + if (uncompressedData?.buffer?.length) { + return uncompressedData.buffer; + } + + return null; + } + + async saveCompressedData(): Promise { + if (!this.compressedData?.buffer?.length) { + // Do not save a record for files with missing or empty data + return null; + } + + this.compressedData = await this.fileStore.database.saveCompressedData(this.compressedData); + return this.compressedData; + } + + async loadCompressedData(): Promise { + const entity = await this.fileStore.database.getCompressedData({ + fileType: this.index.fileType, + key: this.index.key, + archiveKey: this.index.archiveKey, + cacheKey: this.index.cacheKey, + }); + + if (entity) { + this.compressedData = entity; + } + + return this.compressedData; + } + + async getCompressedData(): Promise { + if (!this.index) { + await this.loadIndex(); + } + + if (!this.index.compressionMethod || this.index.compressionMethod === 'none') { + return this.getUncompressedData(); + } + + if (this.compressedData?.buffer?.length) { + return this.compressedData.buffer; + } + + const compressedData = await this.loadCompressedData(); + if (compressedData?.buffer?.length) { + return compressedData.buffer; + } + + return null; + } + + async saveIndex(): Promise { + this.validate(); + this.index = await this.fileStore.database.saveIndex(this.index); + return this.index; + } + + async loadIndex(): Promise { + const indexEntity = await this.fileStore.database.getIndex({ + fileType: this.index.fileType, + key: this.index.key, + cacheKey: this.index.cacheKey, + archiveKey: this.index.archiveKey, + }); + + if (indexEntity) { + this.index = indexEntity; + } + + return this.index; + } + + generateChecksum(data: Buffer): number { + if (!data?.length) { + return -1; + } + + return Crc32.update(0, data.length, data); + } + + generateShaDigest(data: Buffer): string { + if (!data?.length) { + return null; + } + + return createHash('sha256').update(data).digest('hex'); + } + +} diff --git a/src/file-system/jag/jag-file-store.ts b/src/file-system/jag/jag-file-store.ts new file mode 100644 index 0000000..501013e --- /dev/null +++ b/src/file-system/jag/jag-file-store.ts @@ -0,0 +1,114 @@ +import { join } from 'path'; +import { logger } from '@runejs/common'; +import { FileStoreBase } from '../file-store-base'; +import { Jag, caches } from './jag'; +import { JagCache } from './jag-cache'; +import { JagDatabase } from '../../db/jag'; +import { JagArchive } from './jag-archive'; + + +export class JagFileStore extends FileStoreBase { + + readonly jag: Jag; + readonly caches: Map; + + constructor(gameBuild: string | number, storePath: string = './') { + super(gameBuild, storePath); + this.jag = new Jag(this); + this.caches = new Map(); + } + + override async openDatabase(): Promise { + this._database = new JagDatabase( + this.gameBuild, + join(this.fileStorePath, 'index'), + [ 'error', 'warn' ], + ); + await this._database.openConnection(); + return this._database; + } + + override async load( + loadCacheEntities: boolean = false, + loadCacheChildEntities: boolean = false, + loadArchiveChildEntities: boolean = false, + ): Promise { + logger.info(`Loading JAG store for build ${this.gameBuild}...`); + await this.openDatabase(); + + if (loadCacheEntities) { + await this.loadCacheEntities(loadCacheChildEntities, loadArchiveChildEntities); + } + } + + /** + * Load all cache entities for this file store. + * @param loadCacheChildEntities Whether or not to load cache child file entities. + * Defaults to `false`. + * @param loadArchiveChildEntities Whether or not to load archive child file entities. + * Only works if `loadCacheChildEntities` is also set to `true`. Defaults to `false`. + */ + async loadCacheEntities( + loadCacheChildEntities: boolean = false, + loadArchiveChildEntities: boolean = false, + ): Promise { + if (!this.caches.size) { + const cacheNames = Object.keys(caches); + for (const cacheName of cacheNames) { + this.createCache(caches[cacheName]); + } + } + + for (const [ , cache ] of this.caches) { + await cache.loadIndex(); + } + + if (loadCacheChildEntities) { + logger.info(`Loading cache file entities...`); + + for (const [ , cache ] of this.caches) { + await cache.loadFileIndexes(); + } + + if (loadArchiveChildEntities) { + logger.info(`Loading archive file entities...`); + + const archiveIndex = this.getCache('archives'); + + for (const [ , file ] of archiveIndex.files) { + const archive = file as JagArchive; + await archive.loadFileIndexes(); + } + } + } + } + + createCache(cacheKey: number): void { + this.setCache(cacheKey, new JagCache(this, cacheKey)); + } + + getCache(cacheKey: number): JagCache | null; + getCache(cacheName: string): JagCache | null; + getCache(cacheKeyOrName: number | string): JagCache | null; + getCache(cacheKeyOrName: number | string): JagCache | null { + if (typeof cacheKeyOrName === 'string') { + return Array.from(this.caches.values()).find( + i => i?.index?.name === cacheKeyOrName + ) || null; + } else { + return this.caches.get(cacheKeyOrName) || null; + } + } + + setCache(cacheKey: number, cache: JagCache): void; + setCache(cacheName: string, cache: JagCache): void; + setCache(cacheKeyOrName: number | string, cache: JagCache): void; + setCache(cacheKeyOrName: number | string, cache: JagCache): void { + if (typeof cacheKeyOrName === 'string') { + this.caches.set(caches[cacheKeyOrName], cache); + } else { + this.caches.set(cacheKeyOrName, cache); + } + } + +} diff --git a/src/file-system/jag/jag-file.ts b/src/file-system/jag/jag-file.ts new file mode 100644 index 0000000..bc7fff5 --- /dev/null +++ b/src/file-system/jag/jag-file.ts @@ -0,0 +1,11 @@ +import { JagFileStore } from './jag-file-store'; +import { JagFileBase } from './jag-file-base'; + + +export class JagFile extends JagFileBase { + + constructor(jagStore: JagFileStore, fileKey: number, cacheKey: number, archiveKey: number = -1) { + super(jagStore, 'FILE', fileKey, cacheKey, archiveKey); + } + +} diff --git a/src/file-system/jag/jag.ts b/src/file-system/jag/jag.ts new file mode 100644 index 0000000..d918a96 --- /dev/null +++ b/src/file-system/jag/jag.ts @@ -0,0 +1,362 @@ +import { join } from 'path'; +import { existsSync, readdirSync, readFileSync, statSync } from 'graceful-fs'; +import { Buffer } from 'buffer'; +import { logger } from '@runejs/common'; +import { ByteBuffer } from '@runejs/common/buffer'; +import { Bzip2, Gzip } from '@runejs/common/compress'; +import { JagFileStore } from './jag-file-store'; +import { JagFile } from './jag-file'; +import { JagArchive } from './jag-archive'; +import { JagFileBase } from './jag-file-base'; +import { PackedCacheFile } from '../packed'; + + +const dataFileName = 'main_file_cache.dat'; +const indexFileNamePrefix = 'main_file_cache.idx'; + + +export const caches = { + archives: 0, + models: 1, + animations: 2, + midi: 3, + maps: 4, +}; + + +export const archives = { + 'empty.jag': 0, + 'title.jag': 1, + 'config.jag': 2, + 'interface.jag': 3, + 'media.jag': 4, + 'versionlist.jag': 5, + 'textures.jag': 6, + 'wordenc.jag': 7, + 'sounds.jag': 8, +}; + + +export interface JagFileIndex { + fileSize: number; + sectorNumber: number; +} + + +export interface JagSectorHeader { + fileKey: number; + filePartNumber: number; + sectorNumber: number; + cacheKey: number; +} + + +export class Jag { + + readonly jagStore: JagFileStore; + + private indexFiles: Map; + private dataFile: ByteBuffer; + + constructor(jagStore: JagFileStore) { + this.jagStore = jagStore; + this.indexFiles = new Map(); + } + + readOpenRS2PackedCacheFiles(cacheFiles: PackedCacheFile[]): void { + const dataFileBuffer = cacheFiles.find(file => file.name === dataFileName)?.data || null; + if (!dataFileBuffer?.length) { + throw new Error(`The main ${ dataFileName } data file could not be found.`); + } + + this.dataFile = new ByteBuffer(dataFileBuffer); + this.indexFiles.clear(); + + for (const cacheFile of cacheFiles) { + const fileName = cacheFile?.name; + + if (!fileName?.length || fileName === dataFileName) { + continue; + } + + if (!fileName.startsWith(indexFileNamePrefix)) { + continue; + } + + if (!cacheFile?.data?.length) { + logger.error(`Index file ${ fileName } is empty!`); + continue; + } + + const indexString = fileName.substring(fileName.indexOf('.idx') + 4); + const indexKey = Number(indexString); + + if (isNaN(indexKey)) { + logger.error(`Index file ${ fileName } does not have a valid extension.`); + } + + this.indexFiles.set(indexKey, new ByteBuffer(cacheFile.data)); + this.jagStore.createCache(indexKey); + } + + logger.info(`JAG store files loaded for game build ${this.jagStore.gameBuild}.`); + } + + readLocalPackedCacheFiles(): void { + const jagStorePath = join(this.jagStore.fileStorePath, 'jag'); + + if (!existsSync(jagStorePath)) { + throw new Error(`${jagStorePath} could not be found.`); + } + + const stats = statSync(jagStorePath); + if (!stats?.isDirectory()) { + throw new Error(`${jagStorePath} is not a valid directory.`); + } + + const storeFileNames = readdirSync(jagStorePath); + const dataFileName = 'main_file_cache.dat'; + + if (storeFileNames.indexOf(dataFileName) === -1) { + throw new Error(`The main ${dataFileName} data file could not be found.`); + } + + const indexFilePrefix = 'main_file_cache.idx'; + const dataFilePath = join(jagStorePath, dataFileName); + + this.dataFile = new ByteBuffer(readFileSync(dataFilePath)); + this.indexFiles = new Map(); + + for (const fileName of storeFileNames) { + if (!fileName?.length || fileName === dataFileName) { + continue; + } + + if (!fileName.startsWith(indexFilePrefix)) { + continue; + } + + const indexString = fileName.substring(fileName.indexOf('.idx') + 4); + const indexKey = Number(indexString); + + if (isNaN(indexKey)) { + logger.error(`Index file ${fileName} does not have a valid extension.`); + } + + this.indexFiles.set(indexKey, new ByteBuffer(readFileSync(join(jagStorePath, fileName)))); + this.jagStore.createCache(indexKey); + } + + logger.info(`JAG store files loaded for game build ${this.jagStore.gameBuild}.`); + } + + decodeCache(indexName: string): void { + logger.info(`Decoding JAG cache index ${indexName}...`); + + const cacheKey = caches[indexName]; + const indexFile = this.indexFiles.get(cacheKey); + const fileCount = indexFile.length / 6; + + logger.info(`${fileCount} file indexes found.`); + + const cache = this.jagStore.getCache(cacheKey); + cache.fileIndexes = new Array(fileCount); + cache.index.childCount = fileCount; + cache.index.compressionMethod = 'none'; + cache.data.buffer = indexFile.toNodeBuffer(); + + for (let fileKey = 0; fileKey < fileCount; fileKey++) { + const fileSize = indexFile.get('int24', 'unsigned'); + const sectorPos = indexFile.get('int24', 'unsigned'); + cache.fileIndexes[fileKey] = { + fileSize, sectorNumber: sectorPos + }; + + let file: JagFileBase; + + if (indexName === 'archives') { + file = new JagArchive(this.jagStore, fileKey); + } else { + file = new JagFile(this.jagStore, fileKey, cacheKey); + } + + cache.files.set(fileKey, file); + } + + cache.validate(false); + + logger.info(`Cache index ${indexName} has been decoded.`); + } + + unpack(file: JagArchive | JagFile): Buffer | null { + const fileIndexData = this.jagStore.getCache(file.index.cacheKey); + const { fileSize, sectorNumber } = fileIndexData.fileIndexes[file.index.key]; + const fileData = new ByteBuffer(fileSize); + const sectorDataLength = 512; + const sectorLength = 520; + let remainingData = fileSize; + let currentSectorNumber = sectorNumber; + let cycles = 0; + + while (remainingData > 0) { + let readableSectorData = sectorLength; + let remaining = this.dataFile.readable - currentSectorNumber * sectorLength; + + if (remaining < sectorLength) { + readableSectorData = remaining; + } + + const block = this.dataFile.getSlice(currentSectorNumber * sectorLength, readableSectorData); + + const sectorFileKey = block.get('short', 'unsigned'); + const sectorFilePartNumber = block.get('short', 'unsigned'); + const sectorNumber = block.get('int24', 'unsigned'); + const sectorCacheKey = block.get('byte', 'unsigned'); + + readableSectorData -= 8; + + let bytesThisCycle = remainingData; + + if (bytesThisCycle > sectorDataLength) { + bytesThisCycle = sectorDataLength; + } + + block.copy( + fileData, + fileData.writerIndex, + block.readerIndex, + block.readerIndex + readableSectorData + ); + + fileData.writerIndex = fileData.writerIndex + bytesThisCycle; + remainingData -= bytesThisCycle; + + if (cycles !== sectorFilePartNumber) { + logger.error(`Error extracting JAG file ${ file.index.key }, file data is corrupted.`); + return null; + } + + if (remainingData > 0) { + // saved index keys have 1 added to them for some reason + if (sectorCacheKey !== file.index.cacheKey + 1) { + logger.error(`Index key mismatch, expected cache ${ file.index.cacheKey } but found ${ sectorCacheKey }`); + return null; + } + + if (sectorFileKey !== file.index.key) { + logger.error(`File index mismatch, expected ${ file.index.key } but found ${ sectorFileKey }.`); + return null; + } + } + + cycles++; + currentSectorNumber = sectorNumber; + } + + if (!(file instanceof JagArchive)) { + file.index.compressionMethod = 'gzip'; + } + + if (fileData.length) { + file.compressedData.buffer = fileData.toNodeBuffer(); + + if (!(file instanceof JagArchive)) { + file.data.buffer = Gzip.decompress(fileData); + } + } else { + file.compressedData.buffer = null; + file.index.fileError = 'FILE_MISSING'; + } + + if (!(file instanceof JagArchive)) { + file.validate(false); + } + + return file.compressedData.buffer; + } + + decodeArchive(archive: JagArchive): Buffer | null { + if (!archive.compressedData?.buffer?.length) { + return null; + } + + let archiveData = new ByteBuffer(archive.compressedData.buffer); + + const uncompressed = archiveData.get('int24', 'unsigned'); + const compressed = archiveData.get('int24', 'unsigned'); + + if (uncompressed !== compressed) { + const compressedData = archiveData.getSlice(archiveData.readerIndex, archiveData.length - archiveData.readerIndex); + const decompressedData = Bzip2.decompress(compressedData); + archiveData = new ByteBuffer(decompressedData); + archive.data.buffer = decompressedData; + archive.index.compressionMethod = 'bzip2'; + } else { + archive.data.buffer = archiveData.toNodeBuffer(); + archive.index.compressionMethod = 'none'; + } + + const fileCount = archiveData.get('short', 'unsigned'); + archive.index.childCount = fileCount; + archive.files.clear(); + + const fileDataOffsets: number[] = new Array(fileCount); + let fileDataOffset = archiveData.readerIndex + fileCount * 10; + + // Read archive file headers + for (let fileKey = 0; fileKey < fileCount; fileKey++) { + const fileNameHash = archiveData.get('int'); + const fileName = this.jagStore.nameHasher.findFileName(fileNameHash); + const decompressedFileLength = archiveData.get('int24', 'unsigned'); + const compressedFileLength = archiveData.get('int24', 'unsigned'); + fileDataOffsets[fileKey] = fileDataOffset; + fileDataOffset += compressedFileLength; + + const file = new JagFile(this.jagStore, fileKey, archive.index.cacheKey, archive.index.key); + file.index.nameHash = fileNameHash; + file.index.name = fileName; + file.index.fileSize = decompressedFileLength; + file.index.compressedFileSize = compressedFileLength; + + archive.files.set(fileKey, file); + } + + // Read archive file data + for (const [ fileKey, file ] of archive.files) { + try { + const fileDataOffset = fileDataOffsets[fileKey]; + const fileData = Buffer.alloc(file.index.compressedFileSize); + archiveData.copy(fileData, 0, fileDataOffset); + file.compressedData.buffer = fileData; + } catch (error) { + logger.error(`Error reading archive ${archive.index.name } file ${fileKey}`, error); + } + } + + // Decompress archive file data (if needed) + for (const [ fileKey, file ] of archive.files) { + try { + const { compressedFileSize, fileSize } = file.index; + if (file.compressedData?.buffer?.length && compressedFileSize !== fileSize) { + file.data.buffer = Bzip2.decompress(file.compressedData.buffer); + file.index.compressionMethod = 'bzip2'; + } else { + file.data.buffer = file.compressedData.buffer; + file.index.compressionMethod = 'none'; + } + } catch (error) { + logger.error(`Error decompressing archive ${archive.index.name } file ${fileKey}`, error); + } + } + + // Validate each file + for (const [ , file ] of archive.files) { + file.validate(false); + } + + archive.validate(false); + + return archive.data.buffer; + } + +} diff --git a/src/file-system/js5/content/config/config-file.ts b/src/file-system/js5/content/config/config-file.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/file-system/js5/content/config/js5-game-items.ts b/src/file-system/js5/content/config/js5-game-items.ts new file mode 100644 index 0000000..f76c0f7 --- /dev/null +++ b/src/file-system/js5/content/config/js5-game-items.ts @@ -0,0 +1,158 @@ +import { Js5FileStore } from '../../js5-file-store'; +import { ByteBuffer } from '@runejs/common'; + + +export class Js5GameItem { + id: number; + inventoryModelId: number; + name: string; + modelZoom: number; + modelRotationX: number; + modelRotationY: number; + modelOffsetX: number; + modelOffsetY: number; + unknownServerAttribute1: number; + stackable: boolean = false; + value: number; + members: boolean = false; + maleModelId1: number; + maleModelOffset: number; + maleModelId2: number; + femaleModelId1: number; + femaleModelOffset: number; + femaleModelId2: number; + groundOptions: string[]; + interfaceOptions: string[]; + originalModelColors: number[]; + modifiedModelColors: number[]; + maleModelId3: number; + femaleModelId3: number; + maleDialogueModelId1: number; + femaleDialogueModelId1: number; + maleDialogueModelId2: number; + femaleDialogueModelId2: number; + modelRotationZ: number; + noteId: number; + noteTemplateId: number; + stackIds: number[]; + stackAmounts: number[]; + modelScaleX: number; + modelScaleY: number; + modelScaleZ: number; + lightingModifier: number; + shadowModifier: number; + teamIndex: number; +} + +export class Js5GameItems { + + readonly js5Store: Js5FileStore; + readonly items: Map; + + constructor(js5Store: Js5FileStore) { + this.js5Store = js5Store; + this.items = new Map(); + } + + decode(id: number, data: ByteBuffer): Js5GameItem { + const item = new Js5GameItem(); + item.id = id; + + while (true) { + const o = data.get('byte', 'u'); + + if (o === 0) { + break; // eof + } else if (o === 1) { + item.inventoryModelId = data.get('short', 'u'); + } else if (o === 2) { + item.name = data.getString(); + } else if (o === 4) { + // case 3 was item description prior to build 400 + item.modelZoom = data.get('short', 'u'); + } else if (o === 5) { + item.modelRotationX = data.get('short', 'u'); + } else if (o === 6) { + item.modelRotationY = data.get('short', 'u'); + } else if (o === 7) { + // client subtracts 65536 if the value is over 32767 + // so this should be a SIGNED short then, right?... + item.modelOffsetX = data.get('short', 'u'); + } else if (o === 8) { + // client subtracts 65536 if the value is over 32767 + // so this should be a SIGNED short then, right?... + item.modelOffsetY = data.get('short', 'u'); + } else if (o === 10) { + // case 9 missing - what was it (if anything)? + item.unknownServerAttribute1 = data.get('short', 'u'); + } else if (o === 11) { + item.stackable = true; + } else if (o === 12) { + item.value = data.get('int'); + } else if (o === 16) { + // cases 13-15 missing - what were they (if anything)? + item.members = true; + } else if (o === 23) { + // cases 17-22 missing - what were they (if anything)? + item.maleModelId1 = data.get('short', 'u'); + item.maleModelOffset = data.get('byte'); + } else if (o === 24) { + item.maleModelId2 = data.get('short', 'u'); + } else if (o === 25) { + item.femaleModelId1 = data.get('short', 'u'); + item.femaleModelOffset = data.get('byte'); + } else if (o === 26) { + item.femaleModelId2 = data.get('short', 'u'); + } else if (o >= 30 && o < 35) { + // cases 27-29 missing - what were they (if anything)? + if (!item.groundOptions) { + item.groundOptions = new Array(5); + } + + item.groundOptions[o - 30] = data.getString(); + } else if (o >= 35 && o < 40) { + if (!item.interfaceOptions) { + item.interfaceOptions = new Array(5); + } + + item.interfaceOptions[o - 35] = data.getString(); + } else if (o === 40) { + const colorCount = data.get('byte', 'u'); + item.originalModelColors = new Array(colorCount); + item.modifiedModelColors = new Array(colorCount); + for (let i = 0; i < colorCount; i++) { + item.originalModelColors[i] = data.get('short', 'u'); + item.modifiedModelColors[i] = data.get('short', 'u'); + } + } else if (o === 78) { + // cases 41-77 missing - what were they (if anything)? + item.maleModelId3 = data.get('short', 'u'); + } else if (o === 79) { + item.femaleModelId3 = data.get('short', 'u'); + } else if (o === 90) { + item.maleDialogueModelId1 = data.get('short', 'u'); + } else if (o === 91) { + item.femaleDialogueModelId1 = data.get('short', 'u'); + } else if (o === 92) { + item.maleDialogueModelId2 = data.get('short', 'u'); + } else if (o === 93) { + item.femaleDialogueModelId2 = data.get('short', 'u'); + } else if (o === 95) { + // case 94 missing - what was it (if anything)? + item.modelRotationZ = data.get('short', 'u'); + } else if (o === 97) { + // case 96 missing - what was it (if anything)? + item.noteId = data.get('short', 'u'); + } else if (o === 98) { + item.noteTemplateId = data.get('short', 'u'); + } // @todo stopped here - 24/08/22 - Kiko + } + + return item; + } + + decodeAll(): void { + + } + +} diff --git a/src/file-system/js5/index.ts b/src/file-system/js5/index.ts new file mode 100644 index 0000000..0832e68 --- /dev/null +++ b/src/file-system/js5/index.ts @@ -0,0 +1,6 @@ +export * from './js5'; +export * from './js5-file-base'; +export * from './js5-file-store'; +export * from './js5-archive'; +export * from './js5-group'; +export * from './js5-file'; diff --git a/src/file-system/js5/js5-archive.ts b/src/file-system/js5/js5-archive.ts new file mode 100644 index 0000000..aa724e5 --- /dev/null +++ b/src/file-system/js5/js5-archive.ts @@ -0,0 +1,129 @@ +import { Js5FileStore } from './js5-file-store'; +import { Js5Group } from './js5-group'; +import { logger } from '@runejs/common'; +import { Js5FileBase } from './js5-file-base'; +import { Js5ArchiveConfig } from '../../config'; +import { Js5IndexEntity } from '../../db/js5'; + + +export class Js5Archive extends Js5FileBase { + + readonly groups: Map; + readonly config: Js5ArchiveConfig; + + constructor( + fileStore: Js5FileStore, + archiveKey: number, + ) { + super(fileStore, 'ARCHIVE', archiveKey, 255, -1); + this.config = fileStore.getArchiveConfig(archiveKey); + this.index.name = fileStore.getArchiveName(archiveKey); + this.groups = new Map(); + } + + override validate(trackChanges: boolean = true): void { + super.validate(trackChanges); + + let archiveModified: boolean = false; + + const { childCount } = this.index; + const newChildCount = this.groups.size; + + if (childCount !== newChildCount) { + this.index.childCount = newChildCount; + archiveModified = true; + } + + if (archiveModified && trackChanges) { + logger.info(`Archive ${this.index.name || this.index.key} child count has changed.`); + this.index.version++; + } + } + + async upsertGroupIndexes(): Promise { + const groupIndexes = Array.from(this.groups.values()).map(group => group.index); + await this.fileStore.database.upsertIndexes(groupIndexes); + } + + async loadGroupIndexes(): Promise { + const groupIndexes = await this.fileStore.database.getIndexes({ + fileType: 'GROUP', + archiveKey: this.index.key, + }); + + if (!groupIndexes?.length) { + return; + } + + for (const groupIndex of groupIndexes) { + const groupKey = groupIndex.key; + + if (!this.groups.has(groupKey)) { + const group = new Js5Group(this.fileStore, groupKey, this); + group.index = groupIndex; + this.groups.set(groupKey, group); + } + } + } + + async upsertGroupData(): Promise { + const groups = Array.from(this.groups.values()); + const uncompressed = groups.map(group => group.data).filter(data => data?.buffer && data?.buffer?.length !== 0); + const compressed = groups.map(group => group.compressedData).filter(data => data?.buffer && data?.buffer?.length !== 0); + if (uncompressed.length) { + await this.fileStore.database.upsertAllUncompressedData(uncompressed); + } + if (compressed.length) { + await this.fileStore.database.upsertAllCompressedData(compressed); + } + } + + async getGroup(groupKey: number): Promise; + async getGroup(groupName: string): Promise; + async getGroup(groupIdentifier: number | string): Promise; + async getGroup(groupIdentifier: number | string): Promise { + let group: Js5Group; + + if (typeof groupIdentifier === 'string') { + group = Array.from(this.groups.values()).find( + group => group?.index?.name === groupIdentifier + ) || null; + } else { + group = this.groups.get(groupIdentifier) || null; + } + + if (!group?.index) { + let groupEntity: Js5IndexEntity; + + if (typeof groupIdentifier === 'number' || /^\d*$/.test(groupIdentifier)) { + const groupKey = typeof groupIdentifier === 'string' ? parseInt(groupIdentifier, 10) : groupIdentifier; + groupEntity = await this.fileStore.database.getIndex({ + fileType: 'GROUP', + archiveKey: this.index.key, + key: groupKey + }); + } else { + groupEntity = await this.fileStore.database.getIndex({ + fileType: 'GROUP', + archiveKey: this.index.key, + name: String(groupIdentifier) + }); + } + + if (!group) { + group = new Js5Group(this.fileStore, groupEntity.key, this); + group.index = groupEntity; + this.groups.set(groupEntity.key, group); + } else { + group.index = groupEntity; + } + } + + return group; + } + + setGroup(groupKey: number, group: Js5Group): void { + this.groups.set(groupKey, group); + } + +} diff --git a/src/file-system/js5/js5-file-base.ts b/src/file-system/js5/js5-file-base.ts new file mode 100644 index 0000000..a4cae9c --- /dev/null +++ b/src/file-system/js5/js5-file-base.ts @@ -0,0 +1,249 @@ +import { Buffer } from 'buffer'; +import { createHash } from 'crypto'; +import { logger } from '@runejs/common'; +import { Crc32 } from '@runejs/common/crc32'; +import { Js5FileStore } from './js5-file-store'; +import { Js5FileType } from '../../config'; +import { Js5IndexEntity, Js5DataEntity } from '../../db/js5'; + + +export abstract class Js5FileBase { + + readonly fileStore: Js5FileStore; + + index: Js5IndexEntity; + data: Js5DataEntity; + compressedData: Js5DataEntity; + + protected constructor( + fileStore: Js5FileStore, + fileType: Js5FileType, + key: number, + archiveKey: number = -1, + groupKey: number = -1, + ) { + this.fileStore = fileStore; + this.index = new Js5IndexEntity(); + this.data = new Js5DataEntity(); + this.compressedData = new Js5DataEntity(); + this.compressedData.compressed = true; + this.data.gameBuild = this.compressedData.gameBuild = this.index.gameBuild = fileStore.gameBuild; + this.data.fileType = this.compressedData.fileType = this.index.fileType = fileType; + this.data.key = this.compressedData.key = this.index.key = key; + this.data.archiveKey = this.compressedData.archiveKey = this.index.archiveKey = archiveKey; + this.data.groupKey = this.compressedData.groupKey = this.index.groupKey = groupKey; + } + + validate(trackChanges: boolean = true): void { + const data = this.data.buffer; + const compressedData = this.compressedData.buffer; + const { + checksum, shaDigest, fileSize, + compressedChecksum, compressedShaDigest, compressedFileSize, + name, nameHash, compressionMethod + } = this.index; + let fileModified: boolean = false; + + const currentChecksum = this.generateChecksum(data); + const currentShaDigest = this.generateShaDigest(data); + const currentFileSize = data?.length || 0; + + const currentCompressedChecksum = this.generateChecksum(compressedData); + const currentCompressedShaDigest = this.generateShaDigest(compressedData); + const currentCompressedFileSize = compressedData?.length || 0; + + if (name && nameHash === -1) { + // nameHash not set + this.index.nameHash = this.fileStore.nameHasher.hashJs5FileName(name); + } + + if (nameHash !== -1 && !name) { + // name not set + const lookupTableName = this.fileStore.findFileName(this); + if (lookupTableName) { + this.index.name = lookupTableName; + } + } + + if (checksum !== currentChecksum) { + // uncompressed crc32 mismatch + this.index.checksum = currentChecksum; + fileModified = true; + } + + if (shaDigest !== currentShaDigest) { + // uncompressed sha256 mismatch + this.index.shaDigest = currentShaDigest; + fileModified = true; + } + + if (fileSize !== currentFileSize) { + // uncompressed file size mismatch + this.index.fileSize = currentFileSize; + fileModified = true; + } + + if (compressedChecksum !== currentCompressedChecksum) { + // compressed crc32 mismatch + this.index.compressedChecksum = currentCompressedChecksum; + fileModified = true; + } + + if (compressedShaDigest !== currentCompressedShaDigest) { + // compressed sha256 mismatch + this.index.compressedShaDigest = currentCompressedShaDigest; + fileModified = true; + } + + if (compressedFileSize !== currentCompressedFileSize) { + // compressed file size mismatch + this.index.compressedFileSize = currentCompressedFileSize; + fileModified = true; + } + + if ((!this.index.compressionMethod || this.index.compressionMethod === 'none' || this.index.compressedFileSize === fileSize) + && this.compressedData?.buffer?.length) { + // File has no compression, clear the compressed data buffer so that we do not create a + // duplicate data entity record for it + this.compressedData.buffer = null; + this.index.compressedFileSize = 0; + this.index.compressedChecksum = -1; + this.index.compressedShaDigest = null; + } + + if (fileModified && trackChanges) { + logger.info(`File ${this.index.name || this.index.key} has been modified.`); + this.index.version++; + } + } + + async saveUncompressedData(): Promise { + if (!this.data?.buffer?.length) { + // Do not save a record for files with missing or empty data + return null; + } + + this.data = await this.fileStore.database.saveUncompressedData(this.data); + return this.data; + } + + async loadUncompressedData(): Promise { + const entity = await this.fileStore.database.getUncompressedData({ + fileType: this.index.fileType, + key: this.index.key, + archiveKey: this.index.archiveKey, + groupKey: this.index.groupKey, + }); + + if (entity) { + this.data = entity; + } + + return this.data; + } + + async getUncompressedData(): Promise { + if (this.data?.buffer?.length) { + return this.data.buffer; + } + + const uncompressedData = await this.loadUncompressedData(); + if (uncompressedData?.buffer?.length) { + return uncompressedData.buffer; + } + + return null; + } + + async saveCompressedData(): Promise { + if (!this.compressedData?.buffer?.length) { + // Do not save a record for files with missing or empty data + return null; + } + + this.compressedData = await this.fileStore.database.saveCompressedData(this.compressedData); + return this.compressedData; + } + + async loadCompressedData(): Promise { + const entity = await this.fileStore.database.getCompressedData({ + fileType: this.index.fileType, + key: this.index.key, + archiveKey: this.index.archiveKey, + groupKey: this.index.groupKey, + }); + + if (entity) { + this.compressedData = entity; + } + + return this.compressedData; + } + + async getCompressedData(): Promise { + if (!this.index) { + await this.loadIndex(); + } + + if (!this.index.compressionMethod || this.index.compressionMethod === 'none') { + return this.getUncompressedData(); + } + + if (this.compressedData?.buffer?.length) { + return this.compressedData.buffer; + } + + const compressedData = await this.loadCompressedData(); + if (compressedData?.buffer?.length) { + return compressedData.buffer; + } + + return null; + } + + async saveIndex(): Promise { + this.validate(); + this.index = await this.fileStore.database.saveIndex(this.index); + return this.index; + } + + async loadIndex(): Promise { + const indexEntity = await this.fileStore.database.getIndex({ + fileType: this.index.fileType, + key: this.index.key, + archiveKey: this.index.archiveKey, + groupKey: this.index.groupKey, + }); + + if (indexEntity) { + this.index = indexEntity; + } + + return this.index; + } + + generateChecksum(data: Buffer): number { + if (!data?.length) { + return -1; + } + + return Crc32.update(0, data.length, data); + } + + generateShaDigest(data: Buffer): string { + if (!data?.length) { + return null; + } + + return createHash('sha256').update(data).digest('hex'); + } + + get stripes(): number[] { + if (!this.index?.stripes) { + return []; + } + + return this.index.stripes.split(',').map(n => Number(n)); + } + +} diff --git a/src/file-system/js5/js5-file-store.ts b/src/file-system/js5/js5-file-store.ts new file mode 100644 index 0000000..fd16a7f --- /dev/null +++ b/src/file-system/js5/js5-file-store.ts @@ -0,0 +1,240 @@ +import JSON5 from 'json5'; +import { join } from 'path'; +import { existsSync, readFileSync } from 'graceful-fs'; +import { Js5ArchiveConfig } from '../../config'; +import { JS5 } from './js5'; +import { Js5Archive } from './js5-archive'; +import { FileStoreBase } from '../file-store-base'; +import { logger } from '../../../../common'; +import { Js5Database, Js5IndexEntity } from '../../db/js5'; +import { Js5File } from './js5-file'; +import { Js5FileBase } from './js5-file-base'; + + +export class Js5FileStore extends FileStoreBase{ + + readonly js5: JS5; + readonly archives: Map; + + private _archiveConfig: { [key: string]: Js5ArchiveConfig }; + private _loaded: boolean = false; + + constructor(gameBuild: string | number, storePath: string = './') { + super(gameBuild, storePath); + this.archives = new Map(); + this.loadArchiveConfig(); + this.js5 = new JS5(this); + } + + override async openDatabase(): Promise { + this._database = new Js5Database( + this.gameBuild, + join(this.fileStorePath, 'index'), + [ 'error', 'warn' ], + ); + await this._database.openConnection(); + return this._database; + } + + override async load( + loadArchiveEntities: boolean = false, + loadArchiveChildEntities: boolean = false, + loadGroupChildEntities: boolean = false, + ): Promise { + if (this._loaded) { + return; + } + + await this.js5.loadEncryptionKeys(); + await this.openDatabase(); + + if (loadArchiveEntities) { + await this.loadArchiveEntities(loadArchiveChildEntities, loadGroupChildEntities); + } + + this._loaded = true; + } + + /** + * Load all archive entities for this file store. + * @param loadArchiveChildEntities Whether or not to load group entities under each archive. + * Defaults to `false`. + * @param loadGroupChildEntities Whether or not to load flat file entities under each group. + * Only works if `loadArchiveChildEntities` is also set to `true`. Defaults to `false`. + */ + async loadArchiveEntities( + loadArchiveChildEntities: boolean = false, + loadGroupChildEntities: boolean = false, + ): Promise { + if (!this.archives.size) { + const archiveEntities = await this.database.getIndexes({ + fileType: 'ARCHIVE' + }); + + for (const entity of archiveEntities) { + const archive = this.createArchive(entity.key); + archive.index = entity; + } + } else { + for (const [ , archive ] of this.archives) { + await archive.loadIndex(); + } + } + + if (loadArchiveChildEntities) { + for (const [ , archive ] of this.archives) { + await archive.loadGroupIndexes(); + } + + if (loadGroupChildEntities) { + // Bulk load grouped files to save computing time + const files = await this.database.getIndexes({ + fileType: 'FILE', + }); + + for (const fileEntity of files) { + const archive = await this.getArchive(fileEntity.archiveKey); + const group = await archive.getGroup(fileEntity.groupKey); + const file = new Js5File(this, fileEntity.key, group); + file.index = fileEntity; + group.setFile(fileEntity.key, file); + } + } + } + } + + loadArchiveConfig(): void { + const configPath = join(this.fileStorePath, 'config', 'js5-archives.json5'); + + if (!existsSync(configPath)) { + logger.error(`Error loading file store: ${configPath} was not found.`); + return; + } + + this._archiveConfig = JSON5.parse( + readFileSync(configPath, 'utf-8') + ) as { [key: string]: Js5ArchiveConfig }; + + if (!Object.values(this._archiveConfig)?.length) { + throw new Error(`Error reading archive configuration file. ` + + `Please ensure that the ${configPath} file exists and is valid.`); + } + } + + findFileName(file: Js5FileBase): string | null { + const nameHash = file.index.nameHash || -1; + + if (nameHash !== -1) { + return this.nameHasher.findFileName(nameHash, file.index.name || String(file.index.nameHash) || String(file.index.key)); + } + + const { key, groupKey, archiveKey, fileType } = file.index; + + const archiveConfig = this.getArchiveConfig(fileType === 'ARCHIVE' ? key : archiveKey); + + if (archiveConfig) { + if (fileType === 'GROUP' && archiveConfig.groupNames) { + const groupNames = Array.from(Object.entries(archiveConfig.groupNames)); + return groupNames.find(([ , k ]) => k === key)?.[0] || null; + } else if (fileType === 'ARCHIVE') { + const configs = Array.from(Object.entries(this.archiveConfig)); + return configs.find(([ , config ]) => config.key === archiveKey)?.[0] || null; + } else if (fileType === 'FILE') { + // @todo 2/9/22 - Kiko + } + } + + return null; + } + + createArchive(archiveKey: number): Js5Archive { + const archive = new Js5Archive(this, archiveKey); + this.setArchive(archiveKey, archive); + return archive; + } + + async getArchive(archiveKey: number): Promise; + async getArchive(archiveName: string): Promise; + async getArchive(archiveIdentifier: number | string): Promise; + async getArchive(archiveIdentifier: number | string): Promise { + let archive: Js5Archive; + + if (typeof archiveIdentifier === 'string') { + archive = Array.from(this.archives.values()).find( + a => a?.index?.name === archiveIdentifier + ) || null; + } else { + archive = this.archives.get(archiveIdentifier) || null; + } + + if (!archive?.index) { + let archiveEntity: Js5IndexEntity; + + if (typeof archiveIdentifier === 'number' || /^\d*$/.test(archiveIdentifier)) { + const archiveKey = typeof archiveIdentifier === 'string' ? parseInt(archiveIdentifier, 10) : archiveIdentifier; + archiveEntity = await this.database.getIndex({ + fileType: 'ARCHIVE', + key: archiveKey + }); + } else { + archiveEntity = await this.database.getIndex({ + fileType: 'ARCHIVE', + name: String(archiveIdentifier) + }); + } + + if (!archive) { + archive = new Js5Archive(this, archiveEntity.key); + archive.index = archiveEntity; + this.archives.set(archiveEntity.key, archive); + } else { + archive.index = archiveEntity; + } + } + + return archive; + } + + setArchive(archiveKey: number, archive: Js5Archive): void; + setArchive(archiveName: string, archive: Js5Archive): void; + setArchive(archiveKeyOrName: number | string, archive: Js5Archive): void; + setArchive(archiveKeyOrName: number | string, archive: Js5Archive): void { + if (typeof archiveKeyOrName === 'string') { + const archiveConfig = this.getArchiveConfig(archiveKeyOrName); + if (archiveConfig) { + this.archives.set(archiveConfig.key, archive); + } else { + logger.error(`Archive ${ archiveKeyOrName } configuration was not found.`); + } + } else { + this.archives.set(archiveKeyOrName, archive); + } + } + + getArchiveConfig(archiveKey: number): Js5ArchiveConfig | null; + getArchiveConfig(archiveName: string): Js5ArchiveConfig | null; + getArchiveConfig(archiveKeyOrName: number | string): Js5ArchiveConfig | null; + getArchiveConfig(archiveKeyOrName: number | string): Js5ArchiveConfig | null { + if (typeof archiveKeyOrName === 'string') { + return this._archiveConfig[archiveKeyOrName] || null; + } else { + return Object.values(this._archiveConfig).find(c => c.key === archiveKeyOrName) || null; + } + } + + getArchiveName(archiveKey: number): string | null { + const archiveEntries = Object.entries(this._archiveConfig); + for (const [ name, config ] of archiveEntries) { + if (config.key === archiveKey) { + return name; + } + } + + return null; + } + + get archiveConfig(): { [key: string]: Js5ArchiveConfig } { + return this._archiveConfig; + } + +} diff --git a/src/file-system/js5/js5-file.ts b/src/file-system/js5/js5-file.ts new file mode 100644 index 0000000..c4fd8e1 --- /dev/null +++ b/src/file-system/js5/js5-file.ts @@ -0,0 +1,22 @@ +import { Js5FileStore } from './js5-file-store'; +import { Js5Archive } from './js5-archive'; +import { Js5Group } from './js5-group'; +import { Js5FileBase } from './js5-file-base'; + + +export class Js5File extends Js5FileBase { + + readonly archive: Js5Archive; + readonly group: Js5Group; + + constructor( + fileStore: Js5FileStore, + fileKey: number, + group: Js5Group, + ) { + super(fileStore, 'FILE', fileKey, group.archive.index.key, group.index.key); + this.archive = group.archive; + this.group = group; + } + +} diff --git a/src/file-system/js5/js5-group.ts b/src/file-system/js5/js5-group.ts new file mode 100644 index 0000000..53ea6d5 --- /dev/null +++ b/src/file-system/js5/js5-group.ts @@ -0,0 +1,132 @@ +import { Js5FileStore } from './js5-file-store'; +import { Js5Archive } from './js5-archive'; +import { Js5File } from './js5-file'; +import { logger } from '@runejs/common'; +import { Js5FileBase } from './js5-file-base'; +import { Js5IndexEntity } from '../../db/js5'; + + +export class Js5Group extends Js5FileBase { + + readonly archive: Js5Archive; + readonly files: Map; + + constructor( + fileStore: Js5FileStore, + groupKey: number, + archive: Js5Archive, + ) { + super(fileStore, 'GROUP', groupKey, archive.index.key); + this.archive = archive; + this.files = new Map(); + } + + override validate(trackChanges: boolean = true): void { + super.validate(trackChanges); + + let groupModified: boolean = false; + + const { childCount } = this.index; + const newChildCount = this.files.size; + + if (childCount !== newChildCount) { + this.index.childCount = newChildCount; + groupModified = true; + } + + if (groupModified && trackChanges) { + logger.info(`Group ${this.index.name || this.index.key} child count has changed.`); + this.index.version++; + } + } + + async upsertFileIndexes(): Promise { + const fileIndexes = Array.from(this.files.values()).map(file => file.index); + await this.fileStore.database.upsertIndexes(fileIndexes); + } + + async loadFileIndexes(): Promise { + const fileIndexes = await this.fileStore.database.getIndexes({ + fileType: 'FILE', + archiveKey: this.archive.index.key, + groupKey: this.index.key, + }); + + if (!fileIndexes?.length) { + return; + } + + for (const fileIndex of fileIndexes) { + const fileKey = fileIndex.key; + + if (!this.files.has(fileKey)) { + const file = new Js5File(this.fileStore, fileKey, this); + file.index = fileIndex; + this.files.set(fileKey, file); + } + } + } + + async upsertFileData(): Promise { + const files = Array.from(this.files.values()); + const uncompressed = files.map(group => group.data).filter(data => data?.buffer && data?.buffer?.length !== 0); + const compressed = files.map(group => group.compressedData).filter(data => data?.buffer && data?.buffer?.length !== 0); + if (uncompressed.length) { + await this.fileStore.database.upsertAllUncompressedData(uncompressed); + } + if (compressed.length) { + await this.fileStore.database.upsertAllCompressedData(compressed); + } + } + + async getFile(fileKey: number): Promise; + async getFile(fileName: string): Promise; + async getFile(fileIdentifier: number | string): Promise; + async getFile(fileIdentifier: number | string): Promise { + let file: Js5File; + + if (typeof fileIdentifier === 'string') { + file = Array.from(this.files.values()).find( + file => file?.index?.name === fileIdentifier + ) || null; + } else { + file = this.files.get(fileIdentifier) || null; + } + + if (!file?.index) { + let fileEntity: Js5IndexEntity; + + if (typeof fileIdentifier === 'number' || /^\d*$/.test(fileIdentifier)) { + const fileKey = typeof fileIdentifier === 'string' ? parseInt(fileIdentifier, 10) : fileIdentifier; + fileEntity = await this.fileStore.database.getIndex({ + fileType: 'GROUP', + archiveKey: this.index.archiveKey, + groupKey: this.index.key, + key: fileKey + }); + } else { + fileEntity = await this.fileStore.database.getIndex({ + fileType: 'GROUP', + archiveKey: this.index.archiveKey, + groupKey: this.index.key, + name: String(fileIdentifier) + }); + } + + if (!file) { + file = new Js5File(this.fileStore, fileEntity.key, this); + file.index = fileEntity; + this.files.set(fileEntity.key, file); + } else { + file.index = fileEntity; + } + } + + return file; + } + + setFile(fileKey: number, file: Js5File): void { + this.files.set(fileKey, file); + } + +} diff --git a/src/file-system/js5/js5.ts b/src/file-system/js5/js5.ts new file mode 100644 index 0000000..e9a209e --- /dev/null +++ b/src/file-system/js5/js5.ts @@ -0,0 +1,886 @@ +import { join } from 'path'; +import { Buffer } from 'buffer'; +import { existsSync, readdirSync, readFileSync, statSync } from 'graceful-fs'; + +import { logger } from '@runejs/common'; +import { ByteBuffer } from '@runejs/common/buffer'; +import { getCompressionMethod, Gzip, Bzip2 } from '@runejs/common/compress'; +import { Xtea, XteaKeys, XteaConfig } from '@runejs/common/encrypt'; + +import { Js5FileStore, Js5Archive, Js5Group, Js5File } from '.'; +import { archiveFlags, ArchiveFormat } from '../../config'; +import { getXteaKeysByBuild } from '../../openrs2'; +import { PackedCacheFile } from '../packed'; + + +const dataFileName = 'main_file_cache.dat2'; +const indexFileNamePrefix = 'main_file_cache.idx'; +const mainIndexFileName = `${ indexFileNamePrefix }255`; + + +export class JS5 { + + readonly fileStore: Js5FileStore; + + localEncryptionKeys: Map; + openRS2EncryptionKeys: XteaConfig[]; + + private mainIndexFile: ByteBuffer; + private indexFiles: Map; + private dataFile: ByteBuffer; + + constructor(fileStore: Js5FileStore) { + this.fileStore = fileStore; + this.indexFiles = new Map(); + } + + readOpenRS2CacheFiles(cacheFiles: PackedCacheFile[]): void { + const dataFileBuffer = cacheFiles.find(file => file.name === dataFileName)?.data || null; + if (!dataFileBuffer?.length) { + throw new Error(`The main ${ dataFileName } data file could not be found.`); + } + + const mainIndexFileBuffer = cacheFiles.find(file => file.name === mainIndexFileName)?.data || null; + if (!mainIndexFileBuffer?.length) { + throw new Error(`The main ${ mainIndexFileName } index file could not be found.`); + } + + this.dataFile = new ByteBuffer(dataFileBuffer); + this.mainIndexFile = new ByteBuffer(mainIndexFileBuffer); + this.indexFiles.clear(); + + for (const cacheFile of cacheFiles) { + const fileName = cacheFile?.name; + + if (!fileName?.length || fileName === mainIndexFileName || fileName === dataFileName) { + continue; + } + + if (!fileName.startsWith(indexFileNamePrefix)) { + continue; + } + + if (!cacheFile?.data?.length) { + logger.error(`Index file ${ fileName } is empty!`); + continue; + } + + const indexString = fileName.substring(fileName.indexOf('.idx') + 4); + const archiveKey = Number(indexString); + + if (isNaN(archiveKey)) { + logger.error(`Index file ${ fileName } does not have a valid extension.`); + } + + this.indexFiles.set(archiveKey, new ByteBuffer(cacheFile.data)); + this.fileStore.createArchive(archiveKey); + } + + logger.info(`JS5 store file loaded for game build ${ this.fileStore.gameBuild }.`); + } + + readLocalCacheFiles(): void { + const js5StorePath = join(this.fileStore.fileStorePath, 'js5'); + + if (!existsSync(js5StorePath)) { + throw new Error(`${ js5StorePath } could not be found.`); + } + + const stats = statSync(js5StorePath); + if (!stats?.isDirectory()) { + throw new Error(`${ js5StorePath } is not a valid directory.`); + } + + const storeFileNames = readdirSync(js5StorePath); + + if (storeFileNames.indexOf(dataFileName) === -1) { + throw new Error(`The main ${ dataFileName } data file could not be found.`); + } + + if (storeFileNames.indexOf(mainIndexFileName) === -1) { + throw new Error(`The main ${ mainIndexFileName } index file could not be found.`); + } + + const dataFilePath = join(js5StorePath, dataFileName); + const mainIndexFilePath = join(js5StorePath, mainIndexFileName); + + this.dataFile = new ByteBuffer(readFileSync(dataFilePath)); + this.mainIndexFile = new ByteBuffer(readFileSync(mainIndexFilePath)); + this.indexFiles.clear(); + + for (const fileName of storeFileNames) { + if (!fileName?.length || fileName === mainIndexFileName || fileName === dataFileName) { + continue; + } + + if (!fileName.startsWith(indexFileNamePrefix)) { + continue; + } + + const indexString = fileName.substring(fileName.indexOf('.idx') + 4); + const archiveKey = Number(indexString); + + if (isNaN(archiveKey)) { + logger.error(`Index file ${ fileName } does not have a valid extension.`); + } + + this.indexFiles.set(archiveKey, new ByteBuffer(readFileSync(join(js5StorePath, fileName)))); + this.fileStore.createArchive(archiveKey); + } + + logger.info(`JS5 store file loaded for game build ${ this.fileStore.gameBuild }.`); + } + + unpack(file: Js5Group | Js5Archive): Buffer | null { + const fileIndex = file.index; + const fileKey = fileIndex.key; + const archiveKey: number = file instanceof Js5Archive ? 255 : file.archive.index.key; + const archiveName: string = file instanceof Js5Archive ? 'main' : file.archive.index.name; + + const indexChannel: ByteBuffer = archiveKey !== 255 ? + this.indexFiles.get(archiveKey) : this.mainIndexFile; + + if (archiveKey === 255 && fileKey === 255) { + return null; + } + + const indexDataLength = 6; + const dataChannel = this.dataFile; + + indexChannel.readerIndex = 0; + dataChannel.readerIndex = 0; + + let pointer = fileKey * indexDataLength; + + if (pointer < 0 || pointer >= indexChannel.length) { + logger.error(`File ${ fileKey } was not found within the ${ archiveName } archive index file.`); + return null; + } + + const fileIndexData = new ByteBuffer(indexDataLength); + indexChannel.copy(fileIndexData, 0, pointer, pointer + indexDataLength); + + if (fileIndexData.readable !== indexDataLength) { + logger.error(`Error extracting JS5 file ${ fileKey }: the end of the data stream was reached.`); + return null; + } + + fileIndex.fileSize = fileIndexData.get('int24', 'unsigned'); + const sectorNumber = fileIndexData.get('int24', 'unsigned'); + + if (fileIndex.fileSize <= 0) { + logger.warn(`JS5 file ${ fileKey } is empty or has been removed.`); + return null; + } + + const data = new ByteBuffer(fileIndex.fileSize); + const sectorDataLength = 512; + const sectorLength = 520; + + let sector = 0; + let remainingData = fileIndex.fileSize; + pointer = sectorNumber * sectorLength; + + do { + const temp = new ByteBuffer(sectorLength); + dataChannel.copy(temp, 0, pointer, pointer + sectorLength); + + if (temp.readable !== sectorLength) { + logger.error(`Error reading stripe for packed file ${ fileKey }, the end of the data stream was reached.`); + return null; + } + + const sectorFileKey = temp.get('short', 'unsigned'); + const sectorFilePartNumber = temp.get('short', 'unsigned'); + const sectorNumber = temp.get('int24', 'unsigned'); + const sectorIndexKey = temp.get('byte', 'unsigned'); + + const sectorData = new ByteBuffer(sectorDataLength); + temp.copy(sectorData, 0, temp.readerIndex, temp.readerIndex + sectorDataLength); + + if (remainingData > sectorDataLength) { + sectorData.copy(data, data.writerIndex, 0, sectorDataLength); + data.writerIndex = (data.writerIndex + sectorDataLength); + remainingData -= sectorDataLength; + + if (sectorIndexKey !== archiveKey) { + logger.error(`Archive index mismatch, expected archive ${ archiveKey } but found ${ sectorFileKey }`); + return null; + } + + if (sectorFileKey !== fileKey) { + logger.error(`File index mismatch, expected ${ fileKey } but found ${ sectorFileKey }.`); + return null; + } + + if (sectorFilePartNumber !== sector++) { + logger.error(`Error extracting JS5 file ${ fileKey }, file data is corrupted.`); + return null; + } + + pointer = sectorNumber * sectorLength; + } else { + sectorData.copy(data, data.writerIndex, 0, remainingData); + data.writerIndex = (data.writerIndex + remainingData); + remainingData = 0; + } + } while (remainingData > 0); + + if (data.length) { + file.compressedData.buffer = data.toNodeBuffer(); + } else { + file.compressedData.buffer = null; + fileIndex.fileError = 'FILE_MISSING'; + } + + return file.compressedData.buffer; + } + + readCompressedFileHeader(file: Js5Group | Js5Archive): { compressedLength: number, readerIndex: number } { + const fileDetails = file.index; + + if (!file.compressedData.buffer?.length) { + return { compressedLength: 0, readerIndex: 0 }; + } + + const compressedData = new ByteBuffer(file.compressedData.buffer); + + fileDetails.compressionMethod = getCompressionMethod( + compressedData.get('byte', 'unsigned')); + + const compressedLength = compressedData.get('int', 'unsigned'); + const readerIndex = compressedData.readerIndex; + + return { compressedLength, readerIndex }; + } + + decrypt(file: Js5Group | Js5Archive): Buffer { + const fileDetails = file.index; + const fileName = fileDetails.name; + + if (!file.compressedData.buffer?.length) { + logger.error(`Error decrypting file ${ fileName || fileDetails.key }, file data not found.`, + `Please ensure that the file has been unpacked from an existing JS5 file store using JS5.unpack(file);`); + return null; + } + + const archiveName = file instanceof Js5Archive ? 'main' : file.archive.index.name; + const archiveConfig = this.fileStore.archiveConfig[archiveName]; + + if (archiveConfig.encryption) { + const [ encryption, pattern ] = archiveConfig.encryption; + const patternRegex = new RegExp(pattern); + + // Only XTEA encryption is supported at this time + if (encryption !== 'xtea' || !patternRegex.test(fileName)) { + // FileBase name does not match the pattern, data should be unencrypted + return file.compressedData.buffer; + } + } else { + return file.compressedData.buffer; + } + + const gameBuild = this.fileStore.gameBuild; + let keySets: XteaKeys[] = []; + + const loadedKeys = this.getEncryptionKeys(fileName); + if (loadedKeys) { + if (!Array.isArray(loadedKeys)) { + keySets = [ loadedKeys ]; + } else { + keySets = loadedKeys; + } + } + + try { + const { compressedLength, readerIndex } = this.readCompressedFileHeader(file); + const encryptedData = new ByteBuffer(file.compressedData.buffer); + const keySet = keySets.find(keySet => keySet.gameBuild === gameBuild); + + if (Xtea.validKeys(keySet?.key)) { + const dataCopy = encryptedData.clone(); + dataCopy.readerIndex = readerIndex; + + let lengthOffset = readerIndex; + if (dataCopy.length - (compressedLength + readerIndex + 4) >= 2) { + lengthOffset += 2; + } + + const decryptedData = Xtea.decrypt(dataCopy, keySet.key, dataCopy.length - lengthOffset); + + if (decryptedData?.length) { + decryptedData.copy(dataCopy, readerIndex, 0); + dataCopy.readerIndex = readerIndex; + file.compressedData.buffer = dataCopy.toNodeBuffer(); + fileDetails.encrypted = false; + file.validate(false); + return file.compressedData.buffer; + } else { + logger.warn(`Invalid XTEA decryption keys found for file ` + + `${ fileName || fileDetails.key } using game build ${ gameBuild }.`); + fileDetails.fileError = 'MISSING_ENCRYPTION_KEYS'; + } + } else { + logger.warn(`No XTEA decryption keys found for file ` + + `${ fileName || fileDetails.key } using game build ${ gameBuild }.`); + fileDetails.fileError = 'MISSING_ENCRYPTION_KEYS'; + } + } catch (err) { + logger.error(err); + } + + return null; + } + + decompress(file: Js5Group | Js5Archive): Buffer | null { + const fileDetails = file.index; + + if (!file.compressedData.buffer?.length) { + return null; + } + + if (!this.decrypt(file)) { + fileDetails.encrypted = true; + return null; + } + + let data: Buffer; + + try { + const { compressedLength, readerIndex } = this.readCompressedFileHeader(file); + + // JS5.decrypt will set compressedData to the new decrypted data after completion + const compressedData = new ByteBuffer(file.compressedData.buffer); + compressedData.readerIndex = readerIndex; + + if (fileDetails.compressionMethod === 'none') { + // Uncompressed file + data = Buffer.alloc(compressedLength); + compressedData.copy(data, 0, compressedData.readerIndex, compressedLength); + compressedData.readerIndex = (compressedData.readerIndex + compressedLength); + } else { + // BZIP or GZIP compressed file + const decompressedLength = compressedData.get('int', 'unsigned'); + if (decompressedLength < 0) { + const errorPrefix = `Unable to decompress file ${ fileDetails.name || fileDetails.key }:`; + if (fileDetails.fileError === 'FILE_MISSING') { + logger.error(`${ errorPrefix } Missing file data.`); + } else { + logger.error(`${ errorPrefix } Missing or invalid XTEA key.`); + fileDetails.fileError = 'MISSING_ENCRYPTION_KEYS'; + } + } else { + const fileData = new ByteBuffer(compressedLength); + + compressedData.copy(fileData, 0, compressedData.readerIndex); + + data = fileDetails.compressionMethod === 'bzip2' ? + Bzip2.decompress(fileData) : + Gzip.decompress(fileData); + + compressedData.readerIndex = compressedData.readerIndex + compressedLength; + + if (data.length !== decompressedLength) { + logger.error(`Compression length mismatch.`); + data = null; + } + } + } + + if (data?.length) { + // Read the file footer, if it has one + if (compressedData.readable >= 2) { + fileDetails.version = compressedData.get('short', 'unsigned'); + } + } + } catch (error) { + logger.error(`Error decompressing file ${ fileDetails.name || fileDetails.key }: ` + + `${ error?.message ?? error }`); + data = null; + } + + file.data.buffer = data; + file.validate(false); + return file.data.buffer; + } + + async decodeGroup(group: Js5Group): Promise { + const groupDetails = group.index; + const { key: groupKey, name: groupName } = groupDetails; + const files = group.files; + + if (!group.data.buffer) { + this.decompress(group); + + if (!group.data.buffer) { + if (!groupDetails.fileError) { + logger.warn(`Unable to decode group ${ groupName || groupKey }.`); + } + return; + } + } + + const data = new ByteBuffer(group.data.buffer); + + if (groupDetails.childCount === 1) { + return; + } + + data.readerIndex = (data.length - 1); // EOF - 1 byte + + groupDetails.stripeCount = data.get('byte', 'unsigned'); + + data.readerIndex = (data.length - 1 - groupDetails.stripeCount * + groupDetails.childCount * 4); // Stripe data footer + + if (data.readerIndex < 0) { + logger.error(`Invalid reader index of ${ data.readerIndex } for group ` + + `${ groupName || groupKey }.`); + return; + } + + const fileSizeMap = new Map(); + const fileStripeMap = new Map(); + const fileDataMap = new Map(); + + for (const [ flatFileKey, ] of files) { + fileSizeMap.set(flatFileKey, 0); + fileStripeMap.set(flatFileKey, new Array(groupDetails.stripeCount)); + } + + for (let stripe = 0; stripe < groupDetails.stripeCount; stripe++) { + let currentLength = 0; + + for (const [ flatFileKey, ] of files) { + const delta = data.get('int'); + currentLength += delta; + + const fileStripes = fileStripeMap.get(flatFileKey); + const size = fileSizeMap.get(flatFileKey) + currentLength; + + fileStripes[stripe] = currentLength; + fileSizeMap.set(flatFileKey, size + currentLength); + } + } + + for (const [ flatFileKey, file ] of files) { + file.index.fileSize = fileSizeMap.get(flatFileKey); + file.index.stripeCount = groupDetails.stripeCount; + file.index.stripes = fileStripeMap.get(flatFileKey).join(','); + fileDataMap.set(flatFileKey, new ByteBuffer(file.index.fileSize)); + } + + data.readerIndex = 0; + + for (let stripe = 0; stripe < groupDetails.stripeCount; stripe++) { + for (const [ fileIndex, ] of files) { + let stripeLength = fileStripeMap.get(fileIndex)[stripe]; + let sourceEnd: number = data.readerIndex + stripeLength; + + if (data.readerIndex + stripeLength >= data.length) { + sourceEnd = data.length; + stripeLength = (data.readerIndex + stripeLength) - data.length; + } + + const stripeData = data.getSlice(data.readerIndex, stripeLength); + const fileData = fileDataMap.get(fileIndex); + + fileData.putBytes(stripeData); + + data.readerIndex = sourceEnd; + } + } + + for (const [ fileIndex, file ] of files) { + file.data.buffer = fileDataMap.get(fileIndex).toNodeBuffer(); + file.validate(false); + } + + group.validate(false); + } + + async decodeArchive(archive: Js5Archive): Promise { + const archiveDetails = archive.index; + + if (archiveDetails.key === 255) { + return; + } + + const archiveName = archiveDetails.name; + + logger.info(`Decoding archive ${ archiveName }...`); + + if (!archive.data.buffer) { + this.decompress(archive); + + if (!archive.data.buffer) { + logger.error(`Unable to decode archive ${ archiveName }.`); + return; + } + } + + const archiveData = new ByteBuffer(archive.data.buffer); + const format = archiveDetails.archiveFormat = archiveData.get('byte', 'unsigned'); + const mainDataType = format >= ArchiveFormat.smart ? 'smart_int' : 'short'; + archiveDetails.version = format >= ArchiveFormat.versioned ? archiveData.get('int') : 0; + const flags = archiveFlags(archiveData.get('byte', 'unsigned')); + const groupCount = archiveData.get(mainDataType, 'unsigned'); + const groups: Js5Group[] = new Array(groupCount); + let accumulator = 0; + + logger.info(`${ groupCount } groups were found within the ${ archiveName } archive.`); + + // Group index keys + for (let i = 0; i < groupCount; i++) { + const delta = archiveData.get(mainDataType, 'unsigned'); + const groupKey = accumulator += delta; + const group = groups[i] = new Js5Group(this.fileStore, groupKey, archive); + archive.setGroup(groupKey, group); + } + + // Group name hashes + if (flags.groupNames) { + for (const group of groups) { + group.index.nameHash = archiveData.get('int'); + group.index.name = this.fileStore.findFileName(group); + } + } + + // Compressed file data CRC32 checksums + for (const group of groups) { + group.index.compressedChecksum = archiveData.get('int'); + } + + // Decompressed file data CRC32 checksums + if (flags.decompressedCrcs) { + for (const group of groups) { + group.index.checksum = archiveData.get('int'); + } + } + + // File data whirlpool digests + if (flags.whirlpoolDigests) { + for (const group of groups) { + group.index.whirlpoolDigest = Buffer.alloc(512); + archiveData.getBytes(group.index.whirlpoolDigest, 512); + } + } + + // Group file sizes + if (flags.groupSizes) { + for (const group of groups) { + group.index.compressedFileSize = archiveData.get('int'); + group.index.fileSize = archiveData.get('int'); + } + } + + // Group version numbers + for (const group of groups) { + group.index.version = archiveData.get('int'); + } + + // Group child file counts + for (let i = 0; i < groupCount; i++) { + groups[i].index.childCount = archiveData.get('short', 'unsigned'); + } + + // Grouped file index keys + for (const group of groups) { + const fileCount = group.index.childCount || 0; + accumulator = 0; + + for (let i = 0; i < fileCount; i++) { + const delta = archiveData.get(mainDataType, 'unsigned'); + const childFileIndex = accumulator += delta; + const flatFile = new Js5File(this.fileStore, childFileIndex, group); + group.setFile(childFileIndex, flatFile); + } + } + + // Grouped file names + if (flags.groupNames) { + for (const group of groups) { + for (const [ , flatFile ] of group.files) { + flatFile.index.nameHash = archiveData.get('int'); + flatFile.index.name = this.fileStore.findFileName(flatFile); + } + } + } + + archive.validate(false); + } + + // @todo stubbed - 21/07/22 - Kiko + decodeMainIndex(): Buffer | null { + return null; + } + + // @todo stubbed - 21/07/22 - Kiko + pack(file: Js5Group | Js5Archive): Buffer | null { + return null; + } + + // @todo stubbed - 21/07/22 - Kiko + encrypt(file: Js5Group | Js5Archive): Buffer | null { + return null; + } + + compress(file: Js5Group | Js5Archive): Buffer | null { + const fileDetails = file.index; + + if (!file.data.buffer?.length) { + return null; + } + + const decompressedData = new ByteBuffer(file.data.buffer); + let data: ByteBuffer; + + if (fileDetails.compressionMethod === 'none') { + // uncompressed files + data = new ByteBuffer(decompressedData.length + 5); + + // indicate that no file compression is applied + data.put(0); + + // write the uncompressed file length + data.put(decompressedData.length, 'int'); + + // write the uncompressed file data + data.putBytes(decompressedData); + } else { + // compressed Bzip2 or Gzip file + + const compressedData: Buffer = fileDetails.compressionMethod === 'bzip2' ? + Bzip2.compress(decompressedData) : + Gzip.compress(decompressedData); + + const compressedLength: number = compressedData.length; + + data = new ByteBuffer(compressedData.length + 9); + + // indicate which type of file compression was used (1 or 2) + data.put(fileDetails.compressionMethod === 'bzip2' ? 1 : 2); + + // write the compressed file length + data.put(compressedLength, 'int'); + + // write the uncompressed file length + data.put(decompressedData.length, 'int'); + + // write the compressed file data + data.putBytes(compressedData); + } + + if (data?.length) { + file.compressedData.buffer = data.toNodeBuffer(); + } + + return file.compressedData.buffer; + } + + encodeGroup(group: Js5Group): Buffer | null { + const { files: fileMap, index, stripes } = group; + const fileCount = fileMap.size; + + // Single-file group + if (fileCount <= 1) { + group.data.buffer = fileMap.get(0)?.data?.buffer || null; + return group.data.buffer; + } + + // Multi-file group + const files = Array.from(fileMap.values()); + const fileSizes = files.map(file => file.index.fileSize); + const stripeCount = stripes?.length ?? 1; + + // Size of all individual files + 1 int per file containing it's size + // + 1 at the end for the total group stripe count + const groupSize = fileSizes.reduce( + (a, c) => a + c) + (stripeCount * fileCount * 4 + ) + 1; + + const groupBuffer = new ByteBuffer(groupSize); + + // Write child file data + for (let stripe = 0; stripe < stripeCount; stripe++) { + files.forEach(file => { + const fileData = file.data.buffer; + if (!fileData?.length) { + return; + } + + const fileBuffer = new ByteBuffer(fileData); + const stripeSize = file.stripes?.[stripe] ?? 0; + + if (stripeSize) { + const stripeData = fileBuffer.getSlice(fileBuffer.readerIndex, stripeSize); + fileBuffer.readerIndex = fileBuffer.readerIndex + stripeSize; + groupBuffer.putBytes(stripeData); + } + }); + } + + // Write child file stripe lengths + for (let stripe = 0; stripe < stripeCount; stripe++) { + let prevSize = 0; + files.forEach(file => { + const fileData = file.data.buffer; + if (!fileData?.length) { + return; + } + + const stripeLength = file.stripes?.[stripe] ?? 0; + groupBuffer.put(stripeLength - prevSize, 'int'); + prevSize = stripeLength; + }); + } + + // Write group child file stripe count + groupBuffer.put(stripeCount, 'byte'); + + if (groupBuffer.length) { + group.data.buffer = groupBuffer.toNodeBuffer(); + } else { + group.data.buffer = null; + } + + return group.data.buffer; + } + + // @todo support newer archive fields & formats - 21/07/22 - Kiko + encodeArchive(archive: Js5Archive): Buffer | null { + const { groups: groupMap, index } = archive; + const groups = Array.from(groupMap.values()); + const groupCount = groups.length; + const filesNamed = groups.filter(group => group.index.name !== null).length !== 0; + let lastFileKey = 0; + + // @todo calculate the proper size instead of using a set amount here - 21/07/22 - Kiko + const buffer = new ByteBuffer(1000 * 1000); + + // Write archive index file header + buffer.put(index.archiveFormat ?? ArchiveFormat.original); + buffer.put(filesNamed ? 1 : 0); + buffer.put(groupCount, 'short'); + + // Write group keys + groups.forEach(group => { + buffer.put(group.index.key - lastFileKey, 'short'); + lastFileKey = group.index.key; + }); + + // Write group names (if applicable) + if (filesNamed) { + groups.forEach(group => buffer.put(group.index.nameHash ?? -1, 'int')); + } + + // Write uncompressed crc values + groups.forEach(group => buffer.put(group.index.checksum ?? -1, 'int')); + + // Write group version numbers + groups.forEach(group => buffer.put(group.index.version, 'int')); + + // Write group child file counts + groups.forEach(group => buffer.put(group.files?.size ?? 1, 'short')); + + // Write group child file keys + groups.forEach(group => { + if (group.files.size > 1) { + lastFileKey = 0; + + group.files.forEach(file => { + buffer.put(file.index.key - lastFileKey, 'short'); + lastFileKey = file.index.key; + }); + } else { + buffer.put(0, 'short'); + } + }); + + // Write group child file names (if applicable) + if (filesNamed) { + groups.forEach(group => { + if (group.files.size > 1) { + lastFileKey = 0; + + group.files.forEach(file => + buffer.put(file.index.nameHash ?? -1, 'int')); + } else { + buffer.put(0, 'int'); + } + }); + } + + const archiveIndexData = buffer?.flipWriter(); + + if (archiveIndexData?.length) { + archive.data.buffer = archiveIndexData.toNodeBuffer(); + } else { + archive.data.buffer = null; + } + + return archive.data.buffer; + } + + encodeMainIndex(): ByteBuffer { + const archiveCount = this.fileStore.archives.size; + const fileSize = 4 * archiveCount; + + const data = new ByteBuffer(fileSize + 31); + + data.put(0); + data.put(fileSize, 'int'); + + for (let archiveIndex = 0; archiveIndex < archiveCount; archiveIndex++) { + data.put(this.fileStore.archives.get(archiveIndex).index.checksum, 'int'); + } + + this.mainIndexFile = data; + return this.mainIndexFile; + } + + getEncryptionKeys(fileName: string): XteaKeys | XteaKeys[] | null { + if (this.openRS2EncryptionKeys?.length) { + const fileKeys = this.openRS2EncryptionKeys.find(file => file.name === fileName); + if (fileKeys) { + return { + gameBuild: this.fileStore.gameBuild, + key: fileKeys.key + }; + } + } + + const keySets = this.localEncryptionKeys?.get(fileName); + if (!keySets) { + return null; + } + + if (this.fileStore.gameBuild !== undefined) { + return keySets.find(keySet => keySet.gameBuild === this.fileStore.gameBuild) ?? null; + } + + return keySets; + } + + async loadEncryptionKeys(): Promise { + if (/^\d+$/.test(this.fileStore.gameBuild)) { + const openRS2Keys = await getXteaKeysByBuild(parseInt(this.fileStore.gameBuild, 10)); + + if (openRS2Keys?.length) { + logger.info(`XTEA keys found for build ${ this.fileStore.gameBuild } on OpenRS2.org.`); + this.openRS2EncryptionKeys = openRS2Keys; + return; + } + } + + logger.warn(`XTEA keys not found for build ${ this.fileStore.gameBuild } on OpenRS2.org, using local XTEA key files instead.`); + const configPath = join(this.fileStore.fileStorePath, 'config', 'xtea'); + this.localEncryptionKeys = Xtea.loadKeys(configPath); + + if (!this.localEncryptionKeys.size) { + throw new Error(`Error reading encryption key lookup table. ` + + `Please ensure that the ${ configPath } file exists and is valid.`); + } + } + +} diff --git a/src/file-system/packed/index.ts b/src/file-system/packed/index.ts new file mode 100644 index 0000000..6c17315 --- /dev/null +++ b/src/file-system/packed/index.ts @@ -0,0 +1,2 @@ +export * from './packed-cache-format'; +export * from './packed-cache-file'; diff --git a/src/file-system/packed/packed-cache-file.ts b/src/file-system/packed/packed-cache-file.ts new file mode 100644 index 0000000..128c341 --- /dev/null +++ b/src/file-system/packed/packed-cache-file.ts @@ -0,0 +1,7 @@ +import { Buffer } from 'buffer'; + + +export interface PackedCacheFile { + name: string; + data: Buffer; +} diff --git a/src/file-system/packed/packed-cache-format.ts b/src/file-system/packed/packed-cache-format.ts new file mode 100644 index 0000000..a07c955 --- /dev/null +++ b/src/file-system/packed/packed-cache-format.ts @@ -0,0 +1,12 @@ +import { PackedCacheFile } from './packed-cache-file'; + + +export const getPackedCacheFormat = (cacheFiles: PackedCacheFile[]): 'jag' | 'js5' | 'unknown' => { + if (cacheFiles.find(file => file.name === 'main_file_cache.idx255')) { + return 'js5'; + } else if (cacheFiles.find(file => file.name === 'main_file_cache.dat')) { + return 'jag'; + } + + return 'unknown'; +}; diff --git a/src/flat-file.ts b/src/flat-file.ts deleted file mode 100644 index 272a4f3..0000000 --- a/src/flat-file.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { join } from 'path'; -import { existsSync, readFileSync } from 'graceful-fs'; -import { ByteBuffer, logger } from '@runejs/common'; - -import { FileIndexEntity } from './db'; -import { FileBreadcrumb, IndexedFile } from './indexed-file'; -import { FileState } from './file-state'; -import { isSet } from './util'; - - -export class FlatFile extends IndexedFile { - - public stripes: number[] = []; - public stripeCount: number = 1; - - public constructor(index: FileIndexEntity, breadcrumb?: Partial) { - super(index, breadcrumb); - - if(isSet(index.stripes)) { - this.stripes = index.stripes.split(',').map(n => Number(n)); - } - if(isSet(index.stripeCount)) { - this.stripeCount = index.stripeCount; - } - if(isSet(index.version)) { - this.version = index.version; - } - if(isSet(index.nameHash)) { - this.nameHash = index.nameHash; - } - } - - public override read(compress: boolean = false): ByteBuffer | null | Promise { - if(!this.group) { - throw new Error(`Flat file ${this.key} could not be read as it does not belong to any known groups.`); - } - - const filePath = this.path; - - if(!existsSync(filePath)) { - logger.error(`File not found: ${filePath}`); - this.setState(FileState.missing); - return null; - } - - let data: Buffer | null = null; - - try { - data = readFileSync(filePath); - } catch(error) { - logger.error(`Error reading file ${this.name || this.key } at ${filePath}:`, error); - data = null; - } - - if(!data) { - this.setState(FileState.corrupt); - } else if(!data.length) { - this.setState(FileState.empty); - } else { - const fileData = new ByteBuffer(data); - - if(!this.name) { - this.name = this.group.name || this.key; - } - - if(!this.nameHash) { - this.nameHash = this.group.nameHash || undefined; - } - - if(!this.stripes) { - const stripeStr = this.index.stripes; - if(stripeStr?.length) { - this.stripes = stripeStr.split(',')?.map(s => Number(s)) ?? undefined; - } - } - - if(!this.crc32) { - this.crc32 = this.index.crc32 ?? 0; - } - - if(!this.sha256) { - this.sha256 = this.index.sha256 ?? undefined; - } - - this.setData(fileData, FileState.raw); - - if(this.size !== this.index.size || this.sha256 !== this.generateSha256()) { - this._modified = true; - } - - return fileData; - } - - logger.error(`Error reading file data: ${filePath}`); - return null; - } - - public override get path(): string { - const groupPath = this.group?.path || null; - if(!groupPath) { - throw new Error(`Error generating file path; File ${this.key} has not been added to a group.`); - } - - if(this.group.fileCount === 1 || this.archive?.config?.flatten) { - return groupPath + this.type; - } else { - return join(groupPath, String(this.name || this.key)) + this.type; - } - } - - public override get outputPath(): string { - const groupOutputPath = this.group?.outputPath || null; - if(!groupOutputPath) { - throw new Error(`Error generating file output path; File ${this.key} has not been added to a group.`); - } - - if(this.group.fileCount === 1 || this.archive?.config?.flatten) { - return groupOutputPath + this.type; - } else { - return join(groupOutputPath, String(this.name || this.key) + this.type); - } - } - -} diff --git a/src/group.ts b/src/group.ts deleted file mode 100644 index 39590fb..0000000 --- a/src/group.ts +++ /dev/null @@ -1,336 +0,0 @@ -import { join } from 'path'; -import { existsSync, mkdirSync, readdirSync, rmSync, statSync } from 'graceful-fs'; -import { ByteBuffer, logger } from '@runejs/common'; - -import { FlatFile } from './flat-file'; -import { GroupIndexEntity } from './db'; -import { FileBreadcrumb, IndexedFile } from './indexed-file'; -import { FileState } from './file-state'; -import { isSet } from './util'; - - -export class Group extends IndexedFile { - - public readonly files: Map = new Map(); - public readonly fileSizes: Map = new Map(); - - public stripes: number[] = []; - public stripeCount: number = 1; - - private _fileCount: number = 0; - - public constructor(index: GroupIndexEntity, breadcrumb?: Partial) { - super(index, breadcrumb); - - if(isSet(index.stripes)) { - this.stripes = index.stripes.split(',').map(n => Number(n)); - } - if(isSet(index.stripeCount)) { - this.stripeCount = index.stripeCount; - } - if(isSet(index.version)) { - this.version = index.version; - } - if(isSet(index.nameHash)) { - this.nameHash = index.nameHash; - } - } - - public override decode(): ByteBuffer | null { - this.unpack(); - this.decompress(); - - if(this._fileCount === 1) { - const flatFile: FlatFile = Array.from(this.files.values())[0]; - flatFile.name = this.name; - flatFile.nameHash = this.nameHash; - flatFile.sha256 = this.sha256; - flatFile.crc32 = this.crc32; - flatFile.encryption = this.encryption; - flatFile.setData(this._data, this.state); - } else { - const dataLength = this._data?.length || 0; - - if(!dataLength || dataLength <= 0) { - logger.error(`Error decoding group ${this.key}`); - return null; - } - - this._data.readerIndex = (dataLength - 1); // EOF - - this.stripeCount = this._data.get('byte', 'unsigned'); - - this._data.readerIndex = (dataLength - 1 - this.stripeCount * this.files.size * 4); // Stripe data footer - - if(this._data.readerIndex < 0) { - logger.error(`Invalid reader index of ${this._data.readerIndex} for group ${this.archive.name}:${this.key}.`); - return null; - } - - for(let stripe = 0; stripe < this.stripeCount; stripe++) { - let currentLength = 0; - for(const [ fileIndex, file ] of this.files) { - const delta = this._data.get('int'); - currentLength += delta; - - if(!file.stripes?.length) { - file.stripes = new Array(this.stripeCount); - } - - let size = 0; - if(!this.fileSizes.has(fileIndex)) { - this.fileSizes.set(fileIndex, 0); - } else { - size = this.fileSizes.get(fileIndex); - } - - file.stripes[stripe] = currentLength; - this.fileSizes.set(fileIndex, size + currentLength); - } - } - - for(const [ fileIndex, file ] of this.files) { - const fileSize = this.fileSizes.get(fileIndex) || 0; - file.setData(new ByteBuffer(fileSize), FileState.raw); - file.size = fileSize; - } - - this._data.readerIndex = 0; - - for(let stripe = 0; stripe < this.stripeCount; stripe++) { - for(const [ , file ] of this.files) { - let stripeLength = file.stripes[stripe]; - let sourceEnd: number = this._data.readerIndex + stripeLength; - - if(this._data.readerIndex + stripeLength >= this._data.length) { - sourceEnd = this._data.length; - stripeLength = (this._data.readerIndex + stripeLength) - this._data.length; - } - - const stripeData = this._data.getSlice(this._data.readerIndex, stripeLength); - - file.data.putBytes(stripeData); - - this._data.readerIndex = sourceEnd; - } - } - - this.files.forEach(file => file.generateSha256()); - } - - this.setData(this._data, FileState.raw); - this._fileCount = this.files.size; - return this._data ?? null; - } - - public override encode(): ByteBuffer | null { - // Single-file group - if(this._fileCount === 1) { - const flatFile = Array.from(this.files.values())[0]; - this.setData(flatFile.data ?? new ByteBuffer([]), FileState.encoded); - return this._data; - } - - // Multi-file group - const fileData: ByteBuffer[] = Array.from(this.files.values()).map(file => file?.data ?? new ByteBuffer(0)); - const fileSizes = fileData.map(data => data.length); - const fileCount = this._fileCount; - const stripeCount = this.stripes?.length ?? 1; - - if(!stripeCount) { - return null; - } - - // Size of all individual files + 1 int per file containing it's size - // + 1 at the end for the total group stripe count - const groupSize = fileSizes.reduce((a, c) => a + c) + (stripeCount * fileCount * 4) + 1; - const groupBuffer = new ByteBuffer(groupSize); - - fileData.forEach(data => data.readerIndex = 0); - - // Write file content stripes - for(let stripe = 0; stripe < stripeCount; stripe++) { - for(const [ , file ] of this.files) { - if(!file?.data?.length) { - continue; - } - - const stripeSize = file.stripes[stripe]; - - if(stripeSize) { - const stripeData = file.data.getSlice(file.data.readerIndex, stripeSize); - file.data.readerIndex = file.data.readerIndex + stripeSize; - groupBuffer.putBytes(stripeData); - } - } - } - - for(let stripe = 0; stripe < stripeCount; stripe++) { - let prevSize = 0; - for(const [ , file ] of this.files) { - if(!file?.data?.length) { - continue; - } - - const stripeSize = file.stripes[stripe] ?? 0; - groupBuffer.put(stripeSize - prevSize, 'int'); - prevSize = stripeSize; - } - } - - groupBuffer.put(this.stripes?.length ?? 1, 'byte'); - - this.setData(groupBuffer.flipWriter(), FileState.encoded); - return this._data; - } - - public override async read(compress: boolean = false, readDiskFiles: boolean = true): Promise { - if(!this.index) { - logger.error(`Error reading group ${this.name} files: Group is not indexed, please re-index the ` + - `${this.archive.name} archive.`); - return null; - } - - if(this.index.data?.length) { - this.setData(this.index.data, FileState.compressed); - } - - let indexedFiles = await this.index.files; - - if(!indexedFiles?.length) { - // Single file indexes are not stored to save on DB space and read/write times - // So if a group has no children, assume it is a single-file group and create a single index for it - const { name, nameHash, version, size, crc32, sha256, stripes, stripeCount, archive, state } = this; - indexedFiles = this.index.files = [ this.indexService.validateFile({ - numericKey: 0, name, nameHash, version, size, crc32, sha256, stripes, stripeCount, - group: this, archive - }) ]; - } - - let childFileCount = 1; - - const groupPath = this.path; - - if(this.archive.versioned) { - this.version = this.index.version; - } - - if(existsSync(groupPath) && statSync(groupPath).isDirectory()) { - childFileCount = readdirSync(groupPath).length ?? 1; - } - - if(indexedFiles.length !== childFileCount) { - this._modified = true; - } - - this._fileCount = childFileCount; - - this.files.clear(); - this.fileSizes.clear(); - - // Load the group's files - for(const fileIndexData of indexedFiles) { - const file = new FlatFile(fileIndexData, { - group: this, archive: this.archive, store: this.store - }); - - this.files.set(file.key, file); - this.fileSizes.set(file.key, fileIndexData.size); - } - - this.stripeCount = (this.index as GroupIndexEntity).stripeCount || 1; - - // Read the content of each file within the group - if(readDiskFiles) { - Array.from(this.files.values()).forEach(file => file.read(compress)); - - if(this._fileCount === 1) { - // Single file group, set the group data to match the flat file data - const file = this.files.get('0'); - this.setData(file.data, file.state); - } - } - - this.encode(); - - const originalDigest = this.sha256; - this.generateSha256(); - - if(this.sha256 && originalDigest !== this.sha256) { - // logger.info(`Detected changes in file ${this.archive.name}:${groupName}.`); - this.index.sha256 = this.sha256; - this._modified = true; - } - - if(compress && this.state !== FileState.compressed) { - this.compress(); - } - - return this._data; - } - - public override write(): void { - if(!this._fileCount) { - logger.error(`Error writing group ${this.name}: Group is empty.`); - return; - } - - // logger.info(`Writing group ${this.name}...`); - - const groupPath = this.outputPath; - - if(existsSync(groupPath)) { - rmSync(groupPath, { recursive: true, force: true }); - } - - if(this.files.size > 1 && !this.archive.config.flatten) { - mkdirSync(groupPath, { recursive: true }); - } - - if(!this.archive.config.flatten) { - Array.from(this.files.values()).forEach(file => file.write()); - } else { - super.write(); - } - } - - public has(fileIndex: string | number): boolean { - return this.files.has(String(fileIndex)); - } - - public get(fileIndex: string | number): FlatFile | null { - return this.files.get(String(fileIndex)) ?? null; - } - - public set(fileIndex: string | number, file: FlatFile): void { - this.files.set(String(fileIndex), file); - this._fileCount = this.files.size; - } - - public override get path(): string { - const archivePath = this.archive?.path || null; - if(!archivePath) { - throw new Error(`Error generating group path; Archive path not provided to group ${this.key}.`); - } - - return join(archivePath, String(this.name || this.key)); - } - - public override get outputPath(): string { - const archiveOutputPath = this.archive?.outputPath || null; - if(!archiveOutputPath) { - throw new Error(`Error generating group output path; Archive output path not provided to group ${this.key}.`); - } - - const groupPath = join(archiveOutputPath, String(this.name || this.key)); - return this.archive.config.flatten ? groupPath + this.type : groupPath; - } - - public get fileCount(): number { - return this._fileCount; - } - - public get type(): string { - return this.archive?.config?.contentType ?? ''; - } -} diff --git a/src/http/config.ts b/src/http/config.ts new file mode 100644 index 0000000..712c0ae --- /dev/null +++ b/src/http/config.ts @@ -0,0 +1,4 @@ +import { InjectionToken } from '@decorators/di'; + + +export const FILESTORE_DIR = new InjectionToken('FILESTORE_DIR'); diff --git a/src/http/index.ts b/src/http/index.ts new file mode 100644 index 0000000..34b746c --- /dev/null +++ b/src/http/index.ts @@ -0,0 +1,3 @@ +export * from './server'; +export * from './js5/js5.service'; +export * from './js5/js5.controller'; diff --git a/src/http/js5/js5.controller.ts b/src/http/js5/js5.controller.ts new file mode 100644 index 0000000..cf6a18a --- /dev/null +++ b/src/http/js5/js5.controller.ts @@ -0,0 +1,132 @@ +import { Response as ExpressResponse } from 'express'; +import { Controller, Get, Params, Response } from '@decorators/express'; +import { Js5Service } from './js5.service'; +import { Inject } from '@decorators/di'; + + +@Controller('/js5') +export class Js5Controller { + + constructor(@Inject(Js5Service) private js5Service: Js5Service) { + } + + @Get('/:gameBuild/archives/:archiveIdentifier/groups/:groupIdentifier/files/:fileIdentifier/data') + async getArchiveGroupFileData( + @Response() res: ExpressResponse, + @Params('gameBuild') gameBuild: string | number, + @Params('archiveIdentifier') archiveIdentifier: string | number, + @Params('groupIdentifier') groupIdentifier: string | number, + @Params('fileIdentifier') fileIdentifier: string | number + ) { + const data = await this.js5Service.getArchiveGroupFileData(gameBuild, archiveIdentifier, groupIdentifier, fileIdentifier); + + res.writeHead(200, { + 'Content-Type': 'arraybuffer', + 'Content-Length': data.length, + 'Content-disposition': `attachment; filename=${fileIdentifier}` + }); + + res.end(data); + } + + @Get('/:gameBuild/archives/:archiveIdentifier/groups/:groupIdentifier/files/:fileIdentifier') + async getArchiveGroupFile( + @Response() res: ExpressResponse, + @Params('gameBuild') gameBuild: string | number, + @Params('archiveIdentifier') archiveIdentifier: string | number, + @Params('groupIdentifier') groupIdentifier: string | number, + @Params('fileIdentifier') fileIdentifier: string | number + ) { + res.send(await this.js5Service.getArchiveGroupFile(gameBuild, archiveIdentifier, groupIdentifier, fileIdentifier)); + } + + @Get('/:gameBuild/archives/:archiveIdentifier/groups/:groupIdentifier/files') + async getArchiveGroupFileList( + @Response() res: ExpressResponse, + @Params('gameBuild') gameBuild: string | number, + @Params('archiveIdentifier') archiveIdentifier: string | number, + @Params('groupIdentifier') groupIdentifier: string | number + ) { + res.send(await this.js5Service.getArchiveGroupFileList(gameBuild, archiveIdentifier, groupIdentifier)); + } + + @Get('/:gameBuild/archives/:archiveIdentifier/groups/:groupIdentifier/data') + async getArchiveGroupData( + @Response() res: ExpressResponse, + @Params('gameBuild') gameBuild: string | number, + @Params('archiveIdentifier') archiveIdentifier: string | number, + @Params('groupIdentifier') groupIdentifier: string | number + ) { + const data = await this.js5Service.getArchiveGroupData(gameBuild, archiveIdentifier, groupIdentifier); + + res.writeHead(200, { + 'Content-Type': 'arraybuffer', + 'Content-Length': data.length, + 'Content-disposition': `attachment; filename=${groupIdentifier}` + }); + + res.end(data); + } + + @Get('/:gameBuild/archives/:archiveIdentifier/groups/:groupIdentifier') + async getArchiveGroup( + @Response() res: ExpressResponse, + @Params('gameBuild') gameBuild: string | number, + @Params('archiveIdentifier') archiveIdentifier: string | number, + @Params('groupIdentifier') groupIdentifier: string | number + ) { + res.send(await this.js5Service.getArchiveGroup(gameBuild, archiveIdentifier, groupIdentifier)); + } + + @Get('/:gameBuild/archives/:archiveIdentifier/groups') + async getArchiveGroupList( + @Response() res: ExpressResponse, + @Params('gameBuild') gameBuild: string | number, + @Params('archiveIdentifier') archiveIdentifier: string | number + ) { + res.send(await this.js5Service.getArchiveGroupList(gameBuild, archiveIdentifier)); + } + + @Get('/:gameBuild/archives/:archiveIdentifier/data') + async getArchiveData( + @Response() res: ExpressResponse, + @Params('gameBuild') gameBuild: string | number, + @Params('archiveIdentifier') archiveIdentifier: string | number + ) { + const data = await this.js5Service.getArchiveData(gameBuild, archiveIdentifier); + + res.writeHead(200, { + 'Content-Type': 'arraybuffer', + 'Content-Length': data.length, + 'Content-disposition': `attachment; filename=${archiveIdentifier}` + }); + + res.end(data); + } + + @Get('/:gameBuild/archives/:archiveIdentifier') + async getArchive( + @Response() res: ExpressResponse, + @Params('gameBuild') gameBuild: string | number, + @Params('archiveIdentifier') archiveIdentifier: string | number + ) { + res.send(await this.js5Service.getArchive(gameBuild, archiveIdentifier)); + } + + @Get('/:gameBuild/archives') + async getArchiveList( + @Response() res: ExpressResponse, + @Params('gameBuild') gameBuild: string | number + ) { + res.send(await this.js5Service.getArchiveList(gameBuild)); + } + + @Get('/:gameBuild/archive-config') + async getArchiveConfig( + @Response() res: ExpressResponse, + @Params('gameBuild') gameBuild: string | number + ) { + res.send(await this.js5Service.getArchiveConfig(gameBuild)); + } + +} diff --git a/src/http/js5/js5.service.ts b/src/http/js5/js5.service.ts new file mode 100644 index 0000000..4157911 --- /dev/null +++ b/src/http/js5/js5.service.ts @@ -0,0 +1,137 @@ +import { Inject, Injectable } from '@decorators/di'; +import { FILESTORE_DIR } from '../config'; +import { Js5FileStore } from '../../file-system'; +import { Js5IndexEntity } from '../../db/js5'; +import { logger } from '@runejs/common'; +import { Buffer } from 'buffer'; +import { Js5ArchiveConfig } from '../../config'; + + +@Injectable() +export class Js5Service { + + readonly stores: Map; + + constructor( + @Inject(FILESTORE_DIR) private fileStoreDir: string + ) { + this.stores = new Map(); + logger.info('Js5Service initialized'); + } + + async getArchiveGroupFileData( + gameBuild: string | number, + archiveIdentifier: string | number, + groupIdentifier: string | number, + fileIdentifier: string | number + ): Promise { + const fileStore = await this.getFileStore(gameBuild); + const archive = await fileStore.getArchive(archiveIdentifier); + const group = await archive.getGroup(groupIdentifier); + const file = await group.getFile(fileIdentifier); + return file.getCompressedData(); + } + + async getArchiveGroupFile( + gameBuild: string | number, + archiveIdentifier: string | number, + groupIdentifier: string | number, + fileIdentifier: string | number + ): Promise { + const fileStore = await this.getFileStore(gameBuild); + const archive = await fileStore.getArchive(archiveIdentifier); + const group = await archive.getGroup(groupIdentifier); + const file = await group.getFile(fileIdentifier); + return file.index; + } + + async getArchiveGroupFileList( + gameBuild: string | number, + archiveIdentifier: string | number, + groupIdentifier: string | number + ): Promise { + const fileStore = await this.getFileStore(gameBuild); + const archive = await fileStore.getArchive(archiveIdentifier); + const group = await archive.getGroup(groupIdentifier); + + await group.loadFileIndexes(); + + return Array.from(group.files.values()).map(file => file.index); + } + + async getArchiveGroupData( + gameBuild: string | number, + archiveIdentifier: string | number, + groupIdentifier: string | number + ): Promise { + const fileStore = await this.getFileStore(gameBuild); + const archive = await fileStore.getArchive(archiveIdentifier); + const group = await archive.getGroup(groupIdentifier); + return group.getCompressedData(); + } + + async getArchiveGroup( + gameBuild: string | number, + archiveIdentifier: string | number, + groupIdentifier: string | number + ): Promise { + const fileStore = await this.getFileStore(gameBuild); + const archive = await fileStore.getArchive(archiveIdentifier); + const group = await archive.getGroup(groupIdentifier); + return group.index; + } + + async getArchiveGroupList( + gameBuild: string | number, + archiveIdentifier: string | number + ): Promise { + const fileStore = await this.getFileStore(gameBuild); + const archive = await fileStore.getArchive(archiveIdentifier); + + await archive.loadGroupIndexes(); + + return Array.from(archive.groups.values()).map(group => group.index); + } + + async getArchiveData( + gameBuild: string | number, + archiveIdentifier: string | number + ): Promise { + const fileStore = await this.getFileStore(gameBuild); + const archive = await fileStore.getArchive(archiveIdentifier); + return archive.getCompressedData(); + } + + async getArchive( + gameBuild: string | number, + archiveIdentifier: string | number + ): Promise { + const fileStore = await this.getFileStore(gameBuild); + const archive = await fileStore.getArchive(archiveIdentifier); + return archive.index; + } + + async getArchiveList(gameBuild: string | number): Promise { + const fileStore = await this.getFileStore(gameBuild); + return Array.from(fileStore.archives.values()).map(archive => archive.index); + } + + async getArchiveConfig(gameBuild): Promise<{ [key: string]: Js5ArchiveConfig }> { + const fileStore = await this.getFileStore(gameBuild); + return fileStore.archiveConfig; + } + + async getFileStore(gameBuild: string | number): Promise { + if (this.stores.has(gameBuild)) { + return this.stores.get(gameBuild); + } + + const fileStore = new Js5FileStore(gameBuild, this.fileStoreDir); + await fileStore.load(true); + + this.stores.set(gameBuild, fileStore); + + return fileStore; + } + +} diff --git a/src/http/server.ts b/src/http/server.ts new file mode 100644 index 0000000..beffcfb --- /dev/null +++ b/src/http/server.ts @@ -0,0 +1,56 @@ +import express from 'express'; +import { Container } from '@decorators/di'; +import { attachControllers } from '@decorators/express'; +import { logger } from '@runejs/common'; +import { ArgumentOptions, ScriptExecutor } from '../scripts/script-executor'; +import { FILESTORE_DIR } from './config'; +import { Js5Controller } from './js5/js5.controller'; + + +interface ServerOptions { + dir: string; + port: number; +} + + +const serverArgumentOptions: ArgumentOptions = { + dir: { + alias: 'd', + type: 'string', + default: './', + description: `The store root directory. Defaults to the current location.` + }, + port: { + alias: 'p', + type: 'number', + default: 8080, + description: `The port from which the HTTP server will be accessed. Defaults to 8080.` + }, +}; + + +new ScriptExecutor().executeScript(serverArgumentOptions, async ( + { dir, port } +) => { + const app = express(); + + app.use((req, res, next) => { + res.header('Access-Control-Allow-Origin', '*'); + res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); + next(); + }); + + app.set('json spaces', 4); + app.set('port', port); + + Container.provide([ + { provide: FILESTORE_DIR, useValue: dir }, + ]); + + attachControllers(app, [ Js5Controller ]); + + app.listen(port, async () => { + logger.info(`HTTP server listening at port ${port}.`); + }); +}); + diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index 00d2b30..0000000 --- a/src/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -export * from './indexed-file'; -export * from './flat-file'; -export * from './group'; -export * from './archive'; -export * from './store'; -export * from './file-state'; -export * from './util'; -export * from './db'; -export * from './config'; - diff --git a/src/indexed-file.ts b/src/indexed-file.ts deleted file mode 100644 index 0cd75d4..0000000 --- a/src/indexed-file.ts +++ /dev/null @@ -1,502 +0,0 @@ -import { writeFileSync } from 'graceful-fs'; -import { createHash } from 'crypto'; -import { ByteBuffer, logger } from '@runejs/common'; -import { Bzip2, CompressionMethod, getCompressionMethod, Gzip } from '@runejs/common/compress'; -import { EncryptionMethod, Xtea, XteaKeys } from '@runejs/common/encrypt'; -import { Crc32 } from '@runejs/common/crc32'; - -import { IndexEntity, IndexService } from './db'; -import { Store } from './store'; -import { Archive } from './archive'; -import { Group } from './group'; -import { isSet } from './util'; -import { FileState } from './file-state'; - - -export interface FileBreadcrumb { - store: Store; - archive: Archive; - group: Group; -} - - -export abstract class IndexedFile { - - public readonly key: string; - public readonly store: Store; - public readonly archive: Archive; - public readonly group: Group; - - public index: T; - public name: string = ''; - public nameHash: number = -1; - public version: number = 0; - public size: number = 0; - public crc32: number = -1; - public sha256: string = ''; - public encryption: EncryptionMethod | [ EncryptionMethod, string ] = 'none'; - public compression: CompressionMethod = 'none'; - public state: FileState = FileState.unloaded; - - protected _data: ByteBuffer | null = null; - protected _modified: boolean = false; - - protected constructor(index: T, breadcrumb?: Partial) { - this.index = index; - this.key = String(index.key); - - if(isSet(index.name)) { - this.name = index.name; - } - if(isSet(index.size)) { - this.size = index.size; - } - if(isSet(index.crc32)) { - this.crc32 = index.crc32; - } - if(isSet(index.sha256)) { - this.sha256 = index.sha256; - } - if(isSet(index['state'])) { - this.state = FileState[index['state']]; - } - - if(breadcrumb) { - const { store, archive, group } = breadcrumb; - - if(isSet(store)) { - this.store = store; - } - if(isSet(archive)) { - this.archive = archive; - } - if(isSet(group)) { - this.group = group; - } - } - - // Attempt to infer the archive or store that this file belongs to, if not provided in the options - - if(!this.archive) { - if(this.group?.archive) { - this.archive = this.group.archive; - } - } - - if(!this.store) { - if(this.archive?.store) { - this.store = this.archive.store; - } else if(this.group?.store) { - this.store = this.group.store; - } - } - - this.encryption = this.archive?.config?.encryption || 'none'; - this.compression = this.archive?.config?.compression || 'none'; - } - - public unpack(): ByteBuffer | null { - const archiveKey: number = this.archive ? this.archive.numericKey : 255; - const fileKey = this.numericKey; - const archiveName: string = this.archive ? this.archive.name : 'main'; - const indexChannel: ByteBuffer = archiveKey !== 255 ? - this.store.js5ArchiveIndexes.get(String(archiveKey)) : this.store.js5MainIndex; - - if(archiveKey === 255 && fileKey === 255) { - return null; - } - - const indexDataLength = 6; - const dataChannel = this.store.js5MainArchiveData; - - indexChannel.readerIndex = 0; - dataChannel.readerIndex = 0; - - let pointer = fileKey * indexDataLength; - - if(pointer < 0 || pointer >= indexChannel.length) { - logger.error(`File ${fileKey} was not found within the ${archiveName} archive index file.`); - return null; - } - - const fileIndexData = new ByteBuffer(indexDataLength); - indexChannel.copy(fileIndexData, 0, pointer, pointer + indexDataLength); - - if(fileIndexData.readable !== indexDataLength) { - logger.error(`Error extracting JS5 file ${fileKey}: the end of the data stream was reached.`); - return null; - } - - this.size = fileIndexData.get('int24', 'unsigned'); - const stripeCount = fileIndexData.get('int24', 'unsigned'); - - if(this.size <= 0) { - logger.warn(`Extracted JS5 file ${fileKey} has a recorded size of 0, no file data will be extracted.`); - return null; - } - - const data = new ByteBuffer(this.size); - const stripeDataLength = 512; - const stripeLength = 520; - - let stripe = 0; - let remaining = this.size; - pointer = stripeCount * stripeLength; - - do { - const temp = new ByteBuffer(stripeLength); - dataChannel.copy(temp, 0, pointer, pointer + stripeLength); - - if(temp.readable !== stripeLength) { - logger.error(`Error reading stripe for packed file ${fileKey}, the end of the data stream was reached.`); - return null; - } - - const stripeFileIndex = temp.get('short', 'unsigned'); - const currentStripe = temp.get('short', 'unsigned'); - const nextStripe = temp.get('int24', 'unsigned'); - const stripeArchiveIndex = temp.get('byte', 'unsigned'); - const stripeData = new ByteBuffer(stripeDataLength); - temp.copy(stripeData, 0, temp.readerIndex, temp.readerIndex + stripeDataLength); - - if(remaining > stripeDataLength) { - stripeData.copy(data, data.writerIndex, 0, stripeDataLength); - data.writerIndex = (data.writerIndex + stripeDataLength); - remaining -= stripeDataLength; - - if(stripeArchiveIndex !== archiveKey) { - logger.error(`Archive index mismatch, expected archive ${archiveKey} but found archive ${stripeFileIndex}`); - return null; - } - - if(stripeFileIndex !== fileKey) { - logger.error(`File index mismatch, expected ${fileKey} but found ${stripeFileIndex}.`); - return null; - } - - if(currentStripe !== stripe++) { - logger.error(`Error extracting JS5 file ${fileKey}, file data is corrupted.`); - return null; - } - - pointer = nextStripe * stripeLength; - } else { - stripeData.copy(data, data.writerIndex, 0, remaining); - data.writerIndex = (data.writerIndex + remaining); - remaining = 0; - } - } while(remaining > 0); - - if(data?.length) { - this.setData(data, FileState.compressed); - } else { - this.setData(null, FileState.missing); - } - - return data?.length ? data : null; - } - - public pack(): ByteBuffer | null { - return this._data; // @TODO needed for full re-packing of the data file - } - - public decode(): ByteBuffer | null { - return this._data; // stubbed - } - - public encode(): ByteBuffer | null { - return this._data; // stubbed - } - - public decompress(): ByteBuffer | null { - if(!this._data?.length) { - return null; - } - - this._data.readerIndex = 0; - - this.compression = getCompressionMethod(this._data.get('byte', 'unsigned')); - const compressedLength = this._data.get('int', 'unsigned'); - - const readerIndex = this._data.readerIndex; - - const compressedData = this.decrypt(); - compressedData.readerIndex = readerIndex; - let data: ByteBuffer; - - if(this.compression === 'none') { - // Uncompressed file - data = new ByteBuffer(compressedLength); - compressedData.copy(data, 0, compressedData.readerIndex, compressedLength); - compressedData.readerIndex = (compressedData.readerIndex + compressedLength); - } else { - // BZIP or GZIP compressed file - const decompressedLength = compressedData.get('int', 'unsigned'); - if(decompressedLength < 0) { - logger.error(this.encryption === 'xtea' ? `Missing or invalid XTEA key.` : - `Invalid decompressed file length: ${decompressedLength}`); - } else { - const decompressedData = new ByteBuffer(this.compression === 'bzip' ? - decompressedLength : (compressedData.length - compressedData.readerIndex + 2)); - - compressedData.copy(decompressedData, 0, compressedData.readerIndex); - - try { - data = this.compression === 'bzip' ? Bzip2.decompress(decompressedData) : Gzip.decompress(decompressedData); - - compressedData.readerIndex = compressedData.readerIndex + compressedLength; - - if(data.length !== decompressedLength) { - logger.error(`Compression length mismatch.`); - data = null; - } - } catch(error) { - if(this.state === FileState.encrypted) { - logger.error(`Unable to decrypt file ${this.name || this.key}.`); - this.archive?.incrementMissingEncryptionKeys(); - } else { - logger.error(`Unable to decompress file ${this.name || this.key}: ${error?.message ?? error}`); - } - data = null; - } - } - } - - // Read the file footer, if it has one - if(compressedData.readable >= 2) { - this.version = compressedData.get('short', 'unsigned'); - } - - if((data?.length ?? 0) > 0) { - this.setData(data, FileState.encoded); - } - - return this._data ?? null; - } - - public compress(): ByteBuffer | null { - if(!this._data?.length) { - return null; - } - - const decompressedData = this._data; - let data: ByteBuffer; - - if(this.compression === 'none') { - // uncompressed files - data = new ByteBuffer(decompressedData.length + 5); - - // indicate that no file compression is applied - data.put(0); - - // write the uncompressed file length - data.put(decompressedData.length, 'int'); - - // write the uncompressed file data - data.putBytes(decompressedData); - } else { - // compressed Bzip2 or Gzip file - - const compressedData: ByteBuffer = this.compression === 'bzip' ? - Bzip2.compress(decompressedData) : Gzip.compress(decompressedData); - - const compressedLength: number = compressedData.length; - - data = new ByteBuffer(compressedData.length + 9); - - // indicate which type of file compression was used (1 or 2) - data.put(this.compression === 'bzip' ? 1 : 2); - - // write the compressed file length - data.put(compressedLength, 'int'); - - // write the uncompressed file length - data.put(decompressedData.length, 'int'); - - // write the compressed file data - data.putBytes(compressedData); - } - - return this.setData(data, FileState.compressed); - } - - public decrypt(): ByteBuffer { - if(this.state === FileState.encrypted) { - // if(this.archive?.config?.encryption) { - // File name must match the given pattern to be encrypted - if(!this.name) { - throw new Error(`Error decrypting file ${this.key}: File name not found.`); - } - - if(Array.isArray(this.archive.config.encryption)) { - const [ encryption, pattern ] = this.archive.config.encryption; - const patternRegex = new RegExp(pattern); - - // Only XTEA encryption is supported for v1.0.0 - if(encryption !== 'xtea' || !patternRegex.test(this.name)) { - // File name does not match the pattern, data should be unencrypted - this.setState(FileState.decrypted); - return this._data; - } - } else if(this.archive.config.encryption !== 'xtea') { - // Only XTEA encryption is supported for v1.0.0 - this.setState(FileState.decrypted); - return this._data; - } - } - - const gameBuild = this.store.gameBuild ?? null; - - // XTEA requires that we know which game build is running so that we pick the correct keystore file - if(!gameBuild) { - if(this.store && !this.store.gameBuildMissing) { - this.store.setGameBuildMissing(); - logger.warn(`Game build must be supplied to decompress XTEA encrypted files.`, - `Please provide the game build using the --build argument.`); - } - - this.setState(FileState.decrypted); - return this._data; - } - - let keySets: XteaKeys[] = []; - - const loadedKeys = this.store.getEncryptionKeys(this.name); - if(loadedKeys) { - if(!Array.isArray(loadedKeys)) { - keySets = [ loadedKeys ]; - } else { - keySets = loadedKeys; - } - } - - this._data.readerIndex = 0; - - this.compression = getCompressionMethod(this._data.get('byte', 'unsigned')); - const compressedLength = this._data.get('int', 'unsigned'); - - const readerIndex = this._data.readerIndex; - - const keySet = keySets.find(keySet => keySet.gameBuild === gameBuild); - - if(Xtea.validKeys(keySet?.key)) { - const dataCopy = this._data.clone(); - dataCopy.readerIndex = readerIndex; - - let lengthOffset = readerIndex; - if(dataCopy.length - (compressedLength + readerIndex + 4) >= 2) { - lengthOffset += 2; - } - - const decryptedData = Xtea.decrypt(dataCopy, keySet.key, dataCopy.length - lengthOffset); - - if(decryptedData?.length) { - decryptedData.copy(dataCopy, readerIndex, 0); - dataCopy.readerIndex = readerIndex; - this.setState(FileState.decrypted); - return dataCopy; - } else { - logger.warn(`Invalid XTEA decryption keys found for file ${this.name || this.key} using game build ${gameBuild}.`); - } - } else { - // logger.warn(`No XTEA decryption keys found for file ${this.name || this.fileKey} using game build ${gameBuild}.`); - } - - return this._data; - } - - public read(compress?: boolean): ByteBuffer | null | Promise { - if(this.state === FileState.unloaded) { - this.setState(FileState.loaded); - } - - return this._data; - } - - public write(): void { - if(this._data?.length) { - writeFileSync(this.outputPath, Buffer.from(this._data)); - } - } - - public setData(data: Buffer, state: FileState): ByteBuffer | null; - public setData(data: ByteBuffer, state: FileState): ByteBuffer | null; - public setData(data: ByteBuffer | Buffer, state: FileState): ByteBuffer | null; - public setData(data: ByteBuffer | Buffer, state: FileState): ByteBuffer | null { - this.size = data?.length ?? 0; - - if(this.size) { - this._data = new ByteBuffer(data); - this._data.readerIndex = 0; - this._data.writerIndex = 0; - } else { - this._data = null; - } - - this.state = state; - - if(state === FileState.compressed) { - this.generateCrc32(); - } else if(state === FileState.raw || state === FileState.encoded) { - this.generateSha256(); - } - - return this._data; - } - - public generateCrc32(): number { - this.crc32 = this._data?.length ? Crc32.update(0, this.size, Buffer.from(this._data)) : -1; - return this.crc32; - } - - public generateSha256(): string { - this.sha256 = this._data?.length ? createHash('sha256') - .update(Buffer.from(this._data)).digest('hex') : ''; - return this.sha256; - } - - public setState(fileState: FileState): void { - this.state = fileState; - } - - public abstract get path(): string; - - public abstract get outputPath(): string; - - public get numericKey(): number { - return Number(this.key); - } - - public get named(): boolean { - if(!this.name) { - return false; - } - - return !/^\d+$/.test(this.name); - } - - public get hasNameHash(): boolean { - return isSet(this.nameHash) && !isNaN(this.nameHash) && this.nameHash !== -1; - } - - public get data(): ByteBuffer { - return this._data; - } - - public get indexService(): IndexService { - return this.store.indexService; - } - - public get type(): string { - return this.archive?.config?.contentType ?? ''; - } - - public get empty(): boolean { - return (this._data?.length ?? 0) !== 0; - } - - public get modified(): boolean { - return this.index.crc32 !== this.crc32 || this.index.sha256 !== this.sha256 || this.index.size !== this.size; - } - -} diff --git a/src/openrs2/index.ts b/src/openrs2/index.ts new file mode 100644 index 0000000..227d6fa --- /dev/null +++ b/src/openrs2/index.ts @@ -0,0 +1 @@ +export * from './openrs2'; diff --git a/src/openrs2/openrs2.ts b/src/openrs2/openrs2.ts new file mode 100644 index 0000000..5c716c3 --- /dev/null +++ b/src/openrs2/openrs2.ts @@ -0,0 +1,170 @@ +import axios from 'axios'; +import AdmZip from 'adm-zip'; +import { Buffer } from 'buffer'; +import { logger } from '@runejs/common'; +import { XteaConfig } from '@runejs/common/encrypt'; +import { PackedCacheFile } from '../file-system/packed'; + + +const openRS2Endpoint = 'https://archive.openrs2.org'; + + +export interface OpenRS2BuildNumber { + major: number; + minor: number | null; +} + + +export interface OpenRS2Cache { + id: number; + scope: 'runescape' | string; + game: 'runescape' | 'darkscape' | string; + environment: 'live' | 'beta' | string; + language: 'en' | 'de' | 'fr' | 'pt' | string; + builds: OpenRS2BuildNumber[]; + timestamp: Date; // ISO 8601 format + sources: string[]; + valid_indexes: number | null; + indexes: number | null; + valid_groups: number | null; + groups: number | null; + valid_keys: number | null; + keys: number | null; + size: number | null; + blocks: number | null; + disk_store_valid: boolean | null; +} + + +export const getOpenRS2CacheList = async (): Promise => { + const response = await axios.get( + `${ openRS2Endpoint }/caches.json` + ); + return response.data; +}; + + +export const getAvailableBuilds = async ( + scope: string = 'runescape', + game: string = 'runescape' +): Promise => { + const cacheList = (await getOpenRS2CacheList()) + .filter(cacheDetails => + // Filter out caches by desired scope and game + cacheDetails.scope === scope && cacheDetails.game === game + ) + .map(cacheDetails => { + // Map cache list to a list of build numbers + + if (!cacheDetails?.builds?.length) { + return [ -1 ]; + } + + // Map the build number arrays to actual numbers + return cacheDetails.builds.map(build => { + if (!build) { + return -1; + } + + if (build.minor !== null && build.minor > 0) { + return parseFloat(build.major + '.' + build.minor); + } else { + return build.major; + } + }); + }) + // Flatten the array + .flat() + // Filter out anything less than or equal to 0 + .filter(buildNumber => buildNumber > 0) + // Sort the list of numbers + .sort((a, b) => a - b); + + // Remove any duplicates + return [ ...new Set(cacheList) ]; +}; + + +export const getOpenRS2CacheDetailsByBuild = async ( + build: number, +): Promise => { + return (await getOpenRS2CacheList())?.find( + c => c.scope === 'runescape' && c.game === 'runescape' && c.builds.find(b => b.major === build) + ) || null; +}; + + +export const getOpenRS2CacheFilesById = async ( + id: number, + scope: string = 'runescape' +): Promise => { + const response = await axios.get( + `${ openRS2Endpoint }/caches/${ scope }/${ id }/disk.zip`, + { responseType: 'arraybuffer' } + ); + + if (!response?.data) { + return []; + } + + const zip = new AdmZip(Buffer.from(response.data, 'binary')); + return zip.getEntries().map(entry => ({ + name: entry.name, + data: entry.getData() + })); +}; + + +export const getOpenRS2CacheFilesByBuild = async ( + build: number +): Promise => { + logger.info(`Searching OpenRS2 for build ${ build }...`); + + const cacheList = (await getOpenRS2CacheList()) + ?.filter(c => c.scope === 'runescape' && c.game === 'runescape') || []; + + const desiredCacheInfo = cacheList.find(cacheDetails => { + for (const b of cacheDetails.builds) { + if (b.major === build) { + return true; + } + } + + return false; + }); + + if (desiredCacheInfo) { + logger.info(`Build ${ build } was found within the OpenRS2 archive, fetching data...`); + + const cacheFiles = await getOpenRS2CacheFilesById(desiredCacheInfo.id); + return cacheFiles?.length ? cacheFiles : null; + } else { + logger.error(`Build ${ build } was not found within the OpenRS2.org archive.`); + } + + return null; +}; + + +export const getXteaKeysById = async ( + id: number, + scope: string = 'runescape' +): Promise => { + const response = await axios.get( + `${ openRS2Endpoint }/caches/${ scope }/${ id }/keys.json` + ); + + return response?.data || []; +}; + + +export const getXteaKeysByBuild = async ( + build: number +): Promise => { + const cacheDetails = await getOpenRS2CacheDetailsByBuild(build); + if (!cacheDetails) { + return []; + } + + return await getXteaKeysById(cacheDetails.id, cacheDetails.scope); +}; diff --git a/src/scripts/builds.ts b/src/scripts/builds.ts new file mode 100644 index 0000000..9900fc4 --- /dev/null +++ b/src/scripts/builds.ts @@ -0,0 +1,70 @@ +import { ScriptExecutor, ArgumentOptions } from './script-executor'; +import { + getAvailableBuilds +} from '../openrs2'; +import { logger } from '@runejs/common'; + + +interface BuildsOptions { + scope: string; + game: string; + range?: number; +} + + +const buildsArgumentOptions: ArgumentOptions = { + scope: { + alias: 's', + type: 'string', + default: 'runescape', + description: `The scope to search for available builds for within OpenRS2.org. Defaults to 'runescape'.` + }, + game: { + alias: 'g', + type: 'string', + default: 'runescape', + description: `The game to search for available builds for within OpenRS2.org. Defaults to 'runescape'.` + }, + range: { + alias: 'r', + type: 'number', + description: `The lower number range of builds to search for, ie '400' to return builds between 400-499. Returns all build ranges by default.` + }, +}; + + +const buildsScript = async ( + { scope, game, range } +) => { + if (range && range % 100 !== 0) { + logger.error(`Invalid range of ${ range }: Range must be a multiple of 100.`); + return; + } + + const availableBuilds = await getAvailableBuilds(scope, game); + let msg = `Available builds on OpenRS2.org with scope ${ scope } and game ${ game }`; + + if (range) { + msg += ` for build range ${ range } - ${ range + 99 }`; + } + + logger.info(`${ msg }:`); + + let lastHundred = 0; + + for (const buildNumber of availableBuilds) { + const lower100 = Math.floor(buildNumber / 100) * 100; + + if (!range || range === lower100) { + if (lastHundred !== lower100) { + logger.info(`[ ${ lower100 } - ${ lower100 + 99 } ]`); + lastHundred = lower100; + } + + logger.info(String(buildNumber)); + } + } +}; + + +new ScriptExecutor().executeScript(buildsArgumentOptions, buildsScript); diff --git a/src/scripts/dev.ts b/src/scripts/dev.ts new file mode 100644 index 0000000..090d34d --- /dev/null +++ b/src/scripts/dev.ts @@ -0,0 +1,60 @@ +import { writeFileSync, existsSync, mkdirSync } from 'fs'; +import { logger } from '@runejs/common'; +import { JagInterfaceArchive } from '../file-system/jag/content/archives/interfaces/jag-interface-archive'; +import { join } from 'path'; +import { Js5FileStore, JagFileStore } from '../file-system'; + + +const saveInterfaces = async (store: JagFileStore) => { + logger.info(`Decoding game interfaces...`); + + const interfaceArchive = new JagInterfaceArchive(store); + + await interfaceArchive.decodeAll(); + + logger.info(`${interfaceArchive.interfaces.size} interfaces decoded. Saving interface entities...`); + + await interfaceArchive.saveAll(); +}; + + +const dumpInterfaceFile = (store: JagFileStore) => { + const archive = store.getCache('archives') + .getArchive('interface.jag'); + + if (!archive) { + throw new Error('interface.jag archive is not loaded!'); + } + + const dataFile = archive.getFile('data'); + const binaryData = dataFile?.data?.buffer; + if (!binaryData?.length) { + throw new Error('interface.jag data file is not loaded!'); + } + + const outputDir = join('.', 'unpacked', 'jag', 'interface.jag'); + if (!existsSync(outputDir)) { + mkdirSync(outputDir, { recursive: true }); + } + + const outputFile = join(outputDir, 'data'); + + logger.info(`Writing file ${outputFile}`); + + writeFileSync(outputFile, binaryData); +}; + + +const dev = async () => { + const start = Date.now(); + + const store = new JagFileStore(327); + await store.load(true, true, true); + + await saveInterfaces(store); + + const end = Date.now(); + logger.info(`Operations completed in ${(end - start) / 1000} seconds.`); +}; + +dev().catch(console.error); diff --git a/src/scripts/index.ts b/src/scripts/index.ts deleted file mode 100644 index 5848b19..0000000 --- a/src/scripts/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './script-executor'; diff --git a/src/scripts/indexer.ts b/src/scripts/indexer.ts index 114e786..e8ebb81 100644 --- a/src/scripts/indexer.ts +++ b/src/scripts/indexer.ts @@ -1,117 +1,349 @@ -import { join } from 'path'; -import { existsSync, mkdirSync } from 'graceful-fs'; import { logger } from '@runejs/common'; - -import { Store, StoreFormat } from '../index'; -import { ScriptExecutor, ArgumentOptions } from './index'; +import { ScriptExecutor, ArgumentOptions } from './script-executor'; +import { Js5FileStore } from '../file-system/js5'; +import { archives, caches, JagArchive, JagFileStore } from '../file-system/jag'; +import { + getOpenRS2CacheFilesByBuild, +} from '../openrs2'; +import { PackedCacheFile, getPackedCacheFormat } from '../file-system/packed'; +import { + JagInterfaceArchive +} from '../file-system/jag/content/archives/interfaces/jag-interface-archive'; interface IndexerOptions { dir: string; - format: StoreFormat | 'flat' | 'js5'; + format: 'flat' | 'packed'; archive: string; build: string; } const indexerArgumentOptions: ArgumentOptions = { + archive: { + alias: 'a', + type: 'string', + default: 'main', + description: `The archive to index. Defaults to 'main', which will index all store archives one by one. Specify an archive name to index a single archive.` + }, + build: { + alias: 'b', + type: 'string', + default: '435', + description: `The game build (revision) that the store should belong to, also known as the game build number. Defaults to '435', a game build from late October, 2006.` + }, dir: { - alias: 'd', type: 'string', default: './', + alias: 'd', + type: 'string', + default: './', description: `The store root directory. Defaults to the current location.` }, format: { - alias: 'f', type: 'string', default: 'unpacked', choices: [ 'unpacked', 'packed', 'flat', 'js5' ], - description: `The format of the store to index, either 'unpacked' (flat files) or 'packed' (JS5 format). Defaults to 'unpacked'.` + alias: 'f', + type: 'string', + default: 'packed', + choices: [ 'packed', 'flat' ], + description: `The format of the store to index, either 'packed' (JAG or JS5 format) or 'flat' (flat files). Defaults to 'packed'.` }, - archive: { - alias: 'a', type: 'string', default: 'main', - description: `The archive to index. Defaults to 'main', which will index all store archives one by one. Specify an archive name to index a single archive.` + source: { + alias: 's', + type: 'string', + default: 'openrs2', + choices: [ 'openrs2', 'local' ], + description: `The store source location - either 'openrs2' to pull cache and xtea files from OpenRS2.org or 'local' for caches and xtea files stored locally.` }, - build: { - alias: 'b', type: 'string', default: '435', - description: `The game build (revision) that the store should belong to, also known as the game build number. Defaults to '435', a game build from late October, 2006.` +}; + + +const indexJS5Store = async (store: Js5FileStore) => { + logger.info(`Unpacking archives from JS5 store...`); + + for (const [ , archive ] of store.archives) { + store.js5.unpack(archive); + } + + logger.info(`Decoding JS5 archives...`); + + for (const [ , archive ] of store.archives) { + await store.js5.decodeArchive(archive); + } + + logger.info(`Saving archive indexes...`); + + for (const [ , archive ] of store.archives) { + await archive.saveIndex(); + } + + logger.info(`Unpacking groups from JS5 store...`); + + for (const [ , archive ] of store.archives) { + for (const [ , group ] of archive.groups) { + store.js5.unpack(group); + } + + logger.info(`Finished unpacking archive ${ archive.index.name } groups.`); + } + + logger.info(`Decoding JS5 groups...`); + + for (const [ , archive ] of store.archives) { + for (const [ , group ] of archive.groups) { + await store.js5.decodeGroup(group); + } + + logger.info(`Finished decoding archive ${ archive.index.name } groups.`); + } + + logger.info(`Saving group indexes...`); + + for (const [ , archive ] of store.archives) { + await archive.upsertGroupIndexes(); + } + + logger.info(`Saving flat file indexes...`); + + for (const [ , archive ] of store.archives) { + for (const [ , group ] of archive.groups) { + await group.upsertFileIndexes(); + } + } + + logger.info(`Saving archive data...`); + + for (const [ , archive ] of store.archives) { + await archive.saveUncompressedData(); + await archive.saveCompressedData(); + } + + logger.info(`Saving group data...`); + + for (const [ , archive ] of store.archives) { + await archive.upsertGroupData(); + } + + logger.info(`Saving flat file data...`); + + for (const [ , archive ] of store.archives) { + for (const [ , group ] of archive.groups) { + await group.upsertFileData(); + } + } +}; + + +const indexJS5Archive = async (store: Js5FileStore, archiveName: string) => { + const archive = await store.getArchive(archiveName); + + if (!archive) { + logger.error(`Archive ${ archiveName } was not found.`); + return; + } + + logger.info(`Unpacking archive ${ archiveName } from JS5 store...`); + + store.js5.unpack(archive); + + logger.info(`Decoding archive ${ archiveName }...`); + + await store.js5.decodeArchive(archive); + + logger.info(`Saving archive ${ archiveName } index...`); + + await archive.saveIndex(); + + logger.info(`Unpacking groups from archive ${ archiveName }...`); + + for (const [ , group ] of archive.groups) { + store.js5.unpack(group); + } + + logger.info(`Decoding archive ${ archiveName } groups...`); + + for (const [ , group ] of archive.groups) { + await store.js5.decodeGroup(group); + } + + logger.info(`Saving group indexes...`); + + await archive.upsertGroupIndexes(); + + logger.info(`Saving flat file indexes...`); + + for (const [ , group ] of archive.groups) { + await group.upsertFileIndexes(); + } + + logger.info(`Saving archive ${ archiveName } data...`); + + await archive.saveUncompressedData(); + await archive.saveCompressedData(); + + logger.info(`Saving group data...`); + + await archive.upsertGroupData(); + + logger.info(`Saving flat file data...`); + + for (const [ , group ] of archive.groups) { + await group.upsertFileData(); } }; -async function indexFiles(store: Store, args: IndexerOptions): Promise { - const argDebugString = args ? Array.from(Object.entries(args)) - .map(([ key, val ]) => `${key} = ${val}`).join(', ') : ''; +const indexJagStore = async (store: JagFileStore) => { + logger.info(`Decoding JAG store indexes...`); - const { archive: archiveName } = args; + const indexNames = Object.keys(caches); + for (const indexName of indexNames) { + store.jag.decodeCache(indexName); + } + + logger.info(`Saving JAG caches...`); - let format = args.format; - if(format === 'js5') { - format = 'packed'; - } else if(format === 'flat') { - format = 'unpacked'; + for (const [ , cache ] of store.caches) { + await cache.saveIndex(); } - if(format === 'packed') { - store.loadPackedStore(); - } else { - const outputDir = store.outputPath; - if(!existsSync(outputDir)) { - mkdirSync(outputDir, { recursive: true }); + for (const [, cache ] of store.caches) { + logger.info(`Unpacking JAG files for index ${cache.index.name}...`); + + for (const [ , file ] of cache.files) { + store.jag.unpack(file); } } - if(archiveName === 'main') { - logger.info(`Indexing ${format} store with arguments:`, argDebugString); + logger.info(`Decoding JAG archives...`); - if(format === 'unpacked') { - await store.read(); - } else if(format === 'packed') { - store.decode(true); + const archiveIndex = store.getCache(caches.archives); + + for (const [ , archive ] of archiveIndex.files) { + if (archive instanceof JagArchive) { + logger.info(`Decoding archive ${archive.index.name}...`); + store.jag.decodeArchive(archive); } + } - store.encode(true); - store.compress(true); + logger.info(`Saving JAG file indexes...`); - await store.saveIndexData(true, true, true); - } else { - logger.info(`Indexing ${format} archive ${archiveName} with arguments:`, argDebugString); + for (const [, index ] of store.caches) { + await index.upsertFileIndexes(); + } - const archive = store.find(archiveName); + logger.info(`Saving JAG archive file indexes...`); - if(format === 'unpacked') { - await archive.read(false); - } else if(format === 'packed') { - archive.decode(); + for (const [ , archive ] of archiveIndex.files) { + if (archive instanceof JagArchive) { + await archive.upsertFileIndexes(); } + } - archive.encode(true); - archive.compress(true); + logger.info(`Saving JAG cache data...`); - await store.saveIndexData(false, false, false); - await archive.saveIndexData(true, true); + for (const [ , cache ] of store.caches) { + await cache.saveCompressedData(); + await cache.saveUncompressedData(); } -} + logger.info(`Saving JAG cache file data...`); -new ScriptExecutor().executeScript(indexerArgumentOptions, async (terminal, args) => { - const start = Date.now(); - logger.info(`Indexing store...`); + for (const [ , cache ] of store.caches) { + await cache.upsertFileData(); + } - const { build, dir } = args; + logger.info(`Saving JAG archive file data...`); - const logDir = join(dir, 'logs'); + for (const [ , archive ] of archiveIndex.files) { + if (archive instanceof JagArchive) { + await archive.upsertFileData(); + } + } + + const saveInterfaces = async (store: JagFileStore) => { + logger.info(`Decoding game interfaces...`); + + const interfaceArchive = new JagInterfaceArchive(store); + + await interfaceArchive.decodeAll(); + + logger.info(`${interfaceArchive.interfaces.size} interfaces decoded. Saving interface entities...`); + + await interfaceArchive.saveAll(); + }; + + await saveInterfaces(store); +}; + + +const indexJagArchive = async (store: JagFileStore, archiveName: string) => { + // @todo 18/07/22 - Kiko +}; + + +const indexerScript = async ( + { build, dir, format, archive: archiveName, source } +) => { + const start = Date.now(); + const numericBuildNumber: number = /^\d+$/.test(build) ? parseInt(build, 10) : -1; + let cacheFiles: PackedCacheFile[] | 'local' = 'local'; - if(!existsSync(logDir)) { - mkdirSync(logDir, { recursive: true }); + if (source === 'openrs2') { + if (numericBuildNumber) { + const openRS2CacheFiles = await getOpenRS2CacheFilesByBuild(numericBuildNumber); + if (!openRS2CacheFiles?.length) { + return; + } + + cacheFiles = openRS2CacheFiles; + } else { + logger.error(`A numeric build number must be used in order to pull cache information from OpenRS2.org.`); + return; + } } - logger.destination(join(logDir, `index_${build}.log`)); + const storeType = cacheFiles !== 'local' ? getPackedCacheFormat(cacheFiles) : 'flat'; + + logger.info(`Indexing ${ storeType === 'flat' ? storeType : storeType.toUpperCase() } file store...`); + + if (storeType === 'js5') { + const store = new Js5FileStore(numericBuildNumber !== -1 ? numericBuildNumber : build, dir); + await store.load(); + + if (cacheFiles === 'local') { + store.js5.readLocalCacheFiles(); + } else { + store.js5.readOpenRS2CacheFiles(cacheFiles); + } + + if (archiveName === 'main') { + await indexJS5Store(store); + } else { + await indexJS5Archive(store, archiveName); + } + + await store.closeDatabase(); + } else if (storeType === 'jag') { + const store = new JagFileStore(numericBuildNumber !== -1 ? numericBuildNumber : build, dir); + await store.load(); - const store = await Store.create(build, dir); + if (cacheFiles === 'local') { + store.jag.readLocalPackedCacheFiles(); + } else { + store.jag.readOpenRS2PackedCacheFiles(cacheFiles); + } - await indexFiles(store, args); + if (archiveName === 'main') { + await indexJagStore(store); + } else { + await indexJagArchive(store, archiveName); + } - logger.boom.flushSync(); - logger.boom.end(); + await store.closeDatabase(); + } else if (format === 'flat') { + // @todo 18/07/22 - Kiko + } + + logger.info(`Indexing completed in ${ (Date.now() - start) / 1000 } seconds.`); +}; - const end = Date.now(); - logger.info(`Indexing completed in ${(end - start) / 1000} seconds.`); - process.exit(0); -}); +new ScriptExecutor().executeScript(indexerArgumentOptions, indexerScript); diff --git a/src/scripts/name-hasher.ts b/src/scripts/name-hasher.ts new file mode 100644 index 0000000..e352606 --- /dev/null +++ b/src/scripts/name-hasher.ts @@ -0,0 +1,169 @@ +import { logger } from '@runejs/common'; +import { NameHasher } from '../config'; +import { join } from 'path'; + + +// @todo optimize this thing - 08/08/22 - Kiko +const bruteForcer = async () => { + const hashes = [ + 216335554, + 216316762, + ]; + + const validChars = [ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', + 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '_', '-' + ]; + + const validCharCodes = validChars.map(s => s.charCodeAt(0)); + + const INT_MAX = 2147483648; + + // Emulate Java's INT overflow-wrapping + const int32 = (value: number): number => { + while (value > INT_MAX) { + const diff = value - INT_MAX; + value = -INT_MAX + diff; + } + + while (value < -INT_MAX) { + const diff = Math.abs(value) - INT_MAX; + value = INT_MAX - diff; + } + + return value; + } + + const addToHash = (s: string, hash: number): number => { + for (let j = 0; j < s.length; j++) { + hash = int32((hash * 61 + s.charCodeAt(j)) - 32); + } + + return hash; + }; + + const getMatch = (hash: number): number => { + for (let i = 0; i < hashes.length; i++) { + if (hashes[i] === hash) { + return i; + } + } + + return -1; + }; + + const getHashForName = (dataName: string): number => { + let dataNameHash = 0; + for (let j = 0; j < dataName.length; j++) { + dataNameHash = int32((dataNameHash * 61 + dataName.charCodeAt(j)) - 32); + } + return dataNameHash; + }; + + const createString = (...charCodes: number[]): string => { + let result = ''; + for (const charCode of charCodes) { + const i = validCharCodes.indexOf(charCode); + result += validChars[i]; + } + + return result; + }; + + const bruteForceHash = (): void => { + let l1hash; + let l2hash; + let l3hash; + let l4hash; + let l5hash; + let l6hash; + let l7hash; + let l8hash; + let l9hash; + let hash; + + for (const c1 of validCharCodes) { + l1hash = c1 - 32; + for (const c2 of validCharCodes) { + l2hash = int32(l1hash * 61 + c2 - 32); + for (const c3 of validCharCodes) { + l3hash = int32(l2hash * 61 + c3 - 32); + for (const c4 of validCharCodes) { + l4hash = int32(l3hash * 61 + c4 - 32); + const resultString = createString(c1, c2, c3, c4); + hash = addToHash(".DAT", l4hash); + if (getMatch(hash) !== -1) { + logger.info(resultString + '.DAT : ' + hash); + } + + hash = addToHash(".IDX", l4hash); + if (getMatch(hash) !== -1) { + logger.info(resultString + '.IDX : ' + hash); + } + if (getMatch(l4hash) !== -1) { + logger.info(resultString + ' : ' + hash); + } + // for (const c5 of validCharCodes) { + // l5hash = int32(l4hash * 61 + c5 - 32); + // for (const c6 of validCharCodes) { + // l6hash = int32(l5hash * 61 + c6 - 32); + // for (const c7 of validCharCodes) { + // l7hash = int32(l6hash * 61 + c7 - 32); + // for (const c8 of validCharCodes) { + // l8hash = int32(l7hash * 61 + c8 - 32); + // for (const c9 of validCharCodes) { + // l9hash = int32(l8hash * 61 + c9 - 32); + // hash = addToHash(".DAT", l9hash); + // const resultString = createString(c1, c2, c3, c4, c5, c6, c7, c8, c9); + // if (getMatch(hash) !== -1) { + // logger.info(resultString + '.DAT : ' + hash); + // } + // + // hash = addToHash(".IDX", l9hash); + // if (getMatch(hash) !== -1) { + // logger.info(resultString + '.IDX : ' + hash); + // } + // if (getMatch(l9hash) !== -1) { + // logger.info(resultString + ' : ' + hash); + // } + // } + // } + // } + // } + // } + } + } + } + } + } + + bruteForceHash(); +}; + + +const nameHasher = async () => { + const start = Date.now(); + + const hasher = new NameHasher(join('.', 'config')); + + const fileNames = [ + 'hunt.dat', + 'hunt.idx', + ]; + + const keyValueMap: { [key: string]: string } = {}; + + for (const fileName of fileNames) { + keyValueMap[String(hasher.hashJagFileName(fileName))] = fileName; + } + + console.log(JSON.stringify(keyValueMap, null, 4)); + + const end = Date.now(); + logger.info(`Operations completed in ${(end - start) / 1000} seconds.`); +}; + +nameHasher().catch(console.error); diff --git a/src/scripts/script-executor.ts b/src/scripts/script-executor.ts index 29796c5..3ffc161 100644 --- a/src/scripts/script-executor.ts +++ b/src/scripts/script-executor.ts @@ -11,11 +11,13 @@ export class ScriptExecutor { return yargs(process.argv.slice(2)).options(argumentOptions).argv as any as T; } - public executeScript(argumentOptions: ArgumentOptions, - executor: (terminalInterface: ScriptExecutor, args: T) => Promise): void { - (async function(terminal: ScriptExecutor, args: T) { - await executor(terminal, args); - }(this, this.getArguments(argumentOptions))); + public executeScript( + argumentOptions: ArgumentOptions, + script: (args: T) => Promise + ): void { + (async function(args: T) { + await script(args); + }(this.getArguments(argumentOptions))); } } diff --git a/src/scripts/unpacker.ts b/src/scripts/unpacker.ts index c5947db..388e29b 100644 --- a/src/scripts/unpacker.ts +++ b/src/scripts/unpacker.ts @@ -1,9 +1,7 @@ import { join } from 'path'; import { existsSync, readdirSync, statSync, mkdirSync } from 'graceful-fs'; import { logger } from '@runejs/common'; - -import { Store } from '../index'; -import { ScriptExecutor, ArgumentOptions } from './index'; +import { ArgumentOptions, ScriptExecutor } from './script-executor'; interface UnpackOptions { @@ -34,63 +32,59 @@ const unpackerArgumentOptions: ArgumentOptions = { }; -async function unpackFiles(store: Store, args: UnpackOptions): Promise { +/*async function unpackFiles(store: Store, args: UnpackOptions): Promise { const argDebugString = args ? Array.from(Object.entries(args)) .map(([ key, val ]) => `${key} = ${val}`).join(', ') : ''; const { archive: archiveName, debug } = args; - try { - store.loadPackedStore(); - - if(archiveName === 'main') { - logger.info(`Unpacking JS5 file store with arguments:`, argDebugString); + store.loadPackedStore(); - store.decode(true); + if(archiveName === 'main') { + logger.info(`Unpacking JS5 file store with arguments:`, argDebugString); - store.encode(true); - store.compress(true); + store.decode(true); - if(!debug) { - store.write(); - } else { - logger.info(`Flat file store writing is disabled in debug mode.`); - } + store.encode(true); + store.compress(true); - logger.info(`Decoding completed.`); - - await store.saveIndexData(true, true, true); + if(!debug) { + store.write(); } else { - logger.info(`Unpacking JS5 archive with arguments:`, argDebugString); + logger.info(`Flat file store writing is disabled in debug mode.`); + } - const a = store.find(archiveName); + logger.info(`Decoding completed.`); - if(!a) { - throw new Error(`Archive ${ a } was not found.`); - } + await store.saveIndexData(true, true, true); + } else { + logger.info(`Unpacking JS5 archive with arguments:`, argDebugString); - a.decode(true); + const a = store.find(archiveName); - a.encode(true); - a.compress(true); + if(!a) { + throw new Error(`Archive ${ a } was not found.`); + } - if(!debug) { - a.write(); - } else { - logger.info(`Archive writing is disabled in debug mode.`); - } + a.decode(true); - logger.info(`Decoding completed.`); + a.encode(true); + a.compress(true); - await a.saveIndexData(true, true); + if(!debug) { + a.write(); + } else { + logger.info(`Archive writing is disabled in debug mode.`); } - } catch(error) { - logger.error(error); + + logger.info(`Decoding completed.`); + + await a.saveIndexData(true, true); } -} +}*/ -new ScriptExecutor().executeScript(unpackerArgumentOptions, async (terminal, args) => { +new ScriptExecutor().executeScript(unpackerArgumentOptions, async (args) => { const start = Date.now(); logger.info(`Unpacking JS5 store...`); @@ -102,9 +96,7 @@ new ScriptExecutor().executeScript(unpackerArgumentOptions, async mkdirSync(logDir, { recursive: true }); } - logger.destination(join(logDir, `unpack_${ build }.log`)); - - const store = await Store.create(build, dir); + /*const store = await Store.create(build, dir); const js5Dir = join(dir, 'packed'); @@ -122,11 +114,6 @@ new ScriptExecutor().executeScript(unpackerArgumentOptions, async } } - logger.boom.flushSync(); - logger.boom.end(); - const end = Date.now(); - logger.info(`Unpacking completed in ${(end - start) / 1000} seconds.`); - - process.exit(0); + logger.info(`Unpacking completed in ${(end - start) / 1000} seconds.`);*/ }); diff --git a/src/store.ts b/src/store.ts deleted file mode 100644 index d6526e7..0000000 --- a/src/store.ts +++ /dev/null @@ -1,526 +0,0 @@ -import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync } from 'graceful-fs'; -import { join } from 'path'; -import JSON5 from 'json5'; -import { ByteBuffer, logger } from '@runejs/common'; -import { Xtea, XteaKeys } from '@runejs/common/encrypt'; -import { Crc32 } from '@runejs/common/crc32'; - -import { Archive, FileState } from './index'; -import { ArchiveIndexEntity, IndexService, StoreIndexEntity } from './db'; -import { ArchiveConfig } from './config'; - - -export type StoreFormat = 'unpacked' | 'packed'; - -export interface StoreOptions { - outputPath?: string | undefined; -} - - -export class Store { - - public readonly archives: Map = new Map(); - public readonly fileNameHashes: Map = new Map(); - public readonly indexService: IndexService; - - private _js5MainIndex: ByteBuffer; - private _js5ArchiveIndexes: Map; - private _js5MainArchiveData: ByteBuffer; - - private _index: StoreIndexEntity; - private _mainArchive: Archive; - private _data: ByteBuffer; - private _compressed: boolean = false; - private _js5Encoded: boolean = false; - private _path: string; - private _outputPath: string; - private _archiveConfig: { [key: string]: ArchiveConfig }; - private _encryptionKeys: Map; - private _gameBuild: string; - private _gameBuildMissing: boolean; - - protected constructor(gameBuild: string, path: string, outputPath?: string) { - this._gameBuild = gameBuild; - this._path = path; - this._outputPath = outputPath ? outputPath : join(path, 'unpacked'); - this.indexService = new IndexService(this); - this.loadArchiveConfig(); - Crc32.init(); - } - - public static async create(gameBuild: string, path: string = './', options?: StoreOptions): Promise { - const store = new Store(gameBuild, path, options?.outputPath); - - await store.indexService.load(); - - store._index = await store.indexService.getStoreIndex(); - - if(!store._index) { - store._index = new StoreIndexEntity(); - store._index.gameBuild = gameBuild; - } - - store.loadEncryptionKeys(); - store.loadFileNames(); - - store.archives.clear(); - - const archiveConfigs = Object.entries(store.archiveConfig); - const mainArchiveConfig = Array.from(Object.values(store.archiveConfig)).find(a => a.index === 255); - - if(!mainArchiveConfig) { - throw new Error(`Main archive (index 255) configuration was not found. ` + - `Please configure the main archive using the archives.json5 file within the store config directory.`) - } - - const mainArchiveIndex = new ArchiveIndexEntity(); - mainArchiveIndex.key = 255; - mainArchiveIndex.gameBuild = gameBuild; - mainArchiveIndex.name = 'main'; - store._mainArchive = new Archive(mainArchiveIndex, mainArchiveConfig, { store }); - - let archiveIndexes = await store.indexService.getArchiveIndexes(); - - if(!archiveIndexes?.length) { - archiveIndexes = new Array(archiveConfigs.length); - } - - for(const [ name, config ] of archiveConfigs) { - if(config.index === 255) { - continue; - } - - if(config.build) { - let revision: number; - if(gameBuild.includes('_')) { - revision = Number(gameBuild.substring(gameBuild.indexOf('_') + 1)); - } else { - revision = Number(gameBuild); - } - if(revision < config.build) { - logger.info(`Skipping archive ${name} as it is not available in this game build.`); - continue; - } - } - - let archiveIndex = archiveIndexes.find(a => a?.key === config.index); - if(!archiveIndex) { - archiveIndex = store.indexService.validateArchive({ - numericKey: config.index, - name, - nameHash: store.hashFileName(name), - config - }); - } - - const archive = new Archive(archiveIndex, config, { - store, archive: store._mainArchive - }); - - store.archives.set(archive.key, archive); - - // Bulk-fetch the archive's groups - const groups = archiveIndex.groups = await store.indexService.getGroupIndexes(archiveIndex); - - // Bulk-fetch the archive's files and sort them into the appropriate groups - const archiveFileIndexes = await store.indexService.getFileIndexes(archiveIndex); - for(const fileIndex of archiveFileIndexes) { - const group = groups.find(group => group.key === fileIndex.groupKey); - if(!group) { - continue; - } - - if(!Array.isArray(group.files) || !group.files?.length) { - group.files = [ fileIndex ]; - } else { - group.files.push(fileIndex); - } - } - } - - return store; - } - - public loadPackedStore(): void { - const js5StorePath = join(this.path, 'packed'); - - if(!existsSync(js5StorePath)) { - throw new Error(`${js5StorePath} could not be found.`); - } - - const stats = statSync(js5StorePath); - if(!stats?.isDirectory()) { - throw new Error(`${js5StorePath} is not a valid directory.`); - } - - const storeFileNames = readdirSync(js5StorePath); - const dataFile = 'main_file_cache.dat2'; - const mainIndexFile = 'main_file_cache.idx255'; - - if(storeFileNames.indexOf(dataFile) === -1) { - throw new Error(`The main ${dataFile} data file could not be found.`); - } - - if(storeFileNames.indexOf(mainIndexFile) === -1) { - throw new Error(`The main ${mainIndexFile} index file could not be found.`); - } - - const indexFilePrefix = 'main_file_cache.idx'; - const dataFilePath = join(js5StorePath, dataFile); - const mainIndexFilePath = join(js5StorePath, mainIndexFile); - - this._js5MainArchiveData = new ByteBuffer(readFileSync(dataFilePath)); - this._js5MainIndex = new ByteBuffer(readFileSync(mainIndexFilePath)); - this._js5ArchiveIndexes = new Map(); - - for(const fileName of storeFileNames) { - if(!fileName?.length || fileName === mainIndexFile || fileName === dataFile) { - continue; - } - - if(!fileName.startsWith(indexFilePrefix)) { - continue; - } - - const index = fileName.substring(fileName.indexOf('.idx') + 4); - const numericIndex = Number(index); - - if(isNaN(numericIndex)) { - logger.error(`Index file ${fileName} does not have a valid extension.`); - } - - if(!this.has(index)) { - logger.warn(`Archive ${index} was found within the JS5 store, but is not configured for flat file store use.`, - `Please add the archive to the archives.json5 configuration file to load it properly.`); - continue; - } - - this._js5ArchiveIndexes.set(index, new ByteBuffer(readFileSync(join(js5StorePath, fileName)))); - } - - logger.info(`Packed store loaded for game build ${this.gameBuild}.`); - } - - public pack(): void { - // @TODO - } - - public decode(decodeGroups: boolean = true): ByteBuffer | null { - this.archives.forEach(archive => archive.decode(decodeGroups)); - return null; - } - - public encode(encodeGroups: boolean = true): ByteBuffer { - const fileSize = 4 * this.archiveCount; - - this._data = new ByteBuffer(fileSize + 31); - - this._data.put(0); - this._data.put(fileSize, 'int'); - - for(let archiveIndex = 0; archiveIndex < this.archiveCount; archiveIndex++) { - this._data.put(this.get(archiveIndex).index.crc32, 'int'); - } - - this.mainArchive.setData(this._data, FileState.encoded); - this.mainArchive.index.data = this.index.data = this._data.toNodeBuffer(); - - if(encodeGroups) { - this.archives.forEach(archive => archive.encode(true)); - } - - return this.data; - } - - public compress(compressGroups: boolean = true): ByteBuffer | null { - this.archives.forEach(archive => archive.compress(compressGroups)); - - this._compressed = true; - return this._data; - } - - public async read(compress: boolean = false, readDiskFiles: boolean = true): Promise { - this._js5Encoded = false; - this._compressed = false; - - for(const [ , archive ] of this.archives) { - await archive.read(false, readDiskFiles); - } - - if(compress) { - this.compress(); - } - - return this.encode(); - } - - public write(): void { - if(!this.archives.size) { - throw new Error(`Archives not loaded, please load a flat file store or a JS5 store.`); - } - - const start = Date.now(); - logger.info(`Writing flat file store...`); - - try { - if(existsSync(this.outputPath)) { - rmSync(this.outputPath, { recursive: true, force: true }); - } - - mkdirSync(this.outputPath, { recursive: true }); - } catch(error) { - logger.error(`Error clearing file store output path (${this.outputPath}):`, error); - return; - } - - try { - logger.info(`Writing archive contents to disk...`); - this.archives.forEach(archive => archive.write()); - logger.info(`Archives written.`); - } catch(error) { - logger.error(`Error writing archives:`, error); - return; - } - - const end = Date.now(); - logger.info(`Flat file store written in ${(end - start) / 1000} seconds.`); - } - - public async saveIndexData(saveArchives: boolean = true, saveGroups: boolean = true, saveFiles: boolean = true): Promise { - try { - await this.indexService.saveStoreIndex(); - logger.info(`File store index saved.`); - } catch(error) { - logger.error(`Error indexing file store:`, error); - return; - } - - if(saveArchives) { - logger.info(`Indexing archives...`); - - for(const [ , archive ] of this.archives) { - try { - await archive.saveIndexData(false); - } catch(error) { - logger.error(`Error indexing archive:`, error); - return; - } - } - } - - if(saveGroups) { - logger.info(`Indexing archive groups...`); - - for(const [ , archive ] of this.archives) { - try { - await archive.saveGroupIndexes(false); - } catch(error) { - logger.error(`Error indexing group:`, error); - return; - } - } - } - - if(saveFiles) { - logger.info(`Indexing archive files...`); - - for(const [ , archive ] of this.archives) { - try { - await archive.saveFlatFileIndexes(); - } catch(error) { - logger.error(`Error indexing flat file:`, error); - return; - } - } - } - } - - public find(archiveName: string): Archive { - return Array.from(this.archives.values()).find(child => child?.name === archiveName) ?? null; - } - - public get(archiveKey: string): Archive | null; - public get(archiveKey: number): Archive | null; - public get(archiveKey: string | number): Archive | null; - public get(archiveKey: string | number): Archive | null { - return this.archives.get(String(archiveKey)) ?? null; - } - - public set(archiveKey: string, archive: Archive): void; - public set(archiveKey: number, archive: Archive): void; - public set(archiveKey: string | number, archive: Archive): void; - public set(archiveKey: string | number, archive: Archive): void { - this.archives.set(String(archiveKey), archive); - } - - public has(archiveKey: string | number): boolean { - return this.archives.has(String(archiveKey)); - } - - public loadArchiveConfig(): void { - const configPath = join(this.path, 'config', 'archives.json5'); - if(!existsSync(configPath)) { - logger.error(`Error loading store: ${configPath} was not found.`); - return; - } - - this._archiveConfig = JSON5.parse(readFileSync(configPath, 'utf-8')) as { [key: string]: ArchiveConfig }; - - if(!Object.values(this._archiveConfig)?.length) { - throw new Error(`Error reading archive configuration file. ` + - `Please ensure that the ${configPath} file exists and is valid.`); - } - } - - public getEncryptionKeys(fileName: string): XteaKeys | XteaKeys[] | null { - if(!this.encryptionKeys.size) { - this.loadEncryptionKeys(); - } - - const keySets = this.encryptionKeys.get(fileName); - if(!keySets) { - return null; - } - - if(this.gameBuild !== undefined) { - return keySets.find(keySet => keySet.gameBuild === this.gameBuild) ?? null; - } - - return keySets; - } - - public loadEncryptionKeys(): void { - const configPath = join(this.path, 'config', 'xtea'); - this._encryptionKeys = Xtea.loadKeys(configPath); - - if(!this.encryptionKeys.size) { - throw new Error(`Error reading encryption key lookup table. ` + - `Please ensure that the ${configPath} file exists and is valid.`); - } - } - - public hashFileName(fileName: string): number { - if(!fileName) { - return 0; - } - - let hash = 0; - for(let i = 0; i < fileName.length; i++) { - hash = fileName.charCodeAt(i) + ((hash << 5) - hash); - } - - return hash | 0; - } - - public findFileName(nameHash: string | number | undefined, defaultName?: string | undefined): string | undefined { - if(!this.fileNameHashes.size) { - this.loadFileNames(); - } - - if(nameHash === undefined || nameHash === null) { - return defaultName; - } - - if(typeof nameHash === 'string') { - nameHash = Number(nameHash); - } - - if(isNaN(nameHash) || nameHash === -1 || nameHash === 0) { - return defaultName; - } - - return this.fileNameHashes.get(nameHash) || defaultName; - } - - public loadFileNames(): void { - const configPath = join(this.path, 'config', 'name-hashes.json'); - if(!existsSync(configPath)) { - logger.error(`Error loading file names: ${configPath} was not found.`); - return; - } - - const nameTable = JSON.parse(readFileSync(configPath, 'utf-8')) as { [key: string]: string }; - Object.keys(nameTable).forEach(nameHash => this.fileNameHashes.set(Number(nameHash), nameTable[nameHash])); - - if(!this.fileNameHashes.size) { - throw new Error(`Error reading file name lookup table. ` + - `Please ensure that the ${configPath} file exists and is valid.`); - } - } - - public setGameBuildMissing(): void { - this._gameBuildMissing = true; - } - - public get archiveCount(): number { - return this.archives?.size || 0; - } - - public get js5MainIndex(): ByteBuffer { - if(!this._js5MainIndex?.length || !this._js5MainArchiveData?.length) { - this.decode(); - } - - return this._js5MainIndex; - } - - public get js5ArchiveIndexes(): Map { - if(!this._js5MainIndex?.length || !this._js5MainArchiveData?.length) { - this.decode(); - } - - return this._js5ArchiveIndexes; - } - - public get js5MainArchiveData(): ByteBuffer { - if(!this._js5MainIndex?.length || !this._js5MainArchiveData?.length) { - this.decode(); - } - - return this._js5MainArchiveData; - } - - public get index(): StoreIndexEntity { - return this._index; - } - - public get mainArchive(): Archive { - return this._mainArchive; - } - - public get data(): ByteBuffer { - return this._data; - } - - public get compressed(): boolean { - return this._compressed; - } - - public get js5Encoded(): boolean { - return this._js5Encoded; - } - - public get path(): string { - return this._path; - } - - public get outputPath(): string { - return this._outputPath; - } - - public get gameBuild(): string { - return this._gameBuild; - } - - public get archiveConfig(): { [p: string]: ArchiveConfig } { - return this._archiveConfig; - } - - public get encryptionKeys(): Map { - return this._encryptionKeys; - } - - public get gameBuildMissing(): boolean { - return this._gameBuildMissing; - } -} diff --git a/src/util/index.ts b/src/util/index.ts deleted file mode 100644 index 32737a8..0000000 --- a/src/util/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const isSet = (variable: any): boolean => - variable !== undefined && variable !== null;