├── .npmrc ├── .env.example ├── src ├── lib │ ├── constants.ts │ ├── modules │ │ ├── artists │ │ │ ├── artists.type.ts │ │ │ ├── artists.controller.ts │ │ │ └── artists.service.ts │ │ ├── musicsearch │ │ │ ├── interfaces │ │ │ │ ├── record.interface.ts │ │ │ │ ├── musicentryrequest.interface.ts │ │ │ │ └── musicqueryrequest.interface.ts │ │ │ ├── searchrequest.service.ts │ │ │ └── searchrequest.controller.ts │ │ ├── releases │ │ │ ├── releases.controller.ts │ │ │ └── releases.service.ts │ │ ├── tracks │ │ │ ├── tracks.controller.ts │ │ │ └── tracks.service.ts │ │ ├── albums │ │ │ ├── albums.type.ts │ │ │ ├── albums.controller.ts │ │ │ └── albums.service.ts │ │ ├── downloader │ │ │ ├── downloader.service.ts │ │ │ └── downloader.controller.ts │ │ ├── platformenvironment │ │ │ ├── interfaces │ │ │ │ └── PlatformEnvironment.interface.ts │ │ │ ├── platformenvironment.service.ts │ │ │ └── classes │ │ │ │ └── TelegramEnvironment.class.ts │ │ ├── tokens │ │ │ └── tokens.controller.ts │ │ └── httprequest │ │ │ └── httprequest.service.ts │ ├── api.ts │ ├── stores.ts │ └── tools │ │ └── cookies.tool.ts ├── components │ ├── component.types.ts │ ├── icons │ │ └── Icons.ts │ ├── utils │ │ ├── AlbumPlaceholder.svelte │ │ ├── Placeholder.svelte │ │ ├── LoadinWheel.svelte │ │ └── Utils.ts │ ├── elements │ │ ├── Banner.svelte │ │ ├── ArtistCard.svelte │ │ ├── RecordCard.svelte │ │ └── AlbumCard.svelte │ ├── Artists.svelte │ ├── Releases.svelte │ ├── searcher │ │ ├── FilterSelector.svelte │ │ └── Searchbar.svelte │ ├── Albums.svelte │ ├── Tracks.svelte │ └── Main.svelte ├── app.d.ts ├── app.html ├── routes │ ├── artist │ │ ├── albums │ │ │ ├── +page.ts │ │ │ └── +page.svelte │ │ ├── tracks │ │ │ ├── +page.ts │ │ │ └── +page.svelte │ │ ├── +page.ts │ │ └── +page.svelte │ ├── +layout.ts │ ├── album │ │ ├── +page.ts │ │ └── +page.svelte │ ├── +layout.svelte │ └── +page.svelte └── app.css ├── .gitattributes ├── static ├── favicon.png ├── close.svg ├── search.svg ├── check-circle.svg └── telegram-web-app.js ├── .gitignore ├── postcss.config.cjs ├── vite.config.ts ├── tailwind.config.cjs ├── .vscode └── launch.json ├── tsconfig.json ├── svelte.config.js ├── .github └── workflows │ └── deploy-stage.yml ├── README.md ├── package.json └── yarn.lock /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | VITE_API_BASE_URL="https://my-backend-url.com" 2 | -------------------------------------------------------------------------------- /src/lib/constants.ts: -------------------------------------------------------------------------------- 1 | export const TOKEN_NAMES = ['token', 'continuationToken'] 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/egorkonovalov/album-saver-web-v2/HEAD/static/favicon.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | -------------------------------------------------------------------------------- /src/components/component.types.ts: -------------------------------------------------------------------------------- 1 | export type Layout = "track-list" | "album-grid" | "artist-grid" | "artist-list"; 2 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | 'postcss-import': {}, 4 | 'tailwindcss/nesting': {}, 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /src/lib/modules/artists/artists.type.ts: -------------------------------------------------------------------------------- 1 | import type { Record } from "$lib/modules/musicsearch/interfaces/record.interface" 2 | 3 | export type ArtistRecords = { 4 | result: Record[] 5 | } 6 | -------------------------------------------------------------------------------- /src/lib/modules/musicsearch/interfaces/record.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Record { 2 | [key: string]: string; 3 | title: string; 4 | author: string; 5 | imageUrl: string; 6 | year: string; 7 | recordType: string; 8 | } 9 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import type { UserConfig } from 'vite'; 3 | 4 | const config: UserConfig = { 5 | plugins: [ 6 | sveltekit(), 7 | ] 8 | }; 9 | 10 | export default config; 11 | -------------------------------------------------------------------------------- /static/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./src/**/*.{html,js,svelte,ts}'], 4 | darkMode: 'class', 5 | theme: { 6 | extend: {} 7 | }, 8 | plugins: [] 9 | }; 10 | -------------------------------------------------------------------------------- /src/components/icons/Icons.ts: -------------------------------------------------------------------------------- 1 | export const ChevronRight = 2 | ''; 3 | -------------------------------------------------------------------------------- /src/lib/modules/releases/releases.controller.ts: -------------------------------------------------------------------------------- 1 | import releasesService from "./releases.service"; 2 | 3 | class ReleasesController { 4 | async getReleases() { 5 | return await releasesService.getReleases(); 6 | } 7 | } 8 | 9 | export default new ReleasesController(); 10 | -------------------------------------------------------------------------------- /src/lib/modules/tracks/tracks.controller.ts: -------------------------------------------------------------------------------- 1 | import tracksService from "./tracks.service"; 2 | 3 | class TracksController { 4 | async getTracks(query: string) { 5 | return await tracksService.getTracks(query); 6 | } 7 | } 8 | 9 | export default new TracksController(); 10 | -------------------------------------------------------------------------------- /src/lib/modules/musicsearch/interfaces/musicentryrequest.interface.ts: -------------------------------------------------------------------------------- 1 | export interface MusicEntryRequest { 2 | userId: number, 3 | youTubeMusicPlaylistUrl: string; 4 | entityType: number; 5 | } 6 | 7 | export interface MusicSetRequest { 8 | userId: number, 9 | urls: string[] 10 | } 11 | -------------------------------------------------------------------------------- /src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://kit.svelte.dev/docs/types#app 2 | // for information about these interfaces 3 | // and what to do when importing types 4 | declare namespace App { 5 | // interface Locals {} 6 | // interface PageData {} 7 | // interface Error {} 8 | // interface Platform {} 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/modules/albums/albums.type.ts: -------------------------------------------------------------------------------- 1 | import type { Record } from "$lib/modules/musicsearch/interfaces/record.interface" 2 | 3 | export type Album = { 4 | result: Record[] 5 | albumTitle: string 6 | albumImage: string 7 | channelUrl: string 8 | artistName: string 9 | } 10 | 11 | export type Albums = { 12 | result: Record[] 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/modules/albums/albums.controller.ts: -------------------------------------------------------------------------------- 1 | import albumsService from "./albums.service"; 2 | 3 | class AlbumsController { 4 | async getAlbums(query: string) { 5 | return await albumsService.getAlbums(query) 6 | } 7 | async getAlbum(albumUrl: string) { 8 | return await albumsService.getAlbum(albumUrl); 9 | } 10 | } 11 | 12 | export default new AlbumsController(); 13 | -------------------------------------------------------------------------------- /src/components/utils/AlbumPlaceholder.svelte: -------------------------------------------------------------------------------- 1 | 4 | 7 | 10 | -------------------------------------------------------------------------------- /static/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/elements/Banner.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | {#if hasIcon} 8 | search 9 | {/if} 10 |
11 | 12 |
13 |
14 | -------------------------------------------------------------------------------- /src/lib/modules/musicsearch/interfaces/musicqueryrequest.interface.ts: -------------------------------------------------------------------------------- 1 | export enum RequestType { 2 | Album = "albums", 3 | Track = "tracks", 4 | Release = "releases", 5 | Artist = "artists", 6 | ArtistTracks = "artist-tracks", 7 | ArtistAlbums = "artist-albums", 8 | AlbumTracks = "album-tracks", 9 | } 10 | 11 | export type MusicQueryRequest = { 12 | [key: string]: string | number; 13 | }; 14 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | %sveltekit.head% 9 | 10 | 11 |
%sveltekit.body%
12 | 13 | 14 | -------------------------------------------------------------------------------- /src/lib/modules/releases/releases.service.ts: -------------------------------------------------------------------------------- 1 | import { API_RELEASES } from "$lib/api"; 2 | import type { Record } from "$lib/modules/musicsearch/interfaces/record.interface"; 3 | import { get } from "$lib/modules/httprequest/httprequest.service"; 4 | 5 | class ReleasesService { 6 | async getReleases() { 7 | return (await get<{ result: Record[] }>(API_RELEASES)).result; 8 | } 9 | } 10 | 11 | export default new ReleasesService(); 12 | -------------------------------------------------------------------------------- /src/lib/modules/tracks/tracks.service.ts: -------------------------------------------------------------------------------- 1 | import { API_TRACKS } from "$lib/api"; 2 | import { get } from "$lib/modules/httprequest/httprequest.service"; 3 | import type { Record } from "$lib/modules/musicsearch/interfaces/record.interface"; 4 | 5 | class TracksService { 6 | async getTracks(query: string) { 7 | return (await get<{ result: Record[] }>(API_TRACKS, { params: { query } })).result; 8 | } 9 | } 10 | 11 | export default new TracksService(); 12 | -------------------------------------------------------------------------------- /src/components/utils/Placeholder.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | {#each Array(count) as _} 8 |
9 |
10 |
11 |

12 |

13 |

14 |
15 | {/each} 16 |
17 | -------------------------------------------------------------------------------- /src/lib/modules/downloader/downloader.service.ts: -------------------------------------------------------------------------------- 1 | import { API_DOWNLOAD, API_DOWNLOADSET } from "$lib/api" 2 | import { post } from "$lib/modules/httprequest/httprequest.service" 3 | 4 | class DownloaderService { 5 | async download(query: Record) { 6 | return await post(API_DOWNLOAD, { params: { ...query } }) 7 | } 8 | async downloadSet(body: Record) { 9 | return await post(API_DOWNLOADSET, { body }) 10 | } 11 | } 12 | 13 | export default new DownloaderService() 14 | -------------------------------------------------------------------------------- /src/components/elements/ArtistCard.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | {artist.title} 11 |
12 |

{artist.title}

13 |
14 |
15 | -------------------------------------------------------------------------------- /src/lib/modules/albums/albums.service.ts: -------------------------------------------------------------------------------- 1 | import { API_ALBUMS, API_ALBUM } from "$lib/api"; 2 | import { get } from "$lib/modules/httprequest/httprequest.service"; 3 | import type { Album, Albums } from "./albums.type"; 4 | 5 | class AlbumsService { 6 | async getAlbums(query: string) { 7 | return (await get(API_ALBUMS, { params: { query } })).result; 8 | } 9 | async getAlbum(albumUrl: string) { 10 | return await get(API_ALBUM, { params: { albumUrl } }) 11 | } 12 | } 13 | 14 | export default new AlbumsService(); 15 | -------------------------------------------------------------------------------- /src/lib/api.ts: -------------------------------------------------------------------------------- 1 | export const API_SEARCH = "/search"; 2 | export const API_ALBUMS = "/albums"; 3 | export const API_TRACKS = "/tracks"; 4 | export const API_RELEASES = "/releases"; 5 | 6 | export const API_DOWNLOAD = "/download"; 7 | export const API_DOWNLOADSET = "/download-set"; 8 | 9 | export const API_ALBUM = "/album/tracks"; 10 | 11 | export const API_ARTISTS = "/artists"; 12 | export const API_ARTISTS_TRACKS = "/artist/tracks"; 13 | export const API_ARTISTS_ALBUMS = "/artists/albums"; 14 | export const API_ARTISTS_IMAGE = "/artists/image"; 15 | -------------------------------------------------------------------------------- /src/routes/artist/albums/+page.ts: -------------------------------------------------------------------------------- 1 | import { error } from "@sveltejs/kit"; 2 | import type { PageLoad } from "./$types"; 3 | import artistsController from "$lib/modules/artists/artists.controller"; 4 | import { tokens } from "$lib/stores"; 5 | 6 | export const load = (async ({ url }) => { 7 | const artistId = url.searchParams.get("artistId"); 8 | if (artistId) { 9 | return { 10 | streamed: { 11 | albums: artistsController.getAlbums(artistId), 12 | }, 13 | }; 14 | } 15 | 16 | error(404, "Not found"); 17 | }) satisfies PageLoad; 18 | -------------------------------------------------------------------------------- /src/routes/artist/tracks/+page.ts: -------------------------------------------------------------------------------- 1 | import { error } from "@sveltejs/kit"; 2 | import type { PageLoad } from "./$types"; 3 | import artistsController from "$lib/modules/artists/artists.controller"; 4 | import { tokens } from "$lib/stores"; 5 | 6 | export const load = (async ({ url }) => { 7 | const artistId = url.searchParams.get("artistId"); 8 | 9 | if (artistId) { 10 | return { 11 | streamed: { 12 | tracks: artistsController.getTracks(artistId), 13 | }, 14 | }; 15 | } 16 | 17 | error(404, "Not found"); 18 | }) satisfies PageLoad; 19 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "skipFiles": [ 12 | "/**" 13 | ], 14 | "program": "${workspaceFolder}\\src\\routes\\artist\\[artist_id]\\+page.ts", 15 | "outFiles": [ 16 | "${workspaceFolder}/**/*.js" 17 | ] 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /src/routes/+layout.ts: -------------------------------------------------------------------------------- 1 | import platformEnvironmentService from "$lib/modules/platformenvironment/platformenvironment.service"; 2 | import type { LayoutLoad } from "./$types"; 3 | import { Environment } from "$lib/stores"; 4 | import { get } from "svelte/store"; 5 | export const ssr = false; 6 | export const prerender = true 7 | 8 | export const load = (({ route }) => { 9 | Environment.set(platformEnvironmentService.getEnvironment()) 10 | const environmentStore = get(Environment) 11 | if (route.id === '/') environmentStore.hideBackButton(); 12 | else environmentStore.showBackButton() 13 | return { environmentStore }; 14 | }) satisfies LayoutLoad; 15 | -------------------------------------------------------------------------------- /src/components/Artists.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | {#await fetchData()} 14 | 15 | {:then value} 16 |
17 | {#each value as record} 18 | 19 | {/each} 20 |
21 | {/await} 22 | -------------------------------------------------------------------------------- /src/lib/modules/artists/artists.controller.ts: -------------------------------------------------------------------------------- 1 | import artistsService from "./artists.service"; 2 | 3 | class ArtistsController { 4 | async getArtists(query: string) { 5 | return await artistsService.getArtists(query); 6 | } 7 | async getTracks(channelUrl: string, takeCount?: number) { 8 | return await artistsService.getTracks(channelUrl, takeCount); 9 | } 10 | async getAlbums(channelUrl: string, takeCount?: number) { 11 | return await artistsService.getAlbums(channelUrl, takeCount); 12 | } 13 | async getImage(channelUrl: string) { 14 | return await artistsService.getImage(channelUrl) 15 | } 16 | } 17 | 18 | export default new ArtistsController(); 19 | -------------------------------------------------------------------------------- /src/lib/modules/platformenvironment/interfaces/PlatformEnvironment.interface.ts: -------------------------------------------------------------------------------- 1 | export interface PlatformEnvironment { 2 | readonly user: { id: number, username: string } 3 | readonly userId: number; 4 | readonly platformName: string; 5 | close(): void; 6 | showBackButton(): void; 7 | hideBackButton(): void; 8 | onBackButtonClick(callback: () => void): void; 9 | showMainButton(text: string): void; 10 | hideMainButton(): void; 11 | setMainButtonText(text: string): void; 12 | onMainButtonClick(callback: () => void): void; 13 | offMainButtonClick(callback: () => void): void 14 | envokeHaptic(style: "light" | "medium" | "heavy" | "rigid" | "soft"): void 15 | } 16 | -------------------------------------------------------------------------------- /static/check-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true, 12 | "typeRoots": [ 13 | "./node_modules/@types", 14 | "./node_modules/telegram-webapps-types" 15 | ] 16 | } 17 | // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias 18 | // 19 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes 20 | // from the referenced tsconfig.json - TypeScript does not merge them in 21 | } 22 | -------------------------------------------------------------------------------- /src/routes/album/+page.ts: -------------------------------------------------------------------------------- 1 | import { error } from "@sveltejs/kit"; 2 | import type { PageLoad } from "./$types"; 3 | import albumsController from "$lib/modules/albums/albums.controller"; 4 | 5 | function getParams(url: URL, key: string) { 6 | let value = url.searchParams.get(key); 7 | if (value) return value; 8 | error(500, `The value for the parameter "${key}" was not provided.`); 9 | } 10 | 11 | export const load = (async ({ url, parent }) => { 12 | const albumUrl = getParams(url, "albumUrl"); 13 | const { environmentStore } = await parent() 14 | return { 15 | environmentStore, 16 | albumUrl, 17 | streamed: { 18 | album: albumsController.getAlbum(albumUrl), 19 | }, 20 | }; 21 | }) satisfies PageLoad; 22 | -------------------------------------------------------------------------------- /src/lib/modules/platformenvironment/platformenvironment.service.ts: -------------------------------------------------------------------------------- 1 | import { TelegramEnvironment } from "./classes/TelegramEnvironment.class"; 2 | import type { PlatformEnvironment } from "./interfaces/PlatformEnvironment.interface"; 3 | 4 | export class PlatformEnvironmentService { 5 | getEnvironment(): PlatformEnvironment { 6 | try { 7 | let _initData = Telegram.WebApp.initData; 8 | return new TelegramEnvironment(); 9 | } catch (error: any) { 10 | console.info( 11 | `You can use this website only with this telegram bot: ${ 12 | import.meta.env.VITE_API_TG_URL 13 | }` 14 | ); 15 | return error; 16 | } 17 | } 18 | } 19 | 20 | export default new PlatformEnvironmentService(); 21 | -------------------------------------------------------------------------------- /src/components/utils/LoadinWheel.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 | 12 | 20 | 25 | 26 |
27 | -------------------------------------------------------------------------------- /src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | {#if data.environmentStore instanceof TelegramEnvironment} 15 | 16 | {:else} 17 |

18 | You can use this website only with a
Telegram bot 22 |

23 | {/if} 24 | -------------------------------------------------------------------------------- /src/lib/modules/downloader/downloader.controller.ts: -------------------------------------------------------------------------------- 1 | import platformEnvironmentService from "$lib/modules/platformenvironment/platformenvironment.service" 2 | import downloaderService from "./downloader.service" 3 | 4 | class DownloaderController { 5 | async download(youTubeMusicPlaylistUrl: string, entityType: 1 | 2) { 6 | return await downloaderService.download({ 7 | youTubeMusicPlaylistUrl, 8 | userId: platformEnvironmentService.getEnvironment().userId.toString(), 9 | entityType: entityType.toString() 10 | }) 11 | } 12 | 13 | async downloadSet(urls: string[]) { 14 | return await downloaderService.downloadSet({ 15 | urls, 16 | userId: platformEnvironmentService.getEnvironment().userId.toString() 17 | }) 18 | } 19 | } 20 | 21 | export default new DownloaderController() 22 | -------------------------------------------------------------------------------- /src/lib/modules/tokens/tokens.controller.ts: -------------------------------------------------------------------------------- 1 | import { Cookies } from '$lib/tools/cookies.tool' 2 | 3 | class TokensController { 4 | set tokens(tokens: Record) { 5 | for (const [key, value] of Object.entries(tokens)) { 6 | Cookies.set(key, value); 7 | } 8 | } 9 | getTokens(names: string[]) { 10 | let tokens: Record = {}; 11 | for (let name of names) { 12 | const token = Cookies.get(name) 13 | if (token && token !== 'undefined' && token !== 'null') tokens[name] = token 14 | } 15 | if (Object.keys(tokens).length === 0 && tokens.constructor === Object) { 16 | return null 17 | } 18 | return tokens 19 | } 20 | clearTokens(names: string[]) { 21 | for (let name of names) { 22 | Cookies.deleteCookie(name) 23 | } 24 | } 25 | } 26 | 27 | export default new TokensController() 28 | -------------------------------------------------------------------------------- /src/routes/artist/albums/+page.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | {#await data.streamed.albums} 12 | 13 | {:then value} 14 |
    15 | {#each value as record} 16 |
  • 17 | 21 | 22 | 23 |
  • 24 | {/each} 25 |
26 | {/await} 27 | -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import preprocess from 'svelte-preprocess'; 2 | import adapter from '@sveltejs/adapter-static'; 3 | const dev = process.env.NODE_ENV === 'development'; 4 | 5 | /** @type {import('@sveltejs/kit').Config} */ 6 | const config = { 7 | // Consult https://github.com/sveltejs/svelte-preprocess 8 | // for more information about preprocessors 9 | preprocess: preprocess({ 10 | postcss: true, 11 | }), 12 | 13 | kit: { 14 | adapter: adapter({ 15 | // default options are shown. On some platforms 16 | // these options are set automatically — see below 17 | pages: 'build', 18 | assets: 'build', 19 | fallback: undefined, 20 | precompress: false, 21 | strict: true 22 | }), 23 | paths: { 24 | base: dev ? '' : '/album-saver-web-v2', 25 | }, 26 | appDir: 'internal', 27 | alias: { 28 | // this will match a file 29 | '$components': './src/components', 30 | } 31 | } 32 | }; 33 | 34 | export default config; 35 | -------------------------------------------------------------------------------- /src/lib/stores.ts: -------------------------------------------------------------------------------- 1 | import { writable } from "svelte/store"; 2 | import type { Record } from "./modules/musicsearch/interfaces/record.interface"; 3 | import type { PlatformEnvironment } from "./modules/platformenvironment/interfaces/PlatformEnvironment.interface"; 4 | 5 | export const tokens = writable({}); 6 | 7 | export const artist = writable({ 8 | youTubeMusicPlaylistUrl: "", 9 | title: "", 10 | author: "", 11 | imageUrl: "", 12 | year: "", 13 | recordType: "", 14 | }); 15 | 16 | export const album = writable({ 17 | youTubeMusicPlaylistUrl: "", 18 | title: "", 19 | author: "", 20 | imageUrl: "", 21 | year: "", 22 | recordType: "", 23 | }); 24 | 25 | export const Environment = writable(); 26 | 27 | export const popupContentType = writable<"artist" | "album">("album"); 28 | 29 | export const popupIsShown = writable(false); 30 | 31 | export const mainButtonText = writable("Apply"); 32 | -------------------------------------------------------------------------------- /.github/workflows/deploy-stage.yml: -------------------------------------------------------------------------------- 1 | name: Github Pages 2 | run-name: ${{ github.actor }} is deploying the application 3 | on: 4 | push: 5 | branches: 6 | - main 7 | jobs: 8 | build-deploy: 9 | environment: github-pages 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Set-up Node & Checkout 13 | uses: actions/checkout@v3 14 | - name: Set-up Node 15 | uses: actions/setup-node@v3 16 | with: 17 | node-version: 22 18 | cache: "yarn" 19 | - name: Install dependencies 20 | run: | 21 | npm install yarn@latest 22 | yarn install 23 | - name: Build 24 | env: 25 | VITE_API_BASE_URL: ${{ vars.VITE_API_BASE_URL }} 26 | run: yarn build 27 | - name: Deploy 28 | uses: peaceiris/actions-gh-pages@v3 29 | with: 30 | publish_branch: gh-pages 31 | publish_dir: build 32 | deploy_key: ${{ secrets.DEPLOY_SECRET }} 33 | -------------------------------------------------------------------------------- /src/components/Releases.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | {#await fetchData()} 15 | 16 | {:then value} 17 |
18 | {#each value as record} 19 | 23 | 24 | 25 | {/each} 26 |
27 | {/await} 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Album Saver Web (Telegram bot) 2 | 3 |
4 | Music Saver 5 |
6 | 7 | ## Developing 8 | 9 | Once you've created a project and installed dependencies with `yarn install` start a development server: 10 | 11 | ```bash 12 | yarn dev 13 | 14 | # or start the server and open the app in a new browser tab 15 | yarn dev --open 16 | ``` 17 | 18 | 19 | ## Building 20 | 21 | To create a production version of your app: 22 | 23 | ```bash 24 | yarn build 25 | ``` 26 | 27 | You can preview the production build with `yarn preview`. 28 | 29 | ## Deployment 30 | 31 | To deploy the app on GitHub Pages you need to modify [gh-pages.js](./gh-pages.js) so it points to your repository URL and holds your GitHub account information. Then use this command: 32 | 33 | ```bash 34 | yarn deploy 35 | ``` 36 | 37 | > To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. 38 | -------------------------------------------------------------------------------- /src/components/utils/Utils.ts: -------------------------------------------------------------------------------- 1 | import { RequestType } from "$lib/modules/musicsearch/interfaces/musicqueryrequest.interface"; 2 | import type { Layout } from "../component.types"; 3 | 4 | export function getLayoutType(requestType: RequestType): Layout { 5 | switch (requestType) { 6 | case RequestType.Track: 7 | return "track-list"; 8 | case RequestType.Artist: 9 | return "artist-list"; 10 | case RequestType.Album: 11 | case RequestType.Release: 12 | default: 13 | return "album-grid"; 14 | } 15 | } 16 | 17 | /** Dispatch event on click outside of node */ 18 | export function clickOutside(node: Node) { 19 | 20 | const handleClick = (event: Event) => { 21 | if (node && !node.contains(event.target as Node) && !event.defaultPrevented) { 22 | node.dispatchEvent( 23 | new CustomEvent('click_outside', node as CustomEventInit) 24 | ) 25 | } 26 | } 27 | 28 | document.addEventListener('click', handleClick, true); 29 | 30 | return { 31 | destroy() { 32 | document.removeEventListener('click', handleClick, true); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/lib/modules/artists/artists.service.ts: -------------------------------------------------------------------------------- 1 | import { API_ARTISTS, API_ARTISTS_ALBUMS, API_ARTISTS_TRACKS, API_ARTISTS_IMAGE } from "$lib/api"; 2 | import { get } from "../httprequest/httprequest.service"; 3 | import type { ArtistRecords } from "./artists.type"; 4 | 5 | class ArtistsService { 6 | async getArtists(query: string) { 7 | return (await get(API_ARTISTS, { params: { query } })).result; 8 | } 9 | async getTracks(channelUrl: string, takeCount?: number) { 10 | let query: { [key: string]: string } = { channelUrl }; 11 | if (takeCount) query.takeCount = takeCount.toString(); 12 | return (await get(API_ARTISTS_TRACKS, { params: query })).result; 13 | } 14 | async getAlbums(channelUrl: string, takeCount?: number) { 15 | let query: { [key: string]: string } = { channelUrl }; 16 | if (takeCount) query.takeCount = takeCount.toString(); 17 | return (await get(API_ARTISTS_ALBUMS, { params: query })).result; 18 | } 19 | async getImage(channelUrl: string) { 20 | return (await get<{ url: string }>(API_ARTISTS_IMAGE, { params: { channelUrl } })).url 21 | } 22 | } 23 | 24 | export default new ArtistsService(); 25 | -------------------------------------------------------------------------------- /src/components/elements/RecordCard.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 |
14 |

{record.title}

15 | {#if requestType !== RequestType.Artist} 16 |

17 | {record.author === undefined ? "Various Artists" : record.author} 18 |

19 | {/if} 20 | {#if requestType === "albums"} 21 |
22 | {#if record.recordType} 23 |

24 | {record.recordType ? record.recordType : "Album"} 25 |

26 |

27 | {/if} 28 |

{record.year}

29 |
30 | {/if} 31 |
32 | -------------------------------------------------------------------------------- /src/routes/artist/tracks/+page.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | {#await data.streamed.tracks} 18 | 19 | {:then value} 20 | 33 | {/await} 34 | -------------------------------------------------------------------------------- /src/lib/tools/cookies.tool.ts: -------------------------------------------------------------------------------- 1 | export class Cookies { 2 | static get(name: string) { 3 | let matches = document.cookie.match( 4 | new RegExp('(?:^|; )' + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + '=([^;]*)') 5 | ) 6 | return matches ? decodeURIComponent(matches[1]) : null 7 | } 8 | 9 | static set(name: string, value: string, options: any = null) { 10 | options = { 11 | path: '/' 12 | //expires: null 13 | } 14 | 15 | //if (options.expires instanceof Date) { 16 | // options.expires = options.expires.toUTCString(); 17 | //} 18 | let updatedCookie = encodeURIComponent(name) + '=' + encodeURIComponent(value) 19 | 20 | for (let optionKey in options) { 21 | updatedCookie += '; ' + optionKey 22 | let optionValue = options[optionKey] 23 | if (optionValue !== true) { 24 | updatedCookie += '=' + optionValue 25 | } 26 | } 27 | 28 | document.cookie = updatedCookie 29 | } 30 | 31 | static deleteCookie(name: string, options: any = null) { 32 | options = { 33 | path: '/' 34 | //expires: null 35 | } 36 | 37 | document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "album-saver-web", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite dev", 7 | "build": "vite build", 8 | "preview": "vite preview", 9 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 10 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 11 | "test-ct": "playwright test -c playwright-ct.config.ts" 12 | }, 13 | "devDependencies": { 14 | "@sveltejs/kit": "^2.0.0", 15 | "@sveltejs/vite-plugin-svelte": "^3.0.0", 16 | "@types/node": "^20.3.1", 17 | "autoprefixer": "^10.4.14", 18 | "postcss": "^8.4.24", 19 | "postcss-nesting": "^11.2.2", 20 | "svelte": "^4.0.0", 21 | "svelte-check": "^3.4.4", 22 | "svelte-preprocess": "^5.1.1", 23 | "tailwindcss": "^3.3.2", 24 | "ts-node": "^10.9.1", 25 | "tslib": "^2.5.3", 26 | "typescript": "^5.1.3", 27 | "vite": "^5.0.0" 28 | }, 29 | "type": "module", 30 | "dependencies": { 31 | "@sveltejs/adapter-static": "^3.0.0", 32 | "@types/telegram-web-app": "^6.9.3", 33 | "axios": "^1.1.2", 34 | "prettier": "^2.8.8", 35 | "svelte-infinite-scroll": "^2.0.1", 36 | "yarn": "^1.22.21" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/components/elements/AlbumCard.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 |
23 |
24 |

{album.title}

25 |

26 | {album.author === undefined ? "Various Artists" : album.author} 27 |

28 | {#if album.recordType && album.year} 29 |
30 |

31 | {album.recordType} 32 |

33 |

34 |

{album.year}

35 |
36 | {/if} 37 |
38 |
39 | -------------------------------------------------------------------------------- /src/routes/artist/+page.ts: -------------------------------------------------------------------------------- 1 | import { error } from "@sveltejs/kit"; 2 | import type { PageLoad } from "./$types"; 3 | import artistsController from "$lib/modules/artists/artists.controller"; 4 | import tokensController from "$lib/modules/tokens/tokens.controller"; 5 | import { TOKEN_NAMES } from "$lib/constants"; 6 | 7 | async function loadTracks(artistId: string) { 8 | let data = await artistsController.getTracks(artistId, 9) 9 | tokensController.clearTokens(TOKEN_NAMES) 10 | return data 11 | } 12 | 13 | async function loadAlbums(artistId: string) { 14 | let data = await artistsController.getAlbums(artistId, 6) 15 | tokensController.clearTokens(TOKEN_NAMES) 16 | return data 17 | } 18 | 19 | export const load = (async ({ url }) => { 20 | tokensController.clearTokens(TOKEN_NAMES) 21 | const artistId = url.searchParams.get("artistId"); 22 | const artistName = url.searchParams.get("artistName"); 23 | if (artistId && artistName) { 24 | return { 25 | artistId, 26 | artistName, 27 | streamed: { 28 | tracks: loadTracks(artistId), 29 | albums: loadAlbums(artistId), 30 | artistImage: artistsController.getImage(artistId) 31 | }, 32 | }; 33 | } 34 | 35 | error(404, "Not found"); 36 | }) satisfies PageLoad; 37 | -------------------------------------------------------------------------------- /src/components/searcher/FilterSelector.svelte: -------------------------------------------------------------------------------- 1 | 27 | 28 | {#if requestType !== RequestType.Release} 29 |
30 | {#each variants as variant} 31 | 39 | 46 | {/each} 47 |
48 | {/if} 49 | -------------------------------------------------------------------------------- /src/components/searcher/Searchbar.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 |
24 | 48 |
49 | -------------------------------------------------------------------------------- /src/components/Albums.svelte: -------------------------------------------------------------------------------- 1 | 36 | 37 | {#if showPlaceholder} 38 | 39 | {:else if data.length} 40 |
41 | {#each data as record} 42 | 46 | 47 | 48 | {/each} 49 | {#if loadingMore} 50 | 51 | {/if} 52 |
53 | 54 | {/if} 55 | -------------------------------------------------------------------------------- /src/routes/album/+page.svelte: -------------------------------------------------------------------------------- 1 | 26 | 27 |
28 | {#await data.streamed.album} 29 | 30 | {:then value} 31 | cover 32 |
33 |

34 | {value.albumTitle} 35 |

36 | 40 |

{value.artistName}

41 |
42 | {/await} 43 | {#await data.streamed.album} 44 | 45 | {:then value} 46 |
    47 | {#each value.result as record} 48 |
  • 49 |
    50 |

    {record.title}

    51 |
    52 |
  • 53 | {/each} 54 |
55 | {/await} 56 |
57 | -------------------------------------------------------------------------------- /src/lib/modules/musicsearch/searchrequest.service.ts: -------------------------------------------------------------------------------- 1 | import { tokens } from "$lib/stores"; 2 | import { get } from "svelte/store"; 3 | import type { Record } from "./interfaces/record.interface"; 4 | import type { 5 | MusicEntryRequest, 6 | MusicSetRequest, 7 | } from "./interfaces/musicentryrequest.interface"; 8 | import type { MusicQueryRequest } from "./interfaces/musicqueryrequest.interface"; 9 | 10 | class MusicGetterService { 11 | private setTokens(response: { 12 | continuationToken: string; 13 | token: string; 14 | }): void { 15 | const { continuationToken, token } = response; 16 | return tokens.set({ 17 | continuationToken: continuationToken, 18 | token: token, 19 | continuation: true, 20 | }); 21 | } 22 | 23 | async getMusicQuery( 24 | endpoint: string, 25 | requestQuery: MusicQueryRequest 26 | ): Promise { 27 | const tokensToSend = get(tokens); 28 | const response = await createRequest().get(endpoint, { 29 | params: { ...requestQuery, ...tokensToSend }, 30 | }); 31 | this.setTokens(response.data); 32 | return response.data.result; 33 | } 34 | 35 | async getData(url: string, query: T): Promise { 36 | const tokensToSend = get(tokens); 37 | const response = await createRequest().get(url, { params: { ...query, ...tokensToSend } }) 38 | this.setTokens(response.data); 39 | return response.data 40 | } 41 | 42 | async requestMusicEntry(requestQuery: MusicEntryRequest): Promise { 43 | await createRequest().post( 44 | "/download", 45 | {}, 46 | { 47 | params: { 48 | ...requestQuery, 49 | }, 50 | } 51 | ); 52 | } 53 | 54 | async requestMusicSet(requestQuery: MusicSetRequest): Promise { 55 | await createRequest().post("/download-set", requestQuery); 56 | } 57 | } 58 | 59 | export default new MusicGetterService(); 60 | -------------------------------------------------------------------------------- /src/lib/modules/httprequest/httprequest.service.ts: -------------------------------------------------------------------------------- 1 | import tokensController from "$lib/modules/tokens/tokens.controller"; 2 | import { TOKEN_NAMES } from "$lib/constants"; 3 | 4 | type Method = 'GET' | 'POST' 5 | type Data = { params?: Record, body?: Record } 6 | type Fetch = (url: string, data?: Data) => Promise; 7 | type CreateRequest = (url: string, method: Method, data?: Data) => Request 8 | type GetRequestUrl = (url: string, data?: Data) => string 9 | 10 | const BASE_URL = import.meta.env.VITE_API_BASE_URL 11 | 12 | const getRequestUrl: GetRequestUrl = (url, data) => { 13 | const tokens = tokensController.getTokens(TOKEN_NAMES) 14 | let urlParams = { ...data?.params } 15 | if (tokens) urlParams = { ...urlParams, ...tokens, continuation: 'true' } 16 | return url + '?' + new URLSearchParams(urlParams); 17 | } 18 | 19 | const createRequest: CreateRequest = (url, method, data) => { 20 | const requestUrl = getRequestUrl(url, data) 21 | return new Request(BASE_URL + requestUrl, { method, body: JSON.stringify(data?.body), headers: { "Content-Type": "application/json", "ngrok-skip-browser-warning" : "true" } }) 22 | } 23 | 24 | const fetchData = async (request: Request) => { 25 | const response = await fetch(request) 26 | if (response) { 27 | if (response.status === 204) return null 28 | else if (response.headers.get('content-length') === "0") return {} 29 | const data = await response.json() 30 | if (response.ok) { 31 | for (let token of TOKEN_NAMES) tokensController.tokens = { [token]: data[token] } 32 | return data 33 | } 34 | else throw new Error(data.reason) 35 | } else throw new Error('Something went wrong') 36 | } 37 | 38 | export const get: Fetch = async (url, data) => { 39 | const request = createRequest(url, 'GET', data) 40 | return await fetchData(request) 41 | } 42 | 43 | export const post: Fetch = async (url, data) => { 44 | const request = createRequest(url, 'POST', data) 45 | return await fetchData(request) 46 | } 47 | -------------------------------------------------------------------------------- /src/components/Tracks.svelte: -------------------------------------------------------------------------------- 1 | 43 | 44 | {#if showPlaceholder} 45 | 46 | {:else if data.length} 47 | 58 | 59 | {/if} 60 | -------------------------------------------------------------------------------- /src/lib/modules/musicsearch/searchrequest.controller.ts: -------------------------------------------------------------------------------- 1 | import type { Record } from "$lib/modules/musicsearch/interfaces/record.interface"; 2 | import { RequestType } from "$lib/modules/musicsearch/interfaces/musicqueryrequest.interface"; 3 | import type { 4 | MusicEntryRequest, 5 | MusicSetRequest, 6 | } from "$lib/modules/musicsearch/interfaces/musicentryrequest.interface"; 7 | import musicGetterService from "$lib/modules/musicsearch/searchrequest.service"; 8 | import platformEnvironmentService from "../platformenvironment/platformenvironment.service"; 9 | 10 | class SearchRequestController { 11 | getEntityType(requestType: RequestType): number { 12 | switch (requestType) { 13 | case RequestType.Track: 14 | case RequestType.ArtistTracks: 15 | return 2; 16 | case RequestType.Artist: 17 | return 3; 18 | case RequestType.Album: 19 | case RequestType.Release: 20 | default: 21 | return 1; 22 | } 23 | } 24 | 25 | async getRecords(query: string, requestType: RequestType): Promise { 26 | let queryObject = {}; 27 | 28 | switch (requestType) { 29 | case RequestType.ArtistTracks: { 30 | queryObject = { channelUrl: query }; 31 | break; 32 | } 33 | case RequestType.AlbumTracks: { 34 | queryObject = { albumUrl: query }; 35 | break; 36 | } 37 | default: { 38 | queryObject = { query: query }; 39 | } 40 | } 41 | return await musicGetterService.getMusicQuery(requestType, queryObject); 42 | } 43 | 44 | async requestRecord(url: string, requestType: RequestType): Promise { 45 | const environment = platformEnvironmentService.getEnvironment(); 46 | const data: MusicEntryRequest = { 47 | userId: environment.userId, 48 | youTubeMusicPlaylistUrl: url, 49 | entityType: this.getEntityType(requestType), 50 | }; 51 | debugger 52 | return await musicGetterService.requestMusicEntry(data); 53 | } 54 | 55 | async requestSet(urls: string[]): Promise { 56 | const environment = platformEnvironmentService.getEnvironment(); 57 | const data: MusicSetRequest = { userId: environment.userId, urls }; 58 | return await musicGetterService.requestMusicSet(data); 59 | } 60 | } 61 | 62 | export default new SearchRequestController(); 63 | -------------------------------------------------------------------------------- /src/lib/modules/platformenvironment/classes/TelegramEnvironment.class.ts: -------------------------------------------------------------------------------- 1 | import type { PlatformEnvironment } from "../interfaces/PlatformEnvironment.interface"; 2 | 3 | export class TelegramEnvironment implements PlatformEnvironment { 4 | readonly _webApp = Telegram.WebApp; 5 | readonly _backButton = Telegram.WebApp.BackButton; 6 | readonly _appData = Telegram.Utils.urlParseQueryString(this._webApp.initData); 7 | readonly webAppUser = JSON.parse(this._appData.user); 8 | readonly user = { 9 | id: this.webAppUser.id, 10 | username: this.webAppUser.username, 11 | }; 12 | constructor() { 13 | this.applyColorScheme(); 14 | } 15 | 16 | get userId(): number { 17 | if (this.user.id) { 18 | return this.user.id; 19 | } else { 20 | throw "No user id"; 21 | } 22 | } 23 | 24 | get platformName(): string { 25 | return this._appData.platform; 26 | } 27 | 28 | get colorScheme(): string { 29 | return this._webApp.colorScheme; 30 | } 31 | 32 | showBackButton(): void { 33 | this._backButton.show(); 34 | } 35 | 36 | hideBackButton(): void { 37 | this._backButton.hide(); 38 | } 39 | 40 | onBackButtonClick(callback: () => void): void { 41 | this._backButton.onClick(callback); 42 | } 43 | 44 | envokeHaptic(style: string): void { 45 | try { 46 | this._webApp.HapticFeedback.impactOccurred(style); 47 | } catch (e) { 48 | console.error(e); 49 | } 50 | } 51 | 52 | close(): void { 53 | this._webApp.close(); 54 | } 55 | 56 | showMainButton(text: string): void { 57 | this._webApp.MainButton.text = text; 58 | this._webApp.MainButton.show(); 59 | } 60 | 61 | setMainButtonText(text: string): void { 62 | this._webApp.MainButton.setText(text); 63 | } 64 | 65 | onMainButtonClick(callback: () => void): void { 66 | this._webApp.MainButton.onClick(callback); 67 | } 68 | offMainButtonClick(callback: () => void): void { 69 | this._webApp.MainButton.offClick(callback) 70 | } 71 | 72 | hideMainButton(): void { 73 | this._webApp.MainButton.hide(); 74 | } 75 | 76 | private applyColorScheme() { 77 | if (this._webApp.colorScheme === "dark") { 78 | document.documentElement.classList.add("dark"); 79 | } else { 80 | document.documentElement.classList.remove("dark"); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/routes/artist/+page.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 | {#await data.streamed.artistImage} 21 |
22 | {:then value} 23 |
24 |

{data.artistName}

25 |
26 | {/await} 27 |
28 | Top Tracks{@html ChevronRight} 32 | {#await data.streamed.tracks} 33 | 34 | {:then value} 35 | 48 | {/await} 49 |
50 |
51 | Albums{@html ChevronRight} 55 | {#await data.streamed.albums} 56 | 57 | {:then value} 58 | {#if value.length} 59 |
    60 | {#each value as album} 61 |
  • 62 | 63 |
  • 64 | {/each} 65 |
66 | {/if} 67 | {/await} 68 |
69 | -------------------------------------------------------------------------------- /src/components/Main.svelte: -------------------------------------------------------------------------------- 1 | 61 | 62 |
63 |
64 | handleSearch()} 66 | on:inputQueryChange={(event) => 67 | handleInputValueChange(event.detail.value)} 68 | on:inputFocuseChange={(event) => 69 | handleInputFocusChange(event.detail.value)} 70 | /> 71 |
72 | changeRequestType(event.detail.value)} 74 | {requestType} 75 | /> 76 |
77 | 78 | {#key keyObject} 79 |
80 | 85 |
86 | {/key} 87 | -------------------------------------------------------------------------------- /src/routes/+page.svelte: -------------------------------------------------------------------------------- 1 | 58 | 59 |
60 |
61 | (searchQuery = event.detail.value)} 63 | on:inputQueryChange={(event) => 64 | handleInputValueChange(event.detail.value)} 65 | on:inputFocuseChange={(event) => 66 | handleInputFocusChange(event.detail.value)} 67 | /> 68 |
69 | changeRequestType(event.detail.value)} 71 | {requestType} 72 | /> 73 |
74 | 75 | {#key keyObject} 76 |
77 | {#if keyObject.searchQuery !== ""} 78 | {#if keyObject.requestType === RequestType.Album} 79 | 80 | {:else if keyObject.requestType === RequestType.Track} 81 | 85 | {:else if keyObject.requestType === RequestType.Artist} 86 | 87 | {/if} 88 | {:else if keyObject.requestType === RequestType.Release} 89 | 90 | {/if} 91 |
92 | {/key} 93 | -------------------------------------------------------------------------------- /src/app.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --record-cover-side-lg: 10.5rem; 8 | --record-cover-side-md: 8rem; 9 | --record-cover-side-sm: 3.5rem; 10 | } 11 | 12 | input[type="search"]::-webkit-search-cancel-button { 13 | display: none; 14 | } 15 | 16 | body { 17 | @apply w-full h-full bg-[color:var(--tg-theme-bg-color)] text-[var(--tg-theme-text-color)]; 18 | } 19 | input:focus { 20 | @apply outline-none; 21 | } 22 | } 23 | 24 | @layer utilities { 25 | .shading { 26 | @apply inset-0 w-full h-full bg-black/50 transition-all fixed bottom-0; 27 | } 28 | } 29 | 30 | @layer components { 31 | .album-grid, 32 | .artist-grid { 33 | @apply grid grid-cols-2 p-4 gap-4; 34 | } 35 | 36 | .album-grid, 37 | .artist-grid { 38 | & a { 39 | @apply flex flex-col gap-2 cursor-pointer; 40 | } 41 | } 42 | 43 | .album-grid { 44 | & .cover { 45 | @apply aspect-square rounded-lg bg-cover; 46 | } 47 | } 48 | 49 | .artist-grid { 50 | & img { 51 | @apply w-full rounded-full; 52 | } 53 | & p { 54 | @apply truncate text-center; 55 | } 56 | } 57 | 58 | .track-list, 59 | .artist-list { 60 | @apply flex flex-col p-4 gap-4; 61 | } 62 | 63 | .noscroll { 64 | @apply overflow-hidden; 65 | } 66 | 67 | .track-list, 68 | .artist-list { 69 | & a { 70 | @apply flex flex-row gap-2 items-center; 71 | } 72 | } 73 | 74 | .track-list .cover { 75 | @apply rounded-sm w-[var(--record-cover-side-sm)] h-[var(--record-cover-side-sm)] bg-cover; 76 | } 77 | 78 | .artist-list .cover { 79 | @apply rounded-full w-[var(--record-cover-side-sm)] h-[var(--record-cover-side-sm)]; 80 | } 81 | 82 | .cover { 83 | background-position: center center; 84 | } 85 | 86 | .artist-header { 87 | @apply flex mb-4; 88 | min-height: 10rem; 89 | background-position: top; 90 | background-repeat: no-repeat; 91 | object-fit: fill; 92 | background-size: cover; 93 | & .name { 94 | @apply m-4 text-xl font-medium self-end; 95 | filter: invert(1); 96 | mix-blend-mode: difference; 97 | } 98 | } 99 | 100 | .artist { 101 | scrollbar-width: none; /* Firefox */ 102 | &.track-list { 103 | & .record { 104 | @apply flex items-center gap-2; 105 | } 106 | &.conveyer { 107 | @apply grid grid-flow-col grid-rows-3 overflow-auto; 108 | &::-webkit-scrollbar { 109 | display: none; 110 | } 111 | & .record { 112 | width: calc(100vw - 4rem); 113 | } 114 | } 115 | & .cover { 116 | background-size: 100px; 117 | } 118 | } 119 | } 120 | .album-grid.conveyer { 121 | @apply grid-flow-col grid-cols-none gap-3 p-4 overflow-auto; 122 | &::-webkit-scrollbar { 123 | display: none; 124 | } 125 | & .cover { 126 | @apply w-[var(--record-cover-side-md)] h-[var(--record-cover-side-md)] rounded-md object-cover; 127 | background-size: 128px; 128 | } 129 | & .record { 130 | @apply flex flex-col w-fit; 131 | } 132 | } 133 | 134 | .placeholder { 135 | @apply p-4 gap-4 animate-pulse; 136 | & .entry { 137 | @apply flex gap-2 w-full; 138 | } 139 | & .image { 140 | @apply bg-[color:var(--tg-theme-secondary-bg-color)] rounded; 141 | } 142 | & .text-line { 143 | @apply bg-[color:var(--tg-theme-secondary-bg-color)] h-2 rounded; 144 | } 145 | & .text-line-sm { 146 | @apply w-24; 147 | } 148 | & .text-line-md { 149 | @apply w-16; 150 | } 151 | & .text-container { 152 | @apply flex flex-col gap-2 justify-center; 153 | } 154 | 155 | &.album-grid { 156 | @apply grid grid-cols-2; 157 | & .entry { 158 | @apply flex-col h-48; 159 | } 160 | & .image { 161 | @apply w-full h-full; 162 | } 163 | &.conveyer { 164 | @apply grid-cols-none; 165 | & .entry { 166 | & .image { 167 | @apply h-32 w-32; 168 | } 169 | } 170 | } 171 | } 172 | &.track-list { 173 | @apply flex flex-col; 174 | & .image { 175 | @apply w-14 h-14; 176 | } 177 | & .entry { 178 | @apply flex-row; 179 | } 180 | } 181 | &.artist-list { 182 | @apply flex flex-col; 183 | & .image { 184 | @apply w-14 h-14; 185 | } 186 | & .entry { 187 | @apply flex-row; 188 | } 189 | } 190 | &.artist-list { 191 | & .image { 192 | @apply !rounded-full; 193 | } 194 | & .entry .text-container p:nth-child(2) { 195 | @apply hidden; 196 | } 197 | } 198 | &.track-list.album-entry { 199 | & .image { 200 | @apply hidden; 201 | } 202 | & .text-line-sm { 203 | @apply hidden; 204 | } 205 | & .entry { 206 | @apply p-2; 207 | & .text-container { 208 | @apply w-full; 209 | } 210 | & p { 211 | @apply w-full h-5; 212 | } 213 | } 214 | } 215 | } 216 | 217 | .btn { 218 | &.btn--icon { 219 | @apply flex items-center text-sm font-medium; 220 | & svg { 221 | @apply w-4 h-4; 222 | } 223 | } 224 | } 225 | 226 | .album-entry { 227 | @apply flex flex-col; 228 | & .headings { 229 | @apply m-4 grid text-center; 230 | & .title { 231 | @apply text-lg font-medium; 232 | } 233 | } 234 | & .artwork { 235 | @apply w-2/3 self-center rounded-lg shadow-lg mt-3 aspect-square object-cover; 236 | } 237 | & .track-list { 238 | @apply mt-0; 239 | counter-reset: track; 240 | & li { 241 | @apply flex justify-between items-center p-2; 242 | &:nth-child(2n + 2) { 243 | @apply bg-[color:var(--tg-theme-secondary-bg-color)] rounded-md; 244 | } 245 | & .track { 246 | @apply h-5 flex items-center text-sm gap-3 truncate w-full; 247 | &:before { 248 | @apply text-center h-full; 249 | counter-increment: track; 250 | content: counter(track); 251 | } 252 | } 253 | } 254 | } 255 | } 256 | 257 | .popup { 258 | @apply h-full bg-[color:var(--tg-theme-bg-color)] shadow-2xl rounded-t-lg w-full fixed bottom-0 overflow-auto z-10; 259 | } 260 | 261 | .form { 262 | @apply w-full p-4 flex gap-4 justify-between h-16; 263 | } 264 | 265 | .content-in-center { 266 | @apply flex justify-center items-center h-screen; 267 | } 268 | 269 | .filter { 270 | @apply px-4 flex gap-3 my-2; 271 | } 272 | 273 | .filter-button { 274 | @apply rounded-full bg-[color:var(--tg-theme-secondary-bg-color)] text-[color:var(--tg-theme-text-color)] px-2 py-0.5 text-sm font-medium capitalize border-2 border-transparent; 275 | } 276 | 277 | .filter-button.active { 278 | @apply border-blue-700; 279 | } 280 | 281 | .searchbar { 282 | @apply bg-[color:var(--tg-theme-secondary-bg-color)] h-full text-gray-900 text-[16px] rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full px-2.5 dark:border-gray-600 placeholder-[color:var(--tg-theme-hint-color)] dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500; 283 | } 284 | 285 | .top-bar { 286 | @apply flex flex-col z-10 fixed inset-0 bg-[color:var(--tg-theme-bg-color)] bg-opacity-90 backdrop-blur-xl justify-start h-fit; 287 | } 288 | 289 | .record__title-container { 290 | @apply text-[12px] w-fit; 291 | } 292 | .record__name { 293 | @apply font-semibold leading-tight line-clamp-2; 294 | } 295 | .record__artist { 296 | @apply dark:text-stone-400; 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /static/telegram-web-app.js: -------------------------------------------------------------------------------- 1 | // WebView 2 | (function () { 3 | var eventHandlers = {}; 4 | 5 | var locationHash = ''; 6 | try { 7 | locationHash = location.hash.toString(); 8 | } catch (e) { } 9 | 10 | var initParams = urlParseHashParams(locationHash); 11 | var storedParams = sessionStorageGet('initParams'); 12 | if (storedParams) { 13 | for (var key in storedParams) { 14 | if (typeof initParams[key] === 'undefined') { 15 | initParams[key] = storedParams[key]; 16 | } 17 | } 18 | } 19 | sessionStorageSet('initParams', initParams); 20 | 21 | var isIframe = false, iFrameStyle; 22 | try { 23 | isIframe = (window.parent != null && window != window.parent); 24 | if (isIframe) { 25 | window.addEventListener('message', function (event) { 26 | if (event.source !== window.parent) return; 27 | try { 28 | var dataParsed = JSON.parse(event.data); 29 | } catch (e) { 30 | return; 31 | } 32 | if (!dataParsed || !dataParsed.eventType) { 33 | return; 34 | } 35 | if (dataParsed.eventType == 'set_custom_style') { 36 | if (event.origin === 'https://web.telegram.org') { 37 | iFrameStyle.innerHTML = dataParsed.eventData; 38 | } 39 | } else if (dataParsed.eventType == 'reload_iframe') { 40 | try { 41 | window.parent.postMessage(JSON.stringify({ eventType: 'iframe_will_reload' }), '*'); 42 | } catch (e) { } 43 | location.reload(); 44 | } else { 45 | receiveEvent(dataParsed.eventType, dataParsed.eventData); 46 | } 47 | }); 48 | iFrameStyle = document.createElement('style'); 49 | document.head.appendChild(iFrameStyle); 50 | try { 51 | window.parent.postMessage(JSON.stringify({ eventType: 'iframe_ready', eventData: { reload_supported: true } }), '*'); 52 | } catch (e) { } 53 | } 54 | } catch (e) { } 55 | 56 | function urlSafeDecode(urlencoded) { 57 | try { 58 | urlencoded = urlencoded.replace(/\+/g, '%20'); 59 | return decodeURIComponent(urlencoded); 60 | } catch (e) { 61 | return urlencoded; 62 | } 63 | } 64 | 65 | function urlParseHashParams(locationHash) { 66 | locationHash = locationHash.replace(/^#/, ''); 67 | var params = {}; 68 | if (!locationHash.length) { 69 | return params; 70 | } 71 | if (locationHash.indexOf('=') < 0 && locationHash.indexOf('?') < 0) { 72 | params._path = urlSafeDecode(locationHash); 73 | return params; 74 | } 75 | var qIndex = locationHash.indexOf('?'); 76 | if (qIndex >= 0) { 77 | var pathParam = locationHash.substr(0, qIndex); 78 | params._path = urlSafeDecode(pathParam); 79 | locationHash = locationHash.substr(qIndex + 1); 80 | } 81 | var query_params = urlParseQueryString(locationHash); 82 | for (var k in query_params) { 83 | params[k] = query_params[k]; 84 | } 85 | return params; 86 | } 87 | 88 | function urlParseQueryString(queryString) { 89 | var params = {}; 90 | if (!queryString.length) { 91 | return params; 92 | } 93 | var queryStringParams = queryString.split('&'); 94 | var i, param, paramName, paramValue; 95 | for (i = 0; i < queryStringParams.length; i++) { 96 | param = queryStringParams[i].split('='); 97 | paramName = urlSafeDecode(param[0]); 98 | paramValue = param[1] == null ? null : urlSafeDecode(param[1]); 99 | params[paramName] = paramValue; 100 | } 101 | return params; 102 | } 103 | 104 | // Telegram apps will implement this logic to add service params (e.g. tgShareScoreUrl) to game URL 105 | function urlAppendHashParams(url, addHash) { 106 | // url looks like 'https://game.com/path?query=1#hash' 107 | // addHash looks like 'tgShareScoreUrl=' + encodeURIComponent('tgb://share_game_score?hash=very_long_hash123') 108 | 109 | var ind = url.indexOf('#'); 110 | if (ind < 0) { 111 | // https://game.com/path -> https://game.com/path#tgShareScoreUrl=etc 112 | return url + '#' + addHash; 113 | } 114 | var curHash = url.substr(ind + 1); 115 | if (curHash.indexOf('=') >= 0 || curHash.indexOf('?') >= 0) { 116 | // https://game.com/#hash=1 -> https://game.com/#hash=1&tgShareScoreUrl=etc 117 | // https://game.com/#path?query -> https://game.com/#path?query&tgShareScoreUrl=etc 118 | return url + '&' + addHash; 119 | } 120 | // https://game.com/#hash -> https://game.com/#hash?tgShareScoreUrl=etc 121 | if (curHash.length > 0) { 122 | return url + '?' + addHash; 123 | } 124 | // https://game.com/# -> https://game.com/#tgShareScoreUrl=etc 125 | return url + addHash; 126 | } 127 | 128 | function postEvent(eventType, callback, eventData) { 129 | if (!callback) { 130 | callback = function () { }; 131 | } 132 | if (eventData === undefined) { 133 | eventData = ''; 134 | } 135 | console.log('[Telegram.WebView] > postEvent', eventType, eventData); 136 | 137 | if (window.TelegramWebviewProxy !== undefined) { 138 | TelegramWebviewProxy.postEvent(eventType, JSON.stringify(eventData)); 139 | callback(); 140 | } 141 | else if (window.external && 'notify' in window.external) { 142 | window.external.notify(JSON.stringify({ eventType: eventType, eventData: eventData })); 143 | callback(); 144 | } 145 | else if (isIframe) { 146 | try { 147 | var trustedTarget = 'https://web.telegram.org'; 148 | // For now we don't restrict target, for testing purposes 149 | trustedTarget = '*'; 150 | window.parent.postMessage(JSON.stringify({ eventType: eventType, eventData: eventData }), trustedTarget); 151 | callback(); 152 | } catch (e) { 153 | callback(e); 154 | } 155 | } 156 | else { 157 | callback({ notAvailable: true }); 158 | } 159 | }; 160 | 161 | function receiveEvent(eventType, eventData) { 162 | console.log('[Telegram.WebView] < receiveEvent', eventType, eventData); 163 | callEventCallbacks(eventType, function (callback) { 164 | callback(eventType, eventData); 165 | }); 166 | } 167 | 168 | function callEventCallbacks(eventType, func) { 169 | var curEventHandlers = eventHandlers[eventType]; 170 | if (curEventHandlers === undefined || 171 | !curEventHandlers.length) { 172 | return; 173 | } 174 | for (var i = 0; i < curEventHandlers.length; i++) { 175 | try { 176 | func(curEventHandlers[i]); 177 | } catch (e) { } 178 | } 179 | } 180 | 181 | function onEvent(eventType, callback) { 182 | if (eventHandlers[eventType] === undefined) { 183 | eventHandlers[eventType] = []; 184 | } 185 | var index = eventHandlers[eventType].indexOf(callback); 186 | if (index === -1) { 187 | eventHandlers[eventType] = [callback]; // this was changed cause we need just a single callback for the main button 188 | } 189 | }; 190 | 191 | function offEvent(eventType, callback) { 192 | if (eventHandlers[eventType] === undefined) { 193 | return; 194 | } 195 | var index = eventHandlers[eventType].indexOf(callback); 196 | if (index === -1) { 197 | return; 198 | } 199 | eventHandlers[eventType].splice(index, 1); 200 | }; 201 | 202 | function openProtoUrl(url) { 203 | if (!url.match(/^(web\+)?tgb?:\/\/./)) { 204 | return false; 205 | } 206 | var useIframe = navigator.userAgent.match(/iOS|iPhone OS|iPhone|iPod|iPad/i) ? true : false; 207 | if (useIframe) { 208 | var iframeContEl = document.getElementById('tgme_frame_cont') || document.body; 209 | var iframeEl = document.createElement('iframe'); 210 | iframeContEl.appendChild(iframeEl); 211 | var pageHidden = false; 212 | var enableHidden = function () { 213 | pageHidden = true; 214 | }; 215 | window.addEventListener('pagehide', enableHidden, false); 216 | window.addEventListener('blur', enableHidden, false); 217 | if (iframeEl !== null) { 218 | iframeEl.src = url; 219 | } 220 | setTimeout(function () { 221 | if (!pageHidden) { 222 | window.location = url; 223 | } 224 | window.removeEventListener('pagehide', enableHidden, false); 225 | window.removeEventListener('blur', enableHidden, false); 226 | }, 2000); 227 | } 228 | else { 229 | window.location = url; 230 | } 231 | return true; 232 | } 233 | 234 | function sessionStorageSet(key, value) { 235 | try { 236 | window.sessionStorage.setItem('__telegram__' + key, JSON.stringify(value)); 237 | return true; 238 | } catch (e) { } 239 | return false; 240 | } 241 | function sessionStorageGet(key) { 242 | try { 243 | return JSON.parse(window.sessionStorage.getItem('__telegram__' + key)); 244 | } catch (e) { } 245 | return null; 246 | } 247 | 248 | if (!window.Telegram) { 249 | window.Telegram = {}; 250 | } 251 | window.Telegram.WebView = { 252 | initParams: initParams, 253 | isIframe: isIframe, 254 | onEvent: onEvent, 255 | offEvent: offEvent, 256 | postEvent: postEvent, 257 | receiveEvent: receiveEvent, 258 | callEventCallbacks: callEventCallbacks 259 | }; 260 | 261 | window.Telegram.Utils = { 262 | urlSafeDecode: urlSafeDecode, 263 | urlParseQueryString: urlParseQueryString, 264 | urlParseHashParams: urlParseHashParams, 265 | urlAppendHashParams: urlAppendHashParams, 266 | sessionStorageSet: sessionStorageSet, 267 | sessionStorageGet: sessionStorageGet 268 | }; 269 | 270 | // For Windows Phone app 271 | window.TelegramGameProxy_receiveEvent = receiveEvent; 272 | 273 | // App backward compatibility 274 | window.TelegramGameProxy = { 275 | receiveEvent: receiveEvent 276 | }; 277 | })(); 278 | 279 | // WebApp 280 | (function () { 281 | var Utils = window.Telegram.Utils; 282 | var WebView = window.Telegram.WebView; 283 | var initParams = WebView.initParams; 284 | var isIframe = WebView.isIframe; 285 | 286 | var WebApp = {}; 287 | var webAppInitData = '', webAppInitDataUnsafe = {}; 288 | var themeParams = {}, colorScheme = 'light'; 289 | var webAppVersion = '6.0'; 290 | var webAppPlatform = 'unknown'; 291 | 292 | if (initParams.tgWebAppData && initParams.tgWebAppData.length) { 293 | webAppInitData = initParams.tgWebAppData; 294 | webAppInitDataUnsafe = Utils.urlParseQueryString(webAppInitData); 295 | for (var key in webAppInitDataUnsafe) { 296 | var val = webAppInitDataUnsafe[key]; 297 | try { 298 | if (val.substr(0, 1) == '{' && val.substr(-1) == '}' || 299 | val.substr(0, 1) == '[' && val.substr(-1) == ']') { 300 | webAppInitDataUnsafe[key] = JSON.parse(val); 301 | } 302 | } catch (e) { } 303 | } 304 | } 305 | if (initParams.tgWebAppThemeParams && initParams.tgWebAppThemeParams.length) { 306 | var themeParamsRaw = initParams.tgWebAppThemeParams; 307 | try { 308 | var theme_params = JSON.parse(themeParamsRaw); 309 | if (theme_params) { 310 | setThemeParams(theme_params); 311 | } 312 | } catch (e) { } 313 | } 314 | var theme_params = Utils.sessionStorageGet('themeParams'); 315 | if (theme_params) { 316 | setThemeParams(theme_params); 317 | } 318 | if (initParams.tgWebAppVersion) { 319 | webAppVersion = initParams.tgWebAppVersion; 320 | } 321 | if (initParams.tgWebAppPlatform) { 322 | webAppPlatform = initParams.tgWebAppPlatform; 323 | } 324 | 325 | function onThemeChanged(eventType, eventData) { 326 | if (eventData.theme_params) { 327 | setThemeParams(eventData.theme_params); 328 | window.Telegram.WebApp.MainButton.setParams({}); 329 | updateBackgroundColor(); 330 | receiveWebViewEvent('themeChanged'); 331 | } 332 | } 333 | 334 | var lastWindowHeight = window.innerHeight; 335 | function onViewportChanged(eventType, eventData) { 336 | if (eventData.height) { 337 | window.removeEventListener('resize', onWindowResize); 338 | setViewportHeight(eventData); 339 | } 340 | } 341 | 342 | function onWindowResize(e) { 343 | if (lastWindowHeight != window.innerHeight) { 344 | lastWindowHeight = window.innerHeight; 345 | receiveWebViewEvent('viewportChanged', { 346 | isStateStable: true 347 | }); 348 | } 349 | } 350 | 351 | function linkHandler(e) { 352 | if (e.metaKey || e.ctrlKey) return; 353 | var el = e.target; 354 | while (el.tagName != 'A' && el.parentNode) { 355 | el = el.parentNode; 356 | } 357 | if (el.tagName == 'A' && 358 | el.target != '_blank' && 359 | (el.protocol == 'http:' || el.protocol == 'https:') && 360 | el.hostname == 't.me') { 361 | WebApp.openTgLink(el.href); 362 | e.preventDefault(); 363 | } 364 | } 365 | 366 | function strTrim(str) { 367 | return str.toString().replace(/^\s+|\s+$/g, ''); 368 | } 369 | 370 | function receiveWebViewEvent(eventType) { 371 | var args = Array.prototype.slice.call(arguments); 372 | eventType = args.shift(); 373 | WebView.callEventCallbacks('webview:' + eventType, function (callback) { 374 | callback.apply(WebApp, args); 375 | }); 376 | } 377 | 378 | function onWebViewEvent(eventType, callback) { 379 | WebView.onEvent('webview:' + eventType, callback); 380 | }; 381 | 382 | function offWebViewEvent(eventType, callback) { 383 | WebView.offEvent('webview:' + eventType, callback); 384 | }; 385 | 386 | function setCssProperty(name, value) { 387 | var root = document.documentElement; 388 | if (root && root.style && root.style.setProperty) { 389 | root.style.setProperty('--tg-' + name, value); 390 | } 391 | } 392 | 393 | function setThemeParams(theme_params) { 394 | // temp iOS fix 395 | if (theme_params.bg_color == '#1c1c1d' && 396 | theme_params.bg_color == theme_params.secondary_bg_color) { 397 | theme_params.secondary_bg_color = '#2c2c2e'; 398 | } 399 | var color; 400 | for (var key in theme_params) { 401 | if (color = parseColorToHex(theme_params[key])) { 402 | themeParams[key] = color; 403 | if (key == 'bg_color') { 404 | colorScheme = isColorDark(color) ? 'dark' : 'light' 405 | setCssProperty('color-scheme', colorScheme); 406 | } 407 | key = 'theme-' + key.split('_').join('-'); 408 | setCssProperty(key, color); 409 | } 410 | } 411 | Utils.sessionStorageSet('themeParams', themeParams); 412 | } 413 | 414 | var webAppCallbacks = {}; 415 | function generateCallbackId(len) { 416 | var tries = 100; 417 | while (--tries) { 418 | var id = '', chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', chars_len = chars.length; 419 | for (var i = 0; i < len; i++) { 420 | id += chars[Math.floor(Math.random() * chars_len)]; 421 | } 422 | if (!webAppCallbacks[id]) { 423 | webAppCallbacks[id] = {}; 424 | return id; 425 | } 426 | } 427 | throw Error('WebAppCallbackIdGenerateFailed'); 428 | } 429 | 430 | var viewportHeight = false, viewportStableHeight = false, isExpanded = true; 431 | function setViewportHeight(data) { 432 | if (typeof data !== 'undefined') { 433 | isExpanded = !!data.is_expanded; 434 | viewportHeight = data.height; 435 | if (data.is_state_stable) { 436 | viewportStableHeight = data.height; 437 | } 438 | receiveWebViewEvent('viewportChanged', { 439 | isStateStable: !!data.is_state_stable 440 | }); 441 | } 442 | var height, stable_height; 443 | if (viewportHeight !== false) { 444 | height = (viewportHeight - mainButtonHeight) + 'px'; 445 | } else { 446 | height = mainButtonHeight ? 'calc(100vh - ' + mainButtonHeight + 'px)' : '100vh'; 447 | } 448 | if (viewportStableHeight !== false) { 449 | stable_height = (viewportStableHeight - mainButtonHeight) + 'px'; 450 | } else { 451 | stable_height = mainButtonHeight ? 'calc(100vh - ' + mainButtonHeight + 'px)' : '100vh'; 452 | } 453 | setCssProperty('viewport-height', height); 454 | setCssProperty('viewport-stable-height', stable_height); 455 | } 456 | 457 | var isClosingConfirmationEnabled = false; 458 | function setClosingConfirmation(need_confirmation) { 459 | if (!versionAtLeast('6.2')) { 460 | console.warn('[Telegram.WebApp] Closing confirmation is not supported in version ' + webAppVersion); 461 | return; 462 | } 463 | isClosingConfirmationEnabled = !!need_confirmation; 464 | WebView.postEvent('web_app_setup_closing_behavior', false, { need_confirmation: isClosingConfirmationEnabled }); 465 | } 466 | 467 | var headerColorKey = 'bg_color', headerColor = null; 468 | function getHeaderColor() { 469 | if (headerColorKey == 'secondary_bg_color') { 470 | return themeParams.secondary_bg_color; 471 | } else if (headerColorKey == 'bg_color') { 472 | return themeParams.bg_color; 473 | } 474 | return headerColor; 475 | } 476 | function setHeaderColor(color) { 477 | if (!versionAtLeast('6.1')) { 478 | console.warn('[Telegram.WebApp] Header color is not supported in version ' + webAppVersion); 479 | return; 480 | } 481 | if (!versionAtLeast('6.9')) { 482 | if (themeParams.bg_color && 483 | themeParams.bg_color == color) { 484 | color = 'bg_color'; 485 | } else if (themeParams.secondary_bg_color && 486 | themeParams.secondary_bg_color == color) { 487 | color = 'secondary_bg_color'; 488 | } 489 | } 490 | var head_color = null, color_key = null; 491 | if (color == 'bg_color' || color == 'secondary_bg_color') { 492 | color_key = color; 493 | } else if (versionAtLeast('6.9')) { 494 | head_color = parseColorToHex(color); 495 | if (!head_color) { 496 | console.error('[Telegram.WebApp] Header color format is invalid', color); 497 | throw Error('WebAppHeaderColorInvalid'); 498 | } 499 | } 500 | if (!versionAtLeast('6.9') && 501 | color_key != 'bg_color' && 502 | color_key != 'secondary_bg_color') { 503 | console.error('[Telegram.WebApp] Header color key should be one of Telegram.WebApp.themeParams.bg_color, Telegram.WebApp.themeParams.secondary_bg_color, \'bg_color\', \'secondary_bg_color\'', color); 504 | throw Error('WebAppHeaderColorKeyInvalid'); 505 | } 506 | headerColorKey = color_key; 507 | headerColor = head_color; 508 | updateHeaderColor(); 509 | } 510 | var appHeaderColorKey = null, appHeaderColor = null; 511 | function updateHeaderColor() { 512 | if (appHeaderColorKey != headerColorKey || 513 | appHeaderColor != headerColor) { 514 | appHeaderColorKey = headerColorKey; 515 | appHeaderColor = headerColor; 516 | if (appHeaderColor) { 517 | WebView.postEvent('web_app_set_header_color', false, { color: headerColor }); 518 | } else { 519 | WebView.postEvent('web_app_set_header_color', false, { color_key: headerColorKey }); 520 | } 521 | } 522 | } 523 | 524 | var backgroundColor = 'bg_color'; 525 | function getBackgroundColor() { 526 | if (backgroundColor == 'secondary_bg_color') { 527 | return themeParams.secondary_bg_color; 528 | } else if (backgroundColor == 'bg_color') { 529 | return themeParams.bg_color; 530 | } 531 | return backgroundColor; 532 | } 533 | function setBackgroundColor(color) { 534 | if (!versionAtLeast('6.1')) { 535 | console.warn('[Telegram.WebApp] Background color is not supported in version ' + webAppVersion); 536 | return; 537 | } 538 | var bg_color; 539 | if (color == 'bg_color' || color == 'secondary_bg_color') { 540 | bg_color = color; 541 | } else { 542 | bg_color = parseColorToHex(color); 543 | if (!bg_color) { 544 | console.error('[Telegram.WebApp] Background color format is invalid', color); 545 | throw Error('WebAppBackgroundColorInvalid'); 546 | } 547 | } 548 | backgroundColor = bg_color; 549 | updateBackgroundColor(); 550 | } 551 | var appBackgroundColor = null; 552 | function updateBackgroundColor() { 553 | var color = getBackgroundColor(); 554 | if (appBackgroundColor != color) { 555 | appBackgroundColor = color; 556 | WebView.postEvent('web_app_set_background_color', false, { color: color }); 557 | } 558 | } 559 | 560 | 561 | function parseColorToHex(color) { 562 | color += ''; 563 | var match; 564 | if (match = /^\s*#([0-9a-f]{6})\s*$/i.exec(color)) { 565 | return '#' + match[1].toLowerCase(); 566 | } 567 | else if (match = /^\s*#([0-9a-f])([0-9a-f])([0-9a-f])\s*$/i.exec(color)) { 568 | return ('#' + match[1] + match[1] + match[2] + match[2] + match[3] + match[3]).toLowerCase(); 569 | } 570 | else if (match = /^\s*rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+\.{0,1}\d*))?\)\s*$/.exec(color)) { 571 | var r = parseInt(match[1]), g = parseInt(match[2]), b = parseInt(match[3]); 572 | r = (r < 16 ? '0' : '') + r.toString(16); 573 | g = (g < 16 ? '0' : '') + g.toString(16); 574 | b = (b < 16 ? '0' : '') + b.toString(16); 575 | return '#' + r + g + b; 576 | } 577 | return false; 578 | } 579 | 580 | function isColorDark(rgb) { 581 | rgb = rgb.replace(/[\s#]/g, ''); 582 | if (rgb.length == 3) { 583 | rgb = rgb[0] + rgb[0] + rgb[1] + rgb[1] + rgb[2] + rgb[2]; 584 | } 585 | var r = parseInt(rgb.substr(0, 2), 16); 586 | var g = parseInt(rgb.substr(2, 2), 16); 587 | var b = parseInt(rgb.substr(4, 2), 16); 588 | var hsp = Math.sqrt(0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b)); 589 | return hsp < 120; 590 | } 591 | 592 | function versionCompare(v1, v2) { 593 | if (typeof v1 !== 'string') v1 = ''; 594 | if (typeof v2 !== 'string') v2 = ''; 595 | v1 = v1.replace(/^\s+|\s+$/g, '').split('.'); 596 | v2 = v2.replace(/^\s+|\s+$/g, '').split('.'); 597 | var a = Math.max(v1.length, v2.length), i, p1, p2; 598 | for (i = 0; i < a; i++) { 599 | p1 = parseInt(v1[i]) || 0; 600 | p2 = parseInt(v2[i]) || 0; 601 | if (p1 == p2) continue; 602 | if (p1 > p2) return 1; 603 | return -1; 604 | } 605 | return 0; 606 | } 607 | 608 | function versionAtLeast(ver) { 609 | return versionCompare(webAppVersion, ver) >= 0; 610 | } 611 | 612 | function byteLength(str) { 613 | if (window.Blob) { 614 | try { return new Blob([str]).size; } catch (e) { } 615 | } 616 | var s = str.length; 617 | for (var i = str.length - 1; i >= 0; i--) { 618 | var code = str.charCodeAt(i); 619 | if (code > 0x7f && code <= 0x7ff) s++; 620 | else if (code > 0x7ff && code <= 0xffff) s += 2; 621 | if (code >= 0xdc00 && code <= 0xdfff) i--; 622 | } 623 | return s; 624 | } 625 | 626 | var BackButton = (function () { 627 | var isVisible = false; 628 | 629 | var backButton = {}; 630 | Object.defineProperty(backButton, 'isVisible', { 631 | set: function (val) { setParams({ is_visible: val }); }, 632 | get: function () { return isVisible; }, 633 | enumerable: true 634 | }); 635 | 636 | var curButtonState = null; 637 | 638 | WebView.onEvent('back_button_pressed', onBackButtonPressed); 639 | 640 | function onBackButtonPressed() { 641 | receiveWebViewEvent('backButtonClicked'); 642 | } 643 | 644 | function buttonParams() { 645 | return { is_visible: isVisible }; 646 | } 647 | 648 | function buttonState(btn_params) { 649 | if (typeof btn_params === 'undefined') { 650 | btn_params = buttonParams(); 651 | } 652 | return JSON.stringify(btn_params); 653 | } 654 | 655 | function buttonCheckVersion() { 656 | if (!versionAtLeast('6.1')) { 657 | console.warn('[Telegram.WebApp] BackButton is not supported in version ' + webAppVersion); 658 | return false; 659 | } 660 | return true; 661 | } 662 | 663 | function updateButton() { 664 | var btn_params = buttonParams(); 665 | var btn_state = buttonState(btn_params); 666 | if (curButtonState === btn_state) { 667 | return; 668 | } 669 | curButtonState = btn_state; 670 | WebView.postEvent('web_app_setup_back_button', false, btn_params); 671 | } 672 | 673 | function setParams(params) { 674 | if (!buttonCheckVersion()) { 675 | return backButton; 676 | } 677 | if (typeof params.is_visible !== 'undefined') { 678 | isVisible = !!params.is_visible; 679 | } 680 | updateButton(); 681 | return backButton; 682 | } 683 | 684 | backButton.onClick = function (callback) { 685 | if (buttonCheckVersion()) { 686 | onWebViewEvent('backButtonClicked', callback); 687 | } 688 | return backButton; 689 | }; 690 | backButton.offClick = function (callback) { 691 | if (buttonCheckVersion()) { 692 | offWebViewEvent('backButtonClicked', callback); 693 | } 694 | return backButton; 695 | }; 696 | backButton.show = function () { 697 | return setParams({ is_visible: true }); 698 | }; 699 | backButton.hide = function () { 700 | return setParams({ is_visible: false }); 701 | }; 702 | return backButton; 703 | })(); 704 | 705 | var mainButtonHeight = 0; 706 | var MainButton = (function () { 707 | var isVisible = false; 708 | var isActive = true; 709 | var isProgressVisible = false; 710 | var buttonText = 'CONTINUE'; 711 | var buttonColor = false; 712 | var buttonTextColor = false; 713 | 714 | var mainButton = {}; 715 | Object.defineProperty(mainButton, 'text', { 716 | set: function (val) { mainButton.setParams({ text: val }); }, 717 | get: function () { return buttonText; }, 718 | enumerable: true 719 | }); 720 | Object.defineProperty(mainButton, 'color', { 721 | set: function (val) { mainButton.setParams({ color: val }); }, 722 | get: function () { return buttonColor || themeParams.button_color || '#2481cc'; }, 723 | enumerable: true 724 | }); 725 | Object.defineProperty(mainButton, 'textColor', { 726 | set: function (val) { mainButton.setParams({ text_color: val }); }, 727 | get: function () { return buttonTextColor || themeParams.button_text_color || '#ffffff'; }, 728 | enumerable: true 729 | }); 730 | Object.defineProperty(mainButton, 'isVisible', { 731 | set: function (val) { mainButton.setParams({ is_visible: val }); }, 732 | get: function () { return isVisible; }, 733 | enumerable: true 734 | }); 735 | Object.defineProperty(mainButton, 'isProgressVisible', { 736 | get: function () { return isProgressVisible; }, 737 | enumerable: true 738 | }); 739 | Object.defineProperty(mainButton, 'isActive', { 740 | set: function (val) { mainButton.setParams({ is_active: val }); }, 741 | get: function () { return isActive; }, 742 | enumerable: true 743 | }); 744 | 745 | var curButtonState = null; 746 | 747 | WebView.onEvent('main_button_pressed', onMainButtonPressed); 748 | 749 | var debugBtn = null, debugBtnStyle = {}; 750 | if (initParams.tgWebAppDebug) { 751 | debugBtn = document.createElement('tg-main-button'); 752 | debugBtnStyle = { 753 | font: '600 14px/18px sans-serif', 754 | display: 'none', 755 | width: '100%', 756 | height: '48px', 757 | borderRadius: '0', 758 | background: 'no-repeat right center', 759 | position: 'fixed', 760 | left: '0', 761 | right: '0', 762 | bottom: '0', 763 | margin: '0', 764 | padding: '15px 20px', 765 | textAlign: 'center', 766 | boxSizing: 'border-box', 767 | zIndex: '10000' 768 | }; 769 | for (var k in debugBtnStyle) { 770 | debugBtn.style[k] = debugBtnStyle[k]; 771 | } 772 | document.addEventListener('DOMContentLoaded', function onDomLoaded(event) { 773 | document.removeEventListener('DOMContentLoaded', onDomLoaded); 774 | document.body.appendChild(debugBtn); 775 | debugBtn.addEventListener('click', onMainButtonPressed, false); 776 | }); 777 | } 778 | 779 | function onMainButtonPressed() { 780 | if (isActive) { 781 | receiveWebViewEvent('mainButtonClicked'); 782 | } 783 | } 784 | 785 | function buttonParams() { 786 | var color = mainButton.color; 787 | var text_color = mainButton.textColor; 788 | return isVisible ? { 789 | is_visible: true, 790 | is_active: isActive, 791 | is_progress_visible: isProgressVisible, 792 | text: buttonText, 793 | color: color, 794 | text_color: text_color 795 | } : { is_visible: false }; 796 | } 797 | 798 | function buttonState(btn_params) { 799 | if (typeof btn_params === 'undefined') { 800 | btn_params = buttonParams(); 801 | } 802 | return JSON.stringify(btn_params); 803 | } 804 | 805 | function updateButton() { 806 | var btn_params = buttonParams(); 807 | var btn_state = buttonState(btn_params); 808 | if (curButtonState === btn_state) { 809 | return; 810 | } 811 | curButtonState = btn_state; 812 | WebView.postEvent('web_app_setup_main_button', false, btn_params); 813 | if (initParams.tgWebAppDebug) { 814 | updateDebugButton(btn_params); 815 | } 816 | } 817 | 818 | function updateDebugButton(btn_params) { 819 | if (btn_params.is_visible) { 820 | debugBtn.style.display = 'block'; 821 | mainButtonHeight = 48; 822 | 823 | debugBtn.style.opacity = btn_params.is_active ? '1' : '0.8'; 824 | debugBtn.style.cursor = btn_params.is_active ? 'pointer' : 'auto'; 825 | debugBtn.disabled = !btn_params.is_active; 826 | debugBtn.innerText = btn_params.text; 827 | debugBtn.style.backgroundImage = btn_params.is_progress_visible ? "url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20viewport%3D%220%200%2048%2048%22%20width%3D%2248px%22%20height%3D%2248px%22%3E%3Ccircle%20cx%3D%2250%25%22%20cy%3D%2250%25%22%20stroke%3D%22%23fff%22%20stroke-width%3D%222.25%22%20stroke-linecap%3D%22round%22%20fill%3D%22none%22%20stroke-dashoffset%3D%22106%22%20r%3D%229%22%20stroke-dasharray%3D%2256.52%22%20rotate%3D%22-90%22%3E%3Canimate%20attributeName%3D%22stroke-dashoffset%22%20attributeType%3D%22XML%22%20dur%3D%22360s%22%20from%3D%220%22%20to%3D%2212500%22%20repeatCount%3D%22indefinite%22%3E%3C%2Fanimate%3E%3CanimateTransform%20attributeName%3D%22transform%22%20attributeType%3D%22XML%22%20type%3D%22rotate%22%20dur%3D%221s%22%20from%3D%22-90%2024%2024%22%20to%3D%22630%2024%2024%22%20repeatCount%3D%22indefinite%22%3E%3C%2FanimateTransform%3E%3C%2Fcircle%3E%3C%2Fsvg%3E')" : 'none'; 828 | debugBtn.style.backgroundColor = btn_params.color; 829 | debugBtn.style.color = btn_params.text_color; 830 | } else { 831 | debugBtn.style.display = 'none'; 832 | mainButtonHeight = 0; 833 | } 834 | if (document.documentElement) { 835 | document.documentElement.style.boxSizing = 'border-box'; 836 | document.documentElement.style.paddingBottom = mainButtonHeight + 'px'; 837 | } 838 | setViewportHeight(); 839 | } 840 | 841 | function setParams(params) { 842 | if (typeof params.text !== 'undefined') { 843 | var text = strTrim(params.text); 844 | if (!text.length) { 845 | console.error('[Telegram.WebApp] Main button text is required', params.text); 846 | throw Error('WebAppMainButtonParamInvalid'); 847 | } 848 | if (text.length > 64) { 849 | console.error('[Telegram.WebApp] Main button text is too long', text); 850 | throw Error('WebAppMainButtonParamInvalid'); 851 | } 852 | buttonText = text; 853 | } 854 | if (typeof params.color !== 'undefined') { 855 | if (params.color === false || 856 | params.color === null) { 857 | buttonColor = false; 858 | } else { 859 | var color = parseColorToHex(params.color); 860 | if (!color) { 861 | console.error('[Telegram.WebApp] Main button color format is invalid', params.color); 862 | throw Error('WebAppMainButtonParamInvalid'); 863 | } 864 | buttonColor = color; 865 | } 866 | } 867 | if (typeof params.text_color !== 'undefined') { 868 | if (params.text_color === false || 869 | params.text_color === null) { 870 | buttonTextColor = false; 871 | } else { 872 | var text_color = parseColorToHex(params.text_color); 873 | if (!text_color) { 874 | console.error('[Telegram.WebApp] Main button text color format is invalid', params.text_color); 875 | throw Error('WebAppMainButtonParamInvalid'); 876 | } 877 | buttonTextColor = text_color; 878 | } 879 | } 880 | if (typeof params.is_visible !== 'undefined') { 881 | if (params.is_visible && 882 | !mainButton.text.length) { 883 | console.error('[Telegram.WebApp] Main button text is required'); 884 | throw Error('WebAppMainButtonParamInvalid'); 885 | } 886 | isVisible = !!params.is_visible; 887 | } 888 | if (typeof params.is_active !== 'undefined') { 889 | isActive = !!params.is_active; 890 | } 891 | updateButton(); 892 | return mainButton; 893 | } 894 | 895 | mainButton.setText = function (text) { 896 | return mainButton.setParams({ text: text }); 897 | }; 898 | mainButton.onClick = function (callback) { 899 | onWebViewEvent('mainButtonClicked', callback); 900 | return mainButton; 901 | }; 902 | mainButton.offClick = function (callback) { 903 | offWebViewEvent('mainButtonClicked', callback); 904 | return mainButton; 905 | }; 906 | mainButton.show = function () { 907 | return mainButton.setParams({ is_visible: true }); 908 | }; 909 | mainButton.hide = function () { 910 | return mainButton.setParams({ is_visible: false }); 911 | }; 912 | mainButton.enable = function () { 913 | return mainButton.setParams({ is_active: true }); 914 | }; 915 | mainButton.disable = function () { 916 | return mainButton.setParams({ is_active: false }); 917 | }; 918 | mainButton.showProgress = function (leaveActive) { 919 | isActive = !!leaveActive; 920 | isProgressVisible = true; 921 | updateButton(); 922 | return mainButton; 923 | }; 924 | mainButton.hideProgress = function () { 925 | if (!mainButton.isActive) { 926 | isActive = true; 927 | } 928 | isProgressVisible = false; 929 | updateButton(); 930 | return mainButton; 931 | } 932 | mainButton.setParams = setParams; 933 | return mainButton; 934 | })(); 935 | 936 | var SettingsButton = (function () { 937 | var isVisible = false; 938 | 939 | var settingsButton = {}; 940 | Object.defineProperty(settingsButton, 'isVisible', { 941 | set: function (val) { setParams({ is_visible: val }); }, 942 | get: function () { return isVisible; }, 943 | enumerable: true 944 | }); 945 | 946 | var curButtonState = null; 947 | 948 | WebView.onEvent('settings_button_pressed', onSettingsButtonPressed); 949 | 950 | function onSettingsButtonPressed() { 951 | receiveWebViewEvent('settingsButtonClicked'); 952 | } 953 | 954 | function buttonParams() { 955 | return { is_visible: isVisible }; 956 | } 957 | 958 | function buttonState(btn_params) { 959 | if (typeof btn_params === 'undefined') { 960 | btn_params = buttonParams(); 961 | } 962 | return JSON.stringify(btn_params); 963 | } 964 | 965 | function buttonCheckVersion() { 966 | if (!versionAtLeast('6.10')) { 967 | console.warn('[Telegram.WebApp] SettingsButton is not supported in version ' + webAppVersion); 968 | return false; 969 | } 970 | return true; 971 | } 972 | 973 | function updateButton() { 974 | var btn_params = buttonParams(); 975 | var btn_state = buttonState(btn_params); 976 | if (curButtonState === btn_state) { 977 | return; 978 | } 979 | curButtonState = btn_state; 980 | WebView.postEvent('web_app_setup_settings_button', false, btn_params); 981 | } 982 | 983 | function setParams(params) { 984 | if (!buttonCheckVersion()) { 985 | return settingsButton; 986 | } 987 | if (typeof params.is_visible !== 'undefined') { 988 | isVisible = !!params.is_visible; 989 | } 990 | updateButton(); 991 | return settingsButton; 992 | } 993 | 994 | settingsButton.onClick = function (callback) { 995 | if (buttonCheckVersion()) { 996 | onWebViewEvent('settingsButtonClicked', callback); 997 | } 998 | return settingsButton; 999 | }; 1000 | settingsButton.offClick = function (callback) { 1001 | if (buttonCheckVersion()) { 1002 | offWebViewEvent('settingsButtonClicked', callback); 1003 | } 1004 | return settingsButton; 1005 | }; 1006 | settingsButton.show = function () { 1007 | return setParams({ is_visible: true }); 1008 | }; 1009 | settingsButton.hide = function () { 1010 | return setParams({ is_visible: false }); 1011 | }; 1012 | return settingsButton; 1013 | })(); 1014 | 1015 | var HapticFeedback = (function () { 1016 | var hapticFeedback = {}; 1017 | 1018 | function triggerFeedback(params) { 1019 | if (!versionAtLeast('6.1')) { 1020 | console.warn('[Telegram.WebApp] HapticFeedback is not supported in version ' + webAppVersion); 1021 | return hapticFeedback; 1022 | } 1023 | if (params.type == 'impact') { 1024 | if (params.impact_style != 'light' && 1025 | params.impact_style != 'medium' && 1026 | params.impact_style != 'heavy' && 1027 | params.impact_style != 'rigid' && 1028 | params.impact_style != 'soft') { 1029 | console.error('[Telegram.WebApp] Haptic impact style is invalid', params.impact_style); 1030 | throw Error('WebAppHapticImpactStyleInvalid'); 1031 | } 1032 | } else if (params.type == 'notification') { 1033 | if (params.notification_type != 'error' && 1034 | params.notification_type != 'success' && 1035 | params.notification_type != 'warning') { 1036 | console.error('[Telegram.WebApp] Haptic notification type is invalid', params.notification_type); 1037 | throw Error('WebAppHapticNotificationTypeInvalid'); 1038 | } 1039 | } else if (params.type == 'selection_change') { 1040 | // no params needed 1041 | } else { 1042 | console.error('[Telegram.WebApp] Haptic feedback type is invalid', params.type); 1043 | throw Error('WebAppHapticFeedbackTypeInvalid'); 1044 | } 1045 | WebView.postEvent('web_app_trigger_haptic_feedback', false, params); 1046 | return hapticFeedback; 1047 | } 1048 | 1049 | hapticFeedback.impactOccurred = function (style) { 1050 | return triggerFeedback({ type: 'impact', impact_style: style }); 1051 | }; 1052 | hapticFeedback.notificationOccurred = function (type) { 1053 | return triggerFeedback({ type: 'notification', notification_type: type }); 1054 | }; 1055 | hapticFeedback.selectionChanged = function () { 1056 | return triggerFeedback({ type: 'selection_change' }); 1057 | }; 1058 | return hapticFeedback; 1059 | })(); 1060 | 1061 | var CloudStorage = (function () { 1062 | var cloudStorage = {}; 1063 | 1064 | function invokeStorageMethod(method, params, callback) { 1065 | if (!versionAtLeast('6.9')) { 1066 | console.error('[Telegram.WebApp] CloudStorage is not supported in version ' + webAppVersion); 1067 | throw Error('WebAppMethodUnsupported'); 1068 | } 1069 | invokeCustomMethod(method, params, callback); 1070 | return cloudStorage; 1071 | } 1072 | 1073 | cloudStorage.setItem = function (key, value, callback) { 1074 | return invokeStorageMethod('saveStorageValue', { key: key, value: value }, callback); 1075 | }; 1076 | cloudStorage.getItem = function (key, callback) { 1077 | return cloudStorage.getItems([key], callback ? function (err, res) { 1078 | if (err) callback(err); 1079 | else callback(null, res[key]); 1080 | } : null); 1081 | }; 1082 | cloudStorage.getItems = function (keys, callback) { 1083 | return invokeStorageMethod('getStorageValues', { keys: keys }, callback); 1084 | }; 1085 | cloudStorage.removeItem = function (key, callback) { 1086 | return cloudStorage.removeItems([key], callback); 1087 | }; 1088 | cloudStorage.removeItems = function (keys, callback) { 1089 | return invokeStorageMethod('deleteStorageValues', { keys: keys }, callback); 1090 | }; 1091 | cloudStorage.getKeys = function (callback) { 1092 | return invokeStorageMethod('getStorageKeys', {}, callback); 1093 | }; 1094 | return cloudStorage; 1095 | })(); 1096 | 1097 | var webAppInvoices = {}; 1098 | function onInvoiceClosed(eventType, eventData) { 1099 | if (eventData.slug && webAppInvoices[eventData.slug]) { 1100 | var invoiceData = webAppInvoices[eventData.slug]; 1101 | delete webAppInvoices[eventData.slug]; 1102 | if (invoiceData.callback) { 1103 | invoiceData.callback(eventData.status); 1104 | } 1105 | receiveWebViewEvent('invoiceClosed', { 1106 | url: invoiceData.url, 1107 | status: eventData.status 1108 | }); 1109 | } 1110 | } 1111 | 1112 | var webAppPopupOpened = false; 1113 | function onPopupClosed(eventType, eventData) { 1114 | if (webAppPopupOpened) { 1115 | var popupData = webAppPopupOpened; 1116 | webAppPopupOpened = false; 1117 | var button_id = null; 1118 | if (typeof eventData.button_id !== 'undefined') { 1119 | button_id = eventData.button_id; 1120 | } 1121 | if (popupData.callback) { 1122 | popupData.callback(button_id); 1123 | } 1124 | receiveWebViewEvent('popupClosed', { 1125 | button_id: button_id 1126 | }); 1127 | } 1128 | } 1129 | 1130 | var webAppScanQrPopupOpened = false; 1131 | function onQrTextReceived(eventType, eventData) { 1132 | if (webAppScanQrPopupOpened) { 1133 | var popupData = webAppScanQrPopupOpened; 1134 | var data = null; 1135 | if (typeof eventData.data !== 'undefined') { 1136 | data = eventData.data; 1137 | } 1138 | if (popupData.callback) { 1139 | if (popupData.callback(data)) { 1140 | webAppScanQrPopupOpened = false; 1141 | WebView.postEvent('web_app_close_scan_qr_popup', false); 1142 | } 1143 | } 1144 | receiveWebViewEvent('qrTextReceived', { 1145 | data: data 1146 | }); 1147 | } 1148 | } 1149 | function onScanQrPopupClosed(eventType, eventData) { 1150 | webAppScanQrPopupOpened = false; 1151 | } 1152 | 1153 | function onClipboardTextReceived(eventType, eventData) { 1154 | if (eventData.req_id && webAppCallbacks[eventData.req_id]) { 1155 | var requestData = webAppCallbacks[eventData.req_id]; 1156 | delete webAppCallbacks[eventData.req_id]; 1157 | var data = null; 1158 | if (typeof eventData.data !== 'undefined') { 1159 | data = eventData.data; 1160 | } 1161 | if (requestData.callback) { 1162 | requestData.callback(data); 1163 | } 1164 | receiveWebViewEvent('clipboardTextReceived', { 1165 | data: data 1166 | }); 1167 | } 1168 | } 1169 | 1170 | var WebAppWriteAccessRequested = false; 1171 | function onWriteAccessRequested(eventType, eventData) { 1172 | if (WebAppWriteAccessRequested) { 1173 | var requestData = WebAppWriteAccessRequested; 1174 | WebAppWriteAccessRequested = false; 1175 | if (requestData.callback) { 1176 | requestData.callback(eventData.status == 'allowed'); 1177 | } 1178 | receiveWebViewEvent('writeAccessRequested', { 1179 | status: eventData.status 1180 | }); 1181 | } 1182 | } 1183 | 1184 | function getRequestedContact(callback, timeout) { 1185 | var reqTo, fallbackTo, reqDelay = 0; 1186 | var reqInvoke = function () { 1187 | invokeCustomMethod('getRequestedContact', {}, function (err, res) { 1188 | if (res && res.length) { 1189 | clearTimeout(fallbackTo); 1190 | callback(res); 1191 | } else { 1192 | reqDelay += 50; 1193 | reqTo = setTimeout(reqInvoke, reqDelay); 1194 | } 1195 | }); 1196 | }; 1197 | var fallbackInvoke = function () { 1198 | clearTimeout(reqTo); 1199 | callback(''); 1200 | }; 1201 | fallbackTo = setTimeout(fallbackInvoke, timeout); 1202 | reqInvoke(); 1203 | } 1204 | 1205 | var WebAppContactRequested = false; 1206 | function onPhoneRequested(eventType, eventData) { 1207 | if (WebAppContactRequested) { 1208 | var requestData = WebAppContactRequested; 1209 | WebAppContactRequested = false; 1210 | var requestSent = eventData.status == 'sent'; 1211 | var webViewEvent = { 1212 | status: eventData.status 1213 | }; 1214 | if (requestSent) { 1215 | getRequestedContact(function (res) { 1216 | if (res && res.length) { 1217 | webViewEvent.response = res; 1218 | webViewEvent.responseUnsafe = Utils.urlParseQueryString(res); 1219 | for (var key in webViewEvent.responseUnsafe) { 1220 | var val = webViewEvent.responseUnsafe[key]; 1221 | try { 1222 | if (val.substr(0, 1) == '{' && val.substr(-1) == '}' || 1223 | val.substr(0, 1) == '[' && val.substr(-1) == ']') { 1224 | webViewEvent.responseUnsafe[key] = JSON.parse(val); 1225 | } 1226 | } catch (e) { } 1227 | } 1228 | } 1229 | if (requestData.callback) { 1230 | requestData.callback(requestSent, webViewEvent); 1231 | } 1232 | receiveWebViewEvent('contactRequested', webViewEvent); 1233 | }, 3000); 1234 | } else { 1235 | if (requestData.callback) { 1236 | requestData.callback(requestSent, webViewEvent); 1237 | } 1238 | receiveWebViewEvent('contactRequested', webViewEvent); 1239 | } 1240 | } 1241 | } 1242 | 1243 | function onCustomMethodInvoked(eventType, eventData) { 1244 | if (eventData.req_id && webAppCallbacks[eventData.req_id]) { 1245 | var requestData = webAppCallbacks[eventData.req_id]; 1246 | delete webAppCallbacks[eventData.req_id]; 1247 | var res = null, err = null; 1248 | if (typeof eventData.result !== 'undefined') { 1249 | res = eventData.result; 1250 | } 1251 | if (typeof eventData.error !== 'undefined') { 1252 | err = eventData.error; 1253 | } 1254 | if (requestData.callback) { 1255 | requestData.callback(err, res); 1256 | } 1257 | } 1258 | } 1259 | 1260 | function invokeCustomMethod(method, params, callback) { 1261 | if (!versionAtLeast('6.9')) { 1262 | console.error('[Telegram.WebApp] Method invokeCustomMethod is not supported in version ' + webAppVersion); 1263 | throw Error('WebAppMethodUnsupported'); 1264 | } 1265 | var req_id = generateCallbackId(16); 1266 | var req_params = { req_id: req_id, method: method, params: params || {} }; 1267 | webAppCallbacks[req_id] = { 1268 | callback: callback 1269 | }; 1270 | WebView.postEvent('web_app_invoke_custom_method', false, req_params); 1271 | }; 1272 | 1273 | if (!window.Telegram) { 1274 | window.Telegram = {}; 1275 | } 1276 | 1277 | Object.defineProperty(WebApp, 'initData', { 1278 | get: function () { return webAppInitData; }, 1279 | enumerable: true 1280 | }); 1281 | Object.defineProperty(WebApp, 'initDataUnsafe', { 1282 | get: function () { return webAppInitDataUnsafe; }, 1283 | enumerable: true 1284 | }); 1285 | Object.defineProperty(WebApp, 'version', { 1286 | get: function () { return webAppVersion; }, 1287 | enumerable: true 1288 | }); 1289 | Object.defineProperty(WebApp, 'platform', { 1290 | get: function () { return webAppPlatform; }, 1291 | enumerable: true 1292 | }); 1293 | Object.defineProperty(WebApp, 'colorScheme', { 1294 | get: function () { return colorScheme; }, 1295 | enumerable: true 1296 | }); 1297 | Object.defineProperty(WebApp, 'themeParams', { 1298 | get: function () { return themeParams; }, 1299 | enumerable: true 1300 | }); 1301 | Object.defineProperty(WebApp, 'isExpanded', { 1302 | get: function () { return isExpanded; }, 1303 | enumerable: true 1304 | }); 1305 | Object.defineProperty(WebApp, 'viewportHeight', { 1306 | get: function () { return (viewportHeight === false ? window.innerHeight : viewportHeight) - mainButtonHeight; }, 1307 | enumerable: true 1308 | }); 1309 | Object.defineProperty(WebApp, 'viewportStableHeight', { 1310 | get: function () { return (viewportStableHeight === false ? window.innerHeight : viewportStableHeight) - mainButtonHeight; }, 1311 | enumerable: true 1312 | }); 1313 | Object.defineProperty(WebApp, 'isClosingConfirmationEnabled', { 1314 | set: function (val) { setClosingConfirmation(val); }, 1315 | get: function () { return isClosingConfirmationEnabled; }, 1316 | enumerable: true 1317 | }); 1318 | Object.defineProperty(WebApp, 'headerColor', { 1319 | set: function (val) { setHeaderColor(val); }, 1320 | get: function () { return getHeaderColor(); }, 1321 | enumerable: true 1322 | }); 1323 | Object.defineProperty(WebApp, 'backgroundColor', { 1324 | set: function (val) { setBackgroundColor(val); }, 1325 | get: function () { return getBackgroundColor(); }, 1326 | enumerable: true 1327 | }); 1328 | Object.defineProperty(WebApp, 'BackButton', { 1329 | value: BackButton, 1330 | enumerable: true 1331 | }); 1332 | Object.defineProperty(WebApp, 'MainButton', { 1333 | value: MainButton, 1334 | enumerable: true 1335 | }); 1336 | Object.defineProperty(WebApp, 'SettingsButton', { 1337 | value: SettingsButton, 1338 | enumerable: true 1339 | }); 1340 | Object.defineProperty(WebApp, 'HapticFeedback', { 1341 | value: HapticFeedback, 1342 | enumerable: true 1343 | }); 1344 | Object.defineProperty(WebApp, 'CloudStorage', { 1345 | value: CloudStorage, 1346 | enumerable: true 1347 | }); 1348 | WebApp.setHeaderColor = function (color_key) { 1349 | WebApp.headerColor = color_key; 1350 | }; 1351 | WebApp.setBackgroundColor = function (color) { 1352 | WebApp.backgroundColor = color; 1353 | }; 1354 | WebApp.enableClosingConfirmation = function () { 1355 | WebApp.isClosingConfirmationEnabled = true; 1356 | }; 1357 | WebApp.disableClosingConfirmation = function () { 1358 | WebApp.isClosingConfirmationEnabled = false; 1359 | }; 1360 | WebApp.isVersionAtLeast = function (ver) { 1361 | return versionAtLeast(ver); 1362 | }; 1363 | WebApp.onEvent = function (eventType, callback) { 1364 | onWebViewEvent(eventType, callback); 1365 | }; 1366 | WebApp.offEvent = function (eventType, callback) { 1367 | offWebViewEvent(eventType, callback); 1368 | }; 1369 | WebApp.sendData = function (data) { 1370 | if (!data || !data.length) { 1371 | console.error('[Telegram.WebApp] Data is required', data); 1372 | throw Error('WebAppDataInvalid'); 1373 | } 1374 | if (byteLength(data) > 4096) { 1375 | console.error('[Telegram.WebApp] Data is too long', data); 1376 | throw Error('WebAppDataInvalid'); 1377 | } 1378 | WebView.postEvent('web_app_data_send', false, { data: data }); 1379 | }; 1380 | WebApp.switchInlineQuery = function (query, choose_chat_types) { 1381 | if (!versionAtLeast('6.6')) { 1382 | console.error('[Telegram.WebApp] Method switchInlineQuery is not supported in version ' + webAppVersion); 1383 | throw Error('WebAppMethodUnsupported'); 1384 | } 1385 | if (!initParams.tgWebAppBotInline) { 1386 | console.error('[Telegram.WebApp] Inline mode is disabled for this bot. Read more about inline mode: https://core.telegram.org/bots/inline'); 1387 | throw Error('WebAppInlineModeDisabled'); 1388 | } 1389 | query = query || ''; 1390 | if (query.length > 256) { 1391 | console.error('[Telegram.WebApp] Inline query is too long', query); 1392 | throw Error('WebAppInlineQueryInvalid'); 1393 | } 1394 | var chat_types = []; 1395 | if (choose_chat_types) { 1396 | if (!Array.isArray(choose_chat_types)) { 1397 | console.error('[Telegram.WebApp] Choose chat types should be an array', choose_chat_types); 1398 | throw Error('WebAppInlineChooseChatTypesInvalid'); 1399 | } 1400 | var good_types = { users: 1, bots: 1, groups: 1, channels: 1 }; 1401 | for (var i = 0; i < choose_chat_types.length; i++) { 1402 | var chat_type = choose_chat_types[i]; 1403 | if (!good_types[chat_type]) { 1404 | console.error('[Telegram.WebApp] Choose chat type is invalid', chat_type); 1405 | throw Error('WebAppInlineChooseChatTypeInvalid'); 1406 | } 1407 | if (good_types[chat_type] != 2) { 1408 | good_types[chat_type] = 2; 1409 | chat_types.push(chat_type); 1410 | } 1411 | } 1412 | } 1413 | WebView.postEvent('web_app_switch_inline_query', false, { query: query, chat_types: chat_types }); 1414 | }; 1415 | WebApp.openLink = function (url, options) { 1416 | var a = document.createElement('A'); 1417 | a.href = url; 1418 | if (a.protocol != 'http:' && 1419 | a.protocol != 'https:') { 1420 | console.error('[Telegram.WebApp] Url protocol is not supported', url); 1421 | throw Error('WebAppTgUrlInvalid'); 1422 | } 1423 | var url = a.href; 1424 | options = options || {}; 1425 | if (versionAtLeast('6.1')) { 1426 | WebView.postEvent('web_app_open_link', false, { url: url, try_instant_view: versionAtLeast('6.4') && !!options.try_instant_view }); 1427 | } else { 1428 | window.open(url, '_blank'); 1429 | } 1430 | }; 1431 | WebApp.openTelegramLink = function (url) { 1432 | var a = document.createElement('A'); 1433 | a.href = url; 1434 | if (a.protocol != 'http:' && 1435 | a.protocol != 'https:') { 1436 | console.error('[Telegram.WebApp] Url protocol is not supported', url); 1437 | throw Error('WebAppTgUrlInvalid'); 1438 | } 1439 | if (a.hostname != 't.me') { 1440 | console.error('[Telegram.WebApp] Url host is not supported', url); 1441 | throw Error('WebAppTgUrlInvalid'); 1442 | } 1443 | var path_full = a.pathname + a.search; 1444 | if (isIframe || versionAtLeast('6.1')) { 1445 | WebView.postEvent('web_app_open_tg_link', false, { path_full: path_full }); 1446 | } else { 1447 | location.href = 'https://t.me' + path_full; 1448 | } 1449 | }; 1450 | WebApp.openInvoice = function (url, callback) { 1451 | var a = document.createElement('A'), match, slug; 1452 | a.href = url; 1453 | if (a.protocol != 'http:' && 1454 | a.protocol != 'https:' || 1455 | a.hostname != 't.me' || 1456 | !(match = a.pathname.match(/^\/(\$|invoice\/)([A-Za-z0-9\-_=]+)$/)) || 1457 | !(slug = match[2])) { 1458 | console.error('[Telegram.WebApp] Invoice url is invalid', url); 1459 | throw Error('WebAppInvoiceUrlInvalid'); 1460 | } 1461 | if (!versionAtLeast('6.1')) { 1462 | console.error('[Telegram.WebApp] Method openInvoice is not supported in version ' + webAppVersion); 1463 | throw Error('WebAppMethodUnsupported'); 1464 | } 1465 | if (webAppInvoices[slug]) { 1466 | console.error('[Telegram.WebApp] Invoice is already opened'); 1467 | throw Error('WebAppInvoiceOpened'); 1468 | } 1469 | webAppInvoices[slug] = { 1470 | url: url, 1471 | callback: callback 1472 | }; 1473 | WebView.postEvent('web_app_open_invoice', false, { slug: slug }); 1474 | }; 1475 | WebApp.showPopup = function (params, callback) { 1476 | if (!versionAtLeast('6.2')) { 1477 | console.error('[Telegram.WebApp] Method showPopup is not supported in version ' + webAppVersion); 1478 | throw Error('WebAppMethodUnsupported'); 1479 | } 1480 | if (webAppPopupOpened) { 1481 | console.error('[Telegram.WebApp] Popup is already opened'); 1482 | throw Error('WebAppPopupOpened'); 1483 | } 1484 | var title = ''; 1485 | var message = ''; 1486 | var buttons = []; 1487 | var popup_buttons = {}; 1488 | var popup_params = {}; 1489 | if (typeof params.title !== 'undefined') { 1490 | title = strTrim(params.title); 1491 | if (title.length > 64) { 1492 | console.error('[Telegram.WebApp] Popup title is too long', title); 1493 | throw Error('WebAppPopupParamInvalid'); 1494 | } 1495 | if (title.length > 0) { 1496 | popup_params.title = title; 1497 | } 1498 | } 1499 | if (typeof params.message !== 'undefined') { 1500 | message = strTrim(params.message); 1501 | } 1502 | if (!message.length) { 1503 | console.error('[Telegram.WebApp] Popup message is required', params.message); 1504 | throw Error('WebAppPopupParamInvalid'); 1505 | } 1506 | if (message.length > 256) { 1507 | console.error('[Telegram.WebApp] Popup message is too long', message); 1508 | throw Error('WebAppPopupParamInvalid'); 1509 | } 1510 | popup_params.message = message; 1511 | if (typeof params.buttons !== 'undefined') { 1512 | if (!Array.isArray(params.buttons)) { 1513 | console.error('[Telegram.WebApp] Popup buttons should be an array', params.buttons); 1514 | throw Error('WebAppPopupParamInvalid'); 1515 | } 1516 | for (var i = 0; i < params.buttons.length; i++) { 1517 | var button = params.buttons[i]; 1518 | var btn = {}; 1519 | var id = ''; 1520 | if (typeof button.id !== 'undefined') { 1521 | id = button.id.toString(); 1522 | if (id.length > 64) { 1523 | console.error('[Telegram.WebApp] Popup button id is too long', id); 1524 | throw Error('WebAppPopupParamInvalid'); 1525 | } 1526 | } 1527 | btn.id = id; 1528 | var button_type = button.type; 1529 | if (typeof button_type === 'undefined') { 1530 | button_type = 'default'; 1531 | } 1532 | btn.type = button_type; 1533 | if (button_type == 'ok' || 1534 | button_type == 'close' || 1535 | button_type == 'cancel') { 1536 | // no params needed 1537 | } else if (button_type == 'default' || 1538 | button_type == 'destructive') { 1539 | var text = ''; 1540 | if (typeof button.text !== 'undefined') { 1541 | text = strTrim(button.text); 1542 | } 1543 | if (!text.length) { 1544 | console.error('[Telegram.WebApp] Popup button text is required for type ' + button_type, button.text); 1545 | throw Error('WebAppPopupParamInvalid'); 1546 | } 1547 | if (text.length > 64) { 1548 | console.error('[Telegram.WebApp] Popup button text is too long', text); 1549 | throw Error('WebAppPopupParamInvalid'); 1550 | } 1551 | btn.text = text; 1552 | } else { 1553 | console.error('[Telegram.WebApp] Popup button type is invalid', button_type); 1554 | throw Error('WebAppPopupParamInvalid'); 1555 | } 1556 | buttons.push(btn); 1557 | } 1558 | } else { 1559 | buttons.push({ id: '', type: 'close' }); 1560 | } 1561 | if (buttons.length < 1) { 1562 | console.error('[Telegram.WebApp] Popup should have at least one button'); 1563 | throw Error('WebAppPopupParamInvalid'); 1564 | } 1565 | if (buttons.length > 3) { 1566 | console.error('[Telegram.WebApp] Popup should not have more than 3 buttons'); 1567 | throw Error('WebAppPopupParamInvalid'); 1568 | } 1569 | popup_params.buttons = buttons; 1570 | 1571 | webAppPopupOpened = { 1572 | callback: callback 1573 | }; 1574 | WebView.postEvent('web_app_open_popup', false, popup_params); 1575 | }; 1576 | WebApp.showAlert = function (message, callback) { 1577 | WebApp.showPopup({ 1578 | message: message 1579 | }, callback ? function () { callback(); } : null); 1580 | }; 1581 | WebApp.showConfirm = function (message, callback) { 1582 | WebApp.showPopup({ 1583 | message: message, 1584 | buttons: [ 1585 | { type: 'ok', id: 'ok' }, 1586 | { type: 'cancel' } 1587 | ] 1588 | }, callback ? function (button_id) { 1589 | callback(button_id == 'ok'); 1590 | } : null); 1591 | }; 1592 | WebApp.showScanQrPopup = function (params, callback) { 1593 | if (!versionAtLeast('6.4')) { 1594 | console.error('[Telegram.WebApp] Method showScanQrPopup is not supported in version ' + webAppVersion); 1595 | throw Error('WebAppMethodUnsupported'); 1596 | } 1597 | if (webAppScanQrPopupOpened) { 1598 | console.error('[Telegram.WebApp] Popup is already opened'); 1599 | throw Error('WebAppScanQrPopupOpened'); 1600 | } 1601 | var text = ''; 1602 | var popup_params = {}; 1603 | if (typeof params.text !== 'undefined') { 1604 | text = strTrim(params.text); 1605 | if (text.length > 64) { 1606 | console.error('[Telegram.WebApp] Scan QR popup text is too long', text); 1607 | throw Error('WebAppScanQrPopupParamInvalid'); 1608 | } 1609 | if (text.length > 0) { 1610 | popup_params.text = text; 1611 | } 1612 | } 1613 | 1614 | webAppScanQrPopupOpened = { 1615 | callback: callback 1616 | }; 1617 | WebView.postEvent('web_app_open_scan_qr_popup', false, popup_params); 1618 | }; 1619 | WebApp.closeScanQrPopup = function () { 1620 | if (!versionAtLeast('6.4')) { 1621 | console.error('[Telegram.WebApp] Method closeScanQrPopup is not supported in version ' + webAppVersion); 1622 | throw Error('WebAppMethodUnsupported'); 1623 | } 1624 | 1625 | webAppScanQrPopupOpened = false; 1626 | WebView.postEvent('web_app_close_scan_qr_popup', false); 1627 | }; 1628 | WebApp.readTextFromClipboard = function (callback) { 1629 | if (!versionAtLeast('6.4')) { 1630 | console.error('[Telegram.WebApp] Method readTextFromClipboard is not supported in version ' + webAppVersion); 1631 | throw Error('WebAppMethodUnsupported'); 1632 | } 1633 | var req_id = generateCallbackId(16); 1634 | var req_params = { req_id: req_id }; 1635 | webAppCallbacks[req_id] = { 1636 | callback: callback 1637 | }; 1638 | WebView.postEvent('web_app_read_text_from_clipboard', false, req_params); 1639 | }; 1640 | WebApp.requestWriteAccess = function (callback) { 1641 | if (!versionAtLeast('6.9')) { 1642 | console.error('[Telegram.WebApp] Method requestWriteAccess is not supported in version ' + webAppVersion); 1643 | throw Error('WebAppMethodUnsupported'); 1644 | } 1645 | if (WebAppWriteAccessRequested) { 1646 | console.error('[Telegram.WebApp] Write access is already requested'); 1647 | throw Error('WebAppWriteAccessRequested'); 1648 | } 1649 | WebAppWriteAccessRequested = { 1650 | callback: callback 1651 | }; 1652 | WebView.postEvent('web_app_request_write_access'); 1653 | }; 1654 | WebApp.requestContact = function (callback) { 1655 | if (!versionAtLeast('6.9')) { 1656 | console.error('[Telegram.WebApp] Method requestContact is not supported in version ' + webAppVersion); 1657 | throw Error('WebAppMethodUnsupported'); 1658 | } 1659 | if (WebAppContactRequested) { 1660 | console.error('[Telegram.WebApp] Contact is already requested'); 1661 | throw Error('WebAppContactRequested'); 1662 | } 1663 | WebAppContactRequested = { 1664 | callback: callback 1665 | }; 1666 | WebView.postEvent('web_app_request_phone'); 1667 | }; 1668 | WebApp.invokeCustomMethod = function (method, params, callback) { 1669 | invokeCustomMethod(method, params, callback); 1670 | }; 1671 | WebApp.ready = function () { 1672 | WebView.postEvent('web_app_ready'); 1673 | }; 1674 | WebApp.expand = function () { 1675 | WebView.postEvent('web_app_expand'); 1676 | }; 1677 | WebApp.close = function () { 1678 | WebView.postEvent('web_app_close'); 1679 | }; 1680 | 1681 | window.Telegram.WebApp = WebApp; 1682 | 1683 | updateHeaderColor(); 1684 | updateBackgroundColor(); 1685 | setViewportHeight(); 1686 | if (initParams.tgWebAppShowSettings) { 1687 | SettingsButton.show(); 1688 | } 1689 | 1690 | window.addEventListener('resize', onWindowResize); 1691 | if (isIframe) { 1692 | document.addEventListener('click', linkHandler); 1693 | } 1694 | 1695 | WebView.onEvent('theme_changed', onThemeChanged); 1696 | WebView.onEvent('viewport_changed', onViewportChanged); 1697 | WebView.onEvent('invoice_closed', onInvoiceClosed); 1698 | WebView.onEvent('popup_closed', onPopupClosed); 1699 | WebView.onEvent('qr_text_received', onQrTextReceived); 1700 | WebView.onEvent('scan_qr_popup_closed', onScanQrPopupClosed); 1701 | WebView.onEvent('clipboard_text_received', onClipboardTextReceived); 1702 | WebView.onEvent('write_access_requested', onWriteAccessRequested); 1703 | WebView.onEvent('phone_requested', onPhoneRequested); 1704 | WebView.onEvent('custom_method_invoked', onCustomMethodInvoked); 1705 | WebView.postEvent('web_app_request_theme'); 1706 | WebView.postEvent('web_app_request_viewport'); 1707 | 1708 | })(); 1709 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@alloc/quick-lru@^5.2.0": 6 | version "5.2.0" 7 | resolved "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz" 8 | integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== 9 | 10 | "@ampproject/remapping@^2.2.1": 11 | version "2.2.1" 12 | resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz" 13 | integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== 14 | dependencies: 15 | "@jridgewell/gen-mapping" "^0.3.0" 16 | "@jridgewell/trace-mapping" "^0.3.9" 17 | 18 | "@cspotcode/source-map-support@^0.8.0": 19 | version "0.8.1" 20 | resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" 21 | integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== 22 | dependencies: 23 | "@jridgewell/trace-mapping" "0.3.9" 24 | 25 | "@csstools/selector-specificity@^2.0.0": 26 | version "2.2.0" 27 | resolved "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz" 28 | integrity sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw== 29 | 30 | "@esbuild/aix-ppc64@0.21.5": 31 | version "0.21.5" 32 | resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" 33 | integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== 34 | 35 | "@esbuild/android-arm64@0.21.5": 36 | version "0.21.5" 37 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" 38 | integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== 39 | 40 | "@esbuild/android-arm@0.21.5": 41 | version "0.21.5" 42 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" 43 | integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== 44 | 45 | "@esbuild/android-x64@0.21.5": 46 | version "0.21.5" 47 | resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" 48 | integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== 49 | 50 | "@esbuild/darwin-arm64@0.21.5": 51 | version "0.21.5" 52 | resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" 53 | integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== 54 | 55 | "@esbuild/darwin-x64@0.21.5": 56 | version "0.21.5" 57 | resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" 58 | integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== 59 | 60 | "@esbuild/freebsd-arm64@0.21.5": 61 | version "0.21.5" 62 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" 63 | integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== 64 | 65 | "@esbuild/freebsd-x64@0.21.5": 66 | version "0.21.5" 67 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" 68 | integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== 69 | 70 | "@esbuild/linux-arm64@0.21.5": 71 | version "0.21.5" 72 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" 73 | integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== 74 | 75 | "@esbuild/linux-arm@0.21.5": 76 | version "0.21.5" 77 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" 78 | integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== 79 | 80 | "@esbuild/linux-ia32@0.21.5": 81 | version "0.21.5" 82 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" 83 | integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== 84 | 85 | "@esbuild/linux-loong64@0.21.5": 86 | version "0.21.5" 87 | resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" 88 | integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== 89 | 90 | "@esbuild/linux-mips64el@0.21.5": 91 | version "0.21.5" 92 | resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" 93 | integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== 94 | 95 | "@esbuild/linux-ppc64@0.21.5": 96 | version "0.21.5" 97 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" 98 | integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== 99 | 100 | "@esbuild/linux-riscv64@0.21.5": 101 | version "0.21.5" 102 | resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" 103 | integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== 104 | 105 | "@esbuild/linux-s390x@0.21.5": 106 | version "0.21.5" 107 | resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" 108 | integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== 109 | 110 | "@esbuild/linux-x64@0.21.5": 111 | version "0.21.5" 112 | resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" 113 | integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== 114 | 115 | "@esbuild/netbsd-x64@0.21.5": 116 | version "0.21.5" 117 | resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" 118 | integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== 119 | 120 | "@esbuild/openbsd-x64@0.21.5": 121 | version "0.21.5" 122 | resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" 123 | integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== 124 | 125 | "@esbuild/sunos-x64@0.21.5": 126 | version "0.21.5" 127 | resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" 128 | integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== 129 | 130 | "@esbuild/win32-arm64@0.21.5": 131 | version "0.21.5" 132 | resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" 133 | integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== 134 | 135 | "@esbuild/win32-ia32@0.21.5": 136 | version "0.21.5" 137 | resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" 138 | integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== 139 | 140 | "@esbuild/win32-x64@0.21.5": 141 | version "0.21.5" 142 | resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" 143 | integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== 144 | 145 | "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": 146 | version "0.3.3" 147 | resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz" 148 | integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== 149 | dependencies: 150 | "@jridgewell/set-array" "^1.0.1" 151 | "@jridgewell/sourcemap-codec" "^1.4.10" 152 | "@jridgewell/trace-mapping" "^0.3.9" 153 | 154 | "@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": 155 | version "3.1.0" 156 | resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz" 157 | integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== 158 | 159 | "@jridgewell/set-array@^1.0.1": 160 | version "1.1.2" 161 | resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" 162 | integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== 163 | 164 | "@jridgewell/sourcemap-codec@1.4.14": 165 | version "1.4.14" 166 | resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" 167 | integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== 168 | 169 | "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15": 170 | version "1.4.15" 171 | resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz" 172 | integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== 173 | 174 | "@jridgewell/sourcemap-codec@^1.5.0": 175 | version "1.5.0" 176 | resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" 177 | integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== 178 | 179 | "@jridgewell/trace-mapping@0.3.9": 180 | version "0.3.9" 181 | resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" 182 | integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== 183 | dependencies: 184 | "@jridgewell/resolve-uri" "^3.0.3" 185 | "@jridgewell/sourcemap-codec" "^1.4.10" 186 | 187 | "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": 188 | version "0.3.18" 189 | resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz" 190 | integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== 191 | dependencies: 192 | "@jridgewell/resolve-uri" "3.1.0" 193 | "@jridgewell/sourcemap-codec" "1.4.14" 194 | 195 | "@nodelib/fs.scandir@2.1.5": 196 | version "2.1.5" 197 | resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" 198 | integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== 199 | dependencies: 200 | "@nodelib/fs.stat" "2.0.5" 201 | run-parallel "^1.1.9" 202 | 203 | "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": 204 | version "2.0.5" 205 | resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" 206 | integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== 207 | 208 | "@nodelib/fs.walk@^1.2.3": 209 | version "1.2.8" 210 | resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" 211 | integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== 212 | dependencies: 213 | "@nodelib/fs.scandir" "2.1.5" 214 | fastq "^1.6.0" 215 | 216 | "@polka/url@^1.0.0-next.24": 217 | version "1.0.0-next.28" 218 | resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.28.tgz#d45e01c4a56f143ee69c54dd6b12eade9e270a73" 219 | integrity sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw== 220 | 221 | "@rollup/rollup-android-arm-eabi@4.34.9": 222 | version "4.34.9" 223 | resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.9.tgz#661a45a4709c70e59e596ec78daa9cb8b8d27604" 224 | integrity sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA== 225 | 226 | "@rollup/rollup-android-arm64@4.34.9": 227 | version "4.34.9" 228 | resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.9.tgz#128fe8dd510d880cf98b4cb6c7add326815a0c4b" 229 | integrity sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg== 230 | 231 | "@rollup/rollup-darwin-arm64@4.34.9": 232 | version "4.34.9" 233 | resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.9.tgz#363467bc49fd0b1e17075798ac8e9ad1e1e29535" 234 | integrity sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ== 235 | 236 | "@rollup/rollup-darwin-x64@4.34.9": 237 | version "4.34.9" 238 | resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.9.tgz#c2fe3d85fffe47f0ed0f076b3563ada22c8af19c" 239 | integrity sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q== 240 | 241 | "@rollup/rollup-freebsd-arm64@4.34.9": 242 | version "4.34.9" 243 | resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.9.tgz#d95bd8f6eaaf829781144fc8bd2d5d71d9f6a9f5" 244 | integrity sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw== 245 | 246 | "@rollup/rollup-freebsd-x64@4.34.9": 247 | version "4.34.9" 248 | resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.9.tgz#c3576c6011656e4966ded29f051edec636b44564" 249 | integrity sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g== 250 | 251 | "@rollup/rollup-linux-arm-gnueabihf@4.34.9": 252 | version "4.34.9" 253 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.9.tgz#48c87d0dee4f8dc9591a416717f91b4a89d77e3d" 254 | integrity sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg== 255 | 256 | "@rollup/rollup-linux-arm-musleabihf@4.34.9": 257 | version "4.34.9" 258 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.9.tgz#f4c4e7c03a7767f2e5aa9d0c5cfbf5c0f59f2d41" 259 | integrity sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA== 260 | 261 | "@rollup/rollup-linux-arm64-gnu@4.34.9": 262 | version "4.34.9" 263 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.9.tgz#1015c9d07a99005025d13b8622b7600029d0b52f" 264 | integrity sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw== 265 | 266 | "@rollup/rollup-linux-arm64-musl@4.34.9": 267 | version "4.34.9" 268 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.9.tgz#8f895eb5577748fc75af21beae32439626e0a14c" 269 | integrity sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A== 270 | 271 | "@rollup/rollup-linux-loongarch64-gnu@4.34.9": 272 | version "4.34.9" 273 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.9.tgz#c9cd5dbbdc6b3ca4dbeeb0337498cf31949004a0" 274 | integrity sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg== 275 | 276 | "@rollup/rollup-linux-powerpc64le-gnu@4.34.9": 277 | version "4.34.9" 278 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.9.tgz#7ebb5b4441faa17843a210f7d0583a20c93b40e4" 279 | integrity sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA== 280 | 281 | "@rollup/rollup-linux-riscv64-gnu@4.34.9": 282 | version "4.34.9" 283 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.9.tgz#10f5d7349fbd2fe78f9e36ecc90aab3154435c8d" 284 | integrity sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg== 285 | 286 | "@rollup/rollup-linux-s390x-gnu@4.34.9": 287 | version "4.34.9" 288 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.9.tgz#196347d2fa20593ab09d0b7e2589fb69bdf742c6" 289 | integrity sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ== 290 | 291 | "@rollup/rollup-linux-x64-gnu@4.34.9": 292 | version "4.34.9" 293 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.9.tgz#7193cbd8d128212b8acda37e01b39d9e96259ef8" 294 | integrity sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A== 295 | 296 | "@rollup/rollup-linux-x64-musl@4.34.9": 297 | version "4.34.9" 298 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.9.tgz#29a6867278ca0420b891574cfab98ecad70c59d1" 299 | integrity sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA== 300 | 301 | "@rollup/rollup-win32-arm64-msvc@4.34.9": 302 | version "4.34.9" 303 | resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.9.tgz#89427dcac0c8e3a6d32b13a03a296a275d0de9a9" 304 | integrity sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q== 305 | 306 | "@rollup/rollup-win32-ia32-msvc@4.34.9": 307 | version "4.34.9" 308 | resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.9.tgz#ecb9711ba2b6d2bf6ee51265abe057ab90913deb" 309 | integrity sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w== 310 | 311 | "@rollup/rollup-win32-x64-msvc@4.34.9": 312 | version "4.34.9" 313 | resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.9.tgz#1973871850856ae72bc678aeb066ab952330e923" 314 | integrity sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw== 315 | 316 | "@sveltejs/adapter-static@^3.0.0": 317 | version "3.0.8" 318 | resolved "https://registry.yarnpkg.com/@sveltejs/adapter-static/-/adapter-static-3.0.8.tgz#f23ee99a9678dbaec58b79d183bc3defbfe99f1a" 319 | integrity sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg== 320 | 321 | "@sveltejs/kit@^2.0.0": 322 | version "2.17.3" 323 | resolved "https://registry.yarnpkg.com/@sveltejs/kit/-/kit-2.17.3.tgz#523dc5aace2f016e02ef4ecaccc0f9cf2e8eaada" 324 | integrity sha512-GcNaPDr0ti4O/TonPewkML2DG7UVXkSxPN3nPMlpmx0Rs4b2kVP4gymz98WEHlfzPXdd4uOOT1Js26DtieTNBQ== 325 | dependencies: 326 | "@types/cookie" "^0.6.0" 327 | cookie "^0.6.0" 328 | devalue "^5.1.0" 329 | esm-env "^1.2.2" 330 | import-meta-resolve "^4.1.0" 331 | kleur "^4.1.5" 332 | magic-string "^0.30.5" 333 | mrmime "^2.0.0" 334 | sade "^1.8.1" 335 | set-cookie-parser "^2.6.0" 336 | sirv "^3.0.0" 337 | 338 | "@sveltejs/vite-plugin-svelte-inspector@^2.1.0": 339 | version "2.1.0" 340 | resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz#116ba2b73be43c1d7d93de749f37becc7e45bb8c" 341 | integrity sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg== 342 | dependencies: 343 | debug "^4.3.4" 344 | 345 | "@sveltejs/vite-plugin-svelte@^3.0.0": 346 | version "3.1.2" 347 | resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.2.tgz#be3120b52e6d9facb55d58392b0dad9e5a35ba6f" 348 | integrity sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA== 349 | dependencies: 350 | "@sveltejs/vite-plugin-svelte-inspector" "^2.1.0" 351 | debug "^4.3.4" 352 | deepmerge "^4.3.1" 353 | kleur "^4.1.5" 354 | magic-string "^0.30.10" 355 | svelte-hmr "^0.16.0" 356 | vitefu "^0.2.5" 357 | 358 | "@tsconfig/node10@^1.0.7": 359 | version "1.0.9" 360 | resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz" 361 | integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== 362 | 363 | "@tsconfig/node12@^1.0.7": 364 | version "1.0.11" 365 | resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz" 366 | integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== 367 | 368 | "@tsconfig/node14@^1.0.0": 369 | version "1.0.3" 370 | resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz" 371 | integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== 372 | 373 | "@tsconfig/node16@^1.0.2": 374 | version "1.0.4" 375 | resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz" 376 | integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== 377 | 378 | "@types/cookie@^0.6.0": 379 | version "0.6.0" 380 | resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5" 381 | integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA== 382 | 383 | "@types/estree@*", "@types/estree@^1.0.0": 384 | version "1.0.1" 385 | resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz" 386 | integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== 387 | 388 | "@types/estree@1.0.6": 389 | version "1.0.6" 390 | resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" 391 | integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== 392 | 393 | "@types/node@^20.3.1": 394 | version "20.3.1" 395 | resolved "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz" 396 | integrity sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg== 397 | 398 | "@types/pug@^2.0.6": 399 | version "2.0.6" 400 | resolved "https://registry.npmjs.org/@types/pug/-/pug-2.0.6.tgz" 401 | integrity sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg== 402 | 403 | "@types/telegram-web-app@^6.9.3": 404 | version "6.9.3" 405 | resolved "https://registry.yarnpkg.com/@types/telegram-web-app/-/telegram-web-app-6.9.3.tgz#33c577567fbd19d8ea828fb84a8b34446478f097" 406 | integrity sha512-2ud9mtXCwqRIY/CHXCTv4wJ9+Uo7RwqrYVVKAPedMwuabWoINoGkH7i2cJQ6IECiod+EMpGtFkaJkpxaPMUSGw== 407 | 408 | acorn-walk@^8.1.1: 409 | version "8.2.0" 410 | resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz" 411 | integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== 412 | 413 | acorn@^8.4.1, acorn@^8.8.2: 414 | version "8.9.0" 415 | resolved "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz" 416 | integrity sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ== 417 | 418 | any-promise@^1.0.0: 419 | version "1.3.0" 420 | resolved "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz" 421 | integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== 422 | 423 | anymatch@~3.1.2: 424 | version "3.1.3" 425 | resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" 426 | integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== 427 | dependencies: 428 | normalize-path "^3.0.0" 429 | picomatch "^2.0.4" 430 | 431 | arg@^4.1.0: 432 | version "4.1.3" 433 | resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" 434 | integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== 435 | 436 | arg@^5.0.2: 437 | version "5.0.2" 438 | resolved "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz" 439 | integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== 440 | 441 | aria-query@^5.2.1: 442 | version "5.3.0" 443 | resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz" 444 | integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== 445 | dependencies: 446 | dequal "^2.0.3" 447 | 448 | asynckit@^0.4.0: 449 | version "0.4.0" 450 | resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" 451 | integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== 452 | 453 | autoprefixer@^10.4.14: 454 | version "10.4.14" 455 | resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz" 456 | integrity sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ== 457 | dependencies: 458 | browserslist "^4.21.5" 459 | caniuse-lite "^1.0.30001464" 460 | fraction.js "^4.2.0" 461 | normalize-range "^0.1.2" 462 | picocolors "^1.0.0" 463 | postcss-value-parser "^4.2.0" 464 | 465 | axios@^1.1.2: 466 | version "1.4.0" 467 | resolved "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz" 468 | integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== 469 | dependencies: 470 | follow-redirects "^1.15.0" 471 | form-data "^4.0.0" 472 | proxy-from-env "^1.1.0" 473 | 474 | axobject-query@^3.2.1: 475 | version "3.2.1" 476 | resolved "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz" 477 | integrity sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg== 478 | dependencies: 479 | dequal "^2.0.3" 480 | 481 | balanced-match@^1.0.0: 482 | version "1.0.2" 483 | resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" 484 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 485 | 486 | binary-extensions@^2.0.0: 487 | version "2.2.0" 488 | resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" 489 | integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== 490 | 491 | brace-expansion@^1.1.7: 492 | version "1.1.11" 493 | resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" 494 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 495 | dependencies: 496 | balanced-match "^1.0.0" 497 | concat-map "0.0.1" 498 | 499 | braces@^3.0.2, braces@~3.0.2: 500 | version "3.0.2" 501 | resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" 502 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== 503 | dependencies: 504 | fill-range "^7.0.1" 505 | 506 | browserslist@^4.21.5: 507 | version "4.21.7" 508 | resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.7.tgz" 509 | integrity sha512-BauCXrQ7I2ftSqd2mvKHGo85XR0u7Ru3C/Hxsy/0TkfCtjrmAbPdzLGasmoiBxplpDXlPvdjX9u7srIMfgasNA== 510 | dependencies: 511 | caniuse-lite "^1.0.30001489" 512 | electron-to-chromium "^1.4.411" 513 | node-releases "^2.0.12" 514 | update-browserslist-db "^1.0.11" 515 | 516 | buffer-crc32@^0.2.5: 517 | version "0.2.13" 518 | resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" 519 | integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== 520 | 521 | callsites@^3.0.0: 522 | version "3.1.0" 523 | resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" 524 | integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== 525 | 526 | camelcase-css@^2.0.1: 527 | version "2.0.1" 528 | resolved "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz" 529 | integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== 530 | 531 | caniuse-lite@^1.0.30001464, caniuse-lite@^1.0.30001489: 532 | version "1.0.30001568" 533 | resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001568.tgz" 534 | integrity sha512-vSUkH84HontZJ88MiNrOau1EBrCqEQYgkC5gIySiDlpsm8sGVrhU7Kx4V6h0tnqaHzIHZv08HlJIwPbL4XL9+A== 535 | 536 | chokidar@^3.4.1, chokidar@^3.5.3: 537 | version "3.5.3" 538 | resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" 539 | integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== 540 | dependencies: 541 | anymatch "~3.1.2" 542 | braces "~3.0.2" 543 | glob-parent "~5.1.2" 544 | is-binary-path "~2.1.0" 545 | is-glob "~4.0.1" 546 | normalize-path "~3.0.0" 547 | readdirp "~3.6.0" 548 | optionalDependencies: 549 | fsevents "~2.3.2" 550 | 551 | code-red@^1.0.3: 552 | version "1.0.3" 553 | resolved "https://registry.npmjs.org/code-red/-/code-red-1.0.3.tgz" 554 | integrity sha512-kVwJELqiILQyG5aeuyKFbdsI1fmQy1Cmf7dQ8eGmVuJoaRVdwey7WaMknr2ZFeVSYSKT0rExsa8EGw0aoI/1QQ== 555 | dependencies: 556 | "@jridgewell/sourcemap-codec" "^1.4.14" 557 | "@types/estree" "^1.0.0" 558 | acorn "^8.8.2" 559 | estree-walker "^3.0.3" 560 | periscopic "^3.1.0" 561 | 562 | combined-stream@^1.0.8: 563 | version "1.0.8" 564 | resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" 565 | integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== 566 | dependencies: 567 | delayed-stream "~1.0.0" 568 | 569 | commander@^4.0.0: 570 | version "4.1.1" 571 | resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz" 572 | integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== 573 | 574 | concat-map@0.0.1: 575 | version "0.0.1" 576 | resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" 577 | integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== 578 | 579 | cookie@^0.6.0: 580 | version "0.6.0" 581 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" 582 | integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== 583 | 584 | create-require@^1.1.0: 585 | version "1.1.1" 586 | resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" 587 | integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== 588 | 589 | css-tree@^2.3.1: 590 | version "2.3.1" 591 | resolved "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz" 592 | integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw== 593 | dependencies: 594 | mdn-data "2.0.30" 595 | source-map-js "^1.0.1" 596 | 597 | cssesc@^3.0.0: 598 | version "3.0.0" 599 | resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" 600 | integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== 601 | 602 | debug@^4.3.4: 603 | version "4.3.4" 604 | resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" 605 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== 606 | dependencies: 607 | ms "2.1.2" 608 | 609 | deepmerge@^4.3.1: 610 | version "4.3.1" 611 | resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz" 612 | integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== 613 | 614 | delayed-stream@~1.0.0: 615 | version "1.0.0" 616 | resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" 617 | integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== 618 | 619 | dequal@^2.0.3: 620 | version "2.0.3" 621 | resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz" 622 | integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== 623 | 624 | detect-indent@^6.1.0: 625 | version "6.1.0" 626 | resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz" 627 | integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== 628 | 629 | devalue@^5.1.0: 630 | version "5.1.1" 631 | resolved "https://registry.yarnpkg.com/devalue/-/devalue-5.1.1.tgz#a71887ac0f354652851752654e4bd435a53891ae" 632 | integrity sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw== 633 | 634 | didyoumean@^1.2.2: 635 | version "1.2.2" 636 | resolved "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz" 637 | integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== 638 | 639 | diff@^4.0.1: 640 | version "4.0.2" 641 | resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" 642 | integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== 643 | 644 | dlv@^1.1.3: 645 | version "1.1.3" 646 | resolved "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz" 647 | integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== 648 | 649 | electron-to-chromium@^1.4.411: 650 | version "1.4.419" 651 | resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.419.tgz" 652 | integrity sha512-jdie3RiEgygvDTyS2sgjq71B36q2cDSBfPlwzUyuOrfYTNoYWyBxxjGJV/HAu3A2hB0Y+HesvCVkVAFoCKwCSw== 653 | 654 | es6-promise@^3.1.2: 655 | version "3.3.1" 656 | resolved "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz" 657 | integrity sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg== 658 | 659 | esbuild@^0.21.3: 660 | version "0.21.5" 661 | resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" 662 | integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== 663 | optionalDependencies: 664 | "@esbuild/aix-ppc64" "0.21.5" 665 | "@esbuild/android-arm" "0.21.5" 666 | "@esbuild/android-arm64" "0.21.5" 667 | "@esbuild/android-x64" "0.21.5" 668 | "@esbuild/darwin-arm64" "0.21.5" 669 | "@esbuild/darwin-x64" "0.21.5" 670 | "@esbuild/freebsd-arm64" "0.21.5" 671 | "@esbuild/freebsd-x64" "0.21.5" 672 | "@esbuild/linux-arm" "0.21.5" 673 | "@esbuild/linux-arm64" "0.21.5" 674 | "@esbuild/linux-ia32" "0.21.5" 675 | "@esbuild/linux-loong64" "0.21.5" 676 | "@esbuild/linux-mips64el" "0.21.5" 677 | "@esbuild/linux-ppc64" "0.21.5" 678 | "@esbuild/linux-riscv64" "0.21.5" 679 | "@esbuild/linux-s390x" "0.21.5" 680 | "@esbuild/linux-x64" "0.21.5" 681 | "@esbuild/netbsd-x64" "0.21.5" 682 | "@esbuild/openbsd-x64" "0.21.5" 683 | "@esbuild/sunos-x64" "0.21.5" 684 | "@esbuild/win32-arm64" "0.21.5" 685 | "@esbuild/win32-ia32" "0.21.5" 686 | "@esbuild/win32-x64" "0.21.5" 687 | 688 | escalade@^3.1.1: 689 | version "3.1.1" 690 | resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" 691 | integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== 692 | 693 | esm-env@^1.2.2: 694 | version "1.2.2" 695 | resolved "https://registry.yarnpkg.com/esm-env/-/esm-env-1.2.2.tgz#263c9455c55861f41618df31b20cb571fc20b75e" 696 | integrity sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA== 697 | 698 | estree-walker@^3.0.0, estree-walker@^3.0.3: 699 | version "3.0.3" 700 | resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz" 701 | integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== 702 | dependencies: 703 | "@types/estree" "^1.0.0" 704 | 705 | fast-glob@^3.2.12, fast-glob@^3.2.7: 706 | version "3.2.12" 707 | resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz" 708 | integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== 709 | dependencies: 710 | "@nodelib/fs.stat" "^2.0.2" 711 | "@nodelib/fs.walk" "^1.2.3" 712 | glob-parent "^5.1.2" 713 | merge2 "^1.3.0" 714 | micromatch "^4.0.4" 715 | 716 | fastq@^1.6.0: 717 | version "1.15.0" 718 | resolved "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz" 719 | integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== 720 | dependencies: 721 | reusify "^1.0.4" 722 | 723 | fill-range@^7.0.1: 724 | version "7.0.1" 725 | resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" 726 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== 727 | dependencies: 728 | to-regex-range "^5.0.1" 729 | 730 | follow-redirects@^1.15.0: 731 | version "1.15.2" 732 | resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz" 733 | integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== 734 | 735 | form-data@^4.0.0: 736 | version "4.0.0" 737 | resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" 738 | integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== 739 | dependencies: 740 | asynckit "^0.4.0" 741 | combined-stream "^1.0.8" 742 | mime-types "^2.1.12" 743 | 744 | fraction.js@^4.2.0: 745 | version "4.2.0" 746 | resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz" 747 | integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== 748 | 749 | fs.realpath@^1.0.0: 750 | version "1.0.0" 751 | resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" 752 | integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== 753 | 754 | fsevents@~2.3.2, fsevents@~2.3.3: 755 | version "2.3.3" 756 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" 757 | integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== 758 | 759 | function-bind@^1.1.1: 760 | version "1.1.1" 761 | resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" 762 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 763 | 764 | glob-parent@^5.1.2, glob-parent@~5.1.2: 765 | version "5.1.2" 766 | resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" 767 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 768 | dependencies: 769 | is-glob "^4.0.1" 770 | 771 | glob-parent@^6.0.2: 772 | version "6.0.2" 773 | resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" 774 | integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== 775 | dependencies: 776 | is-glob "^4.0.3" 777 | 778 | glob@7.1.6: 779 | version "7.1.6" 780 | resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz" 781 | integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== 782 | dependencies: 783 | fs.realpath "^1.0.0" 784 | inflight "^1.0.4" 785 | inherits "2" 786 | minimatch "^3.0.4" 787 | once "^1.3.0" 788 | path-is-absolute "^1.0.0" 789 | 790 | glob@^7.1.3: 791 | version "7.2.3" 792 | resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" 793 | integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== 794 | dependencies: 795 | fs.realpath "^1.0.0" 796 | inflight "^1.0.4" 797 | inherits "2" 798 | minimatch "^3.1.1" 799 | once "^1.3.0" 800 | path-is-absolute "^1.0.0" 801 | 802 | graceful-fs@^4.1.3: 803 | version "4.2.11" 804 | resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" 805 | integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== 806 | 807 | has@^1.0.3: 808 | version "1.0.3" 809 | resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" 810 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 811 | dependencies: 812 | function-bind "^1.1.1" 813 | 814 | import-fresh@^3.2.1: 815 | version "3.3.0" 816 | resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" 817 | integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== 818 | dependencies: 819 | parent-module "^1.0.0" 820 | resolve-from "^4.0.0" 821 | 822 | import-meta-resolve@^4.1.0: 823 | version "4.1.0" 824 | resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz#f9db8bead9fafa61adb811db77a2bf22c5399706" 825 | integrity sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw== 826 | 827 | inflight@^1.0.4: 828 | version "1.0.6" 829 | resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" 830 | integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== 831 | dependencies: 832 | once "^1.3.0" 833 | wrappy "1" 834 | 835 | inherits@2: 836 | version "2.0.4" 837 | resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" 838 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 839 | 840 | is-binary-path@~2.1.0: 841 | version "2.1.0" 842 | resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" 843 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== 844 | dependencies: 845 | binary-extensions "^2.0.0" 846 | 847 | is-core-module@^2.11.0: 848 | version "2.12.1" 849 | resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz" 850 | integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== 851 | dependencies: 852 | has "^1.0.3" 853 | 854 | is-extglob@^2.1.1: 855 | version "2.1.1" 856 | resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" 857 | integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== 858 | 859 | is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: 860 | version "4.0.3" 861 | resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" 862 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== 863 | dependencies: 864 | is-extglob "^2.1.1" 865 | 866 | is-number@^7.0.0: 867 | version "7.0.0" 868 | resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" 869 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 870 | 871 | is-reference@^3.0.0, is-reference@^3.0.1: 872 | version "3.0.1" 873 | resolved "https://registry.npmjs.org/is-reference/-/is-reference-3.0.1.tgz" 874 | integrity sha512-baJJdQLiYaJdvFbJqXrcGv3WU3QCzBlUcI5QhbesIm6/xPsvmO+2CDoi/GMOFBQEQm+PXkwOPrp9KK5ozZsp2w== 875 | dependencies: 876 | "@types/estree" "*" 877 | 878 | jiti@^1.18.2: 879 | version "1.18.2" 880 | resolved "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz" 881 | integrity sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg== 882 | 883 | kleur@^4.1.5: 884 | version "4.1.5" 885 | resolved "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz" 886 | integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== 887 | 888 | lilconfig@^2.0.5, lilconfig@^2.1.0: 889 | version "2.1.0" 890 | resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz" 891 | integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== 892 | 893 | lines-and-columns@^1.1.6: 894 | version "1.2.4" 895 | resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" 896 | integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== 897 | 898 | locate-character@^3.0.0: 899 | version "3.0.0" 900 | resolved "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz" 901 | integrity sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA== 902 | 903 | magic-string@^0.27.0: 904 | version "0.27.0" 905 | resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz" 906 | integrity sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA== 907 | dependencies: 908 | "@jridgewell/sourcemap-codec" "^1.4.13" 909 | 910 | magic-string@^0.30.0: 911 | version "0.30.0" 912 | resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz" 913 | integrity sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ== 914 | dependencies: 915 | "@jridgewell/sourcemap-codec" "^1.4.13" 916 | 917 | magic-string@^0.30.10, magic-string@^0.30.5: 918 | version "0.30.17" 919 | resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" 920 | integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== 921 | dependencies: 922 | "@jridgewell/sourcemap-codec" "^1.5.0" 923 | 924 | make-error@^1.1.1: 925 | version "1.3.6" 926 | resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" 927 | integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== 928 | 929 | mdn-data@2.0.30: 930 | version "2.0.30" 931 | resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz" 932 | integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== 933 | 934 | merge2@^1.3.0: 935 | version "1.4.1" 936 | resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" 937 | integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== 938 | 939 | micromatch@^4.0.4, micromatch@^4.0.5: 940 | version "4.0.5" 941 | resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" 942 | integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== 943 | dependencies: 944 | braces "^3.0.2" 945 | picomatch "^2.3.1" 946 | 947 | mime-db@1.52.0: 948 | version "1.52.0" 949 | resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" 950 | integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== 951 | 952 | mime-types@^2.1.12: 953 | version "2.1.35" 954 | resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" 955 | integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== 956 | dependencies: 957 | mime-db "1.52.0" 958 | 959 | min-indent@^1.0.0: 960 | version "1.0.1" 961 | resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz" 962 | integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== 963 | 964 | minimatch@^3.0.4, minimatch@^3.1.1: 965 | version "3.1.2" 966 | resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" 967 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== 968 | dependencies: 969 | brace-expansion "^1.1.7" 970 | 971 | minimist@^1.2.0, minimist@^1.2.6: 972 | version "1.2.8" 973 | resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" 974 | integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== 975 | 976 | mkdirp@^0.5.1: 977 | version "0.5.6" 978 | resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" 979 | integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== 980 | dependencies: 981 | minimist "^1.2.6" 982 | 983 | mri@^1.1.0: 984 | version "1.2.0" 985 | resolved "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz" 986 | integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA== 987 | 988 | mrmime@^2.0.0: 989 | version "2.0.1" 990 | resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.1.tgz#bc3e87f7987853a54c9850eeb1f1078cd44adddc" 991 | integrity sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ== 992 | 993 | ms@2.1.2: 994 | version "2.1.2" 995 | resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" 996 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 997 | 998 | mz@^2.7.0: 999 | version "2.7.0" 1000 | resolved "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz" 1001 | integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== 1002 | dependencies: 1003 | any-promise "^1.0.0" 1004 | object-assign "^4.0.1" 1005 | thenify-all "^1.0.0" 1006 | 1007 | nanoid@^3.3.6: 1008 | version "3.3.6" 1009 | resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz" 1010 | integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== 1011 | 1012 | nanoid@^3.3.8: 1013 | version "3.3.8" 1014 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" 1015 | integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== 1016 | 1017 | node-releases@^2.0.12: 1018 | version "2.0.12" 1019 | resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz" 1020 | integrity sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ== 1021 | 1022 | normalize-path@^3.0.0, normalize-path@~3.0.0: 1023 | version "3.0.0" 1024 | resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" 1025 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== 1026 | 1027 | normalize-range@^0.1.2: 1028 | version "0.1.2" 1029 | resolved "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz" 1030 | integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== 1031 | 1032 | object-assign@^4.0.1: 1033 | version "4.1.1" 1034 | resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" 1035 | integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== 1036 | 1037 | object-hash@^3.0.0: 1038 | version "3.0.0" 1039 | resolved "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz" 1040 | integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== 1041 | 1042 | once@^1.3.0: 1043 | version "1.4.0" 1044 | resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" 1045 | integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== 1046 | dependencies: 1047 | wrappy "1" 1048 | 1049 | parent-module@^1.0.0: 1050 | version "1.0.1" 1051 | resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" 1052 | integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== 1053 | dependencies: 1054 | callsites "^3.0.0" 1055 | 1056 | path-is-absolute@^1.0.0: 1057 | version "1.0.1" 1058 | resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" 1059 | integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== 1060 | 1061 | path-parse@^1.0.7: 1062 | version "1.0.7" 1063 | resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" 1064 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== 1065 | 1066 | periscopic@^3.1.0: 1067 | version "3.1.0" 1068 | resolved "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz" 1069 | integrity sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw== 1070 | dependencies: 1071 | "@types/estree" "^1.0.0" 1072 | estree-walker "^3.0.0" 1073 | is-reference "^3.0.0" 1074 | 1075 | picocolors@^1.0.0: 1076 | version "1.0.0" 1077 | resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" 1078 | integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== 1079 | 1080 | picocolors@^1.1.1: 1081 | version "1.1.1" 1082 | resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" 1083 | integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== 1084 | 1085 | picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: 1086 | version "2.3.1" 1087 | resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" 1088 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 1089 | 1090 | pify@^2.3.0: 1091 | version "2.3.0" 1092 | resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" 1093 | integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== 1094 | 1095 | pirates@^4.0.1: 1096 | version "4.0.5" 1097 | resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz" 1098 | integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== 1099 | 1100 | postcss-import@^15.1.0: 1101 | version "15.1.0" 1102 | resolved "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz" 1103 | integrity sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew== 1104 | dependencies: 1105 | postcss-value-parser "^4.0.0" 1106 | read-cache "^1.0.0" 1107 | resolve "^1.1.7" 1108 | 1109 | postcss-js@^4.0.1: 1110 | version "4.0.1" 1111 | resolved "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz" 1112 | integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw== 1113 | dependencies: 1114 | camelcase-css "^2.0.1" 1115 | 1116 | postcss-load-config@^4.0.1: 1117 | version "4.0.1" 1118 | resolved "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz" 1119 | integrity sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA== 1120 | dependencies: 1121 | lilconfig "^2.0.5" 1122 | yaml "^2.1.1" 1123 | 1124 | postcss-nested@^6.0.1: 1125 | version "6.0.1" 1126 | resolved "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz" 1127 | integrity sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ== 1128 | dependencies: 1129 | postcss-selector-parser "^6.0.11" 1130 | 1131 | postcss-nesting@^11.2.2: 1132 | version "11.2.2" 1133 | resolved "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-11.2.2.tgz" 1134 | integrity sha512-aOTiUniAB1bcPE6GGiynWRa6PZFPhOTAm5q3q5cem6QeSijIHHkWr6gs65ukCZMXeak8yXeZVbBJET3VM+HlhA== 1135 | dependencies: 1136 | "@csstools/selector-specificity" "^2.0.0" 1137 | postcss-selector-parser "^6.0.10" 1138 | 1139 | postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.11: 1140 | version "6.0.13" 1141 | resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz" 1142 | integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ== 1143 | dependencies: 1144 | cssesc "^3.0.0" 1145 | util-deprecate "^1.0.2" 1146 | 1147 | postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0: 1148 | version "4.2.0" 1149 | resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" 1150 | integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== 1151 | 1152 | postcss@^8.4.23, postcss@^8.4.24: 1153 | version "8.4.24" 1154 | resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz" 1155 | integrity sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg== 1156 | dependencies: 1157 | nanoid "^3.3.6" 1158 | picocolors "^1.0.0" 1159 | source-map-js "^1.0.2" 1160 | 1161 | postcss@^8.4.43: 1162 | version "8.5.3" 1163 | resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.3.tgz#1463b6f1c7fb16fe258736cba29a2de35237eafb" 1164 | integrity sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A== 1165 | dependencies: 1166 | nanoid "^3.3.8" 1167 | picocolors "^1.1.1" 1168 | source-map-js "^1.2.1" 1169 | 1170 | prettier@^2.8.8: 1171 | version "2.8.8" 1172 | resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz" 1173 | integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== 1174 | 1175 | proxy-from-env@^1.1.0: 1176 | version "1.1.0" 1177 | resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" 1178 | integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== 1179 | 1180 | queue-microtask@^1.2.2: 1181 | version "1.2.3" 1182 | resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" 1183 | integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== 1184 | 1185 | read-cache@^1.0.0: 1186 | version "1.0.0" 1187 | resolved "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz" 1188 | integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== 1189 | dependencies: 1190 | pify "^2.3.0" 1191 | 1192 | readdirp@~3.6.0: 1193 | version "3.6.0" 1194 | resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" 1195 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== 1196 | dependencies: 1197 | picomatch "^2.2.1" 1198 | 1199 | resolve-from@^4.0.0: 1200 | version "4.0.0" 1201 | resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" 1202 | integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== 1203 | 1204 | resolve@^1.1.7, resolve@^1.22.2: 1205 | version "1.22.2" 1206 | resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz" 1207 | integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== 1208 | dependencies: 1209 | is-core-module "^2.11.0" 1210 | path-parse "^1.0.7" 1211 | supports-preserve-symlinks-flag "^1.0.0" 1212 | 1213 | reusify@^1.0.4: 1214 | version "1.0.4" 1215 | resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" 1216 | integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== 1217 | 1218 | rimraf@^2.5.2: 1219 | version "2.7.1" 1220 | resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" 1221 | integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== 1222 | dependencies: 1223 | glob "^7.1.3" 1224 | 1225 | rollup@^4.20.0: 1226 | version "4.34.9" 1227 | resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.34.9.tgz#e1eb397856476778aeb6ac2ac3d09b2ce177a558" 1228 | integrity sha512-nF5XYqWWp9hx/LrpC8sZvvvmq0TeTjQgaZHYmAgwysT9nh8sWnZhBnM8ZyVbbJFIQBLwHDNoMqsBZBbUo4U8sQ== 1229 | dependencies: 1230 | "@types/estree" "1.0.6" 1231 | optionalDependencies: 1232 | "@rollup/rollup-android-arm-eabi" "4.34.9" 1233 | "@rollup/rollup-android-arm64" "4.34.9" 1234 | "@rollup/rollup-darwin-arm64" "4.34.9" 1235 | "@rollup/rollup-darwin-x64" "4.34.9" 1236 | "@rollup/rollup-freebsd-arm64" "4.34.9" 1237 | "@rollup/rollup-freebsd-x64" "4.34.9" 1238 | "@rollup/rollup-linux-arm-gnueabihf" "4.34.9" 1239 | "@rollup/rollup-linux-arm-musleabihf" "4.34.9" 1240 | "@rollup/rollup-linux-arm64-gnu" "4.34.9" 1241 | "@rollup/rollup-linux-arm64-musl" "4.34.9" 1242 | "@rollup/rollup-linux-loongarch64-gnu" "4.34.9" 1243 | "@rollup/rollup-linux-powerpc64le-gnu" "4.34.9" 1244 | "@rollup/rollup-linux-riscv64-gnu" "4.34.9" 1245 | "@rollup/rollup-linux-s390x-gnu" "4.34.9" 1246 | "@rollup/rollup-linux-x64-gnu" "4.34.9" 1247 | "@rollup/rollup-linux-x64-musl" "4.34.9" 1248 | "@rollup/rollup-win32-arm64-msvc" "4.34.9" 1249 | "@rollup/rollup-win32-ia32-msvc" "4.34.9" 1250 | "@rollup/rollup-win32-x64-msvc" "4.34.9" 1251 | fsevents "~2.3.2" 1252 | 1253 | run-parallel@^1.1.9: 1254 | version "1.2.0" 1255 | resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" 1256 | integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== 1257 | dependencies: 1258 | queue-microtask "^1.2.2" 1259 | 1260 | sade@^1.7.4, sade@^1.8.1: 1261 | version "1.8.1" 1262 | resolved "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz" 1263 | integrity sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A== 1264 | dependencies: 1265 | mri "^1.1.0" 1266 | 1267 | sander@^0.5.0: 1268 | version "0.5.1" 1269 | resolved "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz" 1270 | integrity sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA== 1271 | dependencies: 1272 | es6-promise "^3.1.2" 1273 | graceful-fs "^4.1.3" 1274 | mkdirp "^0.5.1" 1275 | rimraf "^2.5.2" 1276 | 1277 | set-cookie-parser@^2.6.0: 1278 | version "2.6.0" 1279 | resolved "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz" 1280 | integrity sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ== 1281 | 1282 | sirv@^3.0.0: 1283 | version "3.0.1" 1284 | resolved "https://registry.yarnpkg.com/sirv/-/sirv-3.0.1.tgz#32a844794655b727f9e2867b777e0060fbe07bf3" 1285 | integrity sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A== 1286 | dependencies: 1287 | "@polka/url" "^1.0.0-next.24" 1288 | mrmime "^2.0.0" 1289 | totalist "^3.0.0" 1290 | 1291 | sorcery@^0.11.0: 1292 | version "0.11.0" 1293 | resolved "https://registry.npmjs.org/sorcery/-/sorcery-0.11.0.tgz" 1294 | integrity sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw== 1295 | dependencies: 1296 | "@jridgewell/sourcemap-codec" "^1.4.14" 1297 | buffer-crc32 "^0.2.5" 1298 | minimist "^1.2.0" 1299 | sander "^0.5.0" 1300 | 1301 | source-map-js@^1.0.1, source-map-js@^1.0.2: 1302 | version "1.0.2" 1303 | resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz" 1304 | integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== 1305 | 1306 | source-map-js@^1.2.1: 1307 | version "1.2.1" 1308 | resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" 1309 | integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== 1310 | 1311 | strip-indent@^3.0.0: 1312 | version "3.0.0" 1313 | resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz" 1314 | integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== 1315 | dependencies: 1316 | min-indent "^1.0.0" 1317 | 1318 | sucrase@^3.32.0: 1319 | version "3.32.0" 1320 | resolved "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz" 1321 | integrity sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ== 1322 | dependencies: 1323 | "@jridgewell/gen-mapping" "^0.3.2" 1324 | commander "^4.0.0" 1325 | glob "7.1.6" 1326 | lines-and-columns "^1.1.6" 1327 | mz "^2.7.0" 1328 | pirates "^4.0.1" 1329 | ts-interface-checker "^0.1.9" 1330 | 1331 | supports-preserve-symlinks-flag@^1.0.0: 1332 | version "1.0.0" 1333 | resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" 1334 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== 1335 | 1336 | svelte-check@^3.4.4: 1337 | version "3.4.4" 1338 | resolved "https://registry.npmjs.org/svelte-check/-/svelte-check-3.4.4.tgz" 1339 | integrity sha512-Uys9+R65cj8TmP8f5UpS7B2xKpNLYNxEWJsA5ZoKcWq/uwvABFF7xS6iPQGLoa7hxz0DS6xU60YFpmq06E4JxA== 1340 | dependencies: 1341 | "@jridgewell/trace-mapping" "^0.3.17" 1342 | chokidar "^3.4.1" 1343 | fast-glob "^3.2.7" 1344 | import-fresh "^3.2.1" 1345 | picocolors "^1.0.0" 1346 | sade "^1.7.4" 1347 | svelte-preprocess "^5.0.3" 1348 | typescript "^5.0.3" 1349 | 1350 | svelte-hmr@^0.16.0: 1351 | version "0.16.0" 1352 | resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.16.0.tgz#9f345b7d1c1662f1613747ed7e82507e376c1716" 1353 | integrity sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA== 1354 | 1355 | svelte-infinite-scroll@^2.0.1: 1356 | version "2.0.1" 1357 | resolved "https://registry.npmjs.org/svelte-infinite-scroll/-/svelte-infinite-scroll-2.0.1.tgz" 1358 | integrity sha512-goTHCfOHRDCs8C5MeSuIc6LlAQ8zVQ+M4Y3LyvrDjx5rqSSxSrdCuQwIyWYNcO6j6/mnqRro3QB64ClBzfn+Wg== 1359 | 1360 | svelte-preprocess@^5.0.3: 1361 | version "5.0.4" 1362 | resolved "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.0.4.tgz" 1363 | integrity sha512-ABia2QegosxOGsVlsSBJvoWeXy1wUKSfF7SWJdTjLAbx/Y3SrVevvvbFNQqrSJw89+lNSsM58SipmZJ5SRi5iw== 1364 | dependencies: 1365 | "@types/pug" "^2.0.6" 1366 | detect-indent "^6.1.0" 1367 | magic-string "^0.27.0" 1368 | sorcery "^0.11.0" 1369 | strip-indent "^3.0.0" 1370 | 1371 | svelte-preprocess@^5.1.1: 1372 | version "5.1.1" 1373 | resolved "https://registry.yarnpkg.com/svelte-preprocess/-/svelte-preprocess-5.1.1.tgz#53d7107c2e8b307afd4418e06239177c4de12025" 1374 | integrity sha512-p/Dp4hmrBW5mrCCq29lEMFpIJT2FZsRlouxEc5qpbOmXRbaFs7clLs8oKPwD3xCFyZfv1bIhvOzpQkhMEVQdMw== 1375 | dependencies: 1376 | "@types/pug" "^2.0.6" 1377 | detect-indent "^6.1.0" 1378 | magic-string "^0.27.0" 1379 | sorcery "^0.11.0" 1380 | strip-indent "^3.0.0" 1381 | 1382 | svelte@^4.0.0: 1383 | version "4.0.0" 1384 | resolved "https://registry.npmjs.org/svelte/-/svelte-4.0.0.tgz" 1385 | integrity sha512-+yCYu3AEUu9n91dnQNGIbnVp8EmNQtuF/YImW4+FTXRHard7NMo+yTsWzggPAbj3fUEJ1FBJLkql/jkp6YB5pg== 1386 | dependencies: 1387 | "@ampproject/remapping" "^2.2.1" 1388 | "@jridgewell/sourcemap-codec" "^1.4.15" 1389 | "@jridgewell/trace-mapping" "^0.3.18" 1390 | acorn "^8.8.2" 1391 | aria-query "^5.2.1" 1392 | axobject-query "^3.2.1" 1393 | code-red "^1.0.3" 1394 | css-tree "^2.3.1" 1395 | estree-walker "^3.0.3" 1396 | is-reference "^3.0.1" 1397 | locate-character "^3.0.0" 1398 | magic-string "^0.30.0" 1399 | periscopic "^3.1.0" 1400 | 1401 | tailwindcss@^3.3.2: 1402 | version "3.3.2" 1403 | resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz" 1404 | integrity sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w== 1405 | dependencies: 1406 | "@alloc/quick-lru" "^5.2.0" 1407 | arg "^5.0.2" 1408 | chokidar "^3.5.3" 1409 | didyoumean "^1.2.2" 1410 | dlv "^1.1.3" 1411 | fast-glob "^3.2.12" 1412 | glob-parent "^6.0.2" 1413 | is-glob "^4.0.3" 1414 | jiti "^1.18.2" 1415 | lilconfig "^2.1.0" 1416 | micromatch "^4.0.5" 1417 | normalize-path "^3.0.0" 1418 | object-hash "^3.0.0" 1419 | picocolors "^1.0.0" 1420 | postcss "^8.4.23" 1421 | postcss-import "^15.1.0" 1422 | postcss-js "^4.0.1" 1423 | postcss-load-config "^4.0.1" 1424 | postcss-nested "^6.0.1" 1425 | postcss-selector-parser "^6.0.11" 1426 | postcss-value-parser "^4.2.0" 1427 | resolve "^1.22.2" 1428 | sucrase "^3.32.0" 1429 | 1430 | thenify-all@^1.0.0: 1431 | version "1.6.0" 1432 | resolved "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz" 1433 | integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== 1434 | dependencies: 1435 | thenify ">= 3.1.0 < 4" 1436 | 1437 | "thenify@>= 3.1.0 < 4": 1438 | version "3.3.1" 1439 | resolved "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz" 1440 | integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== 1441 | dependencies: 1442 | any-promise "^1.0.0" 1443 | 1444 | to-regex-range@^5.0.1: 1445 | version "5.0.1" 1446 | resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" 1447 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 1448 | dependencies: 1449 | is-number "^7.0.0" 1450 | 1451 | totalist@^3.0.0: 1452 | version "3.0.1" 1453 | resolved "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz" 1454 | integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== 1455 | 1456 | ts-interface-checker@^0.1.9: 1457 | version "0.1.13" 1458 | resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz" 1459 | integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== 1460 | 1461 | ts-node@^10.9.1: 1462 | version "10.9.1" 1463 | resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz" 1464 | integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== 1465 | dependencies: 1466 | "@cspotcode/source-map-support" "^0.8.0" 1467 | "@tsconfig/node10" "^1.0.7" 1468 | "@tsconfig/node12" "^1.0.7" 1469 | "@tsconfig/node14" "^1.0.0" 1470 | "@tsconfig/node16" "^1.0.2" 1471 | acorn "^8.4.1" 1472 | acorn-walk "^8.1.1" 1473 | arg "^4.1.0" 1474 | create-require "^1.1.0" 1475 | diff "^4.0.1" 1476 | make-error "^1.1.1" 1477 | v8-compile-cache-lib "^3.0.1" 1478 | yn "3.1.1" 1479 | 1480 | tslib@^2.5.3: 1481 | version "2.5.3" 1482 | resolved "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz" 1483 | integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== 1484 | 1485 | typescript@^5.0.3, typescript@^5.1.3: 1486 | version "5.1.3" 1487 | resolved "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz" 1488 | integrity sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw== 1489 | 1490 | update-browserslist-db@^1.0.11: 1491 | version "1.0.11" 1492 | resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz" 1493 | integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== 1494 | dependencies: 1495 | escalade "^3.1.1" 1496 | picocolors "^1.0.0" 1497 | 1498 | util-deprecate@^1.0.2: 1499 | version "1.0.2" 1500 | resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" 1501 | integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== 1502 | 1503 | v8-compile-cache-lib@^3.0.1: 1504 | version "3.0.1" 1505 | resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" 1506 | integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== 1507 | 1508 | vite@^5.0.0: 1509 | version "5.4.14" 1510 | resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.14.tgz#ff8255edb02134df180dcfca1916c37a6abe8408" 1511 | integrity sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA== 1512 | dependencies: 1513 | esbuild "^0.21.3" 1514 | postcss "^8.4.43" 1515 | rollup "^4.20.0" 1516 | optionalDependencies: 1517 | fsevents "~2.3.3" 1518 | 1519 | vitefu@^0.2.5: 1520 | version "0.2.5" 1521 | resolved "https://registry.yarnpkg.com/vitefu/-/vitefu-0.2.5.tgz#c1b93c377fbdd3e5ddd69840ea3aa70b40d90969" 1522 | integrity sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q== 1523 | 1524 | wrappy@1: 1525 | version "1.0.2" 1526 | resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" 1527 | integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== 1528 | 1529 | yaml@^2.1.1: 1530 | version "2.3.1" 1531 | resolved "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz" 1532 | integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== 1533 | 1534 | yarn@^1.22.21: 1535 | version "1.22.21" 1536 | resolved "https://registry.npmjs.org/yarn/-/yarn-1.22.21.tgz" 1537 | integrity sha512-ynXaJsADJ9JiZ84zU25XkPGOvVMmZ5b7tmTSpKURYwgELdjucAOydqIOrOfTxVYcNXe91xvLZwcRh68SR3liCg== 1538 | 1539 | yn@3.1.1: 1540 | version "3.1.1" 1541 | resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" 1542 | integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== 1543 | --------------------------------------------------------------------------------