A simple to-do app focused on tasks that can be completed within a specific time span.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fixes

benni043 e116f996 bde870aa

+343 -180
+8
README.md
··· 11 11 ```dotenv 12 12 GOOGLE_CLIENT_ID=<google_client_id> 13 13 GOOGLE_CLIENT_SECRET=<google_client_secret> 14 + 15 + NUXT_PUBLIC_SUBSCRIPTIONS_PUBLIC_KEY=<public_key> 16 + SUBSCRIPTIONS_PRIVATE_KEY=<private_key> 17 + SUBSCRIPTIONS_MAIL=<mailto:you@example.com> 14 18 ``` 15 19 16 20 ### Deployment ··· 24 28 25 29 BETTER_AUTH_SECRET=<secret> 26 30 BETTER_AUTH_URL=<base_url> 31 + 32 + NUXT_PUBLIC_SUBSCRIPTIONS_PUBLIC_KEY=<public_key> 33 + SUBSCRIPTIONS_PRIVATE_KEY=<private_key> 34 + SUBSCRIPTIONS_MAIL=<mailto:you@example.com> 27 35 ```
+83 -86
app/components/Settings/SettingsSheet.vue
··· 1 1 <script setup lang="ts"> 2 - import type { Locale } from "vue-i18n"; 3 - import Sheet from "../Utils/Sheet.vue"; 4 - import ApiKeys from "./ApiKeys.vue"; 5 - import Categories from "./Categories.vue"; 6 - import GoogleLogin from "./GoogleLogin.vue"; 7 - import Tags from "./Tags.vue"; 2 + import type {Locale} from "vue-i18n"; 3 + import Sheet from "../Utils/Sheet.vue"; 4 + import ApiKeys from "./ApiKeys.vue"; 5 + import Categories from "./Categories.vue"; 6 + import GoogleLogin from "./GoogleLogin.vue"; 7 + import Tags from "./Tags.vue"; 8 8 9 - const isOpen = defineModel<boolean>("isOpen", { required: true }); 9 + const isOpen = defineModel<boolean>("isOpen", {required: true}); 10 10 11 - const config = useRuntimeConfig(); 11 + const config = useRuntimeConfig(); 12 12 13 - function close() { 14 - isOpen.value = false; 15 - } 13 + function close() { 14 + isOpen.value = false; 15 + } 16 16 17 - const { settings } = useSettings(); 17 + const {settings} = useSettings(); 18 18 19 - const { t, locale, locales, setLocale } = useI18n(); 20 - const supportedLocales = locales.value.map((l) => l.code); 19 + const {t, locale, locales, setLocale} = useI18n(); 20 + const supportedLocales = locales.value.map((l) => l.code); 21 21 22 - async function onLanguageChange(newLocale: string) { 23 - if ((supportedLocales as string[]).includes(newLocale)) { 24 - await setLocale(newLocale as Locale); 25 - } else { 26 - console.error("Unknown locale"); 27 - } 28 - } 22 + async function onLanguageChange(newLocale: string) { 23 + if ((supportedLocales as string[]).includes(newLocale)) { 24 + await setLocale(newLocale as Locale); 25 + } else { 26 + console.error("Unknown locale"); 27 + } 28 + } 29 29 30 - async function sendPush() { 31 - const permission = await Notification.requestPermission(); 30 + async function sendPush() { 31 + const permission = await Notification.requestPermission(); 32 32 33 - if (permission !== "granted") return; 33 + if (permission !== "granted") return; 34 34 35 - const reg = await navigator.serviceWorker.ready; 35 + const reg = await navigator.serviceWorker.ready; 36 36 37 - const sub = await reg.pushManager.subscribe({ 38 - userVisibleOnly: true, 39 - applicationServerKey: config.public.subscriptionsPublicKey, 40 - }); 37 + const sub = await reg.pushManager.subscribe({ 38 + userVisibleOnly: true, 39 + applicationServerKey: config.public.subscriptionsPublicKey, 40 + }); 41 41 42 - const fetch = useRequestFetch(); 43 - 44 - await fetch(`/api/subscription/`, { 45 - method: "POST", 46 - body: sub.toJSON(), 47 - ...useFetchOptions(), 48 - }).catch(async (err) => { 49 - console.warn(err); 50 - }); 51 - } 42 + await $fetch(`/api/subscription/`, { 43 + method: "POST", 44 + body: sub.toJSON(), 45 + }).catch(async (err) => { 46 + console.warn(err); 47 + }); 48 + } 52 49 </script> 53 50 54 51 <template> 55 - <Sheet :is-open="isOpen" title="Settings Sheet" @close="close"> 56 - <div data-testid="settings-sheet" class="flex flex-col gap-1"> 57 - <div class="flex gap-1"> 58 - <div> 59 - <h2 class="text-muted-text text-lg">{{ t("login") }}</h2> 60 - <GoogleLogin /> 61 - </div> 52 + <Sheet :is-open="isOpen" title="Settings Sheet" @close="close"> 53 + <div data-testid="settings-sheet" class="flex flex-col gap-1"> 54 + <div class="flex gap-1"> 55 + <div> 56 + <h2 class="text-muted-text text-lg">{{ t("login") }}</h2> 57 + <GoogleLogin/> 58 + </div> 62 59 63 - <div> 64 - <h2 class="text-muted-text text-lg">{{ t("languages") }}</h2> 65 - <select 66 - data-testid="language-select" 67 - class="bg-surface border-secondary h-8 cursor-pointer rounded border px-2 text-white" 68 - :value="locale" 69 - @change=" 60 + <div> 61 + <h2 class="text-muted-text text-lg">{{ t("languages") }}</h2> 62 + <select 63 + data-testid="language-select" 64 + class="bg-surface border-secondary h-8 cursor-pointer rounded border px-2 text-white" 65 + :value="locale" 66 + @change=" 70 67 onLanguageChange(($event.target as HTMLSelectElement).value) 71 68 " 72 - > 73 - <option v-for="lang in locales" :key="lang.code" :value="lang.code"> 74 - {{ t(`languageNames.${lang.code}`) }} 75 - </option> 76 - </select> 77 - </div> 69 + > 70 + <option v-for="lang in locales" :key="lang.code" :value="lang.code"> 71 + {{ t(`languageNames.${lang.code}`) }} 72 + </option> 73 + </select> 74 + </div> 78 75 79 - <div> 80 - <h2 class="text-muted-text text-lg">{{ t("insertionPoint") }}</h2> 81 - <select 82 - v-model="settings.insertionPoint" 83 - class="bg-surface border-secondary h-8 cursor-pointer rounded border px-2 text-white" 84 - > 85 - <option value="top">{{ t(`top`) }}</option> 86 - <option value="bottom">{{ t(`bottom`) }}</option> 87 - </select> 88 - </div> 76 + <div> 77 + <h2 class="text-muted-text text-lg">{{ t("push") }}</h2> 78 + <button 79 + type="button" 80 + data-testid="add-label-button" 81 + class="bg-surface border-secondary h-8 cursor-pointer rounded border px-2 text-white" 82 + @click="sendPush()" 83 + > 84 + {{ t("push") }} 85 + </button> 86 + </div> 89 87 90 - <div> 91 - <h2 class="text-muted-text text-lg">{{ t("push") }}</h2> 92 - <button 93 - type="button" 94 - data-testid="add-label-button" 95 - class="bg-surface border-secondary h-8 cursor-pointer rounded border px-2 text-white" 96 - @click="sendPush()" 97 - > 98 - {{ t("push") }} 99 - </button> 100 - </div> 101 - </div> 102 - <Categories /> 103 - <Tags /> 104 - <ApiKeys /> 105 - </div> 106 - </Sheet> 88 + <div> 89 + <h2 class="text-muted-text text-lg">{{ t("insertionPoint") }}</h2> 90 + <select 91 + v-model="settings.insertionPoint" 92 + class="bg-surface border-secondary h-8 cursor-pointer rounded border px-2 text-white" 93 + > 94 + <option value="top">{{ t(`top`) }}</option> 95 + <option value="bottom">{{ t(`bottom`) }}</option> 96 + </select> 97 + </div> 98 + </div> 99 + <Categories/> 100 + <Tags/> 101 + <ApiKeys/> 102 + </div> 103 + </Sheet> 107 104 </template>
+164
pnpm-lock.yaml
··· 74 74 vue-router: 75 75 specifier: ^5.0.6 76 76 version: 5.0.6(@vue/compiler-sfc@3.5.33)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.33(typescript@6.0.3)))(vue@3.5.33(typescript@6.0.3)) 77 + web-push: 78 + specifier: ^3.6.7 79 + version: 3.6.7 77 80 zod: 78 81 specifier: ^4.3.6 79 82 version: 4.3.6 ··· 87 90 '@playwright/test': 88 91 specifier: ^1.59.1 89 92 version: 1.59.1 93 + '@types/web-push': 94 + specifier: ^3.6.4 95 + version: 3.6.4 90 96 '@vue/test-utils': 91 97 specifier: ^2.4.6 92 98 version: 2.4.6 ··· 337 343 engines: {node: '>=14.21.3'} 338 344 cpu: [arm64] 339 345 os: [linux] 346 + libc: [musl] 340 347 341 348 '@biomejs/cli-linux-arm64@2.4.13': 342 349 resolution: {integrity: sha512-NzkUDSqfvMBrPplKgVr3aXLHZ2NEELvvF4vZxXulEylKWIGqlvNEcwUcj9OLrn75TD3lJ/GIqCVlBwd1MZCuYQ==} 343 350 engines: {node: '>=14.21.3'} 344 351 cpu: [arm64] 345 352 os: [linux] 353 + libc: [glibc] 346 354 347 355 '@biomejs/cli-linux-x64-musl@2.4.13': 348 356 resolution: {integrity: sha512-Z601MienRgTBDza/+u2CH3RSrWoXo9rtr8NK6A4KJzqGgfxx+H3VlyLgTJ4sRo40T3pIsqpTmiOQEvYzQvBRvQ==} 349 357 engines: {node: '>=14.21.3'} 350 358 cpu: [x64] 351 359 os: [linux] 360 + libc: [musl] 352 361 353 362 '@biomejs/cli-linux-x64@2.4.13': 354 363 resolution: {integrity: sha512-Az3ZZedYRBo9EQzNnD9SxFcR1G5QsGo6VEc2hIyVPZ1rdKwee/7E9oeBBZFpE8Z44ekxsDQBqbiWGW5ShOhUSQ==} 355 364 engines: {node: '>=14.21.3'} 356 365 cpu: [x64] 357 366 os: [linux] 367 + libc: [glibc] 358 368 359 369 '@biomejs/cli-win32-arm64@2.4.13': 360 370 resolution: {integrity: sha512-Px9PS2B5/Q183bUwy/5VHqp3J2lzdOCeVGzMpphYfl8oSa7VDCqenBdqWpy6DCy/en4Rbf/Y1RieZF6dJPcc9A==} ··· 1159 1169 engines: {node: ^20.19.0 || >=22.12.0} 1160 1170 cpu: [arm64] 1161 1171 os: [linux] 1172 + libc: [glibc] 1162 1173 1163 1174 '@oxc-minify/binding-linux-arm64-musl@0.117.0': 1164 1175 resolution: {integrity: sha512-C3zapJconWpl2Y7LR3GkRkH6jxpuV2iVUfkFcHT5Ffn4Zu7l88mZa2dhcfdULZDybN1Phka/P34YUzuskUUrXw==} 1165 1176 engines: {node: ^20.19.0 || >=22.12.0} 1166 1177 cpu: [arm64] 1167 1178 os: [linux] 1179 + libc: [musl] 1168 1180 1169 1181 '@oxc-minify/binding-linux-ppc64-gnu@0.117.0': 1170 1182 resolution: {integrity: sha512-2T/Bm+3/qTfuNS4gKSzL8qbiYk+ErHW2122CtDx+ilZAzvWcJ8IbqdZIbEWOlwwe03lESTxPwTBLFqVgQU2OeQ==} 1171 1183 engines: {node: ^20.19.0 || >=22.12.0} 1172 1184 cpu: [ppc64] 1173 1185 os: [linux] 1186 + libc: [glibc] 1174 1187 1175 1188 '@oxc-minify/binding-linux-riscv64-gnu@0.117.0': 1176 1189 resolution: {integrity: sha512-MKLjpldYkeoB4T+yAi4aIAb0waifxUjLcKkCUDmYAY3RqBJTvWK34KtfaKZL0IBMIXfD92CbKkcxQirDUS9Xcg==} 1177 1190 engines: {node: ^20.19.0 || >=22.12.0} 1178 1191 cpu: [riscv64] 1179 1192 os: [linux] 1193 + libc: [glibc] 1180 1194 1181 1195 '@oxc-minify/binding-linux-riscv64-musl@0.117.0': 1182 1196 resolution: {integrity: sha512-UFVcbPvKUStry6JffriobBp8BHtjmLLPl4bCY+JMxIn/Q3pykCpZzRwFTcDurG/kY8tm+uSNfKKdRNa5Nh9A7g==} 1183 1197 engines: {node: ^20.19.0 || >=22.12.0} 1184 1198 cpu: [riscv64] 1185 1199 os: [linux] 1200 + libc: [musl] 1186 1201 1187 1202 '@oxc-minify/binding-linux-s390x-gnu@0.117.0': 1188 1203 resolution: {integrity: sha512-B9GyPQ1NKbvpETVAMyJMfRlD3c6UJ7kiuFUAlx9LTYiQL+YIyT6vpuRlq1zgsXxavZluVrfeJv6x0owV4KDx4Q==} 1189 1204 engines: {node: ^20.19.0 || >=22.12.0} 1190 1205 cpu: [s390x] 1191 1206 os: [linux] 1207 + libc: [glibc] 1192 1208 1193 1209 '@oxc-minify/binding-linux-x64-gnu@0.117.0': 1194 1210 resolution: {integrity: sha512-fXfhtr+WWBGNy4M5GjAF5vu/lpulR4Me34FjTyaK9nDrTZs7LM595UDsP1wliksqp4hD/KdoqHGmbCrC+6d4vA==} 1195 1211 engines: {node: ^20.19.0 || >=22.12.0} 1196 1212 cpu: [x64] 1197 1213 os: [linux] 1214 + libc: [glibc] 1198 1215 1199 1216 '@oxc-minify/binding-linux-x64-musl@0.117.0': 1200 1217 resolution: {integrity: sha512-jFBgGbx1oLadb83ntJmy1dWlAHSQanXTS21G4PgkxyONmxZdZ/UMKr7KsADzMuoPsd2YhJHxzRpwJd9U+4BFBw==} 1201 1218 engines: {node: ^20.19.0 || >=22.12.0} 1202 1219 cpu: [x64] 1203 1220 os: [linux] 1221 + libc: [musl] 1204 1222 1205 1223 '@oxc-minify/binding-openharmony-arm64@0.117.0': 1206 1224 resolution: {integrity: sha512-nxPd9vx1vYz8IlIMdl9HFdOK/ood1H5hzbSFsyO8JU55tkcJoBL8TLCbuFf9pHpOy27l2gcPyV6z3p4eAcTH5Q==} ··· 1320 1338 engines: {node: ^20.19.0 || >=22.12.0} 1321 1339 cpu: [arm64] 1322 1340 os: [linux] 1341 + libc: [glibc] 1323 1342 1324 1343 '@oxc-parser/binding-linux-arm64-gnu@0.117.0': 1325 1344 resolution: {integrity: sha512-jBxD7DtlHQ36ivjjZdH0noQJgWNouenzpLmXNKnYaCsBfo3jY95m5iyjYQEiWkvkhJ3TJUAs7tQ1/kEpY7x/Kg==} 1326 1345 engines: {node: ^20.19.0 || >=22.12.0} 1327 1346 cpu: [arm64] 1328 1347 os: [linux] 1348 + libc: [glibc] 1329 1349 1330 1350 '@oxc-parser/binding-linux-arm64-musl@0.112.0': 1331 1351 resolution: {integrity: sha512-3R0iqjM3xYOZCnwgcxOQXH7hrz64/USDIuLbNTM1kZqQzRqaR4w7SwoWKU934zABo8d0op2oSwOp+CV3hZnM7A==} 1332 1352 engines: {node: ^20.19.0 || >=22.12.0} 1333 1353 cpu: [arm64] 1334 1354 os: [linux] 1355 + libc: [musl] 1335 1356 1336 1357 '@oxc-parser/binding-linux-arm64-musl@0.117.0': 1337 1358 resolution: {integrity: sha512-QagKTDF4lrz8bCXbUi39Uq5xs7C7itAseKm51f33U+Dyar9eJY/zGKqfME9mKLOiahX7Fc1J3xMWVS0AdDXLPg==} 1338 1359 engines: {node: ^20.19.0 || >=22.12.0} 1339 1360 cpu: [arm64] 1340 1361 os: [linux] 1362 + libc: [musl] 1341 1363 1342 1364 '@oxc-parser/binding-linux-ppc64-gnu@0.112.0': 1343 1365 resolution: {integrity: sha512-lAQf8PQxfgy7h0bmcfSVE3hg3qMueshPYULFsCrHM+8KefGZ9W+ZMvRyU33gLrB4w1O3Fz1orR0hmKMCRxXNrQ==} 1344 1366 engines: {node: ^20.19.0 || >=22.12.0} 1345 1367 cpu: [ppc64] 1346 1368 os: [linux] 1369 + libc: [glibc] 1347 1370 1348 1371 '@oxc-parser/binding-linux-ppc64-gnu@0.117.0': 1349 1372 resolution: {integrity: sha512-RPddpcE/0xxWaommWy0c5i/JdrXcXAkxBS2GOrAUh5LKmyCh03hpJedOAWszG4ADsKQwoUQQ1/tZVGRhZIWtKA==} 1350 1373 engines: {node: ^20.19.0 || >=22.12.0} 1351 1374 cpu: [ppc64] 1352 1375 os: [linux] 1376 + libc: [glibc] 1353 1377 1354 1378 '@oxc-parser/binding-linux-riscv64-gnu@0.112.0': 1355 1379 resolution: {integrity: sha512-2QlvQBUhHuAE3ezD4X3CAEKMXdfgInggQ5Bj/7gb5NcYP3GyfLTj7c+mMu+BRwfC9B3AXBNyqHWbqEuuUvZyRQ==} 1356 1380 engines: {node: ^20.19.0 || >=22.12.0} 1357 1381 cpu: [riscv64] 1358 1382 os: [linux] 1383 + libc: [glibc] 1359 1384 1360 1385 '@oxc-parser/binding-linux-riscv64-gnu@0.117.0': 1361 1386 resolution: {integrity: sha512-ur/WVZF9FSOiZGxyP+nfxZzuv6r5OJDYoVxJnUR7fM/hhXLh4V/be6rjbzm9KLCDBRwYCEKJtt+XXNccwd06IA==} 1362 1387 engines: {node: ^20.19.0 || >=22.12.0} 1363 1388 cpu: [riscv64] 1364 1389 os: [linux] 1390 + libc: [glibc] 1365 1391 1366 1392 '@oxc-parser/binding-linux-riscv64-musl@0.112.0': 1367 1393 resolution: {integrity: sha512-v06iu0osHszgqJ1dLQRb6leWFU1sjG/UQk4MoVBtE6ZPewgfTkby6G9II1SpEAf2onnAuQceVYxQH9iuU3NJqw==} 1368 1394 engines: {node: ^20.19.0 || >=22.12.0} 1369 1395 cpu: [riscv64] 1370 1396 os: [linux] 1397 + libc: [musl] 1371 1398 1372 1399 '@oxc-parser/binding-linux-riscv64-musl@0.117.0': 1373 1400 resolution: {integrity: sha512-ujGcAx8xAMvhy7X5sBFi3GXML1EtyORuJZ5z2T6UV3U416WgDX/4OCi3GnoteeenvxIf6JgP45B+YTHpt71vpA==} 1374 1401 engines: {node: ^20.19.0 || >=22.12.0} 1375 1402 cpu: [riscv64] 1376 1403 os: [linux] 1404 + libc: [musl] 1377 1405 1378 1406 '@oxc-parser/binding-linux-s390x-gnu@0.112.0': 1379 1407 resolution: {integrity: sha512-+5HhNHtxsdcd7+ljXFnn9FOoCNXJX3UPgIfIE6vdwS1HqdGNH6eAcVobuqGOp54l8pvcxDQA6F4cPswCgLrQfQ==} 1380 1408 engines: {node: ^20.19.0 || >=22.12.0} 1381 1409 cpu: [s390x] 1382 1410 os: [linux] 1411 + libc: [glibc] 1383 1412 1384 1413 '@oxc-parser/binding-linux-s390x-gnu@0.117.0': 1385 1414 resolution: {integrity: sha512-hbsfKjUwRjcMZZvvmpZSc+qS0bHcHRu8aV/I3Ikn9BzOA0ZAgUE7ctPtce5zCU7bM8dnTLi4sJ1Pi9YHdx6Urw==} 1386 1415 engines: {node: ^20.19.0 || >=22.12.0} 1387 1416 cpu: [s390x] 1388 1417 os: [linux] 1418 + libc: [glibc] 1389 1419 1390 1420 '@oxc-parser/binding-linux-x64-gnu@0.112.0': 1391 1421 resolution: {integrity: sha512-jKwO7ZLNkjxwg7FoCLw+fJszooL9yXRZsDN0AQ1AQUTWq1l8GH/2e44k68N3fcP19jl8O8jGpqLAZcQTYk6skA==} 1392 1422 engines: {node: ^20.19.0 || >=22.12.0} 1393 1423 cpu: [x64] 1394 1424 os: [linux] 1425 + libc: [glibc] 1395 1426 1396 1427 '@oxc-parser/binding-linux-x64-gnu@0.117.0': 1397 1428 resolution: {integrity: sha512-1QrTrf8rige7UPJrYuDKJLQOuJlgkt+nRSJLBMHWNm9TdivzP48HaK3f4q18EjNlglKtn03lgjMu4fryDm8X4A==} 1398 1429 engines: {node: ^20.19.0 || >=22.12.0} 1399 1430 cpu: [x64] 1400 1431 os: [linux] 1432 + libc: [glibc] 1401 1433 1402 1434 '@oxc-parser/binding-linux-x64-musl@0.112.0': 1403 1435 resolution: {integrity: sha512-TYqnuKV/p3eOc+N61E0961nA7DC+gaCeJ3+V2LcjJdTwFMdikqWL6uVk1jlrpUCBrozHDATVUKDZYH7r4FQYjQ==} 1404 1436 engines: {node: ^20.19.0 || >=22.12.0} 1405 1437 cpu: [x64] 1406 1438 os: [linux] 1439 + libc: [musl] 1407 1440 1408 1441 '@oxc-parser/binding-linux-x64-musl@0.117.0': 1409 1442 resolution: {integrity: sha512-gRvK6HPzF5ITRL68fqb2WYYs/hGviPIbkV84HWCgiJX+LkaOpp+HIHQl3zVZdyKHwopXToTbXbtx/oFjDjl8pg==} 1410 1443 engines: {node: ^20.19.0 || >=22.12.0} 1411 1444 cpu: [x64] 1412 1445 os: [linux] 1446 + libc: [musl] 1413 1447 1414 1448 '@oxc-parser/binding-openharmony-arm64@0.112.0': 1415 1449 resolution: {integrity: sha512-ZhrVmWFifVEFQX4XPwLoVFDHw9tAWH9p9vHsHFH+5uCKdfVR+jje4WxVo6YrokWCboGckoOzHq5KKMOcPZfkRg==} ··· 1564 1598 engines: {node: ^20.19.0 || >=22.12.0} 1565 1599 cpu: [arm64] 1566 1600 os: [linux] 1601 + libc: [glibc] 1567 1602 1568 1603 '@oxc-transform/binding-linux-arm64-gnu@0.117.0': 1569 1604 resolution: {integrity: sha512-mXbDfvDN0RZVg7v4LohNzU0kK3fMAZgkUKTkpFVgxEvzibEG5VpSznkypUwHI4a8U8pz+K6mGaLetX3Xt+CvvA==} 1570 1605 engines: {node: ^20.19.0 || >=22.12.0} 1571 1606 cpu: [arm64] 1572 1607 os: [linux] 1608 + libc: [glibc] 1573 1609 1574 1610 '@oxc-transform/binding-linux-arm64-musl@0.112.0': 1575 1611 resolution: {integrity: sha512-Lg6VOuSd3oXv7J0eGywgqh/086h+qQzIBOD+47pYKMTTJcbDe+f3h/RgGoMKJE5HhiwT5sH1aGEJfIfaYUiVSw==} 1576 1612 engines: {node: ^20.19.0 || >=22.12.0} 1577 1613 cpu: [arm64] 1578 1614 os: [linux] 1615 + libc: [musl] 1579 1616 1580 1617 '@oxc-transform/binding-linux-arm64-musl@0.117.0': 1581 1618 resolution: {integrity: sha512-ykxpPQp0eAcSmhy0Y3qKvdanHY4d8THPonDfmCoktUXb6r0X6qnjpJB3V+taN1wevW55bOEZd97kxtjTKjqhmg==} 1582 1619 engines: {node: ^20.19.0 || >=22.12.0} 1583 1620 cpu: [arm64] 1584 1621 os: [linux] 1622 + libc: [musl] 1585 1623 1586 1624 '@oxc-transform/binding-linux-ppc64-gnu@0.112.0': 1587 1625 resolution: {integrity: sha512-PXzmj82o1moA4IGphYImTRgc2youTi4VRfyFX3CHwLjxPcQ5JtcsgbDt4QUdOzXZ+zC07s5jf2ZzhRapEOlj2w==} 1588 1626 engines: {node: ^20.19.0 || >=22.12.0} 1589 1627 cpu: [ppc64] 1590 1628 os: [linux] 1629 + libc: [glibc] 1591 1630 1592 1631 '@oxc-transform/binding-linux-ppc64-gnu@0.117.0': 1593 1632 resolution: {integrity: sha512-Rvspti4Kr7eq6zSrURK5WjscfWQPvmy/KjJZV45neRKW8RLonE3r9+NgrwSLGoHvQ3F24fbqlkplox1RtlhH5A==} 1594 1633 engines: {node: ^20.19.0 || >=22.12.0} 1595 1634 cpu: [ppc64] 1596 1635 os: [linux] 1636 + libc: [glibc] 1597 1637 1598 1638 '@oxc-transform/binding-linux-riscv64-gnu@0.112.0': 1599 1639 resolution: {integrity: sha512-vhJsMsVH/6xwa3bt1LGts33FXUkGjaEGDwsRyp4lIfOjSfQVWMtCmWMFNaA0dW9FVWdD2Gt2fSFBSZ+azDxlpg==} 1600 1640 engines: {node: ^20.19.0 || >=22.12.0} 1601 1641 cpu: [riscv64] 1602 1642 os: [linux] 1643 + libc: [glibc] 1603 1644 1604 1645 '@oxc-transform/binding-linux-riscv64-gnu@0.117.0': 1605 1646 resolution: {integrity: sha512-Dr2ZW9ZZ4l1eQ5JUEUY3smBh4JFPCPuybWaDZTLn3ADZjyd8ZtNXEjeMT8rQbbhbgSL9hEgbwaqraole3FNThQ==} 1606 1647 engines: {node: ^20.19.0 || >=22.12.0} 1607 1648 cpu: [riscv64] 1608 1649 os: [linux] 1650 + libc: [glibc] 1609 1651 1610 1652 '@oxc-transform/binding-linux-riscv64-musl@0.112.0': 1611 1653 resolution: {integrity: sha512-cXWFb7z+2IjFUEcXtRwluq9oEG5qnyFCjiu3SWrgYNcWwPdHusv3I/7K5/CTbbi4StoZ5txbi7/iSfDHNyWuRw==} 1612 1654 engines: {node: ^20.19.0 || >=22.12.0} 1613 1655 cpu: [riscv64] 1614 1656 os: [linux] 1657 + libc: [musl] 1615 1658 1616 1659 '@oxc-transform/binding-linux-riscv64-musl@0.117.0': 1617 1660 resolution: {integrity: sha512-oD1Bnes1bIC3LVBSrWEoSUBj6fvatESPwAVWfJVGVQlqWuOs/ZBn1e4Nmbipo3KGPHK7DJY75r/j7CQCxhrOFQ==} 1618 1661 engines: {node: ^20.19.0 || >=22.12.0} 1619 1662 cpu: [riscv64] 1620 1663 os: [linux] 1664 + libc: [musl] 1621 1665 1622 1666 '@oxc-transform/binding-linux-s390x-gnu@0.112.0': 1623 1667 resolution: {integrity: sha512-eEFu4SRqJTJ20/88KRWmp+jpHKAw0Y1DsnSgpEeXyBIIcsOaLIUMU/TfYWUmqRbvbMV9rmOmI3kp5xWYUq6kSQ==} 1624 1668 engines: {node: ^20.19.0 || >=22.12.0} 1625 1669 cpu: [s390x] 1626 1670 os: [linux] 1671 + libc: [glibc] 1627 1672 1628 1673 '@oxc-transform/binding-linux-s390x-gnu@0.117.0': 1629 1674 resolution: {integrity: sha512-qT//IAPLvse844t99Kff5j055qEbXfwzWgvCMb0FyjisnB8foy25iHZxZIocNBe6qwrCYWUP1M8rNrB/WyfS1Q==} 1630 1675 engines: {node: ^20.19.0 || >=22.12.0} 1631 1676 cpu: [s390x] 1632 1677 os: [linux] 1678 + libc: [glibc] 1633 1679 1634 1680 '@oxc-transform/binding-linux-x64-gnu@0.112.0': 1635 1681 resolution: {integrity: sha512-ST1MDT+TlOyZ1c5btrGinRSUW2Jf4Pa+0gdKwsyjDSOC3dxy2ZNkN3mosTf4ywc3J+mxfYKqtjs7zSwHz03ILA==} 1636 1682 engines: {node: ^20.19.0 || >=22.12.0} 1637 1683 cpu: [x64] 1638 1684 os: [linux] 1685 + libc: [glibc] 1639 1686 1640 1687 '@oxc-transform/binding-linux-x64-gnu@0.117.0': 1641 1688 resolution: {integrity: sha512-2YEO5X+KgNzFqRVO5dAkhjcI5gwxus4NSWVl/+cs2sI6P0MNPjqE3VWPawl4RTC11LvetiiZdHcujUCPM8aaUw==} 1642 1689 engines: {node: ^20.19.0 || >=22.12.0} 1643 1690 cpu: [x64] 1644 1691 os: [linux] 1692 + libc: [glibc] 1645 1693 1646 1694 '@oxc-transform/binding-linux-x64-musl@0.112.0': 1647 1695 resolution: {integrity: sha512-ISQoA3pD4cyTGpf9sXXeerH6pL2L6EIpdy6oAy2ttkswyVFDyQNVOVIGIdLZDgbpmqGljxZnWqt/J/N68pQaig==} 1648 1696 engines: {node: ^20.19.0 || >=22.12.0} 1649 1697 cpu: [x64] 1650 1698 os: [linux] 1699 + libc: [musl] 1651 1700 1652 1701 '@oxc-transform/binding-linux-x64-musl@0.117.0': 1653 1702 resolution: {integrity: sha512-3wqWbTSaIFZvDr1aqmTul4cg8PRWYh6VC52E8bLI7ytgS/BwJLW+sDUU2YaGIds4sAf/1yKeJRmudRCDPW9INg==} 1654 1703 engines: {node: ^20.19.0 || >=22.12.0} 1655 1704 cpu: [x64] 1656 1705 os: [linux] 1706 + libc: [musl] 1657 1707 1658 1708 '@oxc-transform/binding-openharmony-arm64@0.112.0': 1659 1709 resolution: {integrity: sha512-UOGVrGIv7yLJovyEXEyUTADuLq98vd/cbMHFLJweRXD+11I8Tn4jASi4WzdsN8C3BVYGRHrXH2NlSBmhz33a4g==} ··· 1742 1792 engines: {node: '>= 10.0.0'} 1743 1793 cpu: [arm] 1744 1794 os: [linux] 1795 + libc: [glibc] 1745 1796 1746 1797 '@parcel/watcher-linux-arm-musl@2.5.6': 1747 1798 resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} 1748 1799 engines: {node: '>= 10.0.0'} 1749 1800 cpu: [arm] 1750 1801 os: [linux] 1802 + libc: [musl] 1751 1803 1752 1804 '@parcel/watcher-linux-arm64-glibc@2.5.6': 1753 1805 resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} 1754 1806 engines: {node: '>= 10.0.0'} 1755 1807 cpu: [arm64] 1756 1808 os: [linux] 1809 + libc: [glibc] 1757 1810 1758 1811 '@parcel/watcher-linux-arm64-musl@2.5.6': 1759 1812 resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} 1760 1813 engines: {node: '>= 10.0.0'} 1761 1814 cpu: [arm64] 1762 1815 os: [linux] 1816 + libc: [musl] 1763 1817 1764 1818 '@parcel/watcher-linux-x64-glibc@2.5.6': 1765 1819 resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} 1766 1820 engines: {node: '>= 10.0.0'} 1767 1821 cpu: [x64] 1768 1822 os: [linux] 1823 + libc: [glibc] 1769 1824 1770 1825 '@parcel/watcher-linux-x64-musl@2.5.6': 1771 1826 resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} 1772 1827 engines: {node: '>= 10.0.0'} 1773 1828 cpu: [x64] 1774 1829 os: [linux] 1830 + libc: [musl] 1775 1831 1776 1832 '@parcel/watcher-wasm@2.5.6': 1777 1833 resolution: {integrity: sha512-byAiBZ1t3tXQvc8dMD/eoyE7lTXYorhn+6uVW5AC+JGI1KtJC/LvDche5cfUE+qiefH+Ybq0bUCJU0aB1cSHUA==} ··· 1948 2004 resolution: {integrity: sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==} 1949 2005 cpu: [arm] 1950 2006 os: [linux] 2007 + libc: [glibc] 1951 2008 1952 2009 '@rollup/rollup-linux-arm-musleabihf@4.60.2': 1953 2010 resolution: {integrity: sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==} 1954 2011 cpu: [arm] 1955 2012 os: [linux] 2013 + libc: [musl] 1956 2014 1957 2015 '@rollup/rollup-linux-arm64-gnu@4.60.2': 1958 2016 resolution: {integrity: sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==} 1959 2017 cpu: [arm64] 1960 2018 os: [linux] 2019 + libc: [glibc] 1961 2020 1962 2021 '@rollup/rollup-linux-arm64-musl@4.60.2': 1963 2022 resolution: {integrity: sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==} 1964 2023 cpu: [arm64] 1965 2024 os: [linux] 2025 + libc: [musl] 1966 2026 1967 2027 '@rollup/rollup-linux-loong64-gnu@4.60.2': 1968 2028 resolution: {integrity: sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==} 1969 2029 cpu: [loong64] 1970 2030 os: [linux] 2031 + libc: [glibc] 1971 2032 1972 2033 '@rollup/rollup-linux-loong64-musl@4.60.2': 1973 2034 resolution: {integrity: sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==} 1974 2035 cpu: [loong64] 1975 2036 os: [linux] 2037 + libc: [musl] 1976 2038 1977 2039 '@rollup/rollup-linux-ppc64-gnu@4.60.2': 1978 2040 resolution: {integrity: sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==} 1979 2041 cpu: [ppc64] 1980 2042 os: [linux] 2043 + libc: [glibc] 1981 2044 1982 2045 '@rollup/rollup-linux-ppc64-musl@4.60.2': 1983 2046 resolution: {integrity: sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==} 1984 2047 cpu: [ppc64] 1985 2048 os: [linux] 2049 + libc: [musl] 1986 2050 1987 2051 '@rollup/rollup-linux-riscv64-gnu@4.60.2': 1988 2052 resolution: {integrity: sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==} 1989 2053 cpu: [riscv64] 1990 2054 os: [linux] 2055 + libc: [glibc] 1991 2056 1992 2057 '@rollup/rollup-linux-riscv64-musl@4.60.2': 1993 2058 resolution: {integrity: sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==} 1994 2059 cpu: [riscv64] 1995 2060 os: [linux] 2061 + libc: [musl] 1996 2062 1997 2063 '@rollup/rollup-linux-s390x-gnu@4.60.2': 1998 2064 resolution: {integrity: sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==} 1999 2065 cpu: [s390x] 2000 2066 os: [linux] 2067 + libc: [glibc] 2001 2068 2002 2069 '@rollup/rollup-linux-x64-gnu@4.60.2': 2003 2070 resolution: {integrity: sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==} 2004 2071 cpu: [x64] 2005 2072 os: [linux] 2073 + libc: [glibc] 2006 2074 2007 2075 '@rollup/rollup-linux-x64-musl@4.60.2': 2008 2076 resolution: {integrity: sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==} 2009 2077 cpu: [x64] 2010 2078 os: [linux] 2079 + libc: [musl] 2011 2080 2012 2081 '@rollup/rollup-openbsd-x64@4.60.2': 2013 2082 resolution: {integrity: sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==} ··· 2100 2169 engines: {node: '>= 20'} 2101 2170 cpu: [arm64] 2102 2171 os: [linux] 2172 + libc: [glibc] 2103 2173 2104 2174 '@tailwindcss/oxide-linux-arm64-musl@4.2.4': 2105 2175 resolution: {integrity: sha512-bBADEGAbo4ASnppIziaQJelekCxdMaxisrk+fB7Thit72IBnALp9K6ffA2G4ruj90G9XRS2VQ6q2bCKbfFV82g==} 2106 2176 engines: {node: '>= 20'} 2107 2177 cpu: [arm64] 2108 2178 os: [linux] 2179 + libc: [musl] 2109 2180 2110 2181 '@tailwindcss/oxide-linux-x64-gnu@4.2.4': 2111 2182 resolution: {integrity: sha512-7Mx25E4WTfnht0TVRTyC00j3i0M+EeFe7wguMDTlX4mRxafznw0CA8WJkFjWYH5BlgELd1kSjuU2JiPnNZbJDA==} 2112 2183 engines: {node: '>= 20'} 2113 2184 cpu: [x64] 2114 2185 os: [linux] 2186 + libc: [glibc] 2115 2187 2116 2188 '@tailwindcss/oxide-linux-x64-musl@4.2.4': 2117 2189 resolution: {integrity: sha512-2wwJRF7nyhOR0hhHoChc04xngV3iS+akccHTGtz965FwF0up4b2lOdo6kI1EbDaEXKgvcrFBYcYQQ/rrnWFVfA==} 2118 2190 engines: {node: '>= 20'} 2119 2191 cpu: [x64] 2120 2192 os: [linux] 2193 + libc: [musl] 2121 2194 2122 2195 '@tailwindcss/oxide-wasm32-wasi@4.2.4': 2123 2196 resolution: {integrity: sha512-FQsqApeor8Fo6gUEklzmaa9994orJZZDBAlQpK2Mq+DslRKFJeD6AjHpBQ0kZFQohVr8o85PPh8eOy86VlSCmw==} ··· 2192 2265 2193 2266 '@types/web-bluetooth@0.0.21': 2194 2267 resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} 2268 + 2269 + '@types/web-push@3.6.4': 2270 + resolution: {integrity: sha512-GnJmSr40H3RAnj0s34FNTcJi1hmWFV5KXugE0mYWnYhgTAHLJ/dJKAwDmvPJYMke0RplY2XE9LnM4hqSqKIjhQ==} 2195 2271 2196 2272 '@types/webidl-conversions@7.0.3': 2197 2273 resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} ··· 2490 2566 resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} 2491 2567 engines: {node: '>=10'} 2492 2568 2569 + asn1.js@5.4.1: 2570 + resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==} 2571 + 2493 2572 assertion-error@2.0.1: 2494 2573 resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} 2495 2574 engines: {node: '>=12'} ··· 2657 2736 2658 2737 birpc@4.0.0: 2659 2738 resolution: {integrity: sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==} 2739 + 2740 + bn.js@4.12.3: 2741 + resolution: {integrity: sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==} 2660 2742 2661 2743 boolbase@1.0.0: 2662 2744 resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} ··· 2685 2767 resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} 2686 2768 engines: {node: '>=8.0.0'} 2687 2769 2770 + buffer-equal-constant-time@1.0.1: 2771 + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} 2772 + 2688 2773 buffer-from@1.1.2: 2689 2774 resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} 2690 2775 ··· 2984 3069 2985 3070 eastasianwidth@0.2.0: 2986 3071 resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} 3072 + 3073 + ecdsa-sig-formatter@1.0.11: 3074 + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} 2987 3075 2988 3076 editorconfig@1.0.7: 2989 3077 resolution: {integrity: sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==} ··· 3335 3423 resolution: {integrity: sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw==} 3336 3424 engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} 3337 3425 3426 + http_ece@1.2.0: 3427 + resolution: {integrity: sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA==} 3428 + engines: {node: '>=16'} 3429 + 3338 3430 https-proxy-agent@7.0.6: 3339 3431 resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} 3340 3432 engines: {node: '>= 14'} ··· 3510 3602 resolution: {integrity: sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA==} 3511 3603 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 3512 3604 3605 + jwa@2.0.1: 3606 + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} 3607 + 3608 + jws@4.0.1: 3609 + resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} 3610 + 3513 3611 keyv@4.5.4: 3514 3612 resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} 3515 3613 ··· 3574 3672 engines: {node: '>= 12.0.0'} 3575 3673 cpu: [arm64] 3576 3674 os: [linux] 3675 + libc: [glibc] 3577 3676 3578 3677 lightningcss-linux-arm64-musl@1.32.0: 3579 3678 resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} 3580 3679 engines: {node: '>= 12.0.0'} 3581 3680 cpu: [arm64] 3582 3681 os: [linux] 3682 + libc: [musl] 3583 3683 3584 3684 lightningcss-linux-x64-gnu@1.32.0: 3585 3685 resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} 3586 3686 engines: {node: '>= 12.0.0'} 3587 3687 cpu: [x64] 3588 3688 os: [linux] 3689 + libc: [glibc] 3589 3690 3590 3691 lightningcss-linux-x64-musl@1.32.0: 3591 3692 resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} 3592 3693 engines: {node: '>= 12.0.0'} 3593 3694 cpu: [x64] 3594 3695 os: [linux] 3696 + libc: [musl] 3595 3697 3596 3698 lightningcss-win32-arm64-msvc@1.32.0: 3597 3699 resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} ··· 3705 3807 resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} 3706 3808 engines: {node: '>=12'} 3707 3809 3810 + minimalistic-assert@1.0.1: 3811 + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} 3812 + 3708 3813 minimatch@10.2.5: 3709 3814 resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} 3710 3815 engines: {node: 18 || 20 || >=22} ··· 3716 3821 minimatch@9.0.9: 3717 3822 resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} 3718 3823 engines: {node: '>=16 || 14 >=14.17'} 3824 + 3825 + minimist@1.2.8: 3826 + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} 3719 3827 3720 3828 minipass@7.1.3: 3721 3829 resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} ··· 4330 4438 4331 4439 safe-buffer@5.2.1: 4332 4440 resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 4441 + 4442 + safer-buffer@2.1.2: 4443 + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} 4333 4444 4334 4445 sax@1.6.0: 4335 4446 resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} ··· 4968 5079 peerDependenciesMeta: 4969 5080 typescript: 4970 5081 optional: true 5082 + 5083 + web-push@3.6.7: 5084 + resolution: {integrity: sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A==} 5085 + engines: {node: '>= 16'} 5086 + hasBin: true 4971 5087 4972 5088 webidl-conversions@3.0.1: 4973 5089 resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} ··· 6990 7106 6991 7107 '@types/web-bluetooth@0.0.21': {} 6992 7108 7109 + '@types/web-push@3.6.4': 7110 + dependencies: 7111 + '@types/node': 25.6.0 7112 + 6993 7113 '@types/webidl-conversions@7.0.3': 6994 7114 optional: true 6995 7115 ··· 7410 7530 dependencies: 7411 7531 tslib: 2.8.1 7412 7532 7533 + asn1.js@5.4.1: 7534 + dependencies: 7535 + bn.js: 4.12.3 7536 + inherits: 2.0.4 7537 + minimalistic-assert: 1.0.1 7538 + safer-buffer: 2.1.2 7539 + 7413 7540 assertion-error@2.0.1: {} 7414 7541 7415 7542 ast-kit@2.2.0: ··· 7521 7648 7522 7649 birpc@4.0.0: {} 7523 7650 7651 + bn.js@4.12.3: {} 7652 + 7524 7653 boolbase@1.0.0: {} 7525 7654 7526 7655 brace-expansion@2.1.0: ··· 7547 7676 optional: true 7548 7677 7549 7678 buffer-crc32@1.0.0: {} 7679 + 7680 + buffer-equal-constant-time@1.0.1: {} 7550 7681 7551 7682 buffer-from@1.1.2: {} 7552 7683 ··· 7822 7953 7823 7954 eastasianwidth@0.2.0: {} 7824 7955 7956 + ecdsa-sig-formatter@1.0.11: 7957 + dependencies: 7958 + safe-buffer: 5.2.1 7959 + 7825 7960 editorconfig@1.0.7: 7826 7961 dependencies: 7827 7962 '@one-ini/wasm': 0.1.1 ··· 8224 8359 8225 8360 http-shutdown@1.2.2: {} 8226 8361 8362 + http_ece@1.2.0: {} 8363 + 8227 8364 https-proxy-agent@7.0.6: 8228 8365 dependencies: 8229 8366 agent-base: 7.1.4 ··· 8371 8508 espree: 9.6.1 8372 8509 semver: 7.7.4 8373 8510 8511 + jwa@2.0.1: 8512 + dependencies: 8513 + buffer-equal-constant-time: 1.0.1 8514 + ecdsa-sig-formatter: 1.0.11 8515 + safe-buffer: 5.2.1 8516 + 8517 + jws@4.0.1: 8518 + dependencies: 8519 + jwa: 2.0.1 8520 + safe-buffer: 5.2.1 8521 + 8374 8522 keyv@4.5.4: 8375 8523 dependencies: 8376 8524 json-buffer: 3.0.1 ··· 8551 8699 8552 8700 mimic-fn@4.0.0: {} 8553 8701 8702 + minimalistic-assert@1.0.1: {} 8703 + 8554 8704 minimatch@10.2.5: 8555 8705 dependencies: 8556 8706 brace-expansion: 5.0.5 ··· 8562 8712 minimatch@9.0.9: 8563 8713 dependencies: 8564 8714 brace-expansion: 2.1.0 8715 + 8716 + minimist@1.2.8: {} 8565 8717 8566 8718 minipass@7.1.3: {} 8567 8719 ··· 9465 9617 9466 9618 safe-buffer@5.2.1: {} 9467 9619 9620 + safer-buffer@2.1.2: {} 9621 + 9468 9622 sax@1.6.0: {} 9469 9623 9470 9624 scule@1.3.0: {} ··· 10073 10227 '@vue/shared': 3.5.33 10074 10228 optionalDependencies: 10075 10229 typescript: 6.0.3 10230 + 10231 + web-push@3.6.7: 10232 + dependencies: 10233 + asn1.js: 5.4.1 10234 + http_ece: 1.2.0 10235 + https-proxy-agent: 7.0.6 10236 + jws: 4.0.1 10237 + minimist: 1.2.8 10238 + transitivePeerDependencies: 10239 + - supports-color 10076 10240 10077 10241 webidl-conversions@3.0.1: {} 10078 10242
+1 -1
server/api/subscription/.post.ts
··· 8 8 const subscription = await Subscriptions.updateOrAdd(userId, body); 9 9 if (subscription instanceof Error) throw subscription; 10 10 11 - SubscriptionEventStream.sendUpdate(userId); 11 + await SubscriptionEventStream.sendUpdate(userId); 12 12 return subscription; 13 13 }, 14 14 );
+4 -6
server/tasks/notify/push.ts
··· 1 + import {sendMsg} from "#server/utils/subscriptions"; 2 + 1 3 export default defineTask({ 2 4 async run(_event) { 3 - await Todos.sendMsg(); 4 - 5 - const result = { 6 - sent: 10, 7 - }; 5 + await sendMsg(); 8 6 9 - return { result }; 7 + return { result: "Success" }; 10 8 }, 11 9 });
-1
server/types/web-push.d.ts
··· 1 - declare module "web-push";
+33 -49
server/utils/db/subscription.ts
··· 1 - import type { NuxtError } from "nuxt/app"; 1 + import type {NuxtError} from "nuxt/app"; 2 2 import webpush from "web-push"; 3 - import type { PushSubscriptionJSON } from "~~/shared/types"; 3 + import type {PushSubscriptionJSON} from "~~/shared/types"; 4 4 5 5 function getKey(userId: string): string { 6 - return `subscriptions:${userId}`; 6 + return `subscriptions:${userId}`; 7 7 } 8 8 9 9 webpush.setVapidDetails( 10 - "mailto:you@example.com", 11 - process.env.NUXT_PUBLIC_SUBSCRIPTIONS_PUBLIC_KEY!, 12 - process.env.SUBSCRIPTIONS_PRIVATE_KEY!, 10 + process.env.SUBSCRIPTIONS_MAIL!, 11 + process.env.NUXT_PUBLIC_SUBSCRIPTIONS_PUBLIC_KEY!, 12 + process.env.SUBSCRIPTIONS_PRIVATE_KEY!, 13 13 ); 14 14 15 15 export const Subscriptions = { 16 - async getAll(userId: string): Promise<PushSubscriptionJSON[]> { 17 - const storage = useStorage(); 18 - return (await storage.get<PushSubscriptionJSON[]>(getKey(userId))) ?? []; 19 - }, 20 - async updateOrAdd( 21 - userId: string, 22 - subscription: PushSubscriptionJSON, 23 - ): Promise<PushSubscriptionJSON | NuxtError> { 24 - const storage = useStorage(); 25 - const subscriptions = await Subscriptions.getAll(userId); 26 - 27 - const exists = subscriptions.find( 28 - (s) => 29 - s.endpoint === subscription.endpoint || 30 - (s.keys.p256dh === subscription.keys.p256dh && 31 - s.keys.auth === subscription.keys.auth), 32 - ); 33 - 34 - if (!exists) { 35 - subscriptions.push(subscription); 36 - await storage.set(getKey(userId), subscriptions); 37 - return subscription; 38 - } 16 + async getAll(userId: string): Promise<PushSubscriptionJSON[]> { 17 + const storage = useStorage(); 18 + return (await storage.get<PushSubscriptionJSON[]>(getKey(userId))) ?? []; 19 + }, 20 + async updateOrAdd( 21 + userId: string, 22 + subscription: PushSubscriptionJSON, 23 + ): Promise<PushSubscriptionJSON | NuxtError> { 24 + const storage = useStorage(); 25 + const subscriptions = await Subscriptions.getAll(userId); 39 26 40 - return createError({ 41 - status: 409, 42 - statusMessage: "Conflict", 43 - message: `Subscription already exists`, 44 - }); 45 - }, 46 - async sendNotification(userId: string, title: string, body: string) { 47 - const subscriptions = await Subscriptions.getAll(userId); 27 + const exists = subscriptions.find( 28 + (s) => 29 + s.endpoint === subscription.endpoint || 30 + (s.keys.p256dh === subscription.keys.p256dh && 31 + s.keys.auth === subscription.keys.auth), 32 + ); 48 33 49 - const payload = JSON.stringify({ 50 - title: title, 51 - body: body, 52 - }); 34 + if (!exists) { 35 + subscriptions.push(subscription); 36 + await storage.set(getKey(userId), subscriptions); 37 + return subscription; 38 + } 53 39 54 - for (const sub of subscriptions) { 55 - try { 56 - await webpush.sendNotification(sub, payload); 57 - } catch (err) { 58 - console.error("Push failed", err); 59 - } 60 - } 61 - }, 40 + return createError({ 41 + status: 201, 42 + statusMessage: "Already subscribed", 43 + message: `Subscription already exists`, 44 + }); 45 + } 62 46 };
-37
server/utils/db/todos.ts
··· 13 13 return (await storage.get<Todo[]>(getKey(userId))) ?? []; 14 14 }, 15 15 16 - async sendMsg(): Promise<void> { 17 - const keys = await useStorage().getKeys(); 18 - const filteredKeys = keys.filter((key) => key.startsWith("todos:")); 19 - 20 - filteredKeys.forEach(async (key) => { 21 - const todos = await useStorage().get<Todo[]>(key); 22 - 23 - todos?.forEach((todo) => { 24 - if (todo.time?.type === "point") { 25 - const date = new Date(todo.time.time); 26 - const now = new Date(); 27 - 28 - const sameMinute = 29 - date.getUTCFullYear() === now.getUTCFullYear() && 30 - date.getUTCMonth() === now.getUTCMonth() && 31 - date.getUTCDate() === now.getUTCDate() && 32 - date.getUTCHours() === now.getUTCHours() && 33 - date.getUTCMinutes() === now.getUTCMinutes(); 34 - 35 - if (sameMinute) { 36 - Subscriptions.sendNotification( 37 - key.substring(6), 38 - todo.title, 39 - todo.note, 40 - ); 41 - } 42 - } 43 - }); 44 - }); 45 - }, 46 - 47 - async getAllWithTimeStamp(userId: string): Promise<Todo[]> { 48 - const todos = await Todos.getAll(userId); 49 - 50 - return todos.filter((todo) => todo.time?.type === "range"); 51 - }, 52 - 53 16 async move( 54 17 userId: string, 55 18 toMove: UUID,
+50
server/utils/subscriptions.ts
··· 1 + import type {Todo} from "#shared/types"; 2 + import webpush from "web-push"; 3 + 4 + export async function sendMsg(): Promise<void> { 5 + const keys = await useStorage().getKeys(); 6 + const filteredKeys = keys.filter((key) => key.startsWith("todos:")); 7 + 8 + for (const key of filteredKeys) { 9 + const todos = await useStorage().get<Todo[]>(key); 10 + 11 + for (const todo of todos!) { 12 + if (todo.time?.type === "point") { 13 + const date = new Date(todo.time.time); 14 + const now = new Date(); 15 + 16 + const sameMinute = 17 + date.getUTCFullYear() === now.getUTCFullYear() && 18 + date.getUTCMonth() === now.getUTCMonth() && 19 + date.getUTCDate() === now.getUTCDate() && 20 + date.getUTCHours() === now.getUTCHours() && 21 + date.getUTCMinutes() === now.getUTCMinutes(); 22 + 23 + if (sameMinute) { 24 + await sendNotification( 25 + key.substring(6), 26 + todo.title, 27 + todo.note, 28 + ); 29 + } 30 + } 31 + } 32 + } 33 + } 34 + 35 + async function sendNotification(userId: string, title: string, body: string) { 36 + const subscriptions = await Subscriptions.getAll(userId); 37 + 38 + const payload = JSON.stringify({ 39 + title: title, 40 + body: body, 41 + }); 42 + 43 + for (const sub of subscriptions) { 44 + try { 45 + await webpush.sendNotification(sub, payload); 46 + } catch (err) { 47 + console.error("Push failed", err); 48 + } 49 + } 50 + }