diff --git a/.gitignore b/.gitignore index 7a9816f..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,3 +10,7 @@ local.properties node_modules *.zip +app/release/ +.parcel-cache/ +dist +keystore.properties \ No newline at end of file diff --git a/Readme.md b/Readme.md index e56de00..7ef513f 100644 --- a/Readme.md +++ b/Readme.md @@ -1,89 +1,598 @@ # Breakout 71 -A simple, single player, challenging arcade breakout game. -The goal is to break all the bricks of 7 levels, which catching as many coins as you can. -You have only one life, if you lose your ball you'll go back to the start -At the end of each level, you get to select an upgrade. +Break colourful bricks, catch bouncing coins and select powerful upgrades ! -[Play now](https://breakout.lecaro.me/) - -[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) -[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) + +# Changelog +## To do -## TODO -- automate itch update https://itch.io/docs/butler/pushing.html -- apply a minimum speed to the ball (when 2 slower balls picked it is crawling) -- Fdroid -- easily start a test game with specific upgrades or levels (with query string or through menu) -- show total score on end screen (score added to total) -- show stats on end screen compared to other runs -- handle back bouton in menu -- more levels : famous simple games, letters, fruits, animals -- perk : wrap left / right -- perk : twice as many coins after a wall bounce, twice as little otherwise -- perk : fusion reactor (gather coins in one spot to triple their value) -- perk : missing makes you loose all score of level, but otherwise multiplier goes up after each breaking -- perk : n/10 of the broken bricks respawn when the ball comes back -- perk : bricks take twice as many hits but drop 50% more coins -- perk : wind (puck positions adds force to coins and balls) -- perk : balls repulse coins -- missing triggers and explosive lighting strike around ball path -## maybe +## 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 -- Make a small mp4 of game which can be shown on gameover and shared. https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder -- perk : soft reset, cut combo in half instead of zero -- perk : missile goes when you catch coin -- perk : missile goes when you break a brick -- when game resumes near bottom, be unvulnerable for .5s ? , once per level +- 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 + +## 29097764 + +- Added levels : Fish, Spider, GlidersLone island,Spacewyrm Jon, Taijitu, Egg pan, Inception, Chess + +## 29095000 + +- 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 + +## 29092809 + +- fixed: crash when running out of levels (i think, i didn't try) +- fixed: context menu and tooltip stuck on windows + +## 29091656 + +- 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 + +## 29088680 + +- 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 + +## 29088513 + +- included german corrections by Pock +- added particle effect for wrap +- removed grace period from passive income, updated icon + +## 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 +- 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 + +## 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 +- [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 +- bricks attract coins +- wrap left / right +- correction : pick one past upgrade to remove and replace by something else +- +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 -- mouvement relatif du puck -- balls should collide with each other -- randomize coins gravity a bit, to make fall more appealing -- apply global curve / brightness to canvas when things blow, or just always to make neon effect better -- perk: bricks attract coins -- perk : puck bounce +1 combo, hit nothing resets -- manifest for PWA (android and apple) -- publish on fdroid -- nerf the hot start a bit -- brick parts fly around with trailing effect ? -- trailing white lines behind ball -- some 3d ish effect ? -- shrink brick at beaking time ? -- perk : multiple hits on the same brick (show remaining resistance as number) -- particle effect around ball when loosing some combo (looks bad) -- Make bricks shadow the light ? using a "fill path" in screen mode, with a gradient background...would get very laggy, maybe just for the ball -- keyboard support -- perk : bricks attract ball -- perk : replay last level (remove score, restores lives if any, and rebuild ) -- perk: breaking bricks stains neighbours -- perk: extra kick after bouncing on puck -- perk: transparent coins -- perk: coins of different colors repulse -- 2x coins when ball goes downward ? -- engine: Offline mode web for iphone -- engine: webgl rendering (not with sdf though, that's super slow) +- [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). +## 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 -## Credits +- 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 -I pulled many background patterns from https://pattern.monster/ -They are displayed in [patterns.html](patterns.html) for easy inclusion. -Some of the sound generating code was written by ChatGPT, and heavily -adapted to my usage over time. Some of the pixel art is taken from google -image search. I hope to replace it by my own over time. +## extra levels -[Heart](https://www.youtube.com/watch?v=gdWiTfzXb1g) -[Sonic](https://www.deviantart.com/graystripe2000/art/Pixel-art-16x16-Sonic-936384096) -[Finn](https://at.pinterest.com/pin/finn-the-human-pixel-art--140806230775275/) -[Mushroom](https://pixelartmaker.com/art/cce4295a92035ea) +- Good games : + - FTL + - Nova drift + - Noita + - Enter the gungeon + - Zero Sivert + - Factorio + - Swarm + - Nuclear throne + - Brigador -## APK version -The web app is around 50kb, compressed down to 10kb with gzip -I wanted an APK to start in fullscreen and be able to list it on fdroid and the play store. +- letters and an associated word or name +- famous characters and movies +- famous places : eiffel tower, taj mahal, etc.. +- fruits +- animals +- countries flags and shapes -I stated 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 + +## 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 +- Add color schemes into the game (ex : Catppuccin, Dracula, Terminal, etc) +- 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 the background patterns from https://pattern.monster/ + +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 + +Colin (obigre) brought a lot of fantastic ideas to the game, here's his website (in French) : https://colin-crapahute.bearblog.dev/ + +# 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 41b7697..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 = 28996651 - versionName = "28996651" + 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/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 adcef8f..0000000 --- a/app/src/main/assets/game.js +++ /dev/null @@ -1,2429 +0,0 @@ -const MAX_COINS = 400; -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; - -if (allLevels.find(l => l.focus)) { - allLevels = allLevels.filter(l => l.focus) -} -allLevels.forEach((l,li)=>{ - l.threshold= li < 8 ? 0 : Math.round(Math.pow(10, 1 + (li + l.size) / 30) * (li)) * 10 -}) - -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; -} - -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) { - incrementRunStatistics('combo_resets', 1) - 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: "red", - x: x, - y: y, - duration: 150, - size: puckHeight, - }); - } - } -} - -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: "red", - x: x, - y: y, - duration: 300, - size: puckHeight, - }); - } - } -} - -let gridSize = 12; - -let running = false, puck = 400; - -function play() { - if (running) return - running = true - if (audioContext) { - audioContext.resume() - } -} - -function pause() { - if (!running) return - running = false - needsRender = true - if (audioContext) { - audioContext.suspend() - } -} - - -let offsetX, gameZoneWidth, 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; - 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); - backgroundCanvas.title = 'resized' - // Ensure puck stays within bounds - setMousePos(puck); - coins = []; - flashes = []; - pause() - putBallsAtPuck(); -}; -window.addEventListener("resize", fitSize); - -function recomputeTargetBaseSpeed() { - baseSpeed = 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 getRowCol(index) { - return { - col: index % gridSize, row: Math.floor(index / gridSize), - }; -} - -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; - for (let i = 0; i < count; i++) { - flashes.push({ - type: "particle", - duration, - time: levelTime, - size, - color, - 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, - }); - } -} - - -let score = 0; -let scoreStory = []; - -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) - } - incrementRunStatistics('caught_coins', coin.points) - -} - -let balls = []; - -function resetBalls() { - const count = 1 + (perks?.multiball || 0); - const perBall = puckWidth / (count + 1); - balls = []; - 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, - color: currentLevelInfo()?.black_puck ? '#000' : "#FFF", - hitSinceBounce: 0, - piercedSinceBounce: 0, - sparks: 0, - }); - } -} - -function putBallsAtPuck() { - const count = balls.length; - const perBall = puckWidth / (count + 1); - balls.forEach((ball, i) => { - const x = puck - puckWidth / 2 + perBall * (i + 1); - Object.assign(ball, { - 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, - }); - }); -} - -resetBalls(); -// Default, recomputed at each level load -let bricks = []; -let flashes = []; -let coins = []; -let levelStartScore = 0; -let levelMisses = 0; -let levelSpawnedCoins = 0; - -function getLevelStats() { - const catchRate = (score - levelStartScore) / (levelSpawnedCoins || 1); - let stats = ` - you caught ${score - levelStartScore} coins out of ${levelSpawnedCoins} in ${Math.round(levelTime / 1000)} seconds. - `; - stats += levelMisses ? `You missed ${levelMisses} times. ` : ""; - let text = [stats]; - let repeats = 1; - let choices = 3; - - if (levelTime < 30 * 1000) { - repeats++; - choices++; - text.push("speed bonus: +1 upgrade and choice"); - } else if (levelTime < 60 * 1000) { - choices++; - text.push("speed bonus: +1 choice"); - } - if (catchRate === 1) { - repeats++; - choices++; - text.push("coins bonus: +1 upgrade and choice"); - } else if (catchRate > 0.9) { - choices++; - text.push("coins bonus: +1 choice."); - } - if (levelMisses === 0) { - repeats++; - choices++; - text.push("accuracy bonus: +1 upgrade and choice"); - } else if (levelMisses <= 3) { - choices++; - text.push("accuracy bonus:+1 choice"); - } - - return { - stats, text: text.map(t => '

' + t + '

').join('\n'), repeats, choices, - }; -} - -async function openUpgradesPicker() { - let {text, repeats, choices} = getLevelStats(); - scoreStory.push(`Finished level ${currentLevel + 1} (${currentLevelInfo().name}): ${text}`,); - - while (repeats--) { - const actions = pickRandomUpgrades(choices); - if (!actions.length) break - let textAfterButtons; - if (actions.length < choices) { - textAfterButtons = `

You are running out of upgrades, more will be unlocked when you catch lots of coins.

` - } - const cb = await asyncAlert({ - title: "Pick an upgrade " + (repeats ? "(" + (repeats + 1) + ")" : ""), actions, text, allowClose: false, - textAfterButtons - }); - cb(); - } - resetCombo(); - resetBalls(); -} - -function setLevel(l) { - pause() - if (l > 0) { - openUpgradesPicker().then(); - } - currentLevel = l; - - levelTime = 0; - lastTickDown = levelTime; - levelStartScore = score; - levelSpawnedCoins = 0; - levelMisses = 0; - - resetCombo(); - recomputeTargetBaseSpeed(); - resetBalls(); - - const lvl = currentLevelInfo(); - if (lvl.size !== gridSize) { - gridSize = lvl.size; - fitSize(); - } - incrementRunStatistics('lvl_size_' + lvl.size, 1) - incrementRunStatistics('lvl_name_' + lvl.name, 1) - coins = []; - bricks = [...lvl.bricks]; - flashes = []; - - background.src = 'data:image/svg+xml;base64,' + btoa(lvl.svg) - -} - -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; - // TODO - // perks.puck_repulse_ball=3 - // perks.ball_repulse_ball=3 - // perks.ball_attract_ball=3 - // perks.multiball=3 - - return randomGift -} - -const upgrades = [ - { - "threshold": 0, - "id": "extra_life", - "name": "+1 life", - "max": 3, - "help": "Survive dropping the ball once." - }, - { - "threshold": 0, - "id": "streak_shots", - "giftable": true, - "name": "Single puck hit streak", - "max": 1, - "help": "Break many bricks at once." - }, - - { - "threshold": 0, - "id": "base_combo", - "giftable": true, - "name": "+3 base combo", - "max": 3, - "help": "Your combo starts 3 points higher." - }, - { - "threshold": 0, - "id": "slow_down", - "name": "Slower ball", - "max": 2, - "help": "Slows down the ball." - }, - { - "threshold": 0, - "id": "bigger_puck", - "name": "Bigger puck", - "max": 2, - "help": "Catches more coins." - }, - { - "threshold": 50, - "id": "viscosity", - "name": "Slower coins fall", - "max": 3, - "help": "Coins quickly decelerate." - }, - { - "threshold": 100, - "id": "sides_are_lava", - "giftable": true, - "name": "Shoot straight", - "max": 1, - "help": "Avoid the sides for more coins." - }, - { - "threshold": 200, - "id": "telekinesis", - "giftable": true, - "name": "Puck controls ball", - "max": 2, - "help": "Control the ball's trajectory." - }, - { - "threshold": 400, - "id": "top_is_lava", - "giftable": true, - "name": "Sky is the limit", - "max": 1, - "help": "Avoid the top for more coins." - }, - { - "threshold": 800, - "id": "coin_magnet", - "name": "Puck attracts coins", - "max": 3, - "help": "Coins falling are drawn toward the puck." - }, - { - "threshold": 1600, - "id": "skip_last", - "name": "Last brick breaks", - "max": 3, - "help": "The last brick will self-destruct." - }, - { - "threshold": 3200, - "id": "multiball", - "giftable": true, - "name": "+1 ball", - "max": 3, - "help": "Start each level with one more balls." - }, - { - "threshold": 5600, - "id": "smaller_puck", - "name": "Smaller puck", - "max": 2, - "help": "Gives you more control." - }, - { - "threshold": 7000, - "id": "pierce", - "giftable": true, - "name": "Ball pierces bricks", - "max": 3, - "help": "Go through 3 blocks before bouncing." - }, - { - "threshold": 12000, - "id": "picky_eater", - "giftable": true, - "name": "Single color streak", - "color_blind_exclude": true, - "max": 1, - "help": "Hit groups of bricks of the same color." - }, - { - "threshold": 16000, - "id": "metamorphosis", - "name": "Coins stain bricks", - "color_blind_exclude": true, - "max": 1, - "help": "Coins color the bricks they touch." - }, - { - "threshold": 22000, - "id": "catch_all_coins", - "giftable": true, - "name": "Compound interest", - "max": 3, - "help": "Catch all coins with your puck for even more coins." - }, - { - "threshold": 26000, - "id": "hot_start", - "giftable": true, - "name": "Hot start", - "max": 3, - "help": "Clear the level quickly for more coins." - }, - { - "threshold": 33000, - "id": "sapper", - "giftable": true, - "name": "Bricks become bombs", - "max": 1, - "help": "Broken blocks are replaced by bombs." - }, - { - "threshold": 42000, - "id": "bigger_explosions", - "name": "Bigger explosions", - "max": 1, - "help": "All bombs have larger area of effect." - }, - { - "threshold": 54000, - "id": "extra_levels", - "name": "+1 level", - "max": 3, - "help": "Play one more level before game over." - }, - { - "threshold": 65000, - "id": "pierce_color", - "name": "Color pierce", - "color_blind_exclude": true, - "max": 1, - "help": "Colored ball pierces bricks of the same color." - }, - { - "threshold": 760000, - "id": "soft_reset", - "name": "Soft reset", - "max": 2, - "help": "Only loose half your combo when it resets." - }, - { - "threshold": 87000, - "id": "ball_repulse_ball", - "name": "Balls repulse balls", - requires:'multiball', - "max": 3, - "help": "Only has an effect with 2+ balls." - }, - { - "threshold": 98000, - "id": "ball_attract_ball", - requires:'multiball', - "name": "Balls attract balls", - "max": 3, - "help": "Only has an effect with 2+ balls." - }, - { - "threshold": 120000, - "id": "puck_repulse_ball", - "name": "Puck repulse balls", - "max": 3, - "help": "Prevents the puck from touching the balls." - }, -] - - -function getPossibleUpgrades() { - const ts = getTotalScore() - return upgrades - .filter(u => !(isSettingOn('color_blind') && u.color_blind_exclude)) - .filter(u => ts>=u.threshold) - .filter(u => !u.requires || perks[u.requires]) -} - - -function shuffleLevels(nameToAvoid = null) { - const ts = getTotalScore(); - runLevels = allLevels - .filter(l => nextRunOverrides.level ? l.name === nextRunOverrides.level : true) - .filter((l, li) => ts >= l.threshold) - .filter(l => l.name !== nameToAvoid || allLevels.length === 1) - .sort(() => Math.random() - 0.5) - .slice(0, 7 + 3) - .sort((a, b) => a.bricks.filter(i => i).length - b.bricks.filter(i => i).length); - - nextRunOverrides.level = null -} - -function getUpgraderUnlockPoints() { - let list = [] - - upgrades - .filter(u => !(isSettingOn('color_blind') && u.color_blind_exclude)) - .forEach(u => { - if (u.threshold) { - list.push({ - threshold: u.threshold, - title: u.name + ' (Perk)', - help: u.help, - }) - } - }) - - 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 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) - .map(u => { - incrementRunStatistics('offered_upgrade.' + u.id, 1) - return { - key: u.id, text: u.name, value: () => { - perks[u.id]++; - incrementRunStatistics('picked_upgrade.' + u.id, 1) - scoreStory.push("Picked upgrade : " + u.name); - }, help: u.help, max: u.max, - checked: perks[u.id], - } - }) - - - list.forEach(u=> { - lastOffered[u.key] = Math.round(Date.now()/1000) - }) - - return list; -} - -let nextRunOverrides = {level: null, perks: null} -let hadOverrides = false - -function restart() { - console.log("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 - shuffleLevels(levelTime || score ? currentLevelInfo().name : null); - resetRunStatistics() - score = 0; - scoreStory = []; - if (hadOverrides) { - scoreStory.push(`This is a test run, started from the unlocks menu. It stops after one level and is not recorded in the stats. `) - } - const randomGift = reset_perks(); - - incrementRunStatistics('starting_upgrade.' + randomGift, 1) - - setLevel(0); - scoreStory.push(`You started playing with the upgrade "${upgrades.find(u => u.id === randomGift)?.name}" on the level "${runLevels[0].name}". `,); -} - -function setMousePos(x) { - - needsRender = true; - puck = x; - - if (offsetX > ballSize) { - // We have borders visible, enforce them - if (puck < offsetX + puckWidth / 2) { - puck = offsetX + puckWidth / 2; - } - if (puck > offsetX + gameZoneWidth - puckWidth / 2) { - puck = offsetX + gameZoneWidth - puckWidth / 2; - } - } else { - // Let puck touch the border of the screen - if (puck < puckWidth / 2) { - puck = puckWidth / 2; - } - if (puck > offsetX * 2 + gameZoneWidth - puckWidth / 2) { - puck = offsetX * 2 + gameZoneWidth - puckWidth / 2; - } - } - if (!running && !levelTime) { - putBallsAtPuck(); - } -} - -canvas.addEventListener("mouseup", (e) => { - if (e.button !== 0) return; - if (running) { - pause() - } else { - play() - } -}); - -canvas.addEventListener("mousemove", (e) => { - 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() -}); -canvas.addEventListener("touchcancel", (e) => { - e.preventDefault(); - pause() - 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(ballOrCoin, vhit, hhit, chit) { - if (!perks.pierce_color) return false - // if (ballOrCoin.color === 'white') return true - if (typeof vhit !== 'undefined' && bricks[vhit] !== ballOrCoin.color) { - return false - } - if (typeof hhit !== 'undefined' && bricks[hhit] !== ballOrCoin.color) { - return false - } - if (typeof chit !== 'undefined' && bricks[chit] !== ballOrCoin.color) { - return false - } - return true -} - -function brickHitCheck(ballOrCoin, radius, isBall) { - // Make ball/coin bonce, and return bricks that were hit - const {x, y, previousx, previousy, hitSinceBounce} = 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(ballOrCoin, 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; - - let vhit = 0, hhit = 0; - - - if (coin.x < (offsetX > ballSize ? offsetX : 0) + radius) { - coin.x = offsetX + radius; - coin.vx *= -1; - hhit = 1; - } - if (coin.y < radius) { - coin.y = radius; - coin.vy *= -1; - vhit = 1; - } - if (coin.x > canvas.width - (offsetX > ballSize ? offsetX : 0) - radius) { - coin.x = canvas.width - offsetX - 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 (running) { - - levelTime += currentTick - lastTick; - // How many time 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) { - incrementRunStatistics('level_time', levelTime) - - 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; - - // 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.catch_all_coins) { - decreaseCombo(coin.points * perks.catch_all_coins, 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; - - 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)); - - 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; - } - } - } - }); - } - } - - 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, 2 * perks.ball_attract_ball) - } - } - if (perks.puck_repulse_ball) { - repulse(ball, { - x: puck, - y: gameZoneHeight, - color: currentLevelInfo().black_puck ? '#000' : '#FFF', - }, perks.puck_repulse_ball, false) - } - - - 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 (!ball.hitSinceBounce) { - incrementRunStatistics('miss') - levelMisses++; - flashes.push({ - type: "text", - text: 'miss', - time: levelTime, - color: ball.color, - x: ball.x, - y: ball.y - ballSize, - duration: 450, - size: puckHeight, - }) - if (ball.bouncesList?.length) { - ball.bouncesList.push({ - x: ball.previousx, - y: ball.previousy - }) - for (si = 0; si < ball.bouncesList.length - 1; si++) { - // segement - const start = ball.bouncesList[si] - const end = ball.bouncesList[si + 1] - const distance = distanceBetween(start, end) - - const parts = distance / 30 - for (var i = 0; i < parts; i++) { - flashes.push({ - type: "particle", - duration: 200, - ethereal: true, - time: levelTime, - size: coinSize / 2, - color: ball.color, - x: start.x + (i / (parts - 1)) * (end.x - start.x), - y: start.y + (i / (parts - 1)) * (end.y - start.y), - vx: (Math.random() - 0.5) * baseSpeed, - vy: (Math.random() - 0.5) * baseSpeed, - }); - } - } - } - - } - incrementRunStatistics('puck_bounces') - ball.hitSinceBounce = 0; - ball.piercedSinceBounce = 0; - ball.bouncesList = [{ - x: ball.previousx, - y: ball.previousy - }] - } - - if (ball.y > gameZoneHeight + ballSize / 2 && running) { - ball.destroyed = true; - if (!balls.find((b) => !b.destroyed)) { - if (perks.extra_life) { - perks.extra_life--; - resetBalls(); - sounds.revive(); - pause() - 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 wasABomb = bricks[hitBrick] === "black"; - explodeBrick(hitBrick, ball, false); - - if (perks.sapper && !wasABomb) { - bricks[hitBrick] = "black"; - } - } - - 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: ball.color, - x: ball.x, - y: ball.y, - vx: (Math.random() - 0.5) * baseSpeed, - vy: (Math.random() - 0.5) * baseSpeed, - }); - ball.sparks = 0; - } - } -} - - -let runStatistics = {}; - -function resetRunStatistics() { - runStatistics = { - started: Date.now(), - ended: null, - hadOverrides, - width: window.innerWidth, - height: window.innerHeight, - easy: isSettingOn('easy'), - color_blind: isSettingOn('color_blind'), - } -} - - -function incrementRunStatistics(key, amount = 1) { - runStatistics[key + '_total'] = (runStatistics[key + '_total'] || 0) + amount - runStatistics[key + '_lvl_' + currentLevel] = (runStatistics[key + '_lvl_' + currentLevel] || 0) + amount -} - -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 gameOver(title, intro) { - if (!running) return; - pause() - - runStatistics.ended = Date.now() - - const {stats} = getLevelStats(); - - scoreStory.push(`During level ${currentLevel + 1} ${stats}`); - if (balls.find((b) => !b.destroyed)) { - scoreStory.push(`You cleared the last level and won. `); - } else { - scoreStory.push(`You dropped the ball and finished your run early. `); - } - - try { - // Stores only last 100 runs - const runsHistory = JSON.parse(localStorage.getItem('breakout_71_history') || '[]').slice(0, 99).concat([runStatistics]) - - // Generate some histogram - - localStorage.setItem('breakout_71_history', '
' + JSON.stringify(runsHistory, null, 2) + '
') - } catch { - } - - 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 = list.findLast(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: ` - - ${scoreStory.map((t) => "

" + t + "

").join("")} - ` - }).then(() => restart()); -} - -function explodeBrick(index, ball, isExplosion) { - const color = bricks[index]; - if (color === 'black') { - delete bricks[index]; - const x = brickCenterX(index), y = brickCenterY(index); - - incrementRunStatistics('explosion', 1) - sounds.explode(ball.x); - const {col, row} = getRowCol(index); - 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++; - } else if (color) { - // Flashing is take care of by the tick loop - const x = brickCenterX(index), y = brickCenterY(index); - - bricks[index] = ""; - - levelSpawnedCoins += combo; - - incrementRunStatistics('spawned_coins', combo) - - coins = coins.filter((c) => !c.destroyed); - for (let i = 0; i < combo; i++) { - // Avoids saturating the canvas with coins - if (coins.length > MAX_COINS * (isSettingOn("basic") ? 0.5 : 1)) { - // Just pick a random one - coins[Math.floor(Math.random() * coins.length)].points++; - continue; - } - - const coord = { - x: x + (Math.random() - 0.5) * (brickWidth - coinSize), - y: y + (Math.random() - 0.5) * (brickWidth - coinSize), - }; - - coins.push({ - points: 1, - color, ...coord, - previousx: coord.x, - previousy: coord.y, - vx: ball.vx * (0.5 + Math.random()), - vy: ball.vy * (0.5 + Math.random()), - sx: 0, - sy: 0, - weight: 0.8 + Math.random() * 0.2 - }); - } - - combo += perks.streak_shots + perks.catch_all_coins + perks.sides_are_lava + perks.top_is_lava + perks.picky_eater; - - if (!isExplosion) { - // color change - if ((perks.picky_eater || perks.pierce_color) && color !== ball.color) { - // reset streak - if (perks.picky_eater) resetCombo(ball.x, ball.y); - ball.color = color; - } else { - sounds.comboIncreaseMaybe(ball.x, 1); - } - } - ball.hitSinceBounce++; - - flashes.push({ - type: "ball", duration: 40, time: levelTime, size: brickWidth, color: color, x, y, - }); - spawnExplosion(5 + combo, x, y, color, 100, coinSize / 2); - } -} - -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 += score.toString(); - scoreDisplay.innerText = scoreInfo; - - - if (!isSettingOn("basic") && !level.color && level.svg && !level.black_puck) { - - ctx.globalCompositeOperation = "source-over"; - ctx.globalAlpha = 0.7 - ctx.fillStyle = "#000"; - ctx.fillRect(0, 0, width, height); - - ctx.globalCompositeOperation = "multiply"; - ctx.globalAlpha = 0.3; - const gradient = ctx.createLinearGradient(offsetX, gameZoneHeight - puckHeight, offsetX, height - puckHeight * 3,); - gradient.addColorStop(0, "black"); - gradient.addColorStop(1, "transparent"); - ctx.fillStyle = gradient; - ctx.fillRect(offsetX, gameZoneHeight - puckHeight * 3, gameZoneWidth, puckHeight * 4,); - - 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, ball.color, 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); - } - - }); - - ctx.globalAlpha = 0.9; - ctx.globalCompositeOperation = "multiply"; - if (level.svg && 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 - bgctx.fillRect(0, 0, canvas.width, canvas.height) - bgctx.fillStyle = ctx.createPattern(background, "repeat"); - bgctx.fillRect(0, 0, width, height); - console.log("redrew context") - } - ctx.drawImage(backgroundCanvas, 0, 0) - - - } - } else { - - ctx.globalCompositeOperation = "source-over"; - ctx.globalAlpha = 1 - 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); - } - }); - } - - if (combo > baseCombo()) { - // The red should still be visible on a white bg - ctx.globalCompositeOperation = !level.color && level.svg ? "screen" : 'source-over'; - ctx.globalAlpha = (2 + combo - baseCombo()) / 50; - - if (perks.top_is_lava) { - drawRedGradientSquare(ctx, offsetX, 0, gameZoneWidth, ballSize, 0, 0, 0, ballSize,); - } - if (perks.sides_are_lava) { - drawRedGradientSquare(ctx, offsetX, 0, ballSize, gameZoneHeight, 0, 0, ballSize, 0,); - drawRedGradientSquare(ctx, offsetX + gameZoneWidth - ballSize, 0, ballSize, gameZoneHeight, ballSize, 0, 0, 0,); - } - if (perks.catch_all_coins) { - drawRedGradientSquare(ctx, offsetX, gameZoneHeight - ballSize, gameZoneWidth, ballSize, 0, ballSize, 0, 0,); - } - if (perks.streak_shots) { - drawRedGradientSquare(ctx, puck - puckWidth / 2, gameZoneHeight - puckHeight - ballSize, puckWidth, ballSize, 0, ballSize, 0, 0,); - } - if (perks.picky_eater) { - let okColors = new Set(balls.map((b) => b.color)); - - bricks.forEach((type, index) => { - if (!type || type === "black" || okColors.has(type)) return; - const x = brickCenterX(index), y = brickCenterY(index); - drawFuzzyBall(ctx, "red", brickWidth, x, y); - }); - } - ctx.globalAlpha = 1; - } - - ctx.globalAlpha = 1; - ctx.globalCompositeOperation = "source-over"; - const lastExplosionDelay = Date.now() - lastexplosion + 5; - const shaked = lastExplosionDelay < 200; - if (shaked) { - ctx.translate((Math.sin(Date.now()) * 50) / lastExplosionDelay, (Math.sin(Date.now() + 36) * 50) / lastExplosionDelay,); - } - - 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: 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, level.color || 'black'); - }); - - - // 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, ball.color, ballSize, ball.x, ball.y); - // 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"; - drawPuck(ctx, puckColor, puckWidth, puckHeight) - - if (combo > 1) { - - ctx.globalCompositeOperation = "source-over"; - drawText(ctx, "x " + combo, !level.black_puck ? '#000' : '#FFF', puckHeight, { - x: puck, y: gameZoneHeight - puckHeight / 2, - }); - } - // Borders - ctx.fillStyle = puckColor; - ctx.globalCompositeOperation = "source-over"; - if (offsetX > ballSize) { - ctx.fillRect(offsetX, 0, 1, height); - ctx.fillRect(width - offsetX - 1, 0, 1, height); - } - if (isSettingOn("mobile-mode")) { - ctx.fillRect(offsetX, gameZoneHeight, gameZoneWidth, 1); - if (!running) { - drawText(ctx, "Keep pressing here to play", puckColor, puckHeight, { - x: canvas.width / 2, y: gameZoneHeight + (canvas.height - gameZoneHeight) / 2, - }); - } - } - - if (shaked) { - ctx.resetTransform(); - } -} - -let cachedBricksRender = document.createElement("canvas"); -let cachedBricksRenderKey = null; - -function renderAllBricks(destinationCtx) { - ctx.globalAlpha = 1; - - const level = currentLevelInfo(); - - const newKey = gameZoneWidth + "_" + bricks.join("_") + bombSVG.complete; - 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 - bricks.forEach((color, index) => { - const x = brickCenterX(index), y = brickCenterY(index); - - if (!color) return; - drawBrick(ctx, color, x, y, level.squared || false); - 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) { - - 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,); - - -} - -function drawBall(ctx, color, width, x, y) { - const key = "ball" + color + "_" + width; - - if (!cachedGraphics[key]) { - const can = document.createElement("canvas"); - const size = Math.round(width); - can.width = size; - can.height = size; - - const canctx = can.getContext("2d"); - canctx.beginPath(); - canctx.arc(size / 2, size / 2, Math.round(size / 2), 0, 2 * Math.PI); - canctx.fillStyle = color; - canctx.fill(); - cachedGraphics[key] = can; - } - ctx.drawImage(cachedGraphics[key], Math.round(x - width / 2), Math.round(y - width / 2),); -} - -function drawCoin(ctx, color, width, ball, bg) { - const key = "coin with halo" + "_" + color + "_" + width + '_' + bg; - - const size = width * 3; - 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, width / 2, 0, 2 * Math.PI); - canctx.fillStyle = color; - canctx.fill(); - - canctx.strokeStyle = bg; - canctx.stroke(); - - cachedGraphics[key] = can; - } - ctx.drawImage(cachedGraphics[key], Math.round(ball.x - size / 2), Math.round(ball.y - size / 2),); -} - -function drawFuzzyBall(ctx, color, width, x, y) { - const key = "fuzzy-circle" + color + "_" + width; - - 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, x, y, squared) { - 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 + "_" + width + "_" + height + '_' + squared - // "_" + - // isSettingOn("rounded-bricks"); - - if (!cachedGraphics[key]) { - const can = document.createElement("canvas"); - can.width = width; - can.height = height; - const canctx = can.getContext("2d"); - - - if (squared) { - - canctx.fillStyle = color; - canctx.fillRect(0, 0, width, height); - } else { - - const bord = Math.floor(brickWidth / 6); - canctx.strokeStyle = color; - canctx.lineJoin = "round"; - canctx.lineWidth = bord * 1.5; - canctx.strokeRect(bord, bord, width - bord * 2, height - bord * 2); - - canctx.fillStyle = color; - canctx.fillRect(bord, bord, width - bord * 2, height - bord * 2); - } - - 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 drawRedGradientSquare(ctx, x, y, width, height, redX, redY, blackX, blackY ) { - const key = "gradient" + width + "_" + height + "_" + redX + "_" + redY + "_" + blackX + "_" + blackY ; - - if (!cachedGraphics[key]) { - const can = document.createElement("canvas"); - can.width = width; - can.height = height; - const canctx = can.getContext("2d"); - - const gradient = canctx.createLinearGradient(redX, redY, blackX, blackY); - gradient.addColorStop(0, "rgba(255,0,0,1)"); - gradient.addColorStop(1, "rgba(255,0,0,0)"); - canctx.fillStyle = gradient; - canctx.fillRect(0, 0, width, height); - cachedGraphics[key] = can; - } - ctx.drawImage(cachedGraphics[key], 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}) { - const key = "text" + text + "_" + color + "_" + fontSize; - - 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 = "center"; - canctx.textBaseline = "middle"; - canctx.font = fontSize + "px monospace"; - - canctx.fillText(text, can.width / 2, can.height / 2, can.width); - - cachedGraphics[key] = can; - } - ctx.drawImage(cachedGraphics[key], 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); - }, explode: (pan) => { - if (!isSettingOn("sound")) return; - createExplosionSound(pixelsToPan(pan)); - }, revive: () => { - if (!isSettingOn("sound")) return; - createRevivalSound(500); - }, coinCatch(pan) { - if (!isSettingOn("sound")) return; - createSingleBounceSound(440, pixelsToPan(pan), .8) - } -}; - -// How to play the code on the leftconst context = new window.AudioContext(); -let audioContext, delayNode; - -function getAudioContext() { - if (!audioContext) { - audioContext = new (window.AudioContext || window.webkitAudioContext)(); - } - return audioContext; -} - -function createSingleBounceSound(baseFreq = 800, pan = 0.5, volume = 1, duration = 0.1,) { - 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 = "sine"; - 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); - - // 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); - - // 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.8, 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); - - // 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() - } -}); - -const scoreDisplay = document.getElementById("score"); - - -function asyncAlert({ - title, - text, - actions = [{text: "OK", value: "ok", help: ""}], - allowClose = true, - textAfterButtons = '' - }) { - 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) - }) - 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}) => { - const button = document.createElement("button"); - let checkMark = '' - if (max) { - checkMark += '' - for (let i = 0; i < max; i++) { - checkMark += ''; - } - checkMark += '' - } - button.innerHTML = `${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); - }); -} - -// 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(); - running = false - const cb = await asyncAlert({ - title: `You scored ${score} points so far`, text: ` -

You are playing level ${currentLevel + 1} out of ${max_levels()}.

- ${scoreStory.map((t) => "

" + t + "

").join("")} - `, allowClose: true, actions: [{ - text: "New run", 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.`, - }, "mobile-mode": { - default: window.innerHeight > window.innerWidth, - name: `Mobile mode`, - help: `Leaves space for your thumb.`, - afterChange() { - fitSize(); - }, - }, - basic: { - default: false, name: `Fast mode`, help: `Simpler graphics for older devices.`, - }, - "easy": { - default: false, name: `Easy mode`, help: `Slower ball as starting perk.`, restart: true, - }, "color_blind": { - default: false, name: `Color blind mode`, help: `Removes mechanics about colors.`, restart: true, - }, -}; - -async function openSettingsPanel() { - pause() - - const optionsList = []; - for (const key in options) { - optionsList.push({ - 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: 'Unlocks', - help: "See and try what you've unlocked", - async value() { - const ts = getTotalScore() - const tryOn = await asyncAlert({ - title: 'Your unlocks', - text: ` -

Your high score is ${highScore}. In total, you've cought ${ts} coins. Click an upgrade below to start a test run with it (stops after 1 level).

- `, - actions: [...upgrades - .sort((a, b) => a.threshold - b.threshold) - .map(({ - name, - max, - help, id, - threshold - }) => ({ - text: name, - help: help + (ts >= threshold ? '' : `(${threshold} coins)`), - disabled: ts < threshold, - value: {perks: {[id]: 1}} - }) - ) - - , - ...allLevels - .sort((a, b) => a.threshold - b.threshold) - .map((l, li) => { - const avaliable = ts >= l.threshold - return ({ - text: l.name, - help: `A ${l.size}x${l.size} level with ${l.bricks.filter(i => i).length} bricks` + (avaliable ? '' : `(${l.threshold} coins)`), - disabled: !avaliable, - value: {level: l.name} - - }) - }) - ] - - - , - allowClose: true, - }) - if (tryOn) { - nextRunOverrides = tryOn - restart() - } - } - }, - - ...optionsList, - - (window.screenTop || window.screenY) && { - text: "Fullscreen", - help: "Might not work on some machines", - value() { - const docel = document.documentElement - if (docel.requestFullscreen) { - docel.requestFullscreen(); - } else if (docel.webkitRequestFullscreen) { - docel.webkitRequestFullscreen(); - } - } - }, - { - 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 - - Google Play - - itch.io - -

- ` - }) - 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(${(levelTime / 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 - // TODO - - 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 - - if (!isSettingOn('basic')) { - 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 - - if (!isSettingOn('basic')) { - 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, - }) - } - -} - -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 757f13d..24742a2 100644 --- a/app/src/main/assets/index.html +++ b/app/src/main/assets/index.html @@ -1,21 +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 98f24f6..0000000 --- a/app/src/main/assets/levels.js +++ /dev/null @@ -1,6778 +0,0 @@ -let allLevels=[ - { - "name": "71 mini", - "size": 5, - "bricks": [ - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - "", - "#ffd300", - "#e32119", - "", - "", - "#ffd300", - "#e32119", - "#e32119", - "", - "", - "#ffd300", - "", - "#e32119", - "", - "", - "", - "#e32119", - "#e32119", - "#e32119" - ], - "svg": "", - "color": "" - }, - { - "name": "Butterfly", - "bricks": [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - "#6262EA", - "#6262EA", - "", - "#5DA3EA", - "", - "#5DA3EA", - "", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "", - "#5DA3EA", - "", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#5DA3EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "", - "#6262EA", - "#6262EA", - "#6262EA", - "#5DA3EA", - "#6262EA", - "#6262EA", - "#6262EA", - "", - "", - "", - "", - "#6262EA", - "#5DA3EA", - "#6262EA", - "", - "", - "", - "", - "#6262EA", - "#6262EA", - "#6262EA", - "#5DA3EA", - "#6262EA", - "#6262EA", - "#6262EA", - "", - "", - "#6262EA", - "#6262EA", - "", - "#5DA3EA", - "", - "#6262EA", - "#6262EA", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "size": 9, - "svg": "", - "focus": false, - "color": "" - }, - { - "name": "Castle", - "size": 7, - "bricks": [ - "#E67070", - "", - "#E67070", - "", - "#E67070", - "", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "black", - "black", - "black", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "black", - "black", - "black", - "#E67070", - "#E67070", - "#5DA3EA", - "#5DA3EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#6262EA", - "#5DA3EA", - "#6262EA", - "#5DA3EA", - "#6262EA", - "#5DA3EA" - ], - "svg": "", - "focus": false - }, - { - "name": "Eyes", - "size": 9, - "bricks": [ - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "", - null, - "#5DA3EA", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - null, - "#5DA3EA", - "white", - "#e32119", - "#e32119", - "white", - "#5DA3EA", - "#5DA3EA", - "white", - null, - "#5DA3EA", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - null, - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "", - null, - "", - "", - "", - "#5DA3EA", - "", - "", - "", - "", - null, - "", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "", - "", - null, - "", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "", - "", - null, - null, - null, - "#5DA3EA", - null, - "#5DA3EA" - ], - "svg": "", - "color": "", - "focus": false, - "squared": true - }, - { - "name": "Creeper", - "size": 10, - "bricks": [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#59EEA3", - "#59EEA3", - "#A1F051", - "#A1F051", - "#59EEA3", - "#59EEA3", - "#A1F051", - "#A1F051", - "", - "", - "#59EEA3", - "#A1F051", - "#59EEA3", - "#59EEA3", - "#A1F051", - "#59EEA3", - "#A1F051", - "#59EEA3", - "", - "", - "#A1F051", - "black", - "black", - "#59EEA3", - "#59EEA3", - "black", - "black", - "#59EEA3", - "", - "", - "#59EEA3", - "black", - "black", - "#A1F051", - "#59EEA3", - "black", - "black", - "#59EEA3", - "", - "", - "#A1F051", - "#59EEA3", - "#59EEA3", - "black", - "black", - "#A1F051", - "#A1F051", - "#59EEA3", - "", - "", - "#59EEA3", - "#59EEA3", - "black", - "black", - "black", - "black", - "#59EEA3", - "#A1F051", - "", - "", - "#A1F051", - "#A1F051", - "black", - "black", - "black", - "black", - "#59EEA3", - "#A1F051", - "", - "", - "#59EEA3", - "#A1F051", - "black", - "#59EEA3", - "#59EEA3", - "black", - "#A1F051", - "#59EEA3", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "focus": false - }, - { - "name": "Blocky Stairs", - "size": 8, - "bricks": [ - "#5DA3EA", - "#5DA3EA", - null, - null, - null, - null, - null, - null, - "#5DA3EA", - "#5DA3EA", - null, - null, - null, - null, - null, - null, - "#6262EA", - "#6262EA", - "#5DA3EA", - "#5DA3EA", - null, - null, - null, - null, - "#6262EA", - "#6262EA", - "#5DA3EA", - "#5DA3EA", - null, - null, - null, - null, - "#A664E8", - "#A664E8", - "#6262EA", - "#6262EA", - "#5DA3EA", - "#5DA3EA", - null, - null, - "#A664E8", - "#A664E8", - "#6262EA", - "#6262EA", - "#5DA3EA", - "#5DA3EA", - null, - null, - "#E869E8", - "#E869E8", - "#A664E8", - "#A664E8", - "#6262EA", - "#6262EA", - "#5DA3EA", - "#5DA3EA", - "#E869E8", - "#E869E8", - "#A664E8", - "#A664E8", - "#6262EA", - "#6262EA", - "#5DA3EA", - "#5DA3EA" - ], - "svg": "", - "focus": false - }, - { - "name": "Dots", - "size": 9, - "bricks": [ - "#6262EA", - null, - "#5DA3EA", - null, - "#5BECEC", - null, - "#59EEA3", - null, - "#53EE53", - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - "#6262EA", - null, - "#5DA3EA", - null, - "#5BECEC", - null, - "#59EEA3", - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - "#A664E8", - null, - "#6262EA", - null, - "#5DA3EA", - null, - "#5BECEC", - null, - "#59EEA3", - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - "#A664E8", - null, - "#6262EA", - null, - "#5DA3EA", - null, - "#5BECEC", - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - "#E869E8", - null, - "#A664E8", - null, - "#6262EA", - null, - "#5DA3EA", - null, - "#5BECEC" - ], - "svg": "", - "focus": false - }, - { - "name": "Lines", - "size": 9, - "bricks": [ - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "", - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - null, - null, - null, - null, - null, - null, - null, - null, - null, - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - null, - null, - null, - null, - null, - null, - null, - null, - null, - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC" - ], - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Heart", - "size": 15, - "bricks": [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - null, - "", - "", - "", - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - "", - "", - "", - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - "", - "", - null, - "", - "", - "#ab0c0c", - "#F44848", - "#F44848", - "#F44848", - "#ab0c0c", - "", - "#ab0c0c", - "#F44848", - "#F44848", - "#F44848", - "#ab0c0c", - "", - null, - "", - "#ab0c0c", - "#F44848", - "white", - "white", - "#F44848", - "#F44848", - "#ab0c0c", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#ab0c0c", - null, - "", - "#ab0c0c", - "#F44848", - "white", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#ab0c0c", - null, - "", - "#ab0c0c", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#ab0c0c", - null, - "", - "#ab0c0c", - "#F44848", - "white", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#ab0c0c", - null, - "", - "", - "#ab0c0c", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#ab0c0c", - "", - null, - "", - "", - "", - "#ab0c0c", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#ab0c0c", - "", - "", - null, - "", - "", - "", - "", - "#ab0c0c", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#ab0c0c", - "", - "", - "", - null, - "", - "", - "", - "", - "", - "#ab0c0c", - "#F44848", - "#F44848", - "#F44848", - "#ab0c0c", - "", - "", - "", - "", - null, - "", - "", - "", - "", - "", - "", - "#ab0c0c", - "#F44848", - "#ab0c0c", - "", - "", - "", - "", - "", - null, - "", - "", - "", - "", - "", - "", - "", - "#ab0c0c", - "", - "", - "", - "", - "", - "", - null, - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - "" - ], - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Swiss", - "size": 7, - "bricks": [ - "", - null, - null, - null, - null, - null, - null, - null, - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - null, - null, - "#ab0c0c", - "#ab0c0c", - "white", - "#ab0c0c", - "#ab0c0c", - null, - null, - "#ab0c0c", - "white", - "white", - "white", - "#ab0c0c", - null, - null, - "#ab0c0c", - "#ab0c0c", - "white", - "#ab0c0c", - "#ab0c0c", - null, - null, - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - "#ab0c0c" - ], - "svg": "", - "color": "", - "focus": false, - "squared": true - }, - { - "name": "Germany", - "size": 6, - "bricks": [ - "", - null, - null, - null, - null, - null, - null, - "#231f20", - "#231f20", - "#231f20", - "#231f20", - null, - null, - "#e32119", - "#e32119", - "#e32119", - "#e32119", - null, - null, - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300" - ], - "svg": "", - "squared": false, - "color": "#ffffff", - "focus": false, - "black_puck": true - }, - { - "name": "France", - "size": 8, - "bricks": [ - "", - "", - "", - "", - "", - "", - "", - null, - "", - "#5DA3EA", - "#5DA3EA", - "white", - "white", - "#e32119", - "#e32119", - null, - "", - "#5DA3EA", - "#5DA3EA", - "white", - "white", - "#e32119", - "#e32119", - null, - "", - "#5DA3EA", - "#5DA3EA", - "white", - "white", - "#e32119", - "#e32119", - null, - "", - "#5DA3EA", - "#5DA3EA", - "white", - "white", - "#e32119", - "#e32119", - null, - "", - "#5DA3EA", - "#5DA3EA", - "white", - "white", - "#e32119", - "#e32119", - null, - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Smiley", - "size": 8, - "bricks": [ - "", - null, - null, - null, - null, - null, - null, - null, - null, - "#6262EA", - "white", - null, - null, - "#6262EA", - "white", - null, - null, - "white", - "white", - null, - null, - "white", - "white", - null, - null, - null, - null, - null, - null, - null, - null, - null, - "", - "", - "", - "", - "", - "", - "", - null, - null, - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - null, - null, - null, - "#e32119", - "#e32119", - "#e32119", - "#e32119", - null, - null, - null, - null, - "", - "" - ], - "svg": "", - "focus": false, - "color": "" - }, - { - "name": "Labirynth", - "size": 11, - "bricks": [ - "", - "", - "", - "", - "", - "", - "", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#F44848", - "", - "#F44848", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "", - "#F44848", - "", - "", - null, - "", - "", - "", - "", - "", - "#5DA3EA", - "", - "", - "", - "#F44848", - null, - "", - "#F44848", - "#5DA3EA", - "#5DA3EA", - "", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - null, - "", - "", - "", - "#5DA3EA", - "", - "", - "", - "", - "", - "#F44848", - null, - "", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "", - "#F44848", - "", - "#F44848", - "", - "", - null, - "", - "#5DA3EA", - "", - "", - "", - "#5DA3EA", - "", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "", - "#5DA3EA", - "", - "#F44848", - "", - "#5DA3EA", - "", - "", - "", - "", - "#5DA3EA", - "#F44848", - "#5DA3EA", - "", - "#5DA3EA", - "", - "#5DA3EA", - "", - "#F44848", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "", - "", - "", - "#5DA3EA", - "", - "#5DA3EA", - "", - "", - "", - "", - "", - "#F44848", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#F44848" - ], - "svg": "", - "focus": false - }, - { - "name": "Temple", - "size": 11, - "bricks": [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "white", - "white", - "white", - "", - "", - "", - "", - "", - "", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "", - "", - "", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "", - "", - "", - "#5DA3EA", - "", - "#5DA3EA", - "", - "#5DA3EA", - "", - "#5DA3EA", - "", - "", - "", - "", - "#6262EA", - "", - "#6262EA", - "", - "#6262EA", - "", - "#6262EA", - "", - "", - "", - "", - "#A664E8", - "", - "#A664E8", - "", - "#A664E8", - "", - "#A664E8", - "", - "", - "", - "", - "#E869E8", - "", - "#E869E8", - "", - "#E869E8", - "", - "#E869E8", - "", - "", - "", - "", - "#E66BA8", - "", - "#E66BA8", - "", - "#E66BA8", - "", - "#E66BA8", - "", - "", - "", - "", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "", - "", - "", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "" - ], - "svg": "", - "focus": false, - "color": "" - }, - { - "name": "Pacman", - "size": 12, - "bricks": [ - "", - "", - "", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - null, - "", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - null, - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "black", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - null, - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - null, - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - null, - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - "", - "", - null, - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - "#F44848", - "", - "#F44848", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - null, - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - null, - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - null, - "", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - null, - null, - null, - null, - null, - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300" - ], - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Ship", - "size": 11, - "bricks": [ - "", - "", - "", - "", - "#E67070", - "white", - "white", - "", - "", - "", - null, - "", - "", - "", - "", - "#E67070", - "white", - "white", - "white", - "", - "", - null, - "", - "", - "", - "", - "#E67070", - "white", - "white", - "white", - "", - "", - null, - "", - "", - "", - "", - "#E67070", - "", - "", - "", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "", - "#F29E4A", - "black", - "#F29E4A", - "black", - "#F29E4A", - "black", - "#F29E4A", - "black", - "#F29E4A", - "#F29E4A", - "", - "", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - null, - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#333", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#333", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#333", - "#333", - "#6262EA", - "#6262EA", - "#6262EA", - "#333", - "#333", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA" - ], - "svg": "", - "focus": false - }, - { - "name": "We come in peace", - "size": 13, - "bricks": [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - null, - "", - "", - "", - "#5BECEC", - "", - "", - "", - "", - "", - "#5BECEC", - "", - "", - null, - "", - "", - "", - "", - "#5BECEC", - "", - "", - "", - "#5BECEC", - "", - "", - "", - null, - "", - "", - "", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "", - "", - null, - "", - "", - "#5BECEC", - "#5BECEC", - "black", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "black", - "#5BECEC", - "#5BECEC", - "", - null, - "", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - null, - "", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - null, - "", - "#5BECEC", - "", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "", - "#5BECEC", - null, - "", - "#5BECEC", - "", - "#5BECEC", - "", - "", - "", - "", - "", - "#5BECEC", - "", - "#5BECEC", - null, - "", - "", - "", - "", - "#5BECEC", - "#5BECEC", - "", - "#5BECEC", - "#5BECEC", - "", - "", - "", - null, - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - null, - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "focus": false, - "color": "" - }, - { - "name": "Space mushroom", - "size": 10, - "bricks": [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - null, - "", - "", - "", - "", - "white", - "white", - "", - "", - "", - null, - "", - "", - "", - "white", - "white", - "white", - "white", - "", - "", - null, - "", - "", - "white", - "white", - "white", - "white", - "white", - "white", - "", - null, - "", - "white", - "white", - "black", - "white", - "white", - "black", - "white", - "white", - null, - "", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - null, - "", - "", - "", - "white", - "", - "", - "white", - "", - "", - null, - "", - "", - "white", - "", - "white", - "white", - "", - "white", - "", - null, - "", - "white", - "", - "white", - "", - "", - "white", - "", - "white" - ], - "svg": "", - "focus": false, - "color": "" - }, - { - "name": "Wololo", - "size": 9, - "bricks": [ - "", - "", - "", - "", - "white", - "white", - "", - "#F29E4A", - "#F29E4A", - "white", - "", - "", - "", - "white", - "white", - "", - "", - "#F29E4A", - "white", - "white", - "", - "", - "white", - "", - "", - "", - "#F29E4A", - "white", - "white", - "white", - "#6262EA", - "#6262EA", - "#6262EA", - "white", - "", - "white", - "white", - "white", - "", - "white", - "#6262EA", - "white", - "", - "white", - "#F29E4A", - "white", - "", - "", - "white", - "white", - "#6262EA", - "", - "", - "#F29E4A", - "white", - "", - "", - "#6262EA", - "#6262EA", - "#6262EA", - "", - "", - "#F29E4A", - "", - "", - "", - "white", - "", - "white", - "", - "", - "#F29E4A", - "", - "", - "", - "white", - "", - "white", - "", - "", - "#F29E4A" - ], - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Small heart", - "size": 15, - "bricks": [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - "", - "", - "", - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - "", - "", - "", - "#ab0c0c", - "#e32119", - "white", - "white", - "#e32119", - "#ab0c0c", - "", - "#ab0c0c", - "white", - "white", - "#e32119", - "#e32119", - "#ab0c0c", - "", - "", - "#ab0c0c", - "white", - "white", - "#e32119", - "#e32119", - "#e32119", - "#ab0c0c", - "white", - "white", - "#e32119", - "#e32119", - "#e32119", - "#ab0c0c", - "", - "", - "#ab0c0c", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#ab0c0c", - "", - "", - "#ab0c0c", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#ab0c0c", - "", - "", - "", - "#ab0c0c", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#ab0c0c", - "", - "", - "", - "", - "", - "#ab0c0c", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#ab0c0c", - "", - "", - "", - "", - "", - "", - "", - "#ab0c0c", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#ab0c0c", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#ab0c0c", - "#e32119", - "#e32119", - "#e32119", - "#ab0c0c", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#ab0c0c", - "#e32119", - "#ab0c0c", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#ab0c0c", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "focus": false, - "color": "" - }, - { - "name": "Eye", - "size": 9, - "bricks": [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#231f20", - "#231f20", - "#231f20", - "", - "", - "", - "", - "", - "#231f20", - "white", - "white", - "white", - "#231f20", - "", - "", - "", - "#231f20", - "white", - "#6262EA", - "#6262EA", - "#6262EA", - "white", - "#231f20", - "", - "#231f20", - "white", - "white", - "#6262EA", - "black", - "#6262EA", - "white", - "white", - "#231f20", - "", - "#231f20", - "white", - "#6262EA", - "#6262EA", - "#6262EA", - "white", - "#231f20", - "", - "", - "", - "#231f20", - "white", - "white", - "white", - "#231f20", - "", - "", - "", - "", - "", - "#231f20", - "#231f20", - "#231f20", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "color": "#99c1f1", - "focus": false - }, - { - "name": "Enderman", - "size": 10, - "bricks": [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - "", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "", - "", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "", - "", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "", - "", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "", - "", - "#A664E8", - "#A664E8", - "#A664E8", - "#231f20", - "#231f20", - "#A664E8", - "#A664E8", - "#A664E8", - "", - "", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "", - "", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "", - "", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "color": "#ffffff", - "focus": false, - "squared": true, - "black_puck": true - }, - { - "name": "Mushroom", - "size": 16, - "bricks": [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "white", - "white", - "", - "", - "", - "", - "", - "", - "", - "", - "white", - "white", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "white", - "white", - "white", - "white", - "", - "", - "", - "", - "", - "white", - "white", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "white", - "white", - "white", - "white", - "", - "", - "", - "", - "white", - "#e32119", - "#e32119", - "white", - "white", - "white", - "white", - "#e32119", - "#e32119", - "white", - "white", - "white", - "", - "", - "", - "#e32119", - "#e32119", - "#e32119", - "white", - "white", - "white", - "white", - "white", - "white", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "", - "", - "#e32119", - "#e32119", - "#e32119", - "white", - "white", - "white", - "white", - "white", - "white", - "#e32119", - "#e32119", - "white", - "white", - "#e32119", - "", - "", - "white", - "#e32119", - "#e32119", - "white", - "white", - "white", - "white", - "white", - "white", - "#e32119", - "white", - "white", - "white", - "white", - "", - "", - "white", - "white", - "#e32119", - "#e32119", - "white", - "white", - "white", - "white", - "#e32119", - "#e32119", - "white", - "white", - "white", - "white", - "", - "", - "white", - "white", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "white", - "white", - "#e32119", - "", - "", - "white", - "#e32119", - "#e32119", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "#e32119", - "#e32119", - "#e32119", - "", - "", - "", - "", - "", - "white", - "white", - "black", - "white", - "white", - "black", - "white", - "white", - "", - "", - "", - "", - "", - "", - "", - "white", - "white", - "white", - "black", - "white", - "white", - "black", - "white", - "white", - "white", - "", - "", - "", - "", - "", - "", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "", - "", - "", - "", - "", - "", - "", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "color": "", - "focus": false, - "black_puck": false, - "squared": true - }, - { - "name": "tulip", - "size": 11, - "bricks": [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - "", - "", - "", - "#ab0c0c", - "", - "#ab0c0c", - "", - "#ab0c0c", - "", - "", - "", - "", - "", - "", - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - "", - "", - "", - "", - "", - "", - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - "", - "", - "", - "", - "", - "", - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - "", - "", - "", - "", - "", - "", - "", - "#ab0c0c", - "#ab0c0c", - "#ab0c0c", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#618227", - "", - "", - "", - "", - "", - "", - "", - "", - "#618227", - "", - "#618227", - "", - "#618227", - "", - "", - "", - "", - "", - "", - "#618227", - "", - "#618227", - "", - "#618227", - "", - "", - "", - "", - "", - "", - "", - "#618227", - "#618227", - "#618227", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#618227", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "focus": false, - "color": "" - }, - { - "name": "Gold chain", - "size": 7, - "bricks": [ - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - "", - "#ffd300", - "black", - "#ffd300", - "", - "", - "", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - "", - "#ffd300", - "black", - "#ffd300", - "", - "", - "", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - "", - "#ffd300", - "black", - "#ffd300", - "", - "", - "", - "", - "#ffd300", - "#ffd300", - "#ffd300" - ], - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Marion", - "size": 9, - "bricks": [ - "#ab0c0c", - "#ab0c0c", - "", - "", - "", - "", - "", - "#ab0c0c", - "#ab0c0c", - "", - "#e32119", - "#ab0c0c", - "", - "", - "", - "#ab0c0c", - "#e32119", - null, - "", - "#e32119", - "#e32119", - "#ab0c0c", - "", - "#ab0c0c", - "#e32119", - "#e32119", - null, - "", - "#e32119", - "#e32119", - "#e32119", - "#ab0c0c", - "#e32119", - "#e32119", - "#e32119", - null, - "", - "#e32119", - "#e32119", - "", - "#e32119", - "", - "#e32119", - "#e32119", - null, - "", - "#e32119", - "#e32119", - "", - "", - "", - "#e32119", - "#e32119", - null, - "", - "#e32119", - "#e32119", - "", - "", - "", - "#e32119", - "#e32119", - null, - "", - "#e32119", - "#e32119", - "", - "", - "", - "#e32119", - "#e32119", - null, - "#ab0c0c", - "#e32119", - "#e32119", - null, - null, - null, - "#e32119", - "#e32119", - "#ab0c0c" - ], - "svg": "", - "focus": false, - "color": "" - }, - { - "name": "Renan", - "size": 9, - "bricks": [ - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "#ffd300", - "#ffd300", - "", - "", - "", - "#ffd300", - "#ffd300", - "", - "", - "#ffd300", - "#ffd300", - "", - "", - "", - "#ffd300", - "#ffd300", - "", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - "#ffd300", - "#ffd300", - "", - "#ffd300", - "#ffd300", - "", - "", - "", - "", - "#ffd300", - "#ffd300", - "", - "", - "#ffd300", - "#ffd300", - "", - "", - "", - "#ffd300", - "#ffd300", - "", - "", - "", - "#ffd300", - "#ffd300", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - "", - "#ffd300", - "#ffd300" - ], - "svg": "", - "focus": false, - "color": "" - }, - { - "name": "pairs", - "size": 8, - "bricks": [ - "#6262EA", - null, - "#6262EA", - null, - "#6262EA", - null, - "#6262EA", - null, - "#6262EA", - null, - "#6262EA", - null, - "#6262EA", - null, - "#6262EA", - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - "#5DA3EA", - null, - "#5DA3EA", - null, - "#5DA3EA", - null, - "#5DA3EA", - null, - "#5DA3EA", - null, - "#5DA3EA", - null, - "#5DA3EA", - null, - "#5DA3EA", - null, - null, - null, - null, - null, - null, - null, - null, - "#6262EA", - null, - "#6262EA", - null, - "#6262EA", - null, - "#6262EA", - null, - "#6262EA", - null, - "#6262EA", - null, - "#6262EA", - null, - "#6262EA" - ], - "svg": "", - "focus": false, - "color": "" - }, - { - "name": "cups", - "size": 11, - "bricks": [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - null, - "#e32119", - "black", - "#e32119", - "", - "#e32119", - "black", - "#e32119", - "", - "#e32119", - "black", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "", - "#e32119", - "#e32119", - "#e32119", - "", - "#e32119", - "#e32119", - "#e32119", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - null, - "#e32119", - "", - "#e32119", - "black", - "#e32119", - "", - "#e32119", - "black", - "#e32119", - "", - "#e32119", - "#e32119", - "", - "#e32119", - "#e32119", - "#e32119", - "", - "#e32119", - "#e32119", - "#e32119", - "", - "#e32119", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - null, - "#e32119", - "black", - "#e32119", - "", - "#e32119", - "black", - "#e32119", - "", - "#e32119", - "black", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "", - "#e32119", - "#e32119", - "#e32119", - "", - "#e32119", - "#e32119", - "#e32119", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "focus": false, - "color": "" - }, - { - "name": "cactus", - "size": 10, - "bricks": [ - null, - "", - "", - "", - "#A1F051", - "", - "", - "", - "", - "", - "", - "", - "#A1F051", - "", - "#A1F051", - "#618227", - "", - "", - "", - "", - "", - "", - "#A1F051", - "", - "#A1F051", - "#618227", - "", - "", - "", - "", - "", - "", - "#618227", - "#618227", - "#618227", - "#618227", - "", - "", - "", - "", - "", - "", - "", - "#618227", - "#618227", - "#618227", - "", - "#A1F051", - "", - "", - "", - "", - "", - "", - "#A1F051", - "#618227", - "#A1F051", - "#618227", - "", - "", - "", - "", - "", - "", - "#A1F051", - "#618227", - "#618227", - "", - "", - "", - "", - "", - "", - "", - "#A1F051", - "#618227", - "", - "", - "", - "", - "", - "", - "", - "", - "#618227", - "#618227", - "", - "", - "", - "", - "", - "", - "", - "", - "#618227", - "#618227", - "", - "", - "", - "", - "" - ], - "svg": "", - "focus": false, - "color": "" - }, - { - "name": "sun", - "size": 11, - "bricks": [ - "", - "", - "", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - null, - "", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - null, - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - null, - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - null, - "#ffd300", - "#ffd300", - "#ffd300", - "white", - "white", - "#ffd300", - "white", - "white", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "#ffd300", - "#ffd300", - "white", - "white", - "white", - "white", - "white", - "#ffd300", - "#ffd300", - null, - "", - "#ffd300", - "#ffd300", - "#ffd300", - "white", - "white", - "white", - "#ffd300", - "#ffd300", - "#ffd300", - null, - "", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - null, - null, - null, - null, - null, - "#ffd300", - "#ffd300", - "#ffd300" - ], - "svg": "", - "focus": false, - "color": "#62a0ea", - "squared": true - }, - { - "name": "Mountain", - "size": 9, - "bricks": [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - "", - "", - "", - "", - "", - "", - "white", - "", - "", - "", - "", - "", - "", - "", - "white", - "white", - "white", - "", - "", - "", - "", - "", - "", - "#A1F051", - "#A1F051", - "white", - "white", - "", - "", - "white", - "", - "#A1F051", - "#A1F051", - "#A1F051", - "#A1F051", - "#A1F051", - "", - "#618227", - "#618227", - "#618227", - "#A1F051", - "#A1F051", - "#A1F051", - "#A1F051", - "#A1F051", - "", - "#618227", - "#618227", - "#618227", - "#618227", - "#A1F051", - "#A1F051", - "#A1F051", - "#A1F051", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#A1F051", - "#A1F051", - "#A1F051", - "#A1F051", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#A1F051", - "#A1F051", - "#A1F051", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Dollar", - "size": 17, - "bricks": [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - "", - "", - "", - "", - "", - "", - "", - "#618227", - "", - "#618227", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#618227", - "", - "#618227", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "", - "", - "", - "", - "", - "", - "", - "#618227", - "#618227", - "", - "", - "#618227", - "", - "#618227", - "", - "", - "#618227", - "#618227", - "", - "", - "", - "", - "", - "", - "#618227", - "#618227", - "", - "", - "#618227", - "", - "#618227", - "", - "", - "#618227", - "#618227", - "", - "", - "", - "", - "", - "", - "#618227", - "#618227", - "", - "", - "#618227", - "", - "#618227", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#618227", - "", - "#618227", - "", - "", - "#618227", - "#618227", - "", - "", - "", - "", - "", - "", - "#618227", - "#618227", - "", - "", - "#618227", - "", - "#618227", - "", - "", - "#618227", - "#618227", - "", - "", - "", - "", - "", - "", - "#618227", - "#618227", - "", - "", - "#618227", - "", - "#618227", - "", - "", - "#618227", - "#618227", - "", - "", - "", - "", - "", - "", - "", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "#618227", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#618227", - "", - "#618227", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#618227", - "", - "#618227", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "color": "#f6d32d", - "focus": false - }, - { - "name": "Waves", - "size": 8, - "bricks": [ - "", - "", - "", - "#6262EA", - "#6262EA", - "#6262EA", - "", - "", - "", - "", - "#6262EA", - "#6262EA", - "#6262EA", - "", - "", - "", - "", - "#6262EA", - "#6262EA", - "#5DA3EA", - "#5DA3EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#5DA3EA", - "#5DA3EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5BECEC", - "#5BECEC", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5BECEC", - "#5BECEC", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC", - "#5BECEC" - ], - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Box", - "size": 8, - "bricks": [ - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - "", - "", - "", - "#ffd300", - "#ffd300", - "", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "", - "#ffd300", - "#ffd300", - "", - "#6262EA", - "", - "", - "#6262EA", - "", - "#ffd300", - "#ffd300", - "", - "#6262EA", - "", - "", - "#6262EA", - "", - "#ffd300", - "#ffd300", - "", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "", - "#ffd300", - "#ffd300", - "", - "", - "", - "", - "", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300" - ], - "svg": "", - "color": "", - "focus": false, - "squared": false - }, - { - "name": "Rose", - "size": 9, - "bricks": [ - "", - "", - "#F44848", - "#F44848", - "", - "", - "", - "", - null, - "", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "", - "", - "", - null, - "", - "#e32119", - "#F44848", - "#F44848", - "#ab0c0c", - "", - "", - "", - null, - "", - "#e32119", - "#e32119", - "#ab0c0c", - "#ab0c0c", - "", - "", - "", - null, - "", - "", - "#e32119", - "#ab0c0c", - "", - "#A1F051", - "", - "", - null, - "", - "", - "", - "#618227", - "", - "#A1F051", - "#618227", - "", - null, - "", - "", - "", - "#618227", - "#618227", - "", - "#618227", - "", - null, - "", - "", - "", - "", - "#618227", - "#618227", - "", - "", - null, - null, - null, - null, - null, - null, - "#618227" - ], - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Time", - "size": 9, - "bricks": [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - "", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "", - "", - "", - "white", - "white", - "white", - "white", - "white", - "", - "", - "", - "", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - "", - "", - "", - "", - "#ffd300", - "", - "", - "", - "", - "", - "", - "", - "", - "#ffd300", - "", - "", - "", - "", - "", - "", - "", - "white", - "#ffd300", - "white", - "", - "", - "", - "", - "", - "white", - "#ffd300", - "#ffd300", - "#ffd300", - "white", - "", - "", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "color": "", - "focus": false, - "squared": false - }, - { - "name": "Watermelon", - "size": 8, - "bricks": [ - "", - "", - "", - "", - "", - "#F44848", - "#618227", - "", - "", - "", - "", - "", - "#F44848", - "#F44848", - "black", - "#618227", - "", - "", - "", - "#F44848", - "black", - "#F44848", - "#F44848", - "#618227", - "", - "", - "#F44848", - "#F44848", - "#F44848", - "#F44848", - "#A1F051", - "#618227", - "", - "#F44848", - "#F44848", - "black", - "#F44848", - "#F44848", - "#618227", - "", - "#F44848", - "black", - "#F44848", - "#F44848", - "#A1F051", - "#A1F051", - "#618227", - "", - "#618227", - "#618227", - "#A1F051", - "#A1F051", - "#618227", - "#618227", - "", - "", - "", - "#618227", - "#618227", - "#618227", - "", - "", - "", - "" - ], - "svg": "", - "focus": false, - "color": "" - }, - { - "name": "Worms", - "size": 13, - "bricks": [ - null, - "", - "", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "", - "", - "", - "", - "", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "", - "", - "", - "", - "white", - "white", - "#E67070", - "white", - "white", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "", - "", - "", - "white", - "black", - "#E67070", - "black", - "white", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "", - "", - "", - "white", - "black", - "#E67070", - "black", - "white", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "", - "", - "", - "white", - "white", - "#E67070", - "white", - "white", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "", - "", - "", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "", - "", - "", - "", - "", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "", - "", - "", - "white", - "white", - "white", - "white", - "white", - "white", - "#E67070", - "#E67070", - "", - "", - "", - "", - "", - "", - "", - "white", - "#E67070", - "#E67070", - "white", - "#E67070", - "", - "", - "#E67070", - "", - "", - "", - "", - "", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "", - "#E67070", - "#E67070" - ], - "svg": "\n \n \n \n \n\n", - "color": "", - "focus": false, - "squared": false - }, - { - "name": "sunrise", - "size": 8, - "bricks": [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#e32119", - "#e32119", - "", - "", - "", - "", - "", - "#e32119", - "#ffd300", - "#ffd300", - "#e32119", - "", - "", - "", - "#e32119", - "#ffd300", - "white", - "white", - "#ffd300", - "#e32119", - "", - "#6262EA", - "#6262EA", - "#5DA3EA", - "#5BECEC", - "#5BECEC", - "#5DA3EA", - "#6262EA", - "#6262EA", - "", - "#6262EA", - "#6262EA", - "#5DA3EA", - "#5DA3EA", - "#6262EA", - "#6262EA", - "", - "", - "", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "focus": false, - "color": "" - }, - { - "name": "crosses", - "size": 13, - "bricks": [ - "#6262EA", - "", - "", - "", - "#6262EA", - "", - "", - "", - "#6262EA", - "", - "", - "", - "#6262EA", - "", - "", - "#A664E8", - "", - "", - "", - "#A664E8", - "", - "", - "", - "#A664E8", - "", - "", - "", - "#A664E8", - "#A664E8", - "#A664E8", - "", - "#A664E8", - "#A664E8", - "#A664E8", - "", - "#A664E8", - "#A664E8", - "#A664E8", - "", - "", - "", - "#A664E8", - "", - "", - "", - "#A664E8", - "", - "", - "", - "#A664E8", - "", - "", - "#E869E8", - "", - "", - "", - "#E869E8", - "", - "", - "", - "#E869E8", - "", - "", - "", - "#E869E8", - "#E869E8", - "#E869E8", - "", - "#E869E8", - "#E869E8", - "#E869E8", - "", - "#E869E8", - "#E869E8", - "#E869E8", - "", - "#E869E8", - "#E869E8", - "#E869E8", - "", - "", - "", - "#E869E8", - "", - "", - "", - "#E869E8", - "", - "", - "", - "#E869E8", - "", - "", - "#E66BA8", - "", - "", - "", - "#E66BA8", - "", - "", - "", - "#E66BA8", - "", - "", - "", - "#E66BA8", - "#E66BA8", - "#E66BA8", - "", - "#E66BA8", - "#E66BA8", - "#E66BA8", - "", - "#E66BA8", - "#E66BA8", - "#E66BA8", - "", - "", - "", - "#E66BA8", - "", - "", - "", - "#E66BA8", - "", - "", - "", - "#E66BA8", - "", - "", - "#E869E8", - "", - "", - "", - "#E869E8", - "", - "", - "", - "#E869E8", - "", - "", - "", - "#E869E8", - "#E869E8", - "#E869E8", - "", - "#E869E8", - "#E869E8", - "#E869E8", - "", - "#E869E8", - "#E869E8", - "#E869E8", - "", - "#E869E8", - "#E869E8", - "#E869E8", - "", - "", - "", - "#E869E8", - "", - "", - "", - "#E869E8", - "", - "", - "", - "#E869E8" - ], - "svg": "", - "color": "", - "focus": false - }, - { - "name": "Negative space", - "size": 9, - "bricks": [ - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "", - "#5DA3EA", - "", - "#5DA3EA", - "", - "#5DA3EA", - "", - "#5DA3EA", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#6262EA", - "", - "#6262EA", - "", - "#6262EA", - "", - "#6262EA", - "", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "", - "#6262EA", - "", - "#6262EA", - "", - "#6262EA", - "", - "#6262EA", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#5DA3EA", - "", - "#5DA3EA", - "", - "#5DA3EA", - "", - "#5DA3EA", - "", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "focus": false - }, - { - "name": "UK", - "size": 11, - "bricks": [ - "#6262EA", - "#e32119", - "#6262EA", - "#6262EA", - "white", - "#e32119", - "white", - "#6262EA", - "#6262EA", - "#e32119", - "#6262EA", - "#6262EA", - "#6262EA", - "#e32119", - "#6262EA", - "white", - "#e32119", - "white", - "#6262EA", - "#e32119", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#e32119", - "white", - "#e32119", - "white", - "#e32119", - "#6262EA", - "#6262EA", - "#6262EA", - "white", - "white", - "white", - "white", - "white", - "#e32119", - "white", - "white", - "white", - "white", - "white", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "white", - "white", - "white", - "white", - "white", - "#e32119", - "white", - "white", - "white", - "white", - "white", - "#6262EA", - "#6262EA", - "#6262EA", - "#e32119", - "white", - "#e32119", - "white", - "#e32119", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#6262EA", - "#e32119", - "#6262EA", - "white", - "#e32119", - "white", - "#6262EA", - "#e32119", - "#6262EA", - "#6262EA", - "#6262EA", - "#e32119", - "#6262EA", - "#6262EA", - "white", - "#e32119", - "white", - "#6262EA", - "#6262EA", - "#e32119", - "#6262EA", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "focus": false, - "color": "" - }, - { - "name": "Greece", - "size": 11, - "bricks": [ - "#5DA3EA", - "#5DA3EA", - "white", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "white", - "#5DA3EA", - "#5DA3EA", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "white", - "#5DA3EA", - "#5DA3EA", - "white", - "white", - "white", - "white", - "white", - "white", - "#5DA3EA", - "#5DA3EA", - "white", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "focus": false, - "squared": true, - "color": "" - }, - { - "name": "Russia", - "size": 8, - "bricks": [ - null, - null, - null, - null, - null, - null, - null, - null, - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "white", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "focus": false, - "squared": true, - "color": "" - }, - { - "name": "Ukraine", - "size": 8, - "bricks": [ - "", - "", - "", - "", - "", - "", - "", - "", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "focus": false, - "color": "" - }, - { - "name": "Poland", - "size": 7, - "bricks": [ - "", - "", - "", - "", - "", - "", - "", - "", - "white", - "white", - "white", - "white", - "white", - "", - "", - "white", - "white", - "white", - "white", - "white", - "", - "", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "", - "", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "focus": false, - "color": "", - "squared": true - }, - { - "name": "yellow 71", - "size": 9, - "bricks": [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - "#ffd300", - "#ffd300", - "", - "", - "#ffd300", - "#ffd300", - "", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "#ffd300", - "#ffd300", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - "#ffd300", - "#ffd300", - "", - "#ffd300", - "#ffd300", - "", - "", - "", - "", - "#ffd300", - "#ffd300", - "", - "#ffd300", - "#ffd300", - "", - "", - "", - "", - "#ffd300", - "#ffd300", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "color": "", - "focus": false - }, - { - "name": "71 on white", - "size": 6, - "bricks": [ - "white", - "white", - "white", - "white", - "white", - "white", - "#e32119", - "#e32119", - "#e32119", - "white", - "white", - "#e32119", - "white", - "white", - "#e32119", - "white", - "#e32119", - "#e32119", - "white", - "#e32119", - "white", - "white", - "white", - "#e32119", - "white", - "#e32119", - "white", - "white", - "white", - "#e32119", - "white", - "white", - "white", - "white", - "white", - "white", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "focus": false - }, - { - "name": "blue 71", - "size": 8, - "bricks": [ - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "", - "", - "#6262EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "#5DA3EA", - "", - "#6262EA", - "#6262EA", - null, - null, - "", - "#5DA3EA", - "#5DA3EA", - "#6262EA", - "#6262EA", - "#6262EA", - null, - null, - "#5DA3EA", - "#5DA3EA", - "", - null, - "#6262EA", - "#6262EA", - null, - "", - "#5DA3EA", - "#5DA3EA", - "", - null, - "#6262EA", - "#6262EA", - null, - "#5DA3EA", - "#5DA3EA", - "", - null, - null, - "#6262EA", - "#6262EA", - "", - "#5DA3EA", - "#5DA3EA", - "", - null, - null, - "#6262EA", - "#6262EA", - "", - "#5DA3EA", - "#5DA3EA", - null, - null, - null, - "#6262EA", - "#6262EA" - ], - "svg": "", - "color": "", - "focus": false - }, - { - "name": "seventy one", - "size": 21, - "bricks": [ - "#e32119", - "#e32119", - "", - "#ffd300", - "#ffd300", - "", - "#e32119", - "#e32119", - "#e32119", - "#ffd300", - "", - "#ffd300", - "#e32119", - "#e32119", - "#e32119", - "#ffd300", - "", - "#ffd300", - "#e32119", - "#e32119", - "#e32119", - "#e32119", - "", - "#e32119", - "#ffd300", - "", - "#ffd300", - "#e32119", - "", - "", - "#ffd300", - "", - "#ffd300", - "#e32119", - "", - "#e32119", - "#ffd300", - "", - "#ffd300", - "", - "#e32119", - null, - "#e32119", - "#e32119", - "", - "#ffd300", - "#ffd300", - "", - "#e32119", - "#e32119", - "", - "#ffd300", - "#ffd300", - "", - "#e32119", - "", - "#e32119", - "#ffd300", - "", - "#ffd300", - "", - "#e32119", - null, - "#e32119", - "", - "#e32119", - "#ffd300", - "", - "#ffd300", - "#e32119", - "", - "", - "#ffd300", - "", - "#ffd300", - "#e32119", - "", - "#e32119", - "#ffd300", - "", - "#ffd300", - "", - "#e32119", - null, - "#e32119", - "#e32119", - "", - "#ffd300", - "", - "#ffd300", - "#e32119", - "#e32119", - "#e32119", - "#ffd300", - "", - "#ffd300", - "#e32119", - "#e32119", - "#e32119", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "#e32119", - null, - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#ffd300", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#e32119", - "", - "", - "", - "", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#e32119", - "#e32119", - "#e32119", - "#ffd300", - "", - "#ffd300", - "#e32119", - "#e32119", - "#e32119", - "#ffd300", - "#ffd300", - "#ffd300", - "#e32119", - "#e32119", - "", - "#ffd300", - "", - "#ffd300", - "", - "", - "#ffd300", - "#e32119", - "#e32119", - "", - "#ffd300", - "", - "#ffd300", - "#e32119", - "#e32119", - "", - "#ffd300", - "", - "#ffd300", - "#e32119", - "", - "", - "#ffd300", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#e32119", - "#e32119", - "#e32119", - "", - "#ffd300", - "", - "#e32119", - "#e32119", - "#e32119", - "#ffd300", - "", - "#ffd300", - "#e32119", - "#e32119", - "#e32119", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#ffd300", - "#e32119", - "#e32119", - "#e32119", - "#ffd300", - "#ffd300", - "#ffd300", - "#e32119", - "#e32119", - "#e32119", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#ffd300", - "#ffd300", - null, - "#e32119", - "", - "#e32119", - "#ffd300", - "", - "#ffd300", - "#e32119", - "#e32119", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#e32119", - "#e32119", - "#e32119", - "#ffd300", - "", - "#ffd300", - "#e32119", - "#e32119", - "#e32119", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "focus": false - }, - { - "name": "B71", - "size": 10, - "bricks": [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#e32119", - "#e32119", - "#e32119", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "#e32119", - "", - "#e32119", - "", - "", - "#e32119", - "", - "", - "#ffd300", - "#e32119", - "#e32119", - "", - "#e32119", - "", - "", - "#e32119", - "", - "", - "#ffd300", - "", - "#e32119", - "", - "#e32119", - "#e32119", - "#e32119", - "", - "", - "#ffd300", - "", - "", - "#e32119", - "", - "#e32119", - "", - "", - "#e32119", - "", - "#ffd300", - "", - "", - "#e32119", - "", - "#e32119", - "", - "", - "#e32119", - "#ffd300", - "", - "", - "", - "#e32119", - "", - "#e32119", - "#e32119", - "#e32119", - "", - "#ffd300", - "", - "", - "#e32119", - "#e32119", - "#e32119", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "" - }, - { - "name": "Pig", - "size": 9, - "bricks": [ - null, - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#E66BA8", - "#E66BA8", - "", - "", - "", - "#E66BA8", - "#E66BA8", - "", - "", - "#E66BA8", - "#E66BA8", - "#E66BA8", - "", - "#E66BA8", - "#E66BA8", - "#E66BA8", - "", - "", - "white", - "white", - "#E66BA8", - "#E66BA8", - "#E66BA8", - "white", - "white", - "", - "", - "white", - "black", - "#E66BA8", - "#E66BA8", - "#E66BA8", - "black", - "white", - "", - "", - "#E66BA8", - "#E66BA8", - "#E67070", - "#E67070", - "#E67070", - "#E66BA8", - "#E66BA8", - "", - "", - "#E66BA8", - "#E67070", - "black", - "#E67070", - "black", - "#E67070", - "#E66BA8", - "", - "", - "#E66BA8", - "#E66BA8", - "#E67070", - "#E67070", - "#E67070", - "#E66BA8", - "#E66BA8", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "focus": false - }, - { - "name": "Big pig", - "size": 15, - "bricks": [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - "", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "", - "", - "", - "", - "", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "#E67070", - "#E67070", - "", - "", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "#E67070", - "#E67070", - "", - "", - "", - "", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "", - "", - "", - "#E67070", - "white", - "black", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "black", - "white", - "#E67070", - "", - "", - "", - "#E67070", - "#E67070", - "black", - "black", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "black", - "black", - "#E67070", - "#E67070", - "", - "", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "", - "", - "", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "#E67070", - "#E67070", - "#E67070", - "", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "#E67070", - "#E67070", - "#E67070", - "", - "#E67070", - "black", - "#E67070", - "black", - "#E67070", - "", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "#E67070", - "#E67070", - "#E67070", - "", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "", - "", - "", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "", - "", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "", - "", - "#A1F051", - "#A1F051", - "#A1F051", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#A1F051", - "#A1F051", - "#A1F051", - "#A1F051", - "#A1F051", - "#A1F051", - "#E67070", - "#A1F051", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#E67070", - "#A1F051", - "#E67070", - "#A1F051", - "#A1F051", - "#A1F051", - "#A1F051", - "#A1F051", - "#A1F051", - "#E67070", - "#E67070", - "#A1F051", - "#A1F051", - "#A1F051", - "#A1F051", - "#A1F051", - "#E67070", - "#E67070", - "#A1F051", - "#A1F051", - "#A1F051", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "focus": false, - "color": "", - "squared": true - }, - { - "name": "Donkey Kong", - "size": 9, - "bricks": [ - "#F29E4A", - "#F29E4A", - "#e32119", - "", - "", - "#5BECEC", - "", - "", - "", - "#F29E4A", - "#F29E4A", - "#e32119", - "", - "", - "#5BECEC", - "", - "", - "", - "#E869E8", - "#E869E8", - "#E869E8", - "#E869E8", - "#E869E8", - "#E869E8", - "#E869E8", - "", - "#F29E4A", - "", - "", - "", - "", - "", - "", - "#5BECEC", - "", - "", - "", - "", - "", - "", - "", - "", - "#5BECEC", - "", - "", - "", - "", - "#E869E8", - "#E869E8", - "#E869E8", - "#E869E8", - "#E869E8", - "#E869E8", - "#E869E8", - "#e32119", - "", - "#5BECEC", - "", - "", - "", - "", - "", - "", - "#6262EA", - "", - "#5BECEC", - "", - "", - "", - "#F29E4A", - "", - "", - "#E869E8", - "#E869E8", - "#E869E8", - "#E869E8", - "#E869E8", - "#E869E8", - "#E869E8", - "", - "" - ], - "svg": "", - "focus": false, - "color": "#241f31" - }, - { - "name": "Banana", - "size": 12, - "bricks": [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - null, - "", - "", - "", - "", - "", - "#e1c8b4", - "", - "", - "", - "", - "", - null, - "", - "", - "", - "", - "#e1c8b4", - "#e1c8b4", - "#e1c8b4", - "", - "", - "", - "", - null, - "", - "", - "", - "", - "#e1c8b4", - "#e1c8b4", - "#e1c8b4", - "", - "", - "", - "", - null, - "", - "", - "", - "", - "#e1c8b4", - "#e1c8b4", - "#e1c8b4", - "", - "", - "", - "", - null, - "", - "", - "", - "", - "#e1c8b4", - "#e1c8b4", - "#e1c8b4", - "#ffd300", - "#ffd300", - "", - "", - null, - "", - "", - "#ffd300", - "#ffd300", - "#e1c8b4", - "#e1c8b4", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - null, - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "#e1c8b4", - "#ffd300", - "", - "#ffd300", - "#53EE53", - "", - null, - "", - "#ffd300", - "#ffd300", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - "#53EE53", - null, - "", - "", - "", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - "", - null, - "", - "", - "", - "", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300", - "", - "", - null, - null, - null, - null, - null, - "", - "", - "#ffd300", - "#ffd300", - "#ffd300", - "#ffd300" - ], - "svg": "", - "focus": false - }, - { - "name": "Fox", - "size": 8, - "bricks": [ - "#e1c8b4", - "", - "", - "", - "", - "", - "", - "#e1c8b4", - "#e1c8b4", - "#e1c8b4", - "", - "#F29E4A", - "#F29E4A", - "", - "#e1c8b4", - "#e1c8b4", - "#e1c8b4", - "#e1c8b4", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#e1c8b4", - "#e1c8b4", - "#e1c8b4", - "#F29E4A", - "black", - "#F29E4A", - "#F29E4A", - "black", - "#F29E4A", - "#e1c8b4", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "#F29E4A", - "", - "white", - "white", - "black", - "black", - "white", - "white", - "", - "", - "", - "white", - "white", - "white", - "white", - "", - "", - "", - "", - "", - "white", - "white", - "", - "", - "" - ], - "svg": "", - "focus": false, - "squared": true - }, - { - "name": "Wiki", - "size": 10, - "bricks": [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "#A1F051", - "#A1F051", - "#A1F051", - "#A1F051", - "", - "", - "", - "", - "", - "#A1F051", - "#A1F051", - "#618227", - "#618227", - "#A1F051", - "#A1F051", - "", - "", - "", - "#A1F051", - "#618227", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#618227", - "#A1F051", - "", - "", - "#A1F051", - "#231f20", - "white", - "white", - "white", - "white", - "#231f20", - "#A1F051", - "", - "", - "#A1F051", - "#618227", - "#231f20", - "#231f20", - "#231f20", - "#231f20", - "#618227", - "#A1F051", - "", - "", - "", - "#A1F051", - "#A1F051", - "#618227", - "#618227", - "#A1F051", - "#A1F051", - "", - "", - "", - "", - "", - "#A1F051", - "#A1F051", - "#A1F051", - "#A1F051", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "svg": "", - "focus": false - } -] \ 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 f7353b8..0000000 --- a/app/src/main/assets/style.css +++ /dev/null @@ -1,227 +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; - color: white; - background-size: 120px 120px; - background-color: var(--background1); - --background1: #030c23; - --background2: #03112a; -} - -#game { - position: absolute; - top: 0; - left: 0; - height: 100vh; - 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.95); - z-index: 10; - display: flex; - overflow: auto; -} - -.popup > div { - margin: auto; - padding: 20px; - /*border: 1px solid white;*/ - transform-origin: center; - display: flex; - flex-direction: column; - align-items: stretch; - width: 90%; - 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:none; - border: none; - cursor: pointer; - background: rgba(0,0,0,0.2); - overflow: hidden; -} -.popup button.close-modale:before { - content: "+"; - position: absolute; - transform: translate(-50%, -50%) rotate(45deg) ; - font-size: 80px; - display: inline-block; -} -.popup button.close-modale:hover { - font-weight: bold; -} - -.popup > div > button[disabled] { - /*border: 1px solid #666;*/ - opacity: 0.5; -} - -.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); - } -} \ 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 d67ba02..1d9832e 100644 --- a/app/src/main/java/me/lecaro/breakout/MainActivity.kt +++ b/app/src/main/java/me/lecaro/breakout/MainActivity.kt @@ -1,33 +1,183 @@ package me.lecaro.breakout + +import android.content.ContentValues +import android.content.Intent +import android.net.Uri +import android.os.Build import android.os.Bundle +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.ValueCallback import android.webkit.WebChromeClient import android.webkit.WebView +import android.widget.Toast +import androidx.core.content.FileProvider +import java.io.File +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.loadUrl("file:///android_asset/index.html") + 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 9ce65fd..e15dd3a 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,36 +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 -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/* \ 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 2d8069e..0000000 --- a/editclient.css +++ /dev/null @@ -1,29 +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; - flex-wrap: wrap; - margin-right: 80px; -} \ No newline at end of file diff --git a/editclient.js b/editclient.js deleted file mode 100644 index d5a9320..0000000 --- a/editclient.js +++ /dev/null @@ -1,271 +0,0 @@ -let currentColor = '' - -const colorsList = [ - 'white', - 'black', - '', - '#F44848', - '#ab0c0c', - '#F29E4A', - '#F0F04C', - '#A1F051', - '#53EE53', - '#59EEA3', - '#5BECEC', - '#5DA3EA', - '#6262EA', - '#A664E8', - '#E869E8', - '#E66BA8', - '#E67070', - "#333", - '#231f20', - '#e32119', - '#ffd300', - '#e1c8b4', - '#618227' -] -const palette = document.getElementById('palette'); - -colorsList.forEach(color => { - const btn = document.createElement('button') - Object.assign(btn.style, { - background: color, - display: 'inline-block', - width: '40px', - height: '40px', - border: '1px solid black' - }) - if (color === currentColor) { - btn.className = 'active' - } - palette.appendChild(btn) - btn.addEventListener('click', (e) => { - currentColor = color - 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')){ - level[e.target.getAttribute('data-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 = [] - 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[y * size + x] || '' - } - } - - level.size = newSize; - level.bricks = newBricks; - } else if (moveX && moveY) { - const dx = parseInt(moveX), dy = parseInt(moveY) - const moved = [] - for (let x = 0; x < size; x++) { - for (let y = 0; y < size; y++) { - moved[(y + dy) * size + (x + dx)] = bricks[y * size + x] || '' - } - } - - level.bricks = moved; - } 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 = undefined - -function colorPixel(e) { - if (typeof applying === 'undefined') 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 = applying || 'transparent' - allLevels[levelIndex].bricks[index] = applying - } -} - -document.getElementById('levels').addEventListener('mousedown', e => { - const index = 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 === currentColor ? '' : currentColor - colorPixel(e) - } -}) - -document.getElementById('levels').addEventListener('mouseenter', e => { - if (typeof applying !== undefined) { - - colorPixel(e) - } -}, true) - -document.addEventListener('mouseup', e => { - applying = undefined - 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 d9ff80b..d1c70ef 100644 --- a/editserver.js +++ b/editserver.js @@ -4,47 +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/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 c34bd64..c430919 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,13 +7,3204 @@ "": { "name": "breakout.lecaro.me", "version": "1.0.0", - "license": "ISC", + "license": "GNU AGPLv3", "dependencies": { + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", "body-parser": "^1.20.3", "express": "^4.21.2", - "nodemon": "^3.1.9" + "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", @@ -26,6 +3217,119 @@ "node": ">= 0.6" } }, + "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==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -38,16 +3342,234 @@ "node": ">= 8" } }, + "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==" }, + "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==", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "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==" }, + "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==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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", @@ -82,6 +3604,11 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "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", @@ -102,6 +3629,64 @@ "node": ">=8" } }, + "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", @@ -110,10 +3695,27 @@ "node": ">= 0.8" } }, + "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.1", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", - "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "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==", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -137,6 +3739,85 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/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", @@ -160,6 +3841,107 @@ "fsevents": "~2.3.2" } }, + "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==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "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", @@ -184,6 +3966,12 @@ "node": ">= 0.6" } }, + "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", @@ -197,6 +3985,301 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "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==", + "engines": { + "node": ">= 0.4.0" + } + }, + "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", @@ -205,6 +4288,74 @@ "ms": "2.0.0" } }, + "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", @@ -222,6 +4373,131 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "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", @@ -240,6 +4516,44 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, + "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", @@ -248,6 +4562,110 @@ "node": ">= 0.8" } }, + "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", @@ -275,11 +4693,105 @@ "node": ">= 0.4" } }, + "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==" }, + "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", @@ -288,6 +4800,118 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "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", @@ -333,6 +4957,74 @@ "url": "https://opencollective.com/express" } }, + "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", @@ -361,6 +5053,66 @@ "node": ">= 0.8" } }, + "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==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "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", @@ -377,6 +5129,12 @@ "node": ">= 0.6" } }, + "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", @@ -398,6 +5156,51 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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", @@ -421,6 +5224,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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", @@ -433,6 +5253,55 @@ "node": ">= 0.4" } }, + "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", @@ -444,6 +5313,35 @@ "node": ">= 6" } }, + "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", @@ -455,6 +5353,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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", @@ -463,6 +5377,31 @@ "node": ">=4" } }, + "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", @@ -474,6 +5413,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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", @@ -485,6 +5438,100 @@ "node": ">= 0.4" } }, + "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==", + "bin": { + "he": "bin/he" + } + }, + "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==", + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "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", @@ -500,6 +5547,121 @@ "node": ">= 0.8" } }, + "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==", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "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==", + "dependencies": { + "basic-auth": "^2.0.1", + "chalk": "^4.1.2", + "corser": "^2.0.1", + "he": "^1.2.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy": "^1.18.1", + "mime": "^1.6.0", + "minimist": "^1.2.6", + "opener": "^1.5.1", + "portfinder": "^1.0.28", + "secure-compare": "3.0.1", + "union": "~0.5.0", + "url-join": "^4.0.1" + }, + "bin": { + "http-server": "bin/http-server" + }, + "engines": { + "node": ">=12" + } + }, + "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", @@ -516,11 +5678,91 @@ "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==" }, + "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==" }, + "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", @@ -529,6 +5771,59 @@ "node": ">= 0.10" } }, + "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", @@ -540,6 +5835,77 @@ "node": ">=8" } }, + "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", @@ -548,6 +5914,55 @@ "node": ">=0.10.0" } }, + "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", @@ -559,6 +5974,22 @@ "node": ">=0.10.0" } }, + "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", @@ -567,6 +5998,1477 @@ "node": ">=0.12.0" } }, + "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==" + }, + "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", @@ -575,6 +7477,11 @@ "node": ">= 0.4" } }, + "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", @@ -583,6 +7490,14 @@ "node": ">= 0.6" } }, + "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", @@ -591,6 +7506,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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", @@ -599,6 +7520,18 @@ "node": ">= 0.6" } }, + "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", @@ -629,6 +7562,15 @@ "node": ">= 0.6" } }, + "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", @@ -640,11 +7582,118 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "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", @@ -653,6 +7702,48 @@ "node": ">= 0.6" } }, + "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", @@ -701,6 +7792,25 @@ "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", @@ -709,10 +7819,148 @@ "node": ">=0.10.0" } }, + "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.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", - "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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" }, @@ -731,6 +7979,186 @@ "node": ">= 0.8" } }, + "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==", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "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", @@ -739,11 +8167,66 @@ "node": ">= 0.8" } }, + "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==" }, + "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", @@ -755,6 +8238,166 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "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==", + "dependencies": { + "async": "^2.6.4", + "debug": "^3.2.7", + "mkdirp": "^0.5.6" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "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", @@ -767,11 +8410,53 @@ "node": ">= 0.10" } }, + "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==" }, + "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", @@ -786,6 +8471,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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", @@ -808,6 +8498,56 @@ "node": ">= 0.8" } }, + "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", @@ -819,6 +8559,140 @@ "node": ">=8.10.0" } }, + "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==" + }, + "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", @@ -838,11 +8712,70 @@ } ] }, + "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==" }, + "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==" + }, "node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -904,11 +8837,84 @@ "node": ">= 0.8.0" } }, + "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==" }, + "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", @@ -977,6 +8983,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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", @@ -988,6 +9000,106 @@ "node": ">=10" } }, + "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", @@ -996,6 +9108,144 @@ "node": ">= 0.8" } }, + "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", @@ -1007,6 +9257,90 @@ "node": ">=4" } }, + "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", @@ -1034,6 +9368,115 @@ "nodetouch": "bin/nodetouch.js" } }, + "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", @@ -1046,11 +9489,135 @@ "node": ">= 0.6" } }, + "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==" }, + "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==", + "dependencies": { + "qs": "^6.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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", @@ -1059,6 +9626,57 @@ "node": ">= 0.8" } }, + "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==" + }, + "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", @@ -1067,6 +9685,29 @@ "node": ">= 0.4.0" } }, + "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", @@ -1074,6 +9715,334 @@ "engines": { "node": ">= 0.8" } + }, + "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==", + "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": { + "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 c9a817b..26e13ed 100644 --- a/package.json +++ b/package.json @@ -1,16 +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 " + "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", - "nodemon": "^3.1.9" + "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" } } 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" + + } +}