Merge branch 'main' of github.com:ghostfolio/ghostfolio
Some checks failed
Extract locales / extract_locales (push) Failing after 16m9s

This commit is contained in:
sudacode 2025-01-28 10:21:02 -08:00
commit 7eb5493dd7
12 changed files with 392 additions and 274 deletions

40
.github/workflows/extract-locales.yml vendored Normal file
View File

@ -0,0 +1,40 @@
name: Extract locales
on:
push:
branches:
- main
permissions:
contents: write
pull-requests: write
jobs:
extract_locales:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
run: npm ci
- name: Extract locales
run: npm run extract-locales
- name: Check changes
id: verify-changed-files
uses: tj-actions/verify-changed-files@v20
- name: Create pull request
if: steps.verify-changed-files.outputs.files_changed == 'true'
uses: peter-evans/create-pull-request@v7
with:
author: 'github-actions[bot] <github-actions[bot]@users.noreply.github.com>'
branch: 'feature/update-locales'
commit-message: 'Update locales'
delete-branch: true
title: 'Feature/update locales'
token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -9,10 +9,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Migrated the database seeding to _TypeScript_
- Upgraded `@trivago/prettier-plugin-sort-imports` from version `4.3.0` to `5.2.1`
- Upgraded `ng-extract-i18n-merge` from version `2.13.1` to `2.14.1`
## 2.136.0 - 2025-01-24
### Added
- Set up a _GitHub Action_ to automatically extract locales when the `main` branch changes
### Changed
- Extended the _Financial Modeling Prep_ service
- Improved the language localization for Ukrainian (`uk`)
- Refreshed the cryptocurrencies list
- Upgraded `date-fns` from version `3.6.0` to `4.1.0`
- Upgraded `rxjs` from version `7.5.6` to `7.8.1`
### Fixed
- Fixed an issue with the detection of the thousand separator by locale
- Fixed an issue with holdings and sectors while using symbol profile overrides
- Fixed an issue with the MIME type detection in the scraper configuration
## 2.135.0 - 2025-01-19
### Changed

View File

@ -41,6 +41,7 @@
"0xVPN": "0xVPN.org",
"1-UP": "1-UP",
"1000SATS": "SATS",
"1000X": "1000x by Virtuals",
"10SET": "Tenset",
"1ART": "ArtWallet",
"1CAT": "Bitcoin Cats",
@ -212,6 +213,7 @@
"ACM": "AC Milan Fan Token",
"ACN": "AvonCoin",
"ACOIN": "ACoin",
"ACOLYT": "Acolyte by Virtuals",
"ACP": "Anarchists Prime",
"ACPT": "Crypto Accept",
"ACQ": "Acquire.Fi",
@ -332,6 +334,7 @@
"AGIL": "Agility LSD",
"AGIV1": "SingularityNET v1",
"AGIX": "SingularityNET",
"AGIXBT": "AGIXBT by Virtuals",
"AGLA": "Angola",
"AGLD": "Adventure Gold",
"AGM": "Argoneum",
@ -363,12 +366,13 @@
"AIBCOIN": "AIBLOCK",
"AIBK": "AIB Utility Token",
"AIBU": "AIBUZZ TOKEN",
"AIC": "AI Crypto",
"AIC": "AI Companions",
"AICELL": "AICell",
"AICH": "AIChain",
"AICO": "AICON",
"AICODE": "AI CODE",
"AICORE": "AICORE",
"AICRYPTO": "AI Crypto",
"AID": "AidCoin",
"AIDA": "Ai-Da robot",
"AIDI": "Aidi Inu",
@ -405,6 +409,7 @@
"AINU": "Ainu Token",
"AION": "Aion",
"AIONE": "AiONE",
"AIOS": "INT OS",
"AIOT": "AIOT Token",
"AIOZ": "AIOZ Network",
"AIPAD": "AIPAD",
@ -438,6 +443,7 @@
"AITRA": "Aitra",
"AITT": "AITrading",
"AIUS": "Arbius",
"AIVIA": "AI Virtual Agents",
"AIWALLET": "AiWallet Token",
"AIX": "Aigang",
"AIXBT": "aixbt by Virtuals",
@ -617,6 +623,7 @@
"ANDYBSC": "ANDY",
"ANDYMAN": "ANDYMAN",
"ANDYSOL": "Andy on SOL",
"ANEX": "AstroNexus",
"ANGEL": "Crypto Angel",
"ANGL": "Angel Token",
"ANGLE": "ANGLE",
@ -743,13 +750,14 @@
"ARBS": "Arbswap",
"ARBT": "ARBITRAGE",
"ARBUZ": "ARBUZ",
"ARC": "Arc",
"ARC": "AI Rig Complex",
"ARCA": "Legend of Arcadia",
"ARCAD": "Arcadeum",
"ARCADE": "ARCADE",
"ARCADECITY": "Arcade City",
"ARCADEF": "arcadefi",
"ARCADEN": "ArcadeNetwork",
"ARCAI": "ARCAI",
"ARCANE": "Arcane Token",
"ARCAS": "Arcas",
"ARCH": "Archway",
@ -757,6 +765,7 @@
"ARCHCOIN": "ArchCoin",
"ARCHE": "Archean",
"ARCHIVE": "Chainback",
"ARCINTEL": "Arc",
"ARCO": "AquariusCoin",
"ARCONA": "Arcona",
"ARCT": "ArbitrageCT",
@ -1272,6 +1281,7 @@
"BASEDP": "Based Pepe",
"BASEDR": "Based Rabbit",
"BASEDS": "BasedSwap",
"BASEDTURBO": "Based Turbo",
"BASEDV1": "Based Money v1",
"BASEHEROES": "Baseheroes",
"BASEPROTOCOL": "Base Protocol",
@ -1393,6 +1403,7 @@
"BDID": "BDID",
"BDL": "Bitdeal",
"BDOG": "Bulldog Token",
"BDOGITO": "BullDogito",
"BDOT": "Binance Wrapped DOT",
"BDP": "Big Data Protocol",
"BDPI": "Interest Bearing Defi Pulse Index",
@ -1513,6 +1524,7 @@
"BFT": "BF Token",
"BFTB": "Brazil Fan Token",
"BFTC": "BITS FACTOR",
"BFWOG": "Based Fwog (basedfwog.info)",
"BFX": "BitFinex Tokens",
"BG": "BunnyPark Game",
"BGB": "Bitget token",
@ -1591,7 +1603,7 @@
"BINS": "Bitsense",
"BINTEX": "Bintex Futures",
"BINU": "Blast Inu",
"BIO": "BITONE",
"BIO": "Bio Protocol",
"BIOB": "BioBar",
"BIOC": "BioCrypt",
"BIOCOIN": "Biocoin",
@ -1646,6 +1658,7 @@
"BITNEW": "BitNewChain",
"BITO": "BitoPro Exchange Token",
"BITOK": "BitOKX",
"BITONE": "BITONE",
"BITORB": "BitOrbit",
"BITRA": "Bitratoken",
"BITRADIO": "Bitradio",
@ -2015,6 +2028,7 @@
"BOSU": "Bosu Inu",
"BOT": "Bot Planet",
"BOTC": "BotChain",
"BOTIFY": "BOTIFY",
"BOTS": "ArkDAO",
"BOTTO": "Botto",
"BOTX": "BOTXCOIN",
@ -2144,6 +2158,7 @@
"BROTHER": "BROTHER",
"BROWN": "BrowniesSwap",
"BROZ": "Brozinkerbell",
"BRP": "BananaRepublic",
"BRRR": "Burrow",
"BRS": "Broovs Projects",
"BRT": "Bikerush",
@ -2288,6 +2303,7 @@
"BTNYX": "BitOnyx Token",
"BTO": "Bottos",
"BTOP": "Botopia.Finance",
"BTORO": "Bitoro Network",
"BTP": "Bitpaid",
"BTPL": "Bitcoin Planet",
"BTQ": "BitQuark",
@ -2577,6 +2593,7 @@
"CATELON": "CatElonMars",
"CATEX": "CATEX",
"CATFISH": "Catfish",
"CATG": "Crypto Agent Trading",
"CATGAME": "Cookie Cat Game",
"CATGIRL": "Catgirl",
"CATGOKU": "Catgoku",
@ -2789,6 +2806,7 @@
"CHAT": "Solchat",
"CHATAI": "ChatAI Token",
"CHATGPT": "AI Dragon",
"CHATOSHI": "chAtoshI",
"CHATTY": "ChatGPT's Mascot",
"CHB": "COINHUB TOKEN",
"CHBR": "CryptoHub",
@ -2993,6 +3011,7 @@
"CLU": "CluCoin",
"CLUB": "ClubCoin",
"CLUD": "CludCoin",
"CLUSTR": "Clustr Labs",
"CLV": "Clover Finance",
"CLVA": "Clever DeFi",
"CLVX": "Calvex",
@ -3337,6 +3356,7 @@
"CROWD": "CrowdCoin",
"CROWDWIZ": "Crowdwiz",
"CROWN": "Crown by Third Time Games",
"CROWWITH": "crow with knife",
"CROX": "CroxSwap",
"CRP": "Crypton",
"CRPS": "CryptoPennies",
@ -3793,6 +3813,7 @@
"DEER": "ToxicDeer Finance",
"DEEX": "DEEX",
"DEEZ": "DEEZ NUTS",
"DEFAI": "DeFAI",
"DEFC": "Defi Coin",
"DEFEND": "Blockdefend AI",
"DEFI": "DeFi",
@ -3980,6 +4001,7 @@
"DIGI": "Digiverse",
"DIGIC": "DigiCube",
"DIGIF": "DigiFel",
"DIGIMON": "Digimon",
"DIGIT": "Digital Asset Rights Token",
"DIGITAL": "Digital Reserve Currency",
"DIGITS": "Digits DAO",
@ -4327,6 +4349,7 @@
"DRS": "Digital Rupees",
"DRT": "DomRaider",
"DRUGS": "Big Pharmai",
"DRV": "Derive",
"DRXNE": "Droxne",
"DRZ": "Droidz",
"DS": "DeStorage",
@ -4386,6 +4409,7 @@
"DUC": "DucatusCoin",
"DUCATO": "Ducato Protocol Token",
"DUCK": "Unit Protocol New",
"DUCKAI": "Duck AI",
"DUCKC": "DuckCoin",
"DUCKD": "DuckDuckCoin",
"DUCKER": "Ducker",
@ -4463,6 +4487,7 @@
"DYAD": "Dyad Stable",
"DYC": "Dycoin",
"DYDX": "dYdX",
"DYDXV1": "dYdX v1",
"DYM": "Dymension",
"DYN": "Dynamic",
"DYNA": "Dynamix",
@ -4622,7 +4647,8 @@
"EGI": "eGame",
"EGL": "The Eagle Of Truth",
"EGLD": "eGold",
"EGO": "EGOcoin",
"EGO": "Paysenger EGO",
"EGOCOIN": "EGOcoin",
"EGOD": "EgodCoin",
"EGOLD": "EGOLD",
"EGON": "EgonCoin",
@ -4722,7 +4748,6 @@
"ELUSKMON": "Elusk Mon",
"ELV": "Elvantis",
"ELVN": "11Minutes",
"ELX": "Energy Ledger",
"ELY": "Elysian",
"ELYS": "Elys Network",
"ELYSIUM": "Elysium",
@ -4773,6 +4798,7 @@
"ENE": "EneCoin",
"ENEAR": "Near (Energiswap)",
"ENEDEX": "Enedex",
"ENERGYLEDGER": "Energy Ledger",
"ENERGYX": "Safe Energy",
"ENG": "Enigma",
"ENGT": "Engagement Token",
@ -5003,6 +5029,7 @@
"EVAI": "EVA Intelligence",
"EVAN": "Evanesco Network",
"EVAULT": "EthereumVault",
"EVAV1": "Evadore v1",
"EVC": "Eventchain",
"EVCC": "Eco Value Coin",
"EVCOIN": "EverestCoin",
@ -5111,10 +5138,11 @@
"FADO": "FADO Go",
"FAG": "PoorFag",
"FAH": "Falcons",
"FAI": "Fairum",
"FAI": "Freysa AI",
"FAIR": "FairCoin",
"FAIRC": "Faireum Token",
"FAIRG": "FairGame",
"FAIRUM": "Fairum",
"FAKE": "FAKE COIN",
"FAKEAI": "DeepFakeAI",
"FAKT": "Medifakt",
@ -5633,7 +5661,8 @@
"FUBAO": "FUBAO",
"FUCK": "Fuck Token",
"FUD": "FUD.finance",
"FUEL": "Jetfuel Finance",
"FUEGO": "FUEGO",
"FUEL": "Fuel Network",
"FUELX": "Fuel",
"FUFU": "Fufu Token",
"FUG": "FUG",
@ -5754,6 +5783,7 @@
"GAMEST": "GameStop Coin",
"GAMESTARS": "Game Stars",
"GAMESTO": "GameStop",
"GAMESTOP": "GameStop",
"GAMESTUMP": "GAMESTUMP",
"GAMET": "GAME Token",
"GAMEX": "GameX",
@ -5978,6 +6008,7 @@
"GINGER": "GINGER",
"GINNAN": "Ginnan The Cat",
"GINOA": "Ginoa",
"GINU": "Green Shiba Inu",
"GINUX": "Green Shiba Inu",
"GINZA": "GINZA NETWORK",
"GIO": "Graviocoin",
@ -6546,6 +6577,9 @@
"HEM": "Hemera",
"HEMAN": "HE-MAN",
"HEMULE": "Hemule",
"HENG": "HengCoin",
"HENLO": "Henlo",
"HENLOV1": "Henlo v1",
"HEP": "Health Potion",
"HER": "Hero Node",
"HERA": "Hero Arena",
@ -6652,6 +6686,8 @@
"HLINK": "Chainlink (Harmony One Bridge)",
"HLM": "Helium",
"HLN": "Holonus",
"HLO": "Halo",
"HLOV1": "Halo v1",
"HLP": "Purpose Coin",
"HLPR": "HELPER COIN",
"HLPT": "HLP Token",
@ -6777,6 +6813,7 @@
"HSAI": "HealthSci.AI",
"HSC": "HashCoin",
"HSF": "Hillstone Finance",
"HSK": "HashKey Platform Token",
"HSN": "Hyper Speed Network",
"HSP": "Horse Power",
"HSS": "Hashshare",
@ -6792,6 +6829,7 @@
"HTDF": "Orient Walt",
"HTE": "Hepton",
"HTER": "Biogen",
"HTERM": "Hiero Terminal",
"HTK": "Hard To Kill",
"HTM": "Hatom",
"HTML": "HTML Coin",
@ -6816,6 +6854,7 @@
"HUM": "Humanscape",
"HUMAI": "Humanoid AI",
"HUMP": "Hump",
"HUMV1": "Humanscape v1",
"HUND": "HUND MEME COIN",
"HUNDRED": "HUNDRED",
"HUNNY": "Pancake Hunny",
@ -6952,6 +6991,7 @@
"IDO": "Idexo",
"IDOL": "IDOLINU",
"IDORU": "Vip2Fan",
"IDRISS": "IDRISS",
"IDRT": "Rupiah Token",
"IDRX": "IDRX",
"IDT": "InvestDigital",
@ -7309,10 +7349,12 @@
"JET": "Jet Protocol",
"JETCAT": "Jetcat",
"JETCOIN": "Jetcoin",
"JETFUEL": "Jetfuel Finance",
"JETTON": "JetTon Game",
"JEUR": "Jarvis Synthetic Euro",
"JEW": "Shekel",
"JEWEL": "DeFi Kingdoms",
"JEWELRY": "Jewelry Token",
"JEX": "JEX Token",
"JF": "Jswap.Finance",
"JFI": "JackPool.finance",
@ -7347,6 +7389,7 @@
"JMT": "JMTIME",
"JMZ": "Jimizz",
"JNB": "Jinbi Token",
"JNFTC": "Jumbo Blockchain",
"JNGL": "Jungle Labz",
"JNS": "Janus",
"JNT": "Jibrel Network Token",
@ -7358,6 +7401,7 @@
"JOE": "JOE",
"JOEB": "Joe Biden",
"JOEBIDEN2024 ": "JOEBIDEN2024",
"JOECOIN": "Joe Coin",
"JOEY": "Joey Inu",
"JOGECO": "Jogecodog",
"JOHM": "Johm lemmon",
@ -7556,6 +7600,7 @@
"KEK": "KekCoin",
"KEKE": "KEK",
"KEKEC": "THE BALKAN DWARF",
"KEKIUS": "Kekius Maximus",
"KEL": "KelVPN",
"KELP": "KELP",
"KELPE": "Kelp Earned Points",
@ -7576,6 +7621,9 @@
"KET": "KET",
"KETAMINE": "Ketamine",
"KETAN": "Ketan",
"KEVIN": "Kevin (kevinonbase.xyz)",
"KEVINTOKENME": "KEVIN (kevintoken.me)",
"KEVINTOKENNET": "Kevin",
"KEX": "Kira Network",
"KEXCOIN": "KexCoin",
"KEY": "SelfKey",
@ -7605,6 +7653,7 @@
"KICKS": "GetKicks",
"KIDEN": "RoboKiden",
"KIF": "KittenFinance",
"KIKI": "Kiki Flaminki",
"KIKO": "KIKO",
"KILLA": "The Bitcoin Killa",
"KILLER": "Fat Cat Killer",
@ -7724,6 +7773,7 @@
"KOGE": "BNB48 Club Token",
"KOGECOIN": "KogeCoin.io",
"KOI": "Koi",
"KOII": "Koii",
"KOIN": "Koinos",
"KOINB": "KoinBülteni Token",
"KOINETWORK": "Koi Network",
@ -7823,6 +7873,7 @@
"KUBE": "KubeCoin",
"KUBO": "KUBO",
"KUBOS": "KubosCoin",
"KUDAI": "Kudai",
"KUE": "Kuende",
"KUJI": "Kujira",
"KUKU": "KuKu",
@ -7867,6 +7918,7 @@
"KZC": "KZCash",
"KZEN": "Kaizen",
"L": "L inu",
"L1": "Lamina1",
"L2": "Leverj Gluon",
"L2DAO": "Layer2DAO",
"L3": "Layer3",
@ -7928,7 +7980,8 @@
"LATX": "Latium",
"LAUGHCOIN": "Laughcoin",
"LAUNCH": "Launchblock.com",
"LAVA": "Lavaswap",
"LAVA": "Lava Network",
"LAVASWAP": "Lavaswap",
"LAVAX": "LavaX Labs",
"LAVE": "Lavandos",
"LAVITA": "Lavita AI",
@ -7992,6 +8045,7 @@
"LEE": "Love Earn Enjoy",
"LEET": "LeetSwap",
"LEG": "Legia Warsaw Fan Token",
"LEGEND": "Legend",
"LEGION": "LEGION",
"LEGO": "Lego Coin",
"LEIA": "Leia",
@ -8093,6 +8147,7 @@
"LIGHTSPEED": "LightSpeedCoin",
"LIGMA": "Ligma Node",
"LIGO": "Ligo",
"LIHUA": "LIHUA",
"LIKE": "Only1",
"LIKEC": "LikeCoin",
"LILA": "LiquidLayer",
@ -8100,6 +8155,7 @@
"LILFLOKI": "Lil Floki",
"LILPUMP": "lilpump",
"LILY": "LILY-The Gold Digger",
"LIMBO": "Limbo",
"LIME": "iMe Lab",
"LIMEX": "Limestone Network",
"LIMITEDCOIN": "Limited Coin",
@ -8167,6 +8223,7 @@
"LLAND": "Lyfe Land",
"LLG": "Loligo",
"LLION": "Lydian Lion",
"LLM": "Large Language Model Based",
"LLT": "LILLIUS",
"LM": "LeisureMeta",
"LMAO": "LMAO Finance",
@ -8366,6 +8423,7 @@
"LUFFY": "Luffy",
"LUFFYG": "Luffy G5",
"LUFFYOLD": "Luffy",
"LUFFYV1": "Luffy v1",
"LUIGI": "Luigi Inu",
"LUIS": "Tongue Cat",
"LULU": "LULU",
@ -8394,7 +8452,8 @@
"LUSH": "Lush AI",
"LUT": "Cinemadrom",
"LUTETIUM": "Lutetium Coin",
"LUX": "LUXCoin",
"LUX": "Lux Token",
"LUXCOIN": "LUXCoin",
"LUXO": "Luxo",
"LUXU": "Luxury Travel Token",
"LUXY": "Luxy",
@ -8449,6 +8508,7 @@
"MADAGASCARTOKEN": "Madagascar Token",
"MADANA": "MADANA",
"MADC": "MadCoin",
"MADCOIN": "MAD",
"MADH": "Madhouse",
"MADOG": "MarvelDoge",
"MADP": "Mad Penguin",
@ -8456,13 +8516,14 @@
"MAEP": "Maester Protocol",
"MAF": "MetaMAFIA",
"MAG": "Magnify Cash",
"MAGA": "MAGA Hat",
"MAGA": "MAGA",
"MAGA2024": "MAGA2024",
"MAGAA": "MAGA AGAIN",
"MAGAC": "MAGA CAT",
"MAGACA": "MAGA CAT",
"MAGACAT": "MAGACAT",
"MAGADOGE": "MAGA DOGE",
"MAGAHAT": "MAGA Hat",
"MAGAIBA": "Magaiba",
"MAGAN": "Maganomics On Solana",
"MAGANOMICS": "Maganomics",
@ -8577,6 +8638,7 @@
"MATAR": "MATAR AI",
"MATCH": "Matching Game",
"MATE": "Mate",
"MATES": "MATES",
"MATH": "MATH",
"MATIC": "Polygon",
"MATICX": "Stader MaticX",
@ -8607,6 +8669,7 @@
"MAZI": "MaziMatic",
"MAZZE": "Mazze",
"MB": "MineBee",
"MB28": "MBridge28",
"MB4": "Matthew Box 404",
"MB8": "MB8 Coin",
"MBAG": "MoonBag",
@ -8741,7 +8804,8 @@
"MEI": "Mei Solutions",
"MEIZHU": "GUANGZHOU ZOO NEW BABY PANDA",
"MEL": "MELX",
"MELANIA": "Melania Trump",
"MELANIA": "Melania Meme",
"MELANIATRUMP": "Melania Trump",
"MELB": "Minelab",
"MELD": "MELD",
"MELI": "Meli Games",
@ -8819,6 +8883,7 @@
"METAF": "MetaFastest",
"METAG": "MetagamZ",
"METAGEAR": "MetaGear",
"METAIVERSE": "MetAIverse",
"METAL": "Metal Blockchain",
"METALCOIN": "MetalCoin",
"METAMEME": "met a meta metameme",
@ -9732,6 +9797,7 @@
"NEXXO": "Nexxo",
"NEZHA": "NezhaToken",
"NFAI": "Not Financial Advice",
"NFAIV1": "Not Financial Advice v1",
"NFCR": "NFCore",
"NFD": "Feisty Doge NFT",
"NFE": "Edu3Labs",
@ -9857,6 +9923,7 @@
"NOBS": "No BS Crypto",
"NOCHILL": "AVAX HAS NO CHILL",
"NODE": "Whole Network",
"NODESYNAPSE": "NodeSynapse",
"NODIDDY": "NODIDDY",
"NODIS": "Nodis",
"NODL": "Nodle Network",
@ -9928,7 +9995,7 @@
"NRV": "Nerve Finance",
"NRVE": "Narrative",
"NRX": "Neironix",
"NS": "NodeSynapse",
"NS": "SuiNS Token",
"NS2DRP": "New Silver Series 2 DROP",
"NSBT": "Neutrino Token",
"NSD": "Nasdacoin",
@ -10620,7 +10687,8 @@
"PEGAMAGA": "Pepe Maga",
"PEGG": "PokPok Golden Egg",
"PEGS": "PegShares",
"PEIPEI": "PEIPEI",
"PEIPEI": "PeiPei",
"PEIPEICN": "PEIPEI",
"PEKA": "PEKA",
"PEKC": "Peacock Coin",
"PEKINU": "PEKI INU",
@ -10891,6 +10959,7 @@
"PLANET": "PLANET",
"PLANETCOIN": "PlanetCoin",
"PLANETS": "PlanetWatch",
"PLANT": "Plant",
"PLASTIK": "Plastiks",
"PLAT": "BitGuild PLAT",
"PLATC": "PlatinCoin",
@ -10941,6 +11010,7 @@
"PLU": "Pluton",
"PLUG": "PL^Gnet",
"PLUGCN": "Plug Chain",
"PLUME": "Plume",
"PLUP": "PoolUp",
"PLURA": "PluraCoin",
"PLUS1": "PlusOneCoin",
@ -11118,6 +11188,7 @@
"PPAY": "Plasma Finance",
"PPBLZ": "Pepemon Pepeballs",
"PPC": "PeerCoin",
"PPCOIN": "Project Plutus",
"PPFT": "Papparico Finance",
"PPI": "Primpy",
"PPIZZA": "P Pizza",
@ -11285,6 +11356,7 @@
"PUMP": "PUMP",
"PUMPBTC": "pumpBTC",
"PUMPFUNBAN": "Pump Fun Ban",
"PUMPTRUMP": "PUMP TRUMP",
"PUN": "Punkko",
"PUNCH": "PUNCHWORD",
"PUNDIX": "Pundi X",
@ -11360,6 +11432,7 @@
"PYRV1": "Vulcan Forged v1",
"PYT": "Payther",
"PYTH": "Pyth Network",
"PYTHIA": "Pythia",
"PYUSD": "PayPal USD",
"PZETH": "pzETH",
"PZM": "Prizm",
@ -11683,6 +11756,7 @@
"RENQ": "Renq Finance",
"RENS": "Rens",
"RENT": "Rent AI",
"RENTA": "Renta Network",
"RENTBE": "Rentberry",
"REP": "Augur",
"REPE": "Resistance Pepe",
@ -11766,6 +11840,7 @@
"RIDEMY": "Ride My Car",
"RIF": "RIF Token",
"RIF3": "MetaTariffv3",
"RIFA": "Rifampicin",
"RIFI": "Rikkei Finance",
"RIGEL": "Rigel Finance",
"RIK": "RIKEZA",
@ -11952,6 +12027,7 @@
"RSUN": "RisingSun",
"RSUSHI": "Sushi (Rainbow Bridge)",
"RSV": "Reserve",
"RSVV1": "Reserve v1",
"RSWETH": "Restaked Swell Ethereum",
"RT2": "RotoCoin",
"RTB": "AB-CHAIN",
@ -11999,6 +12075,8 @@
"RUX": "Gacrux NFT",
"RVC": "Revenue Coin",
"RVF": "RocketX exchange",
"RVFV1": "RocketX exchange v1",
"RVFV2": "RocketX exchange v2",
"RVL": "Revolotto",
"RVLNG": "RevolutionGames",
"RVLT": "Revolt 2 Earn",
@ -12170,7 +12248,7 @@
"SBTC": "Super Bitcoin",
"SC": "Siacoin",
"SC20": "Shine Chain",
"SCA": "SiaClassic",
"SCA": "Scallop",
"SCALE": "Scalia Infrastructure",
"SCAM": "Scam Coin",
"SCAMP": "ScamPump",
@ -12244,8 +12322,10 @@
"SDAO": "SingularityDAO",
"SDC": "ShadowCash",
"SDCRV": "Stake DAO CRV",
"SDEUSD": "Staked deUSD",
"SDEX": "SmarDex",
"SDL": "Saddle Finance",
"SDM": "Shieldeum",
"SDME": "SDME",
"SDN": "Shiden Network",
"SDO": "TheSolanDAO",
@ -12311,6 +12391,7 @@
"SENSOV1": "SENSO v1",
"SENSUS": "Sensus",
"SENT": "Sentinel",
"SENTAI": "SentAI",
"SENTI": "Sentinel Bot Ai",
"SENTR": "Sentre Protocol",
"SEON": "Seedon",
@ -12519,6 +12600,7 @@
"SHX": "Stronghold Token",
"SHYTCOIN": "ShytCoin",
"SI": "Siren",
"SIACLASSIC": "SiaClassic",
"SIB": "SibCoin",
"SIBA": "SibaInu",
"SIC": "Swisscoin",
@ -12552,6 +12634,7 @@
"SILVERSTAND": "Silver Standard",
"SILVERWAY": "Silverway",
"SIM": "Simpson",
"SIMBA": "SIMBA The Sloth",
"SIMP": "SO-COL",
"SIMPLE": "SimpleChain",
"SIMPS": "Simpson MAGA",
@ -12715,6 +12798,7 @@
"SMLY": "SmileyCoin",
"SMM": "TrendingTool.io",
"SMOG": "Smog",
"SMOK": "Smoking Chicken Fish",
"SMOKE": "Smoke",
"SMOL": "Smolcoin",
"SMOLE": "smolecoin",
@ -12741,6 +12825,7 @@
"SNA": "SUKUYANA",
"SNAC": "SnackboxAI",
"SNACK": "Crypto Snack",
"SNAI": "SwarmNode.ai",
"SNAIL": "SnailBrook",
"SNAKE": "snake",
"SNAKES": "Snakes Game",
@ -12817,7 +12902,8 @@
"SOFTCO": "SOFT COQ INU",
"SOH": "Stohn Coin",
"SOHOT": "SOHOTRN",
"SOIL": "SoilCoin",
"SOIL": "Soil",
"SOILCOIN": "SoilCoin",
"SOJ": "Sojourn Coin",
"SOK": "shoki",
"SOKU": "Soku Swap",
@ -12877,6 +12963,7 @@
"SOLSPONGE": "Solsponge",
"SOLT": "Soltalk AI",
"SOLTR": "SolTrump",
"SOLV": "Solv Protocol",
"SOLVBTC": "Solv Protocol SolvBTC",
"SOLVBTCBBN": "Solv Protocol SolvBTC.BBN",
"SOLVBTCCORE": "Solv Protocol SolvBTC.CORE",
@ -12971,6 +13058,7 @@
"SPENDC": "SpendCoin",
"SPENT": "Espento",
"SPEPE": "SolanaPepe",
"SPERG": "Bloomsperg Terminal",
"SPEX": "StepEx",
"SPF": "SportyCo",
"SPFC": "São Paulo FC Fan Token",
@ -13040,6 +13128,7 @@
"SPY": "Smarty Pay",
"SPYRO": "SPYRO",
"SQAT": "Syndiqate",
"SQD": "SQD",
"SQG": "Squid Token",
"SQGROW": "SquidGrow",
"SQL": "Squall Coin",
@ -13296,6 +13385,7 @@
"SUIA": "SUIA",
"SUIAI": "SUI Agents",
"SUIB": "Suiba Inu",
"SUIDEPIN": "Sui DePIN",
"SUIJAK": "Suijak",
"SUILAMA": "Suilama",
"SUIMAN": "Suiman",
@ -13385,6 +13475,7 @@
"SWAPP": "SWAPP Protocol",
"SWAPZ": "SWAPZ.app",
"SWARM": "SwarmCoin",
"SWARMS": "Swarms",
"SWASH": "Swash",
"SWAY": "Sway Social",
"SWBTC": "Swell Restaked BTC",
@ -13449,6 +13540,7 @@
"SYLO": "Sylo",
"SYLV": "Sylvester",
"SYM": "SymVerse",
"SYMP": "Sympson AI",
"SYN": "Synapse",
"SYNC": "Syncus",
"SYNCC": "SyncCoin",
@ -13462,7 +13554,8 @@
"SYNR": "MOBLAND",
"SYNT": "Synthetix Network",
"SYNTE": "Synternet",
"SYNTH": "Synthswap",
"SYNTH": "SYNTHR",
"SYNTHSWAP": "Synthswap",
"SYNX": "Syndicate",
"SYPOOL": "Sypool",
"SYRUP": "Syrup",
@ -13679,7 +13772,6 @@
"THAVAGE": "Mike Tython",
"THC": "The Hempcoin",
"THD": "Trump Harris Debate",
"THE": "The Protocol",
"THE9": "THE9",
"THEAICOIN": "AI",
"THEB": "The Boys Club",
@ -13697,7 +13789,9 @@
"THEN": "THENA",
"THEO": "Theopetra",
"THEOS": "Theos",
"THEP": "The Protocol",
"THES": "The Standard Protocol (USDS)",
"THESTANDARD": "Standard Token",
"THETA": "Theta Network",
"THETAN": "Thetan Coin",
"THETRIBE": "The Tribe",
@ -13751,7 +13845,8 @@
"TIM": "TIMTIM GAMES",
"TIME": "Chrono.tech",
"TIMES": "DARKTIMES",
"TIMI": "Timicoin",
"TIMI": "This Is My Iguana",
"TIMICOIN": "Timicoin",
"TIN": "Token IN",
"TINC": "Tiny Coin",
"TINKU": "TinkuCoin",
@ -14023,7 +14118,7 @@
"TRUM": "TrumpBucks",
"TRUMAGA": "TrumpMAGA",
"TRUMATIC": "TruFin Staked MATIC",
"TRUMP": "MAGA",
"TRUMP": "OFFICIAL TRUMP",
"TRUMP2": "Trump2024",
"TRUMP2024": "Donald Trump",
"TRUMP3": "Trump MP3",
@ -14048,7 +14143,8 @@
"TRUMPHAT": "Trump Hat",
"TRUMPINU": "Trump Inu",
"TRUMPJ": "TRUMPJR",
"TRUMPJR": "TrumpJr",
"TRUMPJR": "OFFICIAL TRUMP JR",
"TRUMPJRVIP": "TrumpJr",
"TRUMPM": "TRUMP MAGA PRESIDENT",
"TRUMPMA": "TRUMP MAGA SUPER",
"TRUMPMAGA": "President Trump MAGA",
@ -14094,6 +14190,7 @@
"TSLT": "Tamkin",
"TSN": "Tsunami Exchange Token",
"TSR": "Tesra",
"TST": "Teleport System Token",
"TSUBASAUT": "TSUBASA Utility Token",
"TSUGT": "Captain Tsubasa",
"TSUJI": "Tsutsuji",
@ -14234,6 +14331,7 @@
"UETL": "Useless Eth Token Lite",
"UFARM": "UniFarm",
"UFC": "Union Fair Coin",
"UFD": "Unicorn Fart Dust",
"UFFYI": "Unlimited FiscusFYI",
"UFI": "PureFi",
"UFO": "UFO Gaming",
@ -14306,6 +14404,7 @@
"UNICORN": "UNICORN Token",
"UNIDEXAI": "UniDexAI",
"UNIDX": "UniDex",
"UNIDXV1": "UniDex v1",
"UNIE": "Uniswap Protocol Token (Avalanche Bridge)",
"UNIETH": "Universal ETH",
"UNIFY": "Unify",
@ -14376,6 +14475,7 @@
"USCC": "USC",
"USCOIN": "USCoin",
"USD0": "Usual",
"USD1": "USD1",
"USD3": "Web 3 Dollar",
"USDA": "USDA",
"USDAP": "Bond Appetite USD",
@ -14447,6 +14547,7 @@
"USTCW": "TerraClassicUSD Wormhole",
"USTX": "UpStableToken",
"USUAL": "Usual",
"USUALX": "USUALx",
"USV": "Universal Store of Value",
"USX": "USX Quantum",
"USYC": "Hashnote USYC",
@ -15194,6 +15295,7 @@
"WORX": "Worx",
"WOS": "Wolf Of Solana",
"WOT": "World Of Trump",
"WOULD": "would",
"WOW": "WOWswap",
"WOWS": "Wolves of Wall Street",
"WOZX": "Efforce",
@ -15241,6 +15343,7 @@
"WSTORV1": "StorageChain v1",
"WSTR": "Wrapped Star",
"WSTUSDT": "wstUSDT",
"WSTUSR": "Resolv wstUSR",
"WSX": "WeAreSatoshi",
"WT": "WeToken",
"WTAO": "Wrapped TAO",
@ -15260,6 +15363,7 @@
"WUF": "WUFFI",
"WUK": "WUKONG",
"WUKONG": "Sun Wukong",
"WULFY": "Wulfy",
"WUSD": "Worldwide USD",
"WUST": "Wrapped UST Token",
"WVG0": "Wrapped Virgin Gen-0 CryptoKittties",
@ -15721,6 +15825,8 @@
"YIELD": "Yield Protocol",
"YIELDX": "Yield Finance",
"YIKES": "Yikes Dog",
"YILONG": "Yi Long Ma",
"YILONGMA": "Chinese Elon Musk",
"YIN": "YIN Finance",
"YINBI": "Yinbi",
"YLAY": "Yelay",
@ -15729,6 +15835,7 @@
"YLDY": "Yieldly",
"YMC": "YamahaCoin",
"YMS": "Yeni Malatyaspor Token",
"YNE": "yesnoerror",
"YNETH": "YieldNest Restaked ETH",
"YO": "Yobit Token",
"YOBASE": "All Your Base",
@ -15920,6 +16027,7 @@
"ZKDX": "ZKDX",
"ZKE": "zkEra Finance",
"ZKEVM": "zkEVMChain (BSC)",
"ZKEX": "zkExchange",
"ZKF": "ZKFair",
"ZKGROK": "ZKGROK",
"ZKGUN": "zkGUN",

View File

@ -29,6 +29,7 @@ import { isISIN } from 'class-validator';
import { countries } from 'countries-list';
import {
addDays,
addYears,
format,
isAfter,
isBefore,
@ -123,6 +124,19 @@ export class FinancialModelingPrepService implements DataProviderInterface {
}
);
const [etfInformation] = await fetch(
`${this.getUrl({ version: 4 })}/etf-info?symbol=${symbol}&apikey=${this.apiKey}`,
{
signal: AbortSignal.timeout(
this.configurationService.get('REQUEST_TIMEOUT')
)
}
).then((res) => res.json());
if (etfInformation.website) {
response.url = etfInformation.website;
}
const [portfolioDate] = await fetch(
`${this.getUrl({ version: 4 })}/etf-holdings/portfolio-date?symbol=${symbol}&apikey=${this.apiKey}`,
{
@ -273,30 +287,44 @@ export class FinancialModelingPrepService implements DataProviderInterface {
}: GetHistoricalParams): Promise<{
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse };
}> {
const MAX_YEARS_PER_REQUEST = 5;
const result: {
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse };
} = {
[symbol]: {}
};
let currentFrom = from;
try {
const { historical } = await fetch(
`${this.URL}/historical-price-full/${symbol}?apikey=${this.apiKey}`,
{
signal: AbortSignal.timeout(requestTimeout)
}
).then((res) => res.json());
while (isBefore(currentFrom, to) || isSameDay(currentFrom, to)) {
const currentTo = isBefore(
addYears(currentFrom, MAX_YEARS_PER_REQUEST),
to
)
? addYears(currentFrom, MAX_YEARS_PER_REQUEST)
: to;
const result: {
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse };
} = {
[symbol]: {}
};
const { historical } = await fetch(
`${this.URL}/historical-price-full/${symbol}?apikey=${this.apiKey}&from=${format(currentFrom, DATE_FORMAT)}&to=${format(currentTo, DATE_FORMAT)}`,
{
signal: AbortSignal.timeout(requestTimeout)
}
).then((res) => res.json());
for (const { adjClose, date } of historical) {
if (
(isSameDay(parseDate(date), from) ||
isAfter(parseDate(date), from)) &&
isBefore(parseDate(date), to)
) {
result[symbol][date] = {
marketPrice: adjClose
};
for (const { adjClose, date } of historical) {
if (
(isSameDay(parseDate(date), currentFrom) ||
isAfter(parseDate(date), currentFrom)) &&
isBefore(parseDate(date), currentTo)
) {
result[symbol][date] = {
marketPrice: adjClose
};
}
}
currentFrom = addYears(currentFrom, MAX_YEARS_PER_REQUEST);
}
return result;

View File

@ -282,7 +282,7 @@ export class ManualService implements DataProviderInterface {
)
});
if (response.headers['content-type']?.includes('application/json')) {
if (response.headers.get('content-type')?.includes('application/json')) {
const data = await response.json();
const value = String(

View File

@ -177,9 +177,13 @@ export class SymbolProfileService {
symbolProfile?.countries as unknown as Prisma.JsonArray
),
dateOfFirstActivity: undefined as Date,
holdings: this.getHoldings(symbolProfile),
holdings: this.getHoldings(
symbolProfile?.holdings as unknown as Prisma.JsonArray
),
scraperConfiguration: this.getScraperConfiguration(symbolProfile),
sectors: this.getSectors(symbolProfile),
sectors: this.getSectors(
symbolProfile?.sectors as unknown as Prisma.JsonArray
),
symbolMapping: this.getSymbolMapping(symbolProfile)
};
@ -209,8 +213,9 @@ export class SymbolProfileService {
(item.SymbolProfileOverrides.holdings as unknown as Holding[])
?.length > 0
) {
item.holdings = item.SymbolProfileOverrides
.holdings as unknown as Holding[];
item.holdings = this.getHoldings(
item.SymbolProfileOverrides?.holdings as unknown as Prisma.JsonArray
);
}
item.name = item.SymbolProfileOverrides?.name ?? item.name;
@ -219,8 +224,9 @@ export class SymbolProfileService {
(item.SymbolProfileOverrides.sectors as unknown as Sector[])?.length >
0
) {
item.sectors = item.SymbolProfileOverrides
.sectors as unknown as Sector[];
item.sectors = this.getSectors(
item.SymbolProfileOverrides?.sectors as unknown as Prisma.JsonArray
);
}
item.url = item.SymbolProfileOverrides?.url ?? item.url;
@ -249,18 +255,20 @@ export class SymbolProfileService {
});
}
private getHoldings(symbolProfile: SymbolProfile): Holding[] {
return ((symbolProfile?.holdings as Prisma.JsonArray) ?? []).map(
(holding) => {
const { name, weight } = holding as Prisma.JsonObject;
private getHoldings(aHoldings: Prisma.JsonArray = []): Holding[] {
if (aHoldings === null) {
return [];
}
return {
allocationInPercentage: weight as number,
name: (name as string) ?? UNKNOWN_KEY,
valueInBaseCurrency: undefined
};
}
);
return aHoldings.map((holding) => {
const { name, weight } = holding as Prisma.JsonObject;
return {
allocationInPercentage: weight as number,
name: (name as string) ?? UNKNOWN_KEY,
valueInBaseCurrency: undefined
};
});
}
private getScraperConfiguration(
@ -285,17 +293,19 @@ export class SymbolProfileService {
return null;
}
private getSectors(symbolProfile: SymbolProfile): Sector[] {
return ((symbolProfile?.sectors as Prisma.JsonArray) ?? []).map(
(sector) => {
const { name, weight } = sector as Prisma.JsonObject;
private getSectors(aSectors: Prisma.JsonArray = []): Sector[] {
if (aSectors === null) {
return [];
}
return {
name: (name as string) ?? UNKNOWN_KEY,
weight: weight as number
};
}
);
return aSectors.map((sector) => {
const { name, weight } = sector as Prisma.JsonObject;
return {
name: (name as string) ?? UNKNOWN_KEY,
weight: weight as number
};
});
}
private getSymbolMapping(symbolProfile: SymbolProfile) {

View File

@ -1,4 +1,7 @@
import { extractNumberFromString } from '@ghostfolio/common/helper';
import {
extractNumberFromString,
getNumberFormatGroup
} from '@ghostfolio/common/helper';
describe('Helper', () => {
describe('Extract number from string', () => {
@ -32,8 +35,85 @@ describe('Helper', () => {
).toEqual(99999.99);
});
it('Get decimal number (comma notation) for locale where currency is not grouped by default', () => {
expect(
extractNumberFromString({ locale: 'es-ES', value: '999,99' })
).toEqual(999.99);
});
it('Not a number', () => {
expect(extractNumberFromString({ value: 'X' })).toEqual(NaN);
});
});
describe('Get number format group', () => {
let languageGetter: jest.SpyInstance<string, [], any>;
beforeEach(() => {
languageGetter = jest.spyOn(window.navigator, 'language', 'get');
});
it('Get de-CH number format group', () => {
expect(getNumberFormatGroup('de-CH')).toEqual('');
});
it('Get de-CH number format group when it is default', () => {
languageGetter.mockReturnValue('de-CH');
expect(getNumberFormatGroup()).toEqual('');
});
it('Get de-DE number format group', () => {
expect(getNumberFormatGroup('de-DE')).toEqual('.');
});
it('Get de-DE number format group when it is default', () => {
languageGetter.mockReturnValue('de-DE');
expect(getNumberFormatGroup()).toEqual('.');
});
it('Get en-GB number format group', () => {
expect(getNumberFormatGroup('en-GB')).toEqual(',');
});
it('Get en-GB number format group when it is default', () => {
languageGetter.mockReturnValue('en-GB');
expect(getNumberFormatGroup()).toEqual(',');
});
it('Get en-US number format group', () => {
expect(getNumberFormatGroup('en-US')).toEqual(',');
});
it('Get en-US number format group when it is default', () => {
languageGetter.mockReturnValue('en-US');
expect(getNumberFormatGroup()).toEqual(',');
});
it('Get es-ES number format group', () => {
expect(getNumberFormatGroup('es-ES')).toEqual('.');
});
it('Get es-ES number format group when it is default', () => {
languageGetter.mockReturnValue('es-ES');
expect(getNumberFormatGroup()).toEqual('.');
});
it('Get ru-RU number format group', () => {
expect(getNumberFormatGroup('ru-RU')).toEqual(' ');
});
it('Get ru-RU number format group when it is default', () => {
languageGetter.mockReturnValue('ru-RU');
expect(getNumberFormatGroup()).toEqual(' ');
});
it('Get zh-CN number format group', () => {
expect(getNumberFormatGroup('zh-CN')).toEqual(',');
});
it('Get zh-CN number format group when it is default', () => {
languageGetter.mockReturnValue('zh-CN');
expect(getNumberFormatGroup()).toEqual(',');
});
});
});

View File

@ -251,7 +251,9 @@ export function getNumberFormatDecimal(aLocale?: string) {
}
export function getNumberFormatGroup(aLocale = getLocale()) {
const formatObject = new Intl.NumberFormat(aLocale).formatToParts(9999.99);
const formatObject = new Intl.NumberFormat(aLocale, {
useGrouping: true
}).formatToParts(9999.99);
return formatObject.find((object) => {
return object.type === 'group';

View File

@ -1,5 +1,5 @@
export interface Holding {
allocationInPercentage?: number;
allocationInPercentage: number;
name: string;
valueInBaseCurrency: number;
}

225
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "ghostfolio",
"version": "2.135.0",
"version": "2.136.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ghostfolio",
"version": "2.135.0",
"version": "2.136.0",
"hasInstallScript": true,
"license": "AGPL-3.0",
"dependencies": {
@ -72,7 +72,7 @@
"lodash": "4.17.21",
"marked": "15.0.4",
"ms": "3.0.0-canary.1",
"ng-extract-i18n-merge": "2.13.1",
"ng-extract-i18n-merge": "2.14.1",
"ngx-device-detector": "9.0.0",
"ngx-markdown": "19.0.0",
"ngx-skeleton-loader": "9.0.0",
@ -125,7 +125,7 @@
"@storybook/addon-interactions": "8.4.7",
"@storybook/angular": "8.4.7",
"@storybook/core-server": "8.4.7",
"@trivago/prettier-plugin-sort-imports": "4.3.0",
"@trivago/prettier-plugin-sort-imports": "5.2.1",
"@types/big.js": "6.2.2",
"@types/cache-manager": "4.0.6",
"@types/color": "4.2.0",
@ -2588,46 +2588,6 @@
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
}
},
"node_modules/@babel/helper-environment-visitor": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz",
"integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-function-name": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz",
"integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/template": "^7.24.7",
"@babel/types": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-hoist-variables": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz",
"integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-member-expression-to-functions": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz",
@ -10508,161 +10468,40 @@
}
},
"node_modules/@trivago/prettier-plugin-sort-imports": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.3.0.tgz",
"integrity": "sha512-r3n0onD3BTOVUNPhR4lhVK4/pABGpbA7bW3eumZnYdKaHkf1qEC+Mag6DPbGNuuh0eG8AaYj+YqmVHSiGslaTQ==",
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-5.2.1.tgz",
"integrity": "sha512-NDZndt0fmVThIx/8cExuJHLZagUVzfGCoVrwH9x6aZvwfBdkrDFTYujecek6X2WpG4uUFsVaPg5+aNQPSyjcmw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@babel/generator": "7.17.7",
"@babel/parser": "^7.20.5",
"@babel/traverse": "7.23.2",
"@babel/types": "7.17.0",
"javascript-natural-sort": "0.7.1",
"@babel/generator": "^7.26.2",
"@babel/parser": "^7.26.2",
"@babel/traverse": "^7.25.9",
"@babel/types": "^7.26.0",
"javascript-natural-sort": "^0.7.1",
"lodash": "^4.17.21"
},
"engines": {
"node": ">18.12"
},
"peerDependencies": {
"@vue/compiler-sfc": "3.x",
"prettier": "2.x - 3.x"
"prettier": "2.x - 3.x",
"prettier-plugin-svelte": "3.x",
"svelte": "4.x || 5.x"
},
"peerDependenciesMeta": {
"@vue/compiler-sfc": {
"optional": true
},
"prettier-plugin-svelte": {
"optional": true
},
"svelte": {
"optional": true
}
}
},
"node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/generator": {
"version": "7.17.7",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz",
"integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.17.0",
"jsesc": "^2.5.1",
"source-map": "^0.5.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/generator/node_modules/@babel/types": {
"version": "7.26.3",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz",
"integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.25.9",
"@babel/helper-validator-identifier": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/traverse": {
"version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
"integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.22.13",
"@babel/generator": "^7.23.0",
"@babel/helper-environment-visitor": "^7.22.20",
"@babel/helper-function-name": "^7.23.0",
"@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
"@babel/parser": "^7.23.0",
"@babel/types": "^7.23.0",
"debug": "^4.1.0",
"globals": "^11.1.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/traverse/node_modules/@babel/generator": {
"version": "7.26.3",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz",
"integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.26.3",
"@babel/types": "^7.26.3",
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25",
"jsesc": "^3.0.2"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/traverse/node_modules/@babel/types": {
"version": "7.26.3",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz",
"integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.25.9",
"@babel/helper-validator-identifier": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/traverse/node_modules/jsesc": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
"dev": true,
"license": "MIT",
"bin": {
"jsesc": "bin/jsesc"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/types": {
"version": "7.17.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz",
"integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.16.7",
"to-fast-properties": "^2.0.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@trivago/prettier-plugin-sort-imports/node_modules/jsesc": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true,
"license": "MIT",
"bin": {
"jsesc": "bin/jsesc"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@trivago/prettier-plugin-sort-imports/node_modules/source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/@trysound/sax": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
@ -24935,9 +24774,9 @@
"license": "MIT"
},
"node_modules/ng-extract-i18n-merge": {
"version": "2.13.1",
"resolved": "https://registry.npmjs.org/ng-extract-i18n-merge/-/ng-extract-i18n-merge-2.13.1.tgz",
"integrity": "sha512-aU+shz0VSe0qqKYmlpg42P2C6Ol6eQ+DZDTYzQM4PTMfEBWJNFtl+c7B+MA68/AzdpcNPKu0BWJLZchCdZtjhQ==",
"version": "2.14.1",
"resolved": "https://registry.npmjs.org/ng-extract-i18n-merge/-/ng-extract-i18n-merge-2.14.1.tgz",
"integrity": "sha512-hymcJcjfXJ+0r3EQShaSGmsST0AV3usgJuNMf6l04X+nIsgUPLMrXPBB/hVVVlAOjRcEB7RamKnfmexa5Rq1tw==",
"license": "MIT",
"dependencies": {
"@angular-devkit/architect": "^0.1301.0 || ^0.1401.0 || ^0.1501.0 || ^0.1601.0 || ^0.1700.0 || ^0.1800.0 || ^0.1900.0",
@ -30295,16 +30134,6 @@
"devOptional": true,
"license": "BSD-3-Clause"
},
"node_modules/to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",

View File

@ -1,6 +1,6 @@
{
"name": "ghostfolio",
"version": "2.135.0",
"version": "2.136.0",
"homepage": "https://ghostfol.io",
"license": "AGPL-3.0",
"repository": "https://github.com/ghostfolio/ghostfolio",
@ -118,7 +118,7 @@
"lodash": "4.17.21",
"marked": "15.0.4",
"ms": "3.0.0-canary.1",
"ng-extract-i18n-merge": "2.13.1",
"ng-extract-i18n-merge": "2.14.1",
"ngx-device-detector": "9.0.0",
"ngx-markdown": "19.0.0",
"ngx-skeleton-loader": "9.0.0",
@ -171,7 +171,7 @@
"@storybook/addon-interactions": "8.4.7",
"@storybook/angular": "8.4.7",
"@storybook/core-server": "8.4.7",
"@trivago/prettier-plugin-sort-imports": "4.3.0",
"@trivago/prettier-plugin-sort-imports": "5.2.1",
"@types/big.js": "6.2.2",
"@types/cache-manager": "4.0.6",
"@types/color": "4.2.0",
@ -213,6 +213,6 @@
"node": ">=20"
},
"prisma": {
"seed": "node prisma/seed.js"
"seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts"
}
}

View File

@ -1,4 +1,5 @@
const { PrismaClient } = require('@prisma/client');
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {