From fa9a1fdef96512d0b94f1268e48d378114efd2f4 Mon Sep 17 00:00:00 2001 From: Brunwo Date: Fri, 18 Oct 2024 01:23:17 +0200 Subject: [PATCH] big update : moved to typescript and modulariaztion, added some tests (not all work) rergessions : menus, UI.. --- audiocache.d.ts | 0 e2e/localstorage.spec.js | 22 + e2e/share-target.spec.js | 43 +- package-lock.json | 1146 +++++++++++++++++++++++++++++++++++--- package.json | 11 +- public/manifest.json | 3 +- public/offline.html | 37 ++ readme.md | 4 + script.js | 318 ++--------- service-worker.js | 77 ++- src/api.d.ts | 14 + src/api.js | 100 ++++ src/api.test.js | 39 ++ src/api.test.ts | 42 ++ src/audioCache.js | 68 +++ src/audioCache.test.js | 39 ++ src/audioCache.test.ts | 39 ++ src/audioPlayer.js | 99 ++++ src/audioPlayer.test.js | 41 ++ src/audioPlayer.test.ts | 41 ++ src/audiocache.d.ts | 14 + src/audioplayer.d.ts | 23 + src/utils.d.ts | 2 + src/utils.js | 16 + src/utils.test.js | 17 + src/utils.test.ts | 17 + tsconfig.json | 26 + vitest.config.js | 7 + 28 files changed, 1904 insertions(+), 401 deletions(-) create mode 100644 audiocache.d.ts create mode 100644 e2e/localstorage.spec.js create mode 100644 public/offline.html create mode 100644 readme.md create mode 100644 src/api.d.ts create mode 100644 src/api.js create mode 100644 src/api.test.js create mode 100644 src/api.test.ts create mode 100644 src/audioCache.js create mode 100644 src/audioCache.test.js create mode 100644 src/audioCache.test.ts create mode 100644 src/audioPlayer.js create mode 100644 src/audioPlayer.test.js create mode 100644 src/audioPlayer.test.ts create mode 100644 src/audiocache.d.ts create mode 100644 src/audioplayer.d.ts create mode 100644 src/utils.d.ts create mode 100644 src/utils.js create mode 100644 src/utils.test.js create mode 100644 src/utils.test.ts create mode 100644 tsconfig.json create mode 100644 vitest.config.js diff --git a/audiocache.d.ts b/audiocache.d.ts new file mode 100644 index 0000000..e69de29 diff --git a/e2e/localstorage.spec.js b/e2e/localstorage.spec.js new file mode 100644 index 0000000..1bf222c --- /dev/null +++ b/e2e/localstorage.spec.js @@ -0,0 +1,22 @@ +import { test, expect } from '@playwright/test'; + +test('Simulate localStorage entry in PWA', async ({ page }) => { + await page.evaluate(() => { + localStorage.setItem('audioCache', JSON.stringify({ + 'https://example.com/audio': { + audioUrl: 'https://example.com/audio.mp3', + transcription: 'Test transcription', + lastPosition: 0 + } + })); + }); + + await page.goto('http://localhost:5173/listen-UI/'); + + const audioCache = await page.evaluate(() => localStorage.getItem('audioCache')); + expect(audioCache).toBeTruthy(); + + const historyList = await page.locator('#historyList'); + const historyItems = await historyList.locator('li').count(); + expect(historyItems).toBe(1); +}); diff --git a/e2e/share-target.spec.js b/e2e/share-target.spec.js index 90a4556..7eb3ec5 100644 --- a/e2e/share-target.spec.js +++ b/e2e/share-target.spec.js @@ -1,4 +1,4 @@ -const { test, expect } = require('@playwright/test'); +import { test, expect } from '@playwright/test'; // Add a helper function to check if the server is up async function isServerUp(page) { @@ -45,4 +45,45 @@ test('share-target redirects to index.html when no URL is shared', async ({ page // Check if we've been redirected to the index.html page expect(page.url()).toContain(`index.html`); +}); + +test('Share target functionality', async ({ page }) => { + console.log('Starting Share target functionality test'); + + try { + // Simulate sharing a URL to the PWA + await page.goto('http://localhost:5173/listen-UI/share-target?url=https://example.com/shared'); + console.log('Navigated to share target URL'); + + // Wait for the page to load and process the shared URL + await page.waitForLoadState('networkidle'); + console.log('Page loaded'); + + // Check if the shared URL is processed : we cant test that at the moment as processing takes a lot of time + // const sharedUrlProcessed = await page.evaluate(() => { + // const lastProcessedUrl = localStorage.getItem('lastProcessedUrl'); + // console.log('Last processed URL:', lastProcessedUrl); + // return lastProcessedUrl === 'https://example.com/shared'; + // }); + // expect(sharedUrlProcessed).toBe(true); + // console.log('Shared URL processed correctly'); + + // Check if the audio player is visible + const audioPlayer = await page.locator('#player'); + await expect(audioPlayer).toBeVisible({ timeout: 10000 }); + console.log('Audio player is visible'); + + // Additional checks to ensure the page has loaded correctly + const pageTitle = await page.title(); + expect(pageTitle).not.toBe(''); + console.log('Page title:', pageTitle); + + const bodyContent = await page.textContent('body'); + expect(bodyContent).not.toBe(''); + console.log('Body content length:', bodyContent.length); + + } catch (error) { + console.error('Test failed with error:', error); + throw error; + } }); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1ec48c0..5f6f5a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,17 +9,19 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "vite": "^5.4.8" + "@gradio/client": "^1.6.0-beta.3" }, "devDependencies": { - "@gradio/client": "^1.6.0-beta.3" + "@playwright/test": "^1.35.0", + "jsdom": "^22.1.0", + "vite": "^5.4.8", + "vitest": "^0.34.0" } }, "node_modules/@bundled-es-modules/cookie": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.0.tgz", "integrity": "sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==", - "dev": true, "dependencies": { "cookie": "^0.5.0" } @@ -28,7 +30,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", - "dev": true, "dependencies": { "statuses": "^2.0.1" } @@ -37,7 +38,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/@bundled-es-modules/tough-cookie/-/tough-cookie-0.1.6.tgz", "integrity": "sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==", - "dev": true, "dependencies": { "@types/tough-cookie": "^4.0.5", "tough-cookie": "^4.1.4" @@ -50,6 +50,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "aix" @@ -65,6 +66,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "android" @@ -80,6 +82,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "android" @@ -95,6 +98,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "android" @@ -110,6 +114,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -125,6 +130,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -140,6 +146,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -155,6 +162,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -170,6 +178,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -185,6 +194,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -200,6 +210,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "linux" @@ -215,6 +226,7 @@ "cpu": [ "loong64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -230,6 +242,7 @@ "cpu": [ "mips64el" ], + "dev": true, "optional": true, "os": [ "linux" @@ -245,6 +258,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -260,6 +274,7 @@ "cpu": [ "riscv64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -275,6 +290,7 @@ "cpu": [ "s390x" ], + "dev": true, "optional": true, "os": [ "linux" @@ -290,6 +306,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -305,6 +322,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "netbsd" @@ -320,6 +338,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "openbsd" @@ -335,6 +354,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "sunos" @@ -350,6 +370,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -365,6 +386,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "win32" @@ -380,6 +402,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -392,7 +415,6 @@ "version": "1.6.0-beta.3", "resolved": "https://registry.npmjs.org/@gradio/client/-/client-1.6.0-beta.3.tgz", "integrity": "sha512-mJZVQ4UpfrSu71J4SkbSrpnbRotmB5ziy4fg7zqZhqXwXGZM3cHR9fUGkTFM0eXYnsaeBoiqn+1bcUh32Zcgkg==", - "dev": true, "dependencies": { "@types/eventsource": "^1.1.15", "bufferutil": "^4.0.7", @@ -412,7 +434,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.2.0.tgz", "integrity": "sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==", - "dev": true, "dependencies": { "@inquirer/core": "^9.1.0", "@inquirer/type": "^1.5.3" @@ -425,7 +446,6 @@ "version": "9.2.1", "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz", "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==", - "dev": true, "dependencies": { "@inquirer/figures": "^1.0.6", "@inquirer/type": "^2.0.0", @@ -448,7 +468,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz", "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==", - "dev": true, "dependencies": { "mute-stream": "^1.0.0" }, @@ -460,7 +479,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.6.tgz", "integrity": "sha512-yfZzps3Cso2UbM7WlxKwZQh2Hs6plrbjs1QnzQDZhK2DgyCo6D8AaHps9olkNcUFlcYERMqU3uJSp1gmy3s/qQ==", - "dev": true, "engines": { "node": ">=18" } @@ -469,7 +487,6 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.5.tgz", "integrity": "sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==", - "dev": true, "dependencies": { "mute-stream": "^1.0.0" }, @@ -477,11 +494,28 @@ "node": ">=18" } }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, "node_modules/@mswjs/interceptors": { "version": "0.35.9", "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.35.9.tgz", "integrity": "sha512-SSnyl/4ni/2ViHKkiZb8eajA/eN1DNFaHjhGiLUdZvDz6PKF4COSf/17xqSz64nOo2Ia29SA6B2KNCsyCbVmaQ==", - "dev": true, "dependencies": { "@open-draft/deferred-promise": "^2.2.0", "@open-draft/logger": "^0.3.0", @@ -497,14 +531,12 @@ "node_modules/@open-draft/deferred-promise": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", - "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", - "dev": true + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==" }, "node_modules/@open-draft/logger": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", - "dev": true, "dependencies": { "is-node-process": "^1.2.0", "outvariant": "^1.4.0" @@ -513,8 +545,22 @@ "node_modules/@open-draft/until": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", - "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", - "dev": true + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==" + }, + "node_modules/@playwright/test": { + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.2.tgz", + "integrity": "sha512-jTXRsoSPONAs8Za9QEQdyjFn+0ZQFjCiIztAIF6bi1HqhBzG9Ma7g1WotyiGqFSBRZjIEqMdT8RUlbk1QVhzCQ==", + "dev": true, + "dependencies": { + "playwright": "1.47.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.24.0", @@ -523,6 +569,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "android" @@ -535,6 +582,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "android" @@ -547,6 +595,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -559,6 +608,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -571,6 +621,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -583,6 +634,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -595,6 +647,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -607,6 +660,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -619,6 +673,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -631,6 +686,7 @@ "cpu": [ "riscv64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -643,6 +699,7 @@ "cpu": [ "s390x" ], + "dev": true, "optional": true, "os": [ "linux" @@ -655,6 +712,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -667,6 +725,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -679,6 +738,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -691,6 +751,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "win32" @@ -703,33 +764,62 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" ] }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/chai": { + "version": "4.3.20", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.20.tgz", + "integrity": "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==", + "dev": true + }, + "node_modules/@types/chai-subset": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.5.tgz", + "integrity": "sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==", + "dev": true, + "dependencies": { + "@types/chai": "*" + } + }, "node_modules/@types/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", - "dev": true + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true }, "node_modules/@types/eventsource": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/@types/eventsource/-/eventsource-1.1.15.tgz", - "integrity": "sha512-XQmGcbnxUNa06HR3VBVkc9+A2Vpi9ZyLJcdS5dwaQQ/4ZMWFO+5c90FnMUpbtMZwB/FChoYHwuVg8TvkECacTA==", - "dev": true + "integrity": "sha512-XQmGcbnxUNa06HR3VBVkc9+A2Vpi9ZyLJcdS5dwaQQ/4ZMWFO+5c90FnMUpbtMZwB/FChoYHwuVg8TvkECacTA==" }, "node_modules/@types/mute-stream": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", - "dev": true, "dependencies": { "@types/node": "*" } @@ -738,7 +828,6 @@ "version": "22.7.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.4.tgz", "integrity": "sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==", - "devOptional": true, "dependencies": { "undici-types": "~6.19.2" } @@ -746,26 +835,133 @@ "node_modules/@types/statuses": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.5.tgz", - "integrity": "sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==", - "dev": true + "integrity": "sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==" }, "node_modules/@types/tough-cookie": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", - "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", - "dev": true + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==" }, "node_modules/@types/wrap-ansi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", - "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==" + }, + "node_modules/@vitest/expect": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.34.6.tgz", + "integrity": "sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==", + "dev": true, + "dependencies": { + "@vitest/spy": "0.34.6", + "@vitest/utils": "0.34.6", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.34.6.tgz", + "integrity": "sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==", + "dev": true, + "dependencies": { + "@vitest/utils": "0.34.6", + "p-limit": "^4.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.34.6.tgz", + "integrity": "sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.1", + "pathe": "^1.1.1", + "pretty-format": "^29.5.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.34.6.tgz", + "integrity": "sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==", + "dev": true, + "dependencies": { + "tinyspy": "^2.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.6.tgz", + "integrity": "sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==", + "dev": true, + "dependencies": { + "diff-sequences": "^29.4.3", + "loupe": "^2.3.6", + "pretty-format": "^29.5.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", "dev": true }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, "dependencies": { "type-fest": "^0.21.3" }, @@ -780,7 +976,6 @@ "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, "engines": { "node": ">=10" }, @@ -792,7 +987,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -801,7 +995,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -812,11 +1005,25 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, "node_modules/bufferutil": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", - "dev": true, "hasInstallScript": true, "dependencies": { "node-gyp-build": "^4.3.0" @@ -825,11 +1032,37 @@ "node": ">=6.14.2" } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -841,11 +1074,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, "node_modules/cli-width": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "dev": true, "engines": { "node": ">= 12" } @@ -854,7 +1098,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -868,7 +1111,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -885,7 +1127,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -896,28 +1137,148 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", "dev": true }, "node_modules/cookie": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "dev": true, "engines": { "node": ">= 0.6" } }, + "node_modules/cssstyle": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz", + "integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==", + "dev": true, + "dependencies": { + "rrweb-cssom": "^0.6.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/data-urls": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz", + "integrity": "sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^12.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } }, "node_modules/esbuild": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -955,7 +1316,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "engines": { "node": ">=6" } @@ -964,7 +1324,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", - "dev": true, "engines": { "node": ">=12.0.0" } @@ -972,13 +1331,27 @@ "node_modules/fetch-event-stream": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/fetch-event-stream/-/fetch-event-stream-0.1.5.tgz", - "integrity": "sha512-V1PWovkspxQfssq/NnxoEyQo1DV+MRK/laPuPblIZmSjMN8P5u46OhlFQznSr9p/t0Sp8Uc6SbM3yCMfr0KU8g==", - "dev": true + "integrity": "sha512-V1PWovkspxQfssq/NnxoEyQo1DV+MRK/laPuPblIZmSjMN8P5u46OhlFQznSr9p/t0Sp8Uc6SbM3yCMfr0KU8g==" + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -992,16 +1365,23 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/graphql": { + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/graphql": { "version": "16.9.0", "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", - "dev": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -1010,7 +1390,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -1018,14 +1397,63 @@ "node_modules/headers-polyfill": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", - "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", - "dev": true + "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==" + }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -1033,14 +1461,129 @@ "node_modules/is-node-process": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", - "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==" + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "node_modules/jsdom": { + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz", + "integrity": "sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "cssstyle": "^3.0.0", + "data-urls": "^4.0.0", + "decimal.js": "^10.4.3", + "domexception": "^4.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.4", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.6.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^12.0.1", + "ws": "^8.13.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/local-pkg": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", + "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/magic-string": { + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mlly": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.2.tgz", + "integrity": "sha512-tN3dvVHYVz4DhSXinXIk7u9syPYaJvio118uomkovAtWBT+RdbP6Lfh/5Lvo519YMmwBafwlh20IPTXIStscpA==", + "dev": true, + "dependencies": { + "acorn": "^8.12.1", + "pathe": "^1.1.2", + "pkg-types": "^1.2.0", + "ufo": "^1.5.4" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "node_modules/msw": { "version": "2.4.9", "resolved": "https://registry.npmjs.org/msw/-/msw-2.4.9.tgz", "integrity": "sha512-1m8xccT6ipN4PTqLinPwmzhxQREuxaEJYdx4nIbggxP8aM7r1e71vE7RtOUSQoAm1LydjGfZKy7370XD/tsuYg==", - "dev": true, "hasInstallScript": true, "dependencies": { "@bundled-es-modules/cookie": "^2.0.0", @@ -1083,7 +1626,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", - "dev": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -1092,6 +1634,7 @@ "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, "funding": [ { "type": "github", @@ -1109,34 +1652,136 @@ "version": "4.8.2", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", - "dev": true, "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, + "node_modules/nwsapi": { + "version": "2.2.13", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.13.tgz", + "integrity": "sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==", + "dev": true + }, "node_modules/outvariant": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", - "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", - "dev": true + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==" + }, + "node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dev": true, + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } }, "node_modules/path-to-regexp": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", - "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==" + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", "dev": true }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/picocolors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "dev": true + }, + "node_modules/pkg-types": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.0.tgz", + "integrity": "sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==", + "dev": true, + "dependencies": { + "confbox": "^0.1.7", + "mlly": "^1.7.1", + "pathe": "^1.1.2" + } + }, + "node_modules/playwright": { + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.2.tgz", + "integrity": "sha512-nx1cLMmQWqmA3UsnjaaokyoUpdVaaDhJhMoxX2qj3McpjnsqFHs516QAKYhqHAgOP+oCFTEOCOAaD1RgD/RQfA==", + "dev": true, + "dependencies": { + "playwright-core": "1.47.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.2.tgz", + "integrity": "sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, "node_modules/postcss": { "version": "8.4.47", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -1160,17 +1805,41 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "engines": { "node": ">=6" } @@ -1178,14 +1847,18 @@ "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -1193,13 +1866,13 @@ "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, "node_modules/rollup": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", + "dev": true, "dependencies": { "@types/estree": "1.0.6" }, @@ -1230,20 +1903,48 @@ "fsevents": "~2.3.2" } }, + "node_modules/rrweb-cssom": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", + "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", + "dev": true + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/semiver": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/semiver/-/semiver-1.1.0.tgz", "integrity": "sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==", - "dev": true, "engines": { "node": ">=6" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "engines": { "node": ">=14" }, @@ -1255,30 +1956,40 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, "engines": { "node": ">= 0.8" } }, + "node_modules/std-env": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", + "dev": true + }, "node_modules/strict-event-emitter": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", - "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", - "dev": true + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==" }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -1292,7 +2003,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -1300,11 +2010,22 @@ "node": ">=8" } }, + "node_modules/strip-literal": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.3.0.tgz", + "integrity": "sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==", + "dev": true, + "dependencies": { + "acorn": "^8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -1312,17 +2033,45 @@ "node": ">=8" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, "node_modules/textlinestream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/textlinestream/-/textlinestream-1.1.1.tgz", - "integrity": "sha512-iBHbi7BQxrFmwZUQJsT0SjNzlLLsXhvW/kg7EyOMVMBIrlnj/qYofwo1LVLZi+3GbUEo96Iu2eqToI2+lZoAEQ==", + "integrity": "sha512-iBHbi7BQxrFmwZUQJsT0SjNzlLLsXhvW/kg7EyOMVMBIrlnj/qYofwo1LVLZi+3GbUEo96Iu2eqToI2+lZoAEQ==" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true }, + "node_modules/tinypool": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.7.0.tgz", + "integrity": "sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tough-cookie": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", - "dev": true, "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", @@ -1333,11 +2082,31 @@ "node": ">=6" } }, + "node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "dev": true, + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/type-fest": { "version": "4.26.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==", - "dev": true, "engines": { "node": ">=16" }, @@ -1349,7 +2118,6 @@ "version": "5.6.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", - "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -1358,17 +2126,21 @@ "node": ">=14.17" } }, + "node_modules/ufo": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", + "dev": true + }, "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "devOptional": true + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, "node_modules/universalify": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, "engines": { "node": ">= 4.0.0" } @@ -1377,7 +2149,6 @@ "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" @@ -1387,6 +2158,7 @@ "version": "5.4.8", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", + "dev": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -1441,11 +2213,181 @@ } } }, + "node_modules/vite-node": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.34.6.tgz", + "integrity": "sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==", + "dev": true, + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "mlly": "^1.4.0", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": ">=v14.18.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.6.tgz", + "integrity": "sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==", + "dev": true, + "dependencies": { + "@types/chai": "^4.3.5", + "@types/chai-subset": "^1.3.3", + "@types/node": "*", + "@vitest/expect": "0.34.6", + "@vitest/runner": "0.34.6", + "@vitest/snapshot": "0.34.6", + "@vitest/spy": "0.34.6", + "@vitest/utils": "0.34.6", + "acorn": "^8.9.0", + "acorn-walk": "^8.2.0", + "cac": "^6.7.14", + "chai": "^4.3.10", + "debug": "^4.3.4", + "local-pkg": "^0.4.3", + "magic-string": "^0.30.1", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.3.3", + "strip-literal": "^1.0.1", + "tinybench": "^2.5.0", + "tinypool": "^0.7.0", + "vite": "^3.1.0 || ^4.0.0 || ^5.0.0-0", + "vite-node": "0.34.6", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": ">=v14.18.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@vitest/browser": "*", + "@vitest/ui": "*", + "happy-dom": "*", + "jsdom": "*", + "playwright": "*", + "safaridriver": "*", + "webdriverio": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "playwright": { + "optional": true + }, + "safaridriver": { + "optional": true + }, + "webdriverio": { + "optional": true + } + } + }, + "node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz", + "integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==", + "dev": true, + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -1459,7 +2401,6 @@ "version": "8.18.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "dev": true, "engines": { "node": ">=10.0.0" }, @@ -1476,11 +2417,25 @@ } } }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "engines": { "node": ">=10" } @@ -1489,7 +2444,6 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -1507,16 +2461,26 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, "engines": { "node": ">=12" } }, + "node_modules/yocto-queue": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/yoctocolors-cjs": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", - "dev": true, "engines": { "node": ">=18" }, diff --git a/package.json b/package.json index 7de0700..d15877b 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "build": "vite build", "serve": "vite preview", "test": "vitest", - "test:e2e": "playwright test" + "test:e2e": "playwright test", + "typecheck": "tsc --noEmit" }, "keywords": [], "author": "", @@ -18,8 +19,12 @@ }, "devDependencies": { "vite": "^5.4.8", - "vitest": "^0.34.0", + "vitest": "^0.29.8", "jsdom": "^22.1.0", - "@playwright/test": "^1.35.0" + "@playwright/test": "^1.32.0", + "@types/node": "^18.15.11", + "typescript": "^5.0.3" } } + + diff --git a/public/manifest.json b/public/manifest.json index ffdaeeb..61b12c2 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -32,5 +32,6 @@ "text": "text", "url": "url" } - } + }, + "offline_page": "/listen-UI/offline.html" } diff --git a/public/offline.html b/public/offline.html new file mode 100644 index 0000000..757ff0e --- /dev/null +++ b/public/offline.html @@ -0,0 +1,37 @@ + + + + + + Offline - MP3 Player PWA + + + +

You are offline

+

Please check your internet connection and try again.

+
+

Cached Audio Files

+ +
+ + + \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..5838561 --- /dev/null +++ b/readme.md @@ -0,0 +1,4 @@ + +tests : + +npm run test:e2e \ No newline at end of file diff --git a/script.js b/script.js index 17a9a39..7985414 100644 --- a/script.js +++ b/script.js @@ -1,22 +1,7 @@ -import { Client } from "@gradio/client"; - -// Add these variables at the top of the file -let audioCache = {}; -let currentTrack = null; - -// Add this constant at the top of the file -const DEFAULT_API_SERVER = "Mightypeacock/webtoaudio"; - -// // Add this function at the beginning of your script.js file -// function handleSharedUrl() { -// const urlParams = new URLSearchParams(window.location.search); -// const sharedUrl = urlParams.get('url'); - -// if (sharedUrl) { -// console.log('Shared URL detected:', sharedUrl); -// fetchMp3(sharedUrl); -// } -// } +import { audioCache, loadAudioCache, removeFromCache, clearAudioCache } from './src/audioCache.js'; +import { loadAudioFromCache, setupMediaSessionHandlers } from './src/audioPlayer.js'; +import { fetchMp3 } from './src/api.js'; +import { checkOnlineStatus, handleSharedUrl } from './src/utils.js'; document.addEventListener("DOMContentLoaded", async function() { const audioPlayer = document.getElementById('player'); @@ -32,11 +17,24 @@ document.addEventListener("DOMContentLoaded", async function() { const toggleApiKeyBtn = document.getElementById('toggleApiKey'); const apiServerInput = document.getElementById('apiServer'); + const historyList = document.getElementById('historyList'); + const clearHistoryBtn = document.getElementById('clearHistory'); let originalApiKey = ''; let originalApiServer = ''; - // Load saved settings on page load + //checkOnlineStatus(); + + // window.addEventListener('online', () => { + // alert('You are back online!'); + // updateHistoryList(); + // }); + + // window.addEventListener('offline', () => { + // alert('You are offline. Some features may be limited.'); + // }); + + // Load saved settings const savedApiKey = localStorage.getItem('openaiApiKey'); const savedApiServer = localStorage.getItem('apiServer') || DEFAULT_API_SERVER; if (savedApiKey) { @@ -57,7 +55,7 @@ document.addEventListener("DOMContentLoaded", async function() { } } - // Open settings modal + // Settings modal functionality settingsBtn.onclick = function() { originalApiKey = apiKeyInput.value; originalApiServer = apiServerInput.value; @@ -65,22 +63,19 @@ document.addEventListener("DOMContentLoaded", async function() { apiKeyInput.focus(); } - // Close settings modal function closeModal() { settingsModal.style.display = "none"; - apiKeyInput.value = originalApiKey; // Revert to original value + apiKeyInput.value = originalApiKey; } closeBtn.onclick = closeModal; - // Close modal if clicked outside window.onclick = function(event) { if (event.target == settingsModal) { closeModal(); } } - // Handle keydown events document.addEventListener('keydown', function(event) { if (settingsModal.style.display === "block") { if (event.key === "Escape") { @@ -91,7 +86,6 @@ document.addEventListener("DOMContentLoaded", async function() { } }); - // Save settings function saveSettings() { const apiKey = apiKeyInput.value.trim(); const apiServer = apiServerInput.value.trim(); @@ -109,188 +103,11 @@ document.addEventListener("DOMContentLoaded", async function() { saveSettingsBtn.onclick = saveSettings; - const historyList = document.getElementById('historyList'); - const clearHistoryBtn = document.getElementById('clearHistory'); - - // Load audio cache from localStorage and Cache API + // Load audio cache and update history list await loadAudioCache(); - - // Update history list updateHistoryList(); - // Function to fetch MP3 from API endpoint when a link is shared - async function fetchMp3(link) { - console.log('Starting fetchMp3 function with link:', link); - const loadingIndicator = document.getElementById('loadingIndicator'); - const audioPlayer = document.getElementById('player'); - const playButton = document.getElementById('playButton'); - const transcriptionContainer = document.getElementById('transcriptionContainer'); - const transcriptionElement = document.getElementById('transcription'); - - if (loadingIndicator) loadingIndicator.style.display = 'block'; - if (transcriptionContainer) transcriptionContainer.style.display = 'none'; - - try { - // Check if the link is already in the cache - if (audioCache[link]) { - console.log('Loading audio from cache'); - await loadAudioFromCache(link); - return; - } - - const apiKey = localStorage.getItem('openaiApiKey'); - const apiServer = localStorage.getItem('apiServer') || DEFAULT_API_SERVER; - console.log('Retrieved API key and server from localStorage'); - console.log('API Server:', apiServer); - - if (!apiKey) { - throw new Error("API key not set. Please set your OpenAI API key in the settings."); - } - - console.log('Attempting to connect to Gradio app...'); - - // Connect to Gradio app using the apiServer value - const client = await Client.connect(apiServer); - - console.log('Gradio client created successfully'); - - console.log(await client.view_api()) - - console.log('Preparing to make prediction...'); - // Make the prediction - - const result = await client.predict("/generate_audio", { - url:link, - openai_api_key: apiKey, - text_model: "gpt-4o-mini", - audio_model: "tts-1", - speaker_1_voice: "alloy", - speaker_2_voice: "echo", - api_base: null, // api_base - edited_transcript: "", // edited_transcript - user_feedback: "", // user_feedback - original_text: "summary" // original_text - // debug: true, - }); - - - console.log('Raw result from predict:', result); - console.log('Result data:', result.data); - - - console.log('Prediction made successfully'); - - // Check if result.data is an array and has at least one element - if (!Array.isArray(result.data) || result.data.length === 0) { - throw new Error('Unexpected result format from server'); - } - - // Assuming the audio file URL is the second item in the result - const audioFileUrl = result.data[0].url; - console.log('Received audio file URL:', audioFileUrl); - - // Check if the URL is valid - if (typeof audioFileUrl !== 'string' || !audioFileUrl.startsWith('http')) { - throw new Error(`Invalid audio file URL received: ${audioFileUrl}`); - } - - // After successful API call, add to cache - audioCache[link] = { - audioUrl: audioFileUrl, - transcription: result.data[1], - lastPosition: 0 - }; - await saveAudioCache(link, audioFileUrl); - updateHistoryList(); - - await loadAudioFromCache(link); - - // Update media session metadata - updateMediaSessionMetadata(link, 'Web to Audio', 'Generated Audio'); - - } catch (error) { - console.error('Error in fetchMp3:', error); - console.error('Error stack:', error.stack); - alert(`Error fetching MP3: ${error.message}`); - - // Clear the audio player source and hide the play button - if (audioPlayer) audioPlayer.src = ''; - if (playButton) playButton.style.display = 'none'; - if (transcriptionContainer) transcriptionContainer.style.display = 'none'; - } finally { - if (loadingIndicator) - loadingIndicator.style.display = 'none'; - } - } - - async function loadAudioFromCache(link) { - const cachedAudio = audioCache[link]; - if (!cachedAudio) return; - - const audioPlayer = document.getElementById('player'); - const playButton = document.getElementById('playButton'); - const transcriptionContainer = document.getElementById('transcriptionContainer'); - const transcriptionElement = document.getElementById('transcription'); - - // Fetch the audio file from the Cache API - const cache = await caches.open('audio-cache'); - const response = await cache.match(cachedAudio.audioUrl); - if (response) { - const blob = await response.blob(); - audioPlayer.src = URL.createObjectURL(blob); - } else { - audioPlayer.src = cachedAudio.audioUrl; - } - - audioPlayer.currentTime = cachedAudio.lastPosition; - currentTrack = link; - - if (playButton) { - playButton.style.display = 'block'; - playButton.onclick = () => audioPlayer.play(); - } - - if (transcriptionElement && transcriptionContainer) { - transcriptionElement.textContent = cachedAudio.transcription; - transcriptionContainer.style.display = 'block'; - } - - console.log('Audio loaded from cache and ready for playback'); - - // Update media session metadata - updateMediaSessionMetadata(link, 'Web to Audio', 'Generated Audio'); - } - - async function saveAudioCache(link, audioUrl) { - // Save metadata to localStorage - localStorage.setItem('audioCache', JSON.stringify(audioCache)); - - // Save audio file to Cache API - const cache = await caches.open('audio-cache'); - await cache.add(audioUrl); - } - - async function loadAudioCache() { - const savedCache = localStorage.getItem('audioCache'); - if (savedCache) { - audioCache = JSON.parse(savedCache); - } - - // Verify that all cached audio files are still in the Cache API - const cache = await caches.open('audio-cache'); - for (const link in audioCache) { - const response = await cache.match(audioCache[link].audioUrl); - if (!response) { - console.log(`Audio file for ${link} not found in cache, removing entry`); - delete audioCache[link]; - } - } - - // Save the cleaned-up cache back to localStorage - localStorage.setItem('audioCache', JSON.stringify(audioCache)); - } - - async function updateHistoryList() { + function updateHistoryList() { historyList.innerHTML = ''; Object.keys(audioCache).forEach(link => { const li = document.createElement('li'); @@ -299,7 +116,10 @@ document.addEventListener("DOMContentLoaded", async function() { playBtn.onclick = () => loadAudioFromCache(link); const removeBtn = document.createElement('button'); removeBtn.textContent = 'Remove'; - removeBtn.onclick = () => removeFromCache(link); + removeBtn.onclick = () => { + removeFromCache(link); + updateHistoryList(); + }; li.appendChild(document.createTextNode(link + ' ')); li.appendChild(playBtn); li.appendChild(removeBtn); @@ -307,102 +127,28 @@ document.addEventListener("DOMContentLoaded", async function() { }); } - async function removeFromCache(link) { - const cache = await caches.open('audio-cache'); - await cache.delete(audioCache[link].audioUrl); - delete audioCache[link]; - localStorage.setItem('audioCache', JSON.stringify(audioCache)); - updateHistoryList(); - } - clearHistoryBtn.onclick = async function() { - const cache = await caches.open('audio-cache'); - for (const link in audioCache) { - await cache.delete(audioCache[link].audioUrl); - } - audioCache = {}; - localStorage.setItem('audioCache', JSON.stringify(audioCache)); + await clearAudioCache(); updateHistoryList(); }; // Save current position every 5 seconds setInterval(() => { - if (currentTrack && audioPlayer.currentTime > 0) { + if (typeof currentTrack !== 'undefined' && currentTrack && audioPlayer.currentTime > 0) { audioCache[currentTrack].lastPosition = audioPlayer.currentTime; localStorage.setItem('audioCache', JSON.stringify(audioCache)); } }, 5000); - // Call handleSharedUrl instead of directly checking for the URL parameter - // handleSharedUrl(); - - // Get the link from the shared URL - const queryParams = new URLSearchParams(window.location.search); - const sharedLink = queryParams.get('url'); - - console.log('Shared link from URL:', sharedLink); - - // Only call the API to get MP3 if a valid URL is provided + // Handle shared URL + const sharedLink = handleSharedUrl(); if (sharedLink) { console.log('Valid URL provided, calling fetchMp3'); fetchMp3(sharedLink); } else { console.log("No URL provided. Waiting for user input."); - // You might want to update the UI here to indicate that the user needs to provide a URL - } - - // Add this function to update media session metadata - function updateMediaSessionMetadata(title, artist, album) { - if ('mediaSession' in navigator) { - navigator.mediaSession.metadata = new MediaMetadata({ - title: title || 'Unknown Title', - artist: artist || 'Unknown Artist', - album: album || 'Unknown Album', - artwork: [ - { src: '/icons/imagepodcast-transp500.png', sizes: '500x500', type: 'image/png' }, - { src: '/icons/imagepodcast.png', sizes: '1024x1024', type: 'image/png' } - ] - }); - } - } - - // Add this function to set up media session handlers - function setupMediaSessionHandlers() { - if ('mediaSession' in navigator) { - navigator.mediaSession.setActionHandler('play', () => { - audioPlayer.play(); - playButton.textContent = 'Pause'; - }); - - navigator.mediaSession.setActionHandler('pause', () => { - audioPlayer.pause(); - playButton.textContent = 'Play'; - }); - - navigator.mediaSession.setActionHandler('seekbackward', (details) => { - const skipTime = details.seekOffset || 10; - audioPlayer.currentTime = Math.max(audioPlayer.currentTime - skipTime, 0); - }); - - navigator.mediaSession.setActionHandler('seekforward', (details) => { - const skipTime = details.seekOffset || 10; - audioPlayer.currentTime = Math.min(audioPlayer.currentTime + skipTime, audioPlayer.duration); - }); - - navigator.mediaSession.setActionHandler('seekto', (details) => { - if (details.fastSeek && 'fastSeek' in audioPlayer) { - audioPlayer.fastSeek(details.seekTime); - return; - } - audioPlayer.currentTime = details.seekTime; - }); - - navigator.mediaSession.setActionHandler('previoustrack', () => { - audioPlayer.currentTime = 0; - }); - } } - // Call this function to set up the media session handlers - setupMediaSessionHandlers(); + // Set up media session handlers + setupMediaSessionHandlers(audioPlayer, playButton); }); \ No newline at end of file diff --git a/service-worker.js b/service-worker.js index a0cc6cc..585ec37 100644 --- a/service-worker.js +++ b/service-worker.js @@ -1,19 +1,58 @@ -self.addEventListener('install', (e) => { - e.waitUntil( - caches.open('mp3-player-cache').then((cache) => cache.addAll([ - '/', - '/index.html', - '/styles.css', - '/script.js', - '/icons/icon-192x192.png', - '/icons/icon-512x512.png', - ])) - ); - }); - - self.addEventListener('fetch', (e) => { - e.respondWith( - caches.match(e.request).then((response) => response || fetch(e.request)) - ); - }); - \ No newline at end of file +const CACHE_NAME = 'mp3-player-cache-v1'; +const urlsToCache = [ + '/listen-UI/', + '/listen-UI/index.html', + '/listen-UI/styles.css', + '/listen-UI/script.js', + '/listen-UI/icons/imagepodcast.png', + '/listen-UI/icons/imagepodcast-transp500.png', + '/listen-UI/manifest.json', + '/listen-UI/offline.html' +]; + +self.addEventListener('install', (event) => { + event.waitUntil( + caches.open(CACHE_NAME) + .then((cache) => cache.addAll(urlsToCache)) + ); +}); + +self.addEventListener('activate', (event) => { + event.waitUntil( + caches.keys().then((cacheNames) => { + return Promise.all( + cacheNames.map((cacheName) => { + if (cacheName !== CACHE_NAME) { + return caches.delete(cacheName); + } + }) + ); + }) + ); +}); + +self.addEventListener('fetch', (event) => { + event.respondWith( + caches.match(event.request) + .then((response) => { + if (response) { + return response; + } + return fetch(event.request) + .then((response) => { + if (!response || response.status !== 200 || response.type !== 'basic') { + return response; + } + const responseToCache = response.clone(); + caches.open(CACHE_NAME) + .then((cache) => { + cache.put(event.request, responseToCache); + }); + return response; + }) + .catch(() => { + return caches.match('/listen-UI/offline.html'); + }); + }) + ); +}); diff --git a/src/api.d.ts b/src/api.d.ts new file mode 100644 index 0000000..570ec8d --- /dev/null +++ b/src/api.d.ts @@ -0,0 +1,14 @@ +export function fetchMp3(link: string): Promise; + +// Additional type declarations +interface AudioCacheEntry { + audioUrl: string; + transcription: string; + lastPosition: number; +} + +declare global { + interface Window { + audioCache: Record; + } +} \ No newline at end of file diff --git a/src/api.js b/src/api.js new file mode 100644 index 0000000..3219990 --- /dev/null +++ b/src/api.js @@ -0,0 +1,100 @@ +import { Client } from "@gradio/client"; +import { audioCache, saveAudioCache } from './audioCache.js'; +import { loadAudioFromCache, updateMediaSessionMetadata } from './audioPlayer.js'; + +const DEFAULT_API_SERVER = "Mightypeacock/webtoaudio"; +/** + * Fetches an MP3 file from the specified link. + * @param {string} link - The URL of the audio content to fetch. + * @returns {Promise} + */ + +export async function fetchMp3(link) { + if (!navigator.onLine) { + alert('You are offline. Unable to fetch new audio.'); + return; + } + + console.log('Starting fetchMp3 function with link:', link); + const loadingIndicator = document.getElementById('loadingIndicator'); + const audioPlayer = document.getElementById('player'); + const playButton = document.getElementById('playButton'); + const transcriptionContainer = document.getElementById('transcriptionContainer'); + const transcriptionElement = document.getElementById('transcription'); + + if (loadingIndicator) loadingIndicator.style.display = 'block'; + if (transcriptionContainer) transcriptionContainer.style.display = 'none'; + + try { + if (audioCache[link]) { + console.log('Loading audio from cache'); + await loadAudioFromCache(link); + return; + } + + const apiKey = localStorage.getItem('openaiApiKey'); + const apiServer = localStorage.getItem('apiServer') || DEFAULT_API_SERVER; + console.log('Retrieved API key and server from localStorage'); + console.log('API Server:', apiServer); + + if (!apiKey) { + throw new Error("API key not set. Please set your OpenAI API key in the settings."); + } + + console.log('Attempting to connect to Gradio app...'); + const client = await Client.connect(apiServer); + console.log('Gradio client created successfully'); + console.log(await client.view_api()); + console.log('Preparing to make prediction...'); + + const result = await client.predict("/generate_audio", { + url: link, + openai_api_key: apiKey, + text_model: "gpt-4o-mini", + audio_model: "tts-1", + speaker_1_voice: "alloy", + speaker_2_voice: "echo", + api_base: null, + edited_transcript: "", + user_feedback: "", + original_text: "summary" + }); + + console.log('Raw result from predict:', result); + console.log('Result data:', result.data); + console.log('Prediction made successfully'); + + if (!Array.isArray(result.data) || result.data.length === 0) { + throw new Error('Unexpected result format from server'); + } + + const audioFileUrl = result.data[0].url; + console.log('Received audio file URL:', audioFileUrl); + + if (typeof audioFileUrl !== 'string' || !audioFileUrl.startsWith('http')) { + throw new Error(`Invalid audio file URL received: ${audioFileUrl}`); + } + + audioCache[link] = { + audioUrl: audioFileUrl, + transcription: result.data[1], + lastPosition: 0 + }; + await saveAudioCache(link, audioFileUrl); + await loadAudioFromCache(link); + + updateMediaSessionMetadata(link, 'Web to Audio', 'Generated Audio'); + + } catch (error) { + console.error('Error in fetchMp3:', error); + console.error('Error stack:', error.stack); + alert(`Error fetching MP3: ${error.message}`); + + if (audioPlayer) audioPlayer.src = ''; + if (playButton) playButton.style.display = 'none'; + if (transcriptionContainer) transcriptionContainer.style.display = 'none'; + } finally { + if (loadingIndicator) + loadingIndicator.style.display = 'none'; + } +} \ No newline at end of file diff --git a/src/api.test.js b/src/api.test.js new file mode 100644 index 0000000..9e60983 --- /dev/null +++ b/src/api.test.js @@ -0,0 +1,39 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { fetchMp3 } from './api'; +import { Client } from "@gradio/client"; + +vi.mock("@gradio/client"); + +describe('api', () => { + beforeEach(() => { + global.navigator = { onLine: true }; + global.localStorage = { + getItem: vi.fn(), + setItem: vi.fn() + }; + global.document = { + getElementById: vi.fn().mockReturnValue({ + style: { display: 'none' }, + src: '' + }) + }; + Client.connect = vi.fn().mockResolvedValue({ + predict: vi.fn().mockResolvedValue({ + data: [{ url: 'test.mp3' }, 'Test transcription'] + }) + }); + }); + + it('should fetch MP3 from API', async () => { + localStorage.getItem.mockReturnValue('test-api-key'); + await fetchMp3('https://example.com'); + expect(Client.connect).toHaveBeenCalled(); + }); + + it('should handle offline state', async () => { + global.navigator.onLine = false; + global.alert = vi.fn(); + await fetchMp3('https://example.com'); + expect(alert).toHaveBeenCalledWith('You are offline. Unable to fetch new audio.'); + }); +}); \ No newline at end of file diff --git a/src/api.test.ts b/src/api.test.ts new file mode 100644 index 0000000..f22e928 --- /dev/null +++ b/src/api.test.ts @@ -0,0 +1,42 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { fetchMp3 } from './api'; +import { Client } from "@gradio/client"; + +vi.mock("@gradio/client"); + +describe('api', () => { + beforeEach(() => { + global.navigator = { onLine: true } as any; + global.localStorage = { + getItem: vi.fn(), + setItem: vi.fn() + } as any; + global.document = { + getElementById: vi.fn().mockReturnValue({ + style: { display: 'none' }, + src: '' + }) + } as any; + (Client.connect as any) = vi.fn().mockResolvedValue({ + predict: vi.fn().mockResolvedValue({ + data: [{ url: 'test.mp3' }, 'Test transcription'] + }) + }); + }); + + it('should fetch MP3 from API', async () => { + (localStorage.getItem as any) = vi.fn().mockReturnValue('test-api-key'); + await fetchMp3('https://example.com'); + expect(Client.connect).toHaveBeenCalled(); + }); + + it('should handle offline state', async () => { + Object.defineProperty(navigator, 'onLine', { + value: false, + writable: true + }); + global.alert = vi.fn(); + await fetchMp3('https://example.com'); + expect(alert).toHaveBeenCalledWith('You are offline. Unable to fetch new audio.'); + }); +}); \ No newline at end of file diff --git a/src/audioCache.js b/src/audioCache.js new file mode 100644 index 0000000..998b2ae --- /dev/null +++ b/src/audioCache.js @@ -0,0 +1,68 @@ +export let audioCache = {}; +export let currentTrack = null; +/** + * @typedef {Object} AudioCacheEntry + * @property {string} audioUrl - The URL of the cached audio file + * @property {string} [transcription] - The transcription of the audio content + * @property {number} [lastPosition] - The last playback position of the audio + */ + +/** + * @type {Object.} + * @description A cache object storing audio information keyed by link + */ + +/** + * @type {string|null} + * @description The currently playing track's link + */ + +export async function loadAudioCache() { + const savedCache = localStorage.getItem('audioCache'); + if (savedCache) { + audioCache = JSON.parse(savedCache); + } + + const savedCurrentTrack = localStorage.getItem('currentTrack'); + if (savedCurrentTrack) { + currentTrack = savedCurrentTrack; + } + + const cache = await caches.open('audio-cache'); + for (const link in audioCache) { + const response = await cache.match(audioCache[link].audioUrl); + if (!response) { + console.log(`Audio file for ${link} not found in cache, removing entry`); + delete audioCache[link]; + } + } + + localStorage.setItem('audioCache', JSON.stringify(audioCache)); +} + +export function setCurrentTrack(link) { + currentTrack = link; + localStorage.setItem('currentTrack', currentTrack); +} + +export async function saveAudioCache(link, audioUrl) { + localStorage.setItem('audioCache', JSON.stringify(audioCache)); + const cache = await caches.open('audio-cache'); + await cache.add(audioUrl); +} + +export async function removeFromCache(link) { + const cache = await caches.open('audio-cache'); + await cache.delete(audioCache[link].audioUrl); + delete audioCache[link]; + localStorage.setItem('audioCache', JSON.stringify(audioCache)); +} + +export async function clearAudioCache() { + const cache = await caches.open('audio-cache'); + for (const link in audioCache) { + await cache.delete(audioCache[link].audioUrl); + } + audioCache = {}; + localStorage.setItem('audioCache', JSON.stringify(audioCache)); +} \ No newline at end of file diff --git a/src/audioCache.test.js b/src/audioCache.test.js new file mode 100644 index 0000000..4aaf641 --- /dev/null +++ b/src/audioCache.test.js @@ -0,0 +1,39 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { loadAudioCache, saveAudioCache, removeFromCache, clearAudioCache } from './audioCache'; + +describe('audioCache', () => { + beforeEach(() => { + vi.mock('localStorage', () => ({ + getItem: vi.fn(), + setItem: vi.fn() + })); + vi.mock('caches', () => ({ + open: vi.fn().mockResolvedValue({ + match: vi.fn(), + add: vi.fn(), + delete: vi.fn() + }) + })); + }); + + it('should load audio cache from localStorage', async () => { + localStorage.getItem.mockReturnValue(JSON.stringify({ 'test': { audioUrl: 'test.mp3' } })); + await loadAudioCache(); + expect(localStorage.getItem).toHaveBeenCalledWith('audioCache'); + }); + + it('should save audio cache to localStorage', async () => { + await saveAudioCache('test', 'test.mp3'); + expect(localStorage.setItem).toHaveBeenCalled(); + }); + + it('should remove item from cache', async () => { + await removeFromCache('test'); + expect(localStorage.setItem).toHaveBeenCalled(); + }); + + it('should clear audio cache', async () => { + await clearAudioCache(); + expect(localStorage.setItem).toHaveBeenCalledWith('audioCache', '{}'); + }); +}); \ No newline at end of file diff --git a/src/audioCache.test.ts b/src/audioCache.test.ts new file mode 100644 index 0000000..6eefb8f --- /dev/null +++ b/src/audioCache.test.ts @@ -0,0 +1,39 @@ +import { vi } from 'vitest'; +import { loadAudioCache, saveAudioCache, removeFromCache, clearAudioCache } from './audioCache'; + +describe('Audio Cache', () => { + beforeEach(() => { + vi.mock('localStorage', () => ({ + getItem: vi.fn(), + setItem: vi.fn() + })); + vi.mock('caches', () => ({ + open: vi.fn().mockResolvedValue({ + match: vi.fn(), + add: vi.fn(), + delete: vi.fn() + }) + })); + }); + + it('should load audio cache from localStorage', () => { + (localStorage.getItem as any) = vi.fn().mockReturnValue(JSON.stringify({ 'test': { audioUrl: 'test.mp3' } })); + loadAudioCache(); + expect(localStorage.getItem).toHaveBeenCalledWith('audioCache'); + }); + + it('should save audio cache to localStorage', async () => { + await saveAudioCache('test', 'test.mp3'); + expect(localStorage.setItem).toHaveBeenCalled(); + }); + + it('should remove item from cache', async () => { + await removeFromCache('test'); + expect(localStorage.setItem).toHaveBeenCalled(); + }); + + it('should clear audio cache', async () => { + await clearAudioCache(); + expect(localStorage.setItem).toHaveBeenCalledWith('audioCache', '{}'); + }); +}); \ No newline at end of file diff --git a/src/audioPlayer.js b/src/audioPlayer.js new file mode 100644 index 0000000..8f2c789 --- /dev/null +++ b/src/audioPlayer.js @@ -0,0 +1,99 @@ +import { audioCache, currentTrack } from './audioCache.js'; +import { setCurrentTrack } from './audioCache.js'; + +export async function loadAudioFromCache(link) { + const cachedAudio = audioCache[link]; + if (!cachedAudio) { + if (!navigator.onLine) { + alert('This audio is not available offline.'); + } + return; + } + + const audioPlayer = document.getElementById('player'); + const playButton = document.getElementById('playButton'); + const transcriptionContainer = document.getElementById('transcriptionContainer'); + const transcriptionElement = document.getElementById('transcription'); + + const cache = await caches.open('audio-cache'); + const response = await cache.match(cachedAudio.audioUrl); + if (response) { + const blob = await response.blob(); + audioPlayer.src = URL.createObjectURL(blob); + } else { + audioPlayer.src = cachedAudio.audioUrl; + } + // Import setCurrentTrack function from audioCache.js + + // Ensure audioPlayer is loaded before setting currentTime + audioPlayer.addEventListener('loadedmetadata', () => { + audioPlayer.currentTime = cachedAudio.lastPosition || 0; + }); + + audioPlayer.currentTime = cachedAudio.lastPosition; + setCurrentTrack(link); + + if (playButton) { + playButton.style.display = 'block'; + playButton.onclick = () => audioPlayer.play(); + } + + if (transcriptionElement && transcriptionContainer) { + transcriptionElement.textContent = cachedAudio.transcription; + transcriptionContainer.style.display = 'block'; + } + + console.log('Audio loaded from cache and ready for playback'); + + updateMediaSessionMetadata(link, 'Web to Audio', 'Generated Audio'); +} + +export function updateMediaSessionMetadata(title, artist, album) { + if ('mediaSession' in navigator) { + navigator.mediaSession.metadata = new MediaMetadata({ + title: title || 'Unknown Title', + artist: artist || 'Unknown Artist', + album: album || 'Unknown Album', + artwork: [ + { src: '/icons/imagepodcast-transp500.png', sizes: '500x500', type: 'image/png' }, + { src: '/icons/imagepodcast.png', sizes: '1024x1024', type: 'image/png' } + ] + }); + } +} + +export function setupMediaSessionHandlers(audioPlayer, playButton) { + if ('mediaSession' in navigator) { + navigator.mediaSession.setActionHandler('play', () => { + audioPlayer.play(); + playButton.textContent = 'Pause'; + }); + + navigator.mediaSession.setActionHandler('pause', () => { + audioPlayer.pause(); + playButton.textContent = 'Play'; + }); + + navigator.mediaSession.setActionHandler('seekbackward', (details) => { + const skipTime = details.seekOffset || 10; + audioPlayer.currentTime = Math.max(audioPlayer.currentTime - skipTime, 0); + }); + + navigator.mediaSession.setActionHandler('seekforward', (details) => { + const skipTime = details.seekOffset || 10; + audioPlayer.currentTime = Math.min(audioPlayer.currentTime + skipTime, audioPlayer.duration); + }); + + navigator.mediaSession.setActionHandler('seekto', (details) => { + if (details.fastSeek && 'fastSeek' in audioPlayer) { + audioPlayer.fastSeek(details.seekTime); + return; + } + audioPlayer.currentTime = details.seekTime; + }); + + navigator.mediaSession.setActionHandler('previoustrack', () => { + audioPlayer.currentTime = 0; + }); + } +} \ No newline at end of file diff --git a/src/audioPlayer.test.js b/src/audioPlayer.test.js new file mode 100644 index 0000000..8353093 --- /dev/null +++ b/src/audioPlayer.test.js @@ -0,0 +1,41 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { loadAudioFromCache, updateMediaSessionMetadata, setupMediaSessionHandlers } from './audioPlayer'; +import { audioCache } from './audioCache'; + +describe('audioPlayer', () => { + beforeEach(() => { + vi.mock('./audioCache', () => ({ + audioCache: { + 'test': { audioUrl: 'test.mp3', lastPosition: 0, transcription: 'Test' } + }, + currentTrack: null + })); + global.document = { + getElementById: vi.fn().mockReturnValue({ + src: '', + currentTime: 0, + style: { display: 'none' }, + play: vi.fn() + }) + }; + global.URL = { createObjectURL: vi.fn() }; + global.navigator = { mediaSession: { metadata: null, setActionHandler: vi.fn() } }; + }); + + it('should load audio from cache', async () => { + await loadAudioFromCache('test'); + expect(document.getElementById).toHaveBeenCalledWith('player'); + }); + + it('should update media session metadata', () => { + updateMediaSessionMetadata('Test Title', 'Test Artist', 'Test Album'); + expect(navigator.mediaSession.metadata).toBeTruthy(); + }); + + it('should setup media session handlers', () => { + const audioPlayer = { play: vi.fn(), pause: vi.fn(), currentTime: 0 }; + const playButton = { textContent: '' }; + setupMediaSessionHandlers(audioPlayer, playButton); + expect(navigator.mediaSession.setActionHandler).toHaveBeenCalled(); + }); +}); \ No newline at end of file diff --git a/src/audioPlayer.test.ts b/src/audioPlayer.test.ts new file mode 100644 index 0000000..fc88d49 --- /dev/null +++ b/src/audioPlayer.test.ts @@ -0,0 +1,41 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { loadAudioFromCache, updateMediaSessionMetadata, setupMediaSessionHandlers } from './audioPlayer'; +// import { audioCache } from './audioCache'; + +describe('audioPlayer', () => { + beforeEach(() => { + vi.mock('./audioCache', () => ({ + audioCache: { + 'test': { audioUrl: 'test.mp3', lastPosition: 0, transcription: 'Test' } + }, + currentTrack: null + })); + global.document = { + getElementById: vi.fn().mockReturnValue({ + src: '', + currentTime: 0, + style: { display: 'none' }, + play: vi.fn() + }) + } as any; + global.URL = { createObjectURL: vi.fn() } as any; + global.navigator = { mediaSession: { metadata: null, setActionHandler: vi.fn() } } as any; + }); + + it('should load audio from cache', async () => { + await loadAudioFromCache('test'); + expect(document.getElementById).toHaveBeenCalledWith('player'); + }); + + it('should update media session metadata', () => { + updateMediaSessionMetadata('Test Title', 'Test Artist', 'Test Album'); + expect(navigator.mediaSession.metadata).toBeTruthy(); + }); + + it('should setup media session handlers', () => { + const audioPlayer = { play: vi.fn(), pause: vi.fn(), currentTime: 0 }; + const playButton = { textContent: '' }; + setupMediaSessionHandlers(audioPlayer as any, playButton as any); + expect(navigator.mediaSession.setActionHandler).toHaveBeenCalled(); + }); +}); \ No newline at end of file diff --git a/src/audiocache.d.ts b/src/audiocache.d.ts new file mode 100644 index 0000000..4a8d344 --- /dev/null +++ b/src/audiocache.d.ts @@ -0,0 +1,14 @@ +export let audioCache: Record; +export let currentTrack: string | null; + +export function loadAudioCache(): Promise; +export function saveAudioCache(link: string, audioUrl: string): Promise; +export function removeFromCache(link: string): Promise; +export function clearAudioCache(): Promise; + +// // Declare global interfaces +// declare global { +// interface Window { +// caches: CacheStorage; +// } +// } \ No newline at end of file diff --git a/src/audioplayer.d.ts b/src/audioplayer.d.ts new file mode 100644 index 0000000..c48ed7c --- /dev/null +++ b/src/audioplayer.d.ts @@ -0,0 +1,23 @@ +export function loadAudioFromCache(link: string): Promise; +export function updateMediaSessionMetadata(title: string, artist: string, album: string): void; +export function setupMediaSessionHandlers(): void; + +// If there are any other exported functions or variables from audioPlayer.js, declare them here as well + +// Declare any global interfaces or types used in audioPlayer.js +declare global { + interface Navigator { + mediaSession?: MediaSession; + } + + interface MediaSession { + metadata: MediaMetadata; + setActionHandler(type: string, callback: (() => void) | null): void; + } + + interface MediaMetadata { + title: string; + artist: string; + album: string; + } +} \ No newline at end of file diff --git a/src/utils.d.ts b/src/utils.d.ts new file mode 100644 index 0000000..fa369ff --- /dev/null +++ b/src/utils.d.ts @@ -0,0 +1,2 @@ +export function checkOnlineStatus(): boolean; +export function handleSharedUrl(): String; \ No newline at end of file diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..b887b03 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,16 @@ +export function checkOnlineStatus() { + if (!navigator.onLine) { + alert('You are currently offline. Some features may be limited.'); + } +} + +export function handleSharedUrl() { + const urlParams = new URLSearchParams(window.location.search); + const sharedUrl = urlParams.get('url'); + + if (sharedUrl) { + console.log('Shared URL detected:', sharedUrl); + return sharedUrl; + } + return null; +} \ No newline at end of file diff --git a/src/utils.test.js b/src/utils.test.js new file mode 100644 index 0000000..fedc9b1 --- /dev/null +++ b/src/utils.test.js @@ -0,0 +1,17 @@ +import { describe, it, expect, vi } from 'vitest'; +import { checkOnlineStatus, handleSharedUrl } from './utils'; + +describe('utils', () => { + it('should check online status', () => { + global.navigator = { onLine: false }; + global.alert = vi.fn(); + checkOnlineStatus(); + expect(alert).toHaveBeenCalledWith('You are currently offline. Some features may be limited.'); + }); + + it('should handle shared URL', () => { + global.window = { location: { search: '?url=https://example.com' } }; + const result = handleSharedUrl(); + expect(result).toBe('https://example.com'); + }); +}); \ No newline at end of file diff --git a/src/utils.test.ts b/src/utils.test.ts new file mode 100644 index 0000000..76f8cc3 --- /dev/null +++ b/src/utils.test.ts @@ -0,0 +1,17 @@ +import { describe, it, expect, vi } from 'vitest'; +import { checkOnlineStatus, handleSharedUrl } from './utils'; + +describe('utils', () => { + it('should check online status', () => { + global.navigator = { onLine: false } as any; + global.alert = vi.fn(); + checkOnlineStatus(); + expect(alert).toHaveBeenCalledWith('You are currently offline. Some features may be limited.'); + }); + + it('should handle shared URL', () => { + global.window = { location: { search: '?url=https://example.com' } } as any; + const result = handleSharedUrl(); + expect(result).toBe('https://example.com'); + }); +}); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..18cef09 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + + /* Testing */ + "types": ["vitest/globals", "node"] + }, + "include": ["src", "e2e"] +} \ No newline at end of file diff --git a/vitest.config.js b/vitest.config.js new file mode 100644 index 0000000..61de041 --- /dev/null +++ b/vitest.config.js @@ -0,0 +1,7 @@ +// vitest.config.js +export default { + test: { + environment: 'jsdom', // Needed to simulate a browser-like environment + }, + }; + \ No newline at end of file