feat(tauth): add tauth
refactor(ttp): now using ttp-core package from git
This commit is contained in:
parent
077d1ae864
commit
cbbadc9e67
26 changed files with 519 additions and 2916 deletions
|
|
@ -25,6 +25,7 @@
|
|||
"@tensamin/storage": "workspace:*",
|
||||
"@tensamin/tauri": "workspace:*",
|
||||
"@tensamin/ttp": "workspace:*",
|
||||
"@tensamin/tauth": "workspace:*",
|
||||
"@tensamin/ui": "*",
|
||||
"@tensamin/user": "workspace:*",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import {
|
|||
import { displayCallId } from "@tensamin/call/utils";
|
||||
import { useState } from "react";
|
||||
import { SidebarTrigger, useSidebar } from "@tensamin/ui";
|
||||
import { useTTP } from "@tensamin/ttp/context";
|
||||
import { useTTP } from "@tensamin/ttp";
|
||||
import { WindowControls as Controls } from "@tensamin/ui";
|
||||
|
||||
export default function Navbar({ forMobile }: { forMobile: boolean }) {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export default function Sidebar() {
|
|||
const showMobileNavbar = useShowMobileNavbar();
|
||||
|
||||
return (
|
||||
<SidebarRoot>
|
||||
<SidebarRoot className="border-0!">
|
||||
<SidebarContent
|
||||
className={
|
||||
isTauri() && isMobile ? "pt-[env(safe-area-inset-top)]" : "pt-2"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from "react";
|
||||
import { useVirtualizer } from "@tanstack/react-virtual";
|
||||
import { useTTP } from "@tensamin/ttp/context";
|
||||
import { useTTP } from "@tensamin/ttp";
|
||||
|
||||
import Switch from "./switch";
|
||||
import ConversationModal from "../modal/conversation";
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import "@tensamin/ui/index.css";
|
|||
|
||||
import NotFound from "@/routes/404";
|
||||
|
||||
import RootLayout from "@/routes/layout";
|
||||
import AppLayout from "@/routes/app/layout";
|
||||
import SettingsLayout from "@/features/settings/layout";
|
||||
|
||||
|
|
@ -23,13 +22,28 @@ import Login from "@/routes/screens/login";
|
|||
|
||||
import ChatContext from "@tensamin/chat/context";
|
||||
import CallContext from "@tensamin/call/context";
|
||||
import SocketContext from "@tensamin/ttp/context";
|
||||
import { Provider as SocketContext } from "@tensamin/ttp";
|
||||
import UserContext from "@tensamin/user/context";
|
||||
import DeeplinkContext from "@tensamin/tauri/deeplinkHandler";
|
||||
|
||||
import TAuthWrapper from "@tensamin/tauth/context";
|
||||
|
||||
import { ThemeProvider } from "@tensamin/ui";
|
||||
import z from "zod";
|
||||
|
||||
import { useEffect, useState, type ReactNode } from "react";
|
||||
|
||||
import Storage from "@tensamin/storage/context";
|
||||
import Crypto from "@tensamin/crypto/context";
|
||||
import Mobile from "@tensamin/tauri/context";
|
||||
|
||||
import LegalWrapper from "@/features/legal/screen";
|
||||
|
||||
import { useStorage } from "@tensamin/storage/context";
|
||||
import { useLocation, useNavigate } from "@tanstack/react-router";
|
||||
import { useIsMobile, Toaster, TooltipProvider } from "@tensamin/ui";
|
||||
import { isTauri } from "@tauri-apps/api/core";
|
||||
|
||||
const wrapper = document.getElementById("root");
|
||||
|
||||
if (!wrapper) {
|
||||
|
|
@ -49,14 +63,75 @@ window.setLogLevelToMax = () => {
|
|||
location.reload();
|
||||
};
|
||||
|
||||
function LoginWrapper({ children }: { children: ReactNode }) {
|
||||
const [loggedIn, setLoggedIn] = useState<boolean | null>(null);
|
||||
|
||||
const { load } = useStorage();
|
||||
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
|
||||
load("user_id").then((userId) => {
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (userId !== 0) {
|
||||
setLoggedIn(true);
|
||||
return;
|
||||
}
|
||||
|
||||
setLoggedIn(false);
|
||||
navigate({
|
||||
to: "/login",
|
||||
});
|
||||
});
|
||||
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, [load, navigate]);
|
||||
|
||||
if (loggedIn !== true && location.pathname !== "/login") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
function RootShell() {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
return (
|
||||
<DeeplinkContext>
|
||||
<ThemeProvider>
|
||||
<div className="w-screen h-screen overflow-hidden">
|
||||
<RootLayout>
|
||||
<Outlet />
|
||||
</RootLayout>
|
||||
<Toaster
|
||||
position={isMobile ? "top-center" : "bottom-right"}
|
||||
{...(isTauri() && isMobile
|
||||
? {
|
||||
mobileOffset: {
|
||||
top: "env(safe-area-inset-top)",
|
||||
},
|
||||
}
|
||||
: {})}
|
||||
/>
|
||||
<TooltipProvider>
|
||||
<Storage>
|
||||
<LoginWrapper>
|
||||
<LegalWrapper>
|
||||
<Crypto>
|
||||
<Mobile>
|
||||
<Outlet />
|
||||
</Mobile>
|
||||
</Crypto>
|
||||
</LegalWrapper>
|
||||
</LoginWrapper>
|
||||
</Storage>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
</DeeplinkContext>
|
||||
|
|
@ -68,9 +143,11 @@ function AppShell() {
|
|||
<SocketContext>
|
||||
<UserContext>
|
||||
<CallContext>
|
||||
<AppLayout>
|
||||
<Outlet />
|
||||
</AppLayout>
|
||||
<TAuthWrapper>
|
||||
<AppLayout>
|
||||
<Outlet />
|
||||
</AppLayout>
|
||||
</TAuthWrapper>
|
||||
</CallContext>
|
||||
</UserContext>
|
||||
</SocketContext>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
} from "@tensamin/ui";
|
||||
import z from "zod";
|
||||
import { toast } from "@tensamin/shared/log";
|
||||
import { useTTP } from "@tensamin/ttp/context";
|
||||
import { useTTP } from "@tensamin/ttp";
|
||||
import { useState } from "react";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { isTauri } from "@tauri-apps/api/core";
|
||||
|
|
|
|||
|
|
@ -1,86 +0,0 @@
|
|||
import { useEffect, useState, type ReactNode } from "react";
|
||||
|
||||
import Storage from "@tensamin/storage/context";
|
||||
import Crypto from "@tensamin/crypto/context";
|
||||
import Mobile from "@tensamin/tauri/context";
|
||||
|
||||
import LegalWrapper from "@/features/legal/screen";
|
||||
|
||||
import { useStorage } from "@tensamin/storage/context";
|
||||
import { useLocation, useNavigate } from "@tanstack/react-router";
|
||||
import { useIsMobile, Toaster, TooltipProvider } from "@tensamin/ui";
|
||||
import { isTauri } from "@tauri-apps/api/core";
|
||||
|
||||
/**
|
||||
* Executes Layout.
|
||||
* @param props Parameter props.
|
||||
* @returns unknown.
|
||||
*/
|
||||
export default function Layout(props: { children: ReactNode }) {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toaster
|
||||
position={isMobile ? "top-center" : "bottom-right"}
|
||||
{...(isTauri() && isMobile
|
||||
? {
|
||||
mobileOffset: {
|
||||
top: "env(safe-area-inset-top)",
|
||||
},
|
||||
}
|
||||
: {})}
|
||||
/>
|
||||
<TooltipProvider>
|
||||
<Storage>
|
||||
<LoginWrapper>
|
||||
<LegalWrapper>
|
||||
<Crypto>
|
||||
<Mobile>{props.children}</Mobile>
|
||||
</Crypto>
|
||||
</LegalWrapper>
|
||||
</LoginWrapper>
|
||||
</Storage>
|
||||
</TooltipProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function LoginWrapper({ children }: { children: ReactNode }) {
|
||||
const [loggedIn, setLoggedIn] = useState<boolean | null>(null);
|
||||
|
||||
const { load } = useStorage();
|
||||
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
|
||||
load("user_id").then((userId) => {
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (userId !== 0) {
|
||||
setLoggedIn(true);
|
||||
return;
|
||||
}
|
||||
|
||||
setLoggedIn(false);
|
||||
navigate({
|
||||
to: "/login",
|
||||
});
|
||||
});
|
||||
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, [load, navigate]);
|
||||
|
||||
if (loggedIn !== true && location.pathname !== "/login") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
194
bun.lock
194
bun.lock
|
|
@ -5,6 +5,7 @@
|
|||
"": {
|
||||
"name": "tensamin",
|
||||
"dependencies": {
|
||||
"@tensamin/ttp-core": "*",
|
||||
"@tensamin/ui": "*",
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -55,6 +56,7 @@
|
|||
"@tensamin/shared": "workspace:*",
|
||||
"@tensamin/storage": "workspace:*",
|
||||
"@tensamin/tauri": "workspace:*",
|
||||
"@tensamin/tauth": "workspace:*",
|
||||
"@tensamin/ttp": "workspace:*",
|
||||
"@tensamin/ui": "*",
|
||||
"@tensamin/user": "workspace:*",
|
||||
|
|
@ -167,6 +169,7 @@
|
|||
"name": "@tensamin/shared",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@tensamin/ui": "*",
|
||||
"lucide-react": "^0.564.0",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
|
|
@ -184,6 +187,25 @@
|
|||
"react-dom": "^19.2.0",
|
||||
},
|
||||
},
|
||||
"packages/tauth": {
|
||||
"name": "@tensamin/tauth",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@tanstack/react-router": "^1.0.0",
|
||||
"@tensamin/crypto": "workspace:*",
|
||||
"@tensamin/shared": "workspace:*",
|
||||
"@tensamin/storage": "workspace:*",
|
||||
"@tensamin/tauri": "workspace:*",
|
||||
"@tensamin/ttp": "workspace:*",
|
||||
"@tensamin/ui": "*",
|
||||
"@tensamin/user": "workspace:*",
|
||||
"lucide-react": "^1.8.0",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"sonner": "^2.0.7",
|
||||
"zod": "^4.3.6",
|
||||
},
|
||||
},
|
||||
"packages/ttp": {
|
||||
"name": "@tensamin/ttp",
|
||||
"version": "0.0.0",
|
||||
|
|
@ -192,6 +214,7 @@
|
|||
"@tensamin/crypto": "workspace:*",
|
||||
"@tensamin/shared": "workspace:*",
|
||||
"@tensamin/storage": "workspace:*",
|
||||
"@tensamin/ttp-core": "*",
|
||||
"@tensamin/ui": "*",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
|
|
@ -215,7 +238,8 @@
|
|||
},
|
||||
},
|
||||
"overrides": {
|
||||
"@tensamin/ui": "https://git.methanium.net/tensamin/ui/archive/0.0.20.tar.gz",
|
||||
"@tensamin/ttp-core": "https://git.methanium.net/tensamin/ttp/archive/0.0.3.tar.gz",
|
||||
"@tensamin/ui": "https://git.methanium.net/tensamin/ui/archive/0.0.25.tar.gz",
|
||||
},
|
||||
"packages": {
|
||||
"@antfu/ni": ["@antfu/ni@25.0.0", "", { "dependencies": { "ansis": "^4.0.0", "fzf": "^0.5.2", "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" }, "bin": { "na": "bin/na.mjs", "ni": "bin/ni.mjs", "nr": "bin/nr.mjs", "nci": "bin/nci.mjs", "nlx": "bin/nlx.mjs", "nun": "bin/nun.mjs", "nup": "bin/nup.mjs" } }, "sha512-9q/yCljni37pkMr4sPrI3G4jqdIk074+iukc5aFJl7kmDCCsiJrbZ6zKxnES1Gwg+i9RcDZwvktl23puGslmvA=="],
|
||||
|
|
@ -308,6 +332,8 @@
|
|||
|
||||
"@codemirror/view": ["@codemirror/view@6.41.0", "", { "dependencies": { "@codemirror/state": "^6.6.0", "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, "sha512-6H/qadXsVuDY219Yljhohglve8xf4B8xJkVOEWfA5uiYKiTFppjqsvsfR5iPA0RbvRBoOyTZpbLIxe9+0UR8xA=="],
|
||||
|
||||
"@date-fns/tz": ["@date-fns/tz@1.4.1", "", {}, "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA=="],
|
||||
|
||||
"@dotenvx/dotenvx": ["@dotenvx/dotenvx@1.61.0", "", { "dependencies": { "commander": "^11.1.0", "dotenv": "^17.2.1", "eciesjs": "^0.4.10", "execa": "^5.1.1", "fdir": "^6.2.0", "ignore": "^5.3.0", "object-treeify": "1.1.33", "picomatch": "^4.0.2", "which": "^4.0.0", "yocto-spinner": "^1.1.0" }, "bin": { "dotenvx": "src/cli/dotenvx.js" } }, "sha512-utL3cpZoFzflyqUkjYbxYujI6STBTmO5LFn4bbin/NZnRWN6wQ7eErhr3/Vpa5h/jicPFC6kTa42r940mQftJQ=="],
|
||||
|
||||
"@ecies/ciphers": ["@ecies/ciphers@0.2.6", "", { "peerDependencies": { "@noble/ciphers": "^1.0.0" } }, "sha512-patgsRPKGkhhoBjETV4XxD0En4ui5fbX0hzayqI3M8tvNMGUoUvmyYAIWwlxBc1KX5cturfqByYdj5bYGRpN9g=="],
|
||||
|
|
@ -466,6 +492,42 @@
|
|||
|
||||
"@open-draft/until": ["@open-draft/until@2.1.0", "", {}, "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg=="],
|
||||
|
||||
"@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="],
|
||||
|
||||
"@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
|
||||
|
||||
"@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
|
||||
|
||||
"@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="],
|
||||
|
||||
"@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="],
|
||||
|
||||
"@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="],
|
||||
|
||||
"@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="],
|
||||
|
||||
"@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
|
||||
|
||||
"@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="],
|
||||
|
||||
"@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="],
|
||||
|
||||
"@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="],
|
||||
|
||||
"@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||
|
||||
"@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
|
||||
|
||||
"@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
|
||||
|
||||
"@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
|
||||
|
||||
"@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="],
|
||||
|
||||
"@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
|
||||
|
||||
"@reduxjs/toolkit": ["@reduxjs/toolkit@2.11.2", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@standard-schema/utils": "^0.3.0", "immer": "^11.0.0", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "reselect": "^5.1.0" }, "peerDependencies": { "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" }, "optionalPeers": ["react", "react-redux"] }, "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ=="],
|
||||
|
||||
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="],
|
||||
|
||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.60.1", "", { "os": "android", "cpu": "arm" }, "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA=="],
|
||||
|
|
@ -522,6 +584,12 @@
|
|||
|
||||
"@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="],
|
||||
|
||||
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="],
|
||||
|
||||
"@tabby_ai/hijri-converter": ["@tabby_ai/hijri-converter@1.0.5", "", {}, "sha512-r5bClKrcIusDoo049dSL8CawnHR6mRdDwhlQuIgZRNty68q0x8k3Lf1BtPAMxRf/GgnHBnIO4ujd3+GQdLWzxQ=="],
|
||||
|
||||
"@tailwindcss/node": ["@tailwindcss/node@4.2.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.2" } }, "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA=="],
|
||||
|
||||
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.2", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.2", "@tailwindcss/oxide-darwin-arm64": "4.2.2", "@tailwindcss/oxide-darwin-x64": "4.2.2", "@tailwindcss/oxide-freebsd-x64": "4.2.2", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", "@tailwindcss/oxide-linux-x64-musl": "4.2.2", "@tailwindcss/oxide-wasm32-wasi": "4.2.2", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" } }, "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg=="],
|
||||
|
|
@ -618,9 +686,13 @@
|
|||
|
||||
"@tensamin/tauri": ["@tensamin/tauri@workspace:apps/tauri"],
|
||||
|
||||
"@tensamin/tauth": ["@tensamin/tauth@workspace:packages/tauth"],
|
||||
|
||||
"@tensamin/ttp": ["@tensamin/ttp@workspace:packages/ttp"],
|
||||
|
||||
"@tensamin/ui": ["@tensamin/ui@https://git.methanium.net/tensamin/ui/archive/0.0.20.tar.gz", { "dependencies": { "@base-ui/react": "^1.3.0", "@fontsource-variable/inter": "^5.2.6", "@tauri-apps/api": "^2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^1.8.0", "next-themes": "^0.4.6", "shadcn": "^3.5.0", "sonner": "^2.0.7", "tailwind-merge": "^3.5.0", "tw-animate-css": "^1.3.0" }, "peerDependencies": { "react": "^19.2.0", "react-dom": "^19.2.0" } }, "sha512-Ys0xJcKw191mE6waQ0rgGrnYN93BKXd4rAoM/CC3wbu3wYXhVLJeCkEfGWW2qNJd0MjrmqAEVgO0PBkFjD5FEQ=="],
|
||||
"@tensamin/ttp-core": ["@tensamin/ttp-core@https://git.methanium.net/tensamin/ttp/archive/0.0.3.tar.gz", { "dependencies": { "@eslint/js": "^10.0.1", "@typescript-eslint/parser": "^8.58.1", "globals": "^17.5.0", "typescript": "^6.0.2", "typescript-eslint": "^8.58.1", "zod": "^4.3.6" } }, "sha512-fqpT3W/QeTULDYaZI2uXMHQ+6NUnNa4XTVbbl1k84vnwdzyQgdhezQUzjgXL1XrjEImb5CWosFNvcrzuIfwlfQ=="],
|
||||
|
||||
"@tensamin/ui": ["@tensamin/ui@https://git.methanium.net/tensamin/ui/archive/0.0.25.tar.gz", { "dependencies": { "@base-ui/react": "^1.3.0", "@fontsource-variable/inter": "^5.2.6", "@tauri-apps/api": "^2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", "date-fns": "^4.1.0", "embla-carousel-react": "^8.6.0", "input-otp": "^1.4.2", "lucide-react": "^1.8.0", "next-themes": "^0.4.6", "react-day-picker": "^9.14.0", "react-resizable-panels": "^4.10.0", "recharts": "3.8.0", "shadcn": "^3.5.0", "sonner": "^2.0.7", "tailwind-merge": "^3.5.0", "tw-animate-css": "^1.3.0", "vaul": "^1.1.2" }, "peerDependencies": { "react": "^19.2.0", "react-dom": "^19.2.0" } }, "sha512-pYkl4quhAhV/l/j1Yy5m4DDV5fh42H4G8v+FE6qEMCcJIOeA1LU7brIG7X9zURe7F4gyn1LQxMf2yABuV/NsMQ=="],
|
||||
|
||||
"@tensamin/user": ["@tensamin/user@workspace:packages/user"],
|
||||
|
||||
|
|
@ -636,6 +708,24 @@
|
|||
|
||||
"@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="],
|
||||
|
||||
"@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="],
|
||||
|
||||
"@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="],
|
||||
|
||||
"@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="],
|
||||
|
||||
"@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="],
|
||||
|
||||
"@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="],
|
||||
|
||||
"@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="],
|
||||
|
||||
"@types/d3-shape": ["@types/d3-shape@3.1.8", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w=="],
|
||||
|
||||
"@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="],
|
||||
|
||||
"@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="],
|
||||
|
||||
"@types/dom-mediacapture-record": ["@types/dom-mediacapture-record@1.0.22", "", {}, "sha512-mUMZLK3NvwRLcAAT9qmcK+9p7tpU2FHdDsntR3YI4+GY88XrgG4XiE7u1Q2LAN2/FZOz/tdMDC3GQCR4T8nFuw=="],
|
||||
|
||||
"@types/esrecurse": ["@types/esrecurse@4.3.1", "", {}, "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw=="],
|
||||
|
|
@ -654,6 +744,8 @@
|
|||
|
||||
"@types/statuses": ["@types/statuses@2.0.6", "", {}, "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA=="],
|
||||
|
||||
"@types/use-sync-external-store": ["@types/use-sync-external-store@0.0.6", "", {}, "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="],
|
||||
|
||||
"@types/validate-npm-package-name": ["@types/validate-npm-package-name@4.0.2", "", {}, "sha512-lrpDziQipxCEeK5kWxvljWYhUvOiB2A9izZd9B2AFarYAkqZshb4lPbRs7zKEic6eGtH8V/2qJW+dPp9OtF6bw=="],
|
||||
|
||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.58.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.58.1", "@typescript-eslint/type-utils": "8.58.1", "@typescript-eslint/utils": "8.58.1", "@typescript-eslint/visitor-keys": "8.58.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.58.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-eSkwoemjo76bdXl2MYqtxg51HNwUSkWfODUOQ3PaTLZGh9uIWWFZIjyjaJnex7wXDu+TRx+ATsnSxdN9YWfRTQ=="],
|
||||
|
|
@ -698,6 +790,8 @@
|
|||
|
||||
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="],
|
||||
|
||||
"ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="],
|
||||
|
||||
"balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
||||
|
|
@ -740,6 +834,8 @@
|
|||
|
||||
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
||||
|
||||
"cmdk": ["cmdk@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "^1.1.1", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-id": "^1.1.0", "@radix-ui/react-primitive": "^2.0.2" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg=="],
|
||||
|
||||
"code-block-writer": ["code-block-writer@13.0.3", "", {}, "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
|
|
@ -774,12 +870,40 @@
|
|||
|
||||
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
||||
|
||||
"d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="],
|
||||
|
||||
"d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="],
|
||||
|
||||
"d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="],
|
||||
|
||||
"d3-format": ["d3-format@3.1.2", "", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="],
|
||||
|
||||
"d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="],
|
||||
|
||||
"d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="],
|
||||
|
||||
"d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="],
|
||||
|
||||
"d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="],
|
||||
|
||||
"d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="],
|
||||
|
||||
"d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="],
|
||||
|
||||
"d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="],
|
||||
|
||||
"data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
|
||||
|
||||
"date-fns": ["date-fns@4.1.0", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="],
|
||||
|
||||
"date-fns-jalali": ["date-fns-jalali@4.1.0-0", "", {}, "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg=="],
|
||||
|
||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"decamelize": ["decamelize@1.2.0", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="],
|
||||
|
||||
"decimal.js-light": ["decimal.js-light@2.5.1", "", {}, "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="],
|
||||
|
||||
"dedent": ["dedent@1.7.2", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA=="],
|
||||
|
||||
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
||||
|
|
@ -796,6 +920,8 @@
|
|||
|
||||
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||
|
||||
"detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
|
||||
|
||||
"diff": ["diff@8.0.4", "", {}, "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw=="],
|
||||
|
||||
"dijkstrajs": ["dijkstrajs@1.0.3", "", {}, "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="],
|
||||
|
|
@ -810,6 +936,12 @@
|
|||
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.335", "", {}, "sha512-q9n5T4BR4Xwa2cwbrwcsDJtHD/enpQ5S1xF1IAtdqf5AAgqDFmR/aakqH3ChFdqd/QXJhS3rnnXFtexU7rax6Q=="],
|
||||
|
||||
"embla-carousel": ["embla-carousel@8.6.0", "", {}, "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA=="],
|
||||
|
||||
"embla-carousel-react": ["embla-carousel-react@8.6.0", "", { "dependencies": { "embla-carousel": "8.6.0", "embla-carousel-reactive-utils": "8.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA=="],
|
||||
|
||||
"embla-carousel-reactive-utils": ["embla-carousel-reactive-utils@8.6.0", "", { "peerDependencies": { "embla-carousel": "8.6.0" } }, "sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
||||
|
|
@ -826,6 +958,8 @@
|
|||
|
||||
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
||||
|
||||
"es-toolkit": ["es-toolkit@1.45.1", "", {}, "sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw=="],
|
||||
|
||||
"esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="],
|
||||
|
||||
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||
|
|
@ -856,6 +990,8 @@
|
|||
|
||||
"etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
|
||||
|
||||
"eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="],
|
||||
|
||||
"events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
|
||||
|
||||
"eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="],
|
||||
|
|
@ -924,6 +1060,8 @@
|
|||
|
||||
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||
|
||||
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
|
||||
|
||||
"get-own-enumerable-keys": ["get-own-enumerable-keys@1.0.0", "", {}, "sha512-PKsK2FSrQCyxcGHsGrLDcK0lx+0Ke+6e8KFFozA9/fIQLhQzPaRvJFdcz7+Axg3jUH/Mq+NI4xa5u/UT2tQskA=="],
|
||||
|
||||
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
||||
|
|
@ -932,7 +1070,7 @@
|
|||
|
||||
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
||||
|
||||
"globals": ["globals@17.4.0", "", {}, "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw=="],
|
||||
"globals": ["globals@17.5.0", "", {}, "sha512-qoV+HK2yFl/366t2/Cb3+xxPUo5BuMynomoDmiaZBIdbs+0pYbjfZU+twLhGKp4uCZ/+NbtpVepH5bGCxRyy2g=="],
|
||||
|
||||
"globrex": ["globrex@0.1.2", "", {}, "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="],
|
||||
|
||||
|
|
@ -964,12 +1102,18 @@
|
|||
|
||||
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
||||
|
||||
"immer": ["immer@10.2.0", "", {}, "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw=="],
|
||||
|
||||
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
|
||||
|
||||
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
|
||||
|
||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||
|
||||
"input-otp": ["input-otp@1.4.2", "", { "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA=="],
|
||||
|
||||
"internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="],
|
||||
|
||||
"ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="],
|
||||
|
||||
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
|
||||
|
|
@ -1216,12 +1360,32 @@
|
|||
|
||||
"react": ["react@19.2.5", "", {}, "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA=="],
|
||||
|
||||
"react-day-picker": ["react-day-picker@9.14.0", "", { "dependencies": { "@date-fns/tz": "^1.4.1", "@tabby_ai/hijri-converter": "1.0.5", "date-fns": "^4.1.0", "date-fns-jalali": "4.1.0-0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-tBaoDWjPwe0M5pGrum4H0SR6Lyk+BO9oHnp9JbKpGKW2mlraNPgP9BMfsg5pWpwrssARmeqk7YBl2oXutZTaHA=="],
|
||||
|
||||
"react-dom": ["react-dom@19.2.5", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.5" } }, "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag=="],
|
||||
|
||||
"react-is": ["react-is@19.2.5", "", {}, "sha512-Dn0t8IQhCmeIT3wu+Apm1/YVsJXsGWi6k4sPdnBIdqMVtHtv0IGi6dcpNpNkNac0zB2uUAqNX3MHzN8c+z2rwQ=="],
|
||||
|
||||
"react-redux": ["react-redux@9.2.0", "", { "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "@types/react": "^18.2.25 || ^19", "react": "^18.0 || ^19", "redux": "^5.0.0" }, "optionalPeers": ["@types/react", "redux"] }, "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g=="],
|
||||
|
||||
"react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="],
|
||||
|
||||
"react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="],
|
||||
|
||||
"react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="],
|
||||
|
||||
"react-resizable-panels": ["react-resizable-panels@4.10.0", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-frjewRQt7TCv/vCH1pJfjZ7RxAhr5pKuqVQtVgzFq/vherxBFOWyC3xMbryx5Ti2wylViGUFc93Etg4rB3E0UA=="],
|
||||
|
||||
"react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="],
|
||||
|
||||
"recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="],
|
||||
|
||||
"recharts": ["recharts@3.8.0", "", { "dependencies": { "@reduxjs/toolkit": "^1.9.0 || 2.x.x", "clsx": "^2.1.1", "decimal.js-light": "^2.5.1", "es-toolkit": "^1.39.3", "eventemitter3": "^5.0.1", "immer": "^10.1.1", "react-redux": "8.x.x || 9.x.x", "reselect": "5.1.1", "tiny-invariant": "^1.3.3", "use-sync-external-store": "^1.2.2", "victory-vendor": "^37.0.2" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Z/m38DX3L73ExO4Tpc9/iZWHmHnlzWG4njQbxsF5aSjwqmHNDDIm0rdEBArkwsBvR8U6EirlEHiQNYWCVh9sGQ=="],
|
||||
|
||||
"redux": ["redux@5.0.1", "", {}, "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="],
|
||||
|
||||
"redux-thunk": ["redux-thunk@3.1.0", "", { "peerDependencies": { "redux": "^5.0.0" } }, "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw=="],
|
||||
|
||||
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
|
||||
|
||||
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
||||
|
|
@ -1378,6 +1542,10 @@
|
|||
|
||||
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||
|
||||
"use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
|
||||
|
||||
"use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="],
|
||||
|
||||
"use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="],
|
||||
|
||||
"usehooks-ts": ["usehooks-ts@3.1.1", "", { "dependencies": { "lodash.debounce": "^4.0.8" }, "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA=="],
|
||||
|
|
@ -1388,6 +1556,10 @@
|
|||
|
||||
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
||||
|
||||
"vaul": ["vaul@1.1.2", "", { "dependencies": { "@radix-ui/react-dialog": "^1.1.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA=="],
|
||||
|
||||
"victory-vendor": ["victory-vendor@37.3.6", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ=="],
|
||||
|
||||
"vite": ["vite@7.3.2", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg=="],
|
||||
|
||||
"vite-tsconfig-paths": ["vite-tsconfig-paths@6.1.1", "", { "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", "tsconfck": "^3.0.3" }, "peerDependencies": { "vite": "*" } }, "sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg=="],
|
||||
|
|
@ -1452,6 +1624,18 @@
|
|||
|
||||
"@modelcontextprotocol/sdk/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="],
|
||||
|
||||
"@radix-ui/react-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
|
||||
|
||||
"@radix-ui/react-dismissable-layer/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
|
||||
|
||||
"@radix-ui/react-focus-scope/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
|
||||
|
||||
"@radix-ui/react-portal/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
|
||||
|
||||
"@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="],
|
||||
|
||||
"@reduxjs/toolkit/immer": ["immer@11.1.4", "", {}, "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="],
|
||||
|
|
@ -1468,6 +1652,10 @@
|
|||
|
||||
"@tensamin/tauri/lucide-react": ["lucide-react@1.8.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-WuvlsjngSk7TnTBJ1hsCy3ql9V9VOdcPkd3PKcSmM34vJD8KG6molxz7m7zbYFgICwsanQWmJ13JlYs4Zp7Arw=="],
|
||||
|
||||
"@tensamin/tauth/lucide-react": ["lucide-react@1.8.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-WuvlsjngSk7TnTBJ1hsCy3ql9V9VOdcPkd3PKcSmM34vJD8KG6molxz7m7zbYFgICwsanQWmJ13JlYs4Zp7Arw=="],
|
||||
|
||||
"@tensamin/tauth/sonner": ["sonner@2.0.7", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w=="],
|
||||
|
||||
"@tensamin/ui/lucide-react": ["lucide-react@1.8.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-WuvlsjngSk7TnTBJ1hsCy3ql9V9VOdcPkd3PKcSmM34vJD8KG6molxz7m7zbYFgICwsanQWmJ13JlYs4Zp7Arw=="],
|
||||
|
||||
"@tensamin/ui/shadcn": ["shadcn@3.8.5", "", { "dependencies": { "@antfu/ni": "^25.0.0", "@babel/core": "^7.28.0", "@babel/parser": "^7.28.0", "@babel/plugin-transform-typescript": "^7.28.0", "@babel/preset-typescript": "^7.27.1", "@dotenvx/dotenvx": "^1.48.4", "@modelcontextprotocol/sdk": "^1.26.0", "@types/validate-npm-package-name": "^4.0.2", "browserslist": "^4.26.2", "commander": "^14.0.0", "cosmiconfig": "^9.0.0", "dedent": "^1.6.0", "deepmerge": "^4.3.1", "diff": "^8.0.2", "execa": "^9.6.0", "fast-glob": "^3.3.3", "fs-extra": "^11.3.1", "fuzzysort": "^3.1.0", "https-proxy-agent": "^7.0.6", "kleur": "^4.1.5", "msw": "^2.10.4", "node-fetch": "^3.3.2", "open": "^11.0.0", "ora": "^8.2.0", "postcss": "^8.5.6", "postcss-selector-parser": "^7.1.0", "prompts": "^2.4.2", "recast": "^0.23.11", "stringify-object": "^5.0.0", "tailwind-merge": "^3.0.1", "ts-morph": "^26.0.0", "tsconfig-paths": "^4.2.0", "validate-npm-package-name": "^7.0.1", "zod": "^3.24.1", "zod-to-json-schema": "^3.24.6" }, "bin": { "shadcn": "dist/index.js" } }, "sha512-jPRx44e+eyeV7xwY3BLJXcfrks00+M0h5BGB9l6DdcBW4BpAj4x3lVmVy0TXPEs2iHEisxejr62sZAAw6B1EVA=="],
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
"build:packages": "bun scripts/build-packages.ts",
|
||||
"dev:web": "cd apps/web && bun dev",
|
||||
"build:web": "cd apps/web && bun run build",
|
||||
"preview:web": "cd apps/web && bun run preview",
|
||||
"dev:mobile": "cd apps/tauri && bun dev:mobile",
|
||||
"build:mobile": "cd apps/tauri && bun run build:mobile",
|
||||
"dev:desktop": "cd apps/tauri && bun dev:desktop",
|
||||
|
|
@ -33,9 +34,11 @@
|
|||
"typescript-eslint": "^8.58.1"
|
||||
},
|
||||
"overrides": {
|
||||
"@tensamin/ui": "https://git.methanium.net/tensamin/ui/archive/0.0.20.tar.gz"
|
||||
"@tensamin/ui": "https://git.methanium.net/tensamin/ui/archive/0.0.25.tar.gz",
|
||||
"@tensamin/ttp-core": "https://git.methanium.net/tensamin/ttp/archive/0.0.3.tar.gz"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tensamin/ui": "*"
|
||||
"@tensamin/ui": "*",
|
||||
"@tensamin/ttp-core": "*"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { createContext, useContext, useEffect, useState } from "react";
|
||||
import { log, toast } from "@tensamin/shared/log";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
import { useTTP } from "@tensamin/ttp/context";
|
||||
import { useTTP } from "@tensamin/ttp";
|
||||
import z from "zod";
|
||||
import { ttp } from "@tensamin/shared/data";
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { Button } from "@tensamin/ui";
|
|||
|
||||
import { Plus, Laugh, Clapperboard } from "lucide-react";
|
||||
import { useChat } from "../context";
|
||||
import { useTTP } from "@tensamin/ttp/context";
|
||||
import { useTTP } from "@tensamin/ttp";
|
||||
import { useCrypto } from "@tensamin/crypto/context";
|
||||
import { log, toast } from "@tensamin/shared/log";
|
||||
import Message from "./message";
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import type { LiveMessage, RawMessage, RawMessages } from "./values";
|
|||
import { useCrypto } from "@tensamin/crypto/context";
|
||||
import { useUser } from "@tensamin/user/context";
|
||||
import { useStorage } from "@tensamin/storage/context";
|
||||
import { useTTP } from "@tensamin/ttp/context";
|
||||
import { useTTP } from "@tensamin/ttp";
|
||||
import { log } from "@tensamin/shared/log";
|
||||
|
||||
export const context = createContext<contextType | undefined>(undefined);
|
||||
|
|
@ -54,7 +54,7 @@ function updateMessageStateByTimestamp<
|
|||
* @param props Parameter props.
|
||||
* @returns unknown.
|
||||
*/
|
||||
export default function Provider(props: { children: ReactNode }) {
|
||||
export function Provider(props: { children: ReactNode }) {
|
||||
const { getSharedSecret } = useCrypto();
|
||||
const { get } = useUser();
|
||||
const { load } = useStorage();
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
"build": "tsc -p tsconfig.json --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tensamin/ui": "*",
|
||||
"lucide-react": "^0.564.0",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
|
|
|
|||
|
|
@ -155,6 +155,23 @@ export const ttp = {
|
|||
}),
|
||||
},
|
||||
|
||||
authenticate_app: {
|
||||
request: z.object({
|
||||
app_identifier: z.string(),
|
||||
}),
|
||||
response: z.object({
|
||||
challenge: z.base64(),
|
||||
}),
|
||||
},
|
||||
get_app: {
|
||||
request: z.object({
|
||||
app_identifier: z.string(),
|
||||
}),
|
||||
response: z.object({
|
||||
app_public_key: z.base64(),
|
||||
}),
|
||||
},
|
||||
|
||||
// Calls
|
||||
get_call_data: {
|
||||
request: z.object({
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { toast as sonnerToast } from "sonner";
|
||||
import { toast as sonnerToast } from "@tensamin/ui";
|
||||
import { Ban, Check, Info, TriangleAlert } from "lucide-react";
|
||||
|
||||
/**
|
||||
|
|
|
|||
29
packages/tauth/package.json
Normal file
29
packages/tauth/package.json
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "@tensamin/tauth",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"./context": "./src/context.tsx"
|
||||
},
|
||||
"scripts": {
|
||||
"format": "bunx prettier --write .",
|
||||
"lint": "eslint src",
|
||||
"build": "tsc -p tsconfig.json --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/react-router": "^1.0.0",
|
||||
"@tensamin/crypto": "workspace:*",
|
||||
"@tensamin/shared": "workspace:*",
|
||||
"@tensamin/storage": "workspace:*",
|
||||
"@tensamin/tauri": "workspace:*",
|
||||
"@tensamin/ttp": "workspace:*",
|
||||
"@tensamin/ui": "*",
|
||||
"@tensamin/user": "workspace:*",
|
||||
"lucide-react": "^1.8.0",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"sonner": "^2.0.7",
|
||||
"zod": "^4.3.6"
|
||||
}
|
||||
}
|
||||
160
packages/tauth/src/context.tsx
Normal file
160
packages/tauth/src/context.tsx
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
import { useEffect, useState, type ReactNode } from "react";
|
||||
import { useCrypto } from "@tensamin/crypto/context";
|
||||
import { useUser } from "@tensamin/user/context";
|
||||
import { useStorage } from "@tensamin/storage/context";
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@tensamin/ui";
|
||||
import { useLocation } from "@tanstack/react-router";
|
||||
import { useDeeplinks } from "@tensamin/tauri/deeplinkHandler";
|
||||
import { useTTP } from "@tensamin/ttp";
|
||||
import { log, toast } from "@tensamin/shared/log";
|
||||
import { Loader2 } from "lucide-react";
|
||||
|
||||
export default function Wrapper({ children }: { children: ReactNode }) {
|
||||
const { getSharedSecret } = useCrypto();
|
||||
const { get } = useUser();
|
||||
const { load } = useStorage();
|
||||
const { send } = useTTP();
|
||||
const { decrypt } = useCrypto();
|
||||
const { searchStr } = useLocation();
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const [identifier, setIdentifier] = useState<string | null>(null);
|
||||
const [redirect, setRedirect] = useState<string | null>(null);
|
||||
|
||||
const { deeplinks } = useDeeplinks();
|
||||
|
||||
const authorizeApp = async () => {
|
||||
if (!identifier || !redirect) return;
|
||||
|
||||
try {
|
||||
// Get Data
|
||||
const user = await get(await load("user_id"));
|
||||
const app = await send("get_app", {
|
||||
app_identifier: identifier,
|
||||
});
|
||||
const sharedSecret = await getSharedSecret(
|
||||
await load("private_key"),
|
||||
user.public_key,
|
||||
app.data.app_public_key,
|
||||
);
|
||||
|
||||
// Get Challenge
|
||||
const res = await send("authenticate_app", {
|
||||
app_identifier: identifier,
|
||||
});
|
||||
const challenge = res.data.challenge;
|
||||
|
||||
// Solve Challenge
|
||||
const solvedChallenge = await decrypt(sharedSecret, challenge);
|
||||
|
||||
// Craft Redirect URL
|
||||
const finalUrl = new URL(redirect);
|
||||
finalUrl.searchParams.set("challenge", solvedChallenge);
|
||||
|
||||
const session = crypto.randomUUID();
|
||||
finalUrl.searchParams.set("session", session);
|
||||
|
||||
// Open Redirect URL
|
||||
window.open(finalUrl.toString(), "_blank");
|
||||
} catch (err) {
|
||||
toast("error", "Failed to authorize app");
|
||||
log(1, "tauth", "red", "Failed to authorize app", err);
|
||||
} finally {
|
||||
setDialogOpen(false);
|
||||
setLoading(false);
|
||||
setIdentifier(null);
|
||||
setRedirect(null);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams(searchStr);
|
||||
const identifier = params.get("identifier");
|
||||
const redirect = params.get("redirect");
|
||||
if (!identifier || !redirect) return;
|
||||
|
||||
// @eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
setIdentifier(identifier);
|
||||
setRedirect(redirect);
|
||||
setDialogOpen(true);
|
||||
}, [searchStr]);
|
||||
|
||||
useEffect(() => {
|
||||
deeplinks.forEach((link) => {
|
||||
if (link.startsWith("tensamin://authorize_app")) {
|
||||
const url = new URL(link);
|
||||
const identifier = url.searchParams.get("identifier");
|
||||
const redirect = url.searchParams.get("redirect");
|
||||
|
||||
if (identifier && redirect) {
|
||||
setIdentifier(identifier);
|
||||
setRedirect(redirect);
|
||||
setDialogOpen(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [deeplinks]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
open={dialogOpen}
|
||||
onOpenChange={(value) => {
|
||||
setDialogOpen(value);
|
||||
if (!value) {
|
||||
setIdentifier(null);
|
||||
setRedirect(null);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Allow this app to access your data?</DialogTitle>
|
||||
<DialogDescription>
|
||||
This app is requesting access to your data. Please review the
|
||||
permissions and only grant access if you trust this app.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
setLoading(true);
|
||||
authorizeApp();
|
||||
}}
|
||||
>
|
||||
{loading ? (
|
||||
<>
|
||||
<Loader2 className="animate-spin" />
|
||||
Authorizing...
|
||||
</>
|
||||
) : (
|
||||
"Allow"
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={() => {
|
||||
setDialogOpen(false);
|
||||
setIdentifier(null);
|
||||
setRedirect(null);
|
||||
}}
|
||||
>
|
||||
Deny
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
}
|
||||
12
packages/tauth/tsconfig.json
Normal file
12
packages/tauth/tsconfig.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"jsx": "react-jsx",
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
|
|
@ -4,18 +4,15 @@
|
|||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"./core": "./src/core.ts",
|
||||
"./context": "./src/context.tsx",
|
||||
"./send": "./src/send.ts",
|
||||
"./values": "./src/values.ts"
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"format": "bunx prettier --write .",
|
||||
"lint": "eslint src --ext .ts,.tsx",
|
||||
"test": "bun test",
|
||||
"build": "bun run test && tsc -p tsconfig.json --noEmit"
|
||||
"build": "tsc -p tsconfig.json --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tensamin/ttp-core": "*",
|
||||
"@tanstack/react-router": "^1.0.0",
|
||||
"@tensamin/crypto": "workspace:*",
|
||||
"@tensamin/storage": "workspace:*",
|
||||
|
|
|
|||
11
packages/ttp/src/bun-test.d.ts
vendored
11
packages/ttp/src/bun-test.d.ts
vendored
|
|
@ -1,11 +0,0 @@
|
|||
declare module "bun:test" {
|
||||
export const describe: (...args: unknown[]) => unknown;
|
||||
export const test: (...args: unknown[]) => unknown;
|
||||
export const it: (...args: unknown[]) => unknown;
|
||||
export const expect: (value: unknown) => {
|
||||
toBe: (expected: unknown) => void;
|
||||
toEqual: (expected: unknown) => void;
|
||||
toContain: (expected: unknown) => void;
|
||||
toThrow: (expected?: unknown) => void;
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -11,8 +11,8 @@ import {
|
|||
import { useCrypto } from "@tensamin/crypto/context";
|
||||
import { log } from "@tensamin/shared/log";
|
||||
import { useStorage } from "@tensamin/storage/context";
|
||||
import { createTransportClient, READY_STATE, type BoundSendFn } from "./core";
|
||||
import type { PushHandler } from "./core";
|
||||
import { createTransportClient, READY_STATE, type BoundSendFn } from "@tensamin/ttp-core";
|
||||
import type { PushHandler } from "@tensamin/ttp-core";
|
||||
import {
|
||||
PING_INTERVAL,
|
||||
RECONNECT_RESET,
|
||||
|
|
|
|||
|
|
@ -1,575 +0,0 @@
|
|||
import { describe, it, expect } from "bun:test";
|
||||
import { createTransportClient, type SchemaMap } from "./core";
|
||||
import {
|
||||
decodeCommunicationMessage,
|
||||
encodeCommunicationMessage,
|
||||
type TypedMessage,
|
||||
} from "./codec";
|
||||
import { z } from "zod";
|
||||
|
||||
type MockStreamWriter = {
|
||||
write: (chunk: Uint8Array) => Promise<void>;
|
||||
releaseLock: () => void;
|
||||
close: () => Promise<void>;
|
||||
};
|
||||
|
||||
type MockStream = {
|
||||
getWriter: () => MockStreamWriter;
|
||||
};
|
||||
|
||||
type MockTransportInstance = {
|
||||
ready: Promise<void>;
|
||||
closed: Promise<void>;
|
||||
createUnidirectionalStream: () => Promise<MockStream>;
|
||||
incomingUnidirectionalStreams: ReadableStream<ReadableStream<Uint8Array>>;
|
||||
close: () => void;
|
||||
};
|
||||
|
||||
type MemoryStorage = {
|
||||
getItem: (key: string) => string | null;
|
||||
setItem: (key: string, value: string) => void;
|
||||
removeItem: (key: string) => void;
|
||||
clear: () => void;
|
||||
};
|
||||
|
||||
function ensureLocalStorage() {
|
||||
const globalWithStorage = globalThis as unknown as {
|
||||
localStorage?: MemoryStorage;
|
||||
};
|
||||
|
||||
if (globalWithStorage.localStorage) {
|
||||
return;
|
||||
}
|
||||
|
||||
const storage = new Map<string, string>();
|
||||
globalWithStorage.localStorage = {
|
||||
getItem: (key) => storage.get(key) ?? null,
|
||||
setItem: (key, value) => {
|
||||
storage.set(key, value);
|
||||
},
|
||||
removeItem: (key) => {
|
||||
storage.delete(key);
|
||||
},
|
||||
clear: () => {
|
||||
storage.clear();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createMockWebTransport() {
|
||||
ensureLocalStorage();
|
||||
|
||||
let readyResolve!: () => void;
|
||||
let closedResolve!: () => void;
|
||||
let incomingController: ReadableStreamDefaultController<
|
||||
ReadableStream<Uint8Array>
|
||||
> | null = null;
|
||||
const outgoingMessages: TypedMessage[] = [];
|
||||
|
||||
const ready = new Promise<void>((resolve) => {
|
||||
readyResolve = resolve;
|
||||
});
|
||||
const closed = new Promise<void>((resolve) => {
|
||||
closedResolve = resolve;
|
||||
});
|
||||
|
||||
const writer: MockStreamWriter = {
|
||||
write: async () => {},
|
||||
releaseLock: () => {},
|
||||
close: async () => {},
|
||||
};
|
||||
|
||||
const stream: MockStream = {
|
||||
getWriter: () => writer,
|
||||
};
|
||||
|
||||
const incomingUnidirectionalStreams = new ReadableStream<
|
||||
ReadableStream<Uint8Array>
|
||||
>({
|
||||
start(controller) {
|
||||
incomingController = controller;
|
||||
},
|
||||
});
|
||||
|
||||
const transport: MockTransportInstance = {
|
||||
ready,
|
||||
closed,
|
||||
createUnidirectionalStream: async () => stream,
|
||||
incomingUnidirectionalStreams,
|
||||
close: () => {},
|
||||
};
|
||||
|
||||
class MockWebTransport implements MockTransportInstance {
|
||||
ready = transport.ready;
|
||||
closed = transport.closed;
|
||||
createUnidirectionalStream = transport.createUnidirectionalStream;
|
||||
incomingUnidirectionalStreams = transport.incomingUnidirectionalStreams;
|
||||
close = transport.close;
|
||||
}
|
||||
|
||||
const pushIncomingMessage = (message: TypedMessage) => {
|
||||
if (!incomingController) {
|
||||
throw new Error("Incoming stream controller is not ready");
|
||||
}
|
||||
|
||||
const frame = wrapForDecode(encodeCommunicationMessage(message));
|
||||
const incomingStream = new ReadableStream<Uint8Array>({
|
||||
start(controller) {
|
||||
controller.enqueue(frame);
|
||||
controller.close();
|
||||
},
|
||||
});
|
||||
|
||||
incomingController.enqueue(incomingStream);
|
||||
};
|
||||
|
||||
writer.write = async (chunk: Uint8Array) => {
|
||||
outgoingMessages.push(decodeCommunicationMessage(chunk));
|
||||
};
|
||||
|
||||
return {
|
||||
readyResolve,
|
||||
closedResolve,
|
||||
MockWebTransport,
|
||||
outgoingMessages,
|
||||
pushIncomingMessage,
|
||||
};
|
||||
}
|
||||
|
||||
async function expectRejection(
|
||||
promise: Promise<unknown>,
|
||||
messageSubstring?: string,
|
||||
) {
|
||||
try {
|
||||
await promise;
|
||||
expect(false).toBe(true);
|
||||
} catch (error) {
|
||||
if (messageSubstring) {
|
||||
expect(getErrorMessage(error).includes(messageSubstring)).toBe(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getErrorMessage(error: unknown) {
|
||||
return error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
|
||||
function writeU32BigEndian(buffer: Uint8Array, offset: number, value: number) {
|
||||
new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).setUint32(
|
||||
offset,
|
||||
value,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
function wrapForDecode(body: Uint8Array) {
|
||||
const frame = new Uint8Array(body.byteLength + 4);
|
||||
writeU32BigEndian(frame, 0, body.byteLength);
|
||||
frame.set(body, 4);
|
||||
return frame;
|
||||
}
|
||||
|
||||
describe("Core Protocol", () => {
|
||||
describe("encodeCommunicationMessage", () => {
|
||||
it("encodes message with id", () => {
|
||||
const message: TypedMessage = {
|
||||
id: 123,
|
||||
type: "ping",
|
||||
data: {},
|
||||
};
|
||||
|
||||
const encoded = encodeCommunicationMessage(message);
|
||||
|
||||
expect(encoded instanceof Uint8Array).toBe(true);
|
||||
expect(encoded.byteLength > 0).toBe(true);
|
||||
});
|
||||
|
||||
it("encodes message without id", () => {
|
||||
const message: TypedMessage = {
|
||||
id: 0,
|
||||
type: "pong",
|
||||
data: {},
|
||||
};
|
||||
|
||||
const encoded = encodeCommunicationMessage(message);
|
||||
expect(encoded instanceof Uint8Array).toBe(true);
|
||||
});
|
||||
|
||||
it("encodes message with string data", () => {
|
||||
const message: TypedMessage = {
|
||||
id: 1,
|
||||
type: "message",
|
||||
data: { content: "hello", sender_id: 42 },
|
||||
};
|
||||
|
||||
const encoded = encodeCommunicationMessage(message);
|
||||
const decoded = decodeCommunicationMessage(wrapForDecode(encoded));
|
||||
|
||||
expect(decoded.type).toBe("message");
|
||||
expect(decoded.id).toBe(1);
|
||||
});
|
||||
|
||||
it("throws on unknown message type", () => {
|
||||
const message: TypedMessage = {
|
||||
id: 1,
|
||||
type: "unknown_type",
|
||||
data: {},
|
||||
};
|
||||
|
||||
expect(() => encodeCommunicationMessage(message)).toThrow(
|
||||
"Unknown communication type",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("decodeCommunicationMessage", () => {
|
||||
it("decodes encoded message", () => {
|
||||
const original: TypedMessage = {
|
||||
id: 456,
|
||||
type: "success",
|
||||
data: {},
|
||||
};
|
||||
|
||||
const encoded = encodeCommunicationMessage(original);
|
||||
const decoded = decodeCommunicationMessage(wrapForDecode(encoded));
|
||||
|
||||
expect(decoded.id).toBe(456);
|
||||
expect(decoded.type).toBe("success");
|
||||
});
|
||||
|
||||
it("throws on truncated frame", () => {
|
||||
const truncated = new Uint8Array([0x00, 0x00, 0x00]);
|
||||
expect(() => decodeCommunicationMessage(truncated)).toThrow();
|
||||
});
|
||||
|
||||
it("throws on frame length mismatch", () => {
|
||||
const buffer = new Uint8Array(10);
|
||||
buffer[0] = 0xff;
|
||||
buffer[1] = 0xff;
|
||||
buffer[2] = 0xff;
|
||||
buffer[3] = 0xff;
|
||||
|
||||
expect(() => decodeCommunicationMessage(buffer)).toThrow(
|
||||
"Communication frame length mismatch",
|
||||
);
|
||||
});
|
||||
|
||||
it("decodes error messages with empty data", () => {
|
||||
const original: TypedMessage = {
|
||||
id: 1,
|
||||
type: "error",
|
||||
data: {},
|
||||
};
|
||||
|
||||
const encoded = encodeCommunicationMessage(original);
|
||||
const decoded = decodeCommunicationMessage(wrapForDecode(encoded));
|
||||
|
||||
expect(decoded.type).toBe("error");
|
||||
});
|
||||
});
|
||||
|
||||
describe("createTransportClient", () => {
|
||||
it("creates client with schemas", () => {
|
||||
const { MockWebTransport } = createMockWebTransport();
|
||||
const globalWithWebTransport = globalThis as unknown as {
|
||||
WebTransport?: new (url: string) => MockTransportInstance;
|
||||
};
|
||||
globalWithWebTransport.WebTransport = MockWebTransport;
|
||||
|
||||
const schemas: SchemaMap = {
|
||||
ping: {
|
||||
request: z.object({}),
|
||||
response: z.object({}),
|
||||
},
|
||||
};
|
||||
|
||||
const client = createTransportClient(schemas);
|
||||
|
||||
expect(client !== undefined).toBe(true);
|
||||
expect(typeof client.readyState === "function").toBe(true);
|
||||
expect(typeof client.connect === "function").toBe(true);
|
||||
expect(typeof client.send === "function").toBe(true);
|
||||
expect(typeof client.close === "function").toBe(true);
|
||||
expect(typeof client.subscribePush === "function").toBe(true);
|
||||
});
|
||||
|
||||
it("returns CLOSED ready state initially", () => {
|
||||
const client = createTransportClient({});
|
||||
expect(client.readyState()).toBe(3); // CLOSED
|
||||
});
|
||||
|
||||
it("rejects send when not connected", async () => {
|
||||
const { MockWebTransport } = createMockWebTransport();
|
||||
const globalWithWebTransport = globalThis as unknown as {
|
||||
WebTransport?: new (url: string) => MockTransportInstance;
|
||||
};
|
||||
globalWithWebTransport.WebTransport = MockWebTransport;
|
||||
|
||||
const schemas: SchemaMap = {
|
||||
ping: {
|
||||
request: z.object({}),
|
||||
response: z.object({}),
|
||||
},
|
||||
};
|
||||
|
||||
const client = createTransportClient(schemas);
|
||||
|
||||
await expectRejection(
|
||||
client.send("ping", {}),
|
||||
"Transport is not connected",
|
||||
);
|
||||
});
|
||||
|
||||
it("calls readyStateChange callback", async () => {
|
||||
const { readyResolve, MockWebTransport } = createMockWebTransport();
|
||||
const globalWithWebTransport = globalThis as unknown as {
|
||||
WebTransport?: new (url: string) => MockTransportInstance;
|
||||
};
|
||||
globalWithWebTransport.WebTransport = MockWebTransport;
|
||||
|
||||
const readyStateChanges: number[] = [];
|
||||
const client = createTransportClient(
|
||||
{},
|
||||
{
|
||||
onReadyStateChange: (state) => readyStateChanges.push(state),
|
||||
},
|
||||
);
|
||||
|
||||
const connectPromise = client.connect("http://localhost:8000");
|
||||
readyResolve();
|
||||
await connectPromise;
|
||||
|
||||
expect(readyStateChanges).toContain(0); // CONNECTING
|
||||
expect(readyStateChanges).toContain(1); // OPEN
|
||||
});
|
||||
|
||||
it("calls close callback on intentional close", async () => {
|
||||
const { readyResolve, closedResolve, MockWebTransport } =
|
||||
createMockWebTransport();
|
||||
const globalWithWebTransport = globalThis as unknown as {
|
||||
WebTransport?: new (url: string) => MockTransportInstance;
|
||||
};
|
||||
globalWithWebTransport.WebTransport = MockWebTransport;
|
||||
|
||||
const closeEvents: Array<{ intentional: boolean; error?: unknown }> = [];
|
||||
const client = createTransportClient(
|
||||
{},
|
||||
{
|
||||
onClose: (event) => closeEvents.push(event),
|
||||
},
|
||||
);
|
||||
|
||||
const connectPromise = client.connect("http://localhost:8000");
|
||||
readyResolve();
|
||||
await connectPromise;
|
||||
|
||||
const closePromise = client.close();
|
||||
closedResolve();
|
||||
await closePromise;
|
||||
|
||||
expect(closeEvents.length > 0).toBe(true);
|
||||
expect(closeEvents[closeEvents.length - 1].intentional).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects pending requests on close", async () => {
|
||||
const { readyResolve, closedResolve, MockWebTransport } =
|
||||
createMockWebTransport();
|
||||
const globalWithWebTransport = globalThis as unknown as {
|
||||
WebTransport?: new (url: string) => MockTransportInstance;
|
||||
};
|
||||
globalWithWebTransport.WebTransport = MockWebTransport;
|
||||
|
||||
const schemas: SchemaMap = {
|
||||
ping: {
|
||||
request: z.object({}),
|
||||
response: z.object({}),
|
||||
},
|
||||
};
|
||||
|
||||
const client = createTransportClient(schemas);
|
||||
const connectPromise = client.connect("http://localhost:8000");
|
||||
readyResolve();
|
||||
await connectPromise;
|
||||
|
||||
const sendPromise = client.send("ping", {});
|
||||
const closePromise = client.close();
|
||||
closedResolve();
|
||||
await closePromise;
|
||||
|
||||
await expectRejection(sendPromise);
|
||||
});
|
||||
|
||||
it("queues sends until the active request receives a response", async () => {
|
||||
const {
|
||||
readyResolve,
|
||||
pushIncomingMessage,
|
||||
outgoingMessages,
|
||||
MockWebTransport,
|
||||
} = createMockWebTransport();
|
||||
const globalWithWebTransport = globalThis as unknown as {
|
||||
WebTransport?: new (url: string) => MockTransportInstance;
|
||||
};
|
||||
globalWithWebTransport.WebTransport = MockWebTransport;
|
||||
|
||||
const schemas: SchemaMap = {
|
||||
ping: {
|
||||
request: z.object({}),
|
||||
response: z.object({}),
|
||||
},
|
||||
};
|
||||
|
||||
const client = createTransportClient(schemas);
|
||||
const connectPromise = client.connect("http://localhost:8000");
|
||||
readyResolve();
|
||||
await connectPromise;
|
||||
|
||||
const firstSend = client.send("ping", {});
|
||||
const secondSend = client.send("ping", {});
|
||||
|
||||
for (
|
||||
let attempt = 0;
|
||||
attempt < 10 && outgoingMessages.length === 0;
|
||||
attempt += 1
|
||||
) {
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
expect(outgoingMessages.length).toBe(1);
|
||||
|
||||
pushIncomingMessage({
|
||||
id: outgoingMessages[0].id,
|
||||
type: "ping",
|
||||
data: {},
|
||||
});
|
||||
|
||||
await firstSend;
|
||||
for (
|
||||
let attempt = 0;
|
||||
attempt < 10 && outgoingMessages.length === 1;
|
||||
attempt += 1
|
||||
) {
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
expect(outgoingMessages.length).toBe(2);
|
||||
|
||||
pushIncomingMessage({
|
||||
id: outgoingMessages[1].id,
|
||||
type: "ping",
|
||||
data: {},
|
||||
});
|
||||
|
||||
await secondSend;
|
||||
});
|
||||
});
|
||||
|
||||
describe("Push subscriptions", () => {
|
||||
it("subscribes and unsubscribes from push events", () => {
|
||||
const client = createTransportClient({});
|
||||
|
||||
const handler = () => {};
|
||||
const unsubscribe = client.subscribePush(handler);
|
||||
|
||||
expect(typeof unsubscribe).toBe("function");
|
||||
unsubscribe();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Data type encoding", () => {
|
||||
it("encodes boolean true", () => {
|
||||
const message: TypedMessage = {
|
||||
id: 1,
|
||||
type: "message",
|
||||
data: { signed: true },
|
||||
};
|
||||
|
||||
const encoded = encodeCommunicationMessage(message);
|
||||
const decoded = decodeCommunicationMessage(wrapForDecode(encoded));
|
||||
|
||||
expect(decoded.data.signed).toBe(true);
|
||||
});
|
||||
|
||||
it("encodes boolean false", () => {
|
||||
const message: TypedMessage = {
|
||||
id: 1,
|
||||
type: "message",
|
||||
data: { signed: false },
|
||||
};
|
||||
|
||||
const encoded = encodeCommunicationMessage(message);
|
||||
const decoded = decodeCommunicationMessage(wrapForDecode(encoded));
|
||||
|
||||
expect(decoded.data.signed).toBe(false);
|
||||
});
|
||||
|
||||
it("encodes numbers", () => {
|
||||
const message: TypedMessage = {
|
||||
id: 1,
|
||||
type: "message",
|
||||
data: { user_id: 42, sender_id: 100 },
|
||||
};
|
||||
|
||||
const encoded = encodeCommunicationMessage(message);
|
||||
const decoded = decodeCommunicationMessage(wrapForDecode(encoded));
|
||||
|
||||
expect(decoded.data.user_id).toBe(42);
|
||||
expect(decoded.data.sender_id).toBe(100);
|
||||
});
|
||||
|
||||
it("encodes strings", () => {
|
||||
const message: TypedMessage = {
|
||||
id: 1,
|
||||
type: "message",
|
||||
data: { content: "test message", username: "alice" },
|
||||
};
|
||||
|
||||
const encoded = encodeCommunicationMessage(message);
|
||||
const decoded = decodeCommunicationMessage(wrapForDecode(encoded));
|
||||
|
||||
expect(decoded.data.content).toBe("test message");
|
||||
expect(decoded.data.username).toBe("alice");
|
||||
});
|
||||
|
||||
it("encodes null values", () => {
|
||||
const message: TypedMessage = {
|
||||
id: 1,
|
||||
type: "message",
|
||||
data: { status: null },
|
||||
};
|
||||
|
||||
const encoded = encodeCommunicationMessage(message);
|
||||
const decoded = decodeCommunicationMessage(wrapForDecode(encoded));
|
||||
|
||||
expect(decoded.data.status).toBe(null);
|
||||
});
|
||||
|
||||
it("encodes arrays of numbers", () => {
|
||||
const message: TypedMessage = {
|
||||
id: 1,
|
||||
type: "message",
|
||||
data: { user_ids: [1, 2, 3] },
|
||||
};
|
||||
|
||||
const encoded = encodeCommunicationMessage(message);
|
||||
const decoded = decodeCommunicationMessage(wrapForDecode(encoded));
|
||||
|
||||
expect(decoded.data.user_ids).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it("encodes nested containers", () => {
|
||||
const message: TypedMessage = {
|
||||
id: 1,
|
||||
type: "message",
|
||||
data: { user: { username: "alice", display: "Alice" } },
|
||||
};
|
||||
|
||||
const encoded = encodeCommunicationMessage(message);
|
||||
const decoded = decodeCommunicationMessage(wrapForDecode(encoded));
|
||||
|
||||
expect(decoded.data.user).toEqual({
|
||||
username: "alice",
|
||||
display: "Alice",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load diff
3
packages/ttp/src/index.ts
Normal file
3
packages/ttp/src/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export * from "@tensamin/ttp-core";
|
||||
export * from "./context";
|
||||
export * from "./values";
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import * as React from "react";
|
||||
import { useTTP } from "@tensamin/ttp/context";
|
||||
import { useTTP } from "@tensamin/ttp";
|
||||
|
||||
import { ttp as schemas } from "@tensamin/shared/data";
|
||||
import type z from "zod";
|
||||
|
|
|
|||
Loading…
Reference in a new issue