completely cleanup of the legacy hono router to leaner svelte remote functions
This commit is contained in:
@@ -21,19 +21,16 @@
|
|||||||
"@opentelemetry/exporter-logs-otlp-proto": "^0.212.0",
|
"@opentelemetry/exporter-logs-otlp-proto": "^0.212.0",
|
||||||
"@opentelemetry/exporter-metrics-otlp-proto": "^0.212.0",
|
"@opentelemetry/exporter-metrics-otlp-proto": "^0.212.0",
|
||||||
"@opentelemetry/exporter-trace-otlp-proto": "^0.212.0",
|
"@opentelemetry/exporter-trace-otlp-proto": "^0.212.0",
|
||||||
"@opentelemetry/sdk-node": "^0.212.0",
|
|
||||||
"@opentelemetry/sdk-logs": "^0.212.0",
|
"@opentelemetry/sdk-logs": "^0.212.0",
|
||||||
|
"@opentelemetry/sdk-node": "^0.212.0",
|
||||||
"@pkg/db": "workspace:*",
|
"@pkg/db": "workspace:*",
|
||||||
"@pkg/logger": "workspace:*",
|
"@pkg/logger": "workspace:*",
|
||||||
"@pkg/logic": "workspace:*",
|
"@pkg/logic": "workspace:*",
|
||||||
"@pkg/result": "workspace:*",
|
"@pkg/result": "workspace:*",
|
||||||
"@pkg/settings": "workspace:*",
|
"@pkg/settings": "workspace:*",
|
||||||
"@tanstack/svelte-query": "^6.0.18",
|
|
||||||
"better-auth": "^1.4.20",
|
"better-auth": "^1.4.20",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"hono": "^4.11.1",
|
|
||||||
"import-in-the-middle": "^3.0.0",
|
"import-in-the-middle": "^3.0.0",
|
||||||
"marked": "^17.0.1",
|
|
||||||
"nanoid": "^5.1.6",
|
"nanoid": "^5.1.6",
|
||||||
"neverthrow": "^8.2.0",
|
"neverthrow": "^8.2.0",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
import { usersRouter } from "@pkg/logic/domains/user/router";
|
|
||||||
import { twofaRouter } from "@pkg/logic/domains/2fa/router";
|
|
||||||
import { Hono } from "hono";
|
|
||||||
|
|
||||||
const baseRouter = new Hono()
|
|
||||||
.route("/users", usersRouter)
|
|
||||||
.route("/twofactor", twofaRouter);
|
|
||||||
|
|
||||||
export const apiBasePath = "/api/v1";
|
|
||||||
|
|
||||||
export const api = new Hono().route(apiBasePath, baseRouter);
|
|
||||||
|
|
||||||
export type Router = typeof baseRouter;
|
|
||||||
@@ -1,183 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import hljs from "highlight.js/lib/core";
|
|
||||||
import { marked } from "marked";
|
|
||||||
// Import common languages for syntax highlighting
|
|
||||||
import bash from "highlight.js/lib/languages/bash";
|
|
||||||
import css from "highlight.js/lib/languages/css";
|
|
||||||
import javascript from "highlight.js/lib/languages/javascript";
|
|
||||||
import json from "highlight.js/lib/languages/json";
|
|
||||||
import python from "highlight.js/lib/languages/python";
|
|
||||||
import sql from "highlight.js/lib/languages/sql";
|
|
||||||
import typescript from "highlight.js/lib/languages/typescript";
|
|
||||||
import xml from "highlight.js/lib/languages/xml";
|
|
||||||
|
|
||||||
// Register languages
|
|
||||||
hljs.registerLanguage("javascript", javascript);
|
|
||||||
hljs.registerLanguage("typescript", typescript);
|
|
||||||
hljs.registerLanguage("python", python);
|
|
||||||
hljs.registerLanguage("sql", sql);
|
|
||||||
hljs.registerLanguage("json", json);
|
|
||||||
hljs.registerLanguage("xml", xml);
|
|
||||||
hljs.registerLanguage("css", css);
|
|
||||||
hljs.registerLanguage("bash", bash);
|
|
||||||
hljs.registerLanguage("html", xml); // HTML is an alias for XML
|
|
||||||
hljs.registerLanguage("js", javascript); // JS is an alias for JavaScript
|
|
||||||
hljs.registerLanguage("ts", typescript); // TS is an alias for TypeScript
|
|
||||||
hljs.registerLanguage("sh", bash); // SH is an alias for Bash
|
|
||||||
hljs.registerLanguage("shell", bash); // alias
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
content: string;
|
|
||||||
class?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { content, class: className = "" }: Props = $props();
|
|
||||||
|
|
||||||
// Configure marked with custom extensions
|
|
||||||
marked.use({
|
|
||||||
renderer: {
|
|
||||||
codespan(token: any) {
|
|
||||||
return `<code class="bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded text-sm font-mono">${token.text}</code>`;
|
|
||||||
},
|
|
||||||
|
|
||||||
blockquote(token: any) {
|
|
||||||
return `<blockquote class="border-l-4 border-gray-300 dark:border-gray-600 pl-4 italic text-gray-700 dark:text-gray-300 my-4">${this.parser.parseInline(token.tokens)}</blockquote>`;
|
|
||||||
},
|
|
||||||
|
|
||||||
table(token: any) {
|
|
||||||
let header = "";
|
|
||||||
let body = "";
|
|
||||||
|
|
||||||
// Process header
|
|
||||||
if (token.header && token.header.length > 0) {
|
|
||||||
header = "<thead><tr>";
|
|
||||||
for (const cell of token.header) {
|
|
||||||
header += `<th class="px-4 py-2 bg-secondary dark:bg-secondary font-semibold text-left">${this.parser.parseInline(cell.tokens)}</th>`;
|
|
||||||
}
|
|
||||||
header += "</tr></thead>";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process rows
|
|
||||||
if (token.rows && token.rows.length > 0) {
|
|
||||||
body = "<tbody>";
|
|
||||||
for (const row of token.rows) {
|
|
||||||
body += "<tr class='border-b border-border";
|
|
||||||
for (const cell of row) {
|
|
||||||
body += `<td class="px-4 py-2">${this.parser.parseInline(cell.tokens)}</td>`;
|
|
||||||
}
|
|
||||||
body += "</tr>";
|
|
||||||
}
|
|
||||||
body += "</tbody>";
|
|
||||||
}
|
|
||||||
|
|
||||||
return `<div class="overflow-x-auto my-4"><table class="min-w-full border border-border">${header}${body}</table></div>`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
gfm: true,
|
|
||||||
breaks: true,
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Include highlight.js CSS for syntax highlighting -->
|
|
||||||
<svelte:head>
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css"
|
|
||||||
media="(prefers-color-scheme: dark)"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css"
|
|
||||||
media="(prefers-color-scheme: light)"
|
|
||||||
/>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="prose prose-sm dark:prose-invert max-w-none {className}"
|
|
||||||
class:prose-gray={true}
|
|
||||||
>
|
|
||||||
{@html marked(content)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
@reference "../../../app.css";
|
|
||||||
|
|
||||||
:global(.prose h1) {
|
|
||||||
@apply mt-6 mb-4 text-2xl font-bold text-gray-900 dark:text-gray-100;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.prose h2) {
|
|
||||||
@apply mt-5 mb-3 text-xl font-semibold text-gray-900 dark:text-gray-100;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.prose h3) {
|
|
||||||
@apply mt-4 mb-2 text-lg font-medium text-gray-900 dark:text-gray-100;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.prose p) {
|
|
||||||
@apply mb-3 leading-relaxed text-gray-700 dark:text-gray-300;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.prose a) {
|
|
||||||
@apply text-blue-600 underline hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.prose strong) {
|
|
||||||
@apply font-semibold text-gray-900 dark:text-gray-100;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.prose em) {
|
|
||||||
@apply text-gray-700 italic dark:text-gray-300;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.prose ul) {
|
|
||||||
@apply my-4 ml-4 list-inside list-disc space-y-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.prose ol) {
|
|
||||||
@apply my-4 ml-4 list-inside list-decimal space-y-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.prose li) {
|
|
||||||
@apply leading-relaxed text-gray-700 dark:text-gray-300;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.prose ul ul) {
|
|
||||||
@apply mt-1 mb-1 ml-4;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.prose ol ol) {
|
|
||||||
@apply mt-1 mb-1 ml-4;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.prose ul ol) {
|
|
||||||
@apply mt-1 mb-1 ml-4;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.prose ol ul) {
|
|
||||||
@apply mt-1 mb-1 ml-4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Code block scrollbar styling */
|
|
||||||
:global(.prose pre) {
|
|
||||||
scrollbar-width: thin;
|
|
||||||
scrollbar-color: rgba(255, 255, 255, 0.3) transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.prose pre::-webkit-scrollbar) {
|
|
||||||
height: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.prose pre::-webkit-scrollbar-track) {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.prose pre::-webkit-scrollbar-thumb) {
|
|
||||||
background: rgba(255, 255, 255, 0.3);
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.prose pre::-webkit-scrollbar-thumb:hover) {
|
|
||||||
background: rgba(255, 255, 255, 0.5);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { FlowExecCtx } from "@pkg/logic/core/flow.execution.context";
|
import type { FlowExecCtx } from "@pkg/logic/core/flow.execution.context";
|
||||||
|
import type { Err } from "@pkg/result";
|
||||||
|
|
||||||
export async function getFlowExecCtxForRemoteFuncs(
|
export async function getFlowExecCtxForRemoteFuncs(
|
||||||
locals: App.Locals,
|
locals: App.Locals,
|
||||||
@@ -9,3 +10,16 @@ export async function getFlowExecCtxForRemoteFuncs(
|
|||||||
sessionId: locals.session?.id,
|
sessionId: locals.session?.id,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function unauthorized(fctx: FlowExecCtx) {
|
||||||
|
return {
|
||||||
|
data: null,
|
||||||
|
error: {
|
||||||
|
flowId: fctx.flowId,
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "User not authenticated",
|
||||||
|
description: "Please log in",
|
||||||
|
detail: "No user ID found in session",
|
||||||
|
} as Err,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
export function formatCurrency(amount: number, currency = "EUR"): string {
|
|
||||||
return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(
|
|
||||||
amount,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,22 +1,168 @@
|
|||||||
import { ensureAccountExistsSchema } from "@pkg/logic/domains/user/data";
|
import {
|
||||||
|
banUserSchema,
|
||||||
|
checkUsernameSchema,
|
||||||
|
ensureAccountExistsSchema,
|
||||||
|
rotatePasswordSchema,
|
||||||
|
} from "@pkg/logic/domains/user/data";
|
||||||
|
import {
|
||||||
|
getFlowExecCtxForRemoteFuncs,
|
||||||
|
unauthorized,
|
||||||
|
} from "$lib/core/server.utils";
|
||||||
|
import { getUserController } from "@pkg/logic/domains/user/controller";
|
||||||
import { command, getRequestEvent, query } from "$app/server";
|
import { command, getRequestEvent, query } from "$app/server";
|
||||||
|
import * as v from "valibot";
|
||||||
|
|
||||||
|
const uc = getUserController();
|
||||||
|
|
||||||
export const getMyInfoSQ = query(async () => {
|
export const getMyInfoSQ = query(async () => {
|
||||||
const event = getRequestEvent();
|
const event = getRequestEvent();
|
||||||
// .... do stuff usually done in a router here
|
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||||
|
|
||||||
return { data: "testing" };
|
if (!fctx.userId) {
|
||||||
|
return unauthorized(fctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await uc.getUserInfo(fctx, fctx.userId);
|
||||||
|
return res.isOk()
|
||||||
|
? { data: res.value, error: null }
|
||||||
|
: { data: null, error: res.error };
|
||||||
});
|
});
|
||||||
|
|
||||||
export const getUsersInfoByIdSQ = query(async () => {
|
export const getUserInfoByIdSQ = query(
|
||||||
// .... do stuff usually done in a router here
|
v.object({ userId: v.string() }),
|
||||||
return { data: "testing" };
|
async (input) => {
|
||||||
});
|
const event = getRequestEvent();
|
||||||
|
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||||
|
|
||||||
export const ensureAccountExistsSQ = command(
|
if (!fctx.userId) {
|
||||||
ensureAccountExistsSchema,
|
return unauthorized(fctx);
|
||||||
async (payload) => {
|
}
|
||||||
// .... do stuff usually done in a router here
|
|
||||||
return { data: "testing" };
|
const res = await uc.getUserInfo(fctx, input.userId);
|
||||||
|
return res.isOk()
|
||||||
|
? { data: res.value, error: null }
|
||||||
|
: { data: null, error: res.error };
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ensureAccountExistsSC = command(
|
||||||
|
ensureAccountExistsSchema,
|
||||||
|
async (payload) => {
|
||||||
|
const event = getRequestEvent();
|
||||||
|
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||||
|
|
||||||
|
if (!fctx.userId) {
|
||||||
|
return unauthorized(fctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await uc.ensureAccountExists(fctx, payload.userId);
|
||||||
|
return res.isOk()
|
||||||
|
? { data: res.value, error: null }
|
||||||
|
: { data: null, error: res.error };
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const checkUsernameSC = command(checkUsernameSchema, async (payload) => {
|
||||||
|
const event = getRequestEvent();
|
||||||
|
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||||
|
|
||||||
|
if (!fctx.userId) {
|
||||||
|
return unauthorized(fctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await uc.isUsernameAvailable(fctx, payload.username);
|
||||||
|
return res.isOk()
|
||||||
|
? { data: res.value, error: null }
|
||||||
|
: { data: null, error: res.error };
|
||||||
|
});
|
||||||
|
|
||||||
|
export const update2faVerifiedSC = command(
|
||||||
|
v.object({ userId: v.string() }),
|
||||||
|
async (payload) => {
|
||||||
|
const event = getRequestEvent();
|
||||||
|
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||||
|
|
||||||
|
if (!fctx.userId) {
|
||||||
|
return unauthorized(fctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await uc.updateLastVerified2FaAtToNow(fctx, payload.userId);
|
||||||
|
return res.isOk()
|
||||||
|
? { data: res.value, error: null }
|
||||||
|
: { data: null, error: res.error };
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const banUserSC = command(banUserSchema, async (payload) => {
|
||||||
|
const event = getRequestEvent();
|
||||||
|
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||||
|
|
||||||
|
if (!fctx.userId) {
|
||||||
|
return unauthorized(fctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await uc.banUser(
|
||||||
|
fctx,
|
||||||
|
payload.userId,
|
||||||
|
payload.reason,
|
||||||
|
payload.banExpiresAt,
|
||||||
|
);
|
||||||
|
return res.isOk()
|
||||||
|
? { data: res.value, error: null }
|
||||||
|
: { data: null, error: res.error };
|
||||||
|
});
|
||||||
|
|
||||||
|
export const isUserBannedSQ = query(
|
||||||
|
v.object({ userId: v.string() }),
|
||||||
|
async (input) => {
|
||||||
|
const event = getRequestEvent();
|
||||||
|
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||||
|
|
||||||
|
if (!fctx.userId) {
|
||||||
|
return unauthorized(fctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await uc.isUserBanned(fctx, input.userId);
|
||||||
|
return res.isOk()
|
||||||
|
? { data: res.value, error: null }
|
||||||
|
: { data: null, error: res.error };
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getBanInfoSQ = query(
|
||||||
|
v.object({ userId: v.string() }),
|
||||||
|
async (input) => {
|
||||||
|
const event = getRequestEvent();
|
||||||
|
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||||
|
|
||||||
|
if (!fctx.userId) {
|
||||||
|
return unauthorized(fctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await uc.getBanInfo(fctx, input.userId);
|
||||||
|
return res.isOk()
|
||||||
|
? { data: res.value, error: null }
|
||||||
|
: { data: null, error: res.error };
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const rotatePasswordSC = command(
|
||||||
|
rotatePasswordSchema,
|
||||||
|
async (payload) => {
|
||||||
|
const event = getRequestEvent();
|
||||||
|
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||||
|
|
||||||
|
if (!fctx.userId) {
|
||||||
|
return unauthorized(fctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await uc.rotatePassword(
|
||||||
|
fctx,
|
||||||
|
payload.userId,
|
||||||
|
payload.password,
|
||||||
|
);
|
||||||
|
return res.isOk()
|
||||||
|
? { data: res.value, error: null }
|
||||||
|
: { data: null, error: res.error };
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
|
import { ResultAsync, errAsync, okAsync } from "neverthrow";
|
||||||
import type { User } from "@pkg/logic/domains/user/data";
|
import type { User } from "@pkg/logic/domains/user/data";
|
||||||
import { apiClient, user as userStore } from "$lib/global.stores";
|
import { user as userStore } from "$lib/global.stores";
|
||||||
|
import { rotatePasswordSC } from "./account.remote";
|
||||||
import { authClient } from "$lib/auth.client";
|
import { authClient } from "$lib/auth.client";
|
||||||
|
import type { Err } from "@pkg/result";
|
||||||
import { toast } from "svelte-sonner";
|
import { toast } from "svelte-sonner";
|
||||||
import { get } from "svelte/store";
|
import { get } from "svelte/store";
|
||||||
import { ResultAsync, errAsync, okAsync } from "neverthrow";
|
|
||||||
import type { Err } from "@pkg/result";
|
|
||||||
|
|
||||||
class AccountViewModel {
|
class AccountViewModel {
|
||||||
loading = $state(false);
|
loading = $state(false);
|
||||||
@@ -20,8 +21,7 @@ class AccountViewModel {
|
|||||||
description: "Network request failed",
|
description: "Network request failed",
|
||||||
detail: error instanceof Error ? error.message : String(error),
|
detail: error instanceof Error ? error.message : String(error),
|
||||||
}),
|
}),
|
||||||
)
|
).andThen((response) => {
|
||||||
.andThen((response) => {
|
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
return errAsync({
|
return errAsync({
|
||||||
code: "API_ERROR",
|
code: "API_ERROR",
|
||||||
@@ -29,8 +29,7 @@ class AccountViewModel {
|
|||||||
response.error.message ??
|
response.error.message ??
|
||||||
"Failed to update profile picture",
|
"Failed to update profile picture",
|
||||||
description:
|
description:
|
||||||
response.error.statusText ??
|
response.error.statusText ?? "Please try again later",
|
||||||
"Please try again later",
|
|
||||||
detail: response.error.statusText ?? "Unknown error",
|
detail: response.error.statusText ?? "Unknown error",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -43,7 +42,8 @@ class AccountViewModel {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
this.errorMessage = error.message ?? "Failed to update profile picture";
|
this.errorMessage =
|
||||||
|
error.message ?? "Failed to update profile picture";
|
||||||
toast.error(this.errorMessage, {
|
toast.error(this.errorMessage, {
|
||||||
description: error.description,
|
description: error.description,
|
||||||
});
|
});
|
||||||
@@ -71,16 +71,14 @@ class AccountViewModel {
|
|||||||
description: "Network request failed",
|
description: "Network request failed",
|
||||||
detail: error instanceof Error ? error.message : String(error),
|
detail: error instanceof Error ? error.message : String(error),
|
||||||
}),
|
}),
|
||||||
)
|
).andThen((response) => {
|
||||||
.andThen((response) => {
|
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
return errAsync({
|
return errAsync({
|
||||||
code: "API_ERROR",
|
code: "API_ERROR",
|
||||||
message:
|
message:
|
||||||
response.error.message ?? "Failed to update profile",
|
response.error.message ?? "Failed to update profile",
|
||||||
description:
|
description:
|
||||||
response.error.statusText ??
|
response.error.statusText ?? "Please try again later",
|
||||||
"Please try again later",
|
|
||||||
detail: response.error.statusText ?? "Unknown error",
|
detail: response.error.statusText ?? "Unknown error",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -117,51 +115,19 @@ class AccountViewModel {
|
|||||||
toast.error(this.errorMessage);
|
toast.error(this.errorMessage);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const client = get(apiClient);
|
|
||||||
if (!client) {
|
|
||||||
this.passwordLoading = false;
|
|
||||||
this.errorMessage = "API client not initialized";
|
|
||||||
toast.error(this.errorMessage);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await ResultAsync.fromPromise(
|
const result = await ResultAsync.fromPromise(
|
||||||
client.users["rotate-password"].$put({
|
rotatePasswordSC({ userId: currentUser.id, password }),
|
||||||
json: { userId: currentUser.id, password },
|
|
||||||
}),
|
|
||||||
(error): Err => ({
|
(error): Err => ({
|
||||||
code: "NETWORK_ERROR",
|
code: "NETWORK_ERROR",
|
||||||
message: "Failed to change password",
|
message: "Failed to change password",
|
||||||
description: "Network request failed",
|
description: "Network request failed",
|
||||||
detail: error instanceof Error ? error.message : String(error),
|
detail: error instanceof Error ? error.message : String(error),
|
||||||
}),
|
}),
|
||||||
)
|
|
||||||
.andThen((response) => {
|
|
||||||
if (!response.ok) {
|
|
||||||
return errAsync({
|
|
||||||
code: "API_ERROR",
|
|
||||||
message: "Failed to change password",
|
|
||||||
description: `Response failed with status ${response.status}`,
|
|
||||||
detail: `HTTP ${response.status}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return ResultAsync.fromPromise(
|
|
||||||
response.json(),
|
|
||||||
(error): Err => ({
|
|
||||||
code: "PARSING_ERROR",
|
|
||||||
message: "Failed to parse response",
|
|
||||||
description: "Invalid response format",
|
|
||||||
detail:
|
|
||||||
error instanceof Error
|
|
||||||
? error.message
|
|
||||||
: String(error),
|
|
||||||
}),
|
|
||||||
).andThen((apiResult: any) => {
|
).andThen((apiResult: any) => {
|
||||||
if (apiResult.error) {
|
if (apiResult?.error) {
|
||||||
return errAsync(apiResult.error);
|
return errAsync(apiResult.error);
|
||||||
}
|
}
|
||||||
return okAsync(apiResult.data);
|
return okAsync(apiResult?.data);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const success = result.match(
|
const success = result.match(
|
||||||
@@ -170,7 +136,8 @@ class AccountViewModel {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
this.errorMessage = error.message ?? "Failed to change password";
|
this.errorMessage =
|
||||||
|
(error.message as string) ?? "Failed to change password";
|
||||||
toast.error(this.errorMessage, {
|
toast.error(this.errorMessage, {
|
||||||
description: error.description,
|
description: error.description,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,27 +3,15 @@ import {
|
|||||||
getNotificationsSchema,
|
getNotificationsSchema,
|
||||||
} from "@pkg/logic/domains/notifications/data";
|
} from "@pkg/logic/domains/notifications/data";
|
||||||
import { getNotificationController } from "@pkg/logic/domains/notifications/controller";
|
import { getNotificationController } from "@pkg/logic/domains/notifications/controller";
|
||||||
import type { FlowExecCtx } from "@pkg/logic/core/flow.execution.context";
|
import {
|
||||||
import { getFlowExecCtxForRemoteFuncs } from "$lib/core/server.utils";
|
getFlowExecCtxForRemoteFuncs,
|
||||||
|
unauthorized,
|
||||||
|
} from "$lib/core/server.utils";
|
||||||
import { command, getRequestEvent, query } from "$app/server";
|
import { command, getRequestEvent, query } from "$app/server";
|
||||||
import type { Err } from "@pkg/result";
|
|
||||||
import * as v from "valibot";
|
import * as v from "valibot";
|
||||||
|
|
||||||
const nc = getNotificationController();
|
const nc = getNotificationController();
|
||||||
|
|
||||||
export async function unauthorized(fctx: FlowExecCtx) {
|
|
||||||
return {
|
|
||||||
data: null,
|
|
||||||
error: {
|
|
||||||
flowId: fctx.flowId,
|
|
||||||
code: "UNAUTHORIZED",
|
|
||||||
message: "User not authenticated",
|
|
||||||
description: "Please log in",
|
|
||||||
detail: "No user found in request locals",
|
|
||||||
} as Err,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getNotificationsSQ = query(
|
export const getNotificationsSQ = query(
|
||||||
getNotificationsSchema,
|
getNotificationsSchema,
|
||||||
async (input) => {
|
async (input) => {
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { apiClient, session, user } from "$lib/global.stores";
|
import { session, user } from "$lib/global.stores";
|
||||||
import { authClient } from "$lib/auth.client";
|
import { authClient } from "$lib/auth.client";
|
||||||
|
import {
|
||||||
|
startVerificationSessionSC,
|
||||||
|
verifySessionCodeSC,
|
||||||
|
} from "$lib/domains/security/twofa.remote";
|
||||||
import { toast } from "svelte-sonner";
|
import { toast } from "svelte-sonner";
|
||||||
import { get } from "svelte/store";
|
import { get } from "svelte/store";
|
||||||
import { page } from "$app/state";
|
import { page } from "$app/state";
|
||||||
import { ResultAsync, errAsync, okAsync } from "neverthrow";
|
|
||||||
import type { Err } from "@pkg/result";
|
|
||||||
|
|
||||||
class TwoFactorVerifyViewModel {
|
class TwoFactorVerifyViewModel {
|
||||||
verifying = $state(false);
|
verifying = $state(false);
|
||||||
@@ -32,65 +34,34 @@ class TwoFactorVerifyViewModel {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await ResultAsync.fromPromise(
|
try {
|
||||||
get(apiClient).twofactor["start-verification-session"].$post({
|
const result = await startVerificationSessionSC({
|
||||||
json: { userId: uid, sessionId: sid },
|
userId: uid,
|
||||||
}),
|
sessionId: sid,
|
||||||
(error): Err => ({
|
|
||||||
code: "NETWORK_ERROR",
|
|
||||||
message: "Failed to start verification",
|
|
||||||
description: "Network request failed",
|
|
||||||
detail: error instanceof Error ? error.message : String(error),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.andThen((response) => {
|
|
||||||
if (!response.ok) {
|
|
||||||
return errAsync({
|
|
||||||
code: "API_ERROR",
|
|
||||||
message: "Failed to start verification",
|
|
||||||
description: `Response failed with status ${response.status}`,
|
|
||||||
detail: `HTTP ${response.status}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return ResultAsync.fromPromise(
|
|
||||||
response.json(),
|
|
||||||
(error): Err => ({
|
|
||||||
code: "PARSING_ERROR",
|
|
||||||
message: "Failed to parse response",
|
|
||||||
description: "Invalid response format",
|
|
||||||
detail: error instanceof Error ? error.message : String(error),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.andThen((apiResult) => {
|
|
||||||
if (apiResult.error) {
|
|
||||||
return errAsync(apiResult.error);
|
|
||||||
}
|
|
||||||
if (!apiResult.data?.verificationToken) {
|
|
||||||
return errAsync({
|
|
||||||
code: "API_ERROR",
|
|
||||||
message: "No verification token received",
|
|
||||||
description: "Invalid response data",
|
|
||||||
detail: "Missing verificationToken in response",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return okAsync(apiResult.data.verificationToken);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
result.match(
|
if (result?.error || !result?.data?.verificationToken) {
|
||||||
(token) => {
|
this.errorMessage =
|
||||||
this.verificationToken = token;
|
result?.error?.message || "Failed to start verification";
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
this.errorMessage = error.message || "Failed to start verification";
|
|
||||||
toast.error("Failed to start verification", {
|
toast.error("Failed to start verification", {
|
||||||
description: this.errorMessage || "Failed to start verification",
|
description: this.errorMessage,
|
||||||
});
|
});
|
||||||
},
|
return;
|
||||||
);
|
}
|
||||||
|
|
||||||
|
this.verificationToken = result.data.verificationToken;
|
||||||
|
} catch (error) {
|
||||||
|
this.errorMessage = "Failed to start verification";
|
||||||
|
toast.error("Failed to start verification", {
|
||||||
|
description:
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: "Failed to start verification",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
this.startingVerification = false;
|
this.startingVerification = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async verifyCode() {
|
async verifyCode() {
|
||||||
if (!this.verificationToken) {
|
if (!this.verificationToken) {
|
||||||
@@ -106,69 +77,22 @@ class TwoFactorVerifyViewModel {
|
|||||||
this.verifying = true;
|
this.verifying = true;
|
||||||
this.errorMessage = null;
|
this.errorMessage = null;
|
||||||
|
|
||||||
const result = await ResultAsync.fromPromise(
|
try {
|
||||||
get(apiClient).twofactor["verify-session-code"].$post({
|
const result = await verifySessionCodeSC({
|
||||||
json: {
|
|
||||||
verificationToken: this.verificationToken,
|
verificationToken: this.verificationToken,
|
||||||
code: this.verificationCode,
|
code: this.verificationCode,
|
||||||
},
|
|
||||||
}),
|
|
||||||
(error): Err => ({
|
|
||||||
code: "NETWORK_ERROR",
|
|
||||||
message: "Verification failed",
|
|
||||||
description: "Network request failed",
|
|
||||||
detail: error instanceof Error ? error.message : String(error),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.andThen((response) => {
|
|
||||||
if (!response.ok) {
|
|
||||||
return errAsync({
|
|
||||||
code: "API_ERROR",
|
|
||||||
message: "Verification failed",
|
|
||||||
description: `Response failed with status ${response.status}`,
|
|
||||||
detail: `HTTP ${response.status}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return ResultAsync.fromPromise(
|
|
||||||
response.json(),
|
|
||||||
(error): Err => ({
|
|
||||||
code: "PARSING_ERROR",
|
|
||||||
message: "Failed to parse response",
|
|
||||||
description: "Invalid response format",
|
|
||||||
detail: error instanceof Error ? error.message : String(error),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.andThen((apiResult) => {
|
|
||||||
if (apiResult.error) {
|
|
||||||
return errAsync(apiResult.error);
|
|
||||||
}
|
|
||||||
if (!apiResult.data?.success) {
|
|
||||||
return errAsync({
|
|
||||||
code: "VALIDATION_ERROR",
|
|
||||||
message: "Invalid verification code",
|
|
||||||
description: "Verification failed",
|
|
||||||
detail: "Code verification unsuccessful",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return okAsync(apiResult.data);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
result.match(
|
if (result?.error || !result?.data?.success) {
|
||||||
() => {
|
this.errorMessage =
|
||||||
const redirectUrl = page.url.searchParams.get("redirect") || "/";
|
result?.error?.message || "Failed to verify code";
|
||||||
window.location.href = redirectUrl;
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
this.errorMessage = error.message || "Failed to verify code";
|
|
||||||
|
|
||||||
if (error.code === "BANNED") {
|
if (result?.error?.code === "BANNED") {
|
||||||
authClient.signOut();
|
await authClient.signOut();
|
||||||
window.location.href = "/auth/login";
|
window.location.href = "/auth/login";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't show toast for invalid codes, just show in UI
|
|
||||||
if (
|
if (
|
||||||
this.errorMessage &&
|
this.errorMessage &&
|
||||||
!this.errorMessage.includes("Invalid") &&
|
!this.errorMessage.includes("Invalid") &&
|
||||||
@@ -178,11 +102,21 @@ class TwoFactorVerifyViewModel {
|
|||||||
description: this.errorMessage,
|
description: this.errorMessage,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
return;
|
||||||
);
|
}
|
||||||
|
|
||||||
|
const redirectUrl = page.url.searchParams.get("redirect") || "/";
|
||||||
|
window.location.href = redirectUrl;
|
||||||
|
} catch (error) {
|
||||||
|
this.errorMessage = "Failed to verify code";
|
||||||
|
toast.error("Verification failed", {
|
||||||
|
description:
|
||||||
|
error instanceof Error ? error.message : this.errorMessage,
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
this.verifying = false;
|
this.verifying = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async handleBackupCode() {
|
async handleBackupCode() {
|
||||||
if (!this.verificationToken || !this.verificationCode) {
|
if (!this.verificationToken || !this.verificationCode) {
|
||||||
@@ -193,66 +127,26 @@ class TwoFactorVerifyViewModel {
|
|||||||
this.verifying = true;
|
this.verifying = true;
|
||||||
this.errorMessage = null;
|
this.errorMessage = null;
|
||||||
|
|
||||||
const result = await ResultAsync.fromPromise(
|
try {
|
||||||
get(apiClient).twofactor["verify-session-code"].$post({
|
const result = await verifySessionCodeSC({
|
||||||
json: {
|
|
||||||
verificationToken: this.verificationToken,
|
verificationToken: this.verificationToken,
|
||||||
code: this.verificationCode,
|
code: this.verificationCode,
|
||||||
},
|
|
||||||
}),
|
|
||||||
(error): Err => ({
|
|
||||||
code: "NETWORK_ERROR",
|
|
||||||
message: "Verification failed",
|
|
||||||
description: "Network request failed",
|
|
||||||
detail: error instanceof Error ? error.message : String(error),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.andThen((response) => {
|
|
||||||
if (!response.ok) {
|
|
||||||
return errAsync({
|
|
||||||
code: "API_ERROR",
|
|
||||||
message: "Verification failed",
|
|
||||||
description: `Response failed with status ${response.status}`,
|
|
||||||
detail: `HTTP ${response.status}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return ResultAsync.fromPromise(
|
|
||||||
response.json(),
|
|
||||||
(error): Err => ({
|
|
||||||
code: "PARSING_ERROR",
|
|
||||||
message: "Failed to parse response",
|
|
||||||
description: "Invalid response format",
|
|
||||||
detail: error instanceof Error ? error.message : String(error),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.andThen((apiResult) => {
|
|
||||||
if (apiResult.error) {
|
|
||||||
return errAsync(apiResult.error);
|
|
||||||
}
|
|
||||||
if (!apiResult.data?.success) {
|
|
||||||
return errAsync({
|
|
||||||
code: "VALIDATION_ERROR",
|
|
||||||
message: "Invalid backup code",
|
|
||||||
description: "Verification failed",
|
|
||||||
detail: "Backup code verification unsuccessful",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return okAsync(apiResult.data);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
result.match(
|
if (result?.error || !result?.data?.success) {
|
||||||
() => {
|
this.errorMessage = result?.error?.message || "Invalid backup code";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const redirectUrl = page.url.searchParams.get("redirect") || "/";
|
const redirectUrl = page.url.searchParams.get("redirect") || "/";
|
||||||
window.location.href = redirectUrl;
|
window.location.href = redirectUrl;
|
||||||
},
|
} catch (error) {
|
||||||
(error) => {
|
this.errorMessage =
|
||||||
this.errorMessage = error.message || "Invalid backup code";
|
error instanceof Error ? error.message : "Invalid backup code";
|
||||||
},
|
} finally {
|
||||||
);
|
|
||||||
|
|
||||||
this.verifying = false;
|
this.verifying = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
this.verifying = false;
|
this.verifying = false;
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { apiClient } from "$lib/global.stores";
|
import {
|
||||||
|
disableTwoFactorSC,
|
||||||
|
generateBackupCodesSQ,
|
||||||
|
setupTwoFactorSC,
|
||||||
|
verifyAndEnableTwoFactorSC,
|
||||||
|
} from "$lib/domains/security/twofa.remote";
|
||||||
import { toast } from "svelte-sonner";
|
import { toast } from "svelte-sonner";
|
||||||
import { get } from "svelte/store";
|
|
||||||
import QRCode from "qrcode";
|
import QRCode from "qrcode";
|
||||||
import { ResultAsync, errAsync, okAsync } from "neverthrow";
|
|
||||||
import type { Err } from "@pkg/result";
|
|
||||||
|
|
||||||
class TwoFactorViewModel {
|
class TwoFactorViewModel {
|
||||||
twoFactorEnabled = $state(false);
|
twoFactorEnabled = $state(false);
|
||||||
@@ -20,87 +22,39 @@ class TwoFactorViewModel {
|
|||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
this.errorMessage = null;
|
this.errorMessage = null;
|
||||||
|
|
||||||
const result = await ResultAsync.fromPromise(
|
try {
|
||||||
get(apiClient).twofactor.setup.$post({
|
const result = await setupTwoFactorSC({});
|
||||||
json: { code: "" },
|
if (result?.error || !result?.data?.totpURI) {
|
||||||
}),
|
this.errorMessage =
|
||||||
(error): Err => ({
|
result?.error?.message || "Could not enable 2FA";
|
||||||
code: "NETWORK_ERROR",
|
toast.error(this.errorMessage, {
|
||||||
message: "Failed to set up two-factor authentication.",
|
description:
|
||||||
description: "Network request failed",
|
result?.error?.description || "Please try again later",
|
||||||
detail: error instanceof Error ? error.message : String(error),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.andThen((response) => {
|
|
||||||
if (!response.ok) {
|
|
||||||
return errAsync({
|
|
||||||
code: "API_ERROR",
|
|
||||||
message: "Failed to set up two-factor authentication.",
|
|
||||||
description: `Response failed with status ${response.status}`,
|
|
||||||
detail: `HTTP ${response.status}`,
|
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return ResultAsync.fromPromise(
|
|
||||||
response.json(),
|
const qrCodeDataUrl = await QRCode.toDataURL(result.data.totpURI, {
|
||||||
(error): Err => ({
|
|
||||||
code: "PARSING_ERROR",
|
|
||||||
message: "Failed to parse response",
|
|
||||||
description: "Invalid response format",
|
|
||||||
detail: error instanceof Error ? error.message : String(error),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.andThen((apiResult) => {
|
|
||||||
if (apiResult.error) {
|
|
||||||
return errAsync(apiResult.error);
|
|
||||||
}
|
|
||||||
if (!apiResult.data || !apiResult.data.totpURI) {
|
|
||||||
return errAsync({
|
|
||||||
code: "API_ERROR",
|
|
||||||
message: "Failed to get 2FA setup information",
|
|
||||||
description: "Invalid response data",
|
|
||||||
detail: "Missing totpURI in response",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return okAsync(apiResult.data);
|
|
||||||
})
|
|
||||||
.andThen((data) => {
|
|
||||||
return ResultAsync.fromPromise(
|
|
||||||
QRCode.toDataURL(data.totpURI, {
|
|
||||||
width: 256,
|
width: 256,
|
||||||
margin: 2,
|
margin: 2,
|
||||||
color: { dark: "#000000", light: "#FFFFFF" },
|
color: { dark: "#000000", light: "#FFFFFF" },
|
||||||
}),
|
|
||||||
(error): Err => ({
|
|
||||||
code: "PROCESSING_ERROR",
|
|
||||||
message: "Failed to generate QR code.",
|
|
||||||
description: "QR code generation failed",
|
|
||||||
detail: error instanceof Error ? error.message : String(error),
|
|
||||||
}),
|
|
||||||
).map((qrCodeDataUrl) => ({
|
|
||||||
...data,
|
|
||||||
qrCodeDataUrl,
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
result.match(
|
|
||||||
({ qrCodeDataUrl, secret, totpURI }) => {
|
|
||||||
this.qrCodeUrl = qrCodeDataUrl;
|
this.qrCodeUrl = qrCodeDataUrl;
|
||||||
this.twoFactorSetupInProgress = true;
|
this.twoFactorSetupInProgress = true;
|
||||||
this.twoFactorSecret = secret;
|
this.twoFactorSecret = result.data.secret;
|
||||||
this.twoFactorVerificationCode = "";
|
this.twoFactorVerificationCode = "";
|
||||||
toast("Setup enabled");
|
toast("Setup enabled");
|
||||||
},
|
} catch (error) {
|
||||||
(error) => {
|
this.errorMessage = "Could not enable 2FA";
|
||||||
this.errorMessage = error.message || "Could not enable 2FA";
|
toast.error(this.errorMessage, {
|
||||||
toast.error(this.errorMessage || "Could not enable 2FA", {
|
description:
|
||||||
description: error.description || "Please try again later",
|
error instanceof Error ? error.message : "Please try again later",
|
||||||
});
|
});
|
||||||
},
|
} finally {
|
||||||
);
|
|
||||||
|
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async completeTwoFactorSetup() {
|
async completeTwoFactorSetup() {
|
||||||
if (!this.twoFactorVerificationCode) {
|
if (!this.twoFactorVerificationCode) {
|
||||||
@@ -112,94 +66,30 @@ class TwoFactorViewModel {
|
|||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
this.errorMessage = null;
|
this.errorMessage = null;
|
||||||
|
|
||||||
const verifyResult = await ResultAsync.fromPromise(
|
try {
|
||||||
get(apiClient).twofactor["verify-and-enable"].$post({
|
const verifyResult = await verifyAndEnableTwoFactorSC({
|
||||||
json: { code: this.twoFactorVerificationCode },
|
code: this.twoFactorVerificationCode,
|
||||||
}),
|
|
||||||
(error): Err => ({
|
|
||||||
code: "NETWORK_ERROR",
|
|
||||||
message: "Failed to verify code.",
|
|
||||||
description: "Network request failed",
|
|
||||||
detail: error instanceof Error ? error.message : String(error),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.andThen((response) => {
|
|
||||||
if (!response.ok) {
|
|
||||||
return errAsync({
|
|
||||||
code: "API_ERROR",
|
|
||||||
message: "Failed to verify code.",
|
|
||||||
description: `Response failed with status ${response.status}`,
|
|
||||||
detail: `HTTP ${response.status}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return ResultAsync.fromPromise(
|
|
||||||
response.json(),
|
|
||||||
(error): Err => ({
|
|
||||||
code: "PARSING_ERROR",
|
|
||||||
message: "Failed to parse response",
|
|
||||||
description: "Invalid response format",
|
|
||||||
detail: error instanceof Error ? error.message : String(error),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.andThen((apiResult) => {
|
|
||||||
if (apiResult.error) {
|
|
||||||
return errAsync(apiResult.error);
|
|
||||||
}
|
|
||||||
return okAsync(apiResult.data);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
verifyResult.match(
|
if (verifyResult?.error) {
|
||||||
async () => {
|
this.errorMessage =
|
||||||
const backupCodesResult = await ResultAsync.fromPromise(
|
verifyResult.error.message || "Invalid verification code";
|
||||||
get(apiClient).twofactor["generate-backup-codes"].$get(),
|
toast.error(this.errorMessage, {
|
||||||
(error): Err => ({
|
description:
|
||||||
code: "NETWORK_ERROR",
|
verifyResult.error.description || "Please try again",
|
||||||
message: "Failed to generate backup codes",
|
|
||||||
description: "Network request failed",
|
|
||||||
detail: error instanceof Error ? error.message : String(error),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.andThen((response) => {
|
|
||||||
if (!response.ok) {
|
|
||||||
return errAsync({
|
|
||||||
code: "API_ERROR",
|
|
||||||
message: "Failed to generate backup codes",
|
|
||||||
description: `Response failed with status ${response.status}`,
|
|
||||||
detail: `HTTP ${response.status}`,
|
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return ResultAsync.fromPromise(
|
|
||||||
response.json(),
|
|
||||||
(error): Err => ({
|
|
||||||
code: "PARSING_ERROR",
|
|
||||||
message: "Failed to parse response",
|
|
||||||
description: "Invalid response format",
|
|
||||||
detail: error instanceof Error ? error.message : String(error),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.andThen((apiResult) => {
|
|
||||||
if (apiResult.error) {
|
|
||||||
return errAsync(apiResult.error);
|
|
||||||
}
|
|
||||||
return okAsync(apiResult.data || []);
|
|
||||||
});
|
|
||||||
|
|
||||||
backupCodesResult.match(
|
const backupCodesResult = await generateBackupCodesSQ();
|
||||||
(backupCodes) => {
|
if (backupCodesResult?.error || !Array.isArray(backupCodesResult?.data)) {
|
||||||
this.backupCodes = backupCodes;
|
toast.error("2FA enabled, but failed to generate backup codes", {
|
||||||
this.showingBackupCodes = true;
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
toast.error(
|
|
||||||
"2FA enabled, but failed to generate backup codes",
|
|
||||||
{
|
|
||||||
description: "You can generate them later in settings",
|
description: "You can generate them later in settings",
|
||||||
},
|
});
|
||||||
);
|
} else {
|
||||||
},
|
this.backupCodes = backupCodesResult.data;
|
||||||
);
|
this.showingBackupCodes = true;
|
||||||
|
}
|
||||||
|
|
||||||
this.twoFactorEnabled = true;
|
this.twoFactorEnabled = true;
|
||||||
this.twoFactorSetupInProgress = false;
|
this.twoFactorSetupInProgress = false;
|
||||||
@@ -207,139 +97,79 @@ class TwoFactorViewModel {
|
|||||||
toast.success("Two-factor authentication enabled", {
|
toast.success("Two-factor authentication enabled", {
|
||||||
description: "Your account is now more secure",
|
description: "Your account is now more secure",
|
||||||
});
|
});
|
||||||
},
|
} catch (error) {
|
||||||
(error) => {
|
this.errorMessage = "Invalid verification code";
|
||||||
this.errorMessage = error.message || "Invalid verification code";
|
toast.error(this.errorMessage, {
|
||||||
toast.error(this.errorMessage || "Invalid verification code", {
|
description:
|
||||||
description: error.description || "Please try again",
|
error instanceof Error ? error.message : "Please try again",
|
||||||
});
|
});
|
||||||
},
|
} finally {
|
||||||
);
|
|
||||||
|
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async disableTwoFactor() {
|
async disableTwoFactor() {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
this.errorMessage = null;
|
this.errorMessage = null;
|
||||||
|
|
||||||
const result = await ResultAsync.fromPromise(
|
try {
|
||||||
get(apiClient).twofactor.disable.$delete({
|
const result = await disableTwoFactorSC({ code: "" });
|
||||||
json: { code: "" },
|
if (result?.error) {
|
||||||
}),
|
this.errorMessage = result.error.message || "Failed to disable 2FA";
|
||||||
(error): Err => ({
|
toast.error(this.errorMessage, {
|
||||||
code: "NETWORK_ERROR",
|
description: result.error.description || "Please try again later",
|
||||||
message: "Failed to disable two-factor authentication.",
|
|
||||||
description: "Network request failed",
|
|
||||||
detail: error instanceof Error ? error.message : String(error),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.andThen((response) => {
|
|
||||||
if (!response.ok) {
|
|
||||||
return errAsync({
|
|
||||||
code: "API_ERROR",
|
|
||||||
message: "Failed to disable two-factor authentication.",
|
|
||||||
description: `Response failed with status ${response.status}`,
|
|
||||||
detail: `HTTP ${response.status}`,
|
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return ResultAsync.fromPromise(
|
|
||||||
response.json(),
|
|
||||||
(error): Err => ({
|
|
||||||
code: "PARSING_ERROR",
|
|
||||||
message: "Failed to parse response",
|
|
||||||
description: "Invalid response format",
|
|
||||||
detail: error instanceof Error ? error.message : String(error),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.andThen((apiResult) => {
|
|
||||||
if (apiResult.error) {
|
|
||||||
return errAsync(apiResult.error);
|
|
||||||
}
|
|
||||||
return okAsync(apiResult.data);
|
|
||||||
});
|
|
||||||
|
|
||||||
result.match(
|
|
||||||
() => {
|
|
||||||
this.twoFactorEnabled = false;
|
this.twoFactorEnabled = false;
|
||||||
this.backupCodes = [];
|
this.backupCodes = [];
|
||||||
this.qrCodeUrl = null;
|
this.qrCodeUrl = null;
|
||||||
this.showingBackupCodes = false;
|
this.showingBackupCodes = false;
|
||||||
this.twoFactorSecret = null;
|
this.twoFactorSecret = null;
|
||||||
toast.success("Two-factor authentication disabled");
|
toast.success("Two-factor authentication disabled");
|
||||||
},
|
} catch (error) {
|
||||||
(error) => {
|
this.errorMessage = "Failed to disable 2FA";
|
||||||
this.errorMessage = error.message || "Failed to disable 2FA";
|
toast.error(this.errorMessage, {
|
||||||
toast.error(this.errorMessage || "Failed to disable 2FA", {
|
|
||||||
description:
|
description:
|
||||||
error.description || "Please try again later",
|
error instanceof Error ? error.message : "Please try again later",
|
||||||
});
|
});
|
||||||
},
|
} finally {
|
||||||
);
|
|
||||||
|
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async generateNewBackupCodes() {
|
async generateNewBackupCodes() {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
this.errorMessage = null;
|
this.errorMessage = null;
|
||||||
|
|
||||||
const result = await ResultAsync.fromPromise(
|
try {
|
||||||
get(apiClient).twofactor["generate-backup-codes"].$get(),
|
const result = await generateBackupCodesSQ();
|
||||||
(error): Err => ({
|
if (result?.error || !Array.isArray(result?.data)) {
|
||||||
code: "NETWORK_ERROR",
|
this.errorMessage =
|
||||||
message: "Failed to generate new backup codes.",
|
result?.error?.message || "Failed to generate new backup codes";
|
||||||
description: "Network request failed",
|
toast.error(this.errorMessage, {
|
||||||
detail: error instanceof Error ? error.message : String(error),
|
description:
|
||||||
}),
|
result?.error?.description || "Please try again later",
|
||||||
)
|
|
||||||
.andThen((response) => {
|
|
||||||
if (!response.ok) {
|
|
||||||
return errAsync({
|
|
||||||
code: "API_ERROR",
|
|
||||||
message: "Failed to generate new backup codes.",
|
|
||||||
description: `Response failed with status ${response.status}`,
|
|
||||||
detail: `HTTP ${response.status}`,
|
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return ResultAsync.fromPromise(
|
|
||||||
response.json(),
|
|
||||||
(error): Err => ({
|
|
||||||
code: "PARSING_ERROR",
|
|
||||||
message: "Failed to parse response",
|
|
||||||
description: "Invalid response format",
|
|
||||||
detail: error instanceof Error ? error.message : String(error),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.andThen((apiResult) => {
|
|
||||||
if (apiResult.error) {
|
|
||||||
return errAsync(apiResult.error);
|
|
||||||
}
|
|
||||||
return okAsync(apiResult.data || []);
|
|
||||||
});
|
|
||||||
|
|
||||||
result.match(
|
this.backupCodes = result.data;
|
||||||
(backupCodes) => {
|
|
||||||
this.backupCodes = backupCodes;
|
|
||||||
this.showingBackupCodes = true;
|
this.showingBackupCodes = true;
|
||||||
toast.success("New backup codes generated", {
|
toast.success("New backup codes generated", {
|
||||||
description: "Your previous backup codes are now invalid",
|
description: "Your previous backup codes are now invalid",
|
||||||
});
|
});
|
||||||
},
|
} catch (error) {
|
||||||
(error) => {
|
this.errorMessage = "Failed to generate new backup codes";
|
||||||
this.errorMessage =
|
toast.error(this.errorMessage, {
|
||||||
error.message || "Failed to generate new backup codes";
|
|
||||||
toast.error(this.errorMessage || "Failed to generate new backup codes", {
|
|
||||||
description:
|
description:
|
||||||
error.description || "Please try again later",
|
error instanceof Error ? error.message : "Please try again later",
|
||||||
});
|
});
|
||||||
},
|
} finally {
|
||||||
);
|
|
||||||
|
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
copyAllBackupCodes() {
|
copyAllBackupCodes() {
|
||||||
const codesText = this.backupCodes.join("\n");
|
const codesText = this.backupCodes.join("\n");
|
||||||
|
|||||||
@@ -0,0 +1,205 @@
|
|||||||
|
import {
|
||||||
|
disable2FASchema,
|
||||||
|
enable2FACodeSchema,
|
||||||
|
startVerificationSchema,
|
||||||
|
verifyCodeSchema,
|
||||||
|
} from "@pkg/logic/domains/2fa/data";
|
||||||
|
import {
|
||||||
|
getFlowExecCtxForRemoteFuncs,
|
||||||
|
unauthorized,
|
||||||
|
} from "$lib/core/server.utils";
|
||||||
|
import { getTwofaController } from "@pkg/logic/domains/2fa/controller";
|
||||||
|
import { command, getRequestEvent, query } from "$app/server";
|
||||||
|
import { auth } from "@pkg/logic/domains/auth/config.base";
|
||||||
|
import type { User } from "@pkg/logic/domains/user/data";
|
||||||
|
import * as v from "valibot";
|
||||||
|
|
||||||
|
const tc = getTwofaController();
|
||||||
|
|
||||||
|
function buildIpAddress(headers: Headers) {
|
||||||
|
return (
|
||||||
|
headers.get("x-forwarded-for") ?? headers.get("x-real-ip") ?? "unknown"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildUserAgent(headers: Headers) {
|
||||||
|
return headers.get("user-agent") ?? "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setupTwoFactorSC = command(v.object({}), async () => {
|
||||||
|
const event = getRequestEvent();
|
||||||
|
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||||
|
const currentUser = event.locals.user;
|
||||||
|
|
||||||
|
if (!fctx.userId || !currentUser) {
|
||||||
|
return unauthorized(fctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await tc.setup2FA(fctx, currentUser as User);
|
||||||
|
return res.isOk()
|
||||||
|
? { data: res.value, error: null }
|
||||||
|
: { data: null, error: res.error };
|
||||||
|
});
|
||||||
|
|
||||||
|
export const verifyAndEnableTwoFactorSC = command(
|
||||||
|
enable2FACodeSchema,
|
||||||
|
async (payload) => {
|
||||||
|
const event = getRequestEvent();
|
||||||
|
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||||
|
const currentUser = event.locals.user;
|
||||||
|
|
||||||
|
if (!fctx.userId || !currentUser) {
|
||||||
|
return unauthorized(fctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await tc.verifyAndEnable2FA(
|
||||||
|
fctx,
|
||||||
|
currentUser as User,
|
||||||
|
payload.code,
|
||||||
|
event.request.headers,
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.isOk()
|
||||||
|
? { data: res.value, error: null }
|
||||||
|
: { data: null, error: res.error };
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const generateBackupCodesSQ = query(async () => {
|
||||||
|
const event = getRequestEvent();
|
||||||
|
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||||
|
const currentUser = event.locals.user;
|
||||||
|
|
||||||
|
if (!fctx.userId || !currentUser) {
|
||||||
|
return unauthorized(fctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await tc.generateBackupCodes(fctx, currentUser as User);
|
||||||
|
return res.isOk()
|
||||||
|
? { data: res.value, error: null }
|
||||||
|
: { data: null, error: res.error };
|
||||||
|
});
|
||||||
|
|
||||||
|
export const disableTwoFactorSC = command(disable2FASchema, async (payload) => {
|
||||||
|
const event = getRequestEvent();
|
||||||
|
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||||
|
const currentUser = event.locals.user;
|
||||||
|
|
||||||
|
if (!fctx.userId || !currentUser) {
|
||||||
|
return unauthorized(fctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await tc.disable(fctx, currentUser as User, payload.code);
|
||||||
|
return res.isOk()
|
||||||
|
? { data: res.value, error: null }
|
||||||
|
: { data: null, error: res.error };
|
||||||
|
});
|
||||||
|
|
||||||
|
export const requiresVerificationSQ = query(
|
||||||
|
v.object({ sessionId: v.string() }),
|
||||||
|
async (input) => {
|
||||||
|
const event = getRequestEvent();
|
||||||
|
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||||
|
const currentUser = event.locals.user;
|
||||||
|
|
||||||
|
if (!fctx.userId || !currentUser) {
|
||||||
|
return unauthorized(fctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await tc.requiresInitialVerification(
|
||||||
|
fctx,
|
||||||
|
currentUser as User,
|
||||||
|
input.sessionId,
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.isOk()
|
||||||
|
? { data: res.value, error: null }
|
||||||
|
: { data: null, error: res.error };
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const requiresSensitiveActionSQ = query(async () => {
|
||||||
|
const event = getRequestEvent();
|
||||||
|
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||||
|
const currentUser = event.locals.user;
|
||||||
|
|
||||||
|
if (!fctx.userId || !currentUser) {
|
||||||
|
return unauthorized(fctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await tc.requiresSensitiveActionVerification(
|
||||||
|
fctx,
|
||||||
|
currentUser as User,
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.isOk()
|
||||||
|
? { data: res.value, error: null }
|
||||||
|
: { data: null, error: res.error };
|
||||||
|
});
|
||||||
|
|
||||||
|
export const startVerificationSessionSC = command(
|
||||||
|
startVerificationSchema,
|
||||||
|
async (payload) => {
|
||||||
|
const event = getRequestEvent();
|
||||||
|
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||||
|
|
||||||
|
if (!fctx.userId) {
|
||||||
|
return unauthorized(fctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await tc.startVerification(fctx, {
|
||||||
|
userId: payload.userId,
|
||||||
|
sessionId: payload.sessionId,
|
||||||
|
ipAddress: buildIpAddress(event.request.headers),
|
||||||
|
userAgent: buildUserAgent(event.request.headers),
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.isOk()
|
||||||
|
? { data: res.value, error: null }
|
||||||
|
: { data: null, error: res.error };
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const verifySessionCodeSC = command(
|
||||||
|
verifyCodeSchema,
|
||||||
|
async (payload) => {
|
||||||
|
const event = getRequestEvent();
|
||||||
|
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||||
|
|
||||||
|
let currentUser = event.locals.user;
|
||||||
|
|
||||||
|
if (!currentUser) {
|
||||||
|
const sess = await auth.api.getSession({
|
||||||
|
headers: event.request.headers,
|
||||||
|
});
|
||||||
|
currentUser = sess?.user as User | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await tc.verifyCode(
|
||||||
|
fctx,
|
||||||
|
{
|
||||||
|
verificationSessToken: payload.verificationToken,
|
||||||
|
code: payload.code,
|
||||||
|
},
|
||||||
|
currentUser as User | undefined,
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.isOk()
|
||||||
|
? { data: res.value, error: null }
|
||||||
|
: { data: null, error: res.error };
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const cleanupExpiredSessionsSC = command(v.object({}), async () => {
|
||||||
|
const event = getRequestEvent();
|
||||||
|
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||||
|
|
||||||
|
if (!fctx.userId) {
|
||||||
|
return unauthorized(fctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await tc.cleanupExpiredSessions(fctx);
|
||||||
|
return res.isOk()
|
||||||
|
? { data: res.value, error: null }
|
||||||
|
: { data: null, error: res.error };
|
||||||
|
});
|
||||||
|
|||||||
@@ -2,13 +2,7 @@
|
|||||||
import { goto } from "$app/navigation";
|
import { goto } from "$app/navigation";
|
||||||
import { mainNavTree } from "$lib/core/constants";
|
import { mainNavTree } from "$lib/core/constants";
|
||||||
import { breadcrumbs } from "$lib/global.stores";
|
import { breadcrumbs } from "$lib/global.stores";
|
||||||
import { makeClient } from "$lib/make-client.js";
|
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import type { PageData } from "./$types";
|
|
||||||
|
|
||||||
let { data }: { data: PageData } = $props();
|
|
||||||
|
|
||||||
const client = makeClient(fetch);
|
|
||||||
|
|
||||||
breadcrumbs.set([mainNavTree[0]]);
|
breadcrumbs.set([mainNavTree[0]]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { browser } from "$app/environment";
|
|
||||||
import { Toaster } from "$lib/components/ui/sonner/index.js";
|
import { Toaster } from "$lib/components/ui/sonner/index.js";
|
||||||
import { QueryClient, QueryClientProvider } from "@tanstack/svelte-query";
|
|
||||||
import { ModeWatcher } from "mode-watcher";
|
import { ModeWatcher } from "mode-watcher";
|
||||||
|
|
||||||
import { apiClient, breadcrumbs } from "$lib/global.stores";
|
import { breadcrumbs } from "$lib/global.stores";
|
||||||
import { makeClient } from "$lib/make-client";
|
|
||||||
import { onMount } from "svelte";
|
|
||||||
import "./layout.css";
|
import "./layout.css";
|
||||||
|
|
||||||
let { children }: { children: any } = $props();
|
let { children }: { children: any } = $props();
|
||||||
|
|
||||||
const queryClient = new QueryClient({
|
|
||||||
defaultOptions: {
|
|
||||||
queries: { enabled: browser },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
apiClient.set(makeClient(fetch));
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@@ -31,6 +17,4 @@
|
|||||||
<ModeWatcher />
|
<ModeWatcher />
|
||||||
<Toaster />
|
<Toaster />
|
||||||
|
|
||||||
<QueryClientProvider client={queryClient}>
|
{@render children()}
|
||||||
{@render children()}
|
|
||||||
</QueryClientProvider>
|
|
||||||
|
|||||||
@@ -1,170 +0,0 @@
|
|||||||
import {
|
|
||||||
disable2FASchema,
|
|
||||||
enable2FACodeSchema,
|
|
||||||
startVerificationSchema,
|
|
||||||
verifyCodeSchema,
|
|
||||||
} from "./data";
|
|
||||||
import { sValidator } from "@hono/standard-validator";
|
|
||||||
import { HonoContext } from "@/core/hono.helpers";
|
|
||||||
import { getTwofaController } from "./controller";
|
|
||||||
import { auth } from "@domains/auth/config.base";
|
|
||||||
import { Hono } from "hono";
|
|
||||||
|
|
||||||
const twofaController = getTwofaController();
|
|
||||||
|
|
||||||
export const twofaRouter = new Hono<HonoContext>()
|
|
||||||
.post("/setup", async (c) => {
|
|
||||||
const res = await twofaController.setup2FA(
|
|
||||||
c.env.locals.fCtx,
|
|
||||||
c.env.locals.user,
|
|
||||||
);
|
|
||||||
return c.json(
|
|
||||||
res.isOk()
|
|
||||||
? { data: res.value, error: null }
|
|
||||||
: { data: null, error: res.error },
|
|
||||||
res.isOk() ? 200 : 400,
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.post(
|
|
||||||
"/verify-and-enable",
|
|
||||||
sValidator("json", enable2FACodeSchema),
|
|
||||||
async (c) => {
|
|
||||||
const data = c.req.valid("json");
|
|
||||||
const res = await twofaController.verifyAndEnable2FA(
|
|
||||||
c.env.locals.fCtx,
|
|
||||||
c.env.locals.user,
|
|
||||||
data.code,
|
|
||||||
c.req.raw.headers,
|
|
||||||
);
|
|
||||||
return c.json(
|
|
||||||
res.isOk()
|
|
||||||
? { data: res.value, error: null }
|
|
||||||
: { data: null, error: res.error },
|
|
||||||
res.isOk() ? 200 : 400,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.get("/generate-backup-codes", async (c) => {
|
|
||||||
const res = await twofaController.generateBackupCodes(
|
|
||||||
c.env.locals.fCtx,
|
|
||||||
c.env.locals.user,
|
|
||||||
);
|
|
||||||
return c.json(
|
|
||||||
res.isOk()
|
|
||||||
? { data: res.value, error: null }
|
|
||||||
: { data: null, error: res.error },
|
|
||||||
res.isOk() ? 200 : 400,
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.delete("/disable", sValidator("json", disable2FASchema), async (c) => {
|
|
||||||
const data = c.req.valid("json");
|
|
||||||
const res = await twofaController.disable(
|
|
||||||
c.env.locals.fCtx,
|
|
||||||
c.env.locals.user,
|
|
||||||
data.code,
|
|
||||||
);
|
|
||||||
return c.json(
|
|
||||||
res.isOk()
|
|
||||||
? { data: res.value, error: null }
|
|
||||||
: { data: null, error: res.error },
|
|
||||||
res.isOk() ? 200 : 400,
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.get("/requires-verification", async (c) => {
|
|
||||||
const user = c.env.locals.user;
|
|
||||||
const sessionId = c.req.query("sessionId")?.toString() ?? "";
|
|
||||||
const res = await twofaController.requiresInitialVerification(
|
|
||||||
c.env.locals.fCtx,
|
|
||||||
user,
|
|
||||||
sessionId,
|
|
||||||
);
|
|
||||||
return c.json(
|
|
||||||
res.isOk()
|
|
||||||
? { data: res.value, error: null }
|
|
||||||
: { data: null, error: res.error },
|
|
||||||
res.isOk() ? 200 : 400,
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.get("/requires-sensitive-action", async (c) => {
|
|
||||||
const res = await twofaController.requiresSensitiveActionVerification(
|
|
||||||
c.env.locals.fCtx,
|
|
||||||
c.env.locals.user,
|
|
||||||
);
|
|
||||||
return c.json(
|
|
||||||
res.isOk()
|
|
||||||
? { data: res.value, error: null }
|
|
||||||
: { data: null, error: res.error },
|
|
||||||
res.isOk() ? 200 : 400,
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.post(
|
|
||||||
"/start-verification-session",
|
|
||||||
sValidator("json", startVerificationSchema),
|
|
||||||
async (c) => {
|
|
||||||
const data = c.req.valid("json");
|
|
||||||
|
|
||||||
const ipAddress =
|
|
||||||
c.req.header("x-forwarded-for") ||
|
|
||||||
c.req.header("x-real-ip") ||
|
|
||||||
"unknown";
|
|
||||||
const userAgent = c.req.header("user-agent") || "unknown";
|
|
||||||
|
|
||||||
const res = await twofaController.startVerification(
|
|
||||||
c.env.locals.fCtx,
|
|
||||||
{
|
|
||||||
userId: data.userId,
|
|
||||||
sessionId: data.sessionId,
|
|
||||||
ipAddress,
|
|
||||||
userAgent,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return c.json(
|
|
||||||
res.isOk()
|
|
||||||
? { data: res.value, error: null }
|
|
||||||
: { data: null, error: res.error },
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.post(
|
|
||||||
"/verify-session-code",
|
|
||||||
sValidator("json", verifyCodeSchema),
|
|
||||||
async (c) => {
|
|
||||||
const data = c.req.valid("json");
|
|
||||||
|
|
||||||
let user = c.env.locals.user;
|
|
||||||
if (!user) {
|
|
||||||
const out = await auth.api.getSession({
|
|
||||||
headers: c.req.raw.headers,
|
|
||||||
});
|
|
||||||
user = out?.user as any;
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await twofaController.verifyCode(
|
|
||||||
c.env.locals.fCtx,
|
|
||||||
{
|
|
||||||
verificationSessToken: data.verificationToken,
|
|
||||||
code: data.code,
|
|
||||||
},
|
|
||||||
user,
|
|
||||||
);
|
|
||||||
|
|
||||||
return c.json(
|
|
||||||
res.isOk()
|
|
||||||
? { data: res.value, error: null }
|
|
||||||
: { data: null, error: res.error },
|
|
||||||
res.isOk() ? 200 : 400,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.post("/cleanup-expired-sessions", async (c) => {
|
|
||||||
const res = await twofaController.cleanupExpiredSessions(
|
|
||||||
c.env.locals.fCtx,
|
|
||||||
);
|
|
||||||
return c.json(
|
|
||||||
res.isOk()
|
|
||||||
? { data: res.value, error: null }
|
|
||||||
: { data: null, error: res.error },
|
|
||||||
res.isOk() ? 200 : 400,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
import {
|
|
||||||
banUserSchema,
|
|
||||||
checkUsernameSchema,
|
|
||||||
ensureAccountExistsSchema,
|
|
||||||
rotatePasswordSchema,
|
|
||||||
} from "./data";
|
|
||||||
import { HonoContext } from "@core/hono.helpers";
|
|
||||||
import { sValidator } from "@hono/standard-validator";
|
|
||||||
import { getUserController } from "./controller";
|
|
||||||
import { Hono } from "hono";
|
|
||||||
|
|
||||||
const uc = getUserController();
|
|
||||||
|
|
||||||
export const usersRouter = new Hono<HonoContext>()
|
|
||||||
// Get current user info
|
|
||||||
.get("/me", async (c) => {
|
|
||||||
const fctx = c.env.locals.fCtx;
|
|
||||||
const userId = c.env.locals.user?.id;
|
|
||||||
|
|
||||||
if (!userId) {
|
|
||||||
return c.json(
|
|
||||||
{
|
|
||||||
error: {
|
|
||||||
code: "UNAUTHORIZED",
|
|
||||||
message: "User not authenticated",
|
|
||||||
description: "Please log in",
|
|
||||||
detail: "No user ID found in session",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
401,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await uc.getUserInfo(fctx, userId);
|
|
||||||
return c.json(
|
|
||||||
res.isOk()
|
|
||||||
? { data: res.value, error: null }
|
|
||||||
: { data: null, error: res.error },
|
|
||||||
res.isOk() ? 200 : 400,
|
|
||||||
);
|
|
||||||
})
|
|
||||||
|
|
||||||
// Get user info by ID
|
|
||||||
.get("/:userId", async (c) => {
|
|
||||||
const fctx = c.env.locals.fCtx;
|
|
||||||
const userId = c.req.param("userId");
|
|
||||||
|
|
||||||
const res = await uc.getUserInfo(fctx, userId);
|
|
||||||
return c.json(
|
|
||||||
res.isOk()
|
|
||||||
? { data: res.value, error: null }
|
|
||||||
: { data: null, error: res.error },
|
|
||||||
res.isOk() ? 200 : 400,
|
|
||||||
);
|
|
||||||
})
|
|
||||||
|
|
||||||
// Ensure account exists
|
|
||||||
.put(
|
|
||||||
"/ensure-account-exists",
|
|
||||||
sValidator("json", ensureAccountExistsSchema),
|
|
||||||
async (c) => {
|
|
||||||
const fctx = c.env.locals.fCtx;
|
|
||||||
const data = c.req.valid("json");
|
|
||||||
|
|
||||||
const res = await uc.ensureAccountExists(fctx, data.userId);
|
|
||||||
return c.json(
|
|
||||||
res.isOk()
|
|
||||||
? { data: res.value, error: null }
|
|
||||||
: { data: null, error: res.error },
|
|
||||||
res.isOk() ? 200 : 400,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
// Check username availability
|
|
||||||
.post(
|
|
||||||
"/check-username",
|
|
||||||
sValidator("json", checkUsernameSchema),
|
|
||||||
async (c) => {
|
|
||||||
const fctx = c.env.locals.fCtx;
|
|
||||||
const data = c.req.valid("json");
|
|
||||||
|
|
||||||
const res = await uc.isUsernameAvailable(fctx, data.username);
|
|
||||||
return c.json(
|
|
||||||
res.isOk()
|
|
||||||
? { data: res.value, error: null }
|
|
||||||
: { data: null, error: res.error },
|
|
||||||
res.isOk() ? 200 : 400,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
// Update last 2FA verification time
|
|
||||||
.put("/update-2fa-verified/:userId", async (c) => {
|
|
||||||
const fctx = c.env.locals.fCtx;
|
|
||||||
const userId = c.req.param("userId");
|
|
||||||
|
|
||||||
const res = await uc.updateLastVerified2FaAtToNow(fctx, userId);
|
|
||||||
return c.json(
|
|
||||||
res.isOk()
|
|
||||||
? { data: res.value, error: null }
|
|
||||||
: { data: null, error: res.error },
|
|
||||||
res.isOk() ? 200 : 400,
|
|
||||||
);
|
|
||||||
})
|
|
||||||
|
|
||||||
// Ban user
|
|
||||||
.post("/ban", sValidator("json", banUserSchema), async (c) => {
|
|
||||||
const fctx = c.env.locals.fCtx;
|
|
||||||
const data = c.req.valid("json");
|
|
||||||
|
|
||||||
const res = await uc.banUser(fctx, data.userId, data.reason, data.banExpiresAt);
|
|
||||||
return c.json(
|
|
||||||
res.isOk()
|
|
||||||
? { data: res.value, error: null }
|
|
||||||
: { data: null, error: res.error },
|
|
||||||
res.isOk() ? 200 : 400,
|
|
||||||
);
|
|
||||||
})
|
|
||||||
|
|
||||||
// Check if user is banned
|
|
||||||
.get("/:userId/is-banned", async (c) => {
|
|
||||||
const fctx = c.env.locals.fCtx;
|
|
||||||
const userId = c.req.param("userId");
|
|
||||||
|
|
||||||
const res = await uc.isUserBanned(fctx, userId);
|
|
||||||
return c.json(
|
|
||||||
res.isOk()
|
|
||||||
? { data: res.value, error: null }
|
|
||||||
: { data: null, error: res.error },
|
|
||||||
res.isOk() ? 200 : 400,
|
|
||||||
);
|
|
||||||
})
|
|
||||||
|
|
||||||
// Get ban info
|
|
||||||
.get("/:userId/ban-info", async (c) => {
|
|
||||||
const fctx = c.env.locals.fCtx;
|
|
||||||
const userId = c.req.param("userId");
|
|
||||||
|
|
||||||
const res = await uc.getBanInfo(fctx, userId);
|
|
||||||
return c.json(
|
|
||||||
res.isOk()
|
|
||||||
? { data: res.value, error: null }
|
|
||||||
: { data: null, error: res.error },
|
|
||||||
res.isOk() ? 200 : 400,
|
|
||||||
);
|
|
||||||
})
|
|
||||||
|
|
||||||
// Rotate password
|
|
||||||
.put(
|
|
||||||
"/rotate-password",
|
|
||||||
sValidator("json", rotatePasswordSchema),
|
|
||||||
async (c) => {
|
|
||||||
const fctx = c.env.locals.fCtx;
|
|
||||||
const data = c.req.valid("json");
|
|
||||||
|
|
||||||
const res = await uc.rotatePassword(fctx, data.userId, data.password);
|
|
||||||
return c.json(
|
|
||||||
res.isOk()
|
|
||||||
? { data: res.value, error: null }
|
|
||||||
: { data: null, error: res.error },
|
|
||||||
res.isOk() ? 200 : 400,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
@@ -4,14 +4,13 @@
|
|||||||
"auth:schemagen": "pnpm dlx @better-auth/cli generate --config ./domains/auth/config.base.ts --output ../../packages/db/schema/better.auth.schema.ts"
|
"auth:schemagen": "pnpm dlx @better-auth/cli generate --config ./domains/auth/config.base.ts --output ../../packages/db/schema/better.auth.schema.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hono/standard-validator": "^0.2.1",
|
|
||||||
"@opentelemetry/api": "^1.9.0",
|
"@opentelemetry/api": "^1.9.0",
|
||||||
"@otplib/plugin-base32-scure": "^13.3.0",
|
"@otplib/plugin-base32-scure": "^13.3.0",
|
||||||
"@otplib/plugin-crypto-noble": "^13.3.0",
|
"@otplib/plugin-crypto-noble": "^13.3.0",
|
||||||
"@otplib/totp": "^13.3.0",
|
"@otplib/totp": "^13.3.0",
|
||||||
"@pkg/db": "workspace:*",
|
"@pkg/db": "workspace:*",
|
||||||
"@pkg/logger": "workspace:*",
|
|
||||||
"@pkg/keystore": "workspace:*",
|
"@pkg/keystore": "workspace:*",
|
||||||
|
"@pkg/logger": "workspace:*",
|
||||||
"@pkg/result": "workspace:*",
|
"@pkg/result": "workspace:*",
|
||||||
"@pkg/settings": "workspace:*",
|
"@pkg/settings": "workspace:*",
|
||||||
"@types/pdfkit": "^0.14.0",
|
"@types/pdfkit": "^0.14.0",
|
||||||
@@ -19,17 +18,13 @@
|
|||||||
"better-auth": "^1.4.7",
|
"better-auth": "^1.4.7",
|
||||||
"date-fns-tz": "^3.2.0",
|
"date-fns-tz": "^3.2.0",
|
||||||
"dotenv": "^16.5.0",
|
"dotenv": "^16.5.0",
|
||||||
"hono": "^4.11.1",
|
|
||||||
"imapflow": "^1.0.188",
|
"imapflow": "^1.0.188",
|
||||||
"mailparser": "^3.7.3",
|
"mailparser": "^3.7.3",
|
||||||
"nanoid": "^5.1.5",
|
"nanoid": "^5.1.5",
|
||||||
"neverthrow": "^8.2.0",
|
"neverthrow": "^8.2.0",
|
||||||
"otplib": "^13.3.0",
|
"otplib": "^13.3.0",
|
||||||
"pdfkit": "^0.17.1",
|
|
||||||
"tmp": "^0.2.3",
|
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^11.1.0",
|
||||||
"valibot": "^1.2.0",
|
"valibot": "^1.2.0"
|
||||||
"xlsx": "^0.18.5"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
|
|||||||
247
pnpm-lock.yaml
generated
247
pnpm-lock.yaml
generated
@@ -65,24 +65,15 @@ importers:
|
|||||||
'@pkg/settings':
|
'@pkg/settings':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../packages/settings
|
version: link:../../packages/settings
|
||||||
'@tanstack/svelte-query':
|
|
||||||
specifier: ^6.0.18
|
|
||||||
version: 6.0.18(svelte@5.53.6)
|
|
||||||
better-auth:
|
better-auth:
|
||||||
specifier: ^1.4.20
|
specifier: ^1.4.20
|
||||||
version: 1.4.20(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.6)(vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.6)(typescript@5.9.3)(vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.6)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2))
|
version: 1.4.20(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.6)(vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.6)(typescript@5.9.3)(vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.6)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2))
|
||||||
date-fns:
|
date-fns:
|
||||||
specifier: ^4.1.0
|
specifier: ^4.1.0
|
||||||
version: 4.1.0
|
version: 4.1.0
|
||||||
hono:
|
|
||||||
specifier: ^4.11.1
|
|
||||||
version: 4.12.3
|
|
||||||
import-in-the-middle:
|
import-in-the-middle:
|
||||||
specifier: ^3.0.0
|
specifier: ^3.0.0
|
||||||
version: 3.0.0
|
version: 3.0.0
|
||||||
marked:
|
|
||||||
specifier: ^17.0.1
|
|
||||||
version: 17.0.3
|
|
||||||
nanoid:
|
nanoid:
|
||||||
specifier: ^5.1.6
|
specifier: ^5.1.6
|
||||||
version: 5.1.6
|
version: 5.1.6
|
||||||
@@ -289,9 +280,6 @@ importers:
|
|||||||
|
|
||||||
packages/logic:
|
packages/logic:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@hono/standard-validator':
|
|
||||||
specifier: ^0.2.1
|
|
||||||
version: 0.2.2(@standard-schema/spec@1.1.0)(hono@4.12.3)
|
|
||||||
'@opentelemetry/api':
|
'@opentelemetry/api':
|
||||||
specifier: ^1.9.0
|
specifier: ^1.9.0
|
||||||
version: 1.9.0
|
version: 1.9.0
|
||||||
@@ -334,9 +322,6 @@ importers:
|
|||||||
dotenv:
|
dotenv:
|
||||||
specifier: ^16.5.0
|
specifier: ^16.5.0
|
||||||
version: 16.6.1
|
version: 16.6.1
|
||||||
hono:
|
|
||||||
specifier: ^4.11.1
|
|
||||||
version: 4.12.3
|
|
||||||
imapflow:
|
imapflow:
|
||||||
specifier: ^1.0.188
|
specifier: ^1.0.188
|
||||||
version: 1.2.10
|
version: 1.2.10
|
||||||
@@ -352,12 +337,6 @@ importers:
|
|||||||
otplib:
|
otplib:
|
||||||
specifier: ^13.3.0
|
specifier: ^13.3.0
|
||||||
version: 13.3.0
|
version: 13.3.0
|
||||||
pdfkit:
|
|
||||||
specifier: ^0.17.1
|
|
||||||
version: 0.17.2
|
|
||||||
tmp:
|
|
||||||
specifier: ^0.2.3
|
|
||||||
version: 0.2.5
|
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.9.3
|
specifier: ^5.9.3
|
||||||
version: 5.9.3
|
version: 5.9.3
|
||||||
@@ -367,9 +346,6 @@ importers:
|
|||||||
valibot:
|
valibot:
|
||||||
specifier: ^1.2.0
|
specifier: ^1.2.0
|
||||||
version: 1.2.0(typescript@5.9.3)
|
version: 1.2.0(typescript@5.9.3)
|
||||||
xlsx:
|
|
||||||
specifier: ^0.18.5
|
|
||||||
version: 0.18.5
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@types/bun':
|
'@types/bun':
|
||||||
specifier: latest
|
specifier: latest
|
||||||
@@ -930,12 +906,6 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
hono: ^4
|
hono: ^4
|
||||||
|
|
||||||
'@hono/standard-validator@0.2.2':
|
|
||||||
resolution: {integrity: sha512-mJ7W84Bt/rSvoIl63Ynew+UZOHAzzRAoAXb3JaWuxAkM/Lzg+ZHTCUiz77KOtn2e623WNN8LkD57Dk0szqUrIw==}
|
|
||||||
peerDependencies:
|
|
||||||
'@standard-schema/spec': ^1.0.0
|
|
||||||
hono: '>=3.9.0'
|
|
||||||
|
|
||||||
'@iconify/json@2.2.444':
|
'@iconify/json@2.2.444':
|
||||||
resolution: {integrity: sha512-z0UwFaVtaN/h/iWZ1kzEjqFU3sp0rRy93tzOtpepZU89DY39WsNeYZv2mxtft/2La6Bz2b4z1C/HkU5Cqv3gbw==}
|
resolution: {integrity: sha512-z0UwFaVtaN/h/iWZ1kzEjqFU3sp0rRy93tzOtpepZU89DY39WsNeYZv2mxtft/2La6Bz2b4z1C/HkU5Cqv3gbw==}
|
||||||
|
|
||||||
@@ -1872,14 +1842,6 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
vite: ^5.2.0 || ^6 || ^7
|
vite: ^5.2.0 || ^6 || ^7
|
||||||
|
|
||||||
'@tanstack/query-core@5.90.20':
|
|
||||||
resolution: {integrity: sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==}
|
|
||||||
|
|
||||||
'@tanstack/svelte-query@6.0.18':
|
|
||||||
resolution: {integrity: sha512-iGS8osfrIVUW5pkV4Ig6pspNIMtiNjGnVTNJKDas0m/QaNDFFIKbgg74rCzcjwrTIvO38tMpzb4VUKklvAmjxw==}
|
|
||||||
peerDependencies:
|
|
||||||
svelte: ^5.25.0
|
|
||||||
|
|
||||||
'@tanstack/table-core@8.21.3':
|
'@tanstack/table-core@8.21.3':
|
||||||
resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==}
|
resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@@ -2030,10 +1992,6 @@ packages:
|
|||||||
engines: {node: '>=0.4.0'}
|
engines: {node: '>=0.4.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
adler-32@1.3.1:
|
|
||||||
resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==}
|
|
||||||
engines: {node: '>=0.8'}
|
|
||||||
|
|
||||||
agent-base@7.1.4:
|
agent-base@7.1.4:
|
||||||
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
|
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
@@ -2086,13 +2044,6 @@ packages:
|
|||||||
balanced-match@1.0.2:
|
balanced-match@1.0.2:
|
||||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||||
|
|
||||||
base64-js@0.0.8:
|
|
||||||
resolution: {integrity: sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==}
|
|
||||||
engines: {node: '>= 0.4'}
|
|
||||||
|
|
||||||
base64-js@1.5.1:
|
|
||||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
|
||||||
|
|
||||||
better-auth@1.4.20:
|
better-auth@1.4.20:
|
||||||
resolution: {integrity: sha512-cUQaUhZ/EZwb7xgoL9wHl78yWp0eaxC/L++B/r8RJxk23L766Tk7fLjWG6bQK8eAHDDpfQNwXsJowiei8tJWJw==}
|
resolution: {integrity: sha512-cUQaUhZ/EZwb7xgoL9wHl78yWp0eaxC/L++B/r8RJxk23L766Tk7fLjWG6bQK8eAHDDpfQNwXsJowiei8tJWJw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -2176,9 +2127,6 @@ packages:
|
|||||||
brace-expansion@2.0.2:
|
brace-expansion@2.0.2:
|
||||||
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
|
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
|
||||||
|
|
||||||
brotli@1.3.3:
|
|
||||||
resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==}
|
|
||||||
|
|
||||||
buffer-from@1.1.2:
|
buffer-from@1.1.2:
|
||||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||||
|
|
||||||
@@ -2193,10 +2141,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==}
|
resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==}
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
|
|
||||||
cfb@1.2.2:
|
|
||||||
resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
|
|
||||||
engines: {node: '>=0.8'}
|
|
||||||
|
|
||||||
chai@6.2.2:
|
chai@6.2.2:
|
||||||
resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
|
resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -2218,10 +2162,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
clone@2.1.2:
|
|
||||||
resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==}
|
|
||||||
engines: {node: '>=0.8'}
|
|
||||||
|
|
||||||
clsx@2.1.1:
|
clsx@2.1.1:
|
||||||
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -2230,10 +2170,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
|
resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
codepage@1.15.0:
|
|
||||||
resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==}
|
|
||||||
engines: {node: '>=0.8'}
|
|
||||||
|
|
||||||
color-convert@2.0.1:
|
color-convert@2.0.1:
|
||||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||||
engines: {node: '>=7.0.0'}
|
engines: {node: '>=7.0.0'}
|
||||||
@@ -2274,18 +2210,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
|
resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
crc-32@1.2.2:
|
|
||||||
resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
|
|
||||||
engines: {node: '>=0.8'}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
cross-spawn@7.0.6:
|
cross-spawn@7.0.6:
|
||||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
crypto-js@4.2.0:
|
|
||||||
resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==}
|
|
||||||
|
|
||||||
cssesc@3.0.0:
|
cssesc@3.0.0:
|
||||||
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
|
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -2447,9 +2375,6 @@ packages:
|
|||||||
devalue@5.6.3:
|
devalue@5.6.3:
|
||||||
resolution: {integrity: sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg==}
|
resolution: {integrity: sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg==}
|
||||||
|
|
||||||
dfa@1.2.0:
|
|
||||||
resolution: {integrity: sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==}
|
|
||||||
|
|
||||||
dijkstrajs@1.0.3:
|
dijkstrajs@1.0.3:
|
||||||
resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
|
resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
|
||||||
|
|
||||||
@@ -2666,9 +2591,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==}
|
resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==}
|
||||||
engines: {node: '>=8.0.0'}
|
engines: {node: '>=8.0.0'}
|
||||||
|
|
||||||
fast-deep-equal@3.1.3:
|
|
||||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
|
||||||
|
|
||||||
fdir@6.5.0:
|
fdir@6.5.0:
|
||||||
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
|
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
|
||||||
engines: {node: '>=12.0.0'}
|
engines: {node: '>=12.0.0'}
|
||||||
@@ -2692,9 +2614,6 @@ packages:
|
|||||||
fn.name@1.1.0:
|
fn.name@1.1.0:
|
||||||
resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==}
|
resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==}
|
||||||
|
|
||||||
fontkit@2.0.4:
|
|
||||||
resolution: {integrity: sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==}
|
|
||||||
|
|
||||||
foreground-child@3.3.1:
|
foreground-child@3.3.1:
|
||||||
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
|
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
@@ -2713,10 +2632,6 @@ packages:
|
|||||||
forwarded-parse@2.1.2:
|
forwarded-parse@2.1.2:
|
||||||
resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==}
|
resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==}
|
||||||
|
|
||||||
frac@1.1.2:
|
|
||||||
resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==}
|
|
||||||
engines: {node: '>=0.8'}
|
|
||||||
|
|
||||||
fsevents@2.3.3:
|
fsevents@2.3.3:
|
||||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
@@ -2851,10 +2766,6 @@ packages:
|
|||||||
jose@6.1.3:
|
jose@6.1.3:
|
||||||
resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==}
|
resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==}
|
||||||
|
|
||||||
jpeg-exif@1.1.4:
|
|
||||||
resolution: {integrity: sha512-a+bKEcCjtuW5WTdgeXFzswSrdqi0jk4XlEtZlx5A94wCoBpFjfFTbo/Tra5SpNCl/YFZPvcV1dJc+TAYeg6ROQ==}
|
|
||||||
deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
|
|
||||||
|
|
||||||
json-bigint@1.0.0:
|
json-bigint@1.0.0:
|
||||||
resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==}
|
resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==}
|
||||||
|
|
||||||
@@ -2967,9 +2878,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==}
|
resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
|
|
||||||
linebreak@1.1.0:
|
|
||||||
resolution: {integrity: sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==}
|
|
||||||
|
|
||||||
linkify-it@5.0.0:
|
linkify-it@5.0.0:
|
||||||
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
|
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
|
||||||
|
|
||||||
@@ -3016,11 +2924,6 @@ packages:
|
|||||||
mailparser@3.9.3:
|
mailparser@3.9.3:
|
||||||
resolution: {integrity: sha512-AnB0a3zROum6fLaa52L+/K2SoRJVyFDk78Ea6q1D0ofcZLxWEWDtsS1+OrVqKbV7r5dulKL/AwYQccFGAPpuYQ==}
|
resolution: {integrity: sha512-AnB0a3zROum6fLaa52L+/K2SoRJVyFDk78Ea6q1D0ofcZLxWEWDtsS1+OrVqKbV7r5dulKL/AwYQccFGAPpuYQ==}
|
||||||
|
|
||||||
marked@17.0.3:
|
|
||||||
resolution: {integrity: sha512-jt1v2ObpyOKR8p4XaUJVk3YWRJ5n+i4+rjQopxvV32rSndTJXvIzuUdWWIy/1pFQMkQmvTXawzDNqOH/CUmx6A==}
|
|
||||||
engines: {node: '>= 20'}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
memoize-weak@1.0.2:
|
memoize-weak@1.0.2:
|
||||||
resolution: {integrity: sha512-gj39xkrjEw7nCn4nJ1M5ms6+MyMlyiGmttzsqAUsAKn6bYKwuTHh/AO3cKPF8IBrTIYTxb0wWXFs3E//Y8VoWQ==}
|
resolution: {integrity: sha512-gj39xkrjEw7nCn4nJ1M5ms6+MyMlyiGmttzsqAUsAKn6bYKwuTHh/AO3cKPF8IBrTIYTxb0wWXFs3E//Y8VoWQ==}
|
||||||
|
|
||||||
@@ -3144,9 +3047,6 @@ packages:
|
|||||||
package-manager-detector@1.6.0:
|
package-manager-detector@1.6.0:
|
||||||
resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==}
|
resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==}
|
||||||
|
|
||||||
pako@0.2.9:
|
|
||||||
resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==}
|
|
||||||
|
|
||||||
paneforge@1.0.2:
|
paneforge@1.0.2:
|
||||||
resolution: {integrity: sha512-KzmIXQH1wCfwZ4RsMohD/IUtEjVhteR+c+ulb/CHYJHX8SuDXoJmChtsc/Xs5Wl8NHS4L5Q7cxL8MG40gSU1bA==}
|
resolution: {integrity: sha512-KzmIXQH1wCfwZ4RsMohD/IUtEjVhteR+c+ulb/CHYJHX8SuDXoJmChtsc/Xs5Wl8NHS4L5Q7cxL8MG40gSU1bA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -3173,9 +3073,6 @@ packages:
|
|||||||
pathe@2.0.3:
|
pathe@2.0.3:
|
||||||
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||||
|
|
||||||
pdfkit@0.17.2:
|
|
||||||
resolution: {integrity: sha512-UnwF5fXy08f0dnp4jchFYAROKMNTaPqb/xgR8GtCzIcqoTnbOqtp3bwKvO4688oHI6vzEEs8Q6vqqEnC5IUELw==}
|
|
||||||
|
|
||||||
peberminta@0.9.0:
|
peberminta@0.9.0:
|
||||||
resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==}
|
resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==}
|
||||||
|
|
||||||
@@ -3213,9 +3110,6 @@ packages:
|
|||||||
pkg-types@2.3.0:
|
pkg-types@2.3.0:
|
||||||
resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
|
resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
|
||||||
|
|
||||||
png-js@1.0.0:
|
|
||||||
resolution: {integrity: sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==}
|
|
||||||
|
|
||||||
pngjs@5.0.0:
|
pngjs@5.0.0:
|
||||||
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
|
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
|
||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
@@ -3390,9 +3284,6 @@ packages:
|
|||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
restructure@3.0.2:
|
|
||||||
resolution: {integrity: sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==}
|
|
||||||
|
|
||||||
rimraf@5.0.10:
|
rimraf@5.0.10:
|
||||||
resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==}
|
resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -3516,10 +3407,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
|
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
|
||||||
engines: {node: '>= 10.x'}
|
engines: {node: '>= 10.x'}
|
||||||
|
|
||||||
ssf@0.11.2:
|
|
||||||
resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
|
|
||||||
engines: {node: '>=0.8'}
|
|
||||||
|
|
||||||
stack-trace@0.0.10:
|
stack-trace@0.0.10:
|
||||||
resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==}
|
resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==}
|
||||||
|
|
||||||
@@ -3642,9 +3529,6 @@ packages:
|
|||||||
tiny-case@1.0.3:
|
tiny-case@1.0.3:
|
||||||
resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==}
|
resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==}
|
||||||
|
|
||||||
tiny-inflate@1.0.3:
|
|
||||||
resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==}
|
|
||||||
|
|
||||||
tinybench@2.9.0:
|
tinybench@2.9.0:
|
||||||
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
|
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
|
||||||
|
|
||||||
@@ -3664,10 +3548,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA==}
|
resolution: {integrity: sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
tmp@0.2.5:
|
|
||||||
resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==}
|
|
||||||
engines: {node: '>=14.14'}
|
|
||||||
|
|
||||||
toposort@2.0.2:
|
toposort@2.0.2:
|
||||||
resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==}
|
resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==}
|
||||||
|
|
||||||
@@ -3752,12 +3632,6 @@ packages:
|
|||||||
undici-types@7.18.2:
|
undici-types@7.18.2:
|
||||||
resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
|
resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
|
||||||
|
|
||||||
unicode-properties@1.4.1:
|
|
||||||
resolution: {integrity: sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==}
|
|
||||||
|
|
||||||
unicode-trie@2.0.0:
|
|
||||||
resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==}
|
|
||||||
|
|
||||||
unplugin-icons@23.0.1:
|
unplugin-icons@23.0.1:
|
||||||
resolution: {integrity: sha512-rv0XEJepajKzDLvRUWASM8K+8+/CCfZn2jtogXqg6RIp7kpatRc/aFrVJn8ANQA09e++lPEEv9yX8cC9enc+QQ==}
|
resolution: {integrity: sha512-rv0XEJepajKzDLvRUWASM8K+8+/CCfZn2jtogXqg6RIp7kpatRc/aFrVJn8ANQA09e++lPEEv9yX8cC9enc+QQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -3914,14 +3788,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==}
|
resolution: {integrity: sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
|
|
||||||
wmf@1.0.2:
|
|
||||||
resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==}
|
|
||||||
engines: {node: '>=0.8'}
|
|
||||||
|
|
||||||
word@0.3.0:
|
|
||||||
resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==}
|
|
||||||
engines: {node: '>=0.8'}
|
|
||||||
|
|
||||||
wrap-ansi@6.2.0:
|
wrap-ansi@6.2.0:
|
||||||
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
|
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -3934,11 +3800,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
|
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
xlsx@0.18.5:
|
|
||||||
resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==}
|
|
||||||
engines: {node: '>=0.8'}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
xtend@4.0.2:
|
xtend@4.0.2:
|
||||||
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
|
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
|
||||||
engines: {node: '>=0.4'}
|
engines: {node: '>=0.4'}
|
||||||
@@ -4301,11 +4162,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
hono: 4.12.3
|
hono: 4.12.3
|
||||||
|
|
||||||
'@hono/standard-validator@0.2.2(@standard-schema/spec@1.1.0)(hono@4.12.3)':
|
|
||||||
dependencies:
|
|
||||||
'@standard-schema/spec': 1.1.0
|
|
||||||
hono: 4.12.3
|
|
||||||
|
|
||||||
'@iconify/json@2.2.444':
|
'@iconify/json@2.2.444':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@iconify/types': 2.0.0
|
'@iconify/types': 2.0.0
|
||||||
@@ -5406,13 +5262,6 @@ snapshots:
|
|||||||
tailwindcss: 4.2.1
|
tailwindcss: 4.2.1
|
||||||
vite: 7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)
|
vite: 7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)
|
||||||
|
|
||||||
'@tanstack/query-core@5.90.20': {}
|
|
||||||
|
|
||||||
'@tanstack/svelte-query@6.0.18(svelte@5.53.6)':
|
|
||||||
dependencies:
|
|
||||||
'@tanstack/query-core': 5.90.20
|
|
||||||
svelte: 5.53.6
|
|
||||||
|
|
||||||
'@tanstack/table-core@8.21.3': {}
|
'@tanstack/table-core@8.21.3': {}
|
||||||
|
|
||||||
'@types/aws-lambda@8.10.161': {}
|
'@types/aws-lambda@8.10.161': {}
|
||||||
@@ -5585,8 +5434,6 @@ snapshots:
|
|||||||
|
|
||||||
acorn@8.16.0: {}
|
acorn@8.16.0: {}
|
||||||
|
|
||||||
adler-32@1.3.1: {}
|
|
||||||
|
|
||||||
agent-base@7.1.4: {}
|
agent-base@7.1.4: {}
|
||||||
|
|
||||||
ansi-regex@5.0.1: {}
|
ansi-regex@5.0.1: {}
|
||||||
@@ -5629,10 +5476,6 @@ snapshots:
|
|||||||
|
|
||||||
balanced-match@1.0.2: {}
|
balanced-match@1.0.2: {}
|
||||||
|
|
||||||
base64-js@0.0.8: {}
|
|
||||||
|
|
||||||
base64-js@1.5.1: {}
|
|
||||||
|
|
||||||
better-auth@1.4.20(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.6)(vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.6)(typescript@5.9.3)(vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.6)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)):
|
better-auth@1.4.20(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.6)(vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.6)(typescript@5.9.3)(vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.6)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@better-auth/core': 1.4.20(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1)
|
'@better-auth/core': 1.4.20(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1)
|
||||||
@@ -5680,10 +5523,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
balanced-match: 1.0.2
|
balanced-match: 1.0.2
|
||||||
|
|
||||||
brotli@1.3.3:
|
|
||||||
dependencies:
|
|
||||||
base64-js: 1.5.1
|
|
||||||
|
|
||||||
buffer-from@1.1.2: {}
|
buffer-from@1.1.2: {}
|
||||||
|
|
||||||
bun-types@1.3.9:
|
bun-types@1.3.9:
|
||||||
@@ -5695,11 +5534,6 @@ snapshots:
|
|||||||
camelcase@8.0.0:
|
camelcase@8.0.0:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
cfb@1.2.2:
|
|
||||||
dependencies:
|
|
||||||
adler-32: 1.3.1
|
|
||||||
crc-32: 1.2.2
|
|
||||||
|
|
||||||
chai@6.2.2: {}
|
chai@6.2.2: {}
|
||||||
|
|
||||||
chokidar@4.0.3:
|
chokidar@4.0.3:
|
||||||
@@ -5727,14 +5561,10 @@ snapshots:
|
|||||||
strip-ansi: 6.0.1
|
strip-ansi: 6.0.1
|
||||||
wrap-ansi: 7.0.0
|
wrap-ansi: 7.0.0
|
||||||
|
|
||||||
clone@2.1.2: {}
|
|
||||||
|
|
||||||
clsx@2.1.1: {}
|
clsx@2.1.1: {}
|
||||||
|
|
||||||
cluster-key-slot@1.1.2: {}
|
cluster-key-slot@1.1.2: {}
|
||||||
|
|
||||||
codepage@1.15.0: {}
|
|
||||||
|
|
||||||
color-convert@2.0.1:
|
color-convert@2.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
color-name: 1.1.4
|
color-name: 1.1.4
|
||||||
@@ -5766,16 +5596,12 @@ snapshots:
|
|||||||
|
|
||||||
cookie@0.6.0: {}
|
cookie@0.6.0: {}
|
||||||
|
|
||||||
crc-32@1.2.2: {}
|
|
||||||
|
|
||||||
cross-spawn@7.0.6:
|
cross-spawn@7.0.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
path-key: 3.1.1
|
path-key: 3.1.1
|
||||||
shebang-command: 2.0.0
|
shebang-command: 2.0.0
|
||||||
which: 2.0.2
|
which: 2.0.2
|
||||||
|
|
||||||
crypto-js@4.2.0: {}
|
|
||||||
|
|
||||||
cssesc@3.0.0: {}
|
cssesc@3.0.0: {}
|
||||||
|
|
||||||
d3-array@2.12.1:
|
d3-array@2.12.1:
|
||||||
@@ -5911,8 +5737,6 @@ snapshots:
|
|||||||
|
|
||||||
devalue@5.6.3: {}
|
devalue@5.6.3: {}
|
||||||
|
|
||||||
dfa@1.2.0: {}
|
|
||||||
|
|
||||||
dijkstrajs@1.0.3: {}
|
dijkstrajs@1.0.3: {}
|
||||||
|
|
||||||
dlv@1.1.3:
|
dlv@1.1.3:
|
||||||
@@ -6106,8 +5930,6 @@ snapshots:
|
|||||||
pure-rand: 6.1.0
|
pure-rand: 6.1.0
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
fast-deep-equal@3.1.3: {}
|
|
||||||
|
|
||||||
fdir@6.5.0(picomatch@4.0.3):
|
fdir@6.5.0(picomatch@4.0.3):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
picomatch: 4.0.3
|
picomatch: 4.0.3
|
||||||
@@ -6126,18 +5948,6 @@ snapshots:
|
|||||||
|
|
||||||
fn.name@1.1.0: {}
|
fn.name@1.1.0: {}
|
||||||
|
|
||||||
fontkit@2.0.4:
|
|
||||||
dependencies:
|
|
||||||
'@swc/helpers': 0.5.19
|
|
||||||
brotli: 1.3.3
|
|
||||||
clone: 2.1.2
|
|
||||||
dfa: 1.2.0
|
|
||||||
fast-deep-equal: 3.1.3
|
|
||||||
restructure: 3.0.2
|
|
||||||
tiny-inflate: 1.0.3
|
|
||||||
unicode-properties: 1.4.1
|
|
||||||
unicode-trie: 2.0.0
|
|
||||||
|
|
||||||
foreground-child@3.3.1:
|
foreground-child@3.3.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
cross-spawn: 7.0.6
|
cross-spawn: 7.0.6
|
||||||
@@ -6155,8 +5965,6 @@ snapshots:
|
|||||||
|
|
||||||
forwarded-parse@2.1.2: {}
|
forwarded-parse@2.1.2: {}
|
||||||
|
|
||||||
frac@1.1.2: {}
|
|
||||||
|
|
||||||
fsevents@2.3.3:
|
fsevents@2.3.3:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -6325,8 +6133,6 @@ snapshots:
|
|||||||
|
|
||||||
jose@6.1.3: {}
|
jose@6.1.3: {}
|
||||||
|
|
||||||
jpeg-exif@1.1.4: {}
|
|
||||||
|
|
||||||
json-bigint@1.0.0:
|
json-bigint@1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
bignumber.js: 9.3.1
|
bignumber.js: 9.3.1
|
||||||
@@ -6439,11 +6245,6 @@ snapshots:
|
|||||||
lightningcss-win32-arm64-msvc: 1.31.1
|
lightningcss-win32-arm64-msvc: 1.31.1
|
||||||
lightningcss-win32-x64-msvc: 1.31.1
|
lightningcss-win32-x64-msvc: 1.31.1
|
||||||
|
|
||||||
linebreak@1.1.0:
|
|
||||||
dependencies:
|
|
||||||
base64-js: 0.0.8
|
|
||||||
unicode-trie: 2.0.0
|
|
||||||
|
|
||||||
linkify-it@5.0.0:
|
linkify-it@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
uc.micro: 2.1.0
|
uc.micro: 2.1.0
|
||||||
@@ -6500,8 +6301,6 @@ snapshots:
|
|||||||
punycode.js: 2.3.1
|
punycode.js: 2.3.1
|
||||||
tlds: 1.261.0
|
tlds: 1.261.0
|
||||||
|
|
||||||
marked@17.0.3: {}
|
|
||||||
|
|
||||||
memoize-weak@1.0.2: {}
|
memoize-weak@1.0.2: {}
|
||||||
|
|
||||||
memoize@10.2.0:
|
memoize@10.2.0:
|
||||||
@@ -6599,8 +6398,6 @@ snapshots:
|
|||||||
|
|
||||||
package-manager-detector@1.6.0: {}
|
package-manager-detector@1.6.0: {}
|
||||||
|
|
||||||
pako@0.2.9: {}
|
|
||||||
|
|
||||||
paneforge@1.0.2(svelte@5.53.6):
|
paneforge@1.0.2(svelte@5.53.6):
|
||||||
dependencies:
|
dependencies:
|
||||||
runed: 0.23.4(svelte@5.53.6)
|
runed: 0.23.4(svelte@5.53.6)
|
||||||
@@ -6625,14 +6422,6 @@ snapshots:
|
|||||||
|
|
||||||
pathe@2.0.3: {}
|
pathe@2.0.3: {}
|
||||||
|
|
||||||
pdfkit@0.17.2:
|
|
||||||
dependencies:
|
|
||||||
crypto-js: 4.2.0
|
|
||||||
fontkit: 2.0.4
|
|
||||||
jpeg-exif: 1.1.4
|
|
||||||
linebreak: 1.1.0
|
|
||||||
png-js: 1.0.0
|
|
||||||
|
|
||||||
peberminta@0.9.0: {}
|
peberminta@0.9.0: {}
|
||||||
|
|
||||||
pg-int8@1.0.1: {}
|
pg-int8@1.0.1: {}
|
||||||
@@ -6683,8 +6472,6 @@ snapshots:
|
|||||||
exsolve: 1.0.8
|
exsolve: 1.0.8
|
||||||
pathe: 2.0.3
|
pathe: 2.0.3
|
||||||
|
|
||||||
png-js@1.0.0: {}
|
|
||||||
|
|
||||||
pngjs@5.0.0: {}
|
pngjs@5.0.0: {}
|
||||||
|
|
||||||
postcss-selector-parser@6.0.10:
|
postcss-selector-parser@6.0.10:
|
||||||
@@ -6814,8 +6601,6 @@ snapshots:
|
|||||||
path-parse: 1.0.7
|
path-parse: 1.0.7
|
||||||
supports-preserve-symlinks-flag: 1.0.0
|
supports-preserve-symlinks-flag: 1.0.0
|
||||||
|
|
||||||
restructure@3.0.2: {}
|
|
||||||
|
|
||||||
rimraf@5.0.10:
|
rimraf@5.0.10:
|
||||||
dependencies:
|
dependencies:
|
||||||
glob: 10.5.0
|
glob: 10.5.0
|
||||||
@@ -6949,10 +6734,6 @@ snapshots:
|
|||||||
|
|
||||||
split2@4.2.0: {}
|
split2@4.2.0: {}
|
||||||
|
|
||||||
ssf@0.11.2:
|
|
||||||
dependencies:
|
|
||||||
frac: 1.1.2
|
|
||||||
|
|
||||||
stack-trace@0.0.10: {}
|
stack-trace@0.0.10: {}
|
||||||
|
|
||||||
stackback@0.0.2: {}
|
stackback@0.0.2: {}
|
||||||
@@ -7110,8 +6891,6 @@ snapshots:
|
|||||||
tiny-case@1.0.3:
|
tiny-case@1.0.3:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
tiny-inflate@1.0.3: {}
|
|
||||||
|
|
||||||
tinybench@2.9.0: {}
|
tinybench@2.9.0: {}
|
||||||
|
|
||||||
tinyexec@1.0.2: {}
|
tinyexec@1.0.2: {}
|
||||||
@@ -7125,8 +6904,6 @@ snapshots:
|
|||||||
|
|
||||||
tlds@1.261.0: {}
|
tlds@1.261.0: {}
|
||||||
|
|
||||||
tmp@0.2.5: {}
|
|
||||||
|
|
||||||
toposort@2.0.2:
|
toposort@2.0.2:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -7191,16 +6968,6 @@ snapshots:
|
|||||||
|
|
||||||
undici-types@7.18.2: {}
|
undici-types@7.18.2: {}
|
||||||
|
|
||||||
unicode-properties@1.4.1:
|
|
||||||
dependencies:
|
|
||||||
base64-js: 1.5.1
|
|
||||||
unicode-trie: 2.0.0
|
|
||||||
|
|
||||||
unicode-trie@2.0.0:
|
|
||||||
dependencies:
|
|
||||||
pako: 0.2.9
|
|
||||||
tiny-inflate: 1.0.3
|
|
||||||
|
|
||||||
unplugin-icons@23.0.1(svelte@5.53.6):
|
unplugin-icons@23.0.1(svelte@5.53.6):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@antfu/install-pkg': 1.1.0
|
'@antfu/install-pkg': 1.1.0
|
||||||
@@ -7328,10 +7095,6 @@ snapshots:
|
|||||||
triple-beam: 1.4.1
|
triple-beam: 1.4.1
|
||||||
winston-transport: 4.9.0
|
winston-transport: 4.9.0
|
||||||
|
|
||||||
wmf@1.0.2: {}
|
|
||||||
|
|
||||||
word@0.3.0: {}
|
|
||||||
|
|
||||||
wrap-ansi@6.2.0:
|
wrap-ansi@6.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
ansi-styles: 4.3.0
|
ansi-styles: 4.3.0
|
||||||
@@ -7350,16 +7113,6 @@ snapshots:
|
|||||||
string-width: 5.1.2
|
string-width: 5.1.2
|
||||||
strip-ansi: 7.2.0
|
strip-ansi: 7.2.0
|
||||||
|
|
||||||
xlsx@0.18.5:
|
|
||||||
dependencies:
|
|
||||||
adler-32: 1.3.1
|
|
||||||
cfb: 1.2.2
|
|
||||||
codepage: 1.15.0
|
|
||||||
crc-32: 1.2.2
|
|
||||||
ssf: 0.11.2
|
|
||||||
wmf: 1.0.2
|
|
||||||
word: 0.3.0
|
|
||||||
|
|
||||||
xtend@4.0.2: {}
|
xtend@4.0.2: {}
|
||||||
|
|
||||||
y18n@4.0.3: {}
|
y18n@4.0.3: {}
|
||||||
|
|||||||
Reference in New Issue
Block a user