diff --git a/.gitignore b/.gitignore index 034b495..8a25d13 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,7 @@ *.iml .gradle /local.properties -/.idea/caches -/.idea/libraries -/.idea/modules.xml -/.idea/workspace.xml -/.idea/navEditor.xml -/.idea/assetWizardSettings.xml +/.idea .DS_Store /build /captures @@ -15,4 +10,7 @@ local.properties node_modules *.zip -app/release/app-release.aab \ No newline at end of file +app/release/ +.parcel-cache/ +dist +keystore.properties \ No newline at end of file diff --git a/Readme.md b/Readme.md index 234742b..7ef513f 100644 --- a/Readme.md +++ b/Readme.md @@ -2,241 +2,597 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades ! -[Play now](https://breakout.lecaro.me/) - -[F-Droid](https://f-droid.org/en/packages/me.lecaro.breakout/) - -[Google Play](https://play.google.com/store/apps/details?id=me.lecaro.breakout) - -[itch.io](https://renanlecaro.itch.io/breakout71) - -[GitLab](https://gitlab.com/lecarore/breakout71) - -[HackerNews](https://news.ycombinator.com/item?id=43183131) - -[Donate](https://github.com/sponsors/renanlecaro) +- [Play now](https://breakout.lecaro.me/) +- [Donate](https://paypal.me/renanlecaro) +- Bitcoin : bc1qlh8kywy3ttsuqqa08yx2rdc8dqhdvyt43wlxmr +- [Discord](https://discord.gg/bbcQw4x5zA) +- [itch.io](https://renanlecaro.itch.io/breakout71) +- [F-Droid](https://f-droid.org/en/packages/me.lecaro.breakout/) +- [Google Play](https://play.google.com/store/apps/details?id=me.lecaro.breakout) +- [GitLab](https://gitlab.com/lecarore/breakout71) - -# Goal - -The goal is to catch as many coins as possible during 7 levels. -Coins appear when you break bricks. -They fly around, bounce and roll, and you need to catch them with your puck to increase your score. -Your score is displayed in the top right corner of the screen. -You must delete all bricks to progress to the next level. -If you drop the ball, it's game over, unless you had the "extra life" upgrade. - -# Upgrades - -After clearing a level, you'll be able to pick upgrades among a small selection presented to you. -The upgrade you pick will apply until the end of the run. You will get more upgrade choices, and even the ability to pick -multiple upgrades at the end of the level if you play well : catch all coins, clear the level quickly and never miss. -You also get a free random upgrade at the beginning of each run. You can see which upgrades you have -(and a few more details) by clicking your score at the top right of the screen. - -Upgrades apply to the whole run and can synergize. For example, if you combine "sapper" and "piercing", the first brick -you hit after a puck bounce will immediately be transformed to an explosive brick, and detonated by the same ball, -effectively giving you an explosive ball. - -Some upgrades help with aiming, like "puck control balls". Some upgrades can be picked multiple times to increase the effect, you'll see for example "+1 ball level 2" which adds a third ball. - -When you first play, only a few upgrades are available, you unlock the rest by simply playing and scoring points. There's a similar -mechanic for levels unlock. At the end of a run, the things you just unlocked will be shown, and you can check the full content in menu / unlocks. - -Many upgrades impact your combo. - -# Combo - -Your "combo" is the number of coins spawned when a brick breaks. It is displayed on your puck, for example x4 means each -brick will spawn 4 coins. It will reset if you miss. - -Many upgrades impact your combo : - -### Single puck hit streak - -The combo grows by one when breaking a brick, but resets when a ball hits the puck. -Once you combo is high, the puck will glow red, to remind you that it will hurt your combo to touch it with any ball. -The combo does not reset when the ball is lost, provided you have more than one ball. - -### +3 base combo - -The combo starts at 4, and resets to 4 if another upgrade resets it. -Picking this again will raise the starting combo by 3 each time. -There are no downsides to this upgrade. - -### Shoot straight - -The combo grow each time you break a brick. -The combo resets whenever the ball touches the left or right of the play area. -Once your combo is a bit high, the sides will glow red to let you know you shouldn't touch them. - -### Sky is the limit - -The combo grow each time you break a brick. -The combo resets whenever the ball touches the top of the play area. -Once your combo is a bit high, the top will glow red to let you know you shouldn't touch it. - -### Picky eater - -Each time you break a brick, if the ball and brick color are the same, your combo grows by one. -Otherwise, the combo resets, and the ball takes the color of the brick. -Bricks of the wrong colors should glow red once you have a small combo going. +# Changelog +## To do -### Compound interest +## Done +- reworked level up screen : + - bigger "level X / Y cleared" + - upgardes need to all be spent on the same list of perks (to avoid reading too much) + - instead of rerolls, you get a longer list of choices to pick from with silver/gold medals + - clarified challenges, only show them when you pass one of them + - removed the "sides bounce" challenge, bouncing on sides shouldn't be punished + - once you reach high score of 1000, level unlock hints appear, and required / forbidden upgrades and colored gold/red + - added tooltip on most items on that screen, that can be triggered on mobile by tapping the text -Each time you break a brick, your combo grows by one. -Each time a coin falls around your puck and is lost, your combo decreases by one. -Once you have a small combo going, the bottom of the screen will glow red around the puck, to remind you to catch all coins. -If you level this further, then the combo grows and shrinks faster. +- new perk : steering +- boosted perk : stronger foundation (+3 combo, then +4, then +5..) +- easy cleanup special effect (and X made of particles) +- added a few levels +- level end countdown (on mobile and desktop) +- level start countdown (on mobile) +- loosing ball is ok during level end countdown +- unlocked upgrades and levels : split item description (with tooltip) and "try" button +- creative mode : removed tooltips for perks as they were getting in the way on mobile +- Fix: removed progress bars from unlocked level as there's no real progress +- Fix :upgrades list now uses numbers instead of bars, looks better with limitless +- Fix :somehow score clicks didn't register while the game was playing, that's solved +- Fix : click tooltip to open on mobile, click anywhere to close +- Fix: Can't press help buttons in Creative Menu +- Fix: wait for bricks to respawn before leveling up +- UX : score and menu button look extra clickable until you tap them 3 times and restart the app -### Hot start +## 29097764 -Your combo starts at 15 at the beginning of the level. -Every second, it decreases by one. -If you level this further, the combo starts 15 points higher and shrinks 1 point / s more. +- Added levels : Fish, Spider, GlidersLone island,Spacewyrm Jon, Taijitu, Egg pan, Inception, Chess +## 29095000 -### Soft reset +- hardcoded the levels unlock conditions so that they wouldn't change at each update +- added a "display level code" button in level editor +- passive income : paddle will be transparent for a much shorter time +- better mobile mode detection +- clear tooltip on page scroll -Whenever your combo resets, it only looses half of its value. -However, whenever it should increase, it has 50% chance of staying the same. -If you pick it a second time, the effect is more pronounced : the combo keeps 66% of its value on reset, but only grows 33% of the time. -If you have many perks that grow the combo every time a brick breaks, then it will still grow every time just slower. +## 29092809 -# Longer runs +- fixed: crash when running out of levels (i think, i didn't try) +- fixed: context menu and tooltip stuck on windows -The default run lasts 7 levels. The selection process is to pick those levels at random, then sort them (more or less) by -number of bricks present, so that runs start with smaller levels and the bigger ones are left for the end. You can extend -the run by picking up to three times the "+1 level" upgrade. +## 29091656 -"Sturdy bricks" and "Respawn" can also extend the game time significantly. +- categorized the icons +- color coded the icons +- changed the wording of perks help to be shorter +- added tooltips on perks with full help, and a help button on mobile +- all or nothing : don't show negative number of coins cought, don't reduce score if no combo was lost +- rename hypnosis to golden_goose, apply when hitting any brick, any side at level 2 +- removed comboIncreaseTexts option +- minefield : +10% coins per bomb on screen +- extra life are transparent when you have 2+ balls +- removed : instant_upgrade +- nerfed : helium : now need to be level 3 to have the same effect of keeping coins up +- new level : Blinky by Big Goober +- game over screen : perk list at the bottom, after unlocks and stats -# Aiming +## 29088680 -What decides how the ball flies away is only the position of the puck hit. If the ball hits the puck dead center, it will -bounce back up vertically, while in you hit more on one side, it will have more angle. -The puck speed and incoming angle have no impact on the ball direction after bouncing. -You might find that a smaller puck makes it a bit easier to aim near corners, but also makes it much harder to catch coins. -"Wind" and "puck controls ball" can help you aim even after the ball bounced to the wrong direction. -"Slower ball" gives you a bit more time to aim, particularly useful in later levels where the ball goes faster. The ball also -accelerates as you spend time in each level. +- new perk: happy family: + lvl points per paddle bounce per extra ball, reset on ball lost +- nerfed perk : sticky coins : stick to same color at level 1, any color at level 2+ +- nerfed perk: zen : combo increases every 3 seconds, resets on explosion -# Requirements +## 29088513 -The app should work offline and perform well even on low-end devices. -It's very lean and does not take much storage space (Roughly 0.1MB). -If the app stutters, turn on "fast mode" in the settings to render a simplified view that should be faster. -There's also an easy mode for kids (slower ball) and a color-blind mode (no color related game mechanics). +- included german corrections by Pock +- added particle effect for wrap +- removed grace period from passive income, updated icon -# Roadmap -The "engine" could be better -- apk version soft locks at start. -- shinier coins by applying glow to them ? +## 29087440 + +- zen : now you gain one combo per bomb on screen when breaking a brick (so no bombs, no gain) +- sticky coins : coins stay stuck when there's an explosion +- wrap_left / wrap_right : teleport the ball to the other side of the screen when it hits a border +- passive income : now moving the puck makes it transparent to coins and balls, but not reset the combo +- main menu : split level unlock and perks unlocks + +## 29087252 +- apply percentage boost to combo shown on brick +- smaller puck now gives +50% coins per level +- transparency now gives +50% coins if ALL balls are fully transparent, less otherwise +- new perk : sticky coins (coins stick to bricks) +- left/top/right is laval perks : at level 2+, the corresponding borders completely disappears (reachable with limitless) +- new perk : three cushion (gain point for indirect hits) +- live stats: coins still in the air appear as "lost" in the catch percentage, as in the final computation +- level editor : removed the conditions on bricks count, level name and credits to be able to copy the code +- shadow around ball when there are many coins : enabled in basic mode too +- hot start : after reset, if you raise the combo again, only start ticking down after a whole second. +- new perk : ottawa treaty, breaking a brick near a bomb disarms the bomb +- shocks now doesn't add ball speed at level 1 +- creative mode UI rework +- compound_interest : combo resets as soon as coin passes the paddle line +- added bombs to implosion and kaboom starter levels +- toast an error if storage is blocked +- toast an error if migration fails +- fixed video download in apk - ask for permanent storage -- It's a bit confusing at first to grasp that one upgrade is applied randomly at the start of the game -- on mobile, add an element that feels like it can be "grabbed" and make it shine while writing "Push here to play" -- add a clickable button to allow sound to play in chrome android -- offline mode with service worker -- add pwe manifest -- see how to do fullscreen on ios, or at least explain to do aA/hide toolbars -- experiment with showing the combo somewhere else, maybe top center, maybe instead of score. -- more help somewhere accessible -- limit GC by reusing coins and particles -- convert captures to mp4 unsing ffmpeg wasm because reddit refuses webm files -- few puck bounces = more choices / upgrades -- disable zooming (for ios double tap) -- particles when bouncing on sides / top -- show total score on end screen (score added to total) -- show stats on end screen compared to other runs -- handle back bouton in menu -- mouvement relatif du puck -- balls should collide with each other -- when game resumes near bottom, be unvulnerable for .5s ? , once per level -- apply global curve / brightness to canvas when things blow, or just always to make neon effect better -- manifest for PWA (android and apple) -- lights shadows -- Offline mode web for iphone -- controller support on web/mobile -- webgl rendering -- enable export of gameplay capture in webview -- endgame histograms could work as filters, when you hover a bar, all other histograms would show the stats of those runs only, without changing reference of categories -- sound when ball changes color -- option : don't pause on mobile when lifting finger -- option : accelerated relative movements on mobile -- maybe just have 10 background, and always use the same one for the nth level of each run ? -- would be nice to have a leaderboard for not using each perk too. Like "best runs without hot start" +- option: reuse past frame's light in new frame lighting computation when there are 150+ coins on screen, to limit the performance impact of rendering lots of lights -There are many possible perks left to implement : +## 29084606 + +- simpler and more readable encoding for save files +- removed check of payload signature on save file, seemed to fail because of the poor encoding of the name of the "côte d'ivoire" level +- automatic detection of the number of steps required for physics +- trial runs detection fix + +## 29083397 + +- highlight last used creative level +- access autoplay mode from the menu +- access stress test mode from the menu, show real time stats +- Render bottom border differently to show how far the puck can go +- Corner Shot: scale like Need Some Space +- grey out irrelevant options in the settings +- Back to Creative Menu at the end of a Creative level + +## 29080170 + +- don't show unlock toast at first startup for levels that are unlocked by default +- Droplet particle color should be gold for gold coins +- added levels: A Very Dangerous High-Five, The Boys + +## 29079818 + +- Imported levels : Mario, Minesweeper and Target +- Fixed an issue with localstorage saving of custom levels + + +## 29079805 + +- combo text on paddle will be grey if we're at the base combo +- transparency now rounds up +- import level up to 21 x 21 +- corrected icon of "padding" + +## 29079087 + +- measured and improve the performance (test here https://breakout.lecaro.me/?stresstest) +- added a few levels +- autoplay mode (with wake lock and computer play https://breakout.lecaro.me/?autoplay ) +- Added particle and sound effect when coin drops below the "waterline" of the puck +- slower coins fall once they are under the paddle +- in game level editor +- allow loading newer save in outdated app (for rollback) +- game crashes when reaching level 12 (no level info in runLevels) + +## 29074385 + +- added back some extra languages +- superhot: fixed particles durations and level duration +- bricks aattract coins : less powerfull +- bricks attract balls +- unbounded nerf : just adds padding around bricks, not combo add +- don't tell user to get -100 points to unlock level +- display colored coins when there's hypnosis or rainbow enabled + +## 29071903 + +- new perk : hypnosis +- new perk : rainbow +- new perk : bricks attract coins +- Extra choice: wrong text for french "2 more choices" +- metamorphosis : when coins are spent, display them hollowed out +- super hot : starting level rework +- zen : added bombs to starting level + +## 29071527 + +- super hot : time moves only when paddle moves. Later levels slow down even more the time when you're not moving. +- transparency : ball becomes transparent towards top of screen, +50% coins. +- space coins : coins bounce without loosing momentum +- trickledown : coins spawn at the top of the screen +- unlocked content : start with perk icon as level +- allow removing all starting perks, to get full random +- rename "puck" into paddle +- use french as base language to keep consistent formal/informal tone +- fixed memory leak in language detection code + +## 29069860 + +- when rendering level icons, always use transparent background +- resized some levels to use as flags, added some missing languages as levels +- added machine translation, so that translators can try the game in their language first : ar,de,es,ko,ru,ur,uz,zh +- change translation keys to get better sorted files +- change fortunate ball to work more like coin magnet, carrying the balls around to catch them at next puck bounce +- add a test to forbid more than 5% grey bricks on black background, remove grey bricks border +- simplified texts to make translation easier +- fixed some issues around saved level unlocks +- change donation text to not suggest an amount +- limited history to top 100 runs + +## 29068563 + +- review the "next unlocks" in score and game over +- As soon as upgrade condition is reached, toast +- As soon as level condition is reached, lock it in and tell the user +- extra life only saves your last ball, max 7 instead of 3 +- Don't use "RAZ" in French explanations. +- explain ghost coin's slow down effect +- when there are only a few coins, make them brighter +- Perk : [colin] minefield +- clear scheduled sounds if sounds off +- show unlocked levels above game stats in gameover screen +- reduce resolution of lights even more (1/16) + +## 29067205 + +- tooltip isn't readable at bottom of screen +- added levels as tributes to game players +- display closest unlock with current perks in score and gameover screens +- initial perk icon = first level +- fix starting perk option not working +- progress bar for unlock in unlocks menu +- display runs history +- in the runs history, only save perks that were chosen by the user +- migration to save past content to localStorage.recovery_data right before starting a new version +- mention unlock conditions in help +- show unlock condition in unlocks menu for perks as tooltip +- fallback for mobile user to see unlock conditions +- New perk : "limitless" raises the max of all perks by 1 +- Boosted perk : side kick, now you just need to hit bricks from the left side to gain +lvl combo, hitting from the right side does -2xlvl combo +- add unlock conditions for levels in the form "reach high score X with perk A,B,C but without perk B,C,D" +- remove loop mode : + - remove basecombo + - remove mode + - clear old runs in other mode +- ignore scores in creative mode +- remove the slow mode +- adjusted the light effects +- added white border around dark grey bricks +- remove the opaque coin options, all coins are opaque, but dark grey ones have white border +- archive each version as an html file and apk +- publish 29062687 on play store +- redo video +- review fastlane text + +- tried and cancelled native desktop app build with tauri because : + - there's no cross compilation, so no exe build on linux + - you need to sign executable differently for each platform + - the .deb and .rmp files were 3.8M for a 0.1M app + - the appimage was crazy big (100M) + - I'd need a mac to make a mac version that probably wouldn't run without doing the app store dance with apple + +## 29062687 + +- tried and cancelled webgl rendering + - it's a lot of code + - i'm not great at it + - it requires a significant rewrite + - for most things, no perf difference + - the main goal of having more colorful backgrounds can be achieved by running the lights layer at lower res +- "Miss warning" option is now on by default (ball's particles are red if catching it would be a "miss") +- "Show +X in gold" option is now on by default (show a +X when combo increases) +- "High contrast" option added, off by default (applies lights layer again as "soft light" at the end of the render) +- "Colorful coins" option now applied at render time instead of coin spawn time, to make preview easier +- when settings are opened on pc, they show up on the side and the overlay is transparent to let you preview the changes + +## 29062545 + +- Perks list now only lists upgrades that have been picked, or have banned levels +- After clearing a level, that level is dimmed in the clairvoyant level list [Bearded-Axe] +- limited clairvoyant to level one outside looped runs [obigre] +- yoyo now has more effect when the ball is at the top of the screen [obigre] +- telekinesis now has more effect when the ball is at the bottom of the screen +- "Top is lava" combo lost text is now spawned a bit lower to be more visible [obigre] + +## 29061838 + +- New perk : Fountain toss [colin] - loosing coins makes your combo grow +- Boosted : Asceticism now decreases combo instead of resetting it +- Graphics : show respawn particles even in basic mode [obigre] +- Graphics : adjusted the brightness of the game a bit more + +## 29061490 + +- Graphics : option to add more light (on by default) +- Graphics : option to make coins more readable (on by default) +- Graphics : background light effects optimization +- Graphics : all levels background have been checked (4 buggy ones removed) and will be assigned randomly +- Fixed : display gained combo was showing +0 sometimes + +## 29060272 + +- Fixed: Strict sample size was counting destroyed bricks, now count hits as explained in the help +- Fixed: passive_income was resetting your combo if you moved around the end of the last level +- Fixed: a high score issues was systematically erasing the high score in the web version, i added a migration to load the best score for your top games to recover the high score. +- QOL: option to display gained combo as onscreen text +- QOL: publish an apk to itch.io with every build +- Internal: added a simple game data migration system + +## 29059721 + +- QOL: icons in settings menu +- QOL: choose starting perks +- QOL: fixed issue with reloading with [R] key +- QOL: gameover screen restarts in the same game mode +- Fixed: Trampoline render sides in red. +- Fixed: tooltips stuck on mobile +- Fixed: issues with restarting a game with fullscreen on + +## 29058981 + +- [jaceys] A visual indication of whether a ball has hit a brick this serve (as an option) +- Top down /reach: now only the lowest level of N bricks resets combo, and all other bricks do +N combo +- picky eater: don't reset if no brick of ball color +- main menu : show high score +- keep high score of past runs +- tooltip on stats +- fixed : looping didn't work +- two abstract levels, stripes and openings +- added reset button for perks in lab mode + +## 29058469 + +- New game mode : loop / long game + - the goal is to build many different build centered on one perk + - At the end of level 7, you get to restart at level 1 for 6 levels. + - all your perks are banned except one + - The perk you keep is leveled up, and can be leveled up a second time during the next loop + - the perks you don't keep are "banned", meaning their max level is reduced by as many levels as you had picked + - unlocked after unlocking all perks +- New game mode : lab / creative + - the goal is to come up with 3 completely different but powerful play styles + - you freely create 3 builds from all the perks level available + - you play them against the levels of your choice + - try to make as much score as possible in total + - unlocked after unlocking all perks +- New levels : + - Pingwin + - Sunglasses + - Balloon +- Adjusted levels : + - orca is no longer made of bombs, but gray block +- New perks + - addiction : reward faster gameplay +- Adjusted perks + - Hot start : 30 combo per level instead of 15 + - Telekinesis: limited to level 1 + - Asceticism now gives +3 combo per lvl + - Fortunate ball has a stronger effect + - Bigger puck : puck can now cover the whole screen at higher levels, but not more + - Corner shot : higher levels let you move further away from the play area + - Forgiving : level 2 halves the penalty, level 3 is a third .. + - Helium : stronger anti gravity at higher levels + - Implosions : works like bigger-explosions at higher levels + - Metamorphosis : coins can stain more bricks at higher levels + - Re-spawn : now delay based and probabilistic, to scale more easily with higher levels. no need to hit the puck + - sacrifice : at level 2+ the combo is doubles/tripled just before clearing the screen of any bricks + - shunt : changed the math keep 25% of combo at level 1,50% at level 2,63% at level 3,70% at level 4.. + - soft reset : same math as shunt + - smaller puck : now the puck can get as small as a ball + - Unbounded : at level 2+, the top of the level is gone too + - concave_puck : ball bounces straighter and straighter, to the point where you can't move it without another perk + - shocks lvl 2+ make bigger explosions + - trampoline: nerfed a little bit, now all sides and top hit reduce combo + +- Quality of life + - Updated discord invite link that had expired + - Full screen is now a persistent option, when it's on the game will switch to full screen before starting + - Added an option to always get colored coins + - Made the "combo lost" text last 500ms instead of the pointless 150ms + - Added in-game help and credits, which can be translated + - Balancing : you earn an extra perk when playing well, and a reroll when playing perfectly + - added a prominent "donate" link after 5h of playing, and setting to hide it permanently + - disabled auto-release to F-Droid, i'll use the web version as the testing ground first + - added a white border around all coins, to make dark ones visible on dark bg + - [jaceys] counters for coins lost, misses, and boundary bounces, as well as a timer. + - Unlocked list : split perk and levels, added tooltips + +## 29049575 + +- added rerolls +- Sacrifice : clear screen instead of doubling coins + +## 29048147 + +- Ascetism : render coins with red border if there's a combo +- Warn about unbounded +- Red border dashes + +# Ideas and features + +## Easy perk ideas + +- chill : no perks gain, no level limit,+20 base combo +- when the ball teleport, probability that it's duplicated instead +- combo resets on teleport +- combo resets when hitting puck without a teleport +- teleport ball to puck as soon as it hits something (% chance) +- allow dropping balls that are about to miss. +- square coins : coins loose all horizontal momentum when hitting something. +- ball turns following puck motion +- "+1 coin for each ball within a small radius of the broken brick" ? +- two for one : add a 2 for one upgrade combo to the choice lists +- cash out : double last level's gains +- snowball : Combo resets every 0.1s . +1 combo for each combo gained Since last reset. +- Chain reaction : +lvl*lvl combo per brick broken by an explosion, combo resets after explosion is over +- catching a coin changes the color of the balls +- coins stained by balls +- fast pause : pause delay divided by {{lvl}} (helps with teleport) +- [colin] Capital - les vies non perdues à la fin du niveau rapportent un bonus de points +- ban 3 random perks from pool, gain 2 upgrades +- faster coins, double value +- balls repulse coins +- n% of coins missed respawn at the ball +- +1 combo per brick broken after a wall bounce, reset otherwise +- combo climbs by 1 every 2 second, unless no coin was caught, then it resets +- [colin] golden corners - catch coins at the sides of the puck to double their value +- [colin] varied diet - your combo grows by 2 when your ball changes color, but decreses by one when a brick is broken ? +- [colin] trickle up - inverse of reach more or less +- +lvl combo per bricks / resets after 5/lvl seconds without explosion ? +- +lvl combo per bricks / resets after 5/lvl seconds without coin catch ? +- +lvl combo per bricks / resets after 5/lvl seconds without ball color change ? +- +lvl combo per bricks / resets after 5/lvl seconds without sides hit ? +- + lvl x n combo when destroying a brick after bouncing on a side/top n times ? +- make stats a clairvoyant thing +- [colin]P ocket money — bricks absorb coins that touch them, which are released on brick destruction (with a bonus?) +- [colin] turn ball gravity on after a top bar hit, and until bouncing on puck +- fan : paddle motion creates upward draft that lifts coins and balls + +## Medium difficulty perks ideas +- coins combine when they hit (into one coin with the sum of the values, but need a way to represent that) +- balls collision split them into 4 smaller balls, lvl times (requires rework) - offer next level choice after upgrade pick -- ban 3 random perks from pool, doesn't tell you which ones, gain 2 upgrades -- 3 random perks immediately, or maybe "all level get twice as many upgrades, but they are applied randomly, and you aren't told which ones you have." -- wrap left / right -- pause and cheat again -- wrap top / bottom : coins fall back from top of screen, ball flies to the top and comes back from the screen bottom ? -- faster coins, double value -- +1 upgrade per level but -2 choices -- n% of the broken bricks respawn when the ball touches the puck -- bricks break 50% of the time but drop 50% more coins -- wind (puck positions adds force to coins and balls) -- balls repulse coins -- n% of coins missed respawn at the top -- lightning : missing triggers and explosive lighting strike around ball path -- coins repulse coins (could get really laggy) -- balls repulse coins -- balls attract coins -- twice as many coins after a wall bounce, twice as little otherwise ? -- fusion reactor (gather coins in one spot to triple their value) -- missing makes you loose all score of level, but otherwise multiplier goes up after each breaking -- soft reset, cut combo in half instead of zero -- missile goes when you catch coin -- missile goes when you break a brick -- puck bounce +1 combo, hit nothing resets -- multiple hits on the same brick (show remaining resistance as number) +- [colin] mirror puck - a mirrored puck at the top of the screen follows as you move the bottom puck. it helps with keeping combos up and preventing the ball from touching the ceiling. it could appear as a hollow puck so as to not draw too much attention from the main bottom puck. +- [colin] Combos extrêmes: lvl2 pour tous les combos, qui fait que le combo rapporte double ou triple, mais si sur un niveau la condition n'est pas respectée alors le perk ne donne plus de combo bonus pour ce niveau. +- [colin] Mytosis - les blocs bombe n'explosent pas mais relâchent une nouvelle balle à la place (clashes with "shocks" and "sapper") +- [colin] Juggle - au début du niveau, chaque balle est lancée l'une après au lieu de toutes à la fois (needs some work) +- SUPER HOT (time moves when puck moves) - bricks attract ball -- replay last level (remove score, restores lives if any, and rebuild ) -- accelerometer controls coins and balls - bricks attract coins -- breaking bricks stains neighbours -- extra kick after bouncing on puck -- transparent coins -- coins of different colors repulse -- bricks follow game of life pattern with one update every second -- 2x coins when ball goes downward / upward, half that amount otherwise ? -- new ball spawns when reaching combo X -- missing with combo triggers explosive lightning strike +- wrap left / right - correction : pick one past upgrade to remove and replace by something else -- puck bounce predictions rendered with particles or lines (requires big refactor) +- +1 combo when ball goes downward, reset if upward +- 2x speed after bouncing on puck +- the more balls are close to a brick, the more combo is gained when breaking it. If only one ball, loose one point or reset +- ball avoids brick of wrong color +- puck slowly follows desired position, but +1 combo +- knockback : hitting a brick pushes it (requires sturdy bricks) + +## Hard perk ideas +- accelerometer controls coins and balls +- [colin] side pucks - same as above but with two side pucks : hard to know where to put them +- [colin] Perk: second puck in the middle of the screen +- [colin] Sponge Ball : the ball stores coins it collides with, and releases them when bouncing on any border (left, right, top). -Some extra levels wouldn't hurt +## ideas to sort +- wind : move coins based on puck movement not position +- double coin value when they hit the sides +- [colin]Brambles — coins that touch the walls and ceiling get stuck and are thrown back when the last brick is destroyed +- [colin]Ball of Greed — the ball can collect coins (might be worth dividing into levels: lvl 1, can collect coins only after two bounces on bricks or walls. lvl 2, can collect after 1 bounce. lvl 3, can collect coins anytime)(or change the ball collection radius as the level grows) +- [colin]Phantom ball — the ball phases through 2 bricks then becomes solid (lvl2: through 6 bricks, lvl3; through all bricks until it touches a wall) +- [colin]Cryptomoney — coins that should be generated by bricks are instantly collected, but count for half their value +- [colin]Relative time — ball speed depends on its position: if it's high up on thi screen it's fast, if it's lower it's slower -- famous games -- letters +- ball attracted by bricks of the color of the ball +- level flips horizontally every time a ball bounces on puck +- [colin] close quarters - balle attirée par tous les blocs/par un bloc aléatoire, actif à portée de bloc (+1bloc au lvlup)/proportionnel à une force (+puissance au lvlup)… +- [colin] plusieurs perks qui déclenchent des effets quand une balle est perdue. par ex: +3 combo à chaque balle perdue, 5 blocs transformés en bombe, balle et coins ralentis, blocs régénérés… +- [colin] faster style - augmente le combo en fonction de la vitesse de la balle +- [colin] perk: roulette - gagne instantanément 2 perks aléatoires +- other block types : bumper (speed up ball) [colin], metal (can't break) [nicolas] +- flip perk + +## extra levels + +- Good games : + - FTL + - Nova drift + - Noita + - Enter the gungeon + - Zero Sivert + - Factorio + - Swarm + - Nuclear throne + - Brigador + + +- letters and an associated word or name +- famous characters and movies +- famous places : eiffel tower, taj mahal, etc.. - fruits - animals -- countries flags and shapes, with name as background +- countries flags and shapes -Potential big features or changes -- use ts and a bundler to get fewer bugs and compatibility with old browsers / webviews -- final bosses (large vertical level that scrolls down faster and faster) + +## UX / gameplay + +- make menu and score button more "button like" when you just installed the game. +- avoid showing a +1 and -1 at the same time when a combo increase is reset +- explain to iOS users how to add the app to home screen to get fullscreen +- delayed start on mobile to let users place the puck where they want +- experiment with showing the combo somewhere else, maybe top center, maybe instead of score. +- display a multiplicator if it's not 100%, have some perks add to it + + +## Game engine features ideas +- add a clickable button to allow sound to play in chrome android +- save state in localstorage for easy resume of a game in progress +- handle back bouton in menu +- Offline mode web for iphone +- controller support on web/mobile +- leaderboard for not using each perk, like "best runs without hot start" + + +## Maybe one day +- https://weblate.org/fr/ quite annoying to have merge conflicts while pushing, i'll enable it later. +- auto-detect device performance at first startup and adjust settings accordingly (hard to do in any sort of useful way) +- [jaceys] Move the restart button out of the menu, so that it is more easily accessible (will allow user to choose starting perk instead) +- colored coins only (coins should be of the color of the ball to count, otherwise what ? i'd rather avoid negative points) +- coins avoid ball of different color (pointless) +- [colin] wormhole - the puck sometimes don't bounce the ball back up but teleports it to the top of the screen as if it fell through from bottom to top. higher levels reduce the times it takes to reload that effect (not sure how that to word that in 1 setence) +- [colin] Mental charge - the puck is divided into two smaller pucks, then 3 smaller ones at lvl 2 : what's the point ? +- [colin] sturdy ball - does more damage to bricks, to conter sturdy bricks :that's pierce now +- [colin] plot - plot the ball's trajectory as you position your puck : too hard when you add other perks +- [colin] piggy bank - bricks absorb coins that fall onto it, and release them back as they are broken, with added value : equivalent to Asceticism +- [colin] ball coins - coins share the same physics as coins and bounce on walls and bricks : really hard to balance with speeds and all +- non brick-shaped bricks, tilted bricks,moving blocks : very difficult because of engine optimisations +- 3 random perks immediately, or maybe "all level get twice as many upgrades, but they are applied randomly, and you aren't told which ones you have." +- coins repulse coins, could get really laggy ? +- russian roulette: 5/6 chances to get a free upgrade, 1/6 chance of game over. Not really fun +- [colin] bigger ball - self-explanatory, or is it ? what's the point ? physics would break now if ball bigger than bricks +- [colin] smaller ball - doable, but why +- [colin] earthquake - when the puck hits any side of the screen with velocity, the screen shakes and a brick explodes/falls from the level. alternatively, any brick you catch with the puck gives you the coins at the current combo rate. each level lowers the amount of hits before a brick falls. Problem : no limit on how often you can slam the puck around +- missile goes when you catch coin +- missile goes when you break a brick +- [colin] Batteries - lvl1: recharge les pouvoirs du puck quand la balle touche le haut de l'écran (1 fois par lancer, se recharge en touchant le puck). lvl2: également après voir détruit 6 blocs. lvl3: également quand elle touche les bords de l'écran : i'll probably just let the second puck replace this + - store much more details about run (level by level) as numbers only (instead of json that gets big false) +- [colin] hitman - hit the marked brick for +5 combo. each level increases the combo you get for it. +- [colin] sweet spot - place your puck directly below a moving spot at the top of the level to increase your combo +- [colin] reward the player with more choices/perks for breaking a brick while having reached an increasing combo thresholds. 5 combo, then 10, then 20, then 40 etc… once a threshold is reached you aren't rewarded for that threshold again until you start a rew run +- mobile option: relative movement of the touch would be amplified and added to the puck +- mobile option: don't pause on mobile when lifting finger +- translate fastlane presentation texts to french +- convert captures to mp4 unsing ffmpeg wasm because reddit refuses webm files +- disable zooming (for ios double tap) +- Waterline under the puck, coins slow down a lot, reflections +- webgl rendering: background gradient light map, shinier coins, quite hard +- on mobile, add an element that feels like it can be "grabbed" and make it shine while writing "Push here to play" +- hard mode : bricks take many hits, perks more rare, missing clears level score, missing coins deducts score.. +- architect mode : + - play 7 levels, each with a different build. + - Perk levels can only be used once, so if you take one for level 1, you won't have it to level 2-7. + - Your final score is your worst score times your best score + - You'll see the levels in advance +- stats by lack of perk, like "best score without using hot start". - split screen multiplayer -- translation - Add color schemes into the game (ex : Catppuccin, Dracula, Terminal, etc) -- add a toggle to switch between the “coin” design and the sort of bubble colors -- sandbox mode +- final bosses (large vertical level that scrolls down faster and faster) +- add loop run where user levels can't be used in further loops (boring) +- add lab mode where you need to make three builds (complex, lots of clicking, not fun) # Credits -I pulled many background patterns from https://pattern.monster/ -They are displayed in [patterns.html](patterns.html) for easy inclusion. +I pulled the background patterns from https://pattern.monster/ -Some of the sound generating code was written by ChatGPT, and heavily -adapted to my usage over time. +I wanted an APK to start in fullscreen and be able to list it on fdroid and the play store. I started with an empty view and went to work trimming it down, with the help of that tutorial : https://github.com/fractalwrench/ApkGolf/blob/master/blog/BLOG_POST.md -Some of the pixel art is taken from google image search results, I hope to replace it by my own over time : -[Heart](https://www.youtube.com/watch?v=gdWiTfzXb1g) -[Mushroom](https://pixelartmaker.com/art/cce4295a92035ea) -https://prohama.com/whale-2-pattern/ -https://prohama.com/shark-2-pattern/ -https://prohama.com/bird-1-size-13x12/ -https://prohama.com/pingwin-4-pattern/ -https://prohama.com/dog-21-pattern/ +Colin (obigre) brought a lot of fantastic ideas to the game, here's his website (in French) : https://colin-crapahute.bearblog.dev/ -I wanted an APK to start in fullscreen and be able to list it on fdroid and the play store. I started with an empty view and went to work trimming it down, with the help of that tutorial -https://github.com/fractalwrench/ApkGolf/blob/master/blog/BLOG_POST.md +# How to install + +Breakout 71 can be installed and work offline in many ways: + +- Download an index.html file from [itch.io](https://renanlecaro.itch.io/breakout71) to play offline on your computer (latest version always) +- Download the latest apk from [itch.io](https://renanlecaro.itch.io/breakout71) to play offline on your android phone (latest version always) +- Add [the app](https://breakout.lecaro.me/) to your home screen on android, and it should play even when offline thanks to the service workers (latest version always) +- Install the latest version from the play store : https://play.google.com/store/apps/details?id=me.lecaro.breakout (updated from time to time) +- Install the latest version from Fdroid : https://f-droid.org/packages/me.lecaro.breakout/ (updated very rarely because of the updates publication lag) +- Download the index.html file or apk from my archive server : https://archive.lecaro.me/public-files/b71/ (any version including latests) + +# System requirements + +The game should perform well even on low-end devices. It's very lean and does not take much storage space (Roughly 0.1MB). The web version is supposed to work on iOS safari, Firefox ESR and chrome, on desktop and mobile. +If the app stutters, turn on "fast mode" in the settings to render a simplified view that should be faster. You can adjust many aspects of the game there, go have a look ! + \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e24d7da..a3d8da6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,9 +1,27 @@ +import java.util.Properties +import java.io.FileInputStream + plugins { alias(libs.plugins.androidApplication) alias(libs.plugins.jetbrainsKotlinAndroid) } + +val keystorePropertiesFile = rootProject.file("keystore.properties") +val keystoreProperties = Properties() +keystoreProperties.load(FileInputStream(keystorePropertiesFile)) + + android { + signingConfigs { + create("release") { + keyAlias = keystoreProperties["keyAlias"] as String + keyPassword = keystoreProperties["keyPassword"] as String + storeFile = file(keystoreProperties["storeFile"] as String) + storePassword = keystoreProperties["storePassword"] as String + } + } + namespace = "me.lecaro.breakout" compileSdk = 34 @@ -11,8 +29,8 @@ android { applicationId = "me.lecaro.breakout" minSdk = 21 targetSdk = 34 - versionCode = 29017275 - versionName = "29017275" + versionCode = 29106110 + versionName = "29106110" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { useSupportLibrary = true @@ -27,7 +45,7 @@ android { getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) - signingConfig = signingConfigs.getByName("debug") + signingConfig = signingConfigs.getByName("release") } } compileOptions { @@ -49,22 +67,6 @@ android { } } } - -//dependencies { -// -// implementation(libs.androidx.core.ktx) -// implementation(libs.androidx.lifecycle.runtime.ktx) -// implementation(libs.androidx.activity.compose) -// implementation(platform(libs.androidx.compose.bom)) -// implementation(libs.androidx.ui) -// implementation(libs.androidx.ui.graphics) -// implementation(libs.androidx.ui.tooling.preview) -// implementation(libs.androidx.material3) -// testImplementation(libs.junit) -// androidTestImplementation(libs.androidx.junit) -// androidTestImplementation(libs.androidx.espresso.core) -// androidTestImplementation(platform(libs.androidx.compose.bom)) -// androidTestImplementation(libs.androidx.ui.test.junit4) -// debugImplementation(libs.androidx.ui.tooling) -// debugImplementation(libs.androidx.ui.test.manifest) -//} \ No newline at end of file +dependencies { + implementation(libs.androidx.core) +} diff --git a/app/release/app-release.aab b/app/release/app-release.aab deleted file mode 100644 index 49c7dc1..0000000 Binary files a/app/release/app-release.aab and /dev/null differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2700098..7e787ff 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,8 +1,8 @@ - + + + + \ No newline at end of file diff --git a/app/src/main/assets/game.js b/app/src/main/assets/game.js deleted file mode 100644 index 2040fda..0000000 --- a/app/src/main/assets/game.js +++ /dev/null @@ -1,3239 +0,0 @@ -const MAX_COINS = 400; -const MAX_PARTICLES = 600; -const canvas = document.getElementById("game"); -let ctx = canvas.getContext("2d", {alpha: false}); - - -let ballSize = 20; -const coinSize = Math.round(ballSize * 0.8); -const puckHeight = ballSize; - -const randomPatterns=[ - ``, - ``, - ``, - ``, - ``, - ``, - ``, - ``, - ``, - ``, - ``, -] -let attributed=0 -allLevels.forEach(l => { - l.bricks=l.bricks.split('').map(c=>palette[c]) - if (!l.color && !l.svg) { - l.svg = randomPatterns[attributed%randomPatterns.length] - attributed++ - } -}) -if (allLevels.find(l => l.focus)) { - allLevels = allLevels.filter(l => l.focus) -} - -// Used to render perk icons -const perkIconsLevels = {} -allLevels = allLevels.filter(l => { - if (l.name.startsWith('perk:')) { - perkIconsLevels[l.name.split(':')[1]] = l - return false - } - return true -}) -allLevels.forEach((l, li) => { - l.threshold = li < 8 ? 0 : Math.round(Math.min(Math.pow(10, 1 + (li + l.size) / 30) * 10, 5000) * (li)) - l.sortKey = (Math.random() + 3) / 3.5 * l.bricks.filter(i => i).length -}) - -let runLevels = [] - -let currentLevel = 0; - -const bombSVG = document.createElement('img') -bombSVG.src = 'data:image/svg+xml;base64,' + btoa(` - -`); - - -// Whatever -let puckWidth = 200; -const perks = {}; - -let baseSpeed = 12; // applied to x and y -let combo = 1; - -function baseCombo() { - return 1 + perks.base_combo * 3 + perks.smaller_puck * 5; -} - -function resetCombo(x, y) { - const prev = combo; - combo = baseCombo(); - if (!levelTime) { - combo += perks.hot_start * 15; - } - if (prev > combo && perks.soft_reset) { - combo += Math.floor((prev - combo) / (1 + perks.soft_reset)) - } - const lost = Math.max(0, prev - combo); - if (lost) { - - for (let i = 0; i < lost && i < 8; i++) { - setTimeout(() => sounds.comboDecrease(), i * 100); - } - if (typeof x !== "undefined" && typeof y !== "undefined") { - flashes.push({ - type: "text", - text: "-" + lost, - time: levelTime, - color: "r", - x: x, - y: y, - duration: 150, - size: puckHeight, - }); - } - } - return lost -} - -function decreaseCombo(by, x, y) { - const prev = combo; - combo = Math.max(baseCombo(), combo - by); - const lost = Math.max(0, prev - combo); - - if (lost) { - sounds.comboDecrease(); - if (typeof x !== "undefined" && typeof y !== "undefined") { - flashes.push({ - type: "text", - text: "-" + lost, - time: levelTime, - color: "r", - x: x, - y: y, - duration: 300, - size: puckHeight, - }); - } - } -} - -let gridSize = 12; - -let running = false, puck = 400, pauseTimeout; - -function play() { - if (running) return - running = true - if (audioContext) { - audioContext.resume() - } - resumeRecording() -} - -function pause(playerAskedForPause) { - if (!running) return - if (pauseTimeout) return - - pauseTimeout = setTimeout(() => { - running = false - needsRender = true - if (audioContext) { - setTimeout(() => { - if (!running) - audioContext.suspend() - }, 1000) - } - pauseRecording() - pauseTimeout = null - }, Math.min(Math.max(0, pauseUsesDuringRun - 5) * 50, 500)) - - if (playerAskedForPause) { - // Pausing many times in a run will make pause slower - pauseUsesDuringRun++ - } - - if (document.exitPointerLock) { - document.exitPointerLock() - } - - -} - -let offsetX, offsetXRoundedDown, gameZoneWidth, gameZoneWidthRoundedUp, gameZoneHeight, brickWidth, needsRender = true; - -const background = document.createElement("img"); -const backgroundCanvas = document.createElement("canvas"); -background.addEventListener("load", () => { - needsRender = true -}) - - -const fitSize = () => { - const {width, height} = canvas.getBoundingClientRect(); - canvas.width = width; - canvas.height = height; - ctx.fillStyle = currentLevelInfo()?.color || 'black' - ctx.globalAlpha = 1 - ctx.fillRect(0, 0, width, height) - backgroundCanvas.width = width; - backgroundCanvas.height = height; - - - gameZoneHeight = isSettingOn("mobile-mode") ? (height * 80) / 100 : height; - const baseWidth = Math.round(Math.min(canvas.width, gameZoneHeight * 0.73)); - brickWidth = Math.floor(baseWidth / gridSize / 2) * 2; - gameZoneWidth = brickWidth * gridSize; - offsetX = Math.floor((canvas.width - gameZoneWidth) / 2); - offsetXRoundedDown = offsetX - if (offsetX < ballSize) offsetXRoundedDown = 0 - gameZoneWidthRoundedUp = width - 2 * offsetXRoundedDown - backgroundCanvas.title = 'resized' - // Ensure puck stays within bounds - setMousePos(puck); - coins = []; - flashes = []; - pause(true) - putBallsAtPuck(); - // For safari mobile https://css-tricks.com/the-trick-to-viewport-units-on-mobile/ - document.documentElement.style.setProperty('--vh', `${window.innerHeight * 0.01}px`); -}; -window.addEventListener("resize", fitSize); -window.addEventListener("fullscreenchange", fitSize); - -function recomputeTargetBaseSpeed() { - // We never want the ball to completely stop, it will move at least 3px per frame - baseSpeed = Math.max(3, gameZoneWidth / 12 / 10 + currentLevel / 3 + levelTime / (30 * 1000) - perks.slow_down * 2); -} - - -function brickCenterX(index) { - return offsetX + ((index % gridSize) + 0.5) * brickWidth; -} - -function brickCenterY(index) { - return (Math.floor(index / gridSize) + 0.5) * brickWidth; -} - - -function getRowColIndex(row, col) { - if (row < 0 || col < 0 || row >= gridSize || col >= gridSize) return -1; - return row * gridSize + col; -} - - -function spawnExplosion(count, x, y, color, duration = 150, size = coinSize) { - if (!!isSettingOn("basic")) return; - if (flashes.length > MAX_PARTICLES) { - // Avoid freezing when lots of explosion happen at once - count = 1 - } - for (let i = 0; i < count; i++) { - flashes.push({ - type: "particle", - time: levelTime, - size, - x: x + ((Math.random() - 0.5) * brickWidth) / 2, - y: y + ((Math.random() - 0.5) * brickWidth) / 2, - vx: (Math.random() - 0.5) * 30, - vy: (Math.random() - 0.5) * 30, - color, - duration: 150 - }); - } -} - - -let score = 0; - -let lastexplosion = 0; -let highScore = parseFloat(localStorage.getItem("breakout-3-hs") || "0"); - -let lastPlayedCoinGrab = 0 - -function addToScore(coin) { - coin.destroyed = true - score += coin.points; - addToTotalScore(coin.points) - if (score > highScore && !hadOverrides) { - highScore = score; - localStorage.setItem("breakout-3-hs", score); - } - if (!isSettingOn('basic')) { - flashes.push({ - type: "particle", - duration: 100 + Math.random() * 50, - time: levelTime, - size: coinSize / 2, - color: coin.color, - x: coin.previousx, - y: coin.previousy, - vx: (canvas.width - coin.x) / 100, - vy: -coin.y / 100, - ethereal: true, - }) - } - - if (Date.now() - lastPlayedCoinGrab > 16) { - lastPlayedCoinGrab = Date.now() - sounds.coinCatch(coin.x) - } - runStatistics.score += coin.points - - -} - -let balls = []; -let ballsColor = 'white' - -function resetBalls() { - const count = 1 + (perks?.multiball || 0); - const perBall = puckWidth / (count + 1); - balls = []; - ballsColor = currentLevelInfo()?.black_puck ? '#000' : "#FFF" - for (let i = 0; i < count; i++) { - const x = puck - puckWidth / 2 + perBall * (i + 1); - balls.push({ - x, - previousx: x, - y: gameZoneHeight - 1.5 * ballSize, - previousy: gameZoneHeight - 1.5 * ballSize, - vx: Math.random() > 0.5 ? baseSpeed : -baseSpeed, - vy: -baseSpeed, - sx: 0, - sy: 0, - sparks: 0, - piercedSinceBounce: 0, - hitSinceBounce: 0, - hitItem: [], - sapperUses: 0, - }); - } -} - -function putBallsAtPuck() { - // This reset could be abused to cheat quite easily - const count = balls.length; - const perBall = puckWidth / (count + 1); - balls.forEach((ball, i) => { - const x = puck - puckWidth / 2 + perBall * (i + 1); - ball.x = x - ball.previousx = x - ball.y = gameZoneHeight - 1.5 * ballSize - ball.previousy = ball.y - ball.vx = Math.random() > 0.5 ? baseSpeed : -baseSpeed - ball.vy = -baseSpeed - ball.sx = 0 - ball.sy = 0 - ball.hitItem = [] - ball.hitSinceBounce = 0 - ball.piercedSinceBounce = 0 - }); -} - -resetBalls(); -// Default, recomputed at each level load -let bricks = []; -let flashes = []; -let coins = []; -let levelStartScore = 0; -let levelMisses = 0; -let levelSpawnedCoins = 0; - - -function pickedUpgradesHTMl() { - let list = '' - for (let u of upgrades) { - for (let i = 0; i < perks[u.id]; i++) - list += u.icon + ' ' - } - - return list -} - -async function openUpgradesPicker() { - - const catchRate = (score - levelStartScore) / (levelSpawnedCoins || 1); - - let repeats = 1; - let choices = 3; - - let timeGain = '', catchGain = '', missesGain = '' - if (levelTime < 30 * 1000) { - repeats++; - choices++; - timeGain = " (+1 upgrade and choice)" - } else if (levelTime < 60 * 1000) { - choices++; - timeGain = " (+1 choice)" - } - if (catchRate === 1) { - repeats++; - choices++; - catchGain = " (+1 upgrade and choice)" - } else if (catchRate > 0.9) { - choices++; - catchGain = " (+1 choice)" - } - if (levelMisses === 0) { - repeats++; - choices++; - missesGain = " (+1 upgrade and choice)" - } else if (levelMisses <= 3) { - choices++; - missesGain = " (+1 choice)" - } - - - while (repeats--) { - const actions = pickRandomUpgrades(choices + perks.one_more_choice - perks.instant_upgrade); - if (!actions.length) break - let textAfterButtons = ` -

Upgrades picked so far :

${pickedUpgradesHTMl()}

-
-`; - - const upgradeId = await asyncAlert({ - title: "Pick an upgrade " + (repeats ? "(" + (repeats + 1) + ")" : ""), actions, - text: `

-You caught ${score - levelStartScore} coins ${catchGain} out of ${levelSpawnedCoins} in ${Math.round(levelTime / 1000)} seconds${timeGain}. - You missed ${levelMisses} times ${missesGain}.

`, - allowClose: false, - textAfterButtons - }); - perks[upgradeId]++; - if (upgradeId === 'instant_upgrade') { - repeats += 2 - } - - runStatistics.upgrades_picked++ - } - resetCombo(); - resetBalls(); -} - -function setLevel(l) { - - - pause(false) - if (l > 0) { - openUpgradesPicker().then(); - } - currentLevel = l; - - levelTime = 0; - lastTickDown = levelTime; - levelStartScore = score; - levelSpawnedCoins = 0; - levelMisses = 0; - runStatistics.levelsPlayed++ - - resetCombo(); - recomputeTargetBaseSpeed(); - resetBalls(); - - const lvl = currentLevelInfo(); - if (lvl.size !== gridSize) { - gridSize = lvl.size; - fitSize(); - } - coins = []; - bricks = [...lvl.bricks]; - flashes = []; - - // This caused problems with accented characters like the ô of côte d'ivoire for odd reasons - // background.src = 'data:image/svg+xml;base64,' + btoa(lvl.svg) - background.src = 'data:image/svg+xml;UTF8,' + lvl.svg - stopRecording() - startRecordingGame() -} - -function currentLevelInfo() { - return runLevels[currentLevel % runLevels.length]; -} - -function reset_perks() { - - for (let u of upgrades) { - perks[u.id] = 0; - } - - if (nextRunOverrides.perks) { - const first = Object.keys(nextRunOverrides.perks)[0] - Object.assign(perks, nextRunOverrides.perks) - nextRunOverrides.perks = null - return first - } - - const giftable = getPossibleUpgrades().filter(u => u.giftable) - const randomGift = isSettingOn('easy') ? 'slow_down' : giftable[Math.floor(Math.random() * giftable.length)].id; - perks[randomGift] = 1; - - return randomGift -} - -const upgrades = [ - { - "threshold": 0, - "id": "extra_life", - "name": "+1 life", - "max": 7, - "help": "Survive dropping the ball", - extraLevelsHelp: `One more life just in case`, - fullHelp: `Normally, you just have one life, and the run is over as soon as you drop it. - With this perk, you can survive dropping the ball once. A heart in the top right corner will remind you of how many extra lives you have. ` - }, - { - "threshold": 0, - "id": "streak_shots", - "giftable": true, - "name": "Single puck hit streak", - "max": 1, - "help": "More coins if you break many bricks at once.", - fullHelp: `Every time you break a brick, your combo (number of coins per bricks) increases by one. However, as soon as the ball touches your puck, - the combo is reset to its default value, and you'll just get one coin per brick. So you should try to hit many bricks in one go for more score. - Once your combo rises above the base value, your puck will become red to remind you that it will destroy your combo to touch it with the ball. - This can stack with other combo related perks, the combo will rise faster but reset more easily as any of the conditions is enough to reset it. ` - }, - - { - "threshold": 0, - "id": "base_combo", - "giftable": true, - "name": "+3 base combo", - "max": 7, - "help": "3 more coins from every brick.", - extraLevelsHelp: `Combo starts 3 points higher`, - - fullHelp: `Your combo (number of coins per bricks) normally starts at 1 at the beginning of the level, and resets to one when you bounce around without hitting anything. - With this perk, the combo starts 3 points higher, so you'll always get at least 4 coins per brick. Whenever your combo reset, it will go back to 4 and not 1. - Your ball will glitter a bit to indicate that its combo is higher than one.` - }, - { - "threshold": 0, - "id": "slow_down", - "name": "Slower ball", - "max": 2, - "help": "slow down the ball", - extraLevelsHelp: `Make it even slower`, - - fullHelp: `The ball starts relatively slow, but every level of your run it will start a bit faster, and it will also accelerate if you spend a lot of time in one level. This perk makes it - more manageable. You can get it at the start every time by enabling kid mode in the menu.` - }, - { - "threshold": 0, - "id": "bigger_puck", - "name": "Bigger puck", - "max": 2, - "help": "Catches more coins", - extraLevelsHelp: `Even bigger puck`, - fullHelp: `A bigger puck makes it easier to never miss the ball and to catch more coins, and also to precisely angle the bounces (the ball's angle only depends on where it hits the puck). - However, a large puck is harder to use around the sides of the level, and will make it sometimes unavoidable to miss (not hit anything) which comes with downsides. ` - }, - { - "threshold": 0, - "id": "viscosity", - "name": "Viscosity", - "max": 3, - "help": "Slower coins fall", - extraLevelsHelp: `Even slower fall`, - tryout: { - perks: {viscosity: 3, base_combo: 3}, - level: 'Waves' - }, - fullHelp: `Coins normally accelerate with gravity and explosions to pretty high speeds. This perk constantly makes them slow down, as if they were in some sort of viscous liquid. - This makes catching them easier, and combines nicely with perks that influence the coin's movement. ` - }, - { - "threshold": 0, - "id": "sides_are_lava", - "giftable": true, - "name": "Shoot straight", - "max": 1, - "help": "More coins if you don't touch the sides.", - - fullHelp: `Whenever you break a brick, your combo will increase by one, so you'll get one more coin all the next bricks you break. - However, your combo will reset as soon as your ball hits the left or right side. - As soon as your combo rises, the sides become red to remind you that you should avoid hitting them. The effect stacks with other combo perks, combo rises faster with more upgrades but will also reset if any - of the reset conditions are met.` - }, - { - "threshold": 0, - "id": "top_is_lava", - "giftable": true, - "name": "Sky is the limit", - "max": 1, - "help": "More coins if you don't touch the top.", - - fullHelp: `Whenever you break a brick, your combo will increase by one. However, your combo will reset as soon as your ball hit the top of the screen. - When your combo is above the minimum, a red bar will appear at the top to remind you that you should avoid hitting it. - The effect stacks with other combo perks.` - }, - { - "threshold": 0, - "id": "skip_last", - "name": "Easy Cleanup", - "max": 7, - "help": "The last brick will self-destruct", - extraLevelsHelp: `Level clears one brick earlier`, - fullHelp: `You need to break all bricks to go to the next level. However, it can be hard to get the last ones. - Clearing a level early brings extra choices when upgrading. Never missing the bricks is also very beneficial. - So if you find it difficult to break the last bricks, getting this perk a few time can help.` - }, - { - "threshold": 500, - "id": "telekinesis", - "giftable": true, - "name": "Puck controls ball", - "max": 2, - "help": "Control the ball's trajectory", - extraLevelsHelp: `Stronger effect on the ball`, - fullHelp: `Right after the ball hits your puck, you'll be able to direct it left and right by moving your puck. - The effect stops when the ball hits a brick and resets the next time it touches the puck. It also does nothing when the ball is going downward after bouncing at the top. ` - }, - { - "threshold": 1000, - "id": "coin_magnet", - "name": "Coins magnet", - "max": 3, - "help": "Puck attracts coins", - tryout: { - perks: {coin_magnet: 3, base_combo: 3} - }, - extraLevelsHelp: `Stronger effect on the coins`, - fullHelp: `Directs the coins to the puck. The effect is stronger if the coin is close to it already. Catching 90% or 100% of coins bring special bonuses in the game. - Another way to catch more coins is to hit bricks from the bottom. The ball's speed and direction impacts the spawned coin's velocity. ` - }, - { - "threshold": 1500, - "id": "multiball", - "giftable": true, - "name": "+1 ball", - "max": 6, - "help": "Start with two balls", - extraLevelsHelp: `One more ball`, - fullHelp: `As soon as you drop the ball in Breakout 71, you loose. With this perk, you get two balls, and so you can afford to lose one. - The lost balls come back on the next level or whenever you use one of your extra lives, if you picked that perk. Having more than one balls makes - some further perks available, and of course clears the level faster.` - }, - { - "threshold": 2000, - "id": "smaller_puck", - "name": "Smaller puck", - "max": 2, - "help": "Also gives +5 base combo", - extraLevelsHelp: `Even smaller puck and higher base combo`, - fullHelp: `This makes the puck smaller, which in theory makes some corner shots easier, but really just raises the difficulty. - That's why you also get a nice bonus of +5 coins per brick for all bricks you'll break after picking this. ` - }, - { - "threshold": 3000, - "id": "pierce", - "giftable": true, - "name": "Piercing", - "max": 3, - "help": "Ball pierces 3 bricks", - extraLevelsHelp: `Pierce 3 more bricks`, - fullHelp: `The ball normally bounces as soon as it touches something. With this perk, it will continue its trajectory for up to 3 bricks broken. - After that, it will bounce on the 4th brick, and you'll need to touch the puck to reset the counter. This combines particularly well with Sapper. ` - }, - { - "threshold": 4000, - "id": "picky_eater", - "giftable": true, - "name": "Picky eater", - "max": 1, - "help": "More coins if you break bricks color by color.", - - tryout: { - perks: {picky_eater: 1}, - level: 'Mountain' - }, - fullHelp: `Whenever you break a brick the same color as your ball, your combo increases by one. - If it's a different color, the ball takes that new color, but the combo resets. - The bricks with the right color will get a white border. - Once you get a combo higher than your minimum, the bricks of the wrong color will get a red halo. - If you have more than one ball, they all change color whenever one of them hits a brick. - - ` - }, - { - "threshold": 5000, - "id": "metamorphosis", - "name": "Stain", - "max": 1, - "help": "Coins color the bricks they touch", - tryout: { - perks: {metamorphosis: 3}, - level: 'Lines' - }, - - fullHelp: `With this perk, coins will be of the color of the brick they come from, and will color the first brick they touch in the same color. Coins spawn with the speed - of the ball that broke them, which means you can aim a bit in the direction of the bricks you want to "paint". - ` - }, - { - "threshold": 6000, - "id": "compound_interest", - "giftable": true, - "name": "Compound interest", - "max": 3, - "help": "More coins if you catch them all.", - extraLevelsHelp: `Combo grows faster, but missed coins hurt it more`, - - fullHelp: `Your combo will grow by one every time you break a brick, spawning more and more coin with every brick you break. Be sure however to catch every one of those coins - with your puck, as any lost coin will decrease your combo by one point. One your combo is above the minimum, the bottom of the play area will - have a red line to remind you that coins should not go there. This perk combines with other combo perks, the combo will rise faster but reset more easily. - ` - }, - { - "threshold": 7000, - "id": "hot_start", - "giftable": true, - "name": "Hot start", - "max": 3, - "help": "More coins for 15s.", - extraLevelsHelp: `Combo starts higher but shrinks faster`, - - fullHelp: `At the start of every level, your combo will start at +15 points, but then every second it will be decreased by one. This means the first 15 seconds in a level will spawn - many more coins than the following ones, and you should make sure that you clear the level quickly. The effect stacks with other combo related perks, so you might be able to raise - the combo after the 15s timeout, but it will keep ticking down. Every time you take the perk again, the effect will be more dramatic. - ` - }, - { - "threshold": 9000, - "id": "sapper", - "giftable": true, - "name": "Sapper", - "max": 7, - "help": "1st brick hit becomes bomb", - extraLevelsHelp: `1 more brick replaced by a bomb`, - fullHelp: `Instead of just disappearing, the first brick you break will be replaced by a bomb brick. Bouncing the ball on the puck re-arms the effect. "Piercing" will instantly - detonate the bomb that was just placed. Leveling-up this perk will allow you to place more bombs. Remember that bombs impact the velocity of nearby coins, so too many explosions - could make it hard to catch the fruits of your hard work. - ` - }, - { - "threshold": 11000, - "id": "bigger_explosions", - "name": "Kaboom", - "max": 1, - "help": "Bigger explosions", - tryout: { - perks: {bigger_explosions: 1}, - level: 'Ship' - }, - fullHelp: `The default explosion clears a 3x3 square, with this it becomes a 5x5 square, and the blowback on the coins is also significantly stronger. ` - }, - { - "threshold": 13000, - "id": "extra_levels", - "name": "+1 level", - "max": 3, - "help": "Play 8 levels instead of 7", - extraLevelsHelp: `1 more level to play`, - fullHelp: `The default run can last a max of 7 levels, after which the game is over and whatever score you reached is your run score. - Each level of this perk lets you go one level higher. The last levels are often the ones where you make the most score, so the difference can be dramatic.` - }, - { - "threshold": 15000, - "id": "pierce_color", - "name": "Color pierce", - "max": 1, - "help": "Balls pierce bricks of their color", - fullHelp: `Whenever a ball hits a brick of the same color, it will just go through unimpeded. - Once it reaches a brick of a different color, it will break it, take its color and bounce.` - }, - { - "threshold": 18000, - "id": "soft_reset", - "name": "Soft reset", - "max": 2, - "help": "Combo grows slower but resets less", - extraLevelsHelp: `Even slower combo growth but softer reset`, - fullHelp: `The combo normally climbs every time you break a brick. This will sometimes cancel that climb, but also limit the impact of a combo reset.` - }, - { - "threshold": 21000, - "id": "ball_repulse_ball", - "name": "Personal space", - requires: 'multiball', - "max": 3, - "help": "Balls repulse balls.", - extraLevelsHelp: 'Stronger repulsion force ', - tryout: { - perks: {ball_repulse_ball: 1, multiball: 2}, - }, - fullHelp: `Balls that are less than half a screen width away will start repulsing each other. The repulsion force is stronger if they are close to each other. - Particles will jet out to symbolize this force being applied. This perk is only offered if you have more than one ball already.` - }, - { - "threshold": 25000, - "id": "ball_attract_ball", - requires: 'multiball', - "name": "Gravity", - "max": 3, - "help": "Balls attract balls.", extraLevelsHelp: 'Stronger attraction force ', - tryout: { - perks: {ball_attract_ball: 1, multiball: 2}, - }, - fullHelp: `Balls that are more than half a screen width away will start attracting each other. The attraction force is stronger when they are furthest away from each other. - Rainbow particles will fly to symbolize the attraction force. This perk is only offered if you have more than one ball already.` - }, - { - "threshold": 30000, - "id": "puck_repulse_ball", - "name": "Soft landing", - extraLevelsHelp: 'Stronger repulsion force ', - "max": 3, - "help": "Puck repulses balls.", - fullHelp: `When a ball gets close to the perk, it will start slowing down, and even potentially bouncing without touching the puck.` - }, - { - "threshold": 35000, - "id": "wind", - "name": "Wind", - "max": 3, - "help": "Puck position creates wind.", - extraLevelsHelp: 'Stronger wind force ', - fullHelp: `The wind depends on where your puck is, if it's in the center of the screen nothing happens, if it's on the left it will blow leftwise, if it's on the right of the screen - then it will blow rightwise. The wind affects both the balls and coins.` - }, - { - "threshold": 40000, - "id": "sturdy_bricks", - "name": "Sturdy bricks", - "max": 4, - "help": "Bricks sometimes resist hits but drop more coins.", - extraLevelsHelp: 'Bricks resist more and drop more coins ', - fullHelp: `With level one of this perk, the ball has a 20% chance to bounce harmlessly on bricks, - but generates 10% more coins when it does break one. - This +10% is not shown in the combo number. At level 4 the ball has 80% chance of bouncing and brings 40% more coins.` - }, - { - "threshold": 45000, - "id": "respawn", - "name": "Respawn", - "max": 4, - "help": "The first brick hit of two+ will respawn.", - extraLevelsHelp: 'More bricks can respawn ', - fullHelp: `After breaking two or more bricks, when the ball hits the puck, the first brick will be put back in place, provided that space is free and the brick wasn't a bomb. - Some particle effect will let you know where bricks will appear. Levelling this up lets you respawn up to 4 bricks at a time, but there should always be at least one destroyed. - ` - }, - { - "threshold": 50000, - "id": "one_more_choice", - "name": "+1 choice permanently", - "max": 3, - "help": "Further level ups will offer one more option in the list", - extraLevelsHelp: 'Even more options ', - fullHelp: `Every upgrade menu will have one more option. - Doesn't increase the number of upgrades you can pick. - ` - }, - { - "threshold": 55000, - "id": "instant_upgrade", - "name": "+2 upgrades now", - "max": 2, - "help": "-1 choice permanently", - extraLevelsHelp: 'Even fewer options ', - fullHelp: `Immediately pick two upgrades, so that you get one free one and one to repay the one used to get this perk. - Every further menu to pick upgrades will have fewer options to choose from. - ` - }, -] - - -let totalScoreAtRunStart = getTotalScore() - -function getPossibleUpgrades() { - return upgrades - .filter(u => totalScoreAtRunStart >= u.threshold) - .filter(u => !u.requires || perks[u.requires]) -} - - -function shuffleLevels(nameToAvoid = null) { - const target = nextRunOverrides?.level; - if (target) { - runLevels = allLevels.filter(l => l.name === target) - nextRunOverrides.level = null - if (runLevels.length) return - } - - runLevels = allLevels - .filter((l, li) => totalScoreAtRunStart >= l.threshold) - .filter(l => l.name !== nameToAvoid || allLevels.length === 1) - .sort(() => Math.random() - 0.5) - .slice(0, 7 + 3) - .sort((a, b) => a.sortKey - b.sortKey); - -} - -function getUpgraderUnlockPoints() { - let list = [] - - upgrades - .forEach(u => { - if (u.threshold) { - list.push({ - threshold: u.threshold, - title: u.name + ' (Perk)' - }) - } - }) - - allLevels.forEach((l, li) => { - list.push({ - threshold: l.threshold, - title: l.name + ' (Level)', - }) - }) - - return list.filter(o => o.threshold).sort((a, b) => a.threshold - b.threshold) -} - - -let lastOffered = {} - -function dontOfferTooSoon(id) { - lastOffered[id] = Math.round(Date.now() / 1000) -} - -function pickRandomUpgrades(count) { - - let list = getPossibleUpgrades() - .map(u => ({...u, score: Math.random() + (lastOffered[u.id] || 0)})) - .sort((a, b) => a.score - b.score) - .filter(u => perks[u.id] < u.max) - .slice(0, count) - .sort((a, b) => a.id > b.id ? 1 : -1) - - list.forEach(u => { - dontOfferTooSoon(u.id) - }) - - return list.map(u => ({ - text: u.name + (perks[u.id] ? ' lvl ' + (perks[u.id] + 1) : ''), - icon: u.icon, - value: u.id, - help: (perks[u.id] && u.extraLevelsHelp) || u.help, - // max: u.max, - // checked: perks[u.id] - })) - - -} - -let nextRunOverrides = {level: null, perks: null} -let hadOverrides = false, pauseUsesDuringRun = 0 - -function restart() { - hadOverrides = !!(nextRunOverrides.level || nextRunOverrides.perks) - // When restarting, we want to avoid restarting with the same level we're on, so we exclude from the next - // run's level list - totalScoreAtRunStart = getTotalScore() - shuffleLevels(levelTime || score ? currentLevelInfo().name : null); - resetRunStatistics() - score = 0; - pauseUsesDuringRun = 0 - - const randomGift = reset_perks(); - - dontOfferTooSoon(randomGift) - - setLevel(0); - pauseRecording() -} - -let keyboardPuckSpeed = 0 - -function setMousePos(x) { - - needsRender = true; - puck = x; - - // We have borders visible, enforce them - if (puck < offsetXRoundedDown + puckWidth / 2) { - puck = offsetXRoundedDown + puckWidth / 2; - } - if (puck > offsetXRoundedDown + gameZoneWidthRoundedUp - puckWidth / 2) { - puck = offsetXRoundedDown + gameZoneWidthRoundedUp - puckWidth / 2; - } - if (!running && !levelTime) { - putBallsAtPuck(); - } -} - -canvas.addEventListener("mouseup", (e) => { - if (e.button !== 0) return; - if (running) { - pause(true) - } else { - play() - if (isSettingOn('pointerLock')) { - canvas.requestPointerLock() - } - } -}); - -canvas.addEventListener("mousemove", (e) => { - if (document.pointerLockElement === canvas) { - setMousePos(puck + e.movementX); - } else { - setMousePos(e.x); - } -}); - -canvas.addEventListener("touchstart", (e) => { - e.preventDefault(); - if (!e.touches?.length) return; - setMousePos(e.touches[0].pageX); - play() -}); -canvas.addEventListener("touchend", (e) => { - e.preventDefault(); - pause(true) -}); -canvas.addEventListener("touchcancel", (e) => { - e.preventDefault(); - pause(true) - needsRender = true -}); -canvas.addEventListener("touchmove", (e) => { - if (!e.touches?.length) return; - setMousePos(e.touches[0].pageX); -}); - -let lastTick = performance.now(); - -function brickIndex(x, y) { - return getRowColIndex(Math.floor(y / brickWidth), Math.floor((x - offsetX) / brickWidth)) -} - -function hasBrick(index) { - if (bricks[index]) return index; -} - -function hitsSomething(x, y, radius) { - return (hasBrick(brickIndex(x - radius, y - radius)) ?? hasBrick(brickIndex(x + radius, y - radius)) ?? hasBrick(brickIndex(x + radius, y + radius)) ?? hasBrick(brickIndex(x - radius, y + radius))); -} - -function shouldPierceByColor(vhit, hhit, chit) { - if (!perks.pierce_color) return false - if (typeof vhit !== 'undefined' && bricks[vhit] !== ballsColor) { - return false - } - if (typeof hhit !== 'undefined' && bricks[hhit] !== ballsColor) { - return false - } - if (typeof chit !== 'undefined' && bricks[chit] !== ballsColor) { - return false - } - return true -} - -function brickHitCheck(ballOrCoin, radius, isBall) { - // Make ball/coin bonce, and return bricks that were hit - const {x, y, previousx, previousy} = ballOrCoin; - - const vhit = hitsSomething(previousx, y, radius); - const hhit = hitsSomething(x, previousy, radius); - const chit = (typeof vhit == "undefined" && typeof hhit == "undefined" && hitsSomething(x, y, radius)) || undefined; - - - let pierce = isBall && ballOrCoin.piercedSinceBounce < perks.pierce * 3; - if (pierce && (typeof vhit !== "undefined" || typeof hhit !== "undefined" || typeof chit !== "undefined")) { - ballOrCoin.piercedSinceBounce++ - } - if (isBall && shouldPierceByColor(vhit, hhit, chit)) { - pierce = true - } - - - if (typeof vhit !== "undefined" || typeof chit !== "undefined") { - if (!pierce) { - ballOrCoin.y = ballOrCoin.previousy; - ballOrCoin.vy *= -1; - } - - if (!isBall) { - // Roll on corners - const leftHit = bricks[brickIndex(x - radius, y + radius)]; - const rightHit = bricks[brickIndex(x + radius, y + radius)]; - - if (leftHit && !rightHit) { - ballOrCoin.vx += 1; - } - if (!leftHit && rightHit) { - ballOrCoin.vx -= 1; - } - } - } - if (typeof hhit !== "undefined" || typeof chit !== "undefined") { - if (!pierce) { - ballOrCoin.x = ballOrCoin.previousx; - ballOrCoin.vx *= -1; - } - } - - return vhit ?? hhit ?? chit; -} - -function bordersHitCheck(coin, radius, delta) { - if (coin.destroyed) return; - coin.previousx = coin.x; - coin.previousy = coin.y; - coin.x += coin.vx * delta; - coin.y += coin.vy * delta; - coin.sx ||= 0; - coin.sy ||= 0; - coin.sx += coin.previousx - coin.x; - coin.sy += coin.previousy - coin.y; - coin.sx *= 0.9; - coin.sy *= 0.9; - - if (perks.wind) { - coin.vx += (puck - (offsetX + gameZoneWidth / 2)) / gameZoneWidth * perks.wind * 0.5; - } - - let vhit = 0, hhit = 0; - - - if (coin.x < offsetXRoundedDown + radius) { - coin.x = offsetXRoundedDown + radius; - coin.vx *= -1; - hhit = 1; - } - if (coin.y < radius) { - coin.y = radius; - coin.vy *= -1; - vhit = 1; - } - if (coin.x > canvas.width - offsetXRoundedDown - radius) { - coin.x = canvas.width - offsetXRoundedDown - radius; - coin.vx *= -1; - hhit = 1; - } - - return hhit + vhit * 2; -} - -let lastTickDown = 0; - - -function tick() { - - recomputeTargetBaseSpeed(); - const currentTick = performance.now(); - - puckWidth = (gameZoneWidth / 12) * (3 - perks.smaller_puck + perks.bigger_puck); - - if (keyboardPuckSpeed) { - setMousePos(puck + keyboardPuckSpeed) - - } - - if (running) { - - levelTime += currentTick - lastTick; - runStatistics.runTime += currentTick - lastTick - runStatistics.max_combo = Math.max(runStatistics.max_combo, combo) - - // How many times to compute - let delta = Math.min(4, (currentTick - lastTick) / (1000 / 60)); - delta *= running ? 1 : 0 - - - coins = coins.filter((coin) => !coin.destroyed); - balls = balls.filter((ball) => !ball.destroyed); - - const remainingBricks = bricks.filter((b) => b && b !== "black").length; - - if (levelTime > lastTickDown + 1000 && perks.hot_start) { - lastTickDown = levelTime; - decreaseCombo(perks.hot_start, puck, gameZoneHeight - 2 * puckHeight); - } - - if (remainingBricks <= perks.skip_last) { - bricks.forEach((type, index) => { - if (type) { - explodeBrick(index, balls[0], true); - } - }); - } - if (!remainingBricks && !coins.length) { - - if (currentLevel + 1 < max_levels()) { - setLevel(currentLevel + 1); - } else { - gameOver("Run finished with " + score + " points", "You cleared all levels for this run."); - } - } else if (running || levelTime) { - let playedCoinBounce = false; - const coinRadius = Math.round(coinSize / 2); - - - coins.forEach((coin) => { - if (coin.destroyed) return; - if (perks.coin_magnet) { - coin.vx += ((delta * (puck - coin.x)) / (100 + Math.pow(coin.y - gameZoneHeight, 2) + Math.pow(coin.x - puck, 2))) * perks.coin_magnet * 100; - } - - const ratio = 1 - (perks.viscosity * 0.03 + 0.005) * delta; - - coin.vy *= ratio; - coin.vx *= ratio; - if (coin.vx > 7 * baseSpeed) coin.vx = 7 * baseSpeed - if (coin.vx < -7 * baseSpeed) coin.vx = -7 * baseSpeed - if (coin.vy > 7 * baseSpeed) coin.vy = 7 * baseSpeed - if (coin.vy < -7 * baseSpeed) coin.vy = -7 * baseSpeed - coin.a += coin.sa - - // Gravity - coin.vy += delta * coin.weight * 0.8; - - const speed = Math.abs(coin.sx) + Math.abs(coin.sx); - const hitBorder = bordersHitCheck(coin, coinRadius, delta); - - if (coin.y > gameZoneHeight - coinRadius - puckHeight && coin.y < gameZoneHeight + puckHeight + coin.vy && Math.abs(coin.x - puck) < coinRadius + puckWidth / 2 + // a bit of margin to be nice - puckHeight) { - addToScore(coin); - - } else if (coin.y > canvas.height + coinRadius) { - coin.destroyed = true; - if (perks.compound_interest) { - decreaseCombo(coin.points * perks.compound_interest, coin.x, canvas.height - coinRadius); - } - } - - const hitBrick = brickHitCheck(coin, coinRadius, false); - - if (perks.metamorphosis && typeof hitBrick !== "undefined") { - if (bricks[hitBrick] && coin.color !== bricks[hitBrick] && bricks[hitBrick] !== "black" && !coin.coloredABrick) { - bricks[hitBrick] = coin.color; - coin.coloredABrick = true - } - } - if (typeof hitBrick !== "undefined" || hitBorder) { - coin.vx *= 0.8; - coin.vy *= 0.8; - coin.sa *= 0.9 - if (speed > 20 && !playedCoinBounce) { - playedCoinBounce = true; - sounds.coinBounce(coin.x, 0.2); - } - - if (Math.abs(coin.vy) < 3) { - coin.vy = 0; - } - } - }); - - balls.forEach((ball) => ballTick(ball, delta)); - - if (perks.wind) { - - const windD = (puck - (offsetX + gameZoneWidth / 2)) / gameZoneWidth * 2 * perks.wind - for (var i = 0; i < perks.wind; i++) { - if (Math.random() * Math.abs(windD) > 0.5) { - flashes.push({ - type: "particle", - duration: 150, - ethereal: true, - time: levelTime, - size: coinSize / 2, - color: rainbowColor(), - x: offsetXRoundedDown + Math.random() * gameZoneWidthRoundedUp, - y: Math.random() * gameZoneHeight, - vx: windD * 8, - vy: 0, - }); - } - } - } - - - flashes.forEach((flash) => { - if (flash.type === "particle") { - flash.x += flash.vx * delta; - flash.y += flash.vy * delta; - if (!flash.ethereal) { - flash.vy += 0.5; - if (hasBrick(brickIndex(flash.x, flash.y))) { - flash.destroyed = true; - } - } - } - }); - } - - - if (combo > baseCombo()) { - // The red should still be visible on a white bg - const baseParticle = !isSettingOn('basic') && (combo - baseCombo()) * Math.random() > 5 && running && { - type: "particle", - duration: 100 * (Math.random() + 1), - time: levelTime, - size: coinSize / 2, - color: 'red', - ethereal: true, - } - - if (perks.top_is_lava) { - baseParticle && flashes.push({ - ...baseParticle, - x: offsetXRoundedDown + Math.random() * gameZoneWidthRoundedUp, - y: 0, - vx: (Math.random() - 0.5) * 10, - vy: 5, - }) - } - if (perks.sides_are_lava) { - const fromLeft = Math.random() > 0.5 - baseParticle && flashes.push({ - ...baseParticle, - x: offsetXRoundedDown + (fromLeft ? 0 : gameZoneWidthRoundedUp), - y: Math.random() * gameZoneHeight, - vx: fromLeft ? 5 : -5, - vy: (Math.random() - 0.5) * 10, - }) - } - if (perks.compound_interest) { - let x = puck - do { - x = offsetXRoundedDown + gameZoneWidthRoundedUp * Math.random() - } while (Math.abs(x - puck) < puckWidth / 2) - baseParticle && flashes.push({ - ...baseParticle, - x, - y: gameZoneHeight, - vx: (Math.random() - 0.5) * 10, - vy: -5, - }) - } - if (perks.streak_shots) { - const pos = (0.5 - Math.random()) - baseParticle && flashes.push({ - ...baseParticle, - duration: 100, - x: puck + puckWidth * pos, - y: gameZoneHeight - puckHeight, - vx: (pos) * 10, - vy: -5, - }) - } - } - } - - render(); - - requestAnimationFrame(tick); - lastTick = currentTick; -} - -function isTelekinesisActive(ball) { - return perks.telekinesis && !ball.hitSinceBounce && ball.vy < 0; -} - -function ballTick(ball, delta) { - ball.previousvx = ball.vx; - ball.previousvy = ball.vy; - - if (isTelekinesisActive(ball)) { - ball.vx += ((puck - ball.x) / 1000) * delta * perks.telekinesis; - } - - const speedLimitDampener = 1 + perks.telekinesis + perks.ball_repulse_ball + perks.puck_repulse_ball + perks.ball_attract_ball - if (ball.vx * ball.vx + ball.vy * ball.vy < baseSpeed * baseSpeed * 2) { - ball.vx *= (1 + .02 / speedLimitDampener); - ball.vy *= (1 + .02 / speedLimitDampener); - } else { - ball.vx *= (1 - .02 / speedLimitDampener); - if (Math.abs(ball.vy) > 0.5 * baseSpeed) { - ball.vy *= (1 - .02 / speedLimitDampener); - } - } - - if (perks.ball_repulse_ball) { - for (b2 of balls) { - // avoid computing this twice, and repulsing itself - if (b2.x >= ball.x) continue - repulse(ball, b2, perks.ball_repulse_ball, true) - } - } - if (perks.ball_attract_ball) { - for (b2 of balls) { - // avoid computing this twice, and repulsing itself - if (b2.x >= ball.x) continue - attract(ball, b2, perks.ball_attract_ball) - } - } - if (perks.puck_repulse_ball && Math.abs(ball.x - puck) < puckWidth / 2 + ballSize * (9 + perks.puck_repulse_ball) / 10) { - repulse(ball, { - x: puck, - y: gameZoneHeight - }, perks.puck_repulse_ball, false) - } - - if (perks.respawn && ball.hitItem?.length > 1 && !isSettingOn('basic')) { - for (let i = 0; i < ball.hitItem?.length - 1 && i < perks.respawn; i++) { - const {index, color} = ball.hitItem[i] - if (bricks[index] || color === 'black') continue - const vertical = Math.random() > 0.5 - const dx = Math.random() > 0.5 ? 1 : -1 - const dy = Math.random() > 0.5 ? 1 : -1 - - flashes.push({ - type: "particle", - duration: 250, - ethereal: true, - time: levelTime, - size: coinSize / 2, - color, - x: brickCenterX(index) + dx * brickWidth / 2, - y: brickCenterY(index) + dy * brickWidth / 2, - vx: vertical ? 0 : -dx * baseSpeed, - vy: vertical ? -dy * baseSpeed : 0, - }); - } - } - - const borderHitCode = bordersHitCheck(ball, ballSize / 2, delta); - if (borderHitCode) { - if (perks.sides_are_lava && borderHitCode % 2) { - resetCombo(ball.x, ball.y); - } - if (perks.top_is_lava && borderHitCode >= 2) { - resetCombo(ball.x, ball.y + ballSize); - } - sounds.wallBeep(ball.x); - ball.bouncesList?.push({x: ball.previousx, y: ball.previousy}) - } - - // Puck collision - const ylimit = gameZoneHeight - puckHeight - ballSize / 2; - if (ball.y > ylimit && Math.abs(ball.x - puck) < ballSize / 2 + puckWidth / 2 && ball.vy > 0) { - const speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy); - const angle = Math.atan2(-puckWidth / 2, ball.x - puck); - ball.vx = speed * Math.cos(angle); - ball.vy = speed * Math.sin(angle); - - sounds.wallBeep(ball.x); - if (perks.streak_shots) { - resetCombo(ball.x, ball.y); - } - - if (perks.respawn) { - ball.hitItem.slice(0, -1).slice(0, perks.respawn) - .forEach(({index, color}) => { - if (!bricks[index] && color !== 'black') - bricks[index] = color - }) - } - ball.hitItem = [] - if (!ball.hitSinceBounce) { - runStatistics.misses++ - levelMisses++; - resetCombo(ball.x, ball.y) - flashes.push({ - type: "text", - text: 'miss', - duration: 500, - time: levelTime, - size: puckHeight * 1.5, - color: 'red', - x: puck, - y: gameZoneHeight - puckHeight * 2, - - }); - - - } - runStatistics.puck_bounces++ - ball.hitSinceBounce = 0; - ball.sapperUses = 0; - ball.piercedSinceBounce = 0; - ball.bouncesList = [{ - x: ball.previousx, - y: ball.previousy - }] - } - - if (ball.y > gameZoneHeight + ballSize / 2 && running) { - ball.destroyed = true; - runStatistics.balls_lost++ - if (!balls.find((b) => !b.destroyed)) { - if (perks.extra_life) { - perks.extra_life--; - resetBalls(); - sounds.revive(); - pause(false) - coins = []; - flashes.push({ - type: "ball", - duration: 500, - time: levelTime, - size: brickWidth * 2, - color: "white", - x: ball.x, - y: ball.y, - }); - } else { - gameOver("Game Over", "You dropped the ball after catching " + score + " coins. "); - } - } - } - const hitBrick = brickHitCheck(ball, ballSize / 2, true); - if (typeof hitBrick !== "undefined") { - const initialBrickColor = bricks[hitBrick] - - explodeBrick(hitBrick, ball, false); - - if (ball.sapperUses < perks.sapper && initialBrickColor !== "black" && - // don't replace a brick that bounced with sturdy_bricks - !bricks[hitBrick]) { - bricks[hitBrick] = "black"; - ball.sapperUses++ - } - - } - - if (!isSettingOn("basic")) { - ball.sparks += (delta * (combo - 1)) / 30; - if (ball.sparks > 1) { - flashes.push({ - type: "particle", - duration: 100 * ball.sparks, - time: levelTime, - size: coinSize / 2, - color: ballsColor, - x: ball.x, - y: ball.y, - vx: (Math.random() - 0.5) * baseSpeed, - vy: (Math.random() - 0.5) * baseSpeed, - }); - ball.sparks = 0; - } - } -} - -let runStatistics = {}; - - -function getTotalScore() { - try { - return JSON.parse(localStorage.getItem('breakout_71_total_score') || '0') - } catch (e) { - return 0 - } -} - -function addToTotalScore(points) { - if (hadOverrides) return - try { - localStorage.setItem('breakout_71_total_score', JSON.stringify(getTotalScore() + points)) - } catch (e) { - } -} - -function addToTotalPlayTime(ms) { - try { - localStorage.setItem('breakout_71_total_play_time', JSON.stringify(JSON.parse(localStorage.getItem('breakout_71_total_play_time') || '0') + ms)) - } catch (e) { - } -} - - -function gameOver(title, intro) { - if (!running) return; - pause(true) - stopRecording() - addToTotalPlayTime(runStatistics.runTime) - runStatistics.max_level = currentLevel + 1 - - let animationDelay = -300 - const getDelay = () => { - animationDelay += 800 - return 'animation-delay:' + animationDelay + 'ms;' - } - // unlocks - let unlocksInfo = '' - const endTs = getTotalScore() - const startTs = endTs - score - const list = getUpgraderUnlockPoints() - list.filter(u => u.threshold > startTs && u.threshold < endTs).forEach(u => { - unlocksInfo += ` -

- ${u.title} - -

-` - }) - const previousUnlockAt = findLast(list, u => u.threshold <= endTs)?.threshold || 0 - const nextUnlock = list.find(u => u.threshold > endTs) - - if (nextUnlock) { - const total = nextUnlock?.threshold - previousUnlockAt - const done = endTs - previousUnlockAt - intro += `Score ${nextUnlock.threshold - endTs} more points to reach the next unlock.` - - const scaleX = (done / total).toFixed(2) - unlocksInfo += ` -

- ${nextUnlock.title} - -

- -` - list.slice(list.indexOf(nextUnlock) + 1).slice(0, 3).forEach(u => { - unlocksInfo += ` -

- ${u.title} -

-` - }) - } - - - // Avoid the sad sound right as we restart a new games - combo = 1 - asyncAlert({ - allowClose: true, title, text: ` -

${intro}

- ${unlocksInfo} - `, textAfterButtons: ` -
- ${getHistograms(true)} - ` - }).then(() => restart()); -} - -function getHistograms(saveStats) { - - if (hadOverrides) { - return '' - } - - let runStats = '' - try { - // Stores only top 100 runs - let runsHistory = JSON.parse(localStorage.getItem('breakout_71_runs_history') || '[]'); - runsHistory.sort((a, b) => a.score - b.score).reverse() - runsHistory = runsHistory.slice(0, 10) - runsHistory.push(runStatistics) - - // Generate some histogram - if (saveStats) { - localStorage.setItem('breakout_71_runs_history', JSON.stringify(runsHistory, null, 2)) - } - const makeHistogram = (title, getter, unit) => { - let values = runsHistory.map(h => getter(h) || 0) - let min = Math.min(...values) - let max = Math.max(...values) - // No point - if (min === max) return ''; - if (max - min < 10) { - // This is mostly useful for levels - min = Math.max(0, max - 10) - max = Math.max(max, min + 10) - } - // One bin per unique value, max 10 - const binsCount = Math.min(values.length, 10) - if (binsCount < 3) return '' - const bins = [] - const binsTotal = [] - for (let i = 0; i < binsCount; i++) { - bins.push(0) - binsTotal.push(0) - } - const binSize = (max - min) / bins.length - const binIndexOf = v => Math.min(bins.length - 1, Math.floor((v - min) / binSize)) - values.forEach(v => { - if (isNaN(v)) return - const index = binIndexOf(v) - bins[index]++ - binsTotal[index] += v - }) - if (bins.filter(b => b).length < 3) return '' - const maxBin = Math.max(...bins) - const lastValue = values[values.length - 1] - const activeBin = binIndexOf(lastValue) - return `

${title} : ${lastValue}${unit}

- ${bins.map((v, vi) => `${ - (!v && ' ') || (vi == activeBin && lastValue + unit) || (Math.round(binsTotal[vi] / v) + unit) - }`).join('')} -
- ` - } - - - runStats += makeHistogram('Total score', r => r.score, '') - runStats += makeHistogram('Catch rate', r => Math.round(r.score / r.coins_spawned * 100), '%') - runStats += makeHistogram('Bricks broken', r => r.bricks_broken, '') - runStats += makeHistogram('Bricks broken per minute', r => Math.round(r.bricks_broken / r.runTime * 1000 * 60), ' bpm') - runStats += makeHistogram('Hit rate', r => Math.round((1 - r.misses / r.puck_bounces) * 100), '%') - runStats += makeHistogram('Duration per level', r => Math.round(r.runTime / 1000 / r.levelsPlayed), 's') - runStats += makeHistogram('Level reached', r => r.levelsPlayed, '') - runStats += makeHistogram('Upgrades applied', r => r.upgrades_picked, '') - runStats += makeHistogram('Balls lost', r => r.balls_lost, '') - runStats += makeHistogram('Average combo', r => Math.round(r.coins_spawned / r.bricks_broken), '') - runStats += makeHistogram('Max combo', r => r.max_combo, '') - - if (runStats) { - runStats = `

Find below your run statistics compared to your ${runsHistory.length - 1} best runs.

` + runStats - } - } catch (e) { - console.warn(e) - } - return runStats -} - - -function resetRunStatistics() { - runStatistics = { - started: Date.now(), - levelsPlayed: 0, - runTime: 0, - coins_spawned: 0, - score: 0, - bricks_broken: 0, - misses: 0, - balls_lost: 0, - puck_bounces: 0, - upgrades_picked: 1, - max_combo: 1 - } -} - -function explodeBrick(index, ball, isExplosion) { - const color = bricks[index]; - if (!color) return; - - if (color === 'black') { - delete bricks[index]; - const x = brickCenterX(index), y = brickCenterY(index); - - sounds.explode(ball.x); - - const col = index % gridSize - const row = Math.floor(index / gridSize) - const size = 1 + perks.bigger_explosions; - // Break bricks around - for (let dx = -size; dx <= size; dx++) { - for (let dy = -size; dy <= size; dy++) { - const i = getRowColIndex(row + dy, col + dx); - if (bricks[i] && i !== -1) { - explodeBrick(i, ball, true) - } - } - } - // Blow nearby coins - coins.forEach((c) => { - const dx = c.x - x; - const dy = c.y - y; - const d2 = Math.max(brickWidth, Math.abs(dx) + Math.abs(dy)); - c.vx += (dx / d2) * 10 * size / c.weight; - c.vy += (dy / d2) * 10 * size / c.weight; - }); - lastexplosion = Date.now(); - - flashes.push({ - type: "ball", duration: 150, time: levelTime, size: brickWidth * 2, color: "white", x, y, - }); - spawnExplosion(7 * (1 + perks.bigger_explosions), x, y, 'white', 150, coinSize,); - ball.hitSinceBounce++; - runStatistics.bricks_broken++ - } else if (color) { - // Even if it bounces we don't want to count that as a miss - ball.hitSinceBounce++; - - if (perks.sturdy_bricks && perks.sturdy_bricks > Math.random() * 5) { - // Resist - sounds.coinBounce(ball.x, 1) - return - } - // Flashing is take care of by the tick loop - const x = brickCenterX(index), y = brickCenterY(index); - - bricks[index] = ""; - - - coins = coins.filter((c) => !c.destroyed); - let coinsToSpawn = combo - if (perks.sturdy_bricks) { - // +10% per level - coinsToSpawn += Math.ceil((10 + perks.sturdy_bricks) / 10 * coinsToSpawn) - } - - levelSpawnedCoins += coinsToSpawn; - runStatistics.coins_spawned += coinsToSpawn - runStatistics.bricks_broken++ - const maxCoins = MAX_COINS * (isSettingOn("basic") ? 0.5 : 1) - const spawnableCoins = Math.floor(maxCoins - coins.length) / 3 - const pointsPerCoin = Math.max(1, Math.ceil(coinsToSpawn / spawnableCoins)) - while (coinsToSpawn > 0) { - - const points = Math.min(pointsPerCoin, coinsToSpawn) - coinsToSpawn -= points - const coord = { - x: x + (Math.random() - 0.5) * (brickWidth - coinSize), - y: y + (Math.random() - 0.5) * (brickWidth - coinSize), - }; - - coins.push({ - points, - color: perks.metamorphosis ? color : 'gold', - ...coord, - previousx: coord.x, - previousy: coord.y, - // Use previous speed because the ball has already bounced - vx: ball.previousvx * (0.5 + Math.random()), - vy: ball.previousvy * (0.5 + Math.random()), - sx: 0, - sy: 0, - a: Math.random() * Math.PI * 2, - sa: Math.random() - 0.5, - weight: 0.8 + Math.random() * 0.2 - }); - } - - - combo += Math.max(0, perks.streak_shots + perks.compound_interest + perks.sides_are_lava + perks.top_is_lava + perks.picky_eater - - Math.round(Math.random() * perks.soft_reset)); - - if (!isExplosion) { - // color change - if ((perks.picky_eater || perks.pierce_color) && color !== ballsColor && color) { - if (perks.picky_eater) { - resetCombo(ball.x, ball.y); - } - - ballsColor = color; - } else { - sounds.comboIncreaseMaybe(ball.x, 1); - } - } - - flashes.push({ - type: "ball", duration: 40, time: levelTime, size: brickWidth, color: color, x, y, - }); - spawnExplosion(5 + combo, x, y, color, 100, coinSize / 2); - } - - if (!bricks[index]) { - ball.hitItem?.push({ - index, - color - }) - } -} - - -function max_levels() { - if (hadOverrides) return 1 - return 7 + perks.extra_levels; -} - -function render() { - if (running) needsRender = true - if (!needsRender) { - return - } - needsRender = false; - - const level = currentLevelInfo(); - const {width, height} = canvas; - if (!width || !height) return; - - let scoreInfo = ""; - for (let i = 0; i < perks.extra_life; i++) { - scoreInfo += "🖤 "; - } - - scoreInfo += 'L' + (currentLevel + 1) + '/' + max_levels() + ' '; - scoreInfo += '$' + score.toString(); - - scoreDisplay.innerText = scoreInfo; - // Clear - if (!isSettingOn("basic") && !level.color && level.svg && !level.black_puck) { - - // Without this the light trails everything - ctx.globalCompositeOperation = "source-over"; - ctx.globalAlpha = .4 - ctx.fillStyle = "#000"; - ctx.fillRect(0, 0, width, height); - - - ctx.globalCompositeOperation = "screen"; - ctx.globalAlpha = 0.6; - coins.forEach((coin) => { - if (!coin.destroyed) drawFuzzyBall(ctx, coin.color, coinSize * 2, coin.x, coin.y); - }); - balls.forEach((ball) => { - drawFuzzyBall(ctx, ballsColor, ballSize * 2, ball.x, ball.y); - }); - ctx.globalAlpha = 0.5; - bricks.forEach((color, index) => { - if (!color) return; - const x = brickCenterX(index), y = brickCenterY(index); - drawFuzzyBall(ctx, color == 'black' ? '#666' : color, brickWidth, x, y); - }); - ctx.globalAlpha = 1; - flashes.forEach((flash) => { - const {x, y, time, color, size, type, duration} = flash; - const elapsed = levelTime - time; - ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2); - if (type === "ball") { - drawFuzzyBall(ctx, color, size, x, y); - } - if (type === "particle") { - drawFuzzyBall(ctx, color, size * 3, x, y); - } - - }); - // Decides how brights the bg black parts can get - ctx.globalAlpha = .2; - ctx.globalCompositeOperation = "multiply"; - ctx.fillStyle = "black"; - ctx.fillRect(0, 0, width, height); - // Decides how dark the background black parts are when lit (1=black) - ctx.globalAlpha = .8; - ctx.globalCompositeOperation = "multiply"; - if (level.svg && background.width && background.complete) { - - if (backgroundCanvas.title !== level.name) { - backgroundCanvas.title = level.name - backgroundCanvas.width = canvas.width - backgroundCanvas.height = canvas.height - const bgctx = backgroundCanvas.getContext("2d") - bgctx.fillStyle = level.color || '#000' - bgctx.fillRect(0, 0, canvas.width, canvas.height) - bgctx.fillStyle = ctx.createPattern(background, "repeat"); - bgctx.fillRect(0, 0, width, height); - } - - ctx.drawImage(backgroundCanvas, 0, 0) - } else { - // Background not loaded yes - ctx.fillStyle = "#000"; - ctx.fillRect(0, 0, width, height); - } - } else { - - ctx.globalAlpha = 1 - ctx.globalCompositeOperation = "source-over"; - ctx.fillStyle = level.color || "#000"; - ctx.fillRect(0, 0, width, height); - - flashes.forEach((flash) => { - const {x, y, time, color, size, type, duration} = flash; - const elapsed = levelTime - time; - ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2); - if (type === "particle") { - drawBall(ctx, color, size, x, y); - } - }); - } - - ctx.globalAlpha = 1; - ctx.globalCompositeOperation = "source-over"; - const lastExplosionDelay = Date.now() - lastexplosion + 5; - const shaked = lastExplosionDelay < 200; - if (shaked) { - const amplitude = (perks.bigger_explosions + 1) * 50 / lastExplosionDelay - ctx.translate(Math.sin(Date.now()) * amplitude, Math.sin(Date.now() + 36) * amplitude); - } - - ctx.globalCompositeOperation = "source-over"; - renderAllBricks(ctx); - - ctx.globalCompositeOperation = "screen"; - flashes = flashes.filter((f) => levelTime - f.time < f.duration && !f.destroyed,); - - flashes.forEach((flash) => { - const {x, y, time, color, size, type, text, duration, points} = flash; - const elapsed = levelTime - time; - ctx.globalAlpha = Math.max(0, Math.min(1, 2 - (elapsed / duration) * 2)); - if (type === "text") { - ctx.globalCompositeOperation = "source-over"; - drawText(ctx, text, color, size, x, y - elapsed / 10); - } else if (type === "particle") { - ctx.globalCompositeOperation = "screen"; - drawBall(ctx, color, size, x, y); - drawFuzzyBall(ctx, color, size, x, y); - } - }); - - // Coins - ctx.globalAlpha = 1; - ctx.globalCompositeOperation = "source-over"; - coins.forEach((coin) => { - if (!coin.destroyed) drawCoin(ctx, coin.color, coinSize, coin.x, coin.y, level.color || 'black', coin.a); - }); - - // Black shadow around balls - if (coins.length > 10 && !isSettingOn('basic')) { - ctx.globalAlpha = Math.min(0.8, (coins.length - 10) / 50); - balls.forEach((ball) => { - drawBall(ctx, level.color || "#000", ballSize * 6, ball.x, ball.y); - }); - } - - - ctx.globalAlpha = 1 - ctx.globalCompositeOperation = "source-over"; - const puckColor = level.black_puck ? '#000' : '#FFF' - balls.forEach((ball) => { - drawBall(ctx, ballsColor, ballSize, ball.x, ball.y, puckColor); - // effect - if (isTelekinesisActive(ball)) { - ctx.strokeStyle = puckColor; - ctx.beginPath(); - ctx.bezierCurveTo(puck, gameZoneHeight, puck, ball.y, ball.x, ball.y); - ctx.stroke(); - } - }); - // The puck - ctx.globalAlpha = 1 - ctx.globalCompositeOperation = "source-over"; - if (perks.streak_shots && combo > baseCombo()) { - - drawPuck(ctx, 'red', puckWidth, puckHeight, -2) - } - drawPuck(ctx, puckColor, puckWidth, puckHeight) - - if (combo > 1) { - - ctx.globalCompositeOperation = "source-over"; - const comboText = "x " + combo - const comboTextWidth = comboText.length * puckHeight / 1.80 - const totalWidth = comboTextWidth + coinSize * 2 - const left = puck - totalWidth / 2 - if (totalWidth < puckWidth) { - - drawCoin(ctx, 'gold', coinSize, left + coinSize / 2, gameZoneHeight - puckHeight / 2, !level.black_puck ? '#FFF' : '#000', 0) - drawText(ctx, comboText, !level.black_puck ? '#000' : '#FFF', puckHeight, - left + coinSize * 1.5, gameZoneHeight - puckHeight / 2, true); - } else { - drawText(ctx, comboText, !level.black_puck ? '#000' : '#FFF', puckHeight, - puck, gameZoneHeight - puckHeight / 2, false); - - } - } - // Borders - const redSides = perks.sides_are_lava && combo > baseCombo() - ctx.fillStyle = redSides ? 'red' : puckColor; - ctx.globalCompositeOperation = "source-over"; - if (offsetXRoundedDown) { - // draw outside of gaming area to avoid capturing borders in recordings - ctx.fillRect(offsetX - 1, 0, 1, height); - ctx.fillRect(width - offsetX + 1, 0, 1, height); - } else if (redSides) { - ctx.fillRect(0, 0, 1, height); - ctx.fillRect(width - 1, 0, 1, height); - } - - if (perks.top_is_lava && combo > baseCombo()) - drawRedSquare(ctx, offsetXRoundedDown, 0, gameZoneWidthRoundedUp, 1); - const redBottom = perks.compound_interest && combo > baseCombo() - ctx.fillStyle = redBottom ? 'red' : puckColor; - if (isSettingOn("mobile-mode")) { - ctx.fillRect(offsetXRoundedDown, gameZoneHeight, gameZoneWidthRoundedUp, 1); - if (!running) { - drawText(ctx, "Press and hold here to play", puckColor, puckHeight, - canvas.width / 2, gameZoneHeight + (canvas.height - gameZoneHeight) / 2, - ); - } - } else if (redBottom) { - ctx.fillRect(offsetXRoundedDown, gameZoneHeight - 1, gameZoneWidthRoundedUp, 1); - } - - if (shaked) { - ctx.resetTransform(); - } - - recordOneFrame() -} - -let cachedBricksRender = document.createElement("canvas"); -let cachedBricksRenderKey = null; - -function renderAllBricks(destinationCtx) { - ctx.globalAlpha = 1; - - const level = currentLevelInfo(); - - const redBorderOnBricksWithWrongColor = combo > baseCombo() && perks.picky_eater - - const newKey = gameZoneWidth + "_" + bricks.join("_") + bombSVG.complete + '_' + redBorderOnBricksWithWrongColor + '_' + ballsColor; - if (newKey !== cachedBricksRenderKey) { - cachedBricksRenderKey = newKey; - - cachedBricksRender.width = gameZoneWidth; - cachedBricksRender.height = gameZoneWidth + 1; - const ctx = cachedBricksRender.getContext("2d"); - ctx.clearRect(0, 0, gameZoneWidth, gameZoneWidth); - ctx.resetTransform(); - ctx.translate(-offsetX, 0); - // Bricks - const puckColor = level.black_puck ? '#000' : '#FFF' - bricks.forEach((color, index) => { - const x = brickCenterX(index), y = brickCenterY(index); - - if (!color) return; - const borderColor = (ballsColor === color && puckColor) || (color !== 'black' && redBorderOnBricksWithWrongColor && 'red') || color - drawBrick(ctx, color, borderColor, x, y); - if (color === 'black') { - ctx.globalCompositeOperation = "source-over"; - drawIMG(ctx, bombSVG, brickWidth, x, y); - } - }); - } - - destinationCtx.drawImage(cachedBricksRender, offsetX, 0); -} - -let cachedGraphics = {}; - -function drawPuck(ctx, color, puckWidth, puckHeight, yoffset = 0) { - - const key = "puck" + color + "_" + puckWidth + '_' + puckHeight; - - if (!cachedGraphics[key]) { - const can = document.createElement("canvas"); - can.width = puckWidth; - can.height = puckHeight * 2; - const canctx = can.getContext("2d"); - canctx.fillStyle = color; - - - canctx.beginPath(); - canctx.moveTo(0, puckHeight * 2) - canctx.lineTo(0, puckHeight * 1.25) - canctx.bezierCurveTo(0, puckHeight * .75, puckWidth, puckHeight * .75, puckWidth, puckHeight * 1.25) - canctx.lineTo(puckWidth, puckHeight * 2) - canctx.fill(); - cachedGraphics[key] = can; - } - - ctx.drawImage(cachedGraphics[key], Math.round(puck - puckWidth / 2), gameZoneHeight - puckHeight * 2 + yoffset); - - -} - -function drawBall(ctx, color, width, x, y, borderColor = '') { - const key = "ball" + color + "_" + width + '_' + borderColor; - - const size = Math.round(width); - if (!cachedGraphics[key]) { - const can = document.createElement("canvas"); - can.width = size; - can.height = size; - - const canctx = can.getContext("2d"); - canctx.beginPath(); - canctx.arc(size / 2, size / 2, Math.round(size / 2) - 1, 0, 2 * Math.PI); - canctx.fillStyle = color; - canctx.fill(); - if (borderColor) { - canctx.lineWidth = 2 - canctx.strokeStyle = borderColor - canctx.stroke() - } - - cachedGraphics[key] = can; - } - ctx.drawImage(cachedGraphics[key], Math.round(x - size / 2), Math.round(y - size / 2),); -} - -function drawCoin(ctx, color, size, x, y, bg, rawAngle) { - - const angle = (Math.round(rawAngle / Math.PI * 2 * 16) % 16 + 16) % 16 - const key = "coin with halo" + "_" + color + "_" + size + '_' + bg + '_' + angle; - - if (!cachedGraphics[key]) { - const can = document.createElement("canvas"); - can.width = size; - can.height = size; - - const canctx = can.getContext("2d"); - - // coin - canctx.beginPath(); - canctx.arc(size / 2, size / 2, size / 2, 0, 2 * Math.PI); - canctx.fillStyle = color; - canctx.fill(); - - if (color === 'gold') { - - canctx.strokeStyle = bg; - canctx.stroke(); - - canctx.beginPath(); - canctx.arc(size / 2, size / 2, size / 2 * 0.6, 0, 2 * Math.PI); - canctx.fillStyle = 'rgba(255,255,255,0.5)'; - canctx.fill(); - - canctx.translate(size / 2, size / 2); - canctx.rotate(angle / 16); - canctx.translate(-size / 2, -size / 2); - - canctx.globalCompositeOperation = 'multiply' - drawText(canctx, '$', color, size - 2, size / 2, size / 2 + 1) - drawText(canctx, '$', color, size - 2, size / 2, size / 2 + 1) - } - cachedGraphics[key] = can; - } - ctx.drawImage(cachedGraphics[key], Math.round(x - size / 2), Math.round(y - size / 2)); -} - -function drawFuzzyBall(ctx, color, width, x, y) { - const key = "fuzzy-circle" + color + "_" + width; - if (!color) - debugger - const size = Math.round(width * 3); - if (!cachedGraphics[key]) { - const can = document.createElement("canvas"); - can.width = size; - can.height = size; - - const canctx = can.getContext("2d"); - const gradient = canctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, size / 2,); - gradient.addColorStop(0, color); - gradient.addColorStop(1, "transparent"); - canctx.fillStyle = gradient; - canctx.fillRect(0, 0, size, size); - cachedGraphics[key] = can; - } - ctx.drawImage(cachedGraphics[key], Math.round(x - size / 2), Math.round(y - size / 2),); -} - -function drawBrick(ctx, color, borderColor, x, y) { - const tlx = Math.ceil(x - brickWidth / 2); - const tly = Math.ceil(y - brickWidth / 2); - const brx = Math.ceil(x + brickWidth / 2) - 1; - const bry = Math.ceil(y + brickWidth / 2) - 1; - - const width = brx - tlx, height = bry - tly; - const key = "brick" + color + '_' + borderColor + "_" + width + "_" + height - - if (!cachedGraphics[key]) { - const can = document.createElement("canvas"); - can.width = width; - can.height = height; - const bord = 2; - const cornerRadius= 2 - const canctx = can.getContext("2d"); - - - canctx.fillStyle = color; - canctx.strokeStyle = borderColor; - canctx.lineJoin = "round"; - canctx.lineWidth = bord - roundRect(canctx,bord/2,bord/2, width-bord ,height-bord, cornerRadius) - canctx.fill(); - canctx.stroke(); - - cachedGraphics[key] = can; - } - ctx.drawImage(cachedGraphics[key], tlx, tly, width, height); - // It's not easy to have a 1px gap between bricks without antialiasing -} - -function roundRect( - ctx, - x, - y, - width, - height, - radius ){ - ctx.beginPath(); - ctx.moveTo(x + radius, y); - ctx.lineTo(x + width - radius, y); - ctx.quadraticCurveTo(x + width, y, x + width, y + radius); - ctx.lineTo(x + width, y + height - radius); - ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); - ctx.lineTo(x + radius, y + height); - ctx.quadraticCurveTo(x, y + height, x, y + height - radius); - ctx.lineTo(x, y + radius); - ctx.quadraticCurveTo(x, y, x + radius, y); - ctx.closePath(); - -} -function drawRedSquare(ctx, x, y, width, height) { - ctx.fillStyle = 'red' - ctx.fillRect(x, y, width, height); -} - - -function drawIMG(ctx, img, size, x, y) { - const key = "svg" + img + "_" + size + '_' + img.complete; - - if (!cachedGraphics[key]) { - const can = document.createElement("canvas"); - can.width = size; - can.height = size; - - const canctx = can.getContext("2d"); - - const ratio = size / Math.max(img.width, img.height); - const w = img.width * ratio; - const h = img.height * ratio; - canctx.drawImage(img, (size - w) / 2, (size - h) / 2, w, h); - - cachedGraphics[key] = can; - } - ctx.drawImage(cachedGraphics[key], Math.round(x - size / 2), Math.round(y - size / 2),); -} - -function drawText(ctx, text, color, fontSize, x, y, left = false) { - const key = "text" + text + "_" + color + "_" + fontSize + '_' + left; - - if (!cachedGraphics[key]) { - const can = document.createElement("canvas"); - can.width = fontSize * text.length; - can.height = fontSize; - const canctx = can.getContext("2d"); - canctx.fillStyle = color; - canctx.textAlign = left ? 'left' : "center" - canctx.textBaseline = "middle"; - canctx.font = fontSize + "px monospace"; - - canctx.fillText(text, left ? 0 : can.width / 2, can.height / 2, can.width); - - cachedGraphics[key] = can; - } - ctx.drawImage(cachedGraphics[key], left ? x : Math.round(x - cachedGraphics[key].width / 2), Math.round(y - cachedGraphics[key].height / 2),); -} - -function pixelsToPan(pan) { - return (pan - offsetX) / gameZoneWidth; -} - -let lastComboPlayed = NaN, shepard = 6; - -function playShepard(delta, pan, volume) { - const shepardMax = 11, factor = 1.05945594920268, baseNote = 392; - shepard += delta; - if (shepard > shepardMax) shepard = 0; - if (shepard < 0) shepard = shepardMax; - - const play = (note) => { - const freq = baseNote * Math.pow(factor, note); - const diff = Math.abs(note - shepardMax * 0.5); - const maxDistanceToIdeal = 1.5 * shepardMax; - const vol = Math.max(0, volume * (1 - diff / maxDistanceToIdeal)); - createSingleBounceSound(freq, pan, vol); - return freq.toFixed(2) + " at " + Math.floor(vol * 100) + "% diff " + diff; - }; - - play(1 + shepardMax + shepard); - play(shepard); - play(-1 - shepardMax + shepard); -} - -const sounds = { - wallBeep: (pan) => { - if (!isSettingOn("sound")) return; - createSingleBounceSound(800, pixelsToPan(pan)); - }, - - comboIncreaseMaybe: (x, volume) => { - if (!isSettingOn("sound")) return; - let delta = 0; - if (!isNaN(lastComboPlayed)) { - if (lastComboPlayed < combo) delta = 1; - if (lastComboPlayed > combo) delta = -1; - } - playShepard(delta, pixelsToPan(x), volume); - lastComboPlayed = combo; - }, - - comboDecrease() { - if (!isSettingOn("sound")) return; - playShepard(-1, 0.5, 0.5); - }, - coinBounce: (pan, volume) => { - if (!isSettingOn("sound")) return; - createSingleBounceSound(1200, pixelsToPan(pan), volume, 0.1, 'triangle'); - }, explode: (pan) => { - if (!isSettingOn("sound")) return; - createExplosionSound(pixelsToPan(pan)); - }, revive: () => { - if (!isSettingOn("sound")) return; - createRevivalSound(500); - }, coinCatch(pan) { - if (!isSettingOn("sound")) return; - createSingleBounceSound(900, pixelsToPan(pan), .8, 0.1, 'triangle') - } -}; - -// How to play the code on the leftconst context = new window.AudioContext(); -let audioContext, audioRecordingTrack; - -function getAudioContext() { - if (!audioContext) { - audioContext = new (window.AudioContext || window.webkitAudioContext)(); - audioRecordingTrack = audioContext.createMediaStreamDestination() - } - return audioContext; -} - -function createSingleBounceSound(baseFreq = 800, pan = 0.5, volume = 1, duration = 0.1, type = "sine") { - const context = getAudioContext(); - // Frequency for the metal "ping" - const baseFrequency = baseFreq; // Hz - - // Create an oscillator for the impact sound - const oscillator = context.createOscillator(); - oscillator.type = type; - oscillator.frequency.setValueAtTime(baseFrequency, context.currentTime); - - // Create a gain node to control the volume - const gainNode = context.createGain(); - oscillator.connect(gainNode); - - // Create a stereo panner node for left-right panning - const panner = context.createStereoPanner(); - panner.pan.setValueAtTime(pan * 2 - 1, context.currentTime); - gainNode.connect(panner); - panner.connect(context.destination); - panner.connect(audioRecordingTrack); - - // Set up the gain envelope to simulate the impact and quick decay - gainNode.gain.setValueAtTime(0.8 * volume, context.currentTime); // Initial impact - gainNode.gain.exponentialRampToValueAtTime(0.001, context.currentTime + duration,); // Quick decay - - // Start the oscillator - oscillator.start(context.currentTime); - - // Stop the oscillator after the decay - oscillator.stop(context.currentTime + duration); -} - -function createRevivalSound(baseFreq = 440) { - const context = getAudioContext(); - - // Create multiple oscillators for a richer sound - const oscillators = [context.createOscillator(), context.createOscillator(), context.createOscillator(),]; - - // Set the type and frequency for each oscillator - oscillators.forEach((osc, index) => { - osc.type = "sine"; - osc.frequency.setValueAtTime(baseFreq + index * 2, context.currentTime); // Slight detuning - }); - - // Create a gain node to control the volume - const gainNode = context.createGain(); - - // Connect all oscillators to the gain node - oscillators.forEach((osc) => osc.connect(gainNode)); - - // Create a stereo panner node for left-right panning - const panner = context.createStereoPanner(); - panner.pan.setValueAtTime(0, context.currentTime); // Center panning - gainNode.connect(panner); - panner.connect(context.destination); - panner.connect(audioRecordingTrack); - - // Set up the gain envelope to simulate a smooth attack and decay - gainNode.gain.setValueAtTime(0, context.currentTime); // Start at zero - gainNode.gain.linearRampToValueAtTime(0.5, context.currentTime + 0.5); // Ramp up to full volume - gainNode.gain.exponentialRampToValueAtTime(0.001, context.currentTime + 2); // Slow decay - - // Start all oscillators - oscillators.forEach((osc) => osc.start(context.currentTime)); - - // Stop all oscillators after the decay - oscillators.forEach((osc) => osc.stop(context.currentTime + 2)); -} - -let noiseBuffer; - -function createExplosionSound(pan = 0.5) { - const context = getAudioContext(); - // Create an audio buffer - if (!noiseBuffer) { - const bufferSize = context.sampleRate * 2; // 2 seconds - noiseBuffer = context.createBuffer(1, bufferSize, context.sampleRate); - const output = noiseBuffer.getChannelData(0); - - // Fill the buffer with random noise - for (let i = 0; i < bufferSize; i++) { - output[i] = Math.random() * 2 - 1; - } - } - - // Create a noise source - const noiseSource = context.createBufferSource(); - noiseSource.buffer = noiseBuffer; - - // Create a gain node to control the volume - const gainNode = context.createGain(); - noiseSource.connect(gainNode); - - // Create a filter to shape the explosion sound - const filter = context.createBiquadFilter(); - filter.type = "lowpass"; - filter.frequency.setValueAtTime(1000, context.currentTime); // Set the initial frequency - gainNode.connect(filter); - - // Create a stereo panner node for left-right panning - const panner = context.createStereoPanner(); - panner.pan.setValueAtTime(pan * 2 - 1, context.currentTime); // pan 0 to 1 maps to -1 to 1 - - // Connect filter to panner and then to the destination (speakers) - filter.connect(panner); - panner.connect(context.destination); - panner.connect(audioRecordingTrack); - - // Ramp down the gain to simulate the explosion's fade-out - gainNode.gain.setValueAtTime(1, context.currentTime); - gainNode.gain.exponentialRampToValueAtTime(0.01, context.currentTime + 1); - - // Lower the filter frequency over time to create the "explosive" effect - filter.frequency.exponentialRampToValueAtTime(60, context.currentTime + 1); - - // Start the noise source - noiseSource.start(context.currentTime); - - // Stop the noise source after the sound has played - noiseSource.stop(context.currentTime + 1); -} - -let levelTime = 0; - -setInterval(() => { - document.body.className = (running ? " running " : " paused ") + (currentLevelInfo()?.black_puck ? ' black_puck ' : ' '); -}, 100); - -window.addEventListener("visibilitychange", () => { - if (document.hidden) { - pause(true) - } -}); - -const scoreDisplay = document.getElementById("score"); -let alertsOpen = 0, closeModal = null - -function asyncAlert({ - title, - text, - actions = [{text: "OK", value: "ok", help: ""}], - allowClose = true, - textAfterButtons = '' - }) { - alertsOpen++ - return new Promise((resolve) => { - const popupWrap = document.createElement("div"); - document.body.appendChild(popupWrap); - popupWrap.className = "popup"; - - function closeWithResult(value) { - resolve(value); - // Doing this async lets the menu scroll persist if it's shown a second time - setTimeout(() => { - document.body.removeChild(popupWrap); - }); - } - - if (allowClose) { - const closeButton = document.createElement("button"); - closeButton.title = "close" - closeButton.className = "close-modale" - closeButton.addEventListener('click', (e) => { - e.preventDefault() - closeWithResult(null) - }) - closeModal = () => { - closeWithResult(null) - } - popupWrap.appendChild(closeButton) - } - - const popup = document.createElement("div"); - - if (title) { - const p = document.createElement("h2"); - p.innerHTML = title; - popup.appendChild(p); - } - - if (text) { - const p = document.createElement("div"); - p.innerHTML = text; - popup.appendChild(p); - } - - actions.filter(i => i).forEach(({text, value, help, checked = 0, max = 0, disabled, icon = ''}) => { - const button = document.createElement("button"); - let checkMark = '' - if (max) { - checkMark += '' - for (let i = 0; i < max; i++) { - checkMark += ''; - } - checkMark += '' - } - button.innerHTML = ` -${icon} -${checkMark} -
- ${text} - ${help || ''} -
`; - - - if (disabled) { - button.setAttribute("disabled", "disabled"); - } else { - button.addEventListener("click", (e) => { - e.preventDefault(); - closeWithResult(value) - }); - } - popup.appendChild(button); - }); - - if (textAfterButtons) { - const p = document.createElement("div"); - p.className = 'textAfterButtons' - p.innerHTML = textAfterButtons; - popup.appendChild(p); - } - - - popupWrap.appendChild(popup); - popup.querySelector('button:not([disabled])')?.focus() - }).finally(() => { - closeModal = null - alertsOpen-- - }) -} - -// Settings -let cachedSettings = {}; - -function isSettingOn(key) { - if (typeof cachedSettings[key] == "undefined") { - try { - cachedSettings[key] = JSON.parse(localStorage.getItem("breakout-settings-enable-" + key),); - } catch (e) { - console.warn(e); - } - } - return cachedSettings[key] ?? options[key]?.default ?? false; -} - -function toggleSetting(key) { - cachedSettings[key] = !isSettingOn(key); - try { - const lskey = "breakout-settings-enable-" + key; - localStorage.setItem(lskey, JSON.stringify(cachedSettings[key])); - } catch (e) { - console.warn(e); - } - if (options[key].afterChange) options[key].afterChange(); -} - - -scoreDisplay.addEventListener("click", async (e) => { - e.preventDefault(); - openScorePanel() -}); - -async function openScorePanel() { - pause(true) - const cb = await asyncAlert({ - title: ` ${score} points at level ${currentLevel + 1} / ${max_levels()}`, - text: ` -

Upgrades picked so far :

-

${pickedUpgradesHTMl()}

- `, allowClose: true, actions: [ - { - text: 'Resume', - help: "Return to your run", - }, - { - text: "Restart", help: "Start a brand new run.", - value: () => { - restart(); - return true; - }, - }], - }); - if (cb) { - await cb() - } -} - -document.getElementById("menu").addEventListener("click", (e) => { - e.preventDefault(); - openSettingsPanel(); -}); - - -const options = { - sound: { - default: true, name: `Game sounds`, help: `Can slow down some phones.`, - disabled: () => false - }, "mobile-mode": { - default: window.innerHeight > window.innerWidth, - name: `Mobile mode`, - help: `Leaves space for your thumb.`, - afterChange() { - fitSize(); - }, - disabled: () => false - }, - basic: { - default: false, name: `Basic graphics`, help: `Better performance on older devices.`, - disabled: () => false - }, - pointerLock: { - default: false, name: `Mouse pointer lock`, - help: `Locks and hides the mouse cursor.`, - disabled: () => !canvas.requestPointerLock - }, - "easy": { - default: false, name: `Kids mode`, help: `Starting perk always "slower ball".`, restart: true, - disabled: () => false - }, - // Could not get the sharing to work without loading androidx and all the modern android things so for now i'll just disable sharing in the android app - "record": { - default: false, name: `Record gameplay videos`, help: `Get a video of each level.`, - disabled() { - return window.location.search.includes('isInWebView=true') - } - } -}; - -async function openSettingsPanel() { - - pause(true) - - const optionsList = []; - for (const key in options) { - if (options[key]) - optionsList.push({ - disabled: options[key].disabled(), - checked: isSettingOn(key) ? 1 : 0, - max: 1, text: options[key].name, help: options[key].help, value: () => { - toggleSetting(key) - if (options[key].restart) { - restart() - } else { - openSettingsPanel(); - } - }, - }); - } - - const cb = await asyncAlert({ - title: "Breakout 71", text: ` - `, allowClose: true, actions: [ - { - text: 'Resume', - help: "Return to your run", - async value() { - - } - }, - { - text: 'Unlocks and help', - help: "See perks and levels you unlocked", - async value() { - const ts = getTotalScore() - const actions = [...upgrades - .sort((a, b) => a.threshold - b.threshold) - .map(({ - name, - max, - help, id, - threshold, icon, tryout, fullHelp - }) => ({ - text: name, - help: ts >= threshold ? fullHelp || help : `Unlocks at total score ${threshold}.`, - disabled: ts < threshold, - value: tryout || {perks: {[id]: max}}, - icon - }) - ) - - , - ...allLevels - .sort((a, b) => a.threshold - b.threshold) - .map((l, li) => { - const avaliable = ts >= l.threshold - return ({ - text: l.name, - help: avaliable ? `A ${l.size}x${l.size} level with ${l.bricks.filter(i => i).length} bricks` : `Unlocks at total score ${l.threshold}.`, - disabled: !avaliable, - value: {level: l.name}, - icon: levelIconHTML(l) - }) - }) - ] - - const tryOn = await asyncAlert({ - title: `You unlocked ${Math.round(actions.filter(a => !a.disabled).length / actions.length * 100)}% of the game.`, - text: ` -

Your total score is ${ts}. Below are all the upgrades and levels the games has to offer. They greyed out ones can be unlocked by increasing your total score.

- `, - textAfterButtons: `

-The total score increases every time you score in game. -Your high score is ${highScore}. -Click an item above to start a test run with it. -

`, - actions, - allowClose: true, - }) - if (tryOn) { - if (!currentLevel || await asyncAlert({ - title: 'Restart run to try this item?', - text: 'You\'re about to start a new test run with just the selected unlocked item, is that really what you wanted ? ', - actions: [{ - value: true, - text: 'Restart game to test item' - }, { - value: false, - text: 'Cancel' - }] - })) - nextRunOverrides = tryOn - restart() - } - } - }, - - ...optionsList, - - (document.fullscreenEnabled || document.webkitFullscreenEnabled) && - (document.fullscreenElement !== null ? { - text: "Exit Fullscreen", - help: "Might not work on some machines", - value() { - toggleFullScreen() - } - } : - { - text: "Fullscreen", - help: "Might not work on some machines", - value() { - toggleFullScreen() - } - }), - { - text: 'Reset Game', - help: "Erase high score and statistics", - async value() { - if (await asyncAlert({ - title: 'Reset', - actions: [ - { - text: 'Yes', - value: true - }, - { - text: 'No', - value: false - } - ], - allowClose: true, - })) { - localStorage.clear() - window.location.reload() - } - - } - } - ], - textAfterButtons: ` -

- Made in France by Renan LE CARO. - Privacy Policy - F-Droid - Google Play - itch.io - Gitlab - Web version - HackerNews - v.${window.appVersion} -

- ` - }) - if (cb) { - cb() - } -} - -function distance2(a, b) { - return Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2) -} - -function distanceBetween(a, b) { - return Math.sqrt(distance2(a, b)) -} - -function rainbowColor() { - return `hsl(${Math.round((levelTime / 4)) * 2 % 360},100%,70%)` -} - -function repulse(a, b, power, impactsBToo) { - - const distance = distanceBetween(a, b) - // Ensure we don't get soft locked - const max = gameZoneWidth / 2 - if (distance > max) return - // Unit vector - const dx = (a.x - b.x) / distance - const dy = (a.y - b.y) / distance - const fact = -power * (max - distance) / (max * 1.2) / 3 * Math.min(500, levelTime) / 500 - if (impactsBToo) { - b.vx += dx * fact - b.vy += dy * fact - } - a.vx -= dx * fact - a.vy -= dy * fact - - const speed = 10 - const rand = 2 - flashes.push({ - type: "particle", - duration: 100, - time: levelTime, - size: coinSize / 2, - color: rainbowColor(), - ethereal: true, - x: a.x, - y: a.y, - vx: -dx * speed + a.vx + (Math.random() - 0.5) * rand, - vy: -dy * speed + a.vy + (Math.random() - 0.5) * rand, - }) - if (impactsBToo) { - - flashes.push({ - type: "particle", - duration: 100, - time: levelTime, - size: coinSize / 2, - color: rainbowColor(), - ethereal: true, - x: b.x, - y: b.y, - vx: dx * speed + b.vx + (Math.random() - 0.5) * rand, - vy: dy * speed + b.vy + (Math.random() - 0.5) * rand, - }) - } - -} - -function attract(a, b, power) { - - const distance = distanceBetween(a, b) - // Ensure we don't get soft locked - const min = gameZoneWidth * .5 - if (distance < min) return - // Unit vector - const dx = (a.x - b.x) / distance - const dy = (a.y - b.y) / distance - - const fact = power * (distance - min) / min * Math.min(500, levelTime) / 500 - b.vx += dx * fact - b.vy += dy * fact - a.vx -= dx * fact - a.vy -= dy * fact - - const speed = 10 - const rand = 2 - flashes.push({ - type: "particle", - duration: 100, - time: levelTime, - size: coinSize / 2, - color: rainbowColor(), - ethereal: true, - x: a.x, - y: a.y, - vx: dx * speed + a.vx + (Math.random() - 0.5) * rand, - vy: dy * speed + a.vy + (Math.random() - 0.5) * rand, - }) - flashes.push({ - type: "particle", - duration: 100, - time: levelTime, - size: coinSize / 2, - color: rainbowColor(), - ethereal: true, - x: b.x, - y: b.y, - vx: -dx * speed + b.vx + (Math.random() - 0.5) * rand, - vy: -dy * speed + b.vy + (Math.random() - 0.5) * rand, - }) -} - -let levelIconHTMLCanvas = document.createElement('canvas') -const levelIconHTMLCanvasCtx = levelIconHTMLCanvas.getContext("2d", {antialias: false, alpha: true}) - -function levelIconHTML(level, title) { - const size = 40 - const c = levelIconHTMLCanvas - const ctx = levelIconHTMLCanvasCtx - c.width = size - c.height = size - if (level.color) { - ctx.fillStyle = level.color - ctx.fillRect(0, 0, size, size) - } else { - ctx.clearRect(0, 0, size, size) - } - const pxSize = size / level.size - for (let x = 0; x < level.size; x++) { - for (let y = 0; y < level.size; y++) { - const c = level.bricks[y * level.size + x] - if (c) { - ctx.fillStyle = c - ctx.fillRect(Math.floor(pxSize * x), Math.floor(pxSize * y), Math.ceil(pxSize), Math.ceil(pxSize)) - } - } - } - // I don't think many blind people will benefit for this but it's nice to have something to put in "alt" - return `Icon for ${level.name}` -} - -upgrades.forEach(u => u.icon = levelIconHTML(perkIconsLevels[u.id], u.name)) - -let mediaRecorder, captureStream, recordCanvas, recordCanvasCtx - - -function recordOneFrame() { - if (!isSettingOn('record')) { - return - } - if (!running) return; - if (!captureStream) return; - drawMainCanvasOnSmallCanvas() - if (captureStream.requestFrame) { - captureStream.requestFrame() - } else { - captureStream.getVideoTracks()[0].requestFrame() - } -} - - -function drawMainCanvasOnSmallCanvas() { - if (!recordCanvasCtx) return - recordCanvasCtx.drawImage(canvas, offsetXRoundedDown, 0, gameZoneWidthRoundedUp, gameZoneHeight, 0, 0, recordCanvas.width, recordCanvas.height) - recordCanvasCtx.fillStyle = currentLevelInfo()?.black_puck ? '#000' : '#FFF' - recordCanvasCtx.textBaseline = "top"; - recordCanvasCtx.font = "12px monospace"; - recordCanvasCtx.textAlign = "right"; - recordCanvasCtx.fillText(score.toString(), recordCanvas.width - 12, 12) - - - recordCanvasCtx.textAlign = "left"; - recordCanvasCtx.fillText('Level ' + (currentLevel + 1) + '/' + max_levels(), 12, 12) -} - -function startRecordingGame() { - if (!isSettingOn('record')) { - return - } - if (!recordCanvas) { - // Smaller canvas with less details - recordCanvas = document.createElement("canvas") - recordCanvasCtx = recordCanvas.getContext("2d", {antialias: false, alpha: false}) - - gifCanvas = document.createElement("canvas") - gifCtx = gifCanvas.getContext("2d", {antialias: false, alpha: false}) - - - captureStream = recordCanvas.captureStream(0); - - if (isSettingOn('sound') && getAudioContext() && audioRecordingTrack) { - - captureStream.addTrack(audioRecordingTrack.stream.getAudioTracks()[0]) - // captureStream.addTrack(audioRecordingTrack.stream.getAudioTracks()[1]) - } - } - - recordCanvas.width = gameZoneWidthRoundedUp - recordCanvas.height = gameZoneHeight - gifCanvas.width = 400 - gifCanvas.height = Math.floor(gameZoneHeight * (400 / gameZoneWidthRoundedUp)) - - - // drawMainCanvasOnSmallCanvas() - const recordedChunks = []; - - - const instance = new MediaRecorder(captureStream, {videoBitsPerSecond: 3500000}); - mediaRecorder = instance - instance.start(); - mediaRecorder.pause() - instance.ondataavailable = function (event) { - recordedChunks.push(event.data); - } - - instance.onstop = async function () { - let targetDiv; - let blob = new Blob(recordedChunks, {type: "video/webm"}); - if (blob.size < 200000) return // under 0.2MB, probably bugged out or pointlessly short - - while (!(targetDiv = document.getElementById("level-recording-container"))) { - await new Promise(r => setTimeout(r, 200)) - } - const video = document.createElement("video") - video.autoplay = true - video.controls = false - video.disablepictureinpicture = true - video.disableremoteplayback = true - video.width = recordCanvas.width - video.height = recordCanvas.height - // targetDiv.style.width = recordCanvas.width + 'px' - // targetDiv.style.height = recordCanvas.height + 'px' - video.loop = true - video.muted = true - video.playsinline = true - video.src = URL.createObjectURL(blob); - - const a = document.createElement("a") - a.download = captureFileName('webm') - a.target = "_blank" - a.href = video.src - a.textContent = `Download video (${(blob.size / 1000000).toFixed(2)}MB)` - targetDiv.appendChild(video) - targetDiv.appendChild(a) - - } - - -} - -function pauseRecording() { - if (!isSettingOn('record')) { - return - } - if (mediaRecorder?.state === 'recording') { - mediaRecorder?.pause() - } -} - -function resumeRecording() { - if (!isSettingOn('record')) { - return - } - if (mediaRecorder?.state === 'paused') { - mediaRecorder.resume() - } - -} - -function stopRecording() { - if (!isSettingOn('record')) { - return - } - if (!mediaRecorder) return; - mediaRecorder?.stop() - mediaRecorder = null -} - -function captureFileName(ext) { - return "breakout-71-capture-" + new Date().toISOString().replace(/[^0-9\-]+/gi, '-') + '.' + ext -} - - -function findLast(arr, predicate) { - let i = arr.length - while (--i) - if (predicate(arr[i], i, arr)) { - return arr[i] - } - -} - -function toggleFullScreen() { - try { - if (document.fullscreenElement !== null) { - if (document.exitFullscreen) { - document.exitFullscreen(); - } else if (document.webkitCancelFullScreen) { - document.webkitCancelFullScreen(); - } - } else { - const docel = document.documentElement - if (docel.requestFullscreen) { - docel.requestFullscreen(); - } else if (docel.webkitRequestFullscreen) { - docel.webkitRequestFullscreen(); - } - } - - } catch (e) { - console.warn(e) - } -} - -const pressed = { - ArrowLeft: 0, - ArrowRight: 0, - Shift: 0 -} - -function setKeyPressed(key, on) { - pressed[key] = on - keyboardPuckSpeed = (pressed.ArrowRight - pressed.ArrowLeft) * (1 + pressed.Shift * 2) * gameZoneWidth / 50 - -} - -document.addEventListener('keydown', e => { - if (e.key.toLowerCase() === 'f') { - toggleFullScreen() - } else if (e.key in pressed) { - setKeyPressed(e.key, 1) - } - if (e.key === ' ' && !alertsOpen) { - if (running) { - pause() - } else { - play() - } - } else { - return - } - e.preventDefault() -}) - -document.addEventListener('keyup', e => { - if (e.key in pressed) { - setKeyPressed(e.key, 0) - } else if (e.key === 'ArrowDown' && document.querySelector('button:focus')?.nextElementSibling.tagName === 'BUTTON') { - document.querySelector('button:focus')?.nextElementSibling?.focus() - } else if (e.key === 'ArrowUp' && document.querySelector('button:focus')?.previousElementSibling.tagName === 'BUTTON') { - document.querySelector('button:focus')?.previousElementSibling?.focus() - } else if (e.key === 'Escape' && closeModal) { - closeModal() - } else if (e.key === 'Escape' && running) { - pause() - } else if (e.key.toLowerCase() === 'm' && !alertsOpen) { - openSettingsPanel() - } else if (e.key.toLowerCase() === 's' && !alertsOpen) { - openScorePanel() - } else { - return - } - e.preventDefault() -}) - - -fitSize() -restart() -tick(); \ No newline at end of file diff --git a/app/src/main/assets/icon.svg b/app/src/main/assets/icon.svg deleted file mode 100644 index f411508..0000000 --- a/app/src/main/assets/icon.svg +++ /dev/null @@ -1,146 +0,0 @@ - - - - diff --git a/app/src/main/assets/index.html b/app/src/main/assets/index.html index 1bdf2f3..24742a2 100644 --- a/app/src/main/assets/index.html +++ b/app/src/main/assets/index.html @@ -1,26 +1 @@ - - - - - - - Breakout 71 - - - - - - - - - - - - - - - - +Breakout 71 \ No newline at end of file diff --git a/app/src/main/assets/levels.js b/app/src/main/assets/levels.js deleted file mode 100644 index 4debfb1..0000000 --- a/app/src/main/assets/levels.js +++ /dev/null @@ -1,932 +0,0 @@ -let allLevels=[ - { - "name": "71 mini", - "size": 5, - "bricks": "bbb____bt__btt__b_t___ttt", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Butterfly", - "bricks": "_________bb_t_t_bbbbb_t_bbbbbbbtbbbb_bbbtbbb____btb____bbbtbbb__bb_t_bb___________________", - "size": 9, - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Castle", - "size": 7, - "bricks": "s_s_s_ssssssssssBBBssssBBBssttbbbttttbbbtttbtbtbt", - "svg": "", - "focus": false - }, - { - "name": "Eyes", - "size": 9, - "bricks": "ttttttt__tWWWWWWW_tWrrWttW_tWWWWWWW_ttttttt_____t______ttttt____ttttt_____t_t", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Creeper", - "size": 10, - "bricks": "___________ccGGccGG__cGccGcGc__GBBccBBc__cBBGcBBc__GccBBGGc__ccBBBBcG__GGBBBBcG__cGBccBGc___________", - "svg": "", - "focus": false - }, - { - "name": "Stairs", - "size": 8, - "bricks": "tt______tt______bbtt____bbtt____vvbbtt__vvbbtt__ppvvbbttppvvbbtt", - "svg": "", - "focus": false - }, - { - "name": "Dots", - "size": 9, - "bricks": "b_t_a_c_C__________b_t_a_c__________v_b_t_a_c__________v_b_t_a__________p_v_b_t_a", - "svg": "", - "focus": false - }, - { - "name": "Lines", - "size": 9, - "bricks": "aaaaaaaa___________tttttttt_________aaaaaaaa___________tttttttt_________aaaaaaaa", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Heart", - "size": 15, - "bricks": "__________________RRR___RRR_____RSSSR_RSSSR___RSWWSSRSSSSSR__RSWSSSSSSSSSR__RSSSSSSSSSSSR__RSWSSSSSSSSSR___RSSSSSSSSSR_____RSSSSSSSR_______RSSSSSR_________RSSSR___________RSR_____________R____________________________________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Swiss", - "size": 7, - "bricks": "________RRRRR__RRWRR__RWWWR__RRWRR__RRRRR", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Germany", - "size": 6, - "bricks": "_______gggg__rrrr__yyyy", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "France", - "size": 8, - "bricks": "_________ttWWrr__ttWWrr__ttWWrr__ttWWrr__ttWWrr________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Smiley", - "size": 8, - "bricks": "_________yy__yy__yy__yy__________________yyyyyy___yyyy__________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Labyrinthe", - "size": 11, - "bricks": "_______tttS_Stttt_S________t___S__Stt_ttttt____t_____S__ttt_S_S____t___t_tttt_t_S_t____tSt_t_t_Sttt___t_t_____Sttt_tttttS", - "svg": "", - "focus": false - }, - { - "name": "Temple", - "size": 11, - "bricks": "_______________WWW______WWWWWWW___WWWWWWWWW___t_t_t_t____b_b_b_b____v_v_v_v____p_p_p_p____P_P_P_P____WWWWWWW___WWWWWWWWW_", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Pacman", - "size": 12, - "bricks": "____yyyy______yyyyyyyy___yyyyByyyyy__yyyyyyyyy__yyyyyyyy____yyyyyy______yyyyyy___S_Syyyyyyyy_____yyyyyyyyy___yyyyyyyyyy___yyyyyyyy______yyyy", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Ship", - "size": 11, - "bricks": "____sWW________sWWW_______sWWW_______s___OOOOOOOOOOOOOO_OBOBOBOBOO__OOOOOOOO_bbbbbbbbgbbbbgbbbbggbbbggbbbbbbbb", - "svg": "", - "focus": false - }, - { - "name": "We come in peace", - "size": 13, - "bricks": "________________a_____a_______a___a_______aaaaaaa_____aaBaaaBaa___aaaaaaaaaaa__aaaaaaaaaaa__a_aaaaaaa_a__a_a_____a_a_____aa_aa_____________________________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Space mushroom", - "size": 10, - "bricks": "______________WW_______WWWW_____WWWWWW___WWBWWBWW__WWWWWWWW____W__W_____W_WW_W___W_W__W_W", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Wololo", - "size": 9, - "bricks": "____WW_OOW___WW__OWW__W___OWWWbbbW_WWW_WbW_WOW__WWb__OW__bbb__O___W_W__O___W_W__O", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Small heart", - "size": 15, - "bricks": "________________________________RRRR___RRRR___RrWWrR_RWWrrR__RWWrrrRWWrrrR__RrrrrrrrrrrrR__RrrrrrrrrrrrR___RrrrrrrrrrR_____RrrrrrrrR_______RrrrrrR_________RrrrR___________RrR_____________R______________________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Eye", - "size": 9, - "bricks": "____________ggg_____gWWWg___gWbbbWg_gWWbBbWWg_gWbbbWg___gWWWg_____ggg____________", - "svg": "", - "color": "#5da3ea", - "focus": false - }, - { - "name": "Enderman", - "size": 10, - "bricks": "___________gggggggg__gggggggg__gggggggg__gggggggg__vvvggvvv__gggggggg__gggggggg__gggggggg_____________________", - "svg": "", - "color": "#ffffff", - "black_puck": true, - "focus": false - }, - { - "name": "Mushroom", - "size": 16, - "bricks": "_____________________rrrrWW________WWrrrrWWWW_____WWrrrrrrWWWW____WrrWWWWrrWWW___rrrWWWWWWrrrrr__rrrWWWWWWrrWWr__WrrWWWWWWrWWWW__WWrrWWWWrrWWWW__WWrrrrrrrrrWWr__WrrWWWWWWWWrrr_____WWBWWBWW_______WWWBWWBWWW______WWWWWWWWWW_______WWWWWWWW____________________", - "svg": "", - "color": "", - "black_puck": false, - "focus": false - }, - { - "name": "Tulip", - "size": 11, - "bricks": "______________R_R_R______RRRRR______RRRRR______RRRRR_______RRR_________k________k_k_k______k_k_k_______kkk_________k________________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Chain", - "size": 7, - "bricks": "yyy____yBy____yyyyy____yBy____yyyyy____yBy____yyy", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Marion", - "size": 9, - "bricks": "rr_____rr_rr___rr__rrr_rrr__rrrrrrr__rr_r_rr__rr___rr__rr___rr__rr___rr_rrr___rrr", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Renan", - "size": 9, - "bricks": "yyyyyyy___yyyyyyy__yy___yy__yy___yy__yyyyyy___yy_yy____yy__yy___yy___yy_yyy___yyy", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Violet Pairs", - "size": 8, - "bricks": "b_b_b_b_b_b_b_b__________t_t_t_t_t_t_t_t________b_b_b_b_b_b_b_b", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Red Cups", - "size": 11, - "bricks": "___________rBr_rBr_rBrrrr_rrr_rrr___________r_rBr_rBr_rr_rrr_rrr_r___________rBr_rBr_rBrrrr_rrr_rrr__________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Cactus", - "size": 10, - "bricks": "____G______rG_Gk______G_Gk______kkkk_r_____kkk_G______GkGk_____rGkk_______Gk________kk________kk_____", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Sunny Face", - "size": 11, - "bricks": "____yyy______yyyyyyy___yyyyyyyyy__yyyyyyyyy_yyyWWyWWyyyyyyyyyyyyyyyyyyyyyyyyy_yyWWWWWyy__yyyWWWyyy___yyyyyyy______yyy", - "svg": "", - "color": "#5da3ea", - "focus": false - }, - { - "name": "Mountain", - "size": 9, - "bricks": "_______________W_______WWW______GGWW__W_GGGGG_kkkGGGGG_kkkkGGGGkkkkkGGGGkkkkkkGGG_________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Dollar", - "size": 17, - "bricks": "________________________G_G______________G_G____________GGGGGGG_________GGGGGGGGG_______GG__G_G__GG______GG__G_G__GG______GG__G_G___________GGGGGGGG__________GGGGGGGG___________G_G__GG______GG__G_G__GG______GG__G_G__GG_______GGGGGGGGG_________GGGGGGG____________G_G______________G_G________________________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Waves", - "size": 8, - "bricks": "___bbb____bbb____bbttbbbbbttbbbbttttaatttttaattttaaaaaaa", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Box", - "size": 8, - "bricks": "yyyyyyyyy______yy_bbbb_yy_b__b_yy_b__b_yy_bbbb_yy______yyyyyyyyy", - "svg": "", - "color": "", - "squared": false, - "focus": false - }, - { - "name": "Rose", - "size": 9, - "bricks": "__SS______SSSS_____SSSS_____SSSS______SS_k______k_kk_____kk_k______kk________k", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Time", - "size": 9, - "bricks": "__________WWWWWWW___WWWWW_____yyy_______y________y_______WyW_____WyyyW___yyyyyyy__________", - "svg": "", - "color": "", - "squared": false, - "focus": false - }, - { - "name": "Watermelon", - "size": 8, - "bricks": "_____Sk_____SSBk___SBSSk__SSSSSk_SSBSSk_SBSSSSk_kSSSkk___kkk____", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Worms", - "size": 13, - "bricks": "___sssss_______sssssss______WWsWWsss_____WBsBWsss_____WBsBWsss_____WWsWWsss_____sssssss_______ssssss_____WWWWWWss_______WssWs__s_____ssss__sss___sssssssssss__sssssssss_ss", - "svg": " ", - "color": "", - "squared": false, - "focus": false - }, - { - "name": "Ocean Sunrise", - "size": 8, - "bricks": "SSSSSSSSSSSyySSSSSyyyySSSyyWWyySbttaattbbbttttbbbbbttbbbbbbbbbbb", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Crosses", - "size": 13, - "bricks": "b___b___b___b__v___v___v___vvv_vvv_vvv___v___v___v__p___p___p___ppp_ppp_ppp_ppp___p___p___p__P___P___P___PPP_PPP_PPP___P___P___P__p___p___p___ppp_ppp_ppp_ppp___p___p___p", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Negative space", - "size": 9, - "bricks": "tttttttttt_t_t_t_t_________b_b_b_b_bbbbbbbbbb_b_b_b_b___________t_t_t_t_ttttttttt_________", - "svg": "", - "focus": false - }, - { - "name": "UK", - "size": 11, - "bricks": "brbbWrWbbrbbbrbWrWbrbbbbbrWrWrbbbWWWWWrWWWWWrrrrrrrrrrrWWWWWrWWWWWbbbrWrWrbbbbbrbWrWbrbbbrbbWrWbbrb__________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Greece", - "size": 11, - "bricks": "ttWttttttttttWttWWWWWWWWWWWttttttttWttWWWWWWttWttttttttWWWWWWWWWWWtttttttttttWWWWWWWWWWWttttttttttt__________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Russia", - "size": 8, - "bricks": "________WWWWWWWWWWWWWWWWttttttttttttttttrrrrrrrrrrrrrrrr________________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Ukraine", - "size": 8, - "bricks": "________ttttttttttttttttttttttttyyyyyyyyyyyyyyyyyyyyyyyy________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Poland", - "size": 7, - "bricks": "________WWWWW__WWWWW__rrrrr__rrrrr_______________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Yellow 71", - "size": 9, - "bricks": "_________yyyyy__yyyyyyy_yyy___yy__yy__yyy__yy_yyy___yy_yy____yy_yy____yy__________________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "71 on white", - "size": 6, - "bricks": "WWWWWWrrrWWrWWrWrrWrWWWrWrWWWrWWWWWW______", - "svg": "", - "focus": false - }, - { - "name": "Blue 71", - "size": 8, - "bricks": "ttttt__bttttt_bb___ttbbb__tt__bb__tt__bb_tt___bb_tt___bb_tt___bb", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Seventy one", - "size": 21, - "bricks": "rr_yy_rrry_yrrry_yrrrr_ry_yr__y_yr_ry_y_r_rr_yy_rr_yy_r_ry_y_r_r_ry_yr__y_yr_ry_y_r_rr_y_yrrry_yrrryyy_r_yyy__________________y______________r_____yyyrrry_yrrryyyrr_y_y__yrr_y_yrr_y_yr__y_yyyyrrr_y_rrry_yrrryyy____________________yrrryyyrrr_________yy_r_ry_yrr_____________rrry_yrrryyyyyyyyyyyy_____________________________________________________________________________________________________________________________", - "svg": "", - "focus": false - }, - { - "name": "B71", - "size": 10, - "bricks": "__________bbbtttt_b_b__b__tbb_b__b__t_b_bbb__t__b_b__b_t__b_b__bt___b_bbb_t__bbb__________", - "svg": "", - "focus": false - }, - { - "name": "Pig", - "size": 9, - "bricks": "__________PP___PP__PPP_PPP__WWPPPWW__WBPPPBW__PPsssPP__PsBsBsP__PPsssPP___________", - "svg": "", - "focus": false - }, - { - "name": "Big Pig", - "size": 15, - "bricks": "________________sss_______sss__ss__sssss__ss____sssssssss_____sWBsssssBWs___ssBBsssssBBss__ssss_____ssss__sss_sssss_sss__sss_sBsBs_sss__sss_sssss_sss___sss_____sss____sssssssssss__GGGsssssssssGGGGGGsGsssssGsGGGGGGssGGGGGssGGG_______________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Donkey Kong", - "size": 9, - "bricks": "OOr__a___OOr__a___ppppppp_O______a________a____pppppppr_a______b_a___O__ppppppp__", - "svg": " ", - "color": "", - "focus": false - }, - { - "name": "Banana", - "size": 12, - "bricks": "_________________e__________eee_________eee_________eee_________eeeyy_____yyeeyyyy___yyyyey_yC___yy_yyy___C_____yyyy_________yyyy_________yyyy", - "svg": "", - "focus": false - }, - { - "name": "Fox", - "size": 8, - "bricks": "e______eee_OO_eeeeOOOOeeeOBOOBOeOOOOOOOO_WWBBWW___WWWW_____WW___", - "svg": "", - "focus": false - }, - { - "name": "Wiki", - "size": 10, - "bricks": "_______________________GGGG_____GGkkGG___GkggggkG__GgWWWWgG__GkggggkG___GGkkGG_____GGGG_______________________", - "svg": "", - "focus": false - }, - { - "name": "Baby Dog", - "size": 8, - "bricks": "_______W__eeeeWWWWeeWeWWWegWegeeeeWWWWee_eWggWe__eWWWWe____WW", - "svg": "", - "focus": false - }, - { - "name": "Cute dog", - "size": 9, - "bricks": "__________O_____O_OOOWWWOOOOOWWWWWOOOOeWWWWOO_eBeWWBW__eBeWWBW___eWBWW_____WRW____________", - "svg": "", - "focus": false - }, - { - "name": "perk:extra_life", - "size": 9, - "bricks": "___________rr_rr___rrrrrrr_rrrrrrrrrrrrrrrrrr_rrrrrrr___rrrrr_____rrr_______r_____________", - "svg": "", - "focus": false - }, - { - "name": "perk:streak_shots", - "size": 8, - "bricks": "_W_W_W__W_W_W_W_tttttt_WttttttW_tttttt_W______W______W_____WWWW", - "svg": "", - "focus": false - }, - { - "name": "perk:base_combo", - "size": 8, - "bricks": "ttttttttttyyttttttyytyyttttttyyttyyttttttyytyyttttttyytttttttttt________", - "svg": "", - "focus": false - }, - { - "name": "perk:slow_down", - "size": 10, - "bricks": "_____________kk_______kkkk_____kkkkkkGG__kkkkkkGBG_kkkkkkGGGGkkkkkkGG__GGGGGG____GG__GG_____________", - "svg": "", - "focus": false - }, - { - "name": "perk:bigger_puck", - "size": 8, - "bricks": "_________tttttt__tttttt______________________W___________WWWWWW_", - "svg": "", - "focus": false - }, - { - "name": "perk:viscosity", - "size": 8, - "bricks": "________tt______bbtt__ttbbbbttbbbtbbtbbbbbtbbtbbbbbybbybbbbbbbbb", - "svg": "", - "focus": false - }, - { - "name": "perk:sides_are_lava", - "size": 8, - "bricks": "r______rrttttttrrttttttrr______rr______rr____W_rr______rr_WWW__r", - "svg": "", - "focus": false - }, - { - "name": "perk:telekinesis", - "size": 8, - "bricks": "_____PW_____s______P______s_______P_______s_______P_____WWWWW", - "svg": "", - "focus": false - }, - { - "name": "perk:top_is_lava", - "size": 8, - "bricks": "rrrrrrrr_tttttt__tttttt____________________W_______________WWW__", - "svg": "", - "focus": false - }, - { - "name": "perk:coin_magnet", - "size": 8, - "bricks": "__y__y_yy_________y_y_y_y________y_y______________y______WWW____", - "svg": "", - "focus": false - }, - { - "name": "perk:skip_last", - "size": 5, - "bricks": "_ttt_t_t_ttt_ttt_t_t_ttt_", - "svg": "", - "focus": false - }, - { - "name": "perk:multiball", - "size": 8, - "bricks": "_________tttttt__tttttt___________W__W____________________WWW___", - "svg": "", - "focus": false - }, - { - "name": "perk:smaller_puck", - "size": 8, - "bricks": "_________tttttt__tttttt_____________W_____________________WW____", - "svg": "", - "focus": false - }, - { - "name": "perk:pierce", - "size": 6, - "bricks": "ttttttttttWtttt__ttt__ttt__ttt__tttt", - "svg": "", - "focus": false - }, - { - "name": "perk:picky_eater", - "size": 8, - "bricks": "rtrtrtrttrtrtrtrrtrtrtrt____________________t_____________WWWW", - "svg": "", - "focus": false - }, - { - "name": "perk:metamorphosis", - "size": 8, - "bricks": "aaaaaa__aaaa__________W___________ttaatt__tttttt_________WWW", - "svg": "", - "focus": false - }, - { - "name": "perk:compound_interest", - "size": 8, - "bricks": "_________tttttt__ttt__t______y_____________W__y_________rrWWWrrr", - "svg": "", - "focus": false - }, - { - "name": "perk:hot_start", - "size": 7, - "bricks": "ttttttttttt_tt_____W_____y_y_____y_____y_y_WWW_y_", - "svg": "", - "focus": false - }, - { - "name": "perk:sapper", - "size": 9, - "bricks": "_____WW______W__W_tttWttt_yttgggtt__tgggggt__tgggggt__tgggggt__ttgggtt__ttttttt___________", - "svg": "", - "color": "#000000", - "focus": false - }, - { - "name": "perk:bigger_explosions", - "size": 8, - "bricks": "__r_______ry_rr___ryry__ryyyW_rr_rrWyyy___yryrr__yrry_rr_rr", - "svg": "", - "focus": false - }, - { - "name": "perk:extra_levels", - "size": 6, - "bricks": "__________b__t_bb_ttt_b__t_bbb____________", - "svg": "", - "focus": false - }, - { - "name": "perk:pierce_color", - "size": 8, - "bricks": "bb___bbbb__b_bbb_____bbb____bbbbb____bbbbb____bbbbb____bbbbb____", - "svg": "", - "focus": false - }, - { - "name": "perk:soft_reset", - "size": 8, - "bricks": "___rg_____rrgg___rryggg_rryWggggrryWgggg_ryyggg___rrgg_____rg___", - "svg": "", - "focus": false - }, - { - "name": "perk:ball_repulse_ball", - "size": 8, - "bricks": "WsP__PsWs______sP______P________________P______Ps______sWsP__PsW", - "svg": "", - "focus": false - }, - { - "name": "perk:ball_attract_ball", - "size": 8, - "bricks": "__P__P____s__s__PsW__WsP________________PsW__WsP__s__s____P__P__", - "svg": "", - "focus": false - }, - { - "name": "perk:puck_repulse_ball", - "size": 8, - "bricks": "__________________W_______s___W___P__s______P____________WWW__", - "svg": "", - "focus": false - }, - { - "name": "A", - "size": 7, - "bricks": "___t_____ttt___t___t__t___t_tttttttt_____tt_____t", - "svg": "", - "focus": false - }, - { - "name": "B", - "size": 9, - "bricks": "_bbbbb_____bb_bb____bb_bb____bb_bb____bbbb_____bb_bb____bb_bb____bb_bb___bbbbb____", - "svg": "", - "focus": false - }, - { - "name": "C", - "size": 8, - "bricks": "__rrrr___rrrrrr_rrr___rrrr______rr______rrr___rr_rrrrrr___rrrr", - "svg": "", - "focus": false - }, - { - "name": "D", - "size": 8, - "bricks": "_GGGGG____GG__G___GG__GG__GG__GG__GG__GG__GG__GG__GG__G__GGGGG", - "svg": "", - "focus": false - }, - { - "name": "e", - "size": 8, - "bricks": "__tttt___tttttt_tt____tttt____tttttttttttt_______tt__tt___tttt_", - "svg": "", - "focus": false - }, - { - "name": "perk:wind", - "size": 9, - "bricks": "_ss______s___PPPP_s_________sssssss___________sssssss_s________s___PPPP__ss", - "svg": "", - "focus": false - }, - { - "name": "perk:sturdy_bricks", - "size": 7, - "bricks": "ttbttttbtttbtt____W_____W_W___W___W_______WWW____", - "svg": "", - "focus": false - }, - { - "name": "perk:respawn", - "size": 9, - "bricks": "tttt___ttttt__t__ttta_ttt_______________________________W_________________WWW", - "svg": "", - "focus": false - }, - { - "name": "Elephant", - "size": 18, - "bricks": "_________________________llll_________lll_llllll_lll___lsssllllllllsssl__lsssllllllllsssl__lsssllBllBllsssl__lssllWllllWllssl___ll__llllll__ll_________llll_______________ll______________llll______________ll________________________________________________________________________________________________________________________________________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Orca", - "size": 20, - "bricks": "____________________________________________________________________________________________BBBBB____BBB_BBB___BBBBBBB____BBBBB___BBBBBBBBB____BBB___BBBBWBBWWW_____BBBBBBBBBBBWWWW_____BBBBBBBBBBWWWWW_____BBBBBBBBBWWWWW_______BBBBBBBWWWWW___________WWBBWWW______________BBB_BB______________BB__B______________________________________________________________________________________________________________________________", - "svg": "", - "color": "#1c71d8", - "focus": false - }, - { - "name": "Shark", - "size": 17, - "bricks": "__________________________________________g_______________ggg____________ggggggg_________ggggggggg_______ggggggggggg_____gggggWWWggggg____gBgWWWWWWWgBg___ggWWWWrWrWWWWgg__ggWWWrrrrrWWWgg_ggWWWrrrrrrrWWWggggWWrrrrrrrrrWWgggWWWrWrWrWrWrWWWggWWrrWWWWWWWrrWWggWWWWWWWWWWWWWWWg_________________", - "svg": "", - "color": "#3584e4", - "focus": false - }, - { - "name": "Bird", - "size": 13, - "bricks": "_______RRR____R____RSSSR___RR__RSSWWWR__RSR_RSWWBWR__RSSRRSW_WWyy_RSSSRSWWWR___RSSSSSSRR_____RRSSyyyy______RSyyyyy___RRRRSyyyy____RSSSRyyy_____RRRR________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Tux", - "size": 14, - "bricks": "_____gggg________gggggggg_____gggggggggg____gggggggggg___gggggggggggg__gggWBggWBggg__gggBBggBBggg__ggggyyyygggg_ggggggyyggggggggggWWWWWWggggg_gWWWWWWWWg_g__WWWWWWWWWW____WWWWWWWWWW____yyy____yyy__", - "svg": "", - "color": "#62a0ea", - "focus": false - }, - { - "name": "Armenia", - "size": 6, - "bricks": "_______rrrr__bbbb__yyyy_____________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Austria", - "size": 6, - "bricks": "_______rrrr__WWWW__rrrr______", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Benin", - "size": 8, - "bricks": "_________kkyyyy__kkyyyy__kkrrrr__kkrrrr__________________________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Botswana", - "size": 10, - "bricks": "___________tttttttt__tttttttt__tttttttt__WWWWWWWW__BBBBBBBB__WWWWWWWW__tttttttt__tttttttt__tttttttt___________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Bulgaria", - "size": 6, - "bricks": "_______WWWW__cccc__rrrr_____________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Canada", - "size": 7, - "bricks": "________rWWWr__rWrWr__rWWWr______________________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Chad", - "size": 8, - "bricks": "_________bbyyRR__bbyyRR__bbyyRR", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "China", - "size": 8, - "bricks": "_________RRyRRR__RyRyRR__RRyRRR__RRRRRR", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Colombia", - "size": 7, - "bricks": "________yyyyy__yyyyy__bbbbb__RRRRR_______________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Republic of the Congo", - "size": 7, - "bricks": "________kkkyy__kkyyr__kyyrr__yyrrr_______________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Côte d'Ivoire", - "size": 8, - "bricks": "_________OOWWGG__OOWWGG__OOWWGG", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Denmark", - "size": 8, - "bricks": "_________rrWrrr__rrWrrr__WWWWWW__rrWrrr__rrWrrr", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "El Salvador", - "size": 8, - "bricks": "_________bbbbbb__bbbbbb__WWWkWW__WWkWWW__bbbbbb__bbbbbb", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Egypt", - "size": 8, - "bricks": "_________RRRRRR__RRRRRR__WWWyWW__WWyWWW__gggggg__gggggg", - "svg": "", - "color": "#1c71d8", - "focus": false - }, - { - "name": "Estonia", - "size": 8, - "bricks": "_________tttttt__tttttt__gggggg__gggggg__WWWWWW__WWWWWW", - "svg": "", - "color": "#986a44", - "focus": false - }, - { - "name": "Finland", - "size": 6, - "bricks": "_______WtWW__tttt__WtWW_____________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Gabon", - "size": 5, - "bricks": "______CCC__yyy__ttt______", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Georgia", - "size": 9, - "bricks": "__________WrWrWrW__WWWrWWW__rrrrrrr__WWWrWWW__WrWrWrW__________________", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Guinea", - "size": 8, - "bricks": "_________rryycc__rryycc__rryycc", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Indonesia", - "size": 6, - "bricks": "_______rrrr__rrrr__WWWW__WWWW_______", - "svg": "", - "color": "", - "focus": false - }, - { - "name": "perk:one_more_choice", - "size": 7, - "bricks": "ttt____tbbb___tbttt__tbtbbb__btbbb___tbbb____bbb_", - "svg": "", - "focus": false - }, - { - "name": "perk:instant_upgrade", - "size": 5, - "bricks": "ttt__tbbb_tbbb_tbbb__bbb_", - "svg": "", - "focus": false - } -] \ No newline at end of file diff --git a/app/src/main/assets/palette.js b/app/src/main/assets/palette.js deleted file mode 100644 index 4c2823e..0000000 --- a/app/src/main/assets/palette.js +++ /dev/null @@ -1,25 +0,0 @@ - -const palette={ - _:'', - B:'black', - W:'white', - g:"#231f20", - y: "#ffd300", - b:"#6262EA", - t:"#5DA3EA", - s:"#E67070", - r:"#e32119", - R:"#ab0c0c", - c:"#59EEA3", - G:"#A1F051", - v:"#A664E8", - p:"#E869E8", - a:"#5BECEC", - C:"#53EE53", - S:"#F44848", - P:"#E66BA8", - O:"#F29E4A", - k:"#618227", - e:"#e1c8b4", - l:"#9b9fa4" -} \ No newline at end of file diff --git a/app/src/main/assets/style.css b/app/src/main/assets/style.css deleted file mode 100644 index c20de46..0000000 --- a/app/src/main/assets/style.css +++ /dev/null @@ -1,337 +0,0 @@ -* { - font-family: Courier New, - Courier, - Lucida Sans Typewriter, - Lucida Typewriter, - monospace; - box-sizing: border-box; -} - -body { - margin: 0; - padding: 0; - overflow: hidden; - width: 100vw; - height: 100vh; - height: calc(var(--vh, 1vh) * 100); - color: white; - background-size: 120px 120px; - background-color: var(--background1); - --background1: #030c23; - --background2: #03112a; -} - -#game { - position: absolute; - top: 0; - left: 0; - height: 100vh; - height: calc(var(--vh, 1vh) * 100); - width: 100vw; -} - -#score, -#menu { - position: absolute; - top: 0; - z-index: 1; - padding: 10px; - appearance: none; - background: none; - border: none; - font: inherit; - color: white; - min-width: 40px; - min-height: 40px; - line-height: 20px; -} - -body.black_puck #score, -body.black_puck #menu { - color: black; -} - -#score:hover, -#score:focus, -#menu:hover, -#menu:focus { - background: rgba(0, 0, 0, 0.3); - cursor: pointer; -} - -#score { - right: 0; -} - -#menu { - left: 0; -} - -@media screen and (orientation: portrait) { - #menu > span { - display: none; - } -} - -.popup { - position: fixed; - inset: 0; - background: rgba(0, 0, 0, 0.9); - z-index: 10; - display: flex; - overflow: auto; -} - -.popup > div { - margin: auto; - padding: 20px 10px; - /*border: 1px solid white;*/ - transform-origin: center; - display: flex; - flex-direction: column; - align-items: stretch; - width: 100%; - max-width: 450px; -} - -.popup > div > * { - padding: 0; - margin: 0; -} - -.popup > div > h2, -.popup > div > p { - margin-bottom: 20px; -} - -.popup > div > button { - font: inherit; - background: rgba(0, 0, 0, 0.8); - color: white; - padding: 10px; - cursor: pointer; - border: 1px solid white; - text-align: left; - display: flex; - gap: 10px; - margin-top: -1px; -} - -.popup > div > button:not([disabled]):hover, -.popup > div > button:not([disabled]):focus { - border-color: #f1d33b; - position: relative; - z-index: 1; -} - - -.popup button.close-modale { - color: white; - position: absolute; - top: 0; - right: 0; - width: 60px; - height: 60px; - background: transparent; - border: none; - cursor: pointer; - overflow: hidden; -} - -.popup button.close-modale:before { - content: "+"; - position: absolute; - transform: translate(-50%, -50%) rotate(45deg); - font-size: 80px; - display: inline-block; - top: 34px; - left: 26px; -} - -.popup button.close-modale:hover { - font-weight: bold; - background: black; -} - -.popup > div > button[disabled] { - /*border: 1px solid #666;*/ - opacity: 0.5; - filter: saturate(0); - pointer-events: none; -} - -.popup > div > button > div { - flex-grow: 1; -} - -.popup > div > button > div > em { - display: block; - opacity: 0.8; -} - -.popup > div > button > span.checks { - width: 40px; - height: 40px; - display: inline-flex; - gap: 5px; - flex-grow: 0; - flex-shrink: 0; -} - -.popup > div > button > span.checks > span { - flex-basis: 10px; - flex-grow: 1; - flex-shrink: 1; - /*border: 1px solid white;*/ - background: white; - opacity: 0.1; - border-radius: 4px; - align-self: stretch; -} - -.popup > div > button > span.checks > span.checked { - opacity: 1; -} - -.popup .textAfterButtons { - color: rgba(255, 255, 255, 0.58); -} - -.popup a[href] { - color: inherit; -} - -.popup a[href]:hover, -.popup a[href]:focus { - color: white; -} - -/*Unlocks progress bar*/ -.progress { - display: block; - padding: 5px 10px; - background: #1c1c2f; - color: #fff; - box-shadow: inset 3px 3px 5px rgba(0, 0, 0, 0.5); - border-radius: 5px; - text-align: center; - position: relative; - overflow: hidden; -} - -.progress > .progress_bar_part { - display: block; - background: #4049ca; - box-shadow: inset 3px 3px 5px rgba(0, 0, 0, 0.5); - left: 0; - position: absolute; - right: 0; - top: 0; - bottom: 0; - transform-origin: top left; - animation: grow 1s both ease-out; - z-index: 1; -} - -.progress > span { - display: block; - position: relative; - z-index: 2; -} - -@keyframes grow { - 0% { - transform: scale(0, 1); - } -} - -#level-recording-container { - max-width: 400px; - text-align: center; - margin: 40px; -} - -#level-recording-container img, #level-recording-container video { - max-width: 100%; - height: auto; -} - -#level-recording-container a { - display: block; -} - -#level-recording-container a video { - border-radius: 10px; - display: block; - outline: 1px solid white; - box-shadow: 2px 2px 5px black; - margin: 20px auto; -} - -@media (min-width: 1200px) { - - #level-recording-container { - position: absolute; - top: 40px; - left: 40px; - max-width: calc((100vw - 450px )/2 - 80px); - - } -} - -.histogram { - display: flex; - gap: 10px; - align-items: stretch; - margin: 10px 0 40px 0; -} - - -.histogram > span { -/* Hover zone */ - flex-grow: 1; - width: 10px; - position: relative; - display: flex; - flex-direction: column; - justify-content: flex-end; -} -.histogram > span.active > span{ - background: #4049ca; -} -.histogram > span > span{ - /*Visible bar*/ - background: #1c1c2f; - width: 100%; - display: block; - border-radius: 5px; - min-height: 1px; -} - -.histogram > span > span> span { - /*label */ - position: absolute; - bottom: -20px; - pointer-events: none; - white-space: nowrap; - transform-origin: bottom left; - font-size: 13px; - text-align: center; - display: block; - left: 50%; - transform: translate(-50%,0); - -} - -.histogram > span:not(:hover):not(.active) > span> span { - opacity: 0; -} - -h2.histogram-title { - color: #3b3f75; - font-size: 18px; -} - -h2.histogram-title strong { - color: #4049ca; -} \ No newline at end of file diff --git a/app/src/main/java/me/lecaro/breakout/MainActivity.kt b/app/src/main/java/me/lecaro/breakout/MainActivity.kt index 995ce3f..1d9832e 100644 --- a/app/src/main/java/me/lecaro/breakout/MainActivity.kt +++ b/app/src/main/java/me/lecaro/breakout/MainActivity.kt @@ -1,44 +1,183 @@ package me.lecaro.breakout -import android.content.Context + +import android.content.ContentValues import android.content.Intent import android.net.Uri +import android.os.Build import android.os.Bundle -import android.util.Base64 +import android.os.Environment +import android.provider.MediaStore import android.util.Log import android.view.Window import android.view.WindowManager import android.webkit.ConsoleMessage import android.webkit.DownloadListener -import android.webkit.JavascriptInterface -import android.webkit.MimeTypeMap +import android.webkit.ValueCallback import android.webkit.WebChromeClient import android.webkit.WebView +import android.widget.Toast +import androidx.core.content.FileProvider import java.io.File -import java.io.FileOutputStream +import java.net.URLDecoder +import java.nio.charset.StandardCharsets +import java.text.SimpleDateFormat +import java.util.Date + +const val CHOOSE_FILE_REQUEST_CODE = 548459 class MainActivity : android.app.Activity() { + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + + super.onActivityResult(requestCode, resultCode, data) + when (requestCode) { + CHOOSE_FILE_REQUEST_CODE -> { + if (resultCode == RESULT_OK) { + filePathCallback?.onReceiveValue( + WebChromeClient.FileChooserParams.parseResult( + resultCode, data + ) + ) + filePathCallback = null + } + } + } + } + + var filePathCallback: ValueCallback>? = null + + private fun downloadFile(url: String) { + try { + val sdf = SimpleDateFormat("yyyy-M-dd-hh-mm") + val currentDate = sdf.format(Date()) + + if (url.startsWith("data:application/json;charset=utf-8,")) { + + val urlEncoded = url.substring("data:application/json;charset=utf-8,".length) + val str = URLDecoder.decode(urlEncoded, StandardCharsets.UTF_8.name()) + writeFileAndShare(str.toByteArray(), "breakout-71-save-$currentDate.json", "application/json") + } + + if (url.startsWith("data:video/webm;base64,")) { + val base64Data = url.substring("data:video/webm;base64,".length) + val decodedBytes = android.util.Base64.decode(base64Data, android.util.Base64.DEFAULT) + writeFileAndShare(decodedBytes, "breakout-71-capture-$currentDate.webm", "video/webm") + } + + + } catch (e: Exception) { + Log.e("DL", "Error ${e.message}") + Toast.makeText(this, "Error ${e.message}", Toast.LENGTH_LONG).show() + } + } + + fun writeFileAndShare(bytes:ByteArray, fileName: String, mime: String) { + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + // android 10 + val contentValues = ContentValues().apply { + put(MediaStore.Downloads.DISPLAY_NAME, fileName) + put(MediaStore.Downloads.MIME_TYPE, mime) + put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS) + } + + val uri: Uri? = contentResolver.insert( + MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues + ) + uri?.let { + contentResolver.openOutputStream(it)?.use { outputStream -> + outputStream.write(bytes) + } + } + + val shareIntent: Intent = Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_STREAM, uri) + type = mime + } + startActivity(Intent.createChooser(shareIntent, null)) + + } else { + + val file = File(getExternalFilesDir(null), fileName) + file.writeBytes(bytes) + val uri = FileProvider.getUriForFile( + this, + "$packageName.fileprovider", // Adjust if your authority is different + file + ) + + val shareIntent = Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_STREAM, uri) + type = mime + flags = Intent.FLAG_GRANT_READ_URI_PERMISSION + } + + startActivity(Intent.createChooser(shareIntent, null)) + + } + } + + + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) requestWindowFeature(Window.FEATURE_NO_TITLE); window.setFlags( - WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN + WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN ); -// WebView.setWebContentsDebuggingEnabled(true) val webView = WebView(this) webView.settings.javaScriptEnabled = true webView.settings.domStorageEnabled = true + webView.settings.setSupportZoom(false) + + webView.loadUrl("file:///android_asset/index.html?isInWebView=true") + val activity = this; + webView.webChromeClient = object : WebChromeClient() { override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean { Log.d( - "WebView", "${consoleMessage.message()} -- From line " + - "${consoleMessage.lineNumber()} of ${consoleMessage.sourceId()}" + "WebView", + "${consoleMessage.message()} -- From line " + "${consoleMessage.lineNumber()} of ${consoleMessage.sourceId()}" ) return true } + + + override fun onShowFileChooser( + webView: WebView?, + filePathCallback: ValueCallback>?, + fileChooserParams: FileChooserParams? + ): Boolean { + try { + + startActivityForResult( + fileChooserParams?.createIntent(), CHOOSE_FILE_REQUEST_CODE + ) + this@MainActivity.filePathCallback = filePathCallback + return true + } catch (e: Exception) { + Log.e("DL", "Error ${e.message}") + Toast.makeText(activity, "Error ${e.message}", Toast.LENGTH_LONG).show() + return false + } + } } + webView.setDownloadListener(DownloadListener { url, userAgent, contentDisposition, mimetype, contentLength -> + Log.d("DL", "url: ${url}") + Log.d("DL", "userAgent: ${userAgent}") + Log.d("DL", "contentDisposition: ${contentDisposition}") + Log.d("DL", "mimetype: ${mimetype}") + Log.d("DL", "contentLength: ${contentLength}") + + downloadFile(url) + }) + + + setContentView(webView) } -} +} \ No newline at end of file diff --git a/app/src/main/res/drawable/icon.xml b/app/src/main/res/drawable/icon.xml index fb58588..c29968f 100644 --- a/app/src/main/res/drawable/icon.xml +++ b/app/src/main/res/drawable/icon.xml @@ -1,195 +1,24 @@ + android:width="500dp" + android:height="500dp" + android:viewportWidth="500" + android:viewportHeight="500"> + android:pathData="M0,0h300v100h-300z" + android:fillColor="#6262EA"/> + android:pathData="M200,100h100v100h-100z" + android:fillColor="#6262EA"/> + android:pathData="M100,200h100v200h-100z" + android:fillColor="#6262EA"/> + android:pathData="M200,200h100v100h-100z" + android:fillColor="#5DA3EA"/> + android:pathData="M300,100h100v300h-100z" + android:fillColor="#5DA3EA"/> - - - - - - - - - - - - - - - - - - - - - + android:pathData="M200,400h300v100h-300z" + android:fillColor="#5DA3EA"/> diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml index fa0f996..a483a1c 100644 --- a/app/src/main/res/xml/backup_rules.xml +++ b/app/src/main/res/xml/backup_rules.xml @@ -1,13 +1,5 @@ - + - \ No newline at end of file diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml index 9ee9997..5188608 100644 --- a/app/src/main/res/xml/data_extraction_rules.xml +++ b/app/src/main/res/xml/data_extraction_rules.xml @@ -1,19 +1,9 @@ - + - + - \ No newline at end of file diff --git a/app/src/main/res/xml/file_paths.xml b/app/src/main/res/xml/file_paths.xml new file mode 100644 index 0000000..5e91a74 --- /dev/null +++ b/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..7fdd49d --- /dev/null +++ b/build.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +# the version number is just a unix timestamp in minutes + +defaultVersionCode=$(($(date +%s) / 60)) +versionCode=${1:-$defaultVersionCode} + + +source ~/.nvm/nvm.sh; + +nvm install v21 +nvm use v21 + +if [[ $(node --version) != v21* ]]; then + echo "run first: nvm use v21" + exit 1 +fi + +set -e +set -x + +# clear output folders first, so that they are empty for failed builds +rm -rf ./build/* +rm -rf ./app/src/main/assets/* +rm -rf ./app/build/outputs/apk/release/* +rm -rf ./app/build/outputs/bundle/release/* + + + +# Replace the version code and name in gradle for fdroid and play store +sed -i -e "s/^[[:space:]]*versionCode = .*/ versionCode = $versionCode/" \ + -e "s/^[[:space:]]*versionName = .*/ versionName = \"$versionCode\"/" \ + ./app/build.gradle.kts + +echo "\"$versionCode\"" > src/data/version.json + +# Update service worker +sed -i -e "s/VERSION = .*/ VERSION = '$versionCode'/" ./src/PWA/sw-b71.js + +# remove all exif metadata from pictures, because i think fdroid doesn't like that. odd +find -name '*.jp*g' -o -name '*.png' | xargs exiftool -all= -overwrite_original + +npx prettier --write src/ + +npx jest + +# Actual js app build +npx parcel build src/index.html --dist-dir build + +# Add public files to the web version, but not to the apk +cp public/* build + +# Add only index.html file to the apk, it should be enough +cp build/index.html ./app/src/main/assets/ + + diff --git a/checks.js b/checks.js new file mode 100644 index 0000000..e1a29eb --- /dev/null +++ b/checks.js @@ -0,0 +1,4 @@ +// npx nodemon checks.js +const fs= require('fs') +const english = JSON.parse(fs.readFileSync('./src/i18n/en.json')) +console.debug(Object.entries(english).sort((a,b)=>a[1].length-b[1].length).slice(-10,-1).map(([k,v])=>k+'\n'+k.split('').map(c=>'=').join('')+'\n\n'+v).join('\n\n')) \ No newline at end of file diff --git a/deploy.sh b/deploy.sh index d791257..e15dd3a 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,37 +1,56 @@ #!/bin/bash + +if grep -rE "T[O]DO|F[I]XME|console\.log" src +then + echo "You have left some TO""DO or logs" + exit 1 +fi + +if grep -rE "course|atout" src/i18n/fr.json +then + echo "Bad automatic translations" + exit 1 +fi + + set -e set -x -# Replace the version code and name in gradle for fdroid and play store versionCode=$(($(date +%s) / 60)) -# On macOS you should use -i '' not just -i, but i'm on linux -sed -i -e "s/^[[:space:]]*versionCode = .*/ versionCode = $versionCode/" \ - -e "s/^[[:space:]]*versionName = .*/ versionName = \"$versionCode\"/" \ - ./app/build.gradle.kts +bash ./build.sh $versionCode +# generate signed apk for itch.io +./gradlew assembleRelease -# Invalidate web cache and update version -sed -i "s/\?v=[0-9]*/\?v=$versionCode/g" ./app/src/main/assets/index.html - -# remove all exif metadata from pictures, because i think fdroid doesn't like that. odd -find -name '*.jp*g' -o -name '*.png' | xargs exiftool -all= +# generate signed bundle for play store +./gradlew bundleRelease +# Create a release commit git add . -git commit -m "Automatic deploy $versionCode" -git tag -a $versionCode -m $versionCode +git commit -m "Build $versionCode" git push +# Auto tagging created random releases on fdroid, not great. +# git tag -a $versionCode -m $versionCode - +# upload to breakout.lecaro.me DOMAIN="breakout.lecaro.me" -PUBLIC_CONTENT="./app/src/main/assets/" +PUBLIC_CONTENT="./build/*" + ssh staging "mkdir -p /opt/mup-nginx-proxy/config/html/static_sites/$DOMAIN" rsync -avz --delete --delete-excluded --exclude="*.sh" --exclude="node_modules" --exclude="android" --exclude=".*" $PUBLIC_CONTENT staging:/opt/mup-nginx-proxy/config/html/static_sites/$DOMAIN +# upload to itch.io , upload the index file directly +butler push "./build/index.html" renanlecaro/breakout71:latest --userversion $versionCode +butler push "./build/index.html" renanlecaro/breakout71:offline --userversion $versionCode +butler push app/build/outputs/apk/release/app-release.apk renanlecaro/breakout71:apk --userversion $versionCode + +# archive the output files +FOLDER="/opt/mup-nginx-proxy/config/html/static_sites/archive.lecaro.me/public-files/b71/$versionCode" +ssh staging "mkdir -p $FOLDER" +rsync -vz "./build/index.html" staging:$FOLDER/b71-$versionCode.html +rsync -vz "./app/build/outputs/apk/release/app-release.apk" staging:$FOLDER/b71-$versionCode.apk + -# generate zip for itch -#rm -f breakout.zip -#zip -j breakout.zip app/src/main/assets/* -butler push app/src/main/assets/ renanlecaro/breakout71:latest --userversion $versionCode \ No newline at end of file diff --git a/dist/index.html b/dist/index.html new file mode 100644 index 0000000..a68858a --- /dev/null +++ b/dist/index.html @@ -0,0 +1,7277 @@ + + + + + + + Breakout 71 + + + + + + + + + + + + + + + + + + + diff --git a/editclient.css b/editclient.css deleted file mode 100644 index aa4449f..0000000 --- a/editclient.css +++ /dev/null @@ -1,49 +0,0 @@ - -body { - background: black; - color: white; -} - -#palette button.active { - transform: scale(1.2); -} - -.level-bricks-preview { - position: relative; -} - -#palette { - position: fixed; - top: 0; - right: 0; - width: 80px; - bottom: 0; - overflow: auto; -} - -#levels { - display: flex; - gap: 40px; - align-items: flex-start; - flex-wrap: wrap; - margin-right: 80px; -} - - -#levels>div { - display: grid; - grid-template-columns: auto auto; - grid-template-areas: ". name" "buttons bricks"; -} -#levels>div>*:nth-child(1) { - grid-area: name; -} -#levels>div>div:nth-child(2) { - grid-area: buttons; - display: flex; - flex-direction: column; - align-items: flex-end; -} -#levels>div>div:nth-child(3) { - grid-area: bricks; -} diff --git a/editclient.js b/editclient.js deleted file mode 100644 index a364ee8..0000000 --- a/editclient.js +++ /dev/null @@ -1,247 +0,0 @@ -let currentCode = '_' - -const paletteEl = document.getElementById('palette'); - -Object.entries(palette).forEach(([code, color]) => { - const btn = document.createElement('button') - Object.assign(btn.style, { - background: color ||'linear-gradient(45deg,black,white)', - display: 'inline-block', - width: '40px', - height: '40px', - border: '1px solid black' - }) - if (code === currentCode) { - btn.className = 'active' - } - paletteEl.appendChild(btn) - btn.addEventListener('click', (e) => { - currentCode = code - e.preventDefault() - document.querySelector('#palette button.active')?.classList.remove('active'); - btn.classList.add('active') - }) -}) - -function renderAllLevels() { - allLevels.forEach((level, levelIndex) => { - addLevelEditorToList(level, levelIndex) - - }) -} - -function addLevelEditorToList(level, levelIndex) { - const {name, bricks, size, svg,color} = level - let div = document.createElement('div') - - - div.innerHTML = ` - -
- - - - - - - - - - - - -
- -
-
- `; - document.getElementById('levels').appendChild(div) - - renderLevelBricks(levelIndex) - updateLevelBackground(levelIndex) - -} - -function updateLevelBackground(levelIndex){ - const div=document.getElementById("bricks-of-"+levelIndex) - const {svg, color}= allLevels[levelIndex] - Object.assign(div.style, svg ? - {backgroundImage:`url('data:image/svg+xml,${encodeURIComponent(svg)}')`, backgroundColor:'transparent'} : - {backgroundImage:'none', backgroundColor:color||'#111'} - ) -} - -function renderLevelBricks(levelIndex) { - const {size, bricks} = allLevels[levelIndex] - - const buttons = [] - for (let x = 0; x < size; x++) { - for (let y = 0; y < size; y++) { - const index = y * size + x - buttons.push(``) - } - } - const div = document.getElementById("bricks-of-" + levelIndex) - div.innerHTML = buttons.join('') - Object.assign(div.style, { - width: size * 40 + 'px', - height: size * 40 + 'px' - }) -} - - - -document.getElementById('levels').addEventListener('change', e => { - const levelIndexStr = e.target.getAttribute('data-level') - if ( levelIndexStr) { - const levelIndex = parseInt(levelIndexStr) - const level = allLevels[levelIndex] - if( e.target.getAttribute('type') === 'color'){ - - level.color = e.target.value - level.svg = '' - updateLevelBackground(levelIndex) - }else if( e.target.getAttribute('type') === 'checkbox' && e.target.hasAttribute('data-field')){ - const field=e.target.getAttribute('data-field') - if(field==='focus'){ - allLevels.forEach(l=>l.focus=false) - } - level[field] = !!e.target.checked - - } - - save() - } - -}) -document.getElementById('levels').addEventListener('click', e => { - const resize = e.target.getAttribute('data-offset-level-size') - const moveX = e.target.getAttribute('data-offset-x') - const moveY = e.target.getAttribute('data-offset-y') - const levelIndexStr = e.target.getAttribute('data-level') - if (!levelIndexStr) return - if (e.target.tagName!=='BUTTON') return - - const levelIndex = parseInt(levelIndexStr) - const level = allLevels[levelIndex] - const {bricks, size} = level; - - if (resize) { - const newSize = size + parseInt(resize) - const newBricks = new Array(newSize*newSize).fill('_') - for (let x = 0; x < Math.min(size, newSize); x++) { - for (let y = 0; y < Math.min(size, newSize); y++) { - newBricks[y * newSize + x] = bricks.split('')[y * size + x] || '_' - } - } - level.size = newSize; - level.bricks = newBricks.map(b=>b||'_').join(''); - } else if (moveX && moveY) { - const dx = parseInt(moveX), dy = parseInt(moveY) - const newBricks = new Array(size*size).fill('_') - for (let x = 0; x < size; x++) { - for (let y = 0; y < size; y++) { - newBricks[(y + dy) * size + (x + dx)] = bricks.split('')[y * size + x]|| '_' - } - } - level.bricks = newBricks.map(b=>b||'_').join(''); - } else if (e.target.getAttribute('data-rename')) { - const newName = prompt('Name ? ', level.name) - if (newName) { - level.name = newName - e.target.textContent = newName - } - }else if (e.target.getAttribute('data-delete')) { - - if (confirm('Delete level')) { - allLevels=allLevels.filter((l,i)=>i!==levelIndex) - save().then(()=>window.location.reload()) - } - }else if(e.target.getAttribute('data-set-bg-svg')){ - const newBg = prompt('New svg code',level.svg||'') - if(newBg){ - level.svg=newBg - level.color = '' - } - - updateLevelBackground(levelIndex) - } - renderLevelBricks(levelIndex) - save() - - -}, true) - -let applying = '' - -function colorPixel(e) { - if ( applying == '') return - const index = e.target.getAttribute('data-set-color-of') - const level = e.target.getAttribute('data-level') - if (index && level) { - const levelIndex = parseInt(level) - e.target.style.background = palette[applying] - setBrick(levelIndex,parseInt(index),applying) - - } -} -function setBrick(levelIndex,index,chr) { - const bricks=allLevels[levelIndex].bricks - allLevels[levelIndex].bricks = bricks.substring(0,index) + chr + bricks.substring(index+1); -} -document.getElementById('levels').addEventListener('mousedown', e => { - const index = parseInt(e.target.getAttribute('data-set-color-of')) - const level = e.target.getAttribute('data-level') - if (index && level) { - const before = allLevels[parseInt(level)].bricks[parseInt(index)] || '_' - applying = before === currentCode ? '_' : currentCode - colorPixel(e) - } -}) - -document.getElementById('levels').addEventListener('mouseenter', e => { - if (applying !== '') { - colorPixel(e) - } -}, true) - -document.addEventListener('mouseup', e => { - applying = '' - save() -}) - - -document.getElementById('new-level').addEventListener('click', e => { - - const name = prompt("Name ? ") - if (!name) return; - - allLevels.push({ - name, - size: 8, - bricks: ['white'], - svg: '', - }) - const levelIndex = allLevels.length - 1 - addLevelEditorToList(allLevels[levelIndex], levelIndex) - save() -}, true) - -renderAllLevels() - - -function save() { - return fetch('/', { - method: 'POST', headers: { - 'Content-Type': 'text/plain' - }, - body: 'let allLevels=' + JSON.stringify(allLevels, null, 2) - }) -} diff --git a/editserver.js b/editserver.js index 07fb2aa..d1c70ef 100644 --- a/editserver.js +++ b/editserver.js @@ -4,48 +4,22 @@ const fs = require('fs') const app = express() const port = 4400 -const srcPath = 'app/src/main/assets/levels.js' app.use(bodyParser.text({ type: 'text/plain', limit:'1MB' })); - -app.get('/', (req, res) => { - res.end(` - - - - Level editor - - - - -
-
- - -
- - - - - - - `) +app.get('/src/data/levels.json', (req, res) => { + res.json(JSON.parse(fs.readFileSync('src/data/levels.json'))) }) -app.post('/', (req, res) => { - console.log(req.body) + +app.post('/src/data/levels.json', (req, res) => { if(req.body?.trim()) { - fs.writeFileSync(srcPath, req.body) + fs.writeFileSync('src/data/levels.json', req.body) } res.end('OK') }) app.listen(port, () => { - console.log(`Example app listening on port http://localhost:${port}`) + console.info(`Editor BE listening on port http://localhost:${port}`) }) diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt index 636e4fb..3d9f697 100644 --- a/fastlane/metadata/android/en-US/full_description.txt +++ b/fastlane/metadata/android/en-US/full_description.txt @@ -1,44 +1,21 @@ - -

-This is a roguelite twist on the original Breakout/Arkanoid formula. +This is a roguelike twist on the original Breakout formula: The goal is to catch as many coins as possible during 7 levels. Coins appear when you break bricks. They fly around, bounce and roll, and you need to catch them with your puck. -Your "combo" is the number of coins spawned when a brick breaks, it is displayed on your puck. -Your score is displayed in the top right corner of the screen. -

- -

-At the end of each level, you get to pick an upgrade from a random selection, vampire-survivor style. -Many upgrades impact your "combo". Upgrades apply to the whole run and can synergize. -For example, "hot start" increases your combo greatly at the start of each level, but makes it tick down rapidly. -If you combine it with "+1 ball", "piercing" and "bricks become bombs", you'll clear levels so fast that the combo -will still be high when the fireworks are over and your puck will be showered by coins. -

- -

What decides how the ball flies away is only the position of the puck hit. -The puck speed and incoming angle have no impact. -You must delete all bricks to progress to the next level, and never drop the ball. -

- -

-After clearing a level, you'll be able to pick upgrades among a small selection presented to you. -They'll apply until the end of the run. -A normal run lasts 7 levels, after which your score is recorded, and you can start again. -

- -

-Each run is different, the levels and upgrades you see will change every time, and new ones get added to the pool -when you progress in the game. +At the end of the level, you get to pick upgrades. +There are 50+ different upgrades that impact the gameplay in various ways. +Many upgrades will impact your combo, that's the number of coins spawned for each brick broken. +Your "combo" is displayed on your puck. Your score is displayed in the top right corner of the screen. +Oh, and don't miss the ball, you don't have extra lives.

The app should work offline and perform well even on low-end devices. It's very lean and does not take much storage space (Roughly 0.1MB). -If the app stutters, turn on "fast mode" in the settings to render a simplified view that should be faster. +If the app stutters, turn on "fast mode" in the settings.

-There's also an easy mode for kids (slower ball) and a color-blind mode (no color related game mechanics). -

\ No newline at end of file +The in-game help will tell you more about how to play. Now go have some fun, and come share your best runs on our Discord. +

diff --git a/fastlane/metadata/android/en-US/images/featureGraphic.png b/fastlane/metadata/android/en-US/images/featureGraphic.png index 1cc018b..b5a652b 100644 Binary files a/fastlane/metadata/android/en-US/images/featureGraphic.png and b/fastlane/metadata/android/en-US/images/featureGraphic.png differ diff --git a/fastlane/metadata/android/en-US/images/icon.png b/fastlane/metadata/android/en-US/images/icon.png index b621d50..749546f 100644 Binary files a/fastlane/metadata/android/en-US/images/icon.png and b/fastlane/metadata/android/en-US/images/icon.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png index 9dc87c7..cc89455 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/10.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/10.png deleted file mode 100644 index 58eae03..0000000 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/10.png and /dev/null differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png index 4e92dad..3de10df 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png index 3a5e243..8f1b9b0 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png index 65eeeeb..ebf4637 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png index c6105ad..aa07ac1 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png index 60bb805..58d31fc 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/7.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/7.png index 9b9d85a..2ece186 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/7.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/8.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/8.png index 300150c..75c6a62 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/8.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/8.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/9.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/9.png index f7876d3..e42b287 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/9.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/9.png differ diff --git a/fastlane/metadata/android/en-US/video.txt b/fastlane/metadata/android/en-US/video.txt index 3ac76d0..1656b46 100644 --- a/fastlane/metadata/android/en-US/video.txt +++ b/fastlane/metadata/android/en-US/video.txt @@ -1 +1 @@ -https://www.youtube.com/watch?v=Fmn9320W0Qc \ No newline at end of file +https://www.youtube.com/watch?v=FDJdtSbzCTo \ No newline at end of file diff --git a/gifs/1.gif b/gifs/1.gif deleted file mode 100644 index 6655638..0000000 Binary files a/gifs/1.gif and /dev/null differ diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a641cb5..29739cd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,6 +8,7 @@ espressoCore = "3.5.1" lifecycleRuntimeKtx = "2.6.1" activityCompose = "1.7.0" composeBom = "2023.08.00" +core = "1.13.0" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -24,6 +25,7 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" } +androidx-core = { group = "androidx.core", name = "core", version.ref = "core" } [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } diff --git a/icon.png b/icon.png deleted file mode 100644 index b621d50..0000000 Binary files a/icon.png and /dev/null differ diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..5824b24 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,7 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} **/ +module.exports = { + testEnvironment: "jsdom", + transform: { + "^.+\.tsx?$": ["ts-jest",{}], + }, +}; \ No newline at end of file diff --git a/keystore.properties.example b/keystore.properties.example new file mode 100644 index 0000000..275003d --- /dev/null +++ b/keystore.properties.example @@ -0,0 +1,4 @@ +storePassword=myStorePassword +keyPassword=mykeyPassword +keyAlias=myKeyAlias +storeFile=myStoreFileLocation diff --git a/package-lock.json b/package-lock.json index 6012919..c430919 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,78 +1,3591 @@ { "name": "breakout.lecaro.me", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 3, "requires": true, - "dependencies": { - "accepts": { + "packages": { + "": { + "name": "breakout.lecaro.me", + "version": "1.0.0", + "license": "GNU AGPLv3", + "dependencies": { + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", + "body-parser": "^1.20.3", + "express": "^4.21.2", + "http-server": "^14.1.1", + "jest-environment-jsdom": "^29.7.0", + "nodemon": "^3.1.9", + "npm-run-all": "^4.1.5", + "parcel": "^2.13.3", + "process": "^0.11.10", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "svgo": "^3.3.2" + }, + "devDependencies": { + "@parcel/optimizer-data-url": "^2.13.3", + "@parcel/packager-raw-url": "^2.13.3", + "@parcel/transformer-inline-string": "^2.13.3", + "@parcel/transformer-less": "^2.13.3", + "@parcel/transformer-webmanifest": "^2.13.3", + "@types/jest": "^29.5.14", + "jest": "^29.7.0", + "ts-jest": "^29.2.6", + "typescript": "^5.8.2" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/core/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==", + "dev": true + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.10.tgz", + "integrity": "sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.26.10", + "@babel/types": "^7.26.10", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz", + "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==", + "dev": true, + "dependencies": { + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.10" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz", + "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.26.10" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", + "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.10.tgz", + "integrity": "sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.10", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/traverse/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==", + "dev": true + }, + "node_modules/@babel/types": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", + "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/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==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lezer/common": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", + "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==" + }, + "node_modules/@lezer/lr": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", + "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lmdb/lmdb-darwin-arm64": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.8.5.tgz", + "integrity": "sha512-KPDeVScZgA1oq0CiPBcOa3kHIqU+pTOwRFDIhxvmf8CTNvqdZQYp5cCKW0bUk69VygB2PuTiINFWbY78aR2pQw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-darwin-x64": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.8.5.tgz", + "integrity": "sha512-w/sLhN4T7MW1nB3R/U8WK5BgQLz904wh+/SmA2jD8NnF7BLLoUgflCNxOeSPOWp8geP6nP/+VjWzZVip7rZ1ug==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.8.5.tgz", + "integrity": "sha512-c0TGMbm2M55pwTDIfkDLB6BpIsgxV4PjYck2HiOX+cy/JWiBXz32lYbarPqejKs9Flm7YVAKSILUducU9g2RVg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm64": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.8.5.tgz", + "integrity": "sha512-vtbZRHH5UDlL01TT5jB576Zox3+hdyogvpcbvVJlmU5PdL3c5V7cj1EODdh1CHPksRl+cws/58ugEHi8bcj4Ww==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-x64": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.8.5.tgz", + "integrity": "sha512-Xkc8IUx9aEhP0zvgeKy7IQ3ReX2N8N1L0WPcQwnZweWmOuKfwpS3GRIYqLtK5za/w3E60zhFfNdS+3pBZPytqQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-win32-x64": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.8.5.tgz", + "integrity": "sha512-4wvrf5BgnR8RpogHhtpCPJMKBmvyZPhhUtEwMJbXh0ni2BucpfF07jlmyM11zRqQ2XIq6PbC2j7W7UCCcm1rRQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@mischnic/json-sourcemap": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@mischnic/json-sourcemap/-/json-sourcemap-0.1.1.tgz", + "integrity": "sha512-iA7+tyVqfrATAIsIRWQG+a7ZLLD0VaOCKV2Wd/v4mqIU3J9c4jx9p7S0nw1XH3gJCKNBOOwACOPYYSUu9pgT+w==", + "dependencies": { + "@lezer/common": "^1.0.0", + "@lezer/lr": "^1.0.0", + "json5": "^2.2.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@parcel/bundler-default": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/bundler-default/-/bundler-default-2.13.3.tgz", + "integrity": "sha512-mOuWeth0bZzRv1b9Lrvydis/hAzJyePy0gwa0tix3/zyYBvw0JY+xkXVR4qKyD/blc1Ra2qOlfI2uD3ucnsdXA==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/graph": "3.3.3", + "@parcel/plugin": "2.13.3", + "@parcel/rust": "2.13.3", + "@parcel/utils": "2.13.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/cache": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/cache/-/cache-2.13.3.tgz", + "integrity": "sha512-Vz5+K5uCt9mcuQAMDo0JdbPYDmVdB8Nvu/A2vTEK2rqZPxvoOTczKeMBA4JqzKqGURHPRLaJCvuR8nDG+jhK9A==", + "dependencies": { + "@parcel/fs": "2.13.3", + "@parcel/logger": "2.13.3", + "@parcel/utils": "2.13.3", + "lmdb": "2.8.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.13.3" + } + }, + "node_modules/@parcel/codeframe": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/codeframe/-/codeframe-2.13.3.tgz", + "integrity": "sha512-L/PQf+PT0xM8k9nc0B+PxxOYO2phQYnbuifu9o4pFRiqVmCtHztP+XMIvRJ2gOEXy3pgAImSPFVJ3xGxMFky4g==", + "dependencies": { + "chalk": "^4.1.2" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/compressor-raw": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/compressor-raw/-/compressor-raw-2.13.3.tgz", + "integrity": "sha512-C6vjDlgTLjYc358i7LA/dqcL0XDQZ1IHXFw6hBaHHOfxPKW2T4bzUI6RURyToEK9Q1X7+ggDKqgdLxwp4veCFg==", + "dependencies": { + "@parcel/plugin": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/config-default": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/config-default/-/config-default-2.13.3.tgz", + "integrity": "sha512-WUsx83ic8DgLwwnL1Bua4lRgQqYjxiTT+DBxESGk1paNm1juWzyfPXEQDLXwiCTcWMQGiXQFQ8OuSISauVQ8dQ==", + "dependencies": { + "@parcel/bundler-default": "2.13.3", + "@parcel/compressor-raw": "2.13.3", + "@parcel/namer-default": "2.13.3", + "@parcel/optimizer-css": "2.13.3", + "@parcel/optimizer-htmlnano": "2.13.3", + "@parcel/optimizer-image": "2.13.3", + "@parcel/optimizer-svgo": "2.13.3", + "@parcel/optimizer-swc": "2.13.3", + "@parcel/packager-css": "2.13.3", + "@parcel/packager-html": "2.13.3", + "@parcel/packager-js": "2.13.3", + "@parcel/packager-raw": "2.13.3", + "@parcel/packager-svg": "2.13.3", + "@parcel/packager-wasm": "2.13.3", + "@parcel/reporter-dev-server": "2.13.3", + "@parcel/resolver-default": "2.13.3", + "@parcel/runtime-browser-hmr": "2.13.3", + "@parcel/runtime-js": "2.13.3", + "@parcel/runtime-react-refresh": "2.13.3", + "@parcel/runtime-service-worker": "2.13.3", + "@parcel/transformer-babel": "2.13.3", + "@parcel/transformer-css": "2.13.3", + "@parcel/transformer-html": "2.13.3", + "@parcel/transformer-image": "2.13.3", + "@parcel/transformer-js": "2.13.3", + "@parcel/transformer-json": "2.13.3", + "@parcel/transformer-postcss": "2.13.3", + "@parcel/transformer-posthtml": "2.13.3", + "@parcel/transformer-raw": "2.13.3", + "@parcel/transformer-react-refresh-wrap": "2.13.3", + "@parcel/transformer-svg": "2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.13.3" + } + }, + "node_modules/@parcel/core": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/core/-/core-2.13.3.tgz", + "integrity": "sha512-SRZFtqGiaKHlZ2YAvf+NHvBFWS3GnkBvJMfOJM7kxJRK3M1bhbwJa/GgSdzqro5UVf9Bfj6E+pkdrRQIOZ7jMQ==", + "dependencies": { + "@mischnic/json-sourcemap": "^0.1.0", + "@parcel/cache": "2.13.3", + "@parcel/diagnostic": "2.13.3", + "@parcel/events": "2.13.3", + "@parcel/feature-flags": "2.13.3", + "@parcel/fs": "2.13.3", + "@parcel/graph": "3.3.3", + "@parcel/logger": "2.13.3", + "@parcel/package-manager": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/profiler": "2.13.3", + "@parcel/rust": "2.13.3", + "@parcel/source-map": "^2.1.1", + "@parcel/types": "2.13.3", + "@parcel/utils": "2.13.3", + "@parcel/workers": "2.13.3", + "base-x": "^3.0.8", + "browserslist": "^4.6.6", + "clone": "^2.1.1", + "dotenv": "^16.4.5", + "dotenv-expand": "^11.0.6", + "json5": "^2.2.0", + "msgpackr": "^1.9.9", + "nullthrows": "^1.1.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/diagnostic": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/diagnostic/-/diagnostic-2.13.3.tgz", + "integrity": "sha512-C70KXLBaXLJvr7XCEVu8m6TqNdw1gQLxqg5BQ8roR62R4vWWDnOq8PEksxDi4Y8Z/FF4i3Sapv6tRx9iBNxDEg==", + "dependencies": { + "@mischnic/json-sourcemap": "^0.1.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/events": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/events/-/events-2.13.3.tgz", + "integrity": "sha512-ZkSHTTbD/E+53AjUzhAWTnMLnxLEU5yRw0H614CaruGh+GjgOIKyukGeToF5Gf/lvZ159VrJCGE0Z5EpgHVkuQ==", + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/feature-flags": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/feature-flags/-/feature-flags-2.13.3.tgz", + "integrity": "sha512-UZm14QpamDFoUut9YtCZSpG1HxPs07lUwUCpsAYL0PpxASD3oWJQxIJGfDZPa2272DarXDG9adTKrNXvkHZblw==", + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/fs": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/fs/-/fs-2.13.3.tgz", + "integrity": "sha512-+MPWAt0zr+TCDSlj1LvkORTjfB/BSffsE99A9AvScKytDSYYpY2s0t4vtV9unSh0FHMS2aBCZNJ4t7KL+DcPIg==", + "dependencies": { + "@parcel/feature-flags": "2.13.3", + "@parcel/rust": "2.13.3", + "@parcel/types-internal": "2.13.3", + "@parcel/utils": "2.13.3", + "@parcel/watcher": "^2.0.7", + "@parcel/workers": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.13.3" + } + }, + "node_modules/@parcel/graph": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@parcel/graph/-/graph-3.3.3.tgz", + "integrity": "sha512-pxs4GauEdvCN8nRd6wG3st6LvpHske3GfqGwUSR0P0X0pBPI1/NicvXz6xzp3rgb9gPWfbKXeI/2IOTfIxxVfg==", + "dependencies": { + "@parcel/feature-flags": "2.13.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/logger": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/logger/-/logger-2.13.3.tgz", + "integrity": "sha512-8YF/ZhsQgd7ohQ2vEqcMD1Ag9JlJULROWRPGgGYLGD+twuxAiSdiFBpN3f+j4gQN4PYaLaIS/SwUFx11J243fQ==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/events": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/markdown-ansi": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/markdown-ansi/-/markdown-ansi-2.13.3.tgz", + "integrity": "sha512-B4rUdlNUulJs2xOQuDbN7Hq5a9roq8IZUcJ1vQ8PAv+zMGb7KCfqIIr/BSCDYGhayfAGBVWW8x55Kvrl1zrDYw==", + "dependencies": { + "chalk": "^4.1.2" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/namer-default": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/namer-default/-/namer-default-2.13.3.tgz", + "integrity": "sha512-A2a5A5fuyNcjSGOS0hPcdQmOE2kszZnLIXof7UMGNkNkeC62KAG8WcFZH5RNOY3LT5H773hq51zmc2Y2gE5Rnw==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/node-resolver-core": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@parcel/node-resolver-core/-/node-resolver-core-3.4.3.tgz", + "integrity": "sha512-IEnMks49egEic1ITBp59VQyHzkSQUXqpU9hOHwqN3KoSTdZ6rEgrXcS3pa6tdXay4NYGlcZ88kFCE8i/xYoVCg==", + "dependencies": { + "@mischnic/json-sourcemap": "^0.1.0", + "@parcel/diagnostic": "2.13.3", + "@parcel/fs": "2.13.3", + "@parcel/rust": "2.13.3", + "@parcel/utils": "2.13.3", + "nullthrows": "^1.1.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-css": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-css/-/optimizer-css-2.13.3.tgz", + "integrity": "sha512-A8o9IVCv919vhv69SkLmyW2WjJR5WZgcMqV6L1uiGF8i8z18myrMhrp2JuSHx29PRT9uNyzNC4Xrd4StYjIhJg==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.13.3", + "browserslist": "^4.6.6", + "lightningcss": "^1.22.1", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-data-url": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-data-url/-/optimizer-data-url-2.13.3.tgz", + "integrity": "sha512-qO/FlrnUDAEAtusmCgjUNPMxRO1S/16rnQ+ulgt/Bb/8fkB0h31jrXax+jk6XCyDgyVxdUI8iIqKlin/Qg3ScA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3", + "isbinaryfile": "^5.0.2", + "mime": "^3.0.0" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-data-url/node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@parcel/optimizer-htmlnano": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-htmlnano/-/optimizer-htmlnano-2.13.3.tgz", + "integrity": "sha512-K4Uvg0Sy2pECP7pdvvbud++F0pfcbNkq+IxTrgqBX5HJnLEmRZwgdvZEKF43oMEolclMnURMQRGjRplRaPdbXg==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3", + "htmlnano": "^2.0.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-image": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-image/-/optimizer-image-2.13.3.tgz", + "integrity": "sha512-wlDUICA29J4UnqkKrWiyt68g1e85qfYhp4zJFcFJL0LX1qqh1QwsLUz3YJ+KlruoqPxJSFEC8ncBEKiVCsqhEQ==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/rust": "2.13.3", + "@parcel/utils": "2.13.3", + "@parcel/workers": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.13.3" + } + }, + "node_modules/@parcel/optimizer-svgo": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-svgo/-/optimizer-svgo-2.13.3.tgz", + "integrity": "sha512-piIKxQKzhZK54dJR6yqIcq+urZmpsfgUpLCZT3cnWlX4ux5+S2iN66qqZBs0zVn+a58LcWcoP4Z9ieiJmpiu2w==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-swc": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-swc/-/optimizer-swc-2.13.3.tgz", + "integrity": "sha512-zNSq6oWqLlW8ksPIDjM0VgrK6ZAJbPQCDvs1V+p0oX3CzEe85lT5VkRpnfrN1+/vvEJNGL8e60efHKpI+rXGTA==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.13.3", + "@swc/core": "^1.7.26", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/package-manager": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/package-manager/-/package-manager-2.13.3.tgz", + "integrity": "sha512-FLNI5OrZxymGf/Yln0E/kjnGn5sdkQAxW7pQVdtuM+5VeN75yibJRjsSGv88PvJ+KvpD2ANgiIJo1RufmoPcww==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/fs": "2.13.3", + "@parcel/logger": "2.13.3", + "@parcel/node-resolver-core": "3.4.3", + "@parcel/types": "2.13.3", + "@parcel/utils": "2.13.3", + "@parcel/workers": "2.13.3", + "@swc/core": "^1.7.26", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.13.3" + } + }, + "node_modules/@parcel/packager-css": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/packager-css/-/packager-css-2.13.3.tgz", + "integrity": "sha512-ghDqRMtrUwaDERzFm9le0uz2PTeqqsjsW0ihQSZPSAptElRl9o5BR+XtMPv3r7Ui0evo+w35gD55oQCJ28vCig==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.13.3", + "lightningcss": "^1.22.1", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-html": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/packager-html/-/packager-html-2.13.3.tgz", + "integrity": "sha512-jDLnKSA/EzVEZ3/aegXO3QJ/Ij732AgBBkIQfeC8tUoxwVz5b3HiPBAjVjcUSfZs7mdBSHO+ELWC3UD+HbsIrQ==", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/types": "2.13.3", + "@parcel/utils": "2.13.3", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-js": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/packager-js/-/packager-js-2.13.3.tgz", + "integrity": "sha512-0pMHHf2zOn7EOJe88QJw5h/wcV1bFfj6cXVcE55Wa8GX3V+SdCgolnlvNuBcRQ1Tlx0Xkpo+9hMFVIQbNQY6zw==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/rust": "2.13.3", + "@parcel/source-map": "^2.1.1", + "@parcel/types": "2.13.3", + "@parcel/utils": "2.13.3", + "globals": "^13.2.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-raw": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/packager-raw/-/packager-raw-2.13.3.tgz", + "integrity": "sha512-AWu4UB+akBdskzvT3KGVHIdacU9f7cI678DQQ1jKQuc9yZz5D0VFt3ocFBOmvDfEQDF0uH3jjtJR7fnuvX7Biw==", + "dependencies": { + "@parcel/plugin": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-raw-url": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/packager-raw-url/-/packager-raw-url-2.13.3.tgz", + "integrity": "sha512-Dc8WeVagLGEUzVP4FqJBljXN59XSkvLoZaHeysvN9P33eznocrhIvc9T/OAQjOmsCj18X8jwxm0dIE7LNJbVCA==", + "dev": true, + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-svg": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/packager-svg/-/packager-svg-2.13.3.tgz", + "integrity": "sha512-tKGRiFq/4jh5u2xpTstNQ7gu+RuZWzlWqpw5NaFmcKe6VQe5CMcS499xTFoREAGnRvevSeIgC38X1a+VOo+/AA==", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/types": "2.13.3", + "@parcel/utils": "2.13.3", + "posthtml": "^0.16.4" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-wasm": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/packager-wasm/-/packager-wasm-2.13.3.tgz", + "integrity": "sha512-SZB56/b230vFrSehVXaUAWjJmWYc89gzb8OTLkBm7uvtFtov2J1R8Ig9TTJwinyXE3h84MCFP/YpQElSfoLkJw==", + "dependencies": { + "@parcel/plugin": "2.13.3" + }, + "engines": { + "node": ">=16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/plugin": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/plugin/-/plugin-2.13.3.tgz", + "integrity": "sha512-cterKHHcwg6q11Gpif/aqvHo056TR+yDVJ3fSdiG2xr5KD1VZ2B3hmofWERNNwjMcnR1h9Xq40B7jCKUhOyNFA==", + "dependencies": { + "@parcel/types": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/profiler": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/profiler/-/profiler-2.13.3.tgz", + "integrity": "sha512-ok6BwWSLvyHe5TuSXjSacYnDStFgP5Y30tA9mbtWSm0INDsYf+m5DqzpYPx8U54OaywWMK8w3MXUClosJX3aPA==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/events": "2.13.3", + "@parcel/types-internal": "2.13.3", + "chrome-trace-event": "^1.0.2" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/reporter-cli": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/reporter-cli/-/reporter-cli-2.13.3.tgz", + "integrity": "sha512-EA5tKt/6bXYNMEavSs35qHlFdx6cZmRazlZxPBgxPePQYoouNAPMNLUOEQozaPhz9f5fvNDN7EHOFaAWcdO2LA==", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/types": "2.13.3", + "@parcel/utils": "2.13.3", + "chalk": "^4.1.2", + "term-size": "^2.2.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/reporter-dev-server": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/reporter-dev-server/-/reporter-dev-server-2.13.3.tgz", + "integrity": "sha512-ZNeFp6AOIQFv7mZIv2P5O188dnZHNg0ymeDVcakfZomwhpSva2dFNS3AnvWo4eyWBlUxkmQO8BtaxeWTs7jAuA==", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/reporter-tracer": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/reporter-tracer/-/reporter-tracer-2.13.3.tgz", + "integrity": "sha512-aBsVPI8jLZTDkFYrI69GxnsdvZKEYerkPsu935LcX9rfUYssOnmmUP+3oI+8fbg+qNjJuk9BgoQ4hCp9FOphMQ==", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3", + "chrome-trace-event": "^1.0.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/resolver-default": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/resolver-default/-/resolver-default-2.13.3.tgz", + "integrity": "sha512-urBZuRALWT9pFMeWQ8JirchLmsQEyI9lrJptiwLbJWrwvmlwSUGkcstmPwoNRf/aAQjICB7ser/247Vny0pFxA==", + "dependencies": { + "@parcel/node-resolver-core": "3.4.3", + "@parcel/plugin": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/runtime-browser-hmr": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/runtime-browser-hmr/-/runtime-browser-hmr-2.13.3.tgz", + "integrity": "sha512-EAcPojQFUNUGUrDk66cu3ySPO0NXRVS5CKPd4QrxPCVVbGzde4koKu8krC/TaGsoyUqhie8HMnS70qBP0GFfcQ==", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/runtime-js": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/runtime-js/-/runtime-js-2.13.3.tgz", + "integrity": "sha512-62OucNAnxb2Q0uyTFWW/0Hvv2DJ4b5H6neh/YFu2/wmxaZ37xTpEuEcG2do7KW54xE5DeLP+RliHLwi4NvR3ww==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/runtime-react-refresh": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/runtime-react-refresh/-/runtime-react-refresh-2.13.3.tgz", + "integrity": "sha512-PYZ1klpJVwqE3WuifILjtF1dugtesHEuJcXYZI85T6UoRSD5ctS1nAIpZzT14Ga1lRt/jd+eAmhWL1l3m/Vk1Q==", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3", + "react-error-overlay": "6.0.9", + "react-refresh": ">=0.9 <=0.14" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/runtime-service-worker": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/runtime-service-worker/-/runtime-service-worker-2.13.3.tgz", + "integrity": "sha512-BjMhPuT7Us1+YIo31exPRwomPiL+jrZZS5UUAwlEW2XGHDceEotzRM94LwxeFliCScT4IOokGoxixm19qRuzWg==", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/rust": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/rust/-/rust-2.13.3.tgz", + "integrity": "sha512-dLq85xDAtzr3P5200cvxk+8WXSWauYbxuev9LCPdwfhlaWo/JEj6cu9seVdWlkagjGwkoV1kXC+GGntgUXOLAQ==", + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/source-map": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@parcel/source-map/-/source-map-2.1.1.tgz", + "integrity": "sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==", + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": "^12.18.3 || >=14" + } + }, + "node_modules/@parcel/transformer-babel": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-babel/-/transformer-babel-2.13.3.tgz", + "integrity": "sha512-ikzK9f5WTFrdQsPitQgjCPH6HmVU8AQPRemIJ2BndYhtodn5PQut5cnSvTrqax8RjYvheEKCQk/Zb/uR7qgS3g==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.13.3", + "browserslist": "^4.6.6", + "json5": "^2.2.0", + "nullthrows": "^1.1.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-css": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-css/-/transformer-css-2.13.3.tgz", + "integrity": "sha512-zbrNURGph6JeVADbGydyZ7lcu/izj41kDxQ9xw4RPRW/3rofQiTU0OTREi+uBWiMENQySXVivEdzHA9cA+aLAA==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.13.3", + "browserslist": "^4.6.6", + "lightningcss": "^1.22.1", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-html": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-html/-/transformer-html-2.13.3.tgz", + "integrity": "sha512-Yf74FkL9RCCB4+hxQRVMNQThH9+fZ5w0NLiQPpWUOcgDEEyxTi4FWPQgEBsKl/XK2ehdydbQB9fBgPQLuQxwPg==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/rust": "2.13.3", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "posthtml-parser": "^0.12.1", + "posthtml-render": "^3.0.0", + "semver": "^7.5.2", + "srcset": "4" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-html/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/@parcel/transformer-html/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/@parcel/transformer-html/node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/@parcel/transformer-html/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@parcel/transformer-html/node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/@parcel/transformer-html/node_modules/posthtml-parser": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.12.1.tgz", + "integrity": "sha512-rYFmsDLfYm+4Ts2Oh4DCDSZPtdC1BLnRXAobypVzX9alj28KGl65dIFtgDY9zB57D0TC4Qxqrawuq/2et1P0GA==", + "dependencies": { + "htmlparser2": "^9.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@parcel/transformer-html/node_modules/srcset": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz", + "integrity": "sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@parcel/transformer-image": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-image/-/transformer-image-2.13.3.tgz", + "integrity": "sha512-wL1CXyeFAqbp2wcEq/JD3a/tbAyVIDMTC6laQxlIwnVV7dsENhK1qRuJZuoBdixESeUpFQSmmQvDIhcfT/cUUg==", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3", + "@parcel/workers": "2.13.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "peerDependencies": { + "@parcel/core": "^2.13.3" + } + }, + "node_modules/@parcel/transformer-inline-string": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-inline-string/-/transformer-inline-string-2.13.3.tgz", + "integrity": "sha512-xxFCpY9NKJJ05tECb6nYjswAg0SDccuVeGTuNNXBOSMT2UPSAFZdjVYLjXOiKV417ffJ36ZqLkHDluxNoN++yA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-js": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-js/-/transformer-js-2.13.3.tgz", + "integrity": "sha512-KqfNGn1IHzDoN2aPqt4nDksgb50Xzcny777C7A7hjlQ3cmkjyJrixYjzzsPaPSGJ+kJpknh3KE8unkQ9mhFvRQ==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/rust": "2.13.3", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.13.3", + "@parcel/workers": "2.13.3", + "@swc/helpers": "^0.5.0", + "browserslist": "^4.6.6", + "nullthrows": "^1.1.1", + "regenerator-runtime": "^0.14.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.13.3" + } + }, + "node_modules/@parcel/transformer-json": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-json/-/transformer-json-2.13.3.tgz", + "integrity": "sha512-rrq0ab6J0w9ePtsxi0kAvpCmrUYXXAx1Z5PATZakv89rSYbHBKEdXxyCoKFui/UPVCUEGVs5r0iOFepdHpIyeA==", + "dependencies": { + "@parcel/plugin": "2.13.3", + "json5": "^2.2.0" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-less": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-less/-/transformer-less-2.13.3.tgz", + "integrity": "sha512-w2UHs6zpY6J+ZfI+C4o1TU0HQMdSIbmq6zn35O1S+6zZ23W8XrA1uy+sL5lyhvVDVUyw8v7piNot61DzOkjQgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/source-map": "^2.1.1", + "less": "^4.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-postcss": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-postcss/-/transformer-postcss-2.13.3.tgz", + "integrity": "sha512-AIiWpU0QSFBrPcYIqAnhqB8RGE6yHFznnxztfg1t2zMSOnK3xoU6xqYKv8H/MduShGGrC3qVOeDfM8MUwzL3cw==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/rust": "2.13.3", + "@parcel/utils": "2.13.3", + "clone": "^2.1.1", + "nullthrows": "^1.1.1", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-posthtml": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-posthtml/-/transformer-posthtml-2.13.3.tgz", + "integrity": "sha512-5GSLyccpHASwFAu3uJ83gDIBSvfsGdVmhJvy0Vxe+K1Fklk2ibhvvtUHMhB7mg6SPHC+R9jsNc3ZqY04ZLeGjw==", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "posthtml-parser": "^0.12.1", + "posthtml-render": "^3.0.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-posthtml/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/@parcel/transformer-posthtml/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/@parcel/transformer-posthtml/node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/@parcel/transformer-posthtml/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@parcel/transformer-posthtml/node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/@parcel/transformer-posthtml/node_modules/posthtml-parser": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.12.1.tgz", + "integrity": "sha512-rYFmsDLfYm+4Ts2Oh4DCDSZPtdC1BLnRXAobypVzX9alj28KGl65dIFtgDY9zB57D0TC4Qxqrawuq/2et1P0GA==", + "dependencies": { + "htmlparser2": "^9.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@parcel/transformer-raw": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-raw/-/transformer-raw-2.13.3.tgz", + "integrity": "sha512-BFsAbdQF0l8/Pdb7dSLJeYcd8jgwvAUbHgMink2MNXJuRUvDl19Gns8jVokU+uraFHulJMBj40+K/RTd33in4g==", + "dependencies": { + "@parcel/plugin": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-react-refresh-wrap": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-react-refresh-wrap/-/transformer-react-refresh-wrap-2.13.3.tgz", + "integrity": "sha512-mOof4cRyxsZRdg8kkWaFtaX98mHpxUhcGPU+nF9RQVa9q737ItxrorsPNR9hpZAyE2TtFNflNW7RoYsgvlLw8w==", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3", + "react-refresh": ">=0.9 <=0.14" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-svg": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-svg/-/transformer-svg-2.13.3.tgz", + "integrity": "sha512-9jm7ZF4KHIrGLWlw/SFUz5KKJ20nxHvjFAmzde34R9Wu+F1BOjLZxae7w4ZRwvIc+UVOUcBBQFmhSVwVDZg6Dw==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/rust": "2.13.3", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "posthtml-parser": "^0.12.1", + "posthtml-render": "^3.0.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-svg/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/@parcel/transformer-svg/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/@parcel/transformer-svg/node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/@parcel/transformer-svg/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@parcel/transformer-svg/node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/@parcel/transformer-svg/node_modules/posthtml-parser": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.12.1.tgz", + "integrity": "sha512-rYFmsDLfYm+4Ts2Oh4DCDSZPtdC1BLnRXAobypVzX9alj28KGl65dIFtgDY9zB57D0TC4Qxqrawuq/2et1P0GA==", + "dependencies": { + "htmlparser2": "^9.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@parcel/transformer-webmanifest": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-webmanifest/-/transformer-webmanifest-2.13.3.tgz", + "integrity": "sha512-nUuufZW4VYQTk1qf8HLLckxC5AYtGSWMAHxKTqoJldXl1OgpYkpSPLU+Uh1uiESN+XpQiZB4qnhCExRjPeCRSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mischnic/json-sourcemap": "^0.1.0", + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3" + }, + "engines": { + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/types": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/types/-/types-2.13.3.tgz", + "integrity": "sha512-+RpFHxx8fy8/dpuehHUw/ja9PRExC3wJoIlIIF42E7SLu2SvlTHtKm6EfICZzxCXNEBzjoDbamCRcN0nmTPlhw==", + "dependencies": { + "@parcel/types-internal": "2.13.3", + "@parcel/workers": "2.13.3" + } + }, + "node_modules/@parcel/types-internal": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/types-internal/-/types-internal-2.13.3.tgz", + "integrity": "sha512-Lhx0n+9RCp+Ipktf/I+CLm3zE9Iq9NtDd8b2Vr5lVWyoT8AbzBKIHIpTbhLS4kjZ80L3I6o93OYjqAaIjsqoZw==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/feature-flags": "2.13.3", + "@parcel/source-map": "^2.1.1", + "utility-types": "^3.10.0" + } + }, + "node_modules/@parcel/utils": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/utils/-/utils-2.13.3.tgz", + "integrity": "sha512-yxY9xw2wOUlJaScOXYZmMGoZ4Ck4Kqj+p6Koe5kLkkWM1j98Q0Dj2tf/mNvZi4yrdnlm+dclCwNRnuE8Q9D+pw==", + "dependencies": { + "@parcel/codeframe": "2.13.3", + "@parcel/diagnostic": "2.13.3", + "@parcel/logger": "2.13.3", + "@parcel/markdown-ansi": "2.13.3", + "@parcel/rust": "2.13.3", + "@parcel/source-map": "^2.1.1", + "chalk": "^4.1.2", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "hasInstallScript": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/workers": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/workers/-/workers-2.13.3.tgz", + "integrity": "sha512-oAHmdniWTRwwwsKbcF4t3VjOtKN+/W17Wj5laiYB+HLkfsjGTfIQPj3sdXmrlBAGpI4omIcvR70PHHXnfdTfwA==", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/logger": "2.13.3", + "@parcel/profiler": "2.13.3", + "@parcel/types-internal": "2.13.3", + "@parcel/utils": "2.13.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.13.3" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@swc/core": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.11.7.tgz", + "integrity": "sha512-ICuzjyfz8Hh3U16Mb21uCRJeJd/lUgV999GjgvPhJSISM1L8GDSB5/AMNcwuGs7gFywTKI4vAeeXWyCETUXHAg==", + "hasInstallScript": true, + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.19" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.11.7", + "@swc/core-darwin-x64": "1.11.7", + "@swc/core-linux-arm-gnueabihf": "1.11.7", + "@swc/core-linux-arm64-gnu": "1.11.7", + "@swc/core-linux-arm64-musl": "1.11.7", + "@swc/core-linux-x64-gnu": "1.11.7", + "@swc/core-linux-x64-musl": "1.11.7", + "@swc/core-win32-arm64-msvc": "1.11.7", + "@swc/core-win32-ia32-msvc": "1.11.7", + "@swc/core-win32-x64-msvc": "1.11.7" + }, + "peerDependencies": { + "@swc/helpers": "*" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.11.7.tgz", + "integrity": "sha512-3+LhCP2H50CLI6yv/lhOtoZ5B/hi7Q/23dye1KhbSDeDprLTm/KfLJh/iQqwaHUponf5m8C2U0y6DD+HGLz8Yw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.11.7.tgz", + "integrity": "sha512-1diWpJqwX1XmOghf9ENFaeRaTtqLiqlZIW56RfOqmeZ7tPp3qS7VygWb9akptBsO5pEA5ZwNgSerD6AJlQcjAw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.11.7.tgz", + "integrity": "sha512-MV8+hLREf0NN23NuSKemsjFaWjl/HnqdOkE7uhXTnHzg8WTwp6ddVtU5Yriv15+d/ktfLWPVAOhLHQ4gzaoa8A==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.11.7.tgz", + "integrity": "sha512-5GNs8ZjHQy/UTSnzzn+gm1RCUpCYo43lsxYOl8mpcnZSfxkNFVpjfylBv0QuJ5qhdfZ2iU55+v4iJCwCMtw0nA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.11.7.tgz", + "integrity": "sha512-cTydaYBwDbVV5CspwVcCp9IevYWpGD1cF5B5KlBdjmBzxxeWyTAJRtKzn8w5/UJe/MfdAptarpqMPIs2f33YEQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.11.7.tgz", + "integrity": "sha512-YAX2KfYPlbDsnZiVMI4ZwotF3VeURUrzD+emJgFf1g26F4eEmslldgnDrKybW7V+bObsH22cDqoy6jmQZgpuPQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.11.7.tgz", + "integrity": "sha512-mYT6FTDZyYx5pailc8xt6ClS2yjKmP8jNHxA9Ce3K21n5qkKilI5M2N7NShwXkd3Ksw3F29wKrg+wvEMXTRY/A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.11.7.tgz", + "integrity": "sha512-uLDQEcv0BHcepypstyxKkNsW6KfLyI5jVxTbcxka+B2UnMcFpvoR87nGt2JYW0grO2SNZPoFz+UnoKL9c6JxpA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.11.7.tgz", + "integrity": "sha512-wiq5G3fRizdxAJVFcon7zpyfbfrb+YShuTy+TqJ4Nf5PC0ueMOXmsmeuyQGApn6dVWtGCyymYQYt77wHeQajdA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.11.7.tgz", + "integrity": "sha512-/zQdqY4fHkSORxEJ2cKtRBOwglvf/8gs6Tl4Q6VMx2zFtFpIOwFQstfY5u8wBNN2Z+PkAzyUCPoi8/cQFK8HLQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@swc/types": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.19.tgz", + "integrity": "sha512-WkAZaAfj44kh/UFdAQcrMP1I0nwRqpt27u+08LMBYMqmQfwwMofYoMh/48NGkMMRfC4ynpfwRbJuu8ErfNloeA==", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "node_modules/@types/node": { + "version": "22.13.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", + "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/react": { + "version": "19.0.11", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.11.tgz", + "integrity": "sha512-vrdxRZfo9ALXth6yPfV16PYTLZwsUWhVjjC+DkfE5t1suNSbBrWC9YqSuuxJZ8Ps6z1o2ycRpIqzZJIgklq4Tw==", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz", + "integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==" + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead" + }, + "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==", - "requires": { + "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" } }, - "ansi-styles": { + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/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/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { + "dependencies": { "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "anymatch": { + "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "requires": { + "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "array-flatten": { + "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/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==" }, - "async": { + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "requires": { + "dependencies": { "lodash": "^4.17.14" } }, - "balanced-match": { + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "engines": { + "node": ">= 0.4" + } + }, + "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/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "basic-auth": { + "node_modules/base-x": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.10.tgz", + "integrity": "sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "requires": { + "dependencies": { "safe-buffer": "5.1.2" }, - "dependencies": { - "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==" - } + "engines": { + "node": ">= 0.8" } }, - "binary-extensions": { + "node_modules/basic-auth/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==" + }, + "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==" + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "body-parser": { + "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "requires": { + "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", @@ -85,209 +3598,1325 @@ "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "brace-expansion": { + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "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==", - "requires": { + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "braces": { + "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "requires": { + "dependencies": { "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" } }, - "bytes": { + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "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==" + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } }, - "call-bind-apply-helpers": { + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "requires": { + "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "call-bound": { + "node_modules/call-bound": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", - "requires": { + "dependencies": { "call-bind-apply-helpers": "^1.0.1", "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "chalk": { + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001702", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001702.tgz", + "integrity": "sha512-LoPe/D7zioC0REI5W73PeR1e1MLCipRGq/VkovJnd6Df+QVqT+vT33OXCp8QUd7kA7RZrHWxb1B36OQKI/0gOA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { + "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" }, - "dependencies": { - "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" - } - } + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "chokidar": { + "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/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "requires": { + "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.3.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "color-convert": { + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "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==", - "requires": { + "dependencies": { "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "color-name": { + "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==" }, - "concat-map": { + "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": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "engines": { + "node": ">=18" + } + }, + "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, - "content-disposition": { + "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==", - "requires": { + "dependencies": { "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" } }, - "content-type": { + "node_modules/content-type": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } }, - "cookie": { + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cookie": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==" + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "engines": { + "node": ">= 0.6" + } }, - "cookie-signature": { + "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, - "corser": { + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/corser": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", - "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==" + "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", + "engines": { + "node": ">= 0.4.0" + } }, - "debug": { + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/cross-spawn/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-select/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/css-select/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/css-select/node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/css-select/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==" + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==" + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { + "dependencies": { "ms": "2.0.0" } }, - "depd": { + "node_modules/decimal.js": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==" + }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "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==" + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } }, - "destroy": { + "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } }, - "dunder-proto": { + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", + "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "requires": { + "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" } }, - "ee-first": { + "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==" }, - "encodeurl": { + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.112", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.112.tgz", + "integrity": "sha512-oen93kVyqSb3l+ziUgzIOlWt/oOuy4zRmpwestMn4rhFWAoFJeFuCVte9F2fASjeZZo7l/Cif9TiyrdW4CwEMA==" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "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==", + "dev": true + }, + "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } }, - "es-define-property": { + "node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } }, - "es-errors": { + "node_modules/es-errors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } }, - "es-object-atoms": { + "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "requires": { + "dependencies": { "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" } }, - "escape-html": { + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "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==" }, - "etag": { + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "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" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "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==" + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } }, - "eventemitter3": { + "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, - "express": { + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/execa/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/execa/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/execa/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/execa/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/express": { "version": "4.21.2", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "requires": { + "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", @@ -320,41 +4949,98 @@ "utils-merge": "1.0.1", "vary": "~1.1.2" }, - "dependencies": { - "body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "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.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - } - } + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "fill-range": { + "node_modules/express/node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "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.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "requires": { + "dependencies": { "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "finalhandler": { + "node_modules/finalhandler": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "requires": { + "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", @@ -362,39 +5048,164 @@ "parseurl": "~1.3.3", "statuses": "2.0.1", "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "follow-redirects": { + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { "version": "1.15.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==" + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } }, - "forwarded": { + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "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==" + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } }, - "fresh": { + "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } }, - "fsevents": { + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "optional": true + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, - "function-bind": { + "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "get-intrinsic": { + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.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", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", - "requires": { + "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", @@ -405,88 +5216,389 @@ "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "get-proto": { + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-port": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz", + "integrity": "sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "requires": { + "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" } }, - "glob-parent": { + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "requires": { + "dependencies": { "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, - "gopd": { + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "has-flag": { + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==" + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } }, - "has-symbols": { + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "hasown": { + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "requires": { + "dependencies": { "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "he": { + "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } }, - "html-encoding-sniffer": { + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + }, + "node_modules/html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "requires": { + "dependencies": { "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" } }, - "http-errors": { + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/htmlnano": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.1.1.tgz", + "integrity": "sha512-kAERyg/LuNZYmdqgCdYvugyLWNFAm8MWXpQMz1pLpetmCbFwoMxvkSoaAMlFrOC4OKTWI4KlZGT/RsNxg4ghOw==", + "dependencies": { + "cosmiconfig": "^9.0.0", + "posthtml": "^0.16.5", + "timsort": "^0.3.0" + }, + "peerDependencies": { + "cssnano": "^7.0.0", + "postcss": "^8.3.11", + "purgecss": "^6.0.0", + "relateurl": "^0.2.7", + "srcset": "5.0.1", + "svgo": "^3.0.2", + "terser": "^5.10.0", + "uncss": "^0.17.3" + }, + "peerDependenciesMeta": { + "cssnano": { + "optional": true + }, + "postcss": { + "optional": true + }, + "purgecss": { + "optional": true + }, + "relateurl": { + "optional": true + }, + "srcset": { + "optional": true + }, + "svgo": { + "optional": true + }, + "terser": { + "optional": true + }, + "uncss": { + "optional": true + } + } + }, + "node_modules/htmlparser2": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", + "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.2", + "domutils": "^2.8.0", + "entities": "^3.0.1" + } + }, + "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==", - "requires": { + "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" } }, - "http-proxy": { + "node_modules/http-proxy": { "version": "1.18.1", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "requires": { + "dependencies": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" } }, - "http-server": { + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/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/http-server": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", - "requires": { + "dependencies": { "basic-auth": "^2.0.1", "chalk": "^4.1.2", "corser": "^2.0.1", @@ -500,136 +5612,2143 @@ "secure-compare": "3.0.1", "union": "~0.5.0", "url-join": "^4.0.1" + }, + "bin": { + "http-server": "bin/http-server" + }, + "engines": { + "node": ">=12" } }, - "iconv-lite": { + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/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/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "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==", - "requires": { + "dependencies": { "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" } }, - "ignore-by-default": { + "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==" }, - "inherits": { + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "ipaddr.js": { + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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==" + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } }, - "is-binary-path": { + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "requires": { + "dependencies": { "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" } }, - "is-extglob": { + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } }, - "is-glob": { + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "requires": { + "dependencies": { "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-number": { + "node_modules/is-json": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz", + "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==" + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } }, - "lodash": { + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "node_modules/isbinaryfile": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.4.tgz", + "integrity": "sha512-YKBKVkKhty7s8rxddb40oOkuP0NbaeXrQvLin6QMHL7Ypiy2RW9LwOVrVgZRyOrhQlayMd9t+D8yDy8MKFTSDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/istanbul-lib-source-maps/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==", + "dev": true + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-changed-files/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/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/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/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/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/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jest/node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "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/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/less": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/less/-/less-4.2.2.tgz", + "integrity": "sha512-tkuLHQlvWUTeQ3doAqnHbNn8T6WX1KA8yvbKG9x4VtKtIjHsVKQZCH11zRgAfbDAXC2UNIg/K9BYAAcEzUIrNg==", + "dev": true, + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/lightningcss": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.1.tgz", + "integrity": "sha512-FmGoeD4S05ewj+AkhTY+D+myDvXI6eL27FjHIjoyUkO/uw7WZD1fBVs0QxeYWa7E17CUHJaYX/RUGISCtcrG4Q==", + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.29.1", + "lightningcss-darwin-x64": "1.29.1", + "lightningcss-freebsd-x64": "1.29.1", + "lightningcss-linux-arm-gnueabihf": "1.29.1", + "lightningcss-linux-arm64-gnu": "1.29.1", + "lightningcss-linux-arm64-musl": "1.29.1", + "lightningcss-linux-x64-gnu": "1.29.1", + "lightningcss-linux-x64-musl": "1.29.1", + "lightningcss-win32-arm64-msvc": "1.29.1", + "lightningcss-win32-x64-msvc": "1.29.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.1.tgz", + "integrity": "sha512-HtR5XJ5A0lvCqYAoSv2QdZZyoHNttBpa5EP9aNuzBQeKGfbyH5+UipLWvVzpP4Uml5ej4BYs5I9Lco9u1fECqw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.1.tgz", + "integrity": "sha512-k33G9IzKUpHy/J/3+9MCO4e+PzaFblsgBjSGlpAaFikeBFm8B/CkO3cKU9oI4g+fjS2KlkLM/Bza9K/aw8wsNA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.1.tgz", + "integrity": "sha512-0SUW22fv/8kln2LnIdOCmSuXnxgxVC276W5KLTwoehiO0hxkacBxjHOL5EtHD8BAXg2BvuhsJPmVMasvby3LiQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.1.tgz", + "integrity": "sha512-sD32pFvlR0kDlqsOZmYqH/68SqUMPNj+0pucGxToXZi4XZgZmqeX/NkxNKCPsswAXU3UeYgDSpGhu05eAufjDg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.1.tgz", + "integrity": "sha512-0+vClRIZ6mmJl/dxGuRsE197o1HDEeeRk6nzycSy2GofC2JsY4ifCRnvUWf/CUBQmlrvMzt6SMQNMSEu22csWQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.1.tgz", + "integrity": "sha512-UKMFrG4rL/uHNgelBsDwJcBqVpzNJbzsKkbI3Ja5fg00sgQnHw/VrzUTEc4jhZ+AN2BvQYz/tkHu4vt1kLuJyw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.1.tgz", + "integrity": "sha512-u1S+xdODy/eEtjADqirA774y3jLcm8RPtYztwReEXoZKdzgsHYPl0s5V52Tst+GKzqjebkULT86XMSxejzfISw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.1.tgz", + "integrity": "sha512-L0Tx0DtaNUTzXv0lbGCLB/c/qEADanHbu4QdcNOXLIe1i8i22rZRpbT3gpWYsCh9aSL9zFujY/WmEXIatWvXbw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.1.tgz", + "integrity": "sha512-QoOVnkIEFfbW4xPi+dpdft/zAKmgLgsRHfJalEPYuJDOWf7cLQzYg0DEh8/sn737FaeMJxHZRc1oBreiwZCjog==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.1.tgz", + "integrity": "sha512-NygcbThNBe4JElP+olyTI/doBNGJvLs3bFCRPdvuCcxZCcCZ71B858IHpdm7L1btZex0FvCmM17FK98Y9MRy1Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "node_modules/lmdb": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-2.8.5.tgz", + "integrity": "sha512-9bMdFfc80S+vSldBmG3HOuLVHnxRdNTlpzR6QDnzqCQtCzGUEAGTzBKYMeIM+I/sU4oZfgbcbS7X7F65/z/oxQ==", + "hasInstallScript": true, + "dependencies": { + "msgpackr": "^1.9.5", + "node-addon-api": "^6.1.0", + "node-gyp-build-optional-packages": "5.1.1", + "ordered-binary": "^1.4.1", + "weak-lru-cache": "^1.2.2" + }, + "bin": { + "download-lmdb-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@lmdb/lmdb-darwin-arm64": "2.8.5", + "@lmdb/lmdb-darwin-x64": "2.8.5", + "@lmdb/lmdb-linux-arm": "2.8.5", + "@lmdb/lmdb-linux-arm64": "2.8.5", + "@lmdb/lmdb-linux-x64": "2.8.5", + "@lmdb/lmdb-win32-x64": "2.8.5" + } + }, + "node_modules/lmdb/node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==" + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "math-intrinsics": { + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } }, - "media-typer": { + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" + }, + "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==" + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } }, - "merge-descriptors": { + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==" + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "methods": { + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } }, - "mime": { + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dependencies": { + "braces": "^3.0.3", + "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==" + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } }, - "mime-db": { + "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==" + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } }, - "mime-types": { + "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==", - "requires": { + "dependencies": { "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" } }, - "minimatch": { + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { + "dependencies": { "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "minimist": { + "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "mkdirp": { + "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "requires": { + "dependencies": { "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" } }, - "ms": { + "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "negotiator": { + "node_modules/msgpackr": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.2.tgz", + "integrity": "sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g==", + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + } + }, + "node_modules/msgpackr-extract/node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/needle": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", + "dev": true, + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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==" + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } }, - "nodemon": { + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==" + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz", + "integrity": "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==", + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/node-gyp-build-optional-packages/node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" + }, + "node_modules/nodemon": { "version": "3.1.9", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz", "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==", - "requires": { + "dependencies": { "chokidar": "^3.5.2", "debug": "^4", "ignore-by-default": "^1.0.1", @@ -641,161 +7760,1038 @@ "touch": "^3.1.0", "undefsafe": "^2.0.5" }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dependencies": { - "debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "requires": { - "ms": "^2.1.3" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true } } }, - "normalize-path": { + "node_modules/nodemon/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/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } }, - "object-inspect": { + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm-run-all/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/npm-run-all/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/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/npm-run-all/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/npm-run-all/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/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nullthrows": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", + "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==" + }, + "node_modules/nwsapi": { + "version": "2.2.18", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.18.tgz", + "integrity": "sha512-p1TRH/edngVEHVbwqWnxUViEmq5znDvyB+Sik5cmuLpGOIfDf/39zLiq3swPF8Vakqn+gvNiOQAZu8djYlQILA==" + }, + "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==" + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "on-finished": { + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "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==", - "requires": { + "dependencies": { "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" } }, - "opener": { + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/opener": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==" + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "bin": { + "opener": "bin/opener-bin.js" + } }, - "parseurl": { + "node_modules/ordered-binary": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.5.3.tgz", + "integrity": "sha512-oGFr3T+pYdTGJ+YFEILMpS3es+GiIbs9h/XQrclBXUtd44ey7XwfsMzM31f64I1SQOawDoDr/D823kNCADI8TA==" + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parcel": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/parcel/-/parcel-2.13.3.tgz", + "integrity": "sha512-8GrC8C7J8mwRpAlk7EJ7lwdFTbCN+dcXH2gy5AsEs9pLfzo9wvxOTx6W0fzSlvCOvZOita+8GdfYlGfEt0tRgA==", + "dependencies": { + "@parcel/config-default": "2.13.3", + "@parcel/core": "2.13.3", + "@parcel/diagnostic": "2.13.3", + "@parcel/events": "2.13.3", + "@parcel/feature-flags": "2.13.3", + "@parcel/fs": "2.13.3", + "@parcel/logger": "2.13.3", + "@parcel/package-manager": "2.13.3", + "@parcel/reporter-cli": "2.13.3", + "@parcel/reporter-dev-server": "2.13.3", + "@parcel/reporter-tracer": "2.13.3", + "@parcel/utils": "2.13.3", + "chalk": "^4.1.2", + "commander": "^12.1.0", + "get-port": "^4.2.0" + }, + "bin": { + "parcel": "lib/bin.js" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } }, - "path-to-regexp": { + "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": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-to-regexp": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" }, - "picomatch": { + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-type/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "portfinder": { + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/portfinder": { "version": "1.0.32", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", - "requires": { + "dependencies": { "async": "^2.6.4", "debug": "^3.2.7", "mkdirp": "^0.5.6" }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } + "engines": { + "node": ">= 0.12.0" } }, - "proxy-addr": { + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/portfinder/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/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/posthtml": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", + "integrity": "sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==", + "dependencies": { + "posthtml-parser": "^0.11.0", + "posthtml-render": "^3.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/posthtml-parser": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz", + "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==", + "dependencies": { + "htmlparser2": "^7.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/posthtml-render": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz", + "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==", + "dependencies": { + "is-json": "^2.0.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "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==", - "requires": { + "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" } }, - "pstree.remy": { + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" }, - "qs": { + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "requires": { + "dependencies": { "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "range-parser": { + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "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==" + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } }, - "raw-body": { + "node_modules/raw-body": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "requires": { + "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "readdirp": { + "node_modules/react": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", + "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", + "dependencies": { + "scheduler": "^0.25.0" + }, + "peerDependencies": { + "react": "^19.0.0" + } + }, + "node_modules/react-error-overlay": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", + "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==" + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "requires": { + "dependencies": { "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" } }, - "requires-port": { + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, - "safe-buffer": { + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "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" + } + ] }, - "safer-buffer": { + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "secure-compare": { + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "dev": true, + "optional": true + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/scheduler": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", + "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==" + }, + "node_modules/secure-compare": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==" }, - "semver": { + "node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==" + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "send": { + "node_modules/send": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "requires": { + "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -810,177 +8806,1243 @@ "range-parser": "~1.2.1", "statuses": "2.0.1" }, - "dependencies": { - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } + "engines": { + "node": ">= 0.8.0" } }, - "serve-static": { + "node_modules/send/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/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.16.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "requires": { + "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "setprototypeof": { + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, - "side-channel": { + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shell-quote": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "requires": { + "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "side-channel-list": { + "node_modules/side-channel-list": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "requires": { + "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "side-channel-map": { + "node_modules/side-channel-map": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "requires": { + "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "side-channel-weakmap": { + "node_modules/side-channel-weakmap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "requires": { + "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "simple-update-notifier": { + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/simple-update-notifier": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", - "requires": { + "dependencies": { "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" } }, - "statuses": { + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "devOptional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==" + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/srcset": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/srcset/-/srcset-5.0.1.tgz", + "integrity": "sha512-/P1UYbGfJVlxZag7aABNRrulEXAwCSDo7fklafOQrantuPTDmYgijJMks2zusPCVzgW9+4P69mq7w6pYuZpgxw==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } }, - "supports-color": { + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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/string.prototype.padend": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", + "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", - "requires": { + "dependencies": { "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "to-regex-range": { + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svgo": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", + "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "node_modules/term-size": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "requires": { + "dependencies": { "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "toidentifier": { + "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==" + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } }, - "touch": { + "node_modules/touch": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", - "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==" + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "bin": { + "nodetouch": "bin/nodetouch.js" + } }, - "type-is": { + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/ts-jest": { + "version": "29.2.6", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.6.tgz", + "integrity": "sha512-yTNZVZqc8lSixm+QGVFcPe6+yj7+TWZwIesuOWvfcn4B9bz5x4NDzVCQQjOs7Hfouu36aEqfEbo9Qpo+gq8dDg==", + "dev": true, + "dependencies": { + "bs-logger": "^0.2.6", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "^2.1.0", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.1", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "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==", - "requires": { + "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" } }, - "undefsafe": { + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "devOptional": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" }, - "union": { + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" + }, + "node_modules/union": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", - "requires": { + "dependencies": { "qs": "^6.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "unpipe": { + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "engines": { + "node": ">= 4.0.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==" + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } }, - "url-join": { + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==" }, - "utils-merge": { + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/utility-types": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", + "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", + "engines": { + "node": ">= 4" + } + }, + "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==" + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } }, - "vary": { + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "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==" + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } }, - "whatwg-encoding": { + "node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/weak-lru-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", + "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==" + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "requires": { + "dependencies": { "iconv-lite": "0.6.3" }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dependencies": { - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array/node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array/node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true } } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "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.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "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" + } } } } diff --git a/package.json b/package.json index c7f4b50..26e13ed 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,40 @@ { "name": "breakout.lecaro.me", "version": "1.0.0", - "description": "", - "main": "index.js", + "description": "A roguelite take on the breakout genre, optimised for short runs and replayability.", "scripts": { - "start": "nodemon editserver.js --watch editserver.js ", - "serve": "http-server app/src/main/assets -o" + "start": "bash start.sh", + "dev:game-fe": "rm -rf .parcel-cache && parcel src/*.html --lazy --no-hmr --no-cache", + "dev:editor-be": "nodemon editserver.js --watch editserver.js", + "test": "jest --watch" }, - "author": "", - "license": "ISC", + "browserslist": "since 2009", + "author": "Renan LE CARO", + "license": "GNU AGPLv3", "dependencies": { + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", "body-parser": "^1.20.3", "express": "^4.21.2", "http-server": "^14.1.1", - "nodemon": "^3.1.9" + "jest-environment-jsdom": "^29.7.0", + "nodemon": "^3.1.9", + "npm-run-all": "^4.1.5", + "parcel": "^2.13.3", + "process": "^0.11.10", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "svgo": "^3.3.2" + }, + "devDependencies": { + "@parcel/optimizer-data-url": "^2.13.3", + "@parcel/packager-raw-url": "^2.13.3", + "@parcel/transformer-inline-string": "^2.13.3", + "@parcel/transformer-less": "^2.13.3", + "@parcel/transformer-webmanifest": "^2.13.3", + "@types/jest": "^29.5.14", + "jest": "^29.7.0", + "ts-jest": "^29.2.6", + "typescript": "^5.8.2" } } diff --git a/patterns.html b/patterns.html deleted file mode 100644 index 7f7dfb3..0000000 --- a/patterns.html +++ /dev/null @@ -1,163 +0,0 @@ - - - - - Patterns preview - - - - - - - \ No newline at end of file diff --git a/app/src/main/assets/privacy.html b/public/privacy.html similarity index 80% rename from app/src/main/assets/privacy.html rename to public/privacy.html index 754cb9b..f5c3d6b 100644 --- a/app/src/main/assets/privacy.html +++ b/public/privacy.html @@ -8,7 +8,7 @@ /> Breakout 71 privacy policy - + + + + + + + + + + + + + + + diff --git a/src/levelEditor.ts b/src/levelEditor.ts new file mode 100644 index 0000000..5ac5dbb --- /dev/null +++ b/src/levelEditor.ts @@ -0,0 +1,299 @@ +import { icons, transformRawLevel } from "./loadGameData"; +import { t } from "./i18n/i18n"; +import { getSettingValue, getTotalScore, setSettingValue } from "./settings"; +import { asyncAlert } from "./asyncAlert"; +import { Palette, RawLevel } from "./types"; +import { levelIconHTML } from "./levelIcon"; + +import _palette from "./data/palette.json"; +import { restart } from "./game"; +import { describeLevel } from "./game_utils"; +import { + automaticBackgroundColor, + levelCodeToRawLevel, + MAX_LEVEL_SIZE, + MIN_LEVEL_SIZE, +} from "./pure_functions"; +import { toast } from "./toast"; + +const palette = _palette as Palette; + +export function levelEditorMenuEntry() { + const min = 10000; + const disabled = getTotalScore() < min; + return { + icon: icons["icon:editor"], + text: t("editor.title"), + disabled, + help: disabled ? t("editor.locked", { min }) : t("editor.help"), + async value() { + openLevelEditorLevelsList().then(); + }, + }; +} + +async function openLevelEditorLevelsList() { + const rawList = getSettingValue("custom_levels", []) as RawLevel[]; + const customLevels = rawList.map(transformRawLevel); + + let choice = await asyncAlert({ + title: t("editor.title"), + content: [ + ...customLevels.map((l, li) => ({ + text: l.name, + icon: levelIconHTML(l.bricks, l.size), + value() { + editRawLevelList(li); + }, + help: l.credit || describeLevel(l), + })), + { + text: t("editor.new_level"), + icon: icons["icon:editor"], + value() { + rawList.push({ + color: "", + size: 6, + bricks: "____________________________________", + name: "custom level" + (rawList.length + 1), + credit: "", + }); + setSettingValue("custom_levels", rawList); + editRawLevelList(rawList.length - 1); + }, + }, + { + text: t("editor.import"), + help: t("editor.import_instruction"), + value() { + const code = prompt(t("editor.import_instruction"))?.trim(); + if (code) { + const lvl = levelCodeToRawLevel(code); + if (lvl) { + rawList.push(lvl); + setSettingValue("custom_levels", rawList); + } + } + openLevelEditorLevelsList(); + }, + }, + ], + }); + if (typeof choice == "function") choice(); +} + +export async function editRawLevelList(nth: number, color = "W") { + let rawList = getSettingValue("custom_levels", []) as RawLevel[]; + const level = rawList[nth]; + const bricks = level.bricks.split(""); + let grid = ""; + for (let y = 0; y < level.size; y++) { + grid += '
'; + for (let x = 0; x < level.size; x++) { + const c = bricks[y * level.size + x]; + grid += `${c == "B" ? "💣" : ""}`; + } + grid += "
"; + } + + const levelColors = new Set(bricks); + levelColors.delete("_"); + levelColors.delete("B"); + + let colorList = + '
' + + Object.entries(palette) + .filter(([key, value]) => key !== "_") + .filter( + ([key, value]) => + levelColors.size < 5 || levelColors.has(key) || key === "B", + ) + .map( + ([key, value]) => + `${key == "B" ? "💣" : ""}`, + ) + .join("") + + "
"; + + const clicked = await asyncAlert({ + title: t("editor.editing.title", { name: level.name }), + content: [ + t("editor.editing.color"), + colorList, + t("editor.editing.help"), + `
${grid}
`, + + { + icon: icons["icon:new_run"], + text: t("editor.editing.play"), + value: "play", + }, + { + text: t("editor.editing.rename"), + value: "rename", + help: level.name, + }, + { + text: t("editor.editing.credit"), + value: "credit", + help: level.credit, + }, + { + text: t("editor.editing.delete"), + value: "delete", + }, + { + text: t("editor.editing.copy"), + value: "copy", + help: t("editor.editing.copy_help"), + }, + { + text: t("editor.editing.show_code"), + value: "show_code", + help: t("editor.editing.show_code_help"), + }, + { + text: t("editor.editing.bigger"), + value: "size:+1", + disabled: level.size >= MAX_LEVEL_SIZE, + }, + { + text: t("editor.editing.smaller"), + value: "size:-1", + disabled: level.size <= MIN_LEVEL_SIZE, + }, + { + text: t("editor.editing.left"), + value: "move:-1:0", + }, + { + text: t("editor.editing.right"), + value: "move:1:0", + }, + { + text: t("editor.editing.up"), + value: "move:0:-1", + }, + { + text: t("editor.editing.down"), + value: "move:0:1", + }, + ], + }); + if (!clicked) return; + if (typeof clicked === "string") { + const [action, a, b] = clicked.split(":"); + if (action == "paint_brick") { + const x = parseInt(a), + y = parseInt(b); + bricks[y * level.size + x] = + bricks[y * level.size + x] === color ? "_" : color; + level.bricks = bricks.join(""); + } + if (action == "set_color") { + color = a; + } + if (action == "size") { + const newSize = level.size + parseInt(a); + const newBricks = []; + for (let y = 0; y < newSize; y++) { + for (let x = 0; x < newSize; x++) { + newBricks.push( + (x < level.size && y < level.size && bricks[y * level.size + x]) || + "_", + ); + } + } + level.size = newSize; + level.bricks = newBricks.join(""); + } + if (action == "move") { + const dx = parseInt(a), + dy = parseInt(b); + const newBricks = []; + for (let y = 0; y < level.size; y++) { + for (let x = 0; x < level.size; x++) { + const tx = x - dx; + const ty = y - dy; + if (tx < 0 || tx >= level.size || ty < 0 || ty >= level.size) { + newBricks.push("_"); + } else { + newBricks.push(bricks[ty * level.size + tx]); + } + } + } + level.bricks = newBricks.join(""); + } + if (action === "play") { + restart({ + level: transformRawLevel(level), + isEditorTrialRun: nth, + perks: { + base_combo: 7, + }, + }); + return; + } + if (action === "copy" || action === "show_code") { + let text = + "```\n[" + + (level.name || "unnamed level")?.replace(/\[|\]/gi, " ") + + "]"; + bricks.forEach((b, bi) => { + if (!(bi % level.size)) text += "\n"; + text += b; + }); + text += + "\n[" + + (level.credit?.replace(/\[|\]/gi, " ") || "Missing credits") + + "]\n```"; + + if (action === "copy") { + try { + await navigator.clipboard.writeText(text); + toast(t("editor.editing.copied")); + } catch (e) { + if ("message" in e) { + toast(e.message); + } + } + } else { + await asyncAlert({ + title: t("editor.editing.show_code"), + content: [ + ` +
${text}
+ `, + ], + }); + } + // return + } + if (action === "rename") { + const name = prompt(t("editor.editing.rename_prompt"), level.name); + if (name) { + level.name = name; + } + } + if (action === "credit") { + const credit = prompt( + t("editor.editing.credit_prompt"), + level.credit || "", + ); + if (credit !== "null") { + level.credit = credit || ""; + } + } + if (action === "delete") { + rawList = rawList.filter((l, li) => li !== nth); + setSettingValue("custom_levels", rawList); + openLevelEditorLevelsList(); + return; + } + } + + level.color = automaticBackgroundColor(bricks); + + setSettingValue("custom_levels", rawList); + editRawLevelList(nth, color); +} diff --git a/src/levelIcon.ts b/src/levelIcon.ts new file mode 100644 index 0000000..020f4e5 --- /dev/null +++ b/src/levelIcon.ts @@ -0,0 +1,38 @@ +let levelIconHTMLCanvas = document.createElement("canvas"); + +const levelIconHTMLCanvasCtx = + process.env.NODE_ENV !== "test" && + (levelIconHTMLCanvas.getContext("2d", { + antialias: false, + alpha: true, + }) as CanvasRenderingContext2D); + +export function levelIconHTML(bricks: string[], levelSize: number) { + const size = 46; + const c = levelIconHTMLCanvas; + const ctx = levelIconHTMLCanvasCtx; + + if (!ctx) return ""; + c.width = size; + c.height = size; + + ctx.clearRect(0, 0, size, size); + + const pxSize = size / levelSize; + for (let x = 0; x < levelSize; x++) { + for (let y = 0; y < levelSize; y++) { + const c = bricks[y * levelSize + x]; + if (c) { + ctx.fillStyle = c; + ctx.fillRect( + Math.floor(pxSize * x), + Math.floor(pxSize * y), + Math.ceil(pxSize), + Math.ceil(pxSize), + ); + } + } + } + + return ``; +} diff --git a/src/level_editor/level_editor_util.test.ts b/src/level_editor/level_editor_util.test.ts new file mode 100644 index 0000000..40e001e --- /dev/null +++ b/src/level_editor/level_editor_util.test.ts @@ -0,0 +1,47 @@ +import { moveLevel, resizeLevel, setBrick } from "./levels_editor_util"; + +const baseLevel = { + name: "", + bricks: "AAAA", + size: 2, + svg: null, + color: "", +}; +describe("resizeLevel", () => { + it("should expand levels", () => { + expect(resizeLevel(baseLevel, 1)).toStrictEqual({ + bricks: "AA_AA____", + size: 3, + }); + }); + it("should shrink levels", () => { + expect(resizeLevel(baseLevel, -1)).toStrictEqual({ bricks: "A", size: 1 }); + }); +}); + +describe("moveLevel", () => { + it("should do nothing when coords are 0/0", () => { + expect(moveLevel(baseLevel, 0, 0)).toStrictEqual({ bricks: "AAAA" }); + }); + it("should move right", () => { + expect(moveLevel(baseLevel, 1, 0)).toStrictEqual({ bricks: "_A_A" }); + }); + it("should move left", () => { + expect(moveLevel(baseLevel, -1, 0)).toStrictEqual({ bricks: "A_A_" }); + }); + it("should move up", () => { + expect(moveLevel(baseLevel, 0, -1)).toStrictEqual({ bricks: "AA__" }); + }); + it("should move down", () => { + expect(moveLevel(baseLevel, 0, 1)).toStrictEqual({ bricks: "__AA" }); + }); +}); + +describe("setBrick", () => { + it("should set the first brick", () => { + expect(setBrick(baseLevel, 0, "C")).toStrictEqual({ bricks: "CAAA" }); + }); + it("should any brick", () => { + expect(setBrick(baseLevel, 2, "C")).toStrictEqual({ bricks: "AACA" }); + }); +}); diff --git a/src/level_editor/levels_editor.less b/src/level_editor/levels_editor.less new file mode 100644 index 0000000..5e4e14b --- /dev/null +++ b/src/level_editor/levels_editor.less @@ -0,0 +1,56 @@ +body { + background: black; + color: white; +} + +#palette { + position: fixed; + top: 0; + right: 0; + width: 80px; + bottom: 0; + overflow: auto; + + button.active { + transform: scale(1.2); + } +} + +#levels { + display: flex; + gap: 40px; + align-items: flex-start; + flex-wrap: wrap; + margin-right: 80px; + + .level-bricks-preview { + position: relative; + } + input[type="number"] { + width: 50px; + } + + & > div { + display: grid; + grid-template-columns: auto auto; + grid-template-areas: "name credits" "buttons bricks"; + .name { + grid-area: name; + width: 100px; + } + .credits { + grid-area: credits; + } + + .buttons { + grid-area: buttons; + display: flex; + flex-direction: column; + align-items: flex-end; + } + + .level-bricks-preview { + grid-area: bricks; + } + } +} diff --git a/src/level_editor/levels_editor.tsx b/src/level_editor/levels_editor.tsx new file mode 100644 index 0000000..2db9d5c --- /dev/null +++ b/src/level_editor/levels_editor.tsx @@ -0,0 +1,223 @@ +import { Palette, RawLevel } from "../types"; +import _palette from "../data/palette.json"; +import { createRoot } from "react-dom/client"; +import { useEffect, useState } from "react"; +import { moveLevel, resizeLevel, setBrick } from "./levels_editor_util"; +import { + automaticBackgroundColor, + levelCodeToRawLevel, +} from "../pure_functions"; + +const palette = _palette as Palette; + +let allLevels = null; + +function App() { + const [selected, setSelected] = useState("W"); + const [applying, setApplying] = useState(""); + const [levels, setLevels] = useState([]); + useEffect(() => { + fetch("http://localhost:4400/src/data/levels.json") + .then((r) => r.json()) + .then((lvls) => { + const cleaned = lvls.map((l) => ({ + name: l.name, + size: l.size, + bricks: (l.bricks + "_".repeat(l.size * l.size)).slice( + 0, + l.size * l.size, + ), + credit: l.credit || "", + })); + const sorted = [ + ...cleaned + .filter((l) => l.name.match("icon:")) + .sort((a, b) => (a.name > b.name ? 1 : -1)), + ...cleaned.filter((l) => !l.name.match("icon:")), + ]; + setLevels(sorted as RawLevel[]); + allLevels = sorted; + }); + }, []); + + const updateLevel = (index: number, change: Partial) => { + setLevels((list) => + list.map((l, li) => (li === index ? { ...l, ...change } : l)), + ); + }; + + const deleteLevel = (index: number) => { + if (confirm("Delete level")) { + setLevels(levels.filter((l, i) => i !== index)); + } + }; + useEffect(() => { + if (!allLevels || JSON.stringify(allLevels) === JSON.stringify(levels)) + return; + const timoutId = setTimeout(() => { + return fetch("http://localhost:4400/src/data/levels.json", { + method: "POST", + headers: { + "Content-Type": "text/plain", + }, + body: JSON.stringify(levels, null, 2), + }); + }, 500); + return () => clearTimeout(timoutId); + }, [levels]); + + return ( +
{ + setApplying(""); + }} + > +
+ {levels.map((level, li) => { + const { name, credit, bricks, size, svg, color } = level; + + const brickButtons = []; + for (let x = 0; x < size; x++) { + for (let y = 0; y < size; y++) { + const index = y * size + x; + brickButtons.push( + , + ); + } + } + + return ( +
+ updateLevel(li, { name: e.target.value })} + /> + updateLevel(li, { credit: e.target.value })} + /> + +
+ + + + + + + +
+
+ {brickButtons} +
+
+ ); + })} +
+
+ {Object.entries(palette).map(([code, color]) => ( + + ))} +
+ + +
+ ); +} + +const root = createRoot(document.getElementById("app") as HTMLDivElement); +root.render(); diff --git a/src/level_editor/levels_editor_util.ts b/src/level_editor/levels_editor_util.ts new file mode 100644 index 0000000..e7cf15d --- /dev/null +++ b/src/level_editor/levels_editor_util.ts @@ -0,0 +1,55 @@ +import { RawLevel } from "../types"; + +export function resizeLevel(level: RawLevel, sizeDelta: number) { + const { size, bricks } = level; + const newSize = Math.max(1, size + sizeDelta); + const newBricks = []; + for (let x = 0; x < newSize; x++) { + for (let y = 0; y < newSize; y++) { + newBricks[y * newSize + x] = brickAt(level, x, y); + } + } + return { + size: newSize, + bricks: newBricks.join(""), + }; +} + +export function brickAt(level: RawLevel, x: number, y: number) { + return ( + (x >= 0 && + x < level.size && + y >= 0 && + y < level.size && + level.bricks.split("")[y * level.size + x]) || + "_" + ); +} + +export function moveLevel(level: RawLevel, dx: number, dy: number) { + const { size } = level; + const newBricks = new Array(size * size).fill("_"); + for (let x = 0; x < size; x++) { + for (let y = 0; y < size; y++) { + newBricks[y * size + x] = brickAt(level, x - dx, y - dy); + } + } + return { + bricks: newBricks.join(""), + }; +} + +export function setBrick(level: RawLevel, index: number, colorCode: string) { + const { size } = level; + const newBricks = []; + for (let x = 0; x < size; x++) { + for (let y = 0; y < size; y++) { + const brickIndex = y * size + x; + newBricks[brickIndex] = + (brickIndex === index && colorCode) || brickAt(level, x, y); + } + } + return { + bricks: newBricks.join(""), + }; +} diff --git a/src/loadGameData.test.ts b/src/loadGameData.test.ts new file mode 100644 index 0000000..1559671 --- /dev/null +++ b/src/loadGameData.test.ts @@ -0,0 +1,36 @@ +import _palette from "./data/palette.json"; +import _rawLevelsList from "./data/levels.json"; +import _appVersion from "./data/version.json"; + +describe("json data checks", () => { + it("_rawLevelsList has icon levels", () => { + expect( + _rawLevelsList.filter((l) => l.name.startsWith("icon:")).length, + ).toBeGreaterThan(10); + }); + + it("_rawLevelsList has non-icon few levels", () => { + expect( + _rawLevelsList.filter((l) => !l.name.startsWith("icon:")).length, + ).toBeGreaterThan(10); + }); + + it("_rawLevelsList has max 5 colors per level", () => { + const levelsWithManyBrickColors = _rawLevelsList + .filter((l) => { + const uniqueBricks = l.bricks + .split("") + .filter((b) => b !== "_" && b !== "B") + .filter((a, b, c) => c.indexOf(a) === b); + return uniqueBricks.length > 5 && !l.name.startsWith("icon:"); + }) + .map((l) => l.name); + expect(levelsWithManyBrickColors).toEqual([]); + }); + it("Has a few colors", () => { + expect(Object.keys(_palette).length).toBeGreaterThan(10); + }); + it("Has an _appVersion", () => { + expect(parseInt(_appVersion)).toBeGreaterThan(2000); + }); +}); diff --git a/src/loadGameData.ts b/src/loadGameData.ts new file mode 100644 index 0000000..03f15ed --- /dev/null +++ b/src/loadGameData.ts @@ -0,0 +1,47 @@ +import { Level, Palette, RawLevel, Upgrade } from "./types"; +import _palette from "./data/palette.json"; +import _rawLevelsList from "./data/levels.json"; +import _appVersion from "./data/version.json"; +import { rawUpgrades } from "./upgrades"; +import { getLevelBackground } from "./getLevelBackground"; +import { levelIconHTML } from "./levelIcon"; + +import { automaticBackgroundColor } from "./pure_functions"; + +export const upgrades = [...rawUpgrades].sort( + (a, b) => a.category - b.category || a.threshold - b.threshold, +) as Upgrade[]; +const palette = _palette as Palette; + +const rawLevelsList = _rawLevelsList as RawLevel[]; + +export const appVersion = _appVersion as string; + +export const icons = {} as { [k: string]: string }; + +export function transformRawLevel(level: RawLevel) { + const splitBricks = level.bricks.split(""); + const bricks = splitBricks + .map((c) => palette[c]) + .slice(0, level.size * level.size); + const bricksCount = bricks.filter((i) => i).length; + const icon = levelIconHTML(bricks, level.size); + icons[level.name] = icon; + return { + ...level, + bricks, + bricksCount, + icon, + color: automaticBackgroundColor(splitBricks), + svg: getLevelBackground(level), + sortKey: ((Math.random() + 3) / 3.5) * bricksCount, + }; +} + +export const allLevelsAndIcons = rawLevelsList.map( + transformRawLevel, +) as Level[]; + +export const allLevels = allLevelsAndIcons.filter( + (l) => !l.name.startsWith("icon:"), +); diff --git a/src/migrations.ts b/src/migrations.ts new file mode 100644 index 0000000..0bc4fb1 --- /dev/null +++ b/src/migrations.ts @@ -0,0 +1,170 @@ +import { RunHistoryItem } from "./types"; + +import _appVersion from "./data/version.json"; +import { generateSaveFileContent } from "./generateSaveFileContent"; +import { allLevels } from "./loadGameData"; +import { toast } from "./toast"; +import { + isLevelLocked, + reasonLevelIsLocked, +} from "./get_level_unlock_condition"; + +// The page will be reloaded if any migrations were run +let migrationsRun = 0; +function migrate(name: string, cb: () => void) { + if (!localStorage.getItem(name)) { + try { + cb(); + console.debug("Ran migration : " + name); + localStorage.setItem(name, "" + Date.now()); + migrationsRun++; + } catch (e) { + toast((e as Error).message); + console.warn("Migration " + name + " failed : ", e); + } + } +} +function afterMigration() { + // Avoid a boot loop by setting the hash before reloading + // We can't set the query string as it is used for other things + if (migrationsRun && !window.location.hash) { + window.location.hash = "#reloadAfterMigration"; + window.location.reload(); + } + if (!migrationsRun) { + window.location.hash = ""; + } +} + +migrate("save_data_before_upgrade_to_" + _appVersion, () => { + localStorage.setItem( + "recovery_data", + JSON.stringify(generateSaveFileContent()), + ); +}); + +migrate("migrate_high_scores", () => { + const old = localStorage.getItem("breakout-3-hs"); + if (old) { + localStorage.setItem("breakout-3-hs-short", old); + localStorage.removeItem("breakout-3-hs"); + } +}); +migrate("recover_high_scores", () => { + let runsHistory = JSON.parse( + localStorage.getItem("breakout_71_runs_history") || "[]", + ) as RunHistoryItem[]; + runsHistory.forEach((r) => { + const currentHS = parseInt( + localStorage.getItem("breakout-3-hs-" + (r.mode || "short")) || "0", + ); + if (r.score > currentHS) { + localStorage.setItem( + "breakout-3-hs-" + (r.mode || "short"), + "" + r.score, + ); + } + }); +}); + +migrate("remove_long_and_creative_mode_data", () => { + let runsHistory = JSON.parse( + localStorage.getItem("breakout_71_runs_history") || "[]", + ) as RunHistoryItem[]; + + let cleaned = runsHistory.filter((r) => { + if (!r.perks) return false; + if ("mode" in r) { + if (r.mode !== "short") { + return false; + } + } + return true; + }); + if (cleaned.length !== runsHistory.length) + localStorage.setItem("breakout_71_runs_history", JSON.stringify(cleaned)); +}); + +migrate("compact_runs_data_again", () => { + let runsHistory = JSON.parse( + localStorage.getItem("breakout_71_runs_history") || "[]", + ) as RunHistoryItem[]; + runsHistory = runsHistory.filter((r) => { + if (!r.perks) return false; + if ("mode" in r) { + if (r.mode !== "short") { + return false; + } + delete r.mode; + } + + return true; + }); + runsHistory.forEach((r) => { + r.runTime = Math.round(r.runTime); + if (r.perks) { + for (let key in r.perks) { + if (!r.perks[key]) { + delete r.perks[key]; + } + } + } + if ("best_level_score" in r) { + delete r.best_level_score; + } + if ("worst_level_score" in r) { + delete r.worst_level_score; + } + }); + + localStorage.setItem("breakout_71_runs_history", JSON.stringify(runsHistory)); +}); + +migrate("set_breakout_71_unlocked_levels" + _appVersion, () => { + // We want to lock any level unlocked by an app upgrade too + let runsHistory = JSON.parse( + localStorage.getItem("breakout_71_runs_history") || "[]", + ) as RunHistoryItem[]; + + let breakout_71_unlocked_levels = JSON.parse( + localStorage.getItem("breakout_71_unlocked_levels") || "[]", + ) as string[]; + + allLevels + .filter((l, li) => !isLevelLocked(li, l.name, runsHistory)) + .forEach((l) => { + if (!breakout_71_unlocked_levels.includes(l.name)) { + breakout_71_unlocked_levels.push(l.name); + } + }); + localStorage.setItem( + "breakout_71_unlocked_levels", + JSON.stringify(breakout_71_unlocked_levels), + ); +}); + +migrate("clean_ls", () => { + for (let key in localStorage) { + try { + JSON.parse(localStorage.getItem(key) || "null"); + } catch (e) { + localStorage.removeItem(key); + console.warn("Removed invalid key " + key, e); + } + } +}); + +migrate("set_user_id", () => { + // Useful to identify a player when uploading his save file multiple times to a web service + if (!localStorage.getItem("breakout_71_user_id")) { + localStorage.setItem( + "breakout_71_user_id", + JSON.stringify( + (self?.crypto?.randomUUID && self?.crypto?.randomUUID()) || + "user_" + Math.random(), + ), + ); + } +}); + +afterMigration(); diff --git a/src/monitorLevelsUnlocks.ts b/src/monitorLevelsUnlocks.ts new file mode 100644 index 0000000..390c853 --- /dev/null +++ b/src/monitorLevelsUnlocks.ts @@ -0,0 +1,57 @@ +import { GameState, PerkId } from "./types"; +import { getSettingValue, setSettingValue } from "./settings"; +import { allLevels, icons } from "./loadGameData"; + +import { t } from "./i18n/i18n"; +import { toast } from "./toast"; +import { schedulGameSound } from "./gameStateMutators"; +import { getLevelUnlockCondition } from "./get_level_unlock_condition"; + +let list: { + minScore: number; + forbidden: PerkId[]; + required: PerkId[]; +}[]; +let unlocked: Set | null = null; + +export function monitorLevelsUnlocks(gameState: GameState) { + if (!unlocked) { + unlocked = new Set( + getSettingValue("breakout_71_unlocked_levels", []) as string[], + ); + } + + if (gameState.creative) return; + + if (!list) { + list = allLevels.map((l, li) => ({ + name: l.name, + li, + l, + ...getLevelUnlockCondition(li, l.name), + })); + } + + list.forEach(({ name, minScore, forbidden, required, l }) => { + // Already unlocked + if (unlocked!.has(name)) return; + // Score not reached yet + if (gameState.score < minScore) return; + if (!minScore) return; + + if (gameState.score < minScore) return; + // We are missing a required perk + if (required.find((id) => !gameState.perks[id])) return; + // We have a forbidden perk + if (forbidden.find((id) => gameState.perks[id])) return; + // Level just got unlocked + unlocked!.add(name); + setSettingValue( + "breakout_71_unlocked_levels", + getSettingValue("breakout_71_unlocked_levels", []).concat([name]), + ); + + toast(icons[name] + "" + t("unlocks.just_unlocked") + ""); + schedulGameSound(gameState, "colorChange", 0, 1); + }); +} diff --git a/src/newGameState.ts b/src/newGameState.ts new file mode 100644 index 0000000..2990ca7 --- /dev/null +++ b/src/newGameState.ts @@ -0,0 +1,161 @@ +import { GameState, PerkId, RunParams } from "./types"; +import { allLevels, allLevelsAndIcons, upgrades } from "./loadGameData"; +import { + defaultSounds, + getHighScore, + getPossibleUpgrades, + highScoreText, + makeEmptyPerksMap, + sumOfValues, +} from "./game_utils"; +import { resetBalls } from "./gameStateMutators"; +import { isOptionOn } from "./options"; +import { getHistory } from "./gameOver"; +import { getSettingValue, getTotalScore } from "./settings"; +import { isBlackListedForStart, isStartingPerk } from "./startingPerks"; +import { + isLevelLocked, + reasonLevelIsLocked, +} from "./get_level_unlock_condition"; +import { dontOfferTooSoon } from "./openUpgradesPicker"; + +export function getRunLevels( + params: RunParams, + randomGift: PerkId | undefined, +) { + const unlockedBefore = new Set( + getSettingValue("breakout_71_unlocked_levels", []), + ); + + const history = getHistory(); + const unlocked = allLevels.filter( + (l, li) => + unlockedBefore.has(l.name) || !isLevelLocked(li, l.name, history), + ); + const firstLevel = params?.level + ? [params.level] + : allLevelsAndIcons.filter((l) => l.name == "icon:" + randomGift); + + const restInRandomOrder = unlocked + .filter((l) => l.name !== params?.level?.name) + .filter((l) => l.name !== params?.levelToAvoid) + .sort(() => Math.random() - 0.5); + + return firstLevel + .concat( + restInRandomOrder.slice(0, 7 + 3).sort((a, b) => a.sortKey - b.sortKey), + ) + .concat(restInRandomOrder.slice(7 + 3)); +} + +export function newGameState(params: RunParams): GameState { + const highScore = getHighScore(); + + const perks = { ...makeEmptyPerksMap(upgrades), ...(params?.perks || {}) }; + + let randomGift: PerkId | undefined = undefined; + if (!sumOfValues(perks)) { + let giftable = upgrades.filter((u) => isStartingPerk(u)); + if (!giftable.length) { + giftable = upgrades.filter((u) => !isBlackListedForStart(u)); + } + + randomGift = + (isOptionOn("kid") && "slow_down") || + giftable[Math.floor(Math.random() * giftable.length)].id; + + perks[randomGift] = 1; + } + const runLevels = getRunLevels(params, randomGift); + + const gameState: GameState = { + startParams: params, + runLevels, + level: runLevels[0], + currentLevel: 0, + upgradesOfferedFor: -1, + perks, + puckWidth: 200, + baseSpeed: 12, + combo: 1, + lastCombo: 1, + gridSize: 12, + running: false, + isGameOver: false, + ballStickToPuck: true, + puckPosition: 400, + lastPuckPosition: 400, + lastPuckMove: 0, + lastZenComboIncrease: 0, + pauseTimeout: null, + canvasWidth: 0, + canvasHeight: 0, + offsetX: 0, + offsetXRoundedDown: 0, + gameZoneWidth: 0, + gameZoneWidthRoundedUp: 0, + gameZoneHeight: 0, + brickWidth: 0, + score: 0, + lastScoreIncrease: -1000, + levelCoughtCoins: 0, + lastExplosion: -1000, + lastBrickBroken: 0, + highScore, + balls: [], + ballsColor: "#FFFFFF", + bricks: [], + brickHP: [], + lights: { indexMin: 0, total: 0, list: [] }, + particles: { indexMin: 0, total: 0, list: [] }, + texts: { indexMin: 0, total: 0, list: [] }, + coins: { indexMin: 0, total: 0, list: [] }, + respawns: { indexMin: 0, total: 0, list: [] }, + levelStartScore: 0, + levelMisses: 0, + levelSpawnedCoins: 0, + puckColor: "#FFFFFF", + ballSize: 20, + coinSize: 14, + puckHeight: 20, + + pauseUsesDuringRun: 0, + keyboardPuckSpeed: 0, + lastTick: performance.now(), + lastTickDown: 0, + runStatistics: { + started: Date.now(), + levelsPlayed: 0, + runTime: 0, + coins_spawned: 0, + score: 0, + bricks_broken: 0, + misses: 0, + balls_lost: 0, + puck_bounces: 0, + wall_bounces: 0, + upgrades_picked: 1, + max_combo: 1, + }, + lastOffered: {}, + levelTime: 0, + winAt: 0, + levelWallBounces: 0, + needsRender: true, + autoCleanUses: 0, + ...defaultSounds(), + rerolls: 0, + creative: + params?.computer_controlled || + sumOfValues(params.perks) > 1 || + (params.level && !params.level.name.startsWith("icon:")), + }; + resetBalls(gameState); + + for (let perk of upgrades) { + if (perks[perk.id]) { + dontOfferTooSoon(gameState, perk.id); + } + } + return gameState; +} diff --git a/src/openScorePanel.ts b/src/openScorePanel.ts new file mode 100644 index 0000000..0a89f2e --- /dev/null +++ b/src/openScorePanel.ts @@ -0,0 +1,114 @@ +import { GameState, PerkId } from "./types"; +import { asyncAlert } from "./asyncAlert"; +import { t } from "./i18n/i18n"; +import { levelsListHTMl, max_levels, pickedUpgradesHTMl } from "./game_utils"; +import { getCreativeModeWarning, getHistory } from "./gameOver"; +import { pause } from "./game"; +import { allLevels, icons, upgrades } from "./loadGameData"; +import { firstWhere } from "./pure_functions"; +import { getSettingValue, getTotalScore } from "./settings"; +import { + getLevelUnlockCondition, + reasonLevelIsLocked, + upgradeName, +} from "./get_level_unlock_condition"; +import { isOptionOn } from "./options"; + +export async function openScorePanel(gameState: GameState) { + pause(true); + + await asyncAlert({ + title: t("score_panel.title", { + score: gameState.score, + level: gameState.currentLevel + 1, + max: max_levels(gameState), + }), + + content: [ + getCreativeModeWarning(gameState), + pickedUpgradesHTMl(gameState), + levelsListHTMl(gameState, gameState.currentLevel), + getNearestUnlockHTML(gameState), + gameState.rerolls + ? t("score_panel.upgrade_point_count", { + count: gameState.rerolls, + }) + : "", + ], + allowClose: true, + }); +} + +export function getFirstUnlockable(gameState: GameState) { + if (gameState.creative) return undefined; + const unlocked = new Set(getSettingValue("breakout_71_unlocked_levels", [])); + return firstWhere(allLevels, (l, li) => { + if (unlocked.has(l.name)) return; + const reason = reasonLevelIsLocked(li, l.name, getHistory(), false); + if (!reason) return; + + const { minScore, forbidden, required } = getLevelUnlockCondition( + li, + l.name, + ); + const missing: PerkId[] = required.filter((id) => !gameState?.perks?.[id]); + // we can't have a forbidden perk + if (forbidden.find((id) => gameState?.perks?.[id])) { + return; + } + + // All required upgrades need to be unlocked + if ( + missing.find( + (id) => upgrades.find((u) => u.id === id)!.threshold > getTotalScore(), + ) + ) { + return; + } + + return { + l, + li, + minScore, + forbidden, + required, + missing, + reason, + }; + }); +} + +export function getNearestUnlockHTML(gameState: GameState) { + if (!isOptionOn("level_unlocks_hints")) return ""; + const firstUnlockable = getFirstUnlockable(gameState); + + if (!firstUnlockable) return ""; + let missingPoints = Math.max(0, firstUnlockable.minScore - gameState.score); + let missingUpgrades = firstUnlockable.missing + .map((id) => upgradeName(id)) + .join(", "); + + const title = + (missingUpgrades && + t("score_panel.get_upgrades_to_unlock", { + missingUpgrades, + points: missingPoints, + level: firstUnlockable.l.name, + })) || + t("score_panel.score_to_unlock", { + points: missingPoints, + level: firstUnlockable.l.name, + }); + + return ` +

${t("score_panel.close_to_unlock")}

+
+ ${icons[firstUnlockable.l.name]} +

+ ${title} + ${firstUnlockable.reason?.text} +

+
+ + `; +} diff --git a/src/openUpgradesPicker.ts b/src/openUpgradesPicker.ts new file mode 100644 index 0000000..86f14c3 --- /dev/null +++ b/src/openUpgradesPicker.ts @@ -0,0 +1,211 @@ +import { GameState, PerkId } from "./types"; +import { + catchRateBest, + catchRateGood, + choicePerGold, + choicePerSilver, + levelTimeBest, + levelTimeGood, + missesBest, + missesGood, + upPerGold, + upPerSilver, +} from "./pure_functions"; +import { t } from "./i18n/i18n"; +import { icons } from "./loadGameData"; +import { requiredAsyncAlert } from "./asyncAlert"; +import { + escapeAttribute, + getPossibleUpgrades, + levelsListHTMl, + max_levels, + pickedUpgradesHTMl, + upgradeLevelAndMaxDisplay, +} from "./game_utils"; +import { getFirstUnlockable, getNearestUnlockHTML } from "./openScorePanel"; +import { isOptionOn } from "./options"; + +export async function openUpgradesPicker(gameState: GameState) { + const catchRate = + gameState.levelCoughtCoins / (gameState.levelSpawnedCoins || 1); + + let medals = []; + let upgradePoints = 1; + let extraChoices = 0; + + let hasMedals = 0; + + function challengeResult( + name: String, + description: String, + medal: "gold" | "silver" | "no", + ) { + let choices = 0, + up = 0; + if (medal === "gold") { + choices += choicePerGold; + up += upPerGold; + } else if (medal === "silver") { + choices += choicePerSilver; + up += upPerSilver; + } + if (medal !== "no") { + hasMedals++; + } + + extraChoices += choices; + upgradePoints += up; + medals.push(`
+ ${icons["icon:" + medal + "_medal"]} +

+ ${name}
+ ${ + up || choices + ? t("level_up.challenges.gain", { up, choices }) + : t("level_up.challenges.no_gain") + } + +

+
`); + } + + challengeResult( + t("level_up.challenges.levelTime.name", { + value: Math.ceil(gameState.levelTime / 1000), + }), + t("level_up.challenges.levelTime.description", { + silver: levelTimeGood, + gold: levelTimeBest, + }), + (gameState.levelTime < levelTimeBest * 1000 && "gold") || + (gameState.levelTime < levelTimeGood * 1000 && "silver") || + "no", + ); + + challengeResult( + t("level_up.challenges.catchRateGood.name", { + value: Math.floor(catchRate * 100), + caught: gameState.levelCoughtCoins, + total: gameState.levelSpawnedCoins, + }), + t("level_up.challenges.catchRateGood.description", { + silver: catchRateGood, + gold: catchRateBest, + }), + (catchRate > catchRateBest / 100 && "gold") || + (catchRate > catchRateGood / 100 && "silver") || + "no", + ); + + challengeResult( + gameState.levelMisses + ? t("level_up.challenges.levelMisses.name", { + value: gameState.levelMisses, + }) + : t("level_up.challenges.levelMisses.none"), + t("level_up.challenges.levelMisses.description", { + silver: missesGood, + gold: missesBest, + }), + (gameState.levelMisses < missesBest && "gold") || + (gameState.levelMisses < missesGood && "silver") || + "no", + ); + + if (hasMedals == 0) { + medals.length = 0; + } else if (hasMedals == 1) { + medals.unshift(t("level_up.challenges.earned_medal", { count: hasMedals })); + } else { + medals.unshift( + t("level_up.challenges.earned_medal_plural", { count: hasMedals }), + ); + } + + let sorted = getPossibleUpgrades(gameState) + .map((u) => ({ + ...u, + score: Math.random() + (gameState.lastOffered[u.id] || 0), + })) + .sort((a, b) => a.score - b.score) + .filter((u) => gameState.perks[u.id] < u.max + gameState.perks.limitless); + + while (true) { + // refresh the list if you pick extra one_more_choice + const offered = sorted.slice( + 0, + 3 + extraChoices + gameState.perks.one_more_choice, + ); + offered.forEach((u) => { + dontOfferTooSoon(gameState, u.id); + }); + + const unlockable = getFirstUnlockable(gameState); + let unlockRelatedUpgradesOffered = 0; + + const upgradesActions = offered.map((u) => { + let unlockHint = ""; + let className = ""; + if (isOptionOn("level_unlocks_hints")) { + if (unlockable?.forbidden?.includes(u.id) && !gameState.perks[u.id]) { + unlockRelatedUpgradesOffered++; + className += " forbidden"; + unlockHint = t("level_up.forbidden", { + levelName: unlockable?.l.name || "", + }); + } + if (unlockable?.required?.includes(u.id)) { + unlockRelatedUpgradesOffered++; + className += " required"; + unlockHint = t("level_up.required", { + levelName: unlockable?.l.name || "", + }); + } + } + return { + value: u.id, + disabled: gameState.perks[u.id] >= u.max + gameState.perks.limitless, + text: + u.name + + (gameState.perks[u.id] + ? upgradeLevelAndMaxDisplay(u, gameState) + : ""), + icon: icons["icon:" + u.id], + help: u.help(gameState.perks[u.id] || 1), + tooltip: unlockHint + u.fullHelp(gameState.perks[u.id] || 1), + className, + actionLabel: gameState.perks[u.id] ? "upgrade" : "pick", + }; + }); + + const upgradeId = await requiredAsyncAlert({ + title: t("level_up.title", { + level: gameState.currentLevel, + max: max_levels(gameState), + }), + content: [ + t("level_up.upgrade_perks", { + coins: gameState.levelCoughtCoins, + count: upgradePoints, + }), + ...upgradesActions, + levelsListHTMl(gameState, gameState.currentLevel), + unlockRelatedUpgradesOffered ? getNearestUnlockHTML(gameState) : "", + + ...medals, + pickedUpgradesHTMl(gameState), + `
`, + ], + }); + upgradePoints--; + gameState.perks[upgradeId]++; + gameState.runStatistics.upgrades_picked++; + if (!upgradePoints) { + return; + } + } +} + +export function dontOfferTooSoon(gameState: GameState, id: PerkId) { + gameState.lastOffered[id] = Math.round(Date.now() / 1000); +} diff --git a/src/options.ts b/src/options.ts new file mode 100644 index 0000000..8218714 --- /dev/null +++ b/src/options.ts @@ -0,0 +1,114 @@ +import { t } from "./i18n/i18n"; + +import { OptionDef, OptionId } from "./types"; +import { getSettingValue, setSettingValue } from "./settings"; + +import { getHighScore, hoursSpentPlaying } from "./game_utils"; + +export const options = { + sound: { + default: true, + name: t("settings.sounds"), + help: t("settings.sounds_help"), + }, + "mobile-mode": { + default: + window.innerHeight > window.innerWidth || + "ontouchstart" in window || + navigator.maxTouchPoints > 0, + name: t("settings.mobile"), + help: t("settings.mobile_help"), + }, + touch_delayed_start: { + default: true, + name: t("settings.touch_delayed_start"), + help: t("settings.touch_delayed_start_help"), + }, + basic: { + default: false, + name: t("settings.basic"), + help: t("settings.basic_help"), + }, + colorful_coins: { + default: false, + name: t("settings.colorful_coins"), + help: t("settings.colorful_coins_help"), + }, + extra_bright: { + default: true, + name: t("settings.extra_bright"), + help: t("settings.extra_bright_help"), + }, + smooth_lighting: { + default: true, + name: t("settings.smooth_lighting"), + help: t("settings.smooth_lighting_help"), + }, + precise_lighting: { + default: true, + name: t("settings.precise_lighting"), + help: t("settings.precise_lighting_help"), + }, + probabilistic_lighting: { + default: false, + name: t("settings.probabilistic_lighting"), + help: t("settings.probabilistic_lighting_help"), + }, + contrast: { + default: false, + name: t("settings.contrast"), + help: t("settings.contrast_help"), + }, + show_fps: { + default: false, + name: t("settings.show_fps"), + help: t("settings.show_fps_help"), + }, + show_stats: { + default: false, + name: t("settings.show_stats"), + help: t("settings.show_stats_help"), + }, + pointerLock: { + default: false, + name: t("settings.pointer_lock"), + help: t("settings.pointer_lock_help"), + }, + kid: { + default: false, + name: t("settings.kid"), + help: t("settings.kid_help"), + }, + // Could not get the sharing to work without loading androidx and all the modern android things so for now I'll just disable sharing in the android app + record: { + default: false, + name: t("settings.record"), + help: t("settings.record_help"), + }, + fullscreen: { + default: false, + name: t("settings.fullscreen"), + help: t("settings.fullscreen_help"), + }, + donation_reminder: { + default: hoursSpentPlaying() > 5, + name: t("settings.donation_reminder"), + help: t("settings.donation_reminder_help"), + }, + level_unlocks_hints: { + default: getHighScore() > 1000, + name: t("settings.level_unlocks_hints"), + help: t("settings.level_unlocks_hints_help"), + }, +} as const satisfies { [k: string]: OptionDef }; + +export function isOptionOn(key: OptionId) { + return getSettingValue( + "breakout-settings-enable-" + key, + options[key]?.default, + ); +} + +export function toggleOption(key: OptionId) { + setSettingValue("breakout-settings-enable-" + key, !isOptionOn(key)); +} diff --git a/src/pure_functions.ts b/src/pure_functions.ts new file mode 100644 index 0000000..a85c72d --- /dev/null +++ b/src/pure_functions.ts @@ -0,0 +1,148 @@ +import { Ball, GameState } from "./types"; + +export function clamp(value: number, min: number, max: number) { + return Math.max(min, Math.min(value, max)); +} + +export function ballTransparency(ball: Ball, gameState: GameState) { + if (!gameState.perks.transparency) return 0; + return clamp( + gameState.perks.transparency * + (1 - (ball.y / gameState.gameZoneHeight) * 1.2), + 0, + 1, + ); +} + +export function coinsBoostedCombo(gameState: GameState) { + let boost = + 1 + + gameState.perks.sturdy_bricks / 2 + + gameState.perks.smaller_puck / 2 + + gameState.perks.transparency / 2; + + if (gameState.perks.minefield) { + gameState.bricks.forEach((brick) => { + if (brick === "black") { + boost += 0.1 * gameState.perks.minefield; + } + }); + } + return Math.ceil(Math.max(gameState.combo, gameState.lastCombo) * boost); +} + +export function miniMarkDown(md: string) { + let html: { tagName: string; text: string }[] = []; + let lastNode: { tagName: string; text: string } | null = null; + + md.split("\n").forEach((line) => { + const titlePrefix = line.match(/^#+ /)?.[0]; + + if (titlePrefix) { + if (lastNode) html.push(lastNode); + lastNode = { + tagName: "h" + (titlePrefix.length - 1), + text: line.slice(titlePrefix.length), + }; + } else if (line.startsWith("- ")) { + if (lastNode?.tagName !== "ul") { + if (lastNode) html.push(lastNode); + lastNode = { tagName: "ul", text: "" }; + } + lastNode.text += "
  • " + line.slice(2) + "
  • "; + } else if (!line.trim()) { + if (lastNode) html.push(lastNode); + lastNode = null; + } else { + if (lastNode?.tagName !== "p") { + if (lastNode) html.push(lastNode); + lastNode = { tagName: "p", text: "" }; + } + lastNode.text += line + " "; + } + }); + if (lastNode) { + html.push(lastNode); + } + return html + .map( + (h) => + "<" + + h.tagName + + ">" + + h.text.replace( + /\bhttps?:\/\/[^\s<>]+/gi, + (a) => `${a}`, + ) + + "", + ) + .join("\n"); +} + +export function firstWhere( + arr: Input[], + mapper: (item: Input, index: number) => Output | undefined, +): Output | undefined { + for (let i = 0; i < arr.length; i++) { + const result = mapper(arr[i], i); + if (typeof result !== "undefined") return result; + } +} + +export const levelTimeBest = 25, + levelTimeGood = 45, + catchRateBest = 98, + catchRateGood = 90, + missesBest = 1, + missesGood = 6, + choicePerSilver = 1, + choicePerGold = 3, + upPerSilver = 1, + upPerGold = 1; + +export const MAX_LEVEL_SIZE = 21; +export const MIN_LEVEL_SIZE = 2; + +export function automaticBackgroundColor(bricks: string[]) { + return bricks.filter((b) => b === "g").length > + bricks.filter((b) => b !== "_").length * 0.05 + ? "#115988" + : "#000000"; +} + +export function levelCodeToRawLevel(code: string) { + let [name, credit] = code.match(/\[([^\]]+)]/gi) || ["", ""]; + + let bricks = code.split(name)[1].split(credit)[0].replace(/\s/gi, ""); + name = name.slice(1, -1); + credit = credit.slice(1, -1); + name ||= "Imported on " + new Date().toISOString().slice(0, 10); + credit ||= ""; + const size = Math.sqrt(bricks.length); + if ( + Math.floor(size) === size && + size >= MIN_LEVEL_SIZE && + size <= MAX_LEVEL_SIZE + ) + return { + color: automaticBackgroundColor(bricks.split("")), + size, + bricks, + name, + credit, + }; +} + +export function comboKeepingRate(level: number) { + return clamp(1 - (1 / (1 + level)) * 1.5, 0, 1); +} + +export function base_combo_from_stronger_foundation(perkLevel: number) { + let base = 1; + for (let i = 0; i < perkLevel; i++) { + base += 3 + i; + } + return base; +} diff --git a/src/recording.ts b/src/recording.ts new file mode 100644 index 0000000..2e17ed9 --- /dev/null +++ b/src/recording.ts @@ -0,0 +1,195 @@ +import { gameCanvas } from "./render"; +import { isInWebView, max_levels } from "./game_utils"; +import { getAudioRecordingTrack } from "./sounds"; +import { t } from "./i18n/i18n"; +import { GameState } from "./types"; +import { isOptionOn } from "./options"; +import { toast } from "./toast"; + +let mediaRecorder: MediaRecorder | null, + captureStream: MediaStream, + captureTrack: CanvasCaptureMediaStreamTrack, + recordCanvas: HTMLCanvasElement, + recordCanvasCtx: CanvasRenderingContext2D; + +export function recordOneFrame(gameState: GameState) { + if (!isOptionOn("record")) { + return; + } + // if (!gameState.running) return; + if (!captureStream) return; + drawMainCanvasOnSmallCanvas(gameState); + if (captureTrack?.requestFrame) { + captureTrack?.requestFrame(); + } else if (captureStream?.requestFrame) { + captureStream.requestFrame(); + } +} + +export function drawMainCanvasOnSmallCanvas(gameState: GameState) { + if (!recordCanvasCtx) return; + recordCanvasCtx.drawImage( + gameCanvas, + gameState.offsetXRoundedDown, + 0, + gameState.gameZoneWidthRoundedUp, + gameState.gameZoneHeight, + 0, + 0, + recordCanvas.width, + recordCanvas.height, + ); + + // Here we don't use drawText as we don't want to cache a picture for each distinct value of score + recordCanvasCtx.fillStyle = "#FFFFFF"; + recordCanvasCtx.textBaseline = "top"; + recordCanvasCtx.font = "12px monospace"; + recordCanvasCtx.textAlign = "right"; + recordCanvasCtx.fillText( + gameState.score.toString(), + recordCanvas.width - 12, + 12, + ); + + recordCanvasCtx.textAlign = "left"; + recordCanvasCtx.fillText( + "Level " + (gameState.currentLevel + 1) + "/" + max_levels(gameState), + 12, + 12, + ); +} + +export function startRecordingGame(gameState: GameState) { + if (!isOptionOn("record")) { + return; + } + if (mediaRecorder) return; + if (!recordCanvas) { + // Smaller canvas with fewer details + recordCanvas = document.createElement("canvas"); + recordCanvasCtx = recordCanvas.getContext("2d", { + antialias: false, + alpha: false, + }) as CanvasRenderingContext2D; + + captureStream = recordCanvas.captureStream(0); + captureTrack = + captureStream.getVideoTracks()[0] as CanvasCaptureMediaStreamTrack; + + const track = getAudioRecordingTrack(); + if (track) { + captureStream.addTrack(track.stream.getAudioTracks()[0]); + } + } + + recordCanvas.width = gameState.gameZoneWidthRoundedUp; + recordCanvas.height = gameState.gameZoneHeight; + + // drawMainCanvasOnSmallCanvas() + const recordedChunks: Blob[] = []; + + const instance = new MediaRecorder(captureStream, { + // Required for less mushy result + videoBitsPerSecond: 3500000, + }); + mediaRecorder = instance; + instance.start(); + mediaRecorder.pause(); + instance.ondataavailable = function (event) { + recordedChunks.push(event.data); + }; + + instance.onstop = async function () { + let targetDiv: HTMLElement | null; + let blob = new Blob(recordedChunks, { type: "video/webm" }); + if (blob.size < 200000) return; // under 0.2MB, probably bugged out or pointlessly short + + while ( + !(targetDiv = document.getElementById("level-recording-container")) + ) { + await new Promise((r) => setTimeout(r, 200)); + } + + const video = document.createElement("video"); + video.autoplay = true; + video.controls = false; + video.disablePictureInPicture = true; + video.disableRemotePlayback = true; + video.width = recordCanvas.width; + video.height = recordCanvas.height; + video.loop = true; + video.muted = true; + video.playsInline = true; + + video.src = URL.createObjectURL(blob); + targetDiv.appendChild(video); + + const a = document.createElement("a"); + a.download = captureFileName("webm"); + a.target = "_blank"; + + if (isInWebView) { + a.href = await blobToBase64(blob); + } else { + a.href = video.src; + } + a.textContent = t("settings.record_download", { + size: (blob.size / 1000000).toFixed(2), + }); + targetDiv.appendChild(a); + }; +} + +function blobToBase64(blob: Blob): Promise { + return new Promise((resolve, reject) => { + let reader = new FileReader(); + + reader.onload = function () { + resolve(reader.result); + }; + reader.onerror = function () { + const e = reader.error; + console.error(e); + toast(e?.message || "Failed to convert the video to a data url"); + reject(new Error("Failed to readAsDataURL of the video ")); + }; + + reader.readAsDataURL(blob); + }); +} + +export function pauseRecording() { + if (!isOptionOn("record")) { + return; + } + if (mediaRecorder?.state === "recording") { + mediaRecorder?.pause(); + } +} + +export function resumeRecording() { + if (!isOptionOn("record")) { + return; + } + if (mediaRecorder?.state === "paused") { + mediaRecorder.resume(); + } +} + +export function stopRecording() { + if (!isOptionOn("record")) { + return; + } + if (!mediaRecorder) return; + mediaRecorder?.stop(); + mediaRecorder = null; +} + +export function captureFileName(ext = "webm") { + return ( + "breakout-71-capture-" + + new Date().toISOString().replace(/[^0-9\-]+/gi, "-") + + "." + + ext + ); +} diff --git a/src/render.ts b/src/render.ts new file mode 100644 index 0000000..e13b34d --- /dev/null +++ b/src/render.ts @@ -0,0 +1,1250 @@ +import { baseCombo, forEachLiveOne, liveCount } from "./gameStateMutators"; +import { + brickCenterX, + brickCenterY, + currentLevelInfo, + getCoinRenderColor, + getCornerOffset, + isMovingWhilePassiveIncome, + isPickyEatingPossible, + max_levels, + reachRedRowIndex, + telekinesisEffectRate, + yoyoEffectRate, + zoneLeftBorderX, + zoneRightBorderX, +} from "./game_utils"; +import { colorString, GameState } from "./types"; +import { t } from "./i18n/i18n"; +import { gameState, lastMeasuredFPS, startWork } from "./game"; +import { isOptionOn } from "./options"; +import { + ballTransparency, + catchRateBest, + catchRateGood, + clamp, + coinsBoostedCombo, + levelTimeBest, + levelTimeGood, + missesBest, + missesGood, +} from "./pure_functions"; + +export const gameCanvas = document.getElementById("game") as HTMLCanvasElement; +export const ctx = gameCanvas.getContext("2d", { + alpha: false, +}) as CanvasRenderingContext2D; + +export const bombSVG = document.createElement("img"); +bombSVG.src = + "data:image/svg+xml;base64," + + btoa(` + +`); +bombSVG.onload = () => (gameState.needsRender = true); + +export const background = document.createElement("img"); +background.onload = () => (gameState.needsRender = true); +export const backgroundCanvas = document.createElement("canvas"); + +export const haloCanvas = document.createElement("canvas"); +const haloCanvasCtx = haloCanvas.getContext("2d", { + alpha: false, +}) as CanvasRenderingContext2D; + +export function getHaloScale() { + return 16 * (isOptionOn("precise_lighting") ? 1 : 2); +} + +let framesCounter = 0; + +export function render(gameState: GameState) { + framesCounter++; + startWork("render:init"); + const level = currentLevelInfo(gameState); + + const hasCombo = gameState.combo > baseCombo(gameState); + const { width, height } = gameCanvas; + if (!width || !height) return; + + if (gameState.currentLevel || gameState.levelTime) { + menuLabel.innerText = t("play.current_lvl", { + level: gameState.currentLevel + 1, + max: max_levels(gameState), + }); + } else { + menuLabel.innerText = t("play.menu_label"); + } + + const catchRate = gameState.levelSpawnedCoins + ? gameState.levelCoughtCoins / (gameState.levelSpawnedCoins || 1) + : // (gameState.levelSpawnedCoins - gameState.levelLostCoins) / + // gameState.levelSpawnedCoins + 1; + startWork("render:scoreDisplay"); + scoreDisplay.innerHTML = + (isOptionOn("show_fps") || gameState.startParams.computer_controlled + ? ` + + ${lastMeasuredFPS} FPS + / + ` + : "") + + (isOptionOn("show_stats") + ? ` + catchRateGood / 100 && "good") || ""}" data-tooltip="${t("play.stats.coins_catch_rate")}"> + ${Math.floor(catchRate * 100)}% + / + + ${Math.ceil(gameState.levelTime / 1000)}s + / + + ${gameState.levelMisses} M + / + ` + : "") + + `$${gameState.score}`; + + scoreDisplay.classList[ + gameState.startParams.computer_controlled ? "add" : "remove" + ]("computer_controlled"); + scoreDisplay.classList[ + gameState.lastScoreIncrease > gameState.levelTime - 500 ? "add" : "remove" + ]("active"); + + // Clear + if (!isOptionOn("basic") && level.svg && level.color === "#000000") { + const skipN = + isOptionOn("probabilistic_lighting") && liveCount(gameState.coins) > 150 + ? 3 + : 0; + const shouldSkip = (index) => + skipN ? (framesCounter + index) % (skipN + 1) !== 0 : false; + + const haloScale = getHaloScale(); + startWork("render:halo:clear"); + + haloCanvasCtx.globalCompositeOperation = "source-over"; + haloCanvasCtx.globalAlpha = skipN ? 0.1 : 0.99; + haloCanvasCtx.fillStyle = level.color; + haloCanvasCtx.fillRect(0, 0, width / haloScale, height / haloScale); + + const brightness = isOptionOn("extra_bright") ? 3 : 1; + haloCanvasCtx.globalCompositeOperation = "lighten"; + haloCanvasCtx.globalAlpha = + 0.1 + (0.5 * 10) / (liveCount(gameState.coins) + 10); + startWork("render:halo:coins"); + forEachLiveOne(gameState.coins, (coin, index) => { + if (shouldSkip(index)) return; + const color = getCoinRenderColor(gameState, coin); + drawFuzzyBall( + haloCanvasCtx, + color, + (gameState.coinSize * 2 * brightness) / haloScale, + coin.x / haloScale, + coin.y / haloScale, + ); + }); + + startWork("render:halo:balls"); + gameState.balls.forEach((ball, index) => { + if (shouldSkip(index)) return; + haloCanvasCtx.globalAlpha = 0.3 * (1 - ballTransparency(ball, gameState)); + drawFuzzyBall( + haloCanvasCtx, + gameState.ballsColor, + (gameState.ballSize * 2 * brightness) / haloScale, + ball.x / haloScale, + ball.y / haloScale, + ); + }); + + startWork("render:halo:bricks"); + haloCanvasCtx.globalAlpha = 0.05; + gameState.bricks.forEach((color, index) => { + if (!color) return; + if (shouldSkip(index)) return; + const x = brickCenterX(gameState, index), + y = brickCenterY(gameState, index); + drawFuzzyBall( + haloCanvasCtx, + color == "black" ? "#666666" : color, + // Perf could really go down there because of the size of the halo + Math.min(200, gameState.brickWidth * 1.5 * brightness) / haloScale, + x / haloScale, + y / haloScale, + ); + }); + + startWork("render:halo:particles"); + haloCanvasCtx.globalCompositeOperation = "screen"; + forEachLiveOne(gameState.particles, (flash, index) => { + if (shouldSkip(index)) return; + const { x, y, time, color, size, duration } = flash; + const elapsed = gameState.levelTime - time; + haloCanvasCtx.globalAlpha = + 0.1 * Math.min(1, 2 - (elapsed / duration) * 2); + drawFuzzyBall( + haloCanvasCtx, + color, + (size * 3 * brightness) / haloScale, + x / haloScale, + y / haloScale, + ); + }); + + startWork("render:halo:scale_up"); + ctx.globalAlpha = 1; + ctx.globalCompositeOperation = "source-over"; + + ctx.imageSmoothingQuality = "high"; + ctx.imageSmoothingEnabled = isOptionOn("smooth_lighting") || false; + ctx.drawImage(haloCanvas, 0, 0, width, height); + ctx.imageSmoothingEnabled = false; + + startWork("render:halo:pattern"); + ctx.globalAlpha = 1; + ctx.globalCompositeOperation = "multiply"; + if (level.svg && background.width && background.complete) { + if (backgroundCanvas.title !== level.name) { + backgroundCanvas.title = level.name; + backgroundCanvas.width = gameState.canvasWidth; + backgroundCanvas.height = gameState.canvasHeight; + const bgctx = backgroundCanvas.getContext( + "2d", + ) as CanvasRenderingContext2D; + + bgctx.globalCompositeOperation = "source-over"; + bgctx.fillStyle = level.color || "#000"; + bgctx.fillRect(0, 0, gameState.canvasWidth, gameState.canvasHeight); + if (gameState.perks.clairvoyant >= 3) { + const pageSource = document.body.innerHTML.replace(/\s+/gi, ""); + const lineWidth = Math.ceil(gameState.canvasWidth / 15); + const lines = Math.ceil(gameState.canvasHeight / 20); + const chars = lineWidth * lines; + let start = Math.ceil(Math.random() * (pageSource.length - chars)); + for (let i = 0; i < lines; i++) { + bgctx.fillStyle = "#FFFFFF"; + bgctx.font = "20px Courier"; + bgctx.fillText( + pageSource.slice( + start + i * lineWidth, + start + (i + 1) * lineWidth, + ), + 0, + i * 20, + gameState.canvasWidth, + ); + } + } else { + const pattern = ctx.createPattern(background, "repeat"); + if (pattern) { + bgctx.globalCompositeOperation = "screen"; + bgctx.fillStyle = pattern; + bgctx.fillRect(0, 0, width, height); + } + } + } + + ctx.globalCompositeOperation = "darken"; + ctx.drawImage(backgroundCanvas, 0, 0); + } else { + // Background not loaded yes + ctx.fillStyle = "#000"; + ctx.fillRect(0, 0, width, height); + } + } else { + startWork("render:halo-basic"); + ctx.globalAlpha = 1; + ctx.globalCompositeOperation = "source-over"; + ctx.fillStyle = level.color || "#000"; + ctx.fillRect(0, 0, width, height); + forEachLiveOne(gameState.particles, (flash) => { + const { x, y, time, color, size, duration } = flash; + const elapsed = gameState.levelTime - time; + ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2); + drawBall(ctx, color, size, x, y); + }); + } + + startWork("render:explosionshake"); + ctx.globalAlpha = 1; + ctx.globalCompositeOperation = "source-over"; + const lastExplosionDelay = gameState.levelTime - gameState.lastExplosion + 5; + + const shaked = + lastExplosionDelay < 200 && + !isOptionOn("basic") && + // Otherwise, if you pause after an explosion, moving the mouses shakes the picture + gameState.running; + if (shaked) { + const amplitude = + ((gameState.perks.bigger_explosions + 1) * 50) / lastExplosionDelay; + ctx.translate( + Math.sin(Date.now()) * amplitude, + Math.sin(Date.now() + 36) * amplitude, + ); + } + startWork("render:coins"); + // Coins + ctx.globalAlpha = 1; + forEachLiveOne(gameState.coins, (coin) => { + const color = getCoinRenderColor(gameState, coin); + const hollow = gameState.perks.metamorphosis && !coin.metamorphosisPoints; + + ctx.globalCompositeOperation = "source-over"; + drawCoin( + ctx, + hollow ? "transparent" : color, + coin.size, + coin.x, + coin.y, + // Red border around coins with asceticism + (hasCombo && gameState.perks.asceticism && "#FF0000") || + // Gold coins + // (color === "#ffd300" && "#ffd300") || + (hollow && color) || + gameState.level.color, + coin.a, + ); + }); + startWork("render:ball shade"); + // Black shadow around balls + ctx.globalCompositeOperation = "source-over"; + gameState.balls.forEach((ball) => { + ctx.globalAlpha = + Math.min(0.8, liveCount(gameState.coins) / 20) * + (1 - ballTransparency(ball, gameState)); + + drawBall( + ctx, + level.color || "#000", + gameState.ballSize * 6, + ball.x, + ball.y, + ); + }); + startWork("render:bricks"); + ctx.globalCompositeOperation = "source-over"; + renderAllBricks(); + + startWork("render:lights"); + ctx.globalCompositeOperation = "screen"; + forEachLiveOne(gameState.lights, (flash) => { + const { x, y, time, color, size, duration } = flash; + const elapsed = gameState.levelTime - time; + ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2) * 0.5; + drawBrick( + gameState, + ctx, + color, + x, + y, + -1, + gameState.perks.clairvoyant >= 2, + ); + }); + + startWork("render:texts"); + ctx.globalCompositeOperation = "screen"; + forEachLiveOne(gameState.texts, (flash) => { + const { x, y, time, color, size, duration } = flash; + const elapsed = gameState.levelTime - time; + ctx.globalAlpha = Math.max(0, Math.min(1, 2 - (elapsed / duration) * 2)); + ctx.globalCompositeOperation = "source-over"; + drawText(ctx, flash.text, color, size, x, y - elapsed / 10); + }); + + startWork("render:particles"); + forEachLiveOne(gameState.particles, (particle) => { + const { x, y, time, color, size, duration } = particle; + const elapsed = gameState.levelTime - time; + ctx.globalAlpha = Math.max(0, Math.min(1, 2 - (elapsed / duration) * 2)); + ctx.globalCompositeOperation = "screen"; + drawBall(ctx, color, size, x, y); + }); + // + startWork("render:extra_life"); + if (gameState.perks.extra_life) { + ctx.globalAlpha = gameState.balls.length > 1 ? 0.2 : 1; + ctx.globalCompositeOperation = "source-over"; + ctx.fillStyle = gameState.puckColor; + for (let i = 0; i < gameState.perks.extra_life; i++) { + ctx.fillRect( + gameState.offsetXRoundedDown, + gameState.gameZoneHeight - gameState.puckHeight / 2 + 2 * i, + gameState.gameZoneWidthRoundedUp, + 1, + ); + } + } + + startWork("render:balls"); + ctx.globalAlpha = 1; + ctx.globalCompositeOperation = "source-over"; + gameState.balls.forEach((ball) => { + const drawingColor = gameState.ballsColor; + const ballAlpha = 1 - ballTransparency(ball, gameState); + ctx.globalAlpha = ballAlpha; + // The white border around is to distinguish colored balls from coins/bg + drawBall( + ctx, + drawingColor, + gameState.ballSize, + ball.x, + ball.y, + gameState.puckColor, + ); + + if ( + telekinesisEffectRate(gameState, ball) || + yoyoEffectRate(gameState, ball) + ) { + ctx.beginPath(); + ctx.moveTo(gameState.puckPosition, gameState.gameZoneHeight); + ctx.globalAlpha = clamp( + Math.max( + telekinesisEffectRate(gameState, ball), + yoyoEffectRate(gameState, ball), + ) * ballAlpha, + 0, + 1, + ); + ctx.strokeStyle = gameState.puckColor; + ctx.bezierCurveTo( + gameState.puckPosition, + gameState.gameZoneHeight, + gameState.puckPosition, + ball.y, + ball.x, + ball.y, + ); + ctx.stroke(); + + ctx.lineWidth = 2; + ctx.setLineDash(emptyArray); + } + ctx.globalAlpha = 1; + if ( + (gameState.perks.clairvoyant && gameState.ballStickToPuck) || + (gameState.perks.steering > 1 && !gameState.ballStickToPuck) + ) { + ctx.strokeStyle = gameState.ballsColor; + ctx.beginPath(); + ctx.moveTo(ball.x, ball.y); + ctx.lineTo(ball.x + ball.vx * 10, ball.y + ball.vy * 10); + ctx.stroke(); + } + }); + + startWork("render:puck"); + ctx.globalAlpha = isMovingWhilePassiveIncome(gameState) ? 0.2 : 1; + ctx.globalCompositeOperation = "source-over"; + drawPuck( + ctx, + gameState.puckColor, + gameState.puckWidth, + gameState.puckHeight, + 0, + gameState.perks.concave_puck, + gameState.perks.streak_shots && hasCombo ? getDashOffset(gameState) : -1, + ); + + startWork("render:combotext"); + + const spawns = coinsBoostedCombo(gameState); + if (spawns > 1 && !isMovingWhilePassiveIncome(gameState)) { + ctx.globalCompositeOperation = "source-over"; + ctx.globalAlpha = 1; + + const comboText = spawns.toString(); + const comboTextWidth = (comboText.length * gameState.puckHeight) / 1.8; + const totalWidth = comboTextWidth + gameState.coinSize * 2; + const left = gameState.puckPosition - totalWidth / 2; + + ctx.globalAlpha = gameState.combo > baseCombo(gameState) ? 1 : 0.3; + if (totalWidth < gameState.puckWidth) { + drawText( + ctx, + comboText, + "#000", + gameState.puckHeight, + left + gameState.coinSize * 1.5, + gameState.gameZoneHeight - gameState.puckHeight / 2, + true, + ); + + ctx.globalAlpha = 1; + drawCoin( + ctx, + "#ffd300", + gameState.coinSize, + left + gameState.coinSize / 2, + gameState.gameZoneHeight - gameState.puckHeight / 2, + "#ffd300", + 0, + ); + } else { + drawText( + ctx, + comboTextWidth > gameState.puckWidth + ? gameState.combo.toString() + : comboText, + "#000", + comboTextWidth > gameState.puckWidth ? 12 : 20, + gameState.puckPosition, + gameState.gameZoneHeight - gameState.puckHeight / 2, + false, + ); + } + } + startWork("render:borders"); + // Borders + ctx.globalCompositeOperation = "source-over"; + ctx.globalAlpha = 1; + + let redLeftSide = + hasCombo && (gameState.perks.left_is_lava || gameState.perks.trampoline); + let redRightSide = + hasCombo && (gameState.perks.right_is_lava || gameState.perks.trampoline); + let redTop = + hasCombo && (gameState.perks.top_is_lava || gameState.perks.trampoline); + + if (gameState.offsetXRoundedDown) { + // draw outside of gaming area to avoid capturing borders in recordings + if (gameState.perks.left_is_lava < 2) + drawStraightLine( + ctx, + gameState, + (redLeftSide && "#FF0000") || "#FFFFFF", + zoneLeftBorderX(gameState), + 0, + zoneLeftBorderX(gameState), + height, + 1, + ); + if (gameState.perks.right_is_lava < 2) + drawStraightLine( + ctx, + gameState, + (redRightSide && "#FF0000") || "#FFFFFF", + zoneRightBorderX(gameState), + 0, + zoneRightBorderX(gameState), + height, + 1, + ); + } else { + if (gameState.perks.left_is_lava < 2) + drawStraightLine( + ctx, + gameState, + (redLeftSide && "#FF0000") || "", + 0, + 0, + 0, + height, + 1, + ); + + if (gameState.perks.right_is_lava < 2) + drawStraightLine( + ctx, + gameState, + (redRightSide && "#FF0000") || "", + width - 1, + 0, + width - 1, + height, + 1, + ); + } + + if (redTop && gameState.perks.top_is_lava < 2) + drawStraightLine( + ctx, + gameState, + "#FF0000", + zoneLeftBorderX(gameState), + 1, + zoneRightBorderX(gameState), + 1, + 1, + ); + + startWork("render:bottom_line"); + ctx.globalAlpha = 1; + const corner = getCornerOffset(gameState); + const bottomLineIsRed = hasCombo && gameState.perks.compound_interest; + drawStraightLine( + ctx, + gameState, + (bottomLineIsRed && "#FF0000") || + (isOptionOn("mobile-mode") && "#FFFFFF") || + (corner && "#FFFFFF") || + "", + gameState.offsetXRoundedDown - corner, + gameState.gameZoneHeight - 1, + width - gameState.offsetXRoundedDown + corner, + gameState.gameZoneHeight - 1, + bottomLineIsRed ? 1 : 0.5, + ); + + startWork("render:contrast"); + if ( + !isOptionOn("basic") && + isOptionOn("contrast") && + level.svg && + level.color === "#000000" + ) { + ctx.imageSmoothingEnabled = isOptionOn("smooth_lighting") || false; + + if (isOptionOn("probabilistic_lighting")) { + ctx.globalAlpha = 1; + ctx.globalCompositeOperation = "soft-light"; + } else { + haloCanvasCtx.fillStyle = "#FFFFFF"; + haloCanvasCtx.globalAlpha = 0.25; + haloCanvasCtx.globalCompositeOperation = "screen"; + haloCanvasCtx.fillRect(0, 0, haloCanvas.width, haloCanvas.height); + ctx.globalAlpha = 1; + ctx.globalCompositeOperation = "overlay"; + } + + ctx.drawImage(haloCanvas, 0, 0, width, height); + + ctx.imageSmoothingEnabled = false; + } + + startWork("render:text_under_puck"); + ctx.globalCompositeOperation = "source-over"; + ctx.globalAlpha = 1; + if (isOptionOn("mobile-mode") && gameState.startParams.computer_controlled) { + drawText( + ctx, + "breakout.lecaro.me?autoplay", + gameState.puckColor, + gameState.puckHeight, + gameState.canvasWidth / 2, + gameState.gameZoneHeight + + (gameState.canvasHeight - gameState.gameZoneHeight) / 2, + ); + } + if (isOptionOn("mobile-mode") && !gameState.running) { + drawText( + ctx, + t("play.mobile_press_to_play"), + gameState.puckColor, + gameState.puckHeight, + gameState.canvasWidth / 2, + gameState.gameZoneHeight + + (gameState.canvasHeight - gameState.gameZoneHeight) / 2, + ); + } + + startWork("render:timeout"); + if (gameState.winAt || gameState.startCountDown) { + const remaining = + gameState.startCountDown || + Math.ceil((gameState.winAt - gameState.levelTime) / 1000); + if (remaining > 0 && remaining < 5) { + ctx.globalAlpha = 1; + ctx.globalCompositeOperation = "destination-out"; + drawText( + ctx, + remaining.toString(), + "white", + 65, + gameState.canvasWidth / 2, + gameState.canvasHeight / 2, + ); + + ctx.globalCompositeOperation = "screen"; + ctx.globalAlpha = 1 / remaining; + drawText( + ctx, + remaining.toString(), + "white", + 60, + gameState.canvasWidth / 2, + gameState.canvasHeight / 2, + ); + } + } + ctx.globalAlpha = 1; + + startWork("render:askForWakeLock"); + askForWakeLock(gameState); + + startWork("render:resetTransform"); + if (shaked) { + ctx.resetTransform(); + } +} + +function drawStraightLine( + ctx: CanvasRenderingContext2D, + gameState: GameState, + mode: "#FFFFFF" | "" | "#FF0000" | string, + x1, + y1, + x2, + y2, + alpha = 1, +) { + ctx.globalAlpha = alpha; + if (!mode) return; + + x1 = Math.round(x1); + y1 = Math.round(y1); + x2 = Math.round(x2); + y2 = Math.round(y2); + + if (mode == "#FF0000") { + ctx.strokeStyle = "red"; + ctx.lineDashOffset = getDashOffset(gameState); + ctx.lineWidth = 2; + ctx.setLineDash(redBorderDash); + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + ctx.setLineDash(emptyArray); + ctx.lineWidth = 1; + } else { + ctx.fillStyle = mode; + ctx.fillRect( + Math.min(x1, x2), + Math.min(y1, y2), + Math.max(1, Math.abs(x1 - x2)), + Math.max(1, Math.abs(y1 - y2)), + ); + } + + if (mode == "#FF0000") { + } + ctx.globalAlpha = 1; +} + +let cachedBricksRender = document.createElement("canvas"); +let cachedBricksRenderKey = ""; + +export function renderAllBricks() { + ctx.globalAlpha = 1; + + const hasCombo = gameState.combo > baseCombo(gameState); + + const redBorderOnBricksWithWrongColor = + hasCombo && gameState.perks.picky_eater && isPickyEatingPossible(gameState); + + const redRowReach = reachRedRowIndex(gameState); + const { clairvoyant } = gameState.perks; + let offset = getDashOffset(gameState); + if ( + !( + redBorderOnBricksWithWrongColor || + redRowReach !== -1 || + gameState.perks.zen + ) + ) { + offset = 0; + } + + const clairVoyance = + clairvoyant && gameState.brickHP.reduce((a, b) => a + b, 0); + + const newKey = + gameState.gameZoneWidth + + "_" + + gameState.bricks.join("_") + + bombSVG.complete + + "_" + + redRowReach + + "_" + + redBorderOnBricksWithWrongColor + + "_" + + gameState.ballsColor + + "_" + + gameState.perks.pierce_color + + "_" + + clairVoyance + + "_" + + offset; + + if (newKey !== cachedBricksRenderKey) { + cachedBricksRenderKey = newKey; + + cachedBricksRender.width = gameState.gameZoneWidth; + cachedBricksRender.height = gameState.gameZoneWidth + 1; + const canctx = cachedBricksRender.getContext( + "2d", + ) as CanvasRenderingContext2D; + canctx.clearRect(0, 0, gameState.gameZoneWidth, gameState.gameZoneWidth); + canctx.resetTransform(); + canctx.translate(-gameState.offsetX, 0); + // Bricks + gameState.bricks.forEach((color, index) => { + const x = brickCenterX(gameState, index), + y = brickCenterY(gameState, index); + + if (!color) return; + + let redBecauseOfReach = + redRowReach === Math.floor(index / gameState.level.size); + + let redBorder = + (gameState.ballsColor !== color && + color !== "black" && + redBorderOnBricksWithWrongColor) || + (hasCombo && gameState.perks.zen && color === "black") || + redBecauseOfReach; + + canctx.globalCompositeOperation = "source-over"; + drawBrick( + gameState, + canctx, + color, + x, + y, + redBorder ? offset : -1, + clairvoyant >= 2, + ); + if (gameState.brickHP[index] > 1 && clairvoyant) { + canctx.globalCompositeOperation = "source-over"; + drawText( + canctx, + gameState.brickHP[index].toString(), + clairvoyant >= 2 ? color : gameState.level.color, + gameState.puckHeight, + x, + y, + ); + } + + if (color === "black") { + canctx.globalCompositeOperation = "source-over"; + drawIMG(canctx, bombSVG, gameState.brickWidth, x, y); + } + }); + } + + ctx.drawImage(cachedBricksRender, gameState.offsetX, 0); +} + +let cachedGraphics: { [k: string]: HTMLCanvasElement } = {}; + +export function drawPuck( + ctx: CanvasRenderingContext2D, + color: colorString, + puckWidth: number, + puckHeight: number, + yOffset = 0, + concave_puck: number, + redBorderOffset: number, +) { + const key = + "puck" + + color + + "_" + + puckWidth + + "_" + + puckHeight + + "_" + + concave_puck + + "_" + + redBorderOffset; + + if (!cachedGraphics[key]) { + const can = document.createElement("canvas"); + can.width = puckWidth; + can.height = puckHeight * 2; + const canctx = can.getContext("2d") as CanvasRenderingContext2D; + canctx.fillStyle = color; + + canctx.beginPath(); + canctx.moveTo(0, puckHeight * 2); + + if (concave_puck) { + canctx.lineTo(0, puckHeight * 0.75); + canctx.bezierCurveTo( + puckWidth / 2, + (puckHeight * (2 + concave_puck)) / 3, + puckWidth / 2, + (puckHeight * (2 + concave_puck)) / 3, + puckWidth, + puckHeight * 0.75, + ); + canctx.lineTo(puckWidth, puckHeight * 2); + } else { + canctx.lineTo(0, puckHeight * 1.25); + canctx.bezierCurveTo( + 0, + puckHeight * 0.75, + puckWidth, + puckHeight * 0.75, + puckWidth, + puckHeight * 1.25, + ); + canctx.lineTo(puckWidth, puckHeight * 2); + } + + canctx.fill(); + + if (redBorderOffset !== -1) { + canctx.strokeStyle = "#FF0000"; + canctx.lineWidth = 4; + canctx.setLineDash(redBorderDash); + canctx.lineDashOffset = redBorderOffset; + canctx.stroke(); + } + + cachedGraphics[key] = can; + } + + ctx.drawImage( + cachedGraphics[key], + Math.round(gameState.puckPosition - puckWidth / 2), + gameState.gameZoneHeight - puckHeight * 2 + yOffset, + ); +} + +export function drawBall( + ctx: CanvasRenderingContext2D, + color: colorString, + width: number, + x: number, + y: number, + borderColor = "", +) { + const key = "ball" + color + "_" + width + "_" + borderColor; + + const size = Math.round(width); + if (!cachedGraphics[key]) { + const can = document.createElement("canvas"); + can.width = size; + can.height = size; + + const canctx = can.getContext("2d") as CanvasRenderingContext2D; + canctx.beginPath(); + canctx.arc(size / 2, size / 2, Math.round(size / 2) - 1, 0, 2 * Math.PI); + canctx.fillStyle = color; + canctx.fill(); + if (borderColor) { + canctx.lineWidth = 2; + canctx.strokeStyle = borderColor; + canctx.stroke(); + } + + cachedGraphics[key] = can; + } + ctx.drawImage( + cachedGraphics[key], + Math.round(x - size / 2), + Math.round(y - size / 2), + ); +} + +const angles = 32; + +export function drawCoin( + ctx: CanvasRenderingContext2D, + color: colorString, + size: number, + x: number, + y: number, + borderColor: colorString, + rawAngle: number, +) { + const angle = + ((Math.round((rawAngle / Math.PI) * 2 * angles) % angles) + angles) % + angles; + const key = + "coin with halo" + + "_" + + color + + "_" + + size + + "_" + + borderColor + + "_" + + (color === "#ffd300" ? angle : "whatever"); + + if (!cachedGraphics[key]) { + const can = document.createElement("canvas"); + can.width = size; + can.height = size; + + const canctx = can.getContext("2d") as CanvasRenderingContext2D; + + // coin + canctx.beginPath(); + canctx.arc(size / 2, size / 2, size / 2, 0, 2 * Math.PI); + canctx.fillStyle = color; + canctx.fill(); + + canctx.strokeStyle = borderColor; + if (borderColor == "#FF0000") { + canctx.lineWidth = 2; + canctx.setLineDash(redBorderDash); + } + if (color === "transparent") { + canctx.lineWidth = 2; + } + canctx.stroke(); + + if (color === "#ffd300") { + // Fill in + canctx.beginPath(); + canctx.arc(size / 2, size / 2, (size / 2) * 0.6, 0, 2 * Math.PI); + canctx.fillStyle = "rgba(255,255,255,0.5)"; + canctx.fill(); + + canctx.translate(size / 2, size / 2); + canctx.rotate(angle / 16); + canctx.translate(-size / 2, -size / 2); + + canctx.globalCompositeOperation = "multiply"; + drawText(canctx, "$", color, size - 2, size / 2, size / 2 + 1); + drawText(canctx, "$", color, size - 2, size / 2, size / 2 + 1); + } + cachedGraphics[key] = can; + } + ctx.drawImage( + cachedGraphics[key], + Math.round(x - size / 2), + Math.round(y - size / 2), + ); +} + +export function drawFuzzyBall( + ctx: CanvasRenderingContext2D, + color: colorString, + width: number, + x: number, + y: number, +) { + width = Math.max(width, 2); + const key = "fuzzy-circle" + color + "_" + width; + if (!color?.startsWith("#")) debugger; + + const size = Math.round(width * 3); + if (!size || isNaN(size)) { + debugger; + return; + } + if (!cachedGraphics[key]) { + const can = document.createElement("canvas"); + can.width = size; + can.height = size; + + const canctx = can.getContext("2d") as CanvasRenderingContext2D; + const gradient = canctx.createRadialGradient( + size / 2, + size / 2, + 0, + size / 2, + size / 2, + size / 2, + ); + gradient.addColorStop(0, color); + gradient.addColorStop(0.3, color + "88"); + gradient.addColorStop(0.6, color + "22"); + gradient.addColorStop(1, "transparent"); + canctx.fillStyle = gradient; + canctx.fillRect(0, 0, size, size); + cachedGraphics[key] = can; + } + ctx.drawImage( + cachedGraphics[key], + Math.round(x - size / 2), + Math.round(y - size / 2), + ); +} + +export function drawBrick( + gameState: GameState, + ctx: CanvasRenderingContext2D, + color: colorString, + x: number, + y: number, + offset: number = 0, + borderOnly: boolean, +) { + const tlx = Math.ceil(x - gameState.brickWidth / 2); + const tly = Math.ceil(y - gameState.brickWidth / 2); + const brx = Math.ceil(x + gameState.brickWidth / 2) - 1; + const bry = Math.ceil(y + gameState.brickWidth / 2) - 1; + + const width = brx - tlx, + height = bry - tly; + + const key = + "brick" + + color + + "_" + + "_" + + width + + "_" + + height + + "_" + + offset + + "_" + + borderOnly + + "_"; + + if (!cachedGraphics[key]) { + const can = document.createElement("canvas"); + can.width = width; + can.height = height; + const bord = 4; + const cornerRadius = 2; + const canctx = can.getContext("2d") as CanvasRenderingContext2D; + + canctx.fillStyle = color; + + canctx.setLineDash(offset !== -1 ? redBorderDash : emptyArray); + canctx.lineDashOffset = offset; + canctx.strokeStyle = (offset !== -1 && "#FF000033") || color; + canctx.lineJoin = "round"; + canctx.lineWidth = bord; + roundRect( + canctx, + bord / 2, + bord / 2, + width - bord, + height - bord, + cornerRadius, + ); + if (!borderOnly) { + canctx.fill(); + } + canctx.stroke(); + + cachedGraphics[key] = can; + } + ctx.drawImage(cachedGraphics[key], tlx, tly, width, height); + // It's not easy to have a 1px gap between bricks without antialiasing +} + +export function roundRect( + ctx: CanvasRenderingContext2D, + x: number, + y: number, + width: number, + height: number, + radius: number, +) { + ctx.beginPath(); + ctx.moveTo(x + radius, y); + ctx.lineTo(x + width - radius, y); + ctx.quadraticCurveTo(x + width, y, x + width, y + radius); + ctx.lineTo(x + width, y + height - radius); + ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); + ctx.lineTo(x + radius, y + height); + ctx.quadraticCurveTo(x, y + height, x, y + height - radius); + ctx.lineTo(x, y + radius); + ctx.quadraticCurveTo(x, y, x + radius, y); + ctx.closePath(); +} + +export function drawIMG( + ctx: CanvasRenderingContext2D, + img: HTMLImageElement, + size: number, + x: number, + y: number, +) { + const key = "svg" + img + "_" + size + "_" + img.complete; + + if (!cachedGraphics[key]) { + const can = document.createElement("canvas"); + can.width = size; + can.height = size; + + const canctx = can.getContext("2d") as CanvasRenderingContext2D; + + const ratio = size / Math.max(img.width, img.height); + const w = img.width * ratio; + const h = img.height * ratio; + canctx.drawImage(img, (size - w) / 2, (size - h) / 2, w, h); + + cachedGraphics[key] = can; + } + ctx.drawImage( + cachedGraphics[key], + Math.round(x - size / 2), + Math.round(y - size / 2), + ); +} + +export function drawText( + ctx: CanvasRenderingContext2D, + text: string, + color: colorString, + fontSize: number, + x: number, + y: number, + left = false, +) { + const key = "text" + text + "_" + color + "_" + fontSize + "_" + left; + + if (!cachedGraphics[key]) { + const can = document.createElement("canvas"); + can.width = fontSize * text.length; + can.height = fontSize; + const canctx = can.getContext("2d") as CanvasRenderingContext2D; + canctx.fillStyle = color; + canctx.textAlign = left ? "left" : "center"; + canctx.textBaseline = "middle"; + canctx.font = fontSize + "px monospace"; + + canctx.fillText(text, left ? 0 : can.width / 2, can.height / 2, can.width); + + cachedGraphics[key] = can; + } + ctx.drawImage( + cachedGraphics[key], + left ? x : Math.round(x - cachedGraphics[key].width / 2), + Math.round(y - cachedGraphics[key].height / 2), + ); +} + +export const scoreDisplay = document.getElementById( + "score", +) as HTMLButtonElement; +const menuLabel = document.getElementById("menuLabel") as HTMLButtonElement; + +const emptyArray = []; +const redBorderDash = [5, 5]; + +export function getDashOffset(gameState: GameState) { + if (isOptionOn("basic")) { + return 0; + } + return Math.floor(((gameState.levelTime % 500) / 500) * 10) % 10; +} + +let wakeLock = null, + wakeLockPending = false; + +function askForWakeLock(gameState: GameState) { + if ( + gameState.startParams.computer_controlled && + !wakeLock && + !wakeLockPending + ) { + wakeLockPending = true; + try { + navigator.wakeLock.request("screen").then((lock) => { + wakeLock = lock; + wakeLockPending = false; + lock.addEventListener("release", () => { + // the wake lock has been released + wakeLock = null; + }); + }); + } catch (e) { + console.warn("askForWakeLock error", e); + } + } +} diff --git a/src/runHistoryViewer.ts b/src/runHistoryViewer.ts new file mode 100644 index 0000000..d5d277e --- /dev/null +++ b/src/runHistoryViewer.ts @@ -0,0 +1,120 @@ +import { getHistory } from "./gameOver"; +import { appVersion, icons, upgrades } from "./loadGameData"; +import { t } from "./i18n/i18n"; +import { asyncAlert } from "./asyncAlert"; +import { getSettingValue, setSettingValue } from "./settings"; + +export function runHistoryViewerMenuEntry() { + const history = getHistory(); + + return { + icon: icons["icon:history"], + text: t("history.title"), + disabled: history.length < 10, + help: + history.length < 10 + ? t("history.locked") + : t("history.help", { count: history.length }), + async value() { + let sort = 0; + let sortDir = -1; + let columns = [ + { + label: t("history.columns.started"), + field: (r) => r.started, + render(v) { + return new Date(v).toISOString().slice(0, 10); + }, + }, + { + label: t("history.columns.score"), + field: (r) => r.score, + }, + ...upgrades.map((u) => ({ + label: icons["icon:" + u.id], + tooltip: u.name, + field: (r) => r.perks?.[u.id] || 0, + render(v) { + if (!v) return "-"; + return v; + }, + })), + ]; + while (true) { + const hasCurrentVersion = history.find( + (r) => r.appVersion === appVersion, + ); + const hasPastVersion = history.find((r) => r.appVersion !== appVersion); + + const header = columns + .map( + (c, ci) => + `${c.label}`, + ) + .join(""); + const toString = (v) => v.toString(); + const tbody = history + .filter( + (r) => + !hasCurrentVersion || + r.appVersion === appVersion || + getSettingValue("show_old_versions_in_stats", false), + ) + .sort( + (a, b) => + sortDir * (columns[sort].field(a) - columns[sort].field(b)), + ) + .map( + (h) => + "" + + columns + .map((c) => { + const value = c.field(h) ?? 0; + const render = c.render || toString; + return "" + render(value) + ""; + }) + .join("") + + "", + ) + .join(""); + + const result = await asyncAlert({ + title: t("history.title"), + className: "history", + content: [ + ` + +${header} +${tbody} +
    + `, + hasPastVersion && + hasCurrentVersion && { + icon: getSettingValue("show_old_versions_in_stats", false) + ? icons["icon:checkmark_checked"] + : icons["icon:checkmark_unchecked"], + value: "toggle", + text: t("history.include_past_versions"), + }, + ], + }); + if (!result) return; + if (result.startsWith("sort:")) { + const newSort = parseInt(result.split(":")[1]); + if (newSort == sort) { + sortDir *= -1; + } else { + sortDir = -1; + sort = newSort; + } + } + if (result === "toggle") { + setSettingValue( + "show_old_versions_in_stats", + !getSettingValue("show_old_versions_in_stats", false), + ); + } + } + }, + }; +} diff --git a/src/settings.ts b/src/settings.ts new file mode 100644 index 0000000..27e92d6 --- /dev/null +++ b/src/settings.ts @@ -0,0 +1,84 @@ +// Settings + +import { toast } from "./toast"; + +let cachedSettings: { [key: string]: unknown } = {}; +let warnedUserAboutLSIssue = false; + +try { + for (let key in localStorage) { + try { + cachedSettings[key] = JSON.parse(localStorage.getItem(key) || "null"); + } catch (e) { + if (!warnedUserAboutLSIssue) { + warnedUserAboutLSIssue = true; + toast(`Storage issue : ${(e as Error)?.message}`); + } + console.warn("Reading " + key, e); + } + } +} catch (e) { + console.warn(e); +} + +export function getSettingValue(key: string, defaultValue: T) { + return (cachedSettings[key] as T) ?? defaultValue; +} + +// We avoid using localstorage synchronously for perf reasons +let needsSaving: Set = new Set(); + +export function setSettingValue(key: string, value: T) { + needsSaving.add(key); + cachedSettings[key] = value; +} + +export function commitSettingsChangesToLocalStorage() { + try { + for (let key of needsSaving) { + localStorage.setItem(key, JSON.stringify(cachedSettings[key])); + } + needsSaving.clear(); + } catch (e) { + if (!warnedUserAboutLSIssue) { + warnedUserAboutLSIssue = true; + toast(`Storage issue : ${(e as Error)?.message}`); + } + console.warn(e); + } +} + +setInterval(() => commitSettingsChangesToLocalStorage(), 500); + +export function getTotalScore() { + return getSettingValue("breakout_71_total_score", 0); +} + +export function getCurrentMaxCoins() { + return Math.pow(2, getSettingValue("max_coins", 2)) * 200; +} + +export function getCurrentMaxParticles() { + return getCurrentMaxCoins(); +} + +export function cycleMaxCoins() { + setSettingValue("max_coins", (getSettingValue("max_coins", 2) + 1) % 7); +} + +let asked = false; + +export async function askForPersistentStorage() { + if (asked) return; + asked = true; + if (!navigator.storage) return; + if (!navigator.storage.persist) return; + if (!navigator.storage.persisted) return; + if (await navigator.storage.persisted()) { + return; + } + const persistent = await navigator.storage.persist(); + if (!persistent) { + console.warn("No storage granted"); + } +} diff --git a/src/sounds.ts b/src/sounds.ts new file mode 100644 index 0000000..cb566a4 --- /dev/null +++ b/src/sounds.ts @@ -0,0 +1,280 @@ +import { isOptionOn } from "./options"; +import { GameState } from "./types"; + +let lastPlay = Date.now(); + +export function playPendingSounds(gameState: GameState) { + if (lastPlay > Date.now() - 60) { + return; + } + lastPlay = Date.now(); + for (let key in gameState.aboutToPlaySound) { + const soundName = key as keyof GameState["aboutToPlaySound"]; + const ex = gameState.aboutToPlaySound[soundName] as { + vol: number; + x: number; + }; + if (ex.vol) { + sounds[soundName]( + // In stress test, dim the sounds but play them + Math.min(1, ex.vol), + pixelsToPan(gameState, ex.x), + gameState.combo, + ); + ex.vol = 0; + } + } +} +export const sounds = { + wallBeep: (volume: number, pan: number) => { + if (!isOptionOn("sound")) return; + + createSingleBounceSound(800, pan, volume); + }, + + plouf: (volume: number, pan: number) => { + if (!isOptionOn("sound")) return; + createSingleBounceSound(500, pan, volume * 0.5); + // createWaterDropSound(800, pan, volume*0.2, 0.2,'triangle') + }, + + comboIncreaseMaybe: (volume: number, pan: number, combo: number) => { + if (!isOptionOn("sound")) return; + + let delta = 0; + if (!isNaN(lastComboPlayed)) { + if (lastComboPlayed < combo) delta = 1; + if (lastComboPlayed > combo) delta = -1; + } + playShepard(delta, pan, volume); + lastComboPlayed = combo; + }, + + comboDecrease(volume: number, pan: number, combo: number) { + if (!isOptionOn("sound")) return; + playShepard(-1, pan, volume); + }, + coinBounce: (volume: number, pan: number, combo: number) => { + if (!isOptionOn("sound")) return; + createSingleBounceSound(1200, pan, volume, 0.1, "triangle"); + }, + // void: (volume: number, pan: number) => { + // if (!isOptionOn("sound")) return; + // createSingleBounceSound(1200, pan, volume, 0.5, "sawtooth"); + // createSingleBounceSound(600, pan, volume, 0.3, "sawtooth"); + // }, + // freeze: (volume: number, pan: number) => { + // if (!isOptionOn("sound")) return; + // createSingleBounceSound(220, pan, volume, 0.5, "square"); + // createSingleBounceSound(440, pan, volume, 0.5, "square"); + // }, + explode: (volume: number, pan: number, combo: number) => { + if (!isOptionOn("sound")) return; + createExplosionSound(pan); + }, + lifeLost(volume: number, pan: number, combo: number) { + if (!isOptionOn("sound")) return; + createShatteredGlassSound(pan); + }, + + coinCatch(volume: number, pan: number, combo: number) { + if (!isOptionOn("sound")) return; + createSingleBounceSound(900, pan, volume, 0.1, "triangle"); + }, + colorChange(volume: number, pan: number, combo: number) { + createSingleBounceSound(400, pan, volume, 0.5, "sine"); + createSingleBounceSound(800, pan, volume * 0.5, 0.2, "square"); + }, +}; + +// How to play the code on the leftconst context = new window.AudioContext(); +let audioContext: AudioContext, + audioRecordingTrack: MediaStreamAudioDestinationNode; + +export function getAudioContext() { + if (!audioContext) { + if (!isOptionOn("sound")) return null; + audioContext = new (window.AudioContext || window.webkitAudioContext)(); + audioRecordingTrack = audioContext.createMediaStreamDestination(); + } + return audioContext; +} + +export function getAudioRecordingTrack() { + getAudioContext(); + return audioRecordingTrack; +} + +function createSingleBounceSound( + baseFreq = 800, + pan = 0.5, + volume = 1, + duration = 0.1, + type: OscillatorType = "sine", +) { + const context = getAudioContext(); + if (!context) return; + const oscillator = createOscillator(context, baseFreq, type); + + // Create a gain node to control the volume + const gainNode = context.createGain(); + oscillator.connect(gainNode); + + // Create a stereo panner node for left-right panning + const panner = context.createStereoPanner(); + panner.pan.setValueAtTime(pan * 2 - 1, context.currentTime); + gainNode.connect(panner); + panner.connect(context.destination); + panner.connect(audioRecordingTrack); + + // Set up the gain envelope to simulate the impact and quick decay + gainNode.gain.setValueAtTime(0.8 * volume, context.currentTime); // Initial impact + gainNode.gain.exponentialRampToValueAtTime( + 0.001, + context.currentTime + duration, + ); // Quick decay + + // Start the oscillator + oscillator.start(context.currentTime); + + // Stop the oscillator after the decay + oscillator.stop(context.currentTime + duration); +} + +let noiseBuffer: AudioBuffer; + +function getNoiseBuffer(context: AudioContext) { + if (!noiseBuffer) { + const bufferSize = context.sampleRate * 2; // 2 seconds + noiseBuffer = context.createBuffer(1, bufferSize, context.sampleRate); + const output = noiseBuffer.getChannelData(0); + + // Fill the buffer with random noise + for (let i = 0; i < bufferSize; i++) { + output[i] = Math.random() * 2 - 1; + } + } + return noiseBuffer; +} + +function createExplosionSound(pan = 0.5) { + const context = getAudioContext(); + if (!context) return; + // Create an audio buffer + + // Create a noise source + const noiseSource = context.createBufferSource(); + noiseSource.buffer = getNoiseBuffer(context); + + // Create a gain node to control the volume + const gainNode = context.createGain(); + noiseSource.connect(gainNode); + + // Create a filter to shape the explosion sound + const filter = context.createBiquadFilter(); + filter.type = "lowpass"; + filter.frequency.setValueAtTime(1000, context.currentTime); // Set the initial frequency + gainNode.connect(filter); + + // Create a stereo panner node for left-right panning + const panner = context.createStereoPanner(); + panner.pan.setValueAtTime(pan * 2 - 1, context.currentTime); // pan 0 to 1 maps to -1 to 1 + + // Connect filter to panner and then to the destination (speakers) + filter.connect(panner); + panner.connect(context.destination); + panner.connect(audioRecordingTrack); + + // Ramp down the gain to simulate the explosion's fade-out + gainNode.gain.setValueAtTime(1, context.currentTime); + gainNode.gain.exponentialRampToValueAtTime(0.01, context.currentTime + 1); + + // Lower the filter frequency over time to create the "explosive" effect + filter.frequency.exponentialRampToValueAtTime(60, context.currentTime + 1); + + // Start the noise source + noiseSource.start(context.currentTime); + + // Stop the noise source after the sound has played + noiseSource.stop(context.currentTime + 1); +} + +function pixelsToPan(gameState: GameState, pan: number) { + return Math.max( + 0, + Math.min( + 1, + (pan - gameState.offsetXRoundedDown) / gameState.gameZoneWidthRoundedUp, + ), + ); +} + +let lastComboPlayed = NaN, + shepard = 6; + +function playShepard(delta: number, pan: number, volume: number) { + const shepardMax = 11, + factor = 1.05945594920268, + baseNote = 392; + shepard += delta; + if (shepard > shepardMax) shepard = 0; + if (shepard < 0) shepard = shepardMax; + + const play = (note: number) => { + const freq = baseNote * Math.pow(factor, note); + const diff = Math.abs(note - shepardMax * 0.5); + const maxDistanceToIdeal = 1.5 * shepardMax; + const vol = Math.max(0, volume * (1 - diff / maxDistanceToIdeal)); + createSingleBounceSound(freq, pan, vol); + return freq.toFixed(2) + " at " + Math.floor(vol * 100) + "% diff " + diff; + }; + + play(1 + shepardMax + shepard); + play(shepard); + play(-1 - shepardMax + shepard); +} + +function createShatteredGlassSound(pan: number) { + const context = getAudioContext(); + if (!context) return; + const oscillators = [ + createOscillator(context, 3000, "square"), + createOscillator(context, 4500, "square"), + createOscillator(context, 6000, "square"), + ]; + const gainNode = context.createGain(); + const noiseSource = context.createBufferSource(); + noiseSource.buffer = getNoiseBuffer(context); + + oscillators.forEach((oscillator) => oscillator.connect(gainNode)); + noiseSource.connect(gainNode); + gainNode.gain.setValueAtTime(0.2, context.currentTime); + oscillators.forEach((oscillator) => oscillator.start()); + noiseSource.start(); + oscillators.forEach((oscillator) => + oscillator.stop(context.currentTime + 0.2), + ); + noiseSource.stop(context.currentTime + 0.2); + gainNode.gain.exponentialRampToValueAtTime(0.001, context.currentTime + 0.2); + + // Create a stereo panner node for left-right panning + const panner = context.createStereoPanner(); + panner.pan.setValueAtTime(pan * 2 - 1, context.currentTime); + gainNode.connect(panner); + panner.connect(context.destination); + panner.connect(audioRecordingTrack); + + gainNode.connect(panner); +} + +// Helper function to create an oscillator with a specific frequency +function createOscillator( + context: AudioContext, + frequency: number, + type: OscillatorType, +) { + const oscillator = context.createOscillator(); + oscillator.type = type; + oscillator.frequency.setValueAtTime(frequency, context.currentTime); + return oscillator; +} diff --git a/src/startingPerks.ts b/src/startingPerks.ts new file mode 100644 index 0000000..955b848 --- /dev/null +++ b/src/startingPerks.ts @@ -0,0 +1,61 @@ +import { asyncAlert } from "./asyncAlert"; +import { PerkId, Upgrade } from "./types"; +import { t } from "./i18n/i18n"; +import { icons, upgrades } from "./loadGameData"; +import { getSettingValue, getTotalScore, setSettingValue } from "./settings"; +import { isOptionOn } from "./options"; + +export function startingPerkMenuButton() { + return { + disabled: isOptionOn("kid"), + icon: icons["icon:starting_perks"], + text: t("starting_perks.title"), + help: t("starting_perks.help"), + async value() { + await openStartingPerksEditor(); + }, + }; +} + +export function isBlackListedForStart(u: Upgrade) { + return !!(u.requires || u.threshold > getTotalScore()); +} +export function isStartingPerk(u: Upgrade): boolean { + return ( + !isBlackListedForStart(u) && getSettingValue("start_with_" + u.id, u.gift) + ); +} + +export async function openStartingPerksEditor() { + const avaliable = upgrades.filter((u) => !isBlackListedForStart(u)); + const buttons = avaliable.map((u) => { + const checked = isStartingPerk(u); + return { + icon: icons["icon:" + u.id], + text: u.name, + tooltip: u.help(1), + value: [u], + checked, + }; + }); + const checkedList = buttons.filter((b) => b.checked); + + const perks: Upgrade[] | null | void = await asyncAlert({ + title: t("starting_perks.title"), + className: "actionsAsGrid", + content: [ + checkedList.length + ? t("starting_perks.checked") + : t("starting_perks.random"), + ...checkedList, + t("starting_perks.unchecked"), + ...buttons.filter((b) => !b.checked), + ], + }); + if (perks) { + perks?.forEach((perk) => { + setSettingValue("start_with_" + perk.id, !isStartingPerk(perk)); + }); + openStartingPerksEditor(); + } +} diff --git a/src/toast.ts b/src/toast.ts new file mode 100644 index 0000000..6df4027 --- /dev/null +++ b/src/toast.ts @@ -0,0 +1,18 @@ +let div = document.createElement("div"); +div.classList = "hidden toast"; +document.body.appendChild(div); +let timeout: NodeJS.Timeout | undefined; +export function toast(html: string, className = "") { + clearToasts(); + div.classList = "toast visible " + className; + div.innerHTML = html; + timeout = setTimeout(clearToasts, 1500); +} + +export function clearToasts() { + if (timeout) { + clearTimeout(timeout); + timeout = undefined; + } + div.classList = "hidden toast"; +} diff --git a/src/tooltip.ts b/src/tooltip.ts new file mode 100644 index 0000000..dfd17e0 --- /dev/null +++ b/src/tooltip.ts @@ -0,0 +1,93 @@ +import { isOptionOn } from "./options"; + +const tooltip = document.getElementById("tooltip") as HTMLDivElement; + +export function setupTooltips() { + if (isOptionOn("mobile-mode")) { + setupMobileTooltips(tooltip); + } else { + setupDesktopTooltips(tooltip); + } +} +export function hideAnyTooltip() { + tooltip.style.display = "none"; +} + +function setupMobileTooltips(tooltip: HTMLDivElement) { + tooltip.className = "mobile"; + function openTooltip(e: Event) { + hideAnyTooltip(); + const hovering = e.target as HTMLElement; + const tooltipContent = + hovering?.getAttribute("data-help-content")?.trim() || ""; + if (!tooltipContent) { + return; + } + e.stopPropagation(); + e.preventDefault(); + tooltip.innerHTML = tooltipContent; + tooltip.style.display = ""; + const { top } = hovering.getBoundingClientRect(); + tooltip.style.transform = `translate(0,${top}px) translate(0,-100%)`; + } + + document.body.addEventListener("click", openTooltip, true); + document.addEventListener("scroll", hideAnyTooltip); +} + +function setupDesktopTooltips(tooltip: HTMLDivElement) { + tooltip.className = "desktop"; + function updateTooltipPosition(e: { clientX: number; clientY: number }) { + tooltip.style.transform = `translate(${e.clientX}px,${e.clientY}px) translate(${e.clientX > window.innerWidth / 2 ? "-100%" : "0"},${e.clientY > (window.innerHeight * 2) / 3 ? "-100%" : "20px"})`; + } + + function closeToolTip() { + hideAnyTooltip(); + hovering = null; + } + + let hovering: HTMLElement | null = null; + + document.body.addEventListener( + "mouseenter", + (e: MouseEvent) => { + let parent: HTMLElement | null = e.target as HTMLElement; + while (parent && !parent.hasAttribute("data-tooltip")) { + parent = parent.parentElement; + } + if (parent?.getAttribute("data-tooltip")?.trim()) { + hovering = parent as HTMLElement; + tooltip.innerHTML = hovering.getAttribute("data-tooltip") || ""; + tooltip.style.display = ""; + updateTooltipPosition(e); + } else { + closeToolTip(); + } + }, + true, + ); + + setInterval(() => { + if (hovering) { + if (!document.body.contains(hovering)) { + closeToolTip(); + } + } + }, 200); + document.body.addEventListener( + "mousemove", + (e) => { + if (!tooltip.style.display) { + updateTooltipPosition(e); + } + }, + true, + ); + document.body.addEventListener( + "mouseleave", + (e) => { + closeToolTip(); + }, + true, + ); +} diff --git a/src/types.d.ts b/src/types.d.ts new file mode 100644 index 0000000..5647d99 --- /dev/null +++ b/src/types.d.ts @@ -0,0 +1,310 @@ +import { rawUpgrades } from "./upgrades"; +import { options } from "./options"; + +export type colorString = string; + +export type RawLevel = { + name: string; + size: number; + bricks: string; + credit?: string; +}; +export type Level = { + name: string; + size: number; + bricks: colorString[]; + bricksCount: number; + svg: string; + color: string; + sortKey: number; + credit?: string; +}; + +export type Palette = { [k: string]: string }; + +export type Upgrade = { + threshold: number; + gift: boolean; + id: PerkId; + name: string; + category: number; + max: number; + help: (lvl: number) => string; + fullHelp: (lvl: number) => string; + requires: PerkId | ""; +}; + +export type PerkId = (typeof rawUpgrades)[number]["id"]; + +declare global { + interface Window { + webkitAudioContext?: typeof AudioContext; + } + + interface Document { + webkitFullscreenEnabled?: boolean; + webkitCancelFullScreen?: () => void; + } + + interface Element { + webkitRequestFullscreen: typeof Element.requestFullscreen; + } + + interface MediaStream { + // https://devdoc.net/web/developer.mozilla.org/en-US/docs/Web/API/CanvasCaptureMediaStream.html + // On firefox, the capture stream has the requestFrame option + // instead of the track, go figure + requestFrame?: () => void; + } +} + +export type BallLike = { + x: number; + y: number; + vx?: number; + vy?: number; +}; + +export type Coin = { + points: number; + color: colorString; + x: number; + y: number; + size: number; + previousX: number; + previousY: number; + vx: number; + vy: number; + // sx: number; + // sy: number; + a: number; + sa: number; + weight: number; + destroyed?: boolean; + collidedLastFrame?: boolean; + metamorphosisPoints: number; + floatingTime: number; +}; +export type Ball = { + x: number; + previousX: number; + y: number; + previousY: number; + vx: number; + vy: number; + previousVX: number; + previousVY: number; + // sx: number; + // sy: number; + // Ability to pierce N HP + piercePoints: number; + // Any bounce counts, even if brick resisted the hit + hitSinceBounce: number; + // Brick was really broken ,but could have been respawned as a bomb + brokenSinceBounce: number; + sidesHitsSinceBounce: number; + sapperUses: number; + destroyed?: boolean; +}; + +interface BaseFlash { + time: number; + color: colorString; + duration: number; + size: number; + destroyed?: boolean; + x: number; + y: number; +} + +interface ParticleFlash extends BaseFlash { + // type: "particle"; + vx: number; + vy: number; + ethereal: boolean; +} + +interface TextFlash extends BaseFlash { + // type: "text"; + text: string; +} + +interface LightFlash extends BaseFlash { + // type: "ball"; +} + +export type RunStats = { + started: number; + levelsPlayed: number; + loops: number; + runTime: number; + coins_spawned: number; + score: number; + bricks_broken: number; + misses: number; + balls_lost: number; + puck_bounces: number; + wall_bounces: number; + upgrades_picked: number; + max_combo: number; +}; + +export type PerksMap = { + [k in PerkId]: number; +}; + +export type ReusableArray = { + // All items below that index should not be destroyed + indexMin: number; + total: number; + list: T[]; +}; + +export type RunHistoryItem = RunStats & { + perks?: Partial; + appVersion?: string; +}; +export type GameState = { + // Width of the canvas element in pixels + canvasWidth: number; + // Height of the canvas element in pixels + canvasHeight: number; + // Distance between the left of the canvas and the left of the leftmost brick, in pixels + offsetX: number; + // Distance between the left of the canvas and the left border of the game area, in pixels. + // Can be 0 when no border is shown + offsetXRoundedDown: number; + // Width of the bricks area, in pixels + gameZoneWidth: number; + // Width of the game area between the left and right borders, in pixels + gameZoneWidthRoundedUp: number; + // Height of the play area, between the top of the canvas and the bottom of the puck. + // Does not include the finger zone on mobile. + gameZoneHeight: number; + // Size of one brick in pixels + brickWidth: number; + // Size of the current level's grid + gridSize: number; + // 0 based index of the current level in the run (level X / 7) + currentLevel: number; + upgradesOfferedFor: number; + + // 10 levels selected randomly at start for the run + runLevels: Level[]; + // Current level displayed + level: Level; + // Width of the puck in pixels, changed by some perks and resizes + puckWidth: number; + // perks the user currently has + perks: PerksMap; + // Base speed of the ball in pixels/tick + baseSpeed: number; + // Score multiplier + combo: number; + // Combo at the start of the tick + lastCombo: number; + // Whether the game is running or paused + running: boolean; + isGameOver: boolean; + ballStickToPuck: boolean; + // Whether the game should be re-rendered once even if not running + needsRender: boolean; + // Position of the center of the puck on the canvas in pixels, from the left of the canvas. + puckPosition: number; + lastPuckPosition: number; + // Will be set if the game is about to be paused. Game pause is delayed by a few milliseconds if you pause a few times in a run, + // to avoid abuse of the "release to pause" feature on mobile. + pauseTimeout: NodeJS.Timeout | null; + + // Current run score + score: number; + // levelTime of the last time the score increase, to render the score differently + lastScoreIncrease: number; + levelCoughtCoins: number; + // levelTime of the last explosion, for screen shake + lastExplosion: number; + lastBrickBroken: number; + // High score at the beginning of the run + highScore: number; + // Balls currently in game, game over if it's empty + balls: Ball[]; + // Color of the balls, can be changed by some perks + ballsColor: colorString; + // Array of bricks to display. 'black' means bomb. '' means no brick. + bricks: colorString[]; + // Number of times a brick has been hit already + brickHP: number[]; + + particles: ReusableArray; + texts: ReusableArray; + lights: ReusableArray; + coins: ReusableArray; + + // Bricks that should respawn destroyed + respawns: ReusableArray<{ + index: number; + color: string; + time: number; + destroyed?: boolean; + }>; + + levelStartScore: number; + levelMisses: number; + levelSpawnedCoins: number; + levelLostCoins: number; + + puckColor: colorString; + ballSize: number; + coinSize: number; + puckHeight: number; + pauseUsesDuringRun: number; + keyboardPuckSpeed: number; + lastTick: number; + lastTickDown: number; + runStatistics: RunStats; + lastOffered: Partial<{ [k in PerkId]: number }>; + levelTime: number; + lastPuckMove: number; + lastZenComboIncrease: number; + winAt: number; + levelWallBounces: number; + autoCleanUses: number; + aboutToPlaySound: { + wallBeep: { vol: number; x: number }; + comboIncreaseMaybe: { vol: number; x: number }; + comboDecrease: { vol: number; x: number }; + coinBounce: { vol: number; x: number }; + explode: { vol: number; x: number }; + lifeLost: { vol: number; x: number }; + coinCatch: { vol: number; x: number }; + plouf: { vol: number; x: number }; + colorChange: { vol: number; x: number }; + }; + rerolls: number; + creative: boolean; + startParams: RunParams; + startCountDown: number; +}; + +export type RunParams = { + level?: Level; + levelToAvoid?: string; + perkToAvoid?: PerkId; + perks?: Partial; + computer_controlled?: boolean; + isEditorTrialRun?: number; + isCreativeRun?: boolean; + stress?: boolean; +}; +export type OptionDef = { + default: boolean; + name: string; + help: string; +}; +export type OptionId = keyof typeof options; + +export type UnlockCondition = { + required: PerkId[]; + forbidden: PerkId[]; + minScore: number; +}; diff --git a/src/upgrades.test.ts b/src/upgrades.test.ts new file mode 100644 index 0000000..25f1247 --- /dev/null +++ b/src/upgrades.test.ts @@ -0,0 +1,11 @@ +import _rawLevelsList from "./data/levels.json"; +import { rawUpgrades } from "./upgrades"; + +describe("rawUpgrades", () => { + it("has an icon for each upgrade", () => { + const missingIcon = rawUpgrades + .map((u) => u.id) + .filter((id) => !_rawLevelsList.find((l) => l.name === "icon:" + id)); + expect(missingIcon.join(", ")).toEqual(""); + }); +}); diff --git a/src/upgrades.ts b/src/upgrades.ts new file mode 100644 index 0000000..53ebb1f --- /dev/null +++ b/src/upgrades.ts @@ -0,0 +1,972 @@ +import { t } from "./i18n/i18n"; + +import { PerkId } from "./types"; + +import { + base_combo_from_stronger_foundation, + comboKeepingRate, +} from "./pure_functions"; + +// Those perks are excluded from creative mode +export const noCreative: PerkId[] = [ + "extra_levels", + "shunt", + "one_more_choice", +]; + +export const categories = { + beginner: 1, + combo: 2, + combo_boost: 2.5, + simple: 3, + advanced: 4, +}; + +export const rawUpgrades = [ + { + category: categories.beginner, + requires: "", + threshold: 0, + gift: false, + id: "slow_down", + max: 2, + name: t("upgrades.slow_down.name"), + help: (lvl: number) => t("upgrades.slow_down.tooltip", { lvl }), + fullHelp: (lvl: number) => + t("upgrades.slow_down.verbose_description", { lvl }), + }, + { + category: categories.beginner, + requires: "", + threshold: 0, + gift: false, + id: "extra_life", + max: 7, + name: t("upgrades.extra_life.name"), + help: (lvl: number) => t("upgrades.extra_life.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.extra_life.verbose_description", { lvl }), + }, + + { + category: categories.beginner, + requires: "", + threshold: 0, + gift: false, + id: "bigger_puck", + max: 2, + name: t("upgrades.bigger_puck.name"), + help: () => t("upgrades.bigger_puck.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.bigger_puck.verbose_description", { lvl }), + }, + + { + category: categories.beginner, + requires: "", + threshold: 50, + gift: false, + id: "skip_last", + max: 7, + name: t("upgrades.skip_last.name"), + help: (lvl: number) => t("upgrades.skip_last.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.skip_last.verbose_description", { lvl }), + }, + { + category: categories.beginner, + requires: "", + threshold: 500, + id: "telekinesis", + gift: true, + max: 1, + name: t("upgrades.telekinesis.name"), + help: (lvl: number) => t("upgrades.telekinesis.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.telekinesis.verbose_description", { lvl }), + }, + { + category: categories.beginner, + requires: "", + threshold: 600, + gift: false, + id: "yoyo", + max: 1, + name: t("upgrades.yoyo.name"), + help: (lvl: number) => t("upgrades.yoyo.tooltip"), + fullHelp: (lvl: number) => t("upgrades.yoyo.verbose_description", { lvl }), + }, + { + category: categories.beginner, + requires: "", + threshold: 750, + gift: false, + id: "one_more_choice", + max: 3, + name: t("upgrades.one_more_choice.name"), + help: (lvl: number) => t("upgrades.one_more_choice.tooltip", { lvl }), + fullHelp: (lvl: number) => + t("upgrades.one_more_choice.verbose_description", { lvl }), + }, + { + category: categories.beginner, + requires: "", + threshold: 950, + gift: false, + id: "concave_puck", + max: 1, + name: t("upgrades.concave_puck.name"), + help: (lvl: number) => t("upgrades.concave_puck.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.concave_puck.verbose_description", { lvl }), + }, + { + category: categories.combo, + requires: "", + threshold: 100, + id: "streak_shots", + gift: true, + max: 1, + name: t("upgrades.streak_shots.name"), + help: (lvl: number) => t("upgrades.streak_shots.tooltip", { lvl }), + fullHelp: (lvl: number) => + t("upgrades.streak_shots.verbose_description", { lvl }), + }, + + { + category: categories.combo, + requires: "", + threshold: 200, + id: "left_is_lava", + gift: true, + max: 1, + name: t("upgrades.left_is_lava.name"), + help: (lvl: number) => t("upgrades.left_is_lava.tooltip", { lvl }), + fullHelp: (lvl: number) => + t("upgrades.left_is_lava.verbose_description", { lvl }), + }, + { + category: categories.combo, + requires: "", + threshold: 300, + id: "right_is_lava", + gift: true, + max: 1, + name: t("upgrades.right_is_lava.name"), + help: (lvl: number) => t("upgrades.right_is_lava.tooltip", { lvl }), + fullHelp: (lvl: number) => + t("upgrades.right_is_lava.verbose_description", { lvl }), + }, + { + category: categories.combo, + requires: "", + threshold: 400, + id: "top_is_lava", + gift: true, + max: 1, + name: t("upgrades.top_is_lava.name"), + help: (lvl: number) => t("upgrades.top_is_lava.tooltip", { lvl }), + fullHelp: (lvl: number) => + t("upgrades.top_is_lava.verbose_description", { lvl }), + }, + { + category: categories.combo, + requires: "", + threshold: 4000, + id: "hot_start", + gift: true, + max: 3, + name: t("upgrades.hot_start.name"), + help: (lvl: number) => + t("upgrades.hot_start.tooltip", { + start: lvl * 30 + 1, + loss: lvl, + }), + fullHelp: (lvl: number) => + t("upgrades.hot_start.verbose_description", { + start: lvl * 30 + 1, + loss: lvl, + }), + }, + + { + category: categories.combo, + requires: "", + threshold: 2000, + id: "picky_eater", + gift: true, + max: 1, + name: t("upgrades.picky_eater.name"), + help: (lvl: number) => t("upgrades.picky_eater.tooltip", { lvl }), + fullHelp: (lvl: number) => + t("upgrades.picky_eater.verbose_description", { lvl }), + }, + + { + category: categories.combo, + requires: "", + threshold: 3000, + id: "compound_interest", + gift: true, + max: 1, + name: t("upgrades.compound_interest.name"), + help: (lvl: number) => t("upgrades.compound_interest.tooltip", { lvl }), + fullHelp: (lvl: number) => + t("upgrades.compound_interest.verbose_description", { lvl }), + }, + { + category: categories.combo, + requires: "", + threshold: 150000, + gift: true, + id: "side_kick", + max: 3, + name: t("upgrades.side_kick.name"), + help: (lvl: number) => + t("upgrades.side_kick.tooltip", { lvl, loss: lvl * 2 }), + fullHelp: (lvl: number) => + t("upgrades.side_kick.verbose_description", { lvl, loss: lvl * 2 }), + }, + { + category: categories.combo, + requires: "", + threshold: 150000, + gift: true, + id: "side_flip", + max: 3, + name: t("upgrades.side_flip.name"), + help: (lvl: number) => + t("upgrades.side_flip.tooltip", { lvl, loss: lvl * 2 }), + fullHelp: (lvl: number) => + t("upgrades.side_flip.verbose_description", { lvl, loss: lvl * 2 }), + }, + { + category: categories.combo, + requires: "", + threshold: 135000, + // a bit too hard when starting up + gift: false, + id: "reach", + max: 1, + name: t("upgrades.reach.name"), + help: (lvl: number) => t("upgrades.reach.tooltip", { lvl }), + fullHelp: (lvl: number) => t("upgrades.reach.verbose_description", { lvl }), + }, + { + category: categories.combo, + requires: "multiball", + threshold: 245000, + gift: false, + id: "happy_family", + max: 1, + name: t("upgrades.happy_family.name"), + help: () => t("upgrades.happy_family.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.happy_family.verbose_description", { lvl }), + }, + { + category: categories.combo, + requires: "", + threshold: 165000, + gift: false, + id: "addiction", + max: 7, + name: t("upgrades.addiction.name"), + help: (lvl: number) => + t("upgrades.addiction.tooltip", { lvl, delay: (5 / lvl).toFixed(2) }), + fullHelp: (lvl: number) => + t("upgrades.addiction.verbose_description", { + lvl, + delay: (5 / lvl).toFixed(2), + }), + }, + { + category: categories.combo, + requires: "", + threshold: 90000, + gift: true, + id: "nbricks", + max: 3, + name: t("upgrades.nbricks.name"), + help: (lvl: number) => t("upgrades.nbricks.tooltip", { lvl }), + fullHelp: (lvl: number) => + t("upgrades.nbricks.verbose_description", { lvl }), + }, + { + category: categories.combo, + requires: "", + threshold: 230000, + gift: false, + id: "three_cushion", + max: 1, + name: t("upgrades.three_cushion.name"), + help: (lvl: number) => + t("upgrades.three_cushion.tooltip", { max: lvl * 3 }), + fullHelp: (lvl: number) => + t("upgrades.three_cushion.verbose_description", { max: lvl * 3 }), + }, + { + category: categories.combo, + requires: "", + threshold: 115000, + gift: true, + id: "trampoline", + max: 1, + name: t("upgrades.trampoline.name"), + help: (lvl: number) => t("upgrades.trampoline.tooltip", { lvl }), + fullHelp: (lvl: number) => + t("upgrades.trampoline.verbose_description", { lvl }), + }, + + { + category: categories.combo, + requires: "", + threshold: 105000, + gift: true, + id: "zen", + max: 1, + name: t("upgrades.zen.name"), + help: (lvl: number) => t("upgrades.zen.tooltip", { lvl }), + fullHelp: (lvl: number) => t("upgrades.zen.verbose_description", { lvl }), + }, + { + category: categories.combo, + requires: "", + threshold: 70000, + gift: true, + id: "asceticism", + max: 1, + name: t("upgrades.asceticism.name"), + help: (lvl: number) => t("upgrades.asceticism.tooltip", { combo: lvl * 3 }), + fullHelp: (lvl: number) => + t("upgrades.asceticism.verbose_description", { combo: lvl * 3 }), + }, + // Regular + + { + category: categories.simple, + requires: "", + threshold: 15000, + gift: false, + id: "pierce_color", + max: 4, + name: t("upgrades.pierce_color.name"), + help: (lvl: number) => t("upgrades.pierce_color.tooltip", { lvl }), + fullHelp: (lvl: number) => + t("upgrades.pierce_color.verbose_description", { lvl }), + }, + { + category: categories.simple, + requires: "", + threshold: 1500, + id: "pierce", + gift: false, + max: 3, + name: t("upgrades.pierce.name"), + help: (lvl: number) => t("upgrades.pierce.tooltip", { count: 3 * lvl }), + fullHelp: (lvl: number) => + t("upgrades.pierce.verbose_description", { count: 3 * lvl }), + }, + { + category: categories.simple, + requires: "", + threshold: 800, + id: "multiball", + gift: true, + max: 6, + name: t("upgrades.multiball.name"), + help: (lvl: number) => t("upgrades.multiball.tooltip", { count: lvl + 1 }), + fullHelp: (lvl: number) => + t("upgrades.multiball.verbose_description", { count: lvl + 1 }), + }, + { + category: categories.advanced, + requires: "multiball", + threshold: 21000, + gift: false, + id: "ball_repulse_ball", + max: 3, + name: t("upgrades.ball_repulse_ball.name"), + help: (lvl: number) => t("upgrades.ball_repulse_ball.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.ball_repulse_ball.verbose_description", { lvl }), + }, + { + category: categories.advanced, + requires: "multiball", + threshold: 25000, + gift: false, + id: "ball_attract_ball", + max: 3, + name: t("upgrades.ball_attract_ball.name"), + help: (lvl: number) => t("upgrades.ball_attract_ball.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.ball_attract_ball.verbose_description", { lvl }), + }, + { + category: categories.advanced, + requires: "", + threshold: 30000, + gift: false, + id: "puck_repulse_ball", + max: 2, + name: t("upgrades.puck_repulse_ball.name"), + help: (lvl: number) => t("upgrades.puck_repulse_ball.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.puck_repulse_ball.verbose_description", { lvl }), + }, + { + category: categories.advanced, + requires: "", + threshold: 35000, + gift: false, + id: "wind", + max: 3, + name: t("upgrades.wind.name"), + help: (lvl: number) => t("upgrades.wind.tooltip"), + fullHelp: (lvl: number) => t("upgrades.wind.verbose_description", { lvl }), + }, + { + category: categories.advanced, + requires: "", + threshold: 65000, + gift: false, + id: "helium", + max: 3, + name: t("upgrades.helium.name"), + help: (lvl: number) => t("upgrades.helium.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.helium.verbose_description", { lvl }), + }, + { + category: categories.advanced, + requires: "", + threshold: 200000, + gift: false, + id: "bricks_attract_coins", + max: 3, + name: t("upgrades.bricks_attract_coins.name"), + help: (lvl: number) => t("upgrades.bricks_attract_coins.tooltip", { lvl }), + fullHelp: (lvl: number) => + t("upgrades.bricks_attract_coins.verbose_description", { lvl }), + }, + { + category: categories.advanced, + requires: "", + threshold: 240000, + gift: false, + id: "wrap_left", + max: 1, + name: t("upgrades.wrap_left.name"), + help: () => t("upgrades.wrap_left.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.wrap_left.verbose_description", { lvl }), + }, + { + category: categories.advanced, + requires: "", + threshold: 245000, + gift: false, + id: "wrap_right", + max: 1, + name: t("upgrades.wrap_right.name"), + help: () => t("upgrades.wrap_right.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.wrap_right.verbose_description", { lvl }), + }, + + { + category: categories.simple, + requires: "", + threshold: 45000, + gift: false, + id: "respawn", + max: 4, + name: t("upgrades.respawn.name"), + help: (lvl: number) => + t("upgrades.respawn.tooltip", { + percent: Math.floor(100 * comboKeepingRate(lvl)), + delay: (3 / lvl).toFixed(2), + }), + fullHelp: (lvl: number) => + t("upgrades.respawn.verbose_description", { + percent: Math.floor(100 * comboKeepingRate(lvl)), + delay: (3 / lvl).toFixed(2), + }), + }, + { + category: categories.advanced, + requires: "", + threshold: 55000, + gift: false, + id: "double_or_nothing", + max: 3, + name: t("upgrades.double_or_nothing.name"), + help: (lvl: number) => + t("upgrades.double_or_nothing.tooltip", { + percent: lvl * 10, + multiplier: 1 + lvl, + }), + fullHelp: (lvl: number) => + t("upgrades.double_or_nothing.verbose_description", { + percent: lvl * 10, + multiplier: 1 + lvl, + }), + }, + + { + category: categories.advanced, + requires: "", + threshold: 75000, + gift: false, + id: "unbounded", + max: 3, + name: t("upgrades.unbounded.name"), + help: (lvl: number) => t("upgrades.unbounded.tooltip", { lvl }), + fullHelp: (lvl: number) => + t("upgrades.unbounded.verbose_description", { lvl }), + }, + + { + category: categories.advanced, + requires: "", + threshold: 95000, + gift: false, + id: "etherealcoins", + max: 1, + name: t("upgrades.etherealcoins.name"), + help: (lvl: number) => t("upgrades.etherealcoins.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.etherealcoins.verbose_description", { lvl }), + }, + { + category: categories.advanced, + requires: "multiball", + threshold: 100000, + gift: false, + id: "shocks", + max: 1, + name: t("upgrades.shocks.name"), + help: (lvl: number) => t("upgrades.shocks.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.shocks.verbose_description", { lvl }), + }, + { + category: categories.advanced, + requires: "", + threshold: 110000, + gift: false, + id: "sacrifice", + max: 1, + name: t("upgrades.sacrifice.name"), + help: (lvl: number) => t("upgrades.sacrifice.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.sacrifice.verbose_description", { lvl }), + }, + + { + category: categories.advanced, + requires: "", + threshold: 120000, + gift: false, + id: "ghost_coins", + max: 3, + name: t("upgrades.ghost_coins.name"), + help: (lvl: number) => t("upgrades.ghost_coins.tooltip", { lvl }), + fullHelp: (lvl: number) => + t("upgrades.ghost_coins.verbose_description", { lvl }), + }, + { + category: categories.combo_boost, + requires: "", + threshold: 125000, + gift: false, + id: "forgiving", + max: 1, + name: t("upgrades.forgiving.name"), + help: (lvl: number) => t("upgrades.forgiving.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.forgiving.verbose_description", { lvl }), + }, + { + category: categories.simple, + requires: "", + threshold: 130000, + gift: false, + id: "ball_attracts_coins", + max: 3, + name: t("upgrades.ball_attracts_coins.name"), + help: (lvl: number) => t("upgrades.ball_attracts_coins.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.ball_attracts_coins.verbose_description", { lvl }), + }, + { + category: categories.simple, + requires: "", + threshold: 145000, + gift: false, + id: "clairvoyant", + max: 1, + name: t("upgrades.clairvoyant.name"), + help: (lvl: number) => t("upgrades.clairvoyant.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.clairvoyant.verbose_description", { lvl }), + }, + + { + category: categories.advanced, + requires: "", + threshold: 155000, + gift: false, + id: "implosions", + max: 1, + name: t("upgrades.implosions.name"), + help: (lvl: number) => t("upgrades.implosions.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.implosions.verbose_description", { lvl }), + }, + { + category: categories.simple, + requires: "", + threshold: 160000, + gift: false, + id: "corner_shot", + max: 1, + name: t("upgrades.corner_shot.name"), + help: (lvl: number) => t("upgrades.corner_shot.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.corner_shot.verbose_description", { lvl }), + }, + { + category: categories.advanced, + requires: "", + threshold: 175000, + gift: false, + id: "limitless", + max: 1, + name: t("upgrades.limitless.name"), + help: (lvl: number) => t("upgrades.limitless.tooltip", { lvl }), + fullHelp: (lvl: number) => + t("upgrades.limitless.verbose_description", { lvl }), + }, + { + category: categories.advanced, + requires: "", + threshold: 185000, + gift: false, + id: "trickledown", + max: 1, + name: t("upgrades.trickledown.name"), + help: (lvl: number) => t("upgrades.trickledown.tooltip", { lvl }), + fullHelp: (lvl: number) => + t("upgrades.trickledown.verbose_description", { lvl }), + }, + { + category: categories.combo_boost, + requires: "", + threshold: 190000, + gift: false, + id: "transparency", + max: 3, + name: t("upgrades.transparency.name"), + help: (lvl: number) => + t("upgrades.transparency.tooltip", { lvl, percent: lvl * 50 }), + fullHelp: (lvl: number) => + t("upgrades.transparency.verbose_description", { + lvl, + percent: lvl * 50, + }), + }, + { + category: categories.simple, + requires: "", + threshold: 195000, + gift: false, + id: "superhot", + max: 3, + name: t("upgrades.superhot.name"), + help: (lvl: number) => t("upgrades.superhot.tooltip", { lvl }), + fullHelp: (lvl: number) => + t("upgrades.superhot.verbose_description", { lvl }), + }, + { + category: categories.advanced, + requires: "", + threshold: 205000, + gift: false, + id: "rainbow", + max: 7, + name: t("upgrades.rainbow.name"), + help: (lvl: number) => t("upgrades.rainbow.tooltip", { lvl }), + fullHelp: (lvl: number) => + t("upgrades.rainbow.verbose_description", { lvl }), + }, + { + category: categories.advanced, + requires: "", + threshold: 210000, + gift: false, + id: "golden_goose", + max: 1, + name: t("upgrades.golden_goose.name"), + help: (lvl: number) => t("upgrades.golden_goose.tooltip", { lvl }), + fullHelp: (lvl: number) => + t("upgrades.golden_goose.verbose_description", { lvl }), + }, + { + category: categories.simple, + requires: "", + threshold: 215000, + gift: false, + id: "bricks_attract_ball", + max: 1, + name: t("upgrades.bricks_attract_ball.name"), + help: (lvl: number) => + t("upgrades.bricks_attract_ball.tooltip", { count: lvl * 3 }), + fullHelp: (lvl: number) => + t("upgrades.bricks_attract_ball.verbose_description", { count: lvl * 3 }), + }, + { + category: categories.simple, + requires: "", + threshold: 220000, + gift: false, + id: "buoy", + max: 3, + name: t("upgrades.buoy.name"), + help: (lvl: number) => t("upgrades.buoy.tooltip", { duration: lvl * 0.5 }), + fullHelp: (lvl: number) => + t("upgrades.buoy.verbose_description", { duration: lvl * 0.5 }), + }, + { + category: categories.advanced, + requires: "", + threshold: 225000, + gift: false, + id: "ottawa_treaty", + max: 1, + name: t("upgrades.ottawa_treaty.name"), + help: () => t("upgrades.ottawa_treaty.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.ottawa_treaty.verbose_description", { lvl }), + }, + + { + category: categories.advanced, + requires: "", + threshold: 235000, + gift: false, + id: "sticky_coins", + max: 1, + name: t("upgrades.sticky_coins.name"), + help: (lvl: number) => t("upgrades.sticky_coins.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.sticky_coins.verbose_description", { lvl }), + }, + { + category: categories.combo_boost, + requires: "", + threshold: 0, + id: "base_combo", + gift: true, + max: 7, + name: t("upgrades.base_combo.name"), + help: (lvl: number) => + t("upgrades.base_combo.tooltip", { + coins: base_combo_from_stronger_foundation(lvl), + }), + fullHelp: (lvl: number) => + t("upgrades.base_combo.verbose_description", { lvl }), + }, + { + category: categories.simple, + requires: "", + threshold: 0, + gift: false, + id: "viscosity", + max: 3, + name: t("upgrades.viscosity.name"), + help: () => t("upgrades.viscosity.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.viscosity.verbose_description", { lvl }), + }, + { + category: categories.simple, + requires: "", + threshold: 700, + gift: false, + id: "coin_magnet", + max: 3, + name: t("upgrades.coin_magnet.name"), + help: (lvl: number) => t("upgrades.coin_magnet.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.coin_magnet.verbose_description", { lvl }), + }, + { + category: categories.combo_boost, + requires: "", + threshold: 1000, + gift: false, + id: "smaller_puck", + max: 2, + name: t("upgrades.smaller_puck.name"), + help: (lvl: number) => + t("upgrades.smaller_puck.tooltip", { percent: 50 * lvl }), + fullHelp: (lvl: number) => + t("upgrades.smaller_puck.verbose_description", { percent: 50 * lvl }), + }, + { + category: categories.advanced, + requires: "", + threshold: 2500, + gift: false, + id: "metamorphosis", + max: 1, + name: t("upgrades.metamorphosis.name"), + help: (lvl: number) => t("upgrades.metamorphosis.tooltip", { lvl }), + fullHelp: (lvl: number) => + t("upgrades.metamorphosis.verbose_description", { lvl }), + }, + { + category: categories.simple, + requires: "", + threshold: 6000, + id: "sapper", + gift: false, + max: 7, + name: t("upgrades.sapper.name"), + help: (lvl: number) => t("upgrades.sapper.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.sapper.verbose_description", { lvl }), + }, + { + category: categories.simple, + requires: "", + threshold: 9000, + id: "bigger_explosions", + gift: false, + max: 1, + name: t("upgrades.bigger_explosions.name"), + help: (lvl: number) => t("upgrades.bigger_explosions.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.bigger_explosions.verbose_description", { lvl }), + }, + { + category: categories.simple, + requires: "", + threshold: 13000, + gift: false, + adventure: false, + id: "extra_levels", + max: 3, + name: t("upgrades.extra_levels.name"), + help: (lvl: number) => + t("upgrades.extra_levels.tooltip", { count: lvl + 7 }), + fullHelp: (lvl: number) => t("upgrades.extra_levels.verbose_description"), + }, + { + category: categories.combo_boost, + requires: "", + threshold: 170000, + gift: false, + id: "fountain_toss", + max: 7, + name: t("upgrades.fountain_toss.name"), + help: () => t("upgrades.fountain_toss.tooltip"), + fullHelp: (lvl: number) => + t("upgrades.fountain_toss.verbose_description", { lvl }), + }, + { + category: categories.combo_boost, + requires: "", + threshold: 180000, + gift: false, + id: "minefield", + max: 3, + name: t("upgrades.minefield.name"), + help: (lvl: number) => + t("upgrades.minefield.tooltip", { percent: 10 * lvl }), + fullHelp: (lvl: number) => + t("upgrades.minefield.verbose_description", { percent: 10 * lvl }), + }, + { + category: categories.combo_boost, + requires: "", + threshold: 18000, + gift: false, + id: "soft_reset", + max: 3, + name: t("upgrades.soft_reset.name"), + help: (lvl: number) => + t("upgrades.soft_reset.tooltip", { + percent: Math.round(comboKeepingRate(lvl) * 100), + }), + fullHelp: (lvl: number) => + t("upgrades.soft_reset.verbose_description", { + percent: Math.round(comboKeepingRate(lvl) * 100), + }), + }, + { + category: categories.combo_boost, + requires: "", + threshold: 80000, + gift: false, + id: "shunt", + max: 3, + name: t("upgrades.shunt.name"), + help: (lvl: number) => + t("upgrades.shunt.tooltip", { + percent: Math.round(comboKeepingRate(lvl) * 100), + }), + fullHelp: (lvl: number) => + t("upgrades.shunt.verbose_description", { + percent: Math.round(comboKeepingRate(lvl) * 100), + }), + }, + { + category: categories.combo, + requires: "", + threshold: 140000, + gift: true, + id: "passive_income", + max: 4, + name: t("upgrades.passive_income.name"), + help: (lvl: number) => + t("upgrades.passive_income.tooltip", { + time: (lvl * 0.1 - 0.05).toFixed(2), + lvl, + }), + fullHelp: (lvl: number) => + t("upgrades.passive_income.verbose_description", { + time: (lvl * 0.1 - 0.05).toFixed(2), + lvl, + }), + }, + { + category: categories.combo_boost, + requires: "", + threshold: 40000, + gift: false, + id: "sturdy_bricks", + max: 4, + name: t("upgrades.sturdy_bricks.name"), + help: (lvl: number) => + t("upgrades.sturdy_bricks.tooltip", { lvl, percent: lvl * 50 }), + fullHelp: (lvl: number) => + t("upgrades.sturdy_bricks.verbose_description", { + lvl, + percent: lvl * 50, + }), + }, + { + category: categories.advanced, + requires: "", + threshold: 250000, + gift: false, + id: "steering", + max: 4, + name: t("upgrades.steering.name"), + help: (lvl: number) => t("upgrades.steering.tooltip"), + fullHelp: (lvl: number) => t("upgrades.steering.verbose_description"), + }, +] as const; diff --git a/staging_deploy.sh b/staging_deploy.sh new file mode 100755 index 0000000..6e0d9b6 --- /dev/null +++ b/staging_deploy.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -e +set -x +versionCode=$(($(date +%s) / 60)) + +bash ./build.sh $versionCode + +# we don't add a version tag to let fdroid ignore this build + +# upload to breakout-v3-staging.lecaro.me +DOMAIN="breakout-v3-staging.lecaro.me" +PUBLIC_CONTENT="./build/*" + +ssh staging "mkdir -p /opt/mup-nginx-proxy/config/html/static_sites/$DOMAIN" +rsync -avz --delete $PUBLIC_CONTENT staging:/opt/mup-nginx-proxy/config/html/static_sites/$DOMAIN + diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..c5e58ba --- /dev/null +++ b/start.sh @@ -0,0 +1,9 @@ + + +source ~/.nvm/nvm.sh; + +nvm install v21 +nvm use v21 + +rm -rf .parcel-cache +npx run-p dev:* \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..8820082 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es2017", + "rootDir": "src", + "strict": true, + "skipLibCheck": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "jsx": "preserve" + + } +}