mirror of
https://github.com/get-drexa/drive.git
synced 2025-12-01 14:01:40 +00:00
Compare commits
2 Commits
3209ce1cd2
...
feat/keybo
| Author | SHA1 | Date | |
|---|---|---|---|
| 798bea6bf7 | |||
| 152485e56c |
13
.env.sample
13
.env.sample
@@ -1,13 +1,10 @@
|
|||||||
# this is the url to the convex instance (NOT THE DASHBOARD)
|
|
||||||
CONVEX_SELF_HOSTED_URL=
|
CONVEX_SELF_HOSTED_URL=
|
||||||
CONVEX_SELF_HOSTED_ADMIN_KEY=
|
CONVEX_SELF_HOSTED_ADMIN_KEY=
|
||||||
|
|
||||||
# this is the url to the convex instance (NOT THE DASHBOARD)
|
|
||||||
CONVEX_URL=
|
CONVEX_URL=
|
||||||
# this is the convex url for invoking http actions
|
WORKOS_CLIENT_ID=
|
||||||
CONVEX_SITE_URL=
|
WORKOS_CLIENT_SECRET=
|
||||||
|
WORKOS_API_KEY=
|
||||||
|
|
||||||
# this is the url to the convex instance (NOT THE DASHBOARD)
|
|
||||||
BUN_PUBLIC_CONVEX_URL=
|
BUN_PUBLIC_CONVEX_URL=
|
||||||
# this is the convex url for invoking http actions
|
BUN_PUBLIC_WORKOS_CLIENT_ID=
|
||||||
BUN_PUBLIC_CONVEX_SITE_URL=
|
BUN_PUBLIC_WORKOS_REDIRECT_URI=
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ backend: convex
|
|||||||
# Project structure
|
# Project structure
|
||||||
This project uses npm workspaces.
|
This project uses npm workspaces.
|
||||||
- `packages/convex` - convex functions and models
|
- `packages/convex` - convex functions and models
|
||||||
- `apps/drive-web` - frontend dashboard
|
- `packages/web` - frontend dashboard
|
||||||
- `apps/file-proxy` - proxies uploaded files via opaque share tokens
|
|
||||||
- `packages/path` - path utils
|
- `packages/path` - path utils
|
||||||
|
|
||||||
# General Guidelines
|
# General Guidelines
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
# @drexa/cli
|
|
||||||
|
|
||||||
Admin CLI tool for managing Drexa resources.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
From the project root:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun drexa <command> [subcommand] [options]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
### `generate apikey`
|
|
||||||
|
|
||||||
Generate a new API key for authentication.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun drexa generate apikey
|
|
||||||
```
|
|
||||||
|
|
||||||
The command will interactively prompt you for (using Node.js readline):
|
|
||||||
- **Prefix**: A short identifier for the key (e.g., 'proxy', 'admin'). Cannot contain dashes.
|
|
||||||
- **Key byte length**: Length of the key in bytes (default: 32)
|
|
||||||
- **Description**: A description of what this key is for
|
|
||||||
- **Expiration date**: Optional expiration date in YYYY-MM-DD format
|
|
||||||
|
|
||||||
The command will output:
|
|
||||||
- **Unhashed key**: Save this securely - it won't be shown again
|
|
||||||
- **Hashed key**: Store this in your database
|
|
||||||
- **Description**: The description you provided
|
|
||||||
- **Expiration date**: When the key expires (if set)
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
Run the CLI directly:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun run apps/cli/index.ts <command>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
apps/cli/
|
|
||||||
├── index.ts # Main entry point
|
|
||||||
├── prompts.ts # Interactive prompt utilities
|
|
||||||
└── commands/ # Command structure mirrors CLI structure
|
|
||||||
└── generate/
|
|
||||||
├── index.ts # Generate command group
|
|
||||||
└── apikey.ts # API key generation command
|
|
||||||
```
|
|
||||||
|
|
||||||
## Adding New Commands
|
|
||||||
|
|
||||||
1. Create a new directory under `commands/` for command groups
|
|
||||||
2. Create command files following the pattern in `commands/generate/apikey.ts`
|
|
||||||
3. Export commands from an `index.ts` in the command group directory
|
|
||||||
4. Register the command group in the main `index.ts`
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
import { generateApiKey, newPrefix } from "@drexa/auth"
|
|
||||||
import chalk from "chalk"
|
|
||||||
import { Command } from "commander"
|
|
||||||
import {
|
|
||||||
promptNumber,
|
|
||||||
promptOptionalDate,
|
|
||||||
promptText,
|
|
||||||
} from "../../prompts.ts"
|
|
||||||
|
|
||||||
export const apikeyCommand = new Command("apikey")
|
|
||||||
.description("Generate a new API key")
|
|
||||||
.action(async () => {
|
|
||||||
console.log(chalk.bold.blue("\n🔑 Generate API Key\n"))
|
|
||||||
|
|
||||||
// Prompt for all required information
|
|
||||||
const prefixInput = await promptText(
|
|
||||||
"Enter API key prefix (e.g., 'proxy', 'admin'):",
|
|
||||||
)
|
|
||||||
const prefix = newPrefix(prefixInput)
|
|
||||||
|
|
||||||
if (!prefix) {
|
|
||||||
console.error(
|
|
||||||
chalk.red(
|
|
||||||
'✗ Invalid prefix: cannot contain "-" character. Please use alphanumeric characters only.',
|
|
||||||
),
|
|
||||||
)
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const keyByteLength = await promptNumber("Enter key byte length:", 32)
|
|
||||||
const description = await promptText("Enter description:")
|
|
||||||
const expiresAt = await promptOptionalDate("Enter expiration date")
|
|
||||||
|
|
||||||
console.log(chalk.dim("\n⏳ Generating API key...\n"))
|
|
||||||
|
|
||||||
// Generate the API key
|
|
||||||
const result = await generateApiKey({
|
|
||||||
prefix,
|
|
||||||
keyByteLength,
|
|
||||||
description,
|
|
||||||
expiresAt,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Display results
|
|
||||||
console.log(chalk.green.bold("✓ API Key Generated Successfully!\n"))
|
|
||||||
console.log(chalk.gray("─".repeat(60)))
|
|
||||||
console.log(
|
|
||||||
chalk.yellow.bold(
|
|
||||||
"\n⚠️ IMPORTANT: Save the unhashed key now. It won't be shown again!\n",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
console.log(chalk.bold("Unhashed Key ") + chalk.dim("(save this):"))
|
|
||||||
console.log(chalk.green(` ${result.unhashedKey}\n`))
|
|
||||||
console.log(chalk.gray("─".repeat(60)))
|
|
||||||
console.log(
|
|
||||||
chalk.bold("\nHashed Key ") + chalk.dim("(store this in your database):"),
|
|
||||||
)
|
|
||||||
console.log(chalk.dim(` ${result.hashedKey}\n`))
|
|
||||||
console.log(chalk.bold("Description:"))
|
|
||||||
console.log(chalk.white(` ${result.description}\n`))
|
|
||||||
|
|
||||||
if (result.expiresAt) {
|
|
||||||
console.log(chalk.bold("Expires At:"))
|
|
||||||
console.log(chalk.yellow(` ${result.expiresAt.toISOString()}\n`))
|
|
||||||
} else {
|
|
||||||
console.log(chalk.bold("Expires At:"))
|
|
||||||
console.log(chalk.dim(" Never\n"))
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(chalk.gray("─".repeat(60)) + "\n")
|
|
||||||
})
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { Command } from "commander"
|
|
||||||
import { apikeyCommand } from "./apikey.ts"
|
|
||||||
|
|
||||||
export const generateCommand = new Command("generate")
|
|
||||||
.description("Generate various resources")
|
|
||||||
.addCommand(apikeyCommand)
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#!/usr/bin/env bun
|
|
||||||
|
|
||||||
import { Command } from "commander"
|
|
||||||
import { generateCommand } from "./commands/generate/index.ts"
|
|
||||||
|
|
||||||
const program = new Command()
|
|
||||||
|
|
||||||
program
|
|
||||||
.name("drexa")
|
|
||||||
.description("Drexa CLI - Admin tools for managing Drexa resources")
|
|
||||||
.version("0.1.0")
|
|
||||||
|
|
||||||
// Register command groups
|
|
||||||
program.addCommand(generateCommand)
|
|
||||||
|
|
||||||
// Parse command line arguments
|
|
||||||
program.parse()
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@drexa/cli",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"private": true,
|
|
||||||
"type": "module",
|
|
||||||
"bin": {
|
|
||||||
"drexa": "./index.ts"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"cli": "bun run index.ts"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@drexa/auth": "workspace:*",
|
|
||||||
"chalk": "^5.3.0",
|
|
||||||
"commander": "^12.1.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/bun": "latest"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"typescript": "^5"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
import * as readline from "node:readline/promises"
|
|
||||||
import chalk from "chalk"
|
|
||||||
|
|
||||||
function createReadlineInterface() {
|
|
||||||
return readline.createInterface({
|
|
||||||
input: process.stdin,
|
|
||||||
output: process.stdout,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function promptText(message: string): Promise<string> {
|
|
||||||
const rl = createReadlineInterface()
|
|
||||||
try {
|
|
||||||
const input = await rl.question(chalk.cyan(`${message} `))
|
|
||||||
|
|
||||||
if (!input || input.trim() === "") {
|
|
||||||
console.error(chalk.red("✗ Input is required"))
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
return input.trim()
|
|
||||||
} finally {
|
|
||||||
rl.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function promptNumber(
|
|
||||||
message: string,
|
|
||||||
defaultValue?: number,
|
|
||||||
): Promise<number> {
|
|
||||||
const rl = createReadlineInterface()
|
|
||||||
try {
|
|
||||||
const defaultStr = defaultValue ? chalk.dim(` (default: ${defaultValue})`) : ""
|
|
||||||
const input = await rl.question(chalk.cyan(`${message}${defaultStr} `))
|
|
||||||
|
|
||||||
if ((!input || input.trim() === "") && defaultValue !== undefined) {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!input || input.trim() === "") {
|
|
||||||
console.error(chalk.red("✗ Input is required"))
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const num = Number.parseInt(input.trim(), 10)
|
|
||||||
if (Number.isNaN(num) || num <= 0) {
|
|
||||||
console.error(chalk.red("✗ Please enter a valid positive number"))
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return num
|
|
||||||
} finally {
|
|
||||||
rl.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function promptOptionalDate(
|
|
||||||
message: string,
|
|
||||||
): Promise<Date | undefined> {
|
|
||||||
const rl = createReadlineInterface()
|
|
||||||
try {
|
|
||||||
const input = await rl.question(
|
|
||||||
chalk.cyan(`${message} `) + chalk.dim("(optional, format: YYYY-MM-DD) "),
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!input || input.trim() === "") {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
const date = new Date(input.trim())
|
|
||||||
if (Number.isNaN(date.getTime())) {
|
|
||||||
console.error(chalk.red("✗ Invalid date format. Please use YYYY-MM-DD"))
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (date < new Date()) {
|
|
||||||
console.error(chalk.red("✗ Expiration date must be in the future"))
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return date
|
|
||||||
} finally {
|
|
||||||
rl.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function promptConfirm(
|
|
||||||
message: string,
|
|
||||||
defaultValue = false,
|
|
||||||
): Promise<boolean> {
|
|
||||||
const rl = createReadlineInterface()
|
|
||||||
try {
|
|
||||||
const defaultStr = defaultValue
|
|
||||||
? chalk.dim(" (Y/n)")
|
|
||||||
: chalk.dim(" (y/N)")
|
|
||||||
const input = await rl.question(chalk.cyan(`${message}${defaultStr} `))
|
|
||||||
|
|
||||||
if (!input || input.trim() === "") {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalized = input.toLowerCase().trim()
|
|
||||||
return normalized === "y" || normalized === "yes"
|
|
||||||
} finally {
|
|
||||||
rl.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
// Environment setup & latest features
|
|
||||||
"lib": ["ESNext"],
|
|
||||||
"target": "ESNext",
|
|
||||||
"module": "Preserve",
|
|
||||||
"moduleDetection": "force",
|
|
||||||
"allowJs": true,
|
|
||||||
|
|
||||||
// Bundler mode
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowImportingTsExtensions": true,
|
|
||||||
"verbatimModuleSyntax": true,
|
|
||||||
"noEmit": true,
|
|
||||||
|
|
||||||
// Best practices
|
|
||||||
"strict": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"noUncheckedIndexedAccess": true,
|
|
||||||
"noImplicitOverride": true,
|
|
||||||
|
|
||||||
// Some stricter flags (disabled by default)
|
|
||||||
"noUnusedLocals": false,
|
|
||||||
"noUnusedParameters": false,
|
|
||||||
"noPropertyAccessFromIndexSignature": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
# this is the url to the convex instance (NOT THE DASHBOARD)
|
|
||||||
VITE_CONVEX_URL=
|
|
||||||
# this is the convex url for invoking http actions
|
|
||||||
VITE_CONVEX_SITE_URL=
|
|
||||||
# this is the url to the file proxy
|
|
||||||
FILE_PROXY_URL=
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import {
|
|
||||||
convexClient,
|
|
||||||
crossDomainClient,
|
|
||||||
} from "@convex-dev/better-auth/client/plugins"
|
|
||||||
import { createAuthClient } from "better-auth/react"
|
|
||||||
import { createContext, useContext } from "react"
|
|
||||||
|
|
||||||
export type AuthErrorCode = keyof typeof authClient.$ERROR_CODES
|
|
||||||
|
|
||||||
export class BetterAuthError extends Error {
|
|
||||||
constructor(public readonly errorCode: AuthErrorCode) {
|
|
||||||
super(`better-auth error: ${errorCode}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const authClient = createAuthClient({
|
|
||||||
baseURL: import.meta.env.VITE_CONVEX_SITE_URL,
|
|
||||||
plugins: [convexClient(), crossDomainClient()],
|
|
||||||
})
|
|
||||||
|
|
||||||
export type Session = NonNullable<
|
|
||||||
Awaited<ReturnType<typeof authClient.useSession>>["data"]
|
|
||||||
>
|
|
||||||
|
|
||||||
export const SessionContext = createContext<Session | null>(null)
|
|
||||||
|
|
||||||
export function useSession() {
|
|
||||||
const context = useContext(SessionContext)
|
|
||||||
if (!context) {
|
|
||||||
throw new Error("useSession must be used within a SessionProvider")
|
|
||||||
}
|
|
||||||
return context
|
|
||||||
}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
import { Slot } from "@radix-ui/react-slot"
|
|
||||||
import { ChevronRight, MoreHorizontal } from "lucide-react"
|
|
||||||
import type * as React from "react"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
|
|
||||||
function Breadcrumb({ ...props }: React.ComponentProps<"nav">) {
|
|
||||||
return <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} />
|
|
||||||
}
|
|
||||||
|
|
||||||
function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
|
|
||||||
return (
|
|
||||||
<ol
|
|
||||||
data-slot="breadcrumb-list"
|
|
||||||
className={cn(
|
|
||||||
"text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
|
|
||||||
return (
|
|
||||||
<li
|
|
||||||
data-slot="breadcrumb-item"
|
|
||||||
className={cn("inline-flex items-center gap-1.5", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function BreadcrumbLink({
|
|
||||||
asChild,
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<"a"> & {
|
|
||||||
asChild?: boolean
|
|
||||||
}) {
|
|
||||||
const Comp = asChild ? Slot : "a"
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Comp
|
|
||||||
data-slot="breadcrumb-link"
|
|
||||||
className={cn("hover:text-foreground transition-colors", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
data-slot="breadcrumb-page"
|
|
||||||
role="link"
|
|
||||||
aria-disabled="true"
|
|
||||||
aria-current="page"
|
|
||||||
className={cn("text-foreground font-normal", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function BreadcrumbSeparator({
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<"li">) {
|
|
||||||
return (
|
|
||||||
<li
|
|
||||||
data-slot="breadcrumb-separator"
|
|
||||||
role="presentation"
|
|
||||||
aria-hidden="true"
|
|
||||||
className={cn("[&>svg]:size-3.5", className)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{children ?? <ChevronRight />}
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function BreadcrumbEllipsis({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<"span">) {
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
data-slot="breadcrumb-ellipsis"
|
|
||||||
role="presentation"
|
|
||||||
aria-hidden="true"
|
|
||||||
className={cn("flex size-9 items-center justify-center", className)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<MoreHorizontal className="size-4" />
|
|
||||||
<span className="sr-only">More</span>
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
Breadcrumb,
|
|
||||||
BreadcrumbList,
|
|
||||||
BreadcrumbItem,
|
|
||||||
BreadcrumbLink,
|
|
||||||
BreadcrumbPage,
|
|
||||||
BreadcrumbSeparator,
|
|
||||||
BreadcrumbEllipsis,
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
import * as React from "react"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
|
|
||||||
function Card({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="card"
|
|
||||||
className={cn(
|
|
||||||
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="card-header"
|
|
||||||
className={cn(
|
|
||||||
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="card-title"
|
|
||||||
className={cn("leading-none font-semibold", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="card-description"
|
|
||||||
className={cn("text-muted-foreground text-sm", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="card-action"
|
|
||||||
className={cn(
|
|
||||||
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="card-content"
|
|
||||||
className={cn("px-6", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="card-footer"
|
|
||||||
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
Card,
|
|
||||||
CardHeader,
|
|
||||||
CardFooter,
|
|
||||||
CardTitle,
|
|
||||||
CardAction,
|
|
||||||
CardDescription,
|
|
||||||
CardContent,
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
|
|
||||||
import { CheckIcon } from "lucide-react"
|
|
||||||
import type * as React from "react"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
|
|
||||||
function Checkbox({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
|
|
||||||
return (
|
|
||||||
<CheckboxPrimitive.Root
|
|
||||||
data-slot="checkbox"
|
|
||||||
className={cn(
|
|
||||||
"peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<CheckboxPrimitive.Indicator
|
|
||||||
data-slot="checkbox-indicator"
|
|
||||||
className="flex items-center justify-center text-current transition-none"
|
|
||||||
>
|
|
||||||
<CheckIcon className="size-3.5" />
|
|
||||||
</CheckboxPrimitive.Indicator>
|
|
||||||
</CheckboxPrimitive.Root>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export { Checkbox }
|
|
||||||
@@ -1,242 +0,0 @@
|
|||||||
import { useMemo } from "react"
|
|
||||||
import { cva, type VariantProps } from "class-variance-authority"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
import { Label } from "@/components/ui/label"
|
|
||||||
import { Separator } from "@/components/ui/separator"
|
|
||||||
|
|
||||||
function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
|
|
||||||
return (
|
|
||||||
<fieldset
|
|
||||||
data-slot="field-set"
|
|
||||||
className={cn(
|
|
||||||
"flex flex-col gap-6",
|
|
||||||
"has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function FieldLegend({
|
|
||||||
className,
|
|
||||||
variant = "legend",
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) {
|
|
||||||
return (
|
|
||||||
<legend
|
|
||||||
data-slot="field-legend"
|
|
||||||
data-variant={variant}
|
|
||||||
className={cn(
|
|
||||||
"mb-3 font-medium",
|
|
||||||
"data-[variant=legend]:text-base",
|
|
||||||
"data-[variant=label]:text-sm",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="field-group"
|
|
||||||
className={cn(
|
|
||||||
"group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const fieldVariants = cva(
|
|
||||||
"group/field flex w-full gap-3 data-[invalid=true]:text-destructive",
|
|
||||||
{
|
|
||||||
variants: {
|
|
||||||
orientation: {
|
|
||||||
vertical: ["flex-col [&>*]:w-full [&>.sr-only]:w-auto"],
|
|
||||||
horizontal: [
|
|
||||||
"flex-row items-center",
|
|
||||||
"[&>[data-slot=field-label]]:flex-auto",
|
|
||||||
"has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
|
|
||||||
],
|
|
||||||
responsive: [
|
|
||||||
"flex-col [&>*]:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto",
|
|
||||||
"@md/field-group:[&>[data-slot=field-label]]:flex-auto",
|
|
||||||
"@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultVariants: {
|
|
||||||
orientation: "vertical",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
function Field({
|
|
||||||
className,
|
|
||||||
orientation = "vertical",
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
role="group"
|
|
||||||
data-slot="field"
|
|
||||||
data-orientation={orientation}
|
|
||||||
className={cn(fieldVariants({ orientation }), className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="field-content"
|
|
||||||
className={cn(
|
|
||||||
"group/field-content flex flex-1 flex-col gap-1.5 leading-snug",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function FieldLabel({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof Label>) {
|
|
||||||
return (
|
|
||||||
<Label
|
|
||||||
data-slot="field-label"
|
|
||||||
className={cn(
|
|
||||||
"group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50",
|
|
||||||
"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4",
|
|
||||||
"has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary dark:has-data-[state=checked]:bg-primary/10",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="field-label"
|
|
||||||
className={cn(
|
|
||||||
"flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
|
|
||||||
return (
|
|
||||||
<p
|
|
||||||
data-slot="field-description"
|
|
||||||
className={cn(
|
|
||||||
"text-muted-foreground text-sm leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance",
|
|
||||||
"last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5",
|
|
||||||
"[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function FieldSeparator({
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<"div"> & {
|
|
||||||
children?: React.ReactNode
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="field-separator"
|
|
||||||
data-content={!!children}
|
|
||||||
className={cn(
|
|
||||||
"relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<Separator className="absolute inset-0 top-1/2" />
|
|
||||||
{children && (
|
|
||||||
<span
|
|
||||||
className="bg-background text-muted-foreground relative mx-auto block w-fit px-2"
|
|
||||||
data-slot="field-separator-content"
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function FieldError({
|
|
||||||
className,
|
|
||||||
children,
|
|
||||||
errors,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<"div"> & {
|
|
||||||
errors?: Array<{ message?: string } | undefined>
|
|
||||||
}) {
|
|
||||||
const content = useMemo(() => {
|
|
||||||
if (children) {
|
|
||||||
return children
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!errors) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errors?.length === 1 && errors[0]?.message) {
|
|
||||||
return errors[0].message
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ul className="ml-4 flex list-disc flex-col gap-1">
|
|
||||||
{errors.map(
|
|
||||||
(error, index) =>
|
|
||||||
error?.message && <li key={index}>{error.message}</li>
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
)
|
|
||||||
}, [children, errors])
|
|
||||||
|
|
||||||
if (!content) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
role="alert"
|
|
||||||
data-slot="field-error"
|
|
||||||
className={cn("text-destructive text-sm font-normal", className)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{content}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
Field,
|
|
||||||
FieldLabel,
|
|
||||||
FieldDescription,
|
|
||||||
FieldError,
|
|
||||||
FieldGroup,
|
|
||||||
FieldLegend,
|
|
||||||
FieldSeparator,
|
|
||||||
FieldSet,
|
|
||||||
FieldContent,
|
|
||||||
FieldTitle,
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import type * as React from "react"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
|
|
||||||
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
|
||||||
return (
|
|
||||||
<input
|
|
||||||
type={type}
|
|
||||||
data-slot="input"
|
|
||||||
className={cn(
|
|
||||||
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
||||||
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
||||||
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export { Input }
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import * as React from "react"
|
|
||||||
import * as LabelPrimitive from "@radix-ui/react-label"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
|
|
||||||
function Label({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
|
||||||
return (
|
|
||||||
<LabelPrimitive.Root
|
|
||||||
data-slot="label"
|
|
||||||
className={cn(
|
|
||||||
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export { Label }
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import * as React from "react"
|
|
||||||
import * as ProgressPrimitive from "@radix-ui/react-progress"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
|
|
||||||
function Progress({
|
|
||||||
className,
|
|
||||||
value,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof ProgressPrimitive.Root>) {
|
|
||||||
return (
|
|
||||||
<ProgressPrimitive.Root
|
|
||||||
data-slot="progress"
|
|
||||||
className={cn(
|
|
||||||
"bg-primary/20 relative h-2 w-full overflow-hidden rounded-full",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<ProgressPrimitive.Indicator
|
|
||||||
data-slot="progress-indicator"
|
|
||||||
className="bg-primary h-full w-full flex-1 transition-all"
|
|
||||||
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
|
||||||
/>
|
|
||||||
</ProgressPrimitive.Root>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export { Progress }
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as SheetPrimitive from "@radix-ui/react-dialog"
|
|
||||||
import { XIcon } from "lucide-react"
|
|
||||||
import type * as React from "react"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
|
|
||||||
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
|
|
||||||
return <SheetPrimitive.Root data-slot="sheet" {...props} />
|
|
||||||
}
|
|
||||||
|
|
||||||
function SheetTrigger({
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
|
|
||||||
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
|
|
||||||
}
|
|
||||||
|
|
||||||
function SheetClose({
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Close>) {
|
|
||||||
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
|
|
||||||
}
|
|
||||||
|
|
||||||
function SheetPortal({
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Portal>) {
|
|
||||||
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
|
|
||||||
}
|
|
||||||
|
|
||||||
function SheetOverlay({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
|
|
||||||
return (
|
|
||||||
<SheetPrimitive.Overlay
|
|
||||||
data-slot="sheet-overlay"
|
|
||||||
className={cn(
|
|
||||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function SheetContent({
|
|
||||||
className,
|
|
||||||
children,
|
|
||||||
side = "right",
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
|
|
||||||
side?: "top" | "right" | "bottom" | "left"
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<SheetPortal>
|
|
||||||
<SheetOverlay />
|
|
||||||
<SheetPrimitive.Content
|
|
||||||
data-slot="sheet-content"
|
|
||||||
className={cn(
|
|
||||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
|
||||||
side === "right" &&
|
|
||||||
"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
|
|
||||||
side === "left" &&
|
|
||||||
"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
|
|
||||||
side === "top" &&
|
|
||||||
"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
|
|
||||||
side === "bottom" &&
|
|
||||||
"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
<SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
|
|
||||||
<XIcon className="size-4" />
|
|
||||||
<span className="sr-only">Close</span>
|
|
||||||
</SheetPrimitive.Close>
|
|
||||||
</SheetPrimitive.Content>
|
|
||||||
</SheetPortal>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="sheet-header"
|
|
||||||
className={cn("flex flex-col gap-1.5 p-4", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="sheet-footer"
|
|
||||||
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function SheetTitle({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Title>) {
|
|
||||||
return (
|
|
||||||
<SheetPrimitive.Title
|
|
||||||
data-slot="sheet-title"
|
|
||||||
className={cn("text-foreground font-semibold", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function SheetDescription({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Description>) {
|
|
||||||
return (
|
|
||||||
<SheetPrimitive.Description
|
|
||||||
data-slot="sheet-description"
|
|
||||||
className={cn("text-muted-foreground text-sm", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
Sheet,
|
|
||||||
SheetTrigger,
|
|
||||||
SheetClose,
|
|
||||||
SheetContent,
|
|
||||||
SheetHeader,
|
|
||||||
SheetFooter,
|
|
||||||
SheetTitle,
|
|
||||||
SheetDescription,
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { cn } from "@/lib/utils"
|
|
||||||
|
|
||||||
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="skeleton"
|
|
||||||
className={cn("bg-accent animate-pulse rounded-md", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export { Skeleton }
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
|
||||||
import type * as React from "react"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
|
|
||||||
function TooltipProvider({
|
|
||||||
delayDuration = 0,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
|
|
||||||
return (
|
|
||||||
<TooltipPrimitive.Provider
|
|
||||||
data-slot="tooltip-provider"
|
|
||||||
delayDuration={delayDuration}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function Tooltip({
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
|
|
||||||
return (
|
|
||||||
<TooltipProvider>
|
|
||||||
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
|
||||||
</TooltipProvider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function TooltipTrigger({
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
|
|
||||||
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
|
|
||||||
}
|
|
||||||
|
|
||||||
function TooltipContent({
|
|
||||||
className,
|
|
||||||
sideOffset = 0,
|
|
||||||
children,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
|
|
||||||
return (
|
|
||||||
<TooltipPrimitive.Portal>
|
|
||||||
<TooltipPrimitive.Content
|
|
||||||
data-slot="tooltip-content"
|
|
||||||
sideOffset={sideOffset}
|
|
||||||
className={cn(
|
|
||||||
"bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
<TooltipPrimitive.Arrow className="bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
|
|
||||||
</TooltipPrimitive.Content>
|
|
||||||
</TooltipPrimitive.Portal>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
import {
|
|
||||||
type Atom,
|
|
||||||
type ExtractAtomArgs,
|
|
||||||
type ExtractAtomResult,
|
|
||||||
type ExtractAtomValue,
|
|
||||||
type PrimitiveAtom,
|
|
||||||
type SetStateAction,
|
|
||||||
useAtom,
|
|
||||||
type WritableAtom,
|
|
||||||
} from "jotai"
|
|
||||||
import type * as React from "react"
|
|
||||||
|
|
||||||
type SetAtom<Args extends unknown[], Result> = (...args: Args) => Result
|
|
||||||
|
|
||||||
export function WithAtom<Value, Args extends unknown[], Result>(props: {
|
|
||||||
atom: WritableAtom<Value, Args, Result>
|
|
||||||
children: (
|
|
||||||
value: Awaited<Value>,
|
|
||||||
setAtom: SetAtom<Args, Result>,
|
|
||||||
) => React.ReactNode
|
|
||||||
}): React.ReactNode
|
|
||||||
export function WithAtom<Value>(props: {
|
|
||||||
atom: PrimitiveAtom<Value>
|
|
||||||
children: (
|
|
||||||
value: Awaited<Value>,
|
|
||||||
setAtom: SetAtom<[SetStateAction<Value>], void>,
|
|
||||||
) => React.ReactNode
|
|
||||||
}): React.ReactNode
|
|
||||||
export function WithAtom<Value>(props: {
|
|
||||||
atom: Atom<Value>
|
|
||||||
children: (value: Awaited<Value>, setAtom: never) => React.ReactNode
|
|
||||||
}): React.ReactNode
|
|
||||||
export function WithAtom<
|
|
||||||
AtomType extends WritableAtom<unknown, never[], unknown>,
|
|
||||||
>(props: {
|
|
||||||
atom: AtomType
|
|
||||||
children: (
|
|
||||||
value: Awaited<ExtractAtomValue<AtomType>>,
|
|
||||||
setAtom: SetAtom<
|
|
||||||
ExtractAtomArgs<AtomType>,
|
|
||||||
ExtractAtomResult<AtomType>
|
|
||||||
>,
|
|
||||||
) => React.ReactNode
|
|
||||||
}): React.ReactNode
|
|
||||||
export function WithAtom<AtomType extends Atom<unknown>>(props: {
|
|
||||||
atom: AtomType
|
|
||||||
children: (
|
|
||||||
value: Awaited<ExtractAtomValue<AtomType>>,
|
|
||||||
setAtom: never,
|
|
||||||
) => React.ReactNode
|
|
||||||
}): React.ReactNode
|
|
||||||
export function WithAtom<Value, Args extends unknown[], Result>({
|
|
||||||
atom,
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
atom: Atom<Value> | WritableAtom<Value, Args, Result>
|
|
||||||
children: (
|
|
||||||
value: Awaited<Value>,
|
|
||||||
setAtom: SetAtom<Args, Result> | never,
|
|
||||||
) => React.ReactNode
|
|
||||||
}) {
|
|
||||||
const [value, setAtom] = useAtom(atom as WritableAtom<Value, Args, Result>)
|
|
||||||
return children(value, setAtom)
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { atom } from "jotai"
|
|
||||||
|
|
||||||
type BackgroundTaskProgress = {
|
|
||||||
label: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const backgroundTaskProgressAtom = atom<BackgroundTaskProgress | null>(
|
|
||||||
null,
|
|
||||||
)
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
import { api } from "@fileone/convex/api"
|
|
||||||
import { newFileSystemHandle } from "@fileone/convex/filesystem"
|
|
||||||
import { useMutation } from "@tanstack/react-query"
|
|
||||||
import { useMutation as useContextMutation } from "convex/react"
|
|
||||||
import { useAtom, useAtomValue, useSetAtom, useStore } from "jotai"
|
|
||||||
import { TextCursorInputIcon, TrashIcon } from "lucide-react"
|
|
||||||
import { toast } from "sonner"
|
|
||||||
import {
|
|
||||||
ContextMenu,
|
|
||||||
ContextMenuContent,
|
|
||||||
ContextMenuItem,
|
|
||||||
ContextMenuTrigger,
|
|
||||||
} from "@/components/ui/context-menu"
|
|
||||||
import {
|
|
||||||
contextMenuTargeItemsAtom,
|
|
||||||
itemBeingRenamedAtom,
|
|
||||||
optimisticDeletedItemsAtom,
|
|
||||||
} from "./state"
|
|
||||||
|
|
||||||
export function DirectoryContentContextMenu({
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children: React.ReactNode
|
|
||||||
}) {
|
|
||||||
const store = useStore()
|
|
||||||
const [target, setTarget] = useAtom(contextMenuTargeItemsAtom)
|
|
||||||
const setOptimisticDeletedItems = useSetAtom(optimisticDeletedItemsAtom)
|
|
||||||
const moveToTrashMutation = useContextMutation(api.filesystem.moveToTrash)
|
|
||||||
const { mutate: moveToTrash } = useMutation({
|
|
||||||
mutationFn: moveToTrashMutation,
|
|
||||||
onMutate: ({ handles }) => {
|
|
||||||
setOptimisticDeletedItems(
|
|
||||||
(prev) =>
|
|
||||||
new Set([...prev, ...handles.map((handle) => handle.id)]),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onSuccess: ({ deleted, errors }, { handles }) => {
|
|
||||||
setOptimisticDeletedItems((prev) => {
|
|
||||||
const newSet = new Set(prev)
|
|
||||||
for (const handle of handles) {
|
|
||||||
newSet.delete(handle.id)
|
|
||||||
}
|
|
||||||
return newSet
|
|
||||||
})
|
|
||||||
if (errors.length === 0 && deleted.length === handles.length) {
|
|
||||||
toast.success(`Moved ${handles.length} items to trash`)
|
|
||||||
} else if (errors.length === handles.length) {
|
|
||||||
toast.error("Failed to move to trash")
|
|
||||||
} else {
|
|
||||||
toast.info(
|
|
||||||
`Moved ${deleted.length} items to trash; failed to move ${errors.length} items`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleDelete = () => {
|
|
||||||
const selectedItems = store.get(contextMenuTargeItemsAtom)
|
|
||||||
if (selectedItems.length > 0) {
|
|
||||||
moveToTrash({
|
|
||||||
handles: selectedItems.map(newFileSystemHandle),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ContextMenu
|
|
||||||
onOpenChange={(open) => {
|
|
||||||
if (!open) {
|
|
||||||
setTarget([])
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ContextMenuTrigger asChild>{children}</ContextMenuTrigger>
|
|
||||||
{target && (
|
|
||||||
<ContextMenuContent>
|
|
||||||
<RenameMenuItem />
|
|
||||||
<ContextMenuItem onClick={handleDelete}>
|
|
||||||
<TrashIcon />
|
|
||||||
Move to trash
|
|
||||||
</ContextMenuItem>
|
|
||||||
</ContextMenuContent>
|
|
||||||
)}
|
|
||||||
</ContextMenu>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function RenameMenuItem() {
|
|
||||||
const store = useStore()
|
|
||||||
const target = useAtomValue(contextMenuTargeItemsAtom)
|
|
||||||
const setItemBeingRenamed = useSetAtom(itemBeingRenamedAtom)
|
|
||||||
|
|
||||||
const handleRename = () => {
|
|
||||||
const selectedItems = store.get(contextMenuTargeItemsAtom)
|
|
||||||
if (selectedItems.length === 1) {
|
|
||||||
// biome-ignore lint/style/noNonNullAssertion: length is checked
|
|
||||||
const selectedItem = selectedItems[0]!
|
|
||||||
setItemBeingRenamed({
|
|
||||||
originalItem: selectedItem,
|
|
||||||
name: selectedItem.doc.name,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only render if exactly one item is selected
|
|
||||||
if (target.length !== 1) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ContextMenuItem onClick={handleRename}>
|
|
||||||
<TextCursorInputIcon />
|
|
||||||
Rename
|
|
||||||
</ContextMenuItem>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
import { api } from "@fileone/convex/api"
|
|
||||||
import { type FileSystemItem, FileType } from "@fileone/convex/filesystem"
|
|
||||||
import { useMutation } from "@tanstack/react-query"
|
|
||||||
import { useMutation as useContextMutation } from "convex/react"
|
|
||||||
import { useId } from "react"
|
|
||||||
import { Button } from "@/components/ui/button"
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogClose,
|
|
||||||
DialogContent,
|
|
||||||
DialogFooter,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
} from "@/components/ui/dialog"
|
|
||||||
import { Input } from "@/components/ui/input"
|
|
||||||
|
|
||||||
type RenameFileDialogProps = {
|
|
||||||
item: FileSystemItem
|
|
||||||
onRenameSuccess: () => void
|
|
||||||
onClose: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export function RenameFileDialog({
|
|
||||||
item,
|
|
||||||
onRenameSuccess,
|
|
||||||
onClose,
|
|
||||||
}: RenameFileDialogProps) {
|
|
||||||
const formId = useId()
|
|
||||||
|
|
||||||
const { mutate: renameFile, isPending: isRenaming } = useMutation({
|
|
||||||
mutationFn: useContextMutation(api.files.renameFile),
|
|
||||||
onSuccess: () => {
|
|
||||||
onRenameSuccess()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
|
||||||
event.preventDefault()
|
|
||||||
|
|
||||||
const formData = new FormData(event.currentTarget)
|
|
||||||
const newName = formData.get("itemName") as string
|
|
||||||
|
|
||||||
if (newName) {
|
|
||||||
switch (item.kind) {
|
|
||||||
case FileType.File:
|
|
||||||
renameFile({
|
|
||||||
directoryId: item.doc.directoryId,
|
|
||||||
itemId: item.doc._id,
|
|
||||||
newName,
|
|
||||||
})
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog
|
|
||||||
open
|
|
||||||
onOpenChange={(open) => {
|
|
||||||
if (!open) {
|
|
||||||
onClose()
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DialogContent className="sm:max-w-md">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>Rename File</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
|
|
||||||
<form id={formId} onSubmit={onSubmit}>
|
|
||||||
<RenameFileInput initialValue={item.doc.name} />
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<DialogFooter>
|
|
||||||
<DialogClose asChild>
|
|
||||||
<Button loading={isRenaming} variant="outline">
|
|
||||||
<span>Cancel</span>
|
|
||||||
</Button>
|
|
||||||
</DialogClose>
|
|
||||||
<Button loading={isRenaming} type="submit" form={formId}>
|
|
||||||
<span>Rename</span>
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function RenameFileInput({ initialValue }: { initialValue: string }) {
|
|
||||||
return <Input defaultValue={initialValue} name="itemName" />
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
import type { Id } from "@fileone/convex/dataModel"
|
|
||||||
import type {
|
|
||||||
DirectoryHandle,
|
|
||||||
DirectoryPathComponent,
|
|
||||||
} from "@fileone/convex/filesystem"
|
|
||||||
import type { DirectoryInfo } from "@fileone/convex/types"
|
|
||||||
import { Link } from "@tanstack/react-router"
|
|
||||||
import type { PrimitiveAtom } from "jotai"
|
|
||||||
import { atom } from "jotai"
|
|
||||||
import { Fragment } from "react"
|
|
||||||
import {
|
|
||||||
Breadcrumb,
|
|
||||||
BreadcrumbItem,
|
|
||||||
BreadcrumbLink,
|
|
||||||
BreadcrumbList,
|
|
||||||
BreadcrumbPage,
|
|
||||||
BreadcrumbSeparator,
|
|
||||||
} from "@/components/ui/breadcrumb"
|
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/components/ui/tooltip"
|
|
||||||
import type { FileDragInfo } from "@/files/use-file-drop"
|
|
||||||
import { useFileDrop } from "@/files/use-file-drop"
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is a placeholder file drag info atom that always stores null and is never mutated.
|
|
||||||
*/
|
|
||||||
const nullFileDragInfoAtom = atom<FileDragInfo | null>(null)
|
|
||||||
|
|
||||||
export function DirectoryPathBreadcrumb({
|
|
||||||
directory,
|
|
||||||
rootLabel,
|
|
||||||
directoryUrlFn,
|
|
||||||
fileDragInfoAtom = nullFileDragInfoAtom,
|
|
||||||
}: {
|
|
||||||
directory: DirectoryInfo
|
|
||||||
rootLabel: string
|
|
||||||
directoryUrlFn: (directory: Id<"directories">) => string
|
|
||||||
fileDragInfoAtom?: PrimitiveAtom<FileDragInfo | null>
|
|
||||||
}) {
|
|
||||||
const breadcrumbItems: React.ReactNode[] = [
|
|
||||||
<FilePathBreadcrumbItem
|
|
||||||
key={directory.path[0].handle.id}
|
|
||||||
component={directory.path[0]}
|
|
||||||
rootLabel={rootLabel}
|
|
||||||
directoryUrlFn={directoryUrlFn}
|
|
||||||
fileDragInfoAtom={fileDragInfoAtom}
|
|
||||||
/>,
|
|
||||||
]
|
|
||||||
for (let i = 1; i < directory.path.length - 1; i++) {
|
|
||||||
breadcrumbItems.push(
|
|
||||||
<Fragment key={directory.path[i]?.handle.id}>
|
|
||||||
<BreadcrumbSeparator />
|
|
||||||
<FilePathBreadcrumbItem
|
|
||||||
component={directory.path[i]!}
|
|
||||||
rootLabel={rootLabel}
|
|
||||||
directoryUrlFn={directoryUrlFn}
|
|
||||||
fileDragInfoAtom={fileDragInfoAtom}
|
|
||||||
/>
|
|
||||||
</Fragment>,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Breadcrumb>
|
|
||||||
<BreadcrumbList>
|
|
||||||
{breadcrumbItems}
|
|
||||||
<BreadcrumbSeparator />
|
|
||||||
<BreadcrumbItem>
|
|
||||||
<BreadcrumbPage>{directory.name}</BreadcrumbPage>{" "}
|
|
||||||
</BreadcrumbItem>
|
|
||||||
</BreadcrumbList>
|
|
||||||
</Breadcrumb>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function FilePathBreadcrumbItem({
|
|
||||||
component,
|
|
||||||
rootLabel,
|
|
||||||
directoryUrlFn,
|
|
||||||
fileDragInfoAtom,
|
|
||||||
}: {
|
|
||||||
component: DirectoryPathComponent
|
|
||||||
rootLabel: string
|
|
||||||
directoryUrlFn: (directory: Id<"directories">) => string
|
|
||||||
fileDragInfoAtom: PrimitiveAtom<FileDragInfo | null>
|
|
||||||
}) {
|
|
||||||
const { isDraggedOver, dropHandlers } = useFileDrop({
|
|
||||||
destItem: component.handle as DirectoryHandle,
|
|
||||||
dragInfoAtom: fileDragInfoAtom,
|
|
||||||
})
|
|
||||||
|
|
||||||
const dirName = component.name || rootLabel
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Tooltip open={isDraggedOver}>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<BreadcrumbItem
|
|
||||||
className={cn({ "bg-muted": isDraggedOver })}
|
|
||||||
{...dropHandlers}
|
|
||||||
>
|
|
||||||
<BreadcrumbLink asChild>
|
|
||||||
<Link to={directoryUrlFn(component.handle.id)}>
|
|
||||||
{dirName}
|
|
||||||
</Link>
|
|
||||||
</BreadcrumbLink>
|
|
||||||
</BreadcrumbItem>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>Move to {dirName}</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
import { useAtomValue } from "jotai"
|
|
||||||
import { CircleAlertIcon, XIcon } from "lucide-react"
|
|
||||||
import type React from "react"
|
|
||||||
import { Button } from "@/components/ui/button"
|
|
||||||
import { Progress } from "@/components/ui/progress"
|
|
||||||
import { Tooltip } from "@/components/ui/tooltip"
|
|
||||||
import { FileUploadStatusKind, fileUploadStatusAtomFamily } from "./store"
|
|
||||||
import type { PickedFile } from "./upload-file-dialog"
|
|
||||||
|
|
||||||
export function PickedFileItem({
|
|
||||||
file: pickedFile,
|
|
||||||
onRemove,
|
|
||||||
}: {
|
|
||||||
file: PickedFile
|
|
||||||
onRemove: (file: PickedFile) => void
|
|
||||||
}) {
|
|
||||||
const fileUploadAtom = fileUploadStatusAtomFamily(pickedFile.id)
|
|
||||||
const fileUpload = useAtomValue(fileUploadAtom)
|
|
||||||
console.log("fileUpload", fileUpload)
|
|
||||||
const { file, id } = pickedFile
|
|
||||||
|
|
||||||
let statusIndicator: React.ReactNode
|
|
||||||
if (!fileUpload) {
|
|
||||||
statusIndicator = (
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
onClick={() => onRemove(pickedFile)}
|
|
||||||
>
|
|
||||||
<XIcon className="size-4" />
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
switch (fileUpload.kind) {
|
|
||||||
case FileUploadStatusKind.InProgress:
|
|
||||||
statusIndicator = <Progress value={fileUpload.progress * 100} />
|
|
||||||
break
|
|
||||||
case FileUploadStatusKind.Error:
|
|
||||||
statusIndicator = (
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger>
|
|
||||||
<CircleAlertIcon />
|
|
||||||
</TooltipTrigger>
|
|
||||||
</Tooltip>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<li
|
|
||||||
className="pl-3 pr-1 py-0.5 h-8 hover:bg-muted flex justify-between items-center"
|
|
||||||
key={id}
|
|
||||||
>
|
|
||||||
<span>{file.name}</span>
|
|
||||||
{fileUpload ? (
|
|
||||||
<Progress
|
|
||||||
className="max-w-20"
|
|
||||||
value={fileUpload.progress * 100}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
onClick={() => onRemove(pickedFile)}
|
|
||||||
>
|
|
||||||
<XIcon className="size-4" />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import type { OpenedFile } from "@fileone/convex/filesystem"
|
|
||||||
import { ImagePreviewDialog } from "./image-preview-dialog"
|
|
||||||
|
|
||||||
export function FilePreviewDialog({
|
|
||||||
openedFile,
|
|
||||||
onClose,
|
|
||||||
}: {
|
|
||||||
openedFile: OpenedFile
|
|
||||||
onClose: () => void
|
|
||||||
}) {
|
|
||||||
switch (openedFile.file.mimeType) {
|
|
||||||
case "image/jpeg":
|
|
||||||
case "image/png":
|
|
||||||
case "image/gif":
|
|
||||||
return (
|
|
||||||
<ImagePreviewDialog openedFile={openedFile} onClose={onClose} />
|
|
||||||
)
|
|
||||||
default:
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export function fileShareUrl(shareToken: string) {
|
|
||||||
return `${import.meta.env.VITE_FILE_PROXY_URL}/files/${shareToken}`
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
import { atom } from "jotai"
|
|
||||||
import { atomFamily } from "jotai/utils"
|
|
||||||
|
|
||||||
export enum FileUploadStatusKind {
|
|
||||||
InProgress = "InProgress",
|
|
||||||
Error = "Error",
|
|
||||||
Success = "Success",
|
|
||||||
}
|
|
||||||
|
|
||||||
export type FileUploadInProgress = {
|
|
||||||
kind: FileUploadStatusKind.InProgress
|
|
||||||
progress: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export type FileUploadError = {
|
|
||||||
kind: FileUploadStatusKind.Error
|
|
||||||
error: unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
export type FileUploadSuccess = {
|
|
||||||
kind: FileUploadStatusKind.Success
|
|
||||||
}
|
|
||||||
|
|
||||||
export type FileUploadStatus =
|
|
||||||
| FileUploadInProgress
|
|
||||||
| FileUploadError
|
|
||||||
| FileUploadSuccess
|
|
||||||
|
|
||||||
export const fileUploadStatusesAtom = atom<Record<string, FileUploadStatus>>({})
|
|
||||||
|
|
||||||
export const fileUploadStatusAtomFamily = atomFamily((id: string) =>
|
|
||||||
atom(
|
|
||||||
(get) => get(fileUploadStatusesAtom)[id],
|
|
||||||
(get, set, status: FileUploadStatus) => {
|
|
||||||
const fileUploads = { ...get(fileUploadStatusesAtom) }
|
|
||||||
fileUploads[id] = status
|
|
||||||
set(fileUploadStatusesAtom, fileUploads)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
export const clearFileUploadStatusesAtom = atom(
|
|
||||||
null,
|
|
||||||
(get, set, ids: string[]) => {
|
|
||||||
const fileUploads = { ...get(fileUploadStatusesAtom) }
|
|
||||||
for (const id of ids) {
|
|
||||||
if (fileUploads[id]) {
|
|
||||||
delete fileUploads[id]
|
|
||||||
}
|
|
||||||
fileUploadStatusAtomFamily.remove(id)
|
|
||||||
}
|
|
||||||
set(fileUploadStatusesAtom, fileUploads)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
export const clearAllFileUploadStatusesAtom = atom(
|
|
||||||
null,
|
|
||||||
(get, set) => {
|
|
||||||
set(fileUploadStatusesAtom, {})
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
export const fileUploadCountAtom = atom(
|
|
||||||
(get) => Object.keys(get(fileUploadStatusesAtom)).length,
|
|
||||||
)
|
|
||||||
|
|
||||||
export const inProgressFileUploadCountAtom = atom((get) => {
|
|
||||||
const statuses = get(fileUploadStatusesAtom)
|
|
||||||
let count = 0
|
|
||||||
for (const status in statuses) {
|
|
||||||
if (statuses[status]?.kind === FileUploadStatusKind.InProgress) {
|
|
||||||
count += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
})
|
|
||||||
|
|
||||||
export const successfulFileUploadCountAtom = atom((get) => {
|
|
||||||
const statuses = get(fileUploadStatusesAtom)
|
|
||||||
let count = 0
|
|
||||||
for (const status in statuses) {
|
|
||||||
if (statuses[status]?.kind === FileUploadStatusKind.Success) {
|
|
||||||
count += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
})
|
|
||||||
|
|
||||||
export const hasFileUploadsErrorAtom = atom((get) => {
|
|
||||||
const statuses = get(fileUploadStatusesAtom)
|
|
||||||
for (const status in statuses) {
|
|
||||||
if (statuses[status]?.kind === FileUploadStatusKind.Error) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
@@ -1,599 +0,0 @@
|
|||||||
import type { Doc } from "@fileone/convex/dataModel"
|
|
||||||
import { mutationOptions } from "@tanstack/react-query"
|
|
||||||
import { atom, useAtom, useAtomValue, useSetAtom, useStore } from "jotai"
|
|
||||||
import { atomEffect } from "jotai-effect"
|
|
||||||
import { atomWithMutation } from "jotai-tanstack-query"
|
|
||||||
import {
|
|
||||||
CircleAlertIcon,
|
|
||||||
CircleCheckIcon,
|
|
||||||
FilePlus2Icon,
|
|
||||||
UploadCloudIcon,
|
|
||||||
XIcon,
|
|
||||||
} from "lucide-react"
|
|
||||||
import { nanoid } from "nanoid"
|
|
||||||
import type React from "react"
|
|
||||||
import { useId, useMemo, useRef, useState } from "react"
|
|
||||||
import { toast } from "sonner"
|
|
||||||
import { Button } from "@/components/ui/button"
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogDescription,
|
|
||||||
DialogFooter,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
} from "@/components/ui/dialog"
|
|
||||||
import { Progress } from "@/components/ui/progress"
|
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/components/ui/tooltip"
|
|
||||||
import { formatError } from "@/lib/error"
|
|
||||||
import {
|
|
||||||
clearAllFileUploadStatusesAtom,
|
|
||||||
clearFileUploadStatusesAtom,
|
|
||||||
FileUploadStatusKind,
|
|
||||||
fileUploadCountAtom,
|
|
||||||
fileUploadStatusAtomFamily,
|
|
||||||
fileUploadStatusesAtom,
|
|
||||||
hasFileUploadsErrorAtom,
|
|
||||||
successfulFileUploadCountAtom,
|
|
||||||
} from "./store"
|
|
||||||
import useUploadFile from "./use-upload-file"
|
|
||||||
|
|
||||||
type UploadFileDialogProps = {
|
|
||||||
targetDirectory: Doc<"directories">
|
|
||||||
onClose: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload file atoms
|
|
||||||
export type PickedFile = {
|
|
||||||
id: string
|
|
||||||
file: File
|
|
||||||
}
|
|
||||||
|
|
||||||
export const pickedFilesAtom = atom<PickedFile[]>([])
|
|
||||||
|
|
||||||
function useUploadFilesAtom({
|
|
||||||
targetDirectory,
|
|
||||||
}: {
|
|
||||||
targetDirectory: Doc<"directories">
|
|
||||||
}) {
|
|
||||||
const uploadFile = useUploadFile({ targetDirectory })
|
|
||||||
const store = useStore()
|
|
||||||
const options = useMemo(
|
|
||||||
() =>
|
|
||||||
mutationOptions({
|
|
||||||
mutationFn: async (files: PickedFile[]) => {
|
|
||||||
const promises = files.map((pickedFile) =>
|
|
||||||
uploadFile({
|
|
||||||
file: pickedFile.file,
|
|
||||||
onStart: () => {
|
|
||||||
store.set(
|
|
||||||
fileUploadStatusAtomFamily(pickedFile.id),
|
|
||||||
{
|
|
||||||
kind: FileUploadStatusKind.InProgress,
|
|
||||||
progress: 0,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onProgress: (progress) => {
|
|
||||||
store.set(
|
|
||||||
fileUploadStatusAtomFamily(pickedFile.id),
|
|
||||||
{
|
|
||||||
kind: FileUploadStatusKind.InProgress,
|
|
||||||
progress,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
}).catch((error) => {
|
|
||||||
console.log("error", error)
|
|
||||||
store.set(
|
|
||||||
fileUploadStatusAtomFamily(pickedFile.id),
|
|
||||||
{
|
|
||||||
kind: FileUploadStatusKind.Error,
|
|
||||||
error,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
throw error
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
return await Promise.allSettled(promises)
|
|
||||||
},
|
|
||||||
onSuccess: (results, files) => {
|
|
||||||
const remainingPickedFiles: PickedFile[] = []
|
|
||||||
results.forEach((result, i) => {
|
|
||||||
// biome-ignore lint/style/noNonNullAssertion: results lenght must match input files array length
|
|
||||||
const pickedFile = files[i]!
|
|
||||||
const statusAtom = fileUploadStatusAtomFamily(
|
|
||||||
pickedFile.id,
|
|
||||||
)
|
|
||||||
switch (result.status) {
|
|
||||||
case "fulfilled":
|
|
||||||
store.set(statusAtom, {
|
|
||||||
kind: FileUploadStatusKind.Success,
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case "rejected":
|
|
||||||
store.set(statusAtom, {
|
|
||||||
kind: FileUploadStatusKind.Error,
|
|
||||||
error: result.reason,
|
|
||||||
})
|
|
||||||
remainingPickedFiles.push(pickedFile)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// setPickedFiles(remainingPickedFiles)
|
|
||||||
|
|
||||||
if (remainingPickedFiles.length === 0) {
|
|
||||||
toast.success("All files uploaded successfully")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
[uploadFile, store.set],
|
|
||||||
)
|
|
||||||
return useMemo(() => atomWithMutation(() => options), [options])
|
|
||||||
}
|
|
||||||
type UploadFilesAtom = ReturnType<typeof useUploadFilesAtom>
|
|
||||||
|
|
||||||
export function UploadFileDialog({
|
|
||||||
targetDirectory,
|
|
||||||
onClose,
|
|
||||||
}: UploadFileDialogProps) {
|
|
||||||
const formId = useId()
|
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
|
||||||
const setPickedFiles = useSetAtom(pickedFilesAtom)
|
|
||||||
const clearFileUploadStatuses = useSetAtom(clearFileUploadStatusesAtom)
|
|
||||||
const store = useStore()
|
|
||||||
|
|
||||||
const updateFileInputEffect = useMemo(
|
|
||||||
() =>
|
|
||||||
atomEffect((get) => {
|
|
||||||
const dataTransfer = new DataTransfer()
|
|
||||||
const pickedFiles = get(pickedFilesAtom)
|
|
||||||
for (const { file } of pickedFiles) {
|
|
||||||
dataTransfer.items.add(file)
|
|
||||||
}
|
|
||||||
if (fileInputRef.current) {
|
|
||||||
fileInputRef.current.files = dataTransfer.files
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
useAtom(updateFileInputEffect)
|
|
||||||
|
|
||||||
const uploadFilesAtom = useUploadFilesAtom({
|
|
||||||
targetDirectory,
|
|
||||||
})
|
|
||||||
|
|
||||||
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
function openFilePicker() {
|
|
||||||
fileInputRef.current?.click()
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleFileChange(event: React.ChangeEvent<HTMLInputElement>) {
|
|
||||||
const files = event.target.files
|
|
||||||
if (files) {
|
|
||||||
setPickedFiles((prev) => [
|
|
||||||
...prev,
|
|
||||||
...Array.from(files).map((file) => ({ id: nanoid(), file })),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onUploadButtonClick() {
|
|
||||||
const uploadStatuses = store.get(fileUploadStatusesAtom)
|
|
||||||
const fileUploadCount = store.get(fileUploadCountAtom)
|
|
||||||
const pickedFiles = store.get(pickedFilesAtom)
|
|
||||||
const { mutate: uploadFiles, reset: restUploadFilesMutation } =
|
|
||||||
store.get(uploadFilesAtom)
|
|
||||||
|
|
||||||
if (pickedFiles.length === 0) {
|
|
||||||
// no files are picked, nothing to upload
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileUploadCount === 0) {
|
|
||||||
// no files are being uploaded, upload all picked files
|
|
||||||
uploadFiles(pickedFiles)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const successfulUploads: PickedFile["id"][] = []
|
|
||||||
const nextPickedFiles: PickedFile[] = []
|
|
||||||
for (const file of pickedFiles) {
|
|
||||||
const uploadStatus = uploadStatuses[file.id]
|
|
||||||
if (uploadStatus) {
|
|
||||||
switch (uploadStatus.kind) {
|
|
||||||
case FileUploadStatusKind.Success:
|
|
||||||
successfulUploads.push(file.id)
|
|
||||||
continue
|
|
||||||
case FileUploadStatusKind.InProgress:
|
|
||||||
continue
|
|
||||||
case FileUploadStatusKind.Error:
|
|
||||||
nextPickedFiles.push(file)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clearFileUploadStatuses(successfulUploads)
|
|
||||||
|
|
||||||
if (successfulUploads.length === pickedFiles.length) {
|
|
||||||
// all files were successfully uploaded, close the dialog
|
|
||||||
onClose()
|
|
||||||
} else {
|
|
||||||
// some files were not successfully uploaded, set the next picked files
|
|
||||||
setPickedFiles(nextPickedFiles)
|
|
||||||
restUploadFilesMutation()
|
|
||||||
uploadFiles(nextPickedFiles)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog
|
|
||||||
open
|
|
||||||
onOpenChange={(open) => {
|
|
||||||
if (!open) onClose()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DialogContent className="sm:max-w-2xl">
|
|
||||||
<UploadDialogHeader
|
|
||||||
uploadFilesAtom={uploadFilesAtom}
|
|
||||||
targetDirectory={targetDirectory}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<form id={formId} onSubmit={handleSubmit}>
|
|
||||||
<input
|
|
||||||
hidden
|
|
||||||
multiple
|
|
||||||
type="file"
|
|
||||||
name="files"
|
|
||||||
ref={fileInputRef}
|
|
||||||
onChange={handleFileChange}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<UploadFileDropContainer>
|
|
||||||
<UploadFileArea onClick={openFilePicker} />
|
|
||||||
</UploadFileDropContainer>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<DialogFooter>
|
|
||||||
<ContinueUploadAfterSuccessfulUploadButton
|
|
||||||
uploadFilesAtom={uploadFilesAtom}
|
|
||||||
/>
|
|
||||||
<SelectMoreFilesButton
|
|
||||||
onClick={openFilePicker}
|
|
||||||
uploadFilesAtom={uploadFilesAtom}
|
|
||||||
/>
|
|
||||||
<UploadButton
|
|
||||||
uploadFilesAtom={uploadFilesAtom}
|
|
||||||
onClick={onUploadButtonClick}
|
|
||||||
/>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function UploadDialogHeader({
|
|
||||||
uploadFilesAtom,
|
|
||||||
targetDirectory,
|
|
||||||
}: {
|
|
||||||
uploadFilesAtom: UploadFilesAtom
|
|
||||||
targetDirectory: Doc<"directories">
|
|
||||||
}) {
|
|
||||||
const { data: uploadResults, isPending: isUploading } =
|
|
||||||
useAtomValue(uploadFilesAtom)
|
|
||||||
const successfulUploadCount = useAtomValue(successfulFileUploadCountAtom)
|
|
||||||
|
|
||||||
let dialogTitle: string
|
|
||||||
let dialogDescription: string
|
|
||||||
if (isUploading) {
|
|
||||||
dialogTitle = "Uploading files"
|
|
||||||
dialogDescription =
|
|
||||||
"You can close the dialog while they are being uploaded in the background."
|
|
||||||
} else if (
|
|
||||||
uploadResults &&
|
|
||||||
uploadResults.length > 0 &&
|
|
||||||
successfulUploadCount === uploadResults.length
|
|
||||||
) {
|
|
||||||
dialogTitle = "Files uploaded"
|
|
||||||
dialogDescription =
|
|
||||||
"Click 'Done' to close the dialog, or select more files to upload."
|
|
||||||
} else if (targetDirectory.name) {
|
|
||||||
dialogTitle = `Upload file to "${targetDirectory.name}"`
|
|
||||||
dialogDescription = "Drag and drop files here or click to select files"
|
|
||||||
} else {
|
|
||||||
dialogTitle = "Upload file"
|
|
||||||
dialogDescription = "Drag and drop files here or click to select files"
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>{dialogTitle}</DialogTitle>
|
|
||||||
<DialogDescription>{dialogDescription}</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function ContinueUploadAfterSuccessfulUploadButton({
|
|
||||||
uploadFilesAtom,
|
|
||||||
}: {
|
|
||||||
uploadFilesAtom: UploadFilesAtom
|
|
||||||
}) {
|
|
||||||
const setPickedFiles = useSetAtom(pickedFilesAtom)
|
|
||||||
const clearAllFileUploadStatuses = useSetAtom(
|
|
||||||
clearAllFileUploadStatusesAtom,
|
|
||||||
)
|
|
||||||
const {
|
|
||||||
data: uploadResults,
|
|
||||||
isPending: isUploading,
|
|
||||||
reset: resetUploadFilesMutation,
|
|
||||||
} = useAtomValue(uploadFilesAtom)
|
|
||||||
const successfulUploadCount = useAtomValue(successfulFileUploadCountAtom)
|
|
||||||
|
|
||||||
if (
|
|
||||||
!uploadResults ||
|
|
||||||
uploadResults.length === 0 ||
|
|
||||||
successfulUploadCount !== uploadResults.length
|
|
||||||
) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetUploadState() {
|
|
||||||
setPickedFiles([])
|
|
||||||
clearAllFileUploadStatuses()
|
|
||||||
resetUploadFilesMutation()
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={resetUploadState}
|
|
||||||
disabled={isUploading}
|
|
||||||
>
|
|
||||||
Upload more files
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* allows the user to select more files after they have selected some files for upload. only visible before any upload has been started.
|
|
||||||
*/
|
|
||||||
function SelectMoreFilesButton({
|
|
||||||
onClick,
|
|
||||||
uploadFilesAtom,
|
|
||||||
}: {
|
|
||||||
onClick: () => void
|
|
||||||
uploadFilesAtom: UploadFilesAtom
|
|
||||||
}) {
|
|
||||||
const pickedFiles = useAtomValue(pickedFilesAtom)
|
|
||||||
const { data: uploadResults, isPending: isUploading } =
|
|
||||||
useAtomValue(uploadFilesAtom)
|
|
||||||
|
|
||||||
if (pickedFiles.length === 0 || uploadResults) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Button variant="outline" onClick={onClick} disabled={isUploading}>
|
|
||||||
Select more files
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function UploadButton({
|
|
||||||
uploadFilesAtom,
|
|
||||||
onClick,
|
|
||||||
}: {
|
|
||||||
uploadFilesAtom: UploadFilesAtom
|
|
||||||
onClick: () => void
|
|
||||||
}) {
|
|
||||||
const pickedFiles = useAtomValue(pickedFilesAtom)
|
|
||||||
const hasUploadErrors = useAtomValue(hasFileUploadsErrorAtom)
|
|
||||||
const fileUploadCount = useAtomValue(fileUploadCountAtom)
|
|
||||||
const { isPending: isUploading } = useAtomValue(uploadFilesAtom)
|
|
||||||
|
|
||||||
let label: string
|
|
||||||
if (hasUploadErrors) {
|
|
||||||
label = "Retry failed uploads"
|
|
||||||
} else if (pickedFiles.length > 0) {
|
|
||||||
if (fileUploadCount > 0) {
|
|
||||||
label = "Done"
|
|
||||||
} else {
|
|
||||||
label = `Upload ${pickedFiles.length} files`
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
label = "Upload"
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Button onClick={onClick} disabled={isUploading} loading={isUploading}>
|
|
||||||
{label}
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function UploadFileDropContainer({ children }: React.PropsWithChildren) {
|
|
||||||
const [draggedFiles, setDraggedFiles] = useState<DataTransferItem[]>([])
|
|
||||||
const setPickedFiles = useSetAtom(pickedFilesAtom)
|
|
||||||
|
|
||||||
function handleDragOver(e: React.DragEvent) {
|
|
||||||
e.preventDefault()
|
|
||||||
const items = Array.from(e.dataTransfer.items)
|
|
||||||
const draggedFiles = []
|
|
||||||
for (const item of items) {
|
|
||||||
if (item.kind === "file") {
|
|
||||||
draggedFiles.push(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setDraggedFiles(draggedFiles)
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDragLeave() {
|
|
||||||
setDraggedFiles([])
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDrop(e: React.DragEvent) {
|
|
||||||
e.preventDefault()
|
|
||||||
const items = Array.from(e.dataTransfer.items)
|
|
||||||
const droppedFiles: PickedFile[] = []
|
|
||||||
for (const item of items) {
|
|
||||||
const file = item.getAsFile()
|
|
||||||
if (file) {
|
|
||||||
droppedFiles.push({
|
|
||||||
id: nanoid(),
|
|
||||||
file,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setPickedFiles((prev) => [...prev, ...droppedFiles])
|
|
||||||
setDraggedFiles([])
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section
|
|
||||||
onDragOver={handleDragOver}
|
|
||||||
onDragLeave={handleDragLeave}
|
|
||||||
onDrop={handleDrop}
|
|
||||||
aria-label="File drop area"
|
|
||||||
className="relative"
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
{draggedFiles.length > 0 ? (
|
|
||||||
<div className="border border-accent bg-primary text-primary-foreground absolute inset-0 rounded flex flex-col items-center justify-center text-sm space-y-1">
|
|
||||||
<FilePlus2Icon className="animate-bounce" />
|
|
||||||
<p>Drop {draggedFiles.length} files here</p>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</section>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// tag: uploadfilearea area fileuploadarea
|
|
||||||
function UploadFileArea({ onClick }: { onClick: () => void }) {
|
|
||||||
const [pickedFiles, setPickedFiles] = useAtom(pickedFilesAtom)
|
|
||||||
|
|
||||||
function removeSelectedFile(file: PickedFile) {
|
|
||||||
setPickedFiles((prev) => prev.filter((f) => f.id !== file.id))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pickedFiles.length > 0) {
|
|
||||||
return (
|
|
||||||
<PickedFilesList
|
|
||||||
pickedFiles={pickedFiles}
|
|
||||||
onRemoveFile={removeSelectedFile}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="w-full h-48 border-2 rounded border-dashed border-border flex flex-col items-center justify-center text-muted-foreground text-sm space-y-1 hover:bg-muted transition-all hover:border-solid"
|
|
||||||
onClick={onClick}
|
|
||||||
>
|
|
||||||
<UploadCloudIcon />
|
|
||||||
<span>Click to select files or drag and drop them here</span>
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function PickedFilesList({
|
|
||||||
pickedFiles,
|
|
||||||
onRemoveFile,
|
|
||||||
}: {
|
|
||||||
pickedFiles: PickedFile[]
|
|
||||||
onRemoveFile: (file: PickedFile) => void
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<ul className="min-h-48 border border-border rounded bg-card text-sm">
|
|
||||||
{pickedFiles.map((file: PickedFile) => (
|
|
||||||
<PickedFileItem
|
|
||||||
key={file.id}
|
|
||||||
file={file}
|
|
||||||
onRemove={onRemoveFile}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function PickedFileItem({
|
|
||||||
file: pickedFile,
|
|
||||||
onRemove,
|
|
||||||
}: {
|
|
||||||
file: PickedFile
|
|
||||||
onRemove: (file: PickedFile) => void
|
|
||||||
}) {
|
|
||||||
const fileUploadAtom = fileUploadStatusAtomFamily(pickedFile.id)
|
|
||||||
const fileUpload = useAtomValue(fileUploadAtom)
|
|
||||||
console.log("fileUpload", fileUpload)
|
|
||||||
const { file, id } = pickedFile
|
|
||||||
|
|
||||||
let statusIndicator: React.ReactNode
|
|
||||||
if (!fileUpload) {
|
|
||||||
statusIndicator = (
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
onClick={() => onRemove(pickedFile)}
|
|
||||||
>
|
|
||||||
<XIcon className="size-4" />
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
switch (fileUpload.kind) {
|
|
||||||
case FileUploadStatusKind.InProgress:
|
|
||||||
statusIndicator = (
|
|
||||||
<Progress
|
|
||||||
className="max-w-20"
|
|
||||||
value={fileUpload.progress * 100}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
break
|
|
||||||
case FileUploadStatusKind.Error:
|
|
||||||
statusIndicator = (
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger>
|
|
||||||
<CircleAlertIcon className="pr-2 text-destructive" />
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<p>
|
|
||||||
Failed to upload file:{" "}
|
|
||||||
{formatError(fileUpload.error)}
|
|
||||||
</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
)
|
|
||||||
break
|
|
||||||
case FileUploadStatusKind.Success:
|
|
||||||
statusIndicator = (
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger>
|
|
||||||
<CircleCheckIcon className="pr-2 text-green-500" />
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<p>File uploaded</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<li
|
|
||||||
className="pl-3 pr-1 py-0.5 h-8 hover:bg-muted flex justify-between items-center border-b border-border"
|
|
||||||
key={id}
|
|
||||||
>
|
|
||||||
<p>{file.name} </p>
|
|
||||||
{statusIndicator}
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import { api } from "@fileone/convex/api"
|
|
||||||
import type { Doc, Id } from "@fileone/convex/dataModel"
|
|
||||||
import { useMutation as useConvexMutation } from "convex/react"
|
|
||||||
import { useCallback } from "react"
|
|
||||||
|
|
||||||
function useUploadFile({
|
|
||||||
targetDirectory,
|
|
||||||
}: {
|
|
||||||
targetDirectory: Doc<"directories">
|
|
||||||
}) {
|
|
||||||
const generateUploadUrl = useConvexMutation(api.files.generateUploadUrl)
|
|
||||||
const saveFile = useConvexMutation(api.files.saveFile)
|
|
||||||
|
|
||||||
async function upload({
|
|
||||||
file,
|
|
||||||
onStart,
|
|
||||||
onProgress,
|
|
||||||
}: {
|
|
||||||
file: File
|
|
||||||
onStart: (xhr: XMLHttpRequest) => void
|
|
||||||
onProgress: (progress: number) => void
|
|
||||||
}) {
|
|
||||||
const uploadUrl = await generateUploadUrl()
|
|
||||||
|
|
||||||
return new Promise<{ storageId: Id<"_storage"> }>((resolve, reject) => {
|
|
||||||
const xhr = new XMLHttpRequest()
|
|
||||||
xhr.upload.addEventListener("progress", (e) => {
|
|
||||||
onProgress(e.loaded / e.total)
|
|
||||||
})
|
|
||||||
xhr.upload.addEventListener("error", reject)
|
|
||||||
xhr.addEventListener("load", () => {
|
|
||||||
resolve(
|
|
||||||
xhr.response as {
|
|
||||||
storageId: Id<"_storage">
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
xhr.open("POST", uploadUrl)
|
|
||||||
xhr.responseType = "json"
|
|
||||||
xhr.setRequestHeader("Content-Type", file.type)
|
|
||||||
xhr.send(file)
|
|
||||||
onStart(xhr)
|
|
||||||
}).then(({ storageId }) =>
|
|
||||||
saveFile({
|
|
||||||
storageId,
|
|
||||||
name: file.name,
|
|
||||||
size: file.size,
|
|
||||||
mimeType: file.type,
|
|
||||||
directoryId: targetDirectory._id,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return useCallback(upload, [])
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useUploadFile
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import * as React from "react"
|
|
||||||
|
|
||||||
const MOBILE_BREAKPOINT = 768
|
|
||||||
|
|
||||||
export function useIsMobile() {
|
|
||||||
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
|
|
||||||
undefined,
|
|
||||||
)
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
|
|
||||||
const onChange = () => {
|
|
||||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
||||||
}
|
|
||||||
mql.addEventListener("change", onChange)
|
|
||||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
||||||
return () => mql.removeEventListener("change", onChange)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return !!isMobile
|
|
||||||
}
|
|
||||||
@@ -1,431 +0,0 @@
|
|||||||
import { api } from "@fileone/convex/api"
|
|
||||||
import type { Doc, Id } from "@fileone/convex/dataModel"
|
|
||||||
import {
|
|
||||||
type FileSystemItem,
|
|
||||||
newFileSystemHandle,
|
|
||||||
type OpenedFile,
|
|
||||||
} from "@fileone/convex/filesystem"
|
|
||||||
import { useMutation } from "@tanstack/react-query"
|
|
||||||
import { createFileRoute } from "@tanstack/react-router"
|
|
||||||
import type { Row, Table } from "@tanstack/react-table"
|
|
||||||
import {
|
|
||||||
useMutation as useContextMutation,
|
|
||||||
useMutation as useConvexMutation,
|
|
||||||
useQuery as useConvexQuery,
|
|
||||||
} from "convex/react"
|
|
||||||
import { atom, useAtom, useAtomValue, useSetAtom, useStore } from "jotai"
|
|
||||||
import {
|
|
||||||
ChevronDownIcon,
|
|
||||||
PlusIcon,
|
|
||||||
TextCursorInputIcon,
|
|
||||||
TrashIcon,
|
|
||||||
} from "lucide-react"
|
|
||||||
import { useCallback, useContext } from "react"
|
|
||||||
import { toast } from "sonner"
|
|
||||||
import { DirectoryIcon } from "@/components/icons/directory-icon"
|
|
||||||
import { TextFileIcon } from "@/components/icons/text-file-icon"
|
|
||||||
import { Button } from "@/components/ui/button"
|
|
||||||
import {
|
|
||||||
ContextMenu,
|
|
||||||
ContextMenuContent,
|
|
||||||
ContextMenuItem,
|
|
||||||
ContextMenuTrigger,
|
|
||||||
} from "@/components/ui/context-menu"
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/components/ui/dropdown-menu"
|
|
||||||
import { WithAtom } from "@/components/with-atom"
|
|
||||||
import { backgroundTaskProgressAtom } from "@/dashboard/state"
|
|
||||||
import { DirectoryPageContext } from "@/directories/directory-page/context"
|
|
||||||
import { DirectoryContentTable } from "@/directories/directory-page/directory-content-table"
|
|
||||||
import { DirectoryPageSkeleton } from "@/directories/directory-page/directory-page-skeleton"
|
|
||||||
import { NewDirectoryDialog } from "@/directories/directory-page/new-directory-dialog"
|
|
||||||
import { RenameFileDialog } from "@/directories/directory-page/rename-file-dialog"
|
|
||||||
import { DirectoryPathBreadcrumb } from "@/directories/directory-path-breadcrumb"
|
|
||||||
import { FilePreviewDialog } from "@/files/file-preview-dialog"
|
|
||||||
import { inProgressFileUploadCountAtom } from "@/files/store"
|
|
||||||
import { UploadFileDialog } from "@/files/upload-file-dialog"
|
|
||||||
import type { FileDragInfo } from "@/files/use-file-drop"
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
|
||||||
"/_authenticated/_sidebar-layout/directories/$directoryId",
|
|
||||||
)({
|
|
||||||
component: RouteComponent,
|
|
||||||
})
|
|
||||||
|
|
||||||
enum DialogKind {
|
|
||||||
NewDirectory = "NewDirectory",
|
|
||||||
UploadFile = "UploadFile",
|
|
||||||
}
|
|
||||||
|
|
||||||
type NewDirectoryDialogData = {
|
|
||||||
kind: DialogKind.NewDirectory
|
|
||||||
}
|
|
||||||
|
|
||||||
type UploadFileDialogData = {
|
|
||||||
kind: DialogKind.UploadFile
|
|
||||||
directory: Doc<"directories">
|
|
||||||
}
|
|
||||||
|
|
||||||
type ActiveDialogData = NewDirectoryDialogData | UploadFileDialogData
|
|
||||||
|
|
||||||
// MARK: atoms
|
|
||||||
const contextMenuTargetItemsAtom = atom<FileSystemItem[]>([])
|
|
||||||
const activeDialogDataAtom = atom<ActiveDialogData | null>(null)
|
|
||||||
const fileDragInfoAtom = atom<FileDragInfo | null>(null)
|
|
||||||
const optimisticDeletedItemsAtom = atom(
|
|
||||||
new Set<Id<"files"> | Id<"directories">>(),
|
|
||||||
)
|
|
||||||
const openedFileAtom = atom<OpenedFile | null>(null)
|
|
||||||
const itemBeingRenamedAtom = atom<{
|
|
||||||
originalItem: FileSystemItem
|
|
||||||
name: string
|
|
||||||
} | null>(null)
|
|
||||||
|
|
||||||
// MARK: page entry
|
|
||||||
function RouteComponent() {
|
|
||||||
const { directoryId } = Route.useParams()
|
|
||||||
const rootDirectory = useConvexQuery(api.files.fetchRootDirectory)
|
|
||||||
const directory = useConvexQuery(api.files.fetchDirectory, {
|
|
||||||
directoryId,
|
|
||||||
})
|
|
||||||
const directoryContent = useConvexQuery(
|
|
||||||
api.filesystem.fetchDirectoryContent,
|
|
||||||
{
|
|
||||||
directoryId,
|
|
||||||
trashed: false,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
const directoryUrlById = useCallback(
|
|
||||||
(directoryId: Id<"directories">) => `/directories/${directoryId}`,
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!directory || !directoryContent || !rootDirectory) {
|
|
||||||
return <DirectoryPageSkeleton />
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DirectoryPageContext
|
|
||||||
value={{ rootDirectory, directory, directoryContent }}
|
|
||||||
>
|
|
||||||
<header className="flex py-2 shrink-0 items-center gap-2 border-b px-4 w-full">
|
|
||||||
<DirectoryPathBreadcrumb
|
|
||||||
directory={directory}
|
|
||||||
rootLabel="All Files"
|
|
||||||
directoryUrlFn={directoryUrlById}
|
|
||||||
fileDragInfoAtom={fileDragInfoAtom}
|
|
||||||
/>
|
|
||||||
<div className="ml-auto flex flex-row gap-2">
|
|
||||||
<NewDirectoryItemDropdown />
|
|
||||||
<UploadFileButton />
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
{/* DirectoryContentContextMenu must wrap div instead of DirectoryContentTable, otherwise radix will throw "event.preventDefault is not a function" error, idk why */}
|
|
||||||
<DirectoryContentContextMenu>
|
|
||||||
<div className="w-full">
|
|
||||||
<_DirectoryContentTable />
|
|
||||||
</div>
|
|
||||||
</DirectoryContentContextMenu>
|
|
||||||
|
|
||||||
<WithAtom atom={activeDialogDataAtom}>
|
|
||||||
{(data, setData) => (
|
|
||||||
<>
|
|
||||||
<NewDirectoryDialog
|
|
||||||
open={data?.kind === DialogKind.NewDirectory}
|
|
||||||
directoryId={directory._id}
|
|
||||||
onOpenChange={(open) => {
|
|
||||||
if (!open) {
|
|
||||||
setData(null)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{data?.kind === DialogKind.UploadFile && (
|
|
||||||
<UploadFileDialog
|
|
||||||
targetDirectory={data.directory}
|
|
||||||
onClose={() => setData(null)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</WithAtom>
|
|
||||||
|
|
||||||
<WithAtom atom={itemBeingRenamedAtom}>
|
|
||||||
{(itemBeingRenamed, setItemBeingRenamed) => {
|
|
||||||
if (!itemBeingRenamed) return null
|
|
||||||
return (
|
|
||||||
<RenameFileDialog
|
|
||||||
item={itemBeingRenamed.originalItem}
|
|
||||||
onRenameSuccess={() => {
|
|
||||||
toast.success("File renamed successfully")
|
|
||||||
setItemBeingRenamed(null)
|
|
||||||
}}
|
|
||||||
onClose={() => setItemBeingRenamed(null)}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</WithAtom>
|
|
||||||
|
|
||||||
<WithAtom atom={openedFileAtom}>
|
|
||||||
{(openedFile, setOpenedFile) => {
|
|
||||||
if (!openedFile) return null
|
|
||||||
return (
|
|
||||||
<FilePreviewDialog
|
|
||||||
openedFile={openedFile}
|
|
||||||
onClose={() => setOpenedFile(null)}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</WithAtom>
|
|
||||||
</DirectoryPageContext>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: directory table
|
|
||||||
|
|
||||||
function _DirectoryContentTable() {
|
|
||||||
const optimisticDeletedItems = useAtomValue(optimisticDeletedItemsAtom)
|
|
||||||
const setOpenedFile = useSetAtom(openedFileAtom)
|
|
||||||
const setContextMenuTargetItems = useSetAtom(contextMenuTargetItemsAtom)
|
|
||||||
|
|
||||||
const { mutate: openFile } = useMutation({
|
|
||||||
mutationFn: useConvexMutation(api.filesystem.openFile),
|
|
||||||
onSuccess: (openedFile: OpenedFile) => {
|
|
||||||
console.log("openedFile", openedFile)
|
|
||||||
setOpenedFile(openedFile)
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
console.error(error)
|
|
||||||
toast.error("Failed to open file")
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const onTableOpenFile = (file: Doc<"files">) => {
|
|
||||||
openFile({ fileId: file._id })
|
|
||||||
}
|
|
||||||
|
|
||||||
const directoryUrlFn = useCallback(
|
|
||||||
(directory: Doc<"directories">) => `/directories/${directory._id}`,
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleContextMenuRequest = (
|
|
||||||
row: Row<FileSystemItem>,
|
|
||||||
table: Table<FileSystemItem>,
|
|
||||||
) => {
|
|
||||||
if (row.getIsSelected()) {
|
|
||||||
setContextMenuTargetItems(
|
|
||||||
table.getSelectedRowModel().rows.map((row) => row.original),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
setContextMenuTargetItems([row.original])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DirectoryContentTable
|
|
||||||
hiddenItems={optimisticDeletedItems}
|
|
||||||
directoryUrlFn={directoryUrlFn}
|
|
||||||
fileDragInfoAtom={fileDragInfoAtom}
|
|
||||||
onContextMenu={handleContextMenuRequest}
|
|
||||||
onOpenFile={onTableOpenFile}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================================
|
|
||||||
// MARK: ctx menu
|
|
||||||
|
|
||||||
// tags: ctxmenu contextmenu directorycontextmenu
|
|
||||||
function DirectoryContentContextMenu({
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children: React.ReactNode
|
|
||||||
}) {
|
|
||||||
const store = useStore()
|
|
||||||
const [target, setTarget] = useAtom(contextMenuTargetItemsAtom)
|
|
||||||
const setOptimisticDeletedItems = useSetAtom(optimisticDeletedItemsAtom)
|
|
||||||
const setBackgroundTaskProgress = useSetAtom(backgroundTaskProgressAtom)
|
|
||||||
const moveToTrashMutation = useContextMutation(api.filesystem.moveToTrash)
|
|
||||||
|
|
||||||
const { mutate: moveToTrash } = useMutation({
|
|
||||||
mutationFn: moveToTrashMutation,
|
|
||||||
onMutate: ({ handles }) => {
|
|
||||||
setBackgroundTaskProgress({
|
|
||||||
label: "Moving items to trash…",
|
|
||||||
})
|
|
||||||
setOptimisticDeletedItems(
|
|
||||||
(prev) =>
|
|
||||||
new Set([...prev, ...handles.map((handle) => handle.id)]),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onSuccess: ({ deleted, errors }, { handles }) => {
|
|
||||||
setBackgroundTaskProgress(null)
|
|
||||||
setOptimisticDeletedItems((prev) => {
|
|
||||||
const newSet = new Set(prev)
|
|
||||||
for (const handle of handles) {
|
|
||||||
newSet.delete(handle.id)
|
|
||||||
}
|
|
||||||
return newSet
|
|
||||||
})
|
|
||||||
if (errors.length === 0 && deleted.length === handles.length) {
|
|
||||||
toast.success(`Moved ${handles.length} items to trash`)
|
|
||||||
} else if (errors.length === handles.length) {
|
|
||||||
toast.error("Failed to move to trash")
|
|
||||||
} else {
|
|
||||||
toast.info(
|
|
||||||
`Moved ${deleted.length} items to trash; failed to move ${errors.length} items`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onError: (_err, { handles }) => {
|
|
||||||
setOptimisticDeletedItems((prev) => {
|
|
||||||
const newSet = new Set(prev)
|
|
||||||
for (const handle of handles) {
|
|
||||||
newSet.delete(handle.id)
|
|
||||||
}
|
|
||||||
return newSet
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleDelete = () => {
|
|
||||||
const selectedItems = store.get(contextMenuTargetItemsAtom)
|
|
||||||
if (selectedItems.length > 0) {
|
|
||||||
moveToTrash({
|
|
||||||
handles: selectedItems.map(newFileSystemHandle),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ContextMenu
|
|
||||||
onOpenChange={(open) => {
|
|
||||||
if (!open) {
|
|
||||||
setTarget([])
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ContextMenuTrigger asChild>{children}</ContextMenuTrigger>
|
|
||||||
{target.length > 0 && (
|
|
||||||
<ContextMenuContent>
|
|
||||||
<RenameMenuItem />
|
|
||||||
<ContextMenuItem
|
|
||||||
variant="destructive"
|
|
||||||
onClick={handleDelete}
|
|
||||||
>
|
|
||||||
<TrashIcon />
|
|
||||||
Move to trash
|
|
||||||
</ContextMenuItem>
|
|
||||||
</ContextMenuContent>
|
|
||||||
)}
|
|
||||||
</ContextMenu>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function RenameMenuItem() {
|
|
||||||
const store = useStore()
|
|
||||||
const target = useAtomValue(contextMenuTargetItemsAtom)
|
|
||||||
const setItemBeingRenamed = useSetAtom(itemBeingRenamedAtom)
|
|
||||||
|
|
||||||
const handleRename = () => {
|
|
||||||
const selectedItems = store.get(contextMenuTargetItemsAtom)
|
|
||||||
if (selectedItems.length === 1) {
|
|
||||||
// biome-ignore lint/style/noNonNullAssertion: length is checked
|
|
||||||
const selectedItem = selectedItems[0]!
|
|
||||||
setItemBeingRenamed({
|
|
||||||
originalItem: selectedItem,
|
|
||||||
name: selectedItem.doc.name,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only render if exactly one item is selected
|
|
||||||
if (target.length !== 1) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ContextMenuItem onClick={handleRename}>
|
|
||||||
<TextCursorInputIcon />
|
|
||||||
Rename
|
|
||||||
</ContextMenuItem>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================================
|
|
||||||
|
|
||||||
// tags: upload, uploadfile, uploadfilebutton, fileupload, fileuploadbutton
|
|
||||||
function UploadFileButton() {
|
|
||||||
const { directory } = useContext(DirectoryPageContext)
|
|
||||||
const setActiveDialogData = useSetAtom(activeDialogDataAtom)
|
|
||||||
const inProgressFileUploadCount = useAtomValue(
|
|
||||||
inProgressFileUploadCountAtom,
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleClick = () => {
|
|
||||||
setActiveDialogData({
|
|
||||||
kind: DialogKind.UploadFile,
|
|
||||||
directory: directory,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inProgressFileUploadCount > 0) {
|
|
||||||
return (
|
|
||||||
<Button size="sm" type="button" loading onClick={handleClick}>
|
|
||||||
Uploading {inProgressFileUploadCount} files
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Button size="sm" type="button" onClick={handleClick}>
|
|
||||||
Upload files
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function NewDirectoryItemDropdown() {
|
|
||||||
const [activeDialogData, setActiveDialogData] =
|
|
||||||
useAtom(activeDialogDataAtom)
|
|
||||||
|
|
||||||
const addNewDirectory = () => {
|
|
||||||
setActiveDialogData({
|
|
||||||
kind: DialogKind.NewDirectory,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCloseAutoFocus = (event: Event) => {
|
|
||||||
// If we just created a new item, prevent the dropdown from restoring focus to the trigger
|
|
||||||
if (activeDialogData) {
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button size="sm" type="button" variant="outline">
|
|
||||||
<PlusIcon className="size-4" />
|
|
||||||
New
|
|
||||||
<ChevronDownIcon className="pl-1 size-4 shrink-0" />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent onCloseAutoFocus={handleCloseAutoFocus}>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<TextFileIcon />
|
|
||||||
Text file
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem onClick={addNewDirectory}>
|
|
||||||
<DirectoryIcon />
|
|
||||||
Directory
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,411 +0,0 @@
|
|||||||
import { api } from "@fileone/convex/api"
|
|
||||||
import type { Doc, Id } from "@fileone/convex/dataModel"
|
|
||||||
import {
|
|
||||||
type FileSystemItem,
|
|
||||||
FileType,
|
|
||||||
newFileSystemHandle,
|
|
||||||
} from "@fileone/convex/filesystem"
|
|
||||||
import { useMutation } from "@tanstack/react-query"
|
|
||||||
import { createFileRoute } from "@tanstack/react-router"
|
|
||||||
import type { Row, Table } from "@tanstack/react-table"
|
|
||||||
import {
|
|
||||||
useMutation as useConvexMutation,
|
|
||||||
useQuery as useConvexQuery,
|
|
||||||
} from "convex/react"
|
|
||||||
import { atom, useAtom, useSetAtom, useStore } from "jotai"
|
|
||||||
import { ShredderIcon, TrashIcon, UndoIcon } from "lucide-react"
|
|
||||||
import { useCallback, useContext } from "react"
|
|
||||||
import { toast } from "sonner"
|
|
||||||
import { Button } from "@/components/ui/button"
|
|
||||||
import {
|
|
||||||
ContextMenu,
|
|
||||||
ContextMenuContent,
|
|
||||||
ContextMenuItem,
|
|
||||||
ContextMenuTrigger,
|
|
||||||
} from "@/components/ui/context-menu"
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogClose,
|
|
||||||
DialogContent,
|
|
||||||
DialogFooter,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
} from "@/components/ui/dialog"
|
|
||||||
import { WithAtom } from "@/components/with-atom"
|
|
||||||
import { DirectoryPageContext } from "@/directories/directory-page/context"
|
|
||||||
import { DirectoryContentTable } from "@/directories/directory-page/directory-content-table"
|
|
||||||
import { DirectoryPageSkeleton } from "@/directories/directory-page/directory-page-skeleton"
|
|
||||||
import { DirectoryPathBreadcrumb } from "@/directories/directory-path-breadcrumb"
|
|
||||||
import { FilePreviewDialog } from "@/files/file-preview-dialog"
|
|
||||||
import type { FileDragInfo } from "@/files/use-file-drop"
|
|
||||||
import { backgroundTaskProgressAtom } from "../../../dashboard/state"
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
|
||||||
"/_authenticated/_sidebar-layout/trash/directories/$directoryId",
|
|
||||||
)({
|
|
||||||
component: RouteComponent,
|
|
||||||
})
|
|
||||||
|
|
||||||
enum ActiveDialogKind {
|
|
||||||
DeleteConfirmation = "DeleteConfirmation",
|
|
||||||
EmptyTrashConfirmation = "EmptyTrashConfirmation",
|
|
||||||
}
|
|
||||||
|
|
||||||
const contextMenuTargetItemsAtom = atom<FileSystemItem[]>([])
|
|
||||||
const fileDragInfoAtom = atom<FileDragInfo | null>(null)
|
|
||||||
const activeDialogAtom = atom<ActiveDialogKind | null>(null)
|
|
||||||
const openedFileAtom = atom<Doc<"files"> | null>(null)
|
|
||||||
const optimisticRemovedItemsAtom = atom(
|
|
||||||
new Set<Id<"files"> | Id<"directories">>(),
|
|
||||||
)
|
|
||||||
|
|
||||||
function RouteComponent() {
|
|
||||||
const { directoryId } = Route.useParams()
|
|
||||||
const rootDirectory = useConvexQuery(api.files.fetchRootDirectory)
|
|
||||||
const directory = useConvexQuery(api.files.fetchDirectory, {
|
|
||||||
directoryId,
|
|
||||||
})
|
|
||||||
const directoryContent = useConvexQuery(
|
|
||||||
api.filesystem.fetchDirectoryContent,
|
|
||||||
{
|
|
||||||
directoryId,
|
|
||||||
trashed: true,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
const setContextMenuTargetItems = useSetAtom(contextMenuTargetItemsAtom)
|
|
||||||
const setOpenedFile = useSetAtom(openedFileAtom)
|
|
||||||
|
|
||||||
const directoryUrlFn = useCallback(
|
|
||||||
(directory: Doc<"directories">) =>
|
|
||||||
`/trash/directories/${directory._id}`,
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
|
|
||||||
const directoryUrlById = useCallback(
|
|
||||||
(directoryId: Id<"directories">) => `/trash/directories/${directoryId}`,
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!directory || !directoryContent || !rootDirectory) {
|
|
||||||
return <DirectoryPageSkeleton />
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleContextMenuRequest = (
|
|
||||||
row: Row<FileSystemItem>,
|
|
||||||
table: Table<FileSystemItem>,
|
|
||||||
) => {
|
|
||||||
if (row.getIsSelected()) {
|
|
||||||
setContextMenuTargetItems(
|
|
||||||
table.getSelectedRowModel().rows.map((row) => row.original),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
setContextMenuTargetItems([row.original])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DirectoryPageContext
|
|
||||||
value={{ rootDirectory, directory, directoryContent }}
|
|
||||||
>
|
|
||||||
<header className="flex py-2 shrink-0 items-center gap-2 border-b px-4 w-full">
|
|
||||||
<DirectoryPathBreadcrumb
|
|
||||||
directory={directory}
|
|
||||||
rootLabel="Trash"
|
|
||||||
directoryUrlFn={directoryUrlById}
|
|
||||||
/>
|
|
||||||
<div className="ml-auto flex flex-row gap-2">
|
|
||||||
<EmptyTrashButton />
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<TableContextMenu>
|
|
||||||
<div className="w-full">
|
|
||||||
<WithAtom atom={optimisticRemovedItemsAtom}>
|
|
||||||
{(optimisticRemovedItems) => (
|
|
||||||
<DirectoryContentTable
|
|
||||||
hiddenItems={optimisticRemovedItems}
|
|
||||||
directoryUrlFn={directoryUrlFn}
|
|
||||||
fileDragInfoAtom={fileDragInfoAtom}
|
|
||||||
onContextMenu={handleContextMenuRequest}
|
|
||||||
onOpenFile={setOpenedFile}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</WithAtom>
|
|
||||||
</div>
|
|
||||||
</TableContextMenu>
|
|
||||||
|
|
||||||
<DeleteConfirmationDialog />
|
|
||||||
<EmptyTrashConfirmationDialog />
|
|
||||||
|
|
||||||
<WithAtom atom={openedFileAtom}>
|
|
||||||
{(openedFile, setOpenedFile) => {
|
|
||||||
if (!openedFile) return null
|
|
||||||
return (
|
|
||||||
<FilePreviewDialog
|
|
||||||
file={openedFile}
|
|
||||||
onClose={() => setOpenedFile(null)}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</WithAtom>
|
|
||||||
</DirectoryPageContext>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function TableContextMenu({ children }: React.PropsWithChildren) {
|
|
||||||
const setActiveDialog = useSetAtom(activeDialogAtom)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ContextMenu>
|
|
||||||
<ContextMenuTrigger asChild>{children}</ContextMenuTrigger>
|
|
||||||
<ContextMenuContent>
|
|
||||||
<RestoreContextMenuItem />
|
|
||||||
<ContextMenuItem
|
|
||||||
variant="destructive"
|
|
||||||
onClick={() => {
|
|
||||||
setActiveDialog(ActiveDialogKind.DeleteConfirmation)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ShredderIcon />
|
|
||||||
Delete permanently
|
|
||||||
</ContextMenuItem>
|
|
||||||
</ContextMenuContent>
|
|
||||||
</ContextMenu>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function RestoreContextMenuItem() {
|
|
||||||
const store = useStore()
|
|
||||||
const setOptimisticRemovedItems = useSetAtom(optimisticRemovedItemsAtom)
|
|
||||||
const restoreItemsMutation = useConvexMutation(api.filesystem.restoreItems)
|
|
||||||
|
|
||||||
const { mutate: restoreItems } = useMutation({
|
|
||||||
mutationFn: restoreItemsMutation,
|
|
||||||
onMutate: ({ handles }) => {
|
|
||||||
setBackgroundTaskProgress({
|
|
||||||
label: "Restoring items…",
|
|
||||||
})
|
|
||||||
setOptimisticRemovedItems(
|
|
||||||
new Set(handles.map((handle) => handle.id)),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onSuccess: ({ restored, errors }) => {
|
|
||||||
setBackgroundTaskProgress(null)
|
|
||||||
if (errors.length === 0) {
|
|
||||||
if (restored.files > 0 && restored.directories > 0) {
|
|
||||||
toast.success(
|
|
||||||
`Restored ${restored.files} files and ${restored.directories} directories`,
|
|
||||||
)
|
|
||||||
} else if (restored.files > 0) {
|
|
||||||
toast.success(`Restored ${restored.files} files`)
|
|
||||||
} else if (restored.directories > 0) {
|
|
||||||
toast.success(
|
|
||||||
`Restored ${restored.directories} directories`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
toast.warning(
|
|
||||||
`Restored ${restored.files} files and ${restored.directories} directories; failed to restore ${errors.length} items`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onError: (_err, { handles }) => {
|
|
||||||
setOptimisticRemovedItems((prev) => {
|
|
||||||
const newSet = new Set(prev)
|
|
||||||
for (const handle of handles) {
|
|
||||||
newSet.delete(handle.id)
|
|
||||||
}
|
|
||||||
return newSet
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const setBackgroundTaskProgress = useSetAtom(backgroundTaskProgressAtom)
|
|
||||||
|
|
||||||
const onClick = () => {
|
|
||||||
const targetItems = store.get(contextMenuTargetItemsAtom)
|
|
||||||
restoreItems({
|
|
||||||
handles: targetItems.map(newFileSystemHandle),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ContextMenuItem onClick={onClick}>
|
|
||||||
<UndoIcon />
|
|
||||||
Restore
|
|
||||||
</ContextMenuItem>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function EmptyTrashButton() {
|
|
||||||
const setActiveDialog = useSetAtom(activeDialogAtom)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
type="button"
|
|
||||||
variant="destructive"
|
|
||||||
onClick={() => {
|
|
||||||
setActiveDialog(ActiveDialogKind.EmptyTrashConfirmation)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TrashIcon className="size-4" />
|
|
||||||
Empty trash
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function DeleteConfirmationDialog() {
|
|
||||||
const { rootDirectory } = useContext(DirectoryPageContext)
|
|
||||||
const [activeDialog, setActiveDialog] = useAtom(activeDialogAtom)
|
|
||||||
const [targetItems, setTargetItems] = useAtom(contextMenuTargetItemsAtom)
|
|
||||||
const setOptimisticRemovedItems = useSetAtom(optimisticRemovedItemsAtom)
|
|
||||||
|
|
||||||
const deletePermanentlyMutation = useConvexMutation(
|
|
||||||
api.filesystem.permanentlyDeleteItems,
|
|
||||||
)
|
|
||||||
const { mutate: deletePermanently, isPending: isDeleting } = useMutation({
|
|
||||||
mutationFn: deletePermanentlyMutation,
|
|
||||||
onMutate: ({ handles }) => {
|
|
||||||
setOptimisticRemovedItems(
|
|
||||||
(prev) =>
|
|
||||||
new Set([...prev, ...handles.map((handle) => handle.id)]),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onSuccess: ({ deleted, errors }, { handles }) => {
|
|
||||||
setOptimisticRemovedItems((prev) => {
|
|
||||||
const newSet = new Set(prev)
|
|
||||||
for (const handle of handles) {
|
|
||||||
newSet.delete(handle.id)
|
|
||||||
}
|
|
||||||
return newSet
|
|
||||||
})
|
|
||||||
if (errors.length === 0) {
|
|
||||||
toast.success(
|
|
||||||
`Deleted ${deleted.files} files and ${deleted.directories} directories`,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
toast.warning(
|
|
||||||
`Deleted ${deleted.files} files and ${deleted.directories} directories; failed to delete ${errors.length} items`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
setActiveDialog(null)
|
|
||||||
setTargetItems([])
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const onOpenChange = (open: boolean) => {
|
|
||||||
if (open) {
|
|
||||||
setActiveDialog(ActiveDialogKind.DeleteConfirmation)
|
|
||||||
} else {
|
|
||||||
setActiveDialog(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const confirmDelete = () => {
|
|
||||||
deletePermanently({
|
|
||||||
handles:
|
|
||||||
targetItems.length > 0
|
|
||||||
? targetItems.map(newFileSystemHandle)
|
|
||||||
: [
|
|
||||||
newFileSystemHandle({
|
|
||||||
kind: FileType.Directory,
|
|
||||||
doc: rootDirectory,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog
|
|
||||||
open={activeDialog === ActiveDialogKind.DeleteConfirmation}
|
|
||||||
onOpenChange={onOpenChange}
|
|
||||||
>
|
|
||||||
<DialogContent>
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>
|
|
||||||
Permanently delete {targetItems.length} items?
|
|
||||||
</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
{targetItems.length} items will be permanently deleted. They
|
|
||||||
will be IRRECOVERABLE.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<DialogFooter>
|
|
||||||
<DialogClose asChild>
|
|
||||||
<Button variant="outline" disabled={isDeleting}>
|
|
||||||
Go back
|
|
||||||
</Button>
|
|
||||||
</DialogClose>
|
|
||||||
<Button
|
|
||||||
variant="destructive"
|
|
||||||
onClick={confirmDelete}
|
|
||||||
disabled={isDeleting}
|
|
||||||
loading={isDeleting}
|
|
||||||
>
|
|
||||||
Yes, delete
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function EmptyTrashConfirmationDialog() {
|
|
||||||
const [activeDialog, setActiveDialog] = useAtom(activeDialogAtom)
|
|
||||||
|
|
||||||
const { mutate: emptyTrash, isPending: isEmptying } = useMutation({
|
|
||||||
mutationFn: useConvexMutation(api.filesystem.emptyTrash),
|
|
||||||
onSuccess: () => {
|
|
||||||
toast.success("Trash emptied successfully")
|
|
||||||
setActiveDialog(null)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
function onOpenChange(open: boolean) {
|
|
||||||
if (open) {
|
|
||||||
setActiveDialog(ActiveDialogKind.EmptyTrashConfirmation)
|
|
||||||
} else {
|
|
||||||
setActiveDialog(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function confirmEmpty() {
|
|
||||||
emptyTrash(undefined)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog
|
|
||||||
open={activeDialog === ActiveDialogKind.EmptyTrashConfirmation}
|
|
||||||
onOpenChange={onOpenChange}
|
|
||||||
>
|
|
||||||
<DialogContent>
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>Empty your trash?</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
All items in the trash will be permanently deleted. They
|
|
||||||
will be IRRECOVERABLE.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<DialogFooter>
|
|
||||||
<DialogClose asChild>
|
|
||||||
<Button variant="outline" disabled={isEmptying}>
|
|
||||||
No, go back
|
|
||||||
</Button>
|
|
||||||
</DialogClose>
|
|
||||||
<Button
|
|
||||||
variant="destructive"
|
|
||||||
onClick={confirmEmpty}
|
|
||||||
disabled={isEmptying}
|
|
||||||
loading={isEmptying}
|
|
||||||
>
|
|
||||||
Yes, empty trash
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
import { useMutation } from "@tanstack/react-query"
|
|
||||||
import { createFileRoute } from "@tanstack/react-router"
|
|
||||||
import { GalleryVerticalEnd } from "lucide-react"
|
|
||||||
import { Button } from "@/components/ui/button"
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardDescription,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
} from "@/components/ui/card"
|
|
||||||
import {
|
|
||||||
Field,
|
|
||||||
FieldDescription,
|
|
||||||
FieldGroup,
|
|
||||||
FieldLabel,
|
|
||||||
FieldSeparator,
|
|
||||||
} from "@/components/ui/field"
|
|
||||||
import { Input } from "@/components/ui/input"
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
import { type AuthErrorCode, authClient, BetterAuthError } from "../auth"
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/login")({
|
|
||||||
component: RouteComponent,
|
|
||||||
})
|
|
||||||
|
|
||||||
function RouteComponent() {
|
|
||||||
return (
|
|
||||||
<div className="bg-background flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
|
|
||||||
<div className="flex w-full max-w-lg flex-col gap-6">
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
className="flex items-center gap-2 self-center font-medium text-xl"
|
|
||||||
>
|
|
||||||
<div className="bg-primary text-primary-foreground flex size-6 items-center justify-center rounded-md">
|
|
||||||
<GalleryVerticalEnd className="size-4" />
|
|
||||||
</div>
|
|
||||||
Drexa
|
|
||||||
</a>
|
|
||||||
<LoginFormCard />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function LoginFormCard({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div className={cn("flex flex-col gap-6", className)} {...props}>
|
|
||||||
<Card>
|
|
||||||
<CardHeader className="text-center">
|
|
||||||
<CardTitle className="text-xl">Welcome back</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
Login with your Apple or Google account
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<LoginForm />
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
<FieldDescription className="px-6 text-center">
|
|
||||||
By clicking continue, you agree to our{" "}
|
|
||||||
<a href="#">Terms of Service</a> and{" "}
|
|
||||||
<a href="#">Privacy Policy</a>.
|
|
||||||
</FieldDescription>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function LoginForm() {
|
|
||||||
const {
|
|
||||||
mutate: signIn,
|
|
||||||
isPending,
|
|
||||||
error: signInError,
|
|
||||||
} = useMutation({
|
|
||||||
mutationFn: async ({
|
|
||||||
email,
|
|
||||||
password,
|
|
||||||
}: {
|
|
||||||
email: string
|
|
||||||
password: string
|
|
||||||
}) => {
|
|
||||||
const { data: signInData, error } = await authClient.signIn.email({
|
|
||||||
email,
|
|
||||||
password,
|
|
||||||
callbackURL: "/home",
|
|
||||||
rememberMe: true,
|
|
||||||
})
|
|
||||||
if (error) {
|
|
||||||
throw new BetterAuthError(error.code as AuthErrorCode)
|
|
||||||
}
|
|
||||||
return signInData
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
|
||||||
event.preventDefault()
|
|
||||||
const formData = new FormData(event.currentTarget)
|
|
||||||
signIn({
|
|
||||||
email: formData.get("email") as string,
|
|
||||||
password: formData.get("password") as string,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<FieldGroup>
|
|
||||||
<Field>
|
|
||||||
<Button
|
|
||||||
disabled={isPending}
|
|
||||||
variant="outline"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<title>Apple logo</title>
|
|
||||||
<path
|
|
||||||
d="M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Login with Apple
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
disabled={isPending}
|
|
||||||
variant="outline"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<title>Google logo</title>
|
|
||||||
<path
|
|
||||||
d="M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Login with Google
|
|
||||||
</Button>
|
|
||||||
</Field>
|
|
||||||
<FieldSeparator className="*:data-[slot=field-separator-content]:bg-card">
|
|
||||||
Or continue with
|
|
||||||
</FieldSeparator>
|
|
||||||
<Field>
|
|
||||||
<FieldLabel htmlFor="email">Email</FieldLabel>
|
|
||||||
<Input
|
|
||||||
disabled={isPending}
|
|
||||||
name="email"
|
|
||||||
type="email"
|
|
||||||
placeholder="m@example.com"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
<Field>
|
|
||||||
<div className="flex items-center">
|
|
||||||
<FieldLabel htmlFor="password">Password</FieldLabel>
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
className="ml-auto text-sm underline-offset-4 hover:underline"
|
|
||||||
>
|
|
||||||
Forgot your password?
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<Input
|
|
||||||
disabled={isPending}
|
|
||||||
name="password"
|
|
||||||
type="password"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
<Field>
|
|
||||||
<Button disabled={isPending} type="submit">
|
|
||||||
{isPending ? "Logging in…" : "Login"}
|
|
||||||
</Button>
|
|
||||||
<FieldDescription className="text-center">
|
|
||||||
Don't have an account? <a href="#">Sign up</a>
|
|
||||||
</FieldDescription>
|
|
||||||
</Field>
|
|
||||||
</FieldGroup>
|
|
||||||
</form>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,218 +0,0 @@
|
|||||||
import { useMutation } from "@tanstack/react-query"
|
|
||||||
import { createFileRoute, useNavigate } from "@tanstack/react-router"
|
|
||||||
import { GalleryVerticalEnd } from "lucide-react"
|
|
||||||
import type React from "react"
|
|
||||||
import { toast } from "sonner"
|
|
||||||
import { Button } from "@/components/ui/button"
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardDescription,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
} from "@/components/ui/card"
|
|
||||||
import {
|
|
||||||
Field,
|
|
||||||
FieldDescription,
|
|
||||||
FieldError,
|
|
||||||
FieldGroup,
|
|
||||||
FieldLabel,
|
|
||||||
} from "@/components/ui/field"
|
|
||||||
import { Input } from "@/components/ui/input"
|
|
||||||
import { type AuthErrorCode, authClient, BetterAuthError } from "../auth"
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/sign-up")({
|
|
||||||
component: SignupPage,
|
|
||||||
})
|
|
||||||
|
|
||||||
type SignUpParams = {
|
|
||||||
displayName: string
|
|
||||||
email: string
|
|
||||||
password: string
|
|
||||||
confirmPassword: string
|
|
||||||
}
|
|
||||||
|
|
||||||
class PasswordMismatchError extends Error {
|
|
||||||
constructor() {
|
|
||||||
super("Passwords do not match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function SignupPage() {
|
|
||||||
return (
|
|
||||||
<div className="bg-background flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
|
|
||||||
<div className="flex w-full max-w-xl flex-col gap-6">
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
className="flex items-center gap-2 self-center font-medium text-xl"
|
|
||||||
>
|
|
||||||
<div className="bg-primary text-primary-foreground flex size-6 items-center justify-center rounded-md">
|
|
||||||
<GalleryVerticalEnd className="size-4" />
|
|
||||||
</div>
|
|
||||||
Drexa
|
|
||||||
</a>
|
|
||||||
<SignUpFormContainer>
|
|
||||||
<SignupForm />
|
|
||||||
</SignUpFormContainer>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function SignUpFormContainer({ children }: React.PropsWithChildren) {
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col gap-6">
|
|
||||||
<Card>
|
|
||||||
<CardHeader className="text-center">
|
|
||||||
<CardTitle className="text-xl">
|
|
||||||
Create your account
|
|
||||||
</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
Enter your email below to create your account
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>{children}</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function SignupForm() {
|
|
||||||
const navigate = useNavigate()
|
|
||||||
const {
|
|
||||||
mutate: signUp,
|
|
||||||
isPending,
|
|
||||||
error: signUpError,
|
|
||||||
} = useMutation({
|
|
||||||
mutationFn: async (data: SignUpParams) => {
|
|
||||||
if (data.password !== data.confirmPassword) {
|
|
||||||
throw new PasswordMismatchError()
|
|
||||||
}
|
|
||||||
const { data: signUpData, error } = await authClient.signUp.email({
|
|
||||||
name: data.displayName,
|
|
||||||
email: data.email,
|
|
||||||
password: data.password,
|
|
||||||
})
|
|
||||||
if (error) {
|
|
||||||
throw new BetterAuthError(error.code as AuthErrorCode)
|
|
||||||
}
|
|
||||||
return signUpData
|
|
||||||
},
|
|
||||||
onSuccess: () => {
|
|
||||||
navigate({
|
|
||||||
to: "/",
|
|
||||||
replace: true,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
console.error(error)
|
|
||||||
toast.error("Unable to create your account")
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
|
||||||
event.preventDefault()
|
|
||||||
const formData = new FormData(event.currentTarget)
|
|
||||||
signUp({
|
|
||||||
displayName: formData.get("displayName") as string,
|
|
||||||
email: formData.get("email") as string,
|
|
||||||
password: formData.get("password") as string,
|
|
||||||
confirmPassword: formData.get("confirmPassword") as string,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let passwordFieldError = null
|
|
||||||
let emailFieldError = null
|
|
||||||
if (signUpError instanceof BetterAuthError) {
|
|
||||||
switch (signUpError.errorCode) {
|
|
||||||
case "PASSWORD_TOO_SHORT":
|
|
||||||
passwordFieldError =
|
|
||||||
"Password must be at least 8 characters long"
|
|
||||||
break
|
|
||||||
case "INVALID_EMAIL":
|
|
||||||
emailFieldError = "Invalid email address"
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
passwordFieldError = null
|
|
||||||
}
|
|
||||||
} else if (signUpError instanceof PasswordMismatchError) {
|
|
||||||
passwordFieldError = "Passwords do not match"
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<FieldGroup>
|
|
||||||
<Field>
|
|
||||||
<FieldLabel htmlFor="name">Your Name</FieldLabel>
|
|
||||||
<Input
|
|
||||||
disabled={isPending}
|
|
||||||
name="displayName"
|
|
||||||
type="text"
|
|
||||||
placeholder="John Doe"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<FieldDescription>
|
|
||||||
This is how you will be referred to on Drexa. You can
|
|
||||||
change this later.
|
|
||||||
</FieldDescription>
|
|
||||||
</Field>
|
|
||||||
<Field>
|
|
||||||
<FieldLabel htmlFor="email">Email</FieldLabel>
|
|
||||||
<Input
|
|
||||||
disabled={isPending}
|
|
||||||
name="email"
|
|
||||||
type="email"
|
|
||||||
placeholder="m@example.com"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
{emailFieldError ? (
|
|
||||||
<FieldError>{emailFieldError}</FieldError>
|
|
||||||
) : null}
|
|
||||||
</Field>
|
|
||||||
<Field>
|
|
||||||
<Field className="grid grid-rows-2 md:grid-rows-1 md:grid-cols-2 gap-4">
|
|
||||||
<Field>
|
|
||||||
<FieldLabel htmlFor="password">Password</FieldLabel>
|
|
||||||
<Input
|
|
||||||
disabled={isPending}
|
|
||||||
name="password"
|
|
||||||
type="password"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
<Field>
|
|
||||||
<FieldLabel htmlFor="confirm-password">
|
|
||||||
Confirm Password
|
|
||||||
</FieldLabel>
|
|
||||||
<Input
|
|
||||||
disabled={isPending}
|
|
||||||
name="confirmPassword"
|
|
||||||
type="password"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
</Field>
|
|
||||||
{passwordFieldError ? (
|
|
||||||
<FieldError>{passwordFieldError}</FieldError>
|
|
||||||
) : (
|
|
||||||
<FieldDescription>
|
|
||||||
Must be at least 8 characters long.
|
|
||||||
</FieldDescription>
|
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
<Field>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
loading={isPending}
|
|
||||||
disabled={isPending}
|
|
||||||
>
|
|
||||||
{isPending ? "Creating account…" : "Create Account"}
|
|
||||||
</Button>
|
|
||||||
<FieldDescription className="text-center">
|
|
||||||
Already have an account? <a href="/sign-in">Sign in</a>
|
|
||||||
</FieldDescription>
|
|
||||||
</Field>
|
|
||||||
</FieldGroup>
|
|
||||||
</form>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
10
apps/drive-web/src/vite-env.d.ts
vendored
10
apps/drive-web/src/vite-env.d.ts
vendored
@@ -1,10 +0,0 @@
|
|||||||
/// <reference types="vite/client" />
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface ImportMetaEnv {
|
|
||||||
readonly VITE_CONVEX_URL: string
|
|
||||||
readonly VITE_CONVEX_SITE_URL: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export {}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ES2022",
|
|
||||||
"lib": ["ES2023"],
|
|
||||||
"module": "ESNext",
|
|
||||||
"skipLibCheck": true,
|
|
||||||
|
|
||||||
/* Bundler mode */
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowImportingTsExtensions": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"moduleDetection": "force",
|
|
||||||
"noEmit": true,
|
|
||||||
|
|
||||||
/* Linting */
|
|
||||||
"strict": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"noUnusedParameters": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"noUncheckedSideEffectImports": true
|
|
||||||
},
|
|
||||||
"include": ["vite.config.ts"]
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import tailwindcss from "@tailwindcss/vite"
|
|
||||||
import { TanStackRouterVite } from "@tanstack/router-plugin/vite"
|
|
||||||
import react from "@vitejs/plugin-react"
|
|
||||||
import path from "path"
|
|
||||||
import { defineConfig } from "vite"
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [TanStackRouterVite(), react(), tailwindcss()],
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
"@": path.resolve(__dirname, "./src"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
server: {
|
|
||||||
port: 3000,
|
|
||||||
host: true,
|
|
||||||
fs: {
|
|
||||||
allow: [".."],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
optimizeDeps: {
|
|
||||||
include: ["convex/react", "convex-helpers"],
|
|
||||||
// Workaround for better-auth bug: https://github.com/better-auth/better-auth/issues/4457
|
|
||||||
// Vite's esbuild incorrectly transpiles better-call dependency causing 'super' keyword errors
|
|
||||||
exclude: ["better-auth", "@convex-dev/better-auth"],
|
|
||||||
esbuildOptions: {
|
|
||||||
target: "esnext",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
CONVEX_URL=
|
|
||||||
# api key used to auth with the convex backend
|
|
||||||
# use the drexa cli to generate an api key, then add the api key to the api key table via the convex dashboard
|
|
||||||
API_KEY=
|
|
||||||
34
apps/file-proxy/.gitignore
vendored
34
apps/file-proxy/.gitignore
vendored
@@ -1,34 +0,0 @@
|
|||||||
# dependencies (bun install)
|
|
||||||
node_modules
|
|
||||||
|
|
||||||
# output
|
|
||||||
out
|
|
||||||
dist
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# code coverage
|
|
||||||
coverage
|
|
||||||
*.lcov
|
|
||||||
|
|
||||||
# logs
|
|
||||||
logs
|
|
||||||
_.log
|
|
||||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
|
||||||
|
|
||||||
# dotenv environment variable files
|
|
||||||
.env
|
|
||||||
.env.development.local
|
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
.env.local
|
|
||||||
|
|
||||||
# caches
|
|
||||||
.eslintcache
|
|
||||||
.cache
|
|
||||||
*.tsbuildinfo
|
|
||||||
|
|
||||||
# IntelliJ based IDEs
|
|
||||||
.idea
|
|
||||||
|
|
||||||
# Finder (MacOS) folder config
|
|
||||||
.DS_Store
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# drive-file-proxy
|
|
||||||
|
|
||||||
To install dependencies:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun install
|
|
||||||
```
|
|
||||||
|
|
||||||
To run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun run index.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
This project was created using `bun init` in bun v1.3.0. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { createMiddleware } from "hono/factory"
|
|
||||||
|
|
||||||
export type ApiKeyContextVariable = {
|
|
||||||
apiKey: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiKeyMiddleware = createMiddleware<{ Variables: ApiKeyContextVariable }>(
|
|
||||||
async (c, next) => {
|
|
||||||
c.set("apiKey", process.env.API_KEY)
|
|
||||||
await next()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
export { apiKeyMiddleware }
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { ConvexHttpClient } from "convex/browser"
|
|
||||||
import { createMiddleware } from "hono/factory"
|
|
||||||
|
|
||||||
const _client = new ConvexHttpClient(process.env.CONVEX_URL)
|
|
||||||
|
|
||||||
export type ConvexContextVariables = {
|
|
||||||
convex: ConvexHttpClient
|
|
||||||
}
|
|
||||||
|
|
||||||
export const convexMiddleware = createMiddleware<{
|
|
||||||
Variables: ConvexContextVariables
|
|
||||||
}>(async (c, next) => {
|
|
||||||
c.var
|
|
||||||
c.set("convex", _client)
|
|
||||||
await next()
|
|
||||||
})
|
|
||||||
6
apps/file-proxy/env.d.ts
vendored
6
apps/file-proxy/env.d.ts
vendored
@@ -1,6 +0,0 @@
|
|||||||
declare module "bun" {
|
|
||||||
interface Env {
|
|
||||||
CONVEX_URL: string
|
|
||||||
API_KEY: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import { api } from "@fileone/convex/api"
|
|
||||||
import { newRouter } from "./router"
|
|
||||||
|
|
||||||
const r = newRouter().basePath("/files")
|
|
||||||
|
|
||||||
r.get(":shareToken", async (c) => {
|
|
||||||
const shareToken = c.req.param("shareToken")
|
|
||||||
if (!shareToken) {
|
|
||||||
return c.json({ error: "not found" }, 404)
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileShare = await c.var.convex.query(api.fileshare.findFileShare, {
|
|
||||||
apiKey: c.var.apiKey,
|
|
||||||
shareToken,
|
|
||||||
})
|
|
||||||
if (!fileShare) {
|
|
||||||
return c.json({ error: "not found" }, 404)
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileUrl = await c.var.convex.query(api.filesystem.getStorageUrl, {
|
|
||||||
apiKey: c.var.apiKey,
|
|
||||||
storageId: fileShare.storageId,
|
|
||||||
})
|
|
||||||
if (!fileUrl) {
|
|
||||||
return c.json({ error: "not found" }, 404)
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileResponse = await fetch(fileUrl)
|
|
||||||
if (!fileResponse.ok) {
|
|
||||||
return c.json({ error: "not found" }, 404)
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Response(fileResponse.body, {
|
|
||||||
status: fileResponse.status,
|
|
||||||
headers: fileResponse.headers,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
export { r as files }
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { Hono } from "hono"
|
|
||||||
import { apiKeyMiddleware } from "./auth"
|
|
||||||
import { convexMiddleware } from "./convex"
|
|
||||||
import { files } from "./files"
|
|
||||||
|
|
||||||
const app = new Hono()
|
|
||||||
|
|
||||||
app.use(convexMiddleware)
|
|
||||||
app.use(apiKeyMiddleware)
|
|
||||||
|
|
||||||
app.route("/", files)
|
|
||||||
|
|
||||||
export default {
|
|
||||||
port: 8081,
|
|
||||||
fetch: app.fetch,
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@drexa/file-proxy",
|
|
||||||
"module": "index.ts",
|
|
||||||
"type": "module",
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
|
||||||
"dev": "bun --hot run index.ts"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/bun": "latest"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"typescript": "^5"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@fileone/convex": "workspace:*",
|
|
||||||
"arktype": "^2.1.23",
|
|
||||||
"convex": "^1.28.0",
|
|
||||||
"hono": "^4.10.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { Hono } from "hono"
|
|
||||||
import type { ApiKeyContextVariable } from "./auth"
|
|
||||||
import type { ConvexContextVariables } from "./convex"
|
|
||||||
|
|
||||||
type ContextVariables = ConvexContextVariables & ApiKeyContextVariable
|
|
||||||
|
|
||||||
export function newRouter() {
|
|
||||||
return new Hono<{
|
|
||||||
Variables: ContextVariables
|
|
||||||
}>()
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
// Environment setup & latest features
|
|
||||||
"lib": ["ESNext"],
|
|
||||||
"target": "ESNext",
|
|
||||||
"module": "Preserve",
|
|
||||||
"moduleDetection": "force",
|
|
||||||
"jsx": "react-jsx",
|
|
||||||
"allowJs": true,
|
|
||||||
|
|
||||||
// Bundler mode
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowImportingTsExtensions": true,
|
|
||||||
"verbatimModuleSyntax": true,
|
|
||||||
"noEmit": true,
|
|
||||||
|
|
||||||
// Best practices
|
|
||||||
"strict": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"noUncheckedIndexedAccess": true,
|
|
||||||
"noImplicitOverride": true,
|
|
||||||
|
|
||||||
// Some stricter flags (disabled by default)
|
|
||||||
"noUnusedLocals": false,
|
|
||||||
"noUnusedParameters": false,
|
|
||||||
"noPropertyAccessFromIndexSignature": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
528
bun.lock
528
bun.lock
@@ -2,46 +2,39 @@
|
|||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"workspaces": {
|
"workspaces": {
|
||||||
"": {
|
"": {
|
||||||
"name": "fileone",
|
"name": "bun-react-template",
|
||||||
"dependencies": {
|
|
||||||
"@tailwindcss/vite": "^4.1.14",
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "2.2.4",
|
"@biomejs/biome": "2.2.4",
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
"convex": "^1.27.0",
|
"convex": "^1.27.0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"apps/cli": {
|
"packages/convex": {
|
||||||
"name": "@drexa/cli",
|
"name": "@fileone/convex",
|
||||||
"version": "0.1.0",
|
|
||||||
"bin": {
|
|
||||||
"drexa": "./index.ts",
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@drexa/auth": "workspace:*",
|
"@fileone/path": "workspace:*",
|
||||||
"chalk": "^5.3.0",
|
|
||||||
"commander": "^12.1.0",
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"peerDependencies": {
|
||||||
"@types/bun": "latest",
|
"convex": "^1.27.0",
|
||||||
|
"typescript": "^5",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
"packages/path": {
|
||||||
|
"name": "@fileone/path",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5",
|
"typescript": "^5",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"apps/drive-web": {
|
"packages/web": {
|
||||||
"name": "@fileone/web",
|
"name": "@fileone/web",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@convex-dev/better-auth": "^0.8.9",
|
"@convex-dev/workos": "^0.0.1",
|
||||||
"@fileone/convex": "workspace:*",
|
"@fileone/convex": "workspace:*",
|
||||||
"@radix-ui/react-checkbox": "^1.3.3",
|
"@radix-ui/react-checkbox": "^1.3.3",
|
||||||
"@radix-ui/react-context-menu": "^2.2.16",
|
"@radix-ui/react-context-menu": "^2.2.16",
|
||||||
"@radix-ui/react-dialog": "^1.1.15",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||||
"@radix-ui/react-label": "^2.1.7",
|
|
||||||
"@radix-ui/react-progress": "^1.1.7",
|
|
||||||
"@radix-ui/react-separator": "^1.1.7",
|
"@radix-ui/react-separator": "^1.1.7",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
"@radix-ui/react-tooltip": "^1.2.8",
|
"@radix-ui/react-tooltip": "^1.2.8",
|
||||||
@@ -49,18 +42,15 @@
|
|||||||
"@tanstack/react-router": "^1.131.41",
|
"@tanstack/react-router": "^1.131.41",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"@tanstack/router-devtools": "^1.131.42",
|
"@tanstack/router-devtools": "^1.131.42",
|
||||||
"better-auth": "1.3.8",
|
"@workos-inc/authkit-react": "^0.12.0",
|
||||||
|
"bun-plugin-tailwind": "latest",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"convex": "^1.27.0",
|
"convex": "^1.27.0",
|
||||||
"convex-helpers": "^0.1.104",
|
"convex-helpers": "^0.1.104",
|
||||||
"jotai": "^2.14.0",
|
"jotai": "^2.14.0",
|
||||||
"jotai-effect": "^2.1.3",
|
|
||||||
"jotai-scope": "^0.9.5",
|
|
||||||
"jotai-tanstack-query": "^0.11.0",
|
|
||||||
"lucide-react": "^0.544.0",
|
"lucide-react": "^0.544.0",
|
||||||
"motion": "^12.23.16",
|
"motion": "^12.23.16",
|
||||||
"nanoid": "^5.1.6",
|
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"react": "^19",
|
"react": "^19",
|
||||||
"react-dom": "^19",
|
"react-dom": "^19",
|
||||||
@@ -71,70 +61,12 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tanstack/router-cli": "^1.131.41",
|
"@tanstack/router-cli": "^1.131.41",
|
||||||
"@tanstack/router-plugin": "^1.133.13",
|
|
||||||
"@types/node": "^22.10.5",
|
|
||||||
"@types/react": "^19",
|
"@types/react": "^19",
|
||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "^19",
|
||||||
"@vitejs/plugin-react": "^5.0.4",
|
|
||||||
"vite": "^7.1.10",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"apps/file-proxy": {
|
|
||||||
"name": "@drexa/file-proxy",
|
|
||||||
"dependencies": {
|
|
||||||
"@fileone/convex": "workspace:*",
|
|
||||||
"arktype": "^2.1.23",
|
|
||||||
"convex": "^1.28.0",
|
|
||||||
"hono": "^4.10.1",
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/bun": "latest",
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"typescript": "^5",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"packages/auth": {
|
|
||||||
"name": "@drexa/auth",
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/bun": "latest",
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"typescript": "^5",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"packages/convex": {
|
|
||||||
"name": "@fileone/convex",
|
|
||||||
"dependencies": {
|
|
||||||
"@drexa/auth": "workspace:*",
|
|
||||||
"@fileone/path": "workspace:*",
|
|
||||||
"hash-wasm": "^4.12.0",
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@convex-dev/better-auth": "^0.8.9",
|
|
||||||
"better-auth": "1.3.8",
|
|
||||||
"convex": "^1.27.0",
|
|
||||||
"convex-helpers": "^0.1.104",
|
|
||||||
"typescript": "^5",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"packages/path": {
|
|
||||||
"name": "@fileone/path",
|
|
||||||
"peerDependencies": {
|
|
||||||
"typescript": "^5",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"overrides": {
|
|
||||||
"convex": "1.28.0",
|
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@ark/regex": ["@ark/regex@0.0.0", "", { "dependencies": { "@ark/util": "0.50.0" } }, "sha512-p4vsWnd/LRGOdGQglbwOguIVhPmCAf5UzquvnDoxqhhPWTP84wWgi1INea8MgJ4SnI2gp37f13oA4Waz9vwNYg=="],
|
|
||||||
|
|
||||||
"@ark/schema": ["@ark/schema@0.50.0", "", { "dependencies": { "@ark/util": "0.50.0" } }, "sha512-hfmP82GltBZDadIOeR3argKNlYYyB2wyzHp0eeAqAOFBQguglMV/S7Ip2q007bRtKxIMLDqFY6tfPie1dtssaQ=="],
|
|
||||||
|
|
||||||
"@ark/util": ["@ark/util@0.50.0", "", {}, "sha512-tIkgIMVRpkfXRQIEf0G2CJryZVtHVrqcWHMDa5QKo0OEEBu0tHkRSIMm4Ln8cd8Bn9TPZtvc/kE2Gma8RESPSg=="],
|
|
||||||
|
|
||||||
"@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
|
"@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
|
||||||
|
|
||||||
"@babel/compat-data": ["@babel/compat-data@7.28.4", "", {}, "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw=="],
|
"@babel/compat-data": ["@babel/compat-data@7.28.4", "", {}, "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw=="],
|
||||||
@@ -181,10 +113,6 @@
|
|||||||
|
|
||||||
"@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="],
|
"@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="],
|
||||||
|
|
||||||
"@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="],
|
|
||||||
|
|
||||||
"@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="],
|
|
||||||
|
|
||||||
"@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.28.0", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg=="],
|
"@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.28.0", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg=="],
|
||||||
|
|
||||||
"@babel/preset-typescript": ["@babel/preset-typescript@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ=="],
|
"@babel/preset-typescript": ["@babel/preset-typescript@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ=="],
|
||||||
@@ -195,10 +123,6 @@
|
|||||||
|
|
||||||
"@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="],
|
"@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="],
|
||||||
|
|
||||||
"@better-auth/utils": ["@better-auth/utils@0.2.6", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-3y/vaL5Ox33dBwgJ6ub3OPkVqr6B5xL2kgxNHG8eHZuryLyG/4JSPGqjbdRSgjuy9kALUZYDFl+ORIAxlWMSuA=="],
|
|
||||||
|
|
||||||
"@better-fetch/fetch": ["@better-fetch/fetch@1.1.18", "", {}, "sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA=="],
|
|
||||||
|
|
||||||
"@biomejs/biome": ["@biomejs/biome@2.2.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.2.4", "@biomejs/cli-darwin-x64": "2.2.4", "@biomejs/cli-linux-arm64": "2.2.4", "@biomejs/cli-linux-arm64-musl": "2.2.4", "@biomejs/cli-linux-x64": "2.2.4", "@biomejs/cli-linux-x64-musl": "2.2.4", "@biomejs/cli-win32-arm64": "2.2.4", "@biomejs/cli-win32-x64": "2.2.4" }, "bin": { "biome": "bin/biome" } }, "sha512-TBHU5bUy/Ok6m8c0y3pZiuO/BZoY/OcGxoLlrfQof5s8ISVwbVBdFINPQZyFfKwil8XibYWb7JMwnT8wT4WVPg=="],
|
"@biomejs/biome": ["@biomejs/biome@2.2.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.2.4", "@biomejs/cli-darwin-x64": "2.2.4", "@biomejs/cli-linux-arm64": "2.2.4", "@biomejs/cli-linux-arm64-musl": "2.2.4", "@biomejs/cli-linux-x64": "2.2.4", "@biomejs/cli-linux-x64-musl": "2.2.4", "@biomejs/cli-win32-arm64": "2.2.4", "@biomejs/cli-win32-x64": "2.2.4" }, "bin": { "biome": "bin/biome" } }, "sha512-TBHU5bUy/Ok6m8c0y3pZiuO/BZoY/OcGxoLlrfQof5s8ISVwbVBdFINPQZyFfKwil8XibYWb7JMwnT8wT4WVPg=="],
|
||||||
|
|
||||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RJe2uiyaloN4hne4d2+qVj3d3gFJFbmrr5PYtkkjei1O9c+BjGXgpUPVbi8Pl8syumhzJjFsSIYkcLt2VlVLMA=="],
|
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RJe2uiyaloN4hne4d2+qVj3d3gFJFbmrr5PYtkkjei1O9c+BjGXgpUPVbi8Pl8syumhzJjFsSIYkcLt2VlVLMA=="],
|
||||||
@@ -217,13 +141,13 @@
|
|||||||
|
|
||||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.2.4", "", { "os": "win32", "cpu": "x64" }, "sha512-3Y4V4zVRarVh/B/eSHczR4LYoSVyv3Dfuvm3cWs5w/HScccS0+Wt/lHOcDTRYeHjQmMYVC3rIRWqyN2EI52+zg=="],
|
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.2.4", "", { "os": "win32", "cpu": "x64" }, "sha512-3Y4V4zVRarVh/B/eSHczR4LYoSVyv3Dfuvm3cWs5w/HScccS0+Wt/lHOcDTRYeHjQmMYVC3rIRWqyN2EI52+zg=="],
|
||||||
|
|
||||||
"@convex-dev/better-auth": ["@convex-dev/better-auth@0.8.9", "", { "dependencies": { "@better-fetch/fetch": "^1.1.18", "common-tags": "^1.8.2", "convex-helpers": "^0.1.95", "is-network-error": "^1.1.0", "type-fest": "^4.39.1", "zod": "^3.24.4" }, "peerDependencies": { "better-auth": "1.3.8", "convex": "^1.26.2", "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-t6x2lYsgv0sGL14xmIsTqxVltkS//mWtIjb+Wm39rWUCgeqmCTqsyhQnmTDQNwaqZS0sH0WfTObNHu3xbCSx1w=="],
|
"@convex-dev/workos": ["@convex-dev/workos@0.0.1", "", { "dependencies": { "@workos-inc/authkit-react": "^0.11.0", "tsdown": "^0.12.7", "vitest": "^3.1.4" }, "peerDependencies": { "convex": "^1.25.4", "react": "^18.0.0 || ^19.0.0-0 || ^19.0.0" } }, "sha512-8gZOgmcTitcKXwagdU69XC4Va6wMPFIhSqSOEaXmFXMEPtkMgxPW1dhJzrmm9UQ4iRgZsckjd2O5aQjUH7kHGQ=="],
|
||||||
|
|
||||||
"@drexa/auth": ["@drexa/auth@workspace:packages/auth"],
|
"@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="],
|
||||||
|
|
||||||
"@drexa/cli": ["@drexa/cli@workspace:apps/cli"],
|
"@emnapi/runtime": ["@emnapi/runtime@1.5.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="],
|
||||||
|
|
||||||
"@drexa/file-proxy": ["@drexa/file-proxy@workspace:apps/file-proxy"],
|
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
|
||||||
|
|
||||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="],
|
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="],
|
||||||
|
|
||||||
@@ -279,7 +203,7 @@
|
|||||||
|
|
||||||
"@fileone/path": ["@fileone/path@workspace:packages/path"],
|
"@fileone/path": ["@fileone/path@workspace:packages/path"],
|
||||||
|
|
||||||
"@fileone/web": ["@fileone/web@workspace:apps/drive-web"],
|
"@fileone/web": ["@fileone/web@workspace:packages/web"],
|
||||||
|
|
||||||
"@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="],
|
"@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="],
|
||||||
|
|
||||||
@@ -289,10 +213,6 @@
|
|||||||
|
|
||||||
"@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="],
|
"@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="],
|
||||||
|
|
||||||
"@hexagon/base64": ["@hexagon/base64@1.1.28", "", {}, "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw=="],
|
|
||||||
|
|
||||||
"@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
|
|
||||||
|
|
||||||
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
||||||
|
|
||||||
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
||||||
@@ -303,35 +223,13 @@
|
|||||||
|
|
||||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||||
|
|
||||||
"@levischuck/tiny-cbor": ["@levischuck/tiny-cbor@0.2.11", "", {}, "sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow=="],
|
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="],
|
||||||
|
|
||||||
"@noble/ciphers": ["@noble/ciphers@0.6.0", "", {}, "sha512-mIbq/R9QXk5/cTfESb1OKtyFnk7oc1Om/8onA1158K9/OZUQFDEVy55jVTato+xmp3XX6F6Qh0zz0Nc1AxAlRQ=="],
|
"@oxc-project/runtime": ["@oxc-project/runtime@0.71.0", "", {}, "sha512-QwoF5WUXIGFQ+hSxWEib4U/aeLoiDN9JlP18MnBgx9LLPRDfn1iICtcow7Jgey6HLH4XFceWXQD5WBJ39dyJcw=="],
|
||||||
|
|
||||||
"@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
|
"@oxc-project/types": ["@oxc-project/types@0.71.0", "", {}, "sha512-5CwQ4MI+P4MQbjLWXgNurA+igGwu/opNetIE13LBs9+V93R64MLvDKOOLZIXSzEfovU3Zef3q3GjPnMTgJTn2w=="],
|
||||||
|
|
||||||
"@peculiar/asn1-android": ["@peculiar/asn1-android@2.5.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.5.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-t8A83hgghWQkcneRsgGs2ebAlRe54ns88p7ouv8PW2tzF1nAW4yHcL4uZKrFpIU+uszIRzTkcCuie37gpkId0A=="],
|
"@quansync/fs": ["@quansync/fs@0.1.5", "", { "dependencies": { "quansync": "^0.2.11" } }, "sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA=="],
|
||||||
|
|
||||||
"@peculiar/asn1-cms": ["@peculiar/asn1-cms@2.5.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.5.0", "@peculiar/asn1-x509": "^2.5.0", "@peculiar/asn1-x509-attr": "^2.5.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-p0SjJ3TuuleIvjPM4aYfvYw8Fk1Hn/zAVyPJZTtZ2eE9/MIer6/18ROxX6N/e6edVSfvuZBqhxAj3YgsmSjQ/A=="],
|
|
||||||
|
|
||||||
"@peculiar/asn1-csr": ["@peculiar/asn1-csr@2.5.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.5.0", "@peculiar/asn1-x509": "^2.5.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-ioigvA6WSYN9h/YssMmmoIwgl3RvZlAYx4A/9jD2qaqXZwGcNlAxaw54eSx2QG1Yu7YyBC5Rku3nNoHrQ16YsQ=="],
|
|
||||||
|
|
||||||
"@peculiar/asn1-ecc": ["@peculiar/asn1-ecc@2.5.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.5.0", "@peculiar/asn1-x509": "^2.5.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-t4eYGNhXtLRxaP50h3sfO6aJebUCDGQACoeexcelL4roMFRRVgB20yBIu2LxsPh/tdW9I282gNgMOyg3ywg/mg=="],
|
|
||||||
|
|
||||||
"@peculiar/asn1-pfx": ["@peculiar/asn1-pfx@2.5.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.5.0", "@peculiar/asn1-pkcs8": "^2.5.0", "@peculiar/asn1-rsa": "^2.5.0", "@peculiar/asn1-schema": "^2.5.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-Vj0d0wxJZA+Ztqfb7W+/iu8Uasw6hhKtCdLKXLG/P3kEPIQpqGI4P4YXlROfl7gOCqFIbgsj1HzFIFwQ5s20ug=="],
|
|
||||||
|
|
||||||
"@peculiar/asn1-pkcs8": ["@peculiar/asn1-pkcs8@2.5.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.5.0", "@peculiar/asn1-x509": "^2.5.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-L7599HTI2SLlitlpEP8oAPaJgYssByI4eCwQq2C9eC90otFpm8MRn66PpbKviweAlhinWQ3ZjDD2KIVtx7PaVw=="],
|
|
||||||
|
|
||||||
"@peculiar/asn1-pkcs9": ["@peculiar/asn1-pkcs9@2.5.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.5.0", "@peculiar/asn1-pfx": "^2.5.0", "@peculiar/asn1-pkcs8": "^2.5.0", "@peculiar/asn1-schema": "^2.5.0", "@peculiar/asn1-x509": "^2.5.0", "@peculiar/asn1-x509-attr": "^2.5.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-UgqSMBLNLR5TzEZ5ZzxR45Nk6VJrammxd60WMSkofyNzd3DQLSNycGWSK5Xg3UTYbXcDFyG8pA/7/y/ztVCa6A=="],
|
|
||||||
|
|
||||||
"@peculiar/asn1-rsa": ["@peculiar/asn1-rsa@2.5.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.5.0", "@peculiar/asn1-x509": "^2.5.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-qMZ/vweiTHy9syrkkqWFvbT3eLoedvamcUdnnvwyyUNv5FgFXA3KP8td+ATibnlZ0EANW5PYRm8E6MJzEB/72Q=="],
|
|
||||||
|
|
||||||
"@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.5.0", "", { "dependencies": { "asn1js": "^3.0.6", "pvtsutils": "^1.3.6", "tslib": "^2.8.1" } }, "sha512-YM/nFfskFJSlHqv59ed6dZlLZqtZQwjRVJ4bBAiWV08Oc+1rSd5lDZcBEx0lGDHfSoH3UziI2pXt2UM33KerPQ=="],
|
|
||||||
|
|
||||||
"@peculiar/asn1-x509": ["@peculiar/asn1-x509@2.5.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.5.0", "asn1js": "^3.0.6", "pvtsutils": "^1.3.6", "tslib": "^2.8.1" } }, "sha512-CpwtMCTJvfvYTFMuiME5IH+8qmDe3yEWzKHe7OOADbGfq7ohxeLaXwQo0q4du3qs0AII3UbLCvb9NF/6q0oTKQ=="],
|
|
||||||
|
|
||||||
"@peculiar/asn1-x509-attr": ["@peculiar/asn1-x509-attr@2.5.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.5.0", "@peculiar/asn1-x509": "^2.5.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-9f0hPOxiJDoG/bfNLAFven+Bd4gwz/VzrCIIWc1025LEI4BXO0U5fOCTNDPbbp2ll+UzqKsZ3g61mpBp74gk9A=="],
|
|
||||||
|
|
||||||
"@peculiar/x509": ["@peculiar/x509@1.14.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.5.0", "@peculiar/asn1-csr": "^2.5.0", "@peculiar/asn1-ecc": "^2.5.0", "@peculiar/asn1-pkcs9": "^2.5.0", "@peculiar/asn1-rsa": "^2.5.0", "@peculiar/asn1-schema": "^2.5.0", "@peculiar/asn1-x509": "^2.5.0", "pvtsutils": "^1.3.6", "reflect-metadata": "^0.2.2", "tslib": "^2.8.1", "tsyringe": "^4.10.0" } }, "sha512-Yc4PDxN3OrxUPiXgU63c+ZRXKGE8YKF2McTciYhUHFtHVB0KMnjeFSU0qpztGhsp4P0uKix4+J2xEpIEDu8oXg=="],
|
|
||||||
|
|
||||||
"@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="],
|
"@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="],
|
||||||
|
|
||||||
@@ -361,8 +259,6 @@
|
|||||||
|
|
||||||
"@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-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-label": ["@radix-ui/react-label@2.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.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-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ=="],
|
|
||||||
|
|
||||||
"@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@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-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "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-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg=="],
|
"@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@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-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "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-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg=="],
|
||||||
|
|
||||||
"@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "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-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw=="],
|
"@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "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-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw=="],
|
||||||
@@ -373,8 +269,6 @@
|
|||||||
|
|
||||||
"@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-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-progress": ["@radix-ui/react-progress@1.1.7", "", { "dependencies": { "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.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-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg=="],
|
|
||||||
|
|
||||||
"@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "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-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="],
|
"@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "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-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="],
|
||||||
|
|
||||||
"@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.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-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA=="],
|
"@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.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-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA=="],
|
||||||
@@ -403,182 +297,180 @@
|
|||||||
|
|
||||||
"@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
|
"@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
|
||||||
|
|
||||||
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.38", "", {}, "sha512-N/ICGKleNhA5nc9XXQG/kkKHJ7S55u0x0XUJbbkmdCnFuoRkM1Il12q9q0eX19+M7KKUEPw/daUPIRnxhcxAIw=="],
|
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-beta.9-commit.d91dfb5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Mp0/gqiPdepHjjVm7e0yL1acWvI0rJVVFQEADSezvAjon9sjQ7CEg9JnXICD4B1YrPmN9qV/e7cQZCp87tTV4w=="],
|
||||||
|
|
||||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.4", "", { "os": "android", "cpu": "arm" }, "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA=="],
|
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-beta.9-commit.d91dfb5", "", { "os": "darwin", "cpu": "x64" }, "sha512-40re4rMNrsi57oavRzIOpRGmg3QRlW6Ea8Q3znaqgOuJuKVrrm2bIQInTfkZJG7a4/5YMX7T951d0+toGLTdCA=="],
|
||||||
|
|
||||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.4", "", { "os": "android", "cpu": "arm64" }, "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w=="],
|
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-beta.9-commit.d91dfb5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-8BDM939bbMariZupiHp3OmP5N+LXPT4mULA0hZjDaq970PCxv4krZOSMG+HkWUUwmuQROtV+/00xw39EO0P+8g=="],
|
||||||
|
|
||||||
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg=="],
|
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.9-commit.d91dfb5", "", { "os": "linux", "cpu": "arm" }, "sha512-sntsPaPgrECpBB/+2xrQzVUt0r493TMPI+4kWRMhvMsmrxOqH1Ep5lM0Wua/ZdbfZNwm1aVa5pcESQfNfM4Fhw=="],
|
||||||
|
|
||||||
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw=="],
|
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-beta.9-commit.d91dfb5", "", { "os": "linux", "cpu": "arm64" }, "sha512-5clBW/I+er9F2uM1OFjJFWX86y7Lcy0M+NqsN4s3o07W+8467Zk8oQa4B45vdaXoNUF/yqIAgKkA/OEdQDxZqA=="],
|
||||||
|
|
||||||
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ=="],
|
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-beta.9-commit.d91dfb5", "", { "os": "linux", "cpu": "arm64" }, "sha512-wv+rnAfQDk9p/CheX8/Kmqk2o1WaFa4xhWI9gOyDMk/ljvOX0u0ubeM8nI1Qfox7Tnh71eV5AjzSePXUhFOyOg=="],
|
||||||
|
|
||||||
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw=="],
|
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-beta.9-commit.d91dfb5", "", { "os": "linux", "cpu": "x64" }, "sha512-gxD0/xhU4Py47IH3bKZbWtvB99tMkUPGPJFRfSc5UB9Osoje0l0j1PPbxpUtXIELurYCqwLBKXIMTQGifox1BQ=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.4", "", { "os": "linux", "cpu": "arm" }, "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ=="],
|
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-beta.9-commit.d91dfb5", "", { "os": "linux", "cpu": "x64" }, "sha512-HotuVe3XUjDwqqEMbm3o3IRkP9gdm8raY/btd/6KE3JGLF/cv4+3ff1l6nOhAZI8wulWDPEXPtE7v+HQEaTXnA=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.4", "", { "os": "linux", "cpu": "arm" }, "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q=="],
|
"@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-beta.9-commit.d91dfb5", "", { "dependencies": { "@napi-rs/wasm-runtime": "^0.2.4" }, "cpu": "none" }, "sha512-8Cx+ucbd8n2dIr21FqBh6rUvTVL0uTgEtKR7l+MUZ5BgY4dFh1e4mPVX8oqmoYwOxBiXrsD2JIOCz4AyKLKxWA=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg=="],
|
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-beta.9-commit.d91dfb5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Vhq5vikrVDxAa75fxsyqj0c0Y/uti/TwshXI71Xb8IeUQJOBnmLUsn5dgYf5ljpYYkNa0z9BPAvUDIDMmyDi+w=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g=="],
|
"@rolldown/binding-win32-ia32-msvc": ["@rolldown/binding-win32-ia32-msvc@1.0.0-beta.9-commit.d91dfb5", "", { "os": "win32", "cpu": "ia32" }, "sha512-lN7RIg9Iugn08zP2aZN9y/MIdG8iOOCE93M1UrFlrxMTqPf8X+fDzmR/OKhTSd1A2pYNipZHjyTcb5H8kyQSow=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.4", "", { "os": "linux", "cpu": "none" }, "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ=="],
|
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-beta.9-commit.d91dfb5", "", { "os": "win32", "cpu": "x64" }, "sha512-7/7cLIn48Y+EpQ4CePvf8reFl63F15yPUlg4ZAhl+RXJIfydkdak1WD8Ir3AwAO+bJBXzrfNL+XQbxm0mcQZmw=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g=="],
|
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.9-commit.d91dfb5", "", {}, "sha512-8sExkWRK+zVybw3+2/kBkYBFeLnEUWz1fT7BLHplpzmtqkOfTbAQ9gkt4pzwGIIZmg4Qn5US5ACjUBenrhezwQ=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.4", "", { "os": "linux", "cpu": "none" }, "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg=="],
|
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.50.1", "", { "os": "android", "cpu": "arm" }, "sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.4", "", { "os": "linux", "cpu": "none" }, "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA=="],
|
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.50.1", "", { "os": "android", "cpu": "arm64" }, "sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA=="],
|
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.50.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.4", "", { "os": "linux", "cpu": "x64" }, "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg=="],
|
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.50.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.4", "", { "os": "linux", "cpu": "x64" }, "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw=="],
|
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.50.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA=="],
|
||||||
|
|
||||||
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.4", "", { "os": "none", "cpu": "arm64" }, "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA=="],
|
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.50.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ=="],
|
||||||
|
|
||||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ=="],
|
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.50.1", "", { "os": "linux", "cpu": "arm" }, "sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg=="],
|
||||||
|
|
||||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw=="],
|
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.50.1", "", { "os": "linux", "cpu": "arm" }, "sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw=="],
|
||||||
|
|
||||||
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.4", "", { "os": "win32", "cpu": "x64" }, "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ=="],
|
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.50.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw=="],
|
||||||
|
|
||||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.4", "", { "os": "win32", "cpu": "x64" }, "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w=="],
|
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.50.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w=="],
|
||||||
|
|
||||||
"@simplewebauthn/browser": ["@simplewebauthn/browser@13.2.2", "", {}, "sha512-FNW1oLQpTJyqG5kkDg5ZsotvWgmBaC6jCHR7Ej0qUNep36Wl9tj2eZu7J5rP+uhXgHaLk+QQ3lqcw2vS5MX1IA=="],
|
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.50.1", "", { "os": "linux", "cpu": "none" }, "sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q=="],
|
||||||
|
|
||||||
"@simplewebauthn/server": ["@simplewebauthn/server@13.2.2", "", { "dependencies": { "@hexagon/base64": "^1.1.27", "@levischuck/tiny-cbor": "^0.2.2", "@peculiar/asn1-android": "^2.3.10", "@peculiar/asn1-ecc": "^2.3.8", "@peculiar/asn1-rsa": "^2.3.8", "@peculiar/asn1-schema": "^2.3.8", "@peculiar/asn1-x509": "^2.3.8", "@peculiar/x509": "^1.13.0" } }, "sha512-HcWLW28yTMGXpwE9VLx9J+N2KEUaELadLrkPEEI9tpI5la70xNEVEsu/C+m3u7uoq4FulLqZQhgBCzR9IZhFpA=="],
|
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.50.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q=="],
|
||||||
|
|
||||||
"@tailwindcss/node": ["@tailwindcss/node@4.1.14", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.0", "lightningcss": "1.30.1", "magic-string": "^0.30.19", "source-map-js": "^1.2.1", "tailwindcss": "4.1.14" } }, "sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw=="],
|
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.50.1", "", { "os": "linux", "cpu": "none" }, "sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.14", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.5.1" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.14", "@tailwindcss/oxide-darwin-arm64": "4.1.14", "@tailwindcss/oxide-darwin-x64": "4.1.14", "@tailwindcss/oxide-freebsd-x64": "4.1.14", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.14", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.14", "@tailwindcss/oxide-linux-arm64-musl": "4.1.14", "@tailwindcss/oxide-linux-x64-gnu": "4.1.14", "@tailwindcss/oxide-linux-x64-musl": "4.1.14", "@tailwindcss/oxide-wasm32-wasi": "4.1.14", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.14", "@tailwindcss/oxide-win32-x64-msvc": "4.1.14" } }, "sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw=="],
|
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.50.1", "", { "os": "linux", "cpu": "none" }, "sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.14", "", { "os": "android", "cpu": "arm64" }, "sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ=="],
|
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.50.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.14", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA=="],
|
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.50.1", "", { "os": "linux", "cpu": "x64" }, "sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.14", "", { "os": "darwin", "cpu": "x64" }, "sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw=="],
|
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.50.1", "", { "os": "linux", "cpu": "x64" }, "sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.14", "", { "os": "freebsd", "cpu": "x64" }, "sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw=="],
|
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.50.1", "", { "os": "none", "cpu": "arm64" }, "sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14", "", { "os": "linux", "cpu": "arm" }, "sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw=="],
|
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.50.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w=="],
|
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.50.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ=="],
|
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.50.1", "", { "os": "win32", "cpu": "x64" }, "sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.14", "", { "os": "linux", "cpu": "x64" }, "sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg=="],
|
"@tanstack/history": ["@tanstack/history@1.131.2", "", {}, "sha512-cs1WKawpXIe+vSTeiZUuSBy8JFjEuDgdMKZFRLKwQysKo8y2q6Q1HvS74Yw+m5IhOW1nTZooa6rlgdfXcgFAaw=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.14", "", { "os": "linux", "cpu": "x64" }, "sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q=="],
|
"@tanstack/query-core": ["@tanstack/query-core@5.87.4", "", {}, "sha512-uNsg6zMxraEPDVO2Bn+F3/ctHi+Zsk+MMpcN8h6P7ozqD088F6mFY5TfGM7zuyIrL7HKpDyu6QHfLWiDxh3cuw=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.14", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.0.5", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ=="],
|
"@tanstack/react-query": ["@tanstack/react-query@5.87.4", "", { "dependencies": { "@tanstack/query-core": "5.87.4" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-T5GT/1ZaNsUXf5I3RhcYuT17I4CPlbZgyLxc/ZGv7ciS6esytlbjb3DgUFO6c8JWYMDpdjSWInyGZUErgzqhcA=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.14", "", { "os": "win32", "cpu": "arm64" }, "sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA=="],
|
"@tanstack/react-router": ["@tanstack/react-router@1.131.41", "", { "dependencies": { "@tanstack/history": "1.131.2", "@tanstack/react-store": "^0.7.0", "@tanstack/router-core": "1.131.41", "isbot": "^5.1.22", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-QEbTYpAosiD8e4qEZRr9aJipGSb8pQc+pfZwK6NCD2Tcxwu2oF6MVtwv0bIDLRpZP0VJMBpxXlTRISUDNMNqIA=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.14", "", { "os": "win32", "cpu": "x64" }, "sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA=="],
|
"@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.131.42", "", { "dependencies": { "@tanstack/router-devtools-core": "1.131.42" }, "peerDependencies": { "@tanstack/react-router": "^1.131.41", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-7pymFB1CCimRHot2Zp0ZekQjd1iN812V88n9NLPSeiv9sVRtRVIaLphJjDeudx1NNgkfSJPx2lOhz6K38cuZog=="],
|
||||||
|
|
||||||
"@tailwindcss/vite": ["@tailwindcss/vite@4.1.14", "", { "dependencies": { "@tailwindcss/node": "4.1.14", "@tailwindcss/oxide": "4.1.14", "tailwindcss": "4.1.14" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-BoFUoU0XqgCUS1UXWhmDJroKKhNXeDzD7/XwabjkDIAbMnc4ULn5e2FuEuBbhZ6ENZoSYzKlzvZ44Yr6EUDUSA=="],
|
"@tanstack/react-store": ["@tanstack/react-store@0.7.5", "", { "dependencies": { "@tanstack/store": "0.7.5", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-A+WZtEnHZpvbKXm8qR+xndNKywBLez2KKKKEQc7w0Qs45GvY1LpRI3BTZNmELwEVim8+Apf99iEDH2J+MUIzlQ=="],
|
||||||
|
|
||||||
"@tanstack/history": ["@tanstack/history@1.133.3", "", {}, "sha512-zFQnGdX0S4g5xRuS+95iiEXM+qlGvYG7ksmOKx7LaMv60lDWa0imR8/24WwXXvBWJT1KnwVdZcjvhCwz9IiJCw=="],
|
|
||||||
|
|
||||||
"@tanstack/query-core": ["@tanstack/query-core@5.90.5", "", {}, "sha512-wLamYp7FaDq6ZnNehypKI5fNvxHPfTYylE0m/ZpuuzJfJqhR5Pxg9gvGBHZx4n7J+V5Rg5mZxHHTlv25Zt5u+w=="],
|
|
||||||
|
|
||||||
"@tanstack/react-query": ["@tanstack/react-query@5.90.5", "", { "dependencies": { "@tanstack/query-core": "5.90.5" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-pN+8UWpxZkEJ/Rnnj2v2Sxpx1WFlaa9L6a4UO89p6tTQbeo+m0MS8oYDjbggrR8QcTyjKoYWKS3xJQGr3ExT8Q=="],
|
|
||||||
|
|
||||||
"@tanstack/react-router": ["@tanstack/react-router@1.133.12", "", { "dependencies": { "@tanstack/history": "1.133.3", "@tanstack/react-store": "^0.7.0", "@tanstack/router-core": "1.133.12", "isbot": "^5.1.22", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-IS4/KU2r5PcVsD6PVRK6ZQtn2yVv0HGKpo/8bqbnb13j1f6Osj7VCpZ4n0ur151zMsG4MNkbtfzdJjipLnrFyA=="],
|
|
||||||
|
|
||||||
"@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.133.12", "", { "dependencies": { "@tanstack/router-devtools-core": "1.133.12", "vite": "^7.1.7" }, "peerDependencies": { "@tanstack/react-router": "^1.133.12", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-g5rL5mY99hGyZvqdyCCfppZNa4XcaSw2QBPFujBevZa2HDVW2c9msflr7HWOw83SrZUq8cQH5dHFNzRypcqtxg=="],
|
|
||||||
|
|
||||||
"@tanstack/react-store": ["@tanstack/react-store@0.7.7", "", { "dependencies": { "@tanstack/store": "0.7.7", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-qqT0ufegFRDGSof9D/VqaZgjNgp4tRPHZIJq2+QIHkMUtHjaJ0lYrrXjeIUJvjnTbgPfSD1XgOMEt0lmANn6Zg=="],
|
|
||||||
|
|
||||||
"@tanstack/react-table": ["@tanstack/react-table@8.21.3", "", { "dependencies": { "@tanstack/table-core": "8.21.3" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww=="],
|
"@tanstack/react-table": ["@tanstack/react-table@8.21.3", "", { "dependencies": { "@tanstack/table-core": "8.21.3" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww=="],
|
||||||
|
|
||||||
"@tanstack/router-cli": ["@tanstack/router-cli@1.133.12", "", { "dependencies": { "@tanstack/router-generator": "1.133.12", "chokidar": "^3.6.0", "yargs": "^17.7.2" }, "bin": { "tsr": "bin/tsr.cjs" } }, "sha512-5rBpY1yixbxtuLarXSTXK6mD2Wrluyqy9/LRS1k9o61dLiBi9L4HlYkkXkKtpvOXb4VhxlqgmSg2JwASYCi2ng=="],
|
"@tanstack/router-cli": ["@tanstack/router-cli@1.131.41", "", { "dependencies": { "@tanstack/router-generator": "1.131.41", "chokidar": "^3.6.0", "yargs": "^17.7.2" }, "bin": { "tsr": "bin/tsr.cjs" } }, "sha512-EpLnnCwwCd94HRCWHoa1GZGtIWIffx4rPBb6gbWm4cvyEIGV2Gq+27vL2OEw819/elxyBQmG2RrPB8+7dfVACw=="],
|
||||||
|
|
||||||
"@tanstack/router-core": ["@tanstack/router-core@1.133.13", "", { "dependencies": { "@tanstack/history": "1.133.3", "@tanstack/store": "^0.7.0", "cookie-es": "^2.0.0", "seroval": "^1.3.2", "seroval-plugins": "^1.3.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-zZptdlS/wSkqozb07Y3zX5gas2OapJdjEG6/Id0e/twNefVdR4EY2TK/mgvyhHtKIpCxIcnZz/3opypgeQi9bg=="],
|
"@tanstack/router-core": ["@tanstack/router-core@1.131.41", "", { "dependencies": { "@tanstack/history": "1.131.2", "@tanstack/store": "^0.7.0", "cookie-es": "^1.2.2", "seroval": "^1.3.2", "seroval-plugins": "^1.3.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-VoLly00DWM0abKuVPRm8wiwGtRBHOKs6K896fy48Q/KYoDVLs8kRCRjFGS7rGnYC2FIkmmvHqYRqNg7jgCx2yg=="],
|
||||||
|
|
||||||
"@tanstack/router-devtools": ["@tanstack/router-devtools@1.133.12", "", { "dependencies": { "@tanstack/react-router-devtools": "1.133.12", "clsx": "^2.1.1", "goober": "^2.1.16", "vite": "^7.1.7" }, "peerDependencies": { "@tanstack/react-router": "^1.133.12", "csstype": "^3.0.10", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["csstype"] }, "sha512-CMXzbl7CEAjlR2KxadTY/HdzKElIiU2DHDjxxnZLabFepkAF5rlVZtqY0W+fQA1CnRCJMKsWXnA9hTUpOzgsyQ=="],
|
"@tanstack/router-devtools": ["@tanstack/router-devtools@1.131.42", "", { "dependencies": { "@tanstack/react-router-devtools": "1.131.42", "clsx": "^2.1.1", "goober": "^2.1.16" }, "peerDependencies": { "@tanstack/react-router": "^1.131.41", "csstype": "^3.0.10", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["csstype"] }, "sha512-iWJzr4aN/IOsDSaF/kysM7tPSYj89hnzcWMKNuYN9redIwHgg7rNZ4toKhfNWYNfzxdhKwL9/Yvpf7bDemyc+Q=="],
|
||||||
|
|
||||||
"@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.133.12", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16", "vite": "^7.1.7" }, "peerDependencies": { "@tanstack/router-core": "^1.133.12", "csstype": "^3.0.10", "solid-js": ">=1.9.5", "tiny-invariant": "^1.3.3" }, "optionalPeers": ["csstype"] }, "sha512-MimpwjKda6CnQcgCH9K4XWonOlKT5qyKfSbmZSAc4AhUDYcUmkP+yWZ9FobFAHOZiU6KHpUpCs8nwchAjBp3wA=="],
|
"@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.131.42", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16", "solid-js": "^1.9.5" }, "peerDependencies": { "@tanstack/router-core": "^1.131.41", "csstype": "^3.0.10", "tiny-invariant": "^1.3.3" }, "optionalPeers": ["csstype"] }, "sha512-o8jKTiwXcUSjmkozcMjIw1yhjVYeXcuQO7DtfgjKW3B85iveH6VzYK+bGEVU7wmLNMuUSe2eI/7RBzJ6a5+MCA=="],
|
||||||
|
|
||||||
"@tanstack/router-generator": ["@tanstack/router-generator@1.133.12", "", { "dependencies": { "@tanstack/router-core": "1.133.12", "@tanstack/router-utils": "1.133.3", "@tanstack/virtual-file-routes": "1.133.3", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-4Z/h6s/g6kCw7eMDbNkKqcl2QB89/N9FDlZXnlzmGfUtk0wxnpJTgFEIIiFN9YiSdvVTg6HX2Qo6UwOzTDsdEQ=="],
|
"@tanstack/router-generator": ["@tanstack/router-generator@1.131.41", "", { "dependencies": { "@tanstack/router-core": "1.131.41", "@tanstack/router-utils": "1.131.2", "@tanstack/virtual-file-routes": "1.131.2", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-HsDkBU1u/KvHrzn76v/9oeyMFuxvVlE3dfIu4fldZbPy/i903DWBwODIDGe6fVUsYtzPPrRvNtbjV18HVz5GCA=="],
|
||||||
|
|
||||||
"@tanstack/router-plugin": ["@tanstack/router-plugin@1.133.13", "", { "dependencies": { "@babel/core": "^7.27.7", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.27.7", "@babel/types": "^7.27.7", "@tanstack/router-core": "1.133.13", "@tanstack/router-generator": "1.133.13", "@tanstack/router-utils": "1.133.3", "@tanstack/virtual-file-routes": "1.133.3", "babel-dead-code-elimination": "^1.0.10", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.133.13", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0", "vite-plugin-solid": "^2.11.8", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-R5cbCwdw5chQhgaVERE2JlPpGWcER4FuVkRGDbLaW/rpawIskJCjkAbhqyfgXPF8VsEUOs9+7FK6ocODnqM/qA=="],
|
"@tanstack/router-utils": ["@tanstack/router-utils@1.131.2", "", { "dependencies": { "@babel/core": "^7.27.4", "@babel/generator": "^7.27.5", "@babel/parser": "^7.27.5", "@babel/preset-typescript": "^7.27.1", "ansis": "^4.1.0", "diff": "^8.0.2" } }, "sha512-sr3x0d2sx9YIJoVth0QnfEcAcl+39sQYaNQxThtHmRpyeFYNyM2TTH+Ud3TNEnI3bbzmLYEUD+7YqB987GzhDA=="],
|
||||||
|
|
||||||
"@tanstack/router-utils": ["@tanstack/router-utils@1.133.3", "", { "dependencies": { "@babel/core": "^7.27.4", "@babel/generator": "^7.27.5", "@babel/parser": "^7.27.5", "@babel/preset-typescript": "^7.27.1", "ansis": "^4.1.0", "diff": "^8.0.2", "pathe": "^2.0.3", "tinyglobby": "^0.2.15" } }, "sha512-miPFlt0aG6ID5VDolYuRXgLS7cofvbZGMvHwf2Wmyxjo6GLp/kxxpkQrfM4T1I5cwjwYZZAQmdUKbVHwFZz9sQ=="],
|
"@tanstack/store": ["@tanstack/store@0.7.5", "", {}, "sha512-qd/OjkjaFRKqKU4Yjipaen/EOB9MyEg6Wr9fW103RBPACf1ZcKhbhcu2S5mj5IgdPib6xFIgCUti/mKVkl+fRw=="],
|
||||||
|
|
||||||
"@tanstack/store": ["@tanstack/store@0.7.7", "", {}, "sha512-xa6pTan1bcaqYDS9BDpSiS63qa6EoDkPN9RsRaxHuDdVDNntzq3xNwR5YKTU/V3SkSyC9T4YVOPh2zRQN0nhIQ=="],
|
|
||||||
|
|
||||||
"@tanstack/table-core": ["@tanstack/table-core@8.21.3", "", {}, "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg=="],
|
"@tanstack/table-core": ["@tanstack/table-core@8.21.3", "", {}, "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg=="],
|
||||||
|
|
||||||
"@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.133.3", "", {}, "sha512-6d2AP9hAjEi8mcIew2RkxBX+wClH1xedhfaYhs8fUiX+V2Cedk7RBD9E9ww2z6BGUYD8Es4fS0OIrzXZWHKGhw=="],
|
"@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.131.2", "", {}, "sha512-VEEOxc4mvyu67O+Bl0APtYjwcNRcL9it9B4HKbNgcBTIOEalhk+ufBl4kiqc8WP1sx1+NAaiS+3CcJBhrqaSRg=="],
|
||||||
|
|
||||||
"@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="],
|
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
||||||
|
|
||||||
"@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="],
|
"@types/bun": ["@types/bun@1.2.21", "", { "dependencies": { "bun-types": "1.2.21" } }, "sha512-NiDnvEqmbfQ6dmZ3EeUO577s4P5bf4HCTXtI6trMc6f6RzirY5IrF3aIookuSpyslFzrnvv2lmEWv5HyC1X79A=="],
|
||||||
|
|
||||||
"@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="],
|
"@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="],
|
||||||
|
|
||||||
"@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="],
|
"@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="],
|
||||||
|
|
||||||
"@types/bun": ["@types/bun@1.3.0", "", { "dependencies": { "bun-types": "1.3.0" } }, "sha512-+lAGCYjXjip2qY375xX/scJeVRmZ5cY0wyHYyCYxNcdEXrQ4AOe3gACgd4iQ8ksOslJtW4VNxBJ8llUwc3a6AA=="],
|
|
||||||
|
|
||||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||||
|
|
||||||
"@types/node": ["@types/node@22.18.11", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-Gd33J2XIrXurb+eT2ktze3rJAfAp9ZNjlBdh4SVgyrKEOADwCbdUDaK7QgJno8Ue4kcajscsKqu6n8OBG3hhCQ=="],
|
"@types/node": ["@types/node@24.3.3", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-GKBNHjoNw3Kra1Qg5UXttsY5kiWMEfoHq2TmXb+b1rcm6N7B3wTrFYIf/oSZ1xNQ+hVVijgLkiDZh7jRRsh+Gw=="],
|
||||||
|
|
||||||
"@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="],
|
"@types/react": ["@types/react@19.1.13", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ=="],
|
||||||
|
|
||||||
"@types/react-dom": ["@types/react-dom@19.2.2", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw=="],
|
"@types/react-dom": ["@types/react-dom@19.1.9", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ=="],
|
||||||
|
|
||||||
"@vitejs/plugin-react": ["@vitejs/plugin-react@5.0.4", "", { "dependencies": { "@babel/core": "^7.28.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.38", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-La0KD0vGkVkSk6K+piWDKRUyg8Rl5iAIKRMH0vMJI0Eg47bq1eOxmoObAaQG37WMW9MSyk7Cs8EIWwJC1PtzKA=="],
|
"@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="],
|
||||||
|
|
||||||
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
"@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="],
|
||||||
|
|
||||||
|
"@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="],
|
||||||
|
|
||||||
|
"@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="],
|
||||||
|
|
||||||
|
"@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="],
|
||||||
|
|
||||||
|
"@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="],
|
||||||
|
|
||||||
|
"@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="],
|
||||||
|
|
||||||
|
"@workos-inc/authkit-js": ["@workos-inc/authkit-js@0.13.0", "", {}, "sha512-iA0Dt7D1BmY2/1s4oeA36W/aRt8/b5iyH6rP4AlgnjrcH2lUGkBgDXL76NXc0M7repkDQTMcJJ2NhCSo2rcWmg=="],
|
||||||
|
|
||||||
|
"@workos-inc/authkit-react": ["@workos-inc/authkit-react@0.12.0", "", { "dependencies": { "@workos-inc/authkit-js": "0.13.0" }, "peerDependencies": { "react": ">=17" } }, "sha512-j3OckFxz3iDeheRHMWBCIVDAUhSw/hjBFEjKS9U3azwjnbRGb/x4CHmGa4pdNtq+trf2NrlISC648Nwz6fJdGg=="],
|
||||||
|
|
||||||
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||||
|
|
||||||
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||||
|
|
||||||
"ansis": ["ansis@4.2.0", "", {}, "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig=="],
|
"ansis": ["ansis@4.1.0", "", {}, "sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w=="],
|
||||||
|
|
||||||
"anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
|
"anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
|
||||||
|
|
||||||
"aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="],
|
"aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="],
|
||||||
|
|
||||||
"arktype": ["arktype@2.1.23", "", { "dependencies": { "@ark/regex": "0.0.0", "@ark/schema": "0.50.0", "@ark/util": "0.50.0" } }, "sha512-tyxNWX6xJVMb2EPJJ3OjgQS1G/vIeQRrZuY4DeBNQmh8n7geS+czgbauQWB6Pr+RXiOO8ChEey44XdmxsqGmfQ=="],
|
"assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="],
|
||||||
|
|
||||||
"asn1js": ["asn1js@3.0.6", "", { "dependencies": { "pvtsutils": "^1.3.6", "pvutils": "^1.1.3", "tslib": "^2.8.1" } }, "sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA=="],
|
"ast-kit": ["ast-kit@2.1.2", "", { "dependencies": { "@babel/parser": "^7.28.0", "pathe": "^2.0.3" } }, "sha512-cl76xfBQM6pztbrFWRnxbrDm9EOqDr1BF6+qQnnDZG2Co2LjyUktkN9GTJfBAfdae+DbT2nJf2nCGAdDDN7W2g=="],
|
||||||
|
|
||||||
"ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="],
|
"ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="],
|
||||||
|
|
||||||
"babel-dead-code-elimination": ["babel-dead-code-elimination@1.0.10", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-DV5bdJZTzZ0zn0DC24v3jD7Mnidh6xhKa4GfKCbq3sfW8kaWhDdZjP3i81geA8T33tdYqWKw4D3fVv0CwEgKVA=="],
|
"baseline-browser-mapping": ["baseline-browser-mapping@2.8.3", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-mcE+Wr2CAhHNWxXN/DdTI+n4gsPc5QpXpWnyCQWiQYIYZX+ZMJ8juXZgjRa/0/YPJo/NSsgW15/YgmI4nbysYw=="],
|
||||||
|
|
||||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.8.17", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-j5zJcx6golJYTG6c05LUZ3Z8Gi+M62zRT/ycz4Xq4iCOdpcxwg7ngEYD4KA0eWZC7U17qh/Smq8bYbACJ0ipBA=="],
|
|
||||||
|
|
||||||
"better-auth": ["better-auth@1.3.8", "", { "dependencies": { "@better-auth/utils": "0.2.6", "@better-fetch/fetch": "^1.1.18", "@noble/ciphers": "^0.6.0", "@noble/hashes": "^1.8.0", "@simplewebauthn/browser": "^13.1.2", "@simplewebauthn/server": "^13.1.2", "better-call": "1.0.16", "defu": "^6.1.4", "jose": "^5.10.0", "kysely": "^0.28.5", "nanostores": "^0.11.4", "zod": "^4.1.5" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-uRFzHbWkhr8eWNy+BJwyMnrZPOvQjwrcLND3nc6jusRteYA9cjeRGElgCPTWTIyWUfzaQ708Lb5Mdq9Gv41Qpw=="],
|
|
||||||
|
|
||||||
"better-call": ["better-call@1.0.16", "", { "dependencies": { "@better-fetch/fetch": "^1.1.4", "rou3": "^0.5.1", "set-cookie-parser": "^2.7.1", "uncrypto": "^0.1.3" } }, "sha512-42dgJ1rOtc0anOoxjXPOWuel/Z/4aeO7EJ2SiXNwvlkySSgjXhNjAjTMWa8DL1nt6EXS3jl3VKC3mPsU/lUgVA=="],
|
|
||||||
|
|
||||||
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
|
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
|
||||||
|
|
||||||
|
"birpc": ["birpc@2.5.0", "", {}, "sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ=="],
|
||||||
|
|
||||||
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
||||||
|
|
||||||
"browserslist": ["browserslist@4.26.3", "", { "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", "electron-to-chromium": "^1.5.227", "node-releases": "^2.0.21", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w=="],
|
"browserslist": ["browserslist@4.26.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.2", "caniuse-lite": "^1.0.30001741", "electron-to-chromium": "^1.5.218", "node-releases": "^2.0.21", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-P9go2WrP9FiPwLv3zqRD/Uoxo0RSHjzFCiQz7d4vbmwNqQFo9T9WCeP/Qn5EbcKQY6DBbkxEXNcpJOmncNrb7A=="],
|
||||||
|
|
||||||
"bun-types": ["bun-types@1.3.0", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ=="],
|
"bun-plugin-tailwind": ["bun-plugin-tailwind@0.0.15", "", { "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-qtAXMNGG4R0UGGI8zWrqm2B7BdXqx48vunJXBPzfDOHPA5WkRUZdTSbE7TFwO4jLhYqSE23YMWsM9NhE6ovobw=="],
|
||||||
|
|
||||||
"caniuse-lite": ["caniuse-lite@1.0.30001751", "", {}, "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw=="],
|
"bun-types": ["bun-types@1.2.21", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-sa2Tj77Ijc/NTLS0/Odjq/qngmEPZfbfnOERi0KRUYhT9R8M4VBioWVmMWE5GrYbKMc+5lVybXygLdibHaqVqw=="],
|
||||||
|
|
||||||
"chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
"cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
|
||||||
|
|
||||||
|
"caniuse-lite": ["caniuse-lite@1.0.30001741", "", {}, "sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw=="],
|
||||||
|
|
||||||
|
"chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="],
|
||||||
|
|
||||||
|
"check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="],
|
||||||
|
|
||||||
"chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
"chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
||||||
|
|
||||||
"chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
|
|
||||||
|
|
||||||
"class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="],
|
"class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="],
|
||||||
|
|
||||||
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
|
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
|
||||||
@@ -589,35 +481,35 @@
|
|||||||
|
|
||||||
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||||
|
|
||||||
"commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
|
|
||||||
|
|
||||||
"common-tags": ["common-tags@1.8.2", "", {}, "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA=="],
|
|
||||||
|
|
||||||
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
||||||
|
|
||||||
"convex": ["convex@1.28.0", "", { "dependencies": { "esbuild": "0.25.4", "prettier": "^3.0.0" }, "peerDependencies": { "@auth0/auth0-react": "^2.0.1", "@clerk/clerk-react": "^4.12.8 || ^5.0.0", "react": "^18.0.0 || ^19.0.0-0 || ^19.0.0" }, "optionalPeers": ["@auth0/auth0-react", "@clerk/clerk-react", "react"], "bin": { "convex": "bin/main.js" } }, "sha512-40FgeJ/LxP9TxnkDDztU/A5gcGTdq1klcTT5mM0Ak+kSlQiDktMpjNX1TfkWLxXaE3lI4qvawKH95v2RiYgFxA=="],
|
"convex": ["convex@1.27.0", "", { "dependencies": { "esbuild": "0.25.4", "jwt-decode": "^4.0.0", "prettier": "^3.0.0" }, "peerDependencies": { "@auth0/auth0-react": "^2.0.1", "@clerk/clerk-react": "^4.12.8 || ^5.0.0", "react": "^18.0.0 || ^19.0.0-0 || ^19.0.0" }, "optionalPeers": ["@auth0/auth0-react", "@clerk/clerk-react", "react"], "bin": { "convex": "bin/main.js" } }, "sha512-IHkqZX3GtY4nKFPTAR4mvWHHhDiQX9PM7EjpEv0pJWoMoq0On6oOL3iZ7Xz4Ls96dF7WJd4AjfitJsg2hUnLSQ=="],
|
||||||
|
|
||||||
"convex-helpers": ["convex-helpers@0.1.104", "", { "peerDependencies": { "@standard-schema/spec": "^1.0.0", "convex": "^1.24.0", "hono": "^4.0.5", "react": "^17.0.2 || ^18.0.0 || ^19.0.0", "typescript": "^5.5", "zod": "^3.22.4 || ^4.0.15" }, "optionalPeers": ["@standard-schema/spec", "hono", "react", "typescript", "zod"], "bin": { "convex-helpers": "bin.cjs" } }, "sha512-7CYvx7T3K6n+McDTK4ZQaQNNGBzq5aWezpjzsKbOxPXx7oNcTP9wrpef3JxeXWFzkByJv5hRCjseh9B7eNJ7Ig=="],
|
"convex-helpers": ["convex-helpers@0.1.104", "", { "peerDependencies": { "@standard-schema/spec": "^1.0.0", "convex": "^1.24.0", "hono": "^4.0.5", "react": "^17.0.2 || ^18.0.0 || ^19.0.0", "typescript": "^5.5", "zod": "^3.22.4 || ^4.0.15" }, "optionalPeers": ["@standard-schema/spec", "hono", "react", "typescript", "zod"], "bin": { "convex-helpers": "bin.cjs" } }, "sha512-7CYvx7T3K6n+McDTK4ZQaQNNGBzq5aWezpjzsKbOxPXx7oNcTP9wrpef3JxeXWFzkByJv5hRCjseh9B7eNJ7Ig=="],
|
||||||
|
|
||||||
"cookie-es": ["cookie-es@2.0.0", "", {}, "sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg=="],
|
"cookie-es": ["cookie-es@1.2.2", "", {}, "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="],
|
||||||
|
|
||||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||||
|
|
||||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||||
|
|
||||||
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
"deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
|
||||||
|
|
||||||
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
||||||
|
|
||||||
"detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
|
"detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
|
||||||
|
|
||||||
"diff": ["diff@8.0.2", "", {}, "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg=="],
|
"diff": ["diff@8.0.2", "", {}, "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg=="],
|
||||||
|
|
||||||
"electron-to-chromium": ["electron-to-chromium@1.5.237", "", {}, "sha512-icUt1NvfhGLar5lSWH3tHNzablaA5js3HVHacQimfP8ViEBOQv+L7DKEuHdbTZ0SKCO1ogTJTIL1Gwk9S6Qvcg=="],
|
"dts-resolver": ["dts-resolver@2.1.2", "", { "peerDependencies": { "oxc-resolver": ">=11.0.0" }, "optionalPeers": ["oxc-resolver"] }, "sha512-xeXHBQkn2ISSXxbJWD828PFjtyg+/UrMDo7W4Ffcs7+YWCquxU8YjV1KoxuiL+eJ5pg3ll+bC6flVv61L3LKZg=="],
|
||||||
|
|
||||||
|
"electron-to-chromium": ["electron-to-chromium@1.5.218", "", {}, "sha512-uwwdN0TUHs8u6iRgN8vKeWZMRll4gBkz+QMqdS7DDe49uiK68/UX92lFb61oiFPrpYZNeZIqa4bA7O6Aiasnzg=="],
|
||||||
|
|
||||||
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||||
|
|
||||||
"enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="],
|
"empathic": ["empathic@2.0.0", "", {}, "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA=="],
|
||||||
|
|
||||||
|
"es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="],
|
||||||
|
|
||||||
"esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="],
|
"esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="],
|
||||||
|
|
||||||
@@ -625,11 +517,15 @@
|
|||||||
|
|
||||||
"esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="],
|
"esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="],
|
||||||
|
|
||||||
|
"estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
|
||||||
|
|
||||||
|
"expect-type": ["expect-type@1.2.2", "", {}, "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA=="],
|
||||||
|
|
||||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||||
|
|
||||||
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||||
|
|
||||||
"framer-motion": ["framer-motion@12.23.24", "", { "dependencies": { "motion-dom": "^12.23.23", "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w=="],
|
"framer-motion": ["framer-motion@12.23.16", "", { "dependencies": { "motion-dom": "^12.23.12", "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-N81A8hiHqVsexOzI3wzkibyLURW1nEJsZaRuctPhG4AdbbciYu+bKJq9I2lQFzAO4Bx3h4swI6pBbF/Hu7f7BA=="],
|
||||||
|
|
||||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||||
|
|
||||||
@@ -639,17 +535,13 @@
|
|||||||
|
|
||||||
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
|
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
|
||||||
|
|
||||||
"get-tsconfig": ["get-tsconfig@4.12.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw=="],
|
"get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="],
|
||||||
|
|
||||||
"glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
"glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||||
|
|
||||||
"goober": ["goober@2.1.18", "", { "peerDependencies": { "csstype": "^3.0.10" } }, "sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw=="],
|
"goober": ["goober@2.1.16", "", { "peerDependencies": { "csstype": "^3.0.10" } }, "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g=="],
|
||||||
|
|
||||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
"hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="],
|
||||||
|
|
||||||
"hash-wasm": ["hash-wasm@4.12.0", "", {}, "sha512-+/2B2rYLb48I/evdOIhP+K/DD2ca2fgBjp6O+GBEnCDk2e4rpeXIK8GvIyRPjTezgmWn9gmKwkQjjx6BtqDHVQ=="],
|
|
||||||
|
|
||||||
"hono": ["hono@4.10.1", "", {}, "sha512-rpGNOfacO4WEPClfkEt1yfl8cbu10uB1lNpiI33AKoiAHwOS8lV748JiLx4b5ozO/u4qLjIvfpFsPXdY5Qjkmg=="],
|
|
||||||
|
|
||||||
"is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="],
|
"is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="],
|
||||||
|
|
||||||
@@ -659,23 +551,13 @@
|
|||||||
|
|
||||||
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
||||||
|
|
||||||
"is-network-error": ["is-network-error@1.3.0", "", {}, "sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw=="],
|
|
||||||
|
|
||||||
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
|
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
|
||||||
|
|
||||||
"isbot": ["isbot@5.1.31", "", {}, "sha512-DPgQshehErHAqSCKDb3rNW03pa2wS/v5evvUqtxt6TTnHRqAG8FdzcSSJs9656pK6Y+NT7K9R4acEYXLHYfpUQ=="],
|
"isbot": ["isbot@5.1.30", "", {}, "sha512-3wVJEonAns1OETX83uWsk5IAne2S5zfDcntD2hbtU23LelSqNXzXs9zKjMPOLMzroCgIjCfjYAEHrd2D6FOkiA=="],
|
||||||
|
|
||||||
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
"jiti": ["jiti@2.5.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w=="],
|
||||||
|
|
||||||
"jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="],
|
"jotai": ["jotai@2.14.0", "", { "peerDependencies": { "@babel/core": ">=7.0.0", "@babel/template": ">=7.0.0", "@types/react": ">=17.0.0", "react": ">=17.0.0" }, "optionalPeers": ["@babel/core", "@babel/template", "@types/react", "react"] }, "sha512-JQkNkTnqjk1BlSUjHfXi+pGG/573bVN104gp6CymhrWDseZGDReTNniWrLhJ+zXbM6pH+82+UNJ2vwYQUkQMWQ=="],
|
||||||
|
|
||||||
"jotai": ["jotai@2.15.0", "", { "peerDependencies": { "@babel/core": ">=7.0.0", "@babel/template": ">=7.0.0", "@types/react": ">=17.0.0", "react": ">=17.0.0" }, "optionalPeers": ["@babel/core", "@babel/template", "@types/react", "react"] }, "sha512-nbp/6jN2Ftxgw0VwoVnOg0m5qYM1rVcfvij+MZx99Z5IK13eGve9FJoCwGv+17JvVthTjhSmNtT5e1coJnr6aw=="],
|
|
||||||
|
|
||||||
"jotai-effect": ["jotai-effect@2.1.3", "", { "peerDependencies": { "jotai": ">=2.14.0" } }, "sha512-gFIqKvW5hljRLaZihqI48SFFYZQifaT3ZDsqBdqyMRRvm++PAWpcmrRWvqG2MrG346Chs4QUWeZzAucgViggDQ=="],
|
|
||||||
|
|
||||||
"jotai-scope": ["jotai-scope@0.9.5", "", { "peerDependencies": { "jotai": ">=2.15.0", "react": ">=16.0.0" } }, "sha512-oOUduQ4ObALHz1+tAyoGeiuNTO3X3H8sUoOfliuMvQqS0HAhTHspFTq06b6SvKQkUtruw98XzVntsrGChmBRNA=="],
|
|
||||||
|
|
||||||
"jotai-tanstack-query": ["jotai-tanstack-query@0.11.0", "", { "peerDependencies": { "@tanstack/query-core": "*", "@tanstack/react-query": "*", "jotai": ">=2.0.0", "react": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@tanstack/react-query", "react"] }, "sha512-Ys0u0IuuS6/okUJOulFTdCVfVaeKbm1+lKVSN9zHhIxtrAXl9FM4yu7fNvxM6fSz/NCE9tZOKR0MQ3hvplaH8A=="],
|
|
||||||
|
|
||||||
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||||
|
|
||||||
@@ -683,29 +565,9 @@
|
|||||||
|
|
||||||
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
||||||
|
|
||||||
"kysely": ["kysely@0.28.8", "", {}, "sha512-QUOgl5ZrS9IRuhq5FvOKFSsD/3+IA6MLE81/bOOTRA/YQpKDza2sFdN5g6JCB9BOpqMJDGefLCQ9F12hRS13TA=="],
|
"jwt-decode": ["jwt-decode@4.0.0", "", {}, "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA=="],
|
||||||
|
|
||||||
"lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="],
|
"loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="],
|
||||||
|
|
||||||
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="],
|
|
||||||
|
|
||||||
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="],
|
|
||||||
|
|
||||||
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="],
|
|
||||||
|
|
||||||
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="],
|
|
||||||
|
|
||||||
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="],
|
|
||||||
|
|
||||||
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="],
|
|
||||||
|
|
||||||
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="],
|
|
||||||
|
|
||||||
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="],
|
|
||||||
|
|
||||||
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="],
|
|
||||||
|
|
||||||
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="],
|
|
||||||
|
|
||||||
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||||
|
|
||||||
@@ -713,30 +575,26 @@
|
|||||||
|
|
||||||
"magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="],
|
"magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="],
|
||||||
|
|
||||||
"minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
|
"motion": ["motion@12.23.16", "", { "dependencies": { "framer-motion": "^12.23.16", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-8vVuxZgcfGZm4kgSqFgGrhQ+6034y4UuEsqCX8s7UYeoQ+NO3R9LV5AyDlVr2Mb7xvS7ZM5s/XkTurWbWQ+UHA=="],
|
||||||
|
|
||||||
"minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="],
|
"motion-dom": ["motion-dom@12.23.12", "", { "dependencies": { "motion-utils": "^12.23.6" } }, "sha512-RcR4fvMCTESQBD/uKQe49D5RUeDOokkGRmz4ceaJKDBgHYtZtntC/s2vLvY38gqGaytinij/yi3hMcWVcEF5Kw=="],
|
||||||
|
|
||||||
"motion": ["motion@12.23.24", "", { "dependencies": { "framer-motion": "^12.23.24", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-Rc5E7oe2YZ72N//S3QXGzbnXgqNrTESv8KKxABR20q2FLch9gHLo0JLyYo2hZ238bZ9Gx6cWhj9VO0IgwbMjCw=="],
|
|
||||||
|
|
||||||
"motion-dom": ["motion-dom@12.23.23", "", { "dependencies": { "motion-utils": "^12.23.6" } }, "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA=="],
|
|
||||||
|
|
||||||
"motion-utils": ["motion-utils@12.23.6", "", {}, "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ=="],
|
"motion-utils": ["motion-utils@12.23.6", "", {}, "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ=="],
|
||||||
|
|
||||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
|
|
||||||
"nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="],
|
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||||
|
|
||||||
"nanostores": ["nanostores@0.11.4", "", {}, "sha512-k1oiVNN4hDK8NcNERSZLQiMfRzEGtfnvZvdBvey3SQbgn8Dcrk0h1I6vpxApjb10PFUflZrgJ2WEZyJQ+5v7YQ=="],
|
|
||||||
|
|
||||||
"next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="],
|
"next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="],
|
||||||
|
|
||||||
"node-releases": ["node-releases@2.0.25", "", {}, "sha512-4auku8B/vw5psvTiiN9j1dAOsXvMoGqJuKJcR+dTdqiXEK20mMTk1UEo3HS16LeGQsVG6+qKTPM9u/qQ2LqATA=="],
|
"node-releases": ["node-releases@2.0.21", "", {}, "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw=="],
|
||||||
|
|
||||||
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
||||||
|
|
||||||
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||||
|
|
||||||
|
"pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="],
|
||||||
|
|
||||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||||
|
|
||||||
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||||
@@ -745,15 +603,11 @@
|
|||||||
|
|
||||||
"prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
|
"prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
|
||||||
|
|
||||||
"pvtsutils": ["pvtsutils@1.3.6", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg=="],
|
"quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="],
|
||||||
|
|
||||||
"pvutils": ["pvutils@1.1.3", "", {}, "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ=="],
|
"react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="],
|
||||||
|
|
||||||
"react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="],
|
"react-dom": ["react-dom@19.1.1", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.1" } }, "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw=="],
|
||||||
|
|
||||||
"react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="],
|
|
||||||
|
|
||||||
"react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
|
|
||||||
|
|
||||||
"react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "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-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="],
|
"react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "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-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="],
|
||||||
|
|
||||||
@@ -765,25 +619,25 @@
|
|||||||
|
|
||||||
"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=="],
|
"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=="],
|
||||||
|
|
||||||
"reflect-metadata": ["reflect-metadata@0.2.2", "", {}, "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q=="],
|
|
||||||
|
|
||||||
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
|
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
|
||||||
|
|
||||||
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
|
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
|
||||||
|
|
||||||
"rollup": ["rollup@4.52.4", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.4", "@rollup/rollup-android-arm64": "4.52.4", "@rollup/rollup-darwin-arm64": "4.52.4", "@rollup/rollup-darwin-x64": "4.52.4", "@rollup/rollup-freebsd-arm64": "4.52.4", "@rollup/rollup-freebsd-x64": "4.52.4", "@rollup/rollup-linux-arm-gnueabihf": "4.52.4", "@rollup/rollup-linux-arm-musleabihf": "4.52.4", "@rollup/rollup-linux-arm64-gnu": "4.52.4", "@rollup/rollup-linux-arm64-musl": "4.52.4", "@rollup/rollup-linux-loong64-gnu": "4.52.4", "@rollup/rollup-linux-ppc64-gnu": "4.52.4", "@rollup/rollup-linux-riscv64-gnu": "4.52.4", "@rollup/rollup-linux-riscv64-musl": "4.52.4", "@rollup/rollup-linux-s390x-gnu": "4.52.4", "@rollup/rollup-linux-x64-gnu": "4.52.4", "@rollup/rollup-linux-x64-musl": "4.52.4", "@rollup/rollup-openharmony-arm64": "4.52.4", "@rollup/rollup-win32-arm64-msvc": "4.52.4", "@rollup/rollup-win32-ia32-msvc": "4.52.4", "@rollup/rollup-win32-x64-gnu": "4.52.4", "@rollup/rollup-win32-x64-msvc": "4.52.4", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ=="],
|
"rolldown": ["rolldown@1.0.0-beta.9-commit.d91dfb5", "", { "dependencies": { "@oxc-project/runtime": "0.71.0", "@oxc-project/types": "0.71.0", "@rolldown/pluginutils": "1.0.0-beta.9-commit.d91dfb5", "ansis": "^4.0.0" }, "optionalDependencies": { "@rolldown/binding-darwin-arm64": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-darwin-x64": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-freebsd-x64": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-linux-x64-musl": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-wasm32-wasi": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.9-commit.d91dfb5" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-FHkj6gGEiEgmAXQchglofvUUdwj2Oiw603Rs+zgFAnn9Cb7T7z3fiaEc0DbN3ja4wYkW6sF2rzMEtC1V4BGx/g=="],
|
||||||
|
|
||||||
"rou3": ["rou3@0.5.1", "", {}, "sha512-OXMmJ3zRk2xeXFGfA3K+EOPHC5u7RDFG7lIOx0X1pdnhUkI8MdVrbV+sNsD80ElpUZ+MRHdyxPnFthq9VHs8uQ=="],
|
"rolldown-plugin-dts": ["rolldown-plugin-dts@0.13.14", "", { "dependencies": { "@babel/generator": "^7.28.0", "@babel/parser": "^7.28.0", "@babel/types": "^7.28.1", "ast-kit": "^2.1.1", "birpc": "^2.5.0", "debug": "^4.4.1", "dts-resolver": "^2.1.1", "get-tsconfig": "^4.10.1" }, "peerDependencies": { "@typescript/native-preview": ">=7.0.0-dev.20250601.1", "rolldown": "^1.0.0-beta.9", "typescript": "^5.0.0", "vue-tsc": "^2.2.0 || ^3.0.0" }, "optionalPeers": ["@typescript/native-preview", "typescript", "vue-tsc"] }, "sha512-wjNhHZz9dlN6PTIXyizB6u/mAg1wEFMW9yw7imEVe3CxHSRnNHVyycIX0yDEOVJfDNISLPbkCIPEpFpizy5+PQ=="],
|
||||||
|
|
||||||
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
"rollup": ["rollup@4.50.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.50.1", "@rollup/rollup-android-arm64": "4.50.1", "@rollup/rollup-darwin-arm64": "4.50.1", "@rollup/rollup-darwin-x64": "4.50.1", "@rollup/rollup-freebsd-arm64": "4.50.1", "@rollup/rollup-freebsd-x64": "4.50.1", "@rollup/rollup-linux-arm-gnueabihf": "4.50.1", "@rollup/rollup-linux-arm-musleabihf": "4.50.1", "@rollup/rollup-linux-arm64-gnu": "4.50.1", "@rollup/rollup-linux-arm64-musl": "4.50.1", "@rollup/rollup-linux-loongarch64-gnu": "4.50.1", "@rollup/rollup-linux-ppc64-gnu": "4.50.1", "@rollup/rollup-linux-riscv64-gnu": "4.50.1", "@rollup/rollup-linux-riscv64-musl": "4.50.1", "@rollup/rollup-linux-s390x-gnu": "4.50.1", "@rollup/rollup-linux-x64-gnu": "4.50.1", "@rollup/rollup-linux-x64-musl": "4.50.1", "@rollup/rollup-openharmony-arm64": "4.50.1", "@rollup/rollup-win32-arm64-msvc": "4.50.1", "@rollup/rollup-win32-ia32-msvc": "4.50.1", "@rollup/rollup-win32-x64-msvc": "4.50.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA=="],
|
||||||
|
|
||||||
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
"scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
|
||||||
|
|
||||||
|
"semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
||||||
|
|
||||||
"seroval": ["seroval@1.3.2", "", {}, "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ=="],
|
"seroval": ["seroval@1.3.2", "", {}, "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ=="],
|
||||||
|
|
||||||
"seroval-plugins": ["seroval-plugins@1.3.3", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w=="],
|
"seroval-plugins": ["seroval-plugins@1.3.3", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w=="],
|
||||||
|
|
||||||
"set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="],
|
"siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="],
|
||||||
|
|
||||||
"solid-js": ["solid-js@1.9.9", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.3.0", "seroval-plugins": "~1.3.0" } }, "sha512-A0ZBPJQldAeGCTW0YRYJmt7RCeh5rbFfPZ2aOttgYnctHE7HgKeHCBB/PVc2P7eOfmNXqMFFFoYYdm3S4dcbkA=="],
|
"solid-js": ["solid-js@1.9.9", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.3.0", "seroval-plugins": "~1.3.0" } }, "sha512-A0ZBPJQldAeGCTW0YRYJmt7RCeh5rbFfPZ2aOttgYnctHE7HgKeHCBB/PVc2P7eOfmNXqMFFFoYYdm3S4dcbkA=="],
|
||||||
|
|
||||||
@@ -793,43 +647,51 @@
|
|||||||
|
|
||||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||||
|
|
||||||
|
"stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
|
||||||
|
|
||||||
|
"std-env": ["std-env@3.9.0", "", {}, "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw=="],
|
||||||
|
|
||||||
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||||
|
|
||||||
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||||
|
|
||||||
|
"strip-literal": ["strip-literal@3.0.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA=="],
|
||||||
|
|
||||||
"tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="],
|
"tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="],
|
||||||
|
|
||||||
"tailwindcss": ["tailwindcss@4.1.14", "", {}, "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA=="],
|
"tailwindcss": ["tailwindcss@4.1.13", "", {}, "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w=="],
|
||||||
|
|
||||||
"tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
|
|
||||||
|
|
||||||
"tar": ["tar@7.5.1", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g=="],
|
|
||||||
|
|
||||||
"tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="],
|
"tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="],
|
||||||
|
|
||||||
"tiny-warning": ["tiny-warning@1.0.3", "", {}, "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="],
|
"tiny-warning": ["tiny-warning@1.0.3", "", {}, "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="],
|
||||||
|
|
||||||
|
"tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
|
||||||
|
|
||||||
|
"tinyexec": ["tinyexec@1.0.1", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="],
|
||||||
|
|
||||||
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||||
|
|
||||||
|
"tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="],
|
||||||
|
|
||||||
|
"tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="],
|
||||||
|
|
||||||
|
"tinyspy": ["tinyspy@4.0.3", "", {}, "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A=="],
|
||||||
|
|
||||||
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
||||||
|
|
||||||
|
"tsdown": ["tsdown@0.12.9", "", { "dependencies": { "ansis": "^4.1.0", "cac": "^6.7.14", "chokidar": "^4.0.3", "debug": "^4.4.1", "diff": "^8.0.2", "empathic": "^2.0.0", "hookable": "^5.5.3", "rolldown": "^1.0.0-beta.19", "rolldown-plugin-dts": "^0.13.12", "semver": "^7.7.2", "tinyexec": "^1.0.1", "tinyglobby": "^0.2.14", "unconfig": "^7.3.2" }, "peerDependencies": { "@arethetypeswrong/core": "^0.18.1", "publint": "^0.3.0", "typescript": "^5.0.0", "unplugin-lightningcss": "^0.4.0", "unplugin-unused": "^0.5.0" }, "optionalPeers": ["@arethetypeswrong/core", "publint", "typescript", "unplugin-lightningcss", "unplugin-unused"], "bin": { "tsdown": "dist/run.mjs" } }, "sha512-MfrXm9PIlT3saovtWKf/gCJJ/NQCdE0SiREkdNC+9Qy6UHhdeDPxnkFaBD7xttVUmgp0yUHtGirpoLB+OVLuLA=="],
|
||||||
|
|
||||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
|
|
||||||
"tsx": ["tsx@4.20.6", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg=="],
|
"tsx": ["tsx@4.20.5", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw=="],
|
||||||
|
|
||||||
"tsyringe": ["tsyringe@4.10.0", "", { "dependencies": { "tslib": "^1.9.3" } }, "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw=="],
|
"tw-animate-css": ["tw-animate-css@1.3.8", "", {}, "sha512-Qrk3PZ7l7wUcGYhwZloqfkWCmaXZAoqjkdbIDvzfGshwGtexa/DAs9koXxIkrpEasyevandomzCBAV1Yyop5rw=="],
|
||||||
|
|
||||||
"tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="],
|
"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
|
||||||
|
|
||||||
"type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
|
"unconfig": ["unconfig@7.3.3", "", { "dependencies": { "@quansync/fs": "^0.1.5", "defu": "^6.1.4", "jiti": "^2.5.1", "quansync": "^0.2.11" } }, "sha512-QCkQoOnJF8L107gxfHL0uavn7WD9b3dpBcFX6HtfQYmjw2YzWxGuFQ0N0J6tE9oguCBJn9KOvfqYDCMPHIZrBA=="],
|
||||||
|
|
||||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
"undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="],
|
||||||
|
|
||||||
"uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="],
|
|
||||||
|
|
||||||
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
|
||||||
|
|
||||||
"unplugin": ["unplugin@2.3.10", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw=="],
|
|
||||||
|
|
||||||
"update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="],
|
"update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="],
|
||||||
|
|
||||||
@@ -837,17 +699,21 @@
|
|||||||
|
|
||||||
"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-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=="],
|
"use-sync-external-store": ["use-sync-external-store@1.5.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A=="],
|
||||||
|
|
||||||
"vite": ["vite@7.1.10", "", { "dependencies": { "esbuild": "^0.25.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-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA=="],
|
"vite": ["vite@7.1.5", "", { "dependencies": { "esbuild": "^0.25.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-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ=="],
|
||||||
|
|
||||||
"webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
|
"vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="],
|
||||||
|
|
||||||
|
"vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="],
|
||||||
|
|
||||||
|
"why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
|
||||||
|
|
||||||
"wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
"wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||||
|
|
||||||
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
|
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
|
||||||
|
|
||||||
"yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
|
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||||
|
|
||||||
"yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
|
"yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
|
||||||
|
|
||||||
@@ -855,36 +721,26 @@
|
|||||||
|
|
||||||
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="],
|
"@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.5.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="],
|
"@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
|
"@babel/helper-create-class-features-plugin/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.0.7", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw=="],
|
"@convex-dev/workos/@workos-inc/authkit-react": ["@workos-inc/authkit-react@0.11.0", "", { "dependencies": { "@workos-inc/authkit-js": "0.13.0" }, "peerDependencies": { "react": ">=17" } }, "sha512-67HFSxP4wXC8ECGyvc1yGMwuD5NGkwT2OPt8DavHoKAlO+hRaAlu9wwzqUx1EJrHht0Dcx+l20Byq8Ab0bEhlg=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
|
||||||
|
|
||||||
"@tanstack/react-router/@tanstack/router-core": ["@tanstack/router-core@1.133.12", "", { "dependencies": { "@tanstack/history": "1.133.3", "@tanstack/store": "^0.7.0", "cookie-es": "^2.0.0", "seroval": "^1.3.2", "seroval-plugins": "^1.3.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-mlU0WP3GvirLxVDOe7OO3RkSR/dKxZJ4OxqmvNzDZI2K6E867AVBNwfNecAnuwTJpW6jFELoYMwmulMtA1PSxw=="],
|
|
||||||
|
|
||||||
"@tanstack/router-generator/@tanstack/router-core": ["@tanstack/router-core@1.133.12", "", { "dependencies": { "@tanstack/history": "1.133.3", "@tanstack/store": "^0.7.0", "cookie-es": "^2.0.0", "seroval": "^1.3.2", "seroval-plugins": "^1.3.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-mlU0WP3GvirLxVDOe7OO3RkSR/dKxZJ4OxqmvNzDZI2K6E867AVBNwfNecAnuwTJpW6jFELoYMwmulMtA1PSxw=="],
|
|
||||||
|
|
||||||
"@tanstack/router-plugin/@tanstack/router-generator": ["@tanstack/router-generator@1.133.13", "", { "dependencies": { "@tanstack/router-core": "1.133.13", "@tanstack/router-utils": "1.133.3", "@tanstack/virtual-file-routes": "1.133.3", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-W5locmcYSz0dY+KEOIFijUeOdQEzjCxY+uT9ExY/YeQcOBcBFIk9/UnBkE6wRLCPOBb1gfURjPNc9rI93HGrOA=="],
|
|
||||||
|
|
||||||
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
|
|
||||||
"better-auth/zod": ["zod@4.1.12", "", {}, "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ=="],
|
|
||||||
|
|
||||||
"lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
|
||||||
|
|
||||||
"postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
|
||||||
|
|
||||||
"readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
|
|
||||||
"recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
"recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||||
|
|
||||||
"tsyringe/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
|
"strip-literal/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="],
|
||||||
|
|
||||||
|
"tsdown/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
||||||
|
|
||||||
|
"vitest/tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
|
||||||
|
|
||||||
|
"tsdown/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
package.json
14
package.json
@@ -4,24 +4,16 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/*",
|
"packages/*"
|
||||||
"apps/*"
|
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun run --elide-lines=0 --filter './apps/*' dev",
|
"dev": "bun run --filter=@fileone/web dev",
|
||||||
"build": "bun run --filter=@fileone/web build",
|
"build": "bun run --filter=@fileone/web build",
|
||||||
"preview": "bun run --filter=@fileone/web preview",
|
"start": "bun run --filter=@fileone/web start"
|
||||||
"drexa": "bun run apps/cli/index.ts"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "2.2.4",
|
"@biomejs/biome": "2.2.4",
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
"convex": "^1.27.0"
|
"convex": "^1.27.0"
|
||||||
},
|
|
||||||
"resolutions": {
|
|
||||||
"convex": "1.28.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@tailwindcss/vite": "^4.1.14"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
34
packages/auth/.gitignore
vendored
34
packages/auth/.gitignore
vendored
@@ -1,34 +0,0 @@
|
|||||||
# dependencies (bun install)
|
|
||||||
node_modules
|
|
||||||
|
|
||||||
# output
|
|
||||||
out
|
|
||||||
dist
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# code coverage
|
|
||||||
coverage
|
|
||||||
*.lcov
|
|
||||||
|
|
||||||
# logs
|
|
||||||
logs
|
|
||||||
_.log
|
|
||||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
|
||||||
|
|
||||||
# dotenv environment variable files
|
|
||||||
.env
|
|
||||||
.env.development.local
|
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
.env.local
|
|
||||||
|
|
||||||
# caches
|
|
||||||
.eslintcache
|
|
||||||
.cache
|
|
||||||
*.tsbuildinfo
|
|
||||||
|
|
||||||
# IntelliJ based IDEs
|
|
||||||
.idea
|
|
||||||
|
|
||||||
# Finder (MacOS) folder config
|
|
||||||
.DS_Store
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# @drexa/auth
|
|
||||||
|
|
||||||
To install dependencies:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun install
|
|
||||||
```
|
|
||||||
|
|
||||||
To run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun run index.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
This project was created using `bun init` in bun v1.3.0. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
export interface PassswordHasher {
|
|
||||||
hash(password: string): Promise<string>
|
|
||||||
verify(password: string, hash: string): Promise<boolean>
|
|
||||||
}
|
|
||||||
|
|
||||||
export class BunSha256Hasher implements PassswordHasher {
|
|
||||||
async hash(password: string): Promise<string> {
|
|
||||||
const hasher = new Bun.CryptoHasher("sha256")
|
|
||||||
hasher.update(password)
|
|
||||||
return hasher.digest("base64url")
|
|
||||||
}
|
|
||||||
|
|
||||||
async verify(password: string, hash: string): Promise<boolean> {
|
|
||||||
const hasher = new Bun.CryptoHasher("sha256")
|
|
||||||
hasher.update(password)
|
|
||||||
|
|
||||||
const passwordHash = hasher.digest()
|
|
||||||
const hashBytes = Buffer.from(hash, "base64url")
|
|
||||||
|
|
||||||
if (passwordHash.byteLength !== hashBytes.byteLength) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return crypto.timingSafeEqual(passwordHash, hashBytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class WebCryptoSha256Hasher implements PassswordHasher {
|
|
||||||
async hash(password: string): Promise<string> {
|
|
||||||
const hash = await crypto.subtle.digest(
|
|
||||||
"SHA-256",
|
|
||||||
new TextEncoder().encode(password),
|
|
||||||
)
|
|
||||||
return this.arrayBufferToBase64url(hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
async verify(password: string, hash: string): Promise<boolean> {
|
|
||||||
const passwordHash = await crypto.subtle.digest(
|
|
||||||
"SHA-256",
|
|
||||||
new TextEncoder().encode(password),
|
|
||||||
)
|
|
||||||
const hashBytes = this.base64urlToArrayBuffer(hash)
|
|
||||||
|
|
||||||
if (passwordHash.byteLength !== hashBytes.byteLength) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Timing-safe comparison
|
|
||||||
const passwordHashBytes = new Uint8Array(passwordHash)
|
|
||||||
let result = 0
|
|
||||||
for (let i = 0; i < passwordHashBytes.length; i++) {
|
|
||||||
result |= passwordHashBytes[i]! ^ hashBytes[i]!
|
|
||||||
}
|
|
||||||
return result === 0
|
|
||||||
}
|
|
||||||
|
|
||||||
private arrayBufferToBase64url(buffer: ArrayBuffer): string {
|
|
||||||
const bytes = new Uint8Array(buffer)
|
|
||||||
let binary = ""
|
|
||||||
for (let i = 0; i < bytes.length; i++) {
|
|
||||||
binary += String.fromCharCode(bytes[i]!)
|
|
||||||
}
|
|
||||||
return btoa(binary).replace(/[+/=]/g, (char) => {
|
|
||||||
switch (char) {
|
|
||||||
case "+": return "-"
|
|
||||||
case "/": return "_"
|
|
||||||
case "=": return ""
|
|
||||||
default: return char
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private base64urlToArrayBuffer(base64url: string): Uint8Array {
|
|
||||||
const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/")
|
|
||||||
|
|
||||||
const padded = base64.padEnd(
|
|
||||||
base64.length + ((4 - (base64.length % 4)) % 4),
|
|
||||||
"=",
|
|
||||||
)
|
|
||||||
|
|
||||||
const binary = atob(padded)
|
|
||||||
const bytes = new Uint8Array(binary.length)
|
|
||||||
for (let i = 0; i < binary.length; i++) {
|
|
||||||
bytes[i] = binary.charCodeAt(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
import { BunSha256Hasher, type PassswordHasher } from "./hasher"
|
|
||||||
|
|
||||||
export { WebCryptoSha256Hasher } from "./hasher"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An unhashed api key.
|
|
||||||
* Always starts with sk, then the prefix specified at time of generation,
|
|
||||||
* and ends with the base64url-encoded key.
|
|
||||||
*/
|
|
||||||
export type UnhashedApiKey = `sk-${ApiKeyPrefix}-${string}`
|
|
||||||
|
|
||||||
export type ApiKeyPrefix = string & { __brand: "ApiKeyPrefix" }
|
|
||||||
|
|
||||||
export type ParsedApiKey = {
|
|
||||||
prefix: ApiKeyPrefix
|
|
||||||
unhashedKey: UnhashedApiKey
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GenerateApiKeyOptions = {
|
|
||||||
/**
|
|
||||||
* How long the key should be (excluding prefix) in bytes.
|
|
||||||
*/
|
|
||||||
keyByteLength: number
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prefix of the api key. Can be a brief name of the consumer of the key.
|
|
||||||
* For example, if the prefix is "proxy", the key will be "sk-proxy-asdasjdjsdkjd.."
|
|
||||||
*/
|
|
||||||
prefix: ApiKeyPrefix
|
|
||||||
|
|
||||||
expiresAt?: Date
|
|
||||||
description: string
|
|
||||||
|
|
||||||
hasher?: PassswordHasher
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GenerateApiKeyResult = {
|
|
||||||
unhashedKey: UnhashedApiKey
|
|
||||||
hashedKey: string
|
|
||||||
expiresAt?: Date
|
|
||||||
description: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type VerifyApiKeyOptions = {
|
|
||||||
keyToBeVerified: UnhashedApiKey
|
|
||||||
hashedKey: string
|
|
||||||
hasher?: PassswordHasher
|
|
||||||
}
|
|
||||||
|
|
||||||
function validatePrefix(prefix: string): prefix is ApiKeyPrefix {
|
|
||||||
return !prefix.includes("-")
|
|
||||||
}
|
|
||||||
|
|
||||||
export function newPrefix(prefix: string): ApiKeyPrefix | null {
|
|
||||||
if (!validatePrefix(prefix)) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return prefix
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function generateApiKey({
|
|
||||||
keyByteLength,
|
|
||||||
prefix,
|
|
||||||
expiresAt,
|
|
||||||
description,
|
|
||||||
hasher = new BunSha256Hasher(),
|
|
||||||
}: GenerateApiKeyOptions): Promise<GenerateApiKeyResult> {
|
|
||||||
const keyContent = new Uint8Array(keyByteLength)
|
|
||||||
crypto.getRandomValues(keyContent)
|
|
||||||
|
|
||||||
const base64KeyContent = Buffer.from(keyContent).toString("base64url")
|
|
||||||
const unhashedKey: UnhashedApiKey = `sk-${prefix}-${base64KeyContent}`
|
|
||||||
|
|
||||||
const hashedKey = await hasher.hash(unhashedKey)
|
|
||||||
|
|
||||||
return {
|
|
||||||
unhashedKey,
|
|
||||||
hashedKey,
|
|
||||||
expiresAt,
|
|
||||||
description,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parseApiKey(key: string): ParsedApiKey | null {
|
|
||||||
if (!key.startsWith("sk-")) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const parts = key.split("-")
|
|
||||||
if (parts.length !== 3) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const prefix = parts[1]
|
|
||||||
if (!prefix || !validatePrefix(prefix)) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
prefix,
|
|
||||||
unhashedKey: key as UnhashedApiKey,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function verifyApiKey({
|
|
||||||
keyToBeVerified,
|
|
||||||
hashedKey,
|
|
||||||
hasher = new BunSha256Hasher(),
|
|
||||||
}: VerifyApiKeyOptions): Promise<boolean> {
|
|
||||||
return await hasher.verify(keyToBeVerified, hashedKey)
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@drexa/auth",
|
|
||||||
"module": "index.ts",
|
|
||||||
"type": "module",
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/bun": "latest"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"typescript": "^5"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
// Environment setup & latest features
|
|
||||||
"lib": ["ESNext"],
|
|
||||||
"target": "ESNext",
|
|
||||||
"module": "Preserve",
|
|
||||||
"moduleDetection": "force",
|
|
||||||
"jsx": "react-jsx",
|
|
||||||
"allowJs": true,
|
|
||||||
|
|
||||||
// Bundler mode
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowImportingTsExtensions": true,
|
|
||||||
"verbatimModuleSyntax": true,
|
|
||||||
"noEmit": true,
|
|
||||||
|
|
||||||
// Best practices
|
|
||||||
"strict": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"noUncheckedIndexedAccess": true,
|
|
||||||
"noImplicitOverride": true,
|
|
||||||
|
|
||||||
// Some stricter flags (disabled by default)
|
|
||||||
"noUnusedLocals": false,
|
|
||||||
"noUnusedParameters": false,
|
|
||||||
"noPropertyAccessFromIndexSignature": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
# Welcome to your Convex functions directory!
|
|
||||||
|
|
||||||
Write your Convex functions here.
|
|
||||||
See https://docs.convex.dev/functions for more.
|
|
||||||
|
|
||||||
A query function that takes two arguments looks like:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// convex/myFunctions.ts
|
|
||||||
import { query } from "./_generated/server";
|
|
||||||
import { v } from "convex/values";
|
|
||||||
|
|
||||||
export const myQueryFunction = query({
|
|
||||||
// Validators for arguments.
|
|
||||||
args: {
|
|
||||||
first: v.number(),
|
|
||||||
second: v.string(),
|
|
||||||
},
|
|
||||||
|
|
||||||
// Function implementation.
|
|
||||||
handler: async (ctx, args) => {
|
|
||||||
// Read the database as many times as you need here.
|
|
||||||
// See https://docs.convex.dev/database/reading-data.
|
|
||||||
const documents = await ctx.db.query("tablename").collect();
|
|
||||||
|
|
||||||
// Arguments passed from the client are properties of the args object.
|
|
||||||
console.log(args.first, args.second);
|
|
||||||
|
|
||||||
// Write arbitrary JavaScript here: filter, aggregate, build derived data,
|
|
||||||
// remove non-public properties, or create new objects.
|
|
||||||
return documents;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
Using this query function in a React component looks like:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const data = useQuery(api.myFunctions.myQueryFunction, {
|
|
||||||
first: 10,
|
|
||||||
second: "hello",
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
A mutation function looks like:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// convex/myFunctions.ts
|
|
||||||
import { mutation } from "./_generated/server";
|
|
||||||
import { v } from "convex/values";
|
|
||||||
|
|
||||||
export const myMutationFunction = mutation({
|
|
||||||
// Validators for arguments.
|
|
||||||
args: {
|
|
||||||
first: v.string(),
|
|
||||||
second: v.string(),
|
|
||||||
},
|
|
||||||
|
|
||||||
// Function implementation.
|
|
||||||
handler: async (ctx, args) => {
|
|
||||||
// Insert or modify documents in the database here.
|
|
||||||
// Mutations can also read from the database like queries.
|
|
||||||
// See https://docs.convex.dev/database/writing-data.
|
|
||||||
const message = { body: args.first, author: args.second };
|
|
||||||
const id = await ctx.db.insert("messages", message);
|
|
||||||
|
|
||||||
// Optionally, return a value from your mutation.
|
|
||||||
return await ctx.db.get(id);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
Using this mutation function in a React component looks like:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const mutation = useMutation(api.myFunctions.myMutationFunction);
|
|
||||||
function handleButtonPress() {
|
|
||||||
// fire and forget, the most common way to use mutations
|
|
||||||
mutation({ first: "Hello!", second: "me" });
|
|
||||||
// OR
|
|
||||||
// use the result once the mutation has completed
|
|
||||||
mutation({ first: "Hello!", second: "me" }).then((result) =>
|
|
||||||
console.log(result),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Use the Convex CLI to push your functions to a deployment. See everything
|
|
||||||
the Convex CLI can do by running `npx convex -h` in your project root
|
|
||||||
directory. To learn more, launch the docs with `npx convex docs`.
|
|
||||||
987
packages/convex/_generated/api.d.ts
vendored
987
packages/convex/_generated/api.d.ts
vendored
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@
|
|||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { anyApi, componentsGeneric } from "convex/server";
|
import { anyApi } from "convex/server";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A utility for referencing Convex functions in your app's API.
|
* A utility for referencing Convex functions in your app's API.
|
||||||
@@ -20,4 +20,3 @@ import { anyApi, componentsGeneric } from "convex/server";
|
|||||||
*/
|
*/
|
||||||
export const api = anyApi;
|
export const api = anyApi;
|
||||||
export const internal = anyApi;
|
export const internal = anyApi;
|
||||||
export const components = componentsGeneric();
|
|
||||||
|
|||||||
7
packages/convex/_generated/server.d.ts
vendored
7
packages/convex/_generated/server.d.ts
vendored
@@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
ActionBuilder,
|
ActionBuilder,
|
||||||
AnyComponents,
|
|
||||||
HttpActionBuilder,
|
HttpActionBuilder,
|
||||||
MutationBuilder,
|
MutationBuilder,
|
||||||
QueryBuilder,
|
QueryBuilder,
|
||||||
@@ -19,15 +18,9 @@ import {
|
|||||||
GenericQueryCtx,
|
GenericQueryCtx,
|
||||||
GenericDatabaseReader,
|
GenericDatabaseReader,
|
||||||
GenericDatabaseWriter,
|
GenericDatabaseWriter,
|
||||||
FunctionReference,
|
|
||||||
} from "convex/server";
|
} from "convex/server";
|
||||||
import type { DataModel } from "./dataModel.js";
|
import type { DataModel } from "./dataModel.js";
|
||||||
|
|
||||||
type GenericCtx =
|
|
||||||
| GenericActionCtx<DataModel>
|
|
||||||
| GenericMutationCtx<DataModel>
|
|
||||||
| GenericQueryCtx<DataModel>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define a query in this Convex app's public API.
|
* Define a query in this Convex app's public API.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import {
|
|||||||
internalActionGeneric,
|
internalActionGeneric,
|
||||||
internalMutationGeneric,
|
internalMutationGeneric,
|
||||||
internalQueryGeneric,
|
internalQueryGeneric,
|
||||||
componentsGeneric,
|
|
||||||
} from "convex/server";
|
} from "convex/server";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { verifyApiKey as _verifyApiKey, parseApiKey } from "@drexa/auth"
|
|
||||||
import { WebCryptoSha256Hasher } from "@drexa/auth/hasher"
|
|
||||||
import { v } from "convex/values"
|
|
||||||
import { query } from "./_generated/server"
|
|
||||||
import * as ApiKey from "./model/apikey"
|
|
||||||
|
|
||||||
export const verifyApiKey = query({
|
|
||||||
args: {
|
|
||||||
unhashedKey: v.string(),
|
|
||||||
},
|
|
||||||
handler: async (ctx, args) => {
|
|
||||||
return await ApiKey.verifyApiKey(ctx, args.unhashedKey)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -1,8 +1,22 @@
|
|||||||
export default {
|
const clientId = process.env.WORKOS_CLIENT_ID
|
||||||
|
|
||||||
|
const authConfig = {
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
domain: process.env.CONVEX_SITE_URL,
|
type: "customJwt",
|
||||||
applicationID: "convex",
|
issuer: `https://api.workos.com/`,
|
||||||
|
algorithm: "RS256",
|
||||||
|
jwks: `https://api.workos.com/sso/jwks/${clientId}`,
|
||||||
|
applicationID: clientId,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "customJwt",
|
||||||
|
issuer: `https://api.workos.com/user_management/${clientId}`,
|
||||||
|
algorithm: "RS256",
|
||||||
|
jwks: `https://api.workos.com/sso/jwks/${clientId}`,
|
||||||
|
applicationID: clientId,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default authConfig
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
import { createClient, type GenericCtx } from "@convex-dev/better-auth"
|
|
||||||
import { convex, crossDomain } from "@convex-dev/better-auth/plugins"
|
|
||||||
import { components } from "@fileone/convex/api"
|
|
||||||
import type { DataModel } from "@fileone/convex/dataModel"
|
|
||||||
import { betterAuth } from "better-auth"
|
|
||||||
import authSchema from "./betterauth/schema"
|
|
||||||
|
|
||||||
const siteUrl = process.env.SITE_URL!
|
|
||||||
|
|
||||||
// The component client has methods needed for integrating Convex with Better Auth,
|
|
||||||
// as well as helper methods for general use.
|
|
||||||
export const authComponent = createClient<DataModel, typeof authSchema>(
|
|
||||||
components.betterAuth,
|
|
||||||
{
|
|
||||||
local: {
|
|
||||||
schema: authSchema,
|
|
||||||
},
|
|
||||||
triggers: {
|
|
||||||
user: {
|
|
||||||
onCreate: async (ctx, user) => {
|
|
||||||
const now = Date.now()
|
|
||||||
await ctx.db.insert("directories", {
|
|
||||||
name: "",
|
|
||||||
userId: user._id,
|
|
||||||
createdAt: now,
|
|
||||||
updatedAt: now,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
export const createAuth = (
|
|
||||||
ctx: GenericCtx<DataModel>,
|
|
||||||
{ optionsOnly } = { optionsOnly: false },
|
|
||||||
) => {
|
|
||||||
return betterAuth({
|
|
||||||
// disable logging when createAuth is called just to generate options.
|
|
||||||
// this is not required, but there's a lot of noise in logs without it.
|
|
||||||
logger: {
|
|
||||||
disabled: optionsOnly,
|
|
||||||
},
|
|
||||||
trustedOrigins: [siteUrl],
|
|
||||||
database: authComponent.adapter(ctx),
|
|
||||||
// Configure simple, non-verified email/password to get started
|
|
||||||
emailAndPassword: {
|
|
||||||
enabled: true,
|
|
||||||
requireEmailVerification: false,
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
// The cross domain plugin is required for client side frameworks
|
|
||||||
crossDomain({ siteUrl }),
|
|
||||||
// The Convex plugin is required for Convex compatibility
|
|
||||||
convex(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
971
packages/convex/betterauth/_generated/api.d.ts
vendored
971
packages/convex/betterauth/_generated/api.d.ts
vendored
@@ -1,971 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* Generated `api` utility.
|
|
||||||
*
|
|
||||||
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
||||||
*
|
|
||||||
* To regenerate, run `npx convex dev`.
|
|
||||||
* @module
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type * as adapter from "../adapter.js";
|
|
||||||
import type * as auth from "../auth.js";
|
|
||||||
|
|
||||||
import type {
|
|
||||||
ApiFromModules,
|
|
||||||
FilterApi,
|
|
||||||
FunctionReference,
|
|
||||||
} from "convex/server";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A utility for referencing Convex functions in your app's API.
|
|
||||||
*
|
|
||||||
* Usage:
|
|
||||||
* ```js
|
|
||||||
* const myFunctionReference = api.myModule.myFunction;
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
declare const fullApi: ApiFromModules<{
|
|
||||||
adapter: typeof adapter;
|
|
||||||
auth: typeof auth;
|
|
||||||
}>;
|
|
||||||
export type Mounts = {
|
|
||||||
adapter: {
|
|
||||||
create: FunctionReference<
|
|
||||||
"mutation",
|
|
||||||
"public",
|
|
||||||
{
|
|
||||||
input:
|
|
||||||
| {
|
|
||||||
data: {
|
|
||||||
createdAt: number;
|
|
||||||
email: string;
|
|
||||||
emailVerified: boolean;
|
|
||||||
image?: null | string;
|
|
||||||
name: string;
|
|
||||||
updatedAt: number;
|
|
||||||
userId?: null | string;
|
|
||||||
};
|
|
||||||
model: "user";
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
data: {
|
|
||||||
createdAt: number;
|
|
||||||
expiresAt: number;
|
|
||||||
ipAddress?: null | string;
|
|
||||||
token: string;
|
|
||||||
updatedAt: number;
|
|
||||||
userAgent?: null | string;
|
|
||||||
userId: string;
|
|
||||||
};
|
|
||||||
model: "session";
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
data: {
|
|
||||||
accessToken?: null | string;
|
|
||||||
accessTokenExpiresAt?: null | number;
|
|
||||||
accountId: string;
|
|
||||||
createdAt: number;
|
|
||||||
idToken?: null | string;
|
|
||||||
password?: null | string;
|
|
||||||
providerId: string;
|
|
||||||
refreshToken?: null | string;
|
|
||||||
refreshTokenExpiresAt?: null | number;
|
|
||||||
scope?: null | string;
|
|
||||||
updatedAt: number;
|
|
||||||
userId: string;
|
|
||||||
};
|
|
||||||
model: "account";
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
data: {
|
|
||||||
createdAt: number;
|
|
||||||
expiresAt: number;
|
|
||||||
identifier: string;
|
|
||||||
updatedAt: number;
|
|
||||||
value: string;
|
|
||||||
};
|
|
||||||
model: "verification";
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
data: {
|
|
||||||
createdAt: number;
|
|
||||||
privateKey: string;
|
|
||||||
publicKey: string;
|
|
||||||
};
|
|
||||||
model: "jwks";
|
|
||||||
};
|
|
||||||
onCreateHandle?: string;
|
|
||||||
select?: Array<string>;
|
|
||||||
},
|
|
||||||
any
|
|
||||||
>;
|
|
||||||
deleteMany: FunctionReference<
|
|
||||||
"mutation",
|
|
||||||
"public",
|
|
||||||
{
|
|
||||||
input:
|
|
||||||
| {
|
|
||||||
model: "user";
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field:
|
|
||||||
| "name"
|
|
||||||
| "email"
|
|
||||||
| "emailVerified"
|
|
||||||
| "image"
|
|
||||||
| "createdAt"
|
|
||||||
| "updatedAt"
|
|
||||||
| "userId"
|
|
||||||
| "id";
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
model: "session";
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field:
|
|
||||||
| "expiresAt"
|
|
||||||
| "token"
|
|
||||||
| "createdAt"
|
|
||||||
| "updatedAt"
|
|
||||||
| "ipAddress"
|
|
||||||
| "userAgent"
|
|
||||||
| "userId"
|
|
||||||
| "id";
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
model: "account";
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field:
|
|
||||||
| "accountId"
|
|
||||||
| "providerId"
|
|
||||||
| "userId"
|
|
||||||
| "accessToken"
|
|
||||||
| "refreshToken"
|
|
||||||
| "idToken"
|
|
||||||
| "accessTokenExpiresAt"
|
|
||||||
| "refreshTokenExpiresAt"
|
|
||||||
| "scope"
|
|
||||||
| "password"
|
|
||||||
| "createdAt"
|
|
||||||
| "updatedAt"
|
|
||||||
| "id";
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
model: "verification";
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field:
|
|
||||||
| "identifier"
|
|
||||||
| "value"
|
|
||||||
| "expiresAt"
|
|
||||||
| "createdAt"
|
|
||||||
| "updatedAt"
|
|
||||||
| "id";
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
model: "jwks";
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field: "publicKey" | "privateKey" | "createdAt" | "id";
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
};
|
|
||||||
onDeleteHandle?: string;
|
|
||||||
paginationOpts: {
|
|
||||||
cursor: string | null;
|
|
||||||
endCursor?: string | null;
|
|
||||||
id?: number;
|
|
||||||
maximumBytesRead?: number;
|
|
||||||
maximumRowsRead?: number;
|
|
||||||
numItems: number;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
any
|
|
||||||
>;
|
|
||||||
deleteOne: FunctionReference<
|
|
||||||
"mutation",
|
|
||||||
"public",
|
|
||||||
{
|
|
||||||
input:
|
|
||||||
| {
|
|
||||||
model: "user";
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field:
|
|
||||||
| "name"
|
|
||||||
| "email"
|
|
||||||
| "emailVerified"
|
|
||||||
| "image"
|
|
||||||
| "createdAt"
|
|
||||||
| "updatedAt"
|
|
||||||
| "userId"
|
|
||||||
| "id";
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
model: "session";
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field:
|
|
||||||
| "expiresAt"
|
|
||||||
| "token"
|
|
||||||
| "createdAt"
|
|
||||||
| "updatedAt"
|
|
||||||
| "ipAddress"
|
|
||||||
| "userAgent"
|
|
||||||
| "userId"
|
|
||||||
| "id";
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
model: "account";
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field:
|
|
||||||
| "accountId"
|
|
||||||
| "providerId"
|
|
||||||
| "userId"
|
|
||||||
| "accessToken"
|
|
||||||
| "refreshToken"
|
|
||||||
| "idToken"
|
|
||||||
| "accessTokenExpiresAt"
|
|
||||||
| "refreshTokenExpiresAt"
|
|
||||||
| "scope"
|
|
||||||
| "password"
|
|
||||||
| "createdAt"
|
|
||||||
| "updatedAt"
|
|
||||||
| "id";
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
model: "verification";
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field:
|
|
||||||
| "identifier"
|
|
||||||
| "value"
|
|
||||||
| "expiresAt"
|
|
||||||
| "createdAt"
|
|
||||||
| "updatedAt"
|
|
||||||
| "id";
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
model: "jwks";
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field: "publicKey" | "privateKey" | "createdAt" | "id";
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
};
|
|
||||||
onDeleteHandle?: string;
|
|
||||||
},
|
|
||||||
any
|
|
||||||
>;
|
|
||||||
findMany: FunctionReference<
|
|
||||||
"query",
|
|
||||||
"public",
|
|
||||||
{
|
|
||||||
limit?: number;
|
|
||||||
model: "user" | "session" | "account" | "verification" | "jwks";
|
|
||||||
offset?: number;
|
|
||||||
paginationOpts: {
|
|
||||||
cursor: string | null;
|
|
||||||
endCursor?: string | null;
|
|
||||||
id?: number;
|
|
||||||
maximumBytesRead?: number;
|
|
||||||
maximumRowsRead?: number;
|
|
||||||
numItems: number;
|
|
||||||
};
|
|
||||||
sortBy?: { direction: "asc" | "desc"; field: string };
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field: string;
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
},
|
|
||||||
any
|
|
||||||
>;
|
|
||||||
findOne: FunctionReference<
|
|
||||||
"query",
|
|
||||||
"public",
|
|
||||||
{
|
|
||||||
model: "user" | "session" | "account" | "verification" | "jwks";
|
|
||||||
select?: Array<string>;
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field: string;
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
},
|
|
||||||
any
|
|
||||||
>;
|
|
||||||
updateMany: FunctionReference<
|
|
||||||
"mutation",
|
|
||||||
"public",
|
|
||||||
{
|
|
||||||
input:
|
|
||||||
| {
|
|
||||||
model: "user";
|
|
||||||
update: {
|
|
||||||
createdAt?: number;
|
|
||||||
email?: string;
|
|
||||||
emailVerified?: boolean;
|
|
||||||
image?: null | string;
|
|
||||||
name?: string;
|
|
||||||
updatedAt?: number;
|
|
||||||
userId?: null | string;
|
|
||||||
};
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field:
|
|
||||||
| "name"
|
|
||||||
| "email"
|
|
||||||
| "emailVerified"
|
|
||||||
| "image"
|
|
||||||
| "createdAt"
|
|
||||||
| "updatedAt"
|
|
||||||
| "userId"
|
|
||||||
| "id";
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
model: "session";
|
|
||||||
update: {
|
|
||||||
createdAt?: number;
|
|
||||||
expiresAt?: number;
|
|
||||||
ipAddress?: null | string;
|
|
||||||
token?: string;
|
|
||||||
updatedAt?: number;
|
|
||||||
userAgent?: null | string;
|
|
||||||
userId?: string;
|
|
||||||
};
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field:
|
|
||||||
| "expiresAt"
|
|
||||||
| "token"
|
|
||||||
| "createdAt"
|
|
||||||
| "updatedAt"
|
|
||||||
| "ipAddress"
|
|
||||||
| "userAgent"
|
|
||||||
| "userId"
|
|
||||||
| "id";
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
model: "account";
|
|
||||||
update: {
|
|
||||||
accessToken?: null | string;
|
|
||||||
accessTokenExpiresAt?: null | number;
|
|
||||||
accountId?: string;
|
|
||||||
createdAt?: number;
|
|
||||||
idToken?: null | string;
|
|
||||||
password?: null | string;
|
|
||||||
providerId?: string;
|
|
||||||
refreshToken?: null | string;
|
|
||||||
refreshTokenExpiresAt?: null | number;
|
|
||||||
scope?: null | string;
|
|
||||||
updatedAt?: number;
|
|
||||||
userId?: string;
|
|
||||||
};
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field:
|
|
||||||
| "accountId"
|
|
||||||
| "providerId"
|
|
||||||
| "userId"
|
|
||||||
| "accessToken"
|
|
||||||
| "refreshToken"
|
|
||||||
| "idToken"
|
|
||||||
| "accessTokenExpiresAt"
|
|
||||||
| "refreshTokenExpiresAt"
|
|
||||||
| "scope"
|
|
||||||
| "password"
|
|
||||||
| "createdAt"
|
|
||||||
| "updatedAt"
|
|
||||||
| "id";
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
model: "verification";
|
|
||||||
update: {
|
|
||||||
createdAt?: number;
|
|
||||||
expiresAt?: number;
|
|
||||||
identifier?: string;
|
|
||||||
updatedAt?: number;
|
|
||||||
value?: string;
|
|
||||||
};
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field:
|
|
||||||
| "identifier"
|
|
||||||
| "value"
|
|
||||||
| "expiresAt"
|
|
||||||
| "createdAt"
|
|
||||||
| "updatedAt"
|
|
||||||
| "id";
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
model: "jwks";
|
|
||||||
update: {
|
|
||||||
createdAt?: number;
|
|
||||||
privateKey?: string;
|
|
||||||
publicKey?: string;
|
|
||||||
};
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field: "publicKey" | "privateKey" | "createdAt" | "id";
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
};
|
|
||||||
onUpdateHandle?: string;
|
|
||||||
paginationOpts: {
|
|
||||||
cursor: string | null;
|
|
||||||
endCursor?: string | null;
|
|
||||||
id?: number;
|
|
||||||
maximumBytesRead?: number;
|
|
||||||
maximumRowsRead?: number;
|
|
||||||
numItems: number;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
any
|
|
||||||
>;
|
|
||||||
updateOne: FunctionReference<
|
|
||||||
"mutation",
|
|
||||||
"public",
|
|
||||||
{
|
|
||||||
input:
|
|
||||||
| {
|
|
||||||
model: "user";
|
|
||||||
update: {
|
|
||||||
createdAt?: number;
|
|
||||||
email?: string;
|
|
||||||
emailVerified?: boolean;
|
|
||||||
image?: null | string;
|
|
||||||
name?: string;
|
|
||||||
updatedAt?: number;
|
|
||||||
userId?: null | string;
|
|
||||||
};
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field:
|
|
||||||
| "name"
|
|
||||||
| "email"
|
|
||||||
| "emailVerified"
|
|
||||||
| "image"
|
|
||||||
| "createdAt"
|
|
||||||
| "updatedAt"
|
|
||||||
| "userId"
|
|
||||||
| "id";
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
model: "session";
|
|
||||||
update: {
|
|
||||||
createdAt?: number;
|
|
||||||
expiresAt?: number;
|
|
||||||
ipAddress?: null | string;
|
|
||||||
token?: string;
|
|
||||||
updatedAt?: number;
|
|
||||||
userAgent?: null | string;
|
|
||||||
userId?: string;
|
|
||||||
};
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field:
|
|
||||||
| "expiresAt"
|
|
||||||
| "token"
|
|
||||||
| "createdAt"
|
|
||||||
| "updatedAt"
|
|
||||||
| "ipAddress"
|
|
||||||
| "userAgent"
|
|
||||||
| "userId"
|
|
||||||
| "id";
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
model: "account";
|
|
||||||
update: {
|
|
||||||
accessToken?: null | string;
|
|
||||||
accessTokenExpiresAt?: null | number;
|
|
||||||
accountId?: string;
|
|
||||||
createdAt?: number;
|
|
||||||
idToken?: null | string;
|
|
||||||
password?: null | string;
|
|
||||||
providerId?: string;
|
|
||||||
refreshToken?: null | string;
|
|
||||||
refreshTokenExpiresAt?: null | number;
|
|
||||||
scope?: null | string;
|
|
||||||
updatedAt?: number;
|
|
||||||
userId?: string;
|
|
||||||
};
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field:
|
|
||||||
| "accountId"
|
|
||||||
| "providerId"
|
|
||||||
| "userId"
|
|
||||||
| "accessToken"
|
|
||||||
| "refreshToken"
|
|
||||||
| "idToken"
|
|
||||||
| "accessTokenExpiresAt"
|
|
||||||
| "refreshTokenExpiresAt"
|
|
||||||
| "scope"
|
|
||||||
| "password"
|
|
||||||
| "createdAt"
|
|
||||||
| "updatedAt"
|
|
||||||
| "id";
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
model: "verification";
|
|
||||||
update: {
|
|
||||||
createdAt?: number;
|
|
||||||
expiresAt?: number;
|
|
||||||
identifier?: string;
|
|
||||||
updatedAt?: number;
|
|
||||||
value?: string;
|
|
||||||
};
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field:
|
|
||||||
| "identifier"
|
|
||||||
| "value"
|
|
||||||
| "expiresAt"
|
|
||||||
| "createdAt"
|
|
||||||
| "updatedAt"
|
|
||||||
| "id";
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
model: "jwks";
|
|
||||||
update: {
|
|
||||||
createdAt?: number;
|
|
||||||
privateKey?: string;
|
|
||||||
publicKey?: string;
|
|
||||||
};
|
|
||||||
where?: Array<{
|
|
||||||
connector?: "AND" | "OR";
|
|
||||||
field: "publicKey" | "privateKey" | "createdAt" | "id";
|
|
||||||
operator?:
|
|
||||||
| "lt"
|
|
||||||
| "lte"
|
|
||||||
| "gt"
|
|
||||||
| "gte"
|
|
||||||
| "eq"
|
|
||||||
| "in"
|
|
||||||
| "ne"
|
|
||||||
| "contains"
|
|
||||||
| "starts_with"
|
|
||||||
| "ends_with";
|
|
||||||
value:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| Array<string>
|
|
||||||
| Array<number>
|
|
||||||
| null;
|
|
||||||
}>;
|
|
||||||
};
|
|
||||||
onUpdateHandle?: string;
|
|
||||||
},
|
|
||||||
any
|
|
||||||
>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
// For now fullApiWithMounts is only fullApi which provides
|
|
||||||
// jump-to-definition in component client code.
|
|
||||||
// Use Mounts for the same type without the inference.
|
|
||||||
declare const fullApiWithMounts: typeof fullApi;
|
|
||||||
|
|
||||||
export declare const api: FilterApi<
|
|
||||||
typeof fullApiWithMounts,
|
|
||||||
FunctionReference<any, "public">
|
|
||||||
>;
|
|
||||||
export declare const internal: FilterApi<
|
|
||||||
typeof fullApiWithMounts,
|
|
||||||
FunctionReference<any, "internal">
|
|
||||||
>;
|
|
||||||
|
|
||||||
export declare const components: {};
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* Generated `api` utility.
|
|
||||||
*
|
|
||||||
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
||||||
*
|
|
||||||
* To regenerate, run `npx convex dev`.
|
|
||||||
* @module
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { anyApi, componentsGeneric } from "convex/server";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A utility for referencing Convex functions in your app's API.
|
|
||||||
*
|
|
||||||
* Usage:
|
|
||||||
* ```js
|
|
||||||
* const myFunctionReference = api.myModule.myFunction;
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
export const api = anyApi;
|
|
||||||
export const internal = anyApi;
|
|
||||||
export const components = componentsGeneric();
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* Generated data model types.
|
|
||||||
*
|
|
||||||
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
||||||
*
|
|
||||||
* To regenerate, run `npx convex dev`.
|
|
||||||
* @module
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type {
|
|
||||||
DataModelFromSchemaDefinition,
|
|
||||||
DocumentByName,
|
|
||||||
TableNamesInDataModel,
|
|
||||||
SystemTableNames,
|
|
||||||
} from "convex/server";
|
|
||||||
import type { GenericId } from "convex/values";
|
|
||||||
import schema from "../schema.js";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The names of all of your Convex tables.
|
|
||||||
*/
|
|
||||||
export type TableNames = TableNamesInDataModel<DataModel>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of a document stored in Convex.
|
|
||||||
*
|
|
||||||
* @typeParam TableName - A string literal type of the table name (like "users").
|
|
||||||
*/
|
|
||||||
export type Doc<TableName extends TableNames> = DocumentByName<
|
|
||||||
DataModel,
|
|
||||||
TableName
|
|
||||||
>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An identifier for a document in Convex.
|
|
||||||
*
|
|
||||||
* Convex documents are uniquely identified by their `Id`, which is accessible
|
|
||||||
* on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids).
|
|
||||||
*
|
|
||||||
* Documents can be loaded using `db.get(id)` in query and mutation functions.
|
|
||||||
*
|
|
||||||
* IDs are just strings at runtime, but this type can be used to distinguish them from other
|
|
||||||
* strings when type checking.
|
|
||||||
*
|
|
||||||
* @typeParam TableName - A string literal type of the table name (like "users").
|
|
||||||
*/
|
|
||||||
export type Id<TableName extends TableNames | SystemTableNames> =
|
|
||||||
GenericId<TableName>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A type describing your Convex data model.
|
|
||||||
*
|
|
||||||
* This type includes information about what tables you have, the type of
|
|
||||||
* documents stored in those tables, and the indexes defined on them.
|
|
||||||
*
|
|
||||||
* This type is used to parameterize methods like `queryGeneric` and
|
|
||||||
* `mutationGeneric` to make them type-safe.
|
|
||||||
*/
|
|
||||||
export type DataModel = DataModelFromSchemaDefinition<typeof schema>;
|
|
||||||
149
packages/convex/betterauth/_generated/server.d.ts
vendored
149
packages/convex/betterauth/_generated/server.d.ts
vendored
@@ -1,149 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* Generated utilities for implementing server-side Convex query and mutation functions.
|
|
||||||
*
|
|
||||||
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
||||||
*
|
|
||||||
* To regenerate, run `npx convex dev`.
|
|
||||||
* @module
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {
|
|
||||||
ActionBuilder,
|
|
||||||
AnyComponents,
|
|
||||||
HttpActionBuilder,
|
|
||||||
MutationBuilder,
|
|
||||||
QueryBuilder,
|
|
||||||
GenericActionCtx,
|
|
||||||
GenericMutationCtx,
|
|
||||||
GenericQueryCtx,
|
|
||||||
GenericDatabaseReader,
|
|
||||||
GenericDatabaseWriter,
|
|
||||||
FunctionReference,
|
|
||||||
} from "convex/server";
|
|
||||||
import type { DataModel } from "./dataModel.js";
|
|
||||||
|
|
||||||
type GenericCtx =
|
|
||||||
| GenericActionCtx<DataModel>
|
|
||||||
| GenericMutationCtx<DataModel>
|
|
||||||
| GenericQueryCtx<DataModel>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a query in this Convex app's public API.
|
|
||||||
*
|
|
||||||
* This function will be allowed to read your Convex database and will be accessible from the client.
|
|
||||||
*
|
|
||||||
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
|
|
||||||
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export declare const query: QueryBuilder<DataModel, "public">;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a query that is only accessible from other Convex functions (but not from the client).
|
|
||||||
*
|
|
||||||
* This function will be allowed to read from your Convex database. It will not be accessible from the client.
|
|
||||||
*
|
|
||||||
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
|
|
||||||
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export declare const internalQuery: QueryBuilder<DataModel, "internal">;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a mutation in this Convex app's public API.
|
|
||||||
*
|
|
||||||
* This function will be allowed to modify your Convex database and will be accessible from the client.
|
|
||||||
*
|
|
||||||
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
|
|
||||||
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export declare const mutation: MutationBuilder<DataModel, "public">;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a mutation that is only accessible from other Convex functions (but not from the client).
|
|
||||||
*
|
|
||||||
* This function will be allowed to modify your Convex database. It will not be accessible from the client.
|
|
||||||
*
|
|
||||||
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
|
|
||||||
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export declare const internalMutation: MutationBuilder<DataModel, "internal">;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define an action in this Convex app's public API.
|
|
||||||
*
|
|
||||||
* An action is a function which can execute any JavaScript code, including non-deterministic
|
|
||||||
* code and code with side-effects, like calling third-party services.
|
|
||||||
* They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive.
|
|
||||||
* They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.
|
|
||||||
*
|
|
||||||
* @param func - The action. It receives an {@link ActionCtx} as its first argument.
|
|
||||||
* @returns The wrapped action. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export declare const action: ActionBuilder<DataModel, "public">;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define an action that is only accessible from other Convex functions (but not from the client).
|
|
||||||
*
|
|
||||||
* @param func - The function. It receives an {@link ActionCtx} as its first argument.
|
|
||||||
* @returns The wrapped function. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export declare const internalAction: ActionBuilder<DataModel, "internal">;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define an HTTP action.
|
|
||||||
*
|
|
||||||
* This function will be used to respond to HTTP requests received by a Convex
|
|
||||||
* deployment if the requests matches the path and method where this action
|
|
||||||
* is routed. Be sure to route your action in `convex/http.js`.
|
|
||||||
*
|
|
||||||
* @param func - The function. It receives an {@link ActionCtx} as its first argument.
|
|
||||||
* @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up.
|
|
||||||
*/
|
|
||||||
export declare const httpAction: HttpActionBuilder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A set of services for use within Convex query functions.
|
|
||||||
*
|
|
||||||
* The query context is passed as the first argument to any Convex query
|
|
||||||
* function run on the server.
|
|
||||||
*
|
|
||||||
* This differs from the {@link MutationCtx} because all of the services are
|
|
||||||
* read-only.
|
|
||||||
*/
|
|
||||||
export type QueryCtx = GenericQueryCtx<DataModel>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A set of services for use within Convex mutation functions.
|
|
||||||
*
|
|
||||||
* The mutation context is passed as the first argument to any Convex mutation
|
|
||||||
* function run on the server.
|
|
||||||
*/
|
|
||||||
export type MutationCtx = GenericMutationCtx<DataModel>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A set of services for use within Convex action functions.
|
|
||||||
*
|
|
||||||
* The action context is passed as the first argument to any Convex action
|
|
||||||
* function run on the server.
|
|
||||||
*/
|
|
||||||
export type ActionCtx = GenericActionCtx<DataModel>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface to read from the database within Convex query functions.
|
|
||||||
*
|
|
||||||
* The two entry points are {@link DatabaseReader.get}, which fetches a single
|
|
||||||
* document by its {@link Id}, or {@link DatabaseReader.query}, which starts
|
|
||||||
* building a query.
|
|
||||||
*/
|
|
||||||
export type DatabaseReader = GenericDatabaseReader<DataModel>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface to read from and write to the database within Convex mutation
|
|
||||||
* functions.
|
|
||||||
*
|
|
||||||
* Convex guarantees that all writes within a single mutation are
|
|
||||||
* executed atomically, so you never have to worry about partial writes leaving
|
|
||||||
* your data in an inconsistent state. See [the Convex Guide](https://docs.convex.dev/understanding/convex-fundamentals/functions#atomicity-and-optimistic-concurrency-control)
|
|
||||||
* for the guarantees Convex provides your functions.
|
|
||||||
*/
|
|
||||||
export type DatabaseWriter = GenericDatabaseWriter<DataModel>;
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* Generated utilities for implementing server-side Convex query and mutation functions.
|
|
||||||
*
|
|
||||||
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
||||||
*
|
|
||||||
* To regenerate, run `npx convex dev`.
|
|
||||||
* @module
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {
|
|
||||||
actionGeneric,
|
|
||||||
httpActionGeneric,
|
|
||||||
queryGeneric,
|
|
||||||
mutationGeneric,
|
|
||||||
internalActionGeneric,
|
|
||||||
internalMutationGeneric,
|
|
||||||
internalQueryGeneric,
|
|
||||||
componentsGeneric,
|
|
||||||
} from "convex/server";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a query in this Convex app's public API.
|
|
||||||
*
|
|
||||||
* This function will be allowed to read your Convex database and will be accessible from the client.
|
|
||||||
*
|
|
||||||
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
|
|
||||||
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export const query = queryGeneric;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a query that is only accessible from other Convex functions (but not from the client).
|
|
||||||
*
|
|
||||||
* This function will be allowed to read from your Convex database. It will not be accessible from the client.
|
|
||||||
*
|
|
||||||
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
|
|
||||||
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export const internalQuery = internalQueryGeneric;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a mutation in this Convex app's public API.
|
|
||||||
*
|
|
||||||
* This function will be allowed to modify your Convex database and will be accessible from the client.
|
|
||||||
*
|
|
||||||
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
|
|
||||||
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export const mutation = mutationGeneric;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a mutation that is only accessible from other Convex functions (but not from the client).
|
|
||||||
*
|
|
||||||
* This function will be allowed to modify your Convex database. It will not be accessible from the client.
|
|
||||||
*
|
|
||||||
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
|
|
||||||
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export const internalMutation = internalMutationGeneric;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define an action in this Convex app's public API.
|
|
||||||
*
|
|
||||||
* An action is a function which can execute any JavaScript code, including non-deterministic
|
|
||||||
* code and code with side-effects, like calling third-party services.
|
|
||||||
* They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive.
|
|
||||||
* They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.
|
|
||||||
*
|
|
||||||
* @param func - The action. It receives an {@link ActionCtx} as its first argument.
|
|
||||||
* @returns The wrapped action. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export const action = actionGeneric;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define an action that is only accessible from other Convex functions (but not from the client).
|
|
||||||
*
|
|
||||||
* @param func - The function. It receives an {@link ActionCtx} as its first argument.
|
|
||||||
* @returns The wrapped function. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export const internalAction = internalActionGeneric;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a Convex HTTP action.
|
|
||||||
*
|
|
||||||
* @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object
|
|
||||||
* as its second.
|
|
||||||
* @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`.
|
|
||||||
*/
|
|
||||||
export const httpAction = httpActionGeneric;
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { createApi } from "@convex-dev/better-auth";
|
|
||||||
import schema from "./schema";
|
|
||||||
import { createAuth } from "../auth";
|
|
||||||
|
|
||||||
export const {
|
|
||||||
create,
|
|
||||||
findOne,
|
|
||||||
findMany,
|
|
||||||
updateOne,
|
|
||||||
updateMany,
|
|
||||||
deleteOne,
|
|
||||||
deleteMany,
|
|
||||||
} = createApi(schema, createAuth);
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import { createAuth } from '../auth'
|
|
||||||
import { getStaticAuth } from '@convex-dev/better-auth'
|
|
||||||
|
|
||||||
// Export a static instance for Better Auth schema generation
|
|
||||||
export const auth = getStaticAuth(createAuth)
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import { defineComponent } from "convex/server";
|
|
||||||
|
|
||||||
const component = defineComponent("betterAuth");
|
|
||||||
|
|
||||||
export default component;
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
// This file is auto-generated. Do not edit this file manually.
|
|
||||||
// To regenerate the schema, run:
|
|
||||||
// `npx @better-auth/cli generate --output undefined -y`
|
|
||||||
|
|
||||||
import { defineSchema, defineTable } from "convex/server";
|
|
||||||
import { v } from "convex/values";
|
|
||||||
|
|
||||||
export const tables = {
|
|
||||||
user: defineTable({
|
|
||||||
name: v.string(),
|
|
||||||
email: v.string(),
|
|
||||||
emailVerified: v.boolean(),
|
|
||||||
image: v.optional(v.union(v.null(), v.string())),
|
|
||||||
createdAt: v.number(),
|
|
||||||
updatedAt: v.number(),
|
|
||||||
userId: v.optional(v.union(v.null(), v.string())),
|
|
||||||
})
|
|
||||||
.index("email_name", ["email","name"])
|
|
||||||
.index("name", ["name"])
|
|
||||||
.index("userId", ["userId"]),
|
|
||||||
session: defineTable({
|
|
||||||
expiresAt: v.number(),
|
|
||||||
token: v.string(),
|
|
||||||
createdAt: v.number(),
|
|
||||||
updatedAt: v.number(),
|
|
||||||
ipAddress: v.optional(v.union(v.null(), v.string())),
|
|
||||||
userAgent: v.optional(v.union(v.null(), v.string())),
|
|
||||||
userId: v.string(),
|
|
||||||
})
|
|
||||||
.index("expiresAt", ["expiresAt"])
|
|
||||||
.index("expiresAt_userId", ["expiresAt","userId"])
|
|
||||||
.index("token", ["token"])
|
|
||||||
.index("userId", ["userId"]),
|
|
||||||
account: defineTable({
|
|
||||||
accountId: v.string(),
|
|
||||||
providerId: v.string(),
|
|
||||||
userId: v.string(),
|
|
||||||
accessToken: v.optional(v.union(v.null(), v.string())),
|
|
||||||
refreshToken: v.optional(v.union(v.null(), v.string())),
|
|
||||||
idToken: v.optional(v.union(v.null(), v.string())),
|
|
||||||
accessTokenExpiresAt: v.optional(v.union(v.null(), v.number())),
|
|
||||||
refreshTokenExpiresAt: v.optional(v.union(v.null(), v.number())),
|
|
||||||
scope: v.optional(v.union(v.null(), v.string())),
|
|
||||||
password: v.optional(v.union(v.null(), v.string())),
|
|
||||||
createdAt: v.number(),
|
|
||||||
updatedAt: v.number(),
|
|
||||||
})
|
|
||||||
.index("accountId", ["accountId"])
|
|
||||||
.index("accountId_providerId", ["accountId","providerId"])
|
|
||||||
.index("providerId_userId", ["providerId","userId"])
|
|
||||||
.index("userId", ["userId"]),
|
|
||||||
verification: defineTable({
|
|
||||||
identifier: v.string(),
|
|
||||||
value: v.string(),
|
|
||||||
expiresAt: v.number(),
|
|
||||||
createdAt: v.number(),
|
|
||||||
updatedAt: v.number(),
|
|
||||||
})
|
|
||||||
.index("expiresAt", ["expiresAt"])
|
|
||||||
.index("identifier", ["identifier"]),
|
|
||||||
jwks: defineTable({
|
|
||||||
publicKey: v.string(),
|
|
||||||
privateKey: v.string(),
|
|
||||||
createdAt: v.number(),
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
const schema = defineSchema(tables);
|
|
||||||
|
|
||||||
export default schema;
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import { defineApp } from "convex/server"
|
|
||||||
import betterAuth from "./betterauth/convex.config"
|
|
||||||
|
|
||||||
const app = defineApp()
|
|
||||||
app.use(betterAuth)
|
|
||||||
|
|
||||||
export default app
|
|
||||||
33
packages/convex/convex/_generated/api.d.ts
vendored
33
packages/convex/convex/_generated/api.d.ts
vendored
@@ -1,33 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* Generated `api` utility.
|
|
||||||
*
|
|
||||||
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
||||||
*
|
|
||||||
* To regenerate, run `npx convex dev`.
|
|
||||||
* @module
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type {
|
|
||||||
ApiFromModules,
|
|
||||||
FilterApi,
|
|
||||||
FunctionReference,
|
|
||||||
} from "convex/server";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A utility for referencing Convex functions in your app's API.
|
|
||||||
*
|
|
||||||
* Usage:
|
|
||||||
* ```js
|
|
||||||
* const myFunctionReference = api.myModule.myFunction;
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
declare const fullApi: ApiFromModules<{}>;
|
|
||||||
export declare const api: FilterApi<
|
|
||||||
typeof fullApi,
|
|
||||||
FunctionReference<any, "public">
|
|
||||||
>;
|
|
||||||
export declare const internal: FilterApi<
|
|
||||||
typeof fullApi,
|
|
||||||
FunctionReference<any, "internal">
|
|
||||||
>;
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* Generated `api` utility.
|
|
||||||
*
|
|
||||||
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
||||||
*
|
|
||||||
* To regenerate, run `npx convex dev`.
|
|
||||||
* @module
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { anyApi } from "convex/server";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A utility for referencing Convex functions in your app's API.
|
|
||||||
*
|
|
||||||
* Usage:
|
|
||||||
* ```js
|
|
||||||
* const myFunctionReference = api.myModule.myFunction;
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
export const api = anyApi;
|
|
||||||
export const internal = anyApi;
|
|
||||||
58
packages/convex/convex/_generated/dataModel.d.ts
vendored
58
packages/convex/convex/_generated/dataModel.d.ts
vendored
@@ -1,58 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* Generated data model types.
|
|
||||||
*
|
|
||||||
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
||||||
*
|
|
||||||
* To regenerate, run `npx convex dev`.
|
|
||||||
* @module
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { AnyDataModel } from "convex/server";
|
|
||||||
import type { GenericId } from "convex/values";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* No `schema.ts` file found!
|
|
||||||
*
|
|
||||||
* This generated code has permissive types like `Doc = any` because
|
|
||||||
* Convex doesn't know your schema. If you'd like more type safety, see
|
|
||||||
* https://docs.convex.dev/using/schemas for instructions on how to add a
|
|
||||||
* schema file.
|
|
||||||
*
|
|
||||||
* After you change a schema, rerun codegen with `npx convex dev`.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The names of all of your Convex tables.
|
|
||||||
*/
|
|
||||||
export type TableNames = string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of a document stored in Convex.
|
|
||||||
*/
|
|
||||||
export type Doc = any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An identifier for a document in Convex.
|
|
||||||
*
|
|
||||||
* Convex documents are uniquely identified by their `Id`, which is accessible
|
|
||||||
* on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids).
|
|
||||||
*
|
|
||||||
* Documents can be loaded using `db.get(id)` in query and mutation functions.
|
|
||||||
*
|
|
||||||
* IDs are just strings at runtime, but this type can be used to distinguish them from other
|
|
||||||
* strings when type checking.
|
|
||||||
*/
|
|
||||||
export type Id<TableName extends TableNames = TableNames> =
|
|
||||||
GenericId<TableName>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A type describing your Convex data model.
|
|
||||||
*
|
|
||||||
* This type includes information about what tables you have, the type of
|
|
||||||
* documents stored in those tables, and the indexes defined on them.
|
|
||||||
*
|
|
||||||
* This type is used to parameterize methods like `queryGeneric` and
|
|
||||||
* `mutationGeneric` to make them type-safe.
|
|
||||||
*/
|
|
||||||
export type DataModel = AnyDataModel;
|
|
||||||
142
packages/convex/convex/_generated/server.d.ts
vendored
142
packages/convex/convex/_generated/server.d.ts
vendored
@@ -1,142 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* Generated utilities for implementing server-side Convex query and mutation functions.
|
|
||||||
*
|
|
||||||
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
||||||
*
|
|
||||||
* To regenerate, run `npx convex dev`.
|
|
||||||
* @module
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {
|
|
||||||
ActionBuilder,
|
|
||||||
HttpActionBuilder,
|
|
||||||
MutationBuilder,
|
|
||||||
QueryBuilder,
|
|
||||||
GenericActionCtx,
|
|
||||||
GenericMutationCtx,
|
|
||||||
GenericQueryCtx,
|
|
||||||
GenericDatabaseReader,
|
|
||||||
GenericDatabaseWriter,
|
|
||||||
} from "convex/server";
|
|
||||||
import type { DataModel } from "./dataModel.js";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a query in this Convex app's public API.
|
|
||||||
*
|
|
||||||
* This function will be allowed to read your Convex database and will be accessible from the client.
|
|
||||||
*
|
|
||||||
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
|
|
||||||
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export declare const query: QueryBuilder<DataModel, "public">;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a query that is only accessible from other Convex functions (but not from the client).
|
|
||||||
*
|
|
||||||
* This function will be allowed to read from your Convex database. It will not be accessible from the client.
|
|
||||||
*
|
|
||||||
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
|
|
||||||
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export declare const internalQuery: QueryBuilder<DataModel, "internal">;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a mutation in this Convex app's public API.
|
|
||||||
*
|
|
||||||
* This function will be allowed to modify your Convex database and will be accessible from the client.
|
|
||||||
*
|
|
||||||
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
|
|
||||||
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export declare const mutation: MutationBuilder<DataModel, "public">;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a mutation that is only accessible from other Convex functions (but not from the client).
|
|
||||||
*
|
|
||||||
* This function will be allowed to modify your Convex database. It will not be accessible from the client.
|
|
||||||
*
|
|
||||||
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
|
|
||||||
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export declare const internalMutation: MutationBuilder<DataModel, "internal">;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define an action in this Convex app's public API.
|
|
||||||
*
|
|
||||||
* An action is a function which can execute any JavaScript code, including non-deterministic
|
|
||||||
* code and code with side-effects, like calling third-party services.
|
|
||||||
* They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive.
|
|
||||||
* They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.
|
|
||||||
*
|
|
||||||
* @param func - The action. It receives an {@link ActionCtx} as its first argument.
|
|
||||||
* @returns The wrapped action. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export declare const action: ActionBuilder<DataModel, "public">;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define an action that is only accessible from other Convex functions (but not from the client).
|
|
||||||
*
|
|
||||||
* @param func - The function. It receives an {@link ActionCtx} as its first argument.
|
|
||||||
* @returns The wrapped function. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export declare const internalAction: ActionBuilder<DataModel, "internal">;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define an HTTP action.
|
|
||||||
*
|
|
||||||
* This function will be used to respond to HTTP requests received by a Convex
|
|
||||||
* deployment if the requests matches the path and method where this action
|
|
||||||
* is routed. Be sure to route your action in `convex/http.js`.
|
|
||||||
*
|
|
||||||
* @param func - The function. It receives an {@link ActionCtx} as its first argument.
|
|
||||||
* @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up.
|
|
||||||
*/
|
|
||||||
export declare const httpAction: HttpActionBuilder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A set of services for use within Convex query functions.
|
|
||||||
*
|
|
||||||
* The query context is passed as the first argument to any Convex query
|
|
||||||
* function run on the server.
|
|
||||||
*
|
|
||||||
* This differs from the {@link MutationCtx} because all of the services are
|
|
||||||
* read-only.
|
|
||||||
*/
|
|
||||||
export type QueryCtx = GenericQueryCtx<DataModel>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A set of services for use within Convex mutation functions.
|
|
||||||
*
|
|
||||||
* The mutation context is passed as the first argument to any Convex mutation
|
|
||||||
* function run on the server.
|
|
||||||
*/
|
|
||||||
export type MutationCtx = GenericMutationCtx<DataModel>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A set of services for use within Convex action functions.
|
|
||||||
*
|
|
||||||
* The action context is passed as the first argument to any Convex action
|
|
||||||
* function run on the server.
|
|
||||||
*/
|
|
||||||
export type ActionCtx = GenericActionCtx<DataModel>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface to read from the database within Convex query functions.
|
|
||||||
*
|
|
||||||
* The two entry points are {@link DatabaseReader.get}, which fetches a single
|
|
||||||
* document by its {@link Id}, or {@link DatabaseReader.query}, which starts
|
|
||||||
* building a query.
|
|
||||||
*/
|
|
||||||
export type DatabaseReader = GenericDatabaseReader<DataModel>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface to read from and write to the database within Convex mutation
|
|
||||||
* functions.
|
|
||||||
*
|
|
||||||
* Convex guarantees that all writes within a single mutation are
|
|
||||||
* executed atomically, so you never have to worry about partial writes leaving
|
|
||||||
* your data in an inconsistent state. See [the Convex Guide](https://docs.convex.dev/understanding/convex-fundamentals/functions#atomicity-and-optimistic-concurrency-control)
|
|
||||||
* for the guarantees Convex provides your functions.
|
|
||||||
*/
|
|
||||||
export type DatabaseWriter = GenericDatabaseWriter<DataModel>;
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* Generated utilities for implementing server-side Convex query and mutation functions.
|
|
||||||
*
|
|
||||||
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
||||||
*
|
|
||||||
* To regenerate, run `npx convex dev`.
|
|
||||||
* @module
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {
|
|
||||||
actionGeneric,
|
|
||||||
httpActionGeneric,
|
|
||||||
queryGeneric,
|
|
||||||
mutationGeneric,
|
|
||||||
internalActionGeneric,
|
|
||||||
internalMutationGeneric,
|
|
||||||
internalQueryGeneric,
|
|
||||||
} from "convex/server";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a query in this Convex app's public API.
|
|
||||||
*
|
|
||||||
* This function will be allowed to read your Convex database and will be accessible from the client.
|
|
||||||
*
|
|
||||||
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
|
|
||||||
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export const query = queryGeneric;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a query that is only accessible from other Convex functions (but not from the client).
|
|
||||||
*
|
|
||||||
* This function will be allowed to read from your Convex database. It will not be accessible from the client.
|
|
||||||
*
|
|
||||||
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
|
|
||||||
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export const internalQuery = internalQueryGeneric;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a mutation in this Convex app's public API.
|
|
||||||
*
|
|
||||||
* This function will be allowed to modify your Convex database and will be accessible from the client.
|
|
||||||
*
|
|
||||||
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
|
|
||||||
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export const mutation = mutationGeneric;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a mutation that is only accessible from other Convex functions (but not from the client).
|
|
||||||
*
|
|
||||||
* This function will be allowed to modify your Convex database. It will not be accessible from the client.
|
|
||||||
*
|
|
||||||
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
|
|
||||||
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export const internalMutation = internalMutationGeneric;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define an action in this Convex app's public API.
|
|
||||||
*
|
|
||||||
* An action is a function which can execute any JavaScript code, including non-deterministic
|
|
||||||
* code and code with side-effects, like calling third-party services.
|
|
||||||
* They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive.
|
|
||||||
* They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.
|
|
||||||
*
|
|
||||||
* @param func - The action. It receives an {@link ActionCtx} as its first argument.
|
|
||||||
* @returns The wrapped action. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export const action = actionGeneric;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define an action that is only accessible from other Convex functions (but not from the client).
|
|
||||||
*
|
|
||||||
* @param func - The function. It receives an {@link ActionCtx} as its first argument.
|
|
||||||
* @returns The wrapped function. Include this as an `export` to name it and make it accessible.
|
|
||||||
*/
|
|
||||||
export const internalAction = internalActionGeneric;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a Convex HTTP action.
|
|
||||||
*
|
|
||||||
* @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object
|
|
||||||
* as its second.
|
|
||||||
* @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`.
|
|
||||||
*/
|
|
||||||
export const httpAction = httpActionGeneric;
|
|
||||||
@@ -1,19 +1,26 @@
|
|||||||
import type { Id } from "@fileone/convex/dataModel"
|
|
||||||
import { v } from "convex/values"
|
import { v } from "convex/values"
|
||||||
import {
|
import type { Id } from "./_generated/dataModel"
|
||||||
authenticatedMutation,
|
import { authenticatedMutation, authenticatedQuery } from "./functions"
|
||||||
authenticatedQuery,
|
|
||||||
authorizedGet,
|
|
||||||
} from "./functions"
|
|
||||||
import * as Directories from "./model/directories"
|
import * as Directories from "./model/directories"
|
||||||
import * as Files from "./model/files"
|
import * as Files from "./model/files"
|
||||||
|
import type { FileSystemItem } from "./model/filesystem"
|
||||||
|
|
||||||
export const generateUploadUrl = authenticatedMutation({
|
export const generateUploadUrl = authenticatedMutation({
|
||||||
handler: async (ctx) => {
|
handler: async (ctx) => {
|
||||||
|
// ctx.user and ctx.identity are automatically available
|
||||||
return await ctx.storage.generateUploadUrl()
|
return await ctx.storage.generateUploadUrl()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const generateFileUrl = authenticatedQuery({
|
||||||
|
args: {
|
||||||
|
storageId: v.id("_storage"),
|
||||||
|
},
|
||||||
|
handler: async (ctx, { storageId }) => {
|
||||||
|
return await ctx.storage.getUrl(storageId)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
export const fetchFiles = authenticatedQuery({
|
export const fetchFiles = authenticatedQuery({
|
||||||
args: {
|
args: {
|
||||||
directoryId: v.optional(v.id("directories")),
|
directoryId: v.optional(v.id("directories")),
|
||||||
@@ -39,25 +46,25 @@ export const fetchDirectory = authenticatedQuery({
|
|||||||
directoryId: v.id("directories"),
|
directoryId: v.id("directories"),
|
||||||
},
|
},
|
||||||
handler: async (ctx, { directoryId }) => {
|
handler: async (ctx, { directoryId }) => {
|
||||||
const directory = await authorizedGet(ctx, directoryId)
|
|
||||||
if (!directory) {
|
|
||||||
throw new Error("Directory not found")
|
|
||||||
}
|
|
||||||
return await Directories.fetch(ctx, { directoryId })
|
return await Directories.fetch(ctx, { directoryId })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const fetchDirectoryContent = authenticatedQuery({
|
||||||
|
args: {
|
||||||
|
directoryId: v.optional(v.id("directories")),
|
||||||
|
},
|
||||||
|
handler: async (ctx, { directoryId }): Promise<FileSystemItem[]> => {
|
||||||
|
return await Directories.fetchContent(ctx, { directoryId })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
export const createDirectory = authenticatedMutation({
|
export const createDirectory = authenticatedMutation({
|
||||||
args: {
|
args: {
|
||||||
name: v.string(),
|
name: v.string(),
|
||||||
directoryId: v.id("directories"),
|
directoryId: v.id("directories"),
|
||||||
},
|
},
|
||||||
handler: async (ctx, { name, directoryId }): Promise<Id<"directories">> => {
|
handler: async (ctx, { name, directoryId }): Promise<Id<"directories">> => {
|
||||||
const parentDirectory = await authorizedGet(ctx, directoryId)
|
|
||||||
if (!parentDirectory) {
|
|
||||||
throw new Error("Parent directory not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Directories.create(ctx, {
|
return await Directories.create(ctx, {
|
||||||
name,
|
name,
|
||||||
parentId: directoryId,
|
parentId: directoryId,
|
||||||
@@ -74,12 +81,7 @@ export const saveFile = authenticatedMutation({
|
|||||||
mimeType: v.optional(v.string()),
|
mimeType: v.optional(v.string()),
|
||||||
},
|
},
|
||||||
handler: async (ctx, { name, storageId, directoryId, size, mimeType }) => {
|
handler: async (ctx, { name, storageId, directoryId, size, mimeType }) => {
|
||||||
const directory = await authorizedGet(ctx, directoryId)
|
const now = new Date().toISOString()
|
||||||
if (!directory) {
|
|
||||||
throw new Error("Directory not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
const now = Date.now()
|
|
||||||
|
|
||||||
await ctx.db.insert("files", {
|
await ctx.db.insert("files", {
|
||||||
name,
|
name,
|
||||||
@@ -101,11 +103,6 @@ export const renameFile = authenticatedMutation({
|
|||||||
newName: v.string(),
|
newName: v.string(),
|
||||||
},
|
},
|
||||||
handler: async (ctx, { directoryId, itemId, newName }) => {
|
handler: async (ctx, { directoryId, itemId, newName }) => {
|
||||||
const file = await authorizedGet(ctx, itemId)
|
|
||||||
if (!file) {
|
|
||||||
throw new Error("File not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
await Files.renameFile(ctx, { directoryId, itemId, newName })
|
await Files.renameFile(ctx, { directoryId, itemId, newName })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
import { v } from "convex/values"
|
|
||||||
import { apiKeyAuthenticatedQuery } from "./functions"
|
|
||||||
import * as FileShare from "./model/fileshare"
|
|
||||||
|
|
||||||
export const findFileShare = apiKeyAuthenticatedQuery({
|
|
||||||
args: {
|
|
||||||
shareToken: v.string(),
|
|
||||||
},
|
|
||||||
handler: async (ctx, { shareToken }) => {
|
|
||||||
return await FileShare.find(ctx, { shareToken })
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -1,29 +1,15 @@
|
|||||||
import { v } from "convex/values"
|
import { v } from "convex/values"
|
||||||
import {
|
import { authenticatedMutation } from "./functions"
|
||||||
apiKeyAuthenticatedQuery,
|
|
||||||
authenticatedMutation,
|
|
||||||
authenticatedQuery,
|
|
||||||
authorizedGet,
|
|
||||||
} from "./functions"
|
|
||||||
import * as Directories from "./model/directories"
|
import * as Directories from "./model/directories"
|
||||||
|
import * as Err from "./model/error"
|
||||||
import * as Files from "./model/files"
|
import * as Files from "./model/files"
|
||||||
import * as FileSystem from "./model/filesystem"
|
import type { DirectoryHandle, FileHandle } from "./model/filesystem"
|
||||||
import {
|
import {
|
||||||
deleteItemsPermanently,
|
type FileSystemHandle,
|
||||||
emptyTrash as emptyTrashImpl,
|
FileType,
|
||||||
fetchFileUrl as fetchFileUrlImpl,
|
|
||||||
restoreItems as restoreItemsImpl,
|
|
||||||
VDirectoryHandle,
|
VDirectoryHandle,
|
||||||
VFileSystemHandle,
|
VFileSystemHandle,
|
||||||
} from "./model/filesystem"
|
} from "./model/filesystem"
|
||||||
import * as Err from "./shared/error"
|
|
||||||
import type {
|
|
||||||
DirectoryHandle,
|
|
||||||
FileHandle,
|
|
||||||
FileSystemHandle,
|
|
||||||
FileSystemItem,
|
|
||||||
} from "./shared/filesystem"
|
|
||||||
import { FileType } from "./shared/filesystem"
|
|
||||||
|
|
||||||
export const moveItems = authenticatedMutation({
|
export const moveItems = authenticatedMutation({
|
||||||
args: {
|
args: {
|
||||||
@@ -31,9 +17,9 @@ export const moveItems = authenticatedMutation({
|
|||||||
items: v.array(VFileSystemHandle),
|
items: v.array(VFileSystemHandle),
|
||||||
},
|
},
|
||||||
handler: async (ctx, { targetDirectory: targetDirectoryHandle, items }) => {
|
handler: async (ctx, { targetDirectory: targetDirectoryHandle, items }) => {
|
||||||
const targetDirectory = await authorizedGet(
|
const targetDirectory = await Directories.fetchHandle(
|
||||||
ctx,
|
ctx,
|
||||||
targetDirectoryHandle.id,
|
targetDirectoryHandle,
|
||||||
)
|
)
|
||||||
if (!targetDirectory) {
|
if (!targetDirectory) {
|
||||||
throw Err.create(
|
throw Err.create(
|
||||||
@@ -78,23 +64,13 @@ export const moveToTrash = authenticatedMutation({
|
|||||||
handles: v.array(VFileSystemHandle),
|
handles: v.array(VFileSystemHandle),
|
||||||
},
|
},
|
||||||
handler: async (ctx, { handles }) => {
|
handler: async (ctx, { handles }) => {
|
||||||
for (const handle of handles) {
|
|
||||||
const item = await authorizedGet(ctx, handle.id)
|
|
||||||
if (!item) {
|
|
||||||
throw Err.create(
|
|
||||||
Err.Code.NotFound,
|
|
||||||
`Item ${handle.id} not found`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// biome-ignore lint/suspicious/useIterableCallbackReturn: switch statement is exhaustive
|
// biome-ignore lint/suspicious/useIterableCallbackReturn: switch statement is exhaustive
|
||||||
const promises = handles.map((handle) => {
|
const promises = handles.map((handle) => {
|
||||||
switch (handle.kind) {
|
switch (handle.kind) {
|
||||||
case FileType.File:
|
case FileType.File:
|
||||||
return ctx.db
|
return ctx.db
|
||||||
.patch(handle.id, {
|
.patch(handle.id, {
|
||||||
deletedAt: Date.now(),
|
deletedAt: new Date().toISOString(),
|
||||||
})
|
})
|
||||||
.then(() => handle)
|
.then(() => handle)
|
||||||
case FileType.Directory:
|
case FileType.Directory:
|
||||||
@@ -124,67 +100,3 @@ export const moveToTrash = authenticatedMutation({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export const fetchDirectoryContent = authenticatedQuery({
|
|
||||||
args: {
|
|
||||||
directoryId: v.optional(v.id("directories")),
|
|
||||||
trashed: v.boolean(),
|
|
||||||
},
|
|
||||||
handler: async (
|
|
||||||
ctx,
|
|
||||||
{ directoryId, trashed },
|
|
||||||
): Promise<FileSystemItem[]> => {
|
|
||||||
return await Directories.fetchContent(ctx, { directoryId, trashed })
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export const permanentlyDeleteItems = authenticatedMutation({
|
|
||||||
args: {
|
|
||||||
handles: v.array(VFileSystemHandle),
|
|
||||||
},
|
|
||||||
handler: async (ctx, { handles }) => {
|
|
||||||
return await deleteItemsPermanently(ctx, { handles })
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export const emptyTrash = authenticatedMutation({
|
|
||||||
handler: async (ctx) => {
|
|
||||||
return await emptyTrashImpl(ctx)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export const restoreItems = authenticatedMutation({
|
|
||||||
args: {
|
|
||||||
handles: v.array(VFileSystemHandle),
|
|
||||||
},
|
|
||||||
handler: async (ctx, { handles }) => {
|
|
||||||
return await restoreItemsImpl(ctx, { handles })
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export const getStorageUrl = apiKeyAuthenticatedQuery({
|
|
||||||
args: {
|
|
||||||
storageId: v.id("_storage"),
|
|
||||||
},
|
|
||||||
handler: async (ctx, { storageId }) => {
|
|
||||||
return await ctx.storage.getUrl(storageId)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export const fetchFileUrl = authenticatedQuery({
|
|
||||||
args: {
|
|
||||||
fileId: v.id("files"),
|
|
||||||
},
|
|
||||||
handler: async (ctx, { fileId }) => {
|
|
||||||
return await fetchFileUrlImpl(ctx, { fileId })
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export const openFile = authenticatedMutation({
|
|
||||||
args: {
|
|
||||||
fileId: v.id("files"),
|
|
||||||
},
|
|
||||||
handler: async (ctx, { fileId }) => {
|
|
||||||
return await FileSystem.openFile(ctx, { fileId })
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|||||||
@@ -1,35 +1,24 @@
|
|||||||
import type { DataModel } from "@fileone/convex/dataModel"
|
import type { UserIdentity } from "convex/server"
|
||||||
import type { MutationCtx, QueryCtx } from "@fileone/convex/server"
|
|
||||||
import { mutation, query } from "@fileone/convex/server"
|
|
||||||
import type {
|
|
||||||
DocumentByName,
|
|
||||||
TableNamesInDataModel,
|
|
||||||
UserIdentity,
|
|
||||||
} from "convex/server"
|
|
||||||
import { type GenericId, v } from "convex/values"
|
|
||||||
import {
|
import {
|
||||||
customCtx,
|
customCtx,
|
||||||
customMutation,
|
customMutation,
|
||||||
customQuery,
|
customQuery,
|
||||||
} from "convex-helpers/server/customFunctions"
|
} from "convex-helpers/server/customFunctions"
|
||||||
import * as ApiKey from "./model/apikey"
|
import type { Doc } from "./_generated/dataModel"
|
||||||
import { type AuthUser, userIdentityOrThrow, userOrThrow } from "./model/user"
|
import type { MutationCtx, QueryCtx } from "./_generated/server"
|
||||||
import * as Err from "./shared/error"
|
import { mutation, query } from "./_generated/server"
|
||||||
|
import { userIdentityOrThrow, userOrThrow } from "./model/user"
|
||||||
|
|
||||||
export type AuthenticatedQueryCtx = QueryCtx & {
|
export type AuthenticatedQueryCtx = QueryCtx & {
|
||||||
user: AuthUser
|
user: Doc<"users">
|
||||||
identity: UserIdentity
|
identity: UserIdentity
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AuthenticatedMutationCtx = MutationCtx & {
|
export type AuthenticatedMutationCtx = MutationCtx & {
|
||||||
user: AuthUser
|
user: Doc<"users">
|
||||||
identity: UserIdentity
|
identity: UserIdentity
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ApiKeyAuthenticatedQueryCtx = QueryCtx & {
|
|
||||||
__branded: "ApiKeyAuthenticatedQueryCtx"
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom query that automatically provides authenticated user context
|
* Custom query that automatically provides authenticated user context
|
||||||
* Throws an error if the user is not authenticated
|
* Throws an error if the user is not authenticated
|
||||||
@@ -55,49 +44,3 @@ export const authenticatedMutation = customMutation(
|
|||||||
return { user, identity }
|
return { user, identity }
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom query that requires api key authentication for a query.
|
|
||||||
*/
|
|
||||||
export const apiKeyAuthenticatedQuery = customQuery(query, {
|
|
||||||
args: {
|
|
||||||
apiKey: v.string(),
|
|
||||||
},
|
|
||||||
input: async (ctx, args) => {
|
|
||||||
if (!(await ApiKey.verifyApiKey(ctx, args.apiKey))) {
|
|
||||||
throw Err.create(Err.Code.Unauthenticated, "Invalid API key")
|
|
||||||
}
|
|
||||||
return { ctx: ctx as ApiKeyAuthenticatedQueryCtx, args }
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom mutation that requires api key authentication for a mutation.
|
|
||||||
*/
|
|
||||||
export const apiKeyAuthenticatedMutation = customMutation(mutation, {
|
|
||||||
args: {
|
|
||||||
apiKey: v.string(),
|
|
||||||
},
|
|
||||||
input: async (ctx, args) => {
|
|
||||||
if (!(await ApiKey.verifyApiKey(ctx, args.apiKey))) {
|
|
||||||
throw Err.create(Err.Code.Unauthenticated, "Invalid API key")
|
|
||||||
}
|
|
||||||
return { ctx, args }
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a document by its id and checks if the user is authorized to access it
|
|
||||||
*
|
|
||||||
* @returns The document associated with the id or null if the document is not found.
|
|
||||||
*/
|
|
||||||
export async function authorizedGet<T extends TableNamesInDataModel<DataModel>>(
|
|
||||||
ctx: AuthenticatedQueryCtx | AuthenticatedMutationCtx,
|
|
||||||
id: GenericId<T>,
|
|
||||||
): Promise<DocumentByName<DataModel, T> | null> {
|
|
||||||
const item = await ctx.db.get(id)
|
|
||||||
if (item && item.userId !== ctx.user._id) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return item
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
import { httpRouter } from "convex/server"
|
|
||||||
import { authComponent, createAuth } from "./auth"
|
|
||||||
|
|
||||||
const http = httpRouter()
|
|
||||||
// CORS handling is required for client side frameworks
|
|
||||||
authComponent.registerRoutes(http, createAuth, { cors: true })
|
|
||||||
export default http
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import {
|
|
||||||
verifyApiKey as _verifyApiKey,
|
|
||||||
parseApiKey,
|
|
||||||
WebCryptoSha256Hasher,
|
|
||||||
} from "@drexa/auth"
|
|
||||||
import type { MutationCtx, QueryCtx } from "../_generated/server"
|
|
||||||
|
|
||||||
export async function verifyApiKey(
|
|
||||||
ctx: MutationCtx | QueryCtx,
|
|
||||||
unhashedKey: string,
|
|
||||||
): Promise<boolean> {
|
|
||||||
const parsedKey = parseApiKey(unhashedKey)
|
|
||||||
if (!parsedKey) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiKey = await ctx.db
|
|
||||||
.query("apiKeys")
|
|
||||||
.withIndex("byPublicId", (q) => q.eq("publicId", parsedKey.prefix))
|
|
||||||
.first()
|
|
||||||
if (!apiKey) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return await _verifyApiKey({
|
|
||||||
keyToBeVerified: parsedKey.unhashedKey,
|
|
||||||
hashedKey: apiKey.hashedKey,
|
|
||||||
hasher: new WebCryptoSha256Hasher(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
import type { Doc, Id } from "@fileone/convex/dataModel"
|
import type { Doc, Id } from "@fileone/convex/_generated/dataModel"
|
||||||
import type {
|
import type {
|
||||||
AuthenticatedMutationCtx,
|
AuthenticatedMutationCtx,
|
||||||
AuthenticatedQueryCtx,
|
AuthenticatedQueryCtx,
|
||||||
} from "../functions"
|
} from "../functions"
|
||||||
import { authorizedGet } from "../functions"
|
import * as Err from "./error"
|
||||||
import * as Err from "../shared/error"
|
|
||||||
import {
|
import {
|
||||||
type DirectoryHandle,
|
type DirectoryHandle,
|
||||||
type DirectoryPath,
|
type FilePath,
|
||||||
type FileSystemItem,
|
type FileSystemItem,
|
||||||
FileType,
|
FileType,
|
||||||
newDirectoryHandle,
|
newDirectoryHandle,
|
||||||
} from "../shared/filesystem"
|
type ReverseFilePath,
|
||||||
|
} from "./filesystem"
|
||||||
|
|
||||||
export type DirectoryInfo = Doc<"directories"> & { path: DirectoryPath }
|
export type DirectoryInfo = Doc<"directories"> & { path: FilePath }
|
||||||
|
|
||||||
export async function fetchRoot(ctx: AuthenticatedQueryCtx) {
|
export async function fetchRoot(ctx: AuthenticatedQueryCtx) {
|
||||||
return await ctx.db
|
return await ctx.db
|
||||||
@@ -28,8 +28,8 @@ export async function fetchHandle(
|
|||||||
ctx: AuthenticatedQueryCtx,
|
ctx: AuthenticatedQueryCtx,
|
||||||
handle: DirectoryHandle,
|
handle: DirectoryHandle,
|
||||||
): Promise<Doc<"directories">> {
|
): Promise<Doc<"directories">> {
|
||||||
const directory = await authorizedGet(ctx, handle.id)
|
const directory = await ctx.db.get(handle.id)
|
||||||
if (!directory) {
|
if (!directory || directory.userId !== ctx.user._id) {
|
||||||
throw Err.create(
|
throw Err.create(
|
||||||
Err.Code.DirectoryNotFound,
|
Err.Code.DirectoryNotFound,
|
||||||
`Directory ${handle.id} not found`,
|
`Directory ${handle.id} not found`,
|
||||||
@@ -42,7 +42,7 @@ export async function fetch(
|
|||||||
ctx: AuthenticatedQueryCtx,
|
ctx: AuthenticatedQueryCtx,
|
||||||
{ directoryId }: { directoryId: Id<"directories"> },
|
{ directoryId }: { directoryId: Id<"directories"> },
|
||||||
): Promise<DirectoryInfo> {
|
): Promise<DirectoryInfo> {
|
||||||
const directory = await authorizedGet(ctx, directoryId)
|
const directory = await ctx.db.get(directoryId)
|
||||||
if (!directory) {
|
if (!directory) {
|
||||||
throw Err.create(
|
throw Err.create(
|
||||||
Err.Code.DirectoryNotFound,
|
Err.Code.DirectoryNotFound,
|
||||||
@@ -50,7 +50,7 @@ export async function fetch(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const path: DirectoryPath = [
|
const path: ReverseFilePath = [
|
||||||
{
|
{
|
||||||
handle: newDirectoryHandle(directoryId),
|
handle: newDirectoryHandle(directoryId),
|
||||||
name: directory.name,
|
name: directory.name,
|
||||||
@@ -58,7 +58,7 @@ export async function fetch(
|
|||||||
]
|
]
|
||||||
let parentDirId = directory.parentId
|
let parentDirId = directory.parentId
|
||||||
while (parentDirId) {
|
while (parentDirId) {
|
||||||
const parentDir = await authorizedGet(ctx, parentDirId)
|
const parentDir = await ctx.db.get(parentDirId)
|
||||||
if (parentDir) {
|
if (parentDir) {
|
||||||
path.push({
|
path.push({
|
||||||
handle: newDirectoryHandle(parentDir._id),
|
handle: newDirectoryHandle(parentDir._id),
|
||||||
@@ -66,19 +66,16 @@ export async function fetch(
|
|||||||
})
|
})
|
||||||
parentDirId = parentDir.parentId
|
parentDirId = parentDir.parentId
|
||||||
} else {
|
} else {
|
||||||
throw Err.create(Err.Code.DirectoryNotFound, "Parent directory not found")
|
throw Err.create(Err.Code.Internal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { ...directory, path: path.reverse() as DirectoryPath }
|
return { ...directory, path: path.reverse() as FilePath }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchContent(
|
export async function fetchContent(
|
||||||
ctx: AuthenticatedQueryCtx,
|
ctx: AuthenticatedQueryCtx,
|
||||||
{
|
{ directoryId }: { directoryId?: Id<"directories"> } = {},
|
||||||
directoryId,
|
|
||||||
trashed,
|
|
||||||
}: { directoryId?: Id<"directories">; trashed: boolean },
|
|
||||||
): Promise<FileSystemItem[]> {
|
): Promise<FileSystemItem[]> {
|
||||||
let dirId: Id<"directories"> | undefined
|
let dirId: Id<"directories"> | undefined
|
||||||
if (directoryId) {
|
if (directoryId) {
|
||||||
@@ -88,33 +85,21 @@ export async function fetchContent(
|
|||||||
const [files, directories] = await Promise.all([
|
const [files, directories] = await Promise.all([
|
||||||
ctx.db
|
ctx.db
|
||||||
.query("files")
|
.query("files")
|
||||||
.withIndex("byDirectoryId", (q) => {
|
.withIndex("byDirectoryId", (q) =>
|
||||||
if (trashed) {
|
q
|
||||||
return q
|
|
||||||
.eq("userId", ctx.user._id)
|
|
||||||
.eq("directoryId", dirId)
|
|
||||||
.gte("deletedAt", 0)
|
|
||||||
}
|
|
||||||
return q
|
|
||||||
.eq("userId", ctx.user._id)
|
.eq("userId", ctx.user._id)
|
||||||
.eq("directoryId", dirId)
|
.eq("directoryId", dirId)
|
||||||
.eq("deletedAt", undefined)
|
.eq("deletedAt", undefined),
|
||||||
})
|
)
|
||||||
.collect(),
|
.collect(),
|
||||||
ctx.db
|
ctx.db
|
||||||
.query("directories")
|
.query("directories")
|
||||||
.withIndex("byParentId", (q) => {
|
.withIndex("byParentId", (q) =>
|
||||||
if (trashed) {
|
q
|
||||||
return q
|
|
||||||
.eq("userId", ctx.user._id)
|
|
||||||
.eq("parentId", dirId)
|
|
||||||
.gte("deletedAt", 0)
|
|
||||||
}
|
|
||||||
return q
|
|
||||||
.eq("userId", ctx.user._id)
|
.eq("userId", ctx.user._id)
|
||||||
.eq("parentId", dirId)
|
.eq("parentId", dirId)
|
||||||
.eq("deletedAt", undefined)
|
.eq("deletedAt", undefined),
|
||||||
})
|
)
|
||||||
.collect(),
|
.collect(),
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -133,7 +118,7 @@ export async function create(
|
|||||||
ctx: AuthenticatedMutationCtx,
|
ctx: AuthenticatedMutationCtx,
|
||||||
{ name, parentId }: { name: string; parentId: Id<"directories"> },
|
{ name, parentId }: { name: string; parentId: Id<"directories"> },
|
||||||
): Promise<Id<"directories">> {
|
): Promise<Id<"directories">> {
|
||||||
const parentDir = await authorizedGet(ctx, parentId)
|
const parentDir = await ctx.db.get(parentId)
|
||||||
if (!parentDir) {
|
if (!parentDir) {
|
||||||
throw Err.create(
|
throw Err.create(
|
||||||
Err.Code.DirectoryNotFound,
|
Err.Code.DirectoryNotFound,
|
||||||
@@ -159,7 +144,7 @@ export async function create(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const now = Date.now()
|
const now = new Date().toISOString()
|
||||||
return await ctx.db.insert("directories", {
|
return await ctx.db.insert("directories", {
|
||||||
name,
|
name,
|
||||||
parentId,
|
parentId,
|
||||||
@@ -181,7 +166,7 @@ export async function move(
|
|||||||
) {
|
) {
|
||||||
const conflictCheckResults = await Promise.allSettled(
|
const conflictCheckResults = await Promise.allSettled(
|
||||||
sourceDirectories.map((directory) =>
|
sourceDirectories.map((directory) =>
|
||||||
authorizedGet(ctx, directory.id).then((d) => {
|
ctx.db.get(directory.id).then((d) => {
|
||||||
if (!d) {
|
if (!d) {
|
||||||
throw Err.create(
|
throw Err.create(
|
||||||
Err.Code.DirectoryNotFound,
|
Err.Code.DirectoryNotFound,
|
||||||
@@ -231,10 +216,7 @@ export async function move(
|
|||||||
ignoredHandles.add(handle)
|
ignoredHandles.add(handle)
|
||||||
} else {
|
} else {
|
||||||
promises.push(
|
promises.push(
|
||||||
ctx.db.patch(handle.id, {
|
ctx.db.patch(handle.id, { parentId: targetDirectory.id }),
|
||||||
parentId: targetDirectory.id,
|
|
||||||
updatedAt: Date.now(),
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,7 +239,7 @@ export async function moveToTrashRecursive(
|
|||||||
ctx: AuthenticatedMutationCtx,
|
ctx: AuthenticatedMutationCtx,
|
||||||
handle: DirectoryHandle,
|
handle: DirectoryHandle,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const now = Date.now()
|
const now = new Date().toISOString()
|
||||||
|
|
||||||
const filesToDelete: Id<"files">[] = []
|
const filesToDelete: Id<"files">[] = []
|
||||||
const directoriesToDelete: Id<"directories">[] = []
|
const directoriesToDelete: Id<"directories">[] = []
|
||||||
@@ -307,86 +289,3 @@ export async function moveToTrashRecursive(
|
|||||||
|
|
||||||
await Promise.all([...filePatches, ...directoryPatches])
|
await Promise.all([...filePatches, ...directoryPatches])
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deletePermanently(
|
|
||||||
ctx: AuthenticatedMutationCtx,
|
|
||||||
{
|
|
||||||
items,
|
|
||||||
}: {
|
|
||||||
items: DirectoryHandle[]
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
if (items.length === 0) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const itemsToBeDeleted = await Promise.allSettled(
|
|
||||||
items.map((item) => ctx.db.get(item.id)),
|
|
||||||
).then((results) =>
|
|
||||||
results.filter(
|
|
||||||
(result): result is PromiseFulfilledResult<Doc<"directories">> =>
|
|
||||||
result.status === "fulfilled" && result.value !== null,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
const deleteDirectoryPromises = itemsToBeDeleted.map((item) =>
|
|
||||||
ctx.db.delete(item.value._id),
|
|
||||||
)
|
|
||||||
|
|
||||||
const deleteResults = await Promise.allSettled(deleteDirectoryPromises)
|
|
||||||
|
|
||||||
const errors: Err.ApplicationErrorData[] = []
|
|
||||||
let successfulDeletions = 0
|
|
||||||
for (const result of deleteResults) {
|
|
||||||
if (result.status === "rejected") {
|
|
||||||
errors.push(Err.createJson(Err.Code.Internal))
|
|
||||||
} else {
|
|
||||||
successfulDeletions += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { deleted: successfulDeletions, errors }
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function restore(
|
|
||||||
ctx: AuthenticatedMutationCtx,
|
|
||||||
{
|
|
||||||
items,
|
|
||||||
}: {
|
|
||||||
items: DirectoryHandle[]
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
if (items.length === 0) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const itemsToBeRestored = await Promise.allSettled(
|
|
||||||
items.map((item) => ctx.db.get(item.id)),
|
|
||||||
).then((results) =>
|
|
||||||
results.filter(
|
|
||||||
(result): result is PromiseFulfilledResult<Doc<"directories">> =>
|
|
||||||
result.status === "fulfilled" && result.value !== null,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
const restoreDirectoryPromises = itemsToBeRestored.map((item) =>
|
|
||||||
ctx.db.patch(item.value._id, {
|
|
||||||
deletedAt: undefined,
|
|
||||||
updatedAt: Date.now(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
const restoreResults = await Promise.allSettled(restoreDirectoryPromises)
|
|
||||||
|
|
||||||
const errors: Err.ApplicationErrorData[] = []
|
|
||||||
let successfulRestorations = 0
|
|
||||||
for (const result of restoreResults) {
|
|
||||||
if (result.status === "rejected") {
|
|
||||||
errors.push(Err.createJson(Err.Code.Internal))
|
|
||||||
} else {
|
|
||||||
successfulRestorations += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { restored: successfulRestorations, errors }
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ export enum Code {
|
|||||||
FileNotFound = "FileNotFound",
|
FileNotFound = "FileNotFound",
|
||||||
Internal = "Internal",
|
Internal = "Internal",
|
||||||
Unauthenticated = "Unauthenticated",
|
Unauthenticated = "Unauthenticated",
|
||||||
NotFound = "NotFound",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ApplicationErrorData = { code: Code; message?: string }
|
export type ApplicationErrorData = { code: Code; message?: string }
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import type { Doc, Id } from "../_generated/dataModel"
|
|
||||||
import type {
|
|
||||||
AuthenticatedMutationCtx,
|
|
||||||
AuthenticatedQueryCtx,
|
|
||||||
} from "../functions"
|
|
||||||
import * as FileShare from "./fileshare"
|
|
||||||
|
|
||||||
const PREVIEW_FILE_SHARE_VALID_FOR_MS = 1000 * 60 * 10 // 10 minutes
|
|
||||||
|
|
||||||
export async function find(
|
|
||||||
ctx: AuthenticatedMutationCtx | AuthenticatedQueryCtx,
|
|
||||||
{ storageId }: { storageId: Id<"_storage"> },
|
|
||||||
) {
|
|
||||||
return await FileShare.findByStorageId(ctx, { storageId })
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function create(
|
|
||||||
ctx: AuthenticatedMutationCtx,
|
|
||||||
{ storageId }: { storageId: Id<"_storage"> },
|
|
||||||
) {
|
|
||||||
return await FileShare.create(ctx, {
|
|
||||||
shareToken: crypto.randomUUID(),
|
|
||||||
storageId,
|
|
||||||
expiresAt: new Date(Date.now() + PREVIEW_FILE_SHARE_VALID_FOR_MS),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function extend(
|
|
||||||
ctx: AuthenticatedMutationCtx,
|
|
||||||
{ doc }: { doc: Doc<"fileShares"> },
|
|
||||||
) {
|
|
||||||
return await FileShare.updateExpiry(ctx, {
|
|
||||||
doc,
|
|
||||||
expiresAt: new Date(Date.now() + PREVIEW_FILE_SHARE_VALID_FOR_MS),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { Doc, Id } from "@fileone/convex/dataModel"
|
import type { Id } from "../_generated/dataModel"
|
||||||
import { type AuthenticatedMutationCtx, authorizedGet } from "../functions"
|
import type { AuthenticatedMutationCtx } from "../functions"
|
||||||
import * as Err from "../shared/error"
|
import * as Err from "./error"
|
||||||
import type { DirectoryHandle, FileHandle } from "../shared/filesystem"
|
import type { DirectoryHandle, FileHandle } from "./filesystem"
|
||||||
|
|
||||||
export async function renameFile(
|
export async function renameFile(
|
||||||
ctx: AuthenticatedMutationCtx,
|
ctx: AuthenticatedMutationCtx,
|
||||||
@@ -33,7 +33,7 @@ export async function renameFile(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
await ctx.db.patch(itemId, { name: newName, updatedAt: Date.now() })
|
await ctx.db.patch(itemId, { name: newName })
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function move(
|
export async function move(
|
||||||
@@ -48,7 +48,7 @@ export async function move(
|
|||||||
) {
|
) {
|
||||||
const conflictCheckResults = await Promise.allSettled(
|
const conflictCheckResults = await Promise.allSettled(
|
||||||
items.map((fileHandle) =>
|
items.map((fileHandle) =>
|
||||||
authorizedGet(ctx, fileHandle.id).then((f) => {
|
ctx.db.get(fileHandle.id).then((f) => {
|
||||||
if (!f) {
|
if (!f) {
|
||||||
throw Err.create(
|
throw Err.create(
|
||||||
Err.Code.FileNotFound,
|
Err.Code.FileNotFound,
|
||||||
@@ -90,10 +90,7 @@ export async function move(
|
|||||||
|
|
||||||
const results = await Promise.allSettled(
|
const results = await Promise.allSettled(
|
||||||
okFiles.map((handle) =>
|
okFiles.map((handle) =>
|
||||||
ctx.db.patch(handle.id, {
|
ctx.db.patch(handle.id, { directoryId: targetDirectoryHandle.id }),
|
||||||
directoryId: targetDirectoryHandle.id,
|
|
||||||
updatedAt: Date.now(),
|
|
||||||
}),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -105,89 +102,3 @@ export async function move(
|
|||||||
|
|
||||||
return { moved: okFiles, errors }
|
return { moved: okFiles, errors }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deletePermanently(
|
|
||||||
ctx: AuthenticatedMutationCtx,
|
|
||||||
{
|
|
||||||
items,
|
|
||||||
}: {
|
|
||||||
items: FileHandle[]
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
if (items.length === 0) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const itemsToBeDeleted = await Promise.allSettled(
|
|
||||||
items.map((item) => ctx.db.get(item.id)),
|
|
||||||
).then((results) =>
|
|
||||||
results.filter(
|
|
||||||
(result): result is PromiseFulfilledResult<Doc<"files">> =>
|
|
||||||
result.status === "fulfilled" && result.value !== null,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
const deleteFilePromises = itemsToBeDeleted.map((item) =>
|
|
||||||
Promise.all([
|
|
||||||
ctx.db.delete(item.value._id),
|
|
||||||
ctx.storage.delete(item.value.storageId),
|
|
||||||
]),
|
|
||||||
)
|
|
||||||
|
|
||||||
const deleteResults = await Promise.allSettled(deleteFilePromises)
|
|
||||||
|
|
||||||
const errors: Err.ApplicationErrorData[] = []
|
|
||||||
let successfulDeletions = 0
|
|
||||||
for (const result of deleteResults) {
|
|
||||||
if (result.status === "rejected") {
|
|
||||||
errors.push(Err.createJson(Err.Code.Internal))
|
|
||||||
} else {
|
|
||||||
successfulDeletions += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { deleted: successfulDeletions, errors }
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function restore(
|
|
||||||
ctx: AuthenticatedMutationCtx,
|
|
||||||
{
|
|
||||||
items,
|
|
||||||
}: {
|
|
||||||
items: FileHandle[]
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
if (items.length === 0) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const itemsToBeRestored = await Promise.allSettled(
|
|
||||||
items.map((item) => ctx.db.get(item.id)),
|
|
||||||
).then((results) =>
|
|
||||||
results.filter(
|
|
||||||
(result): result is PromiseFulfilledResult<Doc<"files">> =>
|
|
||||||
result.status === "fulfilled" && result.value !== null,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
const restoreFilePromises = itemsToBeRestored.map((item) =>
|
|
||||||
ctx.db.patch(item.value._id, {
|
|
||||||
deletedAt: undefined,
|
|
||||||
updatedAt: Date.now(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
const restoreResults = await Promise.allSettled(restoreFilePromises)
|
|
||||||
|
|
||||||
const errors: Err.ApplicationErrorData[] = []
|
|
||||||
let successfulRestorations = 0
|
|
||||||
for (const result of restoreResults) {
|
|
||||||
if (result.status === "rejected") {
|
|
||||||
errors.push(Err.createJson(Err.Code.Internal))
|
|
||||||
} else {
|
|
||||||
successfulRestorations += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { restored: successfulRestorations, errors }
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
import type { Doc, Id } from "../_generated/dataModel"
|
|
||||||
import type { MutationCtx } from "../_generated/server"
|
|
||||||
import type {
|
|
||||||
ApiKeyAuthenticatedQueryCtx,
|
|
||||||
AuthenticatedMutationCtx,
|
|
||||||
AuthenticatedQueryCtx,
|
|
||||||
} from "../functions"
|
|
||||||
import * as Err from "../shared/error"
|
|
||||||
|
|
||||||
export async function create(
|
|
||||||
ctx: MutationCtx,
|
|
||||||
{
|
|
||||||
shareToken,
|
|
||||||
storageId,
|
|
||||||
expiresAt,
|
|
||||||
}: { shareToken: string; storageId: Id<"_storage">; expiresAt?: Date },
|
|
||||||
) {
|
|
||||||
const id = await ctx.db.insert("fileShares", {
|
|
||||||
shareToken,
|
|
||||||
storageId,
|
|
||||||
expiresAt: expiresAt?.getTime(),
|
|
||||||
})
|
|
||||||
const doc = await ctx.db.get(id)
|
|
||||||
if (!doc) {
|
|
||||||
throw Err.create(Err.Code.Internal, "Failed to create file share")
|
|
||||||
}
|
|
||||||
return doc
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function remove(
|
|
||||||
ctx: AuthenticatedMutationCtx,
|
|
||||||
{ doc }: { doc: Doc<"fileShares"> },
|
|
||||||
) {
|
|
||||||
return await ctx.db.delete(doc._id)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function find(
|
|
||||||
ctx:
|
|
||||||
| AuthenticatedMutationCtx
|
|
||||||
| AuthenticatedQueryCtx
|
|
||||||
| ApiKeyAuthenticatedQueryCtx,
|
|
||||||
{ shareToken }: { shareToken: string },
|
|
||||||
) {
|
|
||||||
const doc = await ctx.db
|
|
||||||
.query("fileShares")
|
|
||||||
.withIndex("byShareToken", (q) => q.eq("shareToken", shareToken))
|
|
||||||
.first()
|
|
||||||
if (!doc) {
|
|
||||||
throw Err.create(Err.Code.NotFound, "File share not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasExpired(doc)) {
|
|
||||||
throw Err.create(Err.Code.NotFound, "File share not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
return doc
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function findByStorageId(
|
|
||||||
ctx: AuthenticatedMutationCtx | AuthenticatedQueryCtx,
|
|
||||||
{ storageId }: { storageId: Id<"_storage"> },
|
|
||||||
) {
|
|
||||||
return await ctx.db
|
|
||||||
.query("fileShares")
|
|
||||||
.withIndex("byStorageId", (q) => q.eq("storageId", storageId))
|
|
||||||
.first()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function hasExpired(doc: Doc<"fileShares">) {
|
|
||||||
if (!doc.expiresAt) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return doc.expiresAt < Date.now()
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function updateExpiry(
|
|
||||||
ctx: AuthenticatedMutationCtx,
|
|
||||||
{ doc, expiresAt }: { doc: Doc<"fileShares">; expiresAt: Date },
|
|
||||||
) {
|
|
||||||
return await ctx.db.patch(doc._id, {
|
|
||||||
expiresAt: expiresAt.getTime(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,25 +1,68 @@
|
|||||||
import { v } from "convex/values"
|
import { v } from "convex/values"
|
||||||
import type { Doc, Id } from "../_generated/dataModel"
|
import type { Doc, Id } from "../_generated/dataModel"
|
||||||
import {
|
|
||||||
type AuthenticatedMutationCtx,
|
export enum FileType {
|
||||||
type AuthenticatedQueryCtx,
|
File = "File",
|
||||||
authorizedGet,
|
Directory = "Directory",
|
||||||
} from "../functions"
|
}
|
||||||
import * as Err from "../shared/error"
|
|
||||||
import type {
|
export type Directory = {
|
||||||
DirectoryHandle,
|
kind: FileType.Directory
|
||||||
FileHandle,
|
doc: Doc<"directories">
|
||||||
FileSystemHandle,
|
}
|
||||||
} from "../shared/filesystem"
|
export type File = {
|
||||||
import {
|
kind: FileType.File
|
||||||
FileType,
|
doc: Doc<"files">
|
||||||
newDirectoryHandle,
|
}
|
||||||
newFileHandle,
|
export type FileSystemItem = Directory | File
|
||||||
} from "../shared/filesystem"
|
|
||||||
import * as Directories from "./directories"
|
export type DirectoryPathComponent = {
|
||||||
import * as FilePreview from "./filepreview"
|
handle: DirectoryHandle
|
||||||
import * as Files from "./files"
|
name: string
|
||||||
import * as FileShare from "./fileshare"
|
}
|
||||||
|
|
||||||
|
export type FilePathComponent = {
|
||||||
|
handle: FileHandle
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
export type PathComponent = FilePathComponent | DirectoryPathComponent
|
||||||
|
export type FilePath = [...DirectoryPathComponent[], PathComponent]
|
||||||
|
export type ReverseFilePath = [PathComponent, ...DirectoryPathComponent[]]
|
||||||
|
|
||||||
|
export type DirectoryHandle = {
|
||||||
|
kind: FileType.Directory
|
||||||
|
id: Id<"directories">
|
||||||
|
}
|
||||||
|
export type FileHandle = {
|
||||||
|
kind: FileType.File
|
||||||
|
id: Id<"files">
|
||||||
|
}
|
||||||
|
export type FileSystemHandle = DirectoryHandle | FileHandle
|
||||||
|
|
||||||
|
export function newFileSystemHandle(item: FileSystemItem): FileSystemHandle {
|
||||||
|
console.log("item", item)
|
||||||
|
switch (item.kind) {
|
||||||
|
case FileType.File:
|
||||||
|
return { kind: item.kind, id: item.doc._id }
|
||||||
|
case FileType.Directory:
|
||||||
|
return { kind: item.kind, id: item.doc._id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSameHandle(
|
||||||
|
handle1: FileSystemHandle,
|
||||||
|
handle2: FileSystemHandle,
|
||||||
|
): boolean {
|
||||||
|
return handle1.kind === handle2.kind && handle1.id === handle2.id
|
||||||
|
}
|
||||||
|
|
||||||
|
export function newDirectoryHandle(id: Id<"directories">): DirectoryHandle {
|
||||||
|
return { kind: FileType.Directory, id }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function newFileHandle(id: Id<"files">): FileHandle {
|
||||||
|
return { kind: FileType.File, id }
|
||||||
|
}
|
||||||
|
|
||||||
export const VDirectoryHandle = v.object({
|
export const VDirectoryHandle = v.object({
|
||||||
kind: v.literal(FileType.Directory),
|
kind: v.literal(FileType.Directory),
|
||||||
@@ -30,238 +73,3 @@ export const VFileHandle = v.object({
|
|||||||
id: v.id("files"),
|
id: v.id("files"),
|
||||||
})
|
})
|
||||||
export const VFileSystemHandle = v.union(VFileHandle, VDirectoryHandle)
|
export const VFileSystemHandle = v.union(VFileHandle, VDirectoryHandle)
|
||||||
|
|
||||||
export async function queryRootDirectory(
|
|
||||||
ctx: AuthenticatedQueryCtx | AuthenticatedMutationCtx,
|
|
||||||
): Promise<Doc<"directories"> | null> {
|
|
||||||
return await ctx.db
|
|
||||||
.query("directories")
|
|
||||||
.withIndex("byParentId", (q) =>
|
|
||||||
q.eq("userId", ctx.user._id).eq("parentId", undefined),
|
|
||||||
)
|
|
||||||
.first()
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function ensureRootDirectory(
|
|
||||||
ctx: AuthenticatedMutationCtx,
|
|
||||||
): Promise<Id<"directories">> {
|
|
||||||
const existing = await queryRootDirectory(ctx)
|
|
||||||
|
|
||||||
if (existing) {
|
|
||||||
return existing._id
|
|
||||||
}
|
|
||||||
|
|
||||||
const now = Date.now()
|
|
||||||
return await ctx.db.insert("directories", {
|
|
||||||
name: "",
|
|
||||||
createdAt: now,
|
|
||||||
updatedAt: now,
|
|
||||||
userId: ctx.user._id,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively collects all file and directory handles from the given handles,
|
|
||||||
* including all nested items. Only includes items that are in trash (deletedAt >= 0).
|
|
||||||
*/
|
|
||||||
async function collectAllHandlesRecursively(
|
|
||||||
ctx: AuthenticatedMutationCtx,
|
|
||||||
{ handles }: { handles: FileSystemHandle[] },
|
|
||||||
): Promise<{ fileHandles: FileHandle[]; directoryHandles: DirectoryHandle[] }> {
|
|
||||||
const fileHandles: FileHandle[] = []
|
|
||||||
const directoryHandles: DirectoryHandle[] = []
|
|
||||||
|
|
||||||
for (const handle of handles) {
|
|
||||||
const queue: FileSystemHandle[] = [handle]
|
|
||||||
|
|
||||||
while (queue.length > 0) {
|
|
||||||
const currentHandle = queue.shift()!
|
|
||||||
|
|
||||||
if (currentHandle.kind === FileType.File) {
|
|
||||||
fileHandles.push(currentHandle)
|
|
||||||
} else {
|
|
||||||
directoryHandles.push(currentHandle)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentHandle.kind === FileType.Directory) {
|
|
||||||
const childDirectories = await ctx.db
|
|
||||||
.query("directories")
|
|
||||||
.withIndex("byParentId", (q) =>
|
|
||||||
q
|
|
||||||
.eq("userId", ctx.user._id)
|
|
||||||
.eq("parentId", currentHandle.id)
|
|
||||||
.gte("deletedAt", 0),
|
|
||||||
)
|
|
||||||
.collect()
|
|
||||||
|
|
||||||
const childFiles = await ctx.db
|
|
||||||
.query("files")
|
|
||||||
.withIndex("byDirectoryId", (q) =>
|
|
||||||
q
|
|
||||||
.eq("userId", ctx.user._id)
|
|
||||||
.eq("directoryId", currentHandle.id)
|
|
||||||
.gte("deletedAt", 0),
|
|
||||||
)
|
|
||||||
.collect()
|
|
||||||
|
|
||||||
for (const childDir of childDirectories) {
|
|
||||||
queue.push(newDirectoryHandle(childDir._id))
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const childFile of childFiles) {
|
|
||||||
fileHandles.push(newFileHandle(childFile._id))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { fileHandles, directoryHandles }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restores deleted items by unsetting the deletedAt field recursively.
|
|
||||||
* This includes all nested files and directories within the given handles.
|
|
||||||
*/
|
|
||||||
export async function restoreItems(
|
|
||||||
ctx: AuthenticatedMutationCtx,
|
|
||||||
{ handles }: { handles: FileSystemHandle[] },
|
|
||||||
) {
|
|
||||||
const { fileHandles, directoryHandles } =
|
|
||||||
await collectAllHandlesRecursively(ctx, { handles })
|
|
||||||
|
|
||||||
const [filesResult, directoriesResult] = await Promise.all([
|
|
||||||
Files.restore(ctx, { items: fileHandles }),
|
|
||||||
Directories.restore(ctx, { items: directoryHandles }),
|
|
||||||
])
|
|
||||||
|
|
||||||
return {
|
|
||||||
restored: {
|
|
||||||
files: filesResult?.restored || 0,
|
|
||||||
directories: directoriesResult?.restored || 0,
|
|
||||||
},
|
|
||||||
errors: [
|
|
||||||
...(filesResult?.errors || []),
|
|
||||||
...(directoriesResult?.errors || []),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function deleteItemsPermanently(
|
|
||||||
ctx: AuthenticatedMutationCtx,
|
|
||||||
{ handles }: { handles: FileSystemHandle[] },
|
|
||||||
) {
|
|
||||||
const { fileHandles, directoryHandles } =
|
|
||||||
await collectAllHandlesRecursively(ctx, { handles })
|
|
||||||
|
|
||||||
const [filesResult, directoriesResult] = await Promise.all([
|
|
||||||
Files.deletePermanently(ctx, { items: fileHandles }),
|
|
||||||
Directories.deletePermanently(ctx, { items: directoryHandles }),
|
|
||||||
])
|
|
||||||
|
|
||||||
return {
|
|
||||||
deleted: {
|
|
||||||
files: filesResult?.deleted || 0,
|
|
||||||
directories: directoriesResult?.deleted || 0,
|
|
||||||
},
|
|
||||||
errors: [
|
|
||||||
...(filesResult?.errors || []),
|
|
||||||
...(directoriesResult?.errors || []),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function emptyTrash(ctx: AuthenticatedMutationCtx) {
|
|
||||||
const rootDir = await queryRootDirectory(ctx)
|
|
||||||
if (!rootDir) {
|
|
||||||
throw Err.create(Err.Code.NotFound, "user root directory not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
const dirs = await ctx.db
|
|
||||||
.query("directories")
|
|
||||||
.withIndex("byParentId", (q) =>
|
|
||||||
q
|
|
||||||
.eq("userId", ctx.user._id)
|
|
||||||
.eq("parentId", rootDir._id)
|
|
||||||
.gte("deletedAt", 0),
|
|
||||||
)
|
|
||||||
.collect()
|
|
||||||
|
|
||||||
const files = await ctx.db
|
|
||||||
.query("files")
|
|
||||||
.withIndex("byDirectoryId", (q) =>
|
|
||||||
q
|
|
||||||
.eq("userId", ctx.user._id)
|
|
||||||
.eq("directoryId", rootDir._id)
|
|
||||||
.gte("deletedAt", 0),
|
|
||||||
)
|
|
||||||
.collect()
|
|
||||||
|
|
||||||
if (dirs.length === 0 && files.length === 0) {
|
|
||||||
return {
|
|
||||||
deleted: {
|
|
||||||
files: 0,
|
|
||||||
directories: 0,
|
|
||||||
},
|
|
||||||
errors: [],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return await deleteItemsPermanently(ctx, {
|
|
||||||
handles: [
|
|
||||||
...dirs.map((it) => newDirectoryHandle(it._id)),
|
|
||||||
...files.map((it) => newFileHandle(it._id)),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function fetchFileUrl(
|
|
||||||
ctx: AuthenticatedQueryCtx,
|
|
||||||
{ fileId }: { fileId: Id<"files"> },
|
|
||||||
): Promise<string> {
|
|
||||||
const file = await authorizedGet(ctx, fileId)
|
|
||||||
if (!file) {
|
|
||||||
throw Err.create(Err.Code.NotFound, "file not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = await ctx.storage.getUrl(file.storageId)
|
|
||||||
if (!url) {
|
|
||||||
throw Err.create(Err.Code.NotFound, "file not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function openFile(
|
|
||||||
ctx: AuthenticatedMutationCtx,
|
|
||||||
{ fileId }: { fileId: Id<"files"> },
|
|
||||||
) {
|
|
||||||
const file = await authorizedGet(ctx, fileId)
|
|
||||||
if (!file) {
|
|
||||||
throw Err.create(Err.Code.NotFound, "file not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileShare = await FilePreview.find(ctx, {
|
|
||||||
storageId: file.storageId,
|
|
||||||
})
|
|
||||||
if (fileShare && !FileShare.hasExpired(fileShare)) {
|
|
||||||
await FilePreview.extend(ctx, { doc: fileShare })
|
|
||||||
return {
|
|
||||||
file,
|
|
||||||
shareToken: fileShare.shareToken,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const [newFileShare] = await Promise.all([
|
|
||||||
FilePreview.create(ctx, {
|
|
||||||
storageId: file.storageId,
|
|
||||||
}),
|
|
||||||
ctx.db.patch(fileId, {
|
|
||||||
lastAccessedAt: Date.now(),
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
|
|
||||||
return {
|
|
||||||
file,
|
|
||||||
shareToken: newFileShare.shareToken,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,25 +1,54 @@
|
|||||||
import type { MutationCtx, QueryCtx } from "@fileone/convex/server"
|
import type { MutationCtx, QueryCtx } from "../_generated/server"
|
||||||
import { authComponent } from "../auth"
|
import type { AuthenticatedMutationCtx } from "../functions"
|
||||||
import * as Err from "../shared/error"
|
import * as Err from "./error"
|
||||||
|
|
||||||
export type AuthUser = Awaited<ReturnType<typeof authComponent.getAuthUser>>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current authenticated user identity
|
* Get the current authenticated user identity
|
||||||
* Throws an error if the user is not authenticated */
|
* Throws an error if the user is not authenticated */
|
||||||
export async function userIdentityOrThrow(ctx: QueryCtx | MutationCtx) {
|
export async function userIdentityOrThrow(ctx: QueryCtx | MutationCtx) {
|
||||||
const identity = await ctx.auth.getUserIdentity()
|
const identity = await ctx.auth.getUserIdentity()
|
||||||
|
|
||||||
if (!identity) {
|
if (!identity) {
|
||||||
throw Err.create(Err.Code.Unauthenticated, "Not authenticated")
|
throw Err.create(Err.Code.Unauthenticated, "Not authenticated")
|
||||||
}
|
}
|
||||||
|
|
||||||
return identity
|
return identity
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get user document from JWT authentication
|
* Get internal user document from JWT authentication
|
||||||
* Throws an error if the user is not authenticated
|
* Throws an error if the user is not authenticated
|
||||||
*/
|
*/
|
||||||
export async function userOrThrow(ctx: QueryCtx | MutationCtx) {
|
export async function userOrThrow(ctx: QueryCtx | MutationCtx) {
|
||||||
const user = await authComponent.getAuthUser(ctx)
|
const identity = await userIdentityOrThrow(ctx)
|
||||||
|
|
||||||
|
// Look for existing user by JWT subject
|
||||||
|
const user = await ctx.db
|
||||||
|
.query("users")
|
||||||
|
.withIndex("byJwtSubject", (q) => q.eq("jwtSubject", identity.subject))
|
||||||
|
.first()
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw Err.create(
|
||||||
|
Err.Code.Unauthenticated,
|
||||||
|
"User not found - please sync user first",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function register(ctx: AuthenticatedMutationCtx) {
|
||||||
|
const now = new Date().toISOString()
|
||||||
|
await Promise.all([
|
||||||
|
ctx.db.insert("users", {
|
||||||
|
jwtSubject: ctx.identity.subject,
|
||||||
|
}),
|
||||||
|
ctx.db.insert("directories", {
|
||||||
|
name: "",
|
||||||
|
userId: ctx.user._id,
|
||||||
|
createdAt: now,
|
||||||
|
updatedAt: now,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,27 +2,11 @@
|
|||||||
"name": "@fileone/convex",
|
"name": "@fileone/convex",
|
||||||
"module": "index.ts",
|
"module": "index.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
|
||||||
"./filesystem": "./shared/filesystem.ts",
|
|
||||||
"./error": "./shared/error.ts",
|
|
||||||
"./types": "./shared/types.ts",
|
|
||||||
"./dataModel": "./_generated/dataModel.d.ts",
|
|
||||||
"./api": "./_generated/api.js",
|
|
||||||
"./server": "./_generated/server.js",
|
|
||||||
"./_generated/*": "./_generated/*",
|
|
||||||
"./model/*": "./model/*",
|
|
||||||
"./shared/*": "./shared/*"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@drexa/auth": "workspace:*",
|
"@fileone/path": "workspace:*"
|
||||||
"@fileone/path": "workspace:*",
|
|
||||||
"hash-wasm": "^4.12.0"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5",
|
"typescript": "^5",
|
||||||
"better-auth": "1.3.8",
|
"convex": "^1.27.0"
|
||||||
"convex": "^1.27.0",
|
|
||||||
"convex-helpers": "^0.1.104",
|
|
||||||
"@convex-dev/better-auth": "^0.8.9"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,36 +2,36 @@ import { defineSchema, defineTable } from "convex/server"
|
|||||||
import { v } from "convex/values"
|
import { v } from "convex/values"
|
||||||
|
|
||||||
const schema = defineSchema({
|
const schema = defineSchema({
|
||||||
|
users: defineTable({
|
||||||
|
jwtSubject: v.string(),
|
||||||
|
}).index("byJwtSubject", ["jwtSubject"]),
|
||||||
files: defineTable({
|
files: defineTable({
|
||||||
storageId: v.id("_storage"),
|
storageId: v.id("_storage"),
|
||||||
userId: v.string(), // BetterAuth user IDs are strings, not Convex Ids
|
userId: v.id("users"),
|
||||||
directoryId: v.optional(v.id("directories")),
|
directoryId: v.optional(v.id("directories")),
|
||||||
name: v.string(),
|
name: v.string(),
|
||||||
size: v.number(),
|
size: v.number(),
|
||||||
mimeType: v.optional(v.string()),
|
mimeType: v.optional(v.string()),
|
||||||
createdAt: v.number(),
|
createdAt: v.string(),
|
||||||
updatedAt: v.number(),
|
updatedAt: v.string(),
|
||||||
deletedAt: v.optional(v.number()),
|
deletedAt: v.optional(v.string()),
|
||||||
lastAccessedAt: v.optional(v.number()),
|
|
||||||
})
|
})
|
||||||
.index("byDirectoryId", ["userId", "directoryId", "deletedAt"])
|
.index("byDirectoryId", ["userId", "directoryId", "deletedAt"])
|
||||||
.index("byUserId", ["userId", "deletedAt"])
|
.index("byUserId", ["userId", "deletedAt"])
|
||||||
.index("byDeletedAt", ["deletedAt"])
|
.index("byDeletedAt", ["deletedAt"])
|
||||||
.index("byLastAccessedAt", ["userId", "lastAccessedAt"])
|
|
||||||
.index("uniqueFileInDirectory", [
|
.index("uniqueFileInDirectory", [
|
||||||
"userId",
|
"userId",
|
||||||
"directoryId",
|
"directoryId",
|
||||||
"name",
|
"name",
|
||||||
"deletedAt",
|
"deletedAt",
|
||||||
]),
|
]),
|
||||||
|
|
||||||
directories: defineTable({
|
directories: defineTable({
|
||||||
name: v.string(),
|
name: v.string(),
|
||||||
userId: v.string(), // BetterAuth user IDs are strings, not Convex Ids
|
userId: v.id("users"),
|
||||||
parentId: v.optional(v.id("directories")),
|
parentId: v.optional(v.id("directories")),
|
||||||
createdAt: v.number(),
|
createdAt: v.string(),
|
||||||
updatedAt: v.number(),
|
updatedAt: v.string(),
|
||||||
deletedAt: v.optional(v.number()),
|
deletedAt: v.optional(v.string()),
|
||||||
})
|
})
|
||||||
.index("byUserId", ["userId", "deletedAt"])
|
.index("byUserId", ["userId", "deletedAt"])
|
||||||
.index("byParentId", ["userId", "parentId", "deletedAt"])
|
.index("byParentId", ["userId", "parentId", "deletedAt"])
|
||||||
@@ -41,21 +41,6 @@ const schema = defineSchema({
|
|||||||
"name",
|
"name",
|
||||||
"deletedAt",
|
"deletedAt",
|
||||||
]),
|
]),
|
||||||
|
|
||||||
apiKeys: defineTable({
|
|
||||||
publicId: v.string(),
|
|
||||||
hashedKey: v.string(),
|
|
||||||
expiresAt: v.optional(v.number()),
|
|
||||||
}).index("byPublicId", ["publicId"]),
|
|
||||||
|
|
||||||
fileShares: defineTable({
|
|
||||||
shareToken: v.string(),
|
|
||||||
storageId: v.id("_storage"),
|
|
||||||
expiresAt: v.optional(v.number()),
|
|
||||||
})
|
|
||||||
.index("byShareToken", ["shareToken"])
|
|
||||||
.index("byExpiredAt", ["expiresAt"])
|
|
||||||
.index("byStorageId", ["storageId"]),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export default schema
|
export default schema
|
||||||
|
|||||||
@@ -1,96 +0,0 @@
|
|||||||
/**
|
|
||||||
* Client-safe filesystem types and utilities.
|
|
||||||
* This file contains types and pure functions that can be safely imported by frontend code.
|
|
||||||
* NO server-only dependencies should be imported here.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { Doc, Id } from "@fileone/convex/dataModel"
|
|
||||||
import type * as Err from "./error"
|
|
||||||
|
|
||||||
export enum FileType {
|
|
||||||
File = "File",
|
|
||||||
Directory = "Directory",
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Directory = {
|
|
||||||
kind: FileType.Directory
|
|
||||||
doc: Doc<"directories">
|
|
||||||
}
|
|
||||||
|
|
||||||
export type File = {
|
|
||||||
kind: FileType.File
|
|
||||||
doc: Doc<"files">
|
|
||||||
}
|
|
||||||
|
|
||||||
export type FileSystemItem = Directory | File
|
|
||||||
|
|
||||||
export type DirectoryPathComponent = {
|
|
||||||
handle: DirectoryHandle
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type FilePathComponent = {
|
|
||||||
handle: FileHandle
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PathComponent = FilePathComponent | DirectoryPathComponent
|
|
||||||
|
|
||||||
export type DirectoryPath = [
|
|
||||||
DirectoryPathComponent,
|
|
||||||
...DirectoryPathComponent[],
|
|
||||||
]
|
|
||||||
|
|
||||||
export type FilePath = [...DirectoryPathComponent[], PathComponent]
|
|
||||||
|
|
||||||
export type ReverseFilePath = [PathComponent, ...DirectoryPathComponent[]]
|
|
||||||
|
|
||||||
export type DirectoryHandle = {
|
|
||||||
kind: FileType.Directory
|
|
||||||
id: Id<"directories">
|
|
||||||
}
|
|
||||||
|
|
||||||
export type FileHandle = {
|
|
||||||
kind: FileType.File
|
|
||||||
id: Id<"files">
|
|
||||||
}
|
|
||||||
|
|
||||||
export type FileSystemHandle = DirectoryHandle | FileHandle
|
|
||||||
|
|
||||||
export type OpenedFile = {
|
|
||||||
file: Doc<"files">
|
|
||||||
shareToken: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type DeleteResult = {
|
|
||||||
deleted: {
|
|
||||||
files: number
|
|
||||||
directories: number
|
|
||||||
}
|
|
||||||
errors: Err.ApplicationErrorData[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function newFileSystemHandle(item: FileSystemItem): FileSystemHandle {
|
|
||||||
console.log("item", item)
|
|
||||||
switch (item.kind) {
|
|
||||||
case FileType.File:
|
|
||||||
return { kind: item.kind, id: item.doc._id }
|
|
||||||
case FileType.Directory:
|
|
||||||
return { kind: item.kind, id: item.doc._id }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isSameHandle(
|
|
||||||
handle1: FileSystemHandle,
|
|
||||||
handle2: FileSystemHandle,
|
|
||||||
): boolean {
|
|
||||||
return handle1.kind === handle2.kind && handle1.id === handle2.id
|
|
||||||
}
|
|
||||||
|
|
||||||
export function newDirectoryHandle(id: Id<"directories">): DirectoryHandle {
|
|
||||||
return { kind: FileType.Directory, id }
|
|
||||||
}
|
|
||||||
|
|
||||||
export function newFileHandle(id: Id<"files">): FileHandle {
|
|
||||||
return { kind: FileType.File, id }
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
/**
|
|
||||||
* Shared types that can be safely imported by both client and server code.
|
|
||||||
* This file should NOT import any server-only dependencies.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { Doc } from "@fileone/convex/dataModel"
|
|
||||||
import type { DirectoryPath } from "./filesystem"
|
|
||||||
|
|
||||||
export type DirectoryInfo = Doc<"directories"> & { path: DirectoryPath }
|
|
||||||
export type DirectoryItem = DirectoryInfo
|
|
||||||
export type DirectoryItemKind = "directory" | "file"
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
/* This TypeScript project config describes the environment that
|
|
||||||
* Convex functions run in and is used to typecheck them.
|
|
||||||
* You can modify it, but some settings are required to use Convex.
|
|
||||||
*/
|
|
||||||
"compilerOptions": {
|
|
||||||
/* These settings are not required by Convex and can be modified. */
|
|
||||||
"allowJs": true,
|
|
||||||
"strict": true,
|
|
||||||
"moduleResolution": "Bundler",
|
|
||||||
"jsx": "react-jsx",
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
|
|
||||||
/* These compiler options are required by Convex */
|
|
||||||
"target": "ESNext",
|
|
||||||
"lib": ["ES2021", "dom"],
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"module": "ESNext",
|
|
||||||
"isolatedModules": true,
|
|
||||||
"noEmit": true
|
|
||||||
},
|
|
||||||
"include": ["./**/*"],
|
|
||||||
"exclude": ["./_generated"]
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user