upd: web push

Deskripsi:
- install package
- table database
- nb : masih blm bisa

No Issues
This commit is contained in:
amel
2024-09-18 11:48:57 +08:00
parent 6868085bb1
commit bc0f27a4fc
16 changed files with 581 additions and 3 deletions

View File

@@ -33,6 +33,7 @@
"@tiptap/react": "^2.4.0",
"@tiptap/starter-kit": "^2.4.0",
"@types/lodash": "^4.17.6",
"@types/web-push": "^3.6.3",
"dayjs": "^1.11.11",
"echarts": "^5.5.1",
"echarts-for-react": "^3.0.2",
@@ -52,6 +53,7 @@
"readdirp": "^3.6.0",
"recharts": "2",
"rrule": "^2.8.1",
"web-push": "^3.6.7",
"yargs": "^17.7.2"
},
"devDependencies": {

View File

@@ -478,3 +478,8 @@ model ColorTheme {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Subscription {
id String @id @default(cuid())
data Json
}

BIN
public/icon-192x192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

77
public/wibu_worker.js Normal file
View File

@@ -0,0 +1,77 @@
self.addEventListener('install', (event) => {
event.waitUntil(self.skipWaiting());
console.log('Service worker installing...');
});
self.addEventListener('activate', (event) => {
event.waitUntil(self.clients.claim());
console.log('Service worker activating...');
});
self.addEventListener('push', function (event) {
console.log('Push event received:', event);
let title = "Sistem Desa Mandiri";
let options = {
body: "Default notification body",
icon: '/icon-192x192.png',
badge: '/icon-192x192.png',
image: '/icon-192x192.png',
vibrate: [100, 50, 100],
data: {
dateOfArrival: Date.now(),
primaryKey: '2',
},
};
if (event.data) {
try {
const data = event.data.json();
title = data.title || title;
options.body = data.body || options.body;
options.icon = data.icon || options.icon;
options.badge = data.badge || options.badge;
options.image = data.image || options.image;
options.data = {
...options.data,
...data.data, // Merging additional data from the event
};
} catch (e) {
console.error("Error parsing push event data:", e);
}
} else {
console.warn("Push event has no data.");
}
event.waitUntil(
self.registration.showNotification(title, options)
.then(() => console.log('Notification shown.', JSON.stringify(options, null, 2)))
.catch(err => {
console.error("Error showing notification:", err);
})
);
});
self.addEventListener('notificationclick', function (event) {
console.log('Notification click received.');
event.notification.close(); // Close the notification
event.waitUntil(
clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clientList) => {
for (const client of clientList) {
if (client.url.includes('http://localhost:3005') && 'focus' in client) {
return client.focus();
}
}
if (clients.openWindow) {
return clients.openWindow('http://localhost:3005');
}
}).catch(err => {
console.error("Error handling notification click:", err);
})
);
});

View File

@@ -0,0 +1,17 @@
import { NotificationManager } from "@/module/_global/components/notification_manager";
const publicKey = process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY!;
console.log(
process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY,
process.env.VAPID_PRIVATE_KEY
);
export default function Page() {
return (
<div>
{/* <PushNotificationManager publicKey={publicKey} /> */}
<NotificationManager publicKey={publicKey} />
</div>
);
}

View File

@@ -0,0 +1,6 @@
import prisma from "@/lib/prisma";
export async function GET() {
const sub = await prisma.subscription.findMany();
return new Response(JSON.stringify({ data: sub }));
}

View File

@@ -0,0 +1,70 @@
import webpush from "web-push";
import prisma from "@/lib/prisma";
// Set VAPID details for web-push
webpush.setVapidDetails(
"mailto:bip.production.js@gmail.com",
process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY!,
process.env.VAPID_PRIVATE_KEY!
);
export async function POST() {
try {
// Fetch all subscriptions from your database
const subscriptions = await prisma.subscription.findMany();
if (!subscriptions || subscriptions.length === 0) {
console.error("No subscriptions available to send notification");
return new Response("No subscriptions available", { status: 400 });
}
// Notification payload
const notificationPayload = JSON.stringify({
title: "Test Notification",
body: "This is a test notification | makuro",
icon: "/icon-192x192.png",
badge: "/icon-192x192.png",
image: "/icon-192x192.png",
});
let successCount = 0;
let failureCount = 0;
// Loop through all subscriptions and send notifications
for (const sub of subscriptions) {
try {
const subscriptionData = sub.data as any;
await webpush.sendNotification(subscriptionData, notificationPayload);
console.log(
`Notification sent successfully to ${subscriptionData.endpoint}`
);
successCount++;
} catch (error: any) {
console.error(
`Error sending push notification to subscription ${sub.id}:`,
error
);
failureCount++;
}
}
// Return a success or failure response
return new Response(
JSON.stringify({
message: `Notifications sent: ${successCount}, Failed: ${failureCount}`,
success: failureCount === 0
}),
{ status: 200 }
);
} catch (error: any) {
console.error("Error during notification process:", error);
return new Response(
JSON.stringify({
success: false,
error: error.message || "Failed to process notifications"
}),
{ status: 500 }
);
}
}

View File

@@ -0,0 +1,25 @@
import webpush from "web-push";
import prisma from "@/lib/prisma";
webpush.setVapidDetails(
"mailto:bip.production.js@gmail.com",
process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY!,
process.env.VAPID_PRIVATE_KEY!
);
export async function POST(req: Request) {
const { sub } = await req.json();
console.log(sub);
if (!sub || !sub.endpoint) {
console.error("Invalid subscription object");
return new Response("Invalid subscription object", { status: 400 });
}
const data = await prisma.subscription.create({
data: {
id: sub.keys.auth,
data: sub
}
});
return new Response(JSON.stringify({ data }));
}

View File

@@ -0,0 +1,11 @@
import prisma from "@/lib/prisma";
export async function POST(req: Request) {
const { sub } = await req.json();
const data = await prisma.subscription.delete({
where: {
id: sub.keys.auth
}
});
return new Response(JSON.stringify({ data }));
}

29
src/app/manifest.ts Normal file
View File

@@ -0,0 +1,29 @@
import type { MetadataRoute } from "next";
export default function manifest(): MetadataRoute.Manifest {
return {
name: "Sistem Desa Mandiri",
short_name: "SDM",
description: "Sistem Desa Mandiri",
start_url: "/",
display: "standalone",
background_color: "#ffffff",
theme_color: "#000000",
icons: [
{
src: "/icon-192x192.png",
sizes: "192x192",
type: "image/png"
},
{
src: "/icon-512x512.png",
sizes: "512x512",
type: "image/png"
}
],
serviceworker: {
src: "/wibu_worker.js"
},
};
}

15
src/lib/prisma.ts Normal file
View File

@@ -0,0 +1,15 @@
import { PrismaClient } from '@prisma/client'
const prismaClientSingleton = () => {
return new PrismaClient()
}
declare const globalThis: {
prismaGlobal: ReturnType<typeof prismaClientSingleton>;
} & typeof global;
const prisma = globalThis.prismaGlobal ?? prismaClientSingleton()
export default prisma
if (process.env.NODE_ENV !== 'production') globalThis.prismaGlobal = prisma

View File

@@ -0,0 +1,13 @@
export const urlB64ToUint8Array = (base64String: string) => {
const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
};

46
src/lib/usePWAInstall.ts Normal file
View File

@@ -0,0 +1,46 @@
import { useState, useEffect } from "react";
export function usePWAInstall() {
const [deferredPrompt, setDeferredPrompt] = useState<any>(null);
const [isAppInstalled, setIsAppInstalled] = useState(false);
useEffect(() => {
const beforeInstallHandler = (e: any) => {
e.preventDefault();
setDeferredPrompt(e);
};
const appInstalledHandler = () => {
setIsAppInstalled(true);
};
window.addEventListener("beforeinstallprompt", beforeInstallHandler);
window.addEventListener("appinstalled", appInstalledHandler);
return () => {
window.removeEventListener("beforeinstallprompt", beforeInstallHandler);
window.removeEventListener("appinstalled", appInstalledHandler);
};
}, []);
const handleInstallClick = () => {
if (deferredPrompt) {
deferredPrompt.prompt();
deferredPrompt.userChoice.then((choiceResult: any) => {
if (choiceResult.outcome === "accepted") {
console.log("User accepted the install prompt");
} else {
console.log("User dismissed the install prompt");
}
setDeferredPrompt(null);
});
}
};
return {
deferredPrompt,
isAppInstalled,
handleInstallClick,
};
}

View File

@@ -0,0 +1,75 @@
import { useState, useEffect } from "react";
import { urlB64ToUint8Array } from "./urlB64ToUint8Array";
export function usePushNotifications(publicKey: string) {
const [isSupported, setIsSupported] = useState(false);
const [subscription, setSubscription] = useState<PushSubscription | null>(
null
);
useEffect(() => {
if ("serviceWorker" in navigator && "PushManager" in window) {
setIsSupported(true);
registerServiceWorker();
}
}, []);
const registerServiceWorker = async () => {
try {
const registration = await navigator.serviceWorker.register(
"/wibu_worker.js",
{
scope: "/",
updateViaCache: "none"
}
);
const sub = await registration.pushManager.getSubscription();
if (sub) {
setSubscription(sub);
}
} catch (error) {
console.error("Service Worker registration failed:", error);
}
};
const subscribeToPush = async () => {
try {
const registration = await navigator.serviceWorker.ready;
const sub = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlB64ToUint8Array(publicKey)
});
const res = await fetch("/api/set-subscribe", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ sub: sub.toJSON() })
});
if (res.ok) {
setSubscription(sub);
}
} catch (error) {
console.error("Subscription error:", error);
}
};
const unsubscribeFromPush = async () => {
if (subscription) {
await subscription.unsubscribe();
setSubscription(null);
await fetch("/api/unsubscribe", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ sub: subscription.toJSON() })
});
}
};
return {
isSupported,
subscription,
subscribeToPush,
unsubscribeFromPush
};
}

View File

@@ -0,0 +1,83 @@
"use client";
import { usePushNotifications } from "@/lib/usePushNotifications";
import { usePWAInstall } from "@/lib/usePWAInstall";
import { useState } from "react";
// test v1
export function NotificationManager({ publicKey }: { publicKey: string }) {
const {
isSupported,
subscription,
subscribeToPush,
unsubscribeFromPush
} = usePushNotifications(publicKey);
const { deferredPrompt, isAppInstalled, handleInstallClick } =
usePWAInstall();
const [message, setMessage] = useState("halo apa kabar");
const sendTestNotification = async () => {
if (!subscription) return;
try {
const res = await fetch("/api/send-notification", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ sub: subscription.toJSON(), message })
});
if (!res.ok) {
console.error("Failed to send notification:", res.statusText);
}
} catch (error) {
console.error("Notification error:", error);
}
};
if (!isSupported) {
return <p>Push notifications are not supported in this browser.</p>;
}
return (
<div>
<h3>Push Notifications & PWA Install</h3>
{subscription ? (
<>
<p>You are subscribed to push notifications.</p>
<div
style={{
display: "flex",
flexDirection: "column",
gap: "10px"
}}
>
<div>
<button onClick={unsubscribeFromPush}>Unsubscribe</button>
</div>
<input
type="text"
placeholder="Enter notification message"
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
<div>
<button onClick={sendTestNotification}>
Send Test Notification
</button>
</div>
</div>
</>
) : (
<>
<p>You are not subscribed to push notifications.</p>
<button onClick={subscribeToPush}>Subscribe</button>
</>
)}
<hr />
{!isAppInstalled && deferredPrompt && (
<button onClick={handleInstallClick}>Install App</button>
)}
</div>
);
}

110
yarn.lock
View File

@@ -673,6 +673,13 @@
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.6.tgz#193ced6a40c8006cfc1ca3f4553444fb38f0e543"
integrity sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==
"@types/node@*":
version "22.5.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.5.5.tgz#52f939dd0f65fc552a4ad0b392f3c466cc5d7a44"
integrity sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==
dependencies:
undici-types "~6.19.2"
"@types/node@^20.14.9":
version "20.14.10"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.10.tgz#a1a218290f1b6428682e3af044785e5874db469a"
@@ -700,6 +707,13 @@
"@types/prop-types" "*"
csstype "^3.0.2"
"@types/web-push@^3.6.3":
version "3.6.3"
resolved "https://registry.yarnpkg.com/@types/web-push/-/web-push-3.6.3.tgz#7698cdeeabd70d1129a6e02bd58af1e985cdfa03"
integrity sha512-v3oT4mMJsHeJ/rraliZ+7TbZtr5bQQuxcgD7C3/1q/zkAj29c8RE0F9lVZVu3hiQe5Z9fYcBreV7TLnfKR+4mg==
dependencies:
"@types/node" "*"
"@typescript-eslint/parser@^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.2.0.tgz#44356312aea8852a3a82deebdacd52ba614ec07a"
@@ -780,6 +794,13 @@ agent-base@6:
dependencies:
debug "4"
agent-base@^7.0.2:
version "7.1.1"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317"
integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==
dependencies:
debug "^4.3.4"
ajv@^6.12.4:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
@@ -964,6 +985,16 @@ arraybuffer.prototype.slice@^1.0.3:
is-array-buffer "^3.0.4"
is-shared-array-buffer "^1.0.2"
asn1.js@^5.3.0:
version "5.4.1"
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07"
integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==
dependencies:
bn.js "^4.0.0"
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
safer-buffer "^2.1.0"
ast-types-flow@^0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6"
@@ -998,6 +1029,11 @@ binary-extensions@^2.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522"
integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
bn.js@^4.0.0:
version "4.12.0"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@@ -1020,6 +1056,11 @@ braces@^3.0.3, braces@~3.0.2:
dependencies:
fill-range "^7.1.1"
buffer-equal-constant-time@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==
busboy@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
@@ -1422,6 +1463,13 @@ eastasianwidth@^0.2.0:
resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
ecdsa-sig-formatter@1.0.11:
version "1.0.11"
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
dependencies:
safe-buffer "^5.0.1"
echarts-for-react@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/echarts-for-react/-/echarts-for-react-3.0.2.tgz#ac5859157048a1066d4553e34b328abb24f2b7c1"
@@ -2154,6 +2202,11 @@ highlight.js@^11.9.0:
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.10.0.tgz#6e3600dc4b33d6dc23d5bd94fbf72405f5892b92"
integrity sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==
http_ece@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/http_ece/-/http_ece-1.2.0.tgz#84d5885f052eae8c9b075eee4d2eb5105f114479"
integrity sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA==
https-proxy-agent@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
@@ -2162,6 +2215,14 @@ https-proxy-agent@^5.0.0:
agent-base "6"
debug "4"
https-proxy-agent@^7.0.0:
version "7.0.5"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2"
integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==
dependencies:
agent-base "^7.0.2"
debug "4"
ignore@^5.2.0:
version "5.3.1"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef"
@@ -2188,7 +2249,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@^2.0.3:
inherits@2, inherits@^2.0.1, inherits@^2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -2506,6 +2567,23 @@ json5@^1.0.2:
object.assign "^4.1.4"
object.values "^1.1.6"
jwa@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.0.tgz#a7e9c3f29dae94027ebcaf49975c9345593410fc"
integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==
dependencies:
buffer-equal-constant-time "1.0.1"
ecdsa-sig-formatter "1.0.11"
safe-buffer "^5.0.1"
jws@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4"
integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==
dependencies:
jwa "^2.0.0"
safe-buffer "^5.0.1"
keyv@^4.5.3:
version "4.5.4"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
@@ -2617,6 +2695,11 @@ mimic-response@^2.0.0:
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43"
integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==
minimalistic-assert@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
minimatch@9.0.3:
version "9.0.3"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
@@ -2638,7 +2721,7 @@ minimatch@^9.0.1, minimatch@^9.0.4:
dependencies:
brace-expansion "^2.0.1"
minimist@^1.2.0, minimist@^1.2.6:
minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6:
version "1.2.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
@@ -3333,7 +3416,7 @@ safe-array-concat@^1.1.2:
has-symbols "^1.0.3"
isarray "^2.0.5"
safe-buffer@~5.2.0:
safe-buffer@^5.0.1, safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
@@ -3347,6 +3430,11 @@ safe-regex-test@^1.0.3:
es-errors "^1.3.0"
is-regex "^1.1.4"
safer-buffer@^2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
scheduler@^0.23.2:
version "0.23.2"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3"
@@ -3837,6 +3925,11 @@ undici-types@~5.26.4:
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
undici-types@~6.19.2:
version "6.19.8"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
uri-js@^4.2.2:
version "4.4.1"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
@@ -3906,6 +3999,17 @@ victory-vendor@^36.6.8:
d3-time "^3.0.0"
d3-timer "^3.0.1"
web-push@^3.6.7:
version "3.6.7"
resolved "https://registry.yarnpkg.com/web-push/-/web-push-3.6.7.tgz#5f5e645951153e37ef90a6ddea5c150ea0f709e1"
integrity sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A==
dependencies:
asn1.js "^5.3.0"
http_ece "1.2.0"
https-proxy-agent "^7.0.0"
jws "^4.0.0"
minimist "^1.2.5"
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"