feat(web): PWA via @serwist/next + manifest + icons (P3/T22)

The web app is now installable on a phone home screen with offline
fallback for static assets and the navigation shell.

Pieces
------
- `src/app/manifest.webmanifest/route.ts` — dynamic manifest route.
  Standalone display mode, portrait orientation, dark theme matching
  the app, "any maskable" icons so the same PNG works for both
  regular launchers and Android adaptive icons.

- `src/pwa/sw.ts` — service worker entry. Uses serwist's stock
  recipe: skipWaiting + clientsClaim so a new worker takes over on
  the next navigation, navigationPreload to race the network with
  the worker boot, and `defaultCache` for HTML-network-first /
  static-cache-first / image+font cache TTLs.

- `next.config.ts` — wraps the existing config with `withSerwistInit`.
  Disabled in development (`NODE_ENV !== "production"`) because a
  service worker on every dev reload makes hot-reload extremely
  flaky.

- `package.json` build script switched to `next build --webpack`.
  `@serwist/next` doesn't yet support Turbopack (it logs a warning
  and silently skips emitting `sw.js`), and Next 16 defaults the
  build to Turbopack. The dev server still uses Turbopack — only
  production builds switch to webpack.

- `src/app/layout.tsx` metadata gains `manifest`, `icons.icon` (192
  + 512 PNG), and `icons.apple` (180 PNG). The existing
  `appleWebApp.capable` already opts iOS into standalone mode.

Icons
-----
Generated by a tiny one-shot script (`scripts/gen-pwa-icons.ts`)
that uses the workspace's already-installed sharp to render an SVG
wordmark at 512 / 192 / 180 px. Placeholder branding (dark square
with "cm" wordmark) — swap in real artwork later by editing the SVG
in the script and re-running `pnpm --filter @cmbot/web run gen:icons`.

Build artefacts
---------------
- `apps/web/public/icon-512.png`, `icon-192.png`,
  `apple-touch-icon.png` ARE committed (stable input).
- `apps/web/public/sw.js` and `swe-worker-*.js` are NOT — they're
  regenerated on every production build. Added to `.gitignore`.

Verification
------------
- Production build emits `[serwist] Bundling the service worker
  script with the URL '/sw.js' and the scope '/'...` and `sw.js`
  shows up in `public/`.
- `/manifest.webmanifest` is in the build's static-route table.
- 249 web tests still passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
yiekheng 2026-05-10 13:20:45 +08:00
parent cfd3308477
commit 272fbcfa8a
12 changed files with 412 additions and 4 deletions

6
.gitignore vendored
View File

@ -8,6 +8,12 @@ dist/
.turbo/ .turbo/
*.tsbuildinfo *.tsbuildinfo
# serwist emits these into apps/web/public/ on every production build.
# Icons (icon-*.png, apple-touch-icon.png) ARE committed; the generated
# service-worker bundle is regenerated by the build itself.
apps/web/public/sw.js
apps/web/public/swe-worker-*.js
# env files: per project decision, .env.development and .env.production # env files: per project decision, .env.development and .env.production
# ARE committed to this private Gitea. Only ignore example overrides: # ARE committed to this private Gitea. Only ignore example overrides:
.env.local .env.local

View File

@ -1,6 +1,6 @@
/// <reference types="next" /> /// <reference types="next" />
/// <reference types="next/image-types/global" /> /// <reference types="next/image-types/global" />
import "./.next/dev/types/routes.d.ts"; import "./.next/types/routes.d.ts";
// NOTE: This file should not be edited // NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

View File

@ -1,5 +1,6 @@
import type { NextConfig } from "next"; import type { NextConfig } from "next";
import { join } from "node:path"; import { join } from "node:path";
import withSerwistInit from "@serwist/next";
// Pin Turbopack's workspace root explicitly — pnpm + Turbopack can't always // Pin Turbopack's workspace root explicitly — pnpm + Turbopack can't always
// infer it inside Docker bind mounts. // infer it inside Docker bind mounts.
@ -34,4 +35,16 @@ const nextConfig: NextConfig = {
}, },
}; };
export default nextConfig; // PWA: @serwist/next compiles `src/pwa/sw.ts` into `public/sw.js` at
// production build time, baking in the static-asset precache manifest.
// We disable it in dev because Turbopack + a service worker on every
// reload makes hot-reload extremely flaky.
const withSerwist = withSerwistInit({
swSrc: "src/pwa/sw.ts",
swDest: "public/sw.js",
cacheOnNavigation: true,
reloadOnOnline: true,
disable: process.env.NODE_ENV !== "production",
});
export default withSerwist(nextConfig);

View File

@ -5,16 +5,18 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "next dev --hostname 0.0.0.0", "dev": "next dev --hostname 0.0.0.0",
"build": "next build", "build": "next build --webpack",
"start": "next start --hostname 0.0.0.0", "start": "next start --hostname 0.0.0.0",
"lint": "next lint", "lint": "next lint",
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"test": "vitest run" "test": "vitest run",
"gen:icons": "tsx scripts/gen-pwa-icons.ts"
}, },
"dependencies": { "dependencies": {
"@cmbot/db": "workspace:*", "@cmbot/db": "workspace:*",
"@cmbot/shared": "workspace:*", "@cmbot/shared": "workspace:*",
"@hookform/resolvers": "^5.2.2", "@hookform/resolvers": "^5.2.2",
"@serwist/next": "^9.5.11",
"@types/luxon": "^3.4.2", "@types/luxon": "^3.4.2",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
@ -33,6 +35,7 @@
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-hook-form": "^7.75.0", "react-hook-form": "^7.75.0",
"server-only": "^0.0.1", "server-only": "^0.0.1",
"serwist": "^9.5.11",
"shadcn": "^4.7.0", "shadcn": "^4.7.0",
"sonner": "^2.0.7", "sonner": "^2.0.7",
"tailwind-merge": "^3.5.0", "tailwind-merge": "^3.5.0",
@ -47,7 +50,9 @@
"@types/react": "^19.0.0", "@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0", "@types/react-dom": "^19.0.0",
"@vitest/ui": "^2.1.9", "@vitest/ui": "^2.1.9",
"sharp": "^0.34.5",
"tailwindcss": "^4.0.0", "tailwindcss": "^4.0.0",
"tsx": "^4.19.0",
"typescript": "^5.5.0", "typescript": "^5.5.0",
"vitest": "^2.1.9" "vitest": "^2.1.9"
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 1013 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -0,0 +1,69 @@
#!/usr/bin/env tsx
/**
* Generate placeholder PWA icons (icon-512.png, icon-192.png,
* apple-touch-icon.png) into apps/web/public/.
*
* Run once via `pnpm --filter @cmbot/web run gen:icons`. The output is
* intentionally minimal a dark square with the "cm" wordmark in
* a light bold sans-serif until a designer hands us a real icon.
*
* Sharp is already in the workspace's node_modules (Baileys depends
* on it), so we re-use it here rather than introducing a new image
* library. Output is written as PNG with no alpha channel; the
* "any maskable" purpose in the manifest covers both regular launch
* icons and Android adaptive icons (which crop to their own shape).
*/
import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import sharp from "sharp";
const OUT_DIR = join(import.meta.dirname, "..", "public");
const BG = "#0a0a0a"; // matches the manifest theme_color
const FG = "#ffffff"; // wordmark
const TEXT = "cm";
/** Render the icon at the given pixel size. */
async function renderIcon(size: number): Promise<Buffer> {
// Font size is tuned to fill ~half the icon's height with comfortable
// padding around the wordmark — same proportions Apple/Google use
// for their own home-screen icons.
const fontSize = Math.round(size * 0.46);
const svg = `
<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 ${size} ${size}">
<rect width="${size}" height="${size}" fill="${BG}" />
<text
x="50%"
y="50%"
text-anchor="middle"
dominant-baseline="central"
font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, system-ui, sans-serif"
font-weight="700"
font-size="${fontSize}"
fill="${FG}"
letter-spacing="-${Math.round(fontSize * 0.04)}"
>${TEXT}</text>
</svg>
`.trim();
return sharp(Buffer.from(svg)).png().toBuffer();
}
async function main(): Promise<void> {
const targets: Array<{ size: number; filename: string }> = [
{ size: 512, filename: "icon-512.png" },
{ size: 192, filename: "icon-192.png" },
{ size: 180, filename: "apple-touch-icon.png" },
];
for (const { size, filename } of targets) {
const png = await renderIcon(size);
const out = join(OUT_DIR, filename);
await writeFile(out, png);
console.log(` ${filename} (${size}×${size}, ${png.byteLength} bytes)`);
}
}
main().catch((err) => {
console.error(err);
process.exit(1);
});

View File

@ -9,6 +9,18 @@ export const metadata: Metadata = {
title: "cm WhatsApp Bot", title: "cm WhatsApp Bot",
description: "Self-hosted WhatsApp reminder bot", description: "Self-hosted WhatsApp reminder bot",
applicationName: "cm WhatsApp Bot", applicationName: "cm WhatsApp Bot",
// PWA wiring: the manifest comes from the dynamic route at
// src/app/manifest.webmanifest/route.ts, the apple-touch-icon is
// emitted from public/, and `appleWebApp.capable` lets iOS treat the
// page like a standalone app when added to the home screen.
manifest: "/manifest.webmanifest",
icons: {
icon: [
{ url: "/icon-192.png", sizes: "192x192", type: "image/png" },
{ url: "/icon-512.png", sizes: "512x512", type: "image/png" },
],
apple: [{ url: "/apple-touch-icon.png", sizes: "180x180", type: "image/png" }],
},
appleWebApp: { capable: true, title: "cm WA Bot", statusBarStyle: "default" }, appleWebApp: { capable: true, title: "cm WA Bot", statusBarStyle: "default" },
}; };

View File

@ -0,0 +1,29 @@
import { NextResponse } from "next/server";
/**
* PWA manifest. Served from `/manifest.webmanifest` so the document
* `<link rel="manifest" href="/manifest.webmanifest">` (set up via
* Next's metadata API in layout.tsx) can find it.
*
* `purpose: "any maskable"` lets the same icon work for both regular
* launch icons and Android maskable icons (where the OS crops the
* icon to a system-defined shape). `display: "standalone"` removes
* the browser chrome when launched from the home screen.
*/
export function GET() {
return NextResponse.json({
name: "cm WhatsApp Bot",
short_name: "cm WA Bot",
description: "Self-hosted WhatsApp reminder bot",
start_url: "/",
scope: "/",
display: "standalone",
orientation: "portrait",
background_color: "#0a0a0a",
theme_color: "#0a0a0a",
icons: [
{ src: "/icon-192.png", sizes: "192x192", type: "image/png", purpose: "any maskable" },
{ src: "/icon-512.png", sizes: "512x512", type: "image/png", purpose: "any maskable" },
],
});
}

32
apps/web/src/pwa/sw.ts Normal file
View File

@ -0,0 +1,32 @@
/// <reference lib="webworker" />
import { defaultCache } from "@serwist/next/worker";
import { Serwist } from "serwist";
declare const self: ServiceWorkerGlobalScope & {
__SW_MANIFEST: (string | { url: string; revision: string | null })[];
};
/**
* The service worker entry. `@serwist/next` builds this file into
* `public/sw.js` at production build time and the manifest is
* substituted into `__SW_MANIFEST` (URLs the worker should precache).
*
* - `skipWaiting` + `clientsClaim`: a new worker takes over the page
* on the next navigation rather than waiting for every tab to
* close. Operators tend to live in one tab; faster updates win.
* - `navigationPreload: true`: tells the browser it can race the
* network fetch for navigations alongside the worker boot, cutting
* first-paint when the worker is cold.
* - `runtimeCaching: defaultCache`: serwist's stock recipe HTML
* network-first with offline fallback, static assets cache-first,
* image / font caches with sensible TTLs.
*/
const serwist = new Serwist({
precacheEntries: self.__SW_MANIFEST,
skipWaiting: true,
clientsClaim: true,
navigationPreload: true,
runtimeCaching: defaultCache,
});
serwist.addEventListeners();

242
pnpm-lock.yaml generated
View File

@ -84,6 +84,9 @@ importers:
'@hookform/resolvers': '@hookform/resolvers':
specifier: ^5.2.2 specifier: ^5.2.2
version: 5.2.2(react-hook-form@7.75.0(react@19.2.6)) version: 5.2.2(react-hook-form@7.75.0(react@19.2.6))
'@serwist/next':
specifier: ^9.5.11
version: 9.5.11(next@16.2.6(@babel/core@7.29.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(typescript@5.9.3)
'@types/luxon': '@types/luxon':
specifier: ^3.4.2 specifier: ^3.4.2
version: 3.7.1 version: 3.7.1
@ -138,6 +141,9 @@ importers:
server-only: server-only:
specifier: ^0.0.1 specifier: ^0.0.1
version: 0.0.1 version: 0.0.1
serwist:
specifier: ^9.5.11
version: 9.5.11(browserslist@4.28.2)(typescript@5.9.3)
shadcn: shadcn:
specifier: ^4.7.0 specifier: ^4.7.0
version: 4.7.0(@types/node@22.19.18)(typescript@5.9.3) version: 4.7.0(@types/node@22.19.18)(typescript@5.9.3)
@ -175,9 +181,15 @@ importers:
'@vitest/ui': '@vitest/ui':
specifier: ^2.1.9 specifier: ^2.1.9
version: 2.1.9(vitest@2.1.9) version: 2.1.9(vitest@2.1.9)
sharp:
specifier: ^0.34.5
version: 0.34.5
tailwindcss: tailwindcss:
specifier: ^4.0.0 specifier: ^4.0.0
version: 4.3.0 version: 4.3.0
tsx:
specifier: ^4.19.0
version: 4.21.0
typescript: typescript:
specifier: ^5.5.0 specifier: ^5.5.0
version: 5.9.3 version: 5.9.3
@ -2158,6 +2170,57 @@ packages:
'@sec-ant/readable-stream@0.4.1': '@sec-ant/readable-stream@0.4.1':
resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
'@serwist/build@9.5.11':
resolution: {integrity: sha512-PQfW+LhADYFOOp0PhEnjlgJCyKor6cYa06d3rID1OpiKzkmCApJV1WYfdTBB96jXaWv6OWcWSbSV4tqDLxvaVA==}
engines: {node: '>=18.0.0'}
peerDependencies:
typescript: '>=5.0.0'
peerDependenciesMeta:
typescript:
optional: true
'@serwist/next@9.5.11':
resolution: {integrity: sha512-omT32H7U21ihCymSvOG9QeRJBuOEomJx4JdzKhUoqOW3DR10tH3m84VOHj3BvK0OcA7av3qj5FsyNFBB+f0n8A==}
engines: {node: '>=18.0.0'}
peerDependencies:
'@serwist/cli': ^9.5.11
next: '>=14.0.0'
react: '>=18.0.0'
typescript: '>=5.0.0'
peerDependenciesMeta:
'@serwist/cli':
optional: true
typescript:
optional: true
'@serwist/utils@9.5.11':
resolution: {integrity: sha512-zqxmwuHqWA3OwN82Wo8gFZ9QBemygJP3cap5JWAOG4UyJZgUZfmBXAXj+IMaD4eKZ/6pqrxHHDZ9uSWZmJ1mXA==}
peerDependencies:
browserslist: '>=4'
peerDependenciesMeta:
browserslist:
optional: true
'@serwist/webpack-plugin@9.5.11':
resolution: {integrity: sha512-SlvO3A1UMcc1htCzMtLCtPQK6yISCO7B859ixLv7EiY/yayXjVxGm9vHqkJYpQ768PWyjEZXRY/X6EGRMA6wJQ==}
engines: {node: '>=18.0.0'}
peerDependencies:
typescript: '>=5.0.0'
webpack: 4.4.0 || ^5.9.0
peerDependenciesMeta:
typescript:
optional: true
webpack:
optional: true
'@serwist/window@9.5.11':
resolution: {integrity: sha512-OrH9srhmifUvY36NuukHSZby24XTEk4pHh3pfY0GBQzA9ouU1fYh+ORWhKxH7/wkVHRr3sc4YAhjtpfL14PjjQ==}
peerDependencies:
typescript: '>=5.0.0'
peerDependenciesMeta:
typescript:
optional: true
'@sindresorhus/merge-streams@4.0.0': '@sindresorhus/merge-streams@4.0.0':
resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -2342,6 +2405,9 @@ packages:
'@types/statuses@2.0.6': '@types/statuses@2.0.6':
resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==}
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
'@types/validate-npm-package-name@4.0.2': '@types/validate-npm-package-name@4.0.2':
resolution: {integrity: sha512-lrpDziQipxCEeK5kWxvljWYhUvOiB2A9izZd9B2AFarYAkqZshb4lPbRs7zKEic6eGtH8V/2qJW+dPp9OtF6bw==} resolution: {integrity: sha512-lrpDziQipxCEeK5kWxvljWYhUvOiB2A9izZd9B2AFarYAkqZshb4lPbRs7zKEic6eGtH8V/2qJW+dPp9OtF6bw==}
@ -2612,6 +2678,10 @@ packages:
resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==}
engines: {node: '>=20'} engines: {node: '>=20'}
common-tags@1.8.2:
resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==}
engines: {node: '>=4.0.0'}
content-disposition@1.1.0: content-disposition@1.1.0:
resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -3113,6 +3183,10 @@ packages:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'} engines: {node: '>= 6'}
glob@13.0.6:
resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==}
engines: {node: 18 || 20 || >=22}
gopd@1.2.0: gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -3172,6 +3246,9 @@ packages:
resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
idb@8.0.3:
resolution: {integrity: sha512-LtwtVyVYO5BqRvcsKuB2iUMnHwPVByPCXFXOpuU96IZPPoPN6xjOGxZQ74pgSVVLQWtUOYgyeL4GE98BY5D3wg==}
ieee754@1.2.1: ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
@ -3327,6 +3404,9 @@ packages:
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
engines: {node: '>=6'} engines: {node: '>=6'}
kolorist@1.8.0:
resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
lightningcss-android-arm64@1.32.0: lightningcss-android-arm64@1.32.0:
resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
@ -3404,6 +3484,9 @@ packages:
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
engines: {node: '>=8'} engines: {node: '>=8'}
lodash.sortby@4.7.0:
resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==}
log-symbols@6.0.0: log-symbols@6.0.0:
resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -3482,6 +3565,10 @@ packages:
minimist@1.2.8: minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
minipass@7.1.3:
resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==}
engines: {node: '>=16 || 14 >=14.17'}
mpg123-decoder@1.0.3: mpg123-decoder@1.0.3:
resolution: {integrity: sha512-+fjxnWigodWJm3+4pndi+KUg9TBojgn31DPk85zEsim7C6s0X5Ztc/hQYdytXkwuGXH+aB0/aEkG40Emukv6oQ==} resolution: {integrity: sha512-+fjxnWigodWJm3+4pndi+KUg9TBojgn31DPk85zEsim7C6s0X5Ztc/hQYdytXkwuGXH+aB0/aEkG40Emukv6oQ==}
@ -3673,6 +3760,10 @@ packages:
resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
path-scurry@2.0.2:
resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==}
engines: {node: 18 || 20 || >=22}
path-to-regexp@6.3.0: path-to-regexp@6.3.0:
resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==}
@ -3790,6 +3881,10 @@ packages:
resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==} resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==}
engines: {node: '>=20'} engines: {node: '>=20'}
pretty-bytes@6.1.1:
resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==}
engines: {node: ^14.13.1 || >=16.0.0}
pretty-ms@9.3.0: pretty-ms@9.3.0:
resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -3820,6 +3915,10 @@ packages:
pump@3.0.4: pump@3.0.4:
resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==}
punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
qified@0.10.1: qified@0.10.1:
resolution: {integrity: sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==} resolution: {integrity: sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==}
engines: {node: '>=20'} engines: {node: '>=20'}
@ -3988,6 +4087,11 @@ packages:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true hasBin: true
semver@7.7.4:
resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==}
engines: {node: '>=10'}
hasBin: true
semver@7.8.0: semver@7.8.0:
resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -4008,6 +4112,14 @@ packages:
server-only@0.0.1: server-only@0.0.1:
resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==}
serwist@9.5.11:
resolution: {integrity: sha512-Bq6uwJFd4ET60BWI77v3VbazKHv6k7lECOiiCFwKyBu/slaCn0GHJ5L5RfsuJUKrnbD9lYUCDo6sqaKRM5M2vA==}
peerDependencies:
typescript: '>=5.0.0'
peerDependenciesMeta:
typescript:
optional: true
set-blocking@2.0.0: set-blocking@2.0.0:
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
@ -4089,6 +4201,11 @@ packages:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
source-map@0.8.0-beta.0:
resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
engines: {node: '>= 8'}
deprecated: The work that was done in this beta branch won't be included in future versions
split2@4.2.0: split2@4.2.0:
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
engines: {node: '>= 10.x'} engines: {node: '>= 10.x'}
@ -4235,6 +4352,9 @@ packages:
resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==}
engines: {node: '>=16'} engines: {node: '>=16'}
tr46@1.0.1:
resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==}
ts-morph@26.0.0: ts-morph@26.0.0:
resolution: {integrity: sha512-ztMO++owQnz8c/gIENcM9XfCEzgoGphTv+nKpYNM1bgsdOVC/jRZuEBf6N+mLLDNg68Kl+GgUZfOySaRiG1/Ug==} resolution: {integrity: sha512-ztMO++owQnz8c/gIENcM9XfCEzgoGphTv+nKpYNM1bgsdOVC/jRZuEBf6N+mLLDNg68Kl+GgUZfOySaRiG1/Ug==}
@ -4399,9 +4519,15 @@ packages:
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
webidl-conversions@4.0.2:
resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
whatsapp-rust-bridge@0.5.3: whatsapp-rust-bridge@0.5.3:
resolution: {integrity: sha512-Xb3GAgtWQQJ30oI4a4pjM4+YUeli9CMLTwTIewUrb+AJMFElIkiT5uo+j1Zhc+amiV0Jj+LfX76c/EEZirJbGA==} resolution: {integrity: sha512-Xb3GAgtWQQJ30oI4a4pjM4+YUeli9CMLTwTIewUrb+AJMFElIkiT5uo+j1Zhc+amiV0Jj+LfX76c/EEZirJbGA==}
whatwg-url@7.1.0:
resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==}
which-module@2.0.1: which-module@2.0.1:
resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
@ -4496,6 +4622,9 @@ packages:
zod@3.25.76: zod@3.25.76:
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
zod@4.4.1:
resolution: {integrity: sha512-a6ENMBBGZBsnlSebQ/eKCguSBeGKSf4O7BPnqVPmYGtpBYI7VSqoVqw+QcB7kPRjbqPwhYTpFbVj/RqNz/CT0Q==}
snapshots: snapshots:
'@alloc/quick-lru@5.2.0': {} '@alloc/quick-lru@5.2.0': {}
@ -6146,6 +6275,63 @@ snapshots:
'@sec-ant/readable-stream@0.4.1': {} '@sec-ant/readable-stream@0.4.1': {}
'@serwist/build@9.5.11(browserslist@4.28.2)(typescript@5.9.3)':
dependencies:
'@serwist/utils': 9.5.11(browserslist@4.28.2)
common-tags: 1.8.2
glob: 13.0.6
pretty-bytes: 6.1.1
source-map: 0.8.0-beta.0
type-fest: 5.6.0
zod: 4.4.1
optionalDependencies:
typescript: 5.9.3
transitivePeerDependencies:
- browserslist
'@serwist/next@9.5.11(next@16.2.6(@babel/core@7.29.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(typescript@5.9.3)':
dependencies:
'@serwist/build': 9.5.11(browserslist@4.28.2)(typescript@5.9.3)
'@serwist/utils': 9.5.11(browserslist@4.28.2)
'@serwist/webpack-plugin': 9.5.11(browserslist@4.28.2)(typescript@5.9.3)
'@serwist/window': 9.5.11(browserslist@4.28.2)(typescript@5.9.3)
browserslist: 4.28.2
glob: 13.0.6
kolorist: 1.8.0
next: 16.2.6(@babel/core@7.29.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
react: 19.2.6
semver: 7.7.4
serwist: 9.5.11(browserslist@4.28.2)(typescript@5.9.3)
zod: 4.4.1
optionalDependencies:
typescript: 5.9.3
transitivePeerDependencies:
- webpack
'@serwist/utils@9.5.11(browserslist@4.28.2)':
optionalDependencies:
browserslist: 4.28.2
'@serwist/webpack-plugin@9.5.11(browserslist@4.28.2)(typescript@5.9.3)':
dependencies:
'@serwist/build': 9.5.11(browserslist@4.28.2)(typescript@5.9.3)
'@serwist/utils': 9.5.11(browserslist@4.28.2)
pretty-bytes: 6.1.1
zod: 4.4.1
optionalDependencies:
typescript: 5.9.3
transitivePeerDependencies:
- browserslist
'@serwist/window@9.5.11(browserslist@4.28.2)(typescript@5.9.3)':
dependencies:
'@types/trusted-types': 2.0.7
serwist: 9.5.11(browserslist@4.28.2)(typescript@5.9.3)
optionalDependencies:
typescript: 5.9.3
transitivePeerDependencies:
- browserslist
'@sindresorhus/merge-streams@4.0.0': {} '@sindresorhus/merge-streams@4.0.0': {}
'@standard-schema/utils@0.3.0': {} '@standard-schema/utils@0.3.0': {}
@ -6302,6 +6488,8 @@ snapshots:
'@types/statuses@2.0.6': {} '@types/statuses@2.0.6': {}
'@types/trusted-types@2.0.7': {}
'@types/validate-npm-package-name@4.0.2': {} '@types/validate-npm-package-name@4.0.2': {}
'@vitest/expect@2.1.9': '@vitest/expect@2.1.9':
@ -6603,6 +6791,8 @@ snapshots:
commander@14.0.3: {} commander@14.0.3: {}
common-tags@1.8.2: {}
content-disposition@1.1.0: {} content-disposition@1.1.0: {}
content-type@1.0.5: {} content-type@1.0.5: {}
@ -7094,6 +7284,12 @@ snapshots:
dependencies: dependencies:
is-glob: 4.0.3 is-glob: 4.0.3
glob@13.0.6:
dependencies:
minimatch: 10.2.5
minipass: 7.1.3
path-scurry: 2.0.2
gopd@1.2.0: {} gopd@1.2.0: {}
graceful-fs@4.2.11: {} graceful-fs@4.2.11: {}
@ -7146,6 +7342,8 @@ snapshots:
dependencies: dependencies:
safer-buffer: 2.1.2 safer-buffer: 2.1.2
idb@8.0.3: {}
ieee754@1.2.1: {} ieee754@1.2.1: {}
ignore@5.3.2: {} ignore@5.3.2: {}
@ -7245,6 +7443,8 @@ snapshots:
kleur@4.1.5: {} kleur@4.1.5: {}
kolorist@1.8.0: {}
lightningcss-android-arm64@1.32.0: lightningcss-android-arm64@1.32.0:
optional: true optional: true
@ -7300,6 +7500,8 @@ snapshots:
dependencies: dependencies:
p-locate: 4.1.0 p-locate: 4.1.0
lodash.sortby@4.7.0: {}
log-symbols@6.0.0: log-symbols@6.0.0:
dependencies: dependencies:
chalk: 5.6.2 chalk: 5.6.2
@ -7358,6 +7560,8 @@ snapshots:
minimist@1.2.8: {} minimist@1.2.8: {}
minipass@7.1.3: {}
mpg123-decoder@1.0.3: mpg123-decoder@1.0.3:
dependencies: dependencies:
'@wasm-audio-decoders/common': 9.0.7 '@wasm-audio-decoders/common': 9.0.7
@ -7566,6 +7770,11 @@ snapshots:
path-key@4.0.0: {} path-key@4.0.0: {}
path-scurry@2.0.2:
dependencies:
lru-cache: 11.3.6
minipass: 7.1.3
path-to-regexp@6.3.0: {} path-to-regexp@6.3.0: {}
path-to-regexp@8.4.2: {} path-to-regexp@8.4.2: {}
@ -7693,6 +7902,8 @@ snapshots:
powershell-utils@0.1.0: {} powershell-utils@0.1.0: {}
pretty-bytes@6.1.1: {}
pretty-ms@9.3.0: pretty-ms@9.3.0:
dependencies: dependencies:
parse-ms: 4.0.0 parse-ms: 4.0.0
@ -7747,6 +7958,8 @@ snapshots:
end-of-stream: 1.4.5 end-of-stream: 1.4.5
once: 1.4.0 once: 1.4.0
punycode@2.3.1: {}
qified@0.10.1: qified@0.10.1:
dependencies: dependencies:
hookified: 2.2.0 hookified: 2.2.0
@ -7980,6 +8193,8 @@ snapshots:
semver@6.3.1: {} semver@6.3.1: {}
semver@7.7.4: {}
semver@7.8.0: {} semver@7.8.0: {}
send@1.2.1: send@1.2.1:
@ -8014,6 +8229,15 @@ snapshots:
server-only@0.0.1: {} server-only@0.0.1: {}
serwist@9.5.11(browserslist@4.28.2)(typescript@5.9.3):
dependencies:
'@serwist/utils': 9.5.11(browserslist@4.28.2)
idb: 8.0.3
optionalDependencies:
typescript: 5.9.3
transitivePeerDependencies:
- browserslist
set-blocking@2.0.0: {} set-blocking@2.0.0: {}
set-cookie-parser@3.1.0: {} set-cookie-parser@3.1.0: {}
@ -8163,6 +8387,10 @@ snapshots:
source-map@0.6.1: {} source-map@0.6.1: {}
source-map@0.8.0-beta.0:
dependencies:
whatwg-url: 7.1.0
split2@4.2.0: {} split2@4.2.0: {}
stackback@0.0.2: {} stackback@0.0.2: {}
@ -8277,6 +8505,10 @@ snapshots:
dependencies: dependencies:
tldts: 7.0.30 tldts: 7.0.30
tr46@1.0.1:
dependencies:
punycode: 2.3.1
ts-morph@26.0.0: ts-morph@26.0.0:
dependencies: dependencies:
'@ts-morph/common': 0.27.0 '@ts-morph/common': 0.27.0
@ -8429,8 +8661,16 @@ snapshots:
web-streams-polyfill@3.3.3: {} web-streams-polyfill@3.3.3: {}
webidl-conversions@4.0.2: {}
whatsapp-rust-bridge@0.5.3: {} whatsapp-rust-bridge@0.5.3: {}
whatwg-url@7.1.0:
dependencies:
lodash.sortby: 4.7.0
tr46: 1.0.1
webidl-conversions: 4.0.2
which-module@2.0.1: {} which-module@2.0.1: {}
which@2.0.2: which@2.0.2:
@ -8519,3 +8759,5 @@ snapshots:
zod: 3.25.76 zod: 3.25.76
zod@3.25.76: {} zod@3.25.76: {}
zod@4.4.1: {}