@@ -0,0 +1,254 @@
|
|||||||
|
# Pascal Editor - Setup Guide
|
||||||
|
|
||||||
|
This guide will help you set up the Pascal Editor with authentication and database integration.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Node.js 18+ or Bun 1.3+
|
||||||
|
- Docker Desktop (for running Supabase locally)
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Install Dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
This installs the Supabase CLI as a dev dependency - no need for global installation!
|
||||||
|
|
||||||
|
### 2. Start Supabase Local Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun db:start
|
||||||
|
```
|
||||||
|
|
||||||
|
This will start a local Supabase instance. You'll see output like:
|
||||||
|
|
||||||
|
```
|
||||||
|
API URL: http://127.0.0.1:54321
|
||||||
|
DB URL: postgresql://postgres:postgres@127.0.0.1:54322/postgres
|
||||||
|
Studio URL: http://127.0.0.1:54323
|
||||||
|
Anon key: eyJh...
|
||||||
|
Service role key: eyJh...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Configure Environment Variables
|
||||||
|
|
||||||
|
Create `apps/editor/.env.local` with the following variables:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Database Connection (Supabase local)
|
||||||
|
POSTGRES_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres
|
||||||
|
|
||||||
|
# Supabase Configuration
|
||||||
|
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
|
||||||
|
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0
|
||||||
|
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU
|
||||||
|
|
||||||
|
# Better Auth (generate your own secret with: openssl rand -base64 32)
|
||||||
|
BETTER_AUTH_SECRET=<generate_with_command_below>
|
||||||
|
BETTER_AUTH_URL=http://localhost:3000
|
||||||
|
|
||||||
|
# Google Maps (optional, for address search)
|
||||||
|
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=<your_google_maps_key>
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate a secret for `BETTER_AUTH_SECRET`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openssl rand -base64 32
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Run Database Migrations
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun db:reset
|
||||||
|
```
|
||||||
|
|
||||||
|
This will create all necessary tables for authentication and properties.
|
||||||
|
|
||||||
|
### 6. Start the Development Server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun dev
|
||||||
|
```
|
||||||
|
|
||||||
|
The editor will be available at http://localhost:3000
|
||||||
|
|
||||||
|
## Monorepo Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── apps/
|
||||||
|
│ └── editor/ # Next.js editor application
|
||||||
|
│ ├── app/
|
||||||
|
│ │ └── api/auth/ # Better Auth API routes
|
||||||
|
│ ├── components/ # UI components
|
||||||
|
│ └── features/
|
||||||
|
│ └── cloud-sync/ # Cloud sync feature
|
||||||
|
├── packages/
|
||||||
|
│ ├── auth/ # @pascal-app/auth - Authentication package
|
||||||
|
│ │ ├── src/
|
||||||
|
│ │ │ ├── server.ts # Better Auth server config
|
||||||
|
│ │ │ └── client.ts # Better Auth client
|
||||||
|
│ │ └── README.md
|
||||||
|
│ ├── db/ # @pascal-app/db - Database package
|
||||||
|
│ │ ├── src/
|
||||||
|
│ │ │ ├── client.ts # Supabase client (with RLS)
|
||||||
|
│ │ │ ├── server.ts # Supabase admin client
|
||||||
|
│ │ │ └── types.ts # Database types
|
||||||
|
│ │ ├── supabase/
|
||||||
|
│ │ │ ├── config.toml
|
||||||
|
│ │ │ └── migrations/ # SQL migrations
|
||||||
|
│ │ └── README.md
|
||||||
|
│ ├── core/ # @pascal-app/core - Core editor logic
|
||||||
|
│ ├── viewer/ # @pascal-app/viewer - 3D viewer
|
||||||
|
│ └── ui/ # @repo/ui - Shared UI components
|
||||||
|
└── turbo.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Database Schema
|
||||||
|
|
||||||
|
### Auth Tables (Better Auth)
|
||||||
|
|
||||||
|
- **users** - User accounts with email and profile
|
||||||
|
- **sessions** - Active authentication sessions
|
||||||
|
- **accounts** - OAuth provider accounts (for future use)
|
||||||
|
- **verification_tokens** - Magic link tokens
|
||||||
|
|
||||||
|
### Application Tables
|
||||||
|
|
||||||
|
- **properties** - User properties
|
||||||
|
- `id`: Property ID
|
||||||
|
- `name`: Property name
|
||||||
|
- `owner_id`: User ID (foreign key to users)
|
||||||
|
|
||||||
|
- **properties_addresses** - Property addresses with Google Maps data
|
||||||
|
- `id`: Address ID
|
||||||
|
- `property_id`: Property ID (foreign key)
|
||||||
|
- `formatted_address`: Full address
|
||||||
|
- `latitude`, `longitude`: GPS coordinates
|
||||||
|
- Plus detailed address components (street, city, state, etc.)
|
||||||
|
|
||||||
|
- **properties_models** - Scene graph models (versions)
|
||||||
|
- `id`: Model ID
|
||||||
|
- `property_id`: Property ID (foreign key)
|
||||||
|
- `name`: Model name
|
||||||
|
- `version`: Version number
|
||||||
|
- `draft`: Draft status
|
||||||
|
- `scene_graph`: JSONB scene graph data
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
|
||||||
|
- **Magic Link Sign-In**: Passwordless authentication via email
|
||||||
|
- **Session Management**: 7-day sessions with automatic refresh
|
||||||
|
- **Cookie-based**: Secure httpOnly cookies
|
||||||
|
|
||||||
|
### Property Management
|
||||||
|
|
||||||
|
- **Create Properties**: Add properties with real-world addresses
|
||||||
|
- **Google Maps Integration**: Address autocomplete and geocoding
|
||||||
|
- **Switch Properties**: Seamlessly switch between properties
|
||||||
|
|
||||||
|
### Scene Management
|
||||||
|
|
||||||
|
- **Auto-Save**: Changes saved every 2 seconds
|
||||||
|
- **Scene Loading**: Automatic scene loading when switching properties
|
||||||
|
- **Version Control**: Models are versioned for future rollback support
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
### Making Database Changes
|
||||||
|
|
||||||
|
1. Create a new migration:
|
||||||
|
```bash
|
||||||
|
cd packages/db
|
||||||
|
supabase migration new your_migration_name
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Edit the migration file in `supabase/migrations/`
|
||||||
|
|
||||||
|
3. Apply the migration:
|
||||||
|
```bash
|
||||||
|
supabase db reset
|
||||||
|
```
|
||||||
|
|
||||||
|
### Updating Database Types
|
||||||
|
|
||||||
|
After changing the database schema, regenerate TypeScript types:
|
||||||
|
|
||||||
|
1. Update `packages/db/src/types.ts` to match your new schema
|
||||||
|
2. Run `bun install` to update type checking
|
||||||
|
|
||||||
|
### Testing Authentication
|
||||||
|
|
||||||
|
1. Start the editor: `bun dev`
|
||||||
|
2. Click "Save to cloud" button
|
||||||
|
3. Enter your email
|
||||||
|
4. Check console for magic link (not sent via email in development)
|
||||||
|
5. Click the link to authenticate
|
||||||
|
|
||||||
|
## Supabase Studio
|
||||||
|
|
||||||
|
Access the local Supabase Studio at: http://127.0.0.1:54323
|
||||||
|
|
||||||
|
Use this to:
|
||||||
|
- Browse and edit tables
|
||||||
|
- Run SQL queries
|
||||||
|
- View logs
|
||||||
|
- Manage RLS policies
|
||||||
|
- Test database functions
|
||||||
|
|
||||||
|
## Production Deployment
|
||||||
|
|
||||||
|
For production deployment:
|
||||||
|
|
||||||
|
1. Create a Supabase project at https://supabase.com
|
||||||
|
2. Get your production database connection string
|
||||||
|
3. Update environment variables in your hosting platform
|
||||||
|
4. Link and push migrations:
|
||||||
|
```bash
|
||||||
|
cd packages/db
|
||||||
|
bunx supabase link --project-ref your-project-ref
|
||||||
|
bunx supabase db push
|
||||||
|
```
|
||||||
|
5. Configure email provider in `packages/auth/src/server.ts`
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "Missing POSTGRES_URL" error
|
||||||
|
|
||||||
|
Make sure you've set `POSTGRES_URL` in `apps/editor/.env.local` to your Supabase connection string.
|
||||||
|
|
||||||
|
### Supabase not starting
|
||||||
|
|
||||||
|
Try stopping and restarting:
|
||||||
|
```bash
|
||||||
|
bun db:stop
|
||||||
|
bun db:start
|
||||||
|
```
|
||||||
|
|
||||||
|
### Migration errors
|
||||||
|
|
||||||
|
Reset the database:
|
||||||
|
```bash
|
||||||
|
cd packages/db
|
||||||
|
supabase db reset
|
||||||
|
```
|
||||||
|
|
||||||
|
### Auth not working
|
||||||
|
|
||||||
|
1. Check that Better Auth API route exists at `apps/editor/app/api/auth/[...all]/route.ts`
|
||||||
|
2. Verify `BETTER_AUTH_SECRET` and `BETTER_AUTH_URL` are set
|
||||||
|
3. Check console for magic link URLs in development
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
- Configure email provider for magic links
|
||||||
|
- Add OAuth providers (Google, GitHub, etc.)
|
||||||
|
- Set up production Supabase project
|
||||||
|
- Configure RLS policies for additional security
|
||||||
|
- Add more property features (sharing, collaboration, etc.)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* Better Auth API route handler
|
||||||
|
* Handles all /api/auth/* routes for authentication
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { toNextJsHandler } from 'better-auth/next-js'
|
||||||
|
import { auth } from '@/lib/auth'
|
||||||
|
|
||||||
|
const { GET, POST } = toNextJsHandler(auth)
|
||||||
|
|
||||||
|
export { GET, POST }
|
||||||
@@ -4,9 +4,11 @@ import { initSpaceDetectionSync, initSpatialGridSync, useScene } from '@pascal-a
|
|||||||
import { Viewer } from '@pascal-app/viewer'
|
import { Viewer } from '@pascal-app/viewer'
|
||||||
import { useKeyboard } from '@/hooks/use-keyboard'
|
import { useKeyboard } from '@/hooks/use-keyboard'
|
||||||
import useEditor from '@/store/use-editor'
|
import useEditor from '@/store/use-editor'
|
||||||
|
import { usePropertyScene } from '@/features/community/lib/models/hooks'
|
||||||
import { ZoneSystem } from '../systems/zone/zone-system'
|
import { ZoneSystem } from '../systems/zone/zone-system'
|
||||||
import { ToolManager } from '../tools/tool-manager'
|
import { ToolManager } from '../tools/tool-manager'
|
||||||
import { ActionMenu } from '../ui/action-menu'
|
import { ActionMenu } from '../ui/action-menu'
|
||||||
|
import { CloudSaveButton } from '@/features/community/components/cloud-save-button'
|
||||||
import { PanelManager } from '../ui/panels/panel-manager'
|
import { PanelManager } from '../ui/panels/panel-manager'
|
||||||
import { SidebarProvider } from '../ui/primitives/sidebar'
|
import { SidebarProvider } from '../ui/primitives/sidebar'
|
||||||
import { AppSidebar } from '../ui/sidebar/app-sidebar'
|
import { AppSidebar } from '../ui/sidebar/app-sidebar'
|
||||||
@@ -15,8 +17,8 @@ import { ExportManager } from './export-manager'
|
|||||||
import { Grid } from './grid'
|
import { Grid } from './grid'
|
||||||
import { SelectionManager } from './selection-manager'
|
import { SelectionManager } from './selection-manager'
|
||||||
|
|
||||||
|
// Load default scene initially (will be replaced when property loads)
|
||||||
useScene.getState().loadScene()
|
useScene.getState().loadScene()
|
||||||
console.log('Loaded scene in editor')
|
|
||||||
initSpatialGridSync()
|
initSpatialGridSync()
|
||||||
initSpaceDetectionSync(useScene, useEditor)
|
initSpaceDetectionSync(useScene, useEditor)
|
||||||
|
|
||||||
@@ -27,6 +29,7 @@ export default function Editor() {
|
|||||||
<div className="w-full h-full">
|
<div className="w-full h-full">
|
||||||
<ActionMenu />
|
<ActionMenu />
|
||||||
<PanelManager />
|
<PanelManager />
|
||||||
|
<CloudSaveButton />
|
||||||
|
|
||||||
<SidebarProvider className="fixed z-20">
|
<SidebarProvider className="fixed z-20">
|
||||||
<AppSidebar />
|
<AppSidebar />
|
||||||
|
|||||||
@@ -0,0 +1,198 @@
|
|||||||
|
# Community Feature
|
||||||
|
|
||||||
|
This directory contains the **optional** community features (cloud synchronization and authentication) for the Pascal Editor. This feature is specific to the Pascal platform and can be safely removed if you're using the editor standalone.
|
||||||
|
|
||||||
|
## What This Does
|
||||||
|
|
||||||
|
The community feature provides:
|
||||||
|
|
||||||
|
- **Authentication** - Sign in with magic link via Better Auth
|
||||||
|
- **Property Management** - Create and manage properties with Google Maps address search
|
||||||
|
- **Scene Loading** - Automatically load property scenes from the database when a property is selected
|
||||||
|
- **Auto-Save** - Automatically save scene changes to the database (2-second debounce)
|
||||||
|
- **Database Sync** - Save and load editor state from a PostgreSQL database via Supabase
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
features/community/
|
||||||
|
├── lib/
|
||||||
|
│ ├── auth/
|
||||||
|
│ │ ├── client.ts # Re-exports from @pascal-app/auth
|
||||||
|
│ │ ├── server.ts # Server-side session handling
|
||||||
|
│ │ └── hooks.ts # useAuth React hook
|
||||||
|
│ ├── properties/
|
||||||
|
│ │ ├── actions.ts # Server actions for CRUD operations
|
||||||
|
│ │ ├── types.ts # TypeScript types for properties
|
||||||
|
│ │ ├── hooks.ts # Property React hooks
|
||||||
|
│ │ └── store.ts # Zustand store for property state
|
||||||
|
│ ├── models/
|
||||||
|
│ │ ├── actions.ts # Scene model CRUD operations
|
||||||
|
│ │ └── hooks.ts # Scene loading and auto-save hooks
|
||||||
|
│ ├── database/
|
||||||
|
│ │ └── server.ts # Re-exports from @pascal-app/db
|
||||||
|
│ └── utils/
|
||||||
|
│ └── id-generator.ts # nanoid-based ID generation
|
||||||
|
├── components/
|
||||||
|
│ ├── cloud-save-button.tsx # Main UI entry point (top-right button)
|
||||||
|
│ ├── sign-in-dialog.tsx # Magic link sign-in dialog
|
||||||
|
│ ├── profile-dropdown.tsx # User profile menu
|
||||||
|
│ ├── property-dropdown.tsx # Property selector dropdown
|
||||||
|
│ ├── new-property-dialog.tsx # Create new property dialog
|
||||||
|
│ └── google-address-search.tsx # Google Maps autocomplete
|
||||||
|
└── README.md # This file
|
||||||
|
```
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
### Authentication Flow
|
||||||
|
1. User clicks "Save to cloud" button
|
||||||
|
2. Signs in with magic link (email-based, no password)
|
||||||
|
3. Better Auth session is stored in cookies
|
||||||
|
4. Server actions validate session using Better Auth API
|
||||||
|
|
||||||
|
### Property Management
|
||||||
|
1. User creates a property with a real-world address (Google Maps)
|
||||||
|
2. Address and property are saved to PostgreSQL via Supabase
|
||||||
|
3. Properties are associated with the authenticated user
|
||||||
|
4. User can switch between properties
|
||||||
|
|
||||||
|
### Scene Management
|
||||||
|
1. When a property is selected, its scene is loaded from `properties_models` table
|
||||||
|
2. If no scene exists, loads default empty scene
|
||||||
|
3. Scene changes are auto-saved every 2 seconds (debounced)
|
||||||
|
4. Updates existing model (highest version) instead of creating new ones
|
||||||
|
5. Scene graph includes all nodes and hierarchy
|
||||||
|
|
||||||
|
### Database Integration
|
||||||
|
- Uses Supabase (PostgreSQL) for database access
|
||||||
|
- Better Auth manages authentication tables directly
|
||||||
|
- Server actions use service role key to bypass RLS
|
||||||
|
- Permissions enforced by filtering on `owner_id`
|
||||||
|
- Tables: `users`, `sessions`, `properties`, `properties_addresses`, `properties_models`
|
||||||
|
|
||||||
|
## Required Environment Variables
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Database Connection
|
||||||
|
DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres
|
||||||
|
|
||||||
|
# Supabase Configuration
|
||||||
|
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
|
||||||
|
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key_here
|
||||||
|
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key_here
|
||||||
|
|
||||||
|
# Better Auth
|
||||||
|
BETTER_AUTH_SECRET=<generate_with_openssl_rand_base64_32>
|
||||||
|
BETTER_AUTH_URL=http://localhost:3000
|
||||||
|
|
||||||
|
# Google Maps API Key (for address search)
|
||||||
|
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=your_google_maps_key_here
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate `BETTER_AUTH_SECRET`:
|
||||||
|
```bash
|
||||||
|
openssl rand -base64 32
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
The community feature requires these packages:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"better-auth": "^1.4.18",
|
||||||
|
"@supabase/supabase-js": "^2.95.3",
|
||||||
|
"@react-google-maps/api": "^2.20.8",
|
||||||
|
"nanoid": "^5.1.6"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## How to Remove (For Open Source Users)
|
||||||
|
|
||||||
|
If you want to use the editor without community features:
|
||||||
|
|
||||||
|
### 1. Delete this directory
|
||||||
|
```bash
|
||||||
|
rm -rf features/community
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Remove the CloudSaveButton from the editor
|
||||||
|
Edit `components/editor/index.tsx`:
|
||||||
|
```diff
|
||||||
|
- import { CloudSaveButton } from '@/features/community/components/cloud-save-button'
|
||||||
|
|
||||||
|
export default function Editor() {
|
||||||
|
return (
|
||||||
|
<div className="w-full h-full">
|
||||||
|
<ActionMenu />
|
||||||
|
<PanelManager />
|
||||||
|
- <CloudSaveButton />
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Remove dependencies (optional)
|
||||||
|
Edit `package.json`:
|
||||||
|
```diff
|
||||||
|
- "better-auth": "^1.4.18",
|
||||||
|
- "@supabase/supabase-js": "^2.95.3",
|
||||||
|
- "@react-google-maps/api": "^2.20.8",
|
||||||
|
- "nanoid": "^5.1.6"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Remove environment variables
|
||||||
|
Delete from `.env.local` and `.env.example`:
|
||||||
|
```diff
|
||||||
|
- NEXT_PUBLIC_API_URL=...
|
||||||
|
- NEXT_PUBLIC_SUPABASE_URL=...
|
||||||
|
- NEXT_PUBLIC_SUPABASE_ANON_KEY=...
|
||||||
|
- SUPABASE_SERVICE_ROLE_KEY=...
|
||||||
|
- NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=...
|
||||||
|
```
|
||||||
|
|
||||||
|
That's it! The editor will work as a standalone application without any cloud features.
|
||||||
|
|
||||||
|
## Backend Requirements
|
||||||
|
|
||||||
|
This feature requires:
|
||||||
|
- Supabase local development instance
|
||||||
|
- PostgreSQL database with the following tables:
|
||||||
|
- `users` - User accounts (Better Auth)
|
||||||
|
- `sessions` - Authentication sessions (Better Auth)
|
||||||
|
- `verification_tokens` - Magic link tokens (Better Auth)
|
||||||
|
- `properties` - Property records
|
||||||
|
- `properties_addresses` - Property addresses
|
||||||
|
- `properties_models` - Scene graph models
|
||||||
|
- Database migrations are managed in `packages/db/supabase/migrations/`
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
To work on this feature:
|
||||||
|
|
||||||
|
1. Install dependencies:
|
||||||
|
```bash
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
2. Start Supabase local development:
|
||||||
|
```bash
|
||||||
|
bun db:start
|
||||||
|
```
|
||||||
|
3. Run database migrations:
|
||||||
|
```bash
|
||||||
|
bun db:reset
|
||||||
|
```
|
||||||
|
4. Configure all environment variables in `apps/editor/.env.local`
|
||||||
|
5. Run the editor: `bun dev`
|
||||||
|
|
||||||
|
The editor will be available at `http://localhost:3000`.
|
||||||
|
|
||||||
|
For detailed setup instructions, see [SETUP.md](../../../SETUP.md) in the root directory.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- This feature uses **server actions** (Next.js App Router) for all database operations
|
||||||
|
- Authentication is handled by **Better Auth** with magic link support
|
||||||
|
- Better Auth server is configured in `packages/auth` and mounted at `/api/auth/*`
|
||||||
|
- The editor queries the database directly using Supabase with service role key
|
||||||
|
- IDs are generated using nanoid with custom alphabet
|
||||||
|
- Scene state is managed with a **Zustand store** for reliable property switching
|
||||||
|
- Scene changes are auto-saved with 2-second debouncing to the currently selected property
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { Cloud } from 'lucide-react'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { useAuth } from '../lib/auth/hooks'
|
||||||
|
import { usePropertyStore } from '../lib/properties/store'
|
||||||
|
import { ProfileDropdown } from './profile-dropdown'
|
||||||
|
import { PropertyDropdown } from './property-dropdown'
|
||||||
|
import { SignInDialog } from './sign-in-dialog'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CloudSaveButton - Shows authentication state and property management
|
||||||
|
*
|
||||||
|
* Not authenticated: Shows "Save to cloud" button
|
||||||
|
* Authenticated: Shows PropertyDropdown and ProfileDropdown
|
||||||
|
*/
|
||||||
|
export function CloudSaveButton() {
|
||||||
|
const { isAuthenticated, isLoading } = useAuth()
|
||||||
|
const [isSignInDialogOpen, setIsSignInDialogOpen] = useState(false)
|
||||||
|
const initialize = usePropertyStore(state => state.initialize)
|
||||||
|
|
||||||
|
// Initialize property store when authenticated
|
||||||
|
useEffect(() => {
|
||||||
|
if (isAuthenticated) {
|
||||||
|
initialize()
|
||||||
|
}
|
||||||
|
}, [isAuthenticated, initialize])
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="pointer-events-auto fixed top-4 right-4 z-50">
|
||||||
|
<div className="flex items-center gap-2 rounded-lg border border-border bg-background/95 px-3 py-2 shadow-lg backdrop-blur-md">
|
||||||
|
<div className="h-4 w-4 animate-pulse rounded-full bg-muted" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isAuthenticated) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="pointer-events-auto fixed top-4 right-4 z-50">
|
||||||
|
<button
|
||||||
|
className="flex items-center gap-2 rounded-lg border border-border bg-background/95 px-3 py-2 text-sm font-medium shadow-lg backdrop-blur-md transition-colors hover:bg-accent hover:text-accent-foreground"
|
||||||
|
onClick={() => setIsSignInDialogOpen(true)}
|
||||||
|
>
|
||||||
|
<Cloud className="h-4 w-4" />
|
||||||
|
Save to cloud
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<SignInDialog open={isSignInDialogOpen} onOpenChange={setIsSignInDialogOpen} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="pointer-events-auto fixed top-4 right-4 z-50">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<PropertyDropdown />
|
||||||
|
<ProfileDropdown />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { Autocomplete, LoadScript } from '@react-google-maps/api'
|
||||||
|
import { MapPin } from 'lucide-react'
|
||||||
|
import { useEffect, useRef, useState } from 'react'
|
||||||
|
|
||||||
|
const libraries: ('places')[] = ['places']
|
||||||
|
|
||||||
|
interface AddressComponents {
|
||||||
|
streetNumber?: string
|
||||||
|
route?: string
|
||||||
|
city?: string
|
||||||
|
state?: string
|
||||||
|
postalCode?: string
|
||||||
|
country?: string
|
||||||
|
center: [number, number]
|
||||||
|
formattedAddress: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GoogleAddressSearchProps {
|
||||||
|
onAddressSelect: (address: AddressComponents) => void
|
||||||
|
disabled?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GoogleAddressSearch({ onAddressSelect, disabled }: GoogleAddressSearchProps) {
|
||||||
|
const [autocomplete, setAutocomplete] = useState<google.maps.places.Autocomplete | null>(null)
|
||||||
|
const inputRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
|
const apiKey = process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY
|
||||||
|
|
||||||
|
// Fix Google Maps autocomplete dropdown z-index and pointer events to work with dialog
|
||||||
|
useEffect(() => {
|
||||||
|
const style = document.createElement('style')
|
||||||
|
style.textContent = `
|
||||||
|
.pac-container {
|
||||||
|
z-index: 9999 !important;
|
||||||
|
pointer-events: auto !important;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
document.head.appendChild(style)
|
||||||
|
return () => {
|
||||||
|
document.head.removeChild(style)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!apiKey) {
|
||||||
|
return (
|
||||||
|
<div className="rounded-md border border-destructive/50 bg-destructive/10 p-3 text-destructive text-sm">
|
||||||
|
<p className="font-medium">Google Maps API Key Missing</p>
|
||||||
|
<p className="mt-1 text-xs">
|
||||||
|
Add NEXT_PUBLIC_GOOGLE_MAPS_API_KEY to your .env.local file
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onLoad = (autocompleteInstance: google.maps.places.Autocomplete) => {
|
||||||
|
setAutocomplete(autocompleteInstance)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onPlaceChanged = () => {
|
||||||
|
if (!autocomplete) return
|
||||||
|
|
||||||
|
const place = autocomplete.getPlace()
|
||||||
|
if (!place.geometry?.location || !place.address_components) return
|
||||||
|
|
||||||
|
const components: AddressComponents = {
|
||||||
|
center: [place.geometry.location.lng(), place.geometry.location.lat()],
|
||||||
|
formattedAddress: place.formatted_address || '',
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse address components
|
||||||
|
for (const component of place.address_components) {
|
||||||
|
const types = component.types
|
||||||
|
|
||||||
|
if (types.includes('street_number')) {
|
||||||
|
components.streetNumber = component.long_name
|
||||||
|
} else if (types.includes('route')) {
|
||||||
|
components.route = component.long_name
|
||||||
|
} else if (types.includes('locality')) {
|
||||||
|
components.city = component.long_name
|
||||||
|
} else if (types.includes('administrative_area_level_1')) {
|
||||||
|
components.state = component.short_name
|
||||||
|
} else if (types.includes('postal_code')) {
|
||||||
|
components.postalCode = component.long_name
|
||||||
|
} else if (types.includes('country')) {
|
||||||
|
components.country = component.short_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAddressSelect(components)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LoadScript googleMapsApiKey={apiKey} libraries={libraries}>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="flex items-center gap-2 font-medium text-sm">
|
||||||
|
<MapPin className="h-4 w-4" />
|
||||||
|
Property Address
|
||||||
|
</label>
|
||||||
|
<Autocomplete onLoad={onLoad} onPlaceChanged={onPlaceChanged}>
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
disabled={disabled}
|
||||||
|
placeholder="Search for an address..."
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
</Autocomplete>
|
||||||
|
</div>
|
||||||
|
</LoadScript>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { X } from 'lucide-react'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { createProperty } from '../lib/properties/actions'
|
||||||
|
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/primitives/dialog'
|
||||||
|
import { GoogleAddressSearch } from './google-address-search'
|
||||||
|
|
||||||
|
interface NewPropertyDialogProps {
|
||||||
|
open: boolean
|
||||||
|
onOpenChange: (open: boolean) => void
|
||||||
|
onSuccess?: (propertyId: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AddressData {
|
||||||
|
streetNumber?: string
|
||||||
|
route?: string
|
||||||
|
city?: string
|
||||||
|
state?: string
|
||||||
|
postalCode?: string
|
||||||
|
country?: string
|
||||||
|
center: [number, number]
|
||||||
|
formattedAddress: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NewPropertyDialog - Dialog for creating a new property with Google Maps address search
|
||||||
|
*/
|
||||||
|
export function NewPropertyDialog({ open, onOpenChange, onSuccess }: NewPropertyDialogProps) {
|
||||||
|
const [address, setAddress] = useState<AddressData | null>(null)
|
||||||
|
const [isCreating, setIsCreating] = useState(false)
|
||||||
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
|
||||||
|
const handleAddressSelect = (addressData: AddressData) => {
|
||||||
|
setAddress(addressData)
|
||||||
|
setError(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
setError(null)
|
||||||
|
|
||||||
|
if (!address) {
|
||||||
|
setError('Please select an address')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsCreating(true)
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Use formatted address as property name (like monorepo)
|
||||||
|
const result = await createProperty({
|
||||||
|
name: address.formattedAddress,
|
||||||
|
center: address.center,
|
||||||
|
streetNumber: address.streetNumber,
|
||||||
|
route: address.route,
|
||||||
|
city: address.city,
|
||||||
|
state: address.state,
|
||||||
|
postalCode: address.postalCode,
|
||||||
|
country: address.country || 'US',
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result.success && result.data) {
|
||||||
|
onOpenChange(false)
|
||||||
|
setAddress(null)
|
||||||
|
onSuccess?.(result.data.id)
|
||||||
|
} else {
|
||||||
|
setError(result.error || 'Failed to create property')
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setError(err instanceof Error ? err.message : 'An unexpected error occurred')
|
||||||
|
} finally {
|
||||||
|
setIsCreating(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
if (!isCreating) {
|
||||||
|
onOpenChange(false)
|
||||||
|
setAddress(null)
|
||||||
|
setError(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={handleClose} modal={false}>
|
||||||
|
<DialogContent
|
||||||
|
className="sm:max-w-[500px]"
|
||||||
|
onInteractOutside={(e) => e.preventDefault()}
|
||||||
|
>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Add New Property</DialogTitle>
|
||||||
|
<button
|
||||||
|
className="absolute top-4 right-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 disabled:pointer-events-none"
|
||||||
|
disabled={isCreating}
|
||||||
|
onClick={handleClose}
|
||||||
|
>
|
||||||
|
<X className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Close</span>
|
||||||
|
</button>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<form className="space-y-4" onSubmit={handleSubmit}>
|
||||||
|
{/* Google Maps Address Search */}
|
||||||
|
<GoogleAddressSearch onAddressSelect={handleAddressSelect} disabled={isCreating} />
|
||||||
|
|
||||||
|
{/* Show selected address */}
|
||||||
|
{address && (
|
||||||
|
<div className="rounded-md border border-border bg-muted/30 p-3 text-sm">
|
||||||
|
<p className="font-medium">Selected Address:</p>
|
||||||
|
<p className="mt-1 text-muted-foreground">{address.formattedAddress}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<div className="rounded-md border border-destructive/50 bg-destructive/10 p-3 text-destructive text-sm">
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex justify-end gap-2">
|
||||||
|
<button
|
||||||
|
className="rounded-md border border-input px-4 py-2 text-sm transition-colors hover:bg-accent hover:text-accent-foreground disabled:opacity-50"
|
||||||
|
disabled={isCreating}
|
||||||
|
type="button"
|
||||||
|
onClick={handleClose}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="rounded-md bg-primary px-4 py-2 text-primary-foreground text-sm transition-colors hover:bg-primary/90 disabled:opacity-50"
|
||||||
|
disabled={isCreating || !address}
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
{isCreating ? 'Creating...' : 'Create Property'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useAuth } from '../lib/auth/hooks'
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from '@/components/ui/primitives/dropdown-menu'
|
||||||
|
|
||||||
|
function getInitials(name: string): string {
|
||||||
|
return name
|
||||||
|
.split(' ')
|
||||||
|
.map((n) => n[0])
|
||||||
|
.join('')
|
||||||
|
.toUpperCase()
|
||||||
|
.slice(0, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProfileDropdown - User profile menu with sign out
|
||||||
|
*/
|
||||||
|
export function ProfileDropdown() {
|
||||||
|
const { user, signOut } = useAuth()
|
||||||
|
|
||||||
|
const handleSignOut = async () => {
|
||||||
|
await signOut()
|
||||||
|
// TODO: Show sign-in dialog or redirect
|
||||||
|
console.log('Signed out')
|
||||||
|
}
|
||||||
|
|
||||||
|
const initials = user?.name ? getInitials(user.name) : user?.email?.[0]?.toUpperCase() || 'U'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<button
|
||||||
|
className="flex h-9 w-9 items-center justify-center rounded-lg border border-border bg-background/95 font-medium text-sm shadow-lg backdrop-blur-md transition-colors hover:bg-accent hover:text-accent-foreground focus:outline-none"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
{initials}
|
||||||
|
</button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end" className="w-48">
|
||||||
|
{user?.name && (
|
||||||
|
<div className="px-2 py-1.5 text-sm">
|
||||||
|
<div className="font-medium">{user.name}</div>
|
||||||
|
{user.email && <div className="text-muted-foreground text-xs">{user.email}</div>}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{user?.name && <DropdownMenuItem className="h-px bg-border" />}
|
||||||
|
<DropdownMenuItem className="cursor-pointer" variant="destructive" onClick={handleSignOut}>
|
||||||
|
Sign out
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { Check, ChevronDown, Home, Plus } from 'lucide-react'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { usePropertyStore } from '../lib/properties/store'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from '@/components/ui/primitives/dropdown-menu'
|
||||||
|
import { NewPropertyDialog } from './new-property-dialog'
|
||||||
|
import { usePropertyScene } from '../lib/models/hooks'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PropertyDropdown - Shows active property and allows switching between properties
|
||||||
|
*/
|
||||||
|
export function PropertyDropdown() {
|
||||||
|
usePropertyScene() // Load and auto-save property scenes
|
||||||
|
|
||||||
|
// Use property store
|
||||||
|
const properties = usePropertyStore(state => state.properties)
|
||||||
|
const activeProperty = usePropertyStore(state => state.activeProperty)
|
||||||
|
const isLoading = usePropertyStore(state => state.isLoading)
|
||||||
|
const setActiveProperty = usePropertyStore(state => state.setActiveProperty)
|
||||||
|
const fetchProperties = usePropertyStore(state => state.fetchProperties)
|
||||||
|
|
||||||
|
const [isNewPropertyDialogOpen, setIsNewPropertyDialogOpen] = useState(false)
|
||||||
|
|
||||||
|
const handlePropertySelect = async (propertyId: string) => {
|
||||||
|
await setActiveProperty(propertyId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAddNew = () => {
|
||||||
|
setIsNewPropertyDialogOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePropertyCreated = async (propertyId: string) => {
|
||||||
|
// Set the newly created property as active (this will also fetch properties)
|
||||||
|
await setActiveProperty(propertyId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<button
|
||||||
|
className="flex h-9 items-center gap-2 rounded-lg border border-border bg-background/95 px-3 text-sm shadow-lg backdrop-blur-md transition-colors hover:bg-accent hover:text-accent-foreground disabled:opacity-50 focus:outline-none"
|
||||||
|
disabled={isLoading}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<Home className="h-4 w-4" />
|
||||||
|
<span className="max-w-[150px] truncate">
|
||||||
|
{activeProperty
|
||||||
|
? activeProperty.name
|
||||||
|
: properties.length > 0
|
||||||
|
? 'Select Property'
|
||||||
|
: 'Add Property'}
|
||||||
|
</span>
|
||||||
|
<ChevronDown className="h-3 w-3 opacity-50" />
|
||||||
|
</button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end" className="w-[280px]">
|
||||||
|
{/* Property list */}
|
||||||
|
{properties.length > 0 ? (
|
||||||
|
<div className="max-h-[300px] overflow-y-auto">
|
||||||
|
{properties.map((property) => (
|
||||||
|
<DropdownMenuItem
|
||||||
|
className={cn(
|
||||||
|
'cursor-pointer text-sm',
|
||||||
|
activeProperty?.id === property.id && 'cursor-default bg-accent',
|
||||||
|
)}
|
||||||
|
key={property.id}
|
||||||
|
onClick={() =>
|
||||||
|
activeProperty?.id === property.id ? null : handlePropertySelect(property.id)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="flex w-full items-center justify-between gap-2">
|
||||||
|
<div className="flex-1 truncate font-medium">{property.name}</div>
|
||||||
|
{activeProperty?.id === property.id && (
|
||||||
|
<Check className="h-4 w-4 shrink-0 text-primary" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="px-2 py-3 text-center text-muted-foreground text-sm">
|
||||||
|
No properties yet
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Add new property option */}
|
||||||
|
<DropdownMenuItem className="cursor-pointer" onClick={handleAddNew}>
|
||||||
|
<Plus className="mr-2 h-4 w-4" />
|
||||||
|
<span>Add new property</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
|
||||||
|
<NewPropertyDialog
|
||||||
|
open={isNewPropertyDialogOpen}
|
||||||
|
onOpenChange={setIsNewPropertyDialogOpen}
|
||||||
|
onSuccess={handlePropertyCreated}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { Mail, X } from 'lucide-react'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { authClient } from '../lib/auth/client'
|
||||||
|
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/primitives/dialog'
|
||||||
|
|
||||||
|
interface SignInDialogProps {
|
||||||
|
open: boolean
|
||||||
|
onOpenChange: (open: boolean) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SignInDialog - Magic link authentication dialog
|
||||||
|
*/
|
||||||
|
export function SignInDialog({ open, onOpenChange }: SignInDialogProps) {
|
||||||
|
const [email, setEmail] = useState('')
|
||||||
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
const [success, setSuccess] = useState(false)
|
||||||
|
|
||||||
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
setError(null)
|
||||||
|
setIsLoading(true)
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Use better-auth's magic link sign in
|
||||||
|
const result = await authClient.signIn.magicLink({
|
||||||
|
email,
|
||||||
|
callbackURL: window.location.origin,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
setError(result.error.message || 'Failed to send magic link')
|
||||||
|
} else {
|
||||||
|
setSuccess(true)
|
||||||
|
setEmail('')
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setError(err instanceof Error ? err.message : 'An unexpected error occurred')
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
if (!isLoading) {
|
||||||
|
onOpenChange(false)
|
||||||
|
// Reset state after a short delay to avoid flash
|
||||||
|
setTimeout(() => {
|
||||||
|
setEmail('')
|
||||||
|
setError(null)
|
||||||
|
setSuccess(false)
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={handleClose}>
|
||||||
|
<DialogContent className="sm:max-w-[400px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Sign in to Pascal</DialogTitle>
|
||||||
|
<button
|
||||||
|
className="absolute top-4 right-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 disabled:pointer-events-none"
|
||||||
|
disabled={isLoading}
|
||||||
|
onClick={handleClose}
|
||||||
|
>
|
||||||
|
<X className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Close</span>
|
||||||
|
</button>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
{success ? (
|
||||||
|
<div className="space-y-4 py-4">
|
||||||
|
<div className="flex flex-col items-center gap-4 text-center">
|
||||||
|
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-green-100 dark:bg-green-900/20">
|
||||||
|
<Mail className="h-6 w-6 text-green-600 dark:text-green-400" />
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<h3 className="font-semibold text-lg">Check your email</h3>
|
||||||
|
<p className="text-muted-foreground text-sm">
|
||||||
|
We've sent a magic link to <strong>{email}</strong>
|
||||||
|
</p>
|
||||||
|
<p className="text-muted-foreground text-sm">
|
||||||
|
Click the link in the email to sign in to your account.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className="w-full rounded-md border border-input px-4 py-2 text-sm transition-colors hover:bg-accent hover:text-accent-foreground"
|
||||||
|
onClick={handleClose}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<form className="space-y-4" onSubmit={handleSubmit}>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="font-medium text-sm" htmlFor="email">
|
||||||
|
Email address
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
autoComplete="email"
|
||||||
|
className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
disabled={isLoading}
|
||||||
|
id="email"
|
||||||
|
placeholder="you@example.com"
|
||||||
|
required
|
||||||
|
type="email"
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<div className="rounded-md border border-destructive/50 bg-destructive/10 p-3 text-destructive text-sm">
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="flex w-full items-center justify-center gap-2 rounded-md bg-primary px-4 py-2 text-primary-foreground text-sm transition-colors hover:bg-primary/90 disabled:opacity-50"
|
||||||
|
disabled={isLoading || !email}
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
{isLoading ? (
|
||||||
|
<>
|
||||||
|
<div className="h-4 w-4 animate-spin rounded-full border-2 border-primary-foreground border-t-transparent" />
|
||||||
|
Sending magic link...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Mail className="h-4 w-4" />
|
||||||
|
Send magic link
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<p className="text-center text-muted-foreground text-xs">
|
||||||
|
We'll send you a magic link to sign in without a password.
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* Auth client for the editor using better-auth
|
||||||
|
* Re-exports from @pascal-app/auth package
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { authClient } from '@pascal-app/auth/client'
|
||||||
|
export type { AuthState, User, Session } from '@pascal-app/auth/client'
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { authClient } from './client'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook to access authentication state using better-auth
|
||||||
|
* @returns Current auth state including user, session, and loading status
|
||||||
|
*/
|
||||||
|
export function useAuth() {
|
||||||
|
const session = authClient.useSession()
|
||||||
|
|
||||||
|
return {
|
||||||
|
user: session.data?.user ?? null,
|
||||||
|
session: session.data?.session ?? null,
|
||||||
|
isAuthenticated: !!session.data?.user && !!session.data?.session,
|
||||||
|
isLoading: session.isPending,
|
||||||
|
signOut: () => authClient.signOut(),
|
||||||
|
signIn: authClient.signIn,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { headers as nextHeaders } from 'next/headers'
|
||||||
|
|
||||||
|
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current session from Better Auth backend (server-side)
|
||||||
|
*/
|
||||||
|
export async function getSession() {
|
||||||
|
try {
|
||||||
|
const headersList = await nextHeaders()
|
||||||
|
|
||||||
|
// Make authenticated request to the auth backend to get session
|
||||||
|
const response = await fetch(`${API_URL}/api/auth/get-session`, {
|
||||||
|
headers: {
|
||||||
|
cookie: headersList.get('cookie') || '',
|
||||||
|
},
|
||||||
|
credentials: 'include',
|
||||||
|
cache: 'no-store',
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json()
|
||||||
|
|
||||||
|
// Better Auth returns the session data directly
|
||||||
|
if (data?.user && data?.session) {
|
||||||
|
return {
|
||||||
|
user: data.user,
|
||||||
|
session: data.session,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to get session:', error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current user from the session
|
||||||
|
*/
|
||||||
|
export async function getUser() {
|
||||||
|
const session = await getSession()
|
||||||
|
return session?.user ?? null
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Supabase server client for database access
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { supabaseAdmin } from '@/lib/supabase/server'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Supabase client for server-side use with service role key
|
||||||
|
* This bypasses RLS and allows server actions to query the database directly
|
||||||
|
* Authentication is handled by Better Auth, permissions enforced by filtering on user_id
|
||||||
|
*/
|
||||||
|
export async function createServerSupabaseClient() {
|
||||||
|
return supabaseAdmin
|
||||||
|
}
|
||||||
@@ -0,0 +1,242 @@
|
|||||||
|
/**
|
||||||
|
* Property model actions - Server actions for scene loading/saving
|
||||||
|
* Manages 3D models (scene graphs) stored in properties_models table
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use server'
|
||||||
|
|
||||||
|
import type { AnyNode, AnyNodeId } from '@pascal-app/core'
|
||||||
|
import { createServerSupabaseClient } from '../database/server'
|
||||||
|
import { getSession } from '../auth/server'
|
||||||
|
import { createId } from '../utils/id-generator'
|
||||||
|
import type { ActionResult } from '../properties/actions'
|
||||||
|
|
||||||
|
export interface SceneGraph {
|
||||||
|
nodes: Record<AnyNodeId, AnyNode>
|
||||||
|
rootNodeIds: AnyNodeId[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PropertyModel {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
version: number
|
||||||
|
draft: boolean
|
||||||
|
property_id: string
|
||||||
|
scene_graph: SceneGraph | null
|
||||||
|
created_at: string
|
||||||
|
updated_at: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the latest model for a property (highest version)
|
||||||
|
*/
|
||||||
|
export async function getPropertyModel(propertyId: string): Promise<ActionResult<PropertyModel | null>> {
|
||||||
|
try {
|
||||||
|
const session = await getSession()
|
||||||
|
|
||||||
|
if (!session?.user) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'Not authenticated',
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const supabase = await createServerSupabaseClient()
|
||||||
|
|
||||||
|
// Get the property to verify ownership
|
||||||
|
const { data: property, error: propertyError } = await supabase
|
||||||
|
.from('properties')
|
||||||
|
.select('id, owner_id')
|
||||||
|
.eq('id', propertyId)
|
||||||
|
.single<{ id: string; owner_id: string }>()
|
||||||
|
|
||||||
|
if (propertyError || !property) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'Property not found',
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify ownership
|
||||||
|
if (property.owner_id !== session.user.id) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'Unauthorized',
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the latest model (highest version, then most recent)
|
||||||
|
const { data: model, error: modelError } = await supabase
|
||||||
|
.from('properties_models')
|
||||||
|
.select('*')
|
||||||
|
.eq('property_id', propertyId)
|
||||||
|
.is('deleted_at', null)
|
||||||
|
.order('version', { ascending: false })
|
||||||
|
.order('created_at', { ascending: false })
|
||||||
|
.limit(1)
|
||||||
|
.single<PropertyModel>()
|
||||||
|
|
||||||
|
console.log('[getPropertyModel] Query result:', {
|
||||||
|
propertyId,
|
||||||
|
hasModel: !!model,
|
||||||
|
modelError: modelError?.message,
|
||||||
|
errorCode: modelError?.code,
|
||||||
|
modelKeys: model ? Object.keys(model) : [],
|
||||||
|
})
|
||||||
|
|
||||||
|
if (modelError) {
|
||||||
|
// No model found is not an error - just return null
|
||||||
|
if (modelError.code === 'PGRST116') {
|
||||||
|
console.log('[getPropertyModel] No model found (PGRST116), returning null')
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[getPropertyModel] Database error:', modelError)
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: modelError.message,
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[getPropertyModel] Model found:', {
|
||||||
|
id: model.id,
|
||||||
|
version: model.version,
|
||||||
|
hasSceneGraph: !!model.scene_graph,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: model as PropertyModel,
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : 'Failed to fetch property model',
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save or update a property model's scene graph
|
||||||
|
* If a model exists, updates it. Otherwise creates a new one.
|
||||||
|
*/
|
||||||
|
export async function savePropertyModel(
|
||||||
|
propertyId: string,
|
||||||
|
sceneGraph: SceneGraph,
|
||||||
|
): Promise<ActionResult<PropertyModel>> {
|
||||||
|
try {
|
||||||
|
const session = await getSession()
|
||||||
|
|
||||||
|
if (!session?.user) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'Not authenticated',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const supabase = await createServerSupabaseClient()
|
||||||
|
|
||||||
|
// Get the property to verify ownership
|
||||||
|
const { data: property, error: propertyError } = await supabase
|
||||||
|
.from('properties')
|
||||||
|
.select('id, owner_id, name')
|
||||||
|
.eq('id', propertyId)
|
||||||
|
.single<{ id: string; owner_id: string; name: string }>()
|
||||||
|
|
||||||
|
if (propertyError || !property) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'Property not found',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify ownership
|
||||||
|
if (property.owner_id !== session.user.id) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'Unauthorized',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a model already exists
|
||||||
|
const { data: existingModel } = await supabase
|
||||||
|
.from('properties_models')
|
||||||
|
.select('id, version')
|
||||||
|
.eq('property_id', propertyId)
|
||||||
|
.is('deleted_at', null)
|
||||||
|
.order('version', { ascending: false })
|
||||||
|
.order('created_at', { ascending: false })
|
||||||
|
.limit(1)
|
||||||
|
.single<{ id: string; version: number }>()
|
||||||
|
|
||||||
|
if (existingModel) {
|
||||||
|
// Update existing model
|
||||||
|
const updateData = {
|
||||||
|
scene_graph: sceneGraph,
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
|
}
|
||||||
|
const { data: updatedModel, error: updateError } = (await (supabase
|
||||||
|
.from('properties_models') as any)
|
||||||
|
.update(updateData)
|
||||||
|
.eq('id', existingModel.id)
|
||||||
|
.select()
|
||||||
|
.single()) as { data: PropertyModel | null; error: any }
|
||||||
|
|
||||||
|
if (updateError) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: updateError.message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: updatedModel as PropertyModel,
|
||||||
|
message: 'Model updated successfully',
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Create new model
|
||||||
|
const modelId = createId('model')
|
||||||
|
|
||||||
|
const insertData = {
|
||||||
|
id: modelId,
|
||||||
|
property_id: propertyId,
|
||||||
|
name: `${property.name} - Editor`,
|
||||||
|
version: 1,
|
||||||
|
draft: true,
|
||||||
|
scene_graph: sceneGraph,
|
||||||
|
}
|
||||||
|
const { data: newModel, error: createError } = (await (supabase
|
||||||
|
.from('properties_models') as any)
|
||||||
|
.insert(insertData)
|
||||||
|
.select()
|
||||||
|
.single()) as { data: PropertyModel | null; error: any }
|
||||||
|
|
||||||
|
if (createError) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: createError.message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: newModel as PropertyModel,
|
||||||
|
message: 'Model created successfully',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : 'Failed to save property model',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
/**
|
||||||
|
* Hooks for property model (scene) loading and auto-saving
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useScene } from '@pascal-app/core'
|
||||||
|
import { useViewer } from '@pascal-app/viewer'
|
||||||
|
import { useEffect, useRef } from 'react'
|
||||||
|
import useEditor from '@/store/use-editor'
|
||||||
|
import { usePropertyStore } from '../properties/store'
|
||||||
|
import { getPropertyModel, savePropertyModel } from './actions'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the scene when a property becomes active
|
||||||
|
* Saves changes automatically with debouncing
|
||||||
|
*/
|
||||||
|
export function usePropertyScene() {
|
||||||
|
// Subscribe to property store
|
||||||
|
const activeProperty = usePropertyStore((state) => state.activeProperty)
|
||||||
|
const isLoadingProperty = usePropertyStore((state) => state.isLoading)
|
||||||
|
|
||||||
|
const lastPropertyIdRef = useRef<string | null>(null)
|
||||||
|
const saveTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined)
|
||||||
|
const isSavingRef = useRef(false)
|
||||||
|
const currentPropertyIdRef = useRef<string | null>(null)
|
||||||
|
|
||||||
|
// Extract property ID for dependency tracking
|
||||||
|
const propertyId = activeProperty?.id ?? null
|
||||||
|
const propertyName = activeProperty?.name ?? null
|
||||||
|
|
||||||
|
// Load scene when active property changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (isLoadingProperty) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!propertyId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if same property
|
||||||
|
if (lastPropertyIdRef.current === propertyId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lastPropertyIdRef.current = propertyId
|
||||||
|
|
||||||
|
// Load the property's scene
|
||||||
|
async function loadScene() {
|
||||||
|
try {
|
||||||
|
const result = await getPropertyModel(propertyId || '')
|
||||||
|
|
||||||
|
if (result.success && result.data?.scene_graph) {
|
||||||
|
// Load the scene graph into the store
|
||||||
|
const { nodes, rootNodeIds } = result.data.scene_graph
|
||||||
|
useScene.getState().setScene(nodes, rootNodeIds)
|
||||||
|
} else {
|
||||||
|
// No scene found - clear the scene
|
||||||
|
useScene.getState().clearScene()
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Fall back to clear scene
|
||||||
|
useScene.getState().clearScene()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset editor state after loading/clearing scene
|
||||||
|
useEditor.getState().setPhase('site')
|
||||||
|
useViewer.getState().setSelection({
|
||||||
|
buildingId: null,
|
||||||
|
levelId: null,
|
||||||
|
selectedIds: [],
|
||||||
|
zoneId: null,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
loadScene()
|
||||||
|
}, [propertyId, isLoadingProperty])
|
||||||
|
|
||||||
|
// Auto-save scene changes with debouncing
|
||||||
|
useEffect(() => {
|
||||||
|
if (!propertyId) {
|
||||||
|
currentPropertyIdRef.current = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPropertyIdRef.current = propertyId
|
||||||
|
|
||||||
|
// Subscribe to any scene changes
|
||||||
|
// Use JSON stringification to detect any node changes, not just count
|
||||||
|
let lastNodesSnapshot = JSON.stringify(useScene.getState().nodes)
|
||||||
|
|
||||||
|
const unsubscribe = useScene.subscribe((state) => {
|
||||||
|
const currentNodesSnapshot = JSON.stringify(state.nodes)
|
||||||
|
|
||||||
|
// Only trigger save if nodes actually changed
|
||||||
|
if (currentNodesSnapshot === lastNodesSnapshot) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lastNodesSnapshot = currentNodesSnapshot
|
||||||
|
const nodes = state.nodes
|
||||||
|
|
||||||
|
// Skip if currently saving
|
||||||
|
if (isSavingRef.current) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear existing timeout
|
||||||
|
if (saveTimeoutRef.current) {
|
||||||
|
clearTimeout(saveTimeoutRef.current)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debounce save by 2 seconds
|
||||||
|
saveTimeoutRef.current = setTimeout(async () => {
|
||||||
|
// Get the current property ID at save time (not the captured value)
|
||||||
|
const currentPropertyId = currentPropertyIdRef.current
|
||||||
|
if (!currentPropertyId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const rootNodeIds = useScene.getState().rootNodeIds
|
||||||
|
const sceneGraph = { nodes, rootNodeIds }
|
||||||
|
|
||||||
|
isSavingRef.current = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
await savePropertyModel(currentPropertyId, sceneGraph)
|
||||||
|
} finally {
|
||||||
|
isSavingRef.current = false
|
||||||
|
}
|
||||||
|
}, 2000)
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (saveTimeoutRef.current) {
|
||||||
|
clearTimeout(saveTimeoutRef.current)
|
||||||
|
}
|
||||||
|
unsubscribe()
|
||||||
|
}
|
||||||
|
}, [propertyId])
|
||||||
|
}
|
||||||
@@ -0,0 +1,328 @@
|
|||||||
|
/**
|
||||||
|
* Property actions - Server actions for property management
|
||||||
|
* Uses Better Auth session + Supabase to query the same database as the monorepo
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use server'
|
||||||
|
|
||||||
|
import { createServerSupabaseClient } from '../database/server'
|
||||||
|
import { getSession } from '../auth/server'
|
||||||
|
import { createId } from '../utils/id-generator'
|
||||||
|
import type { CreatePropertyParams, Property } from './types'
|
||||||
|
|
||||||
|
export type ActionResult<T = unknown> = {
|
||||||
|
success: boolean
|
||||||
|
data?: T
|
||||||
|
error?: string
|
||||||
|
message?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all properties for the current user
|
||||||
|
*/
|
||||||
|
export async function getUserProperties(): Promise<ActionResult<Property[]>> {
|
||||||
|
try {
|
||||||
|
const session = await getSession()
|
||||||
|
|
||||||
|
if (!session?.user) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'Not authenticated',
|
||||||
|
data: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const supabase = await createServerSupabaseClient()
|
||||||
|
|
||||||
|
// Query properties table with address relation
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('properties')
|
||||||
|
.select(`
|
||||||
|
*,
|
||||||
|
address:properties_addresses(*)
|
||||||
|
`)
|
||||||
|
.eq('owner_id', session.user.id)
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
data: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: data as Property[],
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : 'Failed to fetch properties',
|
||||||
|
data: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the active property for the current session
|
||||||
|
*/
|
||||||
|
export async function getActiveProperty(): Promise<ActionResult<Property | null>> {
|
||||||
|
try {
|
||||||
|
const session = await getSession()
|
||||||
|
|
||||||
|
if (!session?.user) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'Not authenticated',
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const supabase = await createServerSupabaseClient()
|
||||||
|
|
||||||
|
// Get session's active_property_id from sessions table
|
||||||
|
const { data: sessionData, error: sessionError } = await supabase
|
||||||
|
.from('auth_sessions')
|
||||||
|
.select('active_property_id')
|
||||||
|
.eq('user_id', session.user.id)
|
||||||
|
.single<{ active_property_id: string | null }>()
|
||||||
|
|
||||||
|
if (sessionError || !sessionData?.active_property_id) {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the property with address
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('properties')
|
||||||
|
.select(`
|
||||||
|
*,
|
||||||
|
address:properties_addresses(*)
|
||||||
|
`)
|
||||||
|
.eq('id', sessionData.active_property_id)
|
||||||
|
.single<Property>()
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: data as Property,
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : 'Failed to fetch active property',
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the active property for the current session
|
||||||
|
*/
|
||||||
|
export async function setActiveProperty(propertyId: string | null): Promise<ActionResult> {
|
||||||
|
try {
|
||||||
|
const session = await getSession()
|
||||||
|
|
||||||
|
if (!session?.user) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'Not authenticated',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const supabase = await createServerSupabaseClient()
|
||||||
|
|
||||||
|
// Update session's active_property_id
|
||||||
|
const { error } = await (supabase
|
||||||
|
.from('auth_sessions') as any)
|
||||||
|
.update({ active_property_id: propertyId })
|
||||||
|
.eq('user_id', session.user.id)
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: propertyId ? 'Active property updated' : 'Active property cleared',
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : 'Failed to set active property',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new property
|
||||||
|
*/
|
||||||
|
export async function createProperty(params: CreatePropertyParams): Promise<ActionResult<Property>> {
|
||||||
|
try {
|
||||||
|
const session = await getSession()
|
||||||
|
|
||||||
|
if (!session?.user) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'Not authenticated',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const supabase = await createServerSupabaseClient()
|
||||||
|
|
||||||
|
// Generate IDs for address and property
|
||||||
|
const addressId = createId('address')
|
||||||
|
const propertyId = createId('property')
|
||||||
|
|
||||||
|
// First, create the address
|
||||||
|
const addressData = {
|
||||||
|
id: addressId,
|
||||||
|
street_number: params.streetNumber,
|
||||||
|
route: params.route,
|
||||||
|
city: params.city || '',
|
||||||
|
state: params.state || '',
|
||||||
|
postal_code: params.postalCode || '',
|
||||||
|
country: params.country || 'US',
|
||||||
|
latitude: params.center[1].toString(),
|
||||||
|
longitude: params.center[0].toString(),
|
||||||
|
}
|
||||||
|
const { data: address, error: addressError } = (await (supabase
|
||||||
|
.from('properties_addresses') as any)
|
||||||
|
.insert(addressData)
|
||||||
|
.select()
|
||||||
|
.single()) as { data: Property['address'] | null; error: any }
|
||||||
|
|
||||||
|
if (addressError || !address) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: addressError?.message || 'Failed to create address',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the property
|
||||||
|
const propertyData = {
|
||||||
|
id: propertyId,
|
||||||
|
name: params.name,
|
||||||
|
address_id: address.id,
|
||||||
|
owner_id: session.user.id,
|
||||||
|
details_json: {
|
||||||
|
coordinates: params.center,
|
||||||
|
createdFrom: 'editor-app',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const { data, error } = (await (supabase
|
||||||
|
.from('properties') as any)
|
||||||
|
.insert(propertyData)
|
||||||
|
.select(`
|
||||||
|
*,
|
||||||
|
address:properties_addresses(*)
|
||||||
|
`)
|
||||||
|
.single()) as { data: Property | null; error: any }
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: data as Property,
|
||||||
|
message: 'Property created successfully',
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : 'Failed to create property',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a property with the given address already exists
|
||||||
|
*/
|
||||||
|
export async function checkPropertyDuplicate(params: {
|
||||||
|
streetNumber?: string
|
||||||
|
route?: string
|
||||||
|
city?: string
|
||||||
|
state?: string
|
||||||
|
postalCode?: string
|
||||||
|
}): Promise<
|
||||||
|
ActionResult<{
|
||||||
|
isDuplicate: boolean
|
||||||
|
isUserProperty?: boolean
|
||||||
|
existingProperty?: Property
|
||||||
|
}>
|
||||||
|
> {
|
||||||
|
try {
|
||||||
|
const session = await getSession()
|
||||||
|
|
||||||
|
if (!session?.user) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'Not authenticated',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const supabase = await createServerSupabaseClient()
|
||||||
|
|
||||||
|
// Query for existing property with matching address
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('properties')
|
||||||
|
.select(`
|
||||||
|
*,
|
||||||
|
address:properties_addresses!inner(*)
|
||||||
|
`)
|
||||||
|
.eq('address.street_number', params.streetNumber || '')
|
||||||
|
.eq('address.route', params.route || '')
|
||||||
|
.eq('address.city', params.city || '')
|
||||||
|
.eq('address.state', params.state || '')
|
||||||
|
.eq('address.postal_code', params.postalCode || '')
|
||||||
|
.limit(1)
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data && data.length > 0) {
|
||||||
|
const existingProperty = data[0] as unknown as Property
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
isDuplicate: true,
|
||||||
|
isUserProperty: existingProperty.owner_id === session.user.id,
|
||||||
|
existingProperty,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
isDuplicate: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : 'Failed to check for duplicates',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
|
import {
|
||||||
|
getActiveProperty,
|
||||||
|
getUserProperties,
|
||||||
|
setActiveProperty as setActivePropertyAction,
|
||||||
|
} from './actions'
|
||||||
|
import type { Property } from './types'
|
||||||
|
|
||||||
|
interface UsePropertiesReturn {
|
||||||
|
properties: Property[]
|
||||||
|
isLoading: boolean
|
||||||
|
error: string | null
|
||||||
|
refetch: () => Promise<void>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook to fetch and manage user properties
|
||||||
|
*/
|
||||||
|
export function useProperties(): UsePropertiesReturn {
|
||||||
|
const [properties, setProperties] = useState<Property[]>([])
|
||||||
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
|
||||||
|
const fetchProperties = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true)
|
||||||
|
setError(null)
|
||||||
|
|
||||||
|
const result = await getUserProperties()
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
setProperties(result.data || [])
|
||||||
|
} else {
|
||||||
|
setError(result.error || 'Failed to fetch properties')
|
||||||
|
setProperties([])
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setError(err instanceof Error ? err.message : 'An unexpected error occurred')
|
||||||
|
setProperties([])
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchProperties()
|
||||||
|
}, [fetchProperties])
|
||||||
|
|
||||||
|
return {
|
||||||
|
properties,
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
refetch: fetchProperties,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook to fetch a single property by ID
|
||||||
|
*/
|
||||||
|
export function useProperty(propertyId: string | undefined) {
|
||||||
|
const [property, setProperty] = useState<Property | null>(null)
|
||||||
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!propertyId) {
|
||||||
|
setProperty(null)
|
||||||
|
setIsLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchProperty = async () => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true)
|
||||||
|
setError(null)
|
||||||
|
|
||||||
|
const result = await getUserProperties()
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
const found = result.data?.find((p) => p.id === propertyId)
|
||||||
|
if (found) {
|
||||||
|
setProperty(found)
|
||||||
|
} else {
|
||||||
|
setError('Property not found')
|
||||||
|
setProperty(null)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setError(result.error || 'Failed to fetch property')
|
||||||
|
setProperty(null)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setError(err instanceof Error ? err.message : 'An unexpected error occurred')
|
||||||
|
setProperty(null)
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchProperty()
|
||||||
|
}, [propertyId])
|
||||||
|
|
||||||
|
return {
|
||||||
|
property,
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook to manage the active property for the current session
|
||||||
|
*/
|
||||||
|
export function useActiveProperty() {
|
||||||
|
const [activeProperty, setActivePropertyState] = useState<Property | null>(null)
|
||||||
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
const [isPending, setIsPending] = useState(false)
|
||||||
|
const isInitialFetchRef = useRef(true)
|
||||||
|
|
||||||
|
const fetchActiveProperty = useCallback(async (allowAutoSelect = false) => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true)
|
||||||
|
setError(null)
|
||||||
|
|
||||||
|
const result = await getActiveProperty()
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
setActivePropertyState(result.data || null)
|
||||||
|
|
||||||
|
// If no active property is set, automatically set the first property as active
|
||||||
|
// Only do this on initial mount to avoid interfering with property selection/creation
|
||||||
|
if (!result.data && allowAutoSelect) {
|
||||||
|
console.log('[useActiveProperty] No active property, checking if we should auto-select')
|
||||||
|
const propertiesResult = await getUserProperties()
|
||||||
|
|
||||||
|
if (
|
||||||
|
propertiesResult.success &&
|
||||||
|
propertiesResult.data &&
|
||||||
|
propertiesResult.data.length > 0
|
||||||
|
) {
|
||||||
|
console.log('[useActiveProperty] Found properties, auto-selecting first one')
|
||||||
|
const firstProperty = propertiesResult.data[0]
|
||||||
|
if (firstProperty) {
|
||||||
|
const setActiveResult = await setActivePropertyAction(firstProperty.id)
|
||||||
|
|
||||||
|
if (setActiveResult.success) {
|
||||||
|
console.log('[useActiveProperty] Auto-selected property:', firstProperty.name)
|
||||||
|
setActivePropertyState(firstProperty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!result.data && !allowAutoSelect) {
|
||||||
|
console.log('[useActiveProperty] No active property but auto-select is disabled')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setError(result.error || 'Failed to fetch active property')
|
||||||
|
setActivePropertyState(null)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setError(err instanceof Error ? err.message : 'An unexpected error occurred')
|
||||||
|
setActivePropertyState(null)
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const changeActiveProperty = useCallback(
|
||||||
|
async (propertyId: string | null) => {
|
||||||
|
try {
|
||||||
|
setIsPending(true)
|
||||||
|
setIsLoading(true)
|
||||||
|
const result = await setActivePropertyAction(propertyId)
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
if (propertyId) {
|
||||||
|
// Fetch the property data and set it immediately
|
||||||
|
console.log('[useActiveProperty] Fetching property data for ID:', propertyId)
|
||||||
|
const propertiesResult = await getUserProperties()
|
||||||
|
|
||||||
|
if (propertiesResult.success && propertiesResult.data) {
|
||||||
|
const selectedProperty = propertiesResult.data.find(p => p.id === propertyId)
|
||||||
|
if (selectedProperty) {
|
||||||
|
console.log('[useActiveProperty] Found property, setting as active:', selectedProperty.name)
|
||||||
|
console.log('[useActiveProperty] Current isLoading state:', isLoading)
|
||||||
|
setActivePropertyState(selectedProperty)
|
||||||
|
setIsLoading(false)
|
||||||
|
console.log('[useActiveProperty] Set isLoading to false')
|
||||||
|
} else {
|
||||||
|
console.error('[useActiveProperty] Property not found in user properties')
|
||||||
|
// Fall back to refetch
|
||||||
|
await fetchActiveProperty(false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('[useActiveProperty] Failed to fetch properties')
|
||||||
|
// Fall back to refetch
|
||||||
|
await fetchActiveProperty(false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setActivePropertyState(null)
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error(result.error || 'Failed to update active property')
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err instanceof Error ? err.message : 'An unexpected error occurred')
|
||||||
|
setIsLoading(false)
|
||||||
|
} finally {
|
||||||
|
setIsPending(false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[fetchActiveProperty],
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Only allow auto-select on the initial mount
|
||||||
|
const allowAutoSelect = isInitialFetchRef.current
|
||||||
|
if (isInitialFetchRef.current) {
|
||||||
|
isInitialFetchRef.current = false
|
||||||
|
}
|
||||||
|
fetchActiveProperty(allowAutoSelect)
|
||||||
|
}, [fetchActiveProperty])
|
||||||
|
|
||||||
|
return {
|
||||||
|
activeProperty,
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
setActiveProperty: changeActiveProperty,
|
||||||
|
isPending,
|
||||||
|
refetch: fetchActiveProperty,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
* Property store - Zustand store for property state management
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { create } from 'zustand'
|
||||||
|
import type { Property } from './types'
|
||||||
|
import {
|
||||||
|
getActiveProperty,
|
||||||
|
getUserProperties,
|
||||||
|
setActiveProperty as setActivePropertyAction,
|
||||||
|
} from './actions'
|
||||||
|
|
||||||
|
interface PropertyStore {
|
||||||
|
// State
|
||||||
|
activeProperty: Property | null
|
||||||
|
properties: Property[]
|
||||||
|
isLoading: boolean
|
||||||
|
error: string | null
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
fetchProperties: () => Promise<void>
|
||||||
|
fetchActiveProperty: () => Promise<void>
|
||||||
|
setActiveProperty: (propertyId: string) => Promise<void>
|
||||||
|
initialize: () => Promise<void>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const usePropertyStore = create<PropertyStore>((set, get) => ({
|
||||||
|
// Initial state
|
||||||
|
activeProperty: null,
|
||||||
|
properties: [],
|
||||||
|
isLoading: true,
|
||||||
|
error: null,
|
||||||
|
|
||||||
|
// Fetch all properties
|
||||||
|
fetchProperties: async () => {
|
||||||
|
const result = await getUserProperties()
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
set({ properties: result.data || [], error: null })
|
||||||
|
} else {
|
||||||
|
set({ error: result.error || 'Failed to fetch properties', properties: [] })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Fetch the active property from database
|
||||||
|
fetchActiveProperty: async () => {
|
||||||
|
set({ isLoading: true })
|
||||||
|
|
||||||
|
const result = await getActiveProperty()
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
set({
|
||||||
|
activeProperty: result.data || null,
|
||||||
|
isLoading: false,
|
||||||
|
error: null
|
||||||
|
})
|
||||||
|
|
||||||
|
// If no active property, auto-select the first one
|
||||||
|
if (!result.data) {
|
||||||
|
const propertiesResult = await getUserProperties()
|
||||||
|
if (propertiesResult.success && propertiesResult.data && propertiesResult.data.length > 0) {
|
||||||
|
const firstProperty = propertiesResult.data[0]
|
||||||
|
if (firstProperty) {
|
||||||
|
await get().setActiveProperty(firstProperty.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
set({
|
||||||
|
error: result.error || 'Failed to fetch active property',
|
||||||
|
activeProperty: null,
|
||||||
|
isLoading: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Set active property
|
||||||
|
setActiveProperty: async (propertyId: string) => {
|
||||||
|
set({ isLoading: true })
|
||||||
|
|
||||||
|
// Update database
|
||||||
|
const result = await setActivePropertyAction(propertyId)
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
// Fetch properties to get the full property object
|
||||||
|
const propertiesResult = await getUserProperties()
|
||||||
|
|
||||||
|
if (propertiesResult.success && propertiesResult.data) {
|
||||||
|
const selectedProperty = propertiesResult.data.find(p => p.id === propertyId)
|
||||||
|
|
||||||
|
if (selectedProperty) {
|
||||||
|
set({
|
||||||
|
activeProperty: selectedProperty,
|
||||||
|
properties: propertiesResult.data,
|
||||||
|
isLoading: false,
|
||||||
|
error: null
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
set({
|
||||||
|
isLoading: false,
|
||||||
|
error: 'Property not found'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
set({
|
||||||
|
isLoading: false,
|
||||||
|
error: 'Failed to fetch properties'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
set({
|
||||||
|
isLoading: false,
|
||||||
|
error: result.error || 'Failed to set active property'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Initialize - fetch both properties and active property
|
||||||
|
initialize: async () => {
|
||||||
|
set({ isLoading: true })
|
||||||
|
await Promise.all([
|
||||||
|
get().fetchProperties(),
|
||||||
|
get().fetchActiveProperty(),
|
||||||
|
])
|
||||||
|
},
|
||||||
|
}))
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* Property-related type definitions
|
||||||
|
* Isolated from monorepo database schema
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type Property = {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
owner_id: string
|
||||||
|
organization_id: string | null
|
||||||
|
address_id: string
|
||||||
|
created_at: string
|
||||||
|
updated_at: string
|
||||||
|
address: {
|
||||||
|
id: string
|
||||||
|
street_number?: string
|
||||||
|
route?: string
|
||||||
|
city?: string
|
||||||
|
state?: string
|
||||||
|
postal_code?: string
|
||||||
|
country?: string
|
||||||
|
latitude?: string
|
||||||
|
longitude?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CreatePropertyParams = {
|
||||||
|
name: string
|
||||||
|
center: [number, number]
|
||||||
|
streetNumber?: string
|
||||||
|
route?: string
|
||||||
|
routeShort?: string
|
||||||
|
neighborhood?: string
|
||||||
|
city?: string
|
||||||
|
county?: string
|
||||||
|
state?: string
|
||||||
|
stateLong?: string
|
||||||
|
postalCode?: string
|
||||||
|
postalCodeSuffix?: string
|
||||||
|
country?: string
|
||||||
|
countryLong?: string
|
||||||
|
rawJson?: Record<string, unknown>
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { customAlphabet } from 'nanoid'
|
||||||
|
|
||||||
|
const alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
|
||||||
|
const nanoid = customAlphabet(alphabet, 16)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a unique ID with optional prefix (matches monorepo implementation)
|
||||||
|
* @example createId('user') => 'user_Abc123...'
|
||||||
|
*/
|
||||||
|
export const createId = (prefix?: string) => {
|
||||||
|
const id = nanoid()
|
||||||
|
return prefix ? `${prefix}_${id}` : id
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { createAuth } from '@pascal-app/auth/server'
|
||||||
|
import { db } from '@pascal-app/db'
|
||||||
|
import { Resend } from 'resend'
|
||||||
|
|
||||||
|
const resend = process.env.RESEND_API_KEY ? new Resend(process.env.RESEND_API_KEY) : null
|
||||||
|
|
||||||
|
export const auth = createAuth({
|
||||||
|
db,
|
||||||
|
appName: 'Pascal Editor',
|
||||||
|
baseURL: process.env.BETTER_AUTH_URL!,
|
||||||
|
secret: process.env.BETTER_AUTH_SECRET!,
|
||||||
|
sendMagicLink: async ({ email, url }) => {
|
||||||
|
if (!resend) {
|
||||||
|
console.log(`[DEV] Magic link for ${email}: ${url}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await resend.emails.send({
|
||||||
|
from: 'Pascal <noreply@pascal.app>',
|
||||||
|
to: email,
|
||||||
|
subject: 'Sign in to Pascal Editor',
|
||||||
|
html: `
|
||||||
|
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
|
||||||
|
<h2>Sign in to Pascal Editor</h2>
|
||||||
|
<p>Click the button below to sign in to your account:</p>
|
||||||
|
<a href="${url}" style="display: inline-block; background-color: #000; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 6px; margin: 20px 0;">
|
||||||
|
Sign In
|
||||||
|
</a>
|
||||||
|
<p style="color: #666; font-size: 14px;">This link will expire in 5 minutes.</p>
|
||||||
|
<p style="color: #666; font-size: 14px;">If you didn't request this email, you can safely ignore it.</p>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
console.log(`✓ Magic link email sent to ${email}`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to send magic link email:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export type Session = typeof auth.$Infer.Session
|
||||||
|
export type User = typeof auth.$Infer.Session.user
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { createClient } from '@supabase/supabase-js'
|
||||||
|
import type { SupabaseDatabase } from '@pascal-app/db'
|
||||||
|
|
||||||
|
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
|
||||||
|
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supabase client for client-side use with anon key
|
||||||
|
* Uses Row Level Security (RLS) policies
|
||||||
|
*/
|
||||||
|
export const supabase = createClient<SupabaseDatabase>(supabaseUrl, supabaseAnonKey)
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { createClient } from '@supabase/supabase-js'
|
||||||
|
import type { SupabaseDatabase } from '@pascal-app/db'
|
||||||
|
|
||||||
|
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
|
||||||
|
const supabaseServiceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY!
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supabase client for server-side use with service role key
|
||||||
|
* Bypasses Row Level Security (RLS) - use with caution
|
||||||
|
* Always filter by user_id to enforce permissions
|
||||||
|
*/
|
||||||
|
export const supabaseAdmin = createClient<SupabaseDatabase>(
|
||||||
|
supabaseUrl,
|
||||||
|
supabaseServiceRoleKey,
|
||||||
|
{
|
||||||
|
auth: {
|
||||||
|
persistSession: false,
|
||||||
|
autoRefreshToken: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
@@ -11,7 +11,9 @@
|
|||||||
"check-types": "next typegen && tsc --noEmit"
|
"check-types": "next typegen && tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@pascal-app/auth": "*",
|
||||||
"@pascal-app/core": "*",
|
"@pascal-app/core": "*",
|
||||||
|
"@pascal-app/db": "*",
|
||||||
"@pascal-app/viewer": "*",
|
"@pascal-app/viewer": "*",
|
||||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-context-menu": "^2.2.16",
|
"@radix-ui/react-context-menu": "^2.2.16",
|
||||||
@@ -25,14 +27,18 @@
|
|||||||
"@radix-ui/react-switch": "^1.2.6",
|
"@radix-ui/react-switch": "^1.2.6",
|
||||||
"@radix-ui/react-tabs": "^1.1.13",
|
"@radix-ui/react-tabs": "^1.1.13",
|
||||||
"@radix-ui/react-tooltip": "^1.2.8",
|
"@radix-ui/react-tooltip": "^1.2.8",
|
||||||
|
"@react-google-maps/api": "^2.20.8",
|
||||||
"@react-three/uikit-lucide": "^1.0.60",
|
"@react-three/uikit-lucide": "^1.0.60",
|
||||||
"@repo/ui": "*",
|
"@repo/ui": "*",
|
||||||
|
"@supabase/supabase-js": "^2.95.3",
|
||||||
"@tailwindcss/postcss": "^4.1.18",
|
"@tailwindcss/postcss": "^4.1.18",
|
||||||
"@types/three": "^0.182.0",
|
"@types/three": "^0.182.0",
|
||||||
|
"better-auth": "^1.4.18",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.562.0",
|
"lucide-react": "^0.562.0",
|
||||||
"motion": "^12.26.2",
|
"motion": "^12.26.2",
|
||||||
|
"nanoid": "^5.1.6",
|
||||||
"next": "16.1.0",
|
"next": "16.1.0",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
|
"configVersion": 0,
|
||||||
"workspaces": {
|
"workspaces": {
|
||||||
"": {
|
"": {
|
||||||
"name": "editor",
|
"name": "editor",
|
||||||
@@ -13,6 +14,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^2.3.13",
|
"@biomejs/biome": "^2.3.13",
|
||||||
|
"supabase": "2.75.3",
|
||||||
"turbo": "^2.8.1",
|
"turbo": "^2.8.1",
|
||||||
"typescript": "5.9.2",
|
"typescript": "5.9.2",
|
||||||
"ultracite": "^7.1.1",
|
"ultracite": "^7.1.1",
|
||||||
@@ -22,7 +24,9 @@
|
|||||||
"name": "web",
|
"name": "web",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@pascal-app/auth": "*",
|
||||||
"@pascal-app/core": "*",
|
"@pascal-app/core": "*",
|
||||||
|
"@pascal-app/db": "*",
|
||||||
"@pascal-app/viewer": "*",
|
"@pascal-app/viewer": "*",
|
||||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-context-menu": "^2.2.16",
|
"@radix-ui/react-context-menu": "^2.2.16",
|
||||||
@@ -36,14 +40,18 @@
|
|||||||
"@radix-ui/react-switch": "^1.2.6",
|
"@radix-ui/react-switch": "^1.2.6",
|
||||||
"@radix-ui/react-tabs": "^1.1.13",
|
"@radix-ui/react-tabs": "^1.1.13",
|
||||||
"@radix-ui/react-tooltip": "^1.2.8",
|
"@radix-ui/react-tooltip": "^1.2.8",
|
||||||
|
"@react-google-maps/api": "^2.20.8",
|
||||||
"@react-three/uikit-lucide": "^1.0.60",
|
"@react-three/uikit-lucide": "^1.0.60",
|
||||||
"@repo/ui": "*",
|
"@repo/ui": "*",
|
||||||
|
"@supabase/supabase-js": "^2.95.3",
|
||||||
"@tailwindcss/postcss": "^4.1.18",
|
"@tailwindcss/postcss": "^4.1.18",
|
||||||
"@types/three": "^0.182.0",
|
"@types/three": "^0.182.0",
|
||||||
|
"better-auth": "^1.4.18",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.562.0",
|
"lucide-react": "^0.562.0",
|
||||||
"motion": "^12.26.2",
|
"motion": "^12.26.2",
|
||||||
|
"nanoid": "^5.1.6",
|
||||||
"next": "16.1.0",
|
"next": "16.1.0",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
@@ -60,6 +68,19 @@
|
|||||||
"typescript": "5.9.2",
|
"typescript": "5.9.2",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"packages/auth": {
|
||||||
|
"name": "@pascal-app/auth",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@pascal-app/db": "*",
|
||||||
|
"better-auth": "^1.4.18",
|
||||||
|
"resend": "^4.0.1",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@repo/typescript-config": "*",
|
||||||
|
"typescript": "5.9.2",
|
||||||
|
},
|
||||||
|
},
|
||||||
"packages/core": {
|
"packages/core": {
|
||||||
"name": "@pascal-app/core",
|
"name": "@pascal-app/core",
|
||||||
"version": "0.1.11",
|
"version": "0.1.11",
|
||||||
@@ -87,6 +108,22 @@
|
|||||||
"three": "^0.182",
|
"three": "^0.182",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"packages/db": {
|
||||||
|
"name": "@pascal-app/db",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@supabase/supabase-js": "^2.95.3",
|
||||||
|
"drizzle-orm": "^0.39.0",
|
||||||
|
"drizzle-zod": "^0.5.1",
|
||||||
|
"nanoid": "^5.0.9",
|
||||||
|
"postgres": "^3.4.5",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@repo/typescript-config": "*",
|
||||||
|
"drizzle-kit": "^0.30.0",
|
||||||
|
"typescript": "5.9.2",
|
||||||
|
},
|
||||||
|
},
|
||||||
"packages/eslint-config": {
|
"packages/eslint-config": {
|
||||||
"name": "@repo/eslint-config",
|
"name": "@repo/eslint-config",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
@@ -151,6 +188,14 @@
|
|||||||
|
|
||||||
"@babel/runtime": ["@babel/runtime@7.28.6", "", {}, "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="],
|
"@babel/runtime": ["@babel/runtime@7.28.6", "", {}, "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="],
|
||||||
|
|
||||||
|
"@better-auth/core": ["@better-auth/core@1.4.18", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "zod": "^4.3.5" }, "peerDependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21", "better-call": "1.1.8", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1" } }, "sha512-q+awYgC7nkLEBdx2sW0iJjkzgSHlIxGnOpsN1r/O1+a4m7osJNHtfK2mKJSL1I+GfNyIlxJF8WvD/NLuYMpmcg=="],
|
||||||
|
|
||||||
|
"@better-auth/telemetry": ["@better-auth/telemetry@1.4.18", "", { "dependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21" }, "peerDependencies": { "@better-auth/core": "1.4.18" } }, "sha512-e5rDF8S4j3Um/0LIVATL2in9dL4lfO2fr2v1Wio4qTMRbfxqnUDTa+6SZtwdeJrbc4O+a3c+IyIpjG9Q/6GpfQ=="],
|
||||||
|
|
||||||
|
"@better-auth/utils": ["@better-auth/utils@0.3.0", "", {}, "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw=="],
|
||||||
|
|
||||||
|
"@better-fetch/fetch": ["@better-fetch/fetch@1.1.21", "", {}, "sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A=="],
|
||||||
|
|
||||||
"@biomejs/biome": ["@biomejs/biome@2.3.13", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.13", "@biomejs/cli-darwin-x64": "2.3.13", "@biomejs/cli-linux-arm64": "2.3.13", "@biomejs/cli-linux-arm64-musl": "2.3.13", "@biomejs/cli-linux-x64": "2.3.13", "@biomejs/cli-linux-x64-musl": "2.3.13", "@biomejs/cli-win32-arm64": "2.3.13", "@biomejs/cli-win32-x64": "2.3.13" }, "bin": { "biome": "bin/biome" } }, "sha512-Fw7UsV0UAtWIBIm0M7g5CRerpu1eKyKAXIazzxhbXYUyMkwNrkX/KLkGI7b+uVDQ5cLUMfOC9vR60q9IDYDstA=="],
|
"@biomejs/biome": ["@biomejs/biome@2.3.13", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.13", "@biomejs/cli-darwin-x64": "2.3.13", "@biomejs/cli-linux-arm64": "2.3.13", "@biomejs/cli-linux-arm64-musl": "2.3.13", "@biomejs/cli-linux-x64": "2.3.13", "@biomejs/cli-linux-x64-musl": "2.3.13", "@biomejs/cli-win32-arm64": "2.3.13", "@biomejs/cli-win32-x64": "2.3.13" }, "bin": { "biome": "bin/biome" } }, "sha512-Fw7UsV0UAtWIBIm0M7g5CRerpu1eKyKAXIazzxhbXYUyMkwNrkX/KLkGI7b+uVDQ5cLUMfOC9vR60q9IDYDstA=="],
|
||||||
|
|
||||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.13", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0OCwP0/BoKzyJHnFdaTk/i7hIP9JHH9oJJq6hrSCPmJPo8JWcJhprK4gQlhFzrwdTBAW4Bjt/RmCf3ZZe59gwQ=="],
|
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.13", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0OCwP0/BoKzyJHnFdaTk/i7hIP9JHH9oJJq6hrSCPmJPo8JWcJhprK4gQlhFzrwdTBAW4Bjt/RmCf3ZZe59gwQ=="],
|
||||||
@@ -175,8 +220,60 @@
|
|||||||
|
|
||||||
"@dimforge/rapier3d-compat": ["@dimforge/rapier3d-compat@0.12.0", "", {}, "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow=="],
|
"@dimforge/rapier3d-compat": ["@dimforge/rapier3d-compat@0.12.0", "", {}, "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow=="],
|
||||||
|
|
||||||
|
"@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="],
|
||||||
|
|
||||||
"@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="],
|
"@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="],
|
||||||
|
|
||||||
|
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.19.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA=="],
|
||||||
|
|
||||||
|
"@esbuild/android-arm": ["@esbuild/android-arm@0.19.12", "", { "os": "android", "cpu": "arm" }, "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w=="],
|
||||||
|
|
||||||
|
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.19.12", "", { "os": "android", "cpu": "arm64" }, "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA=="],
|
||||||
|
|
||||||
|
"@esbuild/android-x64": ["@esbuild/android-x64@0.19.12", "", { "os": "android", "cpu": "x64" }, "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew=="],
|
||||||
|
|
||||||
|
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.19.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g=="],
|
||||||
|
|
||||||
|
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.19.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A=="],
|
||||||
|
|
||||||
|
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.19.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA=="],
|
||||||
|
|
||||||
|
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.19.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.19.12", "", { "os": "linux", "cpu": "arm" }, "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.19.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.19.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.19.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.19.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.19.12", "", { "os": "linux", "cpu": "x64" }, "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg=="],
|
||||||
|
|
||||||
|
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.19.12", "", { "os": "none", "cpu": "x64" }, "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA=="],
|
||||||
|
|
||||||
|
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.19.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw=="],
|
||||||
|
|
||||||
|
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.19.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA=="],
|
||||||
|
|
||||||
|
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.19.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A=="],
|
||||||
|
|
||||||
|
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.19.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ=="],
|
||||||
|
|
||||||
|
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.19.12", "", { "os": "win32", "cpu": "x64" }, "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA=="],
|
||||||
|
|
||||||
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="],
|
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="],
|
||||||
|
|
||||||
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="],
|
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="],
|
||||||
@@ -203,6 +300,10 @@
|
|||||||
|
|
||||||
"@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=="],
|
||||||
|
|
||||||
|
"@googlemaps/js-api-loader": ["@googlemaps/js-api-loader@1.16.8", "", {}, "sha512-CROqqwfKotdO6EBjZO/gQGVTbeDps5V7Mt9+8+5Q+jTg5CRMi3Ii/L9PmV3USROrt2uWxtGzJHORmByxyo9pSQ=="],
|
||||||
|
|
||||||
|
"@googlemaps/markerclusterer": ["@googlemaps/markerclusterer@2.5.3", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "supercluster": "^8.0.1" } }, "sha512-x7lX0R5yYOoiNectr10wLgCBasNcXFHiADIBdmn7jQllF2B5ENQw5XtZK+hIw4xnV0Df0xhN4LN98XqA5jaiOw=="],
|
||||||
|
|
||||||
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
|
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
|
||||||
|
|
||||||
"@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="],
|
"@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="],
|
||||||
@@ -265,6 +366,8 @@
|
|||||||
|
|
||||||
"@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="],
|
"@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="],
|
||||||
|
|
||||||
|
"@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=="],
|
||||||
@@ -299,16 +402,26 @@
|
|||||||
|
|
||||||
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.1.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Tr0j94MphimCCks+1rtYPzQFK+faJuhHWCegU9S9gDlgyOk8Y3kPmO64UcjyzZAlligeBtYZ/2bEyrKq0d2wqQ=="],
|
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.1.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Tr0j94MphimCCks+1rtYPzQFK+faJuhHWCegU9S9gDlgyOk8Y3kPmO64UcjyzZAlligeBtYZ/2bEyrKq0d2wqQ=="],
|
||||||
|
|
||||||
|
"@noble/ciphers": ["@noble/ciphers@2.1.1", "", {}, "sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw=="],
|
||||||
|
|
||||||
|
"@noble/hashes": ["@noble/hashes@2.0.1", "", {}, "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw=="],
|
||||||
|
|
||||||
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
||||||
|
|
||||||
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
||||||
|
|
||||||
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
||||||
|
|
||||||
|
"@pascal-app/auth": ["@pascal-app/auth@workspace:packages/auth"],
|
||||||
|
|
||||||
"@pascal-app/core": ["@pascal-app/core@workspace:packages/core"],
|
"@pascal-app/core": ["@pascal-app/core@workspace:packages/core"],
|
||||||
|
|
||||||
|
"@pascal-app/db": ["@pascal-app/db@workspace:packages/db"],
|
||||||
|
|
||||||
"@pascal-app/viewer": ["@pascal-app/viewer@workspace:packages/viewer"],
|
"@pascal-app/viewer": ["@pascal-app/viewer@workspace:packages/viewer"],
|
||||||
|
|
||||||
|
"@petamoriken/float16": ["@petamoriken/float16@3.9.3", "", {}, "sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g=="],
|
||||||
|
|
||||||
"@pmndrs/msdfonts": ["@pmndrs/msdfonts@1.0.61", "", {}, "sha512-Nxk+3dAdsXFdx5pq1IHNe1j4zxogkAfhmBKUIHS+aJUs45q/XMtlADVrHLooiQ/Amlp9tXAvbvFYUOGd897POw=="],
|
"@pmndrs/msdfonts": ["@pmndrs/msdfonts@1.0.61", "", {}, "sha512-Nxk+3dAdsXFdx5pq1IHNe1j4zxogkAfhmBKUIHS+aJUs45q/XMtlADVrHLooiQ/Amlp9tXAvbvFYUOGd897POw=="],
|
||||||
|
|
||||||
"@pmndrs/uikit": ["@pmndrs/uikit@1.0.61", "", { "dependencies": { "@pmndrs/msdfonts": "^1.0.61", "@pmndrs/uikit-pub-sub": "^1.0.61", "@preact/signals-core": "^1.5.1", "@zappar/msdf-generator": "^1.2.4", "yoga-layout": "^3.2.1" }, "peerDependencies": { "three": ">=0.162" } }, "sha512-xoNp2jKRoHa7INC3QaAL2ddpGmKy+eORMh4zoNds17EndqyBrqYzfWJK+PVmei/5MQNBTi0KrY2qxBGj6hB0cg=="],
|
"@pmndrs/uikit": ["@pmndrs/uikit@1.0.61", "", { "dependencies": { "@pmndrs/msdfonts": "^1.0.61", "@pmndrs/uikit-pub-sub": "^1.0.61", "@preact/signals-core": "^1.5.1", "@zappar/msdf-generator": "^1.2.4", "yoga-layout": "^3.2.1" }, "peerDependencies": { "three": ">=0.162" } }, "sha512-xoNp2jKRoHa7INC3QaAL2ddpGmKy+eORMh4zoNds17EndqyBrqYzfWJK+PVmei/5MQNBTi0KrY2qxBGj6hB0cg=="],
|
||||||
@@ -397,6 +510,14 @@
|
|||||||
|
|
||||||
"@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
|
"@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
|
||||||
|
|
||||||
|
"@react-email/render": ["@react-email/render@1.1.2", "", { "dependencies": { "html-to-text": "^9.0.5", "prettier": "^3.5.3", "react-promise-suspense": "^0.3.4" }, "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-RnRehYN3v9gVlNMehHPHhyp2RQo7+pSkHDtXPvg3s0GbzM9SQMW4Qrf8GRNvtpLC4gsI+Wt0VatNRUFqjvevbw=="],
|
||||||
|
|
||||||
|
"@react-google-maps/api": ["@react-google-maps/api@2.20.8", "", { "dependencies": { "@googlemaps/js-api-loader": "1.16.8", "@googlemaps/markerclusterer": "2.5.3", "@react-google-maps/infobox": "2.20.0", "@react-google-maps/marker-clusterer": "2.20.0", "@types/google.maps": "3.58.1", "invariant": "2.2.4" }, "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19", "react-dom": "^16.8 || ^17 || ^18 || ^19" } }, "sha512-wtLYFtCGXK3qbIz1H5to3JxbosPnKsvjDKhqGylXUb859EskhzR7OpuNt0LqdLarXUtZCJTKzPn3BNaekNIahg=="],
|
||||||
|
|
||||||
|
"@react-google-maps/infobox": ["@react-google-maps/infobox@2.20.0", "", {}, "sha512-03PJHjohhaVLkX6+NHhlr8CIlvUxWaXhryqDjyaZ8iIqqix/nV8GFdz9O3m5OsjtxtNho09F/15j14yV0nuyLQ=="],
|
||||||
|
|
||||||
|
"@react-google-maps/marker-clusterer": ["@react-google-maps/marker-clusterer@2.20.0", "", {}, "sha512-tieX9Va5w1yP88vMgfH1pHTacDQ9TgDTjox3tLlisKDXRQWdjw+QeVVghhf5XqqIxXHgPdcGwBvKY6UP+SIvLw=="],
|
||||||
|
|
||||||
"@react-three/drei": ["@react-three/drei@10.7.7", "", { "dependencies": { "@babel/runtime": "^7.26.0", "@mediapipe/tasks-vision": "0.10.17", "@monogrid/gainmap-js": "^3.0.6", "@use-gesture/react": "^10.3.1", "camera-controls": "^3.1.0", "cross-env": "^7.0.3", "detect-gpu": "^5.0.56", "glsl-noise": "^0.0.0", "hls.js": "^1.5.17", "maath": "^0.10.8", "meshline": "^3.3.1", "stats-gl": "^2.2.8", "stats.js": "^0.17.0", "suspend-react": "^0.1.3", "three-mesh-bvh": "^0.8.3", "three-stdlib": "^2.35.6", "troika-three-text": "^0.52.4", "tunnel-rat": "^0.1.2", "use-sync-external-store": "^1.4.0", "utility-types": "^3.11.0", "zustand": "^5.0.1" }, "peerDependencies": { "@react-three/fiber": "^9.0.0", "react": "^19", "react-dom": "^19", "three": ">=0.159" }, "optionalPeers": ["react-dom"] }, "sha512-ff+J5iloR0k4tC++QtD/j9u3w5fzfgFAWDtAGQah9pF2B1YgOq/5JxqY0/aVoQG5r3xSZz0cv5tk2YuBob4xEQ=="],
|
"@react-three/drei": ["@react-three/drei@10.7.7", "", { "dependencies": { "@babel/runtime": "^7.26.0", "@mediapipe/tasks-vision": "0.10.17", "@monogrid/gainmap-js": "^3.0.6", "@use-gesture/react": "^10.3.1", "camera-controls": "^3.1.0", "cross-env": "^7.0.3", "detect-gpu": "^5.0.56", "glsl-noise": "^0.0.0", "hls.js": "^1.5.17", "maath": "^0.10.8", "meshline": "^3.3.1", "stats-gl": "^2.2.8", "stats.js": "^0.17.0", "suspend-react": "^0.1.3", "three-mesh-bvh": "^0.8.3", "three-stdlib": "^2.35.6", "troika-three-text": "^0.52.4", "tunnel-rat": "^0.1.2", "use-sync-external-store": "^1.4.0", "utility-types": "^3.11.0", "zustand": "^5.0.1" }, "peerDependencies": { "@react-three/fiber": "^9.0.0", "react": "^19", "react-dom": "^19", "three": ">=0.159" }, "optionalPeers": ["react-dom"] }, "sha512-ff+J5iloR0k4tC++QtD/j9u3w5fzfgFAWDtAGQah9pF2B1YgOq/5JxqY0/aVoQG5r3xSZz0cv5tk2YuBob4xEQ=="],
|
||||||
|
|
||||||
"@react-three/fiber": ["@react-three/fiber@9.5.0", "", { "dependencies": { "@babel/runtime": "^7.17.8", "@types/webxr": "*", "base64-js": "^1.5.1", "buffer": "^6.0.3", "its-fine": "^2.0.0", "react-use-measure": "^2.1.7", "scheduler": "^0.27.0", "suspend-react": "^0.1.3", "use-sync-external-store": "^1.4.0", "zustand": "^5.0.3" }, "peerDependencies": { "expo": ">=43.0", "expo-asset": ">=8.4", "expo-file-system": ">=11.0", "expo-gl": ">=11.0", "react": ">=19 <19.3", "react-dom": ">=19 <19.3", "react-native": ">=0.78", "three": ">=0.156" }, "optionalPeers": ["expo", "expo-asset", "expo-file-system", "expo-gl", "react-dom", "react-native"] }, "sha512-FiUzfYW4wB1+PpmsE47UM+mCads7j2+giRBltfwH7SNhah95rqJs3ltEs9V3pP8rYdS0QlNne+9Aj8dS/SiaIA=="],
|
"@react-three/fiber": ["@react-three/fiber@9.5.0", "", { "dependencies": { "@babel/runtime": "^7.17.8", "@types/webxr": "*", "base64-js": "^1.5.1", "buffer": "^6.0.3", "its-fine": "^2.0.0", "react-use-measure": "^2.1.7", "scheduler": "^0.27.0", "suspend-react": "^0.1.3", "use-sync-external-store": "^1.4.0", "zustand": "^5.0.3" }, "peerDependencies": { "expo": ">=43.0", "expo-asset": ">=8.4", "expo-file-system": ">=11.0", "expo-gl": ">=11.0", "react": ">=19 <19.3", "react-dom": ">=19 <19.3", "react-native": ">=0.78", "three": ">=0.156" }, "optionalPeers": ["expo", "expo-asset", "expo-file-system", "expo-gl", "react-dom", "react-native"] }, "sha512-FiUzfYW4wB1+PpmsE47UM+mCads7j2+giRBltfwH7SNhah95rqJs3ltEs9V3pP8rYdS0QlNne+9Aj8dS/SiaIA=="],
|
||||||
@@ -411,6 +532,22 @@
|
|||||||
|
|
||||||
"@repo/ui": ["@repo/ui@workspace:packages/ui"],
|
"@repo/ui": ["@repo/ui@workspace:packages/ui"],
|
||||||
|
|
||||||
|
"@selderee/plugin-htmlparser2": ["@selderee/plugin-htmlparser2@0.11.0", "", { "dependencies": { "domhandler": "^5.0.3", "selderee": "^0.11.0" } }, "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ=="],
|
||||||
|
|
||||||
|
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||||
|
|
||||||
|
"@supabase/auth-js": ["@supabase/auth-js@2.95.3", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-vD2YoS8E2iKIX0F7EwXTmqhUpaNsmbU6X2R0/NdFcs02oEfnHyNP/3M716f3wVJ2E5XHGiTFXki6lRckhJ0Thg=="],
|
||||||
|
|
||||||
|
"@supabase/functions-js": ["@supabase/functions-js@2.95.3", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-uTuOAKzs9R/IovW1krO0ZbUHSJnsnyJElTXIRhjJTqymIVGcHzkAYnBCJqd7468Fs/Foz1BQ7Dv6DCl05lr7ig=="],
|
||||||
|
|
||||||
|
"@supabase/postgrest-js": ["@supabase/postgrest-js@2.95.3", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-LTrRBqU1gOovxRm1vRXPItSMPBmEFqrfTqdPTRtzOILV4jPSueFz6pES5hpb4LRlkFwCPRmv3nQJ5N625V2Xrg=="],
|
||||||
|
|
||||||
|
"@supabase/realtime-js": ["@supabase/realtime-js@2.95.3", "", { "dependencies": { "@types/phoenix": "^1.6.6", "@types/ws": "^8.18.1", "tslib": "2.8.1", "ws": "^8.18.2" } }, "sha512-D7EAtfU3w6BEUxDACjowWNJo/ZRo7sDIuhuOGKHIm9FHieGeoJV5R6GKTLtga/5l/6fDr2u+WcW/m8I9SYmaIw=="],
|
||||||
|
|
||||||
|
"@supabase/storage-js": ["@supabase/storage-js@2.95.3", "", { "dependencies": { "iceberg-js": "^0.8.1", "tslib": "2.8.1" } }, "sha512-4GxkJiXI3HHWjxpC3sDx1BVrV87O0hfX+wvJdqGv67KeCu+g44SPnII8y0LL/Wr677jB7tpjAxKdtVWf+xhc9A=="],
|
||||||
|
|
||||||
|
"@supabase/supabase-js": ["@supabase/supabase-js@2.95.3", "", { "dependencies": { "@supabase/auth-js": "2.95.3", "@supabase/functions-js": "2.95.3", "@supabase/postgrest-js": "2.95.3", "@supabase/realtime-js": "2.95.3", "@supabase/storage-js": "2.95.3" } }, "sha512-Fukw1cUTQ6xdLiHDJhKKPu6svEPaCEDvThqCne3OaQyZvuq2qjhJAd91kJu3PXLG18aooCgYBaB6qQz35hhABg=="],
|
||||||
|
|
||||||
"@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],
|
"@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],
|
||||||
|
|
||||||
"@tailwindcss/node": ["@tailwindcss/node@4.1.18", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.18" } }, "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ=="],
|
"@tailwindcss/node": ["@tailwindcss/node@4.1.18", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.18" } }, "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ=="],
|
||||||
@@ -449,12 +586,16 @@
|
|||||||
|
|
||||||
"@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/google.maps": ["@types/google.maps@3.58.1", "", {}, "sha512-X9QTSvGJ0nCfMzYOnaVs/k6/4L+7F5uCS+4iUmkLEls6J9S/Phv+m/i3mDeyc49ZBgwab3EFO1HEoBY7k98EGQ=="],
|
||||||
|
|
||||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||||
|
|
||||||
"@types/node": ["@types/node@22.19.7", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw=="],
|
"@types/node": ["@types/node@22.19.7", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw=="],
|
||||||
|
|
||||||
"@types/offscreencanvas": ["@types/offscreencanvas@2019.7.3", "", {}, "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A=="],
|
"@types/offscreencanvas": ["@types/offscreencanvas@2019.7.3", "", {}, "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A=="],
|
||||||
|
|
||||||
|
"@types/phoenix": ["@types/phoenix@1.6.7", "", {}, "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q=="],
|
||||||
|
|
||||||
"@types/react": ["@types/react@19.2.8", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg=="],
|
"@types/react": ["@types/react@19.2.8", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg=="],
|
||||||
|
|
||||||
"@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.2.2", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw=="],
|
||||||
@@ -467,6 +608,8 @@
|
|||||||
|
|
||||||
"@types/webxr": ["@types/webxr@0.5.24", "", {}, "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg=="],
|
"@types/webxr": ["@types/webxr@0.5.24", "", {}, "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg=="],
|
||||||
|
|
||||||
|
"@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.53.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.53.0", "@typescript-eslint/type-utils": "8.53.0", "@typescript-eslint/utils": "8.53.0", "@typescript-eslint/visitor-keys": "8.53.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.53.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-eEXsVvLPu8Z4PkFibtuFJLJOTAV/nPdgtSjkGoPpddpFk3/ym2oy97jynY6ic2m6+nc5M8SE1e9v/mHKsulcJg=="],
|
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.53.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.53.0", "@typescript-eslint/type-utils": "8.53.0", "@typescript-eslint/utils": "8.53.0", "@typescript-eslint/visitor-keys": "8.53.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.53.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-eEXsVvLPu8Z4PkFibtuFJLJOTAV/nPdgtSjkGoPpddpFk3/ym2oy97jynY6ic2m6+nc5M8SE1e9v/mHKsulcJg=="],
|
||||||
|
|
||||||
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.53.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.53.0", "@typescript-eslint/types": "8.53.0", "@typescript-eslint/typescript-estree": "8.53.0", "@typescript-eslint/visitor-keys": "8.53.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-npiaib8XzbjtzS2N4HlqPvlpxpmZ14FjSJrteZpPxGUaYPlvhzlzUZ4mZyABo0EFrOWnvyd0Xxroq//hKhtAWg=="],
|
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.53.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.53.0", "@typescript-eslint/types": "8.53.0", "@typescript-eslint/typescript-estree": "8.53.0", "@typescript-eslint/visitor-keys": "8.53.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-npiaib8XzbjtzS2N4HlqPvlpxpmZ14FjSJrteZpPxGUaYPlvhzlzUZ4mZyABo0EFrOWnvyd0Xxroq//hKhtAWg=="],
|
||||||
@@ -499,6 +642,8 @@
|
|||||||
|
|
||||||
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
||||||
|
|
||||||
|
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||||
|
|
||||||
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
||||||
|
|
||||||
"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=="],
|
||||||
@@ -531,14 +676,22 @@
|
|||||||
|
|
||||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.9.14", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg=="],
|
"baseline-browser-mapping": ["baseline-browser-mapping@2.9.14", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg=="],
|
||||||
|
|
||||||
|
"better-auth": ["better-auth@1.4.18", "", { "dependencies": { "@better-auth/core": "1.4.18", "@better-auth/telemetry": "1.4.18", "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21", "@noble/ciphers": "^2.0.0", "@noble/hashes": "^2.0.0", "better-call": "1.1.8", "defu": "^6.1.4", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1", "zod": "^4.3.5" }, "peerDependencies": { "@lynx-js/react": "*", "@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0", "@sveltejs/kit": "^2.0.0", "@tanstack/react-start": "^1.0.0", "@tanstack/solid-start": "^1.0.0", "better-sqlite3": "^12.0.0", "drizzle-kit": ">=0.31.4", "drizzle-orm": ">=0.41.0", "mongodb": "^6.0.0 || ^7.0.0", "mysql2": "^3.0.0", "next": "^14.0.0 || ^15.0.0 || ^16.0.0", "pg": "^8.0.0", "prisma": "^5.0.0 || ^6.0.0 || ^7.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "solid-js": "^1.0.0", "svelte": "^4.0.0 || ^5.0.0", "vitest": "^2.0.0 || ^3.0.0 || ^4.0.0", "vue": "^3.0.0" }, "optionalPeers": ["@lynx-js/react", "@prisma/client", "@sveltejs/kit", "@tanstack/react-start", "@tanstack/solid-start", "better-sqlite3", "drizzle-kit", "drizzle-orm", "mongodb", "mysql2", "next", "pg", "prisma", "react", "react-dom", "solid-js", "svelte", "vitest", "vue"] }, "sha512-bnyifLWBPcYVltH3RhS7CM62MoelEqC6Q+GnZwfiDWNfepXoQZBjEvn4urcERC7NTKgKq5zNBM8rvPvRBa6xcg=="],
|
||||||
|
|
||||||
|
"better-call": ["better-call@1.1.8", "", { "dependencies": { "@better-auth/utils": "^0.3.0", "@better-fetch/fetch": "^1.1.4", "rou3": "^0.7.10", "set-cookie-parser": "^2.7.1" }, "peerDependencies": { "zod": "^4.0.0" }, "optionalPeers": ["zod"] }, "sha512-XMQ2rs6FNXasGNfMjzbyroSwKwYbZ/T3IxruSS6U2MJRsSYh3wYtG3o6H00ZlKZ/C/UPOAD97tqgQJNsxyeTXw=="],
|
||||||
|
|
||||||
"bidi-js": ["bidi-js@1.0.3", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="],
|
"bidi-js": ["bidi-js@1.0.3", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="],
|
||||||
|
|
||||||
|
"bin-links": ["bin-links@6.0.0", "", { "dependencies": { "cmd-shim": "^8.0.0", "npm-normalize-package-bin": "^5.0.0", "proc-log": "^6.0.0", "read-cmd-shim": "^6.0.0", "write-file-atomic": "^7.0.0" } }, "sha512-X4CiKlcV2GjnCMwnKAfbVWpHa++65th9TuzAEYtZoATiOE2DQKhSp4CJlyLoTqdhBKlXjpXjCTYPNNFS33Fi6w=="],
|
||||||
|
|
||||||
"brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
"brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
||||||
|
|
||||||
"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=="],
|
||||||
|
|
||||||
"buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
"buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
||||||
|
|
||||||
|
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
|
||||||
|
|
||||||
"call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
|
"call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
|
||||||
|
|
||||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||||
@@ -553,6 +706,8 @@
|
|||||||
|
|
||||||
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||||
|
|
||||||
|
"chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
|
||||||
|
|
||||||
"citty": ["citty@0.2.0", "", {}, "sha512-8csy5IBFI2ex2hTVpaHN2j+LNE199AgiI7y4dMintrr8i0lQiFn+0AWMZrWdHKIgMOer65f8IThysYhoReqjWA=="],
|
"citty": ["citty@0.2.0", "", {}, "sha512-8csy5IBFI2ex2hTVpaHN2j+LNE199AgiI7y4dMintrr8i0lQiFn+0AWMZrWdHKIgMOer65f8IThysYhoReqjWA=="],
|
||||||
|
|
||||||
"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=="],
|
||||||
@@ -561,6 +716,8 @@
|
|||||||
|
|
||||||
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
||||||
|
|
||||||
|
"cmd-shim": ["cmd-shim@8.0.0", "", {}, "sha512-Jk/BK6NCapZ58BKUxlSI+ouKRbjH1NLZCgJkYoab+vEHUY3f6OzpNBN9u7HFSv9J6TRDGs4PLOHezoKGaFRSCA=="],
|
||||||
|
|
||||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||||
|
|
||||||
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||||
@@ -577,6 +734,8 @@
|
|||||||
|
|
||||||
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
||||||
|
|
||||||
|
"data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
|
||||||
|
|
||||||
"data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="],
|
"data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="],
|
||||||
|
|
||||||
"data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="],
|
"data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="],
|
||||||
@@ -595,6 +754,8 @@
|
|||||||
|
|
||||||
"define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
|
"define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
|
||||||
|
|
||||||
|
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
||||||
|
|
||||||
"detect-gpu": ["detect-gpu@5.0.70", "", { "dependencies": { "webgl-constants": "^1.1.1" } }, "sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w=="],
|
"detect-gpu": ["detect-gpu@5.0.70", "", { "dependencies": { "webgl-constants": "^1.1.1" } }, "sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w=="],
|
||||||
|
|
||||||
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||||
@@ -603,14 +764,32 @@
|
|||||||
|
|
||||||
"doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
|
"doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
|
||||||
|
|
||||||
|
"dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
|
||||||
|
|
||||||
|
"domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
|
||||||
|
|
||||||
|
"domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
|
||||||
|
|
||||||
|
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
|
||||||
|
|
||||||
"dotenv": ["dotenv@16.0.3", "", {}, "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ=="],
|
"dotenv": ["dotenv@16.0.3", "", {}, "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ=="],
|
||||||
|
|
||||||
"draco3d": ["draco3d@1.5.7", "", {}, "sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ=="],
|
"draco3d": ["draco3d@1.5.7", "", {}, "sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ=="],
|
||||||
|
|
||||||
|
"drizzle-kit": ["drizzle-kit@0.30.6", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.19.7", "esbuild-register": "^3.5.0", "gel": "^2.0.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-U4wWit0fyZuGuP7iNmRleQyK2V8wCuv57vf5l3MnG4z4fzNTjY/U13M8owyQ5RavqvqxBifWORaR3wIUzlN64g=="],
|
||||||
|
|
||||||
|
"drizzle-orm": ["drizzle-orm@0.39.3", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-EZ8ZpYvDIvKU9C56JYLOmUskazhad+uXZCTCRN4OnRMsL+xAJ05dv1eCpAG5xzhsm1hqiuC5kAZUCS924u2DTw=="],
|
||||||
|
|
||||||
|
"drizzle-zod": ["drizzle-zod@0.5.1", "", { "peerDependencies": { "drizzle-orm": ">=0.23.13", "zod": "*" } }, "sha512-C/8bvzUH/zSnVfwdSibOgFjLhtDtbKYmkbPbUCq46QZyZCH6kODIMSOgZ8R7rVjoI+tCj3k06MRJMDqsIeoS4A=="],
|
||||||
|
|
||||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||||
|
|
||||||
"enhanced-resolve": ["enhanced-resolve@5.18.4", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q=="],
|
"enhanced-resolve": ["enhanced-resolve@5.18.4", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q=="],
|
||||||
|
|
||||||
|
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||||
|
|
||||||
|
"env-paths": ["env-paths@3.0.0", "", {}, "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A=="],
|
||||||
|
|
||||||
"es-abstract": ["es-abstract@1.24.1", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw=="],
|
"es-abstract": ["es-abstract@1.24.1", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw=="],
|
||||||
|
|
||||||
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
||||||
@@ -627,6 +806,10 @@
|
|||||||
|
|
||||||
"es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="],
|
"es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="],
|
||||||
|
|
||||||
|
"esbuild": ["esbuild@0.19.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.19.12", "@esbuild/android-arm": "0.19.12", "@esbuild/android-arm64": "0.19.12", "@esbuild/android-x64": "0.19.12", "@esbuild/darwin-arm64": "0.19.12", "@esbuild/darwin-x64": "0.19.12", "@esbuild/freebsd-arm64": "0.19.12", "@esbuild/freebsd-x64": "0.19.12", "@esbuild/linux-arm": "0.19.12", "@esbuild/linux-arm64": "0.19.12", "@esbuild/linux-ia32": "0.19.12", "@esbuild/linux-loong64": "0.19.12", "@esbuild/linux-mips64el": "0.19.12", "@esbuild/linux-ppc64": "0.19.12", "@esbuild/linux-riscv64": "0.19.12", "@esbuild/linux-s390x": "0.19.12", "@esbuild/linux-x64": "0.19.12", "@esbuild/netbsd-x64": "0.19.12", "@esbuild/openbsd-x64": "0.19.12", "@esbuild/sunos-x64": "0.19.12", "@esbuild/win32-arm64": "0.19.12", "@esbuild/win32-ia32": "0.19.12", "@esbuild/win32-x64": "0.19.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg=="],
|
||||||
|
|
||||||
|
"esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="],
|
||||||
|
|
||||||
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
||||||
|
|
||||||
"eslint": ["eslint@9.39.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.2", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw=="],
|
"eslint": ["eslint@9.39.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.2", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw=="],
|
||||||
@@ -667,6 +850,8 @@
|
|||||||
|
|
||||||
"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=="],
|
||||||
|
|
||||||
|
"fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
|
||||||
|
|
||||||
"fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
|
"fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
|
||||||
|
|
||||||
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
||||||
@@ -681,6 +866,8 @@
|
|||||||
|
|
||||||
"for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
|
"for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
|
||||||
|
|
||||||
|
"formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
|
||||||
|
|
||||||
"framer-motion": ["framer-motion@12.26.2", "", { "dependencies": { "motion-dom": "^12.26.2", "motion-utils": "^12.24.10", "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-lflOQEdjquUi9sCg5Y1LrsZDlsjrHw7m0T9Yedvnk7Bnhqfkc89/Uha10J3CFhkL+TCZVCRw9eUGyM/lyYhXQA=="],
|
"framer-motion": ["framer-motion@12.26.2", "", { "dependencies": { "motion-dom": "^12.26.2", "motion-utils": "^12.24.10", "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-lflOQEdjquUi9sCg5Y1LrsZDlsjrHw7m0T9Yedvnk7Bnhqfkc89/Uha10J3CFhkL+TCZVCRw9eUGyM/lyYhXQA=="],
|
||||||
|
|
||||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||||
@@ -689,6 +876,8 @@
|
|||||||
|
|
||||||
"functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="],
|
"functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="],
|
||||||
|
|
||||||
|
"gel": ["gel@2.2.0", "", { "dependencies": { "@petamoriken/float16": "^3.8.7", "debug": "^4.3.4", "env-paths": "^3.0.0", "semver": "^7.6.2", "shell-quote": "^1.8.1", "which": "^4.0.0" }, "bin": { "gel": "dist/cli.mjs" } }, "sha512-q0ma7z2swmoamHQusey8ayo8+ilVdzDt4WTxSPzq/yRqvucWRfymRVMvNgmSC0XK7eNjjEZEcplxpgaNojKdmQ=="],
|
||||||
|
|
||||||
"generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="],
|
"generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="],
|
||||||
|
|
||||||
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||||
@@ -699,6 +888,8 @@
|
|||||||
|
|
||||||
"get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="],
|
"get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="],
|
||||||
|
|
||||||
|
"get-tsconfig": ["get-tsconfig@4.13.6", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw=="],
|
||||||
|
|
||||||
"glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="],
|
"glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="],
|
||||||
|
|
||||||
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
||||||
@@ -729,6 +920,14 @@
|
|||||||
|
|
||||||
"hls.js": ["hls.js@1.6.15", "", {}, "sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA=="],
|
"hls.js": ["hls.js@1.6.15", "", {}, "sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA=="],
|
||||||
|
|
||||||
|
"html-to-text": ["html-to-text@9.0.5", "", { "dependencies": { "@selderee/plugin-htmlparser2": "^0.11.0", "deepmerge": "^4.3.1", "dom-serializer": "^2.0.0", "htmlparser2": "^8.0.2", "selderee": "^0.11.0" } }, "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg=="],
|
||||||
|
|
||||||
|
"htmlparser2": ["htmlparser2@8.0.2", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1", "entities": "^4.4.0" } }, "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA=="],
|
||||||
|
|
||||||
|
"https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||||
|
|
||||||
|
"iceberg-js": ["iceberg-js@0.8.1", "", {}, "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA=="],
|
||||||
|
|
||||||
"idb-keyval": ["idb-keyval@6.2.2", "", {}, "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg=="],
|
"idb-keyval": ["idb-keyval@6.2.2", "", {}, "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg=="],
|
||||||
|
|
||||||
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||||
@@ -743,6 +942,8 @@
|
|||||||
|
|
||||||
"internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="],
|
"internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="],
|
||||||
|
|
||||||
|
"invariant": ["invariant@2.2.4", "", { "dependencies": { "loose-envify": "^1.0.0" } }, "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA=="],
|
||||||
|
|
||||||
"is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="],
|
"is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="],
|
||||||
|
|
||||||
"is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="],
|
"is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="],
|
||||||
@@ -797,7 +998,7 @@
|
|||||||
|
|
||||||
"isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
"isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
||||||
|
|
||||||
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
"isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="],
|
||||||
|
|
||||||
"iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="],
|
"iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="],
|
||||||
|
|
||||||
@@ -805,6 +1006,8 @@
|
|||||||
|
|
||||||
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
||||||
|
|
||||||
|
"jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="],
|
||||||
|
|
||||||
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||||
|
|
||||||
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
||||||
@@ -819,8 +1022,14 @@
|
|||||||
|
|
||||||
"jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="],
|
"jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="],
|
||||||
|
|
||||||
|
"kdbush": ["kdbush@4.0.2", "", {}, "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA=="],
|
||||||
|
|
||||||
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
||||||
|
|
||||||
|
"kysely": ["kysely@0.28.11", "", {}, "sha512-zpGIFg0HuoC893rIjYX1BETkVWdDnzTzF5e0kWXJFg5lE0k1/LfNWBejrcnOFu8Q2Rfq/hTDTU7XLUM8QOrpzg=="],
|
||||||
|
|
||||||
|
"leac": ["leac@0.6.0", "", {}, "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg=="],
|
||||||
|
|
||||||
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
||||||
|
|
||||||
"lie": ["lie@3.3.0", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="],
|
"lie": ["lie@3.3.0", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="],
|
||||||
@@ -877,6 +1086,8 @@
|
|||||||
|
|
||||||
"minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
|
"minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
|
||||||
|
|
||||||
|
"minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="],
|
||||||
|
|
||||||
"mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="],
|
"mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="],
|
||||||
|
|
||||||
"motion": ["motion@12.26.2", "", { "dependencies": { "framer-motion": "^12.26.2", "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-2Q6g0zK1gUJKhGT742DAe42LgietcdiJ3L3OcYAHCQaC1UkLnn6aC8S/obe4CxYTLAgid2asS1QdQ/blYfo5dw=="],
|
"motion": ["motion@12.26.2", "", { "dependencies": { "framer-motion": "^12.26.2", "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-2Q6g0zK1gUJKhGT742DAe42LgietcdiJ3L3OcYAHCQaC1UkLnn6aC8S/obe4CxYTLAgid2asS1QdQ/blYfo5dw=="],
|
||||||
@@ -889,10 +1100,18 @@
|
|||||||
|
|
||||||
"nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="],
|
"nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="],
|
||||||
|
|
||||||
|
"nanostores": ["nanostores@1.1.0", "", {}, "sha512-yJBmDJr18xy47dbNVlHcgdPrulSn1nhSE6Ns9vTG+Nx9VPT6iV1MD6aQFp/t52zpf82FhLLTXAXr30NuCnxvwA=="],
|
||||||
|
|
||||||
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
|
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
|
||||||
|
|
||||||
"next": ["next@16.1.0", "", { "dependencies": { "@next/env": "16.1.0", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.0", "@next/swc-darwin-x64": "16.1.0", "@next/swc-linux-arm64-gnu": "16.1.0", "@next/swc-linux-arm64-musl": "16.1.0", "@next/swc-linux-x64-gnu": "16.1.0", "@next/swc-linux-x64-musl": "16.1.0", "@next/swc-win32-arm64-msvc": "16.1.0", "@next/swc-win32-x64-msvc": "16.1.0", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-Y+KbmDbefYtHDDQKLNrmzE/YYzG2msqo2VXhzh5yrJ54tx/6TmGdkR5+kP9ma7i7LwZpZMfoY3m/AoPPPKxtVw=="],
|
"next": ["next@16.1.0", "", { "dependencies": { "@next/env": "16.1.0", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.0", "@next/swc-darwin-x64": "16.1.0", "@next/swc-linux-arm64-gnu": "16.1.0", "@next/swc-linux-arm64-musl": "16.1.0", "@next/swc-linux-x64-gnu": "16.1.0", "@next/swc-linux-x64-musl": "16.1.0", "@next/swc-win32-arm64-msvc": "16.1.0", "@next/swc-win32-x64-msvc": "16.1.0", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-Y+KbmDbefYtHDDQKLNrmzE/YYzG2msqo2VXhzh5yrJ54tx/6TmGdkR5+kP9ma7i7LwZpZMfoY3m/AoPPPKxtVw=="],
|
||||||
|
|
||||||
|
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
|
||||||
|
|
||||||
|
"node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
|
||||||
|
|
||||||
|
"npm-normalize-package-bin": ["npm-normalize-package-bin@5.0.0", "", {}, "sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag=="],
|
||||||
|
|
||||||
"nypm": ["nypm@0.6.4", "", { "dependencies": { "citty": "^0.2.0", "pathe": "^2.0.3", "tinyexec": "^1.0.2" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-1TvCKjZyyklN+JJj2TS3P4uSQEInrM/HkkuSXsEzm1ApPgBffOn8gFguNnZf07r/1X6vlryfIqMUkJKQMzlZiw=="],
|
"nypm": ["nypm@0.6.4", "", { "dependencies": { "citty": "^0.2.0", "pathe": "^2.0.3", "tinyexec": "^1.0.2" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-1TvCKjZyyklN+JJj2TS3P4uSQEInrM/HkkuSXsEzm1ApPgBffOn8gFguNnZf07r/1X6vlryfIqMUkJKQMzlZiw=="],
|
||||||
|
|
||||||
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||||
@@ -919,6 +1138,8 @@
|
|||||||
|
|
||||||
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
|
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
|
||||||
|
|
||||||
|
"parseley": ["parseley@0.12.1", "", { "dependencies": { "leac": "^0.6.0", "peberminta": "^0.9.0" } }, "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw=="],
|
||||||
|
|
||||||
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
||||||
|
|
||||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||||
@@ -929,6 +1150,24 @@
|
|||||||
|
|
||||||
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||||
|
|
||||||
|
"peberminta": ["peberminta@0.9.0", "", {}, "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ=="],
|
||||||
|
|
||||||
|
"pg": ["pg@8.18.0", "", { "dependencies": { "pg-connection-string": "^2.11.0", "pg-pool": "^3.11.0", "pg-protocol": "^1.11.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.3.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ=="],
|
||||||
|
|
||||||
|
"pg-cloudflare": ["pg-cloudflare@1.3.0", "", {}, "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ=="],
|
||||||
|
|
||||||
|
"pg-connection-string": ["pg-connection-string@2.11.0", "", {}, "sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ=="],
|
||||||
|
|
||||||
|
"pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="],
|
||||||
|
|
||||||
|
"pg-pool": ["pg-pool@3.11.0", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w=="],
|
||||||
|
|
||||||
|
"pg-protocol": ["pg-protocol@1.11.0", "", {}, "sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g=="],
|
||||||
|
|
||||||
|
"pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="],
|
||||||
|
|
||||||
|
"pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="],
|
||||||
|
|
||||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||||
|
|
||||||
"picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
@@ -937,10 +1176,24 @@
|
|||||||
|
|
||||||
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
||||||
|
|
||||||
|
"postgres": ["postgres@3.4.8", "", {}, "sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg=="],
|
||||||
|
|
||||||
|
"postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="],
|
||||||
|
|
||||||
|
"postgres-bytea": ["postgres-bytea@1.0.1", "", {}, "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ=="],
|
||||||
|
|
||||||
|
"postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="],
|
||||||
|
|
||||||
|
"postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
|
||||||
|
|
||||||
"potpack": ["potpack@1.0.2", "", {}, "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ=="],
|
"potpack": ["potpack@1.0.2", "", {}, "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ=="],
|
||||||
|
|
||||||
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
||||||
|
|
||||||
|
"prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="],
|
||||||
|
|
||||||
|
"proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="],
|
||||||
|
|
||||||
"promise-worker-transferable": ["promise-worker-transferable@1.0.4", "", { "dependencies": { "is-promise": "^2.1.0", "lie": "^3.0.2" } }, "sha512-bN+0ehEnrXfxV2ZQvU2PetO0n4gqBD4ulq3MI1WOPLgr7/Mg9yRQkX5+0v1vagr74ZTsl7XtzlaYDo2EuCeYJw=="],
|
"promise-worker-transferable": ["promise-worker-transferable@1.0.4", "", { "dependencies": { "is-promise": "^2.1.0", "lie": "^3.0.2" } }, "sha512-bN+0ehEnrXfxV2ZQvU2PetO0n4gqBD4ulq3MI1WOPLgr7/Mg9yRQkX5+0v1vagr74ZTsl7XtzlaYDo2EuCeYJw=="],
|
||||||
|
|
||||||
"prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
|
"prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
|
||||||
@@ -955,6 +1208,8 @@
|
|||||||
|
|
||||||
"react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
"react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||||
|
|
||||||
|
"react-promise-suspense": ["react-promise-suspense@0.3.4", "", { "dependencies": { "fast-deep-equal": "^2.0.1" } }, "sha512-I42jl7L3Ze6kZaq+7zXWSunBa3b1on5yfvUW6Eo/3fFOj6dZ5Bqmcd264nJbTK/gn1HjjILAjSwnZbV4RpSaNQ=="],
|
||||||
|
|
||||||
"react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="],
|
"react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="],
|
||||||
|
|
||||||
"react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="],
|
"react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="],
|
||||||
@@ -963,18 +1218,26 @@
|
|||||||
|
|
||||||
"react-use-measure": ["react-use-measure@2.1.7", "", { "peerDependencies": { "react": ">=16.13", "react-dom": ">=16.13" }, "optionalPeers": ["react-dom"] }, "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg=="],
|
"react-use-measure": ["react-use-measure@2.1.7", "", { "peerDependencies": { "react": ">=16.13", "react-dom": ">=16.13" }, "optionalPeers": ["react-dom"] }, "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg=="],
|
||||||
|
|
||||||
|
"read-cmd-shim": ["read-cmd-shim@6.0.0", "", {}, "sha512-1zM5HuOfagXCBWMN83fuFI/x+T/UhZ7k+KIzhrHXcQoeX5+7gmaDYjELQHmmzIodumBHeByBJT4QYS7ufAgs7A=="],
|
||||||
|
|
||||||
"reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="],
|
"reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="],
|
||||||
|
|
||||||
"regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
|
"regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
|
||||||
|
|
||||||
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
||||||
|
|
||||||
|
"resend": ["resend@4.8.0", "", { "dependencies": { "@react-email/render": "1.1.2" } }, "sha512-R8eBOFQDO6dzRTDmaMEdpqrkmgSjPpVXt4nGfWsZdYOet0kqra0xgbvTES6HmCriZEXbmGk3e0DiGIaLFTFSHA=="],
|
||||||
|
|
||||||
"resolve": ["resolve@2.0.0-next.5", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA=="],
|
"resolve": ["resolve@2.0.0-next.5", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA=="],
|
||||||
|
|
||||||
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
||||||
|
|
||||||
|
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
|
||||||
|
|
||||||
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
||||||
|
|
||||||
|
"rou3": ["rou3@0.7.12", "", {}, "sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg=="],
|
||||||
|
|
||||||
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
|
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
|
||||||
|
|
||||||
"safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="],
|
"safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="],
|
||||||
@@ -985,8 +1248,12 @@
|
|||||||
|
|
||||||
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||||
|
|
||||||
|
"selderee": ["selderee@0.11.0", "", { "dependencies": { "parseley": "^0.12.0" } }, "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA=="],
|
||||||
|
|
||||||
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||||
|
|
||||||
|
"set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="],
|
||||||
|
|
||||||
"set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
|
"set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
|
||||||
|
|
||||||
"set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="],
|
"set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="],
|
||||||
@@ -999,6 +1266,8 @@
|
|||||||
|
|
||||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||||
|
|
||||||
|
"shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="],
|
||||||
|
|
||||||
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
|
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
|
||||||
|
|
||||||
"side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
|
"side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
|
||||||
@@ -1007,10 +1276,18 @@
|
|||||||
|
|
||||||
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
||||||
|
|
||||||
|
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||||
|
|
||||||
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
|
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
|
||||||
|
|
||||||
|
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||||
|
|
||||||
"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=="],
|
||||||
|
|
||||||
|
"source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="],
|
||||||
|
|
||||||
|
"split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="],
|
||||||
|
|
||||||
"stats-gl": ["stats-gl@2.4.2", "", { "dependencies": { "@types/three": "*", "three": "^0.170.0" } }, "sha512-g5O9B0hm9CvnM36+v7SFl39T7hmAlv541tU81ME8YeSb3i1CIP5/QdDeSB3A0la0bKNHpxpwxOVRo2wFTYEosQ=="],
|
"stats-gl": ["stats-gl@2.4.2", "", { "dependencies": { "@types/three": "*", "three": "^0.170.0" } }, "sha512-g5O9B0hm9CvnM36+v7SFl39T7hmAlv541tU81ME8YeSb3i1CIP5/QdDeSB3A0la0bKNHpxpwxOVRo2wFTYEosQ=="],
|
||||||
|
|
||||||
"stats.js": ["stats.js@0.17.0", "", {}, "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw=="],
|
"stats.js": ["stats.js@0.17.0", "", {}, "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw=="],
|
||||||
@@ -1031,6 +1308,10 @@
|
|||||||
|
|
||||||
"styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="],
|
"styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="],
|
||||||
|
|
||||||
|
"supabase": ["supabase@2.75.3", "", { "dependencies": { "bin-links": "^6.0.0", "https-proxy-agent": "^7.0.2", "node-fetch": "^3.3.2", "tar": "7.5.7" }, "bin": { "supabase": "bin/supabase" } }, "sha512-j2uHJfK8TE7SxL6cd+kQixwkl8H5ASBkQfOEVjf3d3XS1Wixy4UO8WudDYNVriu1KuKzsNnkkSC/jTyHNXNrcQ=="],
|
||||||
|
|
||||||
|
"supercluster": ["supercluster@8.0.1", "", { "dependencies": { "kdbush": "^4.0.2" } }, "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ=="],
|
||||||
|
|
||||||
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||||
|
|
||||||
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
|
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
|
||||||
@@ -1043,6 +1324,8 @@
|
|||||||
|
|
||||||
"tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
|
"tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
|
||||||
|
|
||||||
|
"tar": ["tar@7.5.7", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ=="],
|
||||||
|
|
||||||
"three": ["three@0.182.0", "", {}, "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ=="],
|
"three": ["three@0.182.0", "", {}, "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ=="],
|
||||||
|
|
||||||
"three-bvh-csg": ["three-bvh-csg@0.0.17", "", { "peerDependencies": { "three": ">=0.151.0", "three-mesh-bvh": ">=0.6.6" } }, "sha512-iEkHDF8GRfGM6593Cuw8SnF1vfENCp46gIAtRzuL4nGXGWPcR1sbTBwM9ptDONb5twqlZp5WkAKya5aBKe2qcA=="],
|
"three-bvh-csg": ["three-bvh-csg@0.0.17", "", { "peerDependencies": { "three": ">=0.151.0", "three-mesh-bvh": ">=0.6.6" } }, "sha512-iEkHDF8GRfGM6593Cuw8SnF1vfENCp46gIAtRzuL4nGXGWPcR1sbTBwM9ptDONb5twqlZp5WkAKya5aBKe2qcA=="],
|
||||||
@@ -1117,11 +1400,13 @@
|
|||||||
|
|
||||||
"web": ["web@workspace:apps/editor"],
|
"web": ["web@workspace:apps/editor"],
|
||||||
|
|
||||||
|
"web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
|
||||||
|
|
||||||
"webgl-constants": ["webgl-constants@1.1.1", "", {}, "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg=="],
|
"webgl-constants": ["webgl-constants@1.1.1", "", {}, "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg=="],
|
||||||
|
|
||||||
"webgl-sdf-generator": ["webgl-sdf-generator@1.1.1", "", {}, "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA=="],
|
"webgl-sdf-generator": ["webgl-sdf-generator@1.1.1", "", {}, "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA=="],
|
||||||
|
|
||||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
"which": ["which@4.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="],
|
||||||
|
|
||||||
"which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="],
|
"which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="],
|
||||||
|
|
||||||
@@ -1133,6 +1418,14 @@
|
|||||||
|
|
||||||
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
|
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
|
||||||
|
|
||||||
|
"write-file-atomic": ["write-file-atomic@7.0.0", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" } }, "sha512-YnlPC6JqnZl6aO4uRc+dx5PHguiR9S6WeoLtpxNT9wIG+BDya7ZNE1q7KOjVgaA73hKhKLpVPgJ5QA9THQ5BRg=="],
|
||||||
|
|
||||||
|
"ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="],
|
||||||
|
|
||||||
|
"xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
|
||||||
|
|
||||||
|
"yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
|
||||||
|
|
||||||
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
||||||
|
|
||||||
"yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="],
|
"yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="],
|
||||||
@@ -1143,6 +1436,8 @@
|
|||||||
|
|
||||||
"zustand": ["zustand@5.0.10", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-U1AiltS1O9hSy3rul+Ub82ut2fqIAefiSuwECWt6jlMVUGejvf+5omLcRBSzqbRagSM3hQZbtzdeRc6QVScXTg=="],
|
"zustand": ["zustand@5.0.10", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-U1AiltS1O9hSy3rul+Ub82ut2fqIAefiSuwECWt6jlMVUGejvf+5omLcRBSzqbRagSM3hQZbtzdeRc6QVScXTg=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="],
|
||||||
|
|
||||||
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
||||||
|
|
||||||
"@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
|
"@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
|
||||||
@@ -1187,14 +1482,20 @@
|
|||||||
|
|
||||||
"@typescript-eslint/typescript-estree/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
"@typescript-eslint/typescript-estree/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
||||||
|
|
||||||
|
"cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||||
|
|
||||||
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||||
|
|
||||||
|
"gel/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
||||||
|
|
||||||
"glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
|
"glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
|
||||||
|
|
||||||
"next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
|
"next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
|
||||||
|
|
||||||
"postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
"postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||||
|
|
||||||
|
"react-promise-suspense/fast-deep-equal": ["fast-deep-equal@2.0.1", "", {}, "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w=="],
|
||||||
|
|
||||||
"sharp/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
"sharp/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
||||||
|
|
||||||
"stats-gl/three": ["three@0.170.0", "", {}, "sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ=="],
|
"stats-gl/three": ["three@0.170.0", "", {}, "sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ=="],
|
||||||
@@ -1207,8 +1508,54 @@
|
|||||||
|
|
||||||
"web/@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="],
|
"web/@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="],
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
||||||
|
|
||||||
|
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||||
|
|
||||||
"next/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
"next/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -10,7 +10,11 @@
|
|||||||
"format:check": "biome format",
|
"format:check": "biome format",
|
||||||
"check": "biome check",
|
"check": "biome check",
|
||||||
"check:fix": "biome check --write",
|
"check:fix": "biome check --write",
|
||||||
"check-types": "turbo run check-types"
|
"check-types": "turbo run check-types",
|
||||||
|
"db:start": "cd packages/db && supabase start",
|
||||||
|
"db:stop": "cd packages/db && supabase stop",
|
||||||
|
"db:reset": "cd packages/db && supabase db reset",
|
||||||
|
"db:status": "cd packages/db && supabase status"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-three/drei": "^10.7.7",
|
"@react-three/drei": "^10.7.7",
|
||||||
@@ -22,6 +26,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^2.3.13",
|
"@biomejs/biome": "^2.3.13",
|
||||||
|
"supabase": "2.75.3",
|
||||||
"turbo": "^2.8.1",
|
"turbo": "^2.8.1",
|
||||||
"typescript": "5.9.2",
|
"typescript": "5.9.2",
|
||||||
"ultracite": "^7.1.1"
|
"ultracite": "^7.1.1"
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
# @pascal-app/auth
|
||||||
|
|
||||||
|
Authentication package for Pascal Editor using Better Auth.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Magic Link Authentication** - Passwordless email-based authentication
|
||||||
|
- **Session Management** - Secure cookie-based sessions
|
||||||
|
- **Supabase Integration** - Uses Supabase as the database adapter
|
||||||
|
- **Type-safe** - Full TypeScript support with type inference
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### 1. Configure environment variables
|
||||||
|
|
||||||
|
Add these to `apps/editor/.env.local`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Better Auth
|
||||||
|
BETTER_AUTH_SECRET=<generate_with_openssl_rand_base64_32>
|
||||||
|
BETTER_AUTH_URL=http://localhost:3000
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate a secret for `BETTER_AUTH_SECRET`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openssl rand -base64 32
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Ensure database is running
|
||||||
|
|
||||||
|
Make sure you have Supabase running with the auth tables created. See `@pascal-app/db` package for setup.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Server-side (API routes, server actions)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { auth } from '@pascal-app/auth/server'
|
||||||
|
|
||||||
|
// Get session in server component or action
|
||||||
|
const session = await auth.api.getSession({ headers: request.headers })
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
return { error: 'Unauthorized' }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access user data
|
||||||
|
const userId = session.user.id
|
||||||
|
const email = session.user.email
|
||||||
|
```
|
||||||
|
|
||||||
|
### Client-side (React components)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
'use client'
|
||||||
|
|
||||||
|
import { authClient } from '@pascal-app/auth/client'
|
||||||
|
|
||||||
|
function SignInButton() {
|
||||||
|
const { signIn } = authClient
|
||||||
|
|
||||||
|
const handleSignIn = async (email: string) => {
|
||||||
|
await signIn.magicLink({
|
||||||
|
email,
|
||||||
|
callbackURL: '/dashboard',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return <button onClick={() => handleSignIn('user@example.com')}>Sign In</button>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using the auth hook
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
'use client'
|
||||||
|
|
||||||
|
import { authClient } from '@pascal-app/auth/client'
|
||||||
|
|
||||||
|
function Profile() {
|
||||||
|
const { data: session, isPending } = authClient.useSession()
|
||||||
|
|
||||||
|
if (isPending) return <div>Loading...</div>
|
||||||
|
if (!session) return <div>Not signed in</div>
|
||||||
|
|
||||||
|
return <div>Signed in as {session.user.email}</div>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Routes
|
||||||
|
|
||||||
|
The auth package requires an API route handler in your Next.js app:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// app/api/auth/[...all]/route.ts
|
||||||
|
import { auth } from '@pascal-app/auth/server'
|
||||||
|
import { toNextJsHandler } from 'better-auth/next-js'
|
||||||
|
|
||||||
|
export const { GET, POST } = toNextJsHandler(auth)
|
||||||
|
```
|
||||||
|
|
||||||
|
This handles all Better Auth endpoints:
|
||||||
|
- `/api/auth/sign-in/magic-link` - Send magic link
|
||||||
|
- `/api/auth/sign-in/magic-link/verify` - Verify magic link
|
||||||
|
- `/api/auth/sign-out` - Sign out
|
||||||
|
- `/api/auth/session` - Get session
|
||||||
|
- And more...
|
||||||
|
|
||||||
|
## Email Configuration
|
||||||
|
|
||||||
|
By default, magic links are logged to the console. To send actual emails, you'll need to configure an email provider in `packages/auth/src/server.ts`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
magicLink({
|
||||||
|
sendMagicLink: async ({ email, url }) => {
|
||||||
|
// Use Resend, SendGrid, or your preferred email service
|
||||||
|
await sendEmail({
|
||||||
|
to: email,
|
||||||
|
subject: 'Sign in to Pascal Editor',
|
||||||
|
html: `Click here to sign in: <a href="${url}">${url}</a>`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Database Schema
|
||||||
|
|
||||||
|
The auth package requires these tables (created by `@pascal-app/db` migrations):
|
||||||
|
|
||||||
|
- `users` - User accounts
|
||||||
|
- `sessions` - Active sessions
|
||||||
|
- `accounts` - OAuth provider accounts (for future use)
|
||||||
|
- `verification_tokens` - Magic link tokens
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
- Session cookies are httpOnly and secure (in production)
|
||||||
|
- Sessions expire after 7 days
|
||||||
|
- Session cache is enabled for 5 minutes to reduce database queries
|
||||||
|
- Magic link tokens expire after 15 minutes
|
||||||
|
- All sensitive operations require valid session tokens
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "@pascal-app/auth",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
"./server": {
|
||||||
|
"types": "./src/server.ts",
|
||||||
|
"default": "./src/server.ts"
|
||||||
|
},
|
||||||
|
"./client": {
|
||||||
|
"types": "./src/client.ts",
|
||||||
|
"default": "./src/client.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@pascal-app/db": "*",
|
||||||
|
"better-auth": "^1.4.18",
|
||||||
|
"resend": "^4.0.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@repo/typescript-config": "*",
|
||||||
|
"typescript": "5.9.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { magicLinkClient } from 'better-auth/client/plugins'
|
||||||
|
import { createAuthClient } from 'better-auth/react'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the auth base URL
|
||||||
|
* In development: use the editor URL (localhost:3000)
|
||||||
|
* In production: use the same origin
|
||||||
|
*/
|
||||||
|
function getAuthURL(): string {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
return window.location.origin
|
||||||
|
}
|
||||||
|
|
||||||
|
// SSR fallback
|
||||||
|
return process.env.BETTER_AUTH_URL || 'http://localhost:3000'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auth client instance
|
||||||
|
* Configured for magic link authentication
|
||||||
|
*/
|
||||||
|
export const authClient = createAuthClient({
|
||||||
|
baseURL: getAuthURL(),
|
||||||
|
plugins: [magicLinkClient()],
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export types for use in components
|
||||||
|
*/
|
||||||
|
export type AuthState = {
|
||||||
|
user: (typeof authClient)['$Infer']['Session']['user'] | null
|
||||||
|
session: (typeof authClient)['$Infer']['Session']['session'] | null
|
||||||
|
isLoading: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type User = NonNullable<AuthState['user']>
|
||||||
|
export type Session = NonNullable<AuthState['session']>
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import type { Database } from '@pascal-app/db'
|
||||||
|
import { schema } from '@pascal-app/db'
|
||||||
|
import type { BetterAuthOptions } from 'better-auth'
|
||||||
|
import { betterAuth } from 'better-auth'
|
||||||
|
import { drizzleAdapter } from 'better-auth/adapters/drizzle'
|
||||||
|
import { magicLink } from 'better-auth/plugins'
|
||||||
|
|
||||||
|
export interface SendMagicLinkParams {
|
||||||
|
email: string
|
||||||
|
url: string
|
||||||
|
token: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuthConfig {
|
||||||
|
db: Database
|
||||||
|
appName: string
|
||||||
|
baseURL: string
|
||||||
|
secret: string
|
||||||
|
/** Callback to send magic link emails */
|
||||||
|
sendMagicLink?: (params: SendMagicLinkParams) => Promise<void>
|
||||||
|
/** Additional plugins to add (e.g., nextCookies for web) */
|
||||||
|
additionalPlugins?: BetterAuthOptions['plugins']
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Better Auth instance with full configuration including:
|
||||||
|
* - Magic link authentication
|
||||||
|
* - Custom session with activePropertyId
|
||||||
|
* - Session cookie caching
|
||||||
|
*/
|
||||||
|
export function createAuth(config: AuthConfig): ReturnType<typeof betterAuth> {
|
||||||
|
return betterAuth({
|
||||||
|
appName: config.appName,
|
||||||
|
baseURL: config.baseURL,
|
||||||
|
secret: config.secret,
|
||||||
|
basePath: '/api/auth',
|
||||||
|
database: drizzleAdapter(config.db, {
|
||||||
|
provider: 'pg',
|
||||||
|
usePlural: true,
|
||||||
|
schema,
|
||||||
|
}),
|
||||||
|
advanced: {
|
||||||
|
database: {
|
||||||
|
generateId: false, // Use our prefixed nanoid IDs from schema
|
||||||
|
},
|
||||||
|
},
|
||||||
|
session: {
|
||||||
|
// Session caching to reduce database queries
|
||||||
|
cookieCache: {
|
||||||
|
enabled: true,
|
||||||
|
maxAge: 5 * 60, // Cache duration in seconds (5 minutes)
|
||||||
|
},
|
||||||
|
additionalFields: {
|
||||||
|
// Additional fields for the session table
|
||||||
|
activePropertyId: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
...(config.additionalPlugins ?? []),
|
||||||
|
// Magic link authentication
|
||||||
|
...(config.sendMagicLink
|
||||||
|
? [
|
||||||
|
magicLink({
|
||||||
|
sendMagicLink: config.sendMagicLink,
|
||||||
|
expiresIn: 300, // 5 minutes
|
||||||
|
disableSignUp: false, // Allow new users to sign up via magic link
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Auth = ReturnType<typeof createAuth>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"extends": "@repo/typescript-config/base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src"
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
# @pascal-app/db
|
||||||
|
|
||||||
|
Database package for Pascal Editor with Supabase.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### 1. Install Dependencies
|
||||||
|
|
||||||
|
From the monorepo root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
This installs Supabase CLI as a dev dependency.
|
||||||
|
|
||||||
|
### 2. Start Supabase locally
|
||||||
|
|
||||||
|
From the monorepo root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun db:start
|
||||||
|
```
|
||||||
|
|
||||||
|
This will start a local Supabase instance with PostgreSQL, PostgREST, and Studio.
|
||||||
|
|
||||||
|
### 3. Check Supabase status
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun db:status
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll see output like:
|
||||||
|
|
||||||
|
```
|
||||||
|
API URL: http://127.0.0.1:54321
|
||||||
|
DB URL: postgresql://postgres:postgres@127.0.0.1:54322/postgres
|
||||||
|
Studio URL: http://127.0.0.1:54323
|
||||||
|
Anon key: eyJh...
|
||||||
|
Service role key: eyJh...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Configure environment variables
|
||||||
|
|
||||||
|
Add these to `apps/editor/.env.local`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Supabase
|
||||||
|
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
|
||||||
|
NEXT_PUBLIC_SUPABASE_ANON_KEY=<your_anon_key>
|
||||||
|
SUPABASE_SERVICE_ROLE_KEY=<your_service_role_key>
|
||||||
|
|
||||||
|
# Better Auth
|
||||||
|
BETTER_AUTH_SECRET=<generate_with_openssl_rand_base64_32>
|
||||||
|
BETTER_AUTH_URL=http://localhost:3000
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate a secret for `BETTER_AUTH_SECRET`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openssl rand -base64 32
|
||||||
|
```
|
||||||
|
|
||||||
|
## Migrations
|
||||||
|
|
||||||
|
Migrations are located in `supabase/migrations/`.
|
||||||
|
|
||||||
|
### Apply migrations
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun db:reset # Resets and applies all migrations
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create a new migration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd packages/db
|
||||||
|
bunx supabase migration new <migration_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Database Schema
|
||||||
|
|
||||||
|
### Auth Tables (Better Auth)
|
||||||
|
|
||||||
|
- `users` - User accounts
|
||||||
|
- `sessions` - Active sessions
|
||||||
|
- `accounts` - OAuth provider accounts
|
||||||
|
- `verification_tokens` - Magic link tokens
|
||||||
|
|
||||||
|
### Application Tables
|
||||||
|
|
||||||
|
- `properties` - User properties
|
||||||
|
- `properties_addresses` - Property addresses with Google Maps data
|
||||||
|
- `properties_models` - Scene graph models (versions)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Client-side (with RLS)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { supabase } from '@pascal-app/db/client'
|
||||||
|
|
||||||
|
// RLS policies automatically filter by authenticated user
|
||||||
|
const { data } = await supabase.from('properties').select('*')
|
||||||
|
```
|
||||||
|
|
||||||
|
### Server-side (service role)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { supabaseAdmin } from '@pascal-app/db/server'
|
||||||
|
|
||||||
|
// Bypasses RLS - you must manually filter by user_id
|
||||||
|
const { data } = await supabaseAdmin
|
||||||
|
.from('properties')
|
||||||
|
.select('*')
|
||||||
|
.eq('owner_id', userId)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Supabase Studio
|
||||||
|
|
||||||
|
Access the local Supabase Studio at: http://127.0.0.1:54323
|
||||||
|
|
||||||
|
Use this to:
|
||||||
|
- Browse tables and data
|
||||||
|
- Run SQL queries
|
||||||
|
- View logs
|
||||||
|
- Manage RLS policies
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "@pascal-app/db",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./src/index.ts",
|
||||||
|
"default": "./src/index.ts"
|
||||||
|
},
|
||||||
|
"./types": {
|
||||||
|
"types": "./src/types.ts",
|
||||||
|
"default": "./src/types.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@supabase/supabase-js": "^2.95.3",
|
||||||
|
"drizzle-orm": "^0.39.0",
|
||||||
|
"drizzle-zod": "^0.5.1",
|
||||||
|
"nanoid": "^5.0.9",
|
||||||
|
"postgres": "^3.4.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@repo/typescript-config": "*",
|
||||||
|
"drizzle-kit": "^0.30.0",
|
||||||
|
"typescript": "5.9.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { drizzle } from 'drizzle-orm/postgres-js'
|
||||||
|
import postgres from 'postgres'
|
||||||
|
import * as schema from './schema'
|
||||||
|
|
||||||
|
const connectionString = process.env.POSTGRES_URL!
|
||||||
|
|
||||||
|
const client = postgres(connectionString, { prepare: false })
|
||||||
|
|
||||||
|
export const db = drizzle({ client, schema })
|
||||||
|
|
||||||
|
export type Database = typeof db
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { type AnyColumn, type SQL, sql } from 'drizzle-orm'
|
||||||
|
import { text, timestamp } from 'drizzle-orm/pg-core'
|
||||||
|
import { customAlphabet } from 'nanoid'
|
||||||
|
|
||||||
|
const alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
|
||||||
|
const nanoid = customAlphabet(alphabet, 16)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a unique ID with optional prefix
|
||||||
|
* @example createId('user') => 'user_Abc123...'
|
||||||
|
*/
|
||||||
|
export const createId = (prefix?: string) => {
|
||||||
|
const id = nanoid()
|
||||||
|
return prefix ? `${prefix}_${id}` : id
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Primary key column with auto-generated prefixed ID
|
||||||
|
* @example id('user') => text('id').notNull().primaryKey().$defaultFn(() => createId('user'))
|
||||||
|
*/
|
||||||
|
export const id = (prefix?: string) =>
|
||||||
|
text('id')
|
||||||
|
.notNull()
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => createId(prefix))
|
||||||
|
.$type<string>()
|
||||||
|
|
||||||
|
export const createdAt = timestamp('created_at', { withTimezone: true }).notNull().defaultNow()
|
||||||
|
|
||||||
|
export const updatedAt = timestamp('updated_at', { withTimezone: true })
|
||||||
|
.notNull()
|
||||||
|
.defaultNow()
|
||||||
|
.$onUpdate(() => new Date())
|
||||||
|
|
||||||
|
export const deletedAt = timestamp('deleted_at', { withTimezone: true })
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard timestamp columns for created_at and updated_at
|
||||||
|
*/
|
||||||
|
export const timestamps = {
|
||||||
|
createdAt,
|
||||||
|
updatedAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alias for backwards compatibility with existing code
|
||||||
|
export const timestampsColumns = timestamps
|
||||||
|
|
||||||
|
export const timestampsColumnsSoftDelete = {
|
||||||
|
...timestampsColumns,
|
||||||
|
deletedAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SQL helper for case-insensitive comparison
|
||||||
|
*/
|
||||||
|
export const lower = (column: AnyColumn): SQL => sql`lower(${column})`
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Database package
|
||||||
|
* Exports Drizzle ORM and types
|
||||||
|
* Note: Supabase clients are kept in the app's lib directory to avoid build-time initialization
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type { Database as SupabaseDatabase } from './types'
|
||||||
|
|
||||||
|
// Drizzle exports
|
||||||
|
export { type Database, db } from './drizzle'
|
||||||
|
export * from './schema'
|
||||||
|
|
||||||
|
import * as dbSchema from './schema'
|
||||||
|
export const schema = dbSchema
|
||||||
|
export {
|
||||||
|
createId,
|
||||||
|
deletedAt,
|
||||||
|
id,
|
||||||
|
lower,
|
||||||
|
timestamps,
|
||||||
|
timestampsColumns,
|
||||||
|
timestampsColumnsSoftDelete,
|
||||||
|
} from './helpers'
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { pgTable } from 'drizzle-orm/pg-core'
|
||||||
|
import { createInsertSchema, createSelectSchema } from 'drizzle-zod'
|
||||||
|
import { id, timestamps } from '../../helpers'
|
||||||
|
import { users } from './users'
|
||||||
|
|
||||||
|
export const accounts = pgTable('auth_accounts', (t) => ({
|
||||||
|
id: id('account'),
|
||||||
|
userId: t
|
||||||
|
.text('user_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.id, { onDelete: 'cascade' }),
|
||||||
|
providerId: t.text('provider_id').notNull(),
|
||||||
|
accountId: t.text('account_id').notNull(),
|
||||||
|
password: t.text('password'),
|
||||||
|
accessToken: t.text('access_token'),
|
||||||
|
refreshToken: t.text('refresh_token'),
|
||||||
|
idToken: t.text('id_token'),
|
||||||
|
accessTokenExpiresAt: t.timestamp('access_token_expires_at', {
|
||||||
|
withTimezone: true,
|
||||||
|
}),
|
||||||
|
refreshTokenExpiresAt: t.timestamp('refresh_token_expires_at', {
|
||||||
|
withTimezone: true,
|
||||||
|
}),
|
||||||
|
scope: t.text('scope'),
|
||||||
|
...timestamps,
|
||||||
|
})).enableRLS()
|
||||||
|
|
||||||
|
export type Account = typeof accounts.$inferSelect
|
||||||
|
export type NewAccount = typeof accounts.$inferInsert
|
||||||
|
export const insertAccountSchema = createInsertSchema(accounts)
|
||||||
|
export const selectAccountSchema = createSelectSchema(accounts)
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { pgTable } from 'drizzle-orm/pg-core'
|
||||||
|
import { createInsertSchema, createSelectSchema } from 'drizzle-zod'
|
||||||
|
import { createdAt, id } from '../../helpers'
|
||||||
|
|
||||||
|
export const jwks = pgTable('auth_jwks', (t) => ({
|
||||||
|
id: id('jwks'),
|
||||||
|
publicKey: t.text('public_key').notNull(),
|
||||||
|
privateKey: t.text('private_key').notNull(),
|
||||||
|
createdAt,
|
||||||
|
})).enableRLS()
|
||||||
|
|
||||||
|
export type Jwks = typeof jwks.$inferSelect
|
||||||
|
export type NewJwks = typeof jwks.$inferInsert
|
||||||
|
export const insertJwksSchema = createInsertSchema(jwks)
|
||||||
|
export const selectJwksSchema = createSelectSchema(jwks)
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { pgTable } from 'drizzle-orm/pg-core'
|
||||||
|
import { createInsertSchema, createSelectSchema } from 'drizzle-zod'
|
||||||
|
import { id, timestamps } from '../../helpers'
|
||||||
|
import { users } from './users'
|
||||||
|
|
||||||
|
export const sessions = pgTable('auth_sessions', (t) => ({
|
||||||
|
id: id('session'),
|
||||||
|
userId: t
|
||||||
|
.text('user_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.id, { onDelete: 'cascade' }),
|
||||||
|
expiresAt: t.timestamp('expires_at', { withTimezone: true }),
|
||||||
|
token: t.text('token').notNull(),
|
||||||
|
ipAddress: t.text('ip_address'),
|
||||||
|
userAgent: t.text('user_agent'),
|
||||||
|
// Custom: active property for the session context
|
||||||
|
activePropertyId: t.text('active_property_id'),
|
||||||
|
// Admin plugin support: tracks who is impersonating this session
|
||||||
|
impersonatedBy: t
|
||||||
|
.text('impersonated_by')
|
||||||
|
.references(() => users.id, { onDelete: 'set null' }),
|
||||||
|
...timestamps,
|
||||||
|
})).enableRLS()
|
||||||
|
|
||||||
|
export type Session = typeof sessions.$inferSelect
|
||||||
|
export type NewSession = typeof sessions.$inferInsert
|
||||||
|
export const insertSessionSchema = createInsertSchema(sessions)
|
||||||
|
export const selectSessionSchema = createSelectSchema(sessions)
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { pgEnum, pgTable, uniqueIndex } from 'drizzle-orm/pg-core'
|
||||||
|
import { createInsertSchema, createSelectSchema } from 'drizzle-zod'
|
||||||
|
import { id, lower, timestampsColumns } from '../../helpers'
|
||||||
|
|
||||||
|
export const USER_ROLES = ['user', 'admin'] as const
|
||||||
|
export const userRoles = pgEnum('auth_user_roles', USER_ROLES)
|
||||||
|
|
||||||
|
export const users = pgTable(
|
||||||
|
'auth_users',
|
||||||
|
(t) => ({
|
||||||
|
id: id('user'),
|
||||||
|
email: t.text('email').notNull(),
|
||||||
|
emailVerified: t.boolean('email_verified').notNull().default(false),
|
||||||
|
name: t.text('name').notNull(),
|
||||||
|
image: t.text('image'),
|
||||||
|
role: userRoles('role').notNull().default('user'),
|
||||||
|
banned: t.boolean('banned').notNull().default(false),
|
||||||
|
banReason: t.text('ban_reason'),
|
||||||
|
banExpires: t.timestamp('ban_expires', { withTimezone: true }),
|
||||||
|
...timestampsColumns,
|
||||||
|
}),
|
||||||
|
(t) => [uniqueIndex('email_unique_index').on(lower(t.email))],
|
||||||
|
).enableRLS()
|
||||||
|
|
||||||
|
export type User = typeof users.$inferSelect
|
||||||
|
export type NewUser = typeof users.$inferInsert
|
||||||
|
export const insertUserSchema = createInsertSchema(users)
|
||||||
|
export const selectUserSchema = createSelectSchema(users)
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { index, pgTable } from 'drizzle-orm/pg-core'
|
||||||
|
import { createInsertSchema, createSelectSchema } from 'drizzle-zod'
|
||||||
|
import { id, timestamps } from '../../helpers'
|
||||||
|
|
||||||
|
export const verifications = pgTable(
|
||||||
|
'auth_verifications',
|
||||||
|
(t) => ({
|
||||||
|
id: id('verification'),
|
||||||
|
value: t.text('value').notNull(),
|
||||||
|
identifier: t.text('identifier').notNull(),
|
||||||
|
expiresAt: t.timestamp('expires_at', {
|
||||||
|
withTimezone: true,
|
||||||
|
}),
|
||||||
|
...timestamps,
|
||||||
|
}),
|
||||||
|
(t) => [index('verification_identifier_index').on(t.identifier)],
|
||||||
|
).enableRLS()
|
||||||
|
|
||||||
|
export type Verification = typeof verifications.$inferSelect
|
||||||
|
export type NewVerification = typeof verifications.$inferInsert
|
||||||
|
export const insertVerificationSchema = createInsertSchema(verifications)
|
||||||
|
export const selectVerificationSchema = createSelectSchema(verifications)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// Auth tables
|
||||||
|
export * from './auth/accounts'
|
||||||
|
export * from './auth/jwks'
|
||||||
|
export * from './auth/sessions'
|
||||||
|
export * from './auth/users'
|
||||||
|
export * from './auth/verifications'
|
||||||
|
|
||||||
|
// Property tables
|
||||||
|
export * from './properties/addresses'
|
||||||
|
export * from './properties/models'
|
||||||
|
export * from './properties/properties'
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { pgTable, unique } from 'drizzle-orm/pg-core'
|
||||||
|
import { z } from 'zod'
|
||||||
|
import { id, timestampsColumns } from '../../helpers'
|
||||||
|
|
||||||
|
export const addresses = pgTable(
|
||||||
|
'properties_addresses',
|
||||||
|
(t) => ({
|
||||||
|
id: id('address'),
|
||||||
|
streetNumber: t.text('street_number'),
|
||||||
|
route: t.text('route'),
|
||||||
|
routeShort: t.text('route_short'),
|
||||||
|
neighborhood: t.text('neighborhood'),
|
||||||
|
city: t.text('city'),
|
||||||
|
county: t.text('county'),
|
||||||
|
state: t.text('state'),
|
||||||
|
stateLong: t.text('state_long'),
|
||||||
|
postalCode: t.text('postal_code'),
|
||||||
|
postalCodeSuffix: t.text('postal_code_suffix'),
|
||||||
|
country: t.text('country'),
|
||||||
|
countryLong: t.text('country_long'),
|
||||||
|
latitude: t.numeric('latitude'),
|
||||||
|
longitude: t.numeric('longitude'),
|
||||||
|
rawJson: t.jsonb('raw_json'),
|
||||||
|
...timestampsColumns,
|
||||||
|
}),
|
||||||
|
(t) => [
|
||||||
|
// Unique constraint on core address components to prevent duplicates
|
||||||
|
unique('address_components_unique').on(t.streetNumber, t.route, t.city, t.state, t.postalCode),
|
||||||
|
],
|
||||||
|
).enableRLS()
|
||||||
|
|
||||||
|
// Create address schema manually to avoid issues with generated columns
|
||||||
|
export const addressSchema = z.object({
|
||||||
|
streetNumber: z.string().optional(),
|
||||||
|
route: z.string().optional(),
|
||||||
|
routeShort: z.string().optional(),
|
||||||
|
neighborhood: z.string().optional(),
|
||||||
|
city: z.string().optional(),
|
||||||
|
county: z.string().optional(),
|
||||||
|
state: z.string().optional(),
|
||||||
|
stateLong: z.string().optional(),
|
||||||
|
postalCode: z.string().optional(),
|
||||||
|
postalCodeSuffix: z.string().optional(),
|
||||||
|
country: z.string().default('US'),
|
||||||
|
countryLong: z.string().optional(),
|
||||||
|
latitude: z.string().optional(),
|
||||||
|
longitude: z.string().optional(),
|
||||||
|
rawJson: z.record(z.string(), z.unknown()).optional(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export type AddressSchema = z.infer<typeof addressSchema>
|
||||||
|
export type Address = typeof addresses.$inferSelect
|
||||||
|
export type NewAddress = typeof addresses.$inferInsert
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { relations } from 'drizzle-orm'
|
||||||
|
import { pgTable } from 'drizzle-orm/pg-core'
|
||||||
|
import { createInsertSchema, createSelectSchema } from 'drizzle-zod'
|
||||||
|
import { id, timestampsColumnsSoftDelete } from '../../helpers'
|
||||||
|
import { properties } from './properties'
|
||||||
|
|
||||||
|
export const models = pgTable('properties_models', (t) => ({
|
||||||
|
id: id('model'),
|
||||||
|
name: t.text('name'),
|
||||||
|
version: t.integer('version').default(1),
|
||||||
|
description: t.text('description'),
|
||||||
|
draft: t.boolean('draft').default(true),
|
||||||
|
propertyId: t
|
||||||
|
.text('property_id')
|
||||||
|
.references(() => properties.id, { onDelete: 'set null' }),
|
||||||
|
sceneGraph: t.jsonb('scene_graph'),
|
||||||
|
metadata: t.jsonb('metadata'),
|
||||||
|
...timestampsColumnsSoftDelete,
|
||||||
|
})).enableRLS()
|
||||||
|
|
||||||
|
export const modelsRelations = relations(models, ({ one }) => ({
|
||||||
|
property: one(properties, {
|
||||||
|
fields: [models.propertyId],
|
||||||
|
references: [properties.id],
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
|
export type Model = typeof models.$inferSelect
|
||||||
|
export type NewModel = typeof models.$inferInsert
|
||||||
|
export const insertModelSchema = createInsertSchema(models)
|
||||||
|
export const selectModelSchema = createSelectSchema(models)
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { relations } from 'drizzle-orm'
|
||||||
|
import { index, pgTable } from 'drizzle-orm/pg-core'
|
||||||
|
import { createInsertSchema, createSelectSchema } from 'drizzle-zod'
|
||||||
|
import { id, timestampsColumns } from '../../helpers'
|
||||||
|
import { users } from '../auth/users'
|
||||||
|
import { addresses } from './addresses'
|
||||||
|
|
||||||
|
export const properties = pgTable(
|
||||||
|
'properties',
|
||||||
|
(t) => ({
|
||||||
|
id: id('property'),
|
||||||
|
name: t.text('name'),
|
||||||
|
addressId: t
|
||||||
|
.text('address_id')
|
||||||
|
.references(() => addresses.id, { onDelete: 'set null' })
|
||||||
|
.unique(),
|
||||||
|
ownerId: t
|
||||||
|
.text('owner_id')
|
||||||
|
.references(() => users.id, { onDelete: 'set null' }),
|
||||||
|
detailsJson: t.jsonb('details_json'),
|
||||||
|
metadata: t.jsonb('metadata'),
|
||||||
|
...timestampsColumns,
|
||||||
|
}),
|
||||||
|
(t) => [
|
||||||
|
index('property_address_idx').on(t.addressId),
|
||||||
|
index('property_owner_idx').on(t.ownerId),
|
||||||
|
],
|
||||||
|
).enableRLS()
|
||||||
|
|
||||||
|
export const propertiesRelations = relations(properties, ({ one }) => ({
|
||||||
|
address: one(addresses, {
|
||||||
|
fields: [properties.addressId],
|
||||||
|
references: [addresses.id],
|
||||||
|
}),
|
||||||
|
owner: one(users, {
|
||||||
|
fields: [properties.ownerId],
|
||||||
|
references: [users.id],
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
|
export type Property = typeof properties.$inferSelect
|
||||||
|
export type NewProperty = typeof properties.$inferInsert
|
||||||
|
export const insertPropertySchema = createInsertSchema(properties)
|
||||||
|
export const selectPropertySchema = createSelectSchema(properties)
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
/**
|
||||||
|
* Database types for Supabase
|
||||||
|
* Generated from database schema
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type Json = string | number | boolean | null | { [key: string]: Json | undefined } | Json[]
|
||||||
|
|
||||||
|
export interface Database {
|
||||||
|
public: {
|
||||||
|
Tables: {
|
||||||
|
properties: {
|
||||||
|
Row: {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
owner_id: string
|
||||||
|
created_at: string
|
||||||
|
updated_at: string
|
||||||
|
}
|
||||||
|
Insert: {
|
||||||
|
id?: string
|
||||||
|
name: string
|
||||||
|
owner_id: string
|
||||||
|
created_at?: string
|
||||||
|
updated_at?: string
|
||||||
|
}
|
||||||
|
Update: {
|
||||||
|
id?: string
|
||||||
|
name?: string
|
||||||
|
owner_id?: string
|
||||||
|
created_at?: string
|
||||||
|
updated_at?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
properties_addresses: {
|
||||||
|
Row: {
|
||||||
|
id: string
|
||||||
|
property_id: string
|
||||||
|
formatted_address: string
|
||||||
|
street_number: string | null
|
||||||
|
route: string | null
|
||||||
|
locality: string | null
|
||||||
|
administrative_area_level_1: string | null
|
||||||
|
administrative_area_level_2: string | null
|
||||||
|
country: string | null
|
||||||
|
postal_code: string | null
|
||||||
|
latitude: number | null
|
||||||
|
longitude: number | null
|
||||||
|
place_id: string | null
|
||||||
|
created_at: string
|
||||||
|
updated_at: string
|
||||||
|
}
|
||||||
|
Insert: {
|
||||||
|
id?: string
|
||||||
|
property_id: string
|
||||||
|
formatted_address: string
|
||||||
|
street_number?: string | null
|
||||||
|
route?: string | null
|
||||||
|
locality?: string | null
|
||||||
|
administrative_area_level_1?: string | null
|
||||||
|
administrative_area_level_2?: string | null
|
||||||
|
country?: string | null
|
||||||
|
postal_code?: string | null
|
||||||
|
latitude?: number | null
|
||||||
|
longitude?: number | null
|
||||||
|
place_id?: string | null
|
||||||
|
created_at?: string
|
||||||
|
updated_at?: string
|
||||||
|
}
|
||||||
|
Update: {
|
||||||
|
id?: string
|
||||||
|
property_id?: string
|
||||||
|
formatted_address?: string
|
||||||
|
street_number?: string | null
|
||||||
|
route?: string | null
|
||||||
|
locality?: string | null
|
||||||
|
administrative_area_level_1?: string | null
|
||||||
|
administrative_area_level_2?: string | null
|
||||||
|
country?: string | null
|
||||||
|
postal_code?: string | null
|
||||||
|
latitude?: number | null
|
||||||
|
longitude?: number | null
|
||||||
|
place_id?: string | null
|
||||||
|
created_at?: string
|
||||||
|
updated_at?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
properties_models: {
|
||||||
|
Row: {
|
||||||
|
id: string
|
||||||
|
property_id: string
|
||||||
|
name: string
|
||||||
|
version: number
|
||||||
|
draft: boolean
|
||||||
|
scene_graph: Json | null
|
||||||
|
created_at: string
|
||||||
|
updated_at: string
|
||||||
|
}
|
||||||
|
Insert: {
|
||||||
|
id?: string
|
||||||
|
property_id: string
|
||||||
|
name: string
|
||||||
|
version?: number
|
||||||
|
draft?: boolean
|
||||||
|
scene_graph?: Json | null
|
||||||
|
created_at?: string
|
||||||
|
updated_at?: string
|
||||||
|
}
|
||||||
|
Update: {
|
||||||
|
id?: string
|
||||||
|
property_id?: string
|
||||||
|
name?: string
|
||||||
|
version?: number
|
||||||
|
draft?: boolean
|
||||||
|
scene_graph?: Json | null
|
||||||
|
created_at?: string
|
||||||
|
updated_at?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Views: {}
|
||||||
|
Functions: {}
|
||||||
|
Enums: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
main
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
v2.75.0
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# Supabase local development configuration
|
||||||
|
# This file is used by `supabase start` for local development
|
||||||
|
|
||||||
|
project_id = "pascal-editor"
|
||||||
|
|
||||||
|
[api]
|
||||||
|
enabled = true
|
||||||
|
port = 54321
|
||||||
|
schemas = ["public"]
|
||||||
|
extra_search_path = ["public"]
|
||||||
|
max_rows = 1000
|
||||||
|
|
||||||
|
[db]
|
||||||
|
port = 54322
|
||||||
|
shadow_port = 54320
|
||||||
|
major_version = 17
|
||||||
|
|
||||||
|
[studio]
|
||||||
|
enabled = true
|
||||||
|
port = 54323
|
||||||
|
|
||||||
|
[inbucket]
|
||||||
|
enabled = true
|
||||||
|
port = 54324
|
||||||
|
smtp_port = 54325
|
||||||
|
pop3_port = 54326
|
||||||
|
|
||||||
|
[auth]
|
||||||
|
enabled = true
|
||||||
|
site_url = "http://localhost:3000"
|
||||||
|
additional_redirect_urls = ["http://localhost:3000"]
|
||||||
|
jwt_expiry = 3600
|
||||||
|
enable_signup = true
|
||||||
|
|
||||||
|
[auth.email]
|
||||||
|
enable_signup = true
|
||||||
|
double_confirm_changes = false
|
||||||
|
enable_confirmations = false
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
-- Better Auth tables (copied from working monorepo)
|
||||||
|
-- Using auth_ prefix and snake_case columns
|
||||||
|
|
||||||
|
CREATE TYPE "public"."auth_user_roles" AS ENUM('user', 'admin');
|
||||||
|
|
||||||
|
CREATE TABLE "auth_accounts" (
|
||||||
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
|
"user_id" text NOT NULL,
|
||||||
|
"provider_id" text NOT NULL,
|
||||||
|
"account_id" text NOT NULL,
|
||||||
|
"password" text,
|
||||||
|
"access_token" text,
|
||||||
|
"refresh_token" text,
|
||||||
|
"id_token" text,
|
||||||
|
"access_token_expires_at" timestamp with time zone,
|
||||||
|
"refresh_token_expires_at" timestamp with time zone,
|
||||||
|
"scope" text,
|
||||||
|
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||||
|
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE "auth_accounts" ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
CREATE TABLE "auth_jwks" (
|
||||||
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
|
"public_key" text NOT NULL,
|
||||||
|
"private_key" text NOT NULL,
|
||||||
|
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE "auth_jwks" ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
CREATE TABLE "auth_sessions" (
|
||||||
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
|
"user_id" text NOT NULL,
|
||||||
|
"expires_at" timestamp with time zone,
|
||||||
|
"token" text NOT NULL,
|
||||||
|
"ip_address" text,
|
||||||
|
"user_agent" text,
|
||||||
|
"impersonated_by" text,
|
||||||
|
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||||
|
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE "auth_sessions" ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
CREATE TABLE "auth_users" (
|
||||||
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
|
"email" text NOT NULL,
|
||||||
|
"email_verified" boolean DEFAULT false NOT NULL,
|
||||||
|
"name" text NOT NULL,
|
||||||
|
"image" text,
|
||||||
|
"role" "auth_user_roles" DEFAULT 'user' NOT NULL,
|
||||||
|
"banned" boolean DEFAULT false NOT NULL,
|
||||||
|
"ban_reason" text,
|
||||||
|
"ban_expires" timestamp with time zone,
|
||||||
|
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||||
|
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE "auth_users" ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
CREATE TABLE "auth_verifications" (
|
||||||
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
|
"value" text NOT NULL,
|
||||||
|
"identifier" text NOT NULL,
|
||||||
|
"expires_at" timestamp with time zone,
|
||||||
|
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||||
|
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE "auth_verifications" ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
ALTER TABLE "auth_accounts" ADD CONSTRAINT "auth_accounts_user_id_auth_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."auth_users"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
|
||||||
|
ALTER TABLE "auth_sessions" ADD CONSTRAINT "auth_sessions_user_id_auth_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."auth_users"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
|
||||||
|
ALTER TABLE "auth_sessions" ADD CONSTRAINT "auth_sessions_impersonated_by_auth_users_id_fk" FOREIGN KEY ("impersonated_by") REFERENCES "public"."auth_users"("id") ON DELETE set null ON UPDATE no action;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX "email_unique_index" ON "auth_users" USING btree (lower("email"));
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
-- Properties tables
|
||||||
|
-- Simple property management without organizations
|
||||||
|
|
||||||
|
-- Properties addresses table (must be created first due to foreign key)
|
||||||
|
CREATE TABLE IF NOT EXISTS properties_addresses (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
street_number TEXT,
|
||||||
|
route TEXT,
|
||||||
|
route_short TEXT,
|
||||||
|
neighborhood TEXT,
|
||||||
|
city TEXT,
|
||||||
|
county TEXT,
|
||||||
|
state TEXT,
|
||||||
|
state_long TEXT,
|
||||||
|
postal_code TEXT,
|
||||||
|
postal_code_suffix TEXT,
|
||||||
|
country TEXT,
|
||||||
|
country_long TEXT,
|
||||||
|
latitude NUMERIC,
|
||||||
|
longitude NUMERIC,
|
||||||
|
raw_json JSONB,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
UNIQUE(street_number, route, city, state, postal_code)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_properties_addresses_city_state ON properties_addresses(city, state);
|
||||||
|
|
||||||
|
-- Properties table
|
||||||
|
CREATE TABLE IF NOT EXISTS properties (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
name TEXT,
|
||||||
|
address_id TEXT UNIQUE REFERENCES properties_addresses(id) ON DELETE SET NULL,
|
||||||
|
owner_id TEXT REFERENCES auth_users(id) ON DELETE SET NULL,
|
||||||
|
details_json JSONB,
|
||||||
|
metadata JSONB,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_properties_owner_id ON properties(owner_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_properties_address_id ON properties(address_id);
|
||||||
|
|
||||||
|
-- Properties models table (scene graphs)
|
||||||
|
CREATE TABLE IF NOT EXISTS properties_models (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
name TEXT,
|
||||||
|
version INTEGER DEFAULT 1,
|
||||||
|
description TEXT,
|
||||||
|
draft BOOLEAN DEFAULT true,
|
||||||
|
property_id TEXT REFERENCES properties(id) ON DELETE SET NULL,
|
||||||
|
scene_graph JSONB,
|
||||||
|
metadata JSONB,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
deleted_at TIMESTAMPTZ
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_properties_models_property_id ON properties_models(property_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_properties_models_version ON properties_models(property_id, version DESC);
|
||||||
|
|
||||||
|
-- Enable Row Level Security (RLS)
|
||||||
|
ALTER TABLE properties ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE properties_addresses ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE properties_models ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
-- RLS Policies for properties table
|
||||||
|
CREATE POLICY "Users can view their own properties"
|
||||||
|
ON properties FOR SELECT
|
||||||
|
USING (owner_id = current_setting('app.user_id', true)::TEXT OR owner_id IS NULL);
|
||||||
|
|
||||||
|
CREATE POLICY "Users can insert their own properties"
|
||||||
|
ON properties FOR INSERT
|
||||||
|
WITH CHECK (owner_id = current_setting('app.user_id', true)::TEXT OR owner_id IS NULL);
|
||||||
|
|
||||||
|
CREATE POLICY "Users can update their own properties"
|
||||||
|
ON properties FOR UPDATE
|
||||||
|
USING (owner_id = current_setting('app.user_id', true)::TEXT OR owner_id IS NULL);
|
||||||
|
|
||||||
|
CREATE POLICY "Users can delete their own properties"
|
||||||
|
ON properties FOR DELETE
|
||||||
|
USING (owner_id = current_setting('app.user_id', true)::TEXT OR owner_id IS NULL);
|
||||||
|
|
||||||
|
-- RLS Policies for properties_addresses table
|
||||||
|
-- Addresses table doesn't have property_id, so we'll allow all authenticated users
|
||||||
|
CREATE POLICY "Authenticated users can view all addresses"
|
||||||
|
ON properties_addresses FOR SELECT
|
||||||
|
USING (true);
|
||||||
|
|
||||||
|
CREATE POLICY "Authenticated users can insert addresses"
|
||||||
|
ON properties_addresses FOR INSERT
|
||||||
|
WITH CHECK (true);
|
||||||
|
|
||||||
|
CREATE POLICY "Authenticated users can update addresses"
|
||||||
|
ON properties_addresses FOR UPDATE
|
||||||
|
USING (true);
|
||||||
|
|
||||||
|
CREATE POLICY "Authenticated users can delete addresses"
|
||||||
|
ON properties_addresses FOR DELETE
|
||||||
|
USING (true);
|
||||||
|
|
||||||
|
-- RLS Policies for properties_models table
|
||||||
|
CREATE POLICY "Users can view models of their own properties"
|
||||||
|
ON properties_models FOR SELECT
|
||||||
|
USING (
|
||||||
|
EXISTS (
|
||||||
|
SELECT 1 FROM properties
|
||||||
|
WHERE properties.id = properties_models.property_id
|
||||||
|
AND properties.owner_id = current_setting('app.user_id', true)::TEXT
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE POLICY "Users can insert models for their own properties"
|
||||||
|
ON properties_models FOR INSERT
|
||||||
|
WITH CHECK (
|
||||||
|
EXISTS (
|
||||||
|
SELECT 1 FROM properties
|
||||||
|
WHERE properties.id = properties_models.property_id
|
||||||
|
AND properties.owner_id = current_setting('app.user_id', true)::TEXT
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE POLICY "Users can update models of their own properties"
|
||||||
|
ON properties_models FOR UPDATE
|
||||||
|
USING (
|
||||||
|
EXISTS (
|
||||||
|
SELECT 1 FROM properties
|
||||||
|
WHERE properties.id = properties_models.property_id
|
||||||
|
AND properties.owner_id = current_setting('app.user_id', true)::TEXT
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE POLICY "Users can delete models of their own properties"
|
||||||
|
ON properties_models FOR DELETE
|
||||||
|
USING (
|
||||||
|
EXISTS (
|
||||||
|
SELECT 1 FROM properties
|
||||||
|
WHERE properties.id = properties_models.property_id
|
||||||
|
AND properties.owner_id = current_setting('app.user_id', true)::TEXT
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Trigger to update updated_at timestamp
|
||||||
|
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
NEW.updated_at = NOW();
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE TRIGGER update_properties_updated_at
|
||||||
|
BEFORE UPDATE ON properties
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION update_updated_at_column();
|
||||||
|
|
||||||
|
CREATE TRIGGER update_properties_addresses_updated_at
|
||||||
|
BEFORE UPDATE ON properties_addresses
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION update_updated_at_column();
|
||||||
|
|
||||||
|
CREATE TRIGGER update_properties_models_updated_at
|
||||||
|
BEFORE UPDATE ON properties_models
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION update_updated_at_column();
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- Add active_property_id to sessions table
|
||||||
|
ALTER TABLE auth_sessions ADD COLUMN IF NOT EXISTS active_property_id TEXT;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"extends": "@repo/typescript-config/base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src"
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user