diff --git a/packages/backend/package.json b/packages/backend/package.json index 2404749..b3a699c 100755 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -22,6 +22,7 @@ "@mikro-orm/postgresql": "^6.6.7", "@mikro-orm/reflection": "^6.6.7", "@types/web-push": "^3.6.4", + "lynqchat-js": "workspace:*", "argon2": "^0.44.0", "cross-env": "^10.1.0", "fastify": "^5.7.4", diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index b1ccdad..0dd514e 100755 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -11,6 +11,7 @@ import Authorization from "@/lib/auth"; import { RequestContext } from "@mikro-orm/core"; import { DatabaseError, ErrorBase } from "@/errors"; import { ConfigEntity } from "@/modules/entities/Config"; +import type ApiMap from "lynqchat-js/1.0.0-alpha.0/map"; process.title = "LynqChat"; @@ -83,7 +84,8 @@ try { fastify.addHook("onRequest", async (req, res) => { if ( req.url.startsWith("/api") && - !req.url.startsWith("/api/setup") + !req.url.startsWith("/api/setup") && + !req.url.startsWith("/api/server-info") ) { try { const configCount = await fastify.orm.em.count(ConfigEntity); @@ -123,6 +125,23 @@ try { } }); + fastify.get("/manifest.json", async (req, res) => { + const serverInfo = await (await fastify.inject({ + method: "POST", + url: "/api/server-info", + })).json() as ApiMap["server-info"]["response"]; + + if (!serverInfo.success) { + return res.code(500).send(serverInfo); + } + + return res.send({ + name: serverInfo.name ?? "LynqChat", + start_url: "/", + display: "standalone", + }); + }); + await fastify.register(Routes, { prefix: "/api", }); diff --git a/packages/backend/src/modules/entities/User.ts b/packages/backend/src/modules/entities/User.ts index 140d285..6b7c94f 100644 --- a/packages/backend/src/modules/entities/User.ts +++ b/packages/backend/src/modules/entities/User.ts @@ -51,7 +51,7 @@ export class UserEntity { type: "boolean", default: false, }) - isOwner: boolean = false; + isAdmin: boolean = false; @Property({ type: "boolean", diff --git a/packages/backend/src/modules/repositories/User.ts b/packages/backend/src/modules/repositories/User.ts index 8fbe742..e1636e3 100644 --- a/packages/backend/src/modules/repositories/User.ts +++ b/packages/backend/src/modules/repositories/User.ts @@ -9,7 +9,7 @@ export class UserRepository extends EntityRepository { userid: z.string().trim().min(3).max(20), username: z.string().trim().min(3).max(30), profile: z.string().max(4096).optional(), - email: z.string().min(6).trim().max(254).regex(EmailRegex), + email: z.string().trim().min(6).max(254).regex(EmailRegex), password: z.string().trim().min(8), isAdmin: z.boolean(), }); diff --git a/packages/backend/src/routes/server-info.ts b/packages/backend/src/routes/server-info.ts index c898f3b..4b9abda 100644 --- a/packages/backend/src/routes/server-info.ts +++ b/packages/backend/src/routes/server-info.ts @@ -14,8 +14,13 @@ export default async function ServerInfo(fastify: FastifyInstance) { const configCount = await config.count(); const userCount = await user.count(); + const serverName = await config.findOne({ name: "name" }); + const serverDescription = await config.findOne({ name: "description" }); + return res.send({ success: true, + name: serverName?.value ?? null, + description: serverDescription?.value ?? null, isInitialized: configCount > 0, isFirstAdminExists: userCount > 0, userCount, diff --git a/packages/frontend/index.html b/packages/frontend/index.html index 9e5738c..9a6d8dd 100755 --- a/packages/frontend/index.html +++ b/packages/frontend/index.html @@ -4,7 +4,8 @@ LynqChat - + + \ No newline at end of file diff --git a/packages/frontend/src/NotFound.vue b/packages/frontend/src/NotFound.vue new file mode 100644 index 0000000..48cee76 --- /dev/null +++ b/packages/frontend/src/NotFound.vue @@ -0,0 +1,28 @@ + + + + + \ No newline at end of file diff --git a/packages/frontend/src/components/Button.vue b/packages/frontend/src/components/Button.vue new file mode 100644 index 0000000..b2b9af2 --- /dev/null +++ b/packages/frontend/src/components/Button.vue @@ -0,0 +1,49 @@ + + + + + \ No newline at end of file diff --git a/packages/frontend/src/components/Input.vue b/packages/frontend/src/components/Input.vue new file mode 100644 index 0000000..a0f878a --- /dev/null +++ b/packages/frontend/src/components/Input.vue @@ -0,0 +1,58 @@ + + + + + \ No newline at end of file diff --git a/packages/frontend/src/components/InputPassword.vue b/packages/frontend/src/components/InputPassword.vue new file mode 100644 index 0000000..f100752 --- /dev/null +++ b/packages/frontend/src/components/InputPassword.vue @@ -0,0 +1,95 @@ + + + + + \ No newline at end of file diff --git a/packages/frontend/src/components/Modal/Error.vue b/packages/frontend/src/components/Modal/Error.vue new file mode 100644 index 0000000..c69f760 --- /dev/null +++ b/packages/frontend/src/components/Modal/Error.vue @@ -0,0 +1,49 @@ + + + + + \ No newline at end of file diff --git a/packages/frontend/src/components/Modal/Loading.vue b/packages/frontend/src/components/Modal/Loading.vue new file mode 100644 index 0000000..a6da1a5 --- /dev/null +++ b/packages/frontend/src/components/Modal/Loading.vue @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file diff --git a/packages/frontend/src/components/Modal/Success.vue b/packages/frontend/src/components/Modal/Success.vue new file mode 100644 index 0000000..c4f732f --- /dev/null +++ b/packages/frontend/src/components/Modal/Success.vue @@ -0,0 +1,46 @@ + + + + + \ No newline at end of file diff --git a/packages/frontend/src/components/Progress.vue b/packages/frontend/src/components/Progress.vue index 92011e0..2966d9c 100755 --- a/packages/frontend/src/components/Progress.vue +++ b/packages/frontend/src/components/Progress.vue @@ -4,6 +4,7 @@ + + \ No newline at end of file diff --git a/packages/frontend/src/components/Toggle.vue b/packages/frontend/src/components/Toggle.vue new file mode 100644 index 0000000..600e9f7 --- /dev/null +++ b/packages/frontend/src/components/Toggle.vue @@ -0,0 +1,119 @@ + + + + + \ No newline at end of file diff --git a/packages/frontend/src/global.css b/packages/frontend/src/global.css index 2344566..77851c4 100755 --- a/packages/frontend/src/global.css +++ b/packages/frontend/src/global.css @@ -1,13 +1,23 @@ +@import url("https://fonts.googleapis.com/css2?family=BIZ+UDPGothic&display=swap"); + :root { --bg-color: #ffffff; + --bg-sub-color: #f1f1f1; --text-color: #000000; + --text-sub-color: #595959; --border-color: #e0e0e0; + --error-color: #ff0000; + --success-color: #00ff00; + --accent-color: #3ad0a1; + --accent-in-text-color: #000000; } @media (prefers-color-scheme: dark) { :root { --bg-color: #1b1b1b; + --bg-sub-color: #252525; --text-color: #ffffff; + --text-sub-color: #a4a4a4; --border-color: #2c2c2c; } } @@ -15,12 +25,45 @@ * { margin: 0; padding: 0; + scrollbar-width: thin; + scrollbar-color: var(--accent-color) var(--border-color); +} + +::selection { + background-color: var(--accent-color); + color: var(--accent-in-text-color); } html, body { width: 100dvw; height: 100dvh; font-size: 14px; + font-family: "BIZ UDPGothic", sans-serif; + line-height: 1.5rem; + tab-size: 2; background-color: var(--bg-color); color: var(--text-color); } + +a { + color: var(--accent-color); +} + +label { + width: fit-content; +} + +.title { + font-size: 1.6rem; + font-weight: bold; +} + +.modal { + background-color: var(--bg-color); + border-radius: 2rem; + padding: 3rem 6rem; + width: fit-content; + height: fit-content; + max-width: 90dvw; + max-height: 90dvh; +} \ No newline at end of file diff --git a/packages/frontend/src/lib/account.ts b/packages/frontend/src/lib/account.ts index 64bd2bd..5ba982c 100644 --- a/packages/frontend/src/lib/account.ts +++ b/packages/frontend/src/lib/account.ts @@ -1,5 +1,11 @@ import client from "@/lib/client"; +import type ApiMap from "lynqchat-js/1.0.0-alpha.0/map"; +import { ref } from "vue"; -const serverInfo = await client.request("server-info"); +let serverInfo = ref(await client.request("server-info")); + +export const reloadServerInfo = async () => { + serverInfo.value = await client.request("server-info"); +} export default serverInfo; \ No newline at end of file diff --git a/packages/frontend/src/lib/modal.ts b/packages/frontend/src/lib/modal.ts new file mode 100644 index 0000000..f6c61f0 --- /dev/null +++ b/packages/frontend/src/lib/modal.ts @@ -0,0 +1,52 @@ +import { createApp, h, ref, type Component } from "vue"; + +const layer = ref(0); + +export const createModal = (data: { + component: T, + style?: Record, + props?: Record any) | any>, + onClose?: () => void +}) => { + layer.value++ + const newContainer = document.createElement("div"); + for (const [key, value] of Object.entries({ + ...data.style, + transform: `translateZ(${layer.value})`, + position: "fixed", + inset: "0", + display: "flex", + "justify-content": "center", + "align-items": "center", + width: "100dvw", + height: "100dvh", + "background-color": "#00000080", + })) { + newContainer.style.setProperty(key, value); + } + + const container = document.querySelector(".modals-container")! + .appendChild(newContainer); + + let app: ReturnType; + + const close = () => { + data.onClose?.(); + layer.value--; + app.unmount(); + container.remove(); + }; + + app = createApp({ + render() { + return h(data.component, { + ...data.props, + onPleaseClose: close, + }); + }, + }); + + app.mount(container); + + return close; +} \ No newline at end of file diff --git a/packages/frontend/src/lib/validation.ts b/packages/frontend/src/lib/validation.ts new file mode 100644 index 0000000..b0954da --- /dev/null +++ b/packages/frontend/src/lib/validation.ts @@ -0,0 +1,8 @@ +import type { SafeParseReturnType } from "zod/v3"; + +export const getIssueFromPath = (path: string, result: SafeParseReturnType) => { + const filtered = result.error?.issues.filter(issue => issue.path.join(".") === path); + const issue = (filtered ?? [])[0]; + + return issue; +} \ No newline at end of file diff --git a/packages/frontend/src/main.ts b/packages/frontend/src/main.ts index d393799..8ed6fc6 100755 --- a/packages/frontend/src/main.ts +++ b/packages/frontend/src/main.ts @@ -1,30 +1,69 @@ import { createApp } from "vue"; import { createRouter, createWebHistory } from "vue-router"; import routerStatus from "@/lib/router"; +import { createHead } from "@unhead/vue/client"; +import serverInfo from "@/lib/account"; import "@/global.css"; import Layout from "@/Layout.vue"; const app = createApp(Layout); +app.use(createHead()); + const router = createRouter({ history: createWebHistory(), routes: [ { path: "/", + meta: { + title: "ホーム", + }, component: () => import("@/routes/index.vue"), }, { path: "/setup", - component: () => import("@/routes/setup.vue"), - } + redirect: "/setup/initialization", + }, + { + path: "/setup/initialization", + meta: { + title: "初期設定", + }, + component: () => import("@/routes/setup/initialization.vue"), + }, + { + path: "/setup/create-admin", + meta: { + title: "管理者アカウントの作成", + }, + component: () => import("@/routes/setup/create-admin.vue"), + }, + { + path: "/:NotFound(.*)*", + meta: { + title: "お探しのページは見つかりませんでした。", + }, + component: () => import("@/NotFound.vue"), + }, ], }); router.beforeEach(() => { routerStatus.isLoad = true; return; }); -router.afterEach(() => { +router.afterEach((to) => { + const title = to.meta.title; + + let serverName = "LynqChat"; + + if (serverInfo.value.success && serverInfo.value.name) { + serverName = serverInfo.value.name; + } + + document.title = title + ? `${title} | ${serverName}` + : serverName; routerStatus.isLoad = false; }); app.use(router); diff --git a/packages/frontend/src/routes/setup.vue b/packages/frontend/src/routes/setup.vue deleted file mode 100644 index fd432bb..0000000 --- a/packages/frontend/src/routes/setup.vue +++ /dev/null @@ -1,18 +0,0 @@ - - - \ No newline at end of file diff --git a/packages/frontend/src/routes/setup/create-admin.vue b/packages/frontend/src/routes/setup/create-admin.vue new file mode 100644 index 0000000..794d1f2 --- /dev/null +++ b/packages/frontend/src/routes/setup/create-admin.vue @@ -0,0 +1,198 @@ + + + + + \ No newline at end of file diff --git a/packages/frontend/src/routes/setup/initialization.vue b/packages/frontend/src/routes/setup/initialization.vue new file mode 100644 index 0000000..523048b --- /dev/null +++ b/packages/frontend/src/routes/setup/initialization.vue @@ -0,0 +1,192 @@ + + + + + \ No newline at end of file diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts index e04fa9d..86b2ccd 100755 --- a/packages/frontend/vite.config.ts +++ b/packages/frontend/vite.config.ts @@ -18,6 +18,10 @@ export default defineConfig(({ mode }) => { target: `http://localhost:${apiPort}`, changeOrigin: true, }, + "/manifest.json": { + target: `http://localhost:${apiPort}`, + changeOrigin: true, + }, }, }, resolve: { diff --git a/packages/lynqchat-js/src/1.0.0-alpha.0/api/server-info.d.ts b/packages/lynqchat-js/src/1.0.0-alpha.0/api/server-info.d.ts index d8820a1..ae5e695 100644 --- a/packages/lynqchat-js/src/1.0.0-alpha.0/api/server-info.d.ts +++ b/packages/lynqchat-js/src/1.0.0-alpha.0/api/server-info.d.ts @@ -8,6 +8,8 @@ export default interface ServerInfo { "server-info": { body: never; response: (Success & { + name: string | null; + description: string | null; isInitialized: boolean; isFirstAdminExists: boolean; userCount: number; diff --git a/packages/lynqchat-js/src/1.0.0-alpha.0/api/setup/initialization.d.ts b/packages/lynqchat-js/src/1.0.0-alpha.0/api/setup/initialization.d.ts index e7fe935..b4e6ce0 100644 --- a/packages/lynqchat-js/src/1.0.0-alpha.0/api/setup/initialization.d.ts +++ b/packages/lynqchat-js/src/1.0.0-alpha.0/api/setup/initialization.d.ts @@ -5,7 +5,7 @@ import Success from "../../modules/response/success"; import UnknownError from "../../modules/error/unknown"; export default interface SetupInitialization { - "setup/initilization": { + "setup/initialization": { body: { name: string; description: string; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3e21e94..2569dbe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -43,6 +43,9 @@ importers: fs: specifier: 0.0.1-security version: 0.0.1-security + lynqchat-js: + specifier: workspace:* + version: link:../lynqchat-js os: {specifier: ^0.1.2, version: 0.1.2} tsc-alias: specifier: ^1.8.16 @@ -75,6 +78,12 @@ importers: packages/frontend: dependencies: + '@iconify/vue': + specifier: ^5.0.0 + version: 5.0.0(vue@3.5.30(typescript@5.9.3)) + '@unhead/vue': + specifier: 3.0.0-beta.9 + version: 3.0.0-beta.9(vite@8.0.0(@types/node@24.12.0)(esbuild@0.27.4)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3)) '@vitejs/plugin-vue': specifier: ^6.0.1 version: 6.0.4(vite@8.0.0(@types/node@24.12.0)(esbuild@0.27.4)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3)) @@ -102,6 +111,9 @@ importers: vue-tsc: specifier: ^3.1.4 version: 3.2.5(typescript@5.9.3) + zod: + specifier: ^4.3.6 + version: 4.3.6 devDependencies: '@types/node': specifier: ^24.10.1 @@ -337,6 +349,14 @@ packages: '@fastify/static@9.0.0': resolution: {integrity: sha512-r64H8Woe/vfilg5RTy7lwWlE8ZZcTrc3kebYFMEUBrMqlydhQyoiExQXdYAy2REVpST/G35+stAM8WYp1WGmMA==} + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/vue@5.0.0': + resolution: {integrity: sha512-C+KuEWIF5nSBrobFJhT//JS87OZ++QDORB6f2q2Wm6fl2mueSTpFBeBsveK0KW9hWiZ4mNiPjsh6Zs4jjdROSg==} + peerDependencies: + vue: '>=3' + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -410,10 +430,140 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@oxc-parser/binding-android-arm-eabi@0.106.0': + resolution: {integrity: sha512-uoo8Bbc0/UrsQHlpdelqz8+jQ5hQqJs6MKjeiGqSU0E5Dkben2PuxXjg2jmabT+TzclysNEyE7eKHGTA7uVVqQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxc-parser/binding-android-arm64@0.106.0': + resolution: {integrity: sha512-7+hnrpce0uX96Hu8seWMJXqDnBTtSikibn1xa1yCa/musU1XZOLznhdWKA1usaPnwLBXP+7+h6nrdvKZ4HoT5Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-parser/binding-darwin-arm64@0.106.0': + resolution: {integrity: sha512-J7d6j8PwicRXTL4I00eWhqupuq0Pei9EafTzoB7ccluNo5fXNspkIH1NtGpgxPsLyUkZy5Nb5J3Y80TpdX6yQA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-parser/binding-darwin-x64@0.106.0': + resolution: {integrity: sha512-5LhQlSACZPeyxbcE8WNMW1s88ExWGRnk0LQbQ3Co3gYkmgw12x2q6RnPT0N9BC6490VnWsynFafwCMPSrMnjfg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-parser/binding-freebsd-x64@0.106.0': + resolution: {integrity: sha512-IInBOOMzB54rV/s8K5Feu6krWNHMR/V52prXy+9B0GhjOSQ2Q7EAd8y1gXWgjKB0NMDychCLgdaInanUn45eyQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-parser/binding-linux-arm-gnueabihf@0.106.0': + resolution: {integrity: sha512-p0IQvugmAsA2288b30FP5ncbcp6juBQrsZNZD6SDiWRY3X3g5OH5puVtihE5KMNkeHmmd3S8MEHFCv0G1tYGPA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm-musleabihf@0.106.0': + resolution: {integrity: sha512-VgJPJVygSyFEfFtv6hscx9AbnewsxDUCxWmgrB/GHktoMlDQSDBh9aG1lENiiJnB2FLR8WG15446X3Mw2I4Zog==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm64-gnu@0.106.0': + resolution: {integrity: sha512-Gqs6q/pwlpgzx5qE2RtlTnY7hJuS1a5PYBT3unpSAMUE0LrbV7kQ8thmQo1ngI1tnCImWpuuXjZ2YbI0iKquXw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-arm64-musl@0.106.0': + resolution: {integrity: sha512-Bvtp8SK4MyahReapEPodracfBV9ed7+5WCHyjhSWoljrapJIU4OOLSsRyZ9zV2KhkjuD66DZq/qQv6pC73zzWQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-ppc64-gnu@0.106.0': + resolution: {integrity: sha512-DIXyavnpbBo+F/4G04LZ4xuuGXDY4m9qHB/HWtVj9z+Frb/r+SPAuptqAZFtJ9avcwbAOe3LO+K8BWHmK6+lnw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-riscv64-gnu@0.106.0': + resolution: {integrity: sha512-VdqTcLTET72nPcJkSz3xrpcxab7q2/z04d6y+Th1mUTyXs2b/9VC3BcDmaFAfmhz8GX/5FVuzUTQzda1mTsh/g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-riscv64-musl@0.106.0': + resolution: {integrity: sha512-FgHBGg9DHQ0dePOWQ9rNN+DHueJa1XWHc9u0VJCVY+XXAx3iT2ASj21xZ1wA+Rh92CyuuZ7RpQ6Y+O57fieNlg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-s390x-gnu@0.106.0': + resolution: {integrity: sha512-fEIx2bUggt+s1eTaRVzhy5VgdrO1B8tUKxOPpGwwdF9VSP0KnLPaAv/gA4trJPxuIjjJRRVoK42v9R4O1jkbLg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-x64-gnu@0.106.0': + resolution: {integrity: sha512-DbDQkdK8ZuS/jnRx8UbESQ5ypCJpD7VpERB/RWZfSdA2+B4TbonDwNWbTU+q2VJTbh5Xq1X65eQyz4/MIfiFSQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-x64-musl@0.106.0': + resolution: {integrity: sha512-D0PbaLv1MyNFDmjY4UqLQFlC+0GPCvrzI/8VlAvG7ztAZx0KdFYT3pPGsHjKshUJW9+e42JK29abLd0bZ4I95w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-openharmony-arm64@0.106.0': + resolution: {integrity: sha512-uXSzts/ghlqmWm1cQTctyxdAnvha5dzVW5JkEB30J4M47yj2FcCtzUGdZO/sgXxggD/QM7EANlB66cOyk/NsoA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxc-parser/binding-wasm32-wasi@0.106.0': + resolution: {integrity: sha512-oU8wkw9U1vhkICQIJLX8uy1lCPJqXf7aAidaqT2wJOce4a9XmGr2YNseEKbmVV/1TQaSHpHZNsDXglYicb4qKQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-parser/binding-win32-arm64-msvc@0.106.0': + resolution: {integrity: sha512-zYRSn6MNlL8qcUIPRQWDu1JdgVqZa5iR4Drld8FBue3fHQGL0XrNQEd8qoWmuNo7FI0WiBRRuVgtkPaNoSsYmg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-parser/binding-win32-ia32-msvc@0.106.0': + resolution: {integrity: sha512-FRHVO84i5WgQDk0XI4oRt2qDhRUXyot2EGBSogp34LoE5hsondyuZ244+Fod9czgscmgSb6Aon8PaEhHQ0lJYg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxc-parser/binding-win32-x64-msvc@0.106.0': + resolution: {integrity: sha512-ydMjY15RdfRZZa7RrP+jjeudbDFDqKo5CGDTxvYBJ4jpROvVo0ThqN85vvNfVJ55gEUSjodCqvmA30qNTBZd/A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@oxc-project/runtime@0.115.0': resolution: {integrity: sha512-Rg8Wlt5dCbXhQnsXPrkOjL1DTSvXLgb2R/KYfnf1/K+R0k6UMLEmbQXPM+kwrWqSmWA2t0B1EtHy2/3zikQpvQ==} engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-project/types@0.106.0': + resolution: {integrity: sha512-QdsH3rZq480VnOHSHgPYOhjL8O8LBdcnSjM408BpPCCUc0JYYZPG9Gafl9i3OcGk/7137o+gweb4cCv3WAUykg==} + '@oxc-project/types@0.115.0': resolution: {integrity: sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==} @@ -553,6 +703,9 @@ packages: '@types/argparse@1.0.38': resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/node@24.1.0': resolution: {integrity: sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==} @@ -562,6 +715,15 @@ packages: '@types/web-push@3.6.4': resolution: {integrity: sha512-GnJmSr40H3RAnj0s34FNTcJi1hmWFV5KXugE0mYWnYhgTAHLJ/dJKAwDmvPJYMke0RplY2XE9LnM4hqSqKIjhQ==} + '@unhead/vue@3.0.0-beta.9': + resolution: {integrity: sha512-X66jeffzB+IH3cXBPLhbBAFCsTuTVYD0+0N5aelNw5MbE9CpNnLJCIcwUyqh/UdEwaBMehRNPlGY+fiRrapRqQ==} + peerDependencies: + vite: '>=6' + vue: '>=3.5.18' + peerDependenciesMeta: + vite: + optional: true + '@vitejs/plugin-vue@6.0.4': resolution: {integrity: sha512-uM5iXipgYIn13UUQCZNdWkYk+sysBeA97d5mHsAoAt1u/wpN3+zxOmsVJWosuzX+IMGRzeYUNytztrYznboIkQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -925,6 +1087,9 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + exsolve@1.0.8: resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} @@ -1066,6 +1231,9 @@ packages: hookable@5.5.3: resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + hookable@6.1.0: + resolution: {integrity: sha512-ZoKZSJgu8voGK2geJS+6YtYjvIzu9AOM/KZXsBxr83uhLL++e9pEv/dlgwgy3dvHg06kTz6JOh1hk3C8Ceiymw==} + http-errors@2.0.1: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} @@ -1302,6 +1470,9 @@ packages: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} + magic-regexp@0.10.0: + resolution: {integrity: sha512-Uly1Bu4lO1hwHUW0CQeSWuRtzCMNO00CmXtS8N6fyvB3B979GOEEeAkiTUDsmbYLAbvpUS/Kt5c4ibosAzVyVg==} + magic-string-ast@1.0.3: resolution: {integrity: sha512-CvkkH1i81zl7mmb94DsRiFeG9V2fR2JeuK8yDgS8oiZSFa++wWLEgZ5ufEOyLHbvSbD1gTRKv9NdX69Rnvr9JA==} engines: {node: '>=20.19.0'} @@ -1395,6 +1566,15 @@ packages: os@0.1.2: resolution: {integrity: sha512-ZoXJkvAnljwvc56MbvhtKVWmSkzV712k42Is2mA0+0KTSRakq5XXuXpjZjgAt9ctzl51ojhQWakQQpmOvXWfjQ==} + oxc-parser@0.106.0: + resolution: {integrity: sha512-KSqA8PNgqi+wadUoGJXWyTr0mLuMzEABXQK5hKlj+cEWID+Rhw8xiqLappTDaCUpOqnKCpyO9N5RlzlFxR+TBw==} + engines: {node: ^20.19.0 || >=22.12.0} + + oxc-walker@0.7.0: + resolution: {integrity: sha512-54B4KUhrzbzc4sKvKwVYm7E2PgeROpGba0/2nlNZMqfDyca+yOor5IMb4WLGBatGDT0nkzYdYuzylg7n3YfB7A==} + peerDependencies: + oxc-parser: '>=0.98.0' + path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} @@ -1582,6 +1762,10 @@ packages: reflect-metadata@0.2.2: resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + regexp-tree@0.1.27: + resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} + hasBin: true + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -1775,6 +1959,9 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} + type-level-regexp@0.1.17: + resolution: {integrity: sha512-wTk4DH3cxwk196uGLK/E9pE45aLfeKJacKmcEgEOA/q5dnPGNxXt0cfYdFxb57L+sEpf1oJH4Dnx/pnRcku9jg==} + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -1793,6 +1980,14 @@ packages: undici-types@7.8.0: resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} + unhead@3.0.0-beta.9: + resolution: {integrity: sha512-1GVW+FnpPk3/kdrkqELkhu7XgD6brEezJAESLfy05Y581T1CdpMklcBkKMm7Q5vY1nivrLEVos6C7j9lKEM9oQ==} + peerDependencies: + vite: '>=6' + peerDependenciesMeta: + vite: + optional: true + universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -1801,6 +1996,10 @@ packages: resolution: {integrity: sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==} engines: {node: '>=20.19.0'} + unplugin@2.3.11: + resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==} + engines: {node: '>=18.12.0'} + unplugin@3.0.0: resolution: {integrity: sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2109,6 +2308,13 @@ snapshots: fastq: 1.20.1 glob: 13.0.6 + '@iconify/types@2.0.0': {} + + '@iconify/vue@5.0.0(vue@3.5.30(typescript@5.9.3))': + dependencies: + '@iconify/types': 2.0.0 + vue: 3.5.30(typescript@5.9.3) + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -2218,8 +2424,72 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 + '@oxc-parser/binding-android-arm-eabi@0.106.0': + optional: true + + '@oxc-parser/binding-android-arm64@0.106.0': + optional: true + + '@oxc-parser/binding-darwin-arm64@0.106.0': + optional: true + + '@oxc-parser/binding-darwin-x64@0.106.0': + optional: true + + '@oxc-parser/binding-freebsd-x64@0.106.0': + optional: true + + '@oxc-parser/binding-linux-arm-gnueabihf@0.106.0': + optional: true + + '@oxc-parser/binding-linux-arm-musleabihf@0.106.0': + optional: true + + '@oxc-parser/binding-linux-arm64-gnu@0.106.0': + optional: true + + '@oxc-parser/binding-linux-arm64-musl@0.106.0': + optional: true + + '@oxc-parser/binding-linux-ppc64-gnu@0.106.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-gnu@0.106.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-musl@0.106.0': + optional: true + + '@oxc-parser/binding-linux-s390x-gnu@0.106.0': + optional: true + + '@oxc-parser/binding-linux-x64-gnu@0.106.0': + optional: true + + '@oxc-parser/binding-linux-x64-musl@0.106.0': + optional: true + + '@oxc-parser/binding-openharmony-arm64@0.106.0': + optional: true + + '@oxc-parser/binding-wasm32-wasi@0.106.0': + dependencies: + '@napi-rs/wasm-runtime': 1.1.1 + optional: true + + '@oxc-parser/binding-win32-arm64-msvc@0.106.0': + optional: true + + '@oxc-parser/binding-win32-ia32-msvc@0.106.0': + optional: true + + '@oxc-parser/binding-win32-x64-msvc@0.106.0': + optional: true + '@oxc-project/runtime@0.115.0': {} + '@oxc-project/types@0.106.0': {} + '@oxc-project/types@0.115.0': {} '@phc/format@1.0.0': {} @@ -2319,6 +2589,8 @@ snapshots: '@types/argparse@1.0.38': {} + '@types/estree@1.0.8': {} + '@types/node@24.1.0': dependencies: undici-types: 7.8.0 @@ -2331,6 +2603,17 @@ snapshots: dependencies: '@types/node': 24.12.0 + '@unhead/vue@3.0.0-beta.9(vite@8.0.0(@types/node@24.12.0)(esbuild@0.27.4)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3))': + dependencies: + hookable: 6.1.0 + magic-string: 0.30.21 + oxc-parser: 0.106.0 + oxc-walker: 0.7.0(oxc-parser@0.106.0) + unhead: 3.0.0-beta.9(vite@8.0.0(@types/node@24.12.0)(esbuild@0.27.4)(tsx@4.21.0)(yaml@2.8.2)) + vue: 3.5.30(typescript@5.9.3) + optionalDependencies: + vite: 8.0.0(@types/node@24.12.0)(esbuild@0.27.4)(tsx@4.21.0)(yaml@2.8.2) + '@vitejs/plugin-vue@6.0.4(vite@8.0.0(@types/node@24.12.0)(esbuild@0.27.4)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.2 @@ -2729,6 +3012,10 @@ snapshots: estree-walker@2.0.2: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + exsolve@1.0.8: {} fast-decode-uri-component@1.0.1: {} @@ -2895,6 +3182,8 @@ snapshots: hookable@5.5.3: {} + hookable@6.1.0: {} + http-errors@2.0.1: dependencies: depd: 2.0.0 @@ -3096,6 +3385,16 @@ snapshots: dependencies: yallist: 4.0.0 + magic-regexp@0.10.0: + dependencies: + estree-walker: 3.0.3 + magic-string: 0.30.21 + mlly: 1.8.1 + regexp-tree: 0.1.27 + type-level-regexp: 0.1.17 + ufo: 1.6.3 + unplugin: 2.3.11 + magic-string-ast@1.0.3: dependencies: magic-string: 0.30.21 @@ -3167,6 +3466,36 @@ snapshots: os@0.1.2: {} + oxc-parser@0.106.0: + dependencies: + '@oxc-project/types': 0.106.0 + optionalDependencies: + '@oxc-parser/binding-android-arm-eabi': 0.106.0 + '@oxc-parser/binding-android-arm64': 0.106.0 + '@oxc-parser/binding-darwin-arm64': 0.106.0 + '@oxc-parser/binding-darwin-x64': 0.106.0 + '@oxc-parser/binding-freebsd-x64': 0.106.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.106.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.106.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.106.0 + '@oxc-parser/binding-linux-arm64-musl': 0.106.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.106.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.106.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.106.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.106.0 + '@oxc-parser/binding-linux-x64-gnu': 0.106.0 + '@oxc-parser/binding-linux-x64-musl': 0.106.0 + '@oxc-parser/binding-openharmony-arm64': 0.106.0 + '@oxc-parser/binding-wasm32-wasi': 0.106.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.106.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.106.0 + '@oxc-parser/binding-win32-x64-msvc': 0.106.0 + + oxc-walker@0.7.0(oxc-parser@0.106.0): + dependencies: + magic-regexp: 0.10.0 + oxc-parser: 0.106.0 + path-browserify@1.0.1: {} path-is-absolute@1.0.1: {} @@ -3338,6 +3667,8 @@ snapshots: reflect-metadata@0.2.2: {} + regexp-tree@0.1.27: {} + require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -3521,6 +3852,8 @@ snapshots: type-fest@4.41.0: {} + type-level-regexp@0.1.17: {} + typescript@5.9.3: {} ufo@1.6.3: {} @@ -3539,6 +3872,13 @@ snapshots: undici-types@7.8.0: {} + unhead@3.0.0-beta.9(vite@8.0.0(@types/node@24.12.0)(esbuild@0.27.4)(tsx@4.21.0)(yaml@2.8.2)): + dependencies: + hookable: 6.1.0 + magic-string: 0.30.21 + optionalDependencies: + vite: 8.0.0(@types/node@24.12.0)(esbuild@0.27.4)(tsx@4.21.0)(yaml@2.8.2) + universalify@2.0.1: {} unplugin-utils@0.3.1: @@ -3546,6 +3886,13 @@ snapshots: pathe: 2.0.3 picomatch: 4.0.3 + unplugin@2.3.11: + dependencies: + '@jridgewell/remapping': 2.3.5 + acorn: 8.16.0 + picomatch: 4.0.3 + webpack-virtual-modules: 0.6.2 + unplugin@3.0.0: dependencies: '@jridgewell/remapping': 2.3.5